diff --git a/CHANGELOG.md b/CHANGELOG.md index 218e335a0..bb2249f47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ Version counting is based on semantic versioning (Major.Feature.Patch) +## 9.16.4 + +### YACReaderLibrary +* Fix migration from pre-9.14 libraries. + ## 9.16.3 ### YACReader diff --git a/YACReader/configuration.h b/YACReader/configuration.h index 2845b1227..8572d16d0 100644 --- a/YACReader/configuration.h +++ b/YACReader/configuration.h @@ -115,6 +115,9 @@ class Configuration : public QObject MouseMode getMouseMode() { return static_cast(settings->value(MOUSE_MODE, MouseMode::Normal).toInt()); } void setMouseMode(MouseMode mouseMode) { settings->setValue(MOUSE_MODE, static_cast(mouseMode)); } + + bool getPinchToZoomEnabled() { return settings->value(PINCH_TO_ZOOM_ENABLED, true).toBool(); } + void setPinchToZoomEnabled(bool b) { settings->setValue(PINCH_TO_ZOOM_ENABLED, b); } }; } diff --git a/YACReader/options_dialog.cpp b/YACReader/options_dialog.cpp index 611ce8d38..602e43bf0 100644 --- a/YACReader/options_dialog.cpp +++ b/YACReader/options_dialog.cpp @@ -100,6 +100,12 @@ OptionsDialog::OptionsDialog(QWidget *parent) mouseModeBox->setLayout(mouseModeLayout); + auto zoomModeBox = new QGroupBox(tr("Zoom mode")); + auto zoomModeLayout = new QVBoxLayout(); + pinchToZoomEnabled = new QCheckBox(tr("Enable pinch-to-zoom trackpad gesture")); + zoomModeLayout->addWidget(pinchToZoomEnabled); + zoomModeBox->setLayout(zoomModeLayout); + layoutGeneral->addWidget(pathBox); layoutGeneral->addWidget(displayBox); layoutGeneral->addWidget(slideSizeBox); @@ -107,6 +113,7 @@ OptionsDialog::OptionsDialog(QWidget *parent) layoutGeneral->addWidget(colorBox); layoutGeneral->addWidget(scrollBox); layoutGeneral->addWidget(mouseModeBox); + layoutGeneral->addWidget(zoomModeBox); layoutGeneral->addWidget(shortcutsBox); layoutGeneral->addStretch(); @@ -281,6 +288,8 @@ void OptionsDialog::saveOptions() } Configuration::getConfiguration().setMouseMode(mouseMode); + Configuration::getConfiguration().setPinchToZoomEnabled(pinchToZoomEnabled->isChecked()); + YACReaderOptionsDialog::saveOptions(); } @@ -344,6 +353,8 @@ void OptionsDialog::restoreOptions(QSettings *settings) hotAreasMouseModeRadioButton->setChecked(true); break; } + + pinchToZoomEnabled->setChecked(Configuration::getConfiguration().getPinchToZoomEnabled()); } void OptionsDialog::updateColor(const QColor &color) diff --git a/YACReader/options_dialog.h b/YACReader/options_dialog.h index 0f15e196b..c56ee6345 100644 --- a/YACReader/options_dialog.h +++ b/YACReader/options_dialog.h @@ -59,6 +59,8 @@ class OptionsDialog : public YACReaderOptionsDialog QRadioButton *leftRightNavigationMouseModeRadioButton; QRadioButton *hotAreasMouseModeRadioButton; + QCheckBox *pinchToZoomEnabled; + public slots: void saveOptions() override; void restoreOptions(QSettings *settings) override; diff --git a/YACReader/viewer.cpp b/YACReader/viewer.cpp index b8ac3ccdc..80d521b45 100644 --- a/YACReader/viewer.cpp +++ b/YACReader/viewer.cpp @@ -20,6 +20,7 @@ #include #include +#include #include @@ -38,8 +39,23 @@ Viewer::Viewer(QWidget *parent) shouldOpenPrevious(false), magnifyingGlassShown(false), restoreMagnifyingGlass(false), + pinchStartZoom(100), + pinchAnchorNormX(0.5), + pinchAnchorNormY(0.5), + pinchZoomHud(nullptr), mouseHandler(std::make_unique(this)) { + grabGesture(Qt::PinchGesture); + + pinchZoomHud = new QLabel(this); + pinchZoomHud->setAlignment(Qt::AlignCenter); + pinchZoomHud->setAttribute(Qt::WA_TransparentForMouseEvents); + pinchZoomHud->setTextFormat(Qt::RichText); + pinchZoomHud->setStyleSheet( + "background-color: rgba(0, 0, 0, 153); border-radius: 3px;"); + pinchZoomHud->setFixedSize(100, 60); + pinchZoomHud->hide(); + translator = new YACReaderTranslator(this); translator->hide(); translatorAnimation = new QPropertyAnimation(translator, "pos"); @@ -1092,6 +1108,75 @@ void Viewer::mouseMoveEvent(QMouseEvent *event) mouseHandler->mouseMoveEvent(event); } +bool Viewer::event(QEvent *event) +{ + if (event->type() == QEvent::Gesture) { + return gestureEvent(static_cast(event)); + } + return QScrollArea::event(event); +} + +void Viewer::positionPinchZoomHud() +{ + const int margin = 16; + pinchZoomHud->move(width() - pinchZoomHud->width() - margin, + height() - pinchZoomHud->height() - margin); + pinchZoomHud->raise(); +} + +bool Viewer::gestureEvent(QGestureEvent *event) +{ + if (QGesture *g = event->gesture(Qt::PinchGesture)) { + auto *pinch = static_cast(g); + if (!Configuration::getConfiguration().getPinchToZoomEnabled()) { + pinchZoomHud->hide(); + event->ignore(pinch); + return QScrollArea::event(event); + } + if (!render->hasLoadedComic()) { + event->accept(pinch); + return true; + } + if (pinch->state() == Qt::GestureStarted) { + pinchStartZoom = zoom; + const QPoint cursorViewport = viewport()->mapFromGlobal(QCursor::pos()); + pinchAnchorViewport = cursorViewport; + if (content->width() > 0 && content->height() > 0) { + const QPoint cursorInContent = content->mapFrom(viewport(), cursorViewport); + pinchAnchorNormX = std::clamp(double(cursorInContent.x()) / content->width(), 0.0, 1.0); + pinchAnchorNormY = std::clamp(double(cursorInContent.y()) / content->height(), 0.0, 1.0); + } else { + pinchAnchorNormX = 0.5; + pinchAnchorNormY = 0.5; + } + } + int newZoom = std::clamp(std::lround(pinchStartZoom * pinch->totalScaleFactor()), 30, 500); + if (newZoom != zoom) { + zoom = newZoom; + updateContentSize(); + + const int alignX = std::max(0, (viewport()->width() - content->width()) / 2); + const int alignY = std::max(0, (viewport()->height() - content->height()) / 2); + const int targetH = std::lround(pinchAnchorNormX * content->width()) + alignX - pinchAnchorViewport.x(); + const int targetV = std::lround(pinchAnchorNormY * content->height()) + alignY - pinchAnchorViewport.y(); + horizontalScrollBar()->setValue(targetH); + verticalScrollBar()->setValue(targetV); + + pinchZoomHud->setText(QStringLiteral("%1%").arg(zoom)); + positionPinchZoomHud(); + pinchZoomHud->show(); + + emit zoomUpdated(zoom); + } + if (pinch->state() == Qt::GestureFinished || pinch->state() == Qt::GestureCanceled) { + pinchZoomHud->hide(); + } + event->accept(pinch); + return true; + } + return QScrollArea::event(event); +} + void Viewer::updateZoomRatio(int ratio) { zoom = ratio; diff --git a/YACReader/viewer.h b/YACReader/viewer.h index c2bb183b3..32c4aac90 100644 --- a/YACReader/viewer.h +++ b/YACReader/viewer.h @@ -15,6 +15,7 @@ #include #include #include +#include #include "scroll_management.h" #include "mouse_handler.h" @@ -166,6 +167,15 @@ public slots: void wheelEventMouse(QWheelEvent *event); void wheelEventTrackpad(QWheelEvent *event); void mouseMoveEvent(QMouseEvent *event) override; + bool event(QEvent *event) override; + bool gestureEvent(QGestureEvent *event); + void positionPinchZoomHud(); + + int pinchStartZoom; + QPoint pinchAnchorViewport; + double pinchAnchorNormX; + double pinchAnchorNormY; + QLabel *pinchZoomHud; int verticalScrollStep() const; int horizontalScrollStep() const; diff --git a/YACReaderLibrary/db/data_base_management.cpp b/YACReaderLibrary/db/data_base_management.cpp index 9ed48ef9b..2a3ad18bd 100644 --- a/YACReaderLibrary/db/data_base_management.cpp +++ b/YACReaderLibrary/db/data_base_management.cpp @@ -302,6 +302,87 @@ bool DataBaseManagement::createComicInfoTable(QSqlDatabase &database, QString ta return queryComicInfo.exec(); } +bool DataBaseManagement::createComicInfoTable9_14(QSqlDatabase &database, QString tableName) +{ + QSqlQuery queryComicInfo(database); + queryComicInfo.prepare("CREATE TABLE " + tableName + " (" + "id INTEGER PRIMARY KEY," + "title TEXT," + + "coverPage INTEGER DEFAULT 1," + "numPages INTEGER," + + "number TEXT," // changed to text from INTEGER (9.13) + "isBis BOOLEAN," + "count INTEGER," + + "volume TEXT," + "storyArc TEXT," + "arcNumber TEXT," // changed to text from INTEGER (9.13) + "arcCount INTEGER," + + "genere TEXT," + + "writer TEXT," + "penciller TEXT," + "inker TEXT," + "colorist TEXT," + "letterer TEXT," + "coverArtist TEXT," + + "date TEXT," // publication date dd/mm/yyyy --> se mostrarĂ¡ en 3 campos diferentes + "publisher TEXT," + "format TEXT," + "color BOOLEAN," + "ageRating TEXT," + + "synopsis TEXT," + "characters TEXT," + "notes TEXT," + + "hash TEXT UNIQUE NOT NULL," + "edited BOOLEAN DEFAULT 0," + "read BOOLEAN DEFAULT 0," + // new 7.0 fields + + "hasBeenOpened BOOLEAN DEFAULT 0," + "rating REAL DEFAULT 0," // changed to REAL from INTEGER (9.13) + "currentPage INTEGER DEFAULT 1, " + "bookmark1 INTEGER DEFAULT -1, " + "bookmark2 INTEGER DEFAULT -1, " + "bookmark3 INTEGER DEFAULT -1, " + "brightness INTEGER DEFAULT -1, " + "contrast INTEGER DEFAULT -1, " + "gamma INTEGER DEFAULT -1, " + // new 7.1 fields + "comicVineID TEXT," + // new 9.5 fields + "lastTimeOpened INTEGER," + "coverSizeRatio REAL," + "originalCoverSize STRING," // h/w + // new 9.8 fields + "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" + ")"); + + return queryComicInfo.exec(); +} + bool DataBaseManagement::createV8Tables(QSqlDatabase &database) { bool success = true; @@ -901,6 +982,9 @@ bool DataBaseManagement::updateToCurrentVersion(const QString &libraryPath) { QSqlDatabase db = loadDatabaseFromFile(libraryDatabasePath); if (db.isValid() && db.isOpen()) { + + QSqlQuery pragmaFKOFF("PRAGMA foreign_keys = OFF", db); + if (!db.transaction()) { QLOG_ERROR() << "Failed to start transaction for database update"; returnValue = false; @@ -1066,25 +1150,26 @@ bool DataBaseManagement::updateToCurrentVersion(const QString &libraryPath) { bool pre9_14_successfulMigration = true; - QSqlQuery pragmaFKOFF(db); - pragmaFKOFF.prepare("PRAGMA foreign_keys=OFF"); pre9_14_successfulMigration = pre9_14_successfulMigration && pragmaFKOFF.exec(); - pre9_14_successfulMigration = pre9_14_successfulMigration && createComicInfoTable(db, "comic_info_migration"); + pre9_14_successfulMigration = pre9_14_successfulMigration && createComicInfoTable9_14(db, "comic_info_migration"); QSqlQuery copyComicInfoToComicInfoMigration(db); copyComicInfoToComicInfoMigration.prepare("INSERT INTO comic_info_migration SELECT * FROM comic_info"); + pre9_14_successfulMigration = pre9_14_successfulMigration && copyComicInfoToComicInfoMigration.exec(); QSqlQuery dropComicInfo(db); dropComicInfo.prepare("DROP TABLE comic_info"); pre9_14_successfulMigration = pre9_14_successfulMigration && dropComicInfo.exec(); + QLOG_ERROR() << "Migration failed1:" << dropComicInfo.lastError().text(); + QSqlQuery renameComicInfoMigrationToComicInfo(db); renameComicInfoMigrationToComicInfo.prepare("ALTER TABLE comic_info_migration RENAME TO comic_info"); pre9_14_successfulMigration = pre9_14_successfulMigration && renameComicInfoMigrationToComicInfo.exec(); - QSqlQuery pragmaFKON1("PRAGMA foreign_keys=ON", db); + QLOG_ERROR() << "Migration failed2:" << renameComicInfoMigrationToComicInfo.lastError().text(); returnValue = returnValue && pre9_14_successfulMigration; } @@ -1125,7 +1210,10 @@ bool DataBaseManagement::updateToCurrentVersion(const QString &libraryPath) } else { db.rollback(); } + + QSqlQuery pragmaFKON("PRAGMA foreign_keys = ON", db); } + connectionName = db.connectionName(); } diff --git a/YACReaderLibrary/db/data_base_management.h b/YACReaderLibrary/db/data_base_management.h index e9c67a71b..ec4a5168a 100644 --- a/YACReaderLibrary/db/data_base_management.h +++ b/YACReaderLibrary/db/data_base_management.h @@ -71,6 +71,7 @@ class DataBaseManagement : public QObject static QSqlDatabase loadDatabaseFromFile(QString path); static bool createTables(QSqlDatabase &database); static bool createComicInfoTable(QSqlDatabase &database, QString tableName); + static bool createComicInfoTable9_14(QSqlDatabase &database, QString tableName); static bool createV8Tables(QSqlDatabase &database); static void exportComicsInfo(QString source, QString dest); diff --git a/common/yacreader_global.h b/common/yacreader_global.h index 36b1f5d79..7c2bf1ac5 100644 --- a/common/yacreader_global.h +++ b/common/yacreader_global.h @@ -9,7 +9,7 @@ class QLibrary; -#define VERSION "9.16.3" +#define VERSION "9.16.4" // Used to check if the database needs to be updated, the version is stored in the database. // This value is only incremented when the database structure changes. diff --git a/common/yacreader_global_gui.h b/common/yacreader_global_gui.h index a4e667e4a..dbdfb3856 100644 --- a/common/yacreader_global_gui.h +++ b/common/yacreader_global_gui.h @@ -38,6 +38,7 @@ #define USE_SINGLE_SCROLL_STEP_TO_TURN_PAGE "USE_SINGLE_SCROLL_STEP_TO_TURN_PAGE" #define DISABLE_SCROLL_ANIMATION "DISABLE_SCROLL_ANIMATION" #define MOUSE_MODE "MOUSE_MODE" +#define PINCH_TO_ZOOM_ENABLED "PINCH_TO_ZOOM_ENABLED" #define FLOW_TYPE_GL "FLOW_TYPE_GL" #define Y_POSITION "Y_POSITION"