1 /*
2 * Copyright 2008-2021 Fabrice Colin
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18
19 #include <stdlib.h>
20 #include <iostream>
21 #include <algorithm>
22 #include <glibmm/convert.h>
23 #include <gdkmm/color.h>
24 #include <gtkmm/cellrenderertext.h>
25 #include <gtkmm/colorselection.h>
26 #include <gtkmm/entry.h>
27 #include <gtkmm/label.h>
28 #include <gtkmm/main.h>
29 #include <gtkmm/menu.h>
30 #include <gtkmm/messagedialog.h>
31
32 #include "config.h"
33 #include "NLS.h"
34 #include "StringManip.h"
35 #ifdef HAVE_DBUS
36 #include "DBusIndex.h"
37 #endif
38 #include "ModuleFactory.h"
39 #include "PinotUtils.h"
40 #include "PrefsWindow.h"
41
42 using namespace std;
43 using namespace Glib;
44 using namespace Gdk;
45 using namespace Gtk;
46
47 #ifdef HAVE_DBUS
48 class StartDaemonThread : public WorkerThread
49 {
50 public:
StartDaemonThread()51 StartDaemonThread() :
52 WorkerThread()
53 {
54 }
~StartDaemonThread()55 virtual ~StartDaemonThread()
56 {
57 }
58
getType(void) const59 virtual std::string getType(void) const
60 {
61 return "StartDaemonThread";
62 }
63
64 protected:
doWork(void)65 virtual void doWork(void)
66 {
67 // Ask the daemon to reload its configuration
68 // Let D-Bus activate the service if necessary
69 // We need a pure DBusIndex object
70 DBusIndex index(NULL);
71
72 index.reload();
73 }
74
75 private:
76 StartDaemonThread(const StartDaemonThread &other);
77 StartDaemonThread &operator=(const StartDaemonThread &other);
78
79 };
80 #endif
81
InternalState(PrefsWindow * pWindow)82 PrefsWindow::InternalState::InternalState(PrefsWindow *pWindow) :
83 QueueManager(PinotSettings::getInstance().m_docsIndexLocation),
84 m_savedPrefs(false)
85 {
86 m_onThreadEndSignal.connect(sigc::mem_fun(*pWindow, &PrefsWindow::on_thread_end));
87 }
88
~InternalState()89 PrefsWindow::InternalState::~InternalState()
90 {
91 }
92
PrefsWindow(_GtkWindow * & pParent,Glib::RefPtr<Gtk::Builder> & refBuilder)93 PrefsWindow::PrefsWindow(_GtkWindow *&pParent, Glib::RefPtr<Gtk::Builder>& refBuilder) :
94 Window(pParent),
95 m_settings(PinotSettings::getInstance()),
96 m_state(this)
97 {
98 refBuilder->get_widget("prefsCancelbutton", prefsCancelbutton);
99 refBuilder->get_widget("prefsOkbutton", prefsOkbutton);
100 refBuilder->get_widget("directoriesTreeview", directoriesTreeview);
101 refBuilder->get_widget("addDirectoryButton", addDirectoryButton);
102 refBuilder->get_widget("removeDirectoryButton", removeDirectoryButton);
103 refBuilder->get_widget("patternsTreeview", patternsTreeview);
104 refBuilder->get_widget("patternsCombobox", patternsCombobox);
105 refBuilder->get_widget("addPatternButton", addPatternButton);
106 refBuilder->get_widget("removePatternButton", removePatternButton);
107 refBuilder->get_widget("resetPatternsButton", resetPatternsButton);
108 refBuilder->get_widget("labelsTreeview", labelsTreeview);
109 refBuilder->get_widget("addLabelButton", addLabelButton);
110 refBuilder->get_widget("removeLabelButton", removeLabelButton);
111 refBuilder->get_widget("directConnectionRadiobutton", directConnectionRadiobutton);
112 refBuilder->get_widget("proxyRadiobutton", proxyRadiobutton);
113 refBuilder->get_widget("proxyAddressEntry", proxyAddressEntry);
114 refBuilder->get_widget("proxyPortSpinbutton", proxyPortSpinbutton);
115 refBuilder->get_widget("proxyTypeCombobox", proxyTypeCombobox);
116 refBuilder->get_widget("apiKeyLabel", apiKeyLabel);
117 refBuilder->get_widget("apiKeyEntry", apiKeyEntry);
118 refBuilder->get_widget("enableCompletionCheckbutton", enableCompletionCheckbutton);
119 refBuilder->get_widget("newResultsColorbutton", newResultsColorbutton);
120 refBuilder->get_widget("ignoreRobotsCheckbutton", ignoreRobotsCheckbutton);
121 refBuilder->get_widget("robotsLabels", robotsLabels);
122 refBuilder->get_widget("generalTable", generalTable);
123 refBuilder->get_widget("prefsNotebook", prefsNotebook);
124
125 prefsCancelbutton->signal_clicked().connect(sigc::mem_fun(*this, &PrefsWindow::on_prefsCancelbutton_clicked), false);
126 prefsOkbutton->signal_clicked().connect(sigc::mem_fun(*this, &PrefsWindow::on_prefsOkbutton_clicked), false);
127 addDirectoryButton->signal_clicked().connect(sigc::mem_fun(*this, &PrefsWindow::on_addDirectoryButton_clicked), false);
128 removeDirectoryButton->signal_clicked().connect(sigc::mem_fun(*this, &PrefsWindow::on_removeDirectoryButton_clicked), false);
129 patternsCombobox->signal_changed().connect(sigc::mem_fun(*this, &PrefsWindow::on_patternsCombobox_changed), false);
130 addPatternButton->signal_clicked().connect(sigc::mem_fun(*this, &PrefsWindow::on_addPatternButton_clicked), false);
131 removePatternButton->signal_clicked().connect(sigc::mem_fun(*this, &PrefsWindow::on_removePatternButton_clicked), false);
132 resetPatternsButton->signal_clicked().connect(sigc::mem_fun(*this, &PrefsWindow::on_resetPatternsButton_clicked), false);
133 addLabelButton->signal_clicked().connect(sigc::mem_fun(*this, &PrefsWindow::on_addLabelButton_clicked), false);
134 removeLabelButton->signal_clicked().connect(sigc::mem_fun(*this, &PrefsWindow::on_removeLabelButton_clicked), false);
135 directConnectionRadiobutton->signal_toggled().connect(sigc::mem_fun(*this, &PrefsWindow::on_directConnectionRadiobutton_toggled), false);
136 signal_delete_event().connect(sigc::mem_fun(*this, &PrefsWindow::on_prefsWindow_delete_event), false);
137
138 Color newColour;
139 ustring desktopName(_("File Indexing and Search"));
140 ustring desktopComment(_("Configure Pinot to index your files"));
141
142 newColour.set_red(m_settings.m_newResultsColourRed);
143 newColour.set_green(m_settings.m_newResultsColourGreen);
144 newColour.set_blue(m_settings.m_newResultsColourBlue);
145
146 // Initialize widgets
147 // Ignore robots directives
148 ignoreRobotsCheckbutton->set_active(m_settings.m_ignoreRobotsDirectives);
149 // New results colour
150 newResultsColorbutton->set_color(newColour);
151 // Enable terms suggestion
152 enableCompletionCheckbutton->set_active(m_settings.m_suggestQueryTerms);
153
154 // Any plugin editable parameter ?
155 if (m_settings.m_editablePluginValues.empty() == false)
156 {
157 Glib::PropertyProxy<guint> columnsProp(generalTable->property_n_columns());
158 Glib::PropertyProxy<guint> rowsProp(generalTable->property_n_rows());
159 guint rowsCount = rowsProp.get_value();
160
161 #ifdef DEBUG
162 clog << "PrefsWindow: adding " << m_settings.m_editablePluginValues.size() << " more rows" << endl;
163 #endif
164 generalTable->resize(rowsCount + m_settings.m_editablePluginValues.size(), columnsProp.get_value());
165
166 for (std::map<string, string>::const_iterator valueIter = m_settings.m_editablePluginValues.begin();
167 valueIter != m_settings.m_editablePluginValues.end(); ++valueIter)
168 {
169 ++rowsCount;
170 attach_value_widgets(valueIter->first, valueIter->second, rowsCount);
171 }
172 }
173
174 populate_proxyTypeCombobox();
175 proxyRadiobutton->set_active(m_settings.m_proxyEnabled);
176 proxyAddressEntry->set_text(m_settings.m_proxyAddress);
177 proxyPortSpinbutton->set_value((double)m_settings.m_proxyPort);
178 int proxyType = 0;
179 if (m_settings.m_proxyType == "SOCKS4")
180 {
181 proxyType = 1;
182 }
183 else if (m_settings.m_proxyType == "SOCKS5")
184 {
185 proxyType = 2;
186 }
187 proxyTypeCombobox->set_active(proxyType);
188 on_directConnectionRadiobutton_toggled();
189
190 // Associate the columns model to the labels tree
191 m_refLabelsTree = ListStore::create(m_labelsColumns);
192 labelsTreeview->set_model(m_refLabelsTree);
193 labelsTreeview->append_column_editable(_("Name"), m_labelsColumns.m_name);
194 // Allow only single selection
195 labelsTreeview->get_selection()->set_mode(SELECTION_SINGLE);
196 populate_labelsTreeview();
197
198 // Associate the columns model to the directories tree
199 m_refDirectoriesTree = ListStore::create(m_directoriesColumns);
200 directoriesTreeview->set_model(m_refDirectoriesTree);
201 directoriesTreeview->append_column_editable(_("Monitor"), m_directoriesColumns.m_monitor);
202 directoriesTreeview->append_column(_("Location"), m_directoriesColumns.m_location);
203 // Allow only single selection
204 directoriesTreeview->get_selection()->set_mode(SELECTION_SINGLE);
205 populate_directoriesTreeview();
206
207 // Associate the columns model to the file patterns tree
208 m_refPatternsTree = ListStore::create(m_patternsColumns);
209 patternsTreeview->set_model(m_refPatternsTree);
210 patternsTreeview->append_column_editable(_("Pattern"), m_patternsColumns.m_location);
211 // Allow only single selection
212 patternsTreeview->get_selection()->set_mode(SELECTION_SINGLE);
213 populate_patternsCombobox();
214 populate_patternsTreeview(m_settings.m_filePatternsList, m_settings.m_isBlackList);
215
216 // Hide the Google API entry field ?
217 if (ModuleFactory::isSupported("googleapi") == false)
218 {
219 apiKeyLabel->hide();
220 apiKeyEntry->hide();
221 }
222
223 // Connect to threads' finished signal
224 m_state.connect();
225 }
226
~PrefsWindow()227 PrefsWindow::~PrefsWindow()
228 {
229 }
230
updateLabelRow(const ustring & path_string,const ustring & text)231 void PrefsWindow::updateLabelRow(const ustring &path_string, const ustring &text)
232 {
233 Gtk::TreePath path(path_string);
234
235 // Get the row
236 TreeModel::iterator iter = m_refLabelsTree->get_iter(path);
237 if (iter)
238 {
239 TreeRow row = *iter;
240
241 #ifdef DEBUG
242 clog << "PrefsWindow::updateLabelRow: set label to " << text << endl;
243 #endif
244 // Set the value of the name column
245 row.set_value(m_labelsColumns.m_name, (ustring)text);
246 }
247 }
248
renderLabelNameColumn(CellRenderer * pRenderer,const TreeModel::iterator & iter)249 void PrefsWindow::renderLabelNameColumn(CellRenderer *pRenderer, const TreeModel::iterator &iter)
250 {
251 TreeModel::Row row = *iter;
252
253 if (pRenderer == NULL)
254 {
255 return;
256 }
257
258 CellRendererText *pTextRenderer = dynamic_cast<CellRendererText*>(pRenderer);
259 if (pTextRenderer != NULL)
260 {
261 bool isNewLabel = false;
262
263 // Is this a new label ?
264 if (row[m_labelsColumns.m_enabled] == false)
265 {
266 isNewLabel = true;
267 }
268
269 // Set the editable property
270 #ifdef GLIBMM_PROPERTIES_ENABLED
271 pTextRenderer->property_editable() = isNewLabel;
272 #else
273 pTextRenderer->set_property("editable", isNewLabel);
274 #endif
275 }
276 }
277
on_thread_end(WorkerThread * pThread)278 void PrefsWindow::on_thread_end(WorkerThread *pThread)
279 {
280 bool canQuit = false;
281
282 if (pThread == NULL)
283 {
284 return;
285 }
286
287 // Any thread still running ?
288 unsigned int threadsCount = m_state.get_threads_count();
289
290 // What type of thread was it ?
291 string type = pThread->getType();
292 // Did the thread fail ?
293 string status = pThread->getStatus();
294 if (status.empty() == false)
295 {
296 #ifdef DEBUG
297 clog << "PrefsWindow::on_thread_end: " << status << endl;
298 #endif
299 // FIXME: tell the user the thread failed
300 }
301 else if (type == "StartDaemonThread")
302 {
303 // Save the settings
304 m_state.m_savedPrefs = PinotSettings::getInstance().save(PinotSettings::SAVE_PREFS);
305
306 if (threadsCount == 0)
307 {
308 canQuit = true;
309 }
310 }
311 else if (type == "LabelUpdateThread")
312 {
313 if (threadsCount == 0)
314 {
315 canQuit = true;
316 }
317 }
318
319 // Delete the thread
320 delete pThread;
321
322 if (canQuit == false)
323 {
324 // We might be able to run a queued action
325 m_state.pop_queue();
326 }
327 else
328 {
329 #ifdef DEBUG
330 clog << "PrefsWindow::on_thread_end: quitting" << endl;
331 #endif
332 on_prefsWindow_delete_event(NULL);
333 }
334 }
335
attach_value_widgets(const string & name,const string & value,guint rowNumber)336 void PrefsWindow::attach_value_widgets(const string &name, const string &value, guint rowNumber)
337 {
338 Label *valueLabel = manage(new Label(name + ":"));
339 Entry *valueEntry = manage(new Entry());
340
341 // These settings are what Glade-- would use
342 valueLabel->set_alignment(0,0.5);
343 valueLabel->set_padding(4,4);
344 valueLabel->set_justify(Gtk::JUSTIFY_LEFT);
345 valueLabel->set_line_wrap(false);
346 valueLabel->set_use_markup(false);
347 valueLabel->set_selectable(false);
348 valueLabel->set_ellipsize(Pango::ELLIPSIZE_NONE);
349 valueLabel->set_width_chars(-1);
350 valueLabel->set_angle(0);
351 valueLabel->set_single_line_mode(false);
352
353 valueEntry->set_can_focus();
354 valueEntry->set_visibility(true);
355 valueEntry->set_editable(true);
356 valueEntry->set_max_length(0);
357 valueEntry->set_has_frame(true);
358 valueEntry->set_activates_default(false);
359
360 valueEntry->set_text(to_utf8(value));
361
362 generalTable->attach(*valueLabel, 0, 1, rowNumber, rowNumber + 1, Gtk::FILL, Gtk::FILL, 0, 0);
363 generalTable->attach(*valueEntry, 1, 2, rowNumber, rowNumber + 1, Gtk::EXPAND|Gtk::FILL, Gtk::FILL, 4, 4);
364
365 m_editableValueEntries.push_back(valueEntry);
366
367 valueLabel->show();
368 valueEntry->show();
369 }
370
populate_proxyTypeCombobox()371 void PrefsWindow::populate_proxyTypeCombobox()
372 {
373 proxyTypeCombobox->append("HTTP");
374 proxyTypeCombobox->append("SOCKS v4");
375 proxyTypeCombobox->append("SOCKS v5");
376 }
377
populate_labelsTreeview()378 void PrefsWindow::populate_labelsTreeview()
379 {
380 set<string> &labels = m_settings.m_labels;
381
382 // Now this can be enabled
383 addLabelButton->set_sensitive(true);
384
385 if (labels.empty() == true)
386 {
387 // This button will stay disabled until labels are added to the list
388 removeLabelButton->set_sensitive(false);
389 return;
390 }
391
392 // Populate the tree
393 for (set<string>::const_iterator labelIter = labels.begin();
394 labelIter != labels.end();
395 ++labelIter)
396 {
397 // Create a new row
398 TreeModel::iterator iter = m_refLabelsTree->append();
399 TreeModel::Row row = *iter;
400 // Set its name
401 row[m_labelsColumns.m_name] = *labelIter;
402 // This allows to differentiate existing labels from new labels the user may create
403 row[m_labelsColumns.m_enabled] = true;
404 }
405
406 removeLabelButton->set_sensitive(true);
407 }
408
save_labelsTreeview()409 void PrefsWindow::save_labelsTreeview()
410 {
411 set<string> &labels = m_settings.m_labels;
412
413 labels.clear();
414
415 // Go through the labels tree
416 TreeModel::Children children = m_refLabelsTree->children();
417 if (children.empty() == false)
418 {
419 TreeModel::Children::iterator iter = children.begin();
420 for (; iter != children.end(); ++iter)
421 {
422 TreeModel::Row row = *iter;
423 ustring labelName(row[m_labelsColumns.m_name]);
424
425 // Check user didn't recreate this label after having deleted it
426 set<string>::iterator labelIter = m_deletedLabels.find(from_utf8(labelName));
427 if (labelIter != m_deletedLabels.end())
428 {
429 m_deletedLabels.erase(labelIter);
430 }
431 // Is this a new label ?
432 if (row[m_labelsColumns.m_enabled] == false)
433 {
434 m_addedLabels.insert(from_utf8(labelName));
435 }
436
437 #ifdef DEBUG
438 clog << "PrefsWindow::save_labelsTreeview: " << labelName << endl;
439 #endif
440 // Add this new label to the settings
441 labels.insert(labelName);
442 }
443 }
444 }
445
populate_directoriesTreeview()446 void PrefsWindow::populate_directoriesTreeview()
447 {
448 ustring dirsString;
449
450 if (m_settings.m_indexableLocations.empty() == true)
451 {
452 // This button will stay disabled until directories are added to the list
453 removeDirectoryButton->set_sensitive(false);
454 return;
455 }
456
457 // Populate the tree
458 for (set<PinotSettings::IndexableLocation>::iterator dirIter = m_settings.m_indexableLocations.begin();
459 dirIter != m_settings.m_indexableLocations.end();
460 ++dirIter)
461 {
462 // Create a new row
463 TreeModel::iterator iter = m_refDirectoriesTree->append();
464 TreeModel::Row row = *iter;
465 row[m_directoriesColumns.m_monitor] = dirIter->m_monitor;
466 row[m_directoriesColumns.m_location] = dirIter->m_name;
467 dirsString += dirIter->m_name + (dirIter->m_monitor == true ? "1" : "0") + "|";
468 }
469
470 m_directoriesHash = StringManip::hashString(dirsString);
471 removeDirectoryButton->set_sensitive(true);
472 }
473
save_directoriesTreeview()474 bool PrefsWindow::save_directoriesTreeview()
475 {
476 string dirsString;
477
478 // Clear the current settings
479 m_settings.m_indexableLocations.clear();
480
481 // Go through the directories tree
482 TreeModel::Children children = m_refDirectoriesTree->children();
483 if (children.empty() == false)
484 {
485 TreeModel::Children::iterator iter = children.begin();
486 for (; iter != children.end(); ++iter)
487 {
488 TreeModel::Row row = *iter;
489 PinotSettings::IndexableLocation indexableLocation;
490
491 // Add this new directory to the settings
492 indexableLocation.m_monitor = row[m_directoriesColumns.m_monitor];
493 indexableLocation.m_name = row[m_directoriesColumns.m_location];
494
495 string dirLabel("file://");
496 dirLabel += from_utf8(indexableLocation.m_name);
497
498 // Check user didn't recreate this directory after having deleted it
499 set<string>::iterator dirIter = m_deletedDirectories.find(dirLabel);
500 if (dirIter != m_deletedDirectories.end())
501 {
502 m_deletedDirectories.erase(dirIter);
503 }
504
505 #ifdef DEBUG
506 clog << "PrefsWindow::save_directoriesTreeview: " << indexableLocation.m_name << endl;
507 #endif
508 m_settings.m_indexableLocations.insert(indexableLocation);
509 dirsString += indexableLocation.m_name + (indexableLocation.m_monitor == true ? "1" : "0") + "|";
510 }
511 }
512
513 if (m_directoriesHash != StringManip::hashString(dirsString))
514 {
515 #ifdef DEBUG
516 clog << "PrefsWindow::save_directoriesTreeview: directories changed" << endl;
517 #endif
518 return true;
519 }
520
521 return false;
522 }
523
populate_patternsCombobox()524 void PrefsWindow::populate_patternsCombobox()
525 {
526 patternsCombobox->append(_("Exclude these patterns from indexing"));
527 patternsCombobox->append(_("Only index these patterns"));
528 }
529
populate_patternsTreeview(const set<ustring> & patternsList,bool isBlackList)530 void PrefsWindow::populate_patternsTreeview(const set<ustring> &patternsList, bool isBlackList)
531 {
532 ustring patternsString;
533
534 if (patternsList.empty() == true)
535 {
536 // This button will stay disabled until a ppatern is added to the list
537 removePatternButton->set_sensitive(false);
538 return;
539 }
540
541 // Populate the tree
542 for (set<ustring>::iterator patternIter = patternsList.begin();
543 patternIter != patternsList.end();
544 ++patternIter)
545 {
546 ustring pattern(*patternIter);
547
548 // Create a new row
549 TreeModel::iterator iter = m_refPatternsTree->append();
550 TreeModel::Row row = *iter;
551 // Set its name
552 row[m_patternsColumns.m_location] = pattern;
553 patternsString += pattern + "|";
554 }
555
556 removePatternButton->set_sensitive(true);
557
558 // Is it a black or white list ?
559 if (isBlackList == true)
560 {
561 patternsCombobox->set_active(0);
562 patternsString += "0";
563 }
564 else
565 {
566 patternsCombobox->set_active(1);
567 patternsString += "1";
568 }
569
570 m_patternsHash = StringManip::hashString(patternsString);
571 }
572
save_patternsTreeview()573 bool PrefsWindow::save_patternsTreeview()
574 {
575 ustring patternsString;
576
577 // Clear the current settings
578 m_settings.m_filePatternsList.clear();
579
580 // Go through the file patterns tree
581 TreeModel::Children children = m_refPatternsTree->children();
582 if (children.empty() == false)
583 {
584 TreeModel::Children::iterator iter = children.begin();
585 for (; iter != children.end(); ++iter)
586 {
587 TreeModel::Row row = *iter;
588 ustring pattern(row[m_patternsColumns.m_location]);
589
590 if (pattern.empty() == false)
591 {
592 m_settings.m_filePatternsList.insert(pattern);
593 patternsString += pattern + "|";
594 }
595 }
596 }
597 if (patternsCombobox->get_active_row_number() == 0)
598 {
599 m_settings.m_isBlackList = true;
600 patternsString += "0";
601 }
602 else
603 {
604 m_settings.m_isBlackList = false;
605 patternsString += "1";
606 }
607
608 if (m_patternsHash != StringManip::hashString(patternsString))
609 {
610 #ifdef DEBUG
611 clog << "PrefsWindow::save_patternsTreeview: patterns changed" << endl;
612 #endif
613 return true;
614 }
615
616 return false;
617 }
618
on_prefsCancelbutton_clicked()619 void PrefsWindow::on_prefsCancelbutton_clicked()
620 {
621 on_prefsWindow_delete_event(NULL);
622 }
623
on_prefsOkbutton_clicked()624 void PrefsWindow::on_prefsOkbutton_clicked()
625 {
626 bool startedThread = false;
627
628 // Disable the buttons
629 prefsCancelbutton->set_sensitive(false);
630 prefsOkbutton->set_sensitive(false);
631
632 // Synchronise widgets with settings
633 m_settings.m_ignoreRobotsDirectives = ignoreRobotsCheckbutton->get_active();
634 Color newColour = newResultsColorbutton->get_color();
635 m_settings.m_newResultsColourRed = newColour.get_red();
636 m_settings.m_newResultsColourGreen = newColour.get_green();
637 m_settings.m_newResultsColourBlue = newColour.get_blue();
638 m_settings.m_suggestQueryTerms = enableCompletionCheckbutton->get_active();
639 // Any plugin editable parameter ?
640 if (m_settings.m_editablePluginValues.empty() == false)
641 {
642 std::map<string, string>::iterator valueIter = m_settings.m_editablePluginValues.begin();
643 vector<Entry *>::const_iterator entryIter = m_editableValueEntries.begin();
644 while ((valueIter != m_settings.m_editablePluginValues.end()) &&
645 (entryIter != m_editableValueEntries.end()))
646 {
647 ustring value((*entryIter)->get_text());
648
649 valueIter->second = from_utf8(value);
650
651 // Next
652 ++valueIter;
653 ++entryIter;
654 }
655 }
656
657 m_settings.m_proxyEnabled = proxyRadiobutton->get_active();
658 m_settings.m_proxyAddress = proxyAddressEntry->get_text();
659 m_settings.m_proxyPort = (unsigned int)proxyPortSpinbutton->get_value();
660 int proxyType = proxyTypeCombobox->get_active_row_number();
661 if (proxyType == 1)
662 {
663 m_settings.m_proxyType = "SOCKS4";
664 }
665 else if (proxyType == 2)
666 {
667 m_settings.m_proxyType = "SOCKS5";
668 }
669 else
670 {
671 m_settings.m_proxyType = "HTTP";
672 }
673
674 // Validate the current lists
675 save_labelsTreeview();
676 bool startForDirectories = save_directoriesTreeview();
677 bool startForPatterns = save_patternsTreeview();
678 #ifdef HAVE_DBUS
679 if ((startForDirectories == true) ||
680 (startForPatterns == true))
681 {
682 StartDaemonThread *pThread = new StartDaemonThread();
683
684 if (m_state.start_thread(pThread) == false)
685 {
686 delete pThread;
687 }
688 else
689 {
690 startedThread = true;
691 }
692 }
693 #endif
694 if ((m_addedLabels.empty() == false) ||
695 (m_deletedLabels.empty() == false))
696 {
697 LabelUpdateThread *pThread = new LabelUpdateThread(m_addedLabels, m_deletedLabels);
698
699 if (m_state.start_thread(pThread) == false)
700 {
701 delete pThread;
702 }
703 else
704 {
705 startedThread = true;
706 }
707 }
708
709 if (startedThread == false)
710 {
711 on_prefsWindow_delete_event(NULL);
712 }
713 // FIXME: else, disable all buttons or provide some visual feedback, until threads are done
714 }
715
on_addDirectoryButton_clicked()716 void PrefsWindow::on_addDirectoryButton_clicked()
717 {
718 ustring dirName;
719
720 TreeModel::Children children = m_refDirectoriesTree->children();
721 bool wasEmpty = children.empty();
722
723 if (select_file_name(_("Directory to index"), dirName, true, true) == true)
724 {
725 #ifdef DEBUG
726 clog << "PrefsWindow::on_addDirectoryButton_clicked: "
727 << dirName << endl;
728 #endif
729 // Create a new entry in the directories list
730 TreeModel::iterator iter = m_refDirectoriesTree->append();
731 TreeModel::Row row = *iter;
732
733 row[m_directoriesColumns.m_monitor] = false;
734 row[m_directoriesColumns.m_location] = to_utf8(dirName);
735
736 if (wasEmpty == true)
737 {
738 // Enable this button
739 removeDirectoryButton->set_sensitive(true);
740 }
741 }
742 }
743
on_removeDirectoryButton_clicked()744 void PrefsWindow::on_removeDirectoryButton_clicked()
745 {
746 // Get the selected directory in the list
747 TreeModel::iterator iter = directoriesTreeview->get_selection()->get_selected();
748 if (iter)
749 {
750 string dirLabel("file://");
751
752 // Unselect
753 directoriesTreeview->get_selection()->unselect(iter);
754 // Select another row
755 TreeModel::Path dirPath = m_refDirectoriesTree->get_path(iter);
756 dirPath.next();
757 directoriesTreeview->get_selection()->select(dirPath);
758
759 // Erase
760 TreeModel::Row row = *iter;
761 dirLabel += from_utf8(row[m_directoriesColumns.m_location]);
762 m_deletedDirectories.insert(dirLabel);
763 m_refDirectoriesTree->erase(row);
764
765 TreeModel::Children children = m_refDirectoriesTree->children();
766 if (children.empty() == true)
767 {
768 // Disable this button
769 removeDirectoryButton->set_sensitive(false);
770 }
771 }
772 }
773
on_patternsCombobox_changed()774 void PrefsWindow::on_patternsCombobox_changed()
775 {
776 int activeRow = patternsCombobox->get_active_row_number();
777
778 if (((activeRow == 0) && (m_settings.m_isBlackList == true)) ||
779 ((activeRow > 0) && (m_settings.m_isBlackList == false)))
780 {
781 // No change
782 return;
783 }
784
785 // Unselect
786 patternsTreeview->get_selection()->unselect_all();
787 // Remove all patterns in order to make sure the user enters a new bunch
788 m_refPatternsTree->clear();
789 }
790
on_addPatternButton_clicked()791 void PrefsWindow::on_addPatternButton_clicked()
792 {
793 TreeModel::Children children = m_refPatternsTree->children();
794 bool wasEmpty = children.empty();
795
796 // Create a new entry in the file patterns list
797 TreeModel::iterator iter = m_refPatternsTree->append();
798 TreeModel::Row row = *iter;
799
800 row[m_patternsColumns.m_location] = "";
801 row[m_patternsColumns.m_mTime] = time(NULL);
802
803 // Select and start editing the row
804 TreeViewColumn *pColumn = patternsTreeview->get_column(0);
805 if (pColumn != NULL)
806 {
807 TreeModel::Path patternPath = m_refPatternsTree->get_path(iter);
808 patternsTreeview->set_cursor(patternPath, *pColumn, true);
809 }
810
811 if (wasEmpty == true)
812 {
813 // Enable this button
814 removePatternButton->set_sensitive(true);
815 }
816 }
817
on_removePatternButton_clicked()818 void PrefsWindow::on_removePatternButton_clicked()
819 {
820 // Get the selected file pattern in the list
821 TreeModel::iterator iter = patternsTreeview->get_selection()->get_selected();
822 if (iter)
823 {
824 // Unselect
825 patternsTreeview->get_selection()->unselect(iter);
826 // Select another row
827 TreeModel::Path patternPath = m_refPatternsTree->get_path(iter);
828 patternPath.next();
829 patternsTreeview->get_selection()->select(patternPath);
830
831 // Erase
832 TreeModel::Row row = *iter;
833 m_refPatternsTree->erase(row);
834
835 TreeModel::Children children = m_refPatternsTree->children();
836 if (children.empty() == true)
837 {
838 // Disable this button
839 removePatternButton->set_sensitive(false);
840 }
841 }
842 }
843
on_resetPatternsButton_clicked()844 void PrefsWindow::on_resetPatternsButton_clicked()
845 {
846 set<ustring> defaultPatterns;
847 bool isBlackList = m_settings.getDefaultPatterns(defaultPatterns);
848
849 // Unselect
850 patternsTreeview->get_selection()->unselect_all();
851 // Remove all patterns
852 m_refPatternsTree->clear();
853
854 // Repopulate with defaults
855 populate_patternsTreeview(defaultPatterns, isBlackList);
856 }
857
on_addLabelButton_clicked()858 void PrefsWindow::on_addLabelButton_clicked()
859 {
860 // Now create a new entry in the labels list
861 TreeModel::iterator iter = m_refLabelsTree->append();
862 TreeModel::Row row = *iter;
863 row[m_labelsColumns.m_name] = to_utf8(_("New Label"));
864 // This marks the label as new
865 row[m_labelsColumns.m_enabled] = false;
866
867 // Enable this button
868 removeLabelButton->set_sensitive(true);
869 }
870
on_removeLabelButton_clicked()871 void PrefsWindow::on_removeLabelButton_clicked()
872 {
873 // Get the selected label in the list
874 TreeModel::iterator iter = labelsTreeview->get_selection()->get_selected();
875 if (iter)
876 {
877 // Unselect
878 labelsTreeview->get_selection()->unselect(iter);
879 // Select another row
880 TreeModel::Path labelPath = m_refLabelsTree->get_path(iter);
881 labelPath.next();
882 labelsTreeview->get_selection()->select(labelPath);
883 // Erase
884 TreeModel::Row row = *iter;
885 m_deletedLabels.insert(from_utf8(row[m_labelsColumns.m_name]));
886 m_refLabelsTree->erase(row);
887
888 TreeModel::Children children = m_refLabelsTree->children();
889 if (children.empty() == true)
890 {
891 // Disable this button
892 removeLabelButton->set_sensitive(false);
893 }
894 }
895 }
896
on_directConnectionRadiobutton_toggled()897 void PrefsWindow::on_directConnectionRadiobutton_toggled()
898 {
899 bool enabled = proxyRadiobutton->get_active();
900
901 proxyAddressEntry->set_sensitive(enabled);
902 proxyPortSpinbutton->set_sensitive(enabled);
903 proxyTypeCombobox->set_sensitive(enabled);
904 }
905
on_prefsWindow_delete_event(GdkEventAny * ev)906 bool PrefsWindow::on_prefsWindow_delete_event(GdkEventAny *ev)
907 {
908 // Any thread still running ?
909 if (m_state.get_threads_count() > 0)
910 {
911 ustring boxMsg(_("At least one task hasn't completed yet. Quit now ?"));
912 MessageDialog msgDialog(boxMsg, false, MESSAGE_QUESTION, BUTTONS_YES_NO);
913 msgDialog.set_title(_("Quit"));
914 msgDialog.set_transient_for(*this);
915 msgDialog.show();
916 int result = msgDialog.run();
917 if (result == RESPONSE_NO)
918 {
919 return true;
920 }
921
922 m_state.disconnect();
923 m_state.mustQuit(true);
924 }
925 else
926 {
927 m_state.disconnect();
928 }
929
930 if (m_state.m_savedPrefs == false)
931 {
932 // Save the settings
933 PinotSettings::getInstance().save(PinotSettings::SAVE_PREFS);
934 }
935
936 Main::quit();
937
938 return false;
939 }
940
941