Fix OpenWith on Windows (#1309) (#2801)

* Fix OpenWith on Windows (#1309)

* Clang-format and function name fix

* Clang-format
This commit is contained in:
El Thoro
2022-10-02 15:11:08 +02:00
committed by GitHub
parent 001726a565
commit 2eef14a14d
12 changed files with 276 additions and 10 deletions

View File

@@ -95,5 +95,9 @@
<file>img/material/white/move_down.svg</file> <file>img/material/white/move_down.svg</file>
<file>img/material/white/move_up.svg</file> <file>img/material/white/move_up.svg</file>
<file>img/material/white/delete.svg</file> <file>img/material/white/delete.svg</file>
<file>img/material/black/apps.svg</file>
<file>img/material/black/image.svg</file>
<file>img/material/white/apps.svg</file>
<file>img/material/white/image.svg</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M4 8h4V4H4v4zm6 12h4v-4h-4v4zm-6 0h4v-4H4v4zm0-6h4v-4H4v4zm6 0h4v-4h-4v4zm6-10v4h4V4h-4zm-6 4h4V4h-4v4zm6 6h4v-4h-4v4zm0 6h4v-4h-4v4z"/></svg>

After

Width:  |  Height:  |  Size: 292 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.9 13.98l2.1 2.53 3.1-3.99c.2-.26.6-.26.8.01l3.51 4.68c.25.33.01.8-.4.8H6.02c-.42 0-.65-.48-.39-.81L8.12 14c.19-.26.57-.27.78-.02z"/></svg>

After

Width:  |  Height:  |  Size: 368 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#FFFFFF"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M4 8h4V4H4v4zm6 12h4v-4h-4v4zm-6 0h4v-4H4v4zm0-6h4v-4H4v4zm6 0h4v-4h-4v4zm6-10v4h4V4h-4zm-6 4h4V4h-4v4zm6 6h4v-4h-4v4zm0 6h4v-4h-4v4z"/></svg>

After

Width:  |  Height:  |  Size: 292 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#FFFFFF"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.9 13.98l2.1 2.53 3.1-3.99c.2-.26.6-.26.8.01l3.51 4.68c.25.33.01.8-.4.8H6.02c-.42 0-.65-.48-.39-.81L8.12 14c.19-.26.57-.27.78-.02z"/></svg>

After

Width:  |  Height:  |  Size: 368 B

View File

@@ -18,10 +18,15 @@
#include <QMessageBox> #include <QMessageBox>
#include <QPixmap> #include <QPixmap>
#include <QProcess> #include <QProcess>
#include <QStandardPaths>
#include <QTabWidget> #include <QTabWidget>
namespace { namespace {
#if defined(Q_OS_WIN)
QMap<QString, QString> catIconNames({ { "Graphics", "image.svg" },
{ "Utility", "apps.svg" } });
}
#else
QMap<QString, QString> catIconNames( QMap<QString, QString> catIconNames(
{ { "Multimedia", "applications-multimedia" }, { { "Multimedia", "applications-multimedia" },
{ "Development", "applications-development" }, { "Development", "applications-development" },
@@ -33,6 +38,7 @@ QMap<QString, QString> catIconNames(
{ "System", "preferences-system" }, { "System", "preferences-system" },
{ "Utility", "applications-utilities" } }); { "Utility", "applications-utilities" } });
} }
#endif
AppLauncherWidget::AppLauncherWidget(const QPixmap& p, QWidget* parent) AppLauncherWidget::AppLauncherWidget(const QPixmap& p, QWidget* parent)
: QWidget(parent) : QWidget(parent)
@@ -44,6 +50,18 @@ AppLauncherWidget::AppLauncherWidget(const QPixmap& p, QWidget* parent)
m_keepOpen = ConfigHandler().keepOpenAppLauncher(); 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/"; QString dirLocal = QDir::homePath() + "/.local/share/applications/";
QDir appsDirLocal(dirLocal); QDir appsDirLocal(dirLocal);
m_parser.processDirectory(appsDirLocal); m_parser.processDirectory(appsDirLocal);
@@ -51,6 +69,7 @@ AppLauncherWidget::AppLauncherWidget(const QPixmap& p, QWidget* parent)
QString dir = QStringLiteral("/usr/share/applications/"); QString dir = QStringLiteral("/usr/share/applications/");
QDir appsDir(dir); QDir appsDir(dir);
m_parser.processDirectory(appsDir); m_parser.processDirectory(appsDir);
#endif
initAppMap(); initAppMap();
initListWidget(); 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 // Heuristically, if there is a % in the command we assume it is the file
// name slot // name slot
QString command = index.data(Qt::UserRole).toString(); 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(" "); QStringList prog_args = command.split(" ");
#endif
// no quotes because it is going in an array! // no quotes because it is going in an array!
if (command.contains("%")) { if (command.contains("%")) {
// but that means we need to substitute IN the array not the string! // 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.")); this, tr("Error"), tr("Unable to launch in terminal."));
} }
} else { } else {
QFileInfo fi(m_tempFile);
QString workingDir = fi.absolutePath();
prog_args.removeAt(0); // strip program name out prog_args.removeAt(0); // strip program name out
QProcess::startDetached(app_name, prog_args); QProcess::startDetached(app_name, prog_args, workingDir);
} }
if (!m_keepOpen) { if (!m_keepOpen) {
close(); close();
@@ -185,8 +213,17 @@ void AppLauncherWidget::initListWidget()
const QVector<DesktopAppData>& appList = m_appsMap[cat]; const QVector<DesktopAppData>& appList = m_appsMap[cat];
addAppsToListWidget(itemsWidget, appList); 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( m_tabWidget->addTab(
itemsWidget, QIcon::fromTheme(iconName), QLatin1String("")); itemsWidget, QIcon::fromTheme(iconName), QLatin1String(""));
#endif
m_tabWidget->setTabToolTip(m_tabWidget->count(), cat); m_tabWidget->setTabToolTip(m_tabWidget->count(), cat);
if (cat == QLatin1String("Graphics")) { if (cat == QLatin1String("Graphics")) {
m_tabWidget->setCurrentIndex(m_tabWidget->count() - 1); m_tabWidget->setCurrentIndex(m_tabWidget->count() - 1);
@@ -215,18 +252,21 @@ void AppLauncherWidget::initAppMap()
QStringList multimediaNames; QStringList multimediaNames;
multimediaNames << QStringLiteral("AudioVideo") << QStringLiteral("Audio") multimediaNames << QStringLiteral("AudioVideo") << QStringLiteral("Audio")
<< QStringLiteral("Video"); << QStringLiteral("Video");
for (const QString& name : multimediaNames) { for (const QString& name : qAsConst(multimediaNames)) {
if (!m_appsMap.contains(name)) { if (!m_appsMap.contains(name)) {
continue; continue;
} }
for (auto i : m_appsMap[name]) { for (const auto& i : m_appsMap[name]) {
if (!multimediaList.contains(i)) { if (!multimediaList.contains(i)) {
multimediaList.append(i); multimediaList.append(i);
} }
} }
m_appsMap.remove(name); m_appsMap.remove(name);
} }
m_appsMap.insert(QStringLiteral("Multimedia"), multimediaList);
if (!multimediaList.isEmpty()) {
m_appsMap.insert(QStringLiteral("Multimedia"), multimediaList);
}
} }
void AppLauncherWidget::configureListView(QListWidget* widget) void AppLauncherWidget::configureListView(QListWidget* widget)

View File

@@ -3,10 +3,15 @@
#pragma once #pragma once
#include "src/utils/desktopfileparse.h"
#include <QMap> #include <QMap>
#include <QWidget> #include <QWidget>
#if defined(Q_OS_WIN)
#include "src/utils/winlnkfileparse.h"
#else
#include "src/utils/desktopfileparse.h"
#endif
class QTabWidget; class QTabWidget;
class QCheckBox; class QCheckBox;
class QVBoxLayout; class QVBoxLayout;
@@ -32,7 +37,11 @@ private:
const QVector<DesktopAppData>& appList); const QVector<DesktopAppData>& appList);
void keyPressEvent(QKeyEvent* keyEvent) override; void keyPressEvent(QKeyEvent* keyEvent) override;
#if defined(Q_OS_WIN)
WinLnkFileParser m_parser;
#else
DesktopFileParser m_parser; DesktopFileParser m_parser;
#endif
QPixmap m_pixmap; QPixmap m_pixmap;
QString m_tempFile; QString m_tempFile;
bool m_keepOpen; bool m_keepOpen;

View File

@@ -26,5 +26,12 @@ target_sources(
colorutils.cpp colorutils.cpp
history.cpp history.cpp
strfparse.cpp strfparse.cpp
request.cpp request.cpp
) )
IF (WIN32)
target_sources(
flameshot
PRIVATE winlnkfileparse.cpp
)
ENDIF()

View File

@@ -114,7 +114,7 @@ int DesktopFileParser::processDirectory(const QDir& dir)
dir.entryList({ "*.desktop" }, QDir::NoDotAndDotDot | QDir::Files); dir.entryList({ "*.desktop" }, QDir::NoDotAndDotDot | QDir::Files);
bool ok; bool ok;
int length = m_appList.length(); int length = m_appList.length();
for (QString file : entries) { for (const QString& file : entries) {
DesktopAppData app = parseDesktopFile(dir.absoluteFilePath(file), ok); DesktopAppData app = parseDesktopFile(dir.absoluteFilePath(file), ok);
if (ok) { if (ok) {
m_appList.append(app); m_appList.append(app);
@@ -127,7 +127,7 @@ QVector<DesktopAppData> DesktopFileParser::getAppsByCategory(
const QString& category) const QString& category)
{ {
QVector<DesktopAppData> res; QVector<DesktopAppData> res;
for (const DesktopAppData& app : m_appList) { for (const DesktopAppData& app : qAsConst(m_appList)) {
if (app.categories.contains(category)) { if (app.categories.contains(category)) {
res.append(app); res.append(app);
} }
@@ -139,7 +139,7 @@ QMap<QString, QVector<DesktopAppData>> DesktopFileParser::getAppsByCategory(
const QStringList& categories) const QStringList& categories)
{ {
QMap<QString, QVector<DesktopAppData>> res; QMap<QString, QVector<DesktopAppData>> res;
for (const DesktopAppData& app : m_appList) { for (const DesktopAppData& app : qAsConst(m_appList)) {
for (const QString& category : categories) { for (const QString& category : categories) {
if (app.categories.contains(category)) { if (app.categories.contains(category)) {
res[category].append(app); res[category].append(app);

View File

@@ -0,0 +1,160 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2017-2019 Alejandro Sirgo Rica & Contributors
#include "winlnkfileparse.h"
#include <QDir>
#include <QDirIterator>
#include <QFileSystemModel>
#include <QImageWriter>
#include <QRegularExpression>
#include <QSettings>
#include <QString>
#include <shlobj.h>
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<DesktopAppData> WinLnkFileParser::getAppsByCategory(
const QString& category)
{
QVector<DesktopAppData> 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<QString, QVector<DesktopAppData>> WinLnkFileParser::getAppsByCategory(
const QStringList& categories)
{
QMap<QString, QVector<DesktopAppData>> res;
QVector<DesktopAppData> 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;
}
}
}
}
}

View File

@@ -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 <QFileInfo>
#include <QIcon>
#include <QMap>
#include <QStringList>
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<DesktopAppData> getAppsByCategory(const QString& category);
QMap<QString, QVector<DesktopAppData>> getAppsByCategory(
const QStringList& categories);
private:
void getImageFileExtAssociates(const QStringList& sListImgExt);
QVector<DesktopAppData> m_appList;
QStringList m_GraphicAppsList;
};

View File

@@ -118,9 +118,12 @@ CaptureWidget::CaptureWidget(const CaptureRequest& req,
m_context.origScreenshot = m_context.screenshot; m_context.origScreenshot = m_context.screenshot;
#if defined(Q_OS_WIN) #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 | setWindowFlags(Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint |
Qt::SubWindow // Hides the taskbar icon Qt::SubWindow // Hides the taskbar icon
); );
#endif
for (QScreen* const screen : QGuiApplication::screens()) { for (QScreen* const screen : QGuiApplication::screens()) {
QPoint topLeftScreen = screen->geometry().topLeft(); QPoint topLeftScreen = screen->geometry().topLeft();