Skip to content
Snippets Groups Projects
UserSettingsPage.cpp 54.4 KiB
Newer Older
  • Learn to ignore specific revisions
  • Nicolas Werner's avatar
    Nicolas Werner committed
    // SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr>
    // SPDX-FileCopyrightText: 2021 Nheko Contributors
    //
    // SPDX-License-Identifier: GPL-3.0-or-later
    
    #include <QApplication>
    
    #include <QComboBox>
    
    #include <QCoreApplication>
    
    #include <QFontComboBox>
    
    #include <QFormLayout>
    
    #include <QLabel>
    
    #include <QLineEdit>
    #include <QMessageBox>
    
    Nicolas Werner's avatar
    Nicolas Werner committed
    #include <QPainter>
    
    #include <QPushButton>
    
    #include <QScrollArea>
    
    #include <QStandardPaths>
    
    #include <QTextStream>
    
    #include <QtQml>
    
    #include "Config.h"
    
    #include "UserSettingsPage.h"
    
    #include "Utils.h"
    
    #include "encryption/Olm.h"
    
    #include "ui/FlatButton.h"
    #include "ui/ToggleButton.h"
    
    #include "voip/CallDevices.h"
    
    Konstantinos Sideris's avatar
    Konstantinos Sideris committed
    #include "config/nheko.h"
    
    Nicolas Werner's avatar
    Nicolas Werner committed
    QSharedPointer<UserSettings> UserSettings::instance_;
    
    UserSettings::UserSettings()
    {
    
        connect(
          QCoreApplication::instance(), &QCoreApplication::aboutToQuit, []() { instance_.clear(); });
    
    Nicolas Werner's avatar
    Nicolas Werner committed
    }
    
    QSharedPointer<UserSettings>
    UserSettings::instance()
    {
    
        return instance_;
    
    Nicolas Werner's avatar
    Nicolas Werner committed
    }
    
    Nicolas Werner's avatar
    Nicolas Werner committed
    UserSettings::initialize(std::optional<QString> profile)
    {
    
        instance_.reset(new UserSettings());
        instance_->load(profile);
    
    Nicolas Werner's avatar
    Nicolas Werner committed
    }
    
    void
    UserSettings::load(std::optional<QString> profile)
    
        tray_        = settings.value("user/window/tray", false).toBool();
        startInTray_ = settings.value("user/window/start_in_tray", false).toBool();
    
        roomListWidth_      = settings.value("user/sidebar/room_list_width", -1).toInt();
        communityListWidth_ = settings.value("user/sidebar/community_list_width", -1).toInt();
    
        hasDesktopNotifications_ = settings.value("user/desktop_notifications", true).toBool();
        hasAlertOnNotification_  = settings.value("user/alert_on_notification", false).toBool();
        groupView_               = settings.value("user/group_view", true).toBool();
        buttonsInTimeline_       = settings.value("user/timeline/buttons", true).toBool();
        timelineMaxWidth_        = settings.value("user/timeline/max_width", 0).toInt();
        messageHoverHighlight_ =
          settings.value("user/timeline/message_hover_highlight", false).toBool();
        enlargeEmojiOnlyMessages_ =
          settings.value("user/timeline/enlarge_emoji_only_msg", false).toBool();
        markdown_             = settings.value("user/markdown_enabled", true).toBool();
        animateImagesOnHover_ = settings.value("user/animate_images_on_hover", false).toBool();
        typingNotifications_  = settings.value("user/typing_notifications", true).toBool();
        sortByImportance_     = settings.value("user/sort_by_unread", true).toBool();
        readReceipts_         = settings.value("user/read_receipts", true).toBool();
        theme_                = settings.value("user/theme", defaultTheme_).toString();
        font_                 = settings.value("user/font_family", "default").toString();
        avatarCircles_        = settings.value("user/avatar_circles", true).toBool();
        useIdenticon_         = settings.value("user/use_identicon", true).toBool();
        decryptSidebar_       = settings.value("user/decrypt_sidebar", true).toBool();
        privacyScreen_        = settings.value("user/privacy_screen", false).toBool();
        privacyScreenTimeout_ = settings.value("user/privacy_screen_timeout", 0).toInt();
        mobileMode_           = settings.value("user/mobile_mode", false).toBool();
        emojiFont_            = settings.value("user/emoji_font_family", "default").toString();
        baseFontSize_         = settings.value("user/font_size", QFont().pointSizeF()).toDouble();
        auto tempPresence     = settings.value("user/presence", "").toString().toStdString();
        auto presenceValue    = QMetaEnum::fromType<Presence>().keyToValue(tempPresence.c_str());
        if (presenceValue < 0)
            presenceValue = 0;
        presence_               = static_cast<Presence>(presenceValue);
        ringtone_               = settings.value("user/ringtone", "Default").toString();
        microphone_             = settings.value("user/microphone", QString()).toString();
        camera_                 = settings.value("user/camera", QString()).toString();
        cameraResolution_       = settings.value("user/camera_resolution", QString()).toString();
        cameraFrameRate_        = settings.value("user/camera_frame_rate", QString()).toString();
        screenShareFrameRate_   = settings.value("user/screen_share_frame_rate", 5).toInt();
        screenSharePiP_         = settings.value("user/screen_share_pip", true).toBool();
        screenShareRemoteVideo_ = settings.value("user/screen_share_remote_video", false).toBool();
        screenShareHideCursor_  = settings.value("user/screen_share_hide_cursor", false).toBool();
        useStunServer_          = settings.value("user/use_stun_server", false).toBool();
    
        if (profile) // set to "" if it's the default to maintain compatibility
            profile_ = (*profile == "default") ? "" : *profile;
        else
            profile_ = settings.value("user/currentProfile", "").toString();
    
        QString prefix = (profile_ != "" && profile_ != "default") ? "profile/" + profile_ + "/" : "";
        accessToken_   = settings.value(prefix + "auth/access_token", "").toString();
        homeserver_    = settings.value(prefix + "auth/home_server", "").toString();
        userId_        = settings.value(prefix + "auth/user_id", "").toString();
        deviceId_      = settings.value(prefix + "auth/device_id", "").toString();
    
        hiddenTags_    = settings.value(prefix + "user/hidden_tags", QStringList{}).toStringList();
    
        collapsedSpaces_.clear();
        for (const auto &e :
             settings.value(prefix + "user/collapsed_spaces", QList<QVariant>{}).toList())
            collapsedSpaces_.push_back(e.toStringList());
    
    
        shareKeysWithTrustedUsers_ =
          settings.value(prefix + "user/automatically_share_keys_with_trusted_users", false).toBool();
        onlyShareKeysWithVerifiedUsers_ =
          settings.value(prefix + "user/only_share_keys_with_verified_users", false).toBool();
        useOnlineKeyBackup_ = settings.value(prefix + "user/online_key_backup", false).toBool();
    
        disableCertificateValidation_ =
          settings.value("disable_certificate_validation", false).toBool();
    
        applyTheme();
    
    void
    UserSettings::setMessageHoverHighlight(bool state)
    {
    
        if (state == messageHoverHighlight_)
            return;
        messageHoverHighlight_ = state;
        emit messageHoverHighlightChanged(state);
        save();
    
    }
    void
    UserSettings::setEnlargeEmojiOnlyMessages(bool state)
    {
    
        if (state == enlargeEmojiOnlyMessages_)
            return;
        enlargeEmojiOnlyMessages_ = state;
        emit enlargeEmojiOnlyMessagesChanged(state);
        save();
    
        if (state == tray_)
            return;
        tray_ = state;
        emit trayChanged(state);
        save();
    
        if (state == startInTray_)
            return;
        startInTray_ = state;
        emit startInTrayChanged(state);
        save();
    
    void
    UserSettings::setMobileMode(bool state)
    {
    
        if (state == mobileMode_)
            return;
        mobileMode_ = state;
        emit mobileModeChanged(state);
        save();
    
        if (groupView_ == state)
            return;
    
        groupView_ = state;
        emit groupViewStateChanged(state);
        save();
    
    Nicolas Werner's avatar
    Nicolas Werner committed
    void
    UserSettings::setHiddenTags(QStringList hiddenTags)
    {
    
        hiddenTags_ = hiddenTags;
        save();
    
    void
    UserSettings::setCollapsedSpaces(QList<QStringList> spaces)
    {
        collapsedSpaces_ = spaces;
        save();
    }
    
    
    UserSettings::setMarkdown(bool state)
    
        if (state == markdown_)
            return;
        markdown_ = state;
        emit markdownChanged(state);
        save();
    
    void
    UserSettings::setAnimateImagesOnHover(bool state)
    {
    
        if (state == animateImagesOnHover_)
            return;
        animateImagesOnHover_ = state;
        emit animateImagesOnHoverChanged(state);
        save();
    
    void
    UserSettings::setReadReceipts(bool state)
    {
    
        if (state == readReceipts_)
            return;
        readReceipts_ = state;
        emit readReceiptsChanged(state);
        save();
    
    }
    
    void
    UserSettings::setTypingNotifications(bool state)
    {
    
        if (state == typingNotifications_)
            return;
        typingNotifications_ = state;
        emit typingNotificationsChanged(state);
        save();
    
    }
    
    void
    UserSettings::setSortByImportance(bool state)
    {
    
        if (state == sortByImportance_)
            return;
        sortByImportance_ = state;
        emit roomSortingChanged(state);
        save();
    
    }
    
    void
    UserSettings::setButtonsInTimeline(bool state)
    {
    
        if (state == buttonsInTimeline_)
            return;
        buttonsInTimeline_ = state;
        emit buttonInTimelineChanged(state);
        save();
    
        if (state == timelineMaxWidth_)
            return;
        timelineMaxWidth_ = state;
        emit timelineMaxWidthChanged(state);
        save();
    
    Nicolas Werner's avatar
    Nicolas Werner committed
    void
    UserSettings::setCommunityListWidth(int state)
    {
    
        if (state == communityListWidth_)
            return;
        communityListWidth_ = state;
        emit communityListWidthChanged(state);
        save();
    
    Nicolas Werner's avatar
    Nicolas Werner committed
    }
    void
    UserSettings::setRoomListWidth(int state)
    {
    
        if (state == roomListWidth_)
            return;
        roomListWidth_ = state;
        emit roomListWidthChanged(state);
        save();
    
    
    void
    UserSettings::setDesktopNotifications(bool state)
    {
    
        if (state == hasDesktopNotifications_)
            return;
        hasDesktopNotifications_ = state;
        emit desktopNotificationsChanged(state);
        save();
    
    void
    UserSettings::setAlertOnNotification(bool state)
    {
    
        if (state == hasAlertOnNotification_)
            return;
        hasAlertOnNotification_ = state;
        emit alertOnNotificationChanged(state);
        save();
    
    void
    UserSettings::setAvatarCircles(bool state)
    {
    
        if (state == avatarCircles_)
            return;
        avatarCircles_ = state;
        emit avatarCirclesChanged(state);
        save();
    
        if (state == decryptSidebar_)
            return;
        decryptSidebar_ = state;
        emit decryptSidebarChanged(state);
        save();
    
    void
    UserSettings::setPrivacyScreen(bool state)
    {
    
        if (state == privacyScreen_) {
            return;
        }
        privacyScreen_ = state;
        emit privacyScreenChanged(state);
        save();
    
    }
    
    void
    UserSettings::setPrivacyScreenTimeout(int state)
    {
    
        if (state == privacyScreenTimeout_) {
            return;
        }
        privacyScreenTimeout_ = state;
        emit privacyScreenTimeoutChanged(state);
        save();
    
    void
    UserSettings::setFontSize(double size)
    {
    
        if (size == baseFontSize_)
            return;
        baseFontSize_ = size;
        emit fontSizeChanged(size);
        save();
    
    Joe Donofry's avatar
    Joe Donofry committed
    void
    UserSettings::setFontFamily(QString family)
    {
    
        if (family == font_)
            return;
        font_ = family;
        emit fontChanged(family);
        save();
    
    void
    UserSettings::setEmojiFontFamily(QString family)
    {
    
        if (family == emojiFont_)
            return;
    
        if (family == tr("Default")) {
            emojiFont_ = "default";
        } else {
            emojiFont_ = family;
        }
    
        emit emojiFontChanged(family);
        save();
    
    void
    UserSettings::setPresence(Presence state)
    {
    
        if (state == presence_)
            return;
        presence_ = state;
        emit presenceChanged(state);
        save();
    
    void
    UserSettings::setTheme(QString theme)
    {
    
        if (theme == theme_)
            return;
        theme_ = theme;
        save();
        applyTheme();
        emit themeChanged(theme);
    
    trilene's avatar
    trilene committed
    void
    UserSettings::setUseStunServer(bool useStunServer)
    {
    
        if (useStunServer == useStunServer_)
            return;
        useStunServer_ = useStunServer;
        emit useStunServerChanged(useStunServer);
        save();
    
    trilene's avatar
    trilene committed
    }
    
    
    void
    UserSettings::setOnlyShareKeysWithVerifiedUsers(bool shareKeys)
    {
    
        if (shareKeys == onlyShareKeysWithVerifiedUsers_)
            return;
    
        onlyShareKeysWithVerifiedUsers_ = shareKeys;
        emit onlyShareKeysWithVerifiedUsersChanged(shareKeys);
        save();
    
    void
    UserSettings::setShareKeysWithTrustedUsers(bool shareKeys)
    {
    
        if (shareKeys == shareKeysWithTrustedUsers_)
            return;
    
        shareKeysWithTrustedUsers_ = shareKeys;
        emit shareKeysWithTrustedUsersChanged(shareKeys);
        save();
    
    void
    UserSettings::setUseOnlineKeyBackup(bool useBackup)
    {
    
        if (useBackup == useOnlineKeyBackup_)
            return;
    
        useOnlineKeyBackup_ = useBackup;
        emit useOnlineKeyBackupChanged(useBackup);
        save();
    
    void
    UserSettings::setRingtone(QString ringtone)
    {
    
        if (ringtone == ringtone_)
            return;
        ringtone_ = ringtone;
        emit ringtoneChanged(ringtone);
        save();
    
    void
    
    trilene's avatar
    trilene committed
    UserSettings::setMicrophone(QString microphone)
    
        if (microphone == microphone_)
            return;
        microphone_ = microphone;
        emit microphoneChanged(microphone);
        save();
    
    trilene's avatar
    trilene committed
    }
    
    void
    UserSettings::setCamera(QString camera)
    {
    
        if (camera == camera_)
            return;
        camera_ = camera;
        emit cameraChanged(camera);
        save();
    
    trilene's avatar
    trilene committed
    }
    
    void
    UserSettings::setCameraResolution(QString resolution)
    {
    
        if (resolution == cameraResolution_)
            return;
        cameraResolution_ = resolution;
        emit cameraResolutionChanged(resolution);
        save();
    
    trilene's avatar
    trilene committed
    }
    
    void
    UserSettings::setCameraFrameRate(QString frameRate)
    {
    
        if (frameRate == cameraFrameRate_)
            return;
        cameraFrameRate_ = frameRate;
        emit cameraFrameRateChanged(frameRate);
        save();
    
    void
    UserSettings::setScreenShareFrameRate(int frameRate)
    {
    
        if (frameRate == screenShareFrameRate_)
            return;
        screenShareFrameRate_ = frameRate;
        emit screenShareFrameRateChanged(frameRate);
        save();
    
    void
    UserSettings::setScreenSharePiP(bool state)
    {
    
        if (state == screenSharePiP_)
            return;
        screenSharePiP_ = state;
        emit screenSharePiPChanged(state);
        save();
    
    void
    UserSettings::setScreenShareRemoteVideo(bool state)
    {
    
        if (state == screenShareRemoteVideo_)
            return;
        screenShareRemoteVideo_ = state;
        emit screenShareRemoteVideoChanged(state);
        save();
    
    void
    UserSettings::setScreenShareHideCursor(bool state)
    {
    
        if (state == screenShareHideCursor_)
            return;
        screenShareHideCursor_ = state;
        emit screenShareHideCursorChanged(state);
        save();
    
    void
    UserSettings::setProfile(QString profile)
    {
    
        if (profile == profile_)
            return;
        profile_ = profile;
        emit profileChanged(profile_);
        save();
    
    }
    
    void
    UserSettings::setUserId(QString userId)
    {
    
        if (userId == userId_)
            return;
        userId_ = userId;
        emit userIdChanged(userId_);
        save();
    
    }
    
    void
    UserSettings::setAccessToken(QString accessToken)
    {
    
        if (accessToken == accessToken_)
            return;
        accessToken_ = accessToken;
        emit accessTokenChanged(accessToken_);
        save();
    
    }
    
    void
    UserSettings::setDeviceId(QString deviceId)
    {
    
        if (deviceId == deviceId_)
            return;
        deviceId_ = deviceId;
        emit deviceIdChanged(deviceId_);
        save();
    
    }
    
    void
    UserSettings::setHomeserver(QString homeserver)
    {
    
        if (homeserver == homeserver_)
            return;
        homeserver_ = homeserver;
        emit homeserverChanged(homeserver_);
        save();
    
    void
    UserSettings::setDisableCertificateValidation(bool disabled)
    {
    
        if (disabled == disableCertificateValidation_)
            return;
        disableCertificateValidation_ = disabled;
        http::client()->verify_certificates(!disabled);
        emit disableCertificateValidationChanged(disabled);
    
    Loren Burkholder's avatar
    Loren Burkholder committed
    }
    
    void
    UserSettings::setUseIdenticon(bool state)
    {
    
        if (state == useIdenticon_)
            return;
        useIdenticon_ = state;
        emit useIdenticonChanged(useIdenticon_);
        save();
    
    void
    UserSettings::applyTheme()
    {
    
        QFile stylefile;
    
        if (this->theme() == "light") {
            stylefile.setFileName(":/styles/styles/nheko.qss");
        } else if (this->theme() == "dark") {
            stylefile.setFileName(":/styles/styles/nheko-dark.qss");
        } else {
            stylefile.setFileName(":/styles/styles/system.qss");
        }
        QApplication::setPalette(Theme::paletteFromTheme(this->theme().toStdString()));
    
        stylefile.open(QFile::ReadOnly);
        QString stylesheet = QString(stylefile.readAll());
    
        qobject_cast<QApplication *>(QApplication::instance())->setStyleSheet(stylesheet);
    
    }
    
    void
    UserSettings::save()
    {
    
        settings.beginGroup("user");
    
        settings.beginGroup("window");
        settings.setValue("tray", tray_);
        settings.setValue("start_in_tray", startInTray_);
        settings.endGroup(); // window
    
        settings.beginGroup("sidebar");
        settings.setValue("community_list_width", communityListWidth_);
        settings.setValue("room_list_width", roomListWidth_);
        settings.endGroup(); // window
    
        settings.beginGroup("timeline");
        settings.setValue("buttons", buttonsInTimeline_);
        settings.setValue("message_hover_highlight", messageHoverHighlight_);
        settings.setValue("enlarge_emoji_only_msg", enlargeEmojiOnlyMessages_);
        settings.setValue("max_width", timelineMaxWidth_);
        settings.endGroup(); // timeline
    
        settings.setValue("avatar_circles", avatarCircles_);
        settings.setValue("decrypt_sidebar", decryptSidebar_);
        settings.setValue("privacy_screen", privacyScreen_);
        settings.setValue("privacy_screen_timeout", privacyScreenTimeout_);
        settings.setValue("mobile_mode", mobileMode_);
        settings.setValue("font_size", baseFontSize_);
        settings.setValue("typing_notifications", typingNotifications_);
        settings.setValue("sort_by_unread", sortByImportance_);
        settings.setValue("minor_events", sortByImportance_);
        settings.setValue("read_receipts", readReceipts_);
        settings.setValue("group_view", groupView_);
        settings.setValue("markdown_enabled", markdown_);
        settings.setValue("animate_images_on_hover", animateImagesOnHover_);
        settings.setValue("desktop_notifications", hasDesktopNotifications_);
        settings.setValue("alert_on_notification", hasAlertOnNotification_);
        settings.setValue("theme", theme());
        settings.setValue("font_family", font_);
        settings.setValue("emoji_font_family", emojiFont_);
        settings.setValue(
          "presence",
          QString::fromUtf8(QMetaEnum::fromType<Presence>().valueToKey(static_cast<int>(presence_))));
        settings.setValue("ringtone", ringtone_);
        settings.setValue("microphone", microphone_);
        settings.setValue("camera", camera_);
        settings.setValue("camera_resolution", cameraResolution_);
        settings.setValue("camera_frame_rate", cameraFrameRate_);
        settings.setValue("screen_share_frame_rate", screenShareFrameRate_);
        settings.setValue("screen_share_pip", screenSharePiP_);
        settings.setValue("screen_share_remote_video", screenShareRemoteVideo_);
        settings.setValue("screen_share_hide_cursor", screenShareHideCursor_);
        settings.setValue("use_stun_server", useStunServer_);
        settings.setValue("currentProfile", profile_);
        settings.setValue("use_identicon", useIdenticon_);
    
        settings.endGroup(); // user
    
        QString prefix = (profile_ != "" && profile_ != "default") ? "profile/" + profile_ + "/" : "";
        settings.setValue(prefix + "auth/access_token", accessToken_);
        settings.setValue(prefix + "auth/home_server", homeserver_);
        settings.setValue(prefix + "auth/user_id", userId_);
        settings.setValue(prefix + "auth/device_id", deviceId_);
    
        settings.setValue(prefix + "user/automatically_share_keys_with_trusted_users",
                          shareKeysWithTrustedUsers_);
        settings.setValue(prefix + "user/only_share_keys_with_verified_users",
                          onlyShareKeysWithVerifiedUsers_);
        settings.setValue(prefix + "user/online_key_backup", useOnlineKeyBackup_);
    
        settings.setValue(prefix + "user/hidden_tags", hiddenTags_);
    
        QVariantList v;
        for (const auto &e : collapsedSpaces_)
            v.push_back(e);
        settings.setValue(prefix + "user/collapsed_spaces", v);
    
    
        settings.setValue("disable_certificate_validation", disableCertificateValidation_);
    
        settings.sync();
    
    }
    
    HorizontalLine::HorizontalLine(QWidget *parent)
    
      : QFrame{parent}
    
        setFrameShape(QFrame::HLine);
        setFrameShadow(QFrame::Sunken);
    
    }
    
    UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidget *parent)
    
      : QWidget{parent}
      , settings_{settings}
    
        topLayout_ = new QVBoxLayout{this};
    
        QIcon icon;
    
        icon.addFile(":/icons/icons/ui/angle-arrow-left.svg");
    
    
        auto backBtn_ = new FlatButton{this};
        backBtn_->setMinimumSize(QSize(24, 24));
        backBtn_->setIcon(icon);
        backBtn_->setIconSize(QSize(24, 24));
    
        QFont font;
        font.setPointSizeF(font.pointSizeF() * 1.1);
    
        auto versionInfo = new QLabel(QString("%1 | %2").arg(nheko::version).arg(nheko::build_os));
        if (QCoreApplication::applicationName() != "nheko")
            versionInfo->setText(versionInfo->text() + " | " +
                                 tr("profile: %1").arg(QCoreApplication::applicationName()));
        versionInfo->setTextInteractionFlags(Qt::TextBrowserInteraction);
    
        topBarLayout_ = new QHBoxLayout;
        topBarLayout_->setSpacing(0);
        topBarLayout_->setMargin(0);
        topBarLayout_->addWidget(backBtn_, 1, Qt::AlignLeft | Qt::AlignVCenter);
        topBarLayout_->addStretch(1);
    
        formLayout_ = new QFormLayout;
    
        formLayout_->setLabelAlignment(Qt::AlignLeft);
        formLayout_->setFormAlignment(Qt::AlignRight);
        formLayout_->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
        formLayout_->setRowWrapPolicy(QFormLayout::WrapLongRows);
        formLayout_->setHorizontalSpacing(0);
    
        auto general_ = new QLabel{tr("GENERAL"), this};
        general_->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
        general_->setFont(font);
    
        trayToggle_                     = new Toggle{this};
        startInTrayToggle_              = new Toggle{this};
        avatarCircles_                  = new Toggle{this};
        useIdenticon_                   = new Toggle{this};
        decryptSidebar_                 = new Toggle(this);
        privacyScreen_                  = new Toggle{this};
        onlyShareKeysWithVerifiedUsers_ = new Toggle(this);
        shareKeysWithTrustedUsers_      = new Toggle(this);
        useOnlineKeyBackup_             = new Toggle(this);
        groupViewToggle_                = new Toggle{this};
        timelineButtonsToggle_          = new Toggle{this};
        typingNotifications_            = new Toggle{this};
        messageHoverHighlight_          = new Toggle{this};
        enlargeEmojiOnlyMessages_       = new Toggle{this};
        sortByImportance_               = new Toggle{this};
        readReceipts_                   = new Toggle{this};
        markdown_                       = new Toggle{this};
        animateImagesOnHover_           = new Toggle{this};
        desktopNotifications_           = new Toggle{this};
        alertOnNotification_            = new Toggle{this};
        useStunServer_                  = new Toggle{this};
        mobileMode_                     = new Toggle{this};
        scaleFactorCombo_               = new QComboBox{this};
        fontSizeCombo_                  = new QComboBox{this};
        fontSelectionCombo_             = new QFontComboBox{this};
        emojiFontSelectionCombo_        = new QComboBox{this};
        ringtoneCombo_                  = new QComboBox{this};
        microphoneCombo_                = new QComboBox{this};
        cameraCombo_                    = new QComboBox{this};
        cameraResolutionCombo_          = new QComboBox{this};
        cameraFrameRateCombo_           = new QComboBox{this};
        timelineMaxWidthSpin_           = new QSpinBox{this};
        privacyScreenTimeout_           = new QSpinBox{this};
    
        trayToggle_->setChecked(settings_->tray());
        startInTrayToggle_->setChecked(settings_->startInTray());
        avatarCircles_->setChecked(settings_->avatarCircles());
        useIdenticon_->setChecked(settings_->useIdenticon());
        decryptSidebar_->setChecked(settings_->decryptSidebar());
        privacyScreen_->setChecked(settings_->privacyScreen());
        onlyShareKeysWithVerifiedUsers_->setChecked(settings_->onlyShareKeysWithVerifiedUsers());
        shareKeysWithTrustedUsers_->setChecked(settings_->shareKeysWithTrustedUsers());
        useOnlineKeyBackup_->setChecked(settings_->useOnlineKeyBackup());
        groupViewToggle_->setChecked(settings_->groupView());
        timelineButtonsToggle_->setChecked(settings_->buttonsInTimeline());
        typingNotifications_->setChecked(settings_->typingNotifications());
        messageHoverHighlight_->setChecked(settings_->messageHoverHighlight());
        enlargeEmojiOnlyMessages_->setChecked(settings_->enlargeEmojiOnlyMessages());
        sortByImportance_->setChecked(settings_->sortByImportance());
        readReceipts_->setChecked(settings_->readReceipts());
        markdown_->setChecked(settings_->markdown());
        animateImagesOnHover_->setChecked(settings_->animateImagesOnHover());
        desktopNotifications_->setChecked(settings_->hasDesktopNotifications());
        alertOnNotification_->setChecked(settings_->hasAlertOnNotification());
        useStunServer_->setChecked(settings_->useStunServer());
        mobileMode_->setChecked(settings_->mobileMode());
    
        if (!settings_->tray()) {
            startInTrayToggle_->setState(false);
            startInTrayToggle_->setDisabled(true);
        }
    
        if (!settings_->privacyScreen()) {
            privacyScreenTimeout_->setDisabled(true);
        }
    
        avatarCircles_->setFixedSize(64, 48);
    
        auto uiLabel_ = new QLabel{tr("INTERFACE"), this};
        uiLabel_->setFixedHeight(uiLabel_->minimumHeight() + LayoutTopMargin);
        uiLabel_->setAlignment(Qt::AlignBottom);
        uiLabel_->setFont(font);
    
        for (double option = 1; option <= 3; option += 0.25)
            scaleFactorCombo_->addItem(QString::number(option));
        for (double option = 6; option <= 24; option += 0.5)
            fontSizeCombo_->addItem(QString("%1 ").arg(QString::number(option)));
    
        QFontDatabase fontDb;
    
        // TODO: Is there a way to limit to just emojis, rather than
        // all emoji fonts?
        auto emojiFamilies = fontDb.families(QFontDatabase::Symbol);
        emojiFontSelectionCombo_->addItem(tr("Default"));
        for (const auto &family : emojiFamilies) {
            emojiFontSelectionCombo_->addItem(family);
        }
    
        QString currentFont = settings_->font();
        if (currentFont != "default" || currentFont != "") {
            fontSelectionCombo_->setCurrentIndex(fontSelectionCombo_->findText(currentFont));
        }
    
        emojiFontSelectionCombo_->setCurrentIndex(
          emojiFontSelectionCombo_->findText(settings_->emojiFont()));
    
        themeCombo_ = new QComboBox{this};
        themeCombo_->addItem("Light");
        themeCombo_->addItem("Dark");
        themeCombo_->addItem("System");
    
        QString themeStr = settings_->theme();
        themeStr.replace(0, 1, themeStr[0].toUpper());
        int themeIndex = themeCombo_->findText(themeStr);
        themeCombo_->setCurrentIndex(themeIndex);
    
        timelineMaxWidthSpin_->setMinimum(0);
        timelineMaxWidthSpin_->setMaximum(100'000'000);
        timelineMaxWidthSpin_->setSingleStep(10);
    
        privacyScreenTimeout_->setMinimum(0);
        privacyScreenTimeout_->setMaximum(3600);
        privacyScreenTimeout_->setSingleStep(10);
    
        auto callsLabel = new QLabel{tr("CALLS"), this};
        callsLabel->setFixedHeight(callsLabel->minimumHeight() + LayoutTopMargin);
        callsLabel->setAlignment(Qt::AlignBottom);
        callsLabel->setFont(font);
    
        auto encryptionLabel_ = new QLabel{tr("ENCRYPTION"), this};
        encryptionLabel_->setFixedHeight(encryptionLabel_->minimumHeight() + LayoutTopMargin);
        encryptionLabel_->setAlignment(Qt::AlignBottom);
        encryptionLabel_->setFont(font);
    
        QFont monospaceFont;
        monospaceFont.setFamily("Monospace");
        monospaceFont.setStyleHint(QFont::Monospace);
        monospaceFont.setPointSizeF(monospaceFont.pointSizeF() * 0.9);
    
        deviceIdValue_ = new QLabel{this};
        deviceIdValue_->setTextInteractionFlags(Qt::TextSelectableByMouse);
        deviceIdValue_->setFont(monospaceFont);
    
        deviceFingerprintValue_ = new QLabel{this};
        deviceFingerprintValue_->setTextInteractionFlags(Qt::TextSelectableByMouse);
        deviceFingerprintValue_->setFont(monospaceFont);
    
        deviceFingerprintValue_->setText(utils::humanReadableFingerprint(QString(44, 'X')));
    
        backupSecretCached      = new QLabel{this};
        masterSecretCached      = new QLabel{this};
        selfSigningSecretCached = new QLabel{this};
        userSigningSecretCached = new QLabel{this};
        backupSecretCached->setFont(monospaceFont);
        masterSecretCached->setFont(monospaceFont);
        selfSigningSecretCached->setFont(monospaceFont);
        userSigningSecretCached->setFont(monospaceFont);
    
        auto sessionKeysLabel = new QLabel{tr("Session Keys"), this};
        sessionKeysLabel->setFont(font);
        sessionKeysLabel->setMargin(OptionMargin);
    
        auto sessionKeysImportBtn = new QPushButton{tr("IMPORT"), this};
        auto sessionKeysExportBtn = new QPushButton{tr("EXPORT"), this};
    
        auto sessionKeysLayout = new QHBoxLayout;
        sessionKeysLayout->addWidget(new QLabel{"", this}, 1, Qt::AlignRight);
        sessionKeysLayout->addWidget(sessionKeysExportBtn, 0, Qt::AlignRight);
        sessionKeysLayout->addWidget(sessionKeysImportBtn, 0, Qt::AlignRight);
    
        auto crossSigningKeysLabel = new QLabel{tr("Cross Signing Keys"), this};
        crossSigningKeysLabel->setFont(font);
        crossSigningKeysLabel->setMargin(OptionMargin);
    
        auto crossSigningRequestBtn  = new QPushButton{tr("REQUEST"), this};
        auto crossSigningDownloadBtn = new QPushButton{tr("DOWNLOAD"), this};
    
        auto crossSigningKeysLayout = new QHBoxLayout;
        crossSigningKeysLayout->addWidget(new QLabel{"", this}, 1, Qt::AlignRight);
        crossSigningKeysLayout->addWidget(crossSigningRequestBtn, 0, Qt::AlignRight);
        crossSigningKeysLayout->addWidget(crossSigningDownloadBtn, 0, Qt::AlignRight);
    
        auto boxWrap = [this, &font](QString labelText, QWidget *field, QString tooltipText = "") {
            auto label = new QLabel{labelText, this};
            label->setFont(font);
            label->setMargin(OptionMargin);
    
            if (!tooltipText.isEmpty()) {
                label->setToolTip(tooltipText);
    
            auto layout = new QHBoxLayout;
            layout->addWidget(field, 0, Qt::AlignRight);
    
            formLayout_->addRow(label, layout);
        };
    
        formLayout_->addRow(general_);
        formLayout_->addRow(new HorizontalLine{this});
        boxWrap(tr("Minimize to tray"),
                trayToggle_,
                tr("Keep the application running in the background after closing the client window."));
        boxWrap(tr("Start in tray"),
                startInTrayToggle_,
                tr("Start the application in the background without showing the client window."));
        formLayout_->addRow(new HorizontalLine{this});
        boxWrap(tr("Circular Avatars"),
                avatarCircles_,
                tr("Change the appearance of user avatars in chats.\nOFF - square, ON - Circle."));
        boxWrap(tr("Use identicons"),
                useIdenticon_,
    
    Nicolas Werner's avatar
    Nicolas Werner committed
                tr("Display an identicon instead of a letter when no avatar is set."));
    
        boxWrap(tr("Group's sidebar"),
                groupViewToggle_,
                tr("Show a column containing groups and tags next to the room list."));
        boxWrap(tr("Decrypt messages in sidebar"),
                decryptSidebar_,
                tr("Decrypt the messages shown in the sidebar.\nOnly affects messages in "
                   "encrypted chats."));
        boxWrap(tr("Privacy Screen"),
                privacyScreen_,
                tr("When the window loses focus, the timeline will\nbe blurred."));
        boxWrap(tr("Privacy screen timeout (in seconds [0 - 3600])"),
                privacyScreenTimeout_,
                tr("Set timeout (in seconds) for how long after window loses\nfocus before the screen"
                   " will be blurred.\nSet to 0 to blur immediately after focus loss. Max value of 1 "
                   "hour (3600 seconds)"));
        boxWrap(tr("Show buttons in timeline"),
                timelineButtonsToggle_,
                tr("Show buttons to quickly reply, react or access additional options next to each "
                   "message."));
        boxWrap(tr("Limit width of timeline"),
                timelineMaxWidthSpin_,
                tr("Set the max width of messages in the timeline (in pixels). This can help "
                   "readability on wide screen, when Nheko is maximised"));
        boxWrap(tr("Typing notifications"),
                typingNotifications_,
                tr("Show who is typing in a room.\nThis will also enable or disable sending typing "
                   "notifications to others."));
        boxWrap(
          tr("Sort rooms by unreads"),
          sortByImportance_,