Commit 37f111cd authored by Michael Pyne's avatar Michael Pyne

OK, here it is. This commit introduces a new feature to JuK, the upcoming

playlist (which is currently easily the #1 requested feature).  Although
there's still issues to be solved with it, it seems to work pretty well at this
point, I've been running this code for a few days now.

How it works so far:

* You must enable it by selecting "Show Upcoming Playlist" from the View menu.
* When the upcoming playlist is enabled, it takes over control of playback
  completely.  You can drag-and-drop tracks onto the playlist to add them to
  the end of the line, or you can use the context menu's Add to end of upcoming
  playlist entry.
* If loop playback is disabled, then entries will be added to the end of the
  playlist as entries disappear.
* Hitting Next (or double-clicking an item while a track is playing) will cause
  the currently playing track to disappear.  The History playlist doesn't play
  too well with the upcoming playlist yet, so if you want to keep the songs you
  played, you're better off making a normal playlist.
* On that note, double-clicking a song will add it to the beginning of the
  queue and immediately start playing it.
* Random play should work as normal.  If it doesn't, it's a bug.
* When the list becomes empty, playback stops.
* There is also a selection in the Settings menu, "Save Upcoming Tracks", which
  will save the current status of the upcoming playlist on exit.

This is a rather sizeable re-organization/addition of code, so if you
experience crashes/bugs in the next few days, PLEASE report them, and you can
probably assume it's my fault. =D

This feature will probably be tweaked over the next few days as well, but I
wanted to get it out there for testing.

I'm closing bug 63260 since this implements the feature.  If you'd like to
quibble on the specifics, feel free to continue commenting on the bug, I'll add
myself to the CC: list for it.

CCMAIL:63260-done@bugs.kde.org

svn path=/trunk/kdemultimedia/juk/; revision=338993
parent 73853661
......@@ -48,7 +48,10 @@ juk_SOURCES = \
tagguesserconfigdlgwidget.ui \
trackpickerdialog.cpp \
trackpickerdialogbase.ui \
tracksequenceiterator.cpp \
tracksequencemanager.cpp \
treeviewitemplaylist.cpp \
upcomingplaylist.cpp \
ktrm.cpp \
viewmode.cpp
......
......@@ -27,6 +27,7 @@
#include "tag.h"
#include "searchplaylist.h"
#include "historyplaylist.h"
#include "upcomingplaylist.h"
#include "playlistcollection.h"
#include "actioncollection.h"
......@@ -34,7 +35,7 @@ using namespace ActionCollection;
Cache *Cache::m_cache = 0;
static const int playlistCacheVersion = 2;
enum PlaylistType { Normal = 0, Search = 1, History = 2 };
enum PlaylistType { Normal = 0, Search = 1, History = 2, Upcoming = 3 };
////////////////////////////////////////////////////////////////////////////////
// public methods
......@@ -135,6 +136,15 @@ void Cache::loadPlaylists(PlaylistCollection *collection) // static
playlist = collection->historyPlaylist();
break;
}
case Upcoming:
{
UpcomingPlaylist *p = new UpcomingPlaylist(collection);
action<KToggleAction>("saveUpcomingTracks")->setChecked(true);
s >> *p;
playlist = p;
collection->setUpcomingPlaylist(p);
break;
}
default:
Playlist *p = new Playlist(collection, true);
s >> *p;
......@@ -192,6 +202,12 @@ void Cache::savePlaylists(const PlaylistList &playlists)
s << Q_INT32(Search)
<< *static_cast<SearchPlaylist *>(*it);
}
else if(dynamic_cast<UpcomingPlaylist *>(*it)) {
if(!action<KToggleAction>("saveUpcomingTracks")->isChecked())
continue;
s << Q_INT32(Upcoming)
<< *static_cast<UpcomingPlaylist *>(*it);
}
else {
s << Q_INT32(Normal)
<< *(*it);
......
......@@ -151,6 +151,9 @@ void JuK::setupActions()
new KToggleAction(i18n("Popup &Track Announcement"),
KShortcut(), this, 0, actions(), "togglePopups");
new KToggleAction(i18n("Save &Upcoming Tracks"),
KShortcut(), this, 0, actions(), "saveUpcomingTracks");
connect(m_toggleSystemTrayAction, SIGNAL(toggled(bool)),
this, SLOT(slotToggleSystemTray(bool)));
......
<!DOCTYPE kpartgui>
<kpartgui name="juk" version="3">
<kpartgui name="juk" version="5">
<MenuBar>
<Menu name="file" noMerge="1"><text>&amp;File</text>
<Action name="file_new"/>
......@@ -27,6 +27,7 @@
<Action name="showSearch"/>
<Action name="showEditor"/>
<Action name="showHistory"/>
<Action name="showUpcoming"/>
<Action name="showColumns"/>
<Separator/>
......@@ -68,6 +69,7 @@
<Action name="tagGuesserConfig" append="save_merge"/>
<Action name="fileRenamerConfig" append="save_merge"/>
<Action name="outputSelect" append="save_merge"/>
<Action name="saveUpcomingTracks" append="save_merge"/>
</Menu>
</MenuBar>
......
This diff is collapsed.
......@@ -38,6 +38,8 @@ class PlaylistCollection;
class PlaylistItem;
class PlaylistToolTip;
class UpcomingPlaylist;
typedef QValueList<PlaylistItem *> PlaylistItemList;
typedef QValueList<Playlist *> PlaylistList;
......@@ -137,6 +139,11 @@ public:
*/
PlaylistItemList selectedItems();
/**
* Returns properly casted first child item in list.
*/
PlaylistItem *firstChild() const;
/**
* Allow duplicate files in the playlist.
*/
......@@ -163,7 +170,7 @@ public:
QListViewItem *after = 0,
bool emitChanged = true);
virtual void createItems(const PlaylistItemList &siblings);
virtual void createItems(const PlaylistItemList &siblings, PlaylistItem *after = 0);
/**
* This handles adding files of various types -- music, playlist or directory
......@@ -367,6 +374,7 @@ protected:
virtual bool canDecode(QMimeSource *s);
virtual void decode(QMimeSource *s, PlaylistItem *after = 0);
virtual void contentsDropEvent(QDropEvent *e);
virtual void contentsMouseDoubleClickEvent(QMouseEvent *e);
virtual void showEvent(QShowEvent *e);
virtual bool acceptDrag(QDropEvent *e) const { return KURLDrag::canDecode(e); }
virtual void viewportPaintEvent(QPaintEvent *pe);
......@@ -383,6 +391,10 @@ protected:
*/
virtual void polish();
UpcomingPlaylist *upcomingPlaylist() const { return m_upcomingPlaylist; }
void setUpcomingPlaylist(UpcomingPlaylist *playlist) { m_upcomingPlaylist = playlist; }
/**
* Do some finial initialization of created items. Notably ensure that they
* are shown or hidden based on the contents of the current PlaylistSearch.
......@@ -397,7 +409,7 @@ protected:
* ItemType should be a PlaylistItem subclass.
*/
template <class CollectionItemType, class ItemType, class SiblingType>
void createItems(const QValueList<SiblingType *> &siblings);
void createItems(const QValueList<SiblingType *> &siblings, ItemType *after = 0);
protected slots:
void slotPopulateBackMenu() const;
......@@ -429,7 +441,7 @@ private:
/** Sets up album random play to play songs with the same album as
* the given PlaylistItem.
*/
void initAlbumSearch(const PlaylistItem *item);
// void initAlbumSearch(const PlaylistItem *item);
/**
* Load the playlist from a file. \a fileName should be the absolute path.
......@@ -481,6 +493,8 @@ private slots:
void slotUpdateColumnWidths();
void slotAddToUpcoming();
/**
* Show the RMB menu. Matches the signature for the signal
* QListView::contextMenuRequested().
......@@ -544,6 +558,7 @@ private:
int m_currentColumn;
int m_processed;
int m_rmbEditID;
int m_rmbUpcomingID;
int m_selectedCount;
bool m_allowDuplicates;
......@@ -560,9 +575,10 @@ private:
bool m_widthsDirty;
PlaylistItemList m_randomList;
PlaylistItemList m_history;
// PlaylistItemList m_history;
static PlaylistItemList m_history;
PlaylistSearch m_search;
PlaylistSearch m_albumSearch;
// PlaylistSearch m_albumSearch;
bool m_searchEnabled;
......@@ -594,7 +610,7 @@ private:
static bool m_visibleChanged;
static int m_leftColumn;
static PlaylistItem *m_playingItem;
static PlaylistItem *m_playNextItem;
static UpcomingPlaylist *m_upcomingPlaylist;
static QMap<int, PlaylistItem *> m_backMenuItems;
};
......@@ -643,13 +659,13 @@ ItemType *Playlist::createItem(const FileHandle &file, QListViewItem *after,
}
template <class CollectionItemType, class ItemType, class SiblingType>
void Playlist::createItems(const QValueList<SiblingType *> &siblings)
void Playlist::createItems(const QValueList<SiblingType *> &siblings, ItemType *after)
{
if(siblings.isEmpty())
return;
m_disableColumnWidthUpdates = true;
ItemType *newItem = 0;
ItemType *newItem = after;
QValueListConstIterator<SiblingType *> it = siblings.begin();
for(; it != siblings.end(); ++it) {
......
......@@ -29,12 +29,14 @@
#include "collectionlist.h"
#include "dynamicplaylist.h"
#include "historyplaylist.h"
#include "upcomingplaylist.h"
#include "viewmode.h"
#include "searchplaylist.h"
#include "treeviewitemplaylist.h"
#include "actioncollection.h"
#include "cache.h"
#include "k3bexporter.h"
#include "tracksequencemanager.h"
using namespace ActionCollection;
......@@ -126,6 +128,12 @@ PlaylistBox::PlaylistBox(QWidget *parent, QWidgetStack *playlistStack,
Cache::loadPlaylists(this);
raise(CollectionList::instance());
TrackSequenceManager::instance()->setCurrentPlaylist(CollectionList::instance());
// We need to wait until after Collection List is created to set this up.
setupUpcomingPlaylist();
setSorting(-1); // Disable sorting for speed
performTreeViewSetup();
......@@ -214,7 +222,12 @@ Playlist *PlaylistBox::currentPlaylist() const
void PlaylistBox::setupPlaylist(Playlist *playlist, const QString &iconName)
{
PlaylistCollection::setupPlaylist(playlist, iconName);
new Item(this, iconName, playlist->name(), playlist);
if(iconName == "upcoming_playlist") {
kdDebug(65432) << "Setting up upcoming playlist after Collection List\n";
new Item(this, iconName, playlist->name(), playlist, m_playlistDict[CollectionList::instance()]);
}
else
new Item(this, iconName, playlist->name(), playlist);
}
void PlaylistBox::setupPlaylist(Playlist *playlist, const QString &iconName, Item *parentItem)
......@@ -310,8 +323,14 @@ void PlaylistBox::remove()
setSingleItem(i);
}
for(PlaylistList::ConstIterator it = removeQueue.begin(); it != removeQueue.end(); ++it)
delete *it;
for(PlaylistList::ConstIterator it = removeQueue.begin(); it != removeQueue.end(); ++it) {
if(*it != upcomingPlaylist())
delete *it;
else {
action<KToggleAction>("showUpcoming")->setChecked(false);
setUpcomingPlaylistEnabled(false);
}
}
}
void PlaylistBox::slotPlaylistDestroyed(Playlist *p)
......@@ -565,10 +584,16 @@ void PlaylistBox::slotPlaylistChanged()
if(singlePlaylist) {
playlistStack()->raiseWidget(playlists.front());
TrackSequenceManager::instance()->setCurrentPlaylist(playlists.front());
dataChanged(); // Update the status bar
delete m_dynamicPlaylist;
m_dynamicPlaylist = 0;
if(playlists.front() == upcomingPlaylist())
action("deleteItemPlaylist")->setText(i18n("Hid&e"));
else
action("deleteItemPlaylist")->setText(i18n("R&emove"));
}
else if(!playlists.isEmpty()) {
DynamicPlaylist *p = new DynamicPlaylist(playlists, this, i18n("Dynamic List"), "midi", false);
......@@ -576,6 +601,8 @@ void PlaylistBox::slotPlaylistChanged()
delete m_dynamicPlaylist;
m_dynamicPlaylist = p;
TrackSequenceManager::instance()->setCurrentPlaylist(m_dynamicPlaylist);
}
}
......@@ -620,14 +647,23 @@ void PlaylistBox::performTreeViewSetup()
m_treeViewSetup = true;
}
void PlaylistBox::setupUpcomingPlaylist()
{
KConfigGroup config(KGlobal::config(), "Playlists");
bool enable = config.readBoolEntry("showUpcoming", false);
setUpcomingPlaylistEnabled(enable);
action<KToggleAction>("showUpcoming")->setChecked(enable);
}
////////////////////////////////////////////////////////////////////////////////
// PlaylistBox::Item protected methods
////////////////////////////////////////////////////////////////////////////////
PlaylistBox::Item *PlaylistBox::Item::m_collectionItem = 0;
PlaylistBox::Item::Item(PlaylistBox *listBox, const QString &icon, const QString &text, Playlist *l)
: QObject(listBox), KListViewItem(listBox, text),
PlaylistBox::Item::Item(PlaylistBox *listBox, const QString &icon, const QString &text, Playlist *l, Item *after)
: QObject(listBox), KListViewItem(listBox, after, text),
m_playlist(l), m_text(text), m_iconName(icon), m_sortedFirst(false)
{
init();
......@@ -648,6 +684,12 @@ PlaylistBox::Item::~Item()
int PlaylistBox::Item::compare(QListViewItem *i, int col, bool) const
{
Item *otherItem = static_cast<Item *>(i);
PlaylistBox *playlistBox = static_cast<PlaylistBox *>(listView());
if(m_playlist == playlistBox->upcomingPlaylist() && otherItem->m_playlist != CollectionList::instance())
return -1;
if(otherItem->m_playlist == playlistBox->upcomingPlaylist() && m_playlist != CollectionList::instance())
return 1;
if(m_sortedFirst && !otherItem->m_sortedFirst)
return -1;
......@@ -720,7 +762,7 @@ void PlaylistBox::Item::init()
static_cast<TreeViewMode *>(list->viewMode())->setupCategories();
}
if(m_playlist == list->historyPlaylist())
if(m_playlist == list->historyPlaylist() || m_playlist == list->upcomingPlaylist())
m_sortedFirst = true;
}
......
......@@ -91,6 +91,7 @@ private:
void setupItem(Item *item);
void performTreeViewSetup();
void setupUpcomingPlaylist();
int viewModeIndex() const { return m_viewModeIndex; }
ViewMode *viewMode() const { return m_viewModes[m_viewModeIndex]; }
......@@ -141,7 +142,7 @@ public:
virtual ~Item();
protected:
Item(PlaylistBox *listBox, const QString &icon, const QString &text, Playlist *l = 0);
Item(PlaylistBox *listBox, const QString &icon, const QString &text, Playlist *l = 0, Item *after = 0);
Item(Item *parent, const QString &icon, const QString &text, Playlist *l = 0);
Playlist *playlist() const { return m_playlist; }
......
......@@ -25,6 +25,7 @@
#include "searchplaylist.h"
#include "folderplaylist.h"
#include "historyplaylist.h"
#include "upcomingplaylist.h"
#include "directorylist.h"
#include "mediafiles.h"
#include "playermanager.h"
......@@ -49,6 +50,7 @@ using namespace ActionCollection;
PlaylistCollection::PlaylistCollection(QWidgetStack *playlistStack) :
m_playlistStack(playlistStack),
m_historyPlaylist(0),
m_upcomingPlaylist(0),
m_importPlaylists(true),
m_searchEnabled(true),
m_playing(false)
......@@ -419,6 +421,42 @@ void PlaylistCollection::setHistoryPlaylistEnabled(bool enable)
}
}
UpcomingPlaylist *PlaylistCollection::upcomingPlaylist() const
{
return m_upcomingPlaylist;
}
void PlaylistCollection::setUpcomingPlaylistEnabled(bool enable)
{
if((action<KToggleAction>("showUpcoming")->isChecked() && m_upcomingPlaylist) ||
(!enable && !m_upcomingPlaylist))
{
return;
}
if(enable) {
action<KToggleAction>("showUpcoming")->setChecked(true);
if(!m_upcomingPlaylist)
m_upcomingPlaylist = new UpcomingPlaylist(this);
m_upcomingPlaylist->initialize();
setupPlaylist(m_upcomingPlaylist, "upcoming_playlist");
raise(m_upcomingPlaylist);
}
else {
action<KToggleAction>("showUpcoming")->setChecked(false);
bool raiseCollection = m_playlistStack->visibleWidget() == m_upcomingPlaylist;
delete m_upcomingPlaylist;
m_upcomingPlaylist = 0;
if(raiseCollection) {
kapp->processEvents(); // Seems to stop a crash, weird.
raise(CollectionList::instance());
}
}
}
QObject *PlaylistCollection::object() const
{
return m_actionHandler;
......@@ -555,8 +593,8 @@ void PlaylistCollection::readConfig()
{
KConfigGroup config(KGlobal::config(), "Playlists");
m_importPlaylists = config.readBoolEntry("ImportPlaylists", true);
m_folderList = config.readPathListEntry("DirectoryList");
m_importPlaylists = config.readBoolEntry("ImportPlaylists", true);
m_folderList = config.readPathListEntry("DirectoryList");
QObject::connect(&m_dirWatch, SIGNAL(dirty(const QString &)),
object(), SLOT(slotDirChanged(const QString &)));
......@@ -574,6 +612,7 @@ void PlaylistCollection::saveConfig()
{
KConfigGroup config(KGlobal::config(), "Playlists");
config.writeEntry("ImportPlaylists", m_importPlaylists);
config.writeEntry("showUpcoming", action<KToggleAction>("showUpcoming")->isChecked());
config.writePathEntry("DirectoryList", m_folderList);
}
......@@ -633,8 +672,14 @@ PlaylistCollection::ActionHandler::ActionHandler(PlaylistCollection *collection)
new KToggleAction(i18n("Show &History"), "history", 0, actions(), "showHistory");
historyAction->setCheckedState(i18n("Hide &History"));
KToggleAction *upcomingAction =
new KToggleAction(i18n("Show &Upcoming Playlist"), "upcoming_playlist", 0, actions(), "showUpcoming");
upcomingAction->setCheckedState(i18n("Hide &Upcoming Playlist"));
connect(action<KToggleAction>("showHistory"), SIGNAL(toggled(bool)),
this, SLOT(slotSetHistoryPlaylistEnabled(bool)));
connect(action<KToggleAction>("showUpcoming"), SIGNAL(toggled(bool)),
this, SLOT(slotSetUpcomingPlaylistEnabled(bool)));
}
KAction *PlaylistCollection::ActionHandler::createAction(const QString &text,
......
......@@ -30,6 +30,7 @@ class KAction;
class Playlist;
class PlaylistItem;
class HistoryPlaylist;
class UpcomingPlaylist;
typedef QValueList<PlaylistItem *> PlaylistItemList;
......@@ -97,6 +98,16 @@ public:
HistoryPlaylist *historyPlaylist() const;
void setHistoryPlaylistEnabled(bool enable);
UpcomingPlaylist *upcomingPlaylist() const;
void setUpcomingPlaylistEnabled(bool enable);
/**
* Sets the playlist without initializing it (for use by Cache before GUI
* is completely setup). Call setUpcomingPlaylistEnabled(true) when the
* rest of initialization is ready.
*/
void setUpcomingPlaylist(UpcomingPlaylist *p) { m_upcomingPlaylist = p; }
void dirChanged(const QString &path);
QObject *object() const;
......@@ -126,9 +137,10 @@ private:
void readConfig();
void saveConfig();
QWidgetStack *m_playlistStack;
HistoryPlaylist *m_historyPlaylist;
ActionHandler *m_actionHandler;
QWidgetStack *m_playlistStack;
HistoryPlaylist *m_historyPlaylist;
UpcomingPlaylist *m_upcomingPlaylist;
ActionHandler *m_actionHandler;
KDirWatch m_dirWatch;
StringHash m_playlistNames;
......@@ -184,6 +196,7 @@ private slots:
void slotSetSearchEnabled(bool enable) { m_collection->setSearchEnabled(enable); }
void slotSetHistoryPlaylistEnabled(bool enable) {m_collection->setHistoryPlaylistEnabled(enable); }
void slotSetUpcomingPlaylistEnabled(bool enable) { m_collection->setUpcomingPlaylistEnabled(enable); }
void slotEnableDirWatch(bool enable) { m_collection->enableDirWatch(enable); }
void slotDirChanged(const QString &path) { m_collection->dirChanged(path); }
......
......@@ -23,7 +23,6 @@
#include "musicbrainzquery.h"
#include "tag.h"
#include "actioncollection.h"
#include "ktrm.h"
static void startMusicBrainzQuery(const FileHandle &file)
......
......@@ -18,6 +18,7 @@
#include <klistview.h>
#include <ksharedptr.h>
#include <kdebug.h>
#include <qvaluevector.h>
#include <qptrdict.h>
......@@ -43,6 +44,7 @@ class PlaylistItem : public KListViewItem
{
friend class Playlist;
friend class SearchPlaylist;
friend class UpcomingPlaylist;
friend class CollectionList;
friend class CollectionListItem;
friend class QPtrDict<PlaylistItem>;
......@@ -100,6 +102,16 @@ public:
*/
virtual void clear();
/**
* Returns properly casted item below this one.
*/
PlaylistItem *itemBelow() { return static_cast<PlaylistItem *>(KListViewItem::itemBelow()); }
/**
* Returns properly casted item above this one.
*/
PlaylistItem *itemAbove() { return static_cast<PlaylistItem *>(KListViewItem::itemAbove()); }
protected:
/**
* Items should always be created using Playlist::createItem() or through a
......@@ -150,4 +162,11 @@ private:
bool m_playing;
};
inline kdbgstream &operator<<(kdbgstream &s, const PlaylistItem &item)
{
s << item.text(PlaylistItem::TrackColumn);
return s;
}
#endif
#include <kaction.h>
#include <kdebug.h>
#include <stdlib.h>
#include "tracksequenceiterator.h"
#include "playlist.h"
#include "actioncollection.h"
#include "tag.h"
#include "filehandle.h"
using namespace ActionCollection;
TrackSequenceIterator::TrackSequenceIterator() :
m_current(0)
{
}
TrackSequenceIterator::TrackSequenceIterator(const TrackSequenceIterator &other) :
m_current(other.m_current)
{
}
TrackSequenceIterator::~TrackSequenceIterator()
{
}
void TrackSequenceIterator::setCurrent(PlaylistItem *current)
{
m_current = current;
}
DefaultSequenceIterator::DefaultSequenceIterator() :
TrackSequenceIterator()
{
}
DefaultSequenceIterator::DefaultSequenceIterator(const DefaultSequenceIterator &other)
: TrackSequenceIterator(other)
{
}
DefaultSequenceIterator::~DefaultSequenceIterator()
{
}
void DefaultSequenceIterator::advance()
{
if(!current())
return;
bool isRandom = action("randomPlay") && action<KToggleAction>("randomPlay")->isChecked();
bool loop = action("loopPlaylist") && action<KToggleAction>("loopPlaylist")->isChecked();
bool albumRandom = action("albumRandomPlay") && action<KToggleAction>("albumRandomPlay")->isChecked();
if(isRandom || albumRandom) {
if(m_randomItems.isEmpty() && loop)
refillRandomList();
if(m_randomItems.isEmpty()) {
setCurrent(0);
return;
}
PlaylistItem *item;
if(albumRandom) {
if(m_albumSearch.isNull() || m_albumSearch.matchedItems().isEmpty()) {
item = m_randomItems[::random() % m_randomItems.count()];
initAlbumSearch(item);
}
// This can be null if initAlbumSearch() left the m_albumSearch
// empty because the album text was empty.
if(!m_albumSearch.isNull()) {
PlaylistItemList albumMatches = m_albumSearch.matchedItems();
item = albumMatches[0];
// Pick first song
for(unsigned i = 0; i < albumMatches.count(); ++i)
if(albumMatches[i]->file().tag()->track() < item->file().tag()->track())
item = albumMatches[i];
m_albumSearch.clearItem(item);
if(m_albumSearch.matchedItems().isEmpty()) {
m_albumSearch.clearComponents();
m_albumSearch.search();
}
}
}
else
item = m_randomItems[::random() % m_randomItems.count()];
setCurrent(item);
m_randomItems.remove(item);
}
else {
PlaylistItem *next = static_cast<PlaylistItem *>(current()->itemBelow());
if(!next && loop) {
Playlist *p = current()->playlist();
next = static_cast<PlaylistItem *>(p->firstChild());
}
setCurrent(next);
}
}
void DefaultSequenceIterator::backup()
{
if(!current())
return;
PlaylistItem *item = static_cast<PlaylistItem *>(current()->itemAbove());
if(item)
setCurrent(item);
}
void DefaultSequenceIterator::prepareToPlay(Playlist *playlist)
{
bool random = action("randomPlay") && action<KToggleAction>("randomPlay")->isChecked();
bool albumRandom = action("albumRandomPlay") && action<KToggleAction>("albumRandomPlay")->isChecked();
if(random || albumRandom) {
PlaylistItemList items = playlist->selectedItems();
if(items.isEmpty())
items = playlist->visibleItems();
PlaylistItem *newItem = 0;
if(!items.isEmpty())
newItem = items[::random() % items.count()];
setCurrent(newItem);
refillRandomList();
}
else {
QListViewItemIterator it(playlist, QListViewItemIterator::Visible | QListViewItemIterator::Selected);
if(!it.current())
it = QListViewItemIterator(playlist, QListViewItemIterator::Visible);
setCurrent(static_cast<PlaylistItem *>(it.current()));
}
}
void DefaultSequenceIterator::reset()
{
m_randomItems.clear();
m_albumSearch.clearComponents();
m_albumSearch.search();
setCurrent(0);
}
void DefaultSequenceIterator::setCurrent(PlaylistItem *current)
{
PlaylistItem *oldCurrent = this->current();
TrackSequenceIterator::setCurrent(current);
bool random = action("randomPlay") && action<KToggleAction>("randomPlay")->isChecked();
bool albumRandom = action("albumRandomPlay") && action<KToggleAction>("albumRandomPlay")->isChecked();
if((albumRandom || random) && current && m_randomItems.isEmpty()) {
// We're setting a current item, refill the random list now, and remove
// the current item.
refillRandomList();
}
m_randomItems.remove(current);
if(albumRandom && current && !oldCurrent) {
// Same idea as above
initAlbumSearch(current);
m_albumSearch.clearItem(current);
}
}
DefaultSequenceIterator *DefaultSequenceIterator::clone() const
{
return new DefaultSequenceIterator(*this);
}
void DefaultSequenceIterator::refillRandomList()
{
if(!current())
return;
Playlist *p = current()->playlist();
if(!p) {
kdError(65432) << k_funcinfo << "Item has no playlist!\n";
return;
}
m_randomItems = p->visibleItems();
m_randomItems.remove(current());