Push initial directory code up into CaptureFileDialog.
[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 <wsutil/ws_version_info.h>
29 #include <epan/prefs.h>
30 #include <epan/stats_tree_priv.h>
31 #include <epan/plugin_if.h>
32
33 #ifdef HAVE_LIBPCAP
34 #include "ui/capture.h"
35 #include <capchild/capture_session.h>
36 #endif
37
38 #include "ui/alert_box.h"
39 #ifdef HAVE_LIBPCAP
40 #include "ui/capture_ui_utils.h"
41 #endif
42 #include "ui/capture_globals.h"
43 #include "ui/main_statusbar.h"
44 #include "ui/recent.h"
45 #include "ui/util.h"
46 #include "ui/preference_utils.h"
47
48 #include "byte_view_tab.h"
49 #include "display_filter_edit.h"
50 #include "export_dissection_dialog.h"
51 #include "funnel_statistics.h"
52 #include "import_text_dialog.h"
53 #include "packet_list.h"
54 #include "proto_tree.h"
55 #include "simple_dialog.h"
56 #include "stock_icon.h"
57 #include "tap_parameter_dialog.h"
58 #include "wireless_frame.h"
59 #include "wireshark_application.h"
60
61 #include "qt_ui_utils.h"
62
63 #include <QAction>
64 #include <QActionGroup>
65 #include <QDesktopWidget>
66 #include <QKeyEvent>
67 #include <QMessageBox>
68 #include <QMetaObject>
69 #include <QMimeData>
70 #include <QTabWidget>
71 #include <QToolButton>
72 #include <QTreeWidget>
73 #include <QUrl>
74
75 #if defined(QT_MACEXTRAS_LIB) && QT_VERSION < QT_VERSION_CHECK(5, 2, 1)
76 #include <QtMacExtras/QMacNativeToolBar>
77 #endif
78
79
80 //menu_recent_file_write_all
81
82 // If we ever add support for multiple windows this will need to be replaced.
83 static MainWindow *gbl_cur_main_window_ = NULL;
84
85 void pipe_input_set_handler(gint source, gpointer user_data, ws_process_id *child_process, pipe_input_cb_t input_cb)
86 {
87     gbl_cur_main_window_->setPipeInputHandler(source, user_data, child_process, input_cb);
88 }
89
90 static void plugin_if_mainwindow_apply_filter(gconstpointer user_data)
91 {
92     if (!gbl_cur_main_window_ || !user_data)
93         return;
94
95     GHashTable * data_set = (GHashTable *) user_data;
96
97     if (g_hash_table_lookup_extended(data_set, "filter_string", NULL, NULL)) {
98         QString filter((const char *)g_hash_table_lookup(data_set, "filter_string"));
99         gbl_cur_main_window_->filterPackets(filter);
100     }
101 }
102
103 static void plugin_if_mainwindow_preference(gconstpointer user_data)
104 {
105     if (!gbl_cur_main_window_ || !user_data)
106         return;
107
108     GHashTable * data_set = (GHashTable *) user_data;
109     const char * module_name;
110     const char * pref_name;
111     const char * pref_value;
112
113     if (g_hash_table_lookup_extended(data_set, "pref_module", NULL, (void**)&module_name) &&
114         g_hash_table_lookup_extended(data_set, "pref_key", NULL, (void**)&pref_name) &&
115         g_hash_table_lookup_extended(data_set, "pref_value", NULL, (void**)&pref_value))
116     {
117         if (prefs_store_ext(module_name, pref_name, pref_value)) {
118             wsApp->emitAppSignal(WiresharkApplication::PacketDissectionChanged);
119             wsApp->emitAppSignal(WiresharkApplication::PreferencesChanged);
120         }
121     }
122 }
123
124 static void plugin_if_mainwindow_gotoframe(gconstpointer user_data)
125 {
126     if (!gbl_cur_main_window_ || !user_data)
127         return;
128
129     GHashTable * data_set = (GHashTable *) user_data;
130     gpointer framenr;
131
132     if (g_hash_table_lookup_extended(data_set, "frame_nr", NULL, &framenr)) {
133         if (GPOINTER_TO_UINT(framenr) != 0)
134             gbl_cur_main_window_->gotoFrame(GPOINTER_TO_UINT(framenr));
135     }
136 }
137
138 #ifdef HAVE_LIBPCAP
139
140 static void plugin_if_mainwindow_get_ws_info(gconstpointer user_data)
141 {
142     if (!gbl_cur_main_window_ || !user_data)
143         return;
144
145     GHashTable * data_set = (GHashTable *)user_data;
146     ws_info_t *ws_info = NULL;
147
148     if (!g_hash_table_lookup_extended(data_set, "ws_info", NULL, (void**)&ws_info))
149         return;
150
151     CaptureFile *cfWrap = gbl_cur_main_window_->captureFile();
152     capture_file *cf = cfWrap->capFile();
153
154     ws_info->ws_info_supported = true;
155
156     if (cf) {
157         ws_info->cf_state = cf->state;
158         ws_info->cf_count = cf->count;
159
160         g_free(ws_info->cf_filename);
161         ws_info->cf_filename = g_strdup(cf->filename);
162
163         if (cf->state == FILE_READ_DONE) {
164             ws_info->cf_framenr = cf->current_frame->num;
165             ws_info->frame_passed_dfilter = (cf->current_frame->flags.passed_dfilter == 1);
166         } else {
167             ws_info->cf_framenr = 0;
168             ws_info->frame_passed_dfilter = FALSE;
169         }
170     } else if (ws_info->cf_state != FILE_CLOSED) {
171         /* Initialise the ws_info structure */
172         ws_info->cf_count = 0;
173
174         g_free(ws_info->cf_filename);
175         ws_info->cf_filename = NULL;
176
177         ws_info->cf_framenr = 0;
178         ws_info->frame_passed_dfilter = FALSE;
179         ws_info->cf_state = FILE_CLOSED;
180     }
181 }
182
183 #endif /* HAVE_LIBPCAP */
184
185 gpointer
186 simple_dialog(ESD_TYPE_E type, gint btn_mask, const gchar *msg_format, ...)
187 {
188     va_list ap;
189
190     va_start(ap, msg_format);
191     SimpleDialog sd(gbl_cur_main_window_, type, btn_mask, msg_format, ap);
192     va_end(ap);
193
194     sd.exec();
195     return NULL;
196 }
197
198 /*
199  * Alert box, with optional "don't show this message again" variable
200  * and checkbox, and optional secondary text.
201  */
202 void
203 simple_message_box(ESD_TYPE_E type, gboolean *notagain,
204                    const char *secondary_msg, const char *msg_format, ...)
205 {
206     if (notagain && *notagain) {
207         return;
208     }
209
210     va_list ap;
211
212     va_start(ap, msg_format);
213     SimpleDialog sd(gbl_cur_main_window_, type, ESD_BTN_OK, msg_format, ap);
214     va_end(ap);
215
216     sd.setDetailedText(secondary_msg);
217
218 #if (QT_VERSION > QT_VERSION_CHECK(5, 2, 0))
219     QCheckBox *cb = NULL;
220     if (notagain) {
221         cb = new QCheckBox();
222         cb->setChecked(true);
223         cb->setText(QObject::tr("Don't show this message again."));
224         sd.setCheckBox(cb);
225     }
226 #endif
227
228     sd.exec();
229
230 #if (QT_VERSION > QT_VERSION_CHECK(5, 2, 0))
231     if (notagain && cb) {
232         *notagain = cb->isChecked();
233     }
234 #endif
235 }
236
237 /*
238  * Error alert box, taking a format and a va_list argument.
239  */
240 void
241 vsimple_error_message_box(const char *msg_format, va_list ap)
242 {
243     SimpleDialog sd(gbl_cur_main_window_, ESD_TYPE_ERROR, ESD_BTN_OK, msg_format, ap);
244     sd.exec();
245 }
246
247
248 QMenu* MainWindow::findOrAddMenu(QMenu *parent_menu, QString& menu_text) {
249     QList<QAction *> actions = parent_menu->actions();
250     QList<QAction *>::const_iterator i;
251     for (i = actions.constBegin(); i != actions.constEnd(); ++i) {
252         if ((*i)->text()==menu_text) {
253             return (*i)->menu();
254         }
255     }
256     // If we get here there menu entry was not found, add a sub menu
257     return parent_menu->addMenu(menu_text);
258 }
259
260 MainWindow::MainWindow(QWidget *parent) :
261     QMainWindow(parent),
262     main_ui_(new Ui::MainWindow),
263     cur_layout_(QVector<unsigned>()),
264     df_combo_box_(new DisplayFilterCombo()),
265     packet_list_(NULL),
266     proto_tree_(NULL),
267     previous_focus_(NULL),
268     show_hide_actions_(NULL),
269     time_display_actions_(NULL),
270     time_precision_actions_(NULL),
271     funnel_statistics_(new FunnelStatistics(this, capture_file_)),
272     freeze_focus_(NULL),
273     capture_stopping_(false),
274     capture_filter_valid_(false),
275 #ifdef HAVE_LIBPCAP
276     info_data_(),
277 #endif
278 #ifdef _WIN32
279     pipe_timer_(NULL)
280 #else
281     pipe_notifier_(NULL)
282 #endif
283 {
284     if (!gbl_cur_main_window_) {
285         connect(wsApp, SIGNAL(openStatCommandDialog(QString,const char*,void*)),
286                 this, SLOT(openStatCommandDialog(QString,const char*,void*)));
287         connect(wsApp, SIGNAL(openTapParameterDialog(QString,const QString,void*)),
288                 this, SLOT(openTapParameterDialog(QString,const QString,void*)));
289     }
290     gbl_cur_main_window_ = this;
291 #ifdef HAVE_LIBPCAP
292     capture_session_init(&cap_session_, CaptureFile::globalCapFile());
293 #endif
294     main_ui_->setupUi(this);
295     setWindowIcon(wsApp->normalIcon());
296     setTitlebarForCaptureFile();
297     setMenusForCaptureFile();
298     setForCapturedPackets(false);
299     setMenusForFileSet(false);
300     interfaceSelectionChanged();
301     loadWindowGeometry();
302
303 #ifndef HAVE_LUA
304     main_ui_->actionAnalyzeReloadLuaPlugins->setVisible(false);
305 #endif
306
307     //To prevent users use features before initialization complete
308     //Otherwise unexpected problems may occur
309     setFeaturesEnabled(false);
310     connect(wsApp, SIGNAL(appInitialized()), this, SLOT(setFeaturesEnabled()));
311     connect(wsApp, SIGNAL(appInitialized()), this, SLOT(zoomText()));
312     connect(wsApp, SIGNAL(appInitialized()), this, SLOT(initViewColorizeMenu()));
313     connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addStatsPluginsToMenu()));
314     connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addDynamicMenus()));
315     connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addExternalMenus()));
316
317     connect(wsApp, SIGNAL(profileChanging()), this, SLOT(saveWindowGeometry()));
318     connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(layoutPanes()));
319     connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(layoutToolbars()));
320     connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(updatePreferenceActions()));
321     connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(zoomText()));
322     connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(setTitlebarForCaptureFile()));
323
324     connect(wsApp, SIGNAL(updateRecentItemStatus(const QString &, qint64, bool)), this, SLOT(updateRecentFiles()));
325     updateRecentFiles();
326
327 #ifdef HAVE_LIBPCAP
328     connect(&capture_interfaces_dialog_, SIGNAL(startCapture()), this, SLOT(startCapture()));
329     connect(&capture_interfaces_dialog_, SIGNAL(stopCapture()), this, SLOT(stopCapture()));
330 #endif
331
332     const DisplayFilterEdit *df_edit = dynamic_cast<DisplayFilterEdit *>(df_combo_box_->lineEdit());
333     connect(df_edit, SIGNAL(pushFilterSyntaxStatus(const QString&)),
334             main_ui_->statusBar, SLOT(pushFilterStatus(const QString&)));
335     connect(df_edit, SIGNAL(popFilterSyntaxStatus()), main_ui_->statusBar, SLOT(popFilterStatus()));
336     connect(df_edit, SIGNAL(pushFilterSyntaxWarning(const QString&)),
337             main_ui_->statusBar, SLOT(pushTemporaryStatus(const QString&)));
338     connect(df_edit, SIGNAL(filterPackets(QString,bool)), this, SLOT(filterPackets(QString,bool)));
339     connect(df_edit, SIGNAL(showPreferencesDialog(PreferencesDialog::PreferencesPane)),
340             this, SLOT(showPreferencesDialog(PreferencesDialog::PreferencesPane)));
341     connect(wsApp, SIGNAL(preferencesChanged()), df_edit, SLOT(checkFilter()));
342
343     connect(df_edit, SIGNAL(textChanged(QString)), funnel_statistics_, SLOT(displayFilterTextChanged(QString)));
344     connect(funnel_statistics_, SIGNAL(setDisplayFilter(QString)), df_edit, SLOT(setText(QString)));
345     connect(funnel_statistics_, SIGNAL(applyDisplayFilter()), df_combo_box_, SLOT(applyDisplayFilter()));
346     connect(funnel_statistics_, SIGNAL(openCaptureFile(QString,QString)),
347             this, SLOT(openCaptureFile(QString,QString)));
348     connect(this, SIGNAL(displayFilterSuccess(bool)), df_edit, SLOT(displayFilterSuccess(bool)));
349
350     initMainToolbarIcons();
351
352     // In Qt4 multiple toolbars and "pretty" are mutually exculsive on OS X. If
353     // unifiedTitleAndToolBarOnMac is enabled everything ends up in the same row.
354     // https://bugreports.qt-project.org/browse/QTBUG-22433
355     // This property is obsolete in Qt5 so this issue may be fixed in that version.
356     main_ui_->displayFilterToolBar->insertWidget(main_ui_->actionDisplayFilterExpression, df_combo_box_);
357
358     wireless_frame_ = new WirelessFrame(this);
359     main_ui_->wirelessToolBar->addWidget(wireless_frame_);
360     connect(wireless_frame_, SIGNAL(pushAdapterStatus(const QString&)),
361             main_ui_->statusBar, SLOT(pushTemporaryStatus(const QString&)));
362     connect (wireless_frame_, SIGNAL(showWirelessPreferences(QString)),
363              this, SLOT(showPreferencesDialog(QString)));
364
365     main_ui_->goToFrame->hide();
366     connect(main_ui_->goToFrame, SIGNAL(visibilityChanged(bool)),
367             main_ui_->actionGoGoToPacket, SLOT(setChecked(bool)));
368
369     // XXX For some reason the cursor is drawn funny with an input mask set
370     // https://bugreports.qt-project.org/browse/QTBUG-7174
371
372     main_ui_->searchFrame->hide();
373     connect(main_ui_->searchFrame, SIGNAL(pushFilterSyntaxStatus(const QString&)),
374             main_ui_->statusBar, SLOT(pushTemporaryStatus(const QString&)));
375     connect(main_ui_->searchFrame, SIGNAL(visibilityChanged(bool)),
376             main_ui_->actionEditFindPacket, SLOT(setChecked(bool)));
377
378     main_ui_->addressEditorFrame->hide();
379     main_ui_->columnEditorFrame->hide();
380     main_ui_->preferenceEditorFrame->hide();
381     main_ui_->filterExpressionFrame->hide();
382
383 #ifndef HAVE_LIBPCAP
384     main_ui_->menuCapture->setEnabled(false);
385 #endif
386
387 #if defined(Q_OS_MAC)
388 #if defined(QT_MACEXTRAS_LIB) && QT_VERSION < QT_VERSION_CHECK(5, 2, 1)
389     QMacNativeToolBar *ntb = QtMacExtras::setNativeToolBar(main_ui_->mainToolBar);
390     ntb->setIconSize(QSize(24, 24));
391 #endif // QT_MACEXTRAS_LIB
392
393     main_ui_->goToPacketLabel->setAttribute(Qt::WA_MacSmallSize, true);
394     main_ui_->goToLineEdit->setAttribute(Qt::WA_MacSmallSize, true);
395     main_ui_->goToGo->setAttribute(Qt::WA_MacSmallSize, true);
396     main_ui_->goToCancel->setAttribute(Qt::WA_MacSmallSize, true);
397
398     main_ui_->actionEditPreferences->setMenuRole(QAction::PreferencesRole);
399
400 #endif // Q_OS_MAC
401
402 #ifdef HAVE_SOFTWARE_UPDATE
403     QAction *update_sep = main_ui_->menuHelp->insertSeparator(main_ui_->actionHelpAbout);
404     QAction *update_action = new QAction(tr("Check for Updates" UTF8_HORIZONTAL_ELLIPSIS), main_ui_->menuHelp);
405     main_ui_->menuHelp->insertAction(update_sep, update_action);
406     connect(update_action, SIGNAL(triggered()), this, SLOT(checkForUpdates()));
407 #endif
408     master_split_.setObjectName("splitterMaster");
409     extra_split_.setObjectName("splitterExtra");
410     main_ui_->mainStack->addWidget(&master_split_);
411
412     empty_pane_.setObjectName("emptyPane");
413
414     packet_list_ = new PacketList(&master_split_);
415
416     proto_tree_ = new ProtoTree(&master_split_);
417     proto_tree_->installEventFilter(this);
418
419     byte_view_tab_ = new ByteViewTab(&master_split_);
420
421     packet_list_->setProtoTree(proto_tree_);
422     packet_list_->setByteViewTab(byte_view_tab_);
423     packet_list_->installEventFilter(this);
424
425     main_welcome_ = main_ui_->welcomePage;
426
427     // Packet list and proto tree must exist before these are called.
428     setMenusForSelectedPacket();
429     setMenusForSelectedTreeRow();
430
431     initShowHideMainWidgets();
432     initTimeDisplayFormatMenu();
433     initTimePrecisionFormatMenu();
434     initFreezeActions();
435     updatePreferenceActions();
436     updateRecentActions();
437     setForCaptureInProgress(false);
438
439     setTabOrder(df_combo_box_, packet_list_);
440
441     connect(&capture_file_, SIGNAL(captureCapturePrepared(capture_session *)),
442             this, SLOT(captureCapturePrepared(capture_session *)));
443     connect(&capture_file_, SIGNAL(captureCaptureUpdateStarted(capture_session *)),
444             this, SLOT(captureCaptureUpdateStarted(capture_session *)));
445     connect(&capture_file_, SIGNAL(captureCaptureUpdateFinished(capture_session *)),
446             this, SLOT(captureCaptureUpdateFinished(capture_session *)));
447     connect(&capture_file_, SIGNAL(captureCaptureFixedStarted(capture_session *)),
448             this, SLOT(captureCaptureFixedStarted(capture_session *)));
449     connect(&capture_file_, SIGNAL(captureCaptureFixedContinue(capture_session *)),
450             main_ui_->statusBar, SLOT(updateCaptureFixedStatistics(capture_session*)));
451     connect(&capture_file_, SIGNAL(captureCaptureFixedFinished(capture_session *)),
452             this, SLOT(captureCaptureFixedFinished(capture_session *)));
453     connect(&capture_file_, SIGNAL(captureCaptureStopping(capture_session *)),
454             this, SLOT(captureCaptureStopping(capture_session *)));
455     connect(&capture_file_, SIGNAL(captureCaptureFailed(capture_session *)),
456             this, SLOT(captureCaptureFailed(capture_session *)));
457     connect(&capture_file_, SIGNAL(captureCaptureUpdateContinue(capture_session*)),
458             main_ui_->statusBar, SLOT(updateCaptureStatistics(capture_session*)));
459
460     connect(&capture_file_, SIGNAL(captureCaptureUpdateStarted(capture_session *)),
461             wsApp, SLOT(captureStarted()));
462     connect(&capture_file_, SIGNAL(captureCaptureUpdateFinished(capture_session *)),
463             wsApp, SLOT(captureFinished()));
464     connect(&capture_file_, SIGNAL(captureCaptureFixedStarted(capture_session *)),
465             wsApp, SLOT(captureStarted()));
466     connect(&capture_file_, SIGNAL(captureCaptureFixedFinished(capture_session *)),
467             wsApp, SLOT(captureFinished()));
468
469     connect(&capture_file_, SIGNAL(captureFileOpened()),
470             this, SLOT(captureFileOpened()));
471     connect(&capture_file_, SIGNAL(captureFileReadStarted()),
472             this, SLOT(captureFileReadStarted()));
473     connect(&capture_file_, SIGNAL(captureFileReadFinished()),
474             this, SLOT(captureFileReadFinished()));
475     connect(&capture_file_, SIGNAL(captureFileReloadStarted()),
476             this, SLOT(captureFileReloadStarted()));
477     connect(&capture_file_, SIGNAL(captureFileReloadFinished()),
478             this, SLOT(captureFileReadFinished()));
479     connect(&capture_file_, SIGNAL(captureFileRescanStarted()),
480             this, SLOT(captureFileRescanStarted()));
481     connect(&capture_file_, SIGNAL(captureFileRescanFinished()),
482             this, SLOT(captureFileReadFinished()));
483     connect(&capture_file_, SIGNAL(captureFileRetapStarted()),
484             this, SLOT(captureFileRetapStarted()));
485     connect(&capture_file_, SIGNAL(captureFileRetapFinished()),
486             this, SLOT(captureFileRetapFinished()));
487     connect(&capture_file_, SIGNAL(captureFileFlushTapsData()),
488             this, SLOT(captureFileFlushTapsData()));
489     connect(&capture_file_, SIGNAL(captureFileClosing()),
490             this, SLOT(captureFileClosing()));
491     connect(&capture_file_, SIGNAL(captureFileClosed()),
492             this, SLOT(captureFileClosed()));
493
494     connect(&capture_file_, SIGNAL(captureFileSaveStarted(QString)),
495             this, SLOT(captureFileSaveStarted(QString)));
496     connect(&capture_file_, SIGNAL(captureFileSaveFinished()),
497             main_ui_->statusBar, SLOT(popFileStatus()));
498     connect(&capture_file_, SIGNAL(captureFileSaveFailed()),
499             main_ui_->statusBar, SLOT(popFileStatus()));
500     connect(&capture_file_, SIGNAL(captureFileSaveStopped()),
501             main_ui_->statusBar, SLOT(popFileStatus()));
502
503     connect(&capture_file_, SIGNAL(captureFileReadStarted()),
504             wsApp, SLOT(captureFileReadStarted()));
505     connect(&capture_file_, SIGNAL(captureFileReadFinished()),
506             wsApp, SLOT(updateTaps()));
507
508     connect(wsApp, SIGNAL(columnsChanged()),
509             packet_list_, SLOT(columnsChanged()));
510     connect(wsApp, SIGNAL(preferencesChanged()),
511             packet_list_, SLOT(preferencesChanged()));
512     connect(wsApp, SIGNAL(recentFilesRead()),
513             this, SLOT(applyRecentPaneGeometry()));
514     connect(wsApp, SIGNAL(recentFilesRead()),
515             this, SLOT(updateRecentActions()));
516     connect(wsApp, SIGNAL(packetDissectionChanged()),
517             this, SLOT(redissectPackets()), Qt::QueuedConnection);
518     connect(wsApp, SIGNAL(appInitialized()),
519             this, SLOT(filterExpressionsChanged()));
520     connect(wsApp, SIGNAL(filterExpressionsChanged()),
521             this, SLOT(filterExpressionsChanged()));
522     connect(wsApp, SIGNAL(checkDisplayFilter()),
523             this, SLOT(checkDisplayFilter()));
524     connect(wsApp, SIGNAL(fieldsChanged()),
525             this, SLOT(fieldsChanged()));
526     connect(wsApp, SIGNAL(reloadLuaPlugins()),
527             this, SLOT(reloadLuaPlugins()));
528
529     connect(main_ui_->mainStack, SIGNAL(currentChanged(int)),
530             this, SLOT(mainStackChanged(int)));
531
532     connect(main_welcome_, SIGNAL(startCapture()),
533             this, SLOT(startCapture()));
534     connect(main_welcome_, SIGNAL(recentFileActivated(QString)),
535             this, SLOT(openCaptureFile(QString)));
536     connect(main_welcome_, SIGNAL(pushFilterSyntaxStatus(const QString&)),
537             main_ui_->statusBar, SLOT(pushFilterStatus(const QString&)));
538     connect(main_welcome_, SIGNAL(popFilterSyntaxStatus()),
539             main_ui_->statusBar, SLOT(popFilterStatus()));
540
541     connect(main_ui_->addressEditorFrame, SIGNAL(editAddressStatus(QString)),
542             main_ui_->statusBar, SLOT(pushTemporaryStatus(QString)));
543     connect(main_ui_->addressEditorFrame, SIGNAL(redissectPackets()),
544             this, SLOT(redissectPackets()));
545     connect(main_ui_->addressEditorFrame, SIGNAL(showNameResolutionPreferences(QString)),
546             this, SLOT(showPreferencesDialog(QString)));
547     connect(main_ui_->preferenceEditorFrame, SIGNAL(showProtocolPreferences(QString)),
548             this, SLOT(showPreferencesDialog(QString)));
549     connect(main_ui_->filterExpressionFrame, SIGNAL(showPreferencesDialog(PreferencesDialog::PreferencesPane)),
550             this, SLOT(showPreferencesDialog(PreferencesDialog::PreferencesPane)));
551     connect(main_ui_->filterExpressionFrame, SIGNAL(filterExpressionsChanged()),
552             this, SLOT(filterExpressionsChanged()));
553
554     connect(this, SIGNAL(setCaptureFile(capture_file*)),
555             main_ui_->searchFrame, SLOT(setCaptureFile(capture_file*)));
556     connect(this, SIGNAL(setCaptureFile(capture_file*)),
557             main_ui_->statusBar, SLOT(setCaptureFile(capture_file*)));
558     connect(this, SIGNAL(setCaptureFile(capture_file*)),
559             packet_list_, SLOT(setCaptureFile(capture_file*)));
560     connect(this, SIGNAL(setCaptureFile(capture_file*)),
561             byte_view_tab_, SLOT(setCaptureFile(capture_file*)));
562
563     connect(this, SIGNAL(monospaceFontChanged(QFont)),
564             packet_list_, SLOT(setMonospaceFont(QFont)));
565     connect(this, SIGNAL(monospaceFontChanged(QFont)),
566             proto_tree_, SLOT(setMonospaceFont(QFont)));
567     connect(this, SIGNAL(monospaceFontChanged(QFont)),
568             byte_view_tab_, SLOT(setMonospaceFont(QFont)));
569
570     connect(main_ui_->actionGoNextPacket, SIGNAL(triggered()),
571             packet_list_, SLOT(goNextPacket()));
572     connect(main_ui_->actionGoPreviousPacket, SIGNAL(triggered()),
573             packet_list_, SLOT(goPreviousPacket()));
574     connect(main_ui_->actionGoFirstPacket, SIGNAL(triggered()),
575             packet_list_, SLOT(goFirstPacket()));
576     connect(main_ui_->actionGoLastPacket, SIGNAL(triggered()),
577             packet_list_, SLOT(goLastPacket()));
578
579     connect(main_ui_->actionViewExpandSubtrees, SIGNAL(triggered()),
580             proto_tree_, SLOT(expandSubtrees()));
581     connect(main_ui_->actionViewExpandAll, SIGNAL(triggered()),
582             proto_tree_, SLOT(expandAll()));
583     connect(main_ui_->actionViewCollapseAll, SIGNAL(triggered()),
584             proto_tree_, SLOT(collapseAll()));
585
586     connect(packet_list_, SIGNAL(packetSelectionChanged()),
587             this, SLOT(setMenusForSelectedPacket()));
588     connect(packet_list_, SIGNAL(packetDissectionChanged()),
589             this, SLOT(redissectPackets()));
590     connect(packet_list_, SIGNAL(showColumnPreferences(PreferencesDialog::PreferencesPane)),
591             this, SLOT(showPreferencesDialog(PreferencesDialog::PreferencesPane)));
592     connect(packet_list_, SIGNAL(showProtocolPreferences(QString)),
593             this, SLOT(showPreferencesDialog(QString)));
594     connect(packet_list_, SIGNAL(editProtocolPreference(preference*,pref_module*)),
595             main_ui_->preferenceEditorFrame, SLOT(editPreference(preference*,pref_module*)));
596     connect(packet_list_, SIGNAL(editColumn(int)), this, SLOT(showColumnEditor(int)));
597     connect(main_ui_->columnEditorFrame, SIGNAL(columnEdited()),
598             packet_list_, SLOT(columnsChanged()));
599     connect(packet_list_, SIGNAL(doubleClicked(QModelIndex)),
600             this, SLOT(openPacketDialog()));
601     connect(packet_list_, SIGNAL(packetListScrolled(bool)),
602             main_ui_->actionGoAutoScroll, SLOT(setChecked(bool)));
603     connect(packet_list_->packetListModel(), SIGNAL(pushBusyStatus(QString)),
604             main_ui_->statusBar, SLOT(pushBusyStatus(QString)));
605     connect(packet_list_->packetListModel(), SIGNAL(popBusyStatus()),
606             main_ui_->statusBar, SLOT(popBusyStatus()));
607     connect(packet_list_->packetListModel(), SIGNAL(pushProgressStatus(QString,bool,bool,gboolean*)),
608             main_ui_->statusBar, SLOT(pushProgressStatus(QString,bool,bool,gboolean*)));
609     connect(packet_list_->packetListModel(), SIGNAL(updateProgressStatus(int)),
610             main_ui_->statusBar, SLOT(updateProgressStatus(int)));
611     connect(packet_list_->packetListModel(), SIGNAL(popProgressStatus()),
612             main_ui_->statusBar, SLOT(popProgressStatus()));
613
614     connect(proto_tree_, SIGNAL(protoItemSelected(const QString&)),
615             main_ui_->statusBar, SLOT(pushFieldStatus(const QString&)));
616     connect(proto_tree_, SIGNAL(protoItemSelected(field_info *)),
617             this, SLOT(setMenusForSelectedTreeRow(field_info *)));
618     connect(proto_tree_, SIGNAL(openPacketInNewWindow(bool)),
619             this, SLOT(openPacketDialog(bool)));
620     connect(proto_tree_, SIGNAL(showProtocolPreferences(QString)),
621             this, SLOT(showPreferencesDialog(QString)));
622     connect(proto_tree_, SIGNAL(editProtocolPreference(preference*,pref_module*)),
623             main_ui_->preferenceEditorFrame, SLOT(editPreference(preference*,pref_module*)));
624
625     connect(byte_view_tab_, SIGNAL(byteFieldHovered(const QString&)),
626             main_ui_->statusBar, SLOT(pushByteStatus(const QString&)));
627
628     connect(main_ui_->statusBar, SIGNAL(showExpertInfo()),
629             this, SLOT(on_actionAnalyzeExpertInfo_triggered()));
630
631     connect(main_ui_->statusBar, SIGNAL(stopLoading()),
632             &capture_file_, SLOT(stopLoading()));
633
634     connect(main_ui_->statusBar, SIGNAL(editCaptureComment()),
635             this, SLOT(on_actionStatisticsCaptureFileProperties_triggered()));
636
637     connect(&file_set_dialog_, SIGNAL(fileSetOpenCaptureFile(QString)),
638             this, SLOT(openCaptureFile(QString)));
639
640 #ifdef HAVE_LIBPCAP
641     QTreeWidget *iface_tree = findChild<QTreeWidget *>("interfaceTree");
642     if (iface_tree) {
643         connect(iface_tree, SIGNAL(itemSelectionChanged()),
644                 this, SLOT(interfaceSelectionChanged()));
645     }
646     connect(main_ui_->welcomePage, SIGNAL(captureFilterSyntaxChanged(bool)),
647             this, SLOT(captureFilterSyntaxChanged(bool)));
648
649 #ifdef HAVE_EXTCAP
650         connect(this->main_welcome_, SIGNAL(showExtcapOptions(QString&)),
651                 this, SLOT(showExtcapOptionsDialog(QString&)));
652 #endif
653
654     connect(&capture_interfaces_dialog_, SIGNAL(getPoints(int,PointList*)),
655             this->main_welcome_->getInterfaceTree(), SLOT(getPoints(int,PointList*)));
656     // Changes in interface selections or capture filters should be propagated
657     // to the main welcome screen where they will be applied to the global
658     // capture options.
659     connect(&capture_interfaces_dialog_, SIGNAL(interfaceListChanged()),
660             this->main_welcome_->getInterfaceTree(), SLOT(interfaceListChanged()));
661     connect(&capture_interfaces_dialog_, SIGNAL(interfacesChanged()),
662             this->main_welcome_, SLOT(interfaceSelected()));
663     connect(&capture_interfaces_dialog_, SIGNAL(interfacesChanged()),
664             this->main_welcome_->getInterfaceTree(), SLOT(updateSelectedInterfaces()));
665     connect(&capture_interfaces_dialog_, SIGNAL(interfacesChanged()),
666             this->main_welcome_->getInterfaceTree(), SLOT(updateToolTips()));
667     connect(&capture_interfaces_dialog_, SIGNAL(captureFilterTextEdited(QString)),
668             this->main_welcome_, SLOT(setCaptureFilterText(QString)));
669 #endif
670
671     /* Create plugin_if hooks */
672     plugin_if_register_gui_cb(PLUGIN_IF_FILTER_ACTION_APPLY, plugin_if_mainwindow_apply_filter);
673     plugin_if_register_gui_cb(PLUGIN_IF_FILTER_ACTION_PREPARE, plugin_if_mainwindow_apply_filter);
674     plugin_if_register_gui_cb(PLUGIN_IF_PREFERENCE_SAVE, plugin_if_mainwindow_preference);
675     plugin_if_register_gui_cb(PLUGIN_IF_GOTO_FRAME, plugin_if_mainwindow_gotoframe);
676 #ifdef HAVE_LIBPCAP
677     plugin_if_register_gui_cb(PLUGIN_IF_GET_WS_INFO, plugin_if_mainwindow_get_ws_info);
678 #endif
679
680     main_ui_->mainStack->setCurrentWidget(main_welcome_);
681 }
682
683 MainWindow::~MainWindow()
684 {
685     delete main_ui_;
686 }
687
688 QString MainWindow::getFilter()
689 {
690     return df_combo_box_->currentText();
691 }
692
693 QMenu *MainWindow::createPopupMenu()
694 {
695     QMenu *menu = new QMenu();
696     menu->addAction(main_ui_->actionViewMainToolbar);
697     menu->addAction(main_ui_->actionViewFilterToolbar);
698     menu->addAction(main_ui_->actionViewWirelessToolbar);
699     menu->addAction(main_ui_->actionViewStatusBar);
700     menu->addSeparator();
701     menu->addAction(main_ui_->actionViewPacketList);
702     menu->addAction(main_ui_->actionViewPacketDetails);
703     menu->addAction(main_ui_->actionViewPacketBytes);
704     return menu;
705 }
706
707 void MainWindow::setPipeInputHandler(gint source, gpointer user_data, ws_process_id *child_process, pipe_input_cb_t input_cb)
708 {
709     pipe_source_        = source;
710     pipe_child_process_ = child_process;
711     pipe_user_data_     = user_data;
712     pipe_input_cb_      = input_cb;
713
714 #ifdef _WIN32
715     /* Tricky to use pipes in win9x, as no concept of wait.  NT can
716        do this but that doesn't cover all win32 platforms.  GTK can do
717        this but doesn't seem to work over processes.  Attempt to do
718        something similar here, start a timer and check for data on every
719        timeout. */
720        /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_input_set_handler: new");*/
721
722     if (pipe_timer_) {
723         disconnect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
724         delete pipe_timer_;
725     }
726
727     pipe_timer_ = new QTimer(this);
728     connect(pipe_timer_, SIGNAL(timeout()), this, SLOT(pipeTimeout()));
729     connect(pipe_timer_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
730     pipe_timer_->start(200);
731 #else
732     if (pipe_notifier_) {
733         disconnect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
734         delete pipe_notifier_;
735     }
736
737     pipe_notifier_ = new QSocketNotifier(pipe_source_, QSocketNotifier::Read);
738     // XXX ui/gtk/gui_utils.c sets the encoding. Do we need to do the same?
739     connect(pipe_notifier_, SIGNAL(activated(int)), this, SLOT(pipeActivated(int)));
740     connect(pipe_notifier_, SIGNAL(destroyed()), this, SLOT(pipeNotifierDestroyed()));
741 #endif
742 }
743
744 bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
745
746     // The user typed some text. Start filling in a filter.
747     // We may need to be more choosy here. We just need to catch events for the packet list,
748     // proto tree, and main welcome widgets.
749     if (event->type() == QEvent::KeyPress) {
750         QKeyEvent *kevt = static_cast<QKeyEvent *>(event);
751         if (kevt->text().length() > 0 && kevt->text()[0].isPrint()) {
752             df_combo_box_->lineEdit()->insert(kevt->text());
753             df_combo_box_->lineEdit()->setFocus();
754             return true;
755         }
756     }
757
758     return QMainWindow::eventFilter(obj, event);
759 }
760
761 void MainWindow::keyPressEvent(QKeyEvent *event) {
762
763     // Explicitly focus on the display filter combo.
764     if (event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_Slash) {
765         df_combo_box_->setFocus(Qt::ShortcutFocusReason);
766         return;
767     }
768
769     if (wsApp->focusWidget() == main_ui_->goToLineEdit) {
770         if (event->modifiers() == Qt::NoModifier) {
771             if (event->key() == Qt::Key_Escape) {
772                 on_goToCancel_clicked();
773             } else if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
774                 on_goToGo_clicked();
775             }
776         }
777         return; // goToLineEdit didn't want it and we don't either.
778     }
779
780     // Move up & down the packet list.
781     if (event->key() == Qt::Key_F7) {
782         packet_list_->goPreviousPacket();
783     } else if (event->key() == Qt::Key_F8) {
784         packet_list_->goNextPacket();
785     }
786
787     // Move along, citizen.
788     QMainWindow::keyPressEvent(event);
789 }
790
791 void MainWindow::closeEvent(QCloseEvent *event) {
792     saveWindowGeometry();
793
794     /* If we're in the middle of stopping a capture, don't do anything;
795        the user can try deleting the window after the capture stops. */
796     if (capture_stopping_) {
797         event->ignore();
798         return;
799     }
800
801     QString before_what(tr(" before quitting"));
802     if (!testCaptureFileClose(before_what, Quit)) {
803         event->ignore();
804         return;
805     }
806
807 #ifdef HAVE_LIBPCAP
808     capture_interfaces_dialog_.close();
809 #endif
810     // Make sure we kill any open dumpcap processes.
811     delete main_welcome_;
812
813     // One of the many places we assume one main window.
814     if(!wsApp->isInitialized()) {
815         // If we're still initializing, QCoreApplication::quit() won't
816         // exit properly because we are not in the event loop. This
817         // means that the application won't clean up after itself. We
818         // might want to call wsApp->processEvents() during startup
819         // instead so that we can do a normal exit here.
820         exit(0);
821     }
822     wsApp->quit();
823 }
824
825 void MainWindow::dragEnterEvent(QDragEnterEvent *event)
826 {
827     bool accept = false;
828     foreach (QUrl drag_url, event->mimeData()->urls()) {
829         if (!drag_url.toLocalFile().isEmpty()) {
830             accept = true;
831             break;
832         }
833     }
834     if (accept) event->acceptProposedAction();
835 }
836
837 void MainWindow::dropEvent(QDropEvent *event)
838 {
839     foreach (QUrl drop_url, event->mimeData()->urls()) {
840         QString local_file = drop_url.toLocalFile();
841         if (!local_file.isEmpty()) {
842             event->acceptProposedAction();
843             openCaptureFile(local_file);
844             break;
845         }
846     }
847 }
848
849 // Apply recent settings to the main window geometry.
850 // We haven't loaded the preferences at this point so we assume that the
851 // position and size preference are enabled.
852 void MainWindow::loadWindowGeometry()
853 {
854     int min_sensible_dimension = 200;
855
856 #ifndef Q_OS_MAC
857     if (recent.gui_geometry_main_maximized) {
858         setWindowState(Qt::WindowMaximized);
859     } else
860 #endif
861     {
862         QRect recent_geom(recent.gui_geometry_main_x, recent.gui_geometry_main_y,
863                           recent.gui_geometry_main_width, recent.gui_geometry_main_height);
864         if (!rect_on_screen(recent_geom)) {
865             // We're not visible on any screens. Give up and use the default geometry.
866             return;
867         }
868
869 //        if (prefs.gui_geometry_save_position) {
870             move(recent_geom.topLeft());
871 //        }
872
873         if (// prefs.gui_geometry_save_size &&
874                 recent_geom.width() > min_sensible_dimension &&
875                 recent_geom.height() > min_sensible_dimension) {
876             resize(recent_geom.size());
877         }
878     }
879 }
880
881 void MainWindow::saveWindowGeometry()
882 {
883     if (prefs.gui_geometry_save_position) {
884         recent.gui_geometry_main_x = pos().x();
885         recent.gui_geometry_main_y = pos().y();
886     }
887
888     if (prefs.gui_geometry_save_size) {
889         recent.gui_geometry_main_width = size().width();
890         recent.gui_geometry_main_height = size().height();
891     }
892
893     if (prefs.gui_geometry_save_maximized) {
894         // On OS X this is false when it shouldn't be
895         recent.gui_geometry_main_maximized = isMaximized();
896     }
897
898     if (master_split_.sizes().length() > 0) {
899         recent.gui_geometry_main_upper_pane = master_split_.sizes()[0];
900     }
901
902     if (master_split_.sizes().length() > 2) {
903         recent.gui_geometry_main_lower_pane = master_split_.sizes()[1];
904     } else if (extra_split_.sizes().length() > 0) {
905         recent.gui_geometry_main_lower_pane = extra_split_.sizes()[0];
906     }
907 }
908
909 QWidget* MainWindow::getLayoutWidget(layout_pane_content_e type) {
910     switch (type) {
911         case layout_pane_content_none:
912             return &empty_pane_;
913         case layout_pane_content_plist:
914             return packet_list_;
915         case layout_pane_content_pdetails:
916             return proto_tree_;
917         case layout_pane_content_pbytes:
918             return byte_view_tab_;
919         default:
920             g_assert_not_reached();
921             return NULL;
922     }
923 }
924
925 // Our event loop becomes nested whenever we call update_progress_dlg, which
926 // includes several places in file.c. The GTK+ UI stays out of trouble by
927 // showing a modal progress dialog. We attempt to do the equivalent below by
928 // disabling parts of the main window. At a minumum the ProgressFrame in the
929 // main status bar must remain accessible.
930 //
931 // We might want to do this any time the main status bar progress frame is
932 // shown and hidden.
933 void MainWindow::freeze()
934 {
935     freeze_focus_ = wsApp->focusWidget();
936
937     // XXX Alternatively we could just disable and enable the main menu.
938     for (int i = 0; i < freeze_actions_.size(); i++) {
939         QAction *action = freeze_actions_[i].first;
940         freeze_actions_[i].second = action->isEnabled();
941         action->setEnabled(false);
942     }
943     main_ui_->centralWidget->setEnabled(false);
944 }
945
946 void MainWindow::thaw()
947 {
948     main_ui_->centralWidget->setEnabled(true);
949     for (int i = 0; i < freeze_actions_.size(); i++) {
950         freeze_actions_[i].first->setEnabled(freeze_actions_[i].second);
951     }
952
953     if (freeze_focus_) freeze_focus_->setFocus();
954     freeze_focus_ = NULL;
955 }
956
957 void MainWindow::mergeCaptureFile()
958 {
959     QString file_name = "";
960     QString read_filter = "";
961     dfilter_t *rfcode = NULL;
962     int err;
963
964     if (!capture_file_.capFile())
965         return;
966
967     if (prefs.gui_ask_unsaved) {
968         if (cf_has_unsaved_data(capture_file_.capFile())) {
969             QMessageBox msg_dialog;
970             gchar *display_basename;
971             int response;
972
973             msg_dialog.setIcon(QMessageBox::Question);
974             /* This file has unsaved data; ask the user whether to save
975                the capture. */
976             if (capture_file_.capFile()->is_tempfile) {
977                 msg_dialog.setText(tr("Save packets before merging?"));
978                 msg_dialog.setInformativeText(tr("A temporary capture file can't be merged."));
979             } else {
980                 /*
981                  * Format the message.
982                  */
983                 display_basename = g_filename_display_basename(capture_file_.capFile()->filename);
984                 msg_dialog.setText(QString(tr("Save changes in \"%1\" before merging?")).arg(display_basename));
985                 g_free(display_basename);
986                 msg_dialog.setInformativeText(tr("Changes must be saved before the files can be merged."));
987             }
988
989             msg_dialog.setStandardButtons(QMessageBox::Save | QMessageBox::Cancel);
990             msg_dialog.setDefaultButton(QMessageBox::Save);
991
992             response = msg_dialog.exec();
993
994             switch (response) {
995
996             case QMessageBox::Save:
997                 /* Save the file but don't close it */
998                 saveCaptureFile(capture_file_.capFile(), FALSE);
999                 break;
1000
1001             case QMessageBox::Cancel:
1002             default:
1003                 /* Don't do the merge. */
1004                 return;
1005             }
1006         }
1007     }
1008
1009     for (;;) {
1010         CaptureFileDialog merge_dlg(this, capture_file_.capFile(), read_filter);
1011         int file_type;
1012         cf_status_t  merge_status;
1013         char        *in_filenames[2];
1014         char        *tmpname;
1015
1016         if (merge_dlg.merge(file_name)) {
1017             gchar *err_msg;
1018
1019             if (!dfilter_compile(read_filter.toUtf8().constData(), &rfcode, &err_msg)) {
1020                 /* Not valid.  Tell the user, and go back and run the file
1021                    selection box again once they dismiss the alert. */
1022                 //bad_dfilter_alert_box(top_level, read_filter->str);
1023                 QMessageBox::warning(this, tr("Invalid Read Filter"),
1024                                      QString(tr("The filter expression %1 isn't a valid read filter. (%2).").arg(read_filter, err_msg)),
1025                                      QMessageBox::Ok);
1026                 g_free(err_msg);
1027                 continue;
1028             }
1029         } else {
1030             return;
1031         }
1032
1033         file_type = capture_file_.capFile()->cd_t;
1034
1035         /* Try to merge or append the two files */
1036         tmpname = NULL;
1037         if (merge_dlg.mergeType() == 0) {
1038             /* chronological order */
1039             in_filenames[0] = g_strdup(capture_file_.capFile()->filename);
1040             in_filenames[1] = qstring_strdup(file_name);
1041             merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, FALSE);
1042         } else if (merge_dlg.mergeType() <= 0) {
1043             /* prepend file */
1044             in_filenames[0] = qstring_strdup(file_name);
1045             in_filenames[1] = g_strdup(capture_file_.capFile()->filename);
1046             merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
1047         } else {
1048             /* append file */
1049             in_filenames[0] = g_strdup(capture_file_.capFile()->filename);
1050             in_filenames[1] = qstring_strdup(file_name);
1051             merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
1052         }
1053
1054         g_free(in_filenames[0]);
1055         g_free(in_filenames[1]);
1056
1057         if (merge_status != CF_OK) {
1058             if (rfcode != NULL)
1059                 dfilter_free(rfcode);
1060             g_free(tmpname);
1061             continue;
1062         }
1063
1064         cf_close(capture_file_.capFile());
1065
1066         /* Try to open the merged capture file. */
1067         CaptureFile::globalCapFile()->window = this;
1068         if (cf_open(CaptureFile::globalCapFile(), tmpname, WTAP_TYPE_AUTO, TRUE /* temporary file */, &err) != CF_OK) {
1069             /* We couldn't open it; fail. */
1070             CaptureFile::globalCapFile()->window = NULL;
1071             if (rfcode != NULL)
1072                 dfilter_free(rfcode);
1073             g_free(tmpname);
1074             return;
1075         }
1076
1077         /* Attach the new read filter to "cf" ("cf_open()" succeeded, so
1078            it closed the previous capture file, and thus destroyed any
1079            previous read filter attached to "cf"). */
1080         cf_set_rfcode(CaptureFile::globalCapFile(), rfcode);
1081
1082         switch (cf_read(CaptureFile::globalCapFile(), FALSE)) {
1083
1084         case CF_READ_OK:
1085         case CF_READ_ERROR:
1086             /* Just because we got an error, that doesn't mean we were unable
1087              to read any of the file; we handle what we could get from the
1088              file. */
1089             break;
1090
1091         case CF_READ_ABORTED:
1092             /* The user bailed out of re-reading the capture file; the
1093              capture file has been closed - just free the capture file name
1094              string and return (without changing the last containing
1095              directory). */
1096             g_free(tmpname);
1097             return;
1098         }
1099
1100         /* Save the name of the containing directory specified in the path name,
1101            if any; we can write over cf_merged_name, which is a good thing, given that
1102            "get_dirname()" does write over its argument. */
1103         wsApp->setLastOpenDir(get_dirname(tmpname));
1104         g_free(tmpname);
1105         main_ui_->statusBar->showExpert();
1106         return;
1107     }
1108
1109 }
1110
1111 void MainWindow::importCaptureFile() {
1112     ImportTextDialog import_dlg;
1113
1114     QString before_what(tr(" before importing a capture"));
1115     if (!testCaptureFileClose(before_what))
1116         return;
1117
1118     import_dlg.exec();
1119
1120     if (import_dlg.result() != QDialog::Accepted) {
1121         main_ui_->mainStack->setCurrentWidget(main_welcome_);
1122         return;
1123     }
1124
1125     openCaptureFile(import_dlg.capfileName());
1126 }
1127
1128 void MainWindow::saveCaptureFile(capture_file *cf, bool dont_reopen) {
1129     QString file_name;
1130     gboolean discard_comments;
1131
1132     if (cf->is_tempfile) {
1133         /* This is a temporary capture file, so saving it means saving
1134            it to a permanent file.  Prompt the user for a location
1135            to which to save it.  Don't require that the file format
1136            support comments - if it's a temporary capture file, it's
1137            probably pcap-ng, which supports comments and, if it's
1138            not pcap-ng, let the user decide what they want to do
1139            if they've added comments. */
1140         saveAsCaptureFile(cf, FALSE, dont_reopen);
1141     } else {
1142         if (cf->unsaved_changes) {
1143             cf_write_status_t status;
1144
1145             /* This is not a temporary capture file, but it has unsaved
1146                changes, so saving it means doing a "safe save" on top
1147                of the existing file, in the same format - no UI needed
1148                unless the file has comments and the file's format doesn't
1149                support them.
1150
1151                If the file has comments, does the file's format support them?
1152                If not, ask the user whether they want to discard the comments
1153                or choose a different format. */
1154             switch (CaptureFileDialog::checkSaveAsWithComments(this, cf, cf->cd_t)) {
1155
1156             case SAVE:
1157                 /* The file can be saved in the specified format as is;
1158                    just drive on and save in the format they selected. */
1159                 discard_comments = FALSE;
1160                 break;
1161
1162             case SAVE_WITHOUT_COMMENTS:
1163                 /* The file can't be saved in the specified format as is,
1164                    but it can be saved without the comments, and the user
1165                    said "OK, discard the comments", so save it in the
1166                    format they specified without the comments. */
1167                 discard_comments = TRUE;
1168                 break;
1169
1170             case SAVE_IN_ANOTHER_FORMAT:
1171                 /* There are file formats in which we can save this that
1172                    support comments, and the user said not to delete the
1173                    comments.  Do a "Save As" so the user can select
1174                    one of those formats and choose a file name. */
1175                 saveAsCaptureFile(cf, TRUE, dont_reopen);
1176                 return;
1177
1178             case CANCELLED:
1179                 /* The user said "forget it".  Just return. */
1180                 return;
1181
1182             default:
1183                 /* Squelch warnings that discard_comments is being used
1184                    uninitialized. */
1185                 g_assert_not_reached();
1186                 return;
1187             }
1188
1189             /* XXX - cf->filename might get freed out from under us, because
1190                the code path through which cf_save_records() goes currently
1191                closes the current file and then opens and reloads the saved file,
1192                so make a copy and free it later. */
1193             file_name = cf->filename;
1194             status = cf_save_records(cf, file_name.toUtf8().constData(), cf->cd_t, cf->iscompressed,
1195                                      discard_comments, dont_reopen);
1196             switch (status) {
1197
1198             case CF_WRITE_OK:
1199                 /* The save succeeded; we're done.
1200                    If we discarded comments, redraw the packet list to reflect
1201                    any packets that no longer have comments. */
1202                 if (discard_comments)
1203                     packet_list_queue_draw();
1204
1205                 cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
1206                 updateForUnsavedChanges(); // we update the title bar to remove the *
1207                 break;
1208
1209             case CF_WRITE_ERROR:
1210                 /* The write failed.
1211                    XXX - OK, what do we do now?  Let them try a
1212                    "Save As", in case they want to try to save to a
1213                    different directory r file system? */
1214                 break;
1215
1216             case CF_WRITE_ABORTED:
1217                 /* The write was aborted; just drive on. */
1218                 break;
1219             }
1220         }
1221         /* Otherwise just do nothing. */
1222     }
1223 }
1224
1225 void MainWindow::saveAsCaptureFile(capture_file *cf, bool must_support_comments, bool dont_reopen) {
1226     QString file_name = "";
1227     int file_type;
1228     gboolean compressed;
1229     cf_write_status_t status;
1230     gchar   *dirname;
1231     gboolean discard_comments = FALSE;
1232
1233     if (!cf) {
1234         return;
1235     }
1236
1237     for (;;) {
1238         CaptureFileDialog save_as_dlg(this, cf);
1239
1240         /* If the file has comments, does the format the user selected
1241            support them?  If not, ask the user whether they want to
1242            discard the comments or choose a different format. */
1243         switch(save_as_dlg.saveAs(file_name, must_support_comments)) {
1244
1245         case SAVE:
1246             /* The file can be saved in the specified format as is;
1247                just drive on and save in the format they selected. */
1248             discard_comments = FALSE;
1249             break;
1250
1251         case SAVE_WITHOUT_COMMENTS:
1252             /* The file can't be saved in the specified format as is,
1253                but it can be saved without the comments, and the user
1254                said "OK, discard the comments", so save it in the
1255                format they specified without the comments. */
1256             discard_comments = TRUE;
1257             break;
1258
1259         case SAVE_IN_ANOTHER_FORMAT:
1260             /* There are file formats in which we can save this that
1261                support comments, and the user said not to delete the
1262                comments.  The combo box of file formats has had the
1263                formats that don't support comments trimmed from it,
1264                so run the dialog again, to let the user decide
1265                whether to save in one of those formats or give up. */
1266             must_support_comments = TRUE;
1267             continue;
1268
1269         case CANCELLED:
1270             /* The user said "forget it".  Just get rid of the dialog box
1271                and return. */
1272             return;
1273         }
1274         file_type = save_as_dlg.selectedFileType();
1275         compressed = save_as_dlg.isCompressed();
1276
1277         fileAddExtension(file_name, file_type, compressed);
1278
1279 //#ifndef _WIN32
1280 //        /* If the file exists and it's user-immutable or not writable,
1281 //                       ask the user whether they want to override that. */
1282 //        if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
1283 //            /* They don't.  Let them try another file name or cancel. */
1284 //            continue;
1285 //        }
1286 //#endif
1287
1288         /* Attempt to save the file */
1289         status = cf_save_records(cf, file_name.toUtf8().constData(), file_type, compressed,
1290                                  discard_comments, dont_reopen);
1291         switch (status) {
1292
1293         case CF_WRITE_OK:
1294             /* The save succeeded; we're done. */
1295             /* Save the directory name for future file dialogs. */
1296             dirname = qstring_strdup(file_name);  /* Overwrites cf_name */
1297             set_last_open_dir(get_dirname(dirname));
1298             g_free(dirname);
1299             /* If we discarded comments, redraw the packet list to reflect
1300                any packets that no longer have comments. */
1301             if (discard_comments)
1302                 packet_list_queue_draw();
1303
1304             cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
1305             updateForUnsavedChanges(); // we update the title bar to remove the *
1306             return;
1307
1308         case CF_WRITE_ERROR:
1309             /* The save failed; let the user try again. */
1310             continue;
1311
1312         case CF_WRITE_ABORTED:
1313             /* The user aborted the save; just return. */
1314             return;
1315         }
1316     }
1317     return;
1318 }
1319
1320 void MainWindow::exportSelectedPackets() {
1321     QString file_name = "";
1322     int file_type;
1323     gboolean compressed;
1324     packet_range_t range;
1325     cf_write_status_t status;
1326     gchar   *dirname;
1327     gboolean discard_comments = FALSE;
1328
1329     if (!capture_file_.capFile())
1330         return;
1331
1332     /* Init the packet range */
1333     packet_range_init(&range, capture_file_.capFile());
1334     range.process_filtered = TRUE;
1335     range.include_dependents = TRUE;
1336
1337     for (;;) {
1338         CaptureFileDialog esp_dlg(this, capture_file_.capFile());
1339
1340         /* If the file has comments, does the format the user selected
1341            support them?  If not, ask the user whether they want to
1342            discard the comments or choose a different format. */
1343         switch(esp_dlg.exportSelectedPackets(file_name, &range)) {
1344
1345         case SAVE:
1346             /* The file can be saved in the specified format as is;
1347                just drive on and save in the format they selected. */
1348             discard_comments = FALSE;
1349             break;
1350
1351         case SAVE_WITHOUT_COMMENTS:
1352             /* The file can't be saved in the specified format as is,
1353                but it can be saved without the comments, and the user
1354                said "OK, discard the comments", so save it in the
1355                format they specified without the comments. */
1356             discard_comments = TRUE;
1357             break;
1358
1359         case SAVE_IN_ANOTHER_FORMAT:
1360             /* There are file formats in which we can save this that
1361                support comments, and the user said not to delete the
1362                comments.  The combo box of file formats has had the
1363                formats that don't support comments trimmed from it,
1364                so run the dialog again, to let the user decide
1365                whether to save in one of those formats or give up. */
1366             continue;
1367
1368         case CANCELLED:
1369             /* The user said "forget it".  Just get rid of the dialog box
1370                and return. */
1371             return;
1372         }
1373
1374         /*
1375          * Check that we're not going to save on top of the current
1376          * capture file.
1377          * We do it here so we catch all cases ...
1378          * Unfortunately, the file requester gives us an absolute file
1379          * name and the read file name may be relative (if supplied on
1380          * the command line). From Joerg Mayer.
1381          */
1382         if (files_identical(capture_file_.capFile()->filename, file_name.toUtf8().constData())) {
1383             QMessageBox msg_box;
1384             gchar *display_basename = g_filename_display_basename(file_name.toUtf8().constData());
1385
1386             msg_box.setIcon(QMessageBox::Critical);
1387             msg_box.setText(QString(tr("Unable to export to \"%1\".").arg(display_basename)));
1388             msg_box.setInformativeText(tr("You cannot export packets to the current capture file."));
1389             msg_box.setStandardButtons(QMessageBox::Ok);
1390             msg_box.setDefaultButton(QMessageBox::Ok);
1391             msg_box.exec();
1392             g_free(display_basename);
1393             continue;
1394         }
1395
1396         file_type = esp_dlg.selectedFileType();
1397         compressed = esp_dlg.isCompressed();
1398         fileAddExtension(file_name, file_type, compressed);
1399
1400 //#ifndef _WIN32
1401 //        /* If the file exists and it's user-immutable or not writable,
1402 //                       ask the user whether they want to override that. */
1403 //        if (!file_target_unwritable_ui(top_level, file_name.toUtf8().constData())) {
1404 //            /* They don't.  Let them try another file name or cancel. */
1405 //            continue;
1406 //        }
1407 //#endif
1408
1409         /* Attempt to save the file */
1410         status = cf_export_specified_packets(capture_file_.capFile(), file_name.toUtf8().constData(), &range, file_type, compressed);
1411         switch (status) {
1412
1413         case CF_WRITE_OK:
1414             /* The save succeeded; we're done. */
1415             /* Save the directory name for future file dialogs. */
1416             dirname = qstring_strdup(file_name);  /* Overwrites cf_name */
1417             set_last_open_dir(get_dirname(dirname));
1418             g_free(dirname);
1419             /* If we discarded comments, redraw the packet list to reflect
1420                any packets that no longer have comments. */
1421             if (discard_comments)
1422                 packet_list_queue_draw();
1423             return;
1424
1425         case CF_WRITE_ERROR:
1426             /* The save failed; let the user try again. */
1427             continue;
1428
1429         case CF_WRITE_ABORTED:
1430             /* The user aborted the save; just return. */
1431             return;
1432         }
1433     }
1434     return;
1435 }
1436
1437 void MainWindow::exportDissections(export_type_e export_type) {
1438     ExportDissectionDialog ed_dlg(this, capture_file_.capFile(), export_type);
1439     packet_range_t range;
1440
1441     if (!capture_file_.capFile())
1442         return;
1443
1444     /* Init the packet range */
1445     packet_range_init(&range, capture_file_.capFile());
1446     range.process_filtered = TRUE;
1447     range.include_dependents = TRUE;
1448
1449     ed_dlg.exec();
1450 }
1451
1452 void MainWindow::fileAddExtension(QString &file_name, int file_type, bool compressed) {
1453     QString file_name_lower;
1454     QString file_suffix;
1455     GSList  *extensions_list;
1456     gboolean add_extension;
1457
1458     /*
1459      * Append the default file extension if there's none given by
1460      * the user or if they gave one that's not one of the valid
1461      * extensions for the file type.
1462      */
1463     file_name_lower = file_name.toLower();
1464     extensions_list = wtap_get_file_extensions_list(file_type, FALSE);
1465     if (extensions_list != NULL) {
1466         GSList *extension;
1467
1468         /* We have one or more extensions for this file type.
1469            Start out assuming we need to add the default one. */
1470         add_extension = TRUE;
1471
1472         /* OK, see if the file has one of those extensions. */
1473         for (extension = extensions_list; extension != NULL;
1474              extension = g_slist_next(extension)) {
1475             file_suffix += tr(".") + (char *)extension->data;
1476             if (file_name_lower.endsWith(file_suffix)) {
1477                 /*
1478                  * The file name has one of the extensions for
1479                  * this file type.
1480                  */
1481                 add_extension = FALSE;
1482                 break;
1483             }
1484             file_suffix += ".gz";
1485             if (file_name_lower.endsWith(file_suffix)) {
1486                 /*
1487                  * The file name has one of the extensions for
1488                  * this file type.
1489                  */
1490                 add_extension = FALSE;
1491                 break;
1492             }
1493         }
1494     } else {
1495         /* We have no extensions for this file type.  Don't add one. */
1496         add_extension = FALSE;
1497     }
1498     if (add_extension) {
1499         if (wtap_default_file_extension(file_type) != NULL) {
1500             file_name += tr(".") + wtap_default_file_extension(file_type);
1501             if (compressed) {
1502                 file_name += ".gz";
1503             }
1504         }
1505     }
1506 }
1507
1508 bool MainWindow::testCaptureFileClose(QString before_what, FileCloseContext context) {
1509     bool capture_in_progress = false;
1510     bool do_close_file = false;
1511
1512     if (!capture_file_.capFile() || capture_file_.capFile()->state == FILE_CLOSED)
1513         return true; /* Already closed, nothing to do */
1514
1515 #ifdef HAVE_LIBPCAP
1516     if (capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1517         /* This is true if we're reading a capture file *or* if we're doing
1518          a live capture.  If we're reading a capture file, the main loop
1519          is busy reading packets, and only accepting input from the
1520          progress dialog, so we can't get here, so this means we're
1521          doing a capture. */
1522         capture_in_progress = true;
1523     }
1524 #endif
1525
1526     if (prefs.gui_ask_unsaved) {
1527         if (cf_has_unsaved_data(capture_file_.capFile()) ||
1528             (capture_in_progress && capture_file_.capFile()->count > 0))
1529         {
1530             QMessageBox msg_dialog;
1531             QString question;
1532             QString infotext;
1533             QPushButton *save_button;
1534             QPushButton *discard_button;
1535
1536             msg_dialog.setIcon(QMessageBox::Question);
1537             msg_dialog.setWindowTitle("Unsaved packets" UTF8_HORIZONTAL_ELLIPSIS);
1538
1539             /* This file has unsaved data or there's a capture in
1540                progress; ask the user whether to save the data. */
1541             if (capture_in_progress && context != Restart) {
1542                 question = tr("Do you want to stop the capture and save the captured packets%1?").arg(before_what);
1543                 infotext = tr("Your captured packets will be lost if you don't save them.");
1544             } else if (capture_file_.capFile()->is_tempfile) {
1545                 if (context == Reload) {
1546                     // Reloading a tempfile will keep the packets, so this is not unsaved packets
1547                     question = tr("Do you want to save the changes you've made%1?").arg(before_what);
1548                     infotext = tr("Your changes will be lost if you don't save them.");
1549                 } else {
1550                     question = tr("Do you want to save the captured packets%1?").arg(before_what);
1551                     infotext = tr("Your captured packets will be lost if you don't save them.");
1552                 }
1553             } else {
1554                 // No capture in progress and not a tempfile, so this is not unsaved packets
1555                 gchar *display_basename = g_filename_display_basename(capture_file_.capFile()->filename);
1556                 question = tr("Do you want to save the changes you've made to the capture file \"%1\"%2?").arg(display_basename, before_what);
1557                 infotext = tr("Your changes will be lost if you don't save them.");
1558                 g_free(display_basename);
1559             }
1560
1561             msg_dialog.setText(question);
1562             msg_dialog.setInformativeText(infotext);
1563
1564             // XXX Text comes from ui/gtk/stock_icons.[ch]
1565             // Note that the button roles differ from the GTK+ version.
1566             // Cancel = RejectRole
1567             // Save = AcceptRole
1568             // Don't Save = DestructiveRole
1569             msg_dialog.addButton(QMessageBox::Cancel);
1570
1571             if (capture_in_progress) {
1572                 QString save_button_text;
1573                 if (context == Restart) {
1574                     save_button_text = tr("Save before Continue");
1575                 } else {
1576                     save_button_text = tr("Stop and Save");
1577                 }
1578                 save_button = msg_dialog.addButton(save_button_text, QMessageBox::AcceptRole);
1579             } else {
1580                 save_button = msg_dialog.addButton(QMessageBox::Save);
1581             }
1582             msg_dialog.setDefaultButton(save_button);
1583
1584             QString discard_button_text;
1585             if (capture_in_progress) {
1586                 switch (context) {
1587                 case Quit:
1588                     discard_button_text = tr("Stop and Quit without Saving");
1589                     break;
1590                 case Restart:
1591                     discard_button_text = tr("Continue without Saving");
1592                     break;
1593                 default:
1594                     discard_button_text = tr("Stop and Continue without Saving");
1595                     break;
1596                 }
1597             } else {
1598                 switch (context) {
1599                 case Quit:
1600                     discard_button_text = tr("Quit without Saving");
1601                     break;
1602                 case Restart:
1603                 default:
1604                     discard_button_text = tr("Continue without Saving");
1605                     break;
1606                 }
1607             }
1608             discard_button = msg_dialog.addButton(discard_button_text, QMessageBox::DestructiveRole);
1609
1610             msg_dialog.exec();
1611             /* According to the Qt doc:
1612              * when using QMessageBox with custom buttons, exec() function returns an opaque value.
1613              *
1614              * Therefore we should use clickedButton() to determine which button was clicked. */
1615
1616             if (msg_dialog.clickedButton() == save_button) {
1617 #ifdef HAVE_LIBPCAP
1618                 /* If there's a capture in progress, we have to stop the capture
1619                    and then do the save. */
1620                 if (capture_in_progress)
1621                     captureStop();
1622 #endif
1623                 /* Save the file and close it */
1624                 saveCaptureFile(capture_file_.capFile(), true);
1625             } else if(msg_dialog.clickedButton() == discard_button) {
1626                 /* Just close the file, discarding changes */
1627                 do_close_file = true;
1628             } else {
1629                 // cancelButton or some other unspecified button
1630                 return false;
1631             }
1632         } else {
1633             /* Unchanged file or capturing with no packets */
1634             do_close_file = true;
1635         }
1636     } else {
1637         /* User asked not to be bothered by those prompts, just close it.
1638          XXX - should that apply only to saving temporary files? */
1639         do_close_file = true;
1640     }
1641
1642     if (do_close_file) {
1643 #ifdef HAVE_LIBPCAP
1644         /* If there's a capture in progress, we have to stop the capture
1645            and then do the close. */
1646         if (capture_in_progress)
1647             captureStop();
1648 #endif
1649         /* captureStop() will close the file if not having any packets */
1650         if (capture_file_.capFile() && context != Restart && context != Reload)
1651             // Don't really close if Restart or Reload
1652             cf_close(capture_file_.capFile());
1653     }
1654
1655     return true; /* File closed */
1656 }
1657
1658 void MainWindow::captureStop() {
1659     stopCapture();
1660
1661     while(capture_file_.capFile() && capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1662         WiresharkApplication::processEvents();
1663     }
1664 }
1665
1666 void MainWindow::initMainToolbarIcons()
1667 {
1668     // Normally 16 px. Reflects current GTK+ behavior and other Windows apps.
1669     int icon_size = style()->pixelMetric(QStyle::PM_SmallIconSize);
1670 #if !defined(Q_OS_WIN)
1671     // Force icons to 24x24 for now, otherwise actionFileOpen looks wonky.
1672     // The OS X HIG specifies 32-pixel icons but they're a little too
1673     // large IMHO.
1674     icon_size = icon_size * 3 / 2;
1675 #endif
1676     main_ui_->mainToolBar->setIconSize(QSize(icon_size, icon_size));
1677
1678     // Toolbar actions. The GNOME HIG says that we should have a menu icon for each
1679     // toolbar item but that clutters up our menu. Set menu icons sparingly.
1680
1681     main_ui_->actionCaptureStart->setIcon(StockIcon("x-capture-start"));
1682     main_ui_->actionCaptureStop->setIcon(StockIcon("x-capture-stop"));
1683     main_ui_->actionCaptureRestart->setIcon(StockIcon("x-capture-restart"));
1684     main_ui_->actionCaptureOptions->setIcon(StockIcon("x-capture-options"));
1685
1686     // Menu icons are disabled in main_window.ui for these items.
1687     main_ui_->actionFileOpen->setIcon(StockIcon("document-open"));
1688     main_ui_->actionFileSave->setIcon(StockIcon("x-capture-file-save"));
1689     main_ui_->actionFileClose->setIcon(StockIcon("x-capture-file-close"));
1690     main_ui_->actionViewReload->setIcon(StockIcon("x-capture-file-reload"));
1691
1692     main_ui_->actionEditFindPacket->setIcon(StockIcon("edit-find"));
1693     main_ui_->actionGoPreviousPacket->setIcon(StockIcon("go-previous"));
1694     main_ui_->actionGoNextPacket->setIcon(StockIcon("go-next"));
1695     main_ui_->actionGoGoToPacket->setIcon(StockIcon("go-jump"));
1696     main_ui_->actionGoFirstPacket->setIcon(StockIcon("go-first"));
1697     main_ui_->actionGoLastPacket->setIcon(StockIcon("go-last"));
1698     main_ui_->actionGoPreviousConversationPacket->setIcon(StockIcon("go-previous"));
1699     main_ui_->actionGoNextConversationPacket->setIcon(StockIcon("go-next"));
1700 #if defined(Q_OS_MAC)
1701     main_ui_->actionGoPreviousConversationPacket->setShortcut(QKeySequence(Qt::META | Qt::Key_Comma));
1702     main_ui_->actionGoNextConversationPacket->setShortcut(QKeySequence(Qt::META | Qt::Key_Period));
1703 #endif
1704     main_ui_->actionGoAutoScroll->setIcon(StockIcon("x-stay-last"));
1705
1706     main_ui_->actionViewColorizePacketList->setIcon(StockIcon("x-colorize-packets"));
1707 //    main_ui_->actionViewAutoScroll->setIcon(StockIcon("x-stay-last"));
1708
1709     QList<QKeySequence> zi_seq = main_ui_->actionViewZoomIn->shortcuts();
1710     zi_seq << QKeySequence(Qt::CTRL + Qt::Key_Equal);
1711     main_ui_->actionViewZoomIn->setIcon(StockIcon("zoom-in"));
1712     main_ui_->actionViewZoomIn->setShortcuts(zi_seq);
1713     main_ui_->actionViewZoomOut->setIcon(StockIcon("zoom-out"));
1714     main_ui_->actionViewNormalSize->setIcon(StockIcon("zoom-original"));
1715     main_ui_->actionViewResizeColumns->setIcon(StockIcon("x-resize-columns"));
1716 }
1717
1718 void MainWindow::initShowHideMainWidgets()
1719 {
1720     if (show_hide_actions_) {
1721         return;
1722     }
1723
1724     show_hide_actions_ = new QActionGroup(this);
1725     QMap<QAction *, QWidget *> shmw_actions;
1726
1727     show_hide_actions_->setExclusive(false);
1728     shmw_actions[main_ui_->actionViewMainToolbar] = main_ui_->mainToolBar;
1729     shmw_actions[main_ui_->actionViewFilterToolbar] = main_ui_->displayFilterToolBar;
1730     shmw_actions[main_ui_->actionViewWirelessToolbar] = main_ui_->wirelessToolBar;
1731     shmw_actions[main_ui_->actionViewStatusBar] = main_ui_->statusBar;
1732     shmw_actions[main_ui_->actionViewPacketList] = packet_list_;
1733     shmw_actions[main_ui_->actionViewPacketDetails] = proto_tree_;
1734     shmw_actions[main_ui_->actionViewPacketBytes] = byte_view_tab_;
1735
1736     foreach (QAction *shmwa, shmw_actions.keys()) {
1737         shmwa->setData(qVariantFromValue(shmw_actions[shmwa]));
1738         show_hide_actions_->addAction(shmwa);
1739         showHideMainWidgets(shmwa);
1740     }
1741
1742     connect(show_hide_actions_, SIGNAL(triggered(QAction*)), this, SLOT(showHideMainWidgets(QAction*)));
1743 }
1744
1745 Q_DECLARE_METATYPE(ts_type)
1746
1747 void MainWindow::initTimeDisplayFormatMenu()
1748 {
1749     if (time_display_actions_) {
1750         return;
1751     }
1752
1753     time_display_actions_ = new QActionGroup(this);
1754
1755     td_actions[main_ui_->actionViewTimeDisplayFormatDateYMDandTimeOfDay] = TS_ABSOLUTE_WITH_YMD;
1756     td_actions[main_ui_->actionViewTimeDisplayFormatDateYDOYandTimeOfDay] = TS_ABSOLUTE_WITH_YDOY;
1757     td_actions[main_ui_->actionViewTimeDisplayFormatTimeOfDay] = TS_ABSOLUTE;
1758     td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSinceEpoch] = TS_EPOCH;
1759     td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSinceBeginningOfCapture] = TS_RELATIVE;
1760     td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSincePreviousCapturedPacket] = TS_DELTA;
1761     td_actions[main_ui_->actionViewTimeDisplayFormatSecondsSincePreviousDisplayedPacket] = TS_DELTA_DIS;
1762     td_actions[main_ui_->actionViewTimeDisplayFormatUTCDateYMDandTimeOfDay] = TS_UTC_WITH_YMD;
1763     td_actions[main_ui_->actionViewTimeDisplayFormatUTCDateYDOYandTimeOfDay] = TS_UTC_WITH_YDOY;
1764     td_actions[main_ui_->actionViewTimeDisplayFormatUTCTimeOfDay] = TS_UTC;
1765
1766     foreach (QAction* tda, td_actions.keys()) {
1767         tda->setData(qVariantFromValue(td_actions[tda]));
1768         time_display_actions_->addAction(tda);
1769     }
1770
1771     connect(time_display_actions_, SIGNAL(triggered(QAction*)), this, SLOT(setTimestampFormat(QAction*)));
1772 }
1773
1774 Q_DECLARE_METATYPE(ts_precision)
1775
1776 void MainWindow::initTimePrecisionFormatMenu()
1777 {
1778     if (time_precision_actions_) {
1779         return;
1780     }
1781
1782     time_precision_actions_ = new QActionGroup(this);
1783
1784     tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionAutomatic] = TS_PREC_AUTO;
1785     tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionSeconds] = TS_PREC_FIXED_SEC;
1786     tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionDeciseconds] = TS_PREC_FIXED_DSEC;
1787     tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionCentiseconds] = TS_PREC_FIXED_CSEC;
1788     tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionMilliseconds] = TS_PREC_FIXED_MSEC;
1789     tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionMicroseconds] = TS_PREC_FIXED_USEC;
1790     tp_actions[main_ui_->actionViewTimeDisplayFormatPrecisionNanoseconds] = TS_PREC_FIXED_NSEC;
1791
1792     foreach (QAction* tpa, tp_actions.keys()) {
1793         tpa->setData(qVariantFromValue(tp_actions[tpa]));
1794         time_precision_actions_->addAction(tpa);
1795     }
1796
1797     connect(time_precision_actions_, SIGNAL(triggered(QAction*)), this, SLOT(setTimestampPrecision(QAction*)));
1798 }
1799
1800 // Menu items which will be disabled when we freeze() and whose state will
1801 // be restored when we thaw(). Add to the list as needed.
1802 void MainWindow::initFreezeActions()
1803 {
1804     QList<QAction *> freeze_actions = QList<QAction *>()
1805             << main_ui_->actionFileClose
1806             << main_ui_->actionViewReload
1807             << main_ui_->actionEditMarkPacket
1808             << main_ui_->actionEditMarkAllDisplayed
1809             << main_ui_->actionEditUnmarkAllDisplayed
1810             << main_ui_->actionEditIgnorePacket
1811             << main_ui_->actionEditIgnoreAllDisplayed
1812             << main_ui_->actionEditUnignoreAllDisplayed
1813             << main_ui_->actionEditSetTimeReference
1814             << main_ui_->actionEditUnsetAllTimeReferences;
1815
1816     foreach (QAction *action, freeze_actions) {
1817         freeze_actions_ << QPair<QAction *, bool>(action, false);
1818     }
1819 }
1820
1821 // Titlebar
1822 void MainWindow::setTitlebarForCaptureFile()
1823 {
1824     if (capture_file_.capFile() && capture_file_.capFile()->filename) {
1825         if (capture_file_.capFile()->is_tempfile) {
1826             //
1827             // For a temporary file, put the source of the data
1828             // in the window title, not whatever random pile
1829             // of characters is the last component of the path
1830             // name.
1831             //
1832             // XXX - on non-Mac platforms, put in the application
1833             // name?
1834             //
1835             setWSWindowTitle(QString("[*]%1").arg(cf_get_tempfile_source(capture_file_.capFile())));
1836         } else {
1837             //
1838             // For a user file, set the full path; that way,
1839             // for OS X, it'll set the "proxy icon".  Qt
1840             // handles extracting the last component.
1841             //
1842             // Sadly, some UN*Xes don't necessarily use UTF-8
1843             // for their file names, so we have to map the
1844             // file path to UTF-8.  If that fails, we're somewhat
1845             // stuck.
1846             //
1847             char *utf8_filename = g_filename_to_utf8(capture_file_.capFile()->filename,
1848                                                      -1,
1849                                                      NULL,
1850                                                      NULL,
1851                                                      NULL);
1852             if (utf8_filename) {
1853                 QFileInfo fi(utf8_filename);
1854                 setWSWindowTitle(QString("[*]%1").arg(fi.fileName()));
1855                 setWindowFilePath(utf8_filename);
1856                 g_free(utf8_filename);
1857             } else {
1858                 // So what the heck else can we do here?
1859                 setWSWindowTitle(tr("(File name can't be mapped to UTF-8)"));
1860             }
1861         }
1862         setWindowModified(cf_has_unsaved_data(capture_file_.capFile()));
1863     } else {
1864         /* We have no capture file. */
1865         setWSWindowTitle();
1866     }
1867 }
1868
1869 QString MainWindow::replaceWindowTitleVariables(QString title)
1870 {
1871     title.replace ("%P", get_profile_name());
1872     title.replace ("%V", get_ws_vcs_version_info());
1873
1874     return title;
1875 }
1876
1877 void MainWindow::setWSWindowTitle(QString title)
1878 {
1879     if (title.isEmpty()) {
1880         title = tr("The Wireshark Network Analyzer");
1881     }
1882
1883     if (prefs.gui_prepend_window_title && prefs.gui_prepend_window_title[0]) {
1884         QString custom_title = replaceWindowTitleVariables(prefs.gui_prepend_window_title);
1885         title.prepend(QString("[%1] ").arg(custom_title));
1886     }
1887
1888     if (prefs.gui_window_title && prefs.gui_window_title[0]) {
1889         QString custom_title = replaceWindowTitleVariables(prefs.gui_window_title);
1890 #ifdef __APPLE__
1891         // On OS X we separate the titles with a unicode em dash
1892         title.append(QString(" %1 %2").arg(UTF8_EM_DASH).arg(custom_title));
1893 #else
1894         title.append(QString(" [%1]").arg(custom_title));
1895 #endif
1896     }
1897
1898     setWindowTitle(title);
1899     setWindowFilePath(NULL);
1900 }
1901
1902 void MainWindow::setTitlebarForCaptureInProgress()
1903 {
1904     if (capture_file_.capFile()) {
1905         setWSWindowTitle(tr("Capturing from %1").arg(cf_get_tempfile_source(capture_file_.capFile())));
1906     } else {
1907         /* We have no capture in progress. */
1908         setWSWindowTitle();
1909     }
1910 }
1911
1912 // Menu state
1913
1914 /* Enable or disable menu items based on whether you have a capture file
1915    you've finished reading and, if you have one, whether it's been saved
1916    and whether it could be saved except by copying the raw packet data. */
1917 void MainWindow::setMenusForCaptureFile(bool force_disable)
1918 {
1919     if (force_disable || capture_file_.capFile() == NULL || capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
1920         /* We have no capture file or we're currently reading a file */
1921         main_ui_->actionViewReload_as_File_Format_or_Capture->setEnabled(false);
1922         main_ui_->actionFileMerge->setEnabled(false);
1923         main_ui_->actionFileClose->setEnabled(false);
1924         main_ui_->actionFileSave->setEnabled(false);
1925         main_ui_->actionFileSaveAs->setEnabled(false);
1926         main_ui_->actionStatisticsCaptureFileProperties->setEnabled(false);
1927         main_ui_->actionFileExportPackets->setEnabled(false);
1928         main_ui_->menuFileExportPacketDissections->setEnabled(false);
1929         main_ui_->actionFileExportPacketBytes->setEnabled(false);
1930         main_ui_->actionFileExportPDU->setEnabled(false);
1931         main_ui_->actionFileExportSSLSessionKeys->setEnabled(false);
1932         main_ui_->menuFileExportObjects->setEnabled(false);
1933         main_ui_->actionViewReload->setEnabled(false);
1934     } else {
1935         main_ui_->actionViewReload_as_File_Format_or_Capture->setEnabled(true);
1936         main_ui_->actionFileMerge->setEnabled(cf_can_write_with_wiretap(capture_file_.capFile()));
1937
1938         main_ui_->actionFileClose->setEnabled(true);
1939         main_ui_->actionFileSave->setEnabled(cf_can_save(capture_file_.capFile()));
1940         main_ui_->actionFileSaveAs->setEnabled(cf_can_save_as(capture_file_.capFile()));
1941         main_ui_->actionStatisticsCaptureFileProperties->setEnabled(true);
1942         /*
1943          * "Export Specified Packets..." should be available only if
1944          * we can write the file out in at least one format.
1945          */
1946         main_ui_->actionFileExportPackets->setEnabled(cf_can_write_with_wiretap(capture_file_.capFile()));
1947         main_ui_->menuFileExportPacketDissections->setEnabled(true);
1948         main_ui_->actionFileExportPacketBytes->setEnabled(true);
1949         main_ui_->actionFileExportPDU->setEnabled(true);
1950         main_ui_->actionFileExportSSLSessionKeys->setEnabled(true);
1951         main_ui_->menuFileExportObjects->setEnabled(true);
1952         main_ui_->actionViewReload->setEnabled(true);
1953     }
1954 }
1955
1956 void MainWindow::setMenusForCaptureInProgress(bool capture_in_progress) {
1957     /* Either a capture was started or stopped; in either case, it's not
1958        in the process of stopping, so allow quitting. */
1959
1960     main_ui_->actionFileOpen->setEnabled(!capture_in_progress);
1961     main_ui_->menuOpenRecentCaptureFile->setEnabled(!capture_in_progress);
1962     main_ui_->menuFileExportPacketDissections->setEnabled(capture_in_progress);
1963     main_ui_->actionFileExportPacketBytes->setEnabled(capture_in_progress);
1964     main_ui_->actionFileExportPDU->setEnabled(capture_in_progress);
1965     main_ui_->actionFileExportSSLSessionKeys->setEnabled(capture_in_progress);
1966     main_ui_->menuFileExportObjects->setEnabled(capture_in_progress);
1967     main_ui_->menuFileSet->setEnabled(!capture_in_progress);
1968     main_ui_->actionFileQuit->setEnabled(true);
1969
1970     main_ui_->actionStatisticsCaptureFileProperties->setEnabled(capture_in_progress);
1971
1972     // XXX Fix packet list heading menu sensitivity
1973     //    set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortAscending",
1974     //                         !capture_in_progress);
1975     //    set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortDescending",
1976     //                         !capture_in_progress);
1977     //    set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/NoSorting",
1978     //                         !capture_in_progress);
1979
1980 #ifdef HAVE_LIBPCAP
1981     main_ui_->actionCaptureOptions->setEnabled(!capture_in_progress);
1982     main_ui_->actionCaptureStart->setEnabled(!capture_in_progress);
1983     main_ui_->actionCaptureStart->setChecked(capture_in_progress);
1984     main_ui_->actionCaptureStop->setEnabled(capture_in_progress);
1985     main_ui_->actionCaptureRestart->setEnabled(capture_in_progress);
1986     main_ui_->actionCaptureRefreshInterfaces->setEnabled(!capture_in_progress);
1987 #endif /* HAVE_LIBPCAP */
1988
1989 }
1990
1991 void MainWindow::setMenusForCaptureStopping() {
1992     main_ui_->actionFileQuit->setEnabled(false);
1993     main_ui_->actionStatisticsCaptureFileProperties->setEnabled(false);
1994 #ifdef HAVE_LIBPCAP
1995     main_ui_->actionCaptureStart->setChecked(false);
1996     main_ui_->actionCaptureStop->setEnabled(false);
1997     main_ui_->actionCaptureRestart->setEnabled(false);
1998 #endif /* HAVE_LIBPCAP */
1999 }
2000
2001 void MainWindow::setForCapturedPackets(bool have_captured_packets)
2002 {
2003     main_ui_->actionFilePrint->setEnabled(have_captured_packets);
2004
2005 //    set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/Print",
2006 //                         have_captured_packets);
2007
2008     main_ui_->actionEditFindPacket->setEnabled(have_captured_packets);
2009     main_ui_->actionEditFindNext->setEnabled(have_captured_packets);
2010     main_ui_->actionEditFindPrevious->setEnabled(have_captured_packets);
2011
2012     main_ui_->actionGoGoToPacket->setEnabled(have_captured_packets);
2013     main_ui_->actionGoPreviousPacket->setEnabled(have_captured_packets);
2014     main_ui_->actionGoNextPacket->setEnabled(have_captured_packets);
2015     main_ui_->actionGoFirstPacket->setEnabled(have_captured_packets);
2016     main_ui_->actionGoLastPacket->setEnabled(have_captured_packets);
2017     main_ui_->actionGoNextConversationPacket->setEnabled(have_captured_packets);
2018     main_ui_->actionGoPreviousConversationPacket->setEnabled(have_captured_packets);
2019
2020     main_ui_->actionViewZoomIn->setEnabled(have_captured_packets);
2021     main_ui_->actionViewZoomOut->setEnabled(have_captured_packets);
2022     main_ui_->actionViewNormalSize->setEnabled(have_captured_packets);
2023     main_ui_->actionViewResizeColumns->setEnabled(have_captured_packets);
2024
2025     main_ui_->actionStatisticsCaptureFileProperties->setEnabled(have_captured_packets);
2026     main_ui_->actionStatisticsProtocolHierarchy->setEnabled(have_captured_packets);
2027     main_ui_->actionStatisticsIOGraph->setEnabled(have_captured_packets);
2028 }
2029
2030 void MainWindow::setMenusForFileSet(bool enable_list_files) {
2031     bool enable_next = fileset_get_next() != NULL && enable_list_files;
2032     bool enable_prev = fileset_get_previous() != NULL && enable_list_files;
2033
2034     main_ui_->actionFileSetListFiles->setEnabled(enable_list_files);
2035     main_ui_->actionFileSetNextFile->setEnabled(enable_next);
2036     main_ui_->actionFileSetPreviousFile->setEnabled(enable_prev);
2037 }
2038
2039 void MainWindow::setWindowIcon(const QIcon &icon) {
2040     wsApp->setWindowIcon(icon);
2041     QMainWindow::setWindowIcon(icon);
2042 }
2043
2044 void MainWindow::updateForUnsavedChanges() {
2045     setTitlebarForCaptureFile();
2046     setMenusForCaptureFile();
2047 }
2048
2049 void MainWindow::changeEvent(QEvent* event)
2050 {
2051     if (0 != event)
2052     {
2053         switch (event->type())
2054         {
2055         case QEvent::LanguageChange:
2056             main_ui_->retranslateUi(this);
2057             // make sure that the "Clear Menu" item is retranslated
2058             updateRecentFiles();
2059             break;
2060         case QEvent::LocaleChange:{
2061             QString locale = QLocale::system().name();
2062             locale.truncate(locale.lastIndexOf('_'));
2063             wsApp->loadLanguage(locale);
2064             }
2065             break;
2066         default:
2067             break;
2068         }
2069     }
2070     QMainWindow::changeEvent(event);
2071 }
2072
2073 /* Update main window items based on whether there's a capture in progress. */
2074 void MainWindow::setForCaptureInProgress(gboolean capture_in_progress)
2075 {
2076     setMenusForCaptureInProgress(capture_in_progress);
2077
2078     wireless_frame_->setCaptureInProgress(capture_in_progress);
2079
2080 #ifdef HAVE_LIBPCAP
2081     packet_list_->setCaptureInProgress(capture_in_progress);
2082 //    set_toolbar_for_capture_in_progress(capture_in_progress);
2083
2084 //    set_capture_if_dialog_for_capture_in_progress(capture_in_progress);
2085 #endif
2086 }
2087
2088 static QList<register_stat_group_t> menu_groups = QList<register_stat_group_t>()
2089             << REGISTER_ANALYZE_GROUP_UNSORTED
2090             << REGISTER_ANALYZE_GROUP_CONVERSATION_FILTER
2091             << REGISTER_STAT_GROUP_UNSORTED
2092             << REGISTER_STAT_GROUP_GENERIC
2093             << REGISTER_STAT_GROUP_CONVERSATION_LIST
2094             << REGISTER_STAT_GROUP_ENDPOINT_LIST
2095             << REGISTER_STAT_GROUP_RESPONSE_TIME
2096             << REGISTER_STAT_GROUP_TELEPHONY
2097             << REGISTER_STAT_GROUP_TELEPHONY_ANSI
2098             << REGISTER_STAT_GROUP_TELEPHONY_GSM
2099             << REGISTER_STAT_GROUP_TELEPHONY_LTE
2100             << REGISTER_STAT_GROUP_TELEPHONY_MTP3
2101             << REGISTER_STAT_GROUP_TELEPHONY_SCTP
2102             << REGISTER_TOOLS_GROUP_UNSORTED;
2103
2104 void MainWindow::addMenuActions(QList<QAction *> &actions, int menu_group)
2105 {
2106     foreach (QAction *action, actions) {
2107         switch (menu_group) {
2108         case REGISTER_ANALYZE_GROUP_UNSORTED:
2109         case REGISTER_STAT_GROUP_UNSORTED:
2110             main_ui_->menuStatistics->insertAction(
2111                             main_ui_->actionStatistics_REGISTER_STAT_GROUP_UNSORTED,
2112                             action);
2113             break;
2114         case REGISTER_STAT_GROUP_RESPONSE_TIME:
2115             main_ui_->menuServiceResponseTime->addAction(action);
2116             break;
2117         case REGISTER_STAT_GROUP_TELEPHONY:
2118             main_ui_->menuTelephony->addAction(action);
2119             break;
2120         case REGISTER_STAT_GROUP_TELEPHONY_ANSI:
2121             main_ui_->menuANSI->addAction(action);
2122             break;
2123         case REGISTER_STAT_GROUP_TELEPHONY_GSM:
2124             main_ui_->menuGSM->addAction(action);
2125             break;
2126         case REGISTER_STAT_GROUP_TELEPHONY_LTE:
2127             main_ui_->menuLTE->addAction(action);
2128             break;
2129         case REGISTER_STAT_GROUP_TELEPHONY_MTP3:
2130             main_ui_->menuMTP3->addAction(action);
2131             break;
2132         case REGISTER_TOOLS_GROUP_UNSORTED:
2133         {
2134             // Allow the creation of submenus. Mimics the behavor of
2135             // ui/gtk/main_menubar.c:add_menu_item_to_main_menubar
2136             // and GtkUIManager.
2137             //
2138             // For now we limit the insanity to the "Tools" menu.
2139             QStringList menu_path = action->text().split('/');
2140             QMenu *cur_menu = main_ui_->menuTools;
2141             while (menu_path.length() > 1) {
2142                 QString menu_title = menu_path.takeFirst();
2143 #if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0))
2144                 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower(), Qt::FindDirectChildrenOnly);
2145 #else
2146                 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower());
2147                 if (submenu && submenu->parent() != cur_menu) submenu = NULL;
2148 #endif
2149                 if (!submenu) {
2150                     submenu = cur_menu->addMenu(menu_title);
2151                     submenu->setObjectName(menu_title.toLower());
2152                 }
2153                 cur_menu = submenu;
2154             }
2155             action->setText(menu_path.last());
2156             cur_menu->addAction(action);
2157             break;
2158         }
2159         default:
2160 //            qDebug() << "FIX: Add" << action->text() << "to the menu";
2161             break;
2162         }
2163
2164         // Connect each action type to its corresponding slot. We to
2165         // distinguish various types of actions. Setting their objectName
2166         // seems to work OK.
2167         if (action->objectName() == TapParameterDialog::actionName()) {
2168             connect(action, SIGNAL(triggered(bool)), this, SLOT(openTapParameterDialog()));
2169         } else if (action->objectName() == FunnelStatistics::actionName()) {
2170             connect(action, SIGNAL(triggered(bool)), funnel_statistics_, SLOT(funnelActionTriggered()));
2171         }
2172     }
2173 }
2174 void MainWindow::removeMenuActions(QList<QAction *> &actions, int menu_group)
2175 {
2176     foreach (QAction *action, actions) {
2177         switch (menu_group) {
2178         case REGISTER_ANALYZE_GROUP_UNSORTED:
2179         case REGISTER_STAT_GROUP_UNSORTED:
2180             main_ui_->menuStatistics->removeAction(action);
2181             break;
2182         case REGISTER_STAT_GROUP_RESPONSE_TIME:
2183             main_ui_->menuServiceResponseTime->removeAction(action);
2184             break;
2185         case REGISTER_STAT_GROUP_TELEPHONY:
2186             main_ui_->menuTelephony->removeAction(action);
2187             break;
2188         case REGISTER_STAT_GROUP_TELEPHONY_ANSI:
2189             main_ui_->menuANSI->removeAction(action);
2190             break;
2191         case REGISTER_STAT_GROUP_TELEPHONY_GSM:
2192             main_ui_->menuGSM->removeAction(action);
2193             break;
2194         case REGISTER_STAT_GROUP_TELEPHONY_LTE:
2195             main_ui_->menuLTE->removeAction(action);
2196             break;
2197         case REGISTER_STAT_GROUP_TELEPHONY_MTP3:
2198             main_ui_->menuMTP3->removeAction(action);
2199             break;
2200         case REGISTER_TOOLS_GROUP_UNSORTED:
2201         {
2202             // Allow removal of submenus.
2203             // For now we limit the insanity to the "Tools" menu.
2204             QStringList menu_path = action->text().split('/');
2205             QMenu *cur_menu = main_ui_->menuTools;
2206             while (menu_path.length() > 1) {
2207                 QString menu_title = menu_path.takeFirst();
2208 #if (QT_VERSION > QT_VERSION_CHECK(5, 0, 0))
2209                 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower(), Qt::FindDirectChildrenOnly);
2210 #else
2211                 QMenu *submenu = cur_menu->findChild<QMenu *>(menu_title.toLower());
2212                 if (submenu && submenu->parent() != cur_menu) submenu = NULL;
2213 #endif
2214                 cur_menu = submenu;
2215             }
2216             cur_menu->removeAction(action);
2217             break;
2218         }
2219         default:
2220 //            qDebug() << "FIX: Remove" << action->text() << "from the menu";
2221             break;
2222         }
2223     }
2224 }
2225
2226 void MainWindow::addDynamicMenus()
2227 {
2228     // Manual additions
2229     wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_GSM, main_ui_->actionTelephonyGsmMapSummary);
2230     wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteMacStatistics);
2231     wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteRlcStatistics);
2232     wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteRlcGraph);
2233     wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_MTP3, main_ui_->actionTelephonyMtp3Summary);
2234     wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY, main_ui_->actionTelephonySipFlows);
2235
2236     // Fill in each menu
2237     foreach (register_stat_group_t menu_group, menu_groups) {
2238         QList<QAction *>actions = wsApp->dynamicMenuGroupItems(menu_group);
2239         addMenuActions(actions, menu_group);
2240     }
2241
2242     // Empty menus don't show up: https://bugreports.qt.io/browse/QTBUG-33728
2243     // We've added a placeholder in order to make sure the "Tools" menu is
2244     // visible. Hide it as needed.
2245     if (wsApp->dynamicMenuGroupItems(REGISTER_TOOLS_GROUP_UNSORTED).length() > 0) {
2246         main_ui_->actionToolsPlaceholder->setVisible(false);
2247     }
2248     if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_ANSI).length() > 0) {
2249         main_ui_->actionTelephonyANSIPlaceholder->setVisible(false);
2250     }
2251     if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_GSM).length() > 0) {
2252         main_ui_->actionTelephonyGSMPlaceholder->setVisible(false);
2253     }
2254     if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_LTE).length() > 0) {
2255         main_ui_->actionTelephonyLTEPlaceholder->setVisible(false);
2256     }
2257     if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_MTP3).length() > 0) {
2258         main_ui_->actionTelephonyMTP3Placeholder->setVisible(false);
2259     }
2260 }
2261
2262 void MainWindow::reloadDynamicMenus()
2263 {
2264     foreach (register_stat_group_t menu_group, menu_groups) {
2265         QList<QAction *>actions = wsApp->removedMenuGroupItems(menu_group);
2266         removeMenuActions(actions, menu_group);
2267
2268         actions = wsApp->addedMenuGroupItems(menu_group);
2269         addMenuActions(actions, menu_group);
2270     }
2271
2272     wsApp->clearAddedMenuGroupItems();
2273     wsApp->clearRemovedMenuGroupItems();
2274 }
2275
2276 void MainWindow::externalMenuHelper(ext_menu_t * menu, QMenu  * subMenu, gint depth)
2277 {
2278     QAction * itemAction = NULL;
2279     ext_menubar_t * item = NULL;
2280     GList * children = NULL;
2281
2282     /* There must exists an xpath parent */
2283     g_assert(subMenu != NULL);
2284
2285     /* If the depth counter exceeds, something must have gone wrong */
2286     g_assert(depth < EXT_MENUBAR_MAX_DEPTH);
2287
2288     children = menu->children;
2289     /* Iterate the child entries */
2290     while (children && children->data) {
2291         item = (ext_menubar_t *) children->data;
2292
2293         if (item->type == EXT_MENUBAR_MENU) {
2294             /* Handle Submenu entry */
2295             this->externalMenuHelper(item, subMenu->addMenu(item->label), depth++);
2296         } else if (item->type == EXT_MENUBAR_SEPARATOR) {
2297             subMenu->addSeparator();
2298         } else if (item->type == EXT_MENUBAR_ITEM || item->type == EXT_MENUBAR_URL) {
2299             itemAction = subMenu->addAction(item->name);
2300             itemAction->setData(QVariant::fromValue((void *)item));
2301             itemAction->setText(item->label);
2302             connect(itemAction, SIGNAL(triggered()),
2303                     this, SLOT(externalMenuItem_triggered()));
2304         }
2305
2306         /* Iterate Loop */
2307         children = g_list_next(children);
2308     }
2309 }
2310
2311 QMenu * MainWindow::searchSubMenu(QString objectName)
2312 {
2313     QList<QMenu*> lst;
2314
2315     if (objectName.length() > 0) {
2316         QString searchName = QString("menu") + objectName;
2317
2318         lst = main_ui_->menuBar->findChildren<QMenu*>();
2319         foreach (QMenu* m, lst) {
2320             if (QString::compare(m->objectName(), searchName) == 0)
2321                 return m;
2322         }
2323     }
2324
2325     return 0;
2326 }
2327
2328 void MainWindow::addExternalMenus()
2329 {
2330     QMenu * subMenu = NULL;
2331     GList * user_menu = NULL;
2332     ext_menu_t * menu = NULL;
2333
2334     user_menu = ext_menubar_get_entries();
2335
2336     while (user_menu && user_menu->data) {
2337         menu = (ext_menu_t *) user_menu->data;
2338
2339         /* On this level only menu items should exist. Not doing an assert here,
2340          * as it could be an honest mistake */
2341         if (menu->type != EXT_MENUBAR_MENU) {
2342             user_menu = g_list_next(user_menu);
2343             continue;
2344         }
2345
2346         /* Create main submenu and add it to the menubar */
2347         if (menu->parent_menu) {
2348             QMenu * sortUnderneath = searchSubMenu(QString(menu->parent_menu));
2349             if (sortUnderneath)
2350                 subMenu = sortUnderneath->addMenu(menu->label);
2351         }
2352
2353         if (!subMenu)
2354             subMenu = main_ui_->menuBar->addMenu(menu->label);
2355
2356         /* This will generate the action structure for each menu. It is recursive,
2357          * therefore a sub-routine, and we have a depth counter to prevent endless loops. */
2358         this->externalMenuHelper(menu, subMenu, 0);
2359
2360         /* Iterate Loop */
2361         user_menu = g_list_next (user_menu);
2362     }
2363 }
2364
2365 /*
2366  * Editor modelines
2367  *
2368  * Local Variables:
2369  * c-basic-offset: 4
2370  * tab-width: 8
2371  * indent-tabs-mode: nil
2372  * End:
2373  *
2374  * ex: set shiftwidth=4 tabstop=8 expandtab:
2375  * :indentSize=4:tabSize=8:noTabs=true:
2376  */