Merge pull request #381 from YACReader/new_metadata

feature: New metadata support
This commit is contained in:
Luis Ángel San Martín 2023-05-22 22:27:01 +02:00 committed by GitHub
commit b45fabe057
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
55 changed files with 2334 additions and 974 deletions

View File

@ -6,6 +6,12 @@ Version counting is based on semantic versioning (Major.Feature.Patch)
### YACReaderLibrary
* Avoid showing stale information in the server config dialog by updating the connection information when the dialog is opened.
* Add new metadata support, it improves compatibility with ComicInfo.xml
* Add support for showing a "recently added/updated" indicator.
* Improved comic metadata dialog.
* Add textual tags support that can be queried through the search engine.
* Make = in the search engine work as : does.
* Add new operators to the search engine: exact match ==, <, >, <=, >=.
## 9.12

View File

@ -1575,7 +1575,7 @@ void MainWindowViewer::sendComic()
auto client = new YACReaderLocalClient;
connect(client, &YACReaderLocalClient::finished, client, &YACReaderLocalClient::deleteLater);
currentComicDB.info.lastTimeOpened = QDateTime::currentMSecsSinceEpoch() / 1000;
currentComicDB.info.lastTimeOpened = QDateTime::currentSecsSinceEpoch();
viewer->updateComic(currentComicDB);

View File

@ -85,6 +85,7 @@ HEADERS += comic_flow.h \
library_creator.h \
library_window.h \
add_library_dialog.h \
recent_visibility_coordinator.h \
rename_library_dialog.h \
properties_dialog.h \
options_dialog.h \
@ -171,6 +172,7 @@ SOURCES += comic_flow.cpp \
library_window.cpp \
main.cpp \
add_library_dialog.cpp \
recent_visibility_coordinator.cpp \
rename_library_dialog.cpp \
properties_dialog.cpp \
options_dialog.cpp \

View File

@ -90,7 +90,7 @@ ClassicComicsView::ClassicComicsView(QWidget *parent)
hideFlowViewAction->setText(tr("Hide comic flow"));
hideFlowViewAction->setData(HIDE_COMIC_VIEW_ACTION_YL);
hideFlowViewAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(HIDE_COMIC_VIEW_ACTION_YL));
hideFlowViewAction->setIcon(QIcon(":/images/comics_view_toolbar/hideComicFlow.png"));
hideFlowViewAction->setIcon(QIcon(":/images/comics_view_toolbar/hideComicFlow.svg"));
hideFlowViewAction->setCheckable(true);
hideFlowViewAction->setChecked(false);
@ -120,9 +120,7 @@ void ClassicComicsView::setToolBar(QToolBar *toolBar)
static_cast<QVBoxLayout *>(comics->layout())->insertWidget(0, toolBar);
this->toolbar = toolBar;
toolBarStretch = new YACReaderToolBarStretch(this);
toolBarStretchAction = toolBar->addWidget(toolBarStretch);
startSeparatorAction = toolBar->addSeparator();
toolBar->addAction(hideFlowViewAction);
}
@ -343,7 +341,7 @@ void ClassicComicsView::removeItemsFromFlow(const QModelIndex &parent, int from,
void ClassicComicsView::closeEvent(QCloseEvent *event)
{
toolbar->removeAction(toolBarStretchAction);
toolbar->removeAction(startSeparatorAction);
toolbar->removeAction(hideFlowViewAction);
saveTableHeadersStatus();

View File

@ -54,8 +54,6 @@ protected slots:
private:
YACReaderTableView *tableView;
YACReaderToolBarStretch *toolBarStretch;
QAction *toolBarStretchAction;
QToolBar *toolbar;
QWidget *comics;
QSplitter *sVertical;
@ -63,6 +61,7 @@ private:
QSettings *settings;
void closeEvent(QCloseEvent *event) override;
QAction *hideFlowViewAction;
QAction *startSeparatorAction;
QStackedWidget *stack;

View File

@ -1,5 +1,6 @@
HEADERS += \
$$PWD/comic_vine_json_parser.h \
comic_vine/comic_vine_dialog.h \
comic_vine/comic_vine_client.h \
comic_vine/scraper_lineedit.h \
@ -24,6 +25,7 @@ HEADERS += \
$$PWD/comic_vine_all_volume_comics_retriever.h
SOURCES += \
$$PWD/comic_vine_json_parser.cpp \
comic_vine/comic_vine_dialog.cpp \
comic_vine/comic_vine_client.cpp \
comic_vine/scraper_lineedit.cpp \

View File

@ -10,12 +10,11 @@
#include <QtWidgets>
#include <QtConcurrent/QtConcurrentRun>
#include "data_base_management.h"
#include <QJsonDocument>
#include <QJsonParseError>
#include <QSqlDatabase>
#include "yacreader_busy_widget.h"
#include "comic_vine_client.h"
#include "comic_vine_json_parser.h"
#include "scraper_lineedit.h"
#include "title_header.h"
#include "series_question.h"
@ -461,7 +460,7 @@ void ComicVineDialog::getComicsInfo(QList<QPair<ComicDB, QString>> matchingInfo,
QByteArray result = comicVineClient->getComicDetail(p.second, error, timeout); // TODO check timeOut or Connection error
if (error || timeout)
continue; // TODO
ComicDB comic = parseComicInfo(p.first, result, count, publisher); // TODO check result error
ComicDB comic = YACReader::parseCVJSONComicInfo(p.first, result, count, publisher); // TODO check result error
comic.info.comicVineID = p.second;
comics.push_back(comic);
@ -489,7 +488,7 @@ void ComicVineDialog::getComicInfo(const QString &comicId, int count, const QStr
}
}
ComicDB comic = parseComicInfo(comics[currentIndex], result, count, publisher); // TODO check result error
ComicDB comic = YACReader::parseCVJSONComicInfo(comics[currentIndex], result, count, publisher); // TODO check result error
comic.info.comicVineID = comicId;
setLoadingMessage(tr("Retrieving tags for : %1").arg(comics[currentIndex].getFileName()));
QString connectionName = "";
@ -512,196 +511,6 @@ void ComicVineDialog::getComicInfo(const QString &comicId, int count, const QStr
}
}
ComicDB ComicVineDialog::parseComicInfo(ComicDB &comic, const QString &json, int count, const QString &publisher)
{
QJsonParseError Err;
QVariantMap sc = QJsonDocument::fromJson(json.toUtf8(), &Err).toVariant().toMap();
if (Err.error != QJsonParseError::NoError) {
qDebug("Error detected");
return comic;
}
int numResults = sc.value("number_of_total_results").toInt(); // fix to weird behaviour using hasNext
if (numResults > 0) {
QVariantMap result = sc.value("results").toMap();
comic.info.title = result.value("name");
comic.info.number = result.value("issue_number");
comic.info.volume = result.value("volume").toMap().value("name");
if (result.contains("person_credits") && !result.value("person_credits").isNull()) {
auto authors = getAuthors(result.value("person_credits"));
QString writer = authors.values("writer").join("\n");
QString penciller = authors.values("penciller").join("\n");
QString inker = authors.values("inker").join("\n");
QString colorist = authors.values("colorist").join("\n");
QString letterer = authors.values("letterer").join("\n");
QString coverArtist = authors.values("cover").join("\n");
comic.info.writer = writer;
comic.info.penciller = penciller;
comic.info.inker = inker;
comic.info.colorist = colorist;
comic.info.letterer = letterer;
comic.info.coverArtist = coverArtist;
}
if (result.contains("cover_date") && !result.value("cover_date").isNull()) {
QString date = result.value("cover_date").toString();
QStringList tempList = date.split("-");
if (tempList.length() == 3) {
std::reverse(tempList.begin(), tempList.end());
comic.info.date = tempList.join("/");
}
}
if (result.contains("description") && !result.value("description").isNull()) {
comic.info.synopsis = result.value("description");
}
if (result.contains("character_credits") && !result.value("character_credits").isNull()) {
comic.info.characters = getCharacters(result.value("character_credits"));
}
if (result.contains("story_arc_credits") && !result.value("story_arc_credits").isNull()) {
QPair<QString, QString> storyArcIdAndName = getFirstStoryArcIdAndName(result.value("story_arc_credits"));
QString storyArcId = storyArcIdAndName.first;
QString storyArcName = storyArcIdAndName.second;
if (!storyArcId.isNull()) {
QString comicId = result.value("id").toString();
QPair<QString, QString> arcNumberAndArcCount = getArcNumberAndArcCount(storyArcId, comicId);
if (!arcNumberAndArcCount.first.isNull()) {
QString arcNumber = arcNumberAndArcCount.first;
QString arcCount = arcNumberAndArcCount.second;
comic.info.storyArc = storyArcName;
comic.info.arcNumber = arcNumber;
comic.info.arcCount = arcCount;
}
}
}
comic.info.count = count;
comic.info.publisher = publisher;
comic.info.edited = true;
}
return comic;
}
QString ComicVineDialog::getCharacters(const QVariant &json_characters)
{
QStringList characters;
QListIterator<QVariant> it(json_characters.toList());
QVariantMap resultsValue;
while (it.hasNext()) {
resultsValue = it.next().toMap();
characters << resultsValue.value("name").toString();
}
return (characters.isEmpty()) ? "" : (characters.join("\n") + "\n");
}
QMultiMap<QString, QString> ComicVineDialog::getAuthors(const QVariant &json_authors)
{
QMultiMap<QString, QString> authors;
QListIterator<QVariant> it(json_authors.toList());
QVariantMap resultsValue;
while (it.hasNext()) {
resultsValue = it.next().toMap();
QString authorName = resultsValue.value("name").toString();
QStringList roles = resultsValue.value("role").toString().split(",");
foreach (QString role, roles) {
if (role.trimmed() == "writer")
authors.insert("writer", authorName);
else if (role.trimmed() == "inker")
authors.insert("inker", authorName);
else if (role.trimmed() == "penciler" || role.trimmed() == "penciller")
authors.insert("penciller", authorName);
else if (role.trimmed() == "colorist")
authors.insert("colorist", authorName);
else if (role.trimmed() == "letterer")
authors.insert("letterer", authorName);
else if (role.trimmed() == "cover")
authors.insert("cover", authorName);
}
}
return authors;
}
QPair<QString, QString> ComicVineDialog::getFirstStoryArcIdAndName(const QVariant &json_story_arcs)
{
QString story_arc_id = QString();
QString story_arc_name = QString();
QListIterator<QVariant> it(json_story_arcs.toList());
QVariantMap resultsValue;
while (it.hasNext()) {
resultsValue = it.next().toMap();
story_arc_id = resultsValue.value("id").toString();
story_arc_name = resultsValue.value("name").toString();
break;
}
return qMakePair(story_arc_id, story_arc_name);
}
QPair<QString, QString> ComicVineDialog::getArcNumberAndArcCount(const QString &storyArcId, const QString &comicId)
{
auto comicVineClient = new ComicVineClient;
bool error;
bool timeout;
QByteArray result = comicVineClient->getStoryArcDetail(storyArcId, error, timeout);
if (error || timeout)
return qMakePair(QString(), QString());
QString json = result;
QJsonParseError Err;
QVariantMap sc = QJsonDocument::fromJson(json.toUtf8(), &Err).toVariant().toMap();
if (Err.error != QJsonParseError::NoError) {
qDebug("Error detected");
return qMakePair(QString(), QString());
}
int numResults = sc.value("number_of_total_results").toInt(); // fix to weird behaviour using hasNext
if (numResults > 0) {
QVariantMap result = sc.value("results").toMap();
if (result.contains("issues")) {
QListIterator<QVariant> it(result.value("issues").toList());
int arcNumber = 0;
int arcCount = 0;
QVariantMap resultsValue;
while (it.hasNext()) {
resultsValue = it.next().toMap();
if (comicId == resultsValue.value("id").toString()) {
arcNumber = arcCount + 1;
}
arcCount++;
}
return qMakePair(QString::number(arcNumber), QString::number(arcCount));
}
return qMakePair(QString(), QString());
}
return qMakePair(QString(), QString());
}
void ComicVineDialog::toggleSkipButton()
{
if (mode == SingleComicInList)

View File

@ -58,16 +58,11 @@ protected slots:
void showSelectComic(const QString &json);
void showSortVolumeComics(const QString &json);
void queryTimeOut();
ComicDB parseComicInfo(ComicDB &comic, const QString &json, int count, const QString &publisher);
void setLoadingMessage(const QString &message);
void goToNextComic();
private:
void clearState();
QString getCharacters(const QVariant &json_characters);
QMultiMap<QString, QString> getAuthors(const QVariant &json_authors);
QPair<QString, QString> getFirstStoryArcIdAndName(const QVariant &json_story_arcs);
QPair<QString, QString> getArcNumberAndArcCount(const QString &storyArcId, const QString &comicId);
void toggleSkipButton();

View File

@ -0,0 +1,233 @@
#include "comic_vine_json_parser.h"
#include "comic_vine_client.h"
#include <QJsonDocument>
#include <QJsonParseError>
QString getCharacters(const QVariant &json_characters);
QMultiMap<QString, QString> getAuthors(const QVariant &json_authors);
QPair<QString, QString> getFirstStoryArcIdAndName(const QVariant &json_story_arcs);
QPair<QString, QString> getArcNumberAndArcCount(const QString &storyArcId, const QString &comicId);
QList<QString> getNamesFromList(const QVariant &json_list);
ComicDB YACReader::parseCVJSONComicInfo(ComicDB &comic, const QString &json, int count, const QString &publisher)
{
QJsonParseError Err;
QVariantMap sc = QJsonDocument::fromJson(json.toUtf8(), &Err).toVariant().toMap();
if (Err.error != QJsonParseError::NoError) {
qDebug("Error detected");
return comic;
}
int numResults = sc.value("number_of_total_results").toInt(); // fix to weird behaviour using hasNext
if (numResults > 0) {
QVariantMap result = sc.value("results").toMap();
comic.info.title = result.value("name");
comic.info.number = result.value("issue_number");
// changed in 9.13, volume actually means series in ComicVine
comic.info.series = result.value("volume").toMap().value("name");
if (result.contains("person_credits") && !result.value("person_credits").isNull()) {
auto authors = getAuthors(result.value("person_credits"));
QString writer = authors.values("writer").join("\n");
QString penciller = authors.values("penciller").join("\n");
QString inker = authors.values("inker").join("\n");
QString colorist = authors.values("colorist").join("\n");
QString letterer = authors.values("letterer").join("\n");
QString coverArtist = authors.values("cover").join("\n");
comic.info.writer = writer;
comic.info.penciller = penciller;
comic.info.inker = inker;
comic.info.colorist = colorist;
comic.info.letterer = letterer;
comic.info.coverArtist = coverArtist;
}
if (result.contains("cover_date") && !result.value("cover_date").isNull()) {
QString date = result.value("cover_date").toString();
QStringList tempList = date.split("-");
if (tempList.length() == 3) {
std::reverse(tempList.begin(), tempList.end());
comic.info.date = tempList.join("/");
}
}
if (result.contains("description") && !result.value("description").isNull()) {
comic.info.synopsis = result.value("description");
}
if (result.contains("character_credits") && !result.value("character_credits").isNull()) {
comic.info.characters = getCharacters(result.value("character_credits"));
}
if (result.contains("story_arc_credits") && !result.value("story_arc_credits").isNull()) {
QPair<QString, QString> storyArcIdAndName = getFirstStoryArcIdAndName(result.value("story_arc_credits"));
QString storyArcId = storyArcIdAndName.first;
QString storyArcName = storyArcIdAndName.second;
if (!storyArcId.isNull()) {
QString comicId = result.value("id").toString();
QPair<QString, QString> arcNumberAndArcCount = getArcNumberAndArcCount(storyArcId, comicId);
if (!arcNumberAndArcCount.first.isNull()) {
QString arcNumber = arcNumberAndArcCount.first;
QString arcCount = arcNumberAndArcCount.second;
comic.info.storyArc = storyArcName;
comic.info.arcNumber = arcNumber;
comic.info.arcCount = arcCount;
}
}
}
if (result.contains("location_credits") && !result.value("location_credits").isNull()) {
comic.info.locations = getNamesFromList(result.value("location_credits")).join("\n");
}
if (result.contains("team_credits") && !result.value("team_credits").isNull()) {
comic.info.teams = getNamesFromList(result.value("team_credits")).join("\n");
}
if (result.contains("character_credits") && !result.value("character_credits").isNull()) {
comic.info.characters = getNamesFromList(result.value("character_credits")).join("\n");
}
comic.info.count = count;
comic.info.publisher = publisher;
comic.info.edited = true;
}
return comic;
}
QString getCharacters(const QVariant &json_characters)
{
QStringList characters;
QListIterator<QVariant> it(json_characters.toList());
QVariantMap resultsValue;
while (it.hasNext()) {
resultsValue = it.next().toMap();
characters << resultsValue.value("name").toString();
}
return (characters.isEmpty()) ? "" : (characters.join("\n") + "\n");
}
QMultiMap<QString, QString> getAuthors(const QVariant &json_authors)
{
QMultiMap<QString, QString> authors;
QListIterator<QVariant> it(json_authors.toList());
QVariantMap resultsValue;
while (it.hasNext()) {
resultsValue = it.next().toMap();
QString authorName = resultsValue.value("name").toString();
QStringList roles = resultsValue.value("role").toString().split(",");
foreach (QString role, roles) {
if (role.trimmed() == "writer")
authors.insert("writer", authorName);
else if (role.trimmed() == "inker")
authors.insert("inker", authorName);
else if (role.trimmed() == "penciler" || role.trimmed() == "penciller")
authors.insert("penciller", authorName);
else if (role.trimmed() == "colorist")
authors.insert("colorist", authorName);
else if (role.trimmed() == "letterer")
authors.insert("letterer", authorName);
else if (role.trimmed() == "cover")
authors.insert("cover", authorName);
}
}
return authors;
}
QPair<QString, QString> getFirstStoryArcIdAndName(const QVariant &json_story_arcs)
{
QString story_arc_id = QString();
QString story_arc_name = QString();
QListIterator<QVariant> it(json_story_arcs.toList());
QVariantMap resultsValue;
while (it.hasNext()) {
resultsValue = it.next().toMap();
story_arc_id = resultsValue.value("id").toString();
story_arc_name = resultsValue.value("name").toString();
break;
}
return qMakePair(story_arc_id, story_arc_name);
}
QPair<QString, QString> getArcNumberAndArcCount(const QString &storyArcId, const QString &comicId)
{
auto comicVineClient = new ComicVineClient;
bool error;
bool timeout;
QByteArray result = comicVineClient->getStoryArcDetail(storyArcId, error, timeout);
if (error || timeout)
return qMakePair(QString(), QString());
QString json = result;
QJsonParseError Err;
QVariantMap sc = QJsonDocument::fromJson(json.toUtf8(), &Err).toVariant().toMap();
if (Err.error != QJsonParseError::NoError) {
qDebug("Error detected");
return qMakePair(QString(), QString());
}
int numResults = sc.value("number_of_total_results").toInt(); // fix to weird behaviour using hasNext
if (numResults > 0) {
QVariantMap result = sc.value("results").toMap();
if (result.contains("issues")) {
QListIterator<QVariant> it(result.value("issues").toList());
int arcNumber = 0;
int arcCount = 0;
QVariantMap resultsValue;
while (it.hasNext()) {
resultsValue = it.next().toMap();
if (comicId == resultsValue.value("id").toString()) {
arcNumber = arcCount + 1;
}
arcCount++;
}
return qMakePair(QString::number(arcNumber), QString::number(arcCount));
}
return qMakePair(QString(), QString());
}
return qMakePair(QString(), QString());
}
QList<QString> getNamesFromList(const QVariant &json_list)
{
QList<QString> names;
QListIterator<QVariant> it(json_list.toList());
QVariantMap resultsValue;
while (it.hasNext()) {
resultsValue = it.next().toMap();
QString name = resultsValue.value("name").toString();
names.append(name);
}
return names;
}

View File

@ -0,0 +1,13 @@
#ifndef COMIC_VINE_JSON_PARSER_H
#define COMIC_VINE_JSON_PARSER_H
#include "comic_db.h"
namespace YACReader {
ComicDB parseCVJSONComicInfo(ComicDB &comic, const QString &json, int count, const QString &publisher);
}
#endif // COMIC_VINE_JSON_PARSER_H

View File

@ -17,12 +17,13 @@
#include "QsLog.h"
ComicModel::ComicModel(QObject *parent)
: QAbstractItemModel(parent)
: QAbstractItemModel(parent), showRecent(false), recentDays(1)
{
}
ComicModel::ComicModel(QSqlQuery &sqlquery, QObject *parent)
: QAbstractItemModel(parent)
: QAbstractItemModel(parent), showRecent(false), recentDays(1)
{
setupModelData(sqlquery);
}
@ -239,6 +240,10 @@ QHash<int, QByteArray> ComicModel::roleNames() const
roles[CoverPathRole] = "cover_path";
roles[PublicationDate] = "date";
roles[ReadableTitle] = "readable_title";
roles[AddedRole] = "added_date";
roles[TypeRole] = "type";
roles[ShowRecentRole] = "show_recent";
roles[RecentRangeRole] = "recent_range";
return roles;
}
@ -301,6 +306,14 @@ QVariant ComicModel::data(const QModelIndex &index, int role) const
return item->data(Id);
else if (role == PublicationDateRole)
return QVariant(localizedDate(item->data(PublicationDate).toString()));
else if (role == AddedRole)
return item->data(Added);
else if (role == TypeRole)
return item->data(Type);
else if (role == ShowRecentRole)
return showRecent;
else if (role == ComicModel::RecentRangeRole)
return recentDays * 86400;
if (role != Qt::DisplayRole)
return QVariant();
@ -443,6 +456,8 @@ QStringList ComicModel::getPaths(const QString &_source)
return paths;
}
#define COMIC_MODEL_QUERY_FIELDS "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,ci.date,ci.added,ci.type"
void ComicModel::setupFolderModelData(unsigned long long int folderId, const QString &databasePath)
{
enableResorting = false;
@ -458,7 +473,7 @@ void ComicModel::setupFolderModelData(unsigned long long int folderId, const QSt
{
QSqlDatabase db = DataBaseManagement::loadDatabase(databasePath);
QSqlQuery selectQuery(db);
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,ci.date "
selectQuery.prepare("SELECT " COMIC_MODEL_QUERY_FIELDS " "
"FROM comic c INNER JOIN comic_info ci ON (c.comicInfoId = ci.id) "
"WHERE c.parentId = :parentId");
selectQuery.bindValue(":parentId", folderId);
@ -485,7 +500,7 @@ void ComicModel::setupLabelModelData(unsigned long long parentLabel, const QStri
{
QSqlDatabase db = DataBaseManagement::loadDatabase(databasePath);
QSqlQuery selectQuery(db);
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,ci.date "
selectQuery.prepare("SELECT " COMIC_MODEL_QUERY_FIELDS " "
"FROM comic c INNER JOIN comic_info ci ON (c.comicInfoId = ci.id) "
"INNER JOIN comic_label cl ON (c.id == cl.comic_id) "
"WHERE cl.label_id = :parentLabelId "
@ -529,7 +544,7 @@ void ComicModel::setupReadingListModelData(unsigned long long parentReadingList,
foreach (qulonglong id, ids) {
QSqlQuery selectQuery(db);
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,ci.date "
selectQuery.prepare("SELECT " COMIC_MODEL_QUERY_FIELDS " "
"FROM comic c INNER JOIN comic_info ci ON (c.comicInfoId = ci.id) "
"INNER JOIN comic_reading_list crl ON (c.id == crl.comic_id) "
"WHERE crl.reading_list_id = :parentReadingList "
@ -566,7 +581,7 @@ void ComicModel::setupFavoritesModelData(const QString &databasePath)
{
QSqlDatabase db = DataBaseManagement::loadDatabase(databasePath);
QSqlQuery selectQuery(db);
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,ci.date "
selectQuery.prepare("SELECT " COMIC_MODEL_QUERY_FIELDS " "
"FROM comic c INNER JOIN comic_info ci ON (c.comicInfoId = ci.id) "
"INNER JOIN comic_default_reading_list cdrl ON (c.id == cdrl.comic_id) "
"WHERE cdrl.default_reading_list_id = :parentDefaultListId "
@ -595,7 +610,7 @@ void ComicModel::setupReadingModelData(const QString &databasePath)
{
QSqlDatabase db = DataBaseManagement::loadDatabase(databasePath);
QSqlQuery selectQuery(db);
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,ci.date "
selectQuery.prepare("SELECT " COMIC_MODEL_QUERY_FIELDS " "
"FROM comic c INNER JOIN comic_info ci ON (c.comicInfoId = ci.id) "
"WHERE ci.hasBeenOpened = 1 AND ci.read = 0 "
"ORDER BY ci.lastTimeOpened DESC");
@ -652,7 +667,7 @@ void ComicModel::setupModelData(QSqlQuery &sqlquery)
return naturalSortLessThanCI(c1->data(ComicModel::FileName).toString(), c2->data(ComicModel::FileName).toString());
} else {
if (c1->data(ComicModel::Number).isNull() == false && c2->data(ComicModel::Number).isNull() == false) {
return c1->data(ComicModel::Number).toInt() < c2->data(ComicModel::Number).toInt();
return naturalSortLessThanCI(c1->data(ComicModel::Number).toString(), c2->data(ComicModel::Number).toString());
} else {
return c2->data(ComicModel::Number).isNull();
}
@ -794,7 +809,7 @@ QVector<YACReaderComicReadStatus> ComicModel::setComicsRead(QList<QModelIndex> l
return getReadList();
}
void ComicModel::setComicsManga(QList<QModelIndex> list, bool isManga)
void ComicModel::setComicsType(QList<QModelIndex> list, FileType type)
{
QString connectionName = "";
{
@ -803,7 +818,7 @@ void ComicModel::setComicsManga(QList<QModelIndex> list, bool isManga)
foreach (QModelIndex mi, list) {
bool found;
ComicDB c = DBHelper::loadComic(_data.value(mi.row())->data(ComicModel::Id).toULongLong(), db, found);
c.info.manga = isManga;
c.info.type = QVariant::fromValue(type);
DBHelper::update(&(c.info), db);
}
db.commit();
@ -1122,6 +1137,26 @@ bool ComicModel::isFavorite(const QModelIndex &index)
return isFavorite;
}
void ComicModel::setShowRecent(bool showRecent)
{
if (this->showRecent == showRecent)
return;
this->showRecent = showRecent;
emit dataChanged(index(0, 0), index(rowCount() - 1, 0), { ComicModel::ShowRecentRole });
}
void ComicModel::setRecentRange(int days)
{
if (this->recentDays == days)
return;
this->recentDays = days;
emit dataChanged(index(0, 0), index(rowCount() - 1, 0), { ComicModel::RecentRangeRole });
}
void ComicModel::updateRating(int rating, QModelIndex mi)
{
ComicDB comic = getComic(mi);

View File

@ -31,11 +31,13 @@ public:
Path = 6,
Hash = 7,
ReadColumn = 8,
IsBis = 9,
IsBis = 9, // TODO_METADATA: Remove this column
CurrentPage = 10,
Rating = 11,
HasBeenOpened = 12,
PublicationDate = 13,
Added = 14,
Type = 15,
};
enum Roles {
@ -55,6 +57,10 @@ public:
CoverPathRole,
PublicationDateRole,
ReadableTitle,
AddedRole,
TypeRole,
ShowRecentRole,
RecentRangeRole,
};
enum Mode {
@ -107,7 +113,7 @@ public:
// setComicInfoForAllComics(); --> inserta la información común a todos los cómics de una sola vez.
// setComicInfoForSelectedComis(QList<QModelIndex> list); -->inserta la información común para los comics seleccionados
QVector<YACReaderComicReadStatus> setComicsRead(QList<QModelIndex> list, YACReaderComicReadStatus read);
void setComicsManga(QList<QModelIndex> list, bool isManga);
void setComicsType(QList<QModelIndex> list, FileType type);
qint64 asignNumbers(QList<QModelIndex> list, int startingNumber);
// void remove(ComicDB * comic, int row);
void removeInTransaction(int row);
@ -135,6 +141,9 @@ public:
ComicModel::Mode getMode() { return mode; }
unsigned long long int getSourceId() { return sourceId; }
void setShowRecent(bool visible);
void setRecentRange(int days);
QHash<int, QByteArray> roleNames() const override;
public slots:
@ -164,6 +173,9 @@ private:
qulonglong sourceId;
QString localizedDate(const QString &dbDate) const;
bool showRecent;
qlonglong recentDays;
signals:
void isEmpty();
void searchNumResults(int);

View File

@ -64,23 +64,29 @@ static QString fields = "title,"
//"coverSizeRatio," cover may have changed since the info was exported...
//"originalCoverSize," // h/w
// new 9.8 fields
"manga";
"manga,"
// new 9.13 fields
"added,"
"type,"
"editor,"
"imprint,"
"teams,"
"locations,"
"series,"
"alternateSeries,"
"alternateNumber,"
"alternateCount,"
"languageISO,"
"seriesGroup,"
"mainCharacterOrTeam,"
"review,"
"tags";
DataBaseManagement::DataBaseManagement()
: QObject(), dataBasesList()
{
}
/*TreeModel * DataBaseManagement::newTreeModel(QString path)
{
//la consulta se ejecuta...
QSqlQuery selectQuery(loadDatabase(path));
selectQuery.setForwardOnly(true);
selectQuery.exec("select * from folder order by parentId,name");
//selectQuery.finish();
return new TreeModel(selectQuery);
}*/
QSqlDatabase DataBaseManagement::createDatabase(QString name, QString path)
{
return createDatabase(QDir::cleanPath(path) + "/" + name + ".ydb");
@ -102,12 +108,12 @@ QSqlDatabase DataBaseManagement::createDatabase(QString dest)
// pragma.finish();
DataBaseManagement::createTables(db);
QSqlQuery query("INSERT INTO folder (parentId, name, path) "
"VALUES (1,'root', '/')",
db);
QSqlQuery insertRootQuery(db);
insertRootQuery.prepare("INSERT INTO folder (parentId, name, path, added) "
"VALUES (1, 'root', '/', :added)");
insertRootQuery.bindValue(":added", QDateTime::currentSecsSinceEpoch());
insertRootQuery.exec();
}
// query.finish();
// db.close();
return db;
}
@ -156,13 +162,13 @@ bool DataBaseManagement::createTables(QSqlDatabase &database)
"coverPage INTEGER DEFAULT 1,"
"numPages INTEGER,"
"number INTEGER,"
"number TEXT," // changed to text from INTEGER (9.13)
"isBis BOOLEAN,"
"count INTEGER,"
"volume TEXT,"
"storyArc TEXT,"
"arcNumber INTEGER,"
"arcNumber TEXT," // changed to text from INTEGER (9.13)
"arcCount INTEGER,"
"genere TEXT,"
@ -174,11 +180,11 @@ bool DataBaseManagement::createTables(QSqlDatabase &database)
"letterer TEXT,"
"coverArtist TEXT,"
"date TEXT," // dd/mm/yyyy --> se mostrará en 3 campos diferentes
"date TEXT," // publication date dd/mm/yyyy --> se mostrará en 3 campos diferentes
"publisher TEXT,"
"format TEXT,"
"color BOOLEAN,"
"ageRating BOOLEAN," // this is actually a string (TEXT), funny thing is that the current implementation works
"ageRating TEXT,"
"synopsis TEXT,"
"characters TEXT,"
@ -190,7 +196,7 @@ bool DataBaseManagement::createTables(QSqlDatabase &database)
// new 7.0 fields
"hasBeenOpened BOOLEAN DEFAULT 0,"
"rating INTEGER DEFAULT 0,"
"rating INTEGER DEFAULT 0," // TODO_METADATA change type to REAL with two decimals
"currentPage INTEGER DEFAULT 1, "
"bookmark1 INTEGER DEFAULT -1, "
"bookmark2 INTEGER DEFAULT -1, "
@ -205,8 +211,23 @@ bool DataBaseManagement::createTables(QSqlDatabase &database)
"coverSizeRatio REAL,"
"originalCoverSize STRING," // h/w
// new 9.8 fields
"manga BOOLEAN DEFAULT 0"
"manga BOOLEAN DEFAULT 0," // deprecated 9.13
// new 9.13 fields
"added INTEGER,"
"type INTEGER DEFAULT 0," // 0 = comic, 1 = manga, 2 = manga left to right, 3 = webcomic, 4 = 4koma
"editor TEXT,"
"imprint TEXT,"
"teams TEXT,"
"locations TEXT,"
"series TEXT,"
"alternateSeries TEXT,"
"alternateNumber TEXT,"
"alternateCount INTEGER,"
"languageISO TEXT,"
"seriesGroup TEXT,"
"mainCharacterOrTeam TEXT,"
"review TEXT,"
"tags TEXT"
")");
success = success && queryComicInfo.exec();
// queryComicInfo.finish();
@ -226,7 +247,11 @@ bool DataBaseManagement::createTables(QSqlDatabase &database)
"firstChildHash TEXT,"
"customImage TEXT,"
// new 9.8 fields
"manga BOOLEAN DEFAULT 0,"
"manga BOOLEAN DEFAULT 0," // deprecated 9.13
// new 9.13 fields
"type INTEGER DEFAULT 0," // 0 = comic, 1 = manga, 2 = manga left to right, 3 = webcomic, 4 = 4koma
"added INTEGER,"
"updated INTEGER," // updated when the folder gets new content
"FOREIGN KEY(parentId) REFERENCES folder(id) ON DELETE CASCADE)");
success = success && queryFolder.exec();
@ -460,7 +485,25 @@ bool DataBaseManagement::importComicsInfo(QString source, QString dest)
//--
// new 9.8 fields
"manga = :manga"
"manga = :manga,"
// new 9.13 fields
"added = :added,"
"type = :type," // 0 = comic, 1 = manga, 2 = manga left to right, 3 = webcomic,
"editor = :editor,"
"imprint = :imprint,"
"teams = :teams,"
"locations = :locations,"
"series = :series,"
"alternateSeries = :alternateSeries,"
"alternateNumber = :alternateNumber,"
"alternateCount = :alternateCount,"
"languageISO = :languageISO,"
"seriesGroup = :seriesGroup,"
"mainCharacterOrTeam = :mainCharacterOrTeam,"
"review = :review,"
"tags = :tags"
//--
" WHERE hash = :hash ");
@ -496,6 +539,21 @@ bool DataBaseManagement::importComicsInfo(QString source, QString dest)
"comicVineID,"
"lastTimeOpened,"
"coverSizeRatio,"
"manga,"
"added,"
"type,"
"editor,"
"imprint,"
"teams,"
"locations,"
"series,"
"alternateSeries,"
"alternateNumber,"
"alternateCount,"
"languageISO,"
"seriesGroup,"
"mainCharacterOrTeam,"
"review,"
"hash)"
"VALUES (:title,"
@ -539,6 +597,23 @@ bool DataBaseManagement::importComicsInfo(QString source, QString dest)
":coverSizeRatio,"
":originalCoverSize,"
":manga,"
":added,"
":type,"
":editor,"
":imprint,"
":teams,"
":locations,"
":series,"
":alternateSeries,"
":alternateNumber,"
":alternateCount,"
":languageISO,"
":seriesGroup,"
":mainCharacterOrTeam,"
":review,"
":tags,"
":hash )");
QSqlRecord record = newInfo.record();
@ -596,6 +671,8 @@ bool DataBaseManagement::importComicsInfo(QString source, QString dest)
return b;
}
// TODO: update fields
// TODO fix these bindings
void DataBaseManagement::bindValuesFromRecord(const QSqlRecord &record, QSqlQuery &query)
{
@ -654,6 +731,22 @@ void DataBaseManagement::bindValuesFromRecord(const QSqlRecord &record, QSqlQuer
bindValue("coverSizeRatio", record, query);
bindValue("originalCoverSize", record, query);
bindValue("added", record, query);
bindValue("type", record, query);
bindValue("editor", record, query);
bindValue("imprint", record, query);
bindValue("teams", record, query);
bindValue("locations", record, query);
bindValue("series", record, query);
bindValue("alternateSeries", record, query);
bindValue("alternateNumber", record, query);
bindValue("alternateCount", record, query);
bindValue("languageISO", record, query);
bindValue("seriesGroup", record, query);
bindValue("mainCharacterOrTeam", record, query);
bindValue("review", record, query);
bindValue("tags", record, query);
bindValue("hash", record, query);
}
@ -757,6 +850,7 @@ bool DataBaseManagement::updateToCurrentVersion(const QString &path)
bool pre8 = false;
bool pre9_5 = false;
bool pre9_8 = false;
bool pre9_13 = false;
QString fullPath = path + "/library.ydb";
@ -770,6 +864,8 @@ bool DataBaseManagement::updateToCurrentVersion(const QString &path)
pre9_5 = true;
if (compareVersions(DataBaseManagement::checkValidDB(fullPath), "9.8.0") < 0)
pre9_8 = true;
if (compareVersions(DataBaseManagement::checkValidDB(fullPath), "9.13.0") < 0)
pre9_13 = true;
QString connectionName = "";
bool returnValue = false;
@ -895,6 +991,53 @@ bool DataBaseManagement::updateToCurrentVersion(const QString &path)
returnValue = returnValue && successAddingColumns;
}
}
if (pre9_13) {
{ // comic_info
QStringList columnDefs;
columnDefs << "added INTEGER";
columnDefs << "type INTEGER DEFAULT 0"; // 0 = comic, 1 = manga, 2 = manga left to right, 3 = webcomic,
columnDefs << "editor TEXT";
columnDefs << "imprint TEXT";
columnDefs << "teams TEXT";
columnDefs << "locations TEXT";
columnDefs << "series TEXT";
columnDefs << "alternateSeries TEXT";
columnDefs << "alternateNumber TEXT";
columnDefs << "alternateCount INTEGER";
columnDefs << "languageISO TEXT";
columnDefs << "seriesGroup TEXT";
columnDefs << "mainCharacterOrTeam TEXT";
columnDefs << "review TEXT";
columnDefs << "tags TEXT";
bool successAddingColumns = addColumns("comic_info", columnDefs, db);
returnValue = returnValue && successAddingColumns;
QSqlQuery updateTypeQueryToManga(db);
updateTypeQueryToManga.prepare("UPDATE comic_info SET type = manga");
bool successMigratingManga = updateTypeQueryToManga.exec();
returnValue = returnValue && successMigratingManga;
QSqlQuery updateNumberQueryToBis(db);
updateNumberQueryToBis.prepare("UPDATE comic_info SET number = number + 0.5 WHERE isBis = 1");
bool successMigratingBis = updateNumberQueryToBis.exec();
returnValue = returnValue && successMigratingBis;
}
{ // folder
QStringList columnDefs;
columnDefs << "added INTEGER";
columnDefs << "updated INTEGER";
columnDefs << "type INTEGER DEFAULT 0";
bool successAddingColumns = addColumns("folder", columnDefs, db);
returnValue = returnValue && successAddingColumns;
QSqlQuery updateTypeQueryToManga(db);
updateTypeQueryToManga.prepare("UPDATE folder SET type = manga");
bool successMigratingManga = updateTypeQueryToManga.exec();
returnValue = returnValue && successMigratingManga;
}
}
}
connectionName = db.connectionName();
}

View File

@ -42,7 +42,6 @@ private:
public:
DataBaseManagement();
// TreeModel * newTreeModel(QString path);
// crea una base de datos y todas sus tablas
static QSqlDatabase createDatabase(QString name, QString path);
static QSqlDatabase createDatabase(QString dest);

View File

@ -6,8 +6,6 @@
#include "db_helper.h"
#include "qnaturalsorting.h"
#include "yacreader_global_gui.h"
#include "QsLog.h"
#include "query_parser.h"
#include <QtGui>
@ -54,21 +52,19 @@ void drawMacOSXFinishedFolderIcon()
#define ROOT 1
FolderModel::FolderModel(QObject *parent)
: QAbstractItemModel(parent), isSubfolder(false), rootItem(nullptr), folderIcon(YACReader::noHighlightedIcon(":/images/sidebar/folder.svg")), folderFinishedIcon(YACReader::noHighlightedIcon(":/images/sidebar/folder_finished.svg"))
: QAbstractItemModel(parent), isSubfolder(false), rootItem(nullptr), folderIcon(YACReader::noHighlightedIcon(":/images/sidebar/folder.svg")), folderFinishedIcon(YACReader::noHighlightedIcon(":/images/sidebar/folder_finished.svg")), showRecent(false), recentDays(1)
{
}
FolderModel::FolderModel(QSqlQuery &sqlquery, QObject *parent)
: QAbstractItemModel(parent), isSubfolder(false), rootItem(nullptr)
: QAbstractItemModel(parent), isSubfolder(false), rootItem(nullptr), showRecent(false), recentDays(1)
{
// lo m<>s probable es que el nodo ra<72>z no necesite tener informaci<63>n
QList<QVariant> rootData;
rootData << "root"; // id 0, padre 0, title "root" (el id, y el id del padre van a ir en la clase TreeItem)
rootData << "root"; // id 1, parent 1, title "root"
rootItem = new FolderItem(rootData);
rootItem->id = ROOT;
rootItem->parentItem = nullptr;
setupModelData(sqlquery, rootItem);
// sqlquery.finish();
}
FolderModel::~FolderModel()
@ -98,7 +94,13 @@ QHash<int, QByteArray> FolderModel::roleNames() const
roles[IdRole] = "id";
roles[MangaRole] = "is_manga";
roles[CoverPathRole] = "cover_path";
roles[FolderName] = "name";
roles[FolderNameRole] = "name";
roles[NumChildrenRole] = "num_children";
roles[TypeRole] = "type";
roles[AddedRole] = "added";
roles[UpdatedRole] = "updated";
roles[ShowRecentRole] = "show_recent";
roles[RecentRangeRole] = "recent_range";
return roles;
}
@ -150,7 +152,7 @@ QVariant FolderModel::data(const QModelIndex &index, int role) const
#endif
}
if (role == FolderModel::FolderName) {
if (role == FolderModel::FolderNameRole) {
return item->data(FolderModel::Name);
}
@ -169,6 +171,24 @@ QVariant FolderModel::data(const QModelIndex &index, int role) const
if (role == FolderModel::CoverPathRole)
return getCoverUrlPathForComicHash(item->data(FirstChildHash).toString());
if (role == FolderModel::NumChildrenRole)
return item->data(NumChildren);
if (role == FolderModel::TypeRole)
return item->data(Type);
if (role == FolderModel::AddedRole)
return item->data(Added);
if (role == FolderModel::UpdatedRole)
return item->data(Updated);
if (role == FolderModel::ShowRecentRole)
return showRecent;
if (role == FolderModel::RecentRangeRole)
return recentDays * 86400;
if (role != Qt::DisplayRole)
return QVariant();
@ -183,7 +203,8 @@ Qt::ItemFlags FolderModel::flags(const QModelIndex &index) const
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled | Qt::ItemIsDragEnabled;
}
QVariant FolderModel::headerData(int section, Qt::Orientation orientation,
QVariant FolderModel::headerData(int section,
Qt::Orientation orientation,
int role) const
{
if (rootItem == nullptr) {
@ -323,65 +344,39 @@ void FolderModel::setupModelData(QSqlQuery &sqlquery, FolderItem *parent)
int manga = record.indexOf("manga");
int id = record.indexOf("id");
int parentId = record.indexOf("parentId");
int numChildren = record.indexOf("numChildren");
int firstChildHash = record.indexOf("firstChildHash");
int customImage = record.indexOf("customImage");
int type = record.indexOf("type");
int added = record.indexOf("added");
int updated = record.indexOf("updated");
while (sqlquery.next()) {
QList<QVariant> data;
data << sqlquery.value(name).toString();
data << sqlquery.value(path).toString();
data << sqlquery.value(finished).toBool();
data << sqlquery.value(completed).toBool();
data << sqlquery.value(manga).toBool();
data << sqlquery.value(firstChildHash).toString();
data << sqlquery.value(name);
data << sqlquery.value(path);
data << sqlquery.value(finished);
data << sqlquery.value(completed);
data << sqlquery.value(manga);
data << sqlquery.value(numChildren);
data << sqlquery.value(firstChildHash);
data << sqlquery.value(customImage);
data << sqlquery.value(type);
data << sqlquery.value(added);
data << sqlquery.value(updated);
auto item = new FolderItem(data);
item->id = sqlquery.value(id).toULongLong();
// la inserci<63>n de hijos se hace de forma ordenada
FolderItem *parent = items.value(sqlquery.value(parentId).toULongLong());
// if(parent !=0) //TODO if parent==0 the parent of item was removed from the DB and delete on cascade didn't work, ERROR.
parent->appendChild(item);
// se a<>ade el item al map, de forma que se pueda encontrar como padre en siguientes iteraciones
items.insert(item->id, item);
}
}
void FolderModel::updateFolderModelData(QSqlQuery &sqlquery, FolderItem *parent)
{
Q_UNUSED(parent);
QSqlRecord record = sqlquery.record();
int name = record.indexOf("name");
int path = record.indexOf("path");
int finished = record.indexOf("finished");
int completed = record.indexOf("completed");
int manga = record.indexOf("manga");
int id = record.indexOf("id");
int parentId = record.indexOf("parentId");
int firstChildHash = record.indexOf("firstChildHash");
while (sqlquery.next()) {
QList<QVariant> data;
data << sqlquery.value(name).toString();
data << sqlquery.value(path).toString();
data << sqlquery.value(finished).toBool();
data << sqlquery.value(completed).toBool();
data << sqlquery.value(manga).toBool();
data << sqlquery.value(firstChildHash).toString();
auto item = new FolderItem(data);
item->id = sqlquery.value(id).toULongLong();
// la inserci<63>n de hijos se hace de forma ordenada
FolderItem *parent = items.value(sqlquery.value(parentId).toULongLong());
if (parent != 0) // TODO if parent==0 the parent of item was removed from the DB and delete on cascade didn't work, ERROR.
parent->appendChild(item);
// se a<>ade el item al map, de forma que se pueda encontrar como padre en siguientes iteraciones
items.insert(item->id, item);
}
}
QString FolderModel::getDatabase()
{
return _databasePath;
@ -406,7 +401,7 @@ void FolderModel::updateFolderCompletedStatus(const QModelIndexList &list, bool
if (!isSubfolder) {
Folder f = DBHelper::loadFolder(item->id, db);
f.setCompleted(status);
f.completed = status;
DBHelper::update(f, db);
}
}
@ -415,7 +410,7 @@ void FolderModel::updateFolderCompletedStatus(const QModelIndexList &list, bool
}
QSqlDatabase::removeDatabase(connectionName);
emit dataChanged(index(list.first().row(), FolderModel::Name), index(list.last().row(), FolderModel::FirstChildHash));
emit dataChanged(index(list.first().row(), FolderModel::Name), index(list.last().row(), FolderModel::Updated));
}
void FolderModel::updateFolderFinishedStatus(const QModelIndexList &list, bool status)
@ -430,7 +425,7 @@ void FolderModel::updateFolderFinishedStatus(const QModelIndexList &list, bool s
if (!isSubfolder) {
Folder f = DBHelper::loadFolder(item->id, db);
f.setFinished(status);
f.finished = status;
DBHelper::update(f, db);
}
}
@ -439,10 +434,10 @@ void FolderModel::updateFolderFinishedStatus(const QModelIndexList &list, bool s
}
QSqlDatabase::removeDatabase(connectionName);
emit dataChanged(index(list.first().row(), FolderModel::Name), index(list.last().row(), FolderModel::FirstChildHash));
emit dataChanged(index(list.first().row(), FolderModel::Name), index(list.last().row(), FolderModel::Updated));
}
void FolderModel::updateFolderManga(const QModelIndexList &list, bool manga)
void FolderModel::updateFolderType(const QModelIndexList &list, YACReader::FileType type)
{
QString connectionName = "";
{
@ -451,19 +446,19 @@ void FolderModel::updateFolderManga(const QModelIndexList &list, bool manga)
foreach (QModelIndex mi, list) {
auto item = static_cast<FolderItem *>(mi.internalPointer());
std::function<void(FolderItem *, bool)> setManga;
setManga = [&setManga](FolderItem *item, bool manga) -> void {
item->setData(FolderModel::Manga, manga);
std::function<void(FolderItem *, YACReader::FileType)> setType;
setType = [&setType](FolderItem *item, YACReader::FileType type) -> void {
item->setData(FolderModel::Type, QVariant::fromValue(type));
for (auto child : item->children()) {
setManga(child, manga);
setType(child, type);
}
};
setManga(item, manga);
setType(item, type);
if (!isSubfolder) {
DBHelper::updateFolderTreeManga(item->id, db, manga);
DBHelper::updateFolderTreeType(item->id, db, type);
}
}
db.commit();
@ -471,7 +466,7 @@ void FolderModel::updateFolderManga(const QModelIndexList &list, bool manga)
}
QSqlDatabase::removeDatabase(connectionName);
emit dataChanged(index(list.first().row(), FolderModel::Name), index(list.last().row(), FolderModel::FirstChildHash));
emit dataChanged(index(list.first().row(), FolderModel::Name), index(list.last().row(), FolderModel::Updated));
}
QStringList FolderModel::getSubfoldersNames(const QModelIndex &mi)
@ -552,7 +547,13 @@ Folder FolderModel::getFolder(const QModelIndex &mi)
folderItem->parent()->data(Columns::Path).toString() + "/" + name,
folderItem->data(Columns::Completed).toBool(),
folderItem->data(Columns::Finished).toBool(),
folderItem->data(Columns::Manga).toBool());
folderItem->data(Columns::Manga).toBool(),
folderItem->data(Columns::NumChildren).toInt(),
folderItem->data(Columns::FirstChildHash).toString(),
folderItem->data(Columns::CustomImage).toString(),
folderItem->data(Columns::Type).value<YACReader::FileType>(),
folderItem->data(Columns::Added).toLongLong(),
folderItem->data(Columns::Updated).toLongLong());
return folder;
}
@ -589,74 +590,6 @@ QModelIndex FolderModel::getIndexFromFolder(const Folder &folder, const QModelIn
return QModelIndex();
}
void FolderModel::fetchMoreFromDB(const QModelIndex &parent)
{
FolderItem *item;
if (parent.isValid())
item = static_cast<FolderItem *>(parent.internalPointer());
else
item = rootItem;
// Remove all children
if (item->childCount() > 0) {
beginRemoveRows(parent, 0, item->childCount() - 1);
item->clearChildren();
endRemoveRows();
}
QString connectionName = "";
{
QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
QList<FolderItem *> items;
QList<FolderItem *> nextLevelItems;
QSqlQuery selectQuery(db);
selectQuery.prepare("select * from folder where id <> 1 and parentId = :parentId order by parentId,name");
items << item;
bool firstLevelUpdated = false;
while (items.size() > 0) {
nextLevelItems.clear();
foreach (FolderItem *item, items) {
QLOG_DEBUG() << "ID " << item->id;
selectQuery.bindValue(":parentId", item->id);
selectQuery.exec();
if (!firstLevelUpdated) {
// NO size support
int numResults = 0;
while (selectQuery.next())
numResults++;
if (!selectQuery.seek(-1))
selectQuery.exec();
// END no size support
beginInsertRows(parent, 0, numResults - 1);
}
updateFolderModelData(selectQuery, item);
if (!firstLevelUpdated) {
endInsertRows();
firstLevelUpdated = true;
}
nextLevelItems << item->children();
}
items.clear();
items = nextLevelItems;
}
connectionName = db.connectionName();
}
QLOG_DEBUG() << "item->childCount()-1" << item->childCount() - 1;
QSqlDatabase::removeDatabase(connectionName);
}
QModelIndex FolderModel::addFolderAtParent(const QString &folderName, const QModelIndex &parent)
{
FolderItem *parentItem;
@ -670,7 +603,9 @@ QModelIndex FolderModel::addFolderAtParent(const QString &folderName, const QMod
newFolder.name = folderName;
newFolder.parentId = parentItem->id;
newFolder.path = parentItem->data(Columns::Path).toString() + "/" + folderName;
newFolder.setManga(parentItem->data(Columns::Manga).toBool());
newFolder.manga = parentItem->data(Columns::Manga).toBool();
newFolder.type = parentItem->data(Columns::Type).value<YACReader::FileType>();
QString connectionName = "";
{
QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
@ -687,7 +622,13 @@ QModelIndex FolderModel::addFolderAtParent(const QString &folderName, const QMod
data << newFolder.path;
data << false; // finished
data << true; // completed
data << newFolder.isManga();
data << newFolder.manga;
data << 0; // numChildren
data << QVariant(); // first child hash, new folder is empty
data << QVariant(); // custom cover
data << QVariant::fromValue(newFolder.type);
data << newFolder.added;
data << newFolder.updated;
auto item = new FolderItem(data);
item->id = newFolder.id;
@ -708,6 +649,26 @@ QUrl FolderModel::getCoverUrlPathForComicHash(const QString &hash) const
return QUrl("file:" + _databasePath + "/covers/" + hash + ".jpg");
}
void FolderModel::setShowRecent(bool showRecent)
{
if (this->showRecent == showRecent)
return;
this->showRecent = showRecent;
emit dataChanged(index(0, 0), index(rowCount() - 1, 0), { FolderModel::ShowRecentRole });
}
void FolderModel::setRecentRange(int days)
{
if (this->recentDays == days)
return;
this->recentDays = days;
emit dataChanged(index(0, 0), index(rowCount() - 1, 0), { FolderModel::RecentRangeRole });
}
void FolderModel::deleteFolder(const QModelIndex &mi)
{
beginRemoveRows(mi.parent(), mi.row(), mi.row());

View File

@ -66,13 +66,10 @@ public:
void setupModelData(QString path);
QString getDatabase();
QString getFolderPath(const QModelIndex &folder);
// QModelIndex indexFromItem(FolderItem * item, int column);
// bool isFilterEnabled(){return filterEnabled;};
void updateFolderCompletedStatus(const QModelIndexList &list, bool status);
void updateFolderFinishedStatus(const QModelIndexList &list, bool status);
void updateFolderManga(const QModelIndexList &list, bool manga);
void updateFolderType(const QModelIndexList &list, YACReader::FileType type);
QStringList getSubfoldersNames(const QModelIndex &mi);
FolderModel *getSubfoldersModel(const QModelIndex &mi);
@ -80,28 +77,40 @@ public:
Folder getFolder(const QModelIndex &mi);
QModelIndex getIndexFromFolder(const Folder &folder, const QModelIndex &parent = QModelIndex());
void fetchMoreFromDB(const QModelIndex &parent);
QModelIndex addFolderAtParent(const QString &folderName, const QModelIndex &parent);
Q_INVOKABLE QUrl getCoverUrlPathForComicHash(const QString &hash) const;
void setShowRecent(bool showRecent);
void setRecentRange(int days);
enum Columns {
Name = 0,
Path = 1,
Finished = 2,
Completed = 3,
Manga = 4,
FirstChildHash = 5
}; // id INTEGER PRIMARY KEY, parentId INTEGER NOT NULL, name TEXT NOT NULL, path TEXT NOT NULL
Path,
Finished,
Completed,
Manga, // deprecated
NumChildren,
FirstChildHash,
CustomImage,
Type, // FileType
Added,
Updated,
};
enum Roles {
FinishedRole = Qt::UserRole + 1,
CompletedRole,
IdRole,
MangaRole,
MangaRole, // deprecated
CoverPathRole,
FolderName,
FolderNameRole,
NumChildrenRole,
TypeRole,
AddedRole,
UpdatedRole,
ShowRecentRole,
RecentRangeRole,
};
bool isSubfolder;
@ -112,9 +121,7 @@ public slots:
private:
void fullSetup(QSqlQuery &sqlquery, FolderItem *parent);
void setupModelData(QSqlQuery &sqlquery, FolderItem *parent);
void updateFolderModelData(QSqlQuery &sqlquery, FolderItem *parent);
FolderItem *rootItem; // el árbol
QMap<unsigned long long int, FolderItem *> items; // relación entre folders
@ -123,6 +130,9 @@ private:
QIcon folderIcon;
QIcon folderFinishedIcon;
bool showRecent;
qlonglong recentDays;
};
#endif

View File

@ -17,6 +17,14 @@ Token QueryLexer::next()
case '(':
case ')':
return single(Token::Type::opcode);
case ':':
return single(Token::Type::equal);
case '=':
return equal();
case '<':
return minor();
case '>':
return major();
case '"':
return quotedWord();
default:
@ -44,7 +52,7 @@ Token QueryLexer::word()
auto start = index;
get();
auto current = peek();
while (current != '\0' && !isSpace(current) && current != '"' && current != '(' && current != ')') {
while (current != '\0' && !isSpace(current) && current != '"' && current != '(' && current != ')' && current != ':' && current != '=' && current != '<' && current != '>') {
get();
current = peek();
}
@ -70,6 +78,45 @@ Token QueryLexer::quotedWord()
return Token(Token::Type::eof);
}
Token QueryLexer::minor()
{
auto start = index;
get();
auto current = peek();
if (current == '=') {
get();
return Token(Token::Type::minorOrEqual, input.substr(start, index - start));
}
return Token(Token::Type::minor, input.substr(start, index - start));
}
Token QueryLexer::major()
{
auto start = index;
get();
auto current = peek();
if (current == '=') {
get();
return Token(Token::Type::majorOrEqual, input.substr(start, index - start));
}
return Token(Token::Type::major, input.substr(start, index - start));
}
Token QueryLexer::equal()
{
auto start = index;
get();
auto current = peek();
if (current == '=') {
get();
return Token(Token::Type::exactEqual, input.substr(start, index - start));
}
return Token(Token::Type::equal, input.substr(start, index - start));
}
bool QueryLexer::isSpace(char c)
{
switch (c) {

View File

@ -11,6 +11,12 @@ public:
opcode,
word,
quotedWord,
equal, // =
exactEqual, // ==
minor,
major,
minorOrEqual,
majorOrEqual,
undefined
};
@ -50,6 +56,9 @@ private:
Token single(Token::Type type);
Token word();
Token quotedWord();
Token minor();
Token major();
Token equal();
bool isSpace(char c);
};

View File

@ -5,19 +5,25 @@
#include <numeric>
#include <stdexcept>
#include <QsLog.h>
const std::map<QueryParser::FieldType, std::vector<std::string>> QueryParser::fieldNames {
{ FieldType::numeric, { "numpages", "number", "count", "arcnumber", "arccount" } },
{ FieldType::text, { "title", "volume", "storyarc", "genere", "writer", "penciller", "inker", "colorist", "letterer", "coverartist", "publisher", "format", "agerating", "synopsis", "characters", "notes" } },
{ FieldType::boolean, { "isbis", "color", "read", "manga" } },
{ FieldType::date, { "date" } },
// TODO_METADATA support dates
{ FieldType::numeric, { "numpages", "count", "arccount", "alternateCount", "rating" } },
{ FieldType::text, { "number", "arcnumber", "title", "volume", "storyarc", "genere", "writer", "penciller", "inker", "colorist", "letterer", "coverartist", "publisher", "format", "agerating", "synopsis", "characters", "notes", "editor", "imprint", "teams", "locations", "series", "alternateSeries", "alternateNumber", "languageISO", "seriesGroup", "mainCharacterOrTeam", "review", "tags" } },
{ FieldType::boolean, { "color", "read", "edited", "hasBeenOpened" } },
{ FieldType::date, { "date", "added", "lastTimeOpened" } },
{ FieldType::filename, { "filename" } },
{ FieldType::folder, { "folder" } },
{ FieldType::booleanFolder, { "completed", "finished" } },
{ FieldType::enumField, { "type" } },
{ FieldType::enumFieldFolder, { "foldertype" } }
};
int QueryParser::TreeNode::buildSqlString(std::string &sqlString, int bindPosition) const
{
if (t == "token") {
// TODO: add some semantic checks, not all operators apply to all fields
if (t == "expression") {
++bindPosition;
if (toLower(children[0].t) == "all") {
sqlString += "(";
@ -26,7 +32,15 @@ int QueryParser::TreeNode::buildSqlString(std::string &sqlString, int bindPositi
}
sqlString += "UPPER(c.filename) LIKE UPPER(:bindPosition" + std::to_string(bindPosition) + ") OR ";
sqlString += "UPPER(f.name) LIKE UPPER(:bindPosition" + std::to_string(bindPosition) + ")) ";
} else if (isIn(fieldType(children[0].t), { FieldType::numeric, FieldType::boolean })) {
} else if (isIn(fieldType(children[0].t), { FieldType::numeric })) {
std::string sqlOperator;
if (expOperator == ":" || expOperator == "=" || expOperator == "==") {
sqlOperator = "=";
} else {
sqlOperator = expOperator;
}
sqlString += "ci." + children[0].t + " " + sqlOperator + " :bindPosition" + std::to_string(bindPosition) + " ";
} else if (isIn(fieldType(children[0].t), { FieldType::boolean, FieldType::enumField })) {
sqlString += "ci." + children[0].t + " = :bindPosition" + std::to_string(bindPosition) + " ";
} else if (fieldType(children[0].t) == FieldType::filename) {
sqlString += "(UPPER(c." + children[0].t + ") LIKE UPPER(:bindPosition" + std::to_string(bindPosition) + ")) ";
@ -34,8 +48,23 @@ int QueryParser::TreeNode::buildSqlString(std::string &sqlString, int bindPositi
sqlString += "(UPPER(f.name) LIKE UPPER(:bindPosition" + std::to_string(bindPosition) + ")) ";
} else if (fieldType(children[0].t) == FieldType::booleanFolder) {
sqlString += "f." + children[0].t + " = :bindPosition" + std::to_string(bindPosition) + " ";
} else if (fieldType(children[0].t) == FieldType::enumFieldFolder) {
if (children[0].t == "foldertype") {
sqlString += "f.type = :bindPosition" + std::to_string(bindPosition) + " ";
} else {
sqlString += "f." + children[0].t + " = :bindPosition" + std::to_string(bindPosition) + " ";
}
} else {
sqlString += "(UPPER(ci." + children[0].t + ") LIKE UPPER(:bindPosition" + std::to_string(bindPosition) + ")) ";
if (expOperator == "=" || expOperator == ":" || expOperator == "") {
sqlString += "(UPPER(ci." + children[0].t + ") LIKE UPPER(:bindPosition" + std::to_string(bindPosition) + ")) ";
} else {
if (expOperator == "==") {
sqlString += "(UPPER(ci." + children[0].t + ") = UPPER(:bindPosition" + std::to_string(bindPosition) + ")) ";
} else {
// support for <,>,<=,>= in text fields makes sense for number, arcNumber, alternateNumber, but (TODO) the syntax won't prevent other fields from using this operators
sqlString += "(CAST(ci." + children[0].t + " as REAL) " + expOperator + " CAST(:bindPosition" + std::to_string(bindPosition) + " as REAL)) ";
}
}
}
} else if (t == "not") {
sqlString += "(NOT ";
@ -54,7 +83,7 @@ int QueryParser::TreeNode::buildSqlString(std::string &sqlString, int bindPositi
int QueryParser::TreeNode::bindValues(QSqlQuery &selectQuery, int bindPosition) const
{
if (t == "token") {
if (t == "expression") {
std::string bind_string(":bindPosition" + std::to_string(++bindPosition));
if (isIn(fieldType(children[0].t), { FieldType::numeric })) {
selectQuery.bindValue(QString::fromStdString(bind_string), std::stoi(children[1].t));
@ -67,8 +96,30 @@ int QueryParser::TreeNode::bindValues(QSqlQuery &selectQuery, int bindPosition)
} else {
selectQuery.bindValue(QString::fromStdString(bind_string), std::stoi(value));
}
} else if ((isIn(fieldType(children[0].t), { FieldType::enumField, FieldType::enumFieldFolder }))) {
auto enumType = children[0].t;
auto value = toLower(children[1].t);
if (enumType == "type" || enumType == "foldertype") {
if (value == "comic") {
selectQuery.bindValue(QString::fromStdString(bind_string), 0);
} else if (value == "manga") {
selectQuery.bindValue(QString::fromStdString(bind_string), 1);
} else if (value == "westernmanga") {
selectQuery.bindValue(QString::fromStdString(bind_string), 2);
} else if (value == "webcomic" || value == "web") {
selectQuery.bindValue(QString::fromStdString(bind_string), 3);
} else if (value == "4koma" || value == "yonkoma") {
selectQuery.bindValue(QString::fromStdString(bind_string), 4);
}
} else {
selectQuery.bindValue(QString::fromStdString(bind_string), std::stoi(children[1].t));
}
} else {
selectQuery.bindValue(QString::fromStdString(bind_string), QString::fromStdString("%%" + children[1].t + "%%"));
if (expOperator == "=" || expOperator == ":" || expOperator == "") {
selectQuery.bindValue(QString::fromStdString(bind_string), QString::fromStdString("%%" + children[1].t + "%%"));
} else {
selectQuery.bindValue(QString::fromStdString(bind_string), QString::fromStdString(children[1].t));
}
}
} else if (t == "not") {
bindPosition = children[0].bindValues(selectQuery, bindPosition);
@ -151,6 +202,16 @@ void QueryParser::advance()
currentToken = lexer.next();
}
bool QueryParser::isOperatorToken(Token::Type type)
{
return type == Token::Type::equal ||
type == Token::Type::exactEqual ||
type == Token::Type::minor ||
type == Token::Type::minorOrEqual ||
type == Token::Type::major ||
type == Token::Type::majorOrEqual;
}
QueryParser::FieldType QueryParser::fieldType(const std::string &str)
{
for (const auto &names : fieldNames) {
@ -223,25 +284,39 @@ QueryParser::TreeNode QueryParser::locationExpression()
if (!isIn(tokenType(), { Token::Type::word, Token::Type::quotedWord })) {
throw std::invalid_argument("Invalid syntax. Expected a lookup name or a word");
}
return expression();
}
QueryParser::TreeNode QueryParser::expression()
{
if (tokenType() == Token::Type::word) {
auto left = token(true);
if (isOperatorToken(tokenType())) {
auto expOperator = token(true);
if (tokenType() != Token::Type::word && tokenType() != Token::Type::quotedWord) {
throw std::invalid_argument("missing right operand");
}
auto right = token(true);
return TreeNode("expression", { TreeNode(toLower(left), {}), TreeNode(right, {}) }, expOperator);
} else {
return TreeNode("expression", { TreeNode("all", {}), TreeNode(left, {}) });
}
}
return baseToken();
}
QueryParser::TreeNode QueryParser::baseToken()
{
if (tokenType() == Token::Type::quotedWord) {
return TreeNode("token", { TreeNode("all", {}), TreeNode(token(true), {}) });
return TreeNode("expression", { TreeNode("all", {}), TreeNode(token(true), {}) });
}
auto words(split(token(true), ':'));
if (words.size() > 1 && fieldType(words[0].toStdString()) != FieldType::unknown) {
auto loc(toLower(words[0].toStdString()));
words.erase(words.begin());
if (words.size() == 1 && tokenType() == Token::Type::quotedWord) {
return TreeNode("token", { TreeNode(loc, {}), TreeNode(token(true), {}) });
}
return TreeNode("token", { TreeNode(loc, {}), TreeNode(join(words, ":"), {}) });
if (tokenType() == Token::Type::word) {
return TreeNode("expression", { TreeNode("all", {}), TreeNode(token(true), {}) });
}
return TreeNode("token", { TreeNode("all", {}), TreeNode(join(words, ":"), {}) });
return TreeNode("expression", { TreeNode("all", {}), TreeNode(token(true), {}) });
}

View File

@ -9,8 +9,8 @@
#include <vector>
#include <list>
#define SEARCH_FOLDERS_QUERY "SELECT DISTINCT f.id, f.parentId, f.name, f.path, f.finished, f.completed, f.numChildren, f.firstChildHash FROM folder f LEFT JOIN comic c ON (f.id = c.parentId) INNER JOIN comic_info ci ON (c.comicInfoId = ci.id) WHERE "
#define SEARCH_COMICS_QUERY "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,ci.coverSizeRatio,ci.lastTimeOpened,ci.manga FROM comic c INNER JOIN comic_info ci ON (c.comicInfoId = ci.id) LEFT JOIN folder f ON (f.id == c.parentId) WHERE "
#define SEARCH_FOLDERS_QUERY "SELECT DISTINCT * FROM folder f LEFT JOIN comic c ON (f.id = c.parentId) INNER JOIN comic_info ci ON (c.comicInfoId = ci.id) WHERE "
#define SEARCH_COMICS_QUERY "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,ci.date,ci.added,ci.type FROM comic c INNER JOIN comic_info ci ON (c.comicInfoId = ci.id) LEFT JOIN folder f ON (f.id == c.parentId) WHERE "
/**
* This class is used to generate an SQL query string from a search expression,
@ -25,7 +25,9 @@
* and_expression ::= not_expression [ [ 'and' ] and_expression ]
* not_expression ::= [ 'not' ] location_expression
* location_expression ::= base_token | ( '(' or_expression ')' )
* base_token ::= a sequence of letters and colons, perhaps quoted
* expression :: base_token | base_token 'operator' base_token
* operator :: [':' '=' '<' '>' '<=' '=>']
* base_token ::= a sequence of letters, perhaps quoted
*
* Usage Example:
* QSqlQuery selectQuery(db);
@ -47,9 +49,10 @@ public:
struct TreeNode {
std::string t;
std::vector<TreeNode> children;
std::string expOperator;
explicit TreeNode(std::string t, std::vector<TreeNode> children)
: t(t), children(children)
explicit TreeNode(std::string t, std::vector<TreeNode> children, std::string expOperator = "")
: t(t), children(children), expOperator(expOperator)
{
}
@ -78,6 +81,8 @@ private:
return std::find(v.begin(), v.end(), e) != v.end();
}
bool isOperatorToken(Token::Type type);
enum class FieldType { unknown,
numeric,
text,
@ -85,7 +90,9 @@ private:
date,
folder,
booleanFolder,
filename };
filename,
enumField,
enumFieldFolder };
static FieldType fieldType(const std::string &str);
static std::string join(const QStringList &strings, const std::string &delim);
@ -95,6 +102,7 @@ private:
TreeNode andExpression();
TreeNode notExpression();
TreeNode locationExpression();
TreeNode expression();
TreeNode baseToken();
static const std::map<FieldType, std::vector<std::string>> fieldNames;

View File

@ -66,7 +66,7 @@ QList<LibraryItem *> DBHelper::getFolderComicsFromLibraryForReading(qulonglong l
return naturalSortLessThanCI(c1->name, c2->name);
} else {
if (c1->info.number.isNull() == false && c2->info.number.isNull() == false) {
return c1->info.number.toInt() < c2->info.number.toInt();
return naturalSortLessThanCI(c1->info.number.toString(), c2->info.number.toString());
} else {
return c2->info.number.isNull();
}
@ -637,9 +637,28 @@ void DBHelper::update(ComicInfo *comicInfo, QSqlDatabase &db)
//--
// new 9.8 fields
"manga = :manga"
"manga = :manga,"
//--
" WHERE id = :id ");
// new 9.13 fields
"added = :added,"
"type = :type,"
"editor = :editor,"
"imprint = :imprint,"
"teams = :teams,"
"locations = :locations,"
"series = :series,"
"alternateSeries = :alternateSeries,"
"alternateNumber = :alternateNumber,"
"alternateCount = :alternateCount,"
"languageISO = :languageISO,"
"seriesGroup = :seriesGroup,"
"mainCharacterOrTeam = :mainCharacterOrTeam,"
"review = :review,"
"tags = :tags"
//--
" WHERE id = :id");
updateComicInfo.bindValue(":title", comicInfo->title);
@ -698,7 +717,28 @@ void DBHelper::update(ComicInfo *comicInfo, QSqlDatabase &db)
updateComicInfo.bindValue(":coverSizeRatio", comicInfo->coverSizeRatio);
updateComicInfo.bindValue(":originalCoverSize", comicInfo->originalCoverSize);
updateComicInfo.bindValue(":added", comicInfo->added);
auto intType = static_cast<int>(comicInfo->type.value<YACReader::FileType>());
updateComicInfo.bindValue(":type", intType);
updateComicInfo.bindValue(":editor", comicInfo->editor);
updateComicInfo.bindValue(":imprint", comicInfo->imprint);
updateComicInfo.bindValue(":teams", comicInfo->teams);
updateComicInfo.bindValue(":locations", comicInfo->locations);
updateComicInfo.bindValue(":series", comicInfo->series);
updateComicInfo.bindValue(":alternateSeries", comicInfo->alternateSeries);
updateComicInfo.bindValue(":alternateNumber", comicInfo->alternateNumber);
updateComicInfo.bindValue(":alternateCount", comicInfo->alternateCount);
updateComicInfo.bindValue(":languageISO", comicInfo->languageISO);
updateComicInfo.bindValue(":seriesGroup", comicInfo->seriesGroup);
updateComicInfo.bindValue(":mainCharacterOrTeam", comicInfo->mainCharacterOrTeam);
updateComicInfo.bindValue(":review", comicInfo->review);
updateComicInfo.bindValue(":tags", comicInfo->tags);
updateComicInfo.exec();
QLOG_INFO() << updateComicInfo.lastError().databaseText();
QLOG_INFO() << updateComicInfo.lastError().text();
QLOG_INFO() << updateComicInfo.lastQuery();
}
void DBHelper::updateRead(ComicInfo *comicInfo, QSqlDatabase &db)
@ -718,12 +758,10 @@ void DBHelper::update(const Folder &folder, QSqlDatabase &db)
QSqlQuery updateFolderInfo(db);
updateFolderInfo.prepare("UPDATE folder SET "
"finished = :finished, "
"completed = :completed, "
"manga = :manga "
"completed = :completed "
"WHERE id = :id ");
updateFolderInfo.bindValue(":finished", folder.isFinished() ? 1 : 0);
updateFolderInfo.bindValue(":completed", folder.isCompleted() ? 1 : 0);
updateFolderInfo.bindValue(":manga", folder.isManga() ? 1 : 0);
updateFolderInfo.bindValue(":finished", folder.finished ? 1 : 0);
updateFolderInfo.bindValue(":completed", folder.completed ? 1 : 0);
updateFolderInfo.bindValue(":id", folder.id);
updateFolderInfo.exec();
}
@ -762,7 +800,7 @@ Folder DBHelper::updateChildrenInfo(qulonglong folderId, QSqlDatabase &db)
} else {
for (auto item : updatedSubfolders) {
auto f = static_cast<Folder *>(item);
auto firstChildHash = f->getFirstChildHash();
auto firstChildHash = f->firstChildHash;
if (!firstChildHash.isEmpty()) {
coverHash = firstChildHash;
break;
@ -770,16 +808,16 @@ Folder DBHelper::updateChildrenInfo(qulonglong folderId, QSqlDatabase &db)
}
}
folder.setNumChildren(subfolders.count() + comics.count());
folder.setFirstChildHash(coverHash);
folder.numChildren = subfolders.count() + comics.count();
folder.firstChildHash = coverHash;
QSqlQuery updateFolderInfo(db);
updateFolderInfo.prepare("UPDATE folder SET "
"numChildren = :numChildren, "
"firstChildHash = :firstChildHash "
"WHERE id = :id ");
updateFolderInfo.bindValue(":numChildren", folder.getNumChildren());
updateFolderInfo.bindValue(":firstChildHash", folder.getFirstChildHash());
updateFolderInfo.bindValue(":numChildren", folder.numChildren);
updateFolderInfo.bindValue(":firstChildHash", folder.firstChildHash);
updateFolderInfo.bindValue(":id", folderId);
updateFolderInfo.exec();
@ -854,7 +892,7 @@ void DBHelper::updateReadingRemoteProgress(const ComicInfo &comicInfo, QSqlDatab
updateComicInfo.bindValue(":read", comicInfo.read ? 1 : 0);
updateComicInfo.bindValue(":currentPage", comicInfo.currentPage);
updateComicInfo.bindValue(":hasBeenOpened", comicInfo.hasBeenOpened ? 1 : 0);
updateComicInfo.bindValue(":lastTimeOpened", QDateTime::currentMSecsSinceEpoch() / 1000);
updateComicInfo.bindValue(":lastTimeOpened", QDateTime::currentSecsSinceEpoch());
updateComicInfo.bindValue(":id", comicInfo.id);
updateComicInfo.bindValue(":rating", comicInfo.rating);
updateComicInfo.exec();
@ -1067,7 +1105,7 @@ void DBHelper::updateFromRemoteClientWithHash(const QList<ComicInfo> &comics)
updateComicInfo.bindValue(":read", info.read ? 1 : 0);
updateComicInfo.bindValue(":currentPage", info.currentPage);
updateComicInfo.bindValue(":hasBeenOpened", info.hasBeenOpened ? 1 : 0);
updateComicInfo.bindValue(":lastTimeOpened", QDateTime::currentMSecsSinceEpoch() / 1000);
updateComicInfo.bindValue(":lastTimeOpened", QDateTime::currentSecsSinceEpoch());
updateComicInfo.bindValue(":id", info.id);
updateComicInfo.bindValue(":rating", info.rating);
updateComicInfo.exec();
@ -1194,12 +1232,16 @@ void DBHelper::updateComicsInfo(QList<ComicDB> &comics, const QString &databaseP
// inserts
qulonglong DBHelper::insert(Folder *folder, QSqlDatabase &db)
{
auto added = QDateTime::currentSecsSinceEpoch();
folder->added = added;
QSqlQuery query(db);
query.prepare("INSERT INTO folder (parentId, name, path) "
"VALUES (:parentId, :name, :path)");
query.prepare("INSERT INTO folder (parentId, name, path, added) "
"VALUES (:parentId, :name, :path, :added)");
query.bindValue(":parentId", folder->parentId);
query.bindValue(":name", folder->name);
query.bindValue(":path", folder->path);
query.bindValue(":added", added);
query.exec();
return query.lastInsertId().toULongLong();
@ -1207,16 +1249,21 @@ qulonglong DBHelper::insert(Folder *folder, QSqlDatabase &db)
qulonglong DBHelper::insert(ComicDB *comic, QSqlDatabase &db, bool insertAllInfo)
{
auto added = QDateTime::currentSecsSinceEpoch();
if (!comic->info.existOnDb) {
QSqlQuery comicInfoInsert(db);
comicInfoInsert.prepare("INSERT INTO comic_info (hash,numPages,coverSizeRatio,originalCoverSize) "
"VALUES (:hash,:numPages,:coverSizeRatio,:originalCoverSize)");
comicInfoInsert.prepare("INSERT INTO comic_info (hash,numPages,coverSizeRatio,originalCoverSize,added) "
"VALUES (:hash,:numPages,:coverSizeRatio,:originalCoverSize,:added)");
comicInfoInsert.bindValue(":hash", comic->info.hash);
comicInfoInsert.bindValue(":numPages", comic->info.numPages);
comicInfoInsert.bindValue(":coverSizeRatio", comic->info.coverSizeRatio);
comicInfoInsert.bindValue(":originalCoverSize", comic->info.originalCoverSize);
comicInfoInsert.bindValue(":added", added);
comicInfoInsert.exec();
comic->info.id = comicInfoInsert.lastInsertId().toULongLong();
comic->info.added = added;
comic->_hasCover = false;
if (insertAllInfo) {
@ -1234,6 +1281,14 @@ qulonglong DBHelper::insert(ComicDB *comic, QSqlDatabase &db, bool insertAllInfo
query.bindValue(":path", comic->path);
query.exec();
QSqlQuery updateFolder(db);
updateFolder.prepare("UPDATE folder SET "
"updated = :updated "
"WHERE id = :id ");
updateFolder.bindValue(":updated", added);
updateFolder.bindValue(":id", comic->parentId);
updateFolder.exec();
return query.lastInsertId().toULongLong();
}
@ -1342,6 +1397,7 @@ void DBHelper::insertComicsInReadingList(const QList<ComicDB> &comicsList, qulon
db.commit();
}
// queries
QList<LibraryItem *> DBHelper::getFoldersFromParent(qulonglong parentId, QSqlDatabase &db, bool sort)
{
@ -1356,20 +1412,33 @@ QList<LibraryItem *> DBHelper::getFoldersFromParent(qulonglong parentId, QSqlDat
int name = record.indexOf("name");
int path = record.indexOf("path");
int finished = record.indexOf("finished");
int completed = record.indexOf("completed");
int manga = record.indexOf("manga");
int id = record.indexOf("id");
int numChildren = record.indexOf("numChildren");
int firstChildHash = record.indexOf("firstChildHash");
int customImage = record.indexOf("customImage");
int type = record.indexOf("type");
int added = record.indexOf("added");
int updated = record.indexOf("updated");
Folder *currentItem;
while (selectQuery.next()) {
// TODO sort by sort indicator and name
currentItem = new Folder(selectQuery.value(id).toULongLong(), parentId, selectQuery.value(name).toString(), selectQuery.value(path).toString());
if (!selectQuery.value(numChildren).isNull() && selectQuery.value(numChildren).isValid())
currentItem->setNumChildren(selectQuery.value(numChildren).toInt());
currentItem->setFirstChildHash(selectQuery.value(firstChildHash).toString());
currentItem->setCustomImage(selectQuery.value(customImage).toString());
currentItem->finished = selectQuery.value(finished).toBool();
currentItem->completed = selectQuery.value(completed).toBool();
if (!selectQuery.value(numChildren).isNull() && selectQuery.value(numChildren).isValid()) {
currentItem->numChildren = selectQuery.value(numChildren).toInt();
}
currentItem->firstChildHash = selectQuery.value(firstChildHash).toString();
currentItem->customImage = selectQuery.value(customImage).toString();
currentItem->manga = selectQuery.value(manga).toBool();
currentItem->type = selectQuery.value(type).value<YACReader::FileType>();
currentItem->added = selectQuery.value(added).toLongLong();
currentItem->updated = selectQuery.value(updated).toLongLong();
int lessThan = 0;
@ -1524,21 +1593,21 @@ QList<Label> DBHelper::getLabels(qulonglong libraryId)
return labels;
}
void DBHelper::updateFolderTreeManga(qulonglong id, QSqlDatabase &db, bool manga)
void DBHelper::updateFolderTreeType(qulonglong id, QSqlDatabase &db, YACReader::FileType type)
{
QSqlQuery updateFolderQuery(db);
updateFolderQuery.prepare("UPDATE folder "
"SET manga = :manga "
"SET type = :type "
"WHERE id = :id");
updateFolderQuery.bindValue(":manga", manga ? 1 : 0);
updateFolderQuery.bindValue(":type", static_cast<int>(type));
updateFolderQuery.bindValue(":id", id);
updateFolderQuery.exec();
QSqlQuery updateComicInfo(db);
updateComicInfo.prepare("UPDATE comic_info "
"SET manga = :manga "
"SET type = :type "
"WHERE id IN (SELECT ci.id FROM comic c INNER JOIN comic_info ci ON (c.comicInfoId = ci.id) WHERE c.parentId = :parentId)");
updateComicInfo.bindValue(":manga", manga ? 1 : 0);
updateComicInfo.bindValue(":type", static_cast<int>(type));
updateComicInfo.bindValue(":parentId", id);
updateComicInfo.exec();
@ -1550,24 +1619,49 @@ void DBHelper::updateFolderTreeManga(qulonglong id, QSqlDatabase &db, bool manga
int childFolderIdPos = getSubFoldersQuery.record().indexOf("id");
while (getSubFoldersQuery.next()) {
updateFolderTreeManga(getSubFoldersQuery.value(childFolderIdPos).toULongLong(), db, manga);
updateFolderTreeType(getSubFoldersQuery.value(childFolderIdPos).toULongLong(), db, type);
}
}
// loads
Folder DBHelper::loadFolder(qulonglong id, QSqlDatabase &db)
{
Folder folder;
QSqlQuery query(db);
query.prepare("SELECT * FROM folder WHERE id = :id");
query.bindValue(":id", id);
query.exec();
Folder folder;
folder.id = id;
folder.parentId = 0;
DBHelper::readFolderFromQuery(folder, query);
return folder;
}
Folder DBHelper::loadFolder(const QString &folderName, qulonglong parentId, QSqlDatabase &db)
{
QSqlQuery query(db);
query.prepare("SELECT * FROM folder WHERE parentId = :parentId AND name = :folderName");
query.bindValue(":parentId", parentId);
query.bindValue(":folderName", folderName);
query.exec();
Folder folder;
folder.parentId = parentId;
DBHelper::readFolderFromQuery(folder, query);
return folder;
}
void DBHelper::readFolderFromQuery(Folder &folder, QSqlQuery &query)
{
QSqlRecord record = query.record();
int id = record.indexOf("id");
int parentId = record.indexOf("parentId");
int name = record.indexOf("name");
int path = record.indexOf("path");
@ -1577,74 +1671,36 @@ Folder DBHelper::loadFolder(qulonglong id, QSqlDatabase &db)
int numChildren = record.indexOf("numChildren");
int firstChildHash = record.indexOf("firstChildHash");
int customImage = record.indexOf("customImage");
int type = record.indexOf("type");
int added = record.indexOf("added");
int updated = record.indexOf("updated");
if (query.next()) {
folder.id = query.value(id).toULongLong();
folder.parentId = query.value(parentId).toULongLong();
folder.name = query.value(name).toString();
folder.path = query.value(path).toString();
folder.knownId = true;
// new 7.1
folder.setFinished(query.value(finished).toBool());
folder.setCompleted(query.value(completed).toBool());
folder.finished = query.value(finished).toBool();
folder.completed = query.value(completed).toBool();
// new 9.5
if (!query.value(numChildren).isNull() && query.value(numChildren).isValid())
folder.setNumChildren(query.value(numChildren).toInt());
folder.setFirstChildHash(query.value(firstChildHash).toString());
folder.setCustomImage(query.value(customImage).toString());
if (!query.value(numChildren).isNull() && query.value(numChildren).isValid()) {
folder.numChildren = query.value(numChildren).toInt();
}
folder.firstChildHash = query.value(firstChildHash).toString();
folder.customImage = query.value(customImage).toString();
// new 9.8
folder.setManga(query.value(manga).toBool());
folder.manga = query.value(manga).toBool();
// new 9.13
folder.type = query.value(type).value<YACReader::FileType>();
folder.added = query.value(added).toLongLong();
folder.updated = query.value(updated).toLongLong();
}
return folder;
}
Folder DBHelper::loadFolder(const QString &folderName, qulonglong parentId, QSqlDatabase &db)
{
Folder folder;
QSqlQuery query(db);
query.prepare("SELECT * FROM folder WHERE parentId = :parentId AND name = :folderName");
query.bindValue(":parentId", parentId);
query.bindValue(":folderName", folderName);
query.exec();
QSqlRecord record = query.record();
int id = record.indexOf("id");
int name = record.indexOf("name");
int path = record.indexOf("path");
int finished = record.indexOf("finished");
int completed = record.indexOf("completed");
int manga = record.indexOf("manga");
int numChildren = record.indexOf("numChildren");
int firstChildHash = record.indexOf("firstChildHash");
int customImage = record.indexOf("customImage");
folder.parentId = parentId;
if (query.next()) {
folder.id = query.value(id).toULongLong();
folder.name = query.value(name).toString();
folder.path = query.value(path).toString();
folder.knownId = true;
// new 7.1
folder.setFinished(query.value(finished).toBool());
folder.setCompleted(query.value(completed).toBool());
// new 9.5
if (!query.value(numChildren).isNull() && query.value(numChildren).isValid())
folder.setNumChildren(query.value(numChildren).toInt());
folder.setFirstChildHash(query.value(firstChildHash).toString());
folder.setCustomImage(query.value(customImage).toString());
// new 9.8
folder.setManga(query.value(manga).toBool());
}
return folder;
}
ComicDB DBHelper::loadComic(qulonglong id, QSqlDatabase &db, bool &found)
@ -1776,6 +1832,22 @@ ComicInfo DBHelper::getComicInfoFromQuery(QSqlQuery &query, const QString &idKey
int coverSizeRatio = record.indexOf("coverSizeRatio");
int originalCoverSize = record.indexOf("originalCoverSize");
int added = record.indexOf("added");
int type = record.indexOf("type");
int editor = record.indexOf("editor");
int imprint = record.indexOf("imprint");
int teams = record.indexOf("teams");
int locations = record.indexOf("locations");
int series = record.indexOf("series");
int alternateSeries = record.indexOf("alternateSeries");
int alternateNumber = record.indexOf("alternateNumber");
int alternateCount = record.indexOf("alternateCount");
int languageISO = record.indexOf("languageISO");
int seriesGroup = record.indexOf("seriesGroup");
int mainCharacterOrTeam = record.indexOf("mainCharacterOrTeam");
int review = record.indexOf("review");
int tags = record.indexOf("tags");
ComicInfo comicInfo;
comicInfo.hash = query.value(hash).toString();
@ -1840,6 +1912,24 @@ ComicInfo DBHelper::getComicInfoFromQuery(QSqlQuery &query, const QString &idKey
comicInfo.manga = query.value(manga);
//--
// new 9.13 fields
comicInfo.added = query.value(added);
comicInfo.type = query.value(type);
comicInfo.editor = query.value(editor);
comicInfo.imprint = query.value(imprint);
comicInfo.teams = query.value(teams);
comicInfo.locations = query.value(locations);
comicInfo.series = query.value(series);
comicInfo.alternateSeries = query.value(alternateSeries);
comicInfo.alternateNumber = query.value(alternateNumber);
comicInfo.alternateCount = query.value(alternateCount);
comicInfo.languageISO = query.value(languageISO);
comicInfo.seriesGroup = query.value(seriesGroup);
comicInfo.mainCharacterOrTeam = query.value(mainCharacterOrTeam);
comicInfo.review = query.value(review);
comicInfo.tags = query.value(tags);
//--
comicInfo.existOnDb = true;
return comicInfo;

View File

@ -65,7 +65,7 @@ public:
static void update(ComicDB *comics, QSqlDatabase &db);
static void update(ComicInfo *comicInfo, QSqlDatabase &db);
static void updateRead(ComicInfo *comicInfo, QSqlDatabase &db);
static void update(const Folder &folder, QSqlDatabase &db);
static void update(const Folder &folder, QSqlDatabase &db); // only for finished/completed fields
static void propagateFolderUpdatesToParent(const Folder &folder, QSqlDatabase &db);
static Folder updateChildrenInfo(qulonglong folderId, QSqlDatabase &db);
static void updateChildrenInfo(QSqlDatabase &db);
@ -89,11 +89,12 @@ public:
static QList<LibraryItem *> getComicsFromParent(qulonglong parentId, QSqlDatabase &db, bool sort = true);
static QList<Label> getLabels(qulonglong libraryId);
static void updateFolderTreeManga(qulonglong id, QSqlDatabase &db, bool manga);
static void updateFolderTreeType(qulonglong id, QSqlDatabase &db, YACReader::FileType type);
// load
static Folder loadFolder(qulonglong id, QSqlDatabase &db);
static Folder loadFolder(const QString &folderName, qulonglong parentId, QSqlDatabase &db);
static void readFolderFromQuery(Folder &folder, QSqlQuery &query);
static ComicDB loadComic(qulonglong id, QSqlDatabase &db, bool &found);
static ComicDB loadComic(QString cname, QString cpath, QString chash, QSqlDatabase &database);
static ComicInfo loadComicInfo(QString hash, QSqlDatabase &db);

View File

@ -13,7 +13,7 @@
using namespace YACReader;
FolderContentView::FolderContentView(QWidget *parent)
FolderContentView::FolderContentView(QAction *toogleRecentVisibilityAction, QWidget *parent)
: QWidget { parent }, parent(QModelIndex()), comicModel(new ComicModel()), folderModel(new FolderModel())
{
qmlRegisterType<FolderModel>("com.yacreader.FolderModel", 1, 0, "FolderModel");
@ -59,7 +59,11 @@ FolderContentView::FolderContentView(QWidget *parent)
connect(coverSizeSlider, &QAbstractSlider::valueChanged, this, &FolderContentView::setCoversSize);
toolbar = new QToolBar();
toolbar->setStyleSheet("QToolBar {border: none;}");
toolbar->setIconSize(QSize(18, 18));
toolbar->addWidget(new YACReaderToolBarStretch);
toolbar->addAction(toogleRecentVisibilityAction);
toolbar->addSeparator();
toolbar->addWidget(coverSizeSliderWidget);
auto l = new QVBoxLayout;
@ -205,6 +209,16 @@ void FolderContentView::reloadContinueReadingModel()
}
}
void FolderContentView::setShowRecent(bool visible)
{
folderModel->setShowRecent(visible);
}
void FolderContentView::setRecentRange(int days)
{
folderModel->setRecentRange(days);
}
void FolderContentView::openFolder(int index)
{
emit subfolderSelected(this->parent, index);

View File

@ -19,10 +19,12 @@ class FolderContentView : public QWidget
{
Q_OBJECT
public:
explicit FolderContentView(QWidget *parent = nullptr);
explicit FolderContentView(QAction *toogleRecentVisibilityAction, QWidget *parent = nullptr);
void setModel(const QModelIndex &parent, FolderModel *model);
void setContinueReadingModel(ComicModel *model);
void reloadContinueReadingModel();
void setShowRecent(bool visible);
void setRecentRange(int days);
FolderModel *currentFolderModel() { return folderModel; }
signals:

View File

@ -170,7 +170,6 @@ GridComicsView::~GridComicsView()
void GridComicsView::createCoverSizeSliderWidget()
{
toolBarStretch = new YACReaderToolBarStretch(this);
coverSizeSliderWidget = new QWidget(this);
coverSizeSliderWidget->setFixedWidth(200);
coverSizeSlider = new QSlider();
@ -206,7 +205,7 @@ void GridComicsView::setToolBar(QToolBar *toolBar)
createCoverSizeSliderWidget();
toolBarStretchAction = toolBar->addWidget(toolBarStretch);
startSeparatorAction = toolBar->addSeparator();
toolBar->addAction(showInfoAction);
showInfoSeparatorAction = toolBar->addSeparator();
coverSizeSliderAction = toolBar->addWidget(coverSizeSliderWidget);
@ -553,7 +552,7 @@ void GridComicsView::setShowMarks(bool show)
void GridComicsView::closeEvent(QCloseEvent *event)
{
toolbar->removeAction(toolBarStretchAction);
toolbar->removeAction(startSeparatorAction);
toolbar->removeAction(showInfoAction);
toolbar->removeAction(showInfoSeparatorAction);
toolbar->removeAction(coverSizeSliderAction);

View File

@ -97,13 +97,12 @@ signals:
private:
QSettings *settings;
QToolBar *toolbar;
YACReaderToolBarStretch *toolBarStretch;
QAction *toolBarStretchAction;
QWidget *coverSizeSliderWidget;
QSlider *coverSizeSlider;
QAction *coverSizeSliderAction;
QAction *showInfoAction;
QAction *showInfoSeparatorAction;
QAction *startSeparatorAction;
bool filterEnabled;

View File

@ -25,6 +25,7 @@
<file>../images/comics_view_toolbar/show_comic_info.svg</file>
<file>../images/comics_view_toolbar/setManga.svg</file>
<file>../images/comics_view_toolbar/setNormal.svg</file>
<file>../images/comics_view_toolbar/showRecentIndicator.svg</file>
<file>../images/defaultCover.png</file>
<file>../images/edit.png</file>
<file>../images/empty_current_readings.png</file>

View File

@ -232,7 +232,8 @@ qulonglong LibraryCreator::insertFolders()
for (i = _currentPathFolders.begin(); i != _currentPathFolders.end(); ++i) {
if (!(i->knownId)) {
i->setFather(currentId);
i->setManga(currentParent.isManga());
i->manga = currentParent.manga;
i->type = currentParent.type;
currentId = DBHelper::insert(&(*i), _database); // insertFolder(currentId,*i);
i->setId(currentId);
} else {
@ -325,7 +326,8 @@ void LibraryCreator::insertComic(const QString &relativePath, const QFileInfo &f
}
comic.parentId = _currentPathFolders.last().id;
comic.info.manga = _currentPathFolders.last().isManga();
comic.info.manga = _currentPathFolders.last().manga;
comic.info.type = QVariant::fromValue(_currentPathFolders.last().type); // TODO_METADATA test this
DBHelper::insert(&comic, _database, parsed);
}
@ -539,14 +541,24 @@ void LibraryCreator::update(QDir dirS)
} else // same file
{
if (fileInfoS.isFile() && !fileInfoD->isDir()) {
// TODO comprobar fechas + tamaño
// if(fileInfoS.lastModified()>fileInfoD.lastModified())
//{
// dirD.mkpath(_target+(QDir::cleanPath(fileInfoS.absolutePath()).remove(_source)));
// emit(coverExtracted(QDir::cleanPath(fileInfoS.absoluteFilePath()).remove(_source)));
// ThumbnailCreator tc(QDir::cleanPath(fileInfoS.absoluteFilePath()),_target+(QDir::cleanPath(fileInfoS.absoluteFilePath()).remove(_source))+".jpg");
// tc.create();
// }
// TODO_METADATA use added,
// if added < modified, do something
// copy metadata to avoid loosing it if the imported comics doesn't have it.
// DBHelper::removeFromDB(fileInfoD, _database);
// #ifdef Q_OS_MAC
// QStringList src = _source.split("/");
// QString filePath = fileInfoS.absoluteFilePath();
// QStringList fp = filePath.split("/");
// for (int i = 0; i < src.count(); i++) {
// fp.removeFirst();
// }
// QString path = "/" + fp.join("/");
// #else
// QString path = QDir::cleanPath(fileInfoS.absoluteFilePath()).remove(_source);
// #endif
// insertComic(path, fileInfoS);
}
i++;
j++;

View File

@ -1,5 +1,4 @@
#include "library_window.h"
#include "custom_widgets.h"
#include "folder_item.h"
#include <QHBoxLayout>
@ -31,7 +30,6 @@
#include "library_creator.h"
#include "package_manager.h"
#include "xml_info_library_scanner.h"
#include "comic_flow_widget.h"
#include "create_library_dialog.h"
#include "rename_library_dialog.h"
#include "properties_dialog.h"
@ -45,9 +43,7 @@
#include "server_config_dialog.h"
#include "comic_model.h"
#include "yacreader_tool_bar_stretch.h"
#include "yacreader_table_view.h"
#include "yacreader_dark_menu.h"
#include "yacreader_titled_toolbar.h"
#include "yacreader_main_toolbar.h"
@ -87,6 +83,8 @@
#include "library_comic_opener.h"
#include "recent_visibility_coordinator.h"
#include "QsLog.h"
#include "yacreader_http_server.h"
@ -204,6 +202,8 @@ void LibraryWindow::setupUI()
createToolBars();
createMenus();
setupCoordinators();
navigationController = new YACReaderNavigationController(this, contentViewsManager);
createConnections();
@ -222,24 +222,6 @@ void LibraryWindow::setupUI()
trayIconController = new TrayIconController(settings, this);
}
/*void LibraryWindow::changeEvent(QEvent *event)
{
QMainWindow::changeEvent(event);
if (event->type() == QEvent::WindowStateChange && isMinimized() &&
trayIcon.isVisible()) {
#ifdef Q_OS_MACOS
OSXHideDockIcon();
#endif
hide();
} else if (event->type() == QEvent::WindowStateChange) {
#ifdef Q_OS_MACOS
OSXShowDockIcon();
#endif
show();
}
}*/
void LibraryWindow::doLayout()
{
// LAYOUT ELEMENTS------------------------------------------------------------
@ -461,6 +443,7 @@ void LibraryWindow::setUpShortcutsManagement()
editShortcutsDialog->addActionsGroup("Visualization", QIcon(":/images/shortcuts_group_visualization.svg"),
tmpList = QList<QAction *>()
<< showHideMarksAction
<< toogleShowRecentIndicatorAction
#ifndef Q_OS_MAC
<< toggleFullScreenAction
#endif
@ -485,6 +468,11 @@ void LibraryWindow::doModels()
listsModelProxy = new ReadingListModelProxy(this);
}
void LibraryWindow::setupCoordinators()
{
recentVisibilityCoordinator = new RecentVisibilityCoordinator(settings, foldersModel, contentViewsManager->folderContentView, comicsModel);
}
void LibraryWindow::createActions()
{
backAction = new QAction(this);
@ -587,25 +575,34 @@ void LibraryWindow::createActions()
setAsNonReadAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(SET_AS_NON_READ_ACTION_YL));
setAsNonReadAction->setIcon(QIcon(":/images/comics_view_toolbar/setUnread.svg"));
setMangaAction = new QAction(tr("Set as manga"), this);
setMangaAction = new QAction(tr("manga"), this);
setMangaAction->setToolTip(tr("Set issue as manga"));
setMangaAction->setData(SET_AS_MANGA_ACTION_YL);
setMangaAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(SET_AS_MANGA_ACTION_YL));
setMangaAction->setIcon(QIcon(":/images/comics_view_toolbar/setManga.svg"));
setNormalAction = new QAction(tr("Set as normal"), this);
setNormalAction = new QAction(tr("comic"), this);
setNormalAction->setToolTip(tr("Set issue as normal"));
setNormalAction->setData(SET_AS_NORMAL_ACTION_YL);
setNormalAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(SET_AS_NORMAL_ACTION_YL));
setNormalAction->setIcon(QIcon(":/images/comics_view_toolbar/setNormal.svg"));
/*setAllAsReadAction = new QAction(tr("Set all as read"),this);
setAllAsReadAction->setToolTip(tr("Set all comics as read"));
setAllAsReadAction->setIcon(QIcon(":/images/comics_view_toolbar/setAllRead.png"));
setWesternMangaAction = new QAction(tr("western manga"), this);
setWesternMangaAction->setToolTip(tr("Set issue as western manga"));
setWesternMangaAction->setData(SET_AS_WESTERN_MANGA_ACTION_YL);
setWesternMangaAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(SET_AS_WESTERN_MANGA_ACTION_YL));
// setWesternMangaAction->setIcon(QIcon(":/images/comics_view_toolbar/setWesternManga.svg"));
setAllAsNonReadAction = new QAction(tr("Set all as unread"),this);
setAllAsNonReadAction->setToolTip(tr("Set all comics as unread"));
setAllAsNonReadAction->setIcon(QIcon(":/images/comics_view_toolbar/setAllUnread.png"));*/
setWebComicAction = new QAction(tr("web comic"), this);
setWebComicAction->setToolTip(tr("Set issue as web comic"));
setWebComicAction->setData(SET_AS_WEB_COMIC_ACTION_YL);
setWebComicAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(SET_AS_WEB_COMIC_ACTION_YL));
// setWebComicAction->setIcon(QIcon(":/images/comics_view_toolbar/setWebComic.svg"));
setYonkomaAction = new QAction(tr("yonkoma"), this);
setYonkomaAction->setToolTip(tr("Set issue as yonkoma"));
setYonkomaAction->setData(SET_AS_YONKOMA_ACTION_YL);
setYonkomaAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(SET_AS_YONKOMA_ACTION_YL));
showHideMarksAction = new QAction(tr("Show/Hide marks"), this);
showHideMarksAction->setToolTip(tr("Show or hide read marks"));
@ -614,6 +611,15 @@ void LibraryWindow::createActions()
showHideMarksAction->setCheckable(true);
showHideMarksAction->setIcon(QIcon(":/images/comics_view_toolbar/showMarks.svg"));
showHideMarksAction->setChecked(true);
toogleShowRecentIndicatorAction = new QAction(tr("Show/Hide recent indicator"), this);
toogleShowRecentIndicatorAction->setToolTip(tr("Show or hide recent indicator"));
toogleShowRecentIndicatorAction->setData(SHOW_HIDE_RECENT_INDICATOR_ACTION_YL);
toogleShowRecentIndicatorAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(SHOW_HIDE_RECENT_INDICATOR_ACTION_YL));
toogleShowRecentIndicatorAction->setCheckable(true);
toogleShowRecentIndicatorAction->setIcon(QIcon(":/images/comics_view_toolbar/showRecentIndicator.svg"));
toogleShowRecentIndicatorAction->setChecked(settings->value(DISPLAY_RECENTLY_INDICATOR, true).toBool());
#ifndef Q_OS_MAC
toggleFullScreenAction = new QAction(tr("Fullscreen mode on/off"), this);
toggleFullScreenAction->setToolTip(tr("Fullscreen mode on/off"));
@ -722,15 +728,30 @@ void LibraryWindow::createActions()
setFolderAsUnreadAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(SET_FOLDER_AS_UNREAD_ACTION_YL));
setFolderAsMangaAction = new QAction(this);
setFolderAsMangaAction->setText(tr("Set as manga"));
setFolderAsMangaAction->setText(tr("manga"));
setFolderAsMangaAction->setData(SET_FOLDER_AS_MANGA_ACTION_YL);
setFolderAsMangaAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(SET_FOLDER_AS_MANGA_ACTION_YL));
setFolderAsNormalAction = new QAction(this);
setFolderAsNormalAction->setText(tr("Set as comic"));
setFolderAsNormalAction->setText(tr("comic"));
setFolderAsNormalAction->setData(SET_FOLDER_AS_NORMAL_ACTION_YL);
setFolderAsNormalAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(SET_FOLDER_AS_NORMAL_ACTION_YL));
setFolderAsWesternMangaAction = new QAction(this);
setFolderAsWesternMangaAction->setText(tr("western manga (left to right)"));
setFolderAsWesternMangaAction->setData(SET_FOLDER_AS_WESTERN_MANGA_ACTION_YL);
setFolderAsWesternMangaAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(SET_FOLDER_AS_WESTERN_MANGA_ACTION_YL));
setFolderAsWebComicAction = new QAction(this);
setFolderAsWebComicAction->setText(tr("web comic"));
setFolderAsWebComicAction->setData(SET_FOLDER_AS_WEB_COMIC_ACTION_YL);
setFolderAsWebComicAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(SET_FOLDER_AS_WEB_COMIC_ACTION_YL));
setFolderAsYonkomaAction = new QAction(this);
setFolderAsYonkomaAction->setText(tr("yonkoma"));
setFolderAsYonkomaAction->setData(SET_FOLDER_AS_YONKOMA_ACTION_YL);
setFolderAsYonkomaAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(SET_FOLDER_AS_YONKOMA_ACTION_YL));
//----
openContainingFolderComicAction = new QAction(this);
@ -867,6 +888,9 @@ void LibraryWindow::createActions()
this->addAction(setFolderAsUnreadAction);
this->addAction(setFolderAsMangaAction);
this->addAction(setFolderAsNormalAction);
this->addAction(setFolderAsWesternMangaAction);
this->addAction(setFolderAsWebComicAction);
this->addAction(setFolderAsYonkomaAction);
this->addAction(deleteMetadataAction);
this->addAction(rescanXMLFromCurrentFolderAction);
#ifndef Q_OS_MAC
@ -891,6 +915,9 @@ void LibraryWindow::disableComicsActions(bool disabled)
setAsNonReadAction->setDisabled(disabled);
setNormalAction->setDisabled(disabled);
setMangaAction->setDisabled(disabled);
setWebComicAction->setDisabled(disabled);
setWesternMangaAction->setDisabled(disabled);
setYonkomaAction->setDisabled(disabled);
// setAllAsReadAction->setDisabled(disabled);
// setAllAsNonReadAction->setDisabled(disabled);
showHideMarksAction->setDisabled(disabled);
@ -1001,21 +1028,31 @@ void LibraryWindow::createToolBars()
editInfoToolBar->addSeparator();
editInfoToolBar->addAction(setAsReadAction);
// editInfoToolBar->addAction(setAllAsReadAction);
editInfoToolBar->addAction(setAsNonReadAction);
// editInfoToolBar->addAction(setAllAsNonReadAction);
editInfoToolBar->addAction(showHideMarksAction);
editInfoToolBar->addSeparator();
editInfoToolBar->addAction(setNormalAction);
editInfoToolBar->addAction(setMangaAction);
auto setTypeToolButton = new QToolButton();
setTypeToolButton->addAction(setNormalAction);
setTypeToolButton->addAction(setMangaAction);
setTypeToolButton->addAction(setWesternMangaAction);
setTypeToolButton->addAction(setWebComicAction);
setTypeToolButton->addAction(setYonkomaAction);
setTypeToolButton->setPopupMode(QToolButton::InstantPopup);
setTypeToolButton->setDefaultAction(setNormalAction);
editInfoToolBar->addWidget(setTypeToolButton);
editInfoToolBar->addSeparator();
editInfoToolBar->addAction(deleteComicsAction);
auto toolBarStretch = new YACReaderToolBarStretch(this);
editInfoToolBar->addWidget(toolBarStretch);
editInfoToolBar->addAction(toogleShowRecentIndicatorAction);
contentViewsManager->comicsView->setToolBar(editInfoToolBar);
}
@ -1037,8 +1074,11 @@ void LibraryWindow::createMenus()
foldersView->addAction(setFolderAsUnreadAction);
YACReader::addSperator(foldersView);
foldersView->addAction(setFolderAsMangaAction);
foldersView->addAction(setFolderAsNormalAction);
foldersView->addAction(setFolderAsMangaAction);
foldersView->addAction(setFolderAsWesternMangaAction);
foldersView->addAction(setFolderAsWebComicAction);
foldersView->addAction(setFolderAsYonkomaAction);
selectedLibrary->addAction(updateLibraryAction);
selectedLibrary->addAction(renameLibraryAction);
@ -1093,8 +1133,11 @@ void LibraryWindow::createMenus()
folderMenu->addAction(setFolderAsReadAction);
folderMenu->addAction(setFolderAsUnreadAction);
folderMenu->addSeparator();
foldersView->addAction(setFolderAsMangaAction);
foldersView->addAction(setFolderAsNormalAction);
foldersView->addAction(setFolderAsMangaAction);
foldersView->addAction(setFolderAsWesternMangaAction);
foldersView->addAction(setFolderAsWebComicAction);
foldersView->addAction(setFolderAsYonkomaAction);
// comic
QMenu *comicMenu = new QMenu(tr("Comic"));
@ -1133,7 +1176,7 @@ void LibraryWindow::createConnections()
connect(libraryCreator, &LibraryCreator::comicAdded, importWidget, &ImportWidget::newComic);
// libraryCreator errors
connect(libraryCreator, &LibraryCreator::failedCreatingDB, this, &LibraryWindow::manageCreatingError);
connect(libraryCreator, SIGNAL(failedUpdatingDB(QString)), this, SLOT(manageUpdatingError(QString))); // TODO: implement failedUpdatingDB
// connect(libraryCreator, SIGNAL(failedUpdatingDB(QString)), this, SLOT(manageUpdatingError(QString))); // TODO: implement failedUpdatingDB
connect(xmlInfoLibraryScanner, &QThread::finished, this, &LibraryWindow::showRootWidget);
connect(xmlInfoLibraryScanner, &QThread::finished, this, &LibraryWindow::reloadCurrentFolderComicsContent);
@ -1185,10 +1228,22 @@ void LibraryWindow::createConnections()
connect(openLibraryAction, &QAction::triggered, this, &LibraryWindow::showAddLibrary);
connect(setAsReadAction, &QAction::triggered, this, &LibraryWindow::setCurrentComicReaded);
connect(setAsNonReadAction, &QAction::triggered, this, &LibraryWindow::setCurrentComicUnreaded);
connect(setNormalAction, &QAction::triggered, this, &LibraryWindow::setSelectedComicsAsNormal);
connect(setMangaAction, &QAction::triggered, this, &LibraryWindow::setSelectedComicsAsManga);
// connect(setAllAsReadAction,SIGNAL(triggered()),this,SLOT(setComicsReaded()));
// connect(setAllAsNonReadAction,SIGNAL(triggered()),this,SLOT(setComicsUnreaded()));
connect(setNormalAction, &QAction::triggered, this, [=]() {
setSelectedComicsType(FileType::Comic);
});
connect(setMangaAction, &QAction::triggered, this, [=]() {
setSelectedComicsType(FileType::Manga);
});
connect(setWesternMangaAction, &QAction::triggered, this, [=]() {
setSelectedComicsType(FileType::WesternManga);
});
connect(setWebComicAction, &QAction::triggered, this, [=]() {
setSelectedComicsType(FileType::WebComic);
});
connect(setYonkomaAction, &QAction::triggered, this, [=]() {
setSelectedComicsType(FileType::Yonkoma);
});
// comicsInfoManagement
connect(exportComicsInfoAction, &QAction::triggered, this, &LibraryWindow::showExportComicsInfo);
@ -1241,8 +1296,22 @@ void LibraryWindow::createConnections()
connect(setFolderAsReadAction, &QAction::triggered, this, &LibraryWindow::setFolderAsRead);
connect(setFolderAsUnreadAction, &QAction::triggered, this, &LibraryWindow::setFolderAsUnread);
connect(openContainingFolderAction, &QAction::triggered, this, &LibraryWindow::openContainingFolder);
connect(setFolderAsMangaAction, &QAction::triggered, this, &LibraryWindow::setFolderAsManga);
connect(setFolderAsNormalAction, &QAction::triggered, this, &LibraryWindow::setFolderAsNormal);
connect(setFolderAsMangaAction, &QAction::triggered, this, [=]() {
setFolderType(FileType::Manga);
});
connect(setFolderAsNormalAction, &QAction::triggered, this, [=]() {
setFolderType(FileType::Comic);
});
connect(setFolderAsWesternMangaAction, &QAction::triggered, this, [=]() {
setFolderType(FileType::WesternManga);
});
connect(setFolderAsWebComicAction, &QAction::triggered, this, [=]() {
setFolderType(FileType::WebComic);
});
connect(setFolderAsYonkomaAction, &QAction::triggered, this, [=]() {
setFolderType(FileType::Yonkoma);
});
connect(resetComicRatingAction, &QAction::triggered, this, &LibraryWindow::resetComicRating);
@ -1288,6 +1357,8 @@ void LibraryWindow::createConnections()
// upgrade library
connect(this, &LibraryWindow::libraryUpgraded, this, &LibraryWindow::loadLibrary, Qt::QueuedConnection);
connect(this, &LibraryWindow::errorUpgradingLibrary, this, &LibraryWindow::showErrorUpgradingLibrary, Qt::QueuedConnection);
connect(toogleShowRecentIndicatorAction, &QAction::toggled, recentVisibilityCoordinator, &RecentVisibilityCoordinator::toggleVisibility);
}
void LibraryWindow::showErrorUpgradingLibrary(const QString &path)
@ -1582,12 +1653,18 @@ void LibraryWindow::reloadAfterCopyMove(const QModelIndex &mi)
{
if (getCurrentFolderIndex() == mi) {
auto item = static_cast<FolderItem *>(mi.internalPointer());
auto id = item->id;
foldersModel->reload(mi);
auto newMi = foldersModel->index(id);
foldersView->setCurrentIndex(foldersModelProxy->mapFromSource(newMi));
navigationController->loadFolderInfo(newMi);
if (item == nullptr) {
foldersModel->reload();
navigationController->loadFolderInfo(QModelIndex());
} else {
auto id = item->id;
foldersModel->reload(mi);
auto newMi = foldersModel->index(id);
foldersView->setCurrentIndex(foldersModelProxy->mapFromSource(newMi));
navigationController->loadFolderInfo(newMi);
}
}
enableNeededActions();
@ -1779,8 +1856,13 @@ void LibraryWindow::showComicsViewContextMenu(const QPoint &point)
menu.addAction(setAsReadAction);
menu.addAction(setAsNonReadAction);
menu.addSeparator();
menu.addAction(setNormalAction);
menu.addAction(setMangaAction);
auto typeMenu = new QMenu(tr("Set type"));
menu.addMenu(typeMenu);
typeMenu->addAction(setNormalAction);
typeMenu->addAction(setMangaAction);
typeMenu->addAction(setWesternMangaAction);
typeMenu->addAction(setWebComicAction);
typeMenu->addAction(setYonkomaAction);
menu.addSeparator();
menu.addAction(deleteMetadataAction);
menu.addSeparator();
@ -1817,8 +1899,13 @@ void LibraryWindow::showComicsItemContextMenu(const QPoint &point)
menu.addAction(setAsReadAction);
menu.addAction(setAsNonReadAction);
menu.addSeparator();
menu.addAction(setNormalAction);
menu.addAction(setMangaAction);
auto typeMenu = new QMenu(tr("Set type"));
menu.addMenu(typeMenu);
typeMenu->addAction(setNormalAction);
typeMenu->addAction(setMangaAction);
typeMenu->addAction(setWesternMangaAction);
typeMenu->addAction(setWebComicAction);
typeMenu->addAction(setYonkomaAction);
menu.addSeparator();
menu.addAction(deleteMetadataAction);
menu.addSeparator();
@ -1857,30 +1944,43 @@ void LibraryWindow::showGridFoldersContextMenu(QPoint point, Folder folder)
setFolderAsUnreadAction->setText(tr("Set as unread"));
auto setFolderAsMangaAction = new QAction();
setFolderAsMangaAction->setText(tr("Set as manga"));
setFolderAsMangaAction->setText(tr("manga"));
auto setFolderAsNormalAction = new QAction();
setFolderAsNormalAction->setText(tr("Set as comic"));
setFolderAsNormalAction->setText(tr("comic"));
auto setFolderAsWesternMangaAction = new QAction();
setFolderAsMangaAction->setText(tr("manga (or left to right)"));
auto setFolderAsWebComicAction = new QAction();
setFolderAsNormalAction->setText(tr("web comic"));
auto setFolderAs4KomaAction = new QAction();
setFolderAsMangaAction->setText(tr("4koma (or top to botom"));
menu.addAction(openContainingFolderAction);
menu.addAction(updateFolderAction);
menu.addSeparator();
menu.addAction(rescanLibraryForXMLInfoAction);
menu.addSeparator();
if (folder.isCompleted())
if (folder.completed)
menu.addAction(setFolderAsNotCompletedAction);
else
menu.addAction(setFolderAsCompletedAction);
menu.addSeparator();
if (folder.isFinished())
if (folder.finished)
menu.addAction(setFolderAsUnreadAction);
else
menu.addAction(setFolderAsReadAction);
menu.addSeparator();
if (folder.isManga())
menu.addAction(setFolderAsNormalAction);
else
menu.addAction(setFolderAsMangaAction);
auto typeMenu = new QMenu(tr("Set type"));
menu.addMenu(typeMenu);
typeMenu->addAction(setFolderAsNormalAction);
typeMenu->addAction(setFolderAsMangaAction);
typeMenu->addAction(setFolderAsWesternMangaAction);
typeMenu->addAction(setFolderAsWebComicAction);
typeMenu->addAction(setFolderAs4KomaAction);
auto subfolderModel = contentViewsManager->folderContentView->currentFolderModel();
@ -1910,12 +2010,24 @@ void LibraryWindow::showGridFoldersContextMenu(QPoint point, Folder folder)
subfolderModel->updateFolderFinishedStatus(QModelIndexList() << subfolderModel->getIndexFromFolder(folder), false);
});
connect(setFolderAsMangaAction, &QAction::triggered, this, [=]() {
foldersModel->updateFolderManga(QModelIndexList() << foldersModel->getIndexFromFolder(folder), true);
subfolderModel->updateFolderManga(QModelIndexList() << subfolderModel->getIndexFromFolder(folder), true);
foldersModel->updateFolderType(QModelIndexList() << foldersModel->getIndexFromFolder(folder), FileType::Manga);
subfolderModel->updateFolderType(QModelIndexList() << foldersModel->getIndexFromFolder(folder), FileType::Manga);
});
connect(setFolderAsNormalAction, &QAction::triggered, this, [=]() {
foldersModel->updateFolderManga(QModelIndexList() << foldersModel->getIndexFromFolder(folder), false);
subfolderModel->updateFolderManga(QModelIndexList() << subfolderModel->getIndexFromFolder(folder), false);
foldersModel->updateFolderType(QModelIndexList() << foldersModel->getIndexFromFolder(folder), FileType::Comic);
subfolderModel->updateFolderType(QModelIndexList() << foldersModel->getIndexFromFolder(folder), FileType::Comic);
});
connect(setFolderAsWesternMangaAction, &QAction::triggered, this, [=]() {
foldersModel->updateFolderType(QModelIndexList() << foldersModel->getIndexFromFolder(folder), FileType::WesternManga);
subfolderModel->updateFolderType(QModelIndexList() << foldersModel->getIndexFromFolder(folder), FileType::WesternManga);
});
connect(setFolderAsWebComicAction, &QAction::triggered, this, [=]() {
foldersModel->updateFolderType(QModelIndexList() << foldersModel->getIndexFromFolder(folder), FileType::WebComic);
subfolderModel->updateFolderType(QModelIndexList() << foldersModel->getIndexFromFolder(folder), FileType::WebComic);
});
connect(setFolderAs4KomaAction, &QAction::triggered, this, [=]() {
foldersModel->updateFolderType(QModelIndexList() << foldersModel->getIndexFromFolder(folder), FileType::Yonkoma);
subfolderModel->updateFolderType(QModelIndexList() << foldersModel->getIndexFromFolder(folder), FileType::Yonkoma);
});
menu.exec(contentViewsManager->folderContentView->mapToGlobal(point));
@ -2091,14 +2203,9 @@ void LibraryWindow::setCurrentComicUnreaded()
this->setCurrentComicsStatusReaded(YACReader::Unread);
}
void LibraryWindow::setSelectedComicsAsNormal()
void LibraryWindow::setSelectedComicsType(FileType type)
{
comicsModel->setComicsManga(getSelectedComics(), false);
}
void LibraryWindow::setSelectedComicsAsManga()
{
comicsModel->setComicsManga(getSelectedComics(), true);
comicsModel->setComicsType(getSelectedComics(), type);
}
void LibraryWindow::createLibrary()
@ -2109,7 +2216,7 @@ void LibraryWindow::createLibrary()
void LibraryWindow::create(QString source, QString dest, QString name)
{
QLOG_INFO() << QString("About to create a library from '%1' to '%2' with name '%3'").arg(source).arg(dest).arg(name);
QLOG_INFO() << QString("About to create a library from '%1' to '%2' with name '%3'").arg(source, dest, name);
libraryCreator->createLibrary(source, dest);
libraryCreator->start();
_lastAdded = name;
@ -2625,14 +2732,9 @@ void LibraryWindow::setFolderAsUnread()
foldersModel->updateFolderFinishedStatus(QModelIndexList() << foldersModelProxy->mapToSource(foldersView->currentIndex()), false);
}
void LibraryWindow::setFolderAsManga()
void LibraryWindow::setFolderType(FileType type)
{
foldersModel->updateFolderManga(QModelIndexList() << foldersModelProxy->mapToSource(foldersView->currentIndex()), true);
}
void LibraryWindow::setFolderAsNormal()
{
foldersModel->updateFolderManga(QModelIndexList() << foldersModelProxy->mapToSource(foldersView->currentIndex()), false);
foldersModel->updateFolderType(QModelIndexList() << foldersModelProxy->mapToSource(foldersView->currentIndex()), type);
}
void LibraryWindow::exportLibrary(QString destPath)
@ -2654,6 +2756,8 @@ void LibraryWindow::reloadOptions()
contentViewsManager->comicsView->updateConfig(settings);
trayIconController->updateIconVisibility();
recentVisibilityCoordinator->updateTimeRange();
}
QString LibraryWindow::currentPath()
@ -2872,10 +2976,9 @@ void LibraryWindow::showFoldersContextMenu(const QPoint &point)
bool isCompleted = sourceMI.data(FolderModel::CompletedRole).toBool();
bool isRead = sourceMI.data(FolderModel::FinishedRole).toBool();
bool isManga = sourceMI.data(FolderModel::MangaRole).toBool();
QMenu menu;
// QMenu * folderMenu = new QMenu(tr("Folder"));
menu.addAction(openContainingFolderAction);
menu.addAction(updateFolderAction);
menu.addSeparator(); //-------------------------------
@ -2891,10 +2994,13 @@ void LibraryWindow::showFoldersContextMenu(const QPoint &point)
else
menu.addAction(setFolderAsReadAction);
menu.addSeparator(); //-------------------------------
if (isManga)
menu.addAction(setFolderAsNormalAction);
else
menu.addAction(setFolderAsMangaAction);
auto typeMenu = new QMenu(tr("Set type"));
menu.addMenu(typeMenu);
typeMenu->addAction(setFolderAsNormalAction);
typeMenu->addAction(setFolderAsMangaAction);
typeMenu->addAction(setFolderAsWesternMangaAction);
typeMenu->addAction(setFolderAsWebComicAction);
typeMenu->addAction(setFolderAsYonkomaAction);
menu.exec(foldersView->mapToGlobal(point));
}

View File

@ -81,6 +81,7 @@ class YACReaderHistoryController;
class EmptyLabelWidget;
class EmptySpecialListWidget;
class EmptyReadingListWidget;
class RecentVisibilityCoordinator;
namespace YACReader {
class TrayIconController;
@ -197,8 +198,12 @@ public:
//--
QAction *setFolderAsReadAction;
QAction *setFolderAsUnreadAction;
//--
QAction *setFolderAsMangaAction;
QAction *setFolderAsNormalAction;
QAction *setFolderAsWesternMangaAction;
QAction *setFolderAsWebComicAction;
QAction *setFolderAsYonkomaAction;
QAction *openContainingFolderComicAction;
QAction *setAsReadAction;
@ -206,13 +211,16 @@ public:
QAction *setMangaAction;
QAction *setNormalAction;
QAction *setWesternMangaAction;
QAction *setWebComicAction;
QAction *setYonkomaAction;
// QAction * setAllAsReadAction;
// QAction * setAllAsNonReadAction;
QAction *showHideMarksAction;
QAction *getInfoAction; // comic vine
QAction *resetComicRatingAction;
QAction *toogleShowRecentIndicatorAction;
// edit info actions
QAction *selectAllComicsAction;
QAction *editSelectedComicsAction;
@ -259,10 +267,6 @@ public:
QString _lastAdded;
QString _sourceLastAdded;
// QModelIndex _rootIndex;
// QModelIndex _rootIndexCV;
// QModelIndex updateDestination;
quint64 _comicIdEdited;
enum NavigationStatus {
@ -283,6 +287,7 @@ public:
void doDialogs();
void setUpShortcutsManagement();
void doModels();
void setupCoordinators();
// ACTIONS MANAGEMENT
void disableComicsActions(bool disabled);
@ -291,9 +296,6 @@ public:
void disableFoldersActions(bool disabled);
void disableAllActions();
// void disableActions();
// void enableActions();
// void enableLibraryActions();
QString currentPath();
QString currentFolderPath();
@ -340,8 +342,7 @@ public slots:
void setFolderAsCompleted();
void setFolderAsRead();
void setFolderAsUnread();
void setFolderAsManga();
void setFolderAsNormal();
void setFolderType(FileType type);
void openContainingFolderComic();
void deleteCurrentLibrary();
void removeLibrary();
@ -368,8 +369,7 @@ public slots:
void setCurrentComicsStatusReaded(YACReaderComicReadStatus readStatus);
void setCurrentComicReaded();
void setCurrentComicUnreaded();
void setSelectedComicsAsNormal();
void setSelectedComicsAsManga();
void setSelectedComicsType(FileType type);
void showExportComicsInfo();
void showImportComicsInfo();
void asignNumbers();
@ -450,6 +450,8 @@ private:
TrayIconController *trayIconController;
ComicQueryResultProcessor comicQueryResultProcessor;
std::unique_ptr<FolderQueryResultProcessor> folderQueryResultProcessor;
RecentVisibilityCoordinator *recentVisibilityCoordinator;
};
#endif

View File

@ -86,6 +86,18 @@ OptionsDialog::OptionsDialog(QWidget *parent)
comicInfoXMLBoxLayout->addWidget(comicInfoXMLCheckbox);
comicInfoXMLBox->setLayout(comicInfoXMLBoxLayout);
auto recentlyAddedBox = new QGroupBox(tr("Consider 'recent' items added or updated since X days ago"));
recentIntervalSlider = new QSlider(Qt::Horizontal);
recentIntervalSlider->setRange(1, 30);
auto recentlyAddedLayout = new QHBoxLayout();
numDaysLabel = new QLabel();
numDaysLabel->setMidLineWidth(50);
recentlyAddedLayout->addWidget(numDaysLabel);
recentlyAddedLayout->addWidget(recentIntervalSlider);
recentlyAddedBox->setLayout(recentlyAddedLayout);
connect(recentIntervalSlider, &QAbstractSlider::valueChanged, this, &OptionsDialog::numDaysToConsiderRecentChanged);
// grid view background config
useBackgroundImageCheck = new QCheckBox(tr("Enable background image"));
@ -152,6 +164,7 @@ OptionsDialog::OptionsDialog(QWidget *parent)
generalLayout->addWidget(shortcutsBox);
generalLayout->addWidget(apiKeyBox);
generalLayout->addWidget(comicInfoXMLBox);
generalLayout->addWidget(recentlyAddedBox);
generalLayout->addStretch();
tabWidget->addTab(generalW, tr("General"));
@ -163,8 +176,6 @@ OptionsDialog::OptionsDialog(QWidget *parent)
layout->addWidget(tabWidget);
layout->addLayout(buttons);
setLayout(layout);
// restoreOptions(settings); //load options
// resize(200,0);
setModal(true);
setWindowTitle(tr("Options"));
@ -187,6 +198,8 @@ void OptionsDialog::restoreOptions(QSettings *settings)
comicInfoXMLCheckbox->setChecked(settings->value(IMPORT_COMIC_INFO_XML_METADATA, false).toBool());
recentIntervalSlider->setValue(settings->value(NUM_DAYS_TO_CONSIDER_RECENT, 1).toInt());
bool useBackgroundImage = settings->value(USE_BACKGROUND_IMAGE_IN_GRID_VIEW, true).toBool();
useBackgroundImageCheck->setChecked(useBackgroundImage);
@ -237,6 +250,15 @@ void OptionsDialog::useCurrentComicCoverCheckClicked(bool checked)
emit optionsChanged();
}
void OptionsDialog::numDaysToConsiderRecentChanged(int value)
{
settings->setValue(NUM_DAYS_TO_CONSIDER_RECENT, value);
numDaysLabel->setText(QString("%1").arg(value));
emit optionsChanged();
}
void OptionsDialog::resetToDefaults()
{
settings->setValue(OPACITY_BACKGROUND_IMAGE_IN_GRID_VIEW, 0.2);

View File

@ -24,6 +24,7 @@ private slots:
void backgroundImageOpacitySliderChanged(int value);
void backgroundImageBlurRadiusSliderChanged(int value);
void useCurrentComicCoverCheckClicked(bool checked);
void numDaysToConsiderRecentChanged(int value);
void resetToDefaults();
private:
@ -38,6 +39,8 @@ private:
QCheckBox *trayIconCheckbox;
QCheckBox *startToTrayCheckbox;
QCheckBox *comicInfoXMLCheckbox;
QSlider *recentIntervalSlider;
QLabel *numDaysLabel;
};
#endif

View File

@ -31,6 +31,7 @@ PropertiesDialog::PropertiesDialog(QWidget *parent)
createPublishingBox();
createButtonBox();
createPlotBox();
createNotesBox(); // review, notes, tags
createTabBar();
auto rootLayout = new QGridLayout;
@ -80,9 +81,10 @@ void PropertiesDialog::createTabBar()
{
tabBar = new QTabWidget;
tabBar->addTab(generalInfoBox, tr("General info"));
tabBar->addTab(plotBox, tr("Plot"));
tabBar->addTab(authorsBox, tr("Authors"));
tabBar->addTab(publishingBox, tr("Publishing"));
tabBar->addTab(plotBox, tr("Plot"));
tabBar->addTab(notesBox, tr("Notes"));
}
void PropertiesDialog::createCoverBox()
@ -140,38 +142,44 @@ void PropertiesDialog::createGeneralInfoBox()
generalInfoLayout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);
generalInfoLayout->setRowWrapPolicy(QFormLayout::WrapAllRows);
generalInfoLayout->addRow(tr("Series:"), series = new YACReaderFieldEdit());
generalInfoLayout->addRow(tr("Title:"), title = new YACReaderFieldEdit());
auto number = new QHBoxLayout;
number->addWidget(numberEdit = new YACReaderFieldEdit());
numberValidator.setBottom(0);
numberEdit->setValidator(&numberValidator);
number->addWidget(new QLabel("Bis:"));
number->addWidget(isBisCheck = new QCheckBox());
number->addWidget(new QLabel(tr("of:")));
number->addWidget(countEdit = new YACReaderFieldEdit());
countValidator.setBottom(0);
countEdit->setValidator(&countValidator);
number->addStretch(1);
/*generalInfoLayout->addRow(tr("&Issue number:"), );
generalInfoLayout->addRow(tr("&Bis:"), );*/
generalInfoLayout->addRow(tr("Issue number:"), number);
generalInfoLayout->addRow(tr("Volume:"), volumeEdit = new YACReaderFieldEdit());
auto arc = new QHBoxLayout;
arc->addWidget(storyArcEdit = new YACReaderFieldEdit());
arc->addWidget(storyArcEdit = new YACReaderFieldEdit(), 1);
storyArcEdit->setMinimumWidth(200);
arc->addWidget(new QLabel(tr("Arc number:")));
arc->addWidget(arcNumberEdit = new YACReaderFieldEdit());
arcNumberValidator.setBottom(0);
arcNumberEdit->setValidator(&arcNumberValidator);
arc->addWidget(new QLabel(tr("of:")));
arc->addWidget(arcCountEdit = new YACReaderFieldEdit());
arcCountValidator.setBottom(0);
arcCountEdit->setValidator(&arcCountValidator);
arc->addStretch(1);
generalInfoLayout->addRow(tr("Story arc:"), arc);
auto alternate = new QHBoxLayout;
alternate->addWidget(alternateSeriesEdit = new YACReaderFieldEdit(), 1);
alternateSeriesEdit->setMinimumWidth(200);
alternate->addWidget(new QLabel(tr("alt. number:")));
alternate->addWidget(alternateNumberEdit = new YACReaderFieldEdit());
alternate->addWidget(new QLabel(tr("of:")));
alternate->addWidget(alternateCountEdit = new YACReaderFieldEdit());
arcCountValidator.setBottom(0);
alternateCountEdit->setValidator(&arcCountValidator);
generalInfoLayout->addRow(tr("Alternate series:"), alternate);
generalInfoLayout->addRow(tr("Series Group:"), seriesGroupEdit = new YACReaderFieldEdit());
generalInfoLayout->addRow(tr("Genre:"), genereEdit = new YACReaderFieldEdit());
generalInfoLayout->addRow(tr("Size:"), size = new QLabel("size"));
@ -221,9 +229,20 @@ void PropertiesDialog::createAuthorsBox()
vr3->addWidget(coverArtist = new YACReaderFieldPlainTextEdit());
h3->addLayout(vr3);
auto h4 = new QHBoxLayout;
auto vl4 = new QVBoxLayout;
auto vr4 = new QVBoxLayout;
vl4->addWidget(new QLabel(tr("Editor(s):")));
vl4->addWidget(editor = new YACReaderFieldPlainTextEdit());
h4->addLayout(vl4);
vr4->addWidget(new QLabel(tr("Imprint:")));
vr4->addWidget(imprint = new YACReaderFieldPlainTextEdit());
h4->addLayout(vr4);
authorsLayout->addLayout(h1);
authorsLayout->addLayout(h2);
authorsLayout->addLayout(h3);
authorsLayout->addLayout(h4);
authorsLayout->addStretch(1);
authorsBox->setLayout(authorsLayout);
}
@ -257,7 +276,16 @@ void PropertiesDialog::createPublishingBox()
publishingLayout->addRow(tr("Format:"), formatEdit = new YACReaderFieldEdit());
publishingLayout->addRow(tr("Color/BW:"), colorCheck = new QCheckBox());
publishingLayout->addRow(tr("Age rating:"), ageRatingEdit = new YACReaderFieldEdit());
publishingLayout->addRow(tr("Manga:"), mangaCheck = new QCheckBox());
publishingLayout->addRow(tr("Type:"), typeCombo = new QComboBox());
publishingLayout->addRow(tr("Language (ISO):"), languageEdit = new YACReaderFieldEdit());
typeCombo->addItem("Comic");
typeCombo->addItem("Manga");
typeCombo->addItem("Western Manga");
typeCombo->addItem("Web Comic");
typeCombo->addItem("4koma");
typeCombo->setCurrentIndex(-1);
publishingBox->setLayout(publishingLayout);
}
@ -266,17 +294,61 @@ void PropertiesDialog::createPlotBox()
{
plotBox = new QWidget;
auto plotLayout = new QFormLayout;
plotLayout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);
auto plotLayout = new QVBoxLayout;
plotLayout->setRowWrapPolicy(QFormLayout::WrapAllRows);
plotLayout->addRow(tr("Synopsis:"), synopsis = new YACReaderFieldPlainTextEdit());
plotLayout->addRow(tr("Characters:"), characters = new YACReaderFieldPlainTextEdit());
plotLayout->addRow(tr("Notes:"), notes = new YACReaderFieldPlainTextEdit());
auto h1 = new QHBoxLayout;
auto vl1 = new QVBoxLayout;
vl1->addWidget(new QLabel(tr("Synopsis:")));
vl1->addWidget(synopsis = new YACReaderFieldPlainTextEdit());
h1->addLayout(vl1);
auto h2 = new QHBoxLayout;
auto vl2 = new QVBoxLayout;
auto vr2 = new QVBoxLayout;
vl2->addWidget(new QLabel(tr("Characters:")));
vl2->addWidget(characters = new YACReaderFieldPlainTextEdit());
h2->addLayout(vl2);
vr2->addWidget(new QLabel(tr("Teams:")));
vr2->addWidget(teams = new YACReaderFieldPlainTextEdit());
h2->addLayout(vr2);
auto h3 = new QHBoxLayout;
auto vl3 = new QVBoxLayout;
vl3->addWidget(new QLabel(tr("Locations:")));
vl3->addWidget(locations = new YACReaderFieldPlainTextEdit());
h3->addLayout(vl3);
auto h4 = new QHBoxLayout;
auto vl4 = new QVBoxLayout;
vl4->addWidget(new QLabel(tr("Main character or team:")));
vl4->addWidget(mainCharacterOrTeamEdit = new YACReaderFieldEdit());
h4->addLayout(vl4);
plotLayout->addLayout(h1);
plotLayout->addLayout(h2);
plotLayout->addLayout(h3);
plotLayout->addLayout(h4);
plotLayout->addStretch(1);
plotBox->setLayout(plotLayout);
}
void PropertiesDialog::createNotesBox()
{
notesBox = new QWidget;
auto notesLayout = new QFormLayout;
notesLayout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);
notesLayout->setRowWrapPolicy(QFormLayout::WrapAllRows);
notesLayout->addRow(tr("Review:"), review = new YACReaderFieldPlainTextEdit());
notesLayout->addRow(tr("Notes:"), notes = new YACReaderFieldPlainTextEdit());
notesLayout->addRow(tr("Tags:"), tags = new YACReaderFieldPlainTextEdit());
notesBox->setLayout(notesLayout);
}
void PropertiesDialog::createButtonBox()
{
buttonBox = new QDialogButtonBox;
@ -362,6 +434,8 @@ QImage blurred(const QImage &image, const QRect &rect, int radius, bool alphaOnl
void PropertiesDialog::loadComic(ComicDB &comic)
{
if (!comic.info.series.isNull())
series->setText(comic.info.series.toString());
if (!comic.info.title.isNull())
title->setText(comic.info.title.toString());
if (!comic.info.comicVineID.isNull()) {
@ -401,8 +475,6 @@ void PropertiesDialog::loadComic(ComicDB &comic)
if (!comic.info.number.isNull())
numberEdit->setText(comic.info.number.toString());
if (!comic.info.isBis.isNull())
isBisCheck->setChecked(comic.info.isBis.toBool());
if (!comic.info.count.isNull())
countEdit->setText(comic.info.count.toString());
@ -414,6 +486,14 @@ void PropertiesDialog::loadComic(ComicDB &comic)
arcNumberEdit->setText(comic.info.arcNumber.toString());
if (!comic.info.arcCount.isNull())
arcCountEdit->setText(comic.info.arcCount.toString());
if (!comic.info.alternateSeries.isNull())
alternateSeriesEdit->setText(comic.info.alternateSeries.toString());
if (!comic.info.alternateNumber.isNull())
alternateNumberEdit->setText(comic.info.alternateNumber.toString());
if (!comic.info.alternateCount.isNull())
alternateCountEdit->setText(comic.info.alternateCount.toString());
if (!comic.info.seriesGroup.isNull())
seriesGroupEdit->setText(comic.info.seriesGroup.toString());
if (!comic.info.genere.isNull())
genereEdit->setText(comic.info.genere.toString());
@ -430,6 +510,10 @@ void PropertiesDialog::loadComic(ComicDB &comic)
letterer->setPlainText(comic.info.letterer.toString());
if (!comic.info.coverArtist.isNull())
coverArtist->setPlainText(comic.info.coverArtist.toString());
if (!comic.info.editor.isNull())
editor->setPlainText(comic.info.editor.toString());
if (!comic.info.imprint.isNull())
imprint->setPlainText(comic.info.imprint.toString());
size->setText(QString::number(comic.info.hash.right(comic.info.hash.length() - 40).toInt() / 1024.0 / 1024.0, 'f', 2) + "Mb");
@ -452,7 +536,10 @@ void PropertiesDialog::loadComic(ComicDB &comic)
else
colorCheck->setCheckState(Qt::PartiallyChecked);
mangaCheck->setChecked(comic.info.manga.toBool());
typeCombo->setCurrentIndex(comic.info.type.toInt());
if (!comic.info.languageISO.isNull())
languageEdit->setText(comic.info.languageISO.toString());
if (!comic.info.ageRating.isNull())
ageRatingEdit->setText(comic.info.ageRating.toString());
@ -461,8 +548,19 @@ void PropertiesDialog::loadComic(ComicDB &comic)
synopsis->setPlainText(comic.info.synopsis.toString());
if (!comic.info.characters.isNull())
characters->setPlainText(comic.info.characters.toString());
if (!comic.info.teams.isNull())
teams->setPlainText(comic.info.teams.toString());
if (!comic.info.locations.isNull())
locations->setPlainText(comic.info.locations.toString());
if (!comic.info.mainCharacterOrTeam.isNull())
mainCharacterOrTeamEdit->setText(comic.info.mainCharacterOrTeam.toString());
if (!comic.info.review.isNull())
review->setPlainText(comic.info.review.toString());
if (!comic.info.notes.isNull())
notes->setPlainText(comic.info.notes.toString());
if (!comic.info.tags.isNull())
tags->setPlainText(comic.info.tags.toString());
this->setWindowTitle(tr("Edit comic information"));
setCover(comic.info.getCover(basePath));
@ -503,6 +601,9 @@ void PropertiesDialog::setComics(QList<ComicDB> comics)
QList<ComicDB>::iterator itr;
for (itr = ++comics.begin(); itr != comics.end(); itr++) {
if (itr->info.series.isNull() || itr->info.series.toString() != series->text())
series->clear();
if (itr->info.title.isNull() || itr->info.title.toString() != title->text())
title->clear();
@ -516,8 +617,15 @@ void PropertiesDialog::setComics(QList<ComicDB> comics)
if (itr->info.arcCount.isNull() || itr->info.arcCount.toString() != storyArcEdit->text())
arcCountEdit->clear();
if (itr->info.alternateSeries.isNull() || itr->info.alternateSeries.toString() != alternateSeriesEdit->text())
alternateSeriesEdit->clear();
if (itr->info.alternateCount.isNull() || itr->info.alternateCount.toString() != alternateCountEdit->text())
alternateCountEdit->clear();
if (itr->info.genere.isNull() || itr->info.genere.toString() != genereEdit->text())
genereEdit->clear();
if (itr->info.seriesGroup.isNull() || itr->info.seriesGroup.toString() != seriesGroupEdit->text())
seriesGroupEdit->clear();
if (itr->info.writer.isNull() || itr->info.writer.toString() != writer->toPlainText())
writer->clear();
@ -531,6 +639,10 @@ void PropertiesDialog::setComics(QList<ComicDB> comics)
letterer->clear();
if (itr->info.coverArtist.isNull() || itr->info.coverArtist.toString() != coverArtist->toPlainText())
coverArtist->clear();
if (itr->info.editor.isNull() || itr->info.editor.toString() != editor->toPlainText())
editor->clear();
if (itr->info.imprint.isNull() || itr->info.imprint.toString() != imprint->toPlainText())
imprint->clear();
if (itr->info.date.isNull()) {
dayEdit->clear();
@ -552,8 +664,16 @@ void PropertiesDialog::setComics(QList<ComicDB> comics)
formatEdit->clear();
if (itr->info.color.isNull() || itr->info.color.toBool() != colorCheck->isChecked())
colorCheck->setCheckState(Qt::PartiallyChecked);
if (itr->info.manga.toBool() != colorCheck->isChecked())
mangaCheck->setCheckState(Qt::PartiallyChecked);
if (itr->info.type.toInt() != typeCombo->currentIndex()) {
typeCombo->setCurrentIndex(-1);
} else {
typeCombo->setCurrentIndex(itr->info.type.toInt());
}
if (itr->info.languageISO.toString() != languageEdit->text())
languageEdit->clear();
if (itr->info.ageRating.isNull() || itr->info.ageRating.toString() != ageRatingEdit->text())
ageRatingEdit->clear();
@ -561,8 +681,19 @@ void PropertiesDialog::setComics(QList<ComicDB> comics)
synopsis->clear();
if (itr->info.characters.isNull() || itr->info.characters.toString() != characters->toPlainText())
characters->clear();
if (itr->info.teams.isNull() || itr->info.teams.toString() != teams->toPlainText())
teams->clear();
if (itr->info.locations.isNull() || itr->info.locations.toString() != locations->toPlainText())
locations->clear();
if (itr->info.mainCharacterOrTeam.isNull() || itr->info.mainCharacterOrTeam.toString() != mainCharacterOrTeamEdit->text())
mainCharacterOrTeamEdit->clear();
if (itr->info.review.isNull() || itr->info.review.toString() != review->toPlainText())
review->clear();
if (itr->info.notes.isNull() || itr->info.notes.toString() != notes->toPlainText())
notes->clear();
if (itr->info.tags.isNull() || itr->info.tags.toString() != tags->toPlainText())
tags->clear();
}
}
}
@ -615,7 +746,7 @@ void PropertiesDialog::setMultipleCover()
void PropertiesDialog::setCover(const QPixmap &coverI)
{
coverImage = coverI.scaledToHeight(444, Qt::SmoothTransformation);
coverImage = coverI.scaledToHeight(575, Qt::SmoothTransformation);
cover->setPixmap(coverImage);
}
@ -642,6 +773,12 @@ void PropertiesDialog::save()
for (itr = begin; itr != end; itr++) {
bool edited = false;
if (series->isModified()) {
auto seriesString = series->text();
itr->info.series = seriesString.isEmpty() ? QVariant() : series->text();
edited = true;
}
if (title->isModified()) {
auto titleString = title->text();
itr->info.title = titleString.isEmpty() ? QVariant() : title->text();
@ -654,8 +791,6 @@ void PropertiesDialog::save()
edited = true;
}
/*if(comic.info.numPages != NULL)
numPagesEdit->setText(QString::number(*comic.info.numPages));*/
if (sequentialEditing)
if (numberEdit->isModified()) {
if (numberEdit->text().isEmpty())
@ -664,11 +799,6 @@ void PropertiesDialog::save()
itr->info.number = numberEdit->text();
edited = true;
}
if (sequentialEditing)
if (!itr->info.isBis.isNull() || isBisCheck->isChecked()) {
itr->info.isBis = isBisCheck->isChecked();
edited = true;
}
if (countEdit->isModified()) {
itr->info.count = countEdit->text();
@ -693,6 +823,23 @@ void PropertiesDialog::save()
edited = true;
}
if (alternateSeriesEdit->isModified()) {
itr->info.alternateSeries = alternateSeriesEdit->text();
edited = true;
}
if (sequentialEditing) {
if (alternateNumberEdit->isModified()) {
itr->info.alternateNumber = alternateNumberEdit->text();
edited = true;
}
}
if (alternateCountEdit->isModified()) {
itr->info.alternateCount = alternateCountEdit->text();
edited = true;
}
if (genereEdit->isModified()) {
itr->info.genere = genereEdit->text();
edited = true;
@ -722,6 +869,14 @@ void PropertiesDialog::save()
itr->info.coverArtist = coverArtist->toPlainText();
edited = true;
}
if (editor->document()->isModified()) {
itr->info.editor = editor->toPlainText();
edited = true;
}
if (imprint->document()->isModified()) {
itr->info.imprint = imprint->toPlainText();
edited = true;
}
if (dayEdit->isModified() || monthEdit->isModified() || yearEdit->isModified()) {
itr->info.date = dayEdit->text() + "/" + monthEdit->text() + "/" + yearEdit->text();
@ -740,8 +895,13 @@ void PropertiesDialog::save()
edited = true;
}
if (mangaCheck->checkState() != Qt::PartiallyChecked) {
itr->info.manga = mangaCheck->isChecked();
if (typeCombo->currentIndex() != -1 && itr->info.type.toInt() != typeCombo->currentIndex()) {
itr->info.type = typeCombo->currentIndex();
edited = true;
}
if (languageEdit->isModified()) {
itr->info.languageISO = languageEdit->text();
edited = true;
}
@ -754,14 +914,40 @@ void PropertiesDialog::save()
itr->info.synopsis = synopsis->toPlainText();
edited = true;
}
if (characters->document()->isModified()) {
itr->info.characters = characters->toPlainText();
edited = true;
}
if (locations->document()->isModified()) {
itr->info.locations = locations->toPlainText();
edited = true;
}
if (teams->document()->isModified()) {
itr->info.teams = teams->toPlainText();
edited = true;
}
if (mainCharacterOrTeamEdit->isModified()) {
itr->info.mainCharacterOrTeam = mainCharacterOrTeamEdit->text();
edited = true;
}
if (seriesGroupEdit->isModified()) {
itr->info.seriesGroup = seriesGroupEdit->text();
edited = true;
}
if (review->document()->isModified()) {
itr->info.review = review->toPlainText();
edited = true;
}
if (notes->document()->isModified()) {
itr->info.notes = notes->toPlainText();
edited = true;
}
if (tags->document()->isModified()) {
itr->info.tags = tags->toPlainText();
edited = true;
}
itr->info.edited = edited;
}
@ -829,10 +1015,10 @@ void PropertiesDialog::setDisableUniqueValues(bool disabled)
coverPageEdit->clear();
numberEdit->setDisabled(disabled);
numberEdit->clear();
isBisCheck->setDisabled(disabled);
isBisCheck->setChecked(false);
arcNumberEdit->setDisabled(disabled);
arcNumberEdit->clear();
alternateNumberEdit->setDisabled(disabled);
alternateNumberEdit->clear();
}
void PropertiesDialog::closeEvent(QCloseEvent *e)
@ -841,9 +1027,7 @@ void PropertiesDialog::closeEvent(QCloseEvent *e)
title->clear();
title->setModified(false);
coverPageEdit->clear();
// numPagesEdit->setText(QString::number(*comic.info.numPages));
numberEdit->clear();
isBisCheck->setChecked(false);
countEdit->clear();
volumeEdit->clear();
storyArcEdit->clear();
@ -862,12 +1046,27 @@ void PropertiesDialog::closeEvent(QCloseEvent *e)
publisherEdit->clear();
formatEdit->clear();
colorCheck->setCheckState(Qt::PartiallyChecked);
mangaCheck->setChecked(false);
ageRatingEdit->clear();
synopsis->clear();
characters->clear();
notes->clear();
// 9.13 fields
typeCombo->setCurrentIndex(-1);
editor->clear();
imprint->clear();
teams->clear();
locations->clear();
series->clear();
alternateSeriesEdit->clear();
alternateNumberEdit->clear();
alternateCountEdit->clear();
languageEdit->clear();
seriesGroupEdit->clear();
mainCharacterOrTeamEdit->clear();
review->clear();
tags->clear();
setDisableUniqueValues(false);
tabBar->setCurrentIndex(0);

View File

@ -15,6 +15,7 @@ class YACReaderFieldEdit;
class YACReaderFieldPlainTextEdit;
class QDialogButtonBox;
class QCheckBox;
class QComboBox;
// class YACReaderBusyWidget;
class QToolButton;
@ -36,6 +37,7 @@ private:
QScrollArea *sa;
QWidget *generalInfoBox;
YACReaderFieldEdit *series;
YACReaderFieldEdit *title;
YACReaderFieldEdit *numPagesEdit;
QLabel *size;
@ -46,7 +48,6 @@ private:
YACReaderFieldEdit *numberEdit;
QIntValidator numberValidator;
QCheckBox *isBisCheck;
YACReaderFieldEdit *countEdit;
QIntValidator countValidator;
@ -57,6 +58,12 @@ private:
YACReaderFieldEdit *arcCountEdit;
QIntValidator arcCountValidator;
YACReaderFieldEdit *alternateSeriesEdit;
YACReaderFieldEdit *alternateNumberEdit;
YACReaderFieldEdit *alternateCountEdit;
YACReaderFieldEdit *seriesGroupEdit;
YACReaderFieldEdit *genereEdit;
YACReaderFieldPlainTextEdit *writer;
@ -65,6 +72,8 @@ private:
YACReaderFieldPlainTextEdit *colorist;
YACReaderFieldPlainTextEdit *letterer;
YACReaderFieldPlainTextEdit *coverArtist;
YACReaderFieldPlainTextEdit *editor;
YACReaderFieldPlainTextEdit *imprint;
YACReaderFieldEdit *dayEdit;
QIntValidator dayValidator;
@ -76,11 +85,14 @@ private:
YACReaderFieldEdit *formatEdit;
QCheckBox *colorCheck;
YACReaderFieldEdit *ageRatingEdit;
QCheckBox *mangaCheck;
QComboBox *typeCombo;
YACReaderFieldEdit *languageEdit;
YACReaderFieldPlainTextEdit *synopsis;
YACReaderFieldPlainTextEdit *characters;
YACReaderFieldPlainTextEdit *notes;
YACReaderFieldPlainTextEdit *teams;
YACReaderFieldPlainTextEdit *locations;
YACReaderFieldEdit *mainCharacterOrTeamEdit;
QWidget *authorsBox;
@ -88,6 +100,12 @@ private:
QWidget *plotBox;
QWidget *notesBox;
YACReaderFieldPlainTextEdit *review;
YACReaderFieldPlainTextEdit *notes;
YACReaderFieldPlainTextEdit *tags;
QDialogButtonBox *buttonBox;
QPushButton *closeButton;
QPushButton *saveButton;
@ -107,6 +125,7 @@ private:
void createAuthorsBox();
void createPublishingBox();
void createPlotBox();
void createNotesBox();
void createButtonBox();

View File

@ -140,7 +140,7 @@ Rectangle {
font.pixelSize: mainContainer.compact ? 18 : 21;
wrapMode: Text.WordWrap
text: comic ? comic.getTitleIncludingNumber() ?? "" : ""
text: comic ? comic.getInfoTitle() ?? "" : ""
}
RowLayout
@ -202,6 +202,33 @@ Rectangle {
visible : comicInfo ? comicInfo.number ?? false : false
}
Text {
id: arc
color: infoColor
font: mainContainer.infoFont
text: comicInfo ? comicInfo.getStoryArcInfoString() : ""
rightPadding: 20
visible : comicInfo ? comicInfo.getStoryArcInfoString().length : false
}
Text {
id: alternate
color: infoColor
font: mainContainer.infoFont
text: comicInfo ? comicInfo.getAlternateSeriesString() : ""
rightPadding: 20
visible : comicInfo ? comicInfo.getAlternateSeriesString().length : false
}
Text {
id: seriesGroup
color: infoColor
font: mainContainer.infoFont
text: comicInfo ? comicInfo.seriesGroup ?? "" : ""
rightPadding: 20
visible: comicInfo ? comicInfo.seriesGroup ?? false : false
}
Text {
id: genre
color: infoColor
@ -266,6 +293,129 @@ Rectangle {
textFormat: Text.RichText
}
Text {
Layout.topMargin: 25
Layout.bottomMargin: 5
id: characters_title
color: infoTitleColor
font.family: "Arial"
font.pixelSize: 18
font.bold: true
text: qsTr("Characters")
visible: comicInfo ? comicInfo.getCharacters().length > 0 : false
}
Flow {
Layout.fillWidth: true
spacing: 20
Repeater {
id: characters
model: comicInfo ? comicInfo.getCharacters().length : null
Text {
color: infoTitleColor
font.family: "Arial"
font.pixelSize: 15
text: comicInfo ? comicInfo.getCharacters()[index] : ""
}
}
}
Text {
Layout.topMargin: 25
Layout.bottomMargin: 5
id: main_character_or_team_title
color: infoTitleColor
font.family: "Arial"
font.pixelSize: 18
font.bold: true
text: qsTr("Main character or team")
visible: comicInfo && comicInfo.mainCharacterOrTeam ? comicInfo.mainCharacterOrTeam.length > 0 : false
}
Text {
color: infoTitleColor
font.family: "Arial"
font.pixelSize: 15
text: comicInfo && comicInfo.mainCharacterOrTeam ? comicInfo.mainCharacterOrTeam : ""
visible: comicInfo && comicInfo.mainCharacterOrTeam ? comicInfo.mainCharacterOrTeam.length > 0 : false
}
Text {
Layout.topMargin: 25
Layout.bottomMargin: 5
id: teams_title
color: infoTitleColor
font.family: "Arial"
font.pixelSize: 18
font.bold: true
text: qsTr("Teams")
visible: comicInfo ? comicInfo.getTeams().length > 0 : false
}
Flow {
Layout.fillWidth: true
spacing: 20
Repeater {
id: teams
model: comicInfo ? comicInfo.getTeams().length : null
Text {
color: infoTitleColor
font.family: "Arial"
font.pixelSize: 15
text: comicInfo ? comicInfo.getTeams()[index] : ""
}
}
}
Text {
Layout.topMargin: 25
Layout.bottomMargin: 5
id: locations_title
color: infoTitleColor
font.family: "Arial"
font.pixelSize: 18
font.bold: true
text: qsTr("Locations")
visible: comicInfo ? comicInfo.getLocations().length > 0 : false
}
Flow {
Layout.fillWidth: true
spacing: 20
Repeater {
id: locations
model: comicInfo ? comicInfo.getLocations().length : null
Text {
color: infoTitleColor
font.family: "Arial"
font.pixelSize: 15
text: comicInfo ? comicInfo.getLocations()[index] : ""
}
}
}
Text {
Layout.topMargin: 25
Layout.bottomMargin: 5
@ -283,7 +433,9 @@ Rectangle {
comicInfo.getInkers().length +
comicInfo.getColorists().length +
comicInfo.getLetterers().length +
comicInfo.getCoverArtists().length > 0) : false
comicInfo.getCoverArtists().length +
comicInfo.getEditors().length +
comicInfo.getImprint().length > 0) : false
}
Flow {
@ -401,7 +553,7 @@ Rectangle {
Repeater {
id: cover_artist
model: comicInfo ? comicInfo.getCoverArtists().length : ""
model: comicInfo ? comicInfo.getCoverArtists().length : null
Column{
Text {
color: infoTitleColor
@ -420,6 +572,50 @@ Rectangle {
}
}
}
Repeater {
id: editors
model: comicInfo ? comicInfo.getEditors().length : null
Column{
Text {
color: infoTitleColor
font.family: "Arial"
font.pixelSize: 15
text: comicInfo ? comicInfo.getEditors()[index] : ""
}
Text {
color: infoTextColor
font.family: "Arial"
font.pixelSize: 13
font.italic: true
text: qsTr("editor")
}
}
}
Repeater {
id: imprint
model: comicInfo ? comicInfo.getImprint().length : null
Column{
Text {
color: infoTitleColor
font.family: "Arial"
font.pixelSize: 15
text: comicInfo ? comicInfo.getImprint()[index] : ""
}
Text {
color: infoTextColor
font.family: "Arial"
font.pixelSize: 13
font.italic: true
text: qsTr("imprint")
}
}
}
}
Text {
@ -464,6 +660,18 @@ Rectangle {
visible: comicInfo ? comicInfo.format ?? false : false
}
Text {
id: type
color: infoTitleColor
font.family: "Arial"
font.pixelSize: 15
text: comicInfo ? comicInfo.getTypeString() : ""
visible: comicInfo ? comicInfo.getTypeString() : false
}
Text {
id: color
@ -476,6 +684,18 @@ Rectangle {
visible: comicInfo ? comicInfo.color ?? false : false
}
Text {
id: language
color: infoTitleColor
font.family: "Arial"
font.pixelSize: 15
text: comicInfo && comicInfo.languageISO ? comicInfo.languageISO : ""
visible: comicInfo && comicInfo.languageISO ? comicInfo.languageISO.length > 0 ?? false : false
}
Text {
id: age_rating
@ -488,38 +708,6 @@ Rectangle {
visible: comicInfo ? comicInfo.ageRating ?? false : false
}
}
Text {
Layout.topMargin: 25
Layout.bottomMargin: 5
id: characters_title
color: infoTitleColor
font.family: "Arial"
font.pixelSize: 18
font.bold: true
text: qsTr("Characters")
visible: comicInfo ? comicInfo.getCharacters().length > 0 : false
}
Flow {
Layout.fillWidth: true
spacing: 20
Repeater {
id: characters
model: comicInfo ? comicInfo.getCharacters().length : null
Text {
color: infoTitleColor
font.family: "Arial"
font.pixelSize: 15
text: comicInfo ? comicInfo.getCharacters()[index] : ""
}
}
}
}
Item {

View File

@ -131,6 +131,16 @@ Rectangle {
}
}
//is new
Rectangle {
width: 10
height: 10
radius: 5
anchors { left: coverElement.left; top: coverElement.top; topMargin: 10; leftMargin: 10; }
color: "#FFFFCC00"
visible: (((new Date() / 1000) - added) < recent_range || ((new Date() / 1000) - updated) < recent_range) && show_recent
}
//border
Rectangle {
width: coverElement.width

View File

@ -133,6 +133,16 @@ Rectangle {
}
}
//is new
Rectangle {
width: 10
height: 10
radius: 5
anchors { left: coverElement.left; top: coverElement.top; topMargin: 10; leftMargin: 10; }
color: "#FFFFCC00"
visible: (((new Date() / 1000) - added) < recent_range || ((new Date() / 1000) - updated) < recent_range) && show_recent
}
//border
Rectangle {
width: coverElement.width

View File

@ -280,6 +280,16 @@ SplitView {
}
//is new
Rectangle {
width: 10
height: 10
radius: 5
anchors { left: coverElement.left; top: coverElement.top; topMargin: 5; leftMargin: 5; }
color: "#FFFFCC00"
visible: (((new Date() / 1000) - added_date) < recent_range) && show_recent
}
//border
Rectangle {
width: coverElement.width
@ -489,8 +499,7 @@ SplitView {
font.pixelSize: 21
wrapMode: Text.WordWrap
text: currentComic.getTitleIncludingNumber()
}
text: currentComic ? currentComic.getTitleIncludingNumber() : "" }
Flow {
spacing: 0
@ -523,6 +532,33 @@ SplitView {
visible : currentComicInfo.number ? true : false
}
Text {
id: currentComicInfoArc
color: currentComicDetailsFlowView.infoFlowTextColor
font: currentComicDetailsFlowView.infoFont
text: currentComicInfo.getStoryArcInfoString()
rightPadding: 20
visible : currentComicInfo.getStoryArcInfoString().length > 0
}
Text {
id: currentComicInfoAlternate
color: currentComicDetailsFlowView.infoFlowTextColor
font: currentComicDetailsFlowView.infoFont
text: currentComicInfo.getAlternateSeriesString()
rightPadding: 20
visible : currentComicInfo.getStoryArcInfoString().length > 0
}
Text {
id: currentComicInfoSeriesGroup
color: currentComicDetailsFlowView.infoFlowTextColor
font: currentComicDetailsFlowView.infoFont
text: currentComicInfo.seriesGroup ? currentComicInfo.seriesGroup : ""
rightPadding: 20
visible: currentComicInfo.seriesGroup ? true : false
}
Text {
id: currentComicInfoGenre
color: currentComicDetailsFlowView.infoFlowTextColor

View File

@ -283,6 +283,16 @@ SplitView {
}
//is new
Rectangle {
width: 10
height: 10
radius: 5
anchors { left: coverElement.left; top: coverElement.top; topMargin: 5; leftMargin: 5; }
color: "#FFFFCC00"
visible: (((new Date() / 1000) - added_date) < recent_range) && show_recent
}
//border
Rectangle {
width: coverElement.width
@ -493,7 +503,7 @@ SplitView {
font.pixelSize: 21
wrapMode: Text.WordWrap
text: currentComic.getTitleIncludingNumber()
text: currentComic?.getTitleIncludingNumber() ?? ""
}
Flow {
@ -527,6 +537,33 @@ SplitView {
visible : currentComicInfo.number ? true : false
}
Text {
id: currentComicInfoArc
color: currentComicDetailsFlowView.infoFlowTextColor
font: currentComicDetailsFlowView.infoFont
text: currentComicInfo.getStoryArcInfoString()
rightPadding: 20
visible : currentComicInfo.getStoryArcInfoString().length > 0
}
Text {
id: currentComicInfoAlternate
color: currentComicDetailsFlowView.infoFlowTextColor
font: currentComicDetailsFlowView.infoFont
text: currentComicInfo.getAlternateSeriesString()
rightPadding: 20
visible : currentComicInfo.getStoryArcInfoString().length > 0
}
Text {
id: currentComicInfoSeriesGroup
color: currentComicDetailsFlowView.infoFlowTextColor
font: currentComicDetailsFlowView.infoFont
text: currentComicInfo.seriesGroup ? currentComicInfo.seriesGroup : ""
rightPadding: 20
visible: currentComicInfo.seriesGroup ? true : false
}
Text {
id: currentComicInfoGenre
color: currentComicDetailsFlowView.infoFlowTextColor

View File

@ -0,0 +1,35 @@
#include "recent_visibility_coordinator.h"
#include "yacreader_global_gui.h"
RecentVisibilityCoordinator::RecentVisibilityCoordinator(QSettings *settings, FolderModel *folderModel, FolderContentView *folderContentView, ComicModel *comicModel)
: QObject(), settings(settings), folderModel(folderModel), folderContentView(folderContentView), comicModel(comicModel)
{
updateVisibility();
updateTimeRange();
}
void RecentVisibilityCoordinator::toggleVisibility(bool visibility)
{
settings->setValue(DISPLAY_RECENTLY_INDICATOR, visibility);
updateVisibility();
}
void RecentVisibilityCoordinator::updateTimeRange()
{
auto days = settings->value(NUM_DAYS_TO_CONSIDER_RECENT, 1).toInt();
folderModel->setRecentRange(days);
folderContentView->setRecentRange(days);
comicModel->setRecentRange(days);
}
void RecentVisibilityCoordinator::updateVisibility()
{
auto visibility = settings->value(DISPLAY_RECENTLY_INDICATOR, true).toBool();
folderModel->setShowRecent(visibility);
folderContentView->setShowRecent(visibility);
comicModel->setShowRecent(visibility);
}

View File

@ -0,0 +1,30 @@
#ifndef RECENT_VISIBILITY_COORDINATOR_H
#define RECENT_VISIBILITY_COORDINATOR_H
#include <QtCore>
#include "folder_model.h"
#include "comic_model.h"
#include "folder_content_view.h"
class RecentVisibilityCoordinator : public QObject
{
Q_OBJECT
public:
explicit RecentVisibilityCoordinator(QSettings *settings, FolderModel *folderModel, FolderContentView *folderContentView, ComicModel *comicModel);
public slots:
void toggleVisibility(bool visibility);
void updateTimeRange();
private:
QSettings *settings;
FolderModel *folderModel;
FolderContentView *folderContentView;
ComicModel *comicModel;
void updateVisibility();
};
#endif // RECENT_VISIBILITY_COORDINATOR_H

View File

@ -6,8 +6,8 @@ QString YACReaderServerDataHelper::folderToYSFormat(const qulonglong libraryId,
.arg(libraryId)
.arg(folder.id)
.arg(folder.name)
.arg(folder.getNumChildren())
.arg(folder.getFirstChildHash());
.arg(folder.numChildren)
.arg(folder.firstChildHash);
}
QString YACReaderServerDataHelper::comicToYSFormat(const qulonglong libraryId, const ComicDB &comic)
@ -31,8 +31,15 @@ QJsonObject YACReaderServerDataHelper::folderToJSON(const qulonglong libraryId,
json["id"] = QString::number(folder.id);
json["library_id"] = QString::number(libraryId);
json["folder_name"] = folder.name;
json["num_children"] = folder.getNumChildren();
json["first_comic_hash"] = folder.getFirstChildHash();
json["num_children"] = folder.numChildren;
json["first_comic_hash"] = folder.firstChildHash;
// 9.13
json["finished"] = folder.finished;
json["completed"] = folder.completed;
json["custom_image"] = folder.customImage;
json["file_type"] = static_cast<typename std::underlying_type<YACReader::FileType>::type>(folder.type);
json["added"] = folder.added;
json["updated"] = folder.updated;
return json;
}
@ -47,14 +54,18 @@ QJsonObject YACReaderServerDataHelper::comicToJSON(const qulonglong libraryId, c
json["file_name"] = comic.name;
json["file_size"] = QString::number(comic.getFileSize());
json["hash"] = comic.info.hash;
json["cover_page"] = comic.info.coverPage.toInt(); // 9.13
json["current_page"] = comic.info.currentPage;
json["num_pages"] = comic.info.numPages.toInt();
json["read"] = comic.info.read;
json["cover_size_ratio"] = comic.info.coverSizeRatio.toFloat();
json["title"] = comic.info.title.toString();
json["number"] = comic.info.number.toInt();
json["number"] = comic.info.number.toInt(); // 9.13 legacy, kept for compatibility with old clients
json["universal_number"] = comic.info.number.toString(); // 9.13, text based number
json["last_time_opened"] = comic.info.lastTimeOpened.toLongLong();
json["manga"] = comic.info.manga.toBool();
auto type = comic.info.type.value<YACReader::FileType>();
json["manga"] = type == YACReader::FileType::Manga; // legacy, kept for compatibility with old clients
json["file_type"] = comic.info.type.toInt(); // 9.13
return json;
}
@ -70,6 +81,39 @@ QJsonObject YACReaderServerDataHelper::fullComicToJSON(const qulonglong libraryI
json["synopsis"] = comic.info.synopsis.toString();
// 9.13
json["count"] = comic.info.count.toInt();
json["story_arc"] = comic.info.storyArc.toString();
json["arc_number"] = comic.info.arcNumber.toString();
json["arc_count"] = comic.info.arcCount.toInt();
json["writer"] = comic.info.writer.toString();
json["penciller"] = comic.info.penciller.toString();
json["inker"] = comic.info.inker.toString();
json["colorist"] = comic.info.colorist.toString();
json["letterer"] = comic.info.letterer.toString();
json["cover_artist"] = comic.info.coverArtist.toString();
json["publisher"] = comic.info.publisher.toString();
json["format"] = comic.info.format.toString();
json["color"] = comic.info.color.toBool();
json["age_rating"] = comic.info.ageRating.toString();
json["editor"] = comic.info.editor.toString();
json["characters"] = comic.info.characters.toString();
json["notes"] = comic.info.notes.toString();
json["added"] = comic.info.added.toLongLong();
json["editor"] = comic.info.editor.toString();
json["imprint"] = comic.info.imprint.toString();
json["teams"] = comic.info.teams.toString();
json["locations"] = comic.info.locations.toString();
json["series"] = comic.info.series.toString();
json["alternate_series"] = comic.info.alternateSeries.toString();
json["alternate_number"] = comic.info.alternateNumber.toString();
json["alternate_count"] = comic.info.alternateCount.toInt();
json["language_iso"] = comic.info.languageISO.toString();
json["series_group"] = comic.info.seriesGroup.toString();
json["main_character_or_team"] = comic.info.mainCharacterOrTeam.toString();
json["review"] = comic.info.review.toString();
json["tags"] = comic.info.tags.toString();
return json;
}

View File

@ -1,5 +1,7 @@
#include "xml_info_parser.h"
#include "yacreader_global.h"
#include <QtCore>
bool isValidText(const QString &string)
@ -57,7 +59,7 @@ void consolidateDate(ComicInfo &info)
auto year = info.year.isNull() ? 0 : info.year.toInt();
auto month = info.month.isNull() ? 1 : info.month.toInt();
auto day = info.date.isNull() ? 1 : info.date.toInt();
auto day = info.day.isNull() ? 1 : info.day.toInt();
info.date = QString("%1/%2/%3").arg(day).arg(month).arg(year);
}
@ -65,6 +67,7 @@ void consolidateDate(ComicInfo &info)
bool tryValues(QXmlStreamReader &reader, ComicInfo &info)
{
std::map<QString, QVariant &> stringValues = {
{ "Number", info.number },
{ "Title", info.title },
{ "Volume", info.volume },
{ "StoryArc", info.storyArc },
@ -74,16 +77,23 @@ bool tryValues(QXmlStreamReader &reader, ComicInfo &info)
{ "AgeRating", info.ageRating },
{ "Summary", info.synopsis },
{ "Notes", info.notes },
{ "Editor", info.editor },
{ "Imprint", info.imprint },
{ "Series", info.series },
{ "AlternateSeries", info.alternateSeries },
{ "AlternateNumber", info.alternateNumber },
{ "LanguageISO", info.languageISO },
{ "SeriesGroup", info.seriesGroup },
{ "MainCharacterOrTeam", info.mainCharacterOrTeam },
{ "Review", info.review },
};
std::map<QString, QVariant &> forcedNumbers = {
{ "Number", info.number },
{ "Count", info.count },
{ "AlternateNumber", info.arcNumber },
{ "AlternateCount", info.arcCount },
{ "AlternateCount", info.alternateCount },
{ "Day", info.day },
{ "Month", info.month },
{ "Year", info.year }
{ "Year", info.year },
};
std::map<QString, QVariant &> multiValuedStrings = {
@ -93,7 +103,9 @@ bool tryValues(QXmlStreamReader &reader, ComicInfo &info)
{ "Colorist", info.colorist },
{ "Letterer", info.letterer },
{ "CoverArtist", info.coverArtist },
{ "Characters", info.characters }
{ "Characters", info.characters },
{ "Teams", info.teams },
{ "Locations", info.locations }
};
for (auto &pair : stringValues) {
@ -130,16 +142,17 @@ bool tryValues(QXmlStreamReader &reader, ComicInfo &info)
if (reader.name() == QString("Manga")) {
auto string = reader.readElementText();
if (isValidText(string)) {
if (string == "Yes" || string == "YesAndRightToLeft") {
info.manga = true;
if (string == "Yes" || string == "YesAndRightToLeft") { // there was a breaking change in ComicInfo 2.0, Yes means now WesterManga reading style, but old info stills means manga
info.type = QVariant::fromValue(YACReader::FileType::Manga);
} else if (string == "No") {
info.manga = false;
info.type = QVariant::fromValue(YACReader::FileType::Comic);
}
}
return true;
}
// TODO, check if the url is actually a comic vine link
if (reader.name() == QString("Web")) {
auto string = reader.readElementText();
if (isValidText(string)) {
@ -166,7 +179,7 @@ bool YACReader::parseXMLIntoInfo(const QByteArray &xmlRawData, ComicInfo &info)
while (reader.readNextStartElement()) {
if (tryValues(reader, info)) {
someDataWasParsed = true;
someDataWasParsed = true | someDataWasParsed;
} else {
if (reader.name() != QString("ComicInfo")) {
reader.skipCurrentElement();

View File

@ -45,7 +45,7 @@ YACReaderContentViewsManager::YACReaderContentViewsManager(QSettings *settings,
doComicsViewConnections();
comicsViewStack->addWidget(comicsViewTransition = new ComicsViewTransition());
comicsViewStack->addWidget(folderContentView = new FolderContentView());
comicsViewStack->addWidget(folderContentView = new FolderContentView(parent->toogleShowRecentIndicatorAction));
comicsViewStack->addWidget(emptyLabelWidget = new EmptyLabelWidget());
comicsViewStack->addWidget(emptySpecialList = new EmptySpecialListWidget());
comicsViewStack->addWidget(emptyReadingList = new EmptyReadingListWidget());

View File

@ -91,4 +91,26 @@ void YACReaderFoldersViewItemDeletegate::paint(QPainter *painter, const QStyleOp
}
QStyledItemDelegate::paint(painter, option, index);
auto showRecent = index.data(FolderModel::ShowRecentRole).toBool();
if (showRecent) {
auto now = QDateTime::currentSecsSinceEpoch();
auto added = index.data(FolderModel::AddedRole).toLongLong();
auto updated = index.data(FolderModel::UpdatedRole).toLongLong();
auto daysInSeconds = index.data(FolderModel::RecentRangeRole).toLongLong();
if (now - added < daysInSeconds || now - updated < daysInSeconds) {
painter->save();
painter->setRenderHint(QPainter::Antialiasing);
#ifdef Q_OS_MAC
painter->setBrush(QBrush(QColor(85, 95, 127)));
#else
painter->setBrush(QBrush(QColor(237, 197, 24)));
#endif
painter->setPen(QPen(QBrush(), 0));
painter->drawEllipse(option.rect.x() + 13, option.rect.y() + 2, 7, 7);
painter->restore();
}
}
}

View File

@ -1,5 +1,7 @@
#include "comic_db.h"
#include "yacreader_global.h"
#include <QVariant>
#include <QFileInfo>
@ -118,6 +120,51 @@ QString ComicDB::toTXT()
if (!info.lastTimeOpened.isNull())
txt.append(QString("lastTimeOpened:%1\r\n").arg(info.lastTimeOpened.toULongLong()));
if (!info.added.isNull())
txt.append(QString("added:%1\r\n").arg(info.added.toULongLong()));
if (!info.type.isNull())
txt.append(QString("type:%1\r\n").arg(info.type.toInt()));
if (!info.editor.isNull())
txt.append(QString("editor:%1\r\n").arg(info.editor.toString()));
if (!info.imprint.isNull())
txt.append(QString("imprint:%1\r\n").arg(info.imprint.toString()));
if (!info.teams.isNull())
txt.append(QString("teams:%1\r\n").arg(info.teams.toString()));
if (!info.locations.isNull())
txt.append(QString("locations:%1\r\n").arg(info.locations.toString()));
if (!info.series.isNull())
txt.append(QString("series:%1\r\n").arg(info.series.toString()));
if (!info.alternateSeries.isNull())
txt.append(QString("alternateSeries:%1\r\n").arg(info.alternateSeries.toString()));
if (!info.alternateNumber.isNull())
txt.append(QString("alternateNumber:%1\r\n").arg(info.alternateNumber.toString()));
if (!info.alternateCount.isNull())
txt.append(QString("alternateCount:%1\r\n").arg(info.alternateCount.toString()));
if (!info.languageISO.isNull())
txt.append(QString("languageISO:%1\r\n").arg(info.languageISO.toString()));
if (!info.seriesGroup.isNull())
txt.append(QString("seriesGroup:%1\r\n").arg(info.seriesGroup.toString()));
if (!info.mainCharacterOrTeam.isNull())
txt.append(QString("mainCharacterOrTeam:%1\r\n").arg(info.mainCharacterOrTeam.toString()));
if (!info.review.isNull())
txt.append(QString("review:%1\r\n").arg(info.review.toString()));
if (!info.tags.isNull())
txt.append(QString("tags:%1\r\n").arg(info.tags.toString()));
return txt;
}
@ -169,6 +216,35 @@ QString ComicDB::getTitleIncludingNumber() const
return getTitleOrFileName();
}
QString ComicDB::getInfoTitle() const
{
if (!info.number.isNull() && !info.title.isNull() && !info.series.isNull())
return "#" + info.number.toString() + " - " + info.title.toString() + " (" + info.series.toString() + ")";
if (!info.title.isNull() && !info.series.isNull())
return info.title.toString() + " (" + info.series.toString() + ")";
if (!info.number.isNull() && !info.title.isNull())
return "#" + info.number.toString() + " - " + info.title.toString();
if (!info.number.isNull() && !info.series.isNull())
return "#" + info.number.toString() + " - " + info.series.toString();
if (!info.number.isNull())
return "#" + info.number.toString() + " - " + getTitleOrFileName();
if (!info.title.isNull() && !info.series.isNull())
return info.title.toString() + " (" + info.series.toString() + ")";
if (!info.title.isNull())
return info.title.toString();
if (!info.series.isNull())
return info.series.toString();
return QFileInfo(path).fileName();
}
//-----------------------------------------------------------------------------
// COMIC_INFO-------------------------------------------------------------------
//-----------------------------------------------------------------------------
@ -238,6 +314,22 @@ void ComicInfo::deleteMetadata()
characters = QVariant();
notes = QVariant();
// type = QVariant(); reset or not???
editor = QVariant();
imprint = QVariant();
teams = QVariant();
locations = QVariant();
series = QVariant();
alternateSeries = QVariant();
alternateNumber = QVariant();
alternateCount = QVariant();
languageISO = QVariant();
seriesGroup = QVariant();
mainCharacterOrTeam = QVariant();
review = QVariant();
// tags = QVariant(); reset or not???
comicVineID = QVariant();
}
// the default operator= should work
@ -292,135 +384,25 @@ ComicInfo &ComicInfo::operator=(const ComicInfo &comicInfo)
coverSizeRatio = comicInfo.coverSizeRatio;
originalCoverSize = comicInfo.originalCoverSize;
added = comicInfo.added;
type = comicInfo.type;
editor = comicInfo.editor;
imprint = comicInfo.imprint;
teams = comicInfo.teams;
locations = comicInfo.locations;
series = comicInfo.series;
alternateSeries = comicInfo.alternateSeries;
alternateNumber = comicInfo.alternateNumber;
alternateCount = comicInfo.alternateCount;
languageISO = comicInfo.languageISO;
seriesGroup = comicInfo.seriesGroup;
mainCharacterOrTeam = comicInfo.mainCharacterOrTeam;
review = comicInfo.review;
tags = comicInfo.tags;
return *this;
}
// set fields
/*
void ComicInfo::setTitle(QString value)
{
setValue(title,value);
}
void ComicInfo::setCoverPage(int value)
{
setValue(coverPage,value);
}
void ComicInfo::setNumPages(int value)
{
setValue(numPages,value);
}
void ComicInfo::setNumber(int value)
{
setValue(number,value);
}
void ComicInfo::setIsBis(bool value)
{
setValue(isBis,value);
}
void ComicInfo::setCount(int value)
{
setValue(count,value);
}
void ComicInfo::setVolume(QString value)
{
setValue(volume,value);
}
void ComicInfo::setStoryArc(QString value)
{
setValue(storyArc,value);
}
void ComicInfo::setArcNumber(int value)
{
setValue(arcNumber,value);
}
void ComicInfo::setArcCount(int value)
{
setValue(arcCount,value);
}
void ComicInfo::setGenere(QString value)
{
setValue(genere,value);
}
void ComicInfo::setWriter(QString value)
{
setValue(writer,value);
}
void ComicInfo::setPenciller(QString value)
{
setValue(penciller,value);
}
void ComicInfo::setInker(QString value)
{
setValue(inker,value);
}
void ComicInfo::setColorist(QString value)
{
setValue(colorist,value);
}
void ComicInfo::setLetterer(QString value)
{
setValue(letterer,value);
}
void ComicInfo::setCoverArtist(QString value)
{
setValue(coverArtist,value);
}
void ComicInfo::setDate(QString value)
{
setValue(date,value);
}
void ComicInfo::setPublisher(QString value)
{
setValue(publisher,value);
}
void ComicInfo::setFormat(QString value)
{
setValue(format,value);
}
void ComicInfo::setColor(bool value)
{
setValue(color,value);
}
void ComicInfo::setAgeRating(QString value)
{
setValue(ageRating,value);
}
void ComicInfo::setSynopsis(QString value)
{
setValue(synopsis,value);
}
void ComicInfo::setCharacters(QString value)
{
setValue(characters,value);
}
void ComicInfo::setNotes(QString value)
{
setValue(notes,value);
}*/
QPixmap ComicInfo::getCover(const QString &basePath)
{
if (cover.isNull()) {
@ -485,6 +467,24 @@ QStringList ComicInfo::getCoverArtists()
return QStringList();
}
QStringList ComicInfo::getEditors()
{
if (editor.toString().length() > 0) {
return editor.toString().split("\n");
}
return QStringList();
}
QStringList ComicInfo::getImprint()
{
if (imprint.toString().length() > 0) {
return imprint.toString().split("\n");
}
return QStringList();
}
QStringList ComicInfo::getCharacters()
{
if (characters.toString().length() > 0) {
@ -494,6 +494,75 @@ QStringList ComicInfo::getCharacters()
return QStringList();
}
QStringList ComicInfo::getTeams()
{
if (teams.toString().length() > 0) {
return teams.toString().split("\n");
}
return QStringList();
}
QStringList ComicInfo::getLocations()
{
if (locations.toString().length() > 0) {
return locations.toString().split("\n");
}
return QStringList();
}
QStringList ComicInfo::getTags()
{
if (tags.toString().length() > 0) {
return tags.toString().split("\n");
}
return QStringList();
}
QString ComicInfo::getTypeString()
{
switch (type.value<YACReader::FileType>()) {
case YACReader::FileType::Comic:
return "Comic";
case YACReader::FileType::Manga:
return "Manga";
case YACReader::FileType::WesternManga:
return "Western Manga";
case YACReader::FileType::WebComic:
return "Web Comic";
case YACReader::FileType::Yonkoma:
return "4-Koma";
}
}
QString ComicInfo::getStoryArcInfoString()
{
if (arcNumber.toString().length() > 0 && arcCount.toString().length() > 0 && storyArc.toString().length() > 0) {
return "(" + arcNumber.toString() + "/" + arcCount.toString() + ") " + storyArc.toString();
}
if (arcNumber.toString().length() > 0 && storyArc.toString().length() > 0) {
return "(" + arcNumber.toString() + ") " + storyArc.toString();
}
return storyArc.toString().length() > 0 ? storyArc.toString() : "";
}
QString ComicInfo::getAlternateSeriesString()
{
if (alternateNumber.toString().length() > 0 && alternateCount.toString().length() > 0 && alternateSeries.toString().length() > 0) {
return "(" + alternateNumber.toString() + "/" + alternateCount.toString() + ") " + alternateSeries.toString();
}
if (alternateNumber.toString().length() > 0 && alternateSeries.toString().length() > 0) {
return "(" + alternateNumber.toString() + ") " + alternateSeries.toString();
}
return alternateSeries.toString().length() > 0 ? alternateSeries.toString() : "";
}
void ComicInfo::setRead(bool r)
{
if (r != read) {
@ -599,6 +668,24 @@ QDataStream &operator<<(QDataStream &stream, const ComicInfo &comicInfo)
stream << comicInfo.coverSizeRatio;
stream << comicInfo.originalCoverSize;
stream << comicInfo.added;
stream << comicInfo.type;
stream << comicInfo.added;
stream << comicInfo.type;
stream << comicInfo.editor;
stream << comicInfo.imprint;
stream << comicInfo.teams;
stream << comicInfo.locations;
stream << comicInfo.series;
stream << comicInfo.alternateSeries;
stream << comicInfo.alternateNumber;
stream << comicInfo.alternateCount;
stream << comicInfo.languageISO;
stream << comicInfo.seriesGroup;
stream << comicInfo.mainCharacterOrTeam;
stream << comicInfo.review;
stream << comicInfo.tags;
return stream;
}
@ -661,5 +748,23 @@ QDataStream &operator>>(QDataStream &stream, ComicInfo &comicInfo)
stream >> comicInfo.coverSizeRatio;
stream >> comicInfo.originalCoverSize;
stream >> comicInfo.added;
stream >> comicInfo.type;
stream >> comicInfo.added;
stream >> comicInfo.type;
stream >> comicInfo.editor;
stream >> comicInfo.imprint;
stream >> comicInfo.teams;
stream >> comicInfo.locations;
stream >> comicInfo.series;
stream >> comicInfo.alternateSeries;
stream >> comicInfo.alternateNumber;
stream >> comicInfo.alternateCount;
stream >> comicInfo.languageISO;
stream >> comicInfo.seriesGroup;
stream >> comicInfo.mainCharacterOrTeam;
stream >> comicInfo.review;
stream >> comicInfo.tags;
return stream;
}

View File

@ -33,7 +33,7 @@ public:
QString hash;
bool existOnDb;
int rating;
int rating; // TODO_METADATA: change to float
bool hasBeenOpened;
@ -52,13 +52,13 @@ public:
QVariant coverPage; // int
QVariant numPages; // int
QVariant number; // int
QVariant number; // string (changed in 9.13 from int)
QVariant isBis; // bool
QVariant count; // int
QVariant volume; // string
QVariant storyArc; // string
QVariant arcNumber; // int
QVariant arcNumber; // string (changed in 9.13 from int)
QVariant arcCount; // int
QVariant genere; // string
@ -80,7 +80,7 @@ public:
QVariant format; // string
QVariant color; // bool
QVariant ageRating; // string
QVariant manga; // bool
[[deprecated("use type instead")]] QVariant manga; // bool
QVariant synopsis; // string
QVariant characters; // string
@ -94,38 +94,21 @@ public:
QVariant coverSizeRatio; // h/w
QVariant originalCoverSize; // string "WxH"
/*void setTitle(QVariant value);
void setCoverPage(QVariant value);
void setNumPages(QVariant value);
void setNumber(QVariant value);
void setIsBis(QVariant value);
void setCount(QVariant value);
void setVolume(QVariant value);
void setStoryArc(QVariant value);
void setArcNumber(QVariant value);
void setArcCount(QVariant value);
void setGenere(QVariant value);
void setWriter(QVariant value);
void setPenciller(QVariant value);
void setInker(QVariant value);
void setColorist(QVariant value);
void setLetterer(QVariant value);
void setCoverArtist(QVariant value);
void setDate(QVariant value);
void setPublisher(QVariant value);
void setFormat(QVariant value);
void setColor(QVariant value);
void setAgeRating(QVariant value);
void setSynopsis(QVariant value);
void setCharacters(QVariant value);
void setNotes(QVariant value);*/
QVariant added; // integer/date
QVariant type; // enum
QVariant editor; // string
QVariant imprint; // string
QVariant teams; // string/list
QVariant locations; // string/list
QVariant series; // string
QVariant alternateSeries; // string
QVariant alternateNumber; // string
QVariant alternateCount; // int
QVariant languageISO; // string
QVariant seriesGroup; // string
QVariant mainCharacterOrTeam; // string
QVariant review; // string
QVariant tags; // string/list
QPixmap getCover(const QString &basePath);
@ -135,9 +118,21 @@ public:
Q_INVOKABLE QStringList getColorists();
Q_INVOKABLE QStringList getLetterers();
Q_INVOKABLE QStringList getCoverArtists();
Q_INVOKABLE QStringList getEditors();
Q_INVOKABLE QStringList getImprint();
Q_INVOKABLE QStringList getCharacters();
Q_INVOKABLE QStringList getTeams();
Q_INVOKABLE QStringList getLocations();
Q_INVOKABLE QStringList getTags();
Q_INVOKABLE QString getTypeString();
Q_INVOKABLE QString getStoryArcInfoString();
Q_INVOKABLE QString getAlternateSeriesString();
friend QDataStream &operator<<(QDataStream &stream, const ComicInfo &comicInfo);
friend QDataStream &operator>>(QDataStream &stream, ComicInfo &comicInfo);
@ -206,11 +201,27 @@ public:
Q_PROPERTY(QVariant coverSizeRatio MEMBER coverSizeRatio CONSTANT)
Q_PROPERTY(QVariant originalCoverSize MEMBER originalCoverSize CONSTANT)
Q_PROPERTY(QVariant added MEMBER added CONSTANT)
Q_PROPERTY(QVariant type MEMBER type CONSTANT)
Q_PROPERTY(QVariant editor MEMBER editor CONSTANT)
Q_PROPERTY(QVariant imprint MEMBER imprint CONSTANT)
Q_PROPERTY(QVariant teams MEMBER teams CONSTANT)
Q_PROPERTY(QVariant locations MEMBER locations CONSTANT)
Q_PROPERTY(QVariant series MEMBER series CONSTANT)
Q_PROPERTY(QVariant alternateSeries MEMBER alternateSeries CONSTANT)
Q_PROPERTY(QVariant alternateNumber MEMBER alternateNumber CONSTANT)
Q_PROPERTY(QVariant alternateCount MEMBER alternateCount CONSTANT)
Q_PROPERTY(QVariant languageISO MEMBER languageISO CONSTANT)
Q_PROPERTY(QVariant seriesGroup MEMBER seriesGroup CONSTANT)
Q_PROPERTY(QVariant mainCharacterOrTeam MEMBER mainCharacterOrTeam CONSTANT)
Q_PROPERTY(QVariant review MEMBER review CONSTANT)
Q_PROPERTY(QVariant tags MEMBER tags CONSTANT)
//-new properties, not loaded from the DB automatically
bool isFavorite;
Q_PROPERTY(bool isFavorite MEMBER isFavorite WRITE setFavorite NOTIFY favoriteChanged)
// setters, used in QML only by now
// setters, used in QML only for now
void setRead(bool r);
void setRating(int r);
void setFavorite(bool f);
@ -248,6 +259,7 @@ public:
Q_INVOKABLE qulonglong getFileSize() const;
Q_INVOKABLE QString getTitleIncludingNumber() const;
Q_INVOKABLE QString getInfoTitle() const;
QString toTXT();

View File

@ -20,7 +20,19 @@ Folder::Folder(qulonglong folderId, qulonglong parentId, const QString &folderNa
this->path = folderPath;
}
Folder::Folder(qulonglong folderId, qulonglong parentId, const QString &folderName, const QString &folderPath, bool completed, bool finished, bool manga)
Folder::Folder(qulonglong folderId,
qulonglong parentId,
const QString &folderName,
const QString &folderPath,
bool completed,
bool finished,
bool manga,
int numChildren,
const QString &firstChildHash,
const QString &customImage,
YACReader::FileType type,
qint64 added,
qint64 updated)
: knownParent(true),
knownId(true),
numChildren(-1)
@ -32,6 +44,12 @@ Folder::Folder(qulonglong folderId, qulonglong parentId, const QString &folderNa
this->completed = completed;
this->finished = finished;
this->manga = manga;
this->numChildren = numChildren;
this->firstChildHash = firstChildHash;
this->customImage = customImage;
this->type = type;
this->added = added;
this->updated = updated;
}
Folder::Folder(const Folder &folder)
@ -51,6 +69,9 @@ Folder &Folder::operator=(const Folder &other)
this->numChildren = other.numChildren;
this->firstChildHash = other.firstChildHash;
this->customImage = other.customImage;
this->type = other.type;
this->added = other.added;
this->updated = other.updated;
return *this;
}

View File

@ -2,6 +2,7 @@
#define __FOLDER_H
#include "library_item.h"
#include "yacreader_global.h"
#include <QList>
@ -11,9 +12,33 @@ public:
bool knownParent;
bool knownId;
bool finished; // finished means read, the user has read all the content in this folder
bool completed; // completed means the folder has all the content, e.g. a series got its final issue and the user has collected all of them
[[deprecated("use type instead")]] bool manga;
qint32 numChildren; //-1 for unknown number of children
QString firstChildHash; // empty for unknown first child
QString customImage; // empty for none custom image
YACReader::FileType type;
qint64 added; // epoch in seconds
qint64 updated; // epoch in seconds
Folder();
Folder(qulonglong folderId, qulonglong parentId, const QString &folderName, const QString &folderPath);
Folder(qulonglong folderId, qulonglong parentId, const QString &folderName, const QString &folderPath, bool completed, bool finished, bool manga);
Folder(qulonglong folderId,
qulonglong parentId,
const QString &folderName,
const QString &folderPath,
bool completed,
bool finished,
bool manga,
int numChildren,
const QString &firstChildHash,
const QString &customImage,
YACReader::FileType type,
qint64 added,
qint64 updated);
Folder(const QString &folderName, const QString &folderPath);
Folder(const Folder &folder);
Folder &operator=(const Folder &other);
@ -33,75 +58,6 @@ public:
{
return true;
}
inline bool isFinished() const
{
return finished;
}
inline bool isCompleted() const
{
return completed;
}
inline bool isManga() const
{
return manga;
}
inline void setFinished(bool b)
{
finished = b;
}
inline void setCompleted(bool b)
{
completed = b;
}
inline void setManga(bool b)
{
manga = b;
}
inline qint32 getNumChildren() const
{
return numChildren;
}
inline void setNumChildren(const qint32 v)
{
numChildren = v;
}
inline QString getFirstChildHash() const
{
return firstChildHash;
}
inline void setFirstChildHash(const QString &v)
{
firstChildHash = v;
}
inline QString getCustomImage() const
{
return customImage;
}
inline void setCustomImage(const QString &s)
{
customImage = s;
}
private:
bool finished; // finished means read, the user has read all the content in this folder
bool completed; // completed means the folder has all the content, e.g. a series got its final issue and the user has collected all of them
bool manga;
qint32 numChildren; //-1 for unknown number of children
QString firstChildHash; // empty for unknown first child
QString customImage; // empty for none custom image
};
#endif

View File

@ -7,6 +7,7 @@ int naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity cas
QCollator c;
c.setCaseSensitivity(caseSensitivity);
c.setNumericMode(true);
c.setIgnorePunctuation(false);
return c.compare(s1, s2);
}
bool naturalSortLessThanCS(const QString &left, const QString &right)

View File

@ -6,7 +6,7 @@
#include <QMetaType>
#include <QAbstractItemModel>
#define VERSION "9.12.0"
#define VERSION "9.13.0"
#define IMPORT_COMIC_INFO_XML_METADATA "IMPORT_COMIC_INFO_XML_METADATA"
@ -51,6 +51,14 @@ enum LabelColors {
YDark
};
enum class FileType : int {
Comic = 0,
Manga,
WesternManga,
WebComic, // continuous vertical reading
Yonkoma, // 4Koma
};
struct OpenComicSource {
enum Source {
Folder = 0,
@ -76,5 +84,6 @@ void iterate(const QModelIndex &index,
Q_DECLARE_METATYPE(YACReader::OpenComicSource::Source)
Q_DECLARE_METATYPE(YACReader::OpenComicSource)
Q_DECLARE_METATYPE(YACReader::FileType)
#endif

View File

@ -71,6 +71,8 @@
#define BLUR_RADIUS_BACKGROUND_IMAGE_IN_GRID_VIEW "BLUR_RADIUS_BACKGROUND_IMAGE_IN_GRID_VIEW"
#define USE_SELECTED_COMIC_COVER_AS_BACKGROUND_IMAGE_IN_GRID_VIEW "USE_SELECTED_COMIC_COVER_AS_BACKGROUND_IMAGE_IN_GRID_VIEW"
#define DISPLAY_CONTINUE_READING_IN_GRID_VIEW "DISPLAY_CONTINUE_READING_IN_GRID_VIEW"
#define DISPLAY_RECENTLY_INDICATOR "DISPLAY_RECENTLY_INDICATOR"
#define NUM_DAYS_TO_CONSIDER_RECENT "NUM_DAYS_TO_CONSIDER_RECENT"
namespace YACReader {

View File

@ -46,19 +46,16 @@ YACReader::WhatsNewDialog::WhatsNewDialog(QWidget *parent)
"color:#858585;");
auto text = new QLabel();
text->setText("Update to add support for remote search through the server:<br/>"
text->setText("New metadata support and better search engine:<br/>"
"<br/>"
"<span style=\"font-weight:600\">YACReaderLibrary</span><br/>"
" &#8226; Fix scroll in grid views when using Qt6 builds.<br/>"
" &#8226; Fix deleting metadata from comics, it also deleted the number of pages info.<br/>"
" &#8226; Do not accept empty values for the server port in the server settings dialog.<br/>"
" &#8226; New way of generating QR codes.<br/>"
"<br/>"
"<span style=\"font-weight:600\">YACReaderLibraryServer</span><br/>"
" &#8226; Print scannable QR code at server start.<br/>"
"<br/>"
"<span style=\"font-weight:600\">Server</span><br/>"
" &#8226; New search API that exposes the search engine. This will be used by the upcoming updates for the iOS & Android apps.<br/>"
" &#8226; Avoid showing stale information in the server config dialog by updating the connection information when the dialog is opened.<br/>"
" &#8226; Add new metadata support, it improves compatibility with ComicInfo.xml.<br/>"
" &#8226; Add support for showing a 'recently added/updated' indicator.<br/>"
" &#8226; Improved comic metadata dialog.<br/>"
" &#8226; Add textual tags support that can be queried through the search engine.<br/>"
" &#8226; Make '=' in the search engine work as ':' does.<br/>"
" &#8226; Add new operators to the search engine: exact match ==, <, >, <=, >=.<br/>"
"<br/>"
"I hope you enjoy the new update. Please, if you like YACReader consider to become a patron in <a href=\"https://www.patreon.com/yacreader\" style=\"color:#E8B800;\">Patreon</a> "
"or donate some money using <a href=\"https://www.paypal.com/donate?business=5TAMNQCDDMVP8&item_name=Support+YACReader\" style=\"color:#E8B800;\">Pay-Pal</a> and help keeping the project alive. "

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 B

View File

@ -51,6 +51,9 @@ public:
#define SET_AS_NON_READ_ACTION_YL "SET_AS_NON_READ_ACTION_YL"
#define SET_AS_MANGA_ACTION_YL "SET_AS_MANGA_ACTION_YL"
#define SET_AS_NORMAL_ACTION_YL "SET_AS_MANGA_ACTION_YL"
#define SET_AS_WESTERN_MANGA_ACTION_YL "SET_AS_WESTERN_MANGA_ACTION_YL"
#define SET_AS_WEB_COMIC_ACTION_YL "SET_AS_WEB_COMIC_ACTION_YL"
#define SET_AS_YONKOMA_ACTION_YL "SET_AS_YONKOMA_ACTION_YL"
#define SHOW_HIDE_MARKS_ACTION_YL "SHOW_HIDE_MARKS_ACTION_YL"
#define TOGGLE_FULL_SCREEN_ACTION_YL "TOGGLE_FULL_SCREEN_ACTION_YL"
#define HELP_ABOUT_ACTION_YL "HELP_ABOUT_ACTION_YL"
@ -66,7 +69,10 @@ public:
#define SET_FOLDER_AS_READ_ACTION_YL "SET_FOLDER_AS_READ_ACTION_YL"
#define SET_FOLDER_AS_UNREAD_ACTION_YL "SET_FOLDER_AS_UNREAD_ACTION_YL"
#define SET_FOLDER_AS_MANGA_ACTION_YL "SET_FOLDER_AS_MANGA_ACTION_YL"
#define SET_FOLDER_AS_WESTERN_MANGA_ACTION_YL "SET_FOLDER_AS_WESTERN_MANGA_ACTION_YL"
#define SET_FOLDER_AS_NORMAL_ACTION_YL "SET_FOLDER_AS_NORMAL_ACTION_YL"
#define SET_FOLDER_AS_WEB_COMIC_ACTION_YL "SET_FOLDER_AS_WEB_COMIC_ACTION_YL"
#define SET_FOLDER_AS_YONKOMA_ACTION_YL "SET_FOLDER_AS_YONKOMA_ACTION_YL"
#define OPEN_CONTAINING_FOLDER_COMIC_ACTION_YL "OPEN_CONTAINING_FOLDER_COMIC_ACTION_YL"
#define RESET_COMIC_RATING_ACTION_YL "RESET_COMIC_RATING_ACTION_YL"
#define SELECT_ALL_COMICS_ACTION_YL "SELECT_ALL_COMICS_ACTION_YL"
@ -91,6 +97,7 @@ public:
#define RENAME_LIST_ACTION_YL "RENAME_LIST_ACTION_YL"
#define ADD_TO_FAVORITES_ACTION_YL "ADD_TO_FAVORITES_ACTION_YL"
#define SAVE_COVERS_TO_ACTION_YL "SAVE_COVERS_TO_ACTION_YL"
#define SHOW_HIDE_RECENT_INDICATOR_ACTION_YL "SHOW_HIDE_RECENT_INDICATOR_ACTION_YL"
// COMMANDS YACReaderLibrary
// ACTION NAMES YACReader