From ba206871c233e5786134f0ac8277a1b60e38bf94 Mon Sep 17 00:00:00 2001 From: DbDibyendu <55906713+DbDibyendu@users.noreply.github.com> Date: Sat, 24 Oct 2020 01:23:05 +0530 Subject: [PATCH 01/54] Update README.md Updated the new Hotkeys (cherry picked from commit 8ebb0999a8f31024fab5357710bdd2d4af2b6c75) --- README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/README.md b/README.md index 8bbbbea4..6551a620 100644 --- a/README.md +++ b/README.md @@ -168,15 +168,31 @@ These shortcuts are available in GUI mode: | Keys | Description | |--- |--- | +| P | Set the Pencil as paint tool | +| D | Set the Line as paint tool | +| A | Set the Arrow as paint tool | +| S | Set Selection as paint tool | +| R | Set the Rectangle as paint tool | +| C | Set the Circle as paint tool | +| M | Set the Marker as paint tool | +| T | Add text to your capture | +| B | Set Pixalate as the paint tool | | , , , | Move selection 1px | | Shift + , , , | Resize selection 1px | | Esc | Quit capture | +| Ctrl + M | Move the selection area | | Ctrl + C | Copy to clipboard | | Ctrl + S | Save selection as a file | | Ctrl + Z | Undo the last modification | +| Ctrl + Shift + Z | Redo the next modification | +| Ctrl + Q | Leave the capture screen | +| Ctrl + O | Choose an app to open the capture | +| Return | Upload the selection to Imgur | | Spacebar | Toggle visibility of sidebar with options of the selected tool, color picker for the drawing color and history menu | | Right Click | Show the color wheel | | Mouse Wheel | Change the tool's thickness | +| Print screen | Capture Screen | +| Shift + Print | Screenshot History | Shift + drag a handler of the selection area: mirror redimension in the opposite handler. From 2bae14416e9ca7d5b580eaa61b91e0cac3f24853 Mon Sep 17 00:00:00 2001 From: Jeremy Borgman Date: Sun, 25 Oct 2020 21:38:53 +0200 Subject: [PATCH 02/54] always trigger CI on PR (cherry picked from commit 3acd61fdec8be0cb37faace1d6361bb98dd4d46d) --- .github/workflows/Linux-pack.yml | 2 -- .github/workflows/Windows-pack.yml | 2 -- .github/workflows/build_cmake.yml | 4 ++-- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/Linux-pack.yml b/.github/workflows/Linux-pack.yml index 8734eb9f..080cdab8 100644 --- a/.github/workflows/Linux-pack.yml +++ b/.github/workflows/Linux-pack.yml @@ -9,8 +9,6 @@ on: - 'LICENSE' pull_request: - branches: - - master paths-ignore: - 'README.md' - 'LICENSE' diff --git a/.github/workflows/Windows-pack.yml b/.github/workflows/Windows-pack.yml index aaaead8d..32972e5e 100644 --- a/.github/workflows/Windows-pack.yml +++ b/.github/workflows/Windows-pack.yml @@ -9,8 +9,6 @@ on: - 'LICENSE' pull_request: - branches: - - master* paths-ignore: - 'README.md' - 'LICENSE' diff --git a/.github/workflows/build_cmake.yml b/.github/workflows/build_cmake.yml index 279709a9..155b2b82 100644 --- a/.github/workflows/build_cmake.yml +++ b/.github/workflows/build_cmake.yml @@ -2,12 +2,12 @@ name: Building(CMake) on: push: - branches: [ master, master_nc_merge_upstream_test ] + branches: [ master ] paths-ignore: - 'README.md' - 'LICENSE' pull_request: - branches: [ master, master_nc_merge_upstream_test ] + branches: [ master ] paths-ignore: - 'README.md' - 'LICENSE' From f9f396bd693e41fb1b2eb1ca1dd474f69019ef35 Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Tue, 27 Oct 2020 16:52:26 +0200 Subject: [PATCH 03/54] fixed race condition between notifcation and clipboard (cherry-pick with manual fix) --- src/tools/storage/imgur/imguruploader.cpp | 2 +- src/tools/storage/s3/imgs3uploader.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/storage/imgur/imguruploader.cpp b/src/tools/storage/imgur/imguruploader.cpp index 565f6372..db449061 100644 --- a/src/tools/storage/imgur/imguruploader.cpp +++ b/src/tools/storage/imgur/imguruploader.cpp @@ -79,10 +79,10 @@ void ImgurUploader::handleReply(QNetworkReply* reply) resultStatus = true; if (ConfigHandler().copyAndCloseAfterUploadEnabled()) { - QApplication::clipboard()->setText(imageUrl().toString()); SystemNotification().sendMessage( QObject::tr("URL copied to clipboard.")); Controller::getInstance()->updateRecentScreenshots(); + QApplication::clipboard()->setText(imageUrl().toString()); close(); } else { onUploadOk(); diff --git a/src/tools/storage/s3/imgs3uploader.cpp b/src/tools/storage/s3/imgs3uploader.cpp index 7a7c846f..6012121a 100644 --- a/src/tools/storage/s3/imgs3uploader.cpp +++ b/src/tools/storage/s3/imgs3uploader.cpp @@ -198,9 +198,9 @@ void ImgS3Uploader::handleReplyUpload(QNetworkReply* reply) // Copy url to clipboard if required if (ConfigHandler().copyAndCloseAfterUploadEnabled()) { - QApplication::clipboard()->setText(imageUrl().toString()); SystemNotification().sendMessage(tr("URL copied to clipboard.")); Controller::getInstance()->updateRecentScreenshots(); + QApplication::clipboard()->setText(imageUrl().toString()); close(); } else { onUploadOk(); From 40f65dc252349e0c204bdb31eb06be20837a6ddc Mon Sep 17 00:00:00 2001 From: DbDibyendu <55906713+DbDibyendu@users.noreply.github.com> Date: Sat, 24 Oct 2020 01:23:05 +0530 Subject: [PATCH 04/54] Update README.md --- README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/README.md b/README.md index 6551a620..a864e2ab 100644 --- a/README.md +++ b/README.md @@ -234,6 +234,24 @@ Steps for using the configuration: 6. Now the Flameshot entry should appear in the list. Click _Apply_ to apply the changes. 7. If you want to change the defaults, you can expand the entry, select the appropriate action and modify it as you wish; the process is pretty mush self-explanatory. +### On Ubuntu (Tested on 18.04) + +Taken from [adaptation](https://askubuntu.com/posts/1039949/revisions) of [Pavel Answer on askubuntu](https://askubuntu.com/revisions/1036473/1). To use flameshot instead of default screenshot application in ubuntu we need to release the binding on Prt Sc key, and then create a new binding for `/usr/bin/flameshot gui`. + +1. Release the binding on Prt Sc using the following command. + + ```shell + gsettings set org.gnome.settings-daemon.plugins.media-keys screenshot '' + ``` + +2. Go to Settings > Device > Keyboard and press the '+' button at the bottom. + +3. Name the command as you like it, e.g. `flameshot`. And in the command insert `/usr/bin/flameshot gui`. + +4. Then click "_Set Shortcut.._" and press Prt Sc. This will show as "_print_". + +Now everytime You press Prt Sc it will start the flameshot gui instead of the default application + ## Considerations - Experimental Gnome Wayland and Plasma Wayland support. @@ -294,6 +312,7 @@ some Linux distributions: Alternatively, in case you don't want to have a systray, you can always call Flameshot from the terminal. See [Usage section](#usage). + ### S3 bucket configuration S3 bucket credentials are placed in the file `config.ini` and cannot be configured with UI. From 3c260add45f87dcb48ab57523ec7caf762476109 Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Tue, 27 Oct 2020 17:17:39 +0200 Subject: [PATCH 05/54] Update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 9b11ebb5..fb71df32 100644 --- a/.gitignore +++ b/.gitignore @@ -61,5 +61,6 @@ data/flatpak/.flatpak-builder # Jetbrains .idea/ +.run # End of https://www.gitignore.io/api/snapcraft From babbbb8f9bb465c9e16305af60eef24ff39c0801 Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Wed, 28 Oct 2020 11:48:40 +0200 Subject: [PATCH 06/54] Make preview files on the local disk for the 'Latest uploads' smaller --- src/utils/history.cpp | 13 ++++++++++++- src/utils/history.h | 3 +++ src/widgets/historywidget.cpp | 2 ++ src/widgets/historywidget.h | 3 --- 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/utils/history.cpp b/src/utils/history.cpp index 35b48567..83844a7f 100644 --- a/src/utils/history.cpp +++ b/src/utils/history.cpp @@ -30,9 +30,20 @@ const QString& History::path() void History::save(const QPixmap& pixmap, const QString& fileName) { + // scale preview only in local disk + QPixmap pixmapScaled = QPixmap(pixmap); + if (pixmap.height() / HISTORYPIXMAP_MAX_PREVIEW_HEIGHT >= + pixmap.width() / HISTORYPIXMAP_MAX_PREVIEW_WIDTH) { + pixmapScaled = pixmap.scaledToHeight(HISTORYPIXMAP_MAX_PREVIEW_HEIGHT); + } else { + pixmapScaled = pixmap.scaledToWidth(HISTORYPIXMAP_MAX_PREVIEW_WIDTH); + } + + // save preview QFile file(path() + fileName); file.open(QIODevice::WriteOnly); - pixmap.save(&file, "PNG"); + pixmapScaled.save(&file, "PNG"); + history(); } diff --git a/src/utils/history.h b/src/utils/history.h index a4569175..a0d7266b 100644 --- a/src/utils/history.h +++ b/src/utils/history.h @@ -3,6 +3,9 @@ #define HISTORY_MAX_SIZE 25 +#define HISTORYPIXMAP_MAX_PREVIEW_WIDTH 160 +#define HISTORYPIXMAP_MAX_PREVIEW_HEIGHT 90 + #include #include #include diff --git a/src/widgets/historywidget.cpp b/src/widgets/historywidget.cpp index 4a80eddf..83a0d8a5 100644 --- a/src/widgets/historywidget.cpp +++ b/src/widgets/historywidget.cpp @@ -106,6 +106,8 @@ void HistoryWidget::addLine(const QString& path, const QString& fileName) QPixmap pixmap; pixmap.load(fullFileName, "png"); + // TODO - remove much later, it is still required to keep old previews works + // fine if (pixmap.height() / HISTORYPIXMAP_MAX_PREVIEW_HEIGHT >= pixmap.width() / HISTORYPIXMAP_MAX_PREVIEW_WIDTH) { pixmap = pixmap.scaledToHeight(HISTORYPIXMAP_MAX_PREVIEW_HEIGHT); diff --git a/src/widgets/historywidget.h b/src/widgets/historywidget.h index b713f666..163342bc 100644 --- a/src/widgets/historywidget.h +++ b/src/widgets/historywidget.h @@ -1,9 +1,6 @@ #ifndef HISTORYWIDGET_H #define HISTORYWIDGET_H -#define HISTORYPIXMAP_MAX_PREVIEW_WIDTH 160 -#define HISTORYPIXMAP_MAX_PREVIEW_HEIGHT 90 - #include #include #include From 7e49d4e5eb1ffc93f028d9b8356ff0f9ad1af946 Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Wed, 28 Oct 2020 16:14:45 +0200 Subject: [PATCH 07/54] Fix - Use fixed path for screenshots to save --- src/config/geneneralconf.cpp | 9 +-------- src/utils/confighandler.cpp | 10 ---------- src/utils/confighandler.h | 3 --- src/utils/screenshotsaver.cpp | 4 ++-- 4 files changed, 3 insertions(+), 23 deletions(-) diff --git a/src/config/geneneralconf.cpp b/src/config/geneneralconf.cpp index b0fe1f88..b4f38b60 100644 --- a/src/config/geneneralconf.cpp +++ b/src/config/geneneralconf.cpp @@ -61,13 +61,6 @@ void GeneneralConf::updateComponents() m_copyAndCloseAfterUpload->setChecked( config.copyAndCloseAfterUploadEnabled()); m_saveAfterCopy->setChecked(config.saveAfterCopyValue()); - - if (!config.saveAfterCopyPathValue().isEmpty()) { - m_savePath->setText(config.saveAfterCopyPathValue()); - } else { - ConfigHandler().setSaveAfterCopyPath( - QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); - } m_copyPathAfterSave->setChecked(config.copyPathAfterSaveEnabled()); #if defined(Q_OS_LINUX) || defined(Q_OS_UNIX) @@ -376,7 +369,7 @@ void GeneneralConf::changeSavePath() QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)); if (!path.isEmpty()) { m_savePath->setText(path); - ConfigHandler().setSaveAfterCopyPath(path); + ConfigHandler().setSavePath(path); } } diff --git a/src/utils/confighandler.cpp b/src/utils/confighandler.cpp index fe0e84ec..3a48f069 100644 --- a/src/utils/confighandler.cpp +++ b/src/utils/confighandler.cpp @@ -485,16 +485,6 @@ const QString& ConfigHandler::uploadStorage() return m_strRes; } -QString ConfigHandler::saveAfterCopyPathValue() -{ - return m_settings.value(QStringLiteral("saveAfterCopyPath")).toString(); -} - -void ConfigHandler::setSaveAfterCopyPath(const QString& path) -{ - m_settings.setValue(QStringLiteral("saveAfterCopyPath"), path); -} - void ConfigHandler::setDefaults() { m_settings.clear(); diff --git a/src/utils/confighandler.h b/src/utils/confighandler.h index 0ec01ef0..b493427d 100644 --- a/src/utils/confighandler.h +++ b/src/utils/confighandler.h @@ -87,9 +87,6 @@ public: bool saveAfterCopyValue(); void setSaveAfterCopy(const bool); - QString saveAfterCopyPathValue(); - void setSaveAfterCopyPath(const QString&); - bool copyPathAfterSaveEnabled(); void setCopyPathAfterSaveEnabled(const bool); diff --git a/src/utils/screenshotsaver.cpp b/src/utils/screenshotsaver.cpp index f803987a..a19dbdd2 100644 --- a/src/utils/screenshotsaver.cpp +++ b/src/utils/screenshotsaver.cpp @@ -35,9 +35,9 @@ void ScreenshotSaver::saveToClipboard(const QPixmap& capture) // If we are able to properly save the file, save the file and copy to // clipboard. if ((ConfigHandler().saveAfterCopyValue()) && - (!ConfigHandler().saveAfterCopyPathValue().isEmpty())) { + (!ConfigHandler().savePath().isEmpty())) { saveToFilesystem(capture, - ConfigHandler().saveAfterCopyPathValue(), + ConfigHandler().savePath(), QObject::tr("Capture saved to clipboard.")); QApplication::clipboard()->setPixmap(capture); } From 138707271133cc2c3dcb6655938b4c69833cfd6c Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Wed, 28 Oct 2020 17:06:11 +0200 Subject: [PATCH 08/54] Fix - Remove localized names option for Month, day of week etc for Windows --- src/config/strftimechooserwidget.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/config/strftimechooserwidget.cpp b/src/config/strftimechooserwidget.cpp index e415a077..57de3856 100644 --- a/src/config/strftimechooserwidget.cpp +++ b/src/config/strftimechooserwidget.cpp @@ -51,22 +51,34 @@ QMap StrftimeChooserWidget::m_buttonData{ { QT_TR_NOOP("Century (00-99)"), "%C" }, { QT_TR_NOOP("Year (00-99)"), "%y" }, { QT_TR_NOOP("Year (2000)"), "%Y" }, +#ifndef Q_OS_WIN + // TODO - fix localized names on windows (ex. Cyrillic) { QT_TR_NOOP("Month Name (jan)"), "%b" }, { QT_TR_NOOP("Month Name (january)"), "%B" }, +#endif { QT_TR_NOOP("Month (01-12)"), "%m" }, { QT_TR_NOOP("Week Day (1-7)"), "%u" }, { QT_TR_NOOP("Week (01-53)"), "%V" }, +#ifndef Q_OS_WIN + // TODO - fix localized names on windows (ex. Cyrillic) { QT_TR_NOOP("Day Name (mon)"), "%a" }, { QT_TR_NOOP("Day Name (monday)"), "%A" }, +#endif { QT_TR_NOOP("Day (01-31)"), "%d" }, { QT_TR_NOOP("Day of Month (1-31)"), "%e" }, { QT_TR_NOOP("Day (001-366)"), "%j" }, +#ifndef Q_OS_WIN + // TODO - fix localized names on windows (ex. Cyrillic) { QT_TR_NOOP("Time (%H-%M-%S)"), "%T" }, { QT_TR_NOOP("Time (%H-%M)"), "%R" }, +#endif { QT_TR_NOOP("Hour (00-23)"), "%H" }, { QT_TR_NOOP("Hour (01-12)"), "%I" }, { QT_TR_NOOP("Minute (00-59)"), "%M" }, { QT_TR_NOOP("Second (00-59)"), "%S" }, +#ifndef Q_OS_WIN + // TODO - fix localized names on windows (ex. Cyrillic) { QT_TR_NOOP("Full Date (%m/%d/%y)"), "%D" }, +#endif { QT_TR_NOOP("Full Date (%Y-%m-%d)"), "%F" }, }; From 61cc8a475ce6aa018b18e2fc45e7d27c7b434014 Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Wed, 28 Oct 2020 18:01:40 +0200 Subject: [PATCH 09/54] Release 0.8.5.3 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0436273c..0e603f5a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.13) # This can be read from ${PROJECT_NAME} after project() is called project( flameshot - VERSION 0.8.5.2 + VERSION 0.8.5.3 LANGUAGES CXX) set(PROJECT_NAME_CAPITALIZED "Flameshot") From 50d5afed8ff337603f967d0e5f095daf2aa18d37 Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Fri, 6 Nov 2020 17:31:21 +0200 Subject: [PATCH 10/54] Read s3 creds for upload from URL in private network (VPN) --- data/translations/Internationalization_ca.ts | 255 +++--- data/translations/Internationalization_cs.ts | 255 +++--- .../Internationalization_de_DE.ts | 255 +++--- data/translations/Internationalization_es.ts | 255 +++--- data/translations/Internationalization_eu.ts | 255 +++--- data/translations/Internationalization_fr.ts | 255 +++--- data/translations/Internationalization_hu.ts | 28 +- .../Internationalization_it_IT.ts | 251 +++--- data/translations/Internationalization_ja.ts | 255 +++--- data/translations/Internationalization_ka.ts | 255 +++--- data/translations/Internationalization_nl.ts | 255 +++--- .../Internationalization_nl_NL.ts | 255 +++--- data/translations/Internationalization_pl.ts | 255 +++--- .../Internationalization_pt_BR.ts | 255 +++--- data/translations/Internationalization_ru.ts | 253 +++--- data/translations/Internationalization_sk.ts | 255 +++--- .../Internationalization_sr_SP.ts | 255 +++--- .../Internationalization_sv_SE.ts | 255 +++--- data/translations/Internationalization_tr.ts | 255 +++--- data/translations/Internationalization_uk.ts | 253 +++--- .../Internationalization_zh_CN.ts | 255 +++--- .../Internationalization_zh_HK.ts | 829 ++++++++++++------ .../Internationalization_zh_TW.ts | 255 +++--- src/config/uploadstorageconfig.cpp | 4 +- src/tools/storage/imguploader.cpp | 4 +- .../s3/amazon-server-side/doc/README.md | 6 +- .../storage/s3/config-examples/config.ini | 11 + .../s3/config-examples/config_proxy.ini | 16 + src/tools/storage/s3/imgs3settings.cpp | 338 ++++++- src/tools/storage/s3/imgs3settings.h | 63 +- src/tools/storage/s3/imgs3uploader.cpp | 219 ++--- src/tools/storage/s3/imgs3uploader.h | 16 +- src/tools/storage/storagemanager.cpp | 22 +- src/tools/storage/storagemanager.h | 4 +- src/utils/confighandler.cpp | 37 +- src/utils/confighandler.h | 7 + 36 files changed, 4039 insertions(+), 2912 deletions(-) create mode 100644 src/tools/storage/s3/config-examples/config.ini create mode 100644 src/tools/storage/s3/config-examples/config_proxy.ini diff --git a/data/translations/Internationalization_ca.ts b/data/translations/Internationalization_ca.ts index ff56d241..fa37337d 100644 --- a/data/translations/Internationalization_ca.ts +++ b/data/translations/Internationalization_ca.ts @@ -75,47 +75,47 @@ CaptureLauncher - + <b>Capture Mode</b> - + Rectangular Region - + Full Screen (All Monitors) - + No Delay - + second - + seconds - + Take new screenshot - + Area: - + Delay: @@ -205,22 +205,22 @@ Press Space to open the side panel. Controller - + &Open Launcher - + &Configuration &Configuració - + &About - + &Latest Uploads @@ -229,12 +229,12 @@ Press Space to open the side panel. &Informació - + &Quit &Ix - + &Take Screenshot @@ -324,170 +324,170 @@ Press Space to open the side panel. GeneneralConf - + Show help message Mostra el missatge d'ajuda - + Show the help message at the beginning in the capture mode. Mostra el missatge d'ajuda en iniciar el mode de captura. - - + + Show desktop notifications Mostra les notificacions d'escriptori - + Show tray icon Mostra la icona en la barra de tasques - + Show the systemtray icon Mostra la icona en la barra de tasques - - + + Import Importar - - - - + + + + Error Error - + Unable to read file. Impossible llegir el fitxer. - - + + Unable to write file. Impossible escriure al fitxer. - + Save File Guardar Arxiu - + Confirm Reset Confirmar Reset - + Are you sure you want to reset the configuration? Esteu segur que voleu reiniciar la configuració? - + Show the side panel button - + Show the side panel toggle button in the capture mode. - + Configuration File Fitxer de Configuració - + Export Exportar - + Reset Reset - + Launch at startup Llançament a l'inici - - + + Launch Flameshot - + Show welcome message on launch - + Close application after capture - + Close after taking a screenshot - + Copy URL after upload - + Copy URL and close window after upload - + Save image after copy - + Save image file after copying it - + Save Path - + Change... - - + + Copy file path after save - + Use fixed path for screenshots to save - + Choose a Folder - + Unable to write to directory. @@ -495,27 +495,27 @@ Press Space to open the side panel. HistoryWidget - + Latest Uploads - + Screenshots history is empty - + Copy URL Copia l'URL - + URL copied to clipboard. L'URL s'ha copiat al porta-retalls. - + Open in browser @@ -528,9 +528,14 @@ Press Space to open the side panel. - Uploading Image - S'està pujant la imatge + S'està pujant la imatge + + + + + Uploading Image... + @@ -543,40 +548,56 @@ Press Space to open the side panel. - + URL copied to clipboard. L'URL s'ha copiat al porta-retalls. - + Unable to remove screenshot from the remote storage. - + Network error - + Possibly it doesn't exist anymore - + Do you want to remove screenshot from local history anyway? - + Remove screenshot from history? - + + + Retrieving configuration file with s3 creds... + + + + S3 Creds URL is not found in your configuration file + + + Error + Error + + + + Unable to get s3 credentials, please check your VPN connection and try again + + ImgS3UploaderTool @@ -594,15 +615,8 @@ Press Space to open the side panel. - - Uploading Image - S'està pujant la imatge - - - - Upload image - + S'està pujant la imatge @@ -620,10 +634,16 @@ Press Space to open the side panel. La captura s'ha copiat al porta-retalls. + Deleting image... + + + Uploading Image... + + Copy URL @@ -635,6 +655,7 @@ Press Space to open the side panel. Obri l'URL + Delete image @@ -656,7 +677,7 @@ Press Space to open the side panel. ImgurUploader - + Upload to Imgur Puja a Imgur @@ -677,7 +698,7 @@ Press Space to open the side panel. Imatge al porta-retalls. - + Unable to open the URL. No es pot obrir l'URL. @@ -1045,7 +1066,7 @@ You may need to escape the '#' sign as in '\#FFF' - + URL copied to clipboard. L'URL s'ha copiat al porta-retalls. @@ -1086,77 +1107,77 @@ You can find me in the system tray. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. - + Toggle side panel - + Resize selection left 1px - + Resize selection right 1px - + Resize selection up 1px - + Resize selection down 1px - + Move selection left 1px - + Move selection right 1px - + Move selection up 1px - + Move selection down 1px - + Quit capture Ix de la captura - + Screenshot history - + Capture screen - + Show color picker Mostra el selector de color - + Change the tool's thickness Canvia el gruix de l'eina @@ -1242,22 +1263,22 @@ You can find me in the system tray. ShortcutsWidget - + Hot Keys - + Available shortcuts in the screen capture mode. Dreceres disponibles en el mode de captura de pantalla. - + Description Descripció - + Key Tecla @@ -1316,92 +1337,92 @@ You can find me in the system tray. Any (2000) - + Month Name (jan) Nom del mes (jul) - + Month Name (january) Nom del mes (juliol) - + Month (01-12) Mes (01-12) - + Week Day (1-7) Dia de la setmana (1-7) - + Week (01-53) Setmana (01-53) - + Day Name (mon) Nom del dia (dg) - + Day Name (monday) Nom del dia (diumenge) - + Day (01-31) Dia (01-31) - + Day of Month (1-31) Dia del mes (1-31) - + Day (001-366) Dia (001-366) - + Hour (00-23) Hora (00-23) - + Hour (01-12) Hora (01-12) - + Minute (00-59) Minut (00-59) - + Second (00-59) Segon (00-59) - + Full Date (%m/%d/%y) Data (%m/%d/%y) - + Full Date (%Y-%m-%d) Data (%Y-%m-%d) - + Time (%H-%M-%S) - + Time (%H-%M) diff --git a/data/translations/Internationalization_cs.ts b/data/translations/Internationalization_cs.ts index 2a6fb76a..36cd2633 100644 --- a/data/translations/Internationalization_cs.ts +++ b/data/translations/Internationalization_cs.ts @@ -75,47 +75,47 @@ CaptureLauncher - + <b>Capture Mode</b> <b>Režim zachytávání</b> - + Rectangular Region Pravouhlá oblast - + Full Screen (All Monitors) Celá obrazovka (všechny monitory) - + No Delay Bez zpoždění - + second sekunda - + seconds sekund - + Take new screenshot Zachytit nový snímek - + Area: Oblast: - + Delay: Zpoždění: @@ -208,27 +208,27 @@ Stiskněte mezerník pro otevření postranního panelu. Controller - + &Take Screenshot &Zachytit obrazovku - + &Open Launcher &Otevřít spouštěč - + &Configuration &Nastavení - + &About O &programu - + &Latest Uploads @@ -237,7 +237,7 @@ Stiskněte mezerník pro otevření postranního panelu. &Informace - + &Quit &Ukončit @@ -327,114 +327,114 @@ Stiskněte mezerník pro otevření postranního panelu. GeneneralConf - - + + Import Zavést - - - - + + + + Error Chyba - + Unable to read file. Nelze přečíst soubor. - - + + Unable to write file. Nelze zapsat soubor. - + Save File Uložit soubor - + Confirm Reset Potvrdit vrácení na výchozí - + Are you sure you want to reset the configuration? Opravdu chcete nastavení vrátit do výchozího stavu? - + Show help message Ukázat zprávu s nápovědou - + Show the help message at the beginning in the capture mode. Ukázat zprávu s nápovědou na začátku v režimu zachytávání. - + Show the side panel button Ukázat tlačítko na postranním panelu - + Show the side panel toggle button in the capture mode. V režimu zachytávání ukazovat tlačítko na postranním panelu. - - + + Show desktop notifications Ukázat oznámení - + Show tray icon Ukázat ikonu v oznamovací oblasti panelu - + Show the systemtray icon Ukázat ikonu v oznamovací oblasti panelu - + Configuration File Soubor s nastavením - + Export Vyvést - + Reset Nastavit znovu - + Launch at startup Spustit při spuštění - - + + Launch Flameshot Spustit Flameshot - + Show welcome message on launch - + Close application after capture @@ -443,58 +443,58 @@ Stiskněte mezerník pro otevření postranního panelu. Zavřít po vytvoření snímku - + Close after taking a screenshot Zavřít po vytvoření snímku obrazovky - + Copy URL after upload Kopírovat adresu (URL) po nahrání - + Copy URL and close window after upload Po nahrání zkopírovat URL a zavřít okno - + Save image after copy Uložit obrázek po kopírování - + Save image file after copying it Uložit obrázek se souborem po jeho zkopírování - + Save Path Cesta pro ukládání - + Change... Změnit... - - + + Copy file path after save - + Use fixed path for screenshots to save - + Choose a Folder Vyberte složku - + Unable to write to directory. Nelze zapsat do adresáře. @@ -502,27 +502,27 @@ Stiskněte mezerník pro otevření postranního panelu. HistoryWidget - + Latest Uploads - + Screenshots history is empty - + Copy URL Kopírovat adresu (URL) - + URL copied to clipboard. Adresa (URL) zkopírována do schránky. - + Open in browser @@ -535,9 +535,14 @@ Stiskněte mezerník pro otevření postranního panelu. - Uploading Image - Nahrává se obrázek + Nahrává se obrázek + + + + + Uploading Image... + @@ -550,40 +555,56 @@ Stiskněte mezerník pro otevření postranního panelu. - + URL copied to clipboard. Adresa (URL) zkopírována do schránky. - + Unable to remove screenshot from the remote storage. - + Network error - + Possibly it doesn't exist anymore - + Do you want to remove screenshot from local history anyway? - + Remove screenshot from history? - + + + Retrieving configuration file with s3 creds... + + + + S3 Creds URL is not found in your configuration file + + + Error + Chyba + + + + Unable to get s3 credentials, please check your VPN connection and try again + + ImgS3UploaderTool @@ -601,15 +622,8 @@ Stiskněte mezerník pro otevření postranního panelu. - - Uploading Image - Nahrává se obrázek - - - - Upload image - + Nahrává se obrázek @@ -627,10 +641,16 @@ Stiskněte mezerník pro otevření postranního panelu. Snímek obrazovky zkopírován do schránky. + Deleting image... + + + Uploading Image... + + Copy URL @@ -642,6 +662,7 @@ Stiskněte mezerník pro otevření postranního panelu. Otevřít adresu (URL) + Delete image Smazat obrázek @@ -663,7 +684,7 @@ Stiskněte mezerník pro otevření postranního panelu. ImgurUploader - + Upload to Imgur Nahrát do Imgur @@ -688,7 +709,7 @@ Stiskněte mezerník pro otevření postranního panelu. Obrázek do schránky. - + Unable to open the URL. Nelze otevřít adresu (URL). @@ -1104,7 +1125,7 @@ Možná budete muset napsat před '#' opačné (obrácené) lomítko, Obvykle se Flameshot spouští na pozadí a přidává do oznamovací oblasti panelu ikonu, kterou je ho možné ovládat. - + URL copied to clipboard. Adresa (URL) zkopírována do schránky. @@ -1115,77 +1136,77 @@ You can find me in the system tray. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. - + Toggle side panel - + Resize selection left 1px - + Resize selection right 1px - + Resize selection up 1px - + Resize selection down 1px - + Move selection left 1px - + Move selection right 1px - + Move selection up 1px - + Move selection down 1px - + Quit capture Ukončit zachytávání obrazovky - + Screenshot history - + Capture screen - + Show color picker Ukázat volič barev - + Change the tool's thickness Změnit tloušťku nástroje @@ -1271,22 +1292,22 @@ You can find me in the system tray. ShortcutsWidget - + Hot Keys - + Available shortcuts in the screen capture mode. Dostupné zkratky v režimu zachytávání obrazovky. - + Description Popis - + Key Klávesa @@ -1345,92 +1366,92 @@ You can find me in the system tray. Rok (2000) - + Month Name (jan) Název měsíce (led) - + Month Name (january) Název měsíce (leden) - + Month (01-12) Měsíc (01-12) - + Week Day (1-7) Den v týdnu (1-7) - + Week (01-53) Týden (01-53) - + Day Name (mon) Název dne (pon) - + Day Name (monday) Název dne (pondělí) - + Day (01-31) Den (01-31) - + Day of Month (1-31) Den v měsíci (1-31) - + Day (001-366) Den v roce (001-366) - + Time (%H-%M-%S) Čas (%H-%M-%S) - + Time (%H-%M) Čas (%H-%M) - + Hour (00-23) Hodina (00-23) - + Hour (01-12) Hodina (01-12) - + Minute (00-59) Minuta (00-59) - + Second (00-59) Sekunda (00-59) - + Full Date (%m/%d/%y) Celé datum (%m/%d/%y) - + Full Date (%Y-%m-%d) Celé datum (%Y-%m-%d) diff --git a/data/translations/Internationalization_de_DE.ts b/data/translations/Internationalization_de_DE.ts index b7260abe..71a8a2e2 100644 --- a/data/translations/Internationalization_de_DE.ts +++ b/data/translations/Internationalization_de_DE.ts @@ -75,47 +75,47 @@ CaptureLauncher - + <b>Capture Mode</b> - + Rectangular Region - + Full Screen (All Monitors) - + No Delay - + second - + seconds - + Take new screenshot - + Area: - + Delay: @@ -208,27 +208,27 @@ Drücke die Leertaste um das Seitenmenü zu öffnen. Controller - + &Take Screenshot &Bildschirmaufnahme anfertigen - + &Open Launcher - + &Configuration &Einstellungen - + &About - + &Latest Uploads @@ -237,7 +237,7 @@ Drücke die Leertaste um das Seitenmenü zu öffnen. &Informationen - + &Quit &Beenden @@ -327,170 +327,170 @@ Drücke die Leertaste um das Seitenmenü zu öffnen. GeneneralConf - - + + Import Importieren - - - - + + + + Error Fehler - + Unable to read file. Datei kann nicht gelesen werden. - - + + Unable to write file. Datei kann nicht geschrieben werden. - + Save File Datei speichern - + Confirm Reset Zurücksetzen bestätigen - + Are you sure you want to reset the configuration? Sind Sie sicher, dass sie die Konfiguration zurücksetzen wollen? - + Show help message Hilfetext anzeigen - + Show the help message at the beginning in the capture mode. Hilfetext am Start der Auswahl anzeigen. - + Show the side panel button - + Show the side panel toggle button in the capture mode. - - + + Show desktop notifications Zeige Desktopbenachrichtigungen - + Show tray icon Zeige Taskleistensymbol - + Show the systemtray icon Zeigt das Taskleistensymbol - + Configuration File Konfigurationsdatei - + Export Exportieren - + Reset Zurücksetzen - + Launch at startup Automatisch starten - - + + Launch Flameshot Starte Flameshot - + Show welcome message on launch - + Close application after capture - + Close after taking a screenshot - + Copy URL after upload - + Copy URL and close window after upload - + Save image after copy - + Save image file after copying it - + Save Path - + Change... - - + + Copy file path after save - + Use fixed path for screenshots to save - + Choose a Folder - + Unable to write to directory. @@ -498,27 +498,27 @@ Drücke die Leertaste um das Seitenmenü zu öffnen. HistoryWidget - + Latest Uploads - + Screenshots history is empty - + Copy URL URL kopieren - + URL copied to clipboard. URL kopiert. - + Open in browser @@ -531,9 +531,14 @@ Drücke die Leertaste um das Seitenmenü zu öffnen. - Uploading Image - Bild hochladen + Bild hochladen + + + + + Uploading Image... + @@ -546,40 +551,56 @@ Drücke die Leertaste um das Seitenmenü zu öffnen. - + URL copied to clipboard. URL kopiert. - + Unable to remove screenshot from the remote storage. - + Network error - + Possibly it doesn't exist anymore - + Do you want to remove screenshot from local history anyway? - + Remove screenshot from history? - + + + Retrieving configuration file with s3 creds... + + + + S3 Creds URL is not found in your configuration file + + + Error + Fehler + + + + Unable to get s3 credentials, please check your VPN connection and try again + + ImgS3UploaderTool @@ -597,15 +618,8 @@ Drücke die Leertaste um das Seitenmenü zu öffnen. - - Uploading Image - Bild hochladen - - - - Upload image - + Bild hochladen @@ -623,10 +637,16 @@ Drücke die Leertaste um das Seitenmenü zu öffnen. Bildschirmaufnahme in Zwischenablage kopiert. + Deleting image... + + + Uploading Image... + + Copy URL @@ -638,6 +658,7 @@ Drücke die Leertaste um das Seitenmenü zu öffnen. URL öffnen + Delete image Bild löschen @@ -659,7 +680,7 @@ Drücke die Leertaste um das Seitenmenü zu öffnen. ImgurUploader - + Upload to Imgur Zu Imgur hochladen @@ -684,7 +705,7 @@ Drücke die Leertaste um das Seitenmenü zu öffnen. Bild in Zwischenablage. - + Unable to open the URL. Kann URL nicht öffnen. @@ -1090,7 +1111,7 @@ You may need to escape the '#' sign as in '\#FFF' - + URL copied to clipboard. URL kopiert. @@ -1101,77 +1122,77 @@ You can find me in the system tray. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. - + Toggle side panel - + Resize selection left 1px - + Resize selection right 1px - + Resize selection up 1px - + Resize selection down 1px - + Move selection left 1px - + Move selection right 1px - + Move selection up 1px - + Move selection down 1px - + Quit capture Auswahl verlassen - + Screenshot history - + Capture screen - + Show color picker Zeige Farbauswahl - + Change the tool's thickness Ändere die Dicke des Werkzeugs @@ -1257,22 +1278,22 @@ You can find me in the system tray. ShortcutsWidget - + Hot Keys - + Available shortcuts in the screen capture mode. Verfügbare Tastenkürzel im Aufnahmemodus. - + Description Beschreibung - + Key Taste @@ -1331,92 +1352,92 @@ You can find me in the system tray. Jahr (2000) - + Month Name (jan) Monatsname (Jan) - + Month Name (january) Monatsname (Januar) - + Month (01-12) Monat (01-12) - + Week Day (1-7) Wochentag (1-7) - + Week (01-53) Woche (01-53) - + Day Name (mon) Wochentag (Mon) - + Day Name (monday) Wochentag (Montag) - + Day (01-31) Tag (01-31) - + Day of Month (1-31) Tag des Monats (1-31) - + Day (001-366) Tag (001-366) - + Time (%H-%M-%S) Zeit (%H-%M-%S) - + Time (%H-%M) Zeit (%H-%M) - + Hour (00-23) Stunde (00-23) - + Hour (01-12) Stunde (01-12) - + Minute (00-59) Minute (00-59) - + Second (00-59) Sekunde (00-59) - + Full Date (%m/%d/%y) Komplettes Datum (%m/%d/%y) - + Full Date (%Y-%m-%d) Komplettes Datum (%Y-%m-%d) diff --git a/data/translations/Internationalization_es.ts b/data/translations/Internationalization_es.ts index 0ac6ef0a..906038b1 100644 --- a/data/translations/Internationalization_es.ts +++ b/data/translations/Internationalization_es.ts @@ -75,47 +75,47 @@ CaptureLauncher - + <b>Capture Mode</b> - + Rectangular Region - + Full Screen (All Monitors) - + No Delay - + second - + seconds - + Take new screenshot - + Area: - + Delay: @@ -208,27 +208,27 @@ Presiona Espacio para abrir el panel lateral. Controller - + &Take Screenshot &Tomar captura de pantalla - + &Open Launcher - + &Configuration &Configuración - + &About - + &Latest Uploads @@ -237,7 +237,7 @@ Presiona Espacio para abrir el panel lateral. &Información - + &Quit &Salir @@ -327,170 +327,170 @@ Presiona Espacio para abrir el panel lateral. GeneneralConf - - + + Import Importar - - - - + + + + Error Error - + Unable to read file. Imposible leer el archivo. - - + + Unable to write file. Imposible escribir el archivo. - + Save File Guardar Archivo - + Confirm Reset Confirmar Reset - + Are you sure you want to reset the configuration? ¿Estás seguro de que quieres reiniciar la configuración? - + Show help message Mostrar mensaje de ayuda - + Show the help message at the beginning in the capture mode. Muestra el mensaje de ayuda al iniciar el modo de captura. - + Show the side panel button - + Show the side panel toggle button in the capture mode. - - + + Show desktop notifications Mostrar notificaciones del escritorio - + Show tray icon Mostrar icono en la barra de tareas - + Show the systemtray icon Mostrar el icono en la barra de tareas - + Configuration File Archivo de Configuración - + Export Exportar - + Reset Reset - + Launch at startup Lanzar en el arranque - - + + Launch Flameshot Lanzar Flameshot - + Show welcome message on launch - + Close application after capture - + Close after taking a screenshot - + Copy URL after upload - + Copy URL and close window after upload - + Save image after copy - + Save image file after copying it - + Save Path - + Change... - - + + Copy file path after save - + Use fixed path for screenshots to save - + Choose a Folder - + Unable to write to directory. @@ -498,27 +498,27 @@ Presiona Espacio para abrir el panel lateral. HistoryWidget - + Latest Uploads - + Screenshots history is empty - + Copy URL Copiar URL - + URL copied to clipboard. URL copiada al portapapeles. - + Open in browser @@ -531,9 +531,14 @@ Presiona Espacio para abrir el panel lateral. - Uploading Image - Subiendo Imagen + Subiendo Imagen + + + + + Uploading Image... + @@ -546,40 +551,56 @@ Presiona Espacio para abrir el panel lateral. - + URL copied to clipboard. URL copiada al portapapeles. - + Unable to remove screenshot from the remote storage. - + Network error - + Possibly it doesn't exist anymore - + Do you want to remove screenshot from local history anyway? - + Remove screenshot from history? - + + + Retrieving configuration file with s3 creds... + + + + S3 Creds URL is not found in your configuration file + + + Error + Error + + + + Unable to get s3 credentials, please check your VPN connection and try again + + ImgS3UploaderTool @@ -597,15 +618,8 @@ Presiona Espacio para abrir el panel lateral. - - Uploading Image - Subiendo Imagen - - - - Upload image - + Subiendo Imagen @@ -623,10 +637,16 @@ Presiona Espacio para abrir el panel lateral. Captura copiada al portapapeles. + Deleting image... + + + Uploading Image... + + Copy URL @@ -638,6 +658,7 @@ Presiona Espacio para abrir el panel lateral. Abrir URL + Delete image Borrar imagen @@ -659,7 +680,7 @@ Presiona Espacio para abrir el panel lateral. ImgurUploader - + Upload to Imgur Subir a Imgur @@ -684,7 +705,7 @@ Presiona Espacio para abrir el panel lateral. Imagen al Portapapeles. - + Unable to open the URL. No puede abrir la URL. @@ -1056,7 +1077,7 @@ You may need to escape the '#' sign as in '\#FFF' Imposible escribir en - + URL copied to clipboard. URL copiada al portapapeles. @@ -1097,77 +1118,77 @@ You can find me in the system tray. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. - + Toggle side panel - + Resize selection left 1px - + Resize selection right 1px - + Resize selection up 1px - + Resize selection down 1px - + Move selection left 1px - + Move selection right 1px - + Move selection up 1px - + Move selection down 1px - + Quit capture Salir de la captura - + Screenshot history - + Capture screen - + Show color picker Mostrar el selector de color - + Change the tool's thickness Cambiar el grosor de la herramienta @@ -1253,22 +1274,22 @@ You can find me in the system tray. ShortcutsWidget - + Hot Keys - + Available shortcuts in the screen capture mode. Atajos disponibles en el modo captura de pantalla. - + Description Descripción - + Key Tecla @@ -1327,92 +1348,92 @@ You can find me in the system tray. Año (2000) - + Month Name (jan) Nombre del Mes (jul) - + Month Name (january) Nombre del Mes (julio) - + Month (01-12) Mes (01-12) - + Week Day (1-7) Día de la Semana (1-7) - + Week (01-53) Semana (01-53) - + Day Name (mon) Nombre del Día (dom) - + Day Name (monday) Nombre del Día (domingo) - + Day (01-31) Día (01-31) - + Day of Month (1-31) Día del Mes (1-31) - + Day (001-366) Día (001-366) - + Time (%H-%M-%S) Tiempo (%H-%M-%S) - + Time (%H-%M) Tiempo (%H-%M) - + Hour (00-23) Hora (00-23) - + Hour (01-12) Hora (01-12) - + Minute (00-59) Minuto (00-59) - + Second (00-59) Segundo (00-59) - + Full Date (%m/%d/%y) Fecha (%m/%d/%y) - + Full Date (%Y-%m-%d) Fecha (%Y-%m-%d) diff --git a/data/translations/Internationalization_eu.ts b/data/translations/Internationalization_eu.ts index 71bd4c2c..c914e982 100644 --- a/data/translations/Internationalization_eu.ts +++ b/data/translations/Internationalization_eu.ts @@ -75,47 +75,47 @@ CaptureLauncher - + <b>Capture Mode</b> <b>Argazki-modua</b> - + Rectangular Region Eremu laukizuzena - + Full Screen (All Monitors) Pantaila osoa (pantaila guztiak) - + No Delay Atzerapenik ez - + second segundo - + seconds segundo - + Take new screenshot Egin pantaila-argazki berria - + Area: Eremua: - + Delay: Atzerapena: @@ -208,27 +208,27 @@ Sakatu Zuriunea alboko panela irekitzeko. Controller - + &Take Screenshot &Pantaila-argazkia egin - + &Open Launcher &Abiarazlea ireki - + &Configuration &Ezarpenak - + &About &Honi buruz - + &Latest Uploads @@ -237,7 +237,7 @@ Sakatu Zuriunea alboko panela irekitzeko. &Informazioa - + &Quit &Irten @@ -327,114 +327,114 @@ Sakatu Zuriunea alboko panela irekitzeko. GeneneralConf - - + + Import Inportatu - - - - + + + + Error Errorea - + Unable to read file. Ezin da fitxategia irakurri. - - + + Unable to write file. Ezin da fitxategian idatzi. - + Save File Gorde fitxategia - + Confirm Reset Baieztatu berrezartzea - + Are you sure you want to reset the configuration? Ziur ezarpenak berrezarri nahi dituzula? - + Show help message Erakutsi laguntza-mezua - + Show the help message at the beginning in the capture mode. Erakutsi laguntza-mezua argazki-hartze modua irekitzean. - + Show the side panel button Erakutsi aldeko paneleko botoia - + Show the side panel toggle button in the capture mode. Erakutsi aldeko panela erakusteko botoia argazki-hartze moduan. - - + + Show desktop notifications Erakutsi mahaigaineko jakinarazpenak - + Show tray icon Erakutsi ikonoa erretiluan - + Show the systemtray icon Erakutsi ikonoa sistemako erretiluan - + Configuration File Konfigurazio-fitxategia - + Export Esportatu - + Reset Berrezarri - + Launch at startup Abiarazi saio-hasieran - - + + Launch Flameshot Abiarazi Flamsehot - + Show welcome message on launch - + Close application after capture @@ -443,58 +443,58 @@ Sakatu Zuriunea alboko panela irekitzeko. Itxi argazkia egin ostean - + Close after taking a screenshot Itxi pantaila-argazkia egin ostean - + Copy URL after upload Kopiatu URLa igo ostean - + Copy URL and close window after upload Kopiatu URLa eta itxi leihoa igo ostean - + Save image after copy Gorde irudia kopiatu ostean - + Save image file after copying it Gorde irudia fitxategian kopiatu ostean - + Save Path Gordetzeko bidea - + Change... Aldatu... - - + + Copy file path after save - + Use fixed path for screenshots to save - + Choose a Folder Aukeratu karpeta - + Unable to write to directory. Ezin da direktorioan idatzi. @@ -502,27 +502,27 @@ Sakatu Zuriunea alboko panela irekitzeko. HistoryWidget - + Latest Uploads - + Screenshots history is empty - + Copy URL Kopiatu URLa - + URL copied to clipboard. Arbelean kopiatu da URLa. - + Open in browser @@ -535,9 +535,14 @@ Sakatu Zuriunea alboko panela irekitzeko. - Uploading Image - Irudia igotzen + Irudia igotzen + + + + + Uploading Image... + @@ -550,40 +555,56 @@ Sakatu Zuriunea alboko panela irekitzeko. - + URL copied to clipboard. Arbelean kopiatu da URLa. - + Unable to remove screenshot from the remote storage. - + Network error - + Possibly it doesn't exist anymore - + Do you want to remove screenshot from local history anyway? - + Remove screenshot from history? - + + + Retrieving configuration file with s3 creds... + + + + S3 Creds URL is not found in your configuration file + + + Error + Errorea + + + + Unable to get s3 credentials, please check your VPN connection and try again + + ImgS3UploaderTool @@ -601,15 +622,8 @@ Sakatu Zuriunea alboko panela irekitzeko. - - Uploading Image - Irudia igotzen - - - - Upload image - + Irudia igotzen @@ -627,10 +641,16 @@ Sakatu Zuriunea alboko panela irekitzeko. Arbelean kopiatu da pantaila-argazkia. + Deleting image... + + + Uploading Image... + + Copy URL @@ -642,6 +662,7 @@ Sakatu Zuriunea alboko panela irekitzeko. Ireki URLa + Delete image Ezabatu irudia @@ -663,7 +684,7 @@ Sakatu Zuriunea alboko panela irekitzeko. ImgurUploader - + Upload to Imgur Igo Imgur-era @@ -688,7 +709,7 @@ Sakatu Zuriunea alboko panela irekitzeko. Irudia arbelera. - + Unable to open the URL. Ezin da ireki URLa. @@ -1070,7 +1091,7 @@ Baliteke '#' karakterea ihes egin behar izatea, '\#FFF'n bez Ezin da hemen idatzi: - + URL copied to clipboard. Arbelean kopiatu da URLa. @@ -1115,77 +1136,77 @@ You can find me in the system tray. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. - + Toggle side panel - + Resize selection left 1px - + Resize selection right 1px - + Resize selection up 1px - + Resize selection down 1px - + Move selection left 1px - + Move selection right 1px - + Move selection up 1px - + Move selection down 1px - + Quit capture Irten argazki-hartzetik - + Screenshot history - + Capture screen - + Show color picker Erakutsi kolore hautagailua - + Change the tool's thickness Aldatu tresnaren lodiera @@ -1271,22 +1292,22 @@ You can find me in the system tray. ShortcutsWidget - + Hot Keys - + Available shortcuts in the screen capture mode. Argazki-hartze moduan erabili daitezken laster-teklak. - + Description Deskribapena - + Key Tekla @@ -1345,92 +1366,92 @@ You can find me in the system tray. Urtea (2000) - + Month Name (jan) Hilabetearen izena (ira) - + Month Name (january) Hilabetearen izena (iraila) - + Month (01-12) Hilabetea (01-12) - + Week Day (1-7) Asteko eguna (1-7) - + Week (01-53) Astea (01-53) - + Day Name (mon) Egunaren izena (ar.) - + Day Name (monday) Egunaren izena (asteartea) - + Day (01-31) Eguna (01-31) - + Day of Month (1-31) Hilabeteko eguna (1-31) - + Day (001-366) Urteko eguna (001-366) - + Time (%H-%M-%S) Ordua (%H-%M-%S) - + Time (%H-%M) Ordua (%H-%M) - + Hour (00-23) Eguneko ordua (00-23) - + Hour (01-12) Eguneko ordua (01-12) - + Minute (00-59) Minutua (00-59) - + Second (00-59) Segundoa (00-59) - + Full Date (%m/%d/%y) Data (%h/%e/%u) - + Full Date (%Y-%m-%d) Data (%U-%h-%e) diff --git a/data/translations/Internationalization_fr.ts b/data/translations/Internationalization_fr.ts index 9a91935d..830234a8 100644 --- a/data/translations/Internationalization_fr.ts +++ b/data/translations/Internationalization_fr.ts @@ -75,47 +75,47 @@ CaptureLauncher - + <b>Capture Mode</b> - + Rectangular Region - + Full Screen (All Monitors) - + No Delay - + second - + seconds - + Take new screenshot - + Area: - + Delay: @@ -208,27 +208,27 @@ Appuyer sur Espace pour ouvrir le panneau latéral. Controller - + &Take Screenshot &Capturer l'écran - + &Open Launcher - + &Configuration &Configuration - + &About - + &Latest Uploads @@ -237,7 +237,7 @@ Appuyer sur Espace pour ouvrir le panneau latéral. &Informations - + &Quit &Quitter @@ -327,114 +327,114 @@ Appuyer sur Espace pour ouvrir le panneau latéral. GeneneralConf - - + + Import Importer - - - - + + + + Error Erreur - + Unable to read file. Impossible de lire le fichier. - - + + Unable to write file. Impossible d'écrire le fichier. - + Save File Sauvegarder le fichier - + Confirm Reset Confirmer la Réinitialisation - + Are you sure you want to reset the configuration? Êtes-vous sûr de vouloir réinitialiser la configuration ? - + Show help message Montrer le message d'aide - + Show the help message at the beginning in the capture mode. Afficher ce message au lancement du mode capture. - + Show the side panel button - + Show the side panel toggle button in the capture mode. - - + + Show desktop notifications Afficher les notifications du bureau - + Show tray icon Afficher les icones de la barre d'état - + Show the systemtray icon Afficher l'icône dans la barre de tâches - + Configuration File Fichier de Configuration - + Export Exporter - + Reset Réinitialiser - + Launch at startup Lancer au démarrage - - + + Launch Flameshot Démarrer Flameshot - + Show welcome message on launch - + Close application after capture @@ -443,58 +443,58 @@ Appuyer sur Espace pour ouvrir le panneau latéral. Fermer après une capture - + Close after taking a screenshot Fermer l'application après une capture d'écran - + Copy URL after upload - + Copy URL and close window after upload - + Save image after copy - + Save image file after copying it - + Save Path - + Change... - - + + Copy file path after save - + Use fixed path for screenshots to save - + Choose a Folder - + Unable to write to directory. @@ -502,27 +502,27 @@ Appuyer sur Espace pour ouvrir le panneau latéral. HistoryWidget - + Latest Uploads - + Screenshots history is empty - + Copy URL Copier l'URL - + URL copied to clipboard. URL copiée dans le Presse-papier. - + Open in browser @@ -535,9 +535,14 @@ Appuyer sur Espace pour ouvrir le panneau latéral. - Uploading Image - Mise en ligne de l'image + Mise en ligne de l'image + + + + + Uploading Image... + @@ -550,40 +555,56 @@ Appuyer sur Espace pour ouvrir le panneau latéral. - + URL copied to clipboard. URL copiée dans le Presse-papier. - + Unable to remove screenshot from the remote storage. - + Network error - + Possibly it doesn't exist anymore - + Do you want to remove screenshot from local history anyway? - + Remove screenshot from history? - + + + Retrieving configuration file with s3 creds... + + + + S3 Creds URL is not found in your configuration file + + + Error + Erreur + + + + Unable to get s3 credentials, please check your VPN connection and try again + + ImgS3UploaderTool @@ -601,15 +622,8 @@ Appuyer sur Espace pour ouvrir le panneau latéral. - - Uploading Image - Mise en ligne de l'image - - - - Upload image - + Mise en ligne de l'image @@ -627,10 +641,16 @@ Appuyer sur Espace pour ouvrir le panneau latéral. Capture d'écran copiée dans le Presse-papier. + Deleting image... + + + Uploading Image... + + Copy URL @@ -642,6 +662,7 @@ Appuyer sur Espace pour ouvrir le panneau latéral. Ouvrir l'URL + Delete image @@ -663,7 +684,7 @@ Appuyer sur Espace pour ouvrir le panneau latéral. ImgurUploader - + Upload to Imgur Mettre en ligne vers Imgur @@ -684,7 +705,7 @@ Appuyer sur Espace pour ouvrir le panneau latéral. Image dans le Presse-papier. - + Unable to open the URL. Impossible d'ouvrir l'URL. @@ -1052,7 +1073,7 @@ You may need to escape the '#' sign as in '\#FFF' Impossible d'écrire par dessus - + URL copied to clipboard. URL copiée dans le Presse-papier. @@ -1093,77 +1114,77 @@ You can find me in the system tray. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. - + Toggle side panel - + Resize selection left 1px - + Resize selection right 1px - + Resize selection up 1px - + Resize selection down 1px - + Move selection left 1px - + Move selection right 1px - + Move selection up 1px - + Move selection down 1px - + Quit capture Quitter la capture d'écran - + Screenshot history - + Capture screen - + Show color picker Afficher la palette de couleurs - + Change the tool's thickness Changer l'épaisseur des outils @@ -1249,22 +1270,22 @@ You can find me in the system tray. ShortcutsWidget - + Hot Keys - + Available shortcuts in the screen capture mode. Raccourcis disponibles en mode capture d'écran. - + Description Description - + Key Clé @@ -1323,92 +1344,92 @@ You can find me in the system tray. Année (2000) - + Month Name (jan) Nom des Mois (jan) - + Month Name (january) nom des Mois (janvier) - + Month (01-12) Mois (01-12) - + Week Day (1-7) Jour de la Semaine (1-7) - + Week (01-53) Semaine (01-53) - + Day Name (mon) Nom du Jour (lun) - + Day Name (monday) Nom du Jour (lundi) - + Day (01-31) Jour (01-31) - + Day of Month (1-31) Jour du Mois (1-31) - + Day (001-366) Jour de l'année (001-366) - + Time (%H-%M-%S) Heure (%H-%M-%S) - + Time (%H-%M) Heure (%H-%M) - + Hour (00-23) Heure (00-23) - + Hour (01-12) Heure (01-12) - + Minute (00-59) Minute (00-59) - + Second (00-59) Seconde (00-59) - + Full Date (%m/%d/%y) Date (%m/%d/%y) - + Full Date (%Y-%m-%d) Date Complête (%Y-%m-%d) diff --git a/data/translations/Internationalization_hu.ts b/data/translations/Internationalization_hu.ts index 2d08036b..143b4710 100644 --- a/data/translations/Internationalization_hu.ts +++ b/data/translations/Internationalization_hu.ts @@ -457,7 +457,7 @@ Press Space to open the side panel. Uploading Image - Kép felötlése + Kép felötlése Delete image from S3 @@ -495,6 +495,22 @@ Press Space to open the side panel. S3 Creds URL is not found in your configuration file + + Uploading Image... + + + + Retrieving configuration file with s3 creds... + + + + Error + Hiba + + + Unable to get s3 credentials, please check your VPN connection and try again + + ImgS3UploaderTool @@ -511,11 +527,7 @@ Press Space to open the side panel. Uploading Image - Kép felötlése - - - Upload image - + Kép felötlése Unable to open the URL. @@ -549,6 +561,10 @@ Press Space to open the side panel. Image to Clipboard. Kép a vágolapra. + + Uploading Image... + + ImgUploaderTool diff --git a/data/translations/Internationalization_it_IT.ts b/data/translations/Internationalization_it_IT.ts index 31409aa7..05b667e6 100644 --- a/data/translations/Internationalization_it_IT.ts +++ b/data/translations/Internationalization_it_IT.ts @@ -64,47 +64,47 @@ CaptureLauncher - + <b>Capture Mode</b> - + Rectangular Region - + Full Screen (All Monitors) - + No Delay - + second - + seconds - + Take new screenshot - + Area: - + Delay: @@ -193,32 +193,32 @@ Press Space to open the side panel. Controller - + &Take Screenshot - + &Open Launcher - + &Configuration - + &About - + &Quit - + &Latest Uploads @@ -308,170 +308,170 @@ Press Space to open the side panel. GeneneralConf - - + + Import - - - - + + + + Error - + Unable to read file. - - + + Unable to write file. - + Save File - + Confirm Reset - + Are you sure you want to reset the configuration? - + Show help message - + Show the help message at the beginning in the capture mode. - + Show the side panel button - + Show the side panel toggle button in the capture mode. - - + + Show desktop notifications - + Show tray icon - + Show the systemtray icon - + Configuration File - + Export - + Reset - + Launch at startup - - + + Launch Flameshot - + Show welcome message on launch - + Close application after capture - + Close after taking a screenshot - + Copy URL after upload - + Copy URL and close window after upload - + Save image after copy - + Save image file after copying it - + Save Path - + Change... - - + + Copy file path after save - + Use fixed path for screenshots to save - + Choose a Folder - + Unable to write to directory. @@ -479,27 +479,27 @@ Press Space to open the side panel. HistoryWidget - + Latest Uploads - + Screenshots history is empty - + Copy URL - + URL copied to clipboard. - + Open in browser @@ -513,7 +513,8 @@ Press Space to open the side panel. - Uploading Image + + Uploading Image... @@ -527,40 +528,56 @@ Press Space to open the side panel. - + URL copied to clipboard. - + Unable to remove screenshot from the remote storage. - + Network error - + Possibly it doesn't exist anymore - + Do you want to remove screenshot from local history anyway? - + Remove screenshot from history? - + + + Retrieving configuration file with s3 creds... + + + + S3 Creds URL is not found in your configuration file + + + Error + + + + + Unable to get s3 credentials, please check your VPN connection and try again + + ImgS3UploaderTool @@ -577,17 +594,6 @@ Press Space to open the side panel. Upload image to S3 - - - - Uploading Image - - - - - Upload image - - Unable to open the URL. @@ -604,10 +610,16 @@ Press Space to open the side panel. + Deleting image... + + + Uploading Image... + + Copy URL @@ -619,6 +631,7 @@ Press Space to open the side panel. + Delete image @@ -640,12 +653,12 @@ Press Space to open the side panel. ImgurUploader - + Upload to Imgur - + Unable to open the URL. @@ -932,7 +945,7 @@ You may need to escape the '#' sign as in '\#FFF' - + URL copied to clipboard. @@ -990,77 +1003,77 @@ You can find me in the system tray. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. - + Toggle side panel - + Resize selection left 1px - + Resize selection right 1px - + Resize selection up 1px - + Resize selection down 1px - + Move selection left 1px - + Move selection right 1px - + Move selection up 1px - + Move selection down 1px - + Quit capture - + Screenshot history - + Capture screen - + Show color picker - + Change the tool's thickness @@ -1146,22 +1159,22 @@ You can find me in the system tray. ShortcutsWidget - + Hot Keys - + Available shortcuts in the screen capture mode. - + Description - + Key @@ -1220,92 +1233,92 @@ You can find me in the system tray. - + Month Name (jan) - + Month Name (january) - + Month (01-12) - + Week Day (1-7) - + Week (01-53) - + Day Name (mon) - + Day Name (monday) - + Day (01-31) - + Day of Month (1-31) - + Day (001-366) - + Time (%H-%M-%S) - + Time (%H-%M) - + Hour (00-23) - + Hour (01-12) - + Minute (00-59) - + Second (00-59) - + Full Date (%m/%d/%y) - + Full Date (%Y-%m-%d) diff --git a/data/translations/Internationalization_ja.ts b/data/translations/Internationalization_ja.ts index 373c1fb0..d931281b 100644 --- a/data/translations/Internationalization_ja.ts +++ b/data/translations/Internationalization_ja.ts @@ -75,47 +75,47 @@ CaptureLauncher - + <b>Capture Mode</b> - + Rectangular Region - + Full Screen (All Monitors) - + No Delay - + second - + seconds - + Take new screenshot - + Area: - + Delay: @@ -208,27 +208,27 @@ Enter を押すと画面をキャプチャー。 Controller - + &Take Screenshot スクリーンショットを撮る(&T) - + &Open Launcher - + &Configuration 設定(&C) - + &About - + &Latest Uploads @@ -237,7 +237,7 @@ Enter を押すと画面をキャプチャー。 情報(&I) - + &Quit 終了(&Q) @@ -327,170 +327,170 @@ Enter を押すと画面をキャプチャー。 GeneneralConf - + Show help message ヘルプメッセージを表示する - + Show the help message at the beginning in the capture mode. キャプチャーモード開始時にヘルプメッセージを表示する。 - - + + Show desktop notifications デスクトップの通知を表示する - + Show tray icon トレイアイコンを表示する - + Show the systemtray icon システムトレイアイコンを表示する - - + + Import インポート - - - - + + + + Error エラー - + Unable to read file. ファイルを読み込めません。 - - + + Unable to write file. ファイルに書き込めません。 - + Save File ファイルを保存 - + Confirm Reset リセットの確認 - + Are you sure you want to reset the configuration? 設定をリセットしてもよろしいですか? - + Show the side panel button - + Show the side panel toggle button in the capture mode. - + Configuration File 設定ファイル - + Export エクスポート - + Reset リセット - + Launch at startup スタートアップ時に起動する - - + + Launch Flameshot Flameshot を起動する - + Show welcome message on launch - + Close application after capture - + Close after taking a screenshot - + Copy URL after upload - + Copy URL and close window after upload - + Save image after copy - + Save image file after copying it - + Save Path - + Change... - - + + Copy file path after save - + Use fixed path for screenshots to save - + Choose a Folder - + Unable to write to directory. @@ -498,27 +498,27 @@ Enter を押すと画面をキャプチャー。 HistoryWidget - + Latest Uploads - + Screenshots history is empty - + Copy URL URL をコピー - + URL copied to clipboard. URL をクリップボードにコピーしました。 - + Open in browser @@ -531,9 +531,14 @@ Enter を押すと画面をキャプチャー。 - Uploading Image - 画像をアップロード中 + 画像をアップロード中 + + + + + Uploading Image... + @@ -546,40 +551,56 @@ Enter を押すと画面をキャプチャー。 - + URL copied to clipboard. URL をクリップボードにコピーしました。 - + Unable to remove screenshot from the remote storage. - + Network error - + Possibly it doesn't exist anymore - + Do you want to remove screenshot from local history anyway? - + Remove screenshot from history? - + + + Retrieving configuration file with s3 creds... + + + + S3 Creds URL is not found in your configuration file + + + Error + エラー + + + + Unable to get s3 credentials, please check your VPN connection and try again + + ImgS3UploaderTool @@ -597,15 +618,8 @@ Enter を押すと画面をキャプチャー。 - - Uploading Image - 画像をアップロード中 - - - - Upload image - + 画像をアップロード中 @@ -623,10 +637,16 @@ Enter を押すと画面をキャプチャー。 スクリーンショットをクリップボードにコピーしました。 + Deleting image... + + + Uploading Image... + + Copy URL @@ -638,6 +658,7 @@ Enter を押すと画面をキャプチャー。 URL を開く + Delete image 画像を削除 @@ -659,7 +680,7 @@ Enter を押すと画面をキャプチャー。 ImgurUploader - + Upload to Imgur Imgur にアップロード @@ -684,7 +705,7 @@ Enter を押すと画面をキャプチャー。 画像をクリップボードへ。 - + Unable to open the URL. URL を開けません。 @@ -1052,7 +1073,7 @@ You may need to escape the '#' sign as in '\#FFF' 書き込めません: - + URL copied to clipboard. URL をクリップボードにコピーしました。 @@ -1093,77 +1114,77 @@ You can find me in the system tray. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. - + Toggle side panel - + Resize selection left 1px - + Resize selection right 1px - + Resize selection up 1px - + Resize selection down 1px - + Move selection left 1px - + Move selection right 1px - + Move selection up 1px - + Move selection down 1px - + Quit capture キャプチャーを終了する - + Screenshot history - + Capture screen - + Show color picker カラーピッカーを表示する - + Change the tool's thickness ツールの値 (太さや濃さ) を変更する @@ -1249,22 +1270,22 @@ You can find me in the system tray. ShortcutsWidget - + Hot Keys - + Available shortcuts in the screen capture mode. スクリーンキャプチャーモードで利用可能なショートカット。 - + Description 説明 - + Key キー @@ -1323,92 +1344,92 @@ You can find me in the system tray. 年 (2000) - + Month Name (jan) 月 (jan) - + Month Name (january) 月 (january) - + Month (01-12) 月 (01-12) - + Week Day (1-7) 週日 (1-7) - + Week (01-53) 週 (01-53) - + Day Name (mon) 曜日 (月) - + Day Name (monday) 曜日 (月曜日) - + Day (01-31) 日 (01-31) - + Day of Month (1-31) 日 (1-31) - + Day (001-366) 日 (001-366) - + Time (%H-%M-%S) 時刻 (%H-%M-%S) - + Time (%H-%M) 時刻 (%H-%M) - + Hour (00-23) 時 (00-23) - + Hour (01-12) 時 (01-12) - + Minute (00-59) 分 (00-59) - + Second (00-59) 秒 (00-59) - + Full Date (%m/%d/%y) 年月日 (%m/%d/%y) - + Full Date (%Y-%m-%d) 年月日 (%Y-%m-%d) diff --git a/data/translations/Internationalization_ka.ts b/data/translations/Internationalization_ka.ts index e3931166..6ecbf249 100644 --- a/data/translations/Internationalization_ka.ts +++ b/data/translations/Internationalization_ka.ts @@ -75,47 +75,47 @@ CaptureLauncher - + <b>Capture Mode</b> - + Rectangular Region - + Full Screen (All Monitors) - + No Delay - + second - + seconds - + Take new screenshot - + Area: - + Delay: @@ -204,27 +204,27 @@ Press Space to open the side panel. Controller - + &Take Screenshot - + &Open Launcher - + &Configuration &პარამეტრები - + &About - + &Latest Uploads @@ -233,7 +233,7 @@ Press Space to open the side panel. &ინფორმაცია - + &Quit &გამოსვლა @@ -323,170 +323,170 @@ Press Space to open the side panel. GeneneralConf - - + + Import იმპორტირება - - - - + + + + Error შეცდომა - + Unable to read file. ფაილის წაკითხვა ვერ მოხერხდა. - - + + Unable to write file. ფაილის ჩაწერა ვერ მოხერხდა. - + Save File ფაილის შენახვა - + Confirm Reset განულების დადასტურება - + Are you sure you want to reset the configuration? დარწმუნებული ხართ, რომ გსურთ პარამეტრების განულება? - + Show help message დახმარების შეტყობინების ნახვა - + Show the help message at the beginning in the capture mode. დახმარების შეტყობინების ნახვა გადაღების რეჟიმის დაწყებისას. - + Show the side panel button - + Show the side panel toggle button in the capture mode. - - + + Show desktop notifications ცნობების ჩვენება სამუშაო მაგიდაზე - + Show tray icon ხატულის ჩვენება სისტემურ პანელზე - + Show the systemtray icon ხატულის ჩვენება სისტემურ პანელზე - + Configuration File პარამეტრების ფაილი - + Export ექსპორტირება - + Reset განულება - + Launch at startup გაშვება სისტემის ჩატვირთვისას - - + + Launch Flameshot - + Show welcome message on launch - + Close application after capture - + Close after taking a screenshot - + Copy URL after upload - + Copy URL and close window after upload - + Save image after copy - + Save image file after copying it - + Save Path - + Change... - - + + Copy file path after save - + Use fixed path for screenshots to save - + Choose a Folder - + Unable to write to directory. @@ -494,27 +494,27 @@ Press Space to open the side panel. HistoryWidget - + Latest Uploads - + Screenshots history is empty - + Copy URL URL-ის კოპირება - + URL copied to clipboard. URL დაკოპირდა გაცვლის ბუფერში. - + Open in browser @@ -527,9 +527,14 @@ Press Space to open the side panel. - Uploading Image - სურათის ატვირთვა + სურათის ატვირთვა + + + + + Uploading Image... + @@ -542,40 +547,56 @@ Press Space to open the side panel. - + URL copied to clipboard. URL დაკოპირდა გაცვლის ბუფერში. - + Unable to remove screenshot from the remote storage. - + Network error - + Possibly it doesn't exist anymore - + Do you want to remove screenshot from local history anyway? - + Remove screenshot from history? - + + + Retrieving configuration file with s3 creds... + + + + S3 Creds URL is not found in your configuration file + + + Error + შეცდომა + + + + Unable to get s3 credentials, please check your VPN connection and try again + + ImgS3UploaderTool @@ -593,15 +614,8 @@ Press Space to open the side panel. - - Uploading Image - სურათის ატვირთვა - - - - Upload image - + სურათის ატვირთვა @@ -619,10 +633,16 @@ Press Space to open the side panel. სურათი დაკოპირდა გაცვლის ბუფერში. + Deleting image... + + + Uploading Image... + + Copy URL @@ -634,6 +654,7 @@ Press Space to open the side panel. URL-ის გახსნა + Delete image @@ -655,7 +676,7 @@ Press Space to open the side panel. ImgurUploader - + Upload to Imgur Imgur-ზე ატვირთვა @@ -676,7 +697,7 @@ Press Space to open the side panel. სურათის გაცვლის ბუფერში გაგზავნა - + Unable to open the URL. URL-ის გახსნა ვერ მოხერხდა. @@ -1044,7 +1065,7 @@ You may need to escape the '#' sign as in '\#FFF' შემდეგ მისამართზე ჩაწერა ვერ მოხერხდა: - + URL copied to clipboard. URL დაკოპირდა გაცვლის ბუფერში. @@ -1085,77 +1106,77 @@ You can find me in the system tray. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. - + Toggle side panel - + Resize selection left 1px - + Resize selection right 1px - + Resize selection up 1px - + Resize selection down 1px - + Move selection left 1px - + Move selection right 1px - + Move selection up 1px - + Move selection down 1px - + Quit capture გადაღებიდან გამოსვლა - + Screenshot history - + Capture screen - + Show color picker ფერის შესარჩევის ჩვენება - + Change the tool's thickness ხელსაწყოს სისქის შეცვლა @@ -1241,22 +1262,22 @@ You can find me in the system tray. ShortcutsWidget - + Hot Keys - + Available shortcuts in the screen capture mode. გადაღების რეჟიმში ხელმისაწვდომი მალსახმობები. - + Description აღწერა - + Key კლავიში @@ -1315,92 +1336,92 @@ You can find me in the system tray. წელი (2000) - + Month Name (jan) თვის სახელი (იან) - + Month Name (january) თვის სახელი (იანვარი) - + Month (01-12) თვე (01-12) - + Week Day (1-7) კვირის დღე (1-7) - + Week (01-53) კვირა (01-53) - + Day Name (mon) დღის სახელი (ორშ) - + Day Name (monday) დღის სახელი (ორშაბათი) - + Day (01-31) დღე (01-31) - + Day of Month (1-31) თვის დღე (1-31) - + Day (001-366) დღე (001-366) - + Time (%H-%M-%S) - + Time (%H-%M) - + Hour (00-23) საათი (00-23) - + Hour (01-12) საათი (01-12) - + Minute (00-59) წუთი (00-59) - + Second (00-59) წამი (00-59) - + Full Date (%m/%d/%y) სრული თარიღი (%m/%d/%y) - + Full Date (%Y-%m-%d) სრული თარიღი (%Y-%m-%d) diff --git a/data/translations/Internationalization_nl.ts b/data/translations/Internationalization_nl.ts index a6ddaf86..ce7dd668 100644 --- a/data/translations/Internationalization_nl.ts +++ b/data/translations/Internationalization_nl.ts @@ -75,47 +75,47 @@ CaptureLauncher - + <b>Capture Mode</b> - + Rectangular Region - + Full Screen (All Monitors) - + No Delay - + second - + seconds - + Take new screenshot - + Area: - + Delay: @@ -208,27 +208,27 @@ Druk op spatie om het zijpaneel te openen. Controller - + &Take Screenshot Schermafdruk &maken - + &Open Launcher - + &Configuration &Configuratie - + &About - + &Latest Uploads @@ -237,7 +237,7 @@ Druk op spatie om het zijpaneel te openen. &Informatie - + &Quit &Afsluiten @@ -327,170 +327,170 @@ Druk op spatie om het zijpaneel te openen. GeneneralConf - - + + Import Importeren - - - - + + + + Error Fout - + Unable to read file. Kan bestand niet uitlezen. - - + + Unable to write file. Kan bestand niet wegschrijven. - + Save File Bestand opslaan - + Confirm Reset Herstellen bevestigen - + Are you sure you want to reset the configuration? Weet je zeker dat je de standwaardwaarden van de configuratie wilt herstellen? - + Show help message Uitleg tonen - + Show the help message at the beginning in the capture mode. Toont een bericht met uitleg bij het openen van de vastlegmodus. - + Show the side panel button - + Show the side panel toggle button in the capture mode. - - + + Show desktop notifications Bureaubladmeldingen tonen - + Show tray icon Systeemvakpictogram tonen - + Show the systemtray icon Toont het systeemvakpictogram - + Configuration File Configuratiebestand - + Export Exporteren - + Reset Standaardwaarden - + Launch at startup Automatisch opstarten - - + + Launch Flameshot Flameshot openen - + Show welcome message on launch - + Close application after capture - + Close after taking a screenshot - + Copy URL after upload - + Copy URL and close window after upload - + Save image after copy - + Save image file after copying it - + Save Path - + Change... - - + + Copy file path after save - + Use fixed path for screenshots to save - + Choose a Folder - + Unable to write to directory. @@ -498,27 +498,27 @@ Druk op spatie om het zijpaneel te openen. HistoryWidget - + Latest Uploads - + Screenshots history is empty - + Copy URL URL kopiëren - + URL copied to clipboard. URL gekopieerd naar klembord. - + Open in browser @@ -531,9 +531,14 @@ Druk op spatie om het zijpaneel te openen. - Uploading Image - Bezig met uploaden van afbeelding... + Bezig met uploaden van afbeelding... + + + + + Uploading Image... + @@ -546,40 +551,56 @@ Druk op spatie om het zijpaneel te openen. - + URL copied to clipboard. URL gekopieerd naar klembord. - + Unable to remove screenshot from the remote storage. - + Network error - + Possibly it doesn't exist anymore - + Do you want to remove screenshot from local history anyway? - + Remove screenshot from history? - + + + Retrieving configuration file with s3 creds... + + + + S3 Creds URL is not found in your configuration file + + + Error + Fout + + + + Unable to get s3 credentials, please check your VPN connection and try again + + ImgS3UploaderTool @@ -597,15 +618,8 @@ Druk op spatie om het zijpaneel te openen. - - Uploading Image - Bezig met uploaden van afbeelding... - - - - Upload image - + Bezig met uploaden van afbeelding... @@ -623,10 +637,16 @@ Druk op spatie om het zijpaneel te openen. Schermafdruk gekopieerd naar klembord. + Deleting image... + + + Uploading Image... + + Copy URL @@ -638,6 +658,7 @@ Druk op spatie om het zijpaneel te openen. URL openen + Delete image Afbeelding verwijderen @@ -659,7 +680,7 @@ Druk op spatie om het zijpaneel te openen. ImgurUploader - + Upload to Imgur Uploaden naar Imgur @@ -684,7 +705,7 @@ Druk op spatie om het zijpaneel te openen. Afbeelding naar klembord. - + Unable to open the URL. Kan URL niet openen. @@ -1056,7 +1077,7 @@ You may need to escape the '#' sign as in '\#FFF' Kan niet wegschrijven naar - + URL copied to clipboard. URL gekopieerd naar klembord. @@ -1097,77 +1118,77 @@ You can find me in the system tray. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. - + Toggle side panel - + Resize selection left 1px - + Resize selection right 1px - + Resize selection up 1px - + Resize selection down 1px - + Move selection left 1px - + Move selection right 1px - + Move selection up 1px - + Move selection down 1px - + Quit capture Vastleggen afsluiten - + Screenshot history - + Capture screen - + Show color picker Kleurkiezer tonen - + Change the tool's thickness Wijzig de gereedschapsdikte @@ -1254,22 +1275,22 @@ You can find me in the system tray. ShortcutsWidget - + Hot Keys - + Available shortcuts in the screen capture mode. Beschikbare sneltoetsen in de vastlegmodus. - + Description Omschrijving - + Key Toets @@ -1328,92 +1349,92 @@ You can find me in the system tray. Jaar (2000) - + Month Name (jan) Naam van de maand (јаn) - + Month Name (january) Naam van de maand (јаnuari) - + Month (01-12) Maand (01-12) - + Week Day (1-7) Weekdag (1-7) - + Week (01-53) Week (01-53) - + Day Name (mon) Naam van de dag (ma) - + Day Name (monday) Naam van de dag (maandag) - + Day (01-31) Dag (01-31) - + Day of Month (1-31) Dag van de maand (1-31) - + Day (001-366) Dag (001-366) - + Time (%H-%M-%S) Tijd (%H-%M-%S) - + Time (%H-%M) Tijd (%H-%M) - + Hour (00-23) Uur (00-23) - + Hour (01-12) Uur (01-12) - + Minute (00-59) Minuten (00-59) - + Second (00-59) Seconden (00-59) - + Full Date (%m/%d/%y) Volledige datum (%m/%d/%y) - + Full Date (%Y-%m-%d) Volledige datum (%Y-%m-%d) diff --git a/data/translations/Internationalization_nl_NL.ts b/data/translations/Internationalization_nl_NL.ts index 337d1e65..61fb0bfd 100644 --- a/data/translations/Internationalization_nl_NL.ts +++ b/data/translations/Internationalization_nl_NL.ts @@ -75,47 +75,47 @@ CaptureLauncher - + <b>Capture Mode</b> <b>Opnamemodus</b> - + Rectangular Region Rechthoekige regio - + Full Screen (All Monitors) Volledig scherm (alle monitoren) - + No Delay Geen vertraging - + second seconde - + seconds seconden - + Take new screenshot Maak een nieuwe schermafbeelding - + Area: Gebied: - + Delay: Vertraging: @@ -208,27 +208,27 @@ Druk op de spatiebalk om het zijpaneel te openen. Controller - + &Take Screenshot &Maak een Schermopname - + &Open Launcher &Open Starter - + &Configuration &Configuratie - + &About - + &Latest Uploads @@ -237,7 +237,7 @@ Druk op de spatiebalk om het zijpaneel te openen. &Informatie - + &Quit &Sluiten @@ -327,114 +327,114 @@ Druk op de spatiebalk om het zijpaneel te openen. GeneneralConf - - + + Import Importeren - - - - + + + + Error Fout - + Unable to read file. Bestand kan niet gelezen worden. - - + + Unable to write file. Kan bestand niet wegschrijven. - + Save File Bestand opslaan - + Confirm Reset Herstellen bevestigen - + Are you sure you want to reset the configuration? Weet u zeker dat u de configuratie opnieuw wilt instellen? - + Show help message Toon helpbericht - + Show the help message at the beginning in the capture mode. Toon het helpbericht aan het begin in de vastlegmodus. - + Show the side panel button - + Show the side panel toggle button in the capture mode. - - + + Show desktop notifications Bureaubladmeldingen weergeven - + Show tray icon Pictogram in het systeemvak weergeven - + Show the systemtray icon Toon het systeemvakpictogram - + Configuration File Configuratie bestand - + Export Exporteren - + Reset Standaardwaarden - + Launch at startup Automatisch opstarten - - + + Launch Flameshot Start Flameshot - + Show welcome message on launch - + Close application after capture @@ -443,58 +443,58 @@ Druk op de spatiebalk om het zijpaneel te openen. Sluit na schermopname - + Close after taking a screenshot Sluit na het maken van een schermopname - + Copy URL after upload Kopieer URL na upload - + Copy URL and close window after upload Kopieer URL en sluit venster na upload - + Save image after copy - + Save image file after copying it - + Save Path - + Change... - - + + Copy file path after save - + Use fixed path for screenshots to save - + Choose a Folder - + Unable to write to directory. @@ -502,27 +502,27 @@ Druk op de spatiebalk om het zijpaneel te openen. HistoryWidget - + Latest Uploads - + Screenshots history is empty - + Copy URL URL kopiëren - + URL copied to clipboard. URL gekopieerd naar klembord. - + Open in browser @@ -535,9 +535,14 @@ Druk op de spatiebalk om het zijpaneel te openen. - Uploading Image - Afbeelding uploaden + Afbeelding uploaden + + + + + Uploading Image... + @@ -550,40 +555,56 @@ Druk op de spatiebalk om het zijpaneel te openen. - + URL copied to clipboard. URL gekopieerd naar klembord. - + Unable to remove screenshot from the remote storage. - + Network error - + Possibly it doesn't exist anymore - + Do you want to remove screenshot from local history anyway? - + Remove screenshot from history? - + + + Retrieving configuration file with s3 creds... + + + + S3 Creds URL is not found in your configuration file + + + Error + Fout + + + + Unable to get s3 credentials, please check your VPN connection and try again + + ImgS3UploaderTool @@ -601,15 +622,8 @@ Druk op de spatiebalk om het zijpaneel te openen. - - Uploading Image - Afbeelding uploaden - - - - Upload image - + Afbeelding uploaden @@ -627,10 +641,16 @@ Druk op de spatiebalk om het zijpaneel te openen. Schermafdruk gekopieerd naar klembord. + Deleting image... + + + Uploading Image... + + Copy URL @@ -642,6 +662,7 @@ Druk op de spatiebalk om het zijpaneel te openen. URL Openen + Delete image Afbeelding verwijderen @@ -663,7 +684,7 @@ Druk op de spatiebalk om het zijpaneel te openen. ImgurUploader - + Upload to Imgur Uploaden naar Imgur @@ -688,7 +709,7 @@ Druk op de spatiebalk om het zijpaneel te openen. Afbeelding naar klembord. - + Unable to open the URL. Kan URL niet openen. @@ -1064,7 +1085,7 @@ You may need to escape the '#' sign as in '\#FFF' Geen schrijftoegang tot - + URL copied to clipboard. URL gekopieerd naar klembord. @@ -1105,77 +1126,77 @@ You can find me in the system tray. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. - + Toggle side panel - + Resize selection left 1px - + Resize selection right 1px - + Resize selection up 1px - + Resize selection down 1px - + Move selection left 1px - + Move selection right 1px - + Move selection up 1px - + Move selection down 1px - + Quit capture Stop met vastleggen - + Screenshot history - + Capture screen - + Show color picker Toon kleurkiezer - + Change the tool's thickness Wijzig de gereedschapsdikte @@ -1261,22 +1282,22 @@ You can find me in the system tray. ShortcutsWidget - + Hot Keys - + Available shortcuts in the screen capture mode. Beschikbare snelkoppelingen in de schermopnamemodus. - + Description Omschrijving - + Key Toets @@ -1335,92 +1356,92 @@ You can find me in the system tray. Jaar (2000) - + Month Name (jan) Naam van de maand (јаn) - + Month Name (january) Naam van de maand (јаnuari) - + Month (01-12) Maand (01-12) - + Week Day (1-7) Dag van de week (1-7) - + Week (01-53) Week (01-53) - + Day Name (mon) Naam van de dag (ma) - + Day Name (monday) Naam van de dag (maandag) - + Day (01-31) Dag (01-31) - + Day of Month (1-31) Dag van de maand (1-31) - + Day (001-366) Dag (001-366) - + Time (%H-%M-%S) Tijd (%U-%M-%S) - + Time (%H-%M) Tijd (%U-%M) - + Hour (00-23) Uur (00-23) - + Hour (01-12) Uur (01-12) - + Minute (00-59) Minuten (00-59) - + Second (00-59) Seconde (00-59) - + Full Date (%m/%d/%y) Volledige datum (%m/%d/%j) - + Full Date (%Y-%m-%d) Volledige datum (%J-%m-%d) diff --git a/data/translations/Internationalization_pl.ts b/data/translations/Internationalization_pl.ts index 3751eb9b..b472bc9b 100644 --- a/data/translations/Internationalization_pl.ts +++ b/data/translations/Internationalization_pl.ts @@ -75,47 +75,47 @@ CaptureLauncher - + <b>Capture Mode</b> <b>Tryb przechwytywania</b> - + Rectangular Region Zaznaczony obszar - + Full Screen (All Monitors) Pełny ekran (Wszystkie monitory) - + No Delay Bez opóźnienia - + second sekunda - + seconds sekundy - + Take new screenshot Wykonaj nowy zrzut ekranu - + Area: Obszar: - + Delay: Opóźnienie: @@ -207,27 +207,27 @@ Spacja, aby pokazać panel boczny. Controller - + &Take Screenshot &Zrzut ekranu - + &Open Launcher Pokaż &okno - + &Configuration &Konfiguracja - + &About O progr&amie - + &Latest Uploads @@ -236,7 +236,7 @@ Spacja, aby pokazać panel boczny. &Informacje - + &Quit &Wyjdź @@ -326,114 +326,114 @@ Spacja, aby pokazać panel boczny. GeneneralConf - - + + Import Import - - - - + + + + Error Błąd - + Unable to read file. Nie można odczytać pliku. - - + + Unable to write file. Nie można zapisać pliku. - + Save File Zapisz plik - + Confirm Reset Potwierdź Reset - + Are you sure you want to reset the configuration? Czy na pewno chcesz zresetować konfigurację? - + Show help message Pokaż podpowiedzi - + Show the help message at the beginning in the capture mode. Pokaż podpowiedzi na początku trybu przechwytywania. - + Show the side panel button - + Show the side panel toggle button in the capture mode. - - + + Show desktop notifications Pokaż powiadomienia ekranowe - + Show tray icon Pokaż ikonę w trayu - + Show the systemtray icon Pokaż ikonę w zasobniku systemowym - + Configuration File Plik konfiguracyjny - + Export Export - + Reset Reset - + Launch at startup Uruchom podczas startu - - + + Launch Flameshot Uruchom Flameshot - + Show welcome message on launch - + Close application after capture @@ -442,58 +442,58 @@ Spacja, aby pokazać panel boczny. Zamknij po wykonaniu zrzutu - + Close after taking a screenshot Zamknij po wykonaniu zrzutu ekranu - + Copy URL after upload Kopiuj adres URL po wysłaniu - + Copy URL and close window after upload Kopiuj adres URL po wysłaniu i zamknij okno - + Save image after copy - + Save image file after copying it - + Save Path - + Change... - - + + Copy file path after save - + Use fixed path for screenshots to save - + Choose a Folder - + Unable to write to directory. @@ -501,27 +501,27 @@ Spacja, aby pokazać panel boczny. HistoryWidget - + Latest Uploads - + Screenshots history is empty - + Copy URL Kopiuj URL - + URL copied to clipboard. URL skopiowany do schowka. - + Open in browser @@ -534,9 +534,14 @@ Spacja, aby pokazać panel boczny. - Uploading Image - Wysyłanie obrazka + Wysyłanie obrazka + + + + + Uploading Image... + @@ -549,40 +554,56 @@ Spacja, aby pokazać panel boczny. - + URL copied to clipboard. URL skopiowany do schowka. - + Unable to remove screenshot from the remote storage. - + Network error - + Possibly it doesn't exist anymore - + Do you want to remove screenshot from local history anyway? - + Remove screenshot from history? - + + + Retrieving configuration file with s3 creds... + + + + S3 Creds URL is not found in your configuration file + + + Error + Błąd + + + + Unable to get s3 credentials, please check your VPN connection and try again + + ImgS3UploaderTool @@ -600,15 +621,8 @@ Spacja, aby pokazać panel boczny. - - Uploading Image - Wysyłanie obrazka - - - - Upload image - + Wysyłanie obrazka @@ -626,10 +640,16 @@ Spacja, aby pokazać panel boczny. Zrzut ekranu skopiowany do schowka. + Deleting image... + + + Uploading Image... + + Copy URL @@ -641,6 +661,7 @@ Spacja, aby pokazać panel boczny. Otwórz URL + Delete image Usuń obrazek @@ -662,7 +683,7 @@ Spacja, aby pokazać panel boczny. ImgurUploader - + Upload to Imgur Wyślij do Imgur @@ -687,7 +708,7 @@ Spacja, aby pokazać panel boczny. Obrazek do schowka. - + Unable to open the URL. Nie można otworzyć adresu URL. @@ -1063,7 +1084,7 @@ You may need to escape the '#' sign as in '\#FFF' Nie można zapisać w - + URL copied to clipboard. URL skopiowany do schowka. @@ -1108,77 +1129,77 @@ You can find me in the system tray. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. - + Toggle side panel - + Resize selection left 1px - + Resize selection right 1px - + Resize selection up 1px - + Resize selection down 1px - + Move selection left 1px - + Move selection right 1px - + Move selection up 1px - + Move selection down 1px - + Quit capture Zakończ przechwytywanie - + Screenshot history - + Capture screen - + Show color picker Pokaż próbnik kolorów - + Change the tool's thickness Zmień grubość narzędzia @@ -1264,22 +1285,22 @@ You can find me in the system tray. ShortcutsWidget - + Hot Keys - + Available shortcuts in the screen capture mode. Dostępne skróty w trybie przechwytywania obrazu. - + Description Działanie - + Key Klawisz @@ -1338,92 +1359,92 @@ You can find me in the system tray. Rok (2000) - + Month Name (jan) Nazwa miesiąca (cze) - + Month Name (january) Nazwa miesiąca (czerwiec) - + Month (01-12) Miesiąc (01-12) - + Week Day (1-7) Dzień tygodnia (1-7) - + Week (01-53) Tydzień (01-53) - + Day Name (mon) Nazwa dniaa (pią) - + Day Name (monday) Nazwa dnia (piątek) - + Day (01-31) Dzień (01-31) - + Day of Month (1-31) Dzień miesiąca (1-31) - + Day (001-366) Dzień (001-366) - + Time (%H-%M-%S) Czas (%H-%M-%S) - + Time (%H-%M) Czas (%H-%M) - + Hour (00-23) Godzina (00-23) - + Hour (01-12) Godzina (01-12) - + Minute (00-59) Minuta (00-59) - + Second (00-59) Sekunda (00-59) - + Full Date (%m/%d/%y) Data (%m/%d/%y) - + Full Date (%Y-%m-%d) Data (%Y-%m-%d) diff --git a/data/translations/Internationalization_pt_BR.ts b/data/translations/Internationalization_pt_BR.ts index 6120968e..ff29c0aa 100644 --- a/data/translations/Internationalization_pt_BR.ts +++ b/data/translations/Internationalization_pt_BR.ts @@ -75,47 +75,47 @@ CaptureLauncher - + <b>Capture Mode</b> <b>Modo Captura</b> - + Rectangular Region Região Retangular - + Full Screen (All Monitors) Tela Inteira (Todos os Monitores) - + No Delay Sem atraso - + second segundo - + seconds segundos - + Take new screenshot Tirar uma nova screenshot - + Area: Area: - + Delay: Atraso: @@ -208,27 +208,27 @@ Pressione espaço abrir o painel lateral. Controller - + &Take Screenshot &Tirar Screenshot - + &Open Launcher &Abrir carregador - + &Configuration &Configuração - + &About &Sobre - + &Latest Uploads @@ -237,7 +237,7 @@ Pressione espaço abrir o painel lateral. &Informações - + &Quit &Sair @@ -327,114 +327,114 @@ Pressione espaço abrir o painel lateral. GeneneralConf - - + + Import Importar - - - - + + + + Error Erro - + Unable to read file. Não foi possível ler o arquivo. - - + + Unable to write file. Não foi possível escrever no arquivo. - + Save File Salvar Arquivo - + Confirm Reset Confirmar Reset - + Are you sure you want to reset the configuration? Tem certeza que deseja resetar a configuração? - + Show help message Mostrar mensagem de ajuda - + Show the help message at the beginning in the capture mode. Mostrar mensagem de ajuda no início do modo de captura. - + Show the side panel button Mostrar botão no painel lateral - + Show the side panel toggle button in the capture mode. Mostrar altenador do painel lateral. - - + + Show desktop notifications Mostrar notificações de Desktop - + Show tray icon Mostrar ícone de tray - + Show the systemtray icon Mosrar ícone na barra de aplicações - + Configuration File Arquivo de Configurações - + Export Exportar - + Reset Reset - + Launch at startup Iniciar junto com o sistema - - + + Launch Flameshot Iniciar Flameshot - + Show welcome message on launch - + Close application after capture @@ -443,58 +443,58 @@ Pressione espaço abrir o painel lateral. Fechar após captura - + Close after taking a screenshot Fechar após tirar uma screenshot - + Copy URL after upload Copiar URL após upload - + Copy URL and close window after upload Copiar URL e fechar janela após upload - + Save image after copy Salvar imagem após copiar - + Save image file after copying it Salvar imagem após copiar - + Save Path Salvar Caminho - + Change... Alterar... - - + + Copy file path after save - + Use fixed path for screenshots to save - + Choose a Folder Selecione uma pasta - + Unable to write to directory. Não foi possível escrever no diretório. @@ -502,27 +502,27 @@ Pressione espaço abrir o painel lateral. HistoryWidget - + Latest Uploads - + Screenshots history is empty - + Copy URL Copiar URL - + URL copied to clipboard. URL copiada para a área de transferência. - + Open in browser @@ -535,9 +535,14 @@ Pressione espaço abrir o painel lateral. - Uploading Image - Upando Imagem + Upando Imagem + + + + + Uploading Image... + @@ -550,40 +555,56 @@ Pressione espaço abrir o painel lateral. - + URL copied to clipboard. URL copiada para a área de transferência. - + Unable to remove screenshot from the remote storage. - + Network error - + Possibly it doesn't exist anymore - + Do you want to remove screenshot from local history anyway? - + Remove screenshot from history? - + + + Retrieving configuration file with s3 creds... + + + + S3 Creds URL is not found in your configuration file + + + Error + Erro + + + + Unable to get s3 credentials, please check your VPN connection and try again + + ImgS3UploaderTool @@ -601,15 +622,8 @@ Pressione espaço abrir o painel lateral. - - Uploading Image - Upando Imagem - - - - Upload image - + Upando Imagem @@ -627,10 +641,16 @@ Pressione espaço abrir o painel lateral. Screenshot copiada para a área de transferência. + Deleting image... + + + Uploading Image... + + Copy URL @@ -642,6 +662,7 @@ Pressione espaço abrir o painel lateral. Abrir URL + Delete image Deletar imagem @@ -663,7 +684,7 @@ Pressione espaço abrir o painel lateral. ImgurUploader - + Upload to Imgur Upload no Imgur @@ -688,7 +709,7 @@ Pressione espaço abrir o painel lateral. Imagem na área de transferência. - + Unable to open the URL. Não foi possível abrir a URL. @@ -1104,7 +1125,7 @@ Você pode ter que invalidar o sinal '#', por exemplo '\#FFF&apos Por padrão roda Flameshot no background e adiciona um ícone na bandeija para configuração. - + URL copied to clipboard. URL copiada para a área de transferência. @@ -1115,77 +1136,77 @@ You can find me in the system tray. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. - + Toggle side panel - + Resize selection left 1px - + Resize selection right 1px - + Resize selection up 1px - + Resize selection down 1px - + Move selection left 1px - + Move selection right 1px - + Move selection up 1px - + Move selection down 1px - + Quit capture Sair da captura - + Screenshot history - + Capture screen - + Show color picker Mostrar seletor de cores - + Change the tool's thickness Mudar a grossura do pincel @@ -1271,22 +1292,22 @@ You can find me in the system tray. ShortcutsWidget - + Hot Keys - + Available shortcuts in the screen capture mode. Atalhos disponívels na tela de captura. - + Description Descrição - + Key Tecla @@ -1345,92 +1366,92 @@ You can find me in the system tray. Ano (2000) - + Month Name (jan) Nome do mês (jan) - + Month Name (january) Nome do mês (janeiro) - + Month (01-12) Mês (01-12) - + Week Day (1-7) Dia da semana (1-7) - + Week (01-53) Semana (01-53) - + Day Name (mon) Nome do dia (seg) - + Day Name (monday) Nome do dia (segunda) - + Day (01-31) Dia (01-31) - + Day of Month (1-31) Dia do Mês (1-31) - + Day (001-366) Dia (001-366) - + Time (%H-%M-%S) Tempo (%H-%M-%S) - + Time (%H-%M) Tempo (%H-%M) - + Hour (00-23) Hora (00-23) - + Hour (01-12) Hora (01-12) - + Minute (00-59) Minuto (00-59) - + Second (00-59) Segundo (00-59) - + Full Date (%m/%d/%y) Data Completa (%m/%d/%y) - + Full Date (%Y-%m-%d) Data Completa (%Y-%m-%d) diff --git a/data/translations/Internationalization_ru.ts b/data/translations/Internationalization_ru.ts index 77b0a5c0..6743d7c2 100644 --- a/data/translations/Internationalization_ru.ts +++ b/data/translations/Internationalization_ru.ts @@ -75,47 +75,47 @@ CaptureLauncher - + <b>Capture Mode</b> <b>Режим захвата</b> - + Rectangular Region Прямоугольная область - + Full Screen (All Monitors) Весь экран (все мониторы) - + No Delay Без задержки - + second сек - + seconds сек - + Take new screenshot Сделать новый снимок - + Area: Область: - + Delay: Задержка: @@ -208,27 +208,27 @@ Press Space to open the side panel. Controller - + &Take Screenshot &Сделать снимок - + &Open Launcher &Открыть лаунчер - + &Configuration &Настройка - + &About &Информация - + &Latest Uploads Последние загрузки @@ -237,7 +237,7 @@ Press Space to open the side panel. &Информация - + &Quit &Выход @@ -327,114 +327,114 @@ Press Space to open the side panel. GeneneralConf - - + + Import Импорт - - - - + + + + Error Ошибка - + Unable to read file. Не удалось прочитать файл. - - + + Unable to write file. Не удалось записать файл. - + Save File Сохранить файл - + Confirm Reset Подтвердить сброс - + Are you sure you want to reset the configuration? Вы действительно хотите сбросить настройки? - + Show help message Показывать справочное сообщение - + Show the help message at the beginning in the capture mode. Показывать справочное сообщение перед началом захвата экрана. - + Show the side panel button Показывать кнопку боковой панели - + Show the side panel toggle button in the capture mode. Показывать кнопку открытия боковой панели в режиме захвата. - - + + Show desktop notifications Показывать уведомления - + Show tray icon Показывать значок в трее - + Show the systemtray icon Показать значок в системном трее - + Configuration File Файл конфигурации - + Export Экспорт - + Reset Сброс - + Launch at startup Запускать при старте системы - - + + Launch Flameshot Запустить Flameshot - + Show welcome message on launch Показывать приветствие при запуске - + Close application after capture Закрывать приложение после захвата экрана @@ -443,43 +443,43 @@ Press Space to open the side panel. Закрыть после снимка - + Close after taking a screenshot Закрыть после снимка - + Copy URL after upload Копировать URL после загрузки - + Copy URL and close window after upload Копировать URL и закрыть окно после загрузки - + Save image after copy Сохранять изображение после копирования - + Save image file after copying it Сохранять файл изображения после копирования - + Save Path Путь сохранения - + Change... Сменить... - - + + Copy file path after save Скопировать путь к файлу после сохранения @@ -488,17 +488,17 @@ Press Space to open the side panel. Выберите путь по умолчанию для снимков экрана - + Use fixed path for screenshots to save Использовать фиксированный путь для сохранения снимков экрана - + Choose a Folder Выберите папку - + Unable to write to directory. Не удалось записать в папку. @@ -506,27 +506,27 @@ Press Space to open the side panel. HistoryWidget - + Latest Uploads Последние загрузки - + Screenshots history is empty История скриншотов пуста - + Copy URL Скопировать URL - + URL copied to clipboard. URL скопирован в буфер обмена. - + Open in browser Открыть в браузере @@ -539,9 +539,14 @@ Press Space to open the side panel. Загрузка изображения на S3 - Uploading Image - Загрузка изображения + Загрузка изображения + + + + + Uploading Image... + Выгрузка изображения... @@ -554,40 +559,56 @@ Press Space to open the side panel. Удаление изображения... - + URL copied to clipboard. URL скопирован в буфер обмена. - + Unable to remove screenshot from the remote storage. Невозможно удалить снимок экрана из удаленного хранилища. - + Network error Ошибка сети - + Possibly it doesn't exist anymore Возможно, его больше не существует - + Do you want to remove screenshot from local history anyway? Вы все равно хотите удалить скриншот из локальной истории? - + Remove screenshot from history? Удалить скриншот из истории? - + + + Retrieving configuration file with s3 creds... + Получение конфигурационного файла с параметрами доступа к s3... + + + S3 Creds URL is not found in your configuration file S3 Creds URL не найден в вашем файле конфигурации + + + Error + Ошибка + + + + Unable to get s3 credentials, please check your VPN connection and try again + Не удалось получить конфигурацию для s3, проверьте свое VPN-соединение и повторите попытку + ImgS3UploaderTool @@ -605,15 +626,12 @@ Press Space to open the side panel. Загрузить изображение в S3 - - Uploading Image - Загрузка изображения + Загрузка изображения - Upload image - Загрузить изображение + Загрузить изображение @@ -631,10 +649,16 @@ Press Space to open the side panel. Снимок скопирован в буфер обмена. + Deleting image... Удаление изображения... + + + Uploading Image... + Выгрузка изображения... + Copy URL @@ -646,6 +670,7 @@ Press Space to open the side panel. Открыть URL + Delete image Удалить изображение @@ -671,7 +696,7 @@ Press Space to open the side panel. ImgurUploader - + Upload to Imgur Загрузить в Imgur @@ -696,7 +721,7 @@ Press Space to open the side panel. Изображение в буфер обмена. - + Unable to open the URL. Не удалось открыть URL. @@ -1078,7 +1103,7 @@ You may need to escape the '#' sign as in '\#FFF' Не удалось сохранить - + URL copied to clipboard. URL скопирован в буфер обмена. @@ -1120,77 +1145,77 @@ You can find me in the system tray. Вы можете найти меня в системном трее. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. Привет я тут! Щелкните значок на панели задач, чтобы сделать снимок экрана, или щелкните правой кнопкой, чтобы увидеть дополнительные параметры. - + Toggle side panel Вызвать/спрятать боковую панель - + Resize selection left 1px Изменить размер выделения влево на 1 пиксель - + Resize selection right 1px Изменить размер выделения вправо на 1 пиксель - + Resize selection up 1px Изменить размер выделения вверх на 1 пиксель - + Resize selection down 1px Изменить размер выделения вниз на 1 пиксель - + Move selection left 1px Переместить выделение влево на 1 пиксель - + Move selection right 1px Переместить выделение вправо на 1 пиксель - + Move selection up 1px Переместить выделение вверх на 1 пиксель - + Move selection down 1px Переместить выделение вниз на 1 пиксель - + Quit capture Выйти из захвата экрана - + Screenshot history История скриншотов - + Capture screen Захватить экран - + Show color picker Показать выбор цвета - + Change the tool's thickness Изменить толщину инструмента @@ -1276,22 +1301,22 @@ You can find me in the system tray. ShortcutsWidget - + Hot Keys Горячие клавиши - + Available shortcuts in the screen capture mode. Доступные горячие клавиши в режиме захвата экрана. - + Description Описание - + Key Клавиша @@ -1350,92 +1375,92 @@ You can find me in the system tray. Год (2000) - + Month Name (jan) Название месяца (янв) - + Month Name (january) Название месяца (январь) - + Month (01-12) Месяц (01-12) - + Week Day (1-7) День недели (1-7) - + Week (01-53) Неделя (01-53) - + Day Name (mon) День недели (пн) - + Day Name (monday) День недели (понедельник) - + Day (01-31) День (01-31) - + Day of Month (1-31) День месяца (1-31) - + Day (001-366) День (001-366) - + Time (%H-%M-%S) Время (%H-%M-%S) - + Time (%H-%M) Время (%H-%M) - + Hour (00-23) Час (00-23) - + Hour (01-12) Час (01-12) - + Minute (00-59) Минута (00-59) - + Second (00-59) Секунда (00-59) - + Full Date (%m/%d/%y) Полная дата (%m/%d/%y) - + Full Date (%Y-%m-%d) Полная дата (%Y-%m-%d) diff --git a/data/translations/Internationalization_sk.ts b/data/translations/Internationalization_sk.ts index c5917398..7333366e 100644 --- a/data/translations/Internationalization_sk.ts +++ b/data/translations/Internationalization_sk.ts @@ -75,47 +75,47 @@ CaptureLauncher - + <b>Capture Mode</b> <b>Režim zachytávania</b> - + Rectangular Region Pravouhlá oblasť - + Full Screen (All Monitors) Celá obrazovka (všetky monitory) - + No Delay Bez oneskorenia - + second sekunda - + seconds sekundy - + Take new screenshot Zachytiť novú snímku - + Area: Oblasť: - + Delay: Oneskorenie: @@ -208,27 +208,27 @@ Stlačte medzerník pre otvorenie postranného panelu. Controller - + &Take Screenshot &Vytvoriť snímku - + &Open Launcher - + &Configuration &Konfigurácia - + &About O &programe - + &Latest Uploads @@ -237,7 +237,7 @@ Stlačte medzerník pre otvorenie postranného panelu. &Informácie - + &Quit &Ukončiť @@ -327,114 +327,114 @@ Stlačte medzerník pre otvorenie postranného panelu. GeneneralConf - - + + Import Importovať - - - - + + + + Error Chyba - + Unable to read file. Zlyhalo čítanie súboru. - - + + Unable to write file. Zlyhal zápis do súboru. - + Save File Uložiť súbor - + Confirm Reset Potvrdiť Reset - + Are you sure you want to reset the configuration? Naozaj si želáte resetovať aktuálnu konfiguráciu? - + Show help message Zobraziť nápovedu - + Show the help message at the beginning in the capture mode. Zobraziť nápovedu na začiatku počas režimu zachytávania obrazovky. - + Show the side panel button Zobraziť tlačidlo na postrannom paneli - + Show the side panel toggle button in the capture mode. V režime zachytávania zobrazovať tlačidlo na postrannom paneli. - - + + Show desktop notifications Zobraziť systémové upozornenia - + Show tray icon Zobraziť stavovú ikonu - + Show the systemtray icon Zobraziť ikonu v stavovej oblasti - + Configuration File Súbor s konfiguráciou - + Export Exportovať - + Reset Resetovať - + Launch at startup Spúšťať pri štarte - - + + Launch Flameshot Spustiť Flameshot - + Show welcome message on launch - + Close application after capture @@ -443,58 +443,58 @@ Stlačte medzerník pre otvorenie postranného panelu. Zavrieť po vytvorení snímky - + Close after taking a screenshot Zatvoriť po vytvorení snímky obrazovky - + Copy URL after upload Kopírovať URL po uploade - + Copy URL and close window after upload Po nahratí skopírovať URL a zavrieť okno - + Save image after copy Uložiť obrázok po kopírovaní - + Save image file after copying it Uložiť obrázok so súborom po jeho skopírovaní - + Save Path Cesta pre ukladanie - + Change... Zmeniť... - - + + Copy file path after save - + Use fixed path for screenshots to save - + Choose a Folder Vyberte priečinok - + Unable to write to directory. Zápis do adresára nie je možný. @@ -502,27 +502,27 @@ Stlačte medzerník pre otvorenie postranného panelu. HistoryWidget - + Latest Uploads - + Screenshots history is empty - + Copy URL Kopírovať URL - + URL copied to clipboard. URL skopírovaná do schránky. - + Open in browser @@ -535,9 +535,14 @@ Stlačte medzerník pre otvorenie postranného panelu. - Uploading Image - Nahrávam obrázok + Nahrávam obrázok + + + + + Uploading Image... + @@ -550,40 +555,56 @@ Stlačte medzerník pre otvorenie postranného panelu. - + URL copied to clipboard. URL skopírovaná do schránky. - + Unable to remove screenshot from the remote storage. - + Network error - + Possibly it doesn't exist anymore - + Do you want to remove screenshot from local history anyway? - + Remove screenshot from history? - + + + Retrieving configuration file with s3 creds... + + + + S3 Creds URL is not found in your configuration file + + + Error + Chyba + + + + Unable to get s3 credentials, please check your VPN connection and try again + + ImgS3UploaderTool @@ -601,15 +622,8 @@ Stlačte medzerník pre otvorenie postranného panelu. - - Uploading Image - Nahrávam obrázok - - - - Upload image - + Nahrávam obrázok @@ -627,10 +641,16 @@ Stlačte medzerník pre otvorenie postranného panelu. Snímka obrazovky bola skopírovaná do schránky. + Deleting image... + + + Uploading Image... + + Copy URL @@ -642,6 +662,7 @@ Stlačte medzerník pre otvorenie postranného panelu. Otvoriť URL + Delete image Vymazať obrázok @@ -663,7 +684,7 @@ Stlačte medzerník pre otvorenie postranného panelu. ImgurUploader - + Upload to Imgur Nahrať na Imgur @@ -688,7 +709,7 @@ Stlačte medzerník pre otvorenie postranného panelu. Obrázok do schránky. - + Unable to open the URL. Nepodarilo sa otvoriť URL. @@ -1070,7 +1091,7 @@ Možno budete musieť napísať pred '#' opačnú lomku, teda '\# Chyba pri ukladaní - + URL copied to clipboard. URL skopírovaná do schránky. @@ -1115,77 +1136,77 @@ You can find me in the system tray. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. - + Toggle side panel - + Resize selection left 1px - + Resize selection right 1px - + Resize selection up 1px - + Resize selection down 1px - + Move selection left 1px - + Move selection right 1px - + Move selection up 1px - + Move selection down 1px - + Quit capture Ukončiť zachytávanie obrazovky - + Screenshot history - + Capture screen - + Show color picker Zobraziť dialóg na výber farby - + Change the tool's thickness Zmena hrúbky nástroja @@ -1271,22 +1292,22 @@ You can find me in the system tray. ShortcutsWidget - + Hot Keys - + Available shortcuts in the screen capture mode. Dostupné klávesové skratky v režime zachytávania obrazovky. - + Description Popis - + Key Kláves @@ -1345,92 +1366,92 @@ You can find me in the system tray. Rok (2000) - + Month Name (jan) Meno mesiaca (jan) - + Month Name (january) Meno mesiaca (január) - + Month (01-12) Mesiac (01-12) - + Week Day (1-7) Deň v týždni (1-7) - + Week (01-53) Týždeň (01-53) - + Day Name (mon) Meno dňa (pon) - + Day Name (monday) Meno dňa (pondelok) - + Day (01-31) Deň (01-31) - + Day of Month (1-31) Deň v mesiaci (1-31) - + Day (001-366) Deň (001-366) - + Time (%H-%M-%S) Čas (%H-%M-%S) - + Time (%H-%M) Čas (%H-%M) - + Hour (00-23) Hodina (00-23) - + Hour (01-12) Hodina (01-12) - + Minute (00-59) Minúta (00-59) - + Second (00-59) Sekunda (00-59) - + Full Date (%m/%d/%y) Celý dátum (%m/%d/%y) - + Full Date (%Y-%m-%d) Celý dátum (%Y-%m-%d) diff --git a/data/translations/Internationalization_sr_SP.ts b/data/translations/Internationalization_sr_SP.ts index 707a544e..202f3957 100644 --- a/data/translations/Internationalization_sr_SP.ts +++ b/data/translations/Internationalization_sr_SP.ts @@ -75,47 +75,47 @@ CaptureLauncher - + <b>Capture Mode</b> - + Rectangular Region - + Full Screen (All Monitors) - + No Delay - + second - + seconds - + Take new screenshot - + Area: - + Delay: @@ -208,27 +208,27 @@ Press Space to open the side panel. Controller - + &Take Screenshot &Направи снимак екрана - + &Open Launcher - + &Configuration &Подешавања - + &About - + &Latest Uploads @@ -237,7 +237,7 @@ Press Space to open the side panel. Ин&формације - + &Quit &Излаз @@ -327,170 +327,170 @@ Press Space to open the side panel. GeneneralConf - - + + Import Увоз - - - - + + + + Error Грешка - + Unable to read file. Нисам успео да прочитам датотеку. - - + + Unable to write file. Нисам успео да сачувам датотеку. - + Save File Сачувај датотеку - + Confirm Reset Потврда поништавања - + Are you sure you want to reset the configuration? Да ли сте сигурни да желите да поништите сва прилагођена подешавања? - + Show help message Приказуј поруку са упутством - + Show the help message at the beginning in the capture mode. Приказуј поруку са кратким упутством на почетку снимања екрана. - + Show the side panel button - + Show the side panel toggle button in the capture mode. - - + + Show desktop notifications Користи системска обавештења - + Show tray icon Иконица на системској полици - + Show the systemtray icon Приказуј иконицу на системској полици - + Configuration File Датотека са подешавањима - + Export Извоз - + Reset Поништи - + Launch at startup Покрени на почетку - - + + Launch Flameshot Покрени Flameshot - + Show welcome message on launch - + Close application after capture - + Close after taking a screenshot - + Copy URL after upload - + Copy URL and close window after upload - + Save image after copy - + Save image file after copying it - + Save Path - + Change... - - + + Copy file path after save - + Use fixed path for screenshots to save - + Choose a Folder - + Unable to write to directory. @@ -498,27 +498,27 @@ Press Space to open the side panel. HistoryWidget - + Latest Uploads - + Screenshots history is empty - + Copy URL Запамти интернет адресу - + URL copied to clipboard. Интернет адреса је сачувана у привременој меморији. - + Open in browser @@ -531,9 +531,14 @@ Press Space to open the side panel. - Uploading Image - Објављујем слику + Објављујем слику + + + + + Uploading Image... + @@ -546,40 +551,56 @@ Press Space to open the side panel. - + URL copied to clipboard. Интернет адреса је сачувана у привременој меморији. - + Unable to remove screenshot from the remote storage. - + Network error - + Possibly it doesn't exist anymore - + Do you want to remove screenshot from local history anyway? - + Remove screenshot from history? - + + + Retrieving configuration file with s3 creds... + + + + S3 Creds URL is not found in your configuration file + + + Error + Грешка + + + + Unable to get s3 credentials, please check your VPN connection and try again + + ImgS3UploaderTool @@ -597,15 +618,8 @@ Press Space to open the side panel. - - Uploading Image - Објављујем слику - - - - Upload image - + Објављујем слику @@ -623,10 +637,16 @@ Press Space to open the side panel. Слика је сачувана у привременој меморији. + Deleting image... + + + Uploading Image... + + Copy URL @@ -638,6 +658,7 @@ Press Space to open the side panel. Посети интернет адресу + Delete image Избриши слику @@ -659,7 +680,7 @@ Press Space to open the side panel. ImgurUploader - + Upload to Imgur Објави на Imgur @@ -684,7 +705,7 @@ Press Space to open the side panel. Сачувај у привремену меморију. - + Unable to open the URL. Нисам успео да посетим интернет адресу. @@ -1052,7 +1073,7 @@ You may need to escape the '#' sign as in '\#FFF' Нисам успео са сачувам - + URL copied to clipboard. Интернет адреса је сачувана у привременој меморији. @@ -1093,77 +1114,77 @@ You can find me in the system tray. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. - + Toggle side panel - + Resize selection left 1px - + Resize selection right 1px - + Resize selection up 1px - + Resize selection down 1px - + Move selection left 1px - + Move selection right 1px - + Move selection up 1px - + Move selection down 1px - + Quit capture Излаз из снимача екрана - + Screenshot history - + Capture screen - + Show color picker Прикажи избор боје - + Change the tool's thickness Измени дебљину линије алата @@ -1250,22 +1271,22 @@ You can find me in the system tray. ShortcutsWidget - + Hot Keys - + Available shortcuts in the screen capture mode. Доступне пречице у моду снимка екрана. - + Description Опис - + Key Тастер @@ -1324,92 +1345,92 @@ You can find me in the system tray. Година (2000) - + Month Name (jan) Име месеца (јан) - + Month Name (january) Име месеца (јануар) - + Month (01-12) Месец (01-12) - + Week Day (1-7) Дани у недељи (1-7) - + Week (01-53) Недеља (01-53) - + Day Name (mon) Дан у недељи (пон) - + Day Name (monday) Дан у недељи (понедељак) - + Day (01-31) Дан (01-31) - + Day of Month (1-31) Дан месеца (1-31) - + Day (001-366) Дан (001-366) - + Time (%H-%M-%S) Време (%H-%M-%S) - + Time (%H-%M) Време (%H-%M) - + Hour (00-23) Сат (00-23) - + Hour (01-12) Сат (01-12) - + Minute (00-59) Минута (00-59) - + Second (00-59) Секунда (00-59) - + Full Date (%m/%d/%y) Комплетан датум (%m/%d/%y) - + Full Date (%Y-%m-%d) Комплетан датум (%Y-%m-%d) diff --git a/data/translations/Internationalization_sv_SE.ts b/data/translations/Internationalization_sv_SE.ts index 8d0eff1e..4c06d048 100644 --- a/data/translations/Internationalization_sv_SE.ts +++ b/data/translations/Internationalization_sv_SE.ts @@ -75,47 +75,47 @@ CaptureLauncher - + <b>Capture Mode</b> - + Rectangular Region - + Full Screen (All Monitors) - + No Delay - + second - + seconds - + Take new screenshot - + Area: - + Delay: @@ -208,27 +208,27 @@ Tryck Space för att öppna sidopanelen. Controller - + &Take Screenshot &Ta skärmdump - + &Open Launcher - + &Configuration &Konfiguration - + &About - + &Latest Uploads @@ -237,7 +237,7 @@ Tryck Space för att öppna sidopanelen. &Information - + &Quit &Avsluta @@ -327,170 +327,170 @@ Tryck Space för att öppna sidopanelen. GeneneralConf - - + + Import Importera - - - - + + + + Error Fel - + Unable to read file. Kunde inte läsa filen. - - + + Unable to write file. Kunde inte skriva till filen. - + Save File Spara fil - + Confirm Reset Bekräfta återställning - + Are you sure you want to reset the configuration? Är du säker på att du vill återställa konfigurationen? - + Show help message Visa hjälpmeddelande - + Show the help message at the beginning in the capture mode. Visa hjälpmeddelande vid början av skärmklippsläge. - + Show the side panel button - + Show the side panel toggle button in the capture mode. - - + + Show desktop notifications Visa skrivbordsnotifieringar - + Show tray icon Visa ikon i systemfältet - + Show the systemtray icon Visa ikon i systemfältet - + Configuration File Konfigurationsfil - + Export Exportera - + Reset Återställ - + Launch at startup Starta vid uppstart - - + + Launch Flameshot Starta Flameshot - + Show welcome message on launch - + Close application after capture - + Close after taking a screenshot - + Copy URL after upload - + Copy URL and close window after upload - + Save image after copy - + Save image file after copying it - + Save Path - + Change... - - + + Copy file path after save - + Use fixed path for screenshots to save - + Choose a Folder - + Unable to write to directory. @@ -498,27 +498,27 @@ Tryck Space för att öppna sidopanelen. HistoryWidget - + Latest Uploads - + Screenshots history is empty - + Copy URL Kopiera URL - + URL copied to clipboard. URL kopierad till klippbord. - + Open in browser @@ -531,9 +531,14 @@ Tryck Space för att öppna sidopanelen. - Uploading Image - Laddar upp bild + Laddar upp bild + + + + + Uploading Image... + @@ -546,40 +551,56 @@ Tryck Space för att öppna sidopanelen. - + URL copied to clipboard. URL kopierad till klippbord. - + Unable to remove screenshot from the remote storage. - + Network error - + Possibly it doesn't exist anymore - + Do you want to remove screenshot from local history anyway? - + Remove screenshot from history? - + + + Retrieving configuration file with s3 creds... + + + + S3 Creds URL is not found in your configuration file + + + Error + Fel + + + + Unable to get s3 credentials, please check your VPN connection and try again + + ImgS3UploaderTool @@ -597,15 +618,8 @@ Tryck Space för att öppna sidopanelen. - - Uploading Image - Laddar upp bild - - - - Upload image - + Laddar upp bild @@ -623,10 +637,16 @@ Tryck Space för att öppna sidopanelen. Skärmklipp kopierat till klippbord. + Deleting image... + + + Uploading Image... + + Copy URL @@ -638,6 +658,7 @@ Tryck Space för att öppna sidopanelen. Öppna URL + Delete image Radera bild @@ -659,7 +680,7 @@ Tryck Space för att öppna sidopanelen. ImgurUploader - + Upload to Imgur Ladda upp till Imgur @@ -684,7 +705,7 @@ Tryck Space för att öppna sidopanelen. Bild till klippbord. - + Unable to open the URL. Kunde inte öppna URL. @@ -1086,7 +1107,7 @@ You may need to escape the '#' sign as in '\#FFF' - + URL copied to clipboard. URL kopierad till klippbord. @@ -1097,77 +1118,77 @@ You can find me in the system tray. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. - + Toggle side panel - + Resize selection left 1px - + Resize selection right 1px - + Resize selection up 1px - + Resize selection down 1px - + Move selection left 1px - + Move selection right 1px - + Move selection up 1px - + Move selection down 1px - + Quit capture Stäng skärmavbildning - + Screenshot history - + Capture screen - + Show color picker Visa färgväljare - + Change the tool's thickness Ändra verktygets tjocklek @@ -1253,22 +1274,22 @@ You can find me in the system tray. ShortcutsWidget - + Hot Keys - + Available shortcuts in the screen capture mode. Tillgängliga kortkommandon i skärmklippsläge. - + Description Beskrivning - + Key Tangent @@ -1327,92 +1348,92 @@ You can find me in the system tray. År (2000) - + Month Name (jan) Månad namn (jan) - + Month Name (january) Månad namn (januari) - + Month (01-12) Månad (01-12) - + Week Day (1-7) Veckodag (1-7) - + Week (01-53) Vecka (1-53) - + Day Name (mon) Dag namn (mån) - + Day Name (monday) Dag namn (måndag) - + Day (01-31) Dag (01-31) - + Day of Month (1-31) Dag i månad (1-31) - + Day (001-366) Dag (001-366) - + Time (%H-%M-%S) Tid (%H-%M-%S) - + Time (%H-%M) Tid (%H-%M) - + Hour (00-23) Timme (00-23) - + Hour (01-12) Timme (01-12) - + Minute (00-59) Minut (00-59) - + Second (00-59) Sekund (00-59) - + Full Date (%m/%d/%y) Fullständingt datum (%m/%d/%y) - + Full Date (%Y-%m-%d) Fullständigt datum (%Y-%m-%d) diff --git a/data/translations/Internationalization_tr.ts b/data/translations/Internationalization_tr.ts index 3ca1ca6c..a6170d74 100644 --- a/data/translations/Internationalization_tr.ts +++ b/data/translations/Internationalization_tr.ts @@ -75,47 +75,47 @@ CaptureLauncher - + <b>Capture Mode</b> - + Rectangular Region - + Full Screen (All Monitors) - + No Delay - + second - + seconds - + Take new screenshot - + Area: - + Delay: @@ -208,27 +208,27 @@ Yan paneli açmak için Boşluk tuşuna basın. Controller - + &Take Screenshot &Ekran Resmi Al - + &Open Launcher - + &Configuration &Ayarlar - + &About - + &Latest Uploads @@ -237,7 +237,7 @@ Yan paneli açmak için Boşluk tuşuna basın. &Bilgi - + &Quit &Çıkış @@ -327,170 +327,170 @@ Yan paneli açmak için Boşluk tuşuna basın. GeneneralConf - - + + Import Dışa aktar - - - - + + + + Error Hata - + Unable to read file. Dosya okunamıyor. - - + + Unable to write file. Dosya yazılamıyor. - + Save File Dosyayı Kaydet - + Confirm Reset Sıfırlamayı Onayla - + Are you sure you want to reset the configuration? Ayarları sıfırlamak istediğinizden emin misiniz? - + Show help message Yardım mesajını göster - + Show the help message at the beginning in the capture mode. Yakalama modunda başında yardım mesajını gösterin. - + Show the side panel button - + Show the side panel toggle button in the capture mode. - - + + Show desktop notifications Masaüstü bildirimlerini göster - + Show tray icon Tepsi simgesini göster - + Show the systemtray icon Sistem tepsisi simgesini göster - + Configuration File Yapılandırma Dosyası - + Export Dışa aktar - + Reset Sıfırla - + Launch at startup Başlangıçta başlatın - - + + Launch Flameshot Flameshot'ı başlat - + Show welcome message on launch - + Close application after capture - + Close after taking a screenshot - + Copy URL after upload - + Copy URL and close window after upload - + Save image after copy - + Save image file after copying it - + Save Path - + Change... - - + + Copy file path after save - + Use fixed path for screenshots to save - + Choose a Folder - + Unable to write to directory. @@ -498,27 +498,27 @@ Yan paneli açmak için Boşluk tuşuna basın. HistoryWidget - + Latest Uploads - + Screenshots history is empty - + Copy URL URL Kopyala - + URL copied to clipboard. URL panoya kopyalandı. - + Open in browser @@ -531,9 +531,14 @@ Yan paneli açmak için Boşluk tuşuna basın. - Uploading Image - Resim Yükleniyor + Resim Yükleniyor + + + + + Uploading Image... + @@ -546,40 +551,56 @@ Yan paneli açmak için Boşluk tuşuna basın. - + URL copied to clipboard. URL panoya kopyalandı. - + Unable to remove screenshot from the remote storage. - + Network error - + Possibly it doesn't exist anymore - + Do you want to remove screenshot from local history anyway? - + Remove screenshot from history? - + + + Retrieving configuration file with s3 creds... + + + + S3 Creds URL is not found in your configuration file + + + Error + Hata + + + + Unable to get s3 credentials, please check your VPN connection and try again + + ImgS3UploaderTool @@ -597,15 +618,8 @@ Yan paneli açmak için Boşluk tuşuna basın. - - Uploading Image - Resim Yükleniyor - - - - Upload image - + Resim Yükleniyor @@ -623,10 +637,16 @@ Yan paneli açmak için Boşluk tuşuna basın. Ekran görüntüsü panoya kopyalandı. + Deleting image... + + + Uploading Image... + + Copy URL @@ -638,6 +658,7 @@ Yan paneli açmak için Boşluk tuşuna basın. URL Aç + Delete image Resmi sil @@ -659,7 +680,7 @@ Yan paneli açmak için Boşluk tuşuna basın. ImgurUploader - + Upload to Imgur Imgur'a yükle @@ -684,7 +705,7 @@ Yan paneli açmak için Boşluk tuşuna basın. Resim Pano'ya. - + Unable to open the URL. URL açılamıyor. @@ -1052,7 +1073,7 @@ You may need to escape the '#' sign as in '\#FFF' Yazma mümkün değil - + URL copied to clipboard. URL panoya kopyalandı. @@ -1093,77 +1114,77 @@ You can find me in the system tray. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. - + Toggle side panel - + Resize selection left 1px - + Resize selection right 1px - + Resize selection up 1px - + Resize selection down 1px - + Move selection left 1px - + Move selection right 1px - + Move selection up 1px - + Move selection down 1px - + Quit capture Çıkış - + Screenshot history - + Capture screen - + Show color picker Renk seçici göster - + Change the tool's thickness Araç kalınlığını değiştirin @@ -1249,22 +1270,22 @@ You can find me in the system tray. ShortcutsWidget - + Hot Keys - + Available shortcuts in the screen capture mode. Ekran yakalama modunda kullanılabilir kısayollar. - + Description Tanım - + Key Anahtar @@ -1323,92 +1344,92 @@ You can find me in the system tray. Yıl (2000) - + Month Name (jan) Ay Adı (Oca) - + Month Name (january) Ay Adı (Ocak) - + Month (01-12) Ay (01-12) - + Week Day (1-7) Haftanın Günü (1-7) - + Week (01-53) Hafta (01-53) - + Day Name (mon) Gün Adı (pzt) - + Day Name (monday) Gün Adı (pazartesi) - + Day (01-31) Gün (01-31) - + Day of Month (1-31) Ayın Günü (1-31) - + Day (001-366) Gün (001-366) - + Time (%H-%M-%S) - + Time (%H-%M) - + Hour (00-23) Saat (00-23) - + Hour (01-12) Saat (01-12) - + Minute (00-59) Dakika (00-59) - + Second (00-59) Saniye (00-59) - + Full Date (%m/%d/%y) Tam Tarih (%d/%m/%y) - + Full Date (%Y-%m-%d) Tam Tarih (%d-%m-%Y) diff --git a/data/translations/Internationalization_uk.ts b/data/translations/Internationalization_uk.ts index 992235ca..b23fc6a1 100644 --- a/data/translations/Internationalization_uk.ts +++ b/data/translations/Internationalization_uk.ts @@ -75,47 +75,47 @@ CaptureLauncher - + <b>Capture Mode</b> <b>Режим захвату</b> - + Rectangular Region Прямокутна область - + Full Screen (All Monitors) Весь екран (всі монітори) - + No Delay Без затримки - + second сек - + seconds сек - + Take new screenshot Зробити новий знімок - + Area: Область: - + Delay: Затримка: @@ -208,27 +208,27 @@ Press Space to open the side panel. Controller - + &Take Screenshot &Зробити знімок - + &Open Launcher & Відкрити лаунчер - + &Configuration &Налаштування - + &About Про - + &Latest Uploads Останні завантаження @@ -237,7 +237,7 @@ Press Space to open the side panel. &Інформація - + &Quit Ви&йти @@ -327,114 +327,114 @@ Press Space to open the side panel. GeneneralConf - - + + Import Імпорт - - - - + + + + Error Помилка - + Unable to read file. Не вдалось прочитати файл. - - + + Unable to write file. Не вдалось записати файл. - + Save File Зберегти файл - + Confirm Reset Підтвердити скидання - + Are you sure you want to reset the configuration? Ви дійсно хочете скинути налаштування? - + Show help message Показувати повідомлення довідки - + Show the help message at the beginning in the capture mode. Показувати повідомлення довідки на початку режиму захоплення. - + Show the side panel button Показувати кнопку бічній панелі - + Show the side panel toggle button in the capture mode. Показувати кнопку відкриття бічної панелі в режимі захоплення. - - + + Show desktop notifications Показувати повідомлення - + Show tray icon Показувати значок на панелі - + Show the systemtray icon Показувати значок на панелі повідомленнь - + Configuration File Файл налаштувань - + Export Експорт - + Reset Скинути - + Launch at startup Запускати при старті системи - - + + Launch Flameshot Запускати Flameshot - + Show welcome message on launch Показувати вітання під час запуску - + Close application after capture Закривати програму після захоплення екрану @@ -443,43 +443,43 @@ Press Space to open the side panel. Закрити після знімка - + Close after taking a screenshot Закрити після знімка - + Copy URL after upload Копіювати URL після завантаження - + Copy URL and close window after upload Копіювати URL і закрити вікно після завантаження - + Save image after copy Зберігати зображення після копіювання - + Save image file after copying it Зберігати файл зображення після копіювання - + Save Path Шлях збереження - + Change... Змінити... - - + + Copy file path after save Скопіювати шлях до файлу після збереження @@ -488,17 +488,17 @@ Press Space to open the side panel. Виберіть шлях за замовчуванням для скріншотів - + Use fixed path for screenshots to save Використовувати фіксований шлях для знімків екрана для збереження - + Choose a Folder Обрати папку - + Unable to write to directory. Не вдалося записати в папку. @@ -506,27 +506,27 @@ Press Space to open the side panel. HistoryWidget - + Latest Uploads Останні завантаження - + Screenshots history is empty Історія знімків екрана порожня - + Copy URL Скопіювати URL - + URL copied to clipboard. URL скопійовано до буферу обміну. - + Open in browser Відкрити в браузері @@ -539,9 +539,14 @@ Press Space to open the side panel. Вивантаження зображення на S3 - Uploading Image - Вивантаження зображення + Вивантаження зображення + + + + + Uploading Image... + Вивантаження зображення... @@ -554,40 +559,56 @@ Press Space to open the side panel. Видалення зображення... - + URL copied to clipboard. URL скопійовано до буферу обміну. - + Unable to remove screenshot from the remote storage. Не вдалося видалити знімок екрана з віддаленого сховища. - + Network error Помилка мережі - + Possibly it doesn't exist anymore Можливо, його вже не існує - + Do you want to remove screenshot from local history anyway? Ви все одно хочете видалити знімок екрана з місцевої історії? - + Remove screenshot from history? Видалити знімок екрана з історії? - + + + Retrieving configuration file with s3 creds... + Отримання конфігураційного файлу з параметрами доступу до s3 ... + + + S3 Creds URL is not found in your configuration file S3 Creds URL не знайдено у вашому файлі конфігурації + + + Error + Помилка + + + + Unable to get s3 credentials, please check your VPN connection and try again + Не вдалося отримати конфігурацію для s3, перевірити своє з'єднання VPN і повторити спробу + ImgS3UploaderTool @@ -605,15 +626,12 @@ Press Space to open the side panel. Вивантажити зображення на S3 - - Uploading Image - Вивантаження зображення + Вивантаження зображення - Upload image - Вивантажити зображення + Вивантажити зображення @@ -631,10 +649,16 @@ Press Space to open the side panel. Знімок скопійовано до буферу обміну. + Deleting image... Видалення зображення... + + + Uploading Image... + Вивантаження зображення... + Copy URL @@ -646,6 +670,7 @@ Press Space to open the side panel. Відкрити URL + Delete image Видалити зображення @@ -671,7 +696,7 @@ Press Space to open the side panel. ImgurUploader - + Upload to Imgur Вивантажити до Imgur @@ -696,7 +721,7 @@ Press Space to open the side panel. Зображення до буферу обміну. - + Unable to open the URL. Не вдалось відкрити URL. @@ -1078,7 +1103,7 @@ You may need to escape the '#' sign as in '\#FFF' Не вдалось зберегти - + URL copied to clipboard. URL скопійовано до буферу обміну. @@ -1120,77 +1145,77 @@ You can find me in the system tray. Ви можете знайти мене в системному треї. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. Привіт, я тут! Клацніть піктограму в треї, щоб зробити знімок екрана, або клацніть правою кнопкою, щоб побачити більше опцій. - + Toggle side panel Визвати/сховати бічну панель - + Resize selection left 1px Змінити розмір виділення ліворуч на 1 пікс - + Resize selection right 1px Змінити розмір виділення праворуч на 1 пікс - + Resize selection up 1px Змінити розмір виділення вгору на 1 пікс - + Resize selection down 1px Змінити розмір виділення вниз на 1 пікс - + Move selection left 1px Перемістити виділення вліво на 1 пікс - + Move selection right 1px Перемістити виділення вправо на 1 пікс - + Move selection up 1px Перемістити виділення вгору на 1 пікс - + Move selection down 1px Перемістити виділення вниз на 1 пікс - + Quit capture Вийти із захоплення екрану - + Screenshot history Історія знімків екрану - + Capture screen Захоплення екрану - + Show color picker Показати вибір кольору - + Change the tool's thickness Змінити товщину інструменту @@ -1276,22 +1301,22 @@ You can find me in the system tray. ShortcutsWidget - + Hot Keys Гарячі клавиші - + Available shortcuts in the screen capture mode. Доступні комбінації клавіш у режимі захоплення екрану. - + Description Опис - + Key Клавіша @@ -1350,92 +1375,92 @@ You can find me in the system tray. Рік (2000) - + Month Name (jan) Назва місяця (січ) - + Month Name (january) Назва місяця (січень) - + Month (01-12) Місяць (01-12) - + Week Day (1-7) День тижня (1-7) - + Week (01-53) Тиждень (01-53) - + Day Name (mon) Назва дня тижня (пн) - + Day Name (monday) Назва дня тижня (понеділок) - + Day (01-31) День (01-31) - + Day of Month (1-31) День місяця (1-31) - + Day (001-366) День (001-366) - + Time (%H-%M-%S) Час (%H-%M-%S) - + Time (%H-%M) Час (%H-%M) - + Hour (00-23) Година (00-23) - + Hour (01-12) Година (01-12) - + Minute (00-59) Хвилина (00-59) - + Second (00-59) Секунда (00-59) - + Full Date (%m/%d/%y) Повна дата (%m/%d/%y) - + Full Date (%Y-%m-%d) Повна дата (%Y-%m-%d) diff --git a/data/translations/Internationalization_zh_CN.ts b/data/translations/Internationalization_zh_CN.ts index c67a276f..8988be30 100644 --- a/data/translations/Internationalization_zh_CN.ts +++ b/data/translations/Internationalization_zh_CN.ts @@ -75,47 +75,47 @@ CaptureLauncher - + <b>Capture Mode</b> <b>捕获模式</b> - + Rectangular Region 方形区域 - + Full Screen (All Monitors) 全屏(所有显示器) - + No Delay 无延迟 - + second - + seconds - + Take new screenshot 获取新屏幕截图 - + Area: 区域: - + Delay: 延迟: @@ -209,27 +209,27 @@ Press Space to open the side panel. Controller - + &Take Screenshot 进行截图(&T) - + &Open Launcher 打开启动器(&O) - + &Configuration 配置(&C) - + &About 关于(&A) - + &Latest Uploads @@ -238,7 +238,7 @@ Press Space to open the side panel. 信息(&I) - + &Quit 退出(&Q) @@ -328,114 +328,114 @@ Press Space to open the side panel. GeneneralConf - + Show help message 显示帮助文档 - + Show the help message at the beginning in the capture mode. 在捕获之前显示帮助信息。 - - + + Show desktop notifications 显示桌面通知 - + Show tray icon 显示托盘图标 - + Show the systemtray icon 显示任务栏图标 - - + + Import 导入 - - - - + + + + Error 错误 - + Unable to read file. 无法读取文件。 - - + + Unable to write file. 无法写入文件。 - + Save File 保存到文件 - + Confirm Reset 确定重置 - + Are you sure you want to reset the configuration? 你确定你想要重置配置? - + Show the side panel button 显示侧边栏按钮 - + Show the side panel toggle button in the capture mode. 在捕获模式下显示侧边栏切换按钮。 - + Configuration File 配置文件 - + Export 导出 - + Reset 重置 - + Launch at startup 开机时启动 - - + + Launch Flameshot 启动 Flameshot - + Show welcome message on launch - + Close application after capture @@ -444,58 +444,58 @@ Press Space to open the side panel. 捕获后关闭 - + Close after taking a screenshot 获取屏幕截图后关闭 - + Copy URL after upload 上传后复制 URL - + Copy URL and close window after upload 上传后复制 URL 并关闭窗口 - + Save image after copy 复制后保存图像 - + Save image file after copying it 复制到剪贴板后保存图像文件 - + Save Path 保存路径 - + Change... 变更… - - + + Copy file path after save - + Use fixed path for screenshots to save - + Choose a Folder 选择文件夹 - + Unable to write to directory. 无法写入目录。 @@ -503,27 +503,27 @@ Press Space to open the side panel. HistoryWidget - + Latest Uploads - + Screenshots history is empty - + Copy URL 复制链接 - + URL copied to clipboard. - + Open in browser @@ -536,9 +536,14 @@ Press Space to open the side panel. - Uploading Image - 正在上传 + 正在上传 + + + + + Uploading Image... + @@ -551,40 +556,56 @@ Press Space to open the side panel. - + URL copied to clipboard. - + Unable to remove screenshot from the remote storage. - + Network error - + Possibly it doesn't exist anymore - + Do you want to remove screenshot from local history anyway? - + Remove screenshot from history? - + + + Retrieving configuration file with s3 creds... + + + + S3 Creds URL is not found in your configuration file + + + Error + 错误 + + + + Unable to get s3 credentials, please check your VPN connection and try again + + ImgS3UploaderTool @@ -602,15 +623,8 @@ Press Space to open the side panel. - - Uploading Image - 正在上传 - - - - Upload image - + 正在上传 @@ -628,10 +642,16 @@ Press Space to open the side panel. 截图复制到剪贴板。 + Deleting image... + + + Uploading Image... + + Copy URL @@ -643,6 +663,7 @@ Press Space to open the side panel. 打开链接 + Delete image 删除图像 @@ -664,7 +685,7 @@ Press Space to open the side panel. ImgurUploader - + Upload to Imgur 上传到Imgur @@ -689,7 +710,7 @@ Press Space to open the side panel. 保存文件到剪贴板。 - + Unable to open the URL. 无法打开此链接。 @@ -1071,7 +1092,7 @@ You may need to escape the '#' sign as in '\#FFF' 无法写入 - + URL copied to clipboard. URL 已复制到剪贴板。 @@ -1116,77 +1137,77 @@ You can find me in the system tray. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. - + Toggle side panel - + Resize selection left 1px - + Resize selection right 1px - + Resize selection up 1px - + Resize selection down 1px - + Move selection left 1px - + Move selection right 1px - + Move selection up 1px - + Move selection down 1px - + Quit capture 退出捕获 - + Screenshot history - + Capture screen - + Show color picker 显示颜色选择器 - + Change the tool's thickness 改变工具的厚度 @@ -1272,22 +1293,22 @@ You can find me in the system tray. ShortcutsWidget - + Hot Keys - + Available shortcuts in the screen capture mode. 屏幕捕捉模式中的可用快捷键。 - + Description 描述 - + Key @@ -1346,92 +1367,92 @@ You can find me in the system tray. 年(2000) - + Month Name (jan) 月(1月 - 12月) - + Month Name (january) 月(一月 - 十二月) - + Month (01-12) 月 (01-12) - + Week Day (1-7) 周内的日(1-7) - + Week (01-53) 周(01-53) - + Day Name (mon) 星期(一 - 七) - + Day Name (monday) 星期(星期一 - 星期日) - + Day (01-31) 天(01-31) - + Day of Month (1-31) 一月中的某天(1-31) - + Day (001-366) 天(001-366) - + Time (%H-%M-%S) 时间(%H-%M-%S) - + Time (%H-%M) 时间(%H-%M) - + Hour (00-23) 小时(00-23) - + Hour (01-12) 小时(01-12) - + Minute (00-59) 分钟(00-59) - + Second (00-59) 秒(00-59) - + Full Date (%m/%d/%y) 完整日期(%m/%d/%y) - + Full Date (%Y-%m-%d) 完整日期(%Y-%m-%d) diff --git a/data/translations/Internationalization_zh_HK.ts b/data/translations/Internationalization_zh_HK.ts index a1d38922..113dfd42 100644 --- a/data/translations/Internationalization_zh_HK.ts +++ b/data/translations/Internationalization_zh_HK.ts @@ -4,12 +4,12 @@ AppLauncher - + App Launcher 應用程式啟動器 - + Choose an app to open the capture 選擇一個程式打開此截圖 @@ -32,18 +32,18 @@ 選擇後保持開啟 - - + + Error 錯誤 - + Unable to launch in terminal. 無法從終端啟動。 - + Unable to write in 無法寫入 @@ -51,12 +51,12 @@ ArrowTool - + Arrow 指針 - + Set the Arrow as the paint tool 選取指針作為繪製工具 @@ -75,47 +75,47 @@ CaptureLauncher - + <b>Capture Mode</b> - + Rectangular Region 矩形區域 - + Full Screen (All Monitors) 满屏(所有顯示器) - + No Delay 無時延 - + second - + seconds - + Take new screenshot 捕獲新截圖 - + Area: - + Delay: @@ -123,12 +123,12 @@ CaptureWidget - + Unable to capture screen 無法捕獲屏幕 - + Select an area with the mouse, or press Esc to exit. Press Enter to capture the screen. Press Right Click to show the color picker. @@ -141,7 +141,7 @@ Press Space to open the side panel. 按Space以打開側方面板。 - + Tool Settings 工具選項 @@ -149,12 +149,12 @@ Press Space to open the side panel. CircleCountTool - + Circle Counter 環狀計數器 - + Add an autoincrementing counter bubble 添加自增計數器 @@ -162,12 +162,12 @@ Press Space to open the side panel. CircleTool - + Circle 環形 - + Set the Circle as the paint tool 選取環形作為繪畫工具 @@ -180,49 +180,64 @@ Press Space to open the side panel. 設定 - + Interface 接口 - + Filename Editor 文檔名稱編輯器 - + General 一般 + + + Shortcuts + + + + + Storage + + Controller - + &Take Screenshot &捕獲截圖 - + &Open Launcher &開啓啓動器 - + &Configuration &設定 - + &About &關於 + + + &Latest Uploads + + &Information &資訊 - + &Quit &結束 @@ -230,12 +245,12 @@ Press Space to open the side panel. CopyTool - + Copy 複製 - + Copy the selection into the clipboard 複製選項到剪貼板 @@ -243,7 +258,7 @@ Press Space to open the side panel. DBusUtils - + Unable to connect via DBus 無法通過 DBus 連接 @@ -251,12 +266,12 @@ Press Space to open the side panel. ExitTool - + Exit 離開 - + Leave the capture screen 離開螢幕擷取 @@ -264,47 +279,47 @@ Press Space to open the side panel. FileNameEditor - + Edit the name of your captures: 編輯您的截圖名稱: - + Edit: 編輯器: - + Preview: 預覽: - + Save 存檔 - + Saves the pattern 儲存樣式 - + Reset 重設 - + Restores the saved pattern 恢復儲存的樣式 - + Clear 清除 - + Deletes the name 刪除這個名稱 @@ -312,215 +327,402 @@ Press Space to open the side panel. GeneneralConf - + Show help message 顯示説明資訊 - + Show the help message at the beginning in the capture mode. 在擷取之前顯示説明資訊。 - - + + Show desktop notifications 顯示桌面通知 - + Show tray icon 顯示託盤圖標 - + Show the systemtray icon 顯示工作列圖標 - - + + Import 導入 - - - - + + + + Error 錯誤 - + Unable to read file. 無法讀取檔案。 - - + + Unable to write file. 無法寫入檔案。 - + Save File 存檔 - + Confirm Reset 確認重設 - + Are you sure you want to reset the configuration? 你確定想要重設? - + Show the side panel button 顯示側邊欄按鈕 - + Show the side panel toggle button in the capture mode. 在截圖模式下顯示側邊欄切換按鈕。 - + Configuration File 設定文檔 - + Export 導出 - + Reset 重設 - + Launch at startup 自動啟動 - + + Launch Flameshot 啓動Flameshot - - Close after capture - 捕獲截圖后關閉 + + Show welcome message on launch + - + + Close application after capture + + + + Close after capture + 捕獲截圖后關閉 + + + Close after taking a screenshot 進行截屏后關閉 - + Copy URL after upload 上載后複製URL - + Copy URL and close window after upload 上載后複製URL並關閉窗口 - + Save image after copy 複製後保存圖像 - + Save image file after copying it 複製圖像檔案后保存 - + Save Path 保存路徑 - + Change... 變更... - + + Use fixed path for screenshots to save + + + + + + Copy file path after save + + + + Choose a Folder 選取檔案集 - + Unable to write to directory. 無法寫入目錄。 + + HistoryWidget + + + Latest Uploads + + + + + Screenshots history is empty + + + + + Copy URL + 複製連結 + + + + URL copied to clipboard. + + + + + Open in browser + + + + + ImgS3Uploader + + + Upload image to S3 + + + + + + Uploading Image... + + + + + Delete image from S3 + + + + + Deleting image... + + + + + URL copied to clipboard. + + + + + Unable to remove screenshot from the remote storage. + + + + + Network error + + + + + Possibly it doesn't exist anymore + + + + + Do you want to remove screenshot from local history anyway? + + + + + Remove screenshot from history? + + + + + + Retrieving configuration file with s3 creds... + + + + + S3 Creds URL is not found in your configuration file + + + + + Error + 錯誤 + + + + Unable to get s3 credentials, please check your VPN connection and try again + + + + + ImgS3UploaderTool + + + Upload the selection to S3 bucket + + + + + ImgUploader + + + Upload image to S3 + + + + + Uploading Image... + + + + + + Delete image + 刪除圖像 + + + + + Deleting image... + + + + + Unable to open the URL. + 無法打開該URL。 + + + + URL copied to clipboard. + + + + + Screenshot copied to clipboard. + 截圖已複製到剪貼板。 + + + + Copy URL + 複製連結 + + + + Open URL + 打開連結 + + + + Image to Clipboard. + 將檔案複製到剪貼簿。 + + + + ImgUploaderTool + + + Image uploader tool + + + ImgurUploader - + Upload to Imgur 上傳到 Imgur - Uploading Image - 正在上傳 + 正在上傳 - Copy URL - 複製連結 + 複製連結 - Open URL - 打開連結 + 打開連結 - Delete image - 刪除圖像 + 刪除圖像 - Image to Clipboard. - 將檔案複製到剪貼簿。 + 將檔案複製到剪貼簿。 - - + Unable to open the URL. 無法打開該URL。 - URL copied to clipboard. - URL已複製到剪貼板。 + URL已複製到剪貼板。 - Screenshot copied to clipboard. - 截圖已複製到剪貼板。 + 截圖已複製到剪貼板。 ImgurUploaderTool - + Image Uploader 上傳圖片 - + Upload the selection to Imgur 上載到 Imgur @@ -528,110 +730,90 @@ Press Space to open the side panel. InfoWindow - + About 關於 - - SPACEBAR - - - - Right Click - 右鍵 + 右鍵 - Mouse Wheel - 滑鼠滑輪 + 滑鼠滑輪 - Move selection 1px - 移動 1px + 移動 1px - Resize selection 1px - 調整大小 1px + 調整大小 1px - Quit capture - 結束擷取 + 結束擷取 - Copy to clipboard - 複製到剪貼簿 + 複製到剪貼簿 - Save selection as a file - 將選擇範圍另存新檔 + 將選擇範圍另存新檔 - Undo the last modification - 復原上次修改 + 復原上次修改 - Toggle visibility of sidebar with options of the selected tool - 使用所選工具選項切換側邊欄可見性 + 使用所選工具選項切換側邊欄可見性 - Show color picker - 顯示顏色選擇器 + 顯示顏色選擇器 - Change the tool's thickness - 改變工具的寬度 + 改變工具的寬度 - Key - + - Description - 描述 + 描述 - + <u><b>License</b></u> <u><b>授權條款</b></u> - + <u><b>Version</b></u> <u><b>版本</b></u> - <u><b>Shortcuts</b></u> - <u><b>快速鍵</b></u> + <u><b>快速鍵</b></u> - Available shortcuts in the screen capture mode. - 螢幕捕獲模式中的可用快捷鍵。 + 螢幕捕獲模式中的可用快捷鍵。 LineTool - + Line 直綫 - + Set the Line as the paint tool 將直綫設定為繪畫工具 @@ -639,12 +821,12 @@ Press Space to open the side panel. MarkerTool - + Marker 標記 - + Set the Marker as the paint tool 將標記設定為繪畫工具 @@ -652,12 +834,12 @@ Press Space to open the side panel. MoveTool - + Move 移動 - + Move the selection area 移動選擇區域 @@ -665,12 +847,12 @@ Press Space to open the side panel. PencilTool - + Pencil 鉛筆 - + Set the Pencil as the paint tool 將鉛筆設定為繪畫工具 @@ -678,12 +860,12 @@ Press Space to open the side panel. PinTool - + Pin Tool 固定工具 - + Pin image on the desktop 將圖像固定到桌面 @@ -691,12 +873,12 @@ Press Space to open the side panel. PixelateTool - + Pixelate 馬賽克工具 - + Set Pixelate as the paint tool 將馬賽克工具設定為繪畫工具 @@ -704,18 +886,18 @@ Press Space to open the side panel. QObject - + Save Error 存檔錯誤 - + Capture saved as 截圖已另存為 - + Capture saved to clipboard. 熒幕捕獲已存儲到剪貼板。 @@ -725,122 +907,132 @@ Press Space to open the side panel. 螢幕捕獲已存儲到剪貼板 - - + + Error trying to save as 嘗試另存新檔時發生錯誤 - - - - - + + Save screenshot + + + + + Capture is saved and copied to the clipboard as + + + + + + + + Unable to connect via DBus 無法透過 DBus 進行連接 - + Powerful yet simple to use screenshot software. - + See - + Capture the entire desktop. 捕獲整個桌面。 - + Open the capture launcher. 開啓捕獲啓動器。 - + Start a manual capture in GUI mode. 在GUi模式下開啓手動捕獲。 - + Configure Configure - + Capture a single screen. 捕獲單一熒幕。 - + Path where the capture will be saved - + Save the capture to the clipboard - + Delay time in milliseconds - + Set the filename pattern - + Enable or disable the trayicon - + Enable or disable run at startup - + Show the help message in the capture mode - + Define the main UI color - + Define the contrast UI color - + Print raw PNG capture - + Define the screen to capture - + default: screen containing the cursor - + Screen number - + Invalid color, this flag supports the following formats: - #RGB (each of R, G, and B is a single hex digit) - #RRGGBB @@ -851,37 +1043,37 @@ You may need to escape the '#' sign as in '\#FFF' - + Invalid delay, it must be higher than 0 - + Invalid screen number, it must be non negative - + Invalid path, it must be a real path in the system - + Invalid value, it must be defined as 'true' or 'false' - + Error 錯誤 - + Unable to write in 無法寫入 - + URL copied to clipboard. 連結已複製到剪貼板。 @@ -891,40 +1083,121 @@ You may need to escape the '#' sign as in '\#FFF' 選項 - + Arguments - + arguments - + Usage 使用 - + options 選項 - - Per default runs Flameshot in the background and adds a tray icon for configuration. + + Per default runs Flameshot in the background and adds a tray icon for configuration. + + + Hi, I'm already running! +You can find me in the system tray. + + + + + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. + + + + + Toggle side panel + + + + + Resize selection left 1px + + + + + Resize selection right 1px + + + + + Resize selection up 1px + + + + + Resize selection down 1px + + + + + Move selection left 1px + + + + + Move selection right 1px + + + + + Move selection up 1px + + + + + Move selection down 1px + + + + + Quit capture + 結束擷取 + + + + Screenshot history + + + + + Capture screen + + + + + Show color picker + 顯示顏色選擇器 + + + + Change the tool's thickness + 改變工具的寬度 + RectangleTool - + Rectangle 矩形 - + Set the Rectangle as the paint tool 將矩形設定為繪畫工具 @@ -932,12 +1205,12 @@ You may need to escape the '#' sign as in '\#FFF' RedoTool - + Redo 重做 - + Redo the next modification 重做下一次修改 @@ -945,12 +1218,12 @@ You may need to escape the '#' sign as in '\#FFF' SaveTool - + Save 儲存 - + Save the capture 儲存螢幕捕獲 @@ -958,7 +1231,7 @@ You may need to escape the '#' sign as in '\#FFF' ScreenGrabber - + Unable to capture screen 無法捕獲螢幕 @@ -966,35 +1239,76 @@ You may need to escape the '#' sign as in '\#FFF' SelectionTool - + Rectangular Selection 矩形選擇 - + Set Selection as the paint tool 將矩形選擇設定為繪畫工具 + + SetShortcutDialog + + + Set Shortcut + + + + + Enter new shortcut to change + + + + + Press Esc to cancel or Backspace to disable the keyboard shortcut. + + + + + ShortcutsWidget + + + Hot Keys + + + + + Available shortcuts in the screen capture mode. + 螢幕捕獲模式中的可用快捷鍵。 + + + + Description + 描述 + + + + Key + + + SidePanelWidget - + Active thickness: 動態寬度: - + Active color: 動態顔色: - + Press ESC to cancel 按ESC以取消 - + Grab Color 選取顔色 @@ -1002,12 +1316,12 @@ You may need to escape the '#' sign as in '\#FFF' SizeIndicatorTool - + Selection Size Indicator 選擇尺寸指示 - + Show the dimensions of the selection (X Y) 顯示選擇的尺寸 (X Y) @@ -1015,107 +1329,107 @@ You may need to escape the '#' sign as in '\#FFF' StrftimeChooserWidget - + Century (00-99) 世紀(00-99) - + Year (00-99) 年(00-99) - + Year (2000) 年(2000) - + Month Name (jan) 月(jan) - + Month Name (january) 月(january) - + Month (01-12) 月(01-12) - + Week Day (1-7) 工作日(1-7) - + Week (01-53) 周(01-53) - + Day Name (mon) 星期(mon) - + Day Name (monday) 星期(diumenge) - + Day (01-31) 日(01-31) - + Day of Month (1-31) 一月中的某日(1-31) - + Day (001-366) 日(001-366) - + Time (%H-%M-%S) 時間(%H-%M-%S) - + Time (%H-%M) 時間(%H-%M) - + Hour (00-23) 小時(00-23) - + Hour (01-12) 小時(01-12) - + Minute (00-59) 分(00-59) - + Second (00-59) 秒(00-59) - + Full Date (%m/%d/%y) 日期(%m/%d/%y) - + Full Date (%Y-%m-%d) 日期(%Y-%m-%d) @@ -1154,12 +1468,12 @@ You may need to escape the '#' sign as in '\#FFF' TextTool - + Text 文本工具 - + Add text to your capture 往您捕獲的截圖中添加文本 @@ -1172,32 +1486,32 @@ You may need to escape the '#' sign as in '\#FFF' UI 顏色編輯器 - + Change the color moving the selectors and see the changes in the preview buttons. 移動顏色選擇並在預覽按鈕檢視。 - + Select a Button to modify it 選擇一個按鈕以修改 - + Main Color 主色 - + Click on this button to set the edition mode of the main color. 點選按鈕設定主色。 - + Contrast Color 對比色 - + Click on this button to set the edition mode of the contrast color. 點選按鈕設定對比色。 @@ -1205,38 +1519,61 @@ You may need to escape the '#' sign as in '\#FFF' UndoTool - + Undo 撤銷 - + Undo the last modification 撤銷上次修改 + + UploadStorageConfig + + + Upload storage + + + + + Imgur storage + + + + + S3 storage (require config.ini file with s3 credentials) + + + UtilityPanel - + Close 關閉 + + + Hide + + VisualsEditor - + Opacity of area outside selection: 選取區域以外的不透明度: - + Button Selection 按鈕選取 - + Select All 全選 diff --git a/data/translations/Internationalization_zh_TW.ts b/data/translations/Internationalization_zh_TW.ts index dcdca448..8cead83d 100644 --- a/data/translations/Internationalization_zh_TW.ts +++ b/data/translations/Internationalization_zh_TW.ts @@ -75,47 +75,47 @@ CaptureLauncher - + <b>Capture Mode</b> - + Rectangular Region - + Full Screen (All Monitors) - + No Delay - + second - + seconds - + Take new screenshot - + Area: - + Delay: @@ -204,27 +204,27 @@ Press Space to open the side panel. Controller - + &Take Screenshot - + &Open Launcher - + &Configuration &設定 - + &About - + &Latest Uploads @@ -233,7 +233,7 @@ Press Space to open the side panel. &資訊 - + &Quit &結束 @@ -323,170 +323,170 @@ Press Space to open the side panel. GeneneralConf - + Show help message 顯示説明資訊 - + Show the help message at the beginning in the capture mode. 在擷取之前顯示説明資訊 - - + + Show desktop notifications 顯示桌面通知 - + Show tray icon 顯示託盤圖示 - + Show the systemtray icon 顯示工作列圖示 - - + + Import 匯入 - - - - + + + + Error 錯誤 - + Unable to read file. 無法讀取檔案 - - + + Unable to write file. 無法寫入檔案 - + Save File 存檔 - + Confirm Reset 確認重設 - + Are you sure you want to reset the configuration? 你確定你想要重設? - + Show the side panel button - + Show the side panel toggle button in the capture mode. - + Configuration File 設定檔 - + Export 匯出 - + Reset 重設 - + Launch at startup 自動啟動 - - + + Launch Flameshot - + Show welcome message on launch - + Close application after capture - + Close after taking a screenshot - + Copy URL after upload - + Copy URL and close window after upload - + Save image after copy - + Save image file after copying it - + Save Path - + Change... - - + + Copy file path after save - + Use fixed path for screenshots to save - + Choose a Folder - + Unable to write to directory. @@ -494,27 +494,27 @@ Press Space to open the side panel. HistoryWidget - + Latest Uploads - + Screenshots history is empty - + Copy URL 複製連結 - + URL copied to clipboard. 連結已複製到剪貼簿 - + Open in browser @@ -527,9 +527,14 @@ Press Space to open the side panel. - Uploading Image - 正在上傳 + 正在上傳 + + + + + Uploading Image... + @@ -542,40 +547,56 @@ Press Space to open the side panel. - + URL copied to clipboard. 連結已複製到剪貼簿 - + Unable to remove screenshot from the remote storage. - + Network error - + Possibly it doesn't exist anymore - + Do you want to remove screenshot from local history anyway? - + Remove screenshot from history? - + + + Retrieving configuration file with s3 creds... + + + + S3 Creds URL is not found in your configuration file + + + Error + 錯誤 + + + + Unable to get s3 credentials, please check your VPN connection and try again + + ImgS3UploaderTool @@ -593,15 +614,8 @@ Press Space to open the side panel. - - Uploading Image - 正在上傳 - - - - Upload image - + 正在上傳 @@ -619,10 +633,16 @@ Press Space to open the side panel. 截圖已複製到剪貼簿 + Deleting image... + + + Uploading Image... + + Copy URL @@ -634,6 +654,7 @@ Press Space to open the side panel. 打開連結 + Delete image @@ -655,7 +676,7 @@ Press Space to open the side panel. ImgurUploader - + Upload to Imgur 上傳到 Imgur @@ -676,7 +697,7 @@ Press Space to open the side panel. 將檔案複製到剪貼簿 - + Unable to open the URL. 無法打開此連結 @@ -1044,7 +1065,7 @@ You may need to escape the '#' sign as in '\#FFF' 無法寫入 - + URL copied to clipboard. 連結已複製到剪貼簿 @@ -1085,77 +1106,77 @@ You can find me in the system tray. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. - + Toggle side panel - + Resize selection left 1px - + Resize selection right 1px - + Resize selection up 1px - + Resize selection down 1px - + Move selection left 1px - + Move selection right 1px - + Move selection up 1px - + Move selection down 1px - + Quit capture 結束擷取 - + Screenshot history - + Capture screen - + Show color picker 顯示顏色選擇器 - + Change the tool's thickness 改變工具的寬度 @@ -1241,22 +1262,22 @@ You can find me in the system tray. ShortcutsWidget - + Hot Keys - + Available shortcuts in the screen capture mode. 螢幕擷取模式中的可用快速鍵 - + Description 描述 - + Key @@ -1315,92 +1336,92 @@ You can find me in the system tray. 年 (2000) - + Month Name (jan) 月 (jul) - + Month Name (january) 月 (juliol) - + Month (01-12) 月 (01-12) - + Week Day (1-7) 平常日 (1-7) - + Week (01-53) 周 (01-53) - + Day Name (mon) 星期 (dg) - + Day Name (monday) 星期 (diumenge) - + Day (01-31) 天 (01-31) - + Day of Month (1-31) 一月中的某天 (1-31) - + Day (001-366) 天 (001-366) - + Time (%H-%M-%S) - + Time (%H-%M) - + Hour (00-23) 小時 (00-23) - + Hour (01-12) 小時 (01-12) - + Minute (00-59) 分鐘 (00-59) - + Second (00-59) 秒 (00-59) - + Full Date (%m/%d/%y) 日期 (%m/%d/%y) - + Full Date (%Y-%m-%d) 日期 (%Y-%m-%d) diff --git a/src/config/uploadstorageconfig.cpp b/src/config/uploadstorageconfig.cpp index bd3d3c6b..d256564e 100644 --- a/src/config/uploadstorageconfig.cpp +++ b/src/config/uploadstorageconfig.cpp @@ -48,8 +48,8 @@ UploadStorageConfig::UploadStorageConfig(QWidget* parent) }); StorageManager storageManager; - if (storageManager.storageLocked()) { - ConfigHandler().setUploadStorage(storageManager.storageDefault()); + if (!storageManager.storageLocked().isEmpty()) { + ConfigHandler().setUploadStorage(storageManager.storageLocked()); storageImgUr->setDisabled(true); storageImgS3->setDisabled(true); } diff --git a/src/tools/storage/imguploader.cpp b/src/tools/storage/imguploader.cpp index 92597dbd..ef76244d 100644 --- a/src/tools/storage/imguploader.cpp +++ b/src/tools/storage/imguploader.cpp @@ -43,13 +43,13 @@ ImgUploader::ImgUploader(const QPixmap& capture, QWidget* parent) : QWidget(parent) , m_pixmap(capture) { - init(tr("Upload image to S3"), tr("Uploading Image")); + init(tr("Upload image to S3"), tr("Uploading Image...")); } ImgUploader::ImgUploader(QWidget* parent) : QWidget(parent) { - init(tr("Upload image"), tr("Uploading Image")); + init(tr("Delete image"), tr("Deleting image...")); } void ImgUploader::init(const QString& title, const QString& label) diff --git a/src/tools/storage/s3/amazon-server-side/doc/README.md b/src/tools/storage/s3/amazon-server-side/doc/README.md index 8e7107f0..146e0d8f 100644 --- a/src/tools/storage/s3/amazon-server-side/doc/README.md +++ b/src/tools/storage/s3/amazon-server-side/doc/README.md @@ -22,7 +22,7 @@ if (m_s3Settings.xApiKey().length() > 0) { QByteArray("X-API-Key"), QByteArray(m_s3Settings.xApiKey().toLocal8Bit())); } -m_NetworkAMGetCreds->get(requestCreds); +m_networkAMGetCreds->get(requestCreds); ``` Shell example: @@ -108,7 +108,7 @@ void ImgS3Uploader::uploadToS3(QJsonDocument& response) QNetworkRequest request(qUrl); // upload - m_NetworkAMUpload->post(request, multiPart); + m_networkAMUpload->post(request, multiPart); } ``` @@ -131,5 +131,5 @@ m_deleteToken = deleteToken; request.setUrl(m_s3Settings.credsUrl().toUtf8() + fileName); request.setRawHeader("X-API-Key", m_s3Settings.xApiKey().toLatin1()); request.setRawHeader("Authorization", "Bearer " + deleteToken.toLatin1()); -m_NetworkAMRemove->deleteResource(request); +m_networkAMRemove->deleteResource(request); ``` diff --git a/src/tools/storage/s3/config-examples/config.ini b/src/tools/storage/s3/config-examples/config.ini new file mode 100644 index 00000000..db7d3df4 --- /dev/null +++ b/src/tools/storage/s3/config-examples/config.ini @@ -0,0 +1,11 @@ +[General] +; Lock storage selection for the enterprise users +; (it will lock to the default storage) +STORAGE_LOCKED=s3 +STORAGE_CONFIG_URL="https://git.example.com/repos/flameshot_config/raw/config.ini" + + +[S3] +S3_URL=https://img.example.com/ +S3_CREDS_URL=https://api.img.example.com/ +S3_X_API_KEY= diff --git a/src/tools/storage/s3/config-examples/config_proxy.ini b/src/tools/storage/s3/config-examples/config_proxy.ini new file mode 100644 index 00000000..979fe04c --- /dev/null +++ b/src/tools/storage/s3/config-examples/config_proxy.ini @@ -0,0 +1,16 @@ +[General] +; PROXY SETTINGS +HTTP_PROXY_HOST=0.0.0.0 +HTTP_PROXY_PORT=3128 + +; No authentification USER and PASSWORD should be empty +;HTTP_PROXY_USER= +;HTTP_PROXY_PASSWORD= + +HTTP_PROXY_TYPE=3 +; Proxy Types (3 is default): +; 0 Proxy is determined based on the application proxy set using setApplicationProxy() +; 1 Socks5 proxying is used +; 3 HTTP transparent proxying is used +; 4 Proxying for HTTP requests only +; 5 Proxying for FTP requests only diff --git a/src/tools/storage/s3/imgs3settings.cpp b/src/tools/storage/s3/imgs3settings.cpp index 351ac9a1..dad9e750 100644 --- a/src/tools/storage/s3/imgs3settings.cpp +++ b/src/tools/storage/s3/imgs3settings.cpp @@ -1,45 +1,187 @@ #include "imgs3settings.h" +#include "src/tools/storage/imgstorages.h" +#include "src/utils/confighandler.h" +#include +#include #include +#include #include +#include +#include +#include +#include +#include #include +#include +#include -ImgS3Settings::ImgS3Settings() +ImgS3Settings::ImgS3Settings(QObject* parent) + : QObject(parent) { + m_proxy = nullptr; + m_networkConfig = nullptr; initSettings(); - // get s3 credentials - m_settings->beginGroup("S3"); - m_credsUrl = m_settings->value("S3_CREDS_URL").toString(); - m_credsUrl = - m_credsUrl + - ((m_credsUrl.length() > 0 && m_credsUrl[m_credsUrl.length() - 1] == '/') - ? "" - : "/") + - S3_API_IMG_PATH; + // get remote config url + if (m_localSettings->contains("STORAGE_CONFIG_URL")) { + m_s3ConfigUrl = + QUrl(m_localSettings->value("STORAGE_CONFIG_URL").toString()); + } else { + // set default value if STORAGE_CONFIG_URL not found in the config.ini + m_s3ConfigUrl = QUrl("https://git.namecheap.net/projects/RND/repos/" + "flameshot_config/raw/config.ini"); + } - m_xApiKey = m_settings->value("S3_X_API_KEY").toString(); - - m_url = m_settings->value("S3_URL").toString(); - m_url = - m_url + - ((m_url.length() > 0 && m_url[m_url.length() - 1] == '/') ? "" : "/"); - - m_settings->endGroup(); + // proxy settings + m_proxyType = -1; + m_proxyHost = QString(); + m_proxyPort = -1; + m_proxyUser = QString(); + m_proxyPassword = QString(); } -QSettings* ImgS3Settings::settings() +void ImgS3Settings::initS3Creds() { - return m_settings; + ConfigHandler configHandler; + m_credsUrl = configHandler.value("S3", "S3_CREDS_URL").toString(); + m_xApiKey = configHandler.value("S3", "S3_X_API_KEY").toString(); + m_url = configHandler.value("S3", "S3_URL").toString(); + normalizeS3Creds(); + updateConfigFromRemote(); } -void ImgS3Settings::initSettings() +bool ImgS3Settings::getConfigRemote(int timeout) +{ + if (!m_url.isEmpty() && !m_credsUrl.isEmpty()) { + updateConfigFromRemote(); + return true; + } + QNetworkAccessManager* networkConfig = new QNetworkAccessManager(this); + if (proxy() != nullptr) { + networkConfig->setProxy(*m_proxy); + } + QNetworkReply* reply = networkConfig->get(QNetworkRequest(m_s3ConfigUrl)); + + QEventLoop loop; + QTimer timer; + timer.setSingleShot(true); + connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); + connect(reply, SIGNAL(readyRead()), &loop, SLOT(quit())); + timer.start(timeout * 1000); // 30 secs. timeout + loop.exec(); + QString data = QString(reply->readAll()); + parseConfigurationData(data); + + delete reply; + delete networkConfig; + + return !data.isEmpty(); +} + +void ImgS3Settings::parseConfigurationData(const QString& data) +{ + // read remote and save to the temporary file + QTemporaryFile file; + file.open(); + QTextStream stream(&file); + stream << data; + stream.flush(); + + // parse and get configuration data + QSettings remoteConfig(file.fileName(), QSettings::IniFormat, this); + remoteConfig.beginGroup("S3"); + m_url = remoteConfig.value("S3_URL").toString(); + m_credsUrl = remoteConfig.value("S3_CREDS_URL").toString(); + m_xApiKey = remoteConfig.value("S3_X_API_KEY").toString(); + normalizeS3Creds(); + remoteConfig.endGroup(); + + // close and remove temporary file + file.close(); + + // cache configuration at the local storage + ConfigHandler configHandler; + configHandler.setValue("S3", "S3_URL", m_url); + configHandler.setValue("S3", "S3_CREDS_URL", m_credsUrl); + configHandler.setValue("S3", "S3_X_API_KEY", m_xApiKey); + + // set last update date + QString currentDateTime = + QDateTime::currentDateTime().toString(Qt::ISODate); + configHandler.setValue("S3", "S3_CREDS_UPDATED", QVariant(currentDateTime)); +} + +void ImgS3Settings::normalizeS3Creds() +{ + if (!m_url.isEmpty() && m_url.right(1) != "/") { + m_url += "/"; + } + if (!m_credsUrl.isEmpty() && m_credsUrl.right(1) != "/") { + m_credsUrl += "/"; + } +} + +void ImgS3Settings::updateConfigFromRemote() +{ + // check for outdated s3 creds + ConfigHandler configHandler; + QString credsUpdated = + configHandler.value("S3", "S3_CREDS_UPDATED").toString(); + QDateTime dtCredsUpdated = + QDateTime::currentDateTime().fromString(credsUpdated, Qt::ISODate); + QDateTime now = QDateTime::currentDateTime(); + dtCredsUpdated = dtCredsUpdated.addDays(1); + if (dtCredsUpdated <= now) { + // Do update config from remote + if (nullptr == m_networkConfig) { + m_networkConfig = new QNetworkAccessManager(this); + if (proxy() != nullptr) { + m_networkConfig->setProxy(*m_proxy); + } + connect(m_networkConfig, + &QNetworkAccessManager::finished, + this, + &ImgS3Settings::handleReplyUpdateConfigFromRemote); + } + m_networkConfig->get(QNetworkRequest(m_s3ConfigUrl)); + } +} + +void ImgS3Settings::handleReplyUpdateConfigFromRemote(QNetworkReply* reply) +{ + if (reply->error() == QNetworkReply::NoError) { + QString configData = QString(reply->readAll()); + parseConfigurationData(configData); + } else { + QString reason = + reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute) + .toString(); + QString error = reply->errorString(); + qWarning() << "Update config from remote status:" << error; + qWarning() << reason; + } +} + +const QString& ImgS3Settings::storageLocked() +{ + if (m_localSettings->contains("STORAGE_LOCKED")) { + m_storageLocked = + m_localSettings->value(QStringLiteral("STORAGE_LOCKED")).toString(); + } else { + // FIXME - remove hardcode and add configuration file to the + // installation + m_storageLocked = SCREENSHOT_STORAGE_TYPE_S3; + } + return m_storageLocked; +} + +const QString& ImgS3Settings::localConfigFilePath(const QString& fileName) { // get s3 settings - QString configIniPath = QDir(QDir::currentPath()).filePath("config.ini"); - if (!(QFileInfo::exists(configIniPath) && - QFileInfo(configIniPath).isFile())) { + m_qstr = QDir(QDir::currentPath()).filePath(fileName); + if (!(QFileInfo::exists(m_qstr) && QFileInfo(m_qstr).isFile())) { #if defined(Q_OS_LINUX) || defined(Q_OS_UNIX) - configIniPath = "/etc/flameshot/config.ini"; + m_qstr = "/etc/flameshot/" + fileName; #elif defined(Q_OS_WIN) // calculate workdir for flameshot on startup if is not set yet QSettings bootUpSettings( @@ -47,23 +189,165 @@ void ImgS3Settings::initSettings() "USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", QSettings::NativeFormat); QFileInfo fi(bootUpSettings.value("Flameshot").toString()); - configIniPath = QDir(fi.absolutePath()).filePath("config.ini"); + m_qstr = QDir(fi.absolutePath()).filePath(fileName); #endif } - m_settings = new QSettings(configIniPath, QSettings::IniFormat); + return m_qstr; +} + +void ImgS3Settings::initSettings() +{ + m_localSettings = + new QSettings(localConfigFilePath(S3_CONFIG_LOCAL), QSettings::IniFormat); + m_proxySettings = + new QSettings(localConfigFilePath(S3_CONFIG_PROXY), QSettings::IniFormat); } const QString& ImgS3Settings::credsUrl() { + if (m_credsUrl.isEmpty()) { + initS3Creds(); + if (!m_credsUrl.isEmpty()) { + m_credsUrl += S3_API_IMG_PATH; + } + } return m_credsUrl; } const QString& ImgS3Settings::xApiKey() { + if (m_xApiKey.isEmpty()) { + initS3Creds(); + } return m_xApiKey; } const QString& ImgS3Settings::url() { + if (m_url.isEmpty()) { + initS3Creds(); + } return m_url; } + +QNetworkProxy* ImgS3Settings::proxy() +{ + if (proxyHost().length() > 0) { + m_proxy = new QNetworkProxy(); + switch (proxyType()) { + case 0: + m_proxy->setType(QNetworkProxy::DefaultProxy); + break; + case 1: + m_proxy->setType(QNetworkProxy::Socks5Proxy); + break; + case 2: + m_proxy->setType(QNetworkProxy::NoProxy); + break; + case 4: + m_proxy->setType(QNetworkProxy::HttpCachingProxy); + break; + case 5: + m_proxy->setType(QNetworkProxy::FtpCachingProxy); + break; + case 3: + default: + m_proxy->setType(QNetworkProxy::HttpProxy); + break; + } + m_proxy->setHostName(proxyHost()); + m_proxy->setPort(proxyPort()); + if (proxyUser().length() > 0) { + m_proxy->setUser(proxyUser()); + m_proxy->setPassword(proxyPassword()); + } + + } else { + // Get proxy settings from OS settings + QNetworkProxyQuery q(QUrl(credsUrl().toUtf8())); + q.setQueryType(QNetworkProxyQuery::UrlRequest); + q.setProtocolTag("http"); + + QList proxies = + QNetworkProxyFactory::systemProxyForQuery(q); + if (proxies.size() > 0 && proxies[0].type() != QNetworkProxy::NoProxy) { + m_proxy = new QNetworkProxy(); + m_proxy->setHostName(proxies[0].hostName()); + m_proxy->setPort(proxies[0].port()); + m_proxy->setType(proxies[0].type()); + m_proxy->setUser(proxies[0].user()); + m_proxy->setPassword(proxies[0].password()); + } + } + return m_proxy; +} + +void ImgS3Settings::clearProxy() +{ + if (m_proxy != nullptr) { + delete m_proxy; + m_proxy = nullptr; + } +} + +int ImgS3Settings::proxyType() +{ + if (-1 == m_proxyType) { + m_proxyType = 3; // default - HTTP transparent proxying is used + if (m_proxySettings->contains("HTTP_PROXY_TYPE")) { + m_proxyType = m_proxySettings->value("HTTP_PROXY_TYPE").toInt(); + if (m_proxyType < 0 || m_proxyType > 5) { + m_proxyType = 3; // default - HTTP transparent proxying is used + } + } + } + return m_proxyType; +} + +const QString& ImgS3Settings::proxyHost() +{ + if (m_proxyHost.isNull()) { + if (m_proxySettings->contains("HTTP_PROXY_HOST")) { + m_proxyHost = m_proxySettings->value("HTTP_PROXY_HOST").toString(); + } else { + m_proxyHost = ""; + } + } + return m_proxyHost; +} + +int ImgS3Settings::proxyPort() +{ + if (-1 == m_proxyPort) { + m_proxyPort = 3128; + if (m_proxySettings->contains("HTTP_PROXY_PORT")) { + m_proxyPort = m_proxySettings->value("HTTP_PROXY_PORT").toInt(); + } + } + return m_proxyPort; +} + +const QString& ImgS3Settings::proxyUser() +{ + if (m_proxyUser.isNull()) { + if (m_proxySettings->contains("HTTP_PROXY_USER")) { + m_proxyUser = m_proxySettings->value("HTTP_PROXY_USER").toString(); + } else { + m_proxyUser = ""; + } + } + return m_proxyUser; +} + +const QString& ImgS3Settings::proxyPassword() +{ + if (m_proxyPassword.isNull()) { + if (m_proxySettings->contains("HTTP_PROXY_PASSWORD")) { + m_proxyPassword = + m_proxySettings->value("HTTP_PROXY_PASSWORD").toString(); + } else { + m_proxyPassword = ""; + } + } + return m_proxyPassword; +} diff --git a/src/tools/storage/s3/imgs3settings.h b/src/tools/storage/s3/imgs3settings.h index 3fce40e4..ac1d0556 100644 --- a/src/tools/storage/s3/imgs3settings.h +++ b/src/tools/storage/s3/imgs3settings.h @@ -1,30 +1,79 @@ -#ifndef IMGS3SETTINGS_H -#define IMGS3SETTINGS_H +#ifndef IMG_S3_SETTINGS_H +#define IMG_S3_SETTINGS_H #define S3_API_IMG_PATH "v2/image/" +#define S3_GET_REMOTE_SETTINGS_TIMEOUT 10 +#define S3_CONFIG_LOCAL "config.ini" +#define S3_CONFIG_PROXY "config_proxy.ini" + +#include #include +#include class QSettings; +class QNetworkProxy; +class QNetworkAccessManager; +class QNetworkRequest; +class QNetworkReply; -class ImgS3Settings +class ImgS3Settings : public QObject { + Q_OBJECT + +private slots: + void handleReplyUpdateConfigFromRemote(QNetworkReply* reply); + public: - ImgS3Settings(); + ImgS3Settings(QObject* parent = nullptr); + + bool getConfigRemote(int timeout = S3_GET_REMOTE_SETTINGS_TIMEOUT); + void updateConfigFromRemote(); + + const QString& storageLocked(); const QString& credsUrl(); const QString& xApiKey(); const QString& url(); - QSettings* settings(); + + QNetworkProxy* proxy(); + void clearProxy(); private: + int proxyType(); + const QString& proxyHost(); + int proxyPort(); + const QString& proxyUser(); + const QString& proxyPassword(); + void initSettings(); + const QString& localConfigFilePath(const QString& fileName); + void parseConfigurationData(const QString& data); + void initS3Creds(); + void normalizeS3Creds(); // class members - QSettings* m_settings; + QSettings* m_localSettings; + QString m_storageLocked; + QString m_qstr; + + // s3 + QUrl m_s3ConfigUrl; QString m_credsUrl; QString m_xApiKey; QString m_url; + + // proxy + QNetworkProxy* m_proxy; + QSettings* m_proxySettings; + int m_proxyType; + QString m_proxyHost; + int m_proxyPort; + QString m_proxyUser; + QString m_proxyPassword; + + // + QNetworkAccessManager* m_networkConfig; }; -#endif // IMGS3SETTINGS_H +#endif // IMG_S3_SETTINGS_H diff --git a/src/tools/storage/s3/imgs3uploader.cpp b/src/tools/storage/s3/imgs3uploader.cpp index 6012121a..5795f3b8 100644 --- a/src/tools/storage/s3/imgs3uploader.cpp +++ b/src/tools/storage/s3/imgs3uploader.cpp @@ -17,7 +17,6 @@ // along with Flameshot. If not, see . #include "imgs3uploader.h" -#include "imgs3settings.h" #include "src/core/controller.h" #include "src/utils/confighandler.h" #include "src/utils/history.h" @@ -41,13 +40,14 @@ #include #include #include +#include #include #include ImgS3Uploader::ImgS3Uploader(const QPixmap& capture, QWidget* parent) : ImgUploader(capture, parent) { - init(tr("Upload image to S3"), tr("Uploading Image")); + init(tr("Upload image to S3"), tr("Uploading Image...")); } ImgS3Uploader::ImgS3Uploader(QWidget* parent) @@ -63,10 +63,10 @@ ImgS3Uploader::~ImgS3Uploader() void ImgS3Uploader::init(const QString& title, const QString& label) { - m_proxy = nullptr; - m_NetworkAMUpload = nullptr; - m_NetworkAMGetCreds = nullptr; - m_NetworkAMRemove = nullptr; + m_multiPart = nullptr; + m_networkAMUpload = nullptr; + m_networkAMGetCreds = nullptr; + m_networkAMRemove = nullptr; resultStatus = false; setWindowTitle(title); @@ -75,108 +75,15 @@ void ImgS3Uploader::init(const QString& title, const QString& label) QNetworkProxy* ImgS3Uploader::proxy() { - if (m_proxy == nullptr) { - initProxy(); - } - return m_proxy; -} - -QNetworkProxy* ImgS3Uploader::initProxy() -{ - // get s3 settings - ImgS3Settings imgS3Settings; - - // get proxy settings from "config.ini" file - QSettings* settings = imgS3Settings.settings(); - QString httpProxyHost = settings->value("HTTP_PROXY_HOST").toString(); - - if (httpProxyHost.length() > 0) { - m_proxy = new QNetworkProxy(); - if (settings->contains("HTTP_PROXY_TYPE")) { - switch (settings->value("HTTP_PROXY_TYPE").toInt()) { - case 0: - m_proxy->setType(QNetworkProxy::DefaultProxy); - break; - case 1: - m_proxy->setType(QNetworkProxy::Socks5Proxy); - break; - case 2: - m_proxy->setType(QNetworkProxy::NoProxy); - break; - case 4: - m_proxy->setType(QNetworkProxy::HttpCachingProxy); - break; - case 5: - m_proxy->setType(QNetworkProxy::FtpCachingProxy); - break; - case 3: - default: - m_proxy->setType(QNetworkProxy::HttpProxy); - break; - } - } - - m_proxy->setHostName(httpProxyHost); - int nProxyPort = 3128; - if (settings->contains("HTTP_PROXY_PORT")) { - nProxyPort = settings->value("HTTP_PROXY_PORT").toInt(); - } - m_proxy->setPort(nProxyPort); - - if (settings->contains("HTTP_PROXY_USER")) { - qDebug() << "Proxy user" - << settings->value("HTTP_PROXY_PASSWORD").toString(); - m_proxy->setUser(settings->value("HTTP_PROXY_USER").toString()); - } - if (settings->contains("HTTP_PROXY_PASSWORD")) { - qDebug() << "Proxy password is not empty"; - m_proxy->setPassword( - settings->value("HTTP_PROXY_PASSWORD").toString()); - } - } else { - // Get proxy settings from OS settings - QNetworkProxyQuery q(QUrl(m_s3Settings.credsUrl().toUtf8())); - q.setQueryType(QNetworkProxyQuery::UrlRequest); - q.setProtocolTag("http"); - - QList proxies = - QNetworkProxyFactory::systemProxyForQuery(q); - if (proxies.size() > 0 && proxies[0].type() != QNetworkProxy::NoProxy) { - m_proxy = new QNetworkProxy(); - m_proxy->setHostName(proxies[0].hostName()); - m_proxy->setPort(proxies[0].port()); - m_proxy->setType(proxies[0].type()); - m_proxy->setUser(proxies[0].user()); - m_proxy->setPassword(proxies[0].password()); - } - } -#ifdef QT_DEBUG - if (m_proxy != nullptr) { - qDebug() << "Using proxy server"; - qDebug() << "proxy host:" << m_proxy->hostName(); - qDebug() << "proxy port:" << m_proxy->port(); - qDebug() << "proxy type:" << m_proxy->type(); - qDebug() << "proxy user:" - << (m_proxy->user().length() > 0 ? m_proxy->user() - : "no user"); - qDebug() << "proxy password:" - << (m_proxy->password().length() > 0 ? "***" : "no password"); - } else { - qDebug() << "No proxy"; - } -#endif - return m_proxy; + return m_s3Settings.proxy(); } void ImgS3Uploader::clearProxy() { - if (m_proxy != nullptr) { - delete m_proxy; - m_proxy = nullptr; - } + m_s3Settings.clearProxy(); } -void ImgS3Uploader::handleReplyUpload(QNetworkReply* reply) +void ImgS3Uploader::handleReplyPostUpload(QNetworkReply* reply) { hideSpinner(); m_storageImageName.clear(); @@ -254,19 +161,52 @@ void ImgS3Uploader::handleReplyGetCreds(QNetworkReply* reply) } else { if (m_s3Settings.credsUrl().length() == 0) { setInfoLabelText( - tr("S3 Creds URL is not found in your configuration file")); + tr("Retrieving configuration file with s3 creds...")); + if (!m_s3Settings.getConfigRemote()) { + retry(); + } + hide(); + + if (!m_s3Settings.credsUrl().isEmpty()) { + setInfoLabelText(tr("Uploading Image...")); + upload(); + return; + } } else { setInfoLabelText(reply->errorString()); } + // FIXME - remove not uploaded preview } new QShortcut(Qt::Key_Escape, this, SLOT(close())); } +void ImgS3Uploader::retry() +{ + setInfoLabelText( + tr("S3 Creds URL is not found in your configuration file")); + if (QMessageBox::Retry == + QMessageBox::question(nullptr, + tr("Error"), + tr("Unable to get s3 credentials, please check " + "your VPN connection and try again"), + QMessageBox::Retry | QMessageBox::Cancel)) { + setInfoLabelText(tr("Retrieving configuration file with s3 creds...")); + if (!m_s3Settings.getConfigRemote()) { + retry(); + } + } else { + hide(); + } +} + void ImgS3Uploader::uploadToS3(QJsonDocument& response) { - // set paramets from "fields" - QHttpMultiPart* multiPart = - new QHttpMultiPart(QHttpMultiPart::FormDataType); + // set parameters from "fields" + if (nullptr != m_multiPart) { + delete m_multiPart; + m_multiPart = nullptr; + } + m_multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); // read JSON response QJsonObject json = response.object(); @@ -282,7 +222,7 @@ void ImgS3Uploader::uploadToS3(QJsonDocument& response) part.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"" + key + "\"")); part.setBody(field.toLatin1()); - multiPart->append(part); + m_multiPart->append(part); } QHttpPart imagePart; @@ -297,7 +237,7 @@ void ImgS3Uploader::uploadToS3(QJsonDocument& response) pixmap().save(&buffer, "PNG"); imagePart.setBody(byteArray); - multiPart->append(imagePart); + m_multiPart->append(imagePart); setImageUrl(QUrl(resultUrl)); @@ -305,7 +245,7 @@ void ImgS3Uploader::uploadToS3(QJsonDocument& response) QNetworkRequest request(qUrl); // upload - m_NetworkAMUpload->post(request, multiPart); + m_networkAMUpload->post(request, m_multiPart); } void ImgS3Uploader::deleteResource(const QString& fileName, @@ -314,69 +254,64 @@ void ImgS3Uploader::deleteResource(const QString& fileName, // read network settings on each call to simplify configuration management // without restarting clearProxy(); - if (m_NetworkAMRemove != nullptr) { - delete m_NetworkAMRemove; - m_NetworkAMRemove = nullptr; + if (m_networkAMRemove != nullptr) { + delete m_networkAMRemove; + m_networkAMRemove = nullptr; } - m_NetworkAMRemove = new QNetworkAccessManager(this); - connect(m_NetworkAMRemove, + m_networkAMRemove = new QNetworkAccessManager(this); + connect(m_networkAMRemove, &QNetworkAccessManager::finished, this, &ImgS3Uploader::handleReplyDeleteResource); if (proxy() != nullptr) { - m_NetworkAMRemove->setProxy(*proxy()); + m_networkAMRemove->setProxy(*proxy()); } QNetworkRequest request; m_storageImageName = fileName; m_deleteToken = deleteToken; + request.setUrl(m_s3Settings.credsUrl().toUtf8() + fileName); request.setRawHeader("X-API-Key", m_s3Settings.xApiKey().toLatin1()); request.setRawHeader("Authorization", "Bearer " + deleteToken.toLatin1()); - m_NetworkAMRemove->deleteResource(request); + m_networkAMRemove->deleteResource(request); } void ImgS3Uploader::upload() { m_deleteToken.clear(); m_storageImageName.clear(); + show(); // read network settings on each call to simplify configuration management // without restarting init creds and upload network access managers clearProxy(); - if (m_NetworkAMGetCreds != nullptr) { - delete m_NetworkAMGetCreds; - m_NetworkAMGetCreds = nullptr; - } - m_NetworkAMGetCreds = new QNetworkAccessManager(this); - connect(m_NetworkAMGetCreds, + cleanNetworkAccessManagers(); + + m_networkAMGetCreds = new QNetworkAccessManager(this); + connect(m_networkAMGetCreds, &QNetworkAccessManager::finished, this, &ImgS3Uploader::handleReplyGetCreds); - if (m_NetworkAMUpload != nullptr) { - delete m_NetworkAMUpload; - m_NetworkAMUpload = nullptr; - } - m_NetworkAMUpload = new QNetworkAccessManager(this); - connect(m_NetworkAMUpload, + m_networkAMUpload = new QNetworkAccessManager(this); + connect(m_networkAMUpload, &QNetworkAccessManager::finished, this, - &ImgS3Uploader::handleReplyUpload); + &ImgS3Uploader::handleReplyPostUpload); if (proxy() != nullptr) { - m_NetworkAMGetCreds->setProxy(*proxy()); - m_NetworkAMUpload->setProxy(*proxy()); + m_networkAMGetCreds->setProxy(*proxy()); + m_networkAMUpload->setProxy(*proxy()); } // get creads - QUrl creds(m_s3Settings.credsUrl()); - QNetworkRequest requestCreds(creds); + QNetworkRequest requestCreds(QUrl(m_s3Settings.credsUrl())); if (m_s3Settings.xApiKey().length() > 0) { requestCreds.setRawHeader( QByteArray("X-API-Key"), QByteArray(m_s3Settings.xApiKey().toLocal8Bit())); } - m_NetworkAMGetCreds->get(requestCreds); + m_networkAMGetCreds->get(requestCreds); } void ImgS3Uploader::removeImagePreview() @@ -395,3 +330,19 @@ void ImgS3Uploader::removeImagePreview() m_storageImageName.clear(); resultStatus = true; } + +void ImgS3Uploader::cleanNetworkAccessManagers() +{ + if (nullptr != m_networkAMUpload) { + delete m_networkAMUpload; + m_networkAMUpload = nullptr; + } + if (nullptr != m_networkAMGetCreds) { + delete m_networkAMGetCreds; + m_networkAMGetCreds = nullptr; + } + if (nullptr != m_networkAMRemove) { + delete m_networkAMRemove; + m_networkAMRemove = nullptr; + } +} \ No newline at end of file diff --git a/src/tools/storage/s3/imgs3uploader.h b/src/tools/storage/s3/imgs3uploader.h index 8a9ec7aa..60581ce0 100644 --- a/src/tools/storage/s3/imgs3uploader.h +++ b/src/tools/storage/s3/imgs3uploader.h @@ -17,8 +17,6 @@ #pragma once -#define S3_API_IMG_PATH "v2/image/" - #include "../imguploader.h" #include "imgs3settings.h" #include @@ -34,6 +32,7 @@ class QPushButton; class QUrl; class NotificationWidget; class ImageLabel; +class QHttpMultiPart; class ImgS3Uploader : public ImgUploader { @@ -46,7 +45,7 @@ public: void deleteResource(const QString&, const QString&); private slots: - void handleReplyUpload(QNetworkReply* reply); + void handleReplyPostUpload(QNetworkReply* reply); void handleReplyGetCreds(QNetworkReply* reply); void handleReplyDeleteResource(QNetworkReply* reply); @@ -54,17 +53,18 @@ private: void init(const QString& title, const QString& label); void uploadToS3(QJsonDocument& response); void removeImagePreview(); + void retry(); - QNetworkProxy* initProxy(); void clearProxy(); QNetworkProxy* proxy(); + void cleanNetworkAccessManagers(); // class members private: ImgS3Settings m_s3Settings; - QNetworkProxy* m_proxy; - QNetworkAccessManager* m_NetworkAMUpload; - QNetworkAccessManager* m_NetworkAMGetCreds; - QNetworkAccessManager* m_NetworkAMRemove; + QNetworkAccessManager* m_networkAMUpload; + QNetworkAccessManager* m_networkAMGetCreds; + QNetworkAccessManager* m_networkAMRemove; + QHttpMultiPart* m_multiPart; }; diff --git a/src/tools/storage/storagemanager.cpp b/src/tools/storage/storagemanager.cpp index 1d4c22dd..42d02ab8 100644 --- a/src/tools/storage/storagemanager.cpp +++ b/src/tools/storage/storagemanager.cpp @@ -1,7 +1,6 @@ #include "storagemanager.h" #include "imguploader.h" #include "imgur/imguruploadertool.h" -#include "s3/imgs3settings.h" #include "s3/imgs3uploadertool.h" #include "src/tools/capturetool.h" #include @@ -22,8 +21,7 @@ CaptureTool* StorageManager::imgUploaderTool(const QString& imgUploaderType, const QString& StorageManager::storageUrl(const QString& imgUploaderType) { if (imgUploaderType == SCREENSHOT_STORAGE_TYPE_S3) { - ImgS3Settings s3Settings; - m_qstr = s3Settings.url(); + m_qstr = m_imgS3Settings.url(); } else if (imgUploaderType == SCREENSHOT_STORAGE_TYPE_IMGUR) { m_qstr = "https://i.imgur.com/"; } @@ -32,25 +30,15 @@ const QString& StorageManager::storageUrl(const QString& imgUploaderType) const QString& StorageManager::storageDefault() { - ImgS3Settings imgS3Settings; - if (!imgS3Settings.xApiKey().isEmpty()) { - m_qstr = SCREENSHOT_STORAGE_TYPE_S3; + if (!m_imgS3Settings.storageLocked().isEmpty()) { + m_qstr = m_imgS3Settings.storageLocked(); } else { m_qstr = SCREENSHOT_STORAGE_TYPE_IMGUR; } return m_qstr; } -bool StorageManager::storageLocked() +const QString& StorageManager::storageLocked() { - // TODO - move this to some common config file, not a storage specific - // configuration file - bool res = false; - ImgS3Settings imgS3Settings; - if (imgS3Settings.settings()->contains("STORAGE_LOCK")) { - res = imgS3Settings.settings() - ->value(QStringLiteral("STORAGE_LOCK")) - .toBool(); - } - return res; + return m_imgS3Settings.storageLocked(); } \ No newline at end of file diff --git a/src/tools/storage/storagemanager.h b/src/tools/storage/storagemanager.h index 70a482c0..9e42319c 100644 --- a/src/tools/storage/storagemanager.h +++ b/src/tools/storage/storagemanager.h @@ -1,6 +1,7 @@ #ifndef STORAGEMANAGER_H #define STORAGEMANAGER_H +#include "s3/imgs3settings.h" #include #include @@ -17,11 +18,12 @@ public: QObject* parent = nullptr); const QString& storageUrl(const QString& imgUploaderType); const QString& storageDefault(); - bool storageLocked(); + const QString& storageLocked(); private: // class members QString m_qstr; + ImgS3Settings m_imgS3Settings; }; #endif // STORAGEMANAGER_H diff --git a/src/utils/confighandler.cpp b/src/utils/confighandler.cpp index 3a48f069..843f0cbc 100644 --- a/src/utils/confighandler.cpp +++ b/src/utils/confighandler.cpp @@ -459,11 +459,11 @@ void ConfigHandler::setCopyPathAfterSaveEnabled(const bool value) void ConfigHandler::setUploadStorage(const QString& uploadStorage) { StorageManager storageManager; - if (storageManager.storageLocked()) { - m_settings.setValue(QStringLiteral("uploadStorage"), - storageManager.storageDefault()); - } else { + if (storageManager.storageLocked().isEmpty()) { m_settings.setValue(QStringLiteral("uploadStorage"), uploadStorage); + } else { + m_settings.setValue(QStringLiteral("uploadStorage"), + storageManager.storageLocked()); } } @@ -471,8 +471,8 @@ const QString& ConfigHandler::uploadStorage() { StorageManager storageManager; // check for storage lock - if (storageManager.storageLocked()) { - setUploadStorage(storageManager.storageDefault()); + if (!storageManager.storageLocked().isEmpty()) { + setUploadStorage(storageManager.storageLocked()); } // get storage @@ -616,3 +616,28 @@ const QString& ConfigHandler::shortcut(const QString& shortcutName) m_settings.endGroup(); return m_strRes; } + +void ConfigHandler::setValue(const QString& group, + const QString& key, + const QVariant& value) +{ + if (!group.isEmpty()) { + m_settings.beginGroup(group); + } + m_settings.setValue(key, value); + if (!group.isEmpty()) { + m_settings.endGroup(); + } +} + +QVariant& ConfigHandler::value(const QString& group, const QString& key) +{ + if (!group.isEmpty()) { + m_settings.beginGroup(group); + } + m_varRes = m_settings.value(key); + if (!group.isEmpty()) { + m_settings.endGroup(); + } + return m_varRes; +} diff --git a/src/utils/confighandler.h b/src/utils/confighandler.h index b493427d..8670a893 100644 --- a/src/utils/confighandler.h +++ b/src/utils/confighandler.h @@ -20,6 +20,7 @@ #include "src/widgets/capture/capturetoolbutton.h" #include #include +#include #include class ConfigHandler @@ -103,8 +104,14 @@ public: QString configFilePath() const; + void setValue(const QString& group, + const QString& key, + const QVariant& value); + QVariant& value(const QString& group, const QString& key); + private: QString m_strRes; + QVariant m_varRes; QSettings m_settings; QVector m_shortcuts; From deee448de402f6357da18db7ae0dd929c137bd2e Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Wed, 18 Nov 2020 18:54:36 +0200 Subject: [PATCH 11/54] Read s3 creds for upload from URL in private network (VPN) - fix --- data/translations/Internationalization_ca.ts | 30 +++-- data/translations/Internationalization_cs.ts | 30 +++-- .../Internationalization_de_DE.ts | 30 +++-- data/translations/Internationalization_es.ts | 30 +++-- data/translations/Internationalization_eu.ts | 30 +++-- data/translations/Internationalization_fr.ts | 30 +++-- .../Internationalization_it_IT.ts | 30 +++-- data/translations/Internationalization_ja.ts | 30 +++-- data/translations/Internationalization_ka.ts | 30 +++-- data/translations/Internationalization_nl.ts | 30 +++-- .../Internationalization_nl_NL.ts | 30 +++-- data/translations/Internationalization_pl.ts | 30 +++-- .../Internationalization_pt_BR.ts | 30 +++-- data/translations/Internationalization_ru.ts | 30 +++-- data/translations/Internationalization_sk.ts | 30 +++-- .../Internationalization_sr_SP.ts | 30 +++-- .../Internationalization_sv_SE.ts | 30 +++-- data/translations/Internationalization_tr.ts | 30 +++-- data/translations/Internationalization_uk.ts | 30 +++-- .../Internationalization_zh_CN.ts | 30 +++-- .../Internationalization_zh_HK.ts | 30 +++-- .../Internationalization_zh_TW.ts | 30 +++-- src/tools/storage/s3/imgs3settings.cpp | 96 ++-------------- src/tools/storage/s3/imgs3settings.h | 21 +--- src/tools/storage/s3/imgs3uploader.cpp | 106 ++++++++++++------ src/tools/storage/s3/imgs3uploader.h | 10 +- 26 files changed, 401 insertions(+), 492 deletions(-) diff --git a/data/translations/Internationalization_ca.ts b/data/translations/Internationalization_ca.ts index fa37337d..c80f74a4 100644 --- a/data/translations/Internationalization_ca.ts +++ b/data/translations/Internationalization_ca.ts @@ -523,7 +523,7 @@ Press Space to open the side panel. ImgS3Uploader - + Upload image to S3 @@ -532,69 +532,67 @@ Press Space to open the side panel. S'està pujant la imatge - - + Uploading Image... - + Delete image from S3 - + Deleting image... - + URL copied to clipboard. L'URL s'ha copiat al porta-retalls. - + Unable to remove screenshot from the remote storage. - + Network error - + Possibly it doesn't exist anymore - + Do you want to remove screenshot from local history anyway? - + Remove screenshot from history? - - + Retrieving configuration file with s3 creds... - + S3 Creds URL is not found in your configuration file - + Error Error - + Unable to get s3 credentials, please check your VPN connection and try again diff --git a/data/translations/Internationalization_cs.ts b/data/translations/Internationalization_cs.ts index 36cd2633..8beb94f2 100644 --- a/data/translations/Internationalization_cs.ts +++ b/data/translations/Internationalization_cs.ts @@ -530,7 +530,7 @@ Stiskněte mezerník pro otevření postranního panelu. ImgS3Uploader - + Upload image to S3 @@ -539,69 +539,67 @@ Stiskněte mezerník pro otevření postranního panelu. Nahrává se obrázek - - + Uploading Image... - + Delete image from S3 - + Deleting image... - + URL copied to clipboard. Adresa (URL) zkopírována do schránky. - + Unable to remove screenshot from the remote storage. - + Network error - + Possibly it doesn't exist anymore - + Do you want to remove screenshot from local history anyway? - + Remove screenshot from history? - - + Retrieving configuration file with s3 creds... - + S3 Creds URL is not found in your configuration file - + Error Chyba - + Unable to get s3 credentials, please check your VPN connection and try again diff --git a/data/translations/Internationalization_de_DE.ts b/data/translations/Internationalization_de_DE.ts index 71a8a2e2..90fa5432 100644 --- a/data/translations/Internationalization_de_DE.ts +++ b/data/translations/Internationalization_de_DE.ts @@ -526,7 +526,7 @@ Drücke die Leertaste um das Seitenmenü zu öffnen. ImgS3Uploader - + Upload image to S3 @@ -535,69 +535,67 @@ Drücke die Leertaste um das Seitenmenü zu öffnen. Bild hochladen - - + Uploading Image... - + Delete image from S3 - + Deleting image... - + URL copied to clipboard. URL kopiert. - + Unable to remove screenshot from the remote storage. - + Network error - + Possibly it doesn't exist anymore - + Do you want to remove screenshot from local history anyway? - + Remove screenshot from history? - - + Retrieving configuration file with s3 creds... - + S3 Creds URL is not found in your configuration file - + Error Fehler - + Unable to get s3 credentials, please check your VPN connection and try again diff --git a/data/translations/Internationalization_es.ts b/data/translations/Internationalization_es.ts index 906038b1..0c8707bf 100644 --- a/data/translations/Internationalization_es.ts +++ b/data/translations/Internationalization_es.ts @@ -526,7 +526,7 @@ Presiona Espacio para abrir el panel lateral. ImgS3Uploader - + Upload image to S3 @@ -535,69 +535,67 @@ Presiona Espacio para abrir el panel lateral. Subiendo Imagen - - + Uploading Image... - + Delete image from S3 - + Deleting image... - + URL copied to clipboard. URL copiada al portapapeles. - + Unable to remove screenshot from the remote storage. - + Network error - + Possibly it doesn't exist anymore - + Do you want to remove screenshot from local history anyway? - + Remove screenshot from history? - - + Retrieving configuration file with s3 creds... - + S3 Creds URL is not found in your configuration file - + Error Error - + Unable to get s3 credentials, please check your VPN connection and try again diff --git a/data/translations/Internationalization_eu.ts b/data/translations/Internationalization_eu.ts index c914e982..3b31c526 100644 --- a/data/translations/Internationalization_eu.ts +++ b/data/translations/Internationalization_eu.ts @@ -530,7 +530,7 @@ Sakatu Zuriunea alboko panela irekitzeko. ImgS3Uploader - + Upload image to S3 @@ -539,69 +539,67 @@ Sakatu Zuriunea alboko panela irekitzeko. Irudia igotzen - - + Uploading Image... - + Delete image from S3 - + Deleting image... - + URL copied to clipboard. Arbelean kopiatu da URLa. - + Unable to remove screenshot from the remote storage. - + Network error - + Possibly it doesn't exist anymore - + Do you want to remove screenshot from local history anyway? - + Remove screenshot from history? - - + Retrieving configuration file with s3 creds... - + S3 Creds URL is not found in your configuration file - + Error Errorea - + Unable to get s3 credentials, please check your VPN connection and try again diff --git a/data/translations/Internationalization_fr.ts b/data/translations/Internationalization_fr.ts index 830234a8..1c7b28ae 100644 --- a/data/translations/Internationalization_fr.ts +++ b/data/translations/Internationalization_fr.ts @@ -530,7 +530,7 @@ Appuyer sur Espace pour ouvrir le panneau latéral. ImgS3Uploader - + Upload image to S3 @@ -539,69 +539,67 @@ Appuyer sur Espace pour ouvrir le panneau latéral. Mise en ligne de l'image - - + Uploading Image... - + Delete image from S3 - + Deleting image... - + URL copied to clipboard. URL copiée dans le Presse-papier. - + Unable to remove screenshot from the remote storage. - + Network error - + Possibly it doesn't exist anymore - + Do you want to remove screenshot from local history anyway? - + Remove screenshot from history? - - + Retrieving configuration file with s3 creds... - + S3 Creds URL is not found in your configuration file - + Error Erreur - + Unable to get s3 credentials, please check your VPN connection and try again diff --git a/data/translations/Internationalization_it_IT.ts b/data/translations/Internationalization_it_IT.ts index 05b667e6..aed180a0 100644 --- a/data/translations/Internationalization_it_IT.ts +++ b/data/translations/Internationalization_it_IT.ts @@ -507,74 +507,72 @@ Press Space to open the side panel. ImgS3Uploader - + Upload image to S3 - - + Uploading Image... - + Delete image from S3 - + Deleting image... - + URL copied to clipboard. - + Unable to remove screenshot from the remote storage. - + Network error - + Possibly it doesn't exist anymore - + Do you want to remove screenshot from local history anyway? - + Remove screenshot from history? - - + Retrieving configuration file with s3 creds... - + S3 Creds URL is not found in your configuration file - + Error - + Unable to get s3 credentials, please check your VPN connection and try again diff --git a/data/translations/Internationalization_ja.ts b/data/translations/Internationalization_ja.ts index d931281b..2b00b719 100644 --- a/data/translations/Internationalization_ja.ts +++ b/data/translations/Internationalization_ja.ts @@ -526,7 +526,7 @@ Enter を押すと画面をキャプチャー。 ImgS3Uploader - + Upload image to S3 @@ -535,69 +535,67 @@ Enter を押すと画面をキャプチャー。 画像をアップロード中 - - + Uploading Image... - + Delete image from S3 - + Deleting image... - + URL copied to clipboard. URL をクリップボードにコピーしました。 - + Unable to remove screenshot from the remote storage. - + Network error - + Possibly it doesn't exist anymore - + Do you want to remove screenshot from local history anyway? - + Remove screenshot from history? - - + Retrieving configuration file with s3 creds... - + S3 Creds URL is not found in your configuration file - + Error エラー - + Unable to get s3 credentials, please check your VPN connection and try again diff --git a/data/translations/Internationalization_ka.ts b/data/translations/Internationalization_ka.ts index 6ecbf249..860849c1 100644 --- a/data/translations/Internationalization_ka.ts +++ b/data/translations/Internationalization_ka.ts @@ -522,7 +522,7 @@ Press Space to open the side panel. ImgS3Uploader - + Upload image to S3 @@ -531,69 +531,67 @@ Press Space to open the side panel. სურათის ატვირთვა - - + Uploading Image... - + Delete image from S3 - + Deleting image... - + URL copied to clipboard. URL დაკოპირდა გაცვლის ბუფერში. - + Unable to remove screenshot from the remote storage. - + Network error - + Possibly it doesn't exist anymore - + Do you want to remove screenshot from local history anyway? - + Remove screenshot from history? - - + Retrieving configuration file with s3 creds... - + S3 Creds URL is not found in your configuration file - + Error შეცდომა - + Unable to get s3 credentials, please check your VPN connection and try again diff --git a/data/translations/Internationalization_nl.ts b/data/translations/Internationalization_nl.ts index ce7dd668..0f768148 100644 --- a/data/translations/Internationalization_nl.ts +++ b/data/translations/Internationalization_nl.ts @@ -526,7 +526,7 @@ Druk op spatie om het zijpaneel te openen. ImgS3Uploader - + Upload image to S3 @@ -535,69 +535,67 @@ Druk op spatie om het zijpaneel te openen. Bezig met uploaden van afbeelding... - - + Uploading Image... - + Delete image from S3 - + Deleting image... - + URL copied to clipboard. URL gekopieerd naar klembord. - + Unable to remove screenshot from the remote storage. - + Network error - + Possibly it doesn't exist anymore - + Do you want to remove screenshot from local history anyway? - + Remove screenshot from history? - - + Retrieving configuration file with s3 creds... - + S3 Creds URL is not found in your configuration file - + Error Fout - + Unable to get s3 credentials, please check your VPN connection and try again diff --git a/data/translations/Internationalization_nl_NL.ts b/data/translations/Internationalization_nl_NL.ts index 61fb0bfd..9cd42570 100644 --- a/data/translations/Internationalization_nl_NL.ts +++ b/data/translations/Internationalization_nl_NL.ts @@ -530,7 +530,7 @@ Druk op de spatiebalk om het zijpaneel te openen. ImgS3Uploader - + Upload image to S3 @@ -539,69 +539,67 @@ Druk op de spatiebalk om het zijpaneel te openen. Afbeelding uploaden - - + Uploading Image... - + Delete image from S3 - + Deleting image... - + URL copied to clipboard. URL gekopieerd naar klembord. - + Unable to remove screenshot from the remote storage. - + Network error - + Possibly it doesn't exist anymore - + Do you want to remove screenshot from local history anyway? - + Remove screenshot from history? - - + Retrieving configuration file with s3 creds... - + S3 Creds URL is not found in your configuration file - + Error Fout - + Unable to get s3 credentials, please check your VPN connection and try again diff --git a/data/translations/Internationalization_pl.ts b/data/translations/Internationalization_pl.ts index b472bc9b..16acdea3 100644 --- a/data/translations/Internationalization_pl.ts +++ b/data/translations/Internationalization_pl.ts @@ -529,7 +529,7 @@ Spacja, aby pokazać panel boczny. ImgS3Uploader - + Upload image to S3 @@ -538,69 +538,67 @@ Spacja, aby pokazać panel boczny. Wysyłanie obrazka - - + Uploading Image... - + Delete image from S3 - + Deleting image... - + URL copied to clipboard. URL skopiowany do schowka. - + Unable to remove screenshot from the remote storage. - + Network error - + Possibly it doesn't exist anymore - + Do you want to remove screenshot from local history anyway? - + Remove screenshot from history? - - + Retrieving configuration file with s3 creds... - + S3 Creds URL is not found in your configuration file - + Error Błąd - + Unable to get s3 credentials, please check your VPN connection and try again diff --git a/data/translations/Internationalization_pt_BR.ts b/data/translations/Internationalization_pt_BR.ts index ff29c0aa..cc8eadee 100644 --- a/data/translations/Internationalization_pt_BR.ts +++ b/data/translations/Internationalization_pt_BR.ts @@ -530,7 +530,7 @@ Pressione espaço abrir o painel lateral. ImgS3Uploader - + Upload image to S3 @@ -539,69 +539,67 @@ Pressione espaço abrir o painel lateral. Upando Imagem - - + Uploading Image... - + Delete image from S3 - + Deleting image... - + URL copied to clipboard. URL copiada para a área de transferência. - + Unable to remove screenshot from the remote storage. - + Network error - + Possibly it doesn't exist anymore - + Do you want to remove screenshot from local history anyway? - + Remove screenshot from history? - - + Retrieving configuration file with s3 creds... - + S3 Creds URL is not found in your configuration file - + Error Erro - + Unable to get s3 credentials, please check your VPN connection and try again diff --git a/data/translations/Internationalization_ru.ts b/data/translations/Internationalization_ru.ts index 6743d7c2..6d5c4705 100644 --- a/data/translations/Internationalization_ru.ts +++ b/data/translations/Internationalization_ru.ts @@ -534,7 +534,7 @@ Press Space to open the side panel. ImgS3Uploader - + Upload image to S3 Загрузка изображения на S3 @@ -543,69 +543,67 @@ Press Space to open the side panel. Загрузка изображения - - + Uploading Image... Выгрузка изображения... - + Delete image from S3 Удалить изображение из S3 - + Deleting image... Удаление изображения... - + URL copied to clipboard. URL скопирован в буфер обмена. - + Unable to remove screenshot from the remote storage. Невозможно удалить снимок экрана из удаленного хранилища. - + Network error Ошибка сети - + Possibly it doesn't exist anymore Возможно, его больше не существует - + Do you want to remove screenshot from local history anyway? Вы все равно хотите удалить скриншот из локальной истории? - + Remove screenshot from history? Удалить скриншот из истории? - - + Retrieving configuration file with s3 creds... Получение конфигурационного файла с параметрами доступа к s3... - + S3 Creds URL is not found in your configuration file S3 Creds URL не найден в вашем файле конфигурации - + Error Ошибка - + Unable to get s3 credentials, please check your VPN connection and try again Не удалось получить конфигурацию для s3, проверьте свое VPN-соединение и повторите попытку diff --git a/data/translations/Internationalization_sk.ts b/data/translations/Internationalization_sk.ts index 7333366e..ba5bde02 100644 --- a/data/translations/Internationalization_sk.ts +++ b/data/translations/Internationalization_sk.ts @@ -530,7 +530,7 @@ Stlačte medzerník pre otvorenie postranného panelu. ImgS3Uploader - + Upload image to S3 @@ -539,69 +539,67 @@ Stlačte medzerník pre otvorenie postranného panelu. Nahrávam obrázok - - + Uploading Image... - + Delete image from S3 - + Deleting image... - + URL copied to clipboard. URL skopírovaná do schránky. - + Unable to remove screenshot from the remote storage. - + Network error - + Possibly it doesn't exist anymore - + Do you want to remove screenshot from local history anyway? - + Remove screenshot from history? - - + Retrieving configuration file with s3 creds... - + S3 Creds URL is not found in your configuration file - + Error Chyba - + Unable to get s3 credentials, please check your VPN connection and try again diff --git a/data/translations/Internationalization_sr_SP.ts b/data/translations/Internationalization_sr_SP.ts index 202f3957..7ffa1ae5 100644 --- a/data/translations/Internationalization_sr_SP.ts +++ b/data/translations/Internationalization_sr_SP.ts @@ -526,7 +526,7 @@ Press Space to open the side panel. ImgS3Uploader - + Upload image to S3 @@ -535,69 +535,67 @@ Press Space to open the side panel. Објављујем слику - - + Uploading Image... - + Delete image from S3 - + Deleting image... - + URL copied to clipboard. Интернет адреса је сачувана у привременој меморији. - + Unable to remove screenshot from the remote storage. - + Network error - + Possibly it doesn't exist anymore - + Do you want to remove screenshot from local history anyway? - + Remove screenshot from history? - - + Retrieving configuration file with s3 creds... - + S3 Creds URL is not found in your configuration file - + Error Грешка - + Unable to get s3 credentials, please check your VPN connection and try again diff --git a/data/translations/Internationalization_sv_SE.ts b/data/translations/Internationalization_sv_SE.ts index 4c06d048..8bb08d18 100644 --- a/data/translations/Internationalization_sv_SE.ts +++ b/data/translations/Internationalization_sv_SE.ts @@ -526,7 +526,7 @@ Tryck Space för att öppna sidopanelen. ImgS3Uploader - + Upload image to S3 @@ -535,69 +535,67 @@ Tryck Space för att öppna sidopanelen. Laddar upp bild - - + Uploading Image... - + Delete image from S3 - + Deleting image... - + URL copied to clipboard. URL kopierad till klippbord. - + Unable to remove screenshot from the remote storage. - + Network error - + Possibly it doesn't exist anymore - + Do you want to remove screenshot from local history anyway? - + Remove screenshot from history? - - + Retrieving configuration file with s3 creds... - + S3 Creds URL is not found in your configuration file - + Error Fel - + Unable to get s3 credentials, please check your VPN connection and try again diff --git a/data/translations/Internationalization_tr.ts b/data/translations/Internationalization_tr.ts index a6170d74..5895bcfd 100644 --- a/data/translations/Internationalization_tr.ts +++ b/data/translations/Internationalization_tr.ts @@ -526,7 +526,7 @@ Yan paneli açmak için Boşluk tuşuna basın. ImgS3Uploader - + Upload image to S3 @@ -535,69 +535,67 @@ Yan paneli açmak için Boşluk tuşuna basın. Resim Yükleniyor - - + Uploading Image... - + Delete image from S3 - + Deleting image... - + URL copied to clipboard. URL panoya kopyalandı. - + Unable to remove screenshot from the remote storage. - + Network error - + Possibly it doesn't exist anymore - + Do you want to remove screenshot from local history anyway? - + Remove screenshot from history? - - + Retrieving configuration file with s3 creds... - + S3 Creds URL is not found in your configuration file - + Error Hata - + Unable to get s3 credentials, please check your VPN connection and try again diff --git a/data/translations/Internationalization_uk.ts b/data/translations/Internationalization_uk.ts index b23fc6a1..ed867216 100644 --- a/data/translations/Internationalization_uk.ts +++ b/data/translations/Internationalization_uk.ts @@ -534,7 +534,7 @@ Press Space to open the side panel. ImgS3Uploader - + Upload image to S3 Вивантаження зображення на S3 @@ -543,69 +543,67 @@ Press Space to open the side panel. Вивантаження зображення - - + Uploading Image... Вивантаження зображення... - + Delete image from S3 Видаліть зображення з S3 - + Deleting image... Видалення зображення... - + URL copied to clipboard. URL скопійовано до буферу обміну. - + Unable to remove screenshot from the remote storage. Не вдалося видалити знімок екрана з віддаленого сховища. - + Network error Помилка мережі - + Possibly it doesn't exist anymore Можливо, його вже не існує - + Do you want to remove screenshot from local history anyway? Ви все одно хочете видалити знімок екрана з місцевої історії? - + Remove screenshot from history? Видалити знімок екрана з історії? - - + Retrieving configuration file with s3 creds... Отримання конфігураційного файлу з параметрами доступу до s3 ... - + S3 Creds URL is not found in your configuration file S3 Creds URL не знайдено у вашому файлі конфігурації - + Error Помилка - + Unable to get s3 credentials, please check your VPN connection and try again Не вдалося отримати конфігурацію для s3, перевірити своє з'єднання VPN і повторити спробу diff --git a/data/translations/Internationalization_zh_CN.ts b/data/translations/Internationalization_zh_CN.ts index 8988be30..7ec424c1 100644 --- a/data/translations/Internationalization_zh_CN.ts +++ b/data/translations/Internationalization_zh_CN.ts @@ -531,7 +531,7 @@ Press Space to open the side panel. ImgS3Uploader - + Upload image to S3 @@ -540,69 +540,67 @@ Press Space to open the side panel. 正在上传 - - + Uploading Image... - + Delete image from S3 - + Deleting image... - + URL copied to clipboard. - + Unable to remove screenshot from the remote storage. - + Network error - + Possibly it doesn't exist anymore - + Do you want to remove screenshot from local history anyway? - + Remove screenshot from history? - - + Retrieving configuration file with s3 creds... - + S3 Creds URL is not found in your configuration file - + Error 错误 - + Unable to get s3 credentials, please check your VPN connection and try again diff --git a/data/translations/Internationalization_zh_HK.ts b/data/translations/Internationalization_zh_HK.ts index 113dfd42..e1db3e84 100644 --- a/data/translations/Internationalization_zh_HK.ts +++ b/data/translations/Internationalization_zh_HK.ts @@ -530,74 +530,72 @@ Press Space to open the side panel. ImgS3Uploader - + Upload image to S3 - - + Uploading Image... - + Delete image from S3 - + Deleting image... - + URL copied to clipboard. - + Unable to remove screenshot from the remote storage. - + Network error - + Possibly it doesn't exist anymore - + Do you want to remove screenshot from local history anyway? - + Remove screenshot from history? - - + Retrieving configuration file with s3 creds... - + S3 Creds URL is not found in your configuration file - + Error 錯誤 - + Unable to get s3 credentials, please check your VPN connection and try again diff --git a/data/translations/Internationalization_zh_TW.ts b/data/translations/Internationalization_zh_TW.ts index 8cead83d..67169b50 100644 --- a/data/translations/Internationalization_zh_TW.ts +++ b/data/translations/Internationalization_zh_TW.ts @@ -522,7 +522,7 @@ Press Space to open the side panel. ImgS3Uploader - + Upload image to S3 @@ -531,69 +531,67 @@ Press Space to open the side panel. 正在上傳 - - + Uploading Image... - + Delete image from S3 - + Deleting image... - + URL copied to clipboard. 連結已複製到剪貼簿 - + Unable to remove screenshot from the remote storage. - + Network error - + Possibly it doesn't exist anymore - + Do you want to remove screenshot from local history anyway? - + Remove screenshot from history? - - + Retrieving configuration file with s3 creds... - + S3 Creds URL is not found in your configuration file - + Error 錯誤 - + Unable to get s3 credentials, please check your VPN connection and try again diff --git a/src/tools/storage/s3/imgs3settings.cpp b/src/tools/storage/s3/imgs3settings.cpp index dad9e750..5ce12c3c 100644 --- a/src/tools/storage/s3/imgs3settings.cpp +++ b/src/tools/storage/s3/imgs3settings.cpp @@ -4,22 +4,14 @@ #include #include #include -#include #include -#include #include -#include -#include -#include #include #include -#include -ImgS3Settings::ImgS3Settings(QObject* parent) - : QObject(parent) +ImgS3Settings::ImgS3Settings() { m_proxy = nullptr; - m_networkConfig = nullptr; initSettings(); // get remote config url @@ -47,38 +39,9 @@ void ImgS3Settings::initS3Creds() m_xApiKey = configHandler.value("S3", "S3_X_API_KEY").toString(); m_url = configHandler.value("S3", "S3_URL").toString(); normalizeS3Creds(); - updateConfigFromRemote(); } -bool ImgS3Settings::getConfigRemote(int timeout) -{ - if (!m_url.isEmpty() && !m_credsUrl.isEmpty()) { - updateConfigFromRemote(); - return true; - } - QNetworkAccessManager* networkConfig = new QNetworkAccessManager(this); - if (proxy() != nullptr) { - networkConfig->setProxy(*m_proxy); - } - QNetworkReply* reply = networkConfig->get(QNetworkRequest(m_s3ConfigUrl)); - - QEventLoop loop; - QTimer timer; - timer.setSingleShot(true); - connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); - connect(reply, SIGNAL(readyRead()), &loop, SLOT(quit())); - timer.start(timeout * 1000); // 30 secs. timeout - loop.exec(); - QString data = QString(reply->readAll()); - parseConfigurationData(data); - - delete reply; - delete networkConfig; - - return !data.isEmpty(); -} - -void ImgS3Settings::parseConfigurationData(const QString& data) +void ImgS3Settings::updateConfigurationData(const QString& data) { // read remote and save to the temporary file QTemporaryFile file; @@ -88,11 +51,11 @@ void ImgS3Settings::parseConfigurationData(const QString& data) stream.flush(); // parse and get configuration data - QSettings remoteConfig(file.fileName(), QSettings::IniFormat, this); + QSettings remoteConfig(file.fileName(), QSettings::IniFormat); remoteConfig.beginGroup("S3"); - m_url = remoteConfig.value("S3_URL").toString(); - m_credsUrl = remoteConfig.value("S3_CREDS_URL").toString(); - m_xApiKey = remoteConfig.value("S3_X_API_KEY").toString(); + QString url = remoteConfig.value("S3_URL").toString(); + QString credsUrl = remoteConfig.value("S3_CREDS_URL").toString(); + QString xApiKey = remoteConfig.value("S3_X_API_KEY").toString(); normalizeS3Creds(); remoteConfig.endGroup(); @@ -101,9 +64,9 @@ void ImgS3Settings::parseConfigurationData(const QString& data) // cache configuration at the local storage ConfigHandler configHandler; - configHandler.setValue("S3", "S3_URL", m_url); - configHandler.setValue("S3", "S3_CREDS_URL", m_credsUrl); - configHandler.setValue("S3", "S3_X_API_KEY", m_xApiKey); + configHandler.setValue("S3", "S3_URL", url); + configHandler.setValue("S3", "S3_CREDS_URL", credsUrl); + configHandler.setValue("S3", "S3_X_API_KEY", xApiKey); // set last update date QString currentDateTime = @@ -121,47 +84,6 @@ void ImgS3Settings::normalizeS3Creds() } } -void ImgS3Settings::updateConfigFromRemote() -{ - // check for outdated s3 creds - ConfigHandler configHandler; - QString credsUpdated = - configHandler.value("S3", "S3_CREDS_UPDATED").toString(); - QDateTime dtCredsUpdated = - QDateTime::currentDateTime().fromString(credsUpdated, Qt::ISODate); - QDateTime now = QDateTime::currentDateTime(); - dtCredsUpdated = dtCredsUpdated.addDays(1); - if (dtCredsUpdated <= now) { - // Do update config from remote - if (nullptr == m_networkConfig) { - m_networkConfig = new QNetworkAccessManager(this); - if (proxy() != nullptr) { - m_networkConfig->setProxy(*m_proxy); - } - connect(m_networkConfig, - &QNetworkAccessManager::finished, - this, - &ImgS3Settings::handleReplyUpdateConfigFromRemote); - } - m_networkConfig->get(QNetworkRequest(m_s3ConfigUrl)); - } -} - -void ImgS3Settings::handleReplyUpdateConfigFromRemote(QNetworkReply* reply) -{ - if (reply->error() == QNetworkReply::NoError) { - QString configData = QString(reply->readAll()); - parseConfigurationData(configData); - } else { - QString reason = - reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute) - .toString(); - QString error = reply->errorString(); - qWarning() << "Update config from remote status:" << error; - qWarning() << reason; - } -} - const QString& ImgS3Settings::storageLocked() { if (m_localSettings->contains("STORAGE_LOCKED")) { diff --git a/src/tools/storage/s3/imgs3settings.h b/src/tools/storage/s3/imgs3settings.h index ac1d0556..5971c064 100644 --- a/src/tools/storage/s3/imgs3settings.h +++ b/src/tools/storage/s3/imgs3settings.h @@ -2,7 +2,6 @@ #define IMG_S3_SETTINGS_H #define S3_API_IMG_PATH "v2/image/" -#define S3_GET_REMOTE_SETTINGS_TIMEOUT 10 #define S3_CONFIG_LOCAL "config.ini" #define S3_CONFIG_PROXY "config_proxy.ini" @@ -13,22 +12,12 @@ class QSettings; class QNetworkProxy; -class QNetworkAccessManager; -class QNetworkRequest; -class QNetworkReply; -class ImgS3Settings : public QObject +class ImgS3Settings { - Q_OBJECT - -private slots: - void handleReplyUpdateConfigFromRemote(QNetworkReply* reply); public: - ImgS3Settings(QObject* parent = nullptr); - - bool getConfigRemote(int timeout = S3_GET_REMOTE_SETTINGS_TIMEOUT); - void updateConfigFromRemote(); + ImgS3Settings(); const QString& storageLocked(); @@ -39,6 +28,8 @@ public: QNetworkProxy* proxy(); void clearProxy(); + void updateConfigurationData(const QString& data); + private: int proxyType(); const QString& proxyHost(); @@ -48,7 +39,6 @@ private: void initSettings(); const QString& localConfigFilePath(const QString& fileName); - void parseConfigurationData(const QString& data); void initS3Creds(); void normalizeS3Creds(); @@ -71,9 +61,6 @@ private: int m_proxyPort; QString m_proxyUser; QString m_proxyPassword; - - // - QNetworkAccessManager* m_networkConfig; }; #endif // IMG_S3_SETTINGS_H diff --git a/src/tools/storage/s3/imgs3uploader.cpp b/src/tools/storage/s3/imgs3uploader.cpp index 5795f3b8..d5f3de8a 100644 --- a/src/tools/storage/s3/imgs3uploader.cpp +++ b/src/tools/storage/s3/imgs3uploader.cpp @@ -1,5 +1,4 @@ -// Copyright(c) 2017-2019 Alejandro Sirgo Rica & Contributors -// Copyright(c) 2017-2019 Alejandro Sirgo Rica & Contributors +// Copyright(c) 2017-2019 Namecheap inc. // // This file is part of Flameshot. // @@ -59,6 +58,10 @@ ImgS3Uploader::ImgS3Uploader(QWidget* parent) ImgS3Uploader::~ImgS3Uploader() { clearProxy(); + cleanNetworkAccessManagers(); + if (nullptr != m_networkAMConfig) { + delete m_networkAMConfig; + } } void ImgS3Uploader::init(const QString& title, const QString& label) @@ -67,6 +70,7 @@ void ImgS3Uploader::init(const QString& title, const QString& label) m_networkAMUpload = nullptr; m_networkAMGetCreds = nullptr; m_networkAMRemove = nullptr; + m_networkAMConfig = nullptr; resultStatus = false; setWindowTitle(title); @@ -161,17 +165,7 @@ void ImgS3Uploader::handleReplyGetCreds(QNetworkReply* reply) } else { if (m_s3Settings.credsUrl().length() == 0) { setInfoLabelText( - tr("Retrieving configuration file with s3 creds...")); - if (!m_s3Settings.getConfigRemote()) { - retry(); - } - hide(); - - if (!m_s3Settings.credsUrl().isEmpty()) { - setInfoLabelText(tr("Uploading Image...")); - upload(); - return; - } + tr("S3 Creds URL is not found in your configuration file")); } else { setInfoLabelText(reply->errorString()); } @@ -180,25 +174,6 @@ void ImgS3Uploader::handleReplyGetCreds(QNetworkReply* reply) new QShortcut(Qt::Key_Escape, this, SLOT(close())); } -void ImgS3Uploader::retry() -{ - setInfoLabelText( - tr("S3 Creds URL is not found in your configuration file")); - if (QMessageBox::Retry == - QMessageBox::question(nullptr, - tr("Error"), - tr("Unable to get s3 credentials, please check " - "your VPN connection and try again"), - QMessageBox::Retry | QMessageBox::Cancel)) { - setInfoLabelText(tr("Retrieving configuration file with s3 creds...")); - if (!m_s3Settings.getConfigRemote()) { - retry(); - } - } else { - hide(); - } -} - void ImgS3Uploader::uploadToS3(QJsonDocument& response) { // set parameters from "fields" @@ -281,11 +256,25 @@ void ImgS3Uploader::upload() { m_deleteToken.clear(); m_storageImageName.clear(); - show(); // read network settings on each call to simplify configuration management // without restarting init creds and upload network access managers clearProxy(); + + // check for outdated s3 creds + ConfigHandler configHandler; + QString credsUpdated = + configHandler.value("S3", "S3_CREDS_UPDATED").toString(); + QDateTime dtCredsUpdated = + QDateTime::currentDateTime().fromString(credsUpdated, Qt::ISODate); + QDateTime now = QDateTime::currentDateTime(); + dtCredsUpdated = dtCredsUpdated.addDays(1); + if (m_s3Settings.credsUrl().isEmpty() || dtCredsUpdated <= now) { + getConfigRemote(); + return; + } + + // clean old network connections and start uploading cleanNetworkAccessManagers(); m_networkAMGetCreds = new QNetworkAccessManager(this); @@ -345,4 +334,53 @@ void ImgS3Uploader::cleanNetworkAccessManagers() delete m_networkAMRemove; m_networkAMRemove = nullptr; } -} \ No newline at end of file + if (nullptr != m_multiPart) { + delete m_multiPart; + m_multiPart = nullptr; + } +} + +void ImgS3Uploader::getConfigRemote() +{ + if (nullptr == m_networkAMConfig) { + m_networkAMConfig = new QNetworkAccessManager(this); + connect(m_networkAMConfig, + &QNetworkAccessManager::finished, + this, + &ImgS3Uploader::handleReplyGetConfig); + if (proxy() != nullptr) { + m_networkAMConfig->setProxy(*proxy()); + } + } + QNetworkRequest requestConfig(QUrl(S3_REMOTE_CONFIG_URL)); + m_networkAMConfig->get(requestConfig); +} + +void ImgS3Uploader::handleReplyGetConfig(QNetworkReply* reply) +{ + if (reply->error() == QNetworkReply::NoError) { + bool doUpload = m_s3Settings.credsUrl().isEmpty(); + QString data = QString(reply->readAll()); + m_s3Settings.updateConfigurationData(data); + if (doUpload) { + upload(); + } else { + hide(); + } + } else { + QString message = reply->errorString() + "\n\n" + + tr("Unable to get s3 credentials, please check " + "your VPN connection and try again"); + if (QMessageBox::Retry == + QMessageBox::question(nullptr, + tr("Error"), + message, + QMessageBox::Retry | QMessageBox::Cancel)) { + setInfoLabelText( + tr("Retrieving configuration file with s3 creds...")); + getConfigRemote(); + return; + } + hide(); + } +} diff --git a/src/tools/storage/s3/imgs3uploader.h b/src/tools/storage/s3/imgs3uploader.h index 60581ce0..9757d342 100644 --- a/src/tools/storage/s3/imgs3uploader.h +++ b/src/tools/storage/s3/imgs3uploader.h @@ -1,4 +1,4 @@ -// Copyright(c) 2017-2019 Alejandro Sirgo Rica & Contributors +// Copyright(c) 2017-2019 Namecheap inc. // // This file is part of Flameshot. // @@ -22,6 +22,10 @@ #include #include +#define S3_REMOTE_CONFIG_URL \ + "https://git.namecheap.net/projects/RND/repos/flameshot_config/raw/" \ + "config.ini" + class QNetworkReply; class QNetworkProxy; class QNetworkAccessManager; @@ -48,12 +52,13 @@ private slots: void handleReplyPostUpload(QNetworkReply* reply); void handleReplyGetCreds(QNetworkReply* reply); void handleReplyDeleteResource(QNetworkReply* reply); + void handleReplyGetConfig(QNetworkReply* reply); private: void init(const QString& title, const QString& label); void uploadToS3(QJsonDocument& response); void removeImagePreview(); - void retry(); + void getConfigRemote(); void clearProxy(); QNetworkProxy* proxy(); @@ -67,4 +72,5 @@ private: QNetworkAccessManager* m_networkAMGetCreds; QNetworkAccessManager* m_networkAMRemove; QHttpMultiPart* m_multiPart; + QNetworkAccessManager* m_networkAMConfig; }; From a11a9affa946f2f286bf15e1ffbea9074a48854c Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Wed, 18 Nov 2020 22:15:36 +0200 Subject: [PATCH 12/54] Release 0.8.5.4 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0e603f5a..fd4b1445 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.13) # This can be read from ${PROJECT_NAME} after project() is called project( flameshot - VERSION 0.8.5.3 + VERSION 0.8.5.4 LANGUAGES CXX) set(PROJECT_NAME_CAPITALIZED "Flameshot") From 293525f5b6a2edfed848d5741e12225b9f427e69 Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Fri, 20 Nov 2020 07:35:28 -0800 Subject: [PATCH 13/54] Fix - MacOS capture area, full screen mode, system tray area, etc --- .gitignore | 3 +++ src/core/controller.cpp | 8 +++++++- src/utils/screengrabber.cpp | 16 ++++++++++++++-- src/widgets/capture/capturewidget.cpp | 19 +++++++++++++++---- 4 files changed, 39 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index fb71df32..92414fa7 100644 --- a/.gitignore +++ b/.gitignore @@ -63,4 +63,7 @@ data/flatpak/.flatpak-builder .idea/ .run +# MacOS +.DS_Store + # End of https://www.gitignore.io/api/snapcraft diff --git a/src/core/controller.cpp b/src/core/controller.cpp index 93fb4865..69973cd0 100644 --- a/src/core/controller.cpp +++ b/src/core/controller.cpp @@ -150,9 +150,12 @@ void Controller::startVisualCapture(const uint id, #ifdef Q_OS_WIN m_captureWindow->show(); +#elif (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) + m_captureWindow->show(); + m_captureWindow->activateWindow(); #else m_captureWindow->showFullScreen(); - // m_captureWindow->show(); // Debug #endif } else { emit captureFailed(id); @@ -250,12 +253,15 @@ void Controller::enableTrayIcon() QIcon::fromTheme("flameshot-tray", QIcon(":img/app/flameshot.png")); m_trayIcon->setIcon(trayicon); +#if not(defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(Q_OS_MAC64) || \ + defined(Q_OS_MACOS) || defined(Q_OS_MACX)) auto trayIconActivated = [this](QSystemTrayIcon::ActivationReason r) { if (r == QSystemTrayIcon::Trigger) { startVisualCapture(); } }; connect(m_trayIcon, &QSystemTrayIcon::activated, this, trayIconActivated); +#endif #ifdef Q_OS_WIN // Ensure proper removal of tray icon when program quits on Windows. diff --git a/src/utils/screengrabber.cpp b/src/utils/screengrabber.cpp index 03c76657..4e970ba9 100644 --- a/src/utils/screengrabber.cpp +++ b/src/utils/screengrabber.cpp @@ -37,7 +37,18 @@ ScreenGrabber::ScreenGrabber(QObject* parent) QPixmap ScreenGrabber::grabEntireDesktop(bool& ok) { ok = true; -#if defined(Q_OS_LINUX) || defined(Q_OS_UNIX) +#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) + QScreen* currentScreen = QGuiApplication::screenAt(QCursor::pos()); + QPixmap screenPixmap( + currentScreen->grabWindow(QApplication::desktop()->winId(), + currentScreen->geometry().x(), + currentScreen->geometry().y(), + currentScreen->geometry().width(), + currentScreen->geometry().height())); + screenPixmap.setDevicePixelRatio(currentScreen->devicePixelRatio()); + return screenPixmap; +#elif defined(Q_OS_LINUX) || defined(Q_OS_UNIX) if (m_info.waylandDectected()) { QPixmap res; // handle screenshot based on DE @@ -87,7 +98,7 @@ QPixmap ScreenGrabber::grabEntireDesktop(bool& ok) return res; } #endif - +#if defined(Q_OS_LINUX) || defined(Q_OS_UNIX) || defined(Q_OS_WIN) QRect geometry; for (QScreen* const screen : QGuiApplication::screens()) { QRect scrRect = screen->geometry(); @@ -106,6 +117,7 @@ QPixmap ScreenGrabber::grabEntireDesktop(bool& ok) QScreen* screen = QApplication::screens()[screenNumber]; p.setDevicePixelRatio(screen->devicePixelRatio()); return p; +#endif } QPixmap ScreenGrabber::grabScreen(int screenNumber, bool& ok) diff --git a/src/widgets/capture/capturewidget.cpp b/src/widgets/capture/capturewidget.cpp index 23713c0d..c4b364ca 100644 --- a/src/widgets/capture/capturewidget.cpp +++ b/src/widgets/capture/capturewidget.cpp @@ -85,7 +85,8 @@ CaptureWidget::CaptureWidget(const uint id, initContext(savePath, fullScreen); initShortcuts(); m_context.circleCount = 1; -#ifdef Q_OS_WIN +#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(Q_OS_MAC64) || \ + defined(Q_OS_MACOS) || defined(Q_OS_MACX)) // Top left of the whole set of screens QPoint topLeft(0, 0); #endif @@ -99,7 +100,7 @@ CaptureWidget::CaptureWidget(const uint id, } m_context.origScreenshot = m_context.screenshot; -#ifdef Q_OS_WIN +#if defined(Q_OS_WIN) setWindowFlags(Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::Popup); @@ -111,11 +112,21 @@ CaptureWidget::CaptureWidget(const uint id, } } move(topLeft); + resize(pixmap().size()); +#elif (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) + setWindowFlags(Qt::WindowStaysOnTopHint | Qt::BypassWindowManagerHint | + Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint | + Qt::ToolTip | Qt::Popup); + + QScreen* currentScreen = QGuiApplication::screenAt(QCursor::pos()); + move(currentScreen->geometry().x(), currentScreen->geometry().y()); + resize(currentScreen->size()); #else setWindowFlags(Qt::BypassWindowManagerHint | Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::Tool); -#endif resize(pixmap().size()); +#endif } // Create buttons m_buttonHandler = new ButtonHandler(this); @@ -126,7 +137,7 @@ CaptureWidget::CaptureWidget(const uint id, QRect r = screen->geometry(); r.moveTo(r.x() / screen->devicePixelRatio(), r.y() / screen->devicePixelRatio()); -#ifdef Q_OS_WIN +#if defined(Q_OS_WIN) r.moveTo(r.topLeft() - topLeft); #endif areas.append(r); From 4c49461a7abc443b882dd55aaaf2a6d068a5063a Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Tue, 24 Nov 2020 08:53:15 -0800 Subject: [PATCH 14/54] Fix - MacOS Tool Setting on non-primary screen --- src/widgets/panel/utilitypanel.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/widgets/panel/utilitypanel.cpp b/src/widgets/panel/utilitypanel.cpp index 28f5b80d..ac392d4c 100644 --- a/src/widgets/panel/utilitypanel.cpp +++ b/src/widgets/panel/utilitypanel.cpp @@ -42,6 +42,11 @@ UtilityPanel::UtilityPanel(QWidget* parent) &QPropertyAnimation::finished, m_internalPanel, &QWidget::hide); + +#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(Q_OS_MAC64) || \ + defined(Q_OS_MACOS) || defined(Q_OS_MACX)) + move(0, 0); +#endif } QWidget* UtilityPanel::toolWidget() const @@ -81,6 +86,10 @@ void UtilityPanel::show() m_showAnimation->setEndValue(QRect(0, 0, width(), height())); m_internalPanel->show(); m_showAnimation->start(); +#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(Q_OS_MAC64) || \ + defined(Q_OS_MACOS) || defined(Q_OS_MACX)) + move(0, 0); +#endif QWidget::show(); } From 69e698bdb1d9d7bb7c58ce2cb9aaf4e1e9e65b30 Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Tue, 24 Nov 2020 10:08:12 -0800 Subject: [PATCH 15/54] Fix - MacOS - activate new widgets, swith to fullscreen mode+hotkeys --- src/core/controller.cpp | 16 +++++++++++++++- src/widgets/capture/capturewidget.cpp | 11 +++++++---- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/core/controller.cpp b/src/core/controller.cpp index 69973cd0..c795bf5f 100644 --- a/src/core/controller.cpp +++ b/src/core/controller.cpp @@ -152,7 +152,9 @@ void Controller::startVisualCapture(const uint id, m_captureWindow->show(); #elif (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ defined(Q_OS_MACX)) - m_captureWindow->show(); + // In "Emulate fullscreen mode" + // m_captureWindow->show(); + m_captureWindow->showFullScreen(); m_captureWindow->activateWindow(); #else m_captureWindow->showFullScreen(); @@ -185,6 +187,10 @@ void Controller::openConfigWindow() if (!m_configWindow) { m_configWindow = new ConfigWindow(); m_configWindow->show(); +#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) + m_configWindow->activateWindow(); +#endif } } @@ -202,6 +208,10 @@ void Controller::openLauncherWindow() m_launcherWindow = new CaptureLauncher(); } m_launcherWindow->show(); +#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) + m_launcherWindow->activateWindow(); +#endif } void Controller::enableTrayIcon() @@ -324,6 +334,10 @@ void Controller::showRecentScreenshots() } m_history->loadHistory(); m_history->show(); +#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) + m_history->activateWindow(); +#endif } void Controller::startFullscreenCapture(const uint id) diff --git a/src/widgets/capture/capturewidget.cpp b/src/widgets/capture/capturewidget.cpp index c4b364ca..17f403a2 100644 --- a/src/widgets/capture/capturewidget.cpp +++ b/src/widgets/capture/capturewidget.cpp @@ -115,10 +115,13 @@ CaptureWidget::CaptureWidget(const uint id, resize(pixmap().size()); #elif (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ defined(Q_OS_MACX)) - setWindowFlags(Qt::WindowStaysOnTopHint | Qt::BypassWindowManagerHint | - Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint | - Qt::ToolTip | Qt::Popup); - + // Emulate fullscreen mode + // setWindowFlags(Qt::WindowStaysOnTopHint | + // Qt::BypassWindowManagerHint | + // Qt::FramelessWindowHint | + // Qt::NoDropShadowWindowHint | Qt::ToolTip | + // Qt::Popup + // ); QScreen* currentScreen = QGuiApplication::screenAt(QCursor::pos()); move(currentScreen->geometry().x(), currentScreen->geometry().y()); resize(currentScreen->size()); From bad270cce46f14a2bd122f582f24b35bad549146 Mon Sep 17 00:00:00 2001 From: Yurii Puchkov Date: Thu, 19 Nov 2020 12:50:02 +0200 Subject: [PATCH 16/54] MacOS dmg package build (cmake only, no CI yet) --- .github/workflows/MacOS-pack.yml | 61 ++++++++++++++++ CMakeLists.txt | 8 +++ cmake/modules/MacOSXBundleInfo.plist.in | 40 +++++++++++ data/img/app/org.flameshot.Flameshot-1024.png | Bin 0 -> 49759 bytes src/CMakeLists.txt | 65 ++++++++++++++++-- 5 files changed, 169 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/MacOS-pack.yml create mode 100644 cmake/modules/MacOSXBundleInfo.plist.in create mode 100644 data/img/app/org.flameshot.Flameshot-1024.png diff --git a/.github/workflows/MacOS-pack.yml b/.github/workflows/MacOS-pack.yml new file mode 100644 index 00000000..7bda0e6b --- /dev/null +++ b/.github/workflows/MacOS-pack.yml @@ -0,0 +1,61 @@ +name: Packaging(MacOS) + +on: + push: + branches: + - master + - feature/RND-680-macos-.dmg-package-build + paths-ignore: + - 'README.md' + - 'LICENSE' + + pull_request: + paths-ignore: + - 'README.md' + - 'LICENSE' + +env: + PRODUCT: flameshot + +jobs: + catalina: + name: macOS Catalina 10.15 + runs-on: macos-10.15 + + steps: + - name: Checkout Source code + uses: actions/checkout@v1 + + - name: Install Qt + run: brew install qt cmake + + - name: Configure + run: | + mkdir build + cd build + rm -rf ./src/flameshot.dmg ./src/flameshot.app/ + cmake .. -DQt5_DIR=$(brew --prefix qt5)/lib/cmake/Qt5 + + - name: Compile + run: | + cd build + make + + - name: Build dmg image + run: | + cd build + /usr/local/opt/qt5/bin/macdeployqt src/flameshot.app -dmg + + - name: Upload dmg image + shell: bash + run: | + python3 -m pip install -U -q requests + echo "================MacOS dmg image download link================" + echo $(python3 $GITHUB_WORKSPACE/scripts/upload_services/transferwee.py upload $GITHUB_WORKSPACE/build/src/flameshot.dmg) + echo "=====no operation for you can see link in the log console=====" + + - name: Artifact Upload + uses: actions/upload-artifact@v2 + with: + name: MacOS-artifact + path: ${{ github.workspace }}/build/src/flameshot.dmg diff --git a/CMakeLists.txt b/CMakeLists.txt index fd4b1445..3f2274c1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,10 @@ cmake_minimum_required(VERSION 3.13) # cmake_policy(SET CMP0076 OLD) # This can be read from ${PROJECT_NAME} after project() is called +if(APPLE) + set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version") +endif() + project( flameshot VERSION 0.8.5.4 @@ -18,6 +22,10 @@ if(WIN32) add_definitions(-DFLAMESHOT_VERSION_BUGFIX=${CMAKE_PROJECT_VERSION_PATCH}) add_definitions(-DFLAMESHOT_VERSION_BUILD=1) add_definitions(-DFLAMESHOT_VERSION_STRING="${PROJECT_VERSION}") +elseif(APPLE) + set(Qt5_DIR "$(brew --prefix qt5)/lib/cmake/Qt5/" CACHE PATH "directory where Qt5Config.cmake exists.") + set(CMAKE_MACOSX_BUNDLE ON) + set(CMAKE_MACOSX_RPATH ON) endif() set(RUN_IN_PLACE ${DEFAULT_RUN_IN_PLACE} CACHE BOOL "Run directly in source directory structure") diff --git a/cmake/modules/MacOSXBundleInfo.plist.in b/cmake/modules/MacOSXBundleInfo.plist.in new file mode 100644 index 00000000..bf1e2f76 --- /dev/null +++ b/cmake/modules/MacOSXBundleInfo.plist.in @@ -0,0 +1,40 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + flameshot + CFBundleGetInfoString + + CFBundleIconFile + flameshot + CFBundleIdentifier + https://flameshot.org/ + CFBundleInfoDictionaryVersion + 6.0 + CFBundleLongVersionString + + CFBundleName + + CFBundlePackageType + APPL + CFBundleShortVersionString + + CFBundleSignature + ???? + CFBundleVersion + 0.8.5 + CSResourcesFileMapped + + NSHumanReadableCopyright + + LSMinimumSystemVersion + 10.15 + NSPrincipalClass + NSApplication + NSHighResolutionCapable + True + + diff --git a/data/img/app/org.flameshot.Flameshot-1024.png b/data/img/app/org.flameshot.Flameshot-1024.png new file mode 100644 index 0000000000000000000000000000000000000000..e6f5d3c7b91b2184c25a3c3d7a8effd25086b724 GIT binary patch literal 49759 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

x*uQx$7_85j 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 literal 0 HcmV?d00001 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index eb37bcaa..3b55a49e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -14,7 +14,43 @@ set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_AUTOUIC ON) -add_executable(flameshot) +# set application icon +if (APPLE) + # generate iconset + execute_process( + COMMAND bash "-c" "mkdir -p flameshot.iconset" + ) + execute_process( + COMMAND bash "-c" "sips -z 16 16 ../data/img/app/flameshot.png --out flameshot.iconset/icon_16x16.png" + COMMAND bash "-c" "sips -z 32 32 ../data/img/app/flameshot.png --out flameshot.iconset/icon_16x16@2x.png" + COMMAND bash "-c" "sips -z 32 32 ../data/img/app/flameshot.png --out flameshot.iconset/icon_32x32.png" + COMMAND bash "-c" "sips -z 64 64 ../data/img/app/flameshot.png --out flameshot.iconset/icon_32x32@2x.png" + COMMAND bash "-c" "sips -z 64 64 ../data/img/app/flameshot.png --out flameshot.iconset/icon_64x64x.png" + COMMAND bash "-c" "sips -z 128 128 ../data/img/app/flameshot.png --out flameshot.iconset/icon_64x64@2.png" + COMMAND bash "-c" "sips -z 128 128 ../data/img/app/flameshot.png --out flameshot.iconset/icon_128x128.png" + COMMAND bash "-c" "sips -z 256 256 ../data/img/app/org.flameshot.Flameshot-1024.png --out flameshot.iconset/icon_128x128@2x.png" + COMMAND bash "-c" "sips -z 256 256 ../data/img/app/org.flameshot.Flameshot-1024.png --out flameshot.iconset/icon_256x256.png" + COMMAND bash "-c" "sips -z 512 512 ../data/img/app/org.flameshot.Flameshot-1024.png --out flameshot.iconset/icon_256x256@2x.png" + COMMAND bash "-c" "sips -z 512 512 ../data/img/app/org.flameshot.Flameshot-1024.png --out flameshot.iconset/icon_512x512.png" + COMMAND bash "-c" "sips -z 1024 1024 ../data/img/app/org.flameshot.Flameshot-1024.png --out flameshot.iconset/icon_512x512@2x.png" + COMMAND bash "-c" "iconutil -c icns flameshot.iconset" + ) + + execute_process( + COMMAND bash "-c" "rm -R flameshot.iconset" + ) + + # Set application icon + set(MACOSX_BUNDLE_ICON_FILE flameshot.icns) + + # And this part tells CMake where to find and install the file itself + set(APP_ICON_MACOSX ${CMAKE_BINARY_DIR}/flameshot.icns) + set_source_files_properties(${APP_ICON_MACOSX} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") + + add_executable(flameshot MACOSX_BUNDLE main.cpp ${APP_ICON_MACOSX}) +else() + add_executable(flameshot) +endif() add_executable(Flameshot::flameshot ALIAS flameshot) @@ -22,6 +58,7 @@ if(WIN32) set_property(TARGET flameshot PROPERTY WIN32_EXECUTABLE true) endif() + add_subdirectory(cli) add_subdirectory(config) add_subdirectory(core) @@ -154,10 +191,10 @@ include(GNUInstallDirs) set(INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake/Flameshot) # Install binary -install( - TARGETS flameshot - EXPORT flameshot-targets - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +install(TARGETS flameshot + EXPORT flameshot-targets + BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) # Install desktop files, completion and dbus files configure_file(${CMAKE_SOURCE_DIR}/data/desktopEntry/package/org.flameshot.Flameshot.desktop @@ -245,3 +282,21 @@ if(WIN32) message("Unable to find executable QTDIR/bin/windeployqt.") endif() endif() + +# macdeployqt +if (APPLE) + set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") + execute_process(COMMAND brew --prefix qt5 OUTPUT_VARIABLE QTDIR) + string(REGEX REPLACE "\n$" "" QTDIR "${QTDIR}") + set(MAC_DEPLOY_QT ${QTDIR}/bin/macdeployqt) + if (EXISTS ${MAC_DEPLOY_QT}) + set_source_files_properties(resources/icon.icns PROPERTIES + MACOSX_PACKAGE_LOCATION Resources) + + set_target_properties(${target} PROPERTIES + MACOSX_BUNDLE TRUE + ) + else () + message("Unable to find executable ${MAC_DEPLOY_QT}.") + endif () +endif () From 9e897289b6336d3a2a61b4da9e30d7fe8836e602 Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Fri, 27 Nov 2020 02:44:48 -0800 Subject: [PATCH 17/54] Raise window to the top on MacOS --- cmake/modules/MacOSXBundleInfo.plist.in | 6 ++++++ src/core/controller.cpp | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/cmake/modules/MacOSXBundleInfo.plist.in b/cmake/modules/MacOSXBundleInfo.plist.in index bf1e2f76..535d1fe1 100644 --- a/cmake/modules/MacOSXBundleInfo.plist.in +++ b/cmake/modules/MacOSXBundleInfo.plist.in @@ -36,5 +36,11 @@ NSApplication NSHighResolutionCapable True + LSUIElement + 1 + NSPhotoLibraryAddUsageDescription + Application requires access to save screenshots to your gallery + NSSupportsAutomaticGraphicsSwitching + diff --git a/src/core/controller.cpp b/src/core/controller.cpp index c795bf5f..b9405685 100644 --- a/src/core/controller.cpp +++ b/src/core/controller.cpp @@ -156,6 +156,7 @@ void Controller::startVisualCapture(const uint id, // m_captureWindow->show(); m_captureWindow->showFullScreen(); m_captureWindow->activateWindow(); + m_captureWindow->raise(); #else m_captureWindow->showFullScreen(); #endif @@ -190,6 +191,7 @@ void Controller::openConfigWindow() #if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ defined(Q_OS_MACX)) m_configWindow->activateWindow(); + m_configWindow->raise(); #endif } } @@ -199,6 +201,11 @@ void Controller::openInfoWindow() { if (!m_infoWindow) { m_infoWindow = new InfoWindow(); +#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) + m_infoWindow->activateWindow(); + m_infoWindow->raise(); +#endif } } @@ -211,6 +218,7 @@ void Controller::openLauncherWindow() #if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ defined(Q_OS_MACX)) m_launcherWindow->activateWindow(); + m_launcherWindow->raise(); #endif } @@ -337,6 +345,7 @@ void Controller::showRecentScreenshots() #if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ defined(Q_OS_MACX)) m_history->activateWindow(); + m_history->raise(); #endif } From 2a3c368cf1dc00378d0f00e7f9da6fced5456c26 Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Tue, 1 Dec 2020 17:01:35 +0200 Subject: [PATCH 18/54] MacOS - Take test screenshot on start, MacOS will request a 'Screen Recording' permissions on the first run --- src/core/controller.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/core/controller.cpp b/src/core/controller.cpp index b9405685..446f958e 100644 --- a/src/core/controller.cpp +++ b/src/core/controller.cpp @@ -38,6 +38,11 @@ #include "src/core/globalshortcutfilter.h" #endif +#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) +#include +#endif + // Controller is the core component of Flameshot, creates the trayIcon and // launches the capture widget @@ -68,6 +73,15 @@ Controller::Controller() QString StyleSheet = CaptureButton::globalStyleSheet(); qApp->setStyleSheet(StyleSheet); + +#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) + // Try to take a test screenshot, MacOS will request a "Screen Recording" + // permissions on the first run. Otherwise it will be hidden under the + // CaptureWidget + QScreen* currentScreen = QGuiApplication::screenAt(QCursor::pos()); + currentScreen->grabWindow(QApplication::desktop()->winId(), 0, 0, 1, 1); +#endif } Controller::~Controller() From ea11797d077f7d597c1c63853cc37fd1c8e8976d Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Tue, 1 Dec 2020 19:25:46 +0200 Subject: [PATCH 19/54] MacOS - add Application link to the package --- .github/workflows/MacOS-pack.yml | 9 +++-- packaging/macos/update_package.sh | 55 +++++++++++++++++++++++++++++++ src/CMakeLists.txt | 2 +- 3 files changed, 63 insertions(+), 3 deletions(-) create mode 100755 packaging/macos/update_package.sh diff --git a/.github/workflows/MacOS-pack.yml b/.github/workflows/MacOS-pack.yml index 7bda0e6b..6669e367 100644 --- a/.github/workflows/MacOS-pack.yml +++ b/.github/workflows/MacOS-pack.yml @@ -41,12 +41,17 @@ jobs: cd build make - - name: Build dmg image + - name: Build dmg package run: | cd build /usr/local/opt/qt5/bin/macdeployqt src/flameshot.app -dmg - - name: Upload dmg image + - name: Update dmg package links + run: | + cd build/src + ../../packaging/macos/update_package.sh + + - name: Upload dmg package shell: bash run: | python3 -m pip install -U -q requests diff --git a/packaging/macos/update_package.sh b/packaging/macos/update_package.sh new file mode 100755 index 00000000..048be244 --- /dev/null +++ b/packaging/macos/update_package.sh @@ -0,0 +1,55 @@ +#!/bin/bash + +echo "Change the permision of .dmg file" +hdiutil convert "flameshot.dmg" -format UDRW -o "flameshot_rw.dmg" + +echo "Mount it and save the device" +DEVICE=$(hdiutil attach -readwrite -noverify "flameshot_rw.dmg" | egrep '^/dev/' | sed 1q | awk '{print $1}') +sleep 5 + +echo "Create the sysmbolic link to application folder" +PATH_AT_VOLUME="/Volumes/src:flameshot/" +CURRENT_PATH="$(pwd)" +cd "${PATH_AT_VOLUME}" +ln -s /Applications +cd "${CURRENT_PATH}" + +# TODO - add background and icon location. +# https://forum.qt.io/topic/94987/how-can-i-add-symbolic-link-application-and-background-image-in-dmg-package/3 +#echo "copy the background image in to package" +#mkdir -p "${PATH_AT_VOLUME}".background/ +#cp backgroundImage.png "${PATH_AT_VOLUME}".background/ +#echo "done" +# +## tell the Finder to resize the window, set the background, +## change the icon size, place the icons in the right position, etc. +#echo ' +# tell application "Finder" +# tell disk "/Volumes/src:flameshot" +# open +# set current view of container window to icon view +# set toolbar visible of container window to false +# set statusbar visible of container window to false +# set the bounds of container window to {400, 100, 1110, 645} +# set viewOptions to the icon view options of container window +# set arrangement of viewOptions to not arranged +# set icon size of viewOptions to 72 +# set background picture of viewOptions to file ".background:backgroundImage.png" +# set position of item "flameshot.app" of container window to {160, 325} +# set position of item "Applications" of container window to {560, 320} +# close +# open +# update without registering applications +# delay 2 +# end tell +# end tell +#' | osascript +# +#sync + +# unmount it +hdiutil detach "${DEVICE}" +rm -f "flameshot.dmg" + +hdiutil convert "flameshot_rw.dmg" -format UDZO -o "flameshot.dmg" +rm -f "flameshot_rw.dmg" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3b55a49e..e95c059d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -167,7 +167,7 @@ else() endif() if(NOT USE_OPENSSL) - message(WARNING "OpenSSL is required to upload screenshot to imgur") + message(WARNING "OpenSSL is required to upload screenshots") endif() target_compile_definitions(flameshot PRIVATE APP_PREFIX="${CMAKE_INSTALL_PREFIX}") From c783b666729beff5b2654692d795285ba41b47ff Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Wed, 2 Dec 2020 15:45:28 +0200 Subject: [PATCH 20/54] MacOS - QSystemTrayIcon does not show context menu on primary screen --- src/core/controller.cpp | 39 ++++++++++++++++++++++++++++----------- src/core/controller.h | 2 ++ 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/core/controller.cpp b/src/core/controller.cpp index 446f958e..cf4041f4 100644 --- a/src/core/controller.cpp +++ b/src/core/controller.cpp @@ -50,6 +50,7 @@ Controller::Controller() : m_captureWindow(nullptr) { m_history = nullptr; + m_trayIconMenu = nullptr; qApp->setQuitOnLastWindowClosed(false); @@ -87,6 +88,7 @@ Controller::Controller() Controller::~Controller() { delete m_history; + delete m_trayIconMenu; } Controller* Controller::getInstance() @@ -241,7 +243,7 @@ void Controller::enableTrayIcon() if (m_trayIcon) { return; } - QMenu* trayIconMenu = new QMenu(); + m_trayIconMenu = new QMenu(); ConfigHandler().setDisabledTrayIcon(false); QAction* captureAction = new QAction(tr("&Take Screenshot"), this); @@ -268,19 +270,22 @@ void Controller::enableTrayIcon() recentAction, SIGNAL(triggered()), this, SLOT(showRecentScreenshots())); // generate menu - trayIconMenu->addAction(captureAction); - trayIconMenu->addAction(launcherAction); - trayIconMenu->addSeparator(); - trayIconMenu->addAction(recentAction); - trayIconMenu->addSeparator(); - trayIconMenu->addAction(configAction); - trayIconMenu->addAction(infoAction); - trayIconMenu->addSeparator(); - trayIconMenu->addAction(quitAction); + m_trayIconMenu->addAction(captureAction); + m_trayIconMenu->addAction(launcherAction); + m_trayIconMenu->addSeparator(); + m_trayIconMenu->addAction(recentAction); + m_trayIconMenu->addSeparator(); + m_trayIconMenu->addAction(configAction); + m_trayIconMenu->addAction(infoAction); + m_trayIconMenu->addSeparator(); + m_trayIconMenu->addAction(quitAction); m_trayIcon = new QSystemTrayIcon(); m_trayIcon->setToolTip(QStringLiteral("Flameshot")); - m_trayIcon->setContextMenu(trayIconMenu); +#if not(defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(Q_OS_MAC64) || \ + defined(Q_OS_MACOS) || defined(Q_OS_MACX)) + m_trayIcon->setContextMenu(m_trayIconMenu); +#endif QIcon trayicon = QIcon::fromTheme("flameshot-tray", QIcon(":img/app/flameshot.png")); m_trayIcon->setIcon(trayicon); @@ -293,6 +298,18 @@ void Controller::enableTrayIcon() } }; connect(m_trayIcon, &QSystemTrayIcon::activated, this, trayIconActivated); +#else + // Because of the following issues: + // https://bugreports.qt.io/browse/QTBUG-86393 + // https://developer.apple.com/forums/thread/126072 + auto trayIconActivated = [this](QSystemTrayIcon::ActivationReason r) { + if (m_trayIconMenu->isVisible()) { + m_trayIconMenu->hide(); + } else { + m_trayIconMenu->popup(QPoint(QCursor::pos().x(), 0)); + } + }; + connect(m_trayIcon, &QSystemTrayIcon::activated, this, trayIconActivated); #endif #ifdef Q_OS_WIN diff --git a/src/core/controller.h b/src/core/controller.h index 54cca5cc..c061f6d8 100644 --- a/src/core/controller.h +++ b/src/core/controller.h @@ -19,6 +19,7 @@ #include "src/core/capturerequest.h" #include +#include #include #include #include @@ -92,4 +93,5 @@ private: QPointer m_trayIcon; HistoryWidget* m_history; + QMenu* m_trayIconMenu; }; From 2e7d4431cef194e2331e8af734b97567719615eb Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Thu, 3 Dec 2020 10:37:23 +0200 Subject: [PATCH 21/54] MacOS - disable CaptureToolButton --- src/tools/toolfactory.cpp | 3 ++ src/utils/confighandler.cpp | 3 ++ src/utils/configshortcuts.cpp | 3 ++ src/widgets/capture/capturetoolbutton.cpp | 48 +++++++++++++---------- src/widgets/capture/capturetoolbutton.h | 3 +- 5 files changed, 38 insertions(+), 22 deletions(-) diff --git a/src/tools/toolfactory.cpp b/src/tools/toolfactory.cpp index 65d48c9f..3a6c0487 100644 --- a/src/tools/toolfactory.cpp +++ b/src/tools/toolfactory.cpp @@ -94,9 +94,12 @@ CaptureTool* ToolFactory::CreateTool(CaptureToolButton::ButtonType t, case CaptureToolButton::TYPE_REDO: tool = new RedoTool(parent); break; +#if not(defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) case CaptureToolButton::TYPE_OPEN_APP: tool = new AppLauncher(parent); break; +#endif case CaptureToolButton::TYPE_PIXELATE: tool = new PixelateTool(parent); break; diff --git a/src/utils/confighandler.cpp b/src/utils/confighandler.cpp index 843f0cbc..531c0f1e 100644 --- a/src/utils/confighandler.cpp +++ b/src/utils/confighandler.cpp @@ -60,7 +60,10 @@ QVector ConfigHandler::getButtons() << CaptureToolButton::TYPE_COPY << CaptureToolButton::TYPE_SAVE << CaptureToolButton::TYPE_EXIT << CaptureToolButton::TYPE_IMAGEUPLOADER +#if not(defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) << CaptureToolButton::TYPE_OPEN_APP +#endif << CaptureToolButton::TYPE_PIN << CaptureToolButton::TYPE_TEXT << CaptureToolButton::TYPE_CIRCLECOUNT; } diff --git a/src/utils/configshortcuts.cpp b/src/utils/configshortcuts.cpp index 8bc28d56..4cba9c77 100644 --- a/src/utils/configshortcuts.cpp +++ b/src/utils/configshortcuts.cpp @@ -112,9 +112,12 @@ const QKeySequence& ConfigShortcuts::captureShortcutDefault( case CaptureToolButton::ButtonType::TYPE_IMAGEUPLOADER: m_ks = QKeySequence(Qt::Key_Return); break; +#if not(defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) case CaptureToolButton::ButtonType::TYPE_OPEN_APP: m_ks = QKeySequence(Qt::CTRL + Qt::Key_O); break; +#endif case CaptureToolButton::ButtonType::TYPE_PIXELATE: m_ks = QKeySequence(Qt::Key_B); break; diff --git a/src/widgets/capture/capturetoolbutton.cpp b/src/widgets/capture/capturetoolbutton.cpp index 84a1c811..637a969b 100644 --- a/src/widgets/capture/capturetoolbutton.cpp +++ b/src/widgets/capture/capturetoolbutton.cpp @@ -115,27 +115,32 @@ void CaptureToolButton::setColor(const QColor& c) QColor CaptureToolButton::m_mainColor = ConfigHandler().uiMainColorValue(); -static std::map buttonTypeOrder{ +static std::map buttonTypeOrder +{ { CaptureToolButton::TYPE_PENCIL, 0 }, - { CaptureToolButton::TYPE_DRAWER, 1 }, - { CaptureToolButton::TYPE_ARROW, 2 }, - { CaptureToolButton::TYPE_SELECTION, 3 }, - { CaptureToolButton::TYPE_RECTANGLE, 4 }, - { CaptureToolButton::TYPE_CIRCLE, 5 }, - { CaptureToolButton::TYPE_MARKER, 6 }, - { CaptureToolButton::TYPE_TEXT, 7 }, - { CaptureToolButton::TYPE_PIXELATE, 8 }, - { CaptureToolButton::TYPE_CIRCLECOUNT, 9 }, - { CaptureToolButton::TYPE_SELECTIONINDICATOR, 10 }, - { CaptureToolButton::TYPE_MOVESELECTION, 11 }, - { CaptureToolButton::TYPE_UNDO, 12 }, - { CaptureToolButton::TYPE_REDO, 13 }, - { CaptureToolButton::TYPE_COPY, 14 }, - { CaptureToolButton::TYPE_SAVE, 15 }, - { CaptureToolButton::TYPE_EXIT, 16 }, - { CaptureToolButton::TYPE_IMAGEUPLOADER, 17 }, - { CaptureToolButton::TYPE_OPEN_APP, 18 }, - { CaptureToolButton::TYPE_PIN, 19 }, + { CaptureToolButton::TYPE_DRAWER, 1 }, + { CaptureToolButton::TYPE_ARROW, 2 }, + { CaptureToolButton::TYPE_SELECTION, 3 }, + { CaptureToolButton::TYPE_RECTANGLE, 4 }, + { CaptureToolButton::TYPE_CIRCLE, 5 }, + { CaptureToolButton::TYPE_MARKER, 6 }, + { CaptureToolButton::TYPE_TEXT, 7 }, + { CaptureToolButton::TYPE_PIXELATE, 8 }, + { CaptureToolButton::TYPE_CIRCLECOUNT, 9 }, + { CaptureToolButton::TYPE_SELECTIONINDICATOR, 10 }, + { CaptureToolButton::TYPE_MOVESELECTION, 11 }, + { CaptureToolButton::TYPE_UNDO, 12 }, + { CaptureToolButton::TYPE_REDO, 13 }, + { CaptureToolButton::TYPE_COPY, 14 }, + { CaptureToolButton::TYPE_SAVE, 15 }, + { CaptureToolButton::TYPE_IMAGEUPLOADER, 16 }, +#if not(defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) + { CaptureToolButton::TYPE_OPEN_APP, 17 }, + { CaptureToolButton::TYPE_EXIT, 18 }, { CaptureToolButton::TYPE_PIN, 19 }, +#else + { CaptureToolButton::TYPE_EXIT, 17 }, { CaptureToolButton::TYPE_PIN, 18 }, +#endif }; int CaptureToolButton::getPriorityByButton(CaptureToolButton::ButtonType b) @@ -164,7 +169,10 @@ QVector CaptureToolButton::TYPE_SAVE, CaptureToolButton::TYPE_EXIT, CaptureToolButton::TYPE_IMAGEUPLOADER, +#if not(defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) CaptureToolButton::TYPE_OPEN_APP, +#endif CaptureToolButton::TYPE_PIN, CaptureToolButton::TYPE_CIRCLECOUNT, }; diff --git a/src/widgets/capture/capturetoolbutton.h b/src/widgets/capture/capturetoolbutton.h index 2a2908da..319fb9f2 100644 --- a/src/widgets/capture/capturetoolbutton.h +++ b/src/widgets/capture/capturetoolbutton.h @@ -53,8 +53,7 @@ public: TYPE_REDO = 16, TYPE_PIN = 17, TYPE_TEXT = 18, - TYPE_CIRCLECOUNT = 19, - + TYPE_CIRCLECOUNT = 19 }; Q_ENUM(ButtonType) From 719719f36b764791e772064dbd3cdc34eeb64082 Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Thu, 3 Dec 2020 15:11:02 +0200 Subject: [PATCH 22/54] MacOS and Win - fix tray menu --- src/core/controller.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core/controller.cpp b/src/core/controller.cpp index cf4041f4..9e6f9fa7 100644 --- a/src/core/controller.cpp +++ b/src/core/controller.cpp @@ -282,16 +282,16 @@ void Controller::enableTrayIcon() m_trayIcon = new QSystemTrayIcon(); m_trayIcon->setToolTip(QStringLiteral("Flameshot")); -#if not(defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(Q_OS_MAC64) || \ - defined(Q_OS_MACOS) || defined(Q_OS_MACX)) +#if not(defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) m_trayIcon->setContextMenu(m_trayIconMenu); #endif QIcon trayicon = QIcon::fromTheme("flameshot-tray", QIcon(":img/app/flameshot.png")); m_trayIcon->setIcon(trayicon); -#if not(defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(Q_OS_MAC64) || \ - defined(Q_OS_MACOS) || defined(Q_OS_MACX)) +#if not(defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) auto trayIconActivated = [this](QSystemTrayIcon::ActivationReason r) { if (r == QSystemTrayIcon::Trigger) { startVisualCapture(); @@ -306,7 +306,7 @@ void Controller::enableTrayIcon() if (m_trayIconMenu->isVisible()) { m_trayIconMenu->hide(); } else { - m_trayIconMenu->popup(QPoint(QCursor::pos().x(), 0)); + m_trayIconMenu->popup(QCursor::pos()); } }; connect(m_trayIcon, &QSystemTrayIcon::activated, this, trayIconActivated); From b4be86068d0e707043af019f96bae3da28b818e1 Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Thu, 3 Dec 2020 16:18:30 +0200 Subject: [PATCH 23/54] fix - MacOS - Black screen when saving files with a hotkey --- src/tools/toolfactory.cpp | 1 - src/widgets/capture/capturewidget.cpp | 4 ++++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/tools/toolfactory.cpp b/src/tools/toolfactory.cpp index 3a6c0487..e36de70d 100644 --- a/src/tools/toolfactory.cpp +++ b/src/tools/toolfactory.cpp @@ -112,7 +112,6 @@ CaptureTool* ToolFactory::CreateTool(CaptureToolButton::ButtonType t, case CaptureToolButton::TYPE_CIRCLECOUNT: tool = new CircleCountTool(parent); break; - default: tool = nullptr; break; diff --git a/src/widgets/capture/capturewidget.cpp b/src/widgets/capture/capturewidget.cpp index 17f403a2..90ee559c 100644 --- a/src/widgets/capture/capturewidget.cpp +++ b/src/widgets/capture/capturewidget.cpp @@ -1132,6 +1132,10 @@ void CaptureWidget::copyScreenshot() void CaptureWidget::saveScreenshot() { +#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) + showNormal(); +#endif m_captureDone = true; if (m_activeTool != nullptr) { QPainter painter(&m_context.screenshot); From dbb08d01b5d0a98bc181519604ecfebf2405e66f Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Thu, 3 Dec 2020 16:49:48 +0200 Subject: [PATCH 24/54] fix - MacOS - Black screen when saving files with tool button key --- src/tools/save/savetool.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/tools/save/savetool.cpp b/src/tools/save/savetool.cpp index 43c194b0..079be73e 100644 --- a/src/tools/save/savetool.cpp +++ b/src/tools/save/savetool.cpp @@ -18,6 +18,12 @@ #include "savetool.h" #include "src/utils/screenshotsaver.h" #include +#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) +#include "src/widgets/capture/capturewidget.h" +#include +#include +#endif SaveTool::SaveTool(QObject* parent) : AbstractActionTool(parent) @@ -55,6 +61,18 @@ CaptureTool* SaveTool::copy(QObject* parent) void SaveTool::pressed(const CaptureContext& context) { +#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) + for (QWidget* widget : qApp->topLevelWidgets()) { + QString className(widget->metaObject()->className()); + // if (className.compare("CaptureWidget") == 0) { + if (className.compare(CaptureWidget::staticMetaObject.className()) == + 0) { + widget->showNormal(); + break; + } + } +#endif if (context.savePath.isEmpty()) { emit requestAction(REQ_HIDE_GUI); bool ok = ScreenshotSaver().saveToFilesystemGUI( From 20115b339fd5a8fc291031dd0460903c32417af0 Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Thu, 3 Dec 2020 17:33:07 +0200 Subject: [PATCH 25/54] fix - MacOS - Black screen when saving files with tool button key --- src/tools/save/savetool.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/tools/save/savetool.cpp b/src/tools/save/savetool.cpp index 079be73e..ac5eae54 100644 --- a/src/tools/save/savetool.cpp +++ b/src/tools/save/savetool.cpp @@ -65,9 +65,8 @@ void SaveTool::pressed(const CaptureContext& context) defined(Q_OS_MACX)) for (QWidget* widget : qApp->topLevelWidgets()) { QString className(widget->metaObject()->className()); - // if (className.compare("CaptureWidget") == 0) { - if (className.compare(CaptureWidget::staticMetaObject.className()) == - 0) { + if (0 == + className.compare(CaptureWidget::staticMetaObject.className())) { widget->showNormal(); break; } From 97bea32f950d3198a707569c444db4597f394dda Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Fri, 4 Dec 2020 10:13:58 +0200 Subject: [PATCH 26/54] Code refactoring - clean up excess code --- src/widgets/panel/utilitypanel.cpp | 10 ---------- src/widgets/panel/utilitypanel.h | 6 ------ 2 files changed, 16 deletions(-) diff --git a/src/widgets/panel/utilitypanel.cpp b/src/widgets/panel/utilitypanel.cpp index ac392d4c..1068f906 100644 --- a/src/widgets/panel/utilitypanel.cpp +++ b/src/widgets/panel/utilitypanel.cpp @@ -137,14 +137,4 @@ void UtilityPanel::initInternalPanel() m_internalPanel->setStyleSheet( QStringLiteral("QScrollArea {background-color: %1}").arg(bgColor.name())); m_internalPanel->hide(); - - m_hide = new QPushButton(); - m_hide->setText(tr("Hide")); - m_upLayout->addWidget(m_hide); - connect(m_hide, SIGNAL(clicked()), this, SLOT(slotHidePanel())); -} - -void UtilityPanel::slotHidePanel() -{ - hide(); } diff --git a/src/widgets/panel/utilitypanel.h b/src/widgets/panel/utilitypanel.h index 7a340bf9..7486a347 100644 --- a/src/widgets/panel/utilitypanel.h +++ b/src/widgets/panel/utilitypanel.h @@ -38,13 +38,8 @@ public: void hide(); void show(); -signals: - void mouseEnter(); - void mouseLeave(); - public slots: void toggle(); - void slotHidePanel(); private: void initInternalPanel(); @@ -56,5 +51,4 @@ private: QVBoxLayout* m_layout; QPropertyAnimation* m_showAnimation; QPropertyAnimation* m_hideAnimation; - QPushButton* m_hide; }; From 134239c6d49fdf0835b95bb012f80c9b0aee5f07 Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Fri, 4 Dec 2020 12:02:44 +0200 Subject: [PATCH 27/54] fix - MacOS - Tool Settings (side panel) on retina or if primary and other display has different display sized --- src/widgets/capture/capturewidget.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/widgets/capture/capturewidget.cpp b/src/widgets/capture/capturewidget.cpp index 90ee559c..89fed9c1 100644 --- a/src/widgets/capture/capturewidget.cpp +++ b/src/widgets/capture/capturewidget.cpp @@ -695,9 +695,17 @@ void CaptureWidget::initPanel() m_panel = new UtilityPanel(this); makeChild(m_panel); +#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) + QScreen* currentScreen = QGuiApplication::screenAt(QCursor::pos()); + panelRect.moveTo(mapFromGlobal(panelRect.topLeft())); + m_panel->setFixedWidth(m_colorPicker->width() * 1.5); + m_panel->setFixedHeight(currentScreen->geometry().height()); +#else panelRect.moveTo(mapFromGlobal(panelRect.topLeft())); panelRect.setWidth(m_colorPicker->width() * 1.5); m_panel->setGeometry(panelRect); +#endif SidePanelWidget* sidePanel = new SidePanelWidget(&m_context.screenshot); connect(sidePanel, From 84e4a9c9c6bc754df13841f4511ea97761c969d6 Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Fri, 4 Dec 2020 13:55:25 +0200 Subject: [PATCH 28/54] MacOS - QSystemTrayIcon does not show context menu on primary screen on Catalina, on BigSur is ok and use native menu --- src/core/controller.cpp | 44 ++++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/src/core/controller.cpp b/src/core/controller.cpp index 9e6f9fa7..267adfde 100644 --- a/src/core/controller.cpp +++ b/src/core/controller.cpp @@ -40,6 +40,7 @@ #if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ defined(Q_OS_MACX)) +#include #include #endif @@ -282,34 +283,45 @@ void Controller::enableTrayIcon() m_trayIcon = new QSystemTrayIcon(); m_trayIcon->setToolTip(QStringLiteral("Flameshot")); -#if not(defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ - defined(Q_OS_MACX)) +#if defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX) + // Because of the following issues on MacOS "Catalina": + // https://bugreports.qt.io/browse/QTBUG-86393 + // https://developer.apple.com/forums/thread/126072 + auto currentMacOsVersion = QOperatingSystemVersion::current(); + if (currentMacOsVersion >= currentMacOsVersion.MacOSBigSur) { + m_trayIcon->setContextMenu(m_trayIconMenu); + } +#else m_trayIcon->setContextMenu(m_trayIconMenu); #endif QIcon trayicon = QIcon::fromTheme("flameshot-tray", QIcon(":img/app/flameshot.png")); m_trayIcon->setIcon(trayicon); -#if not(defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ - defined(Q_OS_MACX)) +#if defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX) + if (currentMacOsVersion < currentMacOsVersion.MacOSBigSur) { + // Because of the following issues on MacOS "Catalina": + // https://bugreports.qt.io/browse/QTBUG-86393 + // https://developer.apple.com/forums/thread/126072 + auto trayIconActivated = [this](QSystemTrayIcon::ActivationReason r) { + if (m_trayIconMenu->isVisible()) { + m_trayIconMenu->hide(); + } else { + m_trayIconMenu->popup(QCursor::pos()); + } + }; + connect( + m_trayIcon, &QSystemTrayIcon::activated, this, trayIconActivated); + } +#else auto trayIconActivated = [this](QSystemTrayIcon::ActivationReason r) { if (r == QSystemTrayIcon::Trigger) { startVisualCapture(); } }; connect(m_trayIcon, &QSystemTrayIcon::activated, this, trayIconActivated); -#else - // Because of the following issues: - // https://bugreports.qt.io/browse/QTBUG-86393 - // https://developer.apple.com/forums/thread/126072 - auto trayIconActivated = [this](QSystemTrayIcon::ActivationReason r) { - if (m_trayIconMenu->isVisible()) { - m_trayIconMenu->hide(); - } else { - m_trayIconMenu->popup(QCursor::pos()); - } - }; - connect(m_trayIcon, &QSystemTrayIcon::activated, this, trayIconActivated); #endif #ifdef Q_OS_WIN From a515fcf4df5b396ad9c10ede57ebf937577a5fef Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Sat, 5 Dec 2020 18:47:06 +0200 Subject: [PATCH 29/54] fix - undo/redo for MacOS retina displays for "TwoPointTool" objects (line, arrow, rectangle etc) --- src/tools/abstracttwopointtool.cpp | 37 +++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/src/tools/abstracttwopointtool.cpp b/src/tools/abstracttwopointtool.cpp index ab54291a..90f77cd9 100644 --- a/src/tools/abstracttwopointtool.cpp +++ b/src/tools/abstracttwopointtool.cpp @@ -16,6 +16,9 @@ // along with Flameshot. If not, see . #include "abstracttwopointtool.h" +#include +#include +#include #include namespace { @@ -71,7 +74,19 @@ bool AbstractTwoPointTool::showMousePreview() const void AbstractTwoPointTool::undo(QPixmap& pixmap) { QPainter p(&pixmap); +#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) + int devicePixelRatio = 1; + QScreen* currentScreen = QGuiApplication::screenAt(QCursor::pos()); + if (currentScreen) { + // on edge borders MacOS can return nullptr instead of current screen + devicePixelRatio = currentScreen->devicePixelRatio(); + } + p.drawPixmap(backupRect(pixmap.rect()).topLeft() / devicePixelRatio, + m_pixmapBackup); +#else p.drawPixmap(backupRect(pixmap.rect()).topLeft(), m_pixmapBackup); +#endif if (this->nameID() == ToolType::CIRCLECOUNT) { emit requestAction(REQ_DECREMENT_CIRCLE_COUNT); } @@ -109,10 +124,30 @@ void AbstractTwoPointTool::updateBackup(const QPixmap& pixmap) QRect AbstractTwoPointTool::backupRect(const QRect& limits) const { +#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) + // On edge borders MacOS returns nullptr instead of current screen, + // it means that we cannot get devicePixelRatio for the current screen, + // so we have no choice and have to save the whole screen in the + // history. Possibly commented code will work on a "Big Sur". + // QScreen* currentScreen = QGuiApplication::screenAt(QCursor::pos()); + // int devicePixelRatio = currentScreen->devicePixelRatio(); + // QRect r = QRect(m_points.first, m_points.second).normalized(); + // if (1 != devicePixelRatio) { + // r.moveTo(r.topLeft() * devicePixelRatio); + // r.setSize(r.size() * devicePixelRatio); + // } + // const int val = (m_thickness + m_padding); + // r += QMargins(val, val, val, val); + // return r.intersected(limits);; + + return limits; +#else QRect r = QRect(m_points.first, m_points.second).normalized(); - const int val = m_thickness + m_padding; + const int val = (m_thickness + m_padding); r += QMargins(val, val, val, val); return r.intersected(limits); +#endif } QPoint AbstractTwoPointTool::adjustedVector(QPoint v) const From b03488b65daf9b142316dbc4c9f4b0672c5f6319 Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Sat, 5 Dec 2020 22:23:55 +0200 Subject: [PATCH 30/54] fix - undo/redo for MacOS retina displays for "PathTool" objects (brush etc) --- src/tools/abstractpathtool.cpp | 18 ++++++++++++++++++ src/tools/abstracttwopointtool.cpp | 3 +-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/tools/abstractpathtool.cpp b/src/tools/abstractpathtool.cpp index 67a8b6d2..7096eec0 100644 --- a/src/tools/abstractpathtool.cpp +++ b/src/tools/abstractpathtool.cpp @@ -47,8 +47,17 @@ void AbstractPathTool::undo(QPixmap& pixmap) { QPainter p(&pixmap); const int val = m_thickness + m_padding; +#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) + // On edge borders MacOS returns nullptr instead of current screen, + // it means that we cannot get devicePixelRatio for the current screen, + // so we have no choice and have to save the whole screen in the + // history. + p.drawPixmap(QPoint(0, 0), m_pixmapBackup); +#else QRect area = m_backupArea + QMargins(val, val, val, val); p.drawPixmap(area.intersected(pixmap.rect()).topLeft(), m_pixmapBackup); +#endif } void AbstractPathTool::drawEnd(const QPoint& p) @@ -73,9 +82,18 @@ void AbstractPathTool::thicknessChanged(const int th) void AbstractPathTool::updateBackup(const QPixmap& pixmap) { +#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) + // On edge borders MacOS returns nullptr instead of current screen, + // it means that we cannot get devicePixelRatio for the current screen, + // so we have no choice and have to save the whole screen in the + // history. + m_pixmapBackup = pixmap; +#else const int val = m_thickness + m_padding; QRect area = m_backupArea.normalized() + QMargins(val, val, val, val); m_pixmapBackup = pixmap.copy(area); +#endif } void AbstractPathTool::addPoint(const QPoint& point) diff --git a/src/tools/abstracttwopointtool.cpp b/src/tools/abstracttwopointtool.cpp index 90f77cd9..7d211a30 100644 --- a/src/tools/abstracttwopointtool.cpp +++ b/src/tools/abstracttwopointtool.cpp @@ -139,8 +139,7 @@ QRect AbstractTwoPointTool::backupRect(const QRect& limits) const // } // const int val = (m_thickness + m_padding); // r += QMargins(val, val, val, val); - // return r.intersected(limits);; - + // return r.intersected(limits); return limits; #else QRect r = QRect(m_points.first, m_points.second).normalized(); From 3ae34418adafac9007566c8f5999e5c9c8092be9 Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Sun, 6 Dec 2020 07:33:19 +0200 Subject: [PATCH 31/54] fix - undo/redo for MacOS retina displays for "TextTool" objects --- src/tools/text/texttool.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/tools/text/texttool.cpp b/src/tools/text/texttool.cpp index c791b20d..977b07e9 100644 --- a/src/tools/text/texttool.cpp +++ b/src/tools/text/texttool.cpp @@ -130,7 +130,16 @@ CaptureTool* TextTool::copy(QObject* parent) void TextTool::undo(QPixmap& pixmap) { QPainter p(&pixmap); +#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) + // On edge borders MacOS returns nullptr instead of current screen, + // it means that we cannot get devicePixelRatio for the current screen, + // so we have no choice and have to save the whole screen in the + // history. + p.drawPixmap(QPoint(0, 0), m_pixmapBackup); +#else p.drawPixmap(m_backupArea.topLeft(), m_pixmapBackup); +#endif } void TextTool::process(QPainter& painter, @@ -144,7 +153,16 @@ void TextTool::process(QPainter& painter, QSize size(fm.boundingRect(QRect(), 0, m_text).size()); m_backupArea.setSize(size); if (recordUndo) { +#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) + // On edge borders MacOS returns nullptr instead of current screen, + // it means that we cannot get devicePixelRatio for the current screen, + // so we have no choice and have to save the whole screen in the + // history. + m_pixmapBackup = pixmap; +#else m_pixmapBackup = pixmap.copy(m_backupArea + QMargins(0, 0, 5, 5)); +#endif } // draw text painter.setFont(m_font); From 586ecb07ebcfedc479fa5b328df8b62939830480 Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Sun, 6 Dec 2020 08:18:33 +0200 Subject: [PATCH 32/54] Code refactoring - remove number of build warnings --- data/translations/Internationalization_uk.ts | 2 +- src/tools/abstractactiontool.h | 3 --- src/tools/abstractpathtool.h | 2 -- src/tools/abstracttwopointtool.h | 2 -- src/tools/copy/copytool.h | 2 +- src/tools/exit/exittool.h | 2 +- src/tools/launcher/applaunchertool.h | 2 +- src/tools/move/movetool.h | 2 +- src/tools/pin/pintool.h | 2 +- src/tools/redo/redotool.h | 2 +- src/tools/save/savetool.h | 2 +- src/tools/selection/selectiontool.h | 2 +- src/tools/sizeindicator/sizeindicatortool.h | 2 +- src/tools/storage/imguploadertool.h | 4 ++-- src/tools/undo/undotool.h | 2 +- src/widgets/draggablewidgetmaker.cpp | 2 ++ 16 files changed, 15 insertions(+), 20 deletions(-) diff --git a/data/translations/Internationalization_uk.ts b/data/translations/Internationalization_uk.ts index ed867216..51f286e3 100644 --- a/data/translations/Internationalization_uk.ts +++ b/data/translations/Internationalization_uk.ts @@ -117,7 +117,7 @@ Delay: - Затримка: + Затримка: diff --git a/src/tools/abstractactiontool.h b/src/tools/abstractactiontool.h index 0c973c91..1dba903c 100644 --- a/src/tools/abstractactiontool.h +++ b/src/tools/abstractactiontool.h @@ -36,9 +36,6 @@ public: void paintMousePreview(QPainter& painter, const CaptureContext& context) override; -protected: - virtual ToolType nameID() const = 0; - public slots: void drawEnd(const QPoint& p) override; void drawMove(const QPoint& p) override; diff --git a/src/tools/abstractpathtool.h b/src/tools/abstractpathtool.h index f1f4cfe3..8b4a93f9 100644 --- a/src/tools/abstractpathtool.h +++ b/src/tools/abstractpathtool.h @@ -42,8 +42,6 @@ protected: void updateBackup(const QPixmap& pixmap); void addPoint(const QPoint& point); - virtual ToolType nameID() const = 0; - QPixmap m_pixmapBackup; QRect m_backupArea; QColor m_color; diff --git a/src/tools/abstracttwopointtool.h b/src/tools/abstracttwopointtool.h index 061807a9..3a83bbec 100644 --- a/src/tools/abstracttwopointtool.h +++ b/src/tools/abstracttwopointtool.h @@ -53,8 +53,6 @@ protected: bool m_supportsOrthogonalAdj = false; bool m_supportsDiagonalAdj = false; - virtual ToolType nameID() const = 0; - private: QPoint adjustedVector(QPoint v) const; }; diff --git a/src/tools/copy/copytool.h b/src/tools/copy/copytool.h index f8fe081b..20f1f243 100644 --- a/src/tools/copy/copytool.h +++ b/src/tools/copy/copytool.h @@ -25,7 +25,7 @@ class CopyTool : public AbstractActionTool public: explicit CopyTool(QObject* parent = nullptr); - bool closeOnButtonPressed() const; + bool closeOnButtonPressed() const override; QIcon icon(const QColor& background, bool inEditor) const override; QString name() const override; diff --git a/src/tools/exit/exittool.h b/src/tools/exit/exittool.h index 109b3e32..be586995 100644 --- a/src/tools/exit/exittool.h +++ b/src/tools/exit/exittool.h @@ -25,7 +25,7 @@ class ExitTool : public AbstractActionTool public: explicit ExitTool(QObject* parent = nullptr); - bool closeOnButtonPressed() const; + bool closeOnButtonPressed() const override; QIcon icon(const QColor& background, bool inEditor) const override; QString name() const override; diff --git a/src/tools/launcher/applaunchertool.h b/src/tools/launcher/applaunchertool.h index 9a7513a7..9da32d00 100644 --- a/src/tools/launcher/applaunchertool.h +++ b/src/tools/launcher/applaunchertool.h @@ -25,7 +25,7 @@ class AppLauncher : public AbstractActionTool public: explicit AppLauncher(QObject* parent = nullptr); - bool closeOnButtonPressed() const; + bool closeOnButtonPressed() const override; QIcon icon(const QColor& background, bool inEditor) const override; QString name() const override; diff --git a/src/tools/move/movetool.h b/src/tools/move/movetool.h index dd36d98d..76e1f7f9 100644 --- a/src/tools/move/movetool.h +++ b/src/tools/move/movetool.h @@ -25,7 +25,7 @@ class MoveTool : public AbstractActionTool public: explicit MoveTool(QObject* parent = nullptr); - bool closeOnButtonPressed() const; + bool closeOnButtonPressed() const override; QIcon icon(const QColor& background, bool inEditor) const override; QString name() const override; diff --git a/src/tools/pin/pintool.h b/src/tools/pin/pintool.h index 0192f53d..ec781be2 100644 --- a/src/tools/pin/pintool.h +++ b/src/tools/pin/pintool.h @@ -25,7 +25,7 @@ class PinTool : public AbstractActionTool public: explicit PinTool(QObject* parent = nullptr); - bool closeOnButtonPressed() const; + bool closeOnButtonPressed() const override; QIcon icon(const QColor& background, bool inEditor) const override; QString name() const override; diff --git a/src/tools/redo/redotool.h b/src/tools/redo/redotool.h index 4c0791a0..b23b4bc9 100644 --- a/src/tools/redo/redotool.h +++ b/src/tools/redo/redotool.h @@ -25,7 +25,7 @@ class RedoTool : public AbstractActionTool public: explicit RedoTool(QObject* parent = nullptr); - bool closeOnButtonPressed() const; + bool closeOnButtonPressed() const override; QIcon icon(const QColor& background, bool inEditor) const override; QString name() const override; diff --git a/src/tools/save/savetool.h b/src/tools/save/savetool.h index 3c79e442..64f6306b 100644 --- a/src/tools/save/savetool.h +++ b/src/tools/save/savetool.h @@ -25,7 +25,7 @@ class SaveTool : public AbstractActionTool public: explicit SaveTool(QObject* parent = nullptr); - bool closeOnButtonPressed() const; + bool closeOnButtonPressed() const override; QIcon icon(const QColor& background, bool inEditor) const override; QString name() const override; diff --git a/src/tools/selection/selectiontool.h b/src/tools/selection/selectiontool.h index f8448dbd..a7151da3 100644 --- a/src/tools/selection/selectiontool.h +++ b/src/tools/selection/selectiontool.h @@ -25,7 +25,7 @@ class SelectionTool : public AbstractTwoPointTool public: explicit SelectionTool(QObject* parent = nullptr); - bool closeOnButtonPressed() const; + bool closeOnButtonPressed() const override; QIcon icon(const QColor& background, bool inEditor) const override; QString name() const override; diff --git a/src/tools/sizeindicator/sizeindicatortool.h b/src/tools/sizeindicator/sizeindicatortool.h index 26fca63c..57aad37f 100644 --- a/src/tools/sizeindicator/sizeindicatortool.h +++ b/src/tools/sizeindicator/sizeindicatortool.h @@ -25,7 +25,7 @@ class SizeIndicatorTool : public AbstractActionTool public: explicit SizeIndicatorTool(QObject* parent = nullptr); - bool closeOnButtonPressed() const; + bool closeOnButtonPressed() const override; QIcon icon(const QColor& background, bool inEditor) const override; QString name() const override; diff --git a/src/tools/storage/imguploadertool.h b/src/tools/storage/imguploadertool.h index f0928e55..59fa9503 100644 --- a/src/tools/storage/imguploadertool.h +++ b/src/tools/storage/imguploadertool.h @@ -9,12 +9,12 @@ class ImgUploaderTool : public AbstractActionTool public: explicit ImgUploaderTool(QObject* parent = nullptr); - bool closeOnButtonPressed() const; + bool closeOnButtonPressed() const override; QString name() const override; QIcon icon(const QColor& background, bool inEditor) const override; - void setCapture(const QPixmap& pixmap); + void setCapture(const QPixmap& pixmap) override; const QPixmap& capture(); public slots: diff --git a/src/tools/undo/undotool.h b/src/tools/undo/undotool.h index 140407ea..52292a19 100644 --- a/src/tools/undo/undotool.h +++ b/src/tools/undo/undotool.h @@ -25,7 +25,7 @@ class UndoTool : public AbstractActionTool public: explicit UndoTool(QObject* parent = nullptr); - bool closeOnButtonPressed() const; + bool closeOnButtonPressed() const override; QIcon icon(const QColor& background, bool inEditor) const override; QString name() const override; diff --git a/src/widgets/draggablewidgetmaker.cpp b/src/widgets/draggablewidgetmaker.cpp index c738dc62..70963e90 100644 --- a/src/widgets/draggablewidgetmaker.cpp +++ b/src/widgets/draggablewidgetmaker.cpp @@ -73,6 +73,8 @@ bool DraggableWidgetMaker::eventFilter(QObject* obj, QEvent* event) return true; } } break; + default: + break; } return QObject::eventFilter(obj, event); From ca56d08c194622f489819da5125bf4bbae1dfe98 Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Sun, 6 Dec 2020 11:57:31 +0200 Subject: [PATCH 33/54] fix - MacOS - optimize memory utilization for "TwoPoint" undo feature --- src/tools/abstracttwopointtool.cpp | 47 ++++++++++++------------------ src/tools/abstracttwopointtool.h | 2 +- 2 files changed, 20 insertions(+), 29 deletions(-) diff --git a/src/tools/abstracttwopointtool.cpp b/src/tools/abstracttwopointtool.cpp index 7d211a30..20a648bb 100644 --- a/src/tools/abstracttwopointtool.cpp +++ b/src/tools/abstracttwopointtool.cpp @@ -76,16 +76,12 @@ void AbstractTwoPointTool::undo(QPixmap& pixmap) QPainter p(&pixmap); #if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ defined(Q_OS_MACX)) - int devicePixelRatio = 1; - QScreen* currentScreen = QGuiApplication::screenAt(QCursor::pos()); - if (currentScreen) { - // on edge borders MacOS can return nullptr instead of current screen - devicePixelRatio = currentScreen->devicePixelRatio(); - } - p.drawPixmap(backupRect(pixmap.rect()).topLeft() / devicePixelRatio, - m_pixmapBackup); + const qreal pixelRatio = pixmap.devicePixelRatio(); + p.drawPixmap(backupRect(pixmap).topLeft() / pixelRatio, m_pixmapBackup); + p.drawPixmap(backupRect(pixmap).topLeft() / pixelRatio, m_pixmapBackup); #else - p.drawPixmap(backupRect(pixmap.rect()).topLeft(), m_pixmapBackup); + p.drawPixmap(backupRect(pixmap).topLeft(), m_pixmapBackup); + p.drawPixmap(backupRect(pixmap).topLeft(), m_pixmapBackup); #endif if (this->nameID() == ToolType::CIRCLECOUNT) { emit requestAction(REQ_DECREMENT_CIRCLE_COUNT); @@ -119,34 +115,29 @@ void AbstractTwoPointTool::thicknessChanged(const int th) void AbstractTwoPointTool::updateBackup(const QPixmap& pixmap) { - m_pixmapBackup = pixmap.copy(backupRect(pixmap.rect())); + m_pixmapBackup = pixmap.copy(backupRect(pixmap)); } -QRect AbstractTwoPointTool::backupRect(const QRect& limits) const +QRect AbstractTwoPointTool::backupRect(const QPixmap& pixmap) const { + const QRect& limits = pixmap.rect(); + QRect r = QRect(m_points.first, m_points.second).normalized(); #if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ defined(Q_OS_MACX)) - // On edge borders MacOS returns nullptr instead of current screen, - // it means that we cannot get devicePixelRatio for the current screen, - // so we have no choice and have to save the whole screen in the - // history. Possibly commented code will work on a "Big Sur". - // QScreen* currentScreen = QGuiApplication::screenAt(QCursor::pos()); - // int devicePixelRatio = currentScreen->devicePixelRatio(); - // QRect r = QRect(m_points.first, m_points.second).normalized(); - // if (1 != devicePixelRatio) { - // r.moveTo(r.topLeft() * devicePixelRatio); - // r.setSize(r.size() * devicePixelRatio); - // } - // const int val = (m_thickness + m_padding); - // r += QMargins(val, val, val, val); - // return r.intersected(limits); - return limits; + // Not sure how will it work on 4k and fullHd on Linux or Windows with a + // capture of different displays with different DPI, so let it be MacOS + // specific only. + const qreal pixelRatio = pixmap.devicePixelRatio(); + if (1 != pixelRatio) { + r.moveTo(r.topLeft() * pixelRatio); + r.setSize(r.size() * pixelRatio); + } + const int val = (m_thickness + m_padding) * pixelRatio; #else - QRect r = QRect(m_points.first, m_points.second).normalized(); const int val = (m_thickness + m_padding); +#endif r += QMargins(val, val, val, val); return r.intersected(limits); -#endif } QPoint AbstractTwoPointTool::adjustedVector(QPoint v) const diff --git a/src/tools/abstracttwopointtool.h b/src/tools/abstracttwopointtool.h index 3a83bbec..2bfe46e8 100644 --- a/src/tools/abstracttwopointtool.h +++ b/src/tools/abstracttwopointtool.h @@ -41,7 +41,7 @@ public slots: protected: void updateBackup(const QPixmap& pixmap); - QRect backupRect(const QRect& limits) const; + QRect backupRect(const QPixmap& pixmap) const; QPixmap m_pixmapBackup; QPair m_points; From a50ebf0349aa2351270f3504ddec3fb8ea61cb04 Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Sun, 6 Dec 2020 12:25:41 +0200 Subject: [PATCH 34/54] fix - MacOS - optimize memory utilization for "PathTool" undo feature --- src/tools/abstractpathtool.cpp | 40 +++++++++++++++++++----------- src/tools/abstractpathtool.h | 1 + src/tools/abstracttwopointtool.cpp | 5 ++-- 3 files changed, 29 insertions(+), 17 deletions(-) diff --git a/src/tools/abstractpathtool.cpp b/src/tools/abstractpathtool.cpp index 7096eec0..c4a771b9 100644 --- a/src/tools/abstractpathtool.cpp +++ b/src/tools/abstractpathtool.cpp @@ -46,17 +46,15 @@ bool AbstractPathTool::showMousePreview() const void AbstractPathTool::undo(QPixmap& pixmap) { QPainter p(&pixmap); - const int val = m_thickness + m_padding; #if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ defined(Q_OS_MACX)) - // On edge borders MacOS returns nullptr instead of current screen, - // it means that we cannot get devicePixelRatio for the current screen, - // so we have no choice and have to save the whole screen in the - // history. - p.drawPixmap(QPoint(0, 0), m_pixmapBackup); + // Not sure how will it work on 4k and fullHd on Linux or Windows with a + // capture of different displays with different DPI, so let it be MacOS + // specific only. + const qreal pixelRatio = pixmap.devicePixelRatio(); + p.drawPixmap(backupRect(pixmap).topLeft() / pixelRatio, m_pixmapBackup); #else - QRect area = m_backupArea + QMargins(val, val, val, val); - p.drawPixmap(area.intersected(pixmap.rect()).topLeft(), m_pixmapBackup); + p.drawPixmap(backupRect(pixmap).topLeft(), m_pixmapBackup); #endif } @@ -82,18 +80,30 @@ void AbstractPathTool::thicknessChanged(const int th) void AbstractPathTool::updateBackup(const QPixmap& pixmap) { + m_pixmapBackup = pixmap.copy(backupRect(pixmap)); +} + +QRect AbstractPathTool::backupRect(const QPixmap& pixmap) const +{ + const QRect& limits = pixmap.rect(); #if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ defined(Q_OS_MACX)) - // On edge borders MacOS returns nullptr instead of current screen, - // it means that we cannot get devicePixelRatio for the current screen, - // so we have no choice and have to save the whole screen in the - // history. - m_pixmapBackup = pixmap; + // Not sure how will it work on 4k and fullHd on Linux or Windows with a + // capture of different displays with different DPI, so let it be MacOS + // specific only. + const qreal pixelRatio = pixmap.devicePixelRatio(); + const int val = (m_thickness + m_padding) * pixelRatio; + QRect r = m_backupArea.normalized(); + if (1 != pixelRatio) { + r.moveTo(r.topLeft() * pixelRatio); + r.setSize(r.size() * pixelRatio); + } #else const int val = m_thickness + m_padding; - QRect area = m_backupArea.normalized() + QMargins(val, val, val, val); - m_pixmapBackup = pixmap.copy(area); + QRect r = m_backupArea.normalized(); #endif + r += QMargins(val, val, val, val); + return r.intersected(limits); } void AbstractPathTool::addPoint(const QPoint& point) diff --git a/src/tools/abstractpathtool.h b/src/tools/abstractpathtool.h index 8b4a93f9..4b80016f 100644 --- a/src/tools/abstractpathtool.h +++ b/src/tools/abstractpathtool.h @@ -41,6 +41,7 @@ public slots: protected: void updateBackup(const QPixmap& pixmap); void addPoint(const QPoint& point); + QRect backupRect(const QPixmap& pixmap) const; QPixmap m_pixmapBackup; QRect m_backupArea; diff --git a/src/tools/abstracttwopointtool.cpp b/src/tools/abstracttwopointtool.cpp index 20a648bb..681d8312 100644 --- a/src/tools/abstracttwopointtool.cpp +++ b/src/tools/abstracttwopointtool.cpp @@ -76,12 +76,13 @@ void AbstractTwoPointTool::undo(QPixmap& pixmap) QPainter p(&pixmap); #if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ defined(Q_OS_MACX)) + // Not sure how will it work on 4k and fullHd on Linux or Windows with a + // capture of different displays with different DPI, so let it be MacOS + // specific only. const qreal pixelRatio = pixmap.devicePixelRatio(); p.drawPixmap(backupRect(pixmap).topLeft() / pixelRatio, m_pixmapBackup); - p.drawPixmap(backupRect(pixmap).topLeft() / pixelRatio, m_pixmapBackup); #else p.drawPixmap(backupRect(pixmap).topLeft(), m_pixmapBackup); - p.drawPixmap(backupRect(pixmap).topLeft(), m_pixmapBackup); #endif if (this->nameID() == ToolType::CIRCLECOUNT) { emit requestAction(REQ_DECREMENT_CIRCLE_COUNT); From bf652c8f5addb279dcde2a870cbcff6102eadf45 Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Sun, 6 Dec 2020 14:10:48 +0200 Subject: [PATCH 35/54] fix - MacOS - optimize memory utilization for "TextTool" undo feature --- src/tools/text/texttool.cpp | 43 +++++++++++++++++++++++-------------- src/tools/text/texttool.h | 1 + 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src/tools/text/texttool.cpp b/src/tools/text/texttool.cpp index 977b07e9..7fea1bdf 100644 --- a/src/tools/text/texttool.cpp +++ b/src/tools/text/texttool.cpp @@ -132,11 +132,11 @@ void TextTool::undo(QPixmap& pixmap) QPainter p(&pixmap); #if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ defined(Q_OS_MACX)) - // On edge borders MacOS returns nullptr instead of current screen, - // it means that we cannot get devicePixelRatio for the current screen, - // so we have no choice and have to save the whole screen in the - // history. - p.drawPixmap(QPoint(0, 0), m_pixmapBackup); + // Not sure how will it work on 4k and fullHd on Linux or Windows with a + // capture of different displays with different DPI, so let it be MacOS + // specific only. + const qreal pixelRatio = pixmap.devicePixelRatio(); + p.drawPixmap(backupRect(pixmap).topLeft() / pixelRatio, m_pixmapBackup); #else p.drawPixmap(m_backupArea.topLeft(), m_pixmapBackup); #endif @@ -153,21 +153,32 @@ void TextTool::process(QPainter& painter, QSize size(fm.boundingRect(QRect(), 0, m_text).size()); m_backupArea.setSize(size); if (recordUndo) { -#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ - defined(Q_OS_MACX)) - // On edge borders MacOS returns nullptr instead of current screen, - // it means that we cannot get devicePixelRatio for the current screen, - // so we have no choice and have to save the whole screen in the - // history. - m_pixmapBackup = pixmap; -#else - m_pixmapBackup = pixmap.copy(m_backupArea + QMargins(0, 0, 5, 5)); -#endif + m_pixmapBackup = pixmap.copy(backupRect(pixmap)); } // draw text painter.setFont(m_font); painter.setPen(m_color); - painter.drawText(m_backupArea + QMargins(-5, -5, 5, 5), m_text); + const int val = 5; + painter.drawText(m_backupArea + QMargins(-val, -val, val, val), m_text); +} + +QRect TextTool::backupRect(const QPixmap& pixmap) const +{ + const QRect& limits = pixmap.rect(); + QRect r = m_backupArea.normalized(); +#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) + const qreal pixelRatio = pixmap.devicePixelRatio(); + const int val = 5 * pixelRatio; + if (1 != pixelRatio) { + r.moveTo(r.topLeft() * pixelRatio); + r.setSize(r.size() * pixelRatio); + } +#else + const int val = 5; +#endif + r += QMargins(0, 0, val, val); + return r.intersected(limits); } void TextTool::paintMousePreview(QPainter& painter, diff --git a/src/tools/text/texttool.h b/src/tools/text/texttool.h index 136bf05e..11ff875a 100644 --- a/src/tools/text/texttool.h +++ b/src/tools/text/texttool.h @@ -51,6 +51,7 @@ public: protected: ToolType nameID() const override; + QRect backupRect(const QPixmap& pixmap) const; public slots: void drawEnd(const QPoint& p) override; From beb09bd0cf6dd63bec870175d50dfeec11ba2efd Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Sun, 6 Dec 2020 11:22:34 +0200 Subject: [PATCH 36/54] fix - MacOS - pixelate on Retina displays --- src/tools/pixelate/pixelatetool.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/tools/pixelate/pixelatetool.cpp b/src/tools/pixelate/pixelatetool.cpp index 62de8dbf..13b3d4d4 100644 --- a/src/tools/pixelate/pixelatetool.cpp +++ b/src/tools/pixelate/pixelatetool.cpp @@ -22,7 +22,6 @@ #include #include #include -#include PixelateTool::PixelateTool(QObject* parent) : AbstractTwoPointTool(parent) @@ -65,13 +64,12 @@ void PixelateTool::process(QPainter& painter, QPoint& p0 = m_points.first; QPoint& p1 = m_points.second; QRect selection = QRect(p0, p1).normalized(); + auto pixelRatio = pixmap.devicePixelRatio(); + QRect selectionScaled = + QRect(p0 * pixelRatio, p1 * pixelRatio).normalized(); // If thickness is less than 1, use old blur process if (m_thickness <= 1) { - auto pixelRatio = pixmap.devicePixelRatio(); - - QRect selectionScaled = - QRect(p0 * pixelRatio, p1 * pixelRatio).normalized(); QGraphicsBlurEffect* blur = new QGraphicsBlurEffect; blur->setBlurRadius(10); @@ -93,7 +91,7 @@ void PixelateTool::process(QPainter& painter, int height = selection.height() * (0.5 / qMax(1, m_thickness)); QSize size = QSize(qMax(width, 1), qMax(height, 1)); - QPixmap t = pixmap.copy(selection); + QPixmap t = pixmap.copy(selectionScaled); t = t.scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); t = t.scaled(selection.width(), selection.height()); painter.drawImage(selection, t.toImage()); From 7e09b818c75472cce311836855eaeedb6e10db27 Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Tue, 8 Dec 2020 09:19:05 +0200 Subject: [PATCH 37/54] fix - MacOS - In shortcuts: Ctrl now is Cmd while Ctrl is Meta --- src/config/shortcutswidget.cpp | 25 +++++++++++++++++++++++++ src/config/shortcutswidget.h | 5 +++++ src/utils/configshortcuts.cpp | 3 +++ 3 files changed, 33 insertions(+) diff --git a/src/config/shortcutswidget.cpp b/src/config/shortcutswidget.cpp index b11e9afa..75386cab 100644 --- a/src/config/shortcutswidget.cpp +++ b/src/config/shortcutswidget.cpp @@ -86,7 +86,13 @@ void ShortcutsWidget::initInfoTable() for (int i = 0; i < shortcuts().size(); ++i) { m_table->setItem(i, 0, new QTableWidgetItem(m_shortcuts.at(i).at(1))); +#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) + QTableWidgetItem* item = + new QTableWidgetItem(nativeOSHotKeyText(m_shortcuts.at(i).at(2))); +#else QTableWidgetItem* item = new QTableWidgetItem(m_shortcuts.at(i).at(2)); +#endif item->setTextAlignment(Qt::AlignCenter); m_table->setItem(i, 1, item); @@ -138,8 +144,14 @@ void ShortcutsWidget::slotShortcutCellClicked(int row, int col) } if (m_config.setShortcut(shortcutName, shortcutValue.toString())) { +#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) + QTableWidgetItem* item = new QTableWidgetItem( + nativeOSHotKeyText(shortcutValue.toString())); +#else QTableWidgetItem* item = new QTableWidgetItem(shortcutValue.toString()); +#endif item->setTextAlignment(Qt::AlignCenter); item->setFlags(item->flags() ^ Qt::ItemIsEditable); m_table->setItem(row, col, item); @@ -148,3 +160,16 @@ void ShortcutsWidget::slotShortcutCellClicked(int row, int col) delete setShortcutDialog; } } + +#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) +const QString& ShortcutsWidget::nativeOSHotKeyText(const QString& text) +{ + m_res = text; + m_res.replace("Ctrl+", "⌘"); + m_res.replace("Alt+", "⌥"); + m_res.replace("Meta+", "⌃"); + m_res.replace("Shift+", "⇧"); + return m_res; +} +#endif \ No newline at end of file diff --git a/src/config/shortcutswidget.h b/src/config/shortcutswidget.h index 20fc3909..8845c45b 100644 --- a/src/config/shortcutswidget.h +++ b/src/config/shortcutswidget.h @@ -36,11 +36,16 @@ public: private: void initInfoTable(); +#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) + const QString& nativeOSHotKeyText(const QString& text); +#endif private slots: void slotShortcutCellClicked(int, int); private: + QString m_res; ConfigHandler m_config; QTableWidget* m_table; QVBoxLayout* m_layout; diff --git a/src/utils/configshortcuts.cpp b/src/utils/configshortcuts.cpp index 4cba9c77..432ae60c 100644 --- a/src/utils/configshortcuts.cpp +++ b/src/utils/configshortcuts.cpp @@ -54,10 +54,13 @@ const QVector& ConfigShortcuts::captureShortcutsDefault( m_shortcuts << (QStringList() << "" << QObject::tr("Quit capture") << QKeySequence(Qt::Key_Escape).toString()); +#if not(defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) m_shortcuts << (QStringList() << "" << QObject::tr("Screenshot history") << "Shift+Print Screen"); m_shortcuts << (QStringList() << "" << QObject::tr("Capture screen") << "Print Screen"); +#endif m_shortcuts << (QStringList() << "" << QObject::tr("Show color picker") << "Right Click"); m_shortcuts << (QStringList() From d48d4299e27632e52a78943dd746ee2759e2fe79 Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Tue, 8 Dec 2020 12:32:03 +0200 Subject: [PATCH 38/54] fix - stretch to fit shortcuts table on the configuration window --- src/config/configwindow.cpp | 4 ++-- src/config/shortcutswidget.cpp | 8 +++----- src/config/shortcutswidget.h | 3 +++ 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/config/configwindow.cpp b/src/config/configwindow.cpp index e7d5be6a..48e5a270 100644 --- a/src/config/configwindow.cpp +++ b/src/config/configwindow.cpp @@ -37,8 +37,8 @@ ConfigWindow::ConfigWindow(QWidget* parent) : QTabWidget(parent) { setAttribute(Qt::WA_DeleteOnClose); - const int size = GlobalValues::buttonBaseSize() * 12; - setMinimumSize(size, size); + setMinimumSize(GlobalValues::buttonBaseSize() * 15, + GlobalValues::buttonBaseSize() * 12); setWindowIcon(QIcon(":img/app/flameshot.svg")); setWindowTitle(tr("Configuration")); diff --git a/src/config/shortcutswidget.cpp b/src/config/shortcutswidget.cpp index 75386cab..24c27c1d 100644 --- a/src/config/shortcutswidget.cpp +++ b/src/config/shortcutswidget.cpp @@ -48,7 +48,7 @@ ShortcutsWidget::ShortcutsWidget(QWidget* parent) #endif m_layout = new QVBoxLayout(this); - m_layout->setAlignment(Qt::AlignHCenter); + m_layout->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); m_shortcuts = m_config.shortcuts(); initInfoTable(); @@ -116,10 +116,8 @@ void ShortcutsWidget::initInfoTable() // adjust size m_table->resizeColumnsToContents(); m_table->resizeRowsToContents(); - m_table->setMinimumWidth(400); - m_table->setMaximumWidth(600); - - m_table->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch); + m_table->horizontalHeader()->setMinimumSectionSize(200); + m_table->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); m_table->horizontalHeader()->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); } diff --git a/src/config/shortcutswidget.h b/src/config/shortcutswidget.h index 8845c45b..3d01657a 100644 --- a/src/config/shortcutswidget.h +++ b/src/config/shortcutswidget.h @@ -45,7 +45,10 @@ private slots: void slotShortcutCellClicked(int, int); private: +#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) QString m_res; +#endif ConfigHandler m_config; QTableWidget* m_table; QVBoxLayout* m_layout; From 9d968058dc369fe9082dd3f6f179a6f26af290e8 Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Tue, 8 Dec 2020 14:59:17 +0200 Subject: [PATCH 39/54] fix - Touchpad - Change of the tool thickness in editor --- src/widgets/capture/capturewidget.cpp | 33 ++++++++++++++++++++++++++- src/widgets/capture/capturewidget.h | 1 + 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/widgets/capture/capturewidget.cpp b/src/widgets/capture/capturewidget.cpp index 89fed9c1..7d240965 100644 --- a/src/widgets/capture/capturewidget.cpp +++ b/src/widgets/capture/capturewidget.cpp @@ -38,6 +38,7 @@ #include "src/widgets/orientablepushbutton.h" #include "src/widgets/panel/sidepanelwidget.h" #include +#include #include #include #include @@ -68,6 +69,7 @@ CaptureWidget::CaptureWidget(const uint id, , m_mouseOverHandle(SelectionWidget::NO_SIDE) , m_id(id) { + m_lastMouseWheel = 0; // Base config of the widget m_eventFilter = new HoverEventFilter(this); connect(m_eventFilter, @@ -619,7 +621,36 @@ void CaptureWidget::keyReleaseEvent(QKeyEvent* e) void CaptureWidget::wheelEvent(QWheelEvent* e) { - m_context.thickness += e->angleDelta().y() / 120; + /* Mouse scroll usually gives value 120, not more or less, just how many + * times. + * Touchpad gives the value 2 or more (usually 2-8), it doesn't give + * too big values like mouse wheel on normal scrolling, so it is almost + * impossible to scroll. It's easier to calculate number of requests and do + * not accept events faster that one in 200ms. + * */ + int thicknessOffset = 0; + if (e->angleDelta().y() >= 60) { + // mouse scroll (wheel) increment + thicknessOffset = 1; + } else if (e->angleDelta().y() <= -60) { + // mouse scroll (wheel) decrement + thicknessOffset = -1; + } else { + // touchpad scroll + qint64 current = QDateTime::currentMSecsSinceEpoch(); + if ((current - m_lastMouseWheel) > 200) { + if (e->angleDelta().y() > 0) { + thicknessOffset = 1; + } else if (e->angleDelta().y() < 0) { + thicknessOffset = -1; + } + m_lastMouseWheel = current; + } else { + return; + } + } + + m_context.thickness += thicknessOffset; m_context.thickness = qBound(0, m_context.thickness, 100); QPoint topLeft = qApp->desktop() diff --git a/src/widgets/capture/capturewidget.h b/src/widgets/capture/capturewidget.h index 6797fed6..808f8349 100644 --- a/src/widgets/capture/capturewidget.h +++ b/src/widgets/capture/capturewidget.h @@ -145,6 +145,7 @@ private: QRect extendedRect(QRect* r) const; private: + quint64 m_lastMouseWheel; QUndoStack m_undoStack; QPointer m_sizeIndButton; // Last pressed button From 6bd1f8c6a8aa995865395917e201468cae989267 Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Tue, 8 Dec 2020 16:33:30 +0200 Subject: [PATCH 40/54] fix - ESC - press key twice to exit in the CaptureWidget --- src/widgets/capture/capturewidget.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/widgets/capture/capturewidget.cpp b/src/widgets/capture/capturewidget.cpp index 7d240965..656691a3 100644 --- a/src/widgets/capture/capturewidget.cpp +++ b/src/widgets/capture/capturewidget.cpp @@ -725,6 +725,7 @@ void CaptureWidget::initPanel() } m_panel = new UtilityPanel(this); + m_panel->hide(); makeChild(m_panel); #if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ defined(Q_OS_MACX)) From 1debeb8d6e7f21ca06ccefa5c8d2031c7f2c92ed Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Tue, 8 Dec 2020 16:54:30 +0200 Subject: [PATCH 41/54] Release 0.8.5.5 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f2274c1..7d924ef8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ endif() project( flameshot - VERSION 0.8.5.4 + VERSION 0.8.5.5 LANGUAGES CXX) set(PROJECT_NAME_CAPITALIZED "Flameshot") From 784da1a652356a93b6e98e45768d021cb831b1bb Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Wed, 9 Dec 2020 18:12:37 +0200 Subject: [PATCH 42/54] Update to a new version notification --- src/core/controller.cpp | 80 ++++++++++++- src/core/controller.h | 17 +++ src/tools/storage/s3/imgs3settings.cpp | 1 + src/utils/confighandler.cpp | 10 ++ src/utils/confighandler.h | 3 + src/widgets/CMakeLists.txt | 2 + src/widgets/capture/capturewidget.cpp | 30 ++++- src/widgets/capture/capturewidget.h | 4 + src/widgets/updatenotificationwidget.cpp | 141 +++++++++++++++++++++++ src/widgets/updatenotificationwidget.h | 47 ++++++++ 10 files changed, 329 insertions(+), 6 deletions(-) create mode 100644 src/widgets/updatenotificationwidget.cpp create mode 100644 src/widgets/updatenotificationwidget.h diff --git a/src/core/controller.cpp b/src/core/controller.cpp index 267adfde..bb899c7c 100644 --- a/src/core/controller.cpp +++ b/src/core/controller.cpp @@ -30,8 +30,14 @@ #include #include #include +#include #include +#include +#include #include +#include +#include +#include #include #ifdef Q_OS_WIN @@ -49,10 +55,12 @@ Controller::Controller() : m_captureWindow(nullptr) + , m_history(nullptr) + , m_trayIconMenu(nullptr) + , m_networkCheckUpdates(nullptr) + , m_showCheckAppUpdateStatus(false) { - m_history = nullptr; - m_trayIconMenu = nullptr; - + m_appLatestVersion = QStringLiteral(APP_VERSION).replace("v", ""); qApp->setQuitOnLastWindowClosed(false); // set default shortcusts if not set yet @@ -84,6 +92,7 @@ Controller::Controller() QScreen* currentScreen = QGuiApplication::screenAt(QCursor::pos()); currentScreen->grabWindow(QApplication::desktop()->winId(), 0, 0, 1, 1); #endif + getLatestAvailableVersion(); } Controller::~Controller() @@ -106,6 +115,58 @@ void Controller::enableExports() this, &Controller::captureFailed, this, &Controller::handleCaptureFailed); } +void Controller::getLatestAvailableVersion() +{ + // This features is required for MacOS and Windows user and for Linux users + // who installed Flameshot not from the repository. + m_networkCheckUpdates = new QNetworkAccessManager(); + m_networkCheckUpdates = new QNetworkAccessManager(this); + QNetworkRequest requestCheckUpdates(QUrl(FLAMESHOT_APP_VERSION_URL)); + connect(m_networkCheckUpdates, + &QNetworkAccessManager::finished, + this, + &Controller::handleReplyCheckUpdates); + m_networkCheckUpdates->get(requestCheckUpdates); +} + +void Controller::handleReplyCheckUpdates(QNetworkReply* reply) +{ + if (reply->error() == QNetworkReply::NoError) { + QJsonDocument response = QJsonDocument::fromJson(reply->readAll()); + QJsonObject json = response.object(); + m_appLatestVersion = json["tag_name"].toString().replace("v", ""); + if (m_appLatestVersion.compare( + QStringLiteral(APP_VERSION).replace("v", "")) < 0) { + // Next commented lines are for debugging + // if (m_appLatestVersion.compare( + // QStringLiteral("v0.8.5.4").replace("v", "")) > 0) { + m_appLatestUrl = json["html_url"].toString(); + QString newVersion = + tr("New version %1 is available").arg(m_appLatestVersion); + m_appUpdates->setText(newVersion); + if (m_showCheckAppUpdateStatus) { + sendTrayNotification(newVersion, "Flameshot", 5); + QDesktopServices::openUrl(QUrl(m_appLatestUrl)); + } + } else if (m_showCheckAppUpdateStatus) { + sendTrayNotification( + tr("You have the latest version"), "Flameshot", 5); + } + } + // nothing to do on fails, is not critical for checking for updates + m_showCheckAppUpdateStatus = false; +} + +void Controller::appUpdates() +{ + if (m_appLatestUrl.isEmpty()) { + m_showCheckAppUpdateStatus = true; + getLatestAvailableVersion(); + } else { + QDesktopServices::openUrl(QUrl(m_appLatestUrl)); + } +} + void Controller::requestCapture(const CaptureRequest& request) { uint id = request.id(); @@ -170,13 +231,18 @@ void Controller::startVisualCapture(const uint id, #elif (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ defined(Q_OS_MACX)) // In "Emulate fullscreen mode" - // m_captureWindow->show(); m_captureWindow->showFullScreen(); m_captureWindow->activateWindow(); m_captureWindow->raise(); #else m_captureWindow->showFullScreen(); #endif + if (!m_appLatestUrl.isEmpty() && + 0 != m_appLatestVersion.compare( + ConfigHandler().ignoreUpdateToVersion())) { + m_captureWindow->showAppUpdateNotification(m_appLatestVersion, + m_appLatestUrl); + } } else { emit captureFailed(id); } @@ -262,6 +328,10 @@ void Controller::enableTrayIcon() configAction, &QAction::triggered, this, &Controller::openConfigWindow); QAction* infoAction = new QAction(tr("&About"), this); connect(infoAction, &QAction::triggered, this, &Controller::openInfoWindow); + + m_appUpdates = new QAction(tr("Check for updates"), this); + connect(m_appUpdates, &QAction::triggered, this, &Controller::appUpdates); + QAction* quitAction = new QAction(tr("&Quit"), this); connect(quitAction, &QAction::triggered, qApp, &QCoreApplication::quit); @@ -277,6 +347,8 @@ void Controller::enableTrayIcon() m_trayIconMenu->addAction(recentAction); m_trayIconMenu->addSeparator(); m_trayIconMenu->addAction(configAction); + m_trayIconMenu->addSeparator(); + m_trayIconMenu->addAction(m_appUpdates); m_trayIconMenu->addAction(infoAction); m_trayIconMenu->addSeparator(); m_trayIconMenu->addAction(quitAction); diff --git a/src/core/controller.h b/src/core/controller.h index c061f6d8..3ffff381 100644 --- a/src/core/controller.h +++ b/src/core/controller.h @@ -17,6 +17,9 @@ #pragma once +#define FLAMESHOT_APP_VERSION_URL \ + "https://api.github.com/repos/namecheap/flameshot/releases/latest" + #include "src/core/capturerequest.h" #include #include @@ -32,6 +35,8 @@ class InfoWindow; class QSystemTrayIcon; class CaptureLauncher; class HistoryWidget; +class QNetworkAccessManager; +class QNetworkReply; using lambda = std::function; class Controller : public QObject @@ -57,6 +62,7 @@ public slots: void openConfigWindow(); void openInfoWindow(); + void appUpdates(); void openLauncherWindow(); void enableTrayIcon(); void disableTrayIcon(); @@ -78,13 +84,22 @@ private slots: void handleCaptureTaken(uint id, QPixmap p); void handleCaptureFailed(uint id); + void handleReplyCheckUpdates(QNetworkReply* reply); + private: Controller(); + void getLatestAvailableVersion(); // replace QTimer::singleShot introduced in Qt 5.4 // the actual target Qt version is 5.3 void doLater(int msec, QObject* receiver, lambda func); + // class members + QAction* m_appUpdates; + QString m_appLatestUrl; + QString m_appLatestVersion; + bool m_showCheckAppUpdateStatus; + QMap m_requestMap; QPointer m_captureWindow; QPointer m_infoWindow; @@ -94,4 +109,6 @@ private: HistoryWidget* m_history; QMenu* m_trayIconMenu; + + QNetworkAccessManager* m_networkCheckUpdates; }; diff --git a/src/tools/storage/s3/imgs3settings.cpp b/src/tools/storage/s3/imgs3settings.cpp index 5ce12c3c..6f8df23a 100644 --- a/src/tools/storage/s3/imgs3settings.cpp +++ b/src/tools/storage/s3/imgs3settings.cpp @@ -1,4 +1,5 @@ #include "imgs3settings.h" +#include "src/core/controller.h" #include "src/tools/storage/imgstorages.h" #include "src/utils/confighandler.h" #include diff --git a/src/utils/confighandler.cpp b/src/utils/confighandler.cpp index 531c0f1e..2f21c061 100644 --- a/src/utils/confighandler.cpp +++ b/src/utils/confighandler.cpp @@ -236,6 +236,16 @@ void ConfigHandler::setShowSidePanelButton(const bool showSidePanelButton) showSidePanelButton); } +void ConfigHandler::setIgnoreUpdateToVersion(const QString& text) +{ + m_settings.setValue(QStringLiteral("ignoreUpdateToVersion"), text); +} + +QString ConfigHandler::ignoreUpdateToVersion() +{ + return m_settings.value(QStringLiteral("ignoreUpdateToVersion")).toString(); +} + bool ConfigHandler::desktopNotificationValue() { bool res = true; diff --git a/src/utils/confighandler.h b/src/utils/confighandler.h index 8670a893..637dcbf9 100644 --- a/src/utils/confighandler.h +++ b/src/utils/confighandler.h @@ -97,6 +97,9 @@ public: void setDefaults(); void setAllTheButtons(); + void setIgnoreUpdateToVersion(const QString& text); + QString ignoreUpdateToVersion(); + QVector shortcuts(); void setShortcutsDefault(); bool setShortcut(const QString&, const QString&); diff --git a/src/widgets/CMakeLists.txt b/src/widgets/CMakeLists.txt index 55eee41a..dfb6b6ef 100644 --- a/src/widgets/CMakeLists.txt +++ b/src/widgets/CMakeLists.txt @@ -12,6 +12,7 @@ target_sources( notificationwidget.h orientablepushbutton.h historywidget.h + updatenotificationwidget.h ) target_sources( @@ -24,4 +25,5 @@ target_sources( notificationwidget.cpp orientablepushbutton.cpp historywidget.cpp + updatenotificationwidget.cpp ) diff --git a/src/widgets/capture/capturewidget.cpp b/src/widgets/capture/capturewidget.cpp index 656691a3..21fd1b09 100644 --- a/src/widgets/capture/capturewidget.cpp +++ b/src/widgets/capture/capturewidget.cpp @@ -24,7 +24,6 @@ // #include "capturewidget.h" -#include "src/core/controller.h" #include "src/tools/storage/storagemanager.h" #include "src/tools/toolfactory.h" #include "src/utils/colorutils.h" @@ -37,6 +36,7 @@ #include "src/widgets/capture/notifierbox.h" #include "src/widgets/orientablepushbutton.h" #include "src/widgets/panel/sidepanelwidget.h" +#include "src/widgets/updatenotificationwidget.h" #include #include #include @@ -68,8 +68,9 @@ CaptureWidget::CaptureWidget(const uint id, , m_toolWidget(nullptr) , m_mouseOverHandle(SelectionWidget::NO_SIDE) , m_id(id) + , m_lastMouseWheel(0) + , m_updateNotificationWidget(nullptr) { - m_lastMouseWheel = 0; // Base config of the widget m_eventFilter = new HoverEventFilter(this); connect(m_eventFilter, @@ -294,7 +295,19 @@ void CaptureWidget::paintEvent(QPaintEvent*) painter.setClipRect(rect()); if (m_showInitialMsg) { +#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) + QRect helpRect; + QScreen* currentScreen = QGuiApplication::screenAt(QCursor::pos()); + if (currentScreen) { + helpRect = currentScreen->geometry(); + } else { + helpRect = QGuiApplication::primaryScreen()->geometry(); + } +#else QRect helpRect = QGuiApplication::primaryScreen()->geometry(); +#endif + helpRect.moveTo(mapFromGlobal(helpRect.topLeft())); QString helpTxt = @@ -764,6 +777,19 @@ void CaptureWidget::initPanel() m_panel->pushWidget(new QUndoView(&m_undoStack, this)); } +void CaptureWidget::showAppUpdateNotification(const QString& appLatestVersion, + const QString& appLatestUrl) +{ + if (nullptr == m_updateNotificationWidget) { + m_updateNotificationWidget = + new UpdateNotificationWidget(this, appLatestVersion, appLatestUrl); + } + m_updateNotificationWidget->move( + (width() - m_updateNotificationWidget->width()) / 2, 0); + makeChild(m_updateNotificationWidget); + m_updateNotificationWidget->show(); +} + void CaptureWidget::initSelection() { m_selection = new SelectionWidget(m_uiColor, this); diff --git a/src/widgets/capture/capturewidget.h b/src/widgets/capture/capturewidget.h index 808f8349..74112856 100644 --- a/src/widgets/capture/capturewidget.h +++ b/src/widgets/capture/capturewidget.h @@ -44,6 +44,7 @@ class QNetworkReply; class ColorPicker; class NotifierBox; class HoverEventFilter; +class UpdateNotificationWidget; class CaptureWidget : public QWidget { @@ -58,6 +59,8 @@ public: void updateButtons(); QPixmap pixmap(); + void showAppUpdateNotification(const QString& appLatestVersion, + const QString& appLatestUrl); public slots: void deleteToolwidgetOrClose(); @@ -145,6 +148,7 @@ private: QRect extendedRect(QRect* r) const; private: + UpdateNotificationWidget* m_updateNotificationWidget; quint64 m_lastMouseWheel; QUndoStack m_undoStack; QPointer m_sizeIndButton; diff --git a/src/widgets/updatenotificationwidget.cpp b/src/widgets/updatenotificationwidget.cpp new file mode 100644 index 00000000..5a3c5e92 --- /dev/null +++ b/src/widgets/updatenotificationwidget.cpp @@ -0,0 +1,141 @@ +// +// Created by yuriypuchkov on 09.12.2020. +// + +#include "updatenotificationwidget.h" +#include "src/utils/confighandler.h" +#include +#include +#include +#include +#include +#include +#include +#include + +UpdateNotificationWidget::UpdateNotificationWidget( + QWidget* parent, + const QString& appLatestVersion, + const QString& appLatestUrl) + : QWidget(parent) + , m_appLatestVersion(appLatestVersion) + , m_appLatestUrl(appLatestUrl) + , m_layout(nullptr) +{ + setMinimumSize(400, 100); + initInternalPanel(); + setAttribute(Qt::WA_TransparentForMouseEvents); + setCursor(Qt::ArrowCursor); + + m_showAnimation = new QPropertyAnimation(m_internalPanel, "geometry", this); + m_showAnimation->setEasingCurve(QEasingCurve::InOutQuad); + m_showAnimation->setDuration(300); + + m_hideAnimation = new QPropertyAnimation(m_internalPanel, "geometry", this); + m_hideAnimation->setEasingCurve(QEasingCurve::InOutQuad); + m_hideAnimation->setDuration(300); + + connect(m_hideAnimation, + &QPropertyAnimation::finished, + m_internalPanel, + &QWidget::hide); + setAppLatestVersion(appLatestVersion); +} + +void UpdateNotificationWidget::show() +{ + setAttribute(Qt::WA_TransparentForMouseEvents, false); + m_showAnimation->setStartValue(QRect(0, -height(), width(), height())); + m_showAnimation->setEndValue(QRect(0, 0, width(), height())); + m_internalPanel->show(); + m_showAnimation->start(); + QWidget::show(); +} + +void UpdateNotificationWidget::hide() +{ + setAttribute(Qt::WA_TransparentForMouseEvents); + m_hideAnimation->setStartValue(QRect(0, 0, width(), height())); + m_hideAnimation->setEndValue(QRect(0, -height(), 0, height())); + m_hideAnimation->start(); + m_internalPanel->hide(); + QWidget::hide(); +} + +void UpdateNotificationWidget::setAppLatestVersion(const QString& latestVersion) +{ + m_appLatestVersion = latestVersion; + QString newVersion = + tr("New Flameshot version %1 is available").arg(latestVersion); + m_notification->setText(newVersion); +} + +void UpdateNotificationWidget::laterButton() +{ + hide(); +} + +void UpdateNotificationWidget::ignoreButton() +{ + ConfigHandler().setIgnoreUpdateToVersion(m_appLatestVersion); + hide(); +} + +void UpdateNotificationWidget::updateButton() +{ + QDesktopServices::openUrl(m_appLatestUrl); + hide(); +} + +void UpdateNotificationWidget::initInternalPanel() +{ + m_internalPanel = new QScrollArea(this); + m_internalPanel->setAttribute(Qt::WA_NoMousePropagation); + QWidget* widget = new QWidget(); + m_internalPanel->setWidget(widget); + m_internalPanel->setWidgetResizable(true); + + QColor bgColor = palette().window().color(); + bgColor.setAlphaF(0.0); + m_internalPanel->setStyleSheet( + QStringLiteral("QScrollArea {background-color: %1}").arg(bgColor.name())); + m_internalPanel->hide(); + + // + m_layout = new QVBoxLayout(); + widget->setLayout(m_layout); + + // caption + m_notification = new QLabel(m_appLatestVersion, this); + m_layout->addWidget(m_notification); + + // buttons layout + QHBoxLayout* buttonsLayout = new QHBoxLayout(); + QSpacerItem* bottonsSpacer = new QSpacerItem(1, 1, QSizePolicy::Expanding); + buttonsLayout->addSpacerItem(bottonsSpacer); + m_layout->addLayout(buttonsLayout); + + // ignore + QPushButton* ignoreBtn = new QPushButton(tr("Ignore"), this); + buttonsLayout->addWidget(ignoreBtn); + connect(ignoreBtn, + &QPushButton::clicked, + this, + &UpdateNotificationWidget::ignoreButton); + + // later + QPushButton* laterBtn = new QPushButton(tr("Later"), this); + buttonsLayout->addWidget(laterBtn); + connect(laterBtn, + &QPushButton::clicked, + this, + &UpdateNotificationWidget::laterButton); + + // update + QPushButton* updateBtn = new QPushButton(tr("Update"), this); + buttonsLayout->addWidget(updateBtn); + connect(updateBtn, + &QPushButton::clicked, + this, + &UpdateNotificationWidget::updateButton); +} diff --git a/src/widgets/updatenotificationwidget.h b/src/widgets/updatenotificationwidget.h new file mode 100644 index 00000000..b214842c --- /dev/null +++ b/src/widgets/updatenotificationwidget.h @@ -0,0 +1,47 @@ +// +// Created by yuriypuchkov on 09.12.2020. +// + +#ifndef FLAMESHOT_UPDATENOTIFICATIONWIDGET_H +#define FLAMESHOT_UPDATENOTIFICATIONWIDGET_H + +#include +#include + +class QVBoxLayout; +class QPropertyAnimation; +class QScrollArea; +class QPushButton; +class QLabel; + +class UpdateNotificationWidget : public QWidget +{ + Q_OBJECT +public: + explicit UpdateNotificationWidget(QWidget* parent, + const QString& appLatestVersion, + const QString& appLatestUrl); + void setAppLatestVersion(const QString& latestVersion); + + void hide(); + void show(); + +public slots: + void ignoreButton(); + void laterButton(); + void updateButton(); + +private: + void initInternalPanel(); + + // class members + QString m_appLatestVersion; + QString m_appLatestUrl; + QVBoxLayout* m_layout; + QLabel* m_notification; + QScrollArea* m_internalPanel; + QPropertyAnimation* m_showAnimation; + QPropertyAnimation* m_hideAnimation; +}; + +#endif // FLAMESHOT_UPDATENOTIFICATIONWIDGET_H From 47c99774ceb93c0ec1a868708c9cdf0680528b6f Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Fri, 11 Dec 2020 16:34:26 +0200 Subject: [PATCH 43/54] Add timer for auto-checks for available updates (every 24 hours) --- data/translations/Internationalization_ca.ts | 83 +++++++++++++------ data/translations/Internationalization_cs.ts | 83 +++++++++++++------ .../Internationalization_de_DE.ts | 83 +++++++++++++------ data/translations/Internationalization_es.ts | 83 +++++++++++++------ data/translations/Internationalization_eu.ts | 83 +++++++++++++------ data/translations/Internationalization_fr.ts | 83 +++++++++++++------ data/translations/Internationalization_hu.ts | 35 +++++++- .../Internationalization_it_IT.ts | 83 +++++++++++++------ data/translations/Internationalization_ja.ts | 83 +++++++++++++------ data/translations/Internationalization_ka.ts | 83 +++++++++++++------ data/translations/Internationalization_nl.ts | 83 +++++++++++++------ .../Internationalization_nl_NL.ts | 83 +++++++++++++------ data/translations/Internationalization_pl.ts | 83 +++++++++++++------ .../Internationalization_pt_BR.ts | 83 +++++++++++++------ data/translations/Internationalization_ru.ts | 81 +++++++++++++----- data/translations/Internationalization_sk.ts | 83 +++++++++++++------ .../Internationalization_sr_SP.ts | 83 +++++++++++++------ .../Internationalization_sv_SE.ts | 83 +++++++++++++------ data/translations/Internationalization_tr.ts | 83 +++++++++++++------ data/translations/Internationalization_uk.ts | 81 +++++++++++++----- .../Internationalization_zh_CN.ts | 83 +++++++++++++------ .../Internationalization_zh_HK.ts | 83 +++++++++++++------ .../Internationalization_zh_TW.ts | 83 +++++++++++++------ src/core/controller.cpp | 19 +++-- 24 files changed, 1323 insertions(+), 553 deletions(-) diff --git a/data/translations/Internationalization_ca.ts b/data/translations/Internationalization_ca.ts index c80f74a4..b24d6573 100644 --- a/data/translations/Internationalization_ca.ts +++ b/data/translations/Internationalization_ca.ts @@ -123,13 +123,13 @@ CaptureWidget - + Unable to capture screen Impossible capturar la pantalla Imposible capturar la pantalla - + Select an area with the mouse, or press Esc to exit. Press Enter to capture the screen. Press Right Click to show the color picker. @@ -138,7 +138,7 @@ Press Space to open the side panel. - + Tool Settings @@ -205,22 +205,37 @@ Press Space to open the side panel. Controller - + + New version %1 is available + + + + + You have the latest version + + + + &Open Launcher - + &Configuration &Configuració - + &About - + + Check for updates + + + + &Latest Uploads @@ -229,12 +244,12 @@ Press Space to open the side panel. &Informació - + &Quit &Ix - + &Take Screenshot @@ -864,12 +879,12 @@ Press Space to open the side panel. PixelateTool - + Pixelate - + Set Pixelate as the paint tool @@ -1105,7 +1120,7 @@ You can find me in the system tray. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. @@ -1160,22 +1175,22 @@ You can find me in the system tray. Ix de la captura - + Screenshot history - + Capture screen - + Show color picker Mostra el selector de color - + Change the tool's thickness Canvia el gruix de l'eina @@ -1209,12 +1224,12 @@ You can find me in the system tray. SaveTool - + Save Guarda - + Save the capture Guarda la captura @@ -1222,7 +1237,7 @@ You can find me in the system tray. ScreenGrabber - + Unable to capture screen Imposible capturar la pantalla @@ -1520,6 +1535,29 @@ You can find me in the system tray. Desfés l'última modificació + + UpdateNotificationWidget + + + New Flameshot version %1 is available + + + + + Ignore + + + + + Later + + + + + Update + + + UploadStorageConfig @@ -1541,15 +1579,10 @@ You can find me in the system tray. UtilityPanel - + Close - - - Hide - - VisualsEditor diff --git a/data/translations/Internationalization_cs.ts b/data/translations/Internationalization_cs.ts index 8beb94f2..71093008 100644 --- a/data/translations/Internationalization_cs.ts +++ b/data/translations/Internationalization_cs.ts @@ -123,12 +123,12 @@ CaptureWidget - + Unable to capture screen Nelze zachytit obrazovku - + Select an area with the mouse, or press Esc to exit. Press Enter to capture the screen. Press Right Click to show the color picker. @@ -141,7 +141,7 @@ Použijte kolečko myši pro změnu tloušťky nástroje. Stiskněte mezerník pro otevření postranního panelu. - + Tool Settings Nastavení nástrojů @@ -208,27 +208,42 @@ Stiskněte mezerník pro otevření postranního panelu. Controller - + + New version %1 is available + + + + + You have the latest version + + + + &Take Screenshot &Zachytit obrazovku - + &Open Launcher &Otevřít spouštěč - + &Configuration &Nastavení - + &About O &programu - + + Check for updates + + + + &Latest Uploads @@ -237,7 +252,7 @@ Stiskněte mezerník pro otevření postranního panelu. &Informace - + &Quit &Ukončit @@ -883,12 +898,12 @@ Stiskněte mezerník pro otevření postranního panelu. PixelateTool - + Pixelate Rozčtverečkování - + Set Pixelate as the paint tool Nastaviť rozčtverečkování jako nástroj pro úpravy @@ -1134,7 +1149,7 @@ You can find me in the system tray. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. @@ -1189,22 +1204,22 @@ You can find me in the system tray. Ukončit zachytávání obrazovky - + Screenshot history - + Capture screen - + Show color picker Ukázat volič barev - + Change the tool's thickness Změnit tloušťku nástroje @@ -1238,12 +1253,12 @@ You can find me in the system tray. SaveTool - + Save Uložit - + Save the capture Uložit zachycenou obrazovku @@ -1251,7 +1266,7 @@ You can find me in the system tray. ScreenGrabber - + Unable to capture screen Nelze zachytit obrazovku @@ -1549,6 +1564,29 @@ You can find me in the system tray. Zrušit poslední změnu + + UpdateNotificationWidget + + + New Flameshot version %1 is available + + + + + Ignore + + + + + Later + + + + + Update + + + UploadStorageConfig @@ -1570,15 +1608,10 @@ You can find me in the system tray. UtilityPanel - + Close Zavřít - - - Hide - - VisualsEditor diff --git a/data/translations/Internationalization_de_DE.ts b/data/translations/Internationalization_de_DE.ts index 90fa5432..0eeef2f8 100644 --- a/data/translations/Internationalization_de_DE.ts +++ b/data/translations/Internationalization_de_DE.ts @@ -123,12 +123,12 @@ CaptureWidget - + Unable to capture screen Bereich kann nicht erfasst werden - + Select an area with the mouse, or press Esc to exit. Press Enter to capture the screen. Press Right Click to show the color picker. @@ -141,7 +141,7 @@ Benutze das Mausrad um die Dicke des Werkzeugs auszuwählen. Drücke die Leertaste um das Seitenmenü zu öffnen. - + Tool Settings @@ -208,27 +208,42 @@ Drücke die Leertaste um das Seitenmenü zu öffnen. Controller - + + New version %1 is available + + + + + You have the latest version + + + + &Take Screenshot &Bildschirmaufnahme anfertigen - + &Open Launcher - + &Configuration &Einstellungen - + &About - + + Check for updates + + + + &Latest Uploads @@ -237,7 +252,7 @@ Drücke die Leertaste um das Seitenmenü zu öffnen. &Informationen - + &Quit &Beenden @@ -879,12 +894,12 @@ Drücke die Leertaste um das Seitenmenü zu öffnen. PixelateTool - + Pixelate - + Set Pixelate as the paint tool @@ -1120,7 +1135,7 @@ You can find me in the system tray. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. @@ -1175,22 +1190,22 @@ You can find me in the system tray. Auswahl verlassen - + Screenshot history - + Capture screen - + Show color picker Zeige Farbauswahl - + Change the tool's thickness Ändere die Dicke des Werkzeugs @@ -1224,12 +1239,12 @@ You can find me in the system tray. SaveTool - + Save Speichern - + Save the capture Speichere die Aufnahme @@ -1237,7 +1252,7 @@ You can find me in the system tray. ScreenGrabber - + Unable to capture screen Kann Bereich nicht aufnehmen @@ -1535,6 +1550,29 @@ You can find me in the system tray. Letzte Änderung verwerfen + + UpdateNotificationWidget + + + New Flameshot version %1 is available + + + + + Ignore + + + + + Later + + + + + Update + + + UploadStorageConfig @@ -1556,15 +1594,10 @@ You can find me in the system tray. UtilityPanel - + Close - - - Hide - - VisualsEditor diff --git a/data/translations/Internationalization_es.ts b/data/translations/Internationalization_es.ts index 0c8707bf..4dbc3b27 100644 --- a/data/translations/Internationalization_es.ts +++ b/data/translations/Internationalization_es.ts @@ -123,12 +123,12 @@ CaptureWidget - + Unable to capture screen Imposible capturar la pantalla - + Select an area with the mouse, or press Esc to exit. Press Enter to capture the screen. Press Right Click to show the color picker. @@ -141,7 +141,7 @@ Usa la rueda del ratón para cambiar el grosor de la herramienta. Presiona Espacio para abrir el panel lateral. - + Tool Settings @@ -208,27 +208,42 @@ Presiona Espacio para abrir el panel lateral. Controller - + + New version %1 is available + + + + + You have the latest version + + + + &Take Screenshot &Tomar captura de pantalla - + &Open Launcher - + &Configuration &Configuración - + &About - + + Check for updates + + + + &Latest Uploads @@ -237,7 +252,7 @@ Presiona Espacio para abrir el panel lateral. &Información - + &Quit &Salir @@ -875,12 +890,12 @@ Presiona Espacio para abrir el panel lateral. PixelateTool - + Pixelate - + Set Pixelate as the paint tool @@ -1116,7 +1131,7 @@ You can find me in the system tray. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. @@ -1171,22 +1186,22 @@ You can find me in the system tray. Salir de la captura - + Screenshot history - + Capture screen - + Show color picker Mostrar el selector de color - + Change the tool's thickness Cambiar el grosor de la herramienta @@ -1220,12 +1235,12 @@ You can find me in the system tray. SaveTool - + Save Guardar - + Save the capture Guardar la captura @@ -1233,7 +1248,7 @@ You can find me in the system tray. ScreenGrabber - + Unable to capture screen Imposible capturar la pantalla @@ -1531,6 +1546,29 @@ You can find me in the system tray. Borra la última modificación + + UpdateNotificationWidget + + + New Flameshot version %1 is available + + + + + Ignore + + + + + Later + + + + + Update + + + UploadStorageConfig @@ -1552,15 +1590,10 @@ You can find me in the system tray. UtilityPanel - + Close - - - Hide - - VisualsEditor diff --git a/data/translations/Internationalization_eu.ts b/data/translations/Internationalization_eu.ts index 3b31c526..17f359d5 100644 --- a/data/translations/Internationalization_eu.ts +++ b/data/translations/Internationalization_eu.ts @@ -123,12 +123,12 @@ CaptureWidget - + Unable to capture screen Ezin da pantailaren argazkia egin - + Select an area with the mouse, or press Esc to exit. Press Enter to capture the screen. Press Right Click to show the color picker. @@ -141,7 +141,7 @@ Erabili saguaren gurpila hautatutako tresnaren lodiera aldatzeko. Sakatu Zuriunea alboko panela irekitzeko. - + Tool Settings Tresna-aukerak @@ -208,27 +208,42 @@ Sakatu Zuriunea alboko panela irekitzeko. Controller - + + New version %1 is available + + + + + You have the latest version + + + + &Take Screenshot &Pantaila-argazkia egin - + &Open Launcher &Abiarazlea ireki - + &Configuration &Ezarpenak - + &About &Honi buruz - + + Check for updates + + + + &Latest Uploads @@ -237,7 +252,7 @@ Sakatu Zuriunea alboko panela irekitzeko. &Informazioa - + &Quit &Irten @@ -883,12 +898,12 @@ Sakatu Zuriunea alboko panela irekitzeko. PixelateTool - + Pixelate Pixelatu - + Set Pixelate as the paint tool Ezarri Pixelatu margotzeko tresna gisa @@ -1134,7 +1149,7 @@ You can find me in the system tray. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. @@ -1189,22 +1204,22 @@ You can find me in the system tray. Irten argazki-hartzetik - + Screenshot history - + Capture screen - + Show color picker Erakutsi kolore hautagailua - + Change the tool's thickness Aldatu tresnaren lodiera @@ -1238,12 +1253,12 @@ You can find me in the system tray. SaveTool - + Save Gorde - + Save the capture Gorde argazkia @@ -1251,7 +1266,7 @@ You can find me in the system tray. ScreenGrabber - + Unable to capture screen Ezin da pantailaren argazkia egin @@ -1549,6 +1564,29 @@ You can find me in the system tray. Desegin azken aldaketa + + UpdateNotificationWidget + + + New Flameshot version %1 is available + + + + + Ignore + + + + + Later + + + + + Update + + + UploadStorageConfig @@ -1570,15 +1608,10 @@ You can find me in the system tray. UtilityPanel - + Close Itxi - - - Hide - - VisualsEditor diff --git a/data/translations/Internationalization_fr.ts b/data/translations/Internationalization_fr.ts index 1c7b28ae..dc4fd957 100644 --- a/data/translations/Internationalization_fr.ts +++ b/data/translations/Internationalization_fr.ts @@ -123,12 +123,12 @@ CaptureWidget - + Unable to capture screen Impossible de capturer l'écran - + Select an area with the mouse, or press Esc to exit. Press Enter to capture the screen. Press Right Click to show the color picker. @@ -141,7 +141,7 @@ Utiliser la molette de la souris pour changer l'épaisseur de l'outil. Appuyer sur Espace pour ouvrir le panneau latéral. - + Tool Settings @@ -208,27 +208,42 @@ Appuyer sur Espace pour ouvrir le panneau latéral. Controller - + + New version %1 is available + + + + + You have the latest version + + + + &Take Screenshot &Capturer l'écran - + &Open Launcher - + &Configuration &Configuration - + &About - + + Check for updates + + + + &Latest Uploads @@ -237,7 +252,7 @@ Appuyer sur Espace pour ouvrir le panneau latéral. &Informations - + &Quit &Quitter @@ -871,12 +886,12 @@ Appuyer sur Espace pour ouvrir le panneau latéral. PixelateTool - + Pixelate - + Set Pixelate as the paint tool @@ -1112,7 +1127,7 @@ You can find me in the system tray. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. @@ -1167,22 +1182,22 @@ You can find me in the system tray. Quitter la capture d'écran - + Screenshot history - + Capture screen - + Show color picker Afficher la palette de couleurs - + Change the tool's thickness Changer l'épaisseur des outils @@ -1216,12 +1231,12 @@ You can find me in the system tray. SaveTool - + Save Sauvegarder - + Save the capture Sauvegarder la capture d'écran @@ -1229,7 +1244,7 @@ You can find me in the system tray. ScreenGrabber - + Unable to capture screen Impossible de capturer l'écran @@ -1527,6 +1542,29 @@ You can find me in the system tray. Annuler la dernière modification + + UpdateNotificationWidget + + + New Flameshot version %1 is available + + + + + Ignore + + + + + Later + + + + + Update + + + UploadStorageConfig @@ -1548,15 +1586,10 @@ You can find me in the system tray. UtilityPanel - + Close - - - Hide - - VisualsEditor diff --git a/data/translations/Internationalization_hu.ts b/data/translations/Internationalization_hu.ts index 143b4710..bfb6fb28 100644 --- a/data/translations/Internationalization_hu.ts +++ b/data/translations/Internationalization_hu.ts @@ -208,6 +208,18 @@ Press Space to open the side panel. &Latest Uploads + + New version %1 is available + + + + You have the latest version + + + + Check for updates + + CopyTool @@ -1288,6 +1300,25 @@ You can find me in the system tray. Visszavonja az utolsó módosítást + + UpdateNotificationWidget + + New Flameshot version %1 is available + + + + Ignore + + + + Later + + + + Update + + + UploadStorageConfig @@ -1309,10 +1340,6 @@ You can find me in the system tray. Close - - Hide - - VisualsEditor diff --git a/data/translations/Internationalization_it_IT.ts b/data/translations/Internationalization_it_IT.ts index aed180a0..6c98838f 100644 --- a/data/translations/Internationalization_it_IT.ts +++ b/data/translations/Internationalization_it_IT.ts @@ -112,12 +112,12 @@ CaptureWidget - + Unable to capture screen - + Select an area with the mouse, or press Esc to exit. Press Enter to capture the screen. Press Right Click to show the color picker. @@ -126,7 +126,7 @@ Press Space to open the side panel. - + Tool Settings @@ -193,32 +193,47 @@ Press Space to open the side panel. Controller - + + New version %1 is available + + + + + You have the latest version + + + + &Take Screenshot - + &Open Launcher - + &Configuration - + &About - + + Check for updates + + + + &Quit - + &Latest Uploads @@ -760,12 +775,12 @@ Press Space to open the side panel. PixelateTool - + Pixelate - + Set Pixelate as the paint tool @@ -1001,7 +1016,7 @@ You can find me in the system tray. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. @@ -1056,22 +1071,22 @@ You can find me in the system tray. - + Screenshot history - + Capture screen - + Show color picker - + Change the tool's thickness @@ -1105,12 +1120,12 @@ You can find me in the system tray. SaveTool - + Save - + Save the capture @@ -1118,7 +1133,7 @@ You can find me in the system tray. ScreenGrabber - + Unable to capture screen @@ -1416,6 +1431,29 @@ You can find me in the system tray. + + UpdateNotificationWidget + + + New Flameshot version %1 is available + + + + + Ignore + + + + + Later + + + + + Update + + + UploadStorageConfig @@ -1437,15 +1475,10 @@ You can find me in the system tray. UtilityPanel - + Close - - - Hide - - VisualsEditor diff --git a/data/translations/Internationalization_ja.ts b/data/translations/Internationalization_ja.ts index 2b00b719..b2396e0e 100644 --- a/data/translations/Internationalization_ja.ts +++ b/data/translations/Internationalization_ja.ts @@ -123,12 +123,12 @@ CaptureWidget - + Unable to capture screen 画面をキャプチャーできません - + Select an area with the mouse, or press Esc to exit. Press Enter to capture the screen. Press Right Click to show the color picker. @@ -141,7 +141,7 @@ Enter を押すと画面をキャプチャー。 スペースを押すとサイドパネルを開く。 - + Tool Settings @@ -208,27 +208,42 @@ Enter を押すと画面をキャプチャー。 Controller - + + New version %1 is available + + + + + You have the latest version + + + + &Take Screenshot スクリーンショットを撮る(&T) - + &Open Launcher - + &Configuration 設定(&C) - + &About - + + Check for updates + + + + &Latest Uploads @@ -237,7 +252,7 @@ Enter を押すと画面をキャプチャー。 情報(&I) - + &Quit 終了(&Q) @@ -871,12 +886,12 @@ Enter を押すと画面をキャプチャー。 PixelateTool - + Pixelate - + Set Pixelate as the paint tool @@ -1112,7 +1127,7 @@ You can find me in the system tray. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. @@ -1167,22 +1182,22 @@ You can find me in the system tray. キャプチャーを終了する - + Screenshot history - + Capture screen - + Show color picker カラーピッカーを表示する - + Change the tool's thickness ツールの値 (太さや濃さ) を変更する @@ -1216,12 +1231,12 @@ You can find me in the system tray. SaveTool - + Save 保存 - + Save the capture キャプチャーを保存する @@ -1229,7 +1244,7 @@ You can find me in the system tray. ScreenGrabber - + Unable to capture screen 画面をキャプチャーできません @@ -1527,6 +1542,29 @@ You can find me in the system tray. 最後の変更を元に戻す + + UpdateNotificationWidget + + + New Flameshot version %1 is available + + + + + Ignore + + + + + Later + + + + + Update + + + UploadStorageConfig @@ -1548,15 +1586,10 @@ You can find me in the system tray. UtilityPanel - + Close - - - Hide - - VisualsEditor diff --git a/data/translations/Internationalization_ka.ts b/data/translations/Internationalization_ka.ts index 860849c1..75c54ced 100644 --- a/data/translations/Internationalization_ka.ts +++ b/data/translations/Internationalization_ka.ts @@ -123,12 +123,12 @@ CaptureWidget - + Unable to capture screen ეკრანის გადაღება ვერ მოხერხდა - + Select an area with the mouse, or press Esc to exit. Press Enter to capture the screen. Press Right Click to show the color picker. @@ -137,7 +137,7 @@ Press Space to open the side panel. - + Tool Settings @@ -204,27 +204,42 @@ Press Space to open the side panel. Controller - + + New version %1 is available + + + + + You have the latest version + + + + &Take Screenshot - + &Open Launcher - + &Configuration &პარამეტრები - + &About - + + Check for updates + + + + &Latest Uploads @@ -233,7 +248,7 @@ Press Space to open the side panel. &ინფორმაცია - + &Quit &გამოსვლა @@ -863,12 +878,12 @@ Press Space to open the side panel. PixelateTool - + Pixelate - + Set Pixelate as the paint tool @@ -1104,7 +1119,7 @@ You can find me in the system tray. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. @@ -1159,22 +1174,22 @@ You can find me in the system tray. გადაღებიდან გამოსვლა - + Screenshot history - + Capture screen - + Show color picker ფერის შესარჩევის ჩვენება - + Change the tool's thickness ხელსაწყოს სისქის შეცვლა @@ -1208,12 +1223,12 @@ You can find me in the system tray. SaveTool - + Save შენახვა - + Save the capture სურათის შენახვა @@ -1221,7 +1236,7 @@ You can find me in the system tray. ScreenGrabber - + Unable to capture screen ეკრანის გადაღება ვერ მოხერხდა @@ -1519,6 +1534,29 @@ You can find me in the system tray. ბოლო ცვლილების გაუქმება + + UpdateNotificationWidget + + + New Flameshot version %1 is available + + + + + Ignore + + + + + Later + + + + + Update + + + UploadStorageConfig @@ -1540,15 +1578,10 @@ You can find me in the system tray. UtilityPanel - + Close - - - Hide - - VisualsEditor diff --git a/data/translations/Internationalization_nl.ts b/data/translations/Internationalization_nl.ts index 0f768148..e3b249a6 100644 --- a/data/translations/Internationalization_nl.ts +++ b/data/translations/Internationalization_nl.ts @@ -123,12 +123,12 @@ CaptureWidget - + Unable to capture screen Kan scherm niet vastleggen - + Select an area with the mouse, or press Esc to exit. Press Enter to capture the screen. Press Right Click to show the color picker. @@ -141,7 +141,7 @@ Gebruik het muiswiel om de gereedschapsdikte aan te passen. Druk op spatie om het zijpaneel te openen. - + Tool Settings @@ -208,27 +208,42 @@ Druk op spatie om het zijpaneel te openen. Controller - + + New version %1 is available + + + + + You have the latest version + + + + &Take Screenshot Schermafdruk &maken - + &Open Launcher - + &Configuration &Configuratie - + &About - + + Check for updates + + + + &Latest Uploads @@ -237,7 +252,7 @@ Druk op spatie om het zijpaneel te openen. &Informatie - + &Quit &Afsluiten @@ -875,12 +890,12 @@ Druk op spatie om het zijpaneel te openen. PixelateTool - + Pixelate - + Set Pixelate as the paint tool @@ -1116,7 +1131,7 @@ You can find me in the system tray. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. @@ -1171,22 +1186,22 @@ You can find me in the system tray. Vastleggen afsluiten - + Screenshot history - + Capture screen - + Show color picker Kleurkiezer tonen - + Change the tool's thickness Wijzig de gereedschapsdikte @@ -1220,13 +1235,13 @@ You can find me in the system tray. SaveTool - + Save Сачувај Opslaan - + Save the capture Schermafdruk opslaan @@ -1234,7 +1249,7 @@ You can find me in the system tray. ScreenGrabber - + Unable to capture screen Kan scherm niet vastleggen @@ -1532,6 +1547,29 @@ You can find me in the system tray. Laatste wijziging ongedaan maken + + UpdateNotificationWidget + + + New Flameshot version %1 is available + + + + + Ignore + + + + + Later + + + + + Update + + + UploadStorageConfig @@ -1553,15 +1591,10 @@ You can find me in the system tray. UtilityPanel - + Close - - - Hide - - VisualsEditor diff --git a/data/translations/Internationalization_nl_NL.ts b/data/translations/Internationalization_nl_NL.ts index 9cd42570..fae80955 100644 --- a/data/translations/Internationalization_nl_NL.ts +++ b/data/translations/Internationalization_nl_NL.ts @@ -123,12 +123,12 @@ CaptureWidget - + Unable to capture screen Kan scherm niet vastleggen - + Select an area with the mouse, or press Esc to exit. Press Enter to capture the screen. Press Right Click to show the color picker. @@ -141,7 +141,7 @@ Gebruik het muiswiel om de dikte van uw gereedschap te wijzigen. Druk op de spatiebalk om het zijpaneel te openen. - + Tool Settings @@ -208,27 +208,42 @@ Druk op de spatiebalk om het zijpaneel te openen. Controller - + + New version %1 is available + + + + + You have the latest version + + + + &Take Screenshot &Maak een Schermopname - + &Open Launcher &Open Starter - + &Configuration &Configuratie - + &About - + + Check for updates + + + + &Latest Uploads @@ -237,7 +252,7 @@ Druk op de spatiebalk om het zijpaneel te openen. &Informatie - + &Quit &Sluiten @@ -883,12 +898,12 @@ Druk op de spatiebalk om het zijpaneel te openen. PixelateTool - + Pixelate Verkorrelen - + Set Pixelate as the paint tool Stel verkorrelen in als het tekengereedschap @@ -1124,7 +1139,7 @@ You can find me in the system tray. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. @@ -1179,22 +1194,22 @@ You can find me in the system tray. Stop met vastleggen - + Screenshot history - + Capture screen - + Show color picker Toon kleurkiezer - + Change the tool's thickness Wijzig de gereedschapsdikte @@ -1228,12 +1243,12 @@ You can find me in the system tray. SaveTool - + Save Opslaan - + Save the capture Schermafdruk opslaan @@ -1241,7 +1256,7 @@ You can find me in the system tray. ScreenGrabber - + Unable to capture screen Kan scherm niet vastleggen @@ -1539,6 +1554,29 @@ You can find me in the system tray. Laatste wijziging ongedaan maken + + UpdateNotificationWidget + + + New Flameshot version %1 is available + + + + + Ignore + + + + + Later + + + + + Update + + + UploadStorageConfig @@ -1560,15 +1598,10 @@ You can find me in the system tray. UtilityPanel - + Close - - - Hide - - VisualsEditor diff --git a/data/translations/Internationalization_pl.ts b/data/translations/Internationalization_pl.ts index 16acdea3..50d68676 100644 --- a/data/translations/Internationalization_pl.ts +++ b/data/translations/Internationalization_pl.ts @@ -123,12 +123,12 @@ CaptureWidget - + Unable to capture screen Nie można przechwycić ekranu - + Select an area with the mouse, or press Esc to exit. Press Enter to capture the screen. Press Right Click to show the color picker. @@ -140,7 +140,7 @@ Prawy klik, aby pokazać próbnik kolorów. Spacja, aby pokazać panel boczny. - + Tool Settings @@ -207,27 +207,42 @@ Spacja, aby pokazać panel boczny. Controller - + + New version %1 is available + + + + + You have the latest version + + + + &Take Screenshot &Zrzut ekranu - + &Open Launcher Pokaż &okno - + &Configuration &Konfiguracja - + &About O progr&amie - + + Check for updates + + + + &Latest Uploads @@ -236,7 +251,7 @@ Spacja, aby pokazać panel boczny. &Informacje - + &Quit &Wyjdź @@ -882,12 +897,12 @@ Spacja, aby pokazać panel boczny. PixelateTool - + Pixelate Zamazywanie - + Set Pixelate as the paint tool Ustaw Zamazywanie jako narzędzie malowania @@ -1127,7 +1142,7 @@ You can find me in the system tray. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. @@ -1182,22 +1197,22 @@ You can find me in the system tray. Zakończ przechwytywanie - + Screenshot history - + Capture screen - + Show color picker Pokaż próbnik kolorów - + Change the tool's thickness Zmień grubość narzędzia @@ -1231,12 +1246,12 @@ You can find me in the system tray. SaveTool - + Save Zapisz - + Save the capture Zapisz zaznaczenie @@ -1244,7 +1259,7 @@ You can find me in the system tray. ScreenGrabber - + Unable to capture screen Nie można przechwycić ekranu @@ -1542,6 +1557,29 @@ You can find me in the system tray. Cofnij ostatnią zmianę + + UpdateNotificationWidget + + + New Flameshot version %1 is available + + + + + Ignore + + + + + Later + + + + + Update + + + UploadStorageConfig @@ -1563,15 +1601,10 @@ You can find me in the system tray. UtilityPanel - + Close - - - Hide - - VisualsEditor diff --git a/data/translations/Internationalization_pt_BR.ts b/data/translations/Internationalization_pt_BR.ts index cc8eadee..f9c876d0 100644 --- a/data/translations/Internationalization_pt_BR.ts +++ b/data/translations/Internationalization_pt_BR.ts @@ -123,12 +123,12 @@ CaptureWidget - + Unable to capture screen Não foi possível capturar a tela - + Select an area with the mouse, or press Esc to exit. Press Enter to capture the screen. Press Right Click to show the color picker. @@ -141,7 +141,7 @@ Use a roda do mouse para aumentar a grossura do pincel. Pressione espaço abrir o painel lateral. - + Tool Settings Configurações da ferramenta @@ -208,27 +208,42 @@ Pressione espaço abrir o painel lateral. Controller - + + New version %1 is available + + + + + You have the latest version + + + + &Take Screenshot &Tirar Screenshot - + &Open Launcher &Abrir carregador - + &Configuration &Configuração - + &About &Sobre - + + Check for updates + + + + &Latest Uploads @@ -237,7 +252,7 @@ Pressione espaço abrir o painel lateral. &Informações - + &Quit &Sair @@ -883,12 +898,12 @@ Pressione espaço abrir o painel lateral. PixelateTool - + Pixelate Pixelar - + Set Pixelate as the paint tool Usar Pixelar na ferramenta de pintura @@ -1134,7 +1149,7 @@ You can find me in the system tray. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. @@ -1189,22 +1204,22 @@ You can find me in the system tray. Sair da captura - + Screenshot history - + Capture screen - + Show color picker Mostrar seletor de cores - + Change the tool's thickness Mudar a grossura do pincel @@ -1238,12 +1253,12 @@ You can find me in the system tray. SaveTool - + Save Salvar - + Save the capture Salvar a captura @@ -1251,7 +1266,7 @@ You can find me in the system tray. ScreenGrabber - + Unable to capture screen Não foi possível capturar a tela @@ -1549,6 +1564,29 @@ You can find me in the system tray. Desfazer a última modificação + + UpdateNotificationWidget + + + New Flameshot version %1 is available + + + + + Ignore + + + + + Later + + + + + Update + + + UploadStorageConfig @@ -1570,15 +1608,10 @@ You can find me in the system tray. UtilityPanel - + Close Fechar - - - Hide - - VisualsEditor diff --git a/data/translations/Internationalization_ru.ts b/data/translations/Internationalization_ru.ts index 6d5c4705..bad227b7 100644 --- a/data/translations/Internationalization_ru.ts +++ b/data/translations/Internationalization_ru.ts @@ -123,12 +123,12 @@ CaptureWidget - + Unable to capture screen Не удалось захватить экран - + Select an area with the mouse, or press Esc to exit. Press Enter to capture the screen. Press Right Click to show the color picker. @@ -141,7 +141,7 @@ Press Space to open the side panel. Нажмите Пробел, чтобы открыть боковую панель. - + Tool Settings Настройки инструмента @@ -208,27 +208,42 @@ Press Space to open the side panel. Controller - + + New version %1 is available + Доступна новая версия %1 + + + + You have the latest version + У Вас самая последня версия + + + &Take Screenshot &Сделать снимок - + &Open Launcher &Открыть лаунчер - + &Configuration &Настройка - + &About &Информация - + + Check for updates + Проверить обновления + + + &Latest Uploads Последние загрузки @@ -237,7 +252,7 @@ Press Space to open the side panel. &Информация - + &Quit &Выход @@ -895,12 +910,12 @@ Press Space to open the side panel. PixelateTool - + Pixelate Размытие - + Set Pixelate as the paint tool Выбрать Pixelate инструментом для рисования @@ -1143,7 +1158,7 @@ You can find me in the system tray. Вы можете найти меня в системном трее. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. Привет я тут! Щелкните значок на панели задач, чтобы сделать снимок экрана, или щелкните правой кнопкой, чтобы увидеть дополнительные параметры. @@ -1198,22 +1213,22 @@ You can find me in the system tray. Выйти из захвата экрана - + Screenshot history История скриншотов - + Capture screen Захватить экран - + Show color picker Показать выбор цвета - + Change the tool's thickness Изменить толщину инструмента @@ -1247,12 +1262,12 @@ You can find me in the system tray. SaveTool - + Save Сохранить - + Save the capture Сохранить снимок @@ -1260,7 +1275,7 @@ You can find me in the system tray. ScreenGrabber - + Unable to capture screen Не удалось захватить экран @@ -1558,6 +1573,29 @@ You can find me in the system tray. Отменить последнее изменение + + UpdateNotificationWidget + + + New Flameshot version %1 is available + Доступна новая версия Flameshot %1 + + + + Ignore + Игнорировать + + + + Later + Позже + + + + Update + Обновить + + UploadStorageConfig @@ -1579,14 +1617,13 @@ You can find me in the system tray. UtilityPanel - + Close Закрыть - Hide - Спрятать + Спрятать diff --git a/data/translations/Internationalization_sk.ts b/data/translations/Internationalization_sk.ts index ba5bde02..5ad97823 100644 --- a/data/translations/Internationalization_sk.ts +++ b/data/translations/Internationalization_sk.ts @@ -123,12 +123,12 @@ CaptureWidget - + Unable to capture screen Nepodarilo sa zachytiť obrazovku - + Select an area with the mouse, or press Esc to exit. Press Enter to capture the screen. Press Right Click to show the color picker. @@ -141,7 +141,7 @@ Použite kolečko myši pre zmenu hrúbky vybraného nástroja. Stlačte medzerník pre otvorenie postranného panelu. - + Tool Settings Nastavenia nástrojov @@ -208,27 +208,42 @@ Stlačte medzerník pre otvorenie postranného panelu. Controller - + + New version %1 is available + + + + + You have the latest version + + + + &Take Screenshot &Vytvoriť snímku - + &Open Launcher - + &Configuration &Konfigurácia - + &About O &programe - + + Check for updates + + + + &Latest Uploads @@ -237,7 +252,7 @@ Stlačte medzerník pre otvorenie postranného panelu. &Informácie - + &Quit &Ukončiť @@ -883,12 +898,12 @@ Stlačte medzerník pre otvorenie postranného panelu. PixelateTool - + Pixelate Rozštvorčekovanie - + Set Pixelate as the paint tool Nastaviť Rozštvorčekovanie ako nástroj pre úpravy @@ -1134,7 +1149,7 @@ You can find me in the system tray. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. @@ -1189,22 +1204,22 @@ You can find me in the system tray. Ukončiť zachytávanie obrazovky - + Screenshot history - + Capture screen - + Show color picker Zobraziť dialóg na výber farby - + Change the tool's thickness Zmena hrúbky nástroja @@ -1238,12 +1253,12 @@ You can find me in the system tray. SaveTool - + Save Uložiť - + Save the capture Uložiť snímku obrazovky @@ -1251,7 +1266,7 @@ You can find me in the system tray. ScreenGrabber - + Unable to capture screen Nepodarilo sa zachytiť obrazovku @@ -1549,6 +1564,29 @@ You can find me in the system tray. Vrátiť poslednú úpravu + + UpdateNotificationWidget + + + New Flameshot version %1 is available + + + + + Ignore + + + + + Later + + + + + Update + + + UploadStorageConfig @@ -1570,15 +1608,10 @@ You can find me in the system tray. UtilityPanel - + Close Zavrieť - - - Hide - - VisualsEditor diff --git a/data/translations/Internationalization_sr_SP.ts b/data/translations/Internationalization_sr_SP.ts index 7ffa1ae5..c06a914e 100644 --- a/data/translations/Internationalization_sr_SP.ts +++ b/data/translations/Internationalization_sr_SP.ts @@ -123,12 +123,12 @@ CaptureWidget - + Unable to capture screen Нисам успео да снимим екран - + Select an area with the mouse, or press Esc to exit. Press Enter to capture the screen. Press Right Click to show the color picker. @@ -141,7 +141,7 @@ Press Space to open the side panel. Притисните размак на тастатури за приказ помоћног панела. - + Tool Settings @@ -208,27 +208,42 @@ Press Space to open the side panel. Controller - + + New version %1 is available + + + + + You have the latest version + + + + &Take Screenshot &Направи снимак екрана - + &Open Launcher - + &Configuration &Подешавања - + &About - + + Check for updates + + + + &Latest Uploads @@ -237,7 +252,7 @@ Press Space to open the side panel. Ин&формације - + &Quit &Излаз @@ -871,12 +886,12 @@ Press Space to open the side panel. PixelateTool - + Pixelate - + Set Pixelate as the paint tool @@ -1112,7 +1127,7 @@ You can find me in the system tray. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. @@ -1167,22 +1182,22 @@ You can find me in the system tray. Излаз из снимача екрана - + Screenshot history - + Capture screen - + Show color picker Прикажи избор боје - + Change the tool's thickness Измени дебљину линије алата @@ -1216,13 +1231,13 @@ You can find me in the system tray. SaveTool - + Save Сачувај Сохранить - + Save the capture Сачувај снимак @@ -1230,7 +1245,7 @@ You can find me in the system tray. ScreenGrabber - + Unable to capture screen Нисам успео да снимим екран @@ -1528,6 +1543,29 @@ You can find me in the system tray. Поништи последњу измену + + UpdateNotificationWidget + + + New Flameshot version %1 is available + + + + + Ignore + + + + + Later + + + + + Update + + + UploadStorageConfig @@ -1549,15 +1587,10 @@ You can find me in the system tray. UtilityPanel - + Close - - - Hide - - VisualsEditor diff --git a/data/translations/Internationalization_sv_SE.ts b/data/translations/Internationalization_sv_SE.ts index 8bb08d18..de959dbe 100644 --- a/data/translations/Internationalization_sv_SE.ts +++ b/data/translations/Internationalization_sv_SE.ts @@ -123,12 +123,12 @@ CaptureWidget - + Unable to capture screen Kunde inte avbilda skärmen - + Select an area with the mouse, or press Esc to exit. Press Enter to capture the screen. Press Right Click to show the color picker. @@ -141,7 +141,7 @@ Använd Scrollhjulet för att ändra tjockleken på ditt verktyg. Tryck Space för att öppna sidopanelen. - + Tool Settings @@ -208,27 +208,42 @@ Tryck Space för att öppna sidopanelen. Controller - + + New version %1 is available + + + + + You have the latest version + + + + &Take Screenshot &Ta skärmdump - + &Open Launcher - + &Configuration &Konfiguration - + &About - + + Check for updates + + + + &Latest Uploads @@ -237,7 +252,7 @@ Tryck Space för att öppna sidopanelen. &Information - + &Quit &Avsluta @@ -875,12 +890,12 @@ Tryck Space för att öppna sidopanelen. PixelateTool - + Pixelate - + Set Pixelate as the paint tool @@ -1116,7 +1131,7 @@ You can find me in the system tray. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. @@ -1171,22 +1186,22 @@ You can find me in the system tray. Stäng skärmavbildning - + Screenshot history - + Capture screen - + Show color picker Visa färgväljare - + Change the tool's thickness Ändra verktygets tjocklek @@ -1220,12 +1235,12 @@ You can find me in the system tray. SaveTool - + Save Spara - + Save the capture Spara skärmklippet @@ -1233,7 +1248,7 @@ You can find me in the system tray. ScreenGrabber - + Unable to capture screen Kunde inte avbilda skärmen @@ -1531,6 +1546,29 @@ You can find me in the system tray. Ångra senaste ändringen + + UpdateNotificationWidget + + + New Flameshot version %1 is available + + + + + Ignore + + + + + Later + + + + + Update + + + UploadStorageConfig @@ -1552,15 +1590,10 @@ You can find me in the system tray. UtilityPanel - + Close - - - Hide - - VisualsEditor diff --git a/data/translations/Internationalization_tr.ts b/data/translations/Internationalization_tr.ts index 5895bcfd..1e07140e 100644 --- a/data/translations/Internationalization_tr.ts +++ b/data/translations/Internationalization_tr.ts @@ -123,12 +123,12 @@ CaptureWidget - + Unable to capture screen Ekran resmi alınamadı - + Select an area with the mouse, or press Esc to exit. Press Enter to capture the screen. Press Right Click to show the color picker. @@ -141,7 +141,7 @@ Aracınızın kalınlığını değiştirmek için Fare Tekerleğini kullanın. Yan paneli açmak için Boşluk tuşuna basın. - + Tool Settings @@ -208,27 +208,42 @@ Yan paneli açmak için Boşluk tuşuna basın. Controller - + + New version %1 is available + + + + + You have the latest version + + + + &Take Screenshot &Ekran Resmi Al - + &Open Launcher - + &Configuration &Ayarlar - + &About - + + Check for updates + + + + &Latest Uploads @@ -237,7 +252,7 @@ Yan paneli açmak için Boşluk tuşuna basın. &Bilgi - + &Quit &Çıkış @@ -871,12 +886,12 @@ Yan paneli açmak için Boşluk tuşuna basın. PixelateTool - + Pixelate - + Set Pixelate as the paint tool @@ -1112,7 +1127,7 @@ You can find me in the system tray. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. @@ -1167,22 +1182,22 @@ You can find me in the system tray. Çıkış - + Screenshot history - + Capture screen - + Show color picker Renk seçici göster - + Change the tool's thickness Araç kalınlığını değiştirin @@ -1216,12 +1231,12 @@ You can find me in the system tray. SaveTool - + Save Kaydet - + Save the capture Yakalamayı kaydet @@ -1229,7 +1244,7 @@ You can find me in the system tray. ScreenGrabber - + Unable to capture screen Ekran resmi alınamadı @@ -1527,6 +1542,29 @@ You can find me in the system tray. Son değişikliği geri al + + UpdateNotificationWidget + + + New Flameshot version %1 is available + + + + + Ignore + + + + + Later + + + + + Update + + + UploadStorageConfig @@ -1548,15 +1586,10 @@ You can find me in the system tray. UtilityPanel - + Close - - - Hide - - VisualsEditor diff --git a/data/translations/Internationalization_uk.ts b/data/translations/Internationalization_uk.ts index 51f286e3..1268e716 100644 --- a/data/translations/Internationalization_uk.ts +++ b/data/translations/Internationalization_uk.ts @@ -123,12 +123,12 @@ CaptureWidget - + Unable to capture screen Не вдалось захопити екран - + Select an area with the mouse, or press Esc to exit. Press Enter to capture the screen. Press Right Click to show the color picker. @@ -141,7 +141,7 @@ Press Space to open the side panel. Натисніть Пробіл, щоб відкрити бічну панель. - + Tool Settings Налаштування інструменту @@ -208,27 +208,42 @@ Press Space to open the side panel. Controller - + + New version %1 is available + Доступна нова версія %1 + + + + You have the latest version + Ви маєте найпізнішу версію + + + &Take Screenshot &Зробити знімок - + &Open Launcher & Відкрити лаунчер - + &Configuration &Налаштування - + &About Про - + + Check for updates + Перевірити оновлення + + + &Latest Uploads Останні завантаження @@ -237,7 +252,7 @@ Press Space to open the side panel. &Інформація - + &Quit Ви&йти @@ -895,12 +910,12 @@ Press Space to open the side panel. PixelateTool - + Pixelate Розмиття - + Set Pixelate as the paint tool Обрати Pixelate інструментом для малювання @@ -1143,7 +1158,7 @@ You can find me in the system tray. Ви можете знайти мене в системному треї. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. Привіт, я тут! Клацніть піктограму в треї, щоб зробити знімок екрана, або клацніть правою кнопкою, щоб побачити більше опцій. @@ -1198,22 +1213,22 @@ You can find me in the system tray. Вийти із захоплення екрану - + Screenshot history Історія знімків екрану - + Capture screen Захоплення екрану - + Show color picker Показати вибір кольору - + Change the tool's thickness Змінити товщину інструменту @@ -1247,12 +1262,12 @@ You can find me in the system tray. SaveTool - + Save Зберегти - + Save the capture Зберегти знімок @@ -1260,7 +1275,7 @@ You can find me in the system tray. ScreenGrabber - + Unable to capture screen Не вдалось захопити екран @@ -1558,6 +1573,29 @@ You can find me in the system tray. Скасувати останню зміну + + UpdateNotificationWidget + + + New Flameshot version %1 is available + Доступна нова версія Flameshot %1 + + + + Ignore + Ігнорувати + + + + Later + Пізніше + + + + Update + Оновити + + UploadStorageConfig @@ -1579,14 +1617,13 @@ You can find me in the system tray. UtilityPanel - + Close Затворити - Hide - Сховати + Сховати diff --git a/data/translations/Internationalization_zh_CN.ts b/data/translations/Internationalization_zh_CN.ts index 7ec424c1..8afc09ae 100644 --- a/data/translations/Internationalization_zh_CN.ts +++ b/data/translations/Internationalization_zh_CN.ts @@ -123,13 +123,13 @@ CaptureWidget - + Unable to capture screen 无法捕获屏幕 无法捕获屏幕 - + Select an area with the mouse, or press Esc to exit. Press Enter to capture the screen. Press Right Click to show the color picker. @@ -142,7 +142,7 @@ Press Space to open the side panel. 按下空格键以打开侧边面板。 - + Tool Settings 工具设置 @@ -209,27 +209,42 @@ Press Space to open the side panel. Controller - + + New version %1 is available + + + + + You have the latest version + + + + &Take Screenshot 进行截图(&T) - + &Open Launcher 打开启动器(&O) - + &Configuration 配置(&C) - + &About 关于(&A) - + + Check for updates + + + + &Latest Uploads @@ -238,7 +253,7 @@ Press Space to open the side panel. 信息(&I) - + &Quit 退出(&Q) @@ -884,12 +899,12 @@ Press Space to open the side panel. PixelateTool - + Pixelate 像素化 - + Set Pixelate as the paint tool 将像素化设置为绘画工具 @@ -1135,7 +1150,7 @@ You can find me in the system tray. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. @@ -1190,22 +1205,22 @@ You can find me in the system tray. 退出捕获 - + Screenshot history - + Capture screen - + Show color picker 显示颜色选择器 - + Change the tool's thickness 改变工具的厚度 @@ -1239,12 +1254,12 @@ You can find me in the system tray. SaveTool - + Save 保存 - + Save the capture 保存捕获 @@ -1252,7 +1267,7 @@ You can find me in the system tray. ScreenGrabber - + Unable to capture screen 无法捕获屏幕 @@ -1550,6 +1565,29 @@ You can find me in the system tray. 撤消上次修改 + + UpdateNotificationWidget + + + New Flameshot version %1 is available + + + + + Ignore + + + + + Later + + + + + Update + + + UploadStorageConfig @@ -1571,15 +1609,10 @@ You can find me in the system tray. UtilityPanel - + Close 关闭 - - - Hide - - VisualsEditor diff --git a/data/translations/Internationalization_zh_HK.ts b/data/translations/Internationalization_zh_HK.ts index e1db3e84..aee7801b 100644 --- a/data/translations/Internationalization_zh_HK.ts +++ b/data/translations/Internationalization_zh_HK.ts @@ -123,12 +123,12 @@ CaptureWidget - + Unable to capture screen 無法捕獲屏幕 - + Select an area with the mouse, or press Esc to exit. Press Enter to capture the screen. Press Right Click to show the color picker. @@ -141,7 +141,7 @@ Press Space to open the side panel. 按Space以打開側方面板。 - + Tool Settings 工具選項 @@ -208,27 +208,42 @@ Press Space to open the side panel. Controller - + + New version %1 is available + + + + + You have the latest version + + + + &Take Screenshot &捕獲截圖 - + &Open Launcher &開啓啓動器 - + &Configuration &設定 - + &About &關於 - + + Check for updates + + + + &Latest Uploads @@ -237,7 +252,7 @@ Press Space to open the side panel. &資訊 - + &Quit &結束 @@ -871,12 +886,12 @@ Press Space to open the side panel. PixelateTool - + Pixelate 馬賽克工具 - + Set Pixelate as the paint tool 將馬賽克工具設定為繪畫工具 @@ -1112,7 +1127,7 @@ You can find me in the system tray. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. @@ -1167,22 +1182,22 @@ You can find me in the system tray. 結束擷取 - + Screenshot history - + Capture screen - + Show color picker 顯示顏色選擇器 - + Change the tool's thickness 改變工具的寬度 @@ -1216,12 +1231,12 @@ You can find me in the system tray. SaveTool - + Save 儲存 - + Save the capture 儲存螢幕捕獲 @@ -1229,7 +1244,7 @@ You can find me in the system tray. ScreenGrabber - + Unable to capture screen 無法捕獲螢幕 @@ -1527,6 +1542,29 @@ You can find me in the system tray. 撤銷上次修改 + + UpdateNotificationWidget + + + New Flameshot version %1 is available + + + + + Ignore + + + + + Later + + + + + Update + + + UploadStorageConfig @@ -1548,15 +1586,10 @@ You can find me in the system tray. UtilityPanel - + Close 關閉 - - - Hide - - VisualsEditor diff --git a/data/translations/Internationalization_zh_TW.ts b/data/translations/Internationalization_zh_TW.ts index 67169b50..d925475f 100644 --- a/data/translations/Internationalization_zh_TW.ts +++ b/data/translations/Internationalization_zh_TW.ts @@ -123,12 +123,12 @@ CaptureWidget - + Unable to capture screen 無法擷取螢幕 - + Select an area with the mouse, or press Esc to exit. Press Enter to capture the screen. Press Right Click to show the color picker. @@ -137,7 +137,7 @@ Press Space to open the side panel. - + Tool Settings @@ -204,27 +204,42 @@ Press Space to open the side panel. Controller - + + New version %1 is available + + + + + You have the latest version + + + + &Take Screenshot - + &Open Launcher - + &Configuration &設定 - + &About - + + Check for updates + + + + &Latest Uploads @@ -233,7 +248,7 @@ Press Space to open the side panel. &資訊 - + &Quit &結束 @@ -863,12 +878,12 @@ Press Space to open the side panel. PixelateTool - + Pixelate - + Set Pixelate as the paint tool @@ -1104,7 +1119,7 @@ You can find me in the system tray. - + Hello, I'm here! Click icon in the tray to take a screenshot or click with a right button to see more options. @@ -1159,22 +1174,22 @@ You can find me in the system tray. 結束擷取 - + Screenshot history - + Capture screen - + Show color picker 顯示顏色選擇器 - + Change the tool's thickness 改變工具的寬度 @@ -1208,12 +1223,12 @@ You can find me in the system tray. SaveTool - + Save 儲存 - + Save the capture 儲存擷取 @@ -1221,7 +1236,7 @@ You can find me in the system tray. ScreenGrabber - + Unable to capture screen 無法擷取螢幕 @@ -1519,6 +1534,29 @@ You can find me in the system tray. 復原上次修改 + + UpdateNotificationWidget + + + New Flameshot version %1 is available + + + + + Ignore + + + + + Later + + + + + Update + + + UploadStorageConfig @@ -1540,15 +1578,10 @@ You can find me in the system tray. UtilityPanel - + Close - - - Hide - - VisualsEditor diff --git a/src/core/controller.cpp b/src/core/controller.cpp index bb899c7c..74e99971 100644 --- a/src/core/controller.cpp +++ b/src/core/controller.cpp @@ -119,7 +119,6 @@ void Controller::getLatestAvailableVersion() { // This features is required for MacOS and Windows user and for Linux users // who installed Flameshot not from the repository. - m_networkCheckUpdates = new QNetworkAccessManager(); m_networkCheckUpdates = new QNetworkAccessManager(this); QNetworkRequest requestCheckUpdates(QUrl(FLAMESHOT_APP_VERSION_URL)); connect(m_networkCheckUpdates, @@ -127,6 +126,11 @@ void Controller::getLatestAvailableVersion() this, &Controller::handleReplyCheckUpdates); m_networkCheckUpdates->get(requestCheckUpdates); + + // check for updates each 24 hours + doLater(1000 * 60 * 60 * 24, this, [this]() { + this->getLatestAvailableVersion(); + }); } void Controller::handleReplyCheckUpdates(QNetworkReply* reply) @@ -137,9 +141,6 @@ void Controller::handleReplyCheckUpdates(QNetworkReply* reply) m_appLatestVersion = json["tag_name"].toString().replace("v", ""); if (m_appLatestVersion.compare( QStringLiteral(APP_VERSION).replace("v", "")) < 0) { - // Next commented lines are for debugging - // if (m_appLatestVersion.compare( - // QStringLiteral("v0.8.5.4").replace("v", "")) > 0) { m_appLatestUrl = json["html_url"].toString(); QString newVersion = tr("New version %1 is available").arg(m_appLatestVersion); @@ -152,8 +153,16 @@ void Controller::handleReplyCheckUpdates(QNetworkReply* reply) sendTrayNotification( tr("You have the latest version"), "Flameshot", 5); } + } else { + qWarning() << "Failed to get information about the latest version. " + << reply->errorString(); + if (m_showCheckAppUpdateStatus) { + sendTrayNotification( + tr("Failed to get information about the latest version."), + "Flameshot", + 5); + } } - // nothing to do on fails, is not critical for checking for updates m_showCheckAppUpdateStatus = false; } From 6ff18e74ddcbe5318901e2b1d22c7ee7a624a77d Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Sat, 12 Dec 2020 07:53:32 +0200 Subject: [PATCH 44/54] Get updates url from the current git remote --- CMakeLists.txt | 40 +++++++++++++++++++++++++++++++++++----- src/CMakeLists.txt | 1 + src/core/controller.h | 3 --- 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d924ef8..b18af5b8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,16 +1,46 @@ cmake_minimum_required(VERSION 3.13) # cmake_policy(SET CMP0076 OLD) +set(FLAMESHOT_VERSION 0.8.5.5) + +# Flameshot-org +#set(GIT_API_URL "https://api.github.com/repos/flameshot-org/flameshot/releases/latest") +# Namecheap +set(GIT_API_URL "https://api.github.com/repos/namecheap/flameshot/releases/latest") + +# TODO - fix it for all linux distros +# find_package (Git) +#if (GIT_FOUND) +# message("git found: ${GIT_EXECUTABLE} in version ${GIT_VERSION_STRING}") +# +# # set flameshot updates url +# execute_process(COMMAND ${GIT_EXECUTABLE} ls-remote --get-url OUTPUT_VARIABLE GIT_ORIGIN_REMOTE) +# message("GIT_ORIGIN_REMOTE: ${GIT_ORIGIN_REMOTE}") +# string(REGEX REPLACE ".git\r*\n*$" "/releases/latest" GIT_API_URL ${GIT_ORIGIN_REMOTE}) +# string(REGEX REPLACE "^.*:" "https://api.github.com/repos/" GIT_API_URL ${GIT_API_URL}) +# message("GIT_API_URL: '${GIT_API_URL}'") +# +# # get application version +# execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --abbrev=0 --match v[0-9]* OUTPUT_VARIABLE FLAMESHOT_VERSION) +# string(REGEX REPLACE "\r" "" FLAMESHOT_VERSION ${FLAMESHOT_VERSION}) +# string(REGEX REPLACE "\n" "" FLAMESHOT_VERSION ${FLAMESHOT_VERSION}) +# string(REGEX REPLACE "^v" "" FLAMESHOT_VERSION ${FLAMESHOT_VERSION}) +# message("FLAMESHOT_VERSION: '${FLAMESHOT_VERSION}'") +#else() +# message("git command is not found") +#endif () + +project( + flameshot + VERSION ${FLAMESHOT_VERSION} + LANGUAGES CXX) +set(PROJECT_NAME_CAPITALIZED "Flameshot") + # This can be read from ${PROJECT_NAME} after project() is called if(APPLE) set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version") endif() -project( - flameshot - VERSION 0.8.5.5 - LANGUAGES CXX) -set(PROJECT_NAME_CAPITALIZED "Flameshot") # Configuration options set(DEFAULT_RUN_IN_PLACE FALSE) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e95c059d..16130de8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -174,6 +174,7 @@ target_compile_definitions(flameshot PRIVATE APP_PREFIX="${CMAKE_INSTALL_PREFIX} target_compile_definitions(flameshot PRIVATE APP_VERSION="v${PROJECT_VERSION}") target_compile_definitions(flameshot PRIVATE IMGUR_CLIENT_ID="313baf0c7b4d3ff") target_compile_definitions(flameshot PRIVATE QAPPLICATION_CLASS=QApplication) +target_compile_definitions(flameshot PRIVATE FLAMESHOT_APP_VERSION_URL="${GIT_API_URL}") foreach(FILE ${QM_FILES}) get_filename_component(F_NAME ${FILE} NAME) diff --git a/src/core/controller.h b/src/core/controller.h index 3ffff381..1baf8ba6 100644 --- a/src/core/controller.h +++ b/src/core/controller.h @@ -17,9 +17,6 @@ #pragma once -#define FLAMESHOT_APP_VERSION_URL \ - "https://api.github.com/repos/namecheap/flameshot/releases/latest" - #include "src/core/capturerequest.h" #include #include From f2bb47ac014fce843d1e980d93994f9bf26f1411 Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Tue, 15 Dec 2020 13:48:33 +0200 Subject: [PATCH 45/54] MacOS - remove 'src/' from 'src/flameshot' on starting from DMG --- .github/workflows/MacOS-pack.yml | 4 ++-- packaging/macos/update_package.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/MacOS-pack.yml b/.github/workflows/MacOS-pack.yml index 6669e367..e63f6bd5 100644 --- a/.github/workflows/MacOS-pack.yml +++ b/.github/workflows/MacOS-pack.yml @@ -43,8 +43,8 @@ jobs: - name: Build dmg package run: | - cd build - /usr/local/opt/qt5/bin/macdeployqt src/flameshot.app -dmg + cd build/src + /usr/local/opt/qt5/bin/macdeployqt flameshot.app -dmg - name: Update dmg package links run: | diff --git a/packaging/macos/update_package.sh b/packaging/macos/update_package.sh index 048be244..f68103a9 100755 --- a/packaging/macos/update_package.sh +++ b/packaging/macos/update_package.sh @@ -8,7 +8,7 @@ DEVICE=$(hdiutil attach -readwrite -noverify "flameshot_rw.dmg" | egrep '^/dev/' sleep 5 echo "Create the sysmbolic link to application folder" -PATH_AT_VOLUME="/Volumes/src:flameshot/" +PATH_AT_VOLUME="/Volumes/flameshot/" CURRENT_PATH="$(pwd)" cd "${PATH_AT_VOLUME}" ln -s /Applications From 162b864353d852da8ef0a72f2d9ab1280da51de9 Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Wed, 16 Dec 2020 19:47:50 +0200 Subject: [PATCH 46/54] MacOS - Global shortcuts --- src/CMakeLists.txt | 28 ++- src/core/CMakeLists.txt | 9 +- src/core/QHotkey/CMakeLists.txt | 43 ++++ src/core/QHotkey/QHotkey | 1 + src/core/QHotkey/QHotkey.pro | 16 ++ src/core/QHotkey/qhotkey.cpp | 391 +++++++++++++++++++++++++++++++ src/core/QHotkey/qhotkey.h | 138 +++++++++++ src/core/QHotkey/qhotkey.pri | 1 + src/core/QHotkey/qhotkey_mac.cpp | 328 ++++++++++++++++++++++++++ src/core/QHotkey/qhotkey_p.h | 68 ++++++ src/core/QHotkey/qhotkey_win.cpp | 313 +++++++++++++++++++++++++ src/core/QHotkey/qhotkey_x11.cpp | 259 ++++++++++++++++++++ src/core/controller.cpp | 20 ++ src/core/controller.h | 9 + src/utils/configshortcuts.cpp | 9 +- 15 files changed, 1620 insertions(+), 13 deletions(-) create mode 100644 src/core/QHotkey/CMakeLists.txt create mode 100644 src/core/QHotkey/QHotkey create mode 100644 src/core/QHotkey/QHotkey.pro create mode 100644 src/core/QHotkey/qhotkey.cpp create mode 100644 src/core/QHotkey/qhotkey.h create mode 100644 src/core/QHotkey/qhotkey.pri create mode 100644 src/core/QHotkey/qhotkey_mac.cpp create mode 100644 src/core/QHotkey/qhotkey_p.h create mode 100644 src/core/QHotkey/qhotkey_win.cpp create mode 100644 src/core/QHotkey/qhotkey_x11.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 16130de8..2b741053 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -146,14 +146,26 @@ target_include_directories( $ $) -target_link_libraries( - flameshot - project_warnings - project_options - Qt5::Svg - Qt5::DBus - Qt5::Network - Qt5::Widgets) +if (APPLE) + target_link_libraries( + flameshot + project_warnings + project_options + qhotkey + Qt5::Svg + Qt5::DBus + Qt5::Network + Qt5::Widgets) +else () + target_link_libraries( + flameshot + project_warnings + project_options + Qt5::Svg + Qt5::DBus + Qt5::Network + Qt5::Widgets) +endif () set(USE_OPENSSL FALSE) if(ENABLE_OPENSSL) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 2cea0765..e93645c8 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1,9 +1,12 @@ - # Required to generate MOC +IF (APPLE) + add_subdirectory(QHotkey) +ENDIF () + target_sources(flameshot PRIVATE controller.h flameshotdbusadapter.h) target_sources(flameshot PRIVATE capturerequest.cpp controller.cpp flameshotdbusadapter.cpp) -IF(WIN32) +IF (WIN32) target_sources(flameshot PRIVATE globalshortcutfilter.h globalshortcutfilter.cpp) -ENDIF() +ENDIF () diff --git a/src/core/QHotkey/CMakeLists.txt b/src/core/QHotkey/CMakeLists.txt new file mode 100644 index 00000000..05487262 --- /dev/null +++ b/src/core/QHotkey/CMakeLists.txt @@ -0,0 +1,43 @@ +find_package(Qt5 COMPONENTS Core Widgets REQUIRED) + +qt5_wrap_cpp(MOC_HEADERS + qhotkey.h + qhotkey_p.h) + +set(LIBS + Qt5::Core + Qt5::Widgets) + +set(SRC_FILES + qhotkey.cpp) + +if(APPLE) + set(CMAKE_C_ARCHIVE_CREATE " Scr ") + set(CMAKE_CXX_ARCHIVE_CREATE " Scr ") + set(CMAKE_C_ARCHIVE_FINISH " -no_warning_for_no_symbols -c ") + set(CMAKE_CXX_ARCHIVE_FINISH " -no_warning_for_no_symbols -c ") + + find_library(CARBON_LIBRARY Carbon) + mark_as_advanced(CARBON_LIBRARY) + + set(SRC_FILES ${SRC_FILES} qhotkey_mac.cpp) + set(LIBS ${LIBS} ${CARBON_LIBRARY}) +elseif(WIN32) + set(SRC_FILES ${SRC_FILES} qhotkey_win.cpp) +else() + find_package(X11 REQUIRED) + find_package(Qt5X11Extras REQUIRED) + + include_directories(${X11_INCLUDE_DIR}) + set(LIBS ${LIBS} ${X11_LIBRARIES} Qt5::X11Extras) + set(SRC_FILES ${SRC_FILES} qhotkey_x11.cpp) +endif() + +add_library(qhotkey ${SRC_FILES} ${MOC_HEADERS}) +add_library(QHotkey::QHotkey ALIAS qhotkey) +target_link_libraries(qhotkey ${LIBS}) + +target_include_directories(qhotkey + PUBLIC + $ + $) diff --git a/src/core/QHotkey/QHotkey b/src/core/QHotkey/QHotkey new file mode 100644 index 00000000..21d5e577 --- /dev/null +++ b/src/core/QHotkey/QHotkey @@ -0,0 +1 @@ +#include "qhotkey.h" diff --git a/src/core/QHotkey/QHotkey.pro b/src/core/QHotkey/QHotkey.pro new file mode 100644 index 00000000..44e937bf --- /dev/null +++ b/src/core/QHotkey/QHotkey.pro @@ -0,0 +1,16 @@ +TEMPLATE = lib +win32: CONFIG += dll + +TARGET = QHotkey +VERSION = 1.2.1 + +include(../qhotkey.pri) + +DEFINES += QHOTKEY_LIB QHOTKEY_LIB_BUILD + +# use INSTALL_ROOT to modify the install location +headers.files = $$PUBLIC_HEADERS +headers.path = $$[QT_INSTALL_HEADERS] +target.path = $$[QT_INSTALL_LIBS] +INSTALLS += target headers + diff --git a/src/core/QHotkey/qhotkey.cpp b/src/core/QHotkey/qhotkey.cpp new file mode 100644 index 00000000..0869810c --- /dev/null +++ b/src/core/QHotkey/qhotkey.cpp @@ -0,0 +1,391 @@ +#include "qhotkey.h" +#include "qhotkey_p.h" +#include +#include +#include +#include +#include + +Q_LOGGING_CATEGORY(logQHotkey, "QHotkey") + +void QHotkey::addGlobalMapping(const QKeySequence& shortcut, + QHotkey::NativeShortcut nativeShortcut) +{ + QMetaObject::invokeMethod( + QHotkeyPrivate::instance(), + "addMappingInvoked", + Qt::QueuedConnection, + Q_ARG(Qt::Key, Qt::Key(shortcut[0] & ~Qt::KeyboardModifierMask)), + Q_ARG(Qt::KeyboardModifiers, + Qt::KeyboardModifiers(shortcut[0] & Qt::KeyboardModifierMask)), + Q_ARG(QHotkey::NativeShortcut, nativeShortcut)); +} + +bool QHotkey::isPlatformSupported() +{ + return QHotkeyPrivate::isPlatformSupported(); +} + +QHotkey::QHotkey(QObject* parent) + : QObject(parent) + , _keyCode(Qt::Key_unknown) + , _modifiers(Qt::NoModifier) + , _registered(false) +{} + +QHotkey::QHotkey(const QKeySequence& shortcut, + bool autoRegister, + QObject* parent) + : QHotkey(parent) +{ + setShortcut(shortcut, autoRegister); +} + +QHotkey::QHotkey(Qt::Key keyCode, + Qt::KeyboardModifiers modifiers, + bool autoRegister, + QObject* parent) + : QHotkey(parent) +{ + setShortcut(keyCode, modifiers, autoRegister); +} + +QHotkey::QHotkey(QHotkey::NativeShortcut shortcut, + bool autoRegister, + QObject* parent) + : QHotkey(parent) +{ + setNativeShortcut(shortcut, autoRegister); +} + +QHotkey::~QHotkey() +{ + if (_registered) + QHotkeyPrivate::instance()->removeShortcut(this); +} + +QKeySequence QHotkey::shortcut() const +{ + if (_keyCode == Qt::Key_unknown) + return QKeySequence(); + return QKeySequence(static_cast(_keyCode | _modifiers)); +} + +Qt::Key QHotkey::keyCode() const +{ + return _keyCode; +} + +Qt::KeyboardModifiers QHotkey::modifiers() const +{ + return _modifiers; +} + +QHotkey::NativeShortcut QHotkey::currentNativeShortcut() const +{ + return _nativeShortcut; +} + +bool QHotkey::isRegistered() const +{ + return _registered; +} + +bool QHotkey::setShortcut(const QKeySequence& shortcut, bool autoRegister) +{ + if (shortcut.isEmpty()) + return resetShortcut(); + if (shortcut.count() > 1) { + qCWarning(logQHotkey, + "Keysequences with multiple shortcuts are not allowed! " + "Only the first shortcut will be used!"); + } + + return setShortcut( + Qt::Key(shortcut[0] & ~Qt::KeyboardModifierMask), + Qt::KeyboardModifiers(shortcut[0] & Qt::KeyboardModifierMask), + autoRegister); +} + +bool QHotkey::setShortcut(Qt::Key keyCode, + Qt::KeyboardModifiers modifiers, + bool autoRegister) +{ + if (_registered) { + if (autoRegister) { + if (!QHotkeyPrivate::instance()->removeShortcut(this)) + return false; + } else + return false; + } + + if (keyCode == Qt::Key_unknown) { + _keyCode = Qt::Key_unknown; + _modifiers = Qt::NoModifier; + _nativeShortcut = NativeShortcut(); + return true; + } + + _keyCode = keyCode; + _modifiers = modifiers; + _nativeShortcut = + QHotkeyPrivate::instance()->nativeShortcut(keyCode, modifiers); + if (_nativeShortcut.isValid()) { + if (autoRegister) + return QHotkeyPrivate::instance()->addShortcut(this); + return true; + } + + qCWarning(logQHotkey) << "Unable to map shortcut to native keys. Key:" + << keyCode << "Modifiers:" << modifiers; + _keyCode = Qt::Key_unknown; + _modifiers = Qt::NoModifier; + _nativeShortcut = NativeShortcut(); + return false; +} + +bool QHotkey::resetShortcut() +{ + if (_registered && !QHotkeyPrivate::instance()->removeShortcut(this)) { + return false; + } + + _keyCode = Qt::Key_unknown; + _modifiers = Qt::NoModifier; + _nativeShortcut = NativeShortcut(); + return true; +} + +bool QHotkey::setNativeShortcut(QHotkey::NativeShortcut nativeShortcut, + bool autoRegister) +{ + if (_registered) { + if (autoRegister) { + if (!QHotkeyPrivate::instance()->removeShortcut(this)) + return false; + } else + return false; + } + + if (nativeShortcut.isValid()) { + _keyCode = Qt::Key_unknown; + _modifiers = Qt::NoModifier; + _nativeShortcut = nativeShortcut; + if (autoRegister) + return QHotkeyPrivate::instance()->addShortcut(this); + return true; + } + + _keyCode = Qt::Key_unknown; + _modifiers = Qt::NoModifier; + _nativeShortcut = NativeShortcut(); + return true; +} + +bool QHotkey::setRegistered(bool registered) +{ + if (_registered && !registered) + return QHotkeyPrivate::instance()->removeShortcut(this); + if (!_registered && registered) { + if (!_nativeShortcut.isValid()) + return false; + return QHotkeyPrivate::instance()->addShortcut(this); + } + return true; +} + +// ---------- QHotkeyPrivate implementation ---------- + +QHotkeyPrivate::QHotkeyPrivate() +{ + Q_ASSERT_X(qApp, + Q_FUNC_INFO, + "QHotkey requires QCoreApplication to be instantiated"); + qApp->eventDispatcher()->installNativeEventFilter(this); +} + +QHotkeyPrivate::~QHotkeyPrivate() +{ + if (!shortcuts.isEmpty()) + qCWarning(logQHotkey) + << "QHotkeyPrivate destroyed with registered shortcuts!"; + if (qApp && qApp->eventDispatcher()) + qApp->eventDispatcher()->removeNativeEventFilter(this); +} + +QHotkey::NativeShortcut QHotkeyPrivate::nativeShortcut( + Qt::Key keycode, + Qt::KeyboardModifiers modifiers) +{ + Qt::ConnectionType conType = + (QThread::currentThread() == thread() ? Qt::DirectConnection + : Qt::BlockingQueuedConnection); + QHotkey::NativeShortcut res; + if (!QMetaObject::invokeMethod(this, + "nativeShortcutInvoked", + conType, + Q_RETURN_ARG(QHotkey::NativeShortcut, res), + Q_ARG(Qt::Key, keycode), + Q_ARG(Qt::KeyboardModifiers, modifiers))) { + return QHotkey::NativeShortcut(); + } + return res; +} + +bool QHotkeyPrivate::addShortcut(QHotkey* hotkey) +{ + if (hotkey->_registered) + return false; + + Qt::ConnectionType conType = + (QThread::currentThread() == thread() ? Qt::DirectConnection + : Qt::BlockingQueuedConnection); + bool res = false; + if (!QMetaObject::invokeMethod(this, + "addShortcutInvoked", + conType, + Q_RETURN_ARG(bool, res), + Q_ARG(QHotkey*, hotkey))) { + return false; + } + + if (res) + emit hotkey->registeredChanged(true); + return res; +} + +bool QHotkeyPrivate::removeShortcut(QHotkey* hotkey) +{ + if (!hotkey->_registered) + return false; + + Qt::ConnectionType conType = + (QThread::currentThread() == thread() ? Qt::DirectConnection + : Qt::BlockingQueuedConnection); + bool res = false; + if (!QMetaObject::invokeMethod(this, + "removeShortcutInvoked", + conType, + Q_RETURN_ARG(bool, res), + Q_ARG(QHotkey*, hotkey))) { + return false; + } + + if (res) + emit hotkey->registeredChanged(false); + return res; +} + +void QHotkeyPrivate::activateShortcut(QHotkey::NativeShortcut shortcut) +{ + QMetaMethod signal = QMetaMethod::fromSignal(&QHotkey::activated); + for (QHotkey* hkey : shortcuts.values(shortcut)) + signal.invoke(hkey, Qt::QueuedConnection); +} + +void QHotkeyPrivate::releaseShortcut(QHotkey::NativeShortcut shortcut) +{ + QMetaMethod signal = QMetaMethod::fromSignal(&QHotkey::released); + for (QHotkey* hkey : shortcuts.values(shortcut)) + signal.invoke(hkey, Qt::QueuedConnection); +} + +void QHotkeyPrivate::addMappingInvoked(Qt::Key keycode, + Qt::KeyboardModifiers modifiers, + QHotkey::NativeShortcut nativeShortcut) +{ + mapping.insert({ keycode, modifiers }, nativeShortcut); +} + +bool QHotkeyPrivate::addShortcutInvoked(QHotkey* hotkey) +{ + QHotkey::NativeShortcut shortcut = hotkey->_nativeShortcut; + + if (!shortcuts.contains(shortcut)) { + if (!registerShortcut(shortcut)) { + qCWarning(logQHotkey) + << QHotkey::tr("Failed to register %1. Error: %2") + .arg(hotkey->shortcut().toString(), error); + return false; + } + } + + shortcuts.insert(shortcut, hotkey); + hotkey->_registered = true; + return true; +} + +bool QHotkeyPrivate::removeShortcutInvoked(QHotkey* hotkey) +{ + QHotkey::NativeShortcut shortcut = hotkey->_nativeShortcut; + + if (shortcuts.remove(shortcut, hotkey) == 0) + return false; + hotkey->_registered = false; + emit hotkey->registeredChanged(true); + if (shortcuts.count(shortcut) == 0) { + if (!unregisterShortcut(shortcut)) { + qCWarning(logQHotkey) + << QHotkey::tr("Failed to unregister %1. Error: %2") + .arg(hotkey->shortcut().toString(), error); + return false; + } + return true; + } + return true; +} + +QHotkey::NativeShortcut QHotkeyPrivate::nativeShortcutInvoked( + Qt::Key keycode, + Qt::KeyboardModifiers modifiers) +{ + if (mapping.contains({ keycode, modifiers })) + return mapping.value({ keycode, modifiers }); + + bool ok1 = false; + auto k = nativeKeycode(keycode, ok1); + bool ok2 = false; + auto m = nativeModifiers(modifiers, ok2); + if (ok1 && ok2) + return { k, m }; + return {}; +} + +QHotkey::NativeShortcut::NativeShortcut() + : key() + , modifier() + , valid(false) +{} + +QHotkey::NativeShortcut::NativeShortcut(quint32 key, quint32 modifier) + : key(key) + , modifier(modifier) + , valid(true) +{} + +bool QHotkey::NativeShortcut::isValid() const +{ + return valid; +} + +bool QHotkey::NativeShortcut::operator==(QHotkey::NativeShortcut other) const +{ + return (key == other.key) && (modifier == other.modifier) && + valid == other.valid; +} + +bool QHotkey::NativeShortcut::operator!=(QHotkey::NativeShortcut other) const +{ + return (key != other.key) || (modifier != other.modifier) || + valid != other.valid; +} + +uint qHash(QHotkey::NativeShortcut key) +{ + return qHash(key.key) ^ qHash(key.modifier); +} + +uint qHash(QHotkey::NativeShortcut key, uint seed) +{ + return qHash(key.key, seed) ^ qHash(key.modifier, seed); +} diff --git a/src/core/QHotkey/qhotkey.h b/src/core/QHotkey/qhotkey.h new file mode 100644 index 00000000..8ab96468 --- /dev/null +++ b/src/core/QHotkey/qhotkey.h @@ -0,0 +1,138 @@ +#ifndef QHOTKEY_H +#define QHOTKEY_H + +#include +#include +#include +#include + +#ifdef QHOTKEY_LIB +#ifdef QHOTKEY_LIB_BUILD +#define QHOTKEY_SHARED_EXPORT Q_DECL_EXPORT +#else +#define QHOTKEY_SHARED_EXPORT Q_DECL_IMPORT +#endif +#else +#define QHOTKEY_SHARED_EXPORT +#endif + +//! A class to define global, systemwide Hotkeys +class QHOTKEY_SHARED_EXPORT QHotkey : public QObject +{ + Q_OBJECT + //! @private + friend class QHotkeyPrivate; + + //! Specifies whether this hotkey is currently registered or not + Q_PROPERTY(bool registered READ isRegistered WRITE setRegistered NOTIFY + registeredChanged) + //! Holds the shortcut this hotkey will be triggered on + Q_PROPERTY( + QKeySequence shortcut READ shortcut WRITE setShortcut RESET resetShortcut) + +public: + //! Defines shortcut with native keycodes + class QHOTKEY_SHARED_EXPORT NativeShortcut + { + public: + //! The native keycode + quint32 key; + //! The native modifiers + quint32 modifier; + + //! Creates an invalid native shortcut + NativeShortcut(); + //! Creates a valid native shortcut, with the given key and modifiers + NativeShortcut(quint32 key, quint32 modifier = 0); + + //! Checks, whether this shortcut is valid or not + bool isValid() const; + + //! Equality operator + bool operator==(NativeShortcut other) const; + //! Inequality operator + bool operator!=(NativeShortcut other) const; + + private: + bool valid; + }; + + //! Adds a global mapping of a key sequence to a replacement native shortcut + static void addGlobalMapping(const QKeySequence& shortcut, + NativeShortcut nativeShortcut); + + //! Checks if global shortcuts are supported by the current platform + static bool isPlatformSupported(); + + //! Default Constructor + explicit QHotkey(QObject* parent = nullptr); + //! Constructs a hotkey with a shortcut and optionally registers it + explicit QHotkey(const QKeySequence& shortcut, + bool autoRegister = false, + QObject* parent = nullptr); + //! Constructs a hotkey with a key and modifiers and optionally registers it + explicit QHotkey(Qt::Key keyCode, + Qt::KeyboardModifiers modifiers, + bool autoRegister = false, + QObject* parent = nullptr); + //! Constructs a hotkey from a native shortcut and optionally registers it + explicit QHotkey(NativeShortcut shortcut, + bool autoRegister = false, + QObject* parent = nullptr); + ~QHotkey() override; + + //! @readAcFn{QHotkey::registered} + bool isRegistered() const; + //! @readAcFn{QHotkey::shortcut} + QKeySequence shortcut() const; + //! @readAcFn{QHotkey::shortcut} - the key only + Qt::Key keyCode() const; + //! @readAcFn{QHotkey::shortcut} - the modifiers only + Qt::KeyboardModifiers modifiers() const; + + //! Get the current native shortcut + NativeShortcut currentNativeShortcut() const; + +public slots: + //! @writeAcFn{QHotkey::registered} + bool setRegistered(bool registered); + + //! @writeAcFn{QHotkey::shortcut} + bool setShortcut(const QKeySequence& shortcut, bool autoRegister = false); + //! @writeAcFn{QHotkey::shortcut} + bool setShortcut(Qt::Key keyCode, + Qt::KeyboardModifiers modifiers, + bool autoRegister = false); + //! @resetAcFn{QHotkey::shortcut} + bool resetShortcut(); + + //! Set this hotkey to a native shortcut + bool setNativeShortcut(QHotkey::NativeShortcut nativeShortcut, + bool autoRegister = false); + +signals: + //! Will be emitted if the shortcut is pressed + void activated(QPrivateSignal); + + //! Will be emitted if the shortcut press is released + void released(QPrivateSignal); + + //! @notifyAcFn{QHotkey::registered} + void registeredChanged(bool registered); + +private: + Qt::Key _keyCode; + Qt::KeyboardModifiers _modifiers; + + NativeShortcut _nativeShortcut; + bool _registered; +}; + +uint QHOTKEY_SHARED_EXPORT qHash(QHotkey::NativeShortcut key); +uint QHOTKEY_SHARED_EXPORT qHash(QHotkey::NativeShortcut key, uint seed); + +QHOTKEY_SHARED_EXPORT Q_DECLARE_LOGGING_CATEGORY(logQHotkey) + + Q_DECLARE_METATYPE(QHotkey::NativeShortcut) + +#endif // QHOTKEY_H diff --git a/src/core/QHotkey/qhotkey.pri b/src/core/QHotkey/qhotkey.pri new file mode 100644 index 00000000..a7c97250 --- /dev/null +++ b/src/core/QHotkey/qhotkey.pri @@ -0,0 +1 @@ +message(The pri file has been moved one directory up! use that one instead) diff --git a/src/core/QHotkey/qhotkey_mac.cpp b/src/core/QHotkey/qhotkey_mac.cpp new file mode 100644 index 00000000..29e50df5 --- /dev/null +++ b/src/core/QHotkey/qhotkey_mac.cpp @@ -0,0 +1,328 @@ +#include "qhotkey.h" +#include "qhotkey_p.h" +#include +#include + +class QHotkeyPrivateMac : public QHotkeyPrivate +{ +public: + // QAbstractNativeEventFilter interface + bool nativeEventFilter(const QByteArray& eventType, + void* message, + long* result) Q_DECL_OVERRIDE; + + static OSStatus hotkeyPressEventHandler(EventHandlerCallRef nextHandler, + EventRef event, + void* data); + static OSStatus hotkeyReleaseEventHandler(EventHandlerCallRef nextHandler, + EventRef event, + void* data); + +protected: + // QHotkeyPrivate interface + quint32 nativeKeycode(Qt::Key keycode, bool& ok) Q_DECL_OVERRIDE; + quint32 nativeModifiers(Qt::KeyboardModifiers modifiers, + bool& ok) Q_DECL_OVERRIDE; + bool registerShortcut(QHotkey::NativeShortcut shortcut) Q_DECL_OVERRIDE; + bool unregisterShortcut(QHotkey::NativeShortcut shortcut) Q_DECL_OVERRIDE; + +private: + static bool isHotkeyHandlerRegistered; + static QHash hotkeyRefs; +}; +NATIVE_INSTANCE(QHotkeyPrivateMac) + +bool QHotkeyPrivate::isPlatformSupported() +{ + return true; +} + +bool QHotkeyPrivateMac::isHotkeyHandlerRegistered = false; +QHash QHotkeyPrivateMac::hotkeyRefs; + +bool QHotkeyPrivateMac::nativeEventFilter(const QByteArray& eventType, + void* message, + long* result) +{ + Q_UNUSED(eventType) + Q_UNUSED(message) + Q_UNUSED(result) + return false; +} + +quint32 QHotkeyPrivateMac::nativeKeycode(Qt::Key keycode, bool& ok) +{ + // Constants found in NSEvent.h from AppKit.framework + ok = true; + switch (keycode) { + case Qt::Key_Return: + return kVK_Return; + case Qt::Key_Enter: + return kVK_ANSI_KeypadEnter; + case Qt::Key_Tab: + return kVK_Tab; + case Qt::Key_Space: + return kVK_Space; + case Qt::Key_Backspace: + return kVK_Delete; + case Qt::Key_Escape: + return kVK_Escape; + case Qt::Key_CapsLock: + return kVK_CapsLock; + case Qt::Key_Option: + return kVK_Option; + case Qt::Key_F17: + return kVK_F17; + case Qt::Key_VolumeUp: + return kVK_VolumeUp; + case Qt::Key_VolumeDown: + return kVK_VolumeDown; + case Qt::Key_F18: + return kVK_F18; + case Qt::Key_F19: + return kVK_F19; + case Qt::Key_F20: + return kVK_F20; + case Qt::Key_F5: + return kVK_F5; + case Qt::Key_F6: + return kVK_F6; + case Qt::Key_F7: + return kVK_F7; + case Qt::Key_F3: + return kVK_F3; + case Qt::Key_F8: + return kVK_F8; + case Qt::Key_F9: + return kVK_F9; + case Qt::Key_F11: + return kVK_F11; + case Qt::Key_F13: + return kVK_F13; + case Qt::Key_F16: + return kVK_F16; + case Qt::Key_F14: + return kVK_F14; + case Qt::Key_F10: + return kVK_F10; + case Qt::Key_F12: + return kVK_F12; + case Qt::Key_F15: + return kVK_F15; + case Qt::Key_Help: + return kVK_Help; + case Qt::Key_Home: + return kVK_Home; + case Qt::Key_PageUp: + return kVK_PageUp; + case Qt::Key_Delete: + return kVK_ForwardDelete; + case Qt::Key_F4: + return kVK_F4; + case Qt::Key_End: + return kVK_End; + case Qt::Key_F2: + return kVK_F2; + case Qt::Key_PageDown: + return kVK_PageDown; + case Qt::Key_F1: + return kVK_F1; + case Qt::Key_Left: + return kVK_LeftArrow; + case Qt::Key_Right: + return kVK_RightArrow; + case Qt::Key_Down: + return kVK_DownArrow; + case Qt::Key_Up: + return kVK_UpArrow; + default: + ok = false; + break; + } + + UTF16Char ch = keycode; + + CFDataRef currentLayoutData; + TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource(); + + if (currentKeyboard == NULL) + return 0; + + currentLayoutData = (CFDataRef)TISGetInputSourceProperty( + currentKeyboard, kTISPropertyUnicodeKeyLayoutData); + CFRelease(currentKeyboard); + if (currentLayoutData == NULL) + return 0; + + UCKeyboardLayout* header = + (UCKeyboardLayout*)CFDataGetBytePtr(currentLayoutData); + UCKeyboardTypeHeader* table = header->keyboardTypeList; + + uint8_t* data = (uint8_t*)header; + for (quint32 i = 0; i < header->keyboardTypeCount; i++) { + UCKeyStateRecordsIndex* stateRec = 0; + if (table[i].keyStateRecordsIndexOffset != 0) { + stateRec = reinterpret_cast( + data + table[i].keyStateRecordsIndexOffset); + if (stateRec->keyStateRecordsIndexFormat != + kUCKeyStateRecordsIndexFormat) + stateRec = 0; + } + + UCKeyToCharTableIndex* charTable = + reinterpret_cast( + data + table[i].keyToCharTableIndexOffset); + if (charTable->keyToCharTableIndexFormat != + kUCKeyToCharTableIndexFormat) + continue; + + for (quint32 j = 0; j < charTable->keyToCharTableCount; j++) { + UCKeyOutput* keyToChar = reinterpret_cast( + data + charTable->keyToCharTableOffsets[j]); + for (quint32 k = 0; k < charTable->keyToCharTableSize; k++) { + if (keyToChar[k] & kUCKeyOutputTestForIndexMask) { + long idx = keyToChar[k] & kUCKeyOutputGetIndexMask; + if (stateRec && idx < stateRec->keyStateRecordCount) { + UCKeyStateRecord* rec = + reinterpret_cast( + data + stateRec->keyStateRecordOffsets[idx]); + if (rec->stateZeroCharData == ch) { + ok = true; + return k; + } + } + } else if (!(keyToChar[k] & kUCKeyOutputSequenceIndexMask) && + keyToChar[k] < 0xFFFE) { + if (keyToChar[k] == ch) { + ok = true; + return k; + } + } + } + } + } + return 0; +} + +quint32 QHotkeyPrivateMac::nativeModifiers(Qt::KeyboardModifiers modifiers, + bool& ok) +{ + quint32 nMods = 0; + if (modifiers & Qt::ShiftModifier) + nMods |= shiftKey; + if (modifiers & Qt::ControlModifier) + nMods |= cmdKey; + if (modifiers & Qt::AltModifier) + nMods |= optionKey; + if (modifiers & Qt::MetaModifier) + nMods |= controlKey; + if (modifiers & Qt::KeypadModifier) + nMods |= kEventKeyModifierNumLockMask; + ok = true; + return nMods; +} + +bool QHotkeyPrivateMac::registerShortcut(QHotkey::NativeShortcut shortcut) +{ + if (!this->isHotkeyHandlerRegistered) { + EventTypeSpec pressEventSpec; + pressEventSpec.eventClass = kEventClassKeyboard; + pressEventSpec.eventKind = kEventHotKeyPressed; + InstallApplicationEventHandler( + &QHotkeyPrivateMac::hotkeyPressEventHandler, + 1, + &pressEventSpec, + NULL, + NULL); + + EventTypeSpec releaseEventSpec; + releaseEventSpec.eventClass = kEventClassKeyboard; + releaseEventSpec.eventKind = kEventHotKeyReleased; + InstallApplicationEventHandler( + &QHotkeyPrivateMac::hotkeyReleaseEventHandler, + 1, + &releaseEventSpec, + NULL, + NULL); + } + + EventHotKeyID hkeyID; + hkeyID.signature = shortcut.key; + hkeyID.id = shortcut.modifier; + + EventHotKeyRef eventRef = 0; + OSStatus status = RegisterEventHotKey(shortcut.key, + shortcut.modifier, + hkeyID, + GetApplicationEventTarget(), + 0, + &eventRef); + if (status != noErr) { + error = QString::number(status); + return false; + } else { + this->hotkeyRefs.insert(shortcut, eventRef); + return true; + } +} + +bool QHotkeyPrivateMac::unregisterShortcut(QHotkey::NativeShortcut shortcut) +{ + EventHotKeyRef eventRef = QHotkeyPrivateMac::hotkeyRefs.value(shortcut); + OSStatus status = UnregisterEventHotKey(eventRef); + if (status != noErr) { + error = QString::number(status); + return false; + } else { + this->hotkeyRefs.remove(shortcut); + return true; + } +} + +OSStatus QHotkeyPrivateMac::hotkeyPressEventHandler( + EventHandlerCallRef nextHandler, + EventRef event, + void* data) +{ + Q_UNUSED(nextHandler); + Q_UNUSED(data); + + if (GetEventClass(event) == kEventClassKeyboard && + GetEventKind(event) == kEventHotKeyPressed) { + EventHotKeyID hkeyID; + GetEventParameter(event, + kEventParamDirectObject, + typeEventHotKeyID, + NULL, + sizeof(EventHotKeyID), + NULL, + &hkeyID); + hotkeyPrivate->activateShortcut({ hkeyID.signature, hkeyID.id }); + } + + return noErr; +} + +OSStatus QHotkeyPrivateMac::hotkeyReleaseEventHandler( + EventHandlerCallRef nextHandler, + EventRef event, + void* data) +{ + Q_UNUSED(nextHandler); + Q_UNUSED(data); + + if (GetEventClass(event) == kEventClassKeyboard && + GetEventKind(event) == kEventHotKeyReleased) { + EventHotKeyID hkeyID; + GetEventParameter(event, + kEventParamDirectObject, + typeEventHotKeyID, + NULL, + sizeof(EventHotKeyID), + NULL, + &hkeyID); + hotkeyPrivate->releaseShortcut({ hkeyID.signature, hkeyID.id }); + } + + return noErr; +} \ No newline at end of file diff --git a/src/core/QHotkey/qhotkey_p.h b/src/core/QHotkey/qhotkey_p.h new file mode 100644 index 00000000..c82bf4d2 --- /dev/null +++ b/src/core/QHotkey/qhotkey_p.h @@ -0,0 +1,68 @@ +#ifndef QHOTKEY_P_H +#define QHOTKEY_P_H + +#include "qhotkey.h" +#include +#include +#include +#include + +class QHOTKEY_SHARED_EXPORT QHotkeyPrivate + : public QObject + , public QAbstractNativeEventFilter +{ + Q_OBJECT + +public: + QHotkeyPrivate(); // singleton!!! + ~QHotkeyPrivate(); + + static QHotkeyPrivate* instance(); + static bool isPlatformSupported(); + + QHotkey::NativeShortcut nativeShortcut(Qt::Key keycode, + Qt::KeyboardModifiers modifiers); + + bool addShortcut(QHotkey* hotkey); + bool removeShortcut(QHotkey* hotkey); + +protected: + void activateShortcut(QHotkey::NativeShortcut shortcut); + void releaseShortcut(QHotkey::NativeShortcut shortcut); + + virtual quint32 nativeKeycode(Qt::Key keycode, + bool& ok) = 0; // platform implement + virtual quint32 nativeModifiers(Qt::KeyboardModifiers modifiers, + bool& ok) = 0; // platform implement + + virtual bool registerShortcut( + QHotkey::NativeShortcut shortcut) = 0; // platform implement + virtual bool unregisterShortcut( + QHotkey::NativeShortcut shortcut) = 0; // platform implement + + QString error; + +private: + QHash, QHotkey::NativeShortcut> + mapping; + QMultiHash shortcuts; + + Q_INVOKABLE void addMappingInvoked(Qt::Key keycode, + Qt::KeyboardModifiers modifiers, + QHotkey::NativeShortcut nativeShortcut); + Q_INVOKABLE bool addShortcutInvoked(QHotkey* hotkey); + Q_INVOKABLE bool removeShortcutInvoked(QHotkey* hotkey); + Q_INVOKABLE QHotkey::NativeShortcut nativeShortcutInvoked( + Qt::Key keycode, + Qt::KeyboardModifiers modifiers); +}; + +#define NATIVE_INSTANCE(ClassName) \ + Q_GLOBAL_STATIC(ClassName, hotkeyPrivate) \ + \ + QHotkeyPrivate* QHotkeyPrivate::instance() { return hotkeyPrivate; } + +Q_DECLARE_METATYPE(Qt::Key) +Q_DECLARE_METATYPE(Qt::KeyboardModifiers) + +#endif // QHOTKEY_P_H diff --git a/src/core/QHotkey/qhotkey_win.cpp b/src/core/QHotkey/qhotkey_win.cpp new file mode 100644 index 00000000..9f1ca215 --- /dev/null +++ b/src/core/QHotkey/qhotkey_win.cpp @@ -0,0 +1,313 @@ +#include "qhotkey.h" +#include "qhotkey_p.h" +#include +#include +#include + +#define HKEY_ID(nativeShortcut) \ + (((nativeShortcut.key ^ (nativeShortcut.modifier << 8)) & 0x0FFF) | 0x7000) + +#if !defined(MOD_NOREPEAT) +#define MOD_NOREPEAT 0x4000 +#endif + +class QHotkeyPrivateWin : public QHotkeyPrivate +{ +public: + QHotkeyPrivateWin(); + // QAbstractNativeEventFilter interface + bool nativeEventFilter(const QByteArray& eventType, + void* message, + long* result) Q_DECL_OVERRIDE; + +protected: + void pollForHotkeyRelease(); + // QHotkeyPrivate interface + quint32 nativeKeycode(Qt::Key keycode, bool& ok) Q_DECL_OVERRIDE; + quint32 nativeModifiers(Qt::KeyboardModifiers modifiers, + bool& ok) Q_DECL_OVERRIDE; + bool registerShortcut(QHotkey::NativeShortcut shortcut) Q_DECL_OVERRIDE; + bool unregisterShortcut(QHotkey::NativeShortcut shortcut) Q_DECL_OVERRIDE; + +private: + static QString formatWinError(DWORD winError); + QTimer pollTimer; + QHotkey::NativeShortcut polledShortcut; +}; +NATIVE_INSTANCE(QHotkeyPrivateWin) + +QHotkeyPrivateWin::QHotkeyPrivateWin() +{ + pollTimer.setInterval(50); + connect(&pollTimer, + &QTimer::timeout, + this, + &QHotkeyPrivateWin::pollForHotkeyRelease); +} + +bool QHotkeyPrivate::isPlatformSupported() +{ + return true; +} + +bool QHotkeyPrivateWin::nativeEventFilter(const QByteArray& eventType, + void* message, + long* result) +{ + Q_UNUSED(eventType) + Q_UNUSED(result) + + MSG* msg = static_cast(message); + if (msg->message == WM_HOTKEY) { + QHotkey::NativeShortcut shortcut = { HIWORD(msg->lParam), + LOWORD(msg->lParam) }; + this->activateShortcut(shortcut); + this->polledShortcut = shortcut; + this->pollTimer.start(); + } + + return false; +} + +void QHotkeyPrivateWin::pollForHotkeyRelease() +{ + bool pressed = + (GetAsyncKeyState(this->polledShortcut.key) & (1 << 15)) != 0; + if (!pressed) { + this->pollTimer.stop(); + this->releaseShortcut(this->polledShortcut); + } +} + +quint32 QHotkeyPrivateWin::nativeKeycode(Qt::Key keycode, bool& ok) +{ + ok = true; + if (keycode <= 0xFFFF) { // Try to obtain the key from it's "character" + const SHORT vKey = VkKeyScanW(static_cast(keycode)); + if (vKey > -1) + return LOBYTE(vKey); + } + + // find key from switch/case --> Only finds a very small subset of keys + switch (keycode) { + case Qt::Key_Escape: + return VK_ESCAPE; + case Qt::Key_Tab: + case Qt::Key_Backtab: + return VK_TAB; + case Qt::Key_Backspace: + return VK_BACK; + case Qt::Key_Return: + case Qt::Key_Enter: + return VK_RETURN; + case Qt::Key_Insert: + return VK_INSERT; + case Qt::Key_Delete: + return VK_DELETE; + case Qt::Key_Pause: + return VK_PAUSE; + case Qt::Key_Print: + return VK_PRINT; + case Qt::Key_Clear: + return VK_CLEAR; + case Qt::Key_Home: + return VK_HOME; + case Qt::Key_End: + return VK_END; + case Qt::Key_Left: + return VK_LEFT; + case Qt::Key_Up: + return VK_UP; + case Qt::Key_Right: + return VK_RIGHT; + case Qt::Key_Down: + return VK_DOWN; + case Qt::Key_PageUp: + return VK_PRIOR; + case Qt::Key_PageDown: + return VK_NEXT; + case Qt::Key_CapsLock: + return VK_CAPITAL; + case Qt::Key_NumLock: + return VK_NUMLOCK; + case Qt::Key_ScrollLock: + return VK_SCROLL; + + case Qt::Key_F1: + return VK_F1; + case Qt::Key_F2: + return VK_F2; + case Qt::Key_F3: + return VK_F3; + case Qt::Key_F4: + return VK_F4; + case Qt::Key_F5: + return VK_F5; + case Qt::Key_F6: + return VK_F6; + case Qt::Key_F7: + return VK_F7; + case Qt::Key_F8: + return VK_F8; + case Qt::Key_F9: + return VK_F9; + case Qt::Key_F10: + return VK_F10; + case Qt::Key_F11: + return VK_F11; + case Qt::Key_F12: + return VK_F12; + case Qt::Key_F13: + return VK_F13; + case Qt::Key_F14: + return VK_F14; + case Qt::Key_F15: + return VK_F15; + case Qt::Key_F16: + return VK_F16; + case Qt::Key_F17: + return VK_F17; + case Qt::Key_F18: + return VK_F18; + case Qt::Key_F19: + return VK_F19; + case Qt::Key_F20: + return VK_F20; + case Qt::Key_F21: + return VK_F21; + case Qt::Key_F22: + return VK_F22; + case Qt::Key_F23: + return VK_F23; + case Qt::Key_F24: + return VK_F24; + + case Qt::Key_Menu: + return VK_APPS; + case Qt::Key_Help: + return VK_HELP; + case Qt::Key_MediaNext: + return VK_MEDIA_NEXT_TRACK; + case Qt::Key_MediaPrevious: + return VK_MEDIA_PREV_TRACK; + case Qt::Key_MediaPlay: + return VK_MEDIA_PLAY_PAUSE; + case Qt::Key_MediaStop: + return VK_MEDIA_STOP; + case Qt::Key_VolumeDown: + return VK_VOLUME_DOWN; + case Qt::Key_VolumeUp: + return VK_VOLUME_UP; + case Qt::Key_VolumeMute: + return VK_VOLUME_MUTE; + case Qt::Key_Mode_switch: + return VK_MODECHANGE; + case Qt::Key_Select: + return VK_SELECT; + case Qt::Key_Printer: + return VK_PRINT; + case Qt::Key_Execute: + return VK_EXECUTE; + case Qt::Key_Sleep: + return VK_SLEEP; + case Qt::Key_Period: + return VK_DECIMAL; + case Qt::Key_Play: + return VK_PLAY; + case Qt::Key_Cancel: + return VK_CANCEL; + + case Qt::Key_Forward: + return VK_BROWSER_FORWARD; + case Qt::Key_Refresh: + return VK_BROWSER_REFRESH; + case Qt::Key_Stop: + return VK_BROWSER_STOP; + case Qt::Key_Search: + return VK_BROWSER_SEARCH; + case Qt::Key_Favorites: + return VK_BROWSER_FAVORITES; + case Qt::Key_HomePage: + return VK_BROWSER_HOME; + + case Qt::Key_LaunchMail: + return VK_LAUNCH_MAIL; + case Qt::Key_LaunchMedia: + return VK_LAUNCH_MEDIA_SELECT; + case Qt::Key_Launch0: + return VK_LAUNCH_APP1; + case Qt::Key_Launch1: + return VK_LAUNCH_APP2; + + case Qt::Key_Massyo: + return VK_OEM_FJ_MASSHOU; + case Qt::Key_Touroku: + return VK_OEM_FJ_TOUROKU; + + default: + if (keycode <= 0xFFFF) + return (byte)keycode; + else { + ok = false; + return 0; + } + } +} + +quint32 QHotkeyPrivateWin::nativeModifiers(Qt::KeyboardModifiers modifiers, + bool& ok) +{ + quint32 nMods = 0; + if (modifiers & Qt::ShiftModifier) + nMods |= MOD_SHIFT; + if (modifiers & Qt::ControlModifier) + nMods |= MOD_CONTROL; + if (modifiers & Qt::AltModifier) + nMods |= MOD_ALT; + if (modifiers & Qt::MetaModifier) + nMods |= MOD_WIN; + ok = true; + return nMods; +} + +bool QHotkeyPrivateWin::registerShortcut(QHotkey::NativeShortcut shortcut) +{ + BOOL ok = RegisterHotKey( + NULL, HKEY_ID(shortcut), shortcut.modifier + MOD_NOREPEAT, shortcut.key); + if (ok) + return true; + else { + error = QHotkeyPrivateWin::formatWinError(::GetLastError()); + return false; + } +} + +bool QHotkeyPrivateWin::unregisterShortcut(QHotkey::NativeShortcut shortcut) +{ + BOOL ok = UnregisterHotKey(NULL, HKEY_ID(shortcut)); + if (ok) + return true; + else { + error = QHotkeyPrivateWin::formatWinError(::GetLastError()); + return false; + } +} + +QString QHotkeyPrivateWin::formatWinError(DWORD winError) +{ + wchar_t* buffer = NULL; + DWORD num = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + winError, + 0, + (LPWSTR)&buffer, + 0, + NULL); + if (buffer) { + QString res = QString::fromWCharArray(buffer, num); + LocalFree(buffer); + return res; + } else + return QString(); +} diff --git a/src/core/QHotkey/qhotkey_x11.cpp b/src/core/QHotkey/qhotkey_x11.cpp new file mode 100644 index 00000000..acc0f26d --- /dev/null +++ b/src/core/QHotkey/qhotkey_x11.cpp @@ -0,0 +1,259 @@ +#include "qhotkey.h" +#include "qhotkey_p.h" +#include +#include +#include +#include +#include +#include + +// compability to pre Qt 5.8 +#ifndef Q_FALLTHROUGH +#define Q_FALLTHROUGH() (void)0 +#endif + +class QHotkeyPrivateX11 : public QHotkeyPrivate +{ +public: + // QAbstractNativeEventFilter interface + bool nativeEventFilter(const QByteArray& eventType, + void* message, + long* result) Q_DECL_OVERRIDE; + +protected: + // QHotkeyPrivate interface + quint32 nativeKeycode(Qt::Key keycode, bool& ok) Q_DECL_OVERRIDE; + quint32 nativeModifiers(Qt::KeyboardModifiers modifiers, + bool& ok) Q_DECL_OVERRIDE; + static QString getX11String(Qt::Key keycode); + bool registerShortcut(QHotkey::NativeShortcut shortcut) Q_DECL_OVERRIDE; + bool unregisterShortcut(QHotkey::NativeShortcut shortcut) Q_DECL_OVERRIDE; + +private: + static const QVector specialModifiers; + static const quint32 validModsMask; + xcb_key_press_event_t prevHandledEvent; + xcb_key_press_event_t prevEvent; + + static QString formatX11Error(Display* display, int errorCode); + + class HotkeyErrorHandler + { + public: + HotkeyErrorHandler(); + ~HotkeyErrorHandler(); + + static bool hasError; + static QString errorString; + + private: + XErrorHandler prevHandler; + + static int handleError(Display* display, XErrorEvent* error); + }; +}; +NATIVE_INSTANCE(QHotkeyPrivateX11) + +bool QHotkeyPrivate::isPlatformSupported() +{ + return QX11Info::isPlatformX11(); +} + +const QVector QHotkeyPrivateX11::specialModifiers = { 0, + Mod2Mask, + LockMask, + (Mod2Mask | + LockMask) }; +const quint32 QHotkeyPrivateX11::validModsMask = + ShiftMask | ControlMask | Mod1Mask | Mod4Mask; + +bool QHotkeyPrivateX11::nativeEventFilter(const QByteArray& eventType, + void* message, + long* result) +{ + Q_UNUSED(eventType) + Q_UNUSED(result) + + auto* genericEvent = static_cast(message); + if (genericEvent->response_type == XCB_KEY_PRESS) { + xcb_key_press_event_t keyEvent = + *static_cast(message); + this->prevEvent = keyEvent; + if (this->prevHandledEvent.response_type == XCB_KEY_RELEASE) { + if (this->prevHandledEvent.time == keyEvent.time) + return false; + } + this->activateShortcut( + { keyEvent.detail, + keyEvent.state & QHotkeyPrivateX11::validModsMask }); + } else if (genericEvent->response_type == XCB_KEY_RELEASE) { + xcb_key_release_event_t keyEvent = + *static_cast(message); + this->prevEvent = keyEvent; + QTimer::singleShot(50, [this, keyEvent] { + if (this->prevEvent.time == keyEvent.time && + this->prevEvent.response_type == keyEvent.response_type && + this->prevEvent.detail == keyEvent.detail) { + this->releaseShortcut( + { keyEvent.detail, + keyEvent.state & QHotkeyPrivateX11::validModsMask }); + } + }); + this->prevHandledEvent = keyEvent; + } + + return false; +} + +QString QHotkeyPrivateX11::getX11String(Qt::Key keycode) +{ + switch (keycode) { + + case Qt::Key_MediaLast: + case Qt::Key_MediaPrevious: + return QStringLiteral("XF86AudioPrev"); + case Qt::Key_MediaNext: + return QStringLiteral("XF86AudioNext"); + case Qt::Key_MediaPause: + case Qt::Key_MediaPlay: + case Qt::Key_MediaTogglePlayPause: + return QStringLiteral("XF86AudioPlay"); + case Qt::Key_MediaRecord: + return QStringLiteral("XF86AudioRecord"); + case Qt::Key_MediaStop: + return QStringLiteral("XF86AudioStop"); + default: + return QKeySequence(keycode).toString(QKeySequence::NativeText); + } +} + +quint32 QHotkeyPrivateX11::nativeKeycode(Qt::Key keycode, bool& ok) +{ + QString keyString = getX11String(keycode); + + KeySym keysym = XStringToKeysym(keyString.toLatin1().constData()); + if (keysym == NoSymbol) { + // not found -> just use the key + if (keycode <= 0xFFFF) + keysym = keycode; + else + return 0; + } + + if (QX11Info::isPlatformX11()) { + auto res = XKeysymToKeycode(QX11Info::display(), keysym); + if (res != 0) + ok = true; + return res; + } + return 0; +} + +quint32 QHotkeyPrivateX11::nativeModifiers(Qt::KeyboardModifiers modifiers, + bool& ok) +{ + quint32 nMods = 0; + if (modifiers & Qt::ShiftModifier) + nMods |= ShiftMask; + if (modifiers & Qt::ControlModifier) + nMods |= ControlMask; + if (modifiers & Qt::AltModifier) + nMods |= Mod1Mask; + if (modifiers & Qt::MetaModifier) + nMods |= Mod4Mask; + ok = true; + return nMods; +} + +bool QHotkeyPrivateX11::registerShortcut(QHotkey::NativeShortcut shortcut) +{ + Display* display = QX11Info::display(); + if (!display || !QX11Info::isPlatformX11()) + return false; + + HotkeyErrorHandler errorHandler; + for (quint32 specialMod : QHotkeyPrivateX11::specialModifiers) { + XGrabKey(display, + shortcut.key, + shortcut.modifier | specialMod, + DefaultRootWindow(display), + True, + GrabModeAsync, + GrabModeAsync); + } + XSync(display, False); + + if (errorHandler.hasError) { + error = errorHandler.errorString; + this->unregisterShortcut(shortcut); + return false; + } + return true; +} + +bool QHotkeyPrivateX11::unregisterShortcut(QHotkey::NativeShortcut shortcut) +{ + Display* display = QX11Info::display(); + if (!display) + return false; + + HotkeyErrorHandler errorHandler; + for (quint32 specialMod : QHotkeyPrivateX11::specialModifiers) { + XUngrabKey(display, + shortcut.key, + shortcut.modifier | specialMod, + XDefaultRootWindow(display)); + } + XSync(display, False); + + if (HotkeyErrorHandler::hasError) { + error = HotkeyErrorHandler::errorString; + return false; + } + return true; +} + +QString QHotkeyPrivateX11::formatX11Error(Display* display, int errorCode) +{ + char errStr[256]; + XGetErrorText(display, errorCode, errStr, 256); + return QString::fromLatin1(errStr); +} + +// ---------- QHotkeyPrivateX11::HotkeyErrorHandler implementation ---------- + +bool QHotkeyPrivateX11::HotkeyErrorHandler::hasError = false; +QString QHotkeyPrivateX11::HotkeyErrorHandler::errorString; + +QHotkeyPrivateX11::HotkeyErrorHandler::HotkeyErrorHandler() +{ + prevHandler = XSetErrorHandler(&HotkeyErrorHandler::handleError); +} + +QHotkeyPrivateX11::HotkeyErrorHandler::~HotkeyErrorHandler() +{ + XSetErrorHandler(prevHandler); + hasError = false; + errorString.clear(); +} + +int QHotkeyPrivateX11::HotkeyErrorHandler::handleError(Display* display, + XErrorEvent* error) +{ + switch (error->error_code) { + case BadAccess: + case BadValue: + case BadWindow: + if (error->request_code == 33 || // grab key + error->request_code == 34) { // ungrab key + hasError = true; + errorString = + QHotkeyPrivateX11::formatX11Error(display, error->error_code); + return 1; + } + Q_FALLTHROUGH(); + // fall through + default: + return 0; + } +} diff --git a/src/core/controller.cpp b/src/core/controller.cpp index 74e99971..b0a24dea 100644 --- a/src/core/controller.cpp +++ b/src/core/controller.cpp @@ -17,6 +17,7 @@ #include "controller.h" #include "src/config/configwindow.h" +#include "src/core/QHotkey/QHotkey" #include "src/utils/confighandler.h" #include "src/utils/history.h" #include "src/utils/screengrabber.h" @@ -59,6 +60,11 @@ Controller::Controller() , m_trayIconMenu(nullptr) , m_networkCheckUpdates(nullptr) , m_showCheckAppUpdateStatus(false) +#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) + , m_HotkeyScreenshotCapture(nullptr) + , m_HotkeyScreenshotHistory(nullptr) +#endif { m_appLatestVersion = QStringLiteral(APP_VERSION).replace("v", ""); qApp->setQuitOnLastWindowClosed(false); @@ -91,6 +97,20 @@ Controller::Controller() // CaptureWidget QScreen* currentScreen = QGuiApplication::screenAt(QCursor::pos()); currentScreen->grabWindow(QApplication::desktop()->winId(), 0, 0, 1, 1); + + // set global shortcuts for MacOS + m_HotkeyScreenshotCapture = + new QHotkey(QKeySequence("Ctrl+Alt+Shift+4"), true, this); + QObject::connect(m_HotkeyScreenshotCapture, + &QHotkey::activated, + qApp, + [&]() { this->startVisualCapture(); }); + m_HotkeyScreenshotHistory = + new QHotkey(QKeySequence("Ctrl+Alt+Shift+H"), true, this); + QObject::connect(m_HotkeyScreenshotHistory, + &QHotkey::activated, + qApp, + [&]() { this->showRecentScreenshots(); }); #endif getLatestAvailableVersion(); } diff --git a/src/core/controller.h b/src/core/controller.h index 1baf8ba6..e6524395 100644 --- a/src/core/controller.h +++ b/src/core/controller.h @@ -34,6 +34,10 @@ class CaptureLauncher; class HistoryWidget; class QNetworkAccessManager; class QNetworkReply; +#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) +class QHotkey; +#endif using lambda = std::function; class Controller : public QObject @@ -108,4 +112,9 @@ private: QMenu* m_trayIconMenu; QNetworkAccessManager* m_networkCheckUpdates; +#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) + QHotkey* m_HotkeyScreenshotCapture; + QHotkey* m_HotkeyScreenshotHistory; +#endif }; diff --git a/src/utils/configshortcuts.cpp b/src/utils/configshortcuts.cpp index 432ae60c..323a057d 100644 --- a/src/utils/configshortcuts.cpp +++ b/src/utils/configshortcuts.cpp @@ -54,8 +54,13 @@ const QVector& ConfigShortcuts::captureShortcutsDefault( m_shortcuts << (QStringList() << "" << QObject::tr("Quit capture") << QKeySequence(Qt::Key_Escape).toString()); -#if not(defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ - defined(Q_OS_MACX)) +#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) + m_shortcuts << (QStringList() + << "" << QObject::tr("Screenshot history") << "⇧⌘⌥H"); + m_shortcuts << (QStringList() + << "" << QObject::tr("Capture screen") << "⇧⌘⌥4"); +#else m_shortcuts << (QStringList() << "" << QObject::tr("Screenshot history") << "Shift+Print Screen"); m_shortcuts << (QStringList() From f735a9106ded484adc6c4c2fc919b91873a00ad3 Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Thu, 17 Dec 2020 13:11:29 +0200 Subject: [PATCH 47/54] MacOS - Global shortcuts (add QHotkey license and remove unused files) --- src/core/QHotkey/LICENSE | 27 +++++++++ src/core/QHotkey/QHotkey.pro | 16 ------ src/core/QHotkey/README.md | 104 +++++++++++++++++++++++++++++++++++ src/core/QHotkey/qhotkey.pri | 1 - 4 files changed, 131 insertions(+), 17 deletions(-) create mode 100644 src/core/QHotkey/LICENSE delete mode 100644 src/core/QHotkey/QHotkey.pro create mode 100644 src/core/QHotkey/README.md delete mode 100644 src/core/QHotkey/qhotkey.pri diff --git a/src/core/QHotkey/LICENSE b/src/core/QHotkey/LICENSE new file mode 100644 index 00000000..55e0b00a --- /dev/null +++ b/src/core/QHotkey/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2016, Felix Barz +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of QHotkey nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/core/QHotkey/QHotkey.pro b/src/core/QHotkey/QHotkey.pro deleted file mode 100644 index 44e937bf..00000000 --- a/src/core/QHotkey/QHotkey.pro +++ /dev/null @@ -1,16 +0,0 @@ -TEMPLATE = lib -win32: CONFIG += dll - -TARGET = QHotkey -VERSION = 1.2.1 - -include(../qhotkey.pri) - -DEFINES += QHOTKEY_LIB QHOTKEY_LIB_BUILD - -# use INSTALL_ROOT to modify the install location -headers.files = $$PUBLIC_HEADERS -headers.path = $$[QT_INSTALL_HEADERS] -target.path = $$[QT_INSTALL_LIBS] -INSTALLS += target headers - diff --git a/src/core/QHotkey/README.md b/src/core/QHotkey/README.md new file mode 100644 index 00000000..e4f3f787 --- /dev/null +++ b/src/core/QHotkey/README.md @@ -0,0 +1,104 @@ +# QHotkey +A global shortcut/hotkey for Desktop Qt-Applications. + +The QHotkey is a class that can be used to create hotkeys/global shortcuts, aka shortcuts that work everywhere, independent of the application state. This means your application can be active, inactive, minimized or not visible at all and still receive the shortcuts. + +## Features +- Works on Windows, Mac and X11 +- Easy to use, can use `QKeySequence` for easy shortcut input +- Supports almost all common keys (Depends on OS & Keyboard-Layout) +- Allows direct input of Key/Modifier-Combinations +- Supports multiple QHotkey-instances for the same shortcut (with optimisations) +- Thread-Safe - Can be used on all threads (See section Thread safety) +- Allows usage of native keycodes and modifiers, if needed + +**Note:** For now Wayland is not supported, as it is simply not possible to register a global shortcut with wayland. For more details, or possible Ideas on how to get Hotkeys working on wayland, see [Issue #14](https://github.com/Skycoder42/QHotkey/issues/14). + +## Installation +The package is providet as qpm package, [`de.skycoder42.qhotkey`](https://www.qpm.io/packages/de.skycoder42.qhotkey/index.html). You can install it either via qpmx (preferred) or directly via qpm. + +### Via qpmx +[qpmx](https://github.com/Skycoder42/qpmx) is a frontend for qpm (and other tools) with additional features, and is the preferred way to install packages. To use it: + +1. Install qpmx (See [GitHub - Installation](https://github.com/Skycoder42/qpmx#installation)) +2. Install qpm (See [GitHub - Installing](https://github.com/Cutehacks/qpm/blob/master/README.md#installing), for **windows** see below) +3. In your projects root directory, run `qpmx install de.skycoder42.qhotkey` + +### Via qpm + +1. Install qpm (See [GitHub - Installing](https://github.com/Cutehacks/qpm/blob/master/README.md#installing), for **windows** see below) +2. In your projects root directory, run `qpm install de.skycoder42.qhotkey` +3. Include qpm to your project by adding `include(vendor/vendor.pri)` to your `.pro` file + +Check their [GitHub - Usage for App Developers](https://github.com/Cutehacks/qpm/blob/master/README.md#usage-for-app-developers) to learn more about qpm. + +**Important for Windows users:** QPM Version *0.10.0* (the one you can download on the website) is currently broken on windows! It's already fixed in master, but not released yet. Until a newer versions gets released, you can download the latest dev build from here: +- https://storage.googleapis.com/www.qpm.io/download/latest/windows_amd64/qpm.exe +- https://storage.googleapis.com/www.qpm.io/download/latest/windows_386/qpm.exe + +## Usage +The general usage is to create QHotkey instances for specific hotkeys, register them and then simply connect to the signal emitted once the hotkey is pressed. + +### Example +The following example shows a simple application that will run without a window in the background until you press the key-combination Ctrl+Alt+Q (++Q on Mac). This will quit the application. The debug output will tell if the hotkey was successfully registered and that it was pressed. +```cpp +#include +#include +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + + auto hotkey = new QHotkey(QKeySequence("ctrl+alt+Q"), true, &a);//The hotkey will be automatically registered + qDebug() << "Is Registered: " << hotkey->isRegistered(); + + QObject::connect(hotkey, &QHotkey::activated, qApp, [&](){ + qDebug() << "Hotkey Activated - the application will quit now"; + qApp->quit(); + }); + + return a.exec(); +} +``` + +**Note:** You need the .pri include for this to work. + +### Testing +By running the example in `./HotkeyTest` you can test out the QHotkey class. There are 4 sections: +- **Playground:** You can enter some sequences here and try it out with different key combinations. +- **Testings:** A list of selected hotkeys. Activate it and try out which ones work for you (*Hint:* Depending on OS and keyboard layout, it's very possible that a few don't work). +- **Threading:** Activate the checkbox to move 2 Hotkeys of the playground to seperate threads. It should work without a difference. +- **Native Shortcut**: Allows you to try out the direct usage of native shortcuts + +### Logging +By default, QHotkey prints some warning messages if something goes wrong (For example, a key that cannot be translated). All messages of QHotkey are grouped into the [QLoggingCategory](https://doc.qt.io/qt-5/qloggingcategory.html) `"QHotkey"`. If you want to simply disable the logging, call the folling function somewhere in your code: +```cpp +QLoggingCategory::setFilterRules(QStringLiteral("QHotkey.warning=false")); +``` +This will turn all warnings of QHotkey of (It only uses warnings for now, thats why this is enough). For more information about all the things you can do with the logging categories, check the Qt-Documentation + +## Thread saftey +The QHotkey class itself is reentrant - wich means you can create as many instances as required on any thread. This allows you to use the QHotkey on all threads. **But** you should never use the QHotkey instance on a thread that is different from the one the instance belongs to! Internally the system uses a singleton instance that handles the hotkey events and distributes them to the QHotkey instances. This internal class is completley threadsafe. + +However, this singleton instance only runs on the main thread. (One reason is that some of the OS-Functions are not thread safe). To make threaded hotkeys possible, the critical functions (registering/unregistering hotkeys and keytranslation) are all run on the mainthread too. The QHotkey instances on other threads use `QMetaObject::invokeMethod` with a `Qt::BlockingQueuedConnection`. + +For you this means: QHotkey instances on other threads than the main thread may take a little longer to register/unregister/translate hotkeys, because they have to wait for the main thread to do this for them. **Important:** there is however, one additional limitation that comes with that feature: QHotkey instances on other threads but the main thread *must* be unregistered or destroyed *before* the main eventloop ends. Otherwise, your application will hangup on destruction of the hotkey. This limitation does not apply for instances on the main thread. Furthermore, the same happens if you change the shortcut or register/unregister before the loop started, until it actually starts. + +## Documentation +The documentation is available as release and on [github pages](https://skycoder42.github.io/QHotkey/). + +The documentation was created using [doxygen](http://www.doxygen.org). It includes an HTML-documentation and Qt-Help files that can be included into QtCreator (QtAssistant) to show F1-Help (See [Adding External Documentation](https://doc.qt.io/qtcreator/creator-help.html#adding-external-documentation) for more details). + +## Technical +### Requirements + - Explicit support is only given down to the latest Qt LTS, but may work with earlier versions, too + - At least the QtGui-Module (a QGuiApplication). Hotkeys on console based applications are not supported (By the operating systems). You can however create a gui application without any visible window. + - C++11 + +### Known Limitations + - Only single key/modifier combinations are possible. If using QKeySequence, only the first key+modifier of the sequence will be used. + - Qt::Key makes no difference between normal numbers and the Numpad numbers. Most keyboards however require this. Thus, you can't register shortcuts for the numpad, unless you use a native shortcut. + - Supports not all keys, but most of the common ones. There are differences between platforms and it depends on the Keyboard-Layout. "Delete", for example, works on windows and mac, but not on X11 (At least on my test machines). I tried to use OS-Functions where possible, but since the Qt::Key values need to be converted into native keys, there are some limitations. I can use need such a key, try using the native shortcut. + - The registered keys will be "taken" by QHotkey. This means after a hotkey was cosumend by your application, it will not be sent to the active application. This is done this way by the operating systems and cannot be changed. +- If you get a `QHotkey: Failed to register hotkey. Error: BadAccess (attempt to access private resource denied)` error on X11, this means you are trying to register a hotkey that is private to X11. Those keys simply cannot be registered using the normal API diff --git a/src/core/QHotkey/qhotkey.pri b/src/core/QHotkey/qhotkey.pri deleted file mode 100644 index a7c97250..00000000 --- a/src/core/QHotkey/qhotkey.pri +++ /dev/null @@ -1 +0,0 @@ -message(The pri file has been moved one directory up! use that one instead) From 6300672828d88e3bb4dbe15631c9b38bd4425503 Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Thu, 17 Dec 2020 15:02:22 +0200 Subject: [PATCH 48/54] fix - MacOS - no notification in tray on image upload, save to clipboard and other notifications --- src/main.cpp | 6 ++++-- src/utils/systemnotification.cpp | 26 +++++++++++--------------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 2d44f540..deeb0363 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -76,7 +76,8 @@ int main(int argc, char* argv[]) app.setOrganizationName(QStringLiteral("flameshot")); auto c = Controller::getInstance(); -#if defined(Q_OS_LINUX) || defined(Q_OS_UNIX) +#if not(defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX) || defined(Q_OS_WIN)) new FlameshotDBusAdapter(c); QDBusConnection dbus = QDBusConnection::sessionBus(); if (!dbus.isConnected()) { @@ -92,7 +93,8 @@ int main(int argc, char* argv[]) return app.exec(); } -#ifndef Q_OS_WIN +#if not(defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX) || defined(Q_OS_WIN)) /*--------------| * CLI parsing | * ------------*/ diff --git a/src/utils/systemnotification.cpp b/src/utils/systemnotification.cpp index 3df103af..2e3e62c5 100644 --- a/src/utils/systemnotification.cpp +++ b/src/utils/systemnotification.cpp @@ -1,34 +1,30 @@ #include "systemnotification.h" +#include "src/core/controller.h" #include "src/utils/confighandler.h" #include #include -#ifndef Q_OS_WIN +#if not(defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX) || defined(Q_OS_WIN)) #include #include #include -#else #endif -#include "src/core/controller.h" -#if defined(Q_OS_LINUX) || defined(Q_OS_UNIX) SystemNotification::SystemNotification(QObject* parent) : QObject(parent) + , m_interface(nullptr) { +#if not(defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX) || defined(Q_OS_WIN)) m_interface = new QDBusInterface(QStringLiteral("org.freedesktop.Notifications"), QStringLiteral("/org/freedesktop/Notifications"), QStringLiteral("org.freedesktop.Notifications"), QDBusConnection::sessionBus(), this); -} -#else -SystemNotification::SystemNotification(QObject* parent) - : QObject(parent) -{ - m_interface = nullptr; -} #endif +} void SystemNotification::sendMessage(const QString& text, const QString& savePath) @@ -45,7 +41,10 @@ void SystemNotification::sendMessage(const QString& text, return; } -#ifndef Q_OS_WIN +#if defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX) || defined(Q_OS_WIN) + Controller::getInstance()->sendTrayNotification(text, title, timeout); +#else QList args; QVariantMap hintsMap; if (!savePath.isEmpty()) { @@ -64,8 +63,5 @@ void SystemNotification::sendMessage(const QString& text, << timeout; // timeout m_interface->callWithArgumentList( QDBus::AutoDetect, QStringLiteral("Notify"), args); -#else - auto c = Controller::getInstance(); - c->sendTrayNotification(text, title, timeout); #endif } From d050e474d7447293bcfacb486c41bd9a1d695880 Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Thu, 17 Dec 2020 19:25:29 +0200 Subject: [PATCH 49/54] Add retry option on upload to s3 fail --- src/tools/storage/s3/imgs3uploader.cpp | 148 ++++++++++++++++--------- src/tools/storage/s3/imgs3uploader.h | 3 +- 2 files changed, 96 insertions(+), 55 deletions(-) diff --git a/src/tools/storage/s3/imgs3uploader.cpp b/src/tools/storage/s3/imgs3uploader.cpp index d5f3de8a..e9503607 100644 --- a/src/tools/storage/s3/imgs3uploader.cpp +++ b/src/tools/storage/s3/imgs3uploader.cpp @@ -42,6 +42,11 @@ #include #include #include +#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) +#include "src/widgets/capture/capturewidget.h" +#include +#endif ImgS3Uploader::ImgS3Uploader(const QPixmap& capture, QWidget* parent) : ImgUploader(capture, parent) @@ -58,10 +63,24 @@ ImgS3Uploader::ImgS3Uploader(QWidget* parent) ImgS3Uploader::~ImgS3Uploader() { clearProxy(); - cleanNetworkAccessManagers(); if (nullptr != m_networkAMConfig) { delete m_networkAMConfig; } + if (nullptr != m_networkAMUpload) { + m_networkAMUpload->disconnect(); + delete m_networkAMUpload; + } + if (nullptr != m_networkAMGetCreds) { + m_networkAMGetCreds->disconnect(); + delete m_networkAMGetCreds; + } + if (nullptr != m_networkAMRemove) { + m_networkAMRemove->disconnect(); + delete m_networkAMRemove; + } + if (nullptr != m_multiPart) { + delete m_multiPart; + } } void ImgS3Uploader::init(const QString& title, const QString& label) @@ -117,14 +136,25 @@ void ImgS3Uploader::handleReplyPostUpload(QNetworkReply* reply) onUploadOk(); } } else { - QString reason = - reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute) - .toString(); - setInfoLabelText(reply->errorString()); + onUploadError(reply); } new QShortcut(Qt::Key_Escape, this, SLOT(close())); } +void ImgS3Uploader::onUploadError(QNetworkReply* reply) +{ + hideSpinner(); + if (QMessageBox::Retry == + QMessageBox::question(nullptr, + tr("Error"), + tr("Unable to upload screenshot, please check " + "your internet connection and try again"), + QMessageBox::Retry | QMessageBox::Cancel)) { + upload(); + return; + } +} + void ImgS3Uploader::handleReplyDeleteResource(QNetworkReply* reply) { auto replyError = reply->error(); @@ -141,7 +171,6 @@ void ImgS3Uploader::handleReplyDeleteResource(QNetworkReply* reply) } else if (replyError == QNetworkReply::UnknownServerError) { message += "\n" + tr("Possibly it doesn't exist anymore"); } - message += "\n\n" + reply->errorString(); message += "\n\n" + tr("Do you want to remove screenshot from local history anyway?"); @@ -167,7 +196,8 @@ void ImgS3Uploader::handleReplyGetCreds(QNetworkReply* reply) setInfoLabelText( tr("S3 Creds URL is not found in your configuration file")); } else { - setInfoLabelText(reply->errorString()); + onUploadError(reply); + return; } // FIXME - remove not uploaded preview } @@ -176,6 +206,19 @@ void ImgS3Uploader::handleReplyGetCreds(QNetworkReply* reply) void ImgS3Uploader::uploadToS3(QJsonDocument& response) { + if (nullptr == m_networkAMUpload) { + m_networkAMUpload = new QNetworkAccessManager(this); + connect(m_networkAMUpload, + &QNetworkAccessManager::finished, + this, + &ImgS3Uploader::handleReplyPostUpload); + } + if (proxy() != nullptr) { + m_networkAMUpload->setProxy(*proxy()); + } else { + m_networkAMUpload->setProxy(QNetworkProxy()); + } + // set parameters from "fields" if (nullptr != m_multiPart) { delete m_multiPart; @@ -229,17 +272,17 @@ void ImgS3Uploader::deleteResource(const QString& fileName, // read network settings on each call to simplify configuration management // without restarting clearProxy(); - if (m_networkAMRemove != nullptr) { - delete m_networkAMRemove; - m_networkAMRemove = nullptr; + if (m_networkAMRemove == nullptr) { + m_networkAMRemove = new QNetworkAccessManager(this); + connect(m_networkAMRemove, + &QNetworkAccessManager::finished, + this, + &ImgS3Uploader::handleReplyDeleteResource); } - m_networkAMRemove = new QNetworkAccessManager(this); - connect(m_networkAMRemove, - &QNetworkAccessManager::finished, - this, - &ImgS3Uploader::handleReplyDeleteResource); if (proxy() != nullptr) { m_networkAMRemove->setProxy(*proxy()); + } else { + m_networkAMRemove->setProxy(QNetworkProxy()); } QNetworkRequest request; @@ -271,29 +314,27 @@ void ImgS3Uploader::upload() dtCredsUpdated = dtCredsUpdated.addDays(1); if (m_s3Settings.credsUrl().isEmpty() || dtCredsUpdated <= now) { getConfigRemote(); - return; + if (m_s3Settings.credsUrl().isEmpty()) { + // no creds, even outdated, need to get creds first, cannot continue + return; + } } // clean old network connections and start uploading - cleanNetworkAccessManagers(); - - m_networkAMGetCreds = new QNetworkAccessManager(this); - connect(m_networkAMGetCreds, - &QNetworkAccessManager::finished, - this, - &ImgS3Uploader::handleReplyGetCreds); - - m_networkAMUpload = new QNetworkAccessManager(this); - connect(m_networkAMUpload, - &QNetworkAccessManager::finished, - this, - &ImgS3Uploader::handleReplyPostUpload); + if (nullptr == m_networkAMGetCreds) { + m_networkAMGetCreds = new QNetworkAccessManager(this); + connect(m_networkAMGetCreds, + &QNetworkAccessManager::finished, + this, + &ImgS3Uploader::handleReplyGetCreds); + } if (proxy() != nullptr) { m_networkAMGetCreds->setProxy(*proxy()); - m_networkAMUpload->setProxy(*proxy()); + } else { + m_networkAMGetCreds->setProxy(QNetworkProxy()); } - // get creads + // get creds QNetworkRequest requestCreds(QUrl(m_s3Settings.credsUrl())); if (m_s3Settings.xApiKey().length() > 0) { requestCreds.setRawHeader( @@ -301,6 +342,19 @@ void ImgS3Uploader::upload() QByteArray(m_s3Settings.xApiKey().toLocal8Bit())); } m_networkAMGetCreds->get(requestCreds); +#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) + // Hide capture widget on MacOS + for (QWidget* widget : qApp->topLevelWidgets()) { + QString className(widget->metaObject()->className()); + if (0 == + className.compare(CaptureWidget::staticMetaObject.className())) { + widget->showNormal(); + widget->hide(); + break; + } + } +#endif } void ImgS3Uploader::removeImagePreview() @@ -320,26 +374,6 @@ void ImgS3Uploader::removeImagePreview() resultStatus = true; } -void ImgS3Uploader::cleanNetworkAccessManagers() -{ - if (nullptr != m_networkAMUpload) { - delete m_networkAMUpload; - m_networkAMUpload = nullptr; - } - if (nullptr != m_networkAMGetCreds) { - delete m_networkAMGetCreds; - m_networkAMGetCreds = nullptr; - } - if (nullptr != m_networkAMRemove) { - delete m_networkAMRemove; - m_networkAMRemove = nullptr; - } - if (nullptr != m_multiPart) { - delete m_multiPart; - m_multiPart = nullptr; - } -} - void ImgS3Uploader::getConfigRemote() { if (nullptr == m_networkAMConfig) { @@ -348,9 +382,11 @@ void ImgS3Uploader::getConfigRemote() &QNetworkAccessManager::finished, this, &ImgS3Uploader::handleReplyGetConfig); - if (proxy() != nullptr) { - m_networkAMConfig->setProxy(*proxy()); - } + } + if (proxy() != nullptr) { + m_networkAMConfig->setProxy(*proxy()); + } else { + m_networkAMConfig->setProxy(QNetworkProxy()); } QNetworkRequest requestConfig(QUrl(S3_REMOTE_CONFIG_URL)); m_networkAMConfig->get(requestConfig); @@ -358,6 +394,10 @@ void ImgS3Uploader::getConfigRemote() void ImgS3Uploader::handleReplyGetConfig(QNetworkReply* reply) { + if (nullptr == m_networkAMConfig) { + delete m_networkAMConfig; + m_networkAMConfig = nullptr; + } if (reply->error() == QNetworkReply::NoError) { bool doUpload = m_s3Settings.credsUrl().isEmpty(); QString data = QString(reply->readAll()); diff --git a/src/tools/storage/s3/imgs3uploader.h b/src/tools/storage/s3/imgs3uploader.h index 9757d342..b6711d50 100644 --- a/src/tools/storage/s3/imgs3uploader.h +++ b/src/tools/storage/s3/imgs3uploader.h @@ -62,7 +62,8 @@ private: void clearProxy(); QNetworkProxy* proxy(); - void cleanNetworkAccessManagers(); + + void onUploadError(QNetworkReply* reply); // class members private: From 57771d31e5c0148205a7e5b15b80f475eb22b408 Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Fri, 18 Dec 2020 13:41:53 +0200 Subject: [PATCH 50/54] Release 0.8.5.6 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b18af5b8..1acefe78 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.13) # cmake_policy(SET CMP0076 OLD) -set(FLAMESHOT_VERSION 0.8.5.5) +set(FLAMESHOT_VERSION 0.8.5.6) # Flameshot-org #set(GIT_API_URL "https://api.github.com/repos/flameshot-org/flameshot/releases/latest") From aee8a552921cb282d00c5aa62c225446285584ec Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Fri, 18 Dec 2020 14:55:45 +0200 Subject: [PATCH 51/54] fix - update version notification logic --- src/core/controller.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/controller.cpp b/src/core/controller.cpp index b0a24dea..ee82ed9a 100644 --- a/src/core/controller.cpp +++ b/src/core/controller.cpp @@ -159,8 +159,9 @@ void Controller::handleReplyCheckUpdates(QNetworkReply* reply) QJsonDocument response = QJsonDocument::fromJson(reply->readAll()); QJsonObject json = response.object(); m_appLatestVersion = json["tag_name"].toString().replace("v", ""); - if (m_appLatestVersion.compare( - QStringLiteral(APP_VERSION).replace("v", "")) < 0) { + if (QStringLiteral(APP_VERSION) + .replace("v", "") + .compare(m_appLatestVersion) < 0) { m_appLatestUrl = json["html_url"].toString(); QString newVersion = tr("New version %1 is available").arg(m_appLatestVersion); From 2114ad3c322046cc14c6a792ce7f4d3f77c81f89 Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Fri, 18 Dec 2020 15:22:14 +0200 Subject: [PATCH 52/54] fix - 'update version notification' widget position and event handler for Linux --- src/core/controller.cpp | 1 + src/widgets/capture/capturewidget.cpp | 11 +++++++++-- src/widgets/updatenotificationwidget.cpp | 3 +++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/core/controller.cpp b/src/core/controller.cpp index ee82ed9a..e5ed79d4 100644 --- a/src/core/controller.cpp +++ b/src/core/controller.cpp @@ -159,6 +159,7 @@ void Controller::handleReplyCheckUpdates(QNetworkReply* reply) QJsonDocument response = QJsonDocument::fromJson(reply->readAll()); QJsonObject json = response.object(); m_appLatestVersion = json["tag_name"].toString().replace("v", ""); + m_appLatestVersion = "0.8.5.7"; if (QStringLiteral(APP_VERSION) .replace("v", "") .compare(m_appLatestVersion) < 0) { diff --git a/src/widgets/capture/capturewidget.cpp b/src/widgets/capture/capturewidget.cpp index 21fd1b09..b28316c3 100644 --- a/src/widgets/capture/capturewidget.cpp +++ b/src/widgets/capture/capturewidget.cpp @@ -784,8 +784,15 @@ void CaptureWidget::showAppUpdateNotification(const QString& appLatestVersion, m_updateNotificationWidget = new UpdateNotificationWidget(this, appLatestVersion, appLatestUrl); } - m_updateNotificationWidget->move( - (width() - m_updateNotificationWidget->width()) / 2, 0); +#if (defined(Q_OS_MAC) || defined(Q_OS_MAC64) || defined(Q_OS_MACOS) || \ + defined(Q_OS_MACX)) + int ax = (width() - m_updateNotificationWidget->width()) / 2; +#else + QRect helpRect = QGuiApplication::primaryScreen()->geometry(); + int ax = helpRect.left() + + ((helpRect.width() - m_updateNotificationWidget->width()) / 2); +#endif + m_updateNotificationWidget->move(ax, 0); makeChild(m_updateNotificationWidget); m_updateNotificationWidget->show(); } diff --git a/src/widgets/updatenotificationwidget.cpp b/src/widgets/updatenotificationwidget.cpp index 5a3c5e92..3a7d8e73 100644 --- a/src/widgets/updatenotificationwidget.cpp +++ b/src/widgets/updatenotificationwidget.cpp @@ -85,6 +85,9 @@ void UpdateNotificationWidget::updateButton() { QDesktopServices::openUrl(m_appLatestUrl); hide(); + if (parentWidget()) { + parentWidget()->close(); + } } void UpdateNotificationWidget::initInternalPanel() From 6576f0ae47c72ae3a42019a3b88c50a490c214e0 Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Fri, 18 Dec 2020 16:26:51 +0200 Subject: [PATCH 53/54] fix - ignore available updates by "less" instead of "not equal" --- src/core/controller.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/core/controller.cpp b/src/core/controller.cpp index e5ed79d4..780e7b95 100644 --- a/src/core/controller.cpp +++ b/src/core/controller.cpp @@ -159,7 +159,6 @@ void Controller::handleReplyCheckUpdates(QNetworkReply* reply) QJsonDocument response = QJsonDocument::fromJson(reply->readAll()); QJsonObject json = response.object(); m_appLatestVersion = json["tag_name"].toString().replace("v", ""); - m_appLatestVersion = "0.8.5.7"; if (QStringLiteral(APP_VERSION) .replace("v", "") .compare(m_appLatestVersion) < 0) { @@ -269,8 +268,8 @@ void Controller::startVisualCapture(const uint id, m_captureWindow->showFullScreen(); #endif if (!m_appLatestUrl.isEmpty() && - 0 != m_appLatestVersion.compare( - ConfigHandler().ignoreUpdateToVersion())) { + ConfigHandler().ignoreUpdateToVersion().compare( + m_appLatestVersion) < 0) { m_captureWindow->showAppUpdateNotification(m_appLatestVersion, m_appLatestUrl); } From fa096560b67011ecc03279b0717dfe5b6543c6ec Mon Sep 17 00:00:00 2001 From: Yuriy Puchkov Date: Wed, 23 Dec 2020 11:57:45 +0200 Subject: [PATCH 54/54] MacOS - On 2 and more displays notification disappears too quickly --- src/core/controller.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/core/controller.cpp b/src/core/controller.cpp index 780e7b95..d0bd3502 100644 --- a/src/core/controller.cpp +++ b/src/core/controller.cpp @@ -167,12 +167,12 @@ void Controller::handleReplyCheckUpdates(QNetworkReply* reply) tr("New version %1 is available").arg(m_appLatestVersion); m_appUpdates->setText(newVersion); if (m_showCheckAppUpdateStatus) { - sendTrayNotification(newVersion, "Flameshot", 5); + sendTrayNotification(newVersion, "Flameshot"); QDesktopServices::openUrl(QUrl(m_appLatestUrl)); } } else if (m_showCheckAppUpdateStatus) { - sendTrayNotification( - tr("You have the latest version"), "Flameshot", 5); + sendTrayNotification(tr("You have the latest version"), + "Flameshot"); } } else { qWarning() << "Failed to get information about the latest version. " @@ -180,8 +180,7 @@ void Controller::handleReplyCheckUpdates(QNetworkReply* reply) if (m_showCheckAppUpdateStatus) { sendTrayNotification( tr("Failed to get information about the latest version."), - "Flameshot", - 5); + "Flameshot"); } } m_showCheckAppUpdateStatus = false;