update epan/dissectors/pidl/drsuapi/drsuapi.idl from samba
[wireshark-sm.git] / ui / qt / uat_dialog.cpp
blobbf51888858de59e56ab9f764d8114fa6f42d3a2b
1 /* uat_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
8 */
10 #include "uat_dialog.h"
11 #include <ui_uat_dialog.h>
12 #include "main_application.h"
14 #include "epan/strutil.h"
15 #include "epan/uat-int.h"
16 #include "ui/help_url.h"
17 #include <wsutil/report_message.h>
19 #include <ui/qt/widgets/copy_from_profile_button.h>
20 #include <ui/qt/utils/qt_ui_utils.h>
22 #include <QDesktopServices>
23 #include <QPushButton>
24 #include <QUrl>
26 #include <QDebug>
28 // NOTE currently uat setter is always invoked in UatModel even if the uat checker fails.
30 UatDialog::UatDialog(QWidget *parent, epan_uat *uat) :
31 GeometryStateDialog(parent),
32 ui(new Ui::UatDialog),
33 uat_model_(NULL),
34 uat_delegate_(NULL),
35 uat_(uat)
37 ui->setupUi(this);
38 if (uat) loadGeometry(0, 0, uat->name);
40 ok_button_ = ui->buttonBox->button(QDialogButtonBox::Ok);
41 help_button_ = ui->buttonBox->button(QDialogButtonBox::Help);
43 ui->newToolButton->setStockIcon("list-add");
44 ui->deleteToolButton->setStockIcon("list-remove");
45 ui->copyToolButton->setStockIcon("list-copy");
46 ui->moveUpToolButton->setStockIcon("list-move-up");
47 ui->moveDownToolButton->setStockIcon("list-move-down");
48 ui->clearToolButton->setStockIcon("list-clear");
50 #ifdef Q_OS_MAC
51 ui->newToolButton->setAttribute(Qt::WA_MacSmallSize, true);
52 ui->deleteToolButton->setAttribute(Qt::WA_MacSmallSize, true);
53 ui->copyToolButton->setAttribute(Qt::WA_MacSmallSize, true);
54 ui->moveUpToolButton->setAttribute(Qt::WA_MacSmallSize, true);
55 ui->moveDownToolButton->setAttribute(Qt::WA_MacSmallSize, true);
56 ui->clearToolButton->setAttribute(Qt::WA_MacSmallSize, true);
57 ui->pathLabel->setAttribute(Qt::WA_MacSmallSize, true);
58 #endif
60 setUat(uat);
62 // FIXME: this prevents the columns from being resized, even if the text
63 // within a combobox needs more space (e.g. in the USER DLT settings). For
64 // very long filenames in the TLS RSA keys dialog, it also results in a
65 // vertical scrollbar. Maybe remove this since the editor is not limited to
66 // the column width (and overlays other fields if more width is needed)?
67 ui->uatTreeView->header()->setSectionResizeMode(QHeaderView::Interactive);
69 // start editing as soon as the field is selected or when typing starts
70 ui->uatTreeView->setEditTriggers(ui->uatTreeView->editTriggers() |
71 QAbstractItemView::CurrentChanged | QAbstractItemView::AnyKeyPressed);
73 // Do NOT start editing the first column for the first item
74 ui->uatTreeView->setCurrentIndex(QModelIndex());
77 UatDialog::~UatDialog()
79 delete ui;
80 delete uat_delegate_;
81 delete uat_model_;
84 void UatDialog::setUat(epan_uat *uat)
86 QString title(tr("Unknown User Accessible Table"));
88 uat_ = uat;
90 ui->pathLabel->clear();
91 ui->pathLabel->setEnabled(false);
92 help_button_->setEnabled(false);
94 if (uat_) {
95 if (uat_->name) {
96 title = uat_->name;
99 if (uat->from_profile) {
100 CopyFromProfileButton * copy_button = new CopyFromProfileButton(this, uat->filename);
101 ui->buttonBox->addButton(copy_button, QDialogButtonBox::ActionRole);
102 connect(copy_button, &CopyFromProfileButton::copyProfile, this, &UatDialog::copyFromProfile);
105 QString abs_path = gchar_free_to_qstring(uat_get_actual_filename(uat_, false));
106 if (abs_path.length() > 0) {
107 ui->pathLabel->setText(abs_path);
108 ui->pathLabel->setUrl(QUrl::fromLocalFile(abs_path).toString());
109 ui->pathLabel->setToolTip(tr("Open ") + uat->filename);
110 } else {
111 ui->pathLabel->setText(uat_->filename);
113 ui->pathLabel->setEnabled(true);
115 uat_model_ = new UatModel(NULL, uat);
116 uat_delegate_ = new UatDelegate;
117 ui->uatTreeView->setModel(uat_model_);
118 ui->uatTreeView->setItemDelegate(uat_delegate_);
119 ui->uatTreeView->setSelectionMode(QAbstractItemView::ContiguousSelection);
120 resizeColumns();
121 ui->clearToolButton->setEnabled(uat_model_->rowCount() != 0);
123 connect(uat_model_, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
124 this, SLOT(modelDataChanged(QModelIndex)));
125 connect(uat_model_, SIGNAL(rowsRemoved(QModelIndex, int, int)),
126 this, SLOT(modelRowsRemoved()));
127 connect(uat_model_, SIGNAL(modelReset()), this, SLOT(modelRowsReset()));
129 connect(ui->uatTreeView->selectionModel(), &QItemSelectionModel::selectionChanged,
130 this, &UatDialog::uatTreeViewSelectionChanged);
132 ok_button_->setEnabled(!uat_model_->hasErrors());
134 if (uat_->help && strlen(uat_->help) > 0) {
135 help_button_->setEnabled(true);
138 connect(this, SIGNAL(rejected()), this, SLOT(rejectChanges()));
139 connect(this, SIGNAL(accepted()), this, SLOT(acceptChanges()));
142 setWindowTitle(title);
145 void UatDialog::copyFromProfile(QString filename)
147 char *err = NULL;
148 if (uat_load(uat_, filename.toUtf8().constData(), &err)) {
149 uat_->changed = true;
150 uat_model_->reloadUat();
151 } else {
152 report_failure("Error while loading %s: %s", uat_->name, err);
153 g_free(err);
157 // Invoked when a field in the model changes (e.g. by closing the editor)
158 void UatDialog::modelDataChanged(const QModelIndex &topLeft)
160 checkForErrorHint(topLeft, QModelIndex());
161 ok_button_->setEnabled(!uat_model_->hasErrors());
162 resizeColumns();
165 // Invoked after a row has been removed from the model.
166 void UatDialog::modelRowsRemoved()
168 const QModelIndex &current = ui->uatTreeView->currentIndex();
170 // Because currentItemChanged() is called before the row is removed from the model
171 // we also need to check for button enabling here.
172 if (current.isValid()) {
173 ui->moveUpToolButton->setEnabled(current.row() != 0);
174 ui->moveDownToolButton->setEnabled(current.row() != (uat_model_->rowCount() - 1));
175 } else {
176 ui->moveUpToolButton->setEnabled(false);
177 ui->moveDownToolButton->setEnabled(false);
179 ui->clearToolButton->setEnabled(uat_model_->rowCount() != 0);
181 checkForErrorHint(current, QModelIndex());
182 ok_button_->setEnabled(!uat_model_->hasErrors());
185 void UatDialog::modelRowsReset()
187 ui->deleteToolButton->setEnabled(false);
188 ui->clearToolButton->setEnabled(uat_model_->rowCount() != 0);
189 ui->copyToolButton->setEnabled(false);
190 ui->moveUpToolButton->setEnabled(false);
191 ui->moveDownToolButton->setEnabled(false);
194 void UatDialog::uatTreeViewSelectionChanged(const QItemSelection&, const QItemSelection&)
196 QModelIndexList selectedRows = ui->uatTreeView->selectionModel()->selectedRows();
197 qsizetype num_selected = selectedRows.size();
198 if (num_selected > 0) {
199 std::sort(selectedRows.begin(), selectedRows.end());
200 ui->deleteToolButton->setEnabled(true);
201 ui->copyToolButton->setEnabled(true);
202 ui->moveUpToolButton->setEnabled(selectedRows.first().row() > 0);
203 ui->moveDownToolButton->setEnabled(selectedRows.last().row() < uat_model_->rowCount() - 1);
204 } else {
205 ui->deleteToolButton->setEnabled(false);
206 ui->copyToolButton->setEnabled(false);
207 ui->moveUpToolButton->setEnabled(false);
208 ui->moveDownToolButton->setEnabled(false);
212 // Invoked when a different field is selected. Note: when selecting a different
213 // field after editing, this event is triggered after modelDataChanged.
214 void UatDialog::on_uatTreeView_currentItemChanged(const QModelIndex &current, const QModelIndex &previous)
216 if (current.isValid()) {
217 ui->clearToolButton->setEnabled(true);
218 } else {
219 ui->clearToolButton->setEnabled(false);
222 checkForErrorHint(current, previous);
225 // If the current field has errors, show them.
226 // Otherwise if the row has not changed, but the previous field has errors, show them.
227 // Otherwise pick the first error in the current row.
228 // Otherwise show the error from the previous field (if any).
229 // Otherwise clear the error hint.
230 void UatDialog::checkForErrorHint(const QModelIndex &current, const QModelIndex &previous)
232 if (current.isValid()) {
233 if (trySetErrorHintFromField(current)) {
234 return;
237 const int row = current.row();
238 if (row == previous.row() && trySetErrorHintFromField(previous)) {
239 return;
242 for (int i = 0; i < uat_model_->columnCount(); i++) {
243 if (trySetErrorHintFromField(uat_model_->index(row, i))) {
244 return;
249 if (previous.isValid()) {
250 if (trySetErrorHintFromField(previous)) {
251 return;
255 ui->hintLabel->clear();
258 bool UatDialog::trySetErrorHintFromField(const QModelIndex &index)
260 const QVariant &data = uat_model_->data(index, Qt::UserRole + 1);
261 if (!data.isNull()) {
262 // use HTML instead of PlainText because that handles wordwrap properly
263 ui->hintLabel->setText("<small><i>" + html_escape(data.toString()) + "</i></small>");
264 return true;
266 return false;
269 void UatDialog::addRecord(bool copy_from_current)
271 if (!uat_) return;
273 QModelIndex current = ui->uatTreeView->currentIndex();
274 if (copy_from_current && !current.isValid()) return;
276 QModelIndex new_index;
277 if (copy_from_current) {
278 new_index = uat_model_->copyRow(current);
279 } else {
280 // should not fail, but you never know.
281 if (!uat_model_->insertRows(uat_model_->rowCount(), 1)) {
282 qDebug() << "Failed to add a new record";
283 return;
286 new_index = uat_model_->index(uat_model_->rowCount() - 1, 0);
289 // due to an EditTrigger, this will also start editing.
290 ui->uatTreeView->setCurrentIndex(new_index);
291 // trigger updating error messages and the OK button state.
292 modelDataChanged(new_index);
295 void UatDialog::on_newToolButton_clicked()
297 addRecord();
300 void UatDialog::on_deleteToolButton_clicked()
302 if (uat_model_ == nullptr) {
303 return;
306 for (const auto &range : ui->uatTreeView->selectionModel()->selection()) {
307 // Each QItemSelectionRange is contiguous
308 if (!range.isEmpty()) {
309 if (!uat_model_->removeRows(range.top(), range.bottom() - range.top() + 1)) {
310 qDebug() << "Failed to remove rows" << range.top() << "to" << range.bottom();
316 void UatDialog::on_copyToolButton_clicked()
318 if (uat_model_ == nullptr) {
319 return;
322 QModelIndexList selectedRows = ui->uatTreeView->selectionModel()->selectedRows();
323 if (selectedRows.size() > 0) {
324 std::sort(selectedRows.begin(), selectedRows.end());
326 QModelIndex copyIdx;
328 for (const auto &idx : selectedRows) {
329 copyIdx = uat_model_->copyRow(idx);
330 if (!copyIdx.isValid())
332 qDebug() << "Failed to copy row" << idx.row();
334 // trigger updating error messages and the OK button state.
335 modelDataChanged(copyIdx);
337 // due to an EditTrigger, this will also start editing.
338 ui->uatTreeView->setCurrentIndex(copyIdx);
343 void UatDialog::on_moveUpToolButton_clicked()
345 if (uat_model_ == nullptr) {
346 return;
349 for (const auto &range : ui->uatTreeView->selectionModel()->selection()) {
350 // Each QItemSelectionRange is contiguous
351 if (!range.isEmpty() && range.top() > 0) {
352 // Swap range of rows with the row above the top
353 if (! uat_model_->moveRows(QModelIndex(), range.top(), range.bottom() - range.top() + 1, QModelIndex(), range.top() - 1)) {
354 qDebug() << "Failed to move up rows" << range.top() << "to" << range.bottom();
356 // Our moveRows implementation calls begin/endMoveRows(), so
357 // range.top() already has the new row number.
358 ui->moveUpToolButton->setEnabled(range.top() > 0);
359 ui->moveDownToolButton->setEnabled(true);
364 void UatDialog::on_moveDownToolButton_clicked()
366 if (uat_model_ == nullptr) {
367 return;
370 for (const auto &range : ui->uatTreeView->selectionModel()->selection()) {
371 // Each QItemSelectionRange is contiguous
372 if (!range.isEmpty() && range.bottom() + 1 < uat_model_->rowCount()) {
373 // Swap range of rows with the row below the top
374 if (! uat_model_->moveRows(QModelIndex(), range.top(), range.bottom() - range.top() + 1, QModelIndex(), range.bottom() + 1)) {
375 qDebug() << "Failed to move down rows" << range.top() << "to" << range.bottom();
377 // Our moveRows implementation calls begin/endMoveRows, so
378 // range.bottom() already has the new row number.
379 ui->moveUpToolButton->setEnabled(true);
380 ui->moveDownToolButton->setEnabled(range.bottom() < uat_model_->rowCount() - 1);
385 void UatDialog::on_clearToolButton_clicked()
387 if (uat_model_) {
388 uat_model_->clearAll();
392 void UatDialog::applyChanges()
394 if (!uat_) return;
396 if (uat_->flags & UAT_AFFECTS_FIELDS) {
397 /* Recreate list with new fields */
398 mainApp->queueAppSignal(MainApplication::FieldsChanged);
400 if (uat_->flags & UAT_AFFECTS_DISSECTION) {
401 /* Redissect packets if we have any */
402 mainApp->queueAppSignal(MainApplication::PacketDissectionChanged);
407 void UatDialog::acceptChanges()
409 if (!uat_model_) return;
411 QString error;
412 if (uat_model_->applyChanges(error)) {
413 if (!error.isEmpty()) {
414 report_failure("%s", qPrintable(error));
416 applyChanges();
420 void UatDialog::rejectChanges()
422 if (!uat_model_) return;
424 QString error;
425 if (uat_model_->revertChanges(error)) {
426 if (!error.isEmpty()) {
427 report_failure("%s", qPrintable(error));
429 // Why do we have to trigger a redissection? If the original UAT is
430 // restored and dissectors only apply changes after the post_update_cb
431 // method is invoked, then it should not be necessary to trigger
432 // redissection. One potential exception is when something modifies the
433 // UAT file after Wireshark has started, but this behavior is not
434 // supported and causes potentially unnecessary redissection whenever
435 // the preferences dialog is closed.
436 // XXX audit all UAT providers and check whether it is safe to remove
437 // the next call (that is, when their update_cb has no side-effects).
438 applyChanges();
442 void UatDialog::on_buttonBox_helpRequested()
444 if (!uat_) return;
446 QString help_page = uat_->help, url;
448 help_page.append(".html");
449 url = gchar_free_to_qstring(user_guide_url(help_page.toUtf8().constData()));
450 if (!url.isNull()) {
451 QDesktopServices::openUrl(QUrl(url));
455 void UatDialog::resizeColumns()
457 for (int i = 0; i < uat_model_->columnCount(); i++) {
458 ui->uatTreeView->resizeColumnToContents(i);
459 if (i == 0) {
460 ui->uatTreeView->setColumnWidth(i, ui->uatTreeView->columnWidth(i)+ui->uatTreeView->indentation());