1
2 #include <QtGui>
3 #include <QtDebug>
4 #include <limits>
5
6 #include "comic_item.h"
7 #include "comic_model.h"
8 #include "data_base_management.h"
9 #include "qnaturalsorting.h"
10 #include "comic_db.h"
11 #include "db_helper.h"
12 #include "query_parser.h"
13 #include "reading_list_model.h"
14
15 //ci.number,ci.title,c.fileName,ci.numPages,c.id,c.parentId,c.path,ci.hash,ci.read
16 #include "QsLog.h"
17
ComicModel(QObject * parent)18 ComicModel::ComicModel(QObject *parent)
19 : QAbstractItemModel(parent)
20 {
21 connect(this, SIGNAL(beforeReset()), this, SIGNAL(modelAboutToBeReset()));
22 connect(this, SIGNAL(reset()), this, SIGNAL(modelReset()));
23 }
24
ComicModel(QSqlQuery & sqlquery,QObject * parent)25 ComicModel::ComicModel(QSqlQuery &sqlquery, QObject *parent)
26 : QAbstractItemModel(parent)
27 {
28 setupModelData(sqlquery);
29 }
30
~ComicModel()31 ComicModel::~ComicModel()
32 {
33 qDeleteAll(_data);
34 }
35
columnCount(const QModelIndex & parent) const36 int ComicModel::columnCount(const QModelIndex &parent) const
37 {
38 Q_UNUSED(parent)
39 if (_data.isEmpty())
40 return 0;
41 return _data.first()->columnCount();
42 }
43
canDropMimeData(const QMimeData * data,Qt::DropAction action,int row,int column,const QModelIndex & parent) const44 bool ComicModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const
45 {
46 Q_UNUSED(action);
47 Q_UNUSED(row);
48 Q_UNUSED(column);
49 Q_UNUSED(parent);
50
51 if (!enableResorting)
52 return false;
53 return data->formats().contains(YACReader::YACReaderLibrarComiscSelectionMimeDataFormat);
54 }
55
56 //TODO: optimize this method (seriously)
dropMimeData(const QMimeData * data,Qt::DropAction action,int row,int column,const QModelIndex & parent)57 bool ComicModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
58 {
59
60 QAbstractItemModel::dropMimeData(data, action, row, column, parent);
61 QLOG_TRACE() << ">>>>>>>>>>>>>>dropMimeData ComicModel<<<<<<<<<<<<<<<<<" << parent << row << "," << column;
62
63 if (!data->formats().contains(YACReader::YACReaderLibrarComiscSelectionMimeDataFormat))
64 return false;
65
66 QList<qulonglong> comicIds = YACReader::mimeDataToComicsIds(data);
67 QList<int> currentIndexes;
68 int i;
69 foreach (qulonglong id, comicIds) {
70 i = 0;
71 foreach (ComicItem *item, _data) {
72 if (item->data(Id) == id) {
73 currentIndexes << i;
74 break;
75 }
76 i++;
77 }
78 }
79
80 std::sort(currentIndexes.begin(), currentIndexes.end());
81 QList<ComicItem *> resortedData;
82
83 if (currentIndexes.contains(row)) //no resorting
84 return false;
85
86 ComicItem *destinationItem;
87 if (row == -1 || row >= _data.length())
88 destinationItem = 0;
89 else
90 destinationItem = _data.at(row);
91
92 QList<int> newSorting;
93
94 i = 0;
95 foreach (ComicItem *item, _data) {
96 if (!currentIndexes.contains(i)) {
97
98 if (item == destinationItem) {
99 foreach (int index, currentIndexes) {
100 resortedData << _data.at(index);
101 newSorting << index;
102 }
103 }
104
105 resortedData << item;
106 newSorting << i;
107 }
108
109 i++;
110 }
111
112 if (destinationItem == 0) {
113 foreach (int index, currentIndexes) {
114 resortedData << _data.at(index);
115 newSorting << index;
116 }
117 }
118
119 QLOG_TRACE() << newSorting;
120
121 int tempRow = row;
122
123 if (tempRow < 0)
124 tempRow = _data.count();
125
126 foreach (qulonglong id, comicIds) {
127 int i = 0;
128 foreach (ComicItem *item, _data) {
129 if (item->data(Id) == id) {
130 beginMoveRows(parent, i, i, parent, tempRow);
131
132 bool skipElement = i == tempRow || i + 1 == tempRow;
133
134 if (!skipElement) {
135 if (i > tempRow)
136 _data.move(i, tempRow);
137 else
138 _data.move(i, tempRow - 1);
139 }
140
141 endMoveRows();
142
143 if (i > tempRow)
144 tempRow++;
145
146 break;
147 }
148 i++;
149 }
150 }
151
152 //TODO fix selection
153 QList<qulonglong> allComicIds;
154 foreach (ComicItem *item, _data) {
155 allComicIds << item->data(Id).toULongLong();
156 }
157 QString connectionName = "";
158 {
159 QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
160 switch (mode) {
161 case Favorites:
162 DBHelper::reasignOrderToComicsInFavorites(allComicIds, db);
163 break;
164 case Label:
165 DBHelper::reasignOrderToComicsInLabel(sourceId, allComicIds, db);
166 break;
167 case ReadingList:
168 DBHelper::reasignOrderToComicsInReadingList(sourceId, allComicIds, db);
169 break;
170 default:
171 break;
172 }
173 connectionName = db.connectionName();
174 }
175 QSqlDatabase::removeDatabase(connectionName);
176
177 //endMoveRows();
178
179 emit resortedIndexes(newSorting);
180 int destSelectedIndex = row < 0 ? _data.length() : row;
181
182 if (destSelectedIndex > currentIndexes.at(0))
183 emit newSelectedIndex(index(qMax(0, destSelectedIndex - 1), 0, parent));
184 else
185 emit newSelectedIndex(index(qMax(0, destSelectedIndex), 0, parent));
186
187 return true;
188 }
189
canBeResorted()190 bool ComicModel::canBeResorted()
191 {
192 return enableResorting;
193 }
194
mimeData(const QModelIndexList & indexes) const195 QMimeData *ComicModel::mimeData(const QModelIndexList &indexes) const
196 {
197 //custom model data
198 //application/yacreader-comics-ids + list of ids in a QByteArray
199 QList<qulonglong> ids;
200 foreach (QModelIndex index, indexes) {
201 QLOG_DEBUG() << "dragging : " << index.data(IdRole).toULongLong();
202 ids << index.data(IdRole).toULongLong();
203 }
204
205 QByteArray data;
206 QDataStream out(&data, QIODevice::WriteOnly);
207 out << ids; //serialize the list of identifiers
208
209 auto mimeData = new QMimeData();
210 mimeData->setData(YACReader::YACReaderLibrarComiscSelectionMimeDataFormat, data);
211
212 return mimeData;
213 }
214
mimeTypes() const215 QStringList ComicModel::mimeTypes() const
216 {
217 QLOG_DEBUG() << "mimeTypes";
218 QStringList list;
219 list << YACReader::YACReaderLibrarComiscSelectionMimeDataFormat;
220 return list;
221 }
222
roleNames() const223 QHash<int, QByteArray> ComicModel::roleNames() const
224 {
225 QHash<int, QByteArray> roles;
226
227 roles[NumberRole] = "number";
228 roles[TitleRole] = "title";
229 roles[FileNameRole] = "file_name";
230 roles[NumPagesRole] = "num_pages";
231 roles[IdRole] = "id";
232 roles[Parent_IdRole] = "parent_id";
233 roles[PathRole] = "path";
234 roles[HashRole] = "hash";
235 roles[ReadColumnRole] = "read_column";
236 roles[IsBisRole] = "is_bis";
237 roles[CurrentPageRole] = "current_page";
238 roles[RatingRole] = "rating";
239 roles[HasBeenOpenedRole] = "has_been_opened";
240 roles[CoverPathRole] = "cover_path";
241
242 return roles;
243 }
244
data(const QModelIndex & index,int role) const245 QVariant ComicModel::data(const QModelIndex &index, int role) const
246 {
247 if (!index.isValid())
248 return QVariant();
249
250 /*if (index.column() == TableModel::Rating && role == Qt::DecorationRole)
251 {
252 TableItem *item = static_cast<TableItem*>(index.internalPointer());
253 return QPixmap(QString(":/images/rating%1.png").arg(item->data(index.column()).toInt()));
254 }*/
255
256 if (role == Qt::DecorationRole) {
257 return QVariant();
258 }
259
260 if (role == Qt::TextAlignmentRole) {
261 switch (index.column()) //TODO obtener esto de la query
262 {
263 case ComicModel::Number:
264 return QVariant(Qt::AlignRight | Qt::AlignVCenter);
265 case ComicModel::NumPages:
266 return QVariant(Qt::AlignRight | Qt::AlignVCenter);
267 case ComicModel::Hash:
268 return QVariant(Qt::AlignRight | Qt::AlignVCenter);
269 case ComicModel::CurrentPage:
270 return QVariant(Qt::AlignRight | Qt::AlignVCenter);
271 default:
272 return QVariant(Qt::AlignLeft | Qt::AlignVCenter);
273 }
274 }
275
276 //TODO check here if any view is asking for TableModel::Roles
277 //these roles will be used from QML/GridView
278
279 auto item = static_cast<ComicItem *>(index.internalPointer());
280
281 if (role == NumberRole)
282 return item->data(Number);
283 else if (role == TitleRole)
284 return item->data(Title).isNull() ? item->data(FileName) : item->data(Title);
285 else if (role == FileNameRole)
286 return item->data(FileName);
287 else if (role == RatingRole)
288 return item->data(Rating);
289 else if (role == CoverPathRole)
290 return getCoverUrlPathForComicHash(item->data(Hash).toString());
291 else if (role == NumPagesRole)
292 return item->data(NumPages);
293 else if (role == CurrentPageRole)
294 return item->data(CurrentPage);
295 else if (role == ReadColumnRole)
296 return item->data(ReadColumn).toBool();
297 else if (role == HasBeenOpenedRole)
298 return item->data(ComicModel::HasBeenOpened);
299 else if (role == IdRole)
300 return item->data(Id);
301
302 if (role != Qt::DisplayRole)
303 return QVariant();
304
305 if (index.column() == ComicModel::Hash)
306 return QString::number(item->data(index.column()).toString().right(item->data(index.column()).toString().length() - 40).toInt() / 1024.0 / 1024.0, 'f', 2) + "Mb";
307 if (index.column() == ComicModel::ReadColumn)
308 return (item->data(ComicModel::CurrentPage).toInt() == item->data(ComicModel::NumPages).toInt() || item->data(ComicModel::ReadColumn).toBool()) ? QVariant(tr("yes")) : QVariant(tr("no"));
309 if (index.column() == ComicModel::CurrentPage)
310 return item->data(ComicModel::HasBeenOpened).toBool() ? item->data(index.column()) : QVariant("-");
311
312 if (index.column() == ComicModel::Rating)
313 return QVariant();
314
315 return item->data(index.column());
316 }
317
flags(const QModelIndex & index) const318 Qt::ItemFlags ComicModel::flags(const QModelIndex &index) const
319 {
320 if (!index.isValid())
321 return {};
322 if (index.column() == ComicModel::Rating)
323 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
324 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
325 }
326
headerData(int section,Qt::Orientation orientation,int role) const327 QVariant ComicModel::headerData(int section, Qt::Orientation orientation,
328 int role) const
329 {
330 if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
331 switch (section) //TODO obtener esto de la query
332 {
333 case ComicModel::Number:
334 return QVariant(QString("#"));
335 case ComicModel::Title:
336 return QVariant(QString(tr("Title")));
337 case ComicModel::FileName:
338 return QVariant(QString(tr("File Name")));
339 case ComicModel::NumPages:
340 return QVariant(QString(tr("Pages")));
341 case ComicModel::Hash:
342 return QVariant(QString(tr("Size")));
343 case ComicModel::ReadColumn:
344 return QVariant(QString(tr("Read")));
345 case ComicModel::CurrentPage:
346 return QVariant(QString(tr("Current Page")));
347 case ComicModel::Rating:
348 return QVariant(QString(tr("Rating")));
349 }
350 }
351
352 if (orientation == Qt::Horizontal && role == Qt::TextAlignmentRole) {
353 switch (section) //TODO obtener esto de la query
354 {
355 case ComicModel::Number:
356 return QVariant(Qt::AlignRight | Qt::AlignVCenter);
357 case ComicModel::NumPages:
358 return QVariant(Qt::AlignRight | Qt::AlignVCenter);
359 case ComicModel::Hash:
360 return QVariant(Qt::AlignRight | Qt::AlignVCenter);
361 case ComicModel::CurrentPage:
362 return QVariant(Qt::AlignRight | Qt::AlignVCenter);
363 default:
364 return QVariant(Qt::AlignLeft | Qt::AlignVCenter);
365 }
366 }
367
368 if (orientation == Qt::Vertical && role == Qt::DecorationRole) {
369 QString fileName = _data.value(section)->data(ComicModel::FileName).toString();
370 QFileInfo fi(fileName);
371 QString ext = fi.suffix();
372
373 if (ext.compare("cbr", Qt::CaseInsensitive) == 0)
374 return QVariant(QIcon(":/images/comicRar.png"));
375 else if (ext.compare("cbz", Qt::CaseInsensitive) == 0)
376 return QVariant(QIcon(":/images/comicZip.png"));
377 else if (ext.compare("pdf", Qt::CaseInsensitive) == 0)
378 return QVariant(QIcon(":/images/pdf.png"));
379 else if (ext.compare("tar", Qt::CaseInsensitive) == 0)
380 return QVariant(QIcon(":/images/tar.png"));
381 else if (ext.compare("zip", Qt::CaseInsensitive) == 0)
382 return QVariant(QIcon(":/images/zip.png"));
383 else if (ext.compare("rar", Qt::CaseInsensitive) == 0)
384 return QVariant(QIcon(":/images/rar.png"));
385 #ifndef use_unarr
386 else if (ext.compare("7z", Qt::CaseInsensitive) == 0)
387 return QVariant(QIcon(":/images/7z.png"));
388 else if (ext.compare("cb7", Qt::CaseInsensitive) == 0)
389 return QVariant(QIcon(":/images/comic7z.png"));
390 #endif
391 else if (ext.compare("cbt", Qt::CaseInsensitive) == 0)
392 return QVariant(QIcon(":/images/comicTar.png"));
393 }
394
395 return QVariant();
396 }
397
index(int row,int column,const QModelIndex & parent) const398 QModelIndex ComicModel::index(int row, int column, const QModelIndex &parent)
399 const
400 {
401 if (!hasIndex(row, column, parent))
402 return QModelIndex();
403
404 return createIndex(row, column, _data.at(row));
405 }
406
parent(const QModelIndex & index) const407 QModelIndex ComicModel::parent(const QModelIndex &index) const
408 {
409 Q_UNUSED(index)
410 return QModelIndex();
411 }
412
rowCount(const QModelIndex & parent) const413 int ComicModel::rowCount(const QModelIndex &parent) const
414 {
415 if (parent.column() > 0)
416 return 0;
417
418 if (!parent.isValid())
419 return _data.count();
420
421 return 0;
422 }
423
getPaths(const QString & _source)424 QStringList ComicModel::getPaths(const QString &_source)
425 {
426 QStringList paths;
427 QString source = _source + "/.yacreaderlibrary/covers/";
428 QList<ComicItem *>::ConstIterator itr;
429 for (itr = _data.constBegin(); itr != _data.constEnd(); itr++) {
430 QString hash = (*itr)->data(ComicModel::Hash).toString();
431 paths << source + hash + ".jpg";
432 }
433
434 return paths;
435 }
436
setupFolderModelData(unsigned long long int folderId,const QString & databasePath)437 void ComicModel::setupFolderModelData(unsigned long long int folderId, const QString &databasePath)
438 {
439 enableResorting = false;
440 mode = Folder;
441 sourceId = folderId;
442
443 beginResetModel();
444 qDeleteAll(_data);
445 _data.clear();
446
447 _databasePath = databasePath;
448 QString connectionName = "";
449 {
450 QSqlDatabase db = DataBaseManagement::loadDatabase(databasePath);
451 QSqlQuery selectQuery(db);
452 selectQuery.prepare("SELECT ci.number,ci.title,c.fileName,ci.numPages,c.id,c.parentId,c.path,ci.hash,ci.read,ci.isBis,ci.currentPage,ci.rating,ci.hasBeenOpened "
453 "FROM comic c INNER JOIN comic_info ci ON (c.comicInfoId = ci.id) "
454 "WHERE c.parentId = :parentId");
455 selectQuery.bindValue(":parentId", folderId);
456 selectQuery.exec();
457 setupModelData(selectQuery);
458 connectionName = db.connectionName();
459 }
460 QSqlDatabase::removeDatabase(connectionName);
461 endResetModel();
462 }
463
setupLabelModelData(unsigned long long parentLabel,const QString & databasePath)464 void ComicModel::setupLabelModelData(unsigned long long parentLabel, const QString &databasePath)
465 {
466 enableResorting = true;
467 mode = Label;
468 sourceId = parentLabel;
469
470 beginResetModel();
471 qDeleteAll(_data);
472 _data.clear();
473
474 _databasePath = databasePath;
475 QString connectionName = "";
476 {
477 QSqlDatabase db = DataBaseManagement::loadDatabase(databasePath);
478 QSqlQuery selectQuery(db);
479 selectQuery.prepare("SELECT ci.number,ci.title,c.fileName,ci.numPages,c.id,c.parentId,c.path,ci.hash,ci.read,ci.isBis,ci.currentPage,ci.rating,ci.hasBeenOpened "
480 "FROM comic c INNER JOIN comic_info ci ON (c.comicInfoId = ci.id) "
481 "INNER JOIN comic_label cl ON (c.id == cl.comic_id) "
482 "WHERE cl.label_id = :parentLabelId "
483 "ORDER BY cl.ordering");
484 selectQuery.bindValue(":parentLabelId", parentLabel);
485 selectQuery.exec();
486 setupModelDataForList(selectQuery);
487 connectionName = db.connectionName();
488 }
489 QSqlDatabase::removeDatabase(connectionName);
490 endResetModel();
491 }
492
setupReadingListModelData(unsigned long long parentReadingList,const QString & databasePath)493 void ComicModel::setupReadingListModelData(unsigned long long parentReadingList, const QString &databasePath)
494 {
495 mode = ReadingList;
496 sourceId = parentReadingList;
497
498 beginResetModel();
499 qDeleteAll(_data);
500 _data.clear();
501
502 _databasePath = databasePath;
503 QString connectionName = "";
504 {
505 QSqlDatabase db = DataBaseManagement::loadDatabase(databasePath);
506 QList<qulonglong> ids;
507 ids << parentReadingList;
508
509 QSqlQuery subfolders(db);
510 subfolders.prepare("SELECT id "
511 "FROM reading_list "
512 "WHERE parentId = :parentId "
513 "ORDER BY ordering ASC");
514 subfolders.bindValue(":parentId", parentReadingList);
515 subfolders.exec();
516 while (subfolders.next())
517 ids << subfolders.record().value(0).toULongLong();
518
519 enableResorting = ids.length() == 1; //only resorting if no sublists exist
520
521 foreach (qulonglong id, ids) {
522 QSqlQuery selectQuery(db);
523 selectQuery.prepare("SELECT ci.number,ci.title,c.fileName,ci.numPages,c.id,c.parentId,c.path,ci.hash,ci.read,ci.isBis,ci.currentPage,ci.rating,ci.hasBeenOpened "
524 "FROM comic c INNER JOIN comic_info ci ON (c.comicInfoId = ci.id) "
525 "INNER JOIN comic_reading_list crl ON (c.id == crl.comic_id) "
526 "WHERE crl.reading_list_id = :parentReadingList "
527 "ORDER BY crl.ordering");
528 selectQuery.bindValue(":parentReadingList", id);
529 selectQuery.exec();
530
531 //TODO, extra information is needed (resorting)
532 QList<ComicItem *> tempData = _data;
533 _data.clear();
534
535 setupModelDataForList(selectQuery);
536
537 _data = tempData << _data;
538 }
539 connectionName = db.connectionName();
540 }
541 QSqlDatabase::removeDatabase(connectionName);
542 endResetModel();
543 }
544
setupFavoritesModelData(const QString & databasePath)545 void ComicModel::setupFavoritesModelData(const QString &databasePath)
546 {
547 enableResorting = true;
548 mode = Favorites;
549 sourceId = -1;
550
551 beginResetModel();
552 qDeleteAll(_data);
553 _data.clear();
554
555 _databasePath = databasePath;
556 QString connectionName = "";
557 {
558 QSqlDatabase db = DataBaseManagement::loadDatabase(databasePath);
559 QSqlQuery selectQuery(db);
560 selectQuery.prepare("SELECT ci.number,ci.title,c.fileName,ci.numPages,c.id,c.parentId,c.path,ci.hash,ci.read,ci.isBis,ci.currentPage,ci.rating,ci.hasBeenOpened "
561 "FROM comic c INNER JOIN comic_info ci ON (c.comicInfoId = ci.id) "
562 "INNER JOIN comic_default_reading_list cdrl ON (c.id == cdrl.comic_id) "
563 "WHERE cdrl.default_reading_list_id = :parentDefaultListId "
564 "ORDER BY cdrl.ordering");
565 selectQuery.bindValue(":parentDefaultListId", 1);
566 selectQuery.exec();
567 setupModelDataForList(selectQuery);
568 connectionName = db.connectionName();
569 }
570 QSqlDatabase::removeDatabase(connectionName);
571 endResetModel();
572 }
573
setupReadingModelData(const QString & databasePath)574 void ComicModel::setupReadingModelData(const QString &databasePath)
575 {
576 enableResorting = false;
577 mode = Reading;
578 sourceId = -1;
579
580 beginResetModel();
581 qDeleteAll(_data);
582 _data.clear();
583
584 _databasePath = databasePath;
585 QString connectionName = "";
586 {
587 QSqlDatabase db = DataBaseManagement::loadDatabase(databasePath);
588 QSqlQuery selectQuery(db);
589 selectQuery.prepare("SELECT ci.number,ci.title,c.fileName,ci.numPages,c.id,c.parentId,c.path,ci.hash,ci.read,ci.isBis,ci.currentPage,ci.rating,ci.hasBeenOpened "
590 "FROM comic c INNER JOIN comic_info ci ON (c.comicInfoId = ci.id) "
591 "WHERE ci.hasBeenOpened = 1 AND ci.read = 0 "
592 "ORDER BY ci.lastTimeOpened DESC");
593 selectQuery.exec();
594
595 setupModelDataForList(selectQuery);
596 connectionName = db.connectionName();
597 }
598 QSqlDatabase::removeDatabase(connectionName);
599 endResetModel();
600 }
601
setModelData(QList<ComicItem * > * data,const QString & databasePath)602 void ComicModel::setModelData(QList<ComicItem *> *data, const QString &databasePath)
603 {
604 _databasePath = databasePath;
605
606 beginResetModel();
607
608 qDeleteAll(_data);
609
610 _data.clear();
611
612 _data.append(*data);
613
614 endResetModel();
615
616 emit searchNumResults(_data.length());
617
618 delete data;
619 }
620
getComicPath(QModelIndex mi)621 QString ComicModel::getComicPath(QModelIndex mi)
622 {
623 if (mi.isValid())
624 return _data.at(mi.row())->data(ComicModel::Path).toString();
625 return "";
626 }
627
setupModelData(QSqlQuery & sqlquery)628 void ComicModel::setupModelData(QSqlQuery &sqlquery)
629 {
630 int numColumns = sqlquery.record().count();
631
632 while (sqlquery.next()) {
633 QList<QVariant> data;
634
635 for (int i = 0; i < numColumns; i++)
636 data << sqlquery.value(i);
637
638 _data.append(new ComicItem(data));
639 }
640
641 std::sort(_data.begin(), _data.end(), [](const ComicItem *c1, const ComicItem *c2) {
642 if (c1->data(ComicModel::Number).isNull() && c2->data(ComicModel::Number).isNull()) {
643 return naturalSortLessThanCI(c1->data(ComicModel::FileName).toString(), c2->data(ComicModel::FileName).toString());
644 } else {
645 if (c1->data(ComicModel::Number).isNull() == false && c2->data(ComicModel::Number).isNull() == false) {
646 return c1->data(ComicModel::Number).toInt() < c2->data(ComicModel::Number).toInt();
647 } else {
648 return c2->data(ComicModel::Number).isNull();
649 }
650 }
651 });
652 }
653
654 //comics are sorted by "ordering", the sorting is done in the sql query
setupModelDataForList(QSqlQuery & sqlquery)655 void ComicModel::setupModelDataForList(QSqlQuery &sqlquery)
656 {
657 int numColumns = sqlquery.record().count();
658
659 while (sqlquery.next()) {
660 QList<QVariant> data;
661 for (int i = 0; i < numColumns; i++)
662 data << sqlquery.value(i);
663
664 _data.append(new ComicItem(data));
665 }
666 }
667
getComic(const QModelIndex & mi)668 ComicDB ComicModel::getComic(const QModelIndex &mi)
669 {
670 ComicDB c;
671 QString connectionName = "";
672 {
673 QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
674 c = DBHelper::loadComic(_data.at(mi.row())->data(ComicModel::Id).toULongLong(), db);
675 connectionName = db.connectionName();
676 }
677 QSqlDatabase::removeDatabase(connectionName);
678
679 return c;
680 }
681
_getComic(const QModelIndex & mi)682 ComicDB ComicModel::_getComic(const QModelIndex &mi)
683 {
684 ComicDB c;
685 QString connectionName = "";
686 {
687 QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
688 c = DBHelper::loadComic(_data.at(mi.row())->data(ComicModel::Id).toULongLong(), db);
689 connectionName = db.connectionName();
690 }
691 QSqlDatabase::removeDatabase(connectionName);
692
693 return c;
694 }
695
getReadList()696 QVector<YACReaderComicReadStatus> ComicModel::getReadList()
697 {
698 int numComics = _data.count();
699 QVector<YACReaderComicReadStatus> readList(numComics);
700 for (int i = 0; i < numComics; i++) {
701 if (_data.value(i)->data(ComicModel::ReadColumn).toBool())
702 readList[i] = YACReader::Read;
703 else if (_data.value(i)->data(ComicModel::CurrentPage).toInt() == _data.value(i)->data(ComicModel::NumPages).toInt())
704 readList[i] = YACReader::Read;
705 else if (_data.value(i)->data(ComicModel::HasBeenOpened).toBool())
706 readList[i] = YACReader::Opened;
707 else
708 readList[i] = YACReader::Unread;
709 }
710 return readList;
711 }
712 //TODO untested, this method is no longer used
setAllComicsRead(YACReaderComicReadStatus read)713 QVector<YACReaderComicReadStatus> ComicModel::setAllComicsRead(YACReaderComicReadStatus read)
714 {
715 return setComicsRead(persistentIndexList(), read);
716 }
717
getAllComics()718 QList<ComicDB> ComicModel::getAllComics()
719 {
720 QList<ComicDB> comics;
721 QString connectionName = "";
722 {
723 QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
724 db.transaction();
725
726 int numComics = _data.count();
727 for (int i = 0; i < numComics; i++) {
728 comics.append(DBHelper::loadComic(_data.value(i)->data(ComicModel::Id).toULongLong(), db));
729 }
730
731 db.commit();
732 connectionName = db.connectionName();
733 }
734 QSqlDatabase::removeDatabase(connectionName);
735
736 return comics;
737 }
738
getComics(QList<QModelIndex> list)739 QList<ComicDB> ComicModel::getComics(QList<QModelIndex> list)
740 {
741 QList<ComicDB> comics;
742 for (auto itr = list.constBegin(); itr != list.constEnd(); itr++) {
743 comics.append(_getComic(*itr));
744 }
745 return comics;
746 }
747 //TODO
setComicsRead(QList<QModelIndex> list,YACReaderComicReadStatus read)748 QVector<YACReaderComicReadStatus> ComicModel::setComicsRead(QList<QModelIndex> list, YACReaderComicReadStatus read)
749 {
750 QString connectionName = "";
751 {
752 QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
753 db.transaction();
754 foreach (QModelIndex mi, list) {
755 if (read == YACReader::Read) {
756 _data.value(mi.row())->setData(ComicModel::ReadColumn, QVariant(true));
757 ComicDB c = DBHelper::loadComic(_data.value(mi.row())->data(ComicModel::Id).toULongLong(), db);
758 c.info.read = true;
759 DBHelper::update(&(c.info), db);
760 }
761 if (read == YACReader::Unread) {
762 _data.value(mi.row())->setData(ComicModel::ReadColumn, QVariant(false));
763 _data.value(mi.row())->setData(ComicModel::CurrentPage, QVariant(1));
764 _data.value(mi.row())->setData(ComicModel::HasBeenOpened, QVariant(false));
765 ComicDB c = DBHelper::loadComic(_data.value(mi.row())->data(ComicModel::Id).toULongLong(), db);
766 c.info.read = false;
767 c.info.currentPage = 1;
768 c.info.hasBeenOpened = false;
769 c.info.lastTimeOpened.setValue(QVariant());
770 DBHelper::update(&(c.info), db);
771 }
772 }
773 db.commit();
774 connectionName = db.connectionName();
775 }
776 QSqlDatabase::removeDatabase(connectionName);
777
778 emit dataChanged(index(list.first().row(), ComicModel::ReadColumn), index(list.last().row(), ComicModel::HasBeenOpened), QVector<int>() << ReadColumnRole << CurrentPageRole << HasBeenOpenedRole);
779
780 return getReadList();
781 }
782
setComicsManga(QList<QModelIndex> list,bool isManga)783 void ComicModel::setComicsManga(QList<QModelIndex> list, bool isManga)
784 {
785 QString connectionName = "";
786 {
787 QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
788 db.transaction();
789 foreach (QModelIndex mi, list) {
790 ComicDB c = DBHelper::loadComic(_data.value(mi.row())->data(ComicModel::Id).toULongLong(), db);
791 c.info.manga = isManga;
792 DBHelper::update(&(c.info), db);
793 }
794 db.commit();
795 connectionName = db.connectionName();
796 }
797 QSqlDatabase::removeDatabase(connectionName);
798 }
799
asignNumbers(QList<QModelIndex> list,int startingNumber)800 qint64 ComicModel::asignNumbers(QList<QModelIndex> list, int startingNumber)
801 {
802 qint64 idFirst;
803 QString connectionName = "";
804 {
805 QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
806 db.transaction();
807 idFirst = _data.value(list[0].row())->data(ComicModel::Id).toULongLong();
808 int i = 0;
809 foreach (QModelIndex mi, list) {
810 ComicDB c = DBHelper::loadComic(_data.value(mi.row())->data(ComicModel::Id).toULongLong(), db);
811 c.info.number = startingNumber + i;
812 c.info.edited = true;
813 DBHelper::update(&(c.info), db);
814 i++;
815 }
816 db.commit();
817 connectionName = db.connectionName();
818 }
819
820 QSqlDatabase::removeDatabase(connectionName);
821
822 return idFirst;
823 }
getIndexFromId(quint64 id)824 QModelIndex ComicModel::getIndexFromId(quint64 id)
825 {
826 QList<ComicItem *>::ConstIterator itr;
827 int i = 0;
828 for (itr = _data.constBegin(); itr != _data.constEnd(); itr++) {
829 if ((*itr)->data(ComicModel::Id).toULongLong() == id)
830 break;
831 i++;
832 }
833
834 return index(i, 0);
835 }
836
837 //TODO completely inefficiently
getIndexesFromIds(const QList<qulonglong> & comicIds)838 QList<QModelIndex> ComicModel::getIndexesFromIds(const QList<qulonglong> &comicIds)
839 {
840 QList<QModelIndex> comicsIndexes;
841
842 foreach (qulonglong id, comicIds)
843 comicsIndexes << getIndexFromId(id);
844
845 return comicsIndexes;
846 }
847
startTransaction()848 void ComicModel::startTransaction()
849 {
850 auto dbTransaction = DataBaseManagement::loadDatabase(_databasePath);
851 _databaseConnection = dbTransaction.connectionName();
852 dbTransaction.transaction();
853 }
854
finishTransaction()855 void ComicModel::finishTransaction()
856 {
857 {
858 QSqlDatabase::database(_databaseConnection).commit();
859 }
860 QSqlDatabase::removeDatabase(_databaseConnection);
861 }
862
removeInTransaction(int row)863 void ComicModel::removeInTransaction(int row)
864 {
865 auto dbTransaction = QSqlDatabase::database(_databaseConnection);
866 ComicDB c = DBHelper::loadComic(_data.at(row)->data(ComicModel::Id).toULongLong(), dbTransaction);
867
868 DBHelper::removeFromDB(&c, dbTransaction);
869 beginRemoveRows(QModelIndex(), row, row);
870 removeRow(row);
871 delete _data.at(row);
872 _data.removeAt(row);
873
874 endRemoveRows();
875 }
876
remove(int row)877 void ComicModel::remove(int row)
878 {
879 removeInTransaction(row);
880 }
881
reload(const ComicDB & comic)882 void ComicModel::reload(const ComicDB &comic)
883 {
884 int row = 0;
885 bool found = false;
886 foreach (ComicItem *item, _data) {
887 if (item->data(ComicModel::Id).toULongLong() == comic.id) {
888 found = true;
889 item->setData(ComicModel::ReadColumn, comic.info.read);
890 item->setData(ComicModel::CurrentPage, comic.info.currentPage);
891 item->setData(ComicModel::HasBeenOpened, true);
892 break;
893 }
894 row++;
895 }
896 if (found)
897 emit dataChanged(index(row, ReadColumn), index(row, HasBeenOpened), QVector<int>() << ReadColumnRole << CurrentPageRole << HasBeenOpenedRole);
898 }
899
resetComicRating(const QModelIndex & mi)900 void ComicModel::resetComicRating(const QModelIndex &mi)
901 {
902 ComicDB comic = getComic(mi);
903 QString connectionName = "";
904 {
905 QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
906
907 comic.info.rating = 0;
908 _data[mi.row()]->setData(ComicModel::Rating, 0);
909 DBHelper::update(&(comic.info), db);
910
911 emit dataChanged(mi, mi);
912 connectionName = db.connectionName();
913 }
914 QSqlDatabase::removeDatabase(connectionName);
915 }
916
getCoverUrlPathForComicHash(const QString & hash) const917 QUrl ComicModel::getCoverUrlPathForComicHash(const QString &hash) const
918 {
919 return QUrl("file:" + _databasePath + "/covers/" + hash + ".jpg");
920 }
921
addComicsToFavorites(const QList<qulonglong> & comicIds)922 void ComicModel::addComicsToFavorites(const QList<qulonglong> &comicIds)
923 {
924 addComicsToFavorites(getIndexesFromIds(comicIds));
925 }
926
addComicsToFavorites(const QList<QModelIndex> & comicsList)927 void ComicModel::addComicsToFavorites(const QList<QModelIndex> &comicsList)
928 {
929 QList<ComicDB> comics = getComics(comicsList);
930 QString connectionName = "";
931 {
932 QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
933
934 DBHelper::insertComicsInFavorites(comics, db);
935 connectionName = db.connectionName();
936 }
937 QSqlDatabase::removeDatabase(connectionName);
938 }
939
addComicsToLabel(const QList<qulonglong> & comicIds,qulonglong labelId)940 void ComicModel::addComicsToLabel(const QList<qulonglong> &comicIds, qulonglong labelId)
941 {
942 addComicsToLabel(getIndexesFromIds(comicIds), labelId);
943 }
944
addComicsToLabel(const QList<QModelIndex> & comicsList,qulonglong labelId)945 void ComicModel::addComicsToLabel(const QList<QModelIndex> &comicsList, qulonglong labelId)
946 {
947 QList<ComicDB> comics = getComics(comicsList);
948 QString connectionName = "";
949 {
950 QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
951
952 DBHelper::insertComicsInLabel(comics, labelId, db);
953 connectionName = db.connectionName();
954 }
955 QSqlDatabase::removeDatabase(connectionName);
956 }
957
addComicsToReadingList(const QList<qulonglong> & comicIds,qulonglong readingListId)958 void ComicModel::addComicsToReadingList(const QList<qulonglong> &comicIds, qulonglong readingListId)
959 {
960 addComicsToReadingList(getIndexesFromIds(comicIds), readingListId);
961 }
962
addComicsToReadingList(const QList<QModelIndex> & comicsList,qulonglong readingListId)963 void ComicModel::addComicsToReadingList(const QList<QModelIndex> &comicsList, qulonglong readingListId)
964 {
965 QList<ComicDB> comics = getComics(comicsList);
966 QString connectionName = "";
967 {
968 QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
969
970 DBHelper::insertComicsInReadingList(comics, readingListId, db);
971 connectionName = db.connectionName();
972 }
973 QSqlDatabase::removeDatabase(connectionName);
974 }
975
deleteComicsFromFavorites(const QList<QModelIndex> & comicsList)976 void ComicModel::deleteComicsFromFavorites(const QList<QModelIndex> &comicsList)
977 {
978 QList<ComicDB> comics = getComics(comicsList);
979 QString connectionName = "";
980 {
981 QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
982
983 DBHelper::deleteComicsFromFavorites(comics, db);
984 connectionName = db.connectionName();
985 }
986 QSqlDatabase::removeDatabase(connectionName);
987
988 if (mode == Favorites)
989 deleteComicsFromModel(comicsList);
990 }
991
deleteComicsFromReading(const QList<QModelIndex> & comicsList)992 void ComicModel::deleteComicsFromReading(const QList<QModelIndex> &comicsList)
993 {
994 QList<ComicDB> comics = getComics(comicsList);
995 QString connectionName = "";
996 {
997 QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
998
999 DBHelper::deleteComicsFromReading(comics, db);
1000 connectionName = db.connectionName();
1001 }
1002 QSqlDatabase::removeDatabase(connectionName);
1003
1004 if (mode == Reading)
1005 deleteComicsFromModel(comicsList);
1006 }
1007
deleteComicsFromSpecialList(const QList<QModelIndex> & comicsList,qulonglong specialListId)1008 void ComicModel::deleteComicsFromSpecialList(const QList<QModelIndex> &comicsList, qulonglong specialListId)
1009 {
1010 auto type = (ReadingListModel::TypeSpecialList)specialListId;
1011
1012 switch (type) {
1013 case ReadingListModel::TypeSpecialList::Reading:
1014 deleteComicsFromReading(comicsList);
1015 break;
1016 case ReadingListModel::TypeSpecialList::Favorites:
1017 deleteComicsFromFavorites(comicsList);
1018 break;
1019 }
1020 }
1021
deleteComicsFromLabel(const QList<QModelIndex> & comicsList,qulonglong labelId)1022 void ComicModel::deleteComicsFromLabel(const QList<QModelIndex> &comicsList, qulonglong labelId)
1023 {
1024 QList<ComicDB> comics = getComics(comicsList);
1025 QString connectionName = "";
1026 {
1027 QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
1028
1029 DBHelper::deleteComicsFromLabel(comics, labelId, db);
1030 connectionName = db.connectionName();
1031 }
1032 QSqlDatabase::removeDatabase(connectionName);
1033
1034 deleteComicsFromModel(comicsList);
1035 }
1036
deleteComicsFromReadingList(const QList<QModelIndex> & comicsList,qulonglong readingListId)1037 void ComicModel::deleteComicsFromReadingList(const QList<QModelIndex> &comicsList, qulonglong readingListId)
1038 {
1039 QList<ComicDB> comics = getComics(comicsList);
1040 QString connectionName = "";
1041 {
1042 QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
1043
1044 DBHelper::deleteComicsFromReadingList(comics, readingListId, db);
1045 connectionName = db.connectionName();
1046 }
1047 QSqlDatabase::removeDatabase(connectionName);
1048
1049 deleteComicsFromModel(comicsList);
1050 }
1051
deleteComicsFromModel(const QList<QModelIndex> & comicsList)1052 void ComicModel::deleteComicsFromModel(const QList<QModelIndex> &comicsList)
1053 {
1054 QListIterator<QModelIndex> it(comicsList);
1055 it.toBack();
1056 while (it.hasPrevious()) {
1057 int row = it.previous().row();
1058 beginRemoveRows(QModelIndex(), row, row);
1059 _data.removeAt(row);
1060 endRemoveRows();
1061 }
1062
1063 if (_data.isEmpty())
1064 emit isEmpty();
1065 }
1066
isFavorite(const QModelIndex & index)1067 bool ComicModel::isFavorite(const QModelIndex &index)
1068 {
1069 bool isFavorite;
1070 QString connectionName = "";
1071 {
1072 QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
1073
1074 isFavorite = DBHelper::isFavoriteComic(_data[index.row()]->data(Id).toLongLong(), db);
1075 connectionName = db.connectionName();
1076 }
1077 QSqlDatabase::removeDatabase(connectionName);
1078
1079 return isFavorite;
1080 }
1081
updateRating(int rating,QModelIndex mi)1082 void ComicModel::updateRating(int rating, QModelIndex mi)
1083 {
1084 ComicDB comic = getComic(mi);
1085 QString connectionName = "";
1086 {
1087 QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
1088 //TODO optimize update
1089
1090 comic.info.rating = rating;
1091 _data[mi.row()]->setData(ComicModel::Rating, rating);
1092 DBHelper::update(&(comic.info), db);
1093
1094 emit dataChanged(mi, mi);
1095 connectionName = db.connectionName();
1096 }
1097 QSqlDatabase::removeDatabase(connectionName);
1098 }
1099