]> git.sur5r.net Git - minitube/blobdiff - src/searchview.cpp
New upstream version 3.8
[minitube] / src / searchview.cpp
index 6cbaa1d005dbbde8766aafc7f5e83699b1c012be..5315ca3e32632b9cef5b8531cac699d69678485d 100644 (file)
@@ -19,276 +19,360 @@ along with Minitube.  If not, see <http://www.gnu.org/licenses/>.
 $END_LICENSE */
 
 #include "searchview.h"
+#include "channelsuggest.h"
 #include "constants.h"
 #include "fontutils.h"
 #include "searchparams.h"
 #include "ytsuggester.h"
-#include "channelsuggest.h"
-#ifdef APP_MAC
+#ifdef APP_MAC_SEARCHFIELD
 #include "searchlineedit_mac.h"
 #else
 #include "searchlineedit.h"
 #endif
+#ifdef APP_MAC
+#include "macutils.h"
+#endif
 #ifdef APP_EXTRA
 #include "extra.h"
 #endif
 #ifdef APP_ACTIVATION
 #include "activation.h"
+#include "activationview.h"
 #endif
+#include "clickablelabel.h"
+#include "iconutils.h"
 #include "mainwindow.h"
+#include "messagebar.h"
 #include "painterutils.h"
 
-namespace The {
-QHash<QString, QAction*>* globalActions();
-}
-
-static const QString recentKeywordsKey = "recentKeywords";
-static const QString recentChannelsKey = "recentChannels";
-static const int PADDING = 30;
-
-SearchView::SearchView(QWidget *parent) : QWidget(parent) {
-
-    QFont biggerFont = FontUtils::big();
-    QFont smallerFont = FontUtils::smallBold();
-
-#if defined(APP_MAC) | defined(APP_WIN)
-    // speedup painting since we'll paint the whole background
-    // by ourselves anyway in paintEvent()
-    setAttribute(Qt::WA_OpaquePaintEvent);
+#ifdef UPDATER
+#include "updater.h"
 #endif
 
+namespace {
+const QString recentKeywordsKey = "recentKeywords";
+const QString recentChannelsKey = "recentChannels";
+} // namespace
+
+SearchView::SearchView(QWidget *parent) : View(parent) {
+    setBackgroundRole(QPalette::Base);
+    setForegroundRole(QPalette::Text);
     setAutoFillBackground(true);
 
-    QBoxLayout *mainLayout = new QVBoxLayout(this);
-    mainLayout->setMargin(PADDING);
-    mainLayout->setSpacing(0);
+    const int padding = 30;
+
+    QBoxLayout *vLayout = new QVBoxLayout(this);
+    vLayout->setMargin(padding);
+    vLayout->setSpacing(0);
 
-    // hidden message widget
-    message = new QLabel(this);
-    message->hide();
-    mainLayout->addWidget(message);
+    messageBar = new MessageBar();
+    messageBar->hide();
+    vLayout->addWidget(messageBar, 0, Qt::AlignCenter);
+    vLayout->addSpacing(padding);
+    maybeShowMessage();
 
-    mainLayout->addStretch();
+    vLayout->addStretch();
 
     QBoxLayout *hLayout = new QHBoxLayout();
     hLayout->setAlignment(Qt::AlignCenter);
-    mainLayout->addLayout(hLayout);
 
-    QLabel *logo = new QLabel(this);
-    logo->setPixmap(QPixmap(":/images/app.png"));
+    vLayout->addLayout(hLayout);
+
+    hLayout->addStretch();
+
+    logo = new ClickableLabel();
+    auto setLogoPixmap = [this] {
+        logo->setPixmap(IconUtils::pixmap(":/images/app.png", logo->devicePixelRatioF()));
+    };
+    setLogoPixmap();
+    connect(window()->windowHandle(), &QWindow::screenChanged, this, setLogoPixmap);
+    connect(logo, &ClickableLabel::clicked, MainWindow::instance(), &MainWindow::visitSite);
     hLayout->addWidget(logo, 0, Qt::AlignTop);
-    hLayout->addSpacing(PADDING);
+    hLayout->addSpacing(padding);
 
     QVBoxLayout *layout = new QVBoxLayout();
     layout->setAlignment(Qt::AlignCenter);
     hLayout->addLayout(layout);
 
-    QLabel *welcomeLabel =
-            new QLabel("<h1 style='font-weight:100'>" +
-                       tr("Welcome to <a href='%1'>%2</a>,")
-                       // .replace("<a ", "<a style='color:palette(text)'")
-                       .replace("<a ", "<a style='text-decoration:none; color:palette(text);font-weight:"
-                            #if defined(APP_UBUNTU) || defined(APP_WIN)
-                                "normal"
-                            #else
-                                "bold"
-                            #endif
-                                "' ")
-                       .arg(Constants::WEBSITE, Constants::NAME)
-                       + "</h1>", this);
+    QLabel *welcomeLabel = new QLabel();
+    auto setupWelcomeLabel = [this, welcomeLabel] {
+        QColor titleColor = palette().color(QPalette::WindowText);
+        titleColor.setAlphaF(.75);
+        int r, g, b, a;
+        titleColor.getRgb(&r, &g, &b, &a);
+        QString cssColor = QString::asprintf("rgba(%d,%d,%d,%d)", r, g, b, a);
+        QString text =
+                QString("<h1 style='font-weight:300;color:%1'>").arg(cssColor) +
+                tr("Welcome to <a href='%1'>%2</a>,")
+                        .replace("<a ", "<a style='text-decoration:none; color:palette(text)' ")
+                        .arg(Constants::WEBSITE, Constants::NAME) +
+                "</h1>";
+        welcomeLabel->setText(text);
+    };
+    setupWelcomeLabel();
+    connect(qApp, &QGuiApplication::paletteChanged, this, setupWelcomeLabel);
     welcomeLabel->setOpenExternalLinks(true);
-#ifdef APP_WIN
-    QFont f = welcomeLabel->font();
-    f.setHintingPreference(QFont::PreferNoHinting);
-    f.setFamily("Segoe UI Light");
-    welcomeLabel->setFont(f);
-#endif
+    welcomeLabel->setFont(FontUtils::light(welcomeLabel->font().pointSize()));
     layout->addWidget(welcomeLabel);
 
-    layout->addSpacing(PADDING / 2);
-
-    QBoxLayout *tipLayout = new QHBoxLayout();
-    tipLayout->setSpacing(10);
+    layout->addSpacing(padding / 2);
 
     //: "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos"
-    QLabel *tipLabel = new QLabel(tr("Enter"), this);
-    tipLabel->setFont(biggerFont);
-    tipLayout->addWidget(tipLabel);
-
-    typeCombo = new QComboBox(this);
-    typeCombo->addItem(tr("a keyword"));
-    typeCombo->addItem(tr("a channel"));
-    typeCombo->setFont(biggerFont);
-    connect(typeCombo, SIGNAL(currentIndexChanged(int)), SLOT(searchTypeChanged(int)));
-    tipLayout->addWidget(typeCombo);
-
-    tipLabel = new QLabel(tr("to start watching videos."), this);
-    tipLabel->setFont(biggerFont);
-    tipLayout->addWidget(tipLabel);
-    layout->addLayout(tipLayout);
+    // QLabel *tipLabel = new QLabel(tr("Enter"), this);
+    QString tip;
+    if (qApp->layoutDirection() == Qt::RightToLeft) {
+        tip = tr("to start watching videos.") + " " + tr("a keyword") + " " + tr("Enter");
+    } else {
+        tip = tr("Enter") + " " + tr("a keyword") + " " + tr("to start watching videos.");
+    }
 
-    layout->addSpacing(PADDING / 2);
+    layout->addSpacing(padding / 2);
 
-    QHBoxLayout *searchLayout = new QHBoxLayout();
+    QBoxLayout *searchLayout = new QHBoxLayout();
     searchLayout->setAlignment(Qt::AlignVCenter);
 
-    queryEdit = new SearchLineEdit(this);
-    queryEdit->setFont(biggerFont);
-    queryEdit->setMinimumWidth(queryEdit->fontInfo().pixelSize()*15);
-    connect(queryEdit, SIGNAL(search(const QString&)), SLOT(watch(const QString&)));
-    connect(queryEdit, SIGNAL(textChanged(const QString &)), SLOT(textChanged(const QString &)));
-    connect(queryEdit, SIGNAL(suggestionAccepted(const QString&)), SLOT(watch(const QString&)));
+#ifdef APP_MAC_SEARCHFIELD
+    SearchLineEditMac *slem = new SearchLineEditMac(this);
+    queryEdit = slem;
+    setFocusProxy(slem);
+#else
+    SearchLineEdit *sle = new SearchLineEdit(this);
+    sle->setFont(FontUtils::medium());
+    int tipWidth = sle->fontMetrics().size(Qt::TextSingleLine, tip).width();
+    sle->setMinimumWidth(tipWidth + sle->fontMetrics().width('m') * 6);
+    sle->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
+    queryEdit = sle;
+#endif
+
+    connect(queryEdit->toWidget(), SIGNAL(search(const QString &)), SLOT(watch(const QString &)));
+    connect(queryEdit->toWidget(), SIGNAL(textChanged(const QString &)),
+            SLOT(textChanged(const QString &)));
+    connect(queryEdit->toWidget(), SIGNAL(textEdited(const QString &)),
+            SLOT(textChanged(const QString &)));
+    connect(queryEdit->toWidget(), SIGNAL(suggestionAccepted(Suggestion *)),
+            SLOT(suggestionAccepted(Suggestion *)));
+    queryEdit->setPlaceholderText(tip);
 
     youtubeSuggest = new YTSuggester(this);
     channelSuggest = new ChannelSuggest(this);
+    connect(channelSuggest, SIGNAL(ready(QVector<Suggestion *>)),
+            SLOT(onChannelSuggestions(QVector<Suggestion *>)));
     searchTypeChanged(0);
 
-    searchLayout->addWidget(queryEdit);
-    searchLayout->addSpacing(10);
+    searchLayout->addWidget(queryEdit->toWidget(), 0, Qt::AlignBaseline);
 
-    watchButton = new QPushButton(tr("Watch"), this);
-    watchButton->setDefault(true);
-    watchButton->setEnabled(false);
-    watchButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
-    connect(watchButton, SIGNAL(clicked()), this, SLOT(watch()));
-    searchLayout->addWidget(watchButton);
+    layout->addLayout(searchLayout);
 
-    layout->addItem(searchLayout);
+    layout->addSpacing(padding);
 
-    layout->addSpacing(PADDING / 2);
-
-    QHBoxLayout *otherLayout = new QHBoxLayout();
-    otherLayout->setMargin(0);
-    otherLayout->setSpacing(10);
+    QHBoxLayout *recentLayout = new QHBoxLayout();
+    recentLayout->setMargin(0);
+    recentLayout->setSpacing(10);
 
     recentKeywordsLayout = new QVBoxLayout();
-    recentKeywordsLayout->setSpacing(5);
+    recentKeywordsLayout->setMargin(0);
+    recentKeywordsLayout->setSpacing(0);
     recentKeywordsLayout->setAlignment(Qt::AlignTop | Qt::AlignLeft);
-    recentKeywordsLabel = new QLabel(tr("Recent keywords").toUpper(), this);
+    recentKeywordsLabel = new QLabel(tr("Recent keywords"));
     recentKeywordsLabel->setProperty("recentHeader", true);
-    recentKeywordsLabel->setForegroundRole(QPalette::Dark);
     recentKeywordsLabel->hide();
-    recentKeywordsLabel->setFont(smallerFont);
+    recentKeywordsLabel->setEnabled(false);
     recentKeywordsLayout->addWidget(recentKeywordsLabel);
+    recentLayout->addLayout(recentKeywordsLayout);
 
-    otherLayout->addLayout(recentKeywordsLayout);
-
-    // recent channels
     recentChannelsLayout = new QVBoxLayout();
-    recentChannelsLayout->setSpacing(5);
+    recentChannelsLayout->setMargin(0);
+    recentChannelsLayout->setSpacing(0);
     recentChannelsLayout->setAlignment(Qt::AlignTop | Qt::AlignLeft);
-    recentChannelsLabel = new QLabel(tr("Recent channels").toUpper(), this);
+    recentChannelsLabel = new QLabel(tr("Recent channels"));
     recentChannelsLabel->setProperty("recentHeader", true);
-    recentChannelsLabel->setForegroundRole(QPalette::Dark);
     recentChannelsLabel->hide();
-    recentChannelsLabel->setFont(smallerFont);
+    recentChannelsLabel->setEnabled(false);
     recentChannelsLayout->addWidget(recentChannelsLabel);
+    recentLayout->addLayout(recentChannelsLayout);
 
-    otherLayout->addLayout(recentChannelsLayout);
+    layout->addLayout(recentLayout);
 
-    layout->addLayout(otherLayout);
+    hLayout->addStretch();
 
-    mainLayout->addStretch();
+    vLayout->addStretch();
 
 #ifdef APP_ACTIVATION
     if (!Activation::instance().isActivated())
-        mainLayout->addWidget(Extra::buyButton(tr("Get the full version")), 0, Qt::AlignRight);
+        vLayout->addWidget(ActivationView::buyButton(tr("Get the full version")), 0,
+                           Qt::AlignRight);
 #endif
 }
 
 void SearchView::appear() {
+    MainWindow *w = MainWindow::instance();
+    w->showActionsInStatusBar(
+            {w->getAction("manualplay"), w->getAction("safeSearch"), w->getAction("definition")},
+            true);
+
     updateRecentKeywords();
     updateRecentChannels();
+
     queryEdit->selectAll();
     queryEdit->enableSuggest();
-    QTimer::singleShot(0, queryEdit, SLOT(setFocus()));
+    QTimer::singleShot(0, queryEdit->toWidget(), SLOT(setFocus()));
+}
+
+void SearchView::disappear() {
+    MainWindow *w = MainWindow::instance();
+    w->showActionsInStatusBar(
+            {w->getAction("manualplay"), w->getAction("safeSearch"), w->getAction("definition")},
+            false);
 }
 
 void SearchView::updateRecentKeywords() {
+    // load
+    QSettings settings;
+    const QStringList keywords = settings.value(recentKeywordsKey).toStringList();
+    if (keywords == recentKeywords) return;
+    recentKeywords = keywords;
 
     // cleanup
     QLayoutItem *item;
-    while ((item = recentKeywordsLayout->takeAt(1)) != 0) {
+    while (recentKeywordsLayout->count() - 1 > recentKeywords.size() &&
+           (item = recentKeywordsLayout->takeAt(1)) != nullptr) {
         item->widget()->close();
         delete item;
     }
 
-    // load
-    QSettings settings;
-    QStringList keywords = settings.value(recentKeywordsKey).toStringList();
     recentKeywordsLabel->setVisible(!keywords.isEmpty());
-    The::globalActions()->value("clearRecentKeywords")->setEnabled(!keywords.isEmpty());
+    MainWindow::instance()->getAction("clearRecentKeywords")->setEnabled(!keywords.isEmpty());
+
+    const int maxDisplayLength = 25;
 
-    foreach (QString keyword, keywords) {
+#ifdef APP_MAC
+    QPalette p = palette();
+    p.setColor(QPalette::Highlight, mac::accentColor());
+#endif
+
+    int counter = 1;
+    for (const QString &keyword : keywords) {
         QString link = keyword;
         QString display = keyword;
-        if (keyword.startsWith("http://") || keyword.startsWith("https://")) {
-            int separator = keyword.indexOf("|");
+        if (keyword.startsWith(QLatin1String("http://")) ||
+            keyword.startsWith(QLatin1String("https://"))) {
+            int separator = keyword.indexOf('|');
             if (separator > 0 && separator + 1 < keyword.length()) {
                 link = keyword.left(separator);
-                display = keyword.mid(separator+1);
+                display = keyword.mid(separator + 1);
             }
         }
-        bool needStatusTip = false;
-        if (display.length() > 24) {
-            display.truncate(24);
-            display.append("...");
-            needStatusTip = true;
+        bool needStatusTip = display.length() > maxDisplayLength;
+        if (needStatusTip) {
+            display.truncate(maxDisplayLength);
+            display.append(QStringLiteral("\u2026"));
+        }
+
+        ClickableLabel *item;
+        if (recentKeywordsLayout->count() - 1 >= counter) {
+            item = qobject_cast<ClickableLabel *>(recentKeywordsLayout->itemAt(counter)->widget());
+        } else {
+            item = new ClickableLabel();
+#ifdef APP_MAC
+            item->setPalette(p);
+#endif
+            item->setAttribute(Qt::WA_DeleteOnClose);
+            item->setProperty("recentItem", true);
+            item->setFocusPolicy(Qt::TabFocus);
+            connect(item, &ClickableLabel::hovered, this, [item, this](bool value) {
+                item->setForegroundRole(value ? QPalette::Highlight : QPalette::WindowText);
+                if (value) {
+                    for (int i = 1; i < recentKeywordsLayout->count(); ++i) {
+                        QWidget *w = recentKeywordsLayout->itemAt(i)->widget();
+                        if (w != item) {
+                            w->setForegroundRole(QPalette::WindowText);
+                        }
+                    }
+                }
+            });
+            item->setContextMenuPolicy(Qt::ActionsContextMenu);
+            auto removeAction = new QAction(tr("Remove"));
+            item->addAction(removeAction);
+            connect(removeAction, &QAction::triggered, item, [item] {
+                QSettings settings;
+                QStringList keywords = settings.value(recentKeywordsKey).toStringList();
+                QString keyword = item->property("keyword").toString();
+                keywords.removeOne(keyword);
+                settings.setValue(recentKeywordsKey, keywords);
+                item->deleteLater();
+            });
+            recentKeywordsLayout->addWidget(item);
         }
-        QLabel *itemLabel = new QLabel("<a href=\"" + link
-                                       + "\" style=\"color:palette(text); text-decoration:none\">"
-                                       + display + "</a>", this);
-        itemLabel->setAttribute(Qt::WA_DeleteOnClose);
-        itemLabel->setProperty("recentItem", true);
-        itemLabel->setMaximumWidth(queryEdit->width() + watchButton->width());
-        // itemLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
-        // Make links navigable with the keyboard too
-        itemLabel->setTextInteractionFlags(Qt::LinksAccessibleByKeyboard | Qt::LinksAccessibleByMouse);
+
+        item->setText(display);
         if (needStatusTip)
-            itemLabel->setStatusTip(link);
-        connect(itemLabel, SIGNAL(linkActivated(QString)), this, SLOT(watchKeywords(QString)));
-        recentKeywordsLayout->addWidget(itemLabel);
-    }
+            item->setStatusTip(link);
+        else
+            item->setStatusTip(QString());
+        item->setProperty("keyword", keyword);
+
+        disconnect(item, &ClickableLabel::clicked, nullptr, nullptr);
+        connect(item, &ClickableLabel::clicked, this, [this, link]() { watchKeywords(link); });
 
+        counter++;
+    }
 }
 
 void SearchView::updateRecentChannels() {
+    // load
+    QSettings settings;
+    const QStringList keywords = settings.value(recentChannelsKey).toStringList();
+    if (keywords == recentChannels) return;
+    recentChannels = keywords;
 
     // cleanup
     QLayoutItem *item;
-    while ((item = recentChannelsLayout->takeAt(1)) != 0) {
+    while ((item = recentChannelsLayout->takeAt(1)) != nullptr) {
         item->widget()->close();
         delete item;
     }
 
-    // load
-    QSettings settings;
-    QStringList keywords = settings.value(recentChannelsKey).toStringList();
     recentChannelsLabel->setVisible(!keywords.isEmpty());
-    // TODO The::globalActions()->value("clearRecentKeywords")->setEnabled(!keywords.isEmpty());
 
-    foreach (QString keyword, keywords) {
+#ifdef APP_MAC
+    QPalette p = palette();
+    p.setColor(QPalette::Highlight, mac::accentColor());
+#endif
+
+    for (const QString &keyword : keywords) {
         QString link = keyword;
         QString display = keyword;
         int separator = keyword.indexOf('|');
         if (separator > 0 && separator + 1 < keyword.length()) {
             link = keyword.left(separator);
-            display = keyword.mid(separator+1);
+            display = keyword.mid(separator + 1);
         }
-        QLabel *itemLabel = new QLabel("<a href=\"" + link
-                                       + "\" style=\"color:palette(text); text-decoration:none\">"
-                                       + display + "</a>", this);
-        itemLabel->setAttribute(Qt::WA_DeleteOnClose);
-        itemLabel->setProperty("recentItem", true);
-        itemLabel->setMaximumWidth(queryEdit->width() + watchButton->width());
-        // itemLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
-        // Make links navigable with the keyboard too
-        itemLabel->setTextInteractionFlags(Qt::LinksAccessibleByKeyboard | Qt::LinksAccessibleByMouse);
-
-        connect(itemLabel, SIGNAL(linkActivated(QString)), this, SLOT(watchChannel(QString)));
-        recentChannelsLayout->addWidget(itemLabel);
-    }
 
+        ClickableLabel *item = new ClickableLabel(display);
+        item->setProperty("keyword", keyword);
+#ifdef APP_MAC
+        item->setPalette(p);
+#endif
+        item->setAttribute(Qt::WA_DeleteOnClose);
+        item->setProperty("recentItem", true);
+        item->setFocusPolicy(Qt::TabFocus);
+        connect(item, &ClickableLabel::clicked, [this, link]() { watchChannel(link); });
+        connect(item, &ClickableLabel::hovered, item, [item](bool value) {
+            item->setForegroundRole(value ? QPalette::Highlight : QPalette::WindowText);
+        });
+        item->setContextMenuPolicy(Qt::ActionsContextMenu);
+        auto removeAction = new QAction(tr("Remove"));
+        item->addAction(removeAction);
+        connect(removeAction, &QAction::triggered, item, [item] {
+            QSettings settings;
+            QStringList keywords = settings.value(recentChannelsKey).toStringList();
+            QString keyword = item->property("keyword").toString();
+            keywords.removeOne(keyword);
+            settings.setValue(recentChannelsKey, keywords);
+            item->deleteLater();
+        });
+        recentChannelsLayout->addWidget(item);
+    }
 }
 
 void SearchView::watch() {
@@ -297,98 +381,59 @@ void SearchView::watch() {
 }
 
 void SearchView::textChanged(const QString &text) {
-    watchButton->setEnabled(!text.simplified().isEmpty());
+    lastChannelSuggestions.clear();
 }
 
-void SearchView::watch(QString query) {
-
-    query = query.simplified();
+void SearchView::watch(const QString &query) {
+    QString q = query.simplified();
 
     // check for empty query
-    if (query.length() == 0) {
-        queryEdit->setFocus(Qt::OtherFocusReason);
+    if (q.isEmpty()) {
+        queryEdit->toWidget()->setFocus(Qt::OtherFocusReason);
         return;
     }
 
     SearchParams *searchParams = new SearchParams();
-    if (typeCombo->currentIndex() == 0)
-        searchParams->setKeywords(query);
-    else {
-        // remove spaces from channel name
-        query = query.simplified();
-        searchParams->setAuthor(query);
-        searchParams->setSortBy(SearchParams::SortByNewest);
-    }
+    searchParams->setKeywords(q);
 
     // go!
     emit search(searchParams);
 }
 
-void SearchView::watchChannel(QString channel) {
-
-    channel = channel.simplified();
-
-    // check for empty query
-    if (channel.length() == 0) {
-        queryEdit->setFocus(Qt::OtherFocusReason);
+void SearchView::watchChannel(const QString &channelId) {
+    if (channelId.isEmpty()) {
+        queryEdit->toWidget()->setFocus(Qt::OtherFocusReason);
         return;
     }
 
-    // remove spaces from channel name
-    channel = channel.remove(" ");
-
+    QString id = channelId;
+    qDebug() << "Channel" << id;
     SearchParams *searchParams = new SearchParams();
-    searchParams->setAuthor(channel);
+    searchParams->setChannelId(id);
     searchParams->setSortBy(SearchParams::SortByNewest);
 
     // go!
     emit search(searchParams);
 }
 
-void SearchView::watchKeywords(QString query) {
-
-    query = query.simplified();
+void SearchView::watchKeywords(const QString &query) {
+    QString q = query.simplified();
 
     // check for empty query
-    if (query.length() == 0) {
-        queryEdit->setFocus(Qt::OtherFocusReason);
+    if (q.isEmpty()) {
+        queryEdit->toWidget()->setFocus(Qt::OtherFocusReason);
         return;
     }
 
-    if (typeCombo->currentIndex() == 0) {
-        queryEdit->setText(query);
-        watchButton->setEnabled(true);
-    }
+    queryEdit->setText(q);
 
     SearchParams *searchParams = new SearchParams();
-    searchParams->setKeywords(query);
+    searchParams->setKeywords(q);
 
     // go!
     emit search(searchParams);
 }
 
-void SearchView::paintEvent(QPaintEvent *event) {
-    QWidget::paintEvent(event);
-#if defined(APP_MAC) | defined(APP_WIN)
-    QBrush brush;
-    if (window()->isActiveWindow()) {
-        brush = QBrush(QColor(0xdd, 0xe4, 0xeb));
-    } else {
-        brush = palette().window();
-    }
-    QPainter painter(this);
-    painter.fillRect(0, 0, width(), height(), brush);
-    painter.end();
-#endif
-#ifdef APP_UBUNTU
-    QStyleOption o;
-    o.initFrom(this);
-    QPainter p(this);
-    style()->drawPrimitive(QStyle::PE_Widget, &o, &p, this);
-#endif
-    // PainterUtils::topShadow(this);
-}
-
 void SearchView::searchTypeChanged(int index) {
     if (index == 0) {
         queryEdit->setSuggester(youtubeSuggest);
@@ -396,5 +441,89 @@ void SearchView::searchTypeChanged(int index) {
         queryEdit->setSuggester(channelSuggest);
     }
     queryEdit->selectAll();
-    queryEdit->setFocus();
+    queryEdit->toWidget()->setFocus();
+}
+
+void SearchView::suggestionAccepted(Suggestion *suggestion) {
+    if (suggestion->type == QLatin1String("channel")) {
+        watchChannel(suggestion->userData);
+    } else
+        watch(suggestion->value);
+}
+
+void SearchView::onChannelSuggestions(const QVector<Suggestion *> &suggestions) {
+    lastChannelSuggestions = suggestions;
+}
+
+void SearchView::maybeShowMessage() {
+    QSettings settings;
+    QString key;
+
+    bool showMessages = true;
+#ifdef APP_ACTIVATION
+    showMessages = Activation::instance().isActivated();
+#endif
+
+#if defined APP_MAC && !defined APP_MAC_STORE
+    if (showMessages && !settings.contains(key = "sofa")) {
+        QString msg = tr("Need a remote control for %1? Try %2!").arg(Constants::NAME).arg("Sofa");
+        msg = "<a href='https://" + QLatin1String(Constants::ORG_DOMAIN) + '/' + key +
+              "' style='text-decoration:none;color:palette(windowText)'>" + msg + "</a>";
+        messageBar->setMessage(msg);
+        messageBar->setOpenExternalLinks(true);
+        disconnect(messageBar);
+        connect(messageBar, &MessageBar::closed, this, [key] {
+            QSettings settings;
+            settings.setValue(key, true);
+        });
+        messageBar->show();
+        showMessages = false;
+    }
+#endif
+
+    if (showMessages) {
+        key = "donate" + QLatin1String(Constants::VERSION);
+        if (!settings.contains(key)) {
+            bool oneYearUsage = true;
+#ifdef APP_ACTIVATION
+            oneYearUsage = (QDateTime::currentSecsSinceEpoch() -
+                            Activation::instance().getLicenseTimestamp()) > 86400 * 365;
+#elif APP_MAC_STORE
+            oneYearUsage = false;
+#endif
+            if (oneYearUsage) {
+                QString msg =
+                        tr("I keep improving %1 to make it the best I can. Support this work!")
+                                .arg(Constants::NAME);
+                msg = "<a href='https://" + QLatin1String(Constants::ORG_DOMAIN) + "/donate" +
+                      "' style='text-decoration:none;color:palette(windowText)'>" + msg + "</a>";
+                messageBar->setMessage(msg);
+                messageBar->setOpenExternalLinks(true);
+                disconnect(messageBar);
+                connect(messageBar, &MessageBar::closed, this, [key] {
+                    QSettings settings;
+                    settings.setValue(key, true);
+                });
+                messageBar->show();
+            }
+        }
+    }
+
+#ifdef UPDATER
+    connect(&Updater::instance(), &Updater::statusChanged, this, [this](auto status) {
+        if (status == Updater::Status::UpdateDownloaded) {
+            QString msg = tr("An update is ready to be installed. Quit and install update.");
+            msg = "<a href='http://quit' style='text-decoration:none;color:palette(windowText)'>" +
+                  msg + "</a>";
+            messageBar->setMessage(msg);
+            messageBar->setOpenExternalLinks(false);
+            disconnect(messageBar);
+            connect(messageBar, &MessageBar::linkActivated, this, [] {
+                Updater::instance().setRelaunchAfterInstall(true);
+                qApp->quit();
+            });
+            messageBar->show();
+        }
+    });
+#endif
 }