2012-01-04 22:13:01 +00:00
|
|
|
/* proto_tree.cpp
|
|
|
|
*
|
|
|
|
* Wireshark - Network traffic analyzer
|
|
|
|
* By Gerald Combs <gerald@wireshark.org>
|
|
|
|
* Copyright 1998 Gerald Combs
|
|
|
|
*
|
2018-02-07 11:26:45 +00:00
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
2012-01-04 22:13:01 +00:00
|
|
|
*/
|
|
|
|
|
2012-01-14 00:16:16 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
|
2019-07-28 19:24:24 +00:00
|
|
|
#include <ui/qt/proto_tree.h>
|
2017-12-07 16:15:30 +00:00
|
|
|
#include <ui/qt/models/proto_tree_model.h>
|
2012-01-04 22:13:01 +00:00
|
|
|
|
2012-08-19 23:52:08 +00:00
|
|
|
#include <epan/ftypes/ftypes.h>
|
2012-01-04 22:13:01 +00:00
|
|
|
#include <epan/prefs.h>
|
2019-11-21 09:50:41 +00:00
|
|
|
#include <epan/epan.h>
|
|
|
|
#include <epan/epan_dissect.h>
|
|
|
|
#include <cfile.h>
|
2012-01-04 22:13:01 +00:00
|
|
|
|
2019-03-29 11:50:14 +00:00
|
|
|
#include <ui/qt/utils/color_utils.h>
|
2017-07-25 14:15:18 +00:00
|
|
|
#include <ui/qt/utils/variant_pointer.h>
|
2017-11-07 15:51:43 +00:00
|
|
|
#include <ui/qt/utils/wireshark_mime_data.h>
|
|
|
|
#include <ui/qt/widgets/drag_label.h>
|
2019-07-28 19:24:24 +00:00
|
|
|
#include <ui/qt/widgets/wireshark_file_dialog.h>
|
|
|
|
#include <ui/qt/show_packet_bytes_dialog.h>
|
2019-08-26 13:30:22 +00:00
|
|
|
#include <ui/qt/filter_action.h>
|
2019-07-28 19:24:24 +00:00
|
|
|
#include <ui/all_files_wildcard.h>
|
|
|
|
#include <ui/alert_box.h>
|
2020-10-03 02:17:00 +00:00
|
|
|
#include <ui/urls.h>
|
2019-07-28 19:24:24 +00:00
|
|
|
#include "wireshark_application.h"
|
2017-01-11 12:55:23 +00:00
|
|
|
|
2014-09-23 20:35:10 +00:00
|
|
|
#include <QApplication>
|
|
|
|
#include <QContextMenuEvent>
|
|
|
|
#include <QDesktopServices>
|
2012-01-04 22:13:01 +00:00
|
|
|
#include <QHeaderView>
|
2017-12-07 16:15:30 +00:00
|
|
|
#include <QItemSelectionModel>
|
2016-03-10 20:43:59 +00:00
|
|
|
#include <QScrollBar>
|
2017-12-07 16:15:30 +00:00
|
|
|
#include <QStack>
|
2012-08-19 23:52:08 +00:00
|
|
|
#include <QUrl>
|
2019-07-28 19:24:24 +00:00
|
|
|
#include <QClipboard>
|
2017-11-07 15:51:43 +00:00
|
|
|
#include <QWindow>
|
2019-07-28 19:24:24 +00:00
|
|
|
#include <QMessageBox>
|
2019-11-03 17:12:12 +00:00
|
|
|
#include <QJsonDocument>
|
|
|
|
#include <QJsonObject>
|
2017-11-07 15:51:43 +00:00
|
|
|
|
2015-07-23 19:53:45 +00:00
|
|
|
// To do:
|
|
|
|
// - Fix "apply as filter" behavior.
|
|
|
|
|
2018-04-26 11:13:54 +00:00
|
|
|
ProtoTree::ProtoTree(QWidget *parent, epan_dissect_t *edt_fixed) :
|
2017-12-07 16:15:30 +00:00
|
|
|
QTreeView(parent),
|
|
|
|
proto_tree_model_(new ProtoTreeModel(this)),
|
2017-11-09 17:39:55 +00:00
|
|
|
column_resize_timer_(0),
|
2018-04-26 11:13:54 +00:00
|
|
|
cap_file_(NULL),
|
|
|
|
edt_(edt_fixed)
|
2012-01-04 22:13:01 +00:00
|
|
|
{
|
2012-03-07 10:16:33 +00:00
|
|
|
setAccessibleName(tr("Packet details"));
|
2015-05-12 23:28:39 +00:00
|
|
|
// Leave the uniformRowHeights property as-is (false) since items might
|
|
|
|
// have multiple lines (e.g. packet comments). If this slows things down
|
|
|
|
// too much we should add a custom delegate which handles SizeHintRole
|
|
|
|
// similar to PacketListModel::data.
|
2015-02-11 23:00:27 +00:00
|
|
|
setHeaderHidden(true);
|
|
|
|
|
2019-03-29 11:50:14 +00:00
|
|
|
#if !defined(Q_OS_WIN)
|
|
|
|
#if defined(Q_OS_MAC)
|
|
|
|
QPalette default_pal = QApplication::palette();
|
|
|
|
default_pal.setCurrentColorGroup(QPalette::Active);
|
|
|
|
QColor hover_color = default_pal.highlight().color();
|
|
|
|
#else
|
|
|
|
QColor hover_color = ColorUtils::alphaBlend(palette().window(), palette().highlight(), 0.5);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
setStyleSheet(QString(
|
|
|
|
"QTreeView:item:hover {"
|
|
|
|
" background-color: %1;"
|
|
|
|
" color: palette(text);"
|
2019-06-30 08:04:18 +00:00
|
|
|
"}").arg(hover_color.name(QColor::HexArgb)));
|
2019-03-29 11:50:14 +00:00
|
|
|
#endif
|
|
|
|
|
2018-04-10 09:25:53 +00:00
|
|
|
// Shrink down to a small but nonzero size in the main splitter.
|
|
|
|
int one_em = fontMetrics().height();
|
|
|
|
setMinimumSize(one_em, one_em);
|
|
|
|
|
2017-12-07 16:15:30 +00:00
|
|
|
setModel(proto_tree_model_);
|
|
|
|
|
2018-01-15 18:18:48 +00:00
|
|
|
connect(this, SIGNAL(expanded(QModelIndex)), this, SLOT(syncExpanded(QModelIndex)));
|
|
|
|
connect(this, SIGNAL(collapsed(QModelIndex)), this, SLOT(syncCollapsed(QModelIndex)));
|
2020-03-05 21:56:57 +00:00
|
|
|
connect(this, SIGNAL(clicked(QModelIndex)),
|
|
|
|
this, SLOT(itemClicked(QModelIndex)));
|
2017-12-07 16:15:30 +00:00
|
|
|
connect(this, SIGNAL(doubleClicked(QModelIndex)),
|
|
|
|
this, SLOT(itemDoubleClicked(QModelIndex)));
|
2015-06-17 00:24:19 +00:00
|
|
|
|
|
|
|
connect(&proto_prefs_menu_, SIGNAL(showProtocolPreferences(QString)),
|
|
|
|
this, SIGNAL(showProtocolPreferences(QString)));
|
2015-06-17 15:41:41 +00:00
|
|
|
connect(&proto_prefs_menu_, SIGNAL(editProtocolPreference(preference*,pref_module*)),
|
|
|
|
this, SIGNAL(editProtocolPreference(preference*,pref_module*)));
|
2016-03-10 20:43:59 +00:00
|
|
|
|
|
|
|
// resizeColumnToContents checks 1000 items by default. The user might
|
|
|
|
// have scrolled to an area with a different width at this point.
|
|
|
|
connect(verticalScrollBar(), SIGNAL(sliderReleased()),
|
|
|
|
this, SLOT(updateContentWidth()));
|
2017-11-07 15:51:43 +00:00
|
|
|
|
2019-11-12 15:39:19 +00:00
|
|
|
connect(wsApp, SIGNAL(appInitialized()), this, SLOT(connectToMainWindow()));
|
|
|
|
|
2017-11-07 15:51:43 +00:00
|
|
|
viewport()->installEventFilter(this);
|
2012-01-14 00:16:16 +00:00
|
|
|
}
|
|
|
|
|
2017-12-07 16:15:30 +00:00
|
|
|
void ProtoTree::clear() {
|
|
|
|
proto_tree_model_->setRootNode(NULL);
|
|
|
|
updateContentWidth();
|
|
|
|
}
|
|
|
|
|
2019-11-12 15:39:19 +00:00
|
|
|
void ProtoTree::connectToMainWindow()
|
|
|
|
{
|
2019-11-17 19:02:20 +00:00
|
|
|
if (wsApp->mainWindow())
|
2019-11-12 15:39:19 +00:00
|
|
|
{
|
|
|
|
connect(wsApp->mainWindow(), SIGNAL(fieldSelected(FieldInformation *)),
|
|
|
|
this, SLOT(selectedFieldChanged(FieldInformation *)));
|
2019-11-21 09:50:41 +00:00
|
|
|
connect(wsApp->mainWindow(), SIGNAL(framesSelected(QList<int>)),
|
|
|
|
this, SLOT(selectedFrameChanged(QList<int>)));
|
2019-11-12 15:39:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-28 19:24:24 +00:00
|
|
|
void ProtoTree::ctxCopyVisibleItems()
|
|
|
|
{
|
|
|
|
bool selected_tree = false;
|
|
|
|
|
|
|
|
QAction * send = qobject_cast<QAction *>(sender());
|
2019-11-17 19:02:20 +00:00
|
|
|
if (send && send->property("selected_tree").isValid())
|
2019-07-28 19:24:24 +00:00
|
|
|
selected_tree = true;
|
|
|
|
|
2020-03-08 19:38:12 +00:00
|
|
|
QString clip;
|
2019-11-17 19:02:20 +00:00
|
|
|
if (selected_tree && selectionModel()->hasSelection())
|
2019-07-28 19:24:24 +00:00
|
|
|
clip = toString(selectionModel()->selectedIndexes().first());
|
2020-03-08 19:38:12 +00:00
|
|
|
else
|
|
|
|
clip = toString();
|
2019-07-28 19:24:24 +00:00
|
|
|
|
2019-11-17 19:02:20 +00:00
|
|
|
if (clip.length() > 0)
|
2019-07-28 19:24:24 +00:00
|
|
|
wsApp->clipboard()->setText(clip);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProtoTree::ctxCopyAsFilter()
|
|
|
|
{
|
|
|
|
QModelIndex idx = selectionModel()->selectedIndexes().first();
|
|
|
|
FieldInformation finfo(proto_tree_model_->protoNodeFromIndex(idx).protoNode());
|
2019-11-17 19:02:20 +00:00
|
|
|
if (finfo.isValid())
|
2019-07-28 19:24:24 +00:00
|
|
|
{
|
|
|
|
epan_dissect_t *edt = cap_file_ ? cap_file_->edt : edt_;
|
|
|
|
char *field_filter = proto_construct_match_selected_string(finfo.fieldInfo(), edt);
|
|
|
|
QString filter(field_filter);
|
|
|
|
wmem_free(Q_NULLPTR, field_filter);
|
|
|
|
|
2019-11-17 19:02:20 +00:00
|
|
|
if (filter.length() > 0)
|
2019-07-28 19:24:24 +00:00
|
|
|
wsApp->clipboard()->setText(filter);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProtoTree::ctxCopySelectedInfo()
|
|
|
|
{
|
|
|
|
int val = -1;
|
|
|
|
QString clip;
|
|
|
|
QAction * send = qobject_cast<QAction *>(sender());
|
2019-11-17 19:02:20 +00:00
|
|
|
if (send && send->property("field_type").isValid())
|
2019-07-28 19:24:24 +00:00
|
|
|
val = send->property("field_type").toInt();
|
|
|
|
|
|
|
|
QModelIndex idx = selectionModel()->selectedIndexes().first();
|
|
|
|
FieldInformation finfo(proto_tree_model_->protoNodeFromIndex(idx).protoNode());
|
2019-11-17 19:02:20 +00:00
|
|
|
if (! finfo.isValid())
|
2019-07-28 19:24:24 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
switch (val)
|
|
|
|
{
|
|
|
|
case ProtoTree::Name:
|
|
|
|
clip.append(finfo.headerInfo().abbreviation);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ProtoTree::Description:
|
2020-03-08 19:38:12 +00:00
|
|
|
clip = idx.data(Qt::DisplayRole).toString();
|
2019-07-28 19:24:24 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ProtoTree::Value:
|
|
|
|
{
|
2019-09-10 11:40:23 +00:00
|
|
|
epan_dissect_t *edt = cap_file_ ? cap_file_->edt : edt_;
|
|
|
|
gchar* field_str = get_node_field_value(finfo.fieldInfo(), edt);
|
2019-07-28 19:24:24 +00:00
|
|
|
clip.append(field_str);
|
|
|
|
g_free(field_str);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2019-11-17 19:02:20 +00:00
|
|
|
if (clip.length() > 0)
|
2019-07-28 19:24:24 +00:00
|
|
|
wsApp->clipboard()->setText(clip);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProtoTree::ctxOpenUrlWiki()
|
|
|
|
{
|
|
|
|
QUrl url;
|
|
|
|
bool is_field_reference = false;
|
|
|
|
QAction * send = qobject_cast<QAction *>(sender());
|
2019-11-17 19:02:20 +00:00
|
|
|
if (send && send->property("field_reference").isValid())
|
2019-07-28 19:24:24 +00:00
|
|
|
is_field_reference = send->property("field_reference").toBool();
|
|
|
|
QModelIndex idx = selectionModel()->selectedIndexes().first();
|
|
|
|
FieldInformation finfo(proto_tree_model_->protoNodeFromIndex(idx).protoNode());
|
|
|
|
|
|
|
|
const QString proto_abbrev = proto_registrar_get_abbrev(finfo.headerInfo().id);
|
|
|
|
|
2019-11-17 19:02:20 +00:00
|
|
|
if (! is_field_reference)
|
2019-07-28 19:24:24 +00:00
|
|
|
{
|
|
|
|
int ret = QMessageBox::question(this, wsApp->windowTitleString(tr("Wiki Page for %1").arg(proto_abbrev)),
|
|
|
|
tr("<p>The Wireshark Wiki is maintained by the community.</p>"
|
|
|
|
"<p>The page you are about to load might be wonderful, "
|
|
|
|
"incomplete, wrong, or nonexistent.</p>"
|
|
|
|
"<p>Proceed to the wiki?</p>"),
|
|
|
|
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
|
|
|
|
|
|
|
|
if (ret != QMessageBox::Yes) return;
|
|
|
|
|
2020-10-26 00:42:11 +00:00
|
|
|
url = QString(WS_WIKI_URL("Protocols/%1")).arg(proto_abbrev);
|
2019-07-28 19:24:24 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-10-03 02:17:00 +00:00
|
|
|
url = QString(WS_DOCS_URL "/dfref/%1/%2")
|
2019-07-28 19:24:24 +00:00
|
|
|
.arg(proto_abbrev[0])
|
|
|
|
.arg(proto_abbrev);
|
|
|
|
}
|
|
|
|
|
|
|
|
QDesktopServices::openUrl(url);
|
|
|
|
}
|
|
|
|
|
2012-12-07 01:46:20 +00:00
|
|
|
void ProtoTree::contextMenuEvent(QContextMenuEvent *event)
|
|
|
|
{
|
2019-08-29 12:30:45 +00:00
|
|
|
QModelIndex index = indexAt(event->pos());
|
2019-11-17 19:02:20 +00:00
|
|
|
if (! index.isValid())
|
2019-08-29 12:30:45 +00:00
|
|
|
return;
|
|
|
|
|
2017-12-21 18:59:45 +00:00
|
|
|
// We're in a PacketDialog
|
2019-08-29 12:30:45 +00:00
|
|
|
bool buildForDialog = false;
|
2017-12-21 18:59:45 +00:00
|
|
|
if (! window()->findChild<QAction *>("actionViewExpandSubtrees"))
|
2019-08-29 12:30:45 +00:00
|
|
|
buildForDialog = true;
|
2017-12-21 18:59:45 +00:00
|
|
|
|
2019-08-26 14:30:05 +00:00
|
|
|
QMenu ctx_menu(this);
|
2017-12-21 18:59:45 +00:00
|
|
|
|
|
|
|
QMenu *main_menu_item, *submenu;
|
|
|
|
QAction *action;
|
|
|
|
|
2019-08-29 12:30:45 +00:00
|
|
|
bool have_subtree = false;
|
|
|
|
FieldInformation finfo(proto_tree_model_->protoNodeFromIndex(index).protoNode());
|
|
|
|
field_info * fi = finfo.fieldInfo();
|
|
|
|
bool is_selected = false;
|
|
|
|
epan_dissect_t *edt = cap_file_ ? cap_file_->edt : edt_;
|
|
|
|
|
|
|
|
if (cap_file_ && cap_file_->finfo_selected == fi)
|
|
|
|
is_selected = true;
|
|
|
|
else if (! window()->findChild<QAction *>("actionViewExpandSubtrees"))
|
|
|
|
is_selected = true;
|
|
|
|
|
2019-11-17 19:02:20 +00:00
|
|
|
if (is_selected)
|
2019-08-29 12:30:45 +00:00
|
|
|
{
|
|
|
|
if (fi && fi->tree_type != -1) {
|
|
|
|
have_subtree = true;
|
|
|
|
}
|
|
|
|
}
|
2017-12-21 18:59:45 +00:00
|
|
|
|
2019-08-29 12:30:45 +00:00
|
|
|
action = ctx_menu.addAction(tr("Expand Subtrees"), this, SLOT(expandSubtrees()));
|
|
|
|
action->setEnabled(have_subtree);
|
|
|
|
action = ctx_menu.addAction(tr("Collapse Subtrees"), this, SLOT(collapseSubtrees()));
|
|
|
|
action->setEnabled(have_subtree);
|
|
|
|
ctx_menu.addAction(tr("Expand All"), this, SLOT(expandAll()));
|
|
|
|
ctx_menu.addAction(tr("Collapse All"), this, SLOT(collapseAll()));
|
2019-08-26 14:30:05 +00:00
|
|
|
ctx_menu.addSeparator();
|
2017-12-21 18:59:45 +00:00
|
|
|
|
2019-11-17 19:02:20 +00:00
|
|
|
if (! buildForDialog)
|
2019-08-29 12:30:45 +00:00
|
|
|
{
|
|
|
|
action = window()->findChild<QAction *>("actionAnalyzeCreateAColumn");
|
|
|
|
ctx_menu.addAction(action);
|
|
|
|
ctx_menu.addSeparator();
|
|
|
|
}
|
2019-08-26 13:30:22 +00:00
|
|
|
|
|
|
|
char * selectedfilter = proto_construct_match_selected_string(finfo.fieldInfo(), edt);
|
|
|
|
bool can_match_selected = proto_can_match_selected(finfo.fieldInfo(), edt);
|
2019-08-27 15:14:31 +00:00
|
|
|
ctx_menu.addMenu(FilterAction::createFilterMenu(FilterAction::ActionApply, selectedfilter, can_match_selected, &ctx_menu));
|
|
|
|
ctx_menu.addMenu(FilterAction::createFilterMenu(FilterAction::ActionPrepare, selectedfilter, can_match_selected, &ctx_menu));
|
2019-11-17 19:02:20 +00:00
|
|
|
if (selectedfilter)
|
2019-08-26 13:30:22 +00:00
|
|
|
wmem_free(Q_NULLPTR, selectedfilter);
|
2015-02-11 23:00:27 +00:00
|
|
|
|
2019-11-17 19:02:20 +00:00
|
|
|
if (! buildForDialog)
|
2019-08-29 12:30:45 +00:00
|
|
|
{
|
|
|
|
QMenu *main_conv_menu = window()->findChild<QMenu *>("menuConversationFilter");
|
|
|
|
conv_menu_.setTitle(main_conv_menu->title());
|
|
|
|
conv_menu_.clear();
|
|
|
|
foreach (QAction *action, main_conv_menu->actions()) {
|
|
|
|
conv_menu_.addAction(action);
|
|
|
|
}
|
2017-12-21 18:59:45 +00:00
|
|
|
|
2019-08-29 12:30:45 +00:00
|
|
|
ctx_menu.addMenu(&conv_menu_);
|
2017-12-21 18:59:45 +00:00
|
|
|
|
2019-08-29 12:30:45 +00:00
|
|
|
colorize_menu_.setTitle(tr("Colorize with Filter"));
|
|
|
|
ctx_menu.addMenu(&colorize_menu_);
|
2017-12-21 18:59:45 +00:00
|
|
|
|
2019-08-29 12:30:45 +00:00
|
|
|
main_menu_item = window()->findChild<QMenu *>("menuFollow");
|
|
|
|
submenu = new QMenu(main_menu_item->title(), &ctx_menu);
|
|
|
|
ctx_menu.addMenu(submenu);
|
|
|
|
submenu->addAction(window()->findChild<QAction *>("actionAnalyzeFollowTCPStream"));
|
|
|
|
submenu->addAction(window()->findChild<QAction *>("actionAnalyzeFollowUDPStream"));
|
2021-02-22 11:48:46 +00:00
|
|
|
submenu->addAction(window()->findChild<QAction *>("actionAnalyzeFollowDCCPStream"));
|
2019-08-29 12:30:45 +00:00
|
|
|
submenu->addAction(window()->findChild<QAction *>("actionAnalyzeFollowTLSStream"));
|
|
|
|
submenu->addAction(window()->findChild<QAction *>("actionAnalyzeFollowHTTPStream"));
|
2019-02-27 04:55:52 +00:00
|
|
|
submenu->addAction(window()->findChild<QAction *>("actionAnalyzeFollowHTTP2Stream"));
|
2019-10-02 00:20:43 +00:00
|
|
|
submenu->addAction(window()->findChild<QAction *>("actionAnalyzeFollowQUICStream"));
|
2019-08-29 12:30:45 +00:00
|
|
|
ctx_menu.addSeparator();
|
|
|
|
}
|
2017-12-21 18:59:45 +00:00
|
|
|
|
2019-08-29 12:30:45 +00:00
|
|
|
submenu = ctx_menu.addMenu(tr("Copy"));
|
|
|
|
submenu->addAction(tr("All Visible Items"), this, SLOT(ctxCopyVisibleItems()));
|
|
|
|
action = submenu->addAction(tr("All Visible Selected Tree Items"), this, SLOT(ctxCopyVisibleItems()));
|
2019-12-20 05:57:04 +00:00
|
|
|
action->setProperty("selected_tree", QVariant::fromValue(true));
|
2019-09-21 00:53:29 +00:00
|
|
|
action = submenu->addAction(tr("Description"), this, SLOT(ctxCopySelectedInfo()));
|
2019-08-29 12:30:45 +00:00
|
|
|
action->setProperty("field_type", ProtoTree::Description);
|
|
|
|
action = submenu->addAction(tr("Field Name"), this, SLOT(ctxCopySelectedInfo()));
|
|
|
|
action->setProperty("field_type", ProtoTree::Name);
|
|
|
|
action = submenu->addAction(tr("Value"), this, SLOT(ctxCopySelectedInfo()));
|
|
|
|
action->setProperty("field_type", ProtoTree::Value);
|
2017-12-21 18:59:45 +00:00
|
|
|
submenu->addSeparator();
|
2019-08-29 12:30:45 +00:00
|
|
|
submenu->addAction(tr("As Filter"), this, SLOT(ctxCopyAsFilter()));
|
2020-01-14 09:44:39 +00:00
|
|
|
submenu->addSeparator();
|
2017-12-21 18:59:45 +00:00
|
|
|
QActionGroup * copyEntries = DataPrinter::copyActions(this, &finfo);
|
|
|
|
submenu->addActions(copyEntries->actions());
|
2019-08-29 12:30:45 +00:00
|
|
|
ctx_menu.addSeparator();
|
2015-06-17 00:24:19 +00:00
|
|
|
|
2019-11-17 19:02:20 +00:00
|
|
|
if (! buildForDialog)
|
2019-08-29 12:30:45 +00:00
|
|
|
{
|
|
|
|
action = window()->findChild<QAction *>("actionAnalyzeShowPacketBytes");
|
|
|
|
ctx_menu.addAction(action);
|
|
|
|
action = window()->findChild<QAction *>("actionFileExportPacketBytes");
|
|
|
|
ctx_menu.addAction(action);
|
2017-12-21 18:59:45 +00:00
|
|
|
|
2019-08-29 12:30:45 +00:00
|
|
|
ctx_menu.addSeparator();
|
|
|
|
}
|
2017-12-21 18:59:45 +00:00
|
|
|
|
2019-08-29 12:30:45 +00:00
|
|
|
ctx_menu.addAction(tr("Wiki Protocol Page"), this, SLOT(ctxOpenUrlWiki()));
|
|
|
|
action = ctx_menu.addAction(tr("Filter Field Reference"), this, SLOT(ctxOpenUrlWiki()));
|
2019-12-20 05:57:04 +00:00
|
|
|
action->setProperty("field_reference", QVariant::fromValue(true));
|
2019-08-26 14:30:05 +00:00
|
|
|
ctx_menu.addMenu(&proto_prefs_menu_);
|
|
|
|
ctx_menu.addSeparator();
|
2017-12-21 18:59:45 +00:00
|
|
|
|
2019-11-17 19:02:20 +00:00
|
|
|
if (! buildForDialog)
|
2019-08-29 12:30:45 +00:00
|
|
|
{
|
2020-02-04 02:07:10 +00:00
|
|
|
QAction *decode_as_ = window()->findChild<QAction *>("actionAnalyzeDecodeAs");
|
|
|
|
ctx_menu.addAction(decode_as_);
|
|
|
|
decode_as_->setProperty("create_new", QVariant::fromValue(true));
|
|
|
|
|
2019-08-29 12:30:45 +00:00
|
|
|
ctx_menu.addAction(window()->findChild<QAction *>("actionGoGoToLinkedPacket"));
|
|
|
|
ctx_menu.addAction(window()->findChild<QAction *>("actionContextShowLinkedPacketInNewWindow"));
|
2017-12-21 18:59:45 +00:00
|
|
|
|
2019-08-29 12:30:45 +00:00
|
|
|
// The "text only" header field will not give preferences for the selected protocol.
|
|
|
|
// Use parent in this case.
|
|
|
|
proto_node *node = proto_tree_model_->protoNodeFromIndex(index).protoNode();
|
|
|
|
while (node && node->finfo && node->finfo->hfinfo && node->finfo->hfinfo->id == hf_text_only)
|
|
|
|
node = node->parent;
|
2018-09-07 07:29:39 +00:00
|
|
|
|
2019-08-29 12:30:45 +00:00
|
|
|
FieldInformation pref_finfo(node);
|
|
|
|
proto_prefs_menu_.setModule(pref_finfo.moduleName());
|
|
|
|
}
|
2015-07-29 23:24:39 +00:00
|
|
|
|
2019-08-26 14:30:05 +00:00
|
|
|
ctx_menu.exec(event->globalPos());
|
2012-12-07 01:46:20 +00:00
|
|
|
}
|
|
|
|
|
2016-03-10 20:43:59 +00:00
|
|
|
void ProtoTree::timerEvent(QTimerEvent *event)
|
|
|
|
{
|
|
|
|
if (event->timerId() == column_resize_timer_) {
|
|
|
|
killTimer(column_resize_timer_);
|
|
|
|
column_resize_timer_ = 0;
|
|
|
|
resizeColumnToContents(0);
|
2016-03-14 20:29:21 +00:00
|
|
|
} else {
|
2017-12-07 16:15:30 +00:00
|
|
|
QTreeView::timerEvent(event);
|
2016-03-10 20:43:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// resizeColumnToContents checks 1000 items by default. The user might
|
|
|
|
// have scrolled to an area with a different width at this point.
|
|
|
|
void ProtoTree::keyReleaseEvent(QKeyEvent *event)
|
|
|
|
{
|
|
|
|
if (event->isAutoRepeat()) return;
|
|
|
|
|
|
|
|
switch(event->key()) {
|
|
|
|
case Qt::Key_Up:
|
|
|
|
case Qt::Key_Down:
|
|
|
|
case Qt::Key_PageUp:
|
|
|
|
case Qt::Key_PageDown:
|
|
|
|
case Qt::Key_Home:
|
|
|
|
case Qt::Key_End:
|
|
|
|
updateContentWidth();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProtoTree::updateContentWidth()
|
|
|
|
{
|
|
|
|
if (column_resize_timer_ == 0) {
|
|
|
|
column_resize_timer_ = startTimer(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-23 20:35:10 +00:00
|
|
|
void ProtoTree::setMonospaceFont(const QFont &mono_font)
|
|
|
|
{
|
2019-11-28 16:29:39 +00:00
|
|
|
setFont(mono_font);
|
2014-09-23 20:35:10 +00:00
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
2018-01-16 01:13:45 +00:00
|
|
|
void ProtoTree::foreachTreeNode(proto_node *node, gpointer proto_tree_ptr)
|
|
|
|
{
|
|
|
|
ProtoTree *tree_view = static_cast<ProtoTree *>(proto_tree_ptr);
|
|
|
|
ProtoTreeModel *model = qobject_cast<ProtoTreeModel *>(tree_view->model());
|
|
|
|
if (!tree_view || !model) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Expanded state
|
|
|
|
if (tree_expanded(node->finfo->tree_type)) {
|
|
|
|
ProtoNode expand_node = ProtoNode(node);
|
|
|
|
tree_view->expand(model->indexFromProtoNode(expand_node));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Related frames
|
|
|
|
if (node->finfo->hfinfo->type == FT_FRAMENUM) {
|
|
|
|
ft_framenum_type_t framenum_type = (ft_framenum_type_t)GPOINTER_TO_INT(node->finfo->hfinfo->strings);
|
|
|
|
tree_view->emitRelatedFrame(node->finfo->value.value.uinteger, framenum_type);
|
|
|
|
}
|
|
|
|
|
|
|
|
proto_tree_children_foreach(node, foreachTreeNode, proto_tree_ptr);
|
|
|
|
}
|
|
|
|
|
Qt: fix erratic expansion of tree item when switching packets
ProtoTree::setRootNode() is designed to update the model with the new
packet tree, and additionally expand tree items in its view. When the
current selected packet is changed, it must use this method to ensure
that collapsed trees are properly expanded. Fix this regression.
It was not entirely clear that framesSelected can no longer use previous
state, so document it explicitly. Remove the call to QTreeView::reset(),
it ends up calling QAbstractItemView::reset() which touches the
selection model that refers invalidated proto_node memory. The reset
function of the view is automatically called the model is reset, so the
call was not needed anyway.
Test: open test/captures/tls13-rfc8446.pcap, expand TLS, TLS Record, and
select "Content Type". Change from frame 1 to 2, and then 3. Observe
that the expanded state remains constant with no flickering. In frame 3,
observe that the tree remains expanded even if no item is selected.
Change-Id: I0c820711f1a62aa51ac100f8ac5c89265c51eb18
Fixes: v3.3.0rc0-6-gcfee0f8082 ("Qt: Remove frameSelect signal")
Reviewed-on: https://code.wireshark.org/review/35230
Petri-Dish: Peter Wu <peter@lekensteyn.nl>
Tested-by: Petri Dish Buildbot
Reviewed-by: Roland Knall <rknall@gmail.com>
2019-11-27 00:44:55 +00:00
|
|
|
// setRootNode sets the new contents for the protocol tree and subsequently
|
|
|
|
// restores the previously expanded state.
|
2017-12-07 16:15:30 +00:00
|
|
|
void ProtoTree::setRootNode(proto_node *root_node) {
|
Qt: fix erratic expansion of tree item when switching packets
ProtoTree::setRootNode() is designed to update the model with the new
packet tree, and additionally expand tree items in its view. When the
current selected packet is changed, it must use this method to ensure
that collapsed trees are properly expanded. Fix this regression.
It was not entirely clear that framesSelected can no longer use previous
state, so document it explicitly. Remove the call to QTreeView::reset(),
it ends up calling QAbstractItemView::reset() which touches the
selection model that refers invalidated proto_node memory. The reset
function of the view is automatically called the model is reset, so the
call was not needed anyway.
Test: open test/captures/tls13-rfc8446.pcap, expand TLS, TLS Record, and
select "Content Type". Change from frame 1 to 2, and then 3. Observe
that the expanded state remains constant with no flickering. In frame 3,
observe that the tree remains expanded even if no item is selected.
Change-Id: I0c820711f1a62aa51ac100f8ac5c89265c51eb18
Fixes: v3.3.0rc0-6-gcfee0f8082 ("Qt: Remove frameSelect signal")
Reviewed-on: https://code.wireshark.org/review/35230
Petri-Dish: Peter Wu <peter@lekensteyn.nl>
Tested-by: Petri Dish Buildbot
Reviewed-by: Roland Knall <rknall@gmail.com>
2019-11-27 00:44:55 +00:00
|
|
|
// We track item expansion using proto.c:tree_is_expanded.
|
|
|
|
// Replace any existing (possibly invalidated) proto tree by the new tree.
|
|
|
|
// The expanded state will be reset as well and will be re-expanded below.
|
2017-12-07 16:15:30 +00:00
|
|
|
proto_tree_model_->setRootNode(root_node);
|
2018-01-16 01:13:45 +00:00
|
|
|
|
|
|
|
disconnect(this, SIGNAL(expanded(QModelIndex)), this, SLOT(syncExpanded(QModelIndex)));
|
|
|
|
proto_tree_children_foreach(root_node, foreachTreeNode, this);
|
|
|
|
connect(this, SIGNAL(expanded(QModelIndex)), this, SLOT(syncExpanded(QModelIndex)));
|
|
|
|
|
2016-03-10 20:43:59 +00:00
|
|
|
updateContentWidth();
|
2012-01-14 00:16:16 +00:00
|
|
|
}
|
|
|
|
|
2015-02-24 01:56:14 +00:00
|
|
|
void ProtoTree::emitRelatedFrame(int related_frame, ft_framenum_type_t framenum_type)
|
2013-07-08 16:54:18 +00:00
|
|
|
{
|
2015-02-24 01:56:14 +00:00
|
|
|
emit relatedFrame(related_frame, framenum_type);
|
2013-07-08 16:54:18 +00:00
|
|
|
}
|
|
|
|
|
2018-01-15 18:18:48 +00:00
|
|
|
void ProtoTree::autoScrollTo(const QModelIndex &index)
|
|
|
|
{
|
2019-11-27 02:11:53 +00:00
|
|
|
selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect);
|
2018-03-16 18:46:08 +00:00
|
|
|
if (!index.isValid()) {
|
2018-01-15 18:18:48 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-03-16 18:46:08 +00:00
|
|
|
// ensure item is visible (expanding its parents as needed).
|
|
|
|
scrollTo(index);
|
2018-01-15 18:18:48 +00:00
|
|
|
}
|
|
|
|
|
2015-03-11 22:22:20 +00:00
|
|
|
// XXX We select the first match, which might not be the desired item.
|
2017-12-07 16:15:30 +00:00
|
|
|
void ProtoTree::goToHfid(int hfid)
|
2015-03-11 22:22:20 +00:00
|
|
|
{
|
2017-12-07 16:15:30 +00:00
|
|
|
QModelIndex index = proto_tree_model_->findFirstHfid(hfid);
|
2018-01-15 18:18:48 +00:00
|
|
|
autoScrollTo(index);
|
2015-03-11 22:22:20 +00:00
|
|
|
}
|
|
|
|
|
2017-12-07 16:15:30 +00:00
|
|
|
void ProtoTree::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
|
2015-09-15 06:46:43 +00:00
|
|
|
{
|
2017-12-07 16:15:30 +00:00
|
|
|
QTreeView::selectionChanged(selected, deselected);
|
2018-01-31 19:26:18 +00:00
|
|
|
if (selected.isEmpty()) {
|
|
|
|
emit fieldSelected(0);
|
|
|
|
return;
|
|
|
|
}
|
2012-01-14 00:16:16 +00:00
|
|
|
|
2017-12-07 16:15:30 +00:00
|
|
|
QModelIndex index = selected.indexes().first();
|
2018-01-30 22:59:35 +00:00
|
|
|
saveSelectedField(index);
|
2018-01-31 19:26:18 +00:00
|
|
|
|
|
|
|
// Find and highlight the protocol bytes. select above won't call
|
|
|
|
// selectionChanged if the current and selected indexes are the same
|
|
|
|
// so we do this here.
|
|
|
|
FieldInformation finfo(proto_tree_model_->protoNodeFromIndex(index).protoNode(), this);
|
|
|
|
if (finfo.isValid()) {
|
|
|
|
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());
|
|
|
|
}
|
|
|
|
emit fieldSelected(&finfo);
|
|
|
|
}
|
2012-01-04 22:13:01 +00:00
|
|
|
}
|
2012-08-14 04:12:56 +00:00
|
|
|
|
2018-01-15 18:18:48 +00:00
|
|
|
void ProtoTree::syncExpanded(const QModelIndex &index) {
|
2017-12-07 16:15:30 +00:00
|
|
|
FieldInformation finfo(proto_tree_model_->protoNodeFromIndex(index).protoNode());
|
|
|
|
if (!finfo.isValid()) return;
|
2012-08-19 20:47:11 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Nodes with "finfo->tree_type" of -1 have no ett_ value, and
|
|
|
|
* are thus presumably leaf nodes and cannot be expanded.
|
|
|
|
*/
|
2017-12-07 16:15:30 +00:00
|
|
|
if (finfo.treeType() != -1) {
|
|
|
|
tree_expanded_set(finfo.treeType(), TRUE);
|
2012-08-19 20:47:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-15 18:18:48 +00:00
|
|
|
void ProtoTree::syncCollapsed(const QModelIndex &index) {
|
2017-12-07 16:15:30 +00:00
|
|
|
FieldInformation finfo(proto_tree_model_->protoNodeFromIndex(index).protoNode());
|
|
|
|
if (!finfo.isValid()) return;
|
2012-08-19 20:47:11 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Nodes with "finfo->tree_type" of -1 have no ett_ value, and
|
|
|
|
* are thus presumably leaf nodes and cannot be collapsed.
|
|
|
|
*/
|
2017-12-07 16:15:30 +00:00
|
|
|
if (finfo.treeType() != -1) {
|
|
|
|
tree_expanded_set(finfo.treeType(), FALSE);
|
2012-08-19 20:47:11 +00:00
|
|
|
}
|
|
|
|
}
|
2012-08-14 04:12:56 +00:00
|
|
|
|
|
|
|
void ProtoTree::expandSubtrees()
|
|
|
|
{
|
2017-12-07 16:15:30 +00:00
|
|
|
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);
|
|
|
|
}
|
2012-08-14 04:12:56 +00:00
|
|
|
}
|
|
|
|
}
|
2017-12-07 16:15:30 +00:00
|
|
|
|
2016-03-10 20:43:59 +00:00
|
|
|
updateContentWidth();
|
2012-08-14 04:12:56 +00:00
|
|
|
}
|
|
|
|
|
2017-12-18 09:25:03 +00:00
|
|
|
void ProtoTree::collapseSubtrees()
|
|
|
|
{
|
|
|
|
if (!selectionModel()->hasSelection()) return;
|
|
|
|
|
|
|
|
QStack<QModelIndex> index_stack;
|
|
|
|
index_stack.push(selectionModel()->selectedIndexes().first());
|
|
|
|
|
|
|
|
while (!index_stack.isEmpty()) {
|
|
|
|
QModelIndex index = index_stack.pop();
|
|
|
|
collapse(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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
updateContentWidth();
|
|
|
|
}
|
|
|
|
|
2012-08-19 20:47:11 +00:00
|
|
|
void ProtoTree::expandAll()
|
|
|
|
{
|
2019-11-17 19:02:20 +00:00
|
|
|
for (int i = 0; i < num_tree_types; i++) {
|
2013-07-07 16:33:49 +00:00
|
|
|
tree_expanded_set(i, TRUE);
|
2012-08-19 20:47:11 +00:00
|
|
|
}
|
2017-12-07 16:15:30 +00:00
|
|
|
QTreeView::expandAll();
|
2016-03-10 20:43:59 +00:00
|
|
|
updateContentWidth();
|
2012-08-19 20:47:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ProtoTree::collapseAll()
|
|
|
|
{
|
2019-11-17 19:02:20 +00:00
|
|
|
for (int i = 0; i < num_tree_types; i++) {
|
2013-07-07 16:33:49 +00:00
|
|
|
tree_expanded_set(i, FALSE);
|
2012-08-19 20:47:11 +00:00
|
|
|
}
|
2017-12-07 16:15:30 +00:00
|
|
|
QTreeView::collapseAll();
|
2016-03-10 20:43:59 +00:00
|
|
|
updateContentWidth();
|
2012-08-19 20:47:11 +00:00
|
|
|
}
|
2012-08-19 23:52:08 +00:00
|
|
|
|
2020-10-26 12:15:42 +00:00
|
|
|
void ProtoTree::itemClicked(const QModelIndex &index)
|
|
|
|
{
|
|
|
|
if (selectionModel()->selectedIndexes().isEmpty()) {
|
|
|
|
emit fieldSelected(0);
|
|
|
|
} else if (index == selectionModel()->selectedIndexes().first()) {
|
2020-03-05 21:56:57 +00:00
|
|
|
FieldInformation finfo(proto_tree_model_->protoNodeFromIndex(index).protoNode());
|
|
|
|
|
|
|
|
if (finfo.isValid()) {
|
|
|
|
emit fieldSelected(&finfo);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-26 12:15:42 +00:00
|
|
|
void ProtoTree::itemDoubleClicked(const QModelIndex &index)
|
|
|
|
{
|
2017-12-07 16:15:30 +00:00
|
|
|
FieldInformation finfo(proto_tree_model_->protoNodeFromIndex(index).protoNode());
|
|
|
|
if (!finfo.isValid()) return;
|
2012-08-19 23:52:08 +00:00
|
|
|
|
2017-12-07 16:15:30 +00:00
|
|
|
if (finfo.headerInfo().type == FT_FRAMENUM) {
|
2015-02-13 16:36:53 +00:00
|
|
|
if (QApplication::queryKeyboardModifiers() & Qt::ShiftModifier) {
|
|
|
|
emit openPacketInNewWindow(true);
|
|
|
|
} else {
|
2019-11-21 14:57:11 +00:00
|
|
|
wsApp->gotoFrame(finfo.fieldInfo()->value.value.uinteger);
|
2015-02-13 16:36:53 +00:00
|
|
|
}
|
2017-12-07 16:15:30 +00:00
|
|
|
} else {
|
|
|
|
QString url = finfo.url();
|
|
|
|
if (!url.isEmpty()) {
|
2021-02-12 19:54:54 +00:00
|
|
|
QApplication::clipboard()->setText(url);
|
|
|
|
QString push_msg = tr("Copied ") + url;
|
|
|
|
wsApp->pushStatus(WiresharkApplication::TemporaryStatus, push_msg);
|
2012-08-19 23:52:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-09-04 08:18:31 +00:00
|
|
|
|
2019-11-21 09:50:41 +00:00
|
|
|
void ProtoTree::selectedFrameChanged(QList<int> frames)
|
2019-11-12 15:39:19 +00:00
|
|
|
{
|
Qt: fix erratic expansion of tree item when switching packets
ProtoTree::setRootNode() is designed to update the model with the new
packet tree, and additionally expand tree items in its view. When the
current selected packet is changed, it must use this method to ensure
that collapsed trees are properly expanded. Fix this regression.
It was not entirely clear that framesSelected can no longer use previous
state, so document it explicitly. Remove the call to QTreeView::reset(),
it ends up calling QAbstractItemView::reset() which touches the
selection model that refers invalidated proto_node memory. The reset
function of the view is automatically called the model is reset, so the
call was not needed anyway.
Test: open test/captures/tls13-rfc8446.pcap, expand TLS, TLS Record, and
select "Content Type". Change from frame 1 to 2, and then 3. Observe
that the expanded state remains constant with no flickering. In frame 3,
observe that the tree remains expanded even if no item is selected.
Change-Id: I0c820711f1a62aa51ac100f8ac5c89265c51eb18
Fixes: v3.3.0rc0-6-gcfee0f8082 ("Qt: Remove frameSelect signal")
Reviewed-on: https://code.wireshark.org/review/35230
Petri-Dish: Peter Wu <peter@lekensteyn.nl>
Tested-by: Petri Dish Buildbot
Reviewed-by: Roland Knall <rknall@gmail.com>
2019-11-27 00:44:55 +00:00
|
|
|
if (frames.count() == 1 && cap_file_ && cap_file_->edt && cap_file_->edt->tree) {
|
|
|
|
setRootNode(cap_file_->edt->tree);
|
|
|
|
} else {
|
|
|
|
// Clear the proto tree contents as they have become invalid.
|
|
|
|
proto_tree_model_->setRootNode(NULL);
|
|
|
|
}
|
2019-11-12 15:39:19 +00:00
|
|
|
}
|
|
|
|
|
2018-03-16 18:46:08 +00:00
|
|
|
// Select a field and bring it into view. Intended to be called by external
|
|
|
|
// components (such as the byte view).
|
2017-12-07 16:15:30 +00:00
|
|
|
void ProtoTree::selectedFieldChanged(FieldInformation *finfo)
|
2016-02-23 08:30:48 +00:00
|
|
|
{
|
2018-01-31 19:26:18 +00:00
|
|
|
if (finfo && finfo->parent() == this) {
|
|
|
|
// We only want inbound signals.
|
2017-12-26 16:03:09 +00:00
|
|
|
return;
|
2016-02-23 08:30:48 +00:00
|
|
|
}
|
2018-01-31 19:26:18 +00:00
|
|
|
|
|
|
|
QModelIndex index = proto_tree_model_->findFieldInformation(finfo);
|
|
|
|
setUpdatesEnabled(false);
|
|
|
|
// The new finfo might match the current index. Clear our selection
|
|
|
|
// so that we force a fresh item selection, so that fieldSelected
|
|
|
|
// will in turn be emitted.
|
|
|
|
selectionModel()->clearSelection();
|
2018-01-15 18:18:48 +00:00
|
|
|
autoScrollTo(index);
|
2018-01-31 19:26:18 +00:00
|
|
|
setUpdatesEnabled(true);
|
2016-02-23 08:30:48 +00:00
|
|
|
}
|
|
|
|
|
2016-03-29 22:47:12 +00:00
|
|
|
// Remember the currently focussed field based on:
|
|
|
|
// - current hf_id (obviously)
|
|
|
|
// - parent items (to avoid selecting a text item in a different tree)
|
2017-12-07 16:15:30 +00:00
|
|
|
// - the row of each item
|
|
|
|
void ProtoTree::saveSelectedField(QModelIndex &index)
|
2016-03-29 22:47:12 +00:00
|
|
|
{
|
2017-12-07 16:15:30 +00:00
|
|
|
selected_hfid_path_.clear();
|
2018-01-30 22:59:35 +00:00
|
|
|
QModelIndex save_index = index;
|
|
|
|
while (save_index.isValid()) {
|
|
|
|
FieldInformation finfo(proto_tree_model_->protoNodeFromIndex(save_index).protoNode());
|
2017-12-07 16:15:30 +00:00
|
|
|
if (!finfo.isValid()) break;
|
2018-01-30 22:59:35 +00:00
|
|
|
selected_hfid_path_.prepend(QPair<int,int>(save_index.row(), finfo.headerInfo().id));
|
|
|
|
save_index = save_index.parent();
|
2017-12-07 16:15:30 +00:00
|
|
|
}
|
2016-03-29 22:47:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Try to focus a tree item which was previously also visible
|
|
|
|
void ProtoTree::restoreSelectedField()
|
|
|
|
{
|
2017-12-07 16:15:30 +00:00
|
|
|
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) {
|
2018-05-08 08:50:00 +00:00
|
|
|
// Did not find the selected hfid path in the selected packet
|
2017-12-07 16:15:30 +00:00
|
|
|
cur_index = QModelIndex();
|
2018-05-08 08:50:00 +00:00
|
|
|
emit fieldSelected(0);
|
2016-03-29 22:47:12 +00:00
|
|
|
break;
|
|
|
|
}
|
2017-12-07 16:15:30 +00:00
|
|
|
}
|
|
|
|
|
2018-01-15 18:18:48 +00:00
|
|
|
autoScrollTo(cur_index);
|
2016-03-29 22:47:12 +00:00
|
|
|
}
|
|
|
|
|
2018-05-25 13:19:46 +00:00
|
|
|
QString ProtoTree::traverseTree(const QModelIndex & travTree, int identLevel) const
|
2017-12-07 16:15:30 +00:00
|
|
|
{
|
2018-05-25 13:19:46 +00:00
|
|
|
QString result = "";
|
|
|
|
|
2019-11-17 19:02:20 +00:00
|
|
|
if (travTree.isValid())
|
2018-05-25 13:19:46 +00:00
|
|
|
{
|
|
|
|
result.append(QString(" ").repeated(identLevel));
|
|
|
|
result.append(travTree.data().toString());
|
|
|
|
result.append("\n");
|
|
|
|
|
|
|
|
/* if the element is expanded, we traverse one level down */
|
2019-11-17 19:02:20 +00:00
|
|
|
if (isExpanded(travTree))
|
2018-05-25 13:19:46 +00:00
|
|
|
{
|
|
|
|
int children = proto_tree_model_->rowCount(travTree);
|
|
|
|
identLevel++;
|
2019-11-17 19:02:20 +00:00
|
|
|
for (int child = 0; child < children; child++)
|
2018-05-25 13:19:46 +00:00
|
|
|
result += traverseTree(proto_tree_model_->index(child, 0, travTree), identLevel);
|
2017-12-07 16:15:30 +00:00
|
|
|
}
|
2018-05-25 13:19:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString ProtoTree::toString(const QModelIndex &start_idx) const
|
|
|
|
{
|
|
|
|
QString tree_string = "";
|
2019-11-17 19:02:20 +00:00
|
|
|
if (start_idx.isValid())
|
2018-05-25 13:19:46 +00:00
|
|
|
tree_string = traverseTree(start_idx, 0);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int children = proto_tree_model_->rowCount();
|
2019-11-17 19:02:20 +00:00
|
|
|
for (int child = 0; child < children; child++)
|
2018-05-25 13:19:46 +00:00
|
|
|
tree_string += traverseTree(proto_tree_model_->index(child, 0, QModelIndex()), 0);
|
|
|
|
}
|
2017-12-07 16:15:30 +00:00
|
|
|
|
|
|
|
return tree_string;
|
|
|
|
}
|
|
|
|
|
2017-11-07 15:51:43 +00:00
|
|
|
void ProtoTree::setCaptureFile(capture_file *cf)
|
|
|
|
{
|
2018-04-26 11:13:54 +00:00
|
|
|
// For use by the main view, set the capture file which will later have a
|
|
|
|
// dissection (EDT) ready.
|
|
|
|
// The packet dialog sets a fixed EDT context and MUST NOT use this.
|
|
|
|
Q_ASSERT(edt_ == NULL);
|
2017-11-07 15:51:43 +00:00
|
|
|
cap_file_ = cf;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ProtoTree::eventFilter(QObject * obj, QEvent * event)
|
|
|
|
{
|
2019-11-17 19:02:20 +00:00
|
|
|
if (event->type() != QEvent::MouseButtonPress && event->type() != QEvent::MouseMove)
|
2017-12-07 16:15:30 +00:00
|
|
|
return QTreeView::eventFilter(obj, event);
|
2017-11-07 15:51:43 +00:00
|
|
|
|
2017-11-12 10:16:34 +00:00
|
|
|
/* Mouse was over scrollbar, ignoring */
|
2019-11-17 19:02:20 +00:00
|
|
|
if (qobject_cast<QScrollBar *>(obj))
|
2017-12-07 16:15:30 +00:00
|
|
|
return QTreeView::eventFilter(obj, event);
|
2017-11-12 10:16:34 +00:00
|
|
|
|
2019-11-17 19:02:20 +00:00
|
|
|
if (event->type() == QEvent::MouseButtonPress)
|
2017-11-07 15:51:43 +00:00
|
|
|
{
|
2017-11-11 01:15:32 +00:00
|
|
|
QMouseEvent * ev = (QMouseEvent *)event;
|
|
|
|
|
2019-11-17 19:02:20 +00:00
|
|
|
if (ev->buttons() & Qt::LeftButton)
|
2017-12-07 16:15:30 +00:00
|
|
|
drag_start_position_ = ev->pos();
|
2017-11-07 15:51:43 +00:00
|
|
|
}
|
2019-11-17 19:02:20 +00:00
|
|
|
else if (event->type() == QEvent::MouseMove)
|
2017-11-07 15:51:43 +00:00
|
|
|
{
|
2017-11-11 01:15:32 +00:00
|
|
|
QMouseEvent * ev = (QMouseEvent *)event;
|
|
|
|
|
2019-11-17 19:02:20 +00:00
|
|
|
if ((ev->buttons() & Qt::LeftButton) && (ev->pos() - drag_start_position_).manhattanLength()
|
2017-11-07 15:51:43 +00:00
|
|
|
> QApplication::startDragDistance())
|
|
|
|
{
|
2017-12-07 16:15:30 +00:00
|
|
|
QModelIndex idx = indexAt(drag_start_position_);
|
|
|
|
FieldInformation finfo(proto_tree_model_->protoNodeFromIndex(idx).protoNode());
|
2019-11-17 19:02:20 +00:00
|
|
|
if (finfo.isValid())
|
2017-11-07 15:51:43 +00:00
|
|
|
{
|
2017-12-07 16:15:30 +00:00
|
|
|
/* 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);
|
|
|
|
|
2018-04-26 11:13:54 +00:00
|
|
|
epan_dissect_t *edt = cap_file_ ? cap_file_->edt : edt_;
|
|
|
|
char *field_filter = proto_construct_match_selected_string(finfo.fieldInfo(), edt);
|
|
|
|
QString filter(field_filter);
|
|
|
|
wmem_free(NULL, field_filter);
|
2017-12-07 16:15:30 +00:00
|
|
|
|
2019-11-17 19:02:20 +00:00
|
|
|
if (filter.length() > 0)
|
2017-11-07 15:51:43 +00:00
|
|
|
{
|
2019-11-03 17:12:12 +00:00
|
|
|
QJsonObject filterData;
|
|
|
|
filterData["filter"] = filter;
|
|
|
|
filterData["name"] = finfo.headerInfo().abbreviation;
|
|
|
|
filterData["description"] = finfo.headerInfo().name;
|
|
|
|
QMimeData * mimeData = new QMimeData();
|
|
|
|
|
|
|
|
mimeData->setData(WiresharkMimeData::DisplayFilterMimeType, QJsonDocument(filterData).toJson());
|
|
|
|
mimeData->setText(toString(idx));
|
2019-06-16 21:42:04 +00:00
|
|
|
|
2017-12-07 16:15:30 +00:00
|
|
|
QDrag * drag = new QDrag(this);
|
2019-11-03 17:12:12 +00:00
|
|
|
drag->setMimeData(mimeData);
|
|
|
|
|
2019-11-17 22:37:45 +00:00
|
|
|
QString lblTxt = QString("%1\n%2").arg(finfo.headerInfo().name, filter);
|
2017-12-07 16:15:30 +00:00
|
|
|
|
2019-11-03 17:12:12 +00:00
|
|
|
DragLabel * content = new DragLabel(lblTxt, this);
|
2017-12-07 16:15:30 +00:00
|
|
|
|
|
|
|
qreal dpr = window()->windowHandle()->devicePixelRatio();
|
|
|
|
QPixmap pixmap(content->size() * dpr);
|
|
|
|
pixmap.setDevicePixelRatio(dpr);
|
|
|
|
content->render(&pixmap);
|
|
|
|
drag->setPixmap(pixmap);
|
|
|
|
|
|
|
|
drag->exec(Qt::CopyAction);
|
|
|
|
|
|
|
|
return true;
|
2017-11-07 15:51:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-07 16:15:30 +00:00
|
|
|
return QTreeView::eventFilter(obj, event);
|
2017-11-07 15:51:43 +00:00
|
|
|
}
|
|
|
|
|
2018-02-06 00:48:13 +00:00
|
|
|
QModelIndex ProtoTree::moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
|
|
|
|
{
|
|
|
|
if (cursorAction == MoveLeft && selectionModel()->hasSelection()) {
|
|
|
|
QModelIndex cur_idx = selectionModel()->selectedIndexes().first();
|
|
|
|
QModelIndex parent = cur_idx.parent();
|
|
|
|
if (!isExpanded(cur_idx) && parent.isValid() && parent != rootIndex()) {
|
|
|
|
return parent;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return QTreeView::moveCursor(cursorAction, modifiers);
|
|
|
|
}
|