diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7f4515d..3a70fa5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,96 +1,113 @@
# Spectacle project
project(Spectacle)
# KDE Application Version, managed by release script
-set(KDE_APPLICATIONS_VERSION_MAJOR "15")
-set(KDE_APPLICATIONS_VERSION_MINOR "12")
-set(KDE_APPLICATIONS_VERSION_MICRO "1")
+set(KDE_APPLICATIONS_VERSION_MAJOR "16")
+set(KDE_APPLICATIONS_VERSION_MINOR "07")
+set(KDE_APPLICATIONS_VERSION_MICRO "70")
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)
set(QT_MIN_VERSION "5.4.0")
set(KF5_MIN_VERSION "5.18.0")
set(PLASMA_MIN_VERSION "5.4.0")
find_package(ECM ${KF5_MIN_VERSION} 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 NO_POLICY_SCOPE)
include(ECMInstallIcons)
include(ECMSetupVersion)
include(FeatureSummary)
find_package(
Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED
Core
Widgets
DBus
PrintSupport
Quick
)
find_package(
KF5 ${KF5_MIN_VERSION} REQUIRED
CoreAddons
WidgetsAddons
DBusAddons
Notifications
Config
I18n
KIO
XmlGui
WindowSystem
DocTools
Declarative
)
# optional components
find_package(KF5Kipi)
if (KF5Kipi_FOUND)
set(KIPI_FOUND 1)
endif ()
find_package(KDEExperimentalPurpose)
if (KDEExperimentalPurpose_FOUND)
set(PURPOSE_FOUND 1)
endif()
find_package(XCB COMPONENTS XFIXES IMAGE UTIL CURSOR)
+set(XCB_COMPONENTS_ERRORS FALSE)
if (XCB_FOUND)
- find_package(Qt5X11Extras ${QT_MIN_VERSION} REQUIRED)
- find_package(KF5Screen ${PLASMA_MIN_VERSION} REQUIRED)
+ find_package(Qt5X11Extras ${QT_MIN_VERSION} REQUIRED)
+ find_package(KF5Screen ${PLASMA_MIN_VERSION} REQUIRED)
+endif()
+set(XCB_COMPONENTS_FOUND TRUE)
+if(NOT XCB_XFIXES_FOUND)
+ set(XCB_COMPONENTS_ERRORS "${XCB_COMPONENTS_ERRORS} XCB-XFIXES ")
+ set(XCB_COMPONENTS_FOUND FALSE)
+endif()
+if(NOT XCB_IMAGE_FOUND)
+ set(XCB_COMPONENTS_ERRORS "${XCB_COMPONENTS_ERRORS} XCB-IMAGE ")
+ set(XCB_COMPONENTS_FOUND FALSE)
+endif()
+if(NOT XCB_UTIL_FOUND)
+ set(XCB_COMPONENTS_ERRORS "${XCB_COMPONENTS_ERRORS} XCB-UTIL ")
+ set(XCB_COMPONENTS_FOUND FALSE)
+endif()
+if(NOT XCB_CURSOR_FOUND)
+ set(XCB_COMPONENTS_ERRORS "${XCB_COMPONENTS_ERRORS} XCB-CURSOR ")
+ set(XCB_COMPONENTS_FOUND FALSE)
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")
+if (NOT XCB_FOUND OR NOT XCB_COMPONENTS_FOUND)
+ message(FATAL_ERROR "No suitable backend platform was found. Currenty supported platforms are: XCB Components Required: ${XCB_COMPONENTS_ERRORS}")
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/ExtraDesktop.sh b/ExtraDesktop.sh
new file mode 100644
index 0000000..07a5fd8
--- /dev/null
+++ b/ExtraDesktop.sh
@@ -0,0 +1,4 @@
+#! /bin/sh
+#This file outputs in a separate line each file with a .desktop syntax
+#that needs to be translated but has a non .desktop extension
+find -name \*.khotkeys -print
diff --git a/desktop/org.kde.spectacle.desktop b/desktop/org.kde.spectacle.desktop
index fa6a77b..9574963 100644
--- a/desktop/org.kde.spectacle.desktop
+++ b/desktop/org.kde.spectacle.desktop
@@ -1,168 +1,178 @@
[Desktop Entry]
GenericName=Screenshot Capture Utility
GenericName[ar]=أداة لأخذ اللقطات
GenericName[ast]=Utilidá de captura de pantalla
GenericName[bg]=Инструмент за заснемане на екрана
GenericName[ca]=Utilitat de captura de pantalla
GenericName[ca@valencia]=Utilitat de captura de pantalla
GenericName[cs]=Nástroj na snímání obrazovky
GenericName[de]=Dienstprogramm für Bildschirmfotos
+GenericName[el]=Εργαλείο λήψης στιγμιοτύπων οθόνης
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[it]=Accessorio per catturare schermate
GenericName[ko]=화면 캡처 유틸리티
GenericName[lt]=KDE ekranvaizdžių paveikslavimas
GenericName[nl]=Hulpmiddel voor het vangen van een schermafdruk
GenericName[nn]=Verktøy for å lagra skjermbilete
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[ru]=Создание снимков экрана
GenericName[sk]=Nástroj na snímanie obrazovky
GenericName[sl]=Pripomoček za zajem zaslona
GenericName[sv]=Verktyg för att ta skärmbilder
GenericName[uk]=Програма для створення знімків екрана
GenericName[x-test]=xxScreenshot Capture Utilityxx
GenericName[zh_CN]=屏幕截图工具
Name=Spectacle
Name[ast]=Spectacle
Name[ca]=Spectacle
Name[ca@valencia]=Spectacle
Name[da]=Spectacle
Name[de]=Spectacle
+Name[el]=Spectacle
Name[en_GB]=Spectacle
Name[es]=Spectacle
Name[fi]=Spectacle
Name[gl]=Spectacle
Name[it]=Spectacle
Name[ko]=Spectacle
Name[lt]=Spectacle
Name[nl]=Spectacle
Name[nn]=Spectacle
Name[pl]=Spectacle
Name[pt]=Spectacle
Name[pt_BR]=Spectacle
Name[ru]=Spectacle
Name[sk]=Spectacle
Name[sl]=Spectacle
Name[sv]=Spectacle
Name[uk]=Spectacle
Name[x-test]=xxSpectaclexx
Name[zh_CN]=Spectacle
Categories=Qt;KDE;Utility;
MimeType=
Exec=spectacle
Icon=spectacle
Type=Application
Terminal=false
StartupNotify=false
Actions=FullScreenScreenShot;CurrentMonitorScreenShot;ActiveWindowScreenShot;RectangularRegionScreenShot;
X-DBUS-StartupType=Multi
X-DBUS-ServiceName=org.kde.Spectacle
[Desktop Action FullScreenScreenShot]
Name=Capture Entire Desktop
Name[ar]=التقط سطح المكتب بالكامل
Name[ast]=Capturar tol escritoriu
Name[ca]=Captura l'escriptori sencer
Name[ca@valencia]=Captura l'escriptori sencer
Name[de]=Die gesamte Arbeitsfläche aufnehmen
+Name[el]=Λήψη όλης της επιφάνειας εργασίας
Name[en_GB]=Capture Entire Desktop
Name[es]=Capturar todo el escritorio
Name[fi]=Kaappaa koko työpöytä
Name[gl]=Capturar todo o escritorio
Name[it]=Cattura l'intero desktop
Name[ko]=전체 데스크톱 찍기
Name[lt]=Paveiksluoti visą darbalaukį
Name[nl]=Gehele bureaublad opnemen
Name[nn]=Ta bilete av heile skrivebordet
Name[pl]=Przechwyć cały pulpit
Name[pt]=Capturar Todo o Ecrã
Name[pt_BR]=Capturar toda a área de trabalho
+Name[sk]=Zachytiť celú plochu
Name[sl]=Zajemi celotno namizje
Name[sv]=Ta en bild av hela skrivbordet
Name[uk]=Захопити зображення усієї стільниці
Name[x-test]=xxCapture Entire Desktopxx
Name[zh_CN]=捕获整个桌面
Exec=spectacle -f
[Desktop Action CurrentMonitorScreenShot]
Name=Capture Current Monitor
Name[ar]=التقط الشّاشة الحاليّة
Name[ast]=Capturar monitor actual
Name[ca]=Captura el monitor actual
Name[ca@valencia]=Captura el monitor actual
Name[de]=Den aktuellen Bildschirm aufnehmen
+Name[el]=Λήψη της τρέχουσας οθόνης
Name[en_GB]=Capture Current Monitor
Name[es]=Capturar el monitor actual
Name[fi]=Kaappaa nykyinen näyttö
Name[gl]=Capturar o monitor actual
Name[it]=Cattura il monitor attuale
Name[ko]=현재 모니터 찍기
Name[lt]=Paveiksluoti šio monitoriaus vaizdą
Name[nl]=Huidige monitor opnemen
Name[nn]=Ta bilete av gjeldande skjerm
Name[pl]=Przechwyć bieżący monitor
Name[pt]=Capturar o Monitor Actual
Name[pt_BR]=Capturar do monitor atual
+Name[sk]=Zachytiť aktuálny monitor
Name[sl]=Zajemi trenutni zaslon
Name[sv]=Ta en bild av aktuell bildskärm
Name[uk]=Захопити зображення на моніторі
Name[x-test]=xxCapture Current Monitorxx
Name[zh_CN]=捕获当前屏幕
Exec=spectacle -m
[Desktop Action ActiveWindowScreenShot]
Name=Capture Active Window
Name[ar]=التقط النّافذة النّشطة
Name[ast]=Capturar ventana actual
Name[ca]=Captura la finestra activa
Name[ca@valencia]=Captura la finestra activa
Name[de]=Das aktive Fenster aufnehmen
+Name[el]=Λήψη του ενεργού παραθύρου
Name[en_GB]=Capture Active Window
Name[es]=Capturar la ventana activa
Name[fi]=Kaappaa aktiivinen ikkuna
Name[gl]=Capturar a xanela activa
Name[it]=Cattura la finestra attiva
Name[ko]=활성 창 찍기
Name[lt]=Paveiksluoti veikiamąjį langą
Name[nl]=Actieve venster opnemen
Name[nn]=Ta bilete av aktivt vindauge
Name[pl]=Przechwyć aktywne okno
Name[pt]=Capturar a Janela Activa
Name[pt_BR]=Capturar a janela ativa
+Name[sk]=Zachytiť aktuálne okno
Name[sl]=Zajemi dejavno okno
Name[sv]=Ta en bild av aktivt fönster
Name[uk]=Захопити зображення активного вікна
Name[x-test]=xxCapture Active Windowxx
Name[zh_CN]=捕获活动窗口
Exec=spectacle -a
[Desktop Action RectangularRegionScreenShot]
Name=Capture Rectangular Region
Name[ar]=التقط منطقة مستطيلة
Name[ast]=Capturar rexón rectangular
Name[ca]=Captura una regió rectangular
Name[ca@valencia]=Captura una regió rectangular
Name[de]=Einen rechteckigen Bereich aufnehmen
+Name[el]=Λήψη ορθογώνιας περιοχής
Name[en_GB]=Capture Rectangular Region
Name[es]=Capturar una región rectangular
Name[fi]=Kaappaa suorakulmainen alue
Name[gl]=Capturar unha rexión rectangular
Name[it]=Cattura una regione rettangolare
Name[ko]=화면의 사각형 영역 찍기
Name[lt]=Paveiksluoti stačiakampę ekrano sritį
Name[nl]=Rechthoekig gebied opnemen
Name[nn]=Ta bilete av skjermutsnitt
Name[pl]=Przechwyć obszar prostokątny
Name[pt]=Capturar uma Região Rectangular
Name[pt_BR]=Capturar uma região retangular
+Name[sk]=Zachytiť pravouhlú oblasť
Name[sl]=Zajemi pravokotno območje
Name[sv]=Ta en bild av ett rektangulärt område
Name[uk]=Захопити прямокутну область екрана
Name[x-test]=xxCapture Rectangular Regionxx
Name[zh_CN]=捕获矩形区域
Exec=spectacle -r
diff --git a/desktop/spectacle.khotkeys b/desktop/spectacle.khotkeys
index 95939df..9b242fa 100644
--- a/desktop/spectacle.khotkeys
+++ b/desktop/spectacle.khotkeys
@@ -1,130 +1,414 @@
[Main]
ImportId=spectacle
Version=3
[Data]
DataCount=1
[Data_1]
Comment=Shortcuts for taking screenshots
+Comment[ast]=Atayos pa facer captures
+Comment[ca]=Dreceres per a prendre captures de pantalla
+Comment[ca@valencia]=Dreceres per a prendre captures de pantalla
+Comment[de]=Kurzbefehle für die Aufnahme von Bildschirmfotos
+Comment[el]=Συντομεύσεις για τη λήψη στιγμιοτύπων οθόνης
+Comment[en_GB]=Shortcuts for taking screenshots
+Comment[es]=Accesos rápidos para realizar capturas de pantalla
+Comment[fi]=Kuvankaappausten ottamisen pikanäppäimet
+Comment[gl]=Atallos para facer capturas de pantalla.
+Comment[it]=Scorciatoia per acquisire schermate
+Comment[nl]=Sneltoetsen voor het maken van schermafdrukken
+Comment[nn]=Snarvegar for å ta bilete av skjermen
+Comment[pl]=Skróty do zrzucania ekranu
+Comment[pt]=Atalhos para tirar fotografias
+Comment[pt_BR]=Atalhos para capturar imagens da tela
+Comment[sl]=Bližnjice za zajemanje zaslonskih slik
+Comment[sv]=Genvägar för att ta skärmbilder
+Comment[uk]=Клавіатурні скорочення для створення знімків
+Comment[x-test]=xxShortcuts for taking screenshotsxx
+Comment[zh_CN]=截图快捷键
DataCount=4
Enabled=true
Name=Screenshots
+Name[ast]=Captures de pantalla
+Name[ca]=Captures de pantalla
+Name[ca@valencia]=Captures de pantalla
+Name[de]=Bildschirmfotos
+Name[el]=Στιγμιότυπα οθόνης
+Name[en_GB]=Screenshots
+Name[es]=Capturas de pantalla
+Name[fi]=Kuvankaappaukset
+Name[gl]=Capturas de pantalla
+Name[it]=Schermate
+Name[nl]=Schermafdrukken
+Name[nn]=Skjermbilete
+Name[pl]=Zrzuty ekranu
+Name[pt]=Fotografias
+Name[pt_BR]=Captura de tela
+Name[sl]=Zaslonske slike
+Name[sv]=Skärmbilder
+Name[uk]=Знімки екрана
+Name[x-test]=xxScreenshotsxx
+Name[zh_CN]=截图
SystemGroup=0
Type=ACTION_DATA_GROUP
[Data_1Conditions]
Comment=
ConditionsCount=0
[Data_1_1]
Comment=Start the screenshot tool and show the GUI
+Comment[ast]=Anicia la ferramienta de captures de pantalla y amuesa la GUI
+Comment[ca]=Inicia l'eina de captura de pantalla i mostra la IGU
+Comment[ca@valencia]=Inicia l'eina de captura de pantalla i mostra la IGU
+Comment[de]=Startet das Programm und zeigt die graphische Bedienungsoberfläche an
+Comment[el]=Εκκίνηση του εργαλείου στιγμιοτύπων και εμφάνιση του γραφικού περιβάλλοντος
+Comment[en_GB]=Start the screenshot tool and show the GUI
+Comment[es]=Iniciar la herramienta de captura de pantalla y mostrar su interfaz
+Comment[fi]=Käynnistä kuvankaappausohjelma ja näytä käyttöliittymä
+Comment[gl]=Iniciar a ferramenta de captura de pantalla e mostrar a súa interface.
+Comment[it]=Avvia lo strumento per le schermate e ne mostra l'interfaccia
+Comment[nl]=Start het hulpmiddel voor schermafdrukken en toon de GUI
+Comment[nn]=Start skjermbiletverktøyet og vis grafisk grensesnitt
+Comment[pl]=Wywołaj narzędzie zrzutów ekranu i pokaż graficzny interfejs
+Comment[pt]=Iniciar a ferramenta de fotografias e mostrar a interface
+Comment[pt_BR]=Inicia a ferramenta gráfica para captura de telas
+Comment[sl]=Zaženi orodje za zaslonske slike in prikaži grafični uporabniški vmesnik
+Comment[sv]=Starta skärmbildsverktyget och visa det grafiska användargränssnittet
+Comment[uk]=Запустити інструмент створення знімків і показати графічний інтерфейс
+Comment[x-test]=xxStart the screenshot tool and show the GUIxx
+Comment[zh_CN]=启动截图工具并显示界面
Enabled=true
Name=Start Screenshot Tool
+Name[ast]=Aniciar ferramienta de captures de pantalla
+Name[ca]=Inicia l'eina de captura de pantalla
+Name[ca@valencia]=Inicia l'eina de captura de pantalla
+Name[de]=Bildschirmfoto-Programm starten
+Name[el]=Εκκίνηση του εργαλείου στιγμιοτύπων
+Name[en_GB]=Start Screenshot Tool
+Name[es]=Iniciar la herramienta de captura de pantalla
+Name[fi]=Käynnistä kuvankaappausohjelma
+Name[gl]=Iniciar a ferramenta de captura de pantalla
+Name[it]=Avvia lo strumento per le schermate
+Name[nl]=Het hulpmiddel voor schermafdrukken starten
+Name[nn]=Skjermbiletverktøyet
+Name[pl]=Wywołaj narzędzie zrzutów ekranu
+Name[pt]=Iniciar a Ferramenta de Fotografias
+Name[pt_BR]=Iniciar a ferramenta de captura de tela
+Name[sl]=Zaženi orodje za zaslonske slike
+Name[sv]=Starta skärmbildsverktyg
+Name[uk]=Запустити інструмент створення знімків
+Name[x-test]=xxStart Screenshot Toolxx
+Name[zh_CN]=启动截图工具
Type=SIMPLE_ACTION_DATA
[Data_1_1Actions]
ActionsCount=1
[Data_1_1Actions0]
Arguments=
Call=StartAgent
RemoteApp=org.kde.Spectacle
RemoteObj=/
Type=DBUS
[Data_1_1Conditions]
Comment=
ConditionsCount=0
[Data_1_1Triggers]
Comment=Simple_action
+Comment[ast]=Aición_cenciella
+Comment[ca]=Acció_senzilla
+Comment[ca@valencia]=Acció_senzilla
+Comment[cs]=Jednoduchá činnost
+Comment[de]=Einfache Aktion
+Comment[el]=Απλή_ενέργεια
+Comment[en_GB]=Simple_action
+Comment[es]=Acción_sencilla
+Comment[fi]=Yksinkertainen_toiminto
+Comment[gl]=Acción_sinxela
+Comment[it]=Azione_semplice
+Comment[nl]=Eenvoudige_handeling
+Comment[nn]=Enkel_handling
+Comment[pl]=Proste_działanie
+Comment[pt]=Acção Simples
+Comment[pt_BR]=Ação simples
+Comment[sl]=Preprosto_dejanje
+Comment[sv]=Enkel-åtgärd
+Comment[uk]=Проста_дія
+Comment[x-test]=xxSimple_actionxx
+Comment[zh_CN]=简单动作
TriggersCount=1
[Data_1_1Triggers0]
Key=Print
Type=SHORTCUT
[Data_1_2]
Comment=Take a full screen (all monitors) screenshot and save it
+Comment[ast]=Fai una captura a pantalla completa (tolos monitores) y guárdala
+Comment[ca]=Pren una captura de pantalla completa (tots els monitors) i la desa
+Comment[ca@valencia]=Pren una captura de pantalla completa (tots els monitors) i la guarda
+Comment[de]=Bildschirmfoto des ganzen Bildschirms aller Monitore aufnehmen und speichern
+Comment[el]=Λήψη ενός πλήρους στιγμιοτύπου οθόνης (όλες οι οθόνες) και αποθήκευσή του
+Comment[en_GB]=Take a full screen (all monitors) screenshot and save it
+Comment[es]=Hacer una captura de pantalla completa (de todos los monitores) y guardarla
+Comment[fi]=Ota koko näytön (kaikkien näyttöjen) kuvankaappaus ja tallenna se
+Comment[gl]=Capturar todas as pantallas e gardar a captura.
+Comment[it]=Cattura una schermata a tutto schermo (tutti i monitor) e la salva
+Comment[nl]=Een volledige schermafdruk maken (alle monitoren) en deze opslaan
+Comment[nn]=Ta eit fullskjermbilete (alle skjermar) og lagra det
+Comment[pl]=Wykonaj zrzut całego ekranu (wszystkie monitory) i zapisz go
+Comment[pt]=Capturar o ecrã por inteiro (todos os monitores) e gravar a imagem
+Comment[pt_BR]=Captura e salva a tela inteira (todos os monitores)
+Comment[sl]=Zajemi celozaslonsko sliko (vsi zasloni) in jo shrani
+Comment[sv]=Ta en skärmbild av hela skärmen (alla bildskärmar) och spara den
+Comment[uk]=Зробити знімок усього екрана (усіх моніторів) і зберегти його
+Comment[x-test]=xxTake a full screen (all monitors) screenshot and save itxx
+Comment[zh_CN]=对全屏 (所有显示器) 截图并保存
Enabled=true
Name=Take Full Screen Screenshot
+Name[ast]=Facer captura de pantalla completa
+Name[ca]=Pren una captura de pantalla completa
+Name[ca@valencia]=Pren una captura de pantalla completa
+Name[de]=Bildschirmfoto des ganzen Bildschirms aufnehmen
+Name[el]=Λήψη πλήρους στιγμιοτύπου οθόνης
+Name[en_GB]=Take Full Screen Screenshot
+Name[es]=Hacer una captura de pantalla completa
+Name[fi]=Ota koko näytön kuvankaappaus
+Name[gl]=Capturar todas as pantallas
+Name[it]=Cattura l'intero schermo
+Name[nl]=Schermafdruk van Volledig scherm maken
+Name[nn]=Ta fullskjermsbilete
+Name[pl]=Wykonaj zrzut całego ekranu
+Name[pt]=Tirar uma Imagem do Ecrã Inteiro
+Name[pt_BR]=Capturar a tela inteira
+Name[sl]=Zajemi celozaslonsko sliko
+Name[sv]=Ta en skärmbild av hela skärmen
+Name[uk]=Зробити знімок усього екрана
+Name[x-test]=xxTake Full Screen Screenshotxx
+Name[zh_CN]=对全屏截图
Type=SIMPLE_ACTION_DATA
[Data_1_2Actions]
ActionsCount=1
[Data_1_2Actions0]
Arguments=false
Call=FullScreen
RemoteApp=org.kde.Spectacle
RemoteObj=/
Type=DBUS
[Data_1_2Conditions]
Comment=
ConditionsCount=0
[Data_1_2Triggers]
Comment=Simple_action
+Comment[ast]=Aición_cenciella
+Comment[ca]=Acció_senzilla
+Comment[ca@valencia]=Acció_senzilla
+Comment[cs]=Jednoduchá činnost
+Comment[de]=Einfache Aktion
+Comment[el]=Απλή_ενέργεια
+Comment[en_GB]=Simple_action
+Comment[es]=Acción_sencilla
+Comment[fi]=Yksinkertainen_toiminto
+Comment[gl]=Acción_sinxela
+Comment[it]=Azione_semplice
+Comment[nl]=Eenvoudige_handeling
+Comment[nn]=Enkel_handling
+Comment[pl]=Proste_działanie
+Comment[pt]=Acção Simples
+Comment[pt_BR]=Ação simples
+Comment[sl]=Preprosto_dejanje
+Comment[sv]=Enkel-åtgärd
+Comment[uk]=Проста_дія
+Comment[x-test]=xxSimple_actionxx
+Comment[zh_CN]=简单动作
TriggersCount=1
[Data_1_2Triggers0]
Key=Shift+Print
Type=SHORTCUT
[Data_1_3]
Comment=Take a screenshot of the currently active window and save it
+Comment[ast]=Fai una captura de la ventana activa anguaño y guárdala
+Comment[ca]=Pren una captura de pantalla de la finestra activa i la desa
+Comment[ca@valencia]=Pren una captura de pantalla de la finestra activa i la guarda
+Comment[de]=Erstellt ein Bildschirmfoto des aktuell aktiven Fensters und speichert es
+Comment[el]=Λήψη ενός στιγμιοτύπου του ενεργού παραθύρου και αποθήκευσή του
+Comment[en_GB]=Take a screenshot of the currently active window and save it
+Comment[es]=Hacer una captura de la ventana activa actualmente y guardarla
+Comment[fi]=Ota kuvankaappaus aktiivisesta ikkunasta ja tallenna se
+Comment[gl]=Capturar a xanela activa e gardar a captura.
+Comment[it]=Cattura una schermata della finestra attiva e la salva
+Comment[nl]=Een schermafdruk van het huidige actieve venster maken en het opslaan
+Comment[nn]=Ta skjermbilete frå det aktive vindauget og lagra det
+Comment[pl]=Wykonaj zrzut obecnie aktywnego okna i zapisz go
+Comment[pt]=Capturar uma imagem da janela activa de momento e gravá-la
+Comment[pt_BR]=Captura e salva a imagem da janela ativa
+Comment[sl]=Zajemi zaslonsko sliko trenutno dejavnega okna in jo shrani
+Comment[sv]=Ta en skärmbild av nuvarande aktiva fönster och spara den
+Comment[uk]=Зробити знімок поточного активного вікна і зберегти його
+Comment[x-test]=xxTake a screenshot of the currently active window and save itxx
+Comment[zh_CN]=对当前活动窗口截图并保存
Enabled=true
Name=Take Active Window Screenshot
+Name[ast]=Capturar ventana activa
+Name[ca]=Pren una captura de pantalla de la finestra activa
+Name[ca@valencia]=Pren una captura de pantalla de la finestra activa
+Name[de]=Bildschirmfoto des aktiven Fensters
+Name[el]=Λήψη στιγμιοτύπου ενεργού παραθύρου
+Name[en_GB]=Take Active Window Screenshot
+Name[es]=Hacer una captura de la ventana activa
+Name[fi]=Ota kuvankaappaus aktiivisesta ikkunasta
+Name[gl]=Capturar a xanela activa
+Name[it]=Cattura la finestra attiva
+Name[nl]=Schermafdruk van actieve venster maken
+Name[nn]=Ta skjermbilete av aktivt vindauge
+Name[pl]=Wykonaj zrzut obecnie aktywnego okna
+Name[pt]=Tirar uma Fotografia da Janela Activa
+Name[pt_BR]=Capturar a imagem da janela ativa
+Name[sl]=Zajemi zaslonsko sliko dejavnega okna
+Name[sv]=Ta en skärmbild av aktivt fönster
+Name[uk]=Зробити знімок активного вікна
+Name[x-test]=xxTake Active Window Screenshotxx
+Name[zh_CN]=对活动窗口截图
Type=SIMPLE_ACTION_DATA
[Data_1_3Actions]
ActionsCount=1
[Data_1_3Actions0]
Arguments=true, false
Call=ActiveWindow
RemoteApp=org.kde.Spectacle
RemoteObj=/
Type=DBUS
[Data_1_3Conditions]
Comment=
ConditionsCount=0
[Data_1_3Triggers]
Comment=Simple_action
+Comment[ast]=Aición_cenciella
+Comment[ca]=Acció_senzilla
+Comment[ca@valencia]=Acció_senzilla
+Comment[cs]=Jednoduchá činnost
+Comment[de]=Einfache Aktion
+Comment[el]=Απλή_ενέργεια
+Comment[en_GB]=Simple_action
+Comment[es]=Acción_sencilla
+Comment[fi]=Yksinkertainen_toiminto
+Comment[gl]=Acción_sinxela
+Comment[it]=Azione_semplice
+Comment[nl]=Eenvoudige_handeling
+Comment[nn]=Enkel_handling
+Comment[pl]=Proste_działanie
+Comment[pt]=Acção Simples
+Comment[pt_BR]=Ação simples
+Comment[sl]=Preprosto_dejanje
+Comment[sv]=Enkel-åtgärd
+Comment[uk]=Проста_дія
+Comment[x-test]=xxSimple_actionxx
+Comment[zh_CN]=简单动作
TriggersCount=1
[Data_1_3Triggers0]
Key=Meta+Print
Type=SHORTCUT
[Data_1_4]
Comment=Take a screenshot of a rectangular region you specify and save it
+Comment[ast]=Fai una captura d'una rexón rectangular qu'especifiques y guárdala
+Comment[ca]=Pren una captura de pantalla d'una regió rectangular i la desa
+Comment[ca@valencia]=Pren una captura de pantalla d'una regió rectangular i la guarda
+Comment[de]=Erstellt ein Bildschirmfoto eines rechteckigen Bereichs und speichert es
+Comment[el]=Λήψη στιγμιοτύπου μιας καθορισμένης ορθογώνιας περιοχής και αποθήκευσή του
+Comment[en_GB]=Take a screenshot of a rectangular region you specify and save it
+Comment[es]=Hacer una captura de la región rectangular indicada y guardarla
+Comment[fi]=Ota kuvankaappaus annetusta suorakulmaisesta alueesta ja tallenna se
+Comment[gl]=Capturar unha rexión rectangular indicada e gardar a captura.
+Comment[it]=Cattura una schermata di una regione rettangolare a piacere e la salva
+Comment[nl]=Een schermafdruk van een rechthoekig gebied dat u specificeert maken en het opslaan
+Comment[nn]=Ta rektangulært skjermutsnitt og lagra det
+Comment[pl]=Wykonaj zrzut podanego przez ciebie prostokątnego obszaru i zapisz go
+Comment[pt]=Capturar uma imagem de uma região rectangular à sua escolha e gravá-la
+Comment[pt_BR]=Captura e salva a imagem de uma região retangular
+Comment[sl]=Zajemi zaslonsko sliko pravokotnega območja in jo shrani
+Comment[sv]=Ta en skärmbild av ett angivet rektangulärt område och spara den
+Comment[uk]=Зробити знімок вказаної прямокутної ділянки і зберегти його
+Comment[x-test]=xxTake a screenshot of a rectangular region you specify and save itxx
+Comment[zh_CN]=对指定矩形区域截图并保存
Enabled=true
Name=Take Rectangular Region Screenshot
+Name[ast]=Capturar rexón rectangular
+Name[ca]=Pren una captura de pantalla d'una regió rectangular
+Name[ca@valencia]=Pren una captura de pantalla d'una regió rectangular
+Name[de]=Bildschirmfoto eines rechteckigen Bereichs
+Name[el]=Λήψη στιγμιοτύπου ορθογώνιας περιοχής
+Name[en_GB]=Take Rectangular Region Screenshot
+Name[es]=Hacer una captura de una región rectangular
+Name[fi]=Ota kuvankaappaus suorakulmaisesta alueesta
+Name[gl]=Capturar unha rexión rectangular
+Name[it]=Cattura una regione rettangolare
+Name[nl]=Schermafdruk van rechthoekig gebied maken
+Name[nn]=Ta rektangulært skjermutsnitt
+Name[pl]=Wykonaj zrzut prostokątnego obszaru
+Name[pt]=Tirar uma Fotografia de uma Região Rectangular
+Name[pt_BR]=Capturar a imagem de uma região retangular
+Name[sl]=Zajemi zaslonsko sliko pravokotnega območja
+Name[sv]=Ta en skärmbild av ett rektangulärt område
+Name[uk]=Зробити знімок прямокутної ділянки
+Name[x-test]=xxTake Rectangular Region Screenshotxx
+Name[zh_CN]=对矩形区域截图
Type=SIMPLE_ACTION_DATA
[Data_1_4Actions]
ActionsCount=1
[Data_1_4Actions0]
Arguments=true
Call=RectangularRegion
RemoteApp=org.kde.Spectacle
RemoteObj=/
Type=DBUS
[Data_1_4Conditions]
Comment=
ConditionsCount=0
[Data_1_4Triggers]
Comment=Simple_action
+Comment[ast]=Aición_cenciella
+Comment[ca]=Acció_senzilla
+Comment[ca@valencia]=Acció_senzilla
+Comment[cs]=Jednoduchá činnost
+Comment[de]=Einfache Aktion
+Comment[el]=Απλή_ενέργεια
+Comment[en_GB]=Simple_action
+Comment[es]=Acción_sencilla
+Comment[fi]=Yksinkertainen_toiminto
+Comment[gl]=Acción_sinxela
+Comment[it]=Azione_semplice
+Comment[nl]=Eenvoudige_handeling
+Comment[nn]=Enkel_handling
+Comment[pl]=Proste_działanie
+Comment[pt]=Acção Simples
+Comment[pt_BR]=Ação simples
+Comment[sl]=Preprosto_dejanje
+Comment[sv]=Enkel-åtgärd
+Comment[uk]=Проста_дія
+Comment[x-test]=xxSimple_actionxx
+Comment[zh_CN]=简单动作
TriggersCount=1
[Data_1_4Triggers0]
Key=Meta+Shift+Print
-Type=SHORTCUT
\ No newline at end of file
+Type=SHORTCUT
diff --git a/desktop/spectacle.notifyrc b/desktop/spectacle.notifyrc
index e31c9d4..dd9b445 100644
--- a/desktop/spectacle.notifyrc
+++ b/desktop/spectacle.notifyrc
@@ -1,106 +1,110 @@
[Global]
IconName=spectacle
Name=Spectacle
Name[ast]=Spectacle
Name[ca]=Spectacle
Name[ca@valencia]=Spectacle
Name[da]=Spectacle
Name[de]=Spectacle
+Name[el]=Spectacle
Name[en_GB]=Spectacle
Name[es]=Spectacle
Name[fi]=Spectacle
Name[gl]=Spectacle
Name[it]=Spectacle
Name[ko]=Spectacle
Name[lt]=Spectacle
Name[nl]=Spectacle
Name[nn]=Spectacle
Name[pl]=Spectacle
Name[pt]=Spectacle
Name[pt_BR]=Spectacle
Name[ru]=Spectacle
Name[sk]=Spectacle
Name[sl]=Spectacle
Name[sv]=Spectacle
Name[uk]=Spectacle
Name[x-test]=xxSpectaclexx
Name[zh_CN]=Spectacle
Comment=Screenshot Capture Utility
Comment[ar]=أداة لأخذ اللقطات
Comment[ast]=Utilidá de captura de pantalla
-Comment[ca]=Utilitat per pendre captures de pantalla
-Comment[ca@valencia]=Utilitat per pendre captures de pantalla
+Comment[ca]=Utilitat per prendre captures de pantalla
+Comment[ca@valencia]=Utilitat per prendre captures de pantalla
Comment[cs]=Nástroj na snímání obrazovky
Comment[de]=Dienstprogramm für Bildschirmfotos
+Comment[el]=Εργαλείο λήψης στιγμιοτύπων οθόνης
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[it]=Accessorio per catturare schermate
Comment[ko]=화면 캡처 유틸리티
Comment[lt]=KDE ekranvaizdžių paveikslavimo programa
Comment[nl]=Hulpmiddel voor het maken van een schermafdruk
Comment[nn]=Verktøy for å lagra skjermbilete
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[ru]=Программа для создания снимков экрана
Comment[sk]=Nástroj na zachytávanie obrazovky
Comment[sl]=Pripomoček za zajem zaslona
Comment[sv]=Verktyg för att ta skärmbilder
Comment[uk]=Програма для створення знімків екрана
Comment[x-test]=xxScreenshot Capture Utilityxx
Comment[zh_CN]=屏幕捕获工具
[Event/newScreenshotSaved]
Name=New Screenshot Saved
Name[ar]=حُفظت لقطة جديدة
Name[ast]=Guardóse una captura nueva
Name[ca]=S'ha desat una captura de pantalla nova
Name[ca@valencia]=S'ha guardat una captura de pantalla nova
Name[de]=Neues Bildschirmfoto gespeichert
+Name[el]=Αποθηκεύτηκε νέο στιγμιότυπο
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[it]=Nuova schermata salvata
Name[ko]=새 스크린샷 저장됨
Name[lt]=Ekranvaizdis įrašytas
Name[nl]=Nieuwe schermafdruk opgeslagen
Name[nn]=Nytt skjermbilete lagra
Name[pl]=Zapisano nowy zrzut ekranu
Name[pt]=Nova Imagem Gravada
Name[pt_BR]=Nova captura de tela salva
Name[ru]=Сохранён новый снимок экрана
Name[sk]=Nová obrazovka uložená
Name[sl]=Shranjena nova zaslonska slika
Name[sv]=Ny skärmbild sparad
Name[uk]=Збереженого нових знімок
Name[x-test]=xxNew Screenshot Savedxx
Name[zh_CN]=新屏幕截图已保存
Comment=A new screenshot was captured and saved
Comment[ar]=أُخذت لقطة جديدة وحُفظت
Comment[ast]=Capturóse y guardóse una captura de pantalla nueva
Comment[ca]=S'ha efectuat una captura de pantalla nova i s'ha desat
Comment[ca@valencia]=S'ha efectuat una captura de pantalla nova i s'ha guardat
Comment[de]=Ein neues Bildschirmfoto wurde aufgenommen und gespeichert
+Comment[el]=Λήφθηκε και αποθηκεύτηκε ένα νέο στιγμιότυπο
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[it]=Una nuova schermata è stata catturata e salvata
Comment[ko]=새 스크린샷을 캡처하여 저장함
Comment[lt]=Ekranas nupaveiksluotas ir įrašytas
Comment[nl]=Een nieuwe schermafdruk is opgenomen en opgeslagen
Comment[nn]=Tok og lagra nytt skjermbilete
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[ru]=Новый снимок экрана создан и сохранён.
Comment[sk]=Nová obrazovka bola zachytená a uložená
Comment[sl]=Zajeta in shranjena je bila nova zaslonska slika
Comment[sv]=En ny skärmbild togs och sparades
Comment[uk]=Було створено і збережено новий знімок
Comment[x-test]=xxA new screenshot was captured and savedxx
Comment[zh_CN]=屏幕截图已截取并保存
Action=Popup
diff --git a/doc/index.docbook b/doc/index.docbook
index 40e87d6..e4dce83 100644
--- a/doc/index.docbook
+++ b/doc/index.docbook
@@ -1,278 +1,277 @@
-
BoudhayanGupta">
bgupta@kde.org">
]>
The &spectacle; Handbook
&Boudhayan.Gupta;
&Boudhayan.Gupta.mail;
&Boudhayan.Gupta;
&Boudhayan.Gupta.mail;
1997-2000&Richard.J.Moore;
2000&Matthias.Ettrich;
2015&Boudhayan.Gupta;
&FDLNotice;
2015-10-17
- 15.12.0
+ Applications 15.12
&spectacle; is a simple application for capturing desktop screenshots. It can capture images of the entire desktop, a single monitor, the currently active window, the window currently under the mouse, or a rectangular region of the screen. The images can then be printed, sent to other applications for manipulation, or quickly be saved as-is.
KDE
&spectacle;
kdegraphics
screenshot
screen capture
screen grab
Introduction
&spectacle; is a simple application for capturing desktop screenshots. It can capture images of the entire desktop, a single monitor, the currently active window, the window currently under the mouse, or a rectangular region of the screen. The images can then be printed, sent to other applications for manipulation, or quickly be saved as-is.
Please report any problems or feature requests to the &kde; Bug Tracking System.
Starting &spectacle;
&spectacle; can be started in a variety of ways, as described below:
In the application launcher menu, &spectacle; can be found at ApplicationsGraphicsScreenshot Capture Utility &spectacle;
Pressing the Print Screen button on the keyboard will immediately launch &spectacle;. Additionally, two more keyboard shortcuts are available. Pressing MetaPrint Screen will take a screenshot of the active window and save it in your default save directory without showing the GUI, while pressing &Shift;Print Screen will take a screenshot of your entire desktop and save it in your default save directory without showing the GUI.
You can configure the default save location and filename by starting &spectacle; normally, clicking the downward arrow beside the Save & Exit button, and selecting Configure Save Options from the menu.
The mini command line &krunner; (invoked with &Alt;F2) may also be used to start &spectacle;.
&spectacle; can be started from the command-line. &spectacle; has an extensive set of command-line options, including a background mode which can be used to script the capture of screenshots without showing the GUI or requiring user interaction.
To start &spectacle; from the command prompt, type in:
% spectacle &
To view the full list of command-line options and their explanation, type in:
% spectacle --help
Using &spectacle;
Once &spectacle; starts, you will see a window like the following:
&spectacle; Main Window
&spectacle; grabs an image of your entire desktop immediately after it is started, but before it displays itself on screen. This allows you to quickly create full-desktop screenshot images.
The snapshot taken by &spectacle; is displayed in the preview window, which is located on the left-hand side of the &spectacle; application window.
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 a New Screenshot (&Ctrl;N) button.
You may configure certain options on the right hand side of the application window before taking a new screenshot. These options allow you to select the area of the screen that is to be captured, set a delay before capturing the image, and configure whether the mouse cursor and/or the window decorations should be captured along with the screenshot.
Capture Mode
The capture mode settings allow you to set the area of the screen that should be captured, and whether there should be a delay between pressing the Take a New Screenshot (&Ctrl;N) button and taking the screenshot. You may also enable the On Click checkbox, which disables the delay function and only takes the screenshot after you click anywhere on the screen after clicking the Take a New Screenshot (&Ctrl;N) button.
The Area combo-box allows you to set the area of the screen that should be captured. There are five options to select from, as described below.
The Full Screen (All Monitors) option takes a screenshot of your entire desktop, spread across all the outputs, including all the monitors, projectors etc.
The Current Screen option takes a screenshot of the output that currently contains the mouse pointer.
The Active Window option takes a screenshot of the window that currently has focus. It is advisable to use a delay with this mode, to give you time to select and activate a window before the screenshot is taken.
The Window Under Cursor option takes a screenshot of the window that is under the mouse cursor. If the cursor is on top of a 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, &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 a 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 a New Screenshot (&Ctrl;N) button hides the &spectacle; window and changes the mouse cursor to a crosshair. The screenshot is captured when the mouse is left-clicked, or aborted if any other mouse buttons are clicked. Note that you cannot interact with the desktop using the mouse while the cursor is a crosshair, but you can use the keyboard.
Content Options
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 &spectacle; window. Their functions are described below:
Help
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, &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/src/Config.h.in b/src/Config.h.in
index 158ca57..cca2812 100644
--- a/src/Config.h.in
+++ b/src/Config.h.in
@@ -1,19 +1,19 @@
#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
/* Define to 1 if we have Purpose */
#cmakedefine PURPOSE_FOUND 1
/* Set the Spectacle version from CMake */
#cmakedefine SPECTACLE_VERSION "@SPECTACLE_VERSION@"
/* set the release codename */
-#define SPECTACLE_CODENAME "So Much For Subtlety"
+#define SPECTACLE_CODENAME "Sorry For Any Inconvenience"
#endif
diff --git a/src/ExportManager.cpp b/src/ExportManager.cpp
index d703f22..763b1f3 100644
--- a/src/ExportManager.cpp
+++ b/src/ExportManager.cpp
@@ -1,363 +1,366 @@
/*
* Copyright (C) 2015 Boudhayan Gupta
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "ExportManager.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "SpectacleConfig.h"
ExportManager::ExportManager(QObject *parent) :
QObject(parent),
mSavePixmap(QPixmap()),
mTempFile(QUrl())
{}
ExportManager::~ExportManager()
{}
ExportManager* ExportManager::instance()
{
static ExportManager instance;
return &instance;
}
// screenshot pixmap setter and getter
QPixmap ExportManager::pixmap() const
{
return mSavePixmap;
}
QString ExportManager::pixmapDataUri() const
{
QImage image = mSavePixmap.toImage();
QByteArray imageData;
// write the image into the QByteArray using a QBuffer
{
QBuffer dataBuf(&imageData);
dataBuf.open(QBuffer::WriteOnly);
image.save(&dataBuf, "PNG");
}
// compose the data uri and return it
QString uri = QStringLiteral("data:image/png;base64,") + QString::fromLatin1(imageData.toBase64());
return uri;
}
void ExportManager::setPixmap(const QPixmap &pixmap)
{
mSavePixmap = pixmap;
// reset our saved tempfile
if (mTempFile.isValid()) {
QFile file(mTempFile.toLocalFile());
file.remove();
mTempFile = QUrl();
}
}
// native file save helpers
QString ExportManager::saveLocation() const
{
KSharedConfigPtr config = KSharedConfig::openConfig(QStringLiteral("spectaclerc"));
KConfigGroup generalConfig = KConfigGroup(config, "General");
QString savePath = generalConfig.readPathEntry(
"default-save-location", QStandardPaths::writableLocation(QStandardPaths::PicturesLocation));
if (savePath.isEmpty() || savePath.isNull()) {
savePath = QDir::homePath();
}
savePath = QDir::cleanPath(savePath);
QDir savePathDir(savePath);
if (!(savePathDir.exists())) {
savePathDir.mkpath(QStringLiteral("."));
generalConfig.writePathEntry("last-saved-to", savePath);
}
return savePath;
}
void ExportManager::setSaveLocation(const QString &savePath)
{
KSharedConfigPtr config = KSharedConfig::openConfig(QStringLiteral("spectaclerc"));
KConfigGroup generalConfig = KConfigGroup(config, "General");
generalConfig.writePathEntry("last-saved-to", savePath);
}
QUrl ExportManager::getAutosaveFilename()
{
const QString baseDir = saveLocation();
const QDir baseDirPath(baseDir);
const QString filename = makeAutosaveFilename();
const QString fullpath = autoIncrementFilename(baseDirPath.filePath(filename), QStringLiteral("png"));
const QUrl fileNameUrl = QUrl::fromUserInput(fullpath);
if (fileNameUrl.isValid()) {
return fileNameUrl;
} else {
return QUrl();
}
}
QString ExportManager::makeAutosaveFilename()
{
KSharedConfigPtr config = KSharedConfig::openConfig(QStringLiteral("spectaclerc"));
KConfigGroup generalConfig = KConfigGroup(config, "General");
const QDateTime timestamp = QDateTime::currentDateTime();
QString baseName = generalConfig.readEntry("save-filename-format", "Screenshot_%Y%M%D_%H%m%S");
return baseName.replace(QLatin1String("%Y"), timestamp.toString(QStringLiteral("yyyy")))
.replace(QLatin1String("%y"), timestamp.toString(QStringLiteral("yy")))
.replace(QLatin1String("%M"), timestamp.toString(QStringLiteral("MM")))
.replace(QLatin1String("%D"), timestamp.toString(QStringLiteral("dd")))
.replace(QLatin1String("%H"), timestamp.toString(QStringLiteral("hh")))
.replace(QLatin1String("%m"), timestamp.toString(QStringLiteral("mm")))
.replace(QLatin1String("%S"), timestamp.toString(QStringLiteral("ss")));
}
QString ExportManager::autoIncrementFilename(const QString &baseName, const QString &extension)
{
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-" + QString::number(qrand() % 10000));
}
QString ExportManager::makeSaveMimetype(const QUrl &url)
{
QMimeDatabase mimedb;
QString type = mimedb.mimeTypeForUrl(url).preferredSuffix();
if (type.isEmpty()) {
return QStringLiteral("png");
}
return type;
}
bool ExportManager::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(mSavePixmap.toImage());
}
bool ExportManager::localSave(const QUrl &url, const QString &mimetype)
{
QFile outputFile(url.toLocalFile());
outputFile.open(QFile::WriteOnly);
if(!writeImage(&outputFile, mimetype.toLatin1())) {
emit errorMessage(i18n("Cannot save screenshot. Error while writing file."));
return false;
}
return true;
}
bool ExportManager::remoteSave(const QUrl &url, const QString &mimetype)
{
QTemporaryFile tmpFile;
if (tmpFile.open()) {
if(!writeImage(&tmpFile, mimetype.toLatin1())) {
emit errorMessage(i18n("Cannot save screenshot. Error while writing temporary local file."));
return false;
}
KIO::FileCopyJob *uploadJob = KIO::file_copy(QUrl::fromLocalFile(tmpFile.fileName()), url);
uploadJob->exec();
if (uploadJob->error() != KJob::NoError) {
emit errorMessage(i18n("Unable to save image. Could not upload file to remote location."));
return false;
}
return true;
}
return false;
}
QUrl ExportManager::tempSave(const QString &mimetype)
{
// if we already have a temp file saved, use that
if (mTempFile.isValid()) {
if (QFile(mTempFile.toLocalFile()).exists()) {
return mTempFile;
}
}
QTemporaryFile tmpFile(QDir::tempPath() + QDir::separator() + "Spectacle.XXXXXX." + mimetype);
tmpFile.setAutoRemove(false);
tmpFile.setPermissions(QFile::ReadUser | QFile::WriteUser);
if (tmpFile.open()) {
if(!writeImage(&tmpFile, mimetype.toLatin1())) {
emit errorMessage(i18n("Cannot save screenshot. Error while writing temporary local file."));
return QUrl();
}
mTempFile = QUrl::fromLocalFile(tmpFile.fileName());
return mTempFile;
}
return QUrl();
}
bool ExportManager::save(const QUrl &url)
{
if (!(url.isValid())) {
emit errorMessage(i18n("Cannot save screenshot. The save filename is invalid."));
return false;
}
QString mimetype = makeSaveMimetype(url);
if (url.isLocalFile()) {
return localSave(url, mimetype);
}
return remoteSave(url, mimetype);
}
bool ExportManager::isFileExists(const QUrl &url)
{
if (!(url.isValid())) {
return false;
}
KIO::StatJob * existsJob = KIO::stat(url, KIO::StatJob::DestinationSide, 0);
existsJob->exec();
return (existsJob->error() == KJob::NoError);
}
// save slots
-void ExportManager::doSave(const QUrl &url)
+void ExportManager::doSave(const QUrl &url, bool notify)
{
if (mSavePixmap.isNull()) {
emit errorMessage(i18n("Cannot save an empty screenshot image."));
return;
}
QUrl savePath = url.isValid() ? url : getAutosaveFilename();
if (save(savePath)) {
QDir dir(savePath.path());
dir.cdUp();
setSaveLocation(dir.absolutePath());
emit imageSaved(savePath);
+ if (notify) {
+ emit forceNotify(savePath);
+ }
}
}
void ExportManager::doSaveAs(QWidget *parentWindow)
{
QStringList supportedFilters;
QMimeDatabase db;
SpectacleConfig *config = SpectacleConfig::instance();
// construct the supported mimetype list
Q_FOREACH (auto mimeType, QImageWriter::supportedMimeTypes()) {
supportedFilters.append(QString::fromUtf8(mimeType).trimmed());
}
// construct the file name
QFileDialog dialog(parentWindow);
dialog.setOption(QFileDialog::DontUseNativeDialog);
dialog.setAcceptMode(QFileDialog::AcceptSave);
dialog.setFileMode(QFileDialog::AnyFile);
dialog.setDirectoryUrl(config->lastSaveAsLocation());
dialog.selectFile(makeAutosaveFilename() + QStringLiteral(".png"));
dialog.setDefaultSuffix(QStringLiteral(".png"));
dialog.setMimeTypeFilters(supportedFilters);
dialog.selectMimeTypeFilter(QStringLiteral("image/png"));
// launch the dialog
if (dialog.exec() == QFileDialog::Accepted) {
const QUrl saveUrl = dialog.selectedUrls().first();
if (saveUrl.isValid()) {
if (save(saveUrl)) {
emit imageSaved(saveUrl);
config->setLastSaveAsLocation(saveUrl.adjusted(QUrl::RemoveFilename));
}
}
}
}
// misc helpers
void ExportManager::doCopyToClipboard()
{
QApplication::clipboard()->setPixmap(mSavePixmap, QClipboard::Clipboard);
}
void ExportManager::doPrint(QPrinter *printer)
{
QPainter painter;
if (!(painter.begin(printer))) {
emit errorMessage(i18n("Printing failed. The printer failed to initialize."));
delete printer;
return;
}
QRect devRect(0, 0, printer->width(), printer->height());
QPixmap pixmap = mSavePixmap.scaled(devRect.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
QRect srcRect = pixmap.rect();
srcRect.moveCenter(devRect.center());
painter.drawPixmap(srcRect.topLeft(), pixmap);
painter.end();
delete printer;
return;
}
diff --git a/src/ExportManager.h b/src/ExportManager.h
index b046e83..39c3bc0 100644
--- a/src/ExportManager.h
+++ b/src/ExportManager.h
@@ -1,92 +1,93 @@
/*
* Copyright (C) 2015 Boudhayan Gupta
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef EXPORTMANAGER_H
#define EXPORTMANAGER_H
#include
#include
#include
#include
#include
class ExportManager : public QObject
{
Q_OBJECT
// singleton-ize the class
public:
static ExportManager* instance();
private:
explicit ExportManager(QObject *parent = 0);
virtual ~ExportManager();
ExportManager(ExportManager const&) = delete;
void operator= (ExportManager const&) = delete;
// now the usual stuff
public:
Q_PROPERTY(QString saveLocation READ saveLocation WRITE setSaveLocation NOTIFY saveLocationChanged)
Q_PROPERTY(QPixmap pixmap READ pixmap WRITE setPixmap NOTIFY pixmapChanged)
void setSaveLocation(const QString &location);
QString saveLocation() const;
void setPixmap(const QPixmap &pixmap);
QPixmap pixmap() const;
QString pixmapDataUri() const;
signals:
void errorMessage(const QString &str);
void saveLocationChanged(const QString &location);
void pixmapChanged(const QPixmap &pixmap);
void imageSaved(const QUrl &savedAt);
+ void forceNotify(const QUrl &savedAt);
public slots:
QUrl getAutosaveFilename();
QUrl tempSave(const QString &mimetype = "png");
- void doSave(const QUrl &url = QUrl());
+ void doSave(const QUrl &url = QUrl(), bool notify = false);
void doSaveAs(QWidget *parentWindow = 0);
void doCopyToClipboard();
void doPrint(QPrinter *printer);
private:
QString makeAutosaveFilename();
QString autoIncrementFilename(const QString &baseName, const QString &extension);
QString makeSaveMimetype(const QUrl &url);
bool writeImage(QIODevice *device, const QByteArray &format);
bool save(const QUrl &url);
bool localSave(const QUrl &url, const QString &mimetype);
bool remoteSave(const QUrl &url, const QString &mimetype);
bool isFileExists(const QUrl &url);
QPixmap mSavePixmap;
QUrl mTempFile;
};
#endif // EXPORTMANAGER_H
diff --git a/src/Gui/ExportMenu.cpp b/src/Gui/ExportMenu.cpp
index 0232f4e..50333e0 100644
--- a/src/Gui/ExportMenu.cpp
+++ b/src/Gui/ExportMenu.cpp
@@ -1,191 +1,191 @@
/*
* 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 "ExportMenu.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "Config.h"
#ifdef KIPI_FOUND
#include
#include
#include "KipiInterface/KSGKipiInterface.h"
#endif
ExportMenu::ExportMenu(QWidget *parent) :
QMenu(parent),
#ifdef PURPOSE_FOUND
mPurposeMenu(new Purpose::Menu(this)),
#endif
mExportManager(ExportManager::instance())
{
QTimer::singleShot(300, this, &ExportMenu::populateMenu);
}
void ExportMenu::populateMenu()
{
#ifdef PURPOSE_FOUND
loadPurposeMenu();
#endif
#ifdef KIPI_FOUND
mKipiMenu = addMenu(i18n("More Online Services"));
mKipiMenu->addAction(i18n("Please wait..."));
mKipiMenuLoaded = false;
connect(mKipiMenu, &QMenu::aboutToShow, this, &ExportMenu::loadKipiItems);
#endif
addSeparator();
getKServiceItems();
}
void ExportMenu::imageUpdated(const QString &dataUri)
{
#ifdef PURPOSE_FOUND
mPurposeMenu->model()->setInputData(QJsonObject {
{ QStringLiteral("mimeType"), QStringLiteral("image/png") },
{ QStringLiteral("urls"), QJsonArray({ dataUri }) }
});
mPurposeMenu->model()->setPluginType("Export");
mPurposeMenu->reload();
#else
Q_UNUSED(dataUri);
#endif
}
void ExportMenu::getKServiceItems()
{
// populate all locally installed applications and services
// which can handle images first
const KService::List services = KMimeTypeTrader::self()->query(QStringLiteral("image/png"));
Q_FOREACH (auto service, services) {
QString name = service->name().replace('&', QLatin1String("&&"));
QAction *action = new QAction(QIcon::fromTheme(service->icon()), name, nullptr);
connect(action, &QAction::triggered, [=]() {
QList whereIs({ mExportManager->tempSave() });
KRun::runService(*service, whereIs, parentWidget(), true);
});
addAction(action);
}
// now let the user manually chose an application to open the
// image with
addSeparator();
QAction *openWith = new QAction(this);
openWith->setText(i18n("Other Application"));
openWith->setIcon(QIcon::fromTheme(QStringLiteral("application-x-executable")));
openWith->setShortcuts(KStandardShortcut::open());
connect(openWith, &QAction::triggered, [=]() {
QList whereIs({ mExportManager->tempSave() });
KRun::displayOpenWithDialog(whereIs, parentWidget(), true);
});
addAction(openWith);
}
#ifdef KIPI_FOUND
void ExportMenu::loadKipiItems()
{
if (!mKipiMenuLoaded) {
QTimer::singleShot(500, this, &ExportMenu::getKipiItems);
}
}
void ExportMenu::getKipiItems()
{
mKipiMenu->clear();
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) {
+ Q_FOREACH (const 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) {
+ Q_FOREACH (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) {
+ Q_FOREACH (auto action, exportActions) {
mKipiMenu->addAction(action);
}
}
mKipiMenuLoaded = true;
}
#endif
#ifdef PURPOSE_FOUND
void ExportMenu::loadPurposeMenu()
{
// attach the menu
QAction *purposeMenu = addMenu(mPurposeMenu);
purposeMenu->setText(i18n("Share"));
// set up the callback signal
connect(mPurposeMenu, &Purpose::Menu::finished, this, [this](const QJsonObject &output, int error, const QString &message) {
if (error) {
emit imageShared(true, message);
} else {
emit imageShared(false, output["url"].toString());
}
});
}
#endif
diff --git a/src/Gui/KSMainWindow.cpp b/src/Gui/KSMainWindow.cpp
index 0f3fb28..04cd5a3 100644
--- a/src/Gui/KSMainWindow.cpp
+++ b/src/Gui/KSMainWindow.cpp
@@ -1,329 +1,330 @@
/*
* Copyright (C) 2015 Boudhayan Gupta
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KSMainWindow.h"
#include "Config.h"
#include
#include
#include
#include
#include
#include
#ifdef XCB_FOUND
#include
#include
#endif
#include
#include
#include
#include
#include
#include
#include "SettingsDialog/SettingsDialog.h"
#include "ExportMenu.h"
#include "ExportManager.h"
#include "SpectacleConfig.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),
mMessageWidget(new KMessageWidget),
mExportMenu(new ExportMenu(this)),
mOnClickAvailable(onClickAvailable)
{
// before we do anything, we need to set a window property
// that skips the close/hide window animation on kwin. this
// fixes a ghost image of the spectacle window that appears
// on subsequent screenshots taken with the take new screenshot
// button
//
// credits for this goes to Thomas Lübking
#ifdef XCB_FOUND
if (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(QStringLiteral("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);
// change window title on save
connect(ExportManager::instance(), &ExportManager::imageSaved, this, &KSMainWindow::setScreenshotWindowTitle);
// the KSGWidget
connect(mKSWidget, &KSWidget::newScreenshotRequest, this, &KSMainWindow::captureScreenshot);
connect(mKSWidget, &KSWidget::dragInitiated, this, &KSMainWindow::dragAndDropRequest);
// the Button Bar
mDialogButtonBox->setStandardButtons(QDialogButtonBox::Help | QDialogButtonBox::Discard);
KGuiItem::assign(mSendToButton, KGuiItem(i18n("Export Image...")));
mSendToButton->setIcon(QIcon::fromTheme(QStringLiteral("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(QStringLiteral("edit-copy")));
mClipboardButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
mDialogButtonBox->addButton(mClipboardButton, QDialogButtonBox::ActionRole);
mSaveButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
mSaveButton->setMenu(mSaveMenu);
mSaveButton->setPopupMode(QToolButton::MenuButtonPopup);
buildSaveMenu();
mDialogButtonBox->addButton(mSaveButton, QDialogButtonBox::ActionRole);
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());
// layouts
mDivider->setFrameShape(QFrame::HLine);
mDivider->setLineWidth(2);
QVBoxLayout *layout = new QVBoxLayout(this);
layout->addWidget(mKSWidget);
layout->addWidget(mMessageWidget);
layout->addWidget(mDivider);
layout->addWidget(mDialogButtonBox);
mMessageWidget->hide();
// populate our send-to actions
mSendToButton->setMenu(mExportMenu);
connect(mExportMenu, &ExportMenu::imageShared, this, &KSMainWindow::showImageSharedFeedback);
// disable onClick mode if not available on the platform
if (!mOnClickAvailable) {
mKSWidget->disableOnClick();
}
// done with the init
}
void KSMainWindow::buildSaveMenu()
{
// first clear the menu
mSaveMenu->clear();
// get our actions in order
QAction *actionSave = KStandardAction::save(this, SLOT(save()), this);
QAction *actionSaveAs = KStandardAction::saveAs(this, SLOT(saveAs()), this);
QAction *actionSaveExit = new QAction(QIcon::fromTheme(QStringLiteral("document-save")), i18n("Save &&& Exit"), this);
actionSaveExit->setToolTip(i18n("Save screenshot in your Pictures directory and exit"));
actionSaveExit->setShortcut(QKeySequence(QKeySequence::Quit));
connect(actionSaveExit, &QAction::triggered, this, &KSMainWindow::saveAndExit);
// static or dynamic
SpectacleConfig *cfgManager = SpectacleConfig::instance();
int switchState = cfgManager->useDynamicSaveButton() ? cfgManager->lastUsedSaveMode() : 0;
// put the actions in order
switch (switchState) {
case 1:
mSaveButton->setDefaultAction(actionSave);
mSaveMenu->addAction(actionSaveExit);
mSaveMenu->addAction(actionSaveAs);
break;
case 2:
mSaveButton->setDefaultAction(actionSaveAs);
mSaveMenu->addAction(actionSaveExit);
mSaveMenu->addAction(actionSave);
break;
case 0:
default:
mSaveButton->setDefaultAction(actionSaveExit);
mSaveMenu->addAction(actionSave);
mSaveMenu->addAction(actionSaveAs);
break;
}
// finish off building the menu
mSaveMenu->addAction(KStandardAction::print(this, SLOT(showPrintDialog()), this));
mSaveMenu->addSeparator();
mSaveMenu->addAction(QIcon::fromTheme(QStringLiteral("applications-system")), i18n("Preferences"),
this, SLOT(showPreferencesDialog()));
}
// overrides
void KSMainWindow::moveEvent(QMoveEvent *event)
{
Q_UNUSED(event);
KSharedConfigPtr config = KSharedConfig::openConfig(QStringLiteral("spectaclerc"));
KConfigGroup guiConfig(config, "GuiConfig");
guiConfig.writeEntry("window-position", pos());
guiConfig.sync();
}
// slots
void KSMainWindow::captureScreenshot(ImageGrabber::GrabMode mode, int timeout, bool includePointer, bool includeDecorations)
{
hide();
emit newScreenshotRequest(mode, timeout, includePointer, includeDecorations);
}
void KSMainWindow::setScreenshotAndShow(const QPixmap &pixmap)
{
mKSWidget->setScreenshotPixmap(pixmap);
mExportMenu->imageUpdated(ExportManager::instance()->pixmapDataUri());
setWindowTitle(i18nc("Unsaved Screenshot", "Unsaved[*]"));
setWindowModified(true);
KGuiItem::assign(mDialogButtonBox->button(QDialogButtonBox::Discard), KStandardGuiItem::discard());
show();
}
void KSMainWindow::showPrintDialog()
{
QPrinter *printer = new QPrinter(QPrinter::HighResolution);
QPrintDialog printDialog(printer, this);
if (printDialog.exec() == QDialog::Accepted) {
ExportManager::instance()->doPrint(printer);
return;
}
delete printer;
}
void KSMainWindow::showImageSharedFeedback(bool error, const QString &message)
{
if (error) {
mMessageWidget->setMessageType(KMessageWidget::Error);
mMessageWidget->setText(i18n("There was a problem sharing the image: %1", message));
mMessageWidget->setIcon(QIcon::fromTheme(QStringLiteral("dialog-error")));
} else {
mMessageWidget->setMessageType(KMessageWidget::Positive);
mMessageWidget->setText(i18n("You can find the shared image at: %1", message));
mMessageWidget->setIcon(QIcon::fromTheme(QStringLiteral("dialog-ok-apply")));
}
mMessageWidget->animatedShow();
QTimer::singleShot(20000, mMessageWidget, &KMessageWidget::animatedHide);
}
void KSMainWindow::sendToClipboard()
{
ExportManager::instance()->doCopyToClipboard();
mMessageWidget->setMessageType(KMessageWidget::Information);
mMessageWidget->setText(i18n("The screenshot has been copied to the clipboard."));
mMessageWidget->setIcon(QIcon::fromTheme(QStringLiteral("dialog-information")));
mMessageWidget->animatedShow();
QTimer::singleShot(10000, mMessageWidget, &KMessageWidget::animatedHide);
}
void KSMainWindow::showPreferencesDialog()
{
SettingsDialog prefDialog(this);
prefDialog.exec();
}
void KSMainWindow::setScreenshotWindowTitle(QUrl location)
{
setWindowTitle(location.fileName());
setWindowModified(false);
KGuiItem::assign(mDialogButtonBox->button(QDialogButtonBox::Discard), KStandardGuiItem::quit());
}
void KSMainWindow::save()
{
SpectacleConfig::instance()->setLastUsedSaveMode(1);
buildSaveMenu();
ExportManager::instance()->doSave();
}
void KSMainWindow::saveAs()
{
SpectacleConfig::instance()->setLastUsedSaveMode(2);
buildSaveMenu();
ExportManager::instance()->doSaveAs(this);
}
void KSMainWindow::saveAndExit()
{
SpectacleConfig::instance()->setLastUsedSaveMode(0);
- ExportManager::instance()->doSave();
- QApplication::quit();
+ qApp->setQuitOnLastWindowClosed(false);
+ ExportManager::instance()->doSave(QUrl(), true);
+ hide();
}
diff --git a/src/Gui/KSWidget.cpp b/src/Gui/KSWidget.cpp
index bc9594c..7b66d55 100644
--- a/src/Gui/KSWidget.cpp
+++ b/src/Gui/KSWidget.cpp
@@ -1,223 +1,224 @@
/*
* Copyright (C) 2015 Boudhayan Gupta
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KSWidget.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "KSImageWidget.h"
#include "SmartSpinBox.h"
#include "SpectacleConfig.h"
KSWidget::KSWidget(QWidget *parent) :
QWidget(parent)
{
// get a handle to the configuration manager
SpectacleConfig *configManager = SpectacleConfig::instance();
// we'll init the widget that holds the image first
mImageWidget = new KSImageWidget(this);
mImageWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
connect(mImageWidget, &KSImageWidget::dragInitiated, this, &KSWidget::dragInitiated);
// the capture mode options first
mCaptureModeLabel = new QLabel(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 SmartSpinBox(this);
mDelayMsec->setDecimals(1);
mDelayMsec->setSingleStep(1.0);
mDelayMsec->setMinimum(0.0);
mDelayMsec->setMaximum(999.9);
- mDelayMsec->setSuffix(i18n(" seconds"));
+ mDelayMsec->setSpecialValueText(i18n("No Delay"));
mDelayMsec->setMinimumWidth(160);
- connect(mDelayMsec, static_cast(&SmartSpinBox::valueChanged), configManager, &SpectacleConfig::setCaptureDelay);
+ connect(mDelayMsec, static_cast(&SmartSpinBox::valueChanged),
+ configManager, &SpectacleConfig::setCaptureDelay);
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, configManager, &SpectacleConfig::setOnClickChecked);
mDelayLayout = new QHBoxLayout;
mDelayLayout->addWidget(mDelayMsec);
mDelayLayout->addWidget(mCaptureOnClick);
mCaptureModeForm = new QFormLayout;
mCaptureModeForm->addRow(i18n("Area:"), mCaptureArea);
mCaptureModeForm->addRow(i18n("Delay:"), mDelayLayout);
mCaptureModeForm->setContentsMargins(24, 0, 0, 0);
// the content options (mouse pointer, window decorations)
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, configManager, &SpectacleConfig::setIncludePointerChecked);
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, configManager, &SpectacleConfig::setIncludeDecorationsChecked);
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, configManager, &SpectacleConfig::setCaptureTransientWindowOnlyChecked);
mContentOptionsForm = new QVBoxLayout;
mContentOptionsForm->addWidget(mMousePointer);
mContentOptionsForm->addWidget(mWindowDecorations);
mContentOptionsForm->addWidget(mCaptureTransientOnly);
mContentOptionsForm->setSpacing(16);
mContentOptionsForm->setContentsMargins(24, 0, 0, 0);
// the take a new screenshot button
mTakeScreenshotButton = new QPushButton(this);
mTakeScreenshotButton->setText(i18n("Take a New Screenshot"));
mTakeScreenshotButton->setIcon(QIcon::fromTheme(QStringLiteral("spectacle")));
mTakeScreenshotButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
mTakeScreenshotButton->setFocus();
connect(mTakeScreenshotButton, &QPushButton::clicked, this, &KSWidget::newScreenshotClicked);
QShortcut *shortcut = new QShortcut(QKeySequence(QKeySequence::New), mTakeScreenshotButton);
auto clickFunc = [&]() {
mTakeScreenshotButton->animateClick(100);
QTimer::singleShot(100, mTakeScreenshotButton, &QPushButton::click);
};
connect(shortcut, &QShortcut::activated, clickFunc);
// finally, finish up the layouts
mRightLayout = new QVBoxLayout;
mRightLayout->addStretch(1);
mRightLayout->addWidget(mCaptureModeLabel);
mRightLayout->addSpacing(10);
mRightLayout->addLayout(mCaptureModeForm);
mRightLayout->addStretch(1);
mRightLayout->addWidget(mContentOptionsLabel);
mRightLayout->addSpacing(10);
mRightLayout->addLayout(mContentOptionsForm);
mRightLayout->addStretch(10);
mRightLayout->addWidget(mTakeScreenshotButton, 1, Qt::AlignHCenter);
mRightLayout->setContentsMargins(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
mMousePointer->setChecked (configManager->includePointerChecked());
mWindowDecorations->setChecked (configManager->includeDecorationsChecked());
mCaptureOnClick->setChecked (configManager->onClickChecked());
mCaptureTransientOnly->setChecked (configManager->captureTransientWindowOnlyChecked());
mCaptureArea->setCurrentIndex (configManager->captureMode());
mDelayMsec->setValue (configManager->captureDelay());
// 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::onClickStateChanged(int state)
{
if (state == Qt::Checked) {
mDelayMsec->setEnabled(false);
} else if (state == Qt::Unchecked) {
mDelayMsec->setEnabled(true);
}
}
void KSWidget::captureModeChanged(int index)
{
SpectacleConfig::instance()->setCaptureMode(index);
ImageGrabber::GrabMode mode = static_cast(mCaptureArea->itemData(index).toInt());
switch (mode) {
case ImageGrabber::WindowUnderCursor:
mWindowDecorations->setEnabled(true);
mCaptureTransientOnly->setEnabled(true);
break;
case ImageGrabber::ActiveWindow:
mWindowDecorations->setEnabled(true);
mCaptureTransientOnly->setEnabled(false);
break;
default:
mWindowDecorations->setEnabled(false);
mCaptureTransientOnly->setEnabled(false);
}
}
diff --git a/src/Gui/SettingsDialog/GeneralOptionsPage.cpp b/src/Gui/SettingsDialog/GeneralOptionsPage.cpp
index ab892dc..1ffddd9 100644
--- a/src/Gui/SettingsDialog/GeneralOptionsPage.cpp
+++ b/src/Gui/SettingsDialog/GeneralOptionsPage.cpp
@@ -1,147 +1,174 @@
/*
* Copyright (C) 2015 Boudhayan Gupta
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "GeneralOptionsPage.h"
#include
#include
#include
#include
#include
#include "SpectacleConfig.h"
GeneralOptionsPage::GeneralOptionsPage(QWidget *parent) :
SettingsPage(parent)
{
// preamble and stuff
QVBoxLayout *mainLayout = new QVBoxLayout(this);
// dynamic save button
QGroupBox *dsGroup = new QGroupBox(i18n("Dynamic Save Button"));
QVBoxLayout *dsLayout = new QVBoxLayout;
dsGroup->setLayout(dsLayout);
dsGroup->setStyleSheet(QStringLiteral("QGroupBox { font-weight: bold; }"));
QLabel *dsHelpText = new QLabel;
dsHelpText->setWordWrap(true);
dsHelpText->setText(i18n("The default behavior of the save button is to Save & Exit. Enable this "
"option to change the save button to either Save, Save As or "
"Save & Exit, according to the save action you perform, every time you "
"save an image."));
dsLayout->addWidget(dsHelpText);
mUseLastSaveAction = new QCheckBox;
mUseLastSaveAction->setText(i18n("Set save button action to last used save method"));
connect(mUseLastSaveAction, &QCheckBox::toggled, this, &GeneralOptionsPage::markDirty);
QVBoxLayout *dsCLayout = new QVBoxLayout;
dsCLayout->setContentsMargins(15, 10, 0, 10);
dsCLayout->addWidget(mUseLastSaveAction);
dsLayout->addLayout(dsCLayout);
mainLayout->addWidget(dsGroup, 1);
+ // copy save path to clipboard
+
+ QGroupBox *cpGroup = new QGroupBox(i18n("Copy Save Location to Clipboard"));
+ QVBoxLayout *cpLayout = new QVBoxLayout;
+ cpGroup->setLayout(cpLayout);
+ cpGroup->setStyleSheet(QStringLiteral("QGroupBox { font-weight: bold; }"));
+
+ QLabel *cpHelpText = new QLabel;
+ cpHelpText->setWordWrap(true);
+ cpHelpText->setText(i18n("When a screenshot is saved, copy the location at which the file was saved "
+ "to the clipboard. You can then paste it anywhere that accepts text input. "
+ "Note that you must be running a clipboard manager in order to keep the path "
+ "in the clipboard after Spectacle quits."));
+ cpLayout->addWidget(cpHelpText);
+
+ mCopyPathToClipboard = new QCheckBox;
+ mCopyPathToClipboard->setText(i18n("Copy save location to the clipboard"));
+ connect(mCopyPathToClipboard, &QCheckBox::toggled, this, &GeneralOptionsPage::markDirty);
+
+ QVBoxLayout *cpCLayout = new QVBoxLayout;
+ cpCLayout->setContentsMargins(15, 10, 0, 10);
+ cpCLayout->addWidget(mCopyPathToClipboard);
+ cpLayout->addLayout(cpCLayout);
+ mainLayout->addWidget(cpGroup, 1);
+
// use a light background for the rectangular region grabber
QGroupBox *lbGroup = new QGroupBox(i18n("Light Background For Rectangular Region"));
QVBoxLayout *lbLayout = new QVBoxLayout;
lbGroup->setLayout(lbLayout);
lbGroup->setStyleSheet(QStringLiteral("QGroupBox { font-weight: bold; }"));
QLabel *lbHelpText = new QLabel;
lbHelpText->setWordWrap(true);
lbHelpText->setText(i18n("Use a light background color to mask the cropped-out area in the rectangular "
"region selector. This may make dark cursors easier to see."));
lbLayout->addWidget(lbHelpText);
mUseLightBackground = new QCheckBox;
mUseLightBackground->setText(i18n("Use light background color"));
connect(mUseLightBackground, &QCheckBox::toggled, this, &GeneralOptionsPage::markDirty);
QVBoxLayout *lbCLayout = new QVBoxLayout;
lbCLayout->setContentsMargins(15, 10, 0, 10);
lbCLayout->addWidget(mUseLightBackground);
lbLayout->addLayout(lbCLayout);
mainLayout->addWidget(lbGroup, 1);
// remember rectangular region
QGroupBox *rrGroup = new QGroupBox(i18n("Remember Rectangular Region"));
QVBoxLayout *rrLayout = new QVBoxLayout;
rrGroup->setLayout(rrLayout);
rrGroup->setStyleSheet(QStringLiteral("QGroupBox { font-weight: bold; }"));
QLabel *rrHelpText = new QLabel;
rrHelpText->setWordWrap(true);
rrHelpText->setText(i18n("By default, Spectacle does not show an initial selection when you take a "
"screenshot of a rectangular region. Enable this option to remember the last "
"selected region of the screen, and set it as the initial selection when you "
"use the rectangular region selector the next time."));
rrLayout->addWidget(rrHelpText);
mRememberRect = new QCheckBox;
mRememberRect->setText(i18n("Remember rectangular region"));
connect(mRememberRect, &QCheckBox::toggled, this, &GeneralOptionsPage::markDirty);
QVBoxLayout *rrCLayout = new QVBoxLayout;
rrCLayout->setContentsMargins(15, 10, 0, 10);
rrCLayout->addWidget(mRememberRect);
rrLayout->addLayout(rrCLayout);
mainLayout->addWidget(rrGroup, 1);
// read in the data
resetChanges();
// finish up with the main layout
mainLayout->addStretch(4);
setLayout(mainLayout);
}
void GeneralOptionsPage::markDirty(bool checked)
{
Q_UNUSED(checked);
mChangesMade = true;
}
void GeneralOptionsPage::saveChanges()
{
SpectacleConfig *cfgManager = SpectacleConfig::instance();
cfgManager->setUseDynamicSaveButton(mUseLastSaveAction->checkState() == Qt::Checked);
cfgManager->setUseLightRegionMaskColour(mUseLightBackground->checkState() == Qt::Checked);
cfgManager->setRememberLastRectangularRegion(mRememberRect->checkState() == Qt::Checked);
+ cfgManager->setCopySaveLocationToClipboard(mCopyPathToClipboard->checkState() == Qt::Checked);
mChangesMade = false;
}
void GeneralOptionsPage::resetChanges()
{
SpectacleConfig *cfgManager = SpectacleConfig::instance();
mUseLastSaveAction->setChecked(cfgManager->useDynamicSaveButton());
mUseLightBackground->setChecked(cfgManager->useLightRegionMaskColour());
mRememberRect->setChecked(cfgManager->rememberLastRectangularRegion());
+ mCopyPathToClipboard->setChecked(cfgManager->copySaveLocationToClipboard());
mChangesMade = false;
}
diff --git a/src/Gui/SettingsDialog/GeneralOptionsPage.h b/src/Gui/SettingsDialog/GeneralOptionsPage.h
index 9843cb7..343ce03 100644
--- a/src/Gui/SettingsDialog/GeneralOptionsPage.h
+++ b/src/Gui/SettingsDialog/GeneralOptionsPage.h
@@ -1,51 +1,52 @@
/*
* Copyright (C) 2015 Boudhayan Gupta
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef GENERALOPTIONSPAGE_H
#define GENERALOPTIONSPAGE_H
#include "SettingsPage.h"
class QCheckBox;
class GeneralOptionsPage : public SettingsPage
{
Q_OBJECT
public:
explicit GeneralOptionsPage(QWidget *parent = 0);
public slots:
void saveChanges() Q_DECL_OVERRIDE;
void resetChanges() Q_DECL_OVERRIDE;
private slots:
void markDirty(bool checked);
private:
QCheckBox *mUseLastSaveAction;
QCheckBox *mRememberRect;
QCheckBox *mUseLightBackground;
+ QCheckBox *mCopyPathToClipboard;
};
#endif // GENERALOPTIONSPAGE_H
diff --git a/src/Gui/SmartSpinBox.cpp b/src/Gui/SmartSpinBox.cpp
index eb801aa..4938f84 100644
--- a/src/Gui/SmartSpinBox.cpp
+++ b/src/Gui/SmartSpinBox.cpp
@@ -1,33 +1,47 @@
/*
* 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 "SmartSpinBox.h"
SmartSpinBox::SmartSpinBox(QWidget *parent) :
QDoubleSpinBox(parent)
-{}
+{
+ connect(this, static_cast(&SmartSpinBox::valueChanged),
+ this, &SmartSpinBox::suffixChangeHandler);
+}
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());
}
+
+void SmartSpinBox::suffixChangeHandler(double val)
+{
+ if (val <= 1.0) {
+ setSuffix(i18n(" second"));
+ } else {
+ setSuffix(i18n(" seconds"));
+ }
+}
diff --git a/src/Gui/SmartSpinBox.h b/src/Gui/SmartSpinBox.h
index b75e1cf..c887b0a 100644
--- a/src/Gui/SmartSpinBox.h
+++ b/src/Gui/SmartSpinBox.h
@@ -1,35 +1,39 @@
/*
* 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 SMARTSPINBOX_H
#define SMARTSPINBOX_H
#include
class SmartSpinBox : public QDoubleSpinBox
{
Q_OBJECT
public:
explicit SmartSpinBox(QWidget *parent = 0);
QString textFromValue(double val) const Q_DECL_OVERRIDE;
+
+ private slots:
+
+ void suffixChangeHandler(double val);
};
#endif // SMARTSPINBOX_H
diff --git a/src/KipiInterface/KSGKipiImageCollectionSelector.cpp b/src/KipiInterface/KSGKipiImageCollectionSelector.cpp
index 8a5786c..c6a9676 100644
--- a/src/KipiInterface/KSGKipiImageCollectionSelector.cpp
+++ b/src/KipiInterface/KSGKipiImageCollectionSelector.cpp
@@ -1,66 +1,66 @@
/*
* Copyright (C) 2015 Boudhayan Gupta
* Copyright 2010 Pau Garcia i Quiles
* based on code for Gwenview by
* Copyright 2008 Aurélien Gâteau
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU 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 General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA.
*
*/
#include "KSGKipiImageCollectionSelector.h"
KSGKipiImageCollectionSelector::KSGKipiImageCollectionSelector(KIPI::Interface *interface, QWidget *parent)
: KIPI::ImageCollectionSelector(parent),
mInterface(interface),
mListWidget(new QListWidget)
{
- for(auto collection: interface->allAlbums()) {
+ Q_FOREACH (const auto &collection, interface->allAlbums()) {
QListWidgetItem *item = new QListWidgetItem(mListWidget);
QString name = collection.name();
int imageCount = collection.images().size();
QString title = i18ncp("%1 is collection name, %2 is image count in collection",
"%1 (%2 image)", "%1 (%2 images)", name, imageCount);
item->setText(title);
item->setData(Qt::UserRole, name);
}
connect(mListWidget, &QListWidget::currentRowChanged, this, &KIPI::ImageCollectionSelector::selectionChanged);
QVBoxLayout *layout = new QVBoxLayout(this);
layout->addWidget(mListWidget);
layout->setMargin(0);
}
KSGKipiImageCollectionSelector::~KSGKipiImageCollectionSelector()
{}
QList KSGKipiImageCollectionSelector::selectedImageCollections() const
{
QListWidgetItem *item = mListWidget->currentItem();
QList selectedList;
if (item) {
QString name = item->data(Qt::UserRole).toString();
- for(auto collection: mInterface->allAlbums()) {
+ Q_FOREACH (const auto &collection, mInterface->allAlbums()) {
if (collection.name() == name) {
selectedList.append(collection);
break;
}
}
}
return selectedList;
}
diff --git a/src/Main.cpp b/src/Main.cpp
index bdfcbe7..22d29f0 100644
--- a/src/Main.cpp
+++ b/src/Main.cpp
@@ -1,162 +1,165 @@
/*
* 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 "SpectacleCore.h"
#include "SpectacleDBusAdapter.h"
+#include "ExportManager.h"
#include "Config.h"
int main(int argc, char **argv)
{
// set up the application
QApplication app(argc, argv);
app.setOrganizationDomain("kde.org");
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("spectacle");
KAboutData aboutData("spectacle",
i18n("Spectacle"),
QStringLiteral(SPECTACLE_VERSION) + QStringLiteral(" - ") + QStringLiteral(SPECTACLE_CODENAME),
i18n("KDE Screenshot Utility"),
KAboutLicense::GPL_V2,
i18n("(C) 2015 Boudhayan Gupta"));
aboutData.addAuthor("Boudhayan Gupta", QString(), "bgupta@kde.org");
aboutData.setTranslator(i18nc("NAME OF TRANSLATORS", "Your names"), i18nc("EMAIL OF TRANSLATORS", "Your emails"));
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")},
{{"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")},
{{"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 or dbus mode?
SpectacleCore::StartMode startMode = SpectacleCore::GuiMode;
bool notify = true;
qint64 delayMsec = 0;
QString fileName = QString();
if (parser.isSet("background")) {
startMode = SpectacleCore::BackgroundMode;
} else if (parser.isSet("dbus")) {
startMode = SpectacleCore::DBusMode;
}
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;
}
case SpectacleCore::DBusMode:
app.setQuitOnLastWindowClosed(false);
case SpectacleCore::GuiMode:
break;
}
// release the kraken
SpectacleCore core(startMode, grabMode, fileName, delayMsec, 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, &SpectacleCore::imageSaved, dbusAdapter, &SpectacleDBusAdapter::ScreenshotTaken);
QObject::connect(&core, &SpectacleCore::grabFailed, dbusAdapter, &SpectacleDBusAdapter::ScreenshotFailed);
+ QObject::connect(ExportManager::instance(), &ExportManager::imageSaved, [&](const QUrl savedAt) {
+ emit dbusAdapter->ScreenshotTaken(savedAt.toLocalFile());
+ });
QDBusConnection::sessionBus().registerObject("/", &core);
QDBusConnection::sessionBus().registerService("org.kde.Spectacle");
// fire it up
return app.exec();
}
diff --git a/src/PlatformBackends/X11ImageGrabber.cpp b/src/PlatformBackends/X11ImageGrabber.cpp
index 61fce53..ecc3e3a 100644
--- a/src/PlatformBackends/X11ImageGrabber.cpp
+++ b/src/PlatformBackends/X11ImageGrabber.cpp
@@ -1,710 +1,705 @@
/*
* 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"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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) {
+ Q_FOREACH (const QByteArray &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 30:
+ format = QImage::Format_BGR30;
+ break;
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);
+ const qreal dpr = pixmap.devicePixelRatio();
+
+ // cursor position operates on application's device pixel ratio, not the pixmap!
+ QPoint cursorPos = QCursor::pos() / dpr * qApp->devicePixelRatio();
+ QRect screenRect(x / dpr, y / dpr, width / dpr, height / dpr);
- if (!(screenRect.contains(cursorPos))) {
+ 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);
+ cursorImage.setDevicePixelRatio(dpr);
// a small fix for the cursor position for fancier cursors
- cursorPos -= QPoint(cursorReply->xhot, cursorReply->yhot);
+ cursorPos -= QPoint(cursorReply->xhot, cursorReply->yhot) / dpr;
// now we translate the cursor point to our screen rectangle
- cursorPos -= QPoint(x, y);
+ cursorPos -= QPoint(x, y) / dpr;
// 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(QStringLiteral("org.kde.KWin"))) {
QDBusInterface interface(QStringLiteral("org.kde.KWin"), QStringLiteral("/Effects"), QStringLiteral("org.kde.kwin.Effects"));
QDBusReply reply = interface.call(QStringLiteral("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(QStringLiteral("org.kde.KWin"),
QStringLiteral("/Screenshot"),
QStringLiteral("org.kde.kwin.Screenshot"),
QStringLiteral("screenshotCreated"),
this, SLOT(KWinDBusScreenshotHelper(quint64)));
QDBusInterface interface(QStringLiteral("org.kde.KWin"), QStringLiteral("/Screenshot"), QStringLiteral("org.kde.kwin.Screenshot"));
int mask = 1;
if (mCapturePointer) {
mask |= 1 << 1;
}
interface.call(QStringLiteral("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(QStringLiteral("org.kde.KWin"),
QStringLiteral("/Screenshot"),
QStringLiteral("org.kde.kwin.Screenshot"),
QStringLiteral("screenshotCreated"),
this, SLOT(KWinDBusScreenshotHelper(quint64)));
QDBusInterface interface(QStringLiteral("org.kde.KWin"), QStringLiteral("/Screenshot"), QStringLiteral("org.kde.kwin.Screenshot"));
int mask = 1;
if (mCapturePointer) {
mask |= 1 << 1;
}
interface.call(QStringLiteral("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()
{
QuickEditor *editor = new QuickEditor(getWindowPixmap(QX11Info::appRootWindow(), false));
connect(editor, &QuickEditor::grabDone, this, &X11ImageGrabber::rectangleSelectionConfirmed);
connect(editor, &QuickEditor::grabCancelled, 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/QuickEditor/EditorRoot.qml b/src/QuickEditor/EditorRoot.qml
index 428ba1a..f7fb33b 100644
--- a/src/QuickEditor/EditorRoot.qml
+++ b/src/QuickEditor/EditorRoot.qml
@@ -1,260 +1,260 @@
/*
* Copyright (C) 2016 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.
*/
import QtQuick 2.5
import QtQuick.Window 2.2
Item {
id: editorRoot;
objectName: "editorRoot";
// properties and setters
property var selection: undefined;
property color maskColour: Qt.rgba(0, 0, 0, 0.75);
property color strokeColour: Qt.rgba(0.114, 0.6, 0.953, 1);
function setInitialSelection(xx, yy, ww, hh) {
if (selection) {
selection.destroy();
}
selection = cropRectangle.createObject(parent, {
- "x": xx,
- "y": yy,
+ "x": xx,
+ "y": yy,
"height": hh,
- "width": ww
+ "width": ww
});
cropDisplayCanvas.requestPaint();
}
// key handlers
focus: true;
Keys.onReturnPressed: {
if (selection) {
acceptImage(selection.x, selection.y, selection.width, selection.height);
} else {
acceptImage(-1, -1, -1, -1);
}
}
Keys.onEscapePressed: {
cancelImage();
}
// signals
signal acceptImage(int x, int y, int width, int height);
signal cancelImage();
Image {
id: imageBackground;
objectName: "imageBackground";
source: "image://snapshot/rawimage";
cache: false;
- height: Screen.height;
- width: Screen.width;
+ height: Window.height / Screen.devicePixelRatio;
+ width: Window.width / Screen.devicePixelRatio;
fillMode: Image.PreserveAspectFit;
}
Canvas {
id: cropDisplayCanvas;
objectName: "cropDisplayCanvas";
anchors.fill: imageBackground;
renderTarget: Canvas.FramebufferObject;
renderStrategy: Canvas.Cooperative;
onPaint: {
// start by getting a context on the canvas and clearing it
var ctx = cropDisplayCanvas.getContext("2d");
ctx.clearRect(0, 0, cropDisplayCanvas.width, cropDisplayCanvas.height);
// set up the colours
ctx.strokeStyle = strokeColour;
ctx.fillStyle = maskColour;
// draw a sheet over the whole screen
ctx.fillRect(0, 0, cropDisplayCanvas.width, cropDisplayCanvas.height);
if (selection) {
midHelpText.visible = false;
bottomHelpText.visible = true;
// if we have a selection polygon, cut it out
ctx.fillStyle = strokeColour;
ctx.fillRect(selection.x, selection.y, selection.width, selection.height);
ctx.clearRect(selection.x + 1, selection.y + 1, selection.width - 2, selection.height - 2);
if ((selection.width > 20) && (selection.height > 20)) {
// top-left handle
ctx.beginPath();
ctx.arc(selection.x, selection.y, 8, 0, 0.5 * Math.PI);
ctx.lineTo(selection.x, selection.y);
ctx.fill();
// top-right handle
ctx.beginPath();
ctx.arc(selection.x + selection.width, selection.y, 8, 0.5 * Math.PI, Math.PI);
ctx.lineTo(selection.x + selection.width, selection.y);
ctx.fill();
// bottom-left handle
ctx.beginPath();
ctx.arc(selection.x + selection.width, selection.y + selection.height, 8, Math.PI, 1.5 * Math.PI);
ctx.lineTo(selection.x + selection.width, selection.y + selection.height);
ctx.fill();
// bottom-right handle
ctx.beginPath();
ctx.arc(selection.x, selection.y + selection.height, 8, 1.5 * Math.PI, 2 * Math.PI);
ctx.lineTo(selection.x, selection.y + selection.height);
ctx.fill();
// top-center handle
ctx.beginPath();
ctx.arc(selection.x + selection.width / 2, selection.y, 5, 0, Math.PI);
ctx.fill();
// right-center handle
ctx.beginPath();
ctx.arc(selection.x + selection.width, selection.y + selection.height / 2, 5, 0.5 * Math.PI, 1.5 * Math.PI);
ctx.fill();
// bottom-center handle
ctx.beginPath();
ctx.arc(selection.x + selection.width / 2, selection.y + selection.height, 5, Math.PI, 2 * Math.PI);
ctx.fill();
// left-center handle
ctx.beginPath();
ctx.arc(selection.x, selection.y + selection.height / 2, 5, 1.5 * Math.PI, 0.5 * Math.PI);
ctx.fill();
}
} else {
midHelpText.visible = true;
bottomHelpText.visible = false;
}
}
Rectangle {
id: midHelpText;
objectName: "midHelpText";
height: midHelpTextElement.height + 40;
width: midHelpTextElement.width + 40;
radius: 10;
border.width: 2;
border.color: Qt.rgba(0, 0, 0, 1);
color: Qt.rgba(1, 1, 1, 0.85);
anchors.centerIn: parent;
Text {
id: midHelpTextElement;
text: i18n("Click anywhere on the screen (including here) to start drawing a selection rectangle, or press Esc to quit");
font.pointSize: 12;
anchors.centerIn: parent;
}
}
Rectangle {
id: bottomHelpText;
objectName: "bottomHelpText";
height: bottomHelpTextElement.height + 16;
width: bottomHelpTextElement.width + 24;
border.width: 1;
border.color: Qt.rgba(0, 0, 0, 1);
color: Qt.rgba(1, 1, 1, 0.85);
anchors.bottom: parent.bottom;
anchors.horizontalCenter: parent.horizontalCenter;
Text {
id: bottomHelpTextElement;
text: i18n("To take the screenshot, double-click or press Enter. Right-click to reset the selection, or press Esc to quit");
font.pointSize: 9;
anchors.centerIn: parent;
}
}
}
MouseArea {
anchors.fill: imageBackground;
property int startx: 0;
property int starty: 0;
cursorShape: Qt.CrossCursor;
acceptedButtons: Qt.LeftButton | Qt.RightButton;
onPressed: {
if (selection) {
selection.destroy();
}
startx = mouse.x;
starty = mouse.y;
selection = cropRectangle.createObject(parent, {
"x": startx,
"y": starty,
"height": 0,
"width": 0
});
}
onPositionChanged: {
selection.x = Math.min(startx, mouse.x);
selection.y = Math.min(starty, mouse.y);
selection.width = Math.abs(startx - mouse.x);
selection.height = Math.abs(starty - mouse.y);
cropDisplayCanvas.requestPaint();
}
onClicked: {
if ((mouse.button == Qt.RightButton) && (selection)) {
selection.destroy();
cropDisplayCanvas.requestPaint();
}
}
}
Component {
id: cropRectangle;
SelectionRectangle {
drawCanvas: cropDisplayCanvas;
imageElement: imageBackground;
onDoubleClicked: {
editorRoot.acceptImage(selection.x, selection.y, selection.width, selection.height);
}
}
}
}
diff --git a/src/QuickEditor/QuickEditor.cpp b/src/QuickEditor/QuickEditor.cpp
index eb73c06..a9bb72f 100644
--- a/src/QuickEditor/QuickEditor.cpp
+++ b/src/QuickEditor/QuickEditor.cpp
@@ -1,157 +1,143 @@
/*
* Copyright (C) 2016 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 "QuickEditor.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "SpectacleConfig.h"
struct QuickEditor::ImageStore : public QQuickImageProvider
{
ImageStore(const QPixmap &pixmap) :
QQuickImageProvider(QQuickImageProvider::Pixmap),
mPixmap(pixmap)
{}
QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize)
{
Q_UNUSED(id);
if (size) {
*size = mPixmap.size();
}
if (requestedSize.isEmpty()) {
return mPixmap;
}
return mPixmap.scaled(requestedSize, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
}
QPixmap mPixmap;
};
struct QuickEditor::QuickEditorPrivate
{
KDeclarative::KDeclarative *mDecl;
QQuickView *mQuickView;
QQmlEngine *mQmlEngine;
QRect mGrabRect;
QSharedPointer mCurrentGrabResult;
};
QuickEditor::QuickEditor(const QPixmap &pixmap, QObject *parent) :
QObject(parent),
mImageStore(new ImageStore(pixmap)),
d_ptr(new QuickEditorPrivate)
{
Q_D(QuickEditor);
d->mQmlEngine = new QQmlEngine();
d->mDecl = new KDeclarative::KDeclarative;
d->mDecl->setDeclarativeEngine(d->mQmlEngine);
d->mDecl->setupBindings();
d->mQmlEngine->addImageProvider(QStringLiteral("snapshot"), mImageStore);
d->mQuickView = new QQuickView(d->mQmlEngine, 0);
d->mQuickView->setSource(QUrl("qrc:///QuickEditor/EditorRoot.qml"));
+
+ d->mQuickView->setFlags(Qt::BypassWindowManagerHint | Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::Tool);
+ d->mQuickView->setGeometry(0, 0, pixmap.width(), pixmap.height());
d->mQuickView->showFullScreen();
// connect up the signals
QQuickItem *rootItem = d->mQuickView->rootObject();
connect(rootItem, SIGNAL(acceptImage(int, int, int, int)), this, SLOT(acceptImageHandler(int, int, int, int)));
connect(rootItem, SIGNAL(cancelImage()), this, SIGNAL(grabCancelled()));
// set up initial config
SpectacleConfig *config = SpectacleConfig::instance();
if (config->rememberLastRectangularRegion()) {
QRect cropRegion = config->cropRegion();
if (!cropRegion.isEmpty()) {
QMetaObject::invokeMethod(
rootItem, "setInitialSelection",
Q_ARG(QVariant, cropRegion.x()),
Q_ARG(QVariant, cropRegion.y()),
Q_ARG(QVariant, cropRegion.width()),
Q_ARG(QVariant, cropRegion.height())
);
}
}
if (config->useLightRegionMaskColour()) {
rootItem->setProperty("maskColour", QColor(255, 255, 255, 192));
rootItem->setProperty("strokeColour", QColor(96, 96, 96, 255));
}
}
QuickEditor::~QuickEditor()
{
Q_D(QuickEditor);
delete d->mQuickView;
delete d->mDecl;
delete d->mQmlEngine;
delete d_ptr;
}
void QuickEditor::acceptImageHandler(int x, int y, int width, int height)
{
Q_D(QuickEditor);
if ((x == -1) && (y == -1) && (width == -1) && (height == -1)) {
SpectacleConfig::instance()->setCropRegion(QRect());
emit grabCancelled();
return;
}
d->mGrabRect = QRect(x, y, width, height);
SpectacleConfig::instance()->setCropRegion(d->mGrabRect);
- QQuickItem *target = d->mQuickView->rootObject()->findChild(QStringLiteral("imageBackground"));
- d->mCurrentGrabResult = target->grabToImage();
- if (d->mCurrentGrabResult.isNull()) {
- emit grabCancelled();
- return;
- }
-
- connect(d->mCurrentGrabResult.data(), &QQuickItemGrabResult::ready, this, &QuickEditor::grabReadyHandler);
-}
-
-void QuickEditor::grabReadyHandler()
-{
- Q_D(QuickEditor);
-
- QImage croppedImage = d->mCurrentGrabResult->image().copy(d->mGrabRect);
- QPixmap croppedPixmap = QPixmap::fromImage(croppedImage);
-
d->mQuickView->hide();
- emit grabDone(croppedPixmap, d->mGrabRect);
+ emit grabDone(mImageStore->mPixmap.copy(d->mGrabRect), d->mGrabRect);
}
diff --git a/src/QuickEditor/QuickEditor.h b/src/QuickEditor/QuickEditor.h
index cadd5bb..5cca825 100644
--- a/src/QuickEditor/QuickEditor.h
+++ b/src/QuickEditor/QuickEditor.h
@@ -1,54 +1,53 @@
/*
* Copyright (C) 2016 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 QUICKEDITOR_H
#define QUICKEDITOR_H
#include
class QuickEditor : public QObject
{
Q_OBJECT
public:
explicit QuickEditor(const QPixmap &pixmap, QObject *parent = 0);
virtual ~QuickEditor();
signals:
void grabDone(const QPixmap &pixmap, const QRect &cropRegion);
void grabCancelled();
private slots:
void acceptImageHandler(int x, int y, int width, int height);
- void grabReadyHandler();
private:
struct ImageStore;
ImageStore *mImageStore;
struct QuickEditorPrivate;
Q_DECLARE_PRIVATE(QuickEditor);
QuickEditorPrivate *d_ptr;
};
#endif // QUICKEDITOR_H
diff --git a/src/SpectacleConfig.cpp b/src/SpectacleConfig.cpp
index 4055737..b812b00 100644
--- a/src/SpectacleConfig.cpp
+++ b/src/SpectacleConfig.cpp
@@ -1,224 +1,237 @@
/*
* Copyright (C) 2015 Boudhayan Gupta
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "SpectacleConfig.h"
#include
SpectacleConfig::SpectacleConfig(QObject *parent) :
QObject(parent)
{
mConfig = KSharedConfig::openConfig(QStringLiteral("spectaclerc"));
mGeneralConfig = KConfigGroup(mConfig, "General");
mGuiConfig = KConfigGroup(mConfig, "GuiConfig");
}
SpectacleConfig::~SpectacleConfig()
{}
SpectacleConfig* SpectacleConfig::instance()
{
static SpectacleConfig instance;
return &instance;
}
// lastSaveAsLocation
QUrl SpectacleConfig::lastSaveAsLocation() const
{
return mGeneralConfig.readEntry(QStringLiteral("lastSaveAsLocation"),
QUrl::fromUserInput(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)));
}
void SpectacleConfig::setLastSaveAsLocation(const QUrl &location)
{
mGeneralConfig.writeEntry(QStringLiteral("lastSaveAsLocation"), location);
mGeneralConfig.sync();
}
// cropRegion
QRect SpectacleConfig::cropRegion() const
{
return mGuiConfig.readEntry(QStringLiteral("cropRegion"), QRect());
}
void SpectacleConfig::setCropRegion(const QRect ®ion)
{
mGuiConfig.writeEntry(QStringLiteral("cropRegion"), region);
mGuiConfig.sync();
}
// onclick
bool SpectacleConfig::onClickChecked() const
{
return mGuiConfig.readEntry(QStringLiteral("onClickChecked"), false);
}
void SpectacleConfig::setOnClickChecked(bool enabled)
{
mGuiConfig.writeEntry(QStringLiteral("onClickChecked"), enabled);
mGuiConfig.sync();
}
// include pointer
bool SpectacleConfig::includePointerChecked() const
{
return mGuiConfig.readEntry(QStringLiteral("includePointer"), true);
}
void SpectacleConfig::setIncludePointerChecked(bool enabled)
{
mGuiConfig.writeEntry(QStringLiteral("includePointer"), enabled);
mGuiConfig.sync();
}
// include decorations
bool SpectacleConfig::includeDecorationsChecked() const
{
return mGuiConfig.readEntry(QStringLiteral("includeDecorations"), true);
}
void SpectacleConfig::setIncludeDecorationsChecked(bool enabled)
{
mGuiConfig.writeEntry(QStringLiteral("includeDecorations"), enabled);
mGuiConfig.sync();
}
// capture transient window only
bool SpectacleConfig::captureTransientWindowOnlyChecked() const
{
return mGuiConfig.readEntry(QStringLiteral("transientOnly"), false);
}
void SpectacleConfig::setCaptureTransientWindowOnlyChecked(bool enabled)
{
mGuiConfig.writeEntry(QStringLiteral("transientOnly"), enabled);
mGuiConfig.sync();
}
// capture delay
qreal SpectacleConfig::captureDelay() const
{
return mGuiConfig.readEntry(QStringLiteral("captureDelay"), 0.0);
}
void SpectacleConfig::setCaptureDelay(qreal delay)
{
mGuiConfig.writeEntry(QStringLiteral("captureDelay"), delay);
mGuiConfig.sync();
}
// capture mode
int SpectacleConfig::captureMode() const
{
return mGuiConfig.readEntry(QStringLiteral("captureModeIndex"), 0);
}
void SpectacleConfig::setCaptureMode(int index)
{
mGuiConfig.writeEntry(QStringLiteral("captureModeIndex"), index);
mGuiConfig.sync();
}
// dynamic save button
bool SpectacleConfig::useDynamicSaveButton() const
{
return mGuiConfig.readEntry(QStringLiteral("dynamicSaveButton"), false);
}
void SpectacleConfig::setUseDynamicSaveButton(bool enabled)
{
mGuiConfig.writeEntry(QStringLiteral("dynamicSaveButton"), enabled);
mGuiConfig.sync();
}
// remember last rectangular region
bool SpectacleConfig::rememberLastRectangularRegion() const
{
return mGuiConfig.readEntry(QStringLiteral("rememberLastRectangularRegion"), false);
}
void SpectacleConfig::setRememberLastRectangularRegion(bool enabled)
{
mGuiConfig.writeEntry(QStringLiteral("rememberLastRectangularRegion"), enabled);
mGuiConfig.sync();
}
// use light region mask colour
bool SpectacleConfig::useLightRegionMaskColour() const
{
return mGuiConfig.readEntry(QStringLiteral("useLightMaskColour"), false);
}
void SpectacleConfig::setUseLightRegionMaskColour(bool enabled)
{
mGuiConfig.writeEntry(QStringLiteral("useLightMaskColour"), enabled);
mGuiConfig.sync();
}
// last used save mode
int SpectacleConfig::lastUsedSaveMode() const
{
return mGuiConfig.readEntry(QStringLiteral("lastUsedSaveMode"), 0);
}
void SpectacleConfig::setLastUsedSaveMode(int index)
{
mGuiConfig.writeEntry(QStringLiteral("lastUsedSaveMode"), index);
mGuiConfig.sync();
}
// autosave filename format
QString SpectacleConfig::autoSaveFilenameFormat() const
{
return mGeneralConfig.readEntry(QStringLiteral("save-filename-format"),
QStringLiteral("Screenshot_%Y%M%D_%H%m%S"));
}
void SpectacleConfig::setAutoSaveFilenameFormat(const QString &format)
{
mGeneralConfig.writeEntry(QStringLiteral("save-filename-format"), format);
mGeneralConfig.sync();
}
// autosave location
QString SpectacleConfig::autoSaveLocation() const
{
return mGeneralConfig.readPathEntry(QStringLiteral("default-save-location"),
QStandardPaths::writableLocation(QStandardPaths::PicturesLocation));
}
void SpectacleConfig::setAutoSaveLocation(const QString &location)
{
mGeneralConfig.writePathEntry(QStringLiteral("default-save-location"), location);
mGeneralConfig.sync();
}
+
+// copy save location to clipboard
+
+bool SpectacleConfig::copySaveLocationToClipboard() const
+{
+ return mGeneralConfig.readEntry(QStringLiteral("copySaveLocation"), false);
+}
+
+void SpectacleConfig::setCopySaveLocationToClipboard(bool enabled)
+{
+ mGeneralConfig.writeEntry(QStringLiteral("copySaveLocation"), enabled);
+ mGeneralConfig.sync();
+}
diff --git a/src/SpectacleConfig.h b/src/SpectacleConfig.h
index b85148b..5a91f70 100644
--- a/src/SpectacleConfig.h
+++ b/src/SpectacleConfig.h
@@ -1,101 +1,104 @@
/*
* Copyright (C) 2015 Boudhayan Gupta
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef SPECTACLECONFIG_H
#define SPECTACLECONFIG_H
#include
#include
#include
#include
#include
class SpectacleConfig : public QObject
{
Q_OBJECT
// singleton-ize the class
public:
static SpectacleConfig* instance();
private:
explicit SpectacleConfig(QObject *parent = 0);
virtual ~SpectacleConfig();
SpectacleConfig(SpectacleConfig const&) = delete;
void operator= (SpectacleConfig const&) = delete;
// everything else
public slots:
QUrl lastSaveAsLocation() const;
void setLastSaveAsLocation(const QUrl &location);
QRect cropRegion() const;
void setCropRegion(const QRect ®ion);
bool onClickChecked() const;
void setOnClickChecked(bool enabled);
bool includePointerChecked() const;
void setIncludePointerChecked(bool enabled);
bool includeDecorationsChecked() const;
void setIncludeDecorationsChecked(bool enabled);
bool captureTransientWindowOnlyChecked() const;
void setCaptureTransientWindowOnlyChecked(bool enabled);
qreal captureDelay() const;
void setCaptureDelay(qreal delay);
int captureMode() const;
void setCaptureMode(int index);
bool useDynamicSaveButton() const;
void setUseDynamicSaveButton(bool enabled);
bool rememberLastRectangularRegion() const;
void setRememberLastRectangularRegion(bool enabled);
bool useLightRegionMaskColour() const;
void setUseLightRegionMaskColour(bool enabled);
int lastUsedSaveMode() const;
void setLastUsedSaveMode(int index);
QString autoSaveFilenameFormat() const;
void setAutoSaveFilenameFormat(const QString &format);
QString autoSaveLocation() const;
void setAutoSaveLocation(const QString &location);
+ bool copySaveLocationToClipboard() const;
+ void setCopySaveLocationToClipboard(bool enabled);
+
private:
KSharedConfigPtr mConfig;
KConfigGroup mGeneralConfig;
KConfigGroup mGuiConfig;
};
#endif // SPECTACLECONFIG_H
diff --git a/src/SpectacleCore.cpp b/src/SpectacleCore.cpp
index 34744dd..2a6e641 100644
--- a/src/SpectacleCore.cpp
+++ b/src/SpectacleCore.cpp
@@ -1,274 +1,299 @@
/*
* Copyright (C) 2015 Boudhayan Gupta
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "SpectacleCore.h"
-
+#include "SpectacleConfig.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
#include
+#include
+#include
+
+#include "Config.h"
+#include "PlatformBackends/DummyImageGrabber.h"
+#ifdef XCB_FOUND
+#include "PlatformBackends/X11ImageGrabber.h"
+#endif
SpectacleCore::SpectacleCore(StartMode startMode, ImageGrabber::GrabMode grabMode, QString &saveFileName,
qint64 delayMsec, bool notifyOnGrab, QObject *parent) :
QObject(parent),
mExportManager(ExportManager::instance()),
mStartMode(startMode),
mNotify(notifyOnGrab),
mImageGrabber(nullptr),
mMainWindow(nullptr),
isGuiInited(false)
{
KSharedConfigPtr config = KSharedConfig::openConfig(QStringLiteral("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(mExportManager, &ExportManager::errorMessage, this, &SpectacleCore::showErrorMessage);
connect(this, &SpectacleCore::errorMessage, this, &SpectacleCore::showErrorMessage);
connect(mImageGrabber, &ImageGrabber::pixmapChanged, this, &SpectacleCore::screenshotUpdated);
connect(mImageGrabber, &ImageGrabber::imageGrabFailed, this, &SpectacleCore::screenshotFailed);
- connect(mExportManager, &ExportManager::imageSaved, [&](const QUrl &savedAt) {
- emit imageSaved(savedAt.toLocalFile());
- });
+ connect(mExportManager, &ExportManager::imageSaved, this, &SpectacleCore::doCopyPath);
+ connect(mExportManager, &ExportManager::forceNotify, this, &SpectacleCore::doNotify);
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;
}
}
SpectacleCore::~SpectacleCore()
{
if (mMainWindow) {
delete mMainWindow;
}
}
// Q_PROPERTY stuff
QString SpectacleCore::filename() const
{
return mFileNameString;
}
void SpectacleCore::setFilename(const QString &filename)
{
mFileNameString = filename;
mFileNameUrl = QUrl::fromUserInput(filename);
}
ImageGrabber::GrabMode SpectacleCore::grabMode() const
{
return mImageGrabber->grabMode();
}
void SpectacleCore::setGrabMode(const ImageGrabber::GrabMode &grabMode)
{
mImageGrabber->setGrabMode(grabMode);
}
// Slots
void SpectacleCore::dbusStartAgent()
{
qApp->setQuitOnLastWindowClosed(true);
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 SpectacleCore::showErrorMessage(const QString &errString)
{
qDebug() << "ERROR: " << errString;
if (mStartMode == GuiMode) {
KMessageBox::error(0, errString);
}
}
void SpectacleCore::screenshotUpdated(const QPixmap &pixmap)
{
mExportManager->setPixmap(pixmap);
switch (mStartMode) {
case BackgroundMode:
case DBusMode:
{
if (mNotify) {
connect(mExportManager, &ExportManager::imageSaved, this, &SpectacleCore::doNotify);
}
QUrl savePath = (mStartMode == BackgroundMode && mFileNameUrl.isValid() && mFileNameUrl.isLocalFile()) ?
mFileNameUrl : QUrl();
mExportManager->doSave(savePath);
// if we notify, we emit allDone only if the user either dismissed the notification or pressed
// the "Open" button, otherwise the app closes before it can react to it.
if (!mNotify) {
emit allDone();
}
}
break;
case GuiMode:
mMainWindow->setScreenshotAndShow(pixmap);
}
}
void SpectacleCore::screenshotFailed()
{
switch (mStartMode) {
case BackgroundMode:
showErrorMessage(i18n("Screenshot capture canceled or failed"));
case DBusMode:
emit grabFailed();
emit allDone();
return;
case GuiMode:
mMainWindow->show();
}
}
void SpectacleCore::doNotify(const QUrl &savedAt)
{
KNotification *notify = new KNotification(QStringLiteral("newScreenshotSaved"));
switch(mImageGrabber->grabMode()) {
case ImageGrabber::GrabMode::FullScreen:
notify->setTitle(i18nc("The entire screen area was captured, heading", "Full Screen Captured"));
break;
case ImageGrabber::GrabMode::CurrentScreen:
notify->setTitle(i18nc("The current screen was captured, heading", "Current Screen Captured"));
break;
case ImageGrabber::GrabMode::ActiveWindow:
notify->setTitle(i18nc("The active window was captured, heading", "Active Window Captured"));
break;
case ImageGrabber::GrabMode::WindowUnderCursor:
notify->setTitle(i18nc("The window under the mouse was captured, heading", "Window Under Cursor Captured"));
break;
case ImageGrabber::GrabMode::RectangularRegion:
notify->setTitle(i18nc("A rectangular region was captured, heading", "Rectangular Region Captured"));
break;
default:
break;
}
const QString &path = savedAt.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).path();
// a speaking message is prettier than a URL, special case for the default pictures location
if (path == QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)) {
notify->setText(i18nc("Placeholder is filename", "A screenshot was saved as '%1' to your Pictures folder.", savedAt.fileName()));
} else {
notify->setText(i18n("A screenshot was saved as '%1' to '%2'.", savedAt.fileName(), path));
}
notify->setActions({i18nc("Open the screenshot we just saved", "Open")});
connect(notify, &KNotification::action1Activated, this, [this, savedAt] {
new KRun(savedAt, nullptr);
-
QTimer::singleShot(250, this, &SpectacleCore::allDone);
});
connect(notify, &QObject::destroyed, this, &SpectacleCore::allDone);
notify->sendEvent();
}
+void SpectacleCore::doCopyPath(const QUrl &savedAt)
+{
+ if (SpectacleConfig::instance()->copySaveLocationToClipboard()) {
+ qApp->clipboard()->setText(savedAt.toLocalFile());
+ }
+}
+
void SpectacleCore::doStartDragAndDrop()
{
QUrl tempFile = mExportManager->tempSave();
if (!tempFile.isValid()) {
return;
}
QMimeData *mimeData = new QMimeData;
mimeData->setUrls(QList { tempFile });
mimeData->setImageData(mExportManager->pixmap());
mimeData->setData(QStringLiteral("application/x-kde-suggestedfilename"), QFile::encodeName(tempFile.fileName()));
QDrag *dragHandler = new QDrag(this);
dragHandler->setMimeData(mimeData);
dragHandler->setPixmap(mExportManager->pixmap().scaled(256, 256, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation));
dragHandler->exec();
}
// Private
void SpectacleCore::initGui()
{
if (!isGuiInited) {
mMainWindow = new KSMainWindow(mImageGrabber->onClickGrabSupported());
connect(mMainWindow, &KSMainWindow::newScreenshotRequest, this, &SpectacleCore::takeNewScreenshot);
connect(mMainWindow, &KSMainWindow::dragAndDropRequest, this, &SpectacleCore::doStartDragAndDrop);
isGuiInited = true;
QMetaObject::invokeMethod(mImageGrabber, "doImageGrab", Qt::QueuedConnection);
}
}
diff --git a/src/SpectacleCore.h b/src/SpectacleCore.h
index 3a6ce51..97462c1 100644
--- a/src/SpectacleCore.h
+++ b/src/SpectacleCore.h
@@ -1,127 +1,86 @@
/*
* Copyright (C) 2015 Boudhayan Gupta
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef 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 "Config.h"
+#include
#include "ExportManager.h"
-#include "PlatformBackends/ImageGrabber.h"
-#include "PlatformBackends/DummyImageGrabber.h"
-#ifdef XCB_FOUND
-#include "PlatformBackends/X11ImageGrabber.h"
-#endif
-
#include "Gui/KSMainWindow.h"
+#include "PlatformBackends/ImageGrabber.h"
class SpectacleCore : public QObject
{
Q_OBJECT
Q_PROPERTY(QString filename READ filename WRITE setFilename NOTIFY filenameChanged)
Q_PROPERTY(ImageGrabber::GrabMode grabMode READ grabMode WRITE setGrabMode NOTIFY grabModeChanged)
public:
enum StartMode {
GuiMode = 0,
DBusMode = 1,
BackgroundMode = 2
};
explicit SpectacleCore(StartMode startMode, ImageGrabber::GrabMode grabMode, QString &saveFileName,
qint64 delayMsec, bool notifyOnGrab, QObject *parent = 0);
~SpectacleCore();
QString filename() const;
void setFilename(const QString &filename);
ImageGrabber::GrabMode grabMode() const;
void setGrabMode(const ImageGrabber::GrabMode &grabMode);
signals:
void errorMessage(const QString errString);
void allDone();
void filenameChanged(QString filename);
void grabModeChanged(ImageGrabber::GrabMode mode);
void grabFailed();
- void imageSaved(const QString &savedAt);
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 doNotify(const QUrl &savedAt);
+ void doCopyPath(const QUrl &savedAt);
private:
void initGui();
ExportManager *mExportManager;
StartMode mStartMode;
bool mNotify;
QString mFileNameString;
QUrl mFileNameUrl;
ImageGrabber *mImageGrabber;
KSMainWindow *mMainWindow;
bool isGuiInited;
};
#endif // KSCORE_H