/* * Cantata * * Copyright (c) 2011-2012 Craig Drummond * * ---- * * 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; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "musiclibraryitemalbum.h" #include "musiclibraryitemartist.h" #include "musiclibraryitemsong.h" #include "musiclibraryitemroot.h" #include "devicesmodel.h" #include "settings.h" #include "config.h" #include "covers.h" #include "itemview.h" #include "mpdparseutils.h" #include "mediadevicecache.h" #include "umsdevice.h" #include #include #ifdef ENABLE_KDE_SUPPORT #include #include #include #endif #ifdef ENABLE_KDE_SUPPORT K_GLOBAL_STATIC(DevicesModel, instance) #endif DevicesModel * DevicesModel::self() { #ifdef ENABLE_KDE_SUPPORT return instance; #else static DevicesModel *instance=0;; if(!instance) { instance=new DevicesModel; } return instance; #endif } DevicesModel::DevicesModel(QObject *parent) : QAbstractItemModel(parent) , itemMenu(0) , enabled(false) { connect(MediaDeviceCache::self(), SIGNAL(deviceRemoved(const QString &)), this, SLOT(deviceRemoved(const QString &))); updateItemMenu(); } DevicesModel::~DevicesModel() { qDeleteAll(devices); } QModelIndex DevicesModel::index(int row, int column, const QModelIndex &parent) const { if (!hasIndex(row, column, parent)) { return QModelIndex(); } if (parent.isValid()) { MusicLibraryItem *p=static_cast(parent.internalPointer()); if (p) { return rowchildren().count() ? createIndex(row, column, p->child(row)) : QModelIndex(); } } else { return row(index.internalPointer()); MusicLibraryItem *parentItem = childItem->parent(); if (parentItem) { return createIndex(parentItem->parent() ? parentItem->row() : devices.indexOf(static_cast(parentItem)), 0, parentItem); } else { return QModelIndex(); } } QVariant DevicesModel::headerData(int /*section*/, Qt::Orientation /*orientation*/, int /*role*/) const { return QVariant(); } int DevicesModel::rowCount(const QModelIndex &parent) const { if (parent.column() > 0) { return 0; } return parent.isValid() ? static_cast(parent.internalPointer())->childCount() : devices.count(); } int DevicesModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent) return 1; } QVariant DevicesModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { return QVariant(); } MusicLibraryItem *item = static_cast(index.internalPointer()); switch (role) { case Qt::DecorationRole: switch (item->type()) { case MusicLibraryItem::Type_Root: { QString iconName = static_cast(item)->icon(); return QIcon::fromTheme(iconName.isEmpty() ? QLatin1String("multimedia-player") : iconName); } case MusicLibraryItem::Type_Artist: { MusicLibraryItemArtist *artist = static_cast(item); return artist->isVarious() ? QIcon::fromTheme("cantata-view-media-artist-various") : QIcon::fromTheme("view-media-artist"); } case MusicLibraryItem::Type_Album: if (MusicLibraryItemAlbum::CoverNone==MusicLibraryItemAlbum::currentCoverSize()) { return QIcon::fromTheme("media-optical-audio"); } else { return static_cast(item)->cover(); } case MusicLibraryItem::Type_Song: return QIcon::fromTheme("audio-x-generic"); default: return QVariant(); } case Qt::DisplayRole: if (MusicLibraryItem::Type_Song==item->type()) { MusicLibraryItemSong *song = static_cast(item); if (static_cast(song->parent())->isSingleTracks()) { return song->song().artistSong(); } else if (song->track()>9) { return QString::number(song->track())+QLatin1String(" - ")+item->data(); } else if (song->track()>0) { return QChar('0')+QString::number(song->track())+QLatin1String(" - ")+item->data(); } } else if(MusicLibraryItem::Type_Album==item->type() && MusicLibraryItemAlbum::showDate() && static_cast(item)->year()>0) { return QString::number(static_cast(item)->year())+QLatin1String(" - ")+item->data(); } return item->data(); case Qt::ToolTipRole: switch (item->type()) { case MusicLibraryItem::Type_Root: return 0==item->childCount() ? item->data() : #ifdef ENABLE_KDE_SUPPORT i18np("%1\n1 Artist", "%1\n%2 Artists", item->data(), item->childCount()); #else (item->childCount()>1 ? tr("%1\n%2 Artists").arg(item->data()).arg(item->childCount()) : tr("%1\n1 Artist").arg(item->data())); #endif case MusicLibraryItem::Type_Artist: return 0==item->childCount() ? item->data() : #ifdef ENABLE_KDE_SUPPORT i18np("%1\n1 Album", "%1\n%2 Albums", item->data(), item->childCount()); #else (item->childCount()>1 ? tr("%1\n%2 Albums").arg(item->data()).arg(item->childCount()) : tr("%1\n1 Album").arg(item->data())); #endif case MusicLibraryItem::Type_Album: return 0==item->childCount() ? item->data() : #ifdef ENABLE_KDE_SUPPORT i18np("%1\n1 Track", "%1\n%2 Tracks", item->data(), item->childCount()); #else (item->childCount()>1 ? tr("%1\n%2 Tracks").arg(item->data()).arg(item->childCount()) : tr("%1\n1 Track").arg(item->data())); #endif case MusicLibraryItem::Type_Song: { QString duration=MPDParseUtils::formatDuration(static_cast(item)->time()); if (duration.startsWith(QLatin1String("00:"))) { duration=duration.mid(3); } if (duration.startsWith(QLatin1String("00:"))) { duration=duration.mid(1); } return data(index, Qt::DisplayRole).toString()+QChar('\n')+duration; } default: return QVariant(); } case ItemView::Role_ImageSize: if (MusicLibraryItem::Type_Album==item->type()) { return MusicLibraryItemAlbum::iconSize(); } break; case ItemView::Role_SubText: switch (item->type()) { case MusicLibraryItem::Type_Root: { Device *dev=static_cast(item); if (!dev->statusMessage().isEmpty()) { return dev->statusMessage(); } if (!dev->isConnected()) { return i18n("Not Connected"); } #ifdef ENABLE_KDE_SUPPORT return i18np("1 Artist", "%1 Artists", item->childCount()); #else return 1==item->childCount() ? tr("1 Artist") : tr("%1 Artists").arg(item->childCount()); #endif break; } case MusicLibraryItem::Type_Artist: #ifdef ENABLE_KDE_SUPPORT return i18np("1 Album", "%1 Albums", item->childCount()); #else return 1==item->childCount() ? tr("1 Album") : tr("%1 Albums").arg(item->childCount()); #endif break; case MusicLibraryItem::Type_Song: { QString text=MPDParseUtils::formatDuration(static_cast(item)->time()); if (text.startsWith(QLatin1String("00:"))) { return text.mid(3); } if (text.startsWith(QLatin1String("00:"))) { return text.mid(1); } return text.mid(1); } case MusicLibraryItem::Type_Album: #ifdef ENABLE_KDE_SUPPORT return i18np("1 Track", "%1 Tracks", item->childCount()); #else return 1==item->childCount() ? tr("1 Track") : tr("%1 Tracks").arg(item->childCount()); #endif default: return QVariant(); } case ItemView::Role_Image: if (MusicLibraryItem::Type_Album==item->type()) { QVariant v; v.setValue(static_cast(item)->cover()); return v; } return QVariant(); case ItemView::Role_Capacity: if (MusicLibraryItem::Type_Root==item->type()) { return static_cast(item)->usedCapacity(); } return QVariant(); case ItemView::Role_CapacityText: if (MusicLibraryItem::Type_Root==item->type()) { return static_cast(item)->capacityString(); } return QVariant(); default: return QVariant(); } return QVariant(); } void DevicesModel::clear() { beginResetModel(); qDeleteAll(devices); devices.clear(); indexes.clear(); endResetModel(); updateItemMenu(); // emit updated(rootItem); emit updateGenres(QStringList()); } void DevicesModel::setEnabled(bool e) { if (e==enabled) { return; } enabled=e; if (enabled) { connect(MediaDeviceCache::self(), SIGNAL(deviceAdded(const QString &)), this, SLOT(deviceAdded(const QString &))); connect(Covers::self(), SIGNAL(cover(const QString &, const QString &, const QImage &, const QString &)), this, SLOT(setCover(const QString &, const QString &, const QImage &, const QString &))); MediaDeviceCache::self()->refreshCache(); QStringList deviceUdiList=MediaDeviceCache::self()->getAll(); foreach (const QString &udi, deviceUdiList) { deviceAdded(udi); } if (devices.isEmpty()) { updateItemMenu(); } } else { disconnect(MediaDeviceCache::self(), SIGNAL(deviceAdded(const QString &)), this, SLOT(deviceAdded(const QString &))); disconnect(Covers::self(), SIGNAL(cover(const QString &, const QString &, const QImage &, const QString &)), this, SLOT(setCover(const QString &, const QString &, const QImage &, const QString &))); } } Device * DevicesModel::device(const QString &udi) { if (indexes.contains(udi)) { return devices.at(indexes[udi]); } return 0; } void DevicesModel::setCover(const QString &artist, const QString &album, const QImage &img, const QString &file) { Q_UNUSED(file) if (MusicLibraryItemAlbum::CoverNone==MusicLibraryItemAlbum::currentCoverSize()) { return; } if (img.isNull()) { return; } Song song; song.artist=artist; song.album=album; int i=0; foreach (MusicLibraryItem *device, devices) { MusicLibraryItemArtist *artistItem = static_cast(device)->artist(song, false); if (artistItem) { MusicLibraryItemAlbum *albumItem = artistItem->album(song, false); if (albumItem) { if (static_cast(albumItem)->setCover(img)) { QModelIndex idx=index(artistItem->children().indexOf(albumItem), 0, index(device->children().indexOf(artistItem), 0, index(i, 0, QModelIndex()))); emit dataChanged(idx, idx); } } } i++; } // foreach (const MusicLibraryItem *device, devices) { // bool found=false; // int j=0; // foreach (const MusicLibraryItem *artistItem, device->children()) { // if (artistItem->data()==artist) { // int k=0; // foreach (const MusicLibraryItem *albumItem, artistItem->children()) { // if (albumItem->data()==album) { // if (static_cast(albumItem)->setCover(img)) { // QModelIndex idx=index(k, 0, index(j, 0, index(i, 0, QModelIndex()))); // emit dataChanged(idx, idx); // } // found=true; // break; // } // k++; // } // if (found) { // break; // } // } // j++; // } // i++; // } } void DevicesModel::deviceAdded(const QString &udi) { if (!indexes.contains(udi)) { Device *dev=Device::create(this, udi); if (dev) { beginInsertRows(QModelIndex(), devices.count(), devices.count()); indexes.insert(udi, devices.count()); devices.append(dev); endInsertRows(); connect(dev, SIGNAL(updating(const QString &, bool)), SLOT(deviceUpdating(const QString &, bool))); updateItemMenu(); } } } void DevicesModel::deviceRemoved(const QString &udi) { if (indexes.contains(udi)) { int idx=indexes[udi]; beginRemoveRows(QModelIndex(), idx, idx); delete devices.takeAt(idx); indexes.remove(udi); endRemoveRows(); updateItemMenu(); } } void DevicesModel::deviceUpdating(const QString &udi, bool state) { if (indexes.contains(udi)) { int idx=indexes[udi]; Device *dev=devices.at(idx); if (state) { QModelIndex modelIndex=createIndex(idx, 0, dev); emit dataChanged(modelIndex, modelIndex); } else { if (dev->haveUpdate()) { dev->applyUpdate(); } QModelIndex modelIndex=createIndex(idx, 0, dev); emit dataChanged(modelIndex, modelIndex); QSet genres; foreach (Device *dev, devices) { genres+=dev->genres(); } QStringList genreList=genres.toList(); genreList.sort(); emit updateGenres(genreList); } } } Qt::ItemFlags DevicesModel::flags(const QModelIndex &index) const { if (index.isValid()) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled; } return Qt::ItemIsEnabled; } QStringList DevicesModel::filenames(const QModelIndexList &indexes) const { QStringList fnames; foreach(QModelIndex index, indexes) { MusicLibraryItem *item = static_cast(index.internalPointer()); switch (item->type()) { case MusicLibraryItem::Type_Root: foreach (const MusicLibraryItem *artist, item->children()) { foreach (const MusicLibraryItem *album, artist->children()) { QStringList sorted=static_cast(album)->sortedTracks(); foreach (const QString &f, sorted) { if(!fnames.contains(f)) { fnames << f; } } } } break; case MusicLibraryItem::Type_Artist: foreach (const MusicLibraryItem *album, item->children()) { QStringList sorted=static_cast(album)->sortedTracks(); foreach (const QString &f, sorted) { if(!fnames.contains(f)) { fnames << f; } } } break; case MusicLibraryItem::Type_Album: { QStringList sorted=static_cast(item)->sortedTracks(); foreach (const QString &f, sorted) { if(!fnames.contains(f)) { fnames << f; } } break; } case MusicLibraryItem::Type_Song: if (item->type() == MusicLibraryItem::Type_Song && !fnames.contains(static_cast(item)->file())) { fnames << static_cast(item)->file(); } break; default: break; } } return fnames; } QList DevicesModel::songs(const QModelIndexList &indexes) const { QList songs; foreach(QModelIndex index, indexes) { MusicLibraryItem *item = static_cast(index.internalPointer()); switch (item->type()) { case MusicLibraryItem::Type_Root: foreach (const MusicLibraryItem *artist, item->children()) { foreach (const MusicLibraryItem *album, artist->children()) { foreach (MusicLibraryItem *song, album->children()) { if (MusicLibraryItem::Type_Song==song->type() && !songs.contains(static_cast(song)->song())) { songs << static_cast(song)->song(); } } } } break; case MusicLibraryItem::Type_Artist: foreach (const MusicLibraryItem *album, item->children()) { foreach (MusicLibraryItem *song, album->children()) { if (MusicLibraryItem::Type_Song==song->type() && !songs.contains(static_cast(song)->song())) { songs << static_cast(song)->song(); } } } break; case MusicLibraryItem::Type_Album: foreach (MusicLibraryItem *song, item->children()) { if (MusicLibraryItem::Type_Song==song->type() && !songs.contains(static_cast(song)->song())) { songs << static_cast(song)->song(); } } break; case MusicLibraryItem::Type_Song: if (!songs.contains(static_cast(item)->song())) { songs << static_cast(item)->song(); } break; default: break; } } return songs; } void DevicesModel::emitAddToDevice() { QAction *act=qobject_cast(sender()); if (act) { emit addToDevice(act->data().toString()); } } void DevicesModel::updateItemMenu() { if (!itemMenu) { itemMenu = new QMenu(0); } itemMenu->clear(); if (devices.isEmpty()) { itemMenu->addAction(i18n("No Devices Attached"))->setEnabled(false); } else { QMultiMap items; foreach (const Device *d, devices) { items.insert(d->data(), d); } QMultiMap::ConstIterator it(items.begin()); QMultiMap::ConstIterator end(items.end()); for (; it!=end; ++it) { itemMenu->addAction(QIcon::fromTheme(it.value()->icon()), it.key(), this, SLOT(emitAddToDevice()))->setData(it.value()->dev().udi()); } } }