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