2 #include "videomimedata.h"
5 static const QString recentKeywordsKey = "recentKeywords";
6 static const QString recentChannelsKey = "recentChannels";
8 ListModel::ListModel(QWidget *parent) : QAbstractListModel(parent) {
17 authorHovered = false;
18 authorPressed = false;
21 ListModel::~ListModel() {
25 int ListModel::rowCount(const QModelIndex &/*parent*/) const {
26 int count = videos.size();
28 // add the message item
29 if (videos.isEmpty() || !searching)
35 QVariant ListModel::data(const QModelIndex &index, int role) const {
37 int row = index.row();
39 if (row == videos.size()) {
43 boldFont.setBold(true);
47 return ItemTypeShowMore;
49 case Qt::StatusTipRole:
50 if (!errorMessage.isEmpty()) return errorMessage;
51 if (searching) return tr("Searching...");
52 if (canSearchMore) return tr("Show %1 More").arg(MAX_ITEMS);
53 if (videos.isEmpty()) return tr("No videos");
54 else return tr("No more videos");
55 case Qt::TextAlignmentRole:
56 return QVariant(int(Qt::AlignHCenter | Qt::AlignVCenter));
57 case Qt::ForegroundRole:
58 if (!errorMessage.isEmpty())
59 return palette.color(QPalette::ToolTipText);
61 return palette.color(QPalette::Dark);
62 case Qt::BackgroundColorRole:
63 if (!errorMessage.isEmpty())
64 return palette.color(QPalette::ToolTipBase);
73 } else if (row < 0 || row >= videos.size())
76 Video *video = videos.at(row);
82 return QVariant::fromValue(QPointer<Video>(video));
84 return video == m_activeVideo;
86 return video->title();
88 return hoveredRow == index.row();
89 case AuthorHoveredRole:
91 case AuthorPressedRole:
98 void ListModel::setActiveRow( int row) {
99 if ( rowExists( row ) ) {
102 m_activeVideo = videoAt(row);
104 int oldactiverow = m_activeRow;
106 if ( rowExists( oldactiverow ) )
107 emit dataChanged( createIndex( oldactiverow, 0 ), createIndex( oldactiverow, columnCount() - 1 ) );
109 emit dataChanged( createIndex( m_activeRow, 0 ), createIndex( m_activeRow, columnCount() - 1 ) );
110 emit activeRowChanged(row);
119 int ListModel::nextRow() const {
120 int nextRow = m_activeRow + 1;
121 if (rowExists(nextRow))
126 int ListModel::previousRow() const {
127 int prevRow = m_activeRow - 1;
128 if (rowExists(prevRow))
133 Video* ListModel::videoAt( int row ) const {
134 if ( rowExists( row ) )
135 return videos.at( row );
139 Video* ListModel::activeVideo() const {
140 return m_activeVideo;
143 void ListModel::search(SearchParams *searchParams) {
145 // delete current videos
146 while (!videos.isEmpty())
147 delete videos.takeFirst();
151 errorMessage.clear();
154 // (re)initialize the YouTubeSearch
155 if (youtubeSearch) delete youtubeSearch;
156 youtubeSearch = new YouTubeSearch();
157 connect(youtubeSearch, SIGNAL(gotVideo(Video*)), this, SLOT(addVideo(Video*)));
158 connect(youtubeSearch, SIGNAL(finished(int)), this, SLOT(searchFinished(int)));
159 connect(youtubeSearch, SIGNAL(error(QString)), this, SLOT(searchError(QString)));
161 this->searchParams = searchParams;
163 youtubeSearch->search(searchParams, MAX_ITEMS, skip);
167 void ListModel::searchMore(int max) {
168 if (searching) return;
170 errorMessage.clear();
171 youtubeSearch->search(searchParams, max, skip);
175 void ListModel::searchMore() {
176 searchMore(MAX_ITEMS);
179 void ListModel::searchNeeded() {
180 int remainingRows = videos.size() - m_activeRow;
181 int rowsNeeded = MAX_ITEMS - remainingRows;
183 searchMore(rowsNeeded);
186 void ListModel::abortSearch() {
187 while (!videos.isEmpty())
188 delete videos.takeFirst();
190 youtubeSearch->abort();
194 void ListModel::searchFinished(int total) {
196 canSearchMore = total > 0;
198 // update the message item
199 emit dataChanged( createIndex( MAX_ITEMS, 0 ), createIndex( MAX_ITEMS, columnCount() - 1 ) );
202 void ListModel::searchError(QString message) {
203 errorMessage = message;
204 // update the message item
205 emit dataChanged( createIndex( MAX_ITEMS, 0 ), createIndex( MAX_ITEMS, columnCount() - 1 ) );
208 void ListModel::addVideo(Video* video) {
210 connect(video, SIGNAL(gotThumbnail()), this, SLOT(updateThumbnail()));
212 beginInsertRows(QModelIndex(), videos.size(), videos.size());
217 if (videos.size() == 1) {
221 if (!settings.value("manualplay", false).toBool())
225 QString query = searchParams->keywords();
226 if (!query.isEmpty() && !searchParams->isTransient()) {
227 if (query.startsWith("http://")) {
228 // Save the video title
229 query += "|" + videos.first()->title();
231 QStringList keywords = settings.value(recentKeywordsKey).toStringList();
232 keywords.removeAll(query);
233 keywords.prepend(query);
234 while (keywords.size() > 10)
235 keywords.removeLast();
236 settings.setValue(recentKeywordsKey, keywords);
240 QString channel = searchParams->author();
241 if (!channel.isEmpty() && !searchParams->isTransient()) {
243 QStringList channels = settings.value(recentChannelsKey).toStringList();
244 channels.removeAll(channel);
245 channels.prepend(channel);
246 while (channels.size() > 10)
247 channels.removeLast();
248 settings.setValue(recentChannelsKey, channels);
255 void ListModel::updateThumbnail() {
257 Video *video = static_cast<Video *>(sender());
259 qDebug() << "Cannot get sender";
263 int row = rowForVideo(video);
264 emit dataChanged( createIndex( row, 0 ), createIndex( row, columnCount() - 1 ) );
271 * This function does not free memory
273 bool ListModel::removeRows(int position, int rows, const QModelIndex & /*parent*/) {
274 beginRemoveRows(QModelIndex(), position, position+rows-1);
275 for (int row = 0; row < rows; ++row) {
276 videos.removeAt(position);
282 void ListModel::removeIndexes(QModelIndexList &indexes) {
283 QList<Video*> originalList(videos);
284 QList<Video*> delitems;
285 foreach (QModelIndex index, indexes) {
286 if (index.row() >= originalList.size()) continue;
287 Video* video = originalList.at(index.row());
288 int idx = videos.indexOf(video);
290 beginRemoveRows(QModelIndex(), idx, idx);
291 delitems.append(video);
292 videos.removeAll(video);
297 qDeleteAll(delitems);
301 // --- Sturm und drang ---
305 Qt::DropActions ListModel::supportedDropActions() const {
306 return Qt::MoveAction;
309 Qt::ItemFlags ListModel::flags(const QModelIndex &index) const {
310 Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index);
312 if (index.isValid()) {
313 if (index.row() == videos.size()) {
314 // don't drag the "show 10 more" item
317 return ( defaultFlags | Qt::ItemIsDropEnabled | Qt::ItemIsDragEnabled );
319 return Qt::ItemIsDropEnabled | defaultFlags;
322 QStringList ListModel::mimeTypes() const {
324 types << "application/x-minitube-video";
328 QMimeData* ListModel::mimeData( const QModelIndexList &indexes ) const {
329 VideoMimeData* mime = new VideoMimeData();
331 foreach( const QModelIndex &it, indexes ) {
333 if (row >= 0 && row < videos.size())
334 mime->addVideo( videos.at( it.row() ) );
340 bool ListModel::dropMimeData(const QMimeData *data,
341 Qt::DropAction action, int row, int column,
342 const QModelIndex &parent) {
343 if (action == Qt::IgnoreAction)
346 if (!data->hasFormat("application/x-minitube-video"))
355 else if (parent.isValid())
356 beginRow = parent.row();
358 beginRow = rowCount(QModelIndex());
360 const VideoMimeData* videoMimeData = dynamic_cast<const VideoMimeData*>( data );
361 if(!videoMimeData ) return false;
363 QList<Video*> droppedVideos = videoMimeData->videos();
364 foreach( Video *video, droppedVideos) {
367 int videoRow = videos.indexOf(video);
368 removeRows(videoRow, 1, QModelIndex());
370 // and then add them again at the new position
371 beginInsertRows(QModelIndex(), beginRow, beginRow);
372 videos.insert(beginRow, video);
377 // fix m_activeRow after all this
378 m_activeRow = videos.indexOf(m_activeVideo);
380 // let the MediaView restore the selection
381 emit needSelectionFor(droppedVideos);
387 int ListModel::rowForVideo(Video* video) {
388 return videos.indexOf(video);
391 QModelIndex ListModel::indexForVideo(Video* video) {
392 return createIndex(videos.indexOf(video), 0);
395 void ListModel::move(QModelIndexList &indexes, bool up) {
396 QList<Video*> movedVideos;
398 foreach (QModelIndex index, indexes) {
399 int row = index.row();
400 if (row >= videos.size()) continue;
401 // qDebug() << "index row" << row;
402 Video *video = videoAt(row);
403 movedVideos << video;
406 int end=up ? -1 : rowCount()-1, mod=up ? -1 : 1;
407 foreach (Video *video, movedVideos) {
409 int row = rowForVideo(video);
410 if (row+mod==end) { end=row; continue; }
411 // qDebug() << "video row" << row;
412 removeRows(row, 1, QModelIndex());
417 beginInsertRows(QModelIndex(), row, row);
418 videos.insert(row, video);
423 emit needSelectionFor(movedVideos);
429 void ListModel::setHoveredRow(int row) {
430 int oldRow = hoveredRow;
432 emit dataChanged( createIndex( oldRow, 0 ), createIndex( oldRow, columnCount() - 1 ) );
433 emit dataChanged( createIndex( hoveredRow, 0 ), createIndex( hoveredRow, columnCount() - 1 ) );
436 void ListModel::clearHover() {
437 emit dataChanged( createIndex( hoveredRow, 0 ), createIndex( hoveredRow, columnCount() - 1 ) );
441 /* clickable author */
443 void ListModel::enterAuthorHover() {
444 if (authorHovered) return;
445 authorHovered = true;
449 void ListModel::exitAuthorHover() {
450 if (!authorHovered) return;
451 authorHovered = false;
453 setHoveredRow(hoveredRow);
456 void ListModel::enterAuthorPressed() {
457 if (authorPressed) return;
458 authorPressed = true;
462 void ListModel::exitAuthorPressed() {
463 if (!authorPressed) return;
464 authorPressed = false;
468 void ListModel::updateAuthor() {
469 emit dataChanged( createIndex( hoveredRow, 0 ), createIndex( hoveredRow, columnCount() - 1 ) );