diff --git a/doc/index.docbook b/doc/index.docbook
--- a/doc/index.docbook
+++ b/doc/index.docbook
@@ -311,6 +311,7 @@
%H: Hour
%m: Minute
%S: Second
+ %T: Window title
If a file with this name already exists, a serial number will be appended to the filename. For example, if the filename is Screenshot, and Screenshot.png already exists, the image will be saved as Screenshot-1.png.
Typing an extension into the filename will automatically set the image format correctly and remove the extension from the filename field.
diff --git a/src/ExportManager.h b/src/ExportManager.h
--- a/src/ExportManager.h
+++ b/src/ExportManager.h
@@ -26,6 +26,8 @@
#include
#include
+#include "PlatformBackends/ImageGrabber.h"
+
class QTemporaryDir;
class ExportManager : public QObject
@@ -52,14 +54,20 @@
Q_PROPERTY(QString saveLocation READ saveLocation WRITE setSaveLocation NOTIFY saveLocationChanged)
Q_PROPERTY(QPixmap pixmap READ pixmap WRITE setPixmap NOTIFY pixmapChanged)
+ Q_PROPERTY(QString windowTitle READ windowTitle WRITE setWindowTitle)
+ Q_PROPERTY(ImageGrabber::GrabMode grabMode READ grabMode WRITE setGrabMode)
void setSaveLocation(const QString &location);
QString saveLocation() const;
QUrl lastSavePath() const;
bool isFileExists(const QUrl &url) const;
void setPixmap(const QPixmap &pixmap);
QPixmap pixmap() const;
QString pixmapDataUri() const;
+ void setWindowTitle(const QString &windowTitle);
+ QString windowTitle() const;
+ ImageGrabber::GrabMode grabMode() const;
+ void setGrabMode(const ImageGrabber::GrabMode &grabMode);
signals:
@@ -81,6 +89,7 @@
private:
+ QString truncatedFilename(const QString &filename);
QString makeAutosaveFilename();
using FileNameAlreadyUsedCheck = bool (ExportManager::*)(const QUrl&) const;
QString autoIncrementFilename(const QString &baseName, const QString &extension,
@@ -97,6 +106,8 @@
QUrl mTempFile;
QTemporaryDir *mTempDir;
QList mUsedTempFileNames;
+ QString mWindowTitle;
+ ImageGrabber::GrabMode mGrabMode;
};
#endif // EXPORTMANAGER_H
diff --git a/src/ExportManager.cpp b/src/ExportManager.cpp
--- a/src/ExportManager.cpp
+++ b/src/ExportManager.cpp
@@ -29,6 +29,7 @@
#include
#include
#include
+#include
#include
#include
@@ -87,6 +88,26 @@
return uri;
}
+void ExportManager::setWindowTitle(const QString &windowTitle)
+{
+ mWindowTitle = windowTitle;
+}
+
+QString ExportManager::windowTitle() const
+{
+ return mWindowTitle;
+}
+
+ImageGrabber::GrabMode ExportManager::grabMode() const
+{
+ return mGrabMode;
+}
+
+void ExportManager::setGrabMode(const ImageGrabber::GrabMode &grabMode)
+{
+ mGrabMode = grabMode;
+}
+
void ExportManager::setPixmap(const QPixmap &pixmap)
{
mSavePixmap = pixmap;
@@ -153,41 +174,75 @@
}
}
+QString ExportManager::truncatedFilename(QString const &filename)
+{
+ QString result = filename;
+ constexpr auto maxFilenameLength = 255;
+ constexpr auto maxExtensionLength = 5; // For example, ".jpeg"
+ constexpr auto maxCounterLength = 20; // std::numeric_limits::max() == 18446744073709551615
+ constexpr auto maxLength = maxFilenameLength - maxCounterLength - maxExtensionLength;
+ result.truncate(maxLength);
+ return result;
+}
+
QString ExportManager::makeAutosaveFilename()
{
KSharedConfigPtr config = KSharedConfig::openConfig(QStringLiteral("spectaclerc"));
KConfigGroup generalConfig = KConfigGroup(config, "General");
const QDateTime timestamp = QDateTime::currentDateTime();
QString baseName = generalConfig.readEntry("save-filename-format", "Screenshot_%Y%M%D_%H%m%S");
- return baseName.replace(QLatin1String("%Y"), timestamp.toString(QStringLiteral("yyyy")))
- .replace(QLatin1String("%y"), timestamp.toString(QStringLiteral("yy")))
- .replace(QLatin1String("%M"), timestamp.toString(QStringLiteral("MM")))
- .replace(QLatin1String("%D"), timestamp.toString(QStringLiteral("dd")))
- .replace(QLatin1String("%H"), timestamp.toString(QStringLiteral("hh")))
- .replace(QLatin1String("%m"), timestamp.toString(QStringLiteral("mm")))
- .replace(QLatin1String("%S"), timestamp.toString(QStringLiteral("ss")));
+ QString title;
+
+ if (mGrabMode == ImageGrabber::GrabMode::ActiveWindow ||
+ mGrabMode == ImageGrabber::GrabMode::TransientWithParent ||
+ mGrabMode == ImageGrabber::GrabMode::WindowUnderCursor) {
+ title = mWindowTitle;
+ } else {
+ // Remove '%T' with separators around it
+ const auto wordSymbol = QStringLiteral(R"(\p{L}\p{M}\p{N})");
+ const auto separator = QStringLiteral("([^%1]+)").arg(wordSymbol);
+ const auto re = QRegularExpression(QStringLiteral("(.*?)(%1%T|%T%1)(.*?)").arg(separator));
+ baseName.replace(re, QStringLiteral(R"(\1\5)"));
+ }
+
+ QString result = baseName.replace(QLatin1String("%Y"), timestamp.toString(QStringLiteral("yyyy")))
+ .replace(QLatin1String("%y"), timestamp.toString(QStringLiteral("yy")))
+ .replace(QLatin1String("%M"), timestamp.toString(QStringLiteral("MM")))
+ .replace(QLatin1String("%D"), timestamp.toString(QStringLiteral("dd")))
+ .replace(QLatin1String("%H"), timestamp.toString(QStringLiteral("hh")))
+ .replace(QLatin1String("%m"), timestamp.toString(QStringLiteral("mm")))
+ .replace(QLatin1String("%S"), timestamp.toString(QStringLiteral("ss")))
+ .replace(QLatin1String("%T"), title)
+ .replace(QLatin1String("/"), QLatin1String("_")); // POSIX doesn't allow "/" in filenames
+ if (result.isEmpty()) {
+ result = QStringLiteral("Screenshot");
+ }
+ return truncatedFilename(result);
}
QString ExportManager::autoIncrementFilename(const QString &baseName, const QString &extension,
FileNameAlreadyUsedCheck isFileNameUsed)
{
- if (!((this->*isFileNameUsed)(QUrl::fromUserInput(baseName + QLatin1Char('.') + extension)))) {
- return baseName + QLatin1Char('.') + extension;
+ QString result = truncatedFilename(baseName) + QLatin1Literal(".") + extension;
+ if (!((this->*isFileNameUsed)(QUrl::fromUserInput(result)))) {
+ return result;
}
- QString fileNameFmt(baseName + QStringLiteral("-%1.") + extension);
+ QString fileNameFmt = truncatedFilename(baseName) + QStringLiteral("-%1.");
for (quint64 i = 1; i < std::numeric_limits::max(); i++) {
- if (!((this->*isFileNameUsed)(QUrl::fromUserInput(fileNameFmt.arg(i))))) {
- return fileNameFmt.arg(i);
+ result = fileNameFmt.arg(i) + extension;
+ if (!((this->*isFileNameUsed)(QUrl::fromUserInput(result)))) {
+ return result;
}
}
// unlikely this will ever happen, but just in case we've run
// out of numbers
- return fileNameFmt.arg(QStringLiteral("OVERFLOW-") + QString::number(qrand() % 10000));
+ result = fileNameFmt.arg(QStringLiteral("OVERFLOW-") + QString::number(qrand() % 10000));
+ return truncatedFilename(result) + extension;
}
QString ExportManager::makeSaveMimetype(const QUrl &url)
diff --git a/src/Gui/SettingsDialog/SaveOptionsPage.cpp b/src/Gui/SettingsDialog/SaveOptionsPage.cpp
--- a/src/Gui/SettingsDialog/SaveOptionsPage.cpp
+++ b/src/Gui/SettingsDialog/SaveOptionsPage.cpp
@@ -103,13 +103,15 @@
"%D: Day
"
"%H: Hour
"
"%m: Minute
"
- "%S: Second"
+ "%S: Second
"
+ "%T: Window title"
""
);
QLabel *fmtHelpText = new QLabel(helpText, this);
fmtHelpText->setWordWrap(true);
fmtHelpText->setTextFormat(Qt::RichText);
+ fmtHelpText->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
fmtLayout->addWidget(fmtHelpText);
// read in the data
diff --git a/src/PlatformBackends/ImageGrabber.h b/src/PlatformBackends/ImageGrabber.h
--- a/src/PlatformBackends/ImageGrabber.h
+++ b/src/PlatformBackends/ImageGrabber.h
@@ -69,6 +69,7 @@
signals:
void pixmapChanged(const QPixmap &pixmap);
+ void windowTitleChanged(const QString &windowTitle);
void imageGrabFailed();
void capturePointerChanged(bool capturePointer);
void captureDecorationsChanged(bool captureDecorations);
diff --git a/src/PlatformBackends/X11ImageGrabber.h b/src/PlatformBackends/X11ImageGrabber.h
--- a/src/PlatformBackends/X11ImageGrabber.h
+++ b/src/PlatformBackends/X11ImageGrabber.h
@@ -87,6 +87,7 @@
QPoint getNativeCursorPosition();
OnClickEventFilter *mNativeEventFilter;
+ void updateWindowTitle(xcb_window_t window);
};
template using CScopedPointer = QScopedPointer;
diff --git a/src/PlatformBackends/X11ImageGrabber.cpp b/src/PlatformBackends/X11ImageGrabber.cpp
--- a/src/PlatformBackends/X11ImageGrabber.cpp
+++ b/src/PlatformBackends/X11ImageGrabber.cpp
@@ -44,6 +44,9 @@
#include
#include
+#include
+#include
+
X11ImageGrabber::X11ImageGrabber(QObject *parent) :
ImageGrabber(parent)
{
@@ -413,15 +416,22 @@
// grabber methods
+void X11ImageGrabber::updateWindowTitle(xcb_window_t window)
+{
+ QString windowTitle = KWindowSystem::readNameProperty(window, XA_WM_NAME);
+ emit windowTitleChanged(windowTitle);
+}
+
void X11ImageGrabber::grabFullScreen()
{
mPixmap = getToplevelPixmap(QRect(), mCapturePointer);
emit pixmapChanged(mPixmap);
}
void X11ImageGrabber::grabTransientWithParent()
{
- xcb_window_t curWin = getRealWindowUnderCursor();
+ xcb_window_t curWin = getRealWindowUnderCursor();
+ updateWindowTitle(curWin);
// grab the image early
@@ -514,6 +524,7 @@
void X11ImageGrabber::grabActiveWindow()
{
xcb_window_t activeWindow = KWindowSystem::activeWindow();
+ updateWindowTitle(activeWindow);
// if KWin is available, use the KWin DBus interfaces
@@ -542,6 +553,9 @@
void X11ImageGrabber::grabWindowUnderCursor()
{
+ const xcb_window_t windowUnderCursor = getRealWindowUnderCursor();
+ updateWindowTitle(windowUnderCursor);
+
// if KWin is available, use the KWin DBus interfaces
if (mCaptureDecorations && isKWinAvailable()) {
@@ -564,7 +578,7 @@
// else, go native
- return grabApplicationWindowHelper(getRealWindowUnderCursor());
+ return grabApplicationWindowHelper(windowUnderCursor);
}
void X11ImageGrabber::grabApplicationWindowHelper(xcb_window_t window)
diff --git a/src/SpectacleCore.cpp b/src/SpectacleCore.cpp
--- a/src/SpectacleCore.cpp
+++ b/src/SpectacleCore.cpp
@@ -75,7 +75,7 @@
mImageGrabber = new DummyImageGrabber;
}
- mImageGrabber->setGrabMode(grabMode);
+ setGrabMode(grabMode);
mImageGrabber->setCapturePointer(guiConfig.readEntry("includePointer", true));
mImageGrabber->setCaptureDecorations(guiConfig.readEntry("includeDecorations", true));
@@ -86,6 +86,7 @@
connect(mExportManager, &ExportManager::errorMessage, this, &SpectacleCore::showErrorMessage);
connect(this, &SpectacleCore::errorMessage, this, &SpectacleCore::showErrorMessage);
connect(mImageGrabber, &ImageGrabber::pixmapChanged, this, &SpectacleCore::screenshotUpdated);
+ connect(mImageGrabber, &ImageGrabber::windowTitleChanged, mExportManager, &ExportManager::setWindowTitle);
connect(mImageGrabber, &ImageGrabber::imageGrabFailed, this, &SpectacleCore::screenshotFailed);
connect(mExportManager, &ExportManager::imageSaved, this, &SpectacleCore::doCopyPath);
connect(mExportManager, &ExportManager::forceNotify, this, &SpectacleCore::doNotify);
@@ -132,6 +133,7 @@
void SpectacleCore::setGrabMode(const ImageGrabber::GrabMode &grabMode)
{
mImageGrabber->setGrabMode(grabMode);
+ mExportManager->setGrabMode(grabMode);
}
// Slots
@@ -148,7 +150,7 @@
void SpectacleCore::takeNewScreenshot(const ImageGrabber::GrabMode &mode,
const int &timeout, const bool &includePointer, const bool &includeDecorations)
{
- mImageGrabber->setGrabMode(mode);
+ setGrabMode(mode);
mImageGrabber->setCapturePointer(includePointer);
mImageGrabber->setCaptureDecorations(includeDecorations);