qtui: Set default window icon independent of connection state
[quassel.git] / src / qtui / mainwin.cpp
index e27c2fe..49c3d3d 100644 (file)
@@ -1,5 +1,5 @@
 /***************************************************************************
- *   Copyright (C) 2005-2015 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  *
@@ -24,6 +24,7 @@
 #include <QMenuBar>
 #include <QMessageBox>
 #include <QStatusBar>
+#include <QTableView>
 #include <QToolBar>
 #include <QInputDialog>
 
 #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
 #else /* HAVE_KDE */
 #  include "knotificationbackend.h"
 #endif /* HAVE_KDE */
+#include "systrayanimationnotificationbackend.h"
 
 
 #ifdef HAVE_LIBSNORE
 #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"
@@ -169,7 +178,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
 
@@ -183,6 +193,8 @@ MainWin::MainWin(QWidget *parent)
 
     setWindowTitle("Quassel IRC");
     setWindowIconText("Quassel IRC");
+    // Set the default icon for all windows
+    QApplication::setWindowIcon(QIcon::fromTheme("quassel"));
     updateIcon();
 }
 
@@ -194,9 +206,13 @@ void MainWin::init()
     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(Client::instance(), SIGNAL(showChannelList(NetworkId)), SLOT(showChannelList(NetworkId)));
+    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::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 *)));
@@ -216,10 +232,11 @@ void MainWin::init()
     setupActions();
     setupBufferWidget();
     setupMenus();
+    // setupTransferWidget(); not ready yet
+    setupChatMonitor();
     setupTopicWidget();
-    setupNickWidget();
     setupInputWidget();
-    setupChatMonitor();
+    setupNickWidget();
     setupViewMenuTail();
     setupStatusBar();
     setupToolBars();
@@ -227,7 +244,13 @@ 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
@@ -237,6 +260,9 @@ void MainWin::init()
 #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)
@@ -347,6 +373,15 @@ 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()
 {
@@ -354,9 +389,8 @@ void MainWin::updateIcon()
     if (Client::isConnected())
         icon = QIcon::fromTheme("quassel", QIcon(":/icons/quassel-128.png"));
     else
-        icon = QIcon::fromTheme("quassel-inactive", QIcon(":/icons/quassel-128.png"));
+        icon = QIcon::fromTheme("inactive-quassel", QIcon(":/icons/inactive-quassel.png"));
     setWindowIcon(icon);
-    qApp->setWindowIcon(icon);
 }
 
 
@@ -364,9 +398,9 @@ void MainWin::setupActions()
 {
     ActionCollection *coll = QtUi::actionCollection("General", tr("General"));
     // File
-    coll->addAction("ConnectCore", new Action(QIcon::fromTheme("network-connect"), tr("&Connect to Core..."), coll,
+    coll->addAction("ConnectCore", new Action(QIcon::fromTheme("connect-quassel", QIcon(":/icons/connect-quassel.png")), tr("&Connect to Core..."), coll,
             this, SLOT(showCoreConnectionDlg())));
-    coll->addAction("DisconnectCore", new Action(QIcon::fromTheme("network-disconnect"), tr("&Disconnect from Core"), coll,
+    coll->addAction("DisconnectCore", new Action(QIcon::fromTheme("disconnect-quassel", QIcon(":/icons/disconnect-quassel.png")), tr("&Disconnect from Core"), coll,
             Client::instance(), SLOT(disconnectFromCore())));
     coll->addAction("ChangePassword", new Action(QIcon::fromTheme("dialog-password"), tr("Change &Password..."), coll,
             this, SLOT(showPasswordChangeDlg())));
@@ -374,7 +408,11 @@ void MainWin::setupActions()
             this, SLOT(showCoreInfoDlg())));
     coll->addAction("ConfigureNetworks", new Action(QIcon::fromTheme("configure"), tr("Configure &Networks..."), coll,
             this, SLOT(on_actionConfigureNetworks_triggered())));
-    // FIXME: use QKeySequence::Quit once we depend on Qt 4.6
+    // 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(QIcon::fromTheme("application-exit"), tr("&Quit"), coll,
             this, SLOT(quit()), Qt::CTRL + Qt::Key_Q));
 
@@ -391,7 +429,7 @@ void MainWin::setupActions()
     coll->addAction("ShowAwayLog", new Action(tr("Show Away Log"), coll,
             this, SLOT(showAwayLog())));
     coll->addAction("ToggleMenuBar", new Action(QIcon::fromTheme("show-menu"), tr("Show &Menubar"), coll,
-            0, 0, QKeySequence(Qt::CTRL + Qt::Key_M)))->setCheckable(true);
+            0, 0))->setCheckable(true);
 
     coll->addAction("ToggleStatusBar", new Action(tr("Show Status &Bar"), coll,
             0, 0))->setCheckable(true);
@@ -442,17 +480,52 @@ void MainWin::setupActions()
     coll->addAction("DebugLog", new Action(QIcon::fromTheme("tools-report-bug"), tr("Debug &Log"), coll,
             this, SLOT(on_actionDebugLog_triggered())));
     coll->addAction("ReloadStyle", new Action(QIcon::fromTheme("view-refresh"), tr("Reload Stylesheet"), coll,
-            QtUi::style(), SLOT(reload()), QKeySequence::Refresh));
+            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(
+                        QIcon::fromTheme("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(
+                        QIcon::fromTheme("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(
+                        QIcon::fromTheme("edit-clear"), tr("Clear formatting"), coll,
+                        this, SLOT(on_inputFormatClear_triggered()),
+                        QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_C)));
+
+    coll->addAction("FormatBold", new Action(
+                        QIcon::fromTheme("format-text-bold"), tr("Toggle bold"), coll,
+                        this, SLOT(on_inputFormatBold_triggered()),
+                        QKeySequence::Bold));
+
+    coll->addAction("FormatItalic", new Action(
+                        QIcon::fromTheme("format-text-italic"), tr("Toggle italics"), coll,
+                        this, SLOT(on_inputFormatItalic_triggered()),
+                        QKeySequence::Italic));
+
+    coll->addAction("FormatUnderline", new Action(
+                        QIcon::fromTheme("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;
@@ -627,6 +700,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
 
@@ -716,7 +791,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;
 }
 
 
@@ -725,9 +800,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;
     }
 
@@ -746,7 +820,7 @@ void MainWin::changeActiveBufferView(int bufferViewId)
 
 void MainWin::showPasswordChangeDlg()
 {
-    if((Client::coreFeatures() & Quassel::PasswordChange)) {
+    if(Client::isCoreFeatureEnabled(Quassel::Feature::PasswordChange)) {
         PasswordChangeDlg dlg(this);
         dlg.exec();
     }
@@ -760,11 +834,34 @@ void MainWin::showPasswordChangeDlg()
 }
 
 
+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::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;
@@ -855,11 +952,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);
 }
 
@@ -870,6 +982,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);
@@ -896,7 +1009,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();
@@ -923,8 +1035,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()));
@@ -949,6 +1059,27 @@ 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(QIcon::fromTheme("download"));
+    action->setShortcut(QKeySequence(Qt::Key_F6));
+    QtUi::actionCollection("General")->addAction("ShowTransferWidget", action);
+    _viewMenu->addAction(action);
+}
+
+
 void MainWin::setupViewMenuTail()
 {
     _viewMenu->addSeparator();
@@ -1042,9 +1173,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;
 
@@ -1072,7 +1225,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();
 }
@@ -1301,9 +1456,9 @@ 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();
 }
@@ -1325,6 +1480,15 @@ void MainWin::showChannelList(NetworkId netId)
 }
 
 
+void MainWin::showNetworkConfig(NetworkId netId)
+{
+    SettingsPageDlg dlg(new NetworksSettingsPage(this), this);
+    if (netId.isValid())
+        qobject_cast<NetworksSettingsPage *>(dlg.currentPage())->bufferList_Open(netId);
+    dlg.exec();
+}
+
+
 void MainWin::showIgnoreList(QString newRule)
 {
     SettingsPageDlg dlg(new IgnoreListSettingsPage(this), this);
@@ -1367,6 +1531,7 @@ void MainWin::showSettingsDlg()
     //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));
@@ -1376,6 +1541,7 @@ void MainWin::showSettingsDlg()
     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));
 
@@ -1385,6 +1551,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) {
@@ -1416,10 +1583,18 @@ void MainWin::showShortcutsDlg()
 }
 
 
-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;
+    }
 }
 
 
@@ -1484,14 +1659,21 @@ 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();
     }
+    else {
+        event->ignore();
+    }
 }
 
 
@@ -1634,6 +1816,60 @@ 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())
@@ -1642,6 +1878,16 @@ void MainWin::on_jumpHotBuffer_triggered()
     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();
+}
+
 
 void MainWin::onJumpKey()
 {