/* decode_as_delegate.cpp * Delegates for editing various field types in a Decode As record. * * Wireshark - Network traffic analyzer * By Gerald Combs * 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 #include #include #include #include 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(v.value()); } 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& all_protocols, QList& 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* proto_list = (QMap*)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 da_set; QList 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 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 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::iterator protocol; for(protocol = protocols.begin(); protocol != protocols.end(); protocol++) { editor->addItem(protocol.key(), VariantPointer::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(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(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(editor); const QString &data = combobox->currentText(); model->setData(index, data, Qt::EditRole); break; } case DecodeAsModel::colSelector: if (isSelectorCombo(item)) { QComboBox *combobox = static_cast(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(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::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: */