1 /* protocol_hierarchy_dialog.cpp
3 * Wireshark - Network traffic analyzer
4 * By Gerald Combs <gerald@wireshark.org>
5 * Copyright 1998 Gerald Combs
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "protocol_hierarchy_dialog.h"
23 #include "ui_protocol_hierarchy_dialog.h"
27 #include "ui/proto_hier_stats.h"
28 #include "ui/utf8_entities.h"
30 #include "color_utils.h"
31 #include "qt_ui_utils.h"
32 #include "wireshark_application.h"
36 #include <QPushButton>
37 #include <QTextStream>
38 #include <QTreeWidgetItemIterator>
41 * @file Protocol Hierarchy Statistics dialog
43 * Displays tree of protocols with various statistics
44 * Allows filtering on tree items
48 // - Make "Copy as YAML" output a tree?
49 // - Add time series data to ph_stats_node_t and draw sparklines.
51 const int protocol_col_ = 0;
52 const int pct_packets_col_ = 1;
53 const int packets_col_ = 2;
54 const int pct_bytes_col_ = 3;
55 const int bytes_col_ = 4;
56 const int bandwidth_col_ = 5;
57 const int end_packets_col_ = 6;
58 const int end_bytes_col_ = 7;
59 const int end_bandwidth_col_ = 8;
61 const int bar_em_width_ = 8;
62 const double bar_blend_ = 0.15;
63 void PercentBarDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
64 const QModelIndex &index) const
66 QStyleOptionViewItemV4 optv4 = option;
67 QStyledItemDelegate::initStyleOption(&optv4, index);
68 double value = index.data(Qt::UserRole).toDouble();
70 QStyledItemDelegate::paint(painter, option, index);
74 if (QApplication::style()->objectName().contains("vista")) {
75 // QWindowsVistaStyle::drawControl does this internally. Unfortunately there
76 // doesn't appear to be a more general way to do this.
77 optv4.palette.setColor(QPalette::All, QPalette::HighlightedText,
78 optv4.palette.color(QPalette::Active, QPalette::Text));
81 QColor bar_color = ColorUtils::alphaBlend(optv4.palette.windowText(),
82 optv4.palette.window(), bar_blend_);
83 QPalette::ColorGroup cg = optv4.state & QStyle::State_Enabled
84 ? QPalette::Normal : QPalette::Disabled;
85 if (cg == QPalette::Normal && !(optv4.state & QStyle::State_Active))
86 cg = QPalette::Inactive;
87 if (optv4.state & QStyle::State_Selected) {
88 painter->setPen(optv4.palette.color(cg, QPalette::HighlightedText));
89 bar_color = ColorUtils::alphaBlend(optv4.palette.color(cg, QPalette::Window),
90 optv4.palette.color(cg, QPalette::Highlight),
93 painter->setPen(optv4.palette.color(cg, QPalette::Text));
96 QRect pct_rect = option.rect;
97 pct_rect.adjust(1, 1, -1, -1);
98 pct_rect.setWidth(((pct_rect.width() * value) / 100.0) + 0.5);
99 painter->fillRect(pct_rect, bar_color);
101 QString pct_str = QString::number(value, 'f', 1);
102 painter->drawText(option.rect, Qt::AlignCenter, pct_str);
107 QSize PercentBarDelegate::sizeHint(const QStyleOptionViewItem &option,
108 const QModelIndex &index) const
110 return QSize(option.fontMetrics.height() * bar_em_width_,
111 QStyledItemDelegate::sizeHint(option, index).height());
114 Q_DECLARE_METATYPE(ph_stats_t*)
116 class ProtocolHierarchyTreeWidgetItem : public QTreeWidgetItem
119 ProtocolHierarchyTreeWidgetItem(QTreeWidgetItem *parent, ph_stats_node_t *ph_stats_node) :
120 QTreeWidgetItem(parent),
124 if (!ph_stats_node) return;
126 filter_name_ = ph_stats_node->hfinfo->abbrev;
127 total_packets_ = ph_stats_node->num_pkts_total;
128 last_packets_ = ph_stats_node->num_pkts_last;
129 total_bytes_ = ph_stats_node->num_bytes_total;
130 last_bytes_ = ph_stats_node->num_bytes_last;
133 ph_stats_t *ph_stats = parent->treeWidget()->invisibleRootItem()->data(0, Qt::UserRole).value<ph_stats_t*>();
135 if (!ph_stats || ph_stats->tot_packets < 1) return;
136 percent_packets_ = total_packets_ * 100.0 / ph_stats->tot_packets;
137 percent_bytes_ = total_bytes_ * 100.0 / ph_stats->tot_bytes;
139 double seconds = ph_stats->last_time - ph_stats->first_time;
142 bits_s_ = total_bytes_ * 8.0 / seconds;
143 end_bits_s_ = last_bytes_ * 8.0 / seconds;
146 setText(protocol_col_, ph_stats_node->hfinfo->name);
147 setData(pct_packets_col_, Qt::UserRole, percent_packets_);
148 setText(packets_col_, QString::number(total_packets_));
149 setData(pct_bytes_col_, Qt::UserRole, percent_bytes_);
150 setText(bytes_col_, QString::number(total_bytes_));
151 setText(bandwidth_col_, seconds > 0.0 ? bits_s_to_qstring(bits_s_) : UTF8_EM_DASH);
152 setText(end_packets_col_, QString::number(last_packets_));
153 setText(end_bytes_col_, QString::number(last_bytes_));
154 setText(end_bandwidth_col_, seconds > 0.0 ? bits_s_to_qstring(end_bits_s_) : UTF8_EM_DASH);
157 // Return a QString, int, double, or invalid QVariant representing the raw column data.
158 QVariant colData(int col) const {
162 case (pct_packets_col_):
163 return percent_packets_;
165 return total_packets_;
166 case (pct_bytes_col_):
167 return percent_bytes_;
170 case (bandwidth_col_):
172 case (end_packets_col_):
173 return last_packets_;
174 case (end_bytes_col_):
176 case (end_bandwidth_col_):
184 bool operator< (const QTreeWidgetItem &other) const
186 const ProtocolHierarchyTreeWidgetItem &other_phtwi = dynamic_cast<const ProtocolHierarchyTreeWidgetItem&>(other);
188 switch (treeWidget()->sortColumn()) {
189 case pct_packets_col_:
190 return percent_packets_ < other_phtwi.percent_packets_;
192 return total_packets_ < other_phtwi.total_packets_;
194 return percent_packets_ < other_phtwi.percent_packets_;
196 return total_bytes_ < other_phtwi.total_bytes_;
198 return bits_s_ < other_phtwi.bits_s_;
199 case end_packets_col_:
200 return last_packets_ < other_phtwi.last_packets_;
202 return last_bytes_ < other_phtwi.last_bytes_;
203 case end_bandwidth_col_:
204 return end_bits_s_ < other_phtwi.end_bits_s_;
209 // Fall back to string comparison
210 return QTreeWidgetItem::operator <(other);
213 const QString filterName(void) { return filter_name_; }
216 QString filter_name_;
217 unsigned total_packets_;
218 unsigned last_packets_;
219 unsigned total_bytes_;
220 unsigned last_bytes_;
222 double percent_packets_;
223 double percent_bytes_;
228 ProtocolHierarchyDialog::ProtocolHierarchyDialog(QWidget &parent, CaptureFile &cf) :
229 WiresharkDialog(parent, cf),
230 ui(new Ui::ProtocolHierarchyDialog)
233 setWindowSubtitle(tr("Protocol Hierarchy Statistics"));
235 // XXX Use recent settings instead
236 resize(parent.width() * 4 / 5, parent.height() * 4 / 5);
238 ui->hierStatsTreeWidget->setItemDelegateForColumn(pct_packets_col_, &percent_bar_delegate_);
239 ui->hierStatsTreeWidget->setItemDelegateForColumn(pct_bytes_col_, &percent_bar_delegate_);
240 ph_stats_t *ph_stats = ph_stats_new(cap_file_.capFile());
242 ui->hierStatsTreeWidget->invisibleRootItem()->setData(0, Qt::UserRole, qVariantFromValue(ph_stats));
243 g_node_children_foreach(ph_stats->stats_tree, G_TRAVERSE_ALL, addTreeNode, ui->hierStatsTreeWidget->invisibleRootItem());
244 ph_stats_free(ph_stats);
247 ui->hierStatsTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
248 connect(ui->hierStatsTreeWidget, SIGNAL(customContextMenuRequested(QPoint)),
249 SLOT(showProtoHierMenu(QPoint)));
251 ui->hierStatsTreeWidget->setSortingEnabled(true);
252 ui->hierStatsTreeWidget->expandAll();
254 for (int i = 0; i < ui->hierStatsTreeWidget->columnCount(); i++) {
255 ui->hierStatsTreeWidget->resizeColumnToContents(i);
260 FilterAction::Action cur_action = FilterAction::ActionApply;
261 submenu = ctx_menu_.addMenu(FilterAction::actionName(cur_action));
262 foreach (FilterAction::ActionType at, FilterAction::actionTypes()) {
263 FilterAction *fa = new FilterAction(submenu, cur_action, at);
264 submenu->addAction(fa);
265 connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered()));
268 cur_action = FilterAction::ActionPrepare;
269 submenu = ctx_menu_.addMenu(FilterAction::actionName(cur_action));
270 foreach (FilterAction::ActionType at, FilterAction::actionTypes()) {
271 FilterAction *fa = new FilterAction(submenu, cur_action, at);
272 submenu->addAction(fa);
273 connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered()));
276 FilterAction *fa = new FilterAction(&ctx_menu_, FilterAction::ActionFind);
277 ctx_menu_.addAction(fa);
278 connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered()));
280 fa = new FilterAction(&ctx_menu_, FilterAction::ActionColorize);
281 ctx_menu_.addAction(fa);
283 ctx_menu_.addSeparator();
284 ctx_menu_.addAction(ui->actionCopyAsCsv);
285 ctx_menu_.addAction(ui->actionCopyAsYaml);
287 copy_button_ = ui->buttonBox->addButton(tr("Copy"), QDialogButtonBox::ApplyRole);
289 QMenu *copy_menu = new QMenu();
291 ca = copy_menu->addAction(tr("as CSV"));
292 ca->setToolTip(ui->actionCopyAsCsv->toolTip());
293 connect(ca, SIGNAL(triggered()), this, SLOT(on_actionCopyAsCsv_triggered()));
294 ca = copy_menu->addAction(tr("as YAML"));
295 ca->setToolTip(ui->actionCopyAsYaml->toolTip());
296 connect(ca, SIGNAL(triggered()), this, SLOT(on_actionCopyAsYaml_triggered()));
297 copy_button_->setMenu(copy_menu);
300 display_filter_ = cap_file_.capFile()->dfilter;
304 ProtocolHierarchyDialog::~ProtocolHierarchyDialog()
309 void ProtocolHierarchyDialog::showProtoHierMenu(QPoint pos)
311 bool enable = ui->hierStatsTreeWidget->currentItem() != NULL && !file_closed_ ? true : false;
313 foreach (QMenu *submenu, ctx_menu_.findChildren<QMenu*>()) {
314 submenu->setEnabled(enable);
316 foreach (QAction *action, ctx_menu_.actions()) {
317 if (action != ui->actionCopyAsCsv && action != ui->actionCopyAsYaml) {
318 action->setEnabled(enable);
322 ctx_menu_.popup(ui->hierStatsTreeWidget->viewport()->mapToGlobal(pos));
325 void ProtocolHierarchyDialog::filterActionTriggered()
327 ProtocolHierarchyTreeWidgetItem *phti = static_cast<ProtocolHierarchyTreeWidgetItem *>(ui->hierStatsTreeWidget->currentItem());
328 FilterAction *fa = qobject_cast<FilterAction *>(QObject::sender());
333 QString filter_name(phti->filterName());
335 emit filterAction(filter_name, fa->action(), fa->actionType());
338 void ProtocolHierarchyDialog::addTreeNode(GNode *node, gpointer data)
340 ph_stats_node_t *stats = (ph_stats_node_t *)node->data;
343 QTreeWidgetItem *parent_ti = static_cast<QTreeWidgetItem *>(data);
344 if (!parent_ti) return;
346 ProtocolHierarchyTreeWidgetItem *phti = new ProtocolHierarchyTreeWidgetItem(parent_ti, stats);
348 g_node_children_foreach(node, G_TRAVERSE_ALL, addTreeNode, phti);
352 void ProtocolHierarchyDialog::updateWidgets()
354 QString hint = "<small><i>";
355 if (display_filter_.isEmpty()) {
356 hint += tr("No display filter.");
358 hint += tr("Display filter: %1").arg(display_filter_);
360 hint += "</i></small>";
361 ui->hintLabel->setText(hint);
364 QList<QVariant> ProtocolHierarchyDialog::protoHierRowData(QTreeWidgetItem *item) const
366 QList<QVariant> row_data;
368 for (int col = 0; col < ui->hierStatsTreeWidget->columnCount(); col++) {
370 row_data << ui->hierStatsTreeWidget->headerItem()->text(col);
372 ProtocolHierarchyTreeWidgetItem *phti = static_cast<ProtocolHierarchyTreeWidgetItem*>(item);
374 row_data << phti->colData(col);
381 void ProtocolHierarchyDialog::on_actionCopyAsCsv_triggered()
384 QTextStream stream(&csv, QIODevice::Text);
385 QTreeWidgetItemIterator iter(ui->hierStatsTreeWidget);
389 QStringList separated_value;
390 QTreeWidgetItem *item = first ? NULL : (*iter);
392 foreach (QVariant v, protoHierRowData(item)) {
394 separated_value << "\"\"";
395 } else if ((int) v.type() == (int) QMetaType::QString) {
396 separated_value << QString("\"%1\"").arg(v.toString());
398 separated_value << v.toString();
401 stream << separated_value.join(",") << endl;
406 wsApp->clipboard()->setText(stream.readAll());
409 void ProtocolHierarchyDialog::on_actionCopyAsYaml_triggered()
412 QTextStream stream(&yaml, QIODevice::Text);
413 QTreeWidgetItemIterator iter(ui->hierStatsTreeWidget);
416 stream << "---" << endl;
418 QTreeWidgetItem *item = first ? NULL : (*iter);
420 stream << "-" << endl;
421 foreach (QVariant v, protoHierRowData(item)) {
422 stream << " - " << v.toString() << endl;
427 wsApp->clipboard()->setText(stream.readAll());
430 void ProtocolHierarchyDialog::on_buttonBox_helpRequested()
432 wsApp->helpTopicAction(HELP_STATS_PROTO_HIERARCHY_DIALOG);
441 * indent-tabs-mode: nil
444 * ex: set shiftwidth=4 tabstop=8 expandtab:
445 * :indentSize=4:tabSize=8:noTabs=true: