2 #include "videomimedata.h"
5 static const QString recentKeywordsKey = "recentKeywords";
6 static const QString recentChannelsKey = "recentChannels";
8 ListModel::ListModel(QWidget *parent) : QAbstractListModel(parent) {
17 ListModel::~ListModel() {
21 int ListModel::rowCount(const QModelIndex &/*parent*/) const {
22 int count = videos.size();
24 // add the message item
25 if (videos.isEmpty() || !searching)
31 QVariant ListModel::data(const QModelIndex &index, int role) const {
33 int row = index.row();
35 if (row == videos.size()) {
39 boldFont.setBold(true);
43 return ItemTypeShowMore;
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);
57 return palette.color(QPalette::Dark);
58 case Qt::BackgroundColorRole:
59 if (!errorMessage.isEmpty())
60 return palette.color(QPalette::ToolTipBase);
69 } else if (row < 0 || row >= videos.size())
72 Video *video = videos.at(row);
78 return QVariant::fromValue(QPointer<Video>(video));
80 return video == m_activeVideo;
82 return video->title();
88 void ListModel::setActiveRow( int row) {
89 if ( rowExists( row ) ) {
92 m_activeVideo = videoAt(row);
94 int oldactiverow = m_activeRow;
96 if ( rowExists( oldactiverow ) )
97 emit dataChanged( createIndex( oldactiverow, 0 ), createIndex( oldactiverow, columnCount() - 1 ) );
99 emit dataChanged( createIndex( m_activeRow, 0 ), createIndex( m_activeRow, columnCount() - 1 ) );
100 emit activeRowChanged(row);
109 int ListModel::nextRow() const {
110 int nextRow = m_activeRow + 1;
111 if (rowExists(nextRow))
116 Video* ListModel::videoAt( int row ) const {
117 if ( rowExists( row ) )
118 return videos.at( row );
122 Video* ListModel::activeVideo() const {
123 return m_activeVideo;
126 void ListModel::search(SearchParams *searchParams) {
128 // delete current videos
129 while (!videos.isEmpty())
130 delete videos.takeFirst();
134 errorMessage.clear();
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)));
144 this->searchParams = searchParams;
146 youtubeSearch->search(searchParams, MAX_ITEMS, skip);
150 void ListModel::searchMore(int max) {
151 if (searching) return;
153 errorMessage.clear();
154 youtubeSearch->search(searchParams, max, skip);
158 void ListModel::searchMore() {
159 searchMore(MAX_ITEMS);
162 void ListModel::searchNeeded() {
163 int remainingRows = videos.size() - m_activeRow;
164 int rowsNeeded = MAX_ITEMS - remainingRows;
166 searchMore(rowsNeeded);
169 void ListModel::abortSearch() {
170 while (!videos.isEmpty())
171 delete videos.takeFirst();
173 youtubeSearch->abort();
177 void ListModel::searchFinished(int total) {
179 canSearchMore = total > 0;
181 // update the message item
182 emit dataChanged( createIndex( MAX_ITEMS, 0 ), createIndex( MAX_ITEMS, columnCount() - 1 ) );
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 ) );
191 void ListModel::addVideo(Video* video) {
193 connect(video, SIGNAL(gotThumbnail()), this, SLOT(updateThumbnail()));
195 beginInsertRows(QModelIndex(), videos.size(), videos.size());
200 if (videos.size() == 1) {
205 QString query = searchParams->keywords();
206 if (!query.isEmpty()) {
207 if (query.startsWith("http://")) {
208 // Save the video title
209 query += "|" + videos.first()->title();
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);
221 QString channel = searchParams->author();
222 if (!channel.isEmpty()) {
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);
236 void ListModel::updateThumbnail() {
238 Video *video = static_cast<Video *>(sender());
240 qDebug() << "Cannot get sender";
244 int row = rowForVideo(video);
245 emit dataChanged( createIndex( row, 0 ), createIndex( row, columnCount() - 1 ) );
252 * This function does not free memory
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);
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);
271 beginRemoveRows(QModelIndex(), idx, idx);
272 delitems.append(video);
273 videos.removeAll(video);
278 qDeleteAll(delitems);
282 // --- Sturm und drang ---
286 Qt::DropActions ListModel::supportedDropActions() const {
287 return Qt::MoveAction;
290 Qt::ItemFlags ListModel::flags(const QModelIndex &index) const {
291 Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index);
293 if (index.isValid()) {
294 if (index.row() == videos.size()) {
295 // don't drag the "show 10 more" item
298 return ( defaultFlags | Qt::ItemIsDropEnabled | Qt::ItemIsDragEnabled );
300 return Qt::ItemIsDropEnabled | defaultFlags;
303 QStringList ListModel::mimeTypes() const {
305 types << "application/x-minitube-video";
309 QMimeData* ListModel::mimeData( const QModelIndexList &indexes ) const {
310 VideoMimeData* mime = new VideoMimeData();
312 foreach( const QModelIndex &it, indexes ) {
314 if (row >= 0 && row < videos.size())
315 mime->addVideo( videos.at( it.row() ) );
321 bool ListModel::dropMimeData(const QMimeData *data,
322 Qt::DropAction action, int row, int column,
323 const QModelIndex &parent) {
324 if (action == Qt::IgnoreAction)
327 if (!data->hasFormat("application/x-minitube-video"))
336 else if (parent.isValid())
337 beginRow = parent.row();
339 beginRow = rowCount(QModelIndex());
341 const VideoMimeData* videoMimeData = dynamic_cast<const VideoMimeData*>( data );
342 if(!videoMimeData ) return false;
344 QList<Video*> droppedVideos = videoMimeData->videos();
345 foreach( Video *video, droppedVideos) {
348 int videoRow = videos.indexOf(video);
349 removeRows(videoRow, 1, QModelIndex());
351 // and then add them again at the new position
352 beginInsertRows(QModelIndex(), beginRow, beginRow);
353 videos.insert(beginRow, video);
358 // fix m_activeRow after all this
359 m_activeRow = videos.indexOf(m_activeVideo);
361 // let the MediaView restore the selection
362 emit needSelectionFor(droppedVideos);
368 int ListModel::rowForVideo(Video* video) {
369 return videos.indexOf(video);
372 QModelIndex ListModel::indexForVideo(Video* video) {
373 return createIndex(videos.indexOf(video), 0);
376 void ListModel::move(QModelIndexList &indexes, bool up) {
377 QList<Video*> movedVideos;
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;
387 int end=up ? -1 : rowCount()-1, mod=up ? -1 : 1;
388 foreach (Video *video, movedVideos) {
390 int row = rowForVideo(video);
391 if (row+mod==end) { end=row; continue; }
392 // qDebug() << "video row" << row;
393 removeRows(row, 1, QModelIndex());
398 beginInsertRows(QModelIndex(), row, row);
399 videos.insert(row, video);
404 emit needSelectionFor(movedVideos);