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 
30 UatDelegate::UatDelegate(QObject *parent) : QStyledItemDelegate(parent)
31 {
32 }
33 
34 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 
40 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 
142 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 
170 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 
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