3 This file is part of Minitube.
4 Copyright 2009, Flavio Tordini <flavio.tordini@gmail.com>
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.
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.
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/>.
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"
30 #include "searchlineedit.h"
36 #include "activation.h"
38 #include "mainwindow.h"
39 #include "painterutils.h"
42 QHash<QString, QAction*>* globalActions();
45 static const QString recentKeywordsKey = "recentKeywords";
46 static const QString recentChannelsKey = "recentChannels";
47 static const int PADDING = 30;
49 SearchView::SearchView(QWidget *parent) : QWidget(parent) {
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);
57 QBoxLayout *mainLayout = new QVBoxLayout(this);
58 mainLayout->setMargin(PADDING);
59 mainLayout->setSpacing(0);
61 // hidden message widget
62 message = new QLabel(this);
64 mainLayout->addWidget(message);
66 mainLayout->addStretch();
68 QBoxLayout *hLayout = new QHBoxLayout();
69 hLayout->setAlignment(Qt::AlignCenter);
70 mainLayout->addLayout(hLayout);
72 QLabel *logo = new QLabel(this);
73 logo->setPixmap(QPixmap(":/images/app.png"));
74 hLayout->addWidget(logo, 0, Qt::AlignTop);
75 hLayout->addSpacing(PADDING);
77 QVBoxLayout *layout = new QVBoxLayout();
78 layout->setAlignment(Qt::AlignCenter);
79 hLayout->addLayout(layout);
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)
88 welcomeLabel->setOpenExternalLinks(true);
89 welcomeLabel->setProperty("heading", true);
91 QFont f = welcomeLabel->font();
92 f.setFamily("Helvetica Neue");
93 f.setStyleName("Thin");
94 welcomeLabel->setFont(f);
96 QFont f = welcomeLabel->font();
97 f.setFamily("Segoe UI Light");
98 welcomeLabel->setFont(f);
100 layout->addWidget(welcomeLabel);
102 layout->addSpacing(PADDING / 2);
104 QBoxLayout *tipLayout = new QHBoxLayout();
105 tipLayout->setSpacing(10);
107 //: "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos"
108 QLabel *tipLabel = new QLabel(tr("Enter"), this);
110 const QFont &biggerFont = FontUtils::big();
111 tipLabel->setFont(biggerFont);
113 tipLayout->addWidget(tipLabel);
115 typeCombo = new QComboBox(this);
116 typeCombo->addItem(tr("a keyword"));
117 typeCombo->addItem(tr("a channel"));
119 typeCombo->setFont(biggerFont);
121 connect(typeCombo, SIGNAL(currentIndexChanged(int)), SLOT(searchTypeChanged(int)));
122 tipLayout->addWidget(typeCombo);
124 tipLabel = new QLabel(tr("to start watching videos."), this);
126 tipLabel->setFont(biggerFont);
128 tipLayout->addWidget(tipLabel);
129 layout->addLayout(tipLayout);
131 layout->addSpacing(PADDING / 2);
133 QHBoxLayout *searchLayout = new QHBoxLayout();
134 searchLayout->setAlignment(Qt::AlignVCenter);
136 queryEdit = new SearchLineEdit(this);
137 #ifndef APP_MAC_SEARCHFIELD
138 queryEdit->setFont(biggerFont);
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*)));
144 youtubeSuggest = new YTSuggester(this);
145 channelSuggest = new ChannelSuggest(this);
146 searchTypeChanged(0);
148 searchLayout->addWidget(queryEdit);
149 searchLayout->addSpacing(10);
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);
158 layout->addItem(searchLayout);
160 layout->addSpacing(PADDING / 2);
162 QHBoxLayout *otherLayout = new QHBoxLayout();
163 otherLayout->setMargin(0);
164 otherLayout->setSpacing(10);
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);
175 otherLayout->addLayout(recentKeywordsLayout);
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);
187 otherLayout->addLayout(recentChannelsLayout);
189 layout->addLayout(otherLayout);
191 mainLayout->addStretch();
193 #ifdef APP_ACTIVATION
194 if (!Activation::instance().isActivated())
195 mainLayout->addWidget(Extra::buyButton(tr("Get the full version")), 0, Qt::AlignRight);
199 void SearchView::appear() {
200 setUpdatesEnabled(false);
201 updateRecentKeywords();
202 updateRecentChannels();
203 queryEdit->selectAll();
204 queryEdit->enableSuggest();
205 setUpdatesEnabled(true);
207 MainWindow::instance()->showActionInStatusBar(The::globalActions()->value("definition"), true);
209 if (!queryEdit->hasFocus()) queryEdit->setFocus();
212 void SearchView::disappear() {
213 MainWindow::instance()->showActionInStatusBar(The::globalActions()->value("definition"), false);
216 void SearchView::updateRecentKeywords() {
219 QStringList keywords = settings.value(recentKeywordsKey).toStringList();
220 if (keywords == recentKeywords) return;
221 recentKeywords = keywords;
225 while ((item = recentKeywordsLayout->takeAt(1)) != 0) {
226 item->widget()->close();
230 recentKeywordsLabel->setVisible(!keywords.isEmpty());
231 The::globalActions()->value("clearRecentKeywords")->setEnabled(!keywords.isEmpty());
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);
243 bool needStatusTip = false;
244 if (display.length() > 24) {
245 display.truncate(24);
246 display.append("...");
247 needStatusTip = true;
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);
259 itemLabel->setStatusTip(link);
260 connect(itemLabel, SIGNAL(linkActivated(QString)), this, SLOT(watchKeywords(QString)));
261 recentKeywordsLayout->addWidget(itemLabel);
266 void SearchView::updateRecentChannels() {
269 QStringList keywords = settings.value(recentChannelsKey).toStringList();
270 if (keywords == recentChannels) return;
271 recentChannels = keywords;
275 while ((item = recentChannelsLayout->takeAt(1)) != 0) {
276 item->widget()->close();
280 recentChannelsLabel->setVisible(!keywords.isEmpty());
281 // TODO The::globalActions()->value("clearRecentKeywords")->setEnabled(!keywords.isEmpty());
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);
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);
301 connect(itemLabel, SIGNAL(linkActivated(QString)), this, SLOT(watchChannel(QString)));
302 recentChannelsLayout->addWidget(itemLabel);
307 void SearchView::watch() {
308 QString query = queryEdit->text();
312 void SearchView::textChanged(const QString &text) {
313 watchButton->setEnabled(!text.simplified().isEmpty());
316 void SearchView::watch(const QString &query) {
317 QString q = query.simplified();
319 // check for empty query
320 if (q.length() == 0) {
321 queryEdit->setFocus(Qt::OtherFocusReason);
325 SearchParams *searchParams = new SearchParams();
326 if (typeCombo->currentIndex() == 0)
327 searchParams->setKeywords(q);
329 // remove spaces from channel name
331 searchParams->setChannelId(q);
332 searchParams->setSortBy(SearchParams::SortByNewest);
336 emit search(searchParams);
339 void SearchView::watchChannel(const QString &channelId) {
340 if (channelId.length() == 0) {
341 queryEdit->setFocus(Qt::OtherFocusReason);
345 QString id = channelId;
348 if (!id.startsWith("UC")) id = "UC" + id;
350 SearchParams *searchParams = new SearchParams();
351 searchParams->setChannelId(id);
352 searchParams->setSortBy(SearchParams::SortByNewest);
355 emit search(searchParams);
358 void SearchView::watchKeywords(const QString &query) {
359 QString q = query.simplified();
361 // check for empty query
362 if (query.length() == 0) {
363 queryEdit->setFocus(Qt::OtherFocusReason);
367 if (typeCombo->currentIndex() == 0) {
368 queryEdit->setText(q);
369 watchButton->setEnabled(true);
372 SearchParams *searchParams = new SearchParams();
373 searchParams->setKeywords(q);
376 emit search(searchParams);
379 void SearchView::paintEvent(QPaintEvent *event) {
380 QWidget::paintEvent(event);
381 #if defined(APP_MAC) | defined(APP_WIN)
383 if (window()->isActiveWindow()) {
386 brush = palette().window();
388 QPainter painter(this);
389 painter.fillRect(0, 0, width(), height(), brush);
396 style()->drawPrimitive(QStyle::PE_Widget, &o, &p, this);
400 void SearchView::searchTypeChanged(int index) {
402 queryEdit->setSuggester(youtubeSuggest);
404 queryEdit->setSuggester(channelSuggest);
406 queryEdit->selectAll();
407 queryEdit->setFocus();
410 void SearchView::suggestionAccepted(Suggestion *suggestion) {
411 if (suggestion->type == QLatin1String("channel")) {
412 watchChannel(suggestion->userData);
413 } else watch(suggestion->value);