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"
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 QFont biggerFont = FontUtils::big();
52 QFont smallerFont = FontUtils::smallBold();
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);
60 setAutoFillBackground(true);
62 QBoxLayout *mainLayout = new QVBoxLayout(this);
63 mainLayout->setMargin(PADDING);
64 mainLayout->setSpacing(0);
66 // hidden message widget
67 message = new QLabel(this);
69 mainLayout->addWidget(message);
71 mainLayout->addStretch();
73 QBoxLayout *hLayout = new QHBoxLayout();
74 hLayout->setAlignment(Qt::AlignCenter);
75 mainLayout->addLayout(hLayout);
77 QLabel *logo = new QLabel(this);
78 logo->setPixmap(QPixmap(":/images/app.png"));
79 hLayout->addWidget(logo, 0, Qt::AlignTop);
80 hLayout->addSpacing(PADDING);
82 QVBoxLayout *layout = new QVBoxLayout();
83 layout->setAlignment(Qt::AlignCenter);
84 hLayout->addLayout(layout);
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)
97 .arg(Constants::WEBSITE, Constants::NAME)
99 welcomeLabel->setOpenExternalLinks(true);
101 QFont f = welcomeLabel->font();
102 f.setHintingPreference(QFont::PreferNoHinting);
103 f.setFamily("Segoe UI Light");
104 welcomeLabel->setFont(f);
106 layout->addWidget(welcomeLabel);
108 layout->addSpacing(PADDING / 2);
110 QBoxLayout *tipLayout = new QHBoxLayout();
111 tipLayout->setSpacing(10);
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);
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);
125 tipLabel = new QLabel(tr("to start watching videos."), this);
126 tipLabel->setFont(biggerFont);
127 tipLayout->addWidget(tipLabel);
128 layout->addLayout(tipLayout);
130 layout->addSpacing(PADDING / 2);
132 QHBoxLayout *searchLayout = new QHBoxLayout();
133 searchLayout->setAlignment(Qt::AlignVCenter);
135 queryEdit = new SearchLineEdit(this);
137 queryEdit->setFont(biggerFont);
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*)));
143 youtubeSuggest = new YTSuggester(this);
144 channelSuggest = new ChannelSuggest(this);
145 searchTypeChanged(0);
147 searchLayout->addWidget(queryEdit);
148 searchLayout->addSpacing(10);
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);
157 layout->addItem(searchLayout);
159 layout->addSpacing(PADDING / 2);
161 QHBoxLayout *otherLayout = new QHBoxLayout();
162 otherLayout->setMargin(0);
163 otherLayout->setSpacing(10);
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);
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").toUpper(), this);
182 recentChannelsLabel->setProperty("recentHeader", true);
183 recentChannelsLabel->setForegroundRole(QPalette::Dark);
184 recentChannelsLabel->hide();
185 recentChannelsLabel->setFont(smallerFont);
186 recentChannelsLayout->addWidget(recentChannelsLabel);
188 otherLayout->addLayout(recentChannelsLayout);
190 layout->addLayout(otherLayout);
192 mainLayout->addStretch();
194 #ifdef APP_ACTIVATION
195 if (!Activation::instance().isActivated())
196 mainLayout->addWidget(Extra::buyButton(tr("Get the full version")), 0, Qt::AlignRight);
200 void SearchView::appear() {
201 updateRecentKeywords();
202 updateRecentChannels();
203 queryEdit->selectAll();
204 queryEdit->enableSuggest();
205 if (!queryEdit->hasFocus())
206 QTimer::singleShot(10, queryEdit, SLOT(setFocus()));
209 void SearchView::updateRecentKeywords() {
213 while ((item = recentKeywordsLayout->takeAt(1)) != 0) {
214 item->widget()->close();
220 QStringList keywords = settings.value(recentKeywordsKey).toStringList();
221 recentKeywordsLabel->setVisible(!keywords.isEmpty());
222 The::globalActions()->value("clearRecentKeywords")->setEnabled(!keywords.isEmpty());
224 foreach (QString keyword, keywords) {
225 QString link = keyword;
226 QString display = keyword;
227 if (keyword.startsWith("http://") || keyword.startsWith("https://")) {
228 int separator = keyword.indexOf("|");
229 if (separator > 0 && separator + 1 < keyword.length()) {
230 link = keyword.left(separator);
231 display = keyword.mid(separator+1);
234 bool needStatusTip = false;
235 if (display.length() > 24) {
236 display.truncate(24);
237 display.append("...");
238 needStatusTip = true;
240 QLabel *itemLabel = new QLabel("<a href=\"" + link
241 + "\" style=\"color:palette(text); text-decoration:none\">"
242 + display + "</a>", this);
243 itemLabel->setAttribute(Qt::WA_DeleteOnClose);
244 itemLabel->setProperty("recentItem", true);
245 itemLabel->setMaximumWidth(queryEdit->width() + watchButton->width());
246 // itemLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
247 // Make links navigable with the keyboard too
248 itemLabel->setTextInteractionFlags(Qt::LinksAccessibleByKeyboard | Qt::LinksAccessibleByMouse);
250 itemLabel->setStatusTip(link);
251 connect(itemLabel, SIGNAL(linkActivated(QString)), this, SLOT(watchKeywords(QString)));
252 recentKeywordsLayout->addWidget(itemLabel);
257 void SearchView::updateRecentChannels() {
261 while ((item = recentChannelsLayout->takeAt(1)) != 0) {
262 item->widget()->close();
268 QStringList keywords = settings.value(recentChannelsKey).toStringList();
269 recentChannelsLabel->setVisible(!keywords.isEmpty());
270 // TODO The::globalActions()->value("clearRecentKeywords")->setEnabled(!keywords.isEmpty());
272 foreach (QString keyword, keywords) {
273 QString link = keyword;
274 QString display = keyword;
275 int separator = keyword.indexOf('|');
276 if (separator > 0 && separator + 1 < keyword.length()) {
277 link = keyword.left(separator);
278 display = keyword.mid(separator+1);
280 QLabel *itemLabel = new QLabel("<a href=\"" + link
281 + "\" style=\"color:palette(text); text-decoration:none\">"
282 + display + "</a>", this);
283 itemLabel->setAttribute(Qt::WA_DeleteOnClose);
284 itemLabel->setProperty("recentItem", true);
285 itemLabel->setMaximumWidth(queryEdit->width() + watchButton->width());
286 // itemLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
287 // Make links navigable with the keyboard too
288 itemLabel->setTextInteractionFlags(Qt::LinksAccessibleByKeyboard | Qt::LinksAccessibleByMouse);
290 connect(itemLabel, SIGNAL(linkActivated(QString)), this, SLOT(watchChannel(QString)));
291 recentChannelsLayout->addWidget(itemLabel);
296 void SearchView::watch() {
297 QString query = queryEdit->text();
301 void SearchView::textChanged(const QString &text) {
302 watchButton->setEnabled(!text.simplified().isEmpty());
305 void SearchView::watch(QString query) {
307 query = query.simplified();
309 // check for empty query
310 if (query.length() == 0) {
311 queryEdit->setFocus(Qt::OtherFocusReason);
315 SearchParams *searchParams = new SearchParams();
316 if (typeCombo->currentIndex() == 0)
317 searchParams->setKeywords(query);
319 // remove spaces from channel name
320 query = query.simplified();
321 searchParams->setAuthor(query);
322 searchParams->setSortBy(SearchParams::SortByNewest);
326 emit search(searchParams);
329 void SearchView::watchChannel(QString channel) {
331 channel = channel.simplified();
333 // check for empty query
334 if (channel.length() == 0) {
335 queryEdit->setFocus(Qt::OtherFocusReason);
339 // remove spaces from channel name
340 channel = channel.remove(" ");
342 SearchParams *searchParams = new SearchParams();
343 searchParams->setAuthor(channel);
344 searchParams->setSortBy(SearchParams::SortByNewest);
347 emit search(searchParams);
350 void SearchView::watchKeywords(QString query) {
352 query = query.simplified();
354 // check for empty query
355 if (query.length() == 0) {
356 queryEdit->setFocus(Qt::OtherFocusReason);
360 if (typeCombo->currentIndex() == 0) {
361 queryEdit->setText(query);
362 watchButton->setEnabled(true);
365 SearchParams *searchParams = new SearchParams();
366 searchParams->setKeywords(query);
369 emit search(searchParams);
372 void SearchView::paintEvent(QPaintEvent *event) {
373 QWidget::paintEvent(event);
374 #if defined(APP_MAC) | defined(APP_WIN)
376 if (window()->isActiveWindow()) {
377 brush = QBrush(QColor(0xdd, 0xe4, 0xeb));
379 brush = palette().window();
381 QPainter painter(this);
382 painter.fillRect(0, 0, width(), height(), brush);
389 style()->drawPrimitive(QStyle::PE_Widget, &o, &p, this);
391 PainterUtils::topShadow(this);
394 void SearchView::searchTypeChanged(int index) {
396 queryEdit->setSuggester(youtubeSuggest);
398 queryEdit->setSuggester(channelSuggest);
400 queryEdit->selectAll();
401 queryEdit->setFocus();
404 void SearchView::suggestionAccepted(Suggestion *suggestion) {
405 watch(suggestion->value);