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