|
|
|
/***************************************************************************
|
|
|
|
begin : Thu Aug 19 2004
|
|
|
|
copyright : (C) 2002 - 2004 by Michael Pyne
|
|
|
|
email : michael.pyne@kde.org
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
* *
|
|
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
|
|
* it under the terms of the GNU General Public License as published by *
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
|
|
* (at your option) any later version. *
|
|
|
|
* *
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
|
|
#include <tdeaction.h>
|
|
|
|
#include <tdeapplication.h>
|
|
|
|
#include <kdebug.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;
|
|
|
|
}
|
|
|
|
|
|
|
|
void TrackSequenceIterator::playlistChanged()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void TrackSequenceIterator::itemAboutToDie(const PlaylistItem *)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
DefaultSequenceIterator::DefaultSequenceIterator() :
|
|
|
|
TrackSequenceIterator()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
DefaultSequenceIterator::DefaultSequenceIterator(const DefaultSequenceIterator &other)
|
|
|
|
: TrackSequenceIterator(other)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
DefaultSequenceIterator::~DefaultSequenceIterator()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void DefaultSequenceIterator::advance()
|
|
|
|
{
|
|
|
|
if(!current())
|
|
|
|
return;
|
|
|
|
|
|
|
|
bool isRandom = action("randomPlay") && action<TDEToggleAction>("randomPlay")->isChecked();
|
|
|
|
bool loop = action("loopPlaylist") && action<TDEToggleAction>("loopPlaylist")->isChecked();
|
|
|
|
bool albumRandom = action("albumRandomPlay") && action<TDEToggleAction>("albumRandomPlay")->isChecked();
|
|
|
|
|
|
|
|
if(isRandom || albumRandom) {
|
|
|
|
if(m_randomItems.isEmpty() && loop) {
|
|
|
|
|
|
|
|
// Since refillRandomList will remove the currently playing item,
|
|
|
|
// we should clear it out first since that's not good for e.g.
|
|
|
|
// lists with 1-2 items. We need to remember the Playlist though.
|
|
|
|
|
|
|
|
Playlist *playlist = current()->playlist();
|
|
|
|
setCurrent(0);
|
|
|
|
|
|
|
|
refillRandomList(playlist);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(m_randomItems.isEmpty()) {
|
|
|
|
setCurrent(0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
PlaylistItem *item;
|
|
|
|
|
|
|
|
if(albumRandom) {
|
|
|
|
if(m_albumSearch.isNull() || m_albumSearch.matchedItems().isEmpty()) {
|
|
|
|
item = m_randomItems[TDEApplication::random() % m_randomItems.count()];
|
|
|
|
initAlbumSearch(item);
|
|
|
|
}
|
|
|
|
|
|
|
|
// This can be null if initAlbumSearch() left the m_albumSearch
|
|
|
|
// empty because the album text was empty. Since we initAlbumSearch()
|
|
|
|
// with an item, the matchedItems() should never be empty.
|
|
|
|
|
|
|
|
if(!m_albumSearch.isNull()) {
|
|
|
|
PlaylistItemList albumMatches = m_albumSearch.matchedItems();
|
|
|
|
if(albumMatches.isEmpty()) {
|
|
|
|
kdError(65432) << "Unable to initialize album random play.\n";
|
|
|
|
kdError(65432) << "List of potential results is empty.\n";
|
|
|
|
|
|
|
|
return; // item is still set to random song from a few lines earlier.
|
|
|
|
}
|
|
|
|
|
|
|
|
item = albumMatches[0];
|
|
|
|
|
|
|
|
// Pick first song remaining in list.
|
|
|
|
|
|
|
|
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
|
|
|
|
kdError(65432) << "Unable to perform album random play on " << *item << endl;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
item = m_randomItems[TDEApplication::random() % m_randomItems.count()];
|
|
|
|
|
|
|
|
setCurrent(item);
|
|
|
|
m_randomItems.remove(item);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
PlaylistItem *next = current()->itemBelow();
|
|
|
|
if(!next && loop) {
|
|
|
|
Playlist *p = current()->playlist();
|
|
|
|
next = p->firstChild();
|
|
|
|
while(next && !next->isVisible())
|
|
|
|
next = static_cast<PlaylistItem *>(next->nextSibling());
|
|
|
|
}
|
|
|
|
|
|
|
|
setCurrent(next);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DefaultSequenceIterator::backup()
|
|
|
|
{
|
|
|
|
if(!current())
|
|
|
|
return;
|
|
|
|
|
|
|
|
PlaylistItem *item = current()->itemAbove();
|
|
|
|
|
|
|
|
if(item)
|
|
|
|
setCurrent(item);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DefaultSequenceIterator::prepareToPlay(Playlist *playlist)
|
|
|
|
{
|
|
|
|
bool random = action("randomPlay") && action<TDEToggleAction>("randomPlay")->isChecked();
|
|
|
|
bool albumRandom = action("albumRandomPlay") && action<TDEToggleAction>("albumRandomPlay")->isChecked();
|
|
|
|
|
|
|
|
if(random || albumRandom) {
|
|
|
|
PlaylistItemList items = playlist->selectedItems();
|
|
|
|
if(items.isEmpty())
|
|
|
|
items = playlist->visibleItems();
|
|
|
|
|
|
|
|
PlaylistItem *newItem = 0;
|
|
|
|
if(!items.isEmpty())
|
|
|
|
newItem = items[TDEApplication::random() % items.count()];
|
|
|
|
|
|
|
|
setCurrent(newItem);
|
|
|
|
refillRandomList();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
TQListViewItemIterator it(playlist, TQListViewItemIterator::Visible | TQListViewItemIterator::Selected);
|
|
|
|
if(!it.current())
|
|
|
|
it = TQListViewItemIterator(playlist, TQListViewItemIterator::Visible);
|
|
|
|
|
|
|
|
setCurrent(static_cast<PlaylistItem *>(it.current()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DefaultSequenceIterator::reset()
|
|
|
|
{
|
|
|
|
m_randomItems.clear();
|
|
|
|
m_albumSearch.clearComponents();
|
|
|
|
m_albumSearch.search();
|
|
|
|
setCurrent(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DefaultSequenceIterator::playlistChanged()
|
|
|
|
{
|
|
|
|
refillRandomList();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DefaultSequenceIterator::itemAboutToDie(const PlaylistItem *item)
|
|
|
|
{
|
|
|
|
PlaylistItem *stfu_gcc = const_cast<PlaylistItem *>(item);
|
|
|
|
|
|
|
|
m_randomItems.remove(stfu_gcc);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DefaultSequenceIterator::setCurrent(PlaylistItem *current)
|
|
|
|
{
|
|
|
|
PlaylistItem *oldCurrent = DefaultSequenceIterator::current();
|
|
|
|
|
|
|
|
TrackSequenceIterator::setCurrent(current);
|
|
|
|
|
|
|
|
bool random = action("randomPlay") && action<TDEToggleAction>("randomPlay")->isChecked();
|
|
|
|
bool albumRandom = action("albumRandomPlay") && action<TDEToggleAction>("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(Playlist *p)
|
|
|
|
{
|
|
|
|
if(!p) {
|
|
|
|
if (!current())
|
|
|
|
return;
|
|
|
|
|
|
|
|
p = current()->playlist();
|
|
|
|
|
|
|
|
if(!p) {
|
|
|
|
kdError(65432) << k_funcinfo << "Item has no playlist!\n";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_randomItems = p->visibleItems();
|
|
|
|
m_randomItems.remove(current());
|
|
|
|
m_albumSearch.clearComponents();
|
|
|
|
m_albumSearch.search();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DefaultSequenceIterator::initAlbumSearch(PlaylistItem *searchItem)
|
|
|
|
{
|
|
|
|
if(!searchItem)
|
|
|
|
return;
|
|
|
|
|
|
|
|
m_albumSearch.clearPlaylists();
|
|
|
|
m_albumSearch.addPlaylist(searchItem->playlist());
|
|
|
|
|
|
|
|
ColumnList columns;
|
|
|
|
|
|
|
|
m_albumSearch.setSearchMode(PlaylistSearch::MatchAll);
|
|
|
|
m_albumSearch.clearComponents();
|
|
|
|
|
|
|
|
// If the album name is empty, it will mess up the search,
|
|
|
|
// so ignore empty album names.
|
|
|
|
|
|
|
|
if(searchItem->file().tag()->album().isEmpty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
columns.append(PlaylistItem::AlbumColumn);
|
|
|
|
|
|
|
|
m_albumSearch.addComponent(PlaylistSearch::Component(
|
|
|
|
searchItem->file().tag()->album(),
|
|
|
|
true,
|
|
|
|
columns,
|
|
|
|
PlaylistSearch::Component::Exact)
|
|
|
|
);
|
|
|
|
|
|
|
|
// If there is an Artist tag with the track, match against it as well
|
|
|
|
// to avoid things like multiple "Greatest Hits" albums matching the
|
|
|
|
// search.
|
|
|
|
|
|
|
|
if(!searchItem->file().tag()->artist().isEmpty()) {
|
|
|
|
kdDebug(65432) << "Searching both artist and album.\n";
|
|
|
|
columns[0] = PlaylistItem::ArtistColumn;
|
|
|
|
|
|
|
|
m_albumSearch.addComponent(PlaylistSearch::Component(
|
|
|
|
searchItem->file().tag()->artist(),
|
|
|
|
true,
|
|
|
|
columns,
|
|
|
|
PlaylistSearch::Component::Exact)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_albumSearch.search();
|
|
|
|
}
|