mirror of
https://github.com/YACReader/yacreader
synced 2025-05-28 03:10:27 -04:00
Compare commits
63 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
a4ab11d44d | ||
|
4adad5f966 | ||
|
bed3503aae | ||
|
5950c52e19 | ||
|
8b159c9da6 | ||
|
28952180ab | ||
|
508ff6d547 | ||
|
0457c08fca | ||
|
97685dca73 | ||
|
b064095809 | ||
|
0ac22cdc5e | ||
|
156de8db9e | ||
|
b976b7f809 | ||
|
f0b9d45033 | ||
|
879d316391 | ||
|
6b1987a16a | ||
|
dfb1cb9b70 | ||
|
73bde73fdb | ||
|
868de856a7 | ||
|
e5f02bebe5 | ||
|
e7652355de | ||
|
51590aaa4f | ||
|
fca1e2004e | ||
|
1b4116db1a | ||
|
9d7a554ab3 | ||
|
89d7d76080 | ||
|
b81b7908f3 | ||
|
bba15bef4d | ||
|
90a370680e | ||
|
77e3f6ffb1 | ||
|
d291569e5f | ||
|
8b41bbf711 | ||
|
a51252ca0d | ||
|
25a569cfa6 | ||
|
257436c040 | ||
|
7db47f9147 | ||
|
9e37947479 | ||
|
a425aef45e | ||
|
cb24540766 | ||
|
ed7e9f98f0 | ||
|
3632ebab12 | ||
|
d9b9fda337 | ||
|
6e0e6f3bc3 | ||
|
254652f03e | ||
|
53b63de10f | ||
|
8f84fc7902 | ||
|
c5890ca7bf | ||
|
b73e8e60b1 | ||
|
52f1a57c47 | ||
|
181884a85f | ||
|
7f08ad0776 | ||
|
a0407c910c | ||
|
c142c2eeae | ||
|
b0d2d05bc9 | ||
|
d4b7c6dd8a | ||
|
5aa637fdbe | ||
|
4e675d4326 | ||
|
c148a96d7f | ||
|
af1af3976d | ||
|
bfc0e1bc95 | ||
|
89d16b620a | ||
|
31971c2348 | ||
|
1cd8635808 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -9,6 +9,7 @@ libp7zip
|
||||
YACReader/build
|
||||
YACReaderLibrary/build
|
||||
YACReaderLibraryServer/build
|
||||
build/
|
||||
|
||||
# C++ objects and libs
|
||||
*.slo
|
||||
|
23
CHANGELOG.md
23
CHANGELOG.md
@ -2,6 +2,29 @@
|
||||
|
||||
Version counting is based on semantic versioning (Major.Feature.Patch)
|
||||
|
||||
## WIP (9.15.1)
|
||||
### YACReader
|
||||
* Don't use scroll animations on macos by default, it where hdpi scroll is most likely to be used.
|
||||
* New toolbar on macos.
|
||||
* New mouse modes to turn pages. You can setup the app to use the left/right buttons to turn pages directly or you can click on the left/right part of the screen to turn pages.
|
||||
|
||||
### YACReaderLibrary
|
||||
* Improve flexibility of the open comic in third party app setting so more complex commands can be used. e.g. `open -a "/Applications/My Reader.app" "{comic_file_path}"`.
|
||||
* Fix setting the comic rating in the table view.
|
||||
* Log libraries validation when the app starts.
|
||||
* New toolbar on macos.
|
||||
* New setting in Comic Vine scraper to force exact volume matches.
|
||||
* Better default search query in the Comic Vine scraper.
|
||||
* Improved navigation in Comic Vine scraper, including keeping the current query around to make edits and refined searches easier.
|
||||
* Add support for custom covers for any folder using the context menu.
|
||||
* The edit cover buttons now support looping through pages, going forward from the last returns to the first, and going backward from the first jumps to the last.
|
||||
* Add support for custom covers for comics using the edit metadata dialog, you can use a pick file button or drag&drop an image into the cover view in the dialog.
|
||||
* Covers can be set in bulk for various comics at once.
|
||||
* New button to reset to the default cover of a comic.
|
||||
|
||||
### YACReaderLibraryServer
|
||||
* Log libraries validation when the app starts.
|
||||
|
||||
## 9.15.0
|
||||
|
||||
### YACReader
|
||||
|
@ -80,6 +80,7 @@ HEADERS += ../common/comic.h \
|
||||
goto_dialog.h \
|
||||
magnifying_glass.h \
|
||||
main_window_viewer.h \
|
||||
mouse_handler.h \
|
||||
viewer.h \
|
||||
goto_flow.h \
|
||||
options_dialog.h \
|
||||
@ -119,6 +120,7 @@ SOURCES += ../common/comic.cpp \
|
||||
goto_dialog.cpp \
|
||||
magnifying_glass.cpp \
|
||||
main_window_viewer.cpp \
|
||||
mouse_handler.cpp \
|
||||
viewer.cpp \
|
||||
goto_flow.cpp \
|
||||
options_dialog.cpp \
|
||||
|
@ -1,5 +1,6 @@
|
||||
#ifndef __CONFIGURATION_H
|
||||
#define __CONFIGURATION_H
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
#include <QSize>
|
||||
@ -15,6 +16,21 @@
|
||||
|
||||
using namespace YACReader;
|
||||
|
||||
namespace YACReader {
|
||||
|
||||
enum FitMode {
|
||||
ToWidth = 0x01,
|
||||
ToHeight = 0x02,
|
||||
FullRes = 0x03,
|
||||
FullPage = 0x04
|
||||
};
|
||||
|
||||
enum MouseMode {
|
||||
Normal,
|
||||
LeftRightNavigation,
|
||||
HotAreas
|
||||
};
|
||||
|
||||
class Configuration : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -84,7 +100,21 @@ public:
|
||||
bool getDoNotTurnPageOnScroll() { return settings->value(DO_NOT_TURN_PAGE_ON_SCROLL, false).toBool(); }
|
||||
bool getUseSingleScrollStepToTurnPage() { return settings->value(USE_SINGLE_SCROLL_STEP_TO_TURN_PAGE, false).toBool(); }
|
||||
void setDisableScrollAnimation(bool b) { settings->setValue(DISABLE_SCROLL_ANIMATION, b); }
|
||||
bool getDisableScrollAnimation() { return settings->value(DISABLE_SCROLL_ANIMATION, false).toBool(); }
|
||||
bool getDisableScrollAnimation()
|
||||
{
|
||||
#ifdef Q_OS_MACOS
|
||||
auto defaultValue = true;
|
||||
#else
|
||||
auto defaultValue = false;
|
||||
#endif
|
||||
|
||||
return settings->value(DISABLE_SCROLL_ANIMATION, defaultValue).toBool();
|
||||
}
|
||||
|
||||
MouseMode getMouseMode() { return static_cast<MouseMode>(settings->value(MOUSE_MODE, MouseMode::Normal).toInt()); }
|
||||
void setMouseMode(MouseMode mouseMode) { settings->setValue(MOUSE_MODE, static_cast<int>(mouseMode)); }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -625,7 +625,7 @@ void MainWindowViewer::createToolBars()
|
||||
|
||||
viewer->addAction(closeAction);
|
||||
|
||||
viewer->setContextMenuPolicy(Qt::ActionsContextMenu);
|
||||
updateContextMenuPolicy();
|
||||
|
||||
// MacOSX app menus
|
||||
#ifdef Q_OS_MACOS
|
||||
@ -777,9 +777,25 @@ void MainWindowViewer::openComicFromRecentAction(QAction *action)
|
||||
|
||||
void MainWindowViewer::reloadOptions()
|
||||
{
|
||||
updateContextMenuPolicy();
|
||||
viewer->updateConfig(settings);
|
||||
}
|
||||
|
||||
void MainWindowViewer::updateContextMenuPolicy()
|
||||
{
|
||||
auto mouseMode = Configuration::getConfiguration().getMouseMode();
|
||||
switch (mouseMode) {
|
||||
|
||||
case Normal:
|
||||
case HotAreas:
|
||||
viewer->setContextMenuPolicy(Qt::ActionsContextMenu);
|
||||
break;
|
||||
case LeftRightNavigation:
|
||||
viewer->setContextMenuPolicy(Qt::NoContextMenu);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindowViewer::open()
|
||||
{
|
||||
QFileDialog openDialog;
|
||||
@ -970,9 +986,11 @@ void MainWindowViewer::disablePreviousNextComicActions()
|
||||
|
||||
void MainWindowViewer::mouseDoubleClickEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
toggleFullScreen();
|
||||
event->accept();
|
||||
if (Configuration::getConfiguration().getMouseMode() == MouseMode::Normal) {
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
toggleFullScreen();
|
||||
event->accept();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1319,7 +1337,11 @@ void MainWindowViewer::toggleFitToWidthSlider()
|
||||
if (zoomSliderAction->isVisible()) {
|
||||
zoomSliderAction->hide();
|
||||
} else {
|
||||
#if defined(Y_MAC_UI) && (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||
zoomSliderAction->move((this->width() - zoomSliderAction->width()) / 2, y);
|
||||
#else
|
||||
zoomSliderAction->move(250, y);
|
||||
#endif
|
||||
zoomSliderAction->show();
|
||||
}
|
||||
}
|
||||
|
@ -67,6 +67,7 @@ public slots:
|
||||
void increasePageZoomLevel();
|
||||
void decreasePageZoomLevel();
|
||||
void reloadOptions();
|
||||
void updateContextMenuPolicy();
|
||||
void fitToWidth();
|
||||
void fitToHeight();
|
||||
void toggleWidthHeight();
|
||||
|
149
YACReader/mouse_handler.cpp
Normal file
149
YACReader/mouse_handler.cpp
Normal file
@ -0,0 +1,149 @@
|
||||
#include "mouse_handler.h"
|
||||
|
||||
#include <QtWidgets>
|
||||
|
||||
#include "configuration.h"
|
||||
#include "magnifying_glass.h"
|
||||
#include "render.h"
|
||||
#include "viewer.h"
|
||||
|
||||
#include "goto_flow.h"
|
||||
#ifndef NO_OPENGL
|
||||
#include "goto_flow_gl.h"
|
||||
#else
|
||||
#include <QtWidgets>
|
||||
#endif
|
||||
|
||||
using namespace YACReader;
|
||||
|
||||
YACReader::MouseHandler::MouseHandler(Viewer *viewer)
|
||||
: viewer(viewer)
|
||||
{
|
||||
}
|
||||
|
||||
void YACReader::MouseHandler::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
viewer->drag = true;
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
auto position = event->position();
|
||||
#else
|
||||
auto position = QPointF(event->x(), event->y());
|
||||
#endif
|
||||
dragOrigin = dragLatestPosition = position;
|
||||
viewer->setCursor(Qt::ClosedHandCursor);
|
||||
event->accept();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void YACReader::MouseHandler::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::ForwardButton) {
|
||||
viewer->right();
|
||||
event->accept();
|
||||
return;
|
||||
}
|
||||
|
||||
if (event->button() == Qt::BackButton) {
|
||||
viewer->left();
|
||||
event->accept();
|
||||
return;
|
||||
}
|
||||
|
||||
auto wasDragging = viewer->drag;
|
||||
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
viewer->drag = false;
|
||||
viewer->setCursor(Qt::OpenHandCursor);
|
||||
event->accept();
|
||||
}
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
auto position = event->position();
|
||||
#else
|
||||
auto position = QPointF(event->x(), event->y());
|
||||
#endif
|
||||
auto dragDistance = QLineF(position, dragOrigin).length();
|
||||
|
||||
auto mouseMode = Configuration::getConfiguration().getMouseMode();
|
||||
switch (mouseMode) {
|
||||
case Normal:
|
||||
return;
|
||||
case LeftRightNavigation:
|
||||
if (wasDragging && (dragDistance > 25)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
viewer->left();
|
||||
event->accept();
|
||||
return;
|
||||
}
|
||||
|
||||
if (event->button() == Qt::RightButton) {
|
||||
viewer->right();
|
||||
event->accept();
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
case HotAreas:
|
||||
if (wasDragging && (dragDistance > 25)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
if (position.x() < viewer->width() / 2) {
|
||||
viewer->left();
|
||||
} else {
|
||||
viewer->right();
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
void YACReader::MouseHandler::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
viewer->showCursor();
|
||||
viewer->hideCursorTimer->start(2500);
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
auto position = event->position();
|
||||
#else
|
||||
auto position = QPointF(event->x(), event->y());
|
||||
#endif
|
||||
|
||||
if (viewer->magnifyingGlassShown)
|
||||
viewer->mglass->move(static_cast<int>(position.x() - float(viewer->mglass->width()) / 2), static_cast<int>(position.y() - float(viewer->mglass->height()) / 2));
|
||||
|
||||
if (viewer->render->hasLoadedComic()) {
|
||||
if (viewer->showGoToFlowAnimation->state() != QPropertyAnimation::Running) {
|
||||
if (Configuration::getConfiguration().getDisableShowOnMouseOver() == false) {
|
||||
if (viewer->goToFlow->isVisible()) {
|
||||
QPoint gtfPos = viewer->goToFlow->mapFrom(this->viewer, event->pos());
|
||||
if (gtfPos.y() < 0 || gtfPos.x() < 0 || gtfPos.x() > viewer->goToFlow->width()) // TODO this extra check is for Mavericks (mouseMove over goToFlowGL seems to be broken)
|
||||
viewer->animateHideGoToFlow();
|
||||
// goToFlow->hide();
|
||||
} else {
|
||||
int umbral = (viewer->width() - viewer->goToFlow->width()) / 2;
|
||||
if ((position.y() > viewer->height() - 15) && (position.x() > umbral) && (position.x() < viewer->width() - umbral)) {
|
||||
|
||||
viewer->animateShowGoToFlow();
|
||||
viewer->hideCursorTimer->stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (viewer->drag) {
|
||||
int currentPosY = viewer->verticalScrollBar()->sliderPosition();
|
||||
int currentPosX = viewer->horizontalScrollBar()->sliderPosition();
|
||||
viewer->verticalScrollBar()->setSliderPosition(currentPosY + (dragLatestPosition.y() - position.y()));
|
||||
viewer->horizontalScrollBar()->setSliderPosition(currentPosX + (dragLatestPosition.x() - position.x()));
|
||||
dragLatestPosition = position;
|
||||
}
|
||||
}
|
||||
}
|
25
YACReader/mouse_handler.h
Normal file
25
YACReader/mouse_handler.h
Normal file
@ -0,0 +1,25 @@
|
||||
#ifndef MOUSE_HANDLER_H
|
||||
#define MOUSE_HANDLER_H
|
||||
|
||||
#include <QMouseEvent>
|
||||
|
||||
class Viewer;
|
||||
|
||||
namespace YACReader {
|
||||
class MouseHandler
|
||||
{
|
||||
public:
|
||||
MouseHandler(Viewer *viewer);
|
||||
|
||||
void mousePressEvent(QMouseEvent *event);
|
||||
void mouseReleaseEvent(QMouseEvent *event);
|
||||
void mouseMoveEvent(QMouseEvent *event);
|
||||
|
||||
private:
|
||||
Viewer *viewer;
|
||||
QPointF dragOrigin;
|
||||
QPointF dragLatestPosition;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // MOUSE_HANDLER_H
|
@ -81,11 +81,25 @@ OptionsDialog::OptionsDialog(QWidget *parent)
|
||||
|
||||
scrollBox->setLayout(scrollLayout);
|
||||
|
||||
auto mouseModeBox = new QGroupBox(tr("Mouse mode"));
|
||||
auto mouseModeLayout = new QVBoxLayout();
|
||||
|
||||
normalMouseModeRadioButton = new QRadioButton(tr("Only Back/Forward buttons can turn pages"));
|
||||
leftRightNavigationMouseModeRadioButton = new QRadioButton(tr("Use the Left/Right buttons to turn pages."));
|
||||
hotAreasMouseModeRadioButton = new QRadioButton(tr("Click left or right half of the screen to turn pages."));
|
||||
|
||||
mouseModeLayout->addWidget(normalMouseModeRadioButton);
|
||||
mouseModeLayout->addWidget(leftRightNavigationMouseModeRadioButton);
|
||||
mouseModeLayout->addWidget(hotAreasMouseModeRadioButton);
|
||||
|
||||
mouseModeBox->setLayout(mouseModeLayout);
|
||||
|
||||
layoutGeneral->addWidget(pathBox);
|
||||
layoutGeneral->addWidget(slideSizeBox);
|
||||
// layoutGeneral->addWidget(fitBox);
|
||||
layoutGeneral->addWidget(colorBox);
|
||||
layoutGeneral->addWidget(scrollBox);
|
||||
layoutGeneral->addWidget(mouseModeBox);
|
||||
layoutGeneral->addWidget(shortcutsBox);
|
||||
layoutGeneral->addStretch();
|
||||
|
||||
@ -246,6 +260,18 @@ void OptionsDialog::saveOptions()
|
||||
settings->setValue(USE_SINGLE_SCROLL_STEP_TO_TURN_PAGE, useSingleScrollStepToTurnPage->isChecked());
|
||||
settings->setValue(DISABLE_SCROLL_ANIMATION, disableScrollAnimations->isChecked());
|
||||
|
||||
// get checked radio button to get the mouse mode
|
||||
YACReader::MouseMode mouseMode = Normal;
|
||||
if (normalMouseModeRadioButton->isChecked()) {
|
||||
mouseMode = Normal;
|
||||
;
|
||||
} else if (leftRightNavigationMouseModeRadioButton->isChecked()) {
|
||||
mouseMode = LeftRightNavigation;
|
||||
} else if (hotAreasMouseModeRadioButton->isChecked()) {
|
||||
mouseMode = HotAreas;
|
||||
}
|
||||
Configuration::getConfiguration().setMouseMode(mouseMode);
|
||||
|
||||
YACReaderOptionsDialog::saveOptions();
|
||||
}
|
||||
|
||||
@ -286,7 +312,27 @@ void OptionsDialog::restoreOptions(QSettings *settings)
|
||||
|
||||
doNotTurnPageOnScroll->setChecked(settings->value(DO_NOT_TURN_PAGE_ON_SCROLL, false).toBool());
|
||||
useSingleScrollStepToTurnPage->setChecked(settings->value(USE_SINGLE_SCROLL_STEP_TO_TURN_PAGE, false).toBool());
|
||||
disableScrollAnimations->setChecked(settings->value(DISABLE_SCROLL_ANIMATION, false).toBool());
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
auto defaultDisableScrollAnimationsValue = true;
|
||||
#else
|
||||
auto defaultDisableScrollAnimationsValue = false;
|
||||
#endif
|
||||
disableScrollAnimations->setChecked(settings->value(DISABLE_SCROLL_ANIMATION, defaultDisableScrollAnimationsValue).toBool());
|
||||
|
||||
auto mouseMode = Configuration::getConfiguration().getMouseMode();
|
||||
|
||||
switch (mouseMode) {
|
||||
case Normal:
|
||||
normalMouseModeRadioButton->setChecked(true);
|
||||
break;
|
||||
case LeftRightNavigation:
|
||||
leftRightNavigationMouseModeRadioButton->setChecked(true);
|
||||
break;
|
||||
case HotAreas:
|
||||
hotAreasMouseModeRadioButton->setChecked(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OptionsDialog::updateColor(const QColor &color)
|
||||
|
@ -51,6 +51,11 @@ private:
|
||||
YACReaderSpinSliderWidget *gammaS;
|
||||
|
||||
QColor currentColor;
|
||||
|
||||
QRadioButton *normalMouseModeRadioButton;
|
||||
QRadioButton *leftRightNavigationMouseModeRadioButton;
|
||||
QRadioButton *hotAreasMouseModeRadioButton;
|
||||
|
||||
public slots:
|
||||
void saveOptions() override;
|
||||
void restoreOptions(QSettings *settings) override;
|
||||
|
@ -1,5 +1,4 @@
|
||||
#include "viewer.h"
|
||||
#include "magnifying_glass.h"
|
||||
#include "configuration.h"
|
||||
#include "magnifying_glass.h"
|
||||
#include "goto_flow.h"
|
||||
@ -38,7 +37,8 @@ Viewer::Viewer(QWidget *parent)
|
||||
shouldOpenNext(false),
|
||||
shouldOpenPrevious(false),
|
||||
magnifyingGlassShown(false),
|
||||
restoreMagnifyingGlass(false)
|
||||
restoreMagnifyingGlass(false),
|
||||
mouseHandler(std::make_unique<YACReader::MouseHandler>(this))
|
||||
{
|
||||
translator = new YACReaderTranslator(this);
|
||||
translator->hide();
|
||||
@ -260,6 +260,10 @@ void Viewer::processCRCError(QString message)
|
||||
|
||||
void Viewer::next()
|
||||
{
|
||||
if (!render->hasLoadedComic()) {
|
||||
return;
|
||||
}
|
||||
|
||||
direction = 1;
|
||||
if (doublePage && render->currentPageIsDoublePage()) {
|
||||
render->nextDoublePage();
|
||||
@ -272,6 +276,10 @@ void Viewer::next()
|
||||
|
||||
void Viewer::left()
|
||||
{
|
||||
if (!render->hasLoadedComic()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (doubleMangaPage) {
|
||||
next();
|
||||
} else {
|
||||
@ -281,6 +289,10 @@ void Viewer::left()
|
||||
|
||||
void Viewer::right()
|
||||
{
|
||||
if (!render->hasLoadedComic()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (doubleMangaPage) {
|
||||
prev();
|
||||
} else {
|
||||
@ -290,6 +302,10 @@ void Viewer::right()
|
||||
|
||||
void Viewer::prev()
|
||||
{
|
||||
if (!render->hasLoadedComic()) {
|
||||
return;
|
||||
}
|
||||
|
||||
direction = -1;
|
||||
if (doublePage && render->previousPageIsDoublePage()) {
|
||||
render->previousDoublePage();
|
||||
@ -767,44 +783,6 @@ void Viewer::resizeEvent(QResizeEvent *event)
|
||||
QScrollArea::resizeEvent(event);
|
||||
}
|
||||
|
||||
void Viewer::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
showCursor();
|
||||
hideCursorTimer->start(2500);
|
||||
|
||||
if (magnifyingGlassShown)
|
||||
mglass->move(static_cast<int>(event->x() - float(mglass->width()) / 2), static_cast<int>(event->y() - float(mglass->height()) / 2));
|
||||
|
||||
if (render->hasLoadedComic()) {
|
||||
if (showGoToFlowAnimation->state() != QPropertyAnimation::Running) {
|
||||
if (Configuration::getConfiguration().getDisableShowOnMouseOver() == false) {
|
||||
if (goToFlow->isVisible()) {
|
||||
QPoint gtfPos = goToFlow->mapFrom(this, event->pos());
|
||||
if (gtfPos.y() < 0 || gtfPos.x() < 0 || gtfPos.x() > goToFlow->width()) // TODO this extra check is for Mavericks (mouseMove over goToFlowGL seems to be broken)
|
||||
animateHideGoToFlow();
|
||||
// goToFlow->hide();
|
||||
} else {
|
||||
int umbral = (width() - goToFlow->width()) / 2;
|
||||
if ((event->y() > height() - 15) && (event->x() > umbral) && (event->x() < width() - umbral)) {
|
||||
|
||||
animateShowGoToFlow();
|
||||
hideCursorTimer->stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (drag) {
|
||||
int currentPosY = verticalScrollBar()->sliderPosition();
|
||||
int currentPosX = horizontalScrollBar()->sliderPosition();
|
||||
verticalScrollBar()->setSliderPosition(currentPosY + (yDragOrigin - event->y()));
|
||||
horizontalScrollBar()->setSliderPosition(currentPosX + (xDragOrigin - event->x()));
|
||||
yDragOrigin = event->y();
|
||||
xDragOrigin = event->x();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QPixmap Viewer::pixmap() const
|
||||
{
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
@ -1093,36 +1071,17 @@ void Viewer::animateHideTranslator()
|
||||
|
||||
void Viewer::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
drag = true;
|
||||
yDragOrigin = event->y();
|
||||
xDragOrigin = event->x();
|
||||
setCursor(Qt::ClosedHandCursor);
|
||||
event->accept();
|
||||
return;
|
||||
}
|
||||
mouseHandler->mousePressEvent(event);
|
||||
}
|
||||
|
||||
void Viewer::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
drag = false;
|
||||
setCursor(Qt::OpenHandCursor);
|
||||
event->accept();
|
||||
return;
|
||||
}
|
||||
mouseHandler->mouseReleaseEvent(event);
|
||||
}
|
||||
|
||||
if (event->button() == Qt::ForwardButton) {
|
||||
right();
|
||||
event->accept();
|
||||
return;
|
||||
}
|
||||
|
||||
if (event->button() == Qt::BackButton) {
|
||||
left();
|
||||
event->accept();
|
||||
return;
|
||||
}
|
||||
void Viewer::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
mouseHandler->mouseMoveEvent(event);
|
||||
}
|
||||
|
||||
void Viewer::updateZoomRatio(int ratio)
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <QSettings>
|
||||
|
||||
#include "scroll_management.h"
|
||||
#include "mouse_handler.h"
|
||||
|
||||
class ComicDB;
|
||||
class Comic;
|
||||
@ -147,9 +148,6 @@ private:
|
||||
int translatorXPos;
|
||||
QPropertyAnimation *translatorAnimation;
|
||||
|
||||
int yDragOrigin;
|
||||
int xDragOrigin;
|
||||
|
||||
NotificationsLabelWidget *notificationsLabel;
|
||||
|
||||
bool shouldOpenNext;
|
||||
@ -185,6 +183,9 @@ private:
|
||||
int animationDuration() const;
|
||||
void animateScroll(QPropertyAnimation &scroller, const QScrollBar &scrollBar, int delta);
|
||||
|
||||
//! Mouse handler
|
||||
std::unique_ptr<YACReader::MouseHandler> mouseHandler;
|
||||
|
||||
public:
|
||||
Viewer(QWidget *parent = nullptr);
|
||||
~Viewer();
|
||||
@ -213,6 +214,8 @@ signals:
|
||||
void magnifyingGlassZoomIn();
|
||||
void magnifyingGlassZoomOut();
|
||||
void resetMagnifyingGlass();
|
||||
|
||||
friend class YACReader::MouseHandler;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -74,6 +74,7 @@ greaterThan(QT_MAJOR_VERSION, 5): QT += openglwidgets core5compat
|
||||
# Input
|
||||
HEADERS += comic_flow.h \
|
||||
../common/concurrent_queue.h \
|
||||
../common/cover_utils.h \
|
||||
create_library_dialog.h \
|
||||
db/comic_query_result_processor.h \
|
||||
db/folder_query_result_processor.h \
|
||||
@ -163,6 +164,7 @@ HEADERS += comic_flow.h \
|
||||
|
||||
SOURCES += comic_flow.cpp \
|
||||
../common/concurrent_queue.cpp \
|
||||
../common/cover_utils.cpp \
|
||||
create_library_dialog.cpp \
|
||||
db/comic_query_result_processor.cpp \
|
||||
db/folder_query_result_processor.cpp \
|
||||
|
@ -1,6 +1,9 @@
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/comic_vine_json_parser.h \
|
||||
$$PWD/model/selected_volume_info.h \
|
||||
$$PWD/model/volume_search_query.h \
|
||||
$$PWD/scraper_checkbox.h \
|
||||
comic_vine/comic_vine_dialog.h \
|
||||
comic_vine/comic_vine_client.h \
|
||||
comic_vine/scraper_lineedit.h \
|
||||
@ -20,12 +23,12 @@ HEADERS += \
|
||||
comic_vine/model/volume_comics_model.h \
|
||||
comic_vine/scraper_scroll_label.h \
|
||||
comic_vine/scraper_results_paginator.h \
|
||||
comic_vine/scraper_selector.h \
|
||||
comic_vine/api_key_dialog.h \
|
||||
$$PWD/comic_vine_all_volume_comics_retriever.h
|
||||
|
||||
SOURCES += \
|
||||
$$PWD/comic_vine_json_parser.cpp \
|
||||
$$PWD/scraper_checkbox.cpp \
|
||||
comic_vine/comic_vine_dialog.cpp \
|
||||
comic_vine/comic_vine_client.cpp \
|
||||
comic_vine/scraper_lineedit.cpp \
|
||||
@ -45,6 +48,5 @@ SOURCES += \
|
||||
comic_vine/model/volume_comics_model.cpp \
|
||||
comic_vine/scraper_scroll_label.cpp \
|
||||
comic_vine/scraper_results_paginator.cpp \
|
||||
comic_vine/scraper_selector.cpp \
|
||||
comic_vine/api_key_dialog.cpp \
|
||||
$$PWD/comic_vine_all_volume_comics_retriever.cpp
|
||||
|
@ -18,6 +18,13 @@ static const QString CV_SEARCH = CV_WEB_ADDRESS + "/search/?api_key=" + CV_API_K
|
||||
"&query=%1&page=%2";
|
||||
// http://www.comicvine.com/api/search/?api_key=46680bebb358f1de690a5a365e15d325f9649f91&format=json&limit=100&resources=volume&field_list=name,start_year,publisher,id,image,count_of_issues,deck&query=superman
|
||||
|
||||
// look for exact match volumes
|
||||
static const QString CV_EXACT_VOLUME_SEARCH = CV_WEB_ADDRESS + "/volumes/?api_key=" + CV_API_KEY +
|
||||
"&format=json&limit=100"
|
||||
"&field_list=name,start_year,publisher,id,image,count_of_issues,deck"
|
||||
"&sort=name:asc"
|
||||
"&filter=name:%1&offset=%2";
|
||||
|
||||
// gets the detail for a volume %1
|
||||
static const QString CV_SERIES_DETAIL = CV_WEB_ADDRESS + "/volume/4050-%1/?api_key=" + CV_API_KEY +
|
||||
"&format=json&field_list=name,start_year,publisher,image,count_of_issues,id,description";
|
||||
@ -69,6 +76,17 @@ void ComicVineClient::search(const QString &query, int page)
|
||||
connect(search, &QThread::finished, search, &QObject::deleteLater);
|
||||
search->get();
|
||||
}
|
||||
|
||||
// CV_EXACT_VOLUME_SEARCH
|
||||
void ComicVineClient::searchExactVolume(const QString &query, int page)
|
||||
{
|
||||
HttpWorker *search = new HttpWorker(QString(CV_EXACT_VOLUME_SEARCH).replace(CV_WEB_ADDRESS, baseURL).replace(CV_API_KEY, settings->value(COMIC_VINE_API_KEY, CV_API_KEY_DEFAULT).toString()).arg(query).arg((page - 1) * 100));
|
||||
connect(search, &HttpWorker::dataReady, this, &ComicVineClient::proccessVolumesSearchData);
|
||||
connect(search, &HttpWorker::timeout, this, &ComicVineClient::timeOut);
|
||||
connect(search, &QThread::finished, search, &QObject::deleteLater);
|
||||
search->get();
|
||||
}
|
||||
|
||||
// CV_SEARCH result
|
||||
void ComicVineClient::proccessVolumesSearchData(const QByteArray &data)
|
||||
{
|
||||
@ -120,7 +138,7 @@ void ComicVineClient::getSeriesCover(const QString &url)
|
||||
// CV_COMIC_IDS
|
||||
void ComicVineClient::getVolumeComicsInfo(const QString &idVolume, int page)
|
||||
{
|
||||
HttpWorker *search = new HttpWorker(QString(CV_COMICS_INFO).replace(CV_WEB_ADDRESS, baseURL).replace(CV_API_KEY, settings->value(COMIC_VINE_API_KEY, CV_API_KEY_DEFAULT).toString()).arg(idVolume).arg((page - 1) * 100)); // page doesn't work for search, using offset instead
|
||||
HttpWorker *search = new HttpWorker(QString(CV_COMICS_INFO).replace(CV_WEB_ADDRESS, baseURL).replace(CV_API_KEY, settings->value(COMIC_VINE_API_KEY, CV_API_KEY_DEFAULT).toString()).arg(idVolume).arg((page - 1) * 100));
|
||||
connect(search, &HttpWorker::dataReady, this, &ComicVineClient::processVolumeComicsInfo);
|
||||
connect(search, &HttpWorker::timeout, this, &ComicVineClient::timeOut); // TODO
|
||||
connect(search, &QThread::finished, search, &QObject::deleteLater);
|
||||
|
@ -24,6 +24,7 @@ signals:
|
||||
void finished();
|
||||
public slots:
|
||||
void search(const QString &query, int page = 1);
|
||||
void searchExactVolume(const QString &query, int page = 1);
|
||||
void getSeriesDetail(const QString &id);
|
||||
void getSeriesCover(const QString &url);
|
||||
void getVolumeComicsInfo(const QString &idVolume, int page = 1);
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "search_volume.h"
|
||||
#include "select_comic.h"
|
||||
#include "select_volume.h"
|
||||
#include "selected_volume_info.h"
|
||||
#include "sort_volume_comics.h"
|
||||
#include "db_helper.h"
|
||||
#include "response_parser.h"
|
||||
@ -93,7 +94,7 @@ void ComicVineDialog::doLayout()
|
||||
|
||||
setLayout(mainLayout);
|
||||
|
||||
setWindowTitle("Comic Vine Scraper (beta)");
|
||||
setWindowTitle("Comic Vine Scraper");
|
||||
}
|
||||
|
||||
void ComicVineDialog::doStackedWidgets()
|
||||
@ -115,9 +116,9 @@ void ComicVineDialog::doConnections()
|
||||
connect(searchButton, &QAbstractButton::clicked, this, &ComicVineDialog::search);
|
||||
connect(skipButton, &QAbstractButton::clicked, this, &ComicVineDialog::goToNextComic);
|
||||
|
||||
connect(selectVolumeWidget, &ScraperSelector::loadPage, this, &ComicVineDialog::searchVolume);
|
||||
connect(selectComicWidget, &ScraperSelector::loadPage, this, &ComicVineDialog::getVolumeComicsInfo);
|
||||
connect(sortVolumeComicsWidget, &ScraperSelector::loadPage, this, &ComicVineDialog::getVolumeComicsInfo);
|
||||
connect(selectVolumeWidget, &SelectVolume::loadPage, this, &ComicVineDialog::searchVolume);
|
||||
connect(selectComicWidget, &SelectComic::loadPage, this, &ComicVineDialog::getVolumeComicsInfo);
|
||||
connect(sortVolumeComicsWidget, &SortVolumeComics::loadPage, this, &ComicVineDialog::getVolumeComicsInfo);
|
||||
|
||||
connect(this, &QDialog::accepted, this, &QWidget::close, Qt::QueuedConnection);
|
||||
}
|
||||
@ -130,21 +131,22 @@ void ComicVineDialog::goNext()
|
||||
if (content->currentWidget() == seriesQuestionWidget) {
|
||||
if (seriesQuestionWidget->getYes()) {
|
||||
QString volumeSearchString = comics[0].getParentFolderName();
|
||||
mode = Volume;
|
||||
mode = ScraperMode::Volume;
|
||||
|
||||
showSearchVolume(volumeSearchString);
|
||||
} else {
|
||||
status = AutoSearching;
|
||||
mode = SingleComicInList;
|
||||
status = ScraperStatus::AutoSearching;
|
||||
mode = ScraperMode::SingleComicInList;
|
||||
ComicDB comic = comics[currentIndex];
|
||||
QString title = comic.getTitleOrFileName();
|
||||
titleHeader->setSubTitle(tr("comic %1 of %2 - %3").arg(currentIndex + 1).arg(comics.length()).arg(title));
|
||||
|
||||
showLoading(tr("Looking for volume..."));
|
||||
searchVolume(title);
|
||||
|
||||
searchVolume({ volumeSearchStringFromComic(comic), 1, true });
|
||||
}
|
||||
} else if (content->currentWidget() == selectVolumeWidget) {
|
||||
currentVolumeId = selectVolumeWidget->getSelectedVolumeId();
|
||||
currentVolumeId = selectVolumeWidget->getSelectedVolumeInfo().id;
|
||||
getVolumeComicsInfo(currentVolumeId);
|
||||
|
||||
} else if (content->currentWidget() == sortVolumeComicsWidget) {
|
||||
@ -152,24 +154,23 @@ void ComicVineDialog::goNext()
|
||||
|
||||
// ComicDB-ComicVineID
|
||||
QList<QPair<ComicDB, QString>> matchingInfo = sortVolumeComicsWidget->getMatchingInfo();
|
||||
int count = selectVolumeWidget->getSelectedVolumeNumIssues();
|
||||
QString publisher = selectVolumeWidget->getSelectedVolumePublisher();
|
||||
auto volumeInfo = selectVolumeWidget->getSelectedVolumeInfo();
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
QtConcurrent::run(&ComicVineDialog::getComicsInfo, this, matchingInfo, count, publisher);
|
||||
QtConcurrent::run(&ComicVineDialog::getComicsInfo, this, matchingInfo, volumeInfo);
|
||||
#else
|
||||
QtConcurrent::run(this, &ComicVineDialog::getComicsInfo, matchingInfo, count, publisher);
|
||||
QtConcurrent::run(this, &ComicVineDialog::getComicsInfo, matchingInfo, volumeInfo);
|
||||
#endif
|
||||
|
||||
} else if (content->currentWidget() == selectComicWidget) {
|
||||
showLoading();
|
||||
QString comicId = selectComicWidget->getSelectedComicId();
|
||||
int count = selectVolumeWidget->getSelectedVolumeNumIssues();
|
||||
QString publisher = selectVolumeWidget->getSelectedVolumePublisher();
|
||||
auto volumeInfo = selectVolumeWidget->getSelectedVolumeInfo();
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
QtConcurrent::run(&ComicVineDialog::getComicInfo, this, comicId, count, publisher);
|
||||
QtConcurrent::run(&ComicVineDialog::getComicInfo, this, comicId, volumeInfo);
|
||||
#else
|
||||
QtConcurrent::run(this, &ComicVineDialog::getComicInfo, comicId, count, publisher);
|
||||
QtConcurrent::run(this, &ComicVineDialog::getComicInfo, comicId, volumeInfo);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@ -179,30 +180,35 @@ void ComicVineDialog::goBack()
|
||||
clearState();
|
||||
|
||||
switch (status) {
|
||||
case SelectingSeries:
|
||||
if (mode == Volume)
|
||||
showSearchVolume();
|
||||
case ScraperStatus::SelectingSeries:
|
||||
if (mode == ScraperMode::Volume)
|
||||
showSearchVolume(currentVolumeSearchQuery.volume);
|
||||
else
|
||||
showSearchSingleComic();
|
||||
showSearchSingleComic(currentVolumeSearchQuery.volume);
|
||||
break;
|
||||
case SortingComics:
|
||||
case ScraperStatus::SortingComics:
|
||||
showSelectVolume();
|
||||
break;
|
||||
case SelectingComic:
|
||||
if (mode == SingleComic)
|
||||
case ScraperStatus::SelectingComic:
|
||||
if (mode == ScraperMode::SingleComic)
|
||||
showSelectVolume();
|
||||
break;
|
||||
case AutoSearching:
|
||||
if (mode == Volume)
|
||||
showSearchVolume();
|
||||
case ScraperStatus::AutoSearching:
|
||||
if (mode == ScraperMode::Volume)
|
||||
showSearchVolume(currentVolumeSearchQuery.volume);
|
||||
else
|
||||
showSearchSingleComic();
|
||||
showSearchSingleComic(currentVolumeSearchQuery.volume);
|
||||
break;
|
||||
default:
|
||||
if (mode == Volume)
|
||||
showSearchVolume();
|
||||
|
||||
case ScraperStatus::AskingForInfo:
|
||||
case ScraperStatus::SearchingSingleComic:
|
||||
case ScraperStatus::SearchingVolume:
|
||||
case ScraperStatus::SearchingExactVolume:
|
||||
case ScraperStatus::GettingVolumeComics:
|
||||
if (mode == ScraperMode::Volume)
|
||||
showSearchVolume(currentVolumeSearchQuery.volume);
|
||||
else
|
||||
showSearchSingleComic();
|
||||
showSearchSingleComic(currentVolumeSearchQuery.volume);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -248,16 +254,15 @@ void ComicVineDialog::show()
|
||||
searchVolumeWidget->clean();
|
||||
|
||||
if (comics.length() == 1) {
|
||||
status = AutoSearching;
|
||||
mode = SingleComic;
|
||||
status = ScraperStatus::AskingForInfo;
|
||||
mode = ScraperMode::SingleComic;
|
||||
|
||||
ComicDB singleComic = comics[0];
|
||||
|
||||
QString title = singleComic.getTitleOrFileName();
|
||||
titleHeader->setSubTitle(title);
|
||||
showLoading(tr("Looking for volume..."));
|
||||
|
||||
searchVolume(singleComic.getParentFolderName());
|
||||
QLOG_TRACE() << singleComic.getParentFolderName();
|
||||
showSearchSingleComic(volumeSearchStringFromComic(singleComic));
|
||||
} else if (comics.length() > 1) {
|
||||
titleHeader->setSubTitle(tr("%1 comics selected").arg(comics.length()));
|
||||
showSeriesQuestion();
|
||||
@ -286,7 +291,7 @@ void ComicVineDialog::doLoading()
|
||||
content->addWidget(w);
|
||||
}
|
||||
|
||||
void ComicVineDialog::debugClientResults(const QString &string)
|
||||
void ComicVineDialog::processClientResults(const QString &string)
|
||||
{
|
||||
ResponseParser p;
|
||||
p.loadJSONResponse(string);
|
||||
@ -295,19 +300,20 @@ void ComicVineDialog::debugClientResults(const QString &string)
|
||||
QMessageBox::critical(0, tr("Error connecting to ComicVine"), p.errorDescription());
|
||||
goBack();
|
||||
} else {
|
||||
|
||||
switch (mode) {
|
||||
case SingleComic:
|
||||
case SingleComicInList:
|
||||
case ScraperMode::SingleComic:
|
||||
case ScraperMode::SingleComicInList:
|
||||
if (p.getNumResults() == 0)
|
||||
showSearchSingleComic();
|
||||
else if (status == SearchingVolume)
|
||||
showSearchSingleComic(currentVolumeSearchQuery.volume);
|
||||
else if (status == ScraperStatus::SearchingVolume || status == ScraperStatus::SearchingExactVolume)
|
||||
showSelectVolume(string);
|
||||
else
|
||||
showSelectComic(string);
|
||||
break;
|
||||
case Volume:
|
||||
case ScraperMode::Volume:
|
||||
if (p.getNumResults() == 0)
|
||||
showSearchVolume();
|
||||
showSearchVolume(currentVolumeSearchQuery.volume);
|
||||
else
|
||||
showSelectVolume(string);
|
||||
break;
|
||||
@ -317,7 +323,7 @@ void ComicVineDialog::debugClientResults(const QString &string)
|
||||
|
||||
void ComicVineDialog::showSeriesQuestion()
|
||||
{
|
||||
status = AskingForInfo;
|
||||
status = ScraperStatus::AskingForInfo;
|
||||
content->setCurrentWidget(seriesQuestionWidget);
|
||||
backButton->setHidden(true);
|
||||
skipButton->setHidden(true);
|
||||
@ -330,9 +336,11 @@ void ComicVineDialog::showSeriesQuestion()
|
||||
toggleSkipButton();
|
||||
}
|
||||
|
||||
void ComicVineDialog::showSearchSingleComic()
|
||||
void ComicVineDialog::showSearchSingleComic(const QString &volume)
|
||||
{
|
||||
status = AskingForInfo;
|
||||
searchSingleComicWidget->setVolumeInfo(volume);
|
||||
|
||||
status = ScraperStatus::AskingForInfo;
|
||||
content->setCurrentWidget(searchSingleComicWidget);
|
||||
backButton->setHidden(true);
|
||||
skipButton->setHidden(true);
|
||||
@ -349,7 +357,7 @@ void ComicVineDialog::showSearchVolume(const QString &volume)
|
||||
{
|
||||
searchVolumeWidget->setVolumeInfo(volume);
|
||||
|
||||
status = AskingForInfo;
|
||||
status = ScraperStatus::AskingForInfo;
|
||||
content->setCurrentWidget(searchVolumeWidget);
|
||||
backButton->setHidden(true);
|
||||
nextButton->setHidden(true);
|
||||
@ -364,12 +372,12 @@ void ComicVineDialog::showSearchVolume(const QString &volume)
|
||||
void ComicVineDialog::showSelectVolume(const QString &json)
|
||||
{
|
||||
showSelectVolume();
|
||||
selectVolumeWidget->load(json, currentVolumeSearchString);
|
||||
selectVolumeWidget->load(json, currentVolumeSearchQuery);
|
||||
}
|
||||
|
||||
void ComicVineDialog::showSelectVolume()
|
||||
{
|
||||
status = SelectingSeries;
|
||||
status = ScraperStatus::SelectingSeries;
|
||||
|
||||
content->setCurrentWidget(selectVolumeWidget);
|
||||
|
||||
@ -385,7 +393,7 @@ void ComicVineDialog::showSelectVolume()
|
||||
|
||||
void ComicVineDialog::showSelectComic(const QString &json)
|
||||
{
|
||||
status = SelectingComic;
|
||||
status = ScraperStatus::SelectingComic;
|
||||
|
||||
content->setCurrentWidget(selectComicWidget);
|
||||
selectComicWidget->load(json, currentVolumeId);
|
||||
@ -402,7 +410,7 @@ void ComicVineDialog::showSelectComic(const QString &json)
|
||||
|
||||
void ComicVineDialog::showSortVolumeComics(const QString &json)
|
||||
{
|
||||
status = SortingComics;
|
||||
status = ScraperStatus::SortingComics;
|
||||
|
||||
content->setCurrentWidget(sortVolumeComicsWidget);
|
||||
|
||||
@ -423,30 +431,35 @@ void ComicVineDialog::queryTimeOut()
|
||||
QMessageBox::warning(this, "Comic Vine error", "Time out connecting to Comic Vine");
|
||||
|
||||
switch (status) {
|
||||
case AutoSearching:
|
||||
if (mode == Volume)
|
||||
showSearchVolume();
|
||||
case ScraperStatus::AutoSearching:
|
||||
if (mode == ScraperMode::Volume)
|
||||
showSearchVolume(currentVolumeSearchQuery.volume);
|
||||
else
|
||||
showSearchSingleComic();
|
||||
showSearchSingleComic(currentVolumeSearchQuery.volume);
|
||||
break;
|
||||
case SearchingVolume:
|
||||
if (mode == Volume)
|
||||
showSearchVolume();
|
||||
case ScraperStatus::SearchingVolume:
|
||||
case ScraperStatus::SearchingExactVolume:
|
||||
if (mode == ScraperMode::Volume)
|
||||
showSearchVolume(currentVolumeSearchQuery.volume);
|
||||
else
|
||||
showSearchSingleComic();
|
||||
showSearchSingleComic(currentVolumeSearchQuery.volume);
|
||||
break;
|
||||
case SearchingSingleComic:
|
||||
showSearchSingleComic();
|
||||
case ScraperStatus::SearchingSingleComic:
|
||||
showSearchSingleComic(currentVolumeSearchQuery.volume);
|
||||
break;
|
||||
case GettingVolumeComics:
|
||||
case ScraperStatus::GettingVolumeComics:
|
||||
showSelectVolume();
|
||||
break;
|
||||
default:
|
||||
|
||||
case ScraperStatus::AskingForInfo:
|
||||
case ScraperStatus::SelectingComic:
|
||||
case ScraperStatus::SelectingSeries:
|
||||
case ScraperStatus::SortingComics:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ComicVineDialog::getComicsInfo(QList<QPair<ComicDB, QString>> matchingInfo, int count, const QString &publisher)
|
||||
void ComicVineDialog::getComicsInfo(QList<QPair<ComicDB, QString>> matchingInfo, const SelectedVolumeInfo &volumeInfo)
|
||||
{
|
||||
QPair<ComicDB, QString> p;
|
||||
QList<ComicDB> comics;
|
||||
@ -460,7 +473,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 = YACReader::parseCVJSONComicInfo(p.first, result, count, publisher); // TODO check result error
|
||||
ComicDB comic = YACReader::parseCVJSONComicInfo(p.first, result, volumeInfo); // TODO check result error
|
||||
comic.info.comicVineID = p.second;
|
||||
comics.push_back(comic);
|
||||
|
||||
@ -472,7 +485,7 @@ void ComicVineDialog::getComicsInfo(QList<QPair<ComicDB, QString>> matchingInfo,
|
||||
emit accepted();
|
||||
}
|
||||
|
||||
void ComicVineDialog::getComicInfo(const QString &comicId, int count, const QString &publisher)
|
||||
void ComicVineDialog::getComicInfo(const QString &comicId, const SelectedVolumeInfo &volumeInfo)
|
||||
{
|
||||
|
||||
auto comicVineClient = new ComicVineClient;
|
||||
@ -481,14 +494,14 @@ void ComicVineDialog::getComicInfo(const QString &comicId, int count, const QStr
|
||||
QByteArray result = comicVineClient->getComicDetail(comicId, error, timeout); // TODO check timeOut or Connection error
|
||||
if (error || timeout) {
|
||||
// TODO
|
||||
if (mode == SingleComic || currentIndex == (comics.count() - 1)) {
|
||||
if (mode == ScraperMode::SingleComic || currentIndex == (comics.count() - 1)) {
|
||||
emit accepted();
|
||||
} else {
|
||||
goToNextComic();
|
||||
}
|
||||
}
|
||||
|
||||
ComicDB comic = YACReader::parseCVJSONComicInfo(comics[currentIndex], result, count, publisher); // TODO check result error
|
||||
ComicDB comic = YACReader::parseCVJSONComicInfo(comics[currentIndex], result, volumeInfo); // TODO check result error
|
||||
comic.info.comicVineID = comicId;
|
||||
setLoadingMessage(tr("Retrieving tags for : %1").arg(comics[currentIndex].getFileName()));
|
||||
QString connectionName = "";
|
||||
@ -504,7 +517,7 @@ void ComicVineDialog::getComicInfo(const QString &comicId, int count, const QStr
|
||||
}
|
||||
QSqlDatabase::removeDatabase(connectionName);
|
||||
|
||||
if (mode == SingleComic || currentIndex == (comics.count() - 1)) {
|
||||
if (mode == ScraperMode::SingleComic || currentIndex == (comics.count() - 1)) {
|
||||
emit accepted();
|
||||
} else {
|
||||
goToNextComic();
|
||||
@ -513,26 +526,46 @@ void ComicVineDialog::getComicInfo(const QString &comicId, int count, const QStr
|
||||
|
||||
void ComicVineDialog::toggleSkipButton()
|
||||
{
|
||||
if (mode == SingleComicInList)
|
||||
if (mode == ScraperMode::SingleComicInList)
|
||||
skipButton->setVisible(true);
|
||||
else
|
||||
skipButton->setHidden(true);
|
||||
}
|
||||
|
||||
QString ComicVineDialog::volumeSearchStringFromComic(const ComicDB &comic)
|
||||
{
|
||||
auto volume = comic.info.volume.toString().trimmed();
|
||||
if (!volume.isEmpty())
|
||||
return volume;
|
||||
|
||||
auto series = comic.info.series.toString().trimmed();
|
||||
if (!series.isEmpty())
|
||||
return series;
|
||||
|
||||
auto alternateSeries = comic.info.alternateSeries.toString().trimmed();
|
||||
if (!alternateSeries.isEmpty())
|
||||
return alternateSeries;
|
||||
|
||||
// extract information from file name
|
||||
auto parentFolderName = comic.getParentFolderName();
|
||||
return parentFolderName;
|
||||
}
|
||||
|
||||
void ComicVineDialog::goToNextComic()
|
||||
{
|
||||
if (mode == SingleComic || currentIndex == (comics.count() - 1)) {
|
||||
if (mode == ScraperMode::SingleComic || currentIndex == (comics.count() - 1)) {
|
||||
emit accepted();
|
||||
return;
|
||||
}
|
||||
|
||||
currentIndex++;
|
||||
|
||||
showSearchSingleComic();
|
||||
|
||||
ComicDB comic = comics[currentIndex];
|
||||
|
||||
QString title = comic.getTitleOrFileName();
|
||||
titleHeader->setSubTitle(tr("comic %1 of %2 - %3").arg(currentIndex + 1).arg(comics.length()).arg(title));
|
||||
|
||||
showSearchSingleComic(volumeSearchStringFromComic(comic));
|
||||
}
|
||||
|
||||
void ComicVineDialog::clearState()
|
||||
@ -559,38 +592,44 @@ void ComicVineDialog::setLoadingMessage(const QString &message)
|
||||
void ComicVineDialog::search()
|
||||
{
|
||||
switch (mode) {
|
||||
case Volume:
|
||||
case ScraperMode::Volume:
|
||||
launchSearchVolume();
|
||||
break;
|
||||
default:
|
||||
|
||||
case ScraperMode::SingleComic:
|
||||
case ScraperMode::SingleComicInList:
|
||||
launchSearchComic();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ComicVineDialog::searchVolume(const QString &v, int page)
|
||||
void ComicVineDialog::searchVolume(const VolumeSearchQuery &query)
|
||||
{
|
||||
showLoading(tr("Looking for volume..."));
|
||||
|
||||
currentVolumeSearchString = v;
|
||||
currentVolumeSearchQuery = query;
|
||||
|
||||
auto comicVineClient = new ComicVineClient;
|
||||
connect(comicVineClient, &ComicVineClient::searchResult, this, &ComicVineDialog::debugClientResults);
|
||||
connect(comicVineClient, &ComicVineClient::searchResult, this, &ComicVineDialog::processClientResults);
|
||||
connect(comicVineClient, &ComicVineClient::timeOut, this, &ComicVineDialog::queryTimeOut);
|
||||
connect(comicVineClient, &ComicVineClient::finished, comicVineClient, &QObject::deleteLater);
|
||||
comicVineClient->search(v, page);
|
||||
|
||||
status = SearchingVolume;
|
||||
if (query.exactMatch) {
|
||||
status = ScraperStatus::SearchingExactVolume;
|
||||
comicVineClient->searchExactVolume(query.volume, query.page);
|
||||
} else {
|
||||
status = ScraperStatus::SearchingVolume;
|
||||
comicVineClient->search(query.volume, query.page);
|
||||
}
|
||||
}
|
||||
|
||||
void ComicVineDialog::getVolumeComicsInfo(const QString &vID, int /* page */)
|
||||
{
|
||||
showLoading(tr("Retrieving volume info..."));
|
||||
|
||||
status = GettingVolumeComics;
|
||||
status = ScraperStatus::GettingVolumeComics;
|
||||
|
||||
auto comicVineClient = new ComicVineClient;
|
||||
if (mode == Volume)
|
||||
if (mode == ScraperMode::Volume)
|
||||
connect(comicVineClient, &ComicVineClient::volumeComicsInfo, this, &ComicVineDialog::showSortVolumeComics);
|
||||
else
|
||||
connect(comicVineClient, &ComicVineClient::volumeComicsInfo, this, &ComicVineDialog::showSelectComic);
|
||||
@ -602,21 +641,28 @@ void ComicVineDialog::getVolumeComicsInfo(const QString &vID, int /* page */)
|
||||
comicVineClient->getAllVolumeComicsInfo(vID);
|
||||
}
|
||||
|
||||
// TODO: get the search configuration for exact match or not
|
||||
void ComicVineDialog::launchSearchVolume()
|
||||
{
|
||||
showLoading(tr("Looking for volume..."));
|
||||
// TODO: check if volume info is empty.
|
||||
searchVolume(searchVolumeWidget->getVolumeInfo());
|
||||
|
||||
QString volumeInfo = searchVolumeWidget->getVolumeInfo();
|
||||
bool exactMatch = searchVolumeWidget->getExactMatch();
|
||||
|
||||
searchVolume({ volumeInfo, 1, exactMatch });
|
||||
}
|
||||
|
||||
// TODO: get the search configuration for exact match or not
|
||||
void ComicVineDialog::launchSearchComic()
|
||||
{
|
||||
showLoading(tr("Looking for comic..."));
|
||||
|
||||
QString volumeInfo = searchSingleComicWidget->getVolumeInfo();
|
||||
bool exactMatch = searchSingleComicWidget->getExactMatch();
|
||||
// QString comicInfo = searchSingleComicWidget->getComicInfo();
|
||||
// int comicNumber = searchSingleComicWidget->getComicNumber();
|
||||
|
||||
// if(comicInfo.isEmpty() && comicNumber == -1)
|
||||
searchVolume(volumeInfo);
|
||||
searchVolume({ volumeInfo, 1, exactMatch });
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <QDialog>
|
||||
|
||||
#include "comic_db.h"
|
||||
#include "volume_search_query.h"
|
||||
|
||||
class QPushButton;
|
||||
class QStackedWidget;
|
||||
@ -17,7 +18,9 @@ class SearchSingleComic;
|
||||
class SearchVolume;
|
||||
class SelectComic;
|
||||
class SelectVolume;
|
||||
struct SelectedVolumeInfo;
|
||||
class SortVolumeComics;
|
||||
struct VolumeSearchQuery;
|
||||
|
||||
// TODO this should use a QStateMachine
|
||||
//----------------------------------------
|
||||
@ -31,8 +34,8 @@ public:
|
||||
void setComics(const QList<ComicDB> &comics);
|
||||
QSize sizeHint() const override;
|
||||
QSize minimumSizeHint() const override;
|
||||
void getComicsInfo(QList<QPair<ComicDB, QString>> matchingInfo, int count, const QString &publisher);
|
||||
void getComicInfo(const QString &comicId, int count, const QString &publisher);
|
||||
void getComicsInfo(QList<QPair<ComicDB, QString>> matchingInfo, const SelectedVolumeInfo &volumeInfo);
|
||||
void getComicInfo(const QString &comicId, const SelectedVolumeInfo &volumeInfo);
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
signals:
|
||||
|
||||
@ -42,14 +45,14 @@ public slots:
|
||||
protected slots:
|
||||
void goNext();
|
||||
void goBack();
|
||||
void debugClientResults(const QString &string);
|
||||
void processClientResults(const QString &string);
|
||||
// show widget methods
|
||||
void showSeriesQuestion();
|
||||
void showSearchSingleComic();
|
||||
void showSearchSingleComic(const QString &volume = "");
|
||||
void showSearchVolume(const QString &volume = "");
|
||||
void showLoading(const QString &message = "");
|
||||
void search();
|
||||
void searchVolume(const QString &v, int page = 1);
|
||||
void searchVolume(const VolumeSearchQuery &query);
|
||||
void getVolumeComicsInfo(const QString &vID, int page = 1);
|
||||
void launchSearchVolume();
|
||||
void launchSearchComic();
|
||||
@ -63,22 +66,23 @@ protected slots:
|
||||
|
||||
private:
|
||||
void clearState();
|
||||
|
||||
void toggleSkipButton();
|
||||
QString volumeSearchStringFromComic(const ComicDB &comic);
|
||||
|
||||
enum ScraperMode {
|
||||
enum class ScraperMode {
|
||||
SingleComic, // the scraper has been opened for a single comic
|
||||
Volume, // the scraper is trying to get comics info for a whole volume
|
||||
SingleComicInList // the scraper has been opened for a list of unrelated comics
|
||||
};
|
||||
|
||||
enum ScraperStatus {
|
||||
AutoSearching,
|
||||
AskingForInfo,
|
||||
enum class ScraperStatus {
|
||||
AutoSearching, // Searching for volumes maching a single comic
|
||||
AskingForInfo, // The dialog is showing some UI to ask the user for some info
|
||||
SelectingComic,
|
||||
SelectingSeries,
|
||||
SearchingSingleComic,
|
||||
SearchingVolume,
|
||||
SearchingExactVolume,
|
||||
SortingComics,
|
||||
GettingVolumeComics
|
||||
};
|
||||
@ -118,7 +122,7 @@ private:
|
||||
SelectComic *selectComicWidget;
|
||||
SortVolumeComics *sortVolumeComicsWidget;
|
||||
|
||||
QString currentVolumeSearchString;
|
||||
VolumeSearchQuery currentVolumeSearchQuery;
|
||||
QString currentVolumeId;
|
||||
};
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include "comic_vine_json_parser.h"
|
||||
|
||||
#include "comic_vine_client.h"
|
||||
#include "selected_volume_info.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonParseError>
|
||||
@ -12,7 +13,7 @@ QPair<QString, QString> getFirstStoryArcIdAndName(const QVariant &json_story_arc
|
||||
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)
|
||||
ComicDB YACReader::parseCVJSONComicInfo(ComicDB &comic, const QString &json, const SelectedVolumeInfo &volumeInfo)
|
||||
{
|
||||
QJsonParseError Err;
|
||||
|
||||
@ -61,8 +62,12 @@ ComicDB YACReader::parseCVJSONComicInfo(ComicDB &comic, const QString &json, int
|
||||
}
|
||||
}
|
||||
|
||||
if (result.contains("description") && !result.value("description").isNull()) {
|
||||
if (result.contains("description") && !result.value("description").isNull() && !result.value("description").toString().trimmed().isEmpty()) {
|
||||
comic.info.synopsis = result.value("description");
|
||||
} else if (result.contains("deck") && !result.value("deck").isNull() && !result.value("deck").toString().trimmed().isEmpty()) {
|
||||
comic.info.synopsis = result.value("deck");
|
||||
} else if (!volumeInfo.description.trimmed().isEmpty() && volumeInfo.numIssues < 2) {
|
||||
comic.info.synopsis = volumeInfo.description.trimmed();
|
||||
}
|
||||
|
||||
if (result.contains("character_credits") && !result.value("character_credits").isNull()) {
|
||||
@ -101,9 +106,9 @@ ComicDB YACReader::parseCVJSONComicInfo(ComicDB &comic, const QString &json, int
|
||||
comic.info.characters = getNamesFromList(result.value("character_credits")).join("\n");
|
||||
}
|
||||
|
||||
comic.info.count = count;
|
||||
comic.info.count = volumeInfo.numIssues;
|
||||
|
||||
comic.info.publisher = publisher;
|
||||
comic.info.publisher = volumeInfo.publisher;
|
||||
|
||||
comic.info.edited = true;
|
||||
}
|
||||
|
@ -4,9 +4,11 @@
|
||||
|
||||
#include "comic_db.h"
|
||||
|
||||
struct SelectedVolumeInfo;
|
||||
|
||||
namespace YACReader {
|
||||
|
||||
ComicDB parseCVJSONComicInfo(ComicDB &comic, const QString &json, int count, const QString &publisher);
|
||||
ComicDB parseCVJSONComicInfo(ComicDB &comic, const QString &json, const SelectedVolumeInfo &volumeInfo);
|
||||
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include <QDebug>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonParseError>
|
||||
#include <QVariantMap>
|
||||
|
||||
ResponseParser::ResponseParser(QObject *parent)
|
||||
: QObject(parent), error(false), errorTxt("None"), numResults(-1), currentPage(-1), totalPages(-1)
|
||||
|
13
YACReaderLibrary/comic_vine/model/selected_volume_info.h
Normal file
13
YACReaderLibrary/comic_vine/model/selected_volume_info.h
Normal file
@ -0,0 +1,13 @@
|
||||
#ifndef SELECTED_VOLUME_INFO_H
|
||||
#define SELECTED_VOLUME_INFO_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
struct SelectedVolumeInfo {
|
||||
QString id;
|
||||
int numIssues;
|
||||
QString publisher;
|
||||
QString description;
|
||||
};
|
||||
|
||||
#endif // SELECTED_VOLUME_INFO_H
|
12
YACReaderLibrary/comic_vine/model/volume_search_query.h
Normal file
12
YACReaderLibrary/comic_vine/model/volume_search_query.h
Normal file
@ -0,0 +1,12 @@
|
||||
#ifndef VOLUME_SEARCH_QUERY_H
|
||||
#define VOLUME_SEARCH_QUERY_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
struct VolumeSearchQuery {
|
||||
QString volume;
|
||||
int page;
|
||||
bool exactMatch;
|
||||
};
|
||||
|
||||
#endif // VOLUME_SEARCH_QUERY_H
|
27
YACReaderLibrary/comic_vine/scraper_checkbox.cpp
Normal file
27
YACReaderLibrary/comic_vine/scraper_checkbox.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
#include "scraper_checkbox.h"
|
||||
#include "qwidget.h"
|
||||
|
||||
ScraperCheckBox::ScraperCheckBox(const QString &text, QWidget *parent)
|
||||
: QCheckBox(text, parent)
|
||||
{
|
||||
setStyleSheet(
|
||||
"QCheckBox {"
|
||||
" color: white;"
|
||||
" font-size: 12px;"
|
||||
" font-family: Arial;"
|
||||
" spacing: 10px;"
|
||||
"}"
|
||||
"QCheckBox::indicator {"
|
||||
" width: 13px;"
|
||||
" height: 13px;"
|
||||
" border: 1px solid #242424;"
|
||||
" background: #2e2e2e;"
|
||||
"}"
|
||||
"QCheckBox::indicator:checked {"
|
||||
" image: url(:/images/comic_vine/checkBoxTick.svg);"
|
||||
" background: #2e2e2e;"
|
||||
"}"
|
||||
"QCheckBox::indicator:unchecked {"
|
||||
" background: #2e2e2e;"
|
||||
"}");
|
||||
}
|
12
YACReaderLibrary/comic_vine/scraper_checkbox.h
Normal file
12
YACReaderLibrary/comic_vine/scraper_checkbox.h
Normal file
@ -0,0 +1,12 @@
|
||||
#ifndef SCRAPER_CHECKBOX_H
|
||||
#define SCRAPER_CHECKBOX_H
|
||||
|
||||
#include <QCheckBox>
|
||||
|
||||
class ScraperCheckBox : public QCheckBox
|
||||
{
|
||||
public:
|
||||
ScraperCheckBox(const QString &text, QWidget *parent = nullptr);
|
||||
};
|
||||
|
||||
#endif // SCRAPER_CHECKBOX_H
|
@ -1,25 +0,0 @@
|
||||
#include "scraper_selector.h"
|
||||
|
||||
ScraperSelector::ScraperSelector(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
paginator = new ScraperResultsPaginator;
|
||||
connect(paginator, &ScraperResultsPaginator::loadNextPage, this, &ScraperSelector::loadNextPage);
|
||||
connect(paginator, &ScraperResultsPaginator::loadPreviousPage, this, &ScraperSelector::loadPreviousPage);
|
||||
}
|
||||
|
||||
void ScraperSelector::load(const QString &json, const QString &searchString)
|
||||
{
|
||||
currentSearchString = searchString;
|
||||
paginator->update(json);
|
||||
}
|
||||
|
||||
void ScraperSelector::loadNextPage()
|
||||
{
|
||||
emit loadPage(currentSearchString, paginator->getCurrentPage() + 1);
|
||||
}
|
||||
|
||||
void ScraperSelector::loadPreviousPage()
|
||||
{
|
||||
emit loadPage(currentSearchString, paginator->getCurrentPage() - 1);
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
#ifndef SCRAPER_SELECTOR_H
|
||||
#define SCRAPER_SELECTOR_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include "scraper_results_paginator.h"
|
||||
|
||||
class ScraperSelector : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ScraperSelector(QWidget *parent = nullptr);
|
||||
virtual void load(const QString &json, const QString &searchString);
|
||||
public slots:
|
||||
|
||||
signals:
|
||||
void loadPage(QString, int);
|
||||
|
||||
private slots:
|
||||
void loadNextPage();
|
||||
void loadPreviousPage();
|
||||
|
||||
protected:
|
||||
QString currentSearchString;
|
||||
ScraperResultsPaginator *paginator;
|
||||
};
|
||||
|
||||
#endif // SCRAPER_SELECTOR_H
|
@ -54,6 +54,8 @@ ScraperTableView::ScraperTableView(QWidget *parent)
|
||||
|
||||
setAlternatingRowColors(true);
|
||||
|
||||
horizontalHeader()->setMinimumSectionSize(85);
|
||||
|
||||
verticalHeader()->hide();
|
||||
|
||||
setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
|
@ -11,12 +11,16 @@ SearchSingleComic::SearchSingleComic(QWidget *parent)
|
||||
{
|
||||
|
||||
// QLabel * label = new QLabel(tr("Please provide some additional information. At least one field is needed."));
|
||||
QLabel *label = new QLabel(tr("Please provide some additional information."));
|
||||
QLabel *label = new QLabel(tr("Please provide some additional information for this comic."));
|
||||
label->setStyleSheet("QLabel {color:white; font-size:12px;font-family:Arial;}");
|
||||
|
||||
// titleEdit = new ScraperLineEdit(tr("Title:"));
|
||||
// numberEdit = new ScraperLineEdit(tr("Number:"));
|
||||
volumeEdit = new ScraperLineEdit(tr("Series:"));
|
||||
volumeEdit->setClearButtonEnabled(true);
|
||||
|
||||
exactMatchCheckBox = new ScraperCheckBox(tr("Use exact match search. Disable if you want to find volumes that match some of the words in the name."));
|
||||
exactMatchCheckBox->setChecked(true);
|
||||
|
||||
// numberEdit->setMaximumWidth(126);
|
||||
|
||||
@ -29,6 +33,7 @@ SearchSingleComic::SearchSingleComic(QWidget *parent)
|
||||
l->addWidget(label);
|
||||
// l->addLayout(hl);
|
||||
l->addWidget(volumeEdit);
|
||||
l->addWidget(exactMatchCheckBox);
|
||||
l->addStretch();
|
||||
|
||||
l->setContentsMargins(0, 0, 0, 0);
|
||||
@ -36,11 +41,16 @@ SearchSingleComic::SearchSingleComic(QWidget *parent)
|
||||
setContentsMargins(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
QString SearchSingleComic::getVolumeInfo()
|
||||
QString SearchSingleComic::getVolumeInfo() const
|
||||
{
|
||||
return volumeEdit->text();
|
||||
}
|
||||
|
||||
void SearchSingleComic::setVolumeInfo(const QString &volume)
|
||||
{
|
||||
volumeEdit->setText(volume);
|
||||
}
|
||||
|
||||
QString SearchSingleComic::getComicInfo()
|
||||
{
|
||||
// return titleEdit->text();
|
||||
|
@ -1,7 +1,9 @@
|
||||
#ifndef SEARCH_SINGLE_COMIC_H
|
||||
#define SEARCH_SINGLE_COMIC_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QtWidgets>
|
||||
|
||||
#include "scraper_checkbox.h"
|
||||
|
||||
class ScraperLineEdit;
|
||||
|
||||
@ -10,14 +12,17 @@ class SearchSingleComic : public QWidget
|
||||
Q_OBJECT
|
||||
public:
|
||||
SearchSingleComic(QWidget *parent = nullptr);
|
||||
QString getVolumeInfo();
|
||||
QString getVolumeInfo() const;
|
||||
bool getExactMatch() const { return exactMatchCheckBox->isChecked(); }
|
||||
void setVolumeInfo(const QString &volume);
|
||||
QString getComicInfo();
|
||||
int getComicNumber();
|
||||
void clean();
|
||||
|
||||
private:
|
||||
ScraperLineEdit *titleEdit;
|
||||
ScraperLineEdit *numberEdit;
|
||||
// ScraperLineEdit *titleEdit;
|
||||
// ScraperLineEdit *numberEdit;
|
||||
ScraperLineEdit *volumeEdit;
|
||||
ScraperCheckBox *exactMatchCheckBox;
|
||||
};
|
||||
#endif // SEARCH_SINGLE_COMIC_H
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "search_volume.h"
|
||||
|
||||
#include "scraper_lineedit.h"
|
||||
#include "scraper_checkbox.h"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QVBoxLayout>
|
||||
@ -12,12 +13,17 @@ SearchVolume::SearchVolume(QWidget *parent)
|
||||
label->setStyleSheet("QLabel {color:white; font-size:12px;font-family:Arial;}");
|
||||
|
||||
volumeEdit = new ScraperLineEdit(tr("Series:"));
|
||||
volumeEdit->setClearButtonEnabled(true);
|
||||
|
||||
exactMatchCheckBox = new ScraperCheckBox(tr("Use exact match search. Disable if you want to find volumes that match some of the words in the name."), this);
|
||||
exactMatchCheckBox->setChecked(true);
|
||||
|
||||
QVBoxLayout *l = new QVBoxLayout;
|
||||
|
||||
l->addSpacing(35);
|
||||
l->addWidget(label);
|
||||
l->addWidget(volumeEdit);
|
||||
l->addWidget(exactMatchCheckBox);
|
||||
l->addStretch();
|
||||
|
||||
l->setContentsMargins(0, 0, 0, 0);
|
||||
|
@ -1,9 +1,11 @@
|
||||
#ifndef SEARCH_VOLUME_H
|
||||
#define SEARCH_VOLUME_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QtWidgets>
|
||||
#include "scraper_checkbox.h"
|
||||
|
||||
class ScraperLineEdit;
|
||||
class ScraperCheckBox;
|
||||
|
||||
class SearchVolume : public QWidget
|
||||
{
|
||||
@ -13,9 +15,11 @@ public:
|
||||
void clean();
|
||||
void setVolumeInfo(const QString &volume);
|
||||
QString getVolumeInfo() const;
|
||||
bool getExactMatch() const { return exactMatchCheckBox->isChecked(); }
|
||||
|
||||
private:
|
||||
ScraperLineEdit *volumeEdit;
|
||||
ScraperCheckBox *exactMatchCheckBox;
|
||||
};
|
||||
|
||||
#endif // SEARCH_VOLUME_H
|
||||
|
@ -11,7 +11,7 @@
|
||||
#include <QLayout>
|
||||
|
||||
SelectComic::SelectComic(QWidget *parent)
|
||||
: ScraperSelector(parent), model(0)
|
||||
: QWidget(parent), model(0)
|
||||
{
|
||||
QString labelStylesheet = "QLabel {color:white; font-size:12px;font-family:Arial;}";
|
||||
|
||||
@ -35,6 +35,9 @@ SelectComic::SelectComic(QWidget *parent)
|
||||
// connections
|
||||
connect(tableComics, &QAbstractItemView::clicked, this, &SelectComic::loadComicInfo);
|
||||
|
||||
paginator = new ScraperResultsPaginator;
|
||||
connect(paginator, &ScraperResultsPaginator::loadNextPage, this, &SelectComic::loadNextPage);
|
||||
connect(paginator, &ScraperResultsPaginator::loadPreviousPage, this, &SelectComic::loadPreviousPage);
|
||||
paginator->setCustomLabel(tr("comics"));
|
||||
|
||||
left->addWidget(cover);
|
||||
@ -62,7 +65,7 @@ SelectComic::SelectComic(QWidget *parent)
|
||||
setContentsMargins(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
void SelectComic::load(const QString &json, const QString &searchString)
|
||||
void SelectComic::load(const QString &json, const QString &volumeId)
|
||||
{
|
||||
auto tempM = new VolumeComicsModel();
|
||||
tempM->load(json);
|
||||
@ -80,7 +83,18 @@ void SelectComic::load(const QString &json, const QString &searchString)
|
||||
|
||||
tableComics->resizeColumnToContents(0);
|
||||
|
||||
ScraperSelector::load(json, searchString);
|
||||
currentVolumeId = volumeId;
|
||||
paginator->update(json);
|
||||
}
|
||||
|
||||
void SelectComic::loadNextPage()
|
||||
{
|
||||
emit loadPage(currentVolumeId, paginator->getCurrentPage() + 1);
|
||||
}
|
||||
|
||||
void SelectComic::loadPreviousPage()
|
||||
{
|
||||
emit loadPage(currentVolumeId, paginator->getCurrentPage() - 1);
|
||||
}
|
||||
|
||||
SelectComic::~SelectComic() { }
|
||||
@ -130,9 +144,20 @@ void SelectComic::setDescription(const QString &jsonDetail)
|
||||
return;
|
||||
}
|
||||
|
||||
QVariant descriptionValues = sc.value("results").toMap().value("description");
|
||||
bool valid = !descriptionValues.isNull() && descriptionValues.isValid();
|
||||
detailLabel->setText(valid ? descriptionValues.toString().replace("<a", "<a style = 'color:#827A68; text-decoration:none;'") : tr("description unavailable"));
|
||||
auto resultMap = sc.value("results").toMap();
|
||||
QVariant descriptionValues = resultMap.value("description");
|
||||
auto description = descriptionValues.toString().trimmed();
|
||||
QVariant deckValues = resultMap.value("deck");
|
||||
auto deck = deckValues.toString().trimmed();
|
||||
bool valid = !descriptionValues.isNull() && descriptionValues.isValid() && !description.isEmpty();
|
||||
bool validDeck = !deckValues.isNull() && deckValues.isValid() && !deck.isEmpty();
|
||||
if (valid) {
|
||||
detailLabel->setText(description.replace("<a", "<a style = 'color:#827A68; text-decoration:none;'"));
|
||||
} else if (validDeck) {
|
||||
detailLabel->setText(deck.replace("<a", "<a style = 'color:#827A68; text-decoration:none;'"));
|
||||
} else {
|
||||
detailLabel->setText(tr("comic description unavailable"));
|
||||
}
|
||||
}
|
||||
|
||||
QString SelectComic::getSelectedComicId()
|
||||
|
@ -1,7 +1,9 @@
|
||||
#ifndef SELECT_COMIC_H
|
||||
#define SELECT_COMIC_H
|
||||
|
||||
#include "scraper_selector.h"
|
||||
#include <QtWidgets>
|
||||
|
||||
#include "scraper_results_paginator.h"
|
||||
|
||||
class QLabel;
|
||||
class VolumeComicsModel;
|
||||
@ -10,12 +12,12 @@ class QModelIndex;
|
||||
class ScraperScrollLabel;
|
||||
class ScraperTableView;
|
||||
|
||||
class SelectComic : public ScraperSelector
|
||||
class SelectComic : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SelectComic(QWidget *parent = nullptr);
|
||||
void load(const QString &json, const QString &searchString) override;
|
||||
void load(const QString &json, const QString &volumeId);
|
||||
virtual ~SelectComic();
|
||||
|
||||
public slots:
|
||||
@ -24,11 +26,20 @@ public slots:
|
||||
void setDescription(const QString &jsonDetail);
|
||||
QString getSelectedComicId();
|
||||
|
||||
signals:
|
||||
void loadPage(QString, int);
|
||||
|
||||
private slots:
|
||||
void loadNextPage();
|
||||
void loadPreviousPage();
|
||||
|
||||
private:
|
||||
QLabel *cover;
|
||||
ScraperScrollLabel *detailLabel;
|
||||
ScraperTableView *tableComics;
|
||||
VolumeComicsModel *model;
|
||||
QString currentVolumeId;
|
||||
ScraperResultsPaginator *paginator;
|
||||
};
|
||||
|
||||
#endif // SELECT_COMIC_H
|
||||
|
@ -24,8 +24,10 @@
|
||||
#include "response_parser.h"
|
||||
#include "scraper_results_paginator.h"
|
||||
|
||||
#include "selected_volume_info.h"
|
||||
|
||||
SelectVolume::SelectVolume(QWidget *parent)
|
||||
: ScraperSelector(parent), model(0)
|
||||
: QWidget(parent), model(0)
|
||||
{
|
||||
proxyModel = new QSortFilterProxyModel;
|
||||
proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||
@ -61,6 +63,9 @@ SelectVolume::SelectVolume(QWidget *parent)
|
||||
connect(tableVolumes->horizontalHeader(), qOverload<int, Qt::SortOrder>(&QHeaderView::sortIndicatorChanged), tableVolumes, qOverload<int, Qt::SortOrder>(&QTableView::sortByColumn));
|
||||
connect(tableVolumes, &QAbstractItemView::clicked, this, &SelectVolume::loadVolumeInfo);
|
||||
|
||||
paginator = new ScraperResultsPaginator;
|
||||
connect(paginator, &ScraperResultsPaginator::loadNextPage, this, &SelectVolume::loadNextPage);
|
||||
connect(paginator, &ScraperResultsPaginator::loadPreviousPage, this, &SelectVolume::loadPreviousPage);
|
||||
paginator->setCustomLabel(tr("volumes"));
|
||||
|
||||
top->addWidget(label);
|
||||
@ -91,7 +96,7 @@ SelectVolume::SelectVolume(QWidget *parent)
|
||||
setContentsMargins(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
void SelectVolume::load(const QString &json, const QString &searchString)
|
||||
void SelectVolume::load(const QString &json, const VolumeSearchQuery &searchQuery)
|
||||
{
|
||||
auto tempM = new VolumesModel();
|
||||
tempM->load(json);
|
||||
@ -114,7 +119,18 @@ void SelectVolume::load(const QString &json, const QString &searchString)
|
||||
|
||||
tableVolumes->setColumnWidth(0, 350);
|
||||
|
||||
ScraperSelector::load(json, searchString);
|
||||
currentSearchQuery = searchQuery;
|
||||
paginator->update(json);
|
||||
}
|
||||
|
||||
void SelectVolume::loadNextPage()
|
||||
{
|
||||
emit loadPage({ currentSearchQuery.volume, paginator->getCurrentPage() + 1, currentSearchQuery.exactMatch });
|
||||
}
|
||||
|
||||
void SelectVolume::loadPreviousPage()
|
||||
{
|
||||
emit loadPage({ currentSearchQuery.volume, paginator->getCurrentPage() - 1, currentSearchQuery.exactMatch });
|
||||
}
|
||||
|
||||
void SelectVolume::clearFilter()
|
||||
@ -176,22 +192,29 @@ void SelectVolume::setDescription(const QString &jsonDetail)
|
||||
return;
|
||||
}
|
||||
|
||||
QVariant descriptionValues = sc.value("results").toMap().value("description");
|
||||
bool valid = !descriptionValues.isNull() && descriptionValues.isValid();
|
||||
detailLabel->setText(valid ? descriptionValues.toString().replace("<a", "<a style = 'color:#827A68; text-decoration:none;'") : tr("description unavailable"));
|
||||
auto resultMap = sc.value("results").toMap();
|
||||
QVariant descriptionValues = resultMap.value("description");
|
||||
auto description = descriptionValues.toString().trimmed();
|
||||
QVariant deckValues = resultMap.value("deck");
|
||||
auto deck = deckValues.toString().trimmed();
|
||||
bool valid = !descriptionValues.isNull() && descriptionValues.isValid() && !description.isEmpty();
|
||||
bool validDeck = !deckValues.isNull() && deckValues.isValid() && !deck.isEmpty();
|
||||
if (valid) {
|
||||
selectedVolumeDescription = description;
|
||||
detailLabel->setText(description.replace("<a", "<a style = 'color:#827A68; text-decoration:none;'"));
|
||||
} else if (validDeck) {
|
||||
selectedVolumeDescription = deck;
|
||||
detailLabel->setText(deck.replace("<a", "<a style = 'color:#827A68; text-decoration:none;'"));
|
||||
} else {
|
||||
detailLabel->setText(tr("volume description unavailable"));
|
||||
}
|
||||
}
|
||||
|
||||
QString SelectVolume::getSelectedVolumeId()
|
||||
SelectedVolumeInfo SelectVolume::getSelectedVolumeInfo()
|
||||
{
|
||||
return model->getVolumeId(proxyModel->mapToSource(tableVolumes->currentIndex()));
|
||||
}
|
||||
auto volumeId = model->getVolumeId(proxyModel->mapToSource(tableVolumes->currentIndex()));
|
||||
auto numIssues = model->getNumIssues(proxyModel->mapToSource(tableVolumes->currentIndex()));
|
||||
auto publisher = model->getPublisher(proxyModel->mapToSource(tableVolumes->currentIndex()));
|
||||
|
||||
int SelectVolume::getSelectedVolumeNumIssues()
|
||||
{
|
||||
return model->getNumIssues(proxyModel->mapToSource(tableVolumes->currentIndex()));
|
||||
}
|
||||
|
||||
QString SelectVolume::getSelectedVolumePublisher()
|
||||
{
|
||||
return model->getPublisher(proxyModel->mapToSource(tableVolumes->currentIndex()));
|
||||
return { volumeId, numIssues, publisher, selectedVolumeDescription };
|
||||
}
|
||||
|
@ -1,7 +1,11 @@
|
||||
#ifndef SELECT_VOLUME_H
|
||||
#define SELECT_VOLUME_H
|
||||
|
||||
#include "scraper_selector.h"
|
||||
#include <QtWidgets>
|
||||
|
||||
#include "scraper_results_paginator.h"
|
||||
#include "selected_volume_info.h"
|
||||
#include "volume_search_query.h"
|
||||
|
||||
class QLabel;
|
||||
class VolumesModel;
|
||||
@ -13,12 +17,12 @@ class ScraperScrollLabel;
|
||||
class ScraperTableView;
|
||||
class ScraperLineEdit;
|
||||
|
||||
class SelectVolume : public ScraperSelector
|
||||
class SelectVolume : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SelectVolume(QWidget *parent = nullptr);
|
||||
void load(const QString &json, const QString &searchString) override;
|
||||
void load(const QString &json, const VolumeSearchQuery &searchQuery);
|
||||
void clearFilter();
|
||||
virtual ~SelectVolume();
|
||||
|
||||
@ -26,9 +30,14 @@ public slots:
|
||||
void loadVolumeInfo(const QModelIndex &mi);
|
||||
void setCover(const QByteArray &);
|
||||
void setDescription(const QString &jsonDetail);
|
||||
QString getSelectedVolumeId();
|
||||
int getSelectedVolumeNumIssues();
|
||||
QString getSelectedVolumePublisher();
|
||||
SelectedVolumeInfo getSelectedVolumeInfo();
|
||||
|
||||
signals:
|
||||
void loadPage(VolumeSearchQuery);
|
||||
|
||||
private slots:
|
||||
void loadNextPage();
|
||||
void loadPreviousPage();
|
||||
|
||||
private:
|
||||
QLabel *cover;
|
||||
@ -37,6 +46,9 @@ private:
|
||||
VolumesModel *model;
|
||||
QSortFilterProxyModel *proxyModel;
|
||||
ScraperLineEdit *filterEdit;
|
||||
QString selectedVolumeDescription;
|
||||
VolumeSearchQuery currentSearchQuery;
|
||||
ScraperResultsPaginator *paginator;
|
||||
};
|
||||
|
||||
#endif // SELECT_VOLUME_H
|
||||
|
@ -11,7 +11,7 @@
|
||||
#include "volume_comics_model.h"
|
||||
|
||||
SortVolumeComics::SortVolumeComics(QWidget *parent)
|
||||
: ScraperSelector(parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
QString labelStylesheet = "QLabel {color:white; font-size:12px;font-family:Arial;}";
|
||||
|
||||
@ -55,6 +55,9 @@ SortVolumeComics::SortVolumeComics(QWidget *parent)
|
||||
// connect(tableVolumeComics, SIGNAL(pressed(QModelIndex)), tableFiles, SLOT(setCurrentIndex(QModelIndex)));
|
||||
// connect(tableFiles, SIGNAL(pressed(QModelIndex)), tableVolumeComics, SLOT(setCurrentIndex(QModelIndex)));
|
||||
|
||||
paginator = new ScraperResultsPaginator;
|
||||
connect(paginator, &ScraperResultsPaginator::loadNextPage, this, &SortVolumeComics::loadNextPage);
|
||||
connect(paginator, &ScraperResultsPaginator::loadPreviousPage, this, &SortVolumeComics::loadPreviousPage);
|
||||
paginator->setCustomLabel(tr("issues"));
|
||||
paginator->setMinimumWidth(422);
|
||||
|
||||
@ -77,7 +80,6 @@ SortVolumeComics::SortVolumeComics(QWidget *parent)
|
||||
l->addWidget(label, 0);
|
||||
l->addSpacing(5);
|
||||
l->addLayout(content, 1);
|
||||
l->addLayout(sortButtonsLayout, 0);
|
||||
|
||||
l->setContentsMargins(0, 0, 0, 0);
|
||||
setLayout(l);
|
||||
@ -120,7 +122,18 @@ void SortVolumeComics::setData(QList<ComicDB> &comics, const QString &json, cons
|
||||
|
||||
tableVolumeComics->resizeColumnToContents(0);
|
||||
|
||||
ScraperSelector::load(json, vID);
|
||||
currentVolumeId = vID;
|
||||
paginator->update(json);
|
||||
}
|
||||
|
||||
void SortVolumeComics::loadNextPage()
|
||||
{
|
||||
emit loadPage(currentVolumeId, paginator->getCurrentPage() + 1);
|
||||
}
|
||||
|
||||
void SortVolumeComics::loadPreviousPage()
|
||||
{
|
||||
emit loadPage(currentVolumeId, paginator->getCurrentPage() - 1);
|
||||
}
|
||||
|
||||
void SortVolumeComics::synchronizeScroll(int pos)
|
||||
|
@ -1,13 +1,13 @@
|
||||
#ifndef SORT_VOLUME_COMICS_H
|
||||
#define SORT_VOLUME_COMICS_H
|
||||
|
||||
#include "scraper_selector.h"
|
||||
|
||||
#include <QtWidgets>
|
||||
#include <QModelIndex>
|
||||
#include <QPushButton>
|
||||
#include <QPainter>
|
||||
|
||||
#include "comic_db.h"
|
||||
#include "scraper_results_paginator.h"
|
||||
|
||||
class ScraperTableView;
|
||||
class LocalComicListModel;
|
||||
@ -63,14 +63,12 @@ private:
|
||||
Appearance appearance;
|
||||
};
|
||||
|
||||
class SortVolumeComics : public ScraperSelector
|
||||
class SortVolumeComics : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SortVolumeComics(QWidget *parent = nullptr);
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
void setData(QList<ComicDB> &comics, const QString &json, const QString &vID);
|
||||
QList<QPair<ComicDB, QString>> getMatchingInfo();
|
||||
@ -86,6 +84,13 @@ protected slots:
|
||||
void restoreAllComics();
|
||||
void showRemovedComicsSelector();
|
||||
|
||||
signals:
|
||||
void loadPage(QString, int);
|
||||
|
||||
private slots:
|
||||
void loadNextPage();
|
||||
void loadPreviousPage();
|
||||
|
||||
private:
|
||||
ScraperTableView *tableFiles;
|
||||
ScraperTableView *tableVolumeComics;
|
||||
@ -97,6 +102,9 @@ private:
|
||||
ScrapperToolButton *moveDownButtonCL;
|
||||
ScrapperToolButton *moveUpButtonIL;
|
||||
ScrapperToolButton *moveDownButtonIL;
|
||||
|
||||
QString currentVolumeId;
|
||||
ScraperResultsPaginator *paginator;
|
||||
};
|
||||
|
||||
#endif // SORT_VOLUME_COMICS_H
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "create_library_dialog.h"
|
||||
#include "yacreader_global.h"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QVBoxLayout>
|
||||
@ -6,6 +7,8 @@
|
||||
#include <QSizePolicy>
|
||||
#include <QMessageBox>
|
||||
|
||||
using namespace YACReader;
|
||||
|
||||
CreateLibraryDialog::CreateLibraryDialog(QWidget *parent)
|
||||
: QDialog(parent)
|
||||
{
|
||||
@ -87,7 +90,7 @@ void CreateLibraryDialog::create()
|
||||
QFileInfo f(path->text());
|
||||
if (f.exists() && f.isDir() && f.isWritable()) {
|
||||
if (!libraries.contains(nameEdit->text())) {
|
||||
emit createLibrary(QDir::cleanPath(path->text()), QDir::cleanPath(path->text()) + "/.yacreaderlibrary", nameEdit->text());
|
||||
emit createLibrary(QDir::cleanPath(path->text()), LibraryPaths::libraryDataPath(QDir::cleanPath(path->text())), nameEdit->text());
|
||||
close();
|
||||
} else
|
||||
emit libraryExists(nameEdit->text());
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "comic_db.h"
|
||||
#include "db_helper.h"
|
||||
#include "reading_list_model.h"
|
||||
|
||||
#ifdef use_unarr
|
||||
#include <unarr.h>
|
||||
#endif
|
||||
@ -489,11 +490,10 @@ int ComicModel::rowCount(const QModelIndex &parent) const
|
||||
QStringList ComicModel::getPaths(const QString &_source)
|
||||
{
|
||||
QStringList paths;
|
||||
QString source = _source + "/.yacreaderlibrary/covers/";
|
||||
QList<ComicItem *>::ConstIterator itr;
|
||||
for (itr = _data.constBegin(); itr != _data.constEnd(); itr++) {
|
||||
QString hash = (*itr)->data(ComicModel::Hash).toString();
|
||||
paths << source + hash + ".jpg";
|
||||
paths << LibraryPaths::coverPath(_source, hash);
|
||||
}
|
||||
|
||||
return paths;
|
||||
@ -1228,10 +1228,10 @@ void ComicModel::notifyCoverChange(const ComicDB &comic)
|
||||
// this doesn't work in QML -> emit dataChanged(index(itemIndex, 0), index(itemIndex, 0), QVector<int>() << CoverPathRole);
|
||||
}
|
||||
|
||||
// ????
|
||||
QUrl ComicModel::getCoverUrlPathForComicHash(const QString &hash) const
|
||||
{
|
||||
return QUrl::fromLocalFile(_databasePath + "/covers/" + hash + ".jpg");
|
||||
auto coverPath = LibraryPaths::coverPathFromLibraryDataPath(_databasePath, hash);
|
||||
return QUrl::fromLocalFile(coverPath);
|
||||
}
|
||||
|
||||
void ComicModel::addComicsToFavorites(const QList<qulonglong> &comicIds)
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "initial_comic_info_extractor.h"
|
||||
#include "check_new_version.h"
|
||||
#include "db_helper.h"
|
||||
#include "yacreader_libraries.h"
|
||||
|
||||
#include "QsLog.h"
|
||||
|
||||
@ -118,15 +119,15 @@ QSqlDatabase DataBaseManagement::createDatabase(QString dest)
|
||||
return db;
|
||||
}
|
||||
|
||||
QSqlDatabase DataBaseManagement::loadDatabase(QString path)
|
||||
QSqlDatabase DataBaseManagement::loadDatabase(QString libraryDataPath)
|
||||
{
|
||||
if (!QFile::exists(path + "/library.ydb")) {
|
||||
if (!QFile::exists(libraryDataPath + "/library.ydb")) {
|
||||
return QSqlDatabase();
|
||||
}
|
||||
|
||||
QString threadId = QString::number((long long)QThread::currentThreadId(), 16);
|
||||
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", path + threadId);
|
||||
db.setDatabaseName(path + "/library.ydb");
|
||||
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", libraryDataPath + threadId);
|
||||
db.setDatabaseName(libraryDataPath + "/library.ydb");
|
||||
if (!db.open()) {
|
||||
return QSqlDatabase();
|
||||
}
|
||||
@ -667,7 +668,8 @@ bool DataBaseManagement::importComicsInfo(QString source, QString dest)
|
||||
QString basePath = QString(dest).remove("/.yacreaderlibrary/library.ydb");
|
||||
QString path = basePath + getComic.record().value("path").toString();
|
||||
int coverPage = getComic.record().value("coverPage").toInt();
|
||||
InitialComicInfoExtractor ie(path, basePath + "/.yacreaderlibrary/covers/" + hash + ".jpg", coverPage);
|
||||
auto coverPath = LibraryPaths::coverPath(basePath, hash);
|
||||
InitialComicInfoExtractor ie(path, coverPath, coverPage);
|
||||
ie.extract();
|
||||
}
|
||||
}
|
||||
@ -851,7 +853,7 @@ int DataBaseManagement::compareVersions(const QString &v1, const QString v2)
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool DataBaseManagement::updateToCurrentVersion(const QString &path)
|
||||
bool DataBaseManagement::updateToCurrentVersion(const QString &libraryPath)
|
||||
{
|
||||
bool pre7 = false;
|
||||
bool pre7_1 = false;
|
||||
@ -861,28 +863,28 @@ bool DataBaseManagement::updateToCurrentVersion(const QString &path)
|
||||
bool pre9_13 = false;
|
||||
bool pre9_14 = false;
|
||||
|
||||
QString fullPath = path + "/library.ydb";
|
||||
QString libraryDatabasePath = LibraryPaths::libraryDatabasePath(libraryPath);
|
||||
|
||||
if (compareVersions(DataBaseManagement::checkValidDB(fullPath), "7.0.0") < 0)
|
||||
if (compareVersions(DataBaseManagement::checkValidDB(libraryDatabasePath), "7.0.0") < 0)
|
||||
pre7 = true;
|
||||
if (compareVersions(DataBaseManagement::checkValidDB(fullPath), "7.0.3") < 0)
|
||||
if (compareVersions(DataBaseManagement::checkValidDB(libraryDatabasePath), "7.0.3") < 0)
|
||||
pre7_1 = true;
|
||||
if (compareVersions(DataBaseManagement::checkValidDB(fullPath), "8.0.0") < 0)
|
||||
if (compareVersions(DataBaseManagement::checkValidDB(libraryDatabasePath), "8.0.0") < 0)
|
||||
pre8 = true;
|
||||
if (compareVersions(DataBaseManagement::checkValidDB(fullPath), "9.5.0") < 0)
|
||||
if (compareVersions(DataBaseManagement::checkValidDB(libraryDatabasePath), "9.5.0") < 0)
|
||||
pre9_5 = true;
|
||||
if (compareVersions(DataBaseManagement::checkValidDB(fullPath), "9.8.0") < 0)
|
||||
if (compareVersions(DataBaseManagement::checkValidDB(libraryDatabasePath), "9.8.0") < 0)
|
||||
pre9_8 = true;
|
||||
if (compareVersions(DataBaseManagement::checkValidDB(fullPath), "9.13.0") < 0)
|
||||
if (compareVersions(DataBaseManagement::checkValidDB(libraryDatabasePath), "9.13.0") < 0)
|
||||
pre9_13 = true;
|
||||
if (compareVersions(DataBaseManagement::checkValidDB(fullPath), "9.14.0") < 0)
|
||||
if (compareVersions(DataBaseManagement::checkValidDB(libraryDatabasePath), "9.14.0") < 0)
|
||||
pre9_14 = true;
|
||||
|
||||
QString connectionName = "";
|
||||
bool returnValue = true;
|
||||
|
||||
{
|
||||
QSqlDatabase db = loadDatabaseFromFile(fullPath);
|
||||
QSqlDatabase db = loadDatabaseFromFile(libraryDatabasePath);
|
||||
if (db.isValid() && db.isOpen()) {
|
||||
if (pre7) // TODO: execute only if previous version was < 7.0
|
||||
{
|
||||
@ -966,7 +968,8 @@ bool DataBaseManagement::updateToCurrentVersion(const QString &path)
|
||||
|
||||
QImageReader thumbnail;
|
||||
while (selectQuery.next()) {
|
||||
thumbnail.setFileName(path % "/covers/" % selectQuery.value(1).toString() % ".jpg");
|
||||
auto coverPath = LibraryPaths::coverPath(libraryPath, selectQuery.value(1).toString());
|
||||
thumbnail.setFileName(coverPath);
|
||||
|
||||
float coverSizeRatio = static_cast<float>(thumbnail.size().width()) / thumbnail.size().height();
|
||||
updateCoverInfo.bindValue(":coverSizeRatio", coverSizeRatio);
|
||||
@ -1094,6 +1097,52 @@ bool DataBaseManagement::updateToCurrentVersion(const QString &path)
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
DatabaseAccess DataBaseManagement::getDatabaseAccess(const QString &libraryPath)
|
||||
{
|
||||
DatabaseAccess access = { false, false, false, false };
|
||||
|
||||
auto libraryDataPath = LibraryPaths::libraryDataPath(libraryPath);
|
||||
auto libraryDatabasePath = LibraryPaths::libraryDatabasePath(libraryPath);
|
||||
|
||||
QFile libraryDatabase(libraryDatabasePath);
|
||||
if (!libraryDatabase.exists()) {
|
||||
return access;
|
||||
}
|
||||
|
||||
access.libraryExists = true;
|
||||
|
||||
QDir libraryData(libraryDataPath);
|
||||
QFile testFile(libraryData.filePath("test"));
|
||||
if (testFile.open(QIODevice::WriteOnly)) {
|
||||
access.canWriteToFolder = true;
|
||||
testFile.close();
|
||||
testFile.remove();
|
||||
}
|
||||
|
||||
QString connectionName = "test";
|
||||
{
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabaseFromFile(libraryDatabasePath);
|
||||
|
||||
QSqlQuery versionQuery(db);
|
||||
bool read = versionQuery.exec("SELECT version FROM db_info");
|
||||
|
||||
read = read && versionQuery.next();
|
||||
read = read && !versionQuery.record().value(0).toString().isEmpty();
|
||||
|
||||
access.canRead = read;
|
||||
|
||||
QSqlQuery writeQuery(db);
|
||||
bool write = db.transaction();
|
||||
write = write && writeQuery.exec("CREATE TABLE test_write (id INTEGER);");
|
||||
write = write && db.rollback();
|
||||
|
||||
access.canWrite = write;
|
||||
}
|
||||
QSqlDatabase::removeDatabase(connectionName);
|
||||
|
||||
return access;
|
||||
}
|
||||
|
||||
// COMICS_INFO_EXPORTER
|
||||
ComicsInfoExporter::ComicsInfoExporter()
|
||||
: QThread()
|
||||
|
@ -29,6 +29,27 @@ private:
|
||||
void run() override;
|
||||
};
|
||||
|
||||
struct DatabaseAccess {
|
||||
bool libraryExists;
|
||||
bool canRead; // db read
|
||||
bool canWrite; // db write
|
||||
bool canWriteToFolder; // disk write
|
||||
|
||||
operator QString() const
|
||||
{
|
||||
if (libraryExists && canRead && canWrite && canWriteToFolder) {
|
||||
return "OK";
|
||||
} else if (!libraryExists) {
|
||||
return "WARNING! Library does not exist on disk";
|
||||
} else {
|
||||
return QString("WARNING! DB read access: %1, DB write access: %2, can write to disk: %3")
|
||||
.arg(canRead ? "YES" : "NO")
|
||||
.arg(canWrite ? "YES" : "NO")
|
||||
.arg(canWriteToFolder ? "YES" : "NO");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class DataBaseManagement : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -46,7 +67,7 @@ public:
|
||||
static QSqlDatabase createDatabase(QString name, QString path);
|
||||
static QSqlDatabase createDatabase(QString dest);
|
||||
// carga una base de datos desde la ruta path
|
||||
static QSqlDatabase loadDatabase(QString path);
|
||||
static QSqlDatabase loadDatabase(QString libraryDataPath);
|
||||
static QSqlDatabase loadDatabaseFromFile(QString path);
|
||||
static bool createTables(QSqlDatabase &database);
|
||||
static bool createComicInfoTable(QSqlDatabase &database, QString tableName);
|
||||
@ -57,7 +78,9 @@ public:
|
||||
|
||||
static QString checkValidDB(const QString &fullPath); // retorna "" si la DB es inválida ó la versión si es válida.
|
||||
static int compareVersions(const QString &v1, const QString v2); // retorna <0 si v1 < v2, 0 si v1 = v2 y >0 si v1 > v2
|
||||
static bool updateToCurrentVersion(const QString &path);
|
||||
static bool updateToCurrentVersion(const QString &libraryPath);
|
||||
|
||||
static DatabaseAccess getDatabaseAccess(const QString &libraryPath);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -13,6 +13,8 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using namespace YACReader;
|
||||
|
||||
#ifdef Y_MAC_UI
|
||||
#include <QFileIconProvider>
|
||||
QIcon finishedFolderIcon;
|
||||
@ -368,8 +370,12 @@ QVariant FolderModel::data(const QModelIndex &index, int role) const
|
||||
if (role == FolderModel::IdRole)
|
||||
return item->id;
|
||||
|
||||
if (role == FolderModel::CoverPathRole)
|
||||
return getCoverUrlPathForComicHash(item->data(FirstChildHash).toString());
|
||||
if (role == FolderModel::CoverPathRole) {
|
||||
if (item->data(FolderModel::CustomImage).toString().isEmpty())
|
||||
return getCoverUrlPathForComicHash(item->data(FirstChildHash).toString());
|
||||
else
|
||||
return getCoverUrlPathForFolderId(item->id);
|
||||
}
|
||||
|
||||
if (role == FolderModel::NumChildrenRole)
|
||||
return item->data(NumChildren);
|
||||
@ -673,6 +679,50 @@ void FolderModel::updateTreeType(YACReader::FileType type)
|
||||
QSqlDatabase::removeDatabase(connectionName);
|
||||
}
|
||||
|
||||
void FolderModel::setCustomFolderCover(const QModelIndex &index, const QString &path)
|
||||
{
|
||||
QString connectionName = "";
|
||||
{
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
|
||||
db.transaction();
|
||||
|
||||
auto item = static_cast<FolderItem *>(index.internalPointer());
|
||||
item->setData(FolderModel::CustomImage, path);
|
||||
|
||||
Folder f = DBHelper::loadFolder(item->id, db);
|
||||
f.customImage = path;
|
||||
DBHelper::update(f, db);
|
||||
|
||||
db.commit();
|
||||
connectionName = db.connectionName();
|
||||
}
|
||||
QSqlDatabase::removeDatabase(connectionName);
|
||||
|
||||
emit dataChanged(index, index);
|
||||
}
|
||||
|
||||
void FolderModel::resetFolderCover(const QModelIndex &index)
|
||||
{
|
||||
QString connectionName = "";
|
||||
{
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
|
||||
db.transaction();
|
||||
|
||||
auto item = static_cast<FolderItem *>(index.internalPointer());
|
||||
item->setData(FolderModel::CustomImage, "");
|
||||
|
||||
Folder f = DBHelper::loadFolder(item->id, db);
|
||||
f.customImage = "";
|
||||
DBHelper::update(f, db);
|
||||
|
||||
db.commit();
|
||||
connectionName = db.connectionName();
|
||||
}
|
||||
QSqlDatabase::removeDatabase(connectionName);
|
||||
|
||||
emit dataChanged(index, index);
|
||||
}
|
||||
|
||||
QStringList FolderModel::getSubfoldersNames(const QModelIndex &mi)
|
||||
{
|
||||
QStringList result;
|
||||
@ -852,7 +902,14 @@ QModelIndex FolderModel::addFolderAtParent(const QString &folderName, const QMod
|
||||
|
||||
QUrl FolderModel::getCoverUrlPathForComicHash(const QString &hash) const
|
||||
{
|
||||
return QUrl::fromLocalFile(_databasePath + "/covers/" + hash + ".jpg");
|
||||
auto coverPath = LibraryPaths::coverPathFromLibraryDataPath(_databasePath, hash);
|
||||
return QUrl::fromLocalFile(coverPath);
|
||||
}
|
||||
|
||||
QUrl FolderModel::getCoverUrlPathForFolderId(qulonglong folderId) const
|
||||
{
|
||||
auto coverPath = LibraryPaths::customFolderCoverPathFromDataPath(_databasePath, QString::number(folderId));
|
||||
return QUrl::fromLocalFile(coverPath);
|
||||
}
|
||||
|
||||
void FolderModel::setShowRecent(bool showRecent)
|
||||
|
@ -70,6 +70,8 @@ public:
|
||||
void updateFolderFinishedStatus(const QModelIndexList &list, bool status);
|
||||
void updateFolderType(const QModelIndexList &list, YACReader::FileType type);
|
||||
void updateTreeType(YACReader::FileType type);
|
||||
void setCustomFolderCover(const QModelIndex &index, const QString &path);
|
||||
void resetFolderCover(const QModelIndex &index);
|
||||
|
||||
QStringList getSubfoldersNames(const QModelIndex &mi);
|
||||
FolderModel *getSubfoldersModel(const QModelIndex &mi); // it creates a model that contains just the direct subfolders
|
||||
@ -81,6 +83,7 @@ public:
|
||||
QModelIndex addFolderAtParent(const QString &folderName, const QModelIndex &parent);
|
||||
|
||||
Q_INVOKABLE QUrl getCoverUrlPathForComicHash(const QString &hash) const;
|
||||
Q_INVOKABLE QUrl getCoverUrlPathForFolderId(qulonglong folderId) const;
|
||||
|
||||
void setShowRecent(bool showRecent);
|
||||
void setRecentRange(int days);
|
||||
|
@ -21,10 +21,14 @@
|
||||
#include "data_base_management.h"
|
||||
#include "folder.h"
|
||||
#include "yacreader_libraries.h"
|
||||
#include "yacreader_global.h"
|
||||
|
||||
#include "qnaturalsorting.h"
|
||||
|
||||
#include "QsLog.h"
|
||||
|
||||
using namespace YACReader;
|
||||
|
||||
// server
|
||||
|
||||
YACReaderLibraries DBHelper::getLibraries()
|
||||
@ -40,7 +44,7 @@ QList<LibraryItem *> DBHelper::getFolderSubfoldersFromLibrary(qulonglong library
|
||||
QString connectionName = "";
|
||||
QList<LibraryItem *> list;
|
||||
{
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(libraryPath + "/.yacreaderlibrary");
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(LibraryPaths::libraryDataPath(libraryPath));
|
||||
list = DBHelper::getFoldersFromParent(folderId, db, false);
|
||||
|
||||
connectionName = db.connectionName();
|
||||
@ -82,7 +86,7 @@ QList<LibraryItem *> DBHelper::getFolderComicsFromLibrary(qulonglong libraryId,
|
||||
QString connectionName = "";
|
||||
QList<LibraryItem *> list;
|
||||
{
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(libraryPath + "/.yacreaderlibrary");
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(LibraryPaths::libraryDataPath(libraryPath));
|
||||
list = DBHelper::getComicsFromParent(folderId, db, sort);
|
||||
|
||||
connectionName = db.connectionName();
|
||||
@ -98,7 +102,7 @@ quint32 DBHelper::getNumChildrenFromFolder(qulonglong libraryId, qulonglong fold
|
||||
QString connectionName = "";
|
||||
|
||||
{
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(libraryPath + "/.yacreaderlibrary");
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(LibraryPaths::libraryDataPath(libraryPath));
|
||||
|
||||
QSqlQuery selectQuery(db);
|
||||
selectQuery.prepare("SELECT count(*) FROM folder WHERE parentId = :parentId and id <> 1");
|
||||
@ -126,7 +130,7 @@ qulonglong DBHelper::getParentFromComicFolderId(qulonglong libraryId, qulonglong
|
||||
QString connectionName = "";
|
||||
Folder f;
|
||||
{
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(libraryPath + "/.yacreaderlibrary");
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(LibraryPaths::libraryDataPath(libraryPath));
|
||||
|
||||
f = DBHelper::loadFolder(id, db);
|
||||
connectionName = db.connectionName();
|
||||
@ -141,7 +145,7 @@ ComicDB DBHelper::getComicInfo(qulonglong libraryId, qulonglong id)
|
||||
QString connectionName = "";
|
||||
ComicDB comic;
|
||||
{
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(libraryPath + "/.yacreaderlibrary");
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(LibraryPaths::libraryDataPath(libraryPath));
|
||||
|
||||
bool found;
|
||||
comic = DBHelper::loadComic(id, db, found);
|
||||
@ -157,7 +161,7 @@ QList<ComicDB> DBHelper::getSiblings(qulonglong libraryId, qulonglong parentId)
|
||||
QString connectionName = "";
|
||||
QList<ComicDB> comics;
|
||||
{
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(libraryPath + "/.yacreaderlibrary");
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(LibraryPaths::libraryDataPath(libraryPath));
|
||||
comics = DBHelper::getSortedComicsFromParent(parentId, db);
|
||||
connectionName = db.connectionName();
|
||||
}
|
||||
@ -174,7 +178,7 @@ QString DBHelper::getFolderName(qulonglong libraryId, qulonglong id)
|
||||
QString connectionName = "";
|
||||
|
||||
{
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(libraryPath + "/.yacreaderlibrary");
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(LibraryPaths::libraryDataPath(libraryPath));
|
||||
QSqlQuery selectQuery(db); // TODO check
|
||||
selectQuery.prepare("SELECT name FROM folder WHERE id = :id");
|
||||
selectQuery.bindValue(":id", id);
|
||||
@ -198,7 +202,7 @@ Folder DBHelper::getFolder(qulonglong libraryId, qulonglong id)
|
||||
QString connectionName = "";
|
||||
|
||||
{
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(libraryPath + "/.yacreaderlibrary");
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(LibraryPaths::libraryDataPath(libraryPath));
|
||||
QSqlQuery selectQuery(db); // TODO check
|
||||
selectQuery.prepare("SELECT * FROM folder WHERE id = :id");
|
||||
selectQuery.bindValue(":id", id);
|
||||
@ -259,7 +263,7 @@ QList<ComicDB> DBHelper::getLabelComics(qulonglong libraryId, qulonglong labelId
|
||||
QString connectionName = "";
|
||||
|
||||
{
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(libraryPath + "/.yacreaderlibrary");
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(LibraryPaths::libraryDataPath(libraryPath));
|
||||
QSqlQuery selectQuery(db);
|
||||
selectQuery.prepare("SELECT c.id,c.fileName,ci.title,ci.currentPage,ci.numPages,ci.hash,ci.read,ci.coverSizeRatio "
|
||||
"FROM comic c INNER JOIN comic_info ci ON (c.comicInfoId = ci.id) "
|
||||
@ -300,7 +304,7 @@ QList<ComicDB> DBHelper::getFavorites(qulonglong libraryId)
|
||||
QString connectionName = "";
|
||||
|
||||
{
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(libraryPath + "/.yacreaderlibrary");
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(LibraryPaths::libraryDataPath(libraryPath));
|
||||
QSqlQuery selectQuery(db);
|
||||
selectQuery.prepare("SELECT c.id,c.fileName,ci.title,ci.currentPage,ci.numPages,ci.hash,ci.read,ci.coverSizeRatio "
|
||||
"FROM comic c INNER JOIN comic_info ci ON (c.comicInfoId = ci.id) "
|
||||
@ -341,7 +345,7 @@ QList<ComicDB> DBHelper::getReading(qulonglong libraryId)
|
||||
QString connectionName = "";
|
||||
|
||||
{
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(libraryPath + "/.yacreaderlibrary");
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(LibraryPaths::libraryDataPath(libraryPath));
|
||||
QSqlQuery selectQuery(db);
|
||||
selectQuery.prepare("SELECT c.id,c.parentId,c.fileName,ci.title,ci.currentPage,ci.numPages,ci.hash,ci.read,ci.coverSizeRatio,ci.number "
|
||||
"FROM comic c INNER JOIN comic_info ci ON (c.comicInfoId = ci.id) "
|
||||
@ -381,7 +385,7 @@ QList<ReadingList> DBHelper::getReadingLists(qulonglong libraryId)
|
||||
QList<ReadingList> list;
|
||||
|
||||
{
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(libraryPath + "/.yacreaderlibrary");
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(LibraryPaths::libraryDataPath(libraryPath));
|
||||
|
||||
QSqlQuery selectQuery("SELECT * from reading_list WHERE parentId IS NULL ORDER BY name DESC", db);
|
||||
|
||||
@ -420,7 +424,7 @@ QList<ComicDB> DBHelper::getReadingListFullContent(qulonglong libraryId, qulongl
|
||||
QString connectionName = "";
|
||||
|
||||
{
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(libraryPath + "/.yacreaderlibrary");
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(LibraryPaths::libraryDataPath(libraryPath));
|
||||
QList<qulonglong> ids;
|
||||
ids << readingListId;
|
||||
|
||||
@ -618,7 +622,7 @@ void DBHelper::update(qulonglong libraryId, ComicInfo &comicInfo)
|
||||
QString libraryPath = DBHelper::getLibraries().getPath(libraryId);
|
||||
QString connectionName = "";
|
||||
{
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(libraryPath + "/.yacreaderlibrary");
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(LibraryPaths::libraryDataPath(libraryPath));
|
||||
DBHelper::update(&comicInfo, db);
|
||||
connectionName = db.connectionName();
|
||||
}
|
||||
@ -820,13 +824,34 @@ void DBHelper::updateAdded(ComicInfo *comicInfo, QSqlDatabase &db)
|
||||
void DBHelper::update(const Folder &folder, QSqlDatabase &db)
|
||||
{
|
||||
QSqlQuery updateFolderInfo(db);
|
||||
|
||||
updateFolderInfo.prepare("UPDATE folder SET "
|
||||
"parentId = :parentId, "
|
||||
"name = :name, "
|
||||
"path = :path, "
|
||||
"finished = :finished, "
|
||||
"completed = :completed "
|
||||
"WHERE id = :id ");
|
||||
"completed = :completed, "
|
||||
"numChildren = :numChildren, "
|
||||
"firstChildHash = :firstChildHash, "
|
||||
"customImage = :customImage, "
|
||||
"type = :type, "
|
||||
"added = :added, "
|
||||
"updated = :updated "
|
||||
"WHERE id = :id");
|
||||
|
||||
updateFolderInfo.bindValue(":parentId", folder.parentId);
|
||||
updateFolderInfo.bindValue(":name", folder.name);
|
||||
updateFolderInfo.bindValue(":path", folder.path);
|
||||
updateFolderInfo.bindValue(":finished", folder.finished ? 1 : 0);
|
||||
updateFolderInfo.bindValue(":completed", folder.completed ? 1 : 0);
|
||||
updateFolderInfo.bindValue(":numChildren", folder.numChildren);
|
||||
updateFolderInfo.bindValue(":firstChildHash", folder.firstChildHash);
|
||||
updateFolderInfo.bindValue(":customImage", folder.customImage);
|
||||
updateFolderInfo.bindValue(":type", static_cast<int>(folder.type));
|
||||
updateFolderInfo.bindValue(":added", folder.added);
|
||||
updateFolderInfo.bindValue(":updated", folder.updated);
|
||||
updateFolderInfo.bindValue(":id", folder.id);
|
||||
|
||||
updateFolderInfo.exec();
|
||||
}
|
||||
|
||||
@ -872,6 +897,10 @@ Folder DBHelper::updateChildrenInfo(qulonglong folderId, QSqlDatabase &db)
|
||||
}
|
||||
}
|
||||
|
||||
if (folder.numChildren == subfolders.count() + comics.count() && folder.firstChildHash == coverHash) {
|
||||
return folder;
|
||||
}
|
||||
|
||||
folder.numChildren = subfolders.count() + comics.count();
|
||||
folder.firstChildHash = coverHash;
|
||||
|
||||
@ -894,6 +923,9 @@ Folder DBHelper::updateChildrenInfo(qulonglong folderId, QSqlDatabase &db)
|
||||
|
||||
void DBHelper::updateChildrenInfo(QSqlDatabase &db)
|
||||
{
|
||||
// measure time
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
QSqlQuery selectQuery(db); // TODO check
|
||||
selectQuery.prepare("SELECT id FROM folder f WHERE f.parentId = 1 AND f.id <> 1");
|
||||
selectQuery.exec();
|
||||
@ -901,6 +933,12 @@ void DBHelper::updateChildrenInfo(QSqlDatabase &db)
|
||||
while (selectQuery.next()) {
|
||||
DBHelper::updateChildrenInfo(selectQuery.value(0).toULongLong(), db);
|
||||
}
|
||||
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
|
||||
QString time = QString::number(std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count());
|
||||
QString message = "updateChildrenInfo took " + time + "ms";
|
||||
QLOG_INFO() << message;
|
||||
}
|
||||
|
||||
void DBHelper::updateProgress(qulonglong libraryId, const ComicInfo &comicInfo)
|
||||
@ -908,7 +946,7 @@ void DBHelper::updateProgress(qulonglong libraryId, const ComicInfo &comicInfo)
|
||||
QString libraryPath = DBHelper::getLibraries().getPath(libraryId);
|
||||
QString connectionName = "";
|
||||
{
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(libraryPath + "/.yacreaderlibrary");
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(LibraryPaths::libraryDataPath(libraryPath));
|
||||
|
||||
bool found;
|
||||
ComicDB comic = DBHelper::loadComic(comicInfo.id, db, found);
|
||||
@ -929,7 +967,7 @@ void DBHelper::setComicAsReading(qulonglong libraryId, const ComicInfo &comicInf
|
||||
QString libraryPath = DBHelper::getLibraries().getPath(libraryId);
|
||||
QString connectionName = "";
|
||||
{
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(libraryPath + "/.yacreaderlibrary");
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(LibraryPaths::libraryDataPath(libraryPath));
|
||||
|
||||
bool found;
|
||||
ComicDB comic = DBHelper::loadComic(comicInfo.id, db, found);
|
||||
@ -970,7 +1008,7 @@ void DBHelper::updateFromRemoteClient(qulonglong libraryId, const ComicInfo &com
|
||||
QString libraryPath = DBHelper::getLibraries().getPath(libraryId);
|
||||
QString connectionName = "";
|
||||
{
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(libraryPath + "/.yacreaderlibrary");
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(LibraryPaths::libraryDataPath(libraryPath));
|
||||
|
||||
bool found;
|
||||
ComicDB comic = DBHelper::loadComic(comicInfo.id, db, found);
|
||||
@ -1009,7 +1047,7 @@ QMap<qulonglong, QList<ComicDB>> DBHelper::updateFromRemoteClient(const QMap<qul
|
||||
|
||||
QString connectionName = "";
|
||||
{
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(libraryPath + "/.yacreaderlibrary");
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(LibraryPaths::libraryDataPath(libraryPath));
|
||||
|
||||
db.transaction();
|
||||
|
||||
@ -1100,7 +1138,7 @@ void DBHelper::updateFromRemoteClientWithHash(const QList<ComicInfo> &comics)
|
||||
QString libraryPath = DBHelper::getLibraries().getPath(libraries.getId(name));
|
||||
QString connectionName = "";
|
||||
{
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(libraryPath + "/.yacreaderlibrary");
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(LibraryPaths::libraryDataPath(libraryPath));
|
||||
|
||||
db.transaction();
|
||||
|
||||
@ -1595,7 +1633,7 @@ QList<Label> DBHelper::getLabels(qulonglong libraryId)
|
||||
QString connectionName = "";
|
||||
QList<Label> labels;
|
||||
{
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(libraryPath + "/.yacreaderlibrary");
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(LibraryPaths::libraryDataPath(libraryPath));
|
||||
|
||||
QSqlQuery selectQuery("SELECT * FROM label ORDER BY ordering,name", db); // TODO add some kind of
|
||||
QSqlRecord record = selectQuery.record();
|
||||
@ -2033,7 +2071,7 @@ QString DBHelper::getLibraryInfo(QUuid id)
|
||||
QString connectionName = "";
|
||||
QList<LibraryItem *> list;
|
||||
{
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(libraryPath + "/.yacreaderlibrary");
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(LibraryPaths::libraryDataPath(libraryPath));
|
||||
connectionName = db.connectionName();
|
||||
|
||||
// num folders
|
||||
|
@ -67,7 +67,7 @@ public:
|
||||
static void update(ComicInfo *comicInfo, QSqlDatabase &db);
|
||||
static void updateRead(ComicInfo *comicInfo, QSqlDatabase &db);
|
||||
static void updateAdded(ComicInfo *comicInfo, QSqlDatabase &db);
|
||||
static void update(const Folder &folder, QSqlDatabase &db); // only for finished/completed fields
|
||||
static void update(const Folder &folder, QSqlDatabase &db);
|
||||
static void propagateFolderUpdatesToParent(const Folder &folder, QSqlDatabase &db);
|
||||
static Folder updateChildrenInfo(qulonglong folderId, QSqlDatabase &db);
|
||||
static void updateChildrenInfo(QSqlDatabase &db);
|
||||
|
@ -10,6 +10,7 @@
|
||||
<file>../images/comic_vine/rowDown.png</file>
|
||||
<file>../images/comic_vine/rowUp.png</file>
|
||||
<file>../images/comic_vine/upArrow.png</file>
|
||||
<file>../images/comic_vine/checkBoxTick.svg</file>
|
||||
<file>../images/comics_view_toolbar/asignNumber.svg</file>
|
||||
<file>../images/comics_view_toolbar/big_size_grid_zoom.svg</file>
|
||||
<file>../images/comics_view_toolbar/editComic.svg</file>
|
||||
@ -42,6 +43,7 @@
|
||||
<file>../images/flow4.png</file>
|
||||
<file>../images/flow5.png</file>
|
||||
<file>../images/glowLine.png</file>
|
||||
<file>../images/loadCustomCover.svg</file>
|
||||
<file>../images/hiddenCovers.png</file>
|
||||
<file>../images/icon-new.svg</file>
|
||||
<file>../images/iconLibrary.png</file>
|
||||
@ -69,6 +71,7 @@
|
||||
<file>../images/previousCoverPage.png</file>
|
||||
<file>../images/readingRibbon.png</file>
|
||||
<file>../images/readRibbon.png</file>
|
||||
<file>../images/resetCover.svg</file>
|
||||
<file>../images/searching_icon.png</file>
|
||||
<file>../images/serverConfigBackground.png</file>
|
||||
<file>../images/shortcuts_group_comics.svg</file>
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "comic.h"
|
||||
#include "compressed_archive.h"
|
||||
#include "qnaturalsorting.h"
|
||||
#include "cover_utils.h"
|
||||
|
||||
using namespace YACReader;
|
||||
|
||||
@ -154,17 +155,5 @@ QByteArray InitialComicInfoExtractor::getXMLInfoRawData()
|
||||
|
||||
void InitialComicInfoExtractor::saveCover(const QString &path, const QImage &cover)
|
||||
{
|
||||
QImage scaled;
|
||||
if (cover.width() > cover.height()) {
|
||||
scaled = cover.scaledToWidth(640, Qt::SmoothTransformation);
|
||||
} else {
|
||||
auto aspectRatio = static_cast<double>(cover.width()) / static_cast<double>(cover.height());
|
||||
auto maxAllowedAspectRatio = 0.5;
|
||||
if (aspectRatio < maxAllowedAspectRatio) { // cover is too tall, e.g. webtoon
|
||||
scaled = cover.scaledToHeight(960, Qt::SmoothTransformation);
|
||||
} else {
|
||||
scaled = cover.scaledToWidth(480, Qt::SmoothTransformation);
|
||||
}
|
||||
}
|
||||
scaled.save(_target, 0, 75);
|
||||
YACReader::saveCover(path, cover);
|
||||
}
|
||||
|
@ -5,6 +5,8 @@
|
||||
#include "yacreader_libraries.h"
|
||||
#include "yacreader_global.h"
|
||||
|
||||
using namespace YACReader;
|
||||
|
||||
LibrariesUpdateCoordinator::LibrariesUpdateCoordinator(QSettings *settings, YACReaderLibraries &libraries, const std::function<bool()> &canStartUpdateProvider, QObject *parent)
|
||||
: QObject(parent), libraries(libraries), canStartUpdateProvider(canStartUpdateProvider)
|
||||
{
|
||||
@ -121,7 +123,7 @@ void LibrariesUpdateCoordinator::updateLibrary(const QString &path)
|
||||
|
||||
QString cleanPath = QDir::cleanPath(pathDir.absolutePath());
|
||||
|
||||
libraryCreator->updateLibrary(cleanPath, QDir::cleanPath(pathDir.absolutePath() + "/.yacreaderlibrary"));
|
||||
libraryCreator->updateLibrary(cleanPath, LibraryPaths::libraryDataPath(cleanPath));
|
||||
|
||||
connect(libraryCreator, &LibraryCreator::finished, &eventLoop, &QEventLoop::quit);
|
||||
|
||||
|
@ -47,15 +47,45 @@ bool YACReader::openComic(const ComicDB &comic,
|
||||
return yacreaderFound;
|
||||
}
|
||||
|
||||
QStringList parseCommand(const QString &input)
|
||||
{
|
||||
QRegularExpression regex(R"((?:\"([^\"]*)\")|(\S+))");
|
||||
QStringList result;
|
||||
auto it = regex.globalMatch(input);
|
||||
while (it.hasNext()) {
|
||||
QRegularExpressionMatch match = it.next();
|
||||
if (match.hasMatch()) {
|
||||
result << (match.captured(1).isEmpty() ? match.captured(2) : match.captured(1));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool YACReader::openComicInThirdPartyApp(const QString &command, const QString &path)
|
||||
{
|
||||
QString mutableCommand = command;
|
||||
QString fullCommand;
|
||||
if (mutableCommand.contains("{comic_file_path}")) {
|
||||
fullCommand = mutableCommand.replace("{comic_file_path}", "\"" + path + "\"");
|
||||
} else {
|
||||
fullCommand = mutableCommand + " \"" + path + "\"";
|
||||
QStringList parsed = parseCommand(command);
|
||||
if (parsed.isEmpty()) {
|
||||
qDebug() << "Empty command";
|
||||
return false;
|
||||
}
|
||||
|
||||
return QProcess::startDetached(fullCommand, {});
|
||||
QString program = parsed.takeFirst();
|
||||
QStringList rawArguments = parsed;
|
||||
QStringList arguments;
|
||||
|
||||
auto placeholderFound = false;
|
||||
for (auto argument : rawArguments) {
|
||||
if (argument.contains("{comic_file_path}")) {
|
||||
placeholderFound = true;
|
||||
arguments << argument.replace("{comic_file_path}", path);
|
||||
} else {
|
||||
arguments << argument;
|
||||
}
|
||||
}
|
||||
|
||||
if (!placeholderFound) {
|
||||
arguments << path;
|
||||
}
|
||||
|
||||
return QProcess::startDetached(program, arguments);
|
||||
}
|
||||
|
@ -154,7 +154,7 @@ void LibraryCreator::run()
|
||||
_currentPathFolders.clear();
|
||||
// se crean los directorios .yacreaderlibrary y .yacreaderlibrary/covers
|
||||
QDir dir;
|
||||
dir.mkpath(_target + "/covers");
|
||||
dir.mkpath(LibraryPaths::libraryCoversPathFromLibraryDataPath(_target));
|
||||
|
||||
// se crea la base de datos .yacreaderlibrary/library.ydb
|
||||
{
|
||||
@ -269,7 +269,8 @@ void LibraryCreator::cancel()
|
||||
|
||||
void LibraryCreator::cleanup(QSqlDatabase &db, const QString &target)
|
||||
{
|
||||
QDir coversDir(target + "/covers/");
|
||||
auto coversPath = LibraryPaths::libraryCoversPathFromLibraryDataPath(target);
|
||||
QDir coversDir(coversPath);
|
||||
if (!coversDir.exists()) {
|
||||
return;
|
||||
}
|
||||
@ -285,10 +286,8 @@ void LibraryCreator::cleanup(QSqlDatabase &db, const QString &target)
|
||||
|
||||
while (infoToDeleteQuery.next()) {
|
||||
QString hash = infoToDeleteQuery.value(1).toString();
|
||||
QString cover = hash + ".jpg";
|
||||
|
||||
auto fullPath = coversDir.absoluteFilePath(cover);
|
||||
QFile::remove(fullPath);
|
||||
QString coverPath = LibraryPaths::coverPathFromLibraryDataPath(target, hash);
|
||||
QFile::remove(coverPath);
|
||||
}
|
||||
|
||||
QSqlQuery deleteQuery(db);
|
||||
@ -358,7 +357,7 @@ void LibraryCreator::create(QDir dir)
|
||||
|
||||
bool LibraryCreator::checkCover(const QString &hash)
|
||||
{
|
||||
return QFile::exists(_target + "/covers/" + hash + ".jpg");
|
||||
return QFile::exists(LibraryPaths::coverPathFromLibraryDataPath(_target, hash));
|
||||
}
|
||||
|
||||
QString pseudoHash(const QFileInfo &fileInfo)
|
||||
@ -383,14 +382,15 @@ void LibraryCreator::insertComic(const QString &relativePath, const QFileInfo &f
|
||||
QPair<int, int> originalCoverSize = { 0, 0 };
|
||||
bool exists = checkCover(hash);
|
||||
|
||||
YACReader::InitialComicInfoExtractor ie(QDir::cleanPath(fileInfo.absoluteFilePath()), _target + "/covers/" + hash + ".jpg", comic.info.coverPage.toInt(), settings->value(IMPORT_COMIC_INFO_XML_METADATA, false).toBool());
|
||||
auto coverPath = LibraryPaths::coverPathFromLibraryDataPath(_target, hash);
|
||||
YACReader::InitialComicInfoExtractor ie(QDir::cleanPath(fileInfo.absoluteFilePath()), coverPath, comic.info.coverPage.toInt(), settings->value(IMPORT_COMIC_INFO_XML_METADATA, false).toBool());
|
||||
|
||||
if (!(comic.hasCover() && exists)) {
|
||||
ie.extract();
|
||||
numPages = ie.getNumPages();
|
||||
originalCoverSize = ie.getOriginalCoverSize();
|
||||
if (numPages > 0) {
|
||||
emit comicAdded(relativePath, _target + "/covers/" + hash + ".jpg");
|
||||
emit comicAdded(relativePath, coverPath);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "library_window.h"
|
||||
|
||||
#include "yacreader_global.h"
|
||||
#include "yacreader_global_gui.h"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QSplitter>
|
||||
@ -83,12 +84,15 @@
|
||||
|
||||
#include "recent_visibility_coordinator.h"
|
||||
|
||||
#include "cover_utils.h"
|
||||
|
||||
#include "QsLog.h"
|
||||
|
||||
#include "yacreader_http_server.h"
|
||||
extern YACReaderHttpServer *httpServer;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <windows.h>
|
||||
#include <shellapi.h>
|
||||
#endif
|
||||
|
||||
@ -535,6 +539,10 @@ void LibraryWindow::createMenus()
|
||||
foldersView->addAction(actions.setFolderAsWesternMangaAction);
|
||||
foldersView->addAction(actions.setFolderAsWebComicAction);
|
||||
foldersView->addAction(actions.setFolderAsYonkomaAction);
|
||||
YACReader::addSperator(foldersView);
|
||||
|
||||
foldersView->addAction(actions.setFolderCoverAction);
|
||||
foldersView->addAction(actions.deleteCustomFolderCoverAction);
|
||||
|
||||
selectedLibrary->addAction(actions.updateLibraryAction);
|
||||
selectedLibrary->addAction(actions.renameLibraryAction);
|
||||
@ -668,11 +676,14 @@ void LibraryWindow::createMenus()
|
||||
folderMenu->addAction(actions.setFolderAsReadAction);
|
||||
folderMenu->addAction(actions.setFolderAsUnreadAction);
|
||||
folderMenu->addSeparator();
|
||||
foldersView->addAction(actions.setFolderAsNormalAction);
|
||||
foldersView->addAction(actions.setFolderAsMangaAction);
|
||||
foldersView->addAction(actions.setFolderAsWesternMangaAction);
|
||||
foldersView->addAction(actions.setFolderAsWebComicAction);
|
||||
foldersView->addAction(actions.setFolderAsYonkomaAction);
|
||||
folderMenu->addAction(actions.setFolderAsNormalAction);
|
||||
folderMenu->addAction(actions.setFolderAsMangaAction);
|
||||
folderMenu->addAction(actions.setFolderAsWesternMangaAction);
|
||||
folderMenu->addAction(actions.setFolderAsWebComicAction);
|
||||
folderMenu->addAction(actions.setFolderAsYonkomaAction);
|
||||
folderMenu->addSeparator();
|
||||
folderMenu->addAction(actions.setFolderCoverAction);
|
||||
folderMenu->addAction(actions.deleteCustomFolderCoverAction);
|
||||
|
||||
// comic
|
||||
QMenu *comicMenu = new QMenu(tr("Comic"));
|
||||
@ -769,19 +780,23 @@ void LibraryWindow::createConnections()
|
||||
connect(optionsDialog, &YACReaderOptionsDialog::optionsChanged, this, &LibraryWindow::reloadOptions);
|
||||
connect(optionsDialog, &YACReaderOptionsDialog::editShortcuts, editShortcutsDialog, &QWidget::show);
|
||||
|
||||
auto searchDebouncer = new KDToolBox::KDSignalDebouncer(this);
|
||||
auto searchDebouncer = new KDToolBox::KDStringSignalDebouncer(this);
|
||||
searchDebouncer->setTimeout(400);
|
||||
|
||||
// Search filter
|
||||
#ifdef Y_MAC_UI
|
||||
connect(searchEdit, &YACReaderMacOSXSearchLineEdit::filterChanged, searchDebouncer, &KDToolBox::KDSignalThrottler::throttle);
|
||||
connect(searchDebouncer, &KDToolBox::KDSignalThrottler::triggered, this, [=] {
|
||||
setSearchFilter(searchEdit->text());
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
connect(libraryToolBar, &YACReaderMacOSXToolbar::filterChanged, searchDebouncer, &KDToolBox::KDStringSignalDebouncer::throttle);
|
||||
#else
|
||||
connect(searchEdit, &YACReaderMacOSXSearchLineEdit::filterChanged, searchDebouncer, &KDToolBox::KDStringSignalDebouncer::throttle);
|
||||
#endif
|
||||
connect(searchDebouncer, &KDToolBox::KDStringSignalDebouncer::triggered, this, [=](QString filter) {
|
||||
setSearchFilter(filter);
|
||||
});
|
||||
#else
|
||||
connect(searchEdit, &YACReaderSearchLineEdit::filterChanged, searchDebouncer, &KDToolBox::KDSignalThrottler::throttle);
|
||||
connect(searchDebouncer, &KDToolBox::KDSignalThrottler::triggered, this, [=] {
|
||||
setSearchFilter(searchEdit->text());
|
||||
connect(searchEdit, &YACReaderSearchLineEdit::filterChanged, searchDebouncer, &KDToolBox::KDStringSignalDebouncer::throttle);
|
||||
connect(searchDebouncer, &KDToolBox::KDStringSignalDebouncer::triggered, this, [=](QString filter) {
|
||||
setSearchFilter(filter);
|
||||
});
|
||||
#endif
|
||||
connect(&comicQueryResultProcessor, &ComicQueryResultProcessor::newData, this, &LibraryWindow::setComicSearchFilterData);
|
||||
@ -816,11 +831,17 @@ void LibraryWindow::loadLibrary(const QString &name)
|
||||
historyController->clear();
|
||||
|
||||
showRootWidget();
|
||||
QString path = libraries.getPath(name) + "/.yacreaderlibrary";
|
||||
QString rootPath = libraries.getPath(name);
|
||||
QString path = LibraryPaths::libraryDataPath(rootPath);
|
||||
QString customFolderCoversPath = LibraryPaths::libraryCustomFoldersCoverPath(rootPath);
|
||||
QString databasePath = LibraryPaths::libraryDatabasePath(rootPath);
|
||||
QDir d; // TODO change this by static methods (utils class?? with delTree for example)
|
||||
QString dbVersion;
|
||||
if (d.exists(path) && d.exists(path + "/library.ydb") && (dbVersion = DataBaseManagement::checkValidDB(path + "/library.ydb")) != "") // si existe en disco la biblioteca seleccionada, y es válida..
|
||||
if (d.exists(path) && d.exists(databasePath) && (dbVersion = DataBaseManagement::checkValidDB(databasePath)) != "") // si existe en disco la biblioteca seleccionada, y es válida..
|
||||
{
|
||||
// this folde was added in 9.16, it needs to exist before the user starts importing custom covers for folders
|
||||
d.mkdir(customFolderCoversPath);
|
||||
|
||||
int comparation = DataBaseManagement::compareVersions(dbVersion, DB_VERSION);
|
||||
|
||||
if (comparation < 0) {
|
||||
@ -829,8 +850,8 @@ void LibraryWindow::loadLibrary(const QString &name)
|
||||
importWidget->setUpgradeLook();
|
||||
showImportingWidget();
|
||||
|
||||
upgradeLibraryFuture = std::async(std::launch::async, [this, name, path] {
|
||||
bool updated = DataBaseManagement::updateToCurrentVersion(path);
|
||||
upgradeLibraryFuture = std::async(std::launch::async, [this, name, path, rootPath] {
|
||||
bool updated = DataBaseManagement::updateToCurrentVersion(rootPath);
|
||||
|
||||
if (!updated)
|
||||
emit errorUpgradingLibrary(path);
|
||||
@ -932,11 +953,9 @@ void LibraryWindow::loadLibrary(const QString &name)
|
||||
QString currentLibrary = selectedLibrary->currentText();
|
||||
QString path = libraries.getPath(selectedLibrary->currentText());
|
||||
if (QMessageBox::question(this, tr("Old library"), tr("Library '%1' has been created with an older version of YACReaderLibrary. It must be created again. Do you want to create the library now?").arg(currentLibrary), QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) {
|
||||
QDir d(path + "/.yacreaderlibrary");
|
||||
QDir d(LibraryPaths::libraryDataPath(path));
|
||||
d.removeRecursively();
|
||||
// d.rmdir(path+"/.yacreaderlibrary");
|
||||
createLibraryDialog->setDataAndStart(currentLibrary, path);
|
||||
// create(path,path+"/.yacreaderlibrary",currentLibrary);
|
||||
}
|
||||
// será possible renombrar y borrar estas bibliotecas
|
||||
actions.renameLibraryAction->setEnabled(true);
|
||||
@ -1070,9 +1089,9 @@ void LibraryWindow::updateFolder(const QModelIndex &miFolder)
|
||||
showImportingWidget();
|
||||
|
||||
QString currentLibrary = selectedLibrary->currentText();
|
||||
QString path = libraries.getPath(currentLibrary);
|
||||
QString path = QDir::cleanPath(libraries.getPath(currentLibrary));
|
||||
_lastAdded = currentLibrary;
|
||||
libraryCreator->updateFolder(QDir::cleanPath(path), QDir::cleanPath(path + "/.yacreaderlibrary"), QDir::cleanPath(currentPath() + foldersModel->getFolderPath(miFolder)), miFolder);
|
||||
libraryCreator->updateFolder(path, LibraryPaths::libraryDataPath(path), QDir::cleanPath(currentPath() + foldersModel->getFolderPath(miFolder)), miFolder);
|
||||
libraryCreator->start();
|
||||
}
|
||||
|
||||
@ -1436,6 +1455,12 @@ void LibraryWindow::showGridFoldersContextMenu(QPoint point, Folder folder)
|
||||
auto setFolderAs4KomaAction = new QAction();
|
||||
setFolderAs4KomaAction->setText(tr("4koma (top to botom)"));
|
||||
|
||||
auto setFolderCoverAction = new QAction();
|
||||
setFolderCoverAction->setText(tr("Set custom cover"));
|
||||
|
||||
auto deleteCustomFolderCoverAction = new QAction();
|
||||
deleteCustomFolderCoverAction->setText(tr("Delete custom cover"));
|
||||
|
||||
menu.addAction(openContainingFolderAction);
|
||||
menu.addAction(updateFolderAction);
|
||||
menu.addSeparator();
|
||||
@ -1531,6 +1556,20 @@ void LibraryWindow::showGridFoldersContextMenu(QPoint point, Folder folder)
|
||||
foldersModel->updateFolderType(QModelIndexList() << foldersModel->getIndexFromFolder(folder), FileType::Yonkoma);
|
||||
subfolderModel->updateFolderType(QModelIndexList() << foldersModel->getIndexFromFolder(folder), FileType::Yonkoma);
|
||||
});
|
||||
connect(setFolderCoverAction, &QAction::triggered, this, [=]() {
|
||||
setCustomFolderCover(folder);
|
||||
});
|
||||
|
||||
connect(deleteCustomFolderCoverAction, &QAction::triggered, this, [=]() {
|
||||
resetFolderCover(folder);
|
||||
});
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
menu.addAction(setFolderCoverAction);
|
||||
if (!folder.customImage.isEmpty()) {
|
||||
menu.addAction(deleteCustomFolderCoverAction);
|
||||
}
|
||||
|
||||
menu.exec(contentViewsManager->folderContentView->mapToGlobal(point));
|
||||
}
|
||||
@ -1773,7 +1812,8 @@ void LibraryWindow::openLibrary(QString path, QString name)
|
||||
// TODO: fix bug, /a/b/c/.yacreaderlibrary/d/e
|
||||
path.remove("/.yacreaderlibrary");
|
||||
QDir d; // TODO change this by static methods (utils class?? with delTree for example)
|
||||
if (d.exists(path + "/.yacreaderlibrary")) {
|
||||
auto libraryDataPath = LibraryPaths::libraryDataPath(path);
|
||||
if (d.exists(libraryDataPath)) {
|
||||
_lastAdded = name;
|
||||
_sourceLastAdded = path;
|
||||
openLastCreated();
|
||||
@ -1805,7 +1845,7 @@ void LibraryWindow::updateLibrary()
|
||||
QString currentLibrary = selectedLibrary->currentText();
|
||||
QString path = libraries.getPath(currentLibrary);
|
||||
_lastAdded = currentLibrary;
|
||||
libraryCreator->updateLibrary(path, path + "/.yacreaderlibrary");
|
||||
libraryCreator->updateLibrary(path, LibraryPaths::libraryDataPath(path));
|
||||
libraryCreator->start();
|
||||
}
|
||||
|
||||
@ -1814,8 +1854,7 @@ void LibraryWindow::deleteCurrentLibrary()
|
||||
QString path = libraries.getPath(selectedLibrary->currentText());
|
||||
libraries.remove(selectedLibrary->currentText());
|
||||
selectedLibrary->removeItem(selectedLibrary->currentIndex());
|
||||
// selectedLibrary->setCurrentIndex(0);
|
||||
path = path + "/.yacreaderlibrary";
|
||||
path = LibraryPaths::libraryDatabasePath(path);
|
||||
|
||||
QDir d(path);
|
||||
d.removeRecursively();
|
||||
@ -1895,7 +1934,7 @@ void LibraryWindow::rescanLibraryForXMLInfo()
|
||||
QString path = libraries.getPath(currentLibrary);
|
||||
_lastAdded = currentLibrary;
|
||||
|
||||
xmlInfoLibraryScanner->scanLibrary(path, path + "/.yacreaderlibrary");
|
||||
xmlInfoLibraryScanner->scanLibrary(path, LibraryPaths::libraryDataPath(path));
|
||||
}
|
||||
|
||||
void LibraryWindow::showLibraryInfo()
|
||||
@ -1929,7 +1968,7 @@ void LibraryWindow::rescanFolderForXMLInfo(QModelIndex modelIndex)
|
||||
QString path = libraries.getPath(currentLibrary);
|
||||
_lastAdded = currentLibrary;
|
||||
|
||||
xmlInfoLibraryScanner->scanFolder(path, path + "/.yacreaderlibrary", QDir::cleanPath(currentPath() + foldersModel->getFolderPath(modelIndex)), modelIndex);
|
||||
xmlInfoLibraryScanner->scanFolder(path, LibraryPaths::libraryDataPath(path), QDir::cleanPath(currentPath() + foldersModel->getFolderPath(modelIndex)), modelIndex);
|
||||
}
|
||||
|
||||
void LibraryWindow::cancelCreating()
|
||||
@ -1952,7 +1991,7 @@ void LibraryWindow::stopXMLScanning()
|
||||
void LibraryWindow::setRootIndex()
|
||||
{
|
||||
if (!libraries.isEmpty()) {
|
||||
QString path = libraries.getPath(selectedLibrary->currentText()) + "/.yacreaderlibrary";
|
||||
QString path = LibraryPaths::libraryDataPath(libraries.getPath(selectedLibrary->currentText()));
|
||||
QDir d; // TODO change this by static methods (utils class?? with delTree for example)
|
||||
if (d.exists(path)) {
|
||||
navigationController->selectedFolder(QModelIndex());
|
||||
@ -2267,10 +2306,54 @@ void LibraryWindow::setFolderType(FileType type)
|
||||
foldersModel->updateFolderType(QModelIndexList() << foldersModelProxy->mapToSource(foldersView->currentIndex()), type);
|
||||
}
|
||||
|
||||
void LibraryWindow::setFolderCover()
|
||||
{
|
||||
auto folder = foldersModel->getFolder(foldersModelProxy->mapToSource(foldersView->currentIndex()));
|
||||
setCustomFolderCover(folder);
|
||||
}
|
||||
|
||||
void LibraryWindow::setCustomFolderCover(Folder folder)
|
||||
{
|
||||
auto customCoverPath = YACReader::imageFileLoader(this);
|
||||
if (!customCoverPath.isEmpty()) {
|
||||
QImage cover(customCoverPath);
|
||||
if (cover.isNull()) {
|
||||
QMessageBox::warning(this, tr("Invalid image"), tr("The selected file is not a valid image."));
|
||||
return;
|
||||
}
|
||||
|
||||
auto folderCoverPath = LibraryPaths::customFolderCoverPath(libraries.getPath(selectedLibrary->currentText()), QString::number(folder.id));
|
||||
if (!YACReader::saveCover(folderCoverPath, cover)) {
|
||||
QMessageBox::warning(this, tr("Error saving cover"), tr("There was an error saving the cover image."));
|
||||
}
|
||||
|
||||
QModelIndex folderIndex = foldersModel->getIndexFromFolder(folder);
|
||||
auto coversPath = LibraryPaths::libraryCoversFolderPath(libraries.getPath(selectedLibrary->currentText()));
|
||||
auto relativePath = folderCoverPath.remove(coversPath);
|
||||
foldersModel->setCustomFolderCover(folderIndex, relativePath);
|
||||
}
|
||||
}
|
||||
|
||||
void LibraryWindow::deleteCustomFolderCover()
|
||||
{
|
||||
auto folder = foldersModel->getFolder(foldersModelProxy->mapToSource(foldersView->currentIndex()));
|
||||
resetFolderCover(folder);
|
||||
}
|
||||
|
||||
void LibraryWindow::resetFolderCover(Folder folder)
|
||||
{
|
||||
auto folderCoverPath = LibraryPaths::customFolderCoverPath(libraries.getPath(selectedLibrary->currentText()), QString::number(folder.id));
|
||||
if (QFile::exists(folderCoverPath)) {
|
||||
QFile::remove(folderCoverPath);
|
||||
}
|
||||
QModelIndex folderIndex = foldersModel->getIndexFromFolder(folder);
|
||||
foldersModel->resetFolderCover(folderIndex);
|
||||
}
|
||||
|
||||
void LibraryWindow::exportLibrary(QString destPath)
|
||||
{
|
||||
QString currentLibrary = selectedLibrary->currentText();
|
||||
QString path = libraries.getPath(currentLibrary) + "/.yacreaderlibrary";
|
||||
QString path = LibraryPaths::libraryDataPath(libraries.getPath(currentLibrary));
|
||||
packageManager->createPackage(path, destPath + "/" + currentLibrary);
|
||||
}
|
||||
|
||||
@ -2311,13 +2394,13 @@ QString LibraryWindow::currentFolderPath()
|
||||
|
||||
void LibraryWindow::showExportComicsInfo()
|
||||
{
|
||||
exportComicsInfoDialog->source = currentPath() + "/.yacreaderlibrary/library.ydb";
|
||||
exportComicsInfoDialog->source = LibraryPaths::libraryDatabasePath(currentPath());
|
||||
exportComicsInfoDialog->open();
|
||||
}
|
||||
|
||||
void LibraryWindow::showImportComicsInfo()
|
||||
{
|
||||
importComicsInfoDialog->dest = currentPath() + "/.yacreaderlibrary/library.ydb";
|
||||
importComicsInfoDialog->dest = currentPath() + LibraryPaths::libraryDatabasePath(currentPath());
|
||||
importComicsInfoDialog->open();
|
||||
}
|
||||
|
||||
@ -2508,9 +2591,7 @@ void LibraryWindow::showFoldersContextMenu(const QPoint &point)
|
||||
{
|
||||
QModelIndex sourceMI = foldersModelProxy->mapToSource(foldersView->indexAt(point));
|
||||
|
||||
bool isCompleted = sourceMI.data(FolderModel::CompletedRole).toBool();
|
||||
bool isRead = sourceMI.data(FolderModel::FinishedRole).toBool();
|
||||
auto type = sourceMI.data(FolderModel::TypeRole).value<YACReader::FileType>();
|
||||
auto folder = foldersModel->getFolder(sourceMI);
|
||||
|
||||
actions.setFolderAsNormalAction->setCheckable(true);
|
||||
actions.setFolderAsMangaAction->setCheckable(true);
|
||||
@ -2524,7 +2605,7 @@ void LibraryWindow::showFoldersContextMenu(const QPoint &point)
|
||||
actions.setFolderAsWebComicAction->setChecked(false);
|
||||
actions.setFolderAsYonkomaAction->setChecked(false);
|
||||
|
||||
switch (type) {
|
||||
switch (folder.type) {
|
||||
case FileType::Comic:
|
||||
actions.setFolderAsNormalAction->setChecked(true);
|
||||
break;
|
||||
@ -2549,12 +2630,12 @@ void LibraryWindow::showFoldersContextMenu(const QPoint &point)
|
||||
menu.addSeparator(); //-------------------------------
|
||||
menu.addAction(actions.rescanXMLFromCurrentFolderAction);
|
||||
menu.addSeparator(); //-------------------------------
|
||||
if (isCompleted)
|
||||
if (folder.completed)
|
||||
menu.addAction(actions.setFolderAsNotCompletedAction);
|
||||
else
|
||||
menu.addAction(actions.setFolderAsCompletedAction);
|
||||
menu.addSeparator(); //-------------------------------
|
||||
if (isRead)
|
||||
if (folder.finished)
|
||||
menu.addAction(actions.setFolderAsUnreadAction);
|
||||
else
|
||||
menu.addAction(actions.setFolderAsReadAction);
|
||||
@ -2566,6 +2647,11 @@ void LibraryWindow::showFoldersContextMenu(const QPoint &point)
|
||||
typeMenu->addAction(actions.setFolderAsWesternMangaAction);
|
||||
typeMenu->addAction(actions.setFolderAsWebComicAction);
|
||||
typeMenu->addAction(actions.setFolderAsYonkomaAction);
|
||||
menu.addSeparator(); //-------------------------------
|
||||
menu.addAction(actions.setFolderCoverAction);
|
||||
if (!folder.customImage.isEmpty()) {
|
||||
menu.addAction(actions.deleteCustomFolderCoverAction);
|
||||
}
|
||||
|
||||
menu.exec(foldersView->mapToGlobal(point));
|
||||
}
|
||||
@ -2609,7 +2695,7 @@ void LibraryWindow::updateViewsOnComicUpdateWithId(quint64 libraryId, quint64 co
|
||||
}
|
||||
QString connectionName = "";
|
||||
{
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(path + "/.yacreaderlibrary");
|
||||
QSqlDatabase db = DataBaseManagement::loadDatabase(LibraryPaths::libraryDataPath(path));
|
||||
bool found;
|
||||
auto comic = DBHelper::loadComic(comicId, db, found);
|
||||
if (found) {
|
||||
|
@ -246,6 +246,10 @@ public slots:
|
||||
void setFolderAsRead();
|
||||
void setFolderAsUnread();
|
||||
void setFolderType(FileType type);
|
||||
void setFolderCover();
|
||||
void setCustomFolderCover(Folder folder);
|
||||
void deleteCustomFolderCover();
|
||||
void resetFolderCover(Folder folder);
|
||||
void openContainingFolderComic();
|
||||
void deleteCurrentLibrary();
|
||||
void removeLibrary();
|
||||
|
@ -281,6 +281,16 @@ void LibraryWindowActions::createActions(LibraryWindow *window, QSettings *setti
|
||||
setFolderAsUnreadAction->setData(SET_FOLDER_AS_UNREAD_ACTION_YL);
|
||||
setFolderAsUnreadAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(SET_FOLDER_AS_UNREAD_ACTION_YL));
|
||||
|
||||
setFolderCoverAction = new QAction(window);
|
||||
setFolderCoverAction->setText(tr("Set custom cover"));
|
||||
setFolderCoverAction->setData(SET_FOLDER_COVER_ACTION_YL);
|
||||
setFolderCoverAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(SET_FOLDER_COVER_ACTION_YL));
|
||||
|
||||
deleteCustomFolderCoverAction = new QAction(window);
|
||||
deleteCustomFolderCoverAction->setText(tr("Delete custom cover"));
|
||||
deleteCustomFolderCoverAction->setData(DELETE_CUSTOM_FOLDER_COVER_ACTION_YL);
|
||||
deleteCustomFolderCoverAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(DELETE_CUSTOM_FOLDER_COVER_ACTION_YL));
|
||||
|
||||
setFolderAsMangaAction = new QAction(window);
|
||||
setFolderAsMangaAction->setText(tr("manga"));
|
||||
setFolderAsMangaAction->setData(SET_FOLDER_AS_MANGA_ACTION_YL);
|
||||
@ -445,6 +455,8 @@ void LibraryWindowActions::createActions(LibraryWindow *window, QSettings *setti
|
||||
window->addAction(setFolderAsWesternMangaAction);
|
||||
window->addAction(setFolderAsWebComicAction);
|
||||
window->addAction(setFolderAsYonkomaAction);
|
||||
window->addAction(setFolderCoverAction);
|
||||
window->addAction(deleteCustomFolderCoverAction);
|
||||
window->addAction(deleteMetadataAction);
|
||||
window->addAction(rescanXMLFromCurrentFolderAction);
|
||||
window->addAction(openContainingFolderComicAction);
|
||||
@ -512,6 +524,8 @@ void LibraryWindowActions::createConnections(
|
||||
QObject::connect(setFolderAsReadAction, &QAction::triggered, window, &LibraryWindow::setFolderAsRead);
|
||||
QObject::connect(setFolderAsUnreadAction, &QAction::triggered, window, &LibraryWindow::setFolderAsUnread);
|
||||
QObject::connect(openContainingFolderAction, &QAction::triggered, window, &LibraryWindow::openContainingFolder);
|
||||
QObject::connect(setFolderCoverAction, &QAction::triggered, window, &LibraryWindow::setFolderCover);
|
||||
QObject::connect(deleteCustomFolderCoverAction, &QAction::triggered, window, &LibraryWindow::deleteCustomFolderCover);
|
||||
|
||||
QObject::connect(setFolderAsMangaAction, &QAction::triggered, window, [=]() {
|
||||
window->setFolderType(FileType::Manga);
|
||||
@ -630,7 +644,9 @@ void LibraryWindowActions::setUpShortcutsManagement(EditShortcutsDialog *editSho
|
||||
<< setFolderAsMangaAction
|
||||
<< setFolderAsNormalAction
|
||||
<< updateCurrentFolderAction
|
||||
<< rescanXMLFromCurrentFolderAction);
|
||||
<< rescanXMLFromCurrentFolderAction
|
||||
<< setFolderCoverAction
|
||||
<< deleteCustomFolderCoverAction);
|
||||
allActions << tmpList;
|
||||
|
||||
editShortcutsDialog->addActionsGroup("Lists", QIcon(":/images/shortcuts_group_folders.svg"), // TODO change icon
|
||||
|
@ -72,6 +72,9 @@ public:
|
||||
QAction *setFolderAsWesternMangaAction;
|
||||
QAction *setFolderAsWebComicAction;
|
||||
QAction *setFolderAsYonkomaAction;
|
||||
//--
|
||||
QAction *setFolderCoverAction;
|
||||
QAction *deleteCustomFolderCoverAction;
|
||||
|
||||
QAction *openContainingFolderComicAction;
|
||||
QAction *setAsReadAction;
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "yacreader_http_server.h"
|
||||
#include "yacreader_local_server.h"
|
||||
#include "comic_db.h"
|
||||
#include "data_base_management.h"
|
||||
#include "db_helper.h"
|
||||
#include "yacreader_libraries.h"
|
||||
#include "exit_check.h"
|
||||
@ -85,7 +86,13 @@ void logSystemAndConfig()
|
||||
OpenGLChecker checker;
|
||||
QLOG_INFO() << "OpenGL version : " << checker.textVersionDescription();
|
||||
|
||||
QLOG_INFO() << "Libraries: " << DBHelper::getLibraries().getLibraries();
|
||||
auto libraries = DBHelper::getLibraries().getLibraries();
|
||||
QLOG_INFO() << "Libraries: ";
|
||||
for (auto library : libraries) {
|
||||
QLOG_INFO() << " " << library;
|
||||
auto access = DataBaseManagement::getDatabaseAccess(library.getPath());
|
||||
QLOG_INFO() << " > STATUS: " << access;
|
||||
}
|
||||
QLOG_INFO() << "--------------------------------------------";
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,14 @@
|
||||
#include "properties_dialog.h"
|
||||
|
||||
#include "yacreader_global_gui.h"
|
||||
|
||||
#include "cover_utils.h"
|
||||
#include "data_base_management.h"
|
||||
#include "initial_comic_info_extractor.h"
|
||||
#include "yacreader_field_edit.h"
|
||||
#include "yacreader_field_plain_text_edit.h"
|
||||
#include "db_helper.h"
|
||||
#include "yacreader_cover_label.h"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QApplication>
|
||||
@ -36,7 +40,8 @@ PropertiesDialog::PropertiesDialog(QWidget *parent)
|
||||
createTabBar();
|
||||
auto rootLayout = new QGridLayout;
|
||||
|
||||
cover = new QLabel();
|
||||
cover = new YACReader::CoverLabel();
|
||||
connect(cover, &YACReader::CoverLabel::imageDropped, this, &PropertiesDialog::loadCustomCoverImageFromPath);
|
||||
|
||||
mainLayout = new QGridLayout;
|
||||
mainLayout->addWidget(tabBar, 0, 0);
|
||||
@ -102,11 +107,23 @@ void PropertiesDialog::createCoverBox()
|
||||
|
||||
showPreviousCoverPageButton = new QToolButton();
|
||||
showPreviousCoverPageButton->setIcon(QIcon(":/images/previousCoverPage.png"));
|
||||
showPreviousCoverPageButton->setToolTip(tr("Load previous page as cover"));
|
||||
showPreviousCoverPageButton->setStyleSheet("QToolButton {border:none;}");
|
||||
showNextCoverPageButton = new QToolButton();
|
||||
showNextCoverPageButton->setIcon(QIcon(":/images/nextCoverPage.png"));
|
||||
showNextCoverPageButton->setToolTip(tr("Load next page as cover"));
|
||||
showNextCoverPageButton->setStyleSheet("QToolButton {border:none;}");
|
||||
|
||||
resetCoverButton = new QToolButton();
|
||||
resetCoverButton->setIcon(QIcon(":/images/resetCover.svg"));
|
||||
resetCoverButton->setToolTip(tr("Reset cover to the default image"));
|
||||
resetCoverButton->setStyleSheet("QToolButton {border:none;}");
|
||||
|
||||
loadCustomCoverImageButton = new QToolButton();
|
||||
loadCustomCoverImageButton->setIcon(QIcon(":/images/loadCustomCover.svg"));
|
||||
loadCustomCoverImageButton->setToolTip(tr("Load custom cover image"));
|
||||
loadCustomCoverImageButton->setStyleSheet("QToolButton {border:none;}");
|
||||
|
||||
coverPageNumberLabel = new QLabel("-");
|
||||
|
||||
coverPageNumberLabel->setStyleSheet("QLabel {color: white; font-weight:bold; font-size:14px;}");
|
||||
@ -116,6 +133,10 @@ void PropertiesDialog::createCoverBox()
|
||||
layout->addWidget(coverPageNumberLabel, 0, Qt::AlignVCenter);
|
||||
layout->addSpacing(5);
|
||||
layout->addWidget(showNextCoverPageButton, 0, Qt::AlignVCenter);
|
||||
layout->addSpacing(5);
|
||||
layout->addWidget(resetCoverButton, 0, Qt::AlignVCenter);
|
||||
layout->addSpacing(5);
|
||||
layout->addWidget(loadCustomCoverImageButton, 0, Qt::AlignVCenter);
|
||||
|
||||
coverPageEdit->setStyleSheet("QLineEdit {border:none;}");
|
||||
layout->setSpacing(0);
|
||||
@ -132,6 +153,8 @@ void PropertiesDialog::createCoverBox()
|
||||
|
||||
connect(showPreviousCoverPageButton, &QAbstractButton::clicked, this, &PropertiesDialog::loadPreviousCover);
|
||||
connect(showNextCoverPageButton, &QAbstractButton::clicked, this, &PropertiesDialog::loadNextCover);
|
||||
connect(resetCoverButton, &QAbstractButton::clicked, this, &PropertiesDialog::resetCover);
|
||||
connect(loadCustomCoverImageButton, &QAbstractButton::clicked, this, &PropertiesDialog::loadCustomCoverImage);
|
||||
}
|
||||
|
||||
void PropertiesDialog::createGeneralInfoBox()
|
||||
@ -434,6 +457,8 @@ QImage blurred(const QImage &image, const QRect &rect, int radius, bool alphaOnl
|
||||
|
||||
void PropertiesDialog::loadComic(ComicDB &comic)
|
||||
{
|
||||
customCover = QImage();
|
||||
|
||||
if (!comic.info.series.isNull())
|
||||
series->setText(comic.info.series.toString());
|
||||
if (!comic.info.title.isNull())
|
||||
@ -456,22 +481,17 @@ void PropertiesDialog::loadComic(ComicDB &comic)
|
||||
showPreviousCoverPageButton->setEnabled(true);
|
||||
showNextCoverPageButton->setEnabled(true);
|
||||
|
||||
if (coverPage == 1)
|
||||
showPreviousCoverPageButton->setDisabled(true);
|
||||
if (coverPage == comic.info.numPages.toInt())
|
||||
showNextCoverPageButton->setDisabled(true);
|
||||
|
||||
coverChanged = false;
|
||||
coverBox->show();
|
||||
updateCoverBoxForSingleComic();
|
||||
|
||||
if (!QFileInfo(basePath + comic.path).exists()) {
|
||||
QMessageBox::warning(this, tr("Not found"), tr("Comic not found. You should update your library."));
|
||||
showPreviousCoverPageButton->setDisabled(true);
|
||||
showNextCoverPageButton->setDisabled(true);
|
||||
}
|
||||
} else {
|
||||
coverPageNumberLabel->setText("1");
|
||||
}
|
||||
/*if(comic.info.numPages != NULL)
|
||||
numPagesEdit->setText(QString::number(*comic.info.numPages));*/
|
||||
|
||||
if (!comic.info.number.isNull())
|
||||
numberEdit->setText(comic.info.number.toString());
|
||||
@ -581,9 +601,13 @@ void PropertiesDialog::updateButtons()
|
||||
|
||||
void PropertiesDialog::setComics(QList<ComicDB> comics)
|
||||
{
|
||||
updated = false;
|
||||
sequentialEditing = false;
|
||||
|
||||
updated = false;
|
||||
currentComicIndex = 0;
|
||||
coverChanged = false;
|
||||
customCover = QImage();
|
||||
|
||||
this->comics = comics;
|
||||
|
||||
ComicDB comic = comics[0];
|
||||
@ -593,7 +617,7 @@ void PropertiesDialog::setComics(QList<ComicDB> comics)
|
||||
updateButtons();
|
||||
|
||||
if (comics.length() > 1) {
|
||||
coverBox->hide();
|
||||
updateCoverBoxForMultipleComics();
|
||||
|
||||
setDisableUniqueValues(true);
|
||||
this->setWindowTitle(tr("Edit selected comics information"));
|
||||
@ -700,9 +724,12 @@ void PropertiesDialog::setComics(QList<ComicDB> comics)
|
||||
|
||||
void PropertiesDialog::setComicsForSequentialEditing(int currentComicIndex, QList<ComicDB> comics)
|
||||
{
|
||||
updated = false;
|
||||
sequentialEditing = true;
|
||||
|
||||
updated = false;
|
||||
coverChanged = false;
|
||||
customCover = QImage();
|
||||
|
||||
this->comics = comics;
|
||||
this->currentComicIndex = currentComicIndex;
|
||||
|
||||
@ -726,6 +753,40 @@ void PropertiesDialog::updateComics()
|
||||
DBHelper::update(&(itr->info), db);
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (!sequentialEditing && coverChanged) {
|
||||
auto coverPath = LibraryPaths::coverPath(basePath, itr->info.hash);
|
||||
|
||||
if (customCover.isNull()) { // reseted, we need to restore the default cover
|
||||
itr->info.coverPage = QVariant();
|
||||
|
||||
InitialComicInfoExtractor ie(basePath + itr->path, coverPath, 1);
|
||||
ie.extract();
|
||||
|
||||
if (ie.getOriginalCoverSize().second > 0) {
|
||||
itr->info.originalCoverSize = QString("%1x%2").arg(ie.getOriginalCoverSize().first).arg(ie.getOriginalCoverSize().second);
|
||||
itr->info.coverSizeRatio = static_cast<float>(ie.getOriginalCoverSize().first) / ie.getOriginalCoverSize().second;
|
||||
}
|
||||
|
||||
emit coverChangedSignal(*itr);
|
||||
|
||||
DBHelper::update(&(itr->info), db);
|
||||
updated = true;
|
||||
} else {
|
||||
itr->info.coverPage = QVariant();
|
||||
YACReader::saveCover(coverPath, customCover);
|
||||
|
||||
auto width = customCover.width();
|
||||
auto height = customCover.height();
|
||||
itr->info.originalCoverSize = QString("%1x%2").arg(width).arg(height);
|
||||
itr->info.coverSizeRatio = static_cast<float>(width) / height;
|
||||
|
||||
DBHelper::update(&(itr->info), db);
|
||||
updated = true;
|
||||
|
||||
emit coverChangedSignal(*itr);
|
||||
}
|
||||
}
|
||||
}
|
||||
db.commit();
|
||||
connectionName = db.connectionName();
|
||||
@ -739,14 +800,14 @@ void PropertiesDialog::setMultipleCover()
|
||||
QPixmap last = lastComic.info.getCover(basePath);
|
||||
last = last.scaledToHeight(575, Qt::SmoothTransformation);
|
||||
|
||||
coverImage = QPixmap::fromImage(blurred(last.toImage(), QRect(0, 0, last.width(), last.height()), 15));
|
||||
auto coverImage = QPixmap::fromImage(blurred(last.toImage(), QRect(0, 0, last.width(), last.height()), 15));
|
||||
|
||||
cover->setPixmap(coverImage);
|
||||
}
|
||||
|
||||
void PropertiesDialog::setCover(const QPixmap &coverI)
|
||||
{
|
||||
coverImage = coverI.scaledToHeight(575, Qt::SmoothTransformation);
|
||||
auto coverImage = coverI.scaledToHeight(575, Qt::SmoothTransformation);
|
||||
|
||||
cover->setPixmap(coverImage);
|
||||
}
|
||||
@ -755,13 +816,14 @@ void PropertiesDialog::setFilename(const QString &nameString)
|
||||
{
|
||||
title->setText(nameString);
|
||||
}
|
||||
|
||||
void PropertiesDialog::setNumpages(int pagesNum)
|
||||
{
|
||||
numPagesEdit->setText(QString::number(pagesNum));
|
||||
}
|
||||
|
||||
void PropertiesDialog::setSize(float sizeFloat)
|
||||
{
|
||||
|
||||
size->setText(QString::number(sizeFloat, 'f', 2) + " MB");
|
||||
}
|
||||
|
||||
@ -787,7 +849,11 @@ void PropertiesDialog::save()
|
||||
|
||||
if (sequentialEditing)
|
||||
if (coverChanged) {
|
||||
itr->info.coverPage = coverPageNumberLabel->text().toInt();
|
||||
if (customCover.isNull()) {
|
||||
itr->info.coverPage = coverPageNumberLabel->text().toInt();
|
||||
} else {
|
||||
itr->info.coverPage = QVariant();
|
||||
}
|
||||
edited = true;
|
||||
}
|
||||
|
||||
@ -954,15 +1020,32 @@ void PropertiesDialog::save()
|
||||
|
||||
if (sequentialEditing) {
|
||||
if (coverChanged) {
|
||||
InitialComicInfoExtractor ie(basePath + comics[currentComicIndex].path, basePath + "/.yacreaderlibrary/covers/" + comics[currentComicIndex].info.hash + ".jpg", comics[currentComicIndex].info.coverPage.toInt());
|
||||
ie.extract();
|
||||
auto coverPath = LibraryPaths::coverPath(basePath, comics[currentComicIndex].info.hash);
|
||||
|
||||
if (ie.getOriginalCoverSize().second > 0) {
|
||||
comics[currentComicIndex].info.originalCoverSize = QString("%1x%2").arg(ie.getOriginalCoverSize().first).arg(ie.getOriginalCoverSize().second);
|
||||
comics[currentComicIndex].info.coverSizeRatio = static_cast<float>(ie.getOriginalCoverSize().first) / ie.getOriginalCoverSize().second;
|
||||
if (customCover.isNull()) {
|
||||
InitialComicInfoExtractor ie(basePath + comics[currentComicIndex].path, coverPath, comics[currentComicIndex].info.coverPage.toInt());
|
||||
ie.extract();
|
||||
|
||||
if (ie.getOriginalCoverSize().second > 0) {
|
||||
comics[currentComicIndex].info.originalCoverSize = QString("%1x%2").arg(ie.getOriginalCoverSize().first).arg(ie.getOriginalCoverSize().second);
|
||||
comics[currentComicIndex].info.coverSizeRatio = static_cast<float>(ie.getOriginalCoverSize().first) / ie.getOriginalCoverSize().second;
|
||||
}
|
||||
|
||||
comics[currentComicIndex].info.edited = true;
|
||||
|
||||
emit coverChangedSignal(comics[currentComicIndex]);
|
||||
} else {
|
||||
auto width = customCover.width();
|
||||
auto height = customCover.height();
|
||||
|
||||
comics[currentComicIndex].info.originalCoverSize = QString("%1x%2").arg(width).arg(height);
|
||||
comics[currentComicIndex].info.coverSizeRatio = static_cast<float>(width) / height;
|
||||
|
||||
comics[currentComicIndex].info.edited = true;
|
||||
|
||||
YACReader::saveCover(coverPath, customCover);
|
||||
emit coverChangedSignal(comics[currentComicIndex]);
|
||||
}
|
||||
|
||||
emit coverChangedSignal(comics[currentComicIndex]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1092,48 +1175,87 @@ void PropertiesDialog::updateCoverPageNumberLabel(int n)
|
||||
void PropertiesDialog::loadNextCover()
|
||||
{
|
||||
int current = coverPageNumberLabel->text().toInt();
|
||||
if (current < comics[currentComicIndex].info.numPages.toInt()) {
|
||||
updateCoverPageNumberLabel(current + 1);
|
||||
int next;
|
||||
|
||||
InitialComicInfoExtractor ie(basePath + comics[currentComicIndex].path, "", current + 1);
|
||||
ie.extract();
|
||||
setCover(ie.getCover());
|
||||
repaint();
|
||||
if (current == comics[currentComicIndex].info.numPages.toInt())
|
||||
next = 1;
|
||||
else
|
||||
next = current + 1;
|
||||
|
||||
if ((current + 1) == comics[currentComicIndex].info.numPages.toInt()) {
|
||||
showNextCoverPageButton->setDisabled(true);
|
||||
}
|
||||
setCoverPage(next);
|
||||
|
||||
showPreviousCoverPageButton->setEnabled(true);
|
||||
if (current + 1 != comics[currentComicIndex].info.coverPage)
|
||||
coverChanged = true;
|
||||
else
|
||||
coverChanged = false;
|
||||
}
|
||||
coverChanged = next != comics[currentComicIndex].info.coverPage;
|
||||
}
|
||||
|
||||
void PropertiesDialog::loadPreviousCover()
|
||||
{
|
||||
int current = coverPageNumberLabel->text().toInt();
|
||||
if (current != 1) {
|
||||
updateCoverPageNumberLabel(current - 1);
|
||||
InitialComicInfoExtractor ie(basePath + comics[currentComicIndex].path, "", current - 1);
|
||||
int previous;
|
||||
|
||||
if (current == 1)
|
||||
previous = comics[currentComicIndex].info.numPages.toInt();
|
||||
else
|
||||
previous = current - 1;
|
||||
|
||||
setCoverPage(previous);
|
||||
|
||||
coverChanged = previous != comics[currentComicIndex].info.coverPage.toInt();
|
||||
}
|
||||
|
||||
void PropertiesDialog::resetCover()
|
||||
{
|
||||
if (sequentialEditing) {
|
||||
setCoverPage(1);
|
||||
coverChanged = true; // it could be that the cover is a custom cover, so we need to always update it
|
||||
} else {
|
||||
InitialComicInfoExtractor ie(basePath + comics.last().path, "", 1);
|
||||
ie.extract();
|
||||
setCover(ie.getCover());
|
||||
repaint();
|
||||
auto cover = ie.getCover();
|
||||
cover = cover.scaledToHeight(575, Qt::SmoothTransformation);
|
||||
|
||||
if ((current - 1) == 1) {
|
||||
showPreviousCoverPageButton->setDisabled(true);
|
||||
}
|
||||
auto coverImage = QPixmap::fromImage(blurred(cover.toImage(), QRect(0, 0, cover.width(), cover.height()), 15));
|
||||
|
||||
showNextCoverPageButton->setEnabled(true);
|
||||
if (current - 1 != comics[currentComicIndex].info.coverPage.toInt())
|
||||
coverChanged = true;
|
||||
else
|
||||
coverChanged = false;
|
||||
this->cover->setPixmap(coverImage);
|
||||
|
||||
customCover = QImage();
|
||||
coverChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
void PropertiesDialog::loadCustomCoverImage()
|
||||
{
|
||||
auto path = YACReader::imageFileLoader(this);
|
||||
loadCustomCoverImageFromPath(path);
|
||||
}
|
||||
|
||||
void PropertiesDialog::loadCustomCoverImageFromPath(const QString &path)
|
||||
{
|
||||
if (path.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
customCover = QImage(path);
|
||||
coverChanged = true;
|
||||
|
||||
if (customCover.isNull()) {
|
||||
QMessageBox::warning(this, tr("Invalid cover"), tr("The image is invalid."));
|
||||
return;
|
||||
}
|
||||
|
||||
setCover(QPixmap::fromImage(customCover));
|
||||
}
|
||||
|
||||
void PropertiesDialog::setCoverPage(int pageNumber)
|
||||
{
|
||||
customCover = QImage();
|
||||
|
||||
updateCoverPageNumberLabel(pageNumber);
|
||||
InitialComicInfoExtractor ie(basePath + comics[currentComicIndex].path, "", pageNumber);
|
||||
ie.extract();
|
||||
setCover(ie.getCover());
|
||||
repaint();
|
||||
}
|
||||
|
||||
bool PropertiesDialog::close()
|
||||
{
|
||||
if (updated) {
|
||||
@ -1142,3 +1264,17 @@ bool PropertiesDialog::close()
|
||||
|
||||
return QDialog::close();
|
||||
}
|
||||
|
||||
void PropertiesDialog::updateCoverBoxForMultipleComics()
|
||||
{
|
||||
showPreviousCoverPageButton->hide();
|
||||
showNextCoverPageButton->hide();
|
||||
coverPageNumberLabel->hide();
|
||||
}
|
||||
|
||||
void PropertiesDialog::updateCoverBoxForSingleComic()
|
||||
{
|
||||
showPreviousCoverPageButton->show();
|
||||
showNextCoverPageButton->show();
|
||||
coverPageNumberLabel->show();
|
||||
}
|
||||
|
@ -19,6 +19,10 @@ class QComboBox;
|
||||
// class YACReaderBusyWidget;
|
||||
class QToolButton;
|
||||
|
||||
namespace YACReader {
|
||||
class CoverLabel;
|
||||
}
|
||||
|
||||
#include "comic_db.h"
|
||||
|
||||
class PropertiesDialog : public QDialog
|
||||
@ -33,7 +37,7 @@ private:
|
||||
QTabWidget *tabBar;
|
||||
|
||||
QWidget *coverBox;
|
||||
QLabel *cover;
|
||||
YACReader::CoverLabel *cover;
|
||||
QScrollArea *sa;
|
||||
|
||||
QWidget *generalInfoBox;
|
||||
@ -113,12 +117,13 @@ private:
|
||||
QPushButton *previousButton;
|
||||
QPushButton *restoreButton; //??
|
||||
|
||||
QPixmap coverImage;
|
||||
|
||||
QToolButton *showPreviousCoverPageButton;
|
||||
QToolButton *showNextCoverPageButton;
|
||||
QLabel *coverPageNumberLabel;
|
||||
|
||||
QToolButton *resetCoverButton;
|
||||
QToolButton *loadCustomCoverImageButton;
|
||||
|
||||
void createTabBar();
|
||||
void createCoverBox();
|
||||
void createGeneralInfoBox();
|
||||
@ -144,6 +149,8 @@ private:
|
||||
bool updated;
|
||||
QString originalCoverSize;
|
||||
|
||||
QImage customCover;
|
||||
|
||||
public:
|
||||
PropertiesDialog(QWidget *parent = nullptr);
|
||||
QString databasePath;
|
||||
@ -170,7 +177,13 @@ public slots:
|
||||
void setSize(float size);
|
||||
void loadNextCover();
|
||||
void loadPreviousCover();
|
||||
void resetCover();
|
||||
void loadCustomCoverImage();
|
||||
void loadCustomCoverImageFromPath(const QString &path);
|
||||
void setCoverPage(int pageNumber);
|
||||
bool close();
|
||||
void updateCoverBoxForMultipleComics();
|
||||
void updateCoverBoxForSingleComic();
|
||||
|
||||
signals:
|
||||
void coverChangedSignal(const ComicDB &comic);
|
||||
|
@ -1,10 +1,8 @@
|
||||
#include "covercontroller_v2.h"
|
||||
#include "db_helper.h" //get libraries
|
||||
#include <QImage>
|
||||
#include "yacreader_libraries.h"
|
||||
#include "yacreader_http_session.h"
|
||||
|
||||
#include "template.h"
|
||||
#include "../static.h"
|
||||
#include "yacreader_global.h"
|
||||
|
||||
using stefanfrings::HttpRequest;
|
||||
using stefanfrings::HttpResponse;
|
||||
@ -20,9 +18,10 @@ void CoverControllerV2::service(HttpRequest &request, HttpResponse &response)
|
||||
QString path = QUrl::fromPercentEncoding(request.getPath()).toUtf8();
|
||||
QStringList pathElements = path.split('/');
|
||||
QString libraryName = DBHelper::getLibraryName(pathElements.at(3).toInt());
|
||||
QString fileName = pathElements.at(5);
|
||||
QStringList remainingPathElements = pathElements.mid(5);
|
||||
QString fileName = remainingPathElements.join('/');
|
||||
|
||||
QImage img(libraries.getPath(libraryName) + "/.yacreaderlibrary/covers/" + fileName);
|
||||
QImage img(YACReader::LibraryPaths::coverPathWithFileName(libraries.getPath(libraryName), fileName));
|
||||
if (!img.isNull()) {
|
||||
QByteArray ba;
|
||||
QBuffer buffer(&ba);
|
||||
|
@ -246,7 +246,7 @@ void RequestMapper::serviceV2(HttpRequest &request, HttpResponse &response)
|
||||
QRegExp comicFullInfo("/v2/library/.+/comic/[0-9]+/fullinfo/?"); // get comic info
|
||||
QRegExp comicUpdate("/v2/library/.+/comic/[0-9]+/update/?"); // get comic info
|
||||
QRegExp comicClose("/v2/library/.+/comic/[0-9]+/close/?"); // the server will close the comic and free memory
|
||||
QRegExp cover("/v2/library/.+/cover/[0-9a-f]+.jpg"); // get comic cover (navigation)
|
||||
QRegExp cover("/v2/library/.+/cover/.+"); // get comic cover (navigation)
|
||||
QRegExp comicPage("/v2/library/.+/comic/[0-9]+/page/[0-9]+/?"); // get comic page
|
||||
QRegExp comicPageRemote("/v2/library/.+/comic/[0-9]+/page/[0-9]+/remote?"); // get comic page (remote reading)
|
||||
QRegExp serverVersion("/v2/version/?");
|
||||
|
@ -2,9 +2,11 @@
|
||||
#include "qnaturalsorting.h"
|
||||
#include "yacreader_global.h"
|
||||
|
||||
using namespace YACReader;
|
||||
|
||||
void writeIdToLibraryFolder(const QString &path, const QUuid &id)
|
||||
{
|
||||
QFile file(path + "/.yacreaderlibrary/id");
|
||||
QFile file(LibraryPaths::idPath(path));
|
||||
if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
QTextStream stream(&file);
|
||||
stream << id.toString(QUuid::WithoutBraces);
|
||||
@ -14,7 +16,7 @@ void writeIdToLibraryFolder(const QString &path, const QUuid &id)
|
||||
|
||||
QUuid readFromLibraryFolder(const QString &path)
|
||||
{
|
||||
QFile file(path + "/.yacreaderlibrary/id");
|
||||
QFile file(LibraryPaths::idPath(path));
|
||||
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
QTextStream stream(&file);
|
||||
QString id = stream.readLine();
|
||||
@ -62,7 +64,7 @@ QString YACReaderLibraries::getPath(const QUuid &id)
|
||||
|
||||
QString YACReaderLibraries::getDBPath(int id)
|
||||
{
|
||||
return getPath(id) + "/.yacreaderlibrary";
|
||||
return LibraryPaths::libraryDataPath(getPath(id));
|
||||
}
|
||||
|
||||
QString YACReaderLibraries::getName(int id)
|
||||
@ -228,7 +230,7 @@ QString YACReaderLibrary::getPath() const
|
||||
|
||||
QString YACReaderLibrary::getDBPath() const
|
||||
{
|
||||
return path + "/.yacreaderlibrary";
|
||||
return LibraryPaths::libraryDataPath(path);
|
||||
}
|
||||
|
||||
int YACReaderLibrary::getLegacyId() const
|
||||
|
@ -57,6 +57,7 @@ HEADERS += ../YACReaderLibrary/library_creator.h \
|
||||
../common/bookmarks.h \
|
||||
../common/qnaturalsorting.h \
|
||||
../common/yacreader_global.h \
|
||||
../common/cover_utils.h \
|
||||
../YACReaderLibrary/yacreader_local_server.h \
|
||||
../YACReaderLibrary/comics_remover.h \
|
||||
../common/http_worker.h \
|
||||
@ -87,6 +88,7 @@ SOURCES += ../YACReaderLibrary/library_creator.cpp \
|
||||
../common/comic.cpp \
|
||||
../common/bookmarks.cpp \
|
||||
../common/qnaturalsorting.cpp \
|
||||
../common/cover_utils.cpp \
|
||||
../YACReaderLibrary/yacreader_local_server.cpp \
|
||||
../YACReaderLibrary/comics_remover.cpp \
|
||||
../common/http_worker.cpp \
|
||||
|
@ -33,7 +33,7 @@ void ConsoleUILibraryCreator::createLibrary(const QString &name, const QString &
|
||||
return;
|
||||
}
|
||||
|
||||
libraryCreator->createLibrary(cleanPath, QDir::cleanPath(pathDir.absolutePath() + "/.yacreaderlibrary"));
|
||||
libraryCreator->createLibrary(cleanPath, LibraryPaths::libraryDataPath(cleanPath));
|
||||
|
||||
connect(libraryCreator, &LibraryCreator::finished, this, &ConsoleUILibraryCreator::done);
|
||||
connect(libraryCreator, &LibraryCreator::comicAdded, this, &ConsoleUILibraryCreator::newComic);
|
||||
@ -62,7 +62,7 @@ void ConsoleUILibraryCreator::updateLibrary(const QString &path)
|
||||
LibraryCreator *libraryCreator = new LibraryCreator(settings);
|
||||
QString cleanPath = QDir::cleanPath(pathDir.absolutePath());
|
||||
|
||||
libraryCreator->updateLibrary(cleanPath, QDir::cleanPath(pathDir.absolutePath() + "/.yacreaderlibrary"));
|
||||
libraryCreator->updateLibrary(cleanPath, LibraryPaths::libraryDataPath(cleanPath));
|
||||
|
||||
connect(libraryCreator, &LibraryCreator::finished, this, &ConsoleUILibraryCreator::done);
|
||||
connect(libraryCreator, &LibraryCreator::comicAdded, this, &ConsoleUILibraryCreator::newComic);
|
||||
@ -85,8 +85,8 @@ void ConsoleUILibraryCreator::addExistingLibrary(const QString &name, const QStr
|
||||
}
|
||||
QString cleanPath = QDir::cleanPath(pathDir.absolutePath());
|
||||
|
||||
if (!QDir(cleanPath + "/.yacreaderlibrary").exists()) {
|
||||
std::cout << "No library database found in directory." << std::endl;
|
||||
if (!QDir(LibraryPaths::libraryDataPath(cleanPath)).exists()) {
|
||||
std::cout << "No data folder found in path: " << cleanPath.toStdString() << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -135,7 +135,7 @@ void ConsoleUILibraryCreator::rescanXMLInfoLibrary(const QString &path)
|
||||
connect(scanner, &XMLInfoLibraryScanner::finished, &eventLoop, &QEventLoop::quit);
|
||||
|
||||
std::cout << "Scanning comics";
|
||||
scanner->scanLibrary(cleanPath, QDir::cleanPath(pathDir.absolutePath() + "/.yacreaderlibrary"));
|
||||
scanner->scanLibrary(cleanPath, LibraryPaths::libraryDataPath(cleanPath));
|
||||
|
||||
eventLoop.exec();
|
||||
}
|
||||
|
@ -14,16 +14,18 @@ void LibrariesUpdater::updateIfNeeded()
|
||||
libraries.load();
|
||||
|
||||
foreach (QString name, libraries.getNames()) {
|
||||
QString path = libraries.getPath(name) + "/.yacreaderlibrary";
|
||||
QString libraryPath = libraries.getPath(name);
|
||||
QString libraryDataPath = YACReader::LibraryPaths::libraryDataPath(libraryPath);
|
||||
QString databasePath = YACReader::LibraryPaths::libraryDatabasePath(libraryPath);
|
||||
|
||||
QDir d;
|
||||
|
||||
QString dbVersion;
|
||||
if (d.exists(path) && d.exists(path + "/library.ydb") && (dbVersion = DataBaseManagement::checkValidDB(path + "/library.ydb")) != "") {
|
||||
if (d.exists(libraryDataPath) && d.exists(databasePath) && (dbVersion = DataBaseManagement::checkValidDB(databasePath)) != "") {
|
||||
int comparation = DataBaseManagement::compareVersions(dbVersion, DB_VERSION);
|
||||
|
||||
if (comparation < 0) {
|
||||
bool updated = DataBaseManagement::updateToCurrentVersion(path);
|
||||
bool updated = DataBaseManagement::updateToCurrentVersion(libraryPath);
|
||||
if (!updated) {
|
||||
// TODO log error
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <QImageReader>
|
||||
|
||||
#include "comic_db.h"
|
||||
#include "data_base_management.h"
|
||||
#include "db_helper.h"
|
||||
#include "yacreader_http_server.h"
|
||||
#include "yacreader_global.h"
|
||||
@ -81,7 +82,7 @@ int main(int argc, char **argv)
|
||||
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription(QString(QCoreApplication::tr("\nYACReaderLibraryServer is the headless (no gui) version of YACReaderLibrary.\n\n"
|
||||
"This appplication support persisten settings, to set them up edit this file %1\n"
|
||||
"This appplication supports persistent settings, to set them up edit this file %1\n"
|
||||
"To learn about the available settings please check the documentation at https://raw.githubusercontent.com/YACReader/yacreader/develop/YACReaderLibraryServer/SETTINGS_README.md"))
|
||||
.arg(settingsPath));
|
||||
parser.addHelpOption();
|
||||
@ -461,7 +462,14 @@ void logSystemAndConfig()
|
||||
for (const auto &line : globalInfo.split("\n")) {
|
||||
QLOG_INFO() << line;
|
||||
}
|
||||
QLOG_INFO() << "Libraries: " << DBHelper::getLibraries().getLibraries();
|
||||
|
||||
auto libraries = DBHelper::getLibraries().getLibraries();
|
||||
QLOG_INFO() << "Libraries: ";
|
||||
for (auto library : libraries) {
|
||||
QLOG_INFO() << " " << library;
|
||||
auto access = DataBaseManagement::getDatabaseAccess(library.getPath());
|
||||
QLOG_INFO() << " > STATUS: " << access;
|
||||
}
|
||||
QLOG_INFO() << "--------------------------------------------";
|
||||
}
|
||||
|
||||
|
@ -18,10 +18,15 @@ jobs:
|
||||
pool:
|
||||
vmImage: 'windows-2019'
|
||||
steps:
|
||||
- task: UsePythonVersion@0
|
||||
inputs:
|
||||
versionSpec: '3.10'
|
||||
architecture: 'x64'
|
||||
- script: |
|
||||
pip install -U pip
|
||||
pip install aqtinstall
|
||||
mkdir C:\Qt
|
||||
python -m aqt install -O c:\Qt ${{ parameters.qt_version }} windows desktop ${{ parameters.qt_aqt_spec }} -m qt5compat qtmultimedia qtimageformats
|
||||
python -m aqt install-qt windows desktop ${{ parameters.qt_version }} ${{ parameters.qt_aqt_spec }} -O c:\Qt -m qt5compat qtmultimedia qtimageformats
|
||||
dir C:\Qt\${{ parameters.qt_version }}\${{ parameters.qt_spec }}\bin
|
||||
choco install -y wget
|
||||
choco install innosetup
|
||||
|
@ -18,10 +18,15 @@ jobs:
|
||||
pool:
|
||||
vmImage: 'windows-2019'
|
||||
steps:
|
||||
- task: UsePythonVersion@0
|
||||
inputs:
|
||||
versionSpec: '3.10'
|
||||
architecture: 'x64'
|
||||
- script: |
|
||||
pip install -U pip
|
||||
pip install aqtinstall
|
||||
mkdir C:\Qt
|
||||
python -m aqt install -O c:\Qt ${{ parameters.qt_version }} windows desktop ${{ parameters.qt_aqt_spec }}
|
||||
python -m aqt install-qt windows desktop ${{ parameters.qt_version }} ${{ parameters.qt_aqt_spec }} -O c:\Qt
|
||||
dir C:\Qt\${{ parameters.qt_version }}\${{ parameters.qt_spec }}\bin
|
||||
choco install -y wget
|
||||
choco install innosetup
|
||||
|
@ -404,12 +404,7 @@ ComicInfo &ComicInfo::operator=(const ComicInfo &comicInfo)
|
||||
|
||||
QPixmap ComicInfo::getCover(const QString &basePath)
|
||||
{
|
||||
if (cover.isNull()) {
|
||||
cover.load(basePath + "/.yacreaderlibrary/covers/" + hash + ".jpg");
|
||||
}
|
||||
QPixmap c;
|
||||
c.convertFromImage(cover);
|
||||
return c;
|
||||
return QPixmap(YACReader::LibraryPaths::coverPath(basePath, hash));
|
||||
}
|
||||
|
||||
QStringList ComicInfo::getWriters()
|
||||
|
@ -84,8 +84,6 @@ public:
|
||||
|
||||
QVariant comicVineID; // string
|
||||
|
||||
QImage cover;
|
||||
|
||||
QVariant lastTimeOpened; // integer/date
|
||||
QVariant coverSizeRatio; // h/w
|
||||
QVariant originalCoverSize; // string "WxH"
|
||||
@ -189,8 +187,6 @@ public:
|
||||
|
||||
Q_PROPERTY(QVariant comicVineID MEMBER comicVineID CONSTANT)
|
||||
|
||||
Q_PROPERTY(QImage cover MEMBER cover CONSTANT)
|
||||
|
||||
Q_PROPERTY(QVariant lastTimeOpened MEMBER lastTimeOpened CONSTANT)
|
||||
|
||||
Q_PROPERTY(QVariant coverSizeRatio MEMBER coverSizeRatio CONSTANT)
|
||||
|
18
common/cover_utils.cpp
Normal file
18
common/cover_utils.cpp
Normal file
@ -0,0 +1,18 @@
|
||||
#include "cover_utils.h"
|
||||
|
||||
bool YACReader::saveCover(const QString &path, const QImage &cover)
|
||||
{
|
||||
QImage scaled;
|
||||
if (cover.width() > cover.height()) {
|
||||
scaled = cover.scaledToWidth(640, Qt::SmoothTransformation);
|
||||
} else {
|
||||
auto aspectRatio = static_cast<double>(cover.width()) / static_cast<double>(cover.height());
|
||||
auto maxAllowedAspectRatio = 0.5;
|
||||
if (aspectRatio < maxAllowedAspectRatio) { // cover is too tall, e.g. webtoon
|
||||
scaled = cover.scaledToHeight(960, Qt::SmoothTransformation);
|
||||
} else {
|
||||
scaled = cover.scaledToWidth(480, Qt::SmoothTransformation);
|
||||
}
|
||||
}
|
||||
return scaled.save(path, 0, 75);
|
||||
}
|
9
common/cover_utils.h
Normal file
9
common/cover_utils.h
Normal file
@ -0,0 +1,9 @@
|
||||
#ifndef COVER_UTILS_H
|
||||
#define COVER_UTILS_H
|
||||
|
||||
#include <QImage>
|
||||
|
||||
namespace YACReader {
|
||||
bool saveCover(const QString &path, const QImage &image);
|
||||
}
|
||||
#endif // COVER_UTILS_H
|
@ -5,10 +5,11 @@
|
||||
#include <QDataStream>
|
||||
#include <QMetaType>
|
||||
#include <QAbstractItemModel>
|
||||
#include <QDir>
|
||||
|
||||
class QLibrary;
|
||||
|
||||
#define VERSION "9.15.0"
|
||||
#define VERSION "9.15.1"
|
||||
|
||||
// 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.
|
||||
@ -108,7 +109,77 @@ void iterate(const QModelIndex &index,
|
||||
const QAbstractItemModel *model,
|
||||
const std::function<bool(const QModelIndex &)> &iteration);
|
||||
|
||||
}
|
||||
// TODO: remove all the dataPath variants and always use the root folder of a library `libraryPath` to get all the paths.
|
||||
struct LibraryPaths {
|
||||
LibraryPaths() = delete; // Prevent instantiation
|
||||
|
||||
static QString libraryDataPath(const QString &libraryPath) // libraryPath + /.yacreaderlibrary
|
||||
{
|
||||
return QDir(libraryPath).filePath(".yacreaderlibrary");
|
||||
}
|
||||
|
||||
static QString libraryDatabasePath(const QString &libraryPath) // libraryPath + /.yacreaderlibrary/library.ydb
|
||||
{
|
||||
return QDir(libraryDataPath(libraryPath)).filePath("library.ydb");
|
||||
}
|
||||
|
||||
static QString libraryCoversFolderPath(const QString &libraryPath) // libraryPath + /.yacreaderlibrary/covers
|
||||
{
|
||||
return QDir(libraryDataPath(libraryPath)).filePath("covers");
|
||||
}
|
||||
|
||||
static QString libraryCoversPathFromLibraryDataPath(const QString &libraryDataPath) // libraryDataPath + /covers
|
||||
{
|
||||
return QDir(libraryDataPath).filePath("covers");
|
||||
}
|
||||
|
||||
static QString coverPath(const QString &libraryPath, const QString &hash) // libraryPath + /.yacreaderlibrary/covers/hash + .jpg
|
||||
{
|
||||
return QDir(libraryCoversFolderPath(libraryPath)).filePath(coverFileName(hash));
|
||||
}
|
||||
|
||||
static QString libraryCustomFoldersCoverPath(const QString &libraryPath) // libraryPath + /.yacreaderlibrary/covers/folders
|
||||
{
|
||||
return QDir(libraryCoversFolderPath(libraryPath)).filePath("folders");
|
||||
}
|
||||
|
||||
static QString libraryCustomFoldersCoverPathFromLibraryDataPath(const QString &libraryDataPath)
|
||||
{
|
||||
return QDir(libraryCoversPathFromLibraryDataPath(libraryDataPath)).filePath("folders");
|
||||
}
|
||||
|
||||
static QString customFolderCoverPath(const QString &libraryPath, const QString &folderId)
|
||||
{
|
||||
return QDir(libraryCustomFoldersCoverPath(libraryPath)).filePath(coverFileName(folderId));
|
||||
}
|
||||
|
||||
static QString customFolderCoverPathFromDataPath(const QString &libraryDataPath, const QString &folderId)
|
||||
{
|
||||
return QDir(libraryCustomFoldersCoverPathFromLibraryDataPath(libraryDataPath)).filePath(coverFileName(folderId));
|
||||
}
|
||||
|
||||
static QString coverPathFromLibraryDataPath(const QString &libraryDataPath, const QString &hash) // libraryDataPath + /covers/hash + .jpg
|
||||
{
|
||||
return QDir(libraryCoversPathFromLibraryDataPath(libraryDataPath)).filePath(coverFileName(hash));
|
||||
}
|
||||
|
||||
static QString coverFileName(const QString &id) // id + .jpg (it can be a comic hash or a folder id)
|
||||
{
|
||||
return id + ".jpg";
|
||||
}
|
||||
|
||||
static QString coverPathWithFileName(const QString &libraryPath, const QString &fileName) // libraryPath + /.yacreaderlibrary/covers/hash + fileName
|
||||
{
|
||||
return QDir(libraryCoversFolderPath(libraryPath)).filePath(fileName);
|
||||
}
|
||||
|
||||
static QString idPath(const QString &libraryPath) // libraryPath + /.yacreaderlibrary/id
|
||||
{
|
||||
return QDir(libraryDataPath(libraryPath)).filePath("id");
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace YACReader
|
||||
|
||||
Q_DECLARE_METATYPE(YACReader::OpenComicSource::Source)
|
||||
Q_DECLARE_METATYPE(YACReader::OpenComicSource)
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
#include <QtCore>
|
||||
#include <QAction>
|
||||
#include <QImageReader>
|
||||
#include <QFileDialog>
|
||||
|
||||
using namespace YACReader;
|
||||
|
||||
@ -99,3 +101,47 @@ QPixmap YACReader::hdpiPixmap(const QString &file, QSize size)
|
||||
{
|
||||
return QIcon(file).pixmap(size);
|
||||
}
|
||||
|
||||
QString YACReader::imageFileLoader(QWidget *parent)
|
||||
{
|
||||
QString supportedImageFormatsString;
|
||||
for (const QByteArray &format : QImageReader::supportedImageFormats()) {
|
||||
supportedImageFormatsString += QString("*.%1 ").arg(QString(format));
|
||||
}
|
||||
|
||||
return QFileDialog::getOpenFileName(parent, QObject::tr("Select custom cover"), QDir::homePath(), QObject::tr("Images (%1)").arg(supportedImageFormatsString));
|
||||
}
|
||||
|
||||
QString YACReader::imagePathFromMimeData(const QMimeData *mimeData)
|
||||
{
|
||||
QString filePath;
|
||||
|
||||
if (mimeData->hasUrls()) {
|
||||
QList<QUrl> urlList = mimeData->urls();
|
||||
|
||||
if (!urlList.isEmpty()) {
|
||||
QUrl url = urlList.first();
|
||||
if (url.isLocalFile()) {
|
||||
filePath = url.toLocalFile();
|
||||
|
||||
QFileInfo fileInfo(filePath);
|
||||
QString extension = fileInfo.suffix().toLower();
|
||||
QList<QByteArray> supportedFormats = QImageReader::supportedImageFormats();
|
||||
bool isSupported = false;
|
||||
|
||||
for (const QByteArray &format : supportedFormats) {
|
||||
if (extension == QString(format).toLower()) {
|
||||
isSupported = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isSupported) {
|
||||
filePath.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return filePath;
|
||||
}
|
||||
|
@ -36,6 +36,7 @@
|
||||
#define DO_NOT_TURN_PAGE_ON_SCROLL "DO_NOT_TURN_PAGE_ON_SCROLL"
|
||||
#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 FLOW_TYPE_GL "FLOW_TYPE_GL"
|
||||
#define Y_POSITION "Y_POSITION"
|
||||
@ -97,14 +98,6 @@ enum ComicsViewStatus {
|
||||
Info
|
||||
};
|
||||
|
||||
enum FitMode {
|
||||
ToWidth = 0x01,
|
||||
ToHeight = 0x02,
|
||||
FullRes = 0x03,
|
||||
FullPage = 0x04 //,
|
||||
// Text=0x05
|
||||
};
|
||||
|
||||
enum LibraryUITheme {
|
||||
Light,
|
||||
Dark
|
||||
@ -119,6 +112,7 @@ QString addExtensionToIconPath(const QString &path);
|
||||
QString addExtensionToIconPathInToolbar(const QString &path);
|
||||
QAction *actionWithCustomIcon(const QIcon &icon, QAction *action);
|
||||
QPixmap hdpiPixmap(const QString &file, QSize size);
|
||||
|
||||
QString imageFileLoader(QWidget *parent);
|
||||
QString imagePathFromMimeData(const QMimeData *mimeData);
|
||||
}
|
||||
#endif
|
||||
|
@ -21,7 +21,8 @@ HEADERS += \
|
||||
$$PWD/yacreader_library_list_widget.h \
|
||||
$$PWD/yacreader_library_item_widget.h \
|
||||
$$PWD/yacreader_treeview.h \
|
||||
$$PWD/yacreader_busy_widget.h
|
||||
$$PWD/yacreader_busy_widget.h \
|
||||
$$PWD/yacreader_cover_label.h
|
||||
!CONFIG(no_opengl){
|
||||
HEADERS += $$PWD/yacreader_gl_flow_config_widget.h
|
||||
}
|
||||
@ -50,8 +51,8 @@ SOURCES += \
|
||||
$$PWD/yacreader_library_list_widget.cpp \
|
||||
$$PWD/yacreader_library_item_widget.cpp \
|
||||
$$PWD/yacreader_treeview.cpp \
|
||||
$$PWD/yacreader_busy_widget.cpp
|
||||
|
||||
$$PWD/yacreader_busy_widget.cpp \
|
||||
$$PWD/yacreader_cover_label.cpp
|
||||
!CONFIG(no_opengl){
|
||||
SOURCES += $$PWD/yacreader_gl_flow_config_widget.cpp
|
||||
}
|
||||
|
@ -54,6 +54,7 @@ YACReader::WhatsNewDialog::WhatsNewDialog(QWidget *parent)
|
||||
" • Bump PDF render size<br/>"
|
||||
" • Fix trackpad scrolling, it makes using trackpads more responsive and natural<br/>"
|
||||
" • Added more info to Help -> System info<br/>"
|
||||
" • Don't use scroll animations on macos by default, it where hdpi scroll is most likely to be used and it causes scroll issues. (new 9.15.1)<br/>"
|
||||
"<br/>"
|
||||
"<span style=\"font-weight:600\">YACReaderLibrary</span><br/>"
|
||||
" • Fix headers in the table view getting stuck in a non-movable state<br/>"
|
||||
@ -69,6 +70,8 @@ YACReader::WhatsNewDialog::WhatsNewDialog(QWidget *parent)
|
||||
" • Fix occasional crashes when using automatic library updates<br/>"
|
||||
" • Add setting to hide the \"Continue Reading...\" banner from the home view<br/>"
|
||||
" • Improve Grid and Flow Info comics view scroll performance<br/>"
|
||||
" • Improve flexibility of the open comic in third party app setting so more complex commands can be used. e.g. `open -a \"/Applications/My Reader.app"
|
||||
"{comic_file_path}\"` (new 9.15.1)<br/>"
|
||||
"<br/>"
|
||||
"<span style=\"font-weight:600\">YACReaderLibraryServer</span><br/>"
|
||||
" • New command --system-info to print information about the execution environment and available resources (including what image formats are supported and what libraries are used by the app).<br/>"
|
||||
|
31
custom_widgets/yacreader_cover_label.cpp
Normal file
31
custom_widgets/yacreader_cover_label.cpp
Normal file
@ -0,0 +1,31 @@
|
||||
#include "yacreader_cover_label.h"
|
||||
#include "yacreader_global_gui.h"
|
||||
|
||||
YACReader::CoverLabel::CoverLabel(QWidget *parent)
|
||||
: QLabel(parent)
|
||||
{
|
||||
setAcceptDrops(true);
|
||||
}
|
||||
|
||||
void YACReader::CoverLabel::dragEnterEvent(QDragEnterEvent *event)
|
||||
{
|
||||
if (event->mimeData()->hasUrls() && !YACReader::imagePathFromMimeData(event->mimeData()).isEmpty()) {
|
||||
event->acceptProposedAction();
|
||||
}
|
||||
}
|
||||
|
||||
void YACReader::CoverLabel::dragMoveEvent(QDragMoveEvent *event)
|
||||
{
|
||||
if (event->mimeData()->hasUrls()) {
|
||||
event->acceptProposedAction();
|
||||
}
|
||||
}
|
||||
|
||||
void YACReader::CoverLabel::dropEvent(QDropEvent *event)
|
||||
{
|
||||
QString path = YACReader::imagePathFromMimeData(event->mimeData());
|
||||
if (!path.isEmpty()) {
|
||||
emit imageDropped(path);
|
||||
event->acceptProposedAction();
|
||||
}
|
||||
}
|
29
custom_widgets/yacreader_cover_label.h
Normal file
29
custom_widgets/yacreader_cover_label.h
Normal file
@ -0,0 +1,29 @@
|
||||
#ifndef DROP_LABEL_H
|
||||
#define DROP_LABEL_H
|
||||
|
||||
#include <QLabel>
|
||||
#include <QDragEnterEvent>
|
||||
#include <QDropEvent>
|
||||
#include <QMimeData>
|
||||
|
||||
namespace YACReader {
|
||||
|
||||
class CoverLabel : public QLabel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit CoverLabel(QWidget *parent = nullptr);
|
||||
|
||||
protected:
|
||||
void dragEnterEvent(QDragEnterEvent *event) override;
|
||||
void dragMoveEvent(QDragMoveEvent *event) override;
|
||||
void dropEvent(QDropEvent *event) override;
|
||||
|
||||
signals:
|
||||
void imageDropped(const QString &path);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // DROP_LABEL_H
|
@ -99,6 +99,7 @@ class YACReaderMacOSXSearchLineEdit : public YACReaderSearchLineEdit
|
||||
|
||||
class YACReaderMacOSXToolbar : public YACReaderMainToolBar
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit YACReaderMacOSXToolbar(QWidget *parent = 0);
|
||||
QSize sizeHint() const override;
|
||||
@ -109,20 +110,41 @@ public:
|
||||
void updateViewSelectorIcon(const QIcon &icon);
|
||||
void attachToWindow(QMainWindow *window);
|
||||
|
||||
void *getSearchEditDelegate() { return searchEditDelegate; };
|
||||
|
||||
void emitFilterChange(const QString &filter) { emit filterChanged(filter); };
|
||||
|
||||
QAction *actionFromIdentifier(const QString &identifier);
|
||||
signals:
|
||||
void filterChanged(QString);
|
||||
|
||||
private:
|
||||
void paintEvent(QPaintEvent *) override;
|
||||
|
||||
void *searchEditDelegate;
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
#include <QtWidgets>
|
||||
|
||||
class YACReaderMacOSXToolbar : public QToolBar
|
||||
class YACReaderMacOSXToolbar : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit YACReaderMacOSXToolbar(QWidget *parent = 0);
|
||||
void attachToWindow(QMainWindow *window);
|
||||
void addStretch();
|
||||
|
||||
void setMovable(bool movable) { };
|
||||
void addSeparator() { };
|
||||
|
||||
void setIconSize(const QSize &size) { };
|
||||
|
||||
public slots:
|
||||
void setHidden(bool hidden);
|
||||
void show();
|
||||
void hide();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -398,8 +398,294 @@ void MacToolBarItemWrapper::updateIcon(bool enabled)
|
||||
}
|
||||
#else
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
NSImage *QIconToNSImage(const QIcon &icon, const QSize &size, const QColor &color = QColor())
|
||||
{
|
||||
QPixmap pixmap = icon.pixmap(size);
|
||||
QImage qImage = pixmap.toImage().convertToFormat(QImage::Format_RGBA8888);
|
||||
|
||||
if (color.isValid()) {
|
||||
QPainter p;
|
||||
|
||||
QImage mask(qImage);
|
||||
|
||||
p.begin(&mask);
|
||||
p.setCompositionMode(QPainter::CompositionMode_SourceIn);
|
||||
QBrush brush(color);
|
||||
p.fillRect(QRect(0, 0, size.width(), size.height()), brush);
|
||||
p.end();
|
||||
|
||||
p.begin(&qImage);
|
||||
p.setCompositionMode(QPainter::CompositionMode_Overlay);
|
||||
p.drawImage(0, 0, mask);
|
||||
p.end();
|
||||
}
|
||||
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
CGContextRef context = CGBitmapContextCreate(
|
||||
(void *)qImage.bits(),
|
||||
qImage.width(),
|
||||
qImage.height(),
|
||||
8,
|
||||
qImage.bytesPerLine(),
|
||||
colorSpace,
|
||||
kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
|
||||
|
||||
CGImageRef cgImage = CGBitmapContextCreateImage(context);
|
||||
NSImage *nsImage = [[NSImage alloc] initWithCGImage:cgImage size:NSMakeSize(qImage.width(), qImage.height())];
|
||||
|
||||
// Clean up
|
||||
CGImageRelease(cgImage);
|
||||
CGContextRelease(context);
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
|
||||
return nsImage;
|
||||
}
|
||||
|
||||
void bindActionToNSToolbarItem(QAction *action, NSToolbarItem *toolbarItem, const QColor &iconColor = QColor())
|
||||
{
|
||||
if (action == nullptr || toolbarItem == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto update = [=] {
|
||||
toolbarItem.enabled = action->isEnabled();
|
||||
|
||||
QString text = action->text();
|
||||
QString tooltip = action->toolTip();
|
||||
|
||||
toolbarItem.label = text.isEmpty() ? @"" : [NSString stringWithUTF8String:text.toUtf8().constData()];
|
||||
toolbarItem.paletteLabel = toolbarItem.label;
|
||||
toolbarItem.toolTip = tooltip.isEmpty() ? @"" : [NSString stringWithUTF8String:tooltip.toUtf8().constData()];
|
||||
|
||||
QIcon icon = action->icon();
|
||||
|
||||
__auto_type image = QIconToNSImage(icon, { 24, 24 }, iconColor);
|
||||
|
||||
if (action->isChecked()) {
|
||||
NSSize size = image.size;
|
||||
NSImage *decoratedImage = [[NSImage alloc] initWithSize:size];
|
||||
|
||||
[decoratedImage lockFocus];
|
||||
|
||||
NSRect rect = NSMakeRect(0, 0, size.width, size.height);
|
||||
NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:rect xRadius:8 yRadius:8];
|
||||
[[NSColor colorWithCalibratedRed:0.8 green:0.8 blue:0.8 alpha:0.9] setFill];
|
||||
[path fill];
|
||||
|
||||
NSRect imageRect = NSMakeRect(4, 4, size.width - 8, size.height - 8);
|
||||
[image drawInRect:imageRect
|
||||
fromRect:NSZeroRect
|
||||
operation:NSCompositingOperationSourceOver
|
||||
fraction:1.0];
|
||||
|
||||
[decoratedImage unlockFocus];
|
||||
|
||||
toolbarItem.image = decoratedImage;
|
||||
} else {
|
||||
NSSize size = image.size;
|
||||
NSImage *decoratedImage = [[NSImage alloc] initWithSize:size];
|
||||
|
||||
[decoratedImage lockFocus];
|
||||
|
||||
NSRect imageRect = NSMakeRect(4, 4, size.width - 8, size.height - 8);
|
||||
[image drawInRect:imageRect
|
||||
fromRect:NSZeroRect
|
||||
operation:NSCompositingOperationSourceOver
|
||||
fraction:1.0];
|
||||
|
||||
[decoratedImage unlockFocus];
|
||||
|
||||
toolbarItem.image = decoratedImage;
|
||||
}
|
||||
|
||||
[image release];
|
||||
};
|
||||
|
||||
if (action->isCheckable()) {
|
||||
QObject::connect(
|
||||
action, &QAction::triggered,
|
||||
[=](bool checked) {
|
||||
update();
|
||||
});
|
||||
}
|
||||
|
||||
QObject::connect(
|
||||
action, &QAction::enabledChanged,
|
||||
[=](bool enabled) {
|
||||
toolbarItem.enabled = enabled;
|
||||
});
|
||||
|
||||
QObject::connect(
|
||||
action, &QAction::changed,
|
||||
[=]() {
|
||||
update();
|
||||
});
|
||||
|
||||
toolbarItem.bordered = YES;
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
#ifdef YACREADER_LIBRARY
|
||||
|
||||
@interface YACReaderLibraryToolbarDelegate : NSObject <NSToolbarDelegate> {
|
||||
@public
|
||||
YACReaderMacOSXToolbar *mytoolbar;
|
||||
}
|
||||
|
||||
- (IBAction)itemClicked:(id)sender;
|
||||
|
||||
@end
|
||||
|
||||
@implementation YACReaderLibraryToolbarDelegate
|
||||
|
||||
- (NSArray<NSToolbarItemIdentifier> *)toolbarDefaultItemIdentifiers:(NSToolbar *)toolbar
|
||||
{
|
||||
Q_UNUSED(toolbar);
|
||||
|
||||
return @[
|
||||
@"Back",
|
||||
@"Forward",
|
||||
@"Settings",
|
||||
@"Server",
|
||||
@"Help",
|
||||
NSToolbarSpaceItemIdentifier,
|
||||
@"ToggleView",
|
||||
NSToolbarSpaceItemIdentifier,
|
||||
@"Search",
|
||||
];
|
||||
}
|
||||
|
||||
- (NSArray<NSToolbarItemIdentifier> *)toolbarAllowedItemIdentifiers:(NSToolbar *)toolbar
|
||||
{
|
||||
Q_UNUSED(toolbar);
|
||||
|
||||
return @[
|
||||
@"Back",
|
||||
@"Forward",
|
||||
@"Settings",
|
||||
@"Server",
|
||||
@"Help",
|
||||
@"ToggleView",
|
||||
@"Search",
|
||||
NSToolbarSpaceItemIdentifier,
|
||||
];
|
||||
}
|
||||
|
||||
/*
|
||||
- (NSArray *)toolbarSelectableItemIdentifiers: (NSToolbar *)toolbar
|
||||
{
|
||||
Q_UNUSED(toolbar);
|
||||
|
||||
NSMutableArray *array = [[NSMutableArray alloc] init];
|
||||
|
||||
QList<QMacToolBarItem *> items = mytoolbar->items();
|
||||
foreach (const QMacToolBarItem * item, items) {
|
||||
[array addObject : item->nativeToolBarItem().itemIdentifier];
|
||||
}
|
||||
return array;
|
||||
//NSMutableArray *array = toolbarPrivate->getItemIdentifiers(toolbarPrivate->items, true);
|
||||
//[array addObjectsFromArray:toolbarPrivate->getItemIdentifiers(toolbarPrivate->allowedItems, true)];
|
||||
//return array;
|
||||
}*/
|
||||
|
||||
- (IBAction)itemClicked:(id)sender
|
||||
{
|
||||
NSToolbarItem *item = reinterpret_cast<NSToolbarItem *>(sender);
|
||||
QString identifier = QString::fromNSString([item itemIdentifier]);
|
||||
|
||||
QAction *action = mytoolbar->actionFromIdentifier(identifier);
|
||||
;
|
||||
|
||||
if (action != nullptr) {
|
||||
action->trigger();
|
||||
}
|
||||
}
|
||||
|
||||
- (NSToolbarItem *)toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL)willBeInserted
|
||||
{
|
||||
Q_UNUSED(toolbar);
|
||||
Q_UNUSED(willBeInserted);
|
||||
|
||||
QString identifier = QString::fromNSString(itemIdentifier);
|
||||
|
||||
if (identifier == "Search") {
|
||||
NSSearchToolbarItem *searchItem = [[NSSearchToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
|
||||
|
||||
searchItem.resignsFirstResponderWithCancel = true;
|
||||
searchItem.searchField.delegate = id<NSSearchFieldDelegate>(mytoolbar->getSearchEditDelegate());
|
||||
searchItem.toolTip = @"Search";
|
||||
|
||||
return searchItem;
|
||||
}
|
||||
|
||||
NSToolbarItem *toolbarItem = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
|
||||
|
||||
toolbarItem.target = self;
|
||||
toolbarItem.action = @selector(itemClicked:);
|
||||
|
||||
QAction *action = mytoolbar->actionFromIdentifier(identifier);
|
||||
|
||||
if (identifier == "Back") {
|
||||
toolbarItem.navigational = YES;
|
||||
} else if (identifier == "Forward") {
|
||||
toolbarItem.navigational = YES;
|
||||
}
|
||||
|
||||
bindActionToNSToolbarItem(action, toolbarItem);
|
||||
|
||||
return toolbarItem;
|
||||
}
|
||||
|
||||
- (BOOL)validateToolbarItem:(NSToolbarItem *)item
|
||||
{
|
||||
|
||||
QString identifier = QString::fromNSString([item itemIdentifier]);
|
||||
|
||||
if (identifier == "Search") {
|
||||
return YES;
|
||||
}
|
||||
|
||||
QAction *action = mytoolbar->actionFromIdentifier(identifier);
|
||||
|
||||
if (action == nullptr) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
return action->isEnabled();
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface YACReaderLibrarySearchDelegate : NSObject <NSSearchFieldDelegate> {
|
||||
@public
|
||||
YACReaderMacOSXToolbar *mytoolbar;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation YACReaderLibrarySearchDelegate
|
||||
|
||||
- (void)searchFieldDidStartSearching:(NSSearchField *)sender
|
||||
{
|
||||
}
|
||||
- (void)searchFieldDidEndSearching:(NSSearchField *)sender
|
||||
{
|
||||
[sender resignFirstResponder];
|
||||
}
|
||||
- (void)controlTextDidChange:(NSNotification *)notification
|
||||
{
|
||||
NSSearchField *searchField = notification.object;
|
||||
NSLog(@"Search text changed: %@", searchField.stringValue);
|
||||
|
||||
mytoolbar->emitFilterChange(QString::fromNSString(searchField.stringValue));
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
YACReaderMacOSXToolbar::YACReaderMacOSXToolbar(QWidget *parent)
|
||||
: YACReaderMainToolBar(parent)
|
||||
{
|
||||
@ -467,22 +753,159 @@ void YACReaderMacOSXToolbar::updateViewSelectorIcon(const QIcon &icon)
|
||||
|
||||
void YACReaderMacOSXToolbar::attachToWindow(QMainWindow *window)
|
||||
{
|
||||
auto toolbar = new QToolBar();
|
||||
NSView *nsview = (NSView *)window->winId();
|
||||
NSWindow *nswindow = [nsview window];
|
||||
|
||||
toolbar->addWidget(this);
|
||||
toolbar->setMovable(false);
|
||||
YACReaderLibrarySearchDelegate *searchDelegate = [[YACReaderLibrarySearchDelegate alloc] init];
|
||||
this->searchEditDelegate = searchDelegate;
|
||||
searchDelegate->mytoolbar = this;
|
||||
|
||||
window->addToolBar(toolbar);
|
||||
// Create the NSToolbar
|
||||
NSToolbar *toolbar = [[NSToolbar alloc] initWithIdentifier:@"mainToolbar"];
|
||||
[toolbar setDisplayMode:NSToolbarDisplayModeIconOnly];
|
||||
[toolbar setShowsBaselineSeparator:false];
|
||||
|
||||
__auto_type delegate = [[YACReaderLibraryToolbarDelegate alloc] init];
|
||||
delegate->mytoolbar = this;
|
||||
[toolbar setDelegate:delegate];
|
||||
|
||||
[nswindow setToolbar:toolbar];
|
||||
}
|
||||
|
||||
void YACReaderMacOSXToolbar::paintEvent(QPaintEvent *)
|
||||
{
|
||||
}
|
||||
|
||||
QAction *YACReaderMacOSXToolbar::actionFromIdentifier(const QString &identifier)
|
||||
{
|
||||
if (identifier == "Back") {
|
||||
return backButton->defaultAction();
|
||||
} else if (identifier == "Forward") {
|
||||
return forwardButton->defaultAction();
|
||||
} else if (identifier == "Settings") {
|
||||
return settingsButton->defaultAction();
|
||||
} else if (identifier == "Server") {
|
||||
return serverButton->defaultAction();
|
||||
} else if (identifier == "Help") {
|
||||
return helpButton->defaultAction();
|
||||
} else if (identifier == "ToggleView") {
|
||||
return toggleComicsViewButton->defaultAction();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
@interface YACReaderToolbarDelegate : NSObject <NSToolbarDelegate> {
|
||||
@public
|
||||
YACReaderMacOSXToolbar *mytoolbar;
|
||||
}
|
||||
|
||||
- (IBAction)itemClicked:(id)sender;
|
||||
|
||||
@end
|
||||
|
||||
@implementation YACReaderToolbarDelegate
|
||||
|
||||
- (NSArray<NSToolbarItemIdentifier> *)toolbarDefaultItemIdentifiers:(NSToolbar *)toolbar
|
||||
{
|
||||
Q_UNUSED(toolbar);
|
||||
|
||||
auto actions = mytoolbar->actions();
|
||||
NSMutableArray<NSToolbarItemIdentifier> *identifiers = [NSMutableArray arrayWithCapacity:actions.size()];
|
||||
|
||||
for (QAction *action : actions) {
|
||||
[identifiers addObject:[NSString stringWithFormat:@"action_%p", action]];
|
||||
}
|
||||
|
||||
return identifiers;
|
||||
}
|
||||
|
||||
- (NSArray<NSToolbarItemIdentifier> *)toolbarAllowedItemIdentifiers:(NSToolbar *)toolbar
|
||||
{
|
||||
Q_UNUSED(toolbar);
|
||||
|
||||
auto actions = mytoolbar->actions();
|
||||
NSMutableArray<NSToolbarItemIdentifier> *identifiers = [NSMutableArray arrayWithCapacity:actions.size()];
|
||||
|
||||
for (QAction *action : actions) {
|
||||
[identifiers addObject:[NSString stringWithFormat:@"action_%p", action]];
|
||||
}
|
||||
|
||||
return identifiers;
|
||||
}
|
||||
|
||||
// - (NSArray *)toolbarSelectableItemIdentifiers:(NSToolbar *)toolbar
|
||||
// {
|
||||
// Q_UNUSED(toolbar);
|
||||
|
||||
// auto actions = mytoolbar->actions();
|
||||
// NSMutableArray<NSToolbarItemIdentifier> *identifiers = [NSMutableArray arrayWithCapacity:actions.size()];
|
||||
|
||||
// for (QAction *action : actions) {
|
||||
// if (action->isCheckable()) {
|
||||
// [identifiers addObject:[NSString stringWithFormat:@"action_%p", action]];
|
||||
// }
|
||||
// }
|
||||
|
||||
// return identifiers;
|
||||
// }
|
||||
|
||||
- (IBAction)itemClicked:(id)sender
|
||||
{
|
||||
NSToolbarItem *item = reinterpret_cast<NSToolbarItem *>(sender);
|
||||
NSString *itemIdentifier = [item itemIdentifier];
|
||||
|
||||
auto actions = mytoolbar->actions();
|
||||
|
||||
for (QAction *action : actions) {
|
||||
if ([itemIdentifier isEqualTo:[NSString stringWithFormat:@"action_%p", action]]) {
|
||||
action->trigger();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (NSToolbarItem *)toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL)willBeInserted
|
||||
{
|
||||
Q_UNUSED(toolbar);
|
||||
Q_UNUSED(willBeInserted);
|
||||
|
||||
NSToolbarItem *toolbarItem = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
|
||||
|
||||
toolbarItem.target = self;
|
||||
toolbarItem.action = @selector(itemClicked:);
|
||||
|
||||
auto actions = mytoolbar->actions();
|
||||
|
||||
for (QAction *action : actions) {
|
||||
if ([itemIdentifier isEqualTo:[NSString stringWithFormat:@"action_%p", action]]) {
|
||||
bindActionToNSToolbarItem(action, toolbarItem, QColor(200, 200, 200));
|
||||
}
|
||||
}
|
||||
|
||||
return toolbarItem;
|
||||
}
|
||||
|
||||
- (BOOL)validateToolbarItem:(NSToolbarItem *)item
|
||||
{
|
||||
NSString *itemIdentifier = [item itemIdentifier];
|
||||
|
||||
auto actions = mytoolbar->actions();
|
||||
|
||||
for (QAction *action : actions) {
|
||||
if ([itemIdentifier isEqualTo:[NSString stringWithFormat:@"action_%p", action]]) {
|
||||
return action->isEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
YACReaderMacOSXToolbar::YACReaderMacOSXToolbar(QWidget *parent)
|
||||
: QToolBar(parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
setMovable(false);
|
||||
setIconSize(QSize(24, 24));
|
||||
@ -490,14 +913,42 @@ YACReaderMacOSXToolbar::YACReaderMacOSXToolbar(QWidget *parent)
|
||||
|
||||
void YACReaderMacOSXToolbar::attachToWindow(QMainWindow *window)
|
||||
{
|
||||
window->setUnifiedTitleAndToolBarOnMac(true);
|
||||
window->addToolBar(this);
|
||||
NSView *nsview = (NSView *)window->winId();
|
||||
NSWindow *nswindow = [nsview window];
|
||||
|
||||
NSToolbar *toolbar = [[NSToolbar alloc] initWithIdentifier:@"mainToolbar"];
|
||||
[toolbar setDisplayMode:NSToolbarDisplayModeIconOnly];
|
||||
[toolbar setShowsBaselineSeparator:false];
|
||||
|
||||
__auto_type delegate = [[YACReaderToolbarDelegate alloc] init];
|
||||
delegate->mytoolbar = this;
|
||||
[toolbar setDelegate:delegate];
|
||||
|
||||
[nswindow setToolbar:toolbar];
|
||||
}
|
||||
|
||||
void YACReaderMacOSXToolbar::addStretch()
|
||||
{
|
||||
}
|
||||
|
||||
void YACReaderMacOSXToolbar::setHidden(bool hidden)
|
||||
{
|
||||
NSView *nsView = reinterpret_cast<NSView *>(this->winId());
|
||||
NSWindow *window = [nsView window];
|
||||
if (window && window.toolbar) {
|
||||
window.toolbar.visible = !hidden;
|
||||
}
|
||||
}
|
||||
void YACReaderMacOSXToolbar::show()
|
||||
{
|
||||
setHidden(false);
|
||||
}
|
||||
|
||||
void YACReaderMacOSXToolbar::hide()
|
||||
{
|
||||
setHidden(true);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -285,7 +285,7 @@ QWidget *YACReaderRatingDelegate::createEditor(QWidget *parent,
|
||||
void YACReaderRatingDelegate::setEditorData(QWidget *editor,
|
||||
const QModelIndex &index) const
|
||||
{
|
||||
int rating = ((ComicItem *)index.internalPointer())->data(11).toInt();
|
||||
int rating = ((ComicItem *)index.internalPointer())->data(ComicModel::Rating).toInt();
|
||||
|
||||
StarRating starRating(rating);
|
||||
|
||||
|
BIN
images/comic_vine/checkBoxTick.svg
Normal file
BIN
images/comic_vine/checkBoxTick.svg
Normal file
Binary file not shown.
After Width: | Height: | Size: 358 B |
BIN
images/loadCustomCover.svg
Normal file
BIN
images/loadCustomCover.svg
Normal file
Binary file not shown.
After Width: | Height: | Size: 292 B |
BIN
images/resetCover.svg
Normal file
BIN
images/resetCover.svg
Normal file
Binary file not shown.
After Width: | Height: | Size: 434 B |
@ -74,6 +74,8 @@ public:
|
||||
#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 SET_FOLDER_COVER_ACTION_YL "SET_FOLDER_COVER_ACTION_YL"
|
||||
#define DELETE_CUSTOM_FOLDER_COVER_ACTION_YL "DELETE_CUSTOM_FOLDER_COVER_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"
|
||||
|
@ -209,6 +209,7 @@ void waitAndPrint(const ConcurrentQueue &queue, const QueueControlMessagePrinter
|
||||
printer.printEndWaitingMessage();
|
||||
}
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 9, 0)
|
||||
template<typename T, std::size_t size>
|
||||
QDebug operator<<(QDebug debug, const std::array<T, size> &array)
|
||||
{
|
||||
@ -225,6 +226,7 @@ QDebug operator<<(QDebug debug, const std::array<T, size> &array)
|
||||
|
||||
return debug;
|
||||
}
|
||||
#endif
|
||||
|
||||
using RandomEngine = std::mt19937_64;
|
||||
|
||||
|
25
third_party/KDToolBox/KDSignalThrottler.cpp
vendored
25
third_party/KDToolBox/KDSignalThrottler.cpp
vendored
@ -180,4 +180,29 @@ KDSignalLeadingDebouncer::KDSignalLeadingDebouncer(QObject *parent)
|
||||
|
||||
KDSignalLeadingDebouncer::~KDSignalLeadingDebouncer() = default;
|
||||
|
||||
|
||||
KDStringSignalDebouncer::KDStringSignalDebouncer(QObject *parent)
|
||||
: QObject(parent), m_debouncer(KDGenericSignalThrottler::Kind::Debouncer,
|
||||
KDGenericSignalThrottler::EmissionPolicy::Trailing,
|
||||
parent)
|
||||
{
|
||||
connect(&m_debouncer, &KDGenericSignalThrottler::triggered,
|
||||
this, [=] {
|
||||
emit triggered(this->value);
|
||||
});
|
||||
}
|
||||
|
||||
void KDStringSignalDebouncer::setTimeout(int msec) {
|
||||
m_debouncer.setTimeout(msec);
|
||||
}
|
||||
|
||||
int KDStringSignalDebouncer::timeout() const {
|
||||
return m_debouncer.timeout();
|
||||
}
|
||||
|
||||
void KDStringSignalDebouncer::throttle(QString value) {
|
||||
this->value = value;
|
||||
m_debouncer.throttle();
|
||||
}
|
||||
|
||||
} // namespace KDToolBox
|
||||
|
20
third_party/KDToolBox/KDSignalThrottler.h
vendored
20
third_party/KDToolBox/KDSignalThrottler.h
vendored
@ -125,6 +125,26 @@ public:
|
||||
~KDSignalLeadingDebouncer() override;
|
||||
};
|
||||
|
||||
class KDStringSignalDebouncer : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit KDStringSignalDebouncer(QObject *parent = nullptr);
|
||||
|
||||
void setTimeout(int msec);
|
||||
int timeout() const;
|
||||
|
||||
public slots:
|
||||
void throttle(QString value);
|
||||
|
||||
signals:
|
||||
void triggered(QString value);
|
||||
|
||||
private:
|
||||
QString value;
|
||||
KDGenericSignalThrottler m_debouncer;
|
||||
};
|
||||
|
||||
} // namespace KDToolBox
|
||||
|
||||
#endif // KDSIGNALTHROTTLER_H
|
||||
|
Loading…
Reference in New Issue
Block a user