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