diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -40,6 +40,7 @@ Gui/SettingsDialog/SaveOptionsPage.cpp Gui/SettingsDialog/GeneralOptionsPage.cpp Gui/SettingsDialog/ShortcutsOptionsPage.cpp + Gui/SettingsDialog/TasksOptionsPage.cpp QuickEditor/QuickEditor.cpp ) diff --git a/src/ExportManager.h b/src/ExportManager.h --- a/src/ExportManager.h +++ b/src/ExportManager.h @@ -74,6 +74,7 @@ void errorMessage(const QString &str); void pixmapChanged(const QPixmap &pixmap); void imageSaved(const QUrl &savedAt); + void imageSavedAndCopied(const QUrl &savedAt); void forceNotify(const QUrl &savedAt); public Q_SLOTS: @@ -84,6 +85,7 @@ void setWindowTitle(const QString &windowTitle); void doSave(const QUrl &url = QUrl(), bool notify = false); bool doSaveAs(QWidget *parentWindow = nullptr, bool notify = false); + void doSaveAndCopy(const QUrl &url = QUrl()); void doCopyToClipboard(bool notify); void doPrint(QPrinter *printer); diff --git a/src/ExportManager.cpp b/src/ExportManager.cpp --- a/src/ExportManager.cpp +++ b/src/ExportManager.cpp @@ -504,6 +504,24 @@ return false; } +void ExportManager::doSaveAndCopy(const QUrl &url) +{ + if (mSavePixmap.isNull()) { + emit errorMessage(i18n("Cannot save an empty screenshot image.")); + return; + } + + QUrl savePath = url.isValid() ? url : getAutosaveFilename(); + if (save(savePath)) { + QDir dir(savePath.path()); + dir.cdUp(); + SpectacleConfig::instance()->setLastSaveFile(savePath); + + doCopyToClipboard(false); + emit imageSavedAndCopied(savePath); + } +} + // misc helpers void ExportManager::doCopyToClipboard(bool notify) diff --git a/src/Gui/KSMainWindow.h b/src/Gui/KSMainWindow.h --- a/src/Gui/KSMainWindow.h +++ b/src/Gui/KSMainWindow.h @@ -84,6 +84,7 @@ void setScreenshotAndShow(const QPixmap &pixmap); void imageSaved(const QUrl &location); + void imageSavedAndCopied(const QUrl &location); Q_SIGNALS: diff --git a/src/Gui/KSMainWindow.cpp b/src/Gui/KSMainWindow.cpp --- a/src/Gui/KSMainWindow.cpp +++ b/src/Gui/KSMainWindow.cpp @@ -122,9 +122,10 @@ QPoint location = guiConfig.readEntry("window-position", QPoint(50, 50)); move(location); - // change window title on save + // change window title on save and on autosave connect(ExportManager::instance(), &ExportManager::imageSaved, this, &KSMainWindow::imageSaved); + connect(ExportManager::instance(), &ExportManager::imageSavedAndCopied, this, &KSMainWindow::imageSavedAndCopied); // the KSGWidget @@ -484,6 +485,17 @@ MessageDuration::AutoHide, {openContaining}); } +void KSMainWindow::imageSavedAndCopied(const QUrl &location) +{ + setWindowTitle(location.fileName()); + setWindowModified(false); + QAction* openContaining = new QAction(QIcon::fromTheme(QStringLiteral("document-open-folder")), i18n("Open Containing Folder"), mMessageWidget); + connect(openContaining, &QAction::triggered, [=] { KIO::highlightInFileManager({location});}); + showInlineMessage(i18n("The screenshot was copied to the clipboard and saved as %2", + location.toString(), location.fileName()), KMessageWidget::Positive, + MessageDuration::AutoHide, {openContaining}); +} + void KSMainWindow::save() { SpectacleConfig::instance()->setLastUsedSaveMode(SaveMode::Save); diff --git a/src/Gui/KSWidget.cpp b/src/Gui/KSWidget.cpp --- a/src/Gui/KSWidget.cpp +++ b/src/Gui/KSWidget.cpp @@ -112,8 +112,8 @@ mCaptureTransientOnly->setEnabled(false); connect(mCaptureTransientOnly, &QCheckBox::clicked, lConfigMgr, &SpectacleConfig::setCaptureTransientWindowOnlyChecked); - mQuitAfterSaveOrCopy = new QCheckBox(i18n("Quit after Save or Copy"), this); - mQuitAfterSaveOrCopy->setToolTip(i18n("Quit Spectacle after saving or copying the image")); + mQuitAfterSaveOrCopy = new QCheckBox(i18n("Quit after manual Save or Copy"), this); + mQuitAfterSaveOrCopy->setToolTip(i18n("Quit Spectacle after manually saving or copying the image")); connect(mQuitAfterSaveOrCopy, &QCheckBox::clicked, lConfigMgr, &SpectacleConfig::setQuitAfterSaveOrCopyChecked); mContentOptionsForm = new QVBoxLayout; diff --git a/src/Gui/SettingsDialog/GeneralOptionsPage.h b/src/Gui/SettingsDialog/GeneralOptionsPage.h --- a/src/Gui/SettingsDialog/GeneralOptionsPage.h +++ b/src/Gui/SettingsDialog/GeneralOptionsPage.h @@ -48,7 +48,8 @@ QButtonGroup *mPrintKeyActionGroup; QRadioButton *mRememberAlways; QRadioButton *mRememberUntilClosed; - QButtonGroup *mAfterTakingScreenshotGroup; + QCheckBox *mCopyImageToClipboard; + QCheckBox *mAutoSaveImage; QCheckBox *mUseLightBackground; QCheckBox *mShowMagnifier; QCheckBox *mReleaseToCapture; diff --git a/src/Gui/SettingsDialog/GeneralOptionsPage.cpp b/src/Gui/SettingsDialog/GeneralOptionsPage.cpp --- a/src/Gui/SettingsDialog/GeneralOptionsPage.cpp +++ b/src/Gui/SettingsDialog/GeneralOptionsPage.cpp @@ -36,7 +36,8 @@ { QFormLayout *mainLayout = new QFormLayout(this); setLayout(mainLayout); - + + // When spectacle is running settings KTitleWidget* runningTitle = new KTitleWidget(this); runningTitle->setText(i18n("When Spectacle is Running")); runningTitle->setLevel(2); @@ -59,16 +60,16 @@ mainLayout->addItem(new QSpacerItem(0, 18, QSizePolicy::Fixed, QSizePolicy::Fixed)); - // copy file or file location to clipboard after taking a screenshot - QRadioButton *doNothing = new QRadioButton(i18n("Do nothing"), this); - QRadioButton *copyImageToClipboard = new QRadioButton(i18n("Copy image to clipboard"), this); - mAfterTakingScreenshotGroup = new QButtonGroup(this); - mAfterTakingScreenshotGroup->setExclusive(true); - mAfterTakingScreenshotGroup->addButton(doNothing, SpectacleConfig::AfterTakingScreenshotAction::DoNothing); - mAfterTakingScreenshotGroup->addButton(copyImageToClipboard, SpectacleConfig::AfterTakingScreenshotAction::CopyImageToClipboard); - connect(mAfterTakingScreenshotGroup, qOverload(&QButtonGroup::buttonToggled), this, &GeneralOptionsPage::markDirty); - mainLayout->addRow(i18n("After taking a screenshot:"), doNothing); - mainLayout->addRow(QString(), copyImageToClipboard); + // actions to take after taking a screenshot + mCopyImageToClipboard = new QCheckBox(i18n("Copy image to clipboard"), this); + connect(mCopyImageToClipboard, &QCheckBox::toggled, this, &GeneralOptionsPage::markDirty); + mainLayout->addRow(i18n("After taking a screenshot:"), mCopyImageToClipboard); + + mAutoSaveImage = new QCheckBox(i18n("Autosave the image to the default location"), this); + connect(mAutoSaveImage, &QCheckBox::toggled, this, &GeneralOptionsPage::markDirty); + mainLayout->addRow(QString(), mAutoSaveImage); + + mainLayout->addItem(new QSpacerItem(0, 18, QSizePolicy::Fixed, QSizePolicy::Fixed)); // Rectangular Region settings KTitleWidget *titleWidget = new KTitleWidget(this); @@ -127,7 +128,8 @@ cfgManager->setShowMagnifierChecked(mShowMagnifier->checkState() == Qt::Checked); cfgManager->setUseReleaseToCaptureChecked(mReleaseToCapture->checkState() == Qt::Checked); cfgManager->setPrintKeyActionRunning(static_cast(mPrintKeyActionGroup->checkedId())); - cfgManager->setAfterTakingScreenshotAction(static_cast(mAfterTakingScreenshotGroup->checkedId())); + cfgManager->setCopyImageToClipboard(mCopyImageToClipboard->checkState() == Qt::Checked); + cfgManager->setAutoSaveImage(mAutoSaveImage->checkState() == Qt::Checked); mChangesMade = false; } @@ -142,7 +144,8 @@ mShowMagnifier->setChecked(cfgManager->showMagnifierChecked()); mReleaseToCapture->setChecked(cfgManager->useReleaseToCapture()); mPrintKeyActionGroup->button(cfgManager->printKeyActionRunning())->setChecked(true); - mAfterTakingScreenshotGroup->button(cfgManager->afterTakingScreenshotAction())->setChecked(true); + mCopyImageToClipboard->setChecked(cfgManager->copyImageToClipboard()); + mAutoSaveImage->setChecked(cfgManager->autoSaveImage()); mChangesMade = false; } 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 @@ -22,6 +22,7 @@ #include "GeneralOptionsPage.h" #include "SaveOptionsPage.h" #include "ShortcutsOptionsPage.h" +#include "TasksOptionsPage.h" #include #include @@ -62,6 +63,12 @@ addPage(shortcutOptions); mPages.insert(shortcutOptions); + KPageWidgetItem *tasksOptions = new KPageWidgetItem(new TasksOptionsPage(this), i18n("Tasks")); + tasksOptions->setHeader(i18n("Tasks")); + tasksOptions->setIcon(QIcon::fromTheme(QStringLiteral("task-new"))); + addPage(tasksOptions); + mPages.insert(tasksOptions); + connect(this, &SettingsDialog::currentPageChanged, this, &SettingsDialog::onPageChanged); } diff --git a/src/Gui/SettingsDialog/TasksOptionsPage.h b/src/Gui/SettingsDialog/TasksOptionsPage.h new file mode 100644 --- /dev/null +++ b/src/Gui/SettingsDialog/TasksOptionsPage.h @@ -0,0 +1,52 @@ +/* This file is part of Spectacle, the KDE screenshot utility + * Copyright (C) 2019 KDE + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#pragma once + +#include "SettingsPage.h" + +class KUrlRequester; +class QCheckBox; + +class TasksOptionsPage : public SettingsPage +{ + Q_OBJECT + + public: + + explicit TasksOptionsPage(QWidget *parent = nullptr); + + public Q_SLOTS: + + void saveChanges() override; + void resetChanges() override; + + private Q_SLOTS: + + void markDirty(); + + private: + + void updateFilenamePreview(); + + QCheckBox *mEnableTask; + KUrlRequester *mTaskFileLocation; +}; diff --git a/src/Gui/SettingsDialog/TasksOptionsPage.cpp b/src/Gui/SettingsDialog/TasksOptionsPage.cpp new file mode 100644 --- /dev/null +++ b/src/Gui/SettingsDialog/TasksOptionsPage.cpp @@ -0,0 +1,103 @@ +/* This file is part of Spectacle, the KDE screenshot utility + * Copyright (C) 2019 KDE + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#include "TasksOptionsPage.h" + +#include "SpectacleCommon.h" +#include "SpectacleConfig.h" +#include "ExportManager.h" + +#include +#include + +#include +#include +#include + +TasksOptionsPage::TasksOptionsPage(QWidget *parent) : + SettingsPage(parent) +{ + QFormLayout *mainLayout = new QFormLayout; + setLayout(mainLayout); + + // copy file location to clipboard after saving + mEnableTask = new QCheckBox(i18n("Run a specific script/program after the image has been saved."), this); + connect(mEnableTask, &QCheckBox::toggled, this, &TasksOptionsPage::markDirty); + mainLayout->addRow(QString(), mEnableTask); + + // Task location + mTaskFileLocation = new KUrlRequester; + mTaskFileLocation->setMode(KFile::File); + connect(mTaskFileLocation, &KUrlRequester::textChanged, this, &TasksOptionsPage::markDirty); + mainLayout->addRow(i18n("Script/program Location:"), mTaskFileLocation); + + // Info text + QString helpText = i18n( + "Spectacle passes the location of the saved image as an argument to your script/program. " + "Supported files: executables without extension, .sh, .py (requires Python installed). " + "The working directory is the one where the image is saved." + ); + QLabel *fmtHelpText = new QLabel(helpText, this); + fmtHelpText->setWordWrap(true); + fmtHelpText->setTextFormat(Qt::PlainText); + fmtHelpText->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); + mainLayout->addWidget(fmtHelpText); + + // read in the data + resetChanges(); +} + +void TasksOptionsPage::markDirty() +{ + mChangesMade = true; +} + +void TasksOptionsPage::saveChanges() +{ + // bring up the configuration reader + + SpectacleConfig *cfgManager = SpectacleConfig::instance(); + + // save the data + + cfgManager->setTaskLocation(mTaskFileLocation->url()); + cfgManager->setRunTask(mEnableTask->checkState() == Qt::Checked); + + // done + + mChangesMade = false; +} + +void TasksOptionsPage::resetChanges() +{ + // bring up the configuration reader + + SpectacleConfig *cfgManager = SpectacleConfig::instance(); + + // read in the data + + mTaskFileLocation->setUrl(cfgManager->taskLocation()); + mEnableTask->setChecked(cfgManager->runTask()); + + // done + + mChangesMade = false; +} diff --git a/src/SpectacleConfig.h b/src/SpectacleConfig.h --- a/src/SpectacleConfig.h +++ b/src/SpectacleConfig.h @@ -55,11 +55,6 @@ FocusWindow }; - enum AfterTakingScreenshotAction : int { - DoNothing = 0, - CopyImageToClipboard - }; - KActionCollection* shortCutActions; private: @@ -131,15 +126,24 @@ QUrl defaultSaveLocation() const; void setDefaultSaveLocation(const QUrl &location); - AfterTakingScreenshotAction afterTakingScreenshotAction() const; - void setAfterTakingScreenshotAction(AfterTakingScreenshotAction enabled); + bool copyImageToClipboard() const; + void setCopyImageToClipboard(bool enabled); + + bool autoSaveImage() const; + void setAutoSaveImage(bool enabled); bool copySaveLocationToClipboard() const; void setCopySaveLocationToClipboard(bool enabled); QString saveImageFormat() const; void setSaveImageFormat(const QString &saveFmt); + bool runTask() const; + void setRunTask(bool enabled); + + QUrl taskLocation() const; + void setTaskLocation(const QUrl &location); + PrintKeyActionRunning printKeyActionRunning() const; void setPrintKeyActionRunning (PrintKeyActionRunning action); diff --git a/src/SpectacleConfig.cpp b/src/SpectacleConfig.cpp --- a/src/SpectacleConfig.cpp +++ b/src/SpectacleConfig.cpp @@ -360,15 +360,24 @@ // copy file to clipboard after the screenshot has been made -SpectacleConfig::AfterTakingScreenshotAction SpectacleConfig::afterTakingScreenshotAction() const +bool SpectacleConfig::copyImageToClipboard() const { - int doNothing = static_cast(SpectacleConfig::AfterTakingScreenshotAction::DoNothing); - return static_cast(mGeneralConfig.readEntry(QStringLiteral("afterTakingScreenshot"), doNothing)); + return mGeneralConfig.readEntry(QStringLiteral("copyImageToClipboard"), false); } -void SpectacleConfig::setAfterTakingScreenshotAction (SpectacleConfig::AfterTakingScreenshotAction action) +void SpectacleConfig::setCopyImageToClipboard (bool enabled) { - mGeneralConfig.writeEntry(QStringLiteral("afterTakingScreenshot"), static_cast(action)); + mGeneralConfig.writeEntry(QStringLiteral("copyImageToClipboard"), enabled); + mGeneralConfig.sync(); +} + +bool SpectacleConfig::autoSaveImage() const +{ + return mGeneralConfig.readEntry(QStringLiteral("autoSaveImage"), false); +} +void SpectacleConfig::setAutoSaveImage(bool enabled) +{ + mGeneralConfig.writeEntry(QStringLiteral("autoSaveImage"), enabled); mGeneralConfig.sync(); } @@ -399,6 +408,32 @@ mGeneralConfig.sync(); } +// enable task + +bool SpectacleConfig::runTask() const +{ + return mGeneralConfig.readEntry(QStringLiteral("runTask"), false); +} + +void SpectacleConfig::setRunTask(bool enabled){ + mGeneralConfig.writeEntry(QStringLiteral("runTask"), enabled); + mGeneralConfig.sync(); +} + +// task location + +QUrl SpectacleConfig::taskLocation() const +{ + QString path = mGeneralConfig.readEntry(QStringLiteral("taskFileLocation"), QString()); + return QUrl::fromUserInput(path); +} + +void SpectacleConfig::setTaskLocation(const QUrl &location) +{ + mGeneralConfig.writePathEntry(QStringLiteral("taskFileLocation"), location.toString()); + mGeneralConfig.sync(); +} + SpectacleConfig::PrintKeyActionRunning SpectacleConfig::printKeyActionRunning() const { mConfig->reparseConfiguration(); diff --git a/src/SpectacleCore.h b/src/SpectacleCore.h --- a/src/SpectacleCore.h +++ b/src/SpectacleCore.h @@ -74,6 +74,7 @@ void doStartDragAndDrop(); void doNotify(const QUrl &theSavedAt); void doCopyPath(const QUrl &savedAt); + void doTask(const QUrl &savedAt); private: diff --git a/src/SpectacleCore.cpp b/src/SpectacleCore.cpp --- a/src/SpectacleCore.cpp +++ b/src/SpectacleCore.cpp @@ -52,8 +52,7 @@ mPlatform(loadPlatform()), mMainWindow(nullptr), mIsGuiInited(false), - mCopySaveLocationToClipboard(theCopyToClipboard), - mCopyImageToClipboard(false) + mCopySaveLocationToClipboard(theCopyToClipboard) { auto lConfig = KSharedConfig::openConfig(QStringLiteral("spectaclerc")); KConfigGroup lGuiConfig(lConfig, "GuiConfig"); @@ -88,6 +87,8 @@ connect(lExportManager, &ExportManager::errorMessage, this, &SpectacleCore::showErrorMessage); connect(lExportManager, &ExportManager::imageSaved, this, &SpectacleCore::doCopyPath); connect(lExportManager, &ExportManager::forceNotify, this, &SpectacleCore::doNotify); + connect(lExportManager, &ExportManager::imageSaved, this, &SpectacleCore::doTask); + connect(lExportManager, &ExportManager::imageSavedAndCopied, this, &SpectacleCore::doTask); connect(mPlatform.get(), &Platform::windowTitleChanged, lExportManager, &ExportManager::setWindowTitle); switch (theStartMode) { @@ -265,18 +266,19 @@ case StartMode::Gui: mMainWindow->setScreenshotAndShow(thePixmap); - mCopyImageToClipboard = (SpectacleConfig::instance()->afterTakingScreenshotAction() == 1); - using Actions = SpectacleConfig::AfterTakingScreenshotAction; - switch (SpectacleConfig::instance()->afterTakingScreenshotAction()) { - case Actions::DoNothing: - break; - case Actions::CopyImageToClipboard: - { - lExportManager->doCopyToClipboard(false); - mMainWindow->showInlineMessage(i18n("The screenshot has been copied to the clipboard."), - KMessageWidget::Information); - } - break; + bool autoSaveImage = SpectacleConfig::instance()->autoSaveImage(); + bool copyImageToClipboard = SpectacleConfig::instance()->copyImageToClipboard(); + + if (autoSaveImage && copyImageToClipboard) { + QUrl lSavePath = QUrl(); + lExportManager->doSaveAndCopy(lSavePath); + } else if (autoSaveImage) { + QUrl lSavePath = QUrl(); + lExportManager->doSave(lSavePath); + } else if (copyImageToClipboard) { + lExportManager->doCopyToClipboard(false); + mMainWindow->showInlineMessage(i18n("The screenshot has been copied to the clipboard."), + KMessageWidget::Information); } } } @@ -367,6 +369,31 @@ } } +void SpectacleCore::doTask(const QUrl &savedAt) +{ + if (SpectacleConfig::instance()->runTask()) { + QUrl taskLocationUrl = SpectacleConfig::instance()->taskLocation(); + if (!taskLocationUrl.isValid() || !taskLocationUrl.isLocalFile()) { + return; + } + QString taskLocationString = taskLocationUrl.toString(); + QProcess taskProcess; + taskProcess.setArguments(QStringList() << taskLocationUrl.path(QUrl::FullyEncoded) << savedAt.toString()); + taskProcess.setWorkingDirectory(savedAt.path(QUrl::FullyEncoded | QUrl::RemoveFilename)); + + if (taskLocationString.endsWith(QStringLiteral(".sh"))) { + QString userShell = QString::fromUtf8(qgetenv("SHELL")); + taskProcess.setProgram(userShell); + } else if (taskLocationString.endsWith(QStringLiteral(".py"))) { + taskProcess.setProgram(QStringLiteral("python")); + } else { + taskProcess.setProgram(taskLocationString); + } + + taskProcess.startDetached(); + } +} + void SpectacleCore::doStartDragAndDrop() { auto lExportManager = ExportManager::instance();