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