Rename "ws_version_info.h", also .c
[metze/wireshark/wip.git] / ui / qt / main_window.cpp
index 1fb7e436c8495aad1b8f17f65aa2cb97e5366d7e..eeab9fa92f71790c2824883cf67e38800983c3bd 100644 (file)
  */
 
 #include "main_window.h"
+
+/*
+ * The generated Ui_MainWindow::setupUi() can grow larger than our configured limit,
+ * so turn off -Wframe-larger-than= for ui_main_window.h.
+ */
+DIAG_OFF(frame-larger-than=)
 #include <ui_main_window.h>
+DIAG_ON(frame-larger-than=)
 
 #include <epan/addr_resolv.h>
 #include "epan/dissector_filters.h"
 #include <epan/epan_dissect.h>
 #include <wsutil/filesystem.h>
-#include <ws_version_info.h>
+#include <version_info.h>
 #include <epan/prefs.h>
 #include <epan/stats_tree_priv.h>
 #include <epan/plugin_if.h>
+#include <epan/export_object.h>
+
+#include "ui/iface_toolbar.h"
 
 #ifdef HAVE_LIBPCAP
 #include "ui/capture.h"
@@ -43,6 +53,7 @@
 #include "ui/capture_globals.h"
 #include "ui/main_statusbar.h"
 #include "ui/recent.h"
+#include "ui/recent_utils.h"
 #include "ui/util.h"
 #include "ui/preference_utils.h"
 
 #include "capture_interfaces_dialog.h"
 #endif
 #include "conversation_colorize_action.h"
-#include "display_filter_edit.h"
+#include "export_object_action.h"
+#include <ui/qt/widgets/display_filter_edit.h>
 #include "export_dissection_dialog.h"
 #include "file_set_dialog.h"
 #include "funnel_statistics.h"
 #include "import_text_dialog.h"
+#include "interface_toolbar.h"
 #include "packet_list.h"
+#include "wireless_timeline.h"
 #include "proto_tree.h"
 #include "simple_dialog.h"
-#include "stock_icon.h"
+#include <ui/qt/utils/stock_icon.h>
 #include "tap_parameter_dialog.h"
 #include "wireless_frame.h"
 #include "wireshark_application.h"
 
-#include "qt_ui_utils.h"
+#include <ui/qt/widgets/additional_toolbar.h>
+#include <ui/qt/utils/variant_pointer.h>
+
+#include <ui/qt/utils/qt_ui_utils.h>
+
+#include <ui/qt/widgets/drag_drop_toolbar.h>
 
 #include <QAction>
 #include <QActionGroup>
@@ -166,7 +185,7 @@ static void plugin_if_mainwindow_get_ws_info(gconstpointer user_data)
         g_free(ws_info->cf_filename);
         ws_info->cf_filename = g_strdup(cf->filename);
 
-        if (cf->state == FILE_READ_DONE) {
+        if (cf->state == FILE_READ_DONE && cf->current_frame) {
             ws_info->cf_framenr = cf->current_frame->num;
             ws_info->frame_passed_dfilter = (cf->current_frame->flags.passed_dfilter == 1);
         } else {
@@ -188,68 +207,36 @@ static void plugin_if_mainwindow_get_ws_info(gconstpointer user_data)
 
 #endif /* HAVE_LIBPCAP */
 
-gpointer
-simple_dialog(ESD_TYPE_E type, gint btn_mask, const gchar *msg_format, ...)
-{
-    va_list ap;
-
-    va_start(ap, msg_format);
-    SimpleDialog sd(gbl_cur_main_window_, type, btn_mask, msg_format, ap);
-    va_end(ap);
-
-    sd.exec();
-    return NULL;
-}
-
-/*
- * Alert box, with optional "don't show this message again" variable
- * and checkbox, and optional secondary text.
- */
-void
-simple_message_box(ESD_TYPE_E type, gboolean *notagain,
-                   const char *secondary_msg, const char *msg_format, ...)
+static void plugin_if_mainwindow_update_toolbars(gconstpointer user_data)
 {
-    if (notagain && *notagain) {
+    if (!gbl_cur_main_window_ || ! user_data)
         return;
-    }
-
-    va_list ap;
 
-    va_start(ap, msg_format);
-    SimpleDialog sd(gbl_cur_main_window_, type, ESD_BTN_OK, msg_format, ap);
-    va_end(ap);
-
-    sd.setDetailedText(secondary_msg);
+    GHashTable * data_set = (GHashTable *)user_data;
+    if (g_hash_table_lookup_extended(data_set, "toolbar_name", NULL, NULL)) {
+        QString toolbarName((const char *)g_hash_table_lookup(data_set, "toolbar_name"));
+        gbl_cur_main_window_->removeAdditionalToolbar(toolbarName);
 
-#if (QT_VERSION > QT_VERSION_CHECK(5, 2, 0))
-    QCheckBox *cb = NULL;
-    if (notagain) {
-        cb = new QCheckBox();
-        cb->setChecked(true);
-        cb->setText(QObject::tr("Don't show this message again."));
-        sd.setCheckBox(cb);
     }
-#endif
-
-    sd.exec();
+}
 
-#if (QT_VERSION > QT_VERSION_CHECK(5, 2, 0))
-    if (notagain && cb) {
-        *notagain = cb->isChecked();
+#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
+static void mainwindow_add_toolbar(const iface_toolbar *toolbar_entry)
+{
+    if (gbl_cur_main_window_ && toolbar_entry)
+    {
+        gbl_cur_main_window_->addInterfaceToolbar(toolbar_entry);
     }
-#endif
 }
 
-/*
- * Error alert box, taking a format and a va_list argument.
- */
-void
-vsimple_error_message_box(const char *msg_format, va_list ap)
+static void mainwindow_remove_toolbar(const gchar *menu_title)
 {
-    SimpleDialog sd(gbl_cur_main_window_, ESD_TYPE_ERROR, ESD_BTN_OK, msg_format, ap);
-    sd.exec();
+    if (gbl_cur_main_window_ && menu_title)
+    {
+        gbl_cur_main_window_->removeInterfaceToolbar(menu_title);
+    }
 }
-
+#endif
 
 QMenu* MainWindow::findOrAddMenu(QMenu *parent_menu, QString& menu_text) {
     QList<QAction *> actions = parent_menu->actions();
@@ -277,6 +264,7 @@ MainWindow::MainWindow(QWidget *parent) :
     time_precision_actions_(NULL),
     funnel_statistics_(NULL),
     freeze_focus_(NULL),
+    was_maximized_(false),
     capture_stopping_(false),
     capture_filter_valid_(false)
 #ifdef HAVE_LIBPCAP
@@ -307,6 +295,9 @@ MainWindow::MainWindow(QWidget *parent) :
     // iterates over *all* of our children, looking for matching "on_" slots.
     // The fewer children we have at this point the better.
     main_ui_->setupUi(this);
+#ifdef HAVE_SOFTWARE_UPDATE
+    update_action_ = new QAction(tr("Check for Updates" UTF8_HORIZONTAL_ELLIPSIS), main_ui_->menuHelp);
+#endif
     setWindowIcon(wsApp->normalIcon());
     setTitlebarForCaptureFile();
     setMenusForCaptureFile();
@@ -319,16 +310,24 @@ MainWindow::MainWindow(QWidget *parent) :
     main_ui_->actionAnalyzeReloadLuaPlugins->setVisible(false);
 #endif
 
+    qRegisterMetaType<FilterAction::Action>("FilterAction::Action");
+    qRegisterMetaType<FilterAction::ActionType>("FilterAction::ActionType");
+    connect(this, SIGNAL(filterAction(QString,FilterAction::Action,FilterAction::ActionType)),
+            this, SLOT(queuedFilterAction(QString,FilterAction::Action,FilterAction::ActionType)),
+            Qt::QueuedConnection);
+
     //To prevent users use features before initialization complete
     //Otherwise unexpected problems may occur
     setFeaturesEnabled(false);
     connect(wsApp, SIGNAL(appInitialized()), this, SLOT(setFeaturesEnabled()));
+    connect(wsApp, SIGNAL(appInitialized()), this, SLOT(applyGlobalCommandLineOptions()));
     connect(wsApp, SIGNAL(appInitialized()), this, SLOT(zoomText()));
     connect(wsApp, SIGNAL(appInitialized()), this, SLOT(initViewColorizeMenu()));
     connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addStatsPluginsToMenu()));
     connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addDynamicMenus()));
-    connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addExternalMenus()));
+    connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addPluginIFStructures()));
     connect(wsApp, SIGNAL(appInitialized()), this, SLOT(initConversationMenus()));
+    connect(wsApp, SIGNAL(appInitialized()), this, SLOT(initExportObjectsMenus()));
 
     connect(wsApp, SIGNAL(profileChanging()), this, SLOT(saveWindowGeometry()));
     connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(layoutPanes()));
@@ -337,8 +336,15 @@ MainWindow::MainWindow(QWidget *parent) :
     connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(zoomText()));
     connect(wsApp, SIGNAL(preferencesChanged()), this, SLOT(setTitlebarForCaptureFile()));
 
-    connect(wsApp, SIGNAL(updateRecentItemStatus(const QString &, qint64, bool)), this, SLOT(updateRecentFiles()));
-    updateRecentFiles();
+    connect(wsApp, SIGNAL(updateRecentCaptureStatus(const QString &, qint64, bool)), this, SLOT(updateRecentCaptures()));
+    updateRecentCaptures();
+
+#ifdef HAVE_SOFTWARE_UPDATE
+    connect(wsApp, SIGNAL(softwareUpdateRequested()), this, SLOT(softwareUpdateRequested()),
+        Qt::BlockingQueuedConnection);
+    connect(wsApp, SIGNAL(softwareUpdateClose()), this, SLOT(close()),
+        Qt::BlockingQueuedConnection);
+#endif
 
     df_combo_box_ = new DisplayFilterCombo();
     const DisplayFilterEdit *df_edit = dynamic_cast<DisplayFilterEdit *>(df_combo_box_->lineEdit());
@@ -360,6 +366,10 @@ MainWindow::MainWindow(QWidget *parent) :
             this, SLOT(openCaptureFile(QString,QString)));
     connect(this, SIGNAL(displayFilterSuccess(bool)), df_edit, SLOT(displayFilterSuccess(bool)));
 
+    file_set_dialog_ = new FileSetDialog(this);
+    connect(file_set_dialog_, SIGNAL(fileSetOpenCaptureFile(QString)),
+            this, SLOT(openCaptureFile(QString)));
+
     initMainToolbarIcons();
 
     main_ui_->displayFilterToolBar->insertWidget(main_ui_->actionDisplayFilterExpression, df_combo_box_);
@@ -367,8 +377,14 @@ MainWindow::MainWindow(QWidget *parent) :
     // Make sure filter expressions overflow into a menu instead of a
     // larger toolbar. We do this by adding them to a child toolbar.
     // https://bugreports.qt.io/browse/QTBUG-2472
-    filter_expression_toolbar_ = new QToolBar();
+    filter_expression_toolbar_ = new DragDropToolBar();
     filter_expression_toolbar_->setStyleSheet("QToolBar { background: none; border: none; }");
+    filter_expression_toolbar_->setContextMenuPolicy(Qt::CustomContextMenu);
+    connect(filter_expression_toolbar_, SIGNAL(customContextMenuRequested(QPoint)),
+            this, SLOT(filterToolbarCustomMenuHandler(QPoint)));
+    connect(filter_expression_toolbar_, SIGNAL(actionMoved(QAction*, int, int)),
+            this, SLOT(filterToolbarActionMoved(QAction*, int, int)));
+
     main_ui_->displayFilterToolBar->addWidget(filter_expression_toolbar_);
 
     wireless_frame_ = new WirelessFrame(this);
@@ -393,13 +409,26 @@ MainWindow::MainWindow(QWidget *parent) :
 
     main_ui_->addressEditorFrame->hide();
     main_ui_->columnEditorFrame->hide();
+    connect(main_ui_->columnEditorFrame, SIGNAL(pushFilterSyntaxStatus(const QString&)),
+            main_ui_->statusBar, SLOT(pushTemporaryStatus(const QString&)));
     main_ui_->preferenceEditorFrame->hide();
+    connect(main_ui_->preferenceEditorFrame, SIGNAL(pushFilterSyntaxStatus(const QString&)),
+            main_ui_->statusBar, SLOT(pushTemporaryStatus(const QString&)));
     main_ui_->filterExpressionFrame->hide();
+    connect(main_ui_->filterExpressionFrame, SIGNAL(pushFilterSyntaxStatus(const QString&)),
+            main_ui_->statusBar, SLOT(pushTemporaryStatus(const QString&)));
 
 #ifndef HAVE_LIBPCAP
     main_ui_->menuCapture->setEnabled(false);
 #endif
 
+    // Set OS specific shortcuts for fullscreen mode
+#if defined(Q_OS_MAC) && QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
+    main_ui_->actionViewFullScreen->setShortcut(QKeySequence::FullScreen);
+#else
+    main_ui_->actionViewFullScreen->setShortcut(QKeySequence(Qt::Key_F11));
+#endif
+
 #if defined(Q_OS_MAC)
 #if defined(QT_MACEXTRAS_LIB) && QT_VERSION < QT_VERSION_CHECK(5, 2, 1)
     QMacNativeToolBar *ntb = QtMacExtras::setNativeToolBar(main_ui_->mainToolBar);
@@ -417,9 +446,8 @@ MainWindow::MainWindow(QWidget *parent) :
 
 #ifdef HAVE_SOFTWARE_UPDATE
     QAction *update_sep = main_ui_->menuHelp->insertSeparator(main_ui_->actionHelpAbout);
-    QAction *update_action = new QAction(tr("Check for Updates" UTF8_HORIZONTAL_ELLIPSIS), main_ui_->menuHelp);
-    main_ui_->menuHelp->insertAction(update_sep, update_action);
-    connect(update_action, SIGNAL(triggered()), this, SLOT(checkForUpdates()));
+    main_ui_->menuHelp->insertAction(update_sep, update_action_);
+    connect(update_action_, SIGNAL(triggered()), this, SLOT(checkForUpdates()));
 #endif
     master_split_.setObjectName("splitterMaster");
     extra_split_.setObjectName("splitterExtra");
@@ -428,6 +456,13 @@ MainWindow::MainWindow(QWidget *parent) :
     empty_pane_.setObjectName("emptyPane");
 
     packet_list_ = new PacketList(&master_split_);
+    main_ui_->wirelessTimelineWidget->setPacketList(packet_list_);
+    connect(packet_list_, SIGNAL(packetSelectionChanged()),
+            main_ui_->wirelessTimelineWidget, SLOT(packetSelectionChanged()));
+    connect(packet_list_->packetListModel(), SIGNAL(bgColorizationProgress(int,int)),
+            main_ui_->wirelessTimelineWidget, SLOT(bgColorizationProgress(int,int)));
+    connect(packet_list_, SIGNAL(packetSelectionChanged()),
+            main_ui_->statusBar, SLOT(packetSelectionChanged()));
 
     proto_tree_ = new ProtoTree(&master_split_);
     proto_tree_->installEventFilter(this);
@@ -452,7 +487,8 @@ MainWindow::MainWindow(QWidget *parent) :
     updateRecentActions();
     setForCaptureInProgress(false);
 
-    setTabOrder(df_combo_box_, packet_list_);
+    setTabOrder(df_combo_box_->lineEdit(), packet_list_);
+    setTabOrder(packet_list_, proto_tree_);
 
     connect(&capture_file_, SIGNAL(captureCapturePrepared(capture_session *)),
             this, SLOT(captureCapturePrepared(capture_session *)));
@@ -500,6 +536,10 @@ MainWindow::MainWindow(QWidget *parent) :
             this, SLOT(captureFileRetapStarted()));
     connect(&capture_file_, SIGNAL(captureFileRetapFinished()),
             this, SLOT(captureFileRetapFinished()));
+    connect(&capture_file_, SIGNAL(captureFileMergeStarted()),
+            this, SLOT(captureFileMergeStarted()));
+    connect(&capture_file_, SIGNAL(captureFileMergeFinished()),
+            this, SLOT(captureFileMergeFinished()));
     connect(&capture_file_, SIGNAL(captureFileFlushTapsData()),
             this, SLOT(captureFileFlushTapsData()));
     connect(&capture_file_, SIGNAL(captureFileClosing()),
@@ -525,9 +565,9 @@ MainWindow::MainWindow(QWidget *parent) :
             packet_list_, SLOT(columnsChanged()));
     connect(wsApp, SIGNAL(preferencesChanged()),
             packet_list_, SLOT(preferencesChanged()));
-    connect(wsApp, SIGNAL(recentFilesRead()),
+    connect(wsApp, SIGNAL(recentPreferencesRead()),
             this, SLOT(applyRecentPaneGeometry()));
-    connect(wsApp, SIGNAL(recentFilesRead()),
+    connect(wsApp, SIGNAL(recentPreferencesRead()),
             this, SLOT(updateRecentActions()));
     connect(wsApp, SIGNAL(packetDissectionChanged()),
             this, SLOT(redissectPackets()), Qt::QueuedConnection);
@@ -591,6 +631,10 @@ MainWindow::MainWindow(QWidget *parent) :
             packet_list_, SLOT(goFirstPacket()));
     connect(main_ui_->actionGoLastPacket, SIGNAL(triggered()),
             packet_list_, SLOT(goLastPacket()));
+    connect(main_ui_->actionGoNextHistoryPacket, SIGNAL(triggered()),
+            packet_list_, SLOT(goNextHistoryPacket()));
+    connect(main_ui_->actionGoPreviousHistoryPacket, SIGNAL(triggered()),
+            packet_list_, SLOT(goPreviousHistoryPacket()));
 
     connect(main_ui_->actionViewExpandSubtrees, SIGNAL(triggered()),
             proto_tree_, SLOT(expandSubtrees()));
@@ -640,6 +684,8 @@ MainWindow::MainWindow(QWidget *parent) :
 
     connect(byte_view_tab_, SIGNAL(byteFieldHovered(const QString&)),
             main_ui_->statusBar, SLOT(pushByteStatus(const QString&)));
+    connect(byte_view_tab_, SIGNAL(currentChanged(int)),
+            this, SLOT(byteViewTabChanged(int)));
 
     connect(main_ui_->statusBar, SIGNAL(showExpertInfo()),
             this, SLOT(on_actionAnalyzeExpertInfo_triggered()));
@@ -674,12 +720,32 @@ MainWindow::MainWindow(QWidget *parent) :
 #ifdef HAVE_LIBPCAP
     plugin_if_register_gui_cb(PLUGIN_IF_GET_WS_INFO, plugin_if_mainwindow_get_ws_info);
 #endif
+    plugin_if_register_gui_cb(PLUGIN_IF_REMOVE_TOOLBAR, plugin_if_mainwindow_update_toolbars);
+
+#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
+    // Register Interface Toolbar callbacks
+    //
+    // Qt version must be 5.2 or higher because the use of
+    // QThread::requestInterruption() in interface_toolbar.cpp and
+    // QThread::isInterruptionRequested() in interface_toolbar_reader.cpp
+    iface_toolbar_register_cb(mainwindow_add_toolbar, mainwindow_remove_toolbar);
+#endif
 
     main_ui_->mainStack->setCurrentWidget(main_welcome_);
 }
 
 MainWindow::~MainWindow()
 {
+    disconnect(main_ui_->mainStack, 0, 0, 0);
+
+#ifndef Q_OS_MAC
+    // file_set_dialog_ is a subclass of GeometryStateDialog.
+    // For reasons described in geometry_state_dialog.h no parent is set when
+    // instantiating the dialog and as a result the object is not automatically
+    // freed by its parent. Free it here explicitly to avoid leak and numerous
+    // Valgrind complaints.
+    delete file_set_dialog_;
+#endif
     delete main_ui_;
 }
 
@@ -694,7 +760,23 @@ QMenu *MainWindow::createPopupMenu()
     menu->addAction(main_ui_->actionViewMainToolbar);
     menu->addAction(main_ui_->actionViewFilterToolbar);
     menu->addAction(main_ui_->actionViewWirelessToolbar);
+
+    if (!main_ui_->menuInterfaceToolbars->actions().isEmpty()) {
+        QMenu *submenu = menu->addMenu(main_ui_->menuInterfaceToolbars->title());
+        foreach (QAction *action, main_ui_->menuInterfaceToolbars->actions()) {
+            submenu->addAction(action);
+        }
+    }
+
+    if (!main_ui_->menuAdditionalToolbars->actions().isEmpty()) {
+        QMenu *subMenu = menu->addMenu(main_ui_->menuAdditionalToolbars->title());
+        foreach (QAction *action, main_ui_->menuAdditionalToolbars->actions()) {
+            subMenu->addAction(action);
+        }
+    }
+
     menu->addAction(main_ui_->actionViewStatusBar);
+
     menu->addSeparator();
     menu->addAction(main_ui_->actionViewPacketList);
     menu->addAction(main_ui_->actionViewPacketDetails);
@@ -702,6 +784,78 @@ QMenu *MainWindow::createPopupMenu()
     return menu;
 }
 
+void MainWindow::addInterfaceToolbar(const iface_toolbar *toolbar_entry)
+{
+    QMenu *menu = main_ui_->menuInterfaceToolbars;
+    bool visible = g_list_find_custom(recent.interface_toolbars, toolbar_entry->menu_title, (GCompareFunc) strcmp) ? true : false;
+
+    QString title = QString().fromUtf8(toolbar_entry->menu_title);
+    QAction *action = new QAction(title, menu);
+    action->setEnabled(true);
+    action->setCheckable(true);
+    action->setChecked(visible);
+    action->setToolTip(tr("Show or hide the toolbar"));
+
+    QAction *before = NULL;
+    foreach (QAction *action, menu->actions()) {
+        // Ensure we add the menu entries in sorted order
+        if (action->text().compare(title, Qt::CaseInsensitive) > 0) {
+            before = action;
+            break;
+        }
+    }
+    menu->insertAction(before, action);
+
+    InterfaceToolbar *interface_toolbar = new InterfaceToolbar(this, toolbar_entry);
+    connect(wsApp, SIGNAL(appInitialized()), interface_toolbar, SLOT(interfaceListChanged()));
+    connect(wsApp, SIGNAL(localInterfaceListChanged()), interface_toolbar, SLOT(interfaceListChanged()));
+
+    QToolBar *toolbar = new QToolBar(this);
+    toolbar->addWidget(interface_toolbar);
+    toolbar->setMovable(false);
+    toolbar->setVisible(visible);
+
+    action->setData(qVariantFromValue(toolbar));
+
+    addToolBar(Qt::TopToolBarArea, toolbar);
+    insertToolBarBreak(toolbar);
+
+    if (show_hide_actions_) {
+        show_hide_actions_->addAction(action);
+    }
+
+    menu->menuAction()->setVisible(true);
+}
+
+void MainWindow::removeInterfaceToolbar(const gchar *menu_title)
+{
+    QMenu *menu = main_ui_->menuInterfaceToolbars;
+    QAction *action = NULL;
+    QMap<QAction *, QWidget *>::iterator i;
+
+    QString title = QString().fromUtf8(menu_title);
+    foreach (action, menu->actions()) {
+        if (title.compare(action->text()) == 0) {
+            break;
+        }
+    }
+
+    if (action) {
+        if (show_hide_actions_) {
+            show_hide_actions_->removeAction(action);
+        }
+        menu->removeAction(action);
+
+        QToolBar *toolbar = action->data().value<QToolBar *>();
+        removeToolBar(toolbar);
+
+        delete action;
+        delete toolbar;
+    }
+
+    menu->menuAction()->setVisible(!menu->actions().isEmpty());
+}
+
 void MainWindow::setPipeInputHandler(gint source, gpointer user_data, ws_process_id *child_process, pipe_input_cb_t input_cb)
 {
     pipe_source_        = source;
@@ -746,7 +900,8 @@ bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
     // proto tree, and main welcome widgets.
     if (event->type() == QEvent::KeyPress) {
         QKeyEvent *kevt = static_cast<QKeyEvent *>(event);
-        if (kevt->text().length() > 0 && kevt->text()[0].isPrint()) {
+        if (kevt->text().length() > 0 && kevt->text()[0].isPrint() &&
+            !(kevt->modifiers() & Qt::ControlModifier)) {
             df_combo_box_->lineEdit()->insert(kevt->text());
             df_combo_box_->lineEdit()->setFocus();
             return true;
@@ -818,35 +973,89 @@ void MainWindow::closeEvent(QCloseEvent *event) {
         exit(0);
     }
     wsApp->quit();
+    // When the main loop is not yet running (i.e. when openCaptureFile is
+    // executing in wireshark-qt.cpp), the above quit action has no effect.
+    // Schedule a quit action for the next execution of the main loop.
+    QMetaObject::invokeMethod(wsApp, "quit", Qt::QueuedConnection);
 }
 
+// XXX On windows the drag description is "Copy". It should be "Open" or
+// "Merge" as appropriate. It looks like we need access to IDataObject in
+// order to set DROPDESCRIPTION.
 void MainWindow::dragEnterEvent(QDragEnterEvent *event)
 {
-    bool accept = false;
+    if (!main_ui_->actionFileOpen->isEnabled()) {
+        // We could alternatively call setAcceptDrops(!capture_in_progress)
+        // in setMenusForCaptureInProgress but that wouldn't provide feedback.
+
+        main_ui_->statusBar->pushTemporaryStatus(tr("Unable to drop files during capture."));
+        event->setDropAction(Qt::IgnoreAction);
+        event->ignore();
+        return;
+    }
+
+    bool have_files = false;
     foreach (QUrl drag_url, event->mimeData()->urls()) {
         if (!drag_url.toLocalFile().isEmpty()) {
-            accept = true;
+            have_files = true;
             break;
         }
     }
-    if (accept) event->acceptProposedAction();
+
+    if (have_files) {
+        event->acceptProposedAction();
+    }
 }
 
 void MainWindow::dropEvent(QDropEvent *event)
 {
+    QList<QByteArray> local_files;
+
     foreach (QUrl drop_url, event->mimeData()->urls()) {
-        QString local_file = drop_url.toLocalFile();
-        if (!local_file.isEmpty()) {
-            event->acceptProposedAction();
-            openCaptureFile(local_file);
-            break;
+        QString drop_file = drop_url.toLocalFile();
+        if (!drop_file.isEmpty()) {
+            local_files << drop_file.toUtf8();
         }
     }
+
+    if (local_files.size() < 1) {
+        return;
+    }
+    event->acceptProposedAction();
+
+
+    if (local_files.size() == 1) {
+        openCaptureFile(local_files.at(0));
+        return;
+    }
+
+    char **in_filenames = (char **)g_malloc(sizeof(char*) * local_files.size());
+    char *tmpname = NULL;
+
+    for (int i = 0; i < local_files.size(); i++) {
+        in_filenames[i] = (char *) local_files.at(i).constData();
+    }
+
+    /* merge the files in chronological order */
+    if (cf_merge_files_to_tempfile(this, &tmpname, local_files.size(),
+                                   in_filenames, WTAP_FILE_TYPE_SUBTYPE_PCAPNG,
+                                   FALSE) == CF_OK) {
+        /* Merge succeeded; close the currently-open file and try
+           to open the merged capture file. */
+        openCaptureFile(tmpname, QString(), WTAP_TYPE_AUTO, TRUE);
+    }
+
+    g_free(tmpname);
+    g_free(in_filenames);
+
 }
 
 // Apply recent settings to the main window geometry.
 // We haven't loaded the preferences at this point so we assume that the
 // position and size preference are enabled.
+// Note we might end up with unexpected screen geometries if the user
+// unplugs or plugs in a monitor:
+// https://bugreports.qt.io/browse/QTBUG-44213
 void MainWindow::loadWindowGeometry()
 {
     int min_sensible_dimension = 200;
@@ -860,7 +1069,13 @@ void MainWindow::loadWindowGeometry()
         QRect recent_geom(recent.gui_geometry_main_x, recent.gui_geometry_main_y,
                           recent.gui_geometry_main_width, recent.gui_geometry_main_height);
         if (!rect_on_screen(recent_geom)) {
-            // We're not visible on any screens. Give up and use the default geometry.
+            // We're not visible on any screens. See if we can move onscreen
+            // without resizing.
+            recent_geom.moveTo(50, 50); // recent.c defaults to 20.
+        }
+
+        if (!rect_on_screen(recent_geom)) {
+            // Give up and use the default geometry.
             return;
         }
 
@@ -889,7 +1104,7 @@ void MainWindow::saveWindowGeometry()
     }
 
     if (prefs.gui_geometry_save_maximized) {
-        // On OS X this is false when it shouldn't be
+        // On macOS this is false when it shouldn't be
         recent.gui_geometry_main_maximized = isMaximized();
     }
 
@@ -1015,9 +1230,9 @@ void MainWindow::mergeCaptureFile()
             gchar *err_msg;
 
             if (!dfilter_compile(read_filter.toUtf8().constData(), &rfcode, &err_msg)) {
-                /* Not valid.  Tell the user, and go back and run the file
+                /* Not valid. Tell the user, and go back and run the file
                    selection box again once they dismiss the alert. */
-                //bad_dfilter_alert_box(top_level, read_filter->str);
+                // Similar to commandline_info.jfilter section in main().
                 QMessageBox::warning(this, tr("Invalid Read Filter"),
                                      QString(tr("The filter expression %1 isn't a valid read filter. (%2).").arg(read_filter, err_msg)),
                                      QMessageBox::Ok);
@@ -1031,22 +1246,21 @@ void MainWindow::mergeCaptureFile()
         file_type = capture_file_.capFile()->cd_t;
 
         /* Try to merge or append the two files */
-        tmpname = NULL;
         if (merge_dlg.mergeType() == 0) {
             /* chronological order */
             in_filenames[0] = g_strdup(capture_file_.capFile()->filename);
             in_filenames[1] = qstring_strdup(file_name);
-            merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, FALSE);
+            merge_status = cf_merge_files_to_tempfile(this, &tmpname, 2, in_filenames, file_type, FALSE);
         } else if (merge_dlg.mergeType() <= 0) {
             /* prepend file */
             in_filenames[0] = qstring_strdup(file_name);
             in_filenames[1] = g_strdup(capture_file_.capFile()->filename);
-            merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
+            merge_status = cf_merge_files_to_tempfile(this, &tmpname, 2, in_filenames, file_type, TRUE);
         } else {
             /* append file */
             in_filenames[0] = g_strdup(capture_file_.capFile()->filename);
             in_filenames[1] = qstring_strdup(file_name);
-            merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, TRUE);
+            merge_status = cf_merge_files_to_tempfile(this, &tmpname, 2, in_filenames, file_type, TRUE);
         }
 
         g_free(in_filenames[0]);
@@ -1123,7 +1337,7 @@ void MainWindow::importCaptureFile() {
     openCaptureFile(import_dlg.capfileName());
 }
 
-void MainWindow::saveCaptureFile(capture_file *cf, bool dont_reopen) {
+bool MainWindow::saveCaptureFile(capture_file *cf, bool dont_reopen) {
     QString file_name;
     gboolean discard_comments;
 
@@ -1135,7 +1349,7 @@ void MainWindow::saveCaptureFile(capture_file *cf, bool dont_reopen) {
            probably pcap-ng, which supports comments and, if it's
            not pcap-ng, let the user decide what they want to do
            if they've added comments. */
-        saveAsCaptureFile(cf, FALSE, dont_reopen);
+        return saveAsCaptureFile(cf, FALSE, dont_reopen);
     } else {
         if (cf->unsaved_changes) {
             cf_write_status_t status;
@@ -1170,18 +1384,17 @@ void MainWindow::saveCaptureFile(capture_file *cf, bool dont_reopen) {
                    support comments, and the user said not to delete the
                    comments.  Do a "Save As" so the user can select
                    one of those formats and choose a file name. */
-                saveAsCaptureFile(cf, TRUE, dont_reopen);
-                return;
+                return saveAsCaptureFile(cf, TRUE, dont_reopen);
 
             case CANCELLED:
                 /* The user said "forget it".  Just return. */
-                return;
+                return false;
 
             default:
                 /* Squelch warnings that discard_comments is being used
                    uninitialized. */
                 g_assert_not_reached();
-                return;
+                return false;
             }
 
             /* XXX - cf->filename might get freed out from under us, because
@@ -1213,14 +1426,16 @@ void MainWindow::saveCaptureFile(capture_file *cf, bool dont_reopen) {
 
             case CF_WRITE_ABORTED:
                 /* The write was aborted; just drive on. */
-                break;
+                return false;
             }
         }
         /* Otherwise just do nothing. */
     }
+
+    return true;
 }
 
-void MainWindow::saveAsCaptureFile(capture_file *cf, bool must_support_comments, bool dont_reopen) {
+bool MainWindow::saveAsCaptureFile(capture_file *cf, bool must_support_comments, bool dont_reopen) {
     QString file_name = "";
     int file_type;
     gboolean compressed;
@@ -1229,7 +1444,7 @@ void MainWindow::saveAsCaptureFile(capture_file *cf, bool must_support_comments,
     gboolean discard_comments = FALSE;
 
     if (!cf) {
-        return;
+        return false;
     }
 
     for (;;) {
@@ -1267,7 +1482,7 @@ void MainWindow::saveAsCaptureFile(capture_file *cf, bool must_support_comments,
         case CANCELLED:
             /* The user said "forget it".  Just get rid of the dialog box
                and return. */
-            return;
+            return false;
         }
         file_type = save_as_dlg.selectedFileType();
         compressed = save_as_dlg.isCompressed();
@@ -1301,7 +1516,9 @@ void MainWindow::saveAsCaptureFile(capture_file *cf, bool must_support_comments,
 
             cf->unsaved_changes = false; //we just saved so we signal that we have no unsaved changes
             updateForUnsavedChanges(); // we update the title bar to remove the *
-            return;
+            /* Add this filename to the list of recent files in the "Recent Files" submenu */
+            add_menu_recent_capture_file(file_name.toUtf8().constData());
+            return true;
 
         case CF_WRITE_ERROR:
             /* The save failed; let the user try again. */
@@ -1309,10 +1526,10 @@ void MainWindow::saveAsCaptureFile(capture_file *cf, bool must_support_comments,
 
         case CF_WRITE_ABORTED:
             /* The user aborted the save; just return. */
-            return;
+            return false;
         }
     }
-    return;
+    return true;
 }
 
 void MainWindow::exportSelectedPackets() {
@@ -1418,6 +1635,8 @@ void MainWindow::exportSelectedPackets() {
                any packets that no longer have comments. */
             if (discard_comments)
                 packet_list_queue_draw();
+            /* Add this filename to the list of recent files in the "Recent Files" submenu */
+            add_menu_recent_capture_file(file_name.toUtf8().constData());
             return;
 
         case CF_WRITE_ERROR:
@@ -1449,7 +1668,6 @@ void MainWindow::exportDissections(export_type_e export_type) {
 
 void MainWindow::fileAddExtension(QString &file_name, int file_type, bool compressed) {
     QString file_name_lower;
-    QString file_suffix;
     GSList  *extensions_list;
     gboolean add_extension;
 
@@ -1470,7 +1688,7 @@ void MainWindow::fileAddExtension(QString &file_name, int file_type, bool compre
         /* OK, see if the file has one of those extensions. */
         for (extension = extensions_list; extension != NULL;
              extension = g_slist_next(extension)) {
-            file_suffix += tr(".") + (char *)extension->data;
+            QString file_suffix = tr(".") + (char *)extension->data;
             if (file_name_lower.endsWith(file_suffix)) {
                 /*
                  * The file name has one of the extensions for
@@ -1512,19 +1730,17 @@ bool MainWindow::testCaptureFileClose(QString before_what, FileCloseContext cont
 
 #ifdef HAVE_LIBPCAP
     if (capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
-        /* This is true if we're reading a capture file *or* if we're doing
-         a live capture.  If we're reading a capture file, the main loop
-         is busy reading packets, and only accepting input from the
-         progress dialog, so we can't get here, so this means we're
-         doing a capture. */
-        capture_in_progress = true;
+        /*
+         * This (FILE_READ_IN_PROGRESS) is true if we're reading a capture file
+         * *or* if we're doing a live capture. From the capture file itself we
+         * cannot differentiate the cases, so check the current capture session.
+         */
+        capture_in_progress = captureSession()->state != CAPTURE_STOPPED;
     }
 #endif
 
     if (prefs.gui_ask_unsaved) {
-        if (cf_has_unsaved_data(capture_file_.capFile()) ||
-            (capture_in_progress && capture_file_.capFile()->count > 0))
-        {
+        if (cf_has_unsaved_data(capture_file_.capFile())) {
             QMessageBox msg_dialog;
             QString question;
             QString infotext;
@@ -1605,6 +1821,9 @@ bool MainWindow::testCaptureFileClose(QString before_what, FileCloseContext cont
             }
             discard_button = msg_dialog.addButton(discard_button_text, QMessageBox::DestructiveRole);
 
+            discard_button->setAutoDefault(false);
+            discard_button->setFocus();
+
             msg_dialog.exec();
             /* According to the Qt doc:
              * when using QMessageBox with custom buttons, exec() function returns an opaque value.
@@ -1619,7 +1838,10 @@ bool MainWindow::testCaptureFileClose(QString before_what, FileCloseContext cont
                     captureStop();
 #endif
                 /* Save the file and close it */
-                saveCaptureFile(capture_file_.capFile(), true);
+                // XXX if no packets were captured, any unsaved comments set by
+                // the user are silently discarded because capFile() is null.
+                if (capture_file_.capFile() && saveCaptureFile(capture_file_.capFile(), true) == false)
+                    return false;
                 do_close_file = true;
             } else if(msg_dialog.clickedButton() == discard_button) {
                 /* Just close the file, discarding changes */
@@ -1638,12 +1860,27 @@ bool MainWindow::testCaptureFileClose(QString before_what, FileCloseContext cont
         do_close_file = true;
     }
 
+    /*
+     * Are we done with this file and should we close the file?
+     */
     if (do_close_file) {
 #ifdef HAVE_LIBPCAP
         /* If there's a capture in progress, we have to stop the capture
            and then do the close. */
         if (capture_in_progress)
             captureStop();
+        else if (capture_file_.capFile() && capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
+            /*
+             * When an offline capture is being read, mark it as aborted.
+             * cf_read will be responsible for actually closing the capture.
+             *
+             * We cannot just invoke cf_close here since cf_read is up in the
+             * call chain. (update_progress_dlg can end up processing the Quit
+             * event from the user which then ends up here.)
+             */
+            capture_file_.capFile()->state = FILE_READ_ABORTED;
+            return true;
+        }
 #endif
         /* captureStop() will close the file if not having any packets */
         if (capture_file_.capFile() && context != Restart && context != Reload)
@@ -1668,7 +1905,7 @@ void MainWindow::initMainToolbarIcons()
     int icon_size = style()->pixelMetric(QStyle::PM_SmallIconSize);
 #if !defined(Q_OS_WIN)
     // Force icons to 24x24 for now, otherwise actionFileOpen looks wonky.
-    // The OS X HIG specifies 32-pixel icons but they're a little too
+    // The macOS HIG specifies 32-pixel icons but they're a little too
     // large IMHO.
     icon_size = icon_size * 3 / 2;
 #endif
@@ -1700,10 +1937,11 @@ void MainWindow::initMainToolbarIcons()
     main_ui_->actionGoPreviousConversationPacket->setShortcut(QKeySequence(Qt::META | Qt::Key_Comma));
     main_ui_->actionGoNextConversationPacket->setShortcut(QKeySequence(Qt::META | Qt::Key_Period));
 #endif
+    main_ui_->actionGoPreviousHistoryPacket->setIcon(StockIcon("go-previous"));
+    main_ui_->actionGoNextHistoryPacket->setIcon(StockIcon("go-next"));
     main_ui_->actionGoAutoScroll->setIcon(StockIcon("x-stay-last"));
 
     main_ui_->actionViewColorizePacketList->setIcon(StockIcon("x-colorize-packets"));
-//    main_ui_->actionViewAutoScroll->setIcon(StockIcon("x-stay-last"));
 
     QList<QKeySequence> zi_seq = main_ui_->actionViewZoomIn->shortcuts();
     zi_seq << QKeySequence(Qt::CTRL + Qt::Key_Equal);
@@ -1738,11 +1976,15 @@ void MainWindow::initShowHideMainWidgets()
         showHideMainWidgets(shmwa);
     }
 
+    // Initial hide the Interface Toolbar submenu
+    main_ui_->menuInterfaceToolbars->menuAction()->setVisible(false);
+
+    /* Initially hide the additional toolbars menus */
+    main_ui_->menuAdditionalToolbars->menuAction()->setVisible(false);
+
     connect(show_hide_actions_, SIGNAL(triggered(QAction*)), this, SLOT(showHideMainWidgets(QAction*)));
 }
 
-Q_DECLARE_METATYPE(ts_type)
-
 void MainWindow::initTimeDisplayFormatMenu()
 {
     if (time_display_actions_) {
@@ -1770,8 +2012,6 @@ void MainWindow::initTimeDisplayFormatMenu()
     connect(time_display_actions_, SIGNAL(triggered(QAction*)), this, SLOT(setTimestampFormat(QAction*)));
 }
 
-Q_DECLARE_METATYPE(ts_precision)
-
 void MainWindow::initTimePrecisionFormatMenu()
 {
     if (time_precision_actions_) {
@@ -1850,14 +2090,14 @@ void MainWindow::initConversationMenus()
             conv_action->setColorNumber(i++);
             submenu->addAction(conv_action);
             connect(this, SIGNAL(packetInfoChanged(_packet_info*)), conv_action, SLOT(setPacketInfo(_packet_info*)));
-            connect(conv_action, SIGNAL(triggered()), this, SLOT(colorizeWithFilter()));
+            connect(conv_action, SIGNAL(triggered()), this, SLOT(colorizeActionTriggered()));
         }
 
         conv_action = new ConversationAction(submenu, conv_filter);
         conv_action->setText(main_ui_->actionViewColorizeNewColoringRule->text());
         submenu->addAction(conv_action);
         connect(this, SIGNAL(packetInfoChanged(_packet_info*)), conv_action, SLOT(setPacketInfo(_packet_info*)));
-        connect(conv_action, SIGNAL(triggered()), this, SLOT(colorizeWithFilter()));
+        connect(conv_action, SIGNAL(triggered()), this, SLOT(colorizeActionTriggered()));
 
         // Proto tree conversation menu is filled in in ProtoTree::contextMenuEvent.
         // We should probably do that here.
@@ -1873,14 +2113,36 @@ void MainWindow::initConversationMenus()
         colorize_action->setColorNumber(i++);
         proto_tree_->colorizeMenu()->addAction(colorize_action);
         connect(this, SIGNAL(fieldFilterChanged(QByteArray)), colorize_action, SLOT(setFieldFilter(QByteArray)));
-        connect(colorize_action, SIGNAL(triggered()), this, SLOT(colorizeWithFilter()));
+        connect(colorize_action, SIGNAL(triggered()), this, SLOT(colorizeActionTriggered()));
     }
 
     colorize_action = new ColorizeAction(proto_tree_->colorizeMenu());
     colorize_action->setText(main_ui_->actionViewColorizeNewColoringRule->text());
     proto_tree_->colorizeMenu()->addAction(colorize_action);
     connect(this, SIGNAL(fieldFilterChanged(QByteArray)), colorize_action, SLOT(setFieldFilter(QByteArray)));
-    connect(colorize_action, SIGNAL(triggered()), this, SLOT(colorizeWithFilter()));
+    connect(colorize_action, SIGNAL(triggered()), this, SLOT(colorizeActionTriggered()));
+}
+
+gboolean MainWindow::addExportObjectsMenuItem(const void *, void *value, void *userdata)
+{
+    register_eo_t *eo = (register_eo_t*)value;
+    MainWindow *window = (MainWindow*)userdata;
+
+    ExportObjectAction *export_action = new ExportObjectAction(window->main_ui_->menuFileExportObjects, eo);
+    window->main_ui_->menuFileExportObjects->addAction(export_action);
+
+    //initially disable until a file is loaded (then file signals will take over)
+    export_action->setEnabled(false);
+
+    connect(&window->capture_file_, SIGNAL(captureFileOpened()), export_action, SLOT(captureFileOpened()));
+    connect(&window->capture_file_, SIGNAL(captureFileClosed()), export_action, SLOT(captureFileClosed()));
+    connect(export_action, SIGNAL(triggered()), window, SLOT(applyExportObject()));
+    return FALSE;
+}
+
+void MainWindow::initExportObjectsMenus()
+{
+    eo_iterate_tables(addExportObjectsMenuItem, this);
 }
 
 // Titlebar
@@ -1901,7 +2163,7 @@ void MainWindow::setTitlebarForCaptureFile()
         } else {
             //
             // For a user file, set the full path; that way,
-            // for OS X, it'll set the "proxy icon".  Qt
+            // for macOS, it'll set the "proxy icon".  Qt
             // handles extracting the last component.
             //
             // Sadly, some UN*Xes don't necessarily use UTF-8
@@ -1953,7 +2215,7 @@ void MainWindow::setWSWindowTitle(QString title)
     if (prefs.gui_window_title && prefs.gui_window_title[0]) {
         QString custom_title = replaceWindowTitleVariables(prefs.gui_window_title);
 #ifdef __APPLE__
-        // On OS X we separate the titles with a unicode em dash
+        // On macOS we separate the titles with a unicode em dash
         title.append(QString(" %1 %2").arg(UTF8_EM_DASH).arg(custom_title));
 #else
         title.append(QString(" [%1]").arg(custom_title));
@@ -1984,6 +2246,7 @@ void MainWindow::setMenusForCaptureFile(bool force_disable)
     bool enable = true;
     bool can_write = false;
     bool can_save = false;
+    bool can_save_as = false;
 
     if (force_disable || capture_file_.capFile() == NULL || capture_file_.capFile()->state == FILE_READ_IN_PROGRESS) {
         /* We have no capture file or we're currently reading a file */
@@ -1992,13 +2255,14 @@ void MainWindow::setMenusForCaptureFile(bool force_disable)
         /* We have a capture file. Can we write or save? */
         can_write = cf_can_write_with_wiretap(capture_file_.capFile());
         can_save = cf_can_save(capture_file_.capFile());
+        can_save_as = cf_can_save_as(capture_file_.capFile());
     }
 
     main_ui_->actionViewReload_as_File_Format_or_Capture->setEnabled(enable);
     main_ui_->actionFileMerge->setEnabled(can_write);
     main_ui_->actionFileClose->setEnabled(enable);
     main_ui_->actionFileSave->setEnabled(can_save);
-    main_ui_->actionFileSaveAs->setEnabled(can_save);
+    main_ui_->actionFileSaveAs->setEnabled(can_save_as);
     main_ui_->actionStatisticsCaptureFileProperties->setEnabled(enable);
     /*
      * "Export Specified Packets..." should be available only if
@@ -2011,6 +2275,7 @@ void MainWindow::setMenusForCaptureFile(bool force_disable)
     main_ui_->actionFileExportAsPDML->setEnabled(enable);
     main_ui_->actionFileExportAsPlainText->setEnabled(enable);
     main_ui_->actionFileExportAsPSML->setEnabled(enable);
+    main_ui_->actionFileExportAsJSON->setEnabled(enable);
 
     main_ui_->actionFileExportPacketBytes->setEnabled(enable);
     main_ui_->actionFileExportPDU->setEnabled(enable);
@@ -2021,6 +2286,11 @@ void MainWindow::setMenusForCaptureFile(bool force_disable)
     }
 
     main_ui_->actionViewReload->setEnabled(enable);
+
+#ifdef HAVE_SOFTWARE_UPDATE
+    // We might want to enable or disable automatic checks here as well.
+    update_action_->setEnabled(!can_save);
+#endif
 }
 
 void MainWindow::setMenusForCaptureInProgress(bool capture_in_progress) {
@@ -2035,9 +2305,10 @@ void MainWindow::setMenusForCaptureInProgress(bool capture_in_progress) {
     main_ui_->actionFileExportAsPDML->setEnabled(capture_in_progress);
     main_ui_->actionFileExportAsPlainText->setEnabled(capture_in_progress);
     main_ui_->actionFileExportAsPSML->setEnabled(capture_in_progress);
+    main_ui_->actionFileExportAsJSON->setEnabled(capture_in_progress);
 
     main_ui_->actionFileExportPacketBytes->setEnabled(capture_in_progress);
-    main_ui_->actionFileExportPDU->setEnabled(capture_in_progress);
+    main_ui_->actionFileExportPDU->setEnabled(!capture_in_progress);
     main_ui_->actionFileExportSSLSessionKeys->setEnabled(capture_in_progress);
 
     foreach (QAction *eo_action, main_ui_->menuFileExportObjects->actions()) {
@@ -2046,6 +2317,10 @@ void MainWindow::setMenusForCaptureInProgress(bool capture_in_progress) {
 
     main_ui_->menuFileSet->setEnabled(!capture_in_progress);
     main_ui_->actionFileQuit->setEnabled(true);
+#ifdef HAVE_SOFTWARE_UPDATE
+    // We might want to enable or disable automatic checks here as well.
+    update_action_->setEnabled(!capture_in_progress);
+#endif
 
     main_ui_->actionStatisticsCaptureFileProperties->setEnabled(capture_in_progress);
 
@@ -2070,6 +2345,9 @@ void MainWindow::setMenusForCaptureInProgress(bool capture_in_progress) {
 
 void MainWindow::setMenusForCaptureStopping() {
     main_ui_->actionFileQuit->setEnabled(false);
+#ifdef HAVE_SOFTWARE_UPDATE
+    update_action_->setEnabled(false);
+#endif
     main_ui_->actionStatisticsCaptureFileProperties->setEnabled(false);
 #ifdef HAVE_LIBPCAP
     main_ui_->actionCaptureStart->setChecked(false);
@@ -2135,7 +2413,7 @@ void MainWindow::changeEvent(QEvent* event)
         case QEvent::LanguageChange:
             main_ui_->retranslateUi(this);
             // make sure that the "Clear Menu" item is retranslated
-            updateRecentFiles();
+            wsApp->emitAppSignal(WiresharkApplication::RecentCapturesChanged);
             break;
         case QEvent::LocaleChange:{
             QString locale = QLocale::system().name();
@@ -2143,6 +2421,11 @@ void MainWindow::changeEvent(QEvent* event)
             wsApp->loadLanguage(locale);
             }
             break;
+#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
+        case QEvent::WindowStateChange:
+            main_ui_->actionViewFullScreen->setChecked(this->isFullScreen());
+            break;
+#endif
         default:
             break;
         }
@@ -2157,7 +2440,7 @@ void MainWindow::resizeEvent(QResizeEvent *event)
 }
 
 /* Update main window items based on whether there's a capture in progress. */
-void MainWindow::setForCaptureInProgress(gboolean capture_in_progress)
+void MainWindow::setForCaptureInProgress(bool capture_in_progress, GArray *ifaces)
 {
     setMenusForCaptureInProgress(capture_in_progress);
 
@@ -2165,10 +2448,23 @@ void MainWindow::setForCaptureInProgress(gboolean capture_in_progress)
 
 #ifdef HAVE_LIBPCAP
     packet_list_->setCaptureInProgress(capture_in_progress);
-//    set_toolbar_for_capture_in_progress(capture_in_progress);
+    packet_list_->setVerticalAutoScroll(capture_in_progress && main_ui_->actionGoAutoScroll->isChecked());
 
 //    set_capture_if_dialog_for_capture_in_progress(capture_in_progress);
 #endif
+
+#ifdef HAVE_EXTCAP
+    QList<InterfaceToolbar *> toolbars = findChildren<InterfaceToolbar *>();
+    foreach (InterfaceToolbar *toolbar, toolbars) {
+        if (capture_in_progress && ifaces) {
+            toolbar->startCapture(ifaces);
+        } else {
+            toolbar->stopCapture();
+        }
+    }
+#else
+    Q_UNUSED(ifaces)
+#endif
 }
 
 static QList<register_stat_group_t> menu_groups = QList<register_stat_group_t>()
@@ -2326,11 +2622,8 @@ void MainWindow::addDynamicMenus()
     }
 
     // Empty menus don't show up: https://bugreports.qt.io/browse/QTBUG-33728
-    // We've added a placeholder in order to make sure the "Tools" menu is
-    // visible. Hide it as needed.
-    if (wsApp->dynamicMenuGroupItems(REGISTER_TOOLS_GROUP_UNSORTED).length() > 0) {
-        main_ui_->actionToolsPlaceholder->setVisible(false);
-    }
+    // We've added a placeholder in order to make sure some menus are visible.
+    // Hide them as needed.
     if (wsApp->dynamicMenuGroupItems(REGISTER_STAT_GROUP_TELEPHONY_ANSI).length() > 0) {
         main_ui_->actionTelephonyANSIPlaceholder->setVisible(false);
     }
@@ -2411,16 +2704,13 @@ QMenu * MainWindow::searchSubMenu(QString objectName)
     return 0;
 }
 
-void MainWindow::addExternalMenus()
+void MainWindow::addPluginIFStructures()
 {
-    QMenu * subMenu = NULL;
-    GList * user_menu = NULL;
-    ext_menu_t * menu = NULL;
-
-    user_menu = ext_menubar_get_entries();
+    GList *user_menu = ext_menubar_get_entries();
 
     while (user_menu && user_menu->data) {
-        menu = (ext_menu_t *) user_menu->data;
+        QMenu *subMenu = NULL;
+        ext_menu_t *menu = (ext_menu_t *) user_menu->data;
 
         /* On this level only menu items should exist. Not doing an assert here,
          * as it could be an honest mistake */
@@ -2431,7 +2721,7 @@ void MainWindow::addExternalMenus()
 
         /* Create main submenu and add it to the menubar */
         if (menu->parent_menu) {
-            QMenu * sortUnderneath = searchSubMenu(QString(menu->parent_menu));
+            QMenu *sortUnderneath = searchSubMenu(QString(menu->parent_menu));
             if (sortUnderneath)
                 subMenu = sortUnderneath->addMenu(menu->label);
         }
@@ -2446,8 +2736,92 @@ void MainWindow::addExternalMenus()
         /* Iterate Loop */
         user_menu = g_list_next (user_menu);
     }
+
+    int cntToolbars = 0;
+
+    QMenu *tbMenu = main_ui_->menuAdditionalToolbars;
+    GList *if_toolbars = ext_toolbar_get_entries();
+    while (if_toolbars && if_toolbars->data) {
+        ext_toolbar_t *toolbar = (ext_toolbar_t*) if_toolbars->data;
+
+        if (toolbar->type != EXT_TOOLBAR_BAR) {
+            if_toolbars = g_list_next (if_toolbars);
+            continue;
+        }
+
+        bool visible = g_list_find_custom(recent.gui_additional_toolbars, toolbar->name, (GCompareFunc) strcmp) ? true : false;
+
+        AdditionalToolBar *ifToolBar = AdditionalToolBar::create(this, toolbar);
+
+        if (ifToolBar) {
+            ifToolBar->setVisible(visible);
+
+            QAction *iftbAction = new QAction(QString(toolbar->name), this);
+            iftbAction->setToolTip(toolbar->tooltip);
+            iftbAction->setEnabled(true);
+            iftbAction->setCheckable(true);
+            iftbAction->setChecked(visible);
+            iftbAction->setToolTip(tr("Show or hide the toolbar"));
+            iftbAction->setData(VariantPointer<ext_toolbar_t>::asQVariant(toolbar));
+
+            QAction *before = 0;
+
+            foreach (QAction *action, tbMenu->actions()) {
+                /* Ensure we add the menu entries in sorted order */
+                if (action->text().compare(toolbar->name, Qt::CaseInsensitive) > 0) {
+                    before = action;
+                    break;
+                }
+            }
+
+            tbMenu->insertAction(before, iftbAction);
+
+            addToolBar(Qt::TopToolBarArea, ifToolBar);
+            insertToolBarBreak(ifToolBar);
+
+            if (show_hide_actions_)
+                show_hide_actions_->addAction(iftbAction);
+
+            cntToolbars++;
+        }
+
+        if_toolbars = g_list_next (if_toolbars);
+    }
+
+    if (cntToolbars)
+        tbMenu->menuAction()->setVisible(true);
 }
 
+void MainWindow::removeAdditionalToolbar(QString toolbarName)
+{
+    if (toolbarName.length() == 0)
+        return;
+
+    QList<QToolBar *> toolbars = findChildren<QToolBar *>();
+    foreach(QToolBar *tb, toolbars) {
+        AdditionalToolBar *ifToolBar = dynamic_cast<AdditionalToolBar *>(tb);
+
+        if (ifToolBar && ifToolBar->menuName().compare(toolbarName)) {
+            GList *entry = g_list_find_custom(recent.gui_additional_toolbars, ifToolBar->menuName().toStdString().c_str(), (GCompareFunc) strcmp);
+            if (entry) {
+                recent.gui_additional_toolbars = g_list_remove(recent.gui_additional_toolbars, entry->data);
+            }
+            QList<QAction *> actions = main_ui_->menuAdditionalToolbars->actions();
+            foreach(QAction *action, actions) {
+                ext_toolbar_t *item = VariantPointer<ext_toolbar_t>::asPtr(action->data());
+                if (item && ifToolBar->menuName().compare(item->name)) {
+                    if (show_hide_actions_)
+                        show_hide_actions_->removeAction(action);
+                    main_ui_->menuAdditionalToolbars->removeAction(action);
+                }
+            }
+            break;
+        }
+    }
+
+}
+
+
 /*
  * Editor modelines
  *