From 211a442d90967f09bd7bcc0c66edcf181485318b Mon Sep 17 00:00:00 2001 From: reionwong Date: Thu, 29 Jul 2021 03:30:17 +0800 Subject: [PATCH] Add management app --- CMakeLists.txt | 4 + helper/filelauncher.cpp | 78 ++++++ helper/filelauncher.h | 37 +++ mimetype/mimeappmanager.cpp | 433 +++++++++++++++++++++++++++++++++ mimetype/mimeappmanager.h | 76 ++++++ mimetype/xdgdesktopfile.cpp | 153 ++++++++++++ mimetype/xdgdesktopfile.h | 53 ++++ model/foldermodel.cpp | 26 +- model/foldermodel.h | 2 + qml/FolderGridItem.qml | 2 +- qml/FolderListItem.qml | 1 + thumbnailer/thumbnailcache.cpp | 16 -- 12 files changed, 860 insertions(+), 21 deletions(-) create mode 100644 helper/filelauncher.cpp create mode 100644 helper/filelauncher.h create mode 100644 mimetype/mimeappmanager.cpp create mode 100644 mimetype/mimeappmanager.h create mode 100644 mimetype/xdgdesktopfile.cpp create mode 100644 mimetype/xdgdesktopfile.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b013fa3..6f6b293 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,6 +38,10 @@ add_executable(cutefish-filemanager helper/pathhistory.cpp helper/fm.cpp helper/shortcut.cpp + helper/filelauncher.cpp + + mimetype/mimeappmanager.cpp + mimetype/xdgdesktopfile.cpp thumbnailer/thumbnailprovider.cpp thumbnailer/thumbnailcache.cpp diff --git a/helper/filelauncher.cpp b/helper/filelauncher.cpp new file mode 100644 index 0000000..eb5c969 --- /dev/null +++ b/helper/filelauncher.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2021 CutefishOS Team. + * + * Author: Reion Wong + * + * This program 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 + * any later version. + * + * This program 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 this program. If not, see . + */ + +#include "filelauncher.h" + +#include +#include +#include +#include +#include + +FileLauncher *SELF = nullptr; + +FileLauncher *FileLauncher::self() +{ + if (SELF == nullptr) + SELF = new FileLauncher; + + return SELF; +} + +FileLauncher::FileLauncher(QObject *parent) + : QObject(parent) +{ + +} + +bool FileLauncher::launchApp(const QString &desktopFile, const QString &fileName) +{ + QSettings settings(desktopFile, QSettings::IniFormat); + settings.beginGroup("Desktop Entry"); + + QStringList list = settings.value("Exec").toString().split(' '); + QStringList args; + + if (list.isEmpty() || list.size() < 0) + return false; + + QString exec = list.first(); + list.removeOne(exec); + + for (const QString &arg : list) { + QString newArg = arg; + + if (newArg.startsWith("%F", Qt::CaseInsensitive)) + newArg.replace("%F", fileName, Qt::CaseInsensitive); + + if (newArg.startsWith("%U", Qt::CaseInsensitive)) + newArg.replace("%U", fileName, Qt::CaseInsensitive); + + args.append(newArg); + } + + qDebug() << "launchApp()" << exec << args; + + return QProcess::startDetached(exec, args); +} + +bool FileLauncher::launchExecutable(const QString &fileName) +{ + return QProcess::startDetached(fileName, QStringList()); +} diff --git a/helper/filelauncher.h b/helper/filelauncher.h new file mode 100644 index 0000000..6d4e46c --- /dev/null +++ b/helper/filelauncher.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2021 CutefishOS Team. + * + * Author: Reion Wong + * + * This program 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 + * any later version. + * + * This program 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 this program. If not, see . + */ + +#ifndef FILELAUNCHER_H +#define FILELAUNCHER_H + +#include + +class FileLauncher : public QObject +{ + Q_OBJECT + +public: + static FileLauncher *self(); + explicit FileLauncher(QObject *parent = nullptr); + + Q_INVOKABLE bool launchApp(const QString &desktopFile, const QString &fileName); + Q_INVOKABLE bool launchExecutable(const QString &fileName); +}; + +#endif // FILELAUNCHER_H diff --git a/mimetype/mimeappmanager.cpp b/mimetype/mimeappmanager.cpp new file mode 100644 index 0000000..46b39b8 --- /dev/null +++ b/mimetype/mimeappmanager.cpp @@ -0,0 +1,433 @@ +/* + * Copyright (C) 2021 CutefishOS Team. + * + * Author: Reion Wong + * + * This program 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 + * any later version. + * + * This program 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 this program. If not, see . + */ + +#include "mimeappmanager.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +static MimeAppManager *SELF = nullptr; + +MimeAppManager *MimeAppManager::self() +{ + if (!SELF) + SELF = new MimeAppManager; + + return SELF; +} + +MimeAppManager::MimeAppManager(QObject *parent) + : QObject(parent), + m_fileSystemWatcher(new QFileSystemWatcher), + m_updateTimer(new QTimer(this)) +{ + m_updateTimer->setInterval(100); + m_updateTimer->setSingleShot(true); + + m_fileSystemWatcher->addPaths(desktopPaths()); + + connect(m_fileSystemWatcher, &QFileSystemWatcher::directoryChanged, this, &MimeAppManager::onFileChanged); + connect(m_fileSystemWatcher, &QFileSystemWatcher::fileChanged, this, &MimeAppManager::onFileChanged); + connect(m_updateTimer, &QTimer::timeout, this, &MimeAppManager::initApplications); + + m_updateTimer->start(); +} + +QStringList MimeAppManager::desktopPaths() +{ + QStringList folders; + folders << QString("/usr/share/applications") + << QString("/usr/local/share/applications/") + << QDir::homePath() + QString("/.local/share/applications"); + + return folders; +} + +QString MimeAppManager::mimeAppsListFilePath() +{ + return QString("%1/.config/mimeapps.list").arg(QDir::homePath()); +} + +void MimeAppManager::initApplications() +{ + m_desktopFiles.clear(); + m_desktopObjects.clear(); + + QMap> mimeAppsSet; + + for (const QString &folder : desktopPaths()) { + QDirIterator itor(folder, QStringList("*.desktop"), QDir::Files | QDir::NoDotAndDotDot, QDirIterator::Subdirectories); + while (itor.hasNext()) { + itor.next(); + QString filePath = itor.filePath(); + XdgDesktopFile desktopFile(filePath); + + if (!desktopFile.valid()) + continue; + + m_desktopFiles.append(filePath); + m_desktopObjects.insert(filePath, desktopFile); + + // Load terminal + QStringList categories = desktopFile.value("Categories").toString().split(";"); + if (categories.contains("TerminalEmulator")) { + m_terminalApps.append(desktopFile); + } + + QStringList mimeTypes = desktopFile.value("MimeType").toString().trimmed().split(";"); + for (const QString &mimeType : mimeTypes) { + if (!mimeType.isEmpty()) { + QSet apps; + if (mimeAppsSet.contains(mimeType)) { + apps = mimeAppsSet.value(mimeType); + apps.insert(filePath); + } else { + apps.insert(filePath); + } + + mimeAppsSet.insert(mimeType, apps); + } + } + } + } + + for (const QString &key : mimeAppsSet.keys()) { + QSet apps = mimeAppsSet.value(key); + QStringList orderApps; + + if (apps.count() > 1) { + QFileInfoList fileInfos; + for (const QString &app : apps) { + QFileInfo info(app); + fileInfos.append(info); + } + + std::sort(fileInfos.begin(), fileInfos.end(), [=] (const QFileInfo &f1, const QFileInfo &f2) { + return f1.birthTime() < f2.birthTime(); + }); + + for (QFileInfo info : fileInfos) { + orderApps.append(info.absoluteFilePath()); + } + } else { + orderApps.append(apps.values()); + } + + m_mimeApps.insert(key, orderApps); + } + + // Check from cache. + // ref: https://specifications.freedesktop.org/desktop-entry-spec/0.9.5/ar01s07.html + + QFile file("/usr/share/applications/mimeinfo.cache"); + if (!file.open(QIODevice::ReadOnly)) + return; + + QStringList audioDesktopList; + QStringList imageDeksopList; + QStringList textDekstopList; + QStringList videoDesktopList; + + while (!file.atEnd()) { + QString line = file.readLine(); + QString mimeType = line.split("=").first(); + QString _desktops = line.split("=").last(); + QStringList desktops = _desktops.split(";"); + + for (const QString &desktop : desktops) { + if (desktop.isEmpty() || audioDesktopList.contains(desktop)) + continue; + + if (mimeType.startsWith("audio")) { + if (!audioDesktopList.contains(desktop)) + audioDesktopList.append(desktop); + } else if (mimeType.startsWith("image")) { + if (!imageDeksopList.contains(desktop)) + imageDeksopList.append(desktop); + } else if (mimeType.startsWith("text")) { + if (!textDekstopList.contains(desktop)) + textDekstopList.append(desktop); + } else if (mimeType.startsWith("video")) { + if (!videoDesktopList.contains(desktop)) + videoDesktopList.append(desktop); + } + } + } + + file.close(); + + const QString mimeInfoCacheRootPath = "/usr/share/applications"; + for (const QString &desktop : audioDesktopList) { + const QString &path = QString("%1/%2").arg(mimeInfoCacheRootPath, desktop); + if (!QFile::exists(path)) + continue; + + XdgDesktopFile desktopFile(path); + if (desktopFile.valid()) + m_audioMimeApps.insert(path, desktopFile); + } + + for (const QString &desktop : imageDeksopList) { + const QString &path = QString("%1/%2").arg(mimeInfoCacheRootPath, desktop); + if (!QFile::exists(path)) + continue; + + XdgDesktopFile desktopFile(path); + if (desktopFile.valid()) + m_imageMimeApps.insert(path, desktopFile); + } + + for (const QString &desktop : textDekstopList) { + const QString &path = QString("%1/%2").arg(mimeInfoCacheRootPath, desktop); + if (!QFile::exists(path)) + continue; + + XdgDesktopFile desktopFile(path); + if (desktopFile.valid()) + m_textMimeApps.insert(path, desktopFile); + } + + for (const QString &desktop : videoDesktopList) { + const QString &path = QString("%1/%2").arg(mimeInfoCacheRootPath, desktop); + if (!QFile::exists(path)) + continue; + + XdgDesktopFile desktopFile(path); + if (desktopFile.valid()) + m_videoMimeApps.insert(path, desktopFile); + } +} + +QString MimeAppManager::getDefaultAppByFilePath(const QString &filePath) +{ + return getDefaultAppByMimeType(QMimeDatabase().mimeTypeForFile(filePath)); +} + +QString MimeAppManager::getDefaultAppByMimeType(const QMimeType &mimeType) +{ + QString mimeappsFile = mimeAppsListFilePath(); + + if (!QFile::exists(mimeappsFile)) + return QString(); + + QSettings settings(mimeappsFile, QSettings::IniFormat); + + // for (const QString group : settings.childGroups()) { + // settings.beginGroup(group); + // for (const QString &key : settings.allKeys()) { + // if (key == mimeType.name()) + // return settings.value(key).toString(); + // } + // settings.endGroup(); + // } + + settings.beginGroup("Default Applications"); + // TODO: User applications directory? + if (settings.contains(mimeType.name())) + return QString("/usr/share/applications/%1").arg(settings.value(mimeType.name()).toString()); + + settings.endGroup(); + + settings.beginGroup("Added Associations"); + if (settings.contains(mimeType.name())) + return QString("/usr/share/applications/%1").arg(settings.value(mimeType.name()).toString()); + + return QString(); +} + +QString MimeAppManager::getDefaultAppDesktopByMimeType(const QString &mimeType) +{ + return getDefaultAppByMimeType(QMimeDatabase().mimeTypeForName(mimeType)); +} + +bool MimeAppManager::setDefaultAppForType(const QString &mimeType, const QString &app) +{ + // ref: https://specifications.freedesktop.org/mime-apps-spec/1.0.1/ar01s03.html + + QString mimeappsFile = mimeAppsListFilePath(); + QString desktop = app; + + if (QFile::exists(desktop)) { + QFileInfo info(desktop); + desktop = info.completeBaseName(); + } + +// QSettings settings(mimeappsFile, QSettings::IniFormat); +// settings.setIniCodec("UTF-8"); + +// if (!settings.isWritable()) +// return false; + +// settings.beginGroup("Default Applications"); +// settings.setValue(mimeType, desktop); +// settings.sync(); + + return true; +} + +bool MimeAppManager::setDefaultAppForFile(const QString &filePath, const QString &desktop) +{ + // ref: https://specifications.freedesktop.org/mime-apps-spec/1.0.1/ar01s03.html + + QString mimeappsFile = mimeAppsListFilePath(); + QMimeType mimeType; + QString value = desktop; + + if (!QFile::exists(filePath)) + return false; + else + mimeType = QMimeDatabase().mimeTypeForFile(filePath); + + if (QFile::exists(value)) { + QFileInfo info(value); + value = info.fileName(); + } + +// QSettings settings(mimeappsFile, QSettings::IniFormat); +// settings.setIniCodec("UTF-8"); + +// if (!settings.isWritable()) +// return false; + +// settings.beginGroup("Default Applications"); // Added Associations +// settings.setValue(mimeType.name(), value); +// settings.sync(); + + return true; +} + +QStringList MimeAppManager::getRecommendedAppsByFilePath(const QString &filePath) +{ + return getRecommendedAppsByMimeType(QMimeDatabase().mimeTypeForFile(filePath)); +} + +QStringList MimeAppManager::getRecommendedAppsByMimeType(const QMimeType &mimeType) +{ + QStringList recommendApps; + QList mimeTypeList; + QMimeDatabase mimeDatabase; + + mimeTypeList.append(mimeType); + + while (recommendApps.isEmpty()) { + for (const QMimeType &type : mimeTypeList) { + QStringList typeNameList; + + typeNameList.append(type.name()); + typeNameList.append(type.aliases()); + + for (const QString &name : typeNameList) { + for (const QString &app : m_mimeApps.value(name)) { + bool exists = false; + + for (const QString &other : recommendApps) { + const XdgDesktopFile &appDesktop = m_desktopObjects.value(app); + const XdgDesktopFile &otherDesktop = m_desktopObjects.value(other); + + if (appDesktop.value("Exec").toString() == otherDesktop.value("Exec").toString() && + appDesktop.localeName() == otherDesktop.localeName()) { + exists = true; + break; + } + } + + // if desktop file was not existed do not recommend!! + if (!QFileInfo::exists(app)) { + qWarning() << app << "not exist anymore"; + continue; + } + + if (!exists) + recommendApps.append(app); + } + } + } + + if (!recommendApps.isEmpty()) + break; + + QList newMimeTypeList; + for (const QMimeType &type : mimeTypeList) { + for (const QString &name : type.parentMimeTypes()) + newMimeTypeList.append(mimeDatabase.mimeTypeForName(name)); + } + + mimeTypeList = newMimeTypeList; + + if (mimeTypeList.isEmpty()) + break; + } + + return recommendApps; +} + +QVariantList MimeAppManager::recommendedApps(const QUrl &url) +{ + QVariantList list; + + if (url.isValid()) { + const QString &filePath = url.toString(); + + for (const QString &path : getRecommendedAppsByFilePath(filePath)) { + XdgDesktopFile desktop(path); + + if (desktop.valid()) + continue; + + QVariantMap item; + item["icon"] = desktop.value("IconName").toString(); + item["name"] = desktop.localeName(); + item["desktopFile"] = path; + + list << item; + } + } + + return list; +} + +void MimeAppManager::launchTerminal(const QString &path) +{ + if (m_terminalApps.isEmpty()) + return; + + QString command = m_terminalApps.first().value("Exec").toString(); + QProcess process; + process.setProgram(command); + // Launch terminal with working directory set. + process.setWorkingDirectory(path); + process.startDetached(); +} + +void MimeAppManager::onFileChanged(const QString &path) +{ + Q_UNUSED(path); + + m_updateTimer->start(); +} diff --git a/mimetype/mimeappmanager.h b/mimetype/mimeappmanager.h new file mode 100644 index 0000000..5311fd9 --- /dev/null +++ b/mimetype/mimeappmanager.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2021 CutefishOS Team. + * + * Author: Reion Wong + * + * This program 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 + * any later version. + * + * This program 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 this program. If not, see . + */ + +#ifndef MIMEAPPMANAGER_H +#define MIMEAPPMANAGER_H + +#include +#include +#include +#include +#include + +#include "xdgdesktopfile.h" + +class QMimeType; +class MimeAppManager : public QObject +{ + Q_OBJECT + +public: + static MimeAppManager *self(); + explicit MimeAppManager(QObject *parent = nullptr); + + QStringList desktopPaths(); + QString mimeAppsListFilePath(); + void initApplications(); + + QString getDefaultAppByFilePath(const QString &filePath); + QString getDefaultAppByMimeType(const QMimeType &mimeType); + QString getDefaultAppDesktopByMimeType(const QString &mimeType); + Q_INVOKABLE bool setDefaultAppForType(const QString &mimeType, const QString &app); + Q_INVOKABLE bool setDefaultAppForFile(const QString &filePath, const QString &desktop); + + QStringList getRecommendedAppsByFilePath(const QString &filePath); + QStringList getRecommendedAppsByMimeType(const QMimeType &mimeType); + + Q_INVOKABLE QVariantList recommendedApps(const QUrl &url); + + Q_INVOKABLE void launchTerminal(const QString &path); + +private slots: + void onFileChanged(const QString &path); + +private: + QStringList m_desktopFiles; + QMap m_mimeApps; + + QMap m_videoMimeApps; + QMap m_imageMimeApps; + QMap m_textMimeApps; + QMap m_audioMimeApps; + QMap m_desktopObjects; + + QList m_terminalApps; + + QFileSystemWatcher *m_fileSystemWatcher; + QTimer *m_updateTimer; +}; + +#endif // MIMEAPPMANAGER_H diff --git a/mimetype/xdgdesktopfile.cpp b/mimetype/xdgdesktopfile.cpp new file mode 100644 index 0000000..1c3ec18 --- /dev/null +++ b/mimetype/xdgdesktopfile.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2021 CutefishOS Team. + * + * Author: Reion Wong + * + * This program 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 + * any later version. + * + * This program 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 this program. If not, see . + */ + +#include "xdgdesktopfile.h" +#include +#include +#include + +XdgDesktopFile::XdgDesktopFile(const QString &fileName) + : m_isValid(false) + , m_fileName(fileName) +{ + if (!m_fileName.isEmpty()) + load(); +} + +bool XdgDesktopFile::valid() const +{ + return m_isValid; +} + +QVariant XdgDesktopFile::value(const QString &key, const QVariant &defaultValue) const +{ + QString path = (!prefix().isEmpty()) ? prefix() + QLatin1Char('/') + key : key; + QVariant res = m_items.value(path, defaultValue); + return res; +} + +void XdgDesktopFile::setValue(const QString &key, const QVariant &value) +{ + QString path = (!prefix().isEmpty()) ? prefix() + QLatin1Char('/') + key : key; + + if (value.type() == QVariant::String) { + QString s = value.toString(); + m_items[path] = QVariant(s); + } else { + m_items[path] = value; + } +} + +bool XdgDesktopFile::load() +{ + if (!QFile::exists(m_fileName)) + return false; + + m_items.clear(); + + read("Desktop Entry"); + + return m_isValid; +} + +bool XdgDesktopFile::save() +{ + QFile file(m_fileName); + + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) + return false; + + QTextStream stream(&file); + QMap::const_iterator i = m_items.constBegin(); + QString section; + + while (i != m_items.constBegin()) { + QString path = i.key(); + QString sect = path.section(QChar('/'), 0, 0); + + if (sect != section) { + section = sect; + stream << QLatin1Char('[') << section << QChar(']') << Qt::endl; + } + + QString key = path.section(QChar('/'), 1); + stream << key << QLatin1Char('=') << i.value().toString() << Qt::endl; + ++i; + } + + return true; +} + +QString XdgDesktopFile::localeName() const +{ + QString localeKey = QString("Name[%1]").arg(QLocale::system().name()); + + if (m_items.contains(localeKey)) { + return m_items[localeKey].toString(); + } + + return m_items["Name"].toString(); +} + +QString XdgDesktopFile::prefix() const +{ + return QLatin1String("Desktop Entry"); +} + +bool XdgDesktopFile::read(const QString &prefix) +{ + QFile file(m_fileName); + + // Can't open file. + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) + return false; + + QTextStream stream(&file); + QString section; + bool prefixExists = false; + + while (!stream.atEnd()) { + QString line = stream.readLine().trimmed(); + + // Skip comments. + if (line.startsWith("#")) + continue; + + // Find the prefix string. + if (line.startsWith(QChar('[')) && line.endsWith(QChar(']'))) { + section = line.mid(1, line.length() - 2); + + if (section == prefix) + prefixExists = true; + + continue; + } + + QString key = line.section(QLatin1Char('='), 0, 0).trimmed(); + QString value = line.section(QLatin1Char('='), 1).trimmed(); + + if (key.isEmpty()) + continue; + + m_items[section + QLatin1Char('/') + key] = QVariant(value); + } + + m_isValid = (prefix.isEmpty()) || prefixExists; + return m_isValid; +} diff --git a/mimetype/xdgdesktopfile.h b/mimetype/xdgdesktopfile.h new file mode 100644 index 0000000..44abe35 --- /dev/null +++ b/mimetype/xdgdesktopfile.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2021 CutefishOS Team. + * + * Author: Reion Wong + * + * This program 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 + * any later version. + * + * This program 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 this program. If not, see . + */ + +#ifndef XDGDESKTOPFILE_H +#define XDGDESKTOPFILE_H + +#include +#include +#include + +class XdgDesktopFile +{ +public: + explicit XdgDesktopFile(const QString &fileName = QString()); + + bool valid() const; + + QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const; + void setValue(const QString &key, const QVariant &value); + + bool load(); + bool save(); + + QString localeName() const; + + QString prefix() const; + +private: + bool read(const QString &prefix); + +private: + bool m_isValid; + QString m_fileName; + QMap m_items; +}; + +#endif // XDGDESKTOPFILE_H diff --git a/model/foldermodel.cpp b/model/foldermodel.cpp index 91ff07f..895ca4c 100644 --- a/model/foldermodel.cpp +++ b/model/foldermodel.cpp @@ -29,6 +29,7 @@ #include "../dialogs/createfolderdialog.h" #include "../helper/datehelper.h" +#include "../helper/filelauncher.h" // Qt #include @@ -45,6 +46,7 @@ #include #include #include +#include // Qt Quick #include @@ -63,8 +65,6 @@ #include #include #include -#include -#include FolderModel::FolderModel(QObject *parent) : QSortFilterProxyModel(parent) @@ -77,6 +77,7 @@ FolderModel::FolderModel(QObject *parent) , m_actionCollection(this) , m_dragInProgress(false) , m_viewAdapter(nullptr) + , m_mimeAppManager(MimeAppManager::self()) { DirLister *dirLister = new DirLister(this); dirLister->setDelayedMimeTypes(true); @@ -714,7 +715,24 @@ void FolderModel::openSelected() } for (const QUrl &url : urls) { - (void)new KRun(url, nullptr); + KFileItem item(url); + QString defaultAppDesktopFile = m_mimeAppManager->getDefaultAppByMimeType(item.currentMimeType()); + + // If no default application is found, + // look for the first one of the frequently used applications. + if (defaultAppDesktopFile.isEmpty()) { + QStringList recommendApps = m_mimeAppManager->getRecommendedAppsByMimeType(item.currentMimeType()); + if (recommendApps.count() > 0) { + defaultAppDesktopFile = recommendApps.first(); + } + } + + if (!defaultAppDesktopFile.isEmpty()) { + FileLauncher::self()->launchApp(defaultAppDesktopFile, url.toLocalFile()); + continue; + } + + QDesktopServices::openUrl(url); } } @@ -928,7 +946,7 @@ void FolderModel::openInTerminal() url = rootItem().url().toLocalFile(); } - KToolInvocation::invokeTerminal(QString(), url); + m_mimeAppManager->launchTerminal(url); } void FolderModel::openChangeWallpaperDialog() diff --git a/model/foldermodel.h b/model/foldermodel.h index a5027f0..8fa92a6 100644 --- a/model/foldermodel.h +++ b/model/foldermodel.h @@ -27,6 +27,7 @@ #include "../widgets/itemviewadapter.h" #include "../helper/pathhistory.h" +#include "../mimetype/mimeappmanager.h" #include #include @@ -237,6 +238,7 @@ private: // Save path history PathHistory m_pathHistory; + MimeAppManager *m_mimeAppManager; }; #endif // FOLDERMODEL_H diff --git a/qml/FolderGridItem.qml b/qml/FolderGridItem.qml index f43a86d..3c8d92b 100644 --- a/qml/FolderGridItem.qml +++ b/qml/FolderGridItem.qml @@ -126,7 +126,7 @@ Item { sourceSize.height: height source: model.thumbnail ? model.thumbnail : "" asynchronous: true - cache: true + cache: false // Because of the effect of OpacityMask. ColorOverlay { diff --git a/qml/FolderListItem.qml b/qml/FolderListItem.qml index ebd176a..953c958 100644 --- a/qml/FolderListItem.qml +++ b/qml/FolderListItem.qml @@ -99,6 +99,7 @@ Item { fillMode: Image.PreserveAspectFit asynchronous: true smooth: false + cache: false layer.enabled: true layer.effect: OpacityMask { diff --git a/thumbnailer/thumbnailcache.cpp b/thumbnailer/thumbnailcache.cpp index d148879..691ecca 100644 --- a/thumbnailer/thumbnailcache.cpp +++ b/thumbnailer/thumbnailcache.cpp @@ -31,22 +31,6 @@ static ThumbnailCache *SELF = nullptr; -QImage scaleImage(const QImage &image, const QSize &requestedSize, bool crop, Qt::TransformationMode mode) -{ - const QImage scaledImage(image.size() != requestedSize - ? image.scaled(requestedSize, crop ? Qt::KeepAspectRatioByExpanding : Qt::KeepAspectRatio, mode) - : image); - - if (crop && scaledImage.size() != requestedSize) { - QRect cropRect(0, 0, requestedSize.width(), requestedSize.height()); - cropRect.moveCenter(QPoint(scaledImage.width() / 2, scaledImage.height() / 2)); - - return scaledImage.copy(cropRect); - } else { - return scaledImage; - } -} - ThumbnailCache *ThumbnailCache::self() { if (!SELF) {