diff --git a/CMakeLists.txt b/CMakeLists.txt index 1cadbbd2cd..a49d96a7f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -69,6 +69,12 @@ option(USE_EXTERNAL_SINGLEAPPLICATION "Use external QtSingleApplication library" option(USE_LAUNCHER_ABSOLUTE_PATH "Use absolute path for the desktop launcher" ON) option(USE_WAYLAND_CLIPBOARD "USE KF Gui Wayland Clipboard" OFF) option(DISABLE_UPDATE_CHECKER "Disable check for updates" OFF) +option(USE_PLUGIN_MANAGER "Activate the Plugin Manager" ON) +option(USE_WAYLAND_GRIM "Activate the Wayland GRIM screenshot adapter" OFF) + +if (USE_PLUGIN_MANAGER) + set(PLUGIN_DIRECTORY "app_plugins" CACHE PATH "Setting the Plugin Manager Plugin Directory") +endif() if (DISABLE_UPDATE_CHECKER) add_compile_definitions(DISABLE_UPDATE_CHECKER) endif () diff --git a/data/graphics.qrc b/data/graphics.qrc index fd1d93d139..bed91511de 100644 --- a/data/graphics.qrc +++ b/data/graphics.qrc @@ -99,5 +99,9 @@ img/material/black/image.svg img/material/white/apps.svg img/material/white/image.svg + img/material/black/content-print.svg + img/material/black/save-to-pdf.svg + img/material/white/content-print.svg + img/material/white/save-to-pdf.svg diff --git a/data/img/material/black/content-print.svg b/data/img/material/black/content-print.svg new file mode 100644 index 0000000000..1409b5c0d0 --- /dev/null +++ b/data/img/material/black/content-print.svg @@ -0,0 +1,41 @@ + + + + + + + + + + diff --git a/data/img/material/black/save-to-pdf.svg b/data/img/material/black/save-to-pdf.svg new file mode 100644 index 0000000000..98394b00b0 --- /dev/null +++ b/data/img/material/black/save-to-pdf.svg @@ -0,0 +1,24 @@ + + + + + + + + diff --git a/data/img/material/white/content-print.svg b/data/img/material/white/content-print.svg new file mode 100644 index 0000000000..29a0cda4e1 --- /dev/null +++ b/data/img/material/white/content-print.svg @@ -0,0 +1,56 @@ + + + + + + + + + + + + + diff --git a/data/img/material/white/save-to-pdf.svg b/data/img/material/white/save-to-pdf.svg new file mode 100644 index 0000000000..45434f480e --- /dev/null +++ b/data/img/material/white/save-to-pdf.svg @@ -0,0 +1,36 @@ + + + + + + + + + + + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c29349a525..74bc2b45c1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -8,12 +8,18 @@ find_package( Network Svg DBus - LinguistTools) + LinguistTools + PrintSupport) if (USE_WAYLAND_CLIPBOARD) find_package(KF5GuiAddons) endif() +if (USE_PLUGIN_MANAGER) + find_package(yaml-cpp) +endif() + +set(USE_PLUGIN_MANAGER ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_AUTOUIC ON) @@ -213,6 +219,7 @@ target_link_libraries( Qt5::DBus Qt5::Network Qt5::Widgets + Qt5::PrintSupport ${QTSINGLEAPPLICATION_LIBRARY} QtColorWidgets diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index c933bdd6f1..5bb3cd5339 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -3,6 +3,8 @@ target_sources(flameshot PRIVATE flameshotdaemon.h flameshotdbusadapter.h qguiappcurrentscreen.h + pluginmanager.h + corepluginInterface.h ) target_sources(flameshot PRIVATE @@ -11,6 +13,7 @@ target_sources(flameshot PRIVATE flameshotdaemon.cpp flameshotdbusadapter.cpp qguiappcurrentscreen.cpp + pluginmanager.cpp ) IF (WIN32) diff --git a/src/core/capturerequest.h b/src/core/capturerequest.h index ac8f885cec..ed90cc6ab1 100644 --- a/src/core/capturerequest.h +++ b/src/core/capturerequest.h @@ -27,6 +27,8 @@ class CaptureRequest PIN = 16, UPLOAD = 32, ACCEPT_ON_SELECT = 64, + PRINT_SYSTEM = 128, + SAVE_TO_PDF = 256 }; CaptureRequest(CaptureMode mode, diff --git a/src/core/corepluginInterface.h b/src/core/corepluginInterface.h new file mode 100644 index 0000000000..54f48672e8 --- /dev/null +++ b/src/core/corepluginInterface.h @@ -0,0 +1,20 @@ +#ifndef COREPLUGININTERFACE_H +#define COREPLUGININTERFACE_H + +#include + +class CorePluginInterface { +public: + virtual ~CorePluginInterface() = 0; + virtual bool load(std::map &PluginConfig) = 0; + virtual void unload() = 0; + virtual bool ImagePost(QPixmap &pixmap) = 0; + virtual bool ImageToPDFPost(QPixmap &pixmap) = 0; + virtual bool PrintPre(QPixmap &pixmap) = 0; +}; + +#define FlameshotPlugin_iid "FlameshotPlugin.CorePluginInterface" + +Q_DECLARE_INTERFACE(CorePluginInterface, FlameshotPlugin_iid) + +#endif // COREPLUGININTERFACE_H diff --git a/src/core/flameshot.cpp b/src/core/flameshot.cpp index c7eadcadb6..af564b1774 100644 --- a/src/core/flameshot.cpp +++ b/src/core/flameshot.cpp @@ -31,7 +31,15 @@ #include #include #include +#include #include +#include +#include +#include + +#ifdef USE_PLUGIN_MANAGER +#include "core/pluginmanager.h" +#endif #if defined(Q_OS_MACOS) #include @@ -352,7 +360,12 @@ void Flameshot::exportCapture(const QPixmap& capture, int tasks = req.tasks(), mode = req.captureMode(); QString path = req.path(); + QPixmap PixmapOutputBuffer(capture); + if (tasks & CR::PRINT_GEOMETRY) { +#ifdef USE_PLUGIN_MANAGER + PluginManager::getInstance()->CallImagePost(PixmapOutputBuffer); +#endif QByteArray byteArray; QBuffer buffer(&byteArray); QTextStream(stdout) @@ -361,9 +374,12 @@ void Flameshot::exportCapture(const QPixmap& capture, } if (tasks & CR::PRINT_RAW) { +#ifdef USE_PLUGIN_MANAGER + PluginManager::getInstance()->CallImagePost(PixmapOutputBuffer); +#endif QByteArray byteArray; QBuffer buffer(&byteArray); - capture.save(&buffer, "PNG"); + PixmapOutputBuffer.save(&buffer, "PNG"); QFile file; file.open(stdout, QIODevice::WriteOnly); @@ -372,19 +388,35 @@ void Flameshot::exportCapture(const QPixmap& capture, } if (tasks & CR::SAVE) { +#ifdef USE_PLUGIN_MANAGER + PluginManager::getInstance()->CallImagePost(PixmapOutputBuffer); +#endif if (req.path().isEmpty()) { - saveToFilesystemGUI(capture); + saveToFilesystemGUI(PixmapOutputBuffer); } else { - saveToFilesystem(capture, path); + saveToFilesystem(PixmapOutputBuffer, path); } } + if (tasks & CR::SAVE_TO_PDF) { +#ifdef USE_PLUGIN_MANAGER + PluginManager::getInstance()->CallImageToPDFPost(PixmapOutputBuffer); +#endif + saveToPDF(PixmapOutputBuffer); + } + if (tasks & CR::COPY) { - FlameshotDaemon::copyToClipboard(capture); +#ifdef USE_PLUGIN_MANAGER + PluginManager::getInstance()->CallImagePost(PixmapOutputBuffer); +#endif + FlameshotDaemon::copyToClipboard(PixmapOutputBuffer); } if (tasks & CR::PIN) { - FlameshotDaemon::createPin(capture, selection); +#ifdef USE_PLUGIN_MANAGER + PluginManager::getInstance()->CallImagePost(PixmapOutputBuffer); +#endif + FlameshotDaemon::createPin(PixmapOutputBuffer, selection); if (mode == CR::SCREEN_MODE || mode == CR::FULLSCREEN_MODE) { AbstractLogger::info() << QObject::tr("Full screen screenshot pinned to screen"); @@ -392,6 +424,9 @@ void Flameshot::exportCapture(const QPixmap& capture, } if (tasks & CR::UPLOAD) { +#ifdef USE_PLUGIN_MANAGER + PluginManager::getInstance()->CallImagePost(PixmapOutputBuffer); +#endif if (!ConfigHandler().uploadWithoutConfirmation()) { auto* dialog = new ImgUploadDialog(); if (dialog->exec() == QDialog::Rejected) { @@ -399,7 +434,7 @@ void Flameshot::exportCapture(const QPixmap& capture, } } - ImgUploaderBase* widget = ImgUploaderManager().uploader(capture); + ImgUploaderBase* widget = ImgUploaderManager().uploader(PixmapOutputBuffer); widget->show(); widget->activateWindow(); // NOTE: lambda can't capture 'this' because it might be destroyed later @@ -416,8 +451,68 @@ void Flameshot::exportCapture(const QPixmap& capture, }); } + if(tasks & CR::PRINT_SYSTEM) { + QPixmap pixmap = capture; +#ifdef USE_PLUGIN_MANAGER + PluginManager::getInstance()->CallPrintPre(pixmap); +#endif + QPrinter printer; + printer.setPageOrientation(QPageLayout::Orientation::Landscape); + + QPrintPreviewDialog dialog(&printer); + dialog.setWindowFlag(Qt::WindowMinMaxButtonsHint); + dialog.setWindowTitle(tr("Print Document")); + +#if defined(Q_OS_WIN) + QToolBar *PrintPreviewToolbar = dialog.findChild(); + QList List = PrintPreviewToolbar->actions(); + int index = 0; + QSet RemoveIndex; + RemoveIndex.insert(0); + RemoveIndex.insert(1); + RemoveIndex.insert(16); + RemoveIndex.insert(17); + RemoveIndex.insert(18); + const int PrintAction = 21; + QSet RemoveList; + foreach(auto it, List) { + if(RemoveIndex.find(index) != RemoveIndex.end()) { + RemoveList.insert(it); + } + if(index == PrintAction) { + it->setIcon(QIcon(":/img/material/black/content-print.svg")); + } + index++; + } + foreach(auto it, List) { + if(RemoveList.find(it) != RemoveList.end()) { + PrintPreviewToolbar->removeAction(it); + } + } + +#endif + connect(&dialog, &QPrintPreviewDialog::paintRequested, [&](QPrinter *printer) { + QPainter painter(printer); + if((pixmap.size().width() >= printer->width()) || + (pixmap.size().height() >= printer->height())) { + pixmap = pixmap.scaled(printer->width(), printer->height(), Qt::KeepAspectRatio, Qt::SmoothTransformation); + } else if(pixmap.size().width() >= (printer->width()/2)) { + pixmap = pixmap.scaledToWidth(printer->width(), Qt::SmoothTransformation); + if(pixmap.size().height() >= printer->height()) { + pixmap = pixmap.scaledToHeight(printer->height(), Qt::SmoothTransformation); + } + } + + QRect rect((printer->width() - pixmap.size().width()) /2, + (printer->height() - pixmap.size().height()) /2, + pixmap.size().width(), pixmap.size().height()); + painter.drawPixmap(rect, pixmap); + }); + dialog.exec(); + } + if (!(tasks & CR::UPLOAD)) { - emit captureTaken(capture); + emit captureTaken(PixmapOutputBuffer); } } diff --git a/src/core/pluginmanager.cpp b/src/core/pluginmanager.cpp new file mode 100644 index 0000000000..22aaf2e4bd --- /dev/null +++ b/src/core/pluginmanager.cpp @@ -0,0 +1,157 @@ +#ifdef USE_PLUGIN_MANAGER + +#include "pluginmanager.h" +#include +#include +#include +#include "corepluginInterface.h" + +PluginManager::PluginManager() { + +} + +PluginManager *PluginManager::getInstance() { + static PluginManager pluginManager; + qDebug() << QObject::tr("Get the plugin manager interface: ").toStdString().c_str() << &pluginManager; + return &pluginManager; +} + +void PluginManager::LoopDirsPlugins(QString Base, std::function Callback) { + QDir dirs(Base); + if(dirs.exists()) { + dirs.setFilter(QDir::AllEntries | QDir::NoDotAndDotDot); + QFileInfoList fileList = dirs.entryInfoList(); + foreach(QFileInfo fileInfo, fileList) { + if (Base != pluginDir && fileInfo.isDir()) { + QFileInfo infocheck(fileInfo.absolutePath() + PluginDefineYaml); + if(!infocheck.exists()) { + LoopDirsPlugins(fileInfo.absoluteFilePath(), Callback); + } + } else if(Base == pluginDir && fileInfo.isDir()) { + LoopDirsPlugins(fileInfo.absoluteFilePath(), Callback); + } else if(fileInfo.isFile()) { + if(fileInfo.fileName() == PluginDefineYaml) { + Callback(fileInfo.absoluteFilePath()); + } + } + } + } +} + +int PluginManager::LoadPlugins() { + int count = 0; + LoopDirsPlugins(pluginDir, [&](QString PluginInfoDefaultYaml) { + qDebug() << QObject::tr("Get Plugin Definde: ").toStdString().c_str() << PluginInfoDefaultYaml; + YAML::Node config; + try { + config = YAML::LoadFile(PluginInfoDefaultYaml.toStdString()); + } catch(std::exception &e) { + qDebug() << QObject::tr("Get Plugin Error: ").toStdString().c_str() << e.what() << PluginInfoDefaultYaml; + return; + } + + PluginInfo Info; + Info.PluginInfoFullPath = PluginInfoDefaultYaml; + Info.PluginName = QString::fromStdString(config["plugin"]["name"].as()); + if (config["plugin"]["type"].as() == "qt") { + Info.PluginType = PluginInfo::QTPlugin; + } + + QString plugin = QFileInfo(PluginInfoDefaultYaml).absolutePath() + "/" + QString::fromStdString(config["plugin"]["file"].as()); + Info.Pluginfile = plugin; + + std::map PluginConfig = config["plugin"]["config"].as>(); + + QPluginLoader *Loader = new QPluginLoader(Info.Pluginfile); + if(Loader) { + if(Loader->load()) { + CorePluginInterface *Interface = qobject_cast(Loader->instance()); + if(Interface->load(PluginConfig)) { + Info.PluginLoader = Loader; + } + } + + this->PluginLists.append(Info); + + count++; + } + + }); + qDebug() << QObject::tr("Get Plugin Count: ").toStdString().c_str() << count; + return count; +} + +bool PluginManager::UnLoadPlugins() { + foreach(auto Info, this->PluginLists) { + if(Info.PluginType == PluginInfo::QTPlugin) { + QPluginLoader *Loader = reinterpret_cast(Info.PluginLoader); + if(Loader) { + delete Loader; + Info.PluginLoader = nullptr; + } + } + } + this->PluginLists.clear(); + return false; +} + +bool PluginManager::CallImagePost(QPixmap &pixmap) { + bool Result = false; + foreach(auto Info, this->PluginLists) { + if(Info.PluginType == PluginInfo::QTPlugin) { + qDebug() << QObject::tr("Call Plugin(ImagePost): ").toStdString().c_str() << Info.PluginName; + qDebug() << QObject::tr("Call Plugin(ImagePost) YAML FileName: ").toStdString().c_str() << Info.PluginInfoFullPath; + qDebug() << QObject::tr("Call Plugin(ImagePost) Qt Plugin: ").toStdString().c_str() << Info.Pluginfile; + if (Info.PluginType == PluginInfo::QTPlugin) { + QPluginLoader *Loader = reinterpret_cast(Info.PluginLoader); + if(Loader) { + CorePluginInterface *Interface = qobject_cast(Loader->instance()); + Interface->ImagePost(pixmap); + Result = true; + } + } + } + } + return Result; +} + +bool PluginManager::CallImageToPDFPost(QPixmap &pixmap) { + bool Result = false; + foreach(auto Info, this->PluginLists) { + if(Info.PluginType == PluginInfo::QTPlugin) { + qDebug() << QObject::tr("Call Plugin(ImageToPDFPost): ").toStdString().c_str() << Info.PluginName; + qDebug() << QObject::tr("Call Plugin(ImageToPDFPost) YAML FileName: ").toStdString().c_str() << Info.PluginInfoFullPath; + qDebug() << QObject::tr("Call Plugin(ImageToPDFPost) Qt Plugin: ").toStdString().c_str() << Info.Pluginfile; + if (Info.PluginType == PluginInfo::QTPlugin) { + QPluginLoader *Loader = reinterpret_cast(Info.PluginLoader); + if(Loader) { + CorePluginInterface *Interface = qobject_cast(Loader->instance()); + Interface->ImageToPDFPost(pixmap); + Result = true; + } + } + } + } + return Result; +} + +bool PluginManager::CallPrintPre(QPixmap &pixmap) { + bool Result = false; + foreach(auto Info, this->PluginLists) { + if(Info.PluginType == PluginInfo::QTPlugin) { + qDebug() << QObject::tr("Call Plugin(PrintPre): ").toStdString().c_str() << Info.PluginName; + qDebug() << QObject::tr("Call Plugin(PrintPre) YAML FileName: ").toStdString().c_str() << Info.PluginInfoFullPath; + qDebug() << QObject::tr("Call Plugin(PrintPre) Qt Plugin: ").toStdString().c_str() << Info.Pluginfile; + if (Info.PluginType == PluginInfo::QTPlugin) { + QPluginLoader *Loader = reinterpret_cast(Info.PluginLoader); + if(Loader) { + CorePluginInterface *Interface = qobject_cast(Loader->instance()); + Interface->PrintPre(pixmap); + Result = true; + } + } + } + } + return Result; +} +#endif //USE_PLUGIN_MANAGER diff --git a/src/core/pluginmanager.h b/src/core/pluginmanager.h new file mode 100644 index 0000000000..4b32c21ef0 --- /dev/null +++ b/src/core/pluginmanager.h @@ -0,0 +1,50 @@ +#ifndef PLUGINMANAGER_H +#define PLUGINMANAGER_H + +#ifdef USE_PLUGIN_MANAGER + +#define GET_BUILD_SET(x) #x +#define BUILD_DEFINE_CONV_C_STRING(x) GET_BUILD_SET(x) + +#include +#include +#include +#include + +const QString pluginDir = BUILD_DEFINE_CONV_C_STRING(PLUGIN_DIRECTORY); + +const QString PluginDefineYaml = "plugin.yaml"; + +typedef struct _PluginInfo { + QString PluginName; + QString PluginInfoFullPath; + enum Type { + QTPlugin, + PyPlugin, /*no used*/ + LuaPlugin /*no used*/ + }PluginType; + QString Pluginfile; + void *PluginLoader; +}PluginInfo; + +class PluginManager +{ + typedef void CallbackPluginLoads(QString PluginInfoDefaultYaml); +private: + PluginManager(); +public: + static PluginManager *getInstance(); + int LoadPlugins(); + bool UnLoadPlugins(); + bool CallImagePost(QPixmap &pixmap); + bool CallImageToPDFPost(QPixmap &pixmap); + bool CallPrintPre(QPixmap &pixmap); +private: + static void LoopDirsPlugins(QString Base, std::function Callback); +private: + QList PluginLists; +}; + +#endif //USE_PLUGIN_MANAGER + +#endif // PLUGINMANAGER_H diff --git a/src/main.cpp b/src/main.cpp index 23d3157e01..b5517313a0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -32,6 +32,9 @@ #include #include #endif +#ifdef USE_PLUGIN_MANAGER +#include "core/pluginmanager.h" +#endif #ifdef Q_OS_LINUX // source: https://github.com/ksnip/ksnip/issues/416 @@ -126,6 +129,9 @@ void reinitializeAsQApplication(int& argc, char* argv[]) int main(int argc, char* argv[]) { +#ifdef USE_PLUGIN_MANAGER + PluginManager::getInstance()->LoadPlugins(); +#endif #ifdef Q_OS_LINUX wayland_hacks(); #endif diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt index d2a629242b..6712ca6ac2 100644 --- a/src/tools/CMakeLists.txt +++ b/src/tools/CMakeLists.txt @@ -3,9 +3,11 @@ target_sources(flameshot PRIVATE pixelate/pixelatetool.h pixelate/pixelatetool.c target_sources(flameshot PRIVATE circle/circletool.h circle/circletool.cpp) target_sources(flameshot PRIVATE circlecount/circlecounttool.h circlecount/circlecounttool.cpp) target_sources(flameshot PRIVATE copy/copytool.h copy/copytool.cpp) +target_sources(flameshot PRIVATE print/printtool.h print/printtool.cpp) target_sources(flameshot PRIVATE exit/exittool.h exit/exittool.cpp) target_sources(flameshot PRIVATE sizeincrease/sizeincreasetool.h sizeincrease/sizeincreasetool.cpp) target_sources(flameshot PRIVATE sizedecrease/sizedecreasetool.h sizedecrease/sizedecreasetool.cpp) +target_sources(flameshot PRIVATE save-to-pdf/save-to-pdf.h save-to-pdf/save-to-pdf.cpp) target_sources( flameshot PRIVATE imgupload/storages/imgur/imguruploader.h diff --git a/src/tools/capturetool.h b/src/tools/capturetool.h index a095d61857..0783e3940b 100644 --- a/src/tools/capturetool.h +++ b/src/tools/capturetool.h @@ -41,13 +41,15 @@ class CaptureTool : public QObject TYPE_OPEN_APP = 14, TYPE_PIXELATE = 15, TYPE_REDO = 16, - TYPE_PIN = 17, - TYPE_TEXT = 18, - TYPE_CIRCLECOUNT = 19, - TYPE_SIZEINCREASE = 20, - TYPE_SIZEDECREASE = 21, - TYPE_INVERT = 22, - TYPE_ACCEPT = 23, + TYPE_PRINT = 17, + TYPE_PIN = 18, + TYPE_TEXT = 19, + TYPE_CIRCLECOUNT = 20, + TYPE_SIZEINCREASE = 21, + TYPE_SIZEDECREASE = 22, + TYPE_INVERT = 23, + TYPE_ACCEPT = 24, + TYPE_SAVE_TO_PDF = 25 }; Q_ENUM(Type); diff --git a/src/tools/print/printtool.cpp b/src/tools/print/printtool.cpp new file mode 100644 index 0000000000..b73cb23ab3 --- /dev/null +++ b/src/tools/print/printtool.cpp @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-FileCopyrightText: 2017-2019 Ouyang Chunhui & Contributor + +#include "printtool.h" +#include "src/utils/screenshotsaver.h" +#include + +PrintTool::PrintTool(QObject* parent) + : AbstractActionTool(parent) +{} + +bool PrintTool::closeOnButtonPressed() const +{ + return true; +} + +QIcon PrintTool::icon(const QColor& background, bool inEditor) const +{ + Q_UNUSED(inEditor) + return QIcon(iconPath(background) + "content-print.svg"); +} +QString PrintTool::name() const +{ + return tr("Print"); +} + +CaptureTool::Type PrintTool::type() const +{ + return CaptureTool::TYPE_PRINT; +} + +QString PrintTool::description() const +{ + return tr("Print"); +} + +CaptureTool* PrintTool::copy(QObject* parent) +{ + return new PrintTool(parent); +} + +void PrintTool::pressed(CaptureContext& context) +{ + emit requestAction(REQ_CLEAR_SELECTION); + context.request.addTask(CaptureRequest::PRINT_SYSTEM); + emit requestAction(REQ_CAPTURE_DONE_OK); + emit requestAction(REQ_CLOSE_GUI); +} diff --git a/src/tools/print/printtool.h b/src/tools/print/printtool.h new file mode 100644 index 0000000000..42ae384e7b --- /dev/null +++ b/src/tools/print/printtool.h @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-FileCopyrightText: 2017-2019 Ouyang Chunhui & Contributor + +#pragma once + +#include "src/tools/abstractactiontool.h" + +class PrintTool : public AbstractActionTool +{ + Q_OBJECT +public: + explicit PrintTool(QObject* parent = nullptr); + bool closeOnButtonPressed() const override; + + QIcon icon(const QColor& background, bool inEditor) const override; + QString name() const override; + QString description() const override; + + CaptureTool* copy(QObject* parent = nullptr) override; + +protected: + CaptureTool::Type type() const override; + +public slots: + void pressed(CaptureContext& context) override; +}; diff --git a/src/tools/save-to-pdf/save-to-pdf.cpp b/src/tools/save-to-pdf/save-to-pdf.cpp new file mode 100644 index 0000000000..46fbc7e639 --- /dev/null +++ b/src/tools/save-to-pdf/save-to-pdf.cpp @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-FileCopyrightText: 2017-2019 Ouyang Chunhui & Contributor + +#include "save-to-pdf.h" +#include "src/utils/screenshotsaver.h" +#include + +SaveToPDFTool::SaveToPDFTool(QObject* parent) + : AbstractActionTool(parent) +{} + +bool SaveToPDFTool::closeOnButtonPressed() const +{ + return true; +} + +QIcon SaveToPDFTool::icon(const QColor& background, bool inEditor) const +{ + Q_UNUSED(inEditor) + return QIcon(iconPath(background) + "save-to-pdf.svg"); +} +QString SaveToPDFTool::name() const +{ + return tr("Save To PDF"); +} + +CaptureTool::Type SaveToPDFTool::type() const +{ + return CaptureTool::TYPE_PRINT; +} + +QString SaveToPDFTool::description() const +{ + return tr("Save To PDF"); +} + +CaptureTool* SaveToPDFTool::copy(QObject* parent) +{ + return new SaveToPDFTool(parent); +} + +void SaveToPDFTool::pressed(CaptureContext& context) +{ + emit requestAction(REQ_CLEAR_SELECTION); + context.request.addTask(CaptureRequest::SAVE_TO_PDF); + emit requestAction(REQ_CAPTURE_DONE_OK); + emit requestAction(REQ_CLOSE_GUI); +} diff --git a/src/tools/save-to-pdf/save-to-pdf.h b/src/tools/save-to-pdf/save-to-pdf.h new file mode 100644 index 0000000000..97c35f5351 --- /dev/null +++ b/src/tools/save-to-pdf/save-to-pdf.h @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-FileCopyrightText: 2017-2019 Ouyang Chunhui & Contributor + +#pragma once + +#include "src/tools/abstractactiontool.h" + +class SaveToPDFTool : public AbstractActionTool +{ + Q_OBJECT +public: + explicit SaveToPDFTool(QObject* parent = nullptr); + bool closeOnButtonPressed() const override; + + QIcon icon(const QColor& background, bool inEditor) const override; + QString name() const override; + QString description() const override; + + CaptureTool* copy(QObject* parent = nullptr) override; + +protected: + CaptureTool::Type type() const override; + +public slots: + void pressed(CaptureContext& context) override; +}; diff --git a/src/tools/toolfactory.cpp b/src/tools/toolfactory.cpp index 98ed0ab3d7..5910fd821f 100644 --- a/src/tools/toolfactory.cpp +++ b/src/tools/toolfactory.cpp @@ -20,9 +20,11 @@ #include "rectangle/rectangletool.h" #include "redo/redotool.h" #include "save/savetool.h" +#include "save-to-pdf/save-to-pdf.h" #include "selection/selectiontool.h" #include "sizedecrease/sizedecreasetool.h" #include "sizeincrease/sizeincreasetool.h" +#include "print/printtool.h" #include "text/texttool.h" #include "undo/undotool.h" @@ -48,6 +50,7 @@ CaptureTool* ToolFactory::CreateTool(CaptureTool::Type t, QObject* parent) if_TYPE_return_TOOL(TYPE_UNDO, UndoTool); if_TYPE_return_TOOL(TYPE_COPY, CopyTool); if_TYPE_return_TOOL(TYPE_SAVE, SaveTool); + if_TYPE_return_TOOL(TYPE_SAVE_TO_PDF, SaveToPDFTool); if_TYPE_return_TOOL(TYPE_EXIT, ExitTool); if_TYPE_return_TOOL(TYPE_IMAGEUPLOADER, ImgUploaderTool); #if !defined(Q_OS_MACOS) @@ -55,6 +58,7 @@ CaptureTool* ToolFactory::CreateTool(CaptureTool::Type t, QObject* parent) #endif if_TYPE_return_TOOL(TYPE_PIXELATE, PixelateTool); if_TYPE_return_TOOL(TYPE_REDO, RedoTool); + if_TYPE_return_TOOL(TYPE_PRINT, PrintTool); if_TYPE_return_TOOL(TYPE_PIN, PinTool); if_TYPE_return_TOOL(TYPE_TEXT, TextTool); if_TYPE_return_TOOL(TYPE_CIRCLECOUNT, CircleCountTool); diff --git a/src/utils/confighandler.cpp b/src/utils/confighandler.cpp index 485fb26575..91a87007d0 100644 --- a/src/utils/confighandler.cpp +++ b/src/utils/confighandler.cpp @@ -142,6 +142,7 @@ static QMap> recognizedShortcuts = { SHORTCUT("TYPE_UNDO" , "Ctrl+Z" ), SHORTCUT("TYPE_COPY" , "Ctrl+C" ), SHORTCUT("TYPE_SAVE" , "Ctrl+S" ), + SHORTCUT("TYPE_SAVE_TO_PDF" , ), SHORTCUT("TYPE_ACCEPT" , "Return" ), SHORTCUT("TYPE_EXIT" , "Ctrl+Q" ), SHORTCUT("TYPE_IMAGEUPLOADER" , ), @@ -179,6 +180,7 @@ static QMap> recognizedShortcuts = { SHORTCUT("TYPE_SIZEINCREASE" , ), SHORTCUT("TYPE_SIZEDECREASE" , ), SHORTCUT("TYPE_CIRCLECOUNT" , ), + SHORTCUT("TYPE_PRINT", ) }; // clang-format on diff --git a/src/utils/screenshotsaver.cpp b/src/utils/screenshotsaver.cpp index f7f5a7102b..5932681583 100644 --- a/src/utils/screenshotsaver.cpp +++ b/src/utils/screenshotsaver.cpp @@ -23,6 +23,10 @@ #include #include #include +#include +#ifdef USE_PLUGIN_MANAGER +#include "core/pluginmanager.h" +#endif #if defined(Q_OS_MACOS) #include "src/widgets/capture/capturewidget.h" #endif @@ -101,6 +105,23 @@ QString ShowSaveFileDialog(const QString& title, const QString& directory) } } +QString ShowSaveToPDFDialog(const QString& title, const QString& directory) +{ + QFileDialog dialog(nullptr, title, directory); + dialog.setAcceptMode(QFileDialog::AcceptSave); + + // Build string list of supported image formats + QStringList mimeTypeList; + mimeTypeList.append("application/pdf"); + dialog.setMimeTypeFilters(mimeTypeList); + if (dialog.exec() == QDialog::Accepted) { + return dialog.selectedFiles().constFirst(); + } else { + return {}; + } +} + + void saveToClipboardMime(const QPixmap& capture, const QString& imageType) { QByteArray array; @@ -168,6 +189,49 @@ void saveToClipboard(const QPixmap& capture) } } +bool saveToPDF(const QPixmap& capture) { + bool okay = false; + QString savePath; + ConfigHandler config; + savePath = QDir::toNativeSeparators( + ShowSaveToPDFDialog(QObject::tr("Save screenshot To PDF"), savePath)); + if(savePath.endsWith(".pdf", Qt::CaseInsensitive)) { + QPrinter printer(QPrinter::HighResolution); + printer.setOutputFormat(QPrinter::PdfFormat); + printer.setPageOrientation(QPageLayout::Landscape); + printer.setPageSize(QPageSize::A4); + printer.setOutputFileName(savePath); + QPainter painter(&printer); + QPixmap new_capture = capture.scaled(printer.width(), printer.height(), Qt::KeepAspectRatio, Qt::SmoothTransformation); + QRect rect(0, 0, new_capture.size().width(), new_capture.size().height()); + painter.drawPixmap(rect, new_capture); + okay = true; + } + + if(okay) { + QString pathNoFile = + savePath.left(savePath.lastIndexOf(QDir::separator())); + + ConfigHandler().setSavePath(pathNoFile); + + QString msg = QObject::tr("Capture saved as ") + savePath; + AbstractLogger().attachNotificationPath(savePath) << msg; + + if (config.copyPathAfterSave()) { + FlameshotDaemon::copyToClipboard( + savePath, QObject::tr("Path copied to clipboard as ") + savePath); + } + } else { + QString msg = QObject::tr("Error trying to save as ") + savePath; + QMessageBox saveErrBox( + QMessageBox::Warning, QObject::tr("Save Error"), msg); + saveErrBox.setWindowIcon(QIcon(GlobalValues::iconPath())); + saveErrBox.exec(); + } + + return okay; +} + bool saveToFilesystemGUI(const QPixmap& capture) { bool okay = false; diff --git a/src/utils/screenshotsaver.h b/src/utils/screenshotsaver.h index 9face3468b..a1675eadb9 100644 --- a/src/utils/screenshotsaver.h +++ b/src/utils/screenshotsaver.h @@ -11,6 +11,8 @@ bool saveToFilesystem(const QPixmap& capture, const QString& path, const QString& messagePrefix = ""); QString ShowSaveFileDialog(const QString& title, const QString& directory); +QString ShowSaveToPDFDialog(const QString& title, const QString& directory); void saveToClipboardMime(const QPixmap& capture, const QString& imageType); void saveToClipboard(const QPixmap& capture); bool saveToFilesystemGUI(const QPixmap& capture); +bool saveToPDF(const QPixmap& capture); diff --git a/src/widgets/capture/capturetoolbutton.cpp b/src/widgets/capture/capturetoolbutton.cpp index bc51eb7272..be211d1db3 100644 --- a/src/widgets/capture/capturetoolbutton.cpp +++ b/src/widgets/capture/capturetoolbutton.cpp @@ -146,17 +146,18 @@ static std::map buttonTypeOrder { CaptureTool::TYPE_SELECTIONINDICATOR, 11 }, { CaptureTool::TYPE_MOVESELECTION, 12 }, { CaptureTool::TYPE_UNDO, 13 }, { CaptureTool::TYPE_REDO, 14 }, { CaptureTool::TYPE_COPY, 15 }, - { CaptureTool::TYPE_SAVE, 16 }, { CaptureTool::TYPE_IMAGEUPLOADER, 17 }, - { CaptureTool::TYPE_ACCEPT, 18 }, + { CaptureTool::TYPE_SAVE, 16 }, { CaptureTool::TYPE_SAVE_TO_PDF, 17}, + { CaptureTool::TYPE_PRINT, 18}, { CaptureTool::TYPE_IMAGEUPLOADER, 29 }, + { CaptureTool::TYPE_ACCEPT, 20 }, #if !defined(Q_OS_MACOS) - { CaptureTool::TYPE_OPEN_APP, 19 }, { CaptureTool::TYPE_EXIT, 20 }, - { CaptureTool::TYPE_PIN, 21 }, + { CaptureTool::TYPE_OPEN_APP, 21 }, { CaptureTool::TYPE_EXIT, 22 }, + { CaptureTool::TYPE_PIN, 23 }, #else - { CaptureTool::TYPE_EXIT, 19 }, { CaptureTool::TYPE_PIN, 20 }, + { CaptureTool::TYPE_EXIT, 21 }, { CaptureTool::TYPE_PIN, 22 }, #endif - { CaptureTool::TYPE_SIZEINCREASE, 22 }, - { CaptureTool::TYPE_SIZEDECREASE, 23 }, + { CaptureTool::TYPE_SIZEINCREASE, 24 }, + { CaptureTool::TYPE_SIZEDECREASE, 25 }, }; int CaptureToolButton::getPriorityByButton(CaptureTool::Type b) @@ -174,7 +175,8 @@ QList CaptureToolButton::iterableButtonTypes = { CaptureTool::TYPE_CIRCLECOUNT, CaptureTool::TYPE_PIXELATE, CaptureTool::TYPE_MOVESELECTION, CaptureTool::TYPE_UNDO, CaptureTool::TYPE_REDO, CaptureTool::TYPE_COPY, - CaptureTool::TYPE_SAVE, CaptureTool::TYPE_EXIT, + CaptureTool::TYPE_SAVE, CaptureTool::TYPE_SAVE_TO_PDF, + CaptureTool::TYPE_PRINT, CaptureTool::TYPE_EXIT, CaptureTool::TYPE_IMAGEUPLOADER, #if !defined(Q_OS_MACOS) CaptureTool::TYPE_OPEN_APP, diff --git a/src/widgets/capture/capturewidget.cpp b/src/widgets/capture/capturewidget.cpp index 49ebff9720..9245694680 100644 --- a/src/widgets/capture/capturewidget.cpp +++ b/src/widgets/capture/capturewidget.cpp @@ -296,6 +296,7 @@ void CaptureWidget::initButtons() // Remove irrelevant buttons from both lists for (auto* buttonList : { &allButtonTypes, &visibleButtonTypes }) { buttonList->removeOne(CaptureTool::TYPE_SAVE); + buttonList->removeOne(CaptureTool::TYPE_SAVE_TO_PDF); buttonList->removeOne(CaptureTool::TYPE_COPY); buttonList->removeOne(CaptureTool::TYPE_IMAGEUPLOADER); buttonList->removeOne(CaptureTool::TYPE_OPEN_APP);