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