File indexing completed on 2025-02-02 04:26:11
0001 /* 0002 * SPDX-FileCopyrightText: 2015 Boudhayan Gupta <bgupta@kde.org> 0003 * 0004 * SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "ExportMenu.h" 0008 #include "CaptureWindow.h" 0009 #include "SpectacleCore.h" 0010 #include "WidgetWindowUtils.h" 0011 #include "spectacle_gui_debug.h" 0012 #include "settings.h" 0013 0014 #include <KApplicationTrader> 0015 #include <KIO/ApplicationLauncherJob> 0016 #include <kio_version.h> 0017 #include <KIO/JobUiDelegateFactory> 0018 #include <KIO/OpenFileManagerWindowJob> 0019 #include <KIO/OpenUrlJob> 0020 #include <KLocalizedString> 0021 #include <KNotificationJobUiDelegate> 0022 #include <KStandardAction> 0023 #include <KStandardShortcut> 0024 0025 #include <QJsonArray> 0026 #include <QMimeDatabase> 0027 #include <QPrintDialog> 0028 #include <QPrinter> 0029 #include <QTimer> 0030 #include <QWindow> 0031 #include <chrono> 0032 0033 using namespace std::chrono_literals; 0034 using namespace Qt::StringLiterals; 0035 0036 class ExportMenuSingleton 0037 { 0038 public: 0039 ExportMenu self; 0040 }; 0041 0042 Q_GLOBAL_STATIC(ExportMenuSingleton, privateExportMenuSelf) 0043 0044 ExportMenu::ExportMenu(QWidget *parent) 0045 : SpectacleMenu(parent) 0046 #ifdef PURPOSE_FOUND 0047 , mUpdatedImageAvailable(true) 0048 , mPurposeMenu(new Purpose::Menu) 0049 #endif 0050 { 0051 addAction(QIcon::fromTheme(u"document-open-folder"_s), 0052 i18n("Open Default Screenshots Folder"), 0053 this, &ExportMenu::openScreenshotsFolder); 0054 addAction(KStandardAction::print(this, &ExportMenu::openPrintDialog, this)); 0055 0056 #ifdef PURPOSE_FOUND 0057 loadPurposeMenu(); 0058 connect(ExportManager::instance(), &ExportManager::imageChanged, this, &ExportMenu::onImageChanged); 0059 #endif 0060 0061 addSeparator(); 0062 getKServiceItems(); 0063 } 0064 0065 ExportMenu *ExportMenu::instance() 0066 { 0067 return &privateExportMenuSelf->self; 0068 } 0069 0070 void ExportMenu::onImageChanged() 0071 { 0072 #ifdef PURPOSE_FOUND 0073 // mark cached image as stale 0074 mUpdatedImageAvailable = true; 0075 mPurposeMenu->clear(); 0076 #endif 0077 } 0078 0079 void ExportMenu::getKServiceItems() 0080 { 0081 // populate all locally installed applications and services 0082 // which can handle images first 0083 0084 const KService::List services = KApplicationTrader::queryByMimeType(u"image/png"_s); 0085 0086 for (auto service : services) { 0087 const QString name = service->name().replace('&'_L1, "&&"_L1); 0088 QAction *action = new QAction(QIcon::fromTheme(service->icon()), name, this); 0089 0090 connect(action, &QAction::triggered, this, [this, service]() { 0091 auto captureWindow = qobject_cast<CaptureWindow *>(getWidgetTransientParent(this)); 0092 if(captureWindow && !captureWindow->accept()) { 0093 return; 0094 } 0095 QUrl filename; 0096 if(ExportManager::instance()->isImageSavedNotInTemp()) { 0097 filename = Settings::self()->lastImageSaveLocation(); 0098 } else { 0099 filename = ExportManager::instance()->getAutosaveFilename(); 0100 SpectacleCore::instance()->syncExportImage(); 0101 ExportManager::instance()->exportImage(ExportManager::Save, filename); 0102 } 0103 0104 auto *job = new KIO::ApplicationLauncherJob(service); 0105 auto *delegate = new KNotificationJobUiDelegate; 0106 delegate->setAutoErrorHandlingEnabled(true); 0107 job->setUiDelegate(delegate); 0108 0109 job->setUrls({filename}); 0110 job->start(); 0111 }); 0112 addAction(action); 0113 } 0114 0115 // now let the user manually chose an application to open the 0116 // image with 0117 0118 addSeparator(); 0119 0120 QAction *openWith = new QAction(i18n("Other Application..."), this); 0121 openWith->setShortcuts(KStandardShortcut::open()); 0122 0123 connect(openWith, &QAction::triggered, this, [this]() { 0124 auto captureWindow = qobject_cast<CaptureWindow *>(getWidgetTransientParent(this)); 0125 if(captureWindow && !captureWindow->accept()) { 0126 return; 0127 } 0128 QUrl filename; 0129 if(ExportManager::instance()->isImageSavedNotInTemp()) { 0130 filename = Settings::self()->lastImageSaveLocation(); 0131 } else { 0132 filename = ExportManager::instance()->getAutosaveFilename(); 0133 SpectacleCore::instance()->syncExportImage(); 0134 ExportManager::instance()->exportImage(ExportManager::Save, filename); 0135 } 0136 0137 auto job = new KIO::ApplicationLauncherJob; 0138 job->setUiDelegate(KIO::createDefaultJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, window())); 0139 job->setUrls({filename}); 0140 job->start(); 0141 }); 0142 addAction(openWith); 0143 } 0144 0145 #ifdef PURPOSE_FOUND 0146 void ExportMenu::loadPurposeMenu() 0147 { 0148 // attach the menu 0149 auto purposeMenu = mPurposeMenu.get(); 0150 QAction *purposeMenuAction = addMenu(purposeMenu); 0151 purposeMenuAction->setObjectName("purposeMenuAction"); 0152 purposeMenuAction->setText(i18n("Share")); 0153 purposeMenuAction->setIcon(QIcon::fromTheme(u"document-share"_s)); 0154 0155 // set up the callback signal 0156 connect(purposeMenu, &Purpose::Menu::finished, this, [this](const QJsonObject &output, int error, const QString &message) { 0157 if (error) { 0158 Q_EMIT imageShared(error, message); 0159 } else { 0160 Q_EMIT imageShared(error, output[u"url"_s].toString()); 0161 } 0162 }); 0163 0164 // update available options based on the latest picture 0165 connect(purposeMenu, &QMenu::aboutToShow, this, [this]() { 0166 loadPurposeItems(); 0167 setWidgetTransientParentToWidget(mPurposeMenu.get(), this); 0168 }); 0169 } 0170 0171 void ExportMenu::loadPurposeItems() 0172 { 0173 if (!mUpdatedImageAvailable) { 0174 return; 0175 } 0176 0177 // updated image available, we lazily load it now 0178 const QString dataUri = ExportManager::instance()->tempSave().toString(); 0179 mUpdatedImageAvailable = false; 0180 0181 auto mimeType = QMimeDatabase().mimeTypeForFile(dataUri).name(); 0182 QJsonObject inputData = { 0183 {u"mimeType"_s, mimeType}, 0184 {u"urls"_s, QJsonArray({dataUri})} 0185 }; 0186 mPurposeMenu->model()->setInputData(inputData); 0187 mPurposeMenu->model()->setPluginType(u"Export"_s); 0188 mPurposeMenu->reload(); 0189 } 0190 #endif 0191 0192 void ExportMenu::openScreenshotsFolder() 0193 { 0194 auto job = new KIO::OpenUrlJob(Settings::imageSaveLocation()); 0195 job->setUiDelegate(KIO::createDefaultJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, this)); 0196 job->start(); 0197 } 0198 0199 void ExportMenu::openPrintDialog() 0200 { 0201 if (auto captureWindow = qobject_cast<CaptureWindow *>(getWidgetTransientParent(this))) { 0202 captureWindow->accept(); 0203 } 0204 auto printer = new QPrinter(QPrinter::HighResolution); 0205 auto dialog = new QPrintDialog(printer); 0206 dialog->setAttribute(Qt::WA_DeleteOnClose); 0207 0208 // properly set the transientparent chain 0209 setWidgetTransientParent(dialog, getWidgetTransientParent(this)); 0210 0211 connect(dialog, &QDialog::finished, dialog, [printer](int result){ 0212 if (result == QDialog::Accepted) { 0213 ExportManager::instance()->doPrint(printer); 0214 } 0215 delete printer; 0216 }); 0217 0218 dialog->setVisible(true); 0219 } 0220 0221 #include "moc_ExportMenu.cpp"