1 /***************************************************************************
2 Playlist.h - description
5 copyright : (C) 2002 by Mark Kretschmann
7 (C) 2005 by Gábor Lehel
8 ***************************************************************************/
10 /***************************************************************************
12 * This program is free software; you can redistribute it and/or modify *
13 * it under the terms of the GNU General Public License as published by *
14 * the Free Software Foundation; either version 2 of the License, or *
15 * (at your option) any later version. *
17 ***************************************************************************/
19 #ifndef AMAROK_PLAYLIST_H
20 #define AMAROK_PLAYLIST_H
22 #include "config-amarok.h"
23 #include "amarok_export.h"
24 #include "amarokconfig.h"
25 #include "engineobserver.h" //baseclass
26 #include "dynamicmode.h"
27 #include "MainWindow.h" //friend
28 #include "playlistitem.h"
29 #include "metabundle.h"
30 #include "tooltip.h" //baseclass
31 #include "tracktooltip.h"
33 #include <k3listview.h> //baseclass
34 #include <kurl.h> //KUrl::List
35 #include <QDir> //stack allocated
36 #include <qpoint.h> //stack allocated
37 #include <q3ptrlist.h> //stack allocated
38 #include <QStringList> //stack allocated
40 #include <QDragLeaveEvent>
41 #include <Q3ValueList>
43 #include <QDragEnterEvent>
44 #include <QDragMoveEvent>
46 #include <QResizeEvent>
47 #include <QMouseEvent>
49 #include <QPaintEvent>
50 #include <QWheelEvent>
51 #include <vector> //stack allocated
63 * @authors Mark Kretschmann && Max Howell
65 * Playlist inherits K3ListView privately and thus is no longer a ListView
66 * Instead it is a part of MainWindow and they interact in harmony. The change
67 * was necessary as it is too dangerous to allow public access to PlaylistItems
68 * due to the multi-threading environment.
70 * Unfortunately, since QObject is now inaccessible you have to connect slots
71 * via one of MainWindow's friend members or in Playlist
73 * If you want to add new playlist type functionality you should implement it
74 * inside this class or inside MainWindow.
79 class Playlist
: private K3ListView
, public EngineObserver
, public Amarok::ToolTipClient
86 AMAROK_EXPORT
static Playlist
*instance() { return s_instance
; }
87 static QString
defaultPlaylistPath();
88 static const int NO_SORT
= 200;
90 static const int Append
= 1; /// inserts media after the last item in the playlist
91 static const int Queue
= 2; /// inserts media after the currentTrack
92 static const int Clear
= 4; /// clears the playlist first
93 static const int Replace
= Clear
;
94 static const int DirectPlay
= 8; /// start playback of the first item in the list
95 static const int Unique
= 16; /// don't insert anything already in the playlist
96 static const int StartPlay
= 32; /// start playback of the first item in the list if nothing else playing
97 static const int Colorize
= 64; /// colorize newly added items
98 static const int DefaultOptions
= Append
| Unique
| StartPlay
;
100 // it's really just the *ListView parts we want to hide...
101 Q3ScrollView
*qscrollview() const
103 return reinterpret_cast<Q3ScrollView
*>( const_cast<Playlist
*>( this ) );
106 /** Add media to the playlist
107 * @param options you can OR these together, see the enum
108 * @param sql Sql program to execute */
109 AMAROK_EXPORT
void insertMedia( KUrl::List
, int options
= Append
);
110 void insertMediaSql( const QString
& sql
, int options
= Append
);
112 // Dynamic mode functions
113 void addDynamicModeTracks( uint songCount
);
114 void adjustDynamicUpcoming( bool saveUndo
= false );
115 void adjustDynamicPrevious( uint songCount
, bool saveUndo
= false );
116 void advanceDynamicTrack();
117 void setDynamicHistory( bool enable
= true );
119 void burnPlaylist ( int projectType
= -1 );
120 void burnSelectedTracks( int projectType
= -1 );
121 int currentTrackIndex( bool onlyCountVisible
= true );
122 bool isEmpty() const { return childCount() == 0; }
123 AMAROK_EXPORT
bool isTrackBefore() const;
124 AMAROK_EXPORT
bool isTrackAfter() const;
125 void restoreSession(); // called during initialisation
126 void setPlaylistName( const QString
&name
, bool proposeOverwriting
= false ) { m_playlistName
= name
; m_proposeOverwriting
= proposeOverwriting
; }
127 void proposePlaylistName( const QString
&name
, bool proposeOverwriting
= false ) { if( isEmpty() || m_playlistName
==i18n("Untitled") ) m_playlistName
= name
; m_proposeOverwriting
= proposeOverwriting
; }
128 const QString
&playlistName() const { return m_playlistName
; }
129 bool proposeOverwriteOnSave() const { return m_proposeOverwriting
; }
130 bool saveM3U( const QString
&, bool relative
= AmarokConfig::relativePlaylist() ) const;
131 void saveXML( const QString
& );
132 int totalTrackCount() const;
133 BundleList
nextTracks() const;
134 uint
repeatAlbumTrackCount() const; //returns number of tracks from same album
135 //as current track that are in playlist (may require Play Albums in Order on).
136 //If the information is not available, returns 0.
138 //const so you don't change it behind Playlist's back, use modifyDynamicMode() for that
139 const DynamicMode
*dynamicMode() const;
141 //modify the returned DynamicMode, then finishedModifying() it when done
142 DynamicMode
*modifyDynamicMode();
144 //call this every time you modifyDynamicMode(), otherwise you'll get memory leaks and/or crashes
145 void finishedModifying( DynamicMode
*mode
);
149 void addCustomMenuItem ( const QString
&submenu
, const QString
&itemTitle
);
150 void customMenuClicked ( int id
);
151 bool removeCustomMenuItem( const QString
&submenu
, const QString
&itemTitle
);
153 void setFont( const QFont
&f
) { K3ListView::setFont( f
); } //made public for convenience
154 void unsetFont() { K3ListView::setFont(QFont()); }
156 PlaylistItem
*firstChild() const { return static_cast<PlaylistItem
*>( K3ListView::firstChild() ); }
157 PlaylistItem
*lastItem() const { return static_cast<PlaylistItem
*>( K3ListView::lastItem() ); }
158 PlaylistItem
*currentItem() const { return static_cast<PlaylistItem
*>( K3ListView::currentItem() ); }
160 int numVisibleColumns() const;
161 Q3ValueList
<int> visibleColumns() const;
162 MetaBundle::ColumnMask
getVisibleColumnMask() const;
163 int mapToLogicalColumn( int physical
) const; // Converts physical PlaylistItem column position to logical
164 QString
columnText( int c
) const { return K3ListView::columnText( c
); }
165 void setColumns( Q3ValueList
<int> order
, Q3ValueList
<int> visible
);
167 /** Call this to prevent items being removed from the playlist, it is mostly for internal use only
168 * Don't forget to unlock() !! */
172 //reimplemented to save columns by name instead of index, to be more resilient to reorderings and such
173 void saveLayout(KSharedConfigPtr config
, const QString
&group
) const;
174 void restoreLayout(KSharedConfigPtr config
, const QString
&group
);
176 //AFT-related functions
177 bool checkFileStatus( PlaylistItem
* item
);
178 void addToUniqueMap( const QString uniqueid
, PlaylistItem
* item
);
179 void removeFromUniqueMap( const QString uniqueid
, PlaylistItem
* item
);
181 enum RequestType
{ Prev
= -1, Current
= 0, Next
= 1 };
182 enum StopAfterMode
{ DoNotStop
, StopAfterCurrent
, StopAfterQueue
, StopAfterOther
};
184 class Q3DragObject
*dragObject();
185 friend class PlaylistItem
;
186 friend class UrlLoader
;
187 friend class QueueManager
;
188 friend class QueueLabel
;
189 friend class MainWindow
;
190 friend class ColumnList
;
191 // friend void Amarok::DcopPlaylistHandler::removeCurrentTrack(); //calls removeItem() and currentTrack()
192 // friend void Amarok::DcopPlaylistHandler::removeByIndex( int ); //calls removeItem()
193 friend class TagWriter
; //calls removeItem()
194 friend void MainWindow::init(); //setting up connections etc.
195 friend TrackToolTip::TrackToolTip();
196 friend bool MainWindow::eventFilter( QObject
*, QEvent
* ); //for convenience we handle some playlist events here
199 QPair
<QString
, QRect
> toolTipText( QWidget
*, const QPoint
&pos
) const;
203 void itemCountChanged( int newCount
, int newLength
, int visCount
, int visLength
, int selCount
, int selLength
);
204 void queueChanged( const QList
<PlaylistItem
*> &queued
, const QList
<PlaylistItem
*> &dequeued
);
205 void columnsChanged();
206 void dynamicModeChanged( const DynamicMode
*newMode
);
209 void activateByIndex(int);
210 void addCustomColumn();
211 void appendMedia( const KUrl
&url
);
212 void appendMedia( const QString
&path
);
214 void copyToClipboard( const Q3ListViewItem
* = 0 ) const;
215 void deleteSelectedFiles();
216 void ensureItemCentered( Q3ListViewItem
* item
);
217 void playCurrentTrack();
218 void playNextTrack( const bool forceNext
= true );
219 void playPrevTrack();
220 void queueSelected();
221 void setSelectedRatings( int rating
);
223 void removeDuplicates();
224 void removeSelectedItems();
225 void setDynamicMode( DynamicMode
*mode
);
226 void loadDynamicMode( DynamicMode
*mode
); //saveUndoState() + setDynamicMode()
227 void disableDynamicMode();
228 void editActiveDynamicMode();
229 void rebuildDynamicModeCache();
231 void scoreChanged( const QString
&path
, float score
);
232 void ratingChanged( const QString
&path
, int rating
);
233 void fileMoved( const QString
&srcPath
, const QString
&dstPath
);
234 void selectAll() { Q3ListView::selectAll( true ); }
235 void setFilter( const QString
&filter
);
236 void setFilterSlot( const QString
&filter
); //uses a delay where applicable
237 void setStopAfterCurrent( bool on
);
238 void setStopAfterItem( PlaylistItem
*item
);
239 void toggleStopAfterCurrentItem();
240 void toggleStopAfterCurrentTrack();
241 void setStopAfterMode( int mode
);
242 void showCurrentTrack() { ensureItemCentered( m_currentTrack
); }
243 void showQueueManager();
244 void changeFromQueueManager( QList
<PlaylistItem
*> list
);
247 void updateMetaData( const MetaBundle
& );
248 void adjustColumn( int n
);
249 void updateEntriesUrl( const QString
&oldUrl
, const QString
&newUrl
, const QString
&uniqueid
);
250 void updateEntriesUniqueId( const QString
&url
, const QString
&oldid
, const QString
&newid
);
251 void updateEntriesStatusDeleted( const QString
&absPath
, const QString
&uniqueid
);
252 void updateEntriesStatusAdded( const QString
&absPath
, const QString
&uniqueid
);
253 void updateEntriesStatusAdded( const QMap
<QString
,QString
> &map
);
256 virtual void fontChange( const QFont
&old
);
259 void contentsMouseMoveEvent( QMouseEvent
*e
= 0 );
260 void leaveEvent( QEvent
*e
);
261 void contentsMousePressEvent( QMouseEvent
*e
);
262 void contentsWheelEvent( QWheelEvent
*e
);
265 void mediumChange( int );
266 void slotCountChanged();
267 void activate( Q3ListViewItem
* );
268 void columnOrderChanged();
269 void columnResizeEvent( int, int, int );
270 void doubleClicked( Q3ListViewItem
* );
272 void generateInfo(); //generates info for Random Albums
274 /* the only difference multi makes is whether it emits queueChanged(). (if multi, then no)
275 if you're queue()ing many items, consider passing true and emitting queueChanged() yourself. */
276 /* if invertQueue then queueing an already queued song dequeues it */
277 void queue( Q3ListViewItem
*, bool multi
= false, bool invertQueue
= true );
279 void saveUndoState();
280 void setDelayedFilter(); //after the delay is over
281 void showContextMenu( Q3ListViewItem
*, const QPoint
&, int );
282 void slotEraseMarker();
283 void slotGlowTimer();
284 void reallyEnsureItemCentered();
285 void slotMouseButtonPressed( int, Q3ListViewItem
*, const QPoint
&, int );
286 void slotSingleClick();
287 void slotContentsMoving();
288 void slotRepeatTrackToggled( int mode
);
289 void slotQueueChanged( const QList
<PlaylistItem
*> &in
, const QList
<PlaylistItem
*> &out
);
290 void slotUseScores( bool use
);
291 void slotUseRatings( bool use
);
292 void slotMoodbarPrefs( bool show
, bool moodier
, int alter
, bool withMusic
);
293 void updateNextPrev();
294 void writeTag( Q3ListViewItem
*, const QString
&, int );
297 Playlist( QWidget
* );
298 Playlist( const Playlist
& ); //not defined
300 AMAROK_EXPORT
static Playlist
*s_instance
;
304 PlaylistItem
*currentTrack() const { return m_currentTrack
; }
305 PlaylistItem
*restoreCurrentTrack();
307 void insertMediaInternal( const KUrl::List
&, PlaylistItem
*, int options
= 0 );
308 bool isAdvancedQuery( const QString
&query
);
309 void refreshNextTracks( int = -1 );
310 void removeItem( PlaylistItem
*, bool = false );
311 bool saveState( QStringList
& );
312 void setCurrentTrack( PlaylistItem
* );
313 void setCurrentTrackPixmap( int state
= -1 );
314 void showTagDialog( QList
<Q3ListViewItem
*> items
);
315 void sortQueuedItems();
316 void switchState( QStringList
&, QStringList
& );
317 void saveSelectedAsPlaylist();
318 void initStarPixmaps();
320 //engine observer functions
321 void engineNewMetaData( const MetaBundle
&, bool );
322 void engineStateChanged( Engine::State
, Engine::State
= Engine::Empty
);
324 /// K3ListView Overloaded functions
325 void contentsDropEvent ( QDropEvent
* );
326 void contentsDragEnterEvent( QDragEnterEvent
* );
327 void contentsDragMoveEvent ( QDragMoveEvent
* );
328 void contentsDragLeaveEvent( QDragLeaveEvent
* );
330 #ifdef PURIST //K3ListView imposes hand cursor so override it
331 void contentsMouseMoveEvent( QMouseEvent
*e
) { Q3ListView::contentsMouseMoveEvent( e
); }
334 void customEvent( QEvent
* );
335 bool eventFilter( QObject
*, QEvent
* );
336 void paletteChange( const QPalette
& );
337 void rename( Q3ListViewItem
*, int );
338 void setColumnWidth( int, int );
339 void setSorting( int, bool = true );
341 void viewportPaintEvent( QPaintEvent
* );
342 void viewportResizeEvent( QResizeEvent
* );
344 void appendToPreviousTracks( PlaylistItem
*item
);
345 void appendToPreviousAlbums( PlaylistAlbum
*album
);
346 void removeFromPreviousTracks( PlaylistItem
*item
= 0 );
347 void removeFromPreviousAlbums( PlaylistAlbum
*album
= 0 );
349 typedef QMap
<MyAtomicString
, PlaylistAlbum
*> AlbumMap
;
350 typedef QMap
<MyAtomicString
, AlbumMap
> ArtistAlbumMap
;
351 ArtistAlbumMap m_albums
;
352 uint m_startupTime_t
; //QDateTime::currentDateTime().toTime_t as of startup
353 uint m_oldestTime_t
; //the createdate of the oldest song in the collection
358 PlaylistItem
*m_currentTrack
; //the track that is playing
359 Q3ListViewItem
*m_marker
; //track that has the drag/drop marker under it
360 PlaylistItem
*m_hoveredRating
; //if the mouse is hovering over the rating of an item
362 //NOTE these container types were carefully chosen
363 Q3PtrList
<PlaylistAlbum
> m_prevAlbums
; //the previously played albums in Entire Albums mode
364 PLItemList m_prevTracks
; //the previous history
365 QList
<PlaylistItem
*> m_nextTracks
; //the tracks to be played after the current track
368 QString m_prevfilter
;
369 QTimer
*m_filtertimer
;
371 PLItemList m_itemsToChangeTagsFor
;
373 bool m_smartResizing
;
382 qint64 m_total
; //for Favor Tracks
383 bool m_itemCountDirty
;
385 KAction
*m_undoButton
;
386 KAction
*m_redoButton
;
387 QAction
*m_clearButton
;
390 QStringList m_undoList
;
391 QStringList m_redoList
;
394 DynamicMode
*m_dynamicMode
;
395 KUrl::List m_queueList
;
396 PlaylistItem
*m_stopAfterTrack
;
399 bool m_dynamicDirt
; //So we don't call advanceDynamicTrack() on activate()
400 bool m_queueDirt
; //When queuing disabled items, we need to place the marker on the newly inserted item
401 bool m_undoDirt
; //Make sure we don't repopulate the playlist when dynamic mode and undo()
402 int m_insertFromADT
; //Don't automatically start playing if a user hits Next in dynamic mode when not already playing
403 static QMutex
*s_dynamicADTMutex
;
405 Q3ListViewItem
*m_itemToReallyCenter
;
406 Q3ListViewItem
*m_renameItem
;
408 QTimer
*m_clicktimer
;
409 Q3ListViewItem
*m_itemToRename
;
411 int m_columnToRename
;
413 QMap
<QString
, QStringList
> m_customSubmenuItem
;
414 QMap
<int, QString
> m_customIdItem
;
416 bool isLocked() const { return m_lockStack
> 0; }
418 /// stack counter for PLaylist::lock() and unlock()
421 QString m_editOldTag
; //text before inline editing ( the new tag is written only if it's changed )
423 std::vector
<double> m_columnFraction
;
425 QMap
<QString
,Q3PtrList
<PlaylistItem
>*> m_uniqueMap
;
429 QString m_playlistName
;
430 bool m_proposeOverwriting
;
433 class MyAtomicString
: public AtomicString
437 MyAtomicString(const QString
&string
): AtomicString( string
) { }
438 MyAtomicString(const AtomicString
&other
): AtomicString( other
) { }
439 bool operator<(const AtomicString
&other
) const { return ptr() < other
.ptr(); }
447 qint64 total
; //for Favor Tracks
448 PlaylistAlbum(): refcount( 0 ), total( 0 ) { }
452 * Iterator class that only edits visible items! Preferentially always use
453 * this! Invisible items should not be operated on! To iterate over all
454 * items use MyIt::All as the flags parameter. MyIt::All cannot be OR'd,
458 class PlaylistIterator
: public Q3ListViewItemIterator
461 explicit PlaylistIterator( Q3ListViewItem
*item
, int flags
= 0 )
462 //QListViewItemIterator is not great and doesn't allow you to see everything if you
463 //mask both Visible and Invisible :( instead just visible items are returned
464 : Q3ListViewItemIterator( item
, flags
== All
? 0 : flags
| Visible
)
467 explicit PlaylistIterator( Q3ListView
*view
, int flags
= 0 )
468 : Q3ListViewItemIterator( view
, flags
== All
? 0 : flags
| Visible
)
471 //FIXME! Dirty hack for enabled/disabled items.
473 Visible
= Q3ListViewItemIterator::Visible
,
474 All
= Q3ListViewItemIterator::Invisible
477 inline PlaylistItem
*operator*() { return static_cast<PlaylistItem
*>( Q3ListViewItemIterator::operator*() ); }
479 /// @return the next visible PlaylistItem after item
480 static PlaylistItem
*nextVisible( PlaylistItem
*item
)
482 PlaylistIterator
it( item
);
483 return (*it
== item
) ? *static_cast<PlaylistIterator
&>(++it
) : *it
;
486 static PlaylistItem
*prevVisible( PlaylistItem
*item
)
488 PlaylistIterator
it( item
);
489 return (*it
== item
) ? *static_cast<PlaylistIterator
&>(--it
) : *it
;
494 #endif //AMAROK_PLAYLIST_H