]> git.sur5r.net Git - minitube/blob - src/searchview.cpp
f72751bbafa9e50abcef4af389b8ebeedaed5561
[minitube] / src / searchview.cpp
1 /* $BEGIN_LICENSE
2
3 This file is part of Minitube.
4 Copyright 2009, Flavio Tordini <flavio.tordini@gmail.com>
5
6 Minitube is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 Minitube is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Minitube.  If not, see <http://www.gnu.org/licenses/>.
18
19 $END_LICENSE */
20
21 #include "searchview.h"
22 #include "constants.h"
23 #include "fontutils.h"
24 #include "searchparams.h"
25 #include "ytsuggester.h"
26 #include "channelsuggest.h"
27 #ifdef APP_MAC_SEARCHFIELD
28 #include "searchlineedit_mac.h"
29 #else
30 #include "searchlineedit.h"
31 #endif
32 #ifdef APP_EXTRA
33 #include "extra.h"
34 #endif
35 #ifdef APP_ACTIVATION
36 #include "activation.h"
37 #endif
38 #include "mainwindow.h"
39 #include "painterutils.h"
40
41 namespace The {
42 QHash<QString, QAction*>* globalActions();
43 }
44
45 static const QString recentKeywordsKey = "recentKeywords";
46 static const QString recentChannelsKey = "recentChannels";
47 static const int PADDING = 30;
48
49 SearchView::SearchView(QWidget *parent) : QWidget(parent) {
50
51 #if defined(APP_MAC) | defined(APP_WIN)
52     // speedup painting since we'll paint the whole background
53     // by ourselves anyway in paintEvent()
54     setAttribute(Qt::WA_OpaquePaintEvent);
55 #endif
56
57     QBoxLayout *mainLayout = new QVBoxLayout(this);
58     mainLayout->setMargin(PADDING);
59     mainLayout->setSpacing(0);
60
61     // hidden message widget
62     message = new QLabel(this);
63     message->hide();
64     mainLayout->addWidget(message);
65
66     mainLayout->addStretch();
67
68     QBoxLayout *hLayout = new QHBoxLayout();
69     hLayout->setAlignment(Qt::AlignCenter);
70     mainLayout->addLayout(hLayout);
71
72     QLabel *logo = new QLabel(this);
73     logo->setPixmap(QPixmap(":/images/app.png"));
74     hLayout->addWidget(logo, 0, Qt::AlignTop);
75     hLayout->addSpacing(PADDING);
76
77     QVBoxLayout *layout = new QVBoxLayout();
78     layout->setAlignment(Qt::AlignCenter);
79     hLayout->addLayout(layout);
80
81     QLabel *welcomeLabel =
82             new QLabel("<h1 style='font-weight:100'>" +
83                        tr("Welcome to <a href='%1'>%2</a>,")
84                        // .replace("<a ", "<a style='color:palette(text)'")
85                        .replace("<a ", "<a style='text-decoration:none; color:palette(text);font-weight:normal' ")
86                        .arg(Constants::WEBSITE, Constants::NAME)
87                        + "</h1>", this);
88     welcomeLabel->setOpenExternalLinks(true);
89     welcomeLabel->setProperty("heading", true);
90 #ifdef APP_MAC
91     QFont f = welcomeLabel->font();
92     f.setFamily("Helvetica Neue");
93     f.setStyleName("Thin");
94     welcomeLabel->setFont(f);
95 #elif APP_WIN
96     QFont f = welcomeLabel->font();
97     f.setFamily("Segoe UI Light");
98     welcomeLabel->setFont(f);
99 #endif
100     layout->addWidget(welcomeLabel);
101
102     layout->addSpacing(PADDING / 2);
103
104     QBoxLayout *tipLayout = new QHBoxLayout();
105     tipLayout->setSpacing(10);
106
107     //: "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos"
108     QLabel *tipLabel = new QLabel(tr("Enter"), this);
109 #ifndef APP_MAC
110     const QFont &biggerFont = FontUtils::big();
111     tipLabel->setFont(biggerFont);
112 #endif
113     tipLayout->addWidget(tipLabel);
114
115     typeCombo = new QComboBox(this);
116     typeCombo->addItem(tr("a keyword"));
117     typeCombo->addItem(tr("a channel"));
118 #ifndef APP_MAC
119     typeCombo->setFont(biggerFont);
120 #endif
121     connect(typeCombo, SIGNAL(currentIndexChanged(int)), SLOT(searchTypeChanged(int)));
122     tipLayout->addWidget(typeCombo);
123
124     tipLabel = new QLabel(tr("to start watching videos."), this);
125 #ifndef APP_MAC
126     tipLabel->setFont(biggerFont);
127 #endif
128     tipLayout->addWidget(tipLabel);
129     layout->addLayout(tipLayout);
130
131     layout->addSpacing(PADDING / 2);
132
133     QHBoxLayout *searchLayout = new QHBoxLayout();
134     searchLayout->setAlignment(Qt::AlignVCenter);
135
136     queryEdit = new SearchLineEdit(this);
137 #ifndef APP_MAC_SEARCHFIELD
138     queryEdit->setFont(biggerFont);
139 #endif
140     connect(queryEdit, SIGNAL(search(const QString&)), SLOT(watch(const QString&)));
141     connect(queryEdit, SIGNAL(textEdited(const QString &)), SLOT(textChanged(const QString &)));
142     connect(queryEdit, SIGNAL(suggestionAccepted(Suggestion*)), SLOT(suggestionAccepted(Suggestion*)));
143
144     youtubeSuggest = new YTSuggester(this);
145     channelSuggest = new ChannelSuggest(this);
146     searchTypeChanged(0);
147
148     searchLayout->addWidget(queryEdit);
149     searchLayout->addSpacing(10);
150
151     watchButton = new QPushButton(tr("Watch"), this);
152     watchButton->setDefault(true);
153     watchButton->setEnabled(false);
154     watchButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
155     connect(watchButton, SIGNAL(clicked()), this, SLOT(watch()));
156     searchLayout->addWidget(watchButton);
157
158     layout->addItem(searchLayout);
159
160     layout->addSpacing(PADDING / 2);
161
162     QHBoxLayout *otherLayout = new QHBoxLayout();
163     otherLayout->setMargin(0);
164     otherLayout->setSpacing(10);
165
166     recentKeywordsLayout = new QVBoxLayout();
167     recentKeywordsLayout->setSpacing(5);
168     recentKeywordsLayout->setAlignment(Qt::AlignTop | Qt::AlignLeft);
169     recentKeywordsLabel = new QLabel(tr("Recent keywords"), this);
170     recentKeywordsLabel->setProperty("recentHeader", true);
171     recentKeywordsLabel->setForegroundRole(QPalette::Dark);
172     recentKeywordsLabel->hide();
173     recentKeywordsLayout->addWidget(recentKeywordsLabel);
174
175     otherLayout->addLayout(recentKeywordsLayout);
176
177     // recent channels
178     recentChannelsLayout = new QVBoxLayout();
179     recentChannelsLayout->setSpacing(5);
180     recentChannelsLayout->setAlignment(Qt::AlignTop | Qt::AlignLeft);
181     recentChannelsLabel = new QLabel(tr("Recent channels"), this);
182     recentChannelsLabel->setProperty("recentHeader", true);
183     recentChannelsLabel->setForegroundRole(QPalette::Dark);
184     recentChannelsLabel->hide();
185     recentChannelsLayout->addWidget(recentChannelsLabel);
186
187     otherLayout->addLayout(recentChannelsLayout);
188
189     layout->addLayout(otherLayout);
190
191     mainLayout->addStretch();
192
193 #ifdef APP_ACTIVATION
194     if (!Activation::instance().isActivated())
195         mainLayout->addWidget(Extra::buyButton(tr("Get the full version")), 0, Qt::AlignRight);
196 #endif
197 }
198
199 void SearchView::appear() {
200     setUpdatesEnabled(false);
201     updateRecentKeywords();
202     updateRecentChannels();
203     queryEdit->selectAll();
204     queryEdit->enableSuggest();
205     setUpdatesEnabled(true);
206
207     MainWindow::instance()->showActionInStatusBar(The::globalActions()->value("definition"), true);
208
209     if (!queryEdit->hasFocus()) queryEdit->setFocus();
210 }
211
212 void SearchView::disappear() {
213     MainWindow::instance()->showActionInStatusBar(The::globalActions()->value("definition"), false);
214 }
215
216 void SearchView::updateRecentKeywords() {
217     // load
218     QSettings settings;
219     QStringList keywords = settings.value(recentKeywordsKey).toStringList();
220     if (keywords == recentKeywords) return;
221     recentKeywords = keywords;
222
223     // cleanup
224     QLayoutItem *item;
225     while ((item = recentKeywordsLayout->takeAt(1)) != 0) {
226         item->widget()->close();
227         delete item;
228     }
229
230     recentKeywordsLabel->setVisible(!keywords.isEmpty());
231     The::globalActions()->value("clearRecentKeywords")->setEnabled(!keywords.isEmpty());
232
233     foreach (const QString &keyword, keywords) {
234         QString link = keyword;
235         QString display = keyword;
236         if (keyword.startsWith("http://") || keyword.startsWith("https://")) {
237             int separator = keyword.indexOf("|");
238             if (separator > 0 && separator + 1 < keyword.length()) {
239                 link = keyword.left(separator);
240                 display = keyword.mid(separator+1);
241             }
242         }
243         bool needStatusTip = false;
244         if (display.length() > 24) {
245             display.truncate(24);
246             display.append("...");
247             needStatusTip = true;
248         }
249         QLabel *itemLabel = new QLabel("<a href=\"" + link
250                                        + "\" style=\"color:palette(text); text-decoration:none\">"
251                                        + display + "</a>", this);
252         itemLabel->setAttribute(Qt::WA_DeleteOnClose);
253         itemLabel->setProperty("recentItem", true);
254         itemLabel->setMaximumWidth(queryEdit->width() + watchButton->width());
255         // itemLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
256         // Make links navigable with the keyboard too
257         itemLabel->setTextInteractionFlags(Qt::LinksAccessibleByKeyboard | Qt::LinksAccessibleByMouse);
258         if (needStatusTip)
259             itemLabel->setStatusTip(link);
260         connect(itemLabel, SIGNAL(linkActivated(QString)), this, SLOT(watchKeywords(QString)));
261         recentKeywordsLayout->addWidget(itemLabel);
262     }
263
264 }
265
266 void SearchView::updateRecentChannels() {
267     // load
268     QSettings settings;
269     QStringList keywords = settings.value(recentChannelsKey).toStringList();
270     if (keywords == recentChannels) return;
271     recentChannels = keywords;
272
273     // cleanup
274     QLayoutItem *item;
275     while ((item = recentChannelsLayout->takeAt(1)) != 0) {
276         item->widget()->close();
277         delete item;
278     }
279
280     recentChannelsLabel->setVisible(!keywords.isEmpty());
281     // TODO The::globalActions()->value("clearRecentKeywords")->setEnabled(!keywords.isEmpty());
282
283     foreach (const QString &keyword, keywords) {
284         QString link = keyword;
285         QString display = keyword;
286         int separator = keyword.indexOf('|');
287         if (separator > 0 && separator + 1 < keyword.length()) {
288             link = keyword.left(separator);
289             display = keyword.mid(separator+1);
290         }
291         QLabel *itemLabel = new QLabel("<a href=\"" + link
292                                        + "\" style=\"color:palette(text); text-decoration:none\">"
293                                        + display + "</a>", this);
294         itemLabel->setAttribute(Qt::WA_DeleteOnClose);
295         itemLabel->setProperty("recentItem", true);
296         itemLabel->setMaximumWidth(queryEdit->width() + watchButton->width());
297         // itemLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
298         // Make links navigable with the keyboard too
299         itemLabel->setTextInteractionFlags(Qt::LinksAccessibleByKeyboard | Qt::LinksAccessibleByMouse);
300
301         connect(itemLabel, SIGNAL(linkActivated(QString)), this, SLOT(watchChannel(QString)));
302         recentChannelsLayout->addWidget(itemLabel);
303     }
304
305 }
306
307 void SearchView::watch() {
308     QString query = queryEdit->text();
309     watch(query);
310 }
311
312 void SearchView::textChanged(const QString &text) {
313     watchButton->setEnabled(!text.simplified().isEmpty());
314 }
315
316 void SearchView::watch(const QString &query) {
317     QString q = query.simplified();
318
319     // check for empty query
320     if (q.length() == 0) {
321         queryEdit->setFocus(Qt::OtherFocusReason);
322         return;
323     }
324
325     SearchParams *searchParams = new SearchParams();
326     if (typeCombo->currentIndex() == 0)
327         searchParams->setKeywords(q);
328     else {
329         // remove spaces from channel name
330         q.remove(' ');
331         searchParams->setChannelId(q);
332         searchParams->setSortBy(SearchParams::SortByNewest);
333     }
334
335     // go!
336     emit search(searchParams);
337 }
338
339 void SearchView::watchChannel(const QString &channelId) {
340     if (channelId.length() == 0) {
341         queryEdit->setFocus(Qt::OtherFocusReason);
342         return;
343     }
344
345     QString id = channelId;
346
347     // Fix old settings
348     if (!id.startsWith("UC")) id = "UC" + id;
349
350     SearchParams *searchParams = new SearchParams();
351     searchParams->setChannelId(id);
352     searchParams->setSortBy(SearchParams::SortByNewest);
353
354     // go!
355     emit search(searchParams);
356 }
357
358 void SearchView::watchKeywords(const QString &query) {
359     QString q = query.simplified();
360
361     // check for empty query
362     if (query.length() == 0) {
363         queryEdit->setFocus(Qt::OtherFocusReason);
364         return;
365     }
366
367     if (typeCombo->currentIndex() == 0) {
368         queryEdit->setText(q);
369         watchButton->setEnabled(true);
370     }
371
372     SearchParams *searchParams = new SearchParams();
373     searchParams->setKeywords(q);
374
375     // go!
376     emit search(searchParams);
377 }
378
379 void SearchView::paintEvent(QPaintEvent *event) {
380     QWidget::paintEvent(event);
381 #if defined(APP_MAC) | defined(APP_WIN)
382     QBrush brush;
383     if (window()->isActiveWindow()) {
384         brush = Qt::white;
385     } else {
386         brush = palette().window();
387     }
388     QPainter painter(this);
389     painter.fillRect(0, 0, width(), height(), brush);
390     painter.end();
391 #endif
392 #ifdef APP_UBUNTU
393     QStyleOption o;
394     o.initFrom(this);
395     QPainter p(this);
396     style()->drawPrimitive(QStyle::PE_Widget, &o, &p, this);
397 #endif
398 }
399
400 void SearchView::searchTypeChanged(int index) {
401     if (index == 0) {
402         queryEdit->setSuggester(youtubeSuggest);
403     } else {
404         queryEdit->setSuggester(channelSuggest);
405     }
406     queryEdit->selectAll();
407     queryEdit->setFocus();
408 }
409
410 void SearchView::suggestionAccepted(Suggestion *suggestion) {
411     if (suggestion->type == QLatin1String("channel")) {
412         watchChannel(suggestion->userData);
413     } else watch(suggestion->value);
414 }