From 8a42ab9c53c6672fc6076b5aa8958a5a661941e9 Mon Sep 17 00:00:00 2001 From: kate Date: Sat, 29 Jan 2022 02:19:39 +0800 Subject: [PATCH] feat: add default apps settings --- CMakeLists.txt | 4 + src/application.cpp | 2 + src/defaultapplications.cpp | 255 +++++++++++++++++++++++++++++ src/defaultapplications.h | 86 ++++++++++ src/desktopproperties.cpp | 114 +++++++++++++ src/desktopproperties.h | 32 ++++ src/qml/DefaultApp/AppComboBox.qml | 31 ++++ src/qml/DefaultApp/Main.qml | 49 +++++- src/qml/SideBar.qml | 16 +- src/qml/Sound/Main.qml | 4 +- src/resources.qrc | 1 + translations/en_US.ts | 30 ++-- translations/zh_CN.ts | 30 ++-- 13 files changed, 614 insertions(+), 40 deletions(-) create mode 100644 src/defaultapplications.cpp create mode 100644 src/defaultapplications.h create mode 100644 src/desktopproperties.cpp create mode 100644 src/desktopproperties.h create mode 100644 src/qml/DefaultApp/AppComboBox.qml diff --git a/CMakeLists.txt b/CMakeLists.txt index e642a79..fb07b1a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,6 +21,7 @@ find_package(Libcrypt REQUIRED) find_package(KF5NetworkManagerQt REQUIRED) find_package(KF5ModemManagerQt REQUIRED) +find_package(KF5Config) pkg_search_module(FontConfig REQUIRED fontconfig IMPORTED_TARGET) pkg_search_module(ICU REQUIRED icu-i18n) @@ -59,6 +60,8 @@ set(SRCS src/vpn/nm-openvpn-service.h src/vpn/nm-pptp-service.h src/networkproxy.cpp + src/defaultapplications.cpp + src/desktopproperties.cpp # src/update/updatemodel.cpp ) @@ -87,6 +90,7 @@ target_link_libraries(${PROJECT_NAME} KF5::NetworkManagerQt KF5::ModemManagerQt + KF5::ConfigCore PkgConfig::FontConfig ${FREETYPE_LIBRARIES} diff --git a/src/application.cpp b/src/application.cpp index 1c4e73e..9f1bf57 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -20,6 +20,7 @@ #include "touchpad.h" #include "networkproxy.h" #include "notifications.h" +#include "defaultapplications.h" #include "cursor/cursorthememodel.h" #include "cursor/mouse.h" @@ -81,6 +82,7 @@ Application::Application(int &argc, char **argv) qmlRegisterType(uri, 1, 0, "Touchpad"); qmlRegisterType(uri, 1, 0, "NetworkProxy"); qmlRegisterType(uri, 1, 0, "Notifications"); + qmlRegisterType(uri, 1, 0, "DefaultApplications"); qmlRegisterSingletonType(uri, 1, 0, "Password", passwordSingleton); diff --git a/src/defaultapplications.cpp b/src/defaultapplications.cpp new file mode 100644 index 0000000..b7fec07 --- /dev/null +++ b/src/defaultapplications.cpp @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2021 - 2022 CutefishOS Team. + * + * Author: Kate Leet + * + * 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 "defaultapplications.h" +#include "desktopproperties.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +DefaultApplications::DefaultApplications(QObject *parent) + : QObject(parent) +{ + loadApps(); +} + +void DefaultApplications::loadApps() +{ + QDirIterator it("/usr/share/applications", { "*.desktop" }, + QDir::NoFilter, QDirIterator::Subdirectories); + + // Load apps + while (it.hasNext()) { + const auto fileName = it.next(); + if (!QFile::exists(fileName)) + continue; + + DesktopProperties desktop(fileName, "Desktop Entry"); + QString name = desktop.value(QString("Name[%1]").arg(QLocale::system().name())).toString(); + + if (name.isEmpty()) + name = desktop.value("Name").toString(); + + AppItem item; + item.path = fileName; + item.name = name; + item.icon = desktop.value("Icon").toString(); + item.mimeType = desktop.value("MimeType").toString(); + item.categories = desktop.value("Categories").toString(); + item.fileName = QFileInfo(fileName).fileName(); + + if (item.categories.contains("FileManager") + && item.mimeType.contains("inode/directory")) { + m_fileManagerList.append(item); + } else if (item.categories.contains("WebBrowser") + && item.mimeType.contains("x-scheme-handler/http")) { + m_browserList.append(item); + } else if (item.categories.contains("Email") + && item.mimeType.contains("x-scheme-handler/mailto")) { + m_emailList.append(item); + } else if (item.categories.contains("TerminalEmulator")) { + m_terminalList.append(item); + } + } + + // Load xdg config. + QSettings mimeApps(mimeAppsListFilePath(), QSettings::IniFormat); + mimeApps.beginGroup("Default Applications"); + + QSettings settings("cutefishos", "defaultApps"); + + QString defaultBrowser = mimeApps.value("x-scheme-handler/http").toString(); + QString defaultFM = mimeApps.value("inode/directory").toString(); + QString defaultEMail = mimeApps.value("x-scheme-handler/mailto").toString(); + QString defaultTerminal = settings.value("terminal").toString(); + + // Init indexes. + for (int i = 0; i < m_browserList.size(); ++i) { + if (defaultBrowser == m_browserList.at(i).fileName) { + m_browserIndex = i; + break; + } + } + + for (int i = 0; i < m_fileManagerList.size(); ++i) { + if (defaultFM == m_fileManagerList.at(i).fileName) { + m_fileManagerIndex = i; + break; + } + } + + for (int i = 0; i < m_emailList.size(); ++i) { + if (defaultEMail == m_emailList.at(i).fileName) { + m_emailIndex = i; + break; + } + } + + for (int i = 0; i < m_terminalList.size(); ++i) { + if (defaultTerminal == m_terminalList.at(i).fileName) { + m_terminalIndex = i; + break; + } + } +} + +QVariantList DefaultApplications::browserList() +{ + QVariantList list; + + for (const AppItem &item : m_browserList) { + QVariantMap map; + map["name"] = item.name; + map["icon"] = item.icon; + map["path"] = item.path; + list << map; + } + + return list; +} + +QVariantList DefaultApplications::fileManagerList() +{ + QVariantList list; + + for (const AppItem &item : m_fileManagerList) { + QVariantMap map; + map["name"] = item.name; + map["icon"] = item.icon; + map["path"] = item.path; + list << map; + } + + return list; +} + +QVariantList DefaultApplications::emailList() +{ + QVariantList list; + + for (const AppItem &item : m_emailList) { + QVariantMap map; + map["name"] = item.name; + map["icon"] = item.icon; + map["path"] = item.path; + list << map; + } + + return list; +} + +QVariantList DefaultApplications::terminalList() +{ + QVariantList list; + + for (const AppItem &item : m_terminalList) { + QVariantMap map; + map["name"] = item.name; + map["icon"] = item.icon; + map["path"] = item.path; + list << map; + } + + return list; +} + +int DefaultApplications::browserIndex() +{ + return m_browserIndex; +} + +int DefaultApplications::fileManagerIndex() +{ + return m_fileManagerIndex; +} + +int DefaultApplications::emailIndex() +{ + return m_emailIndex; +} + +int DefaultApplications::terminalIndex() +{ + return m_terminalIndex; +} + +void DefaultApplications::setDefaultBrowser(int index) +{ + if (!m_browserList.isEmpty() && m_browserList.size() < index) + return; + + const QString desktop = m_browserList.at(index).fileName; + + setDefaultApp("x-scheme-handler/http", desktop); + setDefaultApp("x-scheme-handler/https", desktop); +} + +void DefaultApplications::setDefaultFileManager(int index) +{ + if (!m_fileManagerList.isEmpty() && m_fileManagerList.size() < index) + return; + + const QString desktop = m_fileManagerList.at(index).fileName; + + setDefaultApp("inode/directory", desktop); +} + +void DefaultApplications::setDefaultEMail(int index) +{ + if (!m_emailList.isEmpty() && m_emailList.size() < index) + return; + + const QString desktop = m_emailList.at(index).fileName; + + setDefaultApp("x-scheme-handler/mailto", desktop); +} + +void DefaultApplications::setDefaultTerminal(int index) +{ + if (!m_terminalList.isEmpty() && m_terminalList.size() < index) + return; + + const QString desktop = m_terminalList.at(index).fileName; + + qDebug() << index << desktop; + + QSettings settings("cutefishos", "defaultApps"); + settings.setValue("terminal", desktop); +} + +void DefaultApplications::setDefaultApp(const QString &mimeType, const QString &path) +{ + KSharedConfig::Ptr profile = KSharedConfig::openConfig(QStringLiteral("mimeapps.list"), + KConfig::NoGlobals, + QStandardPaths::GenericConfigLocation); + KConfigGroup defaultApp(profile, "Default Applications"); + defaultApp.writeXdgListEntry(mimeType, {path}); +} + +QString DefaultApplications::mimeAppsListFilePath() const +{ + return QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + QLatin1String("/mimeapps.list"); +} diff --git a/src/defaultapplications.h b/src/defaultapplications.h new file mode 100644 index 0000000..91f54c6 --- /dev/null +++ b/src/defaultapplications.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2021 - 2022 CutefishOS Team. + * + * Author: Kate Leet + * + * 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 DEFAULTAPPLICATIONS_H +#define DEFAULTAPPLICATIONS_H + +#include + +class AppItem { +public: + QString path; + QString fileName; + QString name; + QString icon; + QString mimeType; + QString categories; +}; + +class DefaultApplications : public QObject +{ + Q_OBJECT + Q_PROPERTY(QVariantList browserList READ browserList NOTIFY loadFinished) + Q_PROPERTY(QVariantList fileManagerList READ fileManagerList NOTIFY loadFinished) + Q_PROPERTY(QVariantList emailList READ emailList NOTIFY loadFinished) + Q_PROPERTY(QVariantList terminalList READ terminalList NOTIFY loadFinished) + Q_PROPERTY(int browserIndex READ browserIndex NOTIFY loadFinished) + Q_PROPERTY(int fileManagerIndex READ fileManagerIndex NOTIFY loadFinished) + Q_PROPERTY(int emailIndex READ emailIndex NOTIFY loadFinished) + Q_PROPERTY(int terminalIndex READ terminalIndex NOTIFY loadFinished) + +public: + explicit DefaultApplications(QObject *parent = nullptr); + + void loadApps(); + + QVariantList browserList(); + QVariantList fileManagerList(); + QVariantList emailList(); + QVariantList terminalList(); + + int browserIndex(); + int fileManagerIndex(); + int emailIndex(); + int terminalIndex(); + + Q_INVOKABLE void setDefaultBrowser(int index); + Q_INVOKABLE void setDefaultFileManager(int index); + Q_INVOKABLE void setDefaultEMail(int index); + Q_INVOKABLE void setDefaultTerminal(int index); + +private: + void setDefaultApp(const QString &mimeType, const QString &path); + QString mimeAppsListFilePath() const; + +signals: + void loadFinished(); + +private: + QList m_browserList; + QList m_fileManagerList; + QList m_emailList; + QList m_terminalList; + + int m_browserIndex = -1; + int m_fileManagerIndex = -1; + int m_emailIndex = -1; + int m_terminalIndex = -1; +}; + +#endif // DEFAULTAPPLICATIONS_H diff --git a/src/desktopproperties.cpp b/src/desktopproperties.cpp new file mode 100644 index 0000000..c7327d8 --- /dev/null +++ b/src/desktopproperties.cpp @@ -0,0 +1,114 @@ +#include "desktopproperties.h" +#include +#include +#include +#include + +DesktopProperties::DesktopProperties(const QString &fileName, const QString &group) +{ + if (!fileName.isEmpty()) { + load(fileName, group); + } +} + +DesktopProperties::~DesktopProperties() +{ +} + +bool DesktopProperties::load(const QString &fileName, const QString &group) +{ + // NOTE: This class is used for reading of property files instead of QSettings + // class, which considers separator ';' as comment + + // Try open file + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + return false; + } + + // Clear old data + data.clear(); + + // Indicator whether group was found or not, if name of group was not + // specified, groupFound is always true + bool groupFound = group.isEmpty(); + + // Read propeties + QTextStream in(&file); + while (!in.atEnd()) { + + // Read new line + QString line = in.readLine(); + + // Skip empty line or line with invalid format + if (line.trimmed().isEmpty()) { + continue; + } + + // Read group + // NOTE: symbols '[' and ']' can be found not only in group names, but + // only group can start with '[' + if (!group.isEmpty() && line.trimmed().startsWith("[")) { + QString tmp = line.trimmed().replace("[", "").replace("]", ""); + groupFound = group.trimmed().compare(tmp) == 0; + } + + // If we are in correct group and line contains assignment then read data + int first_equal = line.indexOf('='); + + if (groupFound && first_equal >= 0) { + data.insert(line.left(first_equal).trimmed(), line.mid(first_equal + 1).trimmed()); + } + } + file.close(); + + return true; +} + +bool DesktopProperties::save(const QString &fileName, const QString &group) +{ + // Try open file + QFile file(fileName); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + return false; + } + + // Write group + QTextStream out(&file); + if (!group.isEmpty()) { + out << "[" + group + "]\n"; + } + + // Write data + foreach (QString key, data.keys()) { + out << key << "=" << data.value(key).toString() << "\n"; + } + + // Exit + file.close(); + return true; +} + +bool DesktopProperties::contains(const QString &key) const +{ + return data.contains(key); +} + +QVariant DesktopProperties::value(const QString &key, const QVariant &defaultValue) +{ + return data.value(key, defaultValue); +} + +void DesktopProperties::set(const QString &key, const QVariant &value) +{ + if (data.contains(key)) { + data.take(key); + } + + data.insert(key, value); +} + +QStringList DesktopProperties::allKeys() const +{ + return data.keys(); +} diff --git a/src/desktopproperties.h b/src/desktopproperties.h new file mode 100644 index 0000000..4962427 --- /dev/null +++ b/src/desktopproperties.h @@ -0,0 +1,32 @@ +#ifndef DESKTOPPROPERTIES_H +#define DESKTOPPROPERTIES_H + +#include +#include +#include + +/** + * @class DesktopProperties + * @brief Read property files + * @author Michal Rost + * @date 26.1.2013 + */ + +class DesktopProperties +{ +public: + DesktopProperties(const QString &fileName = "", const QString &group = ""); + ~DesktopProperties(); + + QVariant value(const QString &key, const QVariant &defaultValue = QVariant()); + bool load(const QString &fileName, const QString &group = ""); + bool save(const QString &fileName, const QString &group = ""); + void set(const QString &key, const QVariant &value); + bool contains(const QString &key) const; + QStringList allKeys() const; + +protected: + QMap data; +}; + +#endif diff --git a/src/qml/DefaultApp/AppComboBox.qml b/src/qml/DefaultApp/AppComboBox.qml new file mode 100644 index 0000000..138afb4 --- /dev/null +++ b/src/qml/DefaultApp/AppComboBox.qml @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2021 - 2022 CutefishOS Team. + * + * Author: Kate Leet + * + * 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 . + */ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Layouts 1.12 + +import FishUI 1.0 as FishUI +import Cutefish.Settings 1.0 +import "../" + +ComboBox { + id: control + textRole: "name" +} diff --git a/src/qml/DefaultApp/Main.qml b/src/qml/DefaultApp/Main.qml index ed04b11..2565d16 100644 --- a/src/qml/DefaultApp/Main.qml +++ b/src/qml/DefaultApp/Main.qml @@ -20,6 +20,7 @@ import QtQuick 2.12 import QtQuick.Controls 2.12 import QtQuick.Layouts 1.12 + import FishUI 1.0 as FishUI import Cutefish.Settings 1.0 import "../" @@ -27,8 +28,8 @@ import "../" ItemPage { headerTitle: qsTr("Default Applications") - About { - id: about + DefaultApplications { + id: defaultApps } Scrollable { @@ -46,34 +47,70 @@ ItemPage { Label { text: qsTr("Web Browser") + enabled: browserComboBox.count !== 0 } - ComboBox { + AppComboBox { + id: browserComboBox Layout.fillWidth: true + textRole: "name" + model: defaultApps.browserList + currentIndex: defaultApps.browserIndex + enabled: count !== 0 + onActivated: { + defaultApps.setDefaultBrowser(browserComboBox.currentIndex) + } } Label { text: qsTr("File Manager") + enabled: fileManagerComboBox.count !== 0 } - ComboBox { + AppComboBox { + id: fileManagerComboBox Layout.fillWidth: true + textRole: "name" + model: defaultApps.fileManagerList + currentIndex: defaultApps.fileManagerIndex + enabled: count !== 0 + onActivated: { + defaultApps.setDefaultFileManager(fileManagerComboBox.currentIndex) + } } Label { text: qsTr("Email Client") + enabled: emailComboBox.count !== 0 } - ComboBox { + AppComboBox { + id: emailComboBox Layout.fillWidth: true + textRole: "name" + model: defaultApps.emailList + currentIndex: defaultApps.emailIndex + enabled: count !== 0 + onActivated: { + defaultApps.setDefaultEMail(emailComboBox.currentIndex) + } } Label { text: qsTr("Terminal") + enabled: terminalComboBox.count !== 0 } - ComboBox { + AppComboBox { + id: terminalComboBox Layout.fillWidth: true + textRole: "name" + model: defaultApps.terminalList + currentIndex: defaultApps.terminalIndex + enabled: count !== 0 + onActivated: { + defaultApps.setDefaultTerminal(terminalComboBox.currentIndex) + } } } } diff --git a/src/qml/SideBar.qml b/src/qml/SideBar.qml index 41f43e9..9020d55 100644 --- a/src/qml/SideBar.qml +++ b/src/qml/SideBar.qml @@ -213,14 +213,14 @@ Item { category: qsTr("System") } -// ListElement { -// title: qsTr("Default Applications") -// name: "datetime" -// page: "qrc:/qml/DefaultApp/Main.qml" -// iconSource: "defaultapps.svg" -// iconColor: "#418CFF" -// category: qsTr("System") -// } + ListElement { + title: qsTr("Default Applications") + name: "datetime" + page: "qrc:/qml/DefaultApp/Main.qml" + iconSource: "defaultapps.svg" + iconColor: "#418CFF" + category: qsTr("System") + } ListElement { title: qsTr("Language") diff --git a/src/qml/Sound/Main.qml b/src/qml/Sound/Main.qml index 8fb90d1..f1e4dc9 100644 --- a/src/qml/Sound/Main.qml +++ b/src/qml/Sound/Main.qml @@ -145,7 +145,7 @@ ItemPage { Layout.fillWidth: true value: Volume from: PulseAudio.MinimalVolume - to: PulseAudio.MaximalVolume + to: PulseAudio.NormalVolume enabled: VolumeWritable visible: HasVolume @@ -227,7 +227,7 @@ ItemPage { Layout.fillWidth: true value: Volume from: PulseAudio.MinimalVolume - to: PulseAudio.MaximalVolume + to: PulseAudio.NormalVolume enabled: VolumeWritable visible: HasVolume diff --git a/src/resources.qrc b/src/resources.qrc index e02233f..0f66768 100644 --- a/src/resources.qrc +++ b/src/resources.qrc @@ -145,5 +145,6 @@ images/sidebar/dark/notifications.svg qml/WLAN/ConnectDialog.qml qml/WLAN/NewNetworkDialog.qml + qml/DefaultApp/AppComboBox.qml diff --git a/translations/en_US.ts b/translations/en_US.ts index 1d38204..3772b64 100644 --- a/translations/en_US.ts +++ b/translations/en_US.ts @@ -253,37 +253,37 @@ - + Poor - + Normal - + Excellent - + Last Charged to - + Maximum Capacity - + Show percentage in status bar - + No battery found @@ -800,27 +800,27 @@ - + Default Applications - + Web Browser - + File Manager - + Email Client - + Terminal @@ -1007,6 +1007,7 @@ + @@ -1034,6 +1035,11 @@ Date & Time + + + Default Applications + + Language diff --git a/translations/zh_CN.ts b/translations/zh_CN.ts index 6796a16..df68d84 100644 --- a/translations/zh_CN.ts +++ b/translations/zh_CN.ts @@ -253,37 +253,37 @@ 健康 - + Poor 较差 - + Normal 正常 - + Excellent 极佳 - + Last Charged to 上一次充电至 - + Maximum Capacity 最大容量 - + Show percentage in status bar 在状态栏中显示电池百分比 - + No battery found 找不到电池 @@ -800,27 +800,27 @@ 输入设备 - + Default Applications 默认应用 - + Web Browser 网络浏览器 - + File Manager 文件管理器 - + Email Client 邮件客户端 - + Terminal 终端 @@ -1007,6 +1007,7 @@ + @@ -1034,6 +1035,11 @@ Date & Time 日期和时间 + + + Default Applications + 默认应用 + Language