226 lines
6.9 KiB
C++
226 lines
6.9 KiB
C++
#include "playlistlistmodel.h"
|
|
#include "core/logging.h"
|
|
|
|
#include <QMimeData>
|
|
|
|
PlaylistListModel::PlaylistListModel(QObject* parent)
|
|
: QStandardItemModel(parent), dropping_rows_(false) {
|
|
connect(this, SIGNAL(dataChanged(QModelIndex, QModelIndex)),
|
|
SLOT(RowsChanged(QModelIndex, QModelIndex)));
|
|
connect(this, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)),
|
|
SLOT(RowsAboutToBeRemoved(QModelIndex, int, int)));
|
|
connect(this, SIGNAL(rowsInserted(QModelIndex, int, int)),
|
|
SLOT(RowsInserted(QModelIndex, int, int)));
|
|
}
|
|
|
|
void PlaylistListModel::SetIcons(const QIcon& playlist_icon,
|
|
const QIcon& folder_icon) {
|
|
playlist_icon_ = playlist_icon;
|
|
folder_icon_ = folder_icon;
|
|
}
|
|
|
|
bool PlaylistListModel::dropMimeData(const QMimeData* data,
|
|
Qt::DropAction action, int row, int column,
|
|
const QModelIndex& parent) {
|
|
dropping_rows_ = true;
|
|
bool ret =
|
|
QStandardItemModel::dropMimeData(data, action, row, column, parent);
|
|
dropping_rows_ = false;
|
|
|
|
return ret;
|
|
}
|
|
|
|
QString PlaylistListModel::ItemPath(const QStandardItem* item) const {
|
|
QStringList components;
|
|
|
|
const QStandardItem* current = item;
|
|
while (current) {
|
|
if (current->data(Role_Type).toInt() == Type_Folder) {
|
|
components.insert(0, current->data(Qt::DisplayRole).toString());
|
|
}
|
|
current = current->parent();
|
|
}
|
|
|
|
return components.join("/");
|
|
}
|
|
|
|
void PlaylistListModel::RowsChanged(const QModelIndex& begin,
|
|
const QModelIndex& end) {
|
|
AddRowMappings(begin, end);
|
|
}
|
|
|
|
void PlaylistListModel::RowsInserted(const QModelIndex& parent, int start,
|
|
int end) {
|
|
// RowsChanged will take care of these when dropping.
|
|
if (!dropping_rows_) {
|
|
AddRowMappings(index(start, 0, parent), index(end, 0, parent));
|
|
}
|
|
}
|
|
|
|
void PlaylistListModel::AddRowMappings(const QModelIndex& begin,
|
|
const QModelIndex& end) {
|
|
const QString parent_path = ItemPath(itemFromIndex(begin));
|
|
|
|
for (int i = begin.row(); i <= end.row(); ++i) {
|
|
const QModelIndex index = begin.sibling(i, 0);
|
|
QStandardItem* item = itemFromIndex(index);
|
|
AddRowItem(item, parent_path);
|
|
}
|
|
}
|
|
|
|
void PlaylistListModel::AddRowItem(QStandardItem* item,
|
|
const QString& parent_path) {
|
|
switch (item->data(Role_Type).toInt()) {
|
|
case Type_Track: {
|
|
// const int id = item->data(Role_TrackId).toInt();
|
|
// TODO
|
|
break;
|
|
}
|
|
|
|
case Type_Playlist: {
|
|
const int id = item->data(Role_PlaylistId).toInt();
|
|
playlists_by_id_[id] = item;
|
|
if (dropping_rows_) {
|
|
emit PlaylistPathChanged(id, parent_path);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case Type_Folder:
|
|
for (int j = 0; j < item->rowCount(); ++j) {
|
|
QStandardItem* child_item = item->child(j);
|
|
AddRowItem(child_item, parent_path);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void PlaylistListModel::RowsAboutToBeRemoved(const QModelIndex& parent,
|
|
int start, int end) {
|
|
for (int i = start; i <= end; ++i) {
|
|
const QModelIndex idx = index(i, 0, parent);
|
|
const QStandardItem* item = itemFromIndex(idx);
|
|
|
|
switch (idx.data(Role_Type).toInt()) {
|
|
case Type_Playlist: {
|
|
const int id = idx.data(Role_PlaylistId).toInt();
|
|
QMap<int, QStandardItem*>::Iterator it = playlists_by_id_.find(id);
|
|
if (it != playlists_by_id_.end() && it.value() == item) {
|
|
playlists_by_id_.erase(it);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Type_Folder:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
QStandardItem* PlaylistListModel::PlaylistById(int id) const {
|
|
return playlists_by_id_[id];
|
|
}
|
|
|
|
QStandardItem* PlaylistListModel::FolderByPath(const QString& path) {
|
|
if (path.isEmpty()) {
|
|
return invisibleRootItem();
|
|
}
|
|
|
|
// Walk down from the root until we find the target folder. This is pretty
|
|
// inefficient but maintaining a path -> item map is difficult.
|
|
QStandardItem* parent = invisibleRootItem();
|
|
|
|
const QStringList parts = path.split('/', QString::SkipEmptyParts);
|
|
for (const QString& part : parts) {
|
|
QStandardItem* matching_child = nullptr;
|
|
|
|
const int child_count = parent->rowCount();
|
|
for (int i = 0; i < child_count; ++i) {
|
|
if (parent->child(i)->data(Qt::DisplayRole).toString() == part) {
|
|
matching_child = parent->child(i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Does this folder exist already?
|
|
if (matching_child) {
|
|
parent = matching_child;
|
|
} else {
|
|
QStandardItem* child = NewFolder(part);
|
|
parent->appendRow(child);
|
|
parent = child;
|
|
}
|
|
}
|
|
|
|
return parent;
|
|
}
|
|
|
|
QStandardItem* PlaylistListModel::NewFolder(const QString& name) const {
|
|
QStandardItem* ret = new QStandardItem;
|
|
ret->setText(name);
|
|
ret->setData(PlaylistListModel::Type_Folder, PlaylistListModel::Role_Type);
|
|
ret->setIcon(folder_icon_);
|
|
ret->setFlags(Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled |
|
|
Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable);
|
|
return ret;
|
|
}
|
|
|
|
QStandardItem* PlaylistListModel::NewPlaylist(const QString& name,
|
|
int id) const {
|
|
QStandardItem* ret = new QStandardItem;
|
|
ret->setText(name);
|
|
ret->setData(PlaylistListModel::Type_Playlist, PlaylistListModel::Role_Type);
|
|
ret->setData(id, PlaylistListModel::Role_PlaylistId);
|
|
ret->setIcon(playlist_icon_);
|
|
ret->setFlags(Qt::ItemIsDragEnabled | Qt::ItemIsEnabled |
|
|
Qt::ItemIsSelectable | Qt::ItemIsEditable);
|
|
return ret;
|
|
}
|
|
|
|
QStandardItem* PlaylistListModel::NewTrack(const Song& song) const {
|
|
QStandardItem* ret = new QStandardItem;
|
|
ret->setText(song.artist() + " - " + song.title());
|
|
ret->setData(PlaylistListModel::Type_Track, PlaylistListModel::Role_Type);
|
|
ret->setData(song.id(), PlaylistListModel::Role_TrackId);
|
|
ret->setIcon(track_icon_);
|
|
ret->setFlags(Qt::ItemIsDragEnabled | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
|
|
return ret;
|
|
}
|
|
|
|
bool PlaylistListModel::setData(const QModelIndex& index, const QVariant& value,
|
|
int role) {
|
|
if (!QStandardItemModel::setData(index, value, role)) {
|
|
return false;
|
|
}
|
|
|
|
switch (index.data(Role_Type).toInt()) {
|
|
case Type_Playlist:
|
|
emit PlaylistRenamed(index.data(Role_PlaylistId).toInt(),
|
|
value.toString());
|
|
break;
|
|
|
|
case Type_Folder:
|
|
// Walk all the children and modify their paths.
|
|
UpdatePathsRecursive(index);
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void PlaylistListModel::UpdatePathsRecursive(const QModelIndex& parent) {
|
|
switch (parent.data(Role_Type).toInt()) {
|
|
case Type_Playlist:
|
|
emit PlaylistPathChanged(parent.data(Role_PlaylistId).toInt(),
|
|
ItemPath(itemFromIndex(parent)));
|
|
break;
|
|
|
|
case Type_Folder:
|
|
for (int i = 0; i < rowCount(parent); ++i) {
|
|
UpdatePathsRecursive(index(i, 0, parent));
|
|
}
|
|
break;
|
|
}
|
|
}
|