"<p>" + tr("Released under the <a href='%1'>GNU General Public License</a>")
.arg("http://www.gnu.org/licenses/gpl.html") + "</p>"
#endif
- "<p>© 2009-2012 " + Constants::ORG_NAME + "</p>"
+ "<p>© 2009-2013 " + Constants::ORG_NAME + "</p>"
"</body></html>";
QLabel *infoLabel = new QLabel(info, this);
infoLabel->setOpenExternalLinks(true);
+++ /dev/null
-#include "listmodel.h"
-#include "videomimedata.h"
-
-#define MAX_ITEMS 10
-static const QString recentKeywordsKey = "recentKeywords";
-static const QString recentChannelsKey = "recentChannels";
-
-ListModel::ListModel(QWidget *parent) : QAbstractListModel(parent) {
- youtubeSearch = 0;
- searching = false;
- canSearchMore = true;
- m_activeVideo = 0;
- m_activeRow = -1;
- skip = 1;
-
- hoveredRow = -1;
- authorHovered = false;
- authorPressed = false;
-}
-
-ListModel::~ListModel() {
- delete youtubeSearch;
-}
-
-int ListModel::rowCount(const QModelIndex &/*parent*/) const {
- int count = videos.size();
-
- // add the message item
- if (videos.isEmpty() || !searching)
- count++;
-
- return count;
-}
-
-QVariant ListModel::data(const QModelIndex &index, int role) const {
-
- int row = index.row();
-
- if (row == videos.size()) {
-
- QPalette palette;
- QFont boldFont;
- boldFont.setBold(true);
-
- switch (role) {
- case ItemTypeRole:
- return ItemTypeShowMore;
- case Qt::DisplayRole:
- case Qt::StatusTipRole:
- if (!errorMessage.isEmpty()) return errorMessage;
- if (searching) return tr("Searching...");
- if (canSearchMore) return tr("Show %1 More").arg(MAX_ITEMS);
- if (videos.isEmpty()) return tr("No videos");
- else return tr("No more videos");
- case Qt::TextAlignmentRole:
- return QVariant(int(Qt::AlignHCenter | Qt::AlignVCenter));
- case Qt::ForegroundRole:
- if (!errorMessage.isEmpty())
- return palette.color(QPalette::ToolTipText);
- else
- return palette.color(QPalette::Dark);
- case Qt::BackgroundColorRole:
- if (!errorMessage.isEmpty())
- return palette.color(QPalette::ToolTipBase);
- else
- return QVariant();
- case Qt::FontRole:
- return boldFont;
- default:
- return QVariant();
- }
-
- } else if (row < 0 || row >= videos.size())
- return QVariant();
-
- Video *video = videos.at(row);
-
- switch (role) {
- case ItemTypeRole:
- return ItemTypeVideo;
- case VideoRole:
- return QVariant::fromValue(QPointer<Video>(video));
- case ActiveTrackRole:
- return video == m_activeVideo;
- case Qt::DisplayRole:
- return video->title();
- case HoveredItemRole:
- return hoveredRow == index.row();
- case AuthorHoveredRole:
- return authorHovered;
- case AuthorPressedRole:
- return authorPressed;
- }
-
- return QVariant();
-}
-
-void ListModel::setActiveRow( int row) {
- if ( rowExists( row ) ) {
-
- m_activeRow = row;
- m_activeVideo = videoAt(row);
-
- int oldactiverow = m_activeRow;
-
- if ( rowExists( oldactiverow ) )
- emit dataChanged( createIndex( oldactiverow, 0 ), createIndex( oldactiverow, columnCount() - 1 ) );
-
- emit dataChanged( createIndex( m_activeRow, 0 ), createIndex( m_activeRow, columnCount() - 1 ) );
- emit activeRowChanged(row);
-
- } else {
- m_activeRow = -1;
- m_activeVideo = 0;
- }
-
-}
-
-int ListModel::nextRow() const {
- int nextRow = m_activeRow + 1;
- if (rowExists(nextRow))
- return nextRow;
- return -1;
-}
-
-int ListModel::previousRow() const {
- int prevRow = m_activeRow - 1;
- if (rowExists(prevRow))
- return prevRow;
- return -1;
-}
-
-Video* ListModel::videoAt( int row ) const {
- if ( rowExists( row ) )
- return videos.at( row );
- return 0;
-}
-
-Video* ListModel::activeVideo() const {
- return m_activeVideo;
-}
-
-void ListModel::search(SearchParams *searchParams) {
-
- // delete current videos
- while (!videos.isEmpty())
- delete videos.takeFirst();
- m_activeVideo = 0;
- m_activeRow = -1;
- skip = 1;
- errorMessage.clear();
- reset();
-
- // (re)initialize the YouTubeSearch
- if (youtubeSearch) delete youtubeSearch;
- youtubeSearch = new YouTubeSearch();
- connect(youtubeSearch, SIGNAL(gotVideo(Video*)), this, SLOT(addVideo(Video*)));
- connect(youtubeSearch, SIGNAL(finished(int)), this, SLOT(searchFinished(int)));
- connect(youtubeSearch, SIGNAL(error(QString)), this, SLOT(searchError(QString)));
-
- this->searchParams = searchParams;
- searching = true;
- youtubeSearch->search(searchParams, MAX_ITEMS, skip);
- skip += MAX_ITEMS;
-}
-
-void ListModel::searchMore(int max) {
- if (searching) return;
- searching = true;
- errorMessage.clear();
- youtubeSearch->search(searchParams, max, skip);
- skip += max;
-}
-
-void ListModel::searchMore() {
- searchMore(MAX_ITEMS);
-}
-
-void ListModel::searchNeeded() {
- int remainingRows = videos.size() - m_activeRow;
- int rowsNeeded = MAX_ITEMS - remainingRows;
- if (rowsNeeded > 0)
- searchMore(rowsNeeded);
-}
-
-void ListModel::abortSearch() {
- while (!videos.isEmpty())
- delete videos.takeFirst();
- reset();
- youtubeSearch->abort();
- searching = false;
-}
-
-void ListModel::searchFinished(int total) {
- searching = false;
- canSearchMore = total > 0;
-
- // update the message item
- emit dataChanged( createIndex( MAX_ITEMS, 0 ), createIndex( MAX_ITEMS, columnCount() - 1 ) );
-
- if (!youtubeSearch->getSuggestions().isEmpty()) {
- emit haveSuggestions(youtubeSearch->getSuggestions());
- }
-}
-
-void ListModel::searchError(QString message) {
- errorMessage = message;
- // update the message item
- emit dataChanged( createIndex( MAX_ITEMS, 0 ), createIndex( MAX_ITEMS, columnCount() - 1 ) );
-}
-
-void ListModel::addVideo(Video* video) {
-
- connect(video, SIGNAL(gotThumbnail()), this, SLOT(updateThumbnail()));
-
- beginInsertRows(QModelIndex(), videos.size(), videos.size());
- videos << video;
- endInsertRows();
-
- // first result!
- if (videos.size() == 1) {
-
- // manualplay
- QSettings settings;
- if (!settings.value("manualplay", false).toBool())
- setActiveRow(0);
-
- // save keyword
- QString query = searchParams->keywords();
- if (!query.isEmpty() && !searchParams->isTransient()) {
- if (query.startsWith("http://")) {
- // Save the video title
- query += "|" + videos.first()->title();
- }
- 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() && !searchParams->isTransient()) {
- QSettings settings;
- QStringList channels = settings.value(recentChannelsKey).toStringList();
- channels.removeAll(channel);
- channels.prepend(channel);
- while (channels.size() > 10)
- channels.removeLast();
- settings.setValue(recentChannelsKey, channels);
- }
-
- }
-
-}
-
-void ListModel::updateThumbnail() {
-
- Video *video = static_cast<Video *>(sender());
- if (!video) {
- qDebug() << "Cannot get sender";
- return;
- }
-
- int row = rowForVideo(video);
- emit dataChanged( createIndex( row, 0 ), createIndex( row, columnCount() - 1 ) );
-
-}
-
-// --- item removal
-
-/**
- * This function does not free memory
- */
-bool ListModel::removeRows(int position, int rows, const QModelIndex & /*parent*/) {
- beginRemoveRows(QModelIndex(), position, position+rows-1);
- for (int row = 0; row < rows; ++row) {
- videos.removeAt(position);
- }
- endRemoveRows();
- return true;
-}
-
-void ListModel::removeIndexes(QModelIndexList &indexes) {
- QList<Video*> originalList(videos);
- QList<Video*> delitems;
- foreach (QModelIndex index, indexes) {
- if (index.row() >= originalList.size()) continue;
- Video* video = originalList.at(index.row());
- int idx = videos.indexOf(video);
- if (idx != -1) {
- beginRemoveRows(QModelIndex(), idx, idx);
- delitems.append(video);
- videos.removeAll(video);
- endRemoveRows();
- }
- }
-
- qDeleteAll(delitems);
-
-}
-
-// --- Sturm und drang ---
-
-
-
-Qt::DropActions ListModel::supportedDropActions() const {
- return Qt::MoveAction;
-}
-
-Qt::ItemFlags ListModel::flags(const QModelIndex &index) const {
- if (index.isValid())
- if (index.row() == videos.size()) {
- // don't drag the "show 10 more" item
- return Qt::ItemIsEnabled;
- } else return (Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled);
- return Qt::ItemIsDropEnabled;
-}
-
-QStringList ListModel::mimeTypes() const {
- QStringList types;
- types << "application/x-minitube-video";
- return types;
-}
-
-QMimeData* ListModel::mimeData( const QModelIndexList &indexes ) const {
- VideoMimeData* mime = new VideoMimeData();
-
- foreach( const QModelIndex &it, indexes ) {
- int row = it.row();
- if (row >= 0 && row < videos.size())
- mime->addVideo( videos.at( it.row() ) );
- }
-
- return mime;
-}
-
-bool ListModel::dropMimeData(const QMimeData *data,
- Qt::DropAction action, int row, int column,
- const QModelIndex &parent) {
- if (action == Qt::IgnoreAction)
- return true;
-
- if (!data->hasFormat("application/x-minitube-video"))
- return false;
-
- if (column > 0)
- return false;
-
- int beginRow;
- if (row != -1)
- beginRow = row;
- else if (parent.isValid())
- beginRow = parent.row();
- else
- beginRow = rowCount(QModelIndex());
-
- const VideoMimeData* videoMimeData = dynamic_cast<const VideoMimeData*>( data );
- if(!videoMimeData ) return false;
-
- QList<Video*> droppedVideos = videoMimeData->videos();
- foreach( Video *video, droppedVideos) {
-
- // remove videos
- int videoRow = videos.indexOf(video);
- removeRows(videoRow, 1, QModelIndex());
-
- // and then add them again at the new position
- beginInsertRows(QModelIndex(), beginRow, beginRow);
- videos.insert(beginRow, video);
- endInsertRows();
-
- }
-
- // fix m_activeRow after all this
- m_activeRow = videos.indexOf(m_activeVideo);
-
- // let the MediaView restore the selection
- emit needSelectionFor(droppedVideos);
-
- return true;
-
-}
-
-int ListModel::rowForVideo(Video* video) {
- return videos.indexOf(video);
-}
-
-QModelIndex ListModel::indexForVideo(Video* video) {
- return createIndex(videos.indexOf(video), 0);
-}
-
-void ListModel::move(QModelIndexList &indexes, bool up) {
- QList<Video*> movedVideos;
-
- foreach (QModelIndex index, indexes) {
- int row = index.row();
- if (row >= videos.size()) continue;
- // qDebug() << "index row" << row;
- Video *video = videoAt(row);
- movedVideos << video;
- }
-
- int end=up ? -1 : rowCount()-1, mod=up ? -1 : 1;
- foreach (Video *video, movedVideos) {
-
- int row = rowForVideo(video);
- if (row+mod==end) { end=row; continue; }
- // qDebug() << "video row" << row;
- removeRows(row, 1, QModelIndex());
-
- if (up) row--;
- else row++;
-
- beginInsertRows(QModelIndex(), row, row);
- videos.insert(row, video);
- endInsertRows();
-
- }
-
- emit needSelectionFor(movedVideos);
-
-}
-
-/* row hovering */
-
-void ListModel::setHoveredRow(int row) {
- int oldRow = hoveredRow;
- hoveredRow = row;
- emit dataChanged( createIndex( oldRow, 0 ), createIndex( oldRow, columnCount() - 1 ) );
- emit dataChanged( createIndex( hoveredRow, 0 ), createIndex( hoveredRow, columnCount() - 1 ) );
-}
-
-void ListModel::clearHover() {
- emit dataChanged( createIndex( hoveredRow, 0 ), createIndex( hoveredRow, columnCount() - 1 ) );
- hoveredRow = -1;
-}
-
-/* clickable author */
-
-void ListModel::enterAuthorHover() {
- if (authorHovered) return;
- authorHovered = true;
- updateAuthor();
-}
-
-void ListModel::exitAuthorHover() {
- if (!authorHovered) return;
- authorHovered = false;
- updateAuthor();
- setHoveredRow(hoveredRow);
-}
-
-void ListModel::enterAuthorPressed() {
- if (authorPressed) return;
- authorPressed = true;
- updateAuthor();
-}
-
-void ListModel::exitAuthorPressed() {
- if (!authorPressed) return;
- authorPressed = false;
- updateAuthor();
-}
-
-void ListModel::updateAuthor() {
- emit dataChanged( createIndex( hoveredRow, 0 ), createIndex( hoveredRow, columnCount() - 1 ) );
-}
+++ /dev/null
-#ifndef LISTMODEL_H
-#define LISTMODEL_H
-
-#include "video.h"
-#include "youtubesearch.h"
-#include "searchparams.h"
-
-enum DataRoles {
- ItemTypeRole = Qt::UserRole,
- VideoRole,
- ActiveTrackRole,
- DownloadItemRole,
- HoveredItemRole,
- DownloadButtonHoveredRole,
- DownloadButtonPressedRole,
- AuthorHoveredRole,
- AuthorPressedRole
-};
-
-enum ItemTypes {
- ItemTypeVideo = 1,
- ItemTypeShowMore
-};
-
-class ListModel : public QAbstractListModel {
-
- Q_OBJECT
-
-public:
-
- ListModel(QWidget *parent);
- ~ListModel();
-
- // inherited from QAbstractListModel
- int rowCount(const QModelIndex &parent = QModelIndex()) const;
- // int rowCount( const QModelIndex& parent = QModelIndex() ) const { Q_UNUSED( parent ); return videos.size(); }
- int columnCount( const QModelIndex& parent = QModelIndex() ) const { Q_UNUSED( parent ); return 4; }
- QVariant data(const QModelIndex &index, int role) const;
- bool removeRows(int position, int rows, const QModelIndex &parent);
-
- Qt::ItemFlags flags(const QModelIndex &index) const;
- QStringList mimeTypes() const;
- Qt::DropActions supportedDropActions() const;
- QMimeData* mimeData( const QModelIndexList &indexes ) const;
- bool dropMimeData(const QMimeData *data,
- Qt::DropAction action, int row, int column,
- const QModelIndex &parent);
-
- // custom methods
- void setActiveRow( int row );
- bool rowExists( int row ) const { return (( row >= 0 ) && ( row < videos.size() ) ); }
- int activeRow() const { return m_activeRow; } // returns -1 if there is no active row
- int nextRow() const;
- int previousRow() const;
- void removeIndexes(QModelIndexList &indexes);
- int rowForVideo(Video* video);
- QModelIndex indexForVideo(Video* video);
- void move(QModelIndexList &indexes, bool up);
-
- Video* videoAt( int row ) const;
- Video* activeVideo() const;
-
- // video search methods
- void search(SearchParams *searchParams);
- void abortSearch();
-
-
-public slots:
- void searchMore();
- void searchNeeded();
- void addVideo(Video* video);
- void searchFinished(int total);
- void searchError(QString message);
- void updateThumbnail();
-
- void setHoveredRow(int row);
- void clearHover();
- void enterAuthorHover();
- void exitAuthorHover();
- void enterAuthorPressed();
- void exitAuthorPressed();
- void updateAuthor();
-
-signals:
- void activeRowChanged(int);
- void needSelectionFor(QList<Video*>);
- void haveSuggestions(const QStringList &suggestions);
-
-private:
- void searchMore(int max);
-
- YouTubeSearch *youtubeSearch;
- SearchParams *searchParams;
- bool searching;
- bool canSearchMore;
-
- QList<Video*> videos;
- int skip;
-
- // the row being played
- int m_activeRow;
- Video *m_activeVideo;
-
- QString errorMessage;
-
- int hoveredRow;
- bool authorHovered;
- bool authorPressed;
-};
-
-#endif
--- /dev/null
+#include "listmodel.h"
+#include "videomimedata.h"
+
+#define MAX_ITEMS 10
+static const QString recentKeywordsKey = "recentKeywords";
+static const QString recentChannelsKey = "recentChannels";
+
+ListModel::ListModel(QWidget *parent) : QAbstractListModel(parent) {
+ youtubeSearch = 0;
+ searching = false;
+ canSearchMore = true;
+ m_activeVideo = 0;
+ m_activeRow = -1;
+ skip = 1;
+
+ hoveredRow = -1;
+ authorHovered = false;
+ authorPressed = false;
+}
+
+ListModel::~ListModel() {
+ delete youtubeSearch;
+}
+
+int ListModel::rowCount(const QModelIndex &/*parent*/) const {
+ int count = videos.size();
+
+ // add the message item
+ if (videos.isEmpty() || !searching)
+ count++;
+
+ return count;
+}
+
+QVariant ListModel::data(const QModelIndex &index, int role) const {
+
+ int row = index.row();
+
+ if (row == videos.size()) {
+
+ QPalette palette;
+ QFont boldFont;
+ boldFont.setBold(true);
+
+ switch (role) {
+ case ItemTypeRole:
+ return ItemTypeShowMore;
+ case Qt::DisplayRole:
+ case Qt::StatusTipRole:
+ if (!errorMessage.isEmpty()) return errorMessage;
+ if (searching) return tr("Searching...");
+ if (canSearchMore) return tr("Show %1 More").arg(MAX_ITEMS);
+ if (videos.isEmpty()) return tr("No videos");
+ else return tr("No more videos");
+ case Qt::TextAlignmentRole:
+ return QVariant(int(Qt::AlignHCenter | Qt::AlignVCenter));
+ case Qt::ForegroundRole:
+ if (!errorMessage.isEmpty())
+ return palette.color(QPalette::ToolTipText);
+ else
+ return palette.color(QPalette::Dark);
+ case Qt::BackgroundColorRole:
+ if (!errorMessage.isEmpty())
+ return palette.color(QPalette::ToolTipBase);
+ else
+ return QVariant();
+ case Qt::FontRole:
+ return boldFont;
+ default:
+ return QVariant();
+ }
+
+ } else if (row < 0 || row >= videos.size())
+ return QVariant();
+
+ Video *video = videos.at(row);
+
+ switch (role) {
+ case ItemTypeRole:
+ return ItemTypeVideo;
+ case VideoRole:
+ return QVariant::fromValue(QPointer<Video>(video));
+ case ActiveTrackRole:
+ return video == m_activeVideo;
+ case Qt::DisplayRole:
+ return video->title();
+ case HoveredItemRole:
+ return hoveredRow == index.row();
+ case AuthorHoveredRole:
+ return authorHovered;
+ case AuthorPressedRole:
+ return authorPressed;
+ }
+
+ return QVariant();
+}
+
+void ListModel::setActiveRow( int row) {
+ if ( rowExists( row ) ) {
+
+ m_activeRow = row;
+ m_activeVideo = videoAt(row);
+
+ int oldactiverow = m_activeRow;
+
+ if ( rowExists( oldactiverow ) )
+ emit dataChanged( createIndex( oldactiverow, 0 ), createIndex( oldactiverow, columnCount() - 1 ) );
+
+ emit dataChanged( createIndex( m_activeRow, 0 ), createIndex( m_activeRow, columnCount() - 1 ) );
+ emit activeRowChanged(row);
+
+ } else {
+ m_activeRow = -1;
+ m_activeVideo = 0;
+ }
+
+}
+
+int ListModel::nextRow() const {
+ int nextRow = m_activeRow + 1;
+ if (rowExists(nextRow))
+ return nextRow;
+ return -1;
+}
+
+int ListModel::previousRow() const {
+ int prevRow = m_activeRow - 1;
+ if (rowExists(prevRow))
+ return prevRow;
+ return -1;
+}
+
+Video* ListModel::videoAt( int row ) const {
+ if ( rowExists( row ) )
+ return videos.at( row );
+ return 0;
+}
+
+Video* ListModel::activeVideo() const {
+ return m_activeVideo;
+}
+
+void ListModel::search(SearchParams *searchParams) {
+
+ // delete current videos
+ while (!videos.isEmpty())
+ delete videos.takeFirst();
+ m_activeVideo = 0;
+ m_activeRow = -1;
+ skip = 1;
+ errorMessage.clear();
+ reset();
+
+ // (re)initialize the YouTubeSearch
+ if (youtubeSearch) delete youtubeSearch;
+ youtubeSearch = new YouTubeSearch();
+ connect(youtubeSearch, SIGNAL(gotVideo(Video*)), this, SLOT(addVideo(Video*)));
+ connect(youtubeSearch, SIGNAL(finished(int)), this, SLOT(searchFinished(int)));
+ connect(youtubeSearch, SIGNAL(error(QString)), this, SLOT(searchError(QString)));
+
+ this->searchParams = searchParams;
+ searching = true;
+ youtubeSearch->search(searchParams, MAX_ITEMS, skip);
+ skip += MAX_ITEMS;
+}
+
+void ListModel::searchMore(int max) {
+ if (searching) return;
+ searching = true;
+ errorMessage.clear();
+ youtubeSearch->search(searchParams, max, skip);
+ skip += max;
+}
+
+void ListModel::searchMore() {
+ searchMore(MAX_ITEMS);
+}
+
+void ListModel::searchNeeded() {
+ int remainingRows = videos.size() - m_activeRow;
+ int rowsNeeded = MAX_ITEMS - remainingRows;
+ if (rowsNeeded > 0)
+ searchMore(rowsNeeded);
+}
+
+void ListModel::abortSearch() {
+ while (!videos.isEmpty())
+ delete videos.takeFirst();
+ reset();
+ youtubeSearch->abort();
+ searching = false;
+}
+
+void ListModel::searchFinished(int total) {
+ searching = false;
+ canSearchMore = total > 0;
+
+ // update the message item
+ emit dataChanged( createIndex( MAX_ITEMS, 0 ), createIndex( MAX_ITEMS, columnCount() - 1 ) );
+
+ if (!youtubeSearch->getSuggestions().isEmpty()) {
+ emit haveSuggestions(youtubeSearch->getSuggestions());
+ }
+}
+
+void ListModel::searchError(QString message) {
+ errorMessage = message;
+ // update the message item
+ emit dataChanged( createIndex( MAX_ITEMS, 0 ), createIndex( MAX_ITEMS, columnCount() - 1 ) );
+}
+
+void ListModel::addVideo(Video* video) {
+
+ connect(video, SIGNAL(gotThumbnail()), this, SLOT(updateThumbnail()));
+
+ beginInsertRows(QModelIndex(), videos.size(), videos.size());
+ videos << video;
+ endInsertRows();
+
+ // first result!
+ if (videos.size() == 1) {
+
+ // manualplay
+ QSettings settings;
+ if (!settings.value("manualplay", false).toBool())
+ setActiveRow(0);
+
+ // save keyword
+ QString query = searchParams->keywords();
+ if (!query.isEmpty() && !searchParams->isTransient()) {
+ if (query.startsWith("http://")) {
+ // Save the video title
+ query += "|" + videos.first()->title();
+ }
+ 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() && !searchParams->isTransient()) {
+ QSettings settings;
+ QStringList channels = settings.value(recentChannelsKey).toStringList();
+ channels.removeAll(channel);
+ channels.prepend(channel);
+ while (channels.size() > 10)
+ channels.removeLast();
+ settings.setValue(recentChannelsKey, channels);
+ }
+
+ }
+
+}
+
+void ListModel::updateThumbnail() {
+
+ Video *video = static_cast<Video *>(sender());
+ if (!video) {
+ qDebug() << "Cannot get sender";
+ return;
+ }
+
+ int row = rowForVideo(video);
+ emit dataChanged( createIndex( row, 0 ), createIndex( row, columnCount() - 1 ) );
+
+}
+
+// --- item removal
+
+/**
+ * This function does not free memory
+ */
+bool ListModel::removeRows(int position, int rows, const QModelIndex & /*parent*/) {
+ beginRemoveRows(QModelIndex(), position, position+rows-1);
+ for (int row = 0; row < rows; ++row) {
+ videos.removeAt(position);
+ }
+ endRemoveRows();
+ return true;
+}
+
+void ListModel::removeIndexes(QModelIndexList &indexes) {
+ QList<Video*> originalList(videos);
+ QList<Video*> delitems;
+ foreach (QModelIndex index, indexes) {
+ if (index.row() >= originalList.size()) continue;
+ Video* video = originalList.at(index.row());
+ int idx = videos.indexOf(video);
+ if (idx != -1) {
+ beginRemoveRows(QModelIndex(), idx, idx);
+ delitems.append(video);
+ videos.removeAll(video);
+ endRemoveRows();
+ }
+ }
+
+ qDeleteAll(delitems);
+
+}
+
+// --- Sturm und drang ---
+
+
+
+Qt::DropActions ListModel::supportedDropActions() const {
+ return Qt::MoveAction;
+}
+
+Qt::ItemFlags ListModel::flags(const QModelIndex &index) const {
+ if (index.isValid())
+ if (index.row() == videos.size()) {
+ // don't drag the "show 10 more" item
+ return Qt::ItemIsEnabled;
+ } else return (Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled);
+ return Qt::ItemIsDropEnabled;
+}
+
+QStringList ListModel::mimeTypes() const {
+ QStringList types;
+ types << "application/x-minitube-video";
+ return types;
+}
+
+QMimeData* ListModel::mimeData( const QModelIndexList &indexes ) const {
+ VideoMimeData* mime = new VideoMimeData();
+
+ foreach( const QModelIndex &it, indexes ) {
+ int row = it.row();
+ if (row >= 0 && row < videos.size())
+ mime->addVideo( videos.at( it.row() ) );
+ }
+
+ return mime;
+}
+
+bool ListModel::dropMimeData(const QMimeData *data,
+ Qt::DropAction action, int row, int column,
+ const QModelIndex &parent) {
+ if (action == Qt::IgnoreAction)
+ return true;
+
+ if (!data->hasFormat("application/x-minitube-video"))
+ return false;
+
+ if (column > 0)
+ return false;
+
+ int beginRow;
+ if (row != -1)
+ beginRow = row;
+ else if (parent.isValid())
+ beginRow = parent.row();
+ else
+ beginRow = rowCount(QModelIndex());
+
+ const VideoMimeData* videoMimeData = dynamic_cast<const VideoMimeData*>( data );
+ if(!videoMimeData ) return false;
+
+ QList<Video*> droppedVideos = videoMimeData->videos();
+ foreach( Video *video, droppedVideos) {
+
+ // remove videos
+ int videoRow = videos.indexOf(video);
+ removeRows(videoRow, 1, QModelIndex());
+
+ // and then add them again at the new position
+ beginInsertRows(QModelIndex(), beginRow, beginRow);
+ videos.insert(beginRow, video);
+ endInsertRows();
+
+ }
+
+ // fix m_activeRow after all this
+ m_activeRow = videos.indexOf(m_activeVideo);
+
+ // let the MediaView restore the selection
+ emit needSelectionFor(droppedVideos);
+
+ return true;
+
+}
+
+int ListModel::rowForVideo(Video* video) {
+ return videos.indexOf(video);
+}
+
+QModelIndex ListModel::indexForVideo(Video* video) {
+ return createIndex(videos.indexOf(video), 0);
+}
+
+void ListModel::move(QModelIndexList &indexes, bool up) {
+ QList<Video*> movedVideos;
+
+ foreach (QModelIndex index, indexes) {
+ int row = index.row();
+ if (row >= videos.size()) continue;
+ // qDebug() << "index row" << row;
+ Video *video = videoAt(row);
+ movedVideos << video;
+ }
+
+ int end=up ? -1 : rowCount()-1, mod=up ? -1 : 1;
+ foreach (Video *video, movedVideos) {
+
+ int row = rowForVideo(video);
+ if (row+mod==end) { end=row; continue; }
+ // qDebug() << "video row" << row;
+ removeRows(row, 1, QModelIndex());
+
+ if (up) row--;
+ else row++;
+
+ beginInsertRows(QModelIndex(), row, row);
+ videos.insert(row, video);
+ endInsertRows();
+
+ }
+
+ emit needSelectionFor(movedVideos);
+
+}
+
+/* row hovering */
+
+void ListModel::setHoveredRow(int row) {
+ int oldRow = hoveredRow;
+ hoveredRow = row;
+ emit dataChanged( createIndex( oldRow, 0 ), createIndex( oldRow, columnCount() - 1 ) );
+ emit dataChanged( createIndex( hoveredRow, 0 ), createIndex( hoveredRow, columnCount() - 1 ) );
+}
+
+void ListModel::clearHover() {
+ emit dataChanged( createIndex( hoveredRow, 0 ), createIndex( hoveredRow, columnCount() - 1 ) );
+ hoveredRow = -1;
+}
+
+/* clickable author */
+
+void ListModel::enterAuthorHover() {
+ if (authorHovered) return;
+ authorHovered = true;
+ updateAuthor();
+}
+
+void ListModel::exitAuthorHover() {
+ if (!authorHovered) return;
+ authorHovered = false;
+ updateAuthor();
+ setHoveredRow(hoveredRow);
+}
+
+void ListModel::enterAuthorPressed() {
+ if (authorPressed) return;
+ authorPressed = true;
+ updateAuthor();
+}
+
+void ListModel::exitAuthorPressed() {
+ if (!authorPressed) return;
+ authorPressed = false;
+ updateAuthor();
+}
+
+void ListModel::updateAuthor() {
+ emit dataChanged( createIndex( hoveredRow, 0 ), createIndex( hoveredRow, columnCount() - 1 ) );
+}
--- /dev/null
+#ifndef LISTMODEL_H
+#define LISTMODEL_H
+
+#include "video.h"
+#include "youtubesearch.h"
+#include "searchparams.h"
+
+enum DataRoles {
+ ItemTypeRole = Qt::UserRole,
+ VideoRole,
+ ActiveTrackRole,
+ DownloadItemRole,
+ HoveredItemRole,
+ DownloadButtonHoveredRole,
+ DownloadButtonPressedRole,
+ AuthorHoveredRole,
+ AuthorPressedRole
+};
+
+enum ItemTypes {
+ ItemTypeVideo = 1,
+ ItemTypeShowMore
+};
+
+class ListModel : public QAbstractListModel {
+
+ Q_OBJECT
+
+public:
+
+ ListModel(QWidget *parent);
+ ~ListModel();
+
+ // inherited from QAbstractListModel
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ // int rowCount( const QModelIndex& parent = QModelIndex() ) const { Q_UNUSED( parent ); return videos.size(); }
+ int columnCount( const QModelIndex& parent = QModelIndex() ) const { Q_UNUSED( parent ); return 4; }
+ QVariant data(const QModelIndex &index, int role) const;
+ bool removeRows(int position, int rows, const QModelIndex &parent);
+
+ Qt::ItemFlags flags(const QModelIndex &index) const;
+ QStringList mimeTypes() const;
+ Qt::DropActions supportedDropActions() const;
+ QMimeData* mimeData( const QModelIndexList &indexes ) const;
+ bool dropMimeData(const QMimeData *data,
+ Qt::DropAction action, int row, int column,
+ const QModelIndex &parent);
+
+ // custom methods
+ void setActiveRow( int row );
+ bool rowExists( int row ) const { return (( row >= 0 ) && ( row < videos.size() ) ); }
+ int activeRow() const { return m_activeRow; } // returns -1 if there is no active row
+ int nextRow() const;
+ int previousRow() const;
+ void removeIndexes(QModelIndexList &indexes);
+ int rowForVideo(Video* video);
+ QModelIndex indexForVideo(Video* video);
+ void move(QModelIndexList &indexes, bool up);
+
+ Video* videoAt( int row ) const;
+ Video* activeVideo() const;
+
+ // video search methods
+ void search(SearchParams *searchParams);
+ void abortSearch();
+
+
+public slots:
+ void searchMore();
+ void searchNeeded();
+ void addVideo(Video* video);
+ void searchFinished(int total);
+ void searchError(QString message);
+ void updateThumbnail();
+
+ void setHoveredRow(int row);
+ void clearHover();
+ void enterAuthorHover();
+ void exitAuthorHover();
+ void enterAuthorPressed();
+ void exitAuthorPressed();
+ void updateAuthor();
+
+signals:
+ void activeRowChanged(int);
+ void needSelectionFor(QList<Video*>);
+ void haveSuggestions(const QStringList &suggestions);
+
+private:
+ void searchMore(int max);
+
+ YouTubeSearch *youtubeSearch;
+ SearchParams *searchParams;
+ bool searching;
+ bool canSearchMore;
+
+ QList<Video*> videos;
+ int skip;
+
+ // the row being played
+ int m_activeRow;
+ Video *m_activeVideo;
+
+ QString errorMessage;
+
+ int hoveredRow;
+ bool authorHovered;
+ bool authorPressed;
+};
+
+#endif
+++ /dev/null
-#include "playlistwidget.h"
-#include "segmentedcontrol.h"
-
-PlaylistWidget::PlaylistWidget (QWidget *parent, SegmentedControl *tabBar, QListView *listView)
- : QWidget(parent) {
- QBoxLayout *layout = new QVBoxLayout(this);
- layout->setMargin(0);
- layout->setSpacing(0);
- layout->addWidget(tabBar);
- layout->addWidget(listView);
-}
+++ /dev/null
-#ifndef PLAYLISTWIDGET_H
-#define PLAYLISTWIDGET_H
-
-#include <QtGui>
-
-class SegmentedControl;
-
-class PlaylistWidget : public QWidget {
-
- Q_OBJECT
-
-public:
- PlaylistWidget(QWidget *parent, SegmentedControl *tabBar, QListView *listView);
-
-};
-
-#endif // PLAYLISTWIDGET_H
+++ /dev/null
-#include "youtubesearch.h"
-#include "youtubestreamreader.h"
-#include "constants.h"
-#include "networkaccess.h"
-
-namespace The {
- NetworkAccess* http();
-}
-
-YouTubeSearch::YouTubeSearch() : QObject() {}
-
-void YouTubeSearch::search(SearchParams *searchParams, int max, int skip) {
- this->abortFlag = false;
-
- QUrl url("http://gdata.youtube.com/feeds/api/videos/");
- url.addQueryItem("v", "2");
-
- url.addQueryItem("max-results", QString::number(max));
- url.addQueryItem("start-index", QString::number(skip));
-
- if (!searchParams->keywords().isEmpty()) {
- if (searchParams->keywords().startsWith("http://") ||
- searchParams->keywords().startsWith("https://")) {
- url.addQueryItem("q", YouTubeSearch::videoIdFromUrl(searchParams->keywords()));
- } else url.addQueryItem("q", searchParams->keywords());
- }
-
- if (!searchParams->author().isEmpty())
- url.addQueryItem("author", searchParams->author());
-
- switch (searchParams->sortBy()) {
- case SearchParams::SortByNewest:
- url.addQueryItem("orderby", "published");
- break;
- case SearchParams::SortByViewCount:
- url.addQueryItem("orderby", "viewCount");
- break;
- case SearchParams::SortByRating:
- url.addQueryItem("orderby", "rating");
- break;
- }
-
- switch (searchParams->duration()) {
- case SearchParams::DurationShort:
- url.addQueryItem("duration", "short");
- break;
- case SearchParams::DurationMedium:
- url.addQueryItem("duration", "medium");
- break;
- case SearchParams::DurationLong:
- url.addQueryItem("duration", "long");
- break;
- }
-
- switch (searchParams->time()) {
- case SearchParams::TimeToday:
- url.addQueryItem("time", "today");
- break;
- case SearchParams::TimeWeek:
- url.addQueryItem("time", "this_week");
- break;
- case SearchParams::TimeMonth:
- url.addQueryItem("time", "this_month");
- break;
- }
-
- switch (searchParams->quality()) {
- case SearchParams::QualityHD:
- url.addQueryItem("hd", "true");
- break;
- }
-
- QObject *reply = The::http()->get(url);
- connect(reply, SIGNAL(data(QByteArray)), SLOT(parseResults(QByteArray)));
- connect(reply, SIGNAL(error(QNetworkReply*)), SLOT(error(QNetworkReply*)));
-
-}
-
-void YouTubeSearch::error(QNetworkReply *reply) {
- emit error(reply->errorString());
-}
-
-void YouTubeSearch::parseResults(QByteArray data) {
-
- YouTubeStreamReader reader;
- if (!reader.read(data)) {
- qDebug() << "Error parsing XML";
- }
- videos = reader.getVideos();
- suggestions = reader.getSuggestions();
-
- foreach (Video *video, videos) {
- // send it to the model
- emit gotVideo(video);
- }
-
- foreach (Video *video, videos) {
- // preload the thumb
- if (abortFlag) return;
- video->preloadThumbnail();
- }
-
- emit finished(videos.size());
-}
-
-QList<Video*> YouTubeSearch::getResults() {
- return videos;
-}
-
-const QStringList & YouTubeSearch::getSuggestions() const {
- return suggestions;
-}
-
-void YouTubeSearch::abort() {
- this->abortFlag = true;
-}
-
-QString YouTubeSearch::videoIdFromUrl(QString url) {
- QRegExp re = QRegExp("^.*\\?v=([^&]+).*$");
- if (re.exactMatch(url)) {
- QString videoId = re.cap(1);
- videoId.remove('-');
- return videoId;
- }
- return QString();
-}
+++ /dev/null
-#ifndef YOUTUBESEARCH_H
-#define YOUTUBESEARCH_H
-
-#include "video.h"
-#include "searchparams.h"
-
-class YouTubeSearch : public QObject {
-
- Q_OBJECT
-
-public:
- YouTubeSearch();
- void search(SearchParams *searchParams, int max, int skip);
- void abort();
- QList<Video*> getResults();
- const QStringList & getSuggestions() const;
- static QString videoIdFromUrl(QString url);
-
-signals:
- void gotVideo(Video*);
- void finished(int total);
- void error(QString message);
-
-private slots:
- void parseResults(QByteArray data);
- void error(QNetworkReply *reply);
-
-private:
-
- QList<Video*> videos;
- QStringList suggestions;
-
- bool abortFlag;
-
-};
-
-#endif // YOUTUBESEARCH_H
+++ /dev/null
-#include "youtubestreamreader.h"
-#include <QtGui>
-
-
-YouTubeStreamReader::YouTubeStreamReader() {
-
-}
-
-bool YouTubeStreamReader::read(QByteArray data) {
- addData(data);
-
- while (!atEnd()) {
- readNext();
- if (isStartElement()) {
- if (name() == "feed") {
- while (!atEnd()) {
- readNext();
- if (isStartElement() && name() == "entry") {
- readEntry();
- } else if (name() == "link"
- && attributes().value("rel").toString()
- == "http://schemas.google.com/g/2006#spellcorrection") {
- suggestions << attributes().value("title").toString();
- }
- }
- }
- }
- }
-
- return !error();
-}
-
-void YouTubeStreamReader::readMediaGroup() {
-
-}
-
-void YouTubeStreamReader::readEntry() {
- Video* video = new Video();
- // qDebug(" *** ENTRY ***");
-
- while (!atEnd()) {
- readNext();
-
- /*
- qDebug() << name();
- QXmlStreamAttribute attribute;
- foreach (attribute, attributes())
- qDebug() << attribute.name() << ":" << attribute.value();
- */
-
- if (isEndElement() && name() == "entry") break;
- if (isStartElement()) {
-
- if (name() == "link"
- && attributes().value("rel").toString() == "alternate"
- && attributes().value("type").toString() == "text/html"
- ) {
- QString webpage = attributes().value("href").toString();
- webpage.remove("&feature=youtube_gdata");
- video->setWebpage(QUrl(webpage));
- } else if (name() == "author") {
- while(readNextStartElement())
- if (name() == "name") {
- QString author = readElementText();
- video->setAuthor(author);
- } else if (name() == "uri") {
- QString uri = readElementText();
- int i = uri.lastIndexOf('/');
- if (i != -1) uri = uri.mid(i+1);
- video->setAuthorUri(uri);
- } else skipCurrentElement();
- } else if (name() == "published") {
- video->setPublished(QDateTime::fromString(readElementText(), Qt::ISODate));
- } else if (namespaceUri() == "http://gdata.youtube.com/schemas/2007"
- && name() == "statistics") {
- QString viewCount = attributes().value("viewCount").toString();
- video->setViewCount(viewCount.toInt());
- }
- else if (namespaceUri() == "http://search.yahoo.com/mrss/" && name() == "group") {
-
- // read media group
- while (!atEnd()) {
- readNext();
- if (isEndElement() && name() == "group") break;
- if (isStartElement()) {
- if (name() == "thumbnail") {
- // qDebug() << "Thumb: " << attributes().value("url").toString();
- // video->thumbnailUrls() << QUrl(attributes().value("url").toString());
- video->addThumbnailUrl(QUrl(attributes().value("url").toString()));
- }
- else if (name() == "title") {
- QString title = readElementText();
- // qDebug() << "Title: " << title;
- video->setTitle(title);
- }
- else if (name() == "description") {
- QString desc = readElementText();
- // qDebug() << "Description: " << desc;
- video->setDescription(desc);
- }
- else if (name() == "duration") {
- QString duration = attributes().value("seconds").toString();
- // qDebug() << "Duration: " << duration;
- video->setDuration(duration.toInt());
- }
- }
- }
- }
- }
- }
-
- videos.append(video);
-
-}
-
-QList<Video*> YouTubeStreamReader::getVideos() {
- return videos;
-}
-
-const QStringList & YouTubeStreamReader::getSuggestions() const {
- return suggestions;
-}
+++ /dev/null
-#ifndef YOUTUBESTREAMREADER_H
-#define YOUTUBESTREAMREADER_H
-
-#include <QXmlStreamReader>
-#include <QBuffer>
-#include "video.h"
-
-class YouTubeStreamReader : public QXmlStreamReader
-{
-public:
- YouTubeStreamReader();
- bool read(QByteArray data);
- QList<Video*> getVideos();
- const QStringList & getSuggestions() const;
-
-private:
- void readMediaGroup();
- void readEntry();
- QList<Video*> videos;
- QStringList suggestions;
-};
-
-#endif // YOUTUBESTREAMREADER_H
+++ /dev/null
-#include "youtubesuggest.h"
-#include <QtXml>
-#include "networkaccess.h"
-
-#define GSUGGEST_URL "http://suggestqueries.google.com/complete/search?ds=yt&output=toolbar&hl=%1&q=%2"
-
-namespace The {
- NetworkAccess* http();
-}
-
-YouTubeSuggest::YouTubeSuggest(QObject *parent) : Suggester() {
-
-}
-
-void YouTubeSuggest::suggest(QString query) {
- 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)));
-}
-
-void YouTubeSuggest::handleNetworkData(QByteArray response) {
- 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();
- }
- }
- }
- emit ready(choices);
-}
+++ /dev/null
-#ifndef YOUTUBESUGGEST_H
-#define YOUTUBESUGGEST_H
-
-#include <QtCore>
-
-#include "suggester.h"
-
-class YouTubeSuggest : public Suggester {
-
- Q_OBJECT
-
-public:
- YouTubeSuggest(QObject *parent = 0);
- void suggest(QString query);
-
-signals:
- void ready(QStringList);
-
-private slots:
- void handleNetworkData(QByteArray response);
-
-};
-
-#endif // YOUTUBESUGGEST_H
--- /dev/null
+#include "youtubestreamreader.h"
+#include <QtGui>
+
+
+YouTubeStreamReader::YouTubeStreamReader() {
+
+}
+
+bool YouTubeStreamReader::read(QByteArray data) {
+ addData(data);
+
+ while (!atEnd()) {
+ readNext();
+ if (isStartElement()) {
+ if (name() == "feed") {
+ while (!atEnd()) {
+ readNext();
+ if (isStartElement() && name() == "entry") {
+ readEntry();
+ } else if (name() == "link"
+ && attributes().value("rel").toString()
+ == "http://schemas.google.com/g/2006#spellcorrection") {
+ suggestions << attributes().value("title").toString();
+ }
+ }
+ }
+ }
+ }
+
+ return !error();
+}
+
+void YouTubeStreamReader::readMediaGroup() {
+
+}
+
+void YouTubeStreamReader::readEntry() {
+ Video* video = new Video();
+ // qDebug(" *** ENTRY ***");
+
+ while (!atEnd()) {
+ readNext();
+
+ /*
+ qDebug() << name();
+ QXmlStreamAttribute attribute;
+ foreach (attribute, attributes())
+ qDebug() << attribute.name() << ":" << attribute.value();
+ */
+
+ if (isEndElement() && name() == "entry") break;
+ if (isStartElement()) {
+
+ if (name() == "link"
+ && attributes().value("rel").toString() == "alternate"
+ && attributes().value("type").toString() == "text/html"
+ ) {
+ QString webpage = attributes().value("href").toString();
+ webpage.remove("&feature=youtube_gdata");
+ video->setWebpage(QUrl(webpage));
+ } else if (name() == "author") {
+ while(readNextStartElement())
+ if (name() == "name") {
+ QString author = readElementText();
+ video->setAuthor(author);
+ } else if (name() == "uri") {
+ QString uri = readElementText();
+ int i = uri.lastIndexOf('/');
+ if (i != -1) uri = uri.mid(i+1);
+ video->setAuthorUri(uri);
+ } else skipCurrentElement();
+ } else if (name() == "published") {
+ video->setPublished(QDateTime::fromString(readElementText(), Qt::ISODate));
+ } else if (namespaceUri() == "http://gdata.youtube.com/schemas/2007"
+ && name() == "statistics") {
+ QString viewCount = attributes().value("viewCount").toString();
+ video->setViewCount(viewCount.toInt());
+ }
+ else if (namespaceUri() == "http://search.yahoo.com/mrss/" && name() == "group") {
+
+ // read media group
+ while (!atEnd()) {
+ readNext();
+ if (isEndElement() && name() == "group") break;
+ if (isStartElement()) {
+ if (name() == "thumbnail") {
+ // qDebug() << "Thumb: " << attributes().value("url").toString();
+ // video->thumbnailUrls() << QUrl(attributes().value("url").toString());
+ video->addThumbnailUrl(QUrl(attributes().value("url").toString()));
+ }
+ else if (name() == "title") {
+ QString title = readElementText();
+ // qDebug() << "Title: " << title;
+ video->setTitle(title);
+ }
+ else if (name() == "description") {
+ QString desc = readElementText();
+ // qDebug() << "Description: " << desc;
+ video->setDescription(desc);
+ }
+ else if (name() == "duration") {
+ QString duration = attributes().value("seconds").toString();
+ // qDebug() << "Duration: " << duration;
+ video->setDuration(duration.toInt());
+ }
+ }
+ }
+ }
+ }
+ }
+
+ videos.append(video);
+
+}
+
+QList<Video*> YouTubeStreamReader::getVideos() {
+ return videos;
+}
+
+const QStringList & YouTubeStreamReader::getSuggestions() const {
+ return suggestions;
+}
--- /dev/null
+#ifndef YOUTUBESTREAMREADER_H
+#define YOUTUBESTREAMREADER_H
+
+#include <QXmlStreamReader>
+#include <QBuffer>
+#include "video.h"
+
+class YouTubeStreamReader : public QXmlStreamReader
+{
+public:
+ YouTubeStreamReader();
+ bool read(QByteArray data);
+ QList<Video*> getVideos();
+ const QStringList & getSuggestions() const;
+
+private:
+ void readMediaGroup();
+ void readEntry();
+ QList<Video*> videos;
+ QStringList suggestions;
+};
+
+#endif // YOUTUBESTREAMREADER_H
--- /dev/null
+#include "youtubesuggest.h"
+#include <QtXml>
+#include "networkaccess.h"
+
+#define GSUGGEST_URL "http://suggestqueries.google.com/complete/search?ds=yt&output=toolbar&hl=%1&q=%2"
+
+namespace The {
+ NetworkAccess* http();
+}
+
+YouTubeSuggest::YouTubeSuggest(QObject *parent) : Suggester() {
+
+}
+
+void YouTubeSuggest::suggest(QString query) {
+ 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)));
+}
+
+void YouTubeSuggest::handleNetworkData(QByteArray response) {
+ 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();
+ }
+ }
+ }
+ emit ready(choices);
+}
--- /dev/null
+#ifndef YOUTUBESUGGEST_H
+#define YOUTUBESUGGEST_H
+
+#include <QtCore>
+
+#include "suggester.h"
+
+class YouTubeSuggest : public Suggester {
+
+ Q_OBJECT
+
+public:
+ YouTubeSuggest(QObject *parent = 0);
+ void suggest(QString query);
+
+signals:
+ void ready(QStringList);
+
+private slots:
+ void handleNetworkData(QByteArray response);
+
+};
+
+#endif // YOUTUBESUGGEST_H