promoe/backend_xmmsclient++/playlistmodel.cpp
2011-10-03 20:38:24 -07:00

634 lines
14 KiB
C++

/**
* This file is a part of Esperanza, an XMMS2 Client.
*
* Copyright (C) 2005-2007 XMMS2 Team
*
* 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.
*/
#include <xmmsclient/xmmsclient++.h>
#include "compat.h"
#include <QAbstractTableModel>
#include <QHash>
#include <QVariant>
#include <QIcon>
#include <QMimeData>
#include <QSettings>
#include <QUrl>
#include <QFileInfo>
#include "playlistmodel.h"
#include "xclient.h"
#include "xclientcache.h"
// Used to check for Protocolversion at compiletime
#include <xmmsc/xmmsc_idnumbers.h>
PlaylistModel::PlaylistModel (QObject *parent, XClient *client, const QString &name) : QAbstractItemModel (parent), m_current_pos (0)
{
// m_columns.append ("#");
m_columns.append ("Artist");
m_columns.append ("Album");
m_columns.append ("Title");
m_columns.append ("Duration");
// m_colfallback.append ("");
m_colfallback.append ("");
m_colfallback.append ("");
m_colfallback.append ("url");
m_colfallback.append ("");
m_cached_size.append (QSize ());
m_cached_size.append (QSize ());
m_cached_size.append (QSize ());
connect (client, SIGNAL(gotConnection (XClient *)), this, SLOT (got_connection (XClient *)));
connect (client->cache (), SIGNAL(entryChanged (uint32_t)), this, SLOT (entry_changed (uint32_t)));
m_isactive = (name == QLatin1String ("_active"));
m_name = name;
if (client->isConnected ()) {
got_connection (client);
}
}
void
PlaylistModel::set_playlist (const QString &name)
{
if (name == QLatin1String ("_active")) {
m_isactive = true;
m_client->playlist ()->currentActive () (Xmms::bind (&PlaylistModel::handle_current_pls, this));
} else {
m_isactive = false;
}
m_name = name;
m_client->playlist ()->listEntries (XClient::qToStd (name)) (Xmms::bind (&PlaylistModel::handle_list, this));
}
bool
PlaylistModel::handle_current_pls (const std::string &name)
{
if (m_name == QLatin1String ("_active")) {
m_name = XClient::stdToQ (name);
}
return true;
}
void
PlaylistModel::got_connection (XClient *client)
{
if (m_isactive) {
client->playlist ()->currentActive () (Xmms::bind (&PlaylistModel::handle_current_pls, this));
}
client->playlist ()->listEntries (XClient::qToStd (m_name)) (Xmms::bind (&PlaylistModel::handle_list, this));
client->playlist ()->currentPos () (Xmms::bind (&PlaylistModel::handle_update_pos, this));
client->playlist ()->broadcastChanged () (Xmms::bind (&PlaylistModel::handle_change, this));
client->playlist ()->broadcastCurrentPos () (Xmms::bind (&PlaylistModel::handle_update_pos, this));
client->playlist ()->broadcastLoaded () (Xmms::bind (&PlaylistModel::handle_pls_loaded, this));
m_client = client;
}
bool
PlaylistModel::handle_pls_loaded (const std::string &name)
{
if (m_isactive) {
m_client->playlist ()->listEntries (name)
(Xmms::bind (&PlaylistModel::handle_list, this));
m_name = XClient::stdToQ (name);
}
return true;
}
#if (XMMS_IPC_PROTOCOL_VERSION > 10)
bool
PlaylistModel::handle_update_pos (const Xmms::Dict &posdict)
{
QString changed_pl = XClient::stdToQ (posdict.get<std::string> ("name"));
if (changed_pl == m_name) {
#if HAVE_XMMSV
uint32_t pos = posdict.get<int32_t> ("position");
#else
uint32_t pos = posdict.get<uint32_t> ("position");
#endif
m_current_pos = pos;
emit currentPosChanged (index (pos, 0));
emit dataChanged(index (pos, 0), index (pos, m_columns.size ()));
}
return true;
}
#else
bool
PlaylistModel::handle_update_pos (const uint32_t &pos)
{
m_current_pos = pos;
emit currentPosChanged (index (pos, 0));
emit dataChanged(index (pos, 0), index (pos, m_columns.size ()));
return true;
}
#endif
QList<QString>
PlaylistModel::columns () const
{
return m_columns;
}
void
PlaylistModel::setColumns (const QList<QString> &new_columns)
{
m_columns = new_columns;
reset ();
}
void
PlaylistModel::setColumnFallback (const QList<QString> &new_columns)
{
m_colfallback = new_columns;
reset ();
}
bool
PlaylistModel::handle_change (const Xmms::Dict &chg)
{
int32_t change = chg.get<int32_t> ("type");
int32_t pos = 0, npos = 0;
int32_t id = 0;
QString s;
if (chg.contains ("position")) {
pos = chg.get<int32_t> ("position");
}
if (chg.contains ("id")) {
#if HAVE_XMMSV
id = chg.get<int32_t> ("id");
#else
id = chg.get<uint32_t> ("id");
#endif
}
if (chg.contains ("name")) {
s = XClient::stdToQ (chg.get<std::string> ("name"));
}
if (s != m_name) {
return true;
}
QModelIndex idx = QModelIndex ();
switch (change) {
case XMMS_PLAYLIST_CHANGED_ADD:
beginInsertRows (idx, pos, pos);
m_plist.append (id);
endInsertRows ();
break;
case XMMS_PLAYLIST_CHANGED_INSERT:
beginInsertRows (idx, pos, pos);
m_plist.insert (pos, id);
endInsertRows ();
break;
case XMMS_PLAYLIST_CHANGED_MOVE:
npos = chg.get<int32_t> ("newposition");
beginRemoveRows (idx, pos, pos);
m_plist.removeAt (pos);
endRemoveRows ();
beginInsertRows (idx, npos, npos);
m_plist.insert (npos, id);
endInsertRows ();
//if (pos < npos && pos)
// pos --;
emit entryMoved (index (pos, 0), index (npos, 0));
break;
case XMMS_PLAYLIST_CHANGED_REMOVE:
m_client->cache ()->invalidate (m_plist[pos]);
beginRemoveRows (idx, pos, pos);
m_plist.removeAt (pos);
endRemoveRows ();
break;
case XMMS_PLAYLIST_CHANGED_SHUFFLE:
case XMMS_PLAYLIST_CHANGED_SORT:
case XMMS_PLAYLIST_CHANGED_CLEAR:
m_client->cache ()->invalidate_all ();
m_client->playlist ()->listEntries () (Xmms::bind (&PlaylistModel::handle_list, this));
break;
}
/* TODO: call this only for the necessary methods */
emitTotalPlaytime ();
return true;
}
#if HAVE_XMMSV
bool
PlaylistModel::handle_list (const Xmms::List< int > &list)
#else
bool
PlaylistModel::handle_list (const Xmms::List< unsigned int > &list)
#endif
{
beginRemoveRows (QModelIndex (), 0, m_plist.size ());
m_plist.clear ();
endRemoveRows ();
int i = 0;
#if HAVE_XMMSV
for (Xmms::List< int >::const_iterator iter = list.begin();
iter != list.end(); ++iter) {
i++;
}
#else
for (list.first (); list.isValid (); ++list) {
i ++;
}
#endif
beginInsertRows (QModelIndex (), 0, i);
#if HAVE_XMMSV
for (Xmms::List< int >::const_iterator iter = list.begin();
iter != list.end(); ++iter) {
m_plist.append (*iter);
}
#else
for (list.first (); list.isValid (); ++list) {
m_plist.append (*list);
}
#endif
endInsertRows ();
emitTotalPlaytime ();
return true;
}
QModelIndexList
PlaylistModel::get_idxlist_by_id (uint32_t id)
{
QModelIndexList ret;
QList<uint32_t> l = getPosById (id);
for (int i = 0; i < l.count (); i++) {
ret.append (index (l.at (i), 0));
}
return ret;
}
QList<uint32_t>
PlaylistModel::getPosById (uint32_t id)
{
QList<uint32_t> ret;
int32_t pos = m_plist.indexOf (id);
while (pos != -1) {
ret.append (pos);
pos = m_plist.indexOf (id, pos + 1);
}
return ret;
}
void
PlaylistModel::entry_changed (uint32_t id)
{
QList<uint32_t> pos = getPosById (id);
for (int i = 0; i < pos.size (); i ++) {
QModelIndex idx1 = index (pos.at (i), 0);
QModelIndex idx2 = index (pos.at (i), m_columns.size ());
emit dataChanged(idx1, idx2);
}
emitTotalPlaytime ();
}
int
PlaylistModel::columnCount (const QModelIndex &parent) const
{
return m_columns.size ();
}
int
PlaylistModel::rowCount (const QModelIndex &parent) const
{
if (!parent.isValid ()) {
return m_plist.size ();
}
return 0;
}
QModelIndex
PlaylistModel::parent (const QModelIndex &idx) const
{
return QModelIndex ();
}
QModelIndex
PlaylistModel::index (int row, int column, const QModelIndex &parent) const
{
if (!parent.isValid ()) {
if (row > (m_plist.size () - 1))
return QModelIndex ();
if (row < 0)
return QModelIndex ();
return createIndex (row, column, -1);
}
return QModelIndex ();
}
QVariant
PlaylistModel::data (const QModelIndex &index, int role) const
{
if (!index.isValid ()) {
return QVariant ();
}
if (index.row () >= m_plist.size ()) {
return QVariant ();
}
if (role == MedialibIdRole) {
return QVariant (m_plist[index.row ()]);
}
if (role == Qt::SizeHintRole) {
if (m_cached_size[index.column ()].isValid ()) {
return QVariant (m_cached_size[index.column ()]);
}
return QVariant ();
}
if (role == Qt::DisplayRole || role == Qt::ToolTipRole) {
QString key = m_columns[index.column ()].toLower ();
QString fallkey = m_colfallback[index.column ()].toLower ();
if (key == "#") {
return QVariant (index.row ());
} else {
unsigned int id = m_plist[index.row ()];
PlaylistModel *fake = const_cast<PlaylistModel*> (this);
QHash<QString, QVariant> d = fake->m_client->cache ()->get_info (id);
if (d.contains (key)) {
return QVariant (d[key]);
} else if (d.contains (fallkey)) {
return QVariant (d[fallkey]);
}
return QVariant ();
}
} else if (role == CurrentEntryRole) {
int i = m_current_pos;
if (index.row () == i)
return QVariant (true);
return QVariant (false);
}
return QVariant ();
}
QStringList
PlaylistModel::mimeTypes () const
{
QStringList l ("application/x-xmms2poslist");
l << "application/x-xmms2mlibid";
l << "text/uri-list";
return l;
}
QMimeData *
PlaylistModel::mimeData (const QModelIndexList &list) const
{
QMimeData *ret = new QMimeData ();
QByteArray ba;
QDataStream stream (&ba, QIODevice::WriteOnly);
QList<int> l;
for (int i = 0; i < list.size (); i ++) {
QModelIndex idx = list.at (i);
if (idx.column () != 0)
continue;
l.append (idx.row ());
}
stream << l;
ret->setData ("application/x-xmms2poslist", ba);
return ret;
}
bool
PlaylistModel::dropMimeData (const QMimeData *data,
Qt::DropAction action,
int row, int column,
const QModelIndex & parent)
{
if (parent.internalId () != -1 && parent.isValid ()) {
return false;
}
if (data->hasFormat ("application/x-xmms2poslist")) {
if (!parent.isValid ())
return false;
QByteArray ba = data->data ("application/x-xmms2poslist");
QDataStream stream (&ba, QIODevice::ReadOnly);
QList<int> l;
stream >> l;
qSort (l);
int target = parent.row ();
int mod = 0;
while (l.size ()) {
int orow = l.takeAt (0) - mod;
m_client->playlist ()->moveEntry (orow, target) ();
if (orow < target) {
mod ++;
} else {
target ++;
}
}
return true;
} else if (data->hasFormat ("application/x-xmms2mlibid")) {
QByteArray ba = data->data ("application/x-xmms2mlibid");
QDataStream stream (&ba, QIODevice::ReadOnly);
QList<int> l;
stream >> l;
int target;
if (parent.isValid ())
target = parent.row () + 1;
else
target = m_plist.size () + 1;
while (l.size ()) {
int id = l.takeAt (0);
if (target >= m_plist.size ()) {
m_client->playlist ()->addId (id) ();
} else {
m_client->playlist ()->insertId (target ++, id) ();
}
}
return true;
} else if (data->hasFormat ("text/uri-list")) {
int target;
if (parent.isValid ())
target = parent.row () + 1;
else
target = m_plist.size () + 1;
QList<QUrl> l = data->urls ();
qSort (l);
for (int i = 0; i < l.size (); i++) {
QFileInfo fi (l.at (i).toLocalFile ());
std::string s ("file:///");
s.append (fi.absoluteFilePath ().toLocal8Bit ());
if (fi.isFile ()) {
if (target >= m_plist.size ()) {
m_client->playlist ()->addUrl (s) ();
} else {
m_client->playlist ()->insertUrl (target ++, s) ();
}
} else if (fi.isDir ()) {
m_client->playlist ()->addRecursive (s) ();
}
}
return true;
}
return false;
}
Qt::DropActions
PlaylistModel::supportedDropActions () const
{
return Qt::CopyAction | Qt::MoveAction;
}
QVariant
PlaylistModel::headerData (int section, Qt::Orientation orientation, int role) const
{
if (role != Qt::DisplayRole) {
return QVariant ();
}
if (orientation == Qt::Horizontal) {
if (section <= m_columns.size ())
return QVariant (m_columns[section]);
}
return QVariant ();
}
Qt::ItemFlags
PlaylistModel::flags (const QModelIndex &idx) const
{
// TODO: For now a workaround to enable drag and drop in promoe
if (!idx.isValid()) {
return 0;
}
unsigned int id = m_plist[idx.row ()];
PlaylistModel *fake = const_cast<PlaylistModel*> (this);
QHash<QString, QVariant> d = fake->m_client->cache ()->get_info (id);
Qt::ItemFlags f = Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
if (idx.isValid ()) {
f |= Qt::ItemIsDropEnabled;
}
if (d.contains ("status") && d["status"] != XMMS_MEDIALIB_ENTRY_STATUS_NOT_AVAILABLE) {
f |= Qt::ItemIsEnabled;
}
return f;
}
void
PlaylistModel::emitTotalPlaytime ()
{
bool isExact = true;
uint32_t time = 0;
foreach (uint32_t index, m_plist) {
QHash<QString, QVariant> data = m_client->cache ()->get_info (index,
false);
if (!data.isEmpty ()) {
time += data.value ("duration", 0).toInt ();
} else {
isExact = false;
}
}
emit totalPlaytime (time/1000, isExact);
}
uint32_t
PlaylistModel::getPlaytimeForSelection(const QModelIndexList &index_list)
{
uint32_t playtime = 0;
foreach (QModelIndex idx, index_list) {
int id = idx.row ();
if (id >= m_plist.size ()) continue;
QHash<QString, QVariant> data =
m_client->cache ()->get_info (m_plist.at (id), false);
if (!data.isEmpty ()) playtime += data.value ("duration", 0).toInt ();
}
return playtime/1000;
}
QList<uint32_t>
PlaylistModel::get_all_id ()
{
return m_plist;
}
void
PlaylistModel::removeRows (QModelIndexList index_list)
{
QList<uint32_t> idlist;
for (int i = 0; i < index_list.size (); ++i) {
QModelIndex idx = index_list.at(i);
if (idx.column () != 0)
continue;
idlist.append (idx.row ());
}
qSort (idlist);
/* Update of m_plist is done in handle_change through server notification */
for (int i = idlist.size () - 1; i >= 0; --i){
m_client->playlist ()->removeEntry (idlist.at(i));
}
}
#include "playlistmodel.moc"