1 /* preferences_dialog.cpp
3 * Wireshark - Network traffic analyzer
4 * By Gerald Combs <gerald@wireshark.org>
5 * Copyright 1998 Gerald Combs
7 * SPDX-License-Identifier: GPL-2.0-or-later
10 #include "preferences_dialog.h"
11 #include <ui_preferences_dialog.h>
13 #include "module_preferences_scroll_area.h"
15 #include <epan/prefs.h>
16 #include <epan/prefs-int.h>
17 #include <epan/decode_as.h>
18 #include <ui/language.h>
19 #include <ui/preference_utils.h>
21 #include <ui/commandline.h>
22 #include <ui/simple_dialog.h>
23 #include <ui/recent.h>
24 #include <main_window.h>
27 #include <ui/qt/utils/qt_ui_utils.h>
28 #include <ui/qt/utils/color_utils.h>
30 #include "main_application.h"
32 #include <QDesktopServices>
36 // Callbacks prefs routines
39 module_prefs_unstash(module_t
*module
, void *data
)
41 unsigned int *must_redissect_p
= static_cast<unsigned int *>(data
);
42 pref_unstash_data_t unstashed_data
;
44 unstashed_data
.handle_decode_as
= true;
46 module
->prefs_changed_flags
= 0; /* assume none of them changed */
47 for (GList
*pref_l
= module
->prefs
; pref_l
&& pref_l
->data
; pref_l
= gxx_list_next(pref_l
)) {
48 pref_t
*pref
= gxx_list_data(pref_t
*, pref_l
);
50 if (prefs_get_type(pref
) == PREF_OBSOLETE
|| prefs_get_type(pref
) == PREF_STATIC_TEXT
) continue;
52 unstashed_data
.module
= module
;
53 pref_unstash(pref
, &unstashed_data
);
54 commandline_options_drop(module
->name
, prefs_get_name(pref
));
57 /* If any of them changed, indicate that we must redissect and refilter
58 the current capture (if we have one), as the preference change
59 could cause packets to be dissected differently. */
60 *must_redissect_p
|= module
->prefs_changed_flags
;
62 if (prefs_module_has_submodules(module
))
63 return prefs_modules_foreach_submodules(module
, module_prefs_unstash
, data
);
65 return 0; /* Keep unstashing. */
69 module_prefs_clean_stash(module_t
*module
, void *)
71 for (GList
*pref_l
= module
->prefs
; pref_l
&& pref_l
->data
; pref_l
= gxx_list_next(pref_l
)) {
72 pref_t
*pref
= gxx_list_data(pref_t
*, pref_l
);
74 if (prefs_get_type(pref
) == PREF_OBSOLETE
|| prefs_get_type(pref
) == PREF_STATIC_TEXT
) continue;
76 pref_clean_stash(pref
, Q_NULLPTR
);
79 if (prefs_module_has_submodules(module
))
80 return prefs_modules_foreach_submodules(module
, module_prefs_clean_stash
, Q_NULLPTR
);
82 return 0; /* Keep cleaning modules */
87 // Preference tree items
88 const int APPEARANCE_ITEM
= 0;
90 //placeholder key to keep dynamically loaded preferences
91 static const char* MODULES_NAME
= "Modules";
93 PreferencesDialog::PreferencesDialog(QWidget
*parent
) :
94 GeometryStateDialog(parent
),
95 pd_ui_(new Ui::PreferencesDialog
),
97 advancedPrefsModel_(this),
98 advancedPrefsDelegate_(this),
99 modulePrefsModel_(this)
101 advancedPrefsModel_
.setSourceModel(&model_
);
102 modulePrefsModel_
.setSourceModel(&model_
);
103 saved_capture_no_extcap_
= prefs
.capture_no_extcap
;
105 // Some classes depend on pref_ptr_to_pref_ so this MUST be called after
106 // model_.populate().
107 pd_ui_
->setupUi(this);
110 setWindowTitle(mainApp
->windowTitleString(tr("Preferences")));
112 pd_ui_
->advancedView
->setModel(&advancedPrefsModel_
);
113 pd_ui_
->advancedView
->setItemDelegate(&advancedPrefsDelegate_
);
114 advancedPrefsModel_
.setFirstColumnSpanned(pd_ui_
->advancedView
);
116 pd_ui_
->prefsView
->setModel(&modulePrefsModel_
);
118 pd_ui_
->splitter
->setStretchFactor(0, 1);
119 pd_ui_
->splitter
->setStretchFactor(1, 5);
121 // The calculations done in showEvent to set the minimum size of the
122 // protocol column mean that if we load the splitter state it will become
123 // impossible to shrink the splitter below the width of the widest protocol
124 // that initially fits, so don't do this unless we change showEvent.
125 //loadSplitterState(pd_ui_->splitter);
127 pd_ui_
->prefsView
->sortByColumn(ModulePrefsModel::colName
, Qt::AscendingOrder
);
129 //Set the Appearance leaf to expanded
130 pd_ui_
->prefsView
->setExpanded(modulePrefsModel_
.index(APPEARANCE_ITEM
, 0), true);
133 // PreferencesPane, prefsView, and stackedWidget must all correspond to each other.
134 prefs_pane_to_item_
[PrefsModel::typeToString(PrefsModel::Appearance
)] = pd_ui_
->appearanceFrame
;
135 prefs_pane_to_item_
[PrefsModel::typeToString(PrefsModel::Layout
)] = pd_ui_
->layoutFrame
;
136 prefs_pane_to_item_
[PrefsModel::typeToString(PrefsModel::Columns
)] = pd_ui_
->columnFrame
;
137 prefs_pane_to_item_
[PrefsModel::typeToString(PrefsModel::FontAndColors
)] = pd_ui_
->fontandcolorFrame
;
138 prefs_pane_to_item_
[PrefsModel::typeToString(PrefsModel::Capture
)] = pd_ui_
->captureFrame
;
139 prefs_pane_to_item_
[PrefsModel::typeToString(PrefsModel::Expert
)] = pd_ui_
->expertFrame
;
140 prefs_pane_to_item_
[PrefsModel::typeToString(PrefsModel::FilterButtons
)] = pd_ui_
->filterExpressonsFrame
;
141 prefs_pane_to_item_
[PrefsModel::typeToString(PrefsModel::RSAKeys
)] = pd_ui_
->rsaKeysFrame
;
142 prefs_pane_to_item_
[PrefsModel::typeToString(PrefsModel::Advanced
)] = pd_ui_
->advancedFrame
;
143 prefs_pane_to_item_
[MODULES_NAME
] = NULL
;
145 pd_ui_
->filterExpressonsFrame
->setUat(uat_get_table_by_name("Display expressions"));
146 pd_ui_
->expertFrame
->setUat(uat_get_table_by_name("Expert Info Severity Level Configuration"));
148 connect(pd_ui_
->prefsView
, &PrefModuleTreeView::goToPane
, this, &PreferencesDialog::selectPane
);
150 /* Create a single-shot timer for debouncing calls to
151 * updateSearchLineEdit() */
152 searchLineEditTimer
= new QTimer(this);
153 searchLineEditTimer
->setSingleShot(true);
154 connect(searchLineEditTimer
, &QTimer::timeout
, this, &PreferencesDialog::updateSearchLineEdit
);
157 PreferencesDialog::~PreferencesDialog()
160 delete searchLineEditTimer
;
161 prefs_modules_foreach_submodules(NULL
, module_prefs_clean_stash
, NULL
);
164 void PreferencesDialog::setPane(const QString module_name
)
166 pd_ui_
->prefsView
->setPane(module_name
);
169 void PreferencesDialog::showEvent(QShowEvent
*)
171 QStyleOption style_opt
;
172 int new_prefs_tree_width
= pd_ui_
->prefsView
->style()->subElementRect(QStyle::SE_TreeViewDisclosureItem
, &style_opt
).left();
173 QList
<int> sizes
= pd_ui_
->splitter
->sizes();
176 new_prefs_tree_width
*= 2;
178 pd_ui_
->prefsView
->resizeColumnToContents(ModulePrefsModel::colName
);
179 new_prefs_tree_width
+= pd_ui_
->prefsView
->columnWidth(ModulePrefsModel::colName
);
180 pd_ui_
->prefsView
->setMinimumWidth(new_prefs_tree_width
);
182 sizes
[1] += sizes
[0] - new_prefs_tree_width
;
183 sizes
[0] = new_prefs_tree_width
;
184 pd_ui_
->splitter
->setSizes(sizes
);
185 pd_ui_
->splitter
->setStretchFactor(0, 1);
187 pd_ui_
->advancedView
->expandAll();
188 pd_ui_
->advancedView
->setSortingEnabled(true);
189 pd_ui_
->advancedView
->sortByColumn(AdvancedPrefsModel::colName
, Qt::AscendingOrder
);
191 int one_em
= fontMetrics().height();
192 pd_ui_
->advancedView
->setColumnWidth(AdvancedPrefsModel::colName
, one_em
* 12); // Don't let long items widen things too much
193 pd_ui_
->advancedView
->resizeColumnToContents(AdvancedPrefsModel::colStatus
);
194 pd_ui_
->advancedView
->resizeColumnToContents(AdvancedPrefsModel::colType
);
195 pd_ui_
->advancedView
->setColumnWidth(AdvancedPrefsModel::colValue
, one_em
* 30);
198 void PreferencesDialog::selectPane(QString pane
)
200 if (prefs_pane_to_item_
.contains(pane
)) {
201 pd_ui_
->stackedWidget
->setCurrentWidget(prefs_pane_to_item_
[pane
]);
203 //If not found in prefs_pane_to_item_, it must be an individual module
204 module_t
* module
= prefs_find_module(pane
.toStdString().c_str());
205 if (module
!= NULL
) {
206 QWidget
* moduleWindow
= prefs_pane_to_item_
[MODULES_NAME
];
207 if (moduleWindow
!= NULL
) {
208 pd_ui_
->stackedWidget
->removeWidget(moduleWindow
);
212 moduleWindow
= new ModulePreferencesScrollArea(module
);
213 prefs_pane_to_item_
[MODULES_NAME
] = moduleWindow
;
214 pd_ui_
->stackedWidget
->addWidget(moduleWindow
);
215 pd_ui_
->stackedWidget
->setCurrentWidget(moduleWindow
);
220 void PreferencesDialog::updateSearchLineEdit()
222 advancedPrefsModel_
.setFilter(searchLineEditText
);
223 /* If items are filtered out, then filtered back in, the tree remains collapsed
224 Force an expansion */
225 pd_ui_
->advancedView
->expandAll();
228 void PreferencesDialog::on_advancedSearchLineEdit_textEdited(const QString
&text
)
230 /* As running pd_ui_->advancedView->expandAll() takes a noticeable amount
231 * of time and so would introduce significant lag while typing a string
232 * into the Search box, we instead debounce the call to
233 * updateSearchLineEdit(), so that it doesn't run until a set amount of
234 * time has elapsed with no updates to the Search field.
236 * If the user types something before the timer elapses, the timer restarts
239 searchLineEditText
= text
;
240 unsigned gui_debounce_timer
= prefs_get_uint_value("gui", "debounce.timer");
241 searchLineEditTimer
->start(gui_debounce_timer
);
244 void PreferencesDialog::on_showChangedValuesCheckBox_toggled(bool checked
)
246 advancedPrefsModel_
.setShowChangedValues(checked
);
247 /* If items are filtered out, then filtered back in, the tree remains collapsed
248 Force an expansion */
249 pd_ui_
->advancedView
->expandAll();
252 void PreferencesDialog::apply()
255 unsigned int redissect_flags
= 0;
257 // XXX - We should validate preferences as the user changes them, not here.
258 // Some, but not all, of the preference controls validate the input,
259 // but they don't disable the OK/Apply button, and, what's worse, the
260 // "stashed" value is sometimes the last valid input, not, e.g., the
261 // input when the dialog was opened.
262 // XXX - We're also too enthusiastic about setting must_redissect.
263 prefs_modules_foreach_submodules(NULL
, module_prefs_unstash
, (void *)&redissect_flags
);
265 extcap_register_preferences();
267 if (redissect_flags
& PREF_EFFECT_GUI_LAYOUT
) {
268 // Layout type changed, reset sizes
269 recent
.gui_geometry_main_upper_pane
= 0;
270 recent
.gui_geometry_main_lower_pane
= 0;
271 g_free(recent
.gui_geometry_main_master_split
);
272 g_free(recent
.gui_geometry_main_extra_split
);
273 recent
.gui_geometry_main_master_split
= NULL
;
274 recent
.gui_geometry_main_extra_split
= NULL
;
277 pd_ui_
->columnFrame
->unstash();
278 pd_ui_
->filterExpressonsFrame
->acceptChanges();
279 pd_ui_
->expertFrame
->acceptChanges();
280 #ifdef HAVE_LIBGNUTLS
281 pd_ui_
->rsaKeysFrame
->acceptChanges();
284 //Filter expressions don't affect dissection, so there is no need to
285 //send any events to that effect. However, the app needs to know
286 //about any button changes.
287 mainApp
->emitAppSignal(MainApplication::FilterExpressionsChanged
);
290 if (save_decode_as_entries(&err
) < 0)
292 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
, "%s", err
);
296 write_language_prefs();
297 mainApp
->loadLanguage(QString(language
));
299 * Apply the protocol preferences first - "gui_prefs_apply()" could
300 * cause redissection, and we have to make sure the protocol
301 * preference changes have been fully applied.
305 /* Fill in capture options with values from the preferences */
306 prefs_to_capture_opts();
308 mainApp
->setMonospaceFont(prefs
.gui_font_name
);
310 if (redissect_flags
& (PREF_EFFECT_GUI_COLOR
)) {
311 ColorUtils::setScheme(prefs
.gui_color_scheme
);
312 mainApp
->emitAppSignal(MainApplication::ColorsChanged
);
315 if (redissect_flags
& PREF_EFFECT_FIELDS
) {
316 mainApp
->emitAppSignal(MainApplication::FieldsChanged
);
319 if (redissect_flags
& PREF_EFFECT_DISSECTION
) {
320 // Freeze the packet list early to avoid updating column data before doing a
321 // full redissection. The packet list will be thawed when redissection is done.
322 mainApp
->emitAppSignal(MainApplication::FreezePacketList
);
324 /* Redissect all the packets, and re-evaluate the display filter. */
325 mainApp
->emitAppSignal(MainApplication::PacketDissectionChanged
);
328 if (redissect_flags
) {
329 mainApp
->emitAppSignal(MainApplication::PreferencesChanged
);
332 if (redissect_flags
& PREF_EFFECT_GUI_LAYOUT
) {
333 mainApp
->emitAppSignal(MainApplication::RecentPreferencesRead
);
336 if (prefs
.capture_no_extcap
!= saved_capture_no_extcap_
)
337 mainApp
->refreshLocalInterfaces();
340 void PreferencesDialog::on_buttonBox_accepted()
346 void PreferencesDialog::on_buttonBox_rejected()
348 //handle frames that don't have their own OK/Cancel "buttons"
349 pd_ui_
->filterExpressonsFrame
->rejectChanges();
350 pd_ui_
->expertFrame
->rejectChanges();
351 #ifdef HAVE_LIBGNUTLS
352 pd_ui_
->rsaKeysFrame
->rejectChanges();
357 void PreferencesDialog::on_buttonBox_clicked(QAbstractButton
*button
)
359 if (pd_ui_
->buttonBox
->buttonRole(button
) == QDialogButtonBox::ApplyRole
) {
364 void PreferencesDialog::on_buttonBox_helpRequested()
366 QString help_page
= modulePrefsModel_
.data(pd_ui_
->prefsView
->currentIndex(), ModulePrefsModel::ModuleHelp
).toString();
367 if (!help_page
.isEmpty()) {
368 QString url
= gchar_free_to_qstring(user_guide_url(help_page
.toUtf8().constData()));
369 QDesktopServices::openUrl(QUrl(url
));
372 mainApp
->helpTopicAction(HELP_PREFERENCES_DIALOG
);