diff options
| author | trialuser02 <trialuser02@90c681e8-e032-0410-971d-27865f9a5e38> | 2008-12-13 18:14:00 +0000 |
|---|---|---|
| committer | trialuser02 <trialuser02@90c681e8-e032-0410-971d-27865f9a5e38> | 2008-12-13 18:14:00 +0000 |
| commit | 43ee25ac78884c3c579a555fee589f3677ea7f21 (patch) | |
| tree | 313df92c490392b1e501b2cc27bc220335f938fc /src/qmmpui | |
| parent | 2d3e4370a6b7729a30b833aa72c79b6a9d2afdb6 (diff) | |
| download | qmmp-43ee25ac78884c3c579a555fee589f3677ea7f21.tar.gz qmmp-43ee25ac78884c3c579a555fee589f3677ea7f21.tar.bz2 qmmp-43ee25ac78884c3c579a555fee589f3677ea7f21.zip | |
moved playlist model to libqmmpui
git-svn-id: http://svn.code.sf.net/p/qmmp-dev/code/trunk/qmmp@679 90c681e8-e032-0410-971d-27865f9a5e38
Diffstat (limited to 'src/qmmpui')
| -rw-r--r-- | src/qmmpui/CMakeLists.txt | 14 | ||||
| -rw-r--r-- | src/qmmpui/fileloader.cpp | 116 | ||||
| -rw-r--r-- | src/qmmpui/fileloader.h (renamed from src/qmmpui/abstractplaylist.h) | 52 | ||||
| -rw-r--r-- | src/qmmpui/playlistitem.cpp | 179 | ||||
| -rw-r--r-- | src/qmmpui/playlistitem.h (renamed from src/qmmpui/abstractplaylist.cpp) | 56 | ||||
| -rw-r--r-- | src/qmmpui/playlistmodel.cpp | 910 | ||||
| -rw-r--r-- | src/qmmpui/playlistmodel.h | 353 | ||||
| -rw-r--r-- | src/qmmpui/playstate.cpp | 138 | ||||
| -rw-r--r-- | src/qmmpui/playstate.h | 109 | ||||
| -rw-r--r-- | src/qmmpui/qmmpui.pro | 14 |
10 files changed, 1918 insertions, 23 deletions
diff --git a/src/qmmpui/CMakeLists.txt b/src/qmmpui/CMakeLists.txt index 96517cf0f..08b7a5c94 100644 --- a/src/qmmpui/CMakeLists.txt +++ b/src/qmmpui/CMakeLists.txt @@ -35,8 +35,11 @@ SET(libqmmpui_SRCS commandlinemanager.cpp filedialog.cpp qtfiledialog.cpp - abstractplaylist.cpp abstractplaylistitem.cpp + fileloader.cpp + playstate.cpp + playlistmodel.cpp + playlistitem.cpp ) SET(libqmmpui_MOC_HDRS @@ -50,8 +53,11 @@ SET(libqmmpui_MOC_HDRS filedialog.h filedialogfactory.h qtfiledialog.h - abstractplaylist.h abstractplaylistitem.h + fileloader.h + playstate.h + playlistmodel.h + playlistitem.h ) SET(libqmmpui_DEVEL_HDRS @@ -64,8 +70,10 @@ SET(libqmmpui_DEVEL_HDRS commandlineoption.h filedialog.h filedialogfactory.h - abstractplaylist.h + qtfiledialog.h abstractplaylistitem.h + playlistmodel.h + playlistitem.h ) diff --git a/src/qmmpui/fileloader.cpp b/src/qmmpui/fileloader.cpp new file mode 100644 index 000000000..ce5ccb56c --- /dev/null +++ b/src/qmmpui/fileloader.cpp @@ -0,0 +1,116 @@ +/*************************************************************************** + * Copyright (C) 2006 by Ilya Kotov * + * forkotov02@hotmail.ru * + * * + * 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. * + ***************************************************************************/ +#include <qmmp/decoder.h> + +#include "fileloader.h" +#include "playlistitem.h" + +FileLoader::FileLoader(QObject *parent) + : QThread(parent),m_files_to_load(),m_directory() +{ + m_filters = Decoder::nameFilters(); + m_finished = false; +} + + +FileLoader::~FileLoader() +{ + qWarning("FileLoader::~FileLoader()"); +} + + +void FileLoader::addFiles(const QStringList &files) +{ + if (files.isEmpty ()) + return; + + foreach(QString s, files) + { + /*if (s.startsWith("http://") || Decoder::supports(s)) + {*/ + //emit newPlayListItem(new PlayListItem(s)); + QList <FileInfo *> playList = Decoder::createPlayList(s); + foreach(FileInfo *info, playList) + emit newPlayListItem(new PlayListItem(info)); + //} + if (m_finished) return; + } +} + + +void FileLoader::addDirectory(const QString& s) +{ + QList <FileInfo *> playList; + QDir dir(s); + dir.setFilter(QDir::Files | QDir::Hidden | QDir::NoSymLinks); + dir.setSorting(QDir::Name); + QFileInfoList l = dir.entryInfoList(m_filters); + for (int i = 0; i < l.size(); ++i) + { + QFileInfo fileInfo = l.at(i); + QString suff = fileInfo.completeSuffix(); + list << fileInfo; + + /*if (Decoder::supports(fileInfo.absoluteFilePath ())) + {*/ + playList = Decoder::createPlayList(fileInfo.absoluteFilePath ()); + foreach(FileInfo *info, playList) + emit newPlayListItem(new PlayListItem(info)); + //emit newPlayListItem(new PlayListItem(fileInfo.absoluteFilePath ())); + //} + if (m_finished) return; + } + dir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot); + dir.setSorting(QDir::Name); + l.clear(); + l = dir.entryInfoList(); + if (l.size() > 0) + for (int i = 0; i < l.size(); ++i) + { + QFileInfo fileInfo = l.at(i); + addDirectory(fileInfo.absoluteFilePath ()); + if (m_finished) return; + } +} + +void FileLoader::run() +{ + if (!m_files_to_load.isEmpty()) + addFiles(m_files_to_load); + else if (!m_directory.isEmpty()) + addDirectory(m_directory); +} + +void FileLoader::setFilesToLoad(const QStringList & l) +{ + m_files_to_load = l; + m_directory = QString(); +} + +void FileLoader::setDirectoryToLoad(const QString & d) +{ + m_directory = d; + m_files_to_load.clear(); +} + +void FileLoader::finish() +{ + m_finished = true; +} diff --git a/src/qmmpui/abstractplaylist.h b/src/qmmpui/fileloader.h index 6190cd8b4..d2dcc0748 100644 --- a/src/qmmpui/abstractplaylist.h +++ b/src/qmmpui/fileloader.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2008 by Ilya Kotov * + * Copyright (C) 2006 by Ilya Kotov * * forkotov02@hotmail.ru * * * * This program is free software; you can redistribute it and/or modify * @@ -17,22 +17,56 @@ * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ -#ifndef ABSTRACTPLAYLIST_H -#define ABSTRACTPLAYLIST_H +#ifndef FILELOADER_H +#define FILELOADER_H #include <QObject> +#include <QDir> +#include <QThread> -/** - @author Ilya Kotov <forkotov02@hotmail.ru> +class PlayListItem; + +/*! + * This class represents fileloader object that + * processes file list in separate thread and emits + * \b newPlayListItem(PlayListItem*) signal for every newly + * created media file. + @author Ilya Kotov <forkotov02@hotmail.ru> */ -class AbstractPlaylist : public QObject +class FileLoader : public QThread { -Q_OBJECT + Q_OBJECT public: - AbstractPlaylist(QObject *parent = 0); + FileLoader(QObject *parent = 0); + + ~FileLoader(); + virtual void run(); + + /*! + * Call this method when you want to notify the thread about finishing + */ + void finish(); - ~AbstractPlaylist(); + /*! + * Sets filelist to load( directory to load will be cleaned ) + */ + void setFilesToLoad(const QStringList&); + /*! + * Sets directory to load( filelist to load will be cleaned ) + */ + void setDirectoryToLoad(const QString&); +signals: + void newPlayListItem(PlayListItem*); +protected: + void addFiles(const QStringList &files); + void addDirectory(const QString& s); +private: + QFileInfoList list; + QStringList m_filters; + QStringList m_files_to_load; + QString m_directory; + bool m_finished; }; #endif diff --git a/src/qmmpui/playlistitem.cpp b/src/qmmpui/playlistitem.cpp new file mode 100644 index 000000000..bfbaa354c --- /dev/null +++ b/src/qmmpui/playlistitem.cpp @@ -0,0 +1,179 @@ +/*************************************************************************** + * Copyright (C) 2008 by Ilya Kotov * + * forkotov02@hotmail.ru * + * * + * 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. * + ***************************************************************************/ +#include <QSettings> +#include <QDir> + +#include <qmmp/decoder.h> + +#include "playlistitem.h" + +PlayListItem::PlayListItem() : AbstractPlaylistItem(), m_flag(FREE) +{ + m_info = 0; +} + +PlayListItem::PlayListItem(FileInfo *info, QSettings *settings) : AbstractPlaylistItem(), m_flag(FREE) +{ + m_selected = FALSE; + m_current = FALSE; + m_info = info; + + //use external settings or create new + QSettings *s = settings; + if (!s) + s = new QSettings (QDir::homePath() +"/.qmmp/qmmprc", QSettings::IniFormat); + + m_use_meta = s->value ("PlayList/load_metadata", TRUE).toBool(); //TODO move to libqmmp + //format + m_format = s->value("PlayList/title_format", "%p - %t").toString(); + //other properties + m_convertUnderscore = s->value ("PlayList/convert_underscore", TRUE).toBool(); + m_convertTwenty = s->value ("PlayList/convert_twenty", TRUE).toBool(); + m_fullStreamPath = s->value ("PlayList/full_stream_path", FALSE).toBool(); + if (!settings) //delete created settings only + delete s; + + setMetaData(info->metaData()); + setMetaData(Qmmp::URL, m_info->path()); + setLength(m_info->length()); + readMetadata(); +} + +PlayListItem::~PlayListItem() +{ + if (m_info) + delete m_info; +} + +void PlayListItem::setSelected(bool yes) +{ + m_selected = yes; +} + +bool PlayListItem::isSelected() const +{ + return m_selected; +} + +void PlayListItem::setCurrent(bool yes) +{ + m_current = yes; +} + +bool PlayListItem::isCurrent() const +{ + return m_current; +} + +void PlayListItem::setFlag(FLAGS f) +{ + m_flag = f; +} + +PlayListItem::FLAGS PlayListItem::flag() const +{ + return m_flag; +} + +void PlayListItem::updateMetaData(const QMap <Qmmp::MetaData, QString> &metaData) +{ + setMetaData(metaData); + readMetadata(); +} + +void PlayListItem::updateTags() +{ + if (url().startsWith("http://")) + return; + if (m_info) + { + delete m_info; + m_info = 0; + } + m_info = Decoder::createPlayList(url()).at(0); + setMetaData(m_info->metaData()); + setMetaData(Qmmp::URL, m_info->path()); + readMetadata(); +} + +const QString PlayListItem::text() const +{ + return m_title; +} + +void PlayListItem::setText(const QString &title) +{ + m_title = title; +} + +void PlayListItem::readMetadata() +{ + m_title = m_format; + m_title = printTag(m_title, "%p", artist()); + m_title = printTag(m_title, "%a", album()); + m_title = printTag(m_title, "%t", title()); + m_title = printTag(m_title, "%n", QString("%1").arg(track())); + m_title = printTag(m_title, "%g", genre()); + m_title = printTag(m_title, "%f", url().section('/',-1)); + m_title = printTag(m_title, "%F", url()); + m_title = printTag(m_title, "%y", QString("%1").arg(year ())); + + if (m_title.isEmpty()) + { + if (url().startsWith("http://") && m_fullStreamPath) + m_title = url(); + else + m_title = url().split('/',QString::SkipEmptyParts).takeLast (); + } + if (m_info) + delete m_info; + m_info = 0; + if (m_convertUnderscore) + m_title.replace("_", " "); + if (m_convertTwenty) + m_title.replace("%20", " "); +} + +QString PlayListItem::printTag(QString str, QString regExp, QString tagStr) +{ + if (!tagStr.isEmpty()) + str.replace(regExp, tagStr); + else + { + //remove unused separators + int regExpPos = str.indexOf(regExp); + if (regExpPos < 0) + return str; + int nextPos = str.indexOf("%", regExpPos + 1); + if (nextPos < 0) + { + //last separator + regExpPos = m_format.lastIndexOf(regExp); + nextPos = m_format.lastIndexOf("%", regExpPos - 1); + QString lastSep = m_format.right (m_format.size() - nextPos - 2); + str.remove(lastSep); + str.remove(regExp); + } + else + str.remove ( regExpPos, nextPos - regExpPos); + } + return str; +} + diff --git a/src/qmmpui/abstractplaylist.cpp b/src/qmmpui/playlistitem.h index 607165426..ce26ca2f5 100644 --- a/src/qmmpui/abstractplaylist.cpp +++ b/src/qmmpui/playlistitem.h @@ -17,16 +17,58 @@ * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ -#include "abstractplaylist.h" +#ifndef PLAYLISTITEM_H +#define PLAYLISTITEM_H -AbstractPlaylist::AbstractPlaylist(QObject *parent) - : QObject(parent) +#include <qmmp/qmmp.h> +#include <qmmpui/abstractplaylistitem.h> + +class FileInfo; +class QSettings; +/** + @author Ilya Kotov <forkotov02@hotmail.ru> +*/ +class PlayListItem : public AbstractPlaylistItem { -} +public: + /*! + * Current state of media file. + * FREE - instance is free and may be deleted + * EDITING - instance is currently busy in some kind of operation(tags editing etc.) + * and can't be deleted at the moment. Set flag SCHEDULED_FOR_DELETION for it + * instead of delete operator call. + */ + enum FLAGS{FREE = 0,EDITING,SCHEDULED_FOR_DELETION}; + PlayListItem(); + //PlayListItem(const QString& path); + PlayListItem(FileInfo *info, QSettings *settings = 0); + ~PlayListItem(); -AbstractPlaylist::~AbstractPlaylist() -{ -} + //playlist support + void setSelected(bool yes); + bool isSelected() const; + void setCurrent(bool yes); + bool isCurrent() const; + FLAGS flag()const; + void setFlag(FLAGS); + const QString text() const; + void setText(const QString &title); + //modify functions + void updateMetaData(const QMap <Qmmp::MetaData, QString> &metaData); + void updateTags(); +private: + void readMetadata(); + QString printTag(QString str, QString regExp, QString tagStr); + QString m_title; + FileInfo *m_info; + bool m_selected; + bool m_current; + bool m_use_meta; + bool m_convertUnderscore, m_convertTwenty, m_fullStreamPath; + QString m_format; + FLAGS m_flag; +}; +#endif diff --git a/src/qmmpui/playlistmodel.cpp b/src/qmmpui/playlistmodel.cpp new file mode 100644 index 000000000..ffde91ac8 --- /dev/null +++ b/src/qmmpui/playlistmodel.cpp @@ -0,0 +1,910 @@ +/*************************************************************************** + * Copyright(C) 2006-2008 by Ilya Kotov * + * forkotov02@hotmail.ru * + * * + * 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. * + ***************************************************************************/ +#include <QWidget> +#include <QFile> +#include <QDir> +#include <QtAlgorithms> +#include <QFileInfo> +#include <QTextStream> +#include <QPluginLoader> +#include <QApplication> +#include <QTimer> +#include <QSettings> +#include <QMessageBox> +#include <QBuffer> + +#include <time.h> + +#include <qmmp/decoder.h> +#include <qmmp/decoderfactory.h> +#include <qmmpui/playlistparser.h> +#include <qmmpui/playlistformat.h> + +#include "fileloader.h" +#include "playlistmodel.h" +#include "playlistitem.h" +#include "playstate.h" + +#include <QMetaType> + +#define INVALID_ROW -1 + +TagUpdater::TagUpdater(QObject* o,PlayListItem* item):m_observable(o),m_item(item) +{ + m_item->setFlag(PlayListItem::EDITING); + connect(m_observable, SIGNAL(destroyed(QObject *)),SLOT(updateTag())); + connect(m_observable, SIGNAL(destroyed(QObject *)),SLOT(deleteLater())); +} + +void TagUpdater::updateTag() +{ + if (m_item->flag() == PlayListItem::SCHEDULED_FOR_DELETION) + { + delete m_item; + m_item = NULL; + } + else + { + m_item->updateTags(); + m_item->setFlag(PlayListItem::FREE); + } +} + + +PlayListModel::PlayListModel(QObject *parent) + : QObject(parent) , m_selection() +{ + qsrand(time(0)); + m_total_length = 0; + m_current = 0; + m_block_update_signals = false; + is_repeatable_list = false; + m_play_state = new NormalPlayState(this); + //readSettings(); +} + +PlayListModel::~PlayListModel() +{ + writeSettings(); + clear(); + delete m_play_state; + //qDeleteAll(m_registered_pl_formats); + + foreach(GuardedFileLoader l,m_running_loaders) + { + if (!l.isNull()) + { + l->finish(); + l->wait(); + } + } +} + +void PlayListModel::load(PlayListItem *item) +{ + if (m_items.isEmpty()) + m_currentItem = item; + + m_total_length += item->length(); + m_items << item; + + if (m_items.size() == 1) + emit firstAdded(); + + if (!m_block_update_signals) + emit listChanged(); +} + +int PlayListModel::count() +{ + return m_items.size(); +} + +PlayListItem* PlayListModel::currentItem() +{ + if (m_items.isEmpty()) + return 0; + else + return m_items.at(qMin(m_items.size() - 1, m_current)); +} + +int PlayListModel::currentRow() +{ + return m_current; +} + +bool PlayListModel::setCurrent(int c) +{ + if (c > count()-1 || c < 0) + return FALSE; + m_current = c; + m_currentItem = m_items.at(c); + emit currentChanged(); + emit listChanged(); + return TRUE; +} + + +bool PlayListModel::next() +{ + if (isFileLoaderRunning()) + m_play_state->prepare(); + + return m_play_state->next(); +} + +bool PlayListModel::previous() +{ + if (isFileLoaderRunning()) + m_play_state->prepare(); + + return m_play_state->previous();//) +} + +void PlayListModel::clear() +{ + foreach(GuardedFileLoader l,m_running_loaders) + { + if (!l.isNull()) + { + l->finish(); + l->wait(); + } + } + + m_running_loaders.clear(); + + m_current = 0; + while (!m_items.isEmpty()) + { + PlayListItem* mf = m_items.takeFirst(); + + if (mf->flag() == PlayListItem::FREE) + { + delete mf; + } + else if (mf->flag() == PlayListItem::EDITING) + { + mf->setFlag(PlayListItem::SCHEDULED_FOR_DELETION); + } + } + + m_total_length = 0; + m_play_state->resetState(); + emit listChanged(); +} + +void PlayListModel::clearSelection() +{ + for (int i = 0; i<m_items.size(); ++i) + m_items.at(i)->setSelected(FALSE); + emit listChanged(); +} + +QList <QString> PlayListModel::getTitles(int b,int l) +{ + QList <QString> m_titles; + for (int i = b;(i < b + l) &&(i < m_items.size()); ++i) + m_titles << m_items.at(i)->text(); + return m_titles; +} + +QList <QString> PlayListModel::getTimes(int b,int l) +{ + QList <QString> m_times; + for (int i = b;(i < b + l) &&(i < m_items.size()); ++i) + m_times << QString("%1").arg(m_items.at(i)->length() /60) +":" + +QString("%1").arg(m_items.at(i)->length() %60/10) + + QString("%1").arg(m_items.at(i)->length() %60%10); + return m_times; +} + +bool PlayListModel::isSelected(int row) +{ + if (m_items.count() > row && row >= 0) + return m_items.at(row)->isSelected(); + + return false; +} + +void PlayListModel::setSelected(int row, bool yes) +{ + if (m_items.count() > row && row >= 0) + m_items.at(row)->setSelected(yes); +} + +void PlayListModel::removeSelected() +{ + removeSelection(false); +} + +void PlayListModel::removeUnselected() +{ + removeSelection(true); +} + +void PlayListModel::removeSelection(bool inverted) +{ + int i = 0; + + int select_after_delete = -1; + + while (!m_items.isEmpty() && i<m_items.size()) + { + if (m_items.at(i)->isSelected() ^ inverted) + { + PlayListItem* f = m_items.takeAt(i); + m_total_length -= f->length(); + if (m_total_length < 0) + m_total_length = 0; + + if (f->flag() == PlayListItem::FREE) + { + delete f; + f = NULL; + } + else if (f->flag() == PlayListItem::EDITING) + f->setFlag(PlayListItem::SCHEDULED_FOR_DELETION); + + select_after_delete = i; + + if (m_current >= i && m_current!=0) + m_current--; + } + else + i++; + } + + if (!m_items.isEmpty()) + m_currentItem = m_items.at(m_current); + + if (select_after_delete >= m_items.count()) + select_after_delete = m_items.count() - 1; + + setSelected(select_after_delete,true); + + m_play_state->prepare(); + + emit listChanged(); +} + +void PlayListModel::invertSelection() +{ + for (int i = 0; i<m_items.size(); ++i) + m_items.at(i)->setSelected(!m_items.at(i)->isSelected()); + emit listChanged(); +} + +void PlayListModel::selectAll() +{ + for (int i = 0; i<m_items.size(); ++i) + m_items.at(i)->setSelected(TRUE); + emit listChanged(); +} + +void PlayListModel::showDetails() +{ + for (int i = 0; i<m_items.size(); ++i) + { + if (m_items.at(i)->isSelected()) + { + if (!QFile::exists(m_items.at(i)->url())) + { + PlayListItem *item = m_items.at(i); + QString str; + str.append(tr("Url:") + " %1\n"); + str.append(tr("Title:") + " %2\n"); + str.append(tr("Artist:") + " %3\n"); + str.append(tr("Album:") + " %4\n"); + str.append(tr("Comment:") + " %5"); + str = str.arg(item->url()) + .arg(item->title().isEmpty() ? item->text() : item->title()) + .arg(item->artist()) + .arg(item->album()) + .arg(item->comment()); + QMessageBox::information(0, m_items.at(i)->url(), str); + return; + } + + DecoderFactory *fact = Decoder::findByPath(m_items.at(i)->url()); + if (fact) + { + QObject* o = fact->showDetails(0, m_items.at(i)->url()); + if (o) + { + TagUpdater *updater = new TagUpdater(o,m_items.at(i)); + m_editing_items.append(m_items.at(i)); + connect(updater, SIGNAL(destroyed(QObject *)),SIGNAL(listChanged())); + } + } + return; + } + } +} + +void PlayListModel::readSettings() +{ + QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat); + m_current = settings.value("Playlist/current",0).toInt(); + + QString line, param, value; + int s; + QList <FileInfo *> infoList; + QFile file(QDir::homePath() +"/.qmmp/playlist.txt"); + file.open(QIODevice::ReadOnly); + QByteArray array = file.readAll(); + file.close(); + QBuffer buffer(&array); + buffer.open(QIODevice::ReadOnly); + while (!buffer.atEnd()) + { + line = QString::fromUtf8(buffer.readLine()).trimmed(); + if ((s = line.indexOf("=")) < 0) + continue; + + param = line.left(s); + value = line.right(line.size() - s - 1); + + if (param == "file") + infoList << new FileInfo(value); + else if (infoList.isEmpty()) + continue; + else if (param == "title") + infoList.last()->setMetaData(Qmmp::TITLE, value); + else if (param == "artist") + infoList.last()->setMetaData(Qmmp::ARTIST, value); + else if (param == "album") + infoList.last()->setMetaData(Qmmp::ALBUM, value); + else if (param == "comment") + infoList.last()->setMetaData(Qmmp::COMMENT, value); + else if (param == "genre") + infoList.last()->setMetaData(Qmmp::GENRE, value); + else if (param == "year") + infoList.last()->setMetaData(Qmmp::YEAR, value); + else if (param == "track") + infoList.last()->setMetaData(Qmmp::TRACK, value); + else if (param == "length") + infoList.last()->setLength(value.toInt()); + } + buffer.close(); + if (m_current > infoList.count() - 1) + m_current = 0; + m_block_update_signals = TRUE; + foreach(FileInfo *info, infoList) + load(new PlayListItem(info, &settings)); //using one and the same settings object for all playlist items + m_block_update_signals = FALSE; + doCurrentVisibleRequest(); +} + +void PlayListModel::writeSettings() +{ + QFile file(QDir::homePath() +"/.qmmp/playlist.txt"); + file.open(QIODevice::WriteOnly); + foreach(PlayListItem* m, m_items) + { + file.write(QString("file=%1").arg(m->url()).toUtf8() +"\n"); + file.write(QString("title=%1").arg(m->title()).toUtf8() +"\n"); + file.write(QString("artist=%1").arg(m->artist()).toUtf8() +"\n"); + file.write(QString("album=%1").arg(m->album()).toUtf8() +"\n"); + file.write(QString("comment=%1").arg(m->comment()).toUtf8() +"\n"); + file.write(QString("genre=%1").arg(m->genre()).toUtf8() +"\n"); + file.write(QString("year=%1").arg(m->year()).toUtf8() +"\n"); + file.write(QString("track=%1").arg(m->track()).toUtf8() +"\n"); + file.write(QString("length=%1").arg(m->length()).toUtf8() +"\n"); + } + file.close(); + QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat); + settings.setValue("Playlist/current", m_current); +} + +void PlayListModel::addFile(const QString& path) +{ + if (path.isEmpty()) + return; + /*if(path.startsWith("http://")) + load(new PlayListItem(path)); + else if(Decoder::supports(path)) + load(new PlayListItem(path));*/ + QList <FileInfo *> playList = Decoder::createPlayList(path); + foreach(FileInfo *info, playList) + emit load(new PlayListItem(info)); + + m_play_state->prepare(); +} + +FileLoader * PlayListModel::createFileLoader() +{ + FileLoader* f_loader = new FileLoader(this); +// f_loader->setStackSize(20 * 1024 * 1024); + m_running_loaders << f_loader; + connect(f_loader,SIGNAL(newPlayListItem(PlayListItem*)),this,SLOT(load(PlayListItem*)),Qt::QueuedConnection); + connect(f_loader,SIGNAL(finished()),this,SLOT(preparePlayState())); + connect(f_loader,SIGNAL(finished()),f_loader,SLOT(deleteLater())); + return f_loader; +} + +void PlayListModel::addFiles(const QStringList &files) +{ + FileLoader* f_loader = createFileLoader(); + f_loader->setFilesToLoad(files); + f_loader->start(QThread::IdlePriority); +} + +void PlayListModel::addDirectory(const QString& s) +{ + FileLoader* f_loader = createFileLoader(); + f_loader->setDirectoryToLoad(s); + f_loader->start(QThread::IdlePriority); +} + +void PlayListModel::addFileList(const QStringList &l) +{ +// qWarning("void// PlayListModel::addFileList(const QStringList &l)"); + foreach(QString str,l) + { + QFileInfo f_info(str); + if (f_info.exists()) + { + if (f_info.isDir()) + addDirectory(str); + else + { + addFile(str); + loadPlaylist(str); + } + } + // Do processing the rest of events to avoid GUI freezing + QApplication::processEvents(QEventLoop::AllEvents,10); + } +} + +bool PlayListModel::setFileList(const QStringList & l) +{ + bool model_cleared = FALSE; + foreach(QString str,l) + { + QFileInfo f_info(str); + if (f_info.exists()) + { + if (!model_cleared) + { + clear(); + model_cleared = TRUE; + } + if (f_info.isDir()) + addDirectory(str); + else + { + addFile(str); + loadPlaylist(str); + } + } + // Do processing the rest of events to avoid GUI freezing + QApplication::processEvents(QEventLoop::AllEvents,10); + } + + return model_cleared; +} + +int PlayListModel::firstSelectedUpper(int row) +{ + for (int i = row - 1;i >= 0;i--) + { + if (isSelected(i)) + return i; + } + return -1; +} + +int PlayListModel::firstSelectedLower(int row) +{ + for (int i = row + 1;i < count() ;i++) + { + if (isSelected(i)) + return i; + } + return -1; +} + +void PlayListModel::moveItems(int from, int to) +{ + // Get rid of useless work + if (from == to) + return; + + QList<int> selected_rows = getSelectedRows(); + + if (!(bottommostInSelection(from) == INVALID_ROW || + from == INVALID_ROW || + topmostInSelection(from) == INVALID_ROW) + ) + { + if (from > to) + foreach(int i, selected_rows) + if (i + to - from < 0) + break; + else + m_items.move(i,i + to - from); + else + for (int i = selected_rows.count() - 1; i >= 0; i--) + if (selected_rows[i] + to -from >= m_items.count()) + break; + else + m_items.move(selected_rows[i],selected_rows[i] + to - from); + + m_current = m_items.indexOf(m_currentItem); + + emit listChanged(); + } +} + + + +int PlayListModel::topmostInSelection(int row) +{ + if (row == 0) + return 0; + + for (int i = row - 1;i >= 0;i--) + { + if (isSelected(i)) + continue; + else + return i + 1; + } + return 0; +} + +int PlayListModel::bottommostInSelection(int row) +{ + if (row >= m_items.count() - 1) + return row; + + for (int i = row + 1;i < count() ;i++) + { + if (isSelected(i)) + continue; + else + return i - 1; + } + return count() - 1; +} + +const SimpleSelection& PlayListModel::getSelection(int row) +{ + m_selection.m_top = topmostInSelection(row); + m_selection.m_anchor = row; + m_selection.m_bottom = bottommostInSelection(row); + m_selection.m_selected_rows = getSelectedRows(); + return m_selection; +} + +QList<int> PlayListModel::getSelectedRows() const +{ + QList<int>selected_rows; + for (int i = 0;i<m_items.count();i++) + { + if (m_items[i]->isSelected()) + { + selected_rows.append(i); + } + } + return selected_rows; +} + +QList< PlayListItem * > PlayListModel::getSelectedItems() const +{ + QList<PlayListItem*>selected_items; + for (int i = 0;i<m_items.count();i++) + { + if (m_items[i]->isSelected()) + { + selected_items.append(m_items[i]); + } + } + return selected_items; +} + +void PlayListModel::addToQueue() +{ + QList<PlayListItem*> selected_items = getSelectedItems(); + foreach(PlayListItem* file,selected_items) + {/* + if(isQueued(file)) + m_queued_songs.removeAt(m_queued_songs.indexOf(file)); + else + m_queued_songs.append(file); + */ + setQueued(file); + } + emit listChanged(); +} + +void PlayListModel::setQueued(PlayListItem* file) +{ + if (isQueued(file)) + m_queued_songs.removeAt(m_queued_songs.indexOf(file)); + else + m_queued_songs.append(file); + + emit listChanged(); +} + +bool PlayListModel::isQueued(PlayListItem* f) const +{ + return m_queued_songs.contains(f); +} + +void PlayListModel::setCurrentToQueued() +{ + setCurrent(row(m_queued_songs.at(0))); + m_queued_songs.pop_front(); +} + +bool PlayListModel::isEmptyQueue() const +{ + return m_queued_songs.isEmpty(); +} + +void PlayListModel::randomizeList() +{ + for (int i = 0;i < m_items.size();i++) + m_items.swap(qrand()%m_items.size(),qrand()%m_items.size()); + + m_current = m_items.indexOf(m_currentItem); + emit listChanged(); +} + +void PlayListModel::reverseList() +{ + for (int i = 0;i < m_items.size()/2;i++) + m_items.swap(i,m_items.size() - i - 1); + + m_current = m_items.indexOf(m_currentItem); + emit listChanged(); +} + +////===============THE BEGINNING OF SORT IMPLEMENTATION =======================//// + +// First we'll implement bundle of static compare procedures +// to sort items in different ways +static bool _titleLessComparator(PlayListItem* s1,PlayListItem* s2) +{ + return s1->title() < s2->title(); +} + +static bool _titleGreaterComparator(PlayListItem* s1,PlayListItem* s2) +{ + return s1->title() > s2->title(); +} + +static bool _pathAndFilenameLessComparator(PlayListItem* s1,PlayListItem* s2) +{ + return s1->url() < s2->url(); +} + +static bool _pathAndFilenameGreaterComparator(PlayListItem* s1,PlayListItem* s2) +{ + return s1->url() > s2->url(); +} + +static bool _filenameLessComparator(PlayListItem* s1,PlayListItem* s2) +{ + QFileInfo i_s1(s1->url()); + QFileInfo i_s2(s2->url()); + return i_s1.baseName() < i_s2.baseName(); +} + +static bool _filenameGreaterComparator(PlayListItem* s1,PlayListItem* s2) +{ + QFileInfo i_s1(s1->url()); + QFileInfo i_s2(s2->url()); + return i_s1.baseName() > i_s2.baseName(); +} + +static bool _dateLessComparator(PlayListItem* s1,PlayListItem* s2) +{ + return s1->year() < s2->year(); +} + +static bool _dateGreaterComparator(PlayListItem* s1,PlayListItem* s2) +{ + return s1->year() > s2->year(); +} + +static bool _trackLessComparator(PlayListItem* s1,PlayListItem* s2) +{ + return s1->track() < s2->track(); +} + +static bool _trackGreaterComparator(PlayListItem* s1,PlayListItem* s2) +{ + return s1->track() > s2->track(); +} + +// This is main sort method +void PlayListModel::doSort(int sort_mode,QList<PlayListItem*>& list_to_sort) +{ + QList<PlayListItem*>::iterator begin; + QList<PlayListItem*>::iterator end; + + begin = list_to_sort.begin(); + end = list_to_sort.end(); + + bool(*compareLessFunc)(PlayListItem*,PlayListItem*) = 0; + bool(*compareGreaterFunc)(PlayListItem*,PlayListItem*) = 0; + + switch (sort_mode) + { + case TITLE: + compareLessFunc = _titleLessComparator; + compareGreaterFunc = _titleGreaterComparator; + break; + case FILENAME: + compareLessFunc = _filenameLessComparator; + compareGreaterFunc = _filenameGreaterComparator; + break; + case PATH_AND_FILENAME: + compareLessFunc = _pathAndFilenameLessComparator; + compareGreaterFunc = _pathAndFilenameGreaterComparator; + break; + case DATE: + compareLessFunc = _dateLessComparator; + compareGreaterFunc = _dateGreaterComparator; + break; + //qWarning("TODO Sort by Date: %s\t%d",__FILE__,__LINE__); + case TRACK: + compareLessFunc = _trackLessComparator; + compareGreaterFunc = _trackGreaterComparator; + break; + default: + compareLessFunc = _titleLessComparator; + compareGreaterFunc = _titleGreaterComparator; + } + + static bool sorted_asc = false; + if (!sorted_asc) + { + qSort(begin,end,compareLessFunc); + sorted_asc = true; + } + else + { + qSort(begin,end,compareGreaterFunc); + sorted_asc = false; + } + + m_current = m_items.indexOf(m_currentItem); +} + +void PlayListModel::sortSelection(int mode) +{ + QList<PlayListItem*>selected_items = getSelectedItems(); + QList<int>selected_rows = getSelectedRows(); + + doSort(mode,selected_items); + + for (int i = 0;i < selected_rows.count();i++) + m_items.replace(selected_rows[i],selected_items[i]); + + m_current = m_items.indexOf(m_currentItem); + emit listChanged(); +} + +void PlayListModel::sort(int mode) +{ + doSort(mode,m_items); + emit listChanged(); +} + +////=============== THE END OF SORT IMPLEMENTATION =======================//// + +void PlayListModel::prepareForShufflePlaying(bool val) +{ + if (m_play_state) + delete m_play_state; + + if (val) + m_play_state = new ShufflePlayState(this); + else + m_play_state = new NormalPlayState(this); + +} + +void PlayListModel::prepareForRepeatablePlaying(bool val) +{ + is_repeatable_list = val; +} + +void PlayListModel::doCurrentVisibleRequest() +{ + emit currentChanged(); + emit listChanged(); +} + +void PlayListModel::setUpdatesEnabled(bool yes) +{ + if (yes) + { + m_block_update_signals = false; + emit listChanged(); + } + else + { + m_block_update_signals = true; + } +} + +void PlayListModel::loadPlaylist(const QString &f_name) +{ + PlaylistFormat* prs = PlaylistParser::instance()->findByPath(f_name); + if (prs) + { + QFile file(f_name); + if (file.open(QIODevice::ReadOnly)) + { + //clear(); + QStringList list = prs->decode(QTextStream(&file).readAll()); + for (int i = 0; i < list.size(); ++i) + { + if (QFileInfo(list.at(i)).isRelative() && !list.at(i).contains("://")) + QString path = list[i].prepend(QFileInfo(f_name).canonicalPath () + QDir::separator ()); + } + addFiles(list); + file.close(); + } + else + qWarning("Error opening %s",f_name.toLocal8Bit().data()); + } +} + +void PlayListModel::savePlaylist(const QString & f_name) +{ + PlaylistFormat* prs = PlaylistParser::instance()->findByPath(f_name); + if (prs) + { + QFile file(f_name); + if (file.open(QIODevice::WriteOnly)) + { + QTextStream ts(&file); + QList <AbstractPlaylistItem *> songs; + foreach(PlayListItem* item, m_items) + songs << item; + ts << prs->encode(songs); + file.close(); + } + else + qWarning("Error opening %s",f_name.toLocal8Bit().data()); + } +} + +bool PlayListModel::isFileLoaderRunning() const +{ + foreach(FileLoader* l,m_running_loaders) + if (l && l->isRunning()) + return TRUE; + + return FALSE; +} + +void PlayListModel::preparePlayState() +{ + m_play_state->prepare(); +} diff --git a/src/qmmpui/playlistmodel.h b/src/qmmpui/playlistmodel.h new file mode 100644 index 000000000..a5231b44f --- /dev/null +++ b/src/qmmpui/playlistmodel.h @@ -0,0 +1,353 @@ +/*************************************************************************** + * Copyright (C) 2006-2008 by Ilya Kotov * + * forkotov02@hotmail.ru * + * * + * 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. * + ***************************************************************************/ +#ifndef PLAYLISTMODEL_H +#define PLAYLISTMODEL_H + +#include <QObject> +#include <QString> +#include <QStringList> +#include <QMap> +#include <QPointer> +#include <QVector> + +//#include "fileloader.h" +class FileLoader; + +/** + @author Ilya Kotov <forkotov02@hotmail.ru> +*/ + +class PlayListItem; +class PlayState; +class PlaylistFormat; +class PlayListModel; + +struct SimpleSelection +{ + SimpleSelection() + { + ; + } + inline bool isValid()const + { + return (m_bottom != -1) && (m_anchor != -1) && (m_top != -1); + } + inline void dump()const + { + qWarning("top: %d\tbotom: %d\tanchor: %d",m_top,m_bottom,m_anchor); + } + inline int count()const + { + return m_bottom - m_top + 1; + } + int m_bottom; + int m_top; + int m_anchor; + QList<int>m_selected_rows; +}; + +/*! + * Helper class used for tags update after details dialog closing. + * @author Vladimir Kuznetsov <vovanec@gmail.com> + */ +class TagUpdater : public QObject +{ + Q_OBJECT + QObject* m_observable; + PlayListItem* m_item; +public: + TagUpdater(QObject* o, PlayListItem* item); +protected slots: + void updateTag(); +}; + + +class PlayListModel : public QObject +{ + Q_OBJECT +public: + PlayListModel(QObject *parent = 0); + + ~PlayListModel(); + + int count(); + PlayListItem* currentItem(); + int row(PlayListItem* item)const + { + return m_items.indexOf(item); + } + PlayListItem* item(int row)const + { + return m_items.at(row); + } + int currentRow(); + bool setCurrent (int); + bool isSelected(int); + void setSelected(int, bool); + + bool next(); + bool previous(); + + QList <QString> getTitles(int,int); + QList <QString> getTimes(int,int); + + void moveItems(int from,int to); + + /*! + * Returns \b true if \b f file is in play queue, else return \b false + */ + bool isQueued(PlayListItem* item) const; + + bool isRepeatableList()const + { + return is_repeatable_list; + } + + /*! + * Sets current song to the file that is nex in queue, if queue is empty - does nothing + */ + void setCurrentToQueued(); + + /*! + * Returns \b true if play queue is empty,otherwise - \b false. + */ + bool isEmptyQueue()const; + + /*! + * Returns index of \b f file in queue.e + */ + int queuedIndex(PlayListItem* item)const + { + return m_queued_songs.indexOf(item); + } + + /*! + * Returns current selection(playlist can contain a lot of selections, + * this method returns selection which \b row belongs to) + */ + const SimpleSelection& getSelection(int row); + + /*! + * Returns vector with selected rows indexes. + */ + QList<int> getSelectedRows()const; + /*! + * Returns vector of \b PlayListItem pointers that are selected. + */ + QList<PlayListItem*> getSelectedItems()const; + + QList<PlayListItem*> items()const + { + return m_items; + } + + /*! + * Returns number of first item that selected upper the \b row item. + */ + int firstSelectedUpper(int row); + + /*! + * Returns number of first item that selected lower the \b row item. + */ + int firstSelectedLower(int row); + + /*! + * Returns total lenght in seconds of all songs. + */ + int totalLength()const + { + return m_total_length; + } + + /*! + * Loads playlist with \b f_name name. + */ + void loadPlaylist(const QString& f_name); + + /*! + * Saves current songs to the playlist with \b f_name name. + */ + void savePlaylist(const QString& f_name); + + /*! + * Enum of available sort modes + */ + enum SortMode + { + TITLE,FILENAME,PATH_AND_FILENAME,DATE,TRACK + }; + +signals: + void listChanged(); + void currentChanged(); + void firstAdded(); + +public slots: + void load(PlayListItem *); + void clear(); + void clearSelection(); + void removeSelected(); + void removeUnselected(); + void invertSelection(); + void selectAll(); + void showDetails(); + void doCurrentVisibleRequest(); + + void addFile(const QString&); + + /*! + * Adds the list \b l of files to the model. + */ + void addFiles(const QStringList& l); + + /*! + * Adds \b dir to the model. + */ + void addDirectory(const QString& dir); + + /*! + * Loads list of files (regular files or directories), + * returns \b TRUE if at least one file has been successfully loaded, + * otherwise \b FALSE + */ + bool setFileList(const QStringList&); + + void addFileList(const QStringList &l); + + void randomizeList(); + void reverseList(); + + /*! + * Prepares model for shuffle playing. \b yes parameter is true - model iterates in shuffle mode. + */ + void prepareForShufflePlaying(bool yes); + + /*! + * Prepares model for shuffle playing. \b yes parameter is true - model iterates in repeat mode. + */ + void prepareForRepeatablePlaying(bool); + + /*! + * Sorts selected items in \b mode sort mode. + */ + void sortSelection(int mode); + + /*! + * Sorts items in \b mode sort mode. + */ + void sort(int mode); + + /*! + * Adds selected items to play queue. + */ + void addToQueue(); + + /*! + * Sets \b f media file to queue. + */ + void setQueued(PlayListItem* f); + + void preparePlayState(); + +private: + + /*! + * This internal method performs sorting of \b list_to_sort list of items. + */ + void doSort(int mode,QList<PlayListItem*>& list_to_sort); + /*! + * Returns topmost row in current selection + */ + int topmostInSelection(int); + + /*! + * Returns bottommost row in current selection + */ + int bottommostInSelection(int); + + /*! + * Creates and initializes file loader object. + */ + FileLoader* createFileLoader(); + + + /*! + * Is someone of file loaders is running? + */ + bool isFileLoaderRunning()const; + + /*! + * Removes items from model. If \b inverted is \b false - + * selected items will be removed, else - unselected. + */ + void removeSelection(bool inverted = false); + +private: + + QList <PlayListItem*> m_items; + QList <PlayListItem*> m_editing_items; + PlayListItem* m_currentItem; + + int m_current; + void readSettings(); + void writeSettings(); + + void setUpdatesEnabled(bool); + + bool updatesEnabled()const + { + return !m_block_update_signals; + } + + /*! + * This flyweight object represents current selection. + */ + SimpleSelection m_selection; + + /*! + * Songs in play queue. + */ + QList<PlayListItem*>m_queued_songs; + + /*! + * Is playlist repeatable? + */ + bool is_repeatable_list; + + /// Current playing state (Normal or Shuffle) + PlayState* m_play_state; + + bool m_block_update_signals; + + int m_total_length; + + typedef QPointer<FileLoader> GuardedFileLoader; + + /*! Vector of currently running file loaders. + * All loaders are automatically sheduled for deletion + * when finished. + */ + QVector<GuardedFileLoader> m_running_loaders; + + friend class MainWindow; +}; + + +#endif diff --git a/src/qmmpui/playstate.cpp b/src/qmmpui/playstate.cpp new file mode 100644 index 000000000..373619574 --- /dev/null +++ b/src/qmmpui/playstate.cpp @@ -0,0 +1,138 @@ +/*************************************************************************** + * Copyright (C) 2006 by Ilya Kotov * + * forkotov02@hotmail.ru * + * * + * 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. * + ***************************************************************************/ + +#include <playstate.h> + +ShufflePlayState::ShufflePlayState(PlayListModel * model) : PlayState(model) +{ + prepare(); +} + +bool ShufflePlayState::next() +{ + int itm_count = m_model->items().count(); + + if (itm_count > 0) + { + if (m_shuffled_current >= m_shuffled_indexes.count() -1 ) + { + if (!m_model->isRepeatableList()) + return FALSE; + else + prepare(); + } + + if (m_shuffled_current < m_shuffled_indexes.count() - 1)m_shuffled_current++; + + return m_model->setCurrent(m_shuffled_indexes.at(m_shuffled_current)); + } + return FALSE; +} + +bool ShufflePlayState::previous() +{ + int itm_count = m_model->items().count(); + + if (itm_count > 0) + { + if (m_shuffled_current <= 0) + { + if (!m_model->isRepeatableList()) + return FALSE; + else + { + prepare(); + m_shuffled_current = m_shuffled_indexes.count() - 1; + } + } + + if (itm_count > 1) m_shuffled_current --; + + m_model->setCurrent(m_shuffled_indexes.at(m_shuffled_current)); + return TRUE; + } + return FALSE; +} + +void ShufflePlayState::prepare() +{ + resetState(); + for (int i = 0;i < m_model->items().count();i++) + { + if (i != m_model->currentRow()) + m_shuffled_indexes << i; + } + + for (int i = 0;i < m_shuffled_indexes.count();i++) + m_shuffled_indexes.swap(qrand()%m_shuffled_indexes.size(),qrand()%m_shuffled_indexes.size()); + + m_shuffled_indexes.prepend(m_model->currentRow()); +} + +void ShufflePlayState::resetState() +{ + m_shuffled_indexes.clear(); + m_shuffled_current = 0; +} + + + + + +NormalPlayState::NormalPlayState(PlayListModel * model) : PlayState(model) +{} + + +bool NormalPlayState::next() +{ + int itm_count = m_model->items().count(); + + if (itm_count > 0) + { + if ( m_model->currentRow() == itm_count - 1) + { + if (m_model->isRepeatableList()) + return m_model->setCurrent(0); + else + return FALSE; + } + return m_model->setCurrent(m_model->currentRow() + 1); + } + else + return FALSE; +} + +bool NormalPlayState::previous() +{ + int itm_count = m_model->items().count(); + + if (itm_count > 0) + { + if ( m_model->currentRow() < 1 && !m_model->isRepeatableList()) + return FALSE; + else if (m_model->setCurrent(m_model->currentRow() - 1)) + return TRUE; + else if (m_model->isRepeatableList()) + return m_model->setCurrent(m_model->items().count() - 1); + } + + return FALSE; +} + diff --git a/src/qmmpui/playstate.h b/src/qmmpui/playstate.h new file mode 100644 index 000000000..e4af7fa6f --- /dev/null +++ b/src/qmmpui/playstate.h @@ -0,0 +1,109 @@ +/*************************************************************************** + * Copyright (C) 2006 by Ilya Kotov * + * forkotov02@hotmail.ru * + * * + * 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. * + ***************************************************************************/ + +#ifndef _PLAYSTATE_H +#define _PLAYSTATE_H + +/** + @author Vladimir Kuznetsov <vovanec@gmail.com> + */ + +#include <playlistmodel.h> + +/*! + * Abstract class that represents data model playing states + */ +class PlayState +{ +public: + /*! Makes single step forward through songs list. + * If the step has done returns \b true, otherwise \b false + */ + virtual bool next() = 0; + + /*! Makes single step back through songs list. + * If the step has done returns \b true, otherwise \b false + */ + virtual bool previous() = 0; + + /*! + * Service method, resets state to it's defaults. + */ + virtual void resetState() + { + ; + }; + + /*! + * Service method, can be used for state initializing. + */ + virtual void prepare() + { + ; + } + virtual ~PlayState() + { + ; + } + PlayState(PlayListModel* model) : m_model(model) + { + ; + } +protected: + + /// Data model + PlayListModel* m_model; +}; + +/*! + * Represents normal playing state. + * @author Vladimir Kuznetsov <vovanec@gmail.com> + */ +class NormalPlayState : public PlayState +{ +public: + virtual bool next(); + virtual bool previous(); + NormalPlayState(PlayListModel* model); +}; + +/*! + * Represents shuffle playing state. + * @author Vladimir Kuznetsov <vovanec@gmail.com> + */ +class ShufflePlayState : public PlayState +{ +public: + virtual bool next(); + virtual bool previous(); + virtual void prepare(); + ShufflePlayState(PlayListModel* model); + virtual void resetState(); +private: + + /// Current shuffled index. + int m_shuffled_current; + + /// List of indexes used for shuffled playing. + QList<int> m_shuffled_indexes; +}; + + +#endif diff --git a/src/qmmpui/qmmpui.pro b/src/qmmpui/qmmpui.pro index 3d60de0c8..08182560f 100644 --- a/src/qmmpui/qmmpui.pro +++ b/src/qmmpui/qmmpui.pro @@ -40,16 +40,22 @@ HEADERS += general.h \ filedialog.h \ filedialogfactory.h \ qtfiledialog.h \ - abstractplaylist.h \ - abstractplaylistitem.h + abstractplaylistitem.h \ + playlistitem.h \ + playlistmodel.h \ + playstate.h \ + fileloader.h SOURCES += general.cpp \ generalhandler.cpp \ playlistparser.cpp \ commandlinemanager.cpp \ filedialog.cpp \ qtfiledialog.cpp \ - abstractplaylist.cpp \ - abstractplaylistitem.cpp + abstractplaylistitem.cpp \ + playlistmodel.cpp \ + playstate.cpp \ + playlistitem.cpp \ + fileloader.cpp DESTDIR = . |
