diff --git a/flameshot.pro b/flameshot.pro index e37d5c32..7e122a86 100644 --- a/flameshot.pro +++ b/flameshot.pro @@ -91,7 +91,8 @@ SOURCES += src/main.cpp\ src/capture/tools/applauncher.cpp \ src/utils/desktopfileparse.cpp \ src/capture/workers/launcher/launcheritemdelegate.cpp \ - src/capture/tools/blurtool.cpp + src/capture/tools/blurtool.cpp \ + src/capture/workers/launcher/moreappswidget.cpp HEADERS += \ src/capture/widget/buttonhandler.h \ @@ -147,7 +148,8 @@ HEADERS += \ src/capture/tools/applauncher.h \ src/utils/desktopfileparse.h \ src/capture/workers/launcher/launcheritemdelegate.h \ - src/capture/tools/blurtool.h + src/capture/tools/blurtool.h \ + src/capture/workers/launcher/moreappswidget.h RESOURCES += \ graphics.qrc diff --git a/src/capture/workers/launcher/applauncherwidget.cpp b/src/capture/workers/launcher/applauncherwidget.cpp index 20a5ee28..d11ca10d 100644 --- a/src/capture/workers/launcher/applauncherwidget.cpp +++ b/src/capture/workers/launcher/applauncherwidget.cpp @@ -16,12 +16,12 @@ // 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 "src/utils/confighandler.h" +#include "moreappswidget.h" #include -#include +#include #include #include #include @@ -33,36 +33,22 @@ AppLauncherWidget::AppLauncherWidget(const QPixmap &p, QWidget *parent): QWidget(parent), m_pixmap(p) { setAttribute(Qt::WA_DeleteOnClose); + setWindowIcon(QIcon(":img/flameshot.png")); + setWindowTitle(tr("Open With")); + m_keepOpen = ConfigHandler().keepOpenAppLauncherValue(); - // In case of wanting to know the default app for a mime: - // xdg-mime query default image/png - QString dir = "/usr/share/applications/"; - QString dirLocal = "~/.local/share/applications/"; - QDir appsDirLocal(dirLocal); - QDir appsDir(dir); + QString dirLocal = QDir::homePath() + "/.local/share/applications/"; + QDir appsDirLocal(dirLocal); + m_parser.processDirectory(appsDirLocal); - QStringList entries = appsDir.entryList(QDir::NoDotAndDotDot | QDir::Files); - QStringList entriesLocal = appsDirLocal.entryList(QDir::NoDotAndDotDot | QDir::Files); + QString dir = "/usr/share/applications/"; + QDir appsDir(dir); + m_parser.processDirectory(appsDir); - DesktopFileParse parser; - bool ok; - 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 QVBoxLayout(this); - auto *listView = new QListWidget(this); - listView->setItemDelegate(new launcherItemDelegate()); + m_layout = new QVBoxLayout(this); + QListWidget *listView = new QListWidget(this); + listView->setItemDelegate(new LauncherItemDelegate()); listView->setViewMode(QListWidget::IconMode); listView->setResizeMode(QListView::Adjust); listView->setSpacing(4); @@ -70,6 +56,11 @@ AppLauncherWidget::AppLauncherWidget(const QPixmap &p, QWidget *parent): listView->setDragEnabled(false); listView->setMinimumSize(375, 210); + QList appList = m_parser.getAppsByCategory("Graphics"); + appList.append(DesktopAppData(QObject::tr("Other Application"), "", ".", + QIcon::fromTheme("applications-other") + )); + for (auto app: appList) { QListWidgetItem *buttonItem = new QListWidgetItem(listView); buttonItem->setData(Qt::DecorationRole, app.icon); @@ -87,23 +78,32 @@ AppLauncherWidget::AppLauncherWidget(const QPixmap &p, QWidget *parent): m_checkbox = new QCheckBox("Keep open after selection", this); connect(m_checkbox, &QCheckBox::clicked, this, &AppLauncherWidget::checkboxClicked); - layout->addWidget(listView); - layout->addWidget(m_checkbox); + m_layout->addWidget(listView); + m_layout->addWidget(m_checkbox); + m_checkbox->setChecked(ConfigHandler().keepOpenAppLauncherValue()); } void AppLauncherWidget::launch(const QModelIndex &index) { - m_tempFile = FileNameHandler().generateAbsolutePath("/tmp") + ".png"; - bool ok = m_pixmap.save(m_tempFile); - if (!ok) { - // TO DO - return; - } - QString command = index.data(Qt::UserRole).toString().replace( - QRegExp("(\%.)"), m_tempFile); - QProcess::startDetached(command); - if (!m_keepOpen) { - close(); - } + QString command = index.data(Qt::UserRole).toString().replace( + QRegExp("(\%.)"), m_tempFile); + if (command == ".") { + MoreAppsWidget *widget = new MoreAppsWidget(m_pixmap, m_parser); + connect(widget, &MoreAppsWidget::appClicked, + this, &AppLauncherWidget::launch); + m_layout->takeAt(0)->widget()->deleteLater(); + m_layout->insertWidget(0, widget); + } else { + m_tempFile = FileNameHandler().generateAbsolutePath("/tmp") + ".png"; + bool ok = m_pixmap.save(m_tempFile); + if (!ok) { + // TO DO + return; + } + QProcess::startDetached(command); + } + if (!m_keepOpen) { + close(); + } } void AppLauncherWidget::checkboxClicked(const bool enabled) { diff --git a/src/capture/workers/launcher/applauncherwidget.h b/src/capture/workers/launcher/applauncherwidget.h index 97646cde..927c0346 100644 --- a/src/capture/workers/launcher/applauncherwidget.h +++ b/src/capture/workers/launcher/applauncherwidget.h @@ -18,9 +18,11 @@ #ifndef APPLAUNCHERWIDGET_H #define APPLAUNCHERWIDGET_H +#include "src/utils/desktopfileparse.h" #include class QCheckBox; +class QVBoxLayout; class AppLauncherWidget: public QWidget { @@ -33,10 +35,12 @@ private slots: void checkboxClicked(const bool enabled); private: + DesktopFileParser m_parser; QPixmap m_pixmap; QString m_tempFile; bool m_keepOpen; QCheckBox *m_checkbox; + QVBoxLayout *m_layout; }; diff --git a/src/capture/workers/launcher/launcheritemdelegate.cpp b/src/capture/workers/launcher/launcheritemdelegate.cpp index 9a88863b..e98aa62b 100644 --- a/src/capture/workers/launcher/launcheritemdelegate.cpp +++ b/src/capture/workers/launcher/launcheritemdelegate.cpp @@ -18,12 +18,12 @@ #include "launcheritemdelegate.h" #include -launcherItemDelegate::launcherItemDelegate(QObject *parent) : +LauncherItemDelegate::LauncherItemDelegate(QObject *parent) : QStyledItemDelegate(parent) { } -void launcherItemDelegate::paint( +void LauncherItemDelegate::paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const @@ -53,7 +53,7 @@ void launcherItemDelegate::paint( index.data(Qt::DisplayRole).toString()); } -QSize launcherItemDelegate::sizeHint( +QSize LauncherItemDelegate::sizeHint( const QStyleOptionViewItem &option, const QModelIndex &index) const { diff --git a/src/capture/workers/launcher/launcheritemdelegate.h b/src/capture/workers/launcher/launcheritemdelegate.h index 43b11bf7..3d778f0d 100644 --- a/src/capture/workers/launcher/launcheritemdelegate.h +++ b/src/capture/workers/launcher/launcheritemdelegate.h @@ -21,11 +21,11 @@ #include "src/utils/desktopfileparse.h" #include -class launcherItemDelegate : public QStyledItemDelegate +class LauncherItemDelegate : public QStyledItemDelegate { Q_OBJECT public: - explicit launcherItemDelegate(QObject *parent = nullptr); + explicit LauncherItemDelegate(QObject *parent = nullptr); void paint(QPainter *painter, const QStyleOptionViewItem &option, diff --git a/src/capture/workers/launcher/moreappswidget.cpp b/src/capture/workers/launcher/moreappswidget.cpp new file mode 100644 index 00000000..507d85d5 --- /dev/null +++ b/src/capture/workers/launcher/moreappswidget.cpp @@ -0,0 +1,111 @@ +#include "moreappswidget.h" +#include "src/capture/workers/launcher/launcheritemdelegate.h" +#include +#include +#include +#include +#include +#include + +// implement search, open custom and choose terminal + +MoreAppsWidget::MoreAppsWidget( + const QPixmap &pixmap, + const DesktopFileParser &parser, + QWidget *parent) : + QWidget(parent), m_pixmap(pixmap) +{ + setAttribute(Qt::WA_DeleteOnClose); + setWindowIcon(QIcon(":img/flameshot.png")); + setWindowTitle(tr("Open With")); + + DesktopFileParser ctor_parser = std::move(parser); + m_layout = new QVBoxLayout(this); + m_tabs = new QTabWidget; + m_tabs->setIconSize(QSize(30, 30)); + m_layout->addWidget(m_tabs); + + QStringList categories({"AudioVideo", + "Audio", + "Video", + "Development", + "Graphics", + "Network", + "Office", + "Science", + "Settings", + "System", + "Utility"}); + + QMap> appsMap = + ctor_parser.getAppsByCategory(categories); + + // Unify multimedia. + QList multimediaList; + QStringList multimediaNames; + multimediaNames << "AudioVideo" << "Audio" << "Video"; + for (const QString &name : multimediaNames) { + if(!appsMap.contains(name)) { + continue; + } + for (auto i : appsMap[name]) { + if (!multimediaList.contains(i)) { + multimediaList.append(i); + } + } + appsMap.remove(name); + } + appsMap.insert("Multimedia", multimediaList); + + QMap catIconNames({ + { "Multimedia", "applications-multimedia" }, + { "Development","applications-development" }, + { "Graphics", "applications-graphics" }, + { "Network", "preferences-system-network" }, + { "Office", "applications-office" }, + { "Science", "applications-science" }, + { "Settings", "preferences-desktop" }, + { "System", "preferences-system" }, + { "Utility", "applications-utilities" } + }); + + for (auto const& i : catIconNames.toStdMap()) { + const QString &cat = i.first; + const QString &iconName = i.second; + + if (!appsMap.contains(cat)) { + continue; + } + + QListWidget *listView = new QListWidget(); + listView->setItemDelegate(new LauncherItemDelegate()); + listView->setViewMode(QListWidget::IconMode); + listView->setResizeMode(QListView::Adjust); + listView->setSpacing(4); + listView->setFlow(QListView::LeftToRight); + listView->setDragEnabled(false); + listView->setMinimumSize(375, 210); + connect(listView, &QListWidget::clicked, + this, &MoreAppsWidget::appClicked); + + QList appList = appsMap[cat]; + 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); + } + m_tabs->addTab(listView, QIcon::fromTheme(iconName), ""); + m_tabs->setTabToolTip(m_tabs->count(), cat); + if (cat == "Graphics") { + m_tabs->setCurrentIndex(m_tabs->count() -1); + } + } +} diff --git a/src/capture/workers/launcher/moreappswidget.h b/src/capture/workers/launcher/moreappswidget.h new file mode 100644 index 00000000..626d09bc --- /dev/null +++ b/src/capture/workers/launcher/moreappswidget.h @@ -0,0 +1,28 @@ +#ifndef MOREAPPSWIDGET_H +#define MOREAPPSWIDGET_H + +#include "src/utils/desktopfileparse.h" +#include + +class QPixmap; +class QVBoxLayout; +class QTabWidget; + +class MoreAppsWidget : public QWidget +{ + Q_OBJECT +public: + explicit MoreAppsWidget(const QPixmap &pixmap, + const DesktopFileParser &parser, + QWidget *parent = nullptr); + +signals: + void appClicked(const QModelIndex &index); +private: + QPixmap m_pixmap; + QVBoxLayout *m_layout; + QTabWidget *m_tabs; + +}; + +#endif // MOREAPPSWIDGET_H diff --git a/src/config/configwindow.cpp b/src/config/configwindow.cpp index c1e197fb..aedb5cb5 100644 --- a/src/config/configwindow.cpp +++ b/src/config/configwindow.cpp @@ -73,18 +73,18 @@ ConfigWindow::ConfigWindow(QWidget *parent) : QTabWidget(parent) { m_buttonList, &ButtonListView::selectAll); listLayout->addWidget(setAllButtons); - addTab(visuals, tr("Interface")); - setTabIcon(0, QIcon(modifier + "graphics.png")); + addTab(visuals, QIcon(modifier + "graphics.png"), + tr("Interface")); // filename m_filenameEditor = new FileNameEditor(); - addTab(m_filenameEditor, tr("Filename Editor")); - setTabIcon(1, QIcon(modifier + "name_edition.png")); + addTab(m_filenameEditor, QIcon(modifier + "name_edition.png"), + tr("Filename Editor")); // general m_generalConfig = new GeneneralConf(); - addTab(m_generalConfig, tr("General")); - setTabIcon(2, QIcon(modifier + "config.png")); + addTab(m_generalConfig, QIcon(modifier + "config.png"), + tr("General")); // connect update sigslots connect(this, &ConfigWindow::updateChildren, diff --git a/src/utils/desktopfileparse.cpp b/src/utils/desktopfileparse.cpp index 7d02d78f..7843048a 100644 --- a/src/utils/desktopfileparse.cpp +++ b/src/utils/desktopfileparse.cpp @@ -17,11 +17,12 @@ #include "desktopfileparse.h" #include +#include #include #include #include -DesktopFileParse::DesktopFileParse() { +DesktopFileParser::DesktopFileParser() { QString locale = QLocale().name(); QString localeShort = QLocale().name().left(2); m_localeName = QString("Name[%1]").arg(locale); @@ -29,79 +30,123 @@ DesktopFileParse::DesktopFileParse() { m_localeNameShort = QString("Name[%1]").arg(localeShort); m_localeDescriptionShort = QString("Comment[%1]") .arg(localeShort); + m_defaultIcon = QIcon::fromTheme("application-x-executable"); } -DesktopAppData DesktopFileParse::parseDesktopFile( - const QString &fileName, bool &ok) +DesktopAppData DesktopFileParser::parseDesktopFile( + const QString &fileName, bool &ok) const { - 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) || - line.startsWith(m_localeNameShort)) - { - 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_localeDescription) || - line.startsWith(m_localeDescriptionShort)) - { - 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; + 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; + QTextStream in(&file); + // enter the desktop entry definition + while (!in.atEnd() && in.readLine() != "[Desktop Entry]") { + } + // start parsing + while (!in.atEnd()) { + QString line = in.readLine(); + if (line.startsWith("Icon")) { + res.icon = QIcon::fromTheme( + line.mid(line.indexOf("=")+1).trimmed(), + m_defaultIcon); + } + else if (!nameLocaleSet && line.startsWith("Name")) { + if (line.startsWith(m_localeName) || + line.startsWith(m_localeNameShort)) + { + 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_localeDescription) || + line.startsWith(m_localeDescriptionShort)) + { + 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("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")) { + res.categories = line.mid(line.indexOf("=")+1).split(";"); + } + else if (line == "NoDisplay=true") { + ok = false; + break; + } + else if (line == "Terminal=true") { + res.showInTerminal = true; + } + // ignore the other entries + else if (line.startsWith("[")) { + break; + } + } + file.close(); + if (res.exec.isEmpty() || res.name.isEmpty() || !isApplication) { + ok = false; + } + return res; +} + +int DesktopFileParser::processDirectory(const QDir &dir) { + QStringList entries = dir.entryList(QDir::NoDotAndDotDot | QDir::Files); + bool ok; + int length = m_appList.length(); + for (QString file: entries){ + DesktopAppData app = parseDesktopFile(dir.absoluteFilePath(file), ok); + if (ok) { + m_appList.append(app); + } + } + return m_appList.length() - length; +} + +QList DesktopFileParser::getAppsByCategory(const QString &category) { + QList res; + for (const DesktopAppData &app : m_appList) { + if (app.categories.contains(category)) { + res.append(app); + } + } + return res; +} + +QMap> DesktopFileParser::getAppsByCategory( + const QStringList &categories) +{ + QMap> res; + for (const DesktopAppData &app : m_appList) { + for (const QString &category: categories) { + if (app.categories.contains(category)) { + res[category].append(app); + } + } + } + return res; } diff --git a/src/utils/desktopfileparse.h b/src/utils/desktopfileparse.h index abac73b2..f64fc69d 100644 --- a/src/utils/desktopfileparse.h +++ b/src/utils/desktopfileparse.h @@ -19,21 +19,48 @@ #define DESKTOPFILEPARSE_H #include +#include +#include -class QFile; +class QDir; class QString; class QTextStream; struct DesktopAppData { - QIcon icon; + DesktopAppData() = default; + + DesktopAppData( + QString name, + QString description, + QString exec, + QIcon icon) : + name(name), + description(description), + exec(exec), + icon(icon), + showInTerminal(false) + {} + + bool operator==(const DesktopAppData &other) const { + return name == other.name; + } + QString name; QString description; QString exec; + QStringList categories; + QIcon icon; + bool showInTerminal; }; -struct DesktopFileParse { - DesktopFileParse(); - DesktopAppData parseDesktopFile(const QString &fileName, bool &ok); +struct DesktopFileParser { + DesktopFileParser(); + DesktopAppData parseDesktopFile(const QString &fileName, bool &ok) const; + int processDirectory(const QDir &dir); + + QList getAppsByCategory(const QString &category); + QMap> getAppsByCategory( + const QStringList &categories); private: QString m_localeName; @@ -41,6 +68,8 @@ private: QString m_localeNameShort; QString m_localeDescriptionShort; + QIcon m_defaultIcon; + QList m_appList; }; #endif // DESKTOPFILEPARSE_H