#include "settingsview.h" #include "services/preferencesservice.h" #include "services/startupbehaviorservice.h" #include "services/translationservice.h" #include "services/aiactionoptionsservice.h" #include "db/dao/common_structs.h" #include "db/dao/categorydao.h" #include "db/dao/ruledao.h" #include "db/dao/templatedao.h" #include "db/dao/signaturedao.h" #include "ui/ruleditordialog.h" #include "ui/templateeditordialog.h" #include "ui/signaturemanagerdialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include SettingsView::SettingsView(QWidget *parent) : QWidget(parent) { setupUI(); } void SettingsView::setupUI() { QVBoxLayout *mainLayout = new QVBoxLayout(this); mainLayout->setContentsMargins(0, 0, 0, 0); m_tabWidget = new QTabWidget(); m_tabWidget->addTab(createAccountsTab(), tr("Accounts")); m_tabWidget->addTab(createPreferencesTab(), tr("Preferences")); m_tabWidget->addTab(createCategoriesTab(), tr("Categories")); m_tabWidget->addTab(createRulesTab(), tr("Rules")); m_tabWidget->addTab(createTemplatesTab(), tr("Templates")); m_tabWidget->addTab(createSignaturesTab(), tr("Signatures")); m_tabWidget->addTab(createGeneralTab(), tr("General")); m_tabWidget->addTab(createAppearanceTab(), tr("Appearance")); mainLayout->addWidget(m_tabWidget); } QWidget* SettingsView::createAccountsTab() { QWidget *widget = new QWidget(); QVBoxLayout *layout = new QVBoxLayout(widget); layout->setContentsMargins(20, 20, 20, 20); QLabel *title = new QLabel(tr("Email Accounts")); QFont titleFont = title->font(); titleFont.setPointSize(16); titleFont.setBold(true); title->setFont(titleFont); layout->addWidget(title); m_accountList = new QListWidget(); m_accountList->setSelectionMode(QAbstractItemView::SingleSelection); m_accountList->setStyleSheet( "QListWidget { background: #ffffff; border: 1px solid #e0e0e0; border-radius: 4px; }" "QListWidget::item { padding: 12px; border-bottom: 1px solid #f0f0f0; }" "QListWidget::item:selected { background: #e3f2fd; }" ); layout->addWidget(m_accountList); QHBoxLayout *buttonLayout = new QHBoxLayout(); m_addBtn = new QPushButton(tr("Add Account")); m_addBtn->setStyleSheet( "QPushButton { background: #1976D2; color: white; border: none; border-radius: 4px; " "padding: 8px 16px; font-weight: bold; }" "QPushButton:hover { background: #1565C0; }" ); buttonLayout->addWidget(m_addBtn); m_editBtn = new QPushButton(tr("Edit")); m_editBtn->setEnabled(false); m_editBtn->setStyleSheet( "QPushButton { background: #FFA726; color: white; border: none; border-radius: 4px; " "padding: 8px 16px; font-weight: bold; }" "QPushButton:hover { background: #FB8C00; }" ); m_deleteBtn = new QPushButton(tr("Delete")); m_deleteBtn->setEnabled(false); m_deleteBtn->setStyleSheet( "QPushButton { background: #EF5350; color: white; border: none; border-radius: 4px; " "padding: 8px 16px; font-weight: bold; }" "QPushButton:hover { background: #E53935; }" ); buttonLayout->addWidget(m_editBtn); buttonLayout->addWidget(m_deleteBtn); buttonLayout->addStretch(); layout->addLayout(buttonLayout); connect(m_accountList, &QListWidget::itemSelectionChanged, this, &SettingsView::onAccountSelectionChanged); connect(m_editBtn, &QPushButton::clicked, this, &SettingsView::onEditClicked); connect(m_deleteBtn, &QPushButton::clicked, this, &SettingsView::onDeleteClicked); connect(m_addBtn, &QPushButton::clicked, this, &SettingsView::accountAddRequested); return widget; } QWidget* SettingsView::createCategoriesTab() { QWidget *widget = new QWidget(); QVBoxLayout *layout = new QVBoxLayout(widget); layout->setContentsMargins(12, 12, 12, 12); layout->setSpacing(8); QLabel *title = new QLabel(tr("Categories")); QFont titleFont = title->font(); titleFont.setPointSize(16); titleFont.setBold(true); title->setFont(titleFont); layout->addWidget(title); m_categoryTree = new QTreeWidget(); m_categoryTree->setHeaderLabels({tr("Category"), tr("Color"), tr("Account")}); m_categoryTree->setSelectionMode(QAbstractItemView::SingleSelection); m_categoryTree->setRootIsDecorated(true); m_categoryTree->setItemsExpandable(true); m_categoryTree->setStyleSheet( "QTreeWidget { background: #ffffff; border: 1px solid #e0e0e0; border-radius: 4px; }" "QTreeWidget::item { padding: 8px; border-bottom: 1px solid #f0f0f0; }" "QTreeWidget::item:selected { background: #e3f2fd; }" ); m_categoryTree->setColumnWidth(0, 250); m_categoryTree->setColumnWidth(1, 80); m_categoryTree->header()->setStretchLastSection(false); layout->addWidget(m_categoryTree); QHBoxLayout *buttonLayout = new QHBoxLayout(); m_categoryAddBtn = new QPushButton(tr("New Category")); m_categoryAddBtn->setStyleSheet( "QPushButton { background: #1976D2; color: white; border: none; border-radius: 4px; " "padding: 8px 16px; font-weight: bold; }" "QPushButton:hover { background: #1565C0; }" ); m_categoryEditBtn = new QPushButton(tr("Edit")); m_categoryEditBtn->setEnabled(false); m_categoryEditBtn->setStyleSheet( "QPushButton { background: #FFA726; color: white; border: none; border-radius: 4px; " "padding: 8px 16px; font-weight: bold; }" "QPushButton:hover { background: #FB8C00; }" ); m_categoryDeleteBtn = new QPushButton(tr("Delete")); m_categoryDeleteBtn->setEnabled(false); m_categoryDeleteBtn->setStyleSheet( "QPushButton { background: #EF5350; color: white; border: none; border-radius: 4px; " "padding: 8px 16px; font-weight: bold; }" "QPushButton:hover { background: #E53935; }" ); buttonLayout->addWidget(m_categoryAddBtn); buttonLayout->addWidget(m_categoryEditBtn); buttonLayout->addWidget(m_categoryDeleteBtn); buttonLayout->addStretch(); layout->addLayout(buttonLayout); connect(m_categoryTree, &QTreeWidget::itemSelectionChanged, this, &SettingsView::onCategorySelectionChanged); connect(m_categoryAddBtn, &QPushButton::clicked, this, &SettingsView::onCategoryAddClicked); connect(m_categoryEditBtn, &QPushButton::clicked, this, &SettingsView::onCategoryEditClicked); connect(m_categoryDeleteBtn, &QPushButton::clicked, this, &SettingsView::onCategoryDeleteClicked); return widget; } QWidget* SettingsView::createRulesTab() { QWidget *widget = new QWidget(); QVBoxLayout *layout = new QVBoxLayout(widget); layout->setContentsMargins(12, 12, 12, 12); layout->setSpacing(8); QLabel *title = new QLabel(tr("Rules")); QFont titleFont = title->font(); titleFont.setPointSize(16); titleFont.setBold(true); title->setFont(titleFont); layout->addWidget(title); m_ruleList = new QListWidget(); m_ruleList->setSelectionMode(QAbstractItemView::SingleSelection); m_ruleList->setStyleSheet( "QListWidget { background: #ffffff; border: 1px solid #e0e0e0; border-radius: 4px; }" "QListWidget::item { padding: 12px; border-bottom: 1px solid #f0f0f0; }" "QListWidget::item:selected { background: #e3f2fd; }" ); m_ruleList->setSpacing(2); layout->addWidget(m_ruleList); QHBoxLayout *buttonLayout = new QHBoxLayout(); m_ruleAddBtn = new QPushButton(tr("New Rule")); m_ruleAddBtn->setStyleSheet( "QPushButton { background: #1976D2; color: white; border: none; border-radius: 4px; " "padding: 8px 16px; font-weight: bold; }" "QPushButton:hover { background: #1565C0; }" ); m_ruleEditBtn = new QPushButton(tr("Edit")); m_ruleEditBtn->setEnabled(false); m_ruleEditBtn->setStyleSheet( "QPushButton { background: #FFA726; color: white; border: none; border-radius: 4px; " "padding: 8px 16px; font-weight: bold; }" "QPushButton:hover { background: #FB8C00; }" ); m_ruleDeleteBtn = new QPushButton(tr("Delete")); m_ruleDeleteBtn->setEnabled(false); m_ruleDeleteBtn->setStyleSheet( "QPushButton { background: #EF5350; color: white; border: none; border-radius: 4px; " "padding: 8px 16px; font-weight: bold; }" "QPushButton:hover { background: #E53935; }" ); m_ruleToggleBtn = new QPushButton(tr("Toggle")); m_ruleToggleBtn->setEnabled(false); m_ruleToggleBtn->setStyleSheet( "QPushButton { background: #66BB6A; color: white; border: none; border-radius: 4px; " "padding: 8px 16px; font-weight: bold; }" "QPushButton:hover { background: #4CAF50; }" ); buttonLayout->addWidget(m_ruleAddBtn); buttonLayout->addWidget(m_ruleEditBtn); buttonLayout->addWidget(m_ruleDeleteBtn); buttonLayout->addWidget(m_ruleToggleBtn); buttonLayout->addStretch(); layout->addLayout(buttonLayout); connect(m_ruleList, &QListWidget::itemSelectionChanged, this, &SettingsView::onRuleSelectionChanged); connect(m_ruleAddBtn, &QPushButton::clicked, this, &SettingsView::onRuleAddClicked); connect(m_ruleEditBtn, &QPushButton::clicked, this, &SettingsView::onRuleEditClicked); connect(m_ruleDeleteBtn, &QPushButton::clicked, this, &SettingsView::onRuleDeleteClicked); connect(m_ruleToggleBtn, &QPushButton::clicked, this, &SettingsView::onRuleToggleEnabled); return widget; } QWidget* SettingsView::createTemplatesTab() { QWidget *widget = new QWidget(); QVBoxLayout *layout = new QVBoxLayout(widget); layout->setContentsMargins(12, 12, 12, 12); layout->setSpacing(8); QLabel *title = new QLabel(tr("Templates")); QFont titleFont = title->font(); titleFont.setPointSize(16); titleFont.setBold(true); title->setFont(titleFont); layout->addWidget(title); m_templateList = new QListWidget(); m_templateList->setSelectionMode(QAbstractItemView::SingleSelection); m_templateList->setStyleSheet( "QListWidget { background: #ffffff; border: 1px solid #e0e0e0; border-radius: 4px; }" "QListWidget::item { padding: 12px; border-bottom: 1px solid #f0f0f0; }" "QListWidget::item:selected { background: #e3f2fd; }" ); m_templateList->setSpacing(2); layout->addWidget(m_templateList); QHBoxLayout *buttonLayout = new QHBoxLayout(); m_templateAddBtn = new QPushButton(tr("New Template")); m_templateAddBtn->setStyleSheet( "QPushButton { background: #1976D2; color: white; border: none; border-radius: 4px; " "padding: 8px 16px; font-weight: bold; }" "QPushButton:hover { background: #1565C0; }" ); m_templateEditBtn = new QPushButton(tr("Edit")); m_templateEditBtn->setEnabled(false); m_templateEditBtn->setStyleSheet( "QPushButton { background: #FFA726; color: white; border: none; border-radius: 4px; " "padding: 8px 16px; font-weight: bold; }" "QPushButton:hover { background: #FB8C00; }" ); m_templateDeleteBtn = new QPushButton(tr("Delete")); m_templateDeleteBtn->setEnabled(false); m_templateDeleteBtn->setStyleSheet( "QPushButton { background: #EF5350; color: white; border: none; border-radius: 4px; " "padding: 8px 16px; font-weight: bold; }" "QPushButton:hover { background: #E53935; }" ); buttonLayout->addWidget(m_templateAddBtn); buttonLayout->addWidget(m_templateEditBtn); buttonLayout->addWidget(m_templateDeleteBtn); buttonLayout->addStretch(); layout->addLayout(buttonLayout); connect(m_templateList, &QListWidget::itemSelectionChanged, this, &SettingsView::onTemplateSelectionChanged); connect(m_templateAddBtn, &QPushButton::clicked, this, &SettingsView::onTemplateAddClicked); connect(m_templateEditBtn, &QPushButton::clicked, this, &SettingsView::onTemplateEditClicked); connect(m_templateDeleteBtn, &QPushButton::clicked, this, &SettingsView::onTemplateDeleteClicked); return widget; } QWidget* SettingsView::createSignaturesTab() { QWidget *widget = new QWidget(); QVBoxLayout *layout = new QVBoxLayout(widget); layout->setContentsMargins(12, 12, 12, 12); layout->setSpacing(8); QLabel *title = new QLabel(tr("Signatures")); QFont titleFont = title->font(); titleFont.setPointSize(16); titleFont.setBold(true); title->setFont(titleFont); layout->addWidget(title); m_signatureList = new QListWidget(); m_signatureList->setSelectionMode(QAbstractItemView::SingleSelection); m_signatureList->setStyleSheet( "QListWidget { background: #ffffff; border: 1px solid #e0e0e0; border-radius: 4px; }" "QListWidget::item { padding: 12px; border-bottom: 1px solid #f0f0f0; }" "QListWidget::item:selected { background: #e3f2fd; }" ); m_signatureList->setSpacing(2); layout->addWidget(m_signatureList); QHBoxLayout *buttonLayout = new QHBoxLayout(); m_signatureAddBtn = new QPushButton(tr("New Signature")); m_signatureAddBtn->setStyleSheet( "QPushButton { background: #1976D2; color: white; border: none; border-radius: 4px; " "padding: 8px 16px; font-weight: bold; }" "QPushButton:hover { background: #1565C0; }" ); m_signatureEditBtn = new QPushButton(tr("Edit")); m_signatureEditBtn->setEnabled(false); m_signatureEditBtn->setStyleSheet( "QPushButton { background: #FFA726; color: white; border: none; border-radius: 4px; " "padding: 8px 16px; font-weight: bold; }" "QPushButton:hover { background: #FB8C00; }" ); m_signatureDeleteBtn = new QPushButton(tr("Delete")); m_signatureDeleteBtn->setEnabled(false); m_signatureDeleteBtn->setStyleSheet( "QPushButton { background: #EF5350; color: white; border: none; border-radius: 4px; " "padding: 8px 16px; font-weight: bold; }" "QPushButton:hover { background: #E53935; }" ); buttonLayout->addWidget(m_signatureAddBtn); buttonLayout->addWidget(m_signatureEditBtn); buttonLayout->addWidget(m_signatureDeleteBtn); buttonLayout->addStretch(); layout->addLayout(buttonLayout); connect(m_signatureList, &QListWidget::itemSelectionChanged, this, &SettingsView::onSignatureSelectionChanged); connect(m_signatureAddBtn, &QPushButton::clicked, this, &SettingsView::onSignatureAddClicked); connect(m_signatureEditBtn, &QPushButton::clicked, this, &SettingsView::onSignatureEditClicked); connect(m_signatureDeleteBtn, &QPushButton::clicked, this, &SettingsView::onSignatureDeleteClicked); return widget; } QWidget* SettingsView::createGeneralTab() { QWidget *widget = new QWidget(); QVBoxLayout *layout = new QVBoxLayout(widget); layout->setContentsMargins(20, 20, 20, 20); QLabel *title = new QLabel(tr("General Settings")); QFont titleFont = title->font(); titleFont.setPointSize(16); titleFont.setBold(true); title->setFont(titleFont); layout->addWidget(title); QCheckBox *startOnLogin = new QCheckBox(tr("Start application on login")); startOnLogin->setChecked(true); connect(startOnLogin, &QCheckBox::toggled, this, [this](bool checked) { emit settingChanged("start_on_login", QVariant(checked)); }); layout->addWidget(startOnLogin); QCheckBox *enableNotifications = new QCheckBox(tr("Enable notifications")); enableNotifications->setChecked(true); connect(enableNotifications, &QCheckBox::toggled, this, [this](bool checked) { emit settingChanged("enable_notifications", QVariant(checked)); }); layout->addWidget(enableNotifications); QCheckBox *minimizeToTray = new QCheckBox(tr("Minimize to tray on close")); minimizeToTray->setChecked(true); connect(minimizeToTray, &QCheckBox::toggled, this, [this](bool checked) { emit settingChanged("minimize_to_tray", QVariant(checked)); }); layout->addWidget(minimizeToTray); layout->addStretch(); return widget; } QWidget* SettingsView::createAppearanceTab() { QWidget *widget = new QWidget(); QVBoxLayout *layout = new QVBoxLayout(widget); layout->setContentsMargins(20, 20, 20, 20); QLabel *title = new QLabel(tr("Appearance")); QFont titleFont = title->font(); titleFont.setPointSize(16); titleFont.setBold(true); title->setFont(titleFont); layout->addWidget(title); QLabel *themeLabel = new QLabel(tr("Theme")); QFont labelFont = themeLabel->font(); labelFont.setBold(true); themeLabel->setFont(labelFont); layout->addWidget(themeLabel); m_themeGroup = new QButtonGroup(this); QRadioButton *lightRadio = new QRadioButton(tr("Light")); QRadioButton *darkRadio = new QRadioButton(tr("Dark")); QRadioButton *systemRadio = new QRadioButton(tr("System")); m_themeGroup->addButton(lightRadio, 0); m_themeGroup->addButton(darkRadio, 1); m_themeGroup->addButton(systemRadio, 2); QHBoxLayout *themeLayout = new QHBoxLayout(); themeLayout->addWidget(lightRadio); themeLayout->addWidget(darkRadio); themeLayout->addWidget(systemRadio); layout->addLayout(themeLayout); connect(m_themeGroup, QOverload::of(&QButtonGroup::idClicked), this, [this](int id) { QString theme; switch (id) { case 0: theme = "light"; break; case 1: theme = "dark"; break; case 2: theme = "system"; break; } emit themeChanged(theme); }); QCheckBox *deepinTheme = new QCheckBox(tr("Deepin theme (experimental)")); deepinTheme->setChecked(true); connect(deepinTheme, &QCheckBox::toggled, this, [this](bool checked) { emit settingChanged("deepin_theme", QVariant(checked)); }); layout->addWidget(deepinTheme); layout->addStretch(); return widget; } QWidget* SettingsView::createPreferencesTab() { QWidget *widget = new QWidget(); QVBoxLayout *layout = new QVBoxLayout(widget); layout->setContentsMargins(20, 20, 20, 20); layout->setSpacing(16); QLabel *title = new QLabel(tr("Preferences")); QFont titleFont = title->font(); titleFont.setPointSize(16); titleFont.setBold(true); title->setFont(titleFont); layout->addWidget(title); // Search Mode QGroupBox *searchGroup = new QGroupBox(tr("Search")); QFormLayout *searchLayout = new QFormLayout(searchGroup); m_searchModeCombo = new QComboBox(); m_searchModeCombo->addItem(tr("Local"), QVariant(static_cast(SearchMode::Local))); m_searchModeCombo->addItem(tr("Online"), QVariant(static_cast(SearchMode::Online))); if (m_preferencesService) { m_searchModeCombo->setCurrentIndex(static_cast(m_preferencesService->defaultSearchMode())); } connect(m_searchModeCombo, QOverload::of(&QComboBox::currentIndexChanged), this, [this](int index) { if (m_preferencesService) { m_preferencesService->setDefaultSearchMode(static_cast(m_searchModeCombo->itemData(index).toInt())); } }); searchLayout->addRow(tr("Default search mode:"), m_searchModeCombo); layout->addWidget(searchGroup); // Default App Mode QGroupBox *appModeGroup = new QGroupBox(tr("Default Application Mode")); QFormLayout *appModeLayout = new QFormLayout(appModeGroup); m_defaultAppModeCombo = new QComboBox(); m_defaultAppModeCombo->addItem(tr("Mail"), QVariant(static_cast(WinoApplicationMode::Mail))); m_defaultAppModeCombo->addItem(tr("Calendar"), QVariant(static_cast(WinoApplicationMode::Calendar))); m_defaultAppModeCombo->addItem(tr("Contacts"), QVariant(static_cast(WinoApplicationMode::Contacts))); m_defaultAppModeCombo->addItem(tr("Settings"), QVariant(static_cast(WinoApplicationMode::Settings))); if (m_preferencesService) { m_defaultAppModeCombo->setCurrentIndex(static_cast(m_preferencesService->defaultApplicationMode())); } connect(m_defaultAppModeCombo, QOverload::of(&QComboBox::currentIndexChanged), this, [this](int index) { if (m_preferencesService) { m_preferencesService->setDefaultApplicationMode(static_cast(m_defaultAppModeCombo->itemData(index).toInt())); } }); appModeLayout->addRow(tr("Default mode on startup:"), m_defaultAppModeCombo); layout->addWidget(appModeGroup); // Language QGroupBox *languageGroup = new QGroupBox(tr("Language")); QFormLayout *languageLayout = new QFormLayout(languageGroup); m_languageCombo = new QComboBox(); if (m_translationService) { QVector languages = m_translationService->getAvailableLanguages(); for (const AppLanguageModel &lang : languages) { m_languageCombo->addItem(lang.displayName, lang.language); } int idx = m_languageCombo->findData(m_translationService->currentLanguage()); if (idx >= 0) m_languageCombo->setCurrentIndex(idx); } connect(m_languageCombo, QOverload::of(&QComboBox::currentIndexChanged), this, [this](int index) { if (m_translationService) { m_translationService->setLanguage(m_languageCombo->itemData(index).toString()); } }); languageLayout->addRow(tr("Interface language:"), m_languageCombo); layout->addWidget(languageGroup); // Email Sync Interval QGroupBox *syncGroup = new QGroupBox(tr("Email Synchronization")); QFormLayout *syncLayout = new QFormLayout(syncGroup); m_syncIntervalSpin = new QSpinBox(); m_syncIntervalSpin->setRange(1, 1440); // 1 minute to 24 hours m_syncIntervalSpin->setSuffix(tr(" minutes")); if (m_preferencesService) { m_syncIntervalSpin->setValue(m_preferencesService->emailSyncIntervalMinutes()); } connect(m_syncIntervalSpin, QOverload::of(&QSpinBox::valueChanged), this, [this](int value) { if (m_preferencesService) { m_preferencesService->setEmailSyncIntervalMinutes(value); } }); syncLayout->addRow(tr("Sync interval:"), m_syncIntervalSpin); layout->addWidget(syncGroup); // Startup Behavior QGroupBox *startupGroup = new QGroupBox(tr("Startup")); QFormLayout *startupLayout = new QFormLayout(startupGroup); m_startOnLoginCheck = new QCheckBox(tr("Start application on login")); if (m_preferencesService) { m_startOnLoginCheck->setChecked(m_preferencesService->startOnLogin()); } connect(m_startOnLoginCheck, &QCheckBox::toggled, this, [this](bool checked) { if (m_preferencesService) { m_preferencesService->setStartOnLogin(checked); } if (m_startupBehaviorService) { m_startupBehaviorService->toggleStartupBehavior(checked); } }); startupLayout->addRow(m_startOnLoginCheck); m_minimizeToTrayCheck = new QCheckBox(tr("Minimize to tray on close")); if (m_preferencesService) { m_minimizeToTrayCheck->setChecked(m_preferencesService->minimizeToTray()); } connect(m_minimizeToTrayCheck, &QCheckBox::toggled, this, [this](bool checked) { if (m_preferencesService) { m_preferencesService->setMinimizeToTray(checked); } }); startupLayout->addRow(m_minimizeToTrayCheck); m_enableNotificationsCheck = new QCheckBox(tr("Enable notifications")); if (m_preferencesService) { m_enableNotificationsCheck->setChecked(m_preferencesService->enableNotifications()); } connect(m_enableNotificationsCheck, &QCheckBox::toggled, this, [this](bool checked) { if (m_preferencesService) { m_preferencesService->setEnableNotifications(checked); } }); startupLayout->addRow(m_enableNotificationsCheck); layout->addWidget(startupGroup); // AI Translation QGroupBox *aiGroup = new QGroupBox(tr("AI Translation")); QFormLayout *aiLayout = new QFormLayout(aiGroup); m_translateLanguageCombo = new QComboBox(); if (m_aiActionOptionsService) { QVector options = m_aiActionOptionsService->getTranslateLanguageOptions(); for (const AiTranslateLanguageOption &opt : options) { m_translateLanguageCombo->addItem(opt.displayName, opt.code); } if (m_preferencesService) { int idx = m_translateLanguageCombo->findData(m_preferencesService->aiDefaultTranslationLanguageCode()); if (idx >= 0) m_translateLanguageCombo->setCurrentIndex(idx); } } connect(m_translateLanguageCombo, QOverload::of(&QComboBox::currentIndexChanged), this, [this](int index) { if (m_preferencesService) { m_preferencesService->setAiDefaultTranslationLanguageCode(m_translateLanguageCombo->itemData(index).toString()); } }); aiLayout->addRow(tr("Default translation language:"), m_translateLanguageCombo); m_summarizeLanguageCombo = new QComboBox(); if (m_aiActionOptionsService) { QVector options = m_aiActionOptionsService->getTranslateLanguageOptions(); for (const AiTranslateLanguageOption &opt : options) { m_summarizeLanguageCombo->addItem(opt.displayName, opt.code); } if (m_preferencesService) { int idx = m_summarizeLanguageCombo->findData(m_preferencesService->aiSummarizeLanguageCode()); if (idx >= 0) m_summarizeLanguageCombo->setCurrentIndex(idx); } } connect(m_summarizeLanguageCombo, QOverload::of(&QComboBox::currentIndexChanged), this, [this](int index) { if (m_preferencesService) { m_preferencesService->setAiSummarizeLanguageCode(m_summarizeLanguageCombo->itemData(index).toString()); } }); aiLayout->addRow(tr("Summarize language:"), m_summarizeLanguageCombo); m_summarySavePathEdit = new QLineEdit(); m_summarySavePathEdit->setPlaceholderText(tr("Select folder for AI summaries...")); if (m_preferencesService) { m_summarySavePathEdit->setText(m_preferencesService->aiSummarySavePath()); } m_summarySavePathBtn = new QPushButton(tr("Browse...")); connect(m_summarySavePathBtn, &QPushButton::clicked, this, [this]() { QString path = QFileDialog::getExistingDirectory(this, tr("Select Summary Save Folder"), m_summarySavePathEdit->text()); if (!path.isEmpty()) { m_summarySavePathEdit->setText(path); if (m_preferencesService) { m_preferencesService->setAiSummarySavePath(path); } } }); QHBoxLayout *pathLayout = new QHBoxLayout(); pathLayout->addWidget(m_summarySavePathEdit); pathLayout->addWidget(m_summarySavePathBtn); aiLayout->addRow(tr("Summary save path:"), pathLayout); layout->addWidget(aiGroup); layout->addStretch(); return widget; } void SettingsView::setAccountService(AccountService *service) { m_accountService = service; if (m_accountService) { loadAccounts(); connect(m_accountService, &AccountService::accountListChanged, this, &SettingsView::onAccountListChanged); } } void SettingsView::setPreferencesService(PreferencesService *service) { m_preferencesService = service; } void SettingsView::setStartupBehaviorService(StartupBehaviorService *service) { m_startupBehaviorService = service; } void SettingsView::setTranslationService(TranslationService *service) { m_translationService = service; } void SettingsView::setAiActionOptionsService(AiActionOptionsService *service) { m_aiActionOptionsService = service; } void SettingsView::onAccountListChanged() { loadAccounts(); } void SettingsView::loadAccounts() { if (!m_accountService) { qDebug() << "AccountService not set"; return; } m_accountList->clear(); QList accounts = m_accountService->getAllAccounts(); qDebug() << "Loaded" << accounts.size() << "accounts from AccountService"; for (const Account &acc : accounts) { QString display = acc.displayName().isEmpty() ? acc.email() : acc.displayName(); QString text = QString("%1 (%2)").arg(acc.email(), display); QListWidgetItem *item = new QListWidgetItem(text, m_accountList); item->setData(Qt::UserRole, acc.id()); m_accountList->addItem(item); } if (m_accountList->count() > 0) { m_accountList->setCurrentRow(0); } } void SettingsView::onAccountSelectionChanged() { QList items = m_accountList->selectedItems(); qDebug() << "SettingsView::onAccountSelectionChanged, selected items count:" << items.size(); if (items.isEmpty()) { m_selectedAccountId = -1; m_editBtn->setEnabled(false); m_deleteBtn->setEnabled(false); return; } QListWidgetItem *item = items.first(); m_selectedAccountId = item->data(Qt::UserRole).toInt(); qDebug() << "SettingsView::onAccountSelectionChanged, selected account id:" << m_selectedAccountId; m_editBtn->setEnabled(true); m_deleteBtn->setEnabled(true); } void SettingsView::onEditClicked() { qDebug() << "SettingsView::onEditClicked called, selected account id:" << m_selectedAccountId; if (m_selectedAccountId != -1 && m_accountService) { emit accountEditRequested(m_selectedAccountId); } } void SettingsView::onDeleteClicked() { if (m_selectedAccountId != -1 && m_accountService) { QMessageBox msgBox(this); msgBox.setWindowTitle(tr("Delete Account")); Account* account = m_accountService->findAccountById(m_selectedAccountId); QString accountName = account ? account->email() : QString::number(m_selectedAccountId); msgBox.setText(tr("Are you sure you want to delete the account \"%1\"?").arg(accountName)); msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); msgBox.setDefaultButton(QMessageBox::No); if (msgBox.exec() == QMessageBox::Yes) { emit accountDeleteRequested(m_selectedAccountId); } } } // ========== Category Slots ========== void SettingsView::onCategorySelectionChanged() { bool hasSelection = !m_categoryTree->selectedItems().isEmpty(); m_categoryEditBtn->setEnabled(hasSelection); m_categoryDeleteBtn->setEnabled(hasSelection); } void SettingsView::onCategoryAddClicked() { QDialog dlg(this); dlg.setWindowTitle(tr("New Category")); dlg.setMinimumWidth(400); QVBoxLayout *layout = new QVBoxLayout(&dlg); QFormLayout *form = new QFormLayout(); QLineEdit *nameEdit = new QLineEdit(); form->addRow(tr("Name:"), nameEdit); QPushButton *colorBtn = new QPushButton(); colorBtn->setFixedSize(24, 24); QColor catColor = QColor("#1976D2"); colorBtn->setStyleSheet(QString("background-color: %1; border: 1px solid #ccc; border-radius: 4px;").arg(catColor.name())); connect(colorBtn, &QPushButton::clicked, [&catColor, colorBtn]() { QColor c = QColorDialog::getColor(catColor); if (c.isValid()) { catColor = c; colorBtn->setStyleSheet(QString("background-color: %1; border: 1px solid #ccc; border-radius: 4px;").arg(c.name())); } }); QHBoxLayout *colorLayout = new QHBoxLayout(); colorLayout->addWidget(colorBtn); colorLayout->addStretch(); form->addRow(tr("Color:"), colorLayout); QComboBox *parentCombo = new QComboBox(); parentCombo->addItem(tr("(None - Top Level)"), QVariant()); form->addRow(tr("Parent:"), parentCombo); layout->addLayout(form); QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); connect(buttons, &QDialogButtonBox::accepted, &dlg, &QDialog::accept); connect(buttons, &QDialogButtonBox::rejected, &dlg, &QDialog::reject); layout->addWidget(buttons); if (dlg.exec() == QDialog::Accepted) { Category cat; cat.name = nameEdit->text().trimmed(); cat.color = catColor; cat.accountId = -1; // Global by default cat.parentCategoryId = parentCombo->currentData().toLongLong(); if (!cat.name.isEmpty()) { emit categoryAddRequested(cat); } } } void SettingsView::onCategoryEditClicked() { QTreeWidgetItem *item = m_categoryTree->currentItem(); if (!item) return; qint64 catId = item->data(0, Qt::UserRole).toLongLong(); std::optional catOpt = CategoryDao::findById(catId); if (!catOpt.has_value()) return; Category cat = *catOpt; QDialog dlg(this); dlg.setWindowTitle(tr("Edit Category")); dlg.setMinimumWidth(400); QVBoxLayout *layout = new QVBoxLayout(&dlg); QFormLayout *form = new QFormLayout(); QLineEdit *nameEdit = new QLineEdit(cat.name); form->addRow(tr("Name:"), nameEdit); QPushButton *colorBtn = new QPushButton(); colorBtn->setFixedSize(24, 24); colorBtn->setStyleSheet(QString("background-color: %1; border: 1px solid #ccc; border-radius: 4px;").arg(cat.color.name())); QColor catColor = cat.color; connect(colorBtn, &QPushButton::clicked, [&catColor, colorBtn]() { QColor c = QColorDialog::getColor(catColor); if (c.isValid()) { catColor = c; colorBtn->setStyleSheet(QString("background-color: %1; border: 1px solid #ccc; border-radius: 4px;").arg(c.name())); } }); QHBoxLayout *colorLayout = new QHBoxLayout(); colorLayout->addWidget(colorBtn); colorLayout->addStretch(); form->addRow(tr("Color:"), colorLayout); layout->addLayout(form); QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); connect(buttons, &QDialogButtonBox::accepted, &dlg, &QDialog::accept); connect(buttons, &QDialogButtonBox::rejected, &dlg, &QDialog::reject); layout->addWidget(buttons); if (dlg.exec() == QDialog::Accepted) { cat.name = nameEdit->text().trimmed(); cat.color = catColor; if (!cat.name.isEmpty()) { emit categoryEditRequested(cat); } } } void SettingsView::onCategoryDeleteClicked() { QTreeWidgetItem *item = m_categoryTree->currentItem(); if (!item) return; qint64 catId = item->data(0, Qt::UserRole).toLongLong(); QMessageBox msgBox(this); msgBox.setWindowTitle(tr("Delete Category")); msgBox.setText(tr("Are you sure you want to delete this category?")); msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); msgBox.setDefaultButton(QMessageBox::No); if (msgBox.exec() == QMessageBox::Yes) { emit categoryDeleteRequested(catId); } } void SettingsView::loadCategories() { refreshCategoryTree(); } void SettingsView::refreshCategoryTree() { m_categoryTree->clear(); // Load global categories QVector globalCats = CategoryDao::findGlobal(); for (const Category &cat : globalCats) { QTreeWidgetItem *item = createCategoryTreeItem(cat); m_categoryTree->addTopLevelItem(item); } // Load account-specific categories for selected account if (m_selectedAccountId > 0) { QVector accountCats = CategoryDao::findByAccount(m_selectedAccountId); for (const Category &cat : accountCats) { QTreeWidgetItem *item = createCategoryTreeItem(cat); m_categoryTree->addTopLevelItem(item); } } m_categoryTree->expandAll(); } QTreeWidgetItem* SettingsView::createCategoryTreeItem(const Category &cat, QTreeWidgetItem *parent) { QTreeWidgetItem *item = parent ? new QTreeWidgetItem(parent) : new QTreeWidgetItem(); item->setText(0, cat.name); item->setData(0, Qt::UserRole, cat.id); // Color indicator QTableWidgetItem *colorItem = new QTableWidgetItem(); colorItem->setBackground(cat.color); colorItem->setFlags(colorItem->flags() & ~Qt::ItemIsEditable); // Account info QString accountInfo = cat.accountId >= 0 ? QString("Account %1").arg(cat.accountId) : tr("Global"); return item; } // ========== Rule Slots ========== void SettingsView::onRuleSelectionChanged() { bool hasSelection = m_ruleList->currentRow() >= 0; m_ruleEditBtn->setEnabled(hasSelection); m_ruleDeleteBtn->setEnabled(hasSelection); m_ruleToggleBtn->setEnabled(hasSelection); } void SettingsView::onRuleAddClicked() { RuleEditorDialog dlg(Rule(), -1, this); connect(&dlg, &RuleEditorDialog::ruleSaved, this, [this](const Rule& rule) { emit ruleAddRequested(rule); }); dlg.exec(); } void SettingsView::onRuleEditClicked() { int row = m_ruleList->currentRow(); if (row < 0) return; QListWidgetItem *item = m_ruleList->item(row); qint64 ruleId = item->data(Qt::UserRole).toLongLong(); std::optional ruleOpt = RuleDao::findById(ruleId); if (!ruleOpt.has_value()) return; RuleEditorDialog dlg(*ruleOpt, -1, this); connect(&dlg, &RuleEditorDialog::ruleSaved, this, [this](const Rule& rule) { emit ruleEditRequested(rule); }); dlg.exec(); } void SettingsView::onRuleDeleteClicked() { int row = m_ruleList->currentRow(); if (row < 0) return; QListWidgetItem *item = m_ruleList->item(row); qint64 ruleId = item->data(Qt::UserRole).toLongLong(); QMessageBox msgBox(this); msgBox.setWindowTitle(tr("Delete Rule")); msgBox.setText(tr("Are you sure you want to delete this rule?")); msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); msgBox.setDefaultButton(QMessageBox::No); if (msgBox.exec() == QMessageBox::Yes) { emit ruleDeleteRequested(ruleId); } } void SettingsView::onRuleToggleEnabled() { int row = m_ruleList->currentRow(); if (row < 0) return; QListWidgetItem *item = m_ruleList->item(row); qint64 ruleId = item->data(Qt::UserRole).toLongLong(); std::optional ruleOpt = RuleDao::findById(ruleId); if (!ruleOpt.has_value()) return; Rule rule = *ruleOpt; rule.enabled = !rule.enabled; emit ruleEditRequested(rule); } void SettingsView::loadRules() { m_ruleList->clear(); // Load global rules QVector globalRules = RuleDao::findGlobal(); for (const Rule &rule : globalRules) { QListWidgetItem *item = new QListWidgetItem(ruleToString(rule)); item->setData(Qt::UserRole, rule.id); if (!rule.enabled) { item->setForeground(Qt::gray); } m_ruleList->addItem(item); } // Load account-specific rules for selected account if (m_selectedAccountId > 0) { QVector accountRules = RuleDao::findByAccount(m_selectedAccountId); for (const Rule &rule : accountRules) { QListWidgetItem *item = new QListWidgetItem(ruleToString(rule)); item->setData(Qt::UserRole, rule.id); if (!rule.enabled) { item->setForeground(Qt::gray); } m_ruleList->addItem(item); } } } QString SettingsView::ruleToString(const Rule &rule) const { QString status = rule.enabled ? "●" : "○"; return QString("%1 %2 (Priority: %3)").arg(status, rule.name, QString::number(rule.priority)); } // ========== Template Slots ========== void SettingsView::onTemplateSelectionChanged() { bool hasSelection = m_templateList->currentRow() >= 0; m_templateEditBtn->setEnabled(hasSelection); m_templateDeleteBtn->setEnabled(hasSelection); } void SettingsView::onTemplateAddClicked() { TemplateEditorDialog dlg(Template(), -1, this); connect(&dlg, &TemplateEditorDialog::templateSaved, this, [this](const Template& tmpl) { emit templateAddRequested(tmpl); }); dlg.exec(); } void SettingsView::onTemplateEditClicked() { int row = m_templateList->currentRow(); if (row < 0) return; QListWidgetItem *item = m_templateList->item(row); qint64 tmplId = item->data(Qt::UserRole).toLongLong(); std::optional