* Code refactoring - rename deleteToolwidgetOrClose to deleteToolWidgetOrClose (cherry picked from commit cc9d71c660fd335e484b8c2c5a4c9ac939a5b643) * Code refactoring - move part of code from CaptureWidget::paintEvent function into showInitialMessage (cherry picked from commit 377b716dc4cdc460e385e9a953850c377283db73) * Code refactoring - move part of code from CaptureWidget::paintEvent function into showInitialMessage (cherry picked from commit 377b716dc4cdc460e385e9a953850c377283db73) * Code refactoring - move part of code from CaptureWidget::paintEvent function into drawInactiveRegion, rename showInitialMessage -> drawInitialMessage (cherry picked from commit 39bf83588db52b9e5b40fddb2b7a9939a64bd19b) * Re-implement undo/redo feature with a future ability to work with it as with history with access by index (remove/edit not just for a last item) (cherry picked from commit d6c6cf51c38b8dd8f51f443f3dcb162e96796a66) * fix - No bubble border for CircleCountTool (cherry picked from commit 6ef81efd91d9d8d169c1dafd2801fffd2a722ee8) * fix - Restore Pen and Brush after the rectangle tool (effects Circle tool without it) (cherry picked from commit 5239925fa5cf69b6755004f3ab8443d091d731c1) * Tool objects history (undo) list on Utility Panel - added icon to the items (cherry picked from commit 4181990f2677a9c8b62c844546f4ac708ecac076) * Update Ukrainian and Russian translations (cherry picked from commit eae2fe0f5826eda4d44d1616bbd12ff53a9254dd) * Feature - remove any created object on CaptureWidget (cherry picked from commit ed2073fa7c2d4e9067668d5a262a067d3ccf9c9a) * Search tool object on CaptureWidget on mouse click and select it in UtilityPanel (cherry picked from commit 5a997dd5608d0bf72af1b50bcfa1779336b842ac) * Optimize tool object search on CaptureWidget (cherry picked from commit 7cd4872cb4adb29e847c5e3adfdb3a24a240982b) * Move selection with Ctrl modificator key only, set correct mouse cursor for dragging and selecting tool objects (cherry picked from commit bf8e47ccbef33bc765820fe64c12168587117ea6) * Move tool objects based on AbstractTwoPointTool class (cherry picked from commit 3f041b76d2da9326f099b7109984b75a4ce0df28) * Move Text tool objects on CaptureWidget (cherry picked from commit b71b6db9e1a4c64e19a045096bd7df0d5a3f4b8f) * Move Pencil tool objects on CaptureWidget (cherry picked from commit 3a269a57bb38bdfea0596f2a8310de6bd5bdb119) * Code refactoring - clean up unused and some refactoring (cherry picked from commit af667f1e25c7c363a42952b20580620c0e6d7925) * Remove - duplicate 'at' class member with int and uint types (cherry picked from commit 4b5b2f7a75ce568bd23d170b8d8731f630d1bf48) * Search for an active object BLUR/PIXELATE and activate it on mouse click (cherry picked from commit 04cbbdd352e41e23edb4ae596173a6d3971ecdb5) * Increase search radius for text objects (cherry picked from commit 4f189bc40b71b82d0e1e5d3ff06e6641616abb8c) * Code refactoring - remove old unused undo/redo backupPixmap (cherry picked from commit 1b15b9acf514fce854d5e0f467c10a6e14a333a0) * Add object selection arownd the object on select for tools based on AbstractTwoPointTool (cherry picked from commit 26fab8ff365ed0d7ff3eee1191d48248e8a05dcd) * Add object selection arownd the object on select for tools based on AbstractPathTool (cherry picked from commit bbf53e4c35a58d313d0cd94b24b86c15f5841483) * Add object selection arownd the object on select for the TextTool (cherry picked from commit ec5780b5c1ec59fd8924834f67563df36590c0a6) * Edit color for any selected object (cherry picked from commit 465a183978f8e8ff0df0e2c5cf77ec3d3115fccc) * Code refactoring - fix some missprints in MacOS CI (cherry picked from commit 99ef8a55d4ca812b6217bd15934c276a2ed5aa57) * Text object selection has wrong size (cherry picked from commit cd365060f1bc607ee53a77482af50f71d5ffe674) * Make more noticeable object selection for TwoPoints and Path tools (except arrows) (cherry picked from commit 67e304474fd116d95ae6138a50c719186b71bf9a) * Make more noticeable object selection for Arrows (cherry picked from commit 066ff0a425ebfc2c235ef756057c086cd75a863f) * Make more noticeable object selection for Text (cherry picked from commit e5e0afd5853ae2e8169209a0d9a3a6b1ce42f6e1) * fix - Make more noticeable object selection for Arrow tool (cherry picked from commit e1028c7c786d7d8b92b2bd7561193ed5be9066c8) * fix - add '#include <cmath>' for round function (cherry picked from commit a043036627587d78bbc916a4590e4e0cb9a6173d) * Add hotkey for removing objects (cherry picked from commit 13059461d550fbdd7f43d157c3a87fad43989742) * Add hotkey for removing objects (cherry picked from commit 13059461d550fbdd7f43d157c3a87fad43989742) * Code refactoring - remove outdated parts of undo/redo, some optimisation (cherry picked from commit 8e08cea690dcbadb61efbab4139b4bd43a9dd91a) * Add operator = for tool objects to be able to implement undo/redo stack based on the concept of full state saving for each step (just objects, not generated images and layers) (cherry picked from commit 7ffc850883ea97d807514a15d89ac5dd0e45c9f5) * Implement basic Undo/Redo step based on QUndoStack (without move and change color steps), some other fixes (cherry picked from commit 818f3d2939fbe5153787612824e40b3b1997297b) * Add undo/redo stack for changing color (cherry picked from commit 2b8a4ffc4f5e39d8531611f516a222288f54224b) * fix - save objects state before color update for undo/redo stack and do not save state until mouse release event (cherry picked from commit 4ba79d6b96d89a3010f14d0d9759ff98ae407fd1) * Add object movement to the undo/redo stack (cherry picked from commit 1eacc35a15487a815a68db6b7ad5daf8bac7adcf) * fix - undo/redo from first to the latest item (still with a brutal hack) (cherry picked from commit 421f43ff5361da1d215465e133b52b407829340c) * Do not close CaptureWidget if some tool object or selection is active (cherry picked from commit 17fc95e167dd7ac3c17c8ed39c4bbde03301ea9b) * Don't change color if right click is not in the object selection area (cherry picked from commit d8ecead647ca0804c5e7555c8ffa3117abc8f3d1) * Edit thickness for the existing objects (cherry picked from commit 9ab45497e60b1316bf50d67ca7d29b84cd70f515) * fix - restore CaptureWidget thickness on object select (cherry picked from commit 9ff9de5a915eec65bed66a74e3a5ec7f1bee2719) * Code refactoring - move repeated code to the function 'activeToolObject' (cherry picked from commit c388d8c8ec2844c2426148a437f6aaad2a1f7923) * Code refactoring - remove unused lines and many fixes based on Clang-Tidy (cherry picked from commit 7e19019dbaa185387b90fe9f45c8a65f057cd582) * Code refactoring - remove unused lines and many fixes based on Clang-Tidy (cherry picked from commit 7e19019dbaa185387b90fe9f45c8a65f057cd582) * fix - Circle Counter Bubble numeration can skip some items if CPU is high loaded at that moment (cherry picked from commit 6242fe8620bd5a46a7291de41ea8ce5465950664) * Code refactoring - make m_thickness variable in tool objects private and work with it as get/set methods (cherry picked from commit f36e823864c1af35aa94809515f786a45915e180) * fix - CircleTool doesn't place on CaptureWidget without mouse move Code refactoring - make number of variables private for AbstractTwoPointTool and work with them as get/set methods (cherry picked from commit 874ebc3afd3da77fcb954e83429e88016518cbca) * CircleCount - make circles more contrast and mouse preview opaque (cherry picked from commit cebf3b831cf5751cb0b4b9b83f6f63d60b18dc8c) * fix - CircleCount selection and changing color for existing object (cherry picked from commit 477c230194b57af9223a3d68c4ac4f5c1bf3566c) * fix - Move selection area tool cannot be selected (cherry picked from commit 38662ff86e6a858c02050de1caa37295bb25d574) * fix - Selection area disappears on click on existing tool out of area (cherry picked from commit e98c60e226c9883558718f1278879339dc528973) * Change MacOS hotkey Command+Backspace -> Backspace (cherry picked from commit aa96b956348a0192179f03a43a0ce5712ce1a965) * Make rectangle tool with rounded borders (not cut), Make selection tool without rounded edges (cherry picked from commit 2fd9c2ad9f5d00dcb9204b09989a66309b43f472) * fix - Thickness doesn't changed for existing object on changing it in side panel (cherry picked from commit 271731c89185b982a86bb27e3a7bc441a2942c10) * fix - Sync selection on CaptureWidget when clicking on tool in panel (cherry picked from commit 9db0c75ec0e92d5fad5bc3856f46f5ed87c6aefd) * Change selection on mouse down instead of mouse up event (cherry picked from commit 3e09778b701498498c144c5e5516f746f1c9088a) * Code refactoring - remove variable m_rightClick from CaptureWidget, use another existing trigger to detect it (cherry picked from commit 1abc595ce352b8445a943504a7c359cd0830a640) * fix - Rectangle preview and final objects are not the same (cherry picked from commit b31905892a60228cae5c8bcde1f71699eca63447) * fix - Doesn't save image if the 'use fixed path' checkbox is checked (cherry picked from commit 1470859aa2c4f28f2c45581891534f3fa4bd6f0a) * fix - Latest Uploads max size value doesn't restore after reset/import settings (cherry picked from commit fa72bacfc0bb12313dd351ee246dd567dfc3f3c8) * fix - impossible to disable the keyboard shortcut by clicking Delete button (cherry picked from commit 74d36e4b3a333cb81ffb4ad93a2bd5465e79cb94) * fix - MacOS - Increase and Decrease Size tool icon colors are not changed (dark/light) when changing main tool background color (cherry picked from commit e48b685c900540a9cd353f71290be04491621fc7) * fix - Selected region control points cannot be dragged on the screen corners (cherry picked from commit 08bf02b5ee096c14d10c15853543749d58c51ef4) * fix - Object selects if mouse pointer above any object when TextTool is active (cherry picked from commit b7a6a7efc8c3a77c55039721ab0cf388973c25d5) * fix - Test Tool doesn't close editor on next text item, you can see two editors and one of them will be never closed (cherry picked from commit eb12f76fd7121d1917659485673305684de523c8) * Code refactoring - remove function in the CaptureToolObjects class with the same functional (cherry picked from commit 1a108f7be48d5b4d89960ac4a5f4e82c03762cbc) * Code refactoring - CaptureWidget::mousePressEvent function is split into a few, code became more readable (cherry picked from commit dc850b3daa00fa173126d86403c35f3c67c448e6) * optimization - remove unnecessary calls of CaptureWidget::drawToolsData (cherry picked from commit c2432bd5602657233ee9506e84a7806bafeb6f68) * fix - Move tool doesn't work if any object is selected fix - Active tool stays active if existing object is selected in the Tool Settings panel (cherry picked from commit 6154feba575f79a06082f72b5c45a869c8d08f42) * fix - App freezes for a while when changing thickness of the pixelate to the minimal value (cherry picked from commit 3a1c519c8309b831e4cc4f11b9014b1a9599ba93) * fix - Text Tool doubles on press ESC when text editing field is active (cherry picked from commit 00bdebff75a0178a46f38606eec4921fdf2f5917) * fix - Image is pinned/saved with object selection frame if there is one (cherry picked from commit 81871860720fa0527f8dcabe1b274b3f03818b7c) * fix - Object line thickness changes according to previously modified object when switching between objects via object list (cherry picked from commit a4a367d30e18a743f0df849e7db1aa2dc20af2a6) * fix - Configuration: Filename doesn't set default value on clear (cherry picked from commit 289663c0c85f4bae98c1a19a7a05fb1029738c12) * Update README.md - Global shortcut for Ubuntu 20.04 (cherry picked from commit 3bee0aa0ac937c00113f2bcca818a9a57e6c5530) * fix - Saved screenshot has selection if some object is selected on Save (cherry picked from commit e53bb33e804cc7de1102a1ab3069bfb849174b73) * fix - Saved screenshot has selection if some object is selected on Upload * fix - Rename "Reset" to "Restore" in file settings for restoring saved file name rules in the configuration widget (cherry picked from commit d2ac5a330fca902b832fce163e4f5f138dbbcc6b) * Update Ukrainian and Russian translations * fix - Shapes are displayed as semi-empty at the very beginning of drawing if the line is thick (cherry picked from commit d8b09c5d5233fefe3181a69f5200198815bc3ec1) * fix - MacOS - Button "Tool Settings" is not visible on some displays if displays are vertically aligned (cherry picked from commit 6af590a859b2708177c34c3ff37725905f4c3bec) * fix - Incorrect selection border for Rectangle/Pixelate tool (cherry picked from commit c908d3b67c5d8e2188d2699ac11951110493b756) * Code refactoring - replace magic numbers (cherry picked from commit c8e1ce345adb1646659e4e90d314916532d5e67a) * fix merge conflicts for Internationalization files * fix - thickness shouldn't be less than one (cherry picked from commit d8ea8a0db7fffcec601dd3139c57198ba15b64b7) * Update copyright info Co-authored-by: Yuriy Puchkov <yuriy.puchkov@namecheap.com>
604 lines
19 KiB
C++
604 lines
19 KiB
C++
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
// SPDX-FileCopyrightText: 2017-2019 Alejandro Sirgo Rica & Contributors
|
|
|
|
#include "controller.h"
|
|
|
|
#if defined(Q_OS_MACOS)
|
|
#include "external/QHotkey/QHotkey"
|
|
#endif
|
|
|
|
#include "src/config/configwindow.h"
|
|
#include "src/core/qguiappcurrentscreen.h"
|
|
#include "src/utils/confighandler.h"
|
|
#include "src/utils/history.h"
|
|
#include "src/utils/screengrabber.h"
|
|
#include "src/utils/systemnotification.h"
|
|
#include "src/widgets/capture/capturetoolbutton.h"
|
|
#include "src/widgets/capture/capturewidget.h"
|
|
#include "src/widgets/capturelauncher.h"
|
|
#include "src/widgets/historywidget.h"
|
|
#include "src/widgets/infowindow.h"
|
|
#include "src/widgets/notificationwidget.h"
|
|
#include <QAction>
|
|
#include <QApplication>
|
|
#include <QClipboard>
|
|
#include <QDebug>
|
|
#include <QDesktopServices>
|
|
#include <QDesktopWidget>
|
|
#include <QJsonDocument>
|
|
#include <QJsonObject>
|
|
#include <QMenu>
|
|
#include <QMessageBox>
|
|
#include <QNetworkAccessManager>
|
|
#include <QNetworkReply>
|
|
#include <QNetworkRequest>
|
|
#include <QOperatingSystemVersion>
|
|
#include <QSystemTrayIcon>
|
|
#include <QThread>
|
|
|
|
#ifdef Q_OS_WIN
|
|
#include "src/core/globalshortcutfilter.h"
|
|
#endif
|
|
|
|
#if defined(Q_OS_MACOS)
|
|
#include <QOperatingSystemVersion>
|
|
#include <QScreen>
|
|
#endif
|
|
|
|
// Controller is the core component of Flameshot, creates the trayIcon and
|
|
// launches the capture widget
|
|
|
|
Controller::Controller()
|
|
: m_captureWindow(nullptr)
|
|
, m_history(nullptr)
|
|
, m_trayIcon(nullptr)
|
|
, m_trayIconMenu(nullptr)
|
|
, m_networkCheckUpdates(nullptr)
|
|
, m_showCheckAppUpdateStatus(false)
|
|
#if defined(Q_OS_MACOS)
|
|
, m_HotkeyScreenshotCapture(nullptr)
|
|
, m_HotkeyScreenshotHistory(nullptr)
|
|
#endif
|
|
{
|
|
m_appLatestVersion = QStringLiteral(APP_VERSION).replace("v", "");
|
|
qApp->setQuitOnLastWindowClosed(false);
|
|
|
|
// set default shortcusts if not set yet
|
|
ConfigHandler().setShortcutsDefault();
|
|
|
|
// init tray icon
|
|
#if defined(Q_OS_LINUX) || defined(Q_OS_UNIX)
|
|
if (!ConfigHandler().disabledTrayIconValue()) {
|
|
enableTrayIcon();
|
|
}
|
|
#elif defined(Q_OS_WIN)
|
|
enableTrayIcon();
|
|
|
|
GlobalShortcutFilter* nativeFilter = new GlobalShortcutFilter(this);
|
|
qApp->installNativeEventFilter(nativeFilter);
|
|
connect(nativeFilter, &GlobalShortcutFilter::printPressed, this, [this]() {
|
|
this->requestCapture(CaptureRequest(CaptureRequest::GRAPHICAL_MODE));
|
|
});
|
|
#endif
|
|
|
|
QString StyleSheet = CaptureButton::globalStyleSheet();
|
|
qApp->setStyleSheet(StyleSheet);
|
|
|
|
#if defined(Q_OS_MACOS)
|
|
// 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 = QGuiAppCurrentScreen().currentScreen();
|
|
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
|
|
|
|
if (ConfigHandler().checkForUpdates()) {
|
|
getLatestAvailableVersion();
|
|
}
|
|
}
|
|
|
|
Controller::~Controller()
|
|
{
|
|
delete m_history;
|
|
delete m_trayIconMenu;
|
|
}
|
|
|
|
Controller* Controller::getInstance()
|
|
{
|
|
static Controller c;
|
|
return &c;
|
|
}
|
|
|
|
void Controller::enableExports()
|
|
{
|
|
connect(
|
|
this, &Controller::captureTaken, this, &Controller::handleCaptureTaken);
|
|
connect(
|
|
this, &Controller::captureFailed, this, &Controller::handleCaptureFailed);
|
|
}
|
|
|
|
void Controller::setCheckForUpdatesEnabled(const bool enabled)
|
|
{
|
|
m_appUpdates->setVisible(enabled);
|
|
m_appUpdates->setEnabled(enabled);
|
|
if (enabled) {
|
|
getLatestAvailableVersion();
|
|
}
|
|
}
|
|
|
|
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(this);
|
|
QNetworkRequest requestCheckUpdates(QUrl(FLAMESHOT_APP_VERSION_URL));
|
|
connect(m_networkCheckUpdates,
|
|
&QNetworkAccessManager::finished,
|
|
this,
|
|
&Controller::handleReplyCheckUpdates);
|
|
m_networkCheckUpdates->get(requestCheckUpdates);
|
|
|
|
// check for updates each 24 hours
|
|
doLater(1000 * 60 * 60 * 24, this, [this]() {
|
|
if (ConfigHandler().checkForUpdates()) {
|
|
this->getLatestAvailableVersion();
|
|
}
|
|
});
|
|
}
|
|
|
|
void Controller::handleReplyCheckUpdates(QNetworkReply* reply)
|
|
{
|
|
if (!ConfigHandler().checkForUpdates()) {
|
|
return;
|
|
}
|
|
if (reply->error() == QNetworkReply::NoError) {
|
|
QJsonDocument response = QJsonDocument::fromJson(reply->readAll());
|
|
QJsonObject json = response.object();
|
|
m_appLatestVersion = json["tag_name"].toString().replace("v", "");
|
|
|
|
// Transform strings version for correct comparison
|
|
QStringList appLatestVersion =
|
|
m_appLatestVersion.replace("v", "").split(".");
|
|
QStringList currentVersion =
|
|
QStringLiteral(APP_VERSION).replace("v", "").split(".");
|
|
// transform versions to the string which can be compared correctly,
|
|
// example: versions "0.8.5.9" and "0.8.5.10" are transformed into:
|
|
// "0000.0008.0005.0009" and "0000.0008.0005.0010"
|
|
// For string comparison you'll get:
|
|
// "0.8.5.9" < "0.8.5.10" INCORRECT (lower version is bigger)
|
|
// "0000.0008.0005.0009" > "0000.0008.0005.0010" CORRECT
|
|
std::transform(
|
|
appLatestVersion.begin(),
|
|
appLatestVersion.end(),
|
|
appLatestVersion.begin(),
|
|
[](QString c) -> QString { return c = ("0000" + c).right(4); });
|
|
std::transform(
|
|
currentVersion.begin(),
|
|
currentVersion.end(),
|
|
currentVersion.begin(),
|
|
[](QString c) -> QString { return c = ("0000" + c).right(4); });
|
|
|
|
if (currentVersion.join(".").compare(appLatestVersion.join(".")) < 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");
|
|
QDesktopServices::openUrl(QUrl(m_appLatestUrl));
|
|
}
|
|
} else if (m_showCheckAppUpdateStatus) {
|
|
sendTrayNotification(tr("You have the latest version"),
|
|
"Flameshot");
|
|
}
|
|
} 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");
|
|
}
|
|
}
|
|
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();
|
|
m_requestMap.insert(id, request);
|
|
|
|
switch (request.captureMode()) {
|
|
case CaptureRequest::FULLSCREEN_MODE:
|
|
doLater(request.delay(), this, [this, id]() {
|
|
this->startFullscreenCapture(id);
|
|
});
|
|
break;
|
|
// TODO: Figure out the code path that gets here so the deprated
|
|
// warning can be fixed
|
|
case CaptureRequest::SCREEN_MODE: {
|
|
int&& number = request.data().toInt();
|
|
doLater(request.delay(), this, [this, id, number]() {
|
|
this->startScreenGrab(id, number);
|
|
});
|
|
break;
|
|
}
|
|
case CaptureRequest::GRAPHICAL_MODE: {
|
|
QString&& path = request.path();
|
|
doLater(request.delay(), this, [this, id, path]() {
|
|
this->startVisualCapture(id, path);
|
|
});
|
|
break;
|
|
}
|
|
default:
|
|
emit captureFailed(id);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// creation of a new capture in GUI mode
|
|
void Controller::startVisualCapture(const uint id,
|
|
const QString& forcedSavePath)
|
|
{
|
|
#if defined(Q_OS_MACOS)
|
|
// This is required on MacOS because of Mission Control. If you'll switch to
|
|
// another Desktop you cannot take a new screenshot from the tray, you have
|
|
// to switch back to the Flameshot Desktop manually. It is not obvious and a
|
|
// large number of users are confused and report a bug.
|
|
if (m_captureWindow) {
|
|
m_captureWindow->close();
|
|
delete m_captureWindow;
|
|
m_captureWindow = nullptr;
|
|
}
|
|
#endif
|
|
|
|
if (nullptr == m_captureWindow) {
|
|
int timeout = 5000; // 5 seconds
|
|
const int delay = 100;
|
|
QWidget* modalWidget = nullptr;
|
|
for (; timeout >= 0; timeout -= delay) {
|
|
modalWidget = qApp->activeModalWidget();
|
|
if (nullptr == modalWidget) {
|
|
break;
|
|
}
|
|
modalWidget->close();
|
|
modalWidget->deleteLater();
|
|
QThread::msleep(delay);
|
|
}
|
|
if (0 == timeout) {
|
|
QMessageBox::warning(
|
|
nullptr, tr("Error"), tr("Unable to close active modal widgets"));
|
|
return;
|
|
}
|
|
|
|
m_captureWindow = new CaptureWidget(id, forcedSavePath);
|
|
// m_captureWindow = new CaptureWidget(id, forcedSavePath, false); //
|
|
// debug
|
|
connect(m_captureWindow,
|
|
&CaptureWidget::captureFailed,
|
|
this,
|
|
&Controller::captureFailed);
|
|
connect(m_captureWindow,
|
|
&CaptureWidget::captureTaken,
|
|
this,
|
|
&Controller::captureTaken);
|
|
|
|
#ifdef Q_OS_WIN
|
|
m_captureWindow->show();
|
|
#elif defined(Q_OS_MACOS)
|
|
// In "Emulate fullscreen mode"
|
|
m_captureWindow->showFullScreen();
|
|
m_captureWindow->activateWindow();
|
|
m_captureWindow->raise();
|
|
#else
|
|
m_captureWindow->showFullScreen();
|
|
// m_captureWindow->show(); // For CaptureWidget Debugging under Linux
|
|
#endif
|
|
if (!m_appLatestUrl.isEmpty() &&
|
|
ConfigHandler().ignoreUpdateToVersion().compare(
|
|
m_appLatestVersion) < 0) {
|
|
m_captureWindow->showAppUpdateNotification(m_appLatestVersion,
|
|
m_appLatestUrl);
|
|
}
|
|
} else {
|
|
emit captureFailed(id);
|
|
}
|
|
}
|
|
|
|
void Controller::startScreenGrab(const uint id, const int screenNumber)
|
|
{
|
|
bool ok = true;
|
|
int n = screenNumber;
|
|
|
|
if (n < 0) {
|
|
QPoint globalCursorPos = QCursor::pos();
|
|
n = qApp->desktop()->screenNumber(globalCursorPos);
|
|
}
|
|
QPixmap p(ScreenGrabber().grabScreen(n, ok));
|
|
if (ok) {
|
|
QRect selection; // `flameshot screen` does not support --selection
|
|
emit captureTaken(id, p, selection);
|
|
} else {
|
|
emit captureFailed(id);
|
|
}
|
|
}
|
|
|
|
// creation of the configuration window
|
|
void Controller::openConfigWindow()
|
|
{
|
|
if (!m_configWindow) {
|
|
m_configWindow = new ConfigWindow();
|
|
m_configWindow->show();
|
|
#if defined(Q_OS_MACOS)
|
|
m_configWindow->activateWindow();
|
|
m_configWindow->raise();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// creation of the window of information
|
|
void Controller::openInfoWindow()
|
|
{
|
|
if (!m_infoWindow) {
|
|
m_infoWindow = new InfoWindow();
|
|
#if defined(Q_OS_MACOS)
|
|
m_infoWindow->activateWindow();
|
|
m_infoWindow->raise();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void Controller::openLauncherWindow()
|
|
{
|
|
if (!m_launcherWindow) {
|
|
m_launcherWindow = new CaptureLauncher();
|
|
}
|
|
m_launcherWindow->show();
|
|
#if defined(Q_OS_MACOS)
|
|
m_launcherWindow->activateWindow();
|
|
m_launcherWindow->raise();
|
|
#endif
|
|
}
|
|
|
|
void Controller::enableTrayIcon()
|
|
{
|
|
ConfigHandler().setDisabledTrayIcon(false);
|
|
if (m_trayIcon) {
|
|
m_trayIcon->show();
|
|
return;
|
|
}
|
|
if (nullptr == m_trayIconMenu) {
|
|
m_trayIconMenu = new QMenu();
|
|
Q_ASSERT(m_trayIconMenu);
|
|
}
|
|
|
|
QAction* captureAction = new QAction(tr("&Take Screenshot"), this);
|
|
connect(captureAction, &QAction::triggered, this, [this]() {
|
|
#if defined(Q_OS_MACOS)
|
|
auto currentMacOsVersion = QOperatingSystemVersion::current();
|
|
if (currentMacOsVersion >= currentMacOsVersion.MacOSBigSur) {
|
|
startVisualCapture();
|
|
} else {
|
|
// It seems it is not relevant for MacOS BigSur (Wait 400 ms to hide
|
|
// the QMenu)
|
|
doLater(400, this, [this]() { this->startVisualCapture(); });
|
|
}
|
|
#else
|
|
// Wait 400 ms to hide the QMenu
|
|
doLater(400, this, [this]() { this->startVisualCapture(); });
|
|
#endif
|
|
});
|
|
QAction* launcherAction = new QAction(tr("&Open Launcher"), this);
|
|
connect(launcherAction,
|
|
&QAction::triggered,
|
|
this,
|
|
&Controller::openLauncherWindow);
|
|
QAction* configAction = new QAction(tr("&Configuration"), this);
|
|
connect(
|
|
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);
|
|
|
|
// recent screenshots
|
|
QAction* recentAction = new QAction(tr("&Latest Uploads"), this);
|
|
connect(
|
|
recentAction, SIGNAL(triggered()), this, SLOT(showRecentScreenshots()));
|
|
|
|
// generate menu
|
|
m_trayIconMenu->addAction(captureAction);
|
|
m_trayIconMenu->addAction(launcherAction);
|
|
m_trayIconMenu->addSeparator();
|
|
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);
|
|
setCheckForUpdatesEnabled(ConfigHandler().checkForUpdates());
|
|
|
|
if (nullptr == m_trayIcon) {
|
|
m_trayIcon = new QSystemTrayIcon();
|
|
Q_ASSERT(m_trayIcon);
|
|
}
|
|
m_trayIcon->setToolTip(QStringLiteral("Flameshot"));
|
|
#if defined(Q_OS_MACOS)
|
|
// 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 defined(Q_OS_MACOS)
|
|
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);
|
|
#endif
|
|
|
|
#ifdef Q_OS_WIN
|
|
// Ensure proper removal of tray icon when program quits on Windows.
|
|
connect(
|
|
qApp, &QCoreApplication::aboutToQuit, m_trayIcon, &QSystemTrayIcon::hide);
|
|
#endif
|
|
|
|
m_trayIcon->show();
|
|
|
|
if (ConfigHandler().showStartupLaunchMessage()) {
|
|
m_trayIcon->showMessage(
|
|
"Flameshot",
|
|
QObject::tr(
|
|
"Hello, I'm here! Click icon in the tray to take a screenshot or "
|
|
"click with a right button to see more options."),
|
|
trayIcon,
|
|
3000);
|
|
}
|
|
}
|
|
|
|
void Controller::disableTrayIcon()
|
|
{
|
|
#if defined(Q_OS_LINUX) || defined(Q_OS_UNIX) || defined(Q_OS_MACOS)
|
|
if (m_trayIcon) {
|
|
m_trayIcon->hide();
|
|
}
|
|
ConfigHandler().setDisabledTrayIcon(true);
|
|
#endif
|
|
}
|
|
|
|
void Controller::sendTrayNotification(const QString& text,
|
|
const QString& title,
|
|
const int timeout)
|
|
{
|
|
if (m_trayIcon) {
|
|
m_trayIcon->showMessage(
|
|
title, text, QIcon(":img/app/flameshot.svg"), timeout);
|
|
}
|
|
}
|
|
|
|
void Controller::updateConfigComponents()
|
|
{
|
|
if (m_configWindow) {
|
|
m_configWindow->updateChildren();
|
|
}
|
|
}
|
|
|
|
void Controller::updateRecentScreenshots()
|
|
{
|
|
if (nullptr != m_history) {
|
|
if (m_history->isVisible()) {
|
|
m_history->loadHistory();
|
|
}
|
|
}
|
|
}
|
|
|
|
void Controller::showRecentScreenshots()
|
|
{
|
|
if (nullptr == m_history) {
|
|
m_history = new HistoryWidget();
|
|
}
|
|
m_history->loadHistory();
|
|
m_history->show();
|
|
#if defined(Q_OS_MACOS)
|
|
m_history->activateWindow();
|
|
m_history->raise();
|
|
#endif
|
|
}
|
|
|
|
void Controller::sendCaptureSaved(uint id, const QString& savePath)
|
|
{
|
|
emit captureSaved(id, savePath);
|
|
}
|
|
|
|
void Controller::startFullscreenCapture(const uint id)
|
|
{
|
|
bool ok = true;
|
|
QPixmap p(ScreenGrabber().grabEntireDesktop(ok));
|
|
if (ok) {
|
|
QRect selection; // `flameshot full` does not support --selection
|
|
emit captureTaken(id, p, selection);
|
|
} else {
|
|
emit captureFailed(id);
|
|
}
|
|
}
|
|
|
|
void Controller::handleCaptureTaken(uint id, QPixmap p, QRect selection)
|
|
{
|
|
auto it = m_requestMap.find(id);
|
|
if (it != m_requestMap.end()) {
|
|
it.value().exportCapture(p);
|
|
m_requestMap.erase(it);
|
|
}
|
|
}
|
|
|
|
void Controller::handleCaptureFailed(uint id)
|
|
{
|
|
m_requestMap.remove(id);
|
|
}
|
|
|
|
void Controller::doLater(int msec, QObject* receiver, lambda func)
|
|
{
|
|
QTimer* timer = new QTimer(receiver);
|
|
QObject::connect(timer, &QTimer::timeout, receiver, [timer, func]() {
|
|
func();
|
|
timer->deleteLater();
|
|
});
|
|
timer->setInterval(msec);
|
|
timer->start();
|
|
}
|