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
12 #include <epan/filter_expressions.h>
14 #include "uat_frame.h"
15 #include <ui_uat_frame.h>
16 #include <ui/qt/widgets/display_filter_edit.h>
17 #include "main_application.h"
19 #include <ui/qt/widgets/copy_from_profile_button.h>
20 #include <ui/qt/utils/qt_ui_utils.h>
21 #include <wsutil/report_message.h>
25 #include <QTreeWidgetItemIterator>
30 UatFrame::UatFrame(QWidget
*parent
) :
39 ui
->newToolButton
->setStockIcon("list-add");
40 ui
->deleteToolButton
->setStockIcon("list-remove");
41 ui
->copyToolButton
->setStockIcon("list-copy");
42 ui
->moveUpToolButton
->setStockIcon("list-move-up");
43 ui
->moveDownToolButton
->setStockIcon("list-move-down");
44 ui
->clearToolButton
->setStockIcon("list-clear");
47 ui
->newToolButton
->setAttribute(Qt::WA_MacSmallSize
, true);
48 ui
->deleteToolButton
->setAttribute(Qt::WA_MacSmallSize
, true);
49 ui
->copyToolButton
->setAttribute(Qt::WA_MacSmallSize
, true);
50 ui
->moveUpToolButton
->setAttribute(Qt::WA_MacSmallSize
, true);
51 ui
->moveDownToolButton
->setAttribute(Qt::WA_MacSmallSize
, true);
52 ui
->clearToolButton
->setAttribute(Qt::WA_MacSmallSize
, true);
53 ui
->pathLabel
->setAttribute(Qt::WA_MacSmallSize
, true);
56 // FIXME: this prevents the columns from being resized, even if the text
57 // within a combobox needs more space (e.g. in the USER DLT settings). For
58 // very long filenames in the TLS RSA keys dialog, it also results in a
59 // vertical scrollbar. Maybe remove this since the editor is not limited to
60 // the column width (and overlays other fields if more width is needed)?
61 ui
->uatTreeView
->header()->setSectionResizeMode(QHeaderView::Interactive
);
63 // start editing as soon as the field is selected or when typing starts
64 ui
->uatTreeView
->setEditTriggers(ui
->uatTreeView
->editTriggers() |
65 QAbstractItemView::CurrentChanged
| QAbstractItemView::AnyKeyPressed
);
75 void UatFrame::setUat(epan_uat
*uat
)
77 QString
title(tr("Unknown User Accessible Table"));
81 ui
->pathLabel
->clear();
82 ui
->pathLabel
->setEnabled(false);
89 if (uat
->from_profile
) {
90 ui
->copyFromProfileButton
->setFilename(uat
->filename
);
91 connect(ui
->copyFromProfileButton
, &CopyFromProfileButton::copyProfile
, this, &UatFrame::copyFromProfile
);
94 QString abs_path
= gchar_free_to_qstring(uat_get_actual_filename(uat_
, false));
95 if (abs_path
.length() > 0) {
96 ui
->pathLabel
->setText(abs_path
);
97 ui
->pathLabel
->setUrl(QUrl::fromLocalFile(abs_path
).toString());
98 ui
->pathLabel
->setToolTip(tr("Open ") + uat
->filename
);
100 ui
->pathLabel
->setText(uat_
->filename
);
102 ui
->pathLabel
->setEnabled(true);
104 uat_model_
= new UatModel(NULL
, uat
);
105 uat_delegate_
= new UatDelegate
;
106 ui
->uatTreeView
->setModel(uat_model_
);
107 ui
->uatTreeView
->setItemDelegate(uat_delegate_
);
108 ui
->uatTreeView
->setSelectionMode(QAbstractItemView::ContiguousSelection
);
110 ui
->clearToolButton
->setEnabled(uat_model_
->rowCount() != 0);
112 connect(uat_model_
, SIGNAL(dataChanged(QModelIndex
,QModelIndex
)),
113 this, SLOT(modelDataChanged(QModelIndex
)));
114 connect(uat_model_
, SIGNAL(rowsRemoved(QModelIndex
, int, int)),
115 this, SLOT(modelRowsRemoved()));
116 connect(uat_model_
, SIGNAL(modelReset()), this, SLOT(modelRowsReset()));
118 connect(ui
->uatTreeView
->selectionModel(), &QItemSelectionModel::selectionChanged
,
119 this, &UatFrame::uatTreeViewSelectionChanged
);
122 setWindowTitle(title
);
125 void UatFrame::copyFromProfile(QString filename
)
128 if (uat_load(uat_
, filename
.toUtf8().constData(), &err
)) {
129 uat_
->changed
= true;
130 uat_model_
->reloadUat();
132 report_failure("Error while loading %s: %s", uat_
->name
, err
);
137 void UatFrame::showEvent(QShowEvent
*)
140 ui
->copyFromProfileButton
->setFixedHeight(ui
->copyToolButton
->geometry().height());
144 void UatFrame::applyChanges()
148 if (uat_
->flags
& UAT_AFFECTS_FIELDS
) {
149 /* Recreate list with new fields */
150 mainApp
->queueAppSignal(MainApplication::FieldsChanged
);
152 if (uat_
->flags
& UAT_AFFECTS_DISSECTION
) {
153 /* Redissect packets if we have any */
154 mainApp
->queueAppSignal(MainApplication::PacketDissectionChanged
);
158 void UatFrame::acceptChanges()
160 if (!uat_model_
) return;
163 if (uat_model_
->applyChanges(error
)) {
164 if (!error
.isEmpty()) {
165 report_failure("%s", qPrintable(error
));
171 void UatFrame::rejectChanges()
173 if (!uat_model_
) return;
176 if (uat_model_
->revertChanges(error
)) {
177 if (!error
.isEmpty()) {
178 report_failure("%s", qPrintable(error
));
183 void UatFrame::addRecord(bool copy_from_current
)
187 QModelIndex current
= ui
->uatTreeView
->currentIndex();
188 if (copy_from_current
&& !current
.isValid()) return;
190 QModelIndex new_index
;
191 if (copy_from_current
) {
192 new_index
= uat_model_
->copyRow(current
);
194 // should not fail, but you never know.
195 if (!uat_model_
->insertRows(uat_model_
->rowCount(), 1)) {
196 qDebug() << "Failed to add a new record";
199 new_index
= uat_model_
->index(uat_model_
->rowCount() - 1, 0);
202 // due to an EditTrigger, this will also start editing.
203 ui
->uatTreeView
->setCurrentIndex(new_index
);
204 // trigger updating error messages and the OK button state.
205 modelDataChanged(new_index
);
208 void UatFrame::uatTreeViewSelectionChanged(const QItemSelection
&, const QItemSelection
&)
210 QModelIndexList selectedRows
= ui
->uatTreeView
->selectionModel()->selectedRows();
211 qsizetype num_selected
= selectedRows
.size();
212 if (num_selected
> 0) {
213 std::sort(selectedRows
.begin(), selectedRows
.end());
214 ui
->deleteToolButton
->setEnabled(true);
215 ui
->copyToolButton
->setEnabled(true);
216 ui
->moveUpToolButton
->setEnabled(selectedRows
.first().row() > 0);
217 ui
->moveDownToolButton
->setEnabled(selectedRows
.last().row() < uat_model_
->rowCount() - 1);
219 ui
->deleteToolButton
->setEnabled(false);
220 ui
->copyToolButton
->setEnabled(false);
221 ui
->moveUpToolButton
->setEnabled(false);
222 ui
->moveDownToolButton
->setEnabled(false);
226 // Invoked when a different field is selected. Note: when selecting a different
227 // field after editing, this event is triggered after modelDataChanged.
228 void UatFrame::on_uatTreeView_currentItemChanged(const QModelIndex
¤t
, const QModelIndex
&previous
)
230 if (current
.isValid()) {
231 ui
->clearToolButton
->setEnabled(true);
233 ui
->clearToolButton
->setEnabled(false);
236 checkForErrorHint(current
, previous
);
239 // Invoked when a field in the model changes (e.g. by closing the editor)
240 void UatFrame::modelDataChanged(const QModelIndex
&topLeft
)
242 checkForErrorHint(topLeft
, QModelIndex());
246 // Invoked after a row has been removed from the model.
247 void UatFrame::modelRowsRemoved()
249 const QModelIndex
¤t
= ui
->uatTreeView
->currentIndex();
251 // Because currentItemChanged() is called before the row is removed from the model
252 // we also need to check for button enabling here.
253 if (current
.isValid()) {
254 ui
->moveUpToolButton
->setEnabled(current
.row() != 0);
255 ui
->moveDownToolButton
->setEnabled(current
.row() != (uat_model_
->rowCount() - 1));
257 ui
->moveUpToolButton
->setEnabled(false);
258 ui
->moveDownToolButton
->setEnabled(false);
260 ui
->clearToolButton
->setEnabled(uat_model_
->rowCount() != 0);
262 checkForErrorHint(current
, QModelIndex());
265 void UatFrame::modelRowsReset()
267 ui
->deleteToolButton
->setEnabled(false);
268 ui
->clearToolButton
->setEnabled(uat_model_
->rowCount() != 0);
269 ui
->copyToolButton
->setEnabled(false);
270 ui
->moveUpToolButton
->setEnabled(false);
271 ui
->moveDownToolButton
->setEnabled(false);
274 // If the current field has errors, show them.
275 // Otherwise if the row has not changed, but the previous field has errors, show them.
276 // Otherwise pick the first error in the current row.
277 // Otherwise show the error from the previous field (if any).
278 // Otherwise clear the error hint.
279 void UatFrame::checkForErrorHint(const QModelIndex
¤t
, const QModelIndex
&previous
)
281 if (current
.isValid()) {
282 if (trySetErrorHintFromField(current
)) {
286 const int row
= current
.row();
287 if (row
== previous
.row() && trySetErrorHintFromField(previous
)) {
291 for (int i
= 0; i
< uat_model_
->columnCount(); i
++) {
292 if (trySetErrorHintFromField(uat_model_
->index(row
, i
))) {
298 if (previous
.isValid()) {
299 if (trySetErrorHintFromField(previous
)) {
304 ui
->hintLabel
->clear();
307 bool UatFrame::trySetErrorHintFromField(const QModelIndex
&index
)
309 const QVariant
&data
= uat_model_
->data(index
, Qt::UserRole
+ 1);
310 if (!data
.isNull()) {
311 // use HTML instead of PlainText because that handles wordwrap properly
312 ui
->hintLabel
->setText("<small><i>" + html_escape(data
.toString()) + "</i></small>");
318 void UatFrame::on_newToolButton_clicked()
323 void UatFrame::on_deleteToolButton_clicked()
325 if (uat_model_
== nullptr) {
329 for (const auto &range
: ui
->uatTreeView
->selectionModel()->selection()) {
330 // Each QItemSelectionRange is contiguous
331 if (!range
.isEmpty()) {
332 if (!uat_model_
->removeRows(range
.top(), range
.bottom() - range
.top() + 1)) {
333 qDebug() << "Failed to remove rows" << range
.top() << "to" << range
.bottom();
339 void UatFrame::on_copyToolButton_clicked()
341 if (uat_model_
== nullptr) {
345 QModelIndexList selectedRows
= ui
->uatTreeView
->selectionModel()->selectedRows();
346 if (selectedRows
.size() > 0) {
347 std::sort(selectedRows
.begin(), selectedRows
.end());
351 for (const auto &idx
: selectedRows
) {
352 copyIdx
= uat_model_
->copyRow(idx
);
353 if (!copyIdx
.isValid())
355 qDebug() << "Failed to copy row" << idx
.row();
357 // trigger updating error messages and the OK button state.
358 modelDataChanged(copyIdx
);
360 // due to an EditTrigger, this will also start editing.
361 ui
->uatTreeView
->setCurrentIndex(copyIdx
);
366 void UatFrame::on_moveUpToolButton_clicked()
368 if (uat_model_
== nullptr) {
372 for (const auto &range
: ui
->uatTreeView
->selectionModel()->selection()) {
373 // Each QItemSelectionRange is contiguous
374 if (!range
.isEmpty() && range
.top() > 0) {
375 // Swap range of rows with the row above the top
376 if (! uat_model_
->moveRows(QModelIndex(), range
.top(), range
.bottom() - range
.top() + 1, QModelIndex(), range
.top() - 1)) {
377 qDebug() << "Failed to move up rows" << range
.top() << "to" << range
.bottom();
379 // Our moveRows implementation calls begin/endMoveRows(), so
380 // range.top() already has the new row number.
381 ui
->moveUpToolButton
->setEnabled(range
.top() > 0);
382 ui
->moveDownToolButton
->setEnabled(true);
387 void UatFrame::on_moveDownToolButton_clicked()
389 if (uat_model_
== nullptr) {
393 for (const auto &range
: ui
->uatTreeView
->selectionModel()->selection()) {
394 // Each QItemSelectionRange is contiguous
395 if (!range
.isEmpty() && range
.bottom() + 1 < uat_model_
->rowCount()) {
396 // Swap range of rows with the row below the top
397 if (! uat_model_
->moveRows(QModelIndex(), range
.top(), range
.bottom() - range
.top() + 1, QModelIndex(), range
.bottom() + 1)) {
398 qDebug() << "Failed to move down rows" << range
.top() << "to" << range
.bottom();
400 // Our moveRows implementation calls begin/endMoveRows, so
401 // range.bottom() already has the new row number.
402 ui
->moveUpToolButton
->setEnabled(true);
403 ui
->moveDownToolButton
->setEnabled(range
.bottom() < uat_model_
->rowCount() - 1);
408 void UatFrame::on_clearToolButton_clicked()
411 uat_model_
->clearAll();
415 void UatFrame::resizeColumns()
417 for (int i
= 0; i
< uat_model_
->columnCount(); i
++) {
418 ui
->uatTreeView
->resizeColumnToContents(i
);
420 ui
->uatTreeView
->setColumnWidth(i
, ui
->uatTreeView
->columnWidth(i
)+ui
->uatTreeView
->indentation());