From a515b63ee5583c062b74dbe4ad4c4ad802ef710c Mon Sep 17 00:00:00 2001 From: niko_yhc Date: Mon, 8 Jan 2024 15:16:06 +0800 Subject: [PATCH] feature(language-settings):add language-settings into kiran-control-panel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 控制中心新增语言设置 Closes#23737 --- data/category/desktop/language.desktop | 30 ++ data/category/images/language.png | Bin 0 -> 3295 bytes .../kiran-setting-item.cpp | 17 +- .../kiran-setting-item.h | 4 + plugins/CMakeLists.txt | 1 + plugins/language/CMakeLists.txt | 70 ++++ plugins/language/config/config.h.in | 6 + ...com.kylinsec.kiran.language.gschema.xml.in | 10 + plugins/language/data/support_language.json | 46 +++ plugins/language/language-plugin.cpp | 82 ++++ plugins/language/language-plugin.h | 51 +++ plugins/language/language-subitem.cpp | 99 +++++ plugins/language/language-subitem.h | 83 ++++ plugins/language/src/langpack-installer.cpp | 116 ++++++ plugins/language/src/langpack-installer.h | 48 +++ plugins/language/src/langpack-thread.cpp | 58 +++ plugins/language/src/langpack-thread.h | 41 ++ plugins/language/src/language-manager.cpp | 364 ++++++++++++++++++ plugins/language/src/language-manager.h | 99 +++++ plugins/language/src/language-page.cpp | 199 ++++++++++ plugins/language/src/language-page.h | 56 +++ .../language/src/language-select-dialog.cpp | 141 +++++++ plugins/language/src/language-select-dialog.h | 51 +++ .../language/src/language-select-dialog.ui | 174 +++++++++ resources/control-panel-resources.qrc | 3 + resources/images/language.svg | 39 ++ translations/kiran-control-panel.zh_CN.ts | 95 +++++ 27 files changed, 1982 insertions(+), 1 deletion(-) create mode 100644 data/category/desktop/language.desktop create mode 100644 data/category/images/language.png create mode 100644 plugins/language/CMakeLists.txt create mode 100644 plugins/language/config/config.h.in create mode 100644 plugins/language/data/com.kylinsec.kiran.language.gschema.xml.in create mode 100644 plugins/language/data/support_language.json create mode 100644 plugins/language/language-plugin.cpp create mode 100644 plugins/language/language-plugin.h create mode 100644 plugins/language/language-subitem.cpp create mode 100644 plugins/language/language-subitem.h create mode 100644 plugins/language/src/langpack-installer.cpp create mode 100644 plugins/language/src/langpack-installer.h create mode 100644 plugins/language/src/langpack-thread.cpp create mode 100644 plugins/language/src/langpack-thread.h create mode 100644 plugins/language/src/language-manager.cpp create mode 100644 plugins/language/src/language-manager.h create mode 100644 plugins/language/src/language-page.cpp create mode 100644 plugins/language/src/language-page.h create mode 100644 plugins/language/src/language-select-dialog.cpp create mode 100644 plugins/language/src/language-select-dialog.h create mode 100644 plugins/language/src/language-select-dialog.ui create mode 100644 resources/images/language.svg diff --git a/data/category/desktop/language.desktop b/data/category/desktop/language.desktop new file mode 100644 index 00000000..75b6e223 --- /dev/null +++ b/data/category/desktop/language.desktop @@ -0,0 +1,30 @@ +[Kiran Control Panel Category] +Category=language + +Name=language +Name[zh_CN]=语言 +Name[bo_CN]=སྐད་སྒྲ། +Name[kk_KG]=үн +Name[kk_KZ]=дыбыс +Name[mn_MN]=дуу чимээ +Name[ug_CN]=ئاۋاز + +Comment=language settings +Comment[zh_CN]=语言设置 +Comment[bo_CN]=སྐད་སྒྲ་སྒྲིག་པ། +Comment[kk_KG]=Үн параметрлери +Comment[kk_KZ]=Дыбыс параметрлері +Comment[mn_MN]=Дуу чимээний тохиргоо +Comment[ug_CN]=ئاۋاز تەسىس قىلىش + +Icon=language.png + +Weight=7 + +Keywords[zh_CN]=语言 +Keywords[bo_CN]=སྐད་སྒྲ། +Keywords[kk_KG]=үн +Keywords[kk_KZ]=дыбыс +Keywords[mn_MN]=дуу чимээ +Keywords[ug_CN]=ئاۋاز +Keywords=language diff --git a/data/category/images/language.png b/data/category/images/language.png new file mode 100644 index 0000000000000000000000000000000000000000..2d0befefed885ef549c7bff1f3ee4693a2f23d91 GIT binary patch literal 3295 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 + m_midIcon = icon; + m_midIconChanged = midIconChanged; + updateIcon(); +} + void KiranSettingItem::setSwitcherVisible(bool visible) { m_switcher->setVisible(visible); @@ -114,6 +122,12 @@ void KiranSettingItem::initUI() connect(m_switcher, &KiranSwitchButton::toggled, this, [this](bool checked) { emit switchButtonToggled(m_userData, checked); }); + m_midButton = new QPushButton(this); + m_midButton->setFixedSize(16, 16); + m_midButton->setFlat(true); + m_midButton->setVisible(false); + layout->addWidget(m_midButton); + m_rightButton = new QPushButton(this); m_rightButton->setFixedSize(16, 16); m_rightButton->setFlat(true); @@ -137,7 +151,8 @@ void KiranSettingItem::updateIcon() }; QVector buttonIconMap = { {m_leftButton, m_leftIcon, m_leftIconChanged}, - {m_rightButton, m_rightIcon, m_rightIconChanged}}; + {m_rightButton, m_rightIcon, m_rightIconChanged}, + {m_midButton, m_midIcon, m_midIconChanged}}; for (auto& iter : buttonIconMap) { diff --git a/lib/common-widgets/kiran-setting-container/kiran-setting-item.h b/lib/common-widgets/kiran-setting-container/kiran-setting-item.h index 48da4cae..f2dd9acd 100644 --- a/lib/common-widgets/kiran-setting-container/kiran-setting-item.h +++ b/lib/common-widgets/kiran-setting-container/kiran-setting-item.h @@ -36,6 +36,7 @@ public: void setLeftButtonVisible(bool visible, const QIcon& icon, bool leftIconChanged = true); void setRightButtonVisible(bool visible, const QIcon& icon, bool rightIconChanged = true); + void setMidButtonVisible(bool visible, const QIcon& icon, bool midIconChanged = true); void setSwitcherVisible(bool visible); void setSwitcherChecked(bool checked); @@ -60,15 +61,18 @@ private: QIcon m_leftIcon; QIcon m_rightIcon; + QIcon m_midIcon; QPushButton* m_leftButton; KiranLabel* m_label; KiranSwitchButton* m_switcher; QPushButton* m_rightButton; + QPushButton* m_midButton; bool m_clickable = false; bool m_clicked = false; bool m_leftIconChanged; bool m_rightIconChanged; + bool m_midIconChanged; }; diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index ecad7b62..edff4e78 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -11,6 +11,7 @@ add_subdirectory(authentication) if (ENABLE_APPLICATION) add_subdirectory(application) endif() +add_subdirectory(language) if(ENABLE_NETWORK) add_subdirectory(network) endif() diff --git a/plugins/language/CMakeLists.txt b/plugins/language/CMakeLists.txt new file mode 100644 index 00000000..79e787ad --- /dev/null +++ b/plugins/language/CMakeLists.txt @@ -0,0 +1,70 @@ +set(TARGET_NAME kiran-cpanel-language) + +find_package(PkgConfig REQUIRED) +find_package(Qt5 COMPONENTS Widgets Svg DBus LinguistTools Network) +pkg_search_module(KIRAN_WIDGETS_QT5 REQUIRED kiranwidgets-qt5) +pkg_search_module(KLOG_QT5 REQUIRED klog-qt5) +pkg_search_module(KIRAN_CC_DAEMON REQUIRED kiran-cc-daemon) +pkg_search_module(KIRAN_CONTROL_PANEL REQUIRED kiran-control-panel) +pkg_search_module(KIRAN_STYLE_HELPER REQUIRED kiran-style-helper) +pkg_search_module(QGSETTINGS REQUIRED gsettings-qt) +pkg_search_module(LIBNOTIFY REQUIRED libnotify) +pkg_search_module(PACKAGEKIT_QT5 REQUIRED packagekitqt5) + +set(LANGUAGE_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/lib/common-widgets/kiran-setting-container ${PROJECT_SOURCE_DIR}/src/search-edit) + +file(GLOB_RECURSE LANGUAGE_PLUGIN_SRC + ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/*.h + ${CMAKE_CURRENT_SOURCE_DIR}/*.ui) + +add_library(${TARGET_NAME} SHARED + ${LANGUAGE_PLUGIN_SRC}) + +target_include_directories(${TARGET_NAME} PRIVATE + ${CMAKE_CURRENT_BINARY_DIR} + ${KCP_PLUGIN_INCLUDE_DIR} + ${LANGUAGE_INCLUDE_DIRS} + ${KIRAN_WIDGETS_QT5_INCLUDE_DIRS} + ${KLOG_QT5_INCLUDE_DIRS} + ${KIRAN_CC_DAEMON_INCLUDE_DIRS} + ${KIRAN_STYLE_HELPER_INCLUDE_DIRS} + ${QGSETTINGS_INCLUDE_DIRS} + ${LIBNOTIFY_INCLUDE_DIRS} + ${PACKAGEKIT_QT5_INCLUDE_DIRS}) + +target_link_libraries(${TARGET_NAME} + common-widgets + Qt5::Widgets + Qt5::DBus + Qt5::Svg + Qt5::Core + ${KIRAN_WIDGETS_QT5_LIBRARIES} + ${KLOG_QT5_LIBRARIES} + ${KIRAN_STYLE_HELPER_LIBRARIES} + ${QGSETTINGS_LIBRARIES} + ${LIBNOTIFY_LIBRARIES} + ${PACKAGEKIT_QT5_LIBRARIES}) + +install(TARGETS ${TARGET_NAME} + DESTINATION ${PLUGIN_LIBS_DIR}/) + +install(FILES ${LANGUAGE_QM} DESTINATION ${TRANSLATION_DIR}/) + +set(SUPPORT_LANGUAGE_JSON support_language.json) +set(KIRAN_SUPPORT_LANGUAGE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/data) +set(KIRAN_SYSTEM_SUPPORT_LANGUAGE_DIR ${PLUGIN_DIR}/data) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h) +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/data/${SUPPORT_LANGUAGE_JSON} + DESTINATION ${PLUGIN_DIR}/data/) + +set(SCHEMA_IN_FILE com.kylinsec.kiran.language.gschema.xml.in) +set(SCHEMA_FILE com.kylinsec.kiran.language.gschema.xml) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/data/${SCHEMA_IN_FILE} ${CMAKE_CURRENT_BINARY_DIR}/${SCHEMA_FILE}) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${SCHEMA_FILE} + DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/glib-2.0/schemas/) +install( + CODE "execute_process(COMMAND glib-compile-schemas \"${CMAKE_INSTALL_FULL_DATADIR}/glib-2.0/schemas/\")" + ) + + diff --git a/plugins/language/config/config.h.in b/plugins/language/config/config.h.in new file mode 100644 index 00000000..9400f286 --- /dev/null +++ b/plugins/language/config/config.h.in @@ -0,0 +1,6 @@ +#ifndef __LANGUAGE_CONFIG_H__ +#define __LANGUAGE_CONFIG_H__ + +#define KIRAN_SUPPORT_LANGUAGE_DIR "@KIRAN_SUPPORT_LANGUAGE_DIR@/" +#define KIRAN_SYSTEM_SUPPORT_LANGUAGE_DIR "@KIRAN_SYSTEM_SUPPORT_LANGUAGE_DIR@/" +#endif diff --git a/plugins/language/data/com.kylinsec.kiran.language.gschema.xml.in b/plugins/language/data/com.kylinsec.kiran.language.gschema.xml.in new file mode 100644 index 00000000..14904b81 --- /dev/null +++ b/plugins/language/data/com.kylinsec.kiran.language.gschema.xml.in @@ -0,0 +1,10 @@ + + + + + ['en_US.UTF-8','zh_CN.UTF-8'] + Show Language list. + + + + diff --git a/plugins/language/data/support_language.json b/plugins/language/data/support_language.json new file mode 100644 index 00000000..65c95bd4 --- /dev/null +++ b/plugins/language/data/support_language.json @@ -0,0 +1,46 @@ +{ + "SupportLanguage":[ + { + "Locale": "zh_CN.UTF-8", + "Description": "简体中文", + "LangCode": "zh_CN", + "LanguageName": "Simplified Chinese" + }, + { + "Locale": "en_US.UTF-8", + "Description": "English", + "LangCode": "en", + "LanguageName": "English" + }, + { + "Locale": "bo_CN", + "Description": "বাংলা", + "LangCode": "bo", + "LanguageName": "Tibetan" + }, + { + "Locale": "ky_KG", + "Description": "Кыргызча", + "LangCode": "ky", + "LanguageName": "Kirgiz" + }, + { + "Locale": "mn_MN", + "Description": "Монгол", + "LangCode": "mn", + "LanguageName": "Mongolian" + }, + { + "Locale": "kk_KZ", + "Description": "Қазақша", + "LangCode": "kk", + "LanguageName": "Kazakh" + }, + { + "Locale": "ug_CN", + "Description": "Uyghurche", + "LangCode": "ug", + "LanguageName": "Uighur" + } + ] +} \ No newline at end of file diff --git a/plugins/language/language-plugin.cpp b/plugins/language/language-plugin.cpp new file mode 100644 index 00000000..6bf6ec6e --- /dev/null +++ b/plugins/language/language-plugin.cpp @@ -0,0 +1,82 @@ +/** + * Copyright (c) 2022 ~ 2023 KylinSec Co., Ltd. + * kiran-control-panel is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: yinhongchang + */ + +#include "language-plugin.h" +#include "language-subitem.h" +#include "language-page.h" + +LanguagePlugin::LanguagePlugin(QObject* parent) : QObject(parent) +{ +} + +LanguagePlugin::~LanguagePlugin() +{ +} + +int LanguagePlugin::init(KiranControlPanel::PanelInterface* interface) +{ + initSubItem(); + return 0; +} + +void LanguagePlugin::uninit() +{ +} + +QVector LanguagePlugin::getSubItems() +{ + return m_subitems; +} + +void LanguagePlugin::initSubItem() +{ + auto languageSubItemCreator = []() -> QWidget* + { + return new LanguagePage(); + }; + + struct SubItemStruct + { + QString id; + QString name; + QString category; + QString desc; + QString icon; + int weight; + CreateWidgetFunc func; + }; + + QList subitemInfos = { + {"Language", + tr("Language"), + "language", + "", + ":/kcp-language/images/language.svg", + 96, + languageSubItemCreator} + }; + + for (const SubItemStruct& subitemInfo : subitemInfos) + { + LanguageSubItem* subitem = new LanguageSubItem(subitemInfo.func); + + subitem->setID(subitemInfo.id); + subitem->setName(subitemInfo.name); + subitem->setCategory(subitemInfo.category); + subitem->setDesc(subitemInfo.desc); + subitem->setIcon(subitemInfo.icon); + subitem->setWeight(subitemInfo.weight); + m_subitems.append(KiranControlPanel::SubItemPtr(subitem)); + } +} \ No newline at end of file diff --git a/plugins/language/language-plugin.h b/plugins/language/language-plugin.h new file mode 100644 index 00000000..ac73cc97 --- /dev/null +++ b/plugins/language/language-plugin.h @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2022 ~ 2023 KylinSec Co., Ltd. + * kiran-control-panel is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: yinhongchang + */ + +#ifndef __LANGUAGE_PLUGIN_H__ +#define __LANGUAGE_PLUGIN_H__ +#include +#include + +#include "panel-interface.h" +#include "plugin-interface-v2.h" +#include "plugin-subitem-interface.h" + +class LanguagePlugin : public QObject, + public KiranControlPanel::PluginInterfaceV2 +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID KiranControlPanel_PluginInterfaceV2_iid) + Q_INTERFACES(KiranControlPanel::PluginInterfaceV2) + +public: + LanguagePlugin(QObject* parent = nullptr); + virtual ~LanguagePlugin(); + + int init(KiranControlPanel::PanelInterface* interface) override; + void uninit() override; + + QVector getSubItems() override; + +private: + void initSubItem(); + +private: + KiranControlPanel::PanelInterface* m_panelInterface = nullptr; + QVector m_subitems; + + +}; + + +#endif \ No newline at end of file diff --git a/plugins/language/language-subitem.cpp b/plugins/language/language-subitem.cpp new file mode 100644 index 00000000..9ddca3e4 --- /dev/null +++ b/plugins/language/language-subitem.cpp @@ -0,0 +1,99 @@ +/** + * Copyright (c) 2022 ~ 2023 KylinSec Co., Ltd. + * kiran-control-panel is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: yinhongchang + */ + + + +#include "language-subitem.h" +#include + +LanguageSubItem::LanguageSubItem(CreateWidgetFunc func, QObject* parent) + : m_func(func), QObject(parent) +{ +} + +LanguageSubItem::~LanguageSubItem() +{ +} + +// 功能项ID,用于区分功能项,应确保其唯一 +void LanguageSubItem::setID(const QString& subitemID) +{ + m_id = subitemID; +} + +QString LanguageSubItem::getID() +{ + return m_id; +} + +// 功能项名称,用于显示在启动器标题栏之中 +void LanguageSubItem::setName(const QString& name) +{ + m_name = name; +} + +QString LanguageSubItem::getName() +{ + return m_name; +} + +// 获取功能项分类ID,该功能项属于哪个分类 +void LanguageSubItem::setCategory(const QString& category) +{ + m_category = category; +} + +QString LanguageSubItem::getCategory() +{ + return m_category; +} + +// 获取功能项目状态描述,显示在功能项侧边栏右边状态文本描述 +void LanguageSubItem::setDesc(const QString& desc) +{ + m_desc = desc; +} + +QString LanguageSubItem::getDesc() +{ + return m_desc; +} + +// 获取功能项图标显示,用于形成功能项侧边栏的左侧图标 +void LanguageSubItem::setIcon(const QString& icon) +{ + m_icon = icon; +} + +QString LanguageSubItem::getIcon() +{ + return m_icon; +} + +// 获取功能项权重,用于多个功能项排序 +void LanguageSubItem::setWeight(int weight) +{ + m_weight = weight; +} + +int LanguageSubItem::getWeight() +{ + return m_weight; +} + +// 创建显示控件 +QWidget* LanguageSubItem::createWidget() +{ + return m_func(); +} \ No newline at end of file diff --git a/plugins/language/language-subitem.h b/plugins/language/language-subitem.h new file mode 100644 index 00000000..9c325eca --- /dev/null +++ b/plugins/language/language-subitem.h @@ -0,0 +1,83 @@ +/** + * Copyright (c) 2022 ~ 2023 KylinSec Co., Ltd. + * kiran-control-panel is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: yinhongchang + */ + + +#ifndef __LANGUAGE_SUBITEM_H__ +#define __LANGUAGE_SUBITEM_H__ + +#include +#include +#include "panel-interface.h" +#include "plugin-subitem-interface.h" + +typedef QWidget* (*CreateWidgetFunc)(); + +class LanguageSubItem : public QObject, + public KiranControlPanel::PluginSubitemInterface +{ + Q_OBJECT + +public: + LanguageSubItem(CreateWidgetFunc func, QObject* parent = nullptr); + ~LanguageSubItem(); +public: + // 功能项ID,用于区分功能项,应确保其唯一 + void setID(const QString& subitemID); + QString getID() override; + + // 功能项名称,用于显示在启动器标题栏之中 + void setName(const QString& name); + QString getName() override; + + // 获取功能项分类ID,该功能项属于哪个分类 + void setCategory(const QString& category); + QString getCategory() override; + + // 获取功能项目状态描述,显示在功能项侧边栏右边状态文本描述 + void setDesc(const QString& desc); + QString getDesc() override; + + // 获取功能项图标显示,用于形成功能项侧边栏的左侧图标 + void setIcon(const QString& icon); + QString getIcon() override; + + // 获取功能项权重,用于多个功能项排序 + void setWeight(int weight); + int getWeight() override; + + // 创建显示控件 + QWidget* createWidget() override; + + // 获取自定义搜索关键词 + // QVector< 显示文本(已翻译),搜索跳转标识ID > + QVector> getSearchKeys() override { return {}; }; + + // 跳转至自定义搜索项 + bool jumpToSearchEntry(const QString& key) override { return false; }; + + // 该功能项是否存在未保存配置 + bool haveUnSavedOptions() override { return false; }; + +private: + QString m_id; + QString m_name; + QString m_category; + QString m_desc; + QString m_icon; + int m_weight; + CreateWidgetFunc m_func; + +}; + +#endif \ No newline at end of file diff --git a/plugins/language/src/langpack-installer.cpp b/plugins/language/src/langpack-installer.cpp new file mode 100644 index 00000000..55da12f0 --- /dev/null +++ b/plugins/language/src/langpack-installer.cpp @@ -0,0 +1,116 @@ +/** + * Copyright (c) 2022 ~ 2023 KylinSec Co., Ltd. + * kiran-control-panel is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: yinhongchang + */ + +#include +#include +#include +#include +#include +#include "langpack-installer.h" + +typedef enum +{ + PACKAGE_UNKNOW, + PACKAGE_INSTALLED, + PACKAGE_AVAILABLE +} PackageState; + +Daemon* LangpackInstaller::m_daemon = nullptr; +LangpackInstaller::LangpackInstaller(QWidget* parent) : QWidget(parent), m_langpackName(QString()), m_langpackState(PACKAGE_UNKNOW) +{ + m_daemon = Daemon::global(); +} + +LangpackInstaller::~LangpackInstaller() +{ + +} + +void LangpackInstaller::searchLangpack(const QString& langCode) +{ + QString searchLangpack = "langpacks-" + langCode; + Transaction *transaction = m_daemon->searchNames(searchLangpack, Transaction::FilterNone); + QEventLoop loop; + connect(transaction, SIGNAL(finished(PackageKit::Transaction::Exit, uint)), &loop, SLOT(quit())); + connect(transaction, &Transaction::errorCode, + [this](uint errorcode, QString details) { + KLOG_WARNING() << "errorcode:" << errorcode << ", details:" << details; + return; + }); + connect(transaction, &Transaction::package, + [this](uint info, const QString &pid, const QString &summary) { + const QByteArray &pkgInfo = pid.toLatin1(); + QString infoStr = QString("%1").arg(info); + KLOG_INFO() << "pkgInfo:" << pkgInfo << ", infoStr:" << infoStr; + m_langpackName = pkgInfo; + m_langpackState = info; + }); + loop.exec(); +} + +bool LangpackInstaller::checkLangpackInstalled(const QString& langCode) +{ + bool installed = false; + searchLangpack(langCode); + if (m_langpackState == PACKAGE_INSTALLED) + { + installed = true; + } + + m_langpackName.clear(); + m_langpackState = PACKAGE_UNKNOW; + + return installed; +} + + +void LangpackInstaller::showProcessDialog(uint percentage) +{ + if (percentage == 100) + { + m_processDialog->setLabelText(tr("Install Complete!")); + } + m_processDialog->setMinimumDuration(0); + m_processDialog->setValue(percentage); +} + +void LangpackInstaller::installLangpack(const QString& langCode) +{ + searchLangpack(langCode); + if (m_langpackState != PACKAGE_AVAILABLE) + { + KLOG_DEBUG() << langCode << " state is " << m_langpackState; + return; + } + + m_processDialog = new QProgressDialog(this->parentWidget()); + // dialog出现需等待的时间 + m_processDialog->setMinimumDuration(1000000000); + m_processDialog->setValue(0); + m_processDialog->setWindowTitle(tr("Package Install")); + // 当进度条显示到 100%时 不隐藏 + m_processDialog->setAutoClose(0); + // 当进度条显示到 100%时 不调用reset()函数重置进度条 + m_processDialog->setAutoReset(0); + // 设置进度对话框标签文本,向用户提示该进度条正在做什么 + m_processDialog->setLabelText(tr("Installing...")); + // 重新设置 Cancel按钮 的文本 + m_processDialog->setCancelButtonText(QString()); + m_processDialog->setRange(0, 100); + + Transaction *transaction = m_daemon->installPackage(m_langpackName, Transaction::TransactionFlagNone); + m_langpackThread = new LangpackThread(this, transaction); + connect(m_langpackThread, &LangpackThread::showProcessDialogSignal, this, &LangpackInstaller::showProcessDialog); + m_langpackThread->start(); +} \ No newline at end of file diff --git a/plugins/language/src/langpack-installer.h b/plugins/language/src/langpack-installer.h new file mode 100644 index 00000000..2c872d1a --- /dev/null +++ b/plugins/language/src/langpack-installer.h @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2022 ~ 2023 KylinSec Co., Ltd. + * kiran-control-panel is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: yinhongchang + */ + + +#ifndef __LANGPACK_INSTALLER_H__ +#define __LANGPACK_INSTALLER_H__ + +#include +#include +#include "langpack-thread.h" + +class QProgressDialog; +using namespace PackageKit; +class LangpackInstaller : public QWidget +{ + Q_OBJECT + +public: + explicit LangpackInstaller(QWidget* parent = nullptr); + ~LangpackInstaller(); + + void searchLangpack(const QString& langCode); + void installLangpack(const QString& langCode); + bool checkLangpackInstalled(const QString& langCode); + +private slots: + void showProcessDialog(uint percentage); + +private: + QProgressDialog* m_processDialog = nullptr; + static Daemon* m_daemon; + QString m_langpackName; + uint m_langpackState; + LangpackThread* m_langpackThread = nullptr; +}; + +#endif \ No newline at end of file diff --git a/plugins/language/src/langpack-thread.cpp b/plugins/language/src/langpack-thread.cpp new file mode 100644 index 00000000..e71499a6 --- /dev/null +++ b/plugins/language/src/langpack-thread.cpp @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2022 ~ 2023 KylinSec Co., Ltd. + * kiran-control-panel is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: yinhongchang + */ + +#include "langpack-thread.h" +#include + +typedef enum +{ + PACKAGE_UNKNOW, + PACKAGE_INSTALLED, + PACKAGE_AVAILABLE +} PackageState; + +LangpackThread::LangpackThread(QObject* parent, Transaction* transaction) : QThread(parent), m_transaction(transaction) +{ + +} + +LangpackThread::~LangpackThread() +{ +} + + +void LangpackThread::run() +{ + connect(m_transaction, &Transaction::errorCode, + [this](uint errorcode, QString details) { + KLOG_WARNING() << "errorcode:" << errorcode << ", details:" << details; + return; + }); + connect(m_transaction, &Transaction::package, + [this](uint info, const QString &pid, const QString &summary) { + uint percentage = m_transaction->percentage(); + KLOG_INFO() << "info:" << info << ", pid:" << pid << ", summary:" << summary << ", percentage:" << percentage; + emit showProcessDialogSignal(percentage); + }); + connect(m_transaction, &Transaction::finished, + [this](uint status, uint runtime) { + if (status == PACKAGE_INSTALLED) + { + uint percentage = m_transaction->percentage(); + KLOG_INFO() << "percentage:" << percentage; + emit showProcessDialogSignal(percentage); + this->quit(); + } + }); +} diff --git a/plugins/language/src/langpack-thread.h b/plugins/language/src/langpack-thread.h new file mode 100644 index 00000000..552edf61 --- /dev/null +++ b/plugins/language/src/langpack-thread.h @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2022 ~ 2023 KylinSec Co., Ltd. + * kiran-control-panel is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: yinhongchang + */ + + +#ifndef __LANGPACK_THREAD_H__ +#define __LANGPACK_THREAD_H__ + +#include +#include +#include + +using namespace PackageKit; +class LangpackThread : public QThread +{ + Q_OBJECT + +public: + explicit LangpackThread(QObject* parent = nullptr, Transaction* transaction = nullptr); + ~LangpackThread(); + void run(); + +signals: + void showProcessDialogSignal(uint percentage); + +private: + QString m_langCode; + Transaction* m_transaction = nullptr; +}; + +#endif \ No newline at end of file diff --git a/plugins/language/src/language-manager.cpp b/plugins/language/src/language-manager.cpp new file mode 100644 index 00000000..349eb624 --- /dev/null +++ b/plugins/language/src/language-manager.cpp @@ -0,0 +1,364 @@ +/** + * Copyright (c) 2022 ~ 2023 KylinSec Co., Ltd. + * kiran-control-panel is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: yinhongchang + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "config.h" +#include "language-manager.h" +#include "langpack-installer.h" + +#define KIRAN_LANGUAGE_SCHEMA_ID "com.kylinsec.kiran.language" +#define KEY_LANGUAGE_LIST "language-list" +#define LOCALE_CONF "/etc/locale.conf" +#define SUPPORT_LANGUAGE_FILE "support_language.json" + + +LanguageManager::LanguageManager(QWidget* parent) : QWidget(parent), m_languageDialog(new LanguageSelectDialog(this)), m_langpackInstaller(new LangpackInstaller(this)) +{ + m_languageDialog->setHidden(true); + initLanguageDBus(); + initGSettings(); + languageList(); + currentLanguage(); + showLangugaeList(); + initConnect(); +} + +LanguageManager::~LanguageManager() +{ + if (m_languageSettings != nullptr) + { + delete m_languageSettings; + m_languageSettings = nullptr; + } + + if (m_languageDBus != nullptr) + { + delete m_languageDBus; + m_languageDBus = nullptr; + } +} + +void LanguageManager::initLanguageDBus() +{ + if (!m_languageDBus) + { + QString objpath = QString("/org/freedesktop/locale1"); + m_languageDBus = new QDBusInterface("org.freedesktop.locale1", + objpath, + "org.freedesktop.locale1", + QDBusConnection::systemBus()); + } +} + +void LanguageManager::initGSettings() +{ + if (QGSettings::isSchemaInstalled(KIRAN_LANGUAGE_SCHEMA_ID)) + { + m_languageSettings = new QGSettings(KIRAN_LANGUAGE_SCHEMA_ID, QByteArray(), this); + } + + if (m_languageSettings) + { + connect(m_languageSettings, &QGSettings::changed, this, [=](const QString& key) { + + QStringList languageList = m_languageSettings->get(KEY_LANGUAGE_LIST).toStringList(); + if (!languageList.contains(m_currentLanguage)) + { + languageList.append(m_currentLanguage); + } + + QList showLanguageList; + for (const auto& locale : languageList) + { + for (const auto& langInfo : m_languageList) + { + if (langInfo.locale == locale) + { + showLanguageList.append(langInfo); + break; + } + } + } + + m_showLanguageList = showLanguageList; + emit showLanguageChanged(); + }); + } +} + + +void LanguageManager::initConnect() +{ + connect(m_languageDialog, &LanguageSelectDialog::languageAddBtnClicked, this, &LanguageManager::addShowLanguage); +} + +void LanguageManager::currentLanguage() +{ + QString defaultLanguage = "en_US.UTF-8"; + if (!m_languageDBus) + { + m_currentLanguage = defaultLanguage; + return; + } + + QStringList currentLanguageList = m_languageDBus->property("Locale").toStringList(); + m_currentLanguage = currentLanguageList[0]; + if (m_currentLanguage.isEmpty() || !m_currentLanguage.contains("LANG=")) + { + m_currentLanguage = defaultLanguage; + return; + } + m_currentLanguage = m_currentLanguage.mid(m_currentLanguage.indexOf("=") + 1); +} + +QList LanguageManager::parseSupportLang(const QString& langs) +{ + QList langInfoList; + QJsonParseError jsonError; + QJsonDocument jsonDocument = QJsonDocument::fromJson(langs.toUtf8().data(), &jsonError); + if (jsonDocument.isNull() || jsonError.error != QJsonParseError::NoError) + { + KLOG_ERROR() << " please check the string " << langs.toUtf8().data(); + return langInfoList; + } + + if (jsonDocument.isObject()) + { + QJsonObject obj = jsonDocument.object(); + if (obj.contains("SupportLanguage")) + { + QJsonValue langValue = obj.value("SupportLanguage"); + if (langValue.isArray()) + { + QJsonArray array = langValue.toArray(); + for (int i = 0; i < array.size(); i++) + { + QJsonValue value = array.at(i); + if (value.type() == QJsonValue::Object) + { + LangInfo info; + QJsonObject langObj = value.toObject(); + if (langObj.contains("Locale")) + { + info.locale = langObj.value("Locale").toString(); + } + if (langObj.contains("Description")) + { + info.desc = langObj.value("Description").toString(); + } + if (langObj.contains("LangCode")) + { + info.langCode = langObj.value("LangCode").toString(); + } + if (langObj.contains("LanguageName")) + { + info.languageName = langObj.value("LanguageName").toString(); + } + langInfoList.append(info); + } + } + } + } + } + return langInfoList; +} + + +void LanguageManager::languageList() +{ + QList langInfoList; + QString supportLanguageFile = QString("%1%2").arg(KIRAN_SUPPORT_LANGUAGE_DIR).arg(SUPPORT_LANGUAGE_FILE); + QString systemLanguageFile = QString("%1%2").arg(KIRAN_SYSTEM_SUPPORT_LANGUAGE_DIR).arg(supportLanguageFile); + + if (!QFileInfo::exists(supportLanguageFile) && !QFileInfo::exists(systemLanguageFile)) + { + KLOG_WARNING() << SUPPORT_LANGUAGE_FILE << "isn't exists!"; + return; + } + + QString languageFile = QFileInfo::exists(supportLanguageFile) ? supportLanguageFile : systemLanguageFile; + QFile file(languageFile); + if (!file.open(QIODevice::ReadOnly)) + { + KLOG_DEBUG() << SUPPORT_LANGUAGE_FILE << " can't open."; + return; + } + + QTextStream out(&file); + langInfoList = parseSupportLang(out.readAll()); + m_languageList = langInfoList; +} + +void LanguageManager::showLangugaeList() +{ + QList showLanguageList; + if (m_languageSettings) + { + QStringList languageList = m_languageSettings->get(KEY_LANGUAGE_LIST).toStringList(); + if (!languageList.contains(m_currentLanguage)) + { + languageList.append(m_currentLanguage); + QSignalBlocker blocker(m_languageSettings); + m_languageSettings->set(KEY_LANGUAGE_LIST, languageList); + } + + for (const auto& locale : languageList) + { + bool flag = false; + for (const auto& langInfo : m_languageList) + { + if (langInfo.locale == locale) + { + flag = true; + showLanguageList.append(langInfo); + break; + } + } + + // 若gsettings中的locale在在support_language.json中不存在,则报错 + if (!flag) + { + KiranMessageBox::message(nullptr, + tr("Error"), + tr("%1 inexistence in system").arg(locale), + KiranMessageBox::Ok); + } + } + + m_showLanguageList = showLanguageList; + } +} + + +bool LanguageManager::isSupportedLanguage(const QString& locale) +{ + for (const auto& info : m_languageList) + { + if(info.locale == locale) + return true; + } + return false; +} + +void LanguageManager::addShowLanguage(QString locale) +{ + if (m_languageSettings) + { + bool flag = false; + for (const auto& info : m_showLanguageList) + { + if (info.locale == locale) + { + flag = true; + break; + } + } + + if (!flag) + { + for (const auto& info : m_languageList) + { + if (info.locale == locale) + { + m_showLanguageList.append(info); + break; + } + } + + QStringList languageList = m_languageSettings->get(KEY_LANGUAGE_LIST).toStringList(); + languageList.append(locale); + m_languageSettings->set(KEY_LANGUAGE_LIST, languageList); + } + } +} + +void LanguageManager::deleteShowLanguage(const QString& locale) +{ + if (m_languageSettings) + { + bool flag = false; + LangInfo deleteLangInfo; + for (const auto& info : m_showLanguageList) + { + if (info.locale == locale) + { + flag = true; + deleteLangInfo = info; + break; + } + } + + if (flag) + { + m_showLanguageList.removeAll(deleteLangInfo); + + QStringList languageList = m_languageSettings->get(KEY_LANGUAGE_LIST).toStringList(); + languageList.removeAll(locale); + QSignalBlocker blocker(m_languageSettings); + m_languageSettings->set(KEY_LANGUAGE_LIST, languageList); + } + } +} + +void LanguageManager::setCurrentLanguage(const QString& locale) +{ + // 在support_language.json中是否存在 + if (!isSupportedLanguage(locale)) + { + KiranMessageBox::message(nullptr, + tr("set locale failed"), + tr("%1 inexistence in system").arg(locale), + KiranMessageBox::Ok); + return; + } + + // 在gsettings文件中是否存在,不存在则添加 + addShowLanguage(locale); + + // 调用LangpackInstaller接口检查langCode是否存在,不存在则下载 + QString langCode; + for (const auto& info : m_showLanguageList) + { + if (info.locale == locale) + { + langCode = info.langCode; + } + } + + m_langpackInstaller = new LangpackInstaller; + if (!m_langpackInstaller->checkLangpackInstalled(langCode)) + { + m_langpackInstaller->installLangpack(langCode); + } + + // 不论下载langCode是否成功,都将locale设置为系统语言 + if (!m_languageDBus) + { + return; + } + QStringList localeList; + localeList << locale; + m_languageDBus->call("SetLocale", localeList, true); + m_currentLanguage = locale; +} \ No newline at end of file diff --git a/plugins/language/src/language-manager.h b/plugins/language/src/language-manager.h new file mode 100644 index 00000000..639bbe2d --- /dev/null +++ b/plugins/language/src/language-manager.h @@ -0,0 +1,99 @@ +/** + * Copyright (c) 2022 ~ 2023 KylinSec Co., Ltd. + * kiran-control-panel is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: yinhongchang + */ + +#ifndef __LANGUAGE_MANAGER_H__ +#define __LANGUAGE_MANAGER_H__ + +#include +#include +#include "language-select-dialog.h" + +struct LangInfo{ + QString locale; + QString desc; + QString langCode; + QString languageName; + LangInfo() :locale(), desc(), langCode(),languageName(){} + bool operator==(const LangInfo& other) const + { + if (locale == other.locale && desc == other.desc && langCode == other.langCode && languageName == other.languageName) + return true; + else + return false; + } +}; + +class QGSettings; +class LangpackInstaller; +class LanguageManager : public QWidget +{ + Q_OBJECT + +public: + explicit LanguageManager(QWidget* parent = nullptr); + ~LanguageManager(); + + +public: + inline QString getCurrentLanguage() + { + return m_currentLanguage; + } + + inline QList getShowLangugaeList() + { + return m_showLanguageList; + } + + inline QList getLanguageList() + { + return m_languageList; + } + inline LanguageSelectDialog* languageDailog() + { + return m_languageDialog; + } + void setCurrentLanguage(const QString& locale); + void deleteShowLanguage(const QString& locale); + bool isSupportedLanguage(const QString& locale); + void initConnect(); + void initLanguageDBus(); + QList parseSupportLang(const QString& langs); + +private: + void initGSettings(); + // 展示到主界面的语言列表 + void showLangugaeList(); + // 支持的所有语言列表 + void languageList(); + // 当前系统语言 + void currentLanguage(); + +signals: + void showLanguageChanged(); + +private slots: + void addShowLanguage(QString locale); + +private: + QString m_currentLanguage; + QList m_showLanguageList; + QList m_languageList; + QGSettings* m_languageSettings = nullptr; + LangpackInstaller* m_langpackInstaller = nullptr; + LanguageSelectDialog* m_languageDialog = nullptr; + QDBusInterface* m_languageDBus = nullptr; +}; + +#endif \ No newline at end of file diff --git a/plugins/language/src/language-page.cpp b/plugins/language/src/language-page.cpp new file mode 100644 index 00000000..b808c842 --- /dev/null +++ b/plugins/language/src/language-page.cpp @@ -0,0 +1,199 @@ +/** + * Copyright (c) 2022 ~ 2023 KylinSec Co., Ltd. + * kiran-control-panel is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: yinhongchang + */ + +#include +#include +#include +#include +#include +#include "kiran-setting-container.h" +#include "kiran-setting-item.h" +#include "language-page.h" +#include "language-manager.h" + +LanguagePage::LanguagePage(QWidget* parent) : QWidget(parent), + m_languageManager(new LanguageManager(this)) +{ + initUI(); + initLanguageSelectDialogUI(); + initConnect(); +} + +LanguagePage::~LanguagePage() +{ +} + +void LanguagePage::initConnect() +{ + connect(m_languageManager, &LanguageManager::showLanguageChanged, this, &LanguagePage::handleLanguageChanged); +} + +// 加载每一项languageItem +void LanguagePage::loadLangaugeItem() +{ + QString currentLanguage = m_languageManager->getCurrentLanguage(); + QList showLanguageList = m_languageManager->getShowLangugaeList(); + + for (const auto& showLanguage : showLanguageList) + { + auto languageItem = createLanguageItem(showLanguage); + + if (currentLanguage == showLanguage.locale) + { + languageItem->setMidButtonVisible(true, QIcon(":/kiran-control-panel/images/indicator-selected.png")); + m_languageItem = languageItem; + } + m_languageContainer->addItem(languageItem); + connect(languageItem, &KiranSettingItem::clicked, this, &LanguagePage::toggleLanguageItem); + + auto deleteLanguageItemSlot = std::bind(&LanguagePage::deleteLanguageItem, this, languageItem); + connect(languageItem, &KiranSettingItem::rightButtonClicked, this, deleteLanguageItemSlot); + } + initAddBtn(); +} + +void LanguagePage::initUI() +{ + auto mainLayout = new QBoxLayout(QBoxLayout::TopToBottom, this); + mainLayout->setContentsMargins(24, 14, 14, 14); + mainLayout->setSpacing(0); + + auto label = new QLabel(tr("Language Select(Reboot to take effect)"), this); + mainLayout->addWidget(label); + + auto languageWidget = new QWidget(this); + mainLayout->addWidget(languageWidget); + + auto languageLayout = new QBoxLayout(QBoxLayout::TopToBottom, languageWidget); + languageLayout->setSpacing(8); + languageLayout->setContentsMargins(0, 0, 10, 0); + + languageLayout->addSpacerItem(new QSpacerItem(10, 10, QSizePolicy::Minimum, QSizePolicy::Fixed)); + + m_languageContainer = new SettingsContainer(this); + languageLayout->addWidget(m_languageContainer); + + languageLayout->addStretch(); + + loadLangaugeItem(); +} + +void LanguagePage::initAddBtn() +{ + m_languageButton = new QPushButton(this); + Kiran::StylePropertyHelper::setButtonType(m_languageButton, Kiran::BUTTON_Default); + m_languageButton->setIcon(QIcon(":/kcp-application/images/addition.svg")); + m_languageContainer->addItem(m_languageButton); + connect(m_languageButton, &QPushButton::clicked, this, [=]() { + m_languageManager->languageDailog()->show(); + }); +} + +void LanguagePage::deleteLanguageItem(KiranSettingItem* languageItem) +{ + QString locale = languageItem->getUserData().toString(); + QString currentLocale = m_languageManager->getCurrentLanguage(); + if (languageItem == nullptr || locale == currentLocale) + { + KLOG_DEBUG() << "can't delete currentLanguageitem:" << locale; + return; + } + + m_languageContainer->removeItem(languageItem); + m_languageManager->deleteShowLanguage(languageItem->getUserData().toString()); +} + +void LanguagePage::toggleLanguageItem() +{ + KiranSettingItem* languageItem = qobject_cast(sender()); + QString currentLocale = languageItem->getUserData().toString(); + // 取消切换前的语言选中状态 + m_languageItem->setMidButtonVisible(false, QIcon(":/kiran-control-panel/images/indicator-selected.png")); + QString beforeLocale = m_languageItem->getUserData().toString(); + // 添加切换后的语言选中状态 + m_languageItem = languageItem; + languageItem->setMidButtonVisible(true, QIcon(":/kiran-control-panel/images/indicator-selected.png")); + m_languageManager->setCurrentLanguage(currentLocale); +} + +QString LanguagePage::showLanguageName(QString languageCode) +{ + QString languageName; + if (languageCode == "zh_CN.UTF-8") + { + languageName = QString("简体中文") + QString("-%1").arg(tr("Simplified Chinese")); + } + else if (languageCode == "en_US.UTF-8") + { + languageName = QString("English") + QString("-%1").arg(tr("English")); + } + else if (languageCode == "bo_CN") + { + languageName = QString("বাংলা") + QString("-%1").arg(tr("Tibetan")); + } + else if (languageCode == "ky_KG") + { + languageName = QString("Кыргызча") + QString("-%1").arg(tr("Kirgiz")); + } + else if (languageCode == "mn_MN") + { + languageName = QString("Монгол") + QString("-%1").arg(tr("Mongolian")); + } + else if (languageCode == "kk_KZ") + { + languageName = QString("Қазақша") + QString("-%1").arg(tr("Kazakh")); + } + else if (languageCode == "ug_CN") + { + languageName = QString("Uyghurche") + QString("-%1").arg(tr("Uighur")); + } + else + { + languageName = QString("languageCode") + QString("-%1").arg("languageCode"); + } + return languageName; +} + +KiranSettingItem* LanguagePage::createLanguageItem(const LangInfo& langInfo) +{ + auto languageItem = new KiranSettingItem(this); + languageItem->setClickable(true); + languageItem->setLeftButtonVisible(false, QIcon()); + languageItem->setRightButtonVisible(true, QIcon(":/kiran-control-panel/images/trash.svg")); + languageItem->setSwitcherVisible(false); + languageItem->setMidButtonVisible(false, QIcon(":/kiran-control-panel/images/indicator-selected.png")); + languageItem->setUserData(langInfo.locale); + + QString textLable = showLanguageName(langInfo.locale); + languageItem->setText(textLable); + + return languageItem; +} + +void LanguagePage::handleLanguageChanged() +{ + // 重新加载 + m_languageContainer->clear(); + loadLangaugeItem(); +} + + +void LanguagePage::initLanguageSelectDialogUI() +{ + QList languageList = m_languageManager->getLanguageList(); + for (const auto& langInfo : languageList) + { + m_languageManager->languageDailog()->addLanguageItem(showLanguageName(langInfo.locale), langInfo.locale); + } +} \ No newline at end of file diff --git a/plugins/language/src/language-page.h b/plugins/language/src/language-page.h new file mode 100644 index 00000000..89ad883a --- /dev/null +++ b/plugins/language/src/language-page.h @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2022 ~ 2023 KylinSec Co., Ltd. + * kiran-control-panel is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: yinhongchang + */ + + +#ifndef __LANGUAGE_PAGE_H__ +#define __LANGUAGE_PAGE_H__ + +#include + +class LanguageManager; +class QPushButton; +class SettingsContainer; +class KiranSettingItem; +class LangInfo; +class LanguagePage : public QWidget +{ + Q_OBJECT + +public: + explicit LanguagePage(QWidget* parent = nullptr); + ~LanguagePage(); + +private: + void initUI(); + void initAddBtn(); + void initConnect(); + void loadLangaugeItem(); + void initLanguageSelectDialogUI(); + QString showLanguageName(QString languageCode); + KiranSettingItem* createLanguageItem(const LangInfo& langInfo); + +private slots: + void toggleLanguageItem(); + void deleteLanguageItem(KiranSettingItem* languageItem); + void handleLanguageChanged(); + +private: + LanguageManager* m_languageManager = nullptr; + + SettingsContainer* m_languageContainer = nullptr; + QPushButton* m_languageButton = nullptr; + KiranSettingItem* m_languageItem = nullptr; +}; + +#endif \ No newline at end of file diff --git a/plugins/language/src/language-select-dialog.cpp b/plugins/language/src/language-select-dialog.cpp new file mode 100644 index 00000000..c5e53827 --- /dev/null +++ b/plugins/language/src/language-select-dialog.cpp @@ -0,0 +1,141 @@ +/** + * Copyright (c) 2022 ~ 2023 KylinSec Co., Ltd. + * kiran-control-panel is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: yinhongchang + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "language-select-dialog.h" +#include "ui_language-select-dialog.h" +#include "search-delegate.h" + +LanguageSelectDialog::LanguageSelectDialog(QWidget* parent) : QDialog(parent), + ui(new Ui::LanguageSelectDialog), m_kiranSearchBox(new KiranSearchBox(this)) +{ + ui->setupUi(this); + this->setWindowTitle(tr("Add Language")); + m_kiranSearchBox->setPlaceholderText(tr("Search")); + ui->frame->layout()->addWidget(m_kiranSearchBox); + ui->tableView->setStyleSheet("QTableView{border: 1px solid palette(midlight);}"); + model = new QStandardItemModel(this); + ui->tableView->setModel(model); + ui->tableView->setAlternatingRowColors(true); + ui->tableView->setMouseTracking(true); + ui->tableView->setSelectionMode(QAbstractItemView::SelectionMode::SingleSelection); + ui->tableView->setSelectionBehavior(QAbstractItemView::SelectionBehavior::SelectRows); + connect(ui->tableView, &QTableView::entered, + this, [=](const QModelIndex &index){ + QString mStr = index.data().toString(); + QFontMetrics fontMetrics(this->font()); + int fontSize = fontMetrics.width(mStr); + if (fontSize - 16 > 416) { + QToolTip::showText(QCursor::pos(), mStr); + } + }); + // 隐藏垂直表头 + ui->tableView->verticalHeader()->setVisible(false); + // 隐藏水平表头 + ui->tableView->horizontalHeader()->setVisible(false); + // 隐藏水平滑动条 + ui->tableView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + // 不显示表格线 + ui->tableView->setShowGrid(false); + // 水平表格自动拉伸 + ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); + // 行高默认为36 + ui->tableView->verticalHeader()->setDefaultSectionSize(36); + this->setWindowModality(Qt::ApplicationModal); + // 不可编辑 + ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers); + model->setColumnCount(1); + // 加载Completer到KiranSearchBox中,便于搜索 + initCompleter(); + + // 选中语言添加 + connect(ui->yesBtn, &QPushButton::clicked, this, [=]() { + QModelIndexList indexList = ui->tableView->selectionModel()->selectedRows(); + QString locale; + if (!indexList.empty()) + { + QModelIndex modelIndex = indexList.first(); + locale = model->data(modelIndex, Qt::UserRole + 1).toString(); + } + emit languageAddBtnClicked(locale); + this->hide(); + }); + // 取消 + connect(ui->noBtn, &QPushButton::clicked, this, [=]() { + this->hide(); + }); +} + +LanguageSelectDialog::~LanguageSelectDialog() +{ +} + +void LanguageSelectDialog::initCompleter() +{ + m_searchDelegate = new SearchDelegate(this); + m_completer = new QCompleter(this); + m_completer->setModel(model); + m_completer->setMaxVisibleItems(6); + m_completer->popup()->setAttribute(Qt::WA_InputMethodEnabled); + m_completer->popup()->setItemDelegate(m_searchDelegate); + m_completer->setFilterMode(Qt::MatchContains); + m_completer->setCaseSensitivity(Qt::CaseInsensitive); + m_completer->setCompletionMode(QCompleter::PopupCompletion); + m_completer->setWrapAround(false); + if (m_kiranSearchBox) { + m_kiranSearchBox->setCompleter(m_completer); + } + + connect(m_kiranSearchBox, &QLineEdit::returnPressed, this, [=]() { + if (!m_kiranSearchBox->text().isEmpty()) { + QList itemList = model->findItems(m_kiranSearchBox->text()); + if (!itemList.isEmpty()) { + QModelIndex m_modeIndex = model->indexFromItem(itemList.first()); + ui->tableView->scrollTo(m_modeIndex); + int index = m_modeIndex.row(); + ui->tableView->selectRow(index); + } + } + }); + + connect(m_kiranSearchBox, &QLineEdit::textChanged, this, [=](QString text){ + for (int i = 0; i < model->rowCount(); i++) { + if (text.contains(model->data(model->index(i,0)).toString())) { + m_kiranSearchBox->blockSignals(true); + m_kiranSearchBox->setText(model->item(i)->text()); + m_kiranSearchBox->blockSignals(false); + } + } + }); + + connect(m_completer, QOverload::of(&QCompleter::activated), + [=](const QString &text) { + Q_UNUSED(text); + emit m_kiranSearchBox->returnPressed(); + }); +} + +void LanguageSelectDialog::addLanguageItem(const QString &languageName, const QString &locale) +{ + auto item = new QStandardItem(languageName); + item->setData(locale); + model->appendRow(item); +} \ No newline at end of file diff --git a/plugins/language/src/language-select-dialog.h b/plugins/language/src/language-select-dialog.h new file mode 100644 index 00000000..4aeb885b --- /dev/null +++ b/plugins/language/src/language-select-dialog.h @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2022 ~ 2023 KylinSec Co., Ltd. + * kiran-control-panel is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: yinhongchang + */ +#ifndef __LANGUAGE_SELECT_DIALOG_H__ +#define __LANGUAGE_SELECT_DIALOG_H__ + +#include +#include + +namespace Ui +{ + class LanguageSelectDialog; +} + +class QStandardItemModel; +class KiranSearchBox; +class QCompleter; +class SearchDelegate; +class LanguageSelectDialog : public QDialog +{ + Q_OBJECT + +public: + explicit LanguageSelectDialog(QWidget* parent = nullptr); + ~LanguageSelectDialog(); + void initCompleter(); + void addLanguageItem(const QString &languageName, const QString &locale); + +signals: + void languageAddBtnClicked(QString locale); + +private: + Ui::LanguageSelectDialog* ui = nullptr; + KiranSearchBox* m_kiranSearchBox = nullptr; + + QStandardItemModel* model = nullptr; + QCompleter* m_completer = nullptr; + SearchDelegate* m_searchDelegate = nullptr; +}; + +#endif \ No newline at end of file diff --git a/plugins/language/src/language-select-dialog.ui b/plugins/language/src/language-select-dialog.ui new file mode 100644 index 00000000..e4ff5da0 --- /dev/null +++ b/plugins/language/src/language-select-dialog.ui @@ -0,0 +1,174 @@ + + + LanguageSelectDialog + + + + 0 + 0 + 480 + 520 + + + + + 480 + 520 + + + + + 480 + 520 + + + + Dialog + + + + 16 + + + 24 + + + 16 + + + 24 + + + 24 + + + + + + 432 + 46 + + + + + 432 + 46 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + 432 + 360 + + + + + 432 + 360 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + 16 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 96 + 0 + + + + + 96 + 16777215 + + + + No + + + + + + + + 96 + 0 + + + + + 96 + 16777215 + + + + Yes + + + + + + + + + + diff --git a/resources/control-panel-resources.qrc b/resources/control-panel-resources.qrc index 2fc9eeae..e460192b 100644 --- a/resources/control-panel-resources.qrc +++ b/resources/control-panel-resources.qrc @@ -93,4 +93,7 @@ images/desktop-file.svg + + images/language.svg + diff --git a/resources/images/language.svg b/resources/images/language.svg new file mode 100644 index 00000000..138d4b71 --- /dev/null +++ b/resources/images/language.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/translations/kiran-control-panel.zh_CN.ts b/translations/kiran-control-panel.zh_CN.ts index 1ba6ea74..995c4364 100644 --- a/translations/kiran-control-panel.zh_CN.ts +++ b/translations/kiran-control-panel.zh_CN.ts @@ -3386,6 +3386,101 @@ KylinSec.保留所有权利. + + LangpackInstaller + + Package Install + 语言包下载 + + + Installing... + 下载中... + + + Install Complete! + 下载完成! + + + + LanguageManager + + Error + 错误 + + + set locale failed + 设置语言失败 + + + %1 inexistence in system + %1 在系统中不存在 + + + + LanguagePage + + Language Select(Reboot to take effect) + 语言选择(重启生效) + + + Simplified Chinese + 简体中文 + + + English + 英语 + + + Tibetan + 藏语 + + + Kirgiz + 柯尔克孜语 + + + Mongolian + 蒙古语 + + + Kazakh + 哈萨克语 + + + Uighur + 维吾尔语 + + + + LanguagePlugin + + Language + 语言设置 + + + + LanguageSelectDialog + + Dialog + + + + No + 取消 + + + Yes + 确认 + + + Add Language + 添加语言 + + + Search + 输入你想找的内容 + + LayoutItem -- Gitee