1 /*
2  *   License:	GPL - See file COPYING for details.
3  *   Author:	Stefan Hundhammer <sh@suse.de>
4  *              Joshua Hodosh <kdirstat@grumpypenguin.org>
5  */
6 
7 #include <qcheckbox.h>
8 #include <qcombobox.h>
9 #include <qinputdialog.h>
10 #include <qlabel.h>
11 #include <qlayout.h>
12 #include <qlineedit.h>
13 #include <qmessagebox.h>
14 #include <qpushbutton.h>
15 #include <qslider.h>
16 #include <qspinbox.h>
17 
18 #include <kcolorbutton.h>
19 #include <kmessagebox.h>
20 #include <ktoolinvocation.h>
21 
22 #include "kdirstatsettings.h"
23 #include "kdirtreeview.h"
24 #include "kexcluderules.h"
25 #include "ktreemapview.h"
26 #include <KHelpClient>
27 #include <KLocalizedString>
28 #include <KSharedConfig>
29 #include <QGroupBox>
30 #include <QMenu>
31 
32 using namespace KDirStat;
33 
addSettingsPage(const char * name)34 template <class T> int KSettingsDialog::addSettingsPage(const char *name) {
35   KSettingsPage *settingsPage = new T(NULL, _mainWin);
36   _pages.append(settingsPage);
37   return tabWidget->addTab(settingsPage, i18n(name));
38 }
39 
KSettingsDialog(k4dirstat * mainWin)40 KSettingsDialog::KSettingsDialog(k4dirstat *mainWin)
41     : QDialog(mainWin), _mainWin(mainWin) {
42   setModal(false);
43   setWindowTitle(i18n("Settings"));
44   QDialogButtonBox *buttonBox =
45       new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Apply |
46                            QDialogButtonBox::RestoreDefaults |
47                            QDialogButtonBox::Cancel | QDialogButtonBox::Help);
48   tabWidget = new QTabWidget(this);
49   QVBoxLayout *mainLayout = new QVBoxLayout;
50   mainLayout->addWidget(tabWidget);
51   mainLayout->addWidget(buttonBox);
52   setLayout(mainLayout);
53   _cleanupsPageIndex = addSettingsPage<KCleanupPage>("&Actions");
54   _treeColorsPageIndex = addSettingsPage<KTreeColorsPage>("&Tree Colors");
55   _treemapPageIndex = addSettingsPage<KTreemapPage>("Tree&map");
56   _generalSettingsPageIndex = addSettingsPage<KGeneralSettingsPage>("&General");
57 
58   connect(this, SIGNAL(aboutToShow()), this, SLOT(setup()));
59   connect(this, SIGNAL(accepted()), this, SLOT(apply()));
60   connect(buttonBox->button(QDialogButtonBox::Ok), SIGNAL(clicked()), this,
61           SLOT(accept()));
62   connect(buttonBox->button(QDialogButtonBox::Cancel), SIGNAL(clicked()), this,
63           SLOT(reject()));
64   connect(buttonBox->button(QDialogButtonBox::Apply), SIGNAL(clicked()), this,
65           SLOT(apply()));
66   connect(buttonBox->button(QDialogButtonBox::Help), SIGNAL(clicked()), this,
67           SLOT(slotHelp()));
68   connect(buttonBox->button(QDialogButtonBox::RestoreDefaults),
69           SIGNAL(clicked()), this, SLOT(slotDefault()));
70 }
71 
apply()72 void KSettingsDialog::apply() {
73   foreach (KSettingsPage *p, _pages)
74     p->apply();
75 }
76 
revertToDefaults()77 void KSettingsDialog::revertToDefaults() {
78   foreach (KSettingsPage *p, _pages)
79     p->revertToDefaults();
80 }
81 
setup()82 void KSettingsDialog::setup() {
83   foreach (KSettingsPage *p, _pages)
84     p->setup();
85 }
86 
~KSettingsDialog()87 KSettingsDialog::~KSettingsDialog() {
88   // NOP
89 }
90 
show()91 void KSettingsDialog::show() {
92   emit aboutToShow();
93   QDialog::show();
94 }
95 
slotDefault()96 void KSettingsDialog::slotDefault() {
97   if (KMessageBox::warningContinueCancel(
98           this,
99           i18n("Really revert all settings to their default values?\n"
100                "You will lose all changes you ever made!"),
101           i18n("Please Confirm"),                      // caption
102           KGuiItem(i18n("&Really Revert to Defaults")) // continueButton
103           ) == KMessageBox::Continue) {
104     revertToDefaults();
105     apply();
106   }
107 }
108 
slotHelp()109 void KSettingsDialog::slotHelp() {
110   QString helpTopic = "";
111   int i = tabWidget->currentIndex();
112   if (i == _cleanupsPageIndex)
113     helpTopic = "configuring_cleanups";
114   else if (i == _treeColorsPageIndex)
115     helpTopic = "tree_colors";
116   else if (i == _treemapPageIndex)
117     helpTopic = "treemap_settings";
118   else if (i == _generalSettingsPageIndex)
119     helpTopic = "general_settings";
120 
121   // qDebug() << "Help topic: " << helpTopic << endl;
122   KHelpClient::invokeHelp(helpTopic);
123 }
124 
125 /*--------------------------------------------------------------------------*/
126 
~KSettingsPage()127 KSettingsPage::~KSettingsPage() {
128   // NOP
129 }
130 
131 /*--------------------------------------------------------------------------*/
132 
KTreeColorsPage(QWidget * parent,k4dirstat * mainWin)133 KTreeColorsPage::KTreeColorsPage(QWidget *parent, k4dirstat *mainWin)
134     : KSettingsPage(parent), _mainWin(mainWin), _treeView(mainWin->treeView()),
135       _maxButtons(KDirStatSettingsMaxColorButton) {
136   // Outer layout box
137 
138   QHBoxLayout *outerBox = new QHBoxLayout(this);
139   // Inner layout box with a column of color buttons
140 
141   QGridLayout *grid = new QGridLayout();
142   outerBox->addLayout(grid, 1);
143   grid->setColumnStretch(0, 0); // label column - dont' stretch
144 
145   for (int i = 1; i < _maxButtons; i++) {
146     grid->setColumnStretch(i, 1); // all other columns stretch as you like
147   }
148 
149   for (int i = 0; i < _maxButtons; i++) {
150     QString labelText;
151 
152     labelText = i18n("Tree Level %1", i + 1);
153     _colorLabel[i] = new QLabel(labelText, this);
154     grid->addWidget(_colorLabel[i], i, 0);
155 
156     _colorButton[i] = new KColorButton(this);
157     _colorButton[i]->setMinimumSize(QSize(80, 10));
158     grid->addWidget(_colorButton[i], i, i + 1, 1, _maxButtons - i);
159     grid->setRowStretch(i, 1);
160   }
161 
162   // Vertical slider
163 
164   _slider = new QSlider(Qt::Vertical, this);
165   _slider->setMinimum(1);
166   _slider->setMaximum(_maxButtons);
167   _slider->setPageStep(1);
168   _slider->setValue(1);
169 
170   outerBox->addWidget(_slider, 0);
171   outerBox->activate();
172 
173   connect(_slider, SIGNAL(valueChanged(int)), this, SLOT(enableColors(int)));
174 }
175 
~KTreeColorsPage()176 KTreeColorsPage::~KTreeColorsPage() {
177   // NOP
178 }
179 
apply()180 void KTreeColorsPage::apply() {
181   _treeView->setUsedFillColors(_slider->value());
182 
183   for (int i = 0; i < _maxButtons; i++) {
184     _treeView->setFillColor(i, _colorButton[i]->color());
185   }
186 
187   _treeView->viewport()->repaint();
188 }
189 
revertToDefaults()190 void KTreeColorsPage::revertToDefaults() {
191   _treeView->setDefaultFillColors();
192   setup();
193 }
194 
setup()195 void KTreeColorsPage::setup() {
196   for (int i = 0; i < _maxButtons; i++) {
197     _colorButton[i]->setColor(_treeView->rawFillColor(i));
198   }
199 
200   _slider->setValue(_treeView->usedFillColors());
201   enableColors(_treeView->usedFillColors());
202 }
203 
enableColors(int maxColors)204 void KTreeColorsPage::enableColors(int maxColors) {
205   for (int i = 0; i < _maxButtons; i++) {
206     _colorButton[i]->setEnabled(i < maxColors);
207     _colorLabel[i]->setEnabled(i < maxColors);
208   }
209 }
210 
211 /*--------------------------------------------------------------------------*/
212 
KCleanupPage(QWidget * parent,k4dirstat * mainWin)213 KCleanupPage::KCleanupPage(QWidget *parent, k4dirstat *mainWin)
214     : KSettingsPage(parent), _mainWin(mainWin), _currentCleanup(0) {
215   // Copy the main window's cleanup collection.
216 
217   _workCleanupCollection = mainWin->cleanupCollection()->cleanupsCopy();
218 
219   // Create layout and widgets.
220 
221   QHBoxLayout *layout = new QHBoxLayout(this);
222   _listBox = new KCleanupListBox(this);
223   _props = new KCleanupPropertiesPage(this, mainWin);
224 
225   // Connect list box signals to reflect changes in the list
226   // selection in the cleanup properties page - whenever the user
227   // clicks on a different cleanup in the list, the properties page
228   // will display that cleanup's values.
229 
230   connect(_listBox, SIGNAL(selectCleanup(KCleanup *)), this,
231           SLOT(changeCleanup(KCleanup *)));
232 
233   // Fill list box so it can determine a reasonable startup geometry - that
234   // doesn't work if it happens only later.
235 
236   setup();
237 
238   // Now that _listBox will (hopefully) have determined a reasonable
239   // default geometry, add the widgets to the layout.
240 
241   layout->addWidget(_listBox, 0);
242   layout->addWidget(_props, 1);
243   layout->activate();
244 }
245 
~KCleanupPage()246 KCleanupPage::~KCleanupPage() {
247   // NOP
248 }
249 
changeCleanup(KCleanup * newCleanup)250 void KCleanupPage::changeCleanup(KCleanup *newCleanup) {
251   if (_currentCleanup && newCleanup != _currentCleanup) {
252     storeProps(_currentCleanup);
253   }
254 
255   _currentCleanup = newCleanup;
256   _props->setFields(_currentCleanup);
257 }
258 
apply()259 void KCleanupPage::apply() { exportCleanups(); }
260 
revertToDefaults()261 void KCleanupPage::revertToDefaults() {
262   _mainWin->revertCleanupsToDefaults();
263   setup();
264 }
265 
setup()266 void KCleanupPage::setup() {
267   importCleanups();
268 
269   // Fill the list box.
270 
271   _listBox->clear();
272   for (int i = 0; i < _workCleanupCollection.count(); i++)
273     _listBox->insert(&_workCleanupCollection[i]);
274 
275   // (Re-) Initialize list box.
276 
277   // _listBox->resize( _listBox->sizeHint() );
278   _listBox->setCurrentRow(0);
279 }
280 
importCleanups()281 void KCleanupPage::importCleanups() {
282   // Copy the main window's cleanup collecton to _workCleanupCollection.
283 
284   _workCleanupCollection = _mainWin->cleanupCollection()->cleanupsCopy();
285 
286   // Pointers to the old collection contents are now invalid!
287 
288   _currentCleanup = 0;
289 }
290 
exportCleanups()291 void KCleanupPage::exportCleanups() {
292   // Retrieve any pending changes from the properties page and store
293   // them in the current cleanup.
294 
295   storeProps(_currentCleanup);
296 
297   // Copy the _workCleanupCollection to the main window's cleanup
298   // collection.
299 
300   _mainWin->cleanupCollection()->setCleanups(_workCleanupCollection);
301 }
302 
storeProps(KCleanup * cleanup)303 void KCleanupPage::storeProps(KCleanup *cleanup) {
304   if (cleanup) {
305     // Retrieve the current fields contents and store them in the current
306     // cleanup.
307 
308     *cleanup = _props->fields();
309 
310     // Update the list box accordingly - the cleanup's title may have
311     // changed, too!
312 
313     _listBox->updateTitle(cleanup);
314   }
315 }
316 
317 /*--------------------------------------------------------------------------*/
318 
KCleanupListBox(QWidget * parent)319 KCleanupListBox::KCleanupListBox(QWidget *parent) : QListWidget(parent) {
320   _selection = 0;
321 
322   connect(this,
323           SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)),
324           SLOT(selectCleanup(QListWidgetItem *)));
325 }
326 
sizeHint() const327 QSize KCleanupListBox::sizeHint() const {
328   QSize r = QListWidget::sizeHint();
329   r.setWidth(sizeHintForColumn(0));
330   return r;
331 }
332 
insert(KCleanup * cleanup)333 void KCleanupListBox::insert(KCleanup *cleanup) {
334   // Create a new listbox item - this will insert itself (!) automatically.
335   // It took me half an afternoon to figure _this_ out. Not too intuitive
336   // when there is an insertItem() method, too, eh?
337 
338   new KCleanupListBoxItem(this, cleanup);
339 }
340 
selectCleanup(QListWidgetItem * listBoxItem)341 void KCleanupListBox::selectCleanup(QListWidgetItem *listBoxItem) {
342   KCleanupListBoxItem *item = (KCleanupListBoxItem *)listBoxItem;
343   if (item) {
344     _selection = item->cleanup();
345     emit selectCleanup(_selection);
346   }
347 }
348 
updateTitle(KCleanup * cleanup)349 void KCleanupListBox::updateTitle(KCleanup *cleanup) {
350   for (int i = 0; i < count(); i++) {
351     KCleanupListBoxItem *item =
352         static_cast<KCleanupListBoxItem *>(this->item(i));
353     if (!cleanup || item->cleanup() == cleanup)
354       item->updateTitle();
355   }
356 }
357 
358 /*--------------------------------------------------------------------------*/
359 
KCleanupListBoxItem(KCleanupListBox * listBox,KCleanup * cleanup)360 KCleanupListBoxItem::KCleanupListBoxItem(KCleanupListBox *listBox,
361                                          KCleanup *cleanup)
362     : QListWidgetItem(listBox), _cleanup(cleanup) {
363   Q_CHECK_PTR(cleanup);
364   setText(cleanup->cleanTitle());
365 }
366 
updateTitle()367 void KCleanupListBoxItem::updateTitle() { setText(_cleanup->cleanTitle()); }
368 
369 /*--------------------------------------------------------------------------*/
370 
KCleanupPropertiesPage(QWidget * parent,k4dirstat * mainWin)371 KCleanupPropertiesPage::KCleanupPropertiesPage(QWidget *parent,
372                                                k4dirstat *mainWin)
373     : QWidget(parent), _mainWin(mainWin) {
374   QVBoxLayout *outerBox = new QVBoxLayout(this);
375   outerBox->setMargin(0);
376   outerBox->setSpacing(0);
377   // The topmost check box: "Enabled".
378 
379   _enabled = new QCheckBox(i18n("&Enabled"), this);
380   outerBox->addWidget(_enabled, 0);
381   outerBox->addSpacing(7);
382   outerBox->addStretch();
383 
384   connect(_enabled, SIGNAL(toggled(bool)), this, SLOT(enableFields(bool)));
385 
386   // All other widgets of this page are grouped together in a
387   // separate subwidget so they can all be enabled / disabled
388   // together.
389   _fields = new QWidget(this);
390   outerBox->addWidget(_fields, 1);
391 
392   QVBoxLayout *fieldsBox = new QVBoxLayout(_fields);
393 
394   // Grid layout for the edit fields, their labels, some
395   // explanatory text and the "recurse?" check box.
396 
397   QGridLayout *grid = new QGridLayout();
398   grid->setSpacing(4);
399   fieldsBox->addLayout(grid, 0);
400   fieldsBox->addStretch();
401   fieldsBox->addSpacing(5);
402 
403   grid->setColumnStretch(0, 0); // column for field labels - dont' stretch
404   grid->setColumnStretch(1, 1); // column for edit fields - stretch as you like
405 
406   // Edit fields for cleanup action title and command line.
407 
408   QLabel *label;
409   _title = new QLineEdit(_fields);
410   grid->addWidget(_title, 0, 1);
411   _command = new QLineEdit(_fields);
412   grid->addWidget(_command, 1, 1);
413 
414   label = new QLabel(i18n("&Title:"), _fields);
415   label->setBuddy(_title);
416   grid->addWidget(label, 0, 0);
417 
418   label = new QLabel(i18n("&Command Line:"), _fields);
419   label->setBuddy(_command);
420   grid->addWidget(label, 1, 0);
421 
422   label = new QLabel(i18n("%p Full Path"), _fields);
423   grid->addWidget(label, 2, 1);
424 
425   label = new QLabel(i18n("%n File / Directory Name Without Path"), _fields);
426   grid->addWidget(label, 3, 1);
427 
428   label = new QLabel(i18n("%t KDE Trash Directory"), _fields);
429   grid->addWidget(label, 4, 1);
430 
431   // "Recurse into subdirs" check box
432 
433   _recurse = new QCheckBox(i18n("&Recurse into Subdirectories"), _fields);
434   grid->addWidget(_recurse, 5, 1);
435 
436   // "Ask for confirmation" check box
437 
438   _askForConfirmation = new QCheckBox(i18n("&Ask for Confirmation"), _fields);
439   grid->addWidget(_askForConfirmation, 6, 1);
440 
441   // The "Works for..." check boxes, grouped together in a button group.
442 
443   QGroupBox *worksFor = new QGroupBox(i18n("Works for..."), _fields);
444   QVBoxLayout *worksForBox = new QVBoxLayout(worksFor);
445   fieldsBox->addWidget(worksFor, 0);
446   fieldsBox->addSpacing(5);
447   fieldsBox->addStretch();
448 
449   _worksForDir = new QCheckBox(i18n("&Directories"), worksFor);
450   _worksForFile = new QCheckBox(i18n("&Files"), worksFor);
451   _worksForDotEntry = new QCheckBox(i18n("<Files> P&seudo Entries"), worksFor);
452 
453   worksForBox->addWidget(_worksForDir, 1);
454   worksForBox->addWidget(_worksForFile, 1);
455   worksForBox->addWidget(_worksForDotEntry, 1);
456 
457   worksForBox->addSpacing(5);
458   _worksForProtocols = new QComboBox(worksFor);
459   _worksForProtocols->setEditable(false);
460   worksForBox->addWidget(_worksForProtocols, 1);
461 
462   _worksForProtocols->insertItem(
463       0, i18n("On Local Machine Only ('file:/' Protocol)"));
464   _worksForProtocols->insertItem(
465       1, i18n("Network Transparent (ftp, smb, tar, ...)"));
466 
467   // Grid layout for combo boxes at the bottom
468 
469   grid = new QGridLayout();
470   grid->setSpacing(4);
471   fieldsBox->addLayout(grid, 0);
472   fieldsBox->addSpacing(5);
473   fieldsBox->addStretch();
474   int row = 0;
475 
476   // The "Refresh policy" combo box
477 
478   _refreshPolicy = new QComboBox(_fields);
479   _refreshPolicy->setEditable(false);
480   grid->addWidget(_refreshPolicy, row, 1);
481 
482   label = new QLabel(i18n("Refresh &Policy:"), _fields);
483   label->setBuddy(_refreshPolicy);
484   grid->addWidget(label, row++, 0);
485 
486   // Caution: The order of those entries must match the order of
487   // 'enum RefreshPolicy' in 'kcleanup.h'!
488   //
489   // I don't like this one bit. The ComboBox should provide something better
490   // than mere numeric IDs. One of these days I'm going to rewrite this
491   // thing!
492 
493   _refreshPolicy->insertItem(0, i18n("No Refresh"));
494   _refreshPolicy->insertItem(1, i18n("Refresh This Entry"));
495   _refreshPolicy->insertItem(2, i18n("Refresh This Entry's Parent"));
496   _refreshPolicy->insertItem(3, i18n("Assume Entry Has Been Deleted"));
497 
498   outerBox->activate();
499   setMinimumSize(sizeHint());
500 }
501 
enableFields(bool active)502 void KCleanupPropertiesPage::enableFields(bool active) {
503   _fields->setEnabled(active);
504 }
505 
setFields(const KCleanup * cleanup)506 void KCleanupPropertiesPage::setFields(const KCleanup *cleanup) {
507   _id = cleanup->id();
508   _enabled->setChecked(cleanup->enabled());
509   _title->setText(cleanup->title());
510   _command->setText(cleanup->command());
511   _recurse->setChecked(cleanup->recurse());
512   _askForConfirmation->setChecked(cleanup->askForConfirmation());
513   _worksForDir->setChecked(cleanup->worksForDir());
514   _worksForFile->setChecked(cleanup->worksForFile());
515   _worksForDotEntry->setChecked(cleanup->worksForDotEntry());
516   _worksForProtocols->setCurrentIndex(cleanup->worksLocalOnly() ? 0 : 1);
517   _refreshPolicy->setCurrentIndex(cleanup->refreshPolicy());
518 
519   enableFields(cleanup->enabled());
520 }
521 
fields() const522 KCleanup KCleanupPropertiesPage::fields() const {
523   KCleanup cleanup(_id, _command->text(), _title->text());
524 
525   cleanup.setEnabled(_enabled->isChecked());
526   cleanup.setRecurse(_recurse->isChecked());
527   cleanup.setAskForConfirmation(_askForConfirmation->isChecked());
528   cleanup.setWorksForDir(_worksForDir->isChecked());
529   cleanup.setWorksForFile(_worksForFile->isChecked());
530   cleanup.setWorksLocalOnly(_worksForProtocols->currentIndex() == 0 ? true
531                                                                     : false);
532   cleanup.setWorksForDotEntry(_worksForDotEntry->isChecked());
533   cleanup.setRefreshPolicy(
534       (KCleanup::RefreshPolicy)_refreshPolicy->currentIndex());
535 
536   return cleanup;
537 }
538 
539 /*--------------------------------------------------------------------------*/
540 
KGeneralSettingsPage(QWidget * parent,k4dirstat * mainWin)541 KGeneralSettingsPage::KGeneralSettingsPage(QWidget *parent, k4dirstat *mainWin)
542     : KSettingsPage(parent), _mainWin(mainWin), _treeView(mainWin->treeView()) {
543   // Create layout and widgets.
544 
545   QVBoxLayout *layout = new QVBoxLayout(this);
546   QGroupBox *gbox = new QGroupBox(i18n("Directory Reading"), this);
547   layout->addWidget(gbox);
548   QVBoxLayout *gboxLayout = new QVBoxLayout(gbox);
549   gbox->setLayout(gboxLayout);
550 
551   _crossFileSystems = new QCheckBox(i18n("Cross &File System Boundaries"));
552   _enableLocalDirReader =
553       new QCheckBox(i18n("Use Optimized &Local Directory Read Methods"));
554   gboxLayout->addWidget(_crossFileSystems);
555   gboxLayout->addWidget(_enableLocalDirReader);
556 
557   connect(_enableLocalDirReader, SIGNAL(stateChanged(int)), this,
558           SLOT(checkEnabledState()));
559 
560   layout->addSpacing(10);
561 
562   QGroupBox *excludeBox = new QGroupBox(i18n("&Exclude Rules"));
563   layout->addWidget(excludeBox);
564   QVBoxLayout *excludeBoxLayout = new QVBoxLayout();
565   excludeBox->setLayout(excludeBoxLayout);
566   _excludeRulesListView = new QListWidget();
567   _excludeRuleContextMenu = 0;
568   excludeBoxLayout->addWidget(_excludeRulesListView);
569 
570   QHBoxLayout *buttonBoxLayout = new QHBoxLayout();
571   excludeBoxLayout->addLayout(buttonBoxLayout);
572   _addExcludeRuleButton = new QPushButton(i18n("&Add"));
573   _editExcludeRuleButton = new QPushButton(i18n("&Edit"));
574   _deleteExcludeRuleButton = new QPushButton(i18n("&Delete"));
575   buttonBoxLayout->addWidget(_addExcludeRuleButton);
576   buttonBoxLayout->addWidget(_editExcludeRuleButton);
577   buttonBoxLayout->addWidget(_deleteExcludeRuleButton);
578 
579   _excludeRulesListView->setContextMenuPolicy(Qt::CustomContextMenu);
580   connect(_excludeRulesListView,
581           SIGNAL(customContextMenuRequested(const QPoint &)), this,
582           SLOT(showExcludeRuleContextMenu(const QPoint &)));
583 
584   connect(_addExcludeRuleButton, SIGNAL(clicked()), this,
585           SLOT(addExcludeRule()));
586 
587   connect(_editExcludeRuleButton, SIGNAL(clicked()), this,
588           SLOT(editExcludeRule()));
589 
590   connect(_deleteExcludeRuleButton, SIGNAL(clicked()), this,
591           SLOT(deleteExcludeRule()));
592 
593   connect(_excludeRulesListView, SIGNAL(itemDoubleClicked(QListWidgetItem *)),
594           this, SLOT(editExcludeRule()));
595 }
596 
~KGeneralSettingsPage()597 KGeneralSettingsPage::~KGeneralSettingsPage() {
598   if (_excludeRuleContextMenu)
599     delete _excludeRuleContextMenu;
600 }
601 
showExcludeRuleContextMenu(const QPoint & localPos)602 void KGeneralSettingsPage::showExcludeRuleContextMenu(const QPoint &localPos) {
603   if (!_excludeRuleContextMenu) {
604     _excludeRuleContextMenu = new QMenu();
605     QAction *editAction = new QAction(i18n("&Edit"), _excludeRuleContextMenu);
606     connect(editAction, SIGNAL(triggered()), this, SLOT(editExcludeRule()));
607     _excludeRuleContextMenu->addAction(editAction);
608     QAction *deleteAction =
609         new QAction(i18n("&Delete"), _excludeRuleContextMenu);
610     connect(deleteAction, SIGNAL(triggered()), this, SLOT(deleteExcludeRule()));
611     _excludeRuleContextMenu->addAction(deleteAction);
612   }
613 
614   if (_excludeRuleContextMenu && _excludeRulesListView->currentItem()) {
615     QPoint pos = _excludeRulesListView->viewport()->mapToGlobal(localPos);
616     _excludeRuleContextMenu->move(pos.x(), pos.y());
617     _excludeRuleContextMenu->show();
618   }
619 }
620 
apply()621 void KGeneralSettingsPage::apply() {
622   KConfigGroup config = KSharedConfig::openConfig()->group("Directory Reading");
623 
624   config.writeEntry("CrossFileSystems", _crossFileSystems->isChecked());
625   config.writeEntry("EnableLocalDirReader", _enableLocalDirReader->isChecked());
626 
627   config = KSharedConfig::openConfig()->group("Exclude");
628   // config.setGroup( "Exclude" );
629 
630   QStringList excludeRulesStringList;
631   KExcludeRules::excludeRules()->clear();
632 
633   for (int i = 0; i < _excludeRulesListView->count(); i++) {
634     QListWidgetItem *item = _excludeRulesListView->item(i);
635     QString ruleText = item->text();
636     excludeRulesStringList.append(ruleText);
637     // qDebug() << "Adding exclude rule " << ruleText << endl;
638     KExcludeRules::excludeRules()->add(new KExcludeRule(QRegExp(ruleText)));
639   }
640 
641   config.writeEntry("ExcludeRules", excludeRulesStringList);
642 }
643 
revertToDefaults()644 void KGeneralSettingsPage::revertToDefaults() {
645   _crossFileSystems->setChecked(false);
646   _enableLocalDirReader->setChecked(true);
647   _excludeRulesListView->clear();
648   _editExcludeRuleButton->setEnabled(false);
649   _deleteExcludeRuleButton->setEnabled(false);
650 }
651 
setup()652 void KGeneralSettingsPage::setup() {
653   KConfigGroup config = KSharedConfig::openConfig()->group("Directory Reading");
654 
655   _crossFileSystems->setChecked(config.readEntry("CrossFileSystems", false));
656   _enableLocalDirReader->setChecked(
657       config.readEntry("EnableLocalDirReader", true));
658   _excludeRulesListView->clear();
659 
660   foreach (KExcludeRule *excludeRule, KExcludeRules::excludeRules()->rules()) {
661     QListWidgetItem *n = new QListWidgetItem(_excludeRulesListView);
662     n->setText(excludeRule->regexp().pattern());
663   }
664 
665   checkEnabledState();
666 }
667 
checkEnabledState()668 void KGeneralSettingsPage::checkEnabledState() {
669   _crossFileSystems->setEnabled(_enableLocalDirReader->isChecked());
670 
671   int excludeRulesCount = _excludeRulesListView->count();
672 
673   _editExcludeRuleButton->setEnabled(excludeRulesCount > 0);
674   _deleteExcludeRuleButton->setEnabled(excludeRulesCount > 0);
675 }
676 
addExcludeRule()677 void KGeneralSettingsPage::addExcludeRule() {
678   bool ok;
679   QString text =
680       QInputDialog::getText(this, i18n("New exclude rule"),
681                             i18n("Regular expression for new exclude rule:"),
682                             QLineEdit::Normal, QString(), &ok);
683   if (ok && !text.isEmpty()) {
684     QListWidgetItem *l = new QListWidgetItem(_excludeRulesListView);
685     l->setText(text);
686     _excludeRulesListView->addItem(l);
687   }
688 
689   checkEnabledState();
690 }
691 
editExcludeRule()692 void KGeneralSettingsPage::editExcludeRule() {
693   QListWidgetItem *item = _excludeRulesListView->currentItem();
694 
695   if (item) {
696     bool ok;
697     QString text =
698         QInputDialog::getText(this, i18n("Edit exclude rule"),
699                               i18n("Exclude rule (regular expression):"),
700                               QLineEdit::Normal, item->text(), &ok);
701     if (ok) {
702       if (text.isEmpty())
703         _excludeRulesListView->takeItem(_excludeRulesListView->currentRow());
704       else
705         item->setText(text);
706     }
707   }
708 
709   checkEnabledState();
710 }
711 
deleteExcludeRule()712 void KGeneralSettingsPage::deleteExcludeRule() {
713   QListWidgetItem *item = _excludeRulesListView->currentItem();
714 
715   if (item) {
716     QString excludeRule = item->text();
717     int result = KMessageBox::questionYesNo(
718         this, i18n("Really delete exclude rule \"%1\"?", excludeRule),
719         i18n("Delete?")); // Window title
720     if (result == KMessageBox::Yes) {
721       _excludeRulesListView->takeItem(_excludeRulesListView->currentRow());
722     }
723   }
724 
725   checkEnabledState();
726 }
727 
728 /*--------------------------------------------------------------------------*/
729 
KTreemapPage(QWidget * parent,k4dirstat * mainWin)730 KTreemapPage::KTreemapPage(QWidget *parent, k4dirstat *mainWin)
731     : KSettingsPage(parent), _mainWin(mainWin) {
732   // qDebug() << Q_FUNC_INFO << endl;
733 
734   QVBoxLayout *layout = new QVBoxLayout(this); // parent
735   setLayout(layout);
736   _squarify = new QCheckBox(i18n("S&quarify Treemap"), this);
737   _doCushionShading = new QCheckBox(i18n("Use C&ushion Shading"), this);
738   layout->addWidget(_squarify);
739   layout->addWidget(_doCushionShading);
740 
741   // Cushion parameters
742 
743   QGroupBox *gbox = new QGroupBox(i18n("Cushion Parameters"), this);
744   layout->addWidget(gbox);
745   _cushionParams = gbox;
746   QLabel *label = new QLabel(i18n("Ambient &Light"));
747   _ambientLight = new QSlider(Qt::Horizontal);
748   _ambientLight->setMinimum(MinAmbientLight);
749   _ambientLight->setMaximum(MaxAmbientLight);
750   _ambientLight->setPageStep(10);
751   _ambientLight->setValue(DefaultAmbientLight);
752   _ambientLightSB = new QSpinBox();
753   _ambientLightSB->setMinimum(MinAmbientLight);
754   _ambientLightSB->setMaximum(MaxAmbientLight);
755   label->setBuddy(_ambientLightSB);
756 
757   QGridLayout *cushionLayout = new QGridLayout();
758   gbox->setLayout(cushionLayout);
759   cushionLayout->setColumnStretch(1, 1);
760   cushionLayout->addWidget(label, 0, 0, 1, 1);
761   cushionLayout->addWidget(_ambientLight, 0, 1, 1, 2);
762   cushionLayout->addWidget(_ambientLightSB, 0, 3, 1, 1);
763 
764   label = new QLabel(i18n("&Height Scale"));
765   _heightScalePercent = new QSlider(Qt::Horizontal);
766   _heightScalePercent->setMinimum(MinHeightScalePercent);
767   _heightScalePercent->setMaximum(MaxHeightScalePercent);
768   _heightScalePercent->setPageStep(10);
769   _heightScalePercent->setValue(DefaultHeightScalePercent);
770   _heightScalePercent->setSizePolicy(QSizePolicy::Expanding,
771                                      QSizePolicy::Fixed);
772   _heightScalePercentSB = new QSpinBox();
773   _heightScalePercentSB->setMinimum(MinHeightScalePercent);
774   _heightScalePercentSB->setMaximum(MaxHeightScalePercent);
775   label->setBuddy(_heightScalePercentSB);
776 
777   cushionLayout->addWidget(label, 1, 0, 1, 1);
778   cushionLayout->addWidget(_heightScalePercent, 1, 1, 1, 2);
779   cushionLayout->addWidget(_heightScalePercentSB, 1, 3, 1, 1);
780 
781   _ensureContrast = new QCheckBox(i18n("Draw Lines if Lo&w Contrast"));
782 
783   _forceCushionGrid = new QCheckBox(i18n("Always Draw &Grid"));
784   _cushionGridColorL = new QLabel(i18n("Gr&id Color: "));
785   _cushionGridColor = new KColorButton();
786   _cushionGridColorL->setBuddy(_cushionGridColor);
787   cushionLayout->addWidget(_forceCushionGrid, 4, 0, 1, 2);
788   cushionLayout->addWidget(_ensureContrast, 3, 0, 1, 2);
789   cushionLayout->addWidget(_cushionGridColorL, 4, 2, 1, 1, Qt::AlignRight);
790   cushionLayout->addWidget(_cushionGridColor, 4, 3, 1, 1);
791 
792   // Plain treemaps parameters
793 
794   _plainTileParams = new QGroupBox(i18n("Colors for Plain Treemaps"), this);
795   layout->addWidget(_plainTileParams);
796   QHBoxLayout *plainTileLayout = new QHBoxLayout();
797   _plainTileParams->setLayout(plainTileLayout);
798   label = new QLabel(i18n("&Files: "));
799   plainTileLayout->addWidget(label);
800   _fileFillColor = new KColorButton();
801   plainTileLayout->addWidget(_fileFillColor);
802   label->setBuddy(_fileFillColor);
803   label->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
804 
805   label = new QLabel("	   " + i18n("&Directories: "));
806   _dirFillColor = new KColorButton();
807   label->setBuddy(_dirFillColor);
808   label->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
809   plainTileLayout->addWidget(label);
810   plainTileLayout->addWidget(_dirFillColor);
811 
812   label = new QLabel(i18n("Gr&id: "));
813   _outlineColor = new KColorButton();
814   label->setBuddy(_outlineColor);
815   label->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
816   plainTileLayout->addWidget(label);
817   plainTileLayout->addWidget(_outlineColor);
818 
819   // Misc
820 
821   QWidget *gridBox = new QWidget(this);
822   layout->addWidget(gridBox);
823   QGridLayout *grid = new QGridLayout(gridBox);
824   grid->setColumnStretch(0, 0); // (col, stretch) don't stretch this column
825   grid->setColumnStretch(1, 0); // don't stretch
826   grid->setColumnStretch(2, 1); // stretch this as you like
827 
828   label = new QLabel(i18n("Highlight R&ectangle: "), gridBox);
829   _highlightColor = new KColorButton(gridBox);
830   label->setBuddy(_highlightColor);
831 
832   grid->addWidget(label, 0, 0);
833   grid->addWidget(_highlightColor, 0, 1);
834 
835   label = new QLabel(i18n("Minim&um Treemap Tile Size: "), gridBox);
836   _minTileSize = new QSpinBox(gridBox);
837   _minTileSize->setMinimum(0);
838   _minTileSize->setMaximum(30);
839   _minTileSize->setSingleStep(1);
840   label->setBuddy(_minTileSize);
841 
842   grid->addWidget(label, 1, 0);
843   grid->addWidget(_minTileSize, 1, 1);
844 
845   _autoResize = new QCheckBox(i18n("Auto-&Resize Treemap"), this);
846   layout->addWidget(_autoResize);
847 
848   // Connections
849 
850   connect(_ambientLight, SIGNAL(valueChanged(int)), _ambientLightSB,
851           SLOT(setValue(int)));
852 
853   connect(_ambientLightSB, SIGNAL(valueChanged(int)), _ambientLight,
854           SLOT(setValue(int)));
855 
856   connect(_heightScalePercent, SIGNAL(valueChanged(int)), _heightScalePercentSB,
857           SLOT(setValue(int)));
858 
859   connect(_heightScalePercentSB, SIGNAL(valueChanged(int)), _heightScalePercent,
860           SLOT(setValue(int)));
861 
862   connect(_doCushionShading, SIGNAL(stateChanged(int)), this,
863           SLOT(checkEnabledState()));
864   connect(_forceCushionGrid, SIGNAL(stateChanged(int)), this,
865           SLOT(checkEnabledState()));
866 
867   checkEnabledState();
868 }
869 
~KTreemapPage()870 KTreemapPage::~KTreemapPage() {
871   // NOP
872 }
873 
apply()874 void KTreemapPage::apply() {
875   KConfigGroup config = KSharedConfig::openConfig()->group("Treemaps");
876 
877   config.writeEntry("Squarify", _squarify->isChecked());
878   config.writeEntry("CushionShading", _doCushionShading->isChecked());
879   config.writeEntry("AmbientLight", _ambientLight->value());
880   config.writeEntry("HeightScaleFactor", _heightScalePercent->value() / 100.0);
881   config.writeEntry("EnsureContrast", _ensureContrast->isChecked());
882   config.writeEntry("ForceCushionGrid", _forceCushionGrid->isChecked());
883   config.writeEntry("MinTileSize", _minTileSize->value());
884   config.writeEntry("AutoResize", _autoResize->isChecked());
885   config.writeEntry("CushionGridColor", _cushionGridColor->color());
886   config.writeEntry("OutlineColor", _outlineColor->color());
887   config.writeEntry("FileFillColor", _fileFillColor->color());
888   config.writeEntry("DirFillColor", _dirFillColor->color());
889   config.writeEntry("HighlightColor", _highlightColor->color());
890 
891   if (treemapView()) {
892     treemapView()->readConfig();
893     treemapView()->rebuildTreemap();
894   }
895 }
896 
revertToDefaults()897 void KTreemapPage::revertToDefaults() {
898   _squarify->setChecked(true);
899   _doCushionShading->setChecked(true);
900 
901   _ambientLight->setValue(DefaultAmbientLight);
902   _heightScalePercent->setValue(DefaultHeightScalePercent);
903   _ensureContrast->setChecked(true);
904   _forceCushionGrid->setChecked(false);
905   _minTileSize->setValue(DefaultMinTileSize);
906   _autoResize->setChecked(true);
907 
908   _cushionGridColor->setColor(QColor(0x80, 0x80, 0x80));
909   _outlineColor->setColor(QColor(Qt::black));
910   _fileFillColor->setColor(QColor(0xde, 0x8d, 0x53));
911   _dirFillColor->setColor(QColor(0x10, 0x7d, 0xb4));
912   _highlightColor->setColor(QColor(Qt::red));
913 }
914 
setup()915 void KTreemapPage::setup() {
916   KConfigGroup config = KSharedConfig::openConfig()->group("Treemaps");
917 
918   _squarify->setChecked(config.readEntry("Squarify", true));
919   _doCushionShading->setChecked(config.readEntry("CushionShading", true));
920 
921   _ambientLight->setValue(
922       config.readEntry("AmbientLight", DefaultAmbientLight));
923   _heightScalePercent->setValue(
924       (int)(100 *
925             config.readEntry("HeightScaleFactor", DefaultHeightScaleFactor)));
926   _ensureContrast->setChecked(config.readEntry("EnsureContrast", true));
927   _forceCushionGrid->setChecked(config.readEntry("ForceCushionGrid", false));
928   _minTileSize->setValue(config.readEntry("MinTileSize", DefaultMinTileSize));
929   _autoResize->setChecked(config.readEntry("AutoResize", true));
930 
931   _cushionGridColor->setColor(
932       readColorEntry(config, "CushionGridColor", QColor(0x80, 0x80, 0x80)));
933   _outlineColor->setColor(
934       readColorEntry(config, "OutlineColor", QColor(Qt::black)));
935   _fileFillColor->setColor(
936       readColorEntry(config, "FileFillColor", QColor(0xde, 0x8d, 0x53)));
937   _dirFillColor->setColor(
938       readColorEntry(config, "DirFillColor", QColor(0x10, 0x7d, 0xb4)));
939   _highlightColor->setColor(
940       readColorEntry(config, "HighlightColor", QColor(Qt::red)));
941 
942   _ambientLightSB->setValue(_ambientLight->value());
943   _heightScalePercentSB->setValue(_heightScalePercent->value());
944 
945   checkEnabledState();
946 }
947 
checkEnabledState()948 void KTreemapPage::checkEnabledState() {
949   _cushionParams->setEnabled(_doCushionShading->isChecked());
950   _plainTileParams->setEnabled(!_doCushionShading->isChecked());
951 
952   if (_doCushionShading->isChecked()) {
953     _cushionGridColor->setEnabled(_forceCushionGrid->isChecked());
954     _cushionGridColorL->setEnabled(_forceCushionGrid->isChecked());
955     _ensureContrast->setEnabled(!_forceCushionGrid->isChecked());
956   }
957 }
958 
readColorEntry(KConfigGroup config,const char * entryName,QColor defaultColor)959 QColor KTreemapPage::readColorEntry(KConfigGroup config, const char *entryName,
960                                     QColor defaultColor) {
961   return config.readEntry(entryName, defaultColor);
962 }
963 
addHStretch(QWidget * parent)964 void addHStretch(QWidget *parent) {
965   QWidget *stretch = new QWidget(parent);
966   QSizePolicy sp(QSizePolicy::Expanding, QSizePolicy::Minimum);
967   sp.setHorizontalStretch(1);
968   sp.setVerticalStretch(0);
969   stretch->setSizePolicy(sp);
970 }
971 
addVStretch(QWidget * parent)972 void addVStretch(QWidget *parent) {
973   QWidget *stretch = new QWidget(parent);
974   QSizePolicy sp(QSizePolicy::Minimum, QSizePolicy::Expanding);
975   sp.setHorizontalStretch(0);
976   sp.setVerticalStretch(1);
977   stretch->setSizePolicy(sp);
978 }
979 
980