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