2 #include "videomimedata.h"
5 static const QString recentKeywordsKey = "recentKeywords";
7 ListModel::ListModel(QWidget *parent) : QAbstractListModel(parent) {
16 ListModel::~ListModel() {
20 int ListModel::rowCount(const QModelIndex &/*parent*/) const {
21 int count = videos.size();
23 // add the message item
24 if (videos.isEmpty() || !searching)
30 QVariant ListModel::data(const QModelIndex &index, int role) const {
32 int row = index.row();
34 if (row == videos.size()) {
38 boldFont.setBold(true);
42 return ItemTypeShowMore;
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);
56 return palette.color(QPalette::Dark);
57 case Qt::BackgroundColorRole:
58 if (!errorMessage.isEmpty())
59 return palette.color(QPalette::ToolTipBase);
68 } else if (row < 0 || row >= videos.size())
71 Video *video = videos.at(row);
77 return QVariant::fromValue(QPointer<Video>(video));
79 return video == m_activeVideo;
81 return video->title();
87 void ListModel::setActiveRow( int row) {
88 if ( rowExists( row ) ) {
91 m_activeVideo = videoAt(row);
93 int oldactiverow = m_activeRow;
95 if ( rowExists( oldactiverow ) )
96 emit dataChanged( createIndex( oldactiverow, 0 ), createIndex( oldactiverow, columnCount() - 1 ) );
98 emit dataChanged( createIndex( m_activeRow, 0 ), createIndex( m_activeRow, columnCount() - 1 ) );
99 emit activeRowChanged(row);
108 int ListModel::nextRow() const {
109 int nextRow = m_activeRow + 1;
110 if (rowExists(nextRow))
115 Video* ListModel::videoAt( int row ) const {
116 if ( rowExists( row ) )
117 return videos.at( row );
121 Video* ListModel::activeVideo() const {
122 return m_activeVideo;
125 void ListModel::search(SearchParams *searchParams) {
127 // delete current videos
128 while (!videos.isEmpty())
129 delete videos.takeFirst();
133 errorMessage.clear();
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)));
143 this->searchParams = searchParams;
145 youtubeSearch->search(searchParams, MAX_ITEMS, skip);
149 void ListModel::searchMore(int max) {
150 if (searching) return;
152 errorMessage.clear();
153 youtubeSearch->search(searchParams, max, skip);
157 void ListModel::searchMore() {
158 searchMore(MAX_ITEMS);
161 void ListModel::searchNeeded() {
162 int remainingRows = videos.size() - m_activeRow;
163 int rowsNeeded = MAX_ITEMS - remainingRows;
165 searchMore(rowsNeeded);
168 void ListModel::abortSearch() {
169 while (!videos.isEmpty())
170 delete videos.takeFirst();
172 youtubeSearch->abort();
176 void ListModel::searchFinished(int total) {
178 canSearchMore = total > 0;
180 // update the message item
181 emit dataChanged( createIndex( MAX_ITEMS, 0 ), createIndex( MAX_ITEMS, columnCount() - 1 ) );
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 ) );
190 void ListModel::addVideo(Video* video) {
192 connect(video, SIGNAL(gotThumbnail()), this, SLOT(updateThumbnail()));
194 beginInsertRows(QModelIndex(), videos.size(), videos.size());
199 if (videos.size() == 1) {
204 QString query = searchParams->keywords();
205 if (query.startsWith("http://")) {
206 // Save the video title
207 query += "|" + videos.first()->title();
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);
220 void ListModel::updateThumbnail() {
222 Video *video = static_cast<Video *>(sender());
224 qDebug() << "Cannot get sender";
228 int row = rowForVideo(video);
229 emit dataChanged( createIndex( row, 0 ), createIndex( row, columnCount() - 1 ) );
236 * This function does not free memory
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);
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);
255 beginRemoveRows(QModelIndex(), idx, idx);
256 delitems.append(video);
257 videos.removeAll(video);
262 qDeleteAll(delitems);
266 // --- Sturm und drang ---
270 Qt::DropActions ListModel::supportedDropActions() const {
271 return Qt::MoveAction;
274 Qt::ItemFlags ListModel::flags(const QModelIndex &index) const {
275 Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index);
277 if (index.isValid()) {
278 if (index.row() == videos.size()) {
279 // don't drag the "show 10 more" item
282 return ( defaultFlags | Qt::ItemIsDropEnabled | Qt::ItemIsDragEnabled );
284 return Qt::ItemIsDropEnabled | defaultFlags;
287 QStringList ListModel::mimeTypes() const {
289 types << "application/x-minitube-video";
293 QMimeData* ListModel::mimeData( const QModelIndexList &indexes ) const {
294 VideoMimeData* mime = new VideoMimeData();
296 foreach( const QModelIndex &it, indexes ) {
298 if (row >= 0 && row < videos.size())
299 mime->addVideo( videos.at( it.row() ) );
305 bool ListModel::dropMimeData(const QMimeData *data,
306 Qt::DropAction action, int row, int column,
307 const QModelIndex &parent) {
308 if (action == Qt::IgnoreAction)
311 if (!data->hasFormat("application/x-minitube-video"))
320 else if (parent.isValid())
321 beginRow = parent.row();
323 beginRow = rowCount(QModelIndex());
325 const VideoMimeData* videoMimeData = dynamic_cast<const VideoMimeData*>( data );
326 if(!videoMimeData ) return false;
328 QList<Video*> droppedVideos = videoMimeData->videos();
329 foreach( Video *video, droppedVideos) {
332 int videoRow = videos.indexOf(video);
333 removeRows(videoRow, 1, QModelIndex());
335 // and then add them again at the new position
336 beginInsertRows(QModelIndex(), beginRow, beginRow);
337 videos.insert(beginRow, video);
342 // fix m_activeRow after all this
343 m_activeRow = videos.indexOf(m_activeVideo);
345 // let the MediaView restore the selection
346 emit needSelectionFor(droppedVideos);
352 int ListModel::rowForVideo(Video* video) {
353 return videos.indexOf(video);
356 QModelIndex ListModel::indexForVideo(Video* video) {
357 return createIndex(videos.indexOf(video), 0);
360 void ListModel::move(QModelIndexList &indexes, bool up) {
361 QList<Video*> movedVideos;
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;
371 int end=up ? -1 : rowCount()-1, mod=up ? -1 : 1;
372 foreach (Video *video, movedVideos) {
374 int row = rowForVideo(video);
375 if (row+mod==end) { end=row; continue; }
376 // qDebug() << "video row" << row;
377 removeRows(row, 1, QModelIndex());
382 beginInsertRows(QModelIndex(), row, row);
383 videos.insert(row, video);
388 emit needSelectionFor(movedVideos);