aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ui/qt/CMakeLists.txt14
-rw-r--r--ui/qt/Makefile.am18
-rw-r--r--ui/qt/byte_view_tab.cpp4
-rw-r--r--ui/qt/main_window.cpp14
-rw-r--r--ui/qt/main_window.h15
-rw-r--r--ui/qt/main_window_slots.cpp41
-rw-r--r--ui/qt/models/proto_tree_model.cpp222
-rw-r--r--ui/qt/models/proto_tree_model.h58
-rw-r--r--ui/qt/packet_dialog.cpp16
-rw-r--r--ui/qt/packet_list.cpp18
-rw-r--r--ui/qt/proto_tree.cpp615
-rw-r--r--ui/qt/proto_tree.h45
-rw-r--r--ui/qt/protocol_preferences_menu.cpp26
-rw-r--r--ui/qt/protocol_preferences_menu.h16
-rw-r--r--ui/qt/utils/field_information.cpp69
-rw-r--r--ui/qt/utils/field_information.h8
-rw-r--r--ui/qt/utils/proto_node.cpp171
-rw-r--r--ui/qt/utils/proto_node.h69
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:
+ */