0bb669195201fdf39c24cdf2d8f90db37cde9272
[metze/wireshark/wip.git] / ui / qt / stats_tree_dialog.cpp
1 /* stats_tree_dialog.cpp
2  *
3  * $Id$
4  *
5  * Wireshark - Network traffic analyzer
6  * By Gerald Combs <gerald@wireshark.org>
7  * Copyright 1998 Gerald Combs
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22  */
23
24 #include "stats_tree_dialog.h"
25 #include "ui_stats_tree_dialog.h"
26
27 #include "file.h"
28
29 #include "epan/stats_tree_priv.h"
30 #include "wsutil/file_util.h"
31 #include "ui/last_open_dir.h"
32
33 #include "wireshark_application.h"
34
35 #include <QClipboard>
36 #include <QMessageBox>
37 #include <QTreeWidget>
38 #include <QTreeWidgetItemIterator>
39 #include <QFileDialog>
40
41 // The GTK+ counterpart uses tap_param_dlg, which we don't use. If we
42 // need tap parameters we should probably create a TapParameterDialog
43 // class based on QDialog and subclass it here.
44
45 // To do:
46 // - Add help
47
48 #include <QDebug>
49
50 const int item_col_     = 0;
51
52 Q_DECLARE_METATYPE(stat_node *);
53
54 class StatsTreeWidgetItem : public QTreeWidgetItem
55 {
56 public:
57     StatsTreeWidgetItem(int type = Type) : QTreeWidgetItem (type) {}
58     bool operator< (const QTreeWidgetItem &other) const
59     {
60         stat_node *thisnode = data(item_col_, Qt::UserRole).value<stat_node *>();
61         stat_node *othernode = other.data(item_col_, Qt::UserRole).value<stat_node *>();
62         Qt::SortOrder order = treeWidget()->header()->sortIndicatorOrder();
63         int result;
64
65         result = stats_tree_sort_compare(thisnode, othernode, treeWidget()->sortColumn(),
66                                          order==Qt::DescendingOrder);
67         if (order==Qt::DescendingOrder) {
68             result= -result;
69         }
70         return result < 0;
71     }
72 };
73
74 StatsTreeDialog::StatsTreeDialog(QWidget *parent, capture_file *cf, const char *cfg_abbr) :
75     QDialog(parent),
76     ui(new Ui::StatsTreeDialog),
77     st_(NULL),
78     st_cfg_(NULL),
79     cap_file_(cf)
80 {
81     ui->setupUi(this);
82     st_cfg_ = stats_tree_get_cfg_by_abbr(cfg_abbr);
83
84     if (!st_cfg_) {
85         QMessageBox::critical(this, tr("Configuration not found"),
86                              tr("Unable to find configuration for %1.").arg(cfg_abbr));
87         QMetaObject::invokeMethod(this, "reject", Qt::QueuedConnection);
88     }
89
90     ui->statsTreeWidget->addAction(ui->actionCopyToClipboard);
91     ui->statsTreeWidget->addAction(ui->actionSaveAs);
92     ui->statsTreeWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
93
94     QPushButton *button;
95     button = ui->buttonBox->addButton(tr("Copy"), QDialogButtonBox::ActionRole);
96     connect(button, SIGNAL(clicked()), this, SLOT(on_actionCopyToClipboard_triggered()));
97
98     button = ui->buttonBox->addButton(tr("Save as..."), QDialogButtonBox::ActionRole);
99     connect(button, SIGNAL(clicked()), this, SLOT(on_actionSaveAs_triggered()));
100
101     fillTree();
102 }
103
104 StatsTreeDialog::~StatsTreeDialog()
105 {
106     if (st_) {
107         stats_tree_free(st_);
108     }
109     delete ui;
110 }
111
112 void StatsTreeDialog::setCaptureFile(capture_file *cf)
113 {
114     if (!cf) { // We only want to know when the file closes.
115         cap_file_ = NULL;
116         ui->displayFilterLineEdit->setEnabled(false);
117         ui->applyFilterButton->setEnabled(false);
118     }
119 }
120
121 void StatsTreeDialog::fillTree()
122 {
123     GString *error_string;
124     if (!st_cfg_) return;
125
126     gchar* display_name_temp = stats_tree_get_displayname(st_cfg_->name);
127     QString display_name(display_name_temp);
128     g_free(display_name_temp);
129
130     setWindowTitle(display_name + tr(" Stats Tree"));
131
132     if (!cap_file_) return;
133
134     if (st_cfg_->in_use) {
135         QMessageBox::warning(this, tr("%1 already open").arg(display_name),
136                              tr("Each type of tree can only be generated one at at time."));
137         reject();
138     }
139
140     st_cfg_->in_use = TRUE;
141     st_cfg_->pr = &cfg_pr_;
142     cfg_pr_.st_dlg = this;
143
144     if (st_) {
145         stats_tree_free(st_);
146     }
147     st_ = stats_tree_new(st_cfg_, NULL, ui->displayFilterLineEdit->text().toUtf8().constData());
148
149     // Add number of columns for this stats_tree
150     QStringList headerLabels;
151     for (int count = 0; count<st_->num_columns; count++) {
152         headerLabels.push_back(stats_tree_get_column_name(count));
153     }
154     ui->statsTreeWidget->setColumnCount(headerLabels.count());
155     ui->statsTreeWidget->setHeaderLabels(headerLabels);
156     resize(st_->num_columns*80+80, height());
157     for (int count = 0; count<st_->num_columns; count++) {
158         headerLabels.push_back(stats_tree_get_column_name(count));
159     }
160     ui->statsTreeWidget->setSortingEnabled(false);
161
162     error_string = register_tap_listener(st_cfg_->tapname,
163                           st_,
164                           st_->filter,
165                           st_cfg_->flags,
166                           resetTap,
167                           stats_tree_packet,
168                           drawTreeItems);
169     if (error_string) {
170         QMessageBox::critical(this, tr("%1 failed to attach to tap").arg(display_name),
171                              error_string->str);
172         g_string_free(error_string, TRUE);
173         reject();
174     }
175
176     cf_retap_packets(cap_file_);
177     drawTreeItems(st_);
178
179     ui->statsTreeWidget->setSortingEnabled(true);
180     remove_tap_listener(st_);
181
182     st_cfg_->in_use = FALSE;
183     st_cfg_->pr = NULL;
184 }
185
186 void StatsTreeDialog::resetTap(void *st_ptr)
187 {
188     stats_tree *st = (stats_tree *) st_ptr;
189     if (!st || !st->cfg || !st->cfg->pr || !st->cfg->pr->st_dlg) return;
190
191     st->cfg->pr->st_dlg->ui->statsTreeWidget->clear();
192     st->cfg->init(st);
193 }
194
195 // Adds a node to the QTreeWidget
196 // Note: We're passing QTreeWidgetItem pointers as st_node_pres pointers
197 void StatsTreeDialog::setupNode(stat_node* node)
198 {
199     if (!node || !node->st || !node->st->cfg || !node->st->cfg->pr
200             || !node->st->cfg->pr->st_dlg) return;
201     StatsTreeDialog *st_dlg = node->st->cfg->pr->st_dlg;
202
203     QTreeWidgetItem *ti = new StatsTreeWidgetItem(), *parent = NULL;
204
205     ti->setText(item_col_, node->name);
206     ti->setData(item_col_, Qt::UserRole, qVariantFromValue(node));
207     node->pr = (st_node_pres *) ti;
208     if (node->parent && node->parent->pr) {
209         parent = (QTreeWidgetItem *) node->parent->pr;
210         parent->setExpanded(true);
211     }
212     if (parent) {
213         parent->addChild(ti);
214     } else {
215         st_dlg->ui->statsTreeWidget->addTopLevelItem(ti);
216     }
217     st_dlg->ui->statsTreeWidget->resizeColumnToContents(item_col_);
218 }
219
220 void StatsTreeDialog::drawTreeItems(void *st_ptr)
221 {
222     stats_tree *st = (stats_tree *) st_ptr;
223     if (!st || !st->cfg || !st->cfg->pr || !st->cfg->pr->st_dlg) return;
224     StatsTreeDialog *st_dlg = st->cfg->pr->st_dlg;
225     QTreeWidgetItemIterator iter(st_dlg->ui->statsTreeWidget);
226
227     while (*iter) {
228         stat_node *node = (*iter)->data(item_col_, Qt::UserRole).value<stat_node *>();
229         if (node) {
230             gchar    **valstrs = stats_tree_get_values_from_node(node);
231             for (int count = 0; count<st->num_columns; count++) {
232                 (*iter)->setText(count,valstrs[count]);
233                 g_free(valstrs[count]);
234             }
235             if (node->parent==(&st->root)) {
236                 (*iter)->setExpanded(!(node->st_flags&ST_FLG_DEF_NOEXPAND));
237             }
238             g_free(valstrs);
239         }
240         ++iter;
241     }
242     for (int count = 0; count<st->num_columns; count++) {
243         st_dlg->ui->statsTreeWidget->resizeColumnToContents(count);
244     }
245 }
246
247 void StatsTreeDialog::on_applyFilterButton_clicked()
248 {
249     fillTree();
250 }
251
252 void StatsTreeDialog::on_actionCopyToClipboard_triggered()
253 {
254     GString* s= stats_tree_format_as_str(st_ ,ST_FORMAT_PLAIN, ui->statsTreeWidget->sortColumn(),
255                 ui->statsTreeWidget->header()->sortIndicatorOrder()==Qt::DescendingOrder);
256     wsApp->clipboard()->setText(s->str);
257     g_string_free(s,TRUE);
258 }
259
260 void StatsTreeDialog::on_actionSaveAs_triggered()
261 {
262     QString selectedFilter;
263     st_format_type file_type;
264     const char *file_ext;
265     FILE *f;
266     GString *str_tree;
267     bool success= false;
268     int last_errno;
269
270     QFileDialog SaveAsDialog(this, tr("Wireshark: Save stats tree as ..."), get_last_open_dir());
271     SaveAsDialog.setNameFilter(tr("Plain text file (*.txt);;"
272                                     "Comma separated values (*.csv);;"
273                                     "XML document (*.xml);;"
274                                     "YAML document (*.yaml)"));
275     SaveAsDialog.selectNameFilter(tr("Plain text file (*.txt)"));
276     SaveAsDialog.setAcceptMode(QFileDialog::AcceptSave);
277     if (!SaveAsDialog.exec()) {
278         return;
279     }
280     selectedFilter= SaveAsDialog.selectedNameFilter();
281     if (selectedFilter.contains("*.yaml", Qt::CaseInsensitive)) {
282         file_type= ST_FORMAT_YAML;
283         file_ext = ".yaml";
284     }
285     else if (selectedFilter.contains("*.xml", Qt::CaseInsensitive)) {
286         file_type= ST_FORMAT_XML;
287         file_ext = ".xml";
288     }
289     else if (selectedFilter.contains("*.csv", Qt::CaseInsensitive)) {
290         file_type= ST_FORMAT_CSV;
291         file_ext = ".csv";
292     }
293     else {
294         file_type= ST_FORMAT_PLAIN;
295         file_ext = ".txt";
296     }
297
298     // Get selected filename and add extension of necessary
299     QString file_name = SaveAsDialog.selectedFiles()[0];
300     if (!file_name.endsWith(file_ext, Qt::CaseInsensitive)) {
301         file_name.append(file_ext);
302     }
303
304     // produce output in selected format using current sort information
305     str_tree=stats_tree_format_as_str(st_ ,file_type, ui->statsTreeWidget->sortColumn(),
306                 ui->statsTreeWidget->header()->sortIndicatorOrder()==Qt::DescendingOrder);
307
308     // actually save the file
309     f= ws_fopen (file_name.toUtf8().constData(),"w");
310     last_errno= errno;
311     if (f) {
312         if (fputs(str_tree->str, f)!=EOF) {
313             success= true;
314         }
315         last_errno= errno;
316         fclose(f);
317     }
318     if (!success) {
319         QMessageBox::warning(this, tr("Error saving file %1").arg(file_name),
320                              g_strerror (last_errno));
321     }
322
323     g_string_free(str_tree, TRUE);
324 }
325
326 extern "C" {
327 void
328 register_tap_listener_stats_tree_stat(void)
329 {
330
331     stats_tree_presentation(NULL,
332                 StatsTreeDialog::setupNode,
333                 NULL,
334                 NULL,
335                 NULL,
336                 NULL,
337                 NULL,
338                 NULL,
339                 NULL,
340                 NULL);
341 }
342 }
343
344 /*
345  * Editor modelines
346  *
347  * Local Variables:
348  * c-basic-offset: 4
349  * tab-width: 8
350  * indent-tabs-mode: nil
351  * End:
352  *
353  * ex: set shiftwidth=4 tabstop=8 expandtab:
354  * :indentSize=4:tabSize=8:noTabs=true:
355  */