Fix a comparison.
[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/epan_dissect.h>
30 #include <epan/filesystem.h>
31 #include <epan/ipproto.h>
32 #include <epan/prefs.h>
33
34 //#include <wiretap/wtap.h>
35
36 #ifdef HAVE_LIBPCAP
37 #include "capture.h"
38 #include "capture-pcap-util.h"
39 #include "capture_ui_utils.h"
40 #include "capture_session.h"
41 #endif
42
43 #include "ui/alert_box.h"
44 #include "ui/capture_globals.h"
45 #include "ui/main_statusbar.h"
46 #include "ui/recent.h"
47 #include "ui/util.h"
48
49 #include "wireshark_application.h"
50 #include "proto_tree.h"
51 #include "byte_view_tab.h"
52 #include "display_filter_edit.h"
53 #include "import_text_dialog.h"
54 #include "export_dissection_dialog.h"
55
56 #include "qt_ui_utils.h"
57
58 #include <QAction>
59 #include <QDesktopWidget>
60 #include <QKeyEvent>
61 #include <QMessageBox>
62 #include <QMetaObject>
63 #include <QPropertyAnimation>
64 #include <QTabWidget>
65 #include <QToolButton>
66 #include <QTreeWidget>
67
68 #ifdef QT_MACEXTRAS_LIB
69 #include <QtMacExtras/QMacNativeToolBar>
70 #endif
71
72 #include <QDebug>
73
74 //menu_recent_file_write_all
75
76 // If we ever add support for multiple windows this will need to be replaced.
77 static MainWindow *gbl_cur_main_window = NULL;
78
79 void pipe_input_set_handler(gint source, gpointer user_data, int *child_process, pipe_input_cb_t input_cb)
80 {
81     gbl_cur_main_window->setPipeInputHandler(source, user_data, child_process, input_cb);
82 }
83
84 MainWindow::MainWindow(QWidget *parent) :
85     QMainWindow(parent),
86     main_ui_(new Ui::MainWindow),
87     df_combo_box_(new DisplayFilterCombo()),
88     cap_file_(NULL),
89     previous_focus_(NULL),
90     capture_stopping_(false),
91     capture_filter_valid_(false),
92 #ifdef _WIN32
93     pipe_timer_(NULL)
94 #else
95     pipe_notifier_(NULL)
96 #endif
97 {
98     gbl_cur_main_window = this;
99     main_ui_->setupUi(this);
100     setTitlebarForCaptureFile();
101     setMenusForCaptureFile();
102     setForCapturedPackets(false);
103     setMenusForSelectedPacket();
104     setMenusForSelectedTreeRow();
105     setForCaptureInProgress(false);
106     setMenusForFileSet(false);
107     interfaceSelectionChanged();
108
109     //To prevent users use features before initialization complete
110     //Otherwise unexpected problems may occur
111     setFeaturesEnabled(false);
112     connect(wsApp, SIGNAL(appInitialized()), this, SLOT(setFeaturesEnabled()));
113
114     connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(layoutPanes()));
115
116     connect(wsApp, SIGNAL(updateRecentItemStatus(const QString &, qint64, bool)), this, SLOT(updateRecentFiles()));
117     updateRecentFiles();
118
119     connect(&summary_dialog_, SIGNAL(captureCommentChanged()), this, SLOT(updateForUnsavedChanges()));
120     connect(&follow_stream_dialog_, SIGNAL(updateFilter(QString&, bool)), this, SLOT(filterPackets(QString&, bool)));
121
122     const DisplayFilterEdit *df_edit = dynamic_cast<DisplayFilterEdit *>(df_combo_box_->lineEdit());
123     connect(df_edit, SIGNAL(pushFilterSyntaxStatus(QString&)), main_ui_->statusBar, SLOT(pushFilterStatus(QString&)));
124     connect(df_edit, SIGNAL(popFilterSyntaxStatus()), main_ui_->statusBar, SLOT(popFilterStatus()));
125     connect(df_edit, SIGNAL(pushFilterSyntaxWarning(QString&)),
126             main_ui_->statusBar, SLOT(pushTemporaryStatus(QString&)));
127     connect(df_edit, SIGNAL(filterPackets(QString&,bool)), this, SLOT(filterPackets(QString&,bool)));
128     connect(df_edit, SIGNAL(addBookmark(QString)), this, SLOT(addDisplayFilterButton(QString)));
129     connect(this, SIGNAL(displayFilterSuccess(bool)), df_edit, SLOT(displayFilterSuccess(bool)));
130
131     // http://standards.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html
132     // http://qt-project.org/doc/qt-4.8/qstyle.html#StandardPixmap-enum
133     main_ui_->actionFileOpen->setIcon(
134                 QIcon().fromTheme("document-open", style()->standardIcon(QStyle::SP_DirIcon)));
135     // main_ui_->actionFileSave set in main_window.ui
136     main_ui_->actionFileClose->setIcon(
137                 QIcon().fromTheme("process-stop", style()->standardIcon(QStyle::SP_DialogCloseButton)));
138
139     // In Qt4 multiple toolbars and "pretty" are mutually exculsive on OS X. If
140     // unifiedTitleAndToolBarOnMac is enabled everything ends up in the same row.
141     // https://bugreports.qt-project.org/browse/QTBUG-22433
142     // This property is obsolete in Qt5 so this issue may be fixed in that version.
143     main_ui_->displayFilterToolBar->addWidget(df_combo_box_);
144
145     main_ui_->goToFrame->hide();
146     // XXX For some reason the cursor is drawn funny with an input mask set
147     // https://bugreports.qt-project.org/browse/QTBUG-7174
148
149     main_ui_->searchFrame->hide();
150     connect(main_ui_->searchFrame, SIGNAL(pushFilterSyntaxStatus(QString&)),
151             main_ui_->statusBar, SLOT(pushTemporaryStatus(QString&)));
152
153 #ifndef HAVE_LIBPCAP
154     main_ui_->menuCapture->setEnabled(false);
155 #endif
156
157 #if defined(Q_OS_MAC)
158 #ifdef QT_MACEXTRAS_LIB
159     QMacNativeToolBar *ntb = QtMacExtras::setNativeToolBar(main_ui_->mainToolBar);
160     ntb->setIconSize(QSize(24, 24));
161 #endif // QT_MACEXTRAS_LIB
162
163     foreach (QMenu *menu, main_ui_->menuBar->findChildren<QMenu*>()) {
164         foreach (QAction *act, menu->actions()) {
165             act->setIconVisibleInMenu(false);
166         }
167     }
168     main_ui_->goToLineEdit->setAttribute(Qt::WA_MacSmallSize, true);
169     main_ui_->goToGo->setAttribute(Qt::WA_MacSmallSize, true);
170     main_ui_->goToCancel->setAttribute(Qt::WA_MacSmallSize, true);
171
172     main_ui_->actionEditPreferences->setMenuRole(QAction::PreferencesRole);
173
174 #endif // Q_OS_MAC
175
176 #ifdef HAVE_SOFTWARE_UPDATE
177     QAction *update_sep = main_ui_->menuHelp->insertSeparator(main_ui_->actionHelpAbout);
178     QAction *update_action = new QAction(tr("Check for Updates..."), main_ui_->menuHelp);
179     main_ui_->menuHelp->insertAction(update_sep, update_action);
180     connect(update_action, SIGNAL(triggered()), this, SLOT(on_actionHelpCheckForUpdates_triggered()));
181 #endif
182     master_split_.setObjectName(tr("splitterMaster"));
183     extra_split_.setObjectName(tr("splitterExtra"));
184     main_ui_->mainStack->addWidget(&master_split_);
185
186     empty_pane_.setObjectName(tr("emptyPane"));
187
188     packet_list_ = new PacketList(&master_split_);
189
190     proto_tree_ = new ProtoTree(&master_split_);
191     proto_tree_->setHeaderHidden(true);
192     proto_tree_->installEventFilter(this);
193
194     byte_view_tab_ = new ByteViewTab(&master_split_);
195     byte_view_tab_->setTabPosition(QTabWidget::South);
196     byte_view_tab_->setDocumentMode(true);
197
198     packet_list_->setProtoTree(proto_tree_);
199     packet_list_->setByteViewTab(byte_view_tab_);
200     packet_list_->installEventFilter(this);
201
202     main_welcome_ = main_ui_->welcomePage;
203
204     connect(wsApp, SIGNAL(captureCapturePrepared(capture_session *)),
205             this, SLOT(captureCapturePrepared(capture_session *)));
206     connect(wsApp, SIGNAL(captureCaptureUpdateStarted(capture_session *)),
207             this, SLOT(captureCaptureUpdateStarted(capture_session *)));
208     connect(wsApp, SIGNAL(captureCaptureUpdateFinished(capture_session *)),
209             this, SLOT(captureCaptureUpdateFinished(capture_session *)));
210     connect(wsApp, SIGNAL(captureCaptureFixedStarted(capture_session *)),
211             this, SLOT(captureCaptureFixedStarted(capture_session *)));
212     connect(wsApp, SIGNAL(captureCaptureFixedFinished(capture_session *)),
213             this, SLOT(captureCaptureFixedFinished(capture_session *)));
214     connect(wsApp, SIGNAL(captureCaptureStopping(capture_session *)),
215             this, SLOT(captureCaptureStopping(capture_session *)));
216     connect(wsApp, SIGNAL(captureCaptureFailed(capture_session *)),
217             this, SLOT(captureCaptureFailed(capture_session *)));
218
219     connect(wsApp, SIGNAL(captureFileOpened(const capture_file*)),
220             this, SLOT(captureFileOpened(const capture_file*)));
221     connect(wsApp, SIGNAL(captureFileReadStarted(const capture_file*)),
222             this, SLOT(captureFileReadStarted(const capture_file*)));
223     connect(wsApp, SIGNAL(captureFileReadFinished(const capture_file*)),
224             this, SLOT(captureFileReadFinished(const capture_file*)));
225     connect(wsApp, SIGNAL(captureFileClosing(const capture_file*)),
226             this, SLOT(captureFileClosing(const capture_file*)));
227     connect(wsApp, SIGNAL(captureFileClosed(const capture_file*)),
228             this, SLOT(captureFileClosed(const capture_file*)));
229     connect(wsApp, SIGNAL(columnsChanged()),
230             this, SLOT(recreatePacketList()));
231     connect(wsApp, SIGNAL(packetDissectionChanged()),
232             this, SLOT(redissectPackets()));
233     connect(wsApp, SIGNAL(appInitialized()),
234             this, SLOT(filterExpressionsChanged()));
235     connect(wsApp, SIGNAL(filterExpressionsChanged()),
236             this, SLOT(filterExpressionsChanged()));
237
238     connect(main_welcome_, SIGNAL(startCapture()),
239             this, SLOT(startCapture()));
240     connect(main_welcome_, SIGNAL(recentFileActivated(QString&)),
241             this, SLOT(openCaptureFile(QString&)));
242     connect(main_welcome_, SIGNAL(pushFilterSyntaxStatus(QString&)),
243             main_ui_->statusBar, SLOT(pushFilterStatus(QString&)));
244     connect(main_welcome_, SIGNAL(popFilterSyntaxStatus()),
245             main_ui_->statusBar, SLOT(popFilterStatus()));
246
247     connect(this, SIGNAL(setCaptureFile(capture_file*)),
248             main_ui_->searchFrame, SLOT(setCaptureFile(capture_file*)));
249     connect(this, SIGNAL(setCaptureFile(capture_file*)),
250             main_ui_->statusBar, SLOT(setCaptureFile(capture_file*)));
251     connect(this, SIGNAL(setCaptureFile(capture_file*)),
252             packet_list_, SLOT(setCaptureFile(capture_file*)));
253     connect(this, SIGNAL(setCaptureFile(capture_file*)),
254             byte_view_tab_, SLOT(setCaptureFile(capture_file*)));
255
256     connect(main_ui_->actionGoNextPacket, SIGNAL(triggered()),
257             packet_list_, SLOT(goNextPacket()));
258     connect(main_ui_->actionGoPreviousPacket, SIGNAL(triggered()),
259             packet_list_, SLOT(goPreviousPacket()));
260     connect(main_ui_->actionGoFirstPacket, SIGNAL(triggered()),
261             packet_list_, SLOT(goFirstPacket()));
262     connect(main_ui_->actionGoLastPacket, SIGNAL(triggered()),
263             packet_list_, SLOT(goLastPacket()));
264
265     connect(main_ui_->actionViewExpandSubtrees, SIGNAL(triggered()),
266             proto_tree_, SLOT(expandSubtrees()));
267     connect(main_ui_->actionViewExpandAll, SIGNAL(triggered()),
268             proto_tree_, SLOT(expandAll()));
269     connect(main_ui_->actionViewCollapseAll, SIGNAL(triggered()),
270             proto_tree_, SLOT(collapseAll()));
271
272     connect(packet_list_->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
273             this, SLOT(setMenusForSelectedPacket()));
274     connect(packet_list_, SIGNAL(packetDissectionChanged()),
275             this, SLOT(redissectPackets()));
276     connect(packet_list_, SIGNAL(setMenusFollowStream()),
277             this, SLOT(setMenusForFollowStream()));
278
279     connect(proto_tree_, SIGNAL(protoItemSelected(QString&)),
280             main_ui_->statusBar, SLOT(pushFieldStatus(QString&)));
281
282     connect(proto_tree_, SIGNAL(protoItemSelected(field_info *)),
283             this, SLOT(setMenusForSelectedTreeRow(field_info *)));
284
285     connect(&file_set_dialog_, SIGNAL(fileSetOpenCaptureFile(QString&)),
286             this, SLOT(openCaptureFile(QString&)));
287
288     QTreeWidget *iface_tree = findChild<QTreeWidget *>("interfaceTree");
289     if (iface_tree) {
290         connect(iface_tree, SIGNAL(itemSelectionChanged()),
291                 this, SLOT(interfaceSelectionChanged()));
292     }
293     connect(main_ui_->welcomePage, SIGNAL(captureFilterSyntaxChanged(bool)),
294             this, SLOT(captureFilterSyntaxChanged(bool)));
295
296     main_ui_->mainStack->setCurrentWidget(main_welcome_);
297 }
298
299 MainWindow::~MainWindow()
300 {
301     delete main_ui_;
302 }
303
304 QString MainWindow::getFilter()
305 {
306     return df_combo_box_->itemText(df_combo_box_->count());
307 }
308
309 void MainWindow::setPipeInputHandler(gint source, gpointer user_data, int *child_process, pipe_input_cb_t input_cb)
310 {
311     pipe_source_        = source;
312     pipe_child_process_ = child_process;
313     pipe_user_data_     = user_data;
314     pipe_input_cb_      = input_cb;
315
316 #ifdef _WIN32
317     /* Tricky to use pipes in win9x, as no concept of wait.  NT can
318        do this but that doesn't cover all win32 platforms.  GTK can do
319        this but doesn't seem to work over processes.  Attempt to do
320        something similar here, start a timer and check for data on every
321        timeout. */
322        /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_input_set_handler: new");*/
323
324     if (pipe_timer_) {
325         disconnect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
326         delete pipe_timer_;
327     }
328
329     pipe_timer_ = new QTimer(this);
330     connect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
331     connect(pipe_timer_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
332     pipe_timer_->start(200);
333 #else
334     if (pipe_notifier_) {
335         disconnect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
336         delete pipe_notifier_;
337     }
338
339     pipe_notifier_ = new QSocketNotifier(pipe_source_, QSocketNotifier::Read);
340     // XXX ui/gtk/gui_utils.c sets the encoding. Do we need to do the same?
341     connect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
342     connect(pipe_notifier_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
343 #endif
344 }
345
346
347 bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
348
349     // The user typed some text. Start filling in a filter.
350     // We may need to be more choosy here. We just need to catch events for the packet list,
351     // proto tree, and main welcome widgets.
352     if (event->type() == QEvent::KeyPress) {
353         QKeyEvent *kevt = static_cast<QKeyEvent *>(event);
354         if (kevt->text().length() > 0 && kevt->text()[0].isPrint()) {
355             df_combo_box_->lineEdit()->insert(kevt->text());
356             df_combo_box_->lineEdit()->setFocus();
357             return true;
358         }
359     }
360
361     return QObject::eventFilter(obj, event);
362 }
363
364 void MainWindow::keyPressEvent(QKeyEvent *event) {
365
366     // Explicitly focus on the display filter combo.
367     if (event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_Slash) {
368         df_combo_box_->setFocus(Qt::ShortcutFocusReason);
369         return;
370     }
371
372     if (wsApp->focusWidget() == main_ui_->goToLineEdit) {
373         if (event->modifiers() == Qt::NoModifier) {
374             if (event->key() == Qt::Key_Escape) {
375                 on_goToCancel_clicked();
376             } else if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
377                 on_goToGo_clicked();
378             }
379         }
380         return; // goToLineEdit didn't want it and we don't either.
381     }
382
383     // Move up & down the packet list.
384     if (event->key() == Qt::Key_F7) {
385         packet_list_->goPreviousPacket();
386     } else if (event->key() == Qt::Key_F8) {
387         packet_list_->goNextPacket();
388     }
389
390     // Move along, citizen.
391     QMainWindow::keyPressEvent(event);
392 }
393
394 void MainWindow::closeEvent(QCloseEvent *event) {
395     saveWindowGeometry();
396
397     /* If we're in the middle of stopping a capture, don't do anything;
398        the user can try deleting the window after the capture stops. */
399     if (capture_stopping_) {
400         event->ignore();
401         return;
402     }
403
404     // Make sure we kill any open dumpcap processes.
405     delete main_welcome_;
406
407     if(!wsApp->isInitialized()) {
408         // If we're still initializing, QCoreApplication::quit() won't
409         // exit properly because we are not in the event loop. This
410         // means that the application won't clean up after itself. We
411         // might want to call wsApp->processEvents() during startup
412         // instead so that we can do a normal exit here.
413         exit(0);
414     }
415 }
416
417 const int min_sensible_dimension = 200;
418 const int geom_animation_duration = 150;
419 void MainWindow::loadWindowGeometry()
420 {
421     QWidget shadow_main(wsApp->desktop());
422     shadow_main.setVisible(false);
423
424     // Start off with the Widget defaults
425     shadow_main.restoreGeometry(saveGeometry());
426
427     // Apply any saved settings
428
429     // Note that we're saving and restoring the outer window frame
430     // position and the inner client area size.
431     if (prefs.gui_geometry_save_position) {
432         shadow_main.move(recent.gui_geometry_main_x, recent.gui_geometry_main_y);
433     }
434
435     // XXX Preferences haven't been loaded at this point. For now we
436     // assume default (true) values for everything.
437
438     if (// prefs.gui_geometry_save_size &&
439             recent.gui_geometry_main_width > min_sensible_dimension &&
440             recent.gui_geometry_main_width > min_sensible_dimension) {
441         shadow_main.resize(recent.gui_geometry_main_width, recent.gui_geometry_main_height);
442     }
443
444     // Let Qt move and resize our window if needed (e.g. if it's offscreen)
445     QByteArray geom = shadow_main.saveGeometry();
446
447     if (strlen (get_conn_cfilter()) < 1) {
448         QPropertyAnimation *pos_anim = new QPropertyAnimation(this, "pos");
449         QPropertyAnimation *size_anim = new QPropertyAnimation(this, "size");
450
451         shadow_main.restoreGeometry(geom);
452
453         pos_anim->setDuration(geom_animation_duration);
454         pos_anim->setStartValue(pos());
455         pos_anim->setEndValue(shadow_main.pos());
456         size_anim->setDuration(geom_animation_duration);
457         size_anim->setStartValue(size());
458         size_anim->setEndValue(shadow_main.size());
459
460         pos_anim->start(QAbstractAnimation::DeleteWhenStopped);
461         size_anim->start(QAbstractAnimation::DeleteWhenStopped);
462     } else {
463         restoreGeometry(geom);
464     }
465
466 #ifndef Q_OS_MAC
467     if (prefs.gui_geometry_save_maximized && recent.gui_geometry_main_maximized) {
468         setWindowState(Qt::WindowMaximized);
469     }
470 #endif
471 }
472
473 void MainWindow::saveWindowGeometry()
474 {
475     if (prefs.gui_geometry_save_position) {
476         recent.gui_geometry_main_x = pos().x();
477         recent.gui_geometry_main_y = pos().y();
478     }
479
480     if (prefs.gui_geometry_save_size) {
481         recent.gui_geometry_main_width = size().width();
482         recent.gui_geometry_main_height = size().height();
483     }
484
485     if (prefs.gui_geometry_save_maximized) {
486         // On OS X this is false when it shouldn't be
487         recent.gui_geometry_main_maximized = isMaximized();
488     }
489 }
490
491 QWidget* MainWindow::getLayoutWidget(layout_pane_content_e type) {
492     switch (type) {
493         case layout_pane_content_none:
494             return &empty_pane_;
495         case layout_pane_content_plist:
496             return packet_list_;
497         case layout_pane_content_pdetails:
498             return proto_tree_;
499         case layout_pane_content_pbytes:
500             return byte_view_tab_;
501         default:
502             g_assert_not_reached();
503             return NULL;
504     }
505 }
506
507 void MainWindow::mergeCaptureFile()
508 {
509     QString file_name = "";
510     QString display_filter = "";
511     dfilter_t *rfcode = NULL;
512     int err;
513
514     if (!cap_file_)
515         return;
516
517     if (prefs.gui_ask_unsaved) {
518         if (cf_has_unsaved_data(cap_file_)) {
519             QMessageBox msg_dialog;
520             gchar *display_basename;
521             int response;
522
523             msg_dialog.setIcon(QMessageBox::Question);
524             /* This file has unsaved data; ask the user whether to save
525                the capture. */
526             if (cap_file_->is_tempfile) {
527                 msg_dialog.setText(tr("Save packets before merging?"));
528                 msg_dialog.setInformativeText(tr("A temporary capture file can't be merged."));
529             } else {
530                 /*
531                  * Format the message.
532                  */
533                 display_basename = g_filename_display_basename(cap_file_->filename);
534                 msg_dialog.setText(QString(tr("Save changes in \"%1\" before merging?")).arg(display_basename));
535                 g_free(display_basename);
536                 msg_dialog.setInformativeText(tr("Changes must be saved before the files can be merged."));
537             }
538
539             msg_dialog.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel);
540             msg_dialog.setDefaultButton(QMessageBox::Save);
541
542             response = msg_dialog.exec();
543
544             switch (response) {
545
546             case QMessageBox::Save:
547                 /* Save the file but don't close it */
548                 saveCaptureFile(cap_file_, FALSE);
549                 break;
550
551             case QMessageBox::Cancel:
552             default:
553                 /* Don't do the merge. */
554                 return;
555             }
556         }
557     }
558
559     for (;;) {
560         CaptureFileDialog merge_dlg(this, cap_file_, display_filter);
561         int file_type;
562         cf_status_t  merge_status;
563         char        *in_filenames[2];
564         char        *tmpname;
565
566         switch (prefs.gui_fileopen_style) {
567
568         case FO_STYLE_LAST_OPENED:
569             /* The user has specified that we should start out in the last directory
570            we looked in.  If we've already opened a file, use its containing
571            directory, if we could determine it, as the directory, otherwise
572            use the "last opened" directory saved in the preferences file if
573            there was one. */
574             /* This is now the default behaviour in file_selection_new() */
575             break;
576
577         case FO_STYLE_SPECIFIED:
578             /* The user has specified that we should always start out in a
579            specified directory; if they've specified that directory,
580            start out by showing the files in that dir. */
581             if (prefs.gui_fileopen_dir[0] != '\0')
582                 merge_dlg.setDirectory(prefs.gui_fileopen_dir);
583             break;
584         }
585
586         if (merge_dlg.merge(file_name)) {
587             if (dfilter_compile(display_filter.toUtf8().constData(), &rfcode)) {
588                 cf_set_rfcode(cap_file_, rfcode);
589             } else {
590                 /* Not valid.  Tell the user, and go back and run the file
591                    selection box again once they dismiss the alert. */
592                 //bad_dfilter_alert_box(top_level, display_filter->str);
593                 QMessageBox::warning(this, tr("Invalid Display Filter"),
594                                      QString(tr("The filter expression %1 isn't a valid display filter. (%2).").arg(display_filter, dfilter_error_msg)),
595                                      QMessageBox::Ok);
596                 continue;
597             }
598         } else {
599             return;
600         }
601
602         file_type = cap_file_->cd_t;
603
604         /* Try to merge or append the two files */
605         tmpname = NULL;
606         if (merge_dlg.mergeType() == 0) {
607             /* chronological order */
608             in_filenames[0] = cap_file_->filename;
609             in_filenames[1] = file_name.toUtf8().data();
610             merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, FALSE);
611         } else if (merge_dlg.mergeType() <= 0) {
612             /* prepend file */
613             in_filenames[0] = file_name.toUtf8().data();
614             in_filenames[1] = cap_file_->filename;
615             merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
616         } else {
617             /* append file */
618             in_filenames[0] = cap_file_->filename;
619             in_filenames[1] = file_name.toUtf8().data();
620             merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
621         }
622
623         if (merge_status != CF_OK) {
624             if (rfcode != NULL)
625                 dfilter_free(rfcode);
626             g_free(tmpname);
627             continue;
628         }
629
630         cf_close(cap_file_);
631
632         /* Try to open the merged capture file. */
633         cfile.window = this;
634         if (cf_open(&cfile, tmpname, TRUE /* temporary file */, &err) != CF_OK) {
635             /* We couldn't open it; fail. */
636             cfile.window = NULL;
637             if (rfcode != NULL)
638                 dfilter_free(rfcode);
639             g_free(tmpname);
640             return;
641         }
642
643         /* Attach the new read filter to "cf" ("cf_open()" succeeded, so
644            it closed the previous capture file, and thus destroyed any
645            previous read filter attached to "cf"). */
646         cfile.rfcode = rfcode;
647
648         switch (cf_read(&cfile, FALSE)) {
649
650         case CF_READ_OK:
651         case CF_READ_ERROR:
652             /* Just because we got an error, that doesn't mean we were unable
653              to read any of the file; we handle what we could get from the
654              file. */
655             break;
656
657         case CF_READ_ABORTED:
658             /* The user bailed out of re-reading the capture file; the
659              capture file has been closed - just free the capture file name
660              string and return (without changing the last containing
661              directory). */
662             g_free(tmpname);
663             return;
664         }
665
666         /* Save the name of the containing directory specified in the path name,
667            if any; we can write over cf_merged_name, which is a good thing, given that
668            "get_dirname()" does write over its argument. */
669         wsApp->setLastOpenDir(get_dirname(tmpname));
670         g_free(tmpname);
671         df_combo_box_->setEditText(display_filter);
672         main_ui_->statusBar->showExpert();
673         return;
674     }
675
676 }
677
678 void MainWindow::importCaptureFile() {
679     ImportTextDialog import_dlg;
680
681     if (!testCaptureFileClose(FALSE, *new QString(tr(" before importing a new capture"))))
682         return;
683
684     import_dlg.exec();
685
686     if (import_dlg.result() != QDialog::Accepted) {
687         main_ui_->mainStack->setCurrentWidget(main_welcome_);
688         return;
689     }
690
691     openCaptureFile(import_dlg.capfileName());
692 }
693
694 void MainWindow::saveCaptureFile(capture_file *cf, bool stay_closed) {
695     QString file_name;
696     gboolean discard_comments;
697
698     if (cf->is_tempfile) {
699         /* This is a temporary capture file, so saving it means saving
700            it to a permanent file.  Prompt the user for a location
701            to which to save it.  Don't require that the file format
702            support comments - if it's a temporary capture file, it's
703            probably pcap-ng, which supports comments and, if it's
704            not pcap-ng, let the user decide what they want to do
705            if they've added comments. */
706         saveAsCaptureFile(cf, FALSE, stay_closed);
707     } else {
708         if (cf->unsaved_changes) {
709             cf_write_status_t status;
710
711             /* This is not a temporary capture file, but it has unsaved
712                changes, so saving it means doing a "safe save" on top
713                of the existing file, in the same format - no UI needed
714                unless the file has comments and the file's format doesn't
715                support them.
716
717                If the file has comments, does the file's format support them?
718                If not, ask the user whether they want to discard the comments
719                or choose a different format. */
720             switch (CaptureFileDialog::checkSaveAsWithComments(this, cf, cf->cd_t)) {
721
722             case SAVE:
723                 /* The file can be saved in the specified format as is;
724                    just drive on and save in the format they selected. */
725                 discard_comments = FALSE;
726                 break;
727
728             case SAVE_WITHOUT_COMMENTS:
729                 /* The file can't be saved in the specified format as is,
730                    but it can be saved without the comments, and the user
731                    said "OK, discard the comments", so save it in the
732                    format they specified without the comments. */
733                 discard_comments = TRUE;
734                 break;
735
736             case SAVE_IN_ANOTHER_FORMAT:
737                 /* There are file formats in which we can save this that
738                    support comments, and the user said not to delete the
739                    comments.  Do a "Save As" so the user can select
740                    one of those formats and choose a file name. */
741                 saveAsCaptureFile(cf, TRUE, stay_closed);
742                 return;
743
744             case CANCELLED:
745                 /* The user said "forget it".  Just return. */
746                 return;
747
748             default:
749                 /* Squelch warnings that discard_comments is being used
750                    uninitialized. */
751                 g_assert_not_reached();
752                 return;
753             }
754
755             /* XXX - cf->filename might get freed out from under us, because
756                the code path through which cf_save_packets() goes currently
757                closes the current file and then opens and reloads the saved file,
758                so make a copy and free it later. */
759             file_name = cf->filename;
760             status = cf_save_packets(cf, file_name.toUtf8().constData(), cf->cd_t, cf->iscompressed,
761                                      discard_comments, stay_closed);
762             switch (status) {
763
764             case CF_WRITE_OK:
765                 /* The save succeeded; we're done.
766                    If we discarded comments, redraw the packet list to reflect
767                    any packets that no longer have comments. */
768                 if (discard_comments)
769                     packet_list_queue_draw();
770
771                 cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
772                 updateForUnsavedChanges(); // we update the title bar to remove the *
773                 break;
774
775             case CF_WRITE_ERROR:
776                 /* The write failed.
777                    XXX - OK, what do we do now?  Let them try a
778                    "Save As", in case they want to try to save to a
779                    different directory r file system? */
780                 break;
781
782             case CF_WRITE_ABORTED:
783                 /* The write was aborted; just drive on. */
784                 break;
785             }
786         }
787         /* Otherwise just do nothing. */
788     }
789 }
790
791 void MainWindow::saveAsCaptureFile(capture_file *cf, bool must_support_comments, bool stay_closed) {
792     QString file_name = "";
793     int file_type;
794     gboolean compressed;
795     cf_write_status_t status;
796     gchar   *dirname;
797     gboolean discard_comments = FALSE;
798
799     if (!cf) {
800         return;
801     }
802
803     for (;;) {
804         CaptureFileDialog save_as_dlg(this, cf);
805
806         switch (prefs.gui_fileopen_style) {
807
808         case FO_STYLE_LAST_OPENED:
809             /* The user has specified that we should start out in the last directory
810                we looked in.  If we've already opened a file, use its containing
811                directory, if we could determine it, as the directory, otherwise
812                use the "last opened" directory saved in the preferences file if
813                there was one. */
814             /* This is now the default behaviour in file_selection_new() */
815             break;
816
817         case FO_STYLE_SPECIFIED:
818             /* The user has specified that we should always start out in a
819                specified directory; if they've specified that directory,
820                start out by showing the files in that dir. */
821             if (prefs.gui_fileopen_dir[0] != '\0')
822                 save_as_dlg.setDirectory(prefs.gui_fileopen_dir);
823             break;
824         }
825
826         /* If the file has comments, does the format the user selected
827            support them?  If not, ask the user whether they want to
828            discard the comments or choose a different format. */
829         switch(save_as_dlg.saveAs(file_name, must_support_comments)) {
830
831         case SAVE:
832             /* The file can be saved in the specified format as is;
833                just drive on and save in the format they selected. */
834             discard_comments = FALSE;
835             break;
836
837         case SAVE_WITHOUT_COMMENTS:
838             /* The file can't be saved in the specified format as is,
839                but it can be saved without the comments, and the user
840                said "OK, discard the comments", so save it in the
841                format they specified without the comments. */
842             discard_comments = TRUE;
843             break;
844
845         case SAVE_IN_ANOTHER_FORMAT:
846             /* There are file formats in which we can save this that
847                support comments, and the user said not to delete the
848                comments.  The combo box of file formats has had the
849                formats that don't support comments trimmed from it,
850                so run the dialog again, to let the user decide
851                whether to save in one of those formats or give up. */
852             discard_comments = FALSE;
853             must_support_comments = TRUE;
854             continue;
855
856         case CANCELLED:
857             /* The user said "forget it".  Just get rid of the dialog box
858                and return. */
859             return;
860         }
861         file_type = save_as_dlg.selectedFileType();
862         compressed = save_as_dlg.isCompressed();
863
864         fileAddExtension(file_name, file_type, compressed);
865
866 //#ifndef _WIN32
867 //        /* If the file exists and it's user-immutable or not writable,
868 //                       ask the user whether they want to override that. */
869 //        if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
870 //            /* They don't.  Let them try another file name or cancel. */
871 //            continue;
872 //        }
873 //#endif
874
875         /* Attempt to save the file */
876         status = cf_save_packets(cf, file_name.toUtf8().constData(), file_type, compressed,
877                                  discard_comments, stay_closed);
878         switch (status) {
879
880         case CF_WRITE_OK:
881             /* The save succeeded; we're done. */
882             /* Save the directory name for future file dialogs. */
883             dirname = get_dirname(file_name.toUtf8().data());  /* Overwrites cf_name */
884             set_last_open_dir(dirname);
885             /* If we discarded comments, redraw the packet list to reflect
886                any packets that no longer have comments. */
887             if (discard_comments)
888                 packet_list_queue_draw();
889
890             cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
891             updateForUnsavedChanges(); // we update the title bar to remove the *
892             return;
893
894         case CF_WRITE_ERROR:
895             /* The save failed; let the user try again. */
896             continue;
897
898         case CF_WRITE_ABORTED:
899             /* The user aborted the save; just return. */
900             return;
901         }
902     }
903     return;
904 }
905
906 void MainWindow::exportSelectedPackets() {
907     QString file_name = "";
908     int file_type;
909     gboolean compressed;
910     packet_range_t range;
911     cf_write_status_t status;
912     gchar   *dirname;
913     gboolean discard_comments = FALSE;
914
915     if (!cap_file_)
916         return;
917
918     /* Init the packet range */
919     packet_range_init(&range, cap_file_);
920     range.process_filtered = TRUE;
921     range.include_dependents = TRUE;
922
923     for (;;) {
924         CaptureFileDialog esp_dlg(this, cap_file_);
925
926         switch (prefs.gui_fileopen_style) {
927
928         case FO_STYLE_LAST_OPENED:
929             /* The user has specified that we should start out in the last directory
930                we looked in.  If we've already opened a file, use its containing
931                directory, if we could determine it, as the directory, otherwise
932                use the "last opened" directory saved in the preferences file if
933                there was one. */
934             /* This is now the default behaviour in file_selection_new() */
935             break;
936
937         case FO_STYLE_SPECIFIED:
938             /* The user has specified that we should always start out in a
939                specified directory; if they've specified that directory,
940                start out by showing the files in that dir. */
941             if (prefs.gui_fileopen_dir[0] != '\0')
942                 esp_dlg.setDirectory(prefs.gui_fileopen_dir);
943             break;
944         }
945
946         /* If the file has comments, does the format the user selected
947            support them?  If not, ask the user whether they want to
948            discard the comments or choose a different format. */
949         switch(esp_dlg.exportSelectedPackets(file_name, &range)) {
950
951         case SAVE:
952             /* The file can be saved in the specified format as is;
953                just drive on and save in the format they selected. */
954             discard_comments = FALSE;
955             break;
956
957         case SAVE_WITHOUT_COMMENTS:
958             /* The file can't be saved in the specified format as is,
959                but it can be saved without the comments, and the user
960                said "OK, discard the comments", so save it in the
961                format they specified without the comments. */
962             discard_comments = TRUE;
963             break;
964
965         case SAVE_IN_ANOTHER_FORMAT:
966             /* There are file formats in which we can save this that
967                support comments, and the user said not to delete the
968                comments.  The combo box of file formats has had the
969                formats that don't support comments trimmed from it,
970                so run the dialog again, to let the user decide
971                whether to save in one of those formats or give up. */
972             discard_comments = FALSE;
973             continue;
974
975         case CANCELLED:
976             /* The user said "forget it".  Just get rid of the dialog box
977                and return. */
978             return;
979         }
980
981         /*
982          * Check that we're not going to save on top of the current
983          * capture file.
984          * We do it here so we catch all cases ...
985          * Unfortunately, the file requester gives us an absolute file
986          * name and the read file name may be relative (if supplied on
987          * the command line). From Joerg Mayer.
988          */
989         if (files_identical(cap_file_->filename, file_name.toUtf8().constData())) {
990             QMessageBox msg_box;
991             gchar *display_basename = g_filename_display_basename(file_name.toUtf8().constData());
992
993             msg_box.setIcon(QMessageBox::Critical);
994             msg_box.setText(QString(tr("Unable to export to \"%1\".").arg(display_basename)));
995             msg_box.setInformativeText(tr("You cannot export packets to the current capture file."));
996             msg_box.setStandardButtons(QMessageBox::Ok);
997             msg_box.setDefaultButton(QMessageBox::Ok);
998             msg_box.exec();
999             g_free(display_basename);
1000             continue;
1001         }
1002
1003         file_type = esp_dlg.selectedFileType();
1004         compressed = esp_dlg.isCompressed();
1005         fileAddExtension(file_name, file_type, compressed);
1006
1007 //#ifndef _WIN32
1008 //        /* If the file exists and it's user-immutable or not writable,
1009 //                       ask the user whether they want to override that. */
1010 //        if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
1011 //            /* They don't.  Let them try another file name or cancel. */
1012 //            continue;
1013 //        }
1014 //#endif
1015
1016         /* Attempt to save the file */
1017         status = cf_export_specified_packets(cap_file_, file_name.toUtf8().constData(), &range, file_type, compressed);
1018         switch (status) {
1019
1020         case CF_WRITE_OK:
1021             /* The save succeeded; we're done. */
1022             /* Save the directory name for future file dialogs. */
1023             dirname = get_dirname(file_name.toUtf8().data());  /* Overwrites cf_name */
1024             set_last_open_dir(dirname);
1025             /* If we discarded comments, redraw the packet list to reflect
1026                any packets that no longer have comments. */
1027             if (discard_comments)
1028                 packet_list_queue_draw();
1029             return;
1030
1031         case CF_WRITE_ERROR:
1032             /* The save failed; let the user try again. */
1033             continue;
1034
1035         case CF_WRITE_ABORTED:
1036             /* The user aborted the save; just return. */
1037             return;
1038         }
1039     }
1040     return;
1041 }
1042
1043 void MainWindow::exportDissections(export_type_e export_type) {
1044     ExportDissectionDialog ed_dlg(this, cap_file_, export_type);
1045     packet_range_t range;
1046
1047     if (!cap_file_)
1048         return;
1049
1050     /* Init the packet range */
1051     packet_range_init(&range, cap_file_);
1052     range.process_filtered = TRUE;
1053     range.include_dependents = TRUE;
1054
1055     ed_dlg.exec();
1056 }
1057
1058 void MainWindow::fileAddExtension(QString &file_name, int file_type, bool compressed) {
1059     QString file_name_lower;
1060     QString file_suffix;
1061     GSList  *extensions_list;
1062     gboolean add_extension;
1063
1064     /*
1065      * Append the default file extension if there's none given by
1066      * the user or if they gave one that's not one of the valid
1067      * extensions for the file type.
1068      */
1069     file_name_lower = file_name.toLower();
1070     extensions_list = wtap_get_file_extensions_list(file_type, FALSE);
1071     if (extensions_list != NULL) {
1072         GSList *extension;
1073
1074         /* We have one or more extensions for this file type.
1075            Start out assuming we need to add the default one. */
1076         add_extension = TRUE;
1077
1078         /* OK, see if the file has one of those extensions. */
1079         for (extension = extensions_list; extension != NULL;
1080              extension = g_slist_next(extension)) {
1081             file_suffix += tr(".") + (char *)extension->data;
1082             if (file_name_lower.endsWith(file_suffix)) {
1083                 /*
1084                  * The file name has one of the extensions for
1085                  * this file type.
1086                  */
1087                 add_extension = FALSE;
1088                 break;
1089             }
1090             file_suffix += ".gz";
1091             if (file_name_lower.endsWith(file_suffix)) {
1092                 /*
1093                  * The file name has one of the extensions for
1094                  * this file type.
1095                  */
1096                 add_extension = FALSE;
1097                 break;
1098             }
1099         }
1100     } else {
1101         /* We have no extensions for this file type.  Don't add one. */
1102         add_extension = FALSE;
1103     }
1104     if (add_extension) {
1105         if (wtap_default_file_extension(file_type) != NULL) {
1106             file_name += tr(".") + wtap_default_file_extension(file_type);
1107             if (compressed) {
1108                 file_name += ".gz";
1109             }
1110         }
1111     }
1112 }
1113
1114 bool MainWindow::testCaptureFileClose(bool from_quit, QString &before_what) {
1115     bool capture_in_progress = FALSE;
1116
1117     if (!cap_file_ || cap_file_->state == FILE_CLOSED)
1118         return true; /* Already closed, nothing to do */
1119
1120 #ifdef HAVE_LIBPCAP
1121     if (cap_file_->state == FILE_READ_IN_PROGRESS) {
1122         /* This is true if we're reading a capture file *or* if we're doing
1123          a live capture.  If we're reading a capture file, the main loop
1124          is busy reading packets, and only accepting input from the
1125          progress dialog, so we can't get here, so this means we're
1126          doing a capture. */
1127         capture_in_progress = TRUE;
1128     }
1129 #endif
1130
1131     if (prefs.gui_ask_unsaved) {
1132         if (cf_has_unsaved_data(cap_file_) || capture_in_progress) {
1133             QMessageBox msg_dialog;
1134             QString question;
1135             QPushButton *saveButton;
1136             QPushButton *discardButton;
1137
1138             msg_dialog.setIcon(QMessageBox::Question);
1139
1140             /* This file has unsaved data or there's a capture in
1141                progress; ask the user whether to save the data. */
1142             if (cap_file_->is_tempfile) {
1143
1144                 msg_dialog.setText(tr("You have unsaved packets"));
1145                 msg_dialog.setInformativeText(tr("They will be lost if you don't save them."));
1146
1147                 if (capture_in_progress) {
1148                     question.append(tr("Do you want to stop the capture and save the captured packets"));
1149                 } else {
1150                     question.append(tr("Do you want to save the captured packets"));
1151                 }
1152                 question.append(before_what).append(tr("?"));
1153                 msg_dialog.setInformativeText(question);
1154
1155
1156             } else {
1157                 /*
1158                  * Format the message.
1159                  */
1160                 if (capture_in_progress) {
1161                     question.append(tr("Do you want to stop the capture and save the captured packets"));
1162                     question.append(before_what).append(tr("?"));
1163                     msg_dialog.setInformativeText(tr("Your captured packets will be lost if you don't save them."));
1164                 } else {
1165                     gchar *display_basename = g_filename_display_basename(cap_file_->filename);
1166                     question.append(QString(tr("Do you want to save the changes you've made to the capture file \"%1\"%2?"))
1167                                     .arg(display_basename)
1168                                     .arg(before_what)
1169                                     );
1170                     g_free(display_basename);
1171                     msg_dialog.setInformativeText(tr("Your changes will be lost if you don't save them."));
1172                 }
1173             }
1174
1175             // XXX Text comes from ui/gtk/stock_icons.[ch]
1176             // Note that the button roles differ from the GTK+ version.
1177             // Cancel = RejectRole
1178             // Save = AcceptRole
1179             // Don't Save = DestructiveRole
1180             msg_dialog.addButton(QMessageBox::Cancel);
1181
1182             if (capture_in_progress) {
1183                 saveButton = msg_dialog.addButton(tr("Stop and Save"), QMessageBox::AcceptRole);
1184             } else {
1185                 saveButton = msg_dialog.addButton(QMessageBox::Save);
1186             }
1187             msg_dialog.setDefaultButton(saveButton);
1188
1189             if (from_quit) {
1190                 if (cap_file_->state == FILE_READ_IN_PROGRESS) {
1191                     discardButton = msg_dialog.addButton(tr("Stop and Quit without Saving"),
1192                                                          QMessageBox::DestructiveRole);
1193                 } else {
1194                     discardButton = msg_dialog.addButton(tr("Quit without Saving"),
1195                                                          QMessageBox::DestructiveRole);
1196                 }
1197             } else {
1198                 if (capture_in_progress) {
1199                     discardButton = msg_dialog.addButton(tr("Stop and Continue without Saving"),
1200                                                          QMessageBox::DestructiveRole);
1201                 } else {
1202                     discardButton = msg_dialog.addButton(QMessageBox::Discard);
1203                 }
1204             }
1205
1206             msg_dialog.exec();
1207             /* According to the Qt doc:
1208              * when using QMessageBox with custom buttons, exec() function returns an opaque value.
1209              *
1210              * Therefore we should use clickedButton() to determine which button was clicked. */
1211
1212             if(msg_dialog.clickedButton() == saveButton)
1213             {
1214 #ifdef HAVE_LIBPCAP
1215                 /* If there's a capture in progress, we have to stop the capture
1216              and then do the save. */
1217                 if (capture_in_progress)
1218                     captureStop();
1219 #endif
1220                 /* Save the file and close it */
1221                 saveCaptureFile(cap_file_, TRUE);
1222             }
1223             else if(msg_dialog.clickedButton() == discardButton)
1224             {
1225 #ifdef HAVE_LIBPCAP
1226                 /*
1227                  * If there's a capture in progress; we have to stop the capture
1228                  * and then do the close.
1229                  */
1230                 if (capture_in_progress)
1231                     captureStop();
1232 #endif
1233                 /* Just close the file, discarding changes */
1234                 cf_close(cap_file_);
1235                 return true;
1236             }
1237             else    //cancelButton or some other unspecified button
1238             {
1239                 return false;
1240             }
1241
1242         } else {
1243             /* Unchanged file, just close it */
1244             cf_close(cap_file_);
1245         }
1246     } else {
1247         /* User asked not to be bothered by those prompts, just close it.
1248          XXX - should that apply only to saving temporary files? */
1249 #ifdef HAVE_LIBPCAP
1250         /* If there's a capture in progress, we have to stop the capture
1251            and then do the close. */
1252         if (capture_in_progress)
1253             captureStop();
1254 #endif
1255         cf_close(cap_file_);
1256     }
1257
1258     return true; /* File closed */
1259 }
1260
1261 void MainWindow::captureStop() {
1262     stopCapture();
1263
1264     while(cap_file_ && cap_file_->state == FILE_READ_IN_PROGRESS) {
1265         WiresharkApplication::processEvents();
1266     }
1267 }
1268
1269 // Titlebar
1270 void MainWindow::setTitlebarForCaptureFile()
1271 {
1272     if (cap_file_ && cap_file_->filename) {
1273         //
1274         // Qt *REALLY* doesn't like windows that sometimes have a
1275         // title set with setWindowTitle() and other times have a
1276         // file path set; apparently, once you've set the title
1277         // with setWindowTitle(), it sticks, and setWindowFilePath()
1278         // has no effect.  It appears to can clear the title with
1279         // setWindowTitle(NULL), but that clears the actual title in
1280         // the title bar, and setWindowFilePath() then, I guess, sees
1281         // that there's already a file path, and does nothing, leaving
1282         // the title bar empty.  So you then have to clear the file path
1283         // with setWindowFilePath(NULL), and then set it.
1284         //
1285         // Maybe there's a #include "you're holding it wrong" here.
1286         // However, I really don't want to hear from people who think
1287         // that a window can never be associated with something other
1288         // than a user file at time T1 and with a user file at time T2,
1289         // given that, in Wireshark, a window can be associated with a
1290         // live capture at time T1 and then, after you've saved the live
1291         // capture to a user file, associated with a user file at time T2.
1292         //
1293         if (cap_file_->is_tempfile) {
1294             //
1295             // For a temporary file, put the source of the data
1296             // in the window title, not whatever random pile
1297             // of characters is the last component of the path
1298             // name.
1299             //
1300             // XXX - on non-Mac platforms, put in the application
1301             // name?
1302             //
1303             gchar *window_name;
1304             setWindowFilePath(NULL);
1305             window_name = g_strdup_printf("Capturing from %s[*]", cf_get_tempfile_source(cap_file_)); //TODO : Fix Translate
1306             setWindowTitle(window_name);
1307             g_free(window_name);
1308         } else {
1309             //
1310             // For a user file, set the full path; that way,
1311             // for OS X, it'll set the "proxy icon".  Qt
1312             // handles extracting the last component.
1313             //
1314             // Sadly, some UN*Xes don't necessarily use UTF-8
1315             // for their file names, so we have to map the
1316             // file path to UTF-8.  If that fails, we're somewhat
1317             // stuck.
1318             //
1319             char *utf8_filename = g_filename_to_utf8(cap_file_->filename,
1320                                                      -1,
1321                                                      NULL,
1322                                                      NULL,
1323                                                      NULL);
1324             if (utf8_filename == NULL) {
1325                 // So what the heck else can we do here?
1326                 setWindowTitle(tr("(File name can't be mapped to UTF-8)"));
1327             } else {
1328                 setWindowTitle(NULL);
1329                 setWindowFilePath(NULL);
1330                 setWindowFilePath(utf8_filename);
1331                 g_free(utf8_filename);
1332             }
1333         }
1334         setWindowModified(cf_has_unsaved_data(cap_file_));
1335     } else {
1336         /* We have no capture file. */
1337         setWindowFilePath(NULL);
1338         setWindowTitle(tr("The Wireshark Network Analyzer"));
1339     }
1340 }
1341
1342 void MainWindow::setTitlebarForSelectedTreeRow()
1343 {
1344     setWindowTitle(tr("The Wireshark Network Analyzer"));
1345 }
1346
1347
1348 void MainWindow::setTitlebarForCaptureInProgress()
1349 {
1350     gchar *window_name;
1351
1352     setWindowFilePath(NULL);
1353     if (cap_file_) {
1354         window_name = g_strdup_printf("Capturing from %s", cf_get_tempfile_source(cap_file_)); //TODO : Fix Translate
1355         setWindowTitle(window_name);
1356         g_free(window_name);
1357     } else {
1358         /* We have no capture in progress. */
1359         setWindowTitle(tr("The Wireshark Network Analyzer"));
1360     }
1361 }
1362
1363 // Menu state
1364
1365 void MainWindow::setMenusForFollowStream()
1366 {
1367     if (!cap_file_)
1368         return;
1369
1370     if (!cap_file_->edt)
1371         return;
1372
1373     main_ui_->actionAnalyzeFollowTCPStream->setEnabled(false);
1374     main_ui_->actionAnalyzeFollowUDPStream->setEnabled(false);
1375     main_ui_->actionAnalyzeFollowSSLStream->setEnabled(false);
1376
1377     if (cap_file_->edt->pi.ipproto == IP_PROTO_TCP)
1378     {
1379         main_ui_->actionAnalyzeFollowTCPStream->setEnabled(true);
1380     }
1381
1382     if (cap_file_->edt->pi.ipproto == IP_PROTO_UDP)
1383     {
1384         main_ui_->actionAnalyzeFollowUDPStream->setEnabled(true);
1385     }
1386
1387     if ( epan_dissect_packet_contains_field(cap_file_->edt, "ssl") )
1388     {
1389         main_ui_->actionAnalyzeFollowSSLStream->setEnabled(true);
1390     }
1391 }
1392
1393 /* Enable or disable menu items based on whether you have a capture file
1394    you've finished reading and, if you have one, whether it's been saved
1395    and whether it could be saved except by copying the raw packet data. */
1396 void MainWindow::setMenusForCaptureFile(bool force_disable)
1397 {
1398     if (force_disable || cap_file_ == NULL || cap_file_->state == FILE_READ_IN_PROGRESS) {
1399         /* We have no capture file or we're currently reading a file */
1400         main_ui_->actionFileMerge->setEnabled(false);
1401         main_ui_->actionFileClose->setEnabled(false);
1402         main_ui_->actionFileSave->setEnabled(false);
1403         main_ui_->actionFileSaveAs->setEnabled(false);
1404         main_ui_->actionSummary->setEnabled(false);
1405         main_ui_->actionFileExportPackets->setEnabled(false);
1406         main_ui_->menuFileExportPacketDissections->setEnabled(false);
1407         main_ui_->actionFileExportPacketBytes->setEnabled(false);
1408         main_ui_->actionFileExportSSLSessionKeys->setEnabled(false);
1409         main_ui_->menuFileExportObjects->setEnabled(false);
1410         main_ui_->actionViewReload->setEnabled(false);
1411     } else {
1412         main_ui_->actionFileMerge->setEnabled(cf_can_write_with_wiretap(cap_file_));
1413
1414         main_ui_->actionFileClose->setEnabled(true);
1415         main_ui_->actionFileSave->setEnabled(cf_can_save(cap_file_));
1416         main_ui_->actionFileSaveAs->setEnabled(cf_can_save_as(cap_file_));
1417         main_ui_->actionSummary->setEnabled(true);
1418         /*
1419          * "Export Specified Packets..." should be available only if
1420          * we can write the file out in at least one format.
1421          */
1422         main_ui_->actionFileExportPackets->setEnabled(cf_can_write_with_wiretap(cap_file_));
1423         main_ui_->menuFileExportPacketDissections->setEnabled(true);
1424         main_ui_->actionFileExportPacketBytes->setEnabled(true);
1425         main_ui_->actionFileExportSSLSessionKeys->setEnabled(true);
1426         main_ui_->menuFileExportObjects->setEnabled(true);
1427         main_ui_->actionViewReload->setEnabled(true);
1428     }
1429 }
1430
1431 void MainWindow::setMenusForCaptureInProgress(bool capture_in_progress) {
1432     /* Either a capture was started or stopped; in either case, it's not
1433        in the process of stopping, so allow quitting. */
1434
1435     main_ui_->actionFileOpen->setEnabled(!capture_in_progress);
1436     main_ui_->menuOpenRecentCaptureFile->setEnabled(!capture_in_progress);
1437     main_ui_->menuFileExportPacketDissections->setEnabled(capture_in_progress);
1438     main_ui_->actionFileExportPacketBytes->setEnabled(capture_in_progress);
1439     main_ui_->actionFileExportSSLSessionKeys->setEnabled(capture_in_progress);
1440     main_ui_->menuFileExportObjects->setEnabled(capture_in_progress);
1441     main_ui_->menuFileSet->setEnabled(!capture_in_progress);
1442     main_ui_->actionFileQuit->setEnabled(true);
1443
1444     main_ui_->actionSummary->setEnabled(capture_in_progress);
1445
1446     qDebug() << "FIX: packet list heading menu sensitivity";
1447     //    set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortAscending",
1448     //                         !capture_in_progress);
1449     //    set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortDescending",
1450     //                         !capture_in_progress);
1451     //    set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/NoSorting",
1452     //                         !capture_in_progress);
1453
1454 #ifdef HAVE_LIBPCAP
1455     main_ui_->actionCaptureOptions->setEnabled(!capture_in_progress);
1456     main_ui_->actionStartCapture->setEnabled(!capture_in_progress);
1457     main_ui_->actionStartCapture->setChecked(capture_in_progress);
1458     main_ui_->actionStopCapture->setEnabled(capture_in_progress);
1459     main_ui_->actionCaptureRestart->setEnabled(capture_in_progress);
1460 #endif /* HAVE_LIBPCAP */
1461
1462 }
1463
1464 void MainWindow::setMenusForCaptureStopping() {
1465     main_ui_->actionFileQuit->setEnabled(false);
1466     main_ui_->actionSummary->setEnabled(false);
1467 #ifdef HAVE_LIBPCAP
1468     main_ui_->actionStartCapture->setChecked(false);
1469     main_ui_->actionStopCapture->setEnabled(false);
1470     main_ui_->actionCaptureRestart->setEnabled(false);
1471 #endif /* HAVE_LIBPCAP */
1472 }
1473
1474 void MainWindow::setForCapturedPackets(bool have_captured_packets)
1475 {
1476     main_ui_->actionFilePrint->setEnabled(have_captured_packets);
1477
1478 //    set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/Print",
1479 //                         have_captured_packets);
1480
1481     main_ui_->actionEditFindPacket->setEnabled(have_captured_packets);
1482     main_ui_->actionEditFindNext->setEnabled(have_captured_packets);
1483     main_ui_->actionEditFindPrevious->setEnabled(have_captured_packets);
1484 //    set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/ViewMenu/ZoomIn",
1485 //                         have_captured_packets);
1486 //    set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/ViewMenu/ZoomOut",
1487 //                         have_captured_packets);
1488 //    set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/ViewMenu/NormalSize",
1489 //                         have_captured_packets);
1490
1491     main_ui_->actionGoGoToPacket->setEnabled(have_captured_packets);
1492     main_ui_->actionGoPreviousPacket->setEnabled(have_captured_packets);
1493     main_ui_->actionGoNextPacket->setEnabled(have_captured_packets);
1494     main_ui_->actionGoFirstPacket->setEnabled(have_captured_packets);
1495     main_ui_->actionGoLastPacket->setEnabled(have_captured_packets);
1496
1497 //    set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/GoMenu/PreviousPacketInConversation",
1498 //                         have_captured_packets);
1499 //    set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/GoMenu/NextPacketInConversation",
1500 //                         have_captured_packets);
1501 //    set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/StatisticsMenu/Summary",
1502 //                         have_captured_packets);
1503 //    set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/StatisticsMenu/ProtocolHierarchy",
1504 //                         have_captured_packets);
1505 }
1506
1507 void MainWindow::setMenusForFileSet(bool enable_list_files) {
1508     bool enable_next = fileset_get_next() != NULL && enable_list_files;
1509     bool enable_prev = fileset_get_previous() != NULL && enable_list_files;
1510
1511     main_ui_->actionFileSetListFiles->setEnabled(enable_list_files);
1512     main_ui_->actionFileSetNextFile->setEnabled(enable_next);
1513     main_ui_->actionFileSetPreviousFile->setEnabled(enable_prev);
1514 }
1515
1516 void MainWindow::updateForUnsavedChanges() {
1517     setTitlebarForCaptureFile();
1518     setMenusForCaptureFile();
1519 //    set_toolbar_for_capture_file(cf);
1520
1521 }
1522
1523 /* Update main window items based on whether there's a capture in progress. */
1524 void MainWindow::setForCaptureInProgress(gboolean capture_in_progress)
1525 {
1526     setMenusForCaptureInProgress(capture_in_progress);
1527
1528 //#ifdef HAVE_LIBPCAP
1529 //    set_toolbar_for_capture_in_progress(capture_in_progress);
1530
1531 //    set_capture_if_dialog_for_capture_in_progress(capture_in_progress);
1532 //#endif
1533 }
1534
1535 /*
1536  * Editor modelines
1537  *
1538  * Local Variables:
1539  * c-basic-offset: 4
1540  * tab-width: 8
1541  * indent-tabs-mode: nil
1542  * End:
1543  *
1544  * ex: set shiftwidth=4 tabstop=8 expandtab:
1545  * :indentSize=4:tabSize=8:noTabs=true:
1546  */