diff options
author | Gerald Combs <gerald@wireshark.org> | 2017-12-07 08:15:30 -0800 |
---|---|---|
committer | Anders Broman <a.broman58@gmail.com> | 2017-12-15 20:58:14 +0000 |
commit | bdb6baa7405d259fa2cd2f6f7d2fb21e13315885 (patch) | |
tree | 5c70190af46fef3fadb8a876e7b3e7f66dfe77b6 | |
parent | 0909580a7e98c134785a96829b58076534b477b6 (diff) |
Qt: Switch ProtoTree to a treeview+model.
Add a ProtoTreeModel and use it in ProtoTree. This should make the UI
more responsive when we have lots of items in the tree.
Change-Id: Id26e6bcff84663867a8da17fd9ae86ff639b633f
Reviewed-on: https://code.wireshark.org/review/24774
Reviewed-by: Gerald Combs <gerald@wireshark.org>
Petri-Dish: Gerald Combs <gerald@wireshark.org>
Tested-by: Petri Dish Buildbot
Reviewed-by: Anders Broman <a.broman58@gmail.com>
-rw-r--r-- | ui/qt/CMakeLists.txt | 14 | ||||
-rw-r--r-- | ui/qt/Makefile.am | 18 | ||||
-rw-r--r-- | ui/qt/byte_view_tab.cpp | 4 | ||||
-rw-r--r-- | ui/qt/main_window.cpp | 14 | ||||
-rw-r--r-- | ui/qt/main_window.h | 15 | ||||
-rw-r--r-- | ui/qt/main_window_slots.cpp | 41 | ||||
-rw-r--r-- | ui/qt/models/proto_tree_model.cpp | 222 | ||||
-rw-r--r-- | ui/qt/models/proto_tree_model.h | 58 | ||||
-rw-r--r-- | ui/qt/packet_dialog.cpp | 16 | ||||
-rw-r--r-- | ui/qt/packet_list.cpp | 18 | ||||
-rw-r--r-- | ui/qt/proto_tree.cpp | 615 | ||||
-rw-r--r-- | ui/qt/proto_tree.h | 45 | ||||
-rw-r--r-- | ui/qt/protocol_preferences_menu.cpp | 26 | ||||
-rw-r--r-- | ui/qt/protocol_preferences_menu.h | 16 | ||||
-rw-r--r-- | ui/qt/utils/field_information.cpp | 69 | ||||
-rw-r--r-- | ui/qt/utils/field_information.h | 8 | ||||
-rw-r--r-- | ui/qt/utils/proto_node.cpp | 171 | ||||
-rw-r--r-- | ui/qt/utils/proto_node.h | 69 |
18 files changed, 864 insertions, 575 deletions
diff --git a/ui/qt/CMakeLists.txt b/ui/qt/CMakeLists.txt index 28452389bc..568611ae99 100644 --- a/ui/qt/CMakeLists.txt +++ b/ui/qt/CMakeLists.txt @@ -50,14 +50,15 @@ set(WIRESHARK_WIDGET_HEADERS set(WIRESHARK_UTILS_HEADERS utils/color_utils.h + utils/data_printer.h utils/field_information.h utils/frame_information.h - utils/data_printer.h - utils/stock_icon.h + utils/proto_node.h utils/qt_ui_utils.h + utils/stock_icon.h utils/tango_colors.h - utils/wireshark_mime_data.h utils/variant_pointer.h + utils/wireshark_mime_data.h ) set(WIRESHARK_MODEL_HEADERS @@ -77,6 +78,7 @@ set(WIRESHARK_MODEL_HEADERS models/packet_list_record.h models/path_chooser_delegate.h models/percent_bar_delegate.h + models/proto_tree_model.h models/related_packet_delegate.h models/timeline_delegate.h models/sparkline_delegate.h @@ -262,12 +264,13 @@ set(WIRESHARK_WIDGET_SRCS set(WIRESHARK_UTILS_SRCS utils/color_utils.cpp + utils/data_printer.cpp utils/field_information.cpp utils/frame_information.cpp - utils/data_printer.cpp + utils/proto_node.cpp + utils/qt_ui_utils.cpp utils/stock_icon.cpp utils/wireshark_mime_data.cpp - utils/qt_ui_utils.cpp ) set(WIRESHARK_MODEL_SRCS @@ -287,6 +290,7 @@ set(WIRESHARK_MODEL_SRCS models/packet_list_record.cpp models/path_chooser_delegate.cpp models/percent_bar_delegate.cpp + models/proto_tree_model.cpp models/related_packet_delegate.cpp models/sparkline_delegate.cpp models/sparkline_delegate.cpp diff --git a/ui/qt/Makefile.am b/ui/qt/Makefile.am index b9d21eb273..4812892bb7 100644 --- a/ui/qt/Makefile.am +++ b/ui/qt/Makefile.am @@ -180,13 +180,14 @@ MOC_WIDGET_HDRS = \ # Files that are utility classes with multi-purpose, but no widgets MOC_UTILS_HDRS = \ utils/color_utils.h \ + utils/data_printer.h \ utils/field_information.h \ utils/frame_information.h \ - utils/data_printer.h \ - utils/stock_icon.h \ + utils/proto_node.h \ utils/qt_ui_utils.h \ - utils/wireshark_mime_data.h \ - utils/variant_pointer.h + utils/stock_icon.h \ + utils/variant_pointer.h \ + utils/wireshark_mime_data.h # Files for delegates and models MOC_MODELS_HDRS = \ @@ -206,6 +207,7 @@ MOC_MODELS_HDRS = \ models/packet_list_record.h \ models/path_chooser_delegate.h \ models/percent_bar_delegate.h \ + models/proto_tree_model.h \ models/related_packet_delegate.h \ models/timeline_delegate.h \ models/sparkline_delegate.h \ @@ -505,12 +507,13 @@ WIRESHARK_QT_WIDGET_SRC = \ WIRESHARK_QT_UTILS_SRC = \ utils/color_utils.cpp \ + utils/data_printer.cpp \ utils/field_information.cpp \ utils/frame_information.cpp \ - utils/data_printer.cpp \ + utils/proto_node.cpp \ + utils/qt_ui_utils.cpp \ utils/stock_icon.cpp \ - utils/wireshark_mime_data.cpp \ - utils/qt_ui_utils.cpp + utils/wireshark_mime_data.cpp WIRESHARK_QT_MODELS_SRCS = \ models/astringlist_list_model.cpp \ @@ -529,6 +532,7 @@ WIRESHARK_QT_MODELS_SRCS = \ models/packet_list_record.cpp \ models/path_chooser_delegate.cpp \ models/percent_bar_delegate.cpp \ + models/proto_tree_model.cpp \ models/related_packet_delegate.cpp \ models/sparkline_delegate.cpp \ models/timeline_delegate.cpp \ diff --git a/ui/qt/byte_view_tab.cpp b/ui/qt/byte_view_tab.cpp index 7bf47921c1..a022f64a1f 100644 --- a/ui/qt/byte_view_tab.cpp +++ b/ui/qt/byte_view_tab.cpp @@ -255,9 +255,7 @@ void ByteViewTab::selectedFieldChanged(FieldInformation *selected) ByteViewText * byte_view_text = 0; if (selected) { - field_info *fi; - - fi = selected->fieldInfo(); + const field_info *fi = selected->fieldInfo(); int idx = 0; if ( fi ) diff --git a/ui/qt/main_window.cpp b/ui/qt/main_window.cpp index bfd40ed7fa..568a3ebf4c 100644 --- a/ui/qt/main_window.cpp +++ b/ui/qt/main_window.cpp @@ -4,19 +4,7 @@ * 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. + * SPDX-License-Identifier: GPL-2.0+ */ #include "main_window.h" diff --git a/ui/qt/main_window.h b/ui/qt/main_window.h index ad7e6c8a23..c1df0cdafe 100644 --- a/ui/qt/main_window.h +++ b/ui/qt/main_window.h @@ -4,19 +4,7 @@ * 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. + * SPDX-License-Identifier: GPL-2.0+ */ #ifndef MAINWINDOW_H @@ -248,7 +236,6 @@ private: void setForCaptureInProgress(bool capture_in_progress = false, GArray *ifaces = NULL); QMenu* findOrAddMenu(QMenu *parent_menu, QString& menu_text); - void recursiveCopyProtoTreeItems(QTreeWidgetItem *item, QString &clip, int ident_level); void captureFileReadStarted(const QString &action); void addMenuActions(QList<QAction *> &actions, int menu_group); diff --git a/ui/qt/main_window_slots.cpp b/ui/qt/main_window_slots.cpp index 875561f6b8..b83ad46bba 100644 --- a/ui/qt/main_window_slots.cpp +++ b/ui/qt/main_window_slots.cpp @@ -4,19 +4,7 @@ * 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. + * SPDX-License-Identifier: GPL-2.0+ */ #include <config.h> @@ -2024,18 +2012,6 @@ void MainWindow::on_actionFilePrint_triggered() // Edit Menu -void MainWindow::recursiveCopyProtoTreeItems(QTreeWidgetItem *item, QString &clip, int ident_level) { - if (!item->isExpanded()) return; - - for (int i_item = 0; i_item < item->childCount(); i_item += 1) { - clip.append(QString(" ").repeated(ident_level)); - clip.append(item->child(i_item)->text(0)); - clip.append("\n"); - - recursiveCopyProtoTreeItems(item->child(i_item), clip, ident_level + 1); - } -} - // XXX This should probably be somewhere else. void MainWindow::actionEditCopyTriggered(MainWindow::CopySelected selection_type) { @@ -2066,20 +2042,11 @@ void MainWindow::actionEditCopyTriggered(MainWindow::CopySelected selection_type } break; case CopyAllVisibleItems: - for (int i_item = 0; i_item < proto_tree_->topLevelItemCount(); i_item += 1) { - clip.append(proto_tree_->topLevelItem(i_item)->text(0)); - clip.append("\n"); - - recursiveCopyProtoTreeItems(proto_tree_->topLevelItem(i_item), clip, 1); - } - + clip = proto_tree_->toString(); break; case CopyAllVisibleSelectedTreeItems: - if (proto_tree_->selectedItems().count() > 0) { - clip.append(proto_tree_->currentItem()->text(0)); - clip.append("\n"); - - recursiveCopyProtoTreeItems(proto_tree_->currentItem(), clip, 1); + if (proto_tree_->selectionModel()->hasSelection()) { + clip = proto_tree_->toString(proto_tree_->selectionModel()->selectedIndexes().first()); } break; } diff --git a/ui/qt/models/proto_tree_model.cpp b/ui/qt/models/proto_tree_model.cpp new file mode 100644 index 0000000000..b67e772ec0 --- /dev/null +++ b/ui/qt/models/proto_tree_model.cpp @@ -0,0 +1,222 @@ +/* proto_tree_model.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <ui/qt/models/proto_tree_model.h> + +#include <epan/prefs.h> + +#include <ui/qt/utils/color_utils.h> + +#include <QApplication> +#include <QPalette> + +// To do: +// - Add ProtoTreeDelegate +// - Add ProtoTreeModel to CaptureFile + +ProtoTreeModel::ProtoTreeModel(QObject * parent) : + QAbstractItemModel(parent), + root_node_(0) +{} + +QModelIndex ProtoTreeModel::index(int row, int, const QModelIndex &parent) const +{ + ProtoNode parent_node(root_node_); + + if (parent.isValid()) { + // index is not a top level item. + parent_node = protoNodeFromIndex(parent); + } + + if (! parent_node.isValid() ) + return QModelIndex(); + + int cur_row = 0; + ProtoNode::ChildIterator kids = parent_node.children(); + while ( kids.element().isValid() ) + { + if ( cur_row == row ) + break; + cur_row++; + kids.next(); + } + if ( ! kids.element().isValid() ) { + return QModelIndex(); + } + + return createIndex(row, 0, static_cast<void *>(kids.element().protoNode())); +} + +QModelIndex ProtoTreeModel::parent(const QModelIndex &index) const +{ + ProtoNode parent_node = protoNodeFromIndex(index).parentNode(); + return indexFromProtoNode(parent_node); +} + +int ProtoTreeModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) { + return protoNodeFromIndex(parent).childrenCount(); + } + return ProtoNode(root_node_).childrenCount(); +} + +QVariant ProtoTreeModel::data(const QModelIndex &index, int role) const +{ + ProtoNode index_node = protoNodeFromIndex(index); + FieldInformation finfo(index_node.protoNode()); + if (!finfo.isValid()) { + return QVariant(); + } + + switch (role) { + case Qt::DisplayRole: + return index_node.labelText(); + case Qt::BackgroundRole: + { + switch(finfo.flag(PI_SEVERITY_MASK)) { + case(0): + break; + case(PI_COMMENT): + return ColorUtils::expert_color_comment; + case(PI_CHAT): + return ColorUtils::expert_color_chat; + case(PI_NOTE): + return ColorUtils::expert_color_note; + case(PI_WARN): + return ColorUtils::expert_color_warn; + case(PI_ERROR): + return ColorUtils::expert_color_error; + default: + g_warning("%s:%d Unhandled severity flag: %u", G_STRFUNC, __LINE__, finfo.flag(PI_SEVERITY_MASK)); + } + if(finfo.headerInfo().type == FT_PROTOCOL) { + return QApplication::palette().window(); + } + return QApplication::palette().base(); + } + case Qt::ForegroundRole: + { + if(finfo.flag(PI_SEVERITY_MASK)) { + return ColorUtils::expert_color_foreground; + } + if(finfo.headerInfo().type == FT_PROTOCOL) { + return QApplication::palette().windowText(); + } + return QApplication::palette().text(); + } + default: + break; + } + + return QVariant(); +} + +void ProtoTreeModel::setRootNode(proto_node *root_node) +{ + beginResetModel(); + root_node_ = root_node; + endResetModel(); + if (!root_node) return; + + int row_count = ProtoNode(root_node_).childrenCount(); + if (row_count < 1) return; + beginInsertRows(QModelIndex(), 0, row_count - 1); + endInsertRows(); +} + +ProtoNode ProtoTreeModel::protoNodeFromIndex(const QModelIndex &index) const +{ + return ProtoNode(static_cast<proto_node*>(index.internalPointer())); +} + +QModelIndex ProtoTreeModel::indexFromProtoNode(ProtoNode &index_node) const +{ + int row = index_node.row(); + + if (!index_node.isValid() || row < 0) { + return QModelIndex(); + } + + return createIndex(row, 0, static_cast<void *>(index_node.protoNode())); +} + +struct find_hfid_ { + int hfid; + ProtoNode node; +}; + +void ProtoTreeModel::foreachFindHfid(proto_node *node, gpointer find_hfid_ptr) +{ + struct find_hfid_ *find_hfid = (struct find_hfid_ *) find_hfid_ptr; + if (PNODE_FINFO(node)->hfinfo->id == find_hfid->hfid) { + find_hfid->node = ProtoNode(node); + return; + } + proto_tree_children_foreach(node, foreachFindHfid, find_hfid); +} + +QModelIndex ProtoTreeModel::findFirstHfid(int hf_id) +{ + if (!root_node_ || hf_id < 0) return QModelIndex(); + + struct find_hfid_ find_hfid; + find_hfid.hfid = hf_id; + + proto_tree_children_foreach(root_node_, foreachFindHfid, &find_hfid); + + if (find_hfid.node.isValid()) { + return indexFromProtoNode(find_hfid.node); + } + return QModelIndex(); +} + +struct find_field_info_ { + field_info *fi; + ProtoNode node; +}; + +void ProtoTreeModel::foreachFindField(proto_node *node, gpointer find_finfo_ptr) +{ + struct find_field_info_ *find_finfo = (struct find_field_info_ *) find_finfo_ptr; + if (PNODE_FINFO(node) == find_finfo->fi) { + find_finfo->node = ProtoNode(node); + return; + } + proto_tree_children_foreach(node, foreachFindField, find_finfo); +} + +QModelIndex ProtoTreeModel::findFieldInformation(FieldInformation *finfo) +{ + if (!root_node_ || !finfo) return QModelIndex(); + field_info * fi = finfo->fieldInfo(); + if (!fi) return QModelIndex(); + + struct find_field_info_ find_finfo; + find_finfo.fi = fi; + + proto_tree_children_foreach(root_node_, foreachFindField, &find_finfo); + if (find_finfo.node.isValid()) { + return indexFromProtoNode(find_finfo.node); + } + return QModelIndex(); +} + +/* + * 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: + */ diff --git a/ui/qt/models/proto_tree_model.h b/ui/qt/models/proto_tree_model.h new file mode 100644 index 0000000000..e0e815cf4b --- /dev/null +++ b/ui/qt/models/proto_tree_model.h @@ -0,0 +1,58 @@ +/* proto_tree_model.h + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef PROTO_TREE_MODEL_H +#define PROTO_TREE_MODEL_H + +#include <ui/qt/utils/proto_node.h> + +#include <QAbstractItemModel> +#include <QModelIndex> + +class ProtoTreeModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + explicit ProtoTreeModel(QObject * parent = 0); + + QModelIndex index(int row, int, const QModelIndex &parent = QModelIndex()) const; + virtual QModelIndex parent(const QModelIndex &index) const; + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + virtual int columnCount(const QModelIndex &) const { return 1; } + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + + // root_node can be NULL. + void setRootNode(proto_node *root_node); + ProtoNode protoNodeFromIndex(const QModelIndex &index) const; + QModelIndex indexFromProtoNode(ProtoNode &index_node) const; + + QModelIndex findFirstHfid(int hf_id); + QModelIndex findFieldInformation(FieldInformation *finfo); + +private: + proto_node* root_node_; + static void foreachFindHfid(proto_node *node, gpointer find_hfid_ptr); + static void foreachFindField(proto_node *node, gpointer find_finfo_ptr); +}; + +#endif // PROTO_TREE_MODEL_H + +/* + * 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: + */ diff --git a/ui/qt/packet_dialog.cpp b/ui/qt/packet_dialog.cpp index bf3e481b58..c8a8dfb4ca 100644 --- a/ui/qt/packet_dialog.cpp +++ b/ui/qt/packet_dialog.cpp @@ -4,19 +4,7 @@ * 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. + * SPDX-License-Identifier: GPL-2.0+ */ #include "packet_dialog.h" @@ -68,7 +56,7 @@ PacketDialog::PacketDialog(QWidget &parent, CaptureFile &cf, frame_data *fdata) epan_dissect_fill_in_columns(&edt_, TRUE, TRUE); proto_tree_ = new ProtoTree(ui->packetSplitter); - proto_tree_->fillProtocolTree(edt_.tree); + proto_tree_->setRootNode(edt_.tree); byte_view_tab_ = new ByteViewTab(ui->packetSplitter); byte_view_tab_->setCaptureFile(cap_file_.capFile()); diff --git a/ui/qt/packet_list.cpp b/ui/qt/packet_list.cpp index a7966b7f33..d5578702f2 100644 --- a/ui/qt/packet_list.cpp +++ b/ui/qt/packet_list.cpp @@ -4,19 +4,7 @@ * 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. + * SPDX-License-Identifier: GPL-2.0+ */ #include <ui/qt/packet_list.h> @@ -513,7 +501,7 @@ void PacketList::selectionChanged (const QItemSelection & selected, const QItemS if (proto_tree_ && cap_file_->edt->tree) { packet_info *pi = &cap_file_->edt->pi; related_packet_delegate_.setCurrentFrame(pi->num); - proto_tree_->fillProtocolTree(cap_file_->edt->tree); + proto_tree_->setRootNode(cap_file_->edt->tree); conversation_t *conv = find_conversation_pinfo(pi, 0); if (conv) { related_packet_delegate_.setConversation(conv); @@ -1256,7 +1244,7 @@ void PacketList::goToPacket(int packet) { void PacketList::goToPacket(int packet, int hf_id) { goToPacket(packet); - proto_tree_->goToField(hf_id); + proto_tree_->goToHfid(hf_id); } void PacketList::goNextHistoryPacket() diff --git a/ui/qt/proto_tree.cpp b/ui/qt/proto_tree.cpp index 401d948cf8..2b91b1c37d 100644 --- a/ui/qt/proto_tree.cpp +++ b/ui/qt/proto_tree.cpp @@ -4,29 +4,17 @@ * 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. + * SPDX-License-Identifier: GPL-2.0+ */ #include <stdio.h> #include "proto_tree.h" +#include <ui/qt/models/proto_tree_model.h> #include <epan/ftypes/ftypes.h> #include <epan/prefs.h> -#include <ui/qt/utils/color_utils.h> #include <ui/qt/utils/variant_pointer.h> #include <ui/qt/utils/wireshark_mime_data.h> #include <ui/qt/widgets/drag_label.h> @@ -35,10 +23,10 @@ #include <QContextMenuEvent> #include <QDesktopServices> #include <QHeaderView> +#include <QItemSelectionModel> #include <QScrollBar> -#include <QTreeWidgetItemIterator> +#include <QStack> #include <QUrl> -#include <QItemSelectionModel> #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #include <QWindow> @@ -48,118 +36,9 @@ // To do: // - Fix "apply as filter" behavior. -/* Fill a single protocol tree item with its string value and set its color. */ -static void -proto_tree_draw_node(proto_node *node, gpointer data) -{ - field_info *fi = PNODE_FINFO(node); - QString label; - gboolean is_branch; - - /* dissection with an invisible proto tree? */ - g_assert(fi); - - if (PROTO_ITEM_IS_HIDDEN(node) && !prefs.display_hidden_proto_items) - return; - - // Fill in our label - /* was a free format label produced? */ - if (fi->rep) { - label = fi->rep->representation; - } - else { /* no, make a generic label */ - gchar label_str[ITEM_LABEL_LENGTH]; - proto_item_fill_label(fi, label_str); - label = label_str; - } - - if (node->first_child != NULL) { - is_branch = TRUE; - g_assert(fi->tree_type >= 0 && fi->tree_type < num_tree_types); - } - else { - is_branch = FALSE; - } - - if (PROTO_ITEM_IS_GENERATED(node)) { - if (PROTO_ITEM_IS_HIDDEN(node)) { - label = QString("<[%1]>").arg(label); - } else { - label = QString("[%1]").arg(label); - } - } else if (PROTO_ITEM_IS_HIDDEN(node)) { - label = QString("<%1>").arg(label); - } - - QTreeWidgetItem *parentItem = (QTreeWidgetItem *)data; - QTreeWidgetItem *item; - ProtoTree *proto_tree = qobject_cast<ProtoTree *>(parentItem->treeWidget()); - - item = new QTreeWidgetItem(parentItem, 0); - - // Set our colors. - QPalette pal = QApplication::palette(); - if (fi->hfinfo) { - if(fi->hfinfo->type == FT_PROTOCOL) { - item->setData(0, Qt::BackgroundRole, pal.window()); - item->setData(0, Qt::ForegroundRole, pal.windowText()); - } - - if((fi->hfinfo->type == FT_FRAMENUM) || - (FI_GET_FLAG(fi, FI_URL) && IS_FT_STRING(fi->hfinfo->type))) { - QFont font = item->font(0); - - item->setData(0, Qt::ForegroundRole, pal.link()); - font.setUnderline(true); - item->setData(0, Qt::FontRole, font); - - if (fi->hfinfo->type == FT_FRAMENUM) { - ft_framenum_type_t framenum_type = (ft_framenum_type_t)GPOINTER_TO_INT(fi->hfinfo->strings); - proto_tree->emitRelatedFrame(fi->value.value.uinteger, framenum_type); - } - } - } - - // XXX - Add routines to get our severity colors. - if(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) { - switch(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) { - case(PI_COMMENT): - item->setData(0, Qt::BackgroundRole, ColorUtils::expert_color_comment); - break; - case(PI_CHAT): - item->setData(0, Qt::BackgroundRole, ColorUtils::expert_color_chat); - break; - case(PI_NOTE): - item->setData(0, Qt::BackgroundRole, ColorUtils::expert_color_note); - break; - case(PI_WARN): - item->setData(0, Qt::BackgroundRole, ColorUtils::expert_color_warn); - break; - case(PI_ERROR): - item->setData(0, Qt::BackgroundRole, ColorUtils::expert_color_error); - break; - default: - g_assert_not_reached(); - } - item->setData(0, Qt::ForegroundRole, ColorUtils::expert_color_foreground); - } - - item->setText(0, label); - item->setData(0, Qt::UserRole, VariantPointer<field_info>::asQVariant(fi)); - - if (is_branch) { - if (tree_expanded(fi->tree_type)) { - item->setExpanded(true); - } else { - item->setExpanded(false); - } - - proto_tree_children_foreach(node, proto_tree_draw_node, item); - } -} - ProtoTree::ProtoTree(QWidget *parent) : - QTreeWidget(parent), + QTreeView(parent), + proto_tree_model_(new ProtoTreeModel(this)), decode_as_(NULL), column_resize_timer_(0), cap_file_(NULL) @@ -171,6 +50,8 @@ ProtoTree::ProtoTree(QWidget *parent) : // similar to PacketListModel::data. setHeaderHidden(true); + setModel(proto_tree_model_); + if (window()->findChild<QAction *>("actionViewExpandSubtrees")) { // Assume we're a child of the main window. // XXX We might want to reimplement setParent() and fill in the context @@ -266,7 +147,6 @@ ProtoTree::ProtoTree(QWidget *parent) : ctx_menu_.addAction(action); action = window()->findChild<QAction *>("actionContextFilterFieldReference"); ctx_menu_.addAction(action); -// " <menuitem name='ProtocolHelp' action='/ProtocolHelp'/>\n" ctx_menu_.addMenu(&proto_prefs_menu_); ctx_menu_.addSeparator(); decode_as_ = window()->findChild<QAction *>("actionAnalyzeDecodeAs"); @@ -278,12 +158,10 @@ ProtoTree::ProtoTree(QWidget *parent) : ctx_menu_.clear(); } - connect(this, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), - this, SLOT(updateSelectionStatus(QTreeWidgetItem*))); connect(this, SIGNAL(expanded(QModelIndex)), this, SLOT(expand(QModelIndex))); connect(this, SIGNAL(collapsed(QModelIndex)), this, SLOT(collapse(QModelIndex))); - connect(this, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), - this, SLOT(itemDoubleClick(QTreeWidgetItem*, int))); + connect(this, SIGNAL(doubleClicked(QModelIndex)), + this, SLOT(itemDoubleClicked(QModelIndex))); connect(&proto_prefs_menu_, SIGNAL(showProtocolPreferences(QString)), this, SIGNAL(showProtocolPreferences(QString))); @@ -298,17 +176,16 @@ ProtoTree::ProtoTree(QWidget *parent) : viewport()->installEventFilter(this); } +void ProtoTree::clear() { + proto_tree_model_->setRootNode(NULL); + updateContentWidth(); +} + void ProtoTree::closeContextMenu() { ctx_menu_.close(); } -void ProtoTree::clear() { - updateSelectionStatus(NULL); - QTreeWidget::clear(); - updateContentWidth(); -} - void ProtoTree::contextMenuEvent(QContextMenuEvent *event) { if (ctx_menu_.isEmpty()) return; // We're in a PacketDialog @@ -319,31 +196,22 @@ void ProtoTree::contextMenuEvent(QContextMenuEvent *event) conv_menu_.addAction(action); } - FieldInformation * finfo = 0; - field_info *fi = NULL; - const char *module_name = NULL; - if (selectedItems().count() > 0) { - fi = VariantPointer<field_info>::asPtr(selectedItems()[0]->data(0, Qt::UserRole)); - if (fi && fi->hfinfo) { - if (fi->hfinfo->parent == -1) { - module_name = fi->hfinfo->abbrev; - } else { - module_name = proto_registrar_get_abbrev(fi->hfinfo->parent); - } - } - finfo = new FieldInformation(fi, this); + QModelIndex index; + if (selectionModel()->hasSelection()) { + index = selectedIndexes().first(); } - proto_prefs_menu_.setModule(module_name); + FieldInformation finfo(proto_tree_model_->protoNodeFromIndex(index).protoNode()); + proto_prefs_menu_.setModule(finfo.moduleName()); foreach (QAction *action, copy_actions_) { action->setProperty("idataprintable_", - VariantPointer<IDataPrintable>::asQVariant((IDataPrintable *)finfo)); + VariantPointer<IDataPrintable>::asQVariant((IDataPrintable *)&finfo)); } decode_as_->setData(qVariantFromValue(true)); // Set menu sensitivity and action data. - emit fieldSelected(finfo); + emit fieldSelected(&finfo); ctx_menu_.exec(event->globalPos()); decode_as_->setData(QVariant()); } @@ -355,7 +223,7 @@ void ProtoTree::timerEvent(QTimerEvent *event) column_resize_timer_ = 0; resizeColumnToContents(0); } else { - QTreeWidget::timerEvent(event); + QTreeView::timerEvent(event); } } @@ -393,11 +261,9 @@ void ProtoTree::setMonospaceFont(const QFont &mono_font) update(); } -void ProtoTree::fillProtocolTree(proto_tree *protocol_tree) { - clear(); +void ProtoTree::setRootNode(proto_node *root_node) { setFont(mono_font_); - - proto_tree_children_foreach(protocol_tree, proto_tree_draw_node, invisibleRootItem()); + proto_tree_model_->setRootNode(root_node); updateContentWidth(); } @@ -407,76 +273,64 @@ void ProtoTree::emitRelatedFrame(int related_frame, ft_framenum_type_t framenum_ } // XXX We select the first match, which might not be the desired item. -void ProtoTree::goToField(int hf_id) +void ProtoTree::goToHfid(int hfid) { - if (hf_id < 0) return; - - QTreeWidgetItemIterator iter(this); - while (*iter) { - field_info *fi = VariantPointer<field_info>::asPtr((*iter)->data(0, Qt::UserRole)); - - if (fi && fi->hfinfo) { - if (fi->hfinfo->id == hf_id) { - setCurrentItem(*iter); - break; - } - } - ++iter; + QModelIndex index = proto_tree_model_->findFirstHfid(hfid); + if (index.isValid()) { + scrollTo(index); + selectionModel()->select(index, QItemSelectionModel::ClearAndSelect); } } -void ProtoTree::updateSelectionStatus(QTreeWidgetItem* item) +void ProtoTree::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) { - if (item) { - field_info *fi; - QString item_info; + QTreeView::selectionChanged(selected, deselected); + if (selected.isEmpty()) return; - fi = VariantPointer<field_info>::asPtr(item->data(0, Qt::UserRole)); - if (!fi || !fi->hfinfo) return; + QModelIndex index = selected.indexes().first(); - FieldInformation * finfo = new FieldInformation(fi, this); - - // Find and highlight the protocol bytes - QTreeWidgetItem * parent = item->parent(); - while (parent && parent->parent()) { - parent = parent->parent(); - } - if (parent) { - finfo->setParentField(VariantPointer<field_info>::asPtr(parent->data(0, Qt::UserRole))); - } + FieldInformation finfo(proto_tree_model_->protoNodeFromIndex(index).protoNode()); + if (!finfo.isValid()) return; - if ( finfo->isValid() ) - { - saveSelectedField(item); - emit fieldSelected(finfo); - } // else the GTK+ version pushes an empty string as described below. - /* - * Don't show anything if the field name is zero-length; - * the pseudo-field for text-only items is such - * a field, and we don't want "Text (text)" showing up - * on the status line if you've selected such a field. - * - * XXX - there are zero-length fields for which we *do* - * want to show the field name. - * - * XXX - perhaps the name and abbrev field should be null - * pointers rather than null strings for that pseudo-field, - * but we'd have to add checks for null pointers in some - * places if we did that. - * - * Or perhaps text-only items should have -1 as the field - * index, with no pseudo-field being used, but that might - * also require special checks for -1 to be added. - */ + // Find and highlight the protocol bytes + QModelIndex parent = index; + while (parent.isValid() && parent.parent().isValid()) { + parent = parent.parent(); + } + if (parent.isValid()) { + FieldInformation parent_finfo(proto_tree_model_->protoNodeFromIndex(parent).protoNode()); + finfo.setParentField(parent_finfo.fieldInfo()); + } + if ( finfo.isValid() ) + { + saveSelectedField(index); + emit fieldSelected(&finfo); } + // else the GTK+ version pushes an empty string as described below. + /* + * Don't show anything if the field name is zero-length; + * the pseudo-field for text-only items is such + * a field, and we don't want "Text (text)" showing up + * on the status line if you've selected such a field. + * + * XXX - there are zero-length fields for which we *do* + * want to show the field name. + * + * XXX - perhaps the name and abbrev field should be null + * pointers rather than null strings for that pseudo-field, + * but we'd have to add checks for null pointers in some + * places if we did that. + * + * Or perhaps text-only items should have -1 as the field + * index, with no pseudo-field being used, but that might + * also require special checks for -1 to be added. + */ } -void ProtoTree::expand(const QModelIndex & index) { - field_info *fi; - - fi = VariantPointer<field_info>::asPtr(index.data(Qt::UserRole)); - if (!fi) return; +void ProtoTree::expand(const QModelIndex &index) { + FieldInformation finfo(proto_tree_model_->protoNodeFromIndex(index).protoNode()); + if (!finfo.isValid()) return; if(prefs.gui_auto_scroll_on_expand) { ScrollHint scroll_hint = PositionAtTop; @@ -492,211 +346,163 @@ void ProtoTree::expand(const QModelIndex & index) { * Nodes with "finfo->tree_type" of -1 have no ett_ value, and * are thus presumably leaf nodes and cannot be expanded. */ - if (fi->tree_type != -1) { - g_assert(fi->tree_type >= 0 && - fi->tree_type < num_tree_types); - tree_expanded_set(fi->tree_type, TRUE); + if (finfo.treeType() != -1) { + tree_expanded_set(finfo.treeType(), TRUE); } - updateContentWidth(); + QTreeView::expand(index); } -void ProtoTree::collapse(const QModelIndex & index) { - field_info *fi; - - fi = VariantPointer<field_info>::asPtr(index.data(Qt::UserRole)); - if (!fi) return; +void ProtoTree::collapse(const QModelIndex &index) { + FieldInformation finfo(proto_tree_model_->protoNodeFromIndex(index).protoNode()); + if (!finfo.isValid()) return; /* * Nodes with "finfo->tree_type" of -1 have no ett_ value, and * are thus presumably leaf nodes and cannot be collapsed. */ - if (fi->tree_type != -1) { - g_assert(fi->tree_type >= 0 && - fi->tree_type < num_tree_types); - tree_expanded_set(fi->tree_type, FALSE); + if (finfo.treeType() != -1) { + tree_expanded_set(finfo.treeType(), FALSE); } - updateContentWidth(); + QTreeView::collapse(index); } void ProtoTree::expandSubtrees() { - QTreeWidgetItem *top_sel; - - if (selectedItems().length() < 1) { - return; - } - - top_sel = selectedItems()[0]; - - if (!top_sel) { - return; - } - - while (top_sel->parent()) { - top_sel = top_sel->parent(); - } - - QTreeWidgetItemIterator iter(top_sel); - while (*iter) { - if ((*iter) != top_sel && (*iter)->parent() == NULL) { - // We found the next top-level item - break; + if (!selectionModel()->hasSelection()) return; + + QStack<QModelIndex> index_stack; + index_stack.push(selectionModel()->selectedIndexes().first()); + + while (!index_stack.isEmpty()) { + QModelIndex index = index_stack.pop(); + expand(index); + int row_count = proto_tree_model_->rowCount(index); + for (int row = row_count - 1; row >= 0; row--) { + QModelIndex child = proto_tree_model_->index(row, 0, index); + if (proto_tree_model_->hasChildren(child)) { + index_stack.push(child); + } } - (*iter)->setExpanded(true); - ++iter; } + updateContentWidth(); } void ProtoTree::expandAll() { - int i; - for(i=0; i < num_tree_types; i++) { + for(int i = 0; i < num_tree_types; i++) { tree_expanded_set(i, TRUE); } - QTreeWidget::expandAll(); + QTreeView::expandAll(); updateContentWidth(); } void ProtoTree::collapseAll() { - int i; - for(i=0; i < num_tree_types; i++) { + for(int i = 0; i < num_tree_types; i++) { tree_expanded_set(i, FALSE); } - QTreeWidget::collapseAll(); + QTreeView::collapseAll(); updateContentWidth(); } -void ProtoTree::itemDoubleClick(QTreeWidgetItem *item, int) { - field_info *fi; - - fi = VariantPointer<field_info>::asPtr(item->data(0, Qt::UserRole)); - if (!fi || !fi->hfinfo) return; +void ProtoTree::itemDoubleClicked(const QModelIndex &index) { + FieldInformation finfo(proto_tree_model_->protoNodeFromIndex(index).protoNode()); + if (!finfo.isValid()) return; - if (fi->hfinfo->type == FT_FRAMENUM) { + if (finfo.headerInfo().type == FT_FRAMENUM) { if (QApplication::queryKeyboardModifiers() & Qt::ShiftModifier) { emit openPacketInNewWindow(true); } else { - emit goToPacket(fi->value.value.uinteger); + emit goToPacket(finfo.fieldInfo()->value.value.uinteger); } - } else if (FI_GET_FLAG(fi, FI_URL) && IS_FT_STRING(fi->hfinfo->type)) { - gchar *url; - url = fvalue_to_string_repr(NULL, &fi->value, FTREPR_DISPLAY, fi->hfinfo->display); - if(url){ -// browser_open_url(url); + } else { + QString url = finfo.url(); + if (!url.isEmpty()) { QDesktopServices::openUrl(QUrl(url)); - wmem_free(NULL, url); } } } -void ProtoTree::selectedFieldChanged(FieldInformation * finfo) +void ProtoTree::selectedFieldChanged(FieldInformation *finfo) { - if ( finfo ) - { - field_info * fi = finfo->fieldInfo(); - - QTreeWidgetItemIterator iter(this); - while (*iter) { - if (fi == VariantPointer<field_info>::asPtr((*iter)->data(0, Qt::UserRole))) { - setCurrentItem(*iter); - scrollToItem(*iter); - break; - } - ++iter; - } + QModelIndex index = proto_tree_model_->findFieldInformation(finfo); + if (index.isValid()) { + scrollTo(index); + selectionModel()->select(index, QItemSelectionModel::ClearAndSelect); } } -// Finds position item at a level, counting only similar fields. -static unsigned indexOfField(QTreeWidgetItem *item, header_field_info *hfi) -{ - QTreeWidgetItem *parent = item->parent(); - unsigned pos = 0; - if (!parent) { - // In case multiple top-level layers are present for the same protocol, - // try to find its position (this will likely be the first match, zero). - QTreeWidget *tree = item->treeWidget(); - for (int i = 0; i < tree->topLevelItemCount(); i++) { - QTreeWidgetItem *current = tree->topLevelItem(i); - if (current == item) { - return pos; - } - if (hfi == VariantPointer<field_info>::asPtr(current->data(0, Qt::UserRole))->hfinfo) { - pos++; - } - } - } else { - QTreeWidgetItemIterator iter(parent); - while (*iter) { - QTreeWidgetItem *current = *iter; - if (current == item) { - return pos; - } - if (hfi == VariantPointer<field_info>::asPtr(current->data(0, Qt::UserRole))->hfinfo) { - pos++; - } - ++iter; - } - } - // should not happen (child is not found at parent?!) - return 0; -} - -// Assume about 2^8 items in tree and 2^24 different registered fields. -// If there are more of each, then a collision may occur, but since the full -// path is matched this is unlikely to be a problem. -#define POS_SHIFT 24 -#define POS_MASK (((unsigned)-1) << POS_SHIFT) - // Remember the currently focussed field based on: // - current hf_id (obviously) // - parent items (to avoid selecting a text item in a different tree) -// - position within a tree if there are multiple items -static QList<int> serializeAsPath(QTreeWidgetItem *item) -{ - QList<int> path; - do { - field_info *fi = VariantPointer<field_info>::asPtr(item->data(0, Qt::UserRole)); - unsigned pos = indexOfField(item, fi->hfinfo); - path.prepend((pos << POS_SHIFT) | (fi->hfinfo->id & ~POS_MASK)); - } while ((item = item->parent())); - return path; -} -void ProtoTree::saveSelectedField(QTreeWidgetItem *item) +// - the row of each item +void ProtoTree::saveSelectedField(QModelIndex &index) { - selected_field_path_ = serializeAsPath(item); + selected_hfid_path_.clear(); + while (index.isValid()) { + FieldInformation finfo(proto_tree_model_->protoNodeFromIndex(index).protoNode()); + if (!finfo.isValid()) break; + selected_hfid_path_.prepend(QPair<int,int>(index.row(), finfo.headerInfo().id)); + index = index.parent(); + } } // Try to focus a tree item which was previously also visible void ProtoTree::restoreSelectedField() { - if (selected_field_path_.isEmpty()) { - return; - } - int last_hf_id = selected_field_path_.last() & ~POS_MASK; - QTreeWidgetItemIterator iter(this); - while (*iter) { - field_info *fi = VariantPointer<field_info>::asPtr((*iter)->data(0, Qt::UserRole)); - if (last_hf_id == fi->hfinfo->id && - serializeAsPath(*iter) == selected_field_path_) { - // focus the first item, but do not expand collapsed trees. - QTreeWidgetItem *item = *iter, *target = item; - do { - if (!item->isExpanded()) { - target = item; - } - } while ((item = item->parent())); - setCurrentItem(target); - scrollToItem(target); + if (selected_hfid_path_.isEmpty()) return; + + QModelIndex cur_index = QModelIndex(); + QPair<int,int> path_entry; + foreach (path_entry, selected_hfid_path_) { + int row = path_entry.first; + int hf_id = path_entry.second; + cur_index = proto_tree_model_->index(row, 0, cur_index); + FieldInformation finfo(proto_tree_model_->protoNodeFromIndex(cur_index).protoNode()); + if (!finfo.isValid() || finfo.headerInfo().id != hf_id) { + cur_index = QModelIndex(); break; } - ++iter; + } + + if (cur_index.isValid()) { + scrollTo(cur_index); + selectionModel()->select(cur_index, QItemSelectionModel::ClearAndSelect); } } +const QString ProtoTree::toString(const QModelIndex &index) const +{ + QModelIndex cur_idx = index.isValid() ? index : proto_tree_model_->index(0, 0); + QString tree_string; + int indent_level = 0; + + do { + tree_string.append(QString(" ").repeated(indent_level)); + tree_string.append(cur_idx.data().toString()); + tree_string.append("\n"); + // Next child + if (isExpanded(cur_idx)) { + cur_idx = proto_tree_model_->index(0, 0, cur_idx); + indent_level++; + continue; + } + // Next sibling + QModelIndex sibling = proto_tree_model_->index(cur_idx.row() + 1, 0, cur_idx.parent()); + if (sibling.isValid()) { + cur_idx = sibling; + continue; + } + // Next parent + cur_idx = proto_tree_model_->index(cur_idx.parent().row() + 1, 0, cur_idx.parent().parent()); + indent_level--; + } while (cur_idx.isValid() && cur_idx != index && indent_level >= 0); + + return tree_string; +} + void ProtoTree::setCaptureFile(capture_file *cf) { cap_file_ = cf; @@ -705,72 +511,77 @@ void ProtoTree::setCaptureFile(capture_file *cf) bool ProtoTree::eventFilter(QObject * obj, QEvent * event) { if ( cap_file_ && event->type() != QEvent::MouseButtonPress && event->type() != QEvent::MouseMove ) - return QTreeWidget::eventFilter(obj, event); + return QTreeView::eventFilter(obj, event); /* Mouse was over scrollbar, ignoring */ if ( qobject_cast<QScrollBar *>(obj) ) - return QTreeWidget::eventFilter(obj, event); + return QTreeView::eventFilter(obj, event); if ( event->type() == QEvent::MouseButtonPress ) { QMouseEvent * ev = (QMouseEvent *)event; if ( ev->buttons() & Qt::LeftButton ) - dragStartPosition = ev->pos(); + drag_start_position_ = ev->pos(); } else if ( event->type() == QEvent::MouseMove ) { QMouseEvent * ev = (QMouseEvent *)event; - if ( ( ev->buttons() & Qt::LeftButton ) && (ev->pos() - dragStartPosition).manhattanLength() + if ( ( ev->buttons() & Qt::LeftButton ) && (ev->pos() - drag_start_position_).manhattanLength() > QApplication::startDragDistance()) { - QTreeWidgetItem * item = itemAt(dragStartPosition); - if ( item ) + QModelIndex idx = indexAt(drag_start_position_); + FieldInformation finfo(proto_tree_model_->protoNodeFromIndex(idx).protoNode()); + if ( finfo.isValid() ) { - field_info * fi = VariantPointer<field_info>::asPtr(item->data(0, Qt::UserRole)); - if ( fi ) + /* Hack to prevent QItemSelection taking the item which has been dragged over at start + * of drag-drop operation. selectionModel()->blockSignals could have done the trick, but + * it does not take in a QTreeWidget (maybe View) */ + emit fieldSelected(&finfo); + selectionModel()->select(idx, QItemSelectionModel::ClearAndSelect); + + QString filter = QString(proto_construct_match_selected_string(finfo.fieldInfo(), cap_file_->edt)); + + if ( filter.length() > 0 ) { - /* Hack to prevent QItemSelection taking the item which has been dragged over at start - * of drag-drop operation. selectionModel()->blockSignals could have done the trick, but - * it does not take in a QTreeWidget (maybe View) */ - QModelIndex idx = indexFromItem(item, 0); - emit fieldSelected(new FieldInformation(fi, this)); - selectionModel()->select(idx, QItemSelectionModel::ClearAndSelect); - - QString filter = QString(proto_construct_match_selected_string(fi, cap_file_->edt)); - - if ( filter.length() > 0 ) - { - DisplayFilterMimeData * dfmd = - new DisplayFilterMimeData(QString(fi->hfinfo->name), QString(fi->hfinfo->abbrev), filter); - QDrag * drag = new QDrag(this); - drag->setMimeData(dfmd); - - DragLabel * content = new DragLabel(dfmd->labelText(), this); - - #if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) - qreal dpr = window()->windowHandle()->devicePixelRatio(); - QPixmap pixmap(content->size() * dpr); - pixmap.setDevicePixelRatio(dpr); - #else - QPixmap pixmap(content->size()); - #endif - content->render(&pixmap); - drag->setPixmap(pixmap); - - drag->exec(Qt::CopyAction); - - return true; - } + DisplayFilterMimeData * dfmd = + new DisplayFilterMimeData(QString(finfo.headerInfo().name), QString(finfo.headerInfo().abbreviation), filter); + QDrag * drag = new QDrag(this); + drag->setMimeData(dfmd); + + DragLabel * content = new DragLabel(dfmd->labelText(), this); + +#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) + qreal dpr = window()->windowHandle()->devicePixelRatio(); + QPixmap pixmap(content->size() * dpr); + pixmap.setDevicePixelRatio(dpr); +#else + QPixmap pixmap(content->size()); +#endif + content->render(&pixmap); + drag->setPixmap(pixmap); + + drag->exec(Qt::CopyAction); + + return true; } } } } - return QTreeWidget::eventFilter(obj, event); + return QTreeView::eventFilter(obj, event); } +void ProtoTree::rowsInserted(const QModelIndex &parent, int start, int end) +{ + for (int row = start; row <= end; row++) { + QModelIndex index = proto_tree_model_->index(row, 0, parent); + if (proto_tree_model_->protoNodeFromIndex(index).isExpanded()) { + QTreeView::setExpanded(index, true); + } + } +} /* * Editor modelines diff --git a/ui/qt/proto_tree.h b/ui/qt/proto_tree.h index 248d38f28f..2a9da487c2 100644 --- a/ui/qt/proto_tree.h +++ b/ui/qt/proto_tree.h @@ -4,19 +4,7 @@ * 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. + * SPDX-License-Identifier: GPL-2.0+ */ #ifndef PROTO_TREE_H @@ -31,31 +19,34 @@ #include "protocol_preferences_menu.h" #include <ui/qt/utils/field_information.h> -#include <QTreeWidget> +#include <QTreeView> #include <QMenu> -class ProtoTree : public QTreeWidget +class ProtoTreeModel; + +class ProtoTree : public QTreeView { Q_OBJECT public: explicit ProtoTree(QWidget *parent = 0); QMenu *colorizeMenu() { return &colorize_menu_; } - void fillProtocolTree(proto_tree *protocol_tree); + void setRootNode(proto_node *root_node); void emitRelatedFrame(int related_frame, ft_framenum_type_t framenum_type = FT_FRAMENUM_NONE); - void goToField(int hf_id); - void closeContextMenu(); + void goToHfid(int hfid); void clear(); - void saveSelectedField(QTreeWidgetItem *); + void closeContextMenu(); void restoreSelectedField(); + const QString toString(const QModelIndex &index = QModelIndex()) const; protected: virtual void contextMenuEvent(QContextMenuEvent *event); virtual void timerEvent(QTimerEvent *event); virtual void keyReleaseEvent(QKeyEvent *event); - virtual bool eventFilter(QObject * obj, QEvent * ev); + virtual void rowsInserted(const QModelIndex & parent, int start, int end); private: + ProtoTreeModel *proto_tree_model_; QMenu ctx_menu_; QMenu conv_menu_; QMenu colorize_menu_; @@ -64,15 +55,16 @@ private: QList<QAction *> copy_actions_; QFont mono_font_; int column_resize_timer_; - QList<int> selected_field_path_; + QList<QPair<int,int> > selected_hfid_path_; // row, hfinfo - QPoint dragStartPosition; + QPoint drag_start_position_; capture_file *cap_file_; + void saveSelectedField(QModelIndex &index); + signals: void fieldSelected(FieldInformation *); - void openPacketInNewWindow(bool); void goToPacket(int); void relatedFrame(int, ft_framenum_type_t); @@ -84,16 +76,17 @@ public slots: /* Set the capture file */ void setCaptureFile(capture_file *cf); void setMonospaceFont(const QFont &mono_font); - void updateSelectionStatus(QTreeWidgetItem*); void expand(const QModelIndex & index); void collapse(const QModelIndex & index); void expandSubtrees(); void expandAll(); void collapseAll(); - void itemDoubleClick(QTreeWidgetItem *item, int column); - + void itemDoubleClicked(const QModelIndex & index); void selectedFieldChanged(FieldInformation *); +protected slots: + void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); + private slots: void updateContentWidth(); }; diff --git a/ui/qt/protocol_preferences_menu.cpp b/ui/qt/protocol_preferences_menu.cpp index 7e67a986cc..134b303d0c 100644 --- a/ui/qt/protocol_preferences_menu.cpp +++ b/ui/qt/protocol_preferences_menu.cpp @@ -4,19 +4,7 @@ * 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. + * SPDX-License-Identifier: GPL-2.0+ */ #include "config.h" @@ -148,13 +136,13 @@ ProtocolPreferencesMenu::ProtocolPreferencesMenu() setModule(NULL); } -void ProtocolPreferencesMenu::setModule(const char *module_name) +void ProtocolPreferencesMenu::setModule(const QString module_name) { QAction *action; int proto_id = -1; - if (module_name) { - proto_id = proto_get_id_by_filter_name(module_name); + if (!module_name.isEmpty()) { + proto_id = proto_get_id_by_filter_name(module_name.toUtf8().constData()); } clear(); @@ -164,7 +152,7 @@ void ProtocolPreferencesMenu::setModule(const char *module_name) protocol_ = find_protocol_by_id(proto_id); const QString long_name = proto_get_protocol_long_name(protocol_); const QString short_name = proto_get_protocol_short_name(protocol_); - if (!module_name || proto_id < 0 || !protocol_) { + if (module_name.isEmpty() || proto_id < 0 || !protocol_) { action = addAction(tr("No protocol preferences available")); action->setDisabled(true); return; @@ -174,8 +162,8 @@ void ProtocolPreferencesMenu::setModule(const char *module_name) connect(disable_action, SIGNAL(triggered(bool)), this, SLOT(disableProtocolTriggered())); disable_action->setDisabled(!proto_can_toggle_protocol(proto_id)); - module_ = prefs_find_module(module_name); - if (!module_ || !prefs_is_registered_protocol(module_name)) { + module_ = prefs_find_module(module_name.toUtf8().constData()); + if (!module_ || !prefs_is_registered_protocol(module_name.toUtf8().constData())) { action = addAction(tr("%1 has no preferences").arg(long_name)); action->setDisabled(true); addSeparator(); diff --git a/ui/qt/protocol_preferences_menu.h b/ui/qt/protocol_preferences_menu.h index 9a5e9dc800..4672aba7d0 100644 --- a/ui/qt/protocol_preferences_menu.h +++ b/ui/qt/protocol_preferences_menu.h @@ -4,19 +4,7 @@ * 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. + * SPDX-License-Identifier: GPL-2.0+ */ #ifndef __PROTOCOL_PREFERENCES_MENU_H__ @@ -35,7 +23,7 @@ class ProtocolPreferencesMenu : public QMenu public: ProtocolPreferencesMenu(); - void setModule(const char *module_name); + void setModule(const QString module_name); void addMenuItem(struct preference *pref); signals: diff --git a/ui/qt/utils/field_information.cpp b/ui/qt/utils/field_information.cpp index aac03277c5..9b4213412a 100644 --- a/ui/qt/utils/field_information.cpp +++ b/ui/qt/utils/field_information.cpp @@ -11,23 +11,33 @@ #include <ui/qt/utils/field_information.h> -FieldInformation::FieldInformation(field_info * fi, QObject * parent) +FieldInformation::FieldInformation(field_info *fi, QObject * parent) :QObject(parent) { fi_ = fi; parent_fi_ = 0; } +FieldInformation::FieldInformation(proto_node *node, QObject * parent) +:QObject(parent) +{ + fi_ = NULL; + if (node) { + fi_ = node->finfo; + } + parent_fi_ = NULL; +} + bool FieldInformation::isValid() { bool ret = false; - if ( fi_ ) + if ( fi_ && fi_->hfinfo ) { if (fi_->hfinfo->blurb != 0 && fi_->hfinfo->blurb[0] != '\0') { ret = true; } else { - ret = ((QString().fromUtf8(fi_->hfinfo->name)).length() > 0 ); + ret = QString((fi_->hfinfo->name)).length() > 0; } } @@ -39,6 +49,15 @@ void FieldInformation::setParentField(field_info * par_fi) parent_fi_ = par_fi; } +int FieldInformation::treeType() +{ + if (fi_) { + Q_ASSERT(fi_->tree_type >= 0 && fi_->tree_type < num_tree_types); + return fi_->tree_type; + } + return -1; +} + field_info * FieldInformation::fieldInfo() const { return fi_; @@ -52,9 +71,12 @@ FieldInformation::HeaderInfo FieldInformation::headerInfo() const if ( fi_ && fi_->hfinfo ) { header.isValid = true; - header.name = QString().fromUtf8(fi_->hfinfo->name); - header.description = QString().fromUtf8(fi_->hfinfo->blurb); - header.abbreviation = QString().fromUtf8(fi_->hfinfo->abbrev); + header.name = fi_->hfinfo->name; + header.description = fi_->hfinfo->blurb; + header.abbreviation = fi_->hfinfo->abbrev; + header.type = fi_->hfinfo->type; + header.parent = fi_->hfinfo->parent; + header.id = fi_->hfinfo->id; } return header; @@ -73,6 +95,41 @@ bool FieldInformation::tvbContains(FieldInformation *child) return false; } +unsigned FieldInformation::flag(unsigned mask) +{ + if (fi_) { + return FI_GET_FLAG(fi_, mask); + } + return 0; +} + +const QString FieldInformation::moduleName() +{ + QString module_name; + if (isValid()) { + if (headerInfo().parent == -1) { + module_name = fi_->hfinfo->abbrev; + } else { + module_name = proto_registrar_get_abbrev(headerInfo().parent); + } + } + return module_name; +} + +QString FieldInformation::url() +{ + QString url; + if (flag(FI_URL) && headerInfo().isValid && IS_FT_STRING(fi_->hfinfo->type)) { + gchar *url_str; + url_str = fvalue_to_string_repr(NULL, &fi_->value, FTREPR_DISPLAY, fi_->hfinfo->display); + if (url_str) { + url = url_str; + } + wmem_free(NULL, url_str); + } + return url; +} + FieldInformation::Position FieldInformation::position() const { Position pos = {-1, -1}; diff --git a/ui/qt/utils/field_information.h b/ui/qt/utils/field_information.h index 19eacd3ca5..b8f25691ae 100644 --- a/ui/qt/utils/field_information.h +++ b/ui/qt/utils/field_information.h @@ -31,6 +31,9 @@ public: QString description; QString abbreviation; bool isValid; + enum ftenum type; + int parent; + int id; }; struct Position @@ -40,6 +43,7 @@ public: }; explicit FieldInformation(field_info * fi, QObject * parent = Q_NULLPTR); + explicit FieldInformation(proto_node * node, QObject * parent = Q_NULLPTR); bool isValid(); @@ -50,8 +54,12 @@ public: Position appendix() const; void setParentField(field_info * fi); + int treeType(); FieldInformation * parentField() const; bool tvbContains(FieldInformation *); + unsigned flag(unsigned mask); + const QString moduleName(); + QString url(); QByteArray printableData(); diff --git a/ui/qt/utils/proto_node.cpp b/ui/qt/utils/proto_node.cpp new file mode 100644 index 0000000000..0fc87f2a7e --- /dev/null +++ b/ui/qt/utils/proto_node.cpp @@ -0,0 +1,171 @@ +/* proto_node.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <ui/qt/utils/proto_node.h> + +#include <epan/prefs.h> + +ProtoNode::ProtoNode(proto_node *node) : + node_(node) +{ +} + +bool ProtoNode::isValid() const +{ + return node_; +} + +bool ProtoNode::isChild() const +{ + return node_ && node_->parent; +} + +ProtoNode ProtoNode::parentNode() +{ + if (node_) { + return ProtoNode(node_->parent); + } + return ProtoNode(NULL); +} + +QString ProtoNode::labelText() const +{ + if (!node_) { + return QString(); + } + field_info *fi = PNODE_FINFO(node_); + if (!fi) { + return QString(); + } + + QString label; + /* was a free format label produced? */ + if (fi->rep) { + label = fi->rep->representation; + } + else { /* no, make a generic label */ + gchar label_str[ITEM_LABEL_LENGTH]; + proto_item_fill_label(fi, label_str); + label = label_str; + } + + // Generated takes precedence. + if (PROTO_ITEM_IS_GENERATED(node_)) { + label.prepend("["); + label.append("]"); + } + if (PROTO_ITEM_IS_HIDDEN(node_)) { + label.prepend("<"); + label.append(">"); + } + return label; +} + +int ProtoNode::childrenCount() const +{ + if (!node_) return 0; + + int row_count = 0; + ChildIterator kids = children(); + while ( kids.element().isValid() ) + { + row_count++; + kids.next(); + } + + return row_count; +} + +int ProtoNode::row() +{ + if (!isChild()) { + return -1; + } + + int cur_row = 0; + ProtoNode::ChildIterator kids = parentNode().children(); + while ( kids.element().isValid() ) + { + if ( kids.element().protoNode() == node_ ) { + break; + } + cur_row++; + kids.next(); + } + if ( ! kids.element().isValid() ) { + return -1; + } + return cur_row; +} + +bool ProtoNode::isExpanded() const +{ + if (node_ && node_->finfo && tree_expanded(node_->finfo->tree_type)) { + return true; + } + return false; +} + +proto_node * ProtoNode::protoNode() const +{ + return node_; +} + +ProtoNode::ChildIterator ProtoNode::children() const +{ + proto_node *child = node_->first_child; + while (child && isHidden(child)) { + child = child->next; + } + + return ProtoNode::ChildIterator(child); +} + +ProtoNode::ChildIterator::ChildIterator(ProtoNode::ChildIterator::NodePtr n) +{ + node = n; +} + +bool ProtoNode::ChildIterator::hasNext() +{ + if ( ! node || node->next == Q_NULLPTR ) + return false; + return true; +} + +ProtoNode::ChildIterator ProtoNode::ChildIterator::next() +{ + do { + node = node->next; + } while (node && isHidden(node)); + return *this; +} + +ProtoNode ProtoNode::ChildIterator::element() +{ + return ProtoNode(node); +} + +bool ProtoNode::isHidden(proto_node * node) +{ + return PROTO_ITEM_IS_HIDDEN(node) && !prefs.display_hidden_proto_items; +} + +/* + * 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: + */ diff --git a/ui/qt/utils/proto_node.h b/ui/qt/utils/proto_node.h new file mode 100644 index 0000000000..044dbf7e07 --- /dev/null +++ b/ui/qt/utils/proto_node.h @@ -0,0 +1,69 @@ +/* proto_node.h + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef PROTO_NODE_H_ +#define PROTO_NODE_H_ + +#include <config.h> + +#include <ui/qt/utils/field_information.h> + +class ProtoNode +{ +public: + + class ChildIterator { + public: + typedef struct _proto_node * NodePtr; + + ChildIterator(NodePtr n = Q_NULLPTR); + + bool hasNext(); + ChildIterator next(); + ProtoNode element(); + + protected: + NodePtr node; + }; + + explicit ProtoNode(proto_node * node = NULL); + + bool isValid() const; + bool isChild() const; + bool isExpanded() const; + + proto_node *protoNode() const; + int childrenCount() const; + int row(); + ProtoNode parentNode(); + + QString labelText() const; + + ChildIterator children() const; + +private: + proto_node * node_; + static bool isHidden(proto_node * node); +}; + + +#endif // PROTO_NODE_H_ + +/* + * 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: + */ |