Qt: Show the full file path in the status bar.
[metze/wireshark/wip.git] / ui / qt / capture_file_properties_dialog.cpp
1 /* capture_file_properties_dialog.cpp
2  *
3  * GSoC 2013 - QtShark
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 "capture_file_properties_dialog.h"
25 #include "ui_capture_file_properties_dialog.h"
26
27 #include "summary.h"
28
29 #include "wsutil/str_util.h"
30 #include "wsutil/ws_version_info.h"
31
32 #include "qt_ui_utils.h"
33 #include "wireshark_application.h"
34
35 #include <QDateTime>
36 #include <QPushButton>
37 #include <QTextStream>
38
39 // To do:
40 // - Add file hashes
41 // - Add formats (HTML, plain text, YAML)?
42
43 CaptureFilePropertiesDialog::CaptureFilePropertiesDialog(QWidget &parent, CaptureFile &capture_file) :
44     WiresharkDialog(parent, capture_file),
45     ui(new Ui::CaptureFilePropertiesDialog)
46 {
47     ui->setupUi(this);
48
49     // XXX Use recent settings instead
50     resize(parent.width() * 2 / 3, parent.height());
51
52     QPushButton *button = ui->buttonBox->button(QDialogButtonBox::Reset);
53     if (button) {
54         button->setText(tr("Refresh"));
55     }
56
57     button = ui->buttonBox->button(QDialogButtonBox::Apply);
58     if (button) {
59         button->setText(tr("Copy To Clipboard"));
60     }
61
62     button = ui->buttonBox->button(QDialogButtonBox::Save);
63     if (button) {
64         button->setText(tr("Save Comments"));
65     }
66
67     setWindowSubtitle(tr("Capture File Properties"));
68     updateWidgets();
69 }
70
71 /*
72  * Slots
73  */
74
75 CaptureFilePropertiesDialog::~CaptureFilePropertiesDialog()
76 {
77     delete ui;
78 }
79
80 /**/
81
82 void CaptureFilePropertiesDialog::updateWidgets()
83 {
84     QPushButton *refresh_bt = ui->buttonBox->button(QDialogButtonBox::Reset);
85     QPushButton *save_bt = ui->buttonBox->button(QDialogButtonBox::Save);
86
87     if (file_closed_) {
88         if (refresh_bt) {
89             refresh_bt->setEnabled(false);
90         }
91         ui->commentsTextEdit->setReadOnly(true);
92         if (save_bt) {
93             save_bt->setEnabled(false);
94         }
95         return;
96     }
97
98     bool enable = wtap_dump_can_write(cap_file_.capFile()->linktypes, WTAP_COMMENT_PER_SECTION);
99     save_bt->setEnabled(enable);
100     ui->commentsTextEdit->setEnabled(enable);
101
102     ui->detailsTextEdit->setHtml(summaryToHtml());
103     ui->commentsTextEdit->setText(cf_read_shb_comment(cap_file_.capFile()));
104 }
105
106 QString CaptureFilePropertiesDialog::timeToString(time_t ti_time)
107 {
108     QDateTime date_time = QDateTime::fromTime_t(ti_time);
109     QString time_str = date_time.toLocalTime().toString("yyyy-MM-dd hh:mm:ss");
110     return time_str;
111 }
112
113 QString CaptureFilePropertiesDialog::summaryToHtml()
114 {
115     summary_tally summary;
116     double seconds = 0.0;
117     double disp_seconds = 0.0;
118     double marked_seconds = 0.0;
119
120     memset(&summary, 0, sizeof(summary_tally));
121
122     QString section_tmpl;
123     QString table_begin, table_end;
124     QString table_row_begin, table_ul_row_begin, table_row_end;
125     QString table_vheader_tmpl, table_hheader20_tmpl, table_hheader25_tmpl;
126     QString table_data_tmpl;
127
128     section_tmpl = "<p><strong>%1</strong></p>\n";
129     table_begin = "<p><table>\n";
130     table_end = "</table></p>\n";
131     table_row_begin = "<tr>\n";
132     table_ul_row_begin = "<tr style=\"border-bottom: 1px solid gray;\">\n";
133     table_row_end = "</tr>\n";
134     table_vheader_tmpl = "<td width=\"20%\">%1:</td>"; // <th align="left"> looked odd
135     table_hheader20_tmpl = "<td width=\"20%\"><u>%1</u></td>";
136     table_hheader25_tmpl = "<td width=\"25%\"><u>%1</u></td>";
137     table_data_tmpl = "<td>%1</td>";
138
139     if (!file_closed_) {
140         /* initial computations */
141         summary_fill_in(cap_file_.capFile(), &summary);
142 #ifdef HAVE_LIBPCAP
143         summary_fill_in_capture(cap_file_.capFile(), &global_capture_opts, &summary);
144 #endif
145     }
146
147     seconds = summary.stop_time - summary.start_time;
148     disp_seconds = summary.filtered_stop - summary.filtered_start;
149     marked_seconds = summary.marked_stop - summary.marked_start;
150
151     QString summary_str;
152     QTextStream out(&summary_str);
153     QString unknown = tr("Unknown");
154
155     // File Section
156     out << section_tmpl.arg(tr("File"));
157     out << table_begin;
158
159     out << table_row_begin
160         << table_vheader_tmpl.arg(tr("Name"))
161         << table_data_tmpl.arg(summary.filename)
162         << table_row_end;
163
164     out << table_row_begin
165         << table_vheader_tmpl.arg(tr("Length"))
166         << table_data_tmpl.arg(file_size_to_qstring(summary.file_length))
167         << table_row_end;
168
169     QString format_str = wtap_file_type_subtype_string(summary.file_type);
170     if (summary.iscompressed) {
171         format_str.append(tr(" (gzip compressed)"));
172     }
173     out << table_row_begin
174         << table_vheader_tmpl.arg(tr("Format"))
175         << table_data_tmpl.arg(format_str)
176         << table_row_end;
177
178     QString encaps_str;
179     if (summary.file_encap_type == WTAP_ENCAP_PER_PACKET) {
180         for (guint i = 0; i < summary.packet_encap_types->len; i++)
181         {
182             encaps_str = QString(wtap_encap_string(g_array_index(summary.packet_encap_types, int, i)));
183         }
184     } else {
185         encaps_str = QString(wtap_encap_string(summary.file_encap_type));
186     }
187     out << table_row_begin
188         << table_vheader_tmpl.arg(tr("Encapsulation"))
189         << table_data_tmpl.arg(encaps_str)
190         << table_row_end;
191
192     out << table_end;
193
194     // Time Section
195     if (summary.packet_count_ts == summary.packet_count &&
196             summary.packet_count >= 1)
197     {
198         out << section_tmpl.arg(tr("Time"));
199         out << table_begin;
200
201         // start time
202         out << table_row_begin
203             << table_vheader_tmpl.arg(tr("First packet"))
204             << table_data_tmpl.arg(timeToString((time_t)summary.start_time))
205             << table_row_end;
206
207         // stop time
208         out << table_row_begin
209             << table_vheader_tmpl.arg(tr("Last packet"))
210             << table_data_tmpl.arg(timeToString((time_t)summary.stop_time))
211             << table_row_end;
212
213         // elapsed seconds (capture duration)
214         if (summary.packet_count_ts >= 2)
215         {
216             /* elapsed seconds */
217             QString elapsed_str;
218             unsigned int elapsed_time = (unsigned int)summary.elapsed_time;
219             if (elapsed_time/86400)
220             {
221                 elapsed_str = QString("%1 days ").arg(elapsed_time / 86400);
222             }
223
224             elapsed_str += QString("%1:%2:%3")
225                     .arg(elapsed_time % 86400 / 3600, 2, 10, QChar('0'))
226                     .arg(elapsed_time % 3600 / 60, 2, 10, QChar('0'))
227                     .arg(elapsed_time % 60, 2, 10, QChar('0'));
228             out << table_row_begin
229                 << table_vheader_tmpl.arg(tr("Elapsed"))
230                 << table_data_tmpl.arg(elapsed_str)
231                 << table_row_end;
232         }
233
234         out << table_end;
235     }
236
237     // Capture Section
238     out << section_tmpl.arg(tr("Capture"));
239     out << table_begin;
240
241     QString capture_hardware(unknown);
242     if (summary.shb_hardware && summary.shb_hardware[0] != '\0') {
243         capture_hardware = summary.shb_hardware;
244     }
245     // capture HW
246     out << table_row_begin
247         << table_vheader_tmpl.arg(tr("Hardware"))
248         << table_data_tmpl.arg(capture_hardware)
249         << table_row_end;
250
251     QString capture_os(unknown);
252     if (summary.shb_os && summary.shb_os[0] != '\0') {
253         capture_os = summary.shb_os;
254     }
255     out << table_row_begin
256         << table_vheader_tmpl.arg(tr("OS"))
257         << table_data_tmpl.arg(capture_os)
258         << table_row_end;
259
260     QString capture_app(unknown);
261     if (summary.shb_user_appl && summary.shb_user_appl[0] != '\0') {
262         capture_app = summary.shb_user_appl;
263     }
264     out << table_row_begin
265         << table_vheader_tmpl.arg(tr("Application"))
266         << table_data_tmpl.arg(capture_app)
267         << table_row_end;
268
269     out << table_end;
270
271     // capture interfaces info
272     if (summary.ifaces->len > 0) {
273         out << section_tmpl.arg(tr("Interfaces"));
274         out << table_begin;
275
276         out << table_ul_row_begin
277             << table_hheader20_tmpl.arg(tr("Interface"))
278             << table_hheader20_tmpl.arg(tr("Dropped packets"))
279             << table_hheader20_tmpl.arg(tr("Capture filter"))
280             << table_hheader20_tmpl.arg(tr("Link type"))
281             << table_hheader20_tmpl.arg(tr("Packet size limit"))
282             << table_row_end;
283     }
284
285     for (guint i = 0; i < summary.ifaces->len; i++) {
286         iface_options iface;
287         iface = g_array_index(summary.ifaces, iface_options, i);
288
289         /* interface */
290         QString interface_name(unknown);
291         if (iface.descr) {
292             interface_name = iface.descr;
293         } else if (iface.name)
294         {
295             interface_name = iface.name;
296         }
297
298         /* Dropped count */
299         QString interface_drops(unknown);
300         if (iface.drops_known) {
301             interface_drops = QString("%1 (%2 %)").arg(iface.drops).arg(QString::number(
302                 /* MSVC cannot convert from unsigned __int64 to float, so first convert to signed __int64 */
303                 summary.packet_count ?(100.0 * (gint64)iface.drops)/summary.packet_count : 0.0f, 'g', 1));
304         }
305
306         /* Capture filter */
307         QString interface_cfilter(unknown);
308         if (iface.cfilter && iface.cfilter[0] != '\0') {
309             interface_cfilter = iface.cfilter;
310         } else if (iface.name) {
311             interface_cfilter = QString(tr("none"));
312         }
313
314         QString interface_snaplen = QString(tr("%1 bytes").arg(iface.snap));
315
316         out << table_row_begin
317             << table_data_tmpl.arg(interface_name)
318             << table_data_tmpl.arg(interface_drops)
319             << table_data_tmpl.arg(interface_cfilter)
320             << table_data_tmpl.arg(wtap_encap_string(iface.encap_type))
321             << table_data_tmpl.arg(interface_snaplen)
322             << table_row_end;
323
324     }
325     if (summary.ifaces->len > 0) {
326         out << table_end;
327     }
328
329     // Statistics Section
330     out << section_tmpl.arg(tr("Statistics"));
331     out << table_begin;
332
333     out << table_ul_row_begin
334         << table_hheader25_tmpl.arg(tr("Measurement"))
335         << table_hheader25_tmpl.arg(tr("Captured"))
336         << table_hheader25_tmpl.arg(tr("Displayed"))
337         << table_hheader25_tmpl.arg(tr("Marked"))
338         << table_row_end;
339
340     // TRANSLATOR Abbreviation for "not applicable"
341     QString n_a = tr("N/A");
342     QString captured_str, displayed_str, marked_str;
343
344     // Packets
345     displayed_str = marked_str = n_a;
346     if (summary.filtered_count > 0 && summary.packet_count > 0) {
347             displayed_str = QString("%1 (%2%)")
348             .arg(summary.filtered_count)
349             .arg(100.0 * summary.filtered_count / summary.packet_count, 1, 'f', 1);
350     }
351     if (summary.packet_count > 0 && summary.marked_count > 0) {
352             marked_str = QString("%1 (%2%)")
353             .arg(summary.marked_count)
354             .arg(100.0 * summary.marked_count / summary.packet_count, 1, 'f', 1);
355     }
356
357     out << table_row_begin
358         << table_data_tmpl.arg(tr("Packets"))
359         << table_data_tmpl.arg(summary.packet_count)
360         << table_data_tmpl.arg(displayed_str)
361         << table_data_tmpl.arg(marked_str)
362         << table_row_end;
363
364     // Time between first and last
365     captured_str = displayed_str = marked_str = n_a;
366     if (seconds > 0) {
367             captured_str = QString("%1").arg(seconds, 1, 'f', 3);
368     }
369     if (disp_seconds > 0) {
370             displayed_str = QString("%1").arg(disp_seconds, 1, 'f', 3);
371     }
372     if (marked_seconds > 0) {
373             marked_str = QString("%1").arg(marked_seconds, 1, 'f', 3);
374     }
375     out << table_row_begin
376         << table_data_tmpl.arg(tr("Time span, s"))
377         << table_data_tmpl.arg(captured_str)
378         << table_data_tmpl.arg(displayed_str)
379         << table_data_tmpl.arg(marked_str)
380         << table_row_end;
381
382     // Average packets per second
383     captured_str = displayed_str = marked_str = n_a;
384     if (seconds > 0) {
385             captured_str = QString("%1").arg(summary.packet_count/seconds, 1, 'f', 1);
386     }
387     if (disp_seconds > 0) {
388             displayed_str = QString("%1").arg(summary.filtered_count/disp_seconds, 1, 'f', 1);
389     }
390     if (marked_seconds > 0) {
391             marked_str = QString("%1").arg(summary.marked_count/marked_seconds, 1, 'f', 1);
392     }
393     out << table_row_begin
394         << table_data_tmpl.arg(tr("Average pps"))
395         << table_data_tmpl.arg(captured_str)
396         << table_data_tmpl.arg(displayed_str)
397         << table_data_tmpl.arg(marked_str)
398         << table_row_end;
399
400     // Average packets per second
401     captured_str = displayed_str = marked_str = n_a;
402     if (summary.packet_count > 0) {
403             captured_str = QString("%1").arg(summary.bytes/summary.packet_count + 0.5, 1, 'f', 1);
404     }
405     if (summary.filtered_count > 0) {
406             displayed_str = QString("%1").arg(summary.filtered_bytes/summary.filtered_count + 0.5, 1, 'f', 1);
407     }
408     if (summary.marked_count > 0) {
409             marked_str = QString("%1").arg(summary.marked_bytes/summary.marked_count + 0.5, 1, 'f', 1);
410     }
411     out << table_row_begin
412         << table_data_tmpl.arg(tr("Average packet size, B"))
413         << table_data_tmpl.arg(captured_str)
414         << table_data_tmpl.arg(displayed_str)
415         << table_data_tmpl.arg(marked_str)
416         << table_row_end;
417
418     // Byte count
419     displayed_str = marked_str = "0";
420     if (summary.bytes > 0 && summary.filtered_bytes > 0) {
421         displayed_str = QString("%1 (%2%)")
422                 .arg(summary.filtered_bytes)
423                 .arg(100.0 * summary.filtered_bytes / summary.bytes, 1, 'f', 1);
424     }
425     if (summary.bytes > 0 && summary.marked_bytes > 0) {
426         marked_str = QString("%1 (%2%)")
427                 .arg(summary.marked_bytes)
428                 .arg(100.0 * summary.marked_bytes / summary.bytes, 1, 'f', 1);
429     }
430     out << table_row_begin
431         << table_data_tmpl.arg(tr("Bytes"))
432         << table_data_tmpl.arg(summary.bytes)
433         << table_data_tmpl.arg(displayed_str)
434         << table_data_tmpl.arg(marked_str)
435         << table_row_end;
436
437     // Bytes per second
438     captured_str = displayed_str = marked_str = n_a;
439     if (seconds > 0) {
440         captured_str =
441                 gchar_free_to_qstring(format_size(summary.bytes / seconds, format_size_unit_none|format_size_prefix_si));
442     }
443     if (disp_seconds > 0) {
444         displayed_str =
445                 gchar_free_to_qstring(format_size(summary.filtered_bytes / disp_seconds, format_size_unit_none|format_size_prefix_si));
446     }
447     if (marked_seconds > 0) {
448         marked_str =
449                 gchar_free_to_qstring(format_size(summary.marked_bytes / marked_seconds, format_size_unit_none|format_size_prefix_si));
450     }
451     out << table_row_begin
452         << table_data_tmpl.arg(tr("Average bytes/s"))
453         << table_data_tmpl.arg(captured_str)
454         << table_data_tmpl.arg(displayed_str)
455         << table_data_tmpl.arg(marked_str)
456         << table_row_end;
457
458     // Bits per second
459     captured_str = displayed_str = marked_str = n_a;
460     if (seconds > 0) {
461             captured_str =
462                     gchar_free_to_qstring(format_size(summary.bytes * 8 / seconds, format_size_unit_none|format_size_prefix_si));
463     }
464     if (disp_seconds > 0) {
465             displayed_str =
466                     gchar_free_to_qstring(format_size(summary.filtered_bytes * 8 / disp_seconds, format_size_unit_none|format_size_prefix_si));
467     }
468     if (marked_seconds > 0) {
469             marked_str =
470                     gchar_free_to_qstring(format_size(summary.marked_bytes * 8 / marked_seconds, format_size_unit_none|format_size_prefix_si));
471     }
472     out << table_row_begin
473         << table_data_tmpl.arg(tr("Average bits/s"))
474         << table_data_tmpl.arg(captured_str)
475         << table_data_tmpl.arg(displayed_str)
476         << table_data_tmpl.arg(marked_str)
477         << table_row_end;
478
479     out << table_end;
480
481     return summary_str;
482 }
483
484 void CaptureFilePropertiesDialog::changeEvent(QEvent* event)
485 {
486     if (0 != event)
487     {
488         switch (event->type())
489         {
490         case QEvent::LanguageChange:
491             ui->retranslateUi(this);
492             updateWidgets();
493             break;
494         default:
495             break;
496         }
497     }
498     QDialog::changeEvent(event);
499 }
500
501 void CaptureFilePropertiesDialog::on_buttonBox_helpRequested()
502 {
503     wsApp->helpTopicAction(HELP_STATS_SUMMARY_DIALOG);
504 }
505
506 void CaptureFilePropertiesDialog::on_buttonBox_accepted()
507 {
508     if (file_closed_ || !cap_file_.capFile()->filename) {
509         return;
510     }
511
512     if (wtap_dump_can_write(cap_file_.capFile()->linktypes, WTAP_COMMENT_PER_SECTION))
513     {
514         gchar *str = qstring_strdup(ui->commentsTextEdit->toPlainText());
515         cf_update_capture_comment(cap_file_.capFile(), str);
516         emit captureCommentChanged();
517     }
518 }
519
520 void CaptureFilePropertiesDialog::on_buttonBox_clicked(QAbstractButton *button)
521 {
522     if (button == ui->buttonBox->button(QDialogButtonBox::Apply)) {
523         QClipboard *clipboard = QApplication::clipboard();
524         QString details = tr("Created by Wireshark %1\n\n").arg(get_ws_vcs_version_info());
525         details.append(ui->detailsTextEdit->toPlainText());
526         clipboard->setText(details);
527     } else if (button == ui->buttonBox->button(QDialogButtonBox::Reset)) {
528         updateWidgets();
529     }
530 }
531
532 void CaptureFilePropertiesDialog::on_buttonBox_rejected()
533 {
534     reject();
535 }
536
537 /*
538  * Editor modelines
539  *
540  * Local Variables:
541  * c-basic-offset: 4
542  * tab-width: 8
543  * indent-tabs-mode: nil
544  * End:
545  *
546  * ex: set shiftwidth=4 tabstop=8 expandtab:
547  * :indentSize=4:tabSize=8:noTabs=true:
548  */