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