1 // For license of this file, see <project-root-folder>/LICENSE.md.
2 
3 #include "gui/settings/settingsgui.h"
4 
5 #include "core/feedsmodel.h"
6 #include "gui/dialogs/formmain.h"
7 #include "gui/feedmessageviewer.h"
8 #include "gui/systemtrayicon.h"
9 #include "gui/tabwidget.h"
10 #include "gui/toolbars/feedstoolbar.h"
11 #include "gui/toolbars/messagestoolbar.h"
12 #include "gui/toolbars/statusbar.h"
13 #include "miscellaneous/application.h"
14 #include "miscellaneous/iconfactory.h"
15 #include "miscellaneous/settings.h"
16 
17 #include <QDropEvent>
18 #include <QStyleFactory>
19 
SettingsGui(Settings * settings,QWidget * parent)20 SettingsGui::SettingsGui(Settings* settings, QWidget* parent) : SettingsPanel(settings, parent), m_ui(new Ui::SettingsGui) {
21   m_ui->setupUi(this);
22   m_ui->m_editorMessagesToolbar->activeItemsWidget()->viewport()->installEventFilter(this);
23   m_ui->m_editorFeedsToolbar->activeItemsWidget()->viewport()->installEventFilter(this);
24   m_ui->m_editorMessagesToolbar->availableItemsWidget()->viewport()->installEventFilter(this);
25   m_ui->m_editorFeedsToolbar->availableItemsWidget()->viewport()->installEventFilter(this);
26   m_ui->m_treeSkins->setColumnCount(3);
27   m_ui->m_treeSkins->setHeaderHidden(false);
28   m_ui->m_treeSkins->setHeaderLabels(QStringList()
29                                      << /*: Skin list name column. */ tr("Name")
30                                      << /*: Version column of skin list. */ tr("Version")
31                                      << tr("Author"));
32 
33   // Setup skins.
34   m_ui->m_treeSkins->header()->setSectionResizeMode(0, QHeaderView::ResizeMode::ResizeToContents);
35   m_ui->m_treeSkins->header()->setSectionResizeMode(1, QHeaderView::ResizeMode::ResizeToContents);
36   m_ui->m_treeSkins->header()->setSectionResizeMode(2, QHeaderView::ResizeMode::ResizeToContents);
37 
38   connect(m_ui->m_cmbIconTheme, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &SettingsGui::requireRestart);
39   connect(m_ui->m_cmbIconTheme, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
40           &SettingsGui::dirtifySettings);
41   connect(m_ui->m_treeSkins, &QTreeWidget::currentItemChanged, this, &SettingsGui::dirtifySettings);
42   connect(m_ui->m_grpTray, &QGroupBox::toggled, this, &SettingsGui::dirtifySettings);
43   connect(m_ui->m_checkHidden, &QCheckBox::toggled, this, &SettingsGui::dirtifySettings);
44   connect(m_ui->m_checkMonochromeIcons, &QCheckBox::toggled, this, &SettingsGui::dirtifySettings);
45   connect(m_ui->m_checkCountUnreadMessages, &QCheckBox::toggled, this, &SettingsGui::dirtifySettings);
46   connect(m_ui->m_checkHideWhenMinimized, &QCheckBox::toggled, this, &SettingsGui::dirtifySettings);
47   connect(m_ui->m_checkHideTabBarIfOneTabVisible, &QCheckBox::toggled, this, &SettingsGui::dirtifySettings);
48   connect(m_ui->m_checkCloseTabsDoubleClick, &QCheckBox::toggled, this, &SettingsGui::dirtifySettings);
49   connect(m_ui->m_checkCloseTabsMiddleClick, &QCheckBox::toggled, this, &SettingsGui::dirtifySettings);
50   connect(m_ui->m_checkNewTabDoubleClick, &QCheckBox::toggled, this, &SettingsGui::dirtifySettings);
51   connect(m_ui->m_grbCloseTabs, &QGroupBox::toggled, this, &SettingsGui::dirtifySettings);
52   connect(m_ui->m_cmbToolbarButtonStyle, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
53           &SettingsGui::dirtifySettings);
54   connect(m_ui->m_editorFeedsToolbar, &ToolBarEditor::setupChanged, this, &SettingsGui::dirtifySettings);
55   connect(m_ui->m_editorMessagesToolbar, &ToolBarEditor::setupChanged, this, &SettingsGui::dirtifySettings);
56   connect(m_ui->m_editorStatusbar, &ToolBarEditor::setupChanged, this, &SettingsGui::dirtifySettings);
57   connect(m_ui->m_editorStatusbar, &ToolBarEditor::setupChanged, this, &SettingsGui::requireRestart);
58   connect(m_ui->m_cmbStyles, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &SettingsGui::dirtifySettings);
59   connect(m_ui->m_cmbSelectToolBar, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), m_ui->m_stackedToolbars,
60           &QStackedWidget::setCurrentIndex);
61 }
62 
~SettingsGui()63 SettingsGui::~SettingsGui() {
64   delete m_ui;
65 }
66 
eventFilter(QObject * obj,QEvent * e)67 bool SettingsGui::eventFilter(QObject* obj, QEvent* e) {
68   Q_UNUSED(obj)
69 
70   if (e->type() == QEvent::Type::Drop) {
71     auto* drop_event = static_cast<QDropEvent*>(e);
72 
73     if (drop_event->keyboardModifiers() != Qt::KeyboardModifier::NoModifier) {
74       drop_event->setDropAction(Qt::DropAction::MoveAction);
75     }
76   }
77 
78   return false;
79 }
80 
loadSettings()81 void SettingsGui::loadSettings() {
82   onBeginLoadSettings();
83 
84   // Load settings of tray icon.
85   m_ui->m_grpTray->setChecked(settings()->value(GROUP(GUI), SETTING(GUI::UseTrayIcon)).toBool());
86 
87   if (!SystemTrayIcon::isSystemTrayAreaAvailable()) {
88     m_ui->m_grpTray->setTitle(m_ui->m_grpTray->title() + QL1C(' ') + tr("(Your OS does not support tray icons at the moment.)"));
89     m_ui->m_grpTray->setEnabled(false);
90   }
91 
92   m_ui->m_checkHidden->setChecked(settings()->value(GROUP(GUI), SETTING(GUI::MainWindowStartsHidden)).toBool());
93   m_ui->m_checkHideWhenMinimized->setChecked(settings()->value(GROUP(GUI), SETTING(GUI::HideMainWindowWhenMinimized)).toBool());
94 
95   // Load settings of icon theme.
96   const QString current_theme = qApp->icons()->currentIconTheme();
97   auto icons = qApp->icons()->installedIconThemes();
98 
99   for (const QString& icon_theme_name : qAsConst(icons)) {
100     if (icon_theme_name == QSL(APP_NO_THEME)) {
101       // Add just "no theme" on other systems.
102       //: Label for disabling icon theme.
103 #if defined(Q_OS_UNIX)
104       m_ui->m_cmbIconTheme->addItem(tr("system icon theme"), APP_NO_THEME);
105 #else
106       m_ui->m_cmbIconTheme->addItem(tr("no icon theme"), APP_NO_THEME);
107 #endif
108     }
109     else {
110       m_ui->m_cmbIconTheme->addItem(icon_theme_name, icon_theme_name);
111     }
112   }
113 
114   m_ui->m_checkMonochromeIcons->setChecked(settings()->value(GROUP(GUI), SETTING(GUI::MonochromeTrayIcon)).toBool());
115   m_ui->m_checkCountUnreadMessages->setChecked(settings()->value(GROUP(GUI), SETTING(GUI::UnreadNumbersInTrayIcon)).toBool());
116 
117   // Mark active theme.
118   if (current_theme == QL1S(APP_NO_THEME)) {
119     // Because "no icon theme" lies at the index 0.
120     m_ui->m_cmbIconTheme->setCurrentIndex(0);
121   }
122   else {
123     m_ui->m_cmbIconTheme->setCurrentText(current_theme);
124   }
125 
126   // Load skin.
127   const QString selected_skin = qApp->skins()->selectedSkinName();
128   auto skins = qApp->skins()->installedSkins();
129 
130   for (const Skin& skin : qAsConst(skins)) {
131     QTreeWidgetItem* new_item = new QTreeWidgetItem(QStringList() <<
132                                                     skin.m_visibleName <<
133                                                     skin.m_version <<
134                                                     skin.m_author);
135 
136     new_item->setData(0, Qt::UserRole, QVariant::fromValue(skin));
137 
138     // Add this skin and mark it as active if its active now.
139     m_ui->m_treeSkins->addTopLevelItem(new_item);
140 
141     if (skin.m_baseName == selected_skin) {
142       m_ui->m_treeSkins->setCurrentItem(new_item);
143     }
144   }
145 
146   if (m_ui->m_treeSkins->currentItem() == nullptr &&
147       m_ui->m_treeSkins->topLevelItemCount() > 0) {
148     // Currently active skin is NOT available, select another one as selected
149     // if possible.
150     m_ui->m_treeSkins->setCurrentItem(m_ui->m_treeSkins->topLevelItem(0));
151   }
152 
153   // Load styles.
154   auto styles = QStyleFactory::keys();
155 
156   for (const QString& style_name : qAsConst(styles)) {
157     m_ui->m_cmbStyles->addItem(style_name);
158   }
159 
160   int item_style = m_ui->m_cmbStyles->findText(settings()->value(GROUP(GUI), SETTING(GUI::Style)).toString(),
161                                                Qt::MatchFlag::MatchFixedString);
162 
163   if (item_style >= 0) {
164     m_ui->m_cmbStyles->setCurrentIndex(item_style);
165   }
166 
167   // Load tab settings.
168   m_ui->m_checkCloseTabsMiddleClick->setChecked(settings()->value(GROUP(GUI), SETTING(GUI::TabCloseMiddleClick)).toBool());
169   m_ui->m_checkCloseTabsDoubleClick->setChecked(settings()->value(GROUP(GUI), SETTING(GUI::TabCloseDoubleClick)).toBool());
170   m_ui->m_checkNewTabDoubleClick->setChecked(settings()->value(GROUP(GUI), SETTING(GUI::TabNewDoubleClick)).toBool());
171   m_ui->m_checkHideTabBarIfOneTabVisible->setChecked(settings()->value(GROUP(GUI), SETTING(GUI::HideTabBarIfOnlyOneTab)).toBool());
172 
173   // Load toolbar button style.
174   m_ui->m_cmbToolbarButtonStyle->addItem(tr("Icon only"), Qt::ToolButtonStyle::ToolButtonIconOnly);
175   m_ui->m_cmbToolbarButtonStyle->addItem(tr("Text only"), Qt::ToolButtonStyle::ToolButtonTextOnly);
176   m_ui->m_cmbToolbarButtonStyle->addItem(tr("Text beside icon"), Qt::ToolButtonStyle::ToolButtonTextBesideIcon);
177   m_ui->m_cmbToolbarButtonStyle->addItem(tr("Text under icon"), Qt::ToolButtonStyle::ToolButtonTextUnderIcon);
178   m_ui->m_cmbToolbarButtonStyle->addItem(tr("Follow OS style"), Qt::ToolButtonStyle::ToolButtonFollowStyle);
179   m_ui->m_cmbToolbarButtonStyle->setCurrentIndex(m_ui->m_cmbToolbarButtonStyle->findData(settings()->value(GROUP(GUI),
180                                                                                                            SETTING(
181                                                                                                              GUI::ToolbarStyle)).toInt()));
182 
183   // Load toolbars.
184   m_ui->m_editorFeedsToolbar->loadFromToolBar(qApp->mainForm()->tabWidget()->feedMessageViewer()->feedsToolBar());
185   m_ui->m_editorMessagesToolbar->loadFromToolBar(qApp->mainForm()->tabWidget()->feedMessageViewer()->messagesToolBar());
186   m_ui->m_editorStatusbar->loadFromToolBar(qApp->mainForm()->statusBar());
187   onEndLoadSettings();
188 }
189 
saveSettings()190 void SettingsGui::saveSettings() {
191   onBeginSaveSettings();
192 
193   // Save toolbar.
194   settings()->setValue(GROUP(GUI), GUI::ToolbarStyle,
195                        m_ui->m_cmbToolbarButtonStyle->itemData(m_ui->m_cmbToolbarButtonStyle->currentIndex()));
196 
197   // Save tray icon.
198   if (SystemTrayIcon::isSystemTrayAreaAvailable()) {
199     settings()->setValue(GROUP(GUI), GUI::UseTrayIcon, m_ui->m_grpTray->isChecked());
200 
201     if (m_ui->m_grpTray->isChecked()) {
202       qApp->showTrayIcon();
203     }
204     else {
205       qApp->deleteTrayIcon();
206     }
207   }
208 
209   auto old_monochrome = settings()->value(GROUP(GUI), SETTING(GUI::MonochromeTrayIcon)).toBool();
210 
211   if (old_monochrome != m_ui->m_checkMonochromeIcons->isChecked()) {
212     requireRestart();
213     settings()->setValue(GROUP(GUI), GUI::MonochromeTrayIcon, m_ui->m_checkMonochromeIcons->isChecked());
214   }
215 
216   settings()->setValue(GROUP(GUI), GUI::UnreadNumbersInTrayIcon, m_ui->m_checkCountUnreadMessages->isChecked());
217   settings()->setValue(GROUP(GUI), GUI::MainWindowStartsHidden, m_ui->m_checkHidden->isChecked());
218   settings()->setValue(GROUP(GUI), GUI::HideMainWindowWhenMinimized, m_ui->m_checkHideWhenMinimized->isChecked());
219 
220   // Make sure that number of unread messages is shown in tray icon as requested.
221   qApp->feedReader()->feedsModel()->notifyWithCounts();
222 
223   // Save selected icon theme.
224   QString selected_icon_theme = m_ui->m_cmbIconTheme->itemData(m_ui->m_cmbIconTheme->currentIndex()).toString();
225   QString original_icon_theme = qApp->icons()->currentIconTheme();
226 
227   qApp->icons()->setCurrentIconTheme(selected_icon_theme);
228 
229   // Check if icon theme was changed.
230   if (selected_icon_theme != original_icon_theme) {
231     requireRestart();
232   }
233 
234   // Save and activate new skin.
235   if (!m_ui->m_treeSkins->selectedItems().isEmpty()) {
236     const Skin active_skin = m_ui->m_treeSkins->currentItem()->data(0, Qt::ItemDataRole::UserRole).value<Skin>();
237 
238     if (qApp->skins()->selectedSkinName() != active_skin.m_baseName) {
239       qApp->skins()->setCurrentSkinName(active_skin.m_baseName);
240       requireRestart();
241     }
242   }
243 
244   // Set new style.
245   if (m_ui->m_cmbStyles->currentIndex() >= 0) {
246     const QString new_style = m_ui->m_cmbStyles->currentText();
247     const QString old_style = qApp->settings()->value(GROUP(GUI), SETTING(GUI::Style)).toString();
248 
249     if (old_style != new_style) {
250       requireRestart();
251     }
252 
253     qApp->settings()->setValue(GROUP(GUI), GUI::Style, new_style);
254   }
255 
256   // Save tab settings.
257   settings()->setValue(GROUP(GUI), GUI::TabCloseMiddleClick, m_ui->m_checkCloseTabsMiddleClick->isChecked());
258   settings()->setValue(GROUP(GUI), GUI::TabCloseDoubleClick, m_ui->m_checkCloseTabsDoubleClick->isChecked());
259   settings()->setValue(GROUP(GUI), GUI::TabNewDoubleClick, m_ui->m_checkNewTabDoubleClick->isChecked());
260   settings()->setValue(GROUP(GUI), GUI::HideTabBarIfOnlyOneTab, m_ui->m_checkHideTabBarIfOneTabVisible->isChecked());
261   m_ui->m_editorFeedsToolbar->saveToolBar();
262   m_ui->m_editorMessagesToolbar->saveToolBar();
263   m_ui->m_editorStatusbar->saveToolBar();
264   qApp->mainForm()->tabWidget()->checkTabBarVisibility();
265   qApp->mainForm()->tabWidget()->feedMessageViewer()->refreshVisualProperties();
266   onEndSaveSettings();
267 }
268