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