From 400acb077e63113de0b41009005087273ad05eba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20=C3=81ngel=20San=20Mart=C3=ADn?= Date: Sun, 18 Jan 2015 20:36:19 +0100 Subject: [PATCH] reading sublists are now sortable using Drag&Drop --- YACReaderLibrary/db/comic_model.cpp | 3 +- YACReaderLibrary/db/reading_list_item.cpp | 20 ++- YACReaderLibrary/db/reading_list_item.h | 1 + YACReaderLibrary/db/reading_list_model.cpp | 131 ++++++++++++++++-- YACReaderLibrary/db/reading_list_model.h | 4 + YACReaderLibrary/db_helper.cpp | 25 +++- YACReaderLibrary/db_helper.h | 3 +- .../yacreader_reading_lists_view.cpp | 10 ++ .../yacreader_reading_lists_view.h | 4 +- common/yacreader_global.h | 1 + 10 files changed, 182 insertions(+), 20 deletions(-) diff --git a/YACReaderLibrary/db/comic_model.cpp b/YACReaderLibrary/db/comic_model.cpp index b2243a7c..7a5c62a7 100644 --- a/YACReaderLibrary/db/comic_model.cpp +++ b/YACReaderLibrary/db/comic_model.cpp @@ -348,7 +348,8 @@ void ComicModel::setupReadingListModelData(unsigned long long parentReadingList, QSqlQuery subfolders(db); subfolders.prepare("SELECT id " "FROM reading_list " - "WHERE parentId = :parentId"); + "WHERE parentId = :parentId " + "ORDER BY ordering ASC"); subfolders.bindValue(":parentId", parentReadingList); subfolders.exec(); while(subfolders.next()) diff --git a/YACReaderLibrary/db/reading_list_item.cpp b/YACReaderLibrary/db/reading_list_item.cpp index 896c0596..4066cb12 100644 --- a/YACReaderLibrary/db/reading_list_item.cpp +++ b/YACReaderLibrary/db/reading_list_item.cpp @@ -1,4 +1,5 @@ #include "reading_list_item.h" +#include "qnaturalsorting.h" ListItem::ListItem(const QList &data) :itemData(data) @@ -125,10 +126,6 @@ ReadingListItem *ReadingListItem::child(int row) //items are sorted by order void ReadingListItem::appendChild(ReadingListItem *item) { - childItems.append(item); - item->parent = this; - return; //TODO - item->parent = this; if(childItems.isEmpty()) @@ -137,11 +134,17 @@ void ReadingListItem::appendChild(ReadingListItem *item) { if(item->parent->getId()==0) //sort by name, top level child { - + int i= 0; + while(iname(),item->name())) + i++; + childItems.insert(i,item); } else { - + int i= 0; + while(igetOrdering()getOrdering())) + i++; + childItems.insert(i,item); } /*ReadingListItem * last = childItems.back(); @@ -164,6 +167,11 @@ void ReadingListItem::appendChild(ReadingListItem *item) } +void ReadingListItem::appendChild(ReadingListItem *item, int pos) +{ + childItems.insert(pos, item); +} + void ReadingListItem::removeChild(ReadingListItem *item) { childItems.removeOne(item); diff --git a/YACReaderLibrary/db/reading_list_item.h b/YACReaderLibrary/db/reading_list_item.h index 9ee30aa1..e79fea62 100644 --- a/YACReaderLibrary/db/reading_list_item.h +++ b/YACReaderLibrary/db/reading_list_item.h @@ -69,6 +69,7 @@ public: int row() const; ReadingListItem * child(int row); void appendChild(ReadingListItem *item); + void appendChild(ReadingListItem *item, int pos); void removeChild(ReadingListItem *item); qulonglong getId() const; QString name() const; diff --git a/YACReaderLibrary/db/reading_list_model.cpp b/YACReaderLibrary/db/reading_list_model.cpp index 8124e26e..1c15e335 100644 --- a/YACReaderLibrary/db/reading_list_model.cpp +++ b/YACReaderLibrary/db/reading_list_model.cpp @@ -114,7 +114,10 @@ Qt::ItemFlags ReadingListModel::flags(const QModelIndex &index) const if(typeid(*item) == typeid(ReadingListSeparatorItem)) return 0; - return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled | Qt::ItemIsDragEnabled; + if(typeid(*item) == typeid(ReadingListItem) && static_cast(item)->parent->getId()!=0) + return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled | Qt::ItemIsDragEnabled; //only sublists are dragable + + return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled; } QVariant ReadingListModel::headerData(int section, Qt::Orientation orientation, int role) const @@ -183,7 +186,7 @@ QModelIndex ReadingListModel::parent(const QModelIndex &index) const ReadingListItem * childItem = static_cast(index.internalPointer()); ReadingListItem * parent = childItem->parent; if(parent->getId() != 0) - return createIndex(parent->row(), 0, parent); + return createIndex(parent->row()+specialLists.count()+labels.count()+2, 0, parent); } return QModelIndex(); @@ -208,13 +211,39 @@ bool ReadingListModel::canDropMimeData(const QMimeData *data, Qt::DropAction act return false; } - return data->formats().contains(YACReader::YACReaderLibrarComiscSelectionMimeDataFormat); + if(data->formats().contains(YACReader::YACReaderLibrarComiscSelectionMimeDataFormat)) + return true; + + if(rowIsReadingList(row,parent))// TODO avoid droping in a different parent + if(!parent.isValid()) + return false; + else + { + QList > sublistsRows; + QByteArray rawData = data->data(YACReader::YACReaderLibrarSubReadingListMimeDataFormat); + QDataStream in(&rawData,QIODevice::ReadOnly); + in >> sublistsRows; //deserialize the list of indentifiers + if(parent.row()!= sublistsRows.at(0).second) + return false; + return data->formats().contains(YACReader::YACReaderLibrarSubReadingListMimeDataFormat); + + } + + return false; } bool ReadingListModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { QLOG_DEBUG() << "drop mimedata into row = " << row << " column = " << column << "parent" << parent; + if(data->formats().contains(YACReader::YACReaderLibrarComiscSelectionMimeDataFormat)) + return dropComics(data, action, row, column, parent); + if(data->formats().contains(YACReader::YACReaderLibrarSubReadingListMimeDataFormat)) + return dropSublist(data, action, row, column, parent); +} + +bool ReadingListModel::dropComics(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) +{ QList comicIds; QByteArray rawData = data->data(YACReader::YACReaderLibrarComiscSelectionMimeDataFormat); QDataStream in(&rawData,QIODevice::ReadOnly); @@ -258,6 +287,71 @@ bool ReadingListModel::dropMimeData(const QMimeData *data, Qt::DropAction action return false; } +bool ReadingListModel::dropSublist(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) +{ + QList > sublistsRows; + QByteArray rawData = data->data(YACReader::YACReaderLibrarSubReadingListMimeDataFormat); + QDataStream in(&rawData,QIODevice::ReadOnly); + in >> sublistsRows; //deserialize the list of indentifiers + + QLOG_DEBUG() << "dropped : " << sublistsRows; + + int sourceRow = sublistsRows.at(0).first; + int destRow = row; + QModelIndex destParent = parent; + if(row == -1) + { + QLOG_DEBUG() << "droping inside parent"; + destRow = parent.row(); + destParent = parent.parent(); + } + QLOG_DEBUG() << "move " << sourceRow << "-" << destRow; + + if(sourceRow == destRow) + return false; + + //beginMoveRows(destParent,sourceRow,sourceRow,destParent,destRow); + + ReadingListItem * parentItem = static_cast(destParent.internalPointer()); + ReadingListItem * child = parentItem->child(sourceRow); + parentItem->removeChild(child); + parentItem->appendChild(child,destRow); + + reorderingChildren(parentItem->children()); + //endMoveRows(); + + return true; +} + +QMimeData *ReadingListModel::mimeData(const QModelIndexList &indexes) const +{ + QLOG_DEBUG() << "mimeData requested" << indexes; + + if(indexes.length() == 0) + { + QLOG_ERROR() << "mimeData requested: indexes is empty"; + return new QMimeData();//TODO what happens if 0 is returned? + } + + if(indexes.length() > 1) + QLOG_DEBUG() << "mimeData requested for more than one index, this shouldn't be possible"; + + QModelIndex modelIndex = indexes.at(0); + + QList > rows; + rows << QPair(modelIndex.row(),modelIndex.parent().row()); + QLOG_DEBUG() << "mimeData requested for row : " << modelIndex.row(); + + QByteArray data; + QDataStream out(&data,QIODevice::WriteOnly); + out << rows; //serialize the list of identifiers + + QMimeData * mimeData = new QMimeData(); + mimeData->setData(YACReader::YACReaderLibrarSubReadingListMimeDataFormat, data); + + return mimeData; +} + void ReadingListModel::setupReadingListsData(QString path) { beginResetModel(); @@ -331,17 +425,16 @@ void ReadingListModel::addReadingListAt(const QString &name, const QModelIndex & beginInsertRows(mi, 0, 0); //TODO calculate the right coordinates before inserting - qulonglong id = DBHelper::insertReadingSubList(name,mi.data(IDRole).toULongLong(),db); - ReadingListItem * newItem; ReadingListItem * readingListParent = static_cast(mi.internalPointer()); + qulonglong id = DBHelper::insertReadingSubList(name,mi.data(IDRole).toULongLong(),readingListParent->childCount(),db); + ReadingListItem * newItem; + readingListParent->appendChild(newItem = new ReadingListItem(QList() << name << id << false << true - << mi.data(IDRole).toULongLong())); - - + << readingListParent->childCount())); items.insert(id, newItem); @@ -430,6 +523,7 @@ void ReadingListModel::deleteItem(const QModelIndex &mi) { if(isEditable(mi)) { + QLOG_DEBUG() << "parent row :" << mi.parent().data() << "-" << mi.row(); beginRemoveRows(mi.parent(),mi.row(),mi.row()); QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath); @@ -439,8 +533,15 @@ void ReadingListModel::deleteItem(const QModelIndex &mi) if(typeid(*item) == typeid(ReadingListItem)) { ReadingListItem * rli = static_cast(item); + QLOG_DEBUG() << "num children : " << rli->parent->childCount(); rli->parent->removeChild(rli); + QLOG_DEBUG() << "num children : " << rli->parent->childCount(); DBHelper::removeListFromDB(item->getId(), db); + if(rli->parent->getId()!=0) + { + reorderingChildren(rli->parent->children()); + } + QLOG_DEBUG() << "num children : " << rli->parent->childCount(); } else if(typeid(*item) == typeid(LabelItem)) { @@ -607,6 +708,20 @@ int ReadingListModel::addLabelIntoList(LabelItem *item) return 0; } +void ReadingListModel::reorderingChildren(QList children) +{ + QList childrenIds; + int i = 0; + foreach (ReadingListItem * item, children) { + item->setOrdering(i++); + childrenIds << item->getId(); + } + + QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath); + DBHelper::reasignOrderToSublists(childrenIds, db); + QSqlDatabase::removeDatabase(_databasePath); +} + bool ReadingListModel::rowIsSpecialList(int row, const QModelIndex &parent) const { if(parent.isValid()) diff --git a/YACReaderLibrary/db/reading_list_model.h b/YACReaderLibrary/db/reading_list_model.h index 3f332c17..c60eaa3b 100644 --- a/YACReaderLibrary/db/reading_list_model.h +++ b/YACReaderLibrary/db/reading_list_model.h @@ -40,6 +40,7 @@ public: QModelIndex parent(const QModelIndex &index) const; bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const; bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); + QMimeData *mimeData(const QModelIndexList &indexes) const; //Convenience methods void setupReadingListsData(QString path); @@ -86,12 +87,15 @@ private: void setupLabels(QSqlDatabase &db); void setupReadingLists(QSqlDatabase &db); int addLabelIntoList(LabelItem *item); + void reorderingChildren(QList children); bool rowIsSpecialList(int row, const QModelIndex & parent = QModelIndex()) const; bool rowIsLabel(int row, const QModelIndex & parent = QModelIndex()) const; bool rowIsReadingList(int row, const QModelIndex & parent = QModelIndex()) const; bool rowIsSeparator(int row, const QModelIndex & parent = QModelIndex()) const; + bool dropComics(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); + bool dropSublist(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); //Special lists QList specialLists; diff --git a/YACReaderLibrary/db_helper.cpp b/YACReaderLibrary/db_helper.cpp index dbd6c0e2..bb51f8b0 100644 --- a/YACReaderLibrary/db_helper.cpp +++ b/YACReaderLibrary/db_helper.cpp @@ -410,6 +410,24 @@ void DBHelper::renameList(qulonglong id, const QString &name, QSqlDatabase &db) renameLabelQuery.exec(); } +void DBHelper::reasignOrderToSublists(QList ids, QSqlDatabase &db) +{ + QSqlQuery updateOrdering(db); + updateOrdering.prepare("UPDATE reading_list SET " + "ordering = :ordering " + "WHERE id = :id"); + db.transaction(); + int order = 0; + foreach(qulonglong id, ids) + { + updateOrdering.bindValue(":ordering",order++); + updateOrdering.bindValue(":id", id); + updateOrdering.exec(); + } + + db.commit(); +} + //inserts qulonglong DBHelper::insert(Folder * folder, QSqlDatabase & db) { @@ -472,13 +490,14 @@ qulonglong DBHelper::insertReadingList(const QString &name, QSqlDatabase &db) return query.lastInsertId().toULongLong(); } -qulonglong DBHelper::insertReadingSubList(const QString &name, qulonglong parentId, QSqlDatabase &db) +qulonglong DBHelper::insertReadingSubList(const QString &name, qulonglong parentId, int ordering, QSqlDatabase &db) { QSqlQuery query(db); - query.prepare("INSERT INTO reading_list (name, parentId) " - "VALUES (:name, :parentId)"); + query.prepare("INSERT INTO reading_list (name, parentId, ordering) " + "VALUES (:name, :parentId, :ordering)"); query.bindValue(":name", name); query.bindValue(":parentId", parentId); + query.bindValue(":ordering", ordering); query.exec(); return query.lastInsertId().toULongLong(); } diff --git a/YACReaderLibrary/db_helper.h b/YACReaderLibrary/db_helper.h index 55f58bd8..55405bdf 100644 --- a/YACReaderLibrary/db_helper.h +++ b/YACReaderLibrary/db_helper.h @@ -45,7 +45,7 @@ public: static qulonglong insert(ComicDB * comic, QSqlDatabase & db); static qulonglong insertLabel(const QString & name, YACReader::LabelColors color , QSqlDatabase & db); static qulonglong insertReadingList(const QString & name, QSqlDatabase & db); - static qulonglong insertReadingSubList(const QString & name, qulonglong parentId, QSqlDatabase & db); + static qulonglong insertReadingSubList(const QString & name, qulonglong parentId, int ordering, QSqlDatabase & db); static void insertComicsInFavorites(const QList & comicsList, QSqlDatabase & db); static void insertComicsInLabel(const QList & comicsList, qulonglong labelId, QSqlDatabase & db); static void insertComicsInReadingList(const QList & comicsList, qulonglong readingListId, QSqlDatabase & db); @@ -58,6 +58,7 @@ public: static void updateProgress(qulonglong libraryId,const ComicInfo & comicInfo); static void renameLabel(qulonglong id, const QString & name, QSqlDatabase & db); static void renameList(qulonglong id, const QString & name, QSqlDatabase & db); + static void reasignOrderToSublists(QList ids, QSqlDatabase & db); static QList getFoldersFromParent(qulonglong parentId, QSqlDatabase & db, bool sort = true); static QList getSortedComicsFromParent(qulonglong parentId, QSqlDatabase & db); diff --git a/YACReaderLibrary/yacreader_reading_lists_view.cpp b/YACReaderLibrary/yacreader_reading_lists_view.cpp index 3c1e1970..b95a567e 100644 --- a/YACReaderLibrary/yacreader_reading_lists_view.cpp +++ b/YACReaderLibrary/yacreader_reading_lists_view.cpp @@ -8,6 +8,9 @@ YACReaderReadingListsView::YACReaderReadingListsView(QWidget *parent) { setItemDelegate(new YACReaderReadingListsViewItemDeletegate(this)); setUniformRowHeights(false); + + //enabling internal drag&drop + setDragDropMode(QAbstractItemView::DragDrop); } void YACReaderReadingListsView::dragEnterEvent(QDragEnterEvent *event) @@ -27,6 +30,13 @@ void YACReaderReadingListsView::dragMoveEvent(QDragMoveEvent *event) event->acceptProposedAction(); } +void YACReaderReadingListsView::dropEvent(QDropEvent *event) +{ + YACReaderTreeView::dropEvent(event); + + +} + //---------------------------------------------------------------------- YACReaderReadingListsViewItemDeletegate::YACReaderReadingListsViewItemDeletegate(QObject *parent) diff --git a/YACReaderLibrary/yacreader_reading_lists_view.h b/YACReaderLibrary/yacreader_reading_lists_view.h index 5c5b3cfc..31cb099b 100644 --- a/YACReaderLibrary/yacreader_reading_lists_view.h +++ b/YACReaderLibrary/yacreader_reading_lists_view.h @@ -12,9 +12,11 @@ public: explicit YACReaderReadingListsView(QWidget * parent = 0); protected: - //Drop to import + //Drop to import & internal Drag&Drop for resorting void dragEnterEvent(QDragEnterEvent *event); void dragMoveEvent(QDragMoveEvent *event); + void dropEvent(QDropEvent *event); + }; class YACReaderReadingListsViewItemDeletegate: public QStyledItemDelegate diff --git a/common/yacreader_global.h b/common/yacreader_global.h index 300f9ea0..ca13e872 100644 --- a/common/yacreader_global.h +++ b/common/yacreader_global.h @@ -73,6 +73,7 @@ namespace YACReader { static const QString YACReaderLibrarComiscSelectionMimeDataFormat = "application/yacreaderlibrary-comics-ids"; +static const QString YACReaderLibrarSubReadingListMimeDataFormat = "application/yacreaderlibrary-sublist-rows"; enum FlowType {