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