// Copyright (C) 2019 ~ 2026 Uniontech Software Technology Co.,Ltd
// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later

#include "settings.h"
#include "newdspinbox.h"
//#include "utils.h"
#include "shortcutmanager.h"
#include "service.h"
#include "dbusmanager.h"
#include "tabrenamewidget.h"

#include <DSettingsOption>
#include <DSettingsWidgetFactory>
#include <DLog>
#include <DSlider>
#include <DGuiApplicationHelper>
#include <DKeySequenceEdit>
#include <DSysInfo>

#include <QApplication>
#include <QStandardPaths>
#include <QStringList>
#include <QFontDatabase>
#include <QLoggingCategory>
#include <QCollator>
#include <QProcessEnvironment>
#include <QJsonDocument>
#include <QHBoxLayout>

DWIDGET_USE_NAMESPACE
#define PRIVATE_PROPERTY_translateContext "_d_DSettingsWidgetFactory_translateContext"
Settings *Settings::m_settings_instance = nullptr;
DComboBox *Settings::comboBox = nullptr;
DComboBox *Settings::g_shellConfigCombox = nullptr;

// 全局变量  变量定义的位置可以变，目前只有这边用，所以定义到这儿
const QString DEFAULT_SHELL = "$SHELL";

Q_DECLARE_LOGGING_CATEGORY(tsettings)

Settings::Settings() : QObject(qApp)
{
    qCDebug(tsettings) << "Settings constructor called";
    Utils::set_Object_Name(this);
}

Settings *Settings::instance()
{
    qCDebug(tsettings) << "Settings instance requested";
    if(nullptr == m_settings_instance) {
        qCDebug(tsettings) << "Creating new Settings instance";
        m_settings_instance = new Settings();
        m_settings_instance->init();
        qCDebug(tsettings) << "Settings instance initialized";
    }
    return m_settings_instance;
}

Settings::~Settings()
{
    qCDebug(tsettings) << "Settings destructor called";
    if (nullptr != m_Watcher) {
        qCDebug(tsettings) << "Deleting file watcher";
        m_Watcher->deleteLater();
    }

    if (nullptr != m_backend) {
        qCDebug(tsettings) << "Deleting backend";
        m_backend->deleteLater();
    }

    if (nullptr != settings) {
        qCDebug(tsettings) << "Deleting settings";
        settings->deleteLater();
    }
}

// 统一初始化以后方可使用。
void Settings::init()
{
    qCDebug(tsettings) << "Initializing settings";
    m_configPath = QString("%1/%2/%3/config.conf")
                   .arg(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation), qApp->organizationName(), qApp->applicationName());
    qCDebug(tsettings) << "Config path:" << m_configPath;
    m_backend = new QSettingBackend(m_configPath);
    m_backend->setObjectName("SettingsQSettingBackend");//Add by ut001000 renfeixiang 2020-08-13

    // 默认配置
    QFile configFile(":/other/default-config.json");
    if(!configFile.open(QFile::ReadOnly)) {
        qCWarning(tsettings) << "Failed to open default-config.json";
    } else {
        qCDebug(tsettings) << "Default config loaded successfully";
    }
    QByteArray json = configFile.readAll();
    configFile.close();

    QJsonDocument doc = QJsonDocument::fromJson(json);
    QVariant jsonVar = doc.toVariant();
    //龙芯 且 服务器企业版
    if(Utils::isLoongarch() && DSysInfo::uosEditionType() == DSysInfo::UosEnterprise) {
        qCDebug(tsettings) << "Configuring for Loongarch UOS Enterprise";
        //隐藏透明度界面
        Utils::insertToDefaultConfigJson(jsonVar, "basic", "interface", "opacity", "hide", true);
        //隐藏背景模糊界面
        Utils::insertToDefaultConfigJson(jsonVar, "advanced", "window", "blurred_background", "hide", true);
    }
    //终端的默认字体，由固定：Noto Sans Mono，改为读系统的默认字体
    QString defaultFont = Utils::getValueInDefaultConfigJson(jsonVar, "basic", "interface", "font", "default").toString();
    QString systemFixedFont = QFontDatabase::systemFont(QFontDatabase::FixedFont).family();

    //无效字体时，exactMatch：Returns true if a window system font exactly matching the settings of this font is available.
    if(!QFont(defaultFont).exactMatch()) {
        qCDebug(tsettings) << "Default font not available, using system fixed font:" << systemFixedFont;
        Utils::insertToDefaultConfigJson(jsonVar, "basic", "interface", "font", "default", systemFixedFont);
    }
    //更新json
    json = QJsonDocument::fromVariant(jsonVar).toJson();
    settings = DSettings::fromJson(json);

    // 加载自定义配置
    settings->setBackend(m_backend);
    /*
     * 主题分：标题栏(colorScheme)、终端界面(extendColorScheme) 、gsetting
     * colorScheme必为Dark或Light，extendColorScheme为空时是系统主题，不为空时是自带主题,gsettings为Dark或Light或unknownType
     * 例如
     * 1.为Dark主题时:  colorScheme：Dark             extendColorScheme：""           gsettings：Dark
     * 2.为Bim主题时:   colorScheme：Dark             extendColorScheme：Theme4       gsettings：Dark
     * 3.随系统时:      colorScheme：Dark or Light    extendColorScheme：""           gsettings：unknownType
     * 要求以下三个场景启动时，主题为Dark
     * 1.首次运行终端，colorScheme=""
     * 2.删除conf文件，colorScheme=""
     * 3.人为修改conf，colorScheme=不可预知
     * 综上即colorScheme非Light且非Dark时，修改主题为Dark
     */
    if("Light" != colorScheme() && "Dark" != colorScheme()) {
        qCDebug(tsettings) << "Setting default color scheme to Dark";
        DGuiApplicationHelper::instance()->setPaletteType(DGuiApplicationHelper::DarkType);
        setColorScheme("Dark");
        setExtendColorScheme("");
        qCDebug(tsettings) << "Color scheme initialized";
    } else if (DGuiApplicationHelper::instance()->paletteType() == DGuiApplicationHelper::UnknownType
               && extendColorScheme().isEmpty()) {
        // 跟随系统模式：colorScheme 可能因上次关闭后系统主题变化而过期，需同步为当前系统主题
        QString sysTheme = (DGuiApplicationHelper::instance()->themeType() == DGuiApplicationHelper::LightType) ? "Light" : "Dark";
        if (colorScheme() != sysTheme) {
            qCDebug(tsettings) << "Syncing colorScheme to system theme:" << colorScheme() << "->" << sysTheme;
            setColorScheme(sysTheme);
        }
    }

    // 按 colorScheme 对齐 paletteType，激活 dtkgui 的 isCustomPalette() 过滤，
    // 避免外部应用切主题时 dde-appearance 经 DPlatformTheme 把 palette 推送到本进程 qApp。
    // 跟随系统模式（extendColorScheme 为空且 paletteType 为 UnknownType）保持原状。
    {
        auto helper = DGuiApplicationHelper::instance();
        const bool isFollowSystem = extendColorScheme().isEmpty()
                                    && helper->paletteType() == DGuiApplicationHelper::UnknownType;
        if (!isFollowSystem) {
            const auto desiredType = (colorScheme() == "Light")
                                     ? DGuiApplicationHelper::LightType
                                     : DGuiApplicationHelper::DarkType;
            if (helper->paletteType() != desiredType) {
                helper->setPaletteType(desiredType);
            }
        }
    }
    /******** Modify by n014361 wangpeili 2020-01-10:   增加窗口状态选项  ************/
    auto windowState = settings->option("advanced.window.use_on_starting");
    QMap<QString, QVariant> windowStateMap;

    windowStateMap.insert("keys",
                          QStringList() << "window_normal"
                          << "split_screen"
                          << "window_maximum"
                          << "fullscreen");
    windowStateMap.insert("values",
                          QStringList() << tr("Normal window") << tr("Split screen") << tr("Maximum") << tr("Fullscreen"));
    windowState->setData("items", windowStateMap);

    for (QString &key : settings->keys())
        qCDebug(tsettings) <<"Config' Key: " <<  key << " Config' Value: " << settings->value(key);
    /********************* Modify by n014361 wangpeili End ************************/

    initConnection();
    loadDefaultsWhenReinstall();
    /******** Modify by ut000439 wangpeili 2020-06-12: 多进程模式，暂时取消使用*********/
# if 0
    //增加文件昨监视，以便多进程共享配置文件
    m_Watcher = new QFileSystemWatcher();
    m_Watcher->addPath(m_configPath);
    connect(m_Watcher, &QFileSystemWatcher::fileChanged, this, [this](QString file) {
        reload();
        //监控完一次就不再监控了，所以要再添加
        m_Watcher->addPath(m_configPath);
    });
#endif

    /***add begin by ut001121 zhangmeng 20200912 设置字号限制 修复42250***/
    auto option = settings->option("basic.interface.font_size");
    Konsole::__minFontSize = option->data("min").isValid() ? option->data("min").toInt() : DEFAULT_MIN_FONT_SZIE;
    Konsole::__maxFontSize = option->data("max").isValid() ? option->data("max").toInt() : DEFAULT_MAX_FONT_SZIE;

    // 校验正确
    if (Konsole::__minFontSize > Konsole::__maxFontSize) {
        qCDebug(tsettings) << "Font size range invalid, swapping min and max";
        qSwap(Konsole::__minFontSize, Konsole::__maxFontSize);
    }

    /***add end by ut001121***/

    //自定义主题配置初始化处理
    m_configCustomThemePath = QString("%1/%2/%3/customTheme.colorscheme")
                              .arg(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation), qApp->organizationName(), qApp->applicationName());
    themeSetting = new QSettings(m_configCustomThemePath, QSettings::IniFormat, this);

    QFile customThemeFile(m_configCustomThemePath);
    qCDebug(tsettings) << "Custom theme path:" << m_configCustomThemePath;
    if (!customThemeFile.exists()) {
        qCDebug(tsettings) << "Creating default custom theme";
        //标题栏风格   // custom_theme_title_style
        themeSetting->setValue("CustomTheme/TitleStyle", "Dark");

        //背景颜色 “黑色” （0x252525)
        QColor backgroundColor(37, 37, 37);
        themeSetting->setValue("Background/Color", color2str(backgroundColor));
        //前景色 “绿色” （0x00FF00）
        QColor foregroundColor(0, 255, 0);
        themeSetting->setValue("Foreground/Color", color2str(foregroundColor));

        //自定义主题中实际修改的PS1参数只是一个"Color2Intense/Color" ，而配置"Color2/Color" 是为了保证python脚本取色RGB正常显示使用的固定不变，跟终端的所有主题都保持一致 //Green
        QColor color2Color(24, 178, 24);
        //提示符   PS1  “土黄色” （0x859900）
        QColor pS1Color(133, 153, 0);

        themeSetting->setValue("Color2/Color", color2str(color2Color));
        themeSetting->setValue("Color2Intense/Color", color2str(pS1Color));

        //自定义主题中实际修改的参数PS2只是一个"Color4Intense/Color" ，而配置"Color4/Color" 是为了保证python脚本取色RGB正常显示使用的固定不变，跟终端的所有主题都保持一致 //Blue
        QColor color4Color(24, 24, 178);
        //提示符   PS2  “蓝色” （0x3465A4）
        QColor pS2Color(52, 101, 164);
        themeSetting->setValue("Color4/Color", color2str(color4Color));
        themeSetting->setValue("Color4Intense/Color", color2str(pS2Color));

        //配置"Color1/Color"与"Color1Intense/Color"  是为了保证python脚本取色RGB正常显示使用的固定不变，跟终端的所有主题都保持一致 //Red
        QColor color1Color(178, 24, 24);
        QColor color1IntenseColor(255, 84, 84);
        themeSetting->setValue("Color1/Color", color2str(color1Color));
        themeSetting->setValue("Color1Intense/Color", color2str(color1IntenseColor));


        //参考深色主题与浅色主题的配置文件，增加默认参数，保证python脚本取色在自定义主题下可以取到相应参数的配色，begin
        //Color3
        themeSetting->setValue("Color3/Color", color2str(QColor(255, 192, 5)));
        //Color3Intense
        themeSetting->setValue("Color3Intense/Color", color2str(QColor(255, 192, 5)));

        //Color5
        themeSetting->setValue("Color5/Color", color2str(QColor(236, 0, 72)));
        //Color5Intense
        themeSetting->setValue("Color5Intense/Color", color2str(QColor(236, 0, 72)));

        //Color6
        themeSetting->setValue("Color6/Color", color2str(QColor(42, 167, 231)));
        //Color6Intense
        themeSetting->setValue("Color6Intense/Color", color2str(QColor(42, 167, 231)));

        //Color7
        themeSetting->setValue("Color7/Color", color2str(QColor(242, 242, 242)));
        //Color7Intense
        themeSetting->setValue("Color7Intense/Color", color2str(QColor(242, 242, 242)));

        //参考深色主题与浅色主题的配置文件，增加默认参数，保证python脚本取色在自定义主题下可以取到相应参数的配色，end
    }

}

QStringList Settings::color2str(QColor color)
{
    // qCDebug(tsettings) << "Converting color to string:" << color;
    QStringList ret;
    ret << QString::number(color.red());
    ret << QString::number(color.green());
    ret << QString::number(color.blue());
    return ret;
}

//重新安装终端后在这里重置状态
void Settings::loadDefaultsWhenReinstall()
{
    qCDebug(tsettings) << "Loading defaults when reinstall";
    QDir installFlagPath(QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation));
    if (!installFlagPath.exists()) {
        qCDebug(tsettings) << "Creating app config directory";
        installFlagPath.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation));
    }
}

void Settings::addShellOption()
{
    qCDebug(tsettings) << "Adding shell options";
    g_shellConfigCombox->clear();
    // 获取shells
    QMap<QString, QString> shellsMap = Service::instance()->getShells();
    // item列表
    QStringList keysList;
    // 初始值
    keysList << DEFAULT_SHELL;
    // 数据转换
    for (const QString &key : shellsMap.keys()) {
        keysList << key;
    }
    g_shellConfigCombox->addItems(keysList);
    qCDebug(tsettings) << "Shell options added:" << keysList.size() << "shells";
}

QPair<QWidget *, QWidget *>  Settings::createTabTitleFormatWidget(QObject *opt, bool isRemote)
{
    DTK_CORE_NAMESPACE::DSettingsOption *option = qobject_cast<DTK_CORE_NAMESPACE::DSettingsOption *>(opt);
    // tabrenaemewidget 第一个false表示标签重命名 第二个true表示不加标签提示
    TabRenameWidget *tabTitleFormat = new TabRenameWidget(isRemote, true);

    tabTitleFormat->getInputedit()->setText(option->value().toString());

    QPair<QWidget *, QWidget *> optionWidget =
        DSettingsWidgetFactory::createStandardItem(QByteArray(), option, tabTitleFormat);
    // 别的窗口修改了设置，这里需要同步设置显示
    connect(option, &DSettingsOption::valueChanged, tabTitleFormat, [ = ](QVariant var) {
        tabTitleFormat->getInputedit()->setText(var.toString());
    });

    // 点击按钮，改变输入框内容后给输入框设置焦点
    option->connect(tabTitleFormat->getInputedit(), &DLineEdit::textChanged, option, [ = ]() {
        tabTitleFormat->getInputedit()->lineEdit()->setFocus();
    });
    // 编辑结束才修改标签
    option->connect(tabTitleFormat->getInputedit(), &DLineEdit::editingFinished, option, [ = ]() {
        option->setValue(tabTitleFormat->getInputedit()->text());
    });

    return optionWidget;
}

void Settings::initConnection()
{
    qCDebug(tsettings) << "Initializing connections";
    connect(settings, &Dtk::Core::DSettings::valueChanged, this, [ = ](const QString & key, const QVariant & value) {
        Q_UNUSED(value)
        if (key.contains("basic.interface.") || key.contains("advanced.cursor.") || key.contains("advanced.scroll.") || key.contains("advanced.shell.")) {
            qCDebug(tsettings) << "Terminal setting changed:" << key;
            emit terminalSettingChanged(key);
        } else if (key.contains("shortcuts.")) {
            qCDebug(tsettings) << "Shortcut setting changed:" << key;
            emit shortcutSettingChanged(key);
        } else {
            qCDebug(tsettings) << "Window setting changed:" << key;
            emit windowSettingChanged(key);
        }
    });

    QPointer<DSettingsOption> opacity = settings->option("basic.interface.opacity");
    connect(opacity, &Dtk::Core::DSettingsOption::valueChanged, this, [ = ](QVariant value) {
        qCDebug(tsettings) << "Opacity changed:" << value.toInt();
        emit opacityChanged(value.toInt() / 100.0);
    });

    QPointer<DSettingsOption> cursorShape = settings->option("advanced.cursor.cursor_shape");
    connect(cursorShape, &Dtk::Core::DSettingsOption::valueChanged, this, [ = ](QVariant value) {
        qCDebug(tsettings) << "Cursor shape changed:" << value.toInt();
        emit cursorShapeChanged(value.toInt());
    });

    QPointer<DSettingsOption> cursorBlink = settings->option("advanced.cursor.cursor_blink");
    connect(cursorBlink, &Dtk::Core::DSettingsOption::valueChanged, this, [ = ](QVariant value) {
        qCDebug(tsettings) << "Cursor blink changed:" << value.toBool();
        emit cursorBlinkChanged(value.toBool());
    });

    QPointer<DSettingsOption> wordCharacters = settings->option("advanced.cursor.include_special_characters_in_double_click_selections");
    connect(wordCharacters, &Dtk::Core::DSettingsOption::valueChanged, this, [ = ](QVariant value) {
        emit wordCharactersChanged(value.toString());
    });

    QPointer<DSettingsOption> cursorPositionSet = settings->option("advanced.cursor.set_cursor_position");
    connect(cursorPositionSet, &Dtk::Core::DSettingsOption::valueChanged, this, [ = ](QVariant value) {
        emit enableSetCursorPosition(value.toBool());
    });

    QPointer<DSettingsOption> backgroundBlur = settings->option("basic.interface.blurred_background");
    connect(backgroundBlur, &Dtk::Core::DSettingsOption::valueChanged, this, [ = ](QVariant value) {
        qCDebug(tsettings) << "Background blur changed:" << value.toBool();
        emit backgroundBlurChanged(value.toBool());
    });

    /******** Modify by n014361 wangpeili 2020-01-06:  字体，字体大小实时生效 ****************/
    QPointer<DSettingsOption> fontSize = settings->option("basic.interface.font_size");
    connect(fontSize, &Dtk::Core::DSettingsOption::valueChanged, this, [ = ](QVariant value) {
        qCDebug(tsettings) << "Font size changed:" << value.toInt();
        emit fontSizeChanged(value.toInt());
    });

    QPointer<DSettingsOption> historySize = settings->option("advanced.scroll.history_size");
    connect(historySize, &Dtk::Core::DSettingsOption::valueChanged, this, [ = ](QVariant value) {
        qInfo() << "History size changed to" << value.toInt();
        emit historySizeChanged(value.toInt());
    });

    QPointer<DSettingsOption> family = settings->option("basic.interface.font");
    connect(family, &Dtk::Core::DSettingsOption::valueChanged, this, [ = ](QVariant value) {
        qCDebug(tsettings) << "Font changed:" << value.toString();
        emit fontChanged(value.toString());
    });

    QPointer<DSettingsOption> PressingScroll = settings->option("advanced.scroll.scroll_on_key");
    connect(PressingScroll, &Dtk::Core::DSettingsOption::valueChanged, this, [ = ](QVariant value) {
        qCDebug(tsettings) << "Pressing scroll changed:" << value.toBool();
        emit pressingScrollChanged(value.toBool());
    });
    /********************* Modify by n014361 wangpeili End ************************/

    // 标签标题格式变化
    QPointer<DSettingsOption> tabFormat = settings->option("basic.tab_title.tab_title_format");
    connect(tabFormat, &Dtk::Core::DSettingsOption::valueChanged, this, [ = ](QVariant value) {
        qCDebug(tsettings) << "Tab format changed:" << value.toString();
        emit tabFormatChanged(value.toString());
    });

    // 远程标签标题格式变化
    QPointer<DSettingsOption> remoteTabFormat = settings->option("basic.tab_title.remote_tab_title_format");
    connect(remoteTabFormat, &Dtk::Core::DSettingsOption::valueChanged, this, [ = ](QVariant value) {
        qCDebug(tsettings) << "Remote tab format changed:" << value.toString();
        emit remoteTabFormatChanged(value.toString());
    });
}

void Settings::releaseInstance()
{
    qCDebug(tsettings) << "Releasing Settings instance";
    if(nullptr != m_settings_instance) {
        qCDebug(tsettings) << "Deleting Settings instance";
        delete m_settings_instance;
        m_settings_instance = nullptr;
    }
}

qreal Settings::opacity() const
{
    qCDebug(tsettings) << "Getting opacity value";
    return settings->option("basic.interface.opacity")->value().toInt() / 100.0;
}

int Settings::QuakeDuration() const
{
    const int step = settings->option("advanced.window.quake_window_animation_duration")->data("step").toInt();
    return settings->option("advanced.window.quake_window_animation_duration")->value().toInt() * step;
}
QString Settings::encoding() const
{
    qCDebug(tsettings) << "Getting encoding:" << m_EncodeName;
    return m_EncodeName;
}

QString Settings::fontName()
{
    // qCDebug(tsettings) << "Getting font name";
    QString fontName = settings->option("basic.interface.font")->value().toString();
    // qCDebug(tsettings) << "Font name:" << fontName;
    return fontName;
}

int Settings::fontSize()
{
    // qCDebug(tsettings) << "Getting font size";
    int size = settings->option("basic.interface.font_size")->value().toInt();
    // qCDebug(tsettings) << "Font size:" << size;
    return size;
}

bool Settings::PressingScroll()
{
    // qCDebug(tsettings) << "Getting pressing scroll setting";
    bool scroll = settings->option("advanced.scroll.scroll_on_key")->value().toBool();
    // qCDebug(tsettings) << "Pressing scroll:" << scroll;
    return scroll;
}

bool Settings::OutputtingScroll()
{
    // qCDebug(tsettings) << "Getting outputting scroll setting";
    bool scroll = settings->option("advanced.scroll.scroll_on_output")->value().toBool();
    // qCDebug(tsettings) << "Outputting scroll:" << scroll;
    return scroll;
}

/*******************************************************************************
 1. @函数:    reload
 2. @作者:    ut001121 zhangmeng
 3. @日期:    2020-08-11
 4. @说明:    设置界面重新加载设置的键值
*******************************************************************************/
//void Settings::reload()
//{
//    QSettings  newSettings(m_configPath, QSettings::IniFormat);
//    for (QString &key : newSettings.childGroups()) {
//        //　当系统变更键值的时候，配置文件中会有一些＂垃圾＂配置，删除他
//        if (!settings->keys().contains(key)) {
//            newSettings.remove(key);
//            continue;
//        }
//        if (settings->value(key) != newSettings.value(key + "/value")) {
//            settings->option(key)->setValue(newSettings.value(key + "/value"));
//        }
//    }
//}

QString Settings::tabTitleFormat() const
{
    // qCDebug(tsettings) << "Getting tab title format";
    QString format = settings->option("basic.tab_title.tab_title_format")->value().toString();
    // qCDebug(tsettings) << "Tab title format:" << format;
    return format;
}

QString Settings::remoteTabTitleFormat() const
{
    // qCDebug(tsettings) << "Getting remote tab title format";
    QString format = settings->option("basic.tab_title.remote_tab_title_format")->value().toString();
    // qCDebug(tsettings) << "Remote tab title format:" << format;
    return format;
}

QString Settings::shellPath() const
{
    qCDebug(tsettings) << "Getting shell path";
    QString strShellProgram = settings->option("advanced.shell.default_shell")->value().toString();
    // $SHELL无法写入配置文件中
    if (DEFAULT_SHELL == strShellProgram || strShellProgram.isEmpty()) {
        qCDebug(tsettings) << "Using default shell from environment";
        QString shell{ getenv("SHELL") };
        qCDebug(tsettings) << "Default shell:" << shell;
        return shell;
    }

    qCDebug(tsettings) << "Using configured shell:" << strShellProgram;
    return strShellProgram;
}

void Settings::reloadShellOptions()
{
    qCDebug(tsettings) << "Reloading shell options";
    // 记录设置当前值
    QString strShellProgram = settings->option("advanced.shell.default_shell")->value().toString();
    // 更新shell选项
    addShellOption();
    QMap<QString, QString> shellMap = Service::instance()->shellsMap();
    // 设置之前的项 若strShellProgram不存在会自动选取第一项
    g_shellConfigCombox->setCurrentText(shellMap.key(strShellProgram));
    qCDebug(tsettings) << "Shell options reloaded";
}

int Settings::cursorShape() const
{
    // qCDebug(tsettings) << "Getting cursor shape";
    int shape = settings->option("advanced.cursor.cursor_shape")->value().toInt();
    // qCDebug(tsettings) << "Cursor shape:" << shape;
    return shape;
}

bool Settings::cursorBlink() const
{
    // qCDebug(tsettings) << "Getting cursor blink setting";
    bool blink = settings->option("advanced.cursor.cursor_blink")->value().toBool();
    // qCDebug(tsettings) << "Cursor blink:" << blink;
    return blink;
}

bool Settings::enableSetCursorPosition() const
{
    return settings->option("advanced.cursor.set_cursor_position")->value().toBool();
}

bool Settings::backgroundBlur() const
{
    return settings->option("basic.interface.blurred_background")->value().toBool();
}

int Settings::historySize() const
{
    return settings->option("advanced.scroll.history_size")->value().toInt();
}


QString Settings::colorScheme() const
{
    qCDebug(tsettings) << "Getting color scheme";
    //选择主题未确定
    if(!bSwitchTheme) {
        qCDebug(tsettings) << "Theme switch not confirmed, returning from map";
        return switchThemeMap["basic.interface.theme"];
    }
    QString scheme = settings->option("basic.interface.theme")->value().toString();
    qCDebug(tsettings) << "Color scheme:" << scheme;
    return scheme;
}

void Settings::setColorScheme(const QString &name)
{
    qCDebug(tsettings) << "Setting color scheme to:" << name;
    //选择主题未确定
    if(!bSwitchTheme) {
        qCDebug(tsettings) << "Theme switch not confirmed, storing in map";
        switchThemeMap["basic.interface.theme"] = name;
        return;
    }
    settings->option("basic.interface.theme")->setValue(name);
}

QString Settings::extendColorScheme() const
{
    qCDebug(tsettings) << "Getting extend color scheme";
    //选择主题未确定
    if(!bSwitchTheme) {
        qCDebug(tsettings) << "Theme switch not confirmed, returning from map";
        return switchThemeMap["basic.interface.expand_theme"];
    }
    QString scheme = settings->option("basic.interface.expand_theme")->value().toString();
    qCDebug(tsettings) << "Extend color scheme:" << scheme;
    return scheme;
}

void Settings::setExtendColorScheme(const QString &name)
{
    qCDebug(tsettings) << "Setting extend color scheme to:" << name;
    if(!bSwitchTheme) {
        qCDebug(tsettings) << "Theme switch not confirmed, storing in map";
        switchThemeMap["basic.interface.expand_theme"] = name;
        return;
    }
    settings->option("basic.interface.expand_theme")->setValue(name);
}

QString Settings::wordCharacters() const
{
    return settings->option("advanced.cursor.include_special_characters_in_double_click_selections")->value().toString();
}

/*******************************************************************************
 1. @函数:    setEncoding
 2. @作者:    ut001121 zhangmeng
 3. @日期:    2020-08-11
 4. @说明:    设置界面设置编码
*******************************************************************************/
//void Settings::setEncoding(const QString &name)
//{
//    if (name != m_EncodeName) {
//        m_EncodeName = name;
//        emit encodeSettingChanged(name);
//    }
//}

/*******************************************************************************
 1. @函数:    setKeyValue
 2. @作者:    ut001121 zhangmeng
 3. @日期:    2020-08-11
 4. @说明:    设置界面设置键值
*******************************************************************************/
//void Settings::setKeyValue(const QString &name, const QString &value)
//{
//    settings->option(name)->setValue(value);
//}

bool Settings::IsPasteSelection()
{
    // qCDebug(tsettings) << "Getting paste selection setting";
    bool paste = settings->option("advanced.cursor.auto_copy_selection")->value().toBool();
    // qCDebug(tsettings) << "Paste selection:" << paste;
    return paste;
}

bool Settings::isShortcutConflict(const QString &Name, const QString &Key)
{
    qCDebug(tsettings) << "Checking shortcut conflict for:" << Name << "key:" << Key;
    for (QString &tmpKey : settings->keys()) {
        // 获取设置里面的快捷键键值
        QString strKey = settings->value(tmpKey).toString();
        // 比较前将快捷键都设置为同一级别的键值，然后比较
        // 例ctlr+shift+? => ctrl+shift+/
        if (Utils::converUpToDown(strKey) == Utils::converUpToDown(Key)) {
            if (Name != tmpKey) {
                qCInfo(tsettings) << Name << Key << "is conflict with Settings!" << tmpKey << settings->value(tmpKey);
                return  true;
            }
        }
    }
    qCDebug(tsettings) << "No shortcut conflict found";
    return  false;
}

/******** Add by ut001000 renfeixiang 2020-06-15:增加 每次显示设置界面时，更新设置的等宽字体 Begin***************/
void Settings::handleWidthFont()
{
    qCDebug(tsettings) << "Handling width font";
    FontDataList Whitelist = DBusManager::callAppearanceFont("monospacefont");

    //将新安装的字体，加载到字体库中
    QFontDatabase base;
    for (int i = 0; i < Whitelist.count(); ++i) {
        QString name = Whitelist[i].value;
        if (-1 == comboBox->findData(name)) {
            // qCDebug(tsettings) << "Adding new font to database:" << name;
            QString fontpath =  QDir::homePath() + "/.local/share/fonts/" + name + "/";// + name + ".ttf";
            QDir dir(fontpath);
            if (dir.count() > 2) {
                fontpath = fontpath + dir[2];
            }

            int ret = base.addApplicationFont(fontpath);
            if (-1 == ret)
                qCWarning(tsettings) << "load " << name << " font faild";

        }
    }
    //按name小到大排序
    std::sort(Whitelist.begin(), Whitelist.end(), [ = ](const FontData & str1, const FontData & str2) {
        QCollator qc;
        return qc.compare(str1.value, str2.value) < 0;
    });

    //更新设置界面的字体信息
    QVariant fontname = comboBox->currentData();
    comboBox->clear();
    for(int k = 0; k < Whitelist.count(); k ++) {
        comboBox->addItem(Whitelist[k].value, Whitelist[k].key);
    }
    comboBox->setCurrentIndex(comboBox->findData(fontname));
    qCDebug(tsettings) << "Width font handling completed";
}

bool Settings::disableControlFlow(void)
{
    return settings->option("advanced.shell.disable_ctrl_flow")->value().toBool();
}

bool Settings::enableDebuginfod()
{
    return settings->option("advanced.debuginfod.enable_debuginfod")->value().toBool();
}

QString Settings::debuginfodUrls()
{
    return settings->option("advanced.debuginfod.debuginfod_urls")->value().toString();
}

bool Settings::allowOSC52() const
{
    return settings->option("advanced.cursor.allow_osc52")->value().toBool();
}

/******** Add by ut001000 renfeixiang 2020-06-15:增加 每次显示设置界面时，更新设置的等宽字体 End***************/

/*******************************************************************************
 1. @函数:    getKeyshortcutFromKeymap
 2. @作者:    ut001121 zhangmeng
 3. @日期:    2020-08-11
 4. @说明:    设置界面从键盘映射获取密钥快捷方式
*******************************************************************************/
//QString Settings::getKeyshortcutFromKeymap(const QString &keyCategory, const QString &keyName)
//{
//    return settings->option(QString("shortcuts.%1.%2").arg(keyCategory, keyName))->value().toString();
//}

/******** Modify by n014361 wangpeili 2020-01-04: 创建Combox控件        ***********×****/

QPair<QWidget *, QWidget *> Settings::createFontComBoBoxHandle(QObject *obj)
{
    auto option = qobject_cast<DTK_CORE_NAMESPACE::DSettingsOption *>(obj);

    /******** Modify by ut001000 renfeixiang 2020-06-15:修改 comboBox修改成成员变量，修改DBUS获取失败场景，设置成系统默认等宽字体 Begin***************/
    comboBox = new DComboBox;
    comboBox->setObjectName("SettingsFontFamilyComboBox");//Add by ut001000 renfeixiang 2020-08-14

    QPair<QWidget *, QWidget *> optionWidget =
        DSettingsWidgetFactory::createStandardItem(QByteArray(), option, comboBox);

    FontDataList Whitelist = DBusManager::callAppearanceFont("monospacefont");

    std::sort(Whitelist.begin(), Whitelist.end(), [ = ](const FontData & str1, const FontData & str2) {
        QCollator qc;
        return qc.compare(str1.value, str2.value) < 0;
    });

    qCInfo(tsettings) << "createFontComBoBoxHandle get system monospacefont";
    if (Whitelist.size() <= 0) {
        //一般不会走这个分支，除非DBUS出现问题
        qCInfo(tsettings) << "DBusManager::callAppearanceFont failed, get control font failed.";
        //DBUS获取字体失败后，设置系统默认的等宽字体
        QStringList fontlist;
        fontlist << "Courier 10 Pitch" << "DejaVu Sans Mono" << "Liberation Mono"
                 << "Noto Mono" << "Noto Sans Mono" << "Noto Sans Mono CJK JP"
                 << "Noto Sans Mono CJK KR" << "Noto Sans Mono CJK SC"
                 << "Noto Sans Mono CJK TC";
        Whitelist.appendValues(fontlist);
    }
    for(int k = 0; k < Whitelist.count(); k ++) {
        comboBox->addItem(Whitelist[k].value, Whitelist[k].key);
    }
    /******** Modify by ut001000 renfeixiang 2020-06-15:修改 comboBox修改成成员变量，修改DBUS获取失败场景，设置成系统默认等宽字体 End***************/

    if (option->value().toString().isEmpty())
        option->setValue(QFontDatabase::systemFont(QFontDatabase::FixedFont).family());

    // init.
    comboBox->setCurrentIndex(comboBox->findData(option->value()));

    connect(option, &DSettingsOption::valueChanged, comboBox, [ = ](QVariant var) {
        comboBox->setCurrentIndex(comboBox->findData(var));
    });

    option->connect(
        comboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), option, [ = ](int index) {
        option->setValue(comboBox->itemData(index));
    });

    return optionWidget;
}

QPair<QWidget *, QWidget *> Settings::createCustomSliderHandle(QObject *obj)
{
    auto option = qobject_cast<DTK_CORE_NAMESPACE::DSettingsOption *>(obj);

    DSlider *slider = new DSlider;
    slider->setIconSize(QSize(20, 20));
    slider->setLeftIcon(QIcon::fromTheme("dt_opacity_left"));
    slider->setRightIcon(QIcon::fromTheme("dt_opacity_right"));
    slider->setMaximum(option->data("max").toInt());
    slider->setMinimum(option->data("min").toInt());
    slider->setValue(static_cast<int>(instance()->opacity() * 100));
    //fix bug 65140 焦点在透明度，点击Page up/ down，点击8次即可调节到最前/后，和控制中心不一致
    slider->setPageStep(1);
    slider->slider()->setTickInterval(1);
    QPair<QWidget *, QWidget *> optionWidget = DSettingsWidgetFactory::createStandardItem(QByteArray(), option, slider);

    connect(option, &DSettingsOption::valueChanged, slider, [ = ](QVariant var) {
        slider->setValue(var.toInt());
    });

    option->connect(slider, &DSlider::valueChanged, option, [ = ](QVariant var) {
        option->setValue(var.toInt());
    });

    return optionWidget;
}

QPair<QWidget *, QWidget *> Settings::createValSliderHandle(QObject *obj)
{
    auto option = qobject_cast<DTK_CORE_NAMESPACE::DSettingsOption *>(obj);
    DSlider *slider = new DSlider;
    QStringList valTicksList;
    const int maxVal = option->data("max").toInt();
    const int minVal = option->data("min").toInt();
    const int step = option->data("step").toInt();

    valTicksList << tr("Fast");
    for (int i = 0; i < ((maxVal - minVal) / step - 1); i++)
    {
        valTicksList << "";
    }
    valTicksList << tr("Slow");
    slider->setMaximum(maxVal / step);
    slider->setMinimum(minVal / step);
    slider->setValue(instance()->QuakeDuration() / step);
    slider->slider()->setTickInterval(1);
    slider->setRightTicks(valTicksList);
    slider->setProperty("handleType", "Vernier");
    QPair<QWidget *, QWidget *> optionWidget = DSettingsWidgetFactory::createStandardItem(QByteArray(), option, slider);

    connect(option, &DSettingsOption::valueChanged, slider, [ = ](QVariant var) {
        slider->setValue(var.toInt());
    });

    option->connect(slider, &DSlider::valueChanged, option, [ = ](QVariant var) {
        option->setValue(var.toInt());
    });

    return optionWidget;
}

QPair<QWidget *, QWidget *> Settings::createSpinButtonHandle(QObject *obj)
{
    auto option = qobject_cast<DTK_CORE_NAMESPACE::DSettingsOption *>(obj);
    auto rightWidget = new NewDspinBox();

    // 使用新的setRange方法来设置范围，这将自动更新验证器
    int minValue = option->data("min").toInt();
    int maxValue = option->data("max").toInt();
    rightWidget->setRange(minValue, maxValue);
    rightWidget->setSingleStep(option->data("step").toInt());
    rightWidget->setValue(option->value().toInt());

    QPair<QWidget *, QWidget *> optionWidget =
        DSettingsWidgetFactory::createStandardItem(QByteArray(), option, rightWidget);
    connect(
    option, &DSettingsOption::valueChanged, rightWidget, [ = ](QVariant var) {
        rightWidget->setValue(var.toInt());
    });
    //Add by ut001000 renfeixiang 2020-11-06 使用QSpinBox自带的valueChanged信号
    option->connect(rightWidget, static_cast<void (QSpinBox::*)(int value)>(&QSpinBox::valueChanged),
    option, [ = ](int value) {
        option->setValue(value);
    });

    return optionWidget;
} /********************* Modify by n014361 wangpeili End ************************/

QPair<QWidget *, QWidget *> Settings::createShortcutEditOptionHandle(/*DSettingsWidgetFactoryPrivate *p,*/ QObject *opt)
{
    auto option = qobject_cast<DTK_CORE_NAMESPACE::DSettingsOption *>(opt);
    auto rightWidget = new KeySequenceEdit(option);

    rightWidget->setObjectName("OptionShortcutEdit");
    rightWidget->ShortcutDirection(Qt::AlignLeft);

    auto optionValue = option->value();
    auto translateContext = opt->property(PRIVATE_PROPERTY_translateContext).toByteArray();

    // 控件初始加载配置文件的值
    auto updateWidgetValue = [ = ](const QVariant & optionValue, DTK_CORE_NAMESPACE::DSettingsOption * opt) {
        Q_UNUSED(opt)
        QKeySequence sequence(optionValue.toString());
        QString keyseq = sequence.toString();
        if (keyseq == SHORTCUT_VALUE)
            return;

        rightWidget->setKeySequence(sequence);
    };
    updateWidgetValue(optionValue, option);

    // 控件输入
    option->connect(rightWidget, &KeySequenceEdit::editingFinished, rightWidget, [ = ](const QKeySequence & sequence) {
        // 删除
        if ("Backspace" == sequence.toString()) {
            rightWidget->clear();
            option->setValue(SHORTCUT_VALUE);
            return ;
        }
        // 取消
        if ("Esc" == sequence.toString()) {
            rightWidget->clear();
            rightWidget->setKeySequence(QKeySequence(rightWidget->option()->value().toString()));
            return ;
        }

        QString reason;
        // 有效查询
        if (!ShortcutManager::instance()->checkShortcutValid(rightWidget->option()->key(), sequence.toString(), reason)) {
            if (sequence.toString() != "Esc")
                Service::instance()->showShortcutConflictMsgbox(reason);

            // 界面数据还原
            rightWidget->clear();
            rightWidget->setKeySequence(QKeySequence(rightWidget->option()->value().toString()));
            return ;
        }
        option->setValue(sequence.toString());
    });

    // 配置修改
    option->connect(option, &DTK_CORE_NAMESPACE::DSettingsOption::valueChanged, rightWidget, [ = ](const QVariant & value) {
        QString keyseq = value.toString();
        qCInfo(tsettings) << "Current configuration modification! Config's Key: " << rightWidget->option()->key()
                << "Config's Value: " << keyseq;
        if (SHORTCUT_VALUE == keyseq || keyseq.isEmpty()) {
            rightWidget->clear();
            return;
        }

        rightWidget->setKeySequence(QKeySequence(keyseq));
    });

    return DSettingsWidgetFactory::createStandardItem(translateContext, option, rightWidget);
}

QPair<QWidget *, QWidget *> Settings::createTabTitleFormatOptionHandle(QObject *opt)
{
    return createTabTitleFormatWidget(opt, false);
}

QPair<QWidget *, QWidget *> Settings::createRemoteTabTitleFormatOptionHandle(QObject *opt)
{
    return createTabTitleFormatWidget(opt, true);
}

QPair<QWidget *, QWidget *> Settings::createShellConfigComboxOptionHandle(QObject *obj)
{
    DSettingsOption *option = qobject_cast<DTK_CORE_NAMESPACE::DSettingsOption *>(obj);
    // shell配置框
    g_shellConfigCombox = new DComboBox;
    QPair<QWidget *, QWidget *> optionWidget = DSettingsWidgetFactory::createStandardItem(QByteArray(), option, g_shellConfigCombox);
    // 添加shell配置选项
    addShellOption();

    connect(option, &DSettingsOption::valueChanged, g_shellConfigCombox, [ = ](QVariant var) {
        // 恢复默认值
        if (DEFAULT_SHELL == var.toString()) {
            g_shellConfigCombox->setCurrentText(DEFAULT_SHELL);
            return;
        }
        // 不是默认值的其他情况
        QMap<QString, QString> shellMap = Service::instance()->shellsMap();
        g_shellConfigCombox->setCurrentText(shellMap.key(var.toString()));
    });

    option->connect(g_shellConfigCombox, &DComboBox::currentTextChanged, option, [ = ](const QString & strShell) {
        QMap<QString, QString> shellMap = Service::instance()->shellsMap();
        option->setValue(shellMap[strShell]);
        //fix: bug#68644 shell切换为zsh，再次连接远程管理，跳转到新标签页时远程管理未连接
        if (strShell == "zsh") {
            QString path = QProcessEnvironment::systemEnvironment().value("HOME");
            QFile fi(path + "/" + ".zshrc");
            if (!fi.exists()) {
                fi.open(QIODevice::ReadWrite | QIODevice::Text); //不存在的情况下，打开包含了新建文件的操作
                fi.close();
            }
        }
    });

    QMap<QString, QString> shellMap = Service::instance()->shellsMap();
    // 设置默认值
    g_shellConfigCombox->setCurrentText(shellMap.key(option->value().toString()));

    return optionWidget;
}

void Settings::setFontSize(const int size)
{
    if (size < 5 || size > 50) return;
    settings->option("basic.interface.font_size")->setValue(size);
}

void Settings::setFontName(const QString font)
{
    FontDataList fontList = DBusManager::callAppearanceFont("monospacefont");
   qCDebug(tsettings) << "current font name:" << font << "font size:" << fontList.size();
    for (int k = 0; k < fontList.count(); k++) {
        if (font == fontList[k].key || font == fontList[k].value) {
            settings->option("basic.interface.font")->setValue(font);
            break;
        }
    }
}

void Settings::setOpacity(const int opacity)
{
    if (opacity < 0 || opacity > 100) return;
    settings->option("basic.interface.opacity")->setValue(opacity);
}

void Settings::setConsoleColorScheme(const QString scheme)
{
    if (scheme == "Dark" || scheme == "Light") {
        settings->option("basic.interface.theme")->setValue(scheme);
        settings->option("basic.interface.expand_theme")->setValue("");
    }
}

void Settings::setCursorShape(const int shape)
{
    if (shape < 0 || shape > 2) return;
    settings->option("advanced.cursor.cursor_shape")->setValue(shape);
}

void Settings::setCursorBlink(const bool blink)
{
    settings->option("advanced.cursor.cursor_blink")->setValue(blink);
}

void Settings::setConsoleShell(const QString shellName)
{
    QMap<QString, QString> shellMap = Service::instance()->getShells();
    for (auto itr = shellMap.begin(); itr != shellMap.end(); ++itr) {
        qCDebug(tsettings) <<"Console Shell("<< shellName << ")! key:" <<  itr.key() << "value: " << itr.value();
        if (shellName == itr.key() || shellName == itr.value()) {
            settings->option("advanced.shell.default_shell")->setValue(itr.value());
            break;
        }
    }
}

bool Settings::ScrollWheelZoom()
{
    return settings->option("advanced.scroll.zoom_on_ctrl_scrollwheel")->value().toBool();
}

bool Settings::OpacityCtrlAltScrollWheel()
{
    // 为兼容性保留旧键，优先读取新键（Ctrl+Shift+Up/Down）
    if (auto newOpt = settings->option("advanced.scroll.opacity_on_ctrl_shift_updown")) {
        return newOpt->value().toBool();
    }
    if (auto shiftOpt = settings->option("advanced.scroll.opacity_on_shift_scrollwheel")) {
        return shiftOpt->value().toBool();
    }
    if (auto oldOpt = settings->option("advanced.scroll.opacity_on_ctrl_alt_scrollwheel")) {
        return oldOpt->value().toBool();
    }
    return true; // 默认开启
}

