playlist.cpp
来自「Amarok是一款在LINUX或其他类UNIX操作系统中运行的音频播放器软件。 」· C++ 代码 · 共 2,005 行 · 第 1/5 页
CPP
2,005 行
/* Copyright 2002-2004 Mark Kretschmann, Max Howell, Christian Muehlhaeuser * Copyright 2005-2006 Seb Ruiz, Mike Diehl, Ian Monroe, Gábor Lehel, Alexandre Pereira de Oliveira * Licensed as described in the COPYING file found in the root of this distribution * Maintainer: Max Howell <max.howell@methylblue.com> * NOTES * * The PlaylistWindow handles some Playlist events. Thanks! * This class has a QOBJECT but it's private so you can only connect via PlaylistWindow::PlaylistWindow * Mostly it's sensible to implement playlist functionality in this class * TODO Obtaining information about the playlist is currently hard, we need the playlist to be globally * available and have some more useful public functions */#define DEBUG_PREFIX "Playlist"#include <config.h>#include "amarok.h"#include "amarokconfig.h"#include "app.h"#include "debug.h"#include "collectiondb.h"#include "collectionbrowser.h"#include "columnlist.h"#include "deletedialog.h"#include "enginecontroller.h"#include "expression.h"#include "k3bexporter.h"#include "metabundle.h"#include "mountpointmanager.h"#include "osd.h"#include "playerwindow.h"#include "playlistitem.h"#include "playlistbrowser.h"#include "playlistbrowseritem.h" //for stream editor dialog#include "playlistloader.h"#include "playlistselection.h"#include "queuemanager.h"#include "prettypopupmenu.h"#include "scriptmanager.h"#include "sliderwidget.h"#include "starmanager.h"#include "statusbar.h" //for status messages#include "tagdialog.h"#include "threadmanager.h"#include "xspfplaylist.h"#include <cmath> //for pow() in playNextTrack()#include <qbuffer.h>#include <qclipboard.h> //copyToClipboard(), slotMouseButtonPressed()#include <qcolor.h>#include <qevent.h>#include <qfile.h> //undo system#include <qheader.h> //eventFilter()#include <qlabel.h> //showUsageMessage()#include <qpainter.h>#include <qpen.h> //slotGlowTimer()#include <qsimplerichtext.h> //toolTipText()#include <qsortedlist.h>#include <qtimer.h>#include <qtooltip.h>#include <qvaluelist.h> //addHybridTracks()#include <qvaluevector.h> //playNextTrack()#include <qlayout.h>#include <kaction.h>#include <kapplication.h>#include <kcursor.h> //setOverrideCursor()#include <kdialogbase.h>#include <kglobalsettings.h> //rename()#include <kiconeffect.h>#include <kiconloader.h> //slotShowContextMenu()#include <kio/job.h> //deleteSelectedFiles()#include <klineedit.h> //setCurrentTrack()#include <klocale.h>#include <kmessagebox.h>#include <kpopupmenu.h>#include <krandomsequence.h> //random Mode#include <kstandarddirs.h> //KGlobal::dirs()#include <kstdaction.h>#include <kstringhandler.h> //::showContextMenu()#include <kurldrag.h>#include <cstdlib> // absextern "C"{ #if KDE_VERSION < KDE_MAKE_VERSION(3,3,91) #include <X11/Xlib.h> //ControlMask in contentsDragMoveEvent() #endif}#include "playlist.h"namespace Amarok{ const DynamicMode *dynamicMode() { return Playlist::instance() ? Playlist::instance()->dynamicMode() : 0; }}typedef PlaylistIterator MyIt;///////////////////////////////////////////////////////////////////////////////////////////// CLASS TagWriter : Threaded tag-updating//////////////////////////////////////////////////////////////////////////////////////////class TagWriter : public ThreadManager::Job{ //TODO make this do all tags at once when you split playlist.cpp uppublic: TagWriter( PlaylistItem*, const QString &oldTag, const QString &newTag, const int, const bool updateView = true ); ~TagWriter(); bool doJob(); void completeJob();private: PlaylistItem* const m_item; bool m_failed; QString m_oldTagString; QString m_newTagString; int m_tagType; bool m_updateView;};///////////////////////////////////////////////////////////////////////////////////////////// Glow//////////////////////////////////////////////////////////////////////////////////////////namespace Glow{ namespace Text { static float dr, dg, db; static int r, g, b; } namespace Base { static float dr, dg, db; static int r, g, b; } static const uint STEPS = 13; static uint counter; static QTimer timer; inline void startTimer() { counter = 0; timer.start( 40 ); } inline void reset() { counter = 0; timer.stop(); }}///////////////////////////////////////////////////////////////////////////////////////////// CLASS Playlist//////////////////////////////////////////////////////////////////////////////////////////QMutex* Playlist::s_dynamicADTMutex = new QMutex();Playlist *Playlist::s_instance = 0;Playlist::Playlist( QWidget *parent ) : KListView( parent, "ThePlaylist" ) , EngineObserver( EngineController::instance() ) , m_startupTime_t( QDateTime::currentDateTime().toTime_t() ) , m_oldestTime_t( CollectionDB::instance()->query( "SELECT MIN( createdate ) FROM statistics;" ).first().toInt() ) , m_currentTrack( 0 ) , m_marker( 0 ) , m_hoveredRating( 0 ) , m_firstColumn( 0 ) , m_totalCount( 0 ) , m_totalLength( 0 ) , m_selCount( 0 ) , m_selLength( 0 ) , m_visCount( 0 ) , m_visLength( 0 ) , m_total( 0 ) , m_itemCountDirty( false ) , m_undoButton( 0 ) , m_redoButton( 0 ) , m_clearButton( 0 ) , m_undoDir( Amarok::saveLocation( "undo/" ) ) , m_undoCounter( 0 ) , m_dynamicMode( 0 ) , m_stopAfterTrack( 0 ) , m_stopAfterMode( DoNotStop ) , m_showHelp( true ) , m_dynamicDirt( false ) , m_queueDirt( false ) , m_undoDirt( false ) , m_insertFromADT( 0 ) , m_itemToReallyCenter( 0 ) , m_renameItem( 0 ) , m_lockStack( 0 ) , m_columnFraction( PlaylistItem::NUM_COLUMNS, 0 ) , m_oldRandom( 0 ) , m_oldRepeat( 0 ) , m_playlistName( i18n( "Untitled" ) ) , m_proposeOverwriting( false ) , m_urlIndex( &PlaylistItem::url ){ s_instance = this; connect( CollectionDB::instance(), SIGNAL(fileMoved(const QString&, const QString&, const QString&)), SLOT(updateEntriesUrl(const QString&, const QString&, const QString&)) ); connect( CollectionDB::instance(), SIGNAL(uniqueIdChanged(const QString&, const QString&, const QString&)), SLOT(updateEntriesUniqueId(const QString&, const QString&, const QString&)) ); connect( CollectionDB::instance(), SIGNAL(fileDeleted(const QString&, const QString&)), SLOT(updateEntriesStatusDeleted(const QString&, const QString&)) ); connect( CollectionDB::instance(), SIGNAL(fileAdded(const QString&, const QString&)), SLOT(updateEntriesStatusAdded(const QString&, const QString&)) ); connect( CollectionDB::instance(), SIGNAL(filesAdded(const QMap<QString,QString>&)), SLOT(updateEntriesStatusAdded(const QMap<QString,QString>&)) ); initStarPixmaps(); EngineController* const ec = EngineController::instance(); connect( ec, SIGNAL(orderPrevious()), SLOT(playPrevTrack()) ); connect( ec, SIGNAL(orderNext( const bool )), SLOT(playNextTrack( const bool )) ); connect( ec, SIGNAL(orderCurrent()), SLOT(playCurrentTrack()) ); connect( this, SIGNAL( itemCountChanged( int, int, int, int, int, int ) ), ec, SLOT( playlistChanged() ) ); setShowSortIndicator( true ); setDropVisualizer( false ); //we handle the drawing for ourselves setDropVisualizerWidth( 3 ); // FIXME: This doesn't work, and steals focus when an item is clicked twice. //setItemsRenameable( true ); setAcceptDrops( true ); setSelectionMode( QListView::Extended ); setAllColumnsShowFocus( true ); //setItemMargin( 1 ); //aesthetics setMouseTracking( true ); #if KDE_IS_VERSION( 3, 3, 91 ) setShadeSortColumn( true ); #endif for( int i = 0; i < MetaBundle::NUM_COLUMNS; ++i ) { addColumn( PlaylistItem::prettyColumnName( i ), 0 ); switch( i ) { case PlaylistItem::Title: case PlaylistItem::Artist: case PlaylistItem::Composer: case PlaylistItem::Year: case PlaylistItem::Album: case PlaylistItem::DiscNumber: case PlaylistItem::Track: case PlaylistItem::Bpm: case PlaylistItem::Genre: case PlaylistItem::Comment: case PlaylistItem::Score: case PlaylistItem::Rating: setRenameable( i, true ); continue; default: setRenameable( i, false ); } } setColumnWidth( PlaylistItem::Title, 200 ); setColumnWidth( PlaylistItem::Artist, 100 ); setColumnWidth( PlaylistItem::Album, 100 ); setColumnWidth( PlaylistItem::Length, 80 ); if( AmarokConfig::showMoodbar() ) setColumnWidth( PlaylistItem::Mood, 120 ); if( AmarokConfig::useRatings() ) setColumnWidth( PlaylistItem::Rating, PlaylistItem::ratingColumnWidth() ); setColumnAlignment( PlaylistItem::Length, Qt::AlignRight ); setColumnAlignment( PlaylistItem::Track, Qt::AlignCenter ); setColumnAlignment( PlaylistItem::DiscNumber, Qt::AlignCenter ); setColumnAlignment( PlaylistItem::Bpm, Qt::AlignRight ); setColumnAlignment( PlaylistItem::Year, Qt::AlignCenter ); setColumnAlignment( PlaylistItem::Bitrate, Qt::AlignCenter ); setColumnAlignment( PlaylistItem::SampleRate, Qt::AlignCenter ); setColumnAlignment( PlaylistItem::Filesize, Qt::AlignCenter ); setColumnAlignment( PlaylistItem::Score, Qt::AlignCenter ); setColumnAlignment( PlaylistItem::Type, Qt::AlignCenter ); setColumnAlignment( PlaylistItem::PlayCount, Qt::AlignCenter ); connect( this, SIGNAL( doubleClicked( QListViewItem* ) ), this, SLOT( doubleClicked( QListViewItem* ) ) ); connect( this, SIGNAL( returnPressed( QListViewItem* ) ), this, SLOT( activate( QListViewItem* ) ) ); connect( this, SIGNAL( mouseButtonPressed( int, QListViewItem*, const QPoint&, int ) ), this, SLOT( slotMouseButtonPressed( int, QListViewItem*, const QPoint&, int ) ) ); connect( this, SIGNAL( queueChanged( const PLItemList &, const PLItemList & ) ), this, SLOT( slotQueueChanged( const PLItemList &, const PLItemList & ) ) ); connect( this, SIGNAL( itemRenamed( QListViewItem*, const QString&, int ) ), this, SLOT( writeTag( QListViewItem*, const QString&, int ) ) ); connect( this, SIGNAL( aboutToClear() ), this, SLOT( saveUndoState() ) ); connect( CollectionDB::instance(), SIGNAL( scoreChanged( const QString&, float ) ), this, SLOT( scoreChanged( const QString&, float ) ) ); connect( CollectionDB::instance(), SIGNAL( ratingChanged( const QString&, int ) ), this, SLOT( ratingChanged( const QString&, int ) ) ); connect( CollectionDB::instance(), SIGNAL( fileMoved( const QString&, const QString& ) ), this, SLOT( fileMoved( const QString&, const QString& ) ) ); connect( header(), SIGNAL( indexChange( int, int, int ) ), this, SLOT( columnOrderChanged() ) ), connect( &Glow::timer, SIGNAL(timeout()), SLOT(slotGlowTimer()) ); KActionCollection* const ac = Amarok::actionCollection(); KAction *copy = KStdAction::copy( this, SLOT( copyToClipboard() ), ac, "playlist_copy" ); KStdAction::selectAll( this, SLOT( selectAll() ), ac, "playlist_select_all" ); m_clearButton = new KAction( i18n( "clear playlist", "&Clear" ), Amarok::icon( "playlist_clear" ), 0, this, SLOT( clear() ), ac, "playlist_clear" ); m_undoButton = KStdAction::undo( this, SLOT( undo() ), ac, "playlist_undo" ); m_redoButton = KStdAction::redo( this, SLOT( redo() ), ac, "playlist_redo" ); m_undoButton ->setIcon( Amarok::icon( "undo" ) ); m_redoButton ->setIcon( Amarok::icon( "redo" ) ); new KAction( i18n( "&Repopulate" ), Amarok::icon( "playlist_refresh" ), 0, this, SLOT( repopulate() ), ac, "repopulate" ); new KAction( i18n( "S&huffle" ), "rebuild", CTRL+Key_H, this, SLOT( shuffle() ), ac, "playlist_shuffle" ); KAction *gotoCurrent = new KAction( i18n( "&Go To Current Track" ), Amarok::icon( "music" ), CTRL+Key_J, this, SLOT( showCurrentTrack() ), ac, "playlist_show" ); new KAction( i18n( "&Remove Duplicate && Dead Entries" ), 0, this, SLOT( removeDuplicates() ), ac, "playlist_remove_duplicates" ); new KAction( i18n( "&Queue Selected Tracks" ), Amarok::icon( "queue_track" ), CTRL+Key_D, this, SLOT( queueSelected() ), ac, "queue_selected" ); KToggleAction *stopafter = new KToggleAction( i18n( "&Stop Playing After Track" ), Amarok::icon( "stop" ), CTRL+ALT+Key_V, this, SLOT( toggleStopAfterCurrentItem() ), ac, "stop_after" ); { // KAction idiocy -- shortcuts don't work until they've been plugged into a menu KPopupMenu asdf; copy->plug( &asdf ); stopafter->plug( &asdf ); gotoCurrent->plug( &asdf ); copy->unplug( &asdf ); stopafter->unplug( &asdf ); gotoCurrent->unplug( &asdf ); } //ensure we update action enabled states when repeat Playlist is toggled connect( ac->action( "repeat" ), SIGNAL(activated( int )), SLOT(updateNextPrev()) ); connect( ac->action( "repeat" ), SIGNAL( activated( int ) ), SLOT( generateInfo() ) ); connect( ac->action( "favor_tracks" ), SIGNAL( activated( int ) ), SLOT( generateInfo() ) ); connect( ac->action( "random_mode" ), SIGNAL( activated( int ) ), SLOT( generateInfo() ) ); // undostates are written in chronological order, so this is a clever way to get them back in the correct order :) QStringList undos = m_undoDir.entryList( QString("*.xml"), QDir::Files, QDir::Time ); foreach( undos ) m_undoList.append( m_undoDir.absPath() + '/' + (*it) ); m_undoCounter = m_undoList.count(); m_undoButton->setEnabled( !m_undoList.isEmpty() ); m_redoButton->setEnabled( false ); engineStateChanged( EngineController::engine()->state() ); //initialise state of UI paletteChange( palette() ); //sets up glowColors restoreLayout( KGlobal::config(), "PlaylistColumnsLayout" ); // Sorting must be disabled when current.xml is being loaded. See BUG 113042 KListView::setSorting( NO_SORT ); //use base so we don't saveUndoState() too setDynamicMode( 0 ); m_smartResizing = Amarok::config( "PlaylistWindow" )->readBoolEntry( "Smart Resizing", true ); columnOrderChanged(); //cause the column fractions to be updated, but in a safe way, ie no specific column columnResizeEvent( header()->count(), 0, 0 ); //do after you resize all the columns connect( header(), SIGNAL(sizeChange( int, int, int )), SLOT(columnResizeEvent( int, int, int )) ); connect( this, SIGNAL( contentsMoving( int, int ) ), SLOT( slotContentsMoving() ) ); connect( App::instance(), SIGNAL( useScores( bool ) ), this, SLOT( slotUseScores( bool ) ) ); connect( App::instance(), SIGNAL( useRatings( bool ) ), this, SLOT( slotUseRatings( bool ) ) ); connect( App::instance(), SIGNAL( moodbarPrefs( bool, bool, int, bool ) ), this, SLOT( slotMoodbarPrefs( bool, bool, int, bool ) ) ); Amarok::ToolTip::add( this, viewport() ); header()->installEventFilter( this ); renameLineEdit()->installEventFilter( this ); setTabOrderedRenaming( false ); m_filtertimer = new QTimer( this );
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?