|
|
|
/***************************************************************************
|
|
|
|
Playlist.h - description
|
|
|
|
-------------------
|
|
|
|
begin : Don Dez 5 2002
|
|
|
|
copyright : (C) 2002 by Mark Kretschmann
|
|
|
|
(C) 2005 Ian Monroe
|
|
|
|
(C) 2005 by Gábor Lehel
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
* *
|
|
|
|
* 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. *
|
|
|
|
* *
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
|
|
#ifndef AMAROK_PLAYLIST_H
|
|
|
|
#define AMAROK_PLAYLIST_H
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
#include "amarok_export.h"
|
|
|
|
#include "amarokconfig.h"
|
|
|
|
#include "amarokdcophandler.h"
|
|
|
|
#include "engineobserver.h" //baseclass
|
|
|
|
#include "dynamicmode.h"
|
|
|
|
#include "playlistwindow.h" //friend
|
|
|
|
#include "playlistitem.h"
|
|
|
|
#include "metabundle.h"
|
|
|
|
#include "tooltip.h" //baseclass
|
|
|
|
#include "tracktooltip.h"
|
|
|
|
|
|
|
|
#include <tdelistview.h> //baseclass
|
|
|
|
#include <kurl.h> //KURL::List
|
|
|
|
#include <tqdir.h> //stack allocated
|
|
|
|
#include <tqpoint.h> //stack allocated
|
|
|
|
#include <tqptrlist.h> //stack allocated
|
|
|
|
#include <tqstringlist.h> //stack allocated
|
|
|
|
#include <vector> //stack allocated
|
|
|
|
|
|
|
|
class TDEAction;
|
|
|
|
class TDEActionCollection;
|
|
|
|
class PlaylistItem;
|
|
|
|
class PlaylistEntry;
|
|
|
|
class PlaylistLoader;
|
|
|
|
class PlaylistAlbum;
|
|
|
|
class TagWriter;
|
|
|
|
class TQBoxLayout;
|
|
|
|
class TQLabel;
|
|
|
|
class TQTimer;
|
|
|
|
|
|
|
|
class Medium;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @authors Mark Kretschmann && Max Howell
|
|
|
|
*
|
|
|
|
* Playlist inherits TDEListView privately and thus is no longer a ListView
|
|
|
|
* Instead it is a part of PlaylistWindow and they interact in harmony. The change
|
|
|
|
* was necessary as it is too dangerous to allow public access to PlaylistItems
|
|
|
|
* due to the multi-threading environment.
|
|
|
|
*
|
|
|
|
* Unfortunately, since TQObject is now inaccessible you have to connect slots
|
|
|
|
* via one of PlaylistWindow's friend members or in Playlist
|
|
|
|
*
|
|
|
|
* If you want to add new playlist type functionality you should implement it
|
|
|
|
* inside this class or inside PlaylistWindow.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
// template <class FieldType>
|
|
|
|
// AtomicString Index<FieldType>::fieldString(const FieldType &field) { return AtomicString(field); }
|
|
|
|
|
|
|
|
// template<>
|
|
|
|
// AtomicString Index<KURL>::fieldString(const KURL &field);
|
|
|
|
|
|
|
|
|
|
|
|
class Playlist : private TDEListView, public EngineObserver, public Amarok::ToolTipClient
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
~Playlist();
|
|
|
|
|
|
|
|
LIBAMAROK_EXPORT static Playlist *instance() { return s_instance; }
|
|
|
|
static TQString defaultPlaylistPath();
|
|
|
|
static const int NO_SORT = 200;
|
|
|
|
|
|
|
|
static const int Append = 1; /// inserts media after the last item in the playlist
|
|
|
|
static const int Queue = 2; /// inserts media after the currentTrack
|
|
|
|
static const int Clear = 4; /// clears the playlist first
|
|
|
|
static const int Replace = Clear;
|
|
|
|
static const int DirectPlay = 8; /// start playback of the first item in the list
|
|
|
|
static const int Unique = 16; /// don't insert anything already in the playlist
|
|
|
|
static const int StartPlay = 32; /// start playback of the first item in the list if nothing else playing
|
|
|
|
static const int Colorize = 64; /// colorize newly added items
|
|
|
|
static const int DefaultOptions = Append | Unique | StartPlay;
|
|
|
|
|
|
|
|
// it's really just the *ListView parts we want to hide...
|
|
|
|
TQScrollView *qscrollview() const
|
|
|
|
{
|
|
|
|
return reinterpret_cast<TQScrollView*>( const_cast<Playlist*>( this ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Add media to the playlist
|
|
|
|
* @param options you can OR these together, see the enum
|
|
|
|
* @param sql Sql program to execute */
|
|
|
|
LIBAMAROK_EXPORT void insertMedia( const KURL::List &, int options = Append );
|
|
|
|
void insertMediaSql( const TQString& sql, int options = Append );
|
|
|
|
|
|
|
|
// Dynamic mode functions
|
|
|
|
void addDynamicModeTracks( uint songCount );
|
|
|
|
void adjustDynamicUpcoming( bool saveUndo = false );
|
|
|
|
void adjustDynamicPrevious( uint songCount, bool saveUndo = false );
|
|
|
|
void advanceDynamicTrack();
|
|
|
|
void setDynamicHistory( bool enable = true );
|
|
|
|
|
|
|
|
void burnPlaylist ( int projectType = -1 );
|
|
|
|
void burnSelectedTracks( int projectType = -1 );
|
|
|
|
int currentTrackIndex( bool onlyCountVisible = true );
|
|
|
|
bool isEmpty() const { return childCount() == 0; }
|
|
|
|
LIBAMAROK_EXPORT bool isTrackBefore() const;
|
|
|
|
LIBAMAROK_EXPORT bool isTrackAfter() const;
|
|
|
|
void restoreSession(); // called during initialisation
|
|
|
|
void setPlaylistName( const TQString &name, bool proposeOverwriting = false ) { m_playlistName = name; m_proposeOverwriting = proposeOverwriting; }
|
|
|
|
void proposePlaylistName( const TQString &name, bool proposeOverwriting = false ) { if( isEmpty() || m_playlistName==i18n("Untitled") ) m_playlistName = name; m_proposeOverwriting = proposeOverwriting; }
|
|
|
|
const TQString &playlistName() const { return m_playlistName; }
|
|
|
|
bool proposeOverwriteOnSave() const { return m_proposeOverwriting; }
|
|
|
|
bool saveM3U( const TQString&, bool relative = AmarokConfig::relativePlaylist() ) const;
|
|
|
|
void saveXML( const TQString& );
|
|
|
|
int totalTrackCount() const;
|
|
|
|
BundleList nextTracks() const;
|
|
|
|
uint repeatAlbumTrackCount() const; //returns number of tracks from same album
|
|
|
|
//as current track that are in playlist (may require Play Albums in Order on).
|
|
|
|
//If the information is not available, returns 0.
|
|
|
|
|
|
|
|
//const so you don't change it behind Playlist's back, use modifyDynamicMode() for that
|
|
|
|
const DynamicMode *dynamicMode() const;
|
|
|
|
|
|
|
|
//modify the returned DynamicMode, then finishedModifying() it when done
|
|
|
|
DynamicMode *modifyDynamicMode();
|
|
|
|
|
|
|
|
//call this every time you modifyDynamicMode(), otherwise you'll get memory leaks and/or crashes
|
|
|
|
void finishedModifying( DynamicMode *mode );
|
|
|
|
|
|
|
|
int stopAfterMode();
|
|
|
|
|
|
|
|
void addCustomMenuItem ( const TQString &submenu, const TQString &itemTitle );
|
|
|
|
void customMenuClicked ( int id );
|
|
|
|
bool removeCustomMenuItem( const TQString &submenu, const TQString &itemTitle );
|
|
|
|
|
|
|
|
void setFont( const TQFont &f ) { TDEListView::setFont( f ); } //made public for convenience
|
|
|
|
void unsetFont() { TDEListView::unsetFont(); }
|
|
|
|
|
|
|
|
PlaylistItem *firstChild() const { return static_cast<PlaylistItem*>( TDEListView::firstChild() ); }
|
|
|
|
PlaylistItem *lastItem() const { return static_cast<PlaylistItem*>( TDEListView::lastItem() ); }
|
|
|
|
PlaylistItem *currentItem() const { return static_cast<PlaylistItem*>( TDEListView::currentItem() ); }
|
|
|
|
|
|
|
|
int numVisibleColumns() const;
|
|
|
|
TQValueList<int> visibleColumns() const;
|
|
|
|
MetaBundle::ColumnMask getVisibleColumnMask() const;
|
|
|
|
int mapToLogicalColumn( int physical ) const; // Converts physical PlaylistItem column position to logical
|
|
|
|
TQString columnText( int c ) const { return TDEListView::columnText( c ); };
|
|
|
|
void setColumns( TQValueList<int> order, TQValueList<int> visible );
|
|
|
|
|
|
|
|
/** Call this to prevent items being removed from the playlist, it is mostly for internal use only
|
|
|
|
* Don't forget to unlock() !! */
|
|
|
|
void lock();
|
|
|
|
void unlock();
|
|
|
|
|
|
|
|
//reimplemented to save columns by name instead of index, to be more resilient to reorderings and such
|
|
|
|
void saveLayout(TDEConfig *config, const TQString &group) const;
|
|
|
|
void restoreLayout(TDEConfig *config, const TQString &group);
|
|
|
|
|
|
|
|
//AFT-related functions
|
|
|
|
bool checkFileStatus( PlaylistItem * item );
|
|
|
|
void addToUniqueMap( const TQString uniqueid, PlaylistItem* item );
|
|
|
|
void removeFromUniqueMap( const TQString uniqueid, PlaylistItem* item );
|
|
|
|
|
|
|
|
enum RequestType { Prev = -1, Current = 0, Next = 1 };
|
|
|
|
enum StopAfterMode { DoNotStop, StopAfterCurrent, StopAfterQueue, StopAfterOther };
|
|
|
|
|
|
|
|
class TQDragObject *dragObject();
|
|
|
|
friend class PlaylistItem;
|
|
|
|
friend class UrlLoader;
|
|
|
|
friend class QueueManager;
|
|
|
|
friend class QueueLabel;
|
|
|
|
friend class PlaylistWindow;
|
|
|
|
friend class ColumnList;
|
|
|
|
friend void Amarok::DcopPlaylistHandler::removeCurrentTrack(); //calls removeItem() and currentTrack()
|
|
|
|
friend void Amarok::DcopPlaylistHandler::removeByIndex( int ); //calls removeItem()
|
|
|
|
friend class TagWriter; //calls removeItem()
|
|
|
|
friend void PlaylistWindow::init(); //setting up connections etc.
|
|
|
|
friend TrackToolTip::TrackToolTip();
|
|
|
|
friend bool PlaylistWindow::eventFilter( TQObject*, TQEvent* ); //for convenience we handle some playlist events here
|
|
|
|
|
|
|
|
public:
|
|
|
|
TQPair<TQString, TQRect> toolTipText( TQWidget*, const TQPoint &pos ) const;
|
|
|
|
|
|
|
|
signals:
|
|
|
|
void aboutToClear();
|
|
|
|
void itemCountChanged( int newCount, int newLength, int visCount, int visLength, int selCount, int selLength );
|
|
|
|
void queueChanged( const PLItemList &queued, const PLItemList &dequeued );
|
|
|
|
void columnsChanged();
|
|
|
|
void dynamicModeChanged( const DynamicMode *newMode );
|
|
|
|
|
|
|
|
public slots:
|
|
|
|
void activateByIndex(int);
|
|
|
|
void addCustomColumn();
|
|
|
|
void appendMedia( const KURL &url );
|
|
|
|
void appendMedia( const TQString &path );
|
|
|
|
void clear();
|
|
|
|
void copyToClipboard( const TQListViewItem* = 0 ) const;
|
|
|
|
void deleteSelectedFiles();
|
|
|
|
void ensureItemCentered( TQListViewItem* item );
|
|
|
|
void playCurrentTrack();
|
|
|
|
void playNextTrack( const bool forceNext = true );
|
|
|
|
void playPrevTrack();
|
|
|
|
void queueSelected();
|
|
|
|
void setSelectedRatings( int rating );
|
|
|
|
void redo();
|
|
|
|
void removeDuplicates();
|
|
|
|
void removeSelectedItems();
|
|
|
|
void setDynamicMode( DynamicMode *mode );
|
|
|
|
void loadDynamicMode( DynamicMode *mode ); //saveUndoState() + setDynamicMode()
|
|
|
|
void disableDynamicMode();
|
|
|
|
void editActiveDynamicMode();
|
|
|
|
void rebuildDynamicModeCache();
|
|
|
|
void repopulate();
|
|
|
|
void safeClear();
|
|
|
|
void scoreChanged( const TQString &path, float score );
|
|
|
|
void ratingChanged( const TQString &path, int rating );
|
|
|
|
void fileMoved( const TQString &srcPath, const TQString &dstPath );
|
|
|
|
void selectAll() { TQListView::selectAll( true ); }
|
|
|
|
void setFilter( const TQString &filter );
|
|
|
|
void setFilterSlot( const TQString &filter ); //uses a delay where applicable
|
|
|
|
void setStopAfterCurrent( bool on );
|
|
|
|
void setStopAfterItem( PlaylistItem *item );
|
|
|
|
void toggleStopAfterCurrentItem();
|
|
|
|
void toggleStopAfterCurrentTrack();
|
|
|
|
void setStopAfterMode( int mode );
|
|
|
|
void showCurrentTrack() { ensureItemCentered( m_currentTrack ); }
|
|
|
|
void showQueueManager();
|
|
|
|
void changeFromQueueManager(TQPtrList<PlaylistItem> list);
|
|
|
|
void shuffle();
|
|
|
|
void undo();
|
|
|
|
void updateMetaData( const MetaBundle& );
|
|
|
|
void adjustColumn( int n );
|
|
|
|
void updateEntriesUrl( const TQString &oldUrl, const TQString &newUrl, const TQString &uniqueid );
|
|
|
|
void updateEntriesUniqueId( const TQString &url, const TQString &oldid, const TQString &newid );
|
|
|
|
void updateEntriesStatusDeleted( const TQString &absPath, const TQString &uniqueid );
|
|
|
|
void updateEntriesStatusAdded( const TQString &absPath, const TQString &uniqueid );
|
|
|
|
void updateEntriesStatusAdded( const TQMap<TQString,TQString> &map );
|
|
|
|
|
|
|
|
protected:
|
|
|
|
virtual void fontChange( const TQFont &old );
|
|
|
|
|
|
|
|
protected slots:
|
|
|
|
void contentsMouseMoveEvent( TQMouseEvent *e = 0 );
|
|
|
|
void leaveEvent( TQEvent *e );
|
|
|
|
void contentsMousePressEvent( TQMouseEvent *e );
|
|
|
|
void contentsWheelEvent( TQWheelEvent *e );
|
|
|
|
|
|
|
|
private slots:
|
|
|
|
void mediumChange( int );
|
|
|
|
void slotCountChanged();
|
|
|
|
void activate( TQListViewItem* );
|
|
|
|
void columnOrderChanged();
|
|
|
|
void columnResizeEvent( int, int, int );
|
|
|
|
void doubleClicked( TQListViewItem* );
|
|
|
|
|
|
|
|
void generateInfo(); //generates info for Random Albums
|
|
|
|
|
|
|
|
/* the only difference multi makes is whether it emits queueChanged(). (if multi, then no)
|
|
|
|
if you're queue()ing many items, consider passing true and emitting queueChanged() yourself. */
|
|
|
|
/* if invertQueue then queueing an already queued song dequeues it */
|
|
|
|
void queue( TQListViewItem*, bool multi = false, bool invertQueue = true );
|
|
|
|
|
|
|
|
void saveUndoState();
|
|
|
|
void setDelayedFilter(); //after the delay is over
|
|
|
|
void showContextMenu( TQListViewItem*, const TQPoint&, int );
|
|
|
|
void slotEraseMarker();
|
|
|
|
void slotGlowTimer();
|
|
|
|
void reallyEnsureItemCentered();
|
|
|
|
void slotMouseButtonPressed( int, TQListViewItem*, const TQPoint&, int );
|
|
|
|
void slotSingleClick();
|
|
|
|
void slotContentsMoving();
|
|
|
|
void slotRepeatTrackToggled( int mode );
|
|
|
|
void slotQueueChanged( const PLItemList &in, const PLItemList &out);
|
|
|
|
void slotUseScores( bool use );
|
|
|
|
void slotUseRatings( bool use );
|
|
|
|
void slotMoodbarPrefs( bool show, bool moodier, int alter, bool withMusic );
|
|
|
|
void updateNextPrev();
|
|
|
|
void writeTag( TQListViewItem*, const TQString&, int );
|
|
|
|
|
|
|
|
private:
|
|
|
|
Playlist( TQWidget* );
|
|
|
|
Playlist( const Playlist& ); //not defined
|
|
|
|
|
|
|
|
LIBAMAROK_EXPORT static Playlist *s_instance;
|
|
|
|
|
|
|
|
void countChanged();
|
|
|
|
|
|
|
|
PlaylistItem *currentTrack() const { return m_currentTrack; }
|
|
|
|
PlaylistItem *restoreCurrentTrack();
|
|
|
|
|
|
|
|
void insertMediaInternal( const KURL::List&, PlaylistItem*, int options = 0 );
|
|
|
|
bool isAdvancedQuery( const TQString &query );
|
|
|
|
void refreshNextTracks( int = -1 );
|
|
|
|
void removeItem( PlaylistItem*, bool = false );
|
|
|
|
bool saveState( TQStringList& );
|
|
|
|
void setCurrentTrack( PlaylistItem* );
|
|
|
|
void setCurrentTrackPixmap( int state = -1 );
|
|
|
|
void showTagDialog( TQPtrList<TQListViewItem> items );
|
|
|
|
void sortQueuedItems();
|
|
|
|
void switchState( TQStringList&, TQStringList& );
|
|
|
|
void saveSelectedAsPlaylist();
|
|
|
|
void initStarPixmaps();
|
|
|
|
|
|
|
|
//engine observer functions
|
|
|
|
void engineNewMetaData( const MetaBundle&, bool );
|
|
|
|
void engineStateChanged( Engine::State, Engine::State = Engine::Empty );
|
|
|
|
|
|
|
|
/// TDEListView Overloaded functions
|
|
|
|
void contentsDropEvent ( TQDropEvent* );
|
|
|
|
void contentsDragEnterEvent( TQDragEnterEvent* );
|
|
|
|
void contentsDragMoveEvent ( TQDragMoveEvent* );
|
|
|
|
void contentsDragLeaveEvent( TQDragLeaveEvent* );
|
|
|
|
|
|
|
|
#ifdef PURIST //TDEListView imposes hand cursor so override it
|
|
|
|
void contentsMouseMoveEvent( TQMouseEvent *e ) { TQListView::contentsMouseMoveEvent( e ); }
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void customEvent( TQCustomEvent* );
|
|
|
|
bool eventFilter( TQObject*, TQEvent* );
|
|
|
|
void paletteChange( const TQPalette& );
|
|
|
|
void rename( TQListViewItem*, int );
|
|
|
|
void setColumnWidth( int, int );
|
|
|
|
void setSorting( int, bool = true );
|
|
|
|
|
|
|
|
void viewportPaintEvent( TQPaintEvent* );
|
|
|
|
void viewportResizeEvent( TQResizeEvent* );
|
|
|
|
|
|
|
|
void appendToPreviousTracks( PlaylistItem *item );
|
|
|
|
void appendToPreviousAlbums( PlaylistAlbum *album );
|
|
|
|
void removeFromPreviousTracks( PlaylistItem *item = 0 );
|
|
|
|
void removeFromPreviousAlbums( PlaylistAlbum *album = 0 );
|
|
|
|
|
|
|
|
typedef TQMap<AtomicString, PlaylistAlbum*> AlbumMap;
|
|
|
|
typedef TQMap<AtomicString, AlbumMap> ArtistAlbumMap;
|
|
|
|
ArtistAlbumMap m_albums;
|
|
|
|
uint m_startupTime_t; //TQDateTime::currentDateTime().toTime_t as of startup
|
|
|
|
uint m_oldestTime_t; //the createdate of the oldest song in the collection
|
|
|
|
|
|
|
|
|
|
|
|
/// ATTRIBUTES
|
|
|
|
|
|
|
|
PlaylistItem *m_currentTrack; //the track that is playing
|
|
|
|
TQListViewItem *m_marker; //track that has the drag/drop marker under it
|
|
|
|
PlaylistItem *m_hoveredRating; //if the mouse is hovering over the rating of an item
|
|
|
|
|
|
|
|
//NOTE these container types were carefully chosen
|
|
|
|
TQPtrList<PlaylistAlbum> m_prevAlbums; //the previously played albums in Entire Albums mode
|
|
|
|
PLItemList m_prevTracks; //the previous history
|
|
|
|
PLItemList m_nextTracks; //the tracks to be played after the current track
|
|
|
|
|
|
|
|
TQString m_filter;
|
|
|
|
TQString m_prevfilter;
|
|
|
|
TQTimer *m_filtertimer;
|
|
|
|
|
|
|
|
PLItemList m_itemsToChangeTagsFor;
|
|
|
|
|
|
|
|
bool m_smartResizing;
|
|
|
|
|
|
|
|
int m_firstColumn;
|
|
|
|
int m_totalCount;
|
|
|
|
int m_totalLength;
|
|
|
|
int m_selCount;
|
|
|
|
int m_selLength;
|
|
|
|
int m_visCount;
|
|
|
|
int m_visLength;
|
|
|
|
TQ_INT64 m_total; //for Favor Tracks
|
|
|
|
bool m_itemCountDirty;
|
|
|
|
|
|
|
|
TDEAction *m_undoButton;
|
|
|
|
TDEAction *m_redoButton;
|
|
|
|
TDEAction *m_clearButton;
|
|
|
|
|
|
|
|
TQDir m_undoDir;
|
|
|
|
TQStringList m_undoList;
|
|
|
|
TQStringList m_redoList;
|
|
|
|
uint m_undoCounter;
|
|
|
|
|
|
|
|
DynamicMode *m_dynamicMode;
|
|
|
|
KURL::List m_queueList;
|
|
|
|
PlaylistItem *m_stopAfterTrack;
|
|
|
|
int m_stopAfterMode;
|
|
|
|
bool m_showHelp;
|
|
|
|
bool m_dynamicDirt; //So we don't call advanceDynamicTrack() on activate()
|
|
|
|
bool m_queueDirt; //When queuing disabled items, we need to place the marker on the newly inserted item
|
|
|
|
bool m_undoDirt; //Make sure we don't repopulate the playlist when dynamic mode and undo()
|
|
|
|
int m_insertFromADT; //Don't automatically start playing if a user hits Next in dynamic mode when not already playing
|
|
|
|
static TQMutex *s_dynamicADTMutex;
|
|
|
|
|
|
|
|
TQListViewItem *m_itemToReallyCenter;
|
|
|
|
TQListViewItem *m_renameItem;
|
|
|
|
int m_renameColumn;
|
|
|
|
TQTimer *m_clicktimer;
|
|
|
|
TQListViewItem *m_itemToRename;
|
|
|
|
TQPoint m_clickPos;
|
|
|
|
int m_columnToRename;
|
|
|
|
|
|
|
|
TQMap<TQString, TQStringList> m_customSubmenuItem;
|
|
|
|
TQMap<int, TQString> m_customIdItem;
|
|
|
|
|
|
|
|
bool isLocked() const { return m_lockStack > 0; }
|
|
|
|
|
|
|
|
/// stack counter for PLaylist::lock() and unlock()
|
|
|
|
int m_lockStack;
|
|
|
|
|
|
|
|
TQString m_editOldTag; //text before inline editing ( the new tag is written only if it's changed )
|
|
|
|
|
|
|
|
std::vector<double> m_columnFraction;
|
|
|
|
|
|
|
|
TQMap<TQString,TQPtrList<PlaylistItem>*> m_uniqueMap;
|
|
|
|
int m_oldRandom;
|
|
|
|
int m_oldRepeat;
|
|
|
|
|
|
|
|
TQString m_playlistName;
|
|
|
|
bool m_proposeOverwriting;
|
|
|
|
|
|
|
|
// indexing stuff
|
|
|
|
// An index of playlist items by some field. The index is backed by AtomicStrings, to avoid
|
|
|
|
// duplication thread-safely.
|
|
|
|
template <class FieldType>
|
|
|
|
class Index : private TQMap<AtomicString, TQPtrList<PlaylistItem> >
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
// constructors take the PlaylistItem getter to index by
|
|
|
|
Index( FieldType (PlaylistItem::*getter)( ) const)
|
|
|
|
: m_getter( getter ), m_useGetter( true ) { };
|
|
|
|
Index( const FieldType &(PlaylistItem::*refGetter)() const)
|
|
|
|
: m_refGetter( refGetter ), m_useGetter( false ) { };
|
|
|
|
|
|
|
|
// we specialize this method, below, for KURLs
|
|
|
|
AtomicString fieldString( const FieldType &field) { return AtomicString( field ); }
|
|
|
|
|
|
|
|
AtomicString keyOf( const PlaylistItem &item) {
|
|
|
|
return m_useGetter ? fieldString( ( item.*m_getter ) () )
|
|
|
|
: fieldString( ( item.*m_refGetter ) () );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool contains( const FieldType &key ) { return contains( fieldString( key ) ); }
|
|
|
|
|
|
|
|
// Just first match, or NULL
|
|
|
|
PlaylistItem *getFirst( const FieldType &field ) {
|
|
|
|
Iterator it = find( fieldString( field ) );
|
|
|
|
return it == end() || it.data().isEmpty() ? 0 : it.data().getFirst();
|
|
|
|
}
|
|
|
|
|
|
|
|
void add( PlaylistItem *item ) {
|
|
|
|
TQPtrList<PlaylistItem> &row = operator[]( keyOf( *item ) ); // adds one if needed
|
|
|
|
if ( !row.containsRef(item) ) row.append( item );
|
|
|
|
}
|
|
|
|
|
|
|
|
void remove( PlaylistItem *item ) {
|
|
|
|
Iterator it = find( keyOf( *item ) );
|
|
|
|
if (it != end()) {
|
|
|
|
while ( it.data().removeRef( item ) ) { };
|
|
|
|
if ( it.data().isEmpty() ) erase( it );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
FieldType (PlaylistItem::*m_getter) () const;
|
|
|
|
const FieldType &(PlaylistItem::*m_refGetter) () const;
|
|
|
|
bool m_useGetter; // because a valid *member can be zero in C++
|
|
|
|
};
|
|
|
|
|
|
|
|
Index<KURL> m_urlIndex;
|
|
|
|
// TODO: we can convert m_unique to this, to remove some code and for uniformity and thread
|
|
|
|
// safety
|
|
|
|
// TODO: we should just store the url() as AtomicString, it will save headaches (e.g. at least a
|
|
|
|
// crash with multicore enabled traces back to KURL refcounting)
|
|
|
|
//Index<TQString> m_uniqueIndex;
|
|
|
|
};
|
|
|
|
|
|
|
|
class PlaylistAlbum
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
PLItemList tracks;
|
|
|
|
int refcount;
|
|
|
|
TQ_INT64 total; //for Favor Tracks
|
|
|
|
PlaylistAlbum(): refcount( 0 ), total( 0 ) { }
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Iterator class that only edits visible items! Preferentially always use
|
|
|
|
* this! Invisible items should not be operated on! To iterate over all
|
|
|
|
* items use MyIt::All as the flags parameter. MyIt::All cannot be OR'd,
|
|
|
|
* sorry.
|
|
|
|
*/
|
|
|
|
|
|
|
|
class PlaylistIterator : public TQListViewItemIterator
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
PlaylistIterator( TQListViewItem *item, int flags = 0 )
|
|
|
|
//TQListViewItemIterator is not great and doesn't allow you to see everything if you
|
|
|
|
//mask both Visible and Invisible :( instead just visible items are returned
|
|
|
|
: TQListViewItemIterator( item, flags == All ? 0 : flags | Visible )
|
|
|
|
{}
|
|
|
|
|
|
|
|
PlaylistIterator( TQListView *view, int flags = 0 )
|
|
|
|
: TQListViewItemIterator( view, flags == All ? 0 : flags | Visible )
|
|
|
|
{}
|
|
|
|
|
|
|
|
//FIXME! Dirty hack for enabled/disabled items.
|
|
|
|
enum IteratorFlag {
|
|
|
|
Visible = TQListViewItemIterator::Visible,
|
|
|
|
All = TQListViewItemIterator::Invisible
|
|
|
|
};
|
|
|
|
|
|
|
|
inline PlaylistItem *operator*() { return static_cast<PlaylistItem*>( TQListViewItemIterator::operator*() ); }
|
|
|
|
|
|
|
|
/// @return the next visible PlaylistItem after item
|
|
|
|
static PlaylistItem *nextVisible( PlaylistItem *item )
|
|
|
|
{
|
|
|
|
PlaylistIterator it( item );
|
|
|
|
return (*it == item) ? *static_cast<PlaylistIterator&>(++it) : *it;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PlaylistItem *prevVisible( PlaylistItem *item )
|
|
|
|
{
|
|
|
|
PlaylistIterator it( item );
|
|
|
|
return (*it == item) ? *static_cast<PlaylistIterator&>(--it) : *it;
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
// Specialization of Index::fieldString for URLs
|
|
|
|
template<>
|
|
|
|
inline AtomicString Playlist::Index<KURL>::fieldString( const KURL &url )
|
|
|
|
{
|
|
|
|
return AtomicString( url.url() );
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif //AMAROK_PLAYLIST_H
|
|
|
|
|