Refactor range preference.
[metze/wireshark/wip.git] / ui / qt / preferences_dialog.cpp
1 /* preferences_dialog.cpp
2  *
3  * Wireshark - Network traffic analyzer
4  * By Gerald Combs <gerald@wireshark.org>
5  * Copyright 1998 Gerald Combs
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include "preferences_dialog.h"
23 #include <ui_preferences_dialog.h>
24
25 #include "module_preferences_scroll_area.h"
26
27 #ifdef HAVE_LIBPCAP
28 #ifdef _WIN32
29 #include "caputils/capture-wpcap.h"
30 #endif /* _WIN32 */
31 #endif /* HAVE_LIBPCAP */
32
33 #include <epan/prefs-int.h>
34 #include <epan/decode_as.h>
35 #include <ui/language.h>
36 #include <ui/preference_utils.h>
37 #include <ui/simple_dialog.h>
38 #include <main_window.h>
39
40 #include "syntax_line_edit.h"
41 #include "qt_ui_utils.h"
42 #include "uat_dialog.h"
43 #include "wireshark_application.h"
44
45 #include <QColorDialog>
46 #include <QComboBox>
47 #include <QFileDialog>
48 #include <QFrame>
49 #include <QHBoxLayout>
50 #include <QKeyEvent>
51 #include <QLineEdit>
52 #include <QMessageBox>
53 #include <QSpacerItem>
54 #include <QTreeWidgetItemIterator>
55
56 Q_DECLARE_METATYPE(ModulePreferencesScrollArea *)
57 Q_DECLARE_METATYPE(pref_t *)
58 Q_DECLARE_METATYPE(QStackedWidget *)
59
60 // XXX Should we move this to ui/preference_utils?
61 static QHash<void *, pref_t *> pref_ptr_to_pref_;
62 pref_t *prefFromPrefPtr(void *pref_ptr)
63 {
64     return pref_ptr_to_pref_[pref_ptr];
65 }
66
67 enum {
68     module_type_ = 1000,
69     advanced_type_,
70 };
71
72 enum {
73     adv_name_col_,
74     adv_status_col_,
75     adv_type_col_,
76     adv_value_col_
77 };
78
79 enum {
80     stacked_role_ = Qt::UserRole + 1,   // pd_ui_->stackedWidget
81     module_name_role_,                  // QString
82     mpsa_role_                          // QWidget *
83 };
84
85 class AdvancedPrefTreeWidgetItem : public QTreeWidgetItem
86 {
87 public:
88     AdvancedPrefTreeWidgetItem(pref_t *pref, module_t *module) :
89         QTreeWidgetItem (advanced_type_),
90         pref_(pref),
91         module_(module)
92     {}
93     pref_t *pref() { return pref_; }
94     void updatePref() { emitDataChanged(); }
95
96     virtual QVariant data(int column, int role) const {
97         bool is_default = prefs_pref_is_default(pref_);
98         switch (role) {
99         case Qt::DisplayRole:
100             switch (column) {
101             case adv_name_col_:
102             {
103                 QString full_name = QString(module_->name ? module_->name : module_->parent->name);
104                 full_name += QString(".%1").arg(pref_->name);
105                 return full_name;
106             }
107             case adv_status_col_:
108                 if ((pref_->type == PREF_UAT && (pref_->gui == GUI_ALL || pref_->gui == GUI_QT))|| pref_->type == PREF_CUSTOM) {
109                     return QObject::tr("Unknown");
110                 } else if (is_default) {
111                     return QObject::tr("Default");
112                 } else {
113                     return QObject::tr("Changed");
114                 }
115             case adv_type_col_:
116                 return QString(prefs_pref_type_name(pref_));
117             case adv_value_col_:
118             {
119                 QString cur_value = gchar_free_to_qstring(prefs_pref_to_str(pref_, pref_stashed)).remove(QRegExp("\n\t"));
120                 return cur_value;
121             }
122             default:
123                 break;
124             }
125             break;
126         case Qt::ToolTipRole:
127             switch (column) {
128             case adv_name_col_:
129                 return QString("<span>%1</span>").arg(pref_->description);
130             case adv_status_col_:
131                 return QObject::tr("Has this preference been changed?");
132             case adv_type_col_:
133             {
134                 QString type_desc = gchar_free_to_qstring(prefs_pref_type_description(pref_));
135                 return QString("<span>%1</span>").arg(type_desc);
136             }
137             case adv_value_col_:
138             {
139                 QString default_value = gchar_free_to_qstring(prefs_pref_to_str(pref_, pref_stashed));
140                 return QString("<span>%1</span>").arg(
141                             default_value.isEmpty() ? default_value : QObject::tr("Default value is empty"));
142             }
143             default:
144                 break;
145             }
146             break;
147         case Qt::FontRole:
148             if (!is_default && treeWidget()) {
149                 QFont font = treeWidget()->font();
150                 font.setBold(true);
151                 return font;
152             }
153             break;
154         default:
155             break;
156         }
157         return QTreeWidgetItem::data(column, role);
158     }
159
160 private:
161     pref_t *pref_;
162     module_t *module_;
163 };
164
165 class ModulePrefTreeWidgetItem : public QTreeWidgetItem
166 {
167 public:
168     ModulePrefTreeWidgetItem(QTreeWidgetItem *parent, module_t *module) :
169         QTreeWidgetItem (parent, module_type_),
170         module_(module),
171         mpsa_(0)
172     {}
173     void ensureModulePreferencesScrollArea(QStackedWidget *sw) {
174         /*
175          * We create pages for interior nodes even if they don't have
176          * preferences, so that we at least have something to show
177          * if the user clicks on them, even if it's empty.
178          */
179
180         /* Scrolled window */
181         if (!mpsa_) {
182             mpsa_ = new ModulePreferencesScrollArea(module_);
183             if (sw->indexOf(mpsa_) < 0) sw->addWidget(mpsa_);
184         }
185     }
186
187     virtual QVariant data(int column, int role) const {
188         if (column == 0) {
189             switch (role) {
190             case Qt::DisplayRole:
191                 return QString(module_->title);
192             case module_name_role_:
193                 return QString (module_->name);
194             case mpsa_role_:
195                 return qVariantFromValue(mpsa_);
196             default:
197                 break;
198             }
199         }
200         return QTreeWidgetItem::data(column, role);
201     }
202
203 private:
204     module_t *module_;
205     QWidget *mpsa_;
206 };
207
208 extern "C" {
209 // Callbacks prefs routines
210
211 static guint
212 fill_advanced_prefs(module_t *module, gpointer root_ptr)
213 {
214     QTreeWidgetItem *root_item = static_cast<QTreeWidgetItem *>(root_ptr);
215
216     if (!module || !root_item) return 1;
217
218     if (module->numprefs < 1 && !prefs_module_has_submodules(module)) return 0;
219
220     QString module_title = module->title;
221
222     QTreeWidgetItem *tl_item = new QTreeWidgetItem(root_item);
223     tl_item->setText(0, module_title);
224     tl_item->setToolTip(0, QString("<span>%1</span>").arg(module->description));
225     tl_item->setFirstColumnSpanned(true);
226     Qt::ItemFlags item_flags = tl_item->flags();
227     item_flags &= ~(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
228     tl_item->setFlags(item_flags);
229
230
231     QList<QTreeWidgetItem *>tl_children;
232     for (GList *pref_l = module->prefs; pref_l && pref_l->data; pref_l = g_list_next(pref_l)) {
233         pref_t *pref = (pref_t *) pref_l->data;
234
235         if (pref->type == PREF_OBSOLETE || pref->type == PREF_STATIC_TEXT) continue;
236
237         const char *type_name = prefs_pref_type_name(pref);
238         if (!type_name) continue;
239
240         pref_stash(pref, NULL);
241
242         AdvancedPrefTreeWidgetItem *item = new AdvancedPrefTreeWidgetItem(pref, module);
243         tl_children << item;
244
245         // .uat is a void * so it wins the "useful key value" prize.
246         if (pref->varp.uat) {
247             pref_ptr_to_pref_[pref->varp.uat] = pref;
248         }
249     }
250     tl_item->addChildren(tl_children);
251
252     if(prefs_module_has_submodules(module))
253         return prefs_modules_foreach_submodules(module, fill_advanced_prefs, tl_item);
254
255     return 0;
256 }
257
258 static guint
259 pref_exists(pref_t *, gpointer)
260 {
261     return 1;
262 }
263
264 static guint
265 fill_module_prefs(module_t *module, gpointer ti_ptr)
266 {
267     QTreeWidgetItem *item = static_cast<QTreeWidgetItem *>(ti_ptr);
268
269     if (!item) return 0;
270
271     QStackedWidget *stacked_widget = item->data(0, stacked_role_).value<QStackedWidget *>();
272
273     if (!stacked_widget) return 0;
274
275     if (!module->use_gui) {
276         /* This module uses its own GUI interface to modify its
277          * preferences, so ignore it
278          */
279         return 0;
280     }
281
282     /*
283      * Is this module an interior node, with modules underneath it?
284      */
285     if (!prefs_module_has_submodules(module)) {
286         /*
287          * No.
288          * Does it have any preferences (other than possibly obsolete ones)?
289          */
290         if (prefs_pref_foreach(module, pref_exists, NULL) == 0) {
291             /*
292              * No.  Don't put the module into the preferences window,
293              * as there's nothing to show.
294              *
295              * XXX - we should do the same for interior ndes; if the module
296              * has no non-obsolete preferences *and* nothing under it has
297              * non-obsolete preferences, don't put it into the window.
298              */
299             return 0;
300         }
301     }
302
303     /*
304      * Add this module to the tree.
305      */
306     ModulePrefTreeWidgetItem *new_mpti = new ModulePrefTreeWidgetItem(item, module);
307     new_mpti->setData(0, stacked_role_, item->data(0, stacked_role_));
308
309     /*
310      * Is this an interior node?
311      */
312     if (prefs_module_has_submodules(module)) {
313         /*
314          * Yes. Walk the subtree and attach stuff to it.
315          */
316         prefs_modules_foreach_submodules(module, fill_module_prefs, (gpointer) new_mpti);
317     }
318
319     return 0;
320 }
321
322 static guint
323 module_prefs_unstash(module_t *module, gpointer data)
324 {
325     gboolean *must_redissect_p = (gboolean *)data;
326     pref_unstash_data_t unstashed_data;
327
328     unstashed_data.handle_decode_as = TRUE;
329
330     module->prefs_changed = FALSE;        /* assume none of them changed */
331     for (GList *pref_l = module->prefs; pref_l && pref_l->data; pref_l = g_list_next(pref_l)) {
332         pref_t *pref = (pref_t *) pref_l->data;
333
334         if (pref->type == PREF_OBSOLETE || pref->type == PREF_STATIC_TEXT) continue;
335
336         unstashed_data.module = module;
337         pref_unstash(pref, &unstashed_data);
338     }
339
340     /* If any of them changed, indicate that we must redissect and refilter
341        the current capture (if we have one), as the preference change
342        could cause packets to be dissected differently. */
343     if (module->prefs_changed)
344         *must_redissect_p = TRUE;
345
346     if(prefs_module_has_submodules(module))
347         return prefs_modules_foreach_submodules(module, module_prefs_unstash, data);
348
349     return 0;     /* Keep unstashing. */
350 }
351
352 static guint
353 module_prefs_clean_stash(module_t *module, gpointer)
354 {
355     for (GList *pref_l = module->prefs; pref_l && pref_l->data; pref_l = g_list_next(pref_l)) {
356         pref_t *pref = (pref_t *) pref_l->data;
357
358         if (pref->type == PREF_OBSOLETE || pref->type == PREF_STATIC_TEXT) continue;
359
360         pref_clean_stash(pref, NULL);
361     }
362
363     if(prefs_module_has_submodules(module))
364         return prefs_modules_foreach_submodules(module, module_prefs_clean_stash, NULL);
365
366     return 0;     /* Keep cleaning modules */
367 }
368
369 } // extern "C"
370
371 // Preference tree items
372 const int appearance_item_ = 0;
373 const int capture_item_    = 1;
374
375 PreferencesDialog::PreferencesDialog(QWidget *parent) :
376     GeometryStateDialog(parent),
377     pd_ui_(new Ui::PreferencesDialog),
378     cur_pref_type_(0),
379     cur_line_edit_(NULL),
380     cur_combo_box_(NULL),
381     saved_combo_idx_(0)
382 {
383     QTreeWidgetItem tmp_item; // Adding pre-populated top-level items is much faster
384     prefs_modules_foreach_submodules(NULL, fill_advanced_prefs, (gpointer) &tmp_item);
385
386     // Some classes depend on pref_ptr_to_pref_ so this MUST be called after
387     // fill_advanced_prefs.
388     pd_ui_->setupUi(this);
389     loadGeometry();
390
391     setWindowTitle(wsApp->windowTitleString(tr("Preferences")));
392     pd_ui_->advancedTree->invisibleRootItem()->addChildren(tmp_item.takeChildren());
393
394     pd_ui_->splitter->setStretchFactor(0, 1);
395     pd_ui_->splitter->setStretchFactor(1, 5);
396
397     pd_ui_->prefsTree->invisibleRootItem()->child(appearance_item_)->setExpanded(true);
398
399     bool disable_capture = true;
400 #ifdef HAVE_LIBPCAP
401 #ifdef _WIN32
402     /* Is WPcap loaded? */
403     if (has_wpcap) {
404 #endif /* _WIN32 */
405         disable_capture = false;
406 #ifdef _WIN32
407     }
408 #endif /* _WIN32 */
409 #endif /* HAVE_LIBPCAP */
410     pd_ui_->prefsTree->invisibleRootItem()->child(capture_item_)->setDisabled(disable_capture);
411
412     // PreferencesPane, prefsTree, and stackedWidget must all correspond to each other.
413     // This may not be the best way to go about enforcing that.
414     QTreeWidgetItem *item = pd_ui_->prefsTree->topLevelItem(0);
415     item->setSelected(true);
416     pd_ui_->stackedWidget->setCurrentIndex(0);
417     for (int i = 0; i < pd_ui_->stackedWidget->count() && item; i++) {
418         item->setData(0, mpsa_role_, qVariantFromValue(pd_ui_->stackedWidget->widget(i)));
419         item = pd_ui_->prefsTree->itemBelow(item);
420     }
421     item = pd_ui_->prefsTree->topLevelItem(0);
422     prefs_pane_to_item_[ppAppearance] = item;
423     prefs_pane_to_item_[ppLayout] = item->child(0);
424     prefs_pane_to_item_[ppColumn] = item->child(1);
425     prefs_pane_to_item_[ppFontAndColor] = item->child(2);
426     prefs_pane_to_item_[ppCapture] = pd_ui_->prefsTree->topLevelItem(1);
427     prefs_pane_to_item_[ppFilterExpressions] = pd_ui_->prefsTree->topLevelItem(2);
428
429     // Printing prefs don't apply here.
430     module_t *print_module = prefs_find_module("print");
431     if (print_module) print_module->use_gui = FALSE;
432
433     // We called takeChildren above so this shouldn't be necessary.
434     while (tmp_item.childCount() > 0) {
435         tmp_item.removeChild(tmp_item.child(0));
436     }
437     tmp_item.setData(0, stacked_role_, qVariantFromValue(pd_ui_->stackedWidget));
438     prefs_modules_foreach_submodules(NULL, fill_module_prefs, (gpointer) &tmp_item);
439     pd_ui_->prefsTree->invisibleRootItem()->insertChildren(
440                 pd_ui_->prefsTree->invisibleRootItem()->childCount() - 1, tmp_item.takeChildren());
441 }
442
443 PreferencesDialog::~PreferencesDialog()
444 {
445     delete pd_ui_;
446     prefs_modules_foreach_submodules(NULL, module_prefs_clean_stash, NULL);
447 }
448
449 void PreferencesDialog::setPane(PreferencesDialog::PreferencesPane start_pane)
450 {
451     if (prefs_pane_to_item_.contains(start_pane)) {
452         pd_ui_->prefsTree->setCurrentItem(prefs_pane_to_item_[start_pane]);
453     }
454 }
455
456 // Only valid for ModulePrefTreeWidgetItems.
457 void PreferencesDialog::setPane(const QString module_name)
458 {
459     QTreeWidgetItemIterator pref_it(pd_ui_->prefsTree);
460     while (*pref_it) {
461         if ((*pref_it)->type() == module_type_) {
462             ModulePrefTreeWidgetItem *mp_ti = dynamic_cast<ModulePrefTreeWidgetItem *>(*pref_it);
463             // Ensure that the module's scroll area exists and that it's in the
464             // widget stack.
465             if (mp_ti) {
466                 QString mpsa_name = (*pref_it)->data(0, module_name_role_).toString();
467                 if (mpsa_name == module_name) {
468                     mp_ti->ensureModulePreferencesScrollArea(pd_ui_->stackedWidget);
469                     QWidget *mpsa = (*pref_it)->data(0, mpsa_role_).value<QWidget *>();
470                     if (mpsa) {
471                         pd_ui_->prefsTree->setCurrentItem((*pref_it));
472                         break;
473                     }
474                 }
475             }
476         }
477         ++pref_it;
478     }
479 }
480
481 void PreferencesDialog::showEvent(QShowEvent *)
482 {
483     QStyleOption style_opt;
484     int new_prefs_tree_width =  pd_ui_->prefsTree->style()->subElementRect(QStyle::SE_TreeViewDisclosureItem, &style_opt).left();
485     QList<int> sizes = pd_ui_->splitter->sizes();
486
487 #ifdef Q_OS_WIN
488     new_prefs_tree_width *= 2;
489 #endif
490     pd_ui_->prefsTree->resizeColumnToContents(0);
491     new_prefs_tree_width += pd_ui_->prefsTree->columnWidth(0);
492     pd_ui_->prefsTree->setMinimumWidth(new_prefs_tree_width);
493     sizes[1] += sizes[0] - new_prefs_tree_width;
494     sizes[0] = new_prefs_tree_width;
495     pd_ui_->splitter->setSizes(sizes);
496     pd_ui_->splitter->setStretchFactor(0, 1);
497
498     pd_ui_->advancedTree->expandAll();
499     pd_ui_->advancedTree->setSortingEnabled(true);
500     pd_ui_->advancedTree->sortByColumn(0, Qt::AscendingOrder);
501
502     int one_em = fontMetrics().height();
503     pd_ui_->advancedTree->setColumnWidth(adv_name_col_, one_em * 12); // Don't let long items widen things too much
504     pd_ui_->advancedTree->resizeColumnToContents(adv_status_col_);
505     pd_ui_->advancedTree->resizeColumnToContents(adv_type_col_);
506     pd_ui_->advancedTree->setColumnWidth(adv_value_col_, one_em * 30);
507 }
508
509 void PreferencesDialog::keyPressEvent(QKeyEvent *evt)
510 {
511     if (cur_line_edit_ && cur_line_edit_->hasFocus()) {
512         switch (evt->key()) {
513         case Qt::Key_Escape:
514             cur_line_edit_->setText(saved_string_pref_);
515             /* Fall Through */
516         case Qt::Key_Enter:
517         case Qt::Key_Return:
518             switch (cur_pref_type_) {
519             case PREF_UINT:
520                 uintPrefEditingFinished();
521                 break;
522             case PREF_STRING:
523                 stringPrefEditingFinished();
524                 break;
525             case PREF_RANGE:
526                 rangePrefEditingFinished();
527                 break;
528             default:
529                 break;
530             }
531
532             delete cur_line_edit_;
533             return;
534         default:
535             break;
536         }
537     } else if (cur_combo_box_ && cur_combo_box_->hasFocus()) {
538         switch (evt->key()) {
539         case Qt::Key_Escape:
540             cur_combo_box_->setCurrentIndex(saved_combo_idx_);
541             /* Fall Through */
542         case Qt::Key_Enter:
543         case Qt::Key_Return:
544             // XXX The combo box eats enter and return
545             enumPrefCurrentIndexChanged(cur_combo_box_->currentIndex());
546             delete cur_combo_box_;
547             return;
548         default:
549             break;
550         }
551     }
552     QDialog::keyPressEvent(evt);
553 }
554
555 void PreferencesDialog::on_prefsTree_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *)
556 {
557     if (!current) return;
558     QWidget *new_item = NULL;
559
560     // "current" might be a QTreeWidgetItem from our .ui file, e.g. "Columns"
561     // or a ModulePrefTreeWidgetItem created by fill_module_prefs, e.g. a
562     // protocol preference. If it's the latter, ensure that the module's
563     // scroll area exists and that it's in the widget stack.
564     if (current->type() == module_type_) {
565         ModulePrefTreeWidgetItem *mp_ti = dynamic_cast<ModulePrefTreeWidgetItem *>(current);
566         if (mp_ti) mp_ti->ensureModulePreferencesScrollArea(pd_ui_->stackedWidget);
567     }
568
569     new_item = current->data(0, mpsa_role_).value<QWidget *>();
570     g_assert(new_item != NULL);
571     pd_ui_->stackedWidget->setCurrentWidget(new_item);
572 }
573
574 void PreferencesDialog::on_advancedSearchLineEdit_textEdited(const QString &search_re)
575 {
576     QTreeWidgetItemIterator branch_it(pd_ui_->advancedTree);
577     QRegExp regex(search_re, Qt::CaseInsensitive);
578
579     // Hide or show everything
580     while (*branch_it) {
581         (*branch_it)->setHidden(!search_re.isEmpty());
582         ++branch_it;
583     }
584     if (search_re.isEmpty()) return;
585
586     // Hide or show each item, showing its parents if needed
587     QTreeWidgetItemIterator pref_it(pd_ui_->advancedTree);
588     while (*pref_it) {
589         bool hidden = true;
590
591         if ((*pref_it)->type() == advanced_type_) {
592
593             if ((*pref_it)->text(0).contains(regex) ||
594                 (*pref_it)->toolTip(0).contains(regex)) {
595                 hidden = false;
596             }
597
598             (*pref_it)->setHidden(hidden);
599             if (!hidden) {
600                 QTreeWidgetItem *parent = (*pref_it)->parent();
601                 while (parent) {
602                     parent->setHidden(false);
603                     parent = parent->parent();
604                 }
605             }
606         }
607         ++pref_it;
608     }
609 }
610
611 void PreferencesDialog::on_advancedTree_currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *previous)
612 {
613     if (previous && pd_ui_->advancedTree->itemWidget(previous, 3)) {
614         pd_ui_->advancedTree->removeItemWidget(previous, 3);
615     }
616 }
617
618 void PreferencesDialog::on_advancedTree_itemActivated(QTreeWidgetItem *item, int column)
619 {
620     AdvancedPrefTreeWidgetItem *adv_ti;
621     pref_t *pref = NULL;
622
623     if (item->type() == advanced_type_) {
624         adv_ti = dynamic_cast<AdvancedPrefTreeWidgetItem *>(item);
625         if (adv_ti) pref = adv_ti->pref();
626     }
627
628     if (!pref || cur_line_edit_ || cur_combo_box_) return;
629
630     if (column < 3) { // Reset to default
631         reset_stashed_pref(pref);
632         adv_ti->updatePref();
633     } else {
634         QWidget *editor = NULL;
635
636         switch (pref->type) {
637         case PREF_DECODE_AS_UINT:
638         {
639             cur_line_edit_ = new QLineEdit();
640 //            cur_line_edit_->setInputMask("0000000009;");
641             saved_string_pref_ = QString::number(pref->stashed_val.uint, pref->info.base);
642             connect(cur_line_edit_, SIGNAL(editingFinished()), this, SLOT(uintPrefEditingFinished()));
643             editor = cur_line_edit_;
644             break;
645         }
646         case PREF_UINT:
647         {
648             cur_line_edit_ = new QLineEdit();
649 //            cur_line_edit_->setInputMask("0000000009;");
650             saved_string_pref_ = QString::number(pref->stashed_val.uint, pref->info.base);
651             connect(cur_line_edit_, SIGNAL(editingFinished()), this, SLOT(uintPrefEditingFinished()));
652             editor = cur_line_edit_;
653             break;
654         }
655         case PREF_BOOL:
656             pref->stashed_val.boolval = !pref->stashed_val.boolval;
657             adv_ti->updatePref();
658             break;
659         case PREF_ENUM:
660         {
661             cur_combo_box_ = new QComboBox();
662             const enum_val_t *ev;
663             for (ev = pref->info.enum_info.enumvals; ev && ev->description; ev++) {
664                 cur_combo_box_->addItem(ev->description, QVariant(ev->value));
665                 if (pref->stashed_val.enumval == ev->value)
666                     cur_combo_box_->setCurrentIndex(cur_combo_box_->count() - 1);
667             }
668             saved_combo_idx_ = cur_combo_box_->currentIndex();
669             connect(cur_combo_box_, SIGNAL(currentIndexChanged(int)), this, SLOT(enumPrefCurrentIndexChanged(int)));
670             editor = cur_combo_box_;
671             break;
672         }
673         case PREF_STRING:
674         {
675             cur_line_edit_ = new QLineEdit();
676             saved_string_pref_ = pref->stashed_val.string;
677             connect(cur_line_edit_, SIGNAL(editingFinished()), this, SLOT(stringPrefEditingFinished()));
678             editor = cur_line_edit_;
679             break;
680         }
681         case PREF_FILENAME:
682         case PREF_DIRNAME:
683         {
684             QString filename;
685
686             if (pref->type == PREF_FILENAME) {
687                 filename = QFileDialog::getSaveFileName(this, wsApp->windowTitleString(pref->title),
688                                                         pref->stashed_val.string);
689             } else {
690                 filename = QFileDialog::getExistingDirectory(this, wsApp->windowTitleString(pref->title),
691                                                              pref->stashed_val.string);
692             }
693             if (!filename.isEmpty()) {
694                 g_free((void *)pref->stashed_val.string);
695                 pref->stashed_val.string = qstring_strdup(QDir::toNativeSeparators(filename));
696                 adv_ti->updatePref();
697             }
698             break;
699         }
700         case PREF_DECODE_AS_RANGE:
701         case PREF_RANGE:
702         {
703             SyntaxLineEdit *syntax_edit = new SyntaxLineEdit();
704             char *cur_val = prefs_pref_to_str(pref, pref_stashed);
705             saved_string_pref_ = gchar_free_to_qstring(cur_val);
706             connect(syntax_edit, SIGNAL(textChanged(QString)),
707                     this, SLOT(rangePrefTextChanged(QString)));
708             connect(syntax_edit, SIGNAL(editingFinished()), this, SLOT(rangePrefEditingFinished()));
709             editor = cur_line_edit_ = syntax_edit;
710             break;
711         }
712         case PREF_COLOR:
713         {
714             QColorDialog color_dlg;
715
716             color_dlg.setCurrentColor(QColor(
717                                           pref->stashed_val.color.red >> 8,
718                                           pref->stashed_val.color.green >> 8,
719                                           pref->stashed_val.color.blue >> 8
720                                           ));
721             if (color_dlg.exec() == QDialog::Accepted) {
722                 QColor cc = color_dlg.currentColor();
723                 pref->stashed_val.color.red = cc.red() << 8 | cc.red();
724                 pref->stashed_val.color.green = cc.green() << 8 | cc.green();
725                 pref->stashed_val.color.blue = cc.blue() << 8 | cc.blue();
726                 adv_ti->updatePref();
727             }
728             break;
729         }
730         case PREF_UAT:
731         {
732             if (pref->gui == GUI_ALL || pref->gui == GUI_QT) {
733                 UatDialog uat_dlg(this, pref->varp.uat);
734                 uat_dlg.exec();
735             }
736             break;
737         }
738         default:
739             break;
740         }
741         cur_pref_type_ = pref->type;
742         if (cur_line_edit_) {
743             cur_line_edit_->setText(saved_string_pref_);
744             cur_line_edit_->selectAll();
745             connect(cur_line_edit_, SIGNAL(destroyed()), this, SLOT(lineEditPrefDestroyed()));
746         }
747         if (cur_combo_box_) {
748             connect(cur_combo_box_, SIGNAL(destroyed()), this, SLOT(enumPrefDestroyed()));
749         }
750         if (editor) {
751             QFrame *edit_frame = new QFrame();
752             QHBoxLayout *hb = new QHBoxLayout();
753             QSpacerItem *spacer = new QSpacerItem(5, 10);
754
755             hb->addWidget(editor, 0);
756             hb->addSpacerItem(spacer);
757             hb->setStretch(1, 1);
758             hb->setContentsMargins(0, 0, 0, 0);
759
760             edit_frame->setLineWidth(0);
761             edit_frame->setFrameStyle(QFrame::NoFrame);
762             // The documentation suggests setting autoFillbackground. That looks silly
763             // so we clear the item text instead.
764             item->setText(3, "");
765             edit_frame->setLayout(hb);
766             pd_ui_->advancedTree->setItemWidget(item, 3, edit_frame);
767             editor->setFocus();
768         }
769     }
770 }
771
772 void PreferencesDialog::lineEditPrefDestroyed()
773 {
774     cur_line_edit_ = NULL;
775 }
776
777 void PreferencesDialog::enumPrefDestroyed()
778 {
779     cur_combo_box_ = NULL;
780 }
781
782 void PreferencesDialog::uintPrefEditingFinished()
783 {
784     AdvancedPrefTreeWidgetItem *adv_ti = dynamic_cast<AdvancedPrefTreeWidgetItem *>(pd_ui_->advancedTree->currentItem());
785     if (!cur_line_edit_ || !adv_ti) return;
786
787     pref_t *pref = adv_ti->pref();
788     if (!pref) return;
789
790     bool ok;
791     guint new_val = cur_line_edit_->text().toUInt(&ok, pref->info.base);
792
793     if (ok) pref->stashed_val.uint = new_val;
794     pd_ui_->advancedTree->removeItemWidget(adv_ti, 3);
795     adv_ti->updatePref();
796 }
797
798 void PreferencesDialog::enumPrefCurrentIndexChanged(int index)
799 {
800     AdvancedPrefTreeWidgetItem *adv_ti = dynamic_cast<AdvancedPrefTreeWidgetItem *>(pd_ui_->advancedTree->currentItem());
801     if (!cur_combo_box_ || !adv_ti || index < 0) return;
802
803     pref_t *pref = adv_ti->pref();
804     if (!pref) return;
805
806     pref->stashed_val.enumval = cur_combo_box_->itemData(index).toInt();
807     adv_ti->updatePref();
808 }
809
810 void PreferencesDialog::stringPrefEditingFinished()
811 {
812     AdvancedPrefTreeWidgetItem *adv_ti = dynamic_cast<AdvancedPrefTreeWidgetItem *>(pd_ui_->advancedTree->currentItem());
813     if (!cur_line_edit_ || !adv_ti) return;
814
815     pref_t *pref = adv_ti->pref();
816     if (!pref) return;
817
818     g_free((void *)pref->stashed_val.string);
819     pref->stashed_val.string = qstring_strdup(cur_line_edit_->text());
820     pd_ui_->advancedTree->removeItemWidget(adv_ti, 3);
821     adv_ti->updatePref();
822 }
823
824 void PreferencesDialog::rangePrefTextChanged(const QString &text)
825 {
826     SyntaxLineEdit *syntax_edit = qobject_cast<SyntaxLineEdit *>(cur_line_edit_);
827     AdvancedPrefTreeWidgetItem *adv_ti = dynamic_cast<AdvancedPrefTreeWidgetItem *>(pd_ui_->advancedTree->currentItem());
828     if (!syntax_edit || !adv_ti) return;
829
830     pref_t *pref = adv_ti->pref();
831     if (!pref) return;
832
833     if (text.isEmpty()) {
834         syntax_edit->setSyntaxState(SyntaxLineEdit::Empty);
835     } else {
836         range_t *newrange;
837         convert_ret_t ret = range_convert_str(NULL, &newrange, text.toUtf8().constData(), pref->info.max_value);
838
839         if (ret == CVT_NO_ERROR) {
840             syntax_edit->setSyntaxState(SyntaxLineEdit::Valid);
841         } else {
842             syntax_edit->setSyntaxState(SyntaxLineEdit::Invalid);
843         }
844         wmem_free(NULL, newrange);
845     }
846 }
847
848 void PreferencesDialog::rangePrefEditingFinished()
849 {
850     SyntaxLineEdit *syntax_edit = qobject_cast<SyntaxLineEdit *>(QObject::sender());
851     AdvancedPrefTreeWidgetItem *adv_ti = dynamic_cast<AdvancedPrefTreeWidgetItem *>(pd_ui_->advancedTree->currentItem());
852     if (!syntax_edit || !adv_ti) return;
853
854     pref_t *pref = adv_ti->pref();
855     if (!pref) return;
856
857     prefs_set_stashed_range_value(pref, syntax_edit->text().toUtf8().constData());
858     pd_ui_->advancedTree->removeItemWidget(adv_ti, 3);
859     adv_ti->updatePref();
860 }
861
862 void PreferencesDialog::on_buttonBox_accepted()
863 {
864     gchar* err = NULL;
865     gboolean must_redissect = FALSE;
866
867     // XXX - We should validate preferences as the user changes them, not here.
868     // XXX - We're also too enthusiastic about setting must_redissect.
869 //    if (!prefs_main_fetch_all(parent_w, &must_redissect))
870 //        return; /* Errors in some preference setting - already reported */
871     prefs_modules_foreach_submodules(NULL, module_prefs_unstash, (gpointer) &must_redissect);
872
873     pd_ui_->columnFrame->unstash();
874     pd_ui_->filterExpressonsFrame->unstash();
875
876     prefs_main_write();
877     if (save_decode_as_entries(&err) < 0)
878     {
879         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err);
880         g_free(err);
881     }
882
883     write_language_prefs();
884     wsApp->loadLanguage(QString(language));
885
886 #ifdef HAVE_AIRPCAP
887   /*
888    * Load the Wireshark decryption keys (just set) and save
889    * the changes to the adapters' registry
890    */
891   //airpcap_load_decryption_keys(airpcap_if_list);
892 #endif
893
894     // gtk/prefs_dlg.c:prefs_main_apply_all
895     /*
896      * Apply the protocol preferences first - "gui_prefs_apply()" could
897      * cause redissection, and we have to make sure the protocol
898      * preference changes have been fully applied.
899      */
900     prefs_apply_all();
901
902     /* Fill in capture options with values from the preferences */
903     prefs_to_capture_opts();
904
905 #ifdef HAVE_AIRPCAP
906 //    prefs_airpcap_update();
907 #endif
908
909     wsApp->setMonospaceFont(prefs.gui_qt_font_name);
910
911     if (must_redissect) {
912         /* Redissect all the packets, and re-evaluate the display filter. */
913         wsApp->queueAppSignal(WiresharkApplication::PacketDissectionChanged);
914     }
915     wsApp->queueAppSignal(WiresharkApplication::PreferencesChanged);
916 }
917
918 void PreferencesDialog::on_buttonBox_helpRequested()
919 {
920     wsApp->helpTopicAction(HELP_PREFERENCES_DIALOG);
921 }
922
923 /*
924  * Editor modelines
925  *
926  * Local Variables:
927  * c-basic-offset: 4
928  * tab-width: 8
929  * indent-tabs-mode: nil
930  * End:
931  *
932  * ex: set shiftwidth=4 tabstop=8 expandtab:
933  * :indentSize=4:tabSize=8:noTabs=true:
934  */