📄 qmplaylist.cpp
字号:
/* qmplaylist.cpp * * $Id: qmplaylist.cpp,v 1.106.2.3 2002/10/11 06:39:03 kyllingstad Exp $ * * Apollo sound player: http://www.apolloplayer.org * Copyright(C) 2000-2002 Apollo Team. See CREDITS file. * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * The GNU General Public License is also available online at: * * http://www.gnu.org/copyleft/gpl.html */#include "qmplaylist.h"#include "message.h"#include "qmconfig.h"#include "qmdiritem.h"#include "qmplaylistformat.h"#include "qmpropertydialog.h"#include "qmsonginfopropertypage.h"#include "qmsongitem.h"#include "util.h"#include "qcstring.h"#include <algorithm>#include <iostream>#include <time.h>#include <cassert>#include <qdom.h>#include <qfile.h>#include <qfiledialog.h>#include <qfileinfo.h>#include <qmessagebox.h>#include <qstringlist.h>#include <qtextstream.h>int QmPlayList::s_PlayListCounter = 0;/* Disables false warning in Visual Studio about dynamic_cast */#ifdef _WS_WIN_#pragma warning(disable: 4541)#endif/*! \file qmplaylist.cpp \brief The playlist widget. Duh. *//*! \class QmPlayList qmplaylist.h \brief Represents the playlist. This widget is the playlist in Apollo. It provides functionality for loading and saving the playlist, as well as traversing and modifying it. In order to understand the class, a few definitions need to be pointed out: <ul> <li> Selected - An item that is selected is drawn with a filled background. Any number of items can be selected, including none. Notice that if a folder is selected, all its children are implicitly selected too. This is not shown visually. See selectedItems() for more information. </li> <li> Current - The text of the current item, if any, is drawn in a different color than the other items. The current item represents the playing item, or the item that is paused or will be played if `Play' is clicked. If there's no songs to be played, there won't be current item. </li> <li> Bad - Any number of items can be marked as bad. A bad item cannot be played expect for one case: A playing item can be marked as bad while being played. Playing will not stop. </li> </ul> Some methods, such as nextSong, and prevSong, are not const because there is no const QListView iterator.*//*! \fn void QmPlayList::disableNext() Emitted when there's no songs below the current item, this allows clients of this class to disable the next button.*//*! \fn void QmPlayList::enableNext() Emitted when there are one or more songs below the current item, this allows clients of this class to enable the next button.*//*! \fn void QmPlayList::disablePrev() Emitted when there's no songs above the current item, this allows clients of this class to disable the previous button.*//*! \fn void QmPlayList::enablePrev() Emitted when there's one or more songs above the current item, this allows clients of this class to disable the previous button.*//*! Constructs a playlist widget.*/// It is necessary to keep a 'play item' variable (see above) so we can// remove its playing status when another item is selected.// currentItem() cannot be used as it will already be updated with// the new song.QmPlayList::QmPlayList( QWidget *parent, const char *name) : QmListView(parent, name), m_Dirty(false), m_SongIndex(), m_SongIndexDirty(true), m_DeletingItems(false), m_PlayLength(0), m_PlayLengthNotCounted(0), m_pCurrentSong(0), m_Filename(""), m_pSongInfoPage(0), m_pPropertyDlg(0){ setMultiSelection(true); setSelectionMode(Extended); setAcceptDrops(true); setDragAutoScroll(true); setMinimumWidth(m_LengthWidth); setHScrollBarMode(AlwaysOff); reorganizable(true); m_pMenu = new QPopupMenu(this); connect(this, SIGNAL(mouseButtonClicked(int, QListViewItem*, const QPoint &, int)), this, SLOT(mouseClick(int, QListViewItem*, const QPoint &, int))); connect(this, SIGNAL(selectionChanged()), this, SLOT(newSelection())); connect(&m_Delay, SIGNAL(timeout()), this, SLOT(newSelectionHandler()));}/*! */QmPlayList::~QmPlayList(){}/*! Initialize the popup menu */voidQmPlayList::initMenu(){ m_ContinueHereId = m_pMenu->insertItem( tr("Continue Here"), this, SLOT(continueHere())); m_PlayFirstId = m_pMenu->insertItem( tr("Play First (mid click)"), QmMainWindow::mainwin, SLOT(addToPlayFirst())); m_FlattenId = m_pMenu->insertItem( tr("Flatten"), this, SLOT(flatten())); m_ReGroupId = m_pMenu->insertItem( tr("Regroup"), this, SLOT(reGroup())); m_pMenu->insertSeparator(); m_pMenu->insertItem( tr("Expand First"), this, SLOT(expandFirstLevel())); m_pMenu->insertItem( tr("Expand All"), this, SLOT(expandAll())); m_pMenu->insertItem( tr("Collapse All"), this, SLOT(collapseAll())); m_pMenu->insertSeparator(); m_pMenu->insertItem( tr("Update song info for all songs"), this, SLOT(readSongInfo())); m_GenerateId = m_pMenu->insertItem( tr("Generate a playlist for each artist"), this, SLOT(generatePlayListForEachArtistAndAlbum(int))); m_pMenu->setItemParameter(m_GenerateId, 0); m_GenerateTreeId = m_pMenu->insertItem( tr("Generate a playlist for each artist - and playlist tree"), this, SLOT(generatePlayListForEachArtistAndAlbum(int))); m_pMenu->setItemParameter(m_GenerateTreeId, 1); m_pMenu->insertItem( "Clear", this, SLOT(clear())); m_pMenu->insertSeparator(); m_pMenu->insertItem( tr("Properties..."), this, SLOT(showItemProperties()));}/*! Adds the playlist \a filename to the list. The placement of the inserted playlist is related to \a reference. If \a reference is null, \a filename will be appended if \a below is true or prepended if \a below is false. If \a reference is not null, \a filename will be placed below \a reference if \a below is true or above if \a below is false. \return True if successful, false otherwise. */boolQmPlayList::add( const QString & /*filename*/, QListViewItem * /*reference*/, bool /*below*/){ // This function will be used for drag'n drop but is more general than // the current add() and is therefore meant to replace that one. return false;}/*! \returns the total play length of the list in seconds.*/longQmPlayList::playLength() { updateSongIndex(); return m_PlayLength;}/*! \returns the number of songs in the list with unknown length. Songs that haven't been played yet and doesn't have the lenght info in the playlist. (Consider adding a "stat all songs" option in the menu.) */intQmPlayList::playLengthNotCounted() { updateSongIndex(); return m_PlayLengthNotCounted;}/*! \returns number of songs in the playlist.*/int QmPlayList::noSongs() { updateSongIndex(); return m_SongIndex.size();}/*! Increases the global counter. The counter is used for sorting the songs in the playlist. \return The previous counter value, i.e. before it was increased.*/intQmPlayList::count(){ return s_PlayListCounter++;}/*! Wraps around to the beginning if \param wrap is true. \return The first non-bad song following the current song, or 0. \sa prevSong(bool)*/QmSongItem*QmPlayList::nextSong(bool wrap) { updateSongIndex(); if (m_SongIndex.empty()) return 0; SongIndexIterator current = m_pCurrentSong ? std::find(m_SongIndex.begin(), m_SongIndex.end(), m_pCurrentSong) : m_SongIndex.begin(); if (current == m_SongIndex.end()) current = m_SongIndex.begin(); if (*current == m_SongIndex.back()) return wrap ? m_SongIndex.front() : 0; ++current; return *current;}/*! Wraps around to the end if \param wrap is true. \return The first non-bad song preceding the current song, or 0. \sa nextSong(bool)*/QmSongItem*QmPlayList::prevSong(bool wrap) { updateSongIndex(); if (m_SongIndex.empty()) return 0; SongIndexIterator current = m_pCurrentSong ? std::find(m_SongIndex.begin(), m_SongIndex.end(), m_pCurrentSong) : m_SongIndex.begin(); if (current == m_SongIndex.end()) current = m_SongIndex.begin(); if (*current == m_SongIndex.front()) return wrap ? m_SongIndex.back() : 0; --current; return *current;}/*! Wraps around to the beginning if \param wrap is true. \return The first non-bad song following the current song, or 0. \sa prevSong(QmSongItem*, bool)*/QmSongItem*QmPlayList::nextSong( QmSongItem *curSong, bool wrap) { if (isEmpty()) return 0; SongIndexIterator current = m_pCurrentSong ? std::find(m_SongIndex.begin(), m_SongIndex.end(), curSong) : m_SongIndex.begin(); if (current == m_SongIndex.end()) current = m_SongIndex.begin(); if (*current == m_SongIndex.back()) return wrap ? m_SongIndex.front() : 0; ++current; return *current;}/*! Searches for a song with the given filepath. \return the song if it's found, otherwize 0 */QmSongItem* QmPlayList::findSong( const QString &filePath) { updateSongIndex(); for (int i=m_SongIndex.size()-1; i>=0; --i) if (filePath == m_SongIndex[i]->filePath()) return m_SongIndex[i]; return 0;} /*! Wraps around to the end if \param wrap is true. \return The first non-bad song preceding the \param curSong song, or 0. \sa nextSong(QmSongItem*, bool)*/QmSongItem*QmPlayList::prevSong( QmSongItem *curSong, bool wrap) { updateSongIndex(); SongIndexIterator current = m_pCurrentSong ? std::find(m_SongIndex.begin(), m_SongIndex.end(), curSong) : m_SongIndex.begin(); if (current == m_SongIndex.end()) current = m_SongIndex.begin(); if (*current == m_SongIndex.front()) return wrap ? m_SongIndex.back() : 0; --current; return *current;}/*! \return A random song.*/QmSongItem*QmPlayList::randomSong() { updateSongIndex(); return m_SongIndex[static_cast<int>(m_SongIndex.size()*(double)rand()/RAND_MAX)];}/*! If \a item is a non-bad song item, give it playing status and remove the playing status from the song currently having it, if any. If \a item is marked as a bad song or is not a song, this function will do nothing. This function also emits enableNext(), disableNext(), enablePrev() and disablePrev() signals when appropriate.*/voidQmPlayList::setCurrent( QmSongItem *song){ CHECK_PTR(song); // rk: log this if(song->isBad()) return; if(m_pCurrentSong) m_pCurrentSong->setPlaying(false); // rk: disableNext() and enableNext() are currently not connected updateSongIndex(); SongIndexIterator cur = std::find(m_SongIndex.begin(), m_SongIndex.end(), song); assert(cur != m_SongIndex.end()); if (*cur == m_SongIndex.back() && !QmMainWindow::mainwin->loopMode()) emit disableNext(); else emit enableNext(); if(cur == m_SongIndex.begin() && !QmMainWindow::mainwin->loopMode()) emit disablePrev(); else emit enablePrev(); song->setPlaying(true); m_pCurrentSong = song; ensureItemVisible(song);}/*! \return The current song, or 0 if there is no non-bad songs. \todo I don't like this function. The only reason for its existance is to save the name of the playing item when quitting.*/QmSongItem*QmPlayList::currentSong() { updateSongIndex(); if (m_pCurrentSong == 0) { if (m_SongIndex.empty()) return 0; else setCurrent(m_SongIndex.front()); } return m_pCurrentSong;}/*! \returns Song with index \a index, or playing song if \a index is -1 (default). 0 if no song playing or invalid index.*/QmSongItem*QmPlayList::song( int index){ if (index == -1) return m_pCurrentSong; updateSongIndex(); return index >= static_cast<int>(m_SongIndex.size()) || index<0 ? 0 : m_SongIndex[index];}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -