1 /* uat_delegate.cpp
2 * Delegates for editing various field types in a UAT record.
3 *
4 * Copyright 2016 Peter Wu <peter@lekensteyn.nl>
5 *
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
9 *
10 * SPDX-License-Identifier: GPL-2.0-or-later
11 */
12
13 #include <ui/qt/models/uat_delegate.h>
14 #include "epan/value_string.h"
15 #include <wsutil/ws_assert.h>
16 #include <QComboBox>
17 #include <QEvent>
18 #include <QFileDialog>
19 #include <QLineEdit>
20 #include <QCheckBox>
21 #include <QColorDialog>
22
23 #include <ui/qt/widgets/display_filter_edit.h>
24 #include <ui/qt/widgets/field_filter_edit.h>
25 #include <ui/qt/widgets/editor_file_dialog.h>
26
27 // The Qt docs suggest overriding updateEditorGeometry, but the
28 // defaults seem sane.
29
UatDelegate(QObject * parent)30 UatDelegate::UatDelegate(QObject *parent) : QStyledItemDelegate(parent)
31 {
32 }
33
indexToField(const QModelIndex & index) const34 uat_field_t *UatDelegate::indexToField(const QModelIndex &index) const
35 {
36 const QVariant v = index.model()->data(index, Qt::UserRole);
37 return static_cast<uat_field_t *>(v.value<void *>());
38 }
39
createEditor(QWidget * parent,const QStyleOptionViewItem & option,const QModelIndex & index) const40 QWidget *UatDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option,
41 const QModelIndex &index) const
42 {
43 uat_field_t *field = indexToField(index);
44 QWidget *editor = nullptr;
45
46 switch (field->mode) {
47 case PT_TXTMOD_DIRECTORYNAME:
48 if (index.isValid()) {
49 QString filename_old = index.model()->data(index, Qt::EditRole).toString();
50 EditorFileDialog* fileDialog = new EditorFileDialog(index, EditorFileDialog::Directory, parent, QString(field->title), filename_old);
51
52 // Use signals to accept data from cell
53 connect(fileDialog, &EditorFileDialog::acceptEdit, this, &UatDelegate::applyFilename);
54 // Don't fall through and set setAutoFillBackground(true)
55 return fileDialog;
56 }
57 break;
58
59 case PT_TXTMOD_FILENAME:
60 if (index.isValid()) {
61 QString filename_old = index.model()->data(index, Qt::EditRole).toString();
62 EditorFileDialog* fileDialog = new EditorFileDialog(index, EditorFileDialog::ExistingFile, parent, QString(field->title), filename_old);
63
64 fileDialog->setOption(QFileDialog::DontConfirmOverwrite);
65
66 // Use signals to accept data from cell
67 connect(fileDialog, &EditorFileDialog::acceptEdit, this, &UatDelegate::applyFilename);
68 // Don't fall through and set setAutoFillBackground(true)
69 return fileDialog;
70 }
71 break;
72
73 case PT_TXTMOD_COLOR:
74 if (index.isValid()) {
75 QColor color(index.model()->data(index, Qt::DecorationRole).toString());
76 QColorDialog * colorDialog = new QColorDialog(color, parent);
77 // Don't fall through and set setAutoFillBackground(true)
78 return colorDialog;
79 }
80 break;
81
82 case PT_TXTMOD_ENUM:
83 {
84 // Note: the string repr. is written, not the integer value.
85 QComboBox *cb_editor = new QComboBox(parent);
86 const value_string *enum_vals = (const value_string *)field->fld_data;
87 for (int i = 0; enum_vals[i].strptr != nullptr; i++) {
88 cb_editor->addItem(enum_vals[i].strptr);
89 }
90 editor = cb_editor;
91 cb_editor->setMinimumWidth(cb_editor->minimumSizeHint().width());
92 break;
93 }
94
95 case PT_TXTMOD_STRING:
96 // TODO add a live validator? Should SyntaxLineEdit be used?
97 editor = QStyledItemDelegate::createEditor(parent, option, index);
98 break;
99
100 case PT_TXTMOD_DISPLAY_FILTER:
101 editor = new DisplayFilterEdit(parent);
102 break;
103
104 case PT_TXTMOD_PROTO_FIELD:
105 editor = new FieldFilterEdit(parent);
106 break;
107
108 case PT_TXTMOD_HEXBYTES:
109 {
110 // Requires input of the form "ab cd ef" (with possibly no or a colon
111 // separator instead of a single whitespace) for the editor to accept.
112 QRegExp hexbytes_regex("([0-9a-f]{2}[ :]?)*");
113 hexbytes_regex.setCaseSensitivity(Qt::CaseInsensitive);
114 // QString types from QStyledItemDelegate are documented to return a
115 // QLineEdit. Note that Qt returns a subclass from QLineEdit which
116 // automatically adapts the width to the typed contents.
117 QLineEdit *le_editor = static_cast<QLineEdit *>(
118 QStyledItemDelegate::createEditor(parent, option, index));
119 le_editor->setValidator(new QRegExpValidator(hexbytes_regex, le_editor));
120 editor = le_editor;
121 break;
122 }
123
124 case PT_TXTMOD_BOOL:
125 // model will handle creating checkbox
126 break;
127
128 case PT_TXTMOD_NONE:
129 break;
130
131 default:
132 ws_assert_not_reached();
133 break;
134 }
135
136 if (editor) {
137 editor->setAutoFillBackground(true);
138 }
139 return editor;
140 }
141
setEditorData(QWidget * editor,const QModelIndex & index) const142 void UatDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
143 {
144 uat_field_t *field = indexToField(index);
145
146 switch (field->mode) {
147 case PT_TXTMOD_ENUM:
148 {
149 QComboBox *combobox = static_cast<QComboBox *>(editor);
150 const QString &data = index.model()->data(index, Qt::EditRole).toString();
151 combobox->setCurrentText(data);
152
153 break;
154 }
155 case PT_TXTMOD_COLOR:
156 {
157 if (qobject_cast<QColorDialog *>(editor))
158 {
159 QColor color(index.model()->data(index, Qt::DecorationRole).toString());
160 qobject_cast<QColorDialog *>(editor)->setCurrentColor(color);
161 }
162 break;
163 }
164
165 default:
166 QStyledItemDelegate::setEditorData(editor, index);
167 }
168 }
169
setModelData(QWidget * editor,QAbstractItemModel * model,const QModelIndex & index) const170 void UatDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
171 const QModelIndex &index) const
172 {
173 uat_field_t *field = indexToField(index);
174
175 switch (field->mode) {
176 case PT_TXTMOD_ENUM:
177 {
178 QComboBox *combobox = static_cast<QComboBox *>(editor);
179 const QString &data = combobox->currentText();
180 model->setData(index, data, Qt::EditRole);
181 break;
182 }
183 case PT_TXTMOD_COLOR:
184 //do nothing, dialog signals will update table
185 if (qobject_cast<QColorDialog *>(editor))
186 {
187 QColor newColor = qobject_cast<QColorDialog *>(editor)->currentColor();
188 const_cast<QAbstractItemModel *>(index.model())->setData(index, newColor.name(), Qt::EditRole);
189 }
190 break;
191
192 default:
193 QStyledItemDelegate::setModelData(editor, model, index);
194 }
195 }
196
applyFilename(const QModelIndex & index)197 void UatDelegate::applyFilename(const QModelIndex& index)
198 {
199 if (index.isValid()) {
200 EditorFileDialog* fileDialog = static_cast<EditorFileDialog*>(sender());
201 const_cast<QAbstractItemModel *>(index.model())->setData(index, fileDialog->text(), Qt::EditRole);
202 }
203 }
204