diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -117,6 +117,7 @@ add_subdirectory(desktop) add_subdirectory(icons) add_subdirectory(doc) +add_subdirectory(kconf_update) # summaries diff --git a/dbus/org.kde.Spectacle.xml b/dbus/org.kde.Spectacle.xml --- a/dbus/org.kde.Spectacle.xml +++ b/dbus/org.kde.Spectacle.xml @@ -24,6 +24,16 @@ Whether to include an image of the mouse pointer. + + + Whether to copy the image to clipboard. + + + + + Whether to save the image to file. + + Takes a full-screen screenshot. @@ -38,6 +48,16 @@ Whether to include an image of the mouse pointer. + + + Whether to copy the image to clipboard. + + + + + Whether to save the image to file. + + Takes a screenshot of the current screen. @@ -52,6 +72,16 @@ Whether to include the window titlebars and frames. + + + Whether to copy the image to clipboard. + + + + + Whether to save the image to file. + + Whether to include an image of the mouse pointer. @@ -76,6 +106,16 @@ Whether to include an image of the mouse pointer. + + + Whether to copy the image to clipboard. + + + + + Whether to save the image to file. + + Takes a screenshot of the window that is currently under the mouse cursor. @@ -90,6 +130,16 @@ Whether to include an image of the mouse pointer. + + + Whether to copy the image to clipboard. + + + + + Whether to save the image to file. + + Takes a screenshot of a rectangular region. diff --git a/desktop/spectacle.khotkeys b/desktop/spectacle.khotkeys --- a/desktop/spectacle.khotkeys +++ b/desktop/spectacle.khotkeys @@ -42,7 +42,7 @@ Comment[x-test]=xxShortcuts for taking screenshotsxx Comment[zh_CN]=截图快捷键 Comment[zh_TW]=螢幕快照的快捷鍵 -DataCount=4 +DataCount=7 Enabled=true Name=Screenshots Name[ca]=Captures de pantalla @@ -254,7 +254,7 @@ Comment[zh_CN]=对全屏 (所有显示器) 截图并保存 Comment[zh_TW]=取得全螢幕(所有螢幕)快照並儲存 Enabled=true -Name=Take Full Screen Screenshot +Name=Save Screenshot of Full Screen to File Name[ca]=Pren una captura de pantalla completa Name[ca@valencia]=Pren una captura de pantalla completa Name[cs]=Zachytit snímek celé obrazovky @@ -295,7 +295,7 @@ ActionsCount=1 [Data_1_2Actions0] -Arguments=false +Arguments=false false true Call=FullScreen RemoteApp=org.kde.Spectacle RemoteObj=/ @@ -383,7 +383,7 @@ Comment[zh_CN]=对当前活动窗口截图并保存 Comment[zh_TW]=取得目前作用中視窗的快照並儲存 Enabled=true -Name=Take Active Window Screenshot +Name=Save Screenshot of Active Window to File Name[ca]=Pren una captura de pantalla de la finestra activa Name[ca@valencia]=Pren una captura de pantalla de la finestra activa Name[cs]=Zachytit snímek aktivního okna @@ -424,7 +424,7 @@ ActionsCount=1 [Data_1_3Actions0] -Arguments=true false +Arguments=true false false true Call=ActiveWindow RemoteApp=org.kde.Spectacle RemoteObj=/ @@ -477,7 +477,7 @@ Type=SHORTCUT [Data_1_4] -Comment=Take a screenshot of a rectangular region you specify and save it +Comment=Save Screenshot of a rectangular region you specify to File Comment[ca]=Pren una captura de pantalla d'una regió rectangular i la desa Comment[ca@valencia]=Pren una captura de pantalla d'una regió rectangular i la guarda Comment[da]=Tag skærmbillede af et firkantet område som du angiver og gem det @@ -512,7 +512,7 @@ Comment[zh_CN]=对指定矩形区域截图并保存 Comment[zh_TW]=取得您指定的矩形區域的快照並儲存 Enabled=true -Name=Take Rectangular Region Screenshot +Name=Save Screenshot of Rectangular Region To File Name[ca]=Pren una captura de pantalla d'una regió rectangular Name[ca@valencia]=Pren una captura de pantalla d'una regió rectangular Name[cs]=Zachytit snímek obdélníkové oblasti @@ -553,7 +553,7 @@ ActionsCount=1 [Data_1_4Actions0] -Arguments=true +Arguments=true false true Call=RectangularRegion RemoteApp=org.kde.Spectacle RemoteObj=/ @@ -604,3 +604,87 @@ [Data_1_4Triggers0] Key=Meta+Shift+Print Type=SHORTCUT + +[Data_1_5] +Comment=Take a full screen (all monitors) screenshot and save it to clipboard +Enabled=true +Name=Save Screenshot of Full Screen to Clipboard +Type=SIMPLE_ACTION_DATA + +[Data_1_5Actions] +ActionsCount=1 + +[Data_1_5Actions0] +Arguments=false true false +Call=FullScreen +RemoteApp=org.kde.Spectacle +RemoteObj=/ +Type=DBUS + +[Data_1_5Conditions] +Comment= +ConditionsCount=0 + +[Data_1_5Triggers] +Comment=Simple_action +TriggersCount=1 + +[Data_1_5Triggers0] +Key=Ctrl+Shift+Print +Type=SHORTCUT + +[Data_1_6] +Comment=Take a screenshot of the currently active window and save it to clipboard +Enabled=true +Name=Save Screenshot of Active Window to Clipboard +Type=SIMPLE_ACTION_DATA + +[Data_1_6Actions] +ActionsCount=1 + +[Data_1_6Actions0] +Arguments=true false true false +Call=ActiveWindow +RemoteApp=org.kde.Spectacle +RemoteObj=/ +Type=DBUS + +[Data_1_6Conditions] +Comment= +ConditionsCount=0 + +[Data_1_6Triggers] +Comment=Simple_action +TriggersCount=1 + +[Data_1_6Triggers0] +Key=Ctrl+Meta+Print +Type=SHORTCUT + +[Data_1_7] +Comment=Take a screenshot of a rectangular region you specify and save it to clipboard +Enabled=true +Name=Save Screenshot of Rectangular Region To Clipboard +Type=SIMPLE_ACTION_DATA + +[Data_1_7Actions] +ActionsCount=1 + +[Data_1_7Actions0] +Arguments=true true false +Call=RectangularRegion +RemoteApp=org.kde.Spectacle +RemoteObj=/ +Type=DBUS + +[Data_1_7Conditions] +Comment= +ConditionsCount=0 + +[Data_1_7Triggers] +Comment=Simple_action +TriggersCount=1 + +[Data_1_7Triggers0] +Key=Ctrl+Print +Type=SHORTCUT diff --git a/kconf_update/CMakeLists.txt b/kconf_update/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/kconf_update/CMakeLists.txt @@ -0,0 +1,14 @@ +find_package(Qt5 REQUIRED COMPONENTS Core) +find_package(KF5 REQUIRED COMPONENTS Config) + +add_executable(spectacle_update spectacle_update.cpp) + +target_link_libraries(spectacle_update Qt5::Core KF5::ConfigCore) + +install(TARGETS spectacle_update + DESTINATION ${LIB_INSTALL_DIR}/kconf_update_bin/) + +install(FILES spectacle.upd + DESTINATION ${KDE_INSTALL_KCONFUPDATEDIR} +) + diff --git a/kconf_update/spectacle.upd b/kconf_update/spectacle.upd new file mode 100644 --- /dev/null +++ b/kconf_update/spectacle.upd @@ -0,0 +1,4 @@ +Version=5 + +Id=spectacle01 +Script=spectacle_update diff --git a/kconf_update/spectacle_update.cpp b/kconf_update/spectacle_update.cpp new file mode 100644 --- /dev/null +++ b/kconf_update/spectacle_update.cpp @@ -0,0 +1,44 @@ +#include +#include + +int main(void) +{ + KConfig config(QStringLiteral("khotkeysrc")); + + // Remove spectacle (if present) from entry "AlreadyImported" of [Main] group of file khotkeysrc + if (config.hasGroup(QStringLiteral("Main")) + && config.group(QStringLiteral("Main")).hasKey(QStringLiteral("AlreadyImported"))) { + + QStringList importedList(config.group(QStringLiteral("Main")).readEntry( + QStringLiteral("AlreadyImported")).split(QStringLiteral(","), QString::SkipEmptyParts)); + + QStringListIterator it(importedList); + QStringList updatedHotkeys; + while (it.hasNext()) { + QString entry = it.next().simplified(); + if (entry != QStringLiteral("spectacle")) { + updatedHotkeys.push_back(entry); + } + } + + updatedHotkeys.join(QStringLiteral(",")); + config.group(QStringLiteral("Main")).writeEntry(QStringLiteral("AlreadyImported"), + updatedHotkeys.join(QStringLiteral(","))); + } + + // Remove all shortcuts for spectacle present in file khotkeysrc + QStringList groupList(config.groupList()); + for (int i = 1; groupList.contains(QStringLiteral("Data_%1").arg(QString::number(i))); i++) { + + KConfigGroup group = config.group(QStringLiteral("Data_%1").arg(QString::number(i))); + if (group.hasKey(QStringLiteral("ImportId")) + && group.readEntry(QStringLiteral("ImportId")) == QStringLiteral("spectacle")) { + + for (const QString groupName : groupList) { + if (groupName.startsWith(QStringLiteral("Data_%1").arg(QString::number(i)))) { + config.group(groupName).deleteGroup(); + } + } + } + } +} diff --git a/src/ExportManager.cpp b/src/ExportManager.cpp --- a/src/ExportManager.cpp +++ b/src/ExportManager.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -434,6 +435,10 @@ void ExportManager::doCopyToClipboard() { QApplication::clipboard()->setPixmap(mSavePixmap, QClipboard::Clipboard); + QMimeData *mimeData = new QMimeData(); + mimeData->setImageData(mSavePixmap.toImage()); + mimeData->setData(QStringLiteral("x-kde-force-image-copy"), mimeData->data(QStringLiteral("application/x-qt-image"))); + QApplication::clipboard()->setMimeData(mimeData, QClipboard::Clipboard); } void ExportManager::doPrint(QPrinter *printer) diff --git a/src/PlatformBackends/ImageGrabber.h b/src/PlatformBackends/ImageGrabber.h --- a/src/PlatformBackends/ImageGrabber.h +++ b/src/PlatformBackends/ImageGrabber.h @@ -52,19 +52,28 @@ Q_ENUM(GrabMode); + enum SaveMode { + File = 0, + Clipboard = 1 + }; + + Q_ENUM(SaveMode); + explicit ImageGrabber(QObject *parent = 0); ~ImageGrabber(); QPixmap pixmap() const; bool capturePointer() const; bool captureDecorations() const; GrabMode grabMode() const; + SaveMode saveMode() const; virtual bool onClickGrabSupported() const; void setCapturePointer(const bool newCapturePointer); void setCaptureDecorations(const bool newCaptureDecorations); void setGrabMode(const GrabMode newGrabMode); + void setSaveMode(const SaveMode newSaveMode); signals: @@ -93,6 +102,7 @@ bool mCapturePointer; bool mCaptureDecorations; GrabMode mGrabMode; + SaveMode mSaveMode; QPixmap mPixmap; }; diff --git a/src/PlatformBackends/ImageGrabber.cpp b/src/PlatformBackends/ImageGrabber.cpp --- a/src/PlatformBackends/ImageGrabber.cpp +++ b/src/PlatformBackends/ImageGrabber.cpp @@ -61,6 +61,11 @@ return mGrabMode; } +ImageGrabber::SaveMode ImageGrabber::saveMode() const +{ + return mSaveMode; +} + void ImageGrabber::setCapturePointer(const bool newCapturePointer) { mCapturePointer = newCapturePointer; @@ -76,6 +81,11 @@ mGrabMode = newGrabMode; } +void ImageGrabber::setSaveMode(const SaveMode newSaveMode) +{ + mSaveMode = newSaveMode; +} + // Slots void ImageGrabber::doOnClickGrab() diff --git a/src/SpectacleCore.h b/src/SpectacleCore.h --- a/src/SpectacleCore.h +++ b/src/SpectacleCore.h @@ -60,7 +60,8 @@ public slots: - void takeNewScreenshot(const ImageGrabber::GrabMode &mode, const int &timeout, const bool &includePointer, const bool &includeDecorations); + void takeNewScreenshot(const ImageGrabber::GrabMode &mode, const int &timeout, const bool &includePointer, + const bool &includeDecorations, const bool &shouldCopy = false, const bool &shouldSave = true); void showErrorMessage(const QString &errString); void screenshotUpdated(const QPixmap &pixmap); void screenshotFailed(); diff --git a/src/SpectacleCore.cpp b/src/SpectacleCore.cpp --- a/src/SpectacleCore.cpp +++ b/src/SpectacleCore.cpp @@ -40,7 +40,7 @@ #include SpectacleCore::SpectacleCore(StartMode startMode, ImageGrabber::GrabMode grabMode, QString &saveFileName, - qint64 delayMsec, bool notifyOnGrab, QObject *parent) : + qint64 delayMsec, bool notifyOnGrab, QObject *parent) : QObject(parent), mExportManager(ExportManager::instance()), mStartMode(startMode), @@ -75,6 +75,7 @@ setGrabMode(grabMode); mImageGrabber->setCapturePointer(guiConfig.readEntry("includePointer", true)); mImageGrabber->setCaptureDecorations(guiConfig.readEntry("includeDecorations", true)); + mImageGrabber->setSaveMode(ImageGrabber::SaveMode::File); if ((!(mImageGrabber->onClickGrabSupported())) && (delayMsec < 0)) { delayMsec = 0; @@ -92,10 +93,10 @@ case DBusMode: break; case BackgroundMode: { - int msec = (KWindowSystem::compositingActive() ? 200 : 50) + delayMsec; - QTimer::singleShot(msec, mImageGrabber, &ImageGrabber::doImageGrab); - } - break; + int msec = (KWindowSystem::compositingActive() ? 200 : 50) + delayMsec; + QTimer::singleShot(msec, mImageGrabber, &ImageGrabber::doImageGrab); + } + break; case GuiMode: initGui(); break; @@ -145,11 +146,17 @@ } void SpectacleCore::takeNewScreenshot(const ImageGrabber::GrabMode &mode, - const int &timeout, const bool &includePointer, const bool &includeDecorations) + const int &timeout, const bool &includePointer, + const bool &includeDecorations, const bool &shouldCopy, const bool &shouldSave) { setGrabMode(mode); mImageGrabber->setCapturePointer(includePointer); mImageGrabber->setCaptureDecorations(includeDecorations); + if (shouldSave) { + mImageGrabber->setSaveMode(ImageGrabber::SaveMode::File); + } else { + mImageGrabber->setSaveMode(ImageGrabber::SaveMode::Clipboard); + } if (timeout < 0) { mImageGrabber->doOnClickGrab(); @@ -178,17 +185,21 @@ void SpectacleCore::screenshotUpdated(const QPixmap &pixmap) { mExportManager->setPixmap(pixmap); + if (mImageGrabber->saveMode() == ImageGrabber::SaveMode::Clipboard) { + mExportManager->doCopyToClipboard(); + QTimer::singleShot(250, this, &SpectacleCore::allDone); + } - switch (mStartMode) { - case BackgroundMode: - case DBusMode: - { + if (mImageGrabber->saveMode() == ImageGrabber::SaveMode::File) { + switch (mStartMode) { + case BackgroundMode: + case DBusMode: { if (mNotify) { connect(mExportManager, &ExportManager::imageSaved, this, &SpectacleCore::doNotify); } QUrl savePath = (mStartMode == BackgroundMode && mFileNameUrl.isValid() && mFileNameUrl.isLocalFile()) ? - mFileNameUrl : QUrl(); + mFileNameUrl : QUrl(); mExportManager->doSave(savePath); // if we notify, we emit allDone only if the user either dismissed the notification or pressed @@ -198,8 +209,9 @@ } } break; - case GuiMode: - mMainWindow->setScreenshotAndShow(pixmap); + case GuiMode: + mMainWindow->setScreenshotAndShow(pixmap); + } } } @@ -223,7 +235,7 @@ { KNotification *notify = new KNotification(QStringLiteral("newScreenshotSaved")); - switch(mImageGrabber->grabMode()) { + switch (mImageGrabber->grabMode()) { case ImageGrabber::GrabMode::FullScreen: notify->setTitle(i18nc("The entire screen area was captured, heading", "Full Screen Captured")); break; @@ -295,7 +307,10 @@ if (!isGuiInited) { mMainWindow = new KSMainWindow(mImageGrabber->onClickGrabSupported()); - connect(mMainWindow, &KSMainWindow::newScreenshotRequest, this, &SpectacleCore::takeNewScreenshot); + connect(mMainWindow, &KSMainWindow::newScreenshotRequest, + [this](const ImageGrabber::GrabMode & mode, const int &timeout, const bool & includePointer, const bool & includeDecorations) { + takeNewScreenshot(mode, timeout, includePointer, includeDecorations); + }); connect(mMainWindow, &KSMainWindow::dragAndDropRequest, this, &SpectacleCore::doStartDragAndDrop); isGuiInited = true; diff --git a/src/SpectacleDBusAdapter.h b/src/SpectacleDBusAdapter.h --- a/src/SpectacleDBusAdapter.h +++ b/src/SpectacleDBusAdapter.h @@ -33,20 +33,30 @@ " \n" " \n" " \n" + " \n" + " \n" " \n" " \n" " \n" + " \n" + " \n" " \n" " \n" " \n" " \n" + " \n" + " \n" " \n" " \n" " \n" " \n" + " \n" + " \n" " \n" " \n" " \n" + " \n" + " \n" " \n" " \n" " \n" @@ -67,11 +77,11 @@ public slots: Q_NOREPLY void StartAgent(); - Q_NOREPLY void FullScreen(bool includeMousePointer); - Q_NOREPLY void CurrentScreen(bool includeMousePointer); - Q_NOREPLY void ActiveWindow(bool includeWindowDecorations, bool includeMousePointer); - Q_NOREPLY void WindowUnderCursor(bool includeWindowDecorations, bool includeMousePointer); - Q_NOREPLY void RectangularRegion(bool includeMousePointer); + Q_NOREPLY void FullScreen(bool includeMousePointer, bool shouldCopy, bool shouldSave); + Q_NOREPLY void CurrentScreen(bool includeMousePointer, bool shouldCopy, bool shouldSave); + Q_NOREPLY void ActiveWindow(bool includeWindowDecorations, bool includeMousePointer, bool shouldCopy, bool shouldSave); + Q_NOREPLY void WindowUnderCursor(bool includeWindowDecorations, bool includeMousePointer, bool shouldCopy, bool shouldSave); + Q_NOREPLY void RectangularRegion(bool includeMousePointer, bool shouldCopy, bool shouldSave); signals: diff --git a/src/SpectacleDBusAdapter.cpp b/src/SpectacleDBusAdapter.cpp --- a/src/SpectacleDBusAdapter.cpp +++ b/src/SpectacleDBusAdapter.cpp @@ -38,27 +38,27 @@ parent()->dbusStartAgent(); } -Q_NOREPLY void SpectacleDBusAdapter::FullScreen(bool includeMousePointer) +Q_NOREPLY void SpectacleDBusAdapter::FullScreen(bool includeMousePointer, bool shouldCopy, bool shouldSave) { - parent()->takeNewScreenshot(ImageGrabber::FullScreen, 0, includeMousePointer, true); + parent()->takeNewScreenshot(ImageGrabber::FullScreen, 0, includeMousePointer, true, shouldCopy, shouldSave); } -Q_NOREPLY void SpectacleDBusAdapter::CurrentScreen(bool includeMousePointer) +Q_NOREPLY void SpectacleDBusAdapter::CurrentScreen(bool includeMousePointer, bool shouldCopy, bool shouldSave) { - parent()->takeNewScreenshot(ImageGrabber::CurrentScreen, 0, includeMousePointer, true); + parent()->takeNewScreenshot(ImageGrabber::CurrentScreen, 0, includeMousePointer, true, shouldCopy, shouldSave); } -Q_NOREPLY void SpectacleDBusAdapter::ActiveWindow(bool includeWindowDecorations, bool includeMousePointer) +Q_NOREPLY void SpectacleDBusAdapter::ActiveWindow(bool includeWindowDecorations, bool includeMousePointer, bool shouldCopy, bool shouldSave) { - parent()->takeNewScreenshot(ImageGrabber::ActiveWindow, 0, includeMousePointer, includeWindowDecorations); + parent()->takeNewScreenshot(ImageGrabber::ActiveWindow, 0, includeMousePointer, includeWindowDecorations, shouldCopy, shouldSave); } -Q_NOREPLY void SpectacleDBusAdapter::WindowUnderCursor(bool includeWindowDecorations, bool includeMousePointer) +Q_NOREPLY void SpectacleDBusAdapter::WindowUnderCursor(bool includeWindowDecorations, bool includeMousePointer, bool shouldCopy, bool shouldSave) { - parent()->takeNewScreenshot(ImageGrabber::WindowUnderCursor, 0, includeMousePointer, includeWindowDecorations); + parent()->takeNewScreenshot(ImageGrabber::WindowUnderCursor, 0, includeMousePointer, includeWindowDecorations, shouldCopy, shouldSave); } -Q_NOREPLY void SpectacleDBusAdapter::RectangularRegion(bool includeMousePointer) +Q_NOREPLY void SpectacleDBusAdapter::RectangularRegion(bool includeMousePointer, bool shouldCopy, bool shouldSave) { - parent()->takeNewScreenshot(ImageGrabber::RectangularRegion, 0, includeMousePointer, false); + parent()->takeNewScreenshot(ImageGrabber::RectangularRegion, 0, includeMousePointer, false, shouldCopy, shouldSave); }