Files
flameshot/src/core/controller.cpp
Yurii Puchkov 55e61b8743 Namecheap - Basic objects editing (move, remove, change color, change thickness) (#1539)
* 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>
2021-04-19 19:28:10 -05:00

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();
}