1 /* preference_editor_frame.h
2  *
3  * Wireshark - Network traffic analyzer
4  * By Gerald Combs <gerald@wireshark.org>
5  * Copyright 1998 Gerald Combs
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  */
9 
10 #include "config.h"
11 
12 #include <glib.h>
13 
14 #include <epan/prefs.h>
15 #include <epan/prefs-int.h>
16 #include <epan/decode_as.h>
17 
18 #include <ui/preference_utils.h>
19 #include <ui/simple_dialog.h>
20 
21 #include "preference_editor_frame.h"
22 #include <ui_preference_editor_frame.h>
23 
24 #include <ui/qt/utils/qt_ui_utils.h>
25 #include <ui/qt/widgets/wireshark_file_dialog.h>
26 #include <wsutil/utf8_entities.h>
27 
28 #include "wireshark_application.h"
29 
30 #include <QPushButton>
31 #include <QKeyEvent>
32 
PreferenceEditorFrame(QWidget * parent)33 PreferenceEditorFrame::PreferenceEditorFrame(QWidget *parent) :
34     AccordionFrame(parent),
35     ui(new Ui::PreferenceEditorFrame),
36     module_(NULL),
37     pref_(NULL),
38     new_uint_(0),
39     new_str_(""),
40     new_range_(NULL)
41 {
42     ui->setupUi(this);
43 
44 #ifdef Q_OS_MAC
45     foreach (QWidget *w, findChildren<QWidget *>()) {
46         w->setAttribute(Qt::WA_MacSmallSize, true);
47     }
48 #endif
49 
50     connect(ui->preferenceBrowseButton, &QPushButton::clicked, this, &PreferenceEditorFrame::browsePushButtonClicked);
51 }
52 
~PreferenceEditorFrame()53 PreferenceEditorFrame::~PreferenceEditorFrame()
54 {
55     delete ui;
56 }
57 
editPreference(preference * pref,pref_module * module)58 void PreferenceEditorFrame::editPreference(preference *pref, pref_module *module)
59 {
60     pref_ = pref;
61     module_ = module;
62 
63     if (!pref || !module) {
64         hide();
65         return;
66     }
67 
68     ui->modulePreferencesToolButton->setText(tr("Open %1 preferences…").arg(module_->title));
69 
70     pref_stash(pref_, NULL);
71     ui->preferenceTitleLabel->setText(QString("%1:").arg(prefs_get_title(pref)));
72 
73     // Convert the pref description from plain text to rich text.
74     QString description = html_escape(prefs_get_description(pref));
75     description.replace('\n', "<br>");
76     QString tooltip = QString("<span>%1</span>").arg(description);
77     ui->preferenceTitleLabel->setToolTip(tooltip);
78     ui->preferenceLineEdit->setToolTip(tooltip);
79 
80     ui->preferenceLineEdit->clear();
81     ui->preferenceLineEdit->setSyntaxState(SyntaxLineEdit::Empty);
82     disconnect(ui->preferenceLineEdit, 0, 0, 0);
83 
84     bool show = false;
85     bool browse_button = false;
86 
87     switch (prefs_get_type(pref_)) {
88     case PREF_UINT:
89     case PREF_DECODE_AS_UINT:
90         connect(ui->preferenceLineEdit, SIGNAL(textChanged(QString)),
91                 this, SLOT(uintLineEditTextEdited(QString)));
92         show = true;
93         break;
94     case PREF_SAVE_FILENAME:
95     case PREF_OPEN_FILENAME:
96     case PREF_DIRNAME:
97         browse_button = true;
98         // Fallthrough
99     case PREF_STRING:
100         connect(ui->preferenceLineEdit, SIGNAL(textChanged(QString)),
101                 this, SLOT(stringLineEditTextEdited(QString)));
102         show = true;
103         break;
104     case PREF_RANGE:
105     case PREF_DECODE_AS_RANGE:
106         connect(ui->preferenceLineEdit, SIGNAL(textChanged(QString)),
107                 this, SLOT(rangeLineEditTextEdited(QString)));
108         show = true;
109         break;
110     default:
111         break;
112     }
113 
114     if (show) {
115         ui->preferenceLineEdit->setText(gchar_free_to_qstring(prefs_pref_to_str(pref_, pref_stashed)).remove(QRegExp("\n\t")));
116         ui->preferenceBrowseButton->setHidden(!browse_button);
117         animatedShow();
118     }
119 }
120 
uintLineEditTextEdited(const QString & new_str)121 void PreferenceEditorFrame::uintLineEditTextEdited(const QString &new_str)
122 {
123     if (new_str.isEmpty()) {
124         new_uint_ = prefs_get_uint_value_real(pref_, pref_stashed);
125         ui->preferenceLineEdit->setSyntaxState(SyntaxLineEdit::Empty);
126         ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
127         return;
128     }
129 
130     bool ok;
131     uint new_uint = new_str.toUInt(&ok, 0);
132     if (ok) {
133         new_uint_ = new_uint;
134         ui->preferenceLineEdit->setSyntaxState(SyntaxLineEdit::Valid);
135     } else {
136         new_uint_ = prefs_get_uint_value_real(pref_, pref_stashed);
137         ui->preferenceLineEdit->setSyntaxState(SyntaxLineEdit::Invalid);
138     }
139     ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(ok);
140 }
141 
stringLineEditTextEdited(const QString & new_str)142 void PreferenceEditorFrame::stringLineEditTextEdited(const QString &new_str)
143 {
144     new_str_ = new_str;
145 }
146 
browsePushButtonClicked()147 void PreferenceEditorFrame::browsePushButtonClicked()
148 {
149     QString caption = wsApp->windowTitleString(prefs_get_title(pref_));
150     QString dir = prefs_get_string_value(pref_, pref_stashed);
151     QString filename;
152 
153     switch (prefs_get_type(pref_)) {
154     case PREF_SAVE_FILENAME:
155         filename = WiresharkFileDialog::getSaveFileName(this, caption, dir);
156         break;
157     case PREF_OPEN_FILENAME:
158         filename = WiresharkFileDialog::getOpenFileName(this, caption, dir);
159         break;
160     case PREF_DIRNAME:
161         filename = WiresharkFileDialog::getExistingDirectory(this, caption, dir);
162         break;
163     }
164 
165     if (!filename.isEmpty()) {
166         ui->preferenceLineEdit->setText(filename);
167     }
168 }
169 
rangeLineEditTextEdited(const QString & new_str)170 void PreferenceEditorFrame::rangeLineEditTextEdited(const QString &new_str)
171 {
172     range_t *new_range = NULL;
173 
174     convert_ret_t ret = range_convert_str(NULL, &new_range, new_str.toUtf8().constData(), prefs_get_max_value(pref_));
175     wmem_free(NULL, new_range_);
176     new_range_ = new_range;
177 
178     if (ret == CVT_NO_ERROR) {
179         if (new_str.isEmpty()) {
180             ui->preferenceLineEdit->setSyntaxState(SyntaxLineEdit::Empty);
181         } else {
182             ui->preferenceLineEdit->setSyntaxState(SyntaxLineEdit::Valid);
183         }
184     } else {
185         ui->preferenceLineEdit->setSyntaxState(SyntaxLineEdit::Invalid);
186     }
187 }
188 
showEvent(QShowEvent * event)189 void PreferenceEditorFrame::showEvent(QShowEvent *event)
190 {
191     ui->preferenceLineEdit->setFocus();
192     ui->preferenceLineEdit->selectAll();
193 
194     AccordionFrame::showEvent(event);
195 }
196 
on_modulePreferencesToolButton_clicked()197 void PreferenceEditorFrame::on_modulePreferencesToolButton_clicked()
198 {
199     if (module_) {
200         emit showProtocolPreferences(module_->name);
201     }
202     on_buttonBox_rejected();
203 }
204 
on_preferenceLineEdit_returnPressed()205 void PreferenceEditorFrame::on_preferenceLineEdit_returnPressed()
206 {
207     if (ui->buttonBox->button(QDialogButtonBox::Ok)->isEnabled()) {
208         on_buttonBox_accepted();
209     }
210 }
211 
on_buttonBox_accepted()212 void PreferenceEditorFrame::on_buttonBox_accepted()
213 {
214     unsigned int changed_flags = 0;
215     unsigned int apply = 0;
216     switch(prefs_get_type(pref_)) {
217     case PREF_UINT:
218     case PREF_DECODE_AS_UINT:
219         apply = prefs_set_uint_value(pref_, new_uint_, pref_stashed);
220         break;
221     case PREF_STRING:
222     case PREF_SAVE_FILENAME:
223     case PREF_OPEN_FILENAME:
224     case PREF_DIRNAME:
225         apply = prefs_set_string_value(pref_, new_str_.toStdString().c_str(), pref_stashed);
226         break;
227     case PREF_RANGE:
228     case PREF_DECODE_AS_RANGE:
229         apply = prefs_set_range_value(pref_, new_range_, pref_stashed);
230         break;
231     default:
232         break;
233     }
234 
235     if (apply && module_) {
236         changed_flags = module_->prefs_changed_flags;
237         pref_unstash_data_t unstashed_data;
238 
239         unstashed_data.module = module_;
240         unstashed_data.handle_decode_as = TRUE;
241 
242         pref_unstash(pref_, &unstashed_data);
243         prefs_apply(module_);
244         prefs_main_write();
245 
246         gchar* err = NULL;
247         if (save_decode_as_entries(&err) < 0)
248         {
249             simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err);
250             g_free(err);
251         }
252     }
253     on_buttonBox_rejected();
254     // Emit signals once UI is hidden
255     if (apply) {
256         if (changed_flags & PREF_EFFECT_FIELDS) {
257             wsApp->emitAppSignal(WiresharkApplication::FieldsChanged);
258         }
259         wsApp->emitAppSignal(WiresharkApplication::PacketDissectionChanged);
260         wsApp->emitAppSignal(WiresharkApplication::PreferencesChanged);
261     }
262 }
263 
on_buttonBox_rejected()264 void PreferenceEditorFrame::on_buttonBox_rejected()
265 {
266     pref_ = NULL;
267     module_ = NULL;
268     wmem_free(NULL, new_range_);
269     new_range_ = NULL;
270     animatedHide();
271 }
272 
keyPressEvent(QKeyEvent * event)273 void PreferenceEditorFrame::keyPressEvent(QKeyEvent *event)
274 {
275     if (event->modifiers() == Qt::NoModifier) {
276         if (event->key() == Qt::Key_Escape) {
277             on_buttonBox_rejected();
278         } else if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
279             if (ui->buttonBox->button(QDialogButtonBox::Ok)->isEnabled()) {
280                 on_buttonBox_accepted();
281             } else if (ui->preferenceLineEdit->syntaxState() == SyntaxLineEdit::Invalid) {
282                 wsApp->pushStatus(WiresharkApplication::FilterSyntax, tr("Invalid value."));
283             }
284         }
285     }
286 
287     AccordionFrame::keyPressEvent(event);
288 }
289