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