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