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-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>
|
|
|
|
#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)),
|
2016-03-10 20:43:59 +00:00
|
|
|
decode_as_(NULL),
|
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)));
|
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
|
|
|
|
|
|
|
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-07-28 19:24:24 +00:00
|
|
|
void ProtoTree::ctxCopyVisibleItems()
|
|
|
|
{
|
|
|
|
bool selected_tree = false;
|
|
|
|
|
|
|
|
QAction * send = qobject_cast<QAction *>(sender());
|
|
|
|
if ( send && send->property("selected_tree").isValid() )
|
|
|
|
selected_tree = true;
|
|
|
|
|
|
|
|
QString clip = toString();
|
|
|
|
if ( selected_tree && selectionModel()->hasSelection() )
|
|
|
|
clip = toString(selectionModel()->selectedIndexes().first());
|
|
|
|
|
|
|
|
if ( clip.length() > 0 )
|
|
|
|
wsApp->clipboard()->setText(clip);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProtoTree::ctxCopyAsFilter()
|
|
|
|
{
|
|
|
|
QModelIndex idx = selectionModel()->selectedIndexes().first();
|
|
|
|
FieldInformation finfo(proto_tree_model_->protoNodeFromIndex(idx).protoNode());
|
|
|
|
if ( finfo.isValid() )
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
|
|
|
|
if ( filter.length() > 0 )
|
|
|
|
wsApp->clipboard()->setText(filter);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProtoTree::ctxCopySelectedInfo()
|
|
|
|
{
|
|
|
|
int val = -1;
|
|
|
|
QString clip;
|
|
|
|
QAction * send = qobject_cast<QAction *>(sender());
|
|
|
|
if ( send && send->property("field_type").isValid() )
|
|
|
|
val = send->property("field_type").toInt();
|
|
|
|
|
|
|
|
QModelIndex idx = selectionModel()->selectedIndexes().first();
|
|
|
|
FieldInformation finfo(proto_tree_model_->protoNodeFromIndex(idx).protoNode());
|
|
|
|
if ( ! finfo.isValid() )
|
|
|
|
return;
|
|
|
|
|
|
|
|
switch (val)
|
|
|
|
{
|
|
|
|
case ProtoTree::Name:
|
|
|
|
clip.append(finfo.headerInfo().abbreviation);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ProtoTree::Description:
|
|
|
|
if ( finfo.fieldInfo()->rep && strlen(finfo.fieldInfo()->rep->representation) > 0 )
|
|
|
|
clip.append(finfo.fieldInfo()->rep->representation);
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( clip.length() > 0 )
|
|
|
|
wsApp->clipboard()->setText(clip);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProtoTree::ctxOpenUrlWiki()
|
|
|
|
{
|
|
|
|
QUrl url;
|
|
|
|
bool is_field_reference = false;
|
|
|
|
QAction * send = qobject_cast<QAction *>(sender());
|
|
|
|
if ( send && send->property("field_reference").isValid() )
|
|
|
|
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);
|
|
|
|
|
|
|
|
if ( ! is_field_reference )
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
|
|
|
|
url = QString("https://wiki.wireshark.org/Protocols/%1").arg(proto_abbrev);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
url = QString("https://www.wireshark.org/docs/dfref/%1/%2")
|
|
|
|
.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());
|
|
|
|
if ( ! index.isValid() )
|
|
|
|
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;
|
|
|
|
|
|
|
|
if ( is_selected )
|
|
|
|
{
|
|
|
|
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-08-29 12:30:45 +00:00
|
|
|
if ( ! buildForDialog )
|
|
|
|
{
|
|
|
|
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-08-26 13:30:22 +00:00
|
|
|
if ( selectedfilter )
|
|
|
|
wmem_free(Q_NULLPTR, selectedfilter);
|
2015-02-11 23:00:27 +00:00
|
|
|
|
2019-08-29 12:30:45 +00:00
|
|
|
if ( ! buildForDialog )
|
|
|
|
{
|
|
|
|
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"));
|
|
|
|
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()));
|
|
|
|
action->setProperty("selected_tree", qVariantFromValue(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()));
|
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-08-29 12:30:45 +00:00
|
|
|
if ( ! buildForDialog )
|
|
|
|
{
|
|
|
|
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()));
|
|
|
|
action->setProperty("field_reference", qVariantFromValue(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
|
|
|
decode_as_ = window()->findChild<QAction *>("actionAnalyzeDecodeAs");
|
2019-08-26 14:30:05 +00:00
|
|
|
ctx_menu.addAction(decode_as_);
|
2017-12-21 18:59:45 +00:00
|
|
|
|
2019-08-29 12:30:45 +00:00
|
|
|
if ( ! buildForDialog )
|
|
|
|
{
|
|
|
|
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-29 12:30:45 +00:00
|
|
|
decode_as_->setData(QVariant::fromValue(true));
|
|
|
|
}
|
2015-07-29 23:24:39 +00:00
|
|
|
|
2019-08-26 14:30:05 +00:00
|
|
|
ctx_menu.exec(event->globalPos());
|
2019-08-29 12:30:45 +00:00
|
|
|
|
|
|
|
if ( ! buildForDialog )
|
|
|
|
decode_as_->setData(QVariant());
|
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)
|
|
|
|
{
|
|
|
|
mono_font_ = mono_font;
|
|
|
|
setFont(mono_font_);
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
// We track item expansion using proto.c:tree_is_expanded. QTreeView
|
|
|
|
// tracks it using QTreeViewPrivate::expandedIndexes. When we're handed
|
|
|
|
// a new tree, clear expandedIndexes and repopulate it by walking the
|
|
|
|
// tree and calling QTreeView::expand above.
|
2017-12-07 16:15:30 +00:00
|
|
|
void ProtoTree::setRootNode(proto_node *root_node) {
|
2014-09-23 20:35:10 +00:00
|
|
|
setFont(mono_font_);
|
2018-01-14 20:23:07 +00:00
|
|
|
reset(); // clears expandedIndexes.
|
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)
|
|
|
|
{
|
|
|
|
selectionModel()->select(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()
|
|
|
|
{
|
2017-12-07 16:15:30 +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()
|
|
|
|
{
|
2017-12-07 16:15:30 +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
|
|
|
|
2017-12-07 16:15:30 +00:00
|
|
|
void ProtoTree::itemDoubleClicked(const QModelIndex &index) {
|
|
|
|
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 {
|
2017-12-07 16:15:30 +00:00
|
|
|
emit goToPacket(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()) {
|
2012-08-19 23:52:08 +00:00
|
|
|
QDesktopServices::openUrl(QUrl(url));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-09-04 08:18:31 +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 = "";
|
|
|
|
|
|
|
|
if ( travTree.isValid() )
|
|
|
|
{
|
|
|
|
result.append(QString(" ").repeated(identLevel));
|
|
|
|
result.append(travTree.data().toString());
|
|
|
|
result.append("\n");
|
|
|
|
|
|
|
|
/* if the element is expanded, we traverse one level down */
|
|
|
|
if ( isExpanded(travTree) )
|
|
|
|
{
|
|
|
|
int children = proto_tree_model_->rowCount(travTree);
|
|
|
|
identLevel++;
|
|
|
|
for ( int child = 0; child < children; child++ )
|
|
|
|
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 = "";
|
|
|
|
if ( start_idx.isValid() )
|
|
|
|
tree_string = traverseTree(start_idx, 0);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int children = proto_tree_model_->rowCount();
|
|
|
|
for ( int child = 0; child < children; child++ )
|
|
|
|
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)
|
|
|
|
{
|
2018-04-26 11:13:54 +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 */
|
|
|
|
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
|
|
|
|
2017-11-07 15:51:43 +00:00
|
|
|
if ( event->type() == QEvent::MouseButtonPress )
|
|
|
|
{
|
2017-11-11 01:15:32 +00:00
|
|
|
QMouseEvent * ev = (QMouseEvent *)event;
|
|
|
|
|
2017-11-07 15:51:43 +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
|
|
|
}
|
|
|
|
else if ( event->type() == QEvent::MouseMove )
|
|
|
|
{
|
2017-11-11 01:15:32 +00:00
|
|
|
QMouseEvent * ev = (QMouseEvent *)event;
|
|
|
|
|
2017-12-07 16:15:30 +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());
|
|
|
|
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
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
QString lblTxt = QString("%1\n%2").arg(finfo.headerInfo().name, finfo.headerInfo().abbreviation);
|
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);
|
|
|
|
}
|
|
|
|
|
2012-09-04 08:18:31 +00:00
|
|
|
/*
|
|
|
|
* 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:
|
|
|
|
*/
|