]> git.sur5r.net Git - minitube/blob - src/ListModel.cpp
e7592ecbf096e3e17369554b3a5af7ba7bdfa7dc
[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     case Qt::StatusTipRole:
82         return video->title();
83         /*
84         case Qt::ToolTipRole:
85           
86             QString tooltip;
87             if (!element.firstChildElement().text().isEmpty()) {
88                 tooltip.append(QString("<b>").append(element.firstChildElement().text()).append("</b><br/>"));
89             }
90             if (!fromDate.isEmpty()) {
91                 tooltip.append("<i>Pubblicato il</i> ").append(fromDate);
92             }
93             if (!toDate.isEmpty()) {
94                 tooltip.append("<br/><i>Scadenza</i>: ").append(toDate);
95             }
96             tooltip.append("<br/><i>Tipo</i>: ").append(typeName)
97                 .append("<br/><i>Id</i>: ").appen    QFont boldFont;
98     boldFont.setBold(true);d(id);
99             return tooltip;
100             */
101         
102         // case StreamUrlRole:
103         // return video->streamUrl();
104     }
105     
106     return QVariant();
107 }
108
109 void ListModel::setActiveRow( int row) {
110     if ( rowExists( row ) ) {
111         
112         m_activeRow = row;
113         m_activeVideo = videoAt(row);
114         
115         // setStateOfRow( row, Item::Played );
116         
117         int oldactiverow = m_activeRow;
118         
119         if ( rowExists( oldactiverow ) )
120             emit dataChanged( createIndex( oldactiverow, 0 ), createIndex( oldactiverow, columnCount() - 1 ) );
121         
122         emit dataChanged( createIndex( m_activeRow, 0 ), createIndex( m_activeRow, columnCount() - 1 ) );
123         emit activeRowChanged(row);
124         
125     } else {
126         m_activeRow = -1;
127         m_activeVideo = 0;
128     }
129
130 }
131
132 int ListModel::nextRow() const {
133     int nextRow = m_activeRow + 1;
134     if (rowExists(nextRow))
135         return nextRow;
136     return -1;
137 }
138
139 Video* ListModel::videoAt( int row ) const {
140     if ( rowExists( row ) )
141         return videos.at( row );
142     return 0;
143 }
144
145 Video* ListModel::activeVideo() const {
146     return m_activeVideo;
147 }
148
149 void ListModel::search(SearchParams *searchParams) {
150
151     // delete current videos
152     while (!videos.isEmpty())
153         delete videos.takeFirst();
154     m_activeVideo = 0;
155     m_activeRow = -1;
156     skip = 1;
157     errorMessage.clear();
158     reset();
159
160     // (re)initialize the YouTubeSearch
161     if (youtubeSearch) delete youtubeSearch;
162     youtubeSearch = new YouTubeSearch();
163     connect(youtubeSearch, SIGNAL(gotVideo(Video*)), this, SLOT(addVideo(Video*)));
164     connect(youtubeSearch, SIGNAL(finished(int)), this, SLOT(searchFinished(int)));
165     connect(youtubeSearch, SIGNAL(error(QString)), this, SLOT(searchError(QString)));
166
167     this->searchParams = searchParams;
168     searching = true;
169     youtubeSearch->search(searchParams, MAX_ITEMS, skip);
170     skip += MAX_ITEMS;
171 }
172
173 void ListModel::searchMore(int max) {
174     if (searching) return;
175     searching = true;
176     errorMessage.clear();
177     youtubeSearch->search(searchParams, max, skip);
178     skip += max;
179 }
180
181 void ListModel::searchMore() {
182     searchMore(MAX_ITEMS);
183 }
184
185 void ListModel::searchNeeded() {
186     int remainingRows = videos.size() - m_activeRow;
187     int rowsNeeded = MAX_ITEMS - remainingRows;
188     if (rowsNeeded > 0)
189         searchMore(rowsNeeded);
190 }
191
192 void ListModel::abortSearch() {
193     while (!videos.isEmpty())
194         delete videos.takeFirst();
195     reset();
196     youtubeSearch->abort();
197     searching = false;
198 }
199
200 void ListModel::searchFinished(int total) {
201     searching = false;
202     canSearchMore = total > 0;
203
204     // update the message item
205     emit dataChanged( createIndex( MAX_ITEMS, 0 ), createIndex( MAX_ITEMS, columnCount() - 1 ) );
206 }
207
208 void ListModel::searchError(QString message) {
209     errorMessage = message;
210     // update the message item
211     emit dataChanged( createIndex( MAX_ITEMS, 0 ), createIndex( MAX_ITEMS, columnCount() - 1 ) );
212 }
213
214 void ListModel::addVideo(Video* video) {
215     
216     connect(video, SIGNAL(gotThumbnail()), this, SLOT(updateThumbnail()));
217
218     beginInsertRows(QModelIndex(), videos.size(), videos.size());
219     videos << video;
220     endInsertRows();
221     
222     // first result!
223     if (videos.size() == 1) {
224         // autoplay
225         setActiveRow(0);
226
227         // save keyword
228         QString query = searchParams->keywords();
229         QSettings settings;
230         QStringList keywords = settings.value(recentKeywordsKey).toStringList();
231         keywords.removeAll(query);
232         keywords.prepend(query);
233         while (keywords.size() > 10)
234             keywords.removeLast();
235         settings.setValue(recentKeywordsKey, keywords);
236     }
237
238 }
239
240 void ListModel::updateThumbnail() {
241
242     Video *video = static_cast<Video *>(sender());
243     if (!video) {
244         qDebug() << "Cannot get sender";
245         return;
246     }
247
248     int row = rowForVideo(video);
249     emit dataChanged( createIndex( row, 0 ), createIndex( row, columnCount() - 1 ) );
250
251 }
252
253 // --- item removal
254
255 /**
256   * This function does not free memory
257   */
258 bool ListModel::removeRows(int position, int rows, const QModelIndex & /*parent*/) {
259     beginRemoveRows(QModelIndex(), position, position+rows-1);
260     for (int row = 0; row < rows; ++row) {
261         videos.removeAt(position);
262     }
263     endRemoveRows();
264     return true;
265 }
266
267 void ListModel::removeIndexes(QModelIndexList &indexes) {
268     QList<Video*> originalList(videos);
269     QList<Video*> delitems;
270     foreach (QModelIndex index, indexes) {
271         Video* video = originalList.at(index.row());
272         int idx = videos.indexOf(video);
273         if (idx != -1) {
274             beginRemoveRows(QModelIndex(), idx, idx);
275             delitems.append(video);
276             videos.removeAll(video);
277             endRemoveRows();
278         }
279     }
280
281     qDeleteAll(delitems);
282
283 }
284
285 // --- Sturm und drang ---
286
287
288
289 Qt::DropActions ListModel::supportedDropActions() const {
290     return Qt::MoveAction;
291 }
292
293 Qt::ItemFlags ListModel::flags(const QModelIndex &index) const {
294     Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index);
295
296     if (index.isValid()) {
297         if (index.row() == videos.size()) {
298             // don't drag the "show 10 more" item
299             return defaultFlags;
300         } else
301             return ( defaultFlags | Qt::ItemIsDropEnabled | Qt::ItemIsDragEnabled );
302     } else
303         return Qt::ItemIsDropEnabled | defaultFlags;
304 }
305
306 QStringList ListModel::mimeTypes() const {
307     QStringList types;
308     types << "application/x-minitube-video";
309     return types;
310 }
311
312 QMimeData* ListModel::mimeData( const QModelIndexList &indexes ) const {
313     VideoMimeData* mime = new VideoMimeData();
314
315     foreach( const QModelIndex &it, indexes ) {
316         int row = it.row();
317         if (row >= 0 && row < videos.size())
318             mime->addVideo( videos.at( it.row() ) );
319     }
320
321     return mime;
322 }
323
324 bool ListModel::dropMimeData(const QMimeData *data,
325                              Qt::DropAction action, int row, int column,
326                              const QModelIndex &parent) {
327     if (action == Qt::IgnoreAction)
328         return true;
329
330     if (!data->hasFormat("application/x-minitube-video"))
331         return false;
332
333     if (column > 0)
334         return false;
335
336     int beginRow;
337     if (row != -1)
338         beginRow = row;
339     else if (parent.isValid())
340         beginRow = parent.row();
341     else
342         beginRow = rowCount(QModelIndex());
343
344     const VideoMimeData* videoMimeData = dynamic_cast<const VideoMimeData*>( data );
345     if(!videoMimeData ) return false;
346
347     QList<Video*> droppedVideos = videoMimeData->videos();
348     foreach( Video *video, droppedVideos) {
349         
350         // remove videos
351         int videoRow = videos.indexOf(video);
352         removeRows(videoRow, 1, QModelIndex());
353         
354         // and then add them again at the new position
355         beginInsertRows(QModelIndex(), beginRow, beginRow);
356         videos.insert(beginRow, video);
357         endInsertRows();
358
359     }
360
361     // fix m_activeRow after all this
362     m_activeRow = videos.indexOf(m_activeVideo);
363
364     // let the MediaView restore the selection
365     emit needSelectionFor(droppedVideos);
366
367     return true;
368
369 }
370
371 int ListModel::rowForVideo(Video* video) {
372     return videos.indexOf(video);
373 }
374
375 QModelIndex ListModel::indexForVideo(Video* video) {
376     return createIndex(videos.indexOf(video), 0);
377 }
378
379 void ListModel::move(QModelIndexList &indexes, bool up) {
380
381     QList<Video*> movedVideos;
382
383     foreach (QModelIndex index, indexes) {
384         int row = index.row();
385         // qDebug() << "index row" << row;
386         Video *video = videoAt(row);
387         movedVideos << video;
388     }
389
390     int counter = 1;
391     foreach (Video *video, movedVideos) {
392
393         int row = rowForVideo(video);
394         // qDebug() << "video row" << row;
395         removeRows(row, 1, QModelIndex());
396
397         if (up) row--;
398         else row++;
399
400         beginInsertRows(QModelIndex(), row, row);
401         videos.insert(row, video);
402         endInsertRows();
403
404         counter++;
405     }
406
407     emit needSelectionFor(movedVideos);
408
409 }