Add ServiceResponseTimeDialog.
[metze/wireshark/wip.git] / ui / qt / traffic_table_dialog.cpp
1 /* traffic_table_dialog.cpp
2  *
3  * Wireshark - Network traffic analyzer
4  * By Gerald Combs <gerald@wireshark.org>
5  * Copyright 1998 Gerald Combs
6  *
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.
11  *
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.
16  *
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.
20  */
21
22 #include "traffic_table_dialog.h"
23 #include <ui_traffic_table_dialog.h>
24
25 #include <epan/addr_resolv.h>
26 #include <epan/prefs.h>
27
28 //#include <epan/dissectors/packet-tcp.h>
29
30 #include "ui/recent.h"
31 //#include "ui/tap-tcp-stream.h"
32
33 #include "wireshark_application.h"
34
35 #include <QCheckBox>
36 #include <QClipboard>
37 #include <QContextMenuEvent>
38 #include <QDialogButtonBox>
39 #include <QList>
40 #include <QMap>
41 #include <QMessageBox>
42 #include <QPushButton>
43 #include <QTabWidget>
44 #include <QTreeWidget>
45 #include <QTextStream>
46 #include <QToolButton>
47
48 // To do:
49 // - Add "copy" items to the menu.
50
51 // Bugs:
52 // - Name resolution doesn't do anything if its preference is disabled.
53 // - Columns don't resize correctly.
54 // - Closing the capture file clears conversation data.
55
56 TrafficTableDialog::TrafficTableDialog(QWidget &parent, CaptureFile &cf, const char *filter, const QString &table_name) :
57     WiresharkDialog(parent, cf),
58     ui(new Ui::TrafficTableDialog),
59     cap_file_(cf),
60     file_closed_(false),
61     filter_(filter)
62 {
63     ui->setupUi(this);
64
65     ui->enabledTypesPushButton->setText(tr("%1 Types").arg(table_name));
66     setWindowSubtitle(QString("%1s").arg(table_name));
67
68     // XXX Use recent settings instead
69     resize(parent.width(), parent.height() * 3 / 4);
70
71     QMenu *copy_menu = new QMenu();
72     QAction *ca;
73     copy_bt_ = ui->buttonBox->addButton(tr("Copy"), QDialogButtonBox::ActionRole);
74     ca = copy_menu->addAction(tr("as CSV"));
75     ca->setToolTip(tr("Copy all values of this page to the clipboard in CSV (Comma Separated Values) format."));
76     connect(ca, SIGNAL(triggered()), this, SLOT(copyAsCsv()));
77     ca = copy_menu->addAction(tr("as YAML"));
78     ca->setToolTip(tr("Copy all values of this page to the clipboard in the YAML data serialization format."));
79     connect(ca, SIGNAL(triggered()), this, SLOT(copyAsYaml()));
80     copy_bt_->setMenu(copy_menu);
81
82     ui->enabledTypesPushButton->setMenu(&traffic_type_menu_);
83     ui->nameResolutionCheckBox->setChecked(gbl_resolv_flags.network_name);
84     ui->trafficTableTabWidget->setFocus();
85
86     connect(ui->trafficTableTabWidget, SIGNAL(currentChanged(int)),
87             this, SLOT(itemSelectionChanged()));
88     connect(&cap_file_, SIGNAL(captureFileClosing()), this, SLOT(captureFileClosing()));
89 }
90
91 TrafficTableDialog::~TrafficTableDialog()
92 {
93     delete ui;
94 }
95
96 const QList<int> TrafficTableDialog::defaultProtos() const
97 {
98     // Reasonable defaults?
99     return QList<int>() << proto_get_id_by_filter_name( "tcp" ) << proto_get_id_by_filter_name( "eth" )
100                         << proto_get_id_by_filter_name( "ip" ) << proto_get_id_by_filter_name( "ipv6" )
101                         << proto_get_id_by_filter_name( "udp" );
102 }
103
104 void TrafficTableDialog::fillTypeMenu(QList<int> &enabled_protos)
105 {
106     for (guint i = 0; i < conversation_table_get_num(); i++) {
107         int proto_id = get_conversation_proto_id(get_conversation_table_by_num(i));
108         if (proto_id < 0) {
109             continue;
110         }
111         QString title = proto_get_protocol_short_name(find_protocol_by_id(proto_id));
112
113         QAction *endp_action = new QAction(title, this);
114         endp_action->setData(qVariantFromValue(proto_id));
115         endp_action->setCheckable(true);
116         endp_action->setChecked(enabled_protos.contains(proto_id));
117         connect(endp_action, SIGNAL(triggered()), this, SLOT(toggleTable()));
118         traffic_type_menu_.addAction(endp_action);
119     }
120 }
121
122 QDialogButtonBox *TrafficTableDialog::buttonBox() const
123 {
124     return ui->buttonBox;
125 }
126
127 QTabWidget *TrafficTableDialog::trafficTableTabWidget() const
128 {
129     return ui->trafficTableTabWidget;
130 }
131
132 QCheckBox *TrafficTableDialog::displayFilterCheckBox() const
133 {
134     return ui->displayFilterCheckBox;
135 }
136
137 QCheckBox *TrafficTableDialog::nameResolutionCheckBox() const
138 {
139     return ui->nameResolutionCheckBox;
140 }
141
142 QPushButton *TrafficTableDialog::enabledTypesPushButton() const
143 {
144     return ui->enabledTypesPushButton;
145 }
146
147 void TrafficTableDialog::on_nameResolutionCheckBox_toggled(bool checked)
148 {
149     Q_UNUSED(checked);
150     updateWidgets();
151 }
152
153 void TrafficTableDialog::on_displayFilterCheckBox_toggled(bool checked)
154 {
155     if (!cap_file_.isValid()) {
156         return;
157     }
158
159     QByteArray filter_utf8;
160     const char *filter = NULL;
161     if (checked) {
162         filter = cap_file_.capFile()->dfilter;
163     } else if (!filter_.isEmpty()) {
164         filter_utf8 = filter_.toUtf8();
165         filter = filter_utf8.constData();
166     }
167
168     for (int i = 0; i < ui->trafficTableTabWidget->count(); i++) {
169         TrafficTableTreeWidget *cur_tree = qobject_cast<TrafficTableTreeWidget *>(ui->trafficTableTabWidget->widget(i));
170         set_tap_dfilter(cur_tree->trafficTreeHash(), filter);
171     }
172
173     cap_file_.retapPackets();
174 }
175
176 void TrafficTableDialog::setTabText(QWidget *tree, const QString &text)
177 {
178     // Could use QObject::sender as well
179     int index = ui->trafficTableTabWidget->indexOf(tree);
180     if (index >= 0) {
181         ui->trafficTableTabWidget->setTabText(index, text);
182     }
183 }
184
185 void TrafficTableDialog::toggleTable()
186 {
187     QAction *ca = qobject_cast<QAction *>(QObject::sender());
188     if (!ca) {
189         return;
190     }
191
192     int proto_id = ca->data().value<int>();
193     register_ct_t* table = get_conversation_by_proto_id(proto_id);
194
195     bool new_table = addTrafficTable(table);
196     updateWidgets();
197
198     if (ca->isChecked()) {
199         ui->trafficTableTabWidget->setCurrentWidget(proto_id_to_tree_[proto_id]);
200     }
201
202     if (new_table) {
203         cap_file_.retapPackets();
204     }
205 }
206
207 void TrafficTableDialog::updateWidgets()
208 {
209     QWidget *cur_w = ui->trafficTableTabWidget->currentWidget();
210     ui->trafficTableTabWidget->setUpdatesEnabled(false);
211     ui->trafficTableTabWidget->clear();
212     foreach (QAction *ca, traffic_type_menu_.actions()) {
213         int proto_id = ca->data().value<int>();
214         if (proto_id_to_tree_.contains(proto_id) && ca->isChecked()) {
215             ui->trafficTableTabWidget->addTab(proto_id_to_tree_[proto_id],
216                                               proto_id_to_tree_[proto_id]->trafficTreeTitle());
217             proto_id_to_tree_[proto_id]->setNameResolutionEnabled(ui->nameResolutionCheckBox->isChecked());
218         }
219     }
220     ui->trafficTableTabWidget->setCurrentWidget(cur_w);
221     ui->trafficTableTabWidget->setUpdatesEnabled(true);
222 }
223
224 QList<QVariant> TrafficTableDialog::curTreeRowData(int row) const
225 {
226     TrafficTableTreeWidget *cur_tree = qobject_cast<TrafficTableTreeWidget *>(ui->trafficTableTabWidget->currentWidget());
227     if (!cur_tree) {
228         return QList<QVariant>();
229     }
230
231     return cur_tree->rowData(row);
232 }
233
234 void TrafficTableDialog::copyAsCsv()
235 {
236     QTreeWidget *cur_tree = qobject_cast<QTreeWidget *>(ui->trafficTableTabWidget->currentWidget());
237     if (!cur_tree) {
238         return;
239     }
240
241     QString csv;
242     QTextStream stream(&csv, QIODevice::Text);
243     for (int row = -1; row < cur_tree->topLevelItemCount(); row ++) {
244         QStringList rdsl;
245         foreach (QVariant v, curTreeRowData(row)) {
246             if (!v.isValid()) {
247                 rdsl << "\"\"";
248             } else if ((int) v.type() == (int) QMetaType::QString) {
249                 rdsl << QString("\"%1\"").arg(v.toString());
250             } else {
251                 rdsl << v.toString();
252             }
253         }
254         stream << rdsl.join(",") << endl;
255     }
256     wsApp->clipboard()->setText(stream.readAll());
257 }
258
259 void TrafficTableDialog::copyAsYaml()
260 {
261     QTreeWidget *cur_tree = qobject_cast<QTreeWidget *>(ui->trafficTableTabWidget->currentWidget());
262     if (!cur_tree) {
263         return;
264     }
265
266     QString yaml;
267     QTextStream stream(&yaml, QIODevice::Text);
268     stream << "---" << endl;
269     for (int row = -1; row < cur_tree->topLevelItemCount(); row ++) {
270         stream << "-" << endl;
271         foreach (QVariant v, curTreeRowData(row)) {
272             stream << " - " << v.toString() << endl;
273         }
274     }
275     wsApp->clipboard()->setText(stream.readAll());
276 }
277
278
279 TrafficTableTreeWidget::TrafficTableTreeWidget(QWidget *parent, register_ct_t *table) :
280     QTreeWidget(parent),
281     table_(table),
282     hash_(),
283     resolve_names_(false)
284 {
285     setRootIsDecorated(false);
286     sortByColumn(0, Qt::AscendingOrder);
287
288     connect(wsApp, SIGNAL(addressResolutionChanged()), this, SLOT(updateItems()));
289 }
290
291 TrafficTableTreeWidget::~TrafficTableTreeWidget()
292 {
293     remove_tap_listener(&hash_);
294 }
295
296 QList<QVariant> TrafficTableTreeWidget::rowData(int row) const
297 {
298     QList<QVariant> row_data;
299
300     if (row >= topLevelItemCount()) {
301         return row_data;
302     }
303
304     for (int col = 0; col < columnCount(); col++) {
305         if (isColumnHidden(col)) {
306             continue;
307         }
308         if (row < 0) {
309             row_data << headerItem()->text(col);
310         } else {
311             TrafficTableTreeWidgetItem *ti = static_cast<TrafficTableTreeWidgetItem *>(topLevelItem(row));
312             if (ti) {
313                 row_data << ti->colData(col, resolve_names_);
314             }
315         }
316     }
317     return row_data;
318 }
319
320 void TrafficTableTreeWidget::setNameResolutionEnabled(bool enable)
321 {
322     if (resolve_names_ != enable) {
323         resolve_names_ = enable;
324         updateItems();
325     }
326 }
327
328 void TrafficTableTreeWidget::contextMenuEvent(QContextMenuEvent *event)
329 {
330     bool enable = currentItem() != NULL ? true : false;
331
332     foreach (QMenu *submenu, ctx_menu_.findChildren<QMenu*>()) {
333         submenu->setEnabled(enable);
334     }
335
336     ctx_menu_.exec(event->globalPos());
337 }
338
339 /*
340  * Editor modelines
341  *
342  * Local Variables:
343  * c-basic-offset: 4
344  * tab-width: 8
345  * indent-tabs-mode: nil
346  * End:
347  *
348  * ex: set shiftwidth=4 tabstop=8 expandtab:
349  * :indentSize=4:tabSize=8:noTabs=true:
350  */