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"
40 #include "iconutils.h"
43 QHash<QString, QAction*>* globalActions();
46 static const QString recentKeywordsKey = "recentKeywords";
47 static const QString recentChannelsKey = "recentChannels";
48 static const int PADDING = 30;
50 SearchView::SearchView(QWidget *parent) : View(parent) {
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);
58 QBoxLayout *mainLayout = new QVBoxLayout(this);
59 mainLayout->setMargin(PADDING);
60 mainLayout->setSpacing(0);
62 // hidden message widget
63 message = new QLabel(this);
65 mainLayout->addWidget(message);
67 mainLayout->addStretch();
69 QBoxLayout *hLayout = new QHBoxLayout();
70 hLayout->setAlignment(Qt::AlignCenter);
71 mainLayout->addLayout(hLayout);
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);
78 QVBoxLayout *layout = new QVBoxLayout();
79 layout->setAlignment(Qt::AlignCenter);
80 hLayout->addLayout(layout);
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)
89 welcomeLabel->setOpenExternalLinks(true);
90 welcomeLabel->setProperty("heading", true);
92 QFont f = welcomeLabel->font();
93 f.setFamily("Helvetica Neue");
94 f.setStyleName("Thin");
95 welcomeLabel->setFont(f);
97 QFont f = welcomeLabel->font();
98 f.setFamily("Segoe UI Light");
99 welcomeLabel->setFont(f);
101 layout->addWidget(welcomeLabel);
103 layout->addSpacing(PADDING / 2);
105 QBoxLayout *tipLayout = new QHBoxLayout();
106 tipLayout->setSpacing(10);
109 const QFont &biggerFont = FontUtils::big();
112 //: "Enter", as in "type". The whole phrase says: "Enter a keyword to start watching videos"
113 QLabel *tipLabel = new QLabel(tr("Enter"), this);
115 tipLabel->setFont(biggerFont);
117 tipLayout->addWidget(tipLabel);
119 typeCombo = new QComboBox(this);
120 typeCombo->addItem(tr("a keyword"));
121 typeCombo->addItem(tr("a channel"));
123 typeCombo->setFont(biggerFont);
125 connect(typeCombo, SIGNAL(currentIndexChanged(int)), SLOT(searchTypeChanged(int)));
126 tipLayout->addWidget(typeCombo);
128 tipLabel = new QLabel(tr("to start watching videos."), this);
130 tipLabel->setFont(biggerFont);
132 tipLayout->addWidget(tipLabel);
133 layout->addLayout(tipLayout);
135 layout->addSpacing(PADDING / 2);
137 QHBoxLayout *searchLayout = new QHBoxLayout();
138 searchLayout->setAlignment(Qt::AlignVCenter);
140 #ifdef APP_MAC_SEARCHFIELD
141 queryEdit = new SearchLineEditMac(this);
143 queryEdit = new SearchLineEdit(this);
144 queryEdit->toWidget()->setFont(biggerFont);
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*)));
152 youtubeSuggest = new YTSuggester(this);
153 channelSuggest = new ChannelSuggest(this);
154 searchTypeChanged(0);
156 searchLayout->addWidget(queryEdit->toWidget());
157 searchLayout->addSpacing(10);
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);
166 layout->addItem(searchLayout);
168 layout->addSpacing(PADDING / 2);
170 QHBoxLayout *otherLayout = new QHBoxLayout();
171 otherLayout->setMargin(0);
172 otherLayout->setSpacing(10);
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);
183 otherLayout->addLayout(recentKeywordsLayout);
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);
195 otherLayout->addLayout(recentChannelsLayout);
197 layout->addLayout(otherLayout);
199 mainLayout->addStretch();
201 #ifdef APP_ACTIVATION
202 if (!Activation::instance().isActivated())
203 mainLayout->addWidget(Extra::buyButton(tr("Get the full version")), 0, Qt::AlignRight);
207 void SearchView::appear() {
208 MainWindow::instance()->showActionInStatusBar(The::globalActions()->value("definition"), true);
210 updateRecentKeywords();
211 updateRecentChannels();
212 queryEdit->selectAll();
213 queryEdit->enableSuggest();
215 if (!queryEdit->toWidget()->hasFocus()) queryEdit->toWidget()->setFocus();
218 void SearchView::disappear() {
219 MainWindow::instance()->showActionInStatusBar(The::globalActions()->value("definition"), false);
222 void SearchView::updateRecentKeywords() {
225 QStringList keywords = settings.value(recentKeywordsKey).toStringList();
226 if (keywords == recentKeywords) return;
227 recentKeywords = keywords;
231 while ((item = recentKeywordsLayout->takeAt(1)) != 0) {
232 item->widget()->close();
236 recentKeywordsLabel->setVisible(!keywords.isEmpty());
237 The::globalActions()->value("clearRecentKeywords")->setEnabled(!keywords.isEmpty());
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);
249 bool needStatusTip = false;
250 if (display.length() > 24) {
251 display.truncate(24);
252 display.append("...");
253 needStatusTip = true;
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);
265 itemLabel->setStatusTip(link);
266 connect(itemLabel, SIGNAL(linkActivated(QString)), this, SLOT(watchKeywords(QString)));
267 recentKeywordsLayout->addWidget(itemLabel);
272 void SearchView::updateRecentChannels() {
275 QStringList keywords = settings.value(recentChannelsKey).toStringList();
276 if (keywords == recentChannels) return;
277 recentChannels = keywords;
281 while ((item = recentChannelsLayout->takeAt(1)) != 0) {
282 item->widget()->close();
286 recentChannelsLabel->setVisible(!keywords.isEmpty());
287 // TODO The::globalActions()->value("clearRecentKeywords")->setEnabled(!keywords.isEmpty());
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);
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);
307 connect(itemLabel, SIGNAL(linkActivated(QString)), this, SLOT(watchChannel(QString)));
308 recentChannelsLayout->addWidget(itemLabel);
313 void SearchView::watch() {
314 QString query = queryEdit->text();
318 void SearchView::textChanged(const QString &text) {
319 watchButton->setEnabled(!text.simplified().isEmpty());
322 void SearchView::watch(const QString &query) {
323 QString q = query.simplified();
325 // check for empty query
326 if (q.length() == 0) {
327 queryEdit->toWidget()->setFocus(Qt::OtherFocusReason);
331 SearchParams *searchParams = new SearchParams();
332 if (typeCombo->currentIndex() == 0)
333 searchParams->setKeywords(q);
335 // remove spaces from channel name
337 searchParams->setChannelId(q);
338 searchParams->setSortBy(SearchParams::SortByNewest);
342 emit search(searchParams);
345 void SearchView::watchChannel(const QString &channelId) {
346 if (channelId.length() == 0) {
347 queryEdit->toWidget()->setFocus(Qt::OtherFocusReason);
351 QString id = channelId;
354 if (!id.startsWith("UC")) id = "UC" + id;
356 SearchParams *searchParams = new SearchParams();
357 searchParams->setChannelId(id);
358 searchParams->setSortBy(SearchParams::SortByNewest);
361 emit search(searchParams);
364 void SearchView::watchKeywords(const QString &query) {
365 QString q = query.simplified();
367 // check for empty query
368 if (query.length() == 0) {
369 queryEdit->toWidget()->setFocus(Qt::OtherFocusReason);
373 if (typeCombo->currentIndex() == 0) {
374 queryEdit->setText(q);
375 watchButton->setEnabled(true);
378 SearchParams *searchParams = new SearchParams();
379 searchParams->setKeywords(q);
382 emit search(searchParams);
385 void SearchView::paintEvent(QPaintEvent *event) {
386 QWidget::paintEvent(event);
387 #if defined(APP_MAC) | defined(APP_WIN)
389 if (window()->isActiveWindow()) {
392 brush = palette().window();
394 QPainter painter(this);
395 painter.fillRect(0, 0, width(), height(), brush);
402 style()->drawPrimitive(QStyle::PE_Widget, &o, &p, this);
406 void SearchView::searchTypeChanged(int index) {
408 queryEdit->setSuggester(youtubeSuggest);
410 queryEdit->setSuggester(channelSuggest);
412 queryEdit->selectAll();
413 queryEdit->toWidget()->setFocus();
416 void SearchView::suggestionAccepted(Suggestion *suggestion) {
417 if (suggestion->type == QLatin1String("channel")) {
418 watchChannel(suggestion->userData);
419 } else watch(suggestion->value);