]> git.sur5r.net Git - minitube/blobdiff - src/playlistmodel.cpp
Upload 3.9.3-2 to unstable
[minitube] / src / playlistmodel.cpp
index 63cd6a3ff675d2d04c087f8ef0c9cd8976e4bf89..f2e0e5f32d0f5c9a10c4c6e84e3dfa85e7c311e8 100644 (file)
@@ -19,23 +19,26 @@ along with Minitube.  If not, see <http://www.gnu.org/licenses/>.
 $END_LICENSE */
 
 #include "playlistmodel.h"
+#include "mediaview.h"
+#include "playlistitemdelegate.h"
+#include "searchparams.h"
+#include "video.h"
 #include "videomimedata.h"
 #include "videosource.h"
-#include "ytsearch.h"
-#include "video.h"
-#include "searchparams.h"
-#include "mediaview.h"
 
-static const int maxItems = 50;
-static const QString recentKeywordsKey = "recentKeywords";
-static const QString recentChannelsKey = "recentChannels";
+#include "searchvideosource.h"
+
+namespace {
+const QString recentKeywordsKey = "recentKeywords";
+const QString recentChannelsKey = "recentChannels";
+} // namespace
 
 PlaylistModel::PlaylistModel(QWidget *parent) : QAbstractListModel(parent) {
-    videoSource = 0;
+    videoSource = nullptr;
     searching = false;
     canSearchMore = true;
     firstSearch = false;
-    m_activeVideo = 0;
+    m_activeVideo = nullptr;
     m_activeRow = -1;
     startIndex = 1;
     max = 0;
@@ -44,41 +47,37 @@ PlaylistModel::PlaylistModel(QWidget *parent) : QAbstractListModel(parent) {
     authorPressed = false;
 }
 
-int PlaylistModel::rowCount(const QModelIndex &/*parent*/) const {
+int PlaylistModel::rowCount(const QModelIndex & /*parent*/) const {
     int count = videos.size();
-    
+
     // add the message item
-    if (videos.isEmpty() || !searching)
-        count++;
-    
+    if (videos.isEmpty() || !searching) count++;
+
     return count;
 }
 
 QVariant PlaylistModel::data(const QModelIndex &index, int role) const {
-    
     int row = index.row();
-    
+
     if (row == videos.size()) {
-        
         QPalette palette;
-        
+
         switch (role) {
         case ItemTypeRole:
             return ItemTypeShowMore;
         case Qt::DisplayRole:
             if (!errorMessage.isEmpty()) return errorMessage;
-            if (searching) return tr("Searching...");
+            if (searching) return QString(); // tr("Searching...");
             if (canSearchMore) return tr("Show %1 More").arg("").simplified();
-            if (videos.isEmpty()) return tr("No videos");
-            else return tr("No more videos");
+            if (videos.isEmpty())
+                return tr("No videos");
+            else
+                return tr("No more videos");
         case Qt::TextAlignmentRole:
             return QVariant(int(Qt::AlignHCenter | Qt::AlignVCenter));
         case Qt::ForegroundRole:
-            if (!errorMessage.isEmpty())
-                return palette.color(QPalette::ToolTipText);
-            else
-                return palette.color(QPalette::Dark);
-        case Qt::BackgroundColorRole:
+            return palette.color(QPalette::Dark);
+        case Qt::BackgroundRole:
             if (!errorMessage.isEmpty())
                 return palette.color(QPalette::ToolTipBase);
             else
@@ -86,12 +85,12 @@ QVariant PlaylistModel::data(const QModelIndex &index, int role) const {
         default:
             return QVariant();
         }
-        
+
     } else if (row < 0 || row >= videos.size())
         return QVariant();
-    
+
     Video *video = videos.at(row);
-    
+
     switch (role) {
     case ItemTypeRole:
         return ItemTypeVideo;
@@ -100,7 +99,7 @@ QVariant PlaylistModel::data(const QModelIndex &index, int role) const {
     case ActiveTrackRole:
         return video == m_activeVideo;
     case Qt::DisplayRole:
-        return video->title();
+        return video->getTitle();
     case HoveredItemRole:
         return hoveredRow == index.row();
     case AuthorHoveredRole:
@@ -112,172 +111,166 @@ QVariant PlaylistModel::data(const QModelIndex &index, int role) const {
         return video->description();
         */
     }
-    
+
     return QVariant();
 }
 
 void PlaylistModel::setActiveRow(int row, bool notify) {
-    if ( rowExists( row ) ) {
-        
+    if (rowExists(row)) {
         m_activeRow = row;
+        Video *previousVideo = m_activeVideo;
         m_activeVideo = videoAt(row);
-        
+
         int oldactiverow = m_activeRow;
-        
-        if ( rowExists( oldactiverow ) )
-            emit dataChanged( createIndex( oldactiverow, 0 ), createIndex( oldactiverow, columnCount() - 1 ) );
-        
-        emit dataChanged( createIndex( m_activeRow, 0 ), createIndex( m_activeRow, columnCount() - 1 ) );
-        if (notify) emit activeRowChanged(row);
-        
+
+        if (rowExists(oldactiverow))
+            emit dataChanged(createIndex(oldactiverow, 0),
+                             createIndex(oldactiverow, columnCount() - 1));
+
+        emit dataChanged(createIndex(m_activeRow, 0), createIndex(m_activeRow, columnCount() - 1));
+        if (notify) emit activeVideoChanged(m_activeVideo, previousVideo);
+
     } else {
         m_activeRow = -1;
-        m_activeVideo = 0;
+        m_activeVideo = nullptr;
     }
-
 }
 
 int PlaylistModel::nextRow() const {
     int nextRow = m_activeRow + 1;
-    if (rowExists(nextRow))
-        return nextRow;
+    if (rowExists(nextRow)) return nextRow;
     return -1;
 }
 
 int PlaylistModel::previousRow() const {
     int prevRow = m_activeRow - 1;
-    if (rowExists(prevRow))
-        return prevRow;
+    if (rowExists(prevRow)) return prevRow;
     return -1;
 }
 
-Video* PlaylistModel::videoAt( int row ) const {
-    if ( rowExists( row ) )
-        return videos.at( row );
-    return 0;
+Video *PlaylistModel::videoAt(int row) const {
+    if (rowExists(row)) return videos.at(row);
+    return nullptr;
 }
 
-VideoPlaylistModel::activeVideo() const {
+Video *PlaylistModel::activeVideo() const {
     return m_activeVideo;
 }
 
 void PlaylistModel::setVideoSource(VideoSource *videoSource) {
     beginResetModel();
-    while (!videos.isEmpty()) delete videos.takeFirst();
+
+    qDeleteAll(videos);
     videos.clear();
-    m_activeVideo = 0;
+
+    qDeleteAll(deletedVideos);
+    deletedVideos.clear();
+
+    m_activeVideo = nullptr;
     m_activeRow = -1;
     startIndex = 1;
     endResetModel();
 
     this->videoSource = videoSource;
-    connect(videoSource, SIGNAL(gotVideos(QList<Video*>)),
-            SLOT(addVideos(QList<Video*>)), Qt::UniqueConnection);
-    connect(videoSource, SIGNAL(finished(int)),
-            SLOT(searchFinished(int)), Qt::UniqueConnection);
-    connect(videoSource, SIGNAL(error(QString)),
-            SLOT(searchError(QString)), Qt::UniqueConnection);
+    connect(videoSource, SIGNAL(gotVideos(QVector<Video *>)), SLOT(addVideos(QVector<Video *>)),
+            Qt::UniqueConnection);
+    connect(videoSource, SIGNAL(finished(int)), SLOT(searchFinished(int)), Qt::UniqueConnection);
+    connect(videoSource, SIGNAL(error(QString)), SLOT(searchError(QString)), Qt::UniqueConnection);
+    connect(videoSource, &QObject::destroyed, this,
+            [this, videoSource] {
+                if (this->videoSource == videoSource) {
+                    this->videoSource = nullptr;
+                }
+            },
+            Qt::UniqueConnection);
 
+    canSearchMore = true;
     searchMore();
 }
 
-void PlaylistModel::searchMore(int max) {
-    if (searching) return;
+void PlaylistModel::searchMore() {
+    if (!canSearchMore || videoSource == nullptr || searching) return;
     searching = true;
     firstSearch = startIndex == 1;
-    this->max = max;
+    max = videoSource->maxResults();
+    if (max == 0) max = 20;
     errorMessage.clear();
     videoSource->loadVideos(max, startIndex);
     startIndex += max;
 }
 
-void PlaylistModel::searchMore() {
-    searchMore(maxItems);
-}
-
 void PlaylistModel::searchNeeded() {
     const int desiredRowsAhead = 10;
     int remainingRows = videos.size() - m_activeRow;
-    if (remainingRows < desiredRowsAhead)
-        searchMore(maxItems);
+    if (remainingRows < desiredRowsAhead) searchMore();
 }
 
 void PlaylistModel::abortSearch() {
     QMutexLocker locker(&mutex);
     beginResetModel();
-    // while (!videos.isEmpty()) delete videos.takeFirst();
-    // if (videoSource) videoSource->abort();
+    if (videoSource) videoSource->abort();
+    qDeleteAll(videos);
     videos.clear();
+    videos.squeeze();
     searching = false;
     m_activeRow = -1;
-    m_activeVideo = 0;
+    m_activeVideo = nullptr;
     startIndex = 1;
     endResetModel();
 }
 
 void PlaylistModel::searchFinished(int total) {
+    Q_UNUSED(total);
     searching = false;
     canSearchMore = videoSource->hasMoreVideos();
 
     // update the message item
-    emit dataChanged( createIndex( maxItems, 0 ), createIndex( maxItems, columnCount() - 1 ) );
+    emit dataChanged(createIndex(videos.size(), 0), createIndex(videos.size(), columnCount() - 1));
 
-    if (!videoSource->getSuggestions().isEmpty())
-        emit haveSuggestions(videoSource->getSuggestions());
-
-    if (firstSearch && !videos.isEmpty())
-        handleFirstVideo(videos.first());
+    if (firstSearch && !videos.isEmpty()) handleFirstVideo(videos.at(0));
 }
 
 void PlaylistModel::searchError(const QString &message) {
     errorMessage = message;
     // update the message item
-    emit dataChanged( createIndex( maxItems, 0 ), createIndex( maxItems, columnCount() - 1 ) );
+    emit dataChanged(createIndex(videos.size(), 0), createIndex(videos.size(), columnCount() - 1));
 }
 
-void PlaylistModel::addVideos(QList<Video*> newVideos) {
+void PlaylistModel::addVideos(const QVector<Video *> &newVideos) {
     if (newVideos.isEmpty()) return;
+    videos.reserve(videos.size() + newVideos.size());
     beginInsertRows(QModelIndex(), videos.size(), videos.size() + newVideos.size() - 2);
     videos.append(newVideos);
     endInsertRows();
-    foreach (Video* video, newVideos) {
-        connect(video, SIGNAL(gotThumbnail()),
-                SLOT(updateVideoSender()), Qt::UniqueConnection);
-        video->loadThumbnail();
-        qApp->processEvents();
+    for (Video *video : newVideos) {
+        connect(video, &Video::changed, this, [video, this] {
+            int row = rowForVideo(video);
+            emit dataChanged(createIndex(row, 0), createIndex(row, columnCount() - 1));
+        });
     }
 }
 
 void PlaylistModel::handleFirstVideo(Video *video) {
-
+    QSettings settings;
     int currentVideoRow = rowForCloneVideo(MediaView::instance()->getCurrentVideoId());
-    if (currentVideoRow != -1) setActiveRow(currentVideoRow, false);
+    if (currentVideoRow != -1)
+        setActiveRow(currentVideoRow, false);
     else {
-        QSettings settings;
-        if (!settings.value("manualplay", false).toBool())
-            setActiveRow(0);
-    }
-
-    QSettings settings;
-    if (!settings.value("manualplay", false).toBool()) {
-        int newActiveRow = rowForCloneVideo(MediaView::instance()->getCurrentVideoId());
-        if (newActiveRow != -1) setActiveRow(newActiveRow, false);
-        else setActiveRow(0);
+        if (!settings.value("manualplay", false).toBool()) setActiveRow(0);
     }
 
-    if (videoSource->metaObject()->className() == QLatin1String("YTSearch")) {
-
-        static const int maxRecentElements = 10;
-
-        YTSearch *search = qobject_cast<YTSearch *>(videoSource);
+    auto clazz = videoSource->metaObject()->className();
+    if (clazz == QLatin1String("SearchVideoSource")) {
+        auto search = qobject_cast<SearchVideoSource *>(videoSource);
         SearchParams *searchParams = search->getSearchParams();
 
         // save keyword
+        static const int maxRecentElements = 10;
         QString query = searchParams->keywords();
         if (!query.isEmpty() && !searchParams->isTransient()) {
             if (query.startsWith("http://")) {
                 // Save the video title
-                query += "|" + videos.first()->title();
+                query += "|" + videos.at(0)->getTitle();
             }
             QStringList keywords = settings.value(recentKeywordsKey).toStringList();
             keywords.removeAll(query);
@@ -291,9 +284,11 @@ void PlaylistModel::handleFirstVideo(Video *video) {
         QString channelId = searchParams->channelId();
         if (!channelId.isEmpty() && !searchParams->isTransient()) {
             QString value;
-            if (!video->channelId().isEmpty() && video->channelId() != video->channelTitle())
-                value = video->channelId() + "|" + video->channelTitle();
-            else value = video->channelTitle();
+            if (!video->getChannelId().isEmpty() &&
+                video->getChannelId() != video->getChannelTitle())
+                value = video->getChannelId() + "|" + video->getChannelTitle();
+            else
+                value = video->getChannelTitle();
             QStringList channels = settings.value(recentChannelsKey).toStringList();
             channels.removeAll(value);
             channels.removeAll(channelId);
@@ -305,58 +300,44 @@ void PlaylistModel::handleFirstVideo(Video *video) {
     }
 }
 
-void PlaylistModel::updateVideoSender() {
-    Video *video = static_cast<Video *>(sender());
-    if (!video) {
-        qDebug() << "Cannot get sender";
-        return;
-    }
-    int row = rowForVideo(video);
-    emit dataChanged( createIndex( row, 0 ), createIndex( row, columnCount() - 1 ) );
-}
-
 void PlaylistModel::emitDataChanged() {
-    QModelIndex index = createIndex(rowCount()-1, 0);
+    QModelIndex index = createIndex(rowCount() - 1, 0);
     emit dataChanged(index, index);
 }
 
 // --- item removal
 
-/**
-  * This function does not free memory
-  */
 bool PlaylistModel::removeRows(int position, int rows, const QModelIndex & /*parent*/) {
-    beginRemoveRows(QModelIndex(), position, position+rows-1);
+    beginRemoveRows(QModelIndex(), position, position + rows - 1);
     for (int row = 0; row < rows; ++row) {
-        videos.removeAt(position);
+        Video *video = videos.takeAt(position);
     }
     endRemoveRows();
     return true;
 }
 
 void PlaylistModel::removeIndexes(QModelIndexList &indexes) {
-    QList<Video*> originalList(videos);
-    QList<Video*> delitems;
-    foreach (const QModelIndex &index, indexes) {
+    QVector<Video *> originalList(videos);
+    for (const QModelIndex &index : indexes) {
         if (index.row() >= originalList.size()) continue;
-        Videovideo = originalList.at(index.row());
+        Video *video = originalList.at(index.row());
         int idx = videos.indexOf(video);
         if (idx != -1) {
             beginRemoveRows(QModelIndex(), idx, idx);
-            delitems.append(video);
+            deletedVideos.append(video);
+            if (m_activeVideo == video) {
+                m_activeVideo = nullptr;
+                m_activeRow = -1;
+            }
             videos.removeAll(video);
             endRemoveRows();
         }
     }
-
-    qDeleteAll(delitems);
-
+    videos.squeeze();
 }
 
 // --- Sturm und drang ---
 
-
-
 Qt::DropActions PlaylistModel::supportedDropActions() const {
     return Qt::CopyAction;
 }
@@ -370,7 +351,8 @@ Qt::ItemFlags PlaylistModel::flags(const QModelIndex &index) const {
         if (index.row() == videos.size()) {
             // don't drag the "show more" item
             return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
-        } else return (Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled);
+        } else
+            return (Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled);
     }
     return Qt::ItemIsDropEnabled;
 }
@@ -381,29 +363,27 @@ QStringList PlaylistModel::mimeTypes() const {
     return types;
 }
 
-QMimeData* PlaylistModel::mimeData( const QModelIndexList &indexes ) const {
-    VideoMimeDatamime = new VideoMimeData();
+QMimeData *PlaylistModel::mimeData(const QModelIndexList &indexes) const {
+    VideoMimeData *mime = new VideoMimeData();
 
-    foreach( const QModelIndex &it, indexes ) {
+    for (const QModelIndex &it : indexes) {
         int row = it.row();
-        if (row >= 0 && row < videos.size())
-            mime->addVideo( videos.at( it.row() ) );
+        if (row >= 0 && row < videos.size()) mime->addVideo(videos.at(it.row()));
     }
 
     return mime;
 }
 
 bool PlaylistModel::dropMimeData(const QMimeData *data,
-                                 Qt::DropAction action, int row, int column,
+                                 Qt::DropAction action,
+                                 int row,
+                                 int column,
                                  const QModelIndex &parent) {
-    if (action == Qt::IgnoreAction)
-        return true;
+    if (action == Qt::IgnoreAction) return true;
 
-    if (!data->hasFormat("application/x-minitube-video"))
-        return false;
+    if (!data->hasFormat("application/x-minitube-video")) return false;
 
-    if (column > 0)
-        return false;
+    if (column > 0) return false;
 
     int beginRow;
     if (row != -1)
@@ -413,21 +393,23 @@ bool PlaylistModel::dropMimeData(const QMimeData *data,
     else
         beginRow = rowCount(QModelIndex());
 
-    const VideoMimeData* videoMimeData = qobject_cast<const VideoMimeData*>( data );
-    if(!videoMimeData ) return false;
+    const VideoMimeData *videoMimeData = qobject_cast<const VideoMimeData *>(data);
+    if (!videoMimeData) return false;
 
-    QList<Video*> droppedVideos = videoMimeData->videos();
-    foreach( Video *video, droppedVideos) {
-        
+    const QVector<Video *> &droppedVideos = videoMimeData->getVideos();
+    for (Video *video : droppedVideos) {
         // remove videos
         int videoRow = videos.indexOf(video);
         removeRows(videoRow, 1, QModelIndex());
-        
+
         // and then add them again at the new position
         beginInsertRows(QModelIndex(), beginRow, beginRow);
-        videos.insert(beginRow, video);
+        if (beginRow >= videos.size()) {
+            videos.push_back(video);
+        } else {
+            videos.insert(beginRow, video);
+        }
         endInsertRows();
-
     }
 
     // fix m_activeRow after all this
@@ -437,7 +419,6 @@ bool PlaylistModel::dropMimeData(const QMimeData *data,
     emit needSelectionFor(droppedVideos);
 
     return true;
-
 }
 
 int PlaylistModel::rowForCloneVideo(const QString &videoId) const {
@@ -445,23 +426,23 @@ int PlaylistModel::rowForCloneVideo(const QString &videoId) const {
     for (int i = 0; i < videos.size(); ++i) {
         Video *v = videos.at(i);
         // qDebug() << "Comparing" << v->id() << videoId;
-        if (v->id() == videoId) return i;
+        if (v->getId() == videoId) return i;
     }
     return -1;
 }
 
-int PlaylistModel::rowForVideo(Videovideo) {
+int PlaylistModel::rowForVideo(Video *video) {
     return videos.indexOf(video);
 }
 
-QModelIndex PlaylistModel::indexForVideo(Videovideo) {
+QModelIndex PlaylistModel::indexForVideo(Video *video) {
     return createIndex(videos.indexOf(video), 0);
 }
 
 void PlaylistModel::move(QModelIndexList &indexes, bool up) {
-    QList<Video*> movedVideos;
+    QVector<Video *> movedVideos;
 
-    foreach (const QModelIndex &index, indexes) {
+    for (const QModelIndex &index : indexes) {
         int row = index.row();
         if (row >= videos.size()) continue;
         // qDebug() << "index row" << row;
@@ -469,25 +450,27 @@ void PlaylistModel::move(QModelIndexList &indexes, bool up) {
         movedVideos << video;
     }
 
-    int end=up ? -1 : rowCount()-1, mod=up ? -1 : 1;
-    foreach (Video *video, movedVideos) {
-
+    int end = up ? -1 : rowCount() - 1, mod = up ? -1 : 1;
+    for (Video *video : movedVideos) {
         int row = rowForVideo(video);
-        if (row+mod==end) { end=row; continue; }
+        if (row + mod == end) {
+            end = row;
+            continue;
+        }
         // qDebug() << "video row" << row;
         removeRows(row, 1, QModelIndex());
 
-        if (up) row--;
-        else row++;
+        if (up)
+            row--;
+        else
+            row++;
 
         beginInsertRows(QModelIndex(), row, row);
         videos.insert(row, video);
         endInsertRows();
-
     }
 
     emit needSelectionFor(movedVideos);
-
 }
 
 /* row hovering */
@@ -495,18 +478,18 @@ void PlaylistModel::move(QModelIndexList &indexes, bool up) {
 void PlaylistModel::setHoveredRow(int row) {
     int oldRow = hoveredRow;
     hoveredRow = row;
-    emit dataChanged( createIndex( oldRow, 0 ), createIndex( oldRow, columnCount() - 1 ) );
-    emit dataChanged( createIndex( hoveredRow, 0 ), createIndex( hoveredRow, columnCount() - 1 ) );
+    emit dataChanged(createIndex(oldRow, 0), createIndex(oldRow, columnCount() - 1));
+    emit dataChanged(createIndex(hoveredRow, 0), createIndex(hoveredRow, columnCount() - 1));
 }
 
 void PlaylistModel::clearHover() {
     int oldRow = hoveredRow;
     hoveredRow = -1;
-    emit dataChanged( createIndex( oldRow, 0 ), createIndex( oldRow, columnCount() - 1) );
+    emit dataChanged(createIndex(oldRow, 0), createIndex(oldRow, columnCount() - 1));
 }
 
 void PlaylistModel::updateHoveredRow() {
-    emit dataChanged( createIndex( hoveredRow, 0 ), createIndex( hoveredRow, columnCount() - 1 ) );
+    emit dataChanged(createIndex(hoveredRow, 0), createIndex(hoveredRow, columnCount() - 1));
 }
 
 /* clickable author */