Fix comment end after SPDX identifier
[gd/wireshark/.git] / ui / qt / simple_statistics_dialog.cpp
1 /* simple_statistics_dialog.cpp
2  *
3  * Wireshark - Network traffic analyzer
4  * By Gerald Combs <gerald@wireshark.org>
5  * Copyright 1998 Gerald Combs
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  */
9
10 #include "simple_statistics_dialog.h"
11
12 #include "file.h"
13
14 #include "epan/stat_tap_ui.h"
15
16 #include <QTreeWidget>
17
18 #include "wireshark_application.h"
19
20 // To do:
21 // - Hide rows with zero counts.
22
23 static QHash<const QString, stat_tap_table_ui *> cfg_str_to_stu_;
24
25 extern "C" {
26 static void
27 simple_stat_init(const char *args, void*) {
28     QStringList args_l = QString(args).split(',');
29     if (args_l.length() > 1) {
30         QString simple_stat = QString("%1,%2").arg(args_l[0]).arg(args_l[1]);
31         QString filter;
32         if (args_l.length() > 2) {
33             filter = QStringList(args_l.mid(2)).join(",");
34         }
35         wsApp->emitTapParameterSignal(simple_stat, filter, NULL);
36     }
37 }
38 }
39
40 gboolean register_simple_stat_tables(const void *key, void *value, void*) {
41     stat_tap_table_ui *stu = (stat_tap_table_ui*)value;
42
43     cfg_str_to_stu_[stu->cli_string] = stu;
44     TapParameterDialog::registerDialog(
45                 stu->title,
46                 (const char*)key,
47                 stu->group,
48                 simple_stat_init,
49                 SimpleStatisticsDialog::createSimpleStatisticsDialog);
50     return FALSE;
51 }
52
53 enum {
54     simple_row_type_ = 1000
55 };
56
57 class SimpleStatisticsTreeWidgetItem : public QTreeWidgetItem
58 {
59 public:
60     SimpleStatisticsTreeWidgetItem(QTreeWidgetItem *parent, int num_fields, const stat_tap_table_item_type *fields) :
61         QTreeWidgetItem (parent, simple_row_type_),
62         num_fields_(num_fields),
63         fields_(fields)
64     {
65     }
66     void draw() {
67         for (int i = 0; i < num_fields_ && i < treeWidget()->columnCount(); i++) {
68             switch (fields_[i].type) {
69             case TABLE_ITEM_UINT:
70                 setText(i, QString::number(fields_[i].value.uint_value));
71                 break;
72             case TABLE_ITEM_INT:
73                 setText(i, QString::number(fields_[i].value.int_value));
74                 break;
75             case TABLE_ITEM_STRING:
76                 setText(i, fields_[i].value.string_value);
77                 break;
78             case TABLE_ITEM_FLOAT:
79                 setText(i, QString::number(fields_[i].value.float_value, 'f', 6));
80                 break;
81             case TABLE_ITEM_ENUM:
82                 setText(i, QString::number(fields_[i].value.enum_value));
83                 break;
84             default:
85                 break;
86             }
87         }
88     }
89     bool operator< (const QTreeWidgetItem &other) const
90     {
91         int col = treeWidget()->sortColumn();
92         if (other.type() != simple_row_type_ || col >= num_fields_) {
93             return QTreeWidgetItem::operator< (other);
94         }
95         const SimpleStatisticsTreeWidgetItem *other_row = static_cast<const SimpleStatisticsTreeWidgetItem *>(&other);
96         switch (fields_[col].type) {
97         case TABLE_ITEM_UINT:
98             return fields_[col].value.uint_value < other_row->fields_[col].value.uint_value;
99         case TABLE_ITEM_INT:
100             return fields_[col].value.int_value < other_row->fields_[col].value.int_value;
101         case TABLE_ITEM_STRING:
102             return g_strcmp0(fields_[col].value.string_value, other_row->fields_[col].value.string_value) < 0;
103         case TABLE_ITEM_FLOAT:
104             return fields_[col].value.float_value < other_row->fields_[col].value.float_value;
105         case TABLE_ITEM_ENUM:
106             return fields_[col].value.enum_value < other_row->fields_[col].value.enum_value;
107         default:
108             break;
109         }
110
111         return QTreeWidgetItem::operator< (other);
112     }
113     QList<QVariant> rowData() {
114         QList<QVariant> row_data;
115
116         for (int i = 0; i < num_fields_ && i < columnCount(); i++) {
117             switch (fields_[i].type) {
118             case TABLE_ITEM_UINT:
119                 row_data << fields_[i].value.uint_value;
120                 break;
121             case TABLE_ITEM_INT:
122                 row_data << fields_[i].value.int_value;
123                 break;
124             case TABLE_ITEM_STRING:
125                 row_data << fields_[i].value.string_value;
126                 break;
127             case TABLE_ITEM_FLOAT:
128                 row_data << fields_[i].value.float_value;
129                 break;
130             case TABLE_ITEM_ENUM:
131                 row_data << fields_[i].value.enum_value;
132                 break;
133             default:
134                 break;
135             }
136         }
137
138         return row_data;
139     }
140
141 private:
142     const int num_fields_;
143     const stat_tap_table_item_type *fields_;
144 };
145
146 SimpleStatisticsDialog::SimpleStatisticsDialog(QWidget &parent, CaptureFile &cf, struct _stat_tap_table_ui *stu, const QString filter, int help_topic) :
147     TapParameterDialog(parent, cf, help_topic),
148     stu_(stu)
149 {
150     stu->refcount++;
151     setWindowSubtitle(stu_->title);
152     loadGeometry(0, 0, stu_->title);
153
154     QStringList header_labels;
155     for (int col = 0; col < (int) stu_->nfields; col++) {
156         header_labels << stu_->fields[col].column_name;
157     }
158     statsTreeWidget()->setHeaderLabels(header_labels);
159
160     for (int col = 0; col < (int) stu_->nfields; col++) {
161         if (stu_->fields[col].align == TAP_ALIGN_RIGHT) {
162             statsTreeWidget()->headerItem()->setTextAlignment(col, Qt::AlignRight);
163         }
164     }
165
166     setDisplayFilter(filter);
167 }
168
169 TapParameterDialog *SimpleStatisticsDialog::createSimpleStatisticsDialog(QWidget &parent, const QString cfg_str, const QString filter, CaptureFile &cf)
170 {
171     if (!cfg_str_to_stu_.contains(cfg_str)) {
172         // XXX MessageBox?
173         return NULL;
174     }
175
176     stat_tap_table_ui *stu = cfg_str_to_stu_[cfg_str];
177
178     return new SimpleStatisticsDialog(parent, cf, stu, filter);
179 }
180
181 void SimpleStatisticsDialog::addMissingRows(struct _stat_data_t *stat_data)
182 {
183     // Hierarchy:
184     // - tables (GTK+ UI only supports one currently)
185     //   - elements (rows?)
186     //     - fields (columns?)
187     // For multiple table support we might want to add them as subtrees, with
188     // the top-level tree item text set to the column labels for that table.
189
190     // Add any missing tables and rows.
191     for (guint table_idx = 0; table_idx < stat_data->stat_tap_data->tables->len; table_idx++) {
192         stat_tap_table* st_table = g_array_index(stat_data->stat_tap_data->tables, stat_tap_table*, table_idx);
193         QTreeWidgetItem *ti = NULL;
194
195         if ((int) table_idx >= statsTreeWidget()->topLevelItemCount()) {
196             ti = new QTreeWidgetItem(statsTreeWidget());
197             ti->setText(0, st_table->title);
198             ti->setFirstColumnSpanned(true);
199             ti->setExpanded(true);
200         } else {
201             ti = statsTreeWidget()->topLevelItem(table_idx);
202         }
203         for (guint element = ti->childCount(); element < st_table->num_elements; element++) {
204             stat_tap_table_item_type* fields = stat_tap_get_field_data(st_table, element, 0);
205             if (stu_->nfields > 0) {
206                 SimpleStatisticsTreeWidgetItem *ss_ti = new SimpleStatisticsTreeWidgetItem(ti, st_table->num_fields, fields);
207                 for (int col = 0; col < (int) stu_->nfields; col++) {
208                     if (stu_->fields[col].align == TAP_ALIGN_RIGHT) {
209                         ss_ti->setTextAlignment(col, Qt::AlignRight);
210                     }
211                 }
212             }
213         }
214     }
215 }
216
217 void SimpleStatisticsDialog::tapReset(void *sd_ptr)
218 {
219     stat_data_t *sd = (stat_data_t*) sd_ptr;
220     SimpleStatisticsDialog *ss_dlg = static_cast<SimpleStatisticsDialog *>(sd->user_data);
221     if (!ss_dlg) return;
222
223     reset_stat_table(sd->stat_tap_data, NULL, NULL);
224     ss_dlg->statsTreeWidget()->clear();
225 }
226
227 void SimpleStatisticsDialog::tapDraw(void *sd_ptr)
228 {
229     stat_data_t *sd = (stat_data_t*) sd_ptr;
230     SimpleStatisticsDialog *ss_dlg = static_cast<SimpleStatisticsDialog *>(sd->user_data);
231     if (!ss_dlg) return;
232
233     ss_dlg->addMissingRows(sd);
234
235     QTreeWidgetItemIterator it(ss_dlg->statsTreeWidget());
236     while (*it) {
237         if ((*it)->type() == simple_row_type_) {
238             SimpleStatisticsTreeWidgetItem *ss_ti = static_cast<SimpleStatisticsTreeWidgetItem *>((*it));
239             ss_ti->draw();
240         }
241         ++it;
242     }
243
244     for (int i = 0; i < ss_dlg->statsTreeWidget()->columnCount() - 1; i++) {
245         ss_dlg->statsTreeWidget()->resizeColumnToContents(i);
246     }
247 }
248
249 void SimpleStatisticsDialog::fillTree()
250 {
251     stat_data_t stat_data;
252     stat_data.stat_tap_data = stu_;
253     stat_data.user_data = this;
254
255     stu_->stat_tap_init_cb(stu_, NULL, NULL);
256
257     QString display_filter = displayFilter();
258     if (!registerTapListener(stu_->tap_name,
259                              &stat_data,
260                              display_filter.toUtf8().constData(),
261                              0,
262                              tapReset,
263                              stu_->packet_func,
264                              tapDraw)) {
265         free_stat_tables(stu_, NULL, NULL);
266         reject(); // XXX Stay open instead?
267         return;
268     }
269
270     cap_file_.retapPackets();
271
272     // We only have one table. Move its tree items up one level.
273     if (statsTreeWidget()->invisibleRootItem()->childCount() == 1) {
274         statsTreeWidget()->setRootIndex(statsTreeWidget()->model()->index(0, 0));
275     }
276
277     tapDraw(&stat_data);
278
279     removeTapListeners();
280 }
281
282 // This is how an item is represented for exporting.
283 QList<QVariant> SimpleStatisticsDialog::treeItemData(QTreeWidgetItem *it) const
284 {
285     // Cast up to our type.
286     SimpleStatisticsTreeWidgetItem *rit = dynamic_cast<SimpleStatisticsTreeWidgetItem*>(it);
287     if (rit) {
288         return rit->rowData();
289     }
290     else {
291         return QList<QVariant>();
292     }
293 }
294
295
296 SimpleStatisticsDialog::~SimpleStatisticsDialog()
297 {
298     stu_->refcount--;
299     if (stu_->refcount == 0) {
300         if (stu_->tables)
301             free_stat_tables(stu_, NULL, NULL);
302     }
303 }
304
305 /*
306  * Editor modelines
307  *
308  * Local Variables:
309  * c-basic-offset: 4
310  * tab-width: 8
311  * indent-tabs-mode: nil
312  * End:
313  *
314  * ex: set shiftwidth=4 tabstop=8 expandtab:
315  * :indentSize=4:tabSize=8:noTabs=true:
316  */