Rename utilityToolBar to displayFilterToolbar and move the display
[metze/wireshark/wip.git] / ui / qt / main_window.cpp
1 /* main_window.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 "main_window.h"
25 #include "ui_main_window.h"
26
27 #include "globals.h"
28
29 #include <epan/filesystem.h>
30 #include <epan/prefs.h>
31
32 //#include <wiretap/wtap.h>
33
34 #ifdef HAVE_LIBPCAP
35 #include "capture.h"
36 #include "capture-pcap-util.h"
37 #include "capture_ui_utils.h"
38 #endif
39
40 #include "ui/alert_box.h"
41 #include "ui/main_statusbar.h"
42 #include "ui/capture_globals.h"
43
44 #include "wireshark_application.h"
45 #include "proto_tree.h"
46 #include "byte_view_tab.h"
47 #include "display_filter_edit.h"
48 #include "import_text_dialog.h"
49 #include "export_dissection_dialog.h"
50
51 #include "qt_ui_utils.h"
52
53 #include <QTreeWidget>
54 #include <QTabWidget>
55 #include <QAction>
56 #include <QToolButton>
57 #include <QKeyEvent>
58 #include <QMetaObject>
59 #include <QMessageBox>
60
61 //menu_recent_file_write_all
62
63 // If we ever add support for multiple windows this will need to be replaced.
64 static MainWindow *gbl_cur_main_window = NULL;
65
66 void pipe_input_set_handler(gint source, gpointer user_data, int *child_process, pipe_input_cb_t input_cb)
67 {
68     gbl_cur_main_window->setPipeInputHandler(source, user_data, child_process, input_cb);
69 }
70
71 MainWindow::MainWindow(QWidget *parent) :
72     QMainWindow(parent),
73     main_ui_(new Ui::MainWindow),
74     df_combo_box_(new DisplayFilterCombo()),
75     cap_file_(NULL),
76     previous_focus_(NULL),
77     capture_stopping_(false),
78 #ifdef _WIN32
79     pipe_timer_(NULL)
80 #else
81     pipe_notifier_(NULL)
82 #endif
83 {
84     QMargins go_to_margins;
85
86     gbl_cur_main_window = this;
87     main_ui_->setupUi(this);
88     setMenusForCaptureFile();
89     setForCapturedPackets(false);
90     setMenusForSelectedTreeRow();
91     setForCaptureInProgress(false);
92     setMenusForFileSet(false);
93     interfaceSelectionChanged();
94
95     connect(wsApp, SIGNAL(updateRecentItemStatus(const QString &, qint64, bool)), this, SLOT(updateRecentFiles()));
96     updateRecentFiles();
97
98     const DisplayFilterEdit *df_edit = dynamic_cast<DisplayFilterEdit *>(df_combo_box_->lineEdit());
99     connect(df_edit, SIGNAL(pushFilterSyntaxStatus(QString&)), main_ui_->statusBar, SLOT(pushFilterStatus(QString&)));
100     connect(df_edit, SIGNAL(popFilterSyntaxStatus()), main_ui_->statusBar, SLOT(popFilterStatus()));
101     connect(df_edit, SIGNAL(pushFilterSyntaxWarning(QString&)), main_ui_->statusBar, SLOT(pushTemporaryStatus(QString&)));
102
103     // http://standards.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html
104     // http://qt-project.org/doc/qt-4.8/qstyle.html#StandardPixmap-enum
105     main_ui_->actionFileOpen->setIcon(
106                 QIcon().fromTheme("document-open", style()->standardIcon(QStyle::SP_DirIcon)));
107     main_ui_->actionFileSave->setIcon(
108                 QIcon().fromTheme("document-save", style()->standardIcon(QStyle::SP_FileIcon)));
109     main_ui_->actionFileClose->setIcon(
110                 QIcon().fromTheme("process-stop", style()->standardIcon(QStyle::SP_BrowserStop)));
111
112     // In Qt4 multiple toolbars and "pretty" are mutually exculsive on OS X. If
113     // unifiedTitleAndToolBarOnMac is enabled everything ends up in the same row.
114     // https://bugreports.qt-project.org/browse/QTBUG-22433
115     // This property is obsolete in Qt5 so this issue may be fixed in that version.
116     main_ui_->displayFilterToolBar->addWidget(df_combo_box_);
117
118     main_ui_->goToFrame->hide();
119     go_to_margins = main_ui_->goToHB->contentsMargins();
120 //    go_to_margins.setTop(0);
121 //    go_to_margins.setBottom(0);
122     main_ui_->goToHB->setContentsMargins(go_to_margins);
123     // XXX For some reason the cursor is drawn funny with an input mask set
124     // https://bugreports.qt-project.org/browse/QTBUG-7174
125     main_ui_->goToFrame->setStyleSheet(
126                 "QFrame {"
127                 "  background: palette(window);"
128                 "  padding-top: 0.1em;"
129                 "  padding-bottom: 0.1em;"
130                 "  border-bottom: 1px solid palette(shadow);"
131                 "}"
132                 "QLineEdit {"
133                 "  max-width: 5em;"
134                 "}"
135                 );
136
137 #if defined(Q_WS_MAC)
138     foreach (QMenu *menu, main_ui_->menuBar->findChildren<QMenu*>()) {
139         foreach (QAction *act, menu->actions()) {
140             qDebug() << "disabling" << act->objectName();
141             act->setIconVisibleInMenu(false);
142         }
143     }
144     main_ui_->goToLineEdit->setAttribute(Qt::WA_MacSmallSize, true);
145     main_ui_->goToGo->setAttribute(Qt::WA_MacSmallSize, true);
146     main_ui_->goToCancel->setAttribute(Qt::WA_MacSmallSize, true);
147 #endif
148
149     packet_splitter_ = new QSplitter(main_ui_->mainStack);
150     packet_splitter_->setObjectName(QString::fromUtf8("splitterV"));
151     packet_splitter_->setOrientation(Qt::Vertical);
152
153     packet_list_ = new PacketList(packet_splitter_);
154
155     ProtoTree *proto_tree = new ProtoTree(packet_splitter_);
156     proto_tree->setHeaderHidden(true);
157     proto_tree->installEventFilter(this);
158
159     ByteViewTab *byte_view_tab = new ByteViewTab(packet_splitter_);
160     byte_view_tab->setTabPosition(QTabWidget::South);
161     byte_view_tab->setDocumentMode(true);
162
163     packet_list_->setProtoTree(proto_tree);
164     packet_list_->setByteViewTab(byte_view_tab);
165     packet_list_->installEventFilter(this);
166
167     packet_splitter_->addWidget(packet_list_);
168     packet_splitter_->addWidget(proto_tree);
169     packet_splitter_->addWidget(byte_view_tab);
170
171     main_ui_->mainStack->addWidget(packet_splitter_);
172
173     main_welcome_ = main_ui_->welcomePage;
174
175 #ifdef HAVE_LIBPCAP
176     connect(wsApp, SIGNAL(captureCapturePrepared(capture_options *)),
177             this, SLOT(captureCapturePrepared(capture_options *)));
178     connect(wsApp, SIGNAL(captureCaptureUpdateStarted(capture_options *)),
179             this, SLOT(captureCaptureUpdateStarted(capture_options *)));
180     connect(wsApp, SIGNAL(captureCaptureUpdateFinished(capture_options *)),
181             this, SLOT(captureCaptureUpdateFinished(capture_options *)));
182     connect(wsApp, SIGNAL(captureCaptureFixedStarted(capture_options *)),
183             this, SLOT(captureCaptureFixedStarted(capture_options *)));
184     connect(wsApp, SIGNAL(captureCaptureFixedFinished(capture_options *)),
185             this, SLOT(captureCaptureFixedFinished(capture_options *)));
186     connect(wsApp, SIGNAL(captureCaptureStopping(capture_options *)),
187             this, SLOT(captureCaptureStopping(capture_options *)));
188     connect(wsApp, SIGNAL(captureCaptureFailed(capture_options *)),
189             this, SLOT(captureCaptureFailed(capture_options *)));
190 #endif
191
192     connect(wsApp, SIGNAL(captureFileOpened(const capture_file*)),
193             this, SLOT(captureFileOpened(const capture_file*)));
194     connect(wsApp, SIGNAL(captureFileReadStarted(const capture_file*)),
195             this, SLOT(captureFileReadStarted(const capture_file*)));
196     connect(wsApp, SIGNAL(captureFileReadFinished(const capture_file*)),
197             this, SLOT(captureFileReadFinished(const capture_file*)));
198     connect(wsApp, SIGNAL(captureFileClosing(const capture_file*)),
199             this, SLOT(captureFileClosing(const capture_file*)));
200     connect(wsApp, SIGNAL(captureFileClosed(const capture_file*)),
201             this, SLOT(captureFileClosed(const capture_file*)));
202
203     connect(main_welcome_, SIGNAL(startCapture()),
204             this, SLOT(startCapture()));
205     connect(main_welcome_, SIGNAL(recentFileActivated(QString&)),
206             this, SLOT(openCaptureFile(QString&)));
207
208     connect(this, SIGNAL(setCaptureFile(capture_file*)),
209             main_ui_->statusBar, SLOT(setCaptureFile(capture_file*)));
210     connect(this, SIGNAL(setCaptureFile(capture_file*)),
211             packet_list_, SLOT(setCaptureFile(capture_file*)));
212     connect(this, SIGNAL(setCaptureFile(capture_file*)),
213             byte_view_tab, SLOT(setCaptureFile(capture_file*)));
214
215     connect(main_ui_->actionGoNextPacket, SIGNAL(triggered()),
216             packet_list_, SLOT(goNextPacket()));
217     connect(main_ui_->actionGoPreviousPacket, SIGNAL(triggered()),
218             packet_list_, SLOT(goPreviousPacket()));
219     connect(main_ui_->actionGoFirstPacket, SIGNAL(triggered()),
220             packet_list_, SLOT(goFirstPacket()));
221     connect(main_ui_->actionGoLastPacket, SIGNAL(triggered()),
222             packet_list_, SLOT(goLastPacket()));
223
224     connect(main_ui_->actionViewExpandSubtrees, SIGNAL(triggered()),
225             proto_tree, SLOT(expandSubtrees()));
226     connect(main_ui_->actionViewExpandAll, SIGNAL(triggered()),
227             proto_tree, SLOT(expandAll()));
228     connect(main_ui_->actionViewCollapseAll, SIGNAL(triggered()),
229             proto_tree, SLOT(collapseAll()));
230
231     connect(proto_tree, SIGNAL(protoItemSelected(QString&)),
232             main_ui_->statusBar, SLOT(pushFieldStatus(QString&)));
233
234     connect(proto_tree, SIGNAL(protoItemSelected(field_info *)),
235             this, SLOT(setMenusForSelectedTreeRow(field_info *)));
236
237     connect(&file_set_dialog_, SIGNAL(fileSetOpenCaptureFile(QString&)),
238             this, SLOT(openCaptureFile(QString&)));
239
240     QTreeWidget *iface_tree = findChild<QTreeWidget *>("interfaceTree");
241     if (iface_tree) {
242         connect(iface_tree, SIGNAL(itemSelectionChanged()),
243                 this, SLOT(interfaceSelectionChanged()));
244     }
245     main_ui_->mainStack->setCurrentWidget(main_welcome_);
246 }
247
248 MainWindow::~MainWindow()
249 {
250     delete main_ui_;
251 }
252
253 #include <QDebug>
254 void MainWindow::setPipeInputHandler(gint source, gpointer user_data, int *child_process, pipe_input_cb_t input_cb)
255 {
256     pipe_source_        = source;
257     pipe_child_process_ = child_process;
258     pipe_user_data_     = user_data;
259     pipe_input_cb_      = input_cb;
260
261 #ifdef _WIN32
262     /* Tricky to use pipes in win9x, as no concept of wait.  NT can
263        do this but that doesn't cover all win32 platforms.  GTK can do
264        this but doesn't seem to work over processes.  Attempt to do
265        something similar here, start a timer and check for data on every
266        timeout. */
267        /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_input_set_handler: new");*/
268
269     if (pipe_timer_) {
270         disconnect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
271         delete pipe_timer_;
272     }
273
274     pipe_timer_ = new QTimer(this);
275     connect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
276     connect(pipe_timer_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
277     pipe_timer_->start(200);
278 #else
279     if (pipe_notifier_) {
280         disconnect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
281         delete pipe_notifier_;
282     }
283
284     pipe_notifier_ = new QSocketNotifier(pipe_source_, QSocketNotifier::Read);
285     // XXX ui/gtk/gui_utils.c sets the encoding. Do we need to do the same?
286     connect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
287     connect(pipe_notifier_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
288 #endif
289 }
290
291
292 bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
293
294     // The user typed some text. Start filling in a filter.
295     // We may need to be more choosy here. We just need to catch events for the packet list,
296     // proto tree, and main welcome widgets.
297     if (event->type() == QEvent::KeyPress) {
298         QKeyEvent *kevt = static_cast<QKeyEvent *>(event);
299         if (kevt->text().length() > 0 && kevt->text()[0].isPrint()) {
300             df_combo_box_->lineEdit()->insert(kevt->text());
301             df_combo_box_->lineEdit()->setFocus();
302             return true;
303         }
304     }
305
306     return QObject::eventFilter(obj, event);
307 }
308
309 void MainWindow::keyPressEvent(QKeyEvent *event) {
310
311     // Explicitly focus on the display filter combo.
312     if (event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_Slash) {
313         df_combo_box_->setFocus(Qt::ShortcutFocusReason);
314         return;
315     }
316
317     if (wsApp->focusWidget() == main_ui_->goToLineEdit) {
318         if (event->modifiers() == Qt::NoModifier) {
319             if (event->key() == Qt::Key_Escape) {
320                 on_goToCancel_clicked();
321             } else if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
322                 on_goToGo_clicked();
323             }
324         }
325         return; // goToLineEdit didn't want it and we don't either.
326     }
327
328     // Move up & down the packet list.
329     if (event->key() == Qt::Key_F7) {
330         packet_list_->goPreviousPacket();
331     } else if (event->key() == Qt::Key_F8) {
332         packet_list_->goNextPacket();
333     }
334
335     // Move along, citizen.
336     QMainWindow::keyPressEvent(event);
337 }
338
339 void MainWindow::closeEvent(QCloseEvent *event) {
340    /* If we're in the middle of stopping a capture, don't do anything;
341       the user can try deleting the window after the capture stops. */
342     if (capture_stopping_) {
343         event->ignore();
344     }
345 }
346
347
348 void MainWindow::mergeCaptureFile()
349 {
350     QString file_name = "";
351     QString display_filter = "";
352     dfilter_t *rfcode = NULL;
353     int err;
354
355     if (!cap_file_)
356         return;
357
358     if (prefs.gui_ask_unsaved) {
359         if (cap_file_->is_tempfile || cap_file_->unsaved_changes) {
360             QMessageBox msg_dialog;
361             gchar *display_basename;
362             int response;
363
364             msg_dialog.setIcon(QMessageBox::Question);
365             /* This is a temporary capture file or has unsaved changes; ask the
366                user whether to save the capture. */
367             if (cap_file_->is_tempfile) {
368                 msg_dialog.setText(tr("Save packets before merging?"));
369                 msg_dialog.setInformativeText(tr("A temporary capture file can't be merged."));
370             } else {
371                 /*
372                  * Format the message.
373                  */
374                 display_basename = g_filename_display_basename(cap_file_->filename);
375                 msg_dialog.setText(QString(tr("Save changes in \"%1\" before merging?")).arg(display_basename));
376                 g_free(display_basename);
377                 msg_dialog.setInformativeText(tr("Changes must be saved before the files can be merged."));
378             }
379
380             msg_dialog.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel);
381             msg_dialog.setDefaultButton(QMessageBox::Save);
382
383             response = msg_dialog.exec();
384
385             switch (response) {
386
387             case QMessageBox::Save:
388                 /* Save the file but don't close it */
389                 saveCaptureFile(cap_file_, FALSE);
390                 break;
391
392             case QMessageBox::Cancel:
393             default:
394                 /* Don't do the merge. */
395                 return;
396             }
397         }
398     }
399
400     for (;;) {
401         CaptureFileDialog merge_dlg(this, cap_file_, display_filter);
402         int file_type;
403         cf_status_t  merge_status;
404         char        *in_filenames[2];
405         char        *tmpname;
406
407         switch (prefs.gui_fileopen_style) {
408
409         case FO_STYLE_LAST_OPENED:
410             /* The user has specified that we should start out in the last directory
411            we looked in.  If we've already opened a file, use its containing
412            directory, if we could determine it, as the directory, otherwise
413            use the "last opened" directory saved in the preferences file if
414            there was one. */
415             /* This is now the default behaviour in file_selection_new() */
416             break;
417
418         case FO_STYLE_SPECIFIED:
419             /* The user has specified that we should always start out in a
420            specified directory; if they've specified that directory,
421            start out by showing the files in that dir. */
422             if (prefs.gui_fileopen_dir[0] != '\0')
423                 merge_dlg.setDirectory(prefs.gui_fileopen_dir);
424             break;
425         }
426
427         if (merge_dlg.merge(file_name)) {
428             if (dfilter_compile(display_filter.toUtf8().constData(), &rfcode)) {
429                 cf_set_rfcode(cap_file_, rfcode);
430             } else {
431                 /* Not valid.  Tell the user, and go back and run the file
432                    selection box again once they dismiss the alert. */
433                 //bad_dfilter_alert_box(top_level, display_filter->str);
434                 QMessageBox::warning(this, tr("Invalid Display Filter"),
435                                      QString(tr("The filter expression %1 isn't a valid display filter. (%2).").arg(display_filter, dfilter_error_msg)),
436                                      QMessageBox::Ok);
437                 continue;
438             }
439         } else {
440             return;
441         }
442
443         file_type = cap_file_->cd_t;
444
445         /* Try to merge or append the two files */
446         tmpname = NULL;
447         if (merge_dlg.mergeType() == 0) {
448             /* chronological order */
449             in_filenames[0] = cap_file_->filename;
450             in_filenames[1] = file_name.toUtf8().data();
451             merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, FALSE);
452         } else if (merge_dlg.mergeType() <= 0) {
453             /* prepend file */
454             in_filenames[0] = file_name.toUtf8().data();
455             in_filenames[1] = cap_file_->filename;
456             merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
457         } else {
458             /* append file */
459             in_filenames[0] = cap_file_->filename;
460             in_filenames[1] = file_name.toUtf8().data();
461             merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
462         }
463
464         if (merge_status != CF_OK) {
465             if (rfcode != NULL)
466                 dfilter_free(rfcode);
467             g_free(tmpname);
468             continue;
469         }
470
471         cf_close(cap_file_);
472
473         /* Try to open the merged capture file. */
474         cfile.window = this;
475         if (cf_open(&cfile, tmpname, TRUE /* temporary file */, &err) != CF_OK) {
476             /* We couldn't open it; fail. */
477             cfile.window = NULL;
478             if (rfcode != NULL)
479                 dfilter_free(rfcode);
480             g_free(tmpname);
481             return;
482         }
483
484         /* Attach the new read filter to "cf" ("cf_open()" succeeded, so
485            it closed the previous capture file, and thus destroyed any
486            previous read filter attached to "cf"). */
487         cfile.rfcode = rfcode;
488
489         switch (cf_read(&cfile, FALSE)) {
490
491         case CF_READ_OK:
492         case CF_READ_ERROR:
493             /* Just because we got an error, that doesn't mean we were unable
494              to read any of the file; we handle what we could get from the
495              file. */
496             break;
497
498         case CF_READ_ABORTED:
499             /* The user bailed out of re-reading the capture file; the
500              capture file has been closed - just free the capture file name
501              string and return (without changing the last containing
502              directory). */
503             g_free(tmpname);
504             return;
505         }
506
507         /* Save the name of the containing directory specified in the path name,
508            if any; we can write over cf_merged_name, which is a good thing, given that
509            "get_dirname()" does write over its argument. */
510         wsApp->setLastOpenDir(get_dirname(tmpname));
511         g_free(tmpname);
512         df_combo_box_->setEditText(display_filter);
513         main_ui_->statusBar->showExpert();
514         return;
515     }
516
517 }
518
519 void MainWindow::importCaptureFile() {
520     ImportTextDialog import_dlg;
521
522     if (!testCaptureFileClose(FALSE, *new QString(tr(" before importing a new capture"))))
523         return;
524
525     import_dlg.exec();
526
527     if (import_dlg.result() != QDialog::Accepted) {
528         main_ui_->mainStack->setCurrentWidget(main_welcome_);
529         return;
530     }
531
532     openCaptureFile(import_dlg.capfileName());
533 }
534
535 void MainWindow::saveCaptureFile(capture_file *cf, bool stay_closed) {
536     QString file_name;
537     gboolean discard_comments;
538
539     if (cf->is_tempfile) {
540         /* This is a temporary capture file, so saving it means saving
541            it to a permanent file.  Prompt the user for a location
542            to which to save it.  Don't require that the file format
543            support comments - if it's a temporary capture file, it's
544            probably pcap-ng, which supports comments and, if it's
545            not pcap-ng, let the user decide what they want to do
546            if they've added comments. */
547         saveAsCaptureFile(cf, FALSE, stay_closed);
548     } else {
549         if (cf->unsaved_changes) {
550             cf_write_status_t status;
551
552             /* This is not a temporary capture file, but it has unsaved
553                changes, so saving it means doing a "safe save" on top
554                of the existing file, in the same format - no UI needed
555                unless the file has comments and the file's format doesn't
556                support them.
557
558                If the file has comments, does the file's format support them?
559                If not, ask the user whether they want to discard the comments
560                or choose a different format. */
561             switch (CaptureFileDialog::checkSaveAsWithComments(this, cf, cf->cd_t)) {
562
563             case SAVE:
564                 /* The file can be saved in the specified format as is;
565                    just drive on and save in the format they selected. */
566                 discard_comments = FALSE;
567                 break;
568
569             case SAVE_WITHOUT_COMMENTS:
570                 /* The file can't be saved in the specified format as is,
571                    but it can be saved without the comments, and the user
572                    said "OK, discard the comments", so save it in the
573                    format they specified without the comments. */
574                 discard_comments = TRUE;
575                 break;
576
577             case SAVE_IN_ANOTHER_FORMAT:
578                 /* There are file formats in which we can save this that
579                    support comments, and the user said not to delete the
580                    comments.  Do a "Save As" so the user can select
581                    one of those formats and choose a file name. */
582                 saveAsCaptureFile(cf, TRUE, stay_closed);
583                 return;
584
585             case CANCELLED:
586                 /* The user said "forget it".  Just return. */
587                 return;
588
589             default:
590                 /* Squelch warnings that discard_comments is being used
591                    uninitialized. */
592                 g_assert_not_reached();
593                 return;
594             }
595
596             /* XXX - cf->filename might get freed out from under us, because
597                the code path through which cf_save_packets() goes currently
598                closes the current file and then opens and reloads the saved file,
599                so make a copy and free it later. */
600             file_name = cf->filename;
601             status = cf_save_packets(cf, file_name.toUtf8().constData(), cf->cd_t, cf->iscompressed,
602                                      discard_comments, stay_closed);
603             switch (status) {
604
605             case CF_WRITE_OK:
606                 /* The save succeeded; we're done.
607                    If we discarded comments, redraw the packet list to reflect
608                    any packets that no longer have comments. */
609                 if (discard_comments)
610                     packet_list_queue_draw();
611                 break;
612
613             case CF_WRITE_ERROR:
614                 /* The write failed.
615                    XXX - OK, what do we do now?  Let them try a
616                    "Save As", in case they want to try to save to a
617                    different directory r file system? */
618                 break;
619
620             case CF_WRITE_ABORTED:
621                 /* The write was aborted; just drive on. */
622                 break;
623             }
624         }
625         /* Otherwise just do nothing. */
626     }
627 }
628
629 void MainWindow::saveAsCaptureFile(capture_file *cf, bool must_support_comments, bool stay_closed) {
630     QString file_name = "";
631     int file_type;
632     gboolean compressed;
633     cf_write_status_t status;
634     gchar   *dirname;
635     gboolean discard_comments = FALSE;
636
637     if (!cf) {
638         return;
639     }
640
641     for (;;) {
642         CaptureFileDialog save_as_dlg(this, cf);
643
644         switch (prefs.gui_fileopen_style) {
645
646         case FO_STYLE_LAST_OPENED:
647             /* The user has specified that we should start out in the last directory
648                we looked in.  If we've already opened a file, use its containing
649                directory, if we could determine it, as the directory, otherwise
650                use the "last opened" directory saved in the preferences file if
651                there was one. */
652             /* This is now the default behaviour in file_selection_new() */
653             break;
654
655         case FO_STYLE_SPECIFIED:
656             /* The user has specified that we should always start out in a
657                specified directory; if they've specified that directory,
658                start out by showing the files in that dir. */
659             if (prefs.gui_fileopen_dir[0] != '\0')
660                 save_as_dlg.setDirectory(prefs.gui_fileopen_dir);
661             break;
662         }
663
664         /* If the file has comments, does the format the user selected
665            support them?  If not, ask the user whether they want to
666            discard the comments or choose a different format. */
667         switch(save_as_dlg.saveAs(file_name, must_support_comments)) {
668
669         case SAVE:
670             /* The file can be saved in the specified format as is;
671                just drive on and save in the format they selected. */
672             discard_comments = FALSE;
673             break;
674
675         case SAVE_WITHOUT_COMMENTS:
676             /* The file can't be saved in the specified format as is,
677                but it can be saved without the comments, and the user
678                said "OK, discard the comments", so save it in the
679                format they specified without the comments. */
680             discard_comments = TRUE;
681             break;
682
683         case SAVE_IN_ANOTHER_FORMAT:
684             /* There are file formats in which we can save this that
685                support comments, and the user said not to delete the
686                comments.  The combo box of file formats has had the
687                formats that don't support comments trimmed from it,
688                so run the dialog again, to let the user decide
689                whether to save in one of those formats or give up. */
690             discard_comments = FALSE;
691             must_support_comments = TRUE;
692             continue;
693
694         case CANCELLED:
695             /* The user said "forget it".  Just get rid of the dialog box
696                and return. */
697             return;
698         }
699         file_type = save_as_dlg.selectedFileType();
700         compressed = save_as_dlg.isCompressed();
701
702         fileAddExtension(file_name, file_type, compressed);
703
704 //#ifndef _WIN32
705 //        /* If the file exists and it's user-immutable or not writable,
706 //                       ask the user whether they want to override that. */
707 //        if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
708 //            /* They don't.  Let them try another file name or cancel. */
709 //            continue;
710 //        }
711 //#endif
712
713         /* Attempt to save the file */
714         status = cf_save_packets(cf, file_name.toUtf8().constData(), file_type, compressed,
715                                  discard_comments, stay_closed);
716         switch (status) {
717
718         case CF_WRITE_OK:
719             /* The save succeeded; we're done. */
720             /* Save the directory name for future file dialogs. */
721             dirname = get_dirname(file_name.toUtf8().data());  /* Overwrites cf_name */
722             set_last_open_dir(dirname);
723             /* If we discarded comments, redraw the packet list to reflect
724                any packets that no longer have comments. */
725             if (discard_comments)
726                 packet_list_queue_draw();
727             return;
728
729         case CF_WRITE_ERROR:
730             /* The save failed; let the user try again. */
731             continue;
732
733         case CF_WRITE_ABORTED:
734             /* The user aborted the save; just return. */
735             return;
736         }
737     }
738     return;
739 }
740
741 void MainWindow::exportSelectedPackets() {
742     QString file_name = "";
743     int file_type;
744     gboolean compressed;
745     packet_range_t range;
746     cf_write_status_t status;
747     gchar   *dirname;
748     gboolean discard_comments = FALSE;
749
750     if (!cap_file_)
751         return;
752
753     /* Init the packet range */
754     packet_range_init(&range, cap_file_);
755     range.process_filtered = TRUE;
756     range.include_dependents = TRUE;
757
758     for (;;) {
759         CaptureFileDialog esp_dlg(this, cap_file_);
760
761         switch (prefs.gui_fileopen_style) {
762
763         case FO_STYLE_LAST_OPENED:
764             /* The user has specified that we should start out in the last directory
765                we looked in.  If we've already opened a file, use its containing
766                directory, if we could determine it, as the directory, otherwise
767                use the "last opened" directory saved in the preferences file if
768                there was one. */
769             /* This is now the default behaviour in file_selection_new() */
770             break;
771
772         case FO_STYLE_SPECIFIED:
773             /* The user has specified that we should always start out in a
774                specified directory; if they've specified that directory,
775                start out by showing the files in that dir. */
776             if (prefs.gui_fileopen_dir[0] != '\0')
777                 esp_dlg.setDirectory(prefs.gui_fileopen_dir);
778             break;
779         }
780
781         /* If the file has comments, does the format the user selected
782            support them?  If not, ask the user whether they want to
783            discard the comments or choose a different format. */
784         switch(esp_dlg.exportSelectedPackets(file_name, &range)) {
785
786         case SAVE:
787             /* The file can be saved in the specified format as is;
788                just drive on and save in the format they selected. */
789             discard_comments = FALSE;
790             break;
791
792         case SAVE_WITHOUT_COMMENTS:
793             /* The file can't be saved in the specified format as is,
794                but it can be saved without the comments, and the user
795                said "OK, discard the comments", so save it in the
796                format they specified without the comments. */
797             discard_comments = TRUE;
798             break;
799
800         case SAVE_IN_ANOTHER_FORMAT:
801             /* There are file formats in which we can save this that
802                support comments, and the user said not to delete the
803                comments.  The combo box of file formats has had the
804                formats that don't support comments trimmed from it,
805                so run the dialog again, to let the user decide
806                whether to save in one of those formats or give up. */
807             discard_comments = FALSE;
808             continue;
809
810         case CANCELLED:
811             /* The user said "forget it".  Just get rid of the dialog box
812                and return. */
813             return;
814         }
815
816         /*
817          * Check that we're not going to save on top of the current
818          * capture file.
819          * We do it here so we catch all cases ...
820          * Unfortunately, the file requester gives us an absolute file
821          * name and the read file name may be relative (if supplied on
822          * the command line). From Joerg Mayer.
823          */
824         if (files_identical(cap_file_->filename, file_name.toUtf8().constData())) {
825             QMessageBox msg_box;
826             gchar *display_basename = g_filename_display_basename(file_name.toUtf8().constData());
827
828             msg_box.setIcon(QMessageBox::Critical);
829             msg_box.setText(QString(tr("Unable to export to \"%1\".").arg(display_basename)));
830             msg_box.setInformativeText(tr("You cannot export packets to the current capture file."));
831             msg_box.setStandardButtons(QMessageBox::Ok);
832             msg_box.setDefaultButton(QMessageBox::Ok);
833             msg_box.exec();
834             g_free(display_basename);
835             continue;
836         }
837
838         file_type = esp_dlg.selectedFileType();
839         compressed = esp_dlg.isCompressed();
840         fileAddExtension(file_name, file_type, compressed);
841
842 //#ifndef _WIN32
843 //        /* If the file exists and it's user-immutable or not writable,
844 //                       ask the user whether they want to override that. */
845 //        if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
846 //            /* They don't.  Let them try another file name or cancel. */
847 //            continue;
848 //        }
849 //#endif
850
851         /* Attempt to save the file */
852         status = cf_export_specified_packets(cap_file_, file_name.toUtf8().constData(), &range, file_type, compressed);
853         switch (status) {
854
855         case CF_WRITE_OK:
856             /* The save succeeded; we're done. */
857             /* Save the directory name for future file dialogs. */
858             dirname = get_dirname(file_name.toUtf8().data());  /* Overwrites cf_name */
859             set_last_open_dir(dirname);
860             /* If we discarded comments, redraw the packet list to reflect
861                any packets that no longer have comments. */
862             if (discard_comments)
863                 packet_list_queue_draw();
864             return;
865
866         case CF_WRITE_ERROR:
867             /* The save failed; let the user try again. */
868             continue;
869
870         case CF_WRITE_ABORTED:
871             /* The user aborted the save; just return. */
872             return;
873         }
874     }
875     return;
876 }
877
878 void MainWindow::exportDissections(export_type_e export_type) {
879     ExportDissectionDialog ed_dlg(this, cap_file_, export_type);
880     packet_range_t range;
881
882     if (!cap_file_)
883         return;
884
885     /* Init the packet range */
886     packet_range_init(&range, cap_file_);
887     range.process_filtered = TRUE;
888     range.include_dependents = TRUE;
889
890     ed_dlg.exec();
891 }
892
893 void MainWindow::fileAddExtension(QString &file_name, int file_type, bool compressed) {
894     QString file_name_lower;
895     QString file_suffix;
896     GSList  *extensions_list;
897     gboolean add_extension;
898
899     /*
900      * Append the default file extension if there's none given by
901      * the user or if they gave one that's not one of the valid
902      * extensions for the file type.
903      */
904     file_name_lower = file_name.toLower();
905     extensions_list = wtap_get_file_extensions_list(file_type, FALSE);
906     if (extensions_list != NULL) {
907         GSList *extension;
908
909         /* We have one or more extensions for this file type.
910            Start out assuming we need to add the default one. */
911         add_extension = TRUE;
912
913         /* OK, see if the file has one of those extensions. */
914         for (extension = extensions_list; extension != NULL;
915              extension = g_slist_next(extension)) {
916             file_suffix += tr(".") + (char *)extension->data;
917             if (file_name_lower.endsWith(file_suffix)) {
918                 /*
919                  * The file name has one of the extensions for
920                  * this file type.
921                  */
922                 add_extension = FALSE;
923                 break;
924             }
925             file_suffix += ".gz";
926             if (file_name_lower.endsWith(file_suffix)) {
927                 /*
928                  * The file name has one of the extensions for
929                  * this file type.
930                  */
931                 add_extension = FALSE;
932                 break;
933             }
934         }
935     } else {
936         /* We have no extensions for this file type.  Don't add one. */
937         add_extension = FALSE;
938     }
939     if (add_extension) {
940         if (wtap_default_file_extension(file_type) != NULL) {
941             file_name += tr(".") + wtap_default_file_extension(file_type);
942             if (compressed) {
943                 file_name += ".gz";
944             }
945         }
946     }
947 }
948
949 bool MainWindow::testCaptureFileClose(bool from_quit, QString &before_what) {
950     bool   capture_in_progress = FALSE;
951
952     if (!cap_file_ || cap_file_->state == FILE_CLOSED)
953         return true; /* Already closed, nothing to do */
954
955 #ifdef HAVE_LIBPCAP
956     if (cap_file_->state == FILE_READ_IN_PROGRESS) {
957         /* This is true if we're reading a capture file *or* if we're doing
958          a live capture.  If we're reading a capture file, the main loop
959          is busy reading packets, and only accepting input from the
960          progress dialog, so we can't get here, so this means we're
961          doing a capture. */
962         capture_in_progress = TRUE;
963     }
964 #endif
965
966     if (prefs.gui_ask_unsaved) {
967         if (cap_file_->is_tempfile || capture_in_progress || cap_file_->unsaved_changes) {
968             QMessageBox msg_dialog;
969             QString question;
970             QPushButton *default_button;
971             int response;
972
973             msg_dialog.setIcon(QMessageBox::Question);
974
975             /* This is a temporary capture file, or there's a capture in
976                progress, or the file has unsaved changes; ask the user whether
977                to save the data. */
978             if (cap_file_->is_tempfile) {
979
980                 msg_dialog.setText(tr("You have unsaved packets"));
981                 msg_dialog.setInformativeText(tr("They will be lost if you don't save them."));
982
983                 if (capture_in_progress) {
984                     question.append(tr("Do you want to stop the capture and save the captured packets"));
985                 } else {
986                     question.append(tr("Do you want to save the captured packets"));
987                 }
988                 question.append(before_what).append(tr("?"));
989                 msg_dialog.setInformativeText(question);
990
991
992             } else {
993                 /*
994                  * Format the message.
995                  */
996                 if (capture_in_progress) {
997                     question.append(tr("Do you want to stop the capture and save the captured packets"));
998                     question.append(before_what).append(tr("?"));
999                     msg_dialog.setInformativeText(tr("Your captured packets will be lost if you don't save them."));
1000                 } else {
1001                     gchar *display_basename = g_filename_display_basename(cap_file_->filename);
1002                     question.append(QString(tr("Do you want to save the changes you've made to the capture file \"%1\"%2?"))
1003                                     .arg(display_basename)
1004                                     .arg(before_what)
1005                                     );
1006                     g_free(display_basename);
1007                     msg_dialog.setInformativeText(tr("Your changes will be lost if you don't save them."));
1008                 }
1009             }
1010
1011             // XXX Text comes from ui/gtk/stock_icons.[ch]
1012             // Note that the button roles differ from the GTK+ version.
1013             // Cancel = RejectRole
1014             // Save = AcceptRole
1015             // Don't Save = DestructiveRole
1016             msg_dialog.setStandardButtons(QMessageBox::Cancel);
1017
1018             if (capture_in_progress) {
1019                 default_button = msg_dialog.addButton(tr("Stop and Save"), QMessageBox::AcceptRole);
1020             } else {
1021                 default_button = msg_dialog.addButton(QMessageBox::Save);
1022             }
1023             msg_dialog.setDefaultButton(default_button);
1024
1025             if (from_quit) {
1026                 if (cap_file_->state == FILE_READ_IN_PROGRESS) {
1027                     msg_dialog.addButton(tr("Stop and Quit without Saving"), QMessageBox::DestructiveRole);
1028                 } else {
1029                     msg_dialog.addButton(tr("Quit without Saving"), QMessageBox::DestructiveRole);
1030                 }
1031             } else {
1032                 if (capture_in_progress) {
1033                     msg_dialog.addButton(tr("Stop and Continue without Saving"), QMessageBox::DestructiveRole);
1034                 } else {
1035                     msg_dialog.addButton(QMessageBox::Discard);
1036                 }
1037             }
1038
1039             response = msg_dialog.exec();
1040
1041             switch (response) {
1042
1043             case QMessageBox::Save:
1044 #ifdef HAVE_LIBPCAP
1045                 /* If there's a capture in progress, we have to stop the capture
1046              and then do the save. */
1047                 if (capture_in_progress)
1048                     captureStop();
1049 #endif
1050                 /* Save the file and close it */
1051                 saveCaptureFile(cap_file_, TRUE);
1052                 break;
1053
1054             case QMessageBox::Discard:
1055 #ifdef HAVE_LIBPCAP
1056                 /*
1057                  * If there's a capture in progress; we have to stop the capture
1058                  * and then do the close.
1059                  */
1060                 if (capture_in_progress)
1061                     captureStop();
1062 #endif
1063                 /* Just close the file, discarding changes */
1064                 cf_close(cap_file_);
1065                 return true;
1066                 break;
1067
1068             case QMessageBox::Cancel:
1069             default:
1070                 /* Don't close the file (and don't stop any capture in progress). */
1071                 return false; /* file not closed */
1072                 break;
1073             }
1074         } else {
1075             /* Unchanged file, just close it */
1076             cf_close(cap_file_);
1077         }
1078     } else {
1079         /* User asked not to be bothered by those prompts, just close it.
1080          XXX - should that apply only to saving temporary files? */
1081 #ifdef HAVE_LIBPCAP
1082         /* If there's a capture in progress, we have to stop the capture
1083            and then do the close. */
1084         if (capture_in_progress)
1085             captureStop();
1086 #endif
1087         cf_close(cap_file_);
1088     }
1089
1090     return true; /* File closed */
1091 }
1092
1093 void MainWindow::captureStop() {
1094     stopCapture();
1095
1096     while(cap_file_ && cap_file_->state == FILE_READ_IN_PROGRESS) {
1097         WiresharkApplication::processEvents();
1098     }
1099 }
1100
1101 // Menu state
1102
1103 /* Enable or disable menu items based on whether you have a capture file
1104    you've finished reading and, if you have one, whether it's been saved
1105    and whether it could be saved except by copying the raw packet data. */
1106 void MainWindow::setMenusForCaptureFile(bool force_disable)
1107 {
1108     if (force_disable || cap_file_ == NULL || cap_file_->state == FILE_READ_IN_PROGRESS) {
1109         /* We have no capture file or we're currently reading a file */
1110         main_ui_->actionFileMerge->setEnabled(false);
1111         main_ui_->actionFileClose->setEnabled(false);
1112         main_ui_->actionFileSave->setEnabled(false);
1113         main_ui_->actionFileSaveAs->setEnabled(false);
1114         main_ui_->actionFileExportPackets->setEnabled(false);
1115         main_ui_->menuFileExportPacketDissections->setEnabled(false);
1116         main_ui_->actionFileExportPacketBytes->setEnabled(false);
1117         main_ui_->actionFileExportSSLSessionKeys->setEnabled(false);
1118         main_ui_->menuFileExportObjects->setEnabled(false);
1119         main_ui_->actionViewReload->setEnabled(false);
1120     } else {
1121         main_ui_->actionFileMerge->setEnabled(cf_can_write_with_wiretap(cap_file_));
1122
1123         main_ui_->actionFileClose->setEnabled(true);
1124         /*
1125          * "Save" should be available only if:
1126          *
1127          *  the file has unsaved changes, and we can save it in some
1128          *  format through Wiretap
1129          *
1130          * or
1131          *
1132          *  the file is a temporary file and has no unsaved changes (so
1133          *  that "saving" it just means copying it).
1134          */
1135         main_ui_->actionFileSave->setEnabled(
1136                     (cap_file_->unsaved_changes && cf_can_write_with_wiretap(cap_file_)) ||
1137                     (cap_file_->is_tempfile && !cap_file_->unsaved_changes));
1138         /*
1139          * "Save As..." should be available only if:
1140          *
1141          *  we can save it in some format through Wiretap
1142          *
1143          * or
1144          *
1145          *  the file is a temporary file and has no unsaved changes (so
1146          *  that "saving" it just means copying it).
1147          */
1148         main_ui_->actionFileSaveAs->setEnabled(
1149                     cf_can_write_with_wiretap(cap_file_) ||
1150                     (cap_file_->is_tempfile && !cap_file_->unsaved_changes));
1151         /*
1152          * "Export Specified Packets..." should be available only if
1153          * we can write the file out in at least one format.
1154          */
1155         main_ui_->actionFileExportPackets->setEnabled(cf_can_write_with_wiretap(cap_file_));
1156         main_ui_->menuFileExportPacketDissections->setEnabled(true);
1157         main_ui_->actionFileExportPacketBytes->setEnabled(true);
1158         main_ui_->actionFileExportSSLSessionKeys->setEnabled(true);
1159         main_ui_->menuFileExportObjects->setEnabled(true);
1160         main_ui_->actionViewReload->setEnabled(true);
1161     }
1162 }
1163
1164 void MainWindow::setMenusForCaptureInProgress(bool capture_in_progress) {
1165     /* Either a capture was started or stopped; in either case, it's not
1166        in the process of stopping, so allow quitting. */
1167
1168     main_ui_->actionFileOpen->setEnabled(!capture_in_progress);
1169     main_ui_->menuOpenRecentCaptureFile->setEnabled(!capture_in_progress);
1170     main_ui_->menuFileExportPacketDissections->setEnabled(capture_in_progress);
1171     main_ui_->actionFileExportPacketBytes->setEnabled(capture_in_progress);
1172     main_ui_->actionFileExportSSLSessionKeys->setEnabled(capture_in_progress);
1173     main_ui_->menuFileExportObjects->setEnabled(capture_in_progress);
1174     main_ui_->menuFileSet->setEnabled(!capture_in_progress);
1175     main_ui_->actionFileQuit->setEnabled(true);
1176
1177     qDebug() << "FIX: packet list heading menu sensitivity";
1178     //    set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortAscending",
1179     //                         !capture_in_progress);
1180     //    set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortDescending",
1181     //                         !capture_in_progress);
1182     //    set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/NoSorting",
1183     //                         !capture_in_progress);
1184
1185 #ifdef HAVE_LIBPCAP
1186     main_ui_->actionCaptureOptions->setEnabled(!capture_in_progress);
1187     main_ui_->actionStartCapture->setEnabled(!capture_in_progress);
1188     main_ui_->actionStartCapture->setChecked(capture_in_progress);
1189     main_ui_->actionStopCapture->setEnabled(capture_in_progress);
1190     main_ui_->actionCaptureRestart->setEnabled(capture_in_progress);
1191 #endif /* HAVE_LIBPCAP */
1192
1193 }
1194
1195 void MainWindow::setMenusForCaptureStopping() {
1196     main_ui_->actionFileQuit->setEnabled(false);
1197 #ifdef HAVE_LIBPCAP
1198     main_ui_->actionStartCapture->setChecked(false);
1199     main_ui_->actionStopCapture->setEnabled(false);
1200     main_ui_->actionCaptureRestart->setEnabled(false);
1201 #endif /* HAVE_LIBPCAP */
1202 }
1203
1204 void MainWindow::setForCapturedPackets(bool have_captured_packets)
1205 {
1206     main_ui_->actionFilePrint->setEnabled(have_captured_packets);
1207
1208 //    set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/Print",
1209 //                         have_captured_packets);
1210
1211 //    set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/EditMenu/FindPacket",
1212 //                         have_captured_packets);
1213 //    set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/EditMenu/FindNext",
1214 //                         have_captured_packets);
1215 //    set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/EditMenu/FindPrevious",
1216 //                         have_captured_packets);
1217 //    set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/ViewMenu/ZoomIn",
1218 //                         have_captured_packets);
1219 //    set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/ViewMenu/ZoomOut",
1220 //                         have_captured_packets);
1221 //    set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/ViewMenu/NormalSize",
1222 //                         have_captured_packets);
1223
1224     main_ui_->actionGoGoToPacket->setEnabled(have_captured_packets);
1225     main_ui_->actionGoPreviousPacket->setEnabled(have_captured_packets);
1226     main_ui_->actionGoNextPacket->setEnabled(have_captured_packets);
1227     main_ui_->actionGoFirstPacket->setEnabled(have_captured_packets);
1228     main_ui_->actionGoLastPacket->setEnabled(have_captured_packets);
1229
1230 //    set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/GoMenu/PreviousPacketInConversation",
1231 //                         have_captured_packets);
1232 //    set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/GoMenu/NextPacketInConversation",
1233 //                         have_captured_packets);
1234 //    set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/StatisticsMenu/Summary",
1235 //                         have_captured_packets);
1236 //    set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/StatisticsMenu/ProtocolHierarchy",
1237 //                         have_captured_packets);
1238 }
1239
1240 void MainWindow::setMenusForFileSet(bool enable_list_files) {
1241     bool enable_next = fileset_get_next() != NULL && enable_list_files;
1242     bool enable_prev = fileset_get_previous() != NULL && enable_list_files;
1243
1244     main_ui_->actionFileSetListFiles->setEnabled(enable_list_files);
1245     main_ui_->actionFileSetNextFile->setEnabled(enable_next);
1246     main_ui_->actionFileSetPreviousFile->setEnabled(enable_prev);
1247 }
1248
1249 void MainWindow::updateForUnsavedChanges() {
1250 //    set_display_filename(cf);
1251     setMenusForCaptureFile();
1252 //    set_toolbar_for_capture_file(cf);
1253
1254 }
1255
1256 /* Update main window items based on whether there's a capture in progress. */
1257 void MainWindow::setForCaptureInProgress(gboolean capture_in_progress)
1258 {
1259     setMenusForCaptureInProgress(capture_in_progress);
1260
1261 //#ifdef HAVE_LIBPCAP
1262 //    set_toolbar_for_capture_in_progress(capture_in_progress);
1263
1264 //    set_capture_if_dialog_for_capture_in_progress(capture_in_progress);
1265 //#endif
1266 }
1267
1268 /*
1269  * Editor modelines
1270  *
1271  * Local Variables:
1272  * c-basic-offset: 4
1273  * tab-width: 8
1274  * indent-tabs-mode: nil
1275  * End:
1276  *
1277  * ex: set shiftwidth=4 tabstop=8 expandtab:
1278  * :indentSize=4:tabSize=8:noTabs=true:
1279  */