diff --git a/data/graphics.qrc b/data/graphics.qrc
index 06ef24f4..fd1d93d1 100644
--- a/data/graphics.qrc
+++ b/data/graphics.qrc
@@ -95,5 +95,9 @@
img/material/white/move_down.svg
img/material/white/move_up.svg
img/material/white/delete.svg
+ img/material/black/apps.svg
+ img/material/black/image.svg
+ img/material/white/apps.svg
+ img/material/white/image.svg
diff --git a/data/img/material/black/apps.svg b/data/img/material/black/apps.svg
new file mode 100644
index 00000000..be408313
--- /dev/null
+++ b/data/img/material/black/apps.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/data/img/material/black/image.svg b/data/img/material/black/image.svg
new file mode 100644
index 00000000..f06871c5
--- /dev/null
+++ b/data/img/material/black/image.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/data/img/material/white/apps.svg b/data/img/material/white/apps.svg
new file mode 100644
index 00000000..6010350f
--- /dev/null
+++ b/data/img/material/white/apps.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/data/img/material/white/image.svg b/data/img/material/white/image.svg
new file mode 100644
index 00000000..2ad6b8c5
--- /dev/null
+++ b/data/img/material/white/image.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/tools/launcher/applauncherwidget.cpp b/src/tools/launcher/applauncherwidget.cpp
index d348b1c9..8a202c89 100644
--- a/src/tools/launcher/applauncherwidget.cpp
+++ b/src/tools/launcher/applauncherwidget.cpp
@@ -18,10 +18,15 @@
#include
#include
#include
+#include
#include
namespace {
-
+#if defined(Q_OS_WIN)
+QMap catIconNames({ { "Graphics", "image.svg" },
+ { "Utility", "apps.svg" } });
+}
+#else
QMap catIconNames(
{ { "Multimedia", "applications-multimedia" },
{ "Development", "applications-development" },
@@ -33,6 +38,7 @@ QMap catIconNames(
{ "System", "preferences-system" },
{ "Utility", "applications-utilities" } });
}
+#endif
AppLauncherWidget::AppLauncherWidget(const QPixmap& p, QWidget* parent)
: QWidget(parent)
@@ -44,6 +50,18 @@ AppLauncherWidget::AppLauncherWidget(const QPixmap& p, QWidget* parent)
m_keepOpen = ConfigHandler().keepOpenAppLauncher();
+#if defined(Q_OS_WIN)
+ QDir userAppsFolder(
+ QStandardPaths::standardLocations(QStandardPaths::ApplicationsLocation)
+ .at(0));
+ m_parser.processDirectory(userAppsFolder);
+
+ QString dir(m_parser.getAllUsersStartMenuPath());
+ if (!dir.isEmpty()) {
+ QDir allUserAppsFolder(dir);
+ m_parser.processDirectory(allUserAppsFolder);
+ }
+#else
QString dirLocal = QDir::homePath() + "/.local/share/applications/";
QDir appsDirLocal(dirLocal);
m_parser.processDirectory(appsDirLocal);
@@ -51,6 +69,7 @@ AppLauncherWidget::AppLauncherWidget(const QPixmap& p, QWidget* parent)
QString dir = QStringLiteral("/usr/share/applications/");
QDir appsDir(dir);
m_parser.processDirectory(appsDir);
+#endif
initAppMap();
initListWidget();
@@ -99,7 +118,14 @@ void AppLauncherWidget::launch(const QModelIndex& index)
// Heuristically, if there is a % in the command we assume it is the file
// name slot
QString command = index.data(Qt::UserRole).toString();
+#if defined(Q_OS_WIN)
+ // Do not split on Windows, since file path can contain spaces
+ // and % is not used in lnk files
+ QStringList prog_args;
+ prog_args << command;
+#else
QStringList prog_args = command.split(" ");
+#endif
// no quotes because it is going in an array!
if (command.contains("%")) {
// but that means we need to substitute IN the array not the string!
@@ -121,8 +147,10 @@ void AppLauncherWidget::launch(const QModelIndex& index)
this, tr("Error"), tr("Unable to launch in terminal."));
}
} else {
+ QFileInfo fi(m_tempFile);
+ QString workingDir = fi.absolutePath();
prog_args.removeAt(0); // strip program name out
- QProcess::startDetached(app_name, prog_args);
+ QProcess::startDetached(app_name, prog_args, workingDir);
}
if (!m_keepOpen) {
close();
@@ -185,8 +213,17 @@ void AppLauncherWidget::initListWidget()
const QVector& appList = m_appsMap[cat];
addAppsToListWidget(itemsWidget, appList);
+#if defined(Q_OS_WIN)
+ QColor background = this->palette().window().color();
+ bool isDark = ColorUtils::colorIsDark(background);
+ QString modifier =
+ isDark ? PathInfo::whiteIconPath() : PathInfo::blackIconPath();
+ m_tabWidget->addTab(
+ itemsWidget, QIcon(modifier + iconName), QLatin1String(""));
+#else
m_tabWidget->addTab(
itemsWidget, QIcon::fromTheme(iconName), QLatin1String(""));
+#endif
m_tabWidget->setTabToolTip(m_tabWidget->count(), cat);
if (cat == QLatin1String("Graphics")) {
m_tabWidget->setCurrentIndex(m_tabWidget->count() - 1);
@@ -215,18 +252,21 @@ void AppLauncherWidget::initAppMap()
QStringList multimediaNames;
multimediaNames << QStringLiteral("AudioVideo") << QStringLiteral("Audio")
<< QStringLiteral("Video");
- for (const QString& name : multimediaNames) {
+ for (const QString& name : qAsConst(multimediaNames)) {
if (!m_appsMap.contains(name)) {
continue;
}
- for (auto i : m_appsMap[name]) {
+ for (const auto& i : m_appsMap[name]) {
if (!multimediaList.contains(i)) {
multimediaList.append(i);
}
}
m_appsMap.remove(name);
}
- m_appsMap.insert(QStringLiteral("Multimedia"), multimediaList);
+
+ if (!multimediaList.isEmpty()) {
+ m_appsMap.insert(QStringLiteral("Multimedia"), multimediaList);
+ }
}
void AppLauncherWidget::configureListView(QListWidget* widget)
diff --git a/src/tools/launcher/applauncherwidget.h b/src/tools/launcher/applauncherwidget.h
index 65a31f3c..7ed463a1 100644
--- a/src/tools/launcher/applauncherwidget.h
+++ b/src/tools/launcher/applauncherwidget.h
@@ -3,10 +3,15 @@
#pragma once
-#include "src/utils/desktopfileparse.h"
#include
#include
+#if defined(Q_OS_WIN)
+#include "src/utils/winlnkfileparse.h"
+#else
+#include "src/utils/desktopfileparse.h"
+#endif
+
class QTabWidget;
class QCheckBox;
class QVBoxLayout;
@@ -32,7 +37,11 @@ private:
const QVector& appList);
void keyPressEvent(QKeyEvent* keyEvent) override;
+#if defined(Q_OS_WIN)
+ WinLnkFileParser m_parser;
+#else
DesktopFileParser m_parser;
+#endif
QPixmap m_pixmap;
QString m_tempFile;
bool m_keepOpen;
diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt
index 1f787674..c6d23276 100644
--- a/src/utils/CMakeLists.txt
+++ b/src/utils/CMakeLists.txt
@@ -26,5 +26,12 @@ target_sources(
colorutils.cpp
history.cpp
strfparse.cpp
- request.cpp
+ request.cpp
)
+
+IF (WIN32)
+ target_sources(
+ flameshot
+ PRIVATE winlnkfileparse.cpp
+ )
+ENDIF()
diff --git a/src/utils/desktopfileparse.cpp b/src/utils/desktopfileparse.cpp
index c1a703b6..b04163d8 100644
--- a/src/utils/desktopfileparse.cpp
+++ b/src/utils/desktopfileparse.cpp
@@ -114,7 +114,7 @@ int DesktopFileParser::processDirectory(const QDir& dir)
dir.entryList({ "*.desktop" }, QDir::NoDotAndDotDot | QDir::Files);
bool ok;
int length = m_appList.length();
- for (QString file : entries) {
+ for (const QString& file : entries) {
DesktopAppData app = parseDesktopFile(dir.absoluteFilePath(file), ok);
if (ok) {
m_appList.append(app);
@@ -127,7 +127,7 @@ QVector DesktopFileParser::getAppsByCategory(
const QString& category)
{
QVector res;
- for (const DesktopAppData& app : m_appList) {
+ for (const DesktopAppData& app : qAsConst(m_appList)) {
if (app.categories.contains(category)) {
res.append(app);
}
@@ -139,7 +139,7 @@ QMap> DesktopFileParser::getAppsByCategory(
const QStringList& categories)
{
QMap> res;
- for (const DesktopAppData& app : m_appList) {
+ for (const DesktopAppData& app : qAsConst(m_appList)) {
for (const QString& category : categories) {
if (app.categories.contains(category)) {
res[category].append(app);
diff --git a/src/utils/winlnkfileparse.cpp b/src/utils/winlnkfileparse.cpp
new file mode 100644
index 00000000..9cda3c37
--- /dev/null
+++ b/src/utils/winlnkfileparse.cpp
@@ -0,0 +1,160 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+// SPDX-FileCopyrightText: 2017-2019 Alejandro Sirgo Rica & Contributors
+
+#include "winlnkfileparse.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+WinLnkFileParser::WinLnkFileParser()
+{
+ QStringList sListImgFileExt;
+ for (const auto& ext : QImageWriter::supportedImageFormats()) {
+ sListImgFileExt.append(ext);
+ }
+ this->getImageFileExtAssociates(sListImgFileExt);
+}
+
+DesktopAppData WinLnkFileParser::parseLnkFile(const QFileInfo& fiLnk,
+ bool& ok) const
+{
+ DesktopAppData res;
+ ok = true;
+
+ QFileInfo fiSymlink(fiLnk.symLinkTarget());
+ if (!fiSymlink.exists() || !fiSymlink.fileName().endsWith(".exe") ||
+ fiSymlink.baseName().contains("unins")) {
+ ok = false;
+ return res;
+ }
+
+ res.name = fiLnk.baseName();
+ res.exec = fiSymlink.absoluteFilePath();
+
+ // Get icon from exe
+ QFileSystemModel* model = new QFileSystemModel;
+ model->setRootPath(fiSymlink.path());
+ res.icon = model->fileIcon(model->index(fiSymlink.filePath()));
+
+ if (m_GraphicAppsList.contains(fiSymlink.fileName())) {
+ res.categories = QStringList() << "Graphics";
+ } else {
+ res.categories = QStringList() << "Utility";
+ }
+
+ for (const auto& app : m_appList) {
+ if (app.exec == res.exec) {
+ ok = false;
+ break;
+ }
+ }
+
+ if (res.exec.isEmpty() || res.name.isEmpty()) {
+ ok = false;
+ }
+ return res;
+}
+
+int WinLnkFileParser::processDirectory(const QDir& dir)
+{
+ QStringList sListMenuFilter;
+ sListMenuFilter << "Accessibility"
+ << "Administrative Tools"
+ << "Setup"
+ << "System Tools"
+ << "Uninstall"
+ << "Update"
+ << "Updater"
+ << "Windows PowerShell";
+ const QString sMenuFilter("\\b(" + sListMenuFilter.join('|') + ")\\b");
+ QRegularExpression regexfilter(sMenuFilter);
+
+ bool ok;
+ int length = m_appList.length();
+ // Go through all subfolders and *.lnk files
+ QDirIterator it(dir.absolutePath(),
+ { "*.lnk" },
+ QDir::NoFilter,
+ QDirIterator::Subdirectories);
+ while (it.hasNext()) {
+ QFileInfo fiLnk(it.next());
+ if (!regexfilter.match(fiLnk.absoluteFilePath()).hasMatch()) {
+ DesktopAppData app = parseLnkFile(fiLnk, ok);
+ if (ok) {
+ m_appList.append(app);
+ }
+ }
+ }
+
+ return m_appList.length() - length;
+}
+
+QVector WinLnkFileParser::getAppsByCategory(
+ const QString& category)
+{
+ QVector res;
+ for (const DesktopAppData& app : qAsConst(m_appList)) {
+ if (app.categories.contains(category)) {
+ res.append(app);
+ }
+ }
+
+ std::sort(res.begin(), res.end(), CompareAppByName());
+
+ return res;
+}
+
+QMap> WinLnkFileParser::getAppsByCategory(
+ const QStringList& categories)
+{
+ QMap> res;
+
+ QVector tmpAppList;
+ for (const QString& category : categories) {
+ tmpAppList = getAppsByCategory(category);
+ for (const DesktopAppData& app : qAsConst(tmpAppList)) {
+ res[category].append(app);
+ }
+ }
+
+ return res;
+}
+
+QString WinLnkFileParser::getAllUsersStartMenuPath()
+{
+ QString sRet("");
+ WCHAR path[MAX_PATH];
+ HRESULT hr = SHGetFolderPathW(NULL, CSIDL_COMMON_PROGRAMS, NULL, 0, path);
+
+ if (SUCCEEDED(hr)) {
+ sRet = QDir(QString::fromWCharArray(path)).absolutePath();
+ }
+
+ return sRet;
+}
+
+void WinLnkFileParser::getImageFileExtAssociates(const QStringList& sListImgExt)
+{
+ const QString sReg("HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\"
+ "CurrentVersion\\Explorer\\FileExts\\.%1\\OpenWithList");
+
+ for (const auto& sExt : qAsConst(sListImgExt)) {
+ QString sPath(sReg.arg(sExt));
+ QSettings registry(sPath, QSettings::NativeFormat);
+ for (const auto& key : registry.allKeys()) {
+ if (1 == key.size()) { // Keys for OpenWith apps are a, b, c, ...
+ QString sVal = registry.value(key, "").toString();
+ if (sVal.endsWith(".exe") &&
+ !m_GraphicAppsList.contains(sVal)) {
+ m_GraphicAppsList << sVal;
+ }
+ }
+ }
+ }
+}
diff --git a/src/utils/winlnkfileparse.h b/src/utils/winlnkfileparse.h
new file mode 100644
index 00000000..1177bebc
--- /dev/null
+++ b/src/utils/winlnkfileparse.h
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+// SPDX-FileCopyrightText: 2017-2019 Alejandro Sirgo Rica & Contributors
+
+#pragma once
+
+#include "desktopfileparse.h"
+#include
+#include
+#include
+#include
+
+class QDir;
+class QString;
+
+struct CompareAppByName
+{
+ bool operator()(const DesktopAppData a, const DesktopAppData b)
+ {
+ return (a.name < b.name);
+ }
+};
+
+struct WinLnkFileParser
+{
+ WinLnkFileParser();
+ DesktopAppData parseLnkFile(const QFileInfo& fiLnk, bool& ok) const;
+ int processDirectory(const QDir& dir);
+ QString getAllUsersStartMenuPath();
+
+ QVector getAppsByCategory(const QString& category);
+ QMap> getAppsByCategory(
+ const QStringList& categories);
+
+private:
+ void getImageFileExtAssociates(const QStringList& sListImgExt);
+
+ QVector m_appList;
+ QStringList m_GraphicAppsList;
+};
diff --git a/src/widgets/capture/capturewidget.cpp b/src/widgets/capture/capturewidget.cpp
index 3749784f..b4a9da24 100644
--- a/src/widgets/capture/capturewidget.cpp
+++ b/src/widgets/capture/capturewidget.cpp
@@ -118,9 +118,12 @@ CaptureWidget::CaptureWidget(const CaptureRequest& req,
m_context.origScreenshot = m_context.screenshot;
#if defined(Q_OS_WIN)
+// Call cmake with -DFLAMESHOT_DEBUG_CAPTURE=ON to enable easier debugging
+#if !defined(FLAMESHOT_DEBUG_CAPTURE)
setWindowFlags(Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint |
Qt::SubWindow // Hides the taskbar icon
);
+#endif
for (QScreen* const screen : QGuiApplication::screens()) {
QPoint topLeftScreen = screen->geometry().topLeft();