diff --git a/doc/index.docbook b/doc/index.docbook index 946485f..f0630c3 100644 --- a/doc/index.docbook +++ b/doc/index.docbook @@ -1,357 +1,360 @@ Spectacle"> BoudhayanGupta"> bgupta@kde.org"> ]> The &spectacle; Handbook &Boudhayan.Gupta; &Boudhayan.Gupta.mail; &Boudhayan.Gupta; &Boudhayan.Gupta.mail; 1997-2000&Richard.J.Moore; 2000&Matthias.Ettrich; 2015&Boudhayan.Gupta; &FDLNotice; 2017-11-06 Applications 17.12 &spectacle; is a simple application for capturing desktop screenshots. It can capture images of the entire desktop, a single monitor, the currently active window, the window currently under the mouse, or a rectangular region of the screen. The images can then be printed, sent to other applications for manipulation, or quickly be saved as-is. KDE spectacle kdegraphics screenshot screen capture screen grab Introduction &spectacle; is a simple application for capturing desktop screenshots. It can capture images of the entire desktop, a single monitor, the currently active window, the window currently under the mouse, or a rectangular region of the screen. The images can then be printed, sent to other applications for manipulation, or quickly be saved as-is. Please report any problems or feature requests to the &kde; Bug Tracking System. Starting &spectacle; &spectacle; can be started in a variety of ways, as described below: In the application launcher menu, &spectacle; can be found at ApplicationsUtilitiesScreenshot Capture Utility &spectacle; Pressing the Print button on the keyboard will immediately launch &spectacle;. Additionally, three more keyboard shortcuts are available to take screenshots and save them in your default save folder without showing the &GUI;: MetaPrint will take a screenshot of the active window &Shift;Print will take a screenshot of your entire desktop, &ie; all monitors Meta&Shift;Print will take a rectangular region screenshot You can configure the default save location and filename by starting &spectacle; normally and clicking on the Configure... button, then navigating to the Save section. The mini command line &krunner; (invoked with &Alt;F2) may also be used to start &spectacle;. &spectacle; can be started from the command-line. &spectacle; has an extensive set of command-line options, including a background mode which can be used to script the capture of screenshots without showing the &GUI; or requiring user interaction. To start &spectacle; from the command prompt, type in: % spectacle & To view the full list of command-line options and their explanation, type in: % spectacle --help Using &spectacle; Once &spectacle; starts, you will see a window like the following: &spectacle; Main Window &spectacle; grabs an image of your entire desktop immediately after it is started, but before it displays itself on screen. This allows you to quickly create full-desktop screenshot images. The snapshot taken by &spectacle; is displayed in the preview window, which is located on the left-hand side of the &spectacle; application window. The image can be saved to a location of your choice by clicking on the Save As button or typing &Ctrl;&Shift;S. This opens the standard save dialog, where you can choose the filename, the folder location, and the format that your screenshot will be saved in. You may edit the filename to anything you wish, including the name of a previously saved screenshot. To save a screenshot to the default location, click on the arrow portion of the Save As... button and press the Save (&Ctrl;S) button. The new save mode will be remembered for next time; this behavior can be configured in Spectacle's settings. The default save location and filename can also be configured there, as described later. - To quickly save the image and quit &spectacle;, click on the arrow portion of the Save As... button and press the Save & Exit (&Ctrl;Q) item. This saves the image as a PNG file in your default Pictures folder, and exits the application immediately. As above, this new save mode will be remembered. + To quickly save the image and quit &spectacle;, click on the checkbox beside Quit after Save or Copy, then click the arrow portion of the Save As... button and press the Save (&Ctrl;S) item. This saves the image as a PNG file in your default Pictures folder, and exits the application immediately. As above, this new save mode will be remembered. Taking A Screenshot When you open spectacle, it immediately takes a screenshot of the whole screen as a convenience and shows a preview of it in the main window. You can save this screenshot using the buttons on the bottom of the window, or take a new one using the controls to the right of the preview. To discard the current screenshot and take another screenshot, press the Take a New Screenshot (&Ctrl;N) button. You may configure certain options on the right hand side of the application window before taking a new screenshot. These options allow you to select the area of the screen that is to be captured, set a delay before capturing the image, and configure whether the mouse cursor and/or the window decorations should be captured along with the screenshot. Capture Mode The capture mode settings allow you to set the area of the screen that should be captured, and whether there should be a delay between pressing the Take a New Screenshot (&Ctrl;N) button and taking the screenshot. You may also enable the On Click checkbox, which disables the delay function and only takes the screenshot after you click anywhere on the screen after clicking the Take a New Screenshot (&Ctrl;N) button. The Area combo-box allows you to set the area of the screen that should be captured. There are five options to select from, as described below. The Full Screen (All Monitors) option takes a screenshot of your entire desktop, spread across all the outputs, including all the monitors, projectors etc. The Current Screen option takes a screenshot of the output that currently contains the mouse pointer. The Active Window option takes a screenshot of the window that currently has focus. It is advisable to use a delay with this mode, to give you time to select and activate a window before the screenshot is taken. The Window Under Cursor option takes a screenshot of the window that is under the mouse cursor. If the cursor is on top of a popup menu, &spectacle; tries to take a screenshot of the menu as well as its parent window. - While this works most of the time, in certain cases it may fail to obtain information about the parent window. In this case, &spectacle; falls back to old way of capturing the image automatically, and captures an image of only the popup menu. You can also force the old way of capturing the image by checking the Capture the current pop-up only checkbox under Content Options + While this works most of the time, in certain cases it may fail to obtain information about the parent window. In this case, &spectacle; falls back to old way of capturing the image automatically, and captures an image of only the popup menu. You can also force the old way of capturing the image by checking the Capture the current pop-up only checkbox under Options The Rectangular Region option allows you to select a rectangular region of your desktop with your mouse. This region may be spread across different outputs. This mode does not immediately take a screenshot but allows you to draw a rectangle on your screen, which can be moved and resized as needed. Once the desired selection rectangle has been drawn, double-clicking anywhere on the screen, or pressing the &Enter; button on the keyboard will capture the screenshot. The Delay spin-box allows you to set the delay between pressing the Take a New Screenshot (&Ctrl;N) button and taking the screenshot. This delay can be set in increments of 1 seconds using the spinbox buttons or the keyboard. Enabling the On Click checkbox overrides the delay. When this checkbox is enabled, pressing the Take a New Screenshot (&Ctrl;N) button hides the &spectacle; window and changes the mouse cursor to a crosshair. The screenshot is captured when the mouse is left-clicked, or aborted if any other mouse buttons are clicked. Note that you cannot interact with the desktop using the mouse while the cursor is a crosshair, but you can use the keyboard. - Content Options + Options - The content options settings allow you to select whether the mouse cursor should be included in the screenshots, and whether to capture window decorations along with the image of a single application window. In Window Under Cursor mode, it also allows you to select if &spectacle; shall only capture the image of the current popup menu under the cursor, or also include the parent window. + The Options settings allow you to select whether the mouse cursor should be included in the screenshots, and whether to capture window decorations along with the image of a single application window. In Window Under Cursor mode, it also allows you to select if &spectacle; shall only capture the image of the current popup menu under the cursor, or also include the parent window. Finally, Quit after Save or Copy will quit Spectacle after any save or copy operations. Enabling the Include mouse pointer checkbox includes an image of the mouse pointer in the screenshot. The Include window titlebar and borders option is only enabled when either the Active Window mode or the Window Under Cursor mode is selected in the Area combo-box. Checking this option includes the window borders and decoration in the screenshot, while unchecking it gives an image of only the window contents. The Capture the current pop-up only option is only enabled when the Window Under Cursor mode is selected in the Area combo-box. Checking this option captures only the popup menu under the cursor, without its parent window. + + The Quit after Save or Copy option will quit Spectacle after any saving or copying operations. Note that a copied screenshot will only be retained if you are running a clipboard manager that accepts images. KDE Klipper can be configured in this manner by right-clicking on its icon, selecting Configure Clipboard..., and unchecking Ignore images. + Additional Functionality Buttons There are six buttons located at the bottom of the &spectacle; window. Their functions are described below: Help This button gives you the common menu items described in the Help Menu of the &kde; Fundamentals. Configure... This button gives you access to Spectacle's Configure window where you can change the capture settings and default save location and filename. Tools Clicking on this button shows a drop-down menu, offering access to the print dialog. Export This drop-down menu will allow you to directly open the screenshot with all programs that are associated with the PNG (Portable Network Graphics) &MIME; type. Depending on what programs are installed, you will be able to open and edit the snapshot in your graphics applications or viewers. Furthermore, if you have the KIPI Plugins installed, you will be able to email your screenshots and export them directly to some social networks and websites. Copy To Clipboard This button copies the current screenshot to the clipboard. You can also use the &Ctrl;C keyboard shortcut for this. - Save & Exit + Save As - Clicking this button saves the screenshot as a PNG image in your default Pictures folder and immediately exits the application. + Clicking this button saves the screenshot as a PNG image to a location of your choosing. Clicking on the arrow on the side will expose the other save mode, Save, which will save the screenshot as a PNG image to the standard location (which defaults to your Pictures folder). By default, the last-used save mode is remembered for next time. Configure Use the Configure... button to open the configuration dialog. General General Remember last used Save mode The default behavior of the save button is to display Save As. Choosing another save mode by clicking on the arrow portion of the button will make that save mode the default one. Disable this option to make the button stop remembering the last used save mode Copy save location to clipboard When a screenshot is saved, copy the location at which the file was saved to the clipboard. You can then paste it anywhere that accepts text input. Note that you must be running a clipboard manager in order to keep the path in the clipboard after &spectacle; quits. Use light background Use a light background color to mask the cropped-out area in the rectangular region selector. This may make dark cursors easier to see. Remember selected area By default, &spectacle; does not show an initial selection when you take a screenshot of a rectangular region. Enable this option to remember the last selected region of the screen, and set it as the initial selection when you use the rectangular region selector the next time. Save - When you use the Save & Exit or the Save functions, &spectacle; saves the image with a default filename, in your Pictures folder under your home folder. The default filename includes the date and time when the image was taken. + When you use the Save function, &spectacle; saves the image with a default filename, in your Pictures folder (which is inside your home folder). The default filename includes the date and time when the image was taken. The Save page allows you to set the default save location and filename. Clicking this option brings up a dialog box like the following: Save Default Save Location - In the Location text box set the folder where you'd like to save your screenshots when you press Save or Save & Exit. + In the Location text box set the folder where you'd like to save your screenshots when you press Save. Default Save Filename Set a default filename for saved screenshots. You can use the following placeholders in the filename, which will be replaced with actual text when the file is saved: %Y: Year (4 digit) %y: Year (2 digit) %M: Month %D: Day %H: Hour %m: Minute %S: Second 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. Drag and Drop A captured image can be dragged to another application or document. If the application is able to handle images, a copy of the full image is inserted there. If you drag a screenshot into a file manager window, a dialog pops up where you can edit the filename and select the image format and the file will be inserted into the actual folder. If you drag the screenshot to a text box, the path to the temporary saved file is inserted. This is useful for example to upload a screenshot through web forms or to attach screenshots into bug reports on the &kde; bugtracker. This works with all clients that do not pick up the image data, but only look for a &URL; in the dragged mimedata. Credits and License Program copyright © 2015 &Boudhayan.Gupta; &Boudhayan.Gupta.mail;. Portions of the code are based directly on code from the &ksnapshot; project. Copyright © 1997-2011 The &ksnapshot; Developers. Detailed copyright assignment notices are available in the headers in the source code. Portions of the code are based directly on code from the &kwin; project. Copyright © 2008, 2013 The &kwin; Developers. Detailed copyright assignment notices are available in the headers in the source code. Documentation based on the original &ksnapshot; documentation: Copyright © 1997-2000 &Richard.J.Moore; &Richard.J.Moore.mail; Copyright © 2000 &Matthias.Ettrich; &Matthias.Ettrich.mail; &underFDL; &underGPL; &documentation.index; diff --git a/src/ExportManager.cpp b/src/ExportManager.cpp index 16ac3dc..8f36a4a 100644 --- a/src/ExportManager.cpp +++ b/src/ExportManager.cpp @@ -1,388 +1,394 @@ /* * 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 "ExportManager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "SpectacleConfig.h" ExportManager::ExportManager(QObject *parent) : QObject(parent), mSavePixmap(QPixmap()), mTempFile(QUrl()), mTempDir(nullptr) {} ExportManager::~ExportManager() { delete mTempDir; } ExportManager* ExportManager::instance() { static ExportManager instance; return &instance; } // screenshot pixmap setter and getter QPixmap ExportManager::pixmap() const { return mSavePixmap; } QString ExportManager::pixmapDataUri() const { QImage image = mSavePixmap.toImage(); QByteArray imageData; // write the image into the QByteArray using a QBuffer { QBuffer dataBuf(&imageData); dataBuf.open(QBuffer::WriteOnly); image.save(&dataBuf, "PNG"); } // compose the data uri and return it QString uri = QStringLiteral("data:image/png;base64,") + QString::fromLatin1(imageData.toBase64()); return uri; } void ExportManager::setPixmap(const QPixmap &pixmap) { mSavePixmap = pixmap; // reset our saved tempfile if (mTempFile.isValid()) { mUsedTempFileNames.append(mTempFile); QFile file(mTempFile.toLocalFile()); file.remove(); mTempFile = QUrl(); } } // native file save helpers QString ExportManager::saveLocation() const { KSharedConfigPtr config = KSharedConfig::openConfig(QStringLiteral("spectaclerc")); KConfigGroup generalConfig = KConfigGroup(config, "General"); QString savePath = generalConfig.readPathEntry( "default-save-location", QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); if (savePath.isEmpty() || savePath.isNull()) { savePath = QDir::homePath(); } savePath = QDir::cleanPath(savePath); QDir savePathDir(savePath); if (!(savePathDir.exists())) { savePathDir.mkpath(QStringLiteral(".")); generalConfig.writePathEntry("last-saved-to", savePath); } return savePath; } void ExportManager::setSaveLocation(const QString &savePath) { KSharedConfigPtr config = KSharedConfig::openConfig(QStringLiteral("spectaclerc")); KConfigGroup generalConfig = KConfigGroup(config, "General"); generalConfig.writePathEntry("last-saved-to", savePath); } QUrl ExportManager::getAutosaveFilename() { const QString baseDir = saveLocation(); const QDir baseDirPath(baseDir); const QString filename = makeAutosaveFilename(); const QString fullpath = autoIncrementFilename(baseDirPath.filePath(filename), SpectacleConfig::instance()->saveImageFormat(), &ExportManager::isFileExists); const QUrl fileNameUrl = QUrl::fromUserInput(fullpath); if (fileNameUrl.isValid()) { return fileNameUrl; } else { return QUrl(); } } QString ExportManager::makeAutosaveFilename() { KSharedConfigPtr config = KSharedConfig::openConfig(QStringLiteral("spectaclerc")); KConfigGroup generalConfig = KConfigGroup(config, "General"); const QDateTime timestamp = QDateTime::currentDateTime(); QString baseName = generalConfig.readEntry("save-filename-format", "Screenshot_%Y%M%D_%H%m%S"); return baseName.replace(QLatin1String("%Y"), timestamp.toString(QStringLiteral("yyyy"))) .replace(QLatin1String("%y"), timestamp.toString(QStringLiteral("yy"))) .replace(QLatin1String("%M"), timestamp.toString(QStringLiteral("MM"))) .replace(QLatin1String("%D"), timestamp.toString(QStringLiteral("dd"))) .replace(QLatin1String("%H"), timestamp.toString(QStringLiteral("hh"))) .replace(QLatin1String("%m"), timestamp.toString(QStringLiteral("mm"))) .replace(QLatin1String("%S"), timestamp.toString(QStringLiteral("ss"))); } QString ExportManager::autoIncrementFilename(const QString &baseName, const QString &extension, FileNameAlreadyUsedCheck isFileNameUsed) { if (!((this->*isFileNameUsed)(QUrl::fromUserInput(baseName + QLatin1Char('.') + extension)))) { return baseName + QLatin1Char('.') + extension; } QString fileNameFmt(baseName + QStringLiteral("-%1.") + extension); for (quint64 i = 1; i < std::numeric_limits::max(); i++) { if (!((this->*isFileNameUsed)(QUrl::fromUserInput(fileNameFmt.arg(i))))) { return fileNameFmt.arg(i); } } // unlikely this will ever happen, but just in case we've run // out of numbers return fileNameFmt.arg(QStringLiteral("OVERFLOW-") + QString::number(qrand() % 10000)); } QString ExportManager::makeSaveMimetype(const QUrl &url) { QMimeDatabase mimedb; QString type = mimedb.mimeTypeForUrl(url).preferredSuffix(); if (type.isEmpty()) { return SpectacleConfig::instance()->saveImageFormat(); } return type; } bool ExportManager::writeImage(QIODevice *device, const QByteArray &format) { QImageWriter imageWriter(device, format); if (!(imageWriter.canWrite())) { emit errorMessage(i18n("QImageWriter cannot write image: %1", imageWriter.errorString())); return false; } return imageWriter.write(mSavePixmap.toImage()); } bool ExportManager::localSave(const QUrl &url, const QString &mimetype) { QFile outputFile(url.toLocalFile()); outputFile.open(QFile::WriteOnly); if(!writeImage(&outputFile, mimetype.toLatin1())) { emit errorMessage(i18n("Cannot save screenshot. Error while writing file.")); return false; } return true; } bool ExportManager::remoteSave(const QUrl &url, const QString &mimetype) { QTemporaryFile tmpFile; if (tmpFile.open()) { if(!writeImage(&tmpFile, mimetype.toLatin1())) { emit errorMessage(i18n("Cannot save screenshot. Error while writing temporary local file.")); return false; } KIO::FileCopyJob *uploadJob = KIO::file_copy(QUrl::fromLocalFile(tmpFile.fileName()), url); uploadJob->exec(); if (uploadJob->error() != KJob::NoError) { emit errorMessage(i18n("Unable to save image. Could not upload file to remote location.")); return false; } return true; } return false; } QUrl ExportManager::tempSave(const QString &mimetype) { // if we already have a temp file saved, use that if (mTempFile.isValid()) { if (QFile(mTempFile.toLocalFile()).exists()) { return mTempFile; } } if (!mTempDir) { mTempDir = new QTemporaryDir(QDir::tempPath() + QDir::separator() + QStringLiteral("Spectacle.XXXXXX")); } if (mTempDir && mTempDir->isValid()) { // create the temporary file itself with normal file name and also unique one for this session // supports the use-case of creating multiple screenshots in a row // and exporting them to the same destination e.g. via clipboard, // where the temp file name is used as filename suggestion const QString baseFileName = mTempDir->path() + QDir::separator() + makeAutosaveFilename(); const QString fileName = autoIncrementFilename(baseFileName, mimetype, &ExportManager::isTempFileAlreadyUsed); QFile tmpFile(fileName); if (tmpFile.open(QFile::WriteOnly)) { if(writeImage(&tmpFile, mimetype.toLatin1())) { mTempFile = QUrl::fromLocalFile(tmpFile.fileName()); // try to make sure 3rd-party which gets the url of the temporary file e.g. on export // properly treats this as readonly, also hide from other users tmpFile.setPermissions(QFile::ReadUser); return mTempFile; } } } emit errorMessage(i18n("Cannot save screenshot. Error while writing temporary local file.")); return QUrl(); } bool ExportManager::save(const QUrl &url) { if (!(url.isValid())) { emit errorMessage(i18n("Cannot save screenshot. The save filename is invalid.")); return false; } QString mimetype = makeSaveMimetype(url); if (url.isLocalFile()) { return localSave(url, mimetype); } return remoteSave(url, mimetype); } bool ExportManager::isFileExists(const QUrl &url) const { if (!(url.isValid())) { return false; } KIO::StatJob * existsJob = KIO::stat(url, KIO::StatJob::DestinationSide, 0); existsJob->exec(); return (existsJob->error() == KJob::NoError); } bool ExportManager::isTempFileAlreadyUsed(const QUrl &url) const { return mUsedTempFileNames.contains(url); } // save slots void ExportManager::doSave(const QUrl &url, bool notify) { 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(); setSaveLocation(dir.absolutePath()); emit imageSaved(savePath); if (notify) { emit forceNotify(savePath); } } } -void ExportManager::doSaveAs(QWidget *parentWindow) +bool ExportManager::doSaveAs(QWidget *parentWindow, bool notify) { QStringList supportedFilters; SpectacleConfig *config = SpectacleConfig::instance(); // construct the supported mimetype list Q_FOREACH (auto mimeType, QImageWriter::supportedMimeTypes()) { supportedFilters.append(QString::fromUtf8(mimeType).trimmed()); } // construct the file name QFileDialog dialog(parentWindow); dialog.setAcceptMode(QFileDialog::AcceptSave); dialog.setFileMode(QFileDialog::AnyFile); dialog.setDirectoryUrl(config->lastSaveAsLocation()); dialog.selectFile(makeAutosaveFilename() + QStringLiteral(".png")); dialog.setDefaultSuffix(QStringLiteral(".png")); dialog.setMimeTypeFilters(supportedFilters); dialog.selectMimeTypeFilter(QStringLiteral("image/png")); // launch the dialog if (dialog.exec() == QFileDialog::Accepted) { const QUrl saveUrl = dialog.selectedUrls().first(); if (saveUrl.isValid()) { if (save(saveUrl)) { emit imageSaved(saveUrl); config->setLastSaveAsLocation(saveUrl.adjusted(QUrl::RemoveFilename)); + + if (notify) { + emit forceNotify(saveUrl); + } + return true; } } } + return false; } // misc helpers void ExportManager::doCopyToClipboard() { QApplication::clipboard()->setPixmap(mSavePixmap, QClipboard::Clipboard); } void ExportManager::doPrint(QPrinter *printer) { QPainter painter; if (!(painter.begin(printer))) { emit errorMessage(i18n("Printing failed. The printer failed to initialize.")); delete printer; return; } QRect devRect(0, 0, printer->width(), printer->height()); QPixmap pixmap = mSavePixmap.scaled(devRect.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation); QRect srcRect = pixmap.rect(); srcRect.moveCenter(devRect.center()); painter.drawPixmap(srcRect.topLeft(), pixmap); painter.end(); delete printer; return; } diff --git a/src/ExportManager.h b/src/ExportManager.h index 33e7f13..5b45070 100644 --- a/src/ExportManager.h +++ b/src/ExportManager.h @@ -1,100 +1,100 @@ /* * 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 EXPORTMANAGER_H #define EXPORTMANAGER_H #include #include #include #include #include class QTemporaryDir; class ExportManager : public QObject { Q_OBJECT // singleton-ize the class public: static ExportManager* instance(); private: explicit ExportManager(QObject *parent = 0); virtual ~ExportManager(); ExportManager(ExportManager const&) = delete; void operator= (ExportManager const&) = delete; // now the usual stuff public: Q_PROPERTY(QString saveLocation READ saveLocation WRITE setSaveLocation NOTIFY saveLocationChanged) Q_PROPERTY(QPixmap pixmap READ pixmap WRITE setPixmap NOTIFY pixmapChanged) void setSaveLocation(const QString &location); QString saveLocation() const; void setPixmap(const QPixmap &pixmap); QPixmap pixmap() const; QString pixmapDataUri() const; signals: void errorMessage(const QString &str); void saveLocationChanged(const QString &location); void pixmapChanged(const QPixmap &pixmap); void imageSaved(const QUrl &savedAt); void forceNotify(const QUrl &savedAt); public slots: QUrl getAutosaveFilename(); QUrl tempSave(const QString &mimetype = QStringLiteral("png")); void doSave(const QUrl &url = QUrl(), bool notify = false); - void doSaveAs(QWidget *parentWindow = 0); + bool doSaveAs(QWidget *parentWindow = 0, bool notify = false); void doCopyToClipboard(); void doPrint(QPrinter *printer); private: QString makeAutosaveFilename(); using FileNameAlreadyUsedCheck = bool (ExportManager::*)(const QUrl&) const; QString autoIncrementFilename(const QString &baseName, const QString &extension, FileNameAlreadyUsedCheck isFileNameUsed); QString makeSaveMimetype(const QUrl &url); bool writeImage(QIODevice *device, const QByteArray &format); bool save(const QUrl &url); bool localSave(const QUrl &url, const QString &mimetype); bool remoteSave(const QUrl &url, const QString &mimetype); bool isFileExists(const QUrl &url) const; bool isTempFileAlreadyUsed(const QUrl &url) const; QPixmap mSavePixmap; QUrl mTempFile; QTemporaryDir *mTempDir; QList mUsedTempFileNames; }; #endif // EXPORTMANAGER_H diff --git a/src/Gui/KSMainWindow.cpp b/src/Gui/KSMainWindow.cpp index e491cb4..5b3bd1f 100644 --- a/src/Gui/KSMainWindow.cpp +++ b/src/Gui/KSMainWindow.cpp @@ -1,361 +1,369 @@ /* * 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 "KSMainWindow.h" #include "Config.h" #include #include #include #include #include #include #include #ifdef XCB_FOUND #include #include #endif #include #include #include #include #include #include #include #include "SettingsDialog/SettingsDialog.h" #include "ExportMenu.h" #include "ExportManager.h" #include "SpectacleConfig.h" static const int DEFAULT_WINDOW_HEIGHT = 420; static const int DEFAULT_WINDOW_WIDTH = 840; static const int MAXIMUM_WINDOW_WIDTH = 1000; KSMainWindow::KSMainWindow(bool onClickAvailable, QWidget *parent) : QDialog(parent), mKSWidget(new KSWidget), mDivider(new QFrame), mDialogButtonBox(new QDialogButtonBox), mConfigureButton(new QToolButton), mToolsButton(new QPushButton), mSendToButton(new QPushButton), mClipboardButton(new QToolButton), mSaveButton(new QToolButton), mSaveMenu(new QMenu), mMessageWidget(new KMessageWidget), mToolsMenu(new QMenu), mExportMenu(new ExportMenu(this)), mOnClickAvailable(onClickAvailable) { // before we do anything, we need to set a window property // that skips the close/hide window animation on kwin. this // fixes a ghost image of the spectacle window that appears // on subsequent screenshots taken with the take new screenshot // button // // credits for this goes to Thomas Lübking #ifdef XCB_FOUND if (KWindowSystem::isPlatformX11()) { // create a window if we haven't already. note that the QWidget constructor // should already have done this if (winId() == 0) { create(0, true, true); } // do the xcb shenanigans xcb_connection_t *xcbConn = QX11Info::connection(); const QByteArray effectName = QByteArrayLiteral("_KDE_NET_WM_SKIP_CLOSE_ANIMATION"); xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked(xcbConn, false, effectName.length(), effectName.constData()); QScopedPointer atom(xcb_intern_atom_reply(xcbConn, atomCookie, nullptr)); if (atom.isNull()) { goto done; } uint32_t value = 1; xcb_change_property(xcbConn, XCB_PROP_MODE_REPLACE, winId(), atom->atom, XCB_ATOM_CARDINAL, 32, 1, &value); } #endif done: QMetaObject::invokeMethod(this, "init", Qt::QueuedConnection); } KSMainWindow::~KSMainWindow() {} // GUI init void KSMainWindow::init() { KSharedConfigPtr config = KSharedConfig::openConfig(QStringLiteral("spectaclerc")); KConfigGroup guiConfig(config, "GuiConfig"); // window properties setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); QPoint location = guiConfig.readEntry("window-position", QPoint(50, 50)); move(location); // change window title on save connect(ExportManager::instance(), &ExportManager::imageSaved, this, &KSMainWindow::setScreenshotWindowTitle); // the KSGWidget connect(mKSWidget, &KSWidget::newScreenshotRequest, this, &KSMainWindow::captureScreenshot); connect(mKSWidget, &KSWidget::dragInitiated, this, &KSMainWindow::dragAndDropRequest); // the Button Bar mDialogButtonBox->setStandardButtons(QDialogButtonBox::Help); mConfigureButton->setDefaultAction(KStandardAction::preferences(this, SLOT(showPreferencesDialog()), this)); mConfigureButton->setText(i18n("Configure...")); mConfigureButton->setToolTip(i18n("Change Spectacle's settings.")); mConfigureButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); mDialogButtonBox->addButton(mConfigureButton, QDialogButtonBox::ResetRole); KGuiItem::assign(mToolsButton, KGuiItem(i18n("Tools"))); mToolsButton->setIcon(QIcon::fromTheme(QStringLiteral("application-menu"))); mDialogButtonBox->addButton(mToolsButton, QDialogButtonBox::ActionRole); mToolsMenu->addAction(KStandardAction::print(this, SLOT(showPrintDialog()), this)); mToolsButton->setMenu(mToolsMenu); KGuiItem::assign(mSendToButton, KGuiItem(i18n("Export"))); mSendToButton->setIcon(QIcon::fromTheme(QStringLiteral("document-share"))); mDialogButtonBox->addButton(mSendToButton, QDialogButtonBox::ActionRole); mClipboardButton->setDefaultAction(KStandardAction::copy(this, SLOT(sendToClipboard()), this)); mClipboardButton->setText(i18n("Copy To Clipboard")); mClipboardButton->setToolTip(i18n("Copy the current screenshot image to the clipboard.")); mClipboardButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); mDialogButtonBox->addButton(mClipboardButton, QDialogButtonBox::ActionRole); mSaveButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); mSaveButton->setMenu(mSaveMenu); mSaveButton->setPopupMode(QToolButton::MenuButtonPopup); buildSaveMenu(); mDialogButtonBox->addButton(mSaveButton, QDialogButtonBox::ActionRole); // the help menu KHelpMenu *helpMenu = new KHelpMenu(this, KAboutData::applicationData(), true); mDialogButtonBox->button(QDialogButtonBox::Help)->setMenu(helpMenu->menu()); // message widget connect(mMessageWidget, &KMessageWidget::linkActivated, this, [](const QString &str) { QDesktopServices::openUrl(QUrl(str)); } ); // layouts mDivider->setFrameShape(QFrame::HLine); mDivider->setLineWidth(2); QVBoxLayout *layout = new QVBoxLayout(this); layout->addWidget(mKSWidget); layout->addWidget(mMessageWidget); layout->addWidget(mDivider); layout->addWidget(mDialogButtonBox); mMessageWidget->hide(); // populate our send-to actions mSendToButton->setMenu(mExportMenu); connect(mExportMenu, &ExportMenu::imageShared, this, &KSMainWindow::showImageSharedFeedback); // disable onClick mode if not available on the platform if (!mOnClickAvailable) { mKSWidget->disableOnClick(); } resize(QSize(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT).expandedTo(minimumSize())); + // Allow Ctrl+Q to quit the app + QAction *actionQuit = KStandardAction::quit(qApp, &QApplication::quit, this); + actionQuit->setShortcut(QKeySequence::Quit); + addAction(actionQuit); // done with the init } int KSMainWindow::windowWidth(const QPixmap &pixmap) const { // Calculates what the width of the window should be for the captured image to perfectly fit // the area reserved for the image, with the height already set. const float pixmapAspectRatio = (float)pixmap.width() / pixmap.height(); const int imageHeight = mKSWidget->height() - 2 * layout()->spacing(); const int imageWidth = pixmapAspectRatio * imageHeight; int alignedWindowWidth = qMin(mKSWidget->imagePaddingWidth() + imageWidth, MAXIMUM_WINDOW_WIDTH); alignedWindowWidth += layout()->contentsMargins().left() + layout()->contentsMargins().right(); alignedWindowWidth += 2; // margins is removing 1 - 1 pixel for some reason return alignedWindowWidth; } void KSMainWindow::buildSaveMenu() { // first clear the menu mSaveMenu->clear(); // get our actions in order QAction *actionSave = KStandardAction::save(this, &KSMainWindow::save, this); QAction *actionSaveAs = KStandardAction::saveAs(this, &KSMainWindow::saveAs, this); - QAction *actionSaveExit = new QAction(QIcon::fromTheme(QStringLiteral("document-save")), i18n("Save &&& Exit"), this); - actionSaveExit->setToolTip(i18n("Save screenshot in your Pictures directory and exit")); - actionSaveExit->setShortcut(QKeySequence(QKeySequence::Quit)); - connect(actionSaveExit, &QAction::triggered, this, &KSMainWindow::saveAndExit); // static or dynamic SpectacleConfig *cfgManager = SpectacleConfig::instance(); int switchState = cfgManager->useDynamicSaveButton() ? cfgManager->lastUsedSaveMode() : 0; // put the actions in order switch (switchState) { case 0: default: mSaveButton->setDefaultAction(actionSaveAs); - mSaveMenu->addAction(actionSaveExit); mSaveMenu->addAction(actionSave); break; case 1: mSaveButton->setDefaultAction(actionSave); - mSaveMenu->addAction(actionSaveExit); - mSaveMenu->addAction(actionSaveAs); - break; - case 2: - mSaveButton->setDefaultAction(actionSaveExit); - mSaveMenu->addAction(actionSave); mSaveMenu->addAction(actionSaveAs); break; } } // overrides void KSMainWindow::moveEvent(QMoveEvent *event) { Q_UNUSED(event); KSharedConfigPtr config = KSharedConfig::openConfig(QStringLiteral("spectaclerc")); KConfigGroup guiConfig(config, "GuiConfig"); guiConfig.writeEntry("window-position", pos()); guiConfig.sync(); } // slots void KSMainWindow::captureScreenshot(ImageGrabber::GrabMode mode, int timeout, bool includePointer, bool includeDecorations) { hide(); emit newScreenshotRequest(mode, timeout, includePointer, includeDecorations); } void KSMainWindow::setScreenshotAndShow(const QPixmap &pixmap) { mKSWidget->setScreenshotPixmap(pixmap); mExportMenu->imageUpdated(); setWindowTitle(i18nc("Unsaved Screenshot", "Unsaved[*]")); setWindowModified(true); show(); resize(QSize(windowWidth(pixmap), DEFAULT_WINDOW_HEIGHT)); } void KSMainWindow::showPrintDialog() { QPrinter *printer = new QPrinter(QPrinter::HighResolution); QPrintDialog printDialog(printer, this); if (printDialog.exec() == QDialog::Accepted) { ExportManager::instance()->doPrint(printer); return; } delete printer; } void KSMainWindow::showImageSharedFeedback(bool error, const QString &message) { if (error) { mMessageWidget->setMessageType(KMessageWidget::Error); mMessageWidget->setText(i18n("There was a problem sharing the image: %1", message)); mMessageWidget->setIcon(QIcon::fromTheme(QStringLiteral("dialog-error"))); } else { mMessageWidget->setMessageType(KMessageWidget::Positive); if (message.isEmpty()) mMessageWidget->setText(i18n("Image shared")); else mMessageWidget->setText(i18n("You can find the shared image at: %1", message)); mMessageWidget->setIcon(QIcon::fromTheme(QStringLiteral("dialog-ok-apply"))); } mMessageWidget->animatedShow(); QTimer::singleShot(20000, mMessageWidget, &KMessageWidget::animatedHide); } void KSMainWindow::sendToClipboard() { ExportManager::instance()->doCopyToClipboard(); + if (SpectacleConfig::instance()->quitAfterSaveOrCopyChecked()) { + qApp->setQuitOnLastWindowClosed(false); + hide(); + QTimer::singleShot(250, qApp, &QApplication::quit); + } + mMessageWidget->setMessageType(KMessageWidget::Information); mMessageWidget->setText(i18n("The screenshot has been copied to the clipboard.")); mMessageWidget->setIcon(QIcon::fromTheme(QStringLiteral("dialog-information"))); mMessageWidget->animatedShow(); QTimer::singleShot(10000, mMessageWidget, &KMessageWidget::animatedHide); } void KSMainWindow::showPreferencesDialog() { SettingsDialog prefDialog(this); prefDialog.exec(); } void KSMainWindow::setScreenshotWindowTitle(QUrl location) { setWindowTitle(location.fileName()); setWindowModified(false); } void KSMainWindow::save() { SpectacleConfig::instance()->setLastUsedSaveMode(1); buildSaveMenu(); - ExportManager::instance()->doSave(); + + if (SpectacleConfig::instance()->quitAfterSaveOrCopyChecked()) { + ExportManager::instance()->doSave(QUrl(), true); + qApp->setQuitOnLastWindowClosed(false); + hide(); + } + else { + ExportManager::instance()->doSave(); + } } void KSMainWindow::saveAs() { SpectacleConfig::instance()->setLastUsedSaveMode(0); buildSaveMenu(); - ExportManager::instance()->doSaveAs(this); -} -void KSMainWindow::saveAndExit() -{ - SpectacleConfig::instance()->setLastUsedSaveMode(2); - qApp->setQuitOnLastWindowClosed(false); - ExportManager::instance()->doSave(QUrl(), true); - hide(); + if (SpectacleConfig::instance()->quitAfterSaveOrCopyChecked()) { + if (ExportManager::instance()->doSaveAs(this, true)) { + qApp->setQuitOnLastWindowClosed(false); + hide(); + } + } + else { + ExportManager::instance()->doSaveAs(this, false); + } } diff --git a/src/Gui/KSMainWindow.h b/src/Gui/KSMainWindow.h index ab78f31..b51c9a9 100644 --- a/src/Gui/KSMainWindow.h +++ b/src/Gui/KSMainWindow.h @@ -1,89 +1,88 @@ /* * 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 KSMAINWINDOW_H #define KSMAINWINDOW_H #include #include #include #include #include #include #include "PlatformBackends/ImageGrabber.h" #include "ExportMenu.h" #include "KSWidget.h" class KSMainWindow : public QDialog { Q_OBJECT public: explicit KSMainWindow(bool onClickAvailable, QWidget *parent = 0); ~KSMainWindow(); private slots: void captureScreenshot(ImageGrabber::GrabMode mode, int timeout, bool includePointer, bool includeDecorations); void showPrintDialog(); void showPreferencesDialog(); void showImageSharedFeedback(bool error, const QString &message); void sendToClipboard(); void init(); void buildSaveMenu(); void save(); void saveAs(); - void saveAndExit(); int windowWidth(const QPixmap &pixmap) const; public slots: void setScreenshotAndShow(const QPixmap &pixmap); void setScreenshotWindowTitle(QUrl location); signals: void newScreenshotRequest(ImageGrabber::GrabMode mode, int timeout, bool includePointer, bool includeDecorations); void dragAndDropRequest(); protected: void moveEvent(QMoveEvent *event) Q_DECL_OVERRIDE; private: KSWidget *mKSWidget; QFrame *mDivider; QDialogButtonBox *mDialogButtonBox; QToolButton *mConfigureButton; QPushButton *mToolsButton; QPushButton *mSendToButton; QToolButton *mClipboardButton; QToolButton *mSaveButton; QMenu *mSaveMenu; KMessageWidget *mMessageWidget; QMenu *mToolsMenu; ExportMenu *mExportMenu; bool mOnClickAvailable; }; #endif // KSMAINWINDOW_H diff --git a/src/Gui/KSWidget.cpp b/src/Gui/KSWidget.cpp index 28612ec..81382c8 100644 --- a/src/Gui/KSWidget.cpp +++ b/src/Gui/KSWidget.cpp @@ -1,234 +1,237 @@ /* * 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 "KSWidget.h" #include #include #include #include #include #include #include #include #include #include #include #include "KSImageWidget.h" #include "SmartSpinBox.h" #include "SpectacleConfig.h" KSWidget::KSWidget(QWidget *parent) : QWidget(parent) { // get a handle to the configuration manager SpectacleConfig *configManager = SpectacleConfig::instance(); // we'll init the widget that holds the image first mImageWidget = new KSImageWidget(this); mImageWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); connect(mImageWidget, &KSImageWidget::dragInitiated, this, &KSWidget::dragInitiated); // the capture mode options first mCaptureModeLabel = new QLabel(i18n("Capture Mode"), this); mCaptureArea = new QComboBox(this); mCaptureArea->insertItem(1, i18n("Full Screen (All Monitors)"), ImageGrabber::FullScreen); mCaptureArea->insertItem(2, i18n("Current Screen"), ImageGrabber::CurrentScreen); mCaptureArea->insertItem(3, i18n("Active Window"), ImageGrabber::ActiveWindow); mCaptureArea->insertItem(4, i18n("Window Under Cursor"), ImageGrabber::WindowUnderCursor); mCaptureArea->insertItem(5, i18n("Rectangular Region"), ImageGrabber::RectangularRegion); mCaptureArea->setMinimumWidth(240); connect(mCaptureArea, static_cast(&QComboBox::currentIndexChanged), this, &KSWidget::captureModeChanged); mDelayMsec = new SmartSpinBox(this); mDelayMsec->setDecimals(1); mDelayMsec->setSingleStep(1.0); mDelayMsec->setMinimum(0.0); mDelayMsec->setMaximum(999.9); mDelayMsec->setSpecialValueText(i18n("No Delay")); mDelayMsec->setMinimumWidth(160); connect(mDelayMsec, static_cast(&SmartSpinBox::valueChanged), configManager, &SpectacleConfig::setCaptureDelay); mCaptureOnClick = new QCheckBox(i18n("On Click"), this); mCaptureOnClick->setToolTip(i18n("Wait for a mouse click before capturing the screenshot image")); connect(mCaptureOnClick, &QCheckBox::stateChanged, this, &KSWidget::onClickStateChanged); connect(mCaptureOnClick, &QCheckBox::clicked, configManager, &SpectacleConfig::setOnClickChecked); mDelayLayout = new QHBoxLayout; mDelayLayout->addWidget(mDelayMsec); mDelayLayout->addWidget(mCaptureOnClick); mCaptureModeForm = new QFormLayout; mCaptureModeForm->addRow(i18n("Area:"), mCaptureArea); mCaptureModeForm->addRow(i18n("Delay:"), mDelayLayout); mCaptureModeForm->setContentsMargins(24, 0, 0, 0); - // the content options (mouse pointer, window decorations) + // options (mouse pointer, window decorations, quit after saving or copying) mContentOptionsLabel = new QLabel(this); - mContentOptionsLabel->setText(i18n("Content Options")); + mContentOptionsLabel->setText(i18n("Options")); mMousePointer = new QCheckBox(i18n("Include mouse pointer"), this); mMousePointer->setToolTip(i18n("Show the mouse cursor in the screenshot image")); connect(mMousePointer, &QCheckBox::clicked, configManager, &SpectacleConfig::setIncludePointerChecked); mWindowDecorations = new QCheckBox(i18n("Include window titlebar and borders"), this); mWindowDecorations->setToolTip(i18n("Show the window title bar, the minimize/maximize/close buttons, and the window border")); mWindowDecorations->setEnabled(false); connect(mWindowDecorations, &QCheckBox::clicked, configManager, &SpectacleConfig::setIncludeDecorationsChecked); mCaptureTransientOnly = new QCheckBox(i18n("Capture the current pop-up only"), this); mCaptureTransientOnly->setToolTip(i18n("Capture only the current pop-up window (like a menu, tooltip etc).\n" "If disabled, the pop-up is captured along with the parent window")); mCaptureTransientOnly->setEnabled(false); connect(mCaptureTransientOnly, &QCheckBox::clicked, configManager, &SpectacleConfig::setCaptureTransientWindowOnlyChecked); + mQuitAfterSaveOrCopy = new QCheckBox(i18n("Quit after Save or Copy"), this); + mQuitAfterSaveOrCopy->setToolTip(i18n("Quit Spectacle after saving or copying the image")); + connect(mQuitAfterSaveOrCopy, &QCheckBox::clicked, configManager, &SpectacleConfig::setQuitAfterSaveOrCopyChecked); + mContentOptionsForm = new QVBoxLayout; mContentOptionsForm->addWidget(mMousePointer); mContentOptionsForm->addWidget(mWindowDecorations); mContentOptionsForm->addWidget(mCaptureTransientOnly); - mContentOptionsForm->setSpacing(16); + mContentOptionsForm->addWidget(mQuitAfterSaveOrCopy); mContentOptionsForm->setContentsMargins(24, 0, 0, 0); // the take a new screenshot button mTakeScreenshotButton = new QPushButton(i18n("Take a New Screenshot"), this); mTakeScreenshotButton->setIcon(QIcon::fromTheme(QStringLiteral("spectacle"))); mTakeScreenshotButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); mTakeScreenshotButton->setFocus(); connect(mTakeScreenshotButton, &QPushButton::clicked, this, &KSWidget::newScreenshotClicked); QShortcut *shortcut = new QShortcut(QKeySequence(QKeySequence::New), mTakeScreenshotButton); auto clickFunc = [&]() { mTakeScreenshotButton->animateClick(100); QTimer::singleShot(100, mTakeScreenshotButton, &QPushButton::click); }; connect(shortcut, &QShortcut::activated, clickFunc); // finally, finish up the layouts mRightLayout = new QVBoxLayout; mRightLayout->addStretch(1); mRightLayout->addWidget(mCaptureModeLabel); - mRightLayout->addSpacing(10); mRightLayout->addLayout(mCaptureModeForm); mRightLayout->addStretch(1); mRightLayout->addWidget(mContentOptionsLabel); - mRightLayout->addSpacing(10); mRightLayout->addLayout(mContentOptionsForm); mRightLayout->addStretch(10); mRightLayout->addWidget(mTakeScreenshotButton, 1, Qt::AlignHCenter); mRightLayout->setContentsMargins(10, 0, 0, 10); mMainLayout = new QGridLayout(this); mMainLayout->addWidget(mImageWidget, 0, 0, 1, 1); mMainLayout->addLayout(mRightLayout, 0, 1, 1, 1); mMainLayout->setColumnMinimumWidth(0, 320); mMainLayout->setColumnMinimumWidth(1, 320); // and read in the saved checkbox states and capture mode indices mMousePointer->setChecked (configManager->includePointerChecked()); mWindowDecorations->setChecked (configManager->includeDecorationsChecked()); mCaptureOnClick->setChecked (configManager->onClickChecked()); mCaptureTransientOnly->setChecked (configManager->captureTransientWindowOnlyChecked()); + mQuitAfterSaveOrCopy->setChecked (configManager->quitAfterSaveOrCopyChecked()); mCaptureArea->setCurrentIndex (configManager->captureMode()); mDelayMsec->setValue (configManager->captureDelay()); // done } int KSWidget::imagePaddingWidth() const { int rightLayoutLeft = 0; int rightLayoutRight = 0; int mainLayoutRight = 0; mRightLayout->getContentsMargins(&rightLayoutLeft, nullptr, &rightLayoutRight, nullptr); mMainLayout->getContentsMargins(nullptr, nullptr, &mainLayoutRight, nullptr); int paddingWidth = (rightLayoutLeft + rightLayoutRight + mainLayoutRight); paddingWidth += mRightLayout->contentsRect().width(); paddingWidth += 2 * SpectacleImage::SHADOW_RADIUS; // image drop shadow return paddingWidth; } // public slots void KSWidget::setScreenshotPixmap(const QPixmap &pixmap) { mImageWidget->setScreenshot(pixmap); } void KSWidget::disableOnClick() { mCaptureOnClick->setEnabled(false); mDelayMsec->setEnabled(true); } // private slots void KSWidget::newScreenshotClicked() { int delay = mCaptureOnClick->isChecked() ? -1 : (mDelayMsec->value() * 1000); ImageGrabber::GrabMode mode = static_cast(mCaptureArea->currentData().toInt()); if (mode == ImageGrabber::WindowUnderCursor && !(mCaptureTransientOnly->isChecked())) { mode = ImageGrabber::TransientWithParent; } emit newScreenshotRequest(mode, delay, mMousePointer->isChecked(), mWindowDecorations->isChecked()); } void KSWidget::onClickStateChanged(int state) { if (state == Qt::Checked) { mDelayMsec->setEnabled(false); } else if (state == Qt::Unchecked) { mDelayMsec->setEnabled(true); } } void KSWidget::captureModeChanged(int index) { SpectacleConfig::instance()->setCaptureMode(index); ImageGrabber::GrabMode mode = static_cast(mCaptureArea->itemData(index).toInt()); switch (mode) { case ImageGrabber::WindowUnderCursor: mWindowDecorations->setEnabled(true); mCaptureTransientOnly->setEnabled(true); break; case ImageGrabber::ActiveWindow: mWindowDecorations->setEnabled(true); mCaptureTransientOnly->setEnabled(false); break; default: mWindowDecorations->setEnabled(false); mCaptureTransientOnly->setEnabled(false); } } diff --git a/src/Gui/KSWidget.h b/src/Gui/KSWidget.h index 11998af..733c87a 100644 --- a/src/Gui/KSWidget.h +++ b/src/Gui/KSWidget.h @@ -1,85 +1,86 @@ /* * 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 KSWIDGET_H #define KSWIDGET_H #include #include #include "PlatformBackends/ImageGrabber.h" class QGridLayout; class QHBoxLayout; class QVBoxLayout; class QFormLayout; class QComboBox; class QCheckBox; class QLabel; class QPushButton; class KSImageWidget; class SmartSpinBox; class KSWidget : public QWidget { Q_OBJECT public: explicit KSWidget(QWidget *parent = 0); int imagePaddingWidth() const; signals: void dragInitiated(); void newScreenshotRequest(ImageGrabber::GrabMode mode, int captureDelay, bool capturePointer, bool captureDecorations); public slots: void setScreenshotPixmap(const QPixmap &pixmap); void disableOnClick(); private slots: void newScreenshotClicked(); void onClickStateChanged(int state); void captureModeChanged(int index); private: QGridLayout *mMainLayout; QHBoxLayout *mDelayLayout; QVBoxLayout *mRightLayout; QFormLayout *mCaptureModeForm; QVBoxLayout *mContentOptionsForm; KSImageWidget *mImageWidget; QPushButton *mTakeScreenshotButton; QComboBox *mCaptureArea; SmartSpinBox *mDelayMsec; QCheckBox *mCaptureOnClick; QCheckBox *mMousePointer; QCheckBox *mWindowDecorations; QCheckBox *mCaptureTransientOnly; + QCheckBox *mQuitAfterSaveOrCopy; QLabel *mCaptureModeLabel; QLabel *mContentOptionsLabel; }; #endif // KSWIDGET_H diff --git a/src/SpectacleConfig.cpp b/src/SpectacleConfig.cpp index 89be2f6..e3b82b0 100644 --- a/src/SpectacleConfig.cpp +++ b/src/SpectacleConfig.cpp @@ -1,251 +1,264 @@ /* * 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 SpectacleConfig::SpectacleConfig(QObject *parent) : QObject(parent) { mConfig = KSharedConfig::openConfig(QStringLiteral("spectaclerc")); mGeneralConfig = KConfigGroup(mConfig, "General"); mGuiConfig = KConfigGroup(mConfig, "GuiConfig"); } SpectacleConfig::~SpectacleConfig() {} SpectacleConfig* SpectacleConfig::instance() { static SpectacleConfig instance; return &instance; } // lastSaveAsLocation QUrl SpectacleConfig::lastSaveAsLocation() const { return mGeneralConfig.readEntry(QStringLiteral("lastSaveAsLocation"), QUrl::fromUserInput(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation))); } void SpectacleConfig::setLastSaveAsLocation(const QUrl &location) { mGeneralConfig.writeEntry(QStringLiteral("lastSaveAsLocation"), location); mGeneralConfig.sync(); } // 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(); +} + // 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 mGuiConfig.readEntry(QStringLiteral("captureModeIndex"), 0); } void SpectacleConfig::setCaptureMode(int index) { mGuiConfig.writeEntry(QStringLiteral("captureModeIndex"), index); mGuiConfig.sync(); } // dynamic save button bool SpectacleConfig::useDynamicSaveButton() const { return mGuiConfig.readEntry(QStringLiteral("dynamicSaveButton"), true); } void SpectacleConfig::setUseDynamicSaveButton(bool enabled) { mGuiConfig.writeEntry(QStringLiteral("dynamicSaveButton"), enabled); mGuiConfig.sync(); } // remember last rectangular region bool SpectacleConfig::rememberLastRectangularRegion() const { return mGuiConfig.readEntry(QStringLiteral("rememberLastRectangularRegion"), false); } void SpectacleConfig::setRememberLastRectangularRegion(bool enabled) { mGuiConfig.writeEntry(QStringLiteral("rememberLastRectangularRegion"), 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(); } // last used save mode int SpectacleConfig::lastUsedSaveMode() const { return mGuiConfig.readEntry(QStringLiteral("lastUsedSaveMode"), 0); } void SpectacleConfig::setLastUsedSaveMode(int index) { mGuiConfig.writeEntry(QStringLiteral("lastUsedSaveMode"), index); mGuiConfig.sync(); } // autosave filename format QString SpectacleConfig::autoSaveFilenameFormat() const { return mGeneralConfig.readEntry(QStringLiteral("save-filename-format"), QStringLiteral("Screenshot_%Y%M%D_%H%m%S")); } void SpectacleConfig::setAutoSaveFilenameFormat(const QString &format) { mGeneralConfig.writeEntry(QStringLiteral("save-filename-format"), format); mGeneralConfig.sync(); } // autosave location QString SpectacleConfig::autoSaveLocation() const { return mGeneralConfig.readPathEntry(QStringLiteral("default-save-location"), QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); } void SpectacleConfig::setAutoSaveLocation(const QString &location) { mGeneralConfig.writePathEntry(QStringLiteral("default-save-location"), location); mGeneralConfig.sync(); } // copy save location to clipboard 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(); } diff --git a/src/SpectacleConfig.h b/src/SpectacleConfig.h index 2a3bfb7..d033005 100644 --- a/src/SpectacleConfig.h +++ b/src/SpectacleConfig.h @@ -1,107 +1,110 @@ /* * 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 class SpectacleConfig : public QObject { Q_OBJECT // singleton-ize the class public: static SpectacleConfig* instance(); private: explicit SpectacleConfig(QObject *parent = 0); virtual ~SpectacleConfig(); SpectacleConfig(SpectacleConfig const&) = delete; void operator= (SpectacleConfig const&) = delete; // everything else public slots: QUrl lastSaveAsLocation() const; void setLastSaveAsLocation(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); + qreal captureDelay() const; void setCaptureDelay(qreal delay); int captureMode() const; void setCaptureMode(int index); bool useDynamicSaveButton() const; void setUseDynamicSaveButton(bool enabled); bool rememberLastRectangularRegion() const; void setRememberLastRectangularRegion(bool enabled); bool useLightRegionMaskColour() const; void setUseLightRegionMaskColour(bool enabled); int lastUsedSaveMode() const; void setLastUsedSaveMode(int index); QString autoSaveFilenameFormat() const; void setAutoSaveFilenameFormat(const QString &format); QString autoSaveLocation() const; void setAutoSaveLocation(const QString &location); bool copySaveLocationToClipboard() const; void setCopySaveLocationToClipboard(bool enabled); QString saveImageFormat() const; void setSaveImageFormat(const QString &saveFmt); private: KSharedConfigPtr mConfig; KConfigGroup mGeneralConfig; KConfigGroup mGuiConfig; }; #endif // SPECTACLECONFIG_H