diff --git a/.idea/dictionaries/romas.xml b/.idea/dictionaries/romas.xml new file mode 100644 --- /dev/null +++ b/.idea/dictionaries/romas.xml @@ -0,0 +1,7 @@ + + + + filenames + + + \ No newline at end of file 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's 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: @@ -97,6 +105,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 @@ -19,16 +19,17 @@ #include "ExportManager.h" +#include +#include +#include #include -#include +#include #include +#include +#include +#include #include #include -#include -#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; @@ -161,13 +182,36 @@ 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 wordOrEmptyString = QStringLiteral("([%1]*)").arg(wordSymbol); + const auto separator = QStringLiteral("([^%1]+)").arg(wordSymbol); + const auto re = QRegularExpression(QStringLiteral("%1(%2%T|%T%2)%1").arg(wordOrEmptyString, 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"); + } + const auto maxFilenameLength = 255; + result.truncate(maxFilenameLength); + return result; } QString ExportManager::autoIncrementFilename(const QString &baseName, const QString &extension, 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,7 +103,8 @@ "%D: Day
" "%H: Hour
" "%m: Minute
" - "%S: Second" + "%S: Second
" + "%T: Window title" "" ); diff --git a/src/Gui/SettingsDialog/SettingsDialog.cpp b/src/Gui/SettingsDialog/SettingsDialog.cpp --- a/src/Gui/SettingsDialog/SettingsDialog.cpp +++ b/src/Gui/SettingsDialog/SettingsDialog.cpp @@ -35,7 +35,7 @@ // set up window options and geometry setWindowTitle(i18n("Configure")); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); - resize(500, 470); + resize(500, 500); // init all pages QMetaObject::invokeMethod(this, "initPages", Qt::QueuedConnection); diff --git a/src/PlatformBackends/ImageGrabber.h b/src/PlatformBackends/ImageGrabber.h --- a/src/PlatformBackends/ImageGrabber.h +++ b/src/PlatformBackends/ImageGrabber.h @@ -67,6 +67,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,6 +416,12 @@ // 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); @@ -422,6 +431,7 @@ void X11ImageGrabber::grabTransientWithParent() { 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() { + 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);