aboutsummaryrefslogtreecommitdiff
path: root/src/qmmpui
diff options
context:
space:
mode:
authortrialuser02 <trialuser02@90c681e8-e032-0410-971d-27865f9a5e38>2008-12-13 18:14:00 +0000
committertrialuser02 <trialuser02@90c681e8-e032-0410-971d-27865f9a5e38>2008-12-13 18:14:00 +0000
commit43ee25ac78884c3c579a555fee589f3677ea7f21 (patch)
tree313df92c490392b1e501b2cc27bc220335f938fc /src/qmmpui
parent2d3e4370a6b7729a30b833aa72c79b6a9d2afdb6 (diff)
downloadqmmp-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.txt14
-rw-r--r--src/qmmpui/fileloader.cpp116
-rw-r--r--src/qmmpui/fileloader.h (renamed from src/qmmpui/abstractplaylist.h)52
-rw-r--r--src/qmmpui/playlistitem.cpp179
-rw-r--r--src/qmmpui/playlistitem.h (renamed from src/qmmpui/abstractplaylist.cpp)56
-rw-r--r--src/qmmpui/playlistmodel.cpp910
-rw-r--r--src/qmmpui/playlistmodel.h353
-rw-r--r--src/qmmpui/playstate.cpp138
-rw-r--r--src/qmmpui/playstate.h109
-rw-r--r--src/qmmpui/qmmpui.pro14
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 = .