diff --git a/flameshot.pro b/flameshot.pro index 46c42dd6..483acb74 100644 --- a/flameshot.pro +++ b/flameshot.pro @@ -86,7 +86,11 @@ SOURCES += src/main.cpp\ src/core/resourceexporter.cpp \ src/capture/widget/notifierbox.cpp \ src/utils/desktopinfo.cpp \ - src/utils/dbusutils.cpp + src/utils/dbusutils.cpp \ + src/capture/workers/launcher/applauncherwidget.cpp \ + src/capture/tools/applauncher.cpp \ + src/utils/desktopfileparse.cpp \ + src/capture/workers/launcher/launcheritemdelegate.cpp HEADERS += \ src/capture/widget/buttonhandler.h \ @@ -137,7 +141,11 @@ HEADERS += \ src/core/resourceexporter.h \ src/capture/widget/notifierbox.h \ src/utils/desktopinfo.h \ - src/utils/dbusutils.h + src/utils/dbusutils.h \ + src/capture/workers/launcher/applauncherwidget.h \ + src/capture/tools/applauncher.h \ + src/utils/desktopfileparse.h \ + src/capture/workers/launcher/launcheritemdelegate.h RESOURCES += \ graphics.qrc diff --git a/graphics.qrc b/graphics.qrc index 682941ca..c5b1c71f 100644 --- a/graphics.qrc +++ b/graphics.qrc @@ -39,5 +39,7 @@ img/configBlack/name_edition.png img/buttonIconsBlack/size_indicator.png img/buttonIconsWhite/size_indicator.png + img/buttonIconsBlack/open_with.png + img/buttonIconsWhite/open_with.png diff --git a/img/buttonIconsBlack/open_with.png b/img/buttonIconsBlack/open_with.png new file mode 100644 index 00000000..2b4e3c6c Binary files /dev/null and b/img/buttonIconsBlack/open_with.png differ diff --git a/img/buttonIconsBlack/open_with.svg b/img/buttonIconsBlack/open_with.svg new file mode 100644 index 00000000..39bc1c09 --- /dev/null +++ b/img/buttonIconsBlack/open_with.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/buttonIconsWhite/open_with.png b/img/buttonIconsWhite/open_with.png new file mode 100644 index 00000000..b4ad3d9d Binary files /dev/null and b/img/buttonIconsWhite/open_with.png differ diff --git a/img/buttonIconsWhite/open_with.svg b/img/buttonIconsWhite/open_with.svg new file mode 100644 index 00000000..cc6e5344 --- /dev/null +++ b/img/buttonIconsWhite/open_with.svg @@ -0,0 +1,65 @@ + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/src/capture/tools/applauncher.cpp b/src/capture/tools/applauncher.cpp new file mode 100644 index 00000000..ea66260a --- /dev/null +++ b/src/capture/tools/applauncher.cpp @@ -0,0 +1,63 @@ +// Copyright 2017 Alejandro Sirgo Rica +// +// This file is part of Flameshot. +// +// Flameshot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Flameshot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Flameshot. If not, see . + +#include "applauncher.h" + +AppLauncher::AppLauncher(QObject *parent) : CaptureTool(parent) +{ + +} + +int AppLauncher::id() const { + return 0; +} + +bool AppLauncher::isSelectable() const { + return false; +} + +QString AppLauncher::iconName() const { + return "open_with.png"; +} + +QString AppLauncher::name() const { + return tr("App Launcher"); +} + +QString AppLauncher::description() const { + return tr("Choose an app to open the capture"); +} + +CaptureTool::ToolWorkType AppLauncher::toolType() const { + return TYPE_WORKER; +} + +void AppLauncher::processImage( + QPainter &painter, + const QVector &points, + const QColor &color, + const int thickness) +{ + Q_UNUSED(painter); + Q_UNUSED(points); + Q_UNUSED(color); + Q_UNUSED(thickness); +} + +void AppLauncher::onPressed() { + Q_EMIT requestAction(REQ_OPEN_APP); +} diff --git a/src/capture/tools/applauncher.h b/src/capture/tools/applauncher.h new file mode 100644 index 00000000..8a4cb8bb --- /dev/null +++ b/src/capture/tools/applauncher.h @@ -0,0 +1,46 @@ +// Copyright 2017 Alejandro Sirgo Rica +// +// This file is part of Flameshot. +// +// Flameshot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Flameshot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Flameshot. If not, see . + +#ifndef APPOPENER_H +#define APPOPENER_H + +#include "capturetool.h" + +class AppLauncher : public CaptureTool +{ + Q_OBJECT +public: + explicit AppLauncher(QObject *parent = nullptr); + + int id() const override; + bool isSelectable() const override; + ToolWorkType toolType() const override; + + QString iconName() const override; + QString name() const override; + QString description() const override; + + void processImage( + QPainter &painter, + const QVector &points, + const QColor &color, + const int thickness) override; + + void onPressed() override; +}; + +#endif // APPOPENER_H diff --git a/src/capture/tools/capturetool.h b/src/capture/tools/capturetool.h index 199c92b7..c4884ea8 100644 --- a/src/capture/tools/capturetool.h +++ b/src/capture/tools/capturetool.h @@ -45,6 +45,7 @@ public: REQ_TO_CLIPBOARD, REQ_UPLOAD_TO_IMGUR, REQ_MOVE_MODE, + REQ_OPEN_APP, }; explicit CaptureTool(QObject *parent = nullptr); diff --git a/src/capture/tools/toolfactory.cpp b/src/capture/tools/toolfactory.cpp index efff2672..294c1811 100644 --- a/src/capture/tools/toolfactory.cpp +++ b/src/capture/tools/toolfactory.cpp @@ -30,6 +30,7 @@ #include "selectiontool.h" #include "sizeindicatortool.h" #include "undotool.h" +#include "applauncher.h" ToolFactory::ToolFactory(QObject *parent) : QObject(parent) { @@ -84,6 +85,9 @@ CaptureTool* ToolFactory::CreateTool( case CaptureButton::TYPE_UNDO: tool = new UndoTool(parent); break; + case CaptureButton::TYPE_OPEN_APP: + tool = new AppLauncher(parent); + break; default: tool = nullptr; break; diff --git a/src/capture/widget/capturebutton.cpp b/src/capture/widget/capturebutton.cpp index 2b123fba..fe86dd03 100644 --- a/src/capture/widget/capturebutton.cpp +++ b/src/capture/widget/capturebutton.cpp @@ -190,6 +190,7 @@ static std::map buttonTypeOrder { { CaptureButton::TYPE_SAVE, 11 }, { CaptureButton::TYPE_EXIT, 12 }, { CaptureButton::TYPE_IMAGEUPLOADER, 13 }, + { CaptureButton::TYPE_OPEN_APP, 14 }, }; int CaptureButton::getPriorityByButton(CaptureButton::ButtonType b) { @@ -197,7 +198,6 @@ int CaptureButton::getPriorityByButton(CaptureButton::ButtonType b) { return it == buttonTypeOrder.cend() ? (int)buttonTypeOrder.size() : it->second; } - QVector CaptureButton::iterableButtonTypes = { CaptureButton::TYPE_PENCIL, CaptureButton::TYPE_LINE, @@ -213,4 +213,5 @@ QVector CaptureButton::iterableButtonTypes = { CaptureButton::TYPE_SAVE, CaptureButton::TYPE_EXIT, CaptureButton::TYPE_IMAGEUPLOADER, + CaptureButton::TYPE_OPEN_APP, }; diff --git a/src/capture/widget/capturebutton.h b/src/capture/widget/capturebutton.h index 385f3936..44877815 100644 --- a/src/capture/widget/capturebutton.h +++ b/src/capture/widget/capturebutton.h @@ -48,6 +48,7 @@ public: TYPE_SAVE, TYPE_EXIT, TYPE_IMAGEUPLOADER, + TYPE_OPEN_APP, }; CaptureButton() = delete; diff --git a/src/capture/widget/capturewidget.cpp b/src/capture/widget/capturewidget.cpp index d3bfbfe9..8b080ef8 100644 --- a/src/capture/widget/capturewidget.cpp +++ b/src/capture/widget/capturewidget.cpp @@ -470,6 +470,9 @@ void CaptureWidget::handleButtonSignal(CaptureTool::Request r) { case CaptureTool::REQ_UPLOAD_TO_IMGUR: uploadToImgur(); break; + case CaptureTool::REQ_OPEN_APP: + openWithProgram(); + break; case CaptureTool::REQ_MOVE_MODE: m_state = CaptureButton::TYPE_MOVESELECTION; if (m_lastPressedButton) { @@ -613,6 +616,12 @@ void CaptureWidget::uploadToImgur() { close(); } +void CaptureWidget::openWithProgram() { + m_captureDone = true; + ResourceExporter().captureToProgram(pixmap()); + close(); +} + QRect CaptureWidget::extendedSelection() const { if (m_selection.isNull()) return QRect(); diff --git a/src/capture/widget/capturewidget.h b/src/capture/widget/capturewidget.h index 5adff69c..d96beb9e 100644 --- a/src/capture/widget/capturewidget.h +++ b/src/capture/widget/capturewidget.h @@ -60,6 +60,7 @@ private slots: void copyScreenshot(); void saveScreenshot(); void uploadToImgur(); + void openWithProgram(); bool undo(); void leftResize(); diff --git a/src/capture/workers/launcher/applauncherwidget.cpp b/src/capture/workers/launcher/applauncherwidget.cpp new file mode 100644 index 00000000..f5a25723 --- /dev/null +++ b/src/capture/workers/launcher/applauncherwidget.cpp @@ -0,0 +1,91 @@ +// Copyright 2017 Alejandro Sirgo Rica +// +// This file is part of Flameshot. +// +// Flameshot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Flameshot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Flameshot. If not, see . + +#include "applauncherwidget.h" +#include "src/utils/desktopfileparse.h" +#include "src/utils/filenamehandler.h" +#include "src/capture/workers/launcher/launcheritemdelegate.h" +#include +#include +#include +#include +#include +#include +#include + +AppLauncherWidget::AppLauncherWidget(const QPixmap &p, QWidget *parent): + QWidget(parent), m_pixmap(p) +{ + setAttribute(Qt::WA_DeleteOnClose); + m_tempFile = FileNameHandler().generateAbsolutePath("/tmp") + ".png"; + bool ok = p.save(m_tempFile); + if (!ok) { + // TO DO + return; + } + QString dir = "/usr/share/applications/"; + QString dirLocal = "~/.local/share/applications/"; + QDir appsDirLocal(dirLocal); + QDir appsDir(dir); + + QStringList entries = appsDir.entryList(QDir::NoDotAndDotDot | QDir::Files); + QStringList entriesLocal = appsDirLocal.entryList(QDir::NoDotAndDotDot | QDir::Files); + + DesktopFileParse parser; + QList appList; + for (QString file: entries){ + DesktopAppData app = parser.parseDesktopFile(dir + file, ok); + if (ok) { + appList.append(app); + } + } + for (QString file: entriesLocal){ + DesktopAppData app = parser.parseDesktopFile(dirLocal + file, ok); + if (ok) { + appList.append(app); + } + } + auto layout = new QHBoxLayout(this); + auto *listView = new QListWidget(this); + listView->setItemDelegate(new launcherItemDelegate()); + listView->setViewMode(QListWidget::IconMode); + listView->setResizeMode(QListView::Adjust); + listView->setSpacing(4); + listView->setFlow(QListView::LeftToRight); + listView->setDragEnabled(false); + + for (auto app: appList) { + QListWidgetItem *buttonItem = new QListWidgetItem(listView); + buttonItem->setData(Qt::DecorationRole, app.icon); + buttonItem->setData(Qt::DisplayRole, app.name); + buttonItem->setData(Qt::UserRole, app.exec); + QColor foregroundColor = this->palette().color(QWidget::foregroundRole()); + buttonItem->setForeground(foregroundColor); + + buttonItem->setIcon(app.icon); + buttonItem->setText(app.name); + buttonItem->setToolTip(app.description); + } + connect(listView, &QListWidget::clicked, this, &AppLauncherWidget::launch); + layout->addWidget(listView); +} + +void AppLauncherWidget::launch(const QModelIndex &index) { + QString command = index.data(Qt::UserRole).toString().replace( + QRegExp("(\%.)"), m_tempFile); + QProcess::startDetached(command); +} diff --git a/src/capture/workers/launcher/applauncherwidget.h b/src/capture/workers/launcher/applauncherwidget.h new file mode 100644 index 00000000..d3d73bf8 --- /dev/null +++ b/src/capture/workers/launcher/applauncherwidget.h @@ -0,0 +1,38 @@ +// Copyright 2017 Alejandro Sirgo Rica +// +// This file is part of Flameshot. +// +// Flameshot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Flameshot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Flameshot. If not, see . + +#ifndef APPLAUNCHERWIDGET_H +#define APPLAUNCHERWIDGET_H + +#include + +class AppLauncherWidget: public QWidget +{ + Q_OBJECT +public: + explicit AppLauncherWidget(const QPixmap &p, QWidget *parent = nullptr); + +private: + QPixmap m_pixmap; + QString m_tempFile; + +private slots: + void launch(const QModelIndex &index); + +}; + +#endif // APPLAUNCHERWIDGET_H diff --git a/src/capture/workers/launcher/launcheritemdelegate.cpp b/src/capture/workers/launcher/launcheritemdelegate.cpp new file mode 100644 index 00000000..721abd70 --- /dev/null +++ b/src/capture/workers/launcher/launcheritemdelegate.cpp @@ -0,0 +1,63 @@ +// Copyright 2017 Alejandro Sirgo Rica +// +// This file is part of Flameshot. +// +// Flameshot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Flameshot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Flameshot. If not, see . + +#include "launcheritemdelegate.h" +#include + +launcherItemDelegate::launcherItemDelegate(QObject *parent) : + QStyledItemDelegate(parent) +{ +} + +void launcherItemDelegate::paint( + QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + const QRect &rect = option.rect; + if (option.state & (QStyle::State_Selected | QStyle::State_MouseOver)) { + painter->save(); + painter->setPen(Qt::transparent); + painter->setBrush(QPalette().highlight()); + painter->drawRect(rect.x(), rect.y(), + rect.width() -1, rect.height() -1); + painter->restore(); + } + QIcon icon = index.data(Qt::DecorationRole).value(); + + const int iconSide = 40; + const int halfIcon = iconSide/2; + const int halfWidth = rect.width()/2; + const int halfHeight = rect.height()/2; + painter->drawPixmap(rect.x() + (halfWidth - halfIcon), + rect.y()+ (halfHeight/2 - halfIcon), + iconSide, iconSide, + icon.pixmap(QSize(iconSide, iconSide))); + const QRect textRect(rect.x(), rect.y() + halfHeight, + rect.width(), halfHeight); + painter->drawText(textRect, Qt::TextWordWrap | Qt::AlignHCenter, + index.data(Qt::DisplayRole).toString()); +} + +QSize launcherItemDelegate::sizeHint( + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + Q_UNUSED(option); + Q_UNUSED(index); + return QSize(100, 100); +} diff --git a/src/capture/workers/launcher/launcheritemdelegate.h b/src/capture/workers/launcher/launcheritemdelegate.h new file mode 100644 index 00000000..43b11bf7 --- /dev/null +++ b/src/capture/workers/launcher/launcheritemdelegate.h @@ -0,0 +1,40 @@ +// Copyright 2017 Alejandro Sirgo Rica +// +// This file is part of Flameshot. +// +// Flameshot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Flameshot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Flameshot. If not, see . + +#ifndef LAUNCHERITEMDELEGATE_H +#define LAUNCHERITEMDELEGATE_H + +#include "src/utils/desktopfileparse.h" +#include + +class launcherItemDelegate : public QStyledItemDelegate +{ + Q_OBJECT +public: + explicit launcherItemDelegate(QObject *parent = nullptr); + + void paint(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const; + + QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; + +private: + +}; + +#endif // LAUNCHERITEMDELEGATE_H diff --git a/src/core/resourceexporter.cpp b/src/core/resourceexporter.cpp index 60ff8e1b..741fc47c 100644 --- a/src/core/resourceexporter.cpp +++ b/src/core/resourceexporter.cpp @@ -19,6 +19,7 @@ #include "src/capture/workers/imgur/imguruploader.h" #include "src/capture/workers/screenshotsaver.h" #include "src/capture/workers/graphicalscreenshotsaver.h" +#include "src/capture/workers/launcher/applauncherwidget.h" ResourceExporter::ResourceExporter() { @@ -41,3 +42,8 @@ void ResourceExporter::captureToImgur(const QPixmap &p) { auto w = new ImgurUploader(p); w->show(); } + +void ResourceExporter::captureToProgram(const QPixmap &p) { + auto w = new AppLauncherWidget(p); + w->show(); +} diff --git a/src/core/resourceexporter.h b/src/core/resourceexporter.h index dc97ad33..53a1ec07 100644 --- a/src/core/resourceexporter.h +++ b/src/core/resourceexporter.h @@ -28,6 +28,7 @@ public: void captureToFile(const QPixmap &p, const QString &path); void captureToFileUi(const QPixmap &p); void captureToImgur(const QPixmap &p); + void captureToProgram(const QPixmap &p); }; #endif // RESOURCEEXPORTER_H diff --git a/src/utils/desktopfileparse.cpp b/src/utils/desktopfileparse.cpp new file mode 100644 index 00000000..9a92b2e6 --- /dev/null +++ b/src/utils/desktopfileparse.cpp @@ -0,0 +1,99 @@ +// Copyright 2017 Alejandro Sirgo Rica +// +// This file is part of Flameshot. +// +// Flameshot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Flameshot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Flameshot. If not, see . + +#include "desktopfileparse.h" +#include +#include +#include +#include + +DesktopFileParse::DesktopFileParse() { + QString locale = QLocale().name(); + m_localeName = QString("Name[%1]").arg(locale); + m_localeDescription = QString("Comment[%1]").arg(locale); +} + +DesktopAppData DesktopFileParse::parseDesktopFile( + const QString &fileName, bool &ok) +{ + DesktopAppData res; + ok = true; + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + ok = false; + return res; + } + bool nameLocaleSet = false; + bool descriptionLocaleSet = false; + bool isApplication = false; + bool isGraphics = false; + QTextStream in(&file); + while (!in.atEnd()) { + QString line = in.readLine(); + if (line.startsWith("Icon")) { + res.icon = QIcon::fromTheme( + line.mid(line.indexOf("=")+1).trimmed()); + } + else if (!nameLocaleSet && line.startsWith("Name")) { + if (line.startsWith(m_localeName)) { + res.name = line.mid(line.indexOf("=")+1).trimmed(); + nameLocaleSet = true; + } else if (line.startsWith("Name=")) { + res.name = line.mid(line.indexOf("=")+1).trimmed(); + } + } + else if (!descriptionLocaleSet && line.startsWith("Comment")) { + if (line.startsWith(m_localeName)) { + res.description = line.mid(line.indexOf("=")+1).trimmed(); + descriptionLocaleSet = true; + } else if (line.startsWith("Comment=")) { + res.description = line.mid(line.indexOf("=")+1).trimmed(); + } + } + else if (line.startsWith("MimeType") && + !line.contains("image/png")) + { + ok = false; + break; + } + else if (line.startsWith("Exec")) { + if (line.contains("%")) { + res.exec = line.mid(line.indexOf("=")+1) + .trimmed(); + } else { + ok = false; + break; + } + } + else if (line.startsWith("Type")) { + if (line.contains("Application")) { + isApplication = true; + } + } + else if (line.startsWith("Categories")) { + if (line.contains("Graphics")) { + isGraphics = true; + } + } + } + file.close(); + if (res.exec.isEmpty() || res.name.isEmpty() || !isApplication || + !isGraphics) { + ok = false; + } + return res; +} diff --git a/src/utils/desktopfileparse.h b/src/utils/desktopfileparse.h new file mode 100644 index 00000000..8823c764 --- /dev/null +++ b/src/utils/desktopfileparse.h @@ -0,0 +1,44 @@ +// Copyright 2017 Alejandro Sirgo Rica +// +// This file is part of Flameshot. +// +// Flameshot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Flameshot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Flameshot. If not, see . + +#ifndef DESKTOPFILEPARSE_H +#define DESKTOPFILEPARSE_H + +#include + +class QFile; +class QString; +class QTextStream; + +struct DesktopAppData { + QIcon icon; + QString name; + QString description; + QString exec; +}; + +struct DesktopFileParse { + DesktopFileParse(); + DesktopAppData parseDesktopFile(const QString &fileName, bool &ok); + +private: + QString m_localeName; + QString m_localeDescription; + +}; + +#endif // DESKTOPFILEPARSE_H