Fix comment end after SPDX identifier
[metze/wireshark/wip.git] / ui / qt / uat_dialog.cpp
1 /* uat_dialog.cpp
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 "uat_dialog.h"
11 #include <ui_uat_dialog.h>
12 #include "wireshark_application.h"
13
14 #include "epan/strutil.h"
15 #include "epan/uat-int.h"
16 #include "ui/help_url.h"
17 #include <wsutil/report_message.h>
18
19 #include <ui/qt/utils/qt_ui_utils.h>
20
21 #include <QDesktopServices>
22 #include <QPushButton>
23 #include <QUrl>
24
25 #include <QDebug>
26
27 // NOTE currently uat setter is always invoked in UatModel even if the uat checker fails.
28
29 UatDialog::UatDialog(QWidget *parent, epan_uat *uat) :
30     GeometryStateDialog(parent),
31     ui(new Ui::UatDialog),
32     uat_model_(NULL),
33     uat_delegate_(NULL),
34     uat_(uat)
35 {
36     ui->setupUi(this);
37     if (uat) loadGeometry(0, 0, uat->name);
38
39     ui->deleteToolButton->setEnabled(false);
40     ui->copyToolButton->setEnabled(false);
41     ui->moveUpToolButton->setEnabled(false);
42     ui->moveDownToolButton->setEnabled(false);
43     ui->clearToolButton->setEnabled(false);
44     ok_button_ = ui->buttonBox->button(QDialogButtonBox::Ok);
45     help_button_ = ui->buttonBox->button(QDialogButtonBox::Help);
46
47 #ifdef Q_OS_MAC
48     ui->newToolButton->setAttribute(Qt::WA_MacSmallSize, true);
49     ui->deleteToolButton->setAttribute(Qt::WA_MacSmallSize, true);
50     ui->copyToolButton->setAttribute(Qt::WA_MacSmallSize, true);
51     ui->moveUpToolButton->setAttribute(Qt::WA_MacSmallSize, true);
52     ui->moveDownToolButton->setAttribute(Qt::WA_MacSmallSize, true);
53     ui->clearToolButton->setAttribute(Qt::WA_MacSmallSize, true);
54     ui->pathLabel->setAttribute(Qt::WA_MacSmallSize, true);
55 #endif
56
57     setUat(uat);
58
59     // FIXME: this prevents the columns from being resized, even if the text
60     // within a combobox needs more space (e.g. in the USER DLT settings).  For
61     // very long filenames in the SSL RSA keys dialog, it also results in a
62     // vertical scrollbar. Maybe remove this since the editor is not limited to
63     // the column width (and overlays other fields if more width is needed)?
64     ui->uatTreeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
65
66     // start editing as soon as the field is selected or when typing starts
67     ui->uatTreeView->setEditTriggers(ui->uatTreeView->editTriggers() |
68             QAbstractItemView::CurrentChanged | QAbstractItemView::AnyKeyPressed);
69
70     // Need to add uat_move or uat_insert to the UAT API.
71     ui->uatTreeView->setDragEnabled(false);
72     qDebug() << "FIX Add drag reordering to UAT dialog";
73 }
74
75 UatDialog::~UatDialog()
76 {
77     delete ui;
78     delete uat_delegate_;
79     delete uat_model_;
80 }
81
82 void UatDialog::setUat(epan_uat *uat)
83 {
84     QString title(tr("Unknown User Accessible Table"));
85
86     uat_ = uat;
87
88     ui->pathLabel->clear();
89     ui->pathLabel->setEnabled(false);
90     help_button_->setEnabled(false);
91
92     if (uat_) {
93         if (uat_->name) {
94             title = uat_->name;
95         }
96
97         QString abs_path = gchar_free_to_qstring(uat_get_actual_filename(uat_, FALSE));
98         ui->pathLabel->setText(abs_path);
99         ui->pathLabel->setUrl(QUrl::fromLocalFile(abs_path).toString());
100         ui->pathLabel->setToolTip(tr("Open ") + uat->filename);
101         ui->pathLabel->setEnabled(true);
102
103         uat_model_ = new UatModel(NULL, uat);
104         uat_delegate_ = new UatDelegate;
105         ui->uatTreeView->setModel(uat_model_);
106         ui->uatTreeView->setItemDelegate(uat_delegate_);
107
108         connect(uat_model_, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
109                 this, SLOT(modelDataChanged(QModelIndex)));
110         connect(uat_model_, SIGNAL(rowsRemoved(QModelIndex, int, int)),
111                 this, SLOT(modelRowsRemoved()));
112         connect(uat_model_, SIGNAL(modelReset()), this, SLOT(modelRowsReset()));
113
114         ok_button_->setEnabled(!uat_model_->hasErrors());
115
116         if (uat_->help && strlen(uat_->help) > 0) {
117             help_button_->setEnabled(true);
118         }
119
120         connect(this, SIGNAL(rejected()), this, SLOT(rejectChanges()));
121         connect(this, SIGNAL(accepted()), this, SLOT(acceptChanges()));
122     }
123
124     setWindowTitle(title);
125 }
126
127 // Invoked when a field in the model changes (e.g. by closing the editor)
128 void UatDialog::modelDataChanged(const QModelIndex &topLeft)
129 {
130     checkForErrorHint(topLeft, QModelIndex());
131     ok_button_->setEnabled(!uat_model_->hasErrors());
132 }
133
134 // Invoked after a row has been removed from the model.
135 void UatDialog::modelRowsRemoved()
136 {
137     const QModelIndex &current = ui->uatTreeView->currentIndex();
138
139     // Because currentItemChanged() is called before the row is removed from the model
140     // we also need to check for button enabling here.
141     if (current.isValid()) {
142         ui->moveUpToolButton->setEnabled(current.row() != 0);
143         ui->moveDownToolButton->setEnabled(current.row() != (uat_model_->rowCount() - 1));
144     } else {
145         ui->moveUpToolButton->setEnabled(false);
146         ui->moveDownToolButton->setEnabled(false);
147     }
148
149     checkForErrorHint(current, QModelIndex());
150     ok_button_->setEnabled(!uat_model_->hasErrors());
151 }
152
153 void UatDialog::modelRowsReset()
154 {
155     ui->deleteToolButton->setEnabled(false);
156     ui->clearToolButton->setEnabled(false);
157     ui->copyToolButton->setEnabled(false);
158     ui->moveUpToolButton->setEnabled(false);
159     ui->moveDownToolButton->setEnabled(false);
160 }
161
162
163 // Invoked when a different field is selected. Note: when selecting a different
164 // field after editing, this event is triggered after modelDataChanged.
165 void UatDialog::on_uatTreeView_currentItemChanged(const QModelIndex &current, const QModelIndex &previous)
166 {
167     if (current.isValid()) {
168         ui->deleteToolButton->setEnabled(true);
169         ui->clearToolButton->setEnabled(true);
170         ui->copyToolButton->setEnabled(true);
171         ui->moveUpToolButton->setEnabled(current.row() != 0);
172         ui->moveDownToolButton->setEnabled(current.row() != (uat_model_->rowCount() - 1));
173     } else {
174         ui->deleteToolButton->setEnabled(false);
175         ui->clearToolButton->setEnabled(false);
176         ui->copyToolButton->setEnabled(false);
177         ui->moveUpToolButton->setEnabled(false);
178         ui->moveDownToolButton->setEnabled(false);
179     }
180
181     checkForErrorHint(current, previous);
182 }
183
184 // If the current field has errors, show them.
185 // Otherwise if the row has not changed, but the previous field has errors, show them.
186 // Otherwise pick the first error in the current row.
187 // Otherwise show the error from the previous field (if any).
188 // Otherwise clear the error hint.
189 void UatDialog::checkForErrorHint(const QModelIndex &current, const QModelIndex &previous)
190 {
191     if (current.isValid()) {
192         if (trySetErrorHintFromField(current)) {
193             return;
194         }
195
196         const int row = current.row();
197         if (row == previous.row() && trySetErrorHintFromField(previous)) {
198             return;
199         }
200
201         for (int i = 0; i < uat_model_->columnCount(); i++) {
202             if (trySetErrorHintFromField(uat_model_->index(row, i))) {
203                 return;
204             }
205         }
206     }
207
208     if (previous.isValid()) {
209         if (trySetErrorHintFromField(previous)) {
210             return;
211         }
212     }
213
214     ui->hintLabel->clear();
215 }
216
217 bool UatDialog::trySetErrorHintFromField(const QModelIndex &index)
218 {
219     const QVariant &data = uat_model_->data(index, Qt::UserRole + 1);
220     if (!data.isNull()) {
221         // use HTML instead of PlainText because that handles wordwrap properly
222         ui->hintLabel->setText("<small><i>" + html_escape(data.toString()) + "</i></small>");
223         return true;
224     }
225     return false;
226 }
227
228 void UatDialog::addRecord(bool copy_from_current)
229 {
230     if (!uat_) return;
231
232     const QModelIndex &current = ui->uatTreeView->currentIndex();
233     if (copy_from_current && !current.isValid()) return;
234
235     // should not fail, but you never know.
236     if (!uat_model_->insertRows(uat_model_->rowCount(), 1)) {
237         qDebug() << "Failed to add a new record";
238         return;
239     }
240     const QModelIndex &new_index = uat_model_->index(uat_model_->rowCount() - 1, 0);
241     if (copy_from_current) {
242         uat_model_->copyRow(new_index.row(), current.row());
243     }
244     // due to an EditTrigger, this will also start editing.
245     ui->uatTreeView->setCurrentIndex(new_index);
246     // trigger updating error messages and the OK button state.
247     modelDataChanged(new_index);
248 }
249
250 void UatDialog::on_newToolButton_clicked()
251 {
252     addRecord();
253 }
254
255 void UatDialog::on_deleteToolButton_clicked()
256 {
257     const QModelIndex &current = ui->uatTreeView->currentIndex();
258     if (uat_model_ && current.isValid()) {
259         if (!uat_model_->removeRows(current.row(), 1)) {
260             qDebug() << "Failed to remove row";
261         }
262     }
263 }
264
265 void UatDialog::on_copyToolButton_clicked()
266 {
267     addRecord(true);
268 }
269
270 void UatDialog::on_moveUpToolButton_clicked()
271 {
272     const QModelIndex &current = ui->uatTreeView->currentIndex();
273     int current_row = current.row();
274     if (uat_model_ && current.isValid() && current_row > 0) {
275         if (!uat_model_->moveRow(current_row, current_row - 1)) {
276             qDebug() << "Failed to move row up";
277             return;
278         }
279         current_row--;
280         ui->moveUpToolButton->setEnabled(current_row > 0);
281         ui->moveDownToolButton->setEnabled(current_row < (uat_model_->rowCount() - 1));
282     }
283 }
284
285 void UatDialog::on_moveDownToolButton_clicked()
286 {
287     const QModelIndex &current = ui->uatTreeView->currentIndex();
288     int current_row = current.row();
289     if (uat_model_ && current.isValid() && current_row < (uat_model_->rowCount() - 1)) {
290         if (!uat_model_->moveRow(current_row, current_row + 1)) {
291             qDebug() << "Failed to move row down";
292             return;
293         }
294         current_row++;
295         ui->moveUpToolButton->setEnabled(current_row > 0);
296         ui->moveDownToolButton->setEnabled(current_row < (uat_model_->rowCount() - 1));
297     }
298 }
299
300 void UatDialog::on_clearToolButton_clicked()
301 {
302     if (uat_model_) {
303         uat_model_->clearAll();
304     }
305 }
306
307 void UatDialog::applyChanges()
308 {
309     if (!uat_) return;
310
311     if (uat_->flags & UAT_AFFECTS_FIELDS) {
312         /* Recreate list with new fields and redissect packets */
313         wsApp->queueAppSignal(WiresharkApplication::FieldsChanged);
314     }
315     if (uat_->flags & UAT_AFFECTS_DISSECTION) {
316         /* Just redissect packets if we have any */
317         wsApp->queueAppSignal(WiresharkApplication::PacketDissectionChanged);
318     }
319 }
320
321
322 void UatDialog::acceptChanges()
323 {
324     if (!uat_) return;
325
326     if (uat_->changed) {
327         gchar *err = NULL;
328
329         if (!uat_save(uat_, &err)) {
330             report_failure("Error while saving %s: %s", uat_->name, err);
331             g_free(err);
332         }
333
334         if (uat_->post_update_cb) {
335             uat_->post_update_cb();
336         }
337         applyChanges();
338     }
339 }
340
341 void UatDialog::rejectChanges()
342 {
343     if (!uat_) return;
344
345     if (uat_->changed) {
346         gchar *err = NULL;
347         uat_clear(uat_);
348         if (!uat_load(uat_, &err)) {
349             report_failure("Error while loading %s: %s", uat_->name, err);
350             g_free(err);
351         }
352         applyChanges();
353     }
354 }
355
356 void UatDialog::on_buttonBox_helpRequested()
357 {
358     if (!uat_) return;
359
360     QString help_page = uat_->help, url;
361
362     help_page.append(".html");
363     url = gchar_free_to_qstring(user_guide_url(help_page.toUtf8().constData()));
364     if (!url.isNull()) {
365         QDesktopServices::openUrl(QUrl(url));
366     }
367 }
368
369 /* * Editor modelines
370  *
371  * Local Variables:
372  * c-basic-offset: 4
373  * tab-width: 8
374  * indent-tabs-mode: nil
375  * End:
376  *
377  * ex: set shiftwidth=4 tabstop=8 expandtab:
378  * :indentSize=4:tabSize=8:noTabs=true:
379  */