CONFIG += release
TEMPLATE = app
-VERSION = 1.3
+VERSION = 1.4
DEFINES += APP_VERSION="$$VERSION"
INCLUDEPATH += /usr/include/phonon
src/minisplitter.h \
src/loadingwidget.h \
src/videoareawidget.h \
- src/googlesuggest.h \
+ src/autocomplete.h \
src/videowidget.h \
src/videodefinition.h \
src/fontutils.h \
src/downloadview.h \
src/downloadmodel.h \
src/downloadlistview.h \
- src/downloadsettings.h
+ src/downloadsettings.h \
+ src/youtubesuggest.h \
+ src/suggester.h \
+ src/channelsuggest.h
SOURCES += src/main.cpp \
src/MainWindow.cpp \
src/SearchView.cpp \
src/minisplitter.cpp \
src/loadingwidget.cpp \
src/videoareawidget.cpp \
- src/googlesuggest.cpp \
+ src/autocomplete.cpp \
src/videowidget.cpp \
src/videodefinition.cpp \
src/constants.cpp \
src/downloadview.cpp \
src/downloadmodel.cpp \
src/downloadlistview.cpp \
- src/downloadsettings.cpp
+ src/downloadsettings.cpp \
+ src/youtubesuggest.cpp \
+ src/channelsuggest.cpp
RESOURCES += resources.qrc
DESTDIR = build/target/
OBJECTS_DIR = build/obj/
#define MAX_ITEMS 10
static const QString recentKeywordsKey = "recentKeywords";
+static const QString recentChannelsKey = "recentChannels";
ListModel::ListModel(QWidget *parent) : QAbstractListModel(parent) {
youtubeSearch = 0;
// save keyword
QString query = searchParams->keywords();
- if (query.startsWith("http://")) {
- // Save the video title
- query += "|" + videos.first()->title();
+ if (!query.isEmpty()) {
+ if (query.startsWith("http://")) {
+ // Save the video title
+ query += "|" + videos.first()->title();
+ }
+ QSettings settings;
+ QStringList keywords = settings.value(recentKeywordsKey).toStringList();
+ keywords.removeAll(query);
+ keywords.prepend(query);
+ while (keywords.size() > 10)
+ keywords.removeLast();
+ settings.setValue(recentKeywordsKey, keywords);
}
- QSettings settings;
- QStringList keywords = settings.value(recentKeywordsKey).toStringList();
- keywords.removeAll(query);
- keywords.prepend(query);
- while (keywords.size() > 10)
- keywords.removeLast();
- settings.setValue(recentKeywordsKey, keywords);
+
+ // save channel
+ QString channel = searchParams->author();
+ if (!channel.isEmpty()) {
+ QSettings settings;
+ QStringList channels = settings.value(recentChannelsKey).toStringList();
+ channels.removeAll(channel);
+ channels.prepend(channel);
+ while (channels.size() > 10)
+ channels.removeLast();
+ settings.setValue(recentChannelsKey, channels);
+ }
+
}
}
// #include "local/mac/mac_startup.h"
#endif
#include "downloadmanager.h"
+#include "youtubesuggest.h"
MainWindow::MainWindow() :
aboutView(0),
// views
searchView = new SearchView(this);
- connect(searchView, SIGNAL(search(QString)), this, SLOT(showMedia(QString)));
+ connect(searchView, SIGNAL(search(SearchParams*)), this, SLOT(showMedia(SearchParams*)));
views->addWidget(searchView);
mediaView = new MediaView(this);
toolbarSearch = new SearchLineEdit(this);
toolbarSearch->setMinimumWidth(toolbarSearch->fontInfo().pixelSize()*15);
- connect(toolbarSearch, SIGNAL(search(const QString&)), searchView, SLOT(watch(const QString&)));
+ toolbarSearch->setSuggester(new YouTubeSuggest(this));
+ connect(toolbarSearch, SIGNAL(search(const QString&)), this, SLOT(startToolbarSearch(const QString&)));
// build ui
createActions();
totalTime->clear();
}
-void MainWindow::showMedia(QString query) {
- SearchParams *searchParams = new SearchParams();
- searchParams->setKeywords(query);
+void MainWindow::showMedia(SearchParams *searchParams) {
mediaView->search(searchParams);
showWidget(mediaView);
}
if (show) showWidget(downloadView);
else goBack();
}
+
+void MainWindow::startToolbarSearch(QString query) {
+
+ query = query.trimmed();
+
+ // check for empty query
+ if (query.length() == 0) {
+ return;
+ }
+
+ SearchParams *searchParams = new SearchParams();
+ searchParams->setKeywords(query);
+
+ // go!
+ showMedia(searchParams);
+}
void fadeInWidget(QWidget *oldWidget, QWidget *newWidget);
void goBack();
void showSearch();
- void showMedia(QString query);
+ void showMedia(SearchParams *params);
void visitSite();
void donate();
void about();
void downloadsFinished();
void toggleDownloads(bool show);
+ void startToolbarSearch(QString query);
+
private:
void initPhonon();
void createActions();
#include "SearchView.h"
#include "constants.h"
#include "fontutils.h"
+#include "searchparams.h"
+#include "youtubesuggest.h"
+#include "channelsuggest.h"
namespace The {
QMap<QString, QAction*>* globalActions();
}
static const QString recentKeywordsKey = "recentKeywords";
+static const QString recentChannelsKey = "recentChannels";
static const int PADDING = 30;
SearchView::SearchView(QWidget *parent) : QWidget(parent) {
layout->addSpacing(PADDING / 2);
- QLabel *tipLabel = new QLabel(tr("Enter a keyword to start watching videos."), this);
+ QBoxLayout *tipLayout = new QHBoxLayout();
+ tipLayout->setSpacing(10);
+
+ QLabel *tipLabel = new QLabel(tr("Enter"), this);
+ tipLabel->setFont(biggerFont);
+ tipLayout->addWidget(tipLabel);
+
+ typeCombo = new QComboBox(this);
+ typeCombo->addItem(tr("a keyword"));
+ typeCombo->addItem(tr("a channel"));
+ typeCombo->setFont(biggerFont);
+ connect(typeCombo, SIGNAL(currentIndexChanged(int)), SLOT(searchTypeChanged(int)));
+ tipLayout->addWidget(typeCombo);
+
+ tipLabel = new QLabel(tr("to start watching videos."), this);
tipLabel->setFont(biggerFont);
- layout->addWidget(tipLabel);
+ tipLayout->addWidget(tipLabel);
+ layout->addLayout(tipLayout);
layout->addSpacing(PADDING / 2);
queryEdit->setFocus(Qt::OtherFocusReason);
connect(queryEdit, SIGNAL(search(const QString&)), this, SLOT(watch(const QString&)));
connect(queryEdit, SIGNAL(textChanged(const QString &)), this, SLOT(textChanged(const QString &)));
- searchLayout->addWidget(queryEdit);
+ youtubeSuggest = new YouTubeSuggest(this);
+ channelSuggest = new ChannelSuggest(this);
+ searchTypeChanged(0);
+
+ searchLayout->addWidget(queryEdit);
searchLayout->addSpacing(10);
watchButton = new QPushButton(tr("Watch"), this);
recentKeywordsLayout = new QVBoxLayout();
recentKeywordsLayout->setSpacing(5);
- recentKeywordsLayout->setAlignment(Qt::AlignVCenter | Qt::AlignLeft);
+ recentKeywordsLayout->setAlignment(Qt::AlignTop | Qt::AlignLeft);
recentKeywordsLabel = new QLabel(tr("Recent keywords").toUpper(), this);
#if defined(APP_MAC) | defined(APP_WIN)
QPalette palette = recentKeywordsLabel->palette();
otherLayout->addLayout(recentKeywordsLayout);
+ // recent channels
+ recentChannelsLayout = new QVBoxLayout();
+ recentChannelsLayout->setSpacing(5);
+ recentChannelsLayout->setAlignment(Qt::AlignTop | Qt::AlignLeft);
+ recentChannelsLabel = new QLabel(tr("Recent channels").toUpper(), this);
+#if defined(APP_MAC) | defined(APP_WIN)
+ palette = recentChannelsLabel->palette();
+ palette.setColor(QPalette::WindowText, QColor(0x65, 0x71, 0x80));
+ recentChannelsLabel->setPalette(palette);
+#else
+ recentChannelsLabel->setForegroundRole(QPalette::Dark);
+#endif
+ recentChannelsLabel->hide();
+ recentChannelsLabel->setFont(smallerFont);
+ recentChannelsLayout->addWidget(recentChannelsLabel);
+
+ otherLayout->addLayout(recentChannelsLayout);
+
layout->addLayout(otherLayout);
mainLayout->addSpacing(PADDING);
}
+void SearchView::updateRecentChannels() {
+
+ // cleanup
+ QLayoutItem *item;
+ while ((item = recentChannelsLayout->takeAt(1)) != 0) {
+ item->widget()->close();
+ delete item;
+ }
+ // load
+ QSettings settings;
+ QStringList keywords = settings.value(recentChannelsKey).toStringList();
+ recentChannelsLabel->setVisible(!keywords.isEmpty());
+ // TODO The::globalActions()->value("clearRecentKeywords")->setEnabled(!keywords.isEmpty());
+
+ foreach (QString keyword, keywords) {
+ QString link = keyword;
+ QString display = keyword;
+ if (keyword.startsWith("http://")) {
+ int separator = keyword.indexOf("|");
+ if (separator > 0 && separator + 1 < keyword.length()) {
+ link = keyword.left(separator);
+ display = keyword.mid(separator+1);
+ }
+ }
+ QLabel *itemLabel = new QLabel("<a href=\"" + link
+ + "\" style=\"color:palette(text); text-decoration:none\">"
+ + display + "</a>", this);
+
+ itemLabel->setMaximumWidth(queryEdit->width() + watchButton->width());
+ // itemLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+ // Make links navigable with the keyboard too
+ itemLabel->setTextInteractionFlags(Qt::LinksAccessibleByKeyboard | Qt::LinksAccessibleByMouse);
+
+ connect(itemLabel, SIGNAL(linkActivated(QString)), this, SLOT(watchChannel(QString)));
+ recentChannelsLayout->addWidget(itemLabel);
+ }
+
+}
void SearchView::watch() {
- QString query = queryEdit->text().simplified();
+ QString query = queryEdit->text();
watch(query);
}
void SearchView::watch(QString query) {
+ query = query.simplified();
+
// check for empty query
if (query.length() == 0) {
queryEdit->setFocus(Qt::OtherFocusReason);
return;
}
+ SearchParams *searchParams = new SearchParams();
+ if (typeCombo->currentIndex() == 0)
+ searchParams->setKeywords(query);
+ else {
+ // remove spaces from channel name
+ query = query.replace(" ", "");
+ searchParams->setAuthor(query);
+ searchParams->setSortBy(SearchParams::SortByNewest);
+ }
+
// go!
- emit search(query);
+ emit search(searchParams);
+}
+
+void SearchView::watchChannel(QString channel) {
+
+ channel = channel.simplified();
+
+ // check for empty query
+ if (channel.length() == 0) {
+ queryEdit->setFocus(Qt::OtherFocusReason);
+ return;
+ }
+
+ // remove spaces from channel name
+ channel = channel.replace(" ", "");
+
+ SearchParams *searchParams = new SearchParams();
+ searchParams->setAuthor(channel);
+ searchParams->setSortBy(SearchParams::SortByNewest);
+
+ // go!
+ emit search(searchParams);
}
void SearchView::checkForUpdate() {
painter.fillRect(0, 0, width(), height(), brush);
#endif
}
+
+void SearchView::searchTypeChanged(int index) {
+ if (index == 0) {
+ queryEdit->setSuggester(youtubeSuggest);
+ } else {
+ queryEdit->setSuggester(channelSuggest);
+ }
+}
#include "searchlineedit.h"
#include "updatechecker.h"
+class SearchParams;
+class YouTubeSuggest;
+class ChannelSuggest;
+
class SearchView : public QWidget, public View {
Q_OBJECT
public:
SearchView(QWidget *parent);
void updateRecentKeywords();
+ void updateRecentChannels();
void appear() {
updateRecentKeywords();
+ updateRecentChannels();
queryEdit->clear();
queryEdit->setFocus(Qt::OtherFocusReason);
queryEdit->enableSuggest();
public slots:
void watch(QString query);
+ void watchChannel(QString channel);
void gotNewVersion(QString version);
signals:
- void search(QString query);
+ void search(SearchParams*);
protected:
void paintEvent(QPaintEvent *);
private slots:
void watch();
void textChanged(const QString &text);
+ void searchTypeChanged(int index);
private:
void checkForUpdate();
+ YouTubeSuggest *youtubeSuggest;
+ ChannelSuggest *channelSuggest;
+
+ QComboBox *typeCombo;
SearchLineEdit *queryEdit;
QLabel *recentKeywordsLabel;
- QVBoxLayout *recentKeywordsLayout;
+ QBoxLayout *recentKeywordsLayout;
+ QLabel *recentChannelsLabel;
+ QBoxLayout *recentChannelsLayout;
QLabel *message;
QPushButton *watchButton;
-#include "googlesuggest.h"
-#include "networkaccess.h"
+#include "autocomplete.h"
+#include "suggester.h"
-#define GSUGGEST_URL "http://suggestqueries.google.com/complete/search?ds=yt&output=toolbar&hl=%1&q=%2"
-
-namespace The {
- NetworkAccess* http();
-}
-
-GSuggestCompletion::GSuggestCompletion(QWidget *parent, QLineEdit *editor):
- QObject(parent), buddy(parent), editor(editor) {
+AutoComplete::AutoComplete(QWidget *parent, QLineEdit *editor):
+ QObject(parent), buddy(parent), editor(editor), suggester(0) {
enabled = true;
}
-GSuggestCompletion::~GSuggestCompletion() {
+AutoComplete::~AutoComplete() {
delete popup;
}
-bool GSuggestCompletion::eventFilter(QObject *obj, QEvent *ev) {
+bool AutoComplete::eventFilter(QObject *obj, QEvent *ev) {
if (obj != popup)
return false;
return false;
}
-void GSuggestCompletion::showCompletion(const QStringList &choices) {
+void AutoComplete::showCompletion(const QStringList &choices) {
if (choices.isEmpty())
return;
popup->show();
}
-void GSuggestCompletion::doneCompletion() {
+void AutoComplete::doneCompletion() {
timer->stop();
popup->hide();
editor->setFocus();
}
}
-void GSuggestCompletion::preventSuggest() {
+void AutoComplete::preventSuggest() {
// qDebug() << "preventSuggest";
timer->stop();
enabled = false;
popup->hide();
}
-void GSuggestCompletion::enableSuggest() {
+void AutoComplete::enableSuggest() {
// qDebug() << "enableSuggest";
enabled = true;
}
-void GSuggestCompletion::autoSuggest() {
+void AutoComplete::setSuggester(Suggester* suggester) {
+ if (this->suggester) this->suggester->disconnect();
+ this->suggester = suggester;
+ connect(suggester, SIGNAL(ready(QStringList)), SLOT(suggestionsReady(QStringList)));
+}
+
+void AutoComplete::autoSuggest() {
if (!enabled) return;
QString query = editor->text();
// qDebug() << "originalText" << originalText;
if (query.isEmpty()) return;
- QString locale = QLocale::system().name().replace("_", "-");
- // case for system locales such as "C"
- if (locale.length() < 2) {
- locale = "en-US";
- }
-
- QString url = QString(GSUGGEST_URL).arg(locale, query);
-
- QObject *reply = The::http()->get(url);
- connect(reply, SIGNAL(data(QByteArray)), SLOT(handleNetworkData(QByteArray)));
+ if (suggester)
+ suggester->suggest(query);
}
-void GSuggestCompletion::handleNetworkData(QByteArray response) {
+void AutoComplete::suggestionsReady(QStringList suggestions) {
if (!enabled) return;
-
- QStringList choices;
-
- QXmlStreamReader xml(response);
- while (!xml.atEnd()) {
- xml.readNext();
- if (xml.tokenType() == QXmlStreamReader::StartElement)
- if (xml.name() == "suggestion") {
- QStringRef str = xml.attributes().value("data");
- choices << str.toString();
- }
- }
-
- showCompletion(choices);
-
+ showCompletion(suggestions);
}
-void GSuggestCompletion::currentItemChanged(QListWidgetItem *current) {
+void AutoComplete::currentItemChanged(QListWidgetItem *current) {
if (current) {
// qDebug() << "current" << current->text();
current->setSelected(true);
-#ifndef GOOGLESUGGEST_H
-#define GOOGLESUGGEST_H
+#ifndef SUGGESTCOMPLETION_H
+#define SUGGESTCOMPLETION_H
#include <QtGui>
-class GSuggestCompletion : public QObject {
+class Suggester;
+
+class AutoComplete : public QObject {
Q_OBJECT
public:
- GSuggestCompletion(QWidget *parent, QLineEdit *editor);
- ~GSuggestCompletion();
+ AutoComplete(QWidget *parent, QLineEdit *editor);
+ ~AutoComplete();
bool eventFilter(QObject *obj, QEvent *ev);
void showCompletion(const QStringList &choices);
+ void setSuggester(Suggester* suggester);
public slots:
void doneCompletion();
void preventSuggest();
void enableSuggest();
void autoSuggest();
- void handleNetworkData(QByteArray response);
void currentItemChanged(QListWidgetItem *current);
+ void suggestionsReady(QStringList suggestions);
private:
QWidget *buddy;
QListWidget *popup;
QTimer *timer;
bool enabled;
+ Suggester* suggester;
};
-#endif // GOOGLESUGGEST_H
+#endif // SUGGESTCOMPLETION_H
#include <QtGui/QStyle>
#include <QtGui/QStyleOptionFrameV2>
-#include "googlesuggest.h"
+#include "autocomplete.h"
ClearButton::ClearButton(QWidget *parent)
: QAbstractButton(parent)
setSizePolicy(QSizePolicy::Preferred, policy.verticalPolicy());
// completion
- completion = new GSuggestCompletion(this, m_lineEdit);
+ completion = new AutoComplete(this, m_lineEdit);
}
#define SEARCHLINEEDIT_H
#include "urllineedit.h"
+#include "autocomplete.h"
#include <QtGui/QLineEdit>
#include <QtGui/QAbstractButton>
QT_END_NAMESPACE
class SearchButton;
-class GSuggestCompletion;
+class Suggester;
/*
Clear button on the right hand side of the search widget.
void enableSuggest();
void preventSuggest();
void selectAll() { lineEdit()->selectAll(); };
+ void setSuggester(Suggester *suggester) { completion->setSuggester(suggester); }
protected:
void resizeEvent(QResizeEvent *event);
SearchButton *m_searchButton;
QString m_inactiveText;
- GSuggestCompletion *completion;
+ AutoComplete *completion;
};
#endif // SEARCHLINEEDIT_H