Add management app
							parent
							
								
									a8c1fab1d5
								
							
						
					
					
						commit
						211a442d90
					
				@ -0,0 +1,78 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2021 CutefishOS Team.
 | 
			
		||||
 *
 | 
			
		||||
 * Author:     Reion Wong <reionwong@gmail.com>
 | 
			
		||||
 *
 | 
			
		||||
 * 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 <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "filelauncher.h"
 | 
			
		||||
 | 
			
		||||
#include <QSettings>
 | 
			
		||||
#include <QProcess>
 | 
			
		||||
#include <QFile>
 | 
			
		||||
#include <QDir>
 | 
			
		||||
#include <QDebug>
 | 
			
		||||
 | 
			
		||||
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());
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,37 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2021 CutefishOS Team.
 | 
			
		||||
 *
 | 
			
		||||
 * Author:     Reion Wong <reionwong@gmail.com>
 | 
			
		||||
 *
 | 
			
		||||
 * 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 <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef FILELAUNCHER_H
 | 
			
		||||
#define FILELAUNCHER_H
 | 
			
		||||
 | 
			
		||||
#include <QObject>
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
@ -0,0 +1,433 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2021 CutefishOS Team.
 | 
			
		||||
 *
 | 
			
		||||
 * Author:     Reion Wong <reionwong@gmail.com>
 | 
			
		||||
 *
 | 
			
		||||
 * 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 <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "mimeappmanager.h"
 | 
			
		||||
 | 
			
		||||
#include <QDirIterator>
 | 
			
		||||
#include <QUrl>
 | 
			
		||||
#include <QDir>
 | 
			
		||||
 | 
			
		||||
#include <QMimeDatabase>
 | 
			
		||||
#include <QMimeType>
 | 
			
		||||
#include <QProcess>
 | 
			
		||||
#include <QSettings>
 | 
			
		||||
#include <QDateTime>
 | 
			
		||||
#include <QThread>
 | 
			
		||||
#include <QDebug>
 | 
			
		||||
#include <QSet>
 | 
			
		||||
 | 
			
		||||
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<QString, QSet<QString>> 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<QString> 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<QString> 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<QMimeType> 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<QMimeType> 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();
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,76 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2021 CutefishOS Team.
 | 
			
		||||
 *
 | 
			
		||||
 * Author:     Reion Wong <reionwong@gmail.com>
 | 
			
		||||
 *
 | 
			
		||||
 * 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 <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef MIMEAPPMANAGER_H
 | 
			
		||||
#define MIMEAPPMANAGER_H
 | 
			
		||||
 | 
			
		||||
#include <QObject>
 | 
			
		||||
#include <QFileSystemWatcher>
 | 
			
		||||
#include <QFileInfo>
 | 
			
		||||
#include <QTimer>
 | 
			
		||||
#include <QMap>
 | 
			
		||||
 | 
			
		||||
#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<QString, QStringList> m_mimeApps;
 | 
			
		||||
 | 
			
		||||
    QMap<QString, XdgDesktopFile> m_videoMimeApps;
 | 
			
		||||
    QMap<QString, XdgDesktopFile> m_imageMimeApps;
 | 
			
		||||
    QMap<QString, XdgDesktopFile> m_textMimeApps;
 | 
			
		||||
    QMap<QString, XdgDesktopFile> m_audioMimeApps;
 | 
			
		||||
    QMap<QString, XdgDesktopFile> m_desktopObjects;
 | 
			
		||||
 | 
			
		||||
    QList<XdgDesktopFile> m_terminalApps;
 | 
			
		||||
 | 
			
		||||
    QFileSystemWatcher *m_fileSystemWatcher;
 | 
			
		||||
    QTimer *m_updateTimer;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // MIMEAPPMANAGER_H
 | 
			
		||||
@ -0,0 +1,153 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2021 CutefishOS Team.
 | 
			
		||||
 *
 | 
			
		||||
 * Author:     Reion Wong <reionwong@gmail.com>
 | 
			
		||||
 *
 | 
			
		||||
 * 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 <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "xdgdesktopfile.h"
 | 
			
		||||
#include <QTextStream>
 | 
			
		||||
#include <QFile>
 | 
			
		||||
#include <QLocale>
 | 
			
		||||
 | 
			
		||||
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<QString, QVariant>::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;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,53 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2021 CutefishOS Team.
 | 
			
		||||
 *
 | 
			
		||||
 * Author:     Reion Wong <reionwong@gmail.com>
 | 
			
		||||
 *
 | 
			
		||||
 * 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 <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef XDGDESKTOPFILE_H
 | 
			
		||||
#define XDGDESKTOPFILE_H
 | 
			
		||||
 | 
			
		||||
#include <QObject>
 | 
			
		||||
#include <QVariant>
 | 
			
		||||
#include <QMap>
 | 
			
		||||
 | 
			
		||||
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<QString, QVariant> m_items;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // XDGDESKTOPFILE_H
 | 
			
		||||
					Loading…
					
					
				
		Reference in New Issue