The "file types" we have are actually combinations of types and
[metze/wireshark/wip.git] / ui / qt / capture_file_dialog.cpp
1 /* capture_file_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 <wiretap/wtap.h>
25
26 #include "packet_range_group_box.h"
27 #include "capture_file_dialog.h"
28
29 #ifdef Q_OS_WIN
30 #include <windows.h>
31 #include "epan/packet-range.h"
32 #include "ui/win32/file_dlg_win32.h"
33 #else // Q_OS_WIN
34
35 #include <errno.h>
36 #include "file.h"
37 #include "../../epan/addr_resolv.h"
38 #include "../../epan/prefs.h"
39 #include "../../epan/filesystem.h"
40 #include "../../wsutil/nstime.h"
41
42 #include <wireshark_application.h>
43
44 #include <QGridLayout>
45 #include <QHBoxLayout>
46 #include <QVBoxLayout>
47 #include <QLabel>
48 #include <QLineEdit>
49 #include <QCheckBox>
50 #include <QFileInfo>
51 #include <QMessageBox>
52 #include <QSpacerItem>
53 #endif // Q_OS_WIN
54
55 #include <QPushButton>
56
57 #ifdef Q_OS_WIN
58 // All of these routines are required by file_dlg_win32.c.
59 // We don't yet have a good place for them so we'll add them as stubs here.
60
61 extern "C" {
62
63 // From gtk/capture_dlg.[ch]
64 /* capture start confirmed by "Save unsaved capture", so do it now */
65 extern void capture_start_confirmed(void) {
66 }
67
68 // From gtk/drag_and_drop.[ch]
69 /** Open a new file coming from drag and drop.
70  * @param cf_names_freeme the selection data reported from GTK
71  */
72 extern void dnd_open_file_cmd(gchar *cf_names_freeme) {
73     Q_UNUSED(cf_names_freeme);
74 }
75
76 // From gtk/menus.h & main_menubar.c
77 /** User pushed a recent file submenu item.
78  *
79  * @param widget parent widget
80  */
81 extern void menu_open_recent_file_cmd(gpointer action){
82     Q_UNUSED(action)
83 }
84
85 /** One of the name resolution menu items changed. */
86 extern void menu_name_resolution_changed(void) {
87
88 }
89
90 } // extern "C"
91 // End stub routines
92 #endif // Q_OS_WIN
93
94 CaptureFileDialog::CaptureFileDialog(QWidget *parent, capture_file *cf, QString &display_filter) :
95     QFileDialog(parent),
96     cap_file_(cf),
97     display_filter_(display_filter),
98 #if !defined(Q_OS_WIN)
99     default_ft_(-1),
100     save_bt_(NULL),
101     help_topic_(TOPIC_ACTION_NONE)
102 #else
103     file_type_(-1)
104 #endif
105 {
106 #if !defined(Q_OS_WIN)
107     // Add extra widgets
108     // http://qt-project.org/faq/answer/how_can_i_add_widgets_to_my_qfiledialog_instance
109     setOption(QFileDialog::DontUseNativeDialog, true);
110     QGridLayout *fd_grid = qobject_cast<QGridLayout*>(layout());
111     QHBoxLayout *h_box = new QHBoxLayout();
112
113     last_row_ = fd_grid->rowCount();
114
115     fd_grid->addItem(new QSpacerItem(1, 1), last_row_, 0);
116     fd_grid->addLayout(h_box, last_row_, 1);
117     last_row_++;
118
119     // Left and right boxes for controls and preview
120     h_box->addLayout(&left_v_box_);
121     h_box->addLayout(&right_v_box_);
122
123 #else // Q_OS_WIN
124     merge_type_ = 0;
125 #endif // Q_OS_WIN
126 }
127
128 check_savability_t CaptureFileDialog::checkSaveAsWithComments(QWidget *
129 #if defined(Q_OS_WIN)
130         parent
131 #endif
132         , capture_file *cf, int file_type) {
133 #if defined(Q_OS_WIN)
134     if (!parent || !cf)
135         return CANCELLED;
136     return win32_check_save_as_with_comments((HWND)parent->effectiveWinId(), cf, file_type);
137 #else // Q_OS_WIN
138     guint32 comment_types;
139     QMessageBox msg_dialog;
140     int response;
141
142     /* What types of comments do we have? */
143     comment_types = cf_comment_types(cf);
144
145     /* Does the file's format support all the comments we have? */
146     if (wtap_dump_supports_comment_types(file_type, comment_types)) {
147         /* Yes.  Let the save happen; we can save all the comments, so
148            there's no need to delete them. */
149         return SAVE;
150     }
151
152     /* No. Are there formats in which we can write this file that
153        supports all the comments in this file? */
154     if (wtap_dump_can_write(cf->linktypes, comment_types)) {
155         QPushButton *default_button;
156         /* Yes.  Offer the user a choice of "Save in a format that
157            supports comments", "Discard comments and save in the
158            format you selected", or "Cancel", meaning "don't bother
159            saving the file at all". */
160         msg_dialog.setIcon(QMessageBox::Question);
161         msg_dialog.setText(tr("This capture file contains comments."));
162         msg_dialog.setInformativeText(tr("The file format you chose doesn't support comments. "
163                                       "Do you want to save the capture in a format that supports comments "
164                                       "or discard the comments and save in the format you chose?"));
165         msg_dialog.setStandardButtons(QMessageBox::Cancel);
166         // The predefined roles don't really match the tasks at hand...
167         msg_dialog.addButton(tr("Discard comments and save"), QMessageBox::DestructiveRole);
168         default_button = msg_dialog.addButton(tr("Save in another format"), QMessageBox::AcceptRole);
169         msg_dialog.setDefaultButton(default_button);
170     } else {
171         /* No.  Offer the user a choice of "Discard comments and
172            save in the format you selected" or "Cancel". */
173         msg_dialog.setIcon(QMessageBox::Question);
174         msg_dialog.setText(tr("This capture file contains comments."));
175         msg_dialog.setInformativeText(tr("No file format in which it can be saved supports comments. "
176                                       "Do you want to discard the comments and save in the format you chose?"));
177         msg_dialog.setStandardButtons(QMessageBox::Cancel);
178         msg_dialog.addButton(tr("Discard comments and save"), QMessageBox::DestructiveRole);
179         msg_dialog.setDefaultButton(QMessageBox::Cancel);
180     }
181
182     response = msg_dialog.exec();
183
184     switch (response) {
185
186     case QMessageBox::Save:
187       /* OK, the only other format we support is pcap-ng.  Make that
188          the one and only format in the combo box, and return to
189          let the user continue with the dialog.
190
191          XXX - removing all the formats from the combo box will clear
192          the compressed checkbox; get the current value and restore
193          it.
194
195          XXX - we know pcap-ng can be compressed; if we ever end up
196          supporting saving comments in a format that *can't* be
197          compressed, such as NetMon format, we must check this. */
198       /* XXX - need a compressed checkbox here! */
199       return SAVE_IN_ANOTHER_FORMAT;
200
201     case QMessageBox::Discard:
202       /* Save without the comments and, if that succeeds, delete the
203          comments. */
204       return SAVE_WITHOUT_COMMENTS;
205
206     case QMessageBox::Cancel:
207     default:
208       /* Just give up. */
209       break;
210     }
211     return CANCELLED;
212 #endif // Q_OS_WIN
213 }
214
215
216 // You have to use open, merge, saveAs, or exportPackets. We should
217 // probably just make each type a subclass.
218 int CaptureFileDialog::exec() {
219     return QDialog::Rejected;
220 }
221
222
223
224 // Windows
225 #ifdef Q_OS_WIN
226 int CaptureFileDialog::selectedFileType() {
227     return file_type_;
228 }
229
230 bool CaptureFileDialog::isCompressed() {
231     return compressed_;
232 }
233
234 int CaptureFileDialog::open(QString &file_name) {
235     GString *fname = g_string_new(file_name.toUtf8().constData());
236     GString *dfilter = g_string_new(display_filter_.toUtf8().constData());
237     gboolean wof_status;
238
239     // XXX Add a widget->HWND routine to qt_ui_utils and use it instead.
240     wof_status = win32_open_file((HWND)parentWidget()->effectiveWinId(), fname, dfilter);
241     file_name = fname->str;
242     display_filter_ = dfilter->str;
243
244     g_string_free(fname, TRUE);
245     g_string_free(dfilter, TRUE);
246
247     return (int) wof_status;
248 }
249
250 check_savability_t CaptureFileDialog::saveAs(QString &file_name, bool must_support_all_comments) {
251     GString *fname = g_string_new(file_name.toUtf8().constData());
252     gboolean wsf_status;
253
254     wsf_status = win32_save_as_file((HWND)parentWidget()->effectiveWinId(), cap_file_, fname, &file_type_, &compressed_, must_support_all_comments);
255     file_name = fname->str;
256
257     g_string_free(fname, TRUE);
258
259     if (wsf_status) {
260         return win32_check_save_as_with_comments((HWND)parentWidget()->effectiveWinId(), cap_file_, file_type_);
261     }
262
263     return CANCELLED;
264 }
265
266 check_savability_t CaptureFileDialog::exportSelectedPackets(QString &file_name, packet_range_t *range) {
267     GString *fname = g_string_new(file_name.toUtf8().constData());
268     gboolean wespf_status;
269
270     wespf_status = win32_export_specified_packets_file((HWND)parentWidget()->effectiveWinId(), cap_file_, fname, &file_type_, &compressed_, range);
271     file_name = fname->str;
272
273     g_string_free(fname, TRUE);
274
275     if (wespf_status) {
276         return win32_check_save_as_with_comments((HWND)parentWidget()->effectiveWinId(), cap_file_, file_type_);
277     }
278
279     return CANCELLED;
280 }
281
282 int CaptureFileDialog::merge(QString &file_name) {
283     GString *fname = g_string_new(file_name.toUtf8().constData());
284     GString *dfilter = g_string_new(display_filter_.toUtf8().constData());
285     gboolean wmf_status;
286
287     wmf_status = win32_merge_file((HWND)parentWidget()->effectiveWinId(), fname, dfilter, &merge_type_);
288     file_name = fname->str;
289     display_filter_ = dfilter->str;
290
291     g_string_free(fname, TRUE);
292     g_string_free(dfilter, TRUE);
293
294     return (int) wmf_status;
295 }
296
297 int CaptureFileDialog::mergeType() {
298     return merge_type_;
299 }
300
301 #else // not Q_OS_WINDOWS
302 QString CaptureFileDialog::fileExtensionType(int et, bool extension_globs)
303 {
304     QString filter;
305     GSList *extensions_list;
306     GSList *extension;
307
308     filter = wtap_get_file_extension_type_name(et);
309
310     if (!extension_globs) {
311         return filter;
312     }
313
314     filter += " (";
315
316     extensions_list = wtap_get_file_extension_type_extensions(et);
317
318     /* Construct the list of patterns. */
319     for (extension = extensions_list; extension != NULL;
320          extension = g_slist_next(extension)) {
321         if (!filter.endsWith('('))
322             filter += ' ';
323         filter += "*.";
324         filter += (char *)extension->data;
325     }
326     wtap_free_extensions_list(extensions_list);
327     filter += ')';
328     return filter;
329     /* XXX - does QStringList's destructor destroy the strings in the list? */
330 }
331
332 QString CaptureFileDialog::fileType(int ft, bool extension_globs)
333 {
334     QString filter;
335     GSList *extensions_list;
336
337     filter = wtap_file_type_subtype_string(ft);
338
339     if (!extension_globs) {
340         return filter;
341     }
342
343     filter += " (";
344
345     extensions_list = wtap_get_file_extensions_list(ft, TRUE);
346     if (extensions_list == NULL) {
347         /* This file type doesn't have any particular extension
348            conventionally used for it, so we'll just use "*.*"
349            as the pattern; on Windows, that matches all file names
350            - even those with no extension -  so we don't need to
351            worry about compressed file extensions.  (It does not
352            do so on UN*X; the right pattern on UN*X would just
353            be "*".) */
354            filter += "*.*";
355     } else {
356         GSList *extension;
357         /* Construct the list of patterns. */
358         for (extension = extensions_list; extension != NULL;
359              extension = g_slist_next(extension)) {
360             if (!filter.endsWith('('))
361                 filter += ' ';
362             filter += "*.";
363             filter += (char *)extension->data;
364         }
365         wtap_free_extensions_list(extensions_list);
366     }
367     filter += ')';
368     return filter;
369     /* XXX - does QStringList's destructor destroy the strings in the list? */
370 }
371
372 QStringList CaptureFileDialog::buildFileOpenTypeList() {
373     QStringList filters;
374     QString filter, sep;
375     GSList *extensions_list;
376     GSList *extension;
377     int   et;
378
379     /*
380      * Microsoft's UI guidelines say, of the file filters in open and
381      * save dialogs:
382      *
383      *    For meta-filters, remove the file extension list to eliminate
384      *    clutter. Examples: "All files," "All pictures," "All music,"
385      *    and "All videos."
386      *
387      * On both Windows XP and Windows 7, Wordpad doesn't do that, but
388      * Paint does.
389      *
390      * XXX - on Windows, does Qt do that here?  For "All Capture Files",
391      * the filter will be a bit long, so it *really* shouldn't be shown.
392      * What about other platforms?
393      */
394     /* Add the "All Files" entry. */
395     filters << QString(tr("All Files (*.*)"));
396
397     /*
398      * Add an "All Capture Files" entry, with all the extensions we
399      * know about.
400      */
401     filter = "All Capture Files";
402
403     /*
404      * Construct its list of patterns from a list of all extensions
405      * we support.
406      */
407     extensions_list = wtap_get_all_file_extensions_list();
408     sep = " (";
409     for (extension = extensions_list; extension != NULL;
410          extension = g_slist_next(extension)) {
411         filter += sep;
412         filter += "*.";
413         filter += (char *)extension->data;
414         sep = " ";
415     }
416     filter += ")";
417     filters << filter;
418
419     /* Include all the file types Wireshark supports. */
420     for (et = 0; et < wtap_get_num_file_type_extensions(); et++) {
421         filters << fileExtensionType(et);
422     }
423
424     return filters;
425 }
426
427 void CaptureFileDialog::addPreview(QVBoxLayout &v_box) {
428     QGridLayout *preview_grid = new QGridLayout();
429     QLabel *lbl;
430
431     preview_labels_.clear();
432     v_box.addLayout(preview_grid);
433
434     preview_grid->setColumnStretch(0, 0);
435     preview_grid->setColumnStretch(1, 10);
436
437     lbl = new QLabel(tr("Format:"));
438     preview_grid->addWidget(lbl, 0, 0);
439     preview_grid->addWidget(&preview_format_, 0, 1);
440     preview_labels_ << lbl << &preview_format_;
441
442     lbl = new QLabel(tr("Size:"));
443     preview_grid->addWidget(lbl, 1, 0);
444     preview_grid->addWidget(&preview_size_, 1, 1);
445     preview_labels_ << lbl << &preview_size_;
446
447     lbl = new QLabel(tr("Packets:"));
448     preview_grid->addWidget(lbl, 2, 0);
449     preview_grid->addWidget(&preview_packets_, 2, 1);
450     preview_labels_ << lbl << &preview_packets_;
451
452     lbl = new QLabel(tr("First Packet:"));
453     preview_grid->addWidget(lbl, 3, 0);
454     preview_grid->addWidget(&preview_first_, 3, 1);
455     preview_labels_ << lbl << &preview_first_;
456
457     lbl = new QLabel(tr("Elapsed Time:"));
458     preview_grid->addWidget(lbl, 4, 0);
459     preview_grid->addWidget(&preview_elapsed_, 4, 1);
460     preview_labels_ << lbl << &preview_elapsed_;
461
462     connect(this, SIGNAL(currentChanged(const QString &)), this, SLOT(preview(const QString &)));
463
464     preview("");
465 }
466
467 void CaptureFileDialog::addMergeControls(QVBoxLayout &v_box) {
468
469     merge_prepend_.setText(tr("Prepend packets"));
470     merge_prepend_.setToolTip(tr("Insert packets from the selected file before the current file. Packet timestamps will be ignored."));
471     v_box.addWidget(&merge_prepend_, 0, Qt::AlignTop);
472
473     merge_chrono_.setText(tr("Merge chronologically"));
474     merge_chrono_.setToolTip(tr("Insert packets in chronological order."));
475     merge_chrono_.setChecked(true);
476     v_box.addWidget(&merge_chrono_, 0, Qt::AlignTop);
477
478     merge_append_.setText(tr("Append packets"));
479     merge_append_.setToolTip(tr("Insert packets from the selected file after the current file. Packet timestamps will be ignored."));
480     v_box.addWidget(&merge_append_, 0, Qt::AlignTop);
481 }
482
483 int CaptureFileDialog::selectedFileType() {
484     return type_hash_.value(selectedNameFilter(), -1);
485 }
486
487 bool CaptureFileDialog::isCompressed() {
488     return compress_.isChecked();
489 }
490
491 void CaptureFileDialog::addDisplayFilterEdit() {
492     QGridLayout *fd_grid = qobject_cast<QGridLayout*>(layout());
493
494     fd_grid->addWidget(new QLabel(tr("Display Filter:")), last_row_, 0);
495
496     display_filter_edit_ = new DisplayFilterEdit(this, true);
497     display_filter_edit_->setText(display_filter_);
498     fd_grid->addWidget(display_filter_edit_, last_row_, 1);
499     last_row_++;
500 }
501
502 void CaptureFileDialog::addResolutionControls(QVBoxLayout &v_box) {
503     mac_res_.setText(tr("&MAC name resolution"));
504     mac_res_.setChecked(gbl_resolv_flags.mac_name);
505     v_box.addWidget(&mac_res_, 0, Qt::AlignTop);
506
507     transport_res_.setText(tr("&Transport name resolution"));
508     transport_res_.setChecked(gbl_resolv_flags.transport_name);
509     v_box.addWidget(&transport_res_, 0, Qt::AlignTop);
510
511     network_res_.setText(tr("&Network name resolution"));
512     network_res_.setChecked(gbl_resolv_flags.network_name);
513     v_box.addWidget(&network_res_, 0, Qt::AlignTop);
514
515     external_res_.setText(tr("&External name resolver"));
516     external_res_.setChecked(gbl_resolv_flags.use_external_net_name_resolver);
517     v_box.addWidget(&external_res_, 0, Qt::AlignTop);
518 }
519
520 void CaptureFileDialog::addGzipControls(QVBoxLayout &v_box) {
521     compress_.setText(tr("Compress with g&zip"));
522     if (cap_file_->iscompressed && wtap_dump_can_compress(default_ft_)) {
523         compress_.setChecked(true);
524     } else {
525         compress_.setChecked(false);
526     }
527     v_box.addWidget(&compress_, 0, Qt::AlignTop);
528
529 }
530
531 void CaptureFileDialog::addRangeControls(QVBoxLayout &v_box, packet_range_t *range) {
532     packet_range_group_box_.initRange(range);
533     v_box.addWidget(&packet_range_group_box_, 0, Qt::AlignTop);
534 }
535
536 QDialogButtonBox *CaptureFileDialog::addHelpButton(topic_action_e help_topic)
537 {
538     // This doesn't appear to be documented anywhere but it seems pretty obvious
539     // and it works.
540     QDialogButtonBox *button_box = findChild<QDialogButtonBox *>();
541
542     help_topic_ = help_topic;
543
544     if (button_box) {
545         button_box->addButton(QDialogButtonBox::Help);
546         connect(button_box, SIGNAL(helpRequested()), this, SLOT(on_buttonBox_helpRequested()));
547     }
548     return button_box;
549 }
550
551 int CaptureFileDialog::open(QString &file_name) {
552     setWindowTitle(tr("Wireshark: Open Capture File"));
553     setNameFilters(buildFileOpenTypeList());
554     setFileMode(QFileDialog::ExistingFile);
555
556     addDisplayFilterEdit();
557     addResolutionControls(left_v_box_);
558     addPreview(right_v_box_);
559     addHelpButton(HELP_OPEN_DIALOG);
560
561     // Grow the dialog to account for the extra widgets.
562     resize(width(), height() + left_v_box_.minimumSize().height() + display_filter_edit_->minimumSize().height());
563
564     display_filter_.clear();
565
566     if (!file_name.isEmpty()) {
567         selectFile(file_name);
568     }
569
570     if (QFileDialog::exec() && selectedFiles().length() > 0) {
571         file_name = selectedFiles()[0];
572         display_filter_.append(display_filter_edit_->text());
573
574         gbl_resolv_flags.mac_name = mac_res_.isChecked();
575         gbl_resolv_flags.transport_name = transport_res_.isChecked();
576         gbl_resolv_flags.network_name = network_res_.isChecked();
577         gbl_resolv_flags.use_external_net_name_resolver = external_res_.isChecked();
578
579         return QDialog::Accepted;
580     } else {
581         return QDialog::Rejected;
582     }
583 }
584
585 check_savability_t CaptureFileDialog::saveAs(QString &file_name, bool must_support_all_comments) {
586     setWindowTitle(tr("Wireshark: Save Capture File As"));
587     // XXX There doesn't appear to be a way to use setNameFilters without restricting
588     // what the user can select. We might want to use our own combobox instead and
589     // let the user select anything.
590     setNameFilters(buildFileSaveAsTypeList(must_support_all_comments));
591     setAcceptMode(QFileDialog::AcceptSave);
592     setLabelText(FileType, tr("Save as:"));
593
594     addGzipControls(left_v_box_);
595     addHelpButton(HELP_SAVE_DIALOG);
596
597     // Grow the dialog to account for the extra widgets.
598     resize(width(), height() + left_v_box_.minimumSize().height());
599
600     if (!file_name.isEmpty()) {
601         selectFile(file_name);
602     }
603
604     if (QFileDialog::exec() && selectedFiles().length() > 0) {
605         file_name = selectedFiles()[0];
606         return checkSaveAsWithComments(this, cap_file_, selectedFileType());
607     }
608     return CANCELLED;
609 }
610
611 check_savability_t CaptureFileDialog::exportSelectedPackets(QString &file_name, packet_range_t *range) {
612     QDialogButtonBox *button_box;
613
614     setWindowTitle(tr("Wireshark: Export Specified Packets"));
615     // XXX See comment in ::saveAs regarding setNameFilters
616     setNameFilters(buildFileSaveAsTypeList(false));
617     setAcceptMode(QFileDialog::AcceptSave);
618     setLabelText(FileType, tr("Export as:"));
619
620     addRangeControls(left_v_box_, range);
621     addGzipControls(right_v_box_);
622     button_box = addHelpButton(HELP_EXPORT_FILE_DIALOG);
623
624     if (button_box) {
625         save_bt_ = button_box->button(QDialogButtonBox::Save);
626         if (save_bt_) {
627             connect(&packet_range_group_box_, SIGNAL(validityChanged(bool)),
628                     save_bt_, SLOT(setEnabled(bool)));
629         }
630     }
631
632     // Grow the dialog to account for the extra widgets.
633     resize(width(), height() + (packet_range_group_box_.height() * 2 / 3));
634
635     if (!file_name.isEmpty()) {
636         selectFile(file_name);
637     }
638
639     if (QFileDialog::exec() && selectedFiles().length() > 0) {
640         file_name = selectedFiles()[0];
641         return checkSaveAsWithComments(this, cap_file_, selectedFileType());
642     }
643     return CANCELLED;
644 }
645
646 int CaptureFileDialog::merge(QString &file_name) {
647     setWindowTitle(tr("Wireshark: Merge Capture File"));
648     setNameFilters(buildFileOpenTypeList());
649     setFileMode(QFileDialog::ExistingFile);
650
651     addDisplayFilterEdit();
652     addMergeControls(left_v_box_);
653     addPreview(right_v_box_);
654     addHelpButton(HELP_MERGE_DIALOG);
655
656     file_name.clear();
657     display_filter_.clear();
658
659     // Grow the dialog to account for the extra widgets.
660     resize(width(), height() + right_v_box_.minimumSize().height() + display_filter_edit_->minimumSize().height());
661
662     if (QFileDialog::exec() && selectedFiles().length() > 0) {
663         file_name.append(selectedFiles()[0]);
664         display_filter_.append(display_filter_edit_->text());
665
666         return QDialog::Accepted;
667     } else {
668         return QDialog::Rejected;
669     }
670 }
671
672 QStringList CaptureFileDialog::buildFileSaveAsTypeList(bool must_support_all_comments) {
673     QStringList filters;
674     guint32 required_comment_types;
675     GArray *savable_file_types_subtypes;
676     guint i;
677
678     type_hash_.clear();
679
680     /* What types of comments do we have to support? */
681     if (must_support_all_comments)
682         required_comment_types = cf_comment_types(cap_file_); /* all the ones the file has */
683     else
684         required_comment_types = 0; /* none of them */
685
686   /* What types of file can we save this file as? */
687     savable_file_types_subtypes = wtap_get_savable_file_types_subtypes(cap_file_->cd_t,
688                                                                        cap_file_->linktypes,
689                                                                        required_comment_types);
690
691     if (savable_file_types_subtypes != NULL) {
692         QString file_type;
693         int ft;
694         /* OK, we have at least one file type we can save this file as.
695            (If we didn't, we shouldn't have gotten here in the first
696            place.)  Add them all to the combo box.  */
697         for (i = 0; i < savable_file_types_subtypes->len; i++) {
698             ft = g_array_index(savable_file_types_subtypes, int, i);
699             if (default_ft_ < 1)
700                 default_ft_ = ft; /* first file type is the default */
701             file_type = fileType(ft);
702             filters << file_type;
703             type_hash_[file_type] = ft;
704         }
705         g_array_free(savable_file_types_subtypes, TRUE);
706     }
707
708     return filters;
709 }
710
711 int CaptureFileDialog::mergeType() {
712     if (merge_prepend_.isChecked())
713         return -1;
714     else if (merge_append_.isChecked())
715         return 1;
716
717     return 0;
718 }
719
720 // Slots
721
722
723
724 /* do a preview run on the currently selected capture file */
725 void CaptureFileDialog::preview(const QString & path)
726 {
727     wtap        *wth;
728     int          err = 0;
729     gchar       *err_info;
730     gint64       data_offset;
731     const struct wtap_pkthdr *phdr;
732     double       start_time = 0; /* seconds, with nsec resolution */
733     double       stop_time = 0;  /* seconds, with nsec resolution */
734     double       cur_time;
735     unsigned int packets = 0;
736     bool         timed_out = FALSE;
737     time_t       time_preview;
738     time_t       time_current;
739     time_t       ti_time;
740     struct tm   *ti_tm;
741     unsigned int elapsed_time;
742
743     // Follow the same steps as ui/win32/file_dlg_win32.c
744
745     foreach (QLabel *lbl, preview_labels_) {
746         lbl->setEnabled(false);
747     }
748
749     preview_format_.setText(tr("-"));
750     preview_size_.setText(tr("-"));
751     preview_packets_.setText(tr("-"));
752     preview_first_.setText(tr("-"));
753     preview_elapsed_.setText(tr("-"));
754
755     if (path.length() < 1) {
756         return;
757     }
758
759     if (test_for_directory(path.toUtf8().data()) == EISDIR) {
760         preview_format_.setText(tr("directory"));
761         return;
762     }
763
764     wth = wtap_open_offline(path.toUtf8().data(), &err, &err_info, TRUE);
765     if (wth == NULL) {
766         if(err == WTAP_ERR_FILE_UNKNOWN_FORMAT) {
767             preview_format_.setText(tr("unknown file format"));
768         } else {
769             preview_format_.setText(tr("error opening file"));
770         }
771         return;
772     }
773
774     // Success!
775     foreach (QLabel *lbl, preview_labels_) {
776         lbl->setEnabled(true);
777     }
778
779     // Format
780     preview_format_.setText(QString::fromUtf8(wtap_file_type_subtype_string(wtap_file_type_subtype(wth))));
781
782     // Size
783     preview_size_.setText(QString(tr("%1 bytes")).arg(wtap_file_size(wth, &err)));
784
785     time(&time_preview);
786     while ( (wtap_read(wth, &err, &err_info, &data_offset)) ) {
787         phdr = wtap_phdr(wth);
788         cur_time = wtap_nstime_to_sec(&phdr->ts);
789         if(packets == 0) {
790             start_time = cur_time;
791             stop_time = cur_time;
792         }
793         if (cur_time < start_time) {
794             start_time = cur_time;
795         }
796         if (cur_time > stop_time){
797             stop_time = cur_time;
798         }
799
800         packets++;
801         if(packets%1000 == 0) {
802             /* do we have a timeout? */
803             time(&time_current);
804             if(time_current-time_preview >= (time_t) prefs.gui_fileopen_preview) {
805                 timed_out = TRUE;
806                 break;
807             }
808         }
809     }
810
811     if(err != 0) {
812         preview_packets_.setText(QString(tr("error after reading %1 packets")).arg(packets));
813         return;
814     }
815
816     // Packet count
817     if(timed_out) {
818         preview_packets_.setText(QString(tr("more than %1 (preview timeout)")).arg(packets));
819     } else {
820         preview_packets_.setText(QString("%1").arg(packets));
821     }
822
823     // First packet
824     ti_time = (long)start_time;
825     ti_tm = localtime( &ti_time );
826     if(ti_tm) {
827         preview_first_.setText(QString().sprintf(
828                  "%04d-%02d-%02d %02d:%02d:%02d",
829                  ti_tm->tm_year + 1900,
830                  ti_tm->tm_mon + 1,
831                  ti_tm->tm_mday,
832                  ti_tm->tm_hour,
833                  ti_tm->tm_min,
834                  ti_tm->tm_sec
835                  ));
836     } else {
837         preview_first_.setText(tr("?"));
838     }
839
840     // Elapsed time
841     elapsed_time = (unsigned int)(stop_time-start_time);
842     if(timed_out) {
843         preview_elapsed_.setText(tr("unknown"));
844     } else if(elapsed_time/86400) {
845         preview_elapsed_.setText(QString().sprintf("%02u days %02u:%02u:%02u",
846                 elapsed_time/86400, elapsed_time%86400/3600, elapsed_time%3600/60, elapsed_time%60));
847     } else {
848         preview_elapsed_.setText(QString().sprintf("%02u:%02u:%02u",
849                 elapsed_time%86400/3600, elapsed_time%3600/60, elapsed_time%60));
850     }
851
852     wtap_close(wth);
853 }
854
855 void CaptureFileDialog::on_buttonBox_helpRequested()
856 {
857     if (help_topic_ != TOPIC_ACTION_NONE) wsApp->helpTopicAction(help_topic_);
858 }
859
860 #endif // Q_OS_WINDOWS
861
862 /*
863  * Editor modelines
864  *
865  * Local Variables:
866  * c-basic-offset: 4
867  * tab-width: 8
868  * indent-tabs-mode: nil
869  * End:
870  *
871  * ex: set shiftwidth=4 tabstop=8 expandtab:
872  * :indentSize=4:tabSize=8:noTabs=true:
873  */