fonts.cpp \
aniaddsyntaxhighlighter.cpp \
settingsdialog.cpp \
- codeeditor.cpp
+ codeeditor.cpp \
+ tabs/dynamicmodeltab.cpp
HEADERS += mainwindow.h \
databaseconnectiondialog.h \
fonts.h \
aniaddsyntaxhighlighter.h \
settingsdialog.h \
- codeeditor.h
+ codeeditor.h \
+ tabs/dynamicmodeltab.h
FORMS += mainwindow.ui \
databaseconnectiondialog.ui \
tabs/unknownfilestab.ui \
tabs/pendingrequesttab.ui \
tabs/databaselogtab.ui \
- tabs/clientlogtab.ui
+ tabs/clientlogtab.ui \
+ tabs/dynamicmodeltab.ui
include(../localmylist.pri)
include(qtsingleapplication/qtsingleapplication.pri)
#include "tabs/pendingrequesttab.h"
#include "tabs/databaselogtab.h"
#include "tabs/clientlogtab.h"
+#include "tabs/dynamicmodeltab.h"
void registerTabs()
{
TabWidget::registerTab<PendingRequestTab>();
TabWidget::registerTab<DatabaseLogTab>();
TabWidget::registerTab<ClientLogTab>();
+ TabWidget::registerTab<DynamicModelTab>();
}
--- /dev/null
+#include "dynamicmodeltab.h"
+#include "ui_dynamicmodeltab.h"
+
+#include "mainwindow.h"
+#include "database.h"
+#include "mylist.h"
+#include "mylistfiltermodel.h"
+#include "mylistitemdelegate.h"
+
+#include "dynamicmodel/model.h"
+#include "dynamicmodel/datamodel.h"
+#include "dynamicmodel/types.h"
+
+using namespace LocalMyList::DynamicModel;
+
+DynamicModelTab::DynamicModelTab(QWidget *parent) :
+ AbstractTabBase(parent),
+ ui(new Ui::DynamicModelTab)
+{
+ ui->setupUi(this);
+ setLabel(name());
+}
+
+DynamicModelTab::~DynamicModelTab()
+{
+ delete ui;
+}
+
+QString DynamicModelTab::staticId()
+{
+ return "dynamic_model";
+}
+
+QString DynamicModelTab::name()
+{
+ return tr("Dynamic Model");
+}
+
+void DynamicModelTab::init()
+{
+ dataModel = new DataModel(this);
+ dataModel->registerDataType(new AnimeType);
+
+ model = new Model(this);
+ model->setDataModel(dataModel);
+
+// myListFilterModel = new MyListFilterModel(this);
+// myListFilterModel->setSourceModel(model);
+ ui->myListView->setModel(model /*myListFilterModel*/);
+ ui->myListView->setItemDelegate(new MyListItemDelegate(ui->myListView));
+/*
+#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
+ ui->myListView->header()->setSectionResizeMode(0, QHeaderView::Stretch);
+#else
+ ui->myListView->header()->setResizeMode(0, QHeaderView::Stretch);
+#endif
+ ui->myListView->header()->setStretchLastSection(false);
+ ui->myListView->header()->resizeSection(4, 200);
+*/
+ ui->filterType->addItems(QStringList()
+ << tr("Fixed String")
+ << tr("Wildcard")
+ << tr("Regexp"));
+
+ connect(ui->myListView, SIGNAL(renameTest(int)), mainWindow(), SLOT(openRenameScriptEditor(int)));
+ connect(ui->myListView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(currentSelectionChanged(QModelIndex,QModelIndex)));
+ connect(ui->filterInput, SIGNAL(textChanged(QString)), this, SLOT(currentSelectionChanged()));
+
+}
+
+void DynamicModelTab::activate()
+{
+ ui->filterInput->setFocus();
+}
+
+void DynamicModelTab::reload()
+{
+ model->reload();
+}
+
+void DynamicModelTab::changeEvent(QEvent *e)
+{
+ QWidget::changeEvent(e);
+ switch (e->type()) {
+ case QEvent::LanguageChange:
+ ui->retranslateUi(this);
+ break;
+ default:
+ break;
+ }
+}
--- /dev/null
+#ifndef DYNAMICMODELTAB_H
+#define DYNAMICMODELTAB_H
+
+#include "abstracttab.h"
+
+class MyListFilterModel;
+
+namespace LocalMyList {
+namespace DynamicModel {
+class DataModel;
+class Model;
+}
+}
+
+namespace Ui {
+class DynamicModelTab;
+}
+
+class DynamicModelTab : public AbstractTabBase<DynamicModelTab>
+{
+ Q_OBJECT
+
+public:
+ explicit DynamicModelTab(QWidget *parent = 0);
+ ~DynamicModelTab();
+
+ static QString staticId();
+ static QString name();
+
+ void init();
+ void activate();
+
+ void reload();
+
+protected:
+ void changeEvent(QEvent *e);
+
+private:
+ Ui::DynamicModelTab *ui;
+
+ MyListFilterModel *myListFilterModel;
+ LocalMyList::DynamicModel::DataModel *dataModel;
+ LocalMyList::DynamicModel::Model *model;
+};
+
+#endif // DYNAMICMODELTAB_H
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>DynamicModelTab</class>
+ <widget class="QWidget" name="DynamicModelTab">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="filterLayout">
+ <item>
+ <widget class="FilterLineEdit" name="filterInput"/>
+ </item>
+ <item>
+ <widget class="QComboBox" name="filterType"/>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="MyListView" name="myListView"/>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>FilterLineEdit</class>
+ <extends>QLineEdit</extends>
+ <header>filterlineedit.h</header>
+ </customwidget>
+ <customwidget>
+ <class>MyListView</class>
+ <extends>QTreeView</extends>
+ <header>mylistview.h</header>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
#include "node.h"
#include "datatype.h"
+#include <QDebug>
+
namespace LocalMyList {
namespace DynamicModel {
+QString stateIdToState(int id)
+{
+ switch (id)
+ {
+ case -1:
+ return QObject::tr("Mixed");
+ case 0:
+ return QObject::tr("Unknown");
+ case 1:
+ return QObject::tr("On HDD");
+ case 2:
+ return QObject::tr("On Cd");
+ case 3:
+ return QObject::tr("Deleted");
+ }
+ return QString();
+}
+
Data::Data(DataType *dataType) : m_type(dataType)
{
}
}
}
+AnimeData::AnimeData(DataType *dataType) : Data(dataType)
+{
+}
+
+int AnimeData::id() const
+{
+ return animeData.aid;
+}
+
+QVariant AnimeData::data(int column, int role) const
+{
+ switch (role)
+ {
+ case Qt::DisplayRole:
+ switch (column)
+ {
+ case 0:
+ return animeData.titleRomaji;
+ case 1:
+ if (animeData.totalEpisodeCount)
+ return QString("%1 of %2")
+ .arg(episodesInMyList).arg(animeData.totalEpisodeCount);
+ return QString("%1 of (%2)")
+ .arg(episodesInMyList)
+ .arg(qMax(animeData.highestEpno,
+ episodesInMyList));
+ case 2:
+ if (animeData.rating < 1)
+ return "n/a";
+ return QString::number(animeData.rating, 'f', 2);
+ case 3:
+ if (animeData.myVote < 1)
+ return "n/a";
+ return QString::number(animeData.myVote, 'f', 2);
+ case 4:
+ return QString("%1 of %2").arg(watchedEpisodes)
+ .arg(episodesInMyList);
+ case 5:
+ return stateIdToState(myState);
+ }
+ break;
+ case Qt::ToolTipRole:
+ switch (column)
+ {
+ case 0:
+ if (!animeData.titleEnglish.isEmpty() && !animeData.titleKanji.isEmpty())
+ return QString("%1 -- %2").arg(animeData.titleEnglish)
+ .arg(animeData.titleKanji);
+ if (!animeData.titleEnglish.isEmpty())
+ return animeData.titleEnglish;
+ if (!animeData.titleKanji.isEmpty())
+ return animeData.titleKanji;
+ }
+ break;
+ case Qt::EditRole:
+ switch (column)
+ {
+ case 3:
+ return animeData.myVote;
+ }
+ break;
+ }
+
+ return QVariant();
+}
+
} // namespace DynamicModel
} // namespace LocalMyList
#ifndef DATA_H
#define DATA_H
+#include "../localmylist_global.h"
+
#include <QList>
#include <QVariant>
class DataType;
class Node;
+typedef QList<Node *> NodeList;
-class Data
+class LOCALMYLISTSHARED_EXPORT Data
{
public:
Data(DataType *dataType);
void updated();
private:
- QList<Node *> references;
+ NodeList references;
DataType * const m_type;
};
-class AnimeData : public Data
+class LOCALMYLISTSHARED_EXPORT AnimeData : public Data
{
public:
AnimeData(DataType *dataType);
int id() const;
- QVariant data(int row, int role) const;
+ QVariant data(int column, int role) const;
Anime animeData;
int episodesInMyList;
int watchedEpisodes;
+ int myState;
};
} // namespace DynamicModel
+++ /dev/null
-#include "datamanager.h"
-
-#include "datatype.h"
-
-namespace LocalMyList {
-namespace DynamicModel {
-
-DataManager::DataManager(QObject *parent) : QObject(parent)
-{
-}
-
-DataManager::~DataManager()
-{
- qDeleteAll(dataTypes);
-}
-
-bool DataManager::registerDataType(DataType *dataType)
-{
- if (dataTypes.contains(dataType))
- return true;
-
- if (dataTypeNames.contains(dataType->name()))
- return false;
-
- dataTypes.insert(dataType);
- dataTypeNames.insert(dataType->name(), dataType);
- return true;
-}
-
-DataType *DataManager::dataType(const QString &name) const
-{
- return dataTypeNames.value(name, 0);
-}
-
-} // namespace DynamicModel
-} // namespace LocalMyList
--- /dev/null
+#include "datamodel.h"
+
+#include "datatype.h"
+#include "typerelation.h"
+
+namespace LocalMyList {
+namespace DynamicModel {
+
+DataModel::DataModel(QObject *parent) : QObject(parent)
+{
+}
+
+DataModel::~DataModel()
+{
+ qDeleteAll(dataTypes);
+}
+
+bool DataModel::registerDataType(DataType *dataType)
+{
+ if (dataTypes.contains(dataType))
+ return true;
+
+ if (dataTypeNames.contains(dataType->name()))
+ return false;
+
+ dataTypes.insert(dataType);
+ dataTypeNames.insert(dataType->name(), dataType);
+ return true;
+}
+
+bool DataModel::registerTypeRelation(TypeRelation *typeRelation)
+{
+ Q_ASSERT(typeRelation);
+
+ if (!typeRelation->sourceType().isEmpty() && !dataTypeNames.contains(typeRelation->sourceType()))
+ return false;
+
+ if (!dataTypeNames.contains(typeRelation->destinationType()))
+ return false;
+
+ auto it = typeRelations.find(typeRelation->sourceType());
+
+ if (it == typeRelations.end())
+ it = typeRelations.insert(typeRelation->sourceType(), QHash<QString, TypeRelation *>());
+
+ auto inner = it.value().find(typeRelation->destinationType());
+ if (inner != it.value().end())
+ return false;
+
+ it.value().insert(typeRelation->destinationType(), typeRelation);
+ return true;
+}
+
+DataType *DataModel::dataType(const QString &name) const
+{
+ DataType *t = dataTypeNames.value(name, 0);
+ Q_ASSERT(t);
+ return t;
+}
+
+TypeRelation *DataModel::typeRelation(const QString &source, const QString &destiantion)
+{
+ const auto it = typeRelations.find(source);
+
+ if (it == typeRelations.constEnd())
+ return 0;
+
+ const auto inner = it.value().find(destiantion);
+
+ if (inner == it.value().constEnd())
+ return 0;
+
+ return inner.value();
+}
+
+} // namespace DynamicModel
+} // namespace LocalMyList
-#ifndef DATAMANAGER_H
-#define DATAMANAGER_H
+#ifndef DATAMODEL_H
+#define DATAMODEL_H
+#include <functional>
+#include "../localmylist_global.h"
#include "dynamicmodel_global.h"
+#include "node.h"
#include <QObject>
#include <QSet>
namespace DynamicModel {
class DataType;
+class TypeRelation;
-class DataManager : public QObject
+class LOCALMYLISTSHARED_EXPORT DataModel : public QObject
{
Q_OBJECT
public:
- DataManager(QObject *parent = 0);
- ~DataManager();
+ DataModel(QObject *parent = 0);
+ ~DataModel();
bool registerDataType(DataType *dataType);
+ bool registerTypeRelation(TypeRelation *typeRelation);
DataType *dataType(const QString &name) const;
+ TypeRelation *typeRelation(const QString &source, const QString &destiantion);
+
+
private slots:
/* void animeUpdate(int aid);
private:
QHash<QString, DataType *> dataTypeNames;
QSet<DataType *> dataTypes;
+
+ QHash<QString, QHash<QString, TypeRelation *> > typeRelations;
};
} // namespace DynamicModel
} // namespace LocalMyList
-#endif // DATAMANAGER_H
+#endif // DATAMODEL_H
namespace LocalMyList {
namespace DynamicModel {
-DataType::DataType(QObject *parent) : QObject(parent)
+DataType::DataType(QObject *parent) : QObject(parent), m_size(0)
{
+ query = new SqlAsyncQuery(this);
+}
+
+DataType::~DataType()
+{
+
}
QStringList DataType::availableChildRelations() const
#ifndef ABSTRACTDATATYPE_H
#define ABSTRACTDATATYPE_H
-#include "datamanager.h"
+#include "../localmylist_global.h"
+#include "datamodel.h"
+#include "../sqlasyncquery.h"
#include <QObject>
#include <QString>
typedef bool (*NodeCompare)(Node *a, Node *b);
-class DataType : public QObject
+class LOCALMYLISTSHARED_EXPORT DataType : public QObject
{
Q_OBJECT
public:
DataType(QObject *parent = 0);
+ virtual ~DataType();
virtual QString name() const = 0;
virtual QStringList availableChildRelations() const;
// Acquire
virtual bool canGetChildren(const QString &childTypeName) const;
- virtual void getChildren(const Data *parent, const QString &childTypeName, int offset) = 0;
+ virtual NodeList getChildren(Model *model, Node *parent, const QString &childTypeName, int offset) = 0;
// Update
virtual void update(Data *data);
virtual NodeCompare nodeCompareFunction() const = 0;
+protected:
+ SqlAsyncQuery *query;
+ mutable int m_size;
+
+ static const int LIMIT = 400;
private:
QHash<int, Data *> m_dataStore;
};
#include "model.h"
#include "node.h"
+#include "datamodel.h"
namespace LocalMyList {
namespace DynamicModel {
Model::Model(QObject *parent) :
- QAbstractItemModel(parent)
+ QAbstractItemModel(parent), m_dataModel(0)
{
+ rootItem = createRootNode();
}
Model::~Model()
return createIndex(node->row(), 0, node);
}
+DataModel *Model::dataModel() const
+{
+ return m_dataModel;
+}
+
+DataType *Model::rootDataType() const
+{
+ return m_dataModel->dataType("anime");
+}
+
void Model::reload()
{
beginResetModel();
delete rootItem;
- rootItem = new Node(this, 0, 0, 0);
+ rootItem = createRootNode();
endResetModel();
}
+void Model::setDataModel(DataModel *dataModel)
+{
+ if (m_dataModel == dataModel)
+ return;
+
+ m_dataModel = dataModel;
+ emit dataModelChanged(dataModel);
+
+ reload();
+}
+
+Node *Model::createRootNode()
+{
+ Node *n = new Node(this, 0, 0, 0);
+ if (m_dataModel)
+ n->setChildDataType(m_dataModel->dataType("anime"));
+ return n;
+}
+
} // namespace DynamicModel
} // namespace Local
#ifndef MODEL_H
#define MODEL_H
+#include "../localmylist_global.h"
#include <QAbstractItemModel>
namespace LocalMyList {
namespace DynamicModel {
class Node;
+class DataModel;
+class DataType;
-class Model : public QAbstractItemModel
+class LOCALMYLISTSHARED_EXPORT Model : public QAbstractItemModel
{
Q_OBJECT
+ Q_PROPERTY(DataModel* dataModel READ dataModel WRITE setDataModel NOTIFY dataModelChanged)
+
friend class Node;
public:
Node *node(const QModelIndex &idx) const;
QModelIndex index(Node *node) const;
+ DataModel *dataModel() const;
+
+ DataType *rootDataType() const;
+
public slots:
void reload();
+ void setDataModel(DataModel *dataModel);
+
+signals:
+ void dataModelChanged(DataModel *dataModel);
+
private:
+ Node *createRootNode();
+
Node *rootItem;
+ DataModel* m_dataModel;
};
} // namespace DynamicModel
Node::Node(Model *model, Node *parent, int totalRowCount, Data *data)
: m_model(model), m_parent(parent), m_totalRowCount(totalRowCount),
- m_data(data)
+ m_data(data), m_childType(0)
{
+ Q_ASSERT_X((parent && data) || (!parent && !data), "dynamic model", "Root node has no data and no parent. Other nodes must have both");
+
+ if (!data)
+ return;
m_data->ref(this);
}
Node::~Node()
{
+ if (!m_data)
+ return;
+
m_data->deref(this);
qDeleteAll(m_children);
}
+DataType *Node::childDataType() const
+{
+ return m_childType;
+}
+
+void Node::setChildDataType(DataType *dataType)
+{
+ Q_ASSERT_X(dataType, "dynamicmodel", "NULL data type");
+ m_childType = dataType;
+}
+
Node *Node::parent() const
{
return m_parent;
bool Node::hasChildren() const
{
+ if (this == m_model->rootItem)
+ return true;
return totalRowCount() > 0;
}
QVariant Node::data(int column, int role) const
{
+ qDebug() << parent() << column;
if (parent())
return m_data->data(column, role);
int Node::totalRowCount() const
{
- return m_totalRowCount;
+ return m_totalRowCount ? m_totalRowCount : childDataType() ? childDataType()->size() : 0;
}
bool Node::canFetchMore() const
{
+ if (!m_parent && !totalRowCount())
+ return true;
+
if (childCount() < totalRowCount())
return true;
return false;
void Node::fetchMore()
{
- m_data->type()->getChildren(m_data, m_childType->name(), childCount());
+ if (!m_childType)
+ return;
+ qDebug() << "fetchMote" << this;
+ NodeList newItems;
+ if (m_data)
+ newItems = data()->type()->getChildren(m_model, this, m_childType->name(), childCount());
+ else
+ newItems = m_model->rootDataType()->getChildren(m_model, this, m_childType->name(), childCount());
+
+ const QModelIndex parent = m_model->index(this);
+ const int newrows = newItems.count();
+
+ if (!newrows)
+ return;
+
+ qDebug() << parent << m_model->rowCount(parent) << m_model->rowCount(parent) + newrows - 1;
+ m_model->beginInsertRows(parent, m_model->rowCount(parent), m_model->rowCount(parent) + newrows - 1);
+ while (newItems.count())
+ m_children << newItems.takeFirst();
+ m_model->endInsertRows();
+
+ qDebug() << m_children.count(); qDebug() << m_model->rowCount();
}
void Node::fetchComplete()
#ifndef NODE_H
#define NODE_H
+#include "../localmylist_global.h"
#include "dynamicmodel_global.h"
#include <QVariant>
+#include <QList>
namespace LocalMyList {
namespace DynamicModel {
class Data;
class DataType;
-class Node {
+class Node;
+typedef QList<Node *> NodeList;
+
+class LOCALMYLISTSHARED_EXPORT Node {
public:
Node(Model *model, Node *parent, int totalRowCount, Data *data);
~Node();
+ DataType *childDataType() const;
+ void setChildDataType(DataType *dataType);
+
// Structure
Node *parent() const;
Node *child(int row) const;
protected:
Node *m_parent;
- QList<Node *> m_children;
+ NodeList m_children;
int m_totalRowCount;
Model *m_model;
--- /dev/null
+#include "typerelation.h"
+
+#include "../mylist.h"
+#include "../database.h"
+#include "../databaseclasses.h"
+#include "node.h"
+#include "datatype.h"
+#include "data.h"
+#include "types.h"
+
+namespace LocalMyList {
+namespace DynamicModel {
+
+TypeRelation::TypeRelation(QObject *parent) : QObject(parent)
+{
+}
+
+Node *TypeRelation::createNode(Model *model, Node *parent, int totalRowCount, Data *data)
+{
+ return new Node(model, parent, totalRowCount, data);
+}
+
+QString RootAnimeRelation::sourceType() const
+{
+ return QString();
+}
+
+QString RootAnimeRelation::destinationType() const
+{
+ return "anime";
+}
+
+bool RootAnimeRelation::canGetChildren() const
+{
+ return false;
+}
+
+NodeList RootAnimeRelation::getChildren(Model *model, Node *parent, int offset)
+{
+/* QSqlQuery q = MyList::instance()->database()->prepareOneShot(QString(
+ "%1 "
+ "ORDER BY title_romaji ASC "
+ "LIMIT :limit "
+ "OFFSET :offset ")
+ .arg(parent->childDataType()->baseQuery()));
+ q.bindValue(":limit", 200);
+ q.bindValue(":offset", offset);
+
+ if (!q.exec())
+ return NodeList();
+
+ NodeList newItems;
+ while (q.next())
+ {
+ AnimeData *ad = new AnimeData(this);
+ int totalRowCount = query->value(0).toInt();
+
+ {
+ QSqlResultIterator it(q);
+ fillAnimeData(*ad, it);
+ }
+
+ auto it = cache.find(ad->id());
+ if (it != cache.end())
+ {
+ delete ad;
+ ad = *it;
+ }
+ else
+ {
+ cache.insert(ad->id(), ad);
+ }
+
+ auto node = new Node(model, parent, totalRowCount, ad);
+ newItems << node;
+ }
+
+ return newItems;
+*/
+ return NodeList();
+}
+
+} // namespace DynamicModel
+} // namespace LocalMyList
--- /dev/null
+#ifndef TYPERELATION_H
+#define TYPERELATION_H
+
+#include <QObject>
+
+#include <QString>
+#include "node.h"
+#include "dynamicmodel_global.h"
+
+namespace LocalMyList {
+namespace DynamicModel {
+
+class TypeRelation : public QObject
+{
+public:
+ TypeRelation(QObject *parent = 0);
+
+ virtual QString sourceType() const = 0;
+ virtual QString destinationType() const = 0;
+
+ // Acquire
+ virtual bool canGetChildren() const = 0;
+ virtual NodeList getChildren(Model *model, Node *parent, const QString &childTypeName, int offset) = 0;
+
+
+protected:
+ Node *createNode(Model *model, Node *parent, int totalRowCount, Data *data);
+};
+
+// =========================================================================================================
+
+class RootAnimeRelation
+{
+ QString sourceType() const;
+ QString destinationType() const;
+
+ // Acquire
+ bool canGetChildren() const;
+ NodeList getChildren(Model *model, Node *parent, int offset);
+};
+
+} // namespace DynamicModel
+} // namespace LocalMyList
+
+#endif // TYPERELATION_H
#include "types.h"
+#include "../database.h"
+#include "../mylist.h"
+
namespace LocalMyList {
namespace DynamicModel {
+QString AnimeType::name() const
+{
+ return "anime";
+}
+
+QStringList AnimeType::availableChildRelations() const
+{
+ return QStringList();
+}
+
+QString AnimeType::baseQuery() const
+{
+ return QString(
+ "SELECT (SELECT COUNT(eid) FROM episode WHERE aid = a.aid), "
+ " (SELECT COUNT(e.eid) "
+ " FROM episode e "
+ " WHERE e.aid = a.aid), "
+ " (SELECT COUNT(DISTINCT eid) "
+ " FROM "
+ " (SELECT e.eid FROM episode e "
+ " JOIN file f ON (f.eid = e.eid) "
+ " WHERE e.aid = a.aid "
+ " AND f.my_watched IS NOT NULL "
+ " UNION "
+ " SELECT e.eid FROM episode e "
+ " JOIN file_episode_rel fer ON fer.eid = e.eid "
+ " JOIN file f ON f.fid = fer.fid "
+ " WHERE e.aid = a.aid "
+ " AND f.my_watched IS NOT NULL) sq), "
+ " (SELECT CASE WHEN array_length(my_state_array, 1) > 1 THEN -1 ELSE my_state_array[1] END "
+ " FROM "
+ " (SELECT array_agg(my_state) my_state_array "
+ " FROM "
+ " (SELECT my_state "
+ " FROM file "
+ " WHERE aid = a.aid "
+ " UNION "
+ " SELECT f.my_state "
+ " FROM file f "
+ " JOIN file_episode_rel fer ON (fer.fid = f.eid) "
+ " JOIN episode e ON (e.eid = fer.eid AND e.aid = a.aid) "
+ " ) AS sq) AS sq) AS my_state, "
+ " %1 "
+ " FROM anime a ")
+ .arg(Database::animeFields());
+}
+
+int AnimeType::size() const
+{
+ if (m_size)
+ return m_size;
+
+ QSqlQuery &q = MyList::instance()->database()->prepare(
+ "SELECT count(aid) FROM anime");
+
+ if (!MyList::instance()->database()->exec(q))
+ return 0;
+
+ if (!q.next())
+ return 0;
+
+ m_size = q.value(0).toInt();
+ q.finish();
+
+ return m_size;
+}
+
+bool AnimeType::canGetChildren(const QString &childTypeName) const
+{
+ Q_UNUSED(childTypeName)
+ return false;
+}
+
+NodeList AnimeType::getChildren(Model *model, Node *parent, const QString &childTypeName, int offset)
+{
+ QSqlQuery q = MyList::instance()->database()->prepareOneShot(QString(
+ "%1 "
+ "ORDER BY title_romaji ASC "
+ "LIMIT :limit "
+ "OFFSET :offset ")
+ .arg(baseQuery()));
+ q.bindValue(":limit", LIMIT);
+ q.bindValue(":offset", offset);
+
+ if (!q.exec())
+ return NodeList();
+
+ NodeList newItems;
+ while (q.next())
+ {
+ AnimeData *ad = new AnimeData(this);
+ int totalRowCount = query->value(0).toInt();
+
+ {
+ QSqlResultIterator it(q);
+ fillAnimeData(*ad, it);
+ }
+
+ auto it = cache.find(ad->id());
+ if (it != cache.end())
+ {
+ delete ad;
+ ad = *it;
+ }
+ else
+ {
+ cache.insert(ad->id(), ad);
+ }
+
+ auto node = new Node(model, parent, totalRowCount, ad);
+ newItems << node;
+ }
+
+ return newItems;
+}
+
+void AnimeType::update(Data *data)
+{
+
+}
+
+void AnimeType::childUpdate(Data *parentData, const Data *oldData, const Data *newData, Operation operation)
+{
+
+}
+
+NodeCompare AnimeType::nodeCompareFunction() const
+{
+ return [](Node *a, Node *b) -> bool
+ {
+ return a < b;
+ };
+}
+
+void AnimeType::fillAnimeData(AnimeData &data, SqlResultIteratorInterface &query)
+{
+ data.episodesInMyList = query.value(1).toInt();
+ data.watchedEpisodes = query.value(2).toInt();
+ data.myState = query.value(3).toInt();
+ Database::readAnimeData(query, data.animeData, 4);
+}
+
} // namespace DynamicModel
} // namespace LocalMyList
#ifndef TYPES_H
#define TYPES_H
+#include "../localmylist_global.h"
+#include "datatype.h"
+#include "data.h"
+
namespace LocalMyList {
namespace DynamicModel {
+class LOCALMYLISTSHARED_EXPORT AnimeType : public DataType
+{
+ QString name() const;
+ QStringList availableChildRelations() const;
+
+ QString baseQuery() const;
+
+ int size() const;
+
+ bool canGetChildren(const QString &childTypeName) const;
+ NodeList getChildren(Model *model, Node *parent, const QString &childTypeName, int offset);
+
+ void update(Data *data);
+ void childUpdate(Data *parentData, const Data *oldData, const Data *newData, Operation operation);
+
+ NodeCompare nodeCompareFunction() const;
+
+private:
+ void fillAnimeData(AnimeData &data, SqlResultIteratorInterface &query);
+ QMap<int, AnimeData *> cache;
+};
+
} // namespace DynamicModel
} // namespace LocalMyList
filelocationchecktask.cpp \
messagehandler.cpp \
asyncquerytask.cpp \
- dynamicmodel/datamanager.cpp \
dynamicmodel/data.cpp \
dynamicmodel/node.cpp \
dynamicmodel/model.cpp \
dynamicmodel/datatype.cpp \
- dynamicmodel/types.cpp
+ dynamicmodel/types.cpp \
+ dynamicmodel/datamodel.cpp \
+ dynamicmodel/typerelation.cpp
HEADERS += \
localmylist_global.h \
sqlasyncquery.h \
sqlasyncqueryinternal.h \
asyncquerytask.h \
+ filelocationchecktask.h \
sqlresultiteratorinterface.h \
- dynamicmodel/datamanager.h \
dynamicmodel/data.h \
dynamicmodel/node.h \
dynamicmodel/model.h \
dynamicmodel/datatype.h \
dynamicmodel/dynamicmodel_global.h \
- dynamicmodel/types.h
+ dynamicmodel/types.h \
+ dynamicmodel/datamodel.h \
+ dynamicmodel/typerelation.h
+
CONV_HEADERS += \
include/LocalMyList/AbstractTask \
include/LocalMyList/AddFileTask \