File indexing completed on 2024-05-19 04:49:50
0001 /**************************************************************************************** 0002 * Copyright (c) 2008 Seb Ruiz <ruiz@kde.org> * 0003 * Copyright (c) 2008 Soren Harward <stharward@gmail.com> * 0004 * Copyright (c) 2010 Nanno Langstraat <langstr@gmail.com> * 0005 * * 0006 * This program is free software; you can redistribute it and/or modify it under * 0007 * the terms of the GNU General Public License as published by the Free Software * 0008 * Foundation; either version 2 of the License, or (at your option) version 3 or * 0009 * any later version accepted by the membership of KDE e.V. (or its successor approved * 0010 * by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of * 0011 * version 3 of the license. * 0012 * * 0013 * This program is distributed in the hope that it will be useful, but WITHOUT ANY * 0014 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * 0015 * PARTICULAR PURPOSE. See the GNU General Public License for more details. * 0016 * * 0017 * You should have received a copy of the GNU General Public License along with * 0018 * this program. If not, see <http://www.gnu.org/licenses/>. * 0019 ****************************************************************************************/ 0020 0021 #ifndef AMAROK_NONLINEARTRACKNAVIGATOR_H 0022 #define AMAROK_NONLINEARTRACKNAVIGATOR_H 0023 0024 #include "TrackNavigator.h" 0025 0026 #include <QList> 0027 #include <QSet> 0028 0029 namespace Playlist 0030 { 0031 /** 0032 * Base class that offers some standard services for non-linear navigators. 0033 * - Deterministic back/forward in history. 0034 * - High-performance keep-in-sync with changes in the source model. 0035 * - 'm_plannedItems', a standard way to plan ahead. 0036 * 0037 * The simplest use case for a child class is to provide an implementation of 'planOne()'. 0038 * 0039 * A slightly more advanced use case for a child class is to override 0040 * 'notifyItemsInserted()', 'notifyItemsRemoved()', and 'setCurrentItem()'. 0041 * 0042 * If a child class really needs to do something special, it can override 0043 * 'requestNextTrack()' etc., as long as it tries the return value of the base 0044 * implementation first. 0045 */ 0046 class NonlinearTrackNavigator : public TrackNavigator 0047 { 0048 Q_OBJECT 0049 0050 public: 0051 NonlinearTrackNavigator(); 0052 0053 //! Overrides from 'TrackNavigator' 0054 quint64 likelyNextTrack() override; 0055 quint64 requestNextTrack() override; 0056 quint64 requestUserNextTrack() override { return requestNextTrack(); } 0057 0058 quint64 likelyLastTrack() override; 0059 quint64 requestLastTrack() override; 0060 0061 static const int MAX_HISTORY_SIZE = 1000; // This is probably enough for even the most crazed user. 0062 0063 /** 0064 * 'setCurrentItem()' is guaranteed to be called whenever an item is removed from 'm_plannedItems'. 0065 */ 0066 quint64 currentItem(); 0067 virtual void setCurrentItem( const quint64 newItem, bool goingBackward = false ); 0068 0069 /** 0070 * All items currently in the source model. 0071 * 0072 * The QList is not kept in the same order as the source model. We could add 0073 * that guarantee, but currently no navigator needs it. 0074 * 0075 * The current reason for the QList variant is that RandomTrackNavigator needs 0076 * a list, and it would give bad performance to convert the whole playlist 0077 * from QSet to QList on every 'planOne()' call. 0078 * 0079 * The QSet variant currently has O(n) performance. (Nothing needs O(1).) 0080 */ 0081 QList<quint64> allItemsList() { doItemListsMaintenance(); return m_allItemsList; } 0082 QSet<quint64> allItemsSet() { doItemListsMaintenance(); QSet<quint64> r_allItemsSet(m_allItemsList.begin(), m_allItemsList.end()); return r_allItemsSet; } 0083 0084 /** 0085 * Items that we've played. 0086 */ 0087 QList<quint64> historyItems() { doItemListsMaintenance(); return m_historyItems; } 0088 0089 protected: 0090 /** 0091 * Load the contents of the source model. 0092 * 0093 * The bottom-most child class constructor should call this function. We must 0094 * not do it in our constructor or in intermediate constructors, because it 0095 * causes calls to the virtual functions below. And virtual functions don't do 0096 * what you want while you're in a base class constructor. 0097 */ 0098 void loadFromSourceModel(); 0099 0100 /** 0101 * Request-callback for child classes: try to make sure that there's at least 0102 * 1 item in 'm_plannedItems'. 0103 */ 0104 virtual void planOne() = 0; 0105 0106 /** 0107 * Notification-callback for child classes: Playlist items have been inserted. 0108 */ 0109 virtual void notifyItemsInserted( const QSet<quint64> &insertedItems ) = 0; 0110 0111 /** 0112 * Notification-callback for child classes: Playlist items have been removed. 0113 * 0114 * 'currentItem()' still has its old (possibly obsolete) value when this function is called. 0115 * This function can call 'setCurrentItem()' if it knows a good new choice. 0116 * 0117 * Note: by the time this notification gets called, the playlist items are 0118 * usually no longer present in the source model. 0119 */ 0120 virtual void notifyItemsRemoved( const QSet<quint64> &removedItems ) = 0; 0121 0122 /** 0123 * List of planned-ahead playlist items. Should be edited by child classes. 0124 */ 0125 QList<quint64> m_plannedItems; 0126 0127 private Q_SLOTS: 0128 void slotModelReset(); 0129 void slotRowsInserted( const QModelIndex& parent, int start, int end ); 0130 void slotRowsAboutToBeRemoved( const QModelIndex& parent, int start, int end ); 0131 0132 void slotActiveTrackChanged( const quint64 ); 0133 0134 private: 0135 /** 0136 * Maintain internal tem lists. Call before doing anything with internal state. 0137 */ 0138 void doItemListsMaintenance(); 0139 0140 /** 0141 * Choose the playlist item list that we'll take our next item from. 0142 */ 0143 ItemList* nextItemChooseDonorList(); 0144 0145 0146 /** 0147 * These variables hold inserted/removed items until we batch-process them. 0148 * This is due to performance requirements of slotRowsInserted/slotRowsAboutToBeRemoved. 0149 */ 0150 QSet<quint64> m_insertedItems; 0151 QSet<quint64> m_removedItems; 0152 0153 0154 /** 0155 * All items in the source model. See comments at 'allItemsList()'. 0156 */ 0157 QList<quint64> m_allItemsList; 0158 0159 /** 0160 * List of playlist items we already played. May contain items multiple times. 0161 */ 0162 QList<quint64> m_historyItems; 0163 0164 /** 0165 * The playlist item we most recently suggested to the rest of Amarok. 0166 */ 0167 quint64 m_currentItem; 0168 0169 /** 0170 * List of playlist items that had already made it to 'm_history', but have been 0171 * taken out again and re-played because the user did 'requestLastTrack()'. 0172 * 0173 * Makes 'requestNextTrack()' after 'requestLastTrack()' work predictably. 0174 * 0175 * We maintain this separately from 'm_plannedItems' because when new item are 0176 * inserted, they are mixed randomly with 'm_plannedItems'. This list shouldn't 0177 * change under those circumstances. 0178 */ 0179 QList<quint64> m_replayedItems; 0180 }; 0181 0182 } 0183 0184 #endif