aboutsummaryrefslogtreecommitdiffstats
path: root/ui/qt/models/decode_as_delegate.cpp
diff options
context:
space:
mode:
authorMichael Mann <mmann78@netscape.net>2017-07-14 15:21:37 -0400
committerMichael Mann <mmann78@netscape.net>2017-09-16 11:11:50 +0000
commitd992e85fe8e9192cb730abf8796336d3b6d6fd50 (patch)
tree8dc40c70e01bdf3855f3e6950079bb0e28d622a5 /ui/qt/models/decode_as_delegate.cpp
parentc98a7363dafdb1a261068a3e7312e66dcdf2c329 (diff)
Refactor Decode As dialog to use a model/delegate
The model provides a lot more flexibility and abstracting the data from the view (dialog) Noticeable changes from user perspective: 1. Value column doesn't always have a combobox. If registered decode as structure doesn't support multiple values, a simple edit box is used. 2. Existing value will always be a choice (default) in the Value combobox. 3. Duplicate values in Value combobox have been removed (for things like UDP port where source port and dest. port are same) 4. When adding/copying a decode as item, only first column (table) is editable, not the whole column. 5. Separator always present in Current protocol column to distinguish "none" from rest of protocols. 6. "Current" protocol defaults to "default value" when first added to the list instead of "none". Noticeable changes from developer perspective: 1. Code is much more spread out, but most new additions (like DCE/RPC support) should be limited to the model class (maybe delegate). The dialog class probably won't change much anymore. 2. decode_as_dialog.ui is much less useful because information is provided through model and delegate. Change-Id: I70a667cab2c07d251ab370430bc51e5c1f4a3a02 Reviewed-on: https://code.wireshark.org/review/22625 Petri-Dish: Michael Mann <mmann78@netscape.net> Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org> Reviewed-by: Michael Mann <mmann78@netscape.net>
Diffstat (limited to 'ui/qt/models/decode_as_delegate.cpp')
-rw-r--r--ui/qt/models/decode_as_delegate.cpp408
1 files changed, 408 insertions, 0 deletions
diff --git a/ui/qt/models/decode_as_delegate.cpp b/ui/qt/models/decode_as_delegate.cpp
new file mode 100644
index 0000000000..bc632f5449
--- /dev/null
+++ b/ui/qt/models/decode_as_delegate.cpp
@@ -0,0 +1,408 @@
+/* decode_as_delegate.cpp
+ * Delegates for editing various field types in a Decode As record.
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "decode_as_delegate.h"
+
+#include "epan/decode_as.h"
+#include "epan/epan_dissect.h"
+
+#include <ui/qt/utils/variant_pointer.h>
+
+#include <QComboBox>
+#include <QEvent>
+#include <QLineEdit>
+#include <QTreeView>
+
+typedef struct _dissector_info_t {
+ QString proto_name;
+ dissector_handle_t dissector_handle;
+} dissector_info_t;
+
+Q_DECLARE_METATYPE(dissector_info_t *)
+
+DecodeAsDelegate::DecodeAsDelegate(QObject *parent, capture_file *cf)
+ : QStyledItemDelegate(parent),
+ cap_file_(cf)
+{
+ cachePacketProtocols();
+}
+
+DecodeAsItem* DecodeAsDelegate::indexToField(const QModelIndex &index) const
+{
+ const QVariant v = index.model()->data(index, Qt::UserRole);
+ return static_cast<DecodeAsItem*>(v.value<void *>());
+}
+
+void DecodeAsDelegate::cachePacketProtocols()
+{
+ //cache the list of potential decode as protocols in the current packet
+ if (cap_file_ && cap_file_->edt) {
+
+ wmem_list_frame_t * protos = wmem_list_head(cap_file_->edt->pi.layers);
+ guint8 curr_layer_num = 1;
+
+ while (protos != NULL) {
+ int proto_id = GPOINTER_TO_INT(wmem_list_frame_data(protos));
+ const gchar * proto_name = proto_get_protocol_filter_name(proto_id);
+ for (GList *cur = decode_as_list; cur; cur = cur->next) {
+ decode_as_t *entry = (decode_as_t *) cur->data;
+ if (g_strcmp0(proto_name, entry->name) == 0) {
+ packet_proto_data_t proto_data;
+
+ proto_data.table_ui_name = get_dissector_table_ui_name(entry->table_name);
+ proto_data.proto_name = proto_name;
+ proto_data.curr_layer_num = curr_layer_num;
+
+ packet_proto_list_.append(proto_data);
+ }
+ }
+ protos = wmem_list_frame_next(protos);
+ curr_layer_num++;
+ }
+ }
+}
+
+void DecodeAsDelegate::collectDAProtocols(QSet<QString>& all_protocols, QList<QString>& current_list) const
+{
+ // If a packet is selected group its tables at the top in order
+ // from last-dissected to first.
+
+ //gather the initial list
+ for (GList *cur = decode_as_list; cur; cur = cur->next) {
+ decode_as_t *entry = (decode_as_t *) cur->data;
+ const char *table_name = get_dissector_table_ui_name(entry->table_name);
+ if (table_name) {
+ all_protocols.insert(get_dissector_table_ui_name(entry->table_name));
+ }
+ }
+
+ //filter out those in selected packet
+ foreach(packet_proto_data_t proto, packet_proto_list_)
+ {
+ current_list.append(proto.table_ui_name);
+ all_protocols.remove(proto.table_ui_name);
+ }
+}
+
+//Determine if there are multiple values in the selector field that would
+//correspond to using a combo box
+bool DecodeAsDelegate::isSelectorCombo(DecodeAsItem* item) const
+{
+ const gchar *proto_name = NULL;
+
+ foreach(packet_proto_data_t proto, packet_proto_list_)
+ {
+ if (g_strcmp0(proto.table_ui_name, item->tableUIName_) == 0) {
+ proto_name = proto.proto_name;
+ break;
+ }
+ }
+
+ for (GList *cur = decode_as_list; cur; cur = cur->next) {
+ decode_as_t *entry = (decode_as_t *) cur->data;
+ if ((g_strcmp0(proto_name, entry->name) == 0) &&
+ (g_strcmp0(item->tableName_, entry->table_name) == 0) &&
+ (cap_file_ && cap_file_->edt) &&
+ (entry->num_items > 1)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void DecodeAsDelegate::decodeAddProtocol(const gchar *, const gchar *proto_name, gpointer value, gpointer user_data)
+{
+ QMap<QString, dissector_info_t*>* proto_list = (QMap<QString, dissector_info_t*>*)user_data;
+
+ if (!proto_list)
+ return;
+
+ dissector_info_t *dissector_info = new dissector_info_t();
+ dissector_info->proto_name = proto_name;
+ dissector_info->dissector_handle = (dissector_handle_t) value;
+
+ proto_list->insert(proto_name, dissector_info);
+}
+
+QWidget* DecodeAsDelegate::createEditor(QWidget *parentWidget, const QStyleOptionViewItem &option,
+ const QModelIndex &index) const
+{
+ DecodeAsItem* item = indexToField(index);
+
+ switch(index.column())
+ {
+ case DecodeAsModel::colTable:
+ {
+ QComboBox *editor = new QComboBox(parentWidget);
+ QSet<QString> da_set;
+ QList<QString> packet_list;
+ QString table_ui_name;
+
+ collectDAProtocols(da_set, packet_list);
+
+ editor->setSizeAdjustPolicy(QComboBox::AdjustToContents);
+
+ //put the protocols from the packet first in the combo box
+ foreach (table_ui_name, packet_list) {
+ editor->addItem(table_ui_name, table_ui_name);
+ }
+ if (packet_list.count() > 0) {
+ editor->insertSeparator(packet_list.count());
+ }
+
+ //put the rest of the protocols in the combo box
+ QList<QString> da_list = da_set.toList();
+ qSort(da_list.begin(), da_list.end());
+
+ foreach (table_ui_name, da_list) {
+ editor->addItem(table_ui_name, table_ui_name);
+ }
+
+ //Make sure the combo box is at least as wide as the column
+ QTreeView* parentTree = (QTreeView*)parent();
+ int protoColWidth = parentTree->columnWidth(index.column());
+ if (protoColWidth > editor->size().width())
+ editor->setFixedWidth(protoColWidth);
+
+ return editor;
+ }
+ case DecodeAsModel::colSelector:
+ {
+ QComboBox *editor = NULL;
+ const gchar *proto_name = NULL;
+ bool edt_present = cap_file_ && cap_file_->edt;
+ gint8 curr_layer_num_saved = edt_present ? cap_file_->edt->pi.curr_layer_num : 0;
+
+ foreach(packet_proto_data_t proto, packet_proto_list_)
+ {
+ if (g_strcmp0(proto.table_ui_name, item->tableUIName_) == 0) {
+ if (edt_present) {
+ cap_file_->edt->pi.curr_layer_num = proto.curr_layer_num;
+ }
+ proto_name = proto.proto_name;
+ //XXX - break? Or do we always want the last layer of tunnelled protocols?
+ }
+ }
+
+ for (GList *cur = decode_as_list; cur; cur = cur->next) {
+ decode_as_t *entry = (decode_as_t *) cur->data;
+ if ((g_strcmp0(proto_name, entry->name) == 0) &&
+ (g_strcmp0(item->tableName_, entry->table_name) == 0)) {
+ if (edt_present) {
+ if (entry->num_items > 1)
+ {
+ //only create a combobox if there is a choice of values, otherwise it looks funny
+ editor = new QComboBox(parentWidget);
+
+ //Don't limit user to just what's in combo box
+ editor->setEditable(true);
+
+ editor->setSizeAdjustPolicy(QComboBox::AdjustToContents);
+
+ //add the current value of the column
+ const QString& current_value = index.model()->data(index, Qt::EditRole).toString();
+ if (!current_value.isEmpty())
+ editor->addItem(current_value);
+
+ //get the value(s) from the packet
+ for (uint ni = 0; ni < entry->num_items; ni++) {
+ if (entry->values[ni].num_values == 1) { // Skip over multi-value ("both") entries
+ QString entryStr = DecodeAsModel::entryString(entry->table_name,
+ entry->values[ni].build_values[0](&cap_file_->edt->pi));
+ //don't duplicate entries
+ if (editor->findText(entryStr) < 0)
+ editor->addItem(entryStr);
+ }
+ }
+ editor->setCurrentIndex(entry->default_index_value);
+
+ //Make sure the combo box is at least as wide as the column
+ QTreeView* parentTree = (QTreeView*)parent();
+ int protoColWidth = parentTree->columnWidth(index.column());
+ if (protoColWidth > editor->size().width())
+ editor->setFixedWidth(protoColWidth);
+
+ }
+ }
+ break;
+ }
+ }
+
+ if (edt_present) {
+ cap_file_->edt->pi.curr_layer_num = curr_layer_num_saved;
+ }
+
+ //if there isn't a need for a combobox, just let user have a text box for direct edit
+ if (editor == NULL)
+ return QStyledItemDelegate::createEditor(parentWidget, option, index);
+
+ return editor;
+ }
+
+ case DecodeAsModel::colProtocol:
+ {
+ QComboBox *editor = new QComboBox(parentWidget);
+ QMap<QString, dissector_info_t*> protocols;
+
+ editor->setSizeAdjustPolicy(QComboBox::AdjustToContents);
+
+ for (GList *cur = decode_as_list; cur; cur = cur->next) {
+ decode_as_t *entry = (decode_as_t *) cur->data;
+ if (g_strcmp0(item->tableName_, entry->table_name) == 0) {
+ entry->populate_list(entry->table_name, decodeAddProtocol, &protocols);
+ break;
+ }
+ }
+
+ editor->addItem(DECODE_AS_NONE);
+ editor->insertSeparator(editor->count());
+
+ //QMap already sorts the keys (protocols) alphabetically
+ QMap<QString, dissector_info_t*>::iterator protocol;
+ for(protocol = protocols.begin(); protocol != protocols.end(); protocol++)
+ {
+ editor->addItem(protocol.key(), VariantPointer<dissector_info_t>::asQVariant(protocol.value()));
+ }
+
+ //Make sure the combo box is at least as wide as the column
+ QTreeView* parentTree = (QTreeView*)parent();
+ int protoColWidth = parentTree->columnWidth(index.column());
+ if (protoColWidth > editor->size().width())
+ editor->setFixedWidth(protoColWidth);
+
+ return editor;
+ }
+ }
+
+ return NULL;
+}
+
+void DecodeAsDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
+{
+ DecodeAsItem* item = indexToField(index);
+
+ switch(index.column())
+ {
+ case DecodeAsModel::colTable:
+ case DecodeAsModel::colProtocol:
+ {
+ QComboBox *combobox = static_cast<QComboBox *>(editor);
+ const QString &data = index.model()->data(index, Qt::EditRole).toString();
+#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
+ combobox->setCurrentText(data);
+#else
+ int new_index = combobox->findText(data);
+ if (new_index >= 0) {
+ combobox->setCurrentIndex(new_index);
+ }
+#endif
+ }
+ break;
+ case DecodeAsModel::colSelector:
+ if (isSelectorCombo(item)) {
+ QComboBox *combobox = static_cast<QComboBox *>(editor);
+ const QString &data = index.model()->data(index, Qt::EditRole).toString();
+#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
+ combobox->setCurrentText(data);
+#else
+ int new_index = combobox->findText(data);
+ if (new_index >= 0) {
+ combobox->setCurrentIndex(new_index);
+ }
+#endif
+ }
+ else {
+ QStyledItemDelegate::setEditorData(editor, index);
+ }
+ break;
+ default:
+ QStyledItemDelegate::setEditorData(editor, index);
+ break;
+ }
+}
+
+void DecodeAsDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
+ const QModelIndex &index) const
+{
+ DecodeAsItem* item = indexToField(index);
+
+ switch(index.column())
+ {
+ case DecodeAsModel::colTable:
+ {
+ QComboBox *combobox = static_cast<QComboBox *>(editor);
+ const QString &data = combobox->currentText();
+ model->setData(index, data, Qt::EditRole);
+ break;
+ }
+ case DecodeAsModel::colSelector:
+ if (isSelectorCombo(item)) {
+ QComboBox *combobox = static_cast<QComboBox *>(editor);
+ const QString &data = combobox->currentText();
+ model->setData(index, data, Qt::EditRole);
+ } else {
+ QStyledItemDelegate::setModelData(editor, model, index);
+ }
+ break;
+ case DecodeAsModel::colProtocol:
+ {
+ QComboBox *combobox = static_cast<QComboBox *>(editor);
+ const QString &data = combobox->currentText();
+ model->setData(index, data, Qt::EditRole);
+
+ //set the dissector handle
+ QVariant var = combobox->itemData(combobox->currentIndex());
+ dissector_info_t* dissector_info = VariantPointer<dissector_info_t>::asPtr(var);
+ if (dissector_info != NULL) {
+ ((DecodeAsModel*)model)->setDissectorHandle(index, dissector_info->dissector_handle);
+ } else {
+ ((DecodeAsModel*)model)->setDissectorHandle(index, NULL);
+ }
+ break;
+ }
+ default:
+ QStyledItemDelegate::setModelData(editor, model, index);
+ break;
+ }
+}
+
+#if 0
+// Qt docs suggest overriding updateEditorGeometry, but the defaults seem sane.
+void UatDelegate::updateEditorGeometry(QWidget *editor,
+ const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+ QStyledItemDelegate::updateEditorGeometry(editor, option, index);
+}
+#endif
+
+/* * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */