diff --git a/src/Gui/KSMainWindow.h b/src/Gui/KSMainWindow.h index 62b9dc3..43e21c6 100644 --- a/src/Gui/KSMainWindow.h +++ b/src/Gui/KSMainWindow.h @@ -1,116 +1,117 @@ /* This file is part of Spectacle, the KDE screenshot utility * Copyright (C) 2015 Boudhayan Gupta * * 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 #include #include #include #include #include #include #include "SpectacleCommon.h" #include "SpectacleConfig.h" #include "KSWidget.h" #include "ExportMenu.h" #include "Platforms/Platform.h" #include class KSMainWindow: public QDialog { Q_OBJECT public: explicit KSMainWindow(const Platform::GrabModes &theGrabModes, const Platform::ShutterModes &theShutterModes, QWidget *parent = nullptr); virtual ~KSMainWindow() = default; + enum class MessageDuration { + AutoHide, + Persistent + }; + + void showInlineMessage(const QString& message, + const KMessageWidget::MessageType messageType, + const MessageDuration messageDuration = MessageDuration::AutoHide, + const QList& actions = {}); + private: enum class QuitBehavior { QuitImmediately, QuitExternally }; void quit(const QuitBehavior quitBehavior = QuitBehavior::QuitImmediately); - enum class MessageDuration { - AutoHide, - Persistent - }; - void showInlineMessage(const QString& message, - const KMessageWidget::MessageType messageType, - const MessageDuration messageDuration = MessageDuration::AutoHide, - const QList& actions = {}); - private Q_SLOTS: void captureScreenshot(Spectacle::CaptureMode theCaptureMode, int theTimeout, bool theIncludePointer, bool theIncludeDecorations); void showPrintDialog(); void openScreenshotsFolder(); void showPreferencesDialog(); void showImageSharedFeedback(bool error, const QString &message); void sendToClipboard(); void init(); void setDefaultSaveAction(); void save(); void saveAs(); int windowWidth(const QPixmap &pixmap) const; void restoreWindowTitle(); public Q_SLOTS: void setScreenshotAndShow(const QPixmap &pixmap); void imageSaved(const QUrl &location); Q_SIGNALS: void newScreenshotRequest(Spectacle::CaptureMode theCaptureMode, int theTimeout, bool theIncludePointer, bool theIncludeDecorations); void dragAndDropRequest(); protected: void moveEvent(QMoveEvent *event) override; private: KSWidget *mKSWidget; QFrame *mDivider; QDialogButtonBox *mDialogButtonBox; QToolButton *mConfigureButton; QPushButton *mToolsButton; QPushButton *mSendToButton; QToolButton *mClipboardButton; QToolButton *mSaveButton; QMenu *mSaveMenu; QAction *mSaveAsAction; QAction *mSaveAction; KMessageWidget *mMessageWidget; QMenu *mToolsMenu; QMenu *mScreenRecorderToolsMenu; std::unique_ptr mScreenrecorderToolsMenuFactory; ExportMenu *mExportMenu; Platform::ShutterModes mShutterModes; QTimer *mHideMessageWidgetTimer; }; diff --git a/src/Gui/SettingsDialog/GeneralOptionsPage.cpp b/src/Gui/SettingsDialog/GeneralOptionsPage.cpp index 62a8854..09c4607 100644 --- a/src/Gui/SettingsDialog/GeneralOptionsPage.cpp +++ b/src/Gui/SettingsDialog/GeneralOptionsPage.cpp @@ -1,133 +1,148 @@ /* * Copyright (C) 2015 Boudhayan Gupta * * 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. */ #include "GeneralOptionsPage.h" #include "SpectacleConfig.h" #include #include #include #include #include #include #include #include GeneralOptionsPage::GeneralOptionsPage(QWidget *parent) : SettingsPage(parent) { QFormLayout *mainLayout = new QFormLayout(this); setLayout(mainLayout); KTitleWidget* runningTitle = new KTitleWidget(this); runningTitle->setText(i18n("When Spectacle is Running")); runningTitle->setLevel(2); mainLayout->addRow(runningTitle); QRadioButton* takeNew = new QRadioButton(i18n("Take a new screenshot"), this); QRadioButton* startNewInstance = new QRadioButton(i18n("Open a new Spectacle window"), this); mPrintKeyActionGroup = new QButtonGroup(this); mPrintKeyActionGroup->setExclusive(true); mPrintKeyActionGroup->addButton(takeNew, SpectacleConfig::PrintKeyActionRunning::TakeNewScreenshot); mPrintKeyActionGroup->addButton(startNewInstance, SpectacleConfig::PrintKeyActionRunning::StartNewInstance); connect( mPrintKeyActionGroup, qOverload(&QButtonGroup::buttonToggled), this, &GeneralOptionsPage::markDirty); mainLayout->addRow(i18n("Press screenshot key to:"), takeNew); mainLayout->addRow(QString(), startNewInstance); //On Wayland we can't programmatically raise and focus the window so we have to hide the option if (!(KWindowSystem::isPlatformWayland() || qstrcmp(qgetenv("XDG_SESSION_TYPE"), "wayland") == 0)) { QRadioButton* focusWindow = new QRadioButton(i18n("Return focus to Spectacle"), this); mPrintKeyActionGroup->addButton( focusWindow, SpectacleConfig::PrintKeyActionRunning::FocusWindow); mainLayout->addRow(QString(), focusWindow); } + 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); + // Rectangular Region settings KTitleWidget *titleWidget = new KTitleWidget(this); titleWidget->setText(i18n("Rectangular Region")); titleWidget->setLevel(2); mainLayout->addRow(titleWidget); // use light background mUseLightBackground = new QCheckBox(i18n("Use light background"), this); connect(mUseLightBackground, &QCheckBox::toggled, this, &GeneralOptionsPage::markDirty); mainLayout->addRow(i18n("General:"), mUseLightBackground); // show magnifier mShowMagnifier = new QCheckBox(i18n("Show magnifier"), this); connect(mShowMagnifier, &QCheckBox::toggled, this, &GeneralOptionsPage::markDirty); mainLayout->addRow(QString(), mShowMagnifier); // release mouse-button to capture mReleaseToCapture = new QCheckBox(i18n("Accept on click-and-release"), this); connect(mReleaseToCapture, &QCheckBox::toggled, this, &GeneralOptionsPage::markDirty); mainLayout->addRow(QString(), mReleaseToCapture); mainLayout->addItem(new QSpacerItem(0, 18, QSizePolicy::Fixed, QSizePolicy::Fixed)); // remember Rectangular Region box QButtonGroup* rememberGroup = new QButtonGroup(this); rememberGroup->setExclusive(true); QRadioButton* neverButton = new QRadioButton(i18n("Never"), this); mRememberAlways = new QRadioButton(i18n("Always"), this); mRememberUntilClosed = new QRadioButton(i18n("Until Spectacle is closed"), this); rememberGroup->addButton(neverButton); rememberGroup->addButton(mRememberAlways); rememberGroup->addButton(mRememberUntilClosed); neverButton->setChecked(true); connect(rememberGroup, qOverload(&QButtonGroup::buttonToggled), this, &GeneralOptionsPage::markDirty); mainLayout->addRow(i18n("Remember selected area:"), neverButton); mainLayout->addRow(QString(), mRememberAlways); mainLayout->addRow(QString(), mRememberUntilClosed ); // read in the data resetChanges(); } void GeneralOptionsPage::markDirty() { mChangesMade = true; } void GeneralOptionsPage::saveChanges() { SpectacleConfig *cfgManager = SpectacleConfig::instance(); cfgManager->setUseLightRegionMaskColour(mUseLightBackground->checkState() == Qt::Checked); cfgManager->setRememberLastRectangularRegion(mRememberUntilClosed->isChecked() || mRememberAlways->isChecked()); cfgManager->setAlwaysRememberRegion (mRememberAlways->isChecked()); cfgManager->setShowMagnifierChecked(mShowMagnifier->checkState() == Qt::Checked); cfgManager->setUseReleaseToCaptureChecked(mReleaseToCapture->checkState() == Qt::Checked); cfgManager->setPrintKeyActionRunning(static_cast(mPrintKeyActionGroup->checkedId())); + cfgManager->setAfterTakingScreenshotAction(static_cast(mAfterTakingScreenshotGroup->checkedId())); mChangesMade = false; } void GeneralOptionsPage::resetChanges() { SpectacleConfig *cfgManager = SpectacleConfig::instance(); mUseLightBackground->setChecked(cfgManager->useLightRegionMaskColour()); mRememberUntilClosed->setChecked(cfgManager->rememberLastRectangularRegion()); mRememberAlways->setChecked(cfgManager->alwaysRememberRegion()); mShowMagnifier->setChecked(cfgManager->showMagnifierChecked()); mReleaseToCapture->setChecked(cfgManager->useReleaseToCapture()); mPrintKeyActionGroup->button(cfgManager->printKeyActionRunning())->setChecked(true); + mAfterTakingScreenshotGroup->button(cfgManager->afterTakingScreenshotAction())->setChecked(true); mChangesMade = false; } diff --git a/src/Gui/SettingsDialog/GeneralOptionsPage.h b/src/Gui/SettingsDialog/GeneralOptionsPage.h index a42eccf..f334040 100644 --- a/src/Gui/SettingsDialog/GeneralOptionsPage.h +++ b/src/Gui/SettingsDialog/GeneralOptionsPage.h @@ -1,56 +1,57 @@ /* * Copyright (C) 2015 Boudhayan Gupta * * 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. */ #ifndef GENERALOPTIONSPAGE_H #define GENERALOPTIONSPAGE_H #include "SettingsPage.h" class QButtonGroup; class QCheckBox; class QRadioButton; class GeneralOptionsPage : public SettingsPage { Q_OBJECT public: explicit GeneralOptionsPage(QWidget *parent = nullptr); public Q_SLOTS: void saveChanges() override; void resetChanges() override; private Q_SLOTS: void markDirty(); private: - QButtonGroup* mPrintKeyActionGroup; - QRadioButton* mRememberAlways; - QRadioButton* mRememberUntilClosed; + QButtonGroup *mPrintKeyActionGroup; + QRadioButton *mRememberAlways; + QRadioButton *mRememberUntilClosed; + QButtonGroup *mAfterTakingScreenshotGroup; QCheckBox *mUseLightBackground; QCheckBox *mShowMagnifier; QCheckBox *mReleaseToCapture; }; #endif // GENERALOPTIONSPAGE_H diff --git a/src/Gui/SettingsDialog/SaveOptionsPage.cpp b/src/Gui/SettingsDialog/SaveOptionsPage.cpp index 6c2f17f..bdab075 100644 --- a/src/Gui/SettingsDialog/SaveOptionsPage.cpp +++ b/src/Gui/SettingsDialog/SaveOptionsPage.cpp @@ -1,224 +1,223 @@ /* * Copyright (C) 2015 Boudhayan Gupta * * 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. */ #include "SaveOptionsPage.h" #include "SpectacleCommon.h" #include "SpectacleConfig.h" #include "ExportManager.h" #include #include #include #include #include #include #include #include #include SaveOptionsPage::SaveOptionsPage(QWidget *parent) : SettingsPage(parent) { QFormLayout *mainLayout = new QFormLayout; setLayout(mainLayout); // Save location mUrlRequester = new KUrlRequester; mUrlRequester->setMode(KFile::Directory); connect(mUrlRequester, &KUrlRequester::textChanged, this, &SaveOptionsPage::markDirty); mainLayout->addRow(i18n("Save Location:"), mUrlRequester); // copy file location to clipboard after saving mCopyPathToClipboard = new QCheckBox(i18n("Copy file location to clipboard after saving"), this); connect(mCopyPathToClipboard, &QCheckBox::toggled, this, &SaveOptionsPage::markDirty); mainLayout->addRow(QString(), mCopyPathToClipboard); - mainLayout->addItem(new QSpacerItem(0, 18, QSizePolicy::Fixed, QSizePolicy::Fixed)); // Compression quality slider and current value display QHBoxLayout *sliderHorizLayout = new QHBoxLayout(); QVBoxLayout *sliderVertLayout = new QVBoxLayout(); // Current value QLabel *qualityValue = new QLabel(); qualityValue->setNum(SpectacleConfig::instance()->compressionQuality()); qualityValue->setMinimumWidth(qualityValue->fontInfo().pointSize()*3); // Slider mQualitySlider = new QSlider(Qt::Horizontal); mQualitySlider->setRange(0, 100); mQualitySlider->setTickInterval(5); mQualitySlider->setSliderPosition(SpectacleConfig::instance()->compressionQuality()); mQualitySlider->setTickPosition(QSlider::TicksBelow); mQualitySlider->setTracking(true); connect(mQualitySlider, &QSlider::valueChanged, this, [=](int value) { qualityValue->setNum(value); markDirty(); }); sliderHorizLayout->addWidget(mQualitySlider); sliderHorizLayout->addWidget(qualityValue); sliderVertLayout->addLayout(sliderHorizLayout); QLabel *qualitySliderDescription = new QLabel(); qualitySliderDescription->setText(i18n("Choose the image quality when saving with lossy image formats like JPEG")); sliderVertLayout->addWidget(qualitySliderDescription); mainLayout->addRow(i18n("Compression Quality:"), sliderVertLayout); mainLayout->addItem(new QSpacerItem(0, 18, QSizePolicy::Fixed, QSizePolicy::Fixed)); // filename chooser text field QHBoxLayout *saveFieldLayout = new QHBoxLayout; mSaveNameFormat = new QLineEdit; connect(mSaveNameFormat, &QLineEdit::textEdited, this, &SaveOptionsPage::markDirty); connect(mSaveNameFormat, &QLineEdit::textEdited, this, [&](const QString &newText) { QString fmt; const auto imageFormats = QImageWriter::supportedImageFormats(); for (const auto &item : imageFormats) { fmt = QString::fromLocal8Bit(item); if (newText.endsWith(QLatin1Char('.') + fmt, Qt::CaseInsensitive)) { QString txtCopy = newText; txtCopy.chop(fmt.length() + 1); mSaveNameFormat->setText(txtCopy); mSaveImageFormat->setCurrentIndex(mSaveImageFormat->findText(fmt.toUpper())); } } }); connect(mSaveNameFormat, &QLineEdit::textChanged,this, &SaveOptionsPage::updateFilenamePreview); mSaveNameFormat->setPlaceholderText(QStringLiteral("%d")); saveFieldLayout->addWidget(mSaveNameFormat); mSaveImageFormat = new QComboBox; mSaveImageFormat->addItems([&](){ QStringList items; const auto formats = QImageWriter::supportedImageFormats(); for (const auto &fmt : formats) { items.append(QString::fromLocal8Bit(fmt).toUpper()); } return items; }()); connect(mSaveImageFormat, &QComboBox::currentTextChanged, this, &SaveOptionsPage::markDirty); connect(mSaveImageFormat, &QComboBox::currentTextChanged, this, &SaveOptionsPage::updateFilenamePreview); saveFieldLayout->addWidget(mSaveImageFormat); mainLayout->addRow(i18n("Filename:"), saveFieldLayout); mPreviewLabel = new QLabel(this); mainLayout->addRow(i18nc("Preview of the user configured filename", "Preview:"), mPreviewLabel); // now the save filename format layout QString helpText = i18n( "You can use the following placeholders in the filename, which will be replaced " "with actual text when the file is saved:
" ); for (auto option = ExportManager::filenamePlaceholders.cbegin(); option != ExportManager::filenamePlaceholders.cend(); ++option) { helpText += QStringLiteral("%1: %2
").arg(option.key(), option.value().toString()); } helpText += QStringLiteral("/: ") + i18n("To save to a sub-folder"); helpText += QStringLiteral("
"); QLabel *fmtHelpText = new QLabel(helpText, this); fmtHelpText->setWordWrap(true); fmtHelpText->setTextFormat(Qt::RichText); fmtHelpText->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); connect(fmtHelpText, &QLabel::linkActivated, this, [this](const QString& placeholder) { mSaveNameFormat->insert(placeholder); }); mainLayout->addWidget(fmtHelpText); // read in the data resetChanges(); } void SaveOptionsPage::markDirty() { mChangesMade = true; } void SaveOptionsPage::saveChanges() { // bring up the configuration reader SpectacleConfig *cfgManager = SpectacleConfig::instance(); // save the data cfgManager->setDefaultSaveLocation(mUrlRequester->url()); cfgManager->setAutoSaveFilenameFormat(mSaveNameFormat->text()); cfgManager->setSaveImageFormat(mSaveImageFormat->currentText().toLower()); cfgManager->setCopySaveLocationToClipboard(mCopyPathToClipboard->checkState() == Qt::Checked); cfgManager->setCompressionQuality(mQualitySlider->value()); // done mChangesMade = false; } void SaveOptionsPage::resetChanges() { // bring up the configuration reader SpectacleConfig *cfgManager = SpectacleConfig::instance(); // read in the data mSaveNameFormat->setText(cfgManager->autoSaveFilenameFormat()); mUrlRequester->setUrl(cfgManager->defaultSaveLocation()); mCopyPathToClipboard->setChecked(cfgManager->copySaveLocationToClipboard()); mQualitySlider->setSliderPosition(cfgManager->compressionQuality()); // read in the save image format and calculate its index { int index = mSaveImageFormat->findText(cfgManager->saveImageFormat().toUpper()); if (index >= 0) { mSaveImageFormat->setCurrentIndex(index); } } // done mChangesMade = false; } void SaveOptionsPage::updateFilenamePreview() { auto lExportManager = ExportManager::instance(); lExportManager->setWindowTitle(QStringLiteral("Spectacle")); Spectacle::CaptureMode lOldMode = lExportManager->captureMode(); // If the grabMode is not one of those below we need to change it to have the placeholder // replaced by the window title bool lSwitchGrabMode = !(lOldMode == Spectacle::CaptureMode::ActiveWindow || lOldMode == Spectacle::CaptureMode::TransientWithParent || lOldMode == Spectacle::CaptureMode::WindowUnderCursor); if (lSwitchGrabMode) { lExportManager->setCaptureMode(Spectacle::CaptureMode::ActiveWindow); } const QString lFileName = lExportManager->formatFilename(mSaveNameFormat->text()); mPreviewLabel->setText(xi18nc("@info", "%1.%2", lFileName, mSaveImageFormat->currentText().toLower())); if (lSwitchGrabMode) { lExportManager->setCaptureMode(lOldMode); } } diff --git a/src/SpectacleConfig.cpp b/src/SpectacleConfig.cpp index 169a0a6..ce5f4e3 100644 --- a/src/SpectacleConfig.cpp +++ b/src/SpectacleConfig.cpp @@ -1,404 +1,418 @@ /* * Copyright (C) 2015 Boudhayan Gupta * * 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. */ #include "SpectacleConfig.h" #include #include SpectacleConfig::SpectacleConfig(QObject *parent) : QObject(parent) { mConfig = KSharedConfig::openConfig(QStringLiteral("spectaclerc")); mGeneralConfig = KConfigGroup(mConfig, "General"); mGuiConfig = KConfigGroup(mConfig, "GuiConfig"); shortCutActions = new KActionCollection(this); //everything here is named to match the jumplist actions in our .desktop file shortCutActions->setComponentName(QStringLiteral("org.kde.spectacle.desktop")); //qdbus org.kde.kglobalaccel /component/org_kde_spectacle_desktop org.kde.kglobalaccel.Component.shortcutNames // ActiveWindowScreenShot // CurrentMonitorScreenShot // RectangularRegionScreenShot // FullScreenScreenShot // _launch { QAction *action = new QAction(i18n("Launch Spectacle")); action->setObjectName(QStringLiteral("_launch")); shortCutActions->addAction(action->objectName(), action); } { QAction *action = new QAction(i18n("Capture Entire Desktop")); action->setObjectName(QStringLiteral("FullScreenScreenShot")); shortCutActions->addAction(action->objectName(), action); } { QAction *action = new QAction(i18n("Capture Current Monitor")); action->setObjectName(QStringLiteral("CurrentMonitorScreenShot")); shortCutActions->addAction(action->objectName(), action); } { QAction *action = new QAction(i18n("Capture Active Window")); action->setObjectName(QStringLiteral("ActiveWindowScreenShot")); shortCutActions->addAction(action->objectName(), action); } { QAction *action = new QAction(i18n("Capture Rectangular Region")); action->setObjectName(QStringLiteral("RectangularRegionScreenShot")); shortCutActions->addAction(action->objectName(), action); } } SpectacleConfig::~SpectacleConfig() {} SpectacleConfig* SpectacleConfig::instance() { static SpectacleConfig instance; return &instance; } QString SpectacleConfig::defaultFilename() const { return QStringLiteral("Screenshot"); } QString SpectacleConfig::defaultTimestampTemplate() const { // includes separator at the front return QStringLiteral("_%Y%M%D_%H%m%S"); } // lastSaveAsLocation QUrl SpectacleConfig::lastSaveAsFile() const { return mGeneralConfig.readEntry(QStringLiteral("lastSaveAsFile"), this->defaultSaveLocation()); } void SpectacleConfig::setLastSaveAsFile(const QUrl &location) { mGeneralConfig.writeEntry(QStringLiteral("lastSaveAsFile"), location); mGeneralConfig.sync(); } QUrl SpectacleConfig::lastSaveAsLocation() const { return this->lastSaveAsFile().adjusted(QUrl::RemoveFilename); } // lastSaveLocation QUrl SpectacleConfig::lastSaveFile() const { return mGeneralConfig.readEntry(QStringLiteral("lastSaveFile"), this->defaultSaveLocation()); } void SpectacleConfig::setLastSaveFile(const QUrl &location) { mGeneralConfig.writeEntry(QStringLiteral("lastSaveFile"), location); mGeneralConfig.sync(); } QUrl SpectacleConfig::lastSaveLocation() const { return this->lastSaveFile().adjusted(QUrl::RemoveFilename); } // cropRegion QRect SpectacleConfig::cropRegion() const { return mGuiConfig.readEntry(QStringLiteral("cropRegion"), QRect()); } void SpectacleConfig::setCropRegion(const QRect ®ion) { mGuiConfig.writeEntry(QStringLiteral("cropRegion"), region); mGuiConfig.sync(); } // onclick bool SpectacleConfig::onClickChecked() const { return mGuiConfig.readEntry(QStringLiteral("onClickChecked"), false); } void SpectacleConfig::setOnClickChecked(bool enabled) { mGuiConfig.writeEntry(QStringLiteral("onClickChecked"), enabled); mGuiConfig.sync(); } // include pointer bool SpectacleConfig::includePointerChecked() const { return mGuiConfig.readEntry(QStringLiteral("includePointer"), true); } void SpectacleConfig::setIncludePointerChecked(bool enabled) { mGuiConfig.writeEntry(QStringLiteral("includePointer"), enabled); mGuiConfig.sync(); } // include decorations bool SpectacleConfig::includeDecorationsChecked() const { return mGuiConfig.readEntry(QStringLiteral("includeDecorations"), true); } void SpectacleConfig::setIncludeDecorationsChecked(bool enabled) { mGuiConfig.writeEntry(QStringLiteral("includeDecorations"), enabled); mGuiConfig.sync(); } // capture transient window only bool SpectacleConfig::captureTransientWindowOnlyChecked() const { return mGuiConfig.readEntry(QStringLiteral("transientOnly"), false); } void SpectacleConfig::setCaptureTransientWindowOnlyChecked(bool enabled) { mGuiConfig.writeEntry(QStringLiteral("transientOnly"), enabled); mGuiConfig.sync(); } // quit after saving, copying, or exporting the image bool SpectacleConfig::quitAfterSaveOrCopyChecked() const { return mGuiConfig.readEntry(QStringLiteral("quitAfterSaveCopyExport"), false); } void SpectacleConfig::setQuitAfterSaveOrCopyChecked(bool enabled) { mGuiConfig.writeEntry(QStringLiteral("quitAfterSaveCopyExport"), enabled); mGuiConfig.sync(); } // show magnifier bool SpectacleConfig::showMagnifierChecked() const { return mGuiConfig.readEntry(QStringLiteral("showMagnifier"), false); } void SpectacleConfig::setShowMagnifierChecked(bool enabled) { mGuiConfig.writeEntry(QStringLiteral("showMagnifier"), enabled); mGuiConfig.sync(); } // release mouse-button to capture bool SpectacleConfig::useReleaseToCapture() const { return mGuiConfig.readEntry(QStringLiteral("useReleaseToCapture"), false); } void SpectacleConfig::setUseReleaseToCaptureChecked(bool enabled) { mGuiConfig.writeEntry(QStringLiteral("useReleaseToCapture"), enabled); mGuiConfig.sync(); } // capture delay qreal SpectacleConfig::captureDelay() const { return mGuiConfig.readEntry(QStringLiteral("captureDelay"), 0.0); } void SpectacleConfig::setCaptureDelay(qreal delay) { mGuiConfig.writeEntry(QStringLiteral("captureDelay"), delay); mGuiConfig.sync(); } // capture mode int SpectacleConfig::captureMode() const { return std::max(0, mGuiConfig.readEntry(QStringLiteral("captureModeIndex"), 0)); } void SpectacleConfig::setCaptureMode(int index) { mGuiConfig.writeEntry(QStringLiteral("captureModeIndex"), index); mGuiConfig.sync(); } // remember last rectangular region bool SpectacleConfig::rememberLastRectangularRegion() const { return mGuiConfig.readEntry(QStringLiteral("rememberLastRectangularRegion"), true); } void SpectacleConfig::setRememberLastRectangularRegion(bool enabled) { mGuiConfig.writeEntry(QStringLiteral("rememberLastRectangularRegion"), enabled); mGuiConfig.sync(); } bool SpectacleConfig::alwaysRememberRegion() const { // Default Value is for compatibility reasons as the old behavior was always to remember across restarts bool useOldBehavior = mGuiConfig.readEntry(QStringLiteral("rememberLastRectangularRegion"), false); return mGuiConfig.readEntry(QStringLiteral("alwaysRememberRegion"), useOldBehavior); } void SpectacleConfig::setAlwaysRememberRegion (bool enabled) { mGuiConfig.writeEntry(QStringLiteral("alwaysRememberRegion"), enabled); mGuiConfig.sync(); } // use light region mask colour bool SpectacleConfig::useLightRegionMaskColour() const { return mGuiConfig.readEntry(QStringLiteral("useLightMaskColour"), false); } void SpectacleConfig::setUseLightRegionMaskColour(bool enabled) { mGuiConfig.writeEntry(QStringLiteral("useLightMaskColour"), enabled); mGuiConfig.sync(); } // compression quality setting int SpectacleConfig::compressionQuality() const { return mGuiConfig.readEntry(QStringLiteral("compressionQuality"), 90); } void SpectacleConfig::setCompressionQuality(int value) { mGuiConfig.writeEntry(QStringLiteral("compressionQuality"), value); mGuiConfig.sync(); } // last used save mode SaveMode SpectacleConfig::lastUsedSaveMode() const { switch (mGuiConfig.readEntry(QStringLiteral("lastUsedSaveMode"), 0)) { default: case 0: return SaveMode::SaveAs; case 1: return SaveMode::Save; } } void SpectacleConfig::setLastUsedSaveMode(SaveMode mode) { mGuiConfig.writeEntry(QStringLiteral("lastUsedSaveMode"), static_cast(mode)); mGuiConfig.sync(); } // autosave filename format QString SpectacleConfig::autoSaveFilenameFormat() const { const QString sff = mGeneralConfig.readEntry(QStringLiteral("save-filename-format"), QString(defaultFilename() + defaultTimestampTemplate())); return sff.isEmpty() ? QStringLiteral("%d") : sff; } void SpectacleConfig::setAutoSaveFilenameFormat(const QString &format) { mGeneralConfig.writeEntry(QStringLiteral("save-filename-format"), format); mGeneralConfig.sync(); } // autosave location QUrl SpectacleConfig::defaultSaveLocation() const { QString path = mGeneralConfig.readPathEntry(QStringLiteral("default-save-location"), (QStandardPaths::writableLocation(QStandardPaths::PicturesLocation))); if (! path.endsWith(QLatin1Char('/'))) { path += QLatin1Char('/'); } return QUrl::fromUserInput(path); } void SpectacleConfig::setDefaultSaveLocation(const QUrl &location) { mGeneralConfig.writePathEntry(QStringLiteral("default-save-location"), location.toString()); mGeneralConfig.sync(); } +// copy file to clipboard after the screenshot has been made + +SpectacleConfig::AfterTakingScreenshotAction SpectacleConfig::afterTakingScreenshotAction() const +{ + int doNothing = static_cast(SpectacleConfig::AfterTakingScreenshotAction::DoNothing); + return static_cast(mGeneralConfig.readEntry(QStringLiteral("afterTakingScreenshot"), doNothing)); +} + +void SpectacleConfig::setAfterTakingScreenshotAction (SpectacleConfig::AfterTakingScreenshotAction action) +{ + mGeneralConfig.writeEntry(QStringLiteral("afterTakingScreenshot"), static_cast(action)); + mGeneralConfig.sync(); +} + // copy file location to clipboard after saving bool SpectacleConfig::copySaveLocationToClipboard() const { return mGeneralConfig.readEntry(QStringLiteral("copySaveLocation"), false); } void SpectacleConfig::setCopySaveLocationToClipboard(bool enabled) { mGeneralConfig.writeEntry(QStringLiteral("copySaveLocation"), enabled); mGeneralConfig.sync(); } // autosave image format QString SpectacleConfig::saveImageFormat() const { return mGeneralConfig.readEntry(QStringLiteral("default-save-image-format"), QStringLiteral("png")); } void SpectacleConfig::setSaveImageFormat(const QString &saveFmt) { mGeneralConfig.writeEntry(QStringLiteral("default-save-image-format"), saveFmt); mGeneralConfig.sync(); } SpectacleConfig::PrintKeyActionRunning SpectacleConfig::printKeyActionRunning() const { mConfig->reparseConfiguration(); int newScreenshotAction = static_cast(SpectacleConfig::PrintKeyActionRunning::TakeNewScreenshot); int readValue = mGuiConfig.readEntry(QStringLiteral("printKeyActionRunning"), newScreenshotAction); if ((KWindowSystem::isPlatformWayland() || qstrcmp(qgetenv("XDG_SESSION_TYPE"), "wayland") == 0 ) && readValue == SpectacleConfig::PrintKeyActionRunning::FocusWindow) { return SpectacleConfig::PrintKeyActionRunning::TakeNewScreenshot; } return static_cast(readValue); } void SpectacleConfig::setPrintKeyActionRunning (SpectacleConfig::PrintKeyActionRunning action) { mGuiConfig.writeEntry(QStringLiteral("printKeyActionRunning"), static_cast(action)); mGuiConfig.sync(); } diff --git a/src/SpectacleConfig.h b/src/SpectacleConfig.h index 829b7da..1e5c1fe 100644 --- a/src/SpectacleConfig.h +++ b/src/SpectacleConfig.h @@ -1,145 +1,153 @@ /* * Copyright (C) 2015 Boudhayan Gupta * * 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. */ #ifndef SPECTACLECONFIG_H #define SPECTACLECONFIG_H #include #include #include #include #include #include enum class SaveMode { SaveAs, Save }; class SpectacleConfig : public QObject { Q_OBJECT // singleton-ize the class public: static SpectacleConfig* instance(); QString defaultFilename() const; QString defaultTimestampTemplate() const; - + QUrl lastSaveAsLocation() const; QUrl lastSaveLocation() const; - enum PrintKeyActionRunning : int { + enum PrintKeyActionRunning : int { TakeNewScreenshot = 0, StartNewInstance, FocusWindow }; + enum AfterTakingScreenshotAction : int { + DoNothing = 0, + CopyImageToClipboard + }; + KActionCollection* shortCutActions; private: explicit SpectacleConfig(QObject *parent = nullptr); virtual ~SpectacleConfig(); SpectacleConfig(SpectacleConfig const&) = delete; void operator= (SpectacleConfig const&) = delete; // everything else public Q_SLOTS: QUrl lastSaveAsFile() const; void setLastSaveAsFile(const QUrl &location); QUrl lastSaveFile() const; void setLastSaveFile(const QUrl &location); QRect cropRegion() const; void setCropRegion(const QRect ®ion); bool onClickChecked() const; void setOnClickChecked(bool enabled); bool includePointerChecked() const; void setIncludePointerChecked(bool enabled); bool includeDecorationsChecked() const; void setIncludeDecorationsChecked(bool enabled); bool captureTransientWindowOnlyChecked() const; void setCaptureTransientWindowOnlyChecked(bool enabled); bool quitAfterSaveOrCopyChecked() const; void setQuitAfterSaveOrCopyChecked(bool enabled); bool showMagnifierChecked() const; void setShowMagnifierChecked(bool enabled); bool useReleaseToCapture() const; void setUseReleaseToCaptureChecked(bool enabled); qreal captureDelay() const; void setCaptureDelay(qreal delay); int captureMode() const; void setCaptureMode(int index); bool rememberLastRectangularRegion() const; void setRememberLastRectangularRegion(bool enabled); bool alwaysRememberRegion() const; void setAlwaysRememberRegion(bool enabled); bool useLightRegionMaskColour() const; void setUseLightRegionMaskColour(bool enabled); int compressionQuality() const; void setCompressionQuality(int value); SaveMode lastUsedSaveMode() const; void setLastUsedSaveMode(SaveMode mode); QString autoSaveFilenameFormat() const; void setAutoSaveFilenameFormat(const QString &format); QUrl defaultSaveLocation() const; void setDefaultSaveLocation(const QUrl &location); + AfterTakingScreenshotAction afterTakingScreenshotAction() const; + void setAfterTakingScreenshotAction(AfterTakingScreenshotAction enabled); + bool copySaveLocationToClipboard() const; void setCopySaveLocationToClipboard(bool enabled); QString saveImageFormat() const; void setSaveImageFormat(const QString &saveFmt); PrintKeyActionRunning printKeyActionRunning() const; void setPrintKeyActionRunning (PrintKeyActionRunning action); private: KSharedConfigPtr mConfig; KConfigGroup mGeneralConfig; KConfigGroup mGuiConfig; }; #endif // SPECTACLECONFIG_H diff --git a/src/SpectacleCore.cpp b/src/SpectacleCore.cpp index 4f66bd2..568ce06 100644 --- a/src/SpectacleCore.cpp +++ b/src/SpectacleCore.cpp @@ -1,411 +1,427 @@ /* * Copyright (C) 2015 Boudhayan Gupta * * 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. */ #include "SpectacleCore.h" #include "spectacle_core_debug.h" #include "Config.h" #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include SpectacleCore::SpectacleCore(StartMode theStartMode, Spectacle::CaptureMode theCaptureMode, QString &theSaveFileName, qint64 theDelayMsec, bool theNotifyOnGrab, bool theCopyToClipboard, QObject *parent) : QObject(parent), mStartMode(theStartMode), mNotify(theNotifyOnGrab), mPlatform(loadPlatform()), mMainWindow(nullptr), mIsGuiInited(false), - mCopyToClipboard(theCopyToClipboard) + mCopySaveLocationToClipboard(theCopyToClipboard), + mCopyImageToClipboard(false) { auto lConfig = KSharedConfig::openConfig(QStringLiteral("spectaclerc")); KConfigGroup lGuiConfig(lConfig, "GuiConfig"); if (!(theSaveFileName.isEmpty() || theSaveFileName.isNull())) { if (QDir::isRelativePath(theSaveFileName)) { theSaveFileName = QDir::current().absoluteFilePath(theSaveFileName); } setFilename(theSaveFileName); } // essential connections connect(this, &SpectacleCore::errorMessage, this, &SpectacleCore::showErrorMessage); connect(mPlatform.get(), &Platform::newScreenshotTaken, this, &SpectacleCore::screenshotUpdated); connect(mPlatform.get(), &Platform::newScreenshotFailed, this, &SpectacleCore::screenshotFailed); auto lImmediateAvailable = mPlatform->supportedShutterModes().testFlag(Platform::ShutterMode::Immediate); auto lOnClickAvailable = mPlatform->supportedShutterModes().testFlag(Platform::ShutterMode::OnClick); if ((!lOnClickAvailable) && (theDelayMsec < 0)) { theDelayMsec = 0; } // reset last region if it should not be remembered across restarts auto lSpectacleConfig = SpectacleConfig::instance(); if(!lSpectacleConfig->alwaysRememberRegion()) { lSpectacleConfig->setCropRegion(QRect()); } // set up the export manager auto lExportManager = ExportManager::instance(); lExportManager->setCaptureMode(theCaptureMode); connect(lExportManager, &ExportManager::errorMessage, this, &SpectacleCore::showErrorMessage); connect(lExportManager, &ExportManager::imageSaved, this, &SpectacleCore::doCopyPath); connect(lExportManager, &ExportManager::forceNotify, this, &SpectacleCore::doNotify); connect(mPlatform.get(), &Platform::windowTitleChanged, lExportManager, &ExportManager::setWindowTitle); switch (theStartMode) { case StartMode::DBus: break; case StartMode::Background: { auto lMsec = (KWindowSystem::compositingActive() ? 200 : 50) + theDelayMsec; auto lShutterMode = lImmediateAvailable ? Platform::ShutterMode::Immediate : Platform::ShutterMode::OnClick; auto lIncludePointer = lGuiConfig.readEntry("includePointer", true); auto lIncludeDecorations = lGuiConfig.readEntry("includeDecorations", true); const Platform::GrabMode lCaptureMode = toPlatformGrabMode(theCaptureMode); QTimer::singleShot(lMsec, this, [ this, lCaptureMode, lShutterMode, lIncludePointer, lIncludeDecorations ]() { mPlatform->doGrab(lShutterMode, lCaptureMode, lIncludePointer, lIncludeDecorations); }); } break; case StartMode::Gui: initGui(lGuiConfig.readEntry("includePointer", true), lGuiConfig.readEntry("includeDecorations", true)); break; } setUpShortcuts(); } void SpectacleCore::setUpShortcuts() { SpectacleConfig* config = SpectacleConfig::instance(); QAction* openAction = config->shortCutActions->action(QStringLiteral("_launch")); KGlobalAccel::self()->setGlobalShortcut(openAction, Qt::Key_Print); QAction* fullScreenAction = config->shortCutActions->action(QStringLiteral("FullScreenScreenShot")); KGlobalAccel::self()->setGlobalShortcut(fullScreenAction, Qt::SHIFT + Qt::Key_Print); QAction* activeWindowAction = config->shortCutActions->action(QStringLiteral("ActiveWindowScreenShot")); KGlobalAccel::self()->setGlobalShortcut(activeWindowAction, Qt::META + Qt::Key_Print); QAction* regionAction = config->shortCutActions->action(QStringLiteral("RectangularRegionScreenShot")); KGlobalAccel::self()->setGlobalShortcut(regionAction, Qt::META + Qt::SHIFT + Qt::Key_Print); QAction* currentScreenAction = config->shortCutActions->action(QStringLiteral("CurrentMonitorScreenShot")); KGlobalAccel::self()->setGlobalShortcut(currentScreenAction, QList()); } QString SpectacleCore::filename() const { return mFileNameString; } void SpectacleCore::setFilename(const QString &filename) { mFileNameString = filename; mFileNameUrl = QUrl::fromUserInput(filename); } // Slots void SpectacleCore::dbusStartAgent() { qApp->setQuitOnLastWindowClosed(true); auto lConfig = KSharedConfig::openConfig(QStringLiteral("spectaclerc")); KConfigGroup lGuiConfig(lConfig, "GuiConfig"); auto lIncludePointer = lGuiConfig.readEntry("includePointer", true); auto lIncludeDecorations = lGuiConfig.readEntry("includeDecorations", true); if (!(mStartMode == StartMode::Gui)) { mStartMode = StartMode::Gui; initGui(lIncludePointer, lIncludeDecorations); } else { using Actions = SpectacleConfig::PrintKeyActionRunning; switch (SpectacleConfig::instance()->printKeyActionRunning()) { case Actions::TakeNewScreenshot: { auto lShutterMode = mPlatform->supportedShutterModes().testFlag(Platform::ShutterMode::Immediate) ? Platform::ShutterMode::Immediate : Platform::ShutterMode::OnClick; auto lGrabMode = toPlatformGrabMode(ExportManager::instance()->captureMode()); QTimer::singleShot(KWindowSystem::compositingActive() ? 200 : 50, this, [this, lShutterMode, lGrabMode, lIncludePointer, lIncludeDecorations]() { mPlatform->doGrab(lShutterMode, lGrabMode, lIncludePointer, lIncludeDecorations); }); break; } case Actions::FocusWindow: if (mMainWindow->isMinimized()) { mMainWindow->setWindowState(mMainWindow->windowState() & ~Qt::WindowMinimized); } mMainWindow->activateWindow(); break; case Actions::StartNewInstance: QProcess newInstance; newInstance.setProgram(QStringLiteral("spectacle")); newInstance.startDetached(); break; } } } void SpectacleCore::takeNewScreenshot(Spectacle::CaptureMode theCaptureMode, int theTimeout, bool theIncludePointer, bool theIncludeDecorations) { ExportManager::instance()->setCaptureMode(theCaptureMode); auto lGrabMode = toPlatformGrabMode(theCaptureMode); if (theTimeout < 0) { mPlatform->doGrab(Platform::ShutterMode::OnClick, lGrabMode, theIncludePointer, theIncludeDecorations); return; } // when compositing is enabled, we need to give it enough time for the window // to disappear and all the effects are complete before we take the shot. there's // no way of knowing how long the disappearing effects take, but as per default // settings (and unless the user has set an extremely slow effect), 200 // milliseconds is a good amount of wait time. auto lMsec = KWindowSystem::compositingActive() ? 200 : 50; QTimer::singleShot(theTimeout + lMsec, this, [this, lGrabMode, theIncludePointer, theIncludeDecorations]() { mPlatform->doGrab(Platform::ShutterMode::Immediate, lGrabMode, theIncludePointer, theIncludeDecorations); }); } void SpectacleCore::showErrorMessage(const QString &theErrString) { qCDebug(SPECTACLE_CORE_LOG) << "ERROR: " << theErrString; if (mStartMode == StartMode::Gui) { KMessageBox::error(nullptr, theErrString); } } void SpectacleCore::screenshotUpdated(const QPixmap &thePixmap) { auto lExportManager = ExportManager::instance(); // if we were running in rectangular crop mode, now would be // the time to further process the image if (lExportManager->captureMode() == Spectacle::CaptureMode::RectangularRegion) { if(!mQuickEditor) { mQuickEditor = std::make_unique(thePixmap); connect(mQuickEditor.get(), &QuickEditor::grabDone, this, &SpectacleCore::screenshotUpdated); connect(mQuickEditor.get(), &QuickEditor::grabCancelled, this, &SpectacleCore::screenshotFailed); mQuickEditor->showFullScreen(); return; } else { mQuickEditor->hide(); mQuickEditor.reset(nullptr); } } lExportManager->setPixmap(thePixmap); lExportManager->updatePixmapTimestamp(); switch (mStartMode) { case StartMode::Background: case StartMode::DBus: { if (mNotify) { connect(lExportManager, &ExportManager::imageSaved, this, &SpectacleCore::doNotify); } - if (mCopyToClipboard) { + if (mCopySaveLocationToClipboard) { lExportManager->doCopyToClipboard(mNotify); } else { QUrl lSavePath = (mStartMode == StartMode::Background && mFileNameUrl.isValid() && mFileNameUrl.isLocalFile()) ? mFileNameUrl : QUrl(); lExportManager->doSave(lSavePath); } // if we notify, we emit allDone only if the user either dismissed the notification or pressed // the "Open" button, otherwise the app closes before it can react to it. if (!mNotify) { emit allDone(); } } break; 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; + } } } void SpectacleCore::screenshotFailed() { if (ExportManager::instance()->captureMode() == Spectacle::CaptureMode::RectangularRegion && mQuickEditor) { mQuickEditor->hide(); mQuickEditor.reset(nullptr); } switch (mStartMode) { case StartMode::Background: showErrorMessage(i18n("Screenshot capture canceled or failed")); emit allDone(); return; case StartMode::DBus: emit grabFailed(); emit allDone(); return; case StartMode::Gui: mMainWindow->setScreenshotAndShow(QPixmap()); } } void SpectacleCore::doNotify(const QUrl &theSavedAt) { KNotification *lNotify = new KNotification(QStringLiteral("newScreenshotSaved")); switch(ExportManager::instance()->captureMode()) { case Spectacle::CaptureMode::AllScreens: lNotify->setTitle(i18nc("The entire screen area was captured, heading", "Full Screen Captured")); break; case Spectacle::CaptureMode::CurrentScreen: lNotify->setTitle(i18nc("The current screen was captured, heading", "Current Screen Captured")); break; case Spectacle::CaptureMode::ActiveWindow: lNotify->setTitle(i18nc("The active window was captured, heading", "Active Window Captured")); break; case Spectacle::CaptureMode::WindowUnderCursor: case Spectacle::CaptureMode::TransientWithParent: lNotify->setTitle(i18nc("The window under the mouse was captured, heading", "Window Under Cursor Captured")); break; case Spectacle::CaptureMode::RectangularRegion: lNotify->setTitle(i18nc("A rectangular region was captured, heading", "Rectangular Region Captured")); break; case Spectacle::CaptureMode::InvalidChoice: break; } // a speaking message is prettier than a URL, special case for copy to clipboard and the default pictures location const QString &lSavePath = theSavedAt.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).path(); - if (mCopyToClipboard) { + if (mCopySaveLocationToClipboard) { lNotify->setText(i18n("A screenshot was saved to your clipboard.")); } else if (lSavePath == QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)) { lNotify->setText(i18nc("Placeholder is filename", "A screenshot was saved as '%1' to your Pictures folder.", theSavedAt.fileName())); } else { lNotify->setText(i18n("A screenshot was saved as '%1' to '%2'.", theSavedAt.fileName(), lSavePath)); } - if (!mCopyToClipboard) { + if (!mCopySaveLocationToClipboard) { lNotify->setUrls({theSavedAt}); lNotify->setDefaultAction(i18nc("Open the screenshot we just saved", "Open")); connect(lNotify, QOverload::of(&KNotification::activated), this, [this, theSavedAt](uint index) { if (index == 0) { new KRun(theSavedAt, nullptr); QTimer::singleShot(250, this, [this] { if (mStartMode != StartMode::Gui) { emit allDone(); } }); } }); connect(lNotify, &QObject::destroyed, this, [this] { if (mStartMode != StartMode::Gui) { emit allDone(); } }); } lNotify->sendEvent(); } void SpectacleCore::doCopyPath(const QUrl &savedAt) { if (SpectacleConfig::instance()->copySaveLocationToClipboard()) { qApp->clipboard()->setText(savedAt.toLocalFile()); } } void SpectacleCore::doStartDragAndDrop() { auto lExportManager = ExportManager::instance(); QUrl lTempFile = lExportManager->tempSave(); if (!lTempFile.isValid()) { return; } auto lMimeData = new QMimeData; lMimeData->setUrls(QList { lTempFile }); lMimeData->setData(QStringLiteral("application/x-kde-suggestedfilename"), QFile::encodeName(lTempFile.fileName())); auto lDragHandler = new QDrag(this); lDragHandler->setMimeData(lMimeData); lDragHandler->setPixmap(lExportManager->pixmap().scaled(256, 256, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation)); lDragHandler->exec(Qt::CopyAction); } // Private Platform::GrabMode SpectacleCore::toPlatformGrabMode(Spectacle::CaptureMode theCaptureMode) { switch(theCaptureMode) { case Spectacle::CaptureMode::InvalidChoice: return Platform::GrabMode::InvalidChoice; case Spectacle::CaptureMode::AllScreens: case Spectacle::CaptureMode::RectangularRegion: return Platform::GrabMode::AllScreens; case Spectacle::CaptureMode::TransientWithParent: return Platform::GrabMode::TransientWithParent; case Spectacle::CaptureMode::CurrentScreen: return Platform::GrabMode::CurrentScreen; case Spectacle::CaptureMode::ActiveWindow: return Platform::GrabMode::ActiveWindow; case Spectacle::CaptureMode::WindowUnderCursor: return Platform::GrabMode::WindowUnderCursor; } return Platform::GrabMode::InvalidChoice; } void SpectacleCore::initGui(bool theIncludePointer, bool theIncludeDecorations) { if (!mIsGuiInited) { mMainWindow = std::make_unique(mPlatform->supportedGrabModes(), mPlatform->supportedShutterModes()); connect(mMainWindow.get(), &KSMainWindow::newScreenshotRequest, this, &SpectacleCore::takeNewScreenshot); connect(mMainWindow.get(), &KSMainWindow::dragAndDropRequest, this, &SpectacleCore::doStartDragAndDrop); mIsGuiInited = true; auto lShutterMode = mPlatform->supportedShutterModes().testFlag(Platform::ShutterMode::Immediate) ? Platform::ShutterMode::Immediate : Platform::ShutterMode::OnClick; auto lGrabMode = toPlatformGrabMode(ExportManager::instance()->captureMode()); QTimer::singleShot(0, this, [this, lShutterMode, lGrabMode, theIncludePointer, theIncludeDecorations]() { mPlatform->doGrab(lShutterMode, lGrabMode, theIncludePointer, theIncludeDecorations); }); } } diff --git a/src/SpectacleCore.h b/src/SpectacleCore.h index d85b014..c3044e5 100644 --- a/src/SpectacleCore.h +++ b/src/SpectacleCore.h @@ -1,94 +1,95 @@ /* This file is part of Spectacle, the KDE screenshot utility * Copyright (C) 2019 Boudhayan Gupta * * 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 #include "ExportManager.h" #include "Gui/KSMainWindow.h" #include "QuickEditor/QuickEditor.h" #include "Platforms/PlatformLoader.h" #include using MainWindowPtr = std::unique_ptr; using EditorPtr = std::unique_ptr; class SpectacleCore: public QObject { Q_OBJECT public: enum class StartMode { Gui = 0, DBus = 1, Background = 2 }; explicit SpectacleCore(StartMode theStartMode, Spectacle::CaptureMode theCaptureMode, QString &theSaveFileName, qint64 theDelayMsec, bool theNotifyOnGrab, bool theCopyToClipboard, QObject *parent = nullptr); virtual ~SpectacleCore() = default; QString filename() const; void setFilename(const QString &filename); Q_SIGNALS: void errorMessage(const QString &errString); void allDone(); void filenameChanged(const QString &filename); void grabFailed(); public Q_SLOTS: void takeNewScreenshot(Spectacle::CaptureMode theCaptureMode, int theTimeout, bool theIncludePointer, bool theIncludeDecorations); void showErrorMessage(const QString &theErrString); void screenshotUpdated(const QPixmap &thePixmap); void screenshotFailed(); void dbusStartAgent(); void doStartDragAndDrop(); void doNotify(const QUrl &theSavedAt); void doCopyPath(const QUrl &savedAt); private: void initGui(bool theIncludePointer, bool theIncludeDecorations); Platform::GrabMode toPlatformGrabMode(Spectacle::CaptureMode theCaptureMode); void setUpShortcuts(); StartMode mStartMode; bool mNotify; QString mFileNameString; QUrl mFileNameUrl; PlatformPtr mPlatform; MainWindowPtr mMainWindow; EditorPtr mQuickEditor; bool mIsGuiInited; - bool mCopyToClipboard; + bool mCopySaveLocationToClipboard; + bool mCopyImageToClipboard; };