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