update epan/dissectors/pidl/drsuapi/drsuapi.idl from samba
[wireshark-sm.git] / ui / qt / protocol_hierarchy_dialog.cpp
blob826838766e59d2db997f1fb9462b079f43fb7d7e
1 /* protocol_hierarchy_dialog.cpp
3 * Wireshark - Network traffic analyzer
4 * By Gerald Combs <gerald@wireshark.org>
5 * Copyright 1998 Gerald Combs
7 * SPDX-License-Identifier: GPL-2.0-or-later
8 */
10 #include "protocol_hierarchy_dialog.h"
11 #include <ui_protocol_hierarchy_dialog.h>
13 #include "cfile.h"
15 #include "ui/proto_hier_stats.h"
17 #include <ui/qt/utils/variant_pointer.h>
19 #include <wsutil/utf8_entities.h>
21 #include <ui/qt/utils/qt_ui_utils.h>
22 #include "main_application.h"
24 #include <epan/proto.h>
25 #include <epan/disabled_protos.h>
27 #include <QClipboard>
28 #include <QPushButton>
29 #include <QTextStream>
30 #include <QTreeWidgetItemIterator>
33 * @file Protocol Hierarchy Statistics dialog
35 * Displays tree of protocols with various statistics
36 * Allows filtering on tree items
39 // To do:
40 // - Make "Copy as YAML" output a tree?
41 // - Add time series data to ph_stats_node_t and draw sparklines.
43 const int protocol_col_ = 0;
44 const int pct_packets_col_ = 1;
45 const int packets_col_ = 2;
46 const int pct_bytes_col_ = 3;
47 const int bytes_col_ = 4;
48 const int bandwidth_col_ = 5;
49 const int end_packets_col_ = 6;
50 const int end_bytes_col_ = 7;
51 const int end_bandwidth_col_ = 8;
52 const int pdus_col_ = 9;
54 struct addTreeNodeData {
55 QSet<QString> *protos;
56 QTreeWidgetItem *widget;
59 class ProtocolHierarchyTreeWidgetItem : public QTreeWidgetItem
61 public:
62 ProtocolHierarchyTreeWidgetItem(QTreeWidgetItem *parent, ph_stats_node_t& ph_stats_node) :
63 QTreeWidgetItem(parent),
64 total_packets_(ph_stats_node.num_pkts_total),
65 total_pdus_(ph_stats_node.num_pdus_total),
66 last_packets_(ph_stats_node.num_pkts_last),
67 total_bytes_(ph_stats_node.num_bytes_total),
68 last_bytes_(ph_stats_node.num_bytes_last),
69 percent_packets_(0),
70 percent_bytes_(0),
71 bits_s_(0.0),
72 end_bits_s_(0.0)
74 filter_name_ = ph_stats_node.hfinfo->abbrev;
76 if (!parent) return;
77 ph_stats_t *ph_stats = VariantPointer<ph_stats_t>::asPtr(parent->treeWidget()->invisibleRootItem()->data(0, Qt::UserRole));
79 if (!ph_stats || ph_stats->tot_packets < 1) return;
80 percent_packets_ = total_packets_ * 100.0 / ph_stats->tot_packets;
81 percent_bytes_ = total_bytes_ * 100.0 / ph_stats->tot_bytes;
83 double seconds = ph_stats->last_time - ph_stats->first_time;
85 if (seconds > 0.0) {
86 bits_s_ = total_bytes_ * 8.0 / seconds;
87 end_bits_s_ = last_bytes_ * 8.0 / seconds;
90 setText(protocol_col_, ph_stats_node.hfinfo->name);
91 setToolTip(protocol_col_, QStringLiteral("%1").arg(ph_stats_node.hfinfo->abbrev));
92 setData(pct_packets_col_, Qt::UserRole, percent_packets_);
93 setText(packets_col_, QString::number(total_packets_));
94 setData(pct_bytes_col_, Qt::UserRole, percent_bytes_);
95 setText(bytes_col_, QString::number(total_bytes_));
96 setText(bandwidth_col_, seconds > 0.0 ? bits_s_to_qstring(bits_s_) : UTF8_EM_DASH);
97 setText(end_packets_col_, QString::number(last_packets_));
98 setText(end_bytes_col_, QString::number(last_bytes_));
99 setText(end_bandwidth_col_, seconds > 0.0 ? bits_s_to_qstring(end_bits_s_) : UTF8_EM_DASH);
100 setText(pdus_col_, QString::number(total_pdus_));
103 // Return a QString, int, double, or invalid QVariant representing the raw column data.
104 QVariant colData(int col) const {
105 switch(col) {
106 case protocol_col_:
107 return text(col);
108 case (pct_packets_col_):
109 return percent_packets_;
110 case (packets_col_):
111 return total_packets_;
112 case (pct_bytes_col_):
113 return percent_bytes_;
114 case (bytes_col_):
115 return total_bytes_;
116 case (bandwidth_col_):
117 return bits_s_;
118 case (end_packets_col_):
119 return last_packets_;
120 case (end_bytes_col_):
121 return last_bytes_;
122 case (end_bandwidth_col_):
123 return end_bits_s_;
124 case (pdus_col_):
125 return total_pdus_;
126 default:
127 break;
129 return QVariant();
132 bool operator< (const QTreeWidgetItem &other) const
134 const ProtocolHierarchyTreeWidgetItem &other_phtwi = dynamic_cast<const ProtocolHierarchyTreeWidgetItem&>(other);
136 switch (treeWidget()->sortColumn()) {
137 case pct_packets_col_:
138 return percent_packets_ < other_phtwi.percent_packets_;
139 case packets_col_:
140 return total_packets_ < other_phtwi.total_packets_;
141 case pct_bytes_col_:
142 return percent_packets_ < other_phtwi.percent_packets_;
143 case bytes_col_:
144 return total_bytes_ < other_phtwi.total_bytes_;
145 case bandwidth_col_:
146 return bits_s_ < other_phtwi.bits_s_;
147 case end_packets_col_:
148 return last_packets_ < other_phtwi.last_packets_;
149 case end_bytes_col_:
150 return last_bytes_ < other_phtwi.last_bytes_;
151 case end_bandwidth_col_:
152 return end_bits_s_ < other_phtwi.end_bits_s_;
153 case pdus_col_:
154 return total_pdus_ < other_phtwi.total_pdus_;
155 default:
156 break;
159 // Fall back to string comparison
160 return QTreeWidgetItem::operator <(other);
163 const QString filterName(void) { return filter_name_; }
165 private:
166 QString filter_name_;
167 unsigned total_packets_;
168 unsigned total_pdus_;
169 unsigned last_packets_;
170 unsigned total_bytes_;
171 unsigned last_bytes_;
173 double percent_packets_;
174 double percent_bytes_;
175 double bits_s_;
176 double end_bits_s_;
179 ProtocolHierarchyDialog::ProtocolHierarchyDialog(QWidget &parent, CaptureFile &cf) :
180 WiresharkDialog(parent, cf),
181 ui(new Ui::ProtocolHierarchyDialog)
183 ui->setupUi(this);
184 loadGeometry(parent.width() * 4 / 5, parent.height() * 4 / 5);
185 setWindowSubtitle(tr("Protocol Hierarchy Statistics"));
187 ui->hierStatsTreeWidget->setItemDelegateForColumn(pct_packets_col_, &percent_bar_delegate_);
188 ui->hierStatsTreeWidget->setItemDelegateForColumn(pct_bytes_col_, &percent_bar_delegate_);
189 ph_stats_t *ph_stats = ph_stats_new(cap_file_.capFile());
190 if (ph_stats) {
191 ui->hierStatsTreeWidget->invisibleRootItem()->setData(0, Qt::UserRole, VariantPointer<ph_stats_t>::asQVariant(ph_stats));
192 addTreeNodeData atnd { &used_protos_, ui->hierStatsTreeWidget->invisibleRootItem() };
193 g_node_children_foreach(ph_stats->stats_tree, G_TRAVERSE_ALL, addTreeNode, (void *)&atnd);
194 ph_stats_free(ph_stats);
197 ui->hierStatsTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
198 connect(ui->hierStatsTreeWidget, SIGNAL(customContextMenuRequested(QPoint)),
199 SLOT(showProtoHierMenu(QPoint)));
201 ui->hierStatsTreeWidget->setSortingEnabled(true);
202 ui->hierStatsTreeWidget->expandAll();
204 for (int i = 0; i < ui->hierStatsTreeWidget->columnCount(); i++) {
205 ui->hierStatsTreeWidget->resizeColumnToContents(i);
208 QMenu *submenu;
210 FilterAction::Action cur_action = FilterAction::ActionApply;
211 submenu = ctx_menu_.addMenu(FilterAction::actionName(cur_action));
212 foreach (FilterAction::ActionType at, FilterAction::actionTypes()) {
213 FilterAction *fa = new FilterAction(submenu, cur_action, at);
214 submenu->addAction(fa);
215 connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered()));
218 cur_action = FilterAction::ActionPrepare;
219 submenu = ctx_menu_.addMenu(FilterAction::actionName(cur_action));
220 foreach (FilterAction::ActionType at, FilterAction::actionTypes()) {
221 FilterAction *fa = new FilterAction(submenu, cur_action, at);
222 submenu->addAction(fa);
223 connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered()));
226 FilterAction *fa = new FilterAction(&ctx_menu_, FilterAction::ActionFind);
227 ctx_menu_.addAction(fa);
228 connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered()));
230 fa = new FilterAction(&ctx_menu_, FilterAction::ActionColorize);
231 ctx_menu_.addAction(fa);
232 connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered()));
234 ctx_menu_.addSeparator();
235 ctx_menu_.addAction(ui->actionCopyAsCsv);
236 ctx_menu_.addAction(ui->actionCopyAsYaml);
238 QPushButton *copy_button = ui->buttonBox->addButton(tr("Copy"), QDialogButtonBox::ApplyRole);
240 QMenu *copy_menu = new QMenu(copy_button);
241 QAction *ca;
242 ca = copy_menu->addAction(tr("as CSV"));
243 ca->setToolTip(ui->actionCopyAsCsv->toolTip());
244 connect(ca, &QAction::triggered, this, &ProtocolHierarchyDialog::on_actionCopyAsCsv_triggered);
245 ca = copy_menu->addAction(tr("as YAML"));
246 ca->setToolTip(ui->actionCopyAsYaml->toolTip());
247 connect(ca, &QAction::triggered, this, &ProtocolHierarchyDialog::on_actionCopyAsYaml_triggered);
248 copy_button->setMenu(copy_menu);
249 connect(ca, SIGNAL(triggered()), this, SLOT(on_actionCopyAsYaml_triggered()));
250 ca = copy_menu->addAction(tr("protocol short names"));
251 ca->setToolTip(ui->actionCopyProtoList->toolTip());
252 connect(ca, SIGNAL(triggered()), this, SLOT(on_actionCopyProtoList_triggered()));
253 copy_button->setMenu(copy_menu);
255 QPushButton *protos_button = ui->buttonBox->addButton(tr("Protocols"), QDialogButtonBox::ApplyRole);
256 QMenu *protos_menu = new QMenu(protos_button);
257 proto_disable_ = protos_menu->addAction(tr("Disable unused"));
258 proto_disable_->setToolTip(ui->actionDisableProtos->toolTip());
259 connect(proto_disable_, SIGNAL(triggered()), this, SLOT(on_actionDisableProtos_triggered()));
260 proto_revert_ = protos_menu->addAction(tr("Revert changes"));
261 proto_revert_->setToolTip(ui->actionRevertProtos->toolTip());
262 connect(proto_revert_, SIGNAL(triggered()), this, SLOT(on_actionRevertProtos_triggered()));
263 protos_button->setMenu(protos_menu);
265 QPushButton *close_bt = ui->buttonBox->button(QDialogButtonBox::Close);
266 if (close_bt) {
267 close_bt->setDefault(true);
270 display_filter_ = cap_file_.capFile()->dfilter;
271 updateWidgets();
274 ProtocolHierarchyDialog::~ProtocolHierarchyDialog()
276 delete ui;
279 void ProtocolHierarchyDialog::showProtoHierMenu(QPoint pos)
281 bool enable = ui->hierStatsTreeWidget->currentItem() != NULL && !file_closed_ ? true : false;
283 foreach (QMenu *submenu, ctx_menu_.findChildren<QMenu*>()) {
284 submenu->setEnabled(enable);
286 foreach (QAction *action, ctx_menu_.actions()) {
287 if (action != ui->actionCopyAsCsv && action != ui->actionCopyAsYaml) {
288 action->setEnabled(enable);
292 ctx_menu_.popup(ui->hierStatsTreeWidget->viewport()->mapToGlobal(pos));
295 void ProtocolHierarchyDialog::filterActionTriggered()
297 ProtocolHierarchyTreeWidgetItem *phti = static_cast<ProtocolHierarchyTreeWidgetItem *>(ui->hierStatsTreeWidget->currentItem());
298 FilterAction *fa = qobject_cast<FilterAction *>(QObject::sender());
300 if (!fa || !phti) {
301 return;
303 QString filter_name(phti->filterName());
305 emit filterAction(filter_name, fa->action(), fa->actionType());
308 void ProtocolHierarchyDialog::addTreeNode(GNode *node, void *data)
310 ph_stats_node_t *stats = (ph_stats_node_t *)node->data;
311 if (!stats) return;
313 addTreeNodeData *atndp = (addTreeNodeData *)data;
314 QTreeWidgetItem *parent_ti = atndp->widget;
315 if (!parent_ti) return;
317 atndp->protos->insert(QString(stats->hfinfo->abbrev));
319 ProtocolHierarchyTreeWidgetItem *phti = new ProtocolHierarchyTreeWidgetItem(parent_ti, *stats);
320 addTreeNodeData atnd { atndp->protos, phti };
322 g_node_children_foreach(node, G_TRAVERSE_ALL, addTreeNode, (void *)&atnd);
326 void ProtocolHierarchyDialog::updateWidgets()
328 QString hint = "<small><i>";
329 if (display_filter_.isEmpty()) {
330 hint += tr("No display filter.");
331 } else {
332 hint += tr("Display filter: %1").arg(display_filter_);
334 hint += "</i></small>";
335 ui->hintLabel->setText(hint);
337 proto_revert_->setEnabled(enabled_protos_unsaved_changes());
339 WiresharkDialog::updateWidgets();
342 QList<QVariant> ProtocolHierarchyDialog::protoHierRowData(QTreeWidgetItem *item) const
344 QList<QVariant> row_data;
346 for (int col = 0; col < ui->hierStatsTreeWidget->columnCount(); col++) {
347 if (!item) {
348 row_data << ui->hierStatsTreeWidget->headerItem()->text(col);
349 } else {
350 ProtocolHierarchyTreeWidgetItem *phti = static_cast<ProtocolHierarchyTreeWidgetItem*>(item);
351 if (phti) {
352 row_data << phti->colData(col);
356 return row_data;
359 void ProtocolHierarchyDialog::on_actionCopyAsCsv_triggered()
361 QString csv;
362 QTextStream stream(&csv, QIODevice::Text);
363 QTreeWidgetItemIterator iter(ui->hierStatsTreeWidget);
364 bool first = true;
366 while (*iter) {
367 QStringList separated_value;
368 QTreeWidgetItem *item = first ? NULL : (*iter);
370 foreach (QVariant v, protoHierRowData(item)) {
371 if (!v.isValid()) {
372 separated_value << "\"\"";
373 } else if (v.userType() == QMetaType::QString) {
374 separated_value << QStringLiteral("\"%1\"").arg(v.toString());
375 } else {
376 separated_value << v.toString();
379 stream << separated_value.join(",") << '\n';
381 if (!first) ++iter;
382 first = false;
384 mainApp->clipboard()->setText(stream.readAll());
387 void ProtocolHierarchyDialog::on_actionCopyAsYaml_triggered()
389 QString yaml;
390 QTextStream stream(&yaml, QIODevice::Text);
391 QTreeWidgetItemIterator iter(ui->hierStatsTreeWidget);
392 bool first = true;
394 stream << "---" << '\n';
395 while (*iter) {
396 QTreeWidgetItem *item = first ? NULL : (*iter);
398 stream << "-" << '\n';
399 foreach (QVariant v, protoHierRowData(item)) {
400 stream << " - " << v.toString() << '\n';
402 if (!first) ++iter;
403 first = false;
405 mainApp->clipboard()->setText(stream.readAll());
408 void ProtocolHierarchyDialog::on_actionCopyProtoList_triggered()
410 QString plist;
411 QTextStream stream(&plist, QIODevice::Text);
412 bool first = true;
413 QSetIterator<QString> iter(used_protos_);
414 while (iter.hasNext()) {
415 if (!first) stream << ',';
416 stream << iter.next();
417 first = false;
419 mainApp->clipboard()->setText(stream.readAll());
422 void ProtocolHierarchyDialog::on_actionDisableProtos_triggered()
424 proto_disable_all();
426 QSetIterator<QString> iter(used_protos_);
427 while (iter.hasNext()) {
428 proto_enable_proto_by_name(iter.next().toStdString().c_str());
430 /* Note that we aren't saving the changes here; they only apply
431 * to the current dissection.
432 * (Though if the user goes to the Enabled Protocols dialog and
433 * makes changes, these changes as well as the user's will be saved.)
435 proto_revert_->setEnabled(enabled_protos_unsaved_changes());
437 QString hint = "<small><i>"
438 + tr("Unused protocols have been disabled.")
439 + "</i></small>";
440 ui->hintLabel->setText(hint);
442 // If we've done everything right, nothing should change.
443 //wsApp->emitAppSignal(WiresharkApplication::PacketDissectionChanged);
446 void ProtocolHierarchyDialog::on_actionRevertProtos_triggered()
448 proto_reenable_all();
449 read_enabled_and_disabled_lists();
451 proto_revert_->setEnabled(enabled_protos_unsaved_changes());
452 QString hint = "<small><i>"
453 + tr("Protocol changes have been reverted.")
454 + "</i></small>";
455 ui->hintLabel->setText(hint);
457 // If we've done everything right, nothing should change.
458 //wsApp->emitAppSignal(WiresharkApplication::PacketDissectionChanged);
461 void ProtocolHierarchyDialog::on_buttonBox_helpRequested()
463 mainApp->helpTopicAction(HELP_STATS_PROTO_HIERARCHY_DIALOG);