1 /*
2
3 Copyright (C) 2013 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20
21 #include "preferencesdialog.h"
22 #include "application.h"
23 #include "settings.h"
24 #include <QDir>
25 #include <QHash>
26 #include <QStringBuilder>
27 #include <QSettings>
28
29 #include <libfm-qt/folderview.h>
30 #include <libfm-qt/core/terminal.h>
31 #include <libfm-qt/core/archiver.h>
32
33 namespace PCManFM {
34
PreferencesDialog(QString activePage,QWidget * parent)35 PreferencesDialog::PreferencesDialog(QString activePage, QWidget* parent):
36 QDialog(parent) {
37 ui.setupUi(this);
38 setAttribute(Qt::WA_DeleteOnClose);
39 warningCounter_ = 0;
40 ui.warningLabel->hide();
41
42 // resize the list widget according to the width of its content.
43 ui.listWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
44 ui.listWidget->setMaximumWidth(ui.listWidget->sizeHintForColumn(0) + ui.listWidget->frameWidth() * 2 + 4);
45
46 initFromSettings();
47
48 selectPage(activePage);
49 adjustSize();
50 }
51
52 PreferencesDialog::~PreferencesDialog() = default;
53
findIconThemesInDir(QHash<QString,QString> & iconThemes,QString dirName)54 static void findIconThemesInDir(QHash<QString, QString>& iconThemes, QString dirName) {
55 QDir dir(dirName);
56 const QStringList subDirs = dir.entryList(QDir::AllDirs);
57 GKeyFile* kf = g_key_file_new();
58 for(const QString& subDir : subDirs) {
59 QString indexFile = dirName + QLatin1Char('/') + subDir + QStringLiteral("/index.theme");
60 if(g_key_file_load_from_file(kf, indexFile.toLocal8Bit().constData(), GKeyFileFlags(0), nullptr)) {
61 // FIXME: skip hidden ones
62 // icon theme must have this key, so it has icons if it has this key
63 // otherwise, it might be a cursor theme or any other kind of theme.
64 if(g_key_file_has_key(kf, "Icon Theme", "Directories", nullptr)) {
65 char* dispName = g_key_file_get_locale_string(kf, "Icon Theme", "Name", nullptr, nullptr);
66 // char* comment = g_key_file_get_locale_string(kf, "Icon Theme", "Comment", nullptr, nullptr);
67 iconThemes[subDir] = QString::fromUtf8(dispName);
68 g_free(dispName);
69 }
70 }
71 }
72 g_key_file_free(kf);
73 }
74
initIconThemes(Settings & settings)75 void PreferencesDialog::initIconThemes(Settings& settings) {
76 // check if auto-detection is done (for example, from xsettings)
77 if(settings.useFallbackIconTheme()) { // auto-detection failed
78 // load xdg icon themes and select the current one
79 QHash<QString, QString> iconThemes;
80 // user customed icon themes
81 findIconThemesInDir(iconThemes, QString::fromUtf8(g_get_home_dir()) + QStringLiteral("/.icons"));
82
83 // search for icons in system data dir
84 const char* const* dataDirs = g_get_system_data_dirs();
85 for(const char* const* dataDir = dataDirs; *dataDir; ++dataDir) {
86 findIconThemesInDir(iconThemes, QString::fromUtf8(*dataDir) + QStringLiteral("/icons"));
87 }
88
89 iconThemes.remove(QStringLiteral("hicolor")); // remove hicolor, which is only a fallback
90 QHash<QString, QString>::const_iterator it;
91 for(it = iconThemes.constBegin(); it != iconThemes.constEnd(); ++it) {
92 ui.iconTheme->addItem(it.value(), it.key());
93 }
94 ui.iconTheme->model()->sort(0); // sort the list of icon theme names
95
96 // select current theme name
97 int n = ui.iconTheme->count();
98 int i;
99 for(i = 0; i < n; ++i) {
100 QVariant itemData = ui.iconTheme->itemData(i);
101 if(itemData == settings.fallbackIconThemeName()) {
102 break;
103 }
104 }
105 if(i >= n) {
106 i = 0;
107 }
108 ui.iconTheme->setCurrentIndex(i);
109 }
110 else { // auto-detection of icon theme works, hide the fallback icon theme combo box.
111 ui.iconThemeLabel->hide();
112 ui.iconTheme->hide();
113 }
114
115 ui.hMargin->setValue(settings.folderViewCellMargins().width());
116 ui.vMargin->setValue(settings.folderViewCellMargins().height());
117 connect(ui.lockMargins, &QAbstractButton::clicked, this, &PreferencesDialog::lockMargins);
118 }
119
initArchivers(Settings & settings)120 void PreferencesDialog::initArchivers(Settings& settings) {
121 auto& allArchivers = Fm::Archiver::allArchivers();
122 for(int i = 0; i < int(allArchivers.size()); ++i) {
123 auto& archiver = allArchivers[i];
124 ui.archiver->addItem(QString::fromUtf8(archiver->program()), QString::fromUtf8(archiver->program()));
125 if(QString::fromUtf8(archiver->program()) == settings.archiver()) {
126 ui.archiver->setCurrentIndex(i);
127 }
128 }
129 }
130
initDisplayPage(Settings & settings)131 void PreferencesDialog::initDisplayPage(Settings& settings) {
132 initIconThemes(settings);
133 // icon sizes
134 int i = 0;
135 for (const auto & size : Settings::iconSizes(Settings::Big)) {
136 ui.bigIconSize->addItem(QStringLiteral("%1 x %1").arg(size), size);
137 if(settings.bigIconSize() == size) {
138 ui.bigIconSize->setCurrentIndex(i);
139 }
140 ++i;
141 }
142 i = 0;
143 for (const auto & size : Settings::iconSizes(Settings::Small)) {
144 QString text = QStringLiteral("%1 x %1").arg(size);
145 ui.smallIconSize->addItem(text, size);
146 if(settings.smallIconSize() == size) {
147 ui.smallIconSize->setCurrentIndex(i);
148 }
149
150 ui.sidePaneIconSize->addItem(text, size);
151 if(settings.sidePaneIconSize() == size) {
152 ui.sidePaneIconSize->setCurrentIndex(i);
153 }
154 ++i;
155 }
156 i = 0;
157 for (const auto & size : Settings::iconSizes(Settings::Thumbnail)) {
158 ui.thumbnailIconSize->addItem(QStringLiteral("%1 x %1").arg(size), size);
159 if(settings.thumbnailIconSize() == size) {
160 ui.thumbnailIconSize->setCurrentIndex(i);
161 }
162 ++i;
163 }
164
165 ui.siUnit->setChecked(settings.siUnit());
166 ui.backupAsHidden->setChecked(settings.backupAsHidden());
167
168 ui.showFullNames->setChecked(settings.showFullNames());
169 ui.shadowHidden->setChecked(settings.shadowHidden());
170 ui.noItemTooltip->setChecked(settings.noItemTooltip());
171 ui.noScrollPerPixel->setChecked(!settings.scrollPerPixel());
172
173 // app restart warning
174 connect(ui.showFullNames, &QAbstractButton::toggled, [this, &settings] (bool checked) {
175 restartWarning(settings.showFullNames() != checked);
176 });
177 connect(ui.shadowHidden, &QAbstractButton::toggled, [this, &settings] (bool checked) {
178 restartWarning(settings.shadowHidden() != checked);
179 });
180 }
181
initUiPage(Settings & settings)182 void PreferencesDialog::initUiPage(Settings& settings) {
183 ui.alwaysShowTabs->setChecked(settings.alwaysShowTabs());
184 ui.showTabClose->setChecked(settings.showTabClose());
185 ui.switchToNewTab->setChecked(settings.switchToNewTab());
186 ui.reopenLastTabs->setChecked(settings.reopenLastTabs());
187 ui.rememberWindowSize->setChecked(settings.rememberWindowSize());
188 ui.fixedWindowWidth->setValue(settings.fixedWindowWidth());
189 ui.fixedWindowHeight->setValue(settings.fixedWindowHeight());
190 }
191
initBehaviorPage(Settings & settings)192 void PreferencesDialog::initBehaviorPage(Settings& settings) {
193 ui.singleClick->setChecked(settings.singleClick());
194 ui.autoSelectionDelay->setValue(double(settings.autoSelectionDelay()) / 1000);
195 ui.ctrlRightClick->setChecked(settings.ctrlRightClick());
196
197 ui.bookmarkOpenMethod->setCurrentIndex(settings.bookmarkOpenMethod());
198
199 ui.viewMode->addItem(tr("Icon View"), (int)Fm::FolderView::IconMode);
200 ui.viewMode->addItem(tr("Compact View"), (int)Fm::FolderView::CompactMode);
201 ui.viewMode->addItem(tr("Thumbnail View"), (int)Fm::FolderView::ThumbnailMode);
202 ui.viewMode->addItem(tr("Detailed List View"), (int)Fm::FolderView::DetailedListMode);
203 const Fm::FolderView::ViewMode modes[] = {
204 Fm::FolderView::IconMode,
205 Fm::FolderView::CompactMode,
206 Fm::FolderView::ThumbnailMode,
207 Fm::FolderView::DetailedListMode
208 };
209 for(std::size_t i = 0; i < G_N_ELEMENTS(modes); ++i) {
210 if(modes[i] == settings.viewMode()) {
211 ui.viewMode->setCurrentIndex(i);
212 break;
213 }
214 }
215
216 ui.configmDelete->setChecked(settings.confirmDelete());
217
218 if(settings.supportTrash()) {
219 ui.useTrash->setChecked(settings.useTrash());
220 }
221 else {
222 ui.useTrash->hide();
223 }
224
225 ui.noUsbTrash->setChecked(settings.noUsbTrash());
226 ui.confirmTrash->setChecked(settings.confirmTrash());
227 ui.quickExec->setChecked(settings.quickExec());
228 ui.selectNewFiles->setChecked(settings.selectNewFiles());
229 ui.singleWindowMode->setChecked(settings.singleWindowMode());
230
231 // app restart warning
232 connect(ui.quickExec, &QAbstractButton::toggled, [this, &settings] (bool checked) {
233 restartWarning(settings.quickExec() != checked);
234 });
235 }
236
initThumbnailPage(Settings & settings)237 void PreferencesDialog::initThumbnailPage(Settings& settings) {
238 ui.showThumbnails->setChecked(settings.showThumbnails());
239 ui.thumbnailLocal->setChecked(settings.thumbnailLocalFilesOnly());
240
241 // the max. thumbnail size spinboxes are in MiB
242 double m = settings.maxThumbnailFileSize();
243 ui.maxThumbnailFileSize->setValue(qBound(0.0, m / 1024, 1024.0));
244 int m1 = settings.maxExternalThumbnailFileSize();
245 ui.maxExternalThumbnailFileSize->setValue(m1 < 0 ? -1 : qMin(m1 / 1024, 2048));
246 }
247
initVolumePage(Settings & settings)248 void PreferencesDialog::initVolumePage(Settings& settings) {
249 ui.mountOnStartup->setChecked(settings.mountOnStartup());
250 ui.mountRemovable->setChecked(settings.mountRemovable());
251 ui.autoRun->setChecked(settings.autoRun());
252 if(settings.closeOnUnmount()) {
253 ui.closeOnUnmount->setChecked(true);
254 }
255 else {
256 ui.goHomeOnUnmount->setChecked(true);
257 }
258 }
259
initTerminals(Settings & settings)260 void PreferencesDialog::initTerminals(Settings& settings) {
261 // load the known terminal list from the terminal.list file of libfm
262 for(auto& terminal: Fm::allKnownTerminals()) {
263 ui.terminal->addItem(QString::fromUtf8(terminal.get()));
264 }
265 ui.terminal->setEditText(settings.terminal());
266 }
267
initAdvancedPage(Settings & settings)268 void PreferencesDialog::initAdvancedPage(Settings& settings) {
269 initArchivers(settings);
270 initTerminals(settings);
271 ui.suCommand->setText(settings.suCommand());
272
273 ui.onlyUserTemplates->setChecked(settings.onlyUserTemplates());
274 ui.templateTypeOnce->setChecked(settings.templateTypeOnce());
275
276 ui.templateRunApp->setChecked(settings.templateRunApp());
277
278 // FIXME: Hide options that we don't support yet.
279 ui.templateRunApp->hide();
280 }
281
initFromSettings()282 void PreferencesDialog::initFromSettings() {
283 Settings& settings = static_cast<Application*>(qApp)->settings();
284 initDisplayPage(settings);
285 initUiPage(settings);
286 initBehaviorPage(settings);
287 initThumbnailPage(settings);
288 initVolumePage(settings);
289 initAdvancedPage(settings);
290 }
291
applyDisplayPage(Settings & settings)292 void PreferencesDialog::applyDisplayPage(Settings& settings) {
293 if(settings.useFallbackIconTheme()) {
294 // only apply the value if icon theme combo box is in use
295 // the combo box is hidden when auto-detection of icon theme from xsettings works.
296 QString newIconTheme = ui.iconTheme->itemData(ui.iconTheme->currentIndex()).toString();
297 if(newIconTheme != settings.fallbackIconThemeName()) {
298 settings.setFallbackIconThemeName(newIconTheme);
299 QIcon::setThemeName(settings.fallbackIconThemeName());
300 // update the UI by emitting a style change event
301 const auto widgets = QApplication::allWidgets();
302 for(QWidget* widget : widgets) {
303 QEvent event(QEvent::StyleChange);
304 QApplication::sendEvent(widget, &event);
305 }
306 }
307 }
308
309 settings.setBigIconSize(ui.bigIconSize->itemData(ui.bigIconSize->currentIndex()).toInt());
310 settings.setSmallIconSize(ui.smallIconSize->itemData(ui.smallIconSize->currentIndex()).toInt());
311 settings.setThumbnailIconSize(ui.thumbnailIconSize->itemData(ui.thumbnailIconSize->currentIndex()).toInt());
312 settings.setSidePaneIconSize(ui.sidePaneIconSize->itemData(ui.sidePaneIconSize->currentIndex()).toInt());
313
314 settings.setSiUnit(ui.siUnit->isChecked());
315 settings.setBackupAsHidden(ui.backupAsHidden->isChecked());
316 settings.setShowFullNames(ui.showFullNames->isChecked());
317 settings.setShadowHidden(ui.shadowHidden->isChecked());
318 settings.setNoItemTooltip(ui.noItemTooltip->isChecked());
319 settings.setScrollPerPixel(!ui.noScrollPerPixel->isChecked());
320 settings.setFolderViewCellMargins(QSize(ui.hMargin->value(), ui.vMargin->value()));
321 }
322
applyUiPage(Settings & settings)323 void PreferencesDialog::applyUiPage(Settings& settings) {
324 settings.setAlwaysShowTabs(ui.alwaysShowTabs->isChecked());
325 settings.setShowTabClose(ui.showTabClose->isChecked());
326 settings.setSwitchToNewTab(ui.switchToNewTab->isChecked());
327 settings.setReopenLastTabs(ui.reopenLastTabs->isChecked());
328 settings.setRememberWindowSize(ui.rememberWindowSize->isChecked());
329 settings.setFixedWindowWidth(ui.fixedWindowWidth->value());
330 settings.setFixedWindowHeight(ui.fixedWindowHeight->value());
331 }
332
applyBehaviorPage(Settings & settings)333 void PreferencesDialog::applyBehaviorPage(Settings& settings) {
334 settings.setSingleClick(ui.singleClick->isChecked());
335 settings.setAutoSelectionDelay(int(ui.autoSelectionDelay->value() * 1000));
336 settings.setCtrlRightClick(ui.ctrlRightClick->isChecked());
337
338 settings.setBookmarkOpenMethod(OpenDirTargetType(ui.bookmarkOpenMethod->currentIndex()));
339
340 // FIXME: bug here?
341 Fm::FolderView::ViewMode mode = Fm::FolderView::ViewMode(ui.viewMode->itemData(ui.viewMode->currentIndex()).toInt());
342 settings.setViewMode(mode);
343 settings.setConfirmDelete(ui.configmDelete->isChecked());
344
345 if(settings.supportTrash()) {
346 settings.setUseTrash(ui.useTrash->isChecked());
347 }
348
349 settings.setNoUsbTrash(ui.noUsbTrash->isChecked());
350 settings.setConfirmTrash(ui.confirmTrash->isChecked());
351 settings.setQuickExec(ui.quickExec->isChecked());
352 settings.setSelectNewFiles(ui.selectNewFiles->isChecked());
353 settings.setSingleWindowMode(ui.singleWindowMode->isChecked());
354 }
355
applyThumbnailPage(Settings & settings)356 void PreferencesDialog::applyThumbnailPage(Settings& settings) {
357 settings.setShowThumbnails(ui.showThumbnails->isChecked());
358 settings.setThumbnailLocalFilesOnly(ui.thumbnailLocal->isChecked());
359 // the max. thumbnail size spinboxes are in MiB
360 settings.setMaxThumbnailFileSize(qRound(ui.maxThumbnailFileSize->value() * 1024));
361 int m = ui.maxExternalThumbnailFileSize->value();
362 settings.setMaxExternalThumbnailFileSize(m < 0 ? -1 : m * 1024);
363 }
364
applyVolumePage(Settings & settings)365 void PreferencesDialog::applyVolumePage(Settings& settings) {
366 settings.setAutoRun(ui.autoRun->isChecked());
367 settings.setMountOnStartup(ui.mountOnStartup->isChecked());
368 settings.setMountRemovable(ui.mountRemovable->isChecked());
369 settings.setCloseOnUnmount(ui.closeOnUnmount->isChecked());
370 }
371
applyAdvancedPage(Settings & settings)372 void PreferencesDialog::applyAdvancedPage(Settings& settings) {
373 settings.setTerminal(ui.terminal->currentText());
374 settings.setSuCommand(ui.suCommand->text());
375 settings.setArchiver(ui.archiver->itemData(ui.archiver->currentIndex()).toString());
376
377 settings.setOnlyUserTemplates(ui.onlyUserTemplates->isChecked());
378 settings.setTemplateTypeOnce(ui.templateTypeOnce->isChecked());
379 settings.setTemplateRunApp(ui.templateRunApp->isChecked());
380 }
381
382
applySettings()383 void PreferencesDialog::applySettings() {
384 Settings& settings = static_cast<Application*>(qApp)->settings();
385 applyDisplayPage(settings);
386 applyUiPage(settings);
387 applyBehaviorPage(settings);
388 applyThumbnailPage(settings);
389 applyVolumePage(settings);
390 applyAdvancedPage(settings);
391
392 settings.save();
393
394 Application* app = static_cast<Application*>(qApp);
395 app->updateFromSettings();
396 }
397
accept()398 void PreferencesDialog::accept() {
399 applySettings();
400 QDialog::accept();
401 }
402
selectPage(QString name)403 void PreferencesDialog::selectPage(QString name) {
404 if(!name.isEmpty()) {
405 QWidget* page = findChild<QWidget*>(name + QStringLiteral("Page"));
406 if(page) {
407 int index = ui.stackedWidget->indexOf(page);
408 ui.listWidget->setCurrentRow(index);
409 }
410 }
411 }
412
lockMargins(bool lock)413 void PreferencesDialog::lockMargins(bool lock) {
414 ui.vMargin->setDisabled(lock);
415 if(lock) {
416 ui.vMargin->setValue(ui.hMargin->value());
417 connect(ui.hMargin, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), ui.vMargin, &QSpinBox::setValue);
418 }
419 else {
420 disconnect(ui.hMargin, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), ui.vMargin, &QSpinBox::setValue);
421 }
422 }
423
restartWarning(bool warn)424 void PreferencesDialog::restartWarning(bool warn) {
425 if(warn) {
426 ++warningCounter_;
427 }
428 else {
429 --warningCounter_;
430 }
431 ui.warningLabel->setVisible(warningCounter_ > 0);
432 }
433
434 } // namespace PCManFM
435