]> Some of my projects - localmylist.git/commitdiff
New implementation of MyListModel
authorAPTX <marek321@gmail.com>
Mon, 25 Mar 2013 15:30:14 +0000 (16:30 +0100)
committerAPTX <marek321@gmail.com>
Mon, 25 Mar 2013 15:30:14 +0000 (16:30 +0100)
localmylist/localmylist.pro
localmylist/mylistmodel.cpp
localmylist/mylistmodel.h
localmylist/mylistnode.cpp
localmylist/mylistnode.h
localmylist/mylistnodedata.cpp [new file with mode: 0644]
localmylist/mylistnodedata.h [new file with mode: 0644]
management-gui/mainwindow.cpp
management-gui/mylistview.cpp

index ba79b19b9ba79838873beb2655308026236d5ff1..6f50ac1134f79657ef5ad6c11d2183736881d992 100644 (file)
@@ -18,6 +18,7 @@ SOURCES += \
        mylist.cpp \
        mylistmodel.cpp \
        mylistnode.cpp \
+       mylistnodedata.cpp \
        animetitleparsetask.cpp \
        mylistexportparsetask.cpp \
        settings.cpp \
@@ -42,6 +43,7 @@ HEADERS += \
        mylist.h \
        mylistmodel.h \
        mylistnode.h \
+       mylistnodedata.h \
        animetitleparsetask.h \
        mylistexportparsetask.h \
        settings.h \
@@ -54,7 +56,8 @@ HEADERS += \
        sqlquery.h \
        sqlasyncquery.h \
        sqlasyncqueryinternal.h \
-       asyncquerytask.h
+       asyncquerytask.h \
+       sqlresultiteratorinterface.h
 
 CONV_HEADERS += \
        include/LocalMyList/AbstractTask \
index 330e8d77bae8887b4257a186d9214c497ef463f3..d7a38c9d71a2b258c3edac394767b1d909813b5a 100644 (file)
@@ -7,9 +7,11 @@
 namespace LocalMyList {
 
 MyListModel::MyListModel(QObject *parent) :
-       QAbstractItemModel(parent)
+       QAbstractItemModel(parent), delayedFetchNode(0)
 {
-       rootItem = new MyListNode();
+       rootItem = new MyListNode(this);
+       delayFetchTimer.setSingleShot(true);
+       connect(&delayFetchTimer, SIGNAL(timeout()), this, SLOT(finishDelayedFetch()));
 }
 
 MyListModel::~MyListModel()
@@ -103,10 +105,13 @@ int MyListModel::columnCount(const QModelIndex &parent) const
 
 bool MyListModel::canFetchMore(const QModelIndex &parent) const
 {
+       MyListNode *parentItem;
        if (parent.isValid())
-               return static_cast<MyListNode *>(parent.internalPointer())->canFetchMore();
+               parentItem = static_cast<MyListNode *>(parent.internalPointer());
        else
-               return rootItem->canFetchMore();
+               parentItem = rootItem;
+
+       return parentItem->canFetchMore();
 }
 
 void MyListModel::fetchMore(const QModelIndex &parent)
@@ -118,16 +123,11 @@ void MyListModel::fetchMore(const QModelIndex &parent)
        else
                node = rootItem;
 
-       int newrows = node->fetchMore();
-
-       if (!newrows)
+       if (node->isWorking())
                return;
 
-       beginInsertRows(parent, rowCount(parent), rowCount(parent) + newrows - 1);
-       node->addFetched();
-       endInsertRows();
-
-       qDebug() << "added" << newrows << "new rows";
+       node->setWorking(true);
+       node->fetchMore();
 }
 
 bool MyListModel::hasChildren(const QModelIndex &parent) const
@@ -149,8 +149,52 @@ void MyListModel::reload()
 {
        beginResetModel();
        delete rootItem;
-       rootItem = new MyListNode();
+       rootItem = new MyListNode(this);
        endResetModel();
 }
 
+void MyListModel::finishDelayedFetch()
+{
+       if (delayedFetchNode)
+               delayedFetchNode->setWorking(false);
+       delayedFetchNode = 0;
+}
+
+QModelIndex MyListModel::index(MyListNode *node) const
+{
+       if (!node || node == rootItem)
+               return QModelIndex();
+       return createIndex(node->row(), 0, node);
+}
+
+void MyListModel::fetchFinished(MyListNode *node, int newrows)
+{
+       if (!newrows)
+               return;
+
+       const QModelIndex parent = index(node);
+
+
+       beginInsertRows(parent, rowCount(parent), rowCount(parent) + newrows - 1);
+       node->addFetched();
+       endInsertRows();
+
+       // Delay fetching of more rows untill they are really needed.
+       // Only for the root node
+       // Lazy loading didn't work before for children, not sure why
+       // TODO figure out if it can be fixed
+       // Enableing this for non-root makes it never fetch more.
+       if (node == rootItem)
+       {
+               delayFetchTimer.start(0);
+               delayedFetchNode = node;
+       }
+       else
+       {
+               node->setWorking(false);
+       }
+
+       qDebug() << "added" << newrows << "new rows";
+}
+
 } // namespace LocalMyList
index 78f1880c4c9b916bf7136006a538bc5b7bb0a845..b1315b786509d0f9063cb3dda65ad9b7e3bd7c0f 100644 (file)
@@ -3,6 +3,12 @@
 
 #include "localmylist_global.h"
 #include <QAbstractItemModel>
+#include <QTimer>
+
+#include "databaseclasses.h"
+#include "mylistnodedata.h"
+
+#include <boost/intrusive/set.hpp>
 
 namespace LocalMyList {
 
@@ -11,9 +17,14 @@ class MyListNode;
 class LOCALMYLISTSHARED_EXPORT MyListModel : public QAbstractItemModel
 {
        Q_OBJECT
+       friend class MyListNode;
+       friend class MyListAnimeNode;
+       friend class MyListEpisodeNode;
+       friend class MyListFileNode;
+       friend class MyListFileLocationNode;
 
 public:
-       enum NodeType {
+       enum class NodeType {
                None,
                Anime,
                Episode,
@@ -24,9 +35,16 @@ public:
        explicit MyListModel(QObject *parent = 0);
        ~MyListModel();
 
+       Anime anime(int id) const;
+       Anime anime(const QModelIndex &index) const;
+       QModelIndex animeIndex(int id) const;
+
+       File file(int id) const;
+       QModelIndex fileIndex(int id) const;
+
        QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
        Qt::ItemFlags flags(const QModelIndex &index) const;
-       QVariant data(const QModelIndex &index, int role) const;
+       QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
        QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
        QModelIndex parent(const QModelIndex &index) const;
 
@@ -37,14 +55,31 @@ public:
        bool canFetchMore(const QModelIndex &parent) const;
        void fetchMore(const QModelIndex &parent);
        bool hasChildren(const QModelIndex &parent = QModelIndex()) const;
+
 signals:
-       
+
 public slots:
        MyListNode *node(const QModelIndex &idx) const;
 
        void reload();
+
+private slots:
+       void finishDelayedFetch();
+
+protected:
+       QModelIndex index(MyListNode *node) const;
+       void fetchFinished(MyListNode *node, int newrows);
+
 private:
+       boost::intrusive::set<AnimeData> animeSet;
+       boost::intrusive::set<EpisodeData> episodeSet;
+       boost::intrusive::set<FileData> fileSet;
+       boost::intrusive::set<FileLocationData> fileLocationSet;
+
        MyListNode *rootItem;
+
+       QTimer delayFetchTimer;
+       MyListNode *delayedFetchNode;
 };
 
 } // namespace LocalMyList
index 4a4ea67654e8268d559134defed9f0280c966557..7ab2082450b619904ae8d0aad0fee975a791db7a 100644 (file)
@@ -1,22 +1,25 @@
 #include "mylistnode.h"
 
+#include "mylistmodel.h"
 #include "mylist.h"
 #include "database.h"
 #include <QSqlQuery>
-
+#include "sqlasyncquery.h"
 #include <QDebug>
 
 namespace LocalMyList {
 
-MyListNode::MyListNode(NodeType type, int id, const QList<QVariant> &data, MyListNode *parent) :
-       m_totalRowCount(-1), fetchedRowCount(0)
+MyListNode::MyListNode(MyListModel *model_, NodeType type, MyListNode *parent) :
+       m_totalRowCount(-1), fetchedRowCount(0), m_working(false)
 {
        m_type = type;
-       m_id = id;
        parentItem = parent;
-       itemData = data;
+       model = model_;
+
+       query = new SqlAsyncQuery;
+       QObject::connect(query, &SqlAsyncQuery::resultReady, [this](){ fetchComplete();});
 
-       if (m_type != Root)
+       if (m_type != RootNode)
                return;
 
        itemData << "Title" << "Episode / Version" << "Rating / Quality" << "Vote" << "Watched / Renamed";
@@ -25,6 +28,7 @@ MyListNode::MyListNode(NodeType type, int id, const QList<QVariant> &data, MyLis
 MyListNode::~MyListNode()
 {
        qDeleteAll(childItems);
+       delete query;
 }
 
 MyListNode *MyListNode::child(int row)
@@ -47,7 +51,7 @@ int MyListNode::row() const
 
 int MyListNode::columnCount() const
 {
-       return itemData.count();
+       return 5;
 }
 
 QVariant MyListNode::data(int column) const
@@ -65,7 +69,7 @@ int MyListNode::totalRowCount() const
        if (m_totalRowCount != -1)
                return m_totalRowCount;
 
-       if (m_type == FileLocation)
+       if (m_type == FileLocationNode)
        {
                m_totalRowCount = 0;
                return m_totalRowCount;
@@ -87,49 +91,51 @@ int MyListNode::totalRowCount() const
 
 bool MyListNode::canFetchMore() const
 {
-       if (m_type != FileLocation && childCount() < totalRowCount())
+       if (!isWorking() && m_type != FileLocationNode && childCount() < totalRowCount())
                return true;
        return false;
 }
 
-int MyListNode::fetchMore()
+void MyListNode::fetchMore()
 {
-       QSqlQuery &q = LocalMyList::instance()->database()->prepare(
-                       "SELECT aid, title_romaji AS title, "
+       query->prepare(QString(
+                       "SELECT "
                        "               (SELECT COUNT(e.eid) "
                        "                       FROM episode e "
                        "                       WHERE e.aid = a.aid), "
-                       "               rating, my_vote, "
                        "               (SELECT COUNT(DISTINCT f.eid) "
                        "                       FROM episode e "
                        "                       JOIN file f ON (f.eid = e.eid) "
                        "                       WHERE e.aid = a.aid "
-                       "                               AND f.my_watched IS NOT NULL) "
+                       "                               AND f.my_watched IS NOT NULL), "
+                       "               %1 "
                        "       FROM anime a "
-                       "       ORDER BY title ASC "
+                       "       ORDER BY title_romaji ASC "
                        "LIMIT :limit "
-                       "OFFSET :offset ");
-       q.bindValue(":limit", LIMIT);
-       q.bindValue(":offset", childCount());
+                       "OFFSET :offset ").arg(Database::animeFields()));
+       query->bindValue(":limit", LIMIT);
+       query->bindValue(":offset", childCount());
 
-       if (!LocalMyList::instance()->database()->exec(q))
-               return 0;
+       query->exec();
+}
 
-       while (q.next())
+void MyListNode::fetchComplete()
+{
+       while (query->next())
        {
-               int id = q.value(0).toInt();
-               QVariantList data;
-
-               data << q.value(1) << q.value(2)
-                        << (q.value(3).toDouble() < 1 ? "n/a" : QString::number(q.value(3).toDouble(), 'f', 2))
-                        << (q.value(4).toDouble() < 1 ? "n/a" : QString::number(q.value(4).toDouble(), 'f', 2))
-                        << QObject::tr("%1 of %2").arg(q.value(5).toInt()).arg(q.value(2).toInt());
-               newItems << new MyListAnimeNode(id, data, this);
+               int epsInMyList = query->value(0).toInt();
+               int watchedEps = query->value(1).toInt();
+               Anime a;
+               Database::readAnimeData(*query, a, 2);
+
+               AnimeData ad(a, epsInMyList, watchedEps);
+               auto node = new MyListAnimeNode(model, ad, this);
+               newItems << node;
        }
 
-       q.finish();
+       query->finish();
 
-       return newItems.count();
+       model->fetchFinished(this, newItems.count());
 }
 
 void MyListNode::addFetched()
@@ -140,12 +146,22 @@ void MyListNode::addFetched()
 
 bool MyListNode::hasChildren() const
 {
-       if (m_type == FileLocation)
+       if (m_type == FileLocationNode)
                return false;
 
        return true;
 }
 
+bool MyListNode::isWorking() const
+{
+       return m_working;
+}
+
+void MyListNode::setWorking(bool working)
+{
+       m_working = working;
+}
+
 QString MyListNode::totalRowCountSql() const
 {
        return "SELECT COUNT(aid) FROM anime";
@@ -158,26 +174,63 @@ MyListNode::NodeType MyListNode::type() const
 
 int MyListNode::id() const
 {
-       return m_id;
+       return 0;
 }
 
 // ------
 
-MyListAnimeNode::MyListAnimeNode(int id, const QList<QVariant> &data, MyListNode *parent) :
-       MyListNode(Anime, id, data, parent)
+MyListAnimeNode::MyListAnimeNode(MyListModel *model, const AnimeData &data, MyListNode *parent) :
+       MyListNode(model, AnimeNode, parent), animeData(data)
+{
+       animeData.node = this;
+       model->animeSet.insert(animeData);
+}
+
+MyListAnimeNode::~MyListAnimeNode()
+{
+       model->animeSet.erase(model->animeSet.s_iterator_to(animeData));
+}
+
+QVariant MyListAnimeNode::data(int column) const
 {
+       switch (column)
+       {
+               case 0:
+                       return animeData.data.titleRomaji;
+               case 1:
+                       return watchedEpisodes();
+               case 2:
+                       if (animeData.data.rating < 1)
+                               return "n/a";
+                       return QString::number(animeData.data.rating, 'f', 2);
+               case 3:
+                       if (animeData.data.myVote < 1)
+                               return "n/a";
+                       return QString::number(animeData.data.myVote, 'f', 2);
+               case 4:
+                       return QString("%1 of %2").arg(animeData.watchedEpisodes)
+                                       .arg(watchedEpisodes());
+       }
+       return QVariant();
 }
 
 QString MyListAnimeNode::totalRowCountSql() const
 {
-       return "SELECT COUNT(eid) FROM episode WHERE aid = " + QString::number(m_id);
+       return "SELECT COUNT(eid) FROM episode WHERE aid = " + QString::number(id());
+}
+
+int MyListAnimeNode::watchedEpisodes() const
+{
+       return qMax(animeData.data.totalEpisodeCount,
+                               qMax(animeData.data.highestEpno,
+                                        animeData.episodesInMyList));
 }
 
-int MyListAnimeNode::fetchMore()
+void MyListAnimeNode::fetchMore()
 {
-       qDebug() << "fetching some more for aid" << m_id;
-       QSqlQuery &q = LocalMyList::instance()->database()->prepare(
-       "SELECT e.eid, e.title_english, et.type, e.epno, e.rating, e.my_vote, "
+       qDebug() << "fetching some more for aid" << id();
+       query->prepare(QString(
+       "SELECT "
        "               (SELECT MIN(my_watched) "
        "                       FROM "
        "                               (SELECT my_watched "
@@ -189,41 +242,80 @@ int MyListAnimeNode::fetchMore()
        "                                       FROM file f "
        "                                       JOIN file_episode_rel fer ON (fer.fid = f.fid) "
        "                                       WHERE fer.eid = e.eid "
-       "                                               AND my_watched IS NOT NULL) AS sq) AS my_watched "
+       "                                               AND my_watched IS NOT NULL) AS sq) AS my_watched, "
+       "               %1 "
        "       FROM episode e "
        "       JOIN episode_type et ON (et.type = e.type)"
        "       WHERE e.aid = :aid "
        "       ORDER BY et.ordering ASC, e.epno ASC "
        "       LIMIT :limit "
-       "       OFFSET :offset ");
-       q.bindValue(":aid", m_id);
-       q.bindValue(":limit", LIMIT);
-       q.bindValue(":offset", childCount());
+       "       OFFSET :offset ").arg(Database::episodeFields()));
+       query->bindValue(":aid", id());
+       query->bindValue(":limit", LIMIT);
+       query->bindValue(":offset", childCount());
 
-       if (!LocalMyList::instance()->database()->exec(q))
-               return 0;
+       query->exec();
+}
 
-       while (q.next())
+void MyListAnimeNode::fetchComplete()
+{
+       while (query->next())
        {
-               int id = q.value(0).toInt();
-               QVariantList data;
-               data << q.value(1) << (q.value(2).toString() + q.value(3).toString())
-                        << (q.value(4).toDouble() < 1 ? "n/a" : QString::number(q.value(4).toDouble(), 'f', 2))
-                        << (q.value(5).toDouble() < 1 ? "n/a" : QString::number(q.value(5).toDouble(), 'f', 2))
-                        << (q.value(6).toDateTime().isValid() ? QObject::tr("Yes, on %1").arg(q.value(6).toDateTime().toString()) : QObject::tr("No"));
-               newItems << new MyListEpisodeNode(id, data, this);
+               QDateTime watchedDate = query->value(0).toDateTime();
+               Episode e;
+               Database::readEpisodeData(*query, e, 1);
+
+               EpisodeData ed(e, watchedDate);
+               auto node = new MyListEpisodeNode(model, ed, this);
+               newItems << node;
        }
 
-       q.finish();
+       query->finish();
 
-       return newItems.count();
+       model->fetchFinished(this, newItems.count());
+}
+
+int MyListAnimeNode::id() const
+{
+       return animeData.data.aid;
 }
 
 // ----
 
-MyListEpisodeNode::MyListEpisodeNode(int id, const QList<QVariant> &data, MyListNode *parent) :
-       MyListNode(Episode, id, data, parent)
+MyListEpisodeNode::MyListEpisodeNode(MyListModel *model, const EpisodeData &data, MyListNode *parent) :
+       MyListNode(model, EpisodeNode, parent), episodeData(data)
+{
+       episodeData.node = this;
+       model->episodeSet.insert(episodeData);
+}
+
+MyListEpisodeNode::~MyListEpisodeNode()
+{
+       model->episodeSet.erase(model->episodeSet.s_iterator_to(episodeData));
+}
+
+QVariant MyListEpisodeNode::data(int column) const
 {
+       switch (column)
+       {
+               case 0:
+                       return episodeData.data.titleEnglish;
+               case 1:
+                       return episodeData.data.type + QString::number(episodeData.data.epno);
+               case 2:
+                       if (episodeData.data.rating < 1)
+                               return "n/a";
+                       return QString::number(episodeData.data.rating, 'f', 2);
+               case 3:
+                       if (episodeData.data.myVote < 1)
+                               return "n/a";
+                       return QString::number(episodeData.data.myVote, 'f', 2);
+               case 4:
+                       if (!episodeData.watchedDate.isValid())
+                               return QObject::tr("No");
+                       return QObject::tr("Yes, on %1").arg(episodeData.watchedDate.toString());
+       }
+       return QVariant();
 }
 
 QString MyListEpisodeNode::totalRowCountSql() const
@@ -238,23 +330,26 @@ QString MyListEpisodeNode::totalRowCountSql() const
                                "               SELECT f.fid FROM file f "
                                "                       JOIN file_episode_rel fer ON (fer.fid = f.fid) "
                                "                       WHERE fer.eid = %1 "
-                               "               ) AS sq ").arg(m_id);
+                               "               ) AS sq ").arg(id());
 }
 
-int MyListEpisodeNode::fetchMore()
+void MyListEpisodeNode::fetchMore()
 {
-       qDebug() << "fetching some more for eid" << m_id;
-       QSqlQuery &q = LocalMyList::instance()->database()->prepare(
-       "SELECT fid, group_name, version, quality, my_watched "
-       "       FROM file "
-       "       WHERE eid = :eida "
+       qDebug() << "fetching some more for eid" << id();
+       query->prepare(QString(
+       "SELECT %1 "
+       "       FROM file "
+       "       WHERE f.eid = :eida "
        "UNION "
-       "SELECT f.fid, f.group_name, f.version, f.quality, f.my_watched FROM file f "
+       "SELECT %1 FROM file f "
        "       JOIN file_episode_rel fer ON (fer.fid = f.fid) "
-       "       WHERE fer.eid = :eidb ");
-       q.bindValue(":eida", m_id);
-       q.bindValue(":eidb", m_id);
+       "       WHERE fer.eid = :eidb ")
+       .arg(Database::fileFields()));
+       query->bindValue(":eida", id());
+       query->bindValue(":eidb", id());
 
+       query->exec();
+/*
        if (!LocalMyList::instance()->database()->exec(q))
                return 0;
 
@@ -269,28 +364,70 @@ int MyListEpisodeNode::fetchMore()
 
        q.finish();
 
-       return newItems.count();
+*/
+}
+
+void MyListEpisodeNode::fetchComplete()
+{
+       while (query->next())
+       {
+               File f;
+               Database::readFileData(*query, f);
+
+               FileData fd(f);
+               auto node = new MyListFileNode(model, fd, this);
+               newItems << node;
+       }
+
+       query->finish();
+
+       model->fetchFinished(this, newItems.count());
+}
+
+int MyListEpisodeNode::id() const
+{
+       return episodeData.data.eid;
 }
 
 // ---------------
 
-MyListFileNode::MyListFileNode(int id, const QList<QVariant> &data, MyListNode *parent) :
-       MyListNode(File, id, data, parent)
+MyListFileNode::MyListFileNode(MyListModel *model, const FileData &data, MyListNode *parent) :
+       MyListNode(model, FileNode, parent), fileData(data)
 {
 }
 
-int MyListFileNode::fetchMore()
+QVariant MyListFileNode::data(int column) const
 {
-       qDebug() << "fetching some more for fid" << m_id;
-       QSqlQuery &q = LocalMyList::instance()->database()->prepare(
-       "SELECT fl.fid, fl.host_id, h.name, fl.path, fl.renamed, fl.failed_rename FROM file_location fl "
-       "       JOIN host h ON (fl.host_id = h.host_id) "
-       "WHERE fl.fid = :fid");
-       q.bindValue(":fid", m_id);
+       switch (column)
+       {
+               case 0:
+                       return fileData.data.groupName;
+               case 1:
+                       return "v" + QString::number(fileData.data.version);
+               case 2:
+                       return fileData.data.quality;
+               case 3:
+                       return "";
+               case 4:
+                       if (!fileData.data.myWatched.isValid())
+                               return QObject::tr("No");
+                       return QObject::tr("Yes, on %1").arg(fileData.data.myWatched.toString());
+       }
+       return QVariant();
+}
 
-       if (!LocalMyList::instance()->database()->exec(q))
-               return 0;
+void MyListFileNode::fetchMore()
+{
+       qDebug() << "fetching some more for fid" << id();
+       query->prepare(QString(
+       "SELECT h.name, %1 FROM file_location fl "
+       "       JOIN host h ON (fl.host_id = h.host_id) "
+       "WHERE fl.fid = :fid")
+       .arg(Database::fileLocationFields()));
+       query->bindValue(":fid", id());
 
+       query->exec();
+/*
        while (q.next())
        {
                int id = q.value(0).toInt();
@@ -306,23 +443,80 @@ int MyListFileNode::fetchMore()
        q.finish();
 
        return newItems.count();
+*/
+}
+
+void MyListFileNode::fetchComplete()
+{
+       while (query->next())
+       {
+               QString hostName = query->value(0).toString();
+
+               FileLocation fl;
+               Database::readFileLocationData(*query, fl, 1);
+
+               FileLocationData fld(fl, hostName);
+
+               auto node = new MyListFileLocationNode(model, fld, this);
+               newItems << node;
+       }
+
+       query->finish();
+
+       model->fetchFinished(this, newItems.count());
+}
+
+int MyListFileNode::id() const
+{
+       return fileData.data.fid;
 }
 
 QString MyListFileNode::totalRowCountSql() const
 {
-       return "SELECT COUNT(fid) FROM file_location WHERE fid = " + QString::number(m_id);
+       return "SELECT COUNT(fid) FROM file_location WHERE fid = " + QString::number(id());
 }
 
 // ---------------
 
-MyListFileLocationNode::MyListFileLocationNode(int id, const QList<QVariant> &data, MyListNode *parent) :
-       MyListNode(FileLocation, id, data, parent)
+MyListFileLocationNode::MyListFileLocationNode(MyListModel *model, const FileLocationData &data, MyListNode *parent) :
+       MyListNode(model, FileLocationNode, parent), fileLocationData(data)
 {
 }
 
-int MyListFileLocationNode::fetchMore()
+QVariant MyListFileLocationNode::data(int column) const
 {
-       return 0;
+       switch (column)
+       {
+               case 0:
+                       return fileLocationData.data.path;
+               case 1:
+               return QString("%1 (%2)").arg(fileLocationData.hostName)
+                               .arg(fileLocationData.data.hostId);
+               case 2:
+                       return "";
+               case 3:
+                       return "";
+               case 4:
+                       if (!fileLocationData.data.renamed.isValid())
+                               return QObject::tr("No");
+                       if (fileLocationData.data.failedRename)
+                               return QObject::tr("Rename failed");
+                       return QObject::tr("Yes, on %1").arg(fileLocationData.data.renamed.toString());
+       }
+       return QVariant();
+}
+
+void MyListFileLocationNode::fetchMore()
+{
+}
+
+void MyListFileLocationNode::fetchComplete()
+{
+}
+
+int MyListFileLocationNode::id() const
+{
+       return fileLocationData.data.locationId;
 }
 
 QString MyListFileLocationNode::totalRowCountSql() const
index f7442cadec13c6791c5759c39b7b9975ae94d179..d4e01ccbc6b7857cb7693992e4c72137cd60afd2 100644 (file)
@@ -2,46 +2,54 @@
 #define MYLISTNODE_H
 
 #include "localmylist_global.h"
+#include "mylistmodel.h"
+#include "databaseclasses.h"
 #include <QVariant>
 
 namespace LocalMyList {
 
+class MyListModel;
+class SqlAsyncQuery;
+
 class LOCALMYLISTSHARED_EXPORT MyListNode
 {
 public:
        enum NodeType {
-               Root,
-               Anime,
-               Episode,
-               File,
-               FileLocation
+               RootNode,
+               AnimeNode,
+               EpisodeNode,
+               FileNode,
+               FileLocationNode
        };
 
-       MyListNode(NodeType type = Root, int m_id = 0, const QList<QVariant> &data = QList<QVariant>(), MyListNode *parent = 0);
+       MyListNode(MyListModel *model, NodeType type = RootNode, MyListNode *parent = 0);
        virtual ~MyListNode();
 
        MyListNode *child(int row);
        int childCount() const;
        int columnCount() const;
-       QVariant data(int column) const;
+       virtual QVariant data(int column) const;
        int row() const;
        MyListNode *parent();
 
        int totalRowCount() const;
 
        bool canFetchMore() const;
-       virtual int fetchMore();
+       virtual void fetchMore();
+       virtual void fetchComplete();
        void addFetched();
        bool hasChildren() const;
 
+       bool isWorking() const;
+       void setWorking(bool working);
+
        NodeType type() const;
-       int id() const;
+       virtual int id() const;
 
 protected:
        virtual QString totalRowCountSql() const;
 
        NodeType m_type;
-       int m_id;
 
        mutable int m_totalRowCount;
        int fetchedRowCount;
@@ -50,6 +58,10 @@ protected:
        QList<MyListNode *> newItems;
        QList<QVariant> itemData;
        MyListNode *parentItem;
+       MyListModel *model;
+       SqlAsyncQuery *query;
+
+       bool m_working;
 
        static const int LIMIT = 200;
 };
@@ -57,45 +69,72 @@ protected:
 class LOCALMYLISTSHARED_EXPORT MyListAnimeNode : public MyListNode
 {
 public:
-       MyListAnimeNode(int id = 0, const QList<QVariant> &data = QList<QVariant>(), MyListNode *parent = 0);
+       MyListAnimeNode(MyListModel *model, const AnimeData &data, MyListNode *parent);
+       ~MyListAnimeNode();
 
-       int fetchMore();
+       QVariant data(int column) const;
+       void fetchMore();
+       void fetchComplete();
+       int id() const;
 
 protected:
        QString totalRowCountSql() const;
+
+private:
+       int watchedEpisodes() const;
+       AnimeData animeData;
 };
 
 class LOCALMYLISTSHARED_EXPORT MyListEpisodeNode : public MyListNode
 {
 public:
-       MyListEpisodeNode(int id = 0, const QList<QVariant> &data = QList<QVariant>(), MyListNode *parent = 0);
+       MyListEpisodeNode(MyListModel *model, const EpisodeData &data, MyListNode *parent);
+       ~MyListEpisodeNode();
 
-       int fetchMore();
+       QVariant data(int column) const;
+       void fetchMore();
+       void fetchComplete();
+       int id() const;
 
 protected:
        QString totalRowCountSql() const;
+
+private:
+       EpisodeData episodeData;
 };
 
 class LOCALMYLISTSHARED_EXPORT MyListFileNode : public MyListNode
 {
 public:
-       MyListFileNode(int id = 0, const QList<QVariant> &data = QList<QVariant>(), MyListNode *parent = 0);
+       MyListFileNode(MyListModel *model, const FileData &data, MyListNode *parent);
 
-       int fetchMore();
+       QVariant data(int column) const;
+       void fetchMore();
+       void fetchComplete();
+       int id() const;
 
 protected:
        QString totalRowCountSql() const;
+
+private:
+       FileData fileData;
 };
 
 class LOCALMYLISTSHARED_EXPORT MyListFileLocationNode : public MyListNode
 {
 public:
-       MyListFileLocationNode(int id = 0, const QList<QVariant> &data = QList<QVariant>(), MyListNode *parent = 0);
+       MyListFileLocationNode(MyListModel *model, const FileLocationData &data, MyListNode *parent);
 
-       int fetchMore();
+       QVariant data(int column) const;
+       void fetchMore();
+       void fetchComplete();
+       int id() const;
 
 protected:
        QString totalRowCountSql() const;
+
+private:
+       FileLocationData fileLocationData;
 };
 
 } // namespace LocalMyList
diff --git a/localmylist/mylistnodedata.cpp b/localmylist/mylistnodedata.cpp
new file mode 100644 (file)
index 0000000..5e859d8
--- /dev/null
@@ -0,0 +1,3 @@
+#include "mylistnodedata.h"
+
+
diff --git a/localmylist/mylistnodedata.h b/localmylist/mylistnodedata.h
new file mode 100644 (file)
index 0000000..4ed0016
--- /dev/null
@@ -0,0 +1,75 @@
+#ifndef MYLISTNODEDATA_H
+#define MYLISTNODEDATA_H
+
+#include "databaseclasses.h"
+#include <boost/intrusive/set_hook.hpp>
+
+namespace LocalMyList {
+
+class MyListAnimeNode;
+class MyListEpisodeNode;
+class MyListFileNode;
+class MyListFileLocationNode;
+
+struct AnimeData : public boost::intrusive::set_base_hook< >
+{
+       Anime data;
+       MyListAnimeNode *node;
+
+       int episodesInMyList;
+       int watchedEpisodes;
+
+       friend bool operator<(const AnimeData &a, const AnimeData &b)
+       { return a.data.aid < b.data.aid; }
+
+       AnimeData(const Anime &animeData, int epsInML, int watchedEps) : data(animeData), node(0),
+               episodesInMyList(epsInML), watchedEpisodes(watchedEps)
+       {}
+};
+
+struct EpisodeData : public boost::intrusive::set_base_hook< >
+{
+       Episode data;
+       MyListEpisodeNode *node;
+
+       QDateTime watchedDate;
+
+       friend bool operator<(const EpisodeData &a, const EpisodeData &b)
+       { return a.data.eid < b.data.eid; }
+
+       EpisodeData(const Episode &episodeData, const QDateTime &watchedDate_)
+               : data(episodeData), node(0), watchedDate(watchedDate_)
+       {}
+};
+
+struct FileData : public boost::intrusive::set_base_hook< >
+{
+       File data;
+       MyListFileNode *node;
+
+       friend bool operator<(const FileData &a, const FileData &b)
+       { return a.data.fid < b.data.fid; }
+
+       FileData(const File &fileData)
+               : data(fileData), node(0)
+       {}
+};
+
+struct FileLocationData : public boost::intrusive::set_base_hook< >
+{
+       FileLocation data;
+       MyListFileLocationNode *node;
+
+       QString hostName;
+
+       friend bool operator<(const FileLocationData &a, const FileLocationData &b)
+       { return a.data.locationId < b.data.locationId; }
+
+       FileLocationData(const FileLocation &fileLocationData, const QString &hostName_)
+               : data(fileLocationData), node(0), hostName(hostName_)
+       {}
+};
+
+} // namespace LocalMyList
+
+#endif // MYLISTNODEDATA_H
index 4331dd0df3e7aa0c1463932344c92db02c8cb2cd..12e3d2478a5dd95d2ac64845acd21bfb86582f7b 100644 (file)
@@ -296,15 +296,15 @@ void MainWindow::on_myListView_openFileRequested(const QModelIndex &index)
 
        OpenFileData data;
 
-       if (node->type() == MyListNode::Anime)
+       if (node->type() == MyListNode::AnimeNode)
        {
                data = MyList::instance()->database()->firstUnwatchedByAid(node->id());
        }
-       else if (node->type() == MyListNode::Episode)
+       else if (node->type() == MyListNode::EpisodeNode)
        {
                data = MyList::instance()->database()->openFileByEid(node->id());
        }
-       else if (node->type() == MyListNode::File)
+       else if (node->type() == MyListNode::FileNode)
        {
                data = MyList::instance()->database()->openFile(node->id());
        }
@@ -335,7 +335,7 @@ void MainWindow::on_myListView_renameFilesRequested(const QModelIndex &index)
        QSqlQuery q(MyList::instance()->database()->connection());
 
        QChar typeLetter;
-       if (node->type() == MyListNode::Anime)
+       if (node->type() == MyListNode::AnimeNode)
        {
                q.prepare(
                        "UPDATE file_location fl SET renamed = NULL, failed_rename = false "
@@ -345,7 +345,7 @@ void MainWindow::on_myListView_renameFilesRequested(const QModelIndex &index)
 
                typeLetter = 'a';
        }
-       else if (node->type() == MyListNode::Episode)
+       else if (node->type() == MyListNode::EpisodeNode)
        {
                q.prepare(
                        "UPDATE file_location fl SET renamed = NULL, failed_rename = false "
@@ -355,7 +355,7 @@ void MainWindow::on_myListView_renameFilesRequested(const QModelIndex &index)
 
                typeLetter = 'e';
        }
-       else if (node->type() == MyListNode::File)
+       else if (node->type() == MyListNode::FileNode)
        {
                q.prepare(
                        "UPDATE file_location fl SET renamed = NULL, failed_rename = false "
@@ -389,13 +389,13 @@ void MainWindow::on_myListView_dataRequested(const QModelIndex &index)
 
        switch (node->type())
        {
-               case MyListNode::Anime:
+               case MyListNode::AnimeNode:
                        r.aid = node->id();
                break;
-               case MyListNode::Episode:
+               case MyListNode::EpisodeNode:
                        r.eid = node->id();
                break;
-               case MyListNode::File:
+               case MyListNode::FileNode:
                        r.fid = node->id();
                break;
                default:
index 8baf0a91b650aae6abd04ed10d13f59bd493e88b..bebe6ec18a3cb6b1089fc70c88cb5510836289f4 100644 (file)
@@ -60,21 +60,21 @@ void MyListView::showCustomContextMenu(const QPoint &pos)
 
        switch (node->type())
        {
-               case MyListNode::Anime:
+               case MyListNode::AnimeNode:
                        aniDBLinkAction->setText(tr("Open AniDB Page (%1%2)").arg('a').arg(node->id()));
                        actions << aniDBLinkAction
                                        << openNextAction
                                        << renameFilesAction
                                        << requestDataAction;
                break;
-               case MyListNode::Episode:
+               case MyListNode::EpisodeNode:
                        aniDBLinkAction->setText(tr("Open AniDB Page (%1%2)").arg('e').arg(node->id()));
                        actions << aniDBLinkAction
                                        << openAction
                                        << renameFilesAction
                                        << requestDataAction;
                break;
-               case MyListNode::File:
+               case MyListNode::FileNode:
                        aniDBLinkAction->setText(tr("Open AniDB Page (%1%2)").arg('f').arg(node->id()));
                        actions << aniDBLinkAction
                                        << openAction
@@ -83,7 +83,7 @@ void MyListView::showCustomContextMenu(const QPoint &pos)
                                        << renameFilesAction
                                        << requestDataAction;
                break;
-               case MyListNode::FileLocation:
+               case MyListNode::FileLocationNode:
                        aniDBLinkAction->setText(tr("Open AniDB Page (%1%2)").arg('f').arg(node->id()));
                        actions << aniDBLinkAction
                                        << markWatchedAction
@@ -125,14 +125,14 @@ void MyListView::openAnidbPage()
 
        switch (node->type())
        {
-               case MyListNode::Anime:
+               case MyListNode::AnimeNode:
                        QDesktopServices::openUrl(QUrl(aniDBUrlBase.arg('a').arg(node->id())));
                break;
-               case MyListNode::Episode:
+               case MyListNode::EpisodeNode:
                        QDesktopServices::openUrl(QUrl(aniDBUrlBase.arg('e').arg(node->id())));
                break;
-               case MyListNode::File:
-               case MyListNode::FileLocation:
+               case MyListNode::FileNode:
+               case MyListNode::FileLocationNode:
                        QDesktopServices::openUrl(QUrl(aniDBUrlBase.arg('f').arg(node->id())));
                break;
                default: