diff --git a/CMakeLists.txt b/CMakeLists.txt index 2f6e1a9..44192cb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,89 +1,97 @@ -# KScreenGenie project +# Spectacle project -project(KScreenGenie) -set(KSG_VERSION_MAJOR 2) -set(KSG_VERSION_MINOR 0) -set(KSG_VERSION_PATCH 1) -set(KSG_VERSION "${KSG_VERSION_MAJOR}.${KSG_VERSION_MINOR}.${KSG_VERSION_PATCH}") +project(Spectacle) + +# KDE Application Version, managed by release script +set(KDE_APPLICATIONS_VERSION_MAJOR "15") +set(KDE_APPLICATIONS_VERSION_MINOR "08") +set(KDE_APPLICATIONS_VERSION_MICRO "50") +set(KDE_APPLICATIONS_VERSION "${KDE_APPLICATIONS_VERSION_MAJOR}.${KDE_APPLICATIONS_VERSION_MINOR}.${KDE_APPLICATIONS_VERSION_MICRO}") +set(SPECTACLE_VERSION ${KDE_APPLICATIONS_VERSION}) # minimum requirements cmake_minimum_required (VERSION 2.8.12 FATAL_ERROR) +if (POLICY CMP0063) + cmake_policy(SET CMP0063 NEW) +endif() + set(QT_MIN_VERSION "5.4.0") set(KF5_MIN_VERSION "5.6.0") set(PLASMA_MIN_VERSION "5.2.0") find_package(ECM 1.2.0 REQUIRED NO_MODULE) set( CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} ) # set up kf5 include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings) -include(ECMOptionalAddSubdirectory) include(ECMInstallIcons) include(ECMSetupVersion) -include(ECMMarkNonGuiExecutable) -include(ECMGenerateHeaders) -include(GenerateExportHeader) include(FeatureSummary) find_package( Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED Core Quick Widgets DBus PrintSupport ) find_package( KF5 ${KF5_MIN_VERSION} REQUIRED CoreAddons +<<<<<<< HEAD Declarative +======= + WidgetsAddons + DBusAddons +>>>>>>> master Notifications Config I18n KIO XmlGui - WidgetsAddons WindowSystem DocTools ) # optional components find_package(KF5Kipi) if (KF5Kipi_FOUND) set(KIPI_FOUND 1) endif () find_package(XCB COMPONENTS XFIXES IMAGE UTIL CURSOR) if (XCB_FOUND) find_package(Qt5X11Extras ${QT_MIN_VERSION} REQUIRED) find_package(KF5Screen ${PLASMA_MIN_VERSION} REQUIRED) endif() # fail build if none of the platform backends can be found if (!XCB_FOUND) message(FATAL_ERROR "No suitable backend platform was found. Currenty supported platforms are: XCB") endif() # hand off to subdirectories add_subdirectory(src) +add_subdirectory(dbus) add_subdirectory(desktop) add_subdirectory(icons) add_subdirectory(doc) # summaries feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/CONTRIBUTING b/CONTRIBUTING index 3169ab8..1e6b90f 100644 --- a/CONTRIBUTING +++ b/CONTRIBUTING @@ -1,57 +1,57 @@ COMMIT POLICY Everybody is welcome to committing small fixes and one-liners without prior notification to the maintainer, provided that the following rules are followed: 1. Please keep your commits as small and as atomic as possible. 2. Do not push both formatting and code changes in the same commit. 3. Do not fix coding style and code issues in the same commit. For larger commits, please use the Review Board, Bugzilla or send and e-mail to the maintainer. A rule of thumb to check whether your commit is a major commit is if it affects more than 5 lines of code. Break down larger fixes into smaller commits. Even if you push the commits with one "git push", git preserves your commit info. i18n and documentation fixes, however large they are, may be directly committed without prior notification. CODING STYLE -KScreenGenie follows the KDELibs coding style, with a few execptions: +Spectacle follows the KDELibs coding style, with a few execptions: 1. In class definitions, access modifiers are aligned along with member declarations, i.e., at one level right. E.g.: class Hello : public QObject { Q_OBJECT public: void function(); } The access modifier ordering is: public, signals, public slots, protected slots, protected, private slots, private. Member variables come at the end, after all member functions. This is not strictly enforced, but is a good rule to follow. 2. Headers are cumulative, i.e., all headers that a particular class requires, are #include-ed in the class's header file, not the .cpp code file. The .cpp code file includes only one header, which is its own .h file. E.g., a class Foo, defined in Foo.h, will have its code in Foo.cpp, and Foo.cpp will #include only a single header Foo.h 3. Member variables follow the format mCamelCase, and not m_camelCase which is more common throughout the rest of the KDE Applications 4. Source files are mixed case, named the same as the class they contain. i.e., SomeClass will be defined in SomeClass.cpp, not someclass.cpp -Happy coding! \ No newline at end of file +Happy coding! diff --git a/Messages.sh b/Messages.sh index 82a0213..2430fbc 100644 --- a/Messages.sh +++ b/Messages.sh @@ -1,18 +1,16 @@ #!/bin/sh # Invoke the extractrc script on all .ui, .rc, and .kcfg files in the sources. # The results are stored in a pseudo .cpp file to be picked up by xgettext. lst=`find . -name \*.rc -o -name \*.ui -o -name \*.kcfg` if [ -n "$lst" ] ; then $EXTRACTRC $lst >> rc.cpp fi # If your framework contains tips-of-the-day, call preparetips as well. if [ -f "data/tips" ] ; then ( cd data && $PREPARETIPS > ../tips.cpp ) fi # Run xgettext to extract strings from all source files. -$XGETTEXT `find . -name \*.cpp -o -name \*.h -o -name \*.qml` -o $podir/kscreengenie.pot - - +$XGETTEXT `find . -name \*.cpp -o -name \*.h -o -name \*.qml` -o $podir/spectacle.pot diff --git a/README b/README new file mode 100644 index 0000000..ad26d53 --- /dev/null +++ b/README @@ -0,0 +1,34 @@ +Spectacle - The KDE Screenshot Utility + +Spectacle is screenshot taking utility for the KDE desktop. Spectacle +can also be used in non-KDE X11 desktop environments. + +Contributing +************ + +Spectacle is developed under the KDE umbrella and uses KDE infrastructure +for development. + +Please see the file CONTRIBUTING for details on coding style and how +to contribute patches. Please note that pull requests on GitHub aren't +supported. The recommended way of contributing patches is via KDE's +ReviewBoard at https://git.reviewboard.kde.org/ + +Release Schedule +**************** + +Spectacle is released as part of the KDE Applications suite and has three +major releases every year. They are numbered YY.MM, where YY is the two- +digit year and MM is the two-digit month. Major releases are made in April, +August and December every year. The Spectacle version follows the KDE +Applications release version. + +Reporting Bugs +************** + +Please report bugs at KDE's Bugzilla, available at https://bugs.kde.org/ + +For discussions, the #kde-devel IRC channel and the kde-devel mailing list +are good places to post. + +The current maintainer for Spectacle is Boudhayan Gupta \ No newline at end of file diff --git a/dbus/CMakeLists.txt b/dbus/CMakeLists.txt new file mode 100644 index 0000000..70112f8 --- /dev/null +++ b/dbus/CMakeLists.txt @@ -0,0 +1,6 @@ +# install the DBus service and interface files in the correct place + +configure_file(org.freedesktop.Screenshot.service.in ${CMAKE_CURRENT_BINARY_DIR}/org.freedesktop.Screenshot.service) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.freedesktop.Screenshot.service DESTINATION ${DBUS_SERVICES_INSTALL_DIR}) +install(FILES org.freedesktop.Screenshot.xml DESTINATION ${DBUS_INTERFACES_INSTALL_DIR}) diff --git a/dbus/org.freedesktop.Screenshot.service.in b/dbus/org.freedesktop.Screenshot.service.in new file mode 100644 index 0000000..8d740c5 --- /dev/null +++ b/dbus/org.freedesktop.Screenshot.service.in @@ -0,0 +1,3 @@ +[D-BUS Service] +Name=org.freedesktop.Screenshot +Exec=@CMAKE_INSTALL_PREFIX@/bin/spectacle --dbus diff --git a/dbus/org.freedesktop.Screenshot.xml b/dbus/org.freedesktop.Screenshot.xml new file mode 100644 index 0000000..0d90616 --- /dev/null +++ b/dbus/org.freedesktop.Screenshot.xml @@ -0,0 +1,107 @@ + + + + + + + + + + Starts the screenshot agent (the application for taking screenshots). + + + + + + + + Whether to include an image of the mouse pointer. + + + + + Takes a full-screen screenshot. + If the agent was activated using D-Bus, it takes a screenshot in the background without spawning the GUI and exits after the shot has been taken. + + + + + + + + Whether to include an image of the mouse pointer. + + + + + Takes a screenshot of the current screen. + If the agent was activated using D-Bus, it takes a screenshot in the background without spawning the GUI and exits after the shot has been taken. + + + + + + + + Whether to include the window titlebars and frames. + + + + + Whether to include an image of the mouse pointer. + + + + + Takes a screenshot of the window that currently has window focus. + If the agent was activated using D-Bus, it takes a screenshot in the background without spawning the GUI and exits after the shot has been taken. + + + + + + + + Whether to include the window titlebars and frames. + + + + + Whether to include an image of the mouse pointer. + + + + + Takes a screenshot of the window that is currently under the mouse cursor. + If the agent was activated using D-Bus, it takes a screenshot in the background without spawning the GUI and exits after the shot has been taken. + + + + + + + + The file name with which the screenshot was saved. + + + + + Emitted if the screenshot was successfully taken. + + + + + + + Emitted if the screenshot capture failed. + + + + + diff --git a/desktop/CMakeLists.txt b/desktop/CMakeLists.txt index 677331e..891fbba 100644 --- a/desktop/CMakeLists.txt +++ b/desktop/CMakeLists.txt @@ -1,11 +1,11 @@ # install the .desktop and rc files in the correct place install( - PROGRAMS org.kde.kscreengenie.desktop + PROGRAMS org.kde.spectacle.desktop DESTINATION ${XDG_APPS_INSTALL_DIR} ) install( - FILES kscreengenie.notifyrc - DESTINATION ${KNOTIFYRC_INSTALL_DIR} + FILES spectacle.notifyrc + DESTINATION ${KNOTIFYRC_INSTALL_DIR} ) diff --git a/desktop/org.kde.kscreengenie.desktop b/desktop/org.kde.spectacle.desktop similarity index 70% rename from desktop/org.kde.kscreengenie.desktop rename to desktop/org.kde.spectacle.desktop index 3c7f804..05edfac 100644 --- a/desktop/org.kde.kscreengenie.desktop +++ b/desktop/org.kde.spectacle.desktop @@ -1,48 +1,37 @@ [Desktop Entry] GenericName=Screenshot Capture Utility GenericName[bg]=Инструмент за заснемане на екрана GenericName[ca]=Utilitat de captura de pantalla GenericName[cs]=Nástroj na snímání obrazovky GenericName[de]=Dienstprogramm für Bildschirmfotos GenericName[en_GB]=Screenshot Capture Utility GenericName[es]=Utilidad de capturas de pantalla GenericName[fi]=Kuvankaappausohjelma GenericName[gl]=Utilidade para facer capturas de pantalla GenericName[nl]=Hulpmiddel voor het vangen van een schermafdruk GenericName[pl]=Narzędzie do przechwytywania ekranu GenericName[pt]=Utilitário de Captura de Imagens GenericName[pt_BR]=Utilitário de captura de tela GenericName[ro]=Utilitar de capturare a ecranului GenericName[sk]=Nástroj na snímanie obrazovky GenericName[sv]=Verktyg för att ta skärmbilder GenericName[uk]=Програма для створення знімків екрана GenericName[x-test]=xxScreenshot Capture Utilityxx GenericName[zh_CN]=屏幕截图工具 -Name=KScreenGenie -Name[bg]=KScreenGenie -Name[ca]=KScreenGenie -Name[cs]=KScreenGenie -Name[de]=KScreenGenie -Name[en_GB]=KScreenGenie -Name[es]=KScreenGenie -Name[fi]=KScreenGenie -Name[gl]=KScreenGenie -Name[nl]=KScreenGenie -Name[pl]=KDżinEkranu -Name[pt]=KScreenGenie -Name[pt_BR]=KScreenGenie -Name[ro]=KScreenGenie -Name[sk]=KScreenGenie -Name[sv]=Kscreengenie -Name[uk]=KScreenGenie -Name[x-test]=xxKScreenGeniexx -Name[zh_CN]=KScreenGenie +Name=Spectacle +Name[ca]=Spectacle +Name[es]=Spectacle +Name[nl]=Spectacle +Name[pt]=Spectacle +Name[pt_BR]=Spectacle +Name[uk]=Spectacle +Name[x-test]=xxSpectaclexx Categories=Qt;KDE;Utility; MimeType= -Exec=kscreengenie -Icon=ksnapshot +Exec=spectacle +Icon=spectacle Type=Application Terminal=false StartupNotify=false - - +X-DBUS-StartupType=Multi +X-DBUS-ServiceName=org.freedesktop.Screenshot diff --git a/desktop/kscreengenie.notifyrc b/desktop/spectacle.notifyrc similarity index 83% rename from desktop/kscreengenie.notifyrc rename to desktop/spectacle.notifyrc index 193d7e2..a681457 100644 --- a/desktop/kscreengenie.notifyrc +++ b/desktop/spectacle.notifyrc @@ -1,70 +1,59 @@ [Global] -IconName=kscreengenie -Name=KScreenGenie -Name[bg]=KScreenGenie -Name[ca]=KScreenGenie -Name[cs]=KScreenGenie -Name[de]=KScreenGenie -Name[en_GB]=KScreenGenie -Name[es]=KScreenGenie -Name[fi]=KScreenGenie -Name[gl]=KScreenGenie -Name[nl]=KScreenGenie -Name[pl]=KDżinEkranu -Name[pt]=KScreenGenie -Name[pt_BR]=KScreenGenie -Name[ro]=KScreenGenie -Name[sk]=KScreenGenie -Name[sv]=Kscreengenie -Name[uk]=KScreenGenie -Name[x-test]=xxKScreenGeniexx -Name[zh_CN]=KScreenGenie +IconName=spectacle +Name=Spectacle +Name[ca]=Spectacle +Name[es]=Spectacle +Name[nl]=Spectacle +Name[pt]=Spectacle +Name[pt_BR]=Spectacle +Name[uk]=Spectacle +Name[x-test]=xxSpectaclexx Comment=Screenshot Capture Utility Comment[ca]=Utilitat per pendre captures de pantalla Comment[cs]=Nástroj na snímání obrazovky Comment[de]=Dienstprogramm für Bildschirmfotos Comment[en_GB]=Screenshot Capture Utility Comment[es]=Utilidad de capturas de pantalla Comment[fi]=Kuvankaappausohjelma Comment[gl]=Utilidade para facer capturas de pantalla. Comment[nl]=Hulpmiddel voor het maken van een schermafdruk Comment[pl]=Narzędzie do przechwytywania ekranu Comment[pt]=Utilitário de Captura de Imagens Comment[pt_BR]=Utilitário de captura de tela Comment[sk]=Nástroj na zachytávanie obrazovky Comment[sv]=Verktyg för att ta skärmbilder Comment[uk]=Програма для створення знімків екрана Comment[x-test]=xxScreenshot Capture Utilityxx [Event/newScreenshotSaved] Name=New Screenshot Saved Name[ca]=S'ha desat una captura de pantalla nova Name[de]=Neues Bildschirmfoto gespeichert Name[en_GB]=New Screenshot Saved Name[es]=Nueva captura de pantalla guardada Name[fi]=Uusi kuvankaappaus tallennettu Name[gl]=Gardouse a nova captura Name[nl]=Nieuwe schermafdruk opgeslagen Name[pl]=Zapisano nowy zrzut ekranu Name[pt]=Nova Imagem Gravada Name[pt_BR]=Nova captura de tela salva Name[sk]=Nová obrazovka uložená Name[sv]=Ny skärmbild sparad Name[uk]=Збереженого нових знімок Name[x-test]=xxNew Screenshot Savedxx Comment=A new screenshot was captured and saved Comment[ca]=S'ha efectuat una captura de pantalla nova i s'ha desat Comment[de]=Ein neues Bildschirmfoto wurde aufgenommen und gespeichert Comment[en_GB]=A new screenshot was captured and saved Comment[es]=Se ha hecho y se ha guardado una nueva captura de pantalla Comment[fi]=Uusi kuva kaapattiin ja tallennettiin Comment[gl]=Fíxose unha nova captura de pantalla e gardouse. Comment[nl]=Een nieuwe schermafdruk is opgenomen en opgeslagen Comment[pl]=Przechwycono i zapisano zawartość ekranu Comment[pt]=Foi capturada e gravada uma nova imagem Comment[pt_BR]=Uma nova imagem foi obtida e salva Comment[sk]=Nová obrazovka bola zachytená a uložená Comment[sv]=En ny skärmbild togs och sparades Comment[uk]=Було створено і збережено новий знімок Comment[x-test]=xxA new screenshot was captured and savedxx Action=Popup diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 04b0fc6..1f691b0 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -1,7 +1,7 @@ # documentation KDOCTOOLS_CREATE_HANDBOOK( index.docbook INSTALL_DESTINATION ${KDE_INSTALL_DOCBUNDLEDIR}/en - SUBDIR ksnapshot + SUBDIR spectacle ) diff --git a/doc/KScreenGenie.png b/doc/Spectacle.png similarity index 100% rename from doc/KScreenGenie.png rename to doc/Spectacle.png diff --git a/doc/index.docbook b/doc/index.docbook index 280e4d7..deced0e 100644 --- a/doc/index.docbook +++ b/doc/index.docbook @@ -1,276 +1,276 @@ - + + BoudhayanGupta"> - me@BaloneyGeek.com"> + bgupta@kde.org"> ]> - + - The &kscreengenie; Handbook + 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; 2015-06-12 1.9.96 - &kscreengenie; 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. + &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 - &kscreengenie; + &spectacle; kdegraphics screenshot screen capture screen grab Introduction - &kscreengenie; 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. + &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 &kscreengenie; + Starting &spectacle; - &kscreengenie; can be started in a variety of ways, as described below: + &spectacle; can be started in a variety of ways, as described below: - In the application launcher menu, &kscreengenie; can be found at ApplicationsGraphicsScreenshot Capture Utility &kscreengenie; + In the application launcher menu, &spectacle; can be found at ApplicationsGraphicsScreenshot Capture Utility &spectacle; - Pressing the Print Screen button on the keyboard will immediately launch &kscreengenie;. Additionally, two more keyboard shortcuts are available. Pressing &Alt;Print Screen will take a screenshot of the active window and save it in your default Pictures folder without showing the GUI, while pressing &Shift;Print Screen will take a screenshot of your entire desktop and save it in your default Pictures folder without showing the GUI. + Pressing the Print Screen button on the keyboard will immediately launch &spectacle;. Additionally, two more keyboard shortcuts are available. Pressing &Alt;Print Screen will take a screenshot of the active window and save it in your default Pictures folder without showing the GUI, while pressing &Shift;Print Screen will take a screenshot of your entire desktop and save it in your default Pictures folder without showing the GUI. - The mini command line &krunner; (invoked with &Alt;F2) may also be used to start &kscreengenie;. + The mini command line &krunner; (invoked with &Alt;F2) may also be used to start &spectacle;. - &kscreengenie; can be started from the command-line. &kscreengenie; 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. + &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 &kscreengenie; from the command prompt, type in: + To start &spectacle; from the command prompt, type in: - % kscreengenie & + % spectacle & To view the full list of command-line options and their explanation, type in: - % kscreengenie --help + % spectacle --help - Using &kscreengenie; + Using &spectacle; - Once &kscreengenie; starts, you will see a window like the following: + Once &spectacle; starts, you will see a window like the following: - &kscreengenie; Main Window + &spectacle; Main Window - &kscreengenie; 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. + &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 &kscreengenie; is displayed in the preview window, which is located on the left-hand side of the &kscreengenie; application window. + The snapshot taken by &spectacle; is displayed in the preview window, which is located on the left-hand side of the &spectacle; application window. - To quickly save the image and quit &kscreengenie; press the Save & Exit (&Ctrl;Q) button. By default, this saves the image as a PNG file in your default Pictures folder, and exits the application immediately. The default save location and filename can be configured, as described later. + To quickly save the image and quit &spectacle; press the Save & Exit (&Ctrl;Q) button. By default, this saves the image as a PNG file in your default Pictures folder, and exits the application immediately. The default save location and filename can be configured, as described later. The image can be saved by clicking on the arrow portion of the Save & Exit button, and choosing Save As... (&Ctrl;&Shift;S) option. This opens the standard &kde; 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. You may also select the Save... (&Ctrl;S) option, which will save the image to its default location and with the default filename. Taking A Screenshot To discard the current screenshot and take another screenshot, press the Take 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 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 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 pop-up menu, &kscreengenie; tries to take a screenshot of the menu as well as its parent window. + 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 pop-up 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, &kscreengenie; falls back to old way of capturing the image automatically, and captures an image of only the pop-up 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 pop-up 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 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 New Screenshot (&Ctrl;N) button and taking the screenshot. This delay can be set in increments of 0.1 seconds, or 100 milliseconds. - Enabling the On Click checkbox overrides the delay. When this checkbox is enabled, pressing the Take New Screenshot (&Ctrl;N) button hides the &kscreengenie; 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. + Enabling the On Click checkbox overrides the delay. When this checkbox is enabled, pressing the Take 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 - 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 &kscreengenie; shall only capture the image of the current pop-up menu under the cursor, or also include the parent window. + 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 pop-up menu under the cursor, or also include the parent window. 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 Capture 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 pop-up menu under the cursor, without its parent window. Additional Functionality Buttons - There are five buttons located at the bottom of the &kscreengenie; window. Their functions are described below: + There are five buttons located at the bottom of the &spectacle; window. Their functions are described below: Help - This button gives you a menu where you can open the &kscreengenie; Handbook, report a bug, switch the language for &kscreengenie; or get some more information About &kscreengenie; and About &kde;. + This button gives you a menu where you can open the &spectacle; Handbook, report a bug, switch the language for &spectacle; or get some more information About &spectacle; and About &kde;. Open With... 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 e-mail 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 Clicking this button saves the screenshot as a PNG image in your default Pictures directory and immediately exits the application. Additionally, if you click the arrow on the right side of the button, a drop-down menu allows you to simply Save the image, save the image with a different filename, location and format (Save As...), Print the image, and configure the default save options, such as where to save the image by default and what filename to save it as. Discard Discards the screenshot and immediately exits the application. Configure Save Options - When you use the Save & Exit or the Save functions, &kscreengenie; saves the image with a default filename, in your Pictures folder under your home directory. The default filename includes the date and time when the image was taken. + 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 directory. The default filename includes the date and time when the image was taken. The Configure Save Options option allows you to set the default save location and filename. Clicking this option brings up a dialog box like the following: Configure Save Options The dialog box includes appropriate help text on how to configure the save options. 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/icons/16-apps-ksnapshot.png b/icons/16-apps-spectacle.png similarity index 100% rename from icons/16-apps-ksnapshot.png rename to icons/16-apps-spectacle.png diff --git a/icons/22-apps-ksnapshot.png b/icons/22-apps-spectacle.png similarity index 100% rename from icons/22-apps-ksnapshot.png rename to icons/22-apps-spectacle.png diff --git a/icons/32-apps-ksnapshot.png b/icons/32-apps-spectacle.png similarity index 100% rename from icons/32-apps-ksnapshot.png rename to icons/32-apps-spectacle.png diff --git a/icons/48-apps-ksnapshot.png b/icons/48-apps-spectacle.png similarity index 100% rename from icons/48-apps-ksnapshot.png rename to icons/48-apps-spectacle.png diff --git a/icons/CMakeLists.txt b/icons/CMakeLists.txt index d162049..2c6d2e3 100644 --- a/icons/CMakeLists.txt +++ b/icons/CMakeLists.txt @@ -1,14 +1,14 @@ # default icons for the hicolor theme. # have ecm install them ecm_install_icons( ICONS - 16-apps-ksnapshot.png - 22-apps-ksnapshot.png - 32-apps-ksnapshot.png - 48-apps-ksnapshot.png - sc-apps-ksnapshot.svgz + 16-apps-spectacle.png + 22-apps-spectacle.png + 32-apps-spectacle.png + 48-apps-spectacle.png + sc-apps-spectacle.svgz DESTINATION ${ICON_INSTALL_DIR} THEME hicolor ) diff --git a/icons/sc-apps-ksnapshot.svgz b/icons/sc-apps-spectacle.svgz similarity index 100% rename from icons/sc-apps-ksnapshot.svgz rename to icons/sc-apps-spectacle.svgz diff --git a/src/.reviewboardrc b/src/.reviewboardrc index bad6f18..1c284e9 100644 --- a/src/.reviewboardrc +++ b/src/.reviewboardrc @@ -1,3 +1,3 @@ REVIEWBOARD_URL = "https://git.reviewboard.kde.org" -REPOSITORY = 'git://anongit.kde.org/kscreengenie' -PEOPLE='bgupta' \ No newline at end of file +REPOSITORY = 'git://anongit.kde.org/spectacle' +PEOPLE='bgupta' diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5401097..bd1af08 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,91 +1,112 @@ # common - configure file and version definitions configure_file(Config.h.in ${CMAKE_CURRENT_BINARY_DIR}/Config.h) # target set( - KSG_SRCS_DEFAULT + SPECTACLE_SRCS_DEFAULT Main.cpp +<<<<<<< HEAD KSCore.cpp +======= + SpectacleCore.cpp + SpectacleDBusAdapter.cpp +>>>>>>> master PlatformBackends/ImageGrabber.cpp PlatformBackends/DummyImageGrabber.cpp Gui/KSMainWindow.cpp Gui/KSWidget.cpp Gui/KSImageWidget.cpp Gui/KSSaveConfigDialog.cpp Gui/KSSendToMenu.cpp +<<<<<<< HEAD Editor/KSImageEditor.cpp Editor/KSCropArea.cpp Editor/KSLineTool.cpp +======= + Gui/ScreenClipper.cpp + Gui/SmartSpinBox.cpp +>>>>>>> master ) qt5_add_resources(KSG_SRCS_RESOURCES Editor/QmlResources/QmlResources.qrc) if(XCB_FOUND) set( - KSG_SRCS_X11 + SPECTACLE_SRCS_X11 PlatformBackends/X11ImageGrabber.cpp ) endif() if(KF5Kipi_FOUND) set( - KSG_SRCS_KIPI + SPECTACLE_SRCS_KIPI KipiInterface/KSGKipiInterface.cpp KipiInterface/KSGKipiInfoShared.cpp KipiInterface/KSGKipiImageCollectionShared.cpp KipiInterface/KSGKipiImageCollectionSelector.cpp ) endif() set( +<<<<<<< HEAD KSG_SRCS_ALL ${KSG_SRCS_DEFAULT} ${KSG_SRCS_KIPI} ${KSG_SRCS_X11} ${KSG_SRCS_RESOURCES} +======= + SPECTACLE_SRCS_ALL + ${SPECTACLE_SRCS_DEFAULT} + ${SPECTACLE_SRCS_KIPI} + ${SPECTACLE_SRCS_X11} +>>>>>>> master ) add_executable( - kscreengenie - ${KSG_SRCS_ALL} + spectacle + ${SPECTACLE_SRCS_ALL} ) # link libraries target_link_libraries( - kscreengenie + spectacle Qt5::DBus Qt5::PrintSupport Qt5::Quick KF5::CoreAddons +<<<<<<< HEAD KF5::Declarative +======= + KF5::DBusAddons + KF5::WidgetsAddons +>>>>>>> master KF5::Notifications - KF5::ConfigGui + KF5::ConfigCore KF5::I18n KF5::KIOWidgets KF5::WindowSystem KF5::XmlGui - KF5::WidgetsAddons ) if(XCB_FOUND) target_link_libraries( - kscreengenie + spectacle XCB::XFIXES XCB::IMAGE XCB::CURSOR XCB::UTIL Qt5::X11Extras KF5::Screen ) endif() if(KF5Kipi_FOUND) target_link_libraries ( - kscreengenie + spectacle KF5::Kipi ) endif() -install(TARGETS kscreengenie ${INSTALL_TARGETS_DEFAULT_ARGS}) +install(TARGETS spectacle ${INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/src/Config.h.in b/src/Config.h.in index df9710c..bdc8c8f 100644 --- a/src/Config.h.in +++ b/src/Config.h.in @@ -1,13 +1,13 @@ -#ifndef KSG_CONFIG_H -#define KSG_CONFIG_H +#ifndef SPECTACLE_CONFIG_H +#define SPECTACLE_CONFIG_H /* Define to 1 if we are building with XCB */ #cmakedefine XCB_FOUND 1 /* Define to 1 if we have KIPI */ #cmakedefine KIPI_FOUND 1 -/* Set the KScreenGenie version from CMake */ -#cmakedefine KSG_VERSION "@KSG_VERSION@" +/* Set the Spectacle version from CMake */ +#cmakedefine SPECTACLE_VERSION "@SPECTACLE_VERSION@" #endif diff --git a/src/Gui/KSImageWidget.cpp b/src/Gui/KSImageWidget.cpp index 213260d..bb0e8ca 100644 --- a/src/Gui/KSImageWidget.cpp +++ b/src/Gui/KSImageWidget.cpp @@ -1,82 +1,82 @@ /* - * Copyright (C) 2015 Boudhayan Gupta + * 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 "KSImageWidget.h" KSImageWidget::KSImageWidget(QWidget *parent): QLabel(parent), mPixmap(QPixmap()) { mDSEffect = new QGraphicsDropShadowEffect(this); mDSEffect->setBlurRadius(5); mDSEffect->setOffset(0); mDSEffect->setColor(QColor(Qt::black)); setGraphicsEffect(mDSEffect); setCursor(Qt::OpenHandCursor); setAlignment(Qt::AlignCenter); } void KSImageWidget::setScreenshot(const QPixmap &pixmap) { mPixmap = pixmap; setToolTip(i18n("Image Size: %1x%2 pixels", mPixmap.width(), mPixmap.height())); setPixmap(mPixmap.scaled(size(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); } // drag handlers void KSImageWidget::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { mDragStartPosition = event->pos(); setCursor(Qt::ClosedHandCursor); } } void KSImageWidget::mouseReleaseEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { setCursor(Qt::OpenHandCursor); } } void KSImageWidget::mouseMoveEvent(QMouseEvent *event) { if (!(event->buttons() & Qt::LeftButton)) { return; } if ((event->pos() - mDragStartPosition).manhattanLength() < QGuiApplication::styleHints()->startDragDistance()) { return; } setCursor(Qt::OpenHandCursor); emit dragInitiated(); } // resize handler void KSImageWidget::resizeEvent(QResizeEvent *event) { Q_UNUSED(event); setPixmap(mPixmap.scaled(size(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); } diff --git a/src/Gui/KSImageWidget.h b/src/Gui/KSImageWidget.h index 58941e2..aa6f73a 100644 --- a/src/Gui/KSImageWidget.h +++ b/src/Gui/KSImageWidget.h @@ -1,61 +1,61 @@ /* - * Copyright (C) 2015 Boudhayan Gupta + * 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 KSIMAGEWIDGET_H #define KSIMAGEWIDGET_H #include #include #include #include #include #include #include #include #include class KSImageWidget : public QLabel { Q_OBJECT public: explicit KSImageWidget(QWidget *parent = 0); void setScreenshot(const QPixmap &pixmap); signals: void dragInitiated(); protected: void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE; void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE; void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE; private: QGraphicsDropShadowEffect *mDSEffect; QPixmap mPixmap; QPoint mDragStartPosition; }; #endif // KSIMAGEWIDGET_H diff --git a/src/Gui/KSMainWindow.cpp b/src/Gui/KSMainWindow.cpp index 417d62d..beca83b 100644 --- a/src/Gui/KSMainWindow.cpp +++ b/src/Gui/KSMainWindow.cpp @@ -1,236 +1,236 @@ /* - * Copyright (C) 2015 Boudhayan Gupta + * 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" KSMainWindow::KSMainWindow(bool onClickAvailable, QWidget *parent) : QDialog(parent), mKSWidget(new KSWidget), mDivider(new QFrame), mDialogButtonBox(new QDialogButtonBox), mSendToButton(new QPushButton), mClipboardButton(new QToolButton), mSaveButton(new QToolButton), mSaveMenu(new QMenu), mCopyMessage(new KMessageWidget), mSendToMenu(new KSSendToMenu), mActionCollection(new KActionCollection(this, "KSStandardActions")), 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 kscreengenie window that appears + // 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 (qApp->platformName() == QStringLiteral("xcb")) { // 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("kscreengenierc"); + KSharedConfigPtr config = KSharedConfig::openConfig("spectaclerc"); KConfigGroup guiConfig(config, "GuiConfig"); // window properties setMinimumSize(840, 420); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); resize(minimumSize()); QPoint location = guiConfig.readEntry("window-position", QPoint(50, 50)); move(location); // the KSGWidget connect(mKSWidget, &KSWidget::newScreenshotRequest, this, &KSMainWindow::captureScreenshot); connect(mKSWidget, &KSWidget::dragInitiated, this, &KSMainWindow::dragAndDropRequest); // the Button Bar mDialogButtonBox->setStandardButtons(QDialogButtonBox::Help | QDialogButtonBox::Discard); KGuiItem::assign(mSendToButton, KGuiItem(i18n("Open With..."))); mSendToButton->setIcon(QIcon::fromTheme("application-x-executable")); 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->setIcon(QIcon::fromTheme("edit-copy")); mClipboardButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); mDialogButtonBox->addButton(mClipboardButton, QDialogButtonBox::ActionRole); mSaveMenu->addAction(KStandardAction::save(this, SIGNAL(save()), this)); mSaveMenu->addAction(KStandardAction::saveAs(this, SIGNAL(saveAsClicked()), this)); mSaveMenu->addAction(KStandardAction::print(this, SLOT(showPrintDialog()), this)); mSaveMenu->addAction(QIcon::fromTheme("applications-system"), i18n("Configure Save Options"), this, SLOT(showSaveConfigDialog())); QAction *saveAndExitAction = new QAction(QIcon::fromTheme("document-save"), i18n("Save &&& Exit"), this); saveAndExitAction->setToolTip(i18n("Save screenshot in your Pictures directory and exit")); saveAndExitAction->setShortcut(QKeySequence(QKeySequence::Quit)); connect(saveAndExitAction, &QAction::triggered, this, &KSMainWindow::saveAndExit); mSaveButton->setDefaultAction(saveAndExitAction); mSaveButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); mSaveButton->setMenu(mSaveMenu); mSaveButton->setPopupMode(QToolButton::MenuButtonPopup); mDialogButtonBox->addButton(mSaveButton, QDialogButtonBox::ActionRole); QShortcut *shortcut = new QShortcut(QKeySequence(Qt::Key_Escape), mDialogButtonBox->button(QDialogButtonBox::Discard)); connect(shortcut, &QShortcut::activated, qApp, &QApplication::quit); connect(mDialogButtonBox->button(QDialogButtonBox::Discard), &QPushButton::clicked, qApp, &QApplication::quit); // the help menu KHelpMenu *helpMenu = new KHelpMenu(this, KAboutData::applicationData(), true); mDialogButtonBox->button(QDialogButtonBox::Help)->setMenu(helpMenu->menu()); // copy-to-clipboard message mCopyMessage->setText(i18n("The screenshot has been copied to the clipboard.")); mCopyMessage->setMessageType(KMessageWidget::Information); mCopyMessage->setIcon(QIcon::fromTheme("dialog-information")); // layouts mDivider->setFrameShape(QFrame::HLine); mDivider->setLineWidth(2); QVBoxLayout *layout = new QVBoxLayout(this); layout->addWidget(mKSWidget); layout->addWidget(mCopyMessage); layout->addWidget(mDivider); layout->addWidget(mDialogButtonBox); mCopyMessage->hide(); // populate our send-to actions connect(mSendToMenu, &KSSendToMenu::sendToServiceRequest, this, &KSMainWindow::sendToKServiceRequest); connect(mSendToMenu, &KSSendToMenu::sendToClipboardRequest, this, &KSMainWindow::sendToClipboardRequest); connect(mSendToMenu, &KSSendToMenu::sendToOpenWithRequest, this, &KSMainWindow::sendToOpenWithRequest); mSendToButton->setMenu(mSendToMenu->menu()); // disable onClick mode if not available on the platform if (!mOnClickAvailable) { mKSWidget->disableOnClick(); } // done with the init } // overrides void KSMainWindow::moveEvent(QMoveEvent *event) { Q_UNUSED(event); - KSharedConfigPtr config = KSharedConfig::openConfig("kscreengenierc"); + KSharedConfigPtr config = KSharedConfig::openConfig("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); setWindowTitle(i18nc("Unsaved Screenshot", "Unsaved[*]")); setWindowModified(true); KGuiItem::assign(mDialogButtonBox->button(QDialogButtonBox::Discard), KStandardGuiItem::discard()); show(); if (mSendToMenu->menu()->isEmpty()) { QTimer::singleShot(100, mSendToMenu, &KSSendToMenu::populateMenu); } } void KSMainWindow::showPrintDialog() { QPrinter *printer = new QPrinter(QPrinter::HighResolution); QPrintDialog printDialog(printer, this); if (printDialog.exec() == QDialog::Accepted) { emit printRequest(printer); return; } delete printer; } void KSMainWindow::sendToClipboard() { emit sendToClipboardRequest(); mCopyMessage->animatedShow(); QTimer::singleShot(5000, mCopyMessage, &KMessageWidget::animatedHide); } void KSMainWindow::showSaveConfigDialog() { KSSaveConfigDialog saveDialog(this); saveDialog.exec(); } void KSMainWindow::setScreenshotWindowTitle(QUrl location) { setWindowTitle(location.fileName()); setWindowModified(false); KGuiItem::assign(mDialogButtonBox->button(QDialogButtonBox::Discard), KStandardGuiItem::quit()); } diff --git a/src/Gui/KSMainWindow.h b/src/Gui/KSMainWindow.h index 5e63086..4866c05 100644 --- a/src/Gui/KSMainWindow.h +++ b/src/Gui/KSMainWindow.h @@ -1,116 +1,116 @@ /* - * Copyright (C) 2015 Boudhayan Gupta + * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Config.h" #ifdef XCB_FOUND #include #include #endif #include "PlatformBackends/ImageGrabber.h" #include "KSWidget.h" #include "KSSaveConfigDialog.h" #include "KSSendToMenu.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 showSaveConfigDialog(); void sendToClipboard(); void init(); 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(); void save(); void saveAndExit(); void saveAsClicked(); void sendToKServiceRequest(KService::Ptr servicePointer); void sendToClipboardRequest(); void sendToOpenWithRequest(); void printRequest(QPrinter *); protected: void moveEvent(QMoveEvent *event); private: KSWidget *mKSWidget; QFrame *mDivider; QDialogButtonBox *mDialogButtonBox; QPushButton *mSendToButton; QToolButton *mClipboardButton; QToolButton *mSaveButton; QMenu *mSaveMenu; KMessageWidget *mCopyMessage; KSSendToMenu *mSendToMenu; KActionCollection *mActionCollection; bool mOnClickAvailable; }; #endif // KSMAINWINDOW_H diff --git a/src/Gui/KSSaveConfigDialog.cpp b/src/Gui/KSSaveConfigDialog.cpp index 196311b..6b7fb76 100644 --- a/src/Gui/KSSaveConfigDialog.cpp +++ b/src/Gui/KSSaveConfigDialog.cpp @@ -1,141 +1,141 @@ /* - * Copyright (C) 2015 Boudhayan Gupta + * 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 "KSSaveConfigDialog.h" KSSaveConfigDialog::KSSaveConfigDialog(QWidget *parent) : QDialog(parent) { // set the window properties first setWindowTitle(i18n("Configure Save Options")); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); setFixedSize(500, 600); // bring up the configuration reader - KSharedConfigPtr config = KSharedConfig::openConfig("kscreengenierc"); + KSharedConfigPtr config = KSharedConfig::openConfig("spectaclerc"); KConfigGroup generalConfig = KConfigGroup(config, "General"); // set up the layout. start with the directory QGroupBox *dirGroup = new QGroupBox(i18n("Default Save Directory")); QVBoxLayout *dirLayout = new QVBoxLayout; dirGroup->setLayout(dirLayout); dirGroup->setStyleSheet("QGroupBox { font-weight: bold; }"); QLabel *dirHelpText = new QLabel; dirHelpText->setWordWrap(true); dirHelpText->setText(i18n("Set the directory where you'd like to save your screenshots when you press " "Save or Save & Exit.")); dirLayout->addWidget(dirHelpText); QHBoxLayout *urlRequesterLayout = new QHBoxLayout; urlRequesterLayout->addWidget(new QLabel(i18n("Location:"))); mUrlRequester = new KUrlRequester; mUrlRequester->setMode(KFile::Directory); const QString path = generalConfig.readPathEntry("default-save-location", QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); mUrlRequester->setUrl(QUrl::fromUserInput(path)); urlRequesterLayout->addWidget(mUrlRequester); dirLayout->addLayout(urlRequesterLayout); // now the save filename format layout QGroupBox *fmtGroup = new QGroupBox(i18n("Default Save Filename")); QVBoxLayout *fmtLayout = new QVBoxLayout; fmtGroup->setLayout(fmtLayout); fmtGroup->setStyleSheet("QGroupBox { font-weight: bold; }"); const QString helpText = i18n( "

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: Date
" + "%D: Day
" "%H: Hour
" "%m: Minute
" "%S: Second" "
" "

You don't need to enter a filetype extension. By default, screenshots are always saved " "as a PNG (Portable Network Graphics) image with a .png extension.

" "

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\".

" ); QLabel *fmtHelpText = new QLabel; fmtHelpText->setWordWrap(true); fmtHelpText->setText(helpText); fmtHelpText->setTextFormat(Qt::RichText); fmtLayout->addWidget(fmtHelpText); QHBoxLayout *saveNameLayout = new QHBoxLayout; saveNameLayout->addWidget(new QLabel(i18n("Filename:"))); mSaveNameFormat = new QLineEdit; const QString saveFmt = generalConfig.readEntry("save-filename-format", "Screenshot_%Y%M%D_%H%m%S"); mSaveNameFormat->setText(saveFmt); saveNameLayout->addWidget(mSaveNameFormat); fmtLayout->addLayout(saveNameLayout); // finish up with the main layout QVBoxLayout *mainLayout = new QVBoxLayout(this); mainLayout->addWidget(dirGroup); mainLayout->addWidget(fmtGroup); mDialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); connect(mDialogButtonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, this, &KSSaveConfigDialog::accept); connect(mDialogButtonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &KSSaveConfigDialog::reject); mainLayout->addWidget(mDialogButtonBox); setLayout(mainLayout); } KSSaveConfigDialog::~KSSaveConfigDialog() {} void KSSaveConfigDialog::accept() { // bring up the configuration reader - KSharedConfigPtr config = KSharedConfig::openConfig("kscreengenierc"); + KSharedConfigPtr config = KSharedConfig::openConfig("spectaclerc"); KConfigGroup generalConfig = KConfigGroup(config, "General"); // save the data generalConfig.writePathEntry("default-save-location", mUrlRequester->url().toDisplayString(QUrl::PreferLocalFile)); generalConfig.writeEntry("save-filename-format", mSaveNameFormat->text()); // done emit done(QDialog::Accepted); } diff --git a/src/Gui/KSSaveConfigDialog.h b/src/Gui/KSSaveConfigDialog.h index edf39c9..44a72b3 100644 --- a/src/Gui/KSSaveConfigDialog.h +++ b/src/Gui/KSSaveConfigDialog.h @@ -1,59 +1,59 @@ /* - * Copyright (C) 2015 Boudhayan Gupta + * 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 KSSAVECONFIGDIALOG_H #define KSSAVECONFIGDIALOG_H #include #include #include #include #include #include #include #include #include #include #include #include #include class KSSaveConfigDialog : public QDialog { Q_OBJECT public: explicit KSSaveConfigDialog(QWidget *parent = 0); ~KSSaveConfigDialog(); public slots: void accept() Q_DECL_OVERRIDE; private: QDialogButtonBox *mDialogButtonBox; KUrlRequester *mUrlRequester; QLineEdit *mSaveNameFormat; }; #endif // KSSAVECONFIGDIALOG_H diff --git a/src/Gui/KSSendToMenu.cpp b/src/Gui/KSSendToMenu.cpp index f009c28..e64db3d 100644 --- a/src/Gui/KSSendToMenu.cpp +++ b/src/Gui/KSSendToMenu.cpp @@ -1,133 +1,133 @@ /* - * Copyright (C) 2015 Boudhayan Gupta + * Copyright (C) 2015 Boudhayan Gupta * * Contains code from ksnapshotsendtoactions.cpp, part of KSnapshot. * Copyright notices reproduced below: * * Copyright (C) 2014 Gregor Mi * moved from ksnapshot.h, see authors there * * 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 "KSSendToMenu.h" KSSendToMenu::KSSendToMenu(QObject *parent) : QObject(parent), mMenu(new QMenu) {} KSSendToMenu::~KSSendToMenu() {} void KSSendToMenu::populateMenu() { populateKServiceSendToActions(); #ifdef KIPI_FOUND mMenu->addSeparator(); populateKipiSendToActions(); #endif mMenu->addSeparator(); QAction *sendToAction = new QAction(this); sendToAction->setText(i18n("Other Application")); sendToAction->setIcon(QIcon::fromTheme("application-x-executable")); sendToAction->setShortcuts(KStandardShortcut::open()); connect(sendToAction, &QAction::triggered, this, &KSSendToMenu::sendToOpenWithRequest); mMenu->addAction(sendToAction); } // return menu QMenu *KSSendToMenu::menu() { return mMenu; } // send-to handlers void KSSendToMenu::handleSendToKService() { QAction *action = qobject_cast(QObject::sender()); if (!(action)) { qWarning() << "Internal qobject_cast error. This is a bug."; return; } auto data = action->data().value(); emit sendToServiceRequest(data); } // populators void KSSendToMenu::populateKServiceSendToActions() { const KService::List services = KMimeTypeTrader::self()->query("image/png"); for (auto service: services) { QString name = service->name().replace('&', "&&"); QAction *action = new QAction(QIcon::fromTheme(service->icon()), name, nullptr); action->setData(QVariant::fromValue(service)); connect(action, &QAction::triggered, this, &KSSendToMenu::handleSendToKService); mMenu->addAction(action); } } #ifdef KIPI_FOUND void KSSendToMenu::populateKipiSendToActions() { mKipiInterface = new KSGKipiInterface(this); KIPI::PluginLoader *loader = new KIPI::PluginLoader; loader->setInterface(mKipiInterface); loader->init(); KIPI::PluginLoader::PluginList pluginList = loader->pluginList(); for (auto pluginInfo: pluginList) { if (!(pluginInfo->shouldLoad())) { continue; } KIPI::Plugin *plugin = pluginInfo->plugin(); if (!(plugin)) { qWarning() << i18n("KIPI plugin from library %1 failed to load", pluginInfo->library()); continue; } plugin->setup(&mDummyWidget); QList actions = plugin->actions(); QSet exportActions; for (auto action: actions) { KIPI::Category category = plugin->category(action); if (category == KIPI::ExportPlugin) { exportActions += action; } else if (category == KIPI::ImagesPlugin && pluginInfo->library().contains("kipiplugin_sendimages")) { exportActions += action; } } for (auto action: exportActions) { mMenu->addAction(action); } } } #endif diff --git a/src/Gui/KSSendToMenu.h b/src/Gui/KSSendToMenu.h index 062a72a..b5b2ae6 100644 --- a/src/Gui/KSSendToMenu.h +++ b/src/Gui/KSSendToMenu.h @@ -1,88 +1,88 @@ /* - * Copyright (C) 2015 Boudhayan Gupta + * 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 KSSENDTOMENU_H #define KSSENDTOMENU_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Config.h" #ifdef KIPI_FOUND #include #include #include #include "KipiInterface/KSGKipiInterface.h" #endif class KSSendToMenu : public QObject { Q_OBJECT public: explicit KSSendToMenu(QObject *parent = 0); ~KSSendToMenu(); QMenu *menu(); signals: void sendToServiceRequest(KService::Ptr servicePointer); void sendToClipboardRequest(); void sendToOpenWithRequest(); public slots: void populateMenu(); private slots: void handleSendToKService(); private: void populateKServiceSendToActions(); #ifdef KIPI_FOUND void populateKipiSendToActions(); KSGKipiInterface *mKipiInterface; QWidget mDummyWidget; #endif QMenu *mMenu; }; Q_DECLARE_METATYPE(KService::Ptr) #endif // KSSENDTOMENU_H diff --git a/src/Gui/KSWidget.cpp b/src/Gui/KSWidget.cpp index 7b36810..b3e4915 100644 --- a/src/Gui/KSWidget.cpp +++ b/src/Gui/KSWidget.cpp @@ -1,231 +1,231 @@ /* - * Copyright (C) 2015 Boudhayan Gupta + * 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" KSWidget::KSWidget(QWidget *parent) : QWidget(parent) { // 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(this); mCaptureModeLabel->setText(i18n("Capture Mode")); 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 QDoubleSpinBox(this); + mDelayMsec = new SmartSpinBox(this); mDelayMsec->setDecimals(1); mDelayMsec->setSingleStep(1.0); mDelayMsec->setMinimum(0.0); mDelayMsec->setMaximum(999.9); mDelayMsec->setSuffix(i18n(" seconds")); mDelayMsec->setMinimumWidth(160); - connect(mDelayMsec, static_cast(&QDoubleSpinBox::valueChanged), this, &KSWidget::captureDelayChanged); + connect(mDelayMsec, static_cast(&SmartSpinBox::valueChanged), this, &KSWidget::captureDelayChanged); mCaptureOnClick = new QCheckBox(this); mCaptureOnClick->setText(i18n("On Click")); mCaptureOnClick->setToolTip(i18n("Wait for a mouse click before capturing the screenshot image")); connect(mCaptureOnClick, &QCheckBox::stateChanged, this, &KSWidget::onClickStateChanged); connect(mCaptureOnClick, &QCheckBox::stateChanged, this, &KSWidget::checkboxStatesChanged); 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) mContentOptionsLabel = new QLabel(this); mContentOptionsLabel->setText(i18n("Content Options")); mMousePointer = new QCheckBox(this); mMousePointer->setText(i18n("Include mouse pointer")); mMousePointer->setToolTip(i18n("Show the mouse cursor in the screeenshot image")); connect(mMousePointer, &QCheckBox::stateChanged, this, &KSWidget::checkboxStatesChanged); mWindowDecorations = new QCheckBox(this); mWindowDecorations->setText(i18n("Include window titlebar and borders")); mWindowDecorations->setToolTip(i18n("Show the window title bar, the minimize/maximize/close buttons, and the window border")); mWindowDecorations->setEnabled(false); connect(mWindowDecorations, &QCheckBox::stateChanged, this, &KSWidget::checkboxStatesChanged); mCaptureTransientOnly = new QCheckBox(this); mCaptureTransientOnly->setText(i18n("Capture the current pop-up only")); mCaptureTransientOnly->setToolTip(i18n("Capture only the current pop-up window (like a menu, tooltip etc). " "If this is not enabled, the pop-up is captured along with the parent window")); mCaptureTransientOnly->setEnabled(false); connect(mCaptureTransientOnly, &QCheckBox::stateChanged, this, &KSWidget::checkboxStatesChanged); mContentOptionsForm = new QVBoxLayout; mContentOptionsForm->addWidget(mMousePointer); mContentOptionsForm->addWidget(mWindowDecorations); mContentOptionsForm->addWidget(mCaptureTransientOnly); mContentOptionsForm->setSpacing(16); mContentOptionsForm->setContentsMargins(24, 0, 0, 0); // the take new screenshot button mTakeScreenshotButton = new QPushButton(this); mTakeScreenshotButton->setText(i18n("Take New Screenshot")); - mTakeScreenshotButton->setIcon(QIcon::fromTheme("ksnapshot")); + mTakeScreenshotButton->setIcon(QIcon::fromTheme("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(20, 0, 0, 10); mMainLayout = new QGridLayout(this); mMainLayout->addWidget(mImageWidget, 0, 0, 1, 1); mMainLayout->addLayout(mRightLayout, 0, 1, 1, 1); mMainLayout->setColumnMinimumWidth(0, 400); mMainLayout->setColumnMinimumWidth(1, 400); // and read in the saved checkbox states and capture mode indices - KSharedConfigPtr config = KSharedConfig::openConfig("kscreengenierc"); + KSharedConfigPtr config = KSharedConfig::openConfig("spectaclerc"); KConfigGroup guiConfig(config, "GuiConfig"); mMousePointer->setChecked(guiConfig.readEntry("includePointer", true)); mWindowDecorations->setChecked(guiConfig.readEntry("includeDecorations", true)); mCaptureOnClick->setChecked(guiConfig.readEntry("waitCaptureOnClick", false)); mCaptureTransientOnly->setChecked(guiConfig.readEntry("transientOnly", false)); mCaptureArea->setCurrentIndex(guiConfig.readEntry("captureModeIndex", 0)); mDelayMsec->setValue(guiConfig.readEntry("captureDelay", (qreal)0)); // done } // 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::checkboxStatesChanged(int state) { Q_UNUSED(state); - KSharedConfigPtr config = KSharedConfig::openConfig("kscreengenierc"); + KSharedConfigPtr config = KSharedConfig::openConfig("spectaclerc"); KConfigGroup guiConfig(config, "GuiConfig"); guiConfig.writeEntry("includePointer", mMousePointer->isChecked()); guiConfig.writeEntry("includeDecorations", mWindowDecorations->isChecked()); guiConfig.writeEntry("waitCaptureOnClick", mCaptureOnClick->isChecked()); guiConfig.writeEntry("transientOnly", mCaptureTransientOnly->isChecked()); guiConfig.sync(); } 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) { - KSharedConfigPtr config = KSharedConfig::openConfig("kscreengenierc"); + KSharedConfigPtr config = KSharedConfig::openConfig("spectaclerc"); KConfigGroup guiConfig(config, "GuiConfig"); guiConfig.writeEntry("captureModeIndex", index); guiConfig.sync(); 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); } } void KSWidget::captureDelayChanged(qreal value) { - KSharedConfigPtr config = KSharedConfig::openConfig("kscreengenierc"); + KSharedConfigPtr config = KSharedConfig::openConfig("spectaclerc"); KConfigGroup guiConfig(config, "GuiConfig"); guiConfig.writeEntry("captureDelay", value); guiConfig.sync(); } diff --git a/src/Gui/KSWidget.h b/src/Gui/KSWidget.h index 9d6a3fd..d70a577 100644 --- a/src/Gui/KSWidget.h +++ b/src/Gui/KSWidget.h @@ -1,91 +1,91 @@ /* - * Copyright (C) 2015 Boudhayan Gupta + * 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 #include #include #include #include #include #include #include #include -#include #include #include #include #include #include #include #include "PlatformBackends/ImageGrabber.h" #include "KSImageWidget.h" +#include "SmartSpinBox.h" class KSWidget : public QWidget { Q_OBJECT public: explicit KSWidget(QWidget *parent = 0); 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 checkboxStatesChanged(int state); void onClickStateChanged(int state); void captureModeChanged(int index); void captureDelayChanged(qreal value); private: - QGridLayout *mMainLayout; - QHBoxLayout *mDelayLayout; - QVBoxLayout *mRightLayout; - QFormLayout *mCaptureModeForm; - QVBoxLayout *mContentOptionsForm; - KSImageWidget *mImageWidget; - QPushButton *mTakeScreenshotButton; - QComboBox *mCaptureArea; - QDoubleSpinBox *mDelayMsec; - QCheckBox *mCaptureOnClick; - QCheckBox *mMousePointer; - QCheckBox *mWindowDecorations; - QCheckBox *mCaptureTransientOnly; - QLabel *mCaptureModeLabel; - QLabel *mContentOptionsLabel; + 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; + QLabel *mCaptureModeLabel; + QLabel *mContentOptionsLabel; }; #endif // KSWIDGET_H diff --git a/src/Gui/ScreenClipper.cpp b/src/Gui/ScreenClipper.cpp new file mode 100644 index 0000000..1486a59 --- /dev/null +++ b/src/Gui/ScreenClipper.cpp @@ -0,0 +1,450 @@ +/* + * Copyright (C) 2007 Luca Gugelmann + * Copyright (C) 2015 Boudhayan Gupta + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License version 2 as + * published by the Free Software Foundation + * + * 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 Library 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 "ScreenClipper.h" + +ScreenClipper::ScreenClipper(const QPixmap &pixmap) : + QRasterWindow(0), + grabbing(false), + mSelection(QRect()), + mMouseOverHandle(0), + mPixmap(pixmap) +{ + mTLHandle = mTRHandle = mBLHandle = mBRHandle = QRect(0, 0, 15, 15); + mLHandle = mRHandle = QRect(0, 0, 10, 20); + mTHandle = mBHandle = QRect(0, 0, 20, 10); + mHandles = { &mTLHandle, &mTRHandle, &mBLHandle, &mBRHandle, &mLHandle, &mTHandle, &mRHandle, &mBHandle }; + + setFlags(Qt::X11BypassWindowManagerHint | Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::Tool); + QMetaObject::invokeMethod(this, "init", Qt::QueuedConnection); +} + +ScreenClipper::~ScreenClipper() +{} + +void ScreenClipper::init() +{ + setGeometry(0, 0, mPixmap.width(), mPixmap.height()); + setCursor(Qt::CrossCursor); + showFullScreen(); +} + +inline void ScreenClipper::drawTriangle(QPainter *painter, const QColor &color, const QPoint &a, const QPoint &b, const QPoint &c) +{ + painter->save(); + + QPainterPath path; + path.moveTo(a); + path.lineTo(b); + path.lineTo(c); + path.lineTo(a); + + painter->setPen(Qt::NoPen); + painter->fillPath(path, QBrush(color)); + + painter->restore(); +} + +void ScreenClipper::paintEvent(QPaintEvent *e) +{ + Q_UNUSED(e); + if (grabbing) { // grabWindow() should just get the background + return; + } + + // start by initialising the QPainter and drawing the image on it + + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing, true); + painter.drawPixmap(0, 0, mPixmap); + + // set up the colors and fonts + + QPalette palette(QToolTip::palette()); + + QColor textColor = palette.color(QPalette::Active, QPalette::Text); + QColor textBackgroundColor = palette.color(QPalette::Active, QPalette::Base); + QColor handleColor = palette.color(QPalette::Active, QPalette::Highlight); + QColor overlayColor(0, 0, 0); + + overlayColor.setAlphaF(0.5); + textBackgroundColor.setAlphaF(0.85); + + QFont font = QToolTip::font(); + font.setBold(true); + font.setPointSize(10); + painter.setFont(font); + + // if we don't have a selection yet, just draw a semitransparent + // black rectangle over the whole screen, and render the help text + + if (mSelection.isNull() || mSelection.isEmpty()) { + painter.setClipRegion(QRegion(geometry())); + painter.setPen(Qt::NoPen); + painter.setBrush(overlayColor); + painter.drawRect(geometry()); + + painter.setPen(textColor); + painter.setBrush(textBackgroundColor); + + QString helpText = i18n("Click anywhere on the screen (including on this text) to start drawing a selection rectangle, or press Esc to quit"); + QRect helpTextBoundingBox = painter.boundingRect(geometry(), Qt::TextWordWrap, helpText); + helpTextBoundingBox.moveCenter(geometry().center()); + QRect helpTextRect = helpTextBoundingBox.adjusted(-20, -20, 20, 20); + + painter.setPen(textColor); + painter.setBrush(textBackgroundColor); + painter.drawRoundedRect(helpTextRect, 10, 10); + painter.drawText(helpTextBoundingBox, helpText); + + return; + } + + // if we're here, this means we have a valid selection. let's draw + // the overlay first + + QRegion region = QRegion(geometry()).subtracted(mSelection); + painter.setClipRegion(region); + painter.setPen(Qt::NoPen); + painter.setBrush(overlayColor); + painter.drawRect(geometry()); + + // and the selection rectangle border + + region = QRegion(mSelection).subtracted(mSelection.adjusted(1, 1, -1, -1)); + painter.setBrush(handleColor); + painter.setClipRegion(region); + painter.drawRect(mSelection); + painter.setClipRect(geometry()); + + // draw the handles + + updateHandles(); + if ((mSelection.height() > 20) && (mSelection.width() > 20)) { + drawHandles(&painter, handleColor); + } + + // render the help text + + painter.setPen(textColor); + painter.setBrush(textBackgroundColor); + + QString helpText = i18n("To take the screenshot, double-click or press Enter. Right-click to reset the selection, or press Esc to quit."); + QRect helpTextBoundingBox = painter.boundingRect(geometry(), Qt::TextWordWrap, helpText); + helpTextBoundingBox.moveCenter(geometry().center()); + helpTextBoundingBox.moveTop(geometry().top()); + QRect helpTextRect = helpTextBoundingBox.adjusted(-5, 0, 5, 10); + helpTextBoundingBox.moveCenter(helpTextRect.center()); + + painter.setPen(Qt::NoPen); + painter.drawRect(helpTextRect); + + QPoint a = helpTextRect.topLeft(); + QPoint b = helpTextRect.bottomLeft() + QPoint(0, 1); + QPoint c = QPoint(helpTextRect.left() - helpTextRect.height(), helpTextRect.top()); + drawTriangle(&painter, textBackgroundColor, a, b, c); + + a = helpTextRect.topRight() + QPoint(1, 0); + b = helpTextRect.bottomRight() + QPoint(1, 1); + c = QPoint(helpTextRect.right() + helpTextRect.height(), helpTextRect.top()); + drawTriangle(&painter, textBackgroundColor, a, b, c); + + painter.setPen(textColor); + painter.drawText(helpTextBoundingBox, helpText); + +} + +void ScreenClipper::resizeEvent(QResizeEvent *e) +{ + Q_UNUSED(e); + + if (mSelection.isNull()) { + return; + } + + QRect r = mSelection; + r.setTopLeft(limitPointToRect(r.topLeft(), geometry())); + r.setBottomRight(limitPointToRect(r.bottomRight(), geometry())); + + if (r.width() <= 1 || r.height() <= 1) { // this just results in ugly drawing... + mSelection = QRect(); + } else { + mSelection = mSelection.normalized(); + } +} + +void ScreenClipper::mousePressEvent(QMouseEvent *e) +{ + if (e->button() == Qt::LeftButton) { + if (!mSelection.contains(e->pos())) { + mSelection = QRect(e->pos(), e->pos()); + mMouseOverHandle = &mBRHandle; + } else { + mMoveDelta = e->pos() - mSelection.topLeft(); + setCursor(Qt::ClosedHandCursor); + } + } else if (e->button() == Qt::RightButton) { + mSelection = QRect(); + setCursor(Qt::CrossCursor); + } + update(); +} + +void ScreenClipper::mouseMoveEvent(QMouseEvent *e) +{ + if (e->buttons() & Qt::LeftButton) { + if (mMouseOverHandle == nullptr) { // moving the whole selection + QRect r = geometry(); + r.setBottomRight(r.bottomRight() - QPoint(mSelection.width(), mSelection.height()) + QPoint(1, 1)); + if (!(r.isNull() || r.isEmpty()) && r.isValid()) { + const QPoint newTopLeft = limitPointToRect(e->pos() - mMoveDelta, r); + if (newTopLeft == mSelection.topLeft()) { + mMoveDelta = e->pos() - mSelection.topLeft(); + } else { + mSelection.moveTo(newTopLeft); + } + } + } else { // dragging a handle + QRect r = mSelection; + + if (mMouseOverHandle == &mTLHandle) { + if (e->pos().x() <= r.right() && e->pos().y() <= r.bottom()) { + r.setTopLeft(e->pos()); + } else if (e->pos().x() <= r.right() && e->pos().y() > r.bottom()) { + r.setLeft(e->pos().x()); + r.setTop(r.bottom()); + r.setBottom(e->pos().y()); + mMouseOverHandle = &mBLHandle; + } else if (e->pos().x() > r.right() && e->pos().y() <= r.bottom()) { + r.setTop(e->pos().y()); + r.setLeft(r.right()); + r.setRight(e->pos().x()); + mMouseOverHandle = &mTRHandle; + } else { + r.setTopLeft(r.bottomRight()); + r.setBottomRight(e->pos()); + mMouseOverHandle = &mBRHandle; + } + r = r.normalized(); + } else if (mMouseOverHandle == &mTRHandle) { + if (e->pos().x() >= r.left() && e->pos().y() <= r.bottom()) { + r.setTopRight(e->pos()); + } else if (e->pos().x() >= r.left() && e->pos().y() > r.bottom()) { + r.setRight(e->pos().x()); + r.setTop(r.bottom()); + r.setBottom(e->pos().y()); + mMouseOverHandle = &mBRHandle; + } else if (e->pos().x() < r.left() && e->pos().y() <= r.bottom()) { + r.setTop(e->pos().y()); + r.setRight(r.left()); + r.setLeft(e->pos().x()); + mMouseOverHandle = &mTLHandle; + } else { + r.setTopRight(r.bottomLeft()); + r.setBottomLeft(e->pos()); + mMouseOverHandle = &mBLHandle; + } + r = r.normalized(); + } else if (mMouseOverHandle == &mBLHandle) { + if (e->pos().x() <= r.right() && e->pos().y() >= r.top()) { + r.setBottomLeft(e->pos()); + } else if (e->pos().x() <= r.left() && e->pos().y() < r.top()) { + r.setLeft(e->pos().x()); + r.setBottom(r.top()); + r.setTop(e->pos().y()); + mMouseOverHandle = &mTLHandle; + } else if (e->pos().x() > r.left() && e->pos().y() >= r.top()) { + r.setBottom(e->pos().y()); + r.setLeft(r.right()); + r.setRight(e->pos().x()); + mMouseOverHandle = &mBRHandle; + } else { + r.setBottomLeft(r.topRight()); + r.setTopRight(e->pos()); + mMouseOverHandle = &mTRHandle; + } + r = r.normalized(); + } else if (mMouseOverHandle == &mBRHandle) { + if (e->pos().x() >= r.left() && e->pos().y() >= r.top()) { + r.setBottomRight(e->pos()); + } else if (e->pos().x() >= r.left() && e->pos().y() < r.top()) { + r.setRight(e->pos().x()); + r.setBottom(r.top()); + r.setTop(e->pos().y()); + mMouseOverHandle = &mTRHandle; + } else if (e->pos().x() < r.left() && e->pos().y() >= r.top()) { + r.setBottom(e->pos().y()); + r.setRight(r.left()); + r.setLeft(e->pos().x()); + mMouseOverHandle = &mBLHandle; + } else { + r.setBottomRight(r.topLeft()); + r.setTopLeft(e->pos()); + mMouseOverHandle = &mTLHandle; + } + r = r.normalized(); + } else if (mMouseOverHandle == &mTHandle) { + if (e->pos().y() <= r.bottom()) { + r.setTop(e->pos().y()); + } else { + r.setTop(r.bottom()); + r.setBottom(e->pos().y()); + mMouseOverHandle = &mBHandle; + } + r = r.normalized(); + } else if (mMouseOverHandle == &mRHandle) { + if (e->pos().x() >= r.left()) { + r.setRight(e->pos().x()); + } else { + r.setRight(r.left()); + r.setLeft(e->pos().x()); + mMouseOverHandle = &mLHandle; + } + r = r.normalized(); + } else if (mMouseOverHandle == &mLHandle) { + if (e->pos().x() <= r.right()) { + r.setLeft(e->pos().x()); + } else { + r.setLeft(r.right()); + r.setRight(e->pos().x()); + mMouseOverHandle = &mRHandle; + } + r = r.normalized(); + } else if (mMouseOverHandle == &mBHandle) { + if (e->pos().y() >= r.top()) { + r.setBottom(e->pos().y()); + } else { + r.setBottom(r.top()); + r.setTop(e->pos().y()); + mMouseOverHandle = &mTHandle; + } + r = r.normalized(); + } + + mSelection = r.normalized(); + } + + update(); + return; + } + + if (mSelection.isNull()) { + return; + } + + for (auto r: mHandles) { + if (r->contains(e->pos())) { + mMouseOverHandle = r; + + if (r == &mTLHandle || r == &mBRHandle) { + setCursor(Qt::SizeFDiagCursor); + } else if (r == &mTRHandle || r == &mBLHandle) { + setCursor(Qt::SizeBDiagCursor); + } else if (r == &mLHandle || r == &mRHandle) { + setCursor(Qt::SizeHorCursor); + } else if (r == &mTHandle || r == &mBHandle) { + setCursor(Qt::SizeVerCursor); + } + + return; + } + } + + mMouseOverHandle = nullptr; + + if (mSelection.contains(e->pos())) { + setCursor(Qt::OpenHandCursor); + } else { + setCursor(Qt::CrossCursor); + } +} + +void ScreenClipper::mouseReleaseEvent(QMouseEvent *e) +{ + if (mMouseOverHandle == nullptr && mSelection.contains(e->pos())) { + setCursor(Qt::OpenHandCursor); + } + update(); +} + +void ScreenClipper::mouseDoubleClickEvent(QMouseEvent *) +{ + return grabRect(); +} + +void ScreenClipper::keyPressEvent(QKeyEvent *e) +{ + if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) { + return grabRect(); + } + + if (e->key() == Qt::Key_Escape) { + emit regionCancelled(); + return; + } + + return e->ignore(); +} + +inline void ScreenClipper::grabRect() +{ + if (!mSelection.isNull() && mSelection.isValid()) { + grabbing = true; + emit regionGrabbed(mPixmap.copy(mSelection), mSelection); + return; + } + + emit regionCancelled(); +} + +void ScreenClipper::updateHandles() +{ + QRect r = mSelection; + + mTLHandle.moveTopLeft(r.topLeft()); + mTRHandle.moveTopRight(r.topRight()); + mBLHandle.moveBottomLeft(r.bottomLeft()); + mBRHandle.moveBottomRight(r.bottomRight()); + + mLHandle.moveTopLeft(QPoint(r.x(), r.y() + (r.height() / 2) - (mLHandle.height() / 2))); + mTHandle.moveTopLeft(QPoint(r.x() + (r.width() / 2) - (mTHandle.width() / 2), r.y())); + mRHandle.moveTopRight(QPoint(r.right(), r.y() + (r.height() / 2) - (mRHandle.height() / 2))); + mBHandle.moveBottomLeft(QPoint(r.x() + (r.width() / 2) - (mBHandle.width() / 2), r.bottom())); +} + +void ScreenClipper::drawHandles(QPainter *painter, const QColor &color) +{ + drawTriangle(painter, color, mTLHandle.topLeft(), mTLHandle.topRight(), mTLHandle.bottomLeft()); + drawTriangle(painter, color, mTRHandle.topRight(), mTRHandle.topLeft(), mTRHandle.bottomRight()); + drawTriangle(painter, color, mBLHandle.topLeft(), mBLHandle.bottomLeft(), mBLHandle.bottomRight()); + drawTriangle(painter, color, mBRHandle.topRight(), mBRHandle.bottomRight(), mBRHandle.bottomLeft()); + + drawTriangle(painter, color, mTHandle.topLeft(), mTHandle.topRight(), (mTHandle.bottomLeft() + mTHandle.bottomRight()) / 2); + drawTriangle(painter, color, mBHandle.bottomLeft(), mBHandle.bottomRight(), (mBHandle.topLeft() + mBHandle.topRight()) / 2); + drawTriangle(painter, color, mLHandle.topLeft(), mLHandle.bottomLeft(), (mLHandle.topRight() + mLHandle.bottomRight()) / 2); + drawTriangle(painter, color, mRHandle.topRight(), mRHandle.bottomRight(), (mRHandle.topLeft() + mRHandle.bottomLeft()) / 2); +} + +QPoint ScreenClipper::limitPointToRect(const QPoint &p, const QRect &r) const +{ + QPoint q; + q.setX(p.x() < r.x() ? r.x() : p.x() < r.right() ? p.x() : r.right()); + q.setY(p.y() < r.y() ? r.y() : p.y() < r.bottom() ? p.y() : r.bottom()); + return q; +} diff --git a/src/Gui/ScreenClipper.h b/src/Gui/ScreenClipper.h new file mode 100644 index 0000000..2cc00a1 --- /dev/null +++ b/src/Gui/ScreenClipper.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2007 Luca Gugelmann + * Copyright (C) 2015 Boudhayan Gupta + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License version 2 as + * published by the Free Software Foundation + * + * 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 Library 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 SCREENCLIPPER_H +#define SCREENCLIPPER_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +class ScreenClipper : public QRasterWindow +{ + Q_OBJECT + + public: + + ScreenClipper(const QPixmap &pixmap = QPixmap()); + ~ScreenClipper(); + + protected slots: + + void init(); + + signals: + + void regionGrabbed(const QPixmap &pixmap, const QRect ®ion); + void regionCancelled(); + + protected: + + void paintEvent(QPaintEvent *e) Q_DECL_OVERRIDE; + void resizeEvent(QResizeEvent *e) Q_DECL_OVERRIDE; + void mousePressEvent(QMouseEvent *e) Q_DECL_OVERRIDE; + void mouseMoveEvent(QMouseEvent *e) Q_DECL_OVERRIDE; + void mouseReleaseEvent(QMouseEvent *e) Q_DECL_OVERRIDE; + void mouseDoubleClickEvent(QMouseEvent *) Q_DECL_OVERRIDE; + void keyPressEvent(QKeyEvent *e) Q_DECL_OVERRIDE; + + private: + + void updateHandles(); + void drawHandles(QPainter *painter, const QColor &color); + void drawTriangle(QPainter *painter, const QColor &color, const QPoint &a, const QPoint &b, const QPoint &c); + void grabRect(); + QPoint limitPointToRect(const QPoint &p, const QRect &r) const; + + bool grabbing; + + // naming convention for handles + // T top, B bottom, R Right, L left + // 2 letters: a corner + // 1 letter: the handle on the middle of the corresponding side + QRect mTLHandle, mTRHandle, mBLHandle, mBRHandle; + QRect mLHandle, mTHandle, mRHandle, mBHandle; + QVector mHandles; + QRect mSelection; + QRect *mMouseOverHandle; + QPoint mMoveDelta; + + QPixmap mPixmap; +}; + +#endif + diff --git a/src/Gui/SmartSpinBox.cpp b/src/Gui/SmartSpinBox.cpp new file mode 100644 index 0000000..eb801aa --- /dev/null +++ b/src/Gui/SmartSpinBox.cpp @@ -0,0 +1,33 @@ +/* + * 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 +#include "SmartSpinBox.h" + +SmartSpinBox::SmartSpinBox(QWidget *parent) : + QDoubleSpinBox(parent) +{} + +QString SmartSpinBox::textFromValue(double val) const +{ + if ((qFloor(val) == val) && (qCeil(val) == val)) { + return QWidget::locale().toString(qint64(val)); + } + return QWidget::locale().toString(val, 'f', decimals()); +} diff --git a/src/Gui/KSSaveConfigDialog.h b/src/Gui/SmartSpinBox.h similarity index 50% copy from src/Gui/KSSaveConfigDialog.h copy to src/Gui/SmartSpinBox.h index edf39c9..b75e1cf 100644 --- a/src/Gui/KSSaveConfigDialog.h +++ b/src/Gui/SmartSpinBox.h @@ -1,59 +1,35 @@ /* - * Copyright (C) 2015 Boudhayan Gupta + * 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 KSSAVECONFIGDIALOG_H -#define KSSAVECONFIGDIALOG_H +#ifndef SMARTSPINBOX_H +#define SMARTSPINBOX_H -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include -#include -#include -#include -#include - - -class KSSaveConfigDialog : public QDialog +class SmartSpinBox : public QDoubleSpinBox { Q_OBJECT public: - explicit KSSaveConfigDialog(QWidget *parent = 0); - ~KSSaveConfigDialog(); - - public slots: - - void accept() Q_DECL_OVERRIDE; - - private: - - QDialogButtonBox *mDialogButtonBox; - KUrlRequester *mUrlRequester; - QLineEdit *mSaveNameFormat; + explicit SmartSpinBox(QWidget *parent = 0); + QString textFromValue(double val) const Q_DECL_OVERRIDE; }; -#endif // KSSAVECONFIGDIALOG_H +#endif // SMARTSPINBOX_H diff --git a/src/KipiInterface/KSGKipiImageCollectionShared.cpp b/src/KipiInterface/KSGKipiImageCollectionShared.cpp index fcc820b..8052c09 100644 --- a/src/KipiInterface/KSGKipiImageCollectionShared.cpp +++ b/src/KipiInterface/KSGKipiImageCollectionShared.cpp @@ -1,40 +1,40 @@ /* * Copyright (C) 2015 Boudhayan Gupta * Copyright (C) 2010 Pau Garcia i Quiles * Essentially a rip-off of code for Kamoso by: * Copyright (C) 2008-2009 by Aleix Pol * Copyright (C) 2008-2009 by Alex Fiestas * * 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 "KSGKipiImageCollectionShared.h" KSGKipiImageCollectionShared::KSGKipiImageCollectionShared() {} KSGKipiImageCollectionShared::~KSGKipiImageCollectionShared() {} -QString KSGKipiImageCollectionShared::name() { return "KScreenGenie"; } +QString KSGKipiImageCollectionShared::name() { return "Spectacle"; } QString KSGKipiImageCollectionShared::comment() { return QString(); } QString KSGKipiImageCollectionShared::uploadRootName() { return "/"; } QUrl KSGKipiImageCollectionShared::uploadRoot() { return QUrl(uploadRootName()); } bool KSGKipiImageCollectionShared::isDirectory() { return false; } QList KSGKipiImageCollectionShared::images() { QDir tempDir = QDir::temp(); QString tempFile = tempDir.absoluteFilePath("KSTempScreenshot.png"); return QList({ QUrl::fromLocalFile(tempFile) }); } diff --git a/src/KipiInterface/KSGKipiInfoShared.cpp b/src/KipiInterface/KSGKipiInfoShared.cpp index 81a80e3..c3fd1a9 100644 --- a/src/KipiInterface/KSGKipiInfoShared.cpp +++ b/src/KipiInterface/KSGKipiInfoShared.cpp @@ -1,39 +1,39 @@ /* * Copyright (C) 2010 Pau Garcia i Quiles * Essentially a rip-off of code for Kamoso by: * Copyright (C) 2008-2009 by Aleix Pol * Copyright (C) 2008-2009 by Alex Fiestas * * 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 "KSGKipiInfoShared.h" KSGKipiInfoShared::KSGKipiInfoShared(KIPI::Interface *interface, const QUrl &url) : KIPI::ImageInfoShared(interface, url) {} KSGKipiInfoShared::~KSGKipiInfoShared() {} // no-op functions void KSGKipiInfoShared::delAttributes(const QStringList &) {} void KSGKipiInfoShared::addAttributes(const QMap< QString, QVariant > &) {} void KSGKipiInfoShared::clearAttributes() {} QMap KSGKipiInfoShared::attributes() { return QMap(); } void KSGKipiInfoShared::setDescription(const QString &) {} -QString KSGKipiInfoShared::description() { return i18n("Taken with KScreenGenie"); } +QString KSGKipiInfoShared::description() { return i18n("Taken with Spectacle"); } diff --git a/src/Main.cpp b/src/Main.cpp index c668273..bde5686 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -1,141 +1,167 @@ /* - * Copyright (C) 2015 Boudhayan Gupta + * 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 #include #include #include #include +#include #include #include #include +#include -#include "KSCore.h" +#include "SpectacleCore.h" +#include "SpectacleDBusAdapter.h" #include "Config.h" int main(int argc, char **argv) { // set up the application QApplication app(argc, argv); app.setOrganizationDomain("kde.org"); - app.setApplicationName("kscreengenie"); - app.setWindowIcon(QIcon::fromTheme("ksnapshot")); + app.setApplicationName("spectacle"); + app.setWindowIcon(QIcon::fromTheme("spectacle")); app.setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true); app.setAttribute(Qt::AA_UseHighDpiPixmaps, true); // set up the about data - KLocalizedString::setApplicationDomain("kscreengenie"); - KAboutData aboutData("kscreengenie", - i18n("KScreenGenie"), - KSG_VERSION, + KLocalizedString::setApplicationDomain("spectacle"); + KAboutData aboutData("spectacle", + i18n("Spectacle"), + SPECTACLE_VERSION, i18n("KDE Screenshot Utility"), KAboutLicense::GPL_V2, i18n("(C) 2015 Boudhayan Gupta")); - aboutData.addAuthor("Boudhayan Gupta", QString(), "me@BaloneyGeek.com"); + aboutData.addAuthor("Boudhayan Gupta", QString(), "bgupta@kde.org"); KAboutData::setApplicationData(aboutData); // set up the command line options parser QCommandLineParser parser; parser.addVersionOption(); parser.addHelpOption(); aboutData.setupCommandLine(&parser); parser.addOptions({ {{"f", "fullscreen"}, i18n("Capture the entire desktop (default)")}, {{"m", "current"}, i18n("Capture the current monitor")}, {{"a", "activewindow"}, i18n("Capture the active window")}, {{"u", "windowundercursor"}, i18n("Capture the window currently under the cursor, including parents of pop-up menus")}, {{"t", "transientonly"}, i18n("Capture the window currently under the cursor, excluding parents of pop-up menus")}, {{"r", "region"}, i18n("Capture a rectangular region of the screen")}, + {{"g", "gui"}, i18n("Start in GUI mode (default)")}, {{"b", "background"}, i18n("Take a screenshot and exit without showing the GUI")}, - {{"n", "notify"}, i18n("In background mode, pop up a notification when the screenshot is taken")}, + {{"s", "dbus"}, i18n("Start in DBus-Activation mode")}, + {{"n", "nonotify"}, i18n("In background mode, do not pop up a notification when the screenshot is taken")}, {{"c", "clipboard"}, i18n("In background mode, send image to clipboard without saving to file")}, {{"o", "output"}, i18n("In background mode, save image to specified file"), "fileName"}, {{"d", "delay"}, i18n("In background mode, delay before taking the shot (in milliseconds)"), "delayMsec"}, {{"w", "onclick"}, i18n("Wait for a click before taking screenshot. Invalidates delay")} }); parser.process(app); aboutData.processCommandLine(&parser); // extract the capture mode ImageGrabber::GrabMode grabMode = ImageGrabber::FullScreen; if (parser.isSet("current")) { grabMode = ImageGrabber::CurrentScreen; } else if (parser.isSet("activewindow")) { grabMode = ImageGrabber::ActiveWindow; } else if (parser.isSet("region")) { grabMode = ImageGrabber::RectangularRegion; } else if (parser.isSet("windowundercursor")) { grabMode = ImageGrabber::TransientWithParent; } else if (parser.isSet("transientonly")) { grabMode = ImageGrabber::WindowUnderCursor; } - // are we running in background mode? + // are we running in background or dbus mode? - bool backgroundMode = false; + SpectacleCore::StartMode startMode = SpectacleCore::GuiMode; bool sendToClipboard = false; - bool notify = false; + bool notify = true; qint64 delayMsec = 0; QString fileName = QString(); if (parser.isSet("background")) { - backgroundMode = true; - app.setQuitOnLastWindowClosed(false); + startMode = SpectacleCore::BackgroundMode; + } else if (parser.isSet("dbus")) { + startMode = SpectacleCore::DBusMode; + } - if (parser.isSet("notify")) { - notify = true; + switch (startMode) { + case SpectacleCore::BackgroundMode: + if (parser.isSet("nonotify")) { + notify = false; } if (parser.isSet("output")) { fileName = parser.value("output"); } if (parser.isSet("delay")) { bool ok = false; qint64 delayValue = parser.value("delay").toLongLong(&ok); if (ok) { delayMsec = delayValue; } } if (parser.isSet("onclick")) { delayMsec = -1; } if (parser.isSet("clipboard")) { sendToClipboard = true; } + case SpectacleCore::DBusMode: + app.setQuitOnLastWindowClosed(false); + case SpectacleCore::GuiMode: + break; } // release the kraken - KSCore genie(backgroundMode, grabMode, fileName, delayMsec, sendToClipboard, notify); - QObject::connect(&genie, &KSCore::allDone, qApp, &QApplication::quit); + SpectacleCore core(startMode, grabMode, fileName, delayMsec, sendToClipboard, notify); + QObject::connect(&core, &SpectacleCore::allDone, qApp, &QApplication::quit); + + // create the dbus connections + + new KDBusService(KDBusService::Multiple, &core); + + SpectacleDBusAdapter *dbusAdapter = new SpectacleDBusAdapter(&core); + QObject::connect(&core, static_cast(&SpectacleCore::imageSaved), dbusAdapter, &SpectacleDBusAdapter::ScreenshotTaken); + QObject::connect(&core, &SpectacleCore::grabFailed, dbusAdapter, &SpectacleDBusAdapter::ScreenshotFailed); + + QDBusConnection::sessionBus().registerObject("/", &core); + QDBusConnection::sessionBus().registerService("org.freedesktop.Screenshot"); + + // fire it up return app.exec(); } diff --git a/src/PlatformBackends/DummyImageGrabber.cpp b/src/PlatformBackends/DummyImageGrabber.cpp index e960086..4ccea75 100644 --- a/src/PlatformBackends/DummyImageGrabber.cpp +++ b/src/PlatformBackends/DummyImageGrabber.cpp @@ -1,53 +1,72 @@ +/* + * 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 "DummyImageGrabber.h" DummyImageGrabber::DummyImageGrabber(QObject *parent): ImageGrabber(parent) {} DummyImageGrabber::~DummyImageGrabber() {} bool DummyImageGrabber::onClickGrabSupported() const { return false; } QPixmap DummyImageGrabber::blendCursorImage(const QPixmap &pixmap, int x, int y, int width, int height) { Q_UNUSED(pixmap); Q_UNUSED(x); Q_UNUSED(y); Q_UNUSED(width); Q_UNUSED(height); return QPixmap(); } void DummyImageGrabber::grabFullScreen() { emit pixmapChanged(QPixmap()); } void DummyImageGrabber::grabCurrentScreen() { emit pixmapChanged(QPixmap()); } void DummyImageGrabber::grabActiveWindow() { emit pixmapChanged(QPixmap()); } void DummyImageGrabber::grabRectangularRegion() { emit pixmapChanged(QPixmap()); } void DummyImageGrabber::grabWindowUnderCursor() { emit pixmapChanged(QPixmap()); } void DummyImageGrabber::grabTransientWithParent() { emit pixmapChanged(QPixmap()); } diff --git a/src/PlatformBackends/DummyImageGrabber.h b/src/PlatformBackends/DummyImageGrabber.h index 1076b40..73362df 100644 --- a/src/PlatformBackends/DummyImageGrabber.h +++ b/src/PlatformBackends/DummyImageGrabber.h @@ -1,31 +1,50 @@ +/* + * 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 DUMMYIMAGEGRABBER_H #define DUMMYIMAGEGRABBER_H #include #include #include "ImageGrabber.h" class DummyImageGrabber : public ImageGrabber { Q_OBJECT public: explicit DummyImageGrabber(QObject *parent = 0); ~DummyImageGrabber(); bool onClickGrabSupported() const Q_DECL_OVERRIDE; protected: QPixmap blendCursorImage(const QPixmap &pixmap, int x, int y, int width, int height) Q_DECL_OVERRIDE; void grabFullScreen() Q_DECL_OVERRIDE; void grabCurrentScreen() Q_DECL_OVERRIDE; void grabActiveWindow() Q_DECL_OVERRIDE; void grabRectangularRegion() Q_DECL_OVERRIDE; void grabWindowUnderCursor() Q_DECL_OVERRIDE; void grabTransientWithParent() Q_DECL_OVERRIDE; }; #endif // DUMMYIMAGEGRABBER_H diff --git a/src/PlatformBackends/ImageGrabber.cpp b/src/PlatformBackends/ImageGrabber.cpp index 50fd09e..db8fc35 100644 --- a/src/PlatformBackends/ImageGrabber.cpp +++ b/src/PlatformBackends/ImageGrabber.cpp @@ -1,106 +1,106 @@ /* - * Copyright (C) 2015 Boudhayan Gupta + * 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 "ImageGrabber.h" ImageGrabber::ImageGrabber(QObject *parent) : QObject(parent), mCapturePointer(false), mCaptureDecorations(true), mGrabMode(InvalidChoice), mPixmap(QPixmap()) { } ImageGrabber::~ImageGrabber() { } // bool ImageGrabber::onClickGrabSupported() const { return false; } // Q_PROPERTY Stuff QPixmap ImageGrabber::pixmap() const { return mPixmap; } bool ImageGrabber::capturePointer() const { return mCapturePointer; } bool ImageGrabber::captureDecorations() const { return mCaptureDecorations; } ImageGrabber::GrabMode ImageGrabber::grabMode() const { return mGrabMode; } void ImageGrabber::setCapturePointer(const bool newCapturePointer) { mCapturePointer = newCapturePointer; } void ImageGrabber::setCaptureDecorations(const bool newCaptureDecorations) { mCaptureDecorations = newCaptureDecorations; } void ImageGrabber::setGrabMode(const GrabMode newGrabMode) { mGrabMode = newGrabMode; } // Slots void ImageGrabber::doOnClickGrab() { return doImageGrab(); } void ImageGrabber::doImageGrab() { switch(mGrabMode) { case FullScreen: return grabFullScreen(); case CurrentScreen: return grabCurrentScreen(); case ActiveWindow: return grabActiveWindow(); case WindowUnderCursor: return grabWindowUnderCursor(); case TransientWithParent: return grabTransientWithParent(); case RectangularRegion: return grabRectangularRegion(); case InvalidChoice: default: emit imageGrabFailed(); return; } } diff --git a/src/PlatformBackends/ImageGrabber.h b/src/PlatformBackends/ImageGrabber.h index 679b039..0f92d74 100644 --- a/src/PlatformBackends/ImageGrabber.h +++ b/src/PlatformBackends/ImageGrabber.h @@ -1,96 +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 IMAGEGRABBER_H #define IMAGEGRABBER_H #include #include #include #include #include +<<<<<<< HEAD #include "Editor/KSImageEditor.h" +======= +#include "Gui/ScreenClipper.h" +>>>>>>> master class ImageGrabber : public QObject { Q_OBJECT Q_ENUMS(GrabMode) Q_PROPERTY(QPixmap pixmap READ pixmap NOTIFY pixmapChanged) Q_PROPERTY(bool capturePointer READ capturePointer WRITE setCapturePointer NOTIFY capturePointerChanged) Q_PROPERTY(bool captureDecorations READ captureDecorations WRITE setCaptureDecorations NOTIFY captureDecorationsChanged) Q_PROPERTY(GrabMode grabMode READ grabMode WRITE setGrabMode NOTIFY grabModeChanged) public: enum GrabMode { InvalidChoice = -1, FullScreen = 0, CurrentScreen = 1, ActiveWindow = 2, WindowUnderCursor = 3, TransientWithParent = 4, RectangularRegion = 5 }; explicit ImageGrabber(QObject *parent = 0); ~ImageGrabber(); QPixmap pixmap() const; bool capturePointer() const; bool captureDecorations() const; GrabMode grabMode() const; virtual bool onClickGrabSupported() const; void setCapturePointer(const bool newCapturePointer); void setCaptureDecorations(const bool newCaptureDecorations); void setGrabMode(const GrabMode newGrabMode); signals: void pixmapChanged(const QPixmap pixmap); void imageGrabFailed(); void capturePointerChanged(bool capturePointer); void captureDecorationsChanged(bool captureDecorations); void grabModeChanged(GrabMode grabMode); public slots: virtual void doImageGrab(); virtual void doOnClickGrab(); protected: virtual void grabFullScreen() = 0; virtual void grabCurrentScreen() = 0; virtual void grabActiveWindow() = 0; virtual void grabRectangularRegion() = 0; virtual void grabWindowUnderCursor() = 0; virtual void grabTransientWithParent() = 0; virtual QPixmap blendCursorImage(const QPixmap &pixmap, int x, int y, int width, int height) = 0; bool mCapturePointer; bool mCaptureDecorations; GrabMode mGrabMode; QPixmap mPixmap; }; #endif // IMAGEGRABBER_H diff --git a/src/PlatformBackends/X11ImageGrabber.cpp b/src/PlatformBackends/X11ImageGrabber.cpp index fbd6344..42dad19 100644 --- a/src/PlatformBackends/X11ImageGrabber.cpp +++ b/src/PlatformBackends/X11ImageGrabber.cpp @@ -1,683 +1,683 @@ /* - * Copyright (C) 2015 Boudhayan Gupta + * Copyright (C) 2015 Boudhayan Gupta * * Contains code from kxutils.cpp, part of KWindowSystem. Copyright * notices reproduced below: * * Copyright (C) 2008 Lubos Lunak (l.lunak@kde.org) * Copyright (C) 2013 Martin Gräßlin * * 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 "X11ImageGrabber.h" X11ImageGrabber::X11ImageGrabber(QObject *parent) : ImageGrabber(parent), mScreenConfigOperation(nullptr) { mNativeEventFilter = new OnClickEventFilter(this); } X11ImageGrabber::~X11ImageGrabber() { delete mNativeEventFilter; } // for onClick grab OnClickEventFilter::OnClickEventFilter(X11ImageGrabber *grabber) : QAbstractNativeEventFilter(), mImageGrabber(grabber) {} bool OnClickEventFilter::nativeEventFilter(const QByteArray &eventType, void *message, long *result) { Q_UNUSED(result); if (eventType == "xcb_generic_event_t") { xcb_generic_event_t *ev = static_cast(message); switch (ev->response_type & ~0x80) { case XCB_BUTTON_RELEASE: // uninstall the eventfilter and release the mouse qApp->removeNativeEventFilter(this); xcb_ungrab_pointer(QX11Info::connection(), XCB_TIME_CURRENT_TIME); // decide whether to grab or abort. regrab the mouse // on mouse-wheel events { xcb_button_release_event_t *ev2 = static_cast(message); qDebug() << ev2->detail; if (ev2->detail == 1) { QMetaObject::invokeMethod(mImageGrabber, "doImageGrab", Qt::QueuedConnection); } else if (ev2->detail < 4) { emit mImageGrabber->imageGrabFailed(); } else { QMetaObject::invokeMethod(mImageGrabber, "doOnClickGrab", Qt::QueuedConnection); } } return true; default: return false; } } return false; } bool X11ImageGrabber::onClickGrabSupported() const { return true; } void X11ImageGrabber::doOnClickGrab() { // get the cursor image xcb_cursor_t xcbCursor = XCB_CURSOR_NONE; xcb_cursor_context_t *xcbCursorCtx; xcb_screen_t *xcbAppScreen = xcb_aux_get_screen(QX11Info::connection(), QX11Info::appScreen()); if (xcb_cursor_context_new(QX11Info::connection(), xcbAppScreen, &xcbCursorCtx) >= 0) { QVector cursorNames = { QByteArrayLiteral("cross"), QByteArrayLiteral("crosshair"), QByteArrayLiteral("diamond-cross"), QByteArrayLiteral("cross-reverse") }; for (auto cursorName: cursorNames) { xcb_cursor_t cursor = xcb_cursor_load_cursor(xcbCursorCtx, cursorName.constData()); if (cursor != XCB_CURSOR_NONE) { xcbCursor = cursor; break; } } } // grab the cursor xcb_grab_pointer_cookie_t grabPointerCookie = xcb_grab_pointer_unchecked( QX11Info::connection(), // xcb connection 0, // deliver events to owner? nope QX11Info::appRootWindow(), // window to grab pointer for (root) XCB_EVENT_MASK_BUTTON_RELEASE, // which events do I want XCB_GRAB_MODE_SYNC, // pointer grab mode XCB_GRAB_MODE_ASYNC, // keyboard grab mode (why is this even here) XCB_NONE, // confine pointer to which window (none) xcbCursor, // cursor to change to for the duration of grab XCB_TIME_CURRENT_TIME // do this right now ); CScopedPointer grabPointerReply(xcb_grab_pointer_reply(QX11Info::connection(), grabPointerCookie, NULL)); // if the grab failed, take the screenshot right away if (grabPointerReply->status != XCB_GRAB_STATUS_SUCCESS) { return doImageGrab(); } // fix things if our pointer grab causes a lockup xcb_allow_events(QX11Info::connection(), XCB_ALLOW_SYNC_POINTER, XCB_TIME_CURRENT_TIME); // and install our event filter qApp->installNativeEventFilter(mNativeEventFilter); // done. clean stuff up xcb_cursor_context_free(xcbCursorCtx); xcb_free_cursor(QX11Info::connection(), xcbCursor); } // image conversion routine QPixmap X11ImageGrabber::convertFromNative(xcb_image_t *xcbImage) { QImage::Format format = QImage::Format_Invalid; switch (xcbImage->depth) { case 1: format = QImage::Format_MonoLSB; break; case 16: format = QImage::Format_RGB16; break; case 24: format = QImage::Format_RGB32; break; case 30: { // Qt doesn't have a matching image format. We need to convert manually quint32 *pixels = reinterpret_cast(xcbImage->data); for (uint i = 0; i < (xcbImage->size / 4); i++) { int r = (pixels[i] >> 22) & 0xff; int g = (pixels[i] >> 12) & 0xff; int b = (pixels[i] >> 2) & 0xff; pixels[i] = qRgba(r, g, b, 0xff); } // fall through, Qt format is still Format_ARGB32_Premultiplied } case 32: format = QImage::Format_ARGB32_Premultiplied; break; default: return QPixmap(); // we don't know } QImage image(xcbImage->data, xcbImage->width, xcbImage->height, format); if (image.isNull()) { return QPixmap(); } // work around an abort in QImage::color if (image.format() == QImage::Format_MonoLSB) { image.setColorCount(2); image.setColor(0, QColor(Qt::white).rgb()); image.setColor(1, QColor(Qt::black).rgb()); } // done return QPixmap::fromImage(image); } // utility functions QPixmap X11ImageGrabber::blendCursorImage(const QPixmap &pixmap, int x, int y, int width, int height) { // first we get the cursor position, compute the co-ordinates of the region // of the screen we're grabbing, and see if the cursor is actually visible in // the region QPoint cursorPos = QCursor::pos(); QRect screenRect(x, y, width, height); if (!(screenRect.contains(cursorPos))) { return pixmap; } // now we can get the image and start processing xcb_connection_t *xcbConn = QX11Info::connection(); xcb_xfixes_get_cursor_image_cookie_t cursorCookie = xcb_xfixes_get_cursor_image_unchecked(xcbConn); CScopedPointer cursorReply(xcb_xfixes_get_cursor_image_reply(xcbConn, cursorCookie, NULL)); if (cursorReply.isNull()) { return pixmap; } quint32 *pixelData = xcb_xfixes_get_cursor_image_cursor_image(cursorReply.data()); if (!pixelData) { return pixmap; } // process the image into a QImage QImage cursorImage = QImage((quint8 *)pixelData, cursorReply->width, cursorReply->height, QImage::Format_ARGB32_Premultiplied); // a small fix for the cursor position for fancier cursors cursorPos -= QPoint(cursorReply->xhot, cursorReply->yhot); // now we translate the cursor point to our screen rectangle cursorPos -= QPoint(x, y); // and do the painting QPixmap blendedPixmap = pixmap; QPainter painter(&blendedPixmap); painter.drawImage(cursorPos, cursorImage); // and done return blendedPixmap; } QPixmap X11ImageGrabber::getWindowPixmap(xcb_window_t window, bool blendPointer) { xcb_connection_t *xcbConn = QX11Info::connection(); // first get geometry information for our drawable xcb_get_geometry_cookie_t geomCookie = xcb_get_geometry_unchecked(xcbConn, window); CScopedPointer geomReply(xcb_get_geometry_reply(xcbConn, geomCookie, NULL)); // then proceed to get an image CScopedPointer xcbImage( xcb_image_get( xcbConn, window, geomReply->x, geomReply->y, geomReply->width, geomReply->height, ~0, XCB_IMAGE_FORMAT_Z_PIXMAP ) ); // if the image is null, this means we need to get the root image window // and run a crop if (xcbImage.isNull()) { return getWindowPixmap(QX11Info::appRootWindow(), blendPointer) .copy(geomReply->x, geomReply->y, geomReply->width, geomReply->height); } // now process the image QPixmap nativePixmap = convertFromNative(xcbImage.data()); if (!(blendPointer)) { return nativePixmap; } // now we blend in a pointer image xcb_get_geometry_cookie_t geomRootCookie = xcb_get_geometry_unchecked(xcbConn, geomReply->root); CScopedPointer geomRootReply(xcb_get_geometry_reply(xcbConn, geomRootCookie, NULL)); xcb_translate_coordinates_cookie_t translateCookie = xcb_translate_coordinates_unchecked( xcbConn, window, geomReply->root, geomRootReply->x, geomRootReply->y); CScopedPointer translateReply( xcb_translate_coordinates_reply(xcbConn, translateCookie, NULL)); return blendCursorImage(nativePixmap, translateReply->dst_x,translateReply->dst_y, geomReply->width, geomReply->height); } bool X11ImageGrabber::isKWinAvailable() { if (QDBusConnection::sessionBus().interface()->isServiceRegistered("org.kde.KWin")) { QDBusInterface interface("org.kde.KWin", "/Effects", "org.kde.kwin.Effects"); QDBusReply reply = interface.call("isEffectLoaded", "screenshot"); return reply.value(); } return false; } void X11ImageGrabber::KWinDBusScreenshotHelper(quint64 window) { mPixmap = getWindowPixmap((xcb_window_t)window, false); emit pixmapChanged(mPixmap); } void X11ImageGrabber::KScreenCurrentMonitorScreenshotHelper(KScreen::ConfigOperation *op) { KScreen::ConfigPtr config = qobject_cast(op)->config(); if (!config) { return grabFullScreen(); } if (!config->screen()) { return grabFullScreen(); } // we'll store the cursor position first QPoint cursorPosition = QCursor::pos(); // next, we'll get all our outputs and figure out which one has the cursor const KScreen::OutputList outputs = config->outputs(); for (auto output: outputs) { if (!(output->isConnected())) { continue; } if (!(output->currentMode())) { continue; } QPoint screenPosition = output->pos(); QSize screenSize = output->currentMode()->size(); QRect screenRect = QRect(screenPosition, screenSize); if (!(screenRect.contains(cursorPosition))) { continue; } // bingo, we've found an output that contains the cursor. Now // to take a shot mPixmap = getWindowPixmap(QX11Info::appRootWindow(), mCapturePointer); mPixmap = mPixmap.copy(screenPosition.x(), screenPosition.y(), screenSize.width(), screenSize.height()); emit pixmapChanged(mPixmap); mScreenConfigOperation->disconnect(); mScreenConfigOperation->deleteLater(); mScreenConfigOperation = nullptr; return; } mScreenConfigOperation->disconnect(); mScreenConfigOperation->deleteLater(); mScreenConfigOperation = nullptr; return grabFullScreen(); } void X11ImageGrabber::rectangleSelectionCancelled() { QObject *sender = QObject::sender(); sender->disconnect(); sender->deleteLater(); emit imageGrabFailed(); } void X11ImageGrabber::rectangleSelectionConfirmed(const QPixmap &pixmap, const QRect ®ion) { QObject *sender = QObject::sender(); sender->disconnect(); sender->deleteLater(); if (mCapturePointer) { mPixmap = blendCursorImage(pixmap, region.x(), region.y(), region.width(), region.height()); } else { mPixmap = pixmap; } emit pixmapChanged(mPixmap); } // grabber methods void X11ImageGrabber::grabFullScreen() { mPixmap = getWindowPixmap(QX11Info::appRootWindow(), mCapturePointer); emit pixmapChanged(mPixmap); } void X11ImageGrabber::grabTransientWithParent() { xcb_window_t curWin = getRealWindowUnderCursor(); // do we have a top-level or a transient window? KWindowInfo info(curWin, NET::WMName, NET::WM2TransientFor | NET::WM2WindowClass); if (!(info.valid(true) && (info.transientFor() != XCB_WINDOW_NONE)) || info.windowClassClass().isEmpty() || info.windowClassName().isEmpty()) { return grabWindowUnderCursor(); } // grab the image early mPixmap = getWindowPixmap(QX11Info::appRootWindow(), false); // now that we know we have a transient window, let's // see if the parent has any other transient windows who's // transient for the same app QRegion clipRegion; QStack childrenStack = findAllChildren(findParent(curWin)); while (!(childrenStack.isEmpty())) { xcb_window_t winId = childrenStack.pop(); KWindowInfo tempInfo(winId, 0, NET::WM2TransientFor); if (info.transientFor() == tempInfo.transientFor()) { clipRegion += getApplicationWindowGeometry(winId); } } // now we have a list of all the transient windows for the // parent, time to find the parent QList winList = KWindowSystem::stackingOrder(); for (int i = winList.size() - 1; i >= 0; i--) { KWindowInfo tempInfo(winList[i], NET::WMGeometry | NET::WMFrameExtents, NET::WM2WindowClass); QString winClass(tempInfo.windowClassClass()); QString winName(tempInfo.windowClassName()); if (winClass.contains(info.name(), Qt::CaseInsensitive) || winName.contains(info.name(), Qt::CaseInsensitive)) { if (mCaptureDecorations) { clipRegion += tempInfo.frameGeometry(); } else { clipRegion += tempInfo.geometry(); } break; } } // we can probably go ahead and generate the image now QImage tempImage(mPixmap.size(), QImage::Format_ARGB32); tempImage.fill(Qt::transparent); QPainter tempPainter(&tempImage); tempPainter.setClipRegion(clipRegion); tempPainter.drawPixmap(0, 0, mPixmap); tempPainter.end(); mPixmap = QPixmap::fromImage(tempImage).copy(clipRegion.boundingRect()); // why stop here, when we can render a 20px drop shadow all around it QGraphicsDropShadowEffect *effect = new QGraphicsDropShadowEffect; effect->setOffset(0); effect->setBlurRadius(20); QGraphicsPixmapItem *item = new QGraphicsPixmapItem; item->setPixmap(mPixmap); item->setGraphicsEffect(effect); QImage shadowImage(mPixmap.size() + QSize(40, 40), QImage::Format_ARGB32); shadowImage.fill(Qt::transparent); QPainter shadowPainter(&shadowImage); QGraphicsScene scene; scene.addItem(item); scene.render(&shadowPainter, QRectF(), QRectF(-20, -20, mPixmap.width() + 40, mPixmap.height() + 40)); shadowPainter.end(); // we can finish up now mPixmap = QPixmap::fromImage(shadowImage); if (mCapturePointer) { QPoint topLeft = clipRegion.boundingRect().topLeft() - QPoint(20, 20); mPixmap = blendCursorImage(mPixmap, topLeft.x(), topLeft.y(), mPixmap.width(), mPixmap.height()); } emit pixmapChanged(mPixmap); } void X11ImageGrabber::grabActiveWindow() { xcb_window_t activeWindow = KWindowSystem::activeWindow(); // if KWin is available, use the KWin DBus interfaces if (mCaptureDecorations && isKWinAvailable()) { QDBusConnection bus = QDBusConnection::sessionBus(); bus.connect("org.kde.KWin", "/Screenshot", "org.kde.kwin.Screenshot", "screenshotCreated", this, SLOT(KWinDBusScreenshotHelper(quint64))); QDBusInterface interface("org.kde.KWin", "/Screenshot", "org.kde.kwin.Screenshot"); int mask = 1; if (mCapturePointer) { mask |= 1 << 1; } interface.call("screenshotForWindow", (quint64)activeWindow, mask); return; } // otherwise, use the native functionality return grabApplicationWindowHelper(activeWindow); } void X11ImageGrabber::grabWindowUnderCursor() { // if KWin is available, use the KWin DBus interfaces if (mCaptureDecorations && isKWinAvailable()) { QDBusConnection bus = QDBusConnection::sessionBus(); bus.connect("org.kde.KWin", "/Screenshot", "org.kde.kwin.Screenshot", "screenshotCreated", this, SLOT(KWinDBusScreenshotHelper(quint64))); QDBusInterface interface("org.kde.KWin", "/Screenshot", "org.kde.kwin.Screenshot"); int mask = 1; if (mCapturePointer) { mask |= 1 << 1; } interface.call("screenshotWindowUnderCursor", mask); return; } // else, go native return grabApplicationWindowHelper(getRealWindowUnderCursor()); } void X11ImageGrabber::grabApplicationWindowHelper(xcb_window_t window) { // if the user doesn't want decorations captured, we're in luck. This is // the easiest bit mPixmap = getWindowPixmap(window, mCapturePointer); if (!mCaptureDecorations || window == QX11Info::appRootWindow()) { emit pixmapChanged(mPixmap); return; } // if the user wants the window decorations, things get a little tricky. // we can't simply get a handle to the window manager frame window and // just grab it, because some compositing window managers (yes, kwin // included) do not render the window onto the frame but keep it in a // separate opengl buffer, so grabbing this window is going to simply // give us a transparent image with the frame and titlebar. // all is not lost. what we need to do is grab the image of the entire // desktop, find the geometry of the window including its frame, and // crop the root image accordingly. KWindowInfo info(window, NET::WMFrameExtents); if (info.valid()) { QRect frameGeom = info.frameGeometry(); mPixmap = getWindowPixmap(QX11Info::appRootWindow(), mCapturePointer).copy(frameGeom); } // fallback is window without the frame emit pixmapChanged(mPixmap); } QRect X11ImageGrabber::getApplicationWindowGeometry(xcb_window_t window) { xcb_connection_t *xcbConn = QX11Info::connection(); xcb_get_geometry_cookie_t geomCookie = xcb_get_geometry_unchecked(xcbConn, window); CScopedPointer geomReply(xcb_get_geometry_reply(xcbConn, geomCookie, NULL)); return QRect(geomReply->x, geomReply->y, geomReply->width, geomReply->height); } void X11ImageGrabber::grabCurrentScreen() { mScreenConfigOperation = new KScreen::GetConfigOperation; connect(mScreenConfigOperation, &KScreen::GetConfigOperation::finished, this, &X11ImageGrabber::KScreenCurrentMonitorScreenshotHelper); } void X11ImageGrabber::grabRectangularRegion() { KSImageEditor *editor = new KSImageEditor(getWindowPixmap(QX11Info::appRootWindow(), false)); connect(editor, &KSImageEditor::grabAccepted, this, &X11ImageGrabber::rectangleSelectionConfirmed); connect(editor, &KSImageEditor::grabCanceled, this, &X11ImageGrabber::rectangleSelectionCancelled); } xcb_window_t X11ImageGrabber::getRealWindowUnderCursor() { xcb_connection_t *xcbConn = QX11Info::connection(); xcb_window_t curWin = QX11Info::appRootWindow(); const QByteArray atomName("WM_STATE"); xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked(xcbConn, 0, atomName.length(), atomName.constData()); xcb_query_pointer_cookie_t pointerCookie = xcb_query_pointer_unchecked(xcbConn, curWin); CScopedPointer atomReply(xcb_intern_atom_reply(xcbConn, atomCookie, NULL)); CScopedPointer pointerReply(xcb_query_pointer_reply(xcbConn, pointerCookie, NULL)); if (atomReply->atom == XCB_ATOM_NONE) { return QX11Info::appRootWindow(); } // now start testing QStack windowStack; windowStack.push(pointerReply->child); while (!windowStack.isEmpty()) { curWin = windowStack.pop(); // next, check if our window has the WM_STATE peoperty set on // the window. if yes, return the window - we have found it xcb_get_property_cookie_t propertyCookie = xcb_get_property_unchecked(xcbConn, 0, curWin, atomReply->atom, XCB_ATOM_ANY, 0, 0); CScopedPointer propertyReply(xcb_get_property_reply(xcbConn, propertyCookie, NULL)); if (propertyReply->type != XCB_ATOM_NONE) { return curWin; } // if we're here, this means the window is not the real window // we should start looking at its children xcb_query_tree_cookie_t treeCookie = xcb_query_tree_unchecked(xcbConn, curWin); CScopedPointer treeReply(xcb_query_tree_reply(xcbConn, treeCookie, NULL)); xcb_window_t *winChildren = xcb_query_tree_children(treeReply.data()); int winChildrenLength = xcb_query_tree_children_length(treeReply.data()); for (int i = winChildrenLength - 1; i >= 0; i--) { windowStack.push(winChildren[i]); } } // return the window. it has geometry information for a crop return pointerReply->child; } QStack X11ImageGrabber::findAllChildren(xcb_window_t window) { QStack winStack; xcb_connection_t *xcbConn = QX11Info::connection(); xcb_query_tree_cookie_t treeCookie = xcb_query_tree_unchecked(xcbConn, window); CScopedPointer treeReply(xcb_query_tree_reply(xcbConn, treeCookie, NULL)); xcb_window_t *winChildren = xcb_query_tree_children(treeReply.data()); int winChildrenLength = xcb_query_tree_children_length(treeReply.data()); for (int i = winChildrenLength - 1; i >= 0; i--) { winStack.push(winChildren[i]); } return winStack; } xcb_window_t X11ImageGrabber::findParent(xcb_window_t window) { xcb_connection_t *xcbConn = QX11Info::connection(); xcb_query_tree_cookie_t treeCookie = xcb_query_tree_unchecked(xcbConn, window); CScopedPointer treeReply(xcb_query_tree_reply(xcbConn, treeCookie, NULL)); return treeReply->parent; } diff --git a/src/PlatformBackends/X11ImageGrabber.h b/src/PlatformBackends/X11ImageGrabber.h index 9d0dc70..a73112b 100644 --- a/src/PlatformBackends/X11ImageGrabber.h +++ b/src/PlatformBackends/X11ImageGrabber.h @@ -1,125 +1,125 @@ /* - * Copyright (C) 2015 Boudhayan Gupta + * 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 X11IMAGEGRABBER_H #define X11IMAGEGRABBER_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ImageGrabber.h" class X11ImageGrabber; class OnClickEventFilter : public QAbstractNativeEventFilter { public: explicit OnClickEventFilter(X11ImageGrabber *grabber); bool nativeEventFilter(const QByteArray &eventType, void *message, long *result); private: X11ImageGrabber *mImageGrabber; }; class X11ImageGrabber : public ImageGrabber { Q_OBJECT public: explicit X11ImageGrabber(QObject * parent = 0); ~X11ImageGrabber(); bool onClickGrabSupported() const Q_DECL_OVERRIDE; protected: void grabFullScreen() Q_DECL_OVERRIDE; void grabCurrentScreen() Q_DECL_OVERRIDE; void grabActiveWindow() Q_DECL_OVERRIDE; void grabRectangularRegion() Q_DECL_OVERRIDE; void grabWindowUnderCursor() Q_DECL_OVERRIDE; void grabTransientWithParent() Q_DECL_OVERRIDE; QPixmap blendCursorImage(const QPixmap &pixmap, int x, int y, int width, int height) Q_DECL_OVERRIDE; private slots: void KWinDBusScreenshotHelper(quint64 window); void KScreenCurrentMonitorScreenshotHelper(KScreen::ConfigOperation *op); void rectangleSelectionConfirmed(const QPixmap &pixmap, const QRect ®ion); void rectangleSelectionCancelled(); public slots: void doOnClickGrab(); private: bool isKWinAvailable(); xcb_window_t getRealWindowUnderCursor(); void grabApplicationWindowHelper(xcb_window_t window); QRect getApplicationWindowGeometry(xcb_window_t window); QStack findAllChildren(xcb_window_t window); xcb_window_t findParent(xcb_window_t window); QPixmap getWindowPixmap(xcb_window_t window, bool blendPointer); QPixmap convertFromNative(xcb_image_t *xcbImage); OnClickEventFilter *mNativeEventFilter; KScreen::GetConfigOperation *mScreenConfigOperation; }; template using CScopedPointer = QScopedPointer; #endif // X11IMAGEGRABBER_H diff --git a/src/KSCore.cpp b/src/SpectacleCore.cpp similarity index 70% rename from src/KSCore.cpp rename to src/SpectacleCore.cpp index 5c63857..3a603d9 100644 --- a/src/KSCore.cpp +++ b/src/SpectacleCore.cpp @@ -1,562 +1,597 @@ /* - * Copyright (C) 2015 Boudhayan Gupta + * Copyright (C) 2015 Boudhayan Gupta * * Includes code from ksnapshot.cpp, part of KSnapshot. Copyright notices * reproduced below: * * Copyright (C) 1997-2008 Richard J. Moore * Copyright (C) 2000 Matthias Ettrich * Copyright (C) 2002 Aaron J. Seigo * Copyright (C) 2003 Nadeem Hasan * Copyright (C) 2004 Bernd Brandstetter * Copyright (C) 2006 Urs Wolfer * Copyright (C) 2010 Martin Gräßlin * Copyright (C) 2010, 2011 Pau Garcia i Quiles * * 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 "KSCore.h" +#include "SpectacleCore.h" -KSCore::KSCore(bool backgroundMode, ImageGrabber::GrabMode grabMode, QString &saveFileName, qint64 delayMsec, bool sendToClipboard, bool notifyOnGrab, QObject *parent) : +SpectacleCore::SpectacleCore(StartMode startMode, ImageGrabber::GrabMode grabMode, QString &saveFileName, + qint64 delayMsec, bool sendToClipboard, bool notifyOnGrab, QObject *parent) : QObject(parent), - mBackgroundMode(backgroundMode), + mStartMode(startMode), mNotify(notifyOnGrab), mOverwriteOnSave(true), mBackgroundSendToClipboard(sendToClipboard), mLocalPixmap(QPixmap()), mImageGrabber(nullptr), - mMainWindow(nullptr) + mMainWindow(nullptr), + isGuiInited(false) { - KSharedConfigPtr config = KSharedConfig::openConfig("kscreengenierc"); + KSharedConfigPtr config = KSharedConfig::openConfig("spectaclerc"); KConfigGroup guiConfig(config, "GuiConfig"); if (!(saveFileName.isEmpty() || saveFileName.isNull())) { if (QDir::isRelativePath(saveFileName)) { saveFileName = QDir::current().absoluteFilePath(saveFileName); } setFilename(saveFileName); } #ifdef XCB_FOUND if (qApp->platformName() == QStringLiteral("xcb")) { mImageGrabber = new X11ImageGrabber; } #endif if (!mImageGrabber) { mImageGrabber = new DummyImageGrabber; } mImageGrabber->setGrabMode(grabMode); mImageGrabber->setCapturePointer(guiConfig.readEntry("includePointer", true)); mImageGrabber->setCaptureDecorations(guiConfig.readEntry("includeDecorations", true)); if ((!(mImageGrabber->onClickGrabSupported())) && (delayMsec < 0)) { delayMsec = 0; } - connect(this, &KSCore::errorMessage, this, &KSCore::showErrorMessage); - connect(mImageGrabber, &ImageGrabber::pixmapChanged, this, &KSCore::screenshotUpdated); - connect(mImageGrabber, &ImageGrabber::imageGrabFailed, this, &KSCore::screenshotFailed); + connect(this, &SpectacleCore::errorMessage, this, &SpectacleCore::showErrorMessage); + connect(mImageGrabber, &ImageGrabber::pixmapChanged, this, &SpectacleCore::screenshotUpdated); + connect(mImageGrabber, &ImageGrabber::imageGrabFailed, this, &SpectacleCore::screenshotFailed); - if (backgroundMode) { - int msec = (KWindowSystem::compositingActive() ? 200 : 50) + delayMsec; - QTimer::singleShot(msec, mImageGrabber, &ImageGrabber::doImageGrab); - return; + switch (startMode) { + case DBusMode: + break; + case BackgroundMode: { + int msec = (KWindowSystem::compositingActive() ? 200 : 50) + delayMsec; + QTimer::singleShot(msec, mImageGrabber, &ImageGrabber::doImageGrab); + } + break; + case GuiMode: + initGui(); + break; } - - // if we aren't in background mode, this would be a good time to - // init the gui - - mMainWindow = new KSMainWindow(mImageGrabber->onClickGrabSupported()); - - connect(mMainWindow, &KSMainWindow::newScreenshotRequest, this, &KSCore::takeNewScreenshot); - connect(mMainWindow, &KSMainWindow::save, this, &KSCore::doGuiSave); - connect(mMainWindow, &KSMainWindow::saveAndExit, this, &KSCore::doAutoSave); - connect(mMainWindow, &KSMainWindow::saveAsClicked, this, &KSCore::doGuiSaveAs); - connect(mMainWindow, &KSMainWindow::sendToKServiceRequest, this, &KSCore::doSendToService); - connect(mMainWindow, &KSMainWindow::sendToOpenWithRequest, this, &KSCore::doSendToOpenWith); - connect(mMainWindow, &KSMainWindow::sendToClipboardRequest, this, &KSCore::doSendToClipboard); - connect(mMainWindow, &KSMainWindow::dragAndDropRequest, this, &KSCore::doStartDragAndDrop); - connect(mMainWindow, &KSMainWindow::printRequest, this, &KSCore::doPrint); - - connect(this, &KSCore::imageSaved, mMainWindow, &KSMainWindow::setScreenshotWindowTitle); - QMetaObject::invokeMethod(mImageGrabber, "doImageGrab", Qt::QueuedConnection); } -KSCore::~KSCore() +SpectacleCore::~SpectacleCore() { if (mMainWindow) { delete mMainWindow; } } // Q_PROPERTY stuff -QString KSCore::filename() const +QString SpectacleCore::filename() const { return mFileNameString; } -void KSCore::setFilename(const QString &filename) +void SpectacleCore::setFilename(const QString &filename) { mFileNameString = filename; mFileNameUrl = QUrl::fromUserInput(filename); } -ImageGrabber::GrabMode KSCore::grabMode() const +ImageGrabber::GrabMode SpectacleCore::grabMode() const { return mImageGrabber->grabMode(); } -void KSCore::setGrabMode(const ImageGrabber::GrabMode &grabMode) +void SpectacleCore::setGrabMode(const ImageGrabber::GrabMode &grabMode) { mImageGrabber->setGrabMode(grabMode); } -bool KSCore::overwriteOnSave() const +bool SpectacleCore::overwriteOnSave() const { return mOverwriteOnSave; } -void KSCore::setOverwriteOnSave(const bool &overwrite) +void SpectacleCore::setOverwriteOnSave(const bool &overwrite) { mOverwriteOnSave = overwrite; } -QString KSCore::saveLocation() const +QString SpectacleCore::saveLocation() const { - KSharedConfigPtr config = KSharedConfig::openConfig("kscreengenierc"); + KSharedConfigPtr config = KSharedConfig::openConfig("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("."); generalConfig.writePathEntry("last-saved-to", savePath); } return savePath; } -void KSCore::setSaveLocation(const QString &savePath) +void SpectacleCore::setSaveLocation(const QString &savePath) { - KSharedConfigPtr config = KSharedConfig::openConfig("kscreengenierc"); + KSharedConfigPtr config = KSharedConfig::openConfig("spectaclerc"); KConfigGroup generalConfig = KConfigGroup(config, "General"); generalConfig.writePathEntry("last-saved-to", savePath); } // Slots -void KSCore::takeNewScreenshot(const ImageGrabber::GrabMode &mode, const int &timeout, const bool &includePointer, const bool &includeDecorations) +void SpectacleCore::dbusStartAgent() +{ + if (!(mStartMode == GuiMode)) { + mStartMode = GuiMode; + return initGui(); + } +} + +void SpectacleCore::takeNewScreenshot(const ImageGrabber::GrabMode &mode, + const int &timeout, const bool &includePointer, const bool &includeDecorations) { mImageGrabber->setGrabMode(mode); mImageGrabber->setCapturePointer(includePointer); mImageGrabber->setCaptureDecorations(includeDecorations); if (timeout < 0) { mImageGrabber->doOnClickGrab(); 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. const int msec = KWindowSystem::compositingActive() ? 200 : 50; QTimer::singleShot(timeout + msec, mImageGrabber, &ImageGrabber::doImageGrab); } -void KSCore::showErrorMessage(const QString &errString) +void SpectacleCore::showErrorMessage(const QString &errString) { qDebug() << "ERROR: " << errString; - if (!mBackgroundMode) { + if (mStartMode == GuiMode) { KMessageBox::error(0, errString); } } -void KSCore::screenshotUpdated(const QPixmap &pixmap) +void SpectacleCore::screenshotUpdated(const QPixmap &pixmap) { mLocalPixmap = pixmap; - if (mBackgroundMode) { + switch (mStartMode) { + case BackgroundMode: if (mBackgroundSendToClipboard) { qApp->clipboard()->setPixmap(pixmap); qDebug() << i18n("Copied image to clipboard"); - } else { - doAutoSave(); } - } else { + case DBusMode: + doAutoSave(); + break; + case GuiMode: mMainWindow->setScreenshotAndShow(pixmap); tempFileSave(); } } -void KSCore::screenshotFailed() +void SpectacleCore::screenshotFailed() { - if (mBackgroundMode) { - qDebug() << i18n("Screenshot capture canceled or failed"); + switch (mStartMode) { + case BackgroundMode: + showErrorMessage(i18n("Screenshot capture canceled or failed")); + case DBusMode: + emit grabFailed(); emit allDone(); return; + case GuiMode: + mMainWindow->show(); } - - mMainWindow->show(); } -void KSCore::doGuiSave() +void SpectacleCore::doGuiSave() { if (mLocalPixmap.isNull()) { emit errorMessage(i18n("Cannot save an empty screenshot image.")); return; } QUrl savePath = getAutosaveFilename(); if (doSave(savePath)) { emit imageSaved(savePath); + emit imageSaved(savePath.toLocalFile()); } } -void KSCore::doAutoSave() +void SpectacleCore::doAutoSave() { if (mLocalPixmap.isNull()) { emit errorMessage(i18n("Cannot save an empty screenshot image.")); return; } QUrl savePath; - if (mBackgroundMode && mFileNameUrl.isValid() && mFileNameUrl.isLocalFile()) { + if (mStartMode == BackgroundMode && mFileNameUrl.isValid() && mFileNameUrl.isLocalFile()) { savePath = mFileNameUrl; } else { savePath = getAutosaveFilename(); } if (doSave(savePath)) { QDir dir(savePath.path()); dir.cdUp(); setSaveLocation(dir.absolutePath()); - if (mBackgroundMode && mNotify) { + if ((mStartMode == BackgroundMode || mStartMode == DBusMode) && mNotify) { KNotification *notify = new KNotification("newScreenshotSaved"); notify->setText(i18n("A new screenshot was captured and saved to %1", savePath.toLocalFile())); - notify->setPixmap(QIcon::fromTheme("ksnapshot").pixmap(QSize(32, 32))); + notify->setPixmap(QIcon::fromTheme("spectacle").pixmap(QSize(32, 32))); notify->sendEvent(); // unfortunately we can't quit just yet, emitting allDone right away // quits the application before the notification DBus message gets sent. // a token timeout seems to fix this though. Any better ideas? - QTimer::singleShot(50, this, &KSCore::allDone); + QTimer::singleShot(50, this, &SpectacleCore::allDone); } else { emit allDone(); } return; } } -void KSCore::doStartDragAndDrop() +void SpectacleCore::doStartDragAndDrop() { QMimeData *mimeData = new QMimeData; mimeData->setUrls(QList { getTempSaveFilename() }); mimeData->setImageData(mLocalPixmap); mimeData->setData("application/x-kde-suggestedfilename", QFile::encodeName(makeAutosaveFilename() + ".png")); QDrag *dragHandler = new QDrag(this); dragHandler->setMimeData(mimeData); dragHandler->setPixmap(mLocalPixmap.scaled(256, 256, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation)); dragHandler->start(); dragHandler->deleteLater(); } -void KSCore::doPrint(QPrinter *printer) +void SpectacleCore::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 = mLocalPixmap.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; } -void KSCore::doGuiSaveAs() +void SpectacleCore::doGuiSaveAs() { QString selectedFilter; QStringList supportedFilters; QMimeDatabase db; const QUrl autoSavePath = getAutosaveFilename(); const QMimeType mimeTypeForFilename = db.mimeTypeForUrl(autoSavePath); for (auto mimeTypeName: QImageWriter::supportedMimeTypes()) { QMimeType mimetype = db.mimeTypeForName(mimeTypeName); if (mimetype.preferredSuffix() != "") { QString filterString = mimetype.comment() + " (*." + mimetype.preferredSuffix() + ")"; qDebug() << filterString; supportedFilters.append(filterString); if (mimetype == mimeTypeForFilename) { selectedFilter = supportedFilters.last(); } } } QFileDialog dialog(mMainWindow); dialog.setAcceptMode(QFileDialog::AcceptSave); dialog.setFileMode(QFileDialog::AnyFile); dialog.setNameFilters(supportedFilters); dialog.selectNameFilter(selectedFilter); dialog.setDirectoryUrl(autoSavePath); if (dialog.exec() == QFileDialog::Accepted) { const QUrl saveUrl = dialog.selectedUrls().first(); if (saveUrl.isValid()) { if (doSave(saveUrl)) { emit imageSaved(saveUrl); + emit imageSaved(saveUrl.toLocalFile()); } } } } -void KSCore::doSendToService(KService::Ptr service) +void SpectacleCore::doSendToService(KService::Ptr service) { QUrl tempFile; QList tempFileList; tempFile = getTempSaveFilename(); if (!tempFile.isValid()) { emit errorMessage(i18n("Cannot send screenshot to the application")); return; } tempFileList.append(tempFile); KRun::runService(*service, tempFileList, mMainWindow, true); } -void KSCore::doSendToOpenWith() +void SpectacleCore::doSendToOpenWith() { QUrl tempFile; QList tempFileList; tempFile = getTempSaveFilename(); if (!tempFile.isValid()) { emit errorMessage(i18n("Cannot send screenshot to the application")); return; } tempFileList.append(tempFile); KRun::displayOpenWithDialog(tempFileList, mMainWindow, true); } -void KSCore::doSendToClipboard() +void SpectacleCore::doSendToClipboard() { QApplication::clipboard()->setPixmap(mLocalPixmap); } // Private -QUrl KSCore::getAutosaveFilename() +void SpectacleCore::initGui() +{ + if (!isGuiInited) { + mMainWindow = new KSMainWindow(mImageGrabber->onClickGrabSupported()); + + connect(mMainWindow, &KSMainWindow::newScreenshotRequest, this, &SpectacleCore::takeNewScreenshot); + connect(mMainWindow, &KSMainWindow::save, this, &SpectacleCore::doGuiSave); + connect(mMainWindow, &KSMainWindow::saveAndExit, this, &SpectacleCore::doAutoSave); + connect(mMainWindow, &KSMainWindow::saveAsClicked, this, &SpectacleCore::doGuiSaveAs); + connect(mMainWindow, &KSMainWindow::sendToKServiceRequest, this, &SpectacleCore::doSendToService); + connect(mMainWindow, &KSMainWindow::sendToOpenWithRequest, this, &SpectacleCore::doSendToOpenWith); + connect(mMainWindow, &KSMainWindow::sendToClipboardRequest, this, &SpectacleCore::doSendToClipboard); + connect(mMainWindow, &KSMainWindow::dragAndDropRequest, this, &SpectacleCore::doStartDragAndDrop); + connect(mMainWindow, &KSMainWindow::printRequest, this, &SpectacleCore::doPrint); + + connect(this, static_cast(&SpectacleCore::imageSaved), + mMainWindow, &KSMainWindow::setScreenshotWindowTitle); + + isGuiInited = true; + QMetaObject::invokeMethod(mImageGrabber, "doImageGrab", Qt::QueuedConnection); + } +} + +QUrl SpectacleCore::getAutosaveFilename() { const QString baseDir = saveLocation(); const QDir baseDirPath(baseDir); const QString filename = makeAutosaveFilename(); const QString fullpath = autoIncrementFilename(baseDirPath.filePath(filename), "png"); const QUrl fileNameUrl = QUrl::fromUserInput(fullpath); if (fileNameUrl.isValid()) { return fileNameUrl; } else { return QUrl(); } } -QString KSCore::makeAutosaveFilename() +QString SpectacleCore::makeAutosaveFilename() { - KSharedConfigPtr config = KSharedConfig::openConfig("kscreengenierc"); + KSharedConfigPtr config = KSharedConfig::openConfig("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("%Y", timestamp.toString("yyyy")) .replace("%y", timestamp.toString("yy")) .replace("%M", timestamp.toString("MM")) .replace("%D", timestamp.toString("dd")) .replace("%H", timestamp.toString("hh")) .replace("%m", timestamp.toString("mm")) .replace("%S", timestamp.toString("ss")); } -QString KSCore::autoIncrementFilename(const QString &baseName, const QString &extension) +QString SpectacleCore::autoIncrementFilename(const QString &baseName, const QString &extension) { if (!(isFileExists(QUrl::fromUserInput(baseName + '.' + extension)))) { return baseName + '.' + extension; } QString fileNameFmt(baseName + "-%1." + extension); for (quint64 i = 1; i < std::numeric_limits::max(); i++) { if (!(isFileExists(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("OVERFLOW-" + (qrand() % 10000)); } -QString KSCore::makeSaveMimetype(const QUrl &url) +QString SpectacleCore::makeSaveMimetype(const QUrl &url) { QMimeDatabase mimedb; QString type = mimedb.mimeTypeForUrl(url).preferredSuffix(); if (type.isEmpty()) { return QString("png"); } return type; } -bool KSCore::writeImage(QIODevice *device, const QByteArray &format) +bool SpectacleCore::writeImage(QIODevice *device, const QByteArray &format) { QImageWriter imageWriter(device, format); if (!(imageWriter.canWrite())) { emit errorMessage(i18n("QImageWriter cannot write image: ") + imageWriter.errorString()); return false; } return imageWriter.write(mLocalPixmap.toImage()); } -bool KSCore::localSave(const QUrl &url, const QString &mimetype) +bool SpectacleCore::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 KSCore::remoteSave(const QUrl &url, const QString &mimetype) +bool SpectacleCore::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 KSCore::getTempSaveFilename() const +QUrl SpectacleCore::getTempSaveFilename() const { QDir tempDir = QDir::temp(); return QUrl::fromLocalFile(tempDir.absoluteFilePath("KSTempScreenshot.png")); } -bool KSCore::tempFileSave() +bool SpectacleCore::tempFileSave() { if (!(mLocalPixmap.isNull())) { - return localSave(getTempSaveFilename(), "png"); + const QUrl savePath = getTempSaveFilename(); + + if (localSave(savePath, "png")) { + return QFile::setPermissions(savePath.toLocalFile(), QFile::ReadUser | QFile::WriteUser); + } } + return false; } -QUrl KSCore::tempFileSave(const QString &mimetype) +QUrl SpectacleCore::tempFileSave(const QString &mimetype) { QTemporaryFile tmpFile; tmpFile.setAutoRemove(false); if (tmpFile.open()) { if(!writeImage(&tmpFile, mimetype.toLatin1())) { emit errorMessage(i18n("Cannot save screenshot. Error while writing temporary local file.")); return QUrl(); } return QUrl::fromLocalFile(tmpFile.fileName()); } return QUrl(); } -bool KSCore::doSave(const QUrl &url) +bool SpectacleCore::doSave(const QUrl &url) { if (!(url.isValid())) { emit errorMessage(i18n("Cannot save screenshot. The save filename is invalid.")); return false; } if (isFileExists(url) && (mOverwriteOnSave == false)) { emit errorMessage((i18n("Cannot save screenshot. The file already exists."))); return false; } QString mimetype = makeSaveMimetype(url); if (url.isLocalFile()) { return localSave(url, mimetype); } return remoteSave(url, mimetype); } -bool KSCore::isFileExists(const QUrl &url) +bool SpectacleCore::isFileExists(const QUrl &url) { if (!(url.isValid())) { return false; } KIO::StatJob * existsJob = KIO::stat(url, KIO::StatJob::DestinationSide, 0); existsJob->exec(); return (existsJob->error() == KJob::NoError); } diff --git a/src/KSCore.h b/src/SpectacleCore.h similarity index 82% rename from src/KSCore.h rename to src/SpectacleCore.h index fe1b03e..1a15001 100644 --- a/src/KSCore.h +++ b/src/SpectacleCore.h @@ -1,145 +1,155 @@ /* - * Copyright (C) 2015 Boudhayan Gupta + * 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 KSCORE_H #define KSCORE_H -#include - #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Config.h" #include "PlatformBackends/ImageGrabber.h" #include "PlatformBackends/DummyImageGrabber.h" #ifdef XCB_FOUND #include "PlatformBackends/X11ImageGrabber.h" #endif #include "Gui/KSMainWindow.h" -class KSCore : public QObject +class SpectacleCore : public QObject { Q_OBJECT Q_PROPERTY(QString filename READ filename WRITE setFilename NOTIFY filenameChanged) Q_PROPERTY(bool overwriteOnSave READ overwriteOnSave WRITE setOverwriteOnSave NOTIFY overwriteOnSaveChanged) Q_PROPERTY(ImageGrabber::GrabMode grabMode READ grabMode WRITE setGrabMode NOTIFY grabModeChanged) Q_PROPERTY(QString saveLocation READ saveLocation WRITE setSaveLocation NOTIFY saveLocationChanged) public: - explicit KSCore(bool backgroundMode, ImageGrabber::GrabMode grabMode, QString &saveFileName, qint64 delayMsec, bool sendToClipboard, bool notifyOnGrab, QObject *parent = 0); - ~KSCore(); + enum StartMode { + GuiMode = 0, + DBusMode = 1, + BackgroundMode = 2 + }; + + explicit SpectacleCore(StartMode startMode, ImageGrabber::GrabMode grabMode, QString &saveFileName, + qint64 delayMsec, bool sendToClipboard, bool notifyOnGrab, QObject *parent = 0); + ~SpectacleCore(); QString filename() const; void setFilename(const QString &filename); ImageGrabber::GrabMode grabMode() const; void setGrabMode(const ImageGrabber::GrabMode &grabMode); bool overwriteOnSave() const; void setOverwriteOnSave(const bool &overwrite); QString saveLocation() const; void setSaveLocation(const QString &savePath); signals: void errorMessage(const QString errString); void allDone(); void filenameChanged(QString filename); void grabModeChanged(ImageGrabber::GrabMode mode); void overwriteOnSaveChanged(bool overwriteOnSave); void saveLocationChanged(QString savePath); void imageSaved(QUrl location); + void imageSaved(QString location); + void grabFailed(); public slots: void takeNewScreenshot(const ImageGrabber::GrabMode &mode, const int &timeout, const bool &includePointer, const bool &includeDecorations); void showErrorMessage(const QString &errString); void screenshotUpdated(const QPixmap &pixmap); void screenshotFailed(); + void dbusStartAgent(); void doStartDragAndDrop(); void doPrint(QPrinter *printer); void doGuiSaveAs(); void doGuiSave(); void doAutoSave(); void doSendToService(KService::Ptr service); void doSendToOpenWith(); void doSendToClipboard(); private: + void initGui(); QUrl getAutosaveFilename(); QString makeAutosaveFilename(); QString autoIncrementFilename(const QString &baseName, const QString &extension); QString makeSaveMimetype(const QUrl &url); bool writeImage(QIODevice *device, const QByteArray &format); bool localSave(const QUrl &url, const QString &mimetype); bool remoteSave(const QUrl &url, const QString &mimetype); bool tempFileSave(); QUrl tempFileSave(const QString &mimetype); bool doSave(const QUrl &url); bool isFileExists(const QUrl &url); QUrl getTempSaveFilename() const; - bool mBackgroundMode; - bool mNotify; - bool mOverwriteOnSave; - bool mBackgroundSendToClipboard; - QPixmap mLocalPixmap; - QString mFileNameString; - QUrl mFileNameUrl; - ImageGrabber *mImageGrabber; - KSMainWindow *mMainWindow; + StartMode mStartMode; + bool mNotify; + bool mOverwriteOnSave; + bool mBackgroundSendToClipboard; + QPixmap mLocalPixmap; + QString mFileNameString; + QUrl mFileNameUrl; + ImageGrabber *mImageGrabber; + KSMainWindow *mMainWindow; + bool isGuiInited; }; #endif // KSCORE_H diff --git a/src/SpectacleDBusAdapter.cpp b/src/SpectacleDBusAdapter.cpp new file mode 100644 index 0000000..377e07d --- /dev/null +++ b/src/SpectacleDBusAdapter.cpp @@ -0,0 +1,60 @@ +/* + * 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 "SpectacleDBusAdapter.h" + +SpectacleDBusAdapter::SpectacleDBusAdapter(SpectacleCore *parent) + : QDBusAbstractAdaptor(parent) +{ + setAutoRelaySignals(false); +} + +SpectacleDBusAdapter::~SpectacleDBusAdapter() +{} + +inline SpectacleCore *SpectacleDBusAdapter::parent() const +{ + return static_cast(QObject::parent()); +} + +Q_NOREPLY void SpectacleDBusAdapter::StartAgent() +{ + parent()->dbusStartAgent(); +} + +Q_NOREPLY void SpectacleDBusAdapter::FullScreen(bool includeMousePointer) +{ + parent()->takeNewScreenshot(ImageGrabber::FullScreen, 0, includeMousePointer, true); +} + +Q_NOREPLY void SpectacleDBusAdapter::CurrentScreen(bool includeMousePointer) +{ + parent()->takeNewScreenshot(ImageGrabber::CurrentScreen, 0, includeMousePointer, true); +} + +Q_NOREPLY void SpectacleDBusAdapter::ActiveWindow(bool includeWindowDecorations, bool includeMousePointer) +{ + parent()->takeNewScreenshot(ImageGrabber::ActiveWindow, 0, includeMousePointer, includeWindowDecorations); +} + +Q_NOREPLY void SpectacleDBusAdapter::WindowUnderCursor(bool includeWindowDecorations, bool includeMousePointer) +{ + parent()->takeNewScreenshot(ImageGrabber::WindowUnderCursor, 0, includeMousePointer, includeWindowDecorations); +} + diff --git a/src/SpectacleDBusAdapter.h b/src/SpectacleDBusAdapter.h new file mode 100644 index 0000000..e401236 --- /dev/null +++ b/src/SpectacleDBusAdapter.h @@ -0,0 +1,78 @@ +/* + * 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 SPECTACLEDBUSADAPTER_H +#define SPECTACLEDBUSADAPTER_H + +#include +#include "SpectacleCore.h" + +class SpectacleDBusAdapter: public QDBusAbstractAdaptor +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.freedesktop.Screenshot") + Q_CLASSINFO("D-Bus Introspection", "" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "" + ) + + public: + + SpectacleDBusAdapter(SpectacleCore *parent); + virtual ~SpectacleDBusAdapter(); + + inline SpectacleCore *parent() const; + + public slots: + + Q_NOREPLY void StartAgent(); + Q_NOREPLY void FullScreen(bool includeMousePointer); + Q_NOREPLY void CurrentScreen(bool includeMousePointer); + Q_NOREPLY void ActiveWindow(bool includeWindowDecorations, bool includeMousePointer); + Q_NOREPLY void WindowUnderCursor(bool includeWindowDecorations, bool includeMousePointer); + + signals: + + void ScreenshotTaken(const QString &fileName); + void ScreenshotFailed(); +}; + +#endif // SPECTACLEDBUSADAPTER_H