]> git.sur5r.net Git - minitube/blob - src/ListModel.cpp
Imported Upstream version 1.4.1
[minitube] / src / ListModel.cpp
1 #include "ListModel.h"
2 #include "videomimedata.h"
3
4 #define MAX_ITEMS 10
5 static const QString recentKeywordsKey = "recentKeywords";
6 static const QString recentChannelsKey = "recentChannels";
7
8 ListModel::ListModel(QWidget *parent) : QAbstractListModel(parent) {
9     youtubeSearch = 0;
10     searching = false;
11     canSearchMore = true;
12     m_activeVideo = 0;
13     m_activeRow = -1;
14     skip = 1;
15 }
16
17 ListModel::~ListModel() {
18     delete youtubeSearch;
19 }
20
21 int ListModel::rowCount(const QModelIndex &/*parent*/) const {
22     int count = videos.size();
23     
24     // add the message item
25     if (videos.isEmpty() || !searching)
26         count++;
27     
28     return count;
29 }
30
31 QVariant ListModel::data(const QModelIndex &index, int role) const {
32     
33     int row = index.row();
34     
35     if (row == videos.size()) {
36         
37         QPalette palette;
38         QFont boldFont;
39         boldFont.setBold(true);
40         
41         switch (role) {
42         case ItemTypeRole:
43             return ItemTypeShowMore;
44         case Qt::DisplayRole:
45         case Qt::StatusTipRole:
46             if (!errorMessage.isEmpty()) return errorMessage;
47             if (searching) return tr("Searching...");
48             if (canSearchMore) return tr("Show %1 More").arg(MAX_ITEMS);
49             if (videos.isEmpty()) return tr("No videos");
50             else return tr("No more videos");
51         case Qt::TextAlignmentRole:
52             return QVariant(int(Qt::AlignHCenter | Qt::AlignVCenter));
53         case Qt::ForegroundRole:
54             if (!errorMessage.isEmpty())
55                 return palette.color(QPalette::ToolTipText);
56             else
57                 return palette.color(QPalette::Dark);
58         case Qt::BackgroundColorRole:
59             if (!errorMessage.isEmpty())
60                 return palette.color(QPalette::ToolTipBase);
61             else
62                 return QVariant();
63         case Qt::FontRole:
64             return boldFont;
65         default:
66             return QVariant();
67         }
68         
69     } else if (row < 0 || row >= videos.size())
70         return QVariant();
71     
72     Video *video = videos.at(row);
73     
74     switch (role) {
75     case ItemTypeRole:
76         return ItemTypeVideo;
77     case VideoRole:
78         return QVariant::fromValue(QPointer<Video>(video));
79     case ActiveTrackRole:
80         return video == m_activeVideo;
81     case Qt::DisplayRole:
82         return video->title();
83     }
84     
85     return QVariant();
86 }
87
88 void ListModel::setActiveRow( int row) {
89     if ( rowExists( row ) ) {
90         
91         m_activeRow = row;
92         m_activeVideo = videoAt(row);
93         
94         int oldactiverow = m_activeRow;
95         
96         if ( rowExists( oldactiverow ) )
97             emit dataChanged( createIndex( oldactiverow, 0 ), createIndex( oldactiverow, columnCount() - 1 ) );
98         
99         emit dataChanged( createIndex( m_activeRow, 0 ), createIndex( m_activeRow, columnCount() - 1 ) );
100         emit activeRowChanged(row);
101         
102     } else {
103         m_activeRow = -1;
104         m_activeVideo = 0;
105     }
106
107 }
108
109 int ListModel::nextRow() const {
110     int nextRow = m_activeRow + 1;
111     if (rowExists(nextRow))
112         return nextRow;
113     return -1;
114 }
115
116 Video* ListModel::videoAt( int row ) const {
117     if ( rowExists( row ) )
118         return videos.at( row );
119     return 0;
120 }
121
122 Video* ListModel::activeVideo() const {
123     return m_activeVideo;
124 }
125
126 void ListModel::search(SearchParams *searchParams) {
127
128     // delete current videos
129     while (!videos.isEmpty())
130         delete videos.takeFirst();
131     m_activeVideo = 0;
132     m_activeRow = -1;
133     skip = 1;
134     errorMessage.clear();
135     reset();
136
137     // (re)initialize the YouTubeSearch
138     if (youtubeSearch) delete youtubeSearch;
139     youtubeSearch = new YouTubeSearch();
140     connect(youtubeSearch, SIGNAL(gotVideo(Video*)), this, SLOT(addVideo(Video*)));
141     connect(youtubeSearch, SIGNAL(finished(int)), this, SLOT(searchFinished(int)));
142     connect(youtubeSearch, SIGNAL(error(QString)), this, SLOT(searchError(QString)));
143
144     this->searchParams = searchParams;
145     searching = true;
146     youtubeSearch->search(searchParams, MAX_ITEMS, skip);
147     skip += MAX_ITEMS;
148 }
149
150 void ListModel::searchMore(int max) {
151     if (searching) return;
152     searching = true;
153     errorMessage.clear();
154     youtubeSearch->search(searchParams, max, skip);
155     skip += max;
156 }
157
158 void ListModel::searchMore() {
159     searchMore(MAX_ITEMS);
160 }
161
162 void ListModel::searchNeeded() {
163     int remainingRows = videos.size() - m_activeRow;
164     int rowsNeeded = MAX_ITEMS - remainingRows;
165     if (rowsNeeded > 0)
166         searchMore(rowsNeeded);
167 }
168
169 void ListModel::abortSearch() {
170     while (!videos.isEmpty())
171         delete videos.takeFirst();
172     reset();
173     youtubeSearch->abort();
174     searching = false;
175 }
176
177 void ListModel::searchFinished(int total) {
178     searching = false;
179     canSearchMore = total > 0;
180
181     // update the message item
182     emit dataChanged( createIndex( MAX_ITEMS, 0 ), createIndex( MAX_ITEMS, columnCount() - 1 ) );
183 }
184
185 void ListModel::searchError(QString message) {
186     errorMessage = message;
187     // update the message item
188     emit dataChanged( createIndex( MAX_ITEMS, 0 ), createIndex( MAX_ITEMS, columnCount() - 1 ) );
189 }
190
191 void ListModel::addVideo(Video* video) {
192     
193     connect(video, SIGNAL(gotThumbnail()), this, SLOT(updateThumbnail()));
194
195     beginInsertRows(QModelIndex(), videos.size(), videos.size());
196     videos << video;
197     endInsertRows();
198     
199     // first result!
200     if (videos.size() == 1) {
201         // autoplay
202         setActiveRow(0);
203
204         // save keyword
205         QString query = searchParams->keywords();
206         if (!query.isEmpty()) {
207             if (query.startsWith("http://")) {
208                 // Save the video title
209                 query += "|" + videos.first()->title();
210             }
211             QSettings settings;
212             QStringList keywords = settings.value(recentKeywordsKey).toStringList();
213             keywords.removeAll(query);
214             keywords.prepend(query);
215             while (keywords.size() > 10)
216                 keywords.removeLast();
217             settings.setValue(recentKeywordsKey, keywords);
218         }
219
220         // save channel
221         QString channel = searchParams->author();
222         if (!channel.isEmpty()) {
223             QSettings settings;
224             QStringList channels = settings.value(recentChannelsKey).toStringList();
225             channels.removeAll(channel);
226             channels.prepend(channel);
227             while (channels.size() > 10)
228                 channels.removeLast();
229             settings.setValue(recentChannelsKey, channels);
230         }
231
232     }
233
234 }
235
236 void ListModel::updateThumbnail() {
237
238     Video *video = static_cast<Video *>(sender());
239     if (!video) {
240         qDebug() << "Cannot get sender";
241         return;
242     }
243
244     int row = rowForVideo(video);
245     emit dataChanged( createIndex( row, 0 ), createIndex( row, columnCount() - 1 ) );
246
247 }
248
249 // --- item removal
250
251 /**
252   * This function does not free memory
253   */
254 bool ListModel::removeRows(int position, int rows, const QModelIndex & /*parent*/) {
255     beginRemoveRows(QModelIndex(), position, position+rows-1);
256     for (int row = 0; row < rows; ++row) {
257         videos.removeAt(position);
258     }
259     endRemoveRows();
260     return true;
261 }
262
263 void ListModel::removeIndexes(QModelIndexList &indexes) {
264     QList<Video*> originalList(videos);
265     QList<Video*> delitems;
266     foreach (QModelIndex index, indexes) {
267         if (index.row() >= originalList.size()) continue;
268         Video* video = originalList.at(index.row());
269         int idx = videos.indexOf(video);
270         if (idx != -1) {
271             beginRemoveRows(QModelIndex(), idx, idx);
272             delitems.append(video);
273             videos.removeAll(video);
274             endRemoveRows();
275         }
276     }
277
278     qDeleteAll(delitems);
279
280 }
281
282 // --- Sturm und drang ---
283
284
285
286 Qt::DropActions ListModel::supportedDropActions() const {
287     return Qt::MoveAction;
288 }
289
290 Qt::ItemFlags ListModel::flags(const QModelIndex &index) const {
291     Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index);
292
293     if (index.isValid()) {
294         if (index.row() == videos.size()) {
295             // don't drag the "show 10 more" item
296             return defaultFlags;
297         } else
298             return ( defaultFlags | Qt::ItemIsDropEnabled | Qt::ItemIsDragEnabled );
299     } else
300         return Qt::ItemIsDropEnabled | defaultFlags;
301 }
302
303 QStringList ListModel::mimeTypes() const {
304     QStringList types;
305     types << "application/x-minitube-video";
306     return types;
307 }
308
309 QMimeData* ListModel::mimeData( const QModelIndexList &indexes ) const {
310     VideoMimeData* mime = new VideoMimeData();
311
312     foreach( const QModelIndex &it, indexes ) {
313         int row = it.row();
314         if (row >= 0 && row < videos.size())
315             mime->addVideo( videos.at( it.row() ) );
316     }
317
318     return mime;
319 }
320
321 bool ListModel::dropMimeData(const QMimeData *data,
322                              Qt::DropAction action, int row, int column,
323                              const QModelIndex &parent) {
324     if (action == Qt::IgnoreAction)
325         return true;
326
327     if (!data->hasFormat("application/x-minitube-video"))
328         return false;
329
330     if (column > 0)
331         return false;
332
333     int beginRow;
334     if (row != -1)
335         beginRow = row;
336     else if (parent.isValid())
337         beginRow = parent.row();
338     else
339         beginRow = rowCount(QModelIndex());
340
341     const VideoMimeData* videoMimeData = dynamic_cast<const VideoMimeData*>( data );
342     if(!videoMimeData ) return false;
343
344     QList<Video*> droppedVideos = videoMimeData->videos();
345     foreach( Video *video, droppedVideos) {
346         
347         // remove videos
348         int videoRow = videos.indexOf(video);
349         removeRows(videoRow, 1, QModelIndex());
350         
351         // and then add them again at the new position
352         beginInsertRows(QModelIndex(), beginRow, beginRow);
353         videos.insert(beginRow, video);
354         endInsertRows();
355
356     }
357
358     // fix m_activeRow after all this
359     m_activeRow = videos.indexOf(m_activeVideo);
360
361     // let the MediaView restore the selection
362     emit needSelectionFor(droppedVideos);
363
364     return true;
365
366 }
367
368 int ListModel::rowForVideo(Video* video) {
369     return videos.indexOf(video);
370 }
371
372 QModelIndex ListModel::indexForVideo(Video* video) {
373     return createIndex(videos.indexOf(video), 0);
374 }
375
376 void ListModel::move(QModelIndexList &indexes, bool up) {
377     QList<Video*> movedVideos;
378
379     foreach (QModelIndex index, indexes) {
380         int row = index.row();
381         if (row >= videos.size()) continue;
382         // qDebug() << "index row" << row;
383         Video *video = videoAt(row);
384         movedVideos << video;
385     }
386
387     int end=up ? -1 : rowCount()-1, mod=up ? -1 : 1;
388     foreach (Video *video, movedVideos) {
389
390         int row = rowForVideo(video);
391         if (row+mod==end) { end=row; continue; }
392         // qDebug() << "video row" << row;
393         removeRows(row, 1, QModelIndex());
394
395         if (up) row--;
396         else row++;
397
398         beginInsertRows(QModelIndex(), row, row);
399         videos.insert(row, video);
400         endInsertRows();
401
402     }
403
404     emit needSelectionFor(movedVideos);
405
406 }