qtui: Make the debug log a proper dialog
[quassel.git] / src / qtui / mainwin.cpp
index 2a1ce6f..e06d28b 100644 (file)
@@ -1,5 +1,5 @@
 /***************************************************************************
- *   Copyright (C) 2005-2014 by the Quassel Project                        *
+ *   Copyright (C) 2005-2018 by the Quassel Project                        *
  *   devel@quassel-irc.org                                                 *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
 
 #include "mainwin.h"
 
+#include <QIcon>
 #include <QMenuBar>
 #include <QMessageBox>
 #include <QStatusBar>
+#include <QTableView>
 #include <QToolBar>
+#include <QInputDialog>
 
-#ifdef HAVE_KDE
-#  include <KAction>
-#  include <KActionCollection>
+#ifdef HAVE_KDE4
 #  include <KHelpMenu>
 #  include <KMenuBar>
 #  include <KShortcutsDialog>
 #  include <KStatusBar>
 #  include <KToggleFullScreenAction>
 #  include <KToolBar>
-#  include <KWindowSystem>
+#endif
+
+#ifdef HAVE_KF5
+#  include <KConfigWidgets/KStandardAction>
+#  include <KXmlGui/KHelpMenu>
+#  include <KXmlGui/KShortcutsDialog>
+#  include <KXmlGui/KToolBar>
+#  include <KWidgetsAddons/KToggleFullScreenAction>
 #endif
 
 #ifdef Q_WS_X11
 #include "coreinfodlg.h"
 #include "contextmenuactionprovider.h"
 #include "debugbufferviewoverlay.h"
-#include "debuglogwidget.h"
+#include "debuglogdlg.h"
 #include "debugmessagemodelfilter.h"
 #include "flatproxymodel.h"
-#include "iconloader.h"
+#include "icon.h"
 #include "inputwidget.h"
 #include "irclistmodel.h"
 #include "ircconnectionwizard.h"
 #include "legacysystemtray.h"
 #include "msgprocessorstatuswidget.h"
 #include "nicklistwidget.h"
+#include "passwordchangedlg.h"
 #include "qtuiapplication.h"
 #include "qtuimessageprocessor.h"
 #include "qtuisettings.h"
 #include "statusnotifieritem.h"
 #include "toolbaractionprovider.h"
 #include "topicwidget.h"
+#include "transfermodel.h"
 #include "verticaldock.h"
 
 #ifndef HAVE_KDE
+#  ifdef HAVE_QTMULTIMEDIA
+#    include "qtmultimedianotificationbackend.h"
+#  endif
 #  ifdef HAVE_PHONON
 #    include "phononnotificationbackend.h"
 #  endif
-#  ifdef HAVE_LIBSNORE
-#    include "snorenotificationbackend.h"
-#  endif
 #  include "systraynotificationbackend.h"
 #  include "taskbarnotificationbackend.h"
 #else /* HAVE_KDE */
 #  include "knotificationbackend.h"
 #endif /* HAVE_KDE */
+#include "systrayanimationnotificationbackend.h"
+
+
+#ifdef HAVE_LIBSNORE
+#  include "snorenotificationbackend.h"
+#endif
 
 #ifdef HAVE_SSL
 #  include "sslinfodlg.h"
 #include "settingspages/bufferviewsettingspage.h"
 #include "settingspages/chatmonitorsettingspage.h"
 #include "settingspages/chatviewsettingspage.h"
+#include "settingspages/chatviewcolorsettingspage.h"
 #include "settingspages/connectionsettingspage.h"
 #include "settingspages/coreaccountsettingspage.h"
 #include "settingspages/coreconnectionsettingspage.h"
+#include <settingspages/corehighlightsettingspage.h>
+#include "settingspages/dccsettingspage.h"
 #include "settingspages/highlightsettingspage.h"
 #include "settingspages/identitiessettingspage.h"
 #include "settingspages/ignorelistsettingspage.h"
 #  include "settingspages/shortcutssettingspage.h"
 #endif
 
+#ifdef HAVE_SONNET
+#  include "settingspages/sonnetsettingspage.h"
+#endif
+
+
 MainWin::MainWin(QWidget *parent)
 #ifdef HAVE_KDE
-    : KMainWindow(parent),
-    _kHelpMenu(new KHelpMenu(this, KGlobal::mainComponent().aboutData())),
+    : KMainWindow(parent), _kHelpMenu(new KHelpMenu(this)),
 #else
     : QMainWindow(parent),
 #endif
@@ -156,7 +179,8 @@ MainWin::MainWin(QWidget *parent)
     _titleSetter(this),
     _awayLog(0),
     _layoutLoaded(false),
-    _activeBufferViewIndex(-1)
+    _activeBufferViewIndex(-1),
+    _aboutToQuit(false)
 {
     setAttribute(Qt::WA_DeleteOnClose, false); // we delete the mainwin manually
 
@@ -170,6 +194,8 @@ MainWin::MainWin(QWidget *parent)
 
     setWindowTitle("Quassel IRC");
     setWindowIconText("Quassel IRC");
+    // Set the default icon for all windows
+    QApplication::setWindowIcon(icon::get("quassel"));
     updateIcon();
 }
 
@@ -180,10 +206,19 @@ void MainWin::init()
     connect(Client::instance(), SIGNAL(networkRemoved(NetworkId)), SLOT(clientNetworkRemoved(NetworkId)));
     connect(Client::messageModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)),
         SLOT(messagesInserted(const QModelIndex &, int, int)));
-    connect(GraphicalUi::contextMenuActionProvider(), SIGNAL(showChannelList(NetworkId)), SLOT(showChannelList(NetworkId)));
+    connect(GraphicalUi::contextMenuActionProvider(),
+            SIGNAL(showChannelList(NetworkId,QString,bool)),
+            SLOT(showChannelList(NetworkId,QString,bool)));
+    connect(Client::instance(),
+            SIGNAL(showChannelList(NetworkId,QString,bool)),
+            SLOT(showChannelList(NetworkId,QString,bool)));
+    connect(GraphicalUi::contextMenuActionProvider(), SIGNAL(showNetworkConfig(NetworkId)), SLOT(showNetworkConfig(NetworkId)));
     connect(GraphicalUi::contextMenuActionProvider(), SIGNAL(showIgnoreList(QString)), SLOT(showIgnoreList(QString)));
+    connect(Client::instance(), SIGNAL(showIgnoreList(QString)), SLOT(showIgnoreList(QString)));
+    connect(Client::instance(), SIGNAL(dbUpgradeInProgress(bool)), SLOT(showMigrationWarning(bool)));
+    connect(Client::instance(), SIGNAL(exitRequested(QString)), SLOT(onExitRequested(QString)));
 
-    connect(Client::coreConnection(), SIGNAL(startCoreSetup(QVariantList)), SLOT(showCoreConfigWizard(QVariantList)));
+    connect(Client::coreConnection(), SIGNAL(startCoreSetup(QVariantList, QVariantList)), SLOT(showCoreConfigWizard(QVariantList, QVariantList)));
     connect(Client::coreConnection(), SIGNAL(connectionErrorPopup(QString)), SLOT(handleCoreConnectionError(QString)));
     connect(Client::coreConnection(), SIGNAL(userAuthenticationRequired(CoreAccount *, bool *, QString)), SLOT(userAuthenticationRequired(CoreAccount *, bool *, QString)));
     connect(Client::coreConnection(), SIGNAL(handleNoSslInClient(bool *)), SLOT(handleNoSslInClient(bool *)));
@@ -203,10 +238,11 @@ void MainWin::init()
     setupActions();
     setupBufferWidget();
     setupMenus();
+    // setupTransferWidget(); not ready yet
+    setupChatMonitor();
     setupTopicWidget();
-    setupNickWidget();
     setupInputWidget();
-    setupChatMonitor();
+    setupNickWidget();
     setupViewMenuTail();
     setupStatusBar();
     setupToolBars();
@@ -214,22 +250,31 @@ void MainWin::init()
     setupTitleSetter();
     setupHotList();
 
+    _bufferWidget->setFocusProxy(_inputWidget);
+    _chatMonitorView->setFocusProxy(_inputWidget);
+
 #ifndef HAVE_KDE
+#  ifdef HAVE_QTMULTIMEDIA
+    QtUi::registerNotificationBackend(new QtMultimediaNotificationBackend(this));
+#  endif
 #  ifdef HAVE_PHONON
     QtUi::registerNotificationBackend(new PhononNotificationBackend(this));
 #  endif
-#  ifdef HAVE_LIBSNORE
-    QtUi::registerNotificationBackend(new SnoreNotificationBackend(this));
-#  elif !defined(QT_NO_SYSTEMTRAYICON)
-    QtUi::registerNotificationBackend(new SystrayNotificationBackend(this));
-#  endif
-
     QtUi::registerNotificationBackend(new TaskbarNotificationBackend(this));
-
 #else /* HAVE_KDE */
     QtUi::registerNotificationBackend(new KNotificationBackend(this));
 #endif /* HAVE_KDE */
 
+
+#ifndef QT_NO_SYSTEMTRAYICON
+    QtUi::registerNotificationBackend(new SystrayAnimationNotificationBackend(this));
+#endif
+#ifdef HAVE_LIBSNORE
+    QtUi::registerNotificationBackend(new SnoreNotificationBackend(this));
+#elif !defined(QT_NO_SYSTEMTRAYICON) && !defined(HAVE_KDE)
+    QtUi::registerNotificationBackend(new SystrayNotificationBackend(this));
+#endif
+
 #ifdef HAVE_INDICATEQT
     QtUi::registerNotificationBackend(new IndicatorNotificationBackend(this));
 #endif
@@ -260,25 +305,17 @@ void MainWin::init()
     // restore locked state of docks
     QtUi::actionCollection("General")->action("LockLayout")->setChecked(s.value("LockLayout", false).toBool());
 
-    CoreConnection *conn = Client::coreConnection();
-    if (!conn->connectToCore()) {
-        // No autoconnect selected (or no accounts)
-        showCoreConnectionDlg();
-    }
-}
-
+    Quassel::registerQuitHandler([this]() {
+        QtUiSettings s;
+        saveStateToSettings(s);
+        saveLayout();
+        // Close all open dialogs and the MainWin, so we can safely kill the Client instance afterwards
+        // Note: This does not quit the application, as quitOnLastWindowClosed is set to false.
+        //       We rely on another quit handler to be registered that actually quits the application.
+        qApp->closeAllWindows();
+    });
 
-MainWin::~MainWin()
-{
-}
-
-
-void MainWin::quit()
-{
-    QtUiSettings s;
-    saveStateToSettings(s);
-    saveLayout();
-    QApplication::quit();
+    QTimer::singleShot(0, this, SLOT(doAutoConnect()));
 }
 
 
@@ -334,22 +371,24 @@ void MainWin::restoreStateFromSettings(UiSettings &s)
         show();
 }
 
+QMenu *MainWin::createPopupMenu()
+{
+    QMenu *popupMenu = QMainWindow::createPopupMenu();
+    popupMenu->addSeparator();
+    ActionCollection *coll = QtUi::actionCollection("General");
+    popupMenu->addAction(coll->action("ToggleMenuBar"));
+    return popupMenu;
+}
+
 
 void MainWin::updateIcon()
 {
-#ifdef Q_OS_MAC
-    const int size = 128;
-#else
-    const int size = 48;
-#endif
-
-    QPixmap icon;
+    QIcon icon;
     if (Client::isConnected())
-        icon = DesktopIcon("quassel", size);
+        icon = icon::get("quassel");
     else
-        icon = DesktopIcon("quassel-inactive", size);
+        icon = icon::get("inactive-quassel");
     setWindowIcon(icon);
-    qApp->setWindowIcon(icon);
 }
 
 
@@ -357,17 +396,23 @@ void MainWin::setupActions()
 {
     ActionCollection *coll = QtUi::actionCollection("General", tr("General"));
     // File
-    coll->addAction("ConnectCore", new Action(SmallIcon("network-connect"), tr("&Connect to Core..."), coll,
+    coll->addAction("ConnectCore", new Action(icon::get("connect-quassel"), tr("&Connect to Core..."), coll,
             this, SLOT(showCoreConnectionDlg())));
-    coll->addAction("DisconnectCore", new Action(SmallIcon("network-disconnect"), tr("&Disconnect from Core"), coll,
+    coll->addAction("DisconnectCore", new Action(icon::get("disconnect-quassel"), tr("&Disconnect from Core"), coll,
             Client::instance(), SLOT(disconnectFromCore())));
-    coll->addAction("CoreInfo", new Action(SmallIcon("help-about"), tr("Core &Info..."), coll,
+    coll->addAction("ChangePassword", new Action(icon::get("dialog-password"), tr("Change &Password..."), coll,
+            this, SLOT(showPasswordChangeDlg())));
+    coll->addAction("CoreInfo", new Action(icon::get("help-about"), tr("Core &Info..."), coll,
             this, SLOT(showCoreInfoDlg())));
-    coll->addAction("ConfigureNetworks", new Action(SmallIcon("configure"), tr("Configure &Networks..."), coll,
+    coll->addAction("ConfigureNetworks", new Action(icon::get("configure"), tr("Configure &Networks..."), coll,
             this, SLOT(on_actionConfigureNetworks_triggered())));
-    // FIXME: use QKeySequence::Quit once we depend on Qt 4.6
-    coll->addAction("Quit", new Action(SmallIcon("application-exit"), tr("&Quit"), coll,
-            this, SLOT(quit()), Qt::CTRL + Qt::Key_Q));
+    // QKeySequence::Quit was added in Qt 4.6, and could be used instead.  However, that key
+    // sequence is empty by default on Windows, which would remove Ctrl-Q to quit.  It may be best
+    // to just keep it this way.
+    //
+    // See https://doc.qt.io/qt-5/qkeysequence.html
+    coll->addAction("Quit", new Action(icon::get("application-exit"), tr("&Quit"), coll,
+            Quassel::instance(), SLOT(quit()), Qt::CTRL + Qt::Key_Q));
 
     // View
     coll->addAction("ConfigureBufferViews", new Action(tr("&Configure Chat Lists..."), coll,
@@ -377,43 +422,43 @@ void MainWin::setupActions()
     lockAct->setCheckable(true);
     connect(lockAct, SIGNAL(toggled(bool)), SLOT(on_actionLockLayout_toggled(bool)));
 
-    coll->addAction("ToggleSearchBar", new Action(SmallIcon("edit-find"), tr("Show &Search Bar"), coll,
+    coll->addAction("ToggleSearchBar", new Action(icon::get("edit-find"), tr("Show &Search Bar"), coll,
             0, 0, QKeySequence::Find))->setCheckable(true);
     coll->addAction("ShowAwayLog", new Action(tr("Show Away Log"), coll,
             this, SLOT(showAwayLog())));
-    coll->addAction("ToggleMenuBar", new Action(SmallIcon("show-menu"), tr("Show &Menubar"), coll,
-            0, 0, QKeySequence(Qt::CTRL + Qt::Key_M)))->setCheckable(true);
+    coll->addAction("ToggleMenuBar", new Action(icon::get("show-menu"), tr("Show &Menubar"), coll,
+            0, 0))->setCheckable(true);
 
     coll->addAction("ToggleStatusBar", new Action(tr("Show Status &Bar"), coll,
             0, 0))->setCheckable(true);
 
 #ifdef HAVE_KDE
-    QAction *fullScreenAct = KStandardAction::fullScreen(this, SLOT(onFullScreenToggled()), this, coll);
+    _fullScreenAction = KStandardAction::fullScreen(this, SLOT(onFullScreenToggled()), this, coll);
 #else
-    QAction *fullScreenAct = new Action(SmallIcon("view-fullscreen"), tr("&Full Screen Mode"), coll,
+    _fullScreenAction = new Action(icon::get("view-fullscreen"), tr("&Full Screen Mode"), coll,
         this, SLOT(onFullScreenToggled()), QKeySequence(Qt::Key_F11));
-    fullScreenAct->setCheckable(true);
+    _fullScreenAction->setCheckable(true);
+    coll->addAction("ToggleFullScreen", _fullScreenAction);
 #endif
-    coll->addAction("ToggleFullScreen", fullScreenAct);
 
     // Settings
-    QAction *configureShortcutsAct = new Action(SmallIcon("configure-shortcuts"), tr("Configure &Shortcuts..."), coll,
+    QAction *configureShortcutsAct = new Action(icon::get("configure-shortcuts"), tr("Configure &Shortcuts..."), coll,
         this, SLOT(showShortcutsDlg()));
     configureShortcutsAct->setMenuRole(QAction::NoRole);
     coll->addAction("ConfigureShortcuts", configureShortcutsAct);
 
-  #ifdef Q_OS_MAC
-    QAction *configureQuasselAct = new Action(SmallIcon("configure"), tr("&Configure Quassel..."), coll,
+#ifdef Q_OS_MAC
+    QAction *configureQuasselAct = new Action(icon::get("configure"), tr("&Configure Quassel..."), coll,
         this, SLOT(showSettingsDlg()));
     configureQuasselAct->setMenuRole(QAction::PreferencesRole);
-  #else
-    QAction *configureQuasselAct = new Action(SmallIcon("configure"), tr("&Configure Quassel..."), coll,
+#else
+    QAction *configureQuasselAct = new Action(icon::get("configure"), tr("&Configure Quassel..."), coll,
         this, SLOT(showSettingsDlg()), QKeySequence(Qt::Key_F7));
-  #endif
+#endif
     coll->addAction("ConfigureQuassel", configureQuasselAct);
 
     // Help
-    QAction *aboutQuasselAct = new Action(SmallIcon("quassel"), tr("&About Quassel"), coll,
+    QAction *aboutQuasselAct = new Action(icon::get("quassel"), tr("&About Quassel"), coll,
         this, SLOT(showAboutDlg()));
     aboutQuasselAct->setMenuRole(QAction::AboutRole);
     coll->addAction("AboutQuassel", aboutQuasselAct);
@@ -422,28 +467,63 @@ void MainWin::setupActions()
         qApp, SLOT(aboutQt()));
     aboutQtAct->setMenuRole(QAction::AboutQtRole);
     coll->addAction("AboutQt", aboutQtAct);
-    coll->addAction("DebugNetworkModel", new Action(SmallIcon("tools-report-bug"), tr("Debug &NetworkModel"), coll,
+    coll->addAction("DebugNetworkModel", new Action(icon::get("tools-report-bug"), tr("Debug &NetworkModel"), coll,
             this, SLOT(on_actionDebugNetworkModel_triggered())));
-    coll->addAction("DebugBufferViewOverlay", new Action(SmallIcon("tools-report-bug"), tr("Debug &BufferViewOverlay"), coll,
+    coll->addAction("DebugBufferViewOverlay", new Action(icon::get("tools-report-bug"), tr("Debug &BufferViewOverlay"), coll,
             this, SLOT(on_actionDebugBufferViewOverlay_triggered())));
-    coll->addAction("DebugMessageModel", new Action(SmallIcon("tools-report-bug"), tr("Debug &MessageModel"), coll,
+    coll->addAction("DebugMessageModel", new Action(icon::get("tools-report-bug"), tr("Debug &MessageModel"), coll,
             this, SLOT(on_actionDebugMessageModel_triggered())));
-    coll->addAction("DebugHotList", new Action(SmallIcon("tools-report-bug"), tr("Debug &HotList"), coll,
+    coll->addAction("DebugHotList", new Action(icon::get("tools-report-bug"), tr("Debug &HotList"), coll,
             this, SLOT(on_actionDebugHotList_triggered())));
-    coll->addAction("DebugLog", new Action(SmallIcon("tools-report-bug"), tr("Debug &Log"), coll,
+    coll->addAction("DebugLog", new Action(icon::get("tools-report-bug"), tr("Debug &Log"), coll,
             this, SLOT(on_actionDebugLog_triggered())));
-    coll->addAction("ReloadStyle", new Action(SmallIcon("view-refresh"), tr("Reload Stylesheet"), coll,
-            QtUi::style(), SLOT(reload()), QKeySequence::Refresh));
+    coll->addAction("ReloadStyle", new Action(icon::get("view-refresh"), tr("Reload Stylesheet"), coll,
+            QtUi::style(), SLOT(reload()), QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_R)));
 
     coll->addAction("HideCurrentBuffer", new Action(tr("Hide Current Buffer"), coll,
             this, SLOT(hideCurrentBuffer()), QKeySequence::Close));
 
+    // Text formatting
+    coll = QtUi::actionCollection("TextFormat", tr("Text formatting"));
+
+    coll->addAction("FormatApplyColor", new Action(
+                        icon::get("format-text-color"), tr("Apply foreground color"), coll,
+                        this, SLOT(on_inputFormatApplyColor_triggered()),
+                        QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_G)));
+
+    coll->addAction("FormatApplyColorFill", new Action(
+                        icon::get("format-fill-color"), tr("Apply background color"), coll,
+                        this, SLOT(on_inputFormatApplyColorFill_triggered()),
+                        QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_B)));
+
+    coll->addAction("FormatClear", new Action(
+                        icon::get("edit-clear"), tr("Clear formatting"), coll,
+                        this, SLOT(on_inputFormatClear_triggered()),
+                        QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_C)));
+
+    coll->addAction("FormatBold", new Action(
+                        icon::get("format-text-bold"), tr("Toggle bold"), coll,
+                        this, SLOT(on_inputFormatBold_triggered()),
+                        QKeySequence::Bold));
+
+    coll->addAction("FormatItalic", new Action(
+                        icon::get("format-text-italic"), tr("Toggle italics"), coll,
+                        this, SLOT(on_inputFormatItalic_triggered()),
+                        QKeySequence::Italic));
+
+    coll->addAction("FormatUnderline", new Action(
+                        icon::get("format-text-underline"), tr("Toggle underline"), coll,
+                        this, SLOT(on_inputFormatUnderline_triggered()), QKeySequence::Underline));
+
     // Navigation
     coll = QtUi::actionCollection("Navigation", tr("Navigation"));
 
     coll->addAction("JumpHotBuffer", new Action(tr("Jump to hot chat"), coll,
             this, SLOT(on_jumpHotBuffer_triggered()), QKeySequence(Qt::META + Qt::Key_A)));
 
+    coll->addAction("ActivateBufferFilter", new Action(tr("Activate the buffer search"), coll,
+            this, SLOT(on_bufferSearch_triggered()), QKeySequence(Qt::CTRL + Qt::Key_S)));
+
     // Jump keys
 #ifdef Q_OS_MAC
     const int bindModifier = Qt::ControlModifier | Qt::AltModifier;
@@ -496,13 +576,13 @@ void MainWin::setupActions()
             QKeySequence(jumpModifier + Qt::Key_9)))->setProperty("Index", 9);
 
     // Buffer navigation
-    coll->addAction("NextBufferView", new Action(SmallIcon("go-next-view"), tr("Activate Next Chat List"), coll,
+    coll->addAction("NextBufferView", new Action(icon::get("go-next-view"), tr("Activate Next Chat List"), coll,
             this, SLOT(nextBufferView()), QKeySequence(QKeySequence::Forward)));
-    coll->addAction("PreviousBufferView", new Action(SmallIcon("go-previous-view"), tr("Activate Previous Chat List"), coll,
+    coll->addAction("PreviousBufferView", new Action(icon::get("go-previous-view"), tr("Activate Previous Chat List"), coll,
             this, SLOT(previousBufferView()), QKeySequence::Back));
-    coll->addAction("NextBuffer", new Action(SmallIcon("go-down"), tr("Go to Next Chat"), coll,
+    coll->addAction("NextBuffer", new Action(icon::get("go-down"), tr("Go to Next Chat"), coll,
             this, SLOT(nextBuffer()), QKeySequence(Qt::ALT + Qt::Key_Down)));
-    coll->addAction("PreviousBuffer", new Action(SmallIcon("go-up"), tr("Go to Previous Chat"), coll,
+    coll->addAction("PreviousBuffer", new Action(icon::get("go-up"), tr("Go to Previous Chat"), coll,
             this, SLOT(previousBuffer()), QKeySequence(Qt::ALT + Qt::Key_Up)));
 }
 
@@ -514,7 +594,7 @@ void MainWin::setupMenus()
     _fileMenu = menuBar()->addMenu(tr("&File"));
 
     static const QStringList coreActions = QStringList()
-                                           << "ConnectCore" << "DisconnectCore" << "CoreInfo";
+        << "ConnectCore" << "DisconnectCore" << "ChangePassword" << "CoreInfo";
 
     QAction *coreAction;
     foreach(QString actionName, coreActions) {
@@ -556,7 +636,9 @@ void MainWin::setupMenus()
 #endif
     _settingsMenu->addAction(coll->action("ConfigureQuassel"));
 
+
     _helpMenu = menuBar()->addMenu(tr("&Help"));
+
     _helpMenu->addAction(coll->action("AboutQuassel"));
 #ifndef HAVE_KDE
     _helpMenu->addAction(coll->action("AboutQt"));
@@ -564,7 +646,7 @@ void MainWin::setupMenus()
     _helpMenu->addAction(KStandardAction::aboutKDE(_kHelpMenu, SLOT(aboutKDE()), this));
 #endif
     _helpMenu->addSeparator();
-    _helpDebugMenu = _helpMenu->addMenu(SmallIcon("tools-report-bug"), tr("Debug"));
+    _helpDebugMenu = _helpMenu->addMenu(icon::get("tools-report-bug"), tr("Debug"));
     _helpDebugMenu->addAction(coll->action("DebugNetworkModel"));
     _helpDebugMenu->addAction(coll->action("DebugBufferViewOverlay"));
     _helpDebugMenu->addAction(coll->action("DebugMessageModel"));
@@ -616,6 +698,8 @@ void MainWin::addBufferView(ClientBufferViewConfig *config)
 
     Client::bufferModel()->synchronizeView(view);
 
+    dock->setLocked(QtUiSettings().value("LockLayout", false).toBool());
+
     dock->setWidget(view);
     dock->setVisible(_layoutLoaded); // don't show before state has been restored
 
@@ -705,7 +789,7 @@ BufferView *MainWin::activeBufferView() const
     if (_activeBufferViewIndex < 0 || _activeBufferViewIndex >= _bufferViews.count())
         return 0;
     BufferViewDock *dock = _bufferViews.at(_activeBufferViewIndex);
-    return dock->isActive() ? qobject_cast<BufferView *>(dock->widget()) : 0;
+    return dock->isActive() ? dock->bufferView() : 0;
 }
 
 
@@ -714,9 +798,8 @@ void MainWin::changeActiveBufferView(int bufferViewId)
     if (bufferViewId < 0)
         return;
 
-    BufferView *current = activeBufferView();
-    if (current) {
-        qobject_cast<BufferViewDock *>(current->parent())->setActive(false);
+    if (_activeBufferViewIndex >= 0 && _activeBufferViewIndex < _bufferViews.count()) {
+        _bufferViews[_activeBufferViewIndex]->setActive(false);
         _activeBufferViewIndex = -1;
     }
 
@@ -733,11 +816,62 @@ void MainWin::changeActiveBufferView(int bufferViewId)
 }
 
 
+void MainWin::showPasswordChangeDlg()
+{
+    if(Client::isCoreFeatureEnabled(Quassel::Feature::PasswordChange)) {
+        PasswordChangeDlg{}.exec();
+    }
+    else {
+        QMessageBox box(QMessageBox::Warning, tr("Feature Not Supported"),
+                        tr("<b>Your Quassel Core does not support this feature</b>"),
+                        QMessageBox::Ok);
+        box.setInformativeText(tr("You need a Quassel Core v0.12.0 or newer in order to be able to remotely change your password."));
+        box.exec();
+    }
+}
+
+
+void MainWin::showMigrationWarning(bool show)
+{
+    if (show && !_migrationWarning) {
+        _migrationWarning = new QMessageBox(QMessageBox::Information,
+                                            tr("Upgrading..."),
+                                            "<b>" + tr("Your database is being upgraded") + "</b>",
+                                            QMessageBox::NoButton, this);
+        _migrationWarning->setInformativeText("<p>"
+                                              + tr("In order to support new features, we need to make changes to your backlog database. This may take a long while.")
+                                              + "</p><p>"
+                                              + tr("Do not exit Quassel until the upgrade is complete!")
+                                              + "</p>");
+        _migrationWarning->setStandardButtons(QMessageBox::NoButton);
+        _migrationWarning->show();
+    }
+    else if (!show && _migrationWarning) {
+        _migrationWarning->close();
+        _migrationWarning->deleteLater();
+        _migrationWarning = nullptr;
+    }
+}
+
+
+void MainWin::onExitRequested(const QString &reason)
+{
+    if (!reason.isEmpty()) {
+        QMessageBox box(QMessageBox::Critical,
+                        tr("Fatal error"),
+                        "<b>" + tr("Quassel encountered a fatal error and is terminated.") + "</b>",
+                        QMessageBox::Ok);
+        box.setInformativeText("<p>" + tr("Reason:<em>") + " " + reason + "</em>");
+        box.exec();
+    }
+}
+
+
 void MainWin::changeActiveBufferView(bool backwards)
 {
-    BufferView *current = activeBufferView();
-    if (current)
-        qobject_cast<BufferViewDock *>(current->parent())->setActive(false);
+    if (_activeBufferViewIndex >= 0 && _activeBufferViewIndex < _bufferViews.count()) {
+        _bufferViews[_activeBufferViewIndex]->setActive(false);
+    }
 
     if (!_bufferViews.count())
         return;
@@ -803,22 +937,19 @@ void MainWin::hideCurrentBuffer()
 
 void MainWin::showNotificationsDlg()
 {
-    SettingsPageDlg dlg(new NotificationsSettingsPage(this), this);
-    dlg.exec();
+    SettingsPageDlg{new NotificationsSettingsPage{}}.exec();
 }
 
 
 void MainWin::on_actionConfigureNetworks_triggered()
 {
-    SettingsPageDlg dlg(new NetworksSettingsPage(this), this);
-    dlg.exec();
+    SettingsPageDlg{new NetworksSettingsPage{}}.exec();
 }
 
 
 void MainWin::on_actionConfigureViews_triggered()
 {
-    SettingsPageDlg dlg(new BufferViewSettingsPage(this), this);
-    dlg.exec();
+    SettingsPageDlg{new BufferViewSettingsPage{}}.exec();
 }
 
 
@@ -828,11 +959,26 @@ void MainWin::on_actionLockLayout_toggled(bool lock)
     foreach(VerticalDock *dock, docks) {
         dock->showTitle(!lock);
     }
+
+    QList<NickListDock *> nickdocks = findChildren<NickListDock *>();
+    foreach(NickListDock *nickdock, nickdocks) {
+        nickdock->setLocked(lock);
+    }
+
+    QList<BufferViewDock *> bufferdocks = findChildren<BufferViewDock *>();
+    foreach(BufferViewDock *bufferdock, bufferdocks) {
+        bufferdock->setLocked(lock);
+    }
+
     if (Client::bufferViewManager()) {
         foreach(ClientBufferViewConfig *config, Client::bufferViewManager()->clientBufferViewConfigs()) {
             config->setLocked(lock);
         }
     }
+
+    _mainToolBar->setMovable(!lock);
+    _nickToolBar->setMovable(!lock);
+
     QtUiSettings().setValue("LockLayout", lock);
 }
 
@@ -843,6 +989,7 @@ void MainWin::setupNickWidget()
     NickListDock *nickDock = new NickListDock(tr("Nicks"), this);
     nickDock->setObjectName("NickDock");
     nickDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
+    nickDock->setLocked(QtUiSettings().value("LockLayout", false).toBool());
 
     _nickListWidget = new NickListWidget(nickDock);
     nickDock->setWidget(_nickListWidget);
@@ -857,6 +1004,8 @@ void MainWin::setupNickWidget()
     // attach the NickListWidget to the BufferModel and the default selection
     _nickListWidget->setModel(Client::bufferModel());
     _nickListWidget->setSelectionModel(Client::bufferModel()->standardSelectionModel());
+
+    _nickListWidget->setVisible(false);
 }
 
 
@@ -867,7 +1016,6 @@ void MainWin::setupChatMonitor()
 
     ChatMonitorFilter *filter = new ChatMonitorFilter(Client::messageModel(), this);
     _chatMonitorView = new ChatMonitorView(filter, this);
-    _chatMonitorView->setFocusProxy(_inputWidget);
     _chatMonitorView->show();
     dock->setWidget(_chatMonitorView);
     dock->hide();
@@ -894,8 +1042,6 @@ void MainWin::setupInputWidget()
     _inputWidget->setModel(Client::bufferModel());
     _inputWidget->setSelectionModel(Client::bufferModel()->standardSelectionModel());
 
-    _bufferWidget->setFocusProxy(_inputWidget);
-
     _inputWidget->inputLine()->installEventFilter(_bufferWidget);
 
     connect(_topicWidget, SIGNAL(switchedPlain()), _bufferWidget, SLOT(setFocus()));
@@ -920,10 +1066,31 @@ void MainWin::setupTopicWidget()
 }
 
 
+void MainWin::setupTransferWidget()
+{
+    auto dock = new QDockWidget(tr("Transfers"), this);
+    dock->setObjectName("TransferDock");
+    dock->setAllowedAreas(Qt::TopDockWidgetArea|Qt::BottomDockWidgetArea);
+
+    auto view = new QTableView(dock); // to be replaced by the real thing
+    view->setModel(Client::transferModel());
+    dock->setWidget(view);
+    dock->hide(); // hidden by default
+    addDockWidget(Qt::TopDockWidgetArea, dock, Qt::Vertical);
+
+    auto action = dock->toggleViewAction();
+    action->setText(tr("Show File Transfers"));
+    action->setIcon(icon::get("download"));
+    action->setShortcut(QKeySequence(Qt::Key_F6));
+    QtUi::actionCollection("General")->addAction("ShowTransferWidget", action);
+    _viewMenu->addAction(action);
+}
+
+
 void MainWin::setupViewMenuTail()
 {
     _viewMenu->addSeparator();
-    _viewMenu->addAction(QtUi::actionCollection("General")->action("ToggleFullScreen"));
+    _viewMenu->addAction(_fullScreenAction);
 }
 
 
@@ -989,7 +1156,6 @@ void MainWin::setupSystray()
 #else
     _systemTray = new SystemTray(this); // dummy
 #endif
-    _systemTray->init();
 }
 
 
@@ -1013,9 +1179,31 @@ void MainWin::setupToolBars()
     _mainToolBar->setWindowTitle(tr("Main Toolbar"));
     addToolBar(_mainToolBar);
 
+    if (Quassel::runMode() != Quassel::Monolithic) {
+        ActionCollection *coll = QtUi::actionCollection("General");
+        _mainToolBar->addAction(coll->action("ConnectCore"));
+        _mainToolBar->addAction(coll->action("DisconnectCore"));
+    }
+
+    _mainToolBar->setMovable(!QtUiSettings().value("LockLayout", false).toBool());
+
     QtUi::toolBarActionProvider()->addActions(_mainToolBar, ToolBarActionProvider::MainToolBar);
     _toolbarMenu->addAction(_mainToolBar->toggleViewAction());
 
+#ifdef HAVE_KDE
+    _nickToolBar = new KToolBar("NickToolBar", this, Qt::TopToolBarArea, false, true, true);
+#else
+    _nickToolBar = new QToolBar(this);
+    _nickToolBar->setObjectName("NickToolBar");
+#endif
+    _nickToolBar->setWindowTitle(tr("Nick Toolbar"));
+    _nickToolBar->setVisible(false); //default: not visible
+    addToolBar(_nickToolBar);
+    _nickToolBar->setMovable(!QtUiSettings().value("LockLayout", false).toBool());
+
+    QtUi::toolBarActionProvider()->addActions(_nickToolBar, ToolBarActionProvider::NickToolBar);
+    _toolbarMenu->addAction(_nickToolBar->toggleViewAction());
+
 #ifdef Q_OS_MAC
     QtUiSettings uiSettings;
 
@@ -1025,13 +1213,24 @@ void MainWin::setupToolBars()
 #endif
 }
 
-#ifdef Q_OS_MAC
 void MainWin::saveMainToolBarStatus(bool enabled)
 {
+#ifdef Q_OS_MAC
     QtUiSettings uiSettings;
     uiSettings.setValue("ShowMainToolBar", enabled);
-}
+#else
+    Q_UNUSED(enabled);
 #endif
+}
+
+
+void MainWin::doAutoConnect()
+{
+    if (!Client::coreConnection()->connectToCore()) {
+        // No autoconnect selected (or no accounts)
+        showCoreConnectionDlg();
+    }
+}
 
 
 void MainWin::connectedToCore()
@@ -1041,7 +1240,9 @@ void MainWin::connectedToCore()
     connect(Client::bufferViewManager(), SIGNAL(bufferViewConfigDeleted(int)), this, SLOT(removeBufferView(int)));
     connect(Client::bufferViewManager(), SIGNAL(initDone()), this, SLOT(loadLayout()));
 
-    connect(Client::transferManager(), SIGNAL(transferAdded(const ClientTransfer*)), SLOT(showNewTransferDlg(const ClientTransfer*)));
+    if (Client::transferManager()) {
+        connect(Client::transferManager(), SIGNAL(transferAdded(QUuid)), SLOT(showNewTransferDlg(QUuid)));
+    }
 
     setConnectedState();
 }
@@ -1053,6 +1254,7 @@ void MainWin::setConnectedState()
 
     coll->action("ConnectCore")->setEnabled(false);
     coll->action("DisconnectCore")->setEnabled(true);
+    coll->action("ChangePassword")->setEnabled(true);
     coll->action("CoreInfo")->setEnabled(true);
 
     foreach(QAction *action, _fileMenu->actions()) {
@@ -1106,7 +1308,7 @@ void MainWin::loadLayout()
         _layoutLoaded = true;
         return;
     }
-
+    _nickListWidget->setVisible(true);
     restoreState(state, accountId);
     int bufferViewId = s.value(QString("ActiveBufferView-%1").arg(accountId), -1).toInt();
     if (bufferViewId >= 0)
@@ -1169,19 +1371,21 @@ void MainWin::setDisconnectedState()
     coll->action("ConnectCore")->setEnabled(true);
     coll->action("DisconnectCore")->setEnabled(false);
     coll->action("CoreInfo")->setEnabled(false);
+    coll->action("ChangePassword")->setEnabled(false);
     //_viewMenu->setEnabled(false);
     statusBar()->showMessage(tr("Not connected to core."));
     if (_msgProcessorStatusWidget)
         _msgProcessorStatusWidget->setProgress(0, 0);
     updateIcon();
     systemTray()->setState(SystemTray::Passive);
+    _nickListWidget->setVisible(false);
 }
 
 
 void MainWin::userAuthenticationRequired(CoreAccount *account, bool *valid, const QString &errorMessage)
 {
     Q_UNUSED(errorMessage)
-    CoreConnectAuthDlg dlg(account, this);
+    CoreConnectAuthDlg dlg(account);
     *valid = (dlg.exec() == QDialog::Accepted);
 }
 
@@ -1189,7 +1393,7 @@ void MainWin::userAuthenticationRequired(CoreAccount *account, bool *valid, cons
 void MainWin::handleNoSslInClient(bool *accepted)
 {
     QMessageBox box(QMessageBox::Warning, tr("Unencrypted Connection"), tr("<b>Your client does not support SSL encryption</b>"),
-        QMessageBox::Ignore|QMessageBox::Cancel, this);
+        QMessageBox::Ignore|QMessageBox::Cancel);
     box.setInformativeText(tr("Sensitive data, like passwords, will be transmitted unencrypted to your Quassel core."));
     box.setDefaultButton(QMessageBox::Ignore);
     *accepted = box.exec() == QMessageBox::Ignore;
@@ -1199,7 +1403,7 @@ void MainWin::handleNoSslInClient(bool *accepted)
 void MainWin::handleNoSslInCore(bool *accepted)
 {
     QMessageBox box(QMessageBox::Warning, tr("Unencrypted Connection"), tr("<b>Your core does not support SSL encryption</b>"),
-        QMessageBox::Ignore|QMessageBox::Cancel, this);
+        QMessageBox::Ignore|QMessageBox::Cancel);
     box.setInformativeText(tr("Sensitive data, like passwords, will be transmitted unencrypted to your Quassel core."));
     box.setDefaultButton(QMessageBox::Ignore);
     *accepted = box.exec() == QMessageBox::Ignore;
@@ -1218,7 +1422,7 @@ void MainWin::handleSslErrors(const QSslSocket *socket, bool *accepted, bool *pe
     QMessageBox box(QMessageBox::Warning,
         tr("Untrusted Security Certificate"),
         tr("<b>The SSL certificate provided by the core at %1 is untrusted for the following reasons:</b>").arg(socket->peerName()),
-        QMessageBox::Cancel, this);
+        QMessageBox::Cancel);
     box.setInformativeText(errorString);
     box.addButton(tr("Continue"), QMessageBox::AcceptRole);
     box.setDefaultButton(box.addButton(tr("Show Certificate"), QMessageBox::HelpRole));
@@ -1228,7 +1432,7 @@ void MainWin::handleSslErrors(const QSslSocket *socket, bool *accepted, bool *pe
         box.exec();
         role = box.buttonRole(box.clickedButton());
         if (role == QMessageBox::HelpRole) {
-            SslInfoDlg dlg(socket, this);
+            SslInfoDlg dlg(socket);
             dlg.exec();
         }
     }
@@ -1239,7 +1443,7 @@ void MainWin::handleSslErrors(const QSslSocket *socket, bool *accepted, bool *pe
         QMessageBox box2(QMessageBox::Warning,
             tr("Untrusted Security Certificate"),
             tr("Would you like to accept this certificate forever without being prompted?"),
-            0, this);
+            0);
         box2.setDefaultButton(box2.addButton(tr("Current Session Only"), QMessageBox::NoRole));
         box2.addButton(tr("Forever"), QMessageBox::YesRole);
         box2.exec();
@@ -1258,7 +1462,7 @@ void MainWin::handleCoreConnectionError(const QString &error)
 
 void MainWin::showCoreConnectionDlg()
 {
-    CoreConnectDlg dlg(this);
+    CoreConnectDlg dlg;
     if (dlg.exec() == QDialog::Accepted) {
         AccountId accId = dlg.selectedAccount();
         if (accId.isValid())
@@ -1267,33 +1471,57 @@ void MainWin::showCoreConnectionDlg()
 }
 
 
-void MainWin::showCoreConfigWizard(const QVariantList &backends)
+void MainWin::showCoreConfigWizard(const QVariantList &backends, const QVariantList &authenticators)
 {
-    CoreConfigWizard *wizard = new CoreConfigWizard(Client::coreConnection(), backends, this);
+    CoreConfigWizard *wizard = new CoreConfigWizard(Client::coreConnection(), backends, authenticators, this);
 
     wizard->show();
 }
 
 
-void MainWin::showChannelList(NetworkId netId)
+void MainWin::showChannelList(NetworkId netId, const QString &channelFilters, bool listImmediately)
 {
-    ChannelListDlg *channelListDlg = new ChannelListDlg();
-
     if (!netId.isValid()) {
         QAction *action = qobject_cast<QAction *>(sender());
         if (action)
             netId = action->data().value<NetworkId>();
+        if (!netId.isValid()) {
+            // We still haven't found a valid network, probably no network selected, e.g. "/list"
+            // on the client homescreen when no networks are connected.
+            QMessageBox box(QMessageBox::Information, tr("No network selected"),
+                            QString("<b>%1</b>").arg(tr("No network selected")),
+                            QMessageBox::Ok);
+            box.setInformativeText(tr("Select a network before trying to view the channel list."));
+            box.exec();
+            return;
+        }
     }
 
+    ChannelListDlg *channelListDlg = new ChannelListDlg(this);
     channelListDlg->setAttribute(Qt::WA_DeleteOnClose);
     channelListDlg->setNetwork(netId);
+    if (!channelFilters.isEmpty()) {
+        channelListDlg->setChannelFilters(channelFilters);
+    }
+    if (listImmediately) {
+        channelListDlg->requestSearch();
+    }
     channelListDlg->show();
 }
 
 
+void MainWin::showNetworkConfig(NetworkId netId)
+{
+    SettingsPageDlg dlg{new NetworksSettingsPage{}};
+    if (netId.isValid())
+        qobject_cast<NetworksSettingsPage *>(dlg.currentPage())->bufferList_Open(netId);
+    dlg.exec();
+}
+
+
 void MainWin::showIgnoreList(QString newRule)
 {
-    SettingsPageDlg dlg(new IgnoreListSettingsPage(this), this);
+    SettingsPageDlg dlg{new IgnoreListSettingsPage{}};
     // prepare config dialog for new rule
     if (!newRule.isEmpty())
         qobject_cast<IgnoreListSettingsPage *>(dlg.currentPage())->editIgnoreRule(newRule);
@@ -1303,7 +1531,7 @@ void MainWin::showIgnoreList(QString newRule)
 
 void MainWin::showCoreInfoDlg()
 {
-    CoreInfoDlg(this).exec();
+    CoreInfoDlg{}.exec();
 }
 
 
@@ -1328,17 +1556,22 @@ void MainWin::awayLogDestroyed()
 
 void MainWin::showSettingsDlg()
 {
-    SettingsDlg *dlg = new SettingsDlg();
+    SettingsDlg *dlg = new SettingsDlg(this);
 
     //Category: Interface
     dlg->registerSettingsPage(new AppearanceSettingsPage(dlg));
     dlg->registerSettingsPage(new ChatViewSettingsPage(dlg));
+    dlg->registerSettingsPage(new ChatViewColorSettingsPage(dlg));
     dlg->registerSettingsPage(new ChatMonitorSettingsPage(dlg));
     dlg->registerSettingsPage(new ItemViewSettingsPage(dlg));
     dlg->registerSettingsPage(new BufferViewSettingsPage(dlg));
     dlg->registerSettingsPage(new InputWidgetSettingsPage(dlg));
     dlg->registerSettingsPage(new TopicWidgetSettingsPage(dlg));
+#ifdef HAVE_SONNET
+    dlg->registerSettingsPage(new SonnetSettingsPage(dlg));
+#endif
     dlg->registerSettingsPage(new HighlightSettingsPage(dlg));
+    dlg->registerSettingsPage(new CoreHighlightSettingsPage(dlg));
     dlg->registerSettingsPage(new NotificationsSettingsPage(dlg));
     dlg->registerSettingsPage(new BacklogSettingsPage(dlg));
 
@@ -1348,6 +1581,7 @@ void MainWin::showSettingsDlg()
     dlg->registerSettingsPage(new NetworksSettingsPage(dlg));
     dlg->registerSettingsPage(new AliasesSettingsPage(dlg));
     dlg->registerSettingsPage(new IgnoreListSettingsPage(dlg));
+    // dlg->registerSettingsPage(new DccSettingsPage(dlg)); not ready yet
 
     // Category: Remote Cores
     if (Quassel::runMode() != Quassel::Monolithic) {
@@ -1361,28 +1595,35 @@ void MainWin::showSettingsDlg()
 
 void MainWin::showAboutDlg()
 {
-    AboutDlg(this).exec();
+    AboutDlg{}.exec();
 }
 
 
 void MainWin::showShortcutsDlg()
 {
 #ifdef HAVE_KDE
-    KShortcutsDialog dlg(KShortcutsEditor::AllActions, KShortcutsEditor::LetterShortcutsDisallowed, this);
+    KShortcutsDialog dlg(KShortcutsEditor::AllActions, KShortcutsEditor::LetterShortcutsDisallowed);
     foreach(KActionCollection *coll, QtUi::actionCollections())
     dlg.addCollection(coll, coll->property("Category").toString());
-    dlg.exec();
+    dlg.configure(true);
 #else
-    SettingsPageDlg dlg(new ShortcutsSettingsPage(QtUi::actionCollections(), this), this);
-    dlg.exec();
+    SettingsPageDlg{new ShortcutsSettingsPage{QtUi::actionCollections()}}.exec();
 #endif
 }
 
 
-void MainWin::showNewTransferDlg(const ClientTransfer *transfer)
+void MainWin::showNewTransferDlg(const QUuid &transferId)
 {
-    ReceiveFileDlg *dlg = new ReceiveFileDlg(transfer, this);
-    dlg->show();
+    auto transfer = Client::transferManager()->transfer(transferId);
+    if (transfer) {
+        if (transfer->status() == Transfer::Status::New) {
+            ReceiveFileDlg *dlg = new ReceiveFileDlg(transfer, this);
+            dlg->show();
+        }
+    }
+    else {
+        qWarning() << "Unknown transfer ID" << transferId;
+    }
 }
 
 
@@ -1391,15 +1632,10 @@ void MainWin::onFullScreenToggled()
     // Relying on QWidget::isFullScreen is discouraged, see the KToggleFullScreenAction docs
     // Also, one should not use showFullScreen() or showNormal(), as those reset all other window flags
 
-    QAction *action = QtUi::actionCollection("General")->action("ToggleFullScreen");
-    if (!action)
-        return;
-
 #ifdef HAVE_KDE
-    KToggleFullScreenAction *kAct = static_cast<KToggleFullScreenAction *>(action);
-    kAct->setFullScreen(this, kAct->isChecked());
+    static_cast<KToggleFullScreenAction*>(_fullScreenAction)->setFullScreen(this, _fullScreenAction->isChecked());
 #else
-    if (action->isChecked())
+    if (_fullScreenAction->isChecked())
         setWindowState(windowState() | Qt::WindowFullScreen);
     else
         setWindowState(windowState() & ~Qt::WindowFullScreen);
@@ -1452,13 +1688,20 @@ void MainWin::closeEvent(QCloseEvent *event)
     QtUiSettings s;
     QtUiApplication *app = qobject_cast<QtUiApplication *> qApp;
     Q_ASSERT(app);
-    if (!app->isAboutToQuit() && QtUi::haveSystemTray() && s.value("MinimizeOnClose").toBool()) {
+    // On OSX it can happen that the closeEvent occurs twice. (Especially if packaged with Frameworks)
+    // This messes up MainWinState/MainWinHidden save/restore.
+    // It's a bug in Qt: https://bugreports.qt.io/browse/QTBUG-43344
+    if (!_aboutToQuit && !app->isAboutToQuit() && QtUi::haveSystemTray() && s.value("MinimizeOnClose").toBool()) {
         QtUi::hideMainWidget();
         event->ignore();
     }
-    else {
+    else if(!_aboutToQuit) {
+        _aboutToQuit = true;
         event->accept();
-        quit();
+        Quassel::instance()->quit();
+    }
+    else {
+        event->ignore();
     }
 }
 
@@ -1562,7 +1805,7 @@ void MainWin::clientNetworkUpdated()
 
     switch (net->connectionState()) {
     case Network::Initialized:
-        action->setIcon(SmallIcon("network-connect"));
+        action->setIcon(icon::get("network-connect"));
         // if we have no currently selected buffer, jump to the first connecting statusbuffer
         if (!bufferWidget()->currentBuffer().isValid()) {
             QModelIndex idx = Client::networkModel()->networkIndex(net->networkId());
@@ -1573,10 +1816,10 @@ void MainWin::clientNetworkUpdated()
         }
         break;
     case Network::Disconnected:
-        action->setIcon(SmallIcon("network-disconnect"));
+        action->setIcon(icon::get("network-disconnect"));
         break;
     default:
-        action->setIcon(SmallIcon("network-wired"));
+        action->setIcon(icon::get("network-wired"));
     }
 }
 
@@ -1602,14 +1845,76 @@ void MainWin::connectOrDisconnectFromNet()
 }
 
 
+void MainWin::on_inputFormatApplyColor_triggered()
+{
+    if (!_inputWidget)
+        return;
+
+    _inputWidget->applyFormatActiveColor();
+}
+
+
+void MainWin::on_inputFormatApplyColorFill_triggered()
+{
+    if (!_inputWidget)
+        return;
+
+    _inputWidget->applyFormatActiveColorFill();
+}
+
+
+void MainWin::on_inputFormatClear_triggered()
+{
+    if (!_inputWidget)
+        return;
+
+    _inputWidget->clearFormat();
+}
+
+
+void MainWin::on_inputFormatBold_triggered()
+{
+    if (!_inputWidget)
+        return;
+
+    _inputWidget->toggleFormatBold();
+}
+
+
+void MainWin::on_inputFormatItalic_triggered()
+{
+    if (!_inputWidget)
+        return;
+
+    _inputWidget->toggleFormatItalic();
+}
+
+
+void MainWin::on_inputFormatUnderline_triggered()
+{
+    if (!_inputWidget)
+        return;
+
+    _inputWidget->toggleFormatUnderline();
+}
+
+
 void MainWin::on_jumpHotBuffer_triggered()
 {
     if (!_bufferHotList->rowCount())
         return;
 
-    QModelIndex topIndex = _bufferHotList->index(0, 0);
-    BufferId bufferId = _bufferHotList->data(topIndex, NetworkModel::BufferIdRole).value<BufferId>();
-    Client::bufferModel()->switchToBuffer(bufferId);
+    Client::bufferModel()->switchToBuffer(_bufferHotList->hottestBuffer());
+}
+
+void MainWin::on_bufferSearch_triggered()
+{
+    if (_activeBufferViewIndex < 0 || _activeBufferViewIndex >= _bufferViews.count()) {
+        qWarning() << "Tried to activate filter on invalid bufferview:" << _activeBufferViewIndex;
+        return;
+    }
+
+    _bufferViews[_activeBufferViewIndex]->activateFilter();
 }
 
 
@@ -1660,6 +1965,9 @@ void MainWin::on_actionDebugNetworkModel_triggered()
 
 void MainWin::on_actionDebugHotList_triggered()
 {
+    _bufferHotList->invalidate();
+    _bufferHotList->sort(0, Qt::DescendingOrder);
+
     QTreeView *view = new QTreeView;
     view->setAttribute(Qt::WA_DeleteOnClose);
     view->setModel(_bufferHotList);
@@ -1690,8 +1998,8 @@ void MainWin::on_actionDebugMessageModel_triggered()
 
 void MainWin::on_actionDebugLog_triggered()
 {
-    DebugLogWidget *logWidget = new DebugLogWidget(0);
-    logWidget->show();
+    auto dlg = new DebugLogDlg(this);
+    dlg->show();
 }