diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 22ae067c7..1c77c5b80 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -6,9 +6,6 @@ on:
- master
- develop
pull_request:
- branches:
- - master
- - develop
env:
IS_ORIGINAL_REPO: ${{ github.repository == 'YACReader/yacreader' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/develop') }}
@@ -143,6 +140,57 @@ jobs:
# macOS Qt6 Universal build
macos-qt6-universal:
name: macOS (Qt6 Universal)
+ runs-on: macos-26
+ needs: [initialization, code-format-validation]
+ steps:
+ - uses: actions/checkout@v6
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v6
+ with:
+ node-version: '18'
+
+ - name: Install dependencies
+ run: |
+ pip3 install --break-system-packages aqtinstall
+ python3 -m aqt install-qt mac desktop 6.12.0 -m qt5compat qtmultimedia qtimageformats qtshadertools qtspeech
+ echo "${{ github.workspace }}/6.12.0/macos/bin" >> $GITHUB_PATH
+ npm install -g appdmg
+
+ - name: Import Code Signing Certificate
+ if: github.repository == 'YACReader/yacreader' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/develop')
+ uses: apple-actions/import-codesign-certs@v2
+ with:
+ p12-file-base64: ${{ secrets.MACOS_CERTIFICATE_P12_BASE64 }}
+ p12-password: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
+
+ - name: Build
+ env:
+ MACOSX_DEPLOYMENT_TARGET: "14.4"
+ run: |
+ VERSION="$(tr -d '\r\n' < VERSION)"
+ SKIP_CODESIGN="${{ env.IS_FORK }}"
+ SKIP_CODESIGN=$(echo "$SKIP_CODESIGN" | tr '[:upper:]' '[:lower:]')
+ ./compileOSX.sh $VERSION ${{ needs.initialization.outputs.build_number }} $SKIP_CODESIGN Qt6 universal
+
+ - name: Run tests
+ run: ctest --test-dir build --output-on-failure
+
+ - name: Notarize
+ if: github.repository == 'YACReader/yacreader' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/develop')
+ run: |
+ xcrun notarytool submit *.dmg --apple-id "${{ secrets.MACOS_APPLE_ID }}" --team-id "${{ secrets.MACOS_TEAM_ID }}" --password "${{ secrets.MACOS_APP_PASSWORD }}" --wait
+ xcrun stapler staple *.dmg
+
+ - name: Upload DMG
+ uses: actions/upload-artifact@v6
+ with:
+ name: macos-qt6-universal-${{ needs.initialization.outputs.build_number }}-dmg
+ path: "*.dmg"
+
+ # macOS Qt6 Universal legacy build (macOS 12+, older Qt)
+ macos-qt6-universal-legacy:
+ name: macOS (Qt6 Universal Legacy)
runs-on: macos-15
needs: [initialization, code-format-validation]
steps:
@@ -170,6 +218,7 @@ jobs:
- name: Build
env:
MACOSX_DEPLOYMENT_TARGET: "12"
+ DMG_NAME_SUFFIX: "-legacy"
run: |
VERSION="$(tr -d '\r\n' < VERSION)"
SKIP_CODESIGN="${{ env.IS_FORK }}"
@@ -188,7 +237,7 @@ jobs:
- name: Upload DMG
uses: actions/upload-artifact@v6
with:
- name: macos-qt6-universal-${{ needs.initialization.outputs.build_number }}-dmg
+ name: macos-qt6-universal-legacy-${{ needs.initialization.outputs.build_number }}-dmg
path: "*.dmg"
# Windows x64 Qt6 build
@@ -555,6 +604,7 @@ jobs:
- linux-qt6
- linux-qt6-7zip
- macos-qt6-universal
+ - macos-qt6-universal-legacy
- windows-x64-qt6
- windows-arm64-qt6
- docker-amd64
@@ -623,6 +673,7 @@ jobs:
- linux-qt6
- linux-qt6-7zip
- macos-qt6-universal
+ - macos-qt6-universal-legacy
- windows-x64-qt6
- windows-arm64-qt6
- docker-amd64
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dab520da9..7f199ea77 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,61 @@
Version counting is based on semantic versioning (Major.Feature.Patch)
+## 10.5.0
+
+### YACReader
+
+* Add Bicubic as a scaling method in Settings, offering sharper images than Bilinear and faster scaling than Lanczos.
+* Fix opening comics at page -1 after removing them from the "Reading" list.
+* Improve toolbar on macOS.
+
+### YACReaderLibrary
+
+* Reading lists and labels keep comics whose files are removed from the library. Their covers show in gray, with their metadata and last known file name. You can still edit their metadata, get data from Comic Vine, set a custom cover, reorder them, and remove them from the list. When the same file comes back, at any path, the comic is available again.
+* Reading lists and labels keep a comic when its file is replaced, for example by a tool that writes ComicInfo.xml into the archive.
+* Import reading lists in CBL XML or JSON format. Preview and adjust matches to comics in your library, and keep missing books in the reading order so you can find them later.
+* Match missing books to library comics individually or across an entire reading list.
+* Export reading lists as CBL XML, JSON, or a web page.
+* Create smart lists that show the current results of a saved search.
+* Organize reading lists and smart lists in folders.
+* Track missing issues in regular folders, using Comic Vine or entering the issue numbers yourself. Edit their metadata and covers, and automatically match newly added files to the missing issues.
+* Include the same comic more than once in a reading list, with each appearance keeping its place in the reading order.
+* Sort search results by series, volume, and issue number.
+* The metadata editor saves only the fields that you change, also when you edit several comics. Comic Vine changes only the fields that it returns.
+* Report failed Comic Vine lookups and incomplete results.
+* Fix removing comics belonging to child reading lists from a parent list view.
+* Fix newly added comics sometimes appearing in the middle of a reading list instead of at the end.
+* Fix library lock handling and add a way to clear these locks from the GUI and webui.
+* Improve library opening speed over network shares.
+* Unify the title and tool bars for a more integrated look (Windows/macOS).
+
+### Server
+
+* Add server API v3 so compatible mobile clients can browse missing comics, reading lists, and smart lists, including their folders, with content ordered as on the desktop. Existing clients remain supported.
+* Fix a security issue that could let cover requests access image files outside the library's covers folder.
+* Fix memory leak updating libraries.
+* Improve covers API performance.
+
+### WebUI
+
+* Browse reading lists, smart lists, their folders, Favorites, tags, and Recent from the library menu.
+* View missing comics in reading lists and folders, including their available metadata and covers.
+* Choose how many days count as recent in this browser's settings.
+* Improve the home page and add a connect device dialog. iOS and Android clients can use now the QR scaner with the webui.
+* Add mouse wheel page navigation to the reader.
+* Add PageUp, PageDown, Home and End keys to the reader.
+* The back button now returns to where you were, keeping the scroll position.
+* Save the reading progress when the tab goes to the background.
+* Show better error messages.
+* Add a favicon.
+* Open read comics on the first page.
+* Go to the next or previous comic from the reader. Comics opened from a list follow the order of that list, all others follow their folder.
+
+### All GUI apps
+
+* Fix the apps sometimes getting stuck in fullscreen mode after a restart. YACReaderLibrary no longer starts in fullscreen mode, and YACReader still does when you close it in fullscreen mode, but you can always leave it.
+
+
## 10.4.0
### YACReader
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d7a1ffc72..702388e8c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -40,6 +40,7 @@ list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
# Compiler options (MSVC flags)
include(cmake/CompilerOptions.cmake)
+include(cmake/SevenZipRuntime.cmake)
# --- Build options ---
@@ -127,6 +128,7 @@ if(BUILD_SERVER_STANDALONE)
)
else()
find_package(Qt6 6.9 REQUIRED COMPONENTS
+ Concurrent
Core
Core5Compat
Gui
@@ -169,6 +171,7 @@ if(NOT BUILD_SERVER_STANDALONE)
endif()
add_subdirectory(YACReaderLibrary/server)
+add_subdirectory(YACReaderLibrary/cbl)
if(NOT BUILD_SERVER_STANDALONE)
add_subdirectory(YACReaderLibrary/comic_vine)
diff --git a/VERSION b/VERSION
index 7400abcdf..fcc198cb8 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-10.4.0
\ No newline at end of file
+10.5.0
\ No newline at end of file
diff --git a/YACReader/CMakeLists.txt b/YACReader/CMakeLists.txt
index 42f512e78..e87209b97 100644
--- a/YACReader/CMakeLists.txt
+++ b/YACReader/CMakeLists.txt
@@ -229,18 +229,22 @@ qt_add_resources(YACReader "yacreader_common_theme_images"
)
# Translations
+set(yacreader_translation_source_targets
+ YACReader
+ comic_backend
+ common_gui
+ custom_widgets_reader
+ shortcuts_reader
+)
qt_add_translations(YACReader
- SOURCE_TARGETS
- YACReader
- comic_backend
- common_gui
- custom_widgets_reader
- shortcuts_reader
+ SOURCE_TARGETS ${yacreader_translation_source_targets}
+ LUPDATE_OPTIONS -no-obsolete
TS_FILES
yacreader_es.ts
yacreader_fr.ts
yacreader_ru.ts
yacreader_pt.ts
+ yacreader_pt_PT.ts
yacreader_nl.ts
yacreader_tr.ts
yacreader_de.ts
@@ -249,9 +253,14 @@ qt_add_translations(YACReader
yacreader_zh_HK.ts
yacreader_it.ts
yacreader_ko.ts
- yacreader_source.ts
yacreader_en.ts
)
+qt_add_lupdate(
+ SOURCE_TARGETS ${yacreader_translation_source_targets}
+ OPTIONS -no-obsolete
+ TS_FILES yacreader_source.ts
+ LUPDATE_TARGET YACReader_source_lupdate
+)
target_link_libraries(YACReader PRIVATE
Qt6::Core
@@ -277,7 +286,7 @@ target_link_libraries(YACReader PRIVATE
if(WIN32)
target_sources(YACReader PRIVATE icon.rc)
if(MSVC)
- target_link_options(YACReader PRIVATE "/MANIFESTINPUT:${PROJECT_SOURCE_DIR}/cmake/windows/yacreader.manifest")
+ target_sources(YACReader PRIVATE ${PROJECT_SOURCE_DIR}/cmake/windows/yacreader.manifest)
endif()
target_link_libraries(YACReader PRIVATE oleaut32 ole32 shell32 user32)
endif()
diff --git a/YACReader/bookmarks_dialog.cpp b/YACReader/bookmarks_dialog.cpp
index 6348d7016..95f9153c1 100644
--- a/YACReader/bookmarks_dialog.cpp
+++ b/YACReader/bookmarks_dialog.cpp
@@ -24,7 +24,7 @@ BookmarksDialog::BookmarksDialog(QWidget *parent)
// bookmarks
auto bookmarksL = new QGridLayout();
- pages.push_back(new QLabel(tr("Lastest Page")));
+ pages.push_back(new QLabel(tr("Latest Page")));
for (int i = 0; i < 3; i++)
pages.push_back(new QLabel("-"));
diff --git a/YACReader/goto_dialog.cpp b/YACReader/goto_dialog.cpp
index d0c5b12ed..dc5383c6d 100644
--- a/YACReader/goto_dialog.cpp
+++ b/YACReader/goto_dialog.cpp
@@ -13,7 +13,7 @@ GoToDialog::GoToDialog(QWidget *parent)
void GoToDialog::setupUI()
{
- textLabel = new QLabel(tr("Page : "));
+ textLabel = new QLabel(tr("Page: "));
pageNumber = new QLineEdit;
v = new QIntValidator(this);
v->setBottom(1);
@@ -37,7 +37,7 @@ void GoToDialog::setupUI()
bottomLayout->addWidget(cancel);
auto mainLayout = new QVBoxLayout;
- mainLayout->addWidget(numPagesLabel = new QLabel(tr("Total pages : ")));
+ mainLayout->addWidget(numPagesLabel = new QLabel(tr("Total pages: ")));
mainLayout->addLayout(topLayout);
mainLayout->addStretch();
mainLayout->addLayout(bottomLayout);
@@ -67,7 +67,7 @@ void GoToDialog::goTo()
void GoToDialog::setNumPages(unsigned int numPages)
{
- numPagesLabel->setText(tr("Total pages : ") + QString::number(numPages));
+ numPagesLabel->setText(tr("Total pages: ") + QString::number(numPages));
v->setTop(numPages);
}
diff --git a/YACReader/goto_flow_toolbar.cpp b/YACReader/goto_flow_toolbar.cpp
index 5905d76b8..668cc3b95 100644
--- a/YACReader/goto_flow_toolbar.cpp
+++ b/YACReader/goto_flow_toolbar.cpp
@@ -29,7 +29,7 @@ GoToFlowToolBar::GoToFlowToolBar(QWidget *parent)
connect(slider, &QSlider::valueChanged, this, &GoToFlowToolBar::setCenter);
connect(slider, &QSlider::valueChanged, this, &GoToFlowToolBar::setPage);
- pageHint = new QLabel("" + tr("Page : ") + "", this);
+ pageHint = new QLabel("" + tr("Page: ") + "", this);
v = new QIntValidator();
v->setBottom(1);
edit = new QLineEdit();
diff --git a/YACReader/main.cpp b/YACReader/main.cpp
index 10c39f04b..384778f45 100644
--- a/YACReader/main.cpp
+++ b/YACReader/main.cpp
@@ -137,6 +137,8 @@ int main(int argc, char *argv[])
QCommandLineOption comicId("comicId", "", "comicId");
QCommandLineOption libraryId("libraryId", "", "libraryId");
QCommandLineOption readingListId("readingListId", "", "readingListId");
+ QCommandLineOption readingListEntryId("readingListEntryId", "", "readingListEntryId");
+ readingListEntryId.setFlags(QCommandLineOption::HiddenFromHelp);
// hide comicId and libraryId from help
comicId.setFlags(QCommandLineOption::HiddenFromHelp);
libraryId.setFlags(QCommandLineOption::HiddenFromHelp);
@@ -146,6 +148,7 @@ int main(int argc, char *argv[])
parser.addOption(comicId);
parser.addOption(libraryId);
parser.addOption(readingListId);
+ parser.addOption(readingListEntryId);
parser.process(app);
QString destLog = YACReader::getSettingsPath() + "/yacreader.log";
@@ -186,7 +189,7 @@ int main(int argc, char *argv[])
OpenComicSource source;
if (parser.isSet(readingListId)) {
- source = OpenComicSource { OpenComicSource::ReadingList, parser.value(readingListId).toULongLong() };
+ source = OpenComicSource { OpenComicSource::ReadingList, parser.value(readingListId).toULongLong(), parser.value(readingListEntryId).toULongLong() };
} else {
source = OpenComicSource { OpenComicSource::Folder, 33 }; // Folder is not needed to get the comic information, the comid id points to a unique comic
}
diff --git a/YACReader/main_window_viewer.cpp b/YACReader/main_window_viewer.cpp
index 7970cece2..80080a525 100644
--- a/YACReader/main_window_viewer.cpp
+++ b/YACReader/main_window_viewer.cpp
@@ -180,7 +180,7 @@ void MainWindowViewer::syncModeActions(bool manga, bool continuousScroll)
}
}
- doubleMangaPageSwitch();
+ refreshPrevNextActions();
}
MainWindowViewer::~MainWindowViewer()
@@ -267,6 +267,10 @@ void MainWindowViewer::setupUI()
move(QPoint((widthDesktopResolution - width) / 2, ((heightDesktopResolution - height) - 40) / 2));
resize(QSize(width, height));
} else {
+ // Only the FULLSCREEN setting starts the viewer in full screen (see toFullScreen() below);
+ // a full screen state in the saved geometry would leave fullscreen out of sync.
+ if (isFullScreen())
+ setWindowState(windowState() & ~Qt::WindowFullScreen);
// Guard against the window landing off-screen when a monitor is unplugged
// between sessions. Qt 6 tries to remap the geometry to the primary screen
// when the saved screen is gone, but the result can still be off-screen.
@@ -516,10 +520,10 @@ void MainWindowViewer::createActions()
doubleMangaPageAction->setData(DOUBLE_MANGA_PAGE_ACTION_Y);
doubleMangaPageAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(DOUBLE_MANGA_PAGE_ACTION_Y));
connect(doubleMangaPageAction, &QAction::triggered, viewer, &Viewer::doubleMangaPageSwitch);
- connect(doubleMangaPageAction, &QAction::triggered, this, &MainWindowViewer::doubleMangaPageSwitch);
+ connect(doubleMangaPageAction, &QAction::triggered, this, &MainWindowViewer::refreshPrevNextActions);
goToPageAction = new QAction(tr("Go To"), this);
- goToPageAction->setToolTip(tr("Go to page ..."));
+ goToPageAction->setToolTip(tr("Go to page..."));
goToPageAction->setData(GO_TO_PAGE_ACTION_Y);
goToPageAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(GO_TO_PAGE_ACTION_Y));
connect(goToPageAction, &QAction::triggered, viewer, &Viewer::showGoToDialog);
@@ -918,6 +922,8 @@ void MainWindowViewer::openComicFromRecentAction(QAction *action)
openFolderFromPath(action->data().toString());
}
} else {
+ // these neighbours belong to the comic shown before, so the reset must not use them
+ previousComicPath = nextComicPath = "";
viewer->resetContent();
viewer->showMessageErrorOpening();
}
@@ -959,17 +965,13 @@ void MainWindowViewer::open(QString path, ComicDB &comic, QList &siblin
QFileInfo fi(path);
currentComicPath = fi.absoluteFilePath();
- if (!comic.info.title.isNull() && !comic.info.title.toString().isEmpty())
- setWindowTitle("YACReader - " + comic.info.title.toString());
- else
- setWindowTitle("YACReader - " + fi.fileName());
+ setWindowTitle("YACReader - " + titleOrFileName(comic.info, fi.fileName()));
applyLibraryReaderMode(comic.info.type.value());
viewer->open(path, comic);
enableActions();
- int index = siblings.indexOf(comic);
- updatePrevNextActions(index > 0, index + 1 < siblings.count());
+ updatePrevNextActions(currentSiblingIndex > 0, currentSiblingIndex >= 0 && currentSiblingIndex + 1 < siblings.count());
optionsDialog->setFilters(currentComicDB.info.brightness, currentComicDB.info.contrast, currentComicDB.info.gamma);
}
@@ -983,16 +985,16 @@ void MainWindowViewer::open(QString path, qint64 comicId, qint64 libraryId, YACR
enableActions();
- currentComicDB.id = comicId;
+ currentComicDB.file.id = ComicId(comicId);
YACReaderLocalClient client;
int tries = 1;
bool success = false;
- while (!(success = client.requestComicInfo(libraryId, currentComicDB, siblingComics, source)) && tries != 0)
+ while (!(success = client.requestComicInfo(libraryId, currentComicDB, siblingComics, currentSiblingIndex, source)) && tries != 0)
tries--;
if (success) {
isClient = true;
- open(path + currentComicDB.path, currentComicDB, siblingComics);
+ open(path + currentComicDB.file.path, currentComicDB, siblingComics);
} else {
isClient = false;
QMessageBox::information(this, "Connection Error", "Unable to connect to YACReaderLibrary");
@@ -1251,6 +1253,7 @@ void MainWindowViewer::toFullScreen()
hideToolBars();
viewer->hide();
viewer->fullscreen = true; // TODO, change by the right use of windowState();
+ geometryBeforeFullScreen = saveGeometry();
setWindowState(Qt::WindowFullScreen);
viewer->show();
if (viewer->magnifyingGlassIsVisible())
@@ -1340,8 +1343,7 @@ void MainWindowViewer::checkNewVersion()
void MainWindowViewer::processReset()
{
disableActions();
- if (!isClient || siblingComics.size() <= 1)
- disablePreviousNextComicActions();
+ refreshPrevNextActions();
}
void MainWindowViewer::setUpShortcutsManagement()
@@ -1350,8 +1352,8 @@ void MainWindowViewer::setUpShortcutsManagement()
QMap> iconMapping;
iconMapping[tr("Comics")] = [](const Theme &t) { return t.shortcutsIcons.comicsIcon; };
iconMapping[tr("General")] = [](const Theme &t) { return t.shortcutsIcons.generalIcon; };
- iconMapping[tr("Magnifiying glass")] = [](const Theme &t) { return t.shortcutsIcons.magnifyingGlassIcon; };
- iconMapping[tr("Page adjustement")] = [](const Theme &t) { return t.shortcutsIcons.pageIcon; };
+ iconMapping[tr("Magnifying glass")] = [](const Theme &t) { return t.shortcutsIcons.magnifyingGlassIcon; };
+ iconMapping[tr("Page adjustment")] = [](const Theme &t) { return t.shortcutsIcons.pageIcon; };
iconMapping[tr("Reading")] = [](const Theme &t) { return t.shortcutsIcons.readingIcon; };
editShortcutsDialog->setGroupIconMapping(iconMapping);
@@ -1416,7 +1418,7 @@ void MainWindowViewer::setUpShortcutsManagement()
zoomInMglassAction, zoomOutMglassAction,
resetMglassAction };
- editShortcutsDialog->addActionsGroup(tr("Magnifiying glass"), theme.shortcutsIcons.magnifyingGlassIcon,
+ editShortcutsDialog->addActionsGroup(tr("Magnifying glass"), theme.shortcutsIcons.magnifyingGlassIcon,
tmpList = QList()
<< showMagnifyingGlassAction
<< mglassActions);
@@ -1427,7 +1429,7 @@ void MainWindowViewer::setUpShortcutsManagement()
CHANGE_FIT_ACTION_Y);
connect(toggleFitToScreenAction, &QAction::triggered, this, &MainWindowViewer::toggleWidthHeight);
- editShortcutsDialog->addActionsGroup(tr("Page adjustement"), theme.shortcutsIcons.pageIcon,
+ editShortcutsDialog->addActionsGroup(tr("Page adjustment"), theme.shortcutsIcons.pageIcon,
tmpList = QList()
<< adjustHeightAction
<< adjustWidthAction
@@ -1519,11 +1521,10 @@ void MainWindowViewer::setUpShortcutsManagement()
ShortcutsManager::getShortcutsManager().registerActions(allActions);
}
-void MainWindowViewer::doubleMangaPageSwitch()
+void MainWindowViewer::refreshPrevNextActions()
{
if (isClient) {
- int index = siblingComics.indexOf(currentComicDB);
- updatePrevNextActions(index > 0, index + 1 < siblingComics.size());
+ updatePrevNextActions(currentSiblingIndex > 0, currentSiblingIndex >= 0 && currentSiblingIndex + 1 < siblingComics.size());
} else {
updatePrevNextActions(!previousComicPath.isEmpty(), !nextComicPath.isEmpty());
}
@@ -1584,10 +1585,9 @@ void MainWindowViewer::closeEvent(QCloseEvent *event)
viewer->save();
Configuration &conf = Configuration::getConfiguration();
- // saveGeometry() encodes the current screen, the maximized/fullscreen state, and
- // the normal (pre-maximize) geometry in one blob, so restoreGeometry() puts the
- // window back on the right monitor in the right state regardless of how it was closed.
- conf.setGeometry(saveGeometry());
+ // In full screen, save the geometry from before it: it keeps the maximized state, which
+ // showFullScreen() clears. Full screen at start comes from the FULLSCREEN setting.
+ conf.setGeometry(fullscreen ? geometryBeforeFullScreen : saveGeometry());
event->accept();
}
@@ -1597,13 +1597,10 @@ void MainWindowViewer::openPreviousComic()
if (!siblingComics.isEmpty() && isClient) {
sendComic();
- int currentIndex = siblingComics.indexOf(currentComicDB);
- if (currentIndex == -1)
- return;
- if (currentIndex - 1 >= 0 && currentIndex - 1 < siblingComics.count()) {
- siblingComics[currentIndex] = currentComicDB; // updated
- currentComicDB = siblingComics.at(currentIndex - 1);
- open(currentDirectory + currentComicDB.path, currentComicDB, siblingComics);
+ if (currentSiblingIndex - 1 >= 0 && currentSiblingIndex - 1 < siblingComics.count()) {
+ storeCurrentComicInSiblings();
+ currentComicDB = siblingComics.at(--currentSiblingIndex);
+ open(currentDirectory + currentComicDB.file.path, currentComicDB, siblingComics);
}
return;
}
@@ -1617,13 +1614,10 @@ void MainWindowViewer::openNextComic()
if (!siblingComics.isEmpty() && isClient) {
sendComic();
- int currentIndex = siblingComics.indexOf(currentComicDB);
- if (currentIndex == -1)
- return;
- if (currentIndex + 1 > 0 && currentIndex + 1 < siblingComics.count()) {
- siblingComics[currentIndex] = currentComicDB; // updated
- currentComicDB = siblingComics.at(currentIndex + 1);
- open(currentDirectory + currentComicDB.path, currentComicDB, siblingComics);
+ if (currentSiblingIndex + 1 > 0 && currentSiblingIndex + 1 < siblingComics.count()) {
+ storeCurrentComicInSiblings();
+ currentComicDB = siblingComics.at(++currentSiblingIndex);
+ open(currentDirectory + currentComicDB.file.path, currentComicDB, siblingComics);
}
return;
@@ -1821,7 +1815,6 @@ void MainWindowViewer::dragEnterEvent(QDragEnterEvent *event)
// accept just text/uri-list mime format
if (event->mimeData()->hasFormat("text/uri-list")) {
event->acceptProposedAction();
- isClient = false;
}
}
@@ -1864,14 +1857,13 @@ void MainWindowViewer::sendComic()
viewer->updateComic(currentComicDB);
- int currentIndex = siblingComics.indexOf(currentComicDB);
- bool sendNextComicInfo = (currentComicDB.info.currentPage == currentComicDB.info.numPages) && (currentIndex + 1 > 0 && currentIndex + 1 < siblingComics.count());
+ bool sendNextComicInfo = (currentComicDB.info.currentPage == currentComicDB.info.numPages) && (currentSiblingIndex + 1 > 0 && currentSiblingIndex + 1 < siblingComics.count());
if (sendNextComicInfo) {
- ComicDB &nextComic = siblingComics[currentIndex + 1];
+ ComicDB &nextComic = siblingComics[currentSiblingIndex + 1];
nextComic.info.hasBeenOpened = true;
int retries = 1;
- while (!client->sendComicInfo(libraryId, currentComicDB, nextComic.id) && retries != 0)
+ while (!client->sendComicInfo(libraryId, currentComicDB, nextComic.file.id.value()) && retries != 0)
retries--;
} else {
int retries = 1;
@@ -1880,6 +1872,15 @@ void MainWindowViewer::sendComic()
}
}
+// the reading state belongs to the comic, so every occurrence gets it
+void MainWindowViewer::storeCurrentComicInSiblings()
+{
+ for (auto &sibling : siblingComics) {
+ if (sibling == currentComicDB)
+ sibling = currentComicDB;
+ }
+}
+
void MainWindowViewer::updatePrevNextActions(bool thereIsPrevious, bool thereIsNext)
{
if (thereIsPrevious) {
diff --git a/YACReader/main_window_viewer.h b/YACReader/main_window_viewer.h
index d960422d6..15a31fd43 100644
--- a/YACReader/main_window_viewer.h
+++ b/YACReader/main_window_viewer.h
@@ -77,7 +77,7 @@ public slots:
void checkNewVersion();
void processReset();
void setUpShortcutsManagement();
- void doubleMangaPageSwitch();
+ void refreshPrevNextActions();
void toggleFitToWidthSlider();
@@ -95,6 +95,7 @@ public slots:
bool fullscreen;
bool toolbars;
bool fromMaximized;
+ QByteArray geometryBeforeFullScreen;
QString currentDirectory;
QString currentComicPath;
@@ -195,6 +196,9 @@ public slots:
ComicDB currentComicDB;
QList siblingComics;
+ // a reading list can have a comic more than once, so the current comic is tracked by position
+ qint32 currentSiblingIndex = -1;
+ void storeCurrentComicInSiblings();
bool isClient;
QString startComicPath;
quint64 libraryId;
diff --git a/YACReader/options_dialog.cpp b/YACReader/options_dialog.cpp
index 3a59a6a27..1b5bfc995 100644
--- a/YACReader/options_dialog.cpp
+++ b/YACReader/options_dialog.cpp
@@ -105,7 +105,7 @@ OptionsDialog::OptionsDialog(QWidget *parent)
connect(clearBackgroundColorButton, &QAbstractButton::clicked, this, &OptionsDialog::clearBackgroundColor);
colorBox->setLayout(colorSelection);
- auto scrollBox = new QGroupBox(tr("Scroll behaviour"));
+ auto scrollBox = new QGroupBox(tr("Scroll behavior"));
auto scrollLayout = new QVBoxLayout;
disableScrollAnimations = new QCheckBox(tr("Disable scroll animations and smooth scrolling"));
@@ -123,7 +123,7 @@ OptionsDialog::OptionsDialog(QWidget *parent)
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."));
+ hotAreasMouseModeRadioButton = new QRadioButton(tr("Click the left or right half of the screen to turn pages."));
mouseModeLayout->addWidget(normalMouseModeRadioButton);
mouseModeLayout->addWidget(leftRightNavigationMouseModeRadioButton);
@@ -275,6 +275,7 @@ OptionsDialog::OptionsDialog(QWidget *parent)
scalingMethodCombo->addItem(tr("Nearest (fast, low quality)"));
scalingMethodCombo->addItem(tr("Bilinear"));
scalingMethodCombo->addItem(tr("Lanczos (better quality)"));
+ scalingMethodCombo->addItem(tr("Bicubic"));
connect(scalingMethodCombo, &QComboBox::currentIndexChanged, this, [this](int index) {
Configuration::getConfiguration().setScalingMethod(static_cast(index));
emit changedImageOptions();
diff --git a/YACReader/themes/builtin_classic.json b/YACReader/themes/builtin_classic.json
index ac1bda11e..4f1eabc0e 100644
--- a/YACReader/themes/builtin_classic.json
+++ b/YACReader/themes/builtin_classic.json
@@ -6,7 +6,7 @@
"editBackgroundColor": "#55000000",
"editBorderColor": "#77000000",
"editTextColor": "#FFFFFF",
- "flowBackgroundColor": "#282828",
+ "flowBackgroundColor": "#191919",
"flowTextColor": "#FFFFFF",
"iconColor": "#FFFFFF",
"labelTextColor": "#FFFFFF",
diff --git a/YACReader/themes/builtin_dark.json b/YACReader/themes/builtin_dark.json
index 40dc2dd2a..2753d3c35 100644
--- a/YACReader/themes/builtin_dark.json
+++ b/YACReader/themes/builtin_dark.json
@@ -6,7 +6,7 @@
"editBackgroundColor": "#55000000",
"editBorderColor": "#77000000",
"editTextColor": "#FFFFFF",
- "flowBackgroundColor": "#282828",
+ "flowBackgroundColor": "#191919",
"flowTextColor": "#FFFFFF",
"iconColor": "#CCCCCC",
"labelTextColor": "#FFFFFF",
diff --git a/YACReader/themes/builtin_dark1.json b/YACReader/themes/builtin_dark1.json
index 23e5d7130..74f8a253c 100644
--- a/YACReader/themes/builtin_dark1.json
+++ b/YACReader/themes/builtin_dark1.json
@@ -6,7 +6,7 @@
"editBackgroundColor": "#AA403E41",
"editBorderColor": "#885B595C",
"editTextColor": "#C1C0C0",
- "flowBackgroundColor": "#221F22",
+ "flowBackgroundColor": "#161416",
"flowTextColor": "#939293",
"iconColor": "#FCFCFA",
"labelTextColor": "#FCFCFA",
diff --git a/YACReader/themes/builtin_dark2.json b/YACReader/themes/builtin_dark2.json
index b3e3a6ace..643eedd96 100644
--- a/YACReader/themes/builtin_dark2.json
+++ b/YACReader/themes/builtin_dark2.json
@@ -6,7 +6,7 @@
"editBackgroundColor": "#AA424450",
"editBorderColor": "#88424450",
"editTextColor": "#EDEDF0",
- "flowBackgroundColor": "#282A36",
+ "flowBackgroundColor": "#1A1B23",
"flowTextColor": "#6C789C",
"iconColor": "#F8F8F2",
"labelTextColor": "#F8F8F2",
diff --git a/YACReader/themes/builtin_dark3.json b/YACReader/themes/builtin_dark3.json
index 1d43cf455..345f1d027 100644
--- a/YACReader/themes/builtin_dark3.json
+++ b/YACReader/themes/builtin_dark3.json
@@ -6,7 +6,7 @@
"editBackgroundColor": "#AA3C3836",
"editBorderColor": "#88504945",
"editTextColor": "#D5C4A1",
- "flowBackgroundColor": "#282828",
+ "flowBackgroundColor": "#191919",
"flowTextColor": "#928374",
"iconColor": "#EBDBB2",
"labelTextColor": "#EBDBB2",
diff --git a/YACReader/themes/builtin_dark4.json b/YACReader/themes/builtin_dark4.json
index 5d98ecc23..e1b6f14d4 100644
--- a/YACReader/themes/builtin_dark4.json
+++ b/YACReader/themes/builtin_dark4.json
@@ -6,7 +6,7 @@
"editBackgroundColor": "#AA434C5E",
"editBorderColor": "#884C566A",
"editTextColor": "#E5E9F0",
- "flowBackgroundColor": "#2E3440",
+ "flowBackgroundColor": "#20242D",
"flowTextColor": "#787D88",
"iconColor": "#ECEFF4",
"labelTextColor": "#ECEFF4",
diff --git a/YACReader/themes/builtin_light.json b/YACReader/themes/builtin_light.json
index 4027bc539..2dc852e9c 100644
--- a/YACReader/themes/builtin_light.json
+++ b/YACReader/themes/builtin_light.json
@@ -6,7 +6,7 @@
"editBackgroundColor": "#22000000",
"editBorderColor": "#33000000",
"editTextColor": "#202020",
- "flowBackgroundColor": "#F6F6F6",
+ "flowBackgroundColor": "#DEDEDE",
"flowTextColor": "#202020",
"iconColor": "#404040",
"labelTextColor": "#202020",
diff --git a/YACReader/themes/builtin_light1.json b/YACReader/themes/builtin_light1.json
index f19822fe3..c239247c7 100644
--- a/YACReader/themes/builtin_light1.json
+++ b/YACReader/themes/builtin_light1.json
@@ -6,7 +6,7 @@
"editBackgroundColor": "#CCF2F4F8",
"editBorderColor": "#66CCD0DA",
"editTextColor": "#4C4F69",
- "flowBackgroundColor": "#E6E9EF",
+ "flowBackgroundColor": "#D0D3D9",
"flowTextColor": "#5C5F77",
"iconColor": "#4C4F69",
"labelTextColor": "#4C4F69",
diff --git a/YACReader/themes/builtin_light2.json b/YACReader/themes/builtin_light2.json
index bac201e04..44a32e06a 100644
--- a/YACReader/themes/builtin_light2.json
+++ b/YACReader/themes/builtin_light2.json
@@ -6,7 +6,7 @@
"editBackgroundColor": "#CCEEF1F8",
"editBorderColor": "#66C4C8DA",
"editTextColor": "#2F334D",
- "flowBackgroundColor": "#D5D6DB",
+ "flowBackgroundColor": "#BFC0C5",
"flowTextColor": "#4A5685",
"iconColor": "#2F334D",
"labelTextColor": "#2F334D",
diff --git a/YACReader/themes/builtin_light3.json b/YACReader/themes/builtin_light3.json
index 37c822007..81e403892 100644
--- a/YACReader/themes/builtin_light3.json
+++ b/YACReader/themes/builtin_light3.json
@@ -6,7 +6,7 @@
"editBackgroundColor": "#CCFBF7EA",
"editBorderColor": "#66E6E2CC",
"editTextColor": "#4F5B58",
- "flowBackgroundColor": "#EFEBD4",
+ "flowBackgroundColor": "#D9D5BE",
"flowTextColor": "#5C6A72",
"iconColor": "#4F5B58",
"labelTextColor": "#4F5B58",
diff --git a/YACReader/themes/builtin_light4.json b/YACReader/themes/builtin_light4.json
index d7f33aa62..577e38227 100644
--- a/YACReader/themes/builtin_light4.json
+++ b/YACReader/themes/builtin_light4.json
@@ -6,7 +6,7 @@
"editBackgroundColor": "#CCFFF8F1",
"editBorderColor": "#66D9CEC7",
"editTextColor": "#575279",
- "flowBackgroundColor": "#E8DDD3",
+ "flowBackgroundColor": "#D2C7BD",
"flowTextColor": "#6E6A86",
"iconColor": "#575279",
"labelTextColor": "#575279",
diff --git a/YACReader/themes/builtin_light5.json b/YACReader/themes/builtin_light5.json
index 3cb78d05f..88051e322 100644
--- a/YACReader/themes/builtin_light5.json
+++ b/YACReader/themes/builtin_light5.json
@@ -6,7 +6,7 @@
"editBackgroundColor": "#CCFCF7ED",
"editBorderColor": "#66D8CDBA",
"editTextColor": "#545464",
- "flowBackgroundColor": "#E7DCCB",
+ "flowBackgroundColor": "#D1C6B5",
"flowTextColor": "#716E61",
"iconColor": "#545464",
"labelTextColor": "#545464",
diff --git a/YACReader/viewer.cpp b/YACReader/viewer.cpp
index 4925b9c40..100d40ccd 100644
--- a/YACReader/viewer.cpp
+++ b/YACReader/viewer.cpp
@@ -118,11 +118,11 @@ Viewer::Viewer(QWidget *parent)
content->setAlignment(Qt::AlignTop | Qt::AlignHCenter);
content->setMouseTracking(true);
- // dedicated widget for status messages ("Press 'O' to open comic.", "Loading...", etc.)
+ // dedicated widget for status messages ("Press 'O' to open a comic.", "Loading...", etc.)
messageLabel = new QLabel(this);
messageLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
messageLabel->setAlignment(Qt::AlignTop | Qt::AlignHCenter);
- messageLabel->setText(tr("Press 'O' to open comic."));
+ messageLabel->setText(tr("Press 'O' to open a comic."));
messageLabel->setFont(QFont("courier new", 12));
messageLabel->setMouseTracking(true);
@@ -1812,7 +1812,7 @@ void Viewer::doubleMangaPageSwitch()
void Viewer::resetContent()
{
- configureContent(tr("Press 'O' to open comic."));
+ configureContent(tr("Press 'O' to open a comic."));
goToFlow->reset();
continuousViewModel->reset();
continuousPageProvider->reset();
@@ -1829,7 +1829,7 @@ void Viewer::setLoadingMessage()
}
emit pageAvailable(false);
if (!continuousScroll) {
- configureContent(tr("Loading...please wait!"));
+ configureContent(tr("Loading... please wait!"));
}
}
diff --git a/YACReader/yacreader_de.ts b/YACReader/yacreader_de.ts
index c89cef1ab..180a06945 100644
--- a/YACReader/yacreader_de.ts
+++ b/YACReader/yacreader_de.ts
@@ -19,12 +19,12 @@
System
- Systemumgebung
+ SystemLight
- Licht
+ Hell
@@ -34,7 +34,7 @@
Custom
- Brauch
+ Benutzerdefiniert
@@ -44,12 +44,12 @@
Remove this user-imported theme
- Entfernen Sie dieses vom Benutzer importierte Design
+ Dieses vom Benutzer importierte Design entfernenLight:
- Licht:
+ Hell:
@@ -64,37 +64,37 @@
Import theme...
- Theme importieren...
+ Design importieren...Theme
- Thema
+ DesignTheme editor
- Theme-Editor
+ Design-EditorOpen Theme Editor...
- Theme-Editor öffnen...
+ Design-Editor öffnen...Theme editor error
- Fehler im Theme-Editor
+ Fehler im Design-EditorThe current theme JSON could not be loaded.
- Der aktuelle Theme-JSON konnte nicht geladen werden.
+ Das JSON des aktuellen Designs konnte nicht geladen werden.Import theme
- Thema importieren
+ Design importieren
@@ -105,7 +105,7 @@
Could not import theme from:
%1
- Theme konnte nicht importiert werden von:
+ Das Design konnte nicht aus folgender Datei importiert werden:
%1
@@ -114,7 +114,7 @@
%1
%2
- Theme konnte nicht importiert werden von:
+ Das Design konnte nicht aus folgender Datei importiert werden:
%1
%2
@@ -122,7 +122,7 @@
Import failed
- Der Import ist fehlgeschlagen
+ Import fehlgeschlagen
@@ -130,7 +130,7 @@
Close
- Schliessen
+ Schließen
@@ -141,12 +141,12 @@
Click on any image to go to the bookmark
- Klicke auf ein beliebiges Bild, um zum Lesezeichen zu gehen
+ Klicken Sie auf ein beliebiges Bild, um zum Lesezeichen zu gehen
- Lastest Page
- Neueste Seite
+ Latest Page
+ Letzte Seite
@@ -154,7 +154,7 @@
Loading page %1
- Seite wird geladen %1
+ Seite %1 wird geladen
@@ -166,8 +166,8 @@
- To change a shortcut, double click in the key combination and type the new keys.
- Um ein Kürzel zu ändern, klicke doppelt auf die Tastenkombination und füge die neuen Tasten ein.
+ To change a shortcut, double-click the key combination and type the new keys.
+ Um ein Kürzel zu ändern, doppelklicken Sie auf die Tastenkombination und geben Sie die neuen Tasten ein.
@@ -177,36 +177,36 @@
Shortcut in use
- Genutzte Kürzel
+ Kürzel wird verwendet
- The shortcut "%1" is already assigned to other function
+ The shortcut "%1" is already assigned to another functionDas Kürzel "%1" ist bereits für eine andere Funktion in VerwendungFileComic
-
-
+
+ Format not supportedFormat wird nicht unterstützt
-
+ 7z not found7z nicht gefunden
-
+ Unknown error opening the file
- Unbekannter Fehler beim Öffnen des Files
+ Unbekannter Fehler beim Öffnen der Datei
-
+ CRC error on page (%1): some of the pages will not be displayed correctly
- CRC Error auf Seite (%1): Einige Seiten werden nicht korrekt dargestellt
+ CRC-Fehler auf Seite (%1): Einige Seiten werden nicht korrekt dargestellt
@@ -224,8 +224,8 @@
- Total pages :
- Seiten gesamt :
+ Total pages:
+ Seiten gesamt:
@@ -234,16 +234,16 @@
- Page :
- Seite :
+ Page:
+ Seite: GoToFlowToolBar
- Page :
- Seite :
+ Page:
+ Seite:
@@ -261,7 +261,7 @@
Changelog
-
+ Änderungsprotokoll
@@ -274,7 +274,7 @@
Gamma
- Gammawert
+ Gamma
@@ -284,7 +284,7 @@
My comics path
- Meine Comics-Pfad
+ Pfad zu meinen Comics
@@ -304,12 +304,12 @@
Nearest (fast, low quality)
- Am nächsten (schnell, niedrige Qualität)
+ Nächster Nachbar (schnell, niedrige Qualität)Bilinear
- Bilinear-Filter
+ Bilinear
@@ -317,7 +317,12 @@
Lanczos (bessere Qualität)
-
+
+ Bicubic
+
+
+
+ Image adjustmentBildanpassung
@@ -342,12 +347,12 @@
Kontrast
-
+ AppearanceAussehen
-
+ OptionsOptionen
@@ -372,9 +377,9 @@
Löschen
-
+ Comics directory
- Comics-Verzeichnis
+ Comics-Ordner
@@ -382,12 +387,12 @@
Hintergrundfarbe
-
+ Page FlowSeitenfluss
-
+ GeneralAllgemein
@@ -397,7 +402,7 @@
Helligkeit
-
+ Restart is neededNeustart erforderlich
@@ -414,7 +419,7 @@
Show time in current page information label
- Zeit im Informationsetikett der aktuellen Seite anzeigen
+ Uhrzeit in der Informationsanzeige der aktuellen Seite anzeigen
@@ -438,7 +443,7 @@
- Scroll behaviour
+ Scroll behaviorScrollverhalten
@@ -449,12 +454,12 @@
Do not turn page using scroll
- Blättern Sie nicht mit dem Scrollen um
+ Nicht mit dem Scrollen umblätternUse single scroll step to turn page
- Verwenden Sie einen einzelnen Bildlaufschritt, um die Seite umzublättern
+ Einzelnen Scrollschritt zum Umblättern verwenden
@@ -473,7 +478,7 @@
- Click left or right half of the screen to turn pages.
+ Click the left or right half of the screen to turn pages.Klicken Sie auf die linke oder rechte Hälfte des Bildschirms, um die Seiten umzublättern.
@@ -509,7 +514,7 @@ If none is active, Escape does nothing.
Tooltip listing the order in which modes are cancelled. Only the first active mode in the list is cancelled per Escape keypress.Die Escape-Taste beendet den ersten aktiven Modus in dieser Reihenfolge:
-1. Lupe
+1. Vergrößerungsglas
2. Wörterbuch
3. „Gehe zu Seite“-Leiste
4. Vollbildmodus
@@ -529,7 +534,7 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
Disable mouse over activation
- Aktivierung durch Maus deaktivieren
+ Aktivierung beim Überfahren mit der Maus deaktivieren
@@ -539,7 +544,7 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
Enlarge images to fit width/height
- Bilder vergrößern, um sie Breite/Höhe anzupassen
+ Bilder vergrößern, um sie an Breite/Höhe anzupassen
@@ -557,20 +562,20 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
7z lib not found
- 7z-Verzeichnis nicht gefunden
+ 7z-Bibliothek nicht gefundenunable to load 7z lib from ./utils
- 7z -erzeichnis kann von ./utils nicht geladen werden
+ 7z-Bibliothek kann nicht aus ./utils geladen werden
-
+ Select custom coverWählen Sie ein benutzerdefiniertes Cover
-
+ Images (%1)Bilder (%1)
@@ -582,7 +587,7 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
This theme is for %1, not %2.
- Dieses Thema ist für %1, nicht für %2.
+ Dieses Design ist für %1, nicht für %2.
@@ -590,7 +595,7 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
Theme Editor
- Theme-Editor
+ Design-Editor
@@ -605,7 +610,7 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
i
- ich
+ i
@@ -620,7 +625,7 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
Hold to flash the selected value in the UI (magenta / toggled / 0↔10). Releases restore the original.
- Halten Sie gedrückt, um den ausgewählten Wert in der Benutzeroberfläche zu blinken (Magenta / umgeschaltet / 0↔10). Loslassen stellt das Original wieder her.
+ Halten Sie gedrückt, um den ausgewählten Wert in der Benutzeroberfläche blinken zu lassen (Magenta / umgeschaltet / 0↔10). Loslassen stellt das Original wieder her.
@@ -630,7 +635,7 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
Light
- Licht
+ Hell
@@ -640,7 +645,7 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
ID:
- AUSWEIS:
+ ID:
@@ -655,12 +660,12 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
Theme info
- Themeninfo
+ Design-InfoParameter
- Parameterwert
+ Parameter
@@ -685,7 +690,7 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
Close
- Schliessen
+ Schließen
@@ -700,7 +705,7 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
true
- WAHR
+ true
@@ -708,7 +713,7 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
false
- FALSCH
+ false
@@ -730,7 +735,7 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
Save theme
- Thema speichern
+ Design speichern
@@ -753,7 +758,7 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
Load theme
- Theme laden
+ Design laden
@@ -773,7 +778,7 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
Invalid JSON:
%1
- Ungültiger JSON:
+ Ungültiges JSON:
%1
@@ -792,7 +797,7 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
- Press 'O' to open comic.
+ Press 'O' to open a comic.'O' drücken, um Comic zu öffnen.
@@ -803,12 +808,12 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
Cover!
- Titelseite!
+ Cover!CRC Error
- CRC Fehler
+ CRC-Fehler
@@ -827,7 +832,7 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
- Loading...please wait!
+ Loading... please wait!Ladevorgang... Bitte warten!
@@ -861,12 +866,12 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
Roulette look
- Zufalls-Ansicht
+ Roulette-AnsichtShow advanced settings
- Zeige erweiterte Einstellungen
+ Erweiterte Einstellungen anzeigen
@@ -876,42 +881,42 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
View angle
- Anzeige-Winkel
+ BlickwinkelPosition
- Lage
+ PositionCover gap
- Titelbild-Abstand
+ Cover-AbstandCentral gap
- Mittiger Abstand
+ Mittlerer AbstandZoom
- Vergrößern
+ ZoomY offset
- Y-Anpassung
+ Y-VersatzZ offset
- Z-Anpassung
+ Z-VersatzCover Angle
- Titelbild Ansichtswinkel
+ Cover-Winkel
@@ -940,8 +945,8 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
- Use VSync (improve the image quality in fullscreen mode, worse performance)
- Benutze VSync (verbessert die Bildqualität im Vollanzeigemodus, schlechtere Leistung)
+ Use VSync (improves image quality in fullscreen mode, but reduces performance)
+ VSync verwenden (verbessert die Bildqualität im Vollbildmodus, schlechtere Leistung)
@@ -964,7 +969,7 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
New instance
- Neuer Fall
+ Neue Instanz
@@ -984,7 +989,7 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
Open the latest comic opened in the previous reading session
- Öffne den neuesten Comic deiner letzten Sitzung
+ Den zuletzt geöffneten Comic der vorherigen Lesesitzung öffnen
@@ -994,7 +999,7 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
Clear open recent list
- Lösche Liste zuletzt geöffneter Elemente
+ Liste der zuletzt geöffneten Elemente löschen
@@ -1003,35 +1008,35 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
-
-
-
+
+
+ Save current pageAktuelle Seite speichern
-
-
-
-
+
+
+
+ Extract page(s)
-
+ Seite(n) extrahierenExtract page(s) from the original source
-
+ Seite(n) aus der Originalquelle extrahierenPrevious Comic
- Voheriger Comic
+ Vorheriger Comic
-
-
+
+ Open previous comicVorherigen Comic öffnen
@@ -1042,8 +1047,8 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
-
-
+
+ Open next comicNächsten Comic öffnen
@@ -1054,20 +1059,20 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
-
-
+
+ Go to previous pageZur vorherigen Seite gehen&Next
- &Nächstes
+ &Nächste
-
-
+
+ Go to next pageZur nächsten Seite gehen
@@ -1089,12 +1094,12 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
Fit image to width
- Bildbreite anpassen
+ Bild an Breite anpassenShow full size
- Vollansicht anzeigen
+ Originalgröße anzeigen
@@ -1109,7 +1114,7 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
Switch to continuous scroll mode
- Wechseln Sie in den kontinuierlichen Bildlaufmodus
+ In den kontinuierlichen Scrollmodus wechseln
@@ -1124,12 +1129,12 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
Zoom+
- Vergr??ern+
+ Zoom+Zoom-
- Verkleinern-
+ Zoom-
@@ -1168,8 +1173,8 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
- Go to page ...
- Gehe zu Seite ...
+ Go to page...
+ Gehe zu Seite...
@@ -1179,7 +1184,7 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
YACReader options
- YACReader Optionen
+ YACReader-Optionen
@@ -1190,17 +1195,19 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
Help, About YACReader
- Hilfe, über YACReader
+ Hilfe, Über YACReader
+
+ Magnifying glass
- Vergößerungsglas
+ VergrößerungsglasSwitch Magnifying glass
- Vergrößerungsglas wechseln
+ Vergrößerungsglas umschalten
@@ -1245,7 +1252,7 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
Close
- Schliessen
+ Schließen
@@ -1260,7 +1267,7 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
Edit shortcuts
- Kürzel ändern
+ Kürzel bearbeiten
@@ -1281,7 +1288,7 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
Edit
- Ändern
+ Bearbeiten
@@ -1291,7 +1298,7 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
Go
- Los
+ Gehe zu
@@ -1299,222 +1306,217 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.Fenster
-
+ Open ComicComic öffnen
-
+ Comic filesComic-Dateien
-
+ Open folderOrdner öffnen
-
-
+
+ Comics
- Comichefte
+ Comics
-
+ Toggle fullscreen modeVollbild-Modus umschalten
-
+ Hide/show toolbar
- Symbolleiste anzeigen/verstecken
+ Symbolleiste ein-/ausblenden
-
-
+
+ GeneralAllgemein
-
+ Size up magnifying glassVergrößerungsglas vergrößern
-
+ Size down magnifying glassVergrößerungsglas verkleinern
-
+ Zoom in magnifying glass
- Vergrößerungsglas reinzoomen
+ Zoom des Vergrößerungsglases erhöhen
-
+ Zoom out magnifying glass
- Vergrößerungsglas rauszoomen
+ Zoom des Vergrößerungsglases verringern
-
+ Reset magnifying glass
- Lupe zurücksetzen
+ Vergrößerungsglas zurücksetzen
-
-
- Magnifiying glass
- Vergrößerungsglas
-
-
-
+ Toggle between fit to width and fit to height
- Zwischen Anpassung an Seite und Höhe wechseln
+ Zwischen Anpassung an Breite und Höhe wechseln
-
-
- Page adjustement
+
+
+ Page adjustment
+ Page adjustementSeitenanpassung
-
+ The current page is not ready. Please try again later.Die aktuelle Seite ist noch nicht bereit. Bitte versuchen Sie es später erneut.
-
+ Overwrite file?
-
+ Datei überschreiben?
-
+ The file already exists. Do you want to overwrite it?
-
+ Die Datei existiert bereits. Möchten Sie sie überschreiben?
-
+ The current page could not be saved.Die aktuelle Seite konnte nicht gespeichert werden.
-
+ The selected pages are not ready. Please try again later.Die ausgewählten Seiten sind noch nicht bereit. Bitte versuchen Sie es später erneut.
-
+ The current page could not be extracted.
-
+ Die aktuelle Seite konnte nicht extrahiert werden.
-
+ Overwrite files?
-
+ Dateien überschreiben?
-
+ Some files already exist. Do you want to overwrite them?
-
+ Einige Dateien existieren bereits. Möchten Sie sie überschreiben?
-
+ Some pages could not be extracted.
-
+ Einige Seiten konnten nicht extrahiert werden.
-
+ Autoscroll down
- Automatisches Runterscrollen
+ Automatisch nach unten scrollen
-
+ Autoscroll up
- Automatisches Raufscrollen
+ Automatisch nach oben scrollen
-
+ Autoscroll forward, horizontal firstAutomatisches Vorwärtsscrollen, horizontal zuerst
-
+ Autoscroll backward, horizontal firstAutomatisches Zurückscrollen, horizontal zuerst
-
+ Autoscroll forward, vertical firstAutomatisches Vorwärtsscrollen, vertikal zuerst
-
+ Autoscroll backward, vertical firstAutomatisches Zurückscrollen, vertikal zuerst
-
+ Move downNach unten
-
+ Move upNach oben
-
+ Move leftNach links
-
+ Move rightNach rechts
-
+ Go to the first pageZur ersten Seite gehen
-
+ Go to the last pageZur letzten Seite gehen
-
+ Offset double page to the left
- Doppelseite nach links versetzt
+ Doppelseite nach links versetzen
-
+ Offset double page to the right
- Doppelseite nach rechts versetzt
+ Doppelseite nach rechts versetzen
-
-
+
+ Reading
- Lesend
+ Lesen
-
+ There is a new version availableNeue Version verfügbar
-
+ Do you want to download the new version?Möchten Sie die neue Version herunterladen?
-
+ Remind me in 14 daysIn 14 Tagen erneut erinnern
-
+ Not nowNicht jetzt
@@ -1524,12 +1526,12 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
Release notes are not available.
-
+ Versionshinweise sind nicht verfügbar.Previous versions
-
+ Frühere Versionen
@@ -1537,11 +1539,11 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
Restore to default
- Ursprungszustand wiederherstellen
+ Standardwert wiederherstellen
-
+ Click to overwriteAnklicken, um zu überschreiben
@@ -1551,13 +1553,13 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
Restore to default
- Urpsrungszustand wiederherstellen
+ Standardwert wiederherstellen
-
-
-
+
+
+ Click to overwriteAnklicken, um zu überschreiben
@@ -1624,7 +1626,7 @@ Wenn keiner aktiv ist, geschieht beim Drücken der Escape-Taste nichts.
YACReader translator
- YACReader Übersetzer
+ YACReader-Übersetzer
diff --git a/YACReader/yacreader_en.ts b/YACReader/yacreader_en.ts
index 8cc6b1a84..0abd69aba 100644
--- a/YACReader/yacreader_en.ts
+++ b/YACReader/yacreader_en.ts
@@ -129,8 +129,8 @@
BookmarksDialog
- Lastest Page
- Lastest Page
+ Latest Page
+ Latest Page
@@ -166,8 +166,8 @@
- To change a shortcut, double click in the key combination and type the new keys.
- To change a shortcut, double click in the key combination and type the new keys.
+ To change a shortcut, double-click the key combination and type the new keys.
+ To change a shortcut, double-click the key combination and type the new keys.
@@ -181,30 +181,30 @@
- The shortcut "%1" is already assigned to other function
- The shortcut "%1" is already assigned to other function
+ The shortcut "%1" is already assigned to another function
+ The shortcut "%1" is already assigned to another functionFileComic
-
+ CRC error on page (%1): some of the pages will not be displayed correctlyCRC error on page (%1): some of the pages will not be displayed correctly
-
+ Unknown error opening the fileUnknown error opening the file
-
+ 7z not found7z not found
-
-
+
+ Format not supportedFormat not supported
@@ -213,8 +213,8 @@
GoToDialog
- Page :
- Page :
+ Page:
+ Page:
@@ -229,8 +229,8 @@
- Total pages :
- Total pages :
+ Total pages:
+ Total pages:
@@ -242,8 +242,8 @@
GoToFlowToolBar
- Page :
- Page :
+ Page:
+ Page:
@@ -266,7 +266,7 @@
Changelog
-
+ Changelog
@@ -327,7 +327,7 @@
Lanczos (better quality)
-
+ Restart is neededRestart is needed
@@ -339,17 +339,17 @@
Language
-
+ LanguageApplication language
-
+ Application languageSystem default
-
+ System default
@@ -383,8 +383,8 @@
- Scroll behaviour
- Scroll behaviour
+ Scroll behavior
+ Scroll behavior
@@ -418,8 +418,8 @@
- Click left or right half of the screen to turn pages.
- Click left or right half of the screen to turn pages.
+ Click the left or right half of the screen to turn pages.
+ Click the left or right half of the screen to turn pages.
@@ -517,12 +517,17 @@ If none is active, Escape does nothing.
Show book fold shadow
-
+
+ Bicubic
+
+
+
+ GeneralGeneral
-
+ AppearanceAppearance
@@ -532,22 +537,22 @@ If none is active, Escape does nothing.
Clear
-
+ Page FlowPage Flow
-
+ Image adjustmentImage adjustment
-
+ OptionsOptions
-
+ Comics directoryComics directory
@@ -565,12 +570,12 @@ If none is active, Escape does nothing.
unable to load 7z lib from ./utils
-
+ Select custom coverSelect custom cover
-
+ Images (%1)Images (%1)
@@ -787,8 +792,8 @@ If none is active, Escape does nothing.
- Press 'O' to open comic.
- Press 'O' to open comic.
+ Press 'O' to open a comic.
+ Press 'O' to open a comic.
@@ -812,8 +817,8 @@ If none is active, Escape does nothing.
- Loading...please wait!
- Loading...please wait!
+ Loading... please wait!
+ Loading... please wait!
@@ -940,8 +945,8 @@ If none is active, Escape does nothing.
- Use VSync (improve the image quality in fullscreen mode, worse performance)
- Use VSync (improve the image quality in fullscreen mode, worse performance)
+ Use VSync (improves image quality in fullscreen mode, but reduces performance)
+ Use VSync (improves image quality in fullscreen mode, but reduces performance)
@@ -1003,25 +1008,25 @@ If none is active, Escape does nothing.
-
-
-
+
+
+ Save current pageSave current page
-
-
-
-
+
+
+
+ Extract page(s)
-
+ Extract page(s)Extract page(s) from the original source
-
+ Extract page(s) from the original source
@@ -1030,8 +1035,8 @@ If none is active, Escape does nothing.
-
-
+
+ Open previous comicOpen previous comic
@@ -1042,8 +1047,8 @@ If none is active, Escape does nothing.
-
-
+
+ Open next comicOpen next comic
@@ -1054,8 +1059,8 @@ If none is active, Escape does nothing.
-
-
+
+ Go to previous pageGo to previous page
@@ -1066,8 +1071,8 @@ If none is active, Escape does nothing.
-
-
+
+ Go to next pageGo to next page
@@ -1168,8 +1173,8 @@ If none is active, Escape does nothing.
- Go to page ...
- Go to page ...
+ Go to page...
+ Go to page...
@@ -1194,6 +1199,8 @@ If none is active, Escape does nothing.
+
+ Magnifying glassMagnifying glass
@@ -1299,222 +1306,217 @@ If none is active, Escape does nothing.
Window
-
+ Open ComicOpen Comic
-
+ Comic filesComic files
-
+ Open folderOpen folder
-
-
+
+ ComicsComics
-
+ Toggle fullscreen modeToggle fullscreen mode
-
+ Hide/show toolbarHide/show toolbar
-
-
+
+ GeneralGeneral
-
+ Size up magnifying glassSize up magnifying glass
-
+ Size down magnifying glassSize down magnifying glass
-
+ Zoom in magnifying glassZoom in magnifying glass
-
+ Zoom out magnifying glassZoom out magnifying glass
-
+ Reset magnifying glassReset magnifying glass
-
-
- Magnifiying glass
- Magnifiying glass
-
-
-
+ Toggle between fit to width and fit to heightToggle between fit to width and fit to height
-
-
- Page adjustement
- Page adjustement
+
+
+ Page adjustment
+ Page adjustement
+ Page adjustment
-
+ The current page is not ready. Please try again later.The current page is not ready. Please try again later.
-
+ Overwrite file?
-
+ Overwrite file?
-
+ The file already exists. Do you want to overwrite it?
-
+ The file already exists. Do you want to overwrite it?
-
+ The current page could not be saved.The current page could not be saved.
-
+ The selected pages are not ready. Please try again later.The selected pages are not ready. Please try again later.
-
+ The current page could not be extracted.
-
+ The current page could not be extracted.
-
+ Overwrite files?
-
+ Overwrite files?
-
+ Some files already exist. Do you want to overwrite them?
-
+ Some files already exist. Do you want to overwrite them?
-
+ Some pages could not be extracted.
-
+ Some pages could not be extracted.
-
+ Autoscroll downAutoscroll down
-
+ Autoscroll upAutoscroll up
-
+ Autoscroll forward, horizontal firstAutoscroll forward, horizontal first
-
+ Autoscroll backward, horizontal firstAutoscroll backward, horizontal first
-
+ Autoscroll forward, vertical firstAutoscroll forward, vertical first
-
+ Autoscroll backward, vertical firstAutoscroll backward, vertical first
-
+ Move downMove down
-
+ Move upMove up
-
+ Move leftMove left
-
+ Move rightMove right
-
+ Go to the first pageGo to the first page
-
+ Go to the last pageGo to the last page
-
+ Offset double page to the leftOffset double page to the left
-
+ Offset double page to the rightOffset double page to the right
-
-
+
+ ReadingReading
-
+ There is a new version availableThere is a new version available
-
+ Do you want to download the new version?Do you want to download the new version?
-
+ Remind me in 14 daysRemind me in 14 days
-
+ Not nowNot now
@@ -1524,19 +1526,19 @@ If none is active, Escape does nothing.
Release notes are not available.
-
+ Release notes are not available.Previous versions
-
+ Previous versionsYACReaderFieldEdit
-
+ Click to overwriteClick to overwrite
@@ -1550,9 +1552,9 @@ If none is active, Escape does nothing.
YACReaderFieldPlainTextEdit
-
-
-
+
+
+ Click to overwriteClick to overwrite
diff --git a/YACReader/yacreader_es.ts b/YACReader/yacreader_es.ts
index 4c0af8bb6..c9f41ae8a 100644
--- a/YACReader/yacreader_es.ts
+++ b/YACReader/yacreader_es.ts
@@ -24,7 +24,7 @@
Light
- Luz
+ Claro
@@ -145,7 +145,7 @@
- Lastest Page
+ Latest PageÚltima página
@@ -166,45 +166,45 @@
- To change a shortcut, double click in the key combination and type the new keys.
+ To change a shortcut, double-click the key combination and type the new keys.Para cambiar un atajo, haz doble clic en la combinación de teclas y escribe las nuevas teclas.Shortcuts settings
- Configuración de accesos directos
+ Configuración de atajosShortcut in use
- Accesos directos en uso
+ Atajo en uso
- The shortcut "%1" is already assigned to other function
- El acceso directo "%1" ya está asignado a otra función
+ The shortcut "%1" is already assigned to another function
+ El atajo "%1" ya está asignado a otra funciónFileComic
-
-
+
+ Format not supportedFormato no soportado
-
+ 7z not found7z no encontrado
-
+ Unknown error opening the file
- Error desconocido abriendo el archivo
+ Error desconocido al abrir el archivo
-
+ CRC error on page (%1): some of the pages will not be displayed correctlyError CRC en la página (%1): algunas de las páginas no se mostrarán correctamente
@@ -224,8 +224,8 @@
- Total pages :
- Páginas totales :
+ Total pages:
+ Páginas totales:
@@ -234,16 +234,16 @@
- Page :
- Página :
+ Page:
+ Página: GoToFlowToolBar
- Page :
- Página :
+ Page:
+ Página:
@@ -256,12 +256,12 @@
System info
- Información de sistema
+ Información del sistemaChangelog
-
+ Registro de cambios
@@ -317,7 +317,12 @@
Lanczos (mejor calidad)
-
+
+ Bicubic
+
+
+
+ Image adjustmentAjustes de imagen
@@ -342,12 +347,12 @@
Contraste
-
+ AppearanceApariencia
-
+ OptionsOpciones
@@ -372,9 +377,9 @@
Limpiar
-
+ Comics directory
- Directorio de cómics
+ Carpeta de cómics
@@ -382,14 +387,14 @@
Color de fondo
-
+ Page FlowFlujo de página
-
+ General
- Opciones generales
+ General
@@ -397,7 +402,7 @@
Brillo
-
+ Restart is neededEs necesario reiniciar
@@ -438,7 +443,7 @@
- Scroll behaviour
+ Scroll behaviorComportamiento del scroll
@@ -473,7 +478,7 @@
- Click left or right half of the screen to turn pages.
+ Click the left or right half of the screen to turn pages.Hacer clic en la mitad izquierda o derecha de la pantalla para cambiar de página.
@@ -557,20 +562,20 @@ Si ninguno está activo, la tecla Esc no hace nada.
7z lib not found
- 7z lib no encontrado
+ Biblioteca 7z no encontradaunable to load 7z lib from ./utils
- imposible cargar 7z lib de ./utils
+ no se puede cargar la biblioteca 7z desde ./utils
-
+ Select custom coverSeleccionar portada personalizada
-
+ Images (%1)Imágenes (%1)
@@ -605,7 +610,7 @@ Si ninguno está activo, la tecla Esc no hace nada.
i
- ?
+ i
@@ -630,7 +635,7 @@ Si ninguno está activo, la tecla Esc no hace nada.
Light
- Luz
+ Claro
@@ -640,7 +645,7 @@ Si ninguno está activo, la tecla Esc no hace nada.
ID:
- IDENTIFICACIÓN:
+ ID:
@@ -700,7 +705,7 @@ Si ninguno está activo, la tecla Esc no hace nada.
true
- verdadero
+ true
@@ -708,7 +713,7 @@ Si ninguno está activo, la tecla Esc no hace nada.
false
- falso
+ false
@@ -792,8 +797,8 @@ Si ninguno está activo, la tecla Esc no hace nada.
- Press 'O' to open comic.
- Pulsa 'O' para abrir un fichero.
+ Press 'O' to open a comic.
+ Pulsa 'O' para abrir un cómic.
@@ -827,8 +832,8 @@ Si ninguno está activo, la tecla Esc no hace nada.
- Loading...please wait!
- Cargando...espere, por favor!
+ Loading... please wait!
+ Cargando... ¡espera, por favor!
@@ -866,7 +871,7 @@ Si ninguno está activo, la tecla Esc no hace nada.
Show advanced settings
- Opciones avanzadas
+ Mostrar opciones avanzadas
@@ -896,7 +901,7 @@ Si ninguno está activo, la tecla Esc no hace nada.
Zoom
- Ampliaci?n
+ Zoom
@@ -936,11 +941,11 @@ Si ninguno está activo, la tecla Esc no hace nada.
High Performance
- Alto rendimiento
+ Rendimiento alto
- Use VSync (improve the image quality in fullscreen mode, worse performance)
+ Use VSync (improves image quality in fullscreen mode, but reduces performance)Utilizar VSync (mejora la calidad de imagen en pantalla completa, peor rendimiento)
@@ -1003,25 +1008,25 @@ Si ninguno está activo, la tecla Esc no hace nada.
-
-
-
+
+
+ Save current pageGuardar la página actual
-
-
-
-
+
+
+
+ Extract page(s)
-
+ Extraer página(s)Extract page(s) from the original source
-
+ Extraer página(s) del origen original
@@ -1030,20 +1035,20 @@ Si ninguno está activo, la tecla Esc no hace nada.
-
-
+
+ Open previous comicAbrir cómic anteriorNext Comic
- Siguiente Cómic
+ Siguiente cómic
-
-
+
+ Open next comicAbrir siguiente cómic
@@ -1054,8 +1059,8 @@ Si ninguno está activo, la tecla Esc no hace nada.
-
-
+
+ Go to previous pageIr a la página anterior
@@ -1066,8 +1071,8 @@ Si ninguno está activo, la tecla Esc no hace nada.
-
-
+
+ Go to next pageIr a la página siguiente
@@ -1124,12 +1129,12 @@ Si ninguno está activo, la tecla Esc no hace nada.
Zoom+
- Ampliar+
+ Zoom+Zoom-
- Reducir
+ Zoom-
@@ -1144,7 +1149,7 @@ Si ninguno está activo, la tecla Esc no hace nada.
Double page mode
- Modo a doble página
+ Modo de doble página
@@ -1154,12 +1159,12 @@ Si ninguno está activo, la tecla Esc no hace nada.
Double page manga mode
- Modo de manga de página doble
+ Modo manga de doble páginaReverse reading order in double page mode
- Invertir el orden de lectura en modo de página doble
+ Invertir el orden de lectura en modo de doble página
@@ -1168,7 +1173,7 @@ Si ninguno está activo, la tecla Esc no hace nada.
- Go to page ...
+ Go to page...Ir a página...
@@ -1190,17 +1195,19 @@ Si ninguno está activo, la tecla Esc no hace nada.
Help, About YACReader
- Ayuda, Sobre YACReader
+ Ayuda, Acerca de YACReader
+
+ Magnifying glassLupaSwitch Magnifying glass
- Lupa On/Off
+ Alternar lupa
@@ -1260,7 +1267,7 @@ Si ninguno está activo, la tecla Esc no hace nada.
Edit shortcuts
- Editar accesos directos
+ Editar atajos
@@ -1299,222 +1306,217 @@ Si ninguno está activo, la tecla Esc no hace nada.
Ventana
-
+ Open ComicAbrir cómic
-
+ Comic filesArchivos de cómic
-
+ Open folderAbrir carpeta
-
-
+
+ ComicsCómics
-
+ Toggle fullscreen modeAlternar modo de pantalla completa
-
+ Hide/show toolbarOcultar/mostrar barra de herramientas
-
-
+
+ General
- Opciones generales
+ General
-
+ Size up magnifying glassAumentar tamaño de la lupa
-
+ Size down magnifying glassDisminuir tamaño de lupa
-
+ Zoom in magnifying glass
- Incrementar el aumento de la lupa
+ Aumentar el zoom de la lupa
-
+ Zoom out magnifying glass
- Reducir el aumento de la lupa
+ Reducir el zoom de la lupa
-
+ Reset magnifying glass
- Resetear lupa
+ Restablecer lupa
-
-
- Magnifiying glass
- Lupa
-
-
-
+ Toggle between fit to width and fit to heightAlternar entre ajuste al ancho y ajuste al alto
-
-
- Page adjustement
+
+
+ Page adjustment
+ Page adjustementAjuste de página
-
+ The current page is not ready. Please try again later.
- La página actual aún no está lista. Por favor, inténtelo de nuevo más tarde.
+ La página actual aún no está lista. Por favor, inténtalo de nuevo más tarde.
-
+ Overwrite file?
-
+ ¿Sobrescribir el archivo?
-
+ The file already exists. Do you want to overwrite it?
-
+ El archivo ya existe. ¿Quieres sobrescribirlo?
-
+ The current page could not be saved.No se ha podido guardar la página actual.
-
+ The selected pages are not ready. Please try again later.
- Las páginas seleccionadas aún no están listas. Por favor, inténtelo de nuevo más tarde.
+ Las páginas seleccionadas aún no están listas. Por favor, inténtalo de nuevo más tarde.
-
+ The current page could not be extracted.
-
+ No se ha podido extraer la página actual.
-
+ Overwrite files?
-
+ ¿Sobrescribir los archivos?
-
+ Some files already exist. Do you want to overwrite them?
-
+ Algunos archivos ya existen. ¿Quieres sobrescribirlos?
-
+ Some pages could not be extracted.
-
+ No se han podido extraer algunas páginas.
-
+ Autoscroll downDesplazamiento automático hacia abajo
-
+ Autoscroll upDesplazamiento automático hacia arriba
-
+ Autoscroll forward, horizontal firstDesplazamiento automático hacia adelante, primero horizontal
-
+ Autoscroll backward, horizontal firstDesplazamiento automático hacia atrás, primero horizontal
-
+ Autoscroll forward, vertical firstDesplazamiento automático hacia adelante, primero vertical
-
+ Autoscroll backward, vertical firstDesplazamiento automático hacia atrás, primero vertical
-
+ Move downMover abajo
-
+ Move upMover arriba
-
+ Move leftMover a la izquierda
-
+ Move rightMover a la derecha
-
+ Go to the first pageIr a la primera página
-
+ Go to the last pageIr a la última página
-
+ Offset double page to the left
- Mover una página a la izquierda
+ Desplazar la doble página a la izquierda
-
+ Offset double page to the right
- Mover una página a la derecha
+ Desplazar la doble página a la derecha
-
-
+
+ Reading
- Leyendo
+ Lectura
-
+ There is a new version availableHay una nueva versión disponible
-
+ Do you want to download the new version?
- ¿Desea descargar la nueva versión?
+ ¿Quieres descargar la nueva versión?
-
+ Remind me in 14 daysRecordar en 14 días
-
+ Not nowAhora no
@@ -1524,12 +1526,12 @@ Si ninguno está activo, la tecla Esc no hace nada.
Release notes are not available.
-
+ Las notas de la versión no están disponibles.Previous versions
-
+ Versiones anteriores
@@ -1541,7 +1543,7 @@ Si ninguno está activo, la tecla Esc no hace nada.
-
+ Click to overwriteClic para sobrescribir
@@ -1555,9 +1557,9 @@ Si ninguno está activo, la tecla Esc no hace nada.
-
-
-
+
+
+ Click to overwriteClic para sobrescribir
@@ -1587,12 +1589,12 @@ Si ninguno está activo, la tecla Esc no hace nada.
Edit shortcuts
- Editar accesos directos
+ Editar atajosShortcuts
- Accesos directos
+ Atajos
diff --git a/YACReader/yacreader_fr.ts b/YACReader/yacreader_fr.ts
index f6f132b24..a3cfd07c6 100644
--- a/YACReader/yacreader_fr.ts
+++ b/YACReader/yacreader_fr.ts
@@ -6,7 +6,7 @@
None
- Rien
+ Aucun
@@ -24,7 +24,7 @@
Light
- Lumière
+ Clair
@@ -34,7 +34,7 @@
Custom
- Coutume
+ Personnalisé
@@ -49,17 +49,17 @@
Light:
- Lumière:
+ Clair :Dark:
- Sombre:
+ Sombre :Custom:
- Personnalisation:
+ Personnalisé :
@@ -89,7 +89,7 @@
The current theme JSON could not be loaded.
- Le thème actuel JSON n'a pas pu être chargé.
+ Le JSON du thème actuel n'a pas pu être chargé.
@@ -145,8 +145,8 @@
- Lastest Page
- Aller à la dernière page
+ Latest Page
+ Dernière page
@@ -176,37 +176,37 @@
- The shortcut "%1" is already assigned to other function
- Le raccourci "%1" est déjà affecté à une autre fonction
+ The shortcut "%1" is already assigned to another function
+ Le raccourci « %1 » est déjà affecté à une autre fonction
- To change a shortcut, double click in the key combination and type the new keys.
- Pour modifier un raccourci, double-cliquez sur la combinaison de touches et tapez les nouvelles clés.
+ To change a shortcut, double-click the key combination and type the new keys.
+ Pour modifier un raccourci, double-cliquez sur la combinaison de touches et saisissez les nouvelles touches.FileComic
-
-
+
+ Format not supportedFormat non supporté
-
+ 7z not found7z introuvable
-
+ Unknown error opening the fileErreur inconnue lors de l'ouverture du fichier
-
+ CRC error on page (%1): some of the pages will not be displayed correctly
- Erreur CRC sur la page (%1): certaines pages ne s'afficheront pas correctement
+ Erreur CRC sur la page (%1) : certaines pages ne s'afficheront pas correctement
@@ -224,8 +224,8 @@
- Total pages :
- Nombre de pages :
+ Total pages:
+ Nombre de pages :
@@ -234,16 +234,16 @@
- Page :
- Feuille :
+ Page:
+ Page : GoToFlowToolBar
- Page :
- Feuille :
+ Page:
+ Page :
@@ -261,12 +261,12 @@
Changelog
-
+ Journal des modificationsAbout
- A propos
+ À propos
@@ -274,12 +274,12 @@
Gamma
- Valeur gamma
+ GammaReset
- Remise à zéro
+ Réinitialiser
@@ -287,14 +287,14 @@
Chemin de mes bandes dessinées
-
+ Image adjustmentAjustement de l'image"Go to flow" size
- Taille de "Aller à Comic Flow"
+ Taille de « Aller à Comic Flow »
@@ -304,7 +304,7 @@
Image options
- Option de l'image
+ Options de l'image
@@ -312,14 +312,14 @@
Contraste
-
+ AppearanceApparence
-
+ Options
- Possibilités
+ Options
@@ -339,12 +339,12 @@
Clear
- Clair
+ Effacer
-
+ Comics directory
- Répertoire des bandes dessinées
+ Dossier des bandes dessinées
@@ -354,7 +354,7 @@
Display
- Afficher
+ Affichage
@@ -384,11 +384,11 @@
Background color
- Couleur d'arrière plan
+ Couleur d'arrière-plan
- Scroll behaviour
+ Scroll behaviorComportement de défilement
@@ -399,12 +399,12 @@
Do not turn page using scroll
- Ne tournez pas la page en utilisant le défilement
+ Ne pas tourner la page avec le défilementUse single scroll step to turn page
- Utilisez une seule étape de défilement pour tourner la page
+ Utiliser une seule étape de défilement pour tourner la page
@@ -414,7 +414,7 @@
Only Back/Forward buttons can turn pages
- Seuls les boutons Précédent/Avant peuvent tourner les pages
+ Seuls les boutons Précédent/Suivant peuvent tourner les pages
@@ -423,7 +423,7 @@
- Click left or right half of the screen to turn pages.
+ Click the left or right half of the screen to turn pages.Cliquez sur la moitié gauche ou droite de l'écran pour tourner les pages.
@@ -444,7 +444,7 @@
Escape closes the reader, even while a mode is active.
- La touche Échap ferme le lecteur, même lorsqu’un mode est actif.
+ La touche Échap ferme le lecteur, même lorsqu'un mode est actif.
@@ -457,19 +457,19 @@
If none is active, Escape does nothing.
Tooltip listing the order in which modes are cancelled. Only the first active mode in the list is cancelled per Escape keypress.
- La touche Échap annule le premier des modes actifs suivants :
+ La touche Échap annule le premier des modes actifs suivants :
1. Loupe
2. Dictionnaire
3. Barre Aller à la page
4. Plein écran
-Si aucun n’est actif, la touche Échap ne fait rien.
+Si aucun n'est actif, la touche Échap ne fait rien.Use software-rendered "go to flow" (requires restart)
- Utiliser "Aller à Comic Flow" avec rendu logiciel (redémarrage requis)
+ Utiliser « Aller à Comic Flow » avec rendu logiciel (redémarrage requis)
@@ -479,12 +479,12 @@ Si aucun n’est actif, la touche Échap ne fait rien.
Disable mouse over activation
- Désactiver la souris sur l'activation
+ Désactiver l'activation au survol de la sourisShow book fold shadow
- Afficher l’ombre du pli du livre
+ Afficher l'ombre du pli du livre
@@ -512,12 +512,17 @@ Si aucun n’est actif, la touche Échap ne fait rien.
Lanczos (meilleure qualité)
-
+
+ Bicubic
+
+
+
+ Page FlowFlux des pages
-
+ GeneralGénéral
@@ -527,7 +532,7 @@ Si aucun n’est actif, la touche Échap ne fait rien.
Luminosité
-
+ Restart is neededRedémarrage nécessaire
@@ -557,22 +562,22 @@ Si aucun n’est actif, la touche Échap ne fait rien.
7z lib not found
- lib 7z introuvable
+ Bibliothèque 7z introuvableunable to load 7z lib from ./utils
- impossible de charger 7z depuis ./utils
+ impossible de charger la bibliothèque 7z depuis ./utils
-
+ Select custom coverSélectionnez une couverture personnalisée
-
+ Images (%1)
- Illustrations (%1)
+ Images (%1)
@@ -605,7 +610,7 @@ Si aucun n’est actif, la touche Échap ne fait rien.
i
- je
+ i
@@ -620,7 +625,7 @@ Si aucun n’est actif, la touche Échap ne fait rien.
Hold to flash the selected value in the UI (magenta / toggled / 0↔10). Releases restore the original.
- Maintenez enfoncé pour faire clignoter la valeur sélectionnée dans l'interface utilisateur (magenta / basculé / 0↔10). Les versions restaurent l'original.
+ Maintenez enfoncé pour faire clignoter la valeur sélectionnée dans l'interface (magenta / basculé / 0↔10). Le relâchement restaure l'original.
@@ -630,7 +635,7 @@ Si aucun n’est actif, la touche Échap ne fait rien.
Light
- Lumière
+ Clair
@@ -640,7 +645,7 @@ Si aucun n’est actif, la touche Échap ne fait rien.
ID:
- IDENTIFIANT:
+ ID :
@@ -650,7 +655,7 @@ Si aucun n’est actif, la touche Échap ne fait rien.
Variant:
- Variante:
+ Variante :
@@ -670,7 +675,7 @@ Si aucun n’est actif, la touche Échap ne fait rien.
Save and apply
- Enregistrer et postuler
+ Enregistrer et appliquer
@@ -700,7 +705,7 @@ Si aucun n’est actif, la touche Échap ne fait rien.
true
- vrai
+ true
@@ -708,7 +713,7 @@ Si aucun n’est actif, la touche Échap ne fait rien.
false
- FAUX
+ false
@@ -779,7 +784,7 @@ Si aucun n’est actif, la touche Échap ne fait rien.
Expected a JSON object.
- Attendu un objet JSON.
+ Un objet JSON était attendu.
@@ -787,13 +792,13 @@ Si aucun n’est actif, la touche Échap ne fait rien.
Page not available!
- Page non disponible !
+ Page non disponible !
- Press 'O' to open comic.
- Appuyez sur "O" pour ouvrir une bande dessinée.
+ Press 'O' to open a comic.
+ Appuyez sur « O » pour ouvrir une bande dessinée.
@@ -803,7 +808,7 @@ Si aucun n’est actif, la touche Échap ne fait rien.
Cover!
- Couverture!
+ Couverture !
@@ -823,12 +828,12 @@ Si aucun n’est actif, la touche Échap ne fait rien.
Last page!
- Dernière page!
+ Dernière page !
- Loading...please wait!
- Chargement... Patientez
+ Loading... please wait!
+ Chargement... Veuillez patienter !
@@ -836,7 +841,7 @@ Si aucun n’est actif, la touche Échap ne fait rien.
Presets:
- Réglages:
+ Réglages :
@@ -871,7 +876,7 @@ Si aucun n’est actif, la touche Échap ne fait rien.
Custom:
- Personnalisation:
+ Personnalisé :
@@ -896,17 +901,17 @@ Si aucun n’est actif, la touche Échap ne fait rien.
Zoom
- Agrandissement
+ ZoomY offset
- Axe Y
+ Décalage YZ offset
- Axe Z
+ Décalage Z
@@ -926,7 +931,7 @@ Si aucun n’est actif, la touche Échap ne fait rien.
Max angle
- Angle Maximum
+ Angle maximum
@@ -940,13 +945,13 @@ Si aucun n’est actif, la touche Échap ne fait rien.
- Use VSync (improve the image quality in fullscreen mode, worse performance)
- Utiliser VSync (Améliore la qualité d'image en mode plein écran, ralentit la performance)
+ Use VSync (improves image quality in fullscreen mode, but reduces performance)
+ Utiliser VSync (améliore la qualité d'image en plein écran, diminue les performances)Performance:
- Performance :
+ Performance :
@@ -989,7 +994,7 @@ Si aucun n’est actif, la touche Échap ne fait rien.
Clear
- Clair
+ Effacer
@@ -999,29 +1004,29 @@ Si aucun n’est actif, la touche Échap ne fait rien.
Save
- Sauvegarder
+ Enregistrer
-
-
-
+
+
+ Save current page
- Sauvegarder la page actuelle
+ Enregistrer la page actuelle
-
-
-
-
+
+
+
+ Extract page(s)
-
+ Extraire la ou les pagesExtract page(s) from the original source
-
+ Extraire la ou les pages depuis la source d'origine
@@ -1030,10 +1035,10 @@ Si aucun n’est actif, la touche Échap ne fait rien.
-
-
+
+ Open previous comic
- Ouvrir la bande dessiné précédente
+ Ouvrir la bande dessinée précédente
@@ -1042,8 +1047,8 @@ Si aucun n’est actif, la touche Échap ne fait rien.
-
-
+
+ Open next comicOuvrir la bande dessinée suivante
@@ -1054,8 +1059,8 @@ Si aucun n’est actif, la touche Échap ne fait rien.
-
-
+
+ Go to previous pageAller à la page précédente
@@ -1066,8 +1071,8 @@ Si aucun n’est actif, la touche Échap ne fait rien.
-
-
+
+ Go to next pageAller à la page suivante
@@ -1094,7 +1099,7 @@ Si aucun n’est actif, la touche Échap ne fait rien.
Show full size
- Plein écran
+ Afficher en taille réelle
@@ -1124,12 +1129,12 @@ Si aucun n’est actif, la touche Échap ne fait rien.
Zoom+
- Agrandir
+ Zoom+Zoom-
- R?duire
+ Zoom-
@@ -1159,7 +1164,7 @@ Si aucun n’est actif, la touche Échap ne fait rien.
Reverse reading order in double page mode
- Ordre de lecture inversée en mode double page
+ Ordre de lecture inversé en mode double page
@@ -1168,13 +1173,13 @@ Si aucun n’est actif, la touche Échap ne fait rien.
- Go to page ...
- Aller à la page ...
+ Go to page...
+ Aller à la page...Options
- Possibilités
+ Options
@@ -1190,17 +1195,19 @@ Si aucun n’est actif, la touche Échap ne fait rien.
Help, About YACReader
- Aide, à propos de YACReader
+ Aide, À propos de YACReader
+
+ Magnifying glassLoupeSwitch Magnifying glass
- Utiliser la loupe
+ Activer/désactiver la loupe
@@ -1240,7 +1247,7 @@ Si aucun n’est actif, la touche Échap ne fait rien.
Escape key: quit, or cancel the active mode
- Touche Échap : quitter ou annuler le mode actif
+ Touche Échap : quitter ou annuler le mode actif
@@ -1255,7 +1262,7 @@ Si aucun n’est actif, la touche Échap ne fait rien.
Show go to flow
- Afficher "Aller à Comic Flow"
+ Afficher « Aller à Comic Flow »
@@ -1271,7 +1278,7 @@ Si aucun n’est actif, la touche Échap ne fait rien.
Open recent
- Ouvrir récent
+ Récemment ouverts
@@ -1281,7 +1288,7 @@ Si aucun n’est actif, la touche Échap ne fait rien.
Edit
- Editer
+ Modifier
@@ -1299,222 +1306,217 @@ Si aucun n’est actif, la touche Échap ne fait rien.
Fenêtre
-
+ Open ComicOuvrir la bande dessinée
-
+ Comic files
- Bande dessinée
+ Fichiers de bandes dessinées
-
+ Open folder
- Ouvirir le dossier
+ Ouvrir le dossier
-
-
+
+ ComicsBandes dessinées
-
+ Toggle fullscreen modeBasculer en mode plein écran
-
+ Hide/show toolbarMasquer / afficher la barre d'outils
-
-
+
+ GeneralGénéral
-
+ Size up magnifying glassAugmenter la taille de la loupe
-
+ Size down magnifying glassRéduire la taille de la loupe
-
+ Zoom in magnifying glass
- Zoomer
+ Augmenter le zoom de la loupe
-
+ Zoom out magnifying glass
- Dézoomer
+ Diminuer le zoom de la loupe
-
+ Reset magnifying glassRéinitialiser la loupe
-
-
- Magnifiying glass
- Loupe
-
-
-
+ Toggle between fit to width and fit to heightBasculer entre adapter à la largeur et adapter à la hauteur
-
-
- Page adjustement
+
+
+ Page adjustment
+ Page adjustementAjustement de la page
-
+ The current page is not ready. Please try again later.
- La page actuelle n’est pas encore prête. Veuillez réessayer plus tard.
+ La page actuelle n'est pas encore prête. Veuillez réessayer plus tard.
-
+ Overwrite file?
-
+ Écraser le fichier ?
-
+ The file already exists. Do you want to overwrite it?
-
+ Le fichier existe déjà. Voulez-vous l'écraser ?
-
+ The current page could not be saved.
- La page actuelle n’a pas pu être enregistrée.
+ La page actuelle n'a pas pu être enregistrée.
-
+ The selected pages are not ready. Please try again later.Les pages sélectionnées ne sont pas encore prêtes. Veuillez réessayer plus tard.
-
+ The current page could not be extracted.
-
+ La page actuelle n'a pas pu être extraite.
-
+ Overwrite files?
-
+ Écraser les fichiers ?
-
+ Some files already exist. Do you want to overwrite them?
-
+ Certains fichiers existent déjà. Voulez-vous les écraser ?
-
+ Some pages could not be extracted.
-
+ Certaines pages n'ont pas pu être extraites.
-
+ Autoscroll downDéfilement automatique vers le bas
-
+ Autoscroll upDéfilement automatique vers le haut
-
+ Autoscroll forward, horizontal first
- Défilement automatique en avant, horizontal
+ Défilement automatique en avant, horizontal en premier
-
+ Autoscroll backward, horizontal first
- Défilement automatique en arrière horizontal
+ Défilement automatique en arrière, horizontal en premier
-
+ Autoscroll forward, vertical first
- Défilement automatique en avant, vertical
+ Défilement automatique en avant, vertical en premier
-
+ Autoscroll backward, vertical first
- Défilement automatique en arrière, verticak
+ Défilement automatique en arrière, vertical en premier
-
+ Move downDescendre
-
+ Move upMonter
-
+ Move leftDéplacer à gauche
-
+ Move rightDéplacer à droite
-
+ Go to the first pageAller à la première page
-
+ Go to the last pageAller à la dernière page
-
+ Offset double page to the leftDouble page décalée vers la gauche
-
+ Offset double page to the rightDouble page décalée à droite
-
-
+
+ ReadingLecture
-
+ There is a new version availableUne nouvelle version est disponible
-
+ Do you want to download the new version?
- Voulez-vous télécharger la nouvelle version?
+ Voulez-vous télécharger la nouvelle version ?
-
+ Remind me in 14 daysRappelez-moi dans 14 jours
-
+ Not nowPas maintenant
@@ -1524,12 +1526,12 @@ Si aucun n’est actif, la touche Échap ne fait rien.
Release notes are not available.
-
+ Les notes de version ne sont pas disponibles.Previous versions
-
+ Versions précédentes
@@ -1541,7 +1543,7 @@ Si aucun n’est actif, la touche Échap ne fait rien.
-
+ Click to overwriteCliquez pour remplacer
@@ -1555,9 +1557,9 @@ Si aucun n’est actif, la touche Échap ne fait rien.
-
-
-
+
+
+ Click to overwriteCliquez pour remplacer
@@ -1567,7 +1569,7 @@ Si aucun n’est actif, la touche Échap ne fait rien.
Save
- Sauvegarder
+ Enregistrer
@@ -1587,7 +1589,7 @@ Si aucun n’est actif, la touche Échap ne fait rien.
Customize the keyboard shortcuts used by the application.
- Personnalisez les raccourcis clavier utilisés par l’application.
+ Personnalisez les raccourcis clavier utilisés par l'application.
@@ -1600,7 +1602,7 @@ Si aucun n’est actif, la touche Échap ne fait rien.
Reset
- Remise à zéro
+ Réinitialiser
diff --git a/YACReader/yacreader_it.ts b/YACReader/yacreader_it.ts
index 9193c5ba3..7984c1737 100644
--- a/YACReader/yacreader_it.ts
+++ b/YACReader/yacreader_it.ts
@@ -24,22 +24,22 @@
Light
- Luce
+ ChiaroDark
- Buio
+ ScuroCustom
- Costume
+ PersonalizzatoRemove
- Rimuovere
+ Rimuovi
@@ -49,17 +49,17 @@
Light:
- Leggero:
+ Chiaro:Dark:
- Buio:
+ Scuro:Custom:
- Personalizzazione:
+ Personalizzato:
@@ -74,7 +74,7 @@
Theme editor
- Redattore del tema
+ Editor del tema
@@ -89,7 +89,7 @@
The current theme JSON could not be loaded.
- Impossibile caricare il tema corrente JSON.
+ Impossibile caricare il JSON del tema corrente.
@@ -145,7 +145,7 @@
- Lastest Page
+ Latest PageUltima Pagina
@@ -167,44 +167,44 @@
Restore defaults
- Resetta al default
+ Ripristina predefinitiShortcuts settings
- impostazione scorciatoie
+ Impostazioni scorciatoie
- The shortcut "%1" is already assigned to other function
- La scorciatoia "%1" è già assegnata ad un'altra funzione
+ The shortcut "%1" is already assigned to another function
+ La scorciatoia "%1" è già assegnata a un'altra funzione
- To change a shortcut, double click in the key combination and type the new keys.
- Per cambiare una scorciatoia doppio click sulla combinazione tasti e digita la nuova combinazione.
+ To change a shortcut, double-click the key combination and type the new keys.
+ Per cambiare una scorciatoia, fai doppio clic sulla combinazione di tasti e digita la nuova combinazione.FileComic
-
-
+
+ Format not supportedFormato non supportato
-
+ 7z not found7z non trovato
-
+ Unknown error opening the fileErrore sconosciuto aprendo il file
-
+ CRC error on page (%1): some of the pages will not be displayed correctlyErrore CRC alla pagina (%1): alcune pagine non saranno visualizzate correttamente
@@ -224,7 +224,7 @@
- Total pages :
+ Total pages: Pagine totali:
@@ -234,7 +234,7 @@
- Page :
+ Page: Pagina:
@@ -242,7 +242,7 @@
GoToFlowToolBar
- Page :
+ Page: Pagina:
@@ -261,7 +261,7 @@
Changelog
-
+ Registro modifiche
@@ -274,7 +274,7 @@
Gamma
- Valore gamma
+ Gamma
@@ -287,7 +287,7 @@
Percorso dei miei fumetti
-
+ Image adjustmentCorrezioni immagine
@@ -304,7 +304,7 @@
Image options
- Opzione immagine
+ Opzioni immagine
@@ -312,12 +312,12 @@
Contrasto
-
+ AppearanceAspetto
-
+ OptionsOpzioni
@@ -342,14 +342,14 @@
Cancella
-
+ Comics directory
- Cartella Fumetti
+ Cartella fumettiQuick Navigation Mode
- Modo navigazione rapida
+ Modalità navigazione rapida
@@ -388,7 +388,7 @@
- Scroll behaviour
+ Scroll behaviorComportamento di scorrimento
@@ -404,7 +404,7 @@
Use single scroll step to turn page
- Utilizzare un singolo passaggio di scorrimento per voltare pagina
+ Usa un singolo passaggio di scorrimento per voltare pagina
@@ -419,12 +419,12 @@
Use the Left/Right buttons to turn pages.
- Utilizzare i pulsanti Sinistra/Destra per girare le pagine.
+ Usa i pulsanti Sinistra/Destra per girare le pagine.
- Click left or right half of the screen to turn pages.
- Fare clic sulla metà sinistra o destra dello schermo per girare le pagine.
+ Click the left or right half of the screen to turn pages.
+ Clicca sulla metà sinistra o destra dello schermo per girare le pagine.
@@ -479,12 +479,12 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.
Disable mouse over activation
- Disabilita il mouse all'attivazione
+ Disabilita l'attivazione al passaggio del mouseShow book fold shadow
- Mostra l’ombra della piega del libro
+ Mostra l'ombra della piega del libro
@@ -494,7 +494,7 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.
Scaling method
- Metodo di scala
+ Metodo di ridimensionamento
@@ -512,12 +512,17 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.Lanczos (qualità migliore)
-
+
+ Bicubic
+
+
+
+ Page FlowFlusso pagine
-
+ GeneralGenerale
@@ -527,9 +532,9 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.Luminosità
-
+ Restart is needed
- Riavvio Necessario
+ Riavvio necessario
@@ -562,15 +567,15 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.
unable to load 7z lib from ./utils
- Impossibile caricare 7z da ./utils
+ Impossibile caricare la libreria 7z da ./utils
-
+ Select custom coverSeleziona la copertina personalizzata
-
+ Images (%1)Immagini (%1)
@@ -590,7 +595,7 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.
Theme Editor
- Redattore del tema
+ Editor del tema
@@ -605,7 +610,7 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.
i
- io
+ i
@@ -620,7 +625,7 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.
Hold to flash the selected value in the UI (magenta / toggled / 0↔10). Releases restore the original.
- Tieni premuto per far lampeggiare il valore selezionato nell'interfaccia utente (magenta / alternato / 0↔10). Le versioni ripristinano l'originale.
+ Tieni premuto per far lampeggiare il valore selezionato nell'interfaccia (magenta / alternato / 0↔10). Al rilascio viene ripristinato l'originale.
@@ -630,17 +635,17 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.
Light
- Luce
+ ChiaroDark
- Buio
+ ScuroID:
- Identificativo:
+ ID:
@@ -690,7 +695,7 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.
Double-click to edit color
- Fare doppio clic per modificare il colore
+ Fai doppio clic per modificare il colore
@@ -700,7 +705,7 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.
true
- VERO
+ true
@@ -708,17 +713,17 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.
false
- falso
+ falseDouble-click to toggle
- Fare doppio clic per attivare/disattivare
+ Fai doppio clic per attivare/disattivareDouble-click to edit value
- Fare doppio clic per modificare il valore
+ Fai doppio clic per modificare il valore
@@ -792,13 +797,13 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.
- Press 'O' to open comic.
- Premi "O" per aprire il fumettto.
+ Press 'O' to open a comic.
+ Premi "O" per aprire un fumetto.Error opening comic
- Errore nell'apertura
+ Errore nell'apertura del fumetto
@@ -827,8 +832,8 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.
- Loading...please wait!
- In caricamento...Attendi!
+ Loading... please wait!
+ Caricamento... Attendi!
@@ -871,7 +876,7 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.
Custom:
- Personalizzazione:
+ Personalizzato:
@@ -886,7 +891,7 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.
Cover gap
- Distanza Copertine
+ Distanza tra le copertine
@@ -896,17 +901,17 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.
Zoom
- Ingrandimento
+ ZoomY offset
- Compensazione Y
+ Offset YZ offset
- Compensazione Z
+ Offset Z
@@ -931,17 +936,17 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.
Low Performance
- Prestazioni Basse
+ Prestazioni basseHigh Performance
- Prestazioni Alte
+ Prestazioni alte
- Use VSync (improve the image quality in fullscreen mode, worse performance)
- Usa VSync (migliora la qualtà a tutto schermo, peggiora le prestazioni)
+ Use VSync (improves image quality in fullscreen mode, but reduces performance)
+ Usa VSync (migliora la qualità dell'immagine a schermo intero, peggiora le prestazioni)
@@ -959,7 +964,7 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.
Open a comic
- Apri un Fumetto
+ Apri un fumetto
@@ -974,7 +979,7 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.
Open image folder
- Apri la crettal immagini
+ Apri la cartella di immagini
@@ -994,7 +999,7 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.
Clear open recent list
- Svuota la lista degli aperti
+ Svuota l'elenco dei file recenti
@@ -1003,37 +1008,37 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.
-
-
-
+
+
+ Save current pageSalva la pagina corrente
-
-
-
-
+
+
+
+ Extract page(s)
-
+ Estrai pagina/eExtract page(s) from the original source
-
+ Estrai pagina/e dalla sorgente originalePrevious Comic
- Fumetto precendente
+ Fumetto precedente
-
-
+
+ Open previous comic
- Apri il fumetto precendente
+ Apri il fumetto precedente
@@ -1042,8 +1047,8 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.
-
-
+
+ Open next comicApri il prossimo fumetto
@@ -1054,8 +1059,8 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.
-
-
+
+ Go to previous pageVai alla pagina precedente
@@ -1066,10 +1071,10 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.
-
-
+
+ Go to next page
- Vai alla prossima Pagina
+ Vai alla prossima pagina
@@ -1084,7 +1089,7 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.
Fit Width
- Adatta Larghezza
+ Adatta larghezza
@@ -1094,7 +1099,7 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.
Show full size
- Mostra dimesioni reali
+ Mostra dimensioni reali
@@ -1114,7 +1119,7 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.
Reset zoom
- Resetta Zoom
+ Resetta zoom
@@ -1124,12 +1129,12 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.
Zoom+
- Aumenta
+ Zoom+Zoom-
- Riduci
+ Zoom-
@@ -1144,7 +1149,7 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.
Double page mode
- Modalita doppia pagina
+ Modalità doppia pagina
@@ -1159,7 +1164,7 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.
Reverse reading order in double page mode
- Ordine lettura inverso in modo doppia pagina
+ Ordine di lettura inverso in modalità doppia pagina
@@ -1168,8 +1173,8 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.
- Go to page ...
- Vai a Pagina ...
+ Go to page...
+ Vai a pagina...
@@ -1190,37 +1195,39 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.
Help, About YACReader
- Aiuto, crediti YACReader
+ Aiuto, informazioni su YACReader
+
+ Magnifying glassLente ingrandimentoSwitch Magnifying glass
- Passa a lente ingrandimento
+ Attiva/Disattiva lente d'ingrandimentoSet bookmark
- Imposta Segnalibro
+ Imposta segnalibroSet a bookmark on the current page
- Imposta segnalibro a pagina corrente
+ Imposta un segnalibro nella pagina correnteShow bookmarks
- Mostra segnalibro
+ Mostra segnalibriShow the bookmarks of the current comic
- Mostra il segnalibro del fumetto corrente
+ Mostra i segnalibri del fumetto corrente
@@ -1260,12 +1267,12 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.
Edit shortcuts
- Edita scorciatoie
+ Modifica scorciatoie&File
- &Documento
+ &File
@@ -1276,17 +1283,17 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.
File
- Documento
+ FileEdit
- Edita
+ ModificaView
- Mostra
+ Visualizza
@@ -1299,222 +1306,217 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.Finestra
-
+ Open Comic
- Apri Fumetto
+ Apri fumetto
-
+ Comic files
- File Fumetto
+ File di fumetti
-
+ Open folderApri cartella
-
-
+
+ Comics
- Fumetto
+ Fumetti
-
+ Toggle fullscreen modeAttiva/Disattiva schermo intero
-
+ Hide/show toolbar
- Mostra/Nascondi Barra strumenti
+ Mostra/Nascondi barra strumenti
-
-
+
+ GeneralGenerale
-
+ Size up magnifying glass
- Ingrandisci lente ingrandimento
+ Ingrandisci la lente d'ingrandimento
-
+ Size down magnifying glass
- Riduci lente ingrandimento
+ Riduci la lente d'ingrandimento
-
+ Zoom in magnifying glass
- Ingrandisci in lente di ingrandimento
+ Aumenta lo zoom della lente
-
+ Zoom out magnifying glass
- Riduci in lente di ingrandimento
+ Riduci lo zoom della lente
-
+ Reset magnifying glassReimposta la lente d'ingrandimento
-
-
- Magnifiying glass
- Lente ingrandimento
-
-
-
+ Toggle between fit to width and fit to height
- Passa tra adatta in larghezza ad altezza
+ Passa tra adatta alla larghezza e adatta all'altezza
-
-
- Page adjustement
- Correzioni di pagna
+
+
+ Page adjustment
+ Page adjustement
+ Regolazione della pagina
-
+ The current page is not ready. Please try again later.La pagina corrente non è ancora pronta. Riprova più tardi.
-
+ Overwrite file?
-
+ Sovrascrivere il file?
-
+ The file already exists. Do you want to overwrite it?
-
+ Il file esiste già. Vuoi sovrascriverlo?
-
+ The current page could not be saved.Non è stato possibile salvare la pagina corrente.
-
+ The selected pages are not ready. Please try again later.Le pagine selezionate non sono ancora pronte. Riprova più tardi.
-
+ The current page could not be extracted.
-
+ Non è stato possibile estrarre la pagina corrente.
-
+ Overwrite files?
-
+ Sovrascrivere i file?
-
+ Some files already exist. Do you want to overwrite them?
-
+ Alcuni file esistono già. Vuoi sovrascriverli?
-
+ Some pages could not be extracted.
-
+ Non è stato possibile estrarre alcune pagine.
-
+ Autoscroll down
- Autoscorri Giù
+ Scorrimento automatico in giù
-
+ Autoscroll up
- Autoscorri Sù
+ Scorrimento automatico in su
-
+ Autoscroll forward, horizontal first
- Autoscorri avanti, priorità Orizzontale
+ Scorrimento automatico in avanti, prima orizzontale
-
+ Autoscroll backward, horizontal first
- Autoscorri indietro, priorità Orizzontale
+ Scorrimento automatico indietro, prima orizzontale
-
+ Autoscroll forward, vertical first
- Autoscorri avanti, priorità Verticale
+ Scorrimento automatico in avanti, prima verticale
-
+ Autoscroll backward, vertical first
- Autoscorri indietro, priorità Verticale
+ Scorrimento automatico indietro, prima verticale
-
+ Move down
- Muovi Giù
+ Sposta in giù
-
+ Move up
- Muovi Sù
+ Sposta in su
-
+ Move left
- Muovi Sinistra
+ Sposta a sinistra
-
+ Move right
- Muovi Destra
+ Sposta a destra
-
+ Go to the first pageVai alla pagina iniziale
-
+ Go to the last pageVai all'ultima pagina
-
+ Offset double page to the leftDoppia pagina spostata a sinistra
-
+ Offset double page to the rightDoppia pagina spostata a destra
-
-
+
+ Reading
- Leggi
+ Lettura
-
+ There is a new version availableNuova versione disponibile
-
+ Do you want to download the new version?Vuoi scaricare la nuova versione?
-
+ Remind me in 14 daysRicordamelo in 14 giorni
-
+ Not nowNon ora
@@ -1524,12 +1526,12 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.
Release notes are not available.
-
+ Le note di rilascio non sono disponibili.Previous versions
-
+ Versioni precedenti
@@ -1537,11 +1539,11 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.
Restore to default
- Resetta al default
+ Ripristina predefinito
-
+ Click to overwriteClicca per sovrascrivere
@@ -1551,13 +1553,13 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.
Restore to default
- Resetta al default
+ Ripristina predefinito
-
-
-
+
+
+ Click to overwriteClicca per sovrascrivere
@@ -1572,12 +1574,12 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.
Cancel
- Cancella
+ AnnullaShortcuts
- Scorciatoia
+ Scorciatoie
@@ -1592,7 +1594,7 @@ Se non è attiva alcuna modalità, il tasto Esc non esegue alcuna azione.
Edit shortcuts
- Edita Scorciatoia
+ Modifica scorciatoie
diff --git a/YACReader/yacreader_ko.ts b/YACReader/yacreader_ko.ts
index 13dacf490..a86d11c7c 100644
--- a/YACReader/yacreader_ko.ts
+++ b/YACReader/yacreader_ko.ts
@@ -44,7 +44,7 @@
Remove this user-imported theme
- 사용자 가져온 이 테마 제거
+ 사용자가 가져온 이 테마 제거
@@ -129,7 +129,7 @@
BookmarksDialog
- Lastest Page
+ Latest Page마지막 페이지
@@ -166,8 +166,8 @@
- To change a shortcut, double click in the key combination and type the new keys.
- 단축키를 바꾸려면 키 조합을 더블 클릭하고 새 키를 입력하세요.
+ To change a shortcut, double-click the key combination and type the new keys.
+ 단축키를 바꾸려면 키 조합을 두 번 클릭하고 새 키를 입력하세요.
@@ -181,30 +181,30 @@
- The shortcut "%1" is already assigned to other function
+ The shortcut "%1" is already assigned to another function"%1" 단축키는 이미 다른 기능이 사용 중입니다FileComic
-
+ 7z not found7z를 찾을 수 없습니다
-
+ CRC error on page (%1): some of the pages will not be displayed correctly%1번 페이지에서 CRC 오류 발생: 일부 페이지가 올바르게 표시되지 않을 수 있습니다
-
+ Unknown error opening the file파일을 여는 중 알 수 없는 오류가 발생했습니다
-
-
+
+ Format not supported지원하지 않는 형식입니다
@@ -213,7 +213,7 @@
GoToDialog
- Page :
+ Page: 페이지:
@@ -229,7 +229,7 @@
- Total pages :
+ Total pages: 전체 페이지:
@@ -242,7 +242,7 @@
GoToFlowToolBar
- Page :
+ Page: 페이지:
@@ -266,7 +266,7 @@
Changelog
-
+ 변경 로그
@@ -292,17 +292,17 @@
지우기
-
+ General일반
-
+ Appearance외관
-
+ Options환경설정
@@ -358,7 +358,7 @@
- Scroll behaviour
+ Scroll behavior스크롤 동작
@@ -393,7 +393,7 @@
- Click left or right half of the screen to turn pages.
+ Click the left or right half of the screen to turn pages.화면 왼쪽 또는 오른쪽 절반을 클릭하여 페이지 넘김.
@@ -489,7 +489,7 @@ If none is active, Escape does nothing.
Enlarge images to fit width/height
- 작은 그림도 꽉차게 보기
+ 이미지를 폭/높이에 맞게 확대
@@ -519,35 +519,40 @@ If none is active, Escape does nothing.
Nearest (fast, low quality)
- 빠른 모드 (빠름, 저화질)
+ 최근접 (빠름, 저화질)Bilinear
- 보통 모드 (중간 품질)
+ BilinearLanczos (better quality)
- 고화질 모드 (더 좋은 화질)
+ Lanczos (더 좋은 화질)
-
- Page Flow
- 페이지 플로우
+
+ Bicubic
+
+ Page Flow
+ 페이지 흐름
+
+
+ Image adjustment이미지 조정
-
+ Restart is needed재시작이 필요합니다
-
+ Comics directory만화 폴더
@@ -565,12 +570,12 @@ If none is active, Escape does nothing.
./utils에서 7z 라이브러리를 불러올 수 없습니다
-
+ Select custom cover사용자 지정 표지 선택
-
+ Images (%1)이미지 (%1)
@@ -787,7 +792,7 @@ If none is active, Escape does nothing.
- Press 'O' to open comic.
+ Press 'O' to open a comic.'O'를 눌러 만화를 열어보세요.
@@ -812,7 +817,7 @@ If none is active, Escape does nothing.
- Loading...please wait!
+ Loading... please wait!불러오는 중... 잠시 기다려주세요!
@@ -896,7 +901,7 @@ If none is active, Escape does nothing.
Zoom
- 확대/축소
+ Zoom
@@ -940,8 +945,8 @@ If none is active, Escape does nothing.
- Use VSync (improve the image quality in fullscreen mode, worse performance)
- 수직 동기화 사용 (전체화면 결의 이미지 품질 향상, 성능 저하)
+ Use VSync (improves image quality in fullscreen mode, but reduces performance)
+ VSync 사용 (전체 화면 모드에서 이미지 품질 향상, 성능 저하)
@@ -1003,18 +1008,18 @@ If none is active, Escape does nothing.
-
-
-
+
+
+ Save current page현재 페이지 저장
-
-
-
-
+
+
+
+ Extract page(s)페이지 추출
@@ -1030,8 +1035,8 @@ If none is active, Escape does nothing.
-
-
+
+ Open previous comic이전 만화 열기
@@ -1042,8 +1047,8 @@ If none is active, Escape does nothing.
-
-
+
+ Open next comic다음 만화 열기
@@ -1054,8 +1059,8 @@ If none is active, Escape does nothing.
-
-
+
+ Go to previous page이전 페이지로 이동
@@ -1066,15 +1071,15 @@ If none is active, Escape does nothing.
-
-
+
+ Go to next page다음 페이지로 이동Fit Height
- 꽉차게 보기 (높이 맞춤)
+ 높이 맞춤
@@ -1084,7 +1089,7 @@ If none is active, Escape does nothing.
Fit Width
- 꽉차게 보기 (폭 맞춤)
+ 폭 맞춤
@@ -1099,7 +1104,7 @@ If none is active, Escape does nothing.
Fit to page
- 꽉차게 보기
+ 페이지에 맞춤
@@ -1114,22 +1119,22 @@ If none is active, Escape does nothing.
Reset zoom
- 확대/축소 초기화
+ Zoom 초기화Show zoom slider
- 확대/축소 슬라이더 보기
+ Zoom 슬라이더 보기Zoom+
- 확대+
+ Zoom+Zoom-
- 축소-
+ Zoom-
@@ -1168,7 +1173,7 @@ If none is active, Escape does nothing.
- Go to page ...
+ Go to page...페이지로 이동...
@@ -1194,6 +1199,8 @@ If none is active, Escape does nothing.
+
+ Magnifying glass돋보기
@@ -1299,222 +1306,217 @@ If none is active, Escape does nothing.
창
-
+ Open Comic만화 열기
-
+ Comic files만화 파일
-
+ Open folder폴더 열기
-
+ The current page is not ready. Please try again later.현재 페이지가 아직 준비되지 않았습니다. 나중에 다시 시도해 주세요.
-
+ Overwrite file?파일을 덮어쓰시겠습니까?
-
+ The file already exists. Do you want to overwrite it?파일이 이미 존재합니다. 덮어쓰시겠습니까?
-
+ The current page could not be saved.현재 페이지를 저장할 수 없습니다.
-
+ The selected pages are not ready. Please try again later.선택한 페이지가 아직 준비되지 않았습니다. 나중에 다시 시도해 주세요.
-
+ The current page could not be extracted.현재 페이지를 추출할 수 없습니다.
-
+ Overwrite files?파일을 덮어쓰시겠습니까?
-
+ Some files already exist. Do you want to overwrite them?일부 파일이 이미 존재합니다. 덮어쓰시겠습니까?
-
+ Some pages could not be extracted.일부 페이지를 추출할 수 없습니다.
-
-
+
+ Comics만화
-
-
+
+ General일반
-
-
- Magnifiying glass
- 돋보기
-
-
-
-
- Page adjustement
+
+
+ Page adjustment
+ Page adjustement페이지 조정
-
-
+
+ Reading읽기
-
+ Toggle fullscreen mode
- 전체화면 전환
+ 전체 화면 전환
-
+ Hide/show toolbar도구 모음 표시/숨김
-
+ Size up magnifying glass돋보기 크게
-
+ Size down magnifying glass돋보기 작게
-
+ Zoom in magnifying glass
- 돋보기 확대
+ 돋보기 Zoom 확대
-
+ Zoom out magnifying glass
- 돋보기 축소
+ 돋보기 Zoom 축소
-
+ Reset magnifying glass돋보기 초기화
-
+ Toggle between fit to width and fit to height폭 맞춤 / 높이 맞춤 전환
-
+ Autoscroll down아래로 자동 스크롤
-
+ Autoscroll up위로 자동 스크롤
-
+ Autoscroll forward, horizontal first
- 세로 우선으로 정방향 자동 스크롤
+ 가로 우선으로 정방향 자동 스크롤
-
+ Autoscroll backward, horizontal first
- 가로 우선으로 정방향 자동 스크롤
+ 가로 우선으로 역방향 자동 스크롤
-
+ Autoscroll forward, vertical first
- 세로 우선으로 역방향 자동 스크롤
+ 세로 우선으로 정방향 자동 스크롤
-
+ Autoscroll backward, vertical first
- 가로 우선으로 역방향 자동 스크롤
+ 세로 우선으로 역방향 자동 스크롤
-
+ Move down아래로 이동
-
+ Move up위로 이동
-
+ Move left왼쪽으로 이동
-
+ Move right오른쪽으로 이동
-
+ Go to the first page첫 페이지로 이동
-
+ Go to the last page마지막 페이지로 이동
-
+ Offset double page to the left두 페이지 왼쪽으로 이동
-
+ Offset double page to the right두 페이지 오른쪽으로 이동
-
+ There is a new version available
- 새 버전을 내려받으시겠습니까?
+ 새 버전이 있습니다
-
+ Do you want to download the new version?새 버전을 내려받으시겠습니까?
-
+ Remind me in 14 days14일 후에 다시 알림
-
+ Not now나중에
@@ -1536,7 +1538,7 @@ If none is active, Escape does nothing.
YACReaderFieldEdit
-
+ Click to overwrite클릭해서 덮어쓰기
@@ -1550,9 +1552,9 @@ If none is active, Escape does nothing.
YACReaderFieldPlainTextEdit
-
-
-
+
+
+ Click to overwrite클릭해서 덮어쓰기
diff --git a/YACReader/yacreader_local_client.cpp b/YACReader/yacreader_local_client.cpp
index 6cb6cb922..4b2061976 100644
--- a/YACReader/yacreader_local_client.cpp
+++ b/YACReader/yacreader_local_client.cpp
@@ -28,7 +28,7 @@ void YACReaderLocalClient::readMessage()
}
#include
-bool YACReaderLocalClient::requestComicInfo(quint64 libraryId, ComicDB &comic, QList &siblings, OpenComicSource source)
+bool YACReaderLocalClient::requestComicInfo(quint64 libraryId, ComicDB &comic, QList &siblings, qint32 &position, OpenComicSource source)
{
localSocket->connectToServer(YACREADERLIBRARY_GUID);
if (localSocket->isOpen()) {
@@ -107,6 +107,7 @@ bool YACReaderLocalClient::requestComicInfo(quint64 libraryId, ComicDB &comic, Q
QDataStream dataStream(data);
dataStream >> comic;
dataStream >> siblings;
+ dataStream >> position;
localSocket->close();
return true;
} else {
diff --git a/YACReader/yacreader_local_client.h b/YACReader/yacreader_local_client.h
index d4472b0b9..b23a281f4 100644
--- a/YACReader/yacreader_local_client.h
+++ b/YACReader/yacreader_local_client.h
@@ -18,7 +18,8 @@ class YACReaderLocalClient : public QObject
void finished();
public slots:
void readMessage();
- bool requestComicInfo(quint64 libraryId, ComicDB &comic, QList &siblings, YACReader::OpenComicSource source);
+ // position: the index of the opened comic in siblings, -1 if it is not there
+ bool requestComicInfo(quint64 libraryId, ComicDB &comic, QList &siblings, qint32 &position, YACReader::OpenComicSource source);
bool sendComicInfo(quint64 libraryId, ComicDB &comic);
bool sendComicInfo(quint64 libraryId, ComicDB &comic, qulonglong nextComicId);
diff --git a/YACReader/yacreader_nl.ts b/YACReader/yacreader_nl.ts
index 62a8a0761..c94e5a2ec 100644
--- a/YACReader/yacreader_nl.ts
+++ b/YACReader/yacreader_nl.ts
@@ -44,7 +44,7 @@
Remove this user-imported theme
- Verwijder dit door de gebruiker geïmporteerde thema
+ Dit door de gebruiker geïmporteerde thema verwijderen
@@ -141,11 +141,11 @@
Click on any image to go to the bookmark
- Klik op een afbeelding om naar de bladwijzer te gaan
+ Klik op een afbeelding om naar de bladwijzer te gaan
- Lastest Page
+ Latest PageLaatste Pagina
@@ -154,7 +154,7 @@
Loading page %1
- Pagina laden %1
+ Pagina %1 laden
@@ -166,45 +166,45 @@
- To change a shortcut, double click in the key combination and type the new keys.
- Om een snelkoppeling te wijzigen, dubbelklikt u op de toetsencombinatie en typt u de nieuwe toetsen.
+ To change a shortcut, double-click the key combination and type the new keys.
+ Om een sneltoets te wijzigen, dubbelklikt u op de toetsencombinatie en typt u de nieuwe toetsen.Shortcuts settings
- Instellingen voor snelkoppelingen
+ Instellingen voor sneltoetsenShortcut in use
- Snelkoppeling in gebruik
+ Sneltoets in gebruik
- The shortcut "%1" is already assigned to other function
+ The shortcut "%1" is already assigned to another functionDe sneltoets "%1" is al aan een andere functie toegewezenFileComic
-
+ 7z not found
- 7Z Archiefbestand niet gevonden
+ 7z niet gevonden
-
+ CRC error on page (%1): some of the pages will not be displayed correctlyCRC-fout op pagina (%1): sommige pagina's worden niet correct weergegeven
-
+ Unknown error opening the fileOnbekende fout bij het openen van het bestand
-
-
+
+ Format not supportedFormaat niet ondersteund
@@ -214,7 +214,7 @@
Go To
- Ga Naar
+ Ga naar
@@ -224,8 +224,8 @@
- Total pages :
- Totaal aantal pagina's :
+ Total pages:
+ Totaal aantal pagina's:
@@ -234,16 +234,16 @@
- Page :
- Pagina :
+ Page:
+ Pagina: GoToFlowToolBar
- Page :
- Pagina :
+ Page:
+ Pagina:
@@ -261,7 +261,7 @@
Changelog
-
+ Wijzigingslogboek
@@ -274,12 +274,12 @@
Gamma
- Gammawaarde
+ GammaReset
- Standaardwaarden terugzetten
+ Resetten
@@ -309,7 +309,7 @@
Bilinear
- Bilineair
+ Bilinear
@@ -317,7 +317,12 @@
Lanczos (betere kwaliteit)
-
+
+ Bicubic
+
+
+
+ Image adjustmentBeeldaanpassing
@@ -334,20 +339,20 @@
Image options
- Afbeelding opties
+ AfbeeldingsoptiesContrast
- Contrastwaarde
+ Contrast
-
+ Appearance
- Verschijning
+ Uiterlijk
-
+ OptionsOpties
@@ -369,12 +374,12 @@
Clear
- Duidelijk
+ Wissen
-
+ Comics directory
- Strips map
+ Stripmap
@@ -382,12 +387,12 @@
Achtergrondkleur
-
+ Page Flow
- Omslagbrowser
+ Pagina-flow
-
+ GeneralAlgemeen
@@ -397,7 +402,7 @@
Helderheid
-
+ Restart is neededHerstart is nodig
@@ -438,7 +443,7 @@
- Scroll behaviour
+ Scroll behaviorScrollgedrag
@@ -473,7 +478,7 @@
- Click left or right half of the screen to turn pages.
+ Click the left or right half of the screen to turn pages.Klik op de linker- of rechterhelft van het scherm om pagina's om te slaan.
@@ -534,7 +539,7 @@ Als geen enkele modus actief is, doet de Escape-toets niets.
Fit options
- Pas opties
+ Aanpassingsopties
@@ -565,12 +570,12 @@ Als geen enkele modus actief is, doet de Escape-toets niets.
kan 7z lib niet laden vanuit ./utils
-
+ Select custom cover
- Selecteer een aangepaste omslag
+ Selecteer een aangepast omslag
-
+ Images (%1)Afbeeldingen (%1)
@@ -605,7 +610,7 @@ Als geen enkele modus actief is, doet de Escape-toets niets.
i
- ?
+ i
@@ -625,7 +630,7 @@ Als geen enkele modus actief is, doet de Escape-toets niets.
Search…
- Zoekopdracht…
+ Zoeken…
@@ -640,7 +645,7 @@ Als geen enkele modus actief is, doet de Escape-toets niets.
ID:
- Identiteitskaart:
+ ID:
@@ -650,7 +655,7 @@ Als geen enkele modus actief is, doet de Escape-toets niets.
Variant:
- Variatie:
+ Variant:
@@ -660,7 +665,7 @@ Als geen enkele modus actief is, doet de Escape-toets niets.
Parameter
- Instelwaarde
+ Parameter
@@ -700,7 +705,7 @@ Als geen enkele modus actief is, doet de Escape-toets niets.
true
- WAAR
+ true
@@ -708,7 +713,7 @@ Als geen enkele modus actief is, doet de Escape-toets niets.
false
- vals
+ false
@@ -787,8 +792,8 @@ Als geen enkele modus actief is, doet de Escape-toets niets.
- Press 'O' to open comic.
- Druk 'O' om een strip te openen.
+ Press 'O' to open a comic.
+ Druk op 'O' om een strip te openen.
@@ -812,8 +817,8 @@ Als geen enkele modus actief is, doet de Escape-toets niets.
- Loading...please wait!
- Inladen...even wachten!
+ Loading... please wait!
+ Inladen... even wachten!
@@ -846,7 +851,7 @@ Als geen enkele modus actief is, doet de Escape-toets niets.
Stripe look
- Brede band
+ Brede band
@@ -886,7 +891,7 @@ Als geen enkele modus actief is, doet de Escape-toets niets.
Cover gap
- Ruimte tss Omslag
+ Ruimte tussen omslagen
@@ -896,22 +901,22 @@ Als geen enkele modus actief is, doet de Escape-toets niets.
Zoom
- Vergroting
+ ZoomY offset
- Y-positie
+ Y-verschuivingZ offset
- Z- positie
+ Z-verschuivingCover Angle
- Omslag hoek
+ Omslaghoek
@@ -940,8 +945,8 @@ Als geen enkele modus actief is, doet de Escape-toets niets.
- Use VSync (improve the image quality in fullscreen mode, worse performance)
- Gebruik VSync (verbetering van de beeldkwaliteit in de modus volledig scherm, slechtere prestatie)
+ Use VSync (improves image quality in fullscreen mode, but reduces performance)
+ Gebruik VSync (verbetert de beeldkwaliteit in de modus volledig scherm, maar verlaagt de prestaties)
@@ -959,12 +964,12 @@ Als geen enkele modus actief is, doet de Escape-toets niets.
Open a comic
- Open een strip
+ Een strip openenNew instance
- Nieuw exemplaar
+ Nieuwe instantie
@@ -974,54 +979,54 @@ Als geen enkele modus actief is, doet de Escape-toets niets.
Open image folder
- Open afbeeldings map
+ Afbeeldingenmap openenOpen latest comic
- Open de nieuwste strip
+ Laatst geopende strip openenOpen the latest comic opened in the previous reading session
- Open de nieuwste strip die in de vorige leessessie is geopend
+ De laatst geopende strip van de vorige leessessie openenClear
- Duidelijk
+ WissenClear open recent list
- Wis geopende recente lijst
+ Lijst met recent geopende bestanden wissenSave
- Bewaar
+ Opslaan
-
-
-
+
+
+ Save current page
- Bewaren huidige pagina
+ Huidige pagina opslaan
-
-
-
-
+
+
+
+ Extract page(s)
-
+ Pagina('s) extraherenExtract page(s) from the original source
-
+ Pagina('s) uit de oorspronkelijke bron extraheren
@@ -1030,8 +1035,8 @@ Als geen enkele modus actief is, doet de Escape-toets niets.
-
-
+
+ Open previous comicOpen de vorige strip
@@ -1042,8 +1047,8 @@ Als geen enkele modus actief is, doet de Escape-toets niets.
-
-
+
+ Open next comicOpen volgende strip
@@ -1054,8 +1059,8 @@ Als geen enkele modus actief is, doet de Escape-toets niets.
-
-
+
+ Go to previous pageGa naar de vorige pagina
@@ -1066,15 +1071,15 @@ Als geen enkele modus actief is, doet de Escape-toets niets.
-
-
+
+ Go to next pageGa naar de volgende paginaFit Height
- Geschikte hoogte
+ Hoogte aanpassen
@@ -1084,7 +1089,7 @@ Als geen enkele modus actief is, doet de Escape-toets niets.
Fit Width
- Vensterbreedte aanpassen
+ Breedte aanpassen
@@ -1094,7 +1099,7 @@ Als geen enkele modus actief is, doet de Escape-toets niets.
Show full size
- Volledig Scherm
+ Op ware grootte tonen
@@ -1124,32 +1129,32 @@ Als geen enkele modus actief is, doet de Escape-toets niets.
Zoom+
- Inzoomen
+ Zoom+Zoom-
- Uitzoomen
+ Zoom-Rotate image to the left
- Links omdraaien
+ Afbeelding naar links draaienRotate image to the right
- Rechts omdraaien
+ Afbeelding naar rechts draaienDouble page mode
- Dubbele bladzijde modus
+ Modus met dubbele paginaSwitch to double page mode
- Naar dubbele bladzijde modus
+ Overschakelen naar modus met dubbele pagina
@@ -1164,12 +1169,12 @@ Als geen enkele modus actief is, doet de Escape-toets niets.
Go To
- Ga Naar
+ Ga naar
- Go to page ...
- Ga naar bladzijde ...
+ Go to page...
+ Ga naar pagina...
@@ -1194,13 +1199,15 @@ Als geen enkele modus actief is, doet de Escape-toets niets.
+
+ Magnifying glassVergrootglasSwitch Magnifying glass
- Overschakelen naar Vergrootglas
+ Vergrootglas in-/uitschakelen
@@ -1250,7 +1257,7 @@ Als geen enkele modus actief is, doet de Escape-toets niets.
Show Dictionary
- Woordenlijst weergeven
+ Woordenboek weergeven
@@ -1260,7 +1267,7 @@ Als geen enkele modus actief is, doet de Escape-toets niets.
Edit shortcuts
- Snelkoppelingen bewerken
+ Sneltoetsen bewerken
@@ -1291,230 +1298,225 @@ Als geen enkele modus actief is, doet de Escape-toets niets.
Go
- Gaan
+ Ga naarWindow
- Raam
+ Venster
-
+ Open Comic
- Open een Strip
+ Strip openen
-
+ Comic files
- Strip bestanden
+ Stripbestanden
-
+ Open folder
- Open een Map
+ Map openen
-
-
+
+ ComicsStrips
-
+ Toggle fullscreen mode
- Schakel de modus Volledig scherm in
+ Modus Volledig scherm in-/uitschakelen
-
+ Hide/show toolbarWerkbalk verbergen/tonen
-
-
+
+ GeneralAlgemeen
-
+ Size up magnifying glassVergrootglas vergroten
-
+ Size down magnifying glassVergrootglas kleiner maken
-
+ Zoom in magnifying glass
- Zoom in vergrootglas
+ Zoom van vergrootglas vergroten
-
+ Zoom out magnifying glass
- Uitzoomen vergrootglas
+ Zoom van vergrootglas verkleinen
-
+ Reset magnifying glassVergrootglas opnieuw instellen
-
-
- Magnifiying glass
- Vergrootglas
-
-
-
+ Toggle between fit to width and fit to heightSchakel tussen Aanpassen aan breedte en Aanpassen aan hoogte
-
-
- Page adjustement
+
+
+ Page adjustment
+ Page adjustementPagina-aanpassing
-
+ The current page is not ready. Please try again later.De huidige pagina is nog niet klaar. Probeer het later opnieuw.
-
+ Overwrite file?
-
+ Bestand overschrijven?
-
+ The file already exists. Do you want to overwrite it?
-
+ Het bestand bestaat al. Wilt u het overschrijven?
-
+ The current page could not be saved.De huidige pagina kon niet worden opgeslagen.
-
+ The selected pages are not ready. Please try again later.
- De geselecteerde pagina’s zijn nog niet klaar. Probeer het later opnieuw.
+ De geselecteerde pagina's zijn nog niet klaar. Probeer het later opnieuw.
-
+ The current page could not be extracted.
-
+ De huidige pagina kon niet worden geëxtraheerd.
-
+ Overwrite files?
-
+ Bestanden overschrijven?
-
+ Some files already exist. Do you want to overwrite them?
-
+ Sommige bestanden bestaan al. Wilt u ze overschrijven?
-
+ Some pages could not be extracted.
-
+ Sommige pagina's konden niet worden geëxtraheerd.
-
+ Autoscroll downAutomatisch naar beneden scrollen
-
+ Autoscroll upAutomatisch omhoog scrollen
-
+ Autoscroll forward, horizontal firstAutomatisch vooruit scrollen, eerst horizontaal
-
+ Autoscroll backward, horizontal firstAutomatisch achteruit scrollen, eerst horizontaal
-
+ Autoscroll forward, vertical firstAutomatisch vooruit scrollen, eerst verticaal
-
+ Autoscroll backward, vertical firstAutomatisch achteruit scrollen, eerst verticaal
-
+ Move downGa naar beneden
-
+ Move upGa omhoog
-
+ Move leftGa naar links
-
+ Move rightGa naar rechts
-
+ Go to the first pageGa naar de eerste pagina
-
+ Go to the last pageGa naar de laatste pagina
-
+ Offset double page to the left
- Dubbele pagina naar links verschoven
+ Dubbele pagina naar links verschuiven
-
+ Offset double page to the right
- Offset dubbele pagina naar rechts
+ Dubbele pagina naar rechts verschuiven
-
-
+
+ Reading
- Lezing
+ Lezen
-
+ There is a new version availableEr is een nieuwe versie beschikbaar
-
+ Do you want to download the new version?Wilt u de nieuwe versie downloaden?
-
+ Remind me in 14 daysHerinner mij er over 14 dagen aan
-
+ Not nowNiet nu
@@ -1524,12 +1526,12 @@ Als geen enkele modus actief is, doet de Escape-toets niets.
Release notes are not available.
-
+ Releaseopmerkingen zijn niet beschikbaar.Previous versions
-
+ Vorige versies
@@ -1541,7 +1543,7 @@ Als geen enkele modus actief is, doet de Escape-toets niets.
-
+ Click to overwriteKlik hier om te overschrijven
@@ -1555,9 +1557,9 @@ Als geen enkele modus actief is, doet de Escape-toets niets.
-
-
-
+
+
+ Click to overwriteKlik hier om te overschrijven
@@ -1567,7 +1569,7 @@ Als geen enkele modus actief is, doet de Escape-toets niets.
Save
- Bewaar
+ Opslaan
@@ -1587,12 +1589,12 @@ Als geen enkele modus actief is, doet de Escape-toets niets.
Edit shortcuts
- Snelkoppelingen bewerken
+ Sneltoetsen bewerkenShortcuts
- Snelkoppelingen
+ Sneltoetsen
@@ -1600,7 +1602,7 @@ Als geen enkele modus actief is, doet de Escape-toets niets.
Reset
- Standaardwaarden terugzetten
+ Resetten
@@ -1619,7 +1621,7 @@ Als geen enkele modus actief is, doet de Escape-toets niets.
clear
- duidelijk
+ wissen
diff --git a/YACReader/yacreader_pt.ts b/YACReader/yacreader_pt.ts
index dcda1449c..685a47c7e 100644
--- a/YACReader/yacreader_pt.ts
+++ b/YACReader/yacreader_pt.ts
@@ -24,7 +24,7 @@
Light
- Luz
+ Claro
@@ -44,12 +44,12 @@
Remove this user-imported theme
- Remova este tema importado pelo usuário
+ Remover este tema importado pelo usuárioLight:
- Luz:
+ Claro:
@@ -79,7 +79,7 @@
Open Theme Editor...
- Abra o Editor de Tema...
+ Abrir editor de tema...
@@ -89,7 +89,7 @@
The current theme JSON could not be loaded.
- O tema atual JSON não pôde ser carregado.
+ Não foi possível carregar o JSON do tema atual.
@@ -145,7 +145,7 @@
- Lastest Page
+ Latest PageÚltima Página
@@ -166,7 +166,7 @@
- To change a shortcut, double click in the key combination and type the new keys.
+ To change a shortcut, double-click the key combination and type the new keys.Para alterar um atalho, clique duas vezes na combinação de teclas e digite as novas teclas.
@@ -181,30 +181,30 @@
- The shortcut "%1" is already assigned to other function
+ The shortcut "%1" is already assigned to another functionO atalho "%1" já está atribuído a outra funçãoFileComic
-
+ 7z not found7z não encontrado
-
+ CRC error on page (%1): some of the pages will not be displayed correctlyErro CRC na página (%1): algumas páginas não serão exibidas corretamente
-
+ Unknown error opening the fileErro desconhecido ao abrir o arquivo
-
-
+
+ Format not supportedFormato não suportado
@@ -224,8 +224,8 @@
- Total pages :
- Total de páginas :
+ Total pages:
+ Total de páginas:
@@ -234,16 +234,16 @@
- Page :
- Página :
+ Page:
+ Página: GoToFlowToolBar
- Page :
- Página :
+ Page:
+ Página:
@@ -261,7 +261,7 @@
Changelog
-
+ Registro de alterações
@@ -274,7 +274,7 @@
My comics path
- Meu caminho de quadrinhos
+ Caminho dos meus quadrinhos
@@ -282,12 +282,12 @@
Tamanho de "Ir para Comic Flow"
-
+ AppearanceAparência
-
+ OptionsOpções
@@ -309,22 +309,22 @@
Clear
- Claro
+ Limpar
-
+ Comics directory
- Diretório de quadrinhos
+ Pasta de quadrinhos
-
+ Restart is needed
- Reiniciar é necessário
+ É necessário reiniciarDisplay
- Mostrar
+ Exibição
@@ -363,28 +363,28 @@
- Scroll behaviour
+ Scroll behaviorComportamento de rolagemDisable scroll animations and smooth scrolling
- Desative animações de rolagem e rolagem suave
+ Desativar animações de rolagem e rolagem suaveDo not turn page using scroll
- Não vire a página usando scroll
+ Não virar a página ao rolarUse single scroll step to turn page
- Use uma única etapa de rolagem para virar a página
+ Usar um único passo de rolagem para virar a páginaMouse mode
- Modo mouse
+ Modo do mouse
@@ -398,7 +398,7 @@
- Click left or right half of the screen to turn pages.
+ Click the left or right half of the screen to turn pages.Clique na metade esquerda ou direita da tela para virar as páginas.
@@ -437,7 +437,7 @@ If none is active, Escape does nothing.
1. Lupa
2. Dicionário
3. Barra Ir para a página
-4. Ecrã inteiro
+4. Tela cheia
Se nenhum estiver ativo, a tecla Escape não faz nada.
@@ -459,7 +459,7 @@ Se nenhum estiver ativo, a tecla Escape não faz nada.
Disable mouse over activation
- Desativar ativação do mouse sobre
+ Desativar ativação ao passar o mouse
@@ -479,7 +479,7 @@ Se nenhum estiver ativo, a tecla Escape não faz nada.
Reset
- Reiniciar
+ Redefinir
@@ -494,7 +494,7 @@ Se nenhum estiver ativo, a tecla Escape não faz nada.
Enlarge images to fit width/height
- Amplie as imagens para caber na largura/altura
+ Ampliar imagens para caber na largura/altura
@@ -529,7 +529,7 @@ Se nenhum estiver ativo, a tecla Escape não faz nada.
Bilinear
- Interpola??o bilinear
+ Bilinear
@@ -537,17 +537,22 @@ Se nenhum estiver ativo, a tecla Escape não faz nada.
Lanczos (melhor qualidade)
-
- General
- Em geral
+
+ Bicubic
+
+ General
+ Geral
+
+
+ Page FlowFluxo de página
-
+ Image adjustmentAjuste de imagem
@@ -562,15 +567,15 @@ Se nenhum estiver ativo, a tecla Escape não faz nada.
unable to load 7z lib from ./utils
- não é possível carregar 7z lib de ./utils
+ não foi possível carregar a biblioteca 7z de ./utils
-
+ Select custom coverSelecione a capa personalizada
-
+ Images (%1)Imagens (%1)
@@ -605,7 +610,7 @@ Se nenhum estiver ativo, a tecla Escape não faz nada.
i
- eu
+ i
@@ -620,7 +625,7 @@ Se nenhum estiver ativo, a tecla Escape não faz nada.
Hold to flash the selected value in the UI (magenta / toggled / 0↔10). Releases restore the original.
- Segure para piscar o valor selecionado na UI (magenta/alternado/0↔10). Os lançamentos restauram o original.
+ Segure para destacar o valor selecionado na interface (magenta / alternado / 0↔10). Ao soltar, o original é restaurado.
@@ -630,7 +635,7 @@ Se nenhum estiver ativo, a tecla Escape não faz nada.
Light
- Luz
+ Claro
@@ -640,7 +645,7 @@ Se nenhum estiver ativo, a tecla Escape não faz nada.
ID:
- EU IA:
+ ID:
@@ -700,7 +705,7 @@ Se nenhum estiver ativo, a tecla Escape não faz nada.
true
- verdadeiro
+ true
@@ -708,7 +713,7 @@ Se nenhum estiver ativo, a tecla Escape não faz nada.
false
- falso
+ false
@@ -779,7 +784,7 @@ Se nenhum estiver ativo, a tecla Escape não faz nada.
Expected a JSON object.
- Esperava um objeto JSON.
+ Era esperado um objeto JSON.
@@ -787,12 +792,12 @@ Se nenhum estiver ativo, a tecla Escape não faz nada.
- Press 'O' to open comic.
+ Press 'O' to open a comic.Pressione 'O' para abrir um quadrinho.
- Loading...please wait!
+ Loading... please wait!Carregando... por favor, aguarde!
@@ -823,7 +828,7 @@ Se nenhum estiver ativo, a tecla Escape não faz nada.
Cover!
- Cobrir!
+ Capa!
@@ -846,12 +851,12 @@ Se nenhum estiver ativo, a tecla Escape não faz nada.
Stripe look
- Olhar lista
+ Aparência em faixasOverlapped Stripe look
- Olhar lista sobreposta
+ Aparência em faixas sobrepostas
@@ -886,17 +891,17 @@ Se nenhum estiver ativo, a tecla Escape não faz nada.
Cover gap
- Cubra a lacuna
+ Espaço entre capasCentral gap
- Lacuna central
+ Espaço centralZoom
- Amplia??o
+ Zoom
@@ -911,7 +916,7 @@ Se nenhum estiver ativo, a tecla Escape não faz nada.
Cover Angle
- Ângulo de cobertura
+ Ângulo da capa
@@ -940,8 +945,8 @@ Se nenhum estiver ativo, a tecla Escape não faz nada.
- Use VSync (improve the image quality in fullscreen mode, worse performance)
- Use VSync (melhora a qualidade da imagem em modo tela cheia, pior desempenho)
+ Use VSync (improves image quality in fullscreen mode, but reduces performance)
+ Usar VSync (melhora a qualidade da imagem em modo tela cheia, mas reduz o desempenho)
@@ -974,27 +979,27 @@ Se nenhum estiver ativo, a tecla Escape não faz nada.
Open image folder
- Abra a pasta de imagens
+ Abrir pasta de imagensOpen latest comic
- Abra o último quadrinho
+ Abrir o último quadrinhoOpen the latest comic opened in the previous reading session
- Abra o último quadrinho aberto na sessão de leitura anterior
+ Abrir o último quadrinho aberto na sessão de leitura anteriorClear
- Claro
+ LimparClear open recent list
- Limpar lista recente aberta
+ Limpar lista de abertos recentemente
@@ -1003,25 +1008,25 @@ Se nenhum estiver ativo, a tecla Escape não faz nada.
-
-
-
+
+
+ Save current pageSalvar página atual
-
-
-
-
+
+
+
+ Extract page(s)
-
+ Extrair página(s)Extract page(s) from the original source
-
+ Extrair página(s) da fonte original
@@ -1030,8 +1035,8 @@ Se nenhum estiver ativo, a tecla Escape não faz nada.
-
-
+
+ Open previous comicAbrir quadrinho anterior
@@ -1042,8 +1047,8 @@ Se nenhum estiver ativo, a tecla Escape não faz nada.
-
-
+
+ Open next comicAbrir próximo quadrinho
@@ -1054,8 +1059,8 @@ Se nenhum estiver ativo, a tecla Escape não faz nada.
-
-
+
+ Go to previous pageIr para a página anterior
@@ -1066,8 +1071,8 @@ Se nenhum estiver ativo, a tecla Escape não faz nada.
-
-
+
+ Go to next pageIr para a próxima página
@@ -1094,7 +1099,7 @@ Se nenhum estiver ativo, a tecla Escape não faz nada.
Show full size
- Mostrar tamanho grande
+ Mostrar tamanho original
@@ -1124,12 +1129,12 @@ Se nenhum estiver ativo, a tecla Escape não faz nada.
Zoom+
- Ampliar
+ Zoom+Zoom-
- Reduzir
+ Zoom-
@@ -1144,12 +1149,12 @@ Se nenhum estiver ativo, a tecla Escape não faz nada.
Double page mode
- Modo dupla página
+ Modo de página duplaSwitch to double page mode
- Alternar para o modo dupla página
+ Alternar para o modo de página dupla
@@ -1168,7 +1173,7 @@ Se nenhum estiver ativo, a tecla Escape não faz nada.
- Go to page ...
+ Go to page...Ir para a página...
@@ -1194,6 +1199,8 @@ Se nenhum estiver ativo, a tecla Escape não faz nada.
+
+ Magnifying glassLupa
@@ -1225,7 +1232,7 @@ Se nenhum estiver ativo, a tecla Escape não faz nada.
Show keyboard shortcuts
- Mostrar teclas de atalhos
+ Mostrar atalhos de teclado
@@ -1299,222 +1306,217 @@ Se nenhum estiver ativo, a tecla Escape não faz nada.
Janela
-
+ Open ComicAbrir Quadrinho
-
+ Comic filesArquivos de quadrinhos
-
+ Open folderAbrir pasta
-
-
+
+ ComicsQuadrinhos
-
+ Toggle fullscreen modeAlternar modo de tela cheia
-
+ Hide/show toolbarOcultar/mostrar barra de ferramentas
-
-
+
+ General
- Em geral
+ Geral
-
+ Size up magnifying glass
- Dimensione a lupa
+ Aumentar o tamanho da lupa
-
+ Size down magnifying glassDiminuir o tamanho da lupa
-
+ Zoom in magnifying glass
- Zoom na lupa
+ Aumentar o zoom da lupa
-
+ Zoom out magnifying glassDiminuir o zoom da lupa
-
+ Reset magnifying glassRedefinir lupa
-
-
- Magnifiying glass
- Lupa
-
-
-
+ Toggle between fit to width and fit to heightAlternar entre ajustar à largura e ajustar à altura
-
-
- Page adjustement
+
+
+ Page adjustment
+ Page adjustementAjuste de página
-
+ The current page is not ready. Please try again later.A página atual ainda não está pronta. Por favor, tente novamente mais tarde.
-
+ Overwrite file?
-
+ Substituir arquivo?
-
+ The file already exists. Do you want to overwrite it?
-
+ O arquivo já existe. Deseja substituí-lo?
-
+ The current page could not be saved.
- Não foi possível guardar a página atual.
+ Não foi possível salvar a página atual.
-
+ The selected pages are not ready. Please try again later.As páginas selecionadas ainda não estão prontas. Por favor, tente novamente mais tarde.
-
+ The current page could not be extracted.
-
+ Não foi possível extrair a página atual.
-
+ Overwrite files?
-
+ Substituir arquivos?
-
+ Some files already exist. Do you want to overwrite them?
-
+ Alguns arquivos já existem. Deseja substituí-los?
-
+ Some pages could not be extracted.
-
+ Não foi possível extrair algumas páginas.
-
+ Autoscroll downRolagem automática para baixo
-
+ Autoscroll upRolagem automática para cima
-
+ Autoscroll forward, horizontal firstRolagem automática para frente, horizontal primeiro
-
+ Autoscroll backward, horizontal firstRolagem automática para trás, horizontal primeiro
-
+ Autoscroll forward, vertical firstRolagem automática para frente, vertical primeiro
-
+ Autoscroll backward, vertical firstRolagem automática para trás, vertical primeiro
-
+ Move downMover para baixo
-
+ Move up
- Subir
+ Mover para cima
-
+ Move leftMover para a esquerda
-
+ Move rightMover para a direita
-
+ Go to the first page
- Vá para a primeira página
+ Ir para a primeira página
-
+ Go to the last pageIr para a última página
-
+ Offset double page to the leftDeslocar página dupla para a esquerda
-
+ Offset double page to the rightDeslocar página dupla para a direita
-
-
+
+ ReadingLeitura
-
+ There is a new version availableHá uma nova versão disponível
-
+ Do you want to download the new version?Você deseja baixar a nova versão?
-
+ Remind me in 14 daysLembre-me em 14 dias
-
+ Not nowAgora não
@@ -1524,19 +1526,19 @@ Se nenhum estiver ativo, a tecla Escape não faz nada.
Release notes are not available.
-
+ As notas de versão não estão disponíveis.Previous versions
-
+ Versões anterioresYACReaderFieldEdit
-
+ Click to overwriteClique para substituir
@@ -1550,9 +1552,9 @@ Se nenhum estiver ativo, a tecla Escape não faz nada.
YACReaderFieldPlainTextEdit
-
-
-
+
+
+ Click to overwriteClique para substituir
@@ -1582,7 +1584,7 @@ Se nenhum estiver ativo, a tecla Escape não faz nada.
Customize the keyboard shortcuts used by the application.
- Personalize os atalhos de teclado utilizados pela aplicação.
+ Personalize os atalhos de teclado utilizados pelo aplicativo.
@@ -1600,7 +1602,7 @@ Se nenhum estiver ativo, a tecla Escape não faz nada.
Reset
- Reiniciar
+ Redefinir
@@ -1619,7 +1621,7 @@ Se nenhum estiver ativo, a tecla Escape não faz nada.
clear
- claro
+ limpar
diff --git a/YACReader/yacreader_pt_PT.ts b/YACReader/yacreader_pt_PT.ts
new file mode 100644
index 000000000..858e9810b
--- /dev/null
+++ b/YACReader/yacreader_pt_PT.ts
@@ -0,0 +1,1631 @@
+
+
+
+
+ ActionsShortcutsModel
+
+
+ None
+ Nenhum
+
+
+
+ AppearanceTabWidget
+
+
+ Color scheme
+ Esquema de cores
+
+
+
+ System
+ Sistema
+
+
+
+ Light
+ Claro
+
+
+
+ Dark
+ Escuro
+
+
+
+ Custom
+ Personalizado
+
+
+
+ Remove
+ Remover
+
+
+
+ Remove this user-imported theme
+ Remover este tema importado pelo utilizador
+
+
+
+ Light:
+ Claro:
+
+
+
+ Dark:
+ Escuro:
+
+
+
+ Custom:
+ Personalizado:
+
+
+
+ Import theme...
+ Importar tema...
+
+
+
+ Theme
+ Tema
+
+
+
+ Theme editor
+ Editor de temas
+
+
+
+ Open Theme Editor...
+ Abrir editor de temas...
+
+
+
+ Theme editor error
+ Erro do editor de temas
+
+
+
+ The current theme JSON could not be loaded.
+ Não foi possível carregar o JSON do tema atual.
+
+
+
+ Import theme
+ Importar tema
+
+
+
+ JSON files (*.json);;All files (*)
+ Ficheiros JSON (*.json);;Todos os ficheiros (*)
+
+
+
+ Could not import theme from:
+%1
+ Não foi possível importar o tema de:
+%1
+
+
+
+ Could not import theme from:
+%1
+
+%2
+ Não foi possível importar o tema de:
+%1
+
+%2
+
+
+
+ Import failed
+ Falha na importação
+
+
+
+ BookmarksDialog
+
+
+ Latest Page
+ Última página
+
+
+
+ Close
+ Fechar
+
+
+
+ Click on any image to go to the bookmark
+ Clique em qualquer imagem para ir para o marcador
+
+
+
+
+ Loading...
+ A carregar...
+
+
+
+ ContinuousPageWidget
+
+
+ Loading page %1
+ A carregar a página %1
+
+
+
+ EditShortcutsDialog
+
+
+ Restore defaults
+ Restaurar predefinições
+
+
+
+ To change a shortcut, double-click the key combination and type the new keys.
+ Para alterar um atalho, faça duplo clique na combinação de teclas e introduza as novas teclas.
+
+
+
+ Shortcuts settings
+ Definições de atalhos
+
+
+
+ Shortcut in use
+ Atalho em utilização
+
+
+
+ The shortcut "%1" is already assigned to another function
+ O atalho "%1" já está atribuído a outra função
+
+
+
+ FileComic
+
+
+ CRC error on page (%1): some of the pages will not be displayed correctly
+ Erro de CRC na página (%1): algumas páginas não serão apresentadas corretamente
+
+
+
+ Unknown error opening the file
+ Erro desconhecido ao abrir o ficheiro
+
+
+
+ 7z not found
+ 7z não encontrado
+
+
+
+
+ Format not supported
+ Formato não suportado
+
+
+
+ GoToDialog
+
+
+ Page:
+ Página:
+
+
+
+ Go To
+ Ir para
+
+
+
+ Cancel
+ Cancelar
+
+
+
+
+ Total pages:
+ Total de páginas:
+
+
+
+ Go to...
+ Ir para...
+
+
+
+ GoToFlowToolBar
+
+
+ Page:
+ Página:
+
+
+
+ HelpAboutDialog
+
+
+ About
+ Acerca de
+
+
+
+ Help
+ Ajuda
+
+
+
+ System info
+ Informações do sistema
+
+
+
+ Changelog
+ Registo de alterações
+
+
+
+ OptionsDialog
+
+
+ "Go to flow" size
+ Tamanho do "Ir para Comic Flow"
+
+
+
+ My comics path
+ Caminho das minhas bandas desenhadas
+
+
+
+ Background color
+ Cor de fundo
+
+
+
+ Choose
+ Escolher
+
+
+
+ Quick Navigation Mode
+ Modo de navegação rápida
+
+
+
+ Disable mouse over activation
+ Desativar ativação ao passar o rato
+
+
+
+ Restart is needed
+ É necessário reiniciar
+
+
+
+ Brightness
+ Brilho
+
+
+
+ Language
+ Idioma
+
+
+
+ Application language
+ Idioma da aplicação
+
+
+
+ System default
+ Predefinição do sistema
+
+
+
+ Display
+ Apresentação
+
+
+
+ Show time in current page information label
+ Mostrar a hora na etiqueta de informação da página atual
+
+
+
+ Magnifying glass
+ Lupa
+
+
+
+ Circular magnifying glass
+ Lupa circular
+
+
+
+ Draw a ring around the circular magnifying glass
+ Desenhar um anel à volta da lupa circular
+
+
+
+ Ease cursor movement toward the edges
+ Suavizar o movimento do cursor em direção às extremidades
+
+
+
+ Clear
+ Limpar
+
+
+
+ Scroll behavior
+ Comportamento de deslocação
+
+
+
+ Disable scroll animations and smooth scrolling
+ Desativar animações de deslocação e deslocação suave
+
+
+
+ Do not turn page using scroll
+ Não virar a página com a deslocação
+
+
+
+ Use single scroll step to turn page
+ Usar um único passo de deslocação para virar a página
+
+
+
+ Mouse mode
+ Modo do rato
+
+
+
+ Only Back/Forward buttons can turn pages
+ Só os botões Recuar/Avançar podem virar as páginas
+
+
+
+ Use the Left/Right buttons to turn pages.
+ Utilize os botões Esquerda/Direita para virar as páginas.
+
+
+
+ Click the left or right half of the screen to turn pages.
+ Clique na metade esquerda ou direita do ecrã para virar as páginas.
+
+
+
+ Escape key
+ Tecla Escape
+
+
+
+ Quit the reader
+ Sair do leitor
+
+
+
+ Cancel the active mode
+ Cancelar o modo ativo
+
+
+
+ Escape closes the reader, even while a mode is active.
+ A tecla Escape fecha o leitor, mesmo quando há um modo ativo.
+
+
+
+ Escape cancels the first of these that is active:
+
+1. Magnifying glass
+2. Dictionary
+3. Go to page bar
+4. Fullscreen
+
+If none is active, Escape does nothing.
+ Tooltip listing the order in which modes are cancelled. Only the first active mode in the list is cancelled per Escape keypress.
+ A tecla Escape cancela o primeiro destes modos que estiver ativo:
+
+1. Lupa
+2. Dicionário
+3. Barra Ir para a página
+4. Ecrã inteiro
+
+Se nenhum estiver ativo, a tecla Escape não faz nada.
+
+
+
+ Use software-rendered "go to flow" (requires restart)
+ Usar "Ir para Comic Flow" renderizado por software (requer reinício)
+
+
+
+ Use a simple horizontal list of page thumbnails instead of the GPU-rendered 3D flow. Enable this if reading is not smooth on your system.
+ Utilize uma lista horizontal simples de miniaturas das páginas em vez do fluxo 3D renderizado pela GPU. Ative esta opção se a leitura não for fluida no seu sistema.
+
+
+
+ Contrast
+ Contraste
+
+
+
+ Gamma
+ Gama
+
+
+
+ Reset
+ Repor
+
+
+
+ Image options
+ Opções de imagem
+
+
+
+ Fit options
+ Opções de ajuste
+
+
+
+ Enlarge images to fit width/height
+ Ampliar imagens para caberem na largura/altura
+
+
+
+ Double Page options
+ Opções de página dupla
+
+
+
+ Show covers as single page
+ Mostrar capas como página única
+
+
+
+ Show book fold shadow
+ Mostrar sombra na dobra do livro
+
+
+
+ Scaling
+ Dimensionamento
+
+
+
+ Scaling method
+ Método de dimensionamento
+
+
+
+ Nearest (fast, low quality)
+ Vizinho mais próximo (rápido, baixa qualidade)
+
+
+
+ Bilinear
+ Bilinear
+
+
+
+ Lanczos (better quality)
+ Lanczos (melhor qualidade)
+
+
+
+ Bicubic
+
+
+
+
+ General
+ Geral
+
+
+
+ Page Flow
+ Fluxo de páginas
+
+
+
+ Image adjustment
+ Ajuste de imagem
+
+
+
+ Appearance
+ Aspeto
+
+
+
+ Options
+ Opções
+
+
+
+ Comics directory
+ Pasta das bandas desenhadas
+
+
+
+ QObject
+
+
+ Select custom cover
+ Selecionar capa personalizada
+
+
+
+ Images (%1)
+ Imagens (%1)
+
+
+
+ 7z lib not found
+ Biblioteca 7z não encontrada
+
+
+
+ unable to load 7z lib from ./utils
+ não foi possível carregar a biblioteca 7z a partir de ./utils
+
+
+
+ The file could not be read or is not valid JSON.
+ Não foi possível ler o ficheiro ou este não é um JSON válido.
+
+
+
+ This theme is for %1, not %2.
+ Este tema é para %1, não para %2.
+
+
+
+ ThemeEditorDialog
+
+
+ Theme Editor
+ Editor de temas
+
+
+
+ +
+ +
+
+
+
+ -
+ -
+
+
+
+ i
+ i
+
+
+
+ Expand all
+ Expandir tudo
+
+
+
+ Collapse all
+ Recolher tudo
+
+
+
+ Hold to flash the selected value in the UI (magenta / toggled / 0↔10). Releases restore the original.
+ Mantenha premido para fazer piscar o valor selecionado na interface (magenta / alternado / 0↔10). Ao soltar, o original é restaurado.
+
+
+
+ Search…
+ Pesquisar…
+
+
+
+ Light
+ Claro
+
+
+
+ Dark
+ Escuro
+
+
+
+ ID:
+ ID:
+
+
+
+ Display name:
+ Nome de apresentação:
+
+
+
+ Variant:
+ Variante:
+
+
+
+ Theme info
+ Informações do tema
+
+
+
+ Parameter
+ Parâmetro
+
+
+
+ Value
+ Valor
+
+
+
+ Save and apply
+ Guardar e aplicar
+
+
+
+ Export to file...
+ Exportar para ficheiro...
+
+
+
+ Load from file...
+ Carregar a partir de ficheiro...
+
+
+
+ Close
+ Fechar
+
+
+
+ Double-click to edit color
+ Faça duplo clique para editar a cor
+
+
+
+
+
+
+
+
+ true
+ true
+
+
+
+
+
+
+ false
+ false
+
+
+
+ Double-click to toggle
+ Faça duplo clique para alternar
+
+
+
+ Double-click to edit value
+ Faça duplo clique para editar o valor
+
+
+
+
+
+ Edit: %1
+ Editar: %1
+
+
+
+ Save theme
+ Guardar tema
+
+
+
+
+ JSON files (*.json);;All files (*)
+ Ficheiros JSON (*.json);;Todos os ficheiros (*)
+
+
+
+ Save failed
+ Falha ao guardar
+
+
+
+ Could not open file for writing:
+%1
+ Não foi possível abrir o ficheiro para escrita:
+%1
+
+
+
+ Load theme
+ Carregar tema
+
+
+
+
+
+ Load failed
+ Falha ao carregar
+
+
+
+ Could not open file:
+%1
+ Não foi possível abrir o ficheiro:
+%1
+
+
+
+ Invalid JSON:
+%1
+ JSON inválido:
+%1
+
+
+
+ Expected a JSON object.
+ Era esperado um objeto JSON.
+
+
+
+ Viewer
+
+
+
+ Press 'O' to open a comic.
+ Prima 'O' para abrir uma banda desenhada.
+
+
+
+ Not found
+ Não encontrado
+
+
+
+ Comic not found
+ Banda desenhada não encontrada
+
+
+
+ Error opening comic
+ Erro ao abrir a banda desenhada
+
+
+
+ CRC Error
+ Erro de CRC
+
+
+
+ Loading... please wait!
+ A carregar... aguarde, por favor!
+
+
+
+ Page not available!
+ Página indisponível!
+
+
+
+ Cover!
+ Capa!
+
+
+
+ Last page!
+ Última página!
+
+
+
+ YACReader3DFlowConfigWidget
+
+
+ Presets:
+ Predefinições:
+
+
+
+ Classic look
+ Aspeto clássico
+
+
+
+ Stripe look
+ Aspeto em faixas
+
+
+
+ Overlapped Stripe look
+ Aspeto em faixas sobrepostas
+
+
+
+ Modern look
+ Aspeto moderno
+
+
+
+ Roulette look
+ Aspeto de roleta
+
+
+
+ Show advanced settings
+ Mostrar definições avançadas
+
+
+
+ Custom:
+ Personalizado:
+
+
+
+ View angle
+ Ângulo de visão
+
+
+
+ Position
+ Posição
+
+
+
+ Cover gap
+ Espaço entre capas
+
+
+
+ Central gap
+ Espaço central
+
+
+
+ Zoom
+ Zoom
+
+
+
+ Y offset
+ Deslocamento Y
+
+
+
+ Z offset
+ Deslocamento Z
+
+
+
+ Cover Angle
+ Ângulo das capas
+
+
+
+ Visibility
+ Visibilidade
+
+
+
+ Light
+ Luz
+
+
+
+ Max angle
+ Ângulo máximo
+
+
+
+ Low Performance
+ Baixo desempenho
+
+
+
+ High Performance
+ Alto desempenho
+
+
+
+ Use VSync (improves image quality in fullscreen mode, but reduces performance)
+ Usar VSync (melhora a qualidade da imagem em modo de ecrã inteiro, mas reduz o desempenho)
+
+
+
+ Performance:
+ Desempenho:
+
+
+
+ YACReader::MainWindowViewer
+
+
+ &Open
+ &Abrir
+
+
+
+ Open a comic
+ Abrir uma banda desenhada
+
+
+
+ New instance
+ Nova instância
+
+
+
+ Open Folder
+ Abrir pasta
+
+
+
+ Open image folder
+ Abrir pasta de imagens
+
+
+
+ Open latest comic
+ Abrir a banda desenhada mais recente
+
+
+
+ Open the latest comic opened in the previous reading session
+ Abrir a banda desenhada mais recente aberta na sessão de leitura anterior
+
+
+
+ Clear
+ Limpar
+
+
+
+ Clear open recent list
+ Limpar lista de abertos recentemente
+
+
+
+ Save
+ Guardar
+
+
+
+
+
+
+ Save current page
+ Guardar página atual
+
+
+
+
+
+
+
+ Extract page(s)
+ Extrair página(s)
+
+
+
+ Extract page(s) from the original source
+ Extrair página(s) da origem original
+
+
+
+ Previous Comic
+ Banda desenhada anterior
+
+
+
+
+
+ Open previous comic
+ Abrir banda desenhada anterior
+
+
+
+ Next Comic
+ Banda desenhada seguinte
+
+
+
+
+
+ Open next comic
+ Abrir banda desenhada seguinte
+
+
+
+ &Previous
+ A&nterior
+
+
+
+
+
+ Go to previous page
+ Ir para a página anterior
+
+
+
+ &Next
+ &Seguinte
+
+
+
+
+
+ Go to next page
+ Ir para a página seguinte
+
+
+
+ Fit Height
+ Ajustar altura
+
+
+
+ Fit image to height
+ Ajustar imagem à altura
+
+
+
+ Fit Width
+ Ajustar largura
+
+
+
+ Fit image to width
+ Ajustar imagem à largura
+
+
+
+ Show full size
+ Mostrar tamanho real
+
+
+
+ Fit to page
+ Ajustar à página
+
+
+
+ Continuous scroll
+ Deslocação contínua
+
+
+
+ Switch to continuous scroll mode
+ Mudar para o modo de deslocação contínua
+
+
+
+ Reset zoom
+ Repor Zoom
+
+
+
+ Show zoom slider
+ Mostrar controlo de deslize do Zoom
+
+
+
+ Zoom+
+ Zoom+
+
+
+
+ Zoom-
+ Zoom-
+
+
+
+ Rotate image to the left
+ Rodar imagem para a esquerda
+
+
+
+ Rotate image to the right
+ Rodar imagem para a direita
+
+
+
+ Double page mode
+ Modo de página dupla
+
+
+
+ Switch to double page mode
+ Mudar para o modo de página dupla
+
+
+
+ Double page manga mode
+ Modo manga de página dupla
+
+
+
+ Reverse reading order in double page mode
+ Inverter a ordem de leitura no modo de página dupla
+
+
+
+ Go To
+ Ir para
+
+
+
+ Go to page...
+ Ir para a página...
+
+
+
+ Options
+ Opções
+
+
+
+ YACReader options
+ Opções do YACReader
+
+
+
+
+ Help
+ Ajuda
+
+
+
+ Help, About YACReader
+ Ajuda, Acerca do YACReader
+
+
+
+
+
+ Magnifying glass
+ Lupa
+
+
+
+ Switch Magnifying glass
+ Alternar lupa
+
+
+
+ Set bookmark
+ Definir marcador
+
+
+
+ Set a bookmark on the current page
+ Definir um marcador na página atual
+
+
+
+ Show bookmarks
+ Mostrar marcadores
+
+
+
+ Show the bookmarks of the current comic
+ Mostrar os marcadores da banda desenhada atual
+
+
+
+ Show keyboard shortcuts
+ Mostrar atalhos de teclado
+
+
+
+ Show Info
+ Mostrar informações
+
+
+
+ Escape
+ Escape
+
+
+
+ Escape key: quit, or cancel the active mode
+ Tecla Escape: sair ou cancelar o modo ativo
+
+
+
+ Close
+ Fechar
+
+
+
+ Show Dictionary
+ Mostrar dicionário
+
+
+
+ Show go to flow
+ Mostrar "Ir para Comic Flow"
+
+
+
+ Edit shortcuts
+ Editar atalhos
+
+
+
+ &File
+ &Ficheiro
+
+
+
+
+ Open recent
+ Abrir recentes
+
+
+
+ File
+ Ficheiro
+
+
+
+ Edit
+ Editar
+
+
+
+ View
+ Ver
+
+
+
+ Go
+ Ir
+
+
+
+ Window
+ Janela
+
+
+
+ Open Comic
+ Abrir banda desenhada
+
+
+
+ Comic files
+ Ficheiros de banda desenhada
+
+
+
+ Open folder
+ Abrir pasta
+
+
+
+ The current page is not ready. Please try again later.
+ A página atual ainda não está pronta. Tente novamente mais tarde.
+
+
+
+ Overwrite file?
+ Substituir o ficheiro?
+
+
+
+ The file already exists. Do you want to overwrite it?
+ O ficheiro já existe. Pretende substituí-lo?
+
+
+
+ The current page could not be saved.
+ Não foi possível guardar a página atual.
+
+
+
+ The selected pages are not ready. Please try again later.
+ As páginas selecionadas ainda não estão prontas. Tente novamente mais tarde.
+
+
+
+ The current page could not be extracted.
+ Não foi possível extrair a página atual.
+
+
+
+ Overwrite files?
+ Substituir os ficheiros?
+
+
+
+ Some files already exist. Do you want to overwrite them?
+ Alguns ficheiros já existem. Pretende substituí-los?
+
+
+
+ Some pages could not be extracted.
+ Não foi possível extrair algumas páginas.
+
+
+
+
+ Comics
+ Bandas desenhadas
+
+
+
+
+ General
+ Geral
+
+
+
+
+ Page adjustment
+ Ajuste da página
+
+
+
+
+ Reading
+ Leitura
+
+
+
+ Toggle fullscreen mode
+ Alternar modo de ecrã inteiro
+
+
+
+ Hide/show toolbar
+ Ocultar/mostrar barra de ferramentas
+
+
+
+ Size up magnifying glass
+ Aumentar o tamanho da lupa
+
+
+
+ Size down magnifying glass
+ Diminuir o tamanho da lupa
+
+
+
+ Zoom in magnifying glass
+ Aumentar o Zoom da lupa
+
+
+
+ Zoom out magnifying glass
+ Diminuir o Zoom da lupa
+
+
+
+ Reset magnifying glass
+ Repor lupa
+
+
+
+ Toggle between fit to width and fit to height
+ Alternar entre ajustar à largura e ajustar à altura
+
+
+
+ Autoscroll down
+ Deslocação automática para baixo
+
+
+
+ Autoscroll up
+ Deslocação automática para cima
+
+
+
+ Autoscroll forward, horizontal first
+ Deslocação automática para a frente, primeiro na horizontal
+
+
+
+ Autoscroll backward, horizontal first
+ Deslocação automática para trás, primeiro na horizontal
+
+
+
+ Autoscroll forward, vertical first
+ Deslocação automática para a frente, primeiro na vertical
+
+
+
+ Autoscroll backward, vertical first
+ Deslocação automática para trás, primeiro na vertical
+
+
+
+ Move down
+ Mover para baixo
+
+
+
+ Move up
+ Mover para cima
+
+
+
+ Move left
+ Mover para a esquerda
+
+
+
+ Move right
+ Mover para a direita
+
+
+
+ Go to the first page
+ Ir para a primeira página
+
+
+
+ Go to the last page
+ Ir para a última página
+
+
+
+ Offset double page to the left
+ Deslocar a página dupla para a esquerda
+
+
+
+ Offset double page to the right
+ Deslocar a página dupla para a direita
+
+
+
+ There is a new version available
+ Existe uma nova versão disponível
+
+
+
+ Do you want to download the new version?
+ Pretende transferir a nova versão?
+
+
+
+ Remind me in 14 days
+ Lembrar-me daqui a 14 dias
+
+
+
+ Not now
+ Agora não
+
+
+
+ YACReader::WhatsNewDialog
+
+
+ Release notes are not available.
+ As notas de versão não estão disponíveis.
+
+
+
+ Previous versions
+ Versões anteriores
+
+
+
+ YACReaderFieldEdit
+
+
+
+ Click to overwrite
+ Clique para substituir
+
+
+
+ Restore to default
+ Restaurar predefinição
+
+
+
+ YACReaderFieldPlainTextEdit
+
+
+
+
+
+ Click to overwrite
+ Clique para substituir
+
+
+
+ Restore to default
+ Restaurar predefinição
+
+
+
+ YACReaderOptionsDialog
+
+
+ Save
+ Guardar
+
+
+
+ Cancel
+ Cancelar
+
+
+
+ Keyboard shortcuts
+ Atalhos de teclado
+
+
+
+ Customize the keyboard shortcuts used by the application.
+ Personalize os atalhos de teclado utilizados pela aplicação.
+
+
+
+ Edit shortcuts
+ Editar atalhos
+
+
+
+ Shortcuts
+ Atalhos
+
+
+
+ YACReaderSlider
+
+
+ Reset
+ Repor
+
+
+
+ YACReaderTranslator
+
+
+ YACReader translator
+ Tradutor do YACReader
+
+
+
+
+ Translation
+ Tradução
+
+
+
+ clear
+ limpar
+
+
+
+ Service not available
+ Serviço indisponível
+
+
+
diff --git a/YACReader/yacreader_ru.ts b/YACReader/yacreader_ru.ts
index e523571de..04d534a42 100644
--- a/YACReader/yacreader_ru.ts
+++ b/YACReader/yacreader_ru.ts
@@ -6,7 +6,7 @@
None
- Никто
+ Нет
@@ -24,22 +24,22 @@
Light
- Осветить
+ СветлаяDark
- Темный
+ ТемнаяCustom
- Обычай
+ ПользовательскаяRemove
- Удалять
+ Удалить
@@ -49,17 +49,17 @@
Light:
- Свет:
+ Светлая:Dark:
- Темный:
+ Темная:Custom:
- Пользовательский:
+ Пользовательская:
@@ -141,11 +141,11 @@
Click on any image to go to the bookmark
- Нажмите на любое изображение чтобы перейти к закладке
+ Нажмите на любое изображение, чтобы перейти к закладке
- Lastest Page
+ Latest PageПоследняя страница
@@ -176,35 +176,35 @@
- The shortcut "%1" is already assigned to other function
- Сочетание клавиш "%1" уже назначено для другой функции
+ The shortcut "%1" is already assigned to another function
+ Сочетание клавиш «%1» уже назначено для другой функции
- To change a shortcut, double click in the key combination and type the new keys.
- Чтобы изменить горячую клавишу дважды щелкните по выбранной комбинации клавиш и введите новые сочетания клавиш.
+ To change a shortcut, double-click the key combination and type the new keys.
+ Чтобы изменить горячую клавишу, дважды щелкните по выбранной комбинации клавиш и введите новые сочетания клавиш.FileComic
-
-
+
+ Format not supportedФормат не поддерживается
-
+ 7z not found7z не найден
-
+ Unknown error opening the fileНеизвестная ошибка при открытии файла
-
+ CRC error on page (%1): some of the pages will not be displayed correctlyОшибка контрольной суммы CRC на странице (%1): некоторые страницы будут отображаться неправильно
@@ -224,8 +224,8 @@
- Total pages :
- Общее количество страниц :
+ Total pages:
+ Общее количество страниц:
@@ -234,16 +234,16 @@
- Page :
- Страница :
+ Page:
+ Страница: GoToFlowToolBar
- Page :
- Страница :
+ Page:
+ Страница:
@@ -261,7 +261,7 @@
Changelog
-
+ Журнал изменений
@@ -279,22 +279,22 @@
Reset
- Вернуть к первоначальным значениям
+ СброситьMy comics path
- Папка комиксов
+ Путь к моим комиксам
-
+ Image adjustmentНастройка изображения"Go to flow" size
- Размер "Перейти к Comic Flow"
+ Размер «Перейти к Comic Flow»
@@ -312,12 +312,12 @@
Контраст
-
+ Appearance
- Появление
+ Внешний вид
-
+ OptionsНастройки
@@ -342,19 +342,19 @@
Очистить
-
+ Comics directoryПапка комиксовQuick Navigation Mode
- Ползунок для быстрой навигации по страницам
+ Режим быстрой навигацииDisplay
- Отображать
+ Отображение
@@ -388,7 +388,7 @@
- Scroll behaviour
+ Scroll behaviorПоведение прокрутки
@@ -399,12 +399,12 @@
Do not turn page using scroll
- Не переворачивайте страницу с помощью прокрутки
+ Не переворачивать страницу с помощью прокруткиUse single scroll step to turn page
- Используйте один шаг прокрутки, чтобы перевернуть страницу
+ Использовать один шаг прокрутки, чтобы перевернуть страницу
@@ -414,7 +414,7 @@
Only Back/Forward buttons can turn pages
- Только кнопки «Назад/Вперед» могут перелистывать страницы.
+ Только кнопки «Назад/Вперед» могут перелистывать страницы
@@ -423,7 +423,7 @@
- Click left or right half of the screen to turn pages.
+ Click the left or right half of the screen to turn pages.Нажмите левую или правую половину экрана, чтобы перелистывать страницы.
@@ -457,9 +457,9 @@
If none is active, Escape does nothing.
Tooltip listing the order in which modes are cancelled. Only the first active mode in the list is cancelled per Escape keypress.
- Клавиша Esc отключает первый активный режим из списка:
+ Клавиша Esc отменяет первый активный режим из списка:
-1. Лупа
+1. Увеличительное стекло
2. Словарь
3. Панель перехода к странице
4. Полноэкранный режим
@@ -469,7 +469,7 @@ If none is active, Escape does nothing.
Use software-rendered "go to flow" (requires restart)
- Использовать программную отрисовку "Перейти к Comic Flow" (требуется перезапуск)
+ Использовать программную отрисовку «Перейти к Comic Flow» (требуется перезапуск)
@@ -504,20 +504,25 @@ If none is active, Escape does nothing.
Bilinear
- Билинейный
+ BilinearLanczos (better quality)
- Ланцос (лучшее качество)
+ Lanczos (лучшее качество)
-
+
+ Bicubic
+
+
+
+ Page Flow
- Поток Страниц
+ Поток страниц
-
+ GeneralОбщие
@@ -527,19 +532,19 @@ If none is active, Escape does nothing.
Яркость
-
+ Restart is needed
- Требуется перезагрузка
+ Требуется перезапускFit options
- Варианты подгонки
+ Параметры подгонкиEnlarge images to fit width/height
- Увеличьте изображения по ширине/высоте
+ Увеличивать изображения по ширине/высоте
@@ -562,15 +567,15 @@ If none is active, Escape does nothing.
unable to load 7z lib from ./utils
- не удается загрузить 7z lib из ./ utils
+ не удается загрузить библиотеку 7z из ./utils
-
+ Select custom cover
- Выбрать индивидуальную обложку
+ Выбрать пользовательскую обложку
-
+ Images (%1)Изображения (%1)
@@ -605,7 +610,7 @@ If none is active, Escape does nothing.
i
- я
+ i
@@ -620,7 +625,7 @@ If none is active, Escape does nothing.
Hold to flash the selected value in the UI (magenta / toggled / 0↔10). Releases restore the original.
- Удерживайте, чтобы выбранное значение мигало в пользовательском интерфейсе (пурпурный / переключено / 0↔10). Релизы восстанавливают оригинал.
+ Удерживайте, чтобы выбранное значение мигало в интерфейсе (пурпурный / переключено / 0↔10). При отпускании восстанавливается исходное значение.
@@ -630,17 +635,17 @@ If none is active, Escape does nothing.
Light
- Осветить
+ СветлаяDark
- Темный
+ ТемнаяID:
- ИДЕНТИФИКАТОР:
+ ID:
@@ -665,12 +670,12 @@ If none is active, Escape does nothing.
Value
- Ценить
+ ЗначениеSave and apply
- Сохраните и примените
+ Сохранить и применить
@@ -700,7 +705,7 @@ If none is active, Escape does nothing.
true
- истинный
+ true
@@ -708,7 +713,7 @@ If none is active, Escape does nothing.
false
- ЛОЖЬ
+ false
@@ -741,7 +746,7 @@ If none is active, Escape does nothing.
Save failed
- Сохранить не удалось
+ Сохранение не удалось
@@ -792,8 +797,8 @@ If none is active, Escape does nothing.
- Press 'O' to open comic.
- Нажмите "O" чтобы открыть комикс.
+ Press 'O' to open a comic.
+ Нажмите «O», чтобы открыть комикс.
@@ -803,7 +808,7 @@ If none is active, Escape does nothing.
Cover!
- Начало!
+ Обложка!
@@ -823,12 +828,12 @@ If none is active, Escape does nothing.
Last page!
- Конец!
+ Последняя страница!
- Loading...please wait!
- Загрузка... Пожалуйста подождите!
+ Loading... please wait!
+ Загрузка... Пожалуйста, подождите!
@@ -871,7 +876,7 @@ If none is active, Escape does nothing.
Custom:
- Пользовательский:
+ Пользовательская:
@@ -886,17 +891,17 @@ If none is active, Escape does nothing.
Cover gap
- Осветить разрыв
+ Зазор между обложкамиCentral gap
- Сфокусировать разрыв
+ Центральный зазорZoom
- Масштабировать
+ Zoom
@@ -911,17 +916,17 @@ If none is active, Escape does nothing.
Cover Angle
- Охватить угол
+ Угол обложекVisibility
- Прозрачность
+ ВидимостьLight
- Осветить
+ Освещение
@@ -940,8 +945,8 @@ If none is active, Escape does nothing.
- Use VSync (improve the image quality in fullscreen mode, worse performance)
- Использовать VSync (повысить формат изображения в полноэкранном режиме , хуже производительность)
+ Use VSync (improves image quality in fullscreen mode, but reduces performance)
+ Использовать VSync (улучшает качество изображения в полноэкранном режиме, снижает производительность)
@@ -984,7 +989,7 @@ If none is active, Escape does nothing.
Open the latest comic opened in the previous reading session
- Открыть комикс открытый в предыдущем сеансе чтения
+ Открыть последний комикс, открытый в предыдущем сеансе чтения
@@ -1003,25 +1008,25 @@ If none is active, Escape does nothing.
-
-
-
+
+
+ Save current page
- Сохранить текущию страницу
+ Сохранить текущую страницу
-
-
-
-
+
+
+
+ Extract page(s)
-
+ Извлечь страницу(ы)Extract page(s) from the original source
-
+ Извлечь страницу(ы) из исходного файла
@@ -1030,10 +1035,10 @@ If none is active, Escape does nothing.
-
-
+
+ Open previous comic
- Открыть предыдуший комикс
+ Открыть предыдущий комикс
@@ -1042,8 +1047,8 @@ If none is active, Escape does nothing.
-
-
+
+ Open next comicОткрыть следующий комикс
@@ -1054,8 +1059,8 @@ If none is active, Escape does nothing.
-
-
+
+ Go to previous pageПерейти к предыдущей странице
@@ -1066,8 +1071,8 @@ If none is active, Escape does nothing.
-
-
+
+ Go to next pageПерейти к следующей странице
@@ -1079,7 +1084,7 @@ If none is active, Escape does nothing.
Fit image to height
- Подогнать по высоте
+ Подогнать изображение по высоте
@@ -1089,7 +1094,7 @@ If none is active, Escape does nothing.
Fit image to width
- Подогнать по ширине
+ Подогнать изображение по ширине
@@ -1114,22 +1119,22 @@ If none is active, Escape does nothing.
Reset zoom
- Сбросить масштаб
+ Сбросить ZoomShow zoom slider
- Показать ползунок масштабирования
+ Показать ползунок ZoomZoom+
- Увеличить масштаб
+ Zoom+Zoom-
- Уменьшить масштаб
+ Zoom-
@@ -1149,7 +1154,7 @@ If none is active, Escape does nothing.
Switch to double page mode
- Двухстраничный режим
+ Переключиться в двухстраничный режим
@@ -1159,7 +1164,7 @@ If none is active, Escape does nothing.
Reverse reading order in double page mode
- Двухстраничный режим манги
+ Обратный порядок чтения в двухстраничном режиме
@@ -1168,7 +1173,7 @@ If none is active, Escape does nothing.
- Go to page ...
+ Go to page...Перейти к странице...
@@ -1179,7 +1184,7 @@ If none is active, Escape does nothing.
YACReader options
- Настройки
+ Настройки YACReader
@@ -1190,17 +1195,19 @@ If none is active, Escape does nothing.
Help, About YACReader
- Справка
+ Справка, о программе YACReader
+
+ Magnifying glassУвеличительное стеклоSwitch Magnifying glass
- Увеличительное стекло
+ Включить/выключить увеличительное стекло
@@ -1250,12 +1257,12 @@ If none is active, Escape does nothing.
Show Dictionary
- Переводчик YACreader
+ Показать словарьShow go to flow
- Показать "Перейти к Comic Flow"
+ Показать «Перейти к Comic Flow»
@@ -1265,7 +1272,7 @@ If none is active, Escape does nothing.
&File
- &Отображать панель инструментов
+ &Файл
@@ -1286,7 +1293,7 @@ If none is active, Escape does nothing.
View
- Посмотреть
+ Вид
@@ -1299,222 +1306,217 @@ If none is active, Escape does nothing.
Окно
-
+ Open ComicОткрыть комикс
-
+ Comic files
- Файлы комикса
+ Файлы комиксов
-
+ Open folderОткрыть папку
-
-
+
+ Comics
- Комикс
+ Комиксы
-
+ Toggle fullscreen modeПолноэкранный режим включить/выключить
-
+ Hide/show toolbarПоказать/скрыть панель инструментов
-
-
+
+ GeneralОбщие
-
+ Size up magnifying glassУвеличение размера окошка увеличительного стекла
-
+ Size down magnifying glassУменьшение размера окошка увеличительного стекла
-
+ Zoom in magnifying glass
- Увеличить
+ Увеличить Zoom увеличительного стекла
-
+ Zoom out magnifying glass
- Уменьшить
+ Уменьшить Zoom лупы
-
+ Reset magnifying glassСбросить увеличительное стекло
-
-
- Magnifiying glass
- Увеличительное стекло
-
-
-
+ Toggle between fit to width and fit to heightПереключение режима подгонки страницы по ширине/высоте
-
-
- Page adjustement
+
+
+ Page adjustment
+ Page adjustementНастройка страницы
-
+ The current page is not ready. Please try again later.Текущая страница ещё не готова. Повторите попытку позже.
-
+ Overwrite file?
-
+ Перезаписать файл?
-
+ The file already exists. Do you want to overwrite it?
-
+ Файл уже существует. Перезаписать его?
-
+ The current page could not be saved.Не удалось сохранить текущую страницу.
-
+ The selected pages are not ready. Please try again later.Выбранные страницы ещё не готовы. Повторите попытку позже.
-
+ The current page could not be extracted.
-
+ Не удалось извлечь текущую страницу.
-
+ Overwrite files?
-
+ Перезаписать файлы?
-
+ Some files already exist. Do you want to overwrite them?
-
+ Некоторые файлы уже существуют. Перезаписать их?
-
+ Some pages could not be extracted.
-
+ Не удалось извлечь некоторые страницы.
-
+ Autoscroll downАвтопрокрутка вниз
-
+ Autoscroll upАвтопрокрутка вверх
-
+ Autoscroll forward, horizontal first
- Автопрокрутка вперед, горизонтальная
+ Автопрокрутка вперед, сначала по горизонтали
-
+ Autoscroll backward, horizontal first
- Автопрокрутка назад, горизонтальная
+ Автопрокрутка назад, сначала по горизонтали
-
+ Autoscroll forward, vertical first
- Автопрокрутка вперед, вертикальная
+ Автопрокрутка вперед, сначала по вертикали
-
+ Autoscroll backward, vertical first
- Автопрокрутка назад, вертикальная
+ Автопрокрутка назад, сначала по вертикали
-
+ Move downПереместить вниз
-
+ Move upПереместить вверх
-
+ Move leftПереместить влево
-
+ Move rightПереместить вправо
-
+ Go to the first pageПерейти к первой странице
-
+ Go to the last pageПерейти к последней странице
-
+ Offset double page to the leftСмещение разворота влево
-
+ Offset double page to the rightСмещение разворота вправо
-
-
+
+ ReadingЧтение
-
+ There is a new version availableДоступна новая версия
-
+ Do you want to download the new version?
- Хотите загрузить новую версию ?
+ Хотите загрузить новую версию?
-
+ Remind me in 14 daysНапомнить через 14 дней
-
+ Not nowНе сейчас
@@ -1524,12 +1526,12 @@ If none is active, Escape does nothing.
Release notes are not available.
-
+ Примечания к выпуску недоступны.Previous versions
-
+ Предыдущие версии
@@ -1541,9 +1543,9 @@ If none is active, Escape does nothing.
-
+ Click to overwrite
- Изменить
+ Щелкните, чтобы перезаписать
@@ -1551,15 +1553,15 @@ If none is active, Escape does nothing.
Restore to default
- Вернуть начальные установки
+ Восстановить значения по умолчанию
-
-
-
+
+
+ Click to overwrite
- Изменить
+ Щелкните, чтобы перезаписать
@@ -1600,7 +1602,7 @@ If none is active, Escape does nothing.
Reset
- Сброс мастштаба
+ Сбросить
diff --git a/YACReader/yacreader_source.ts b/YACReader/yacreader_source.ts
index bc2d6dc94..422181e5c 100644
--- a/YACReader/yacreader_source.ts
+++ b/YACReader/yacreader_source.ts
@@ -125,7 +125,7 @@
BookmarksDialog
- Lastest Page
+ Latest Page
@@ -162,7 +162,7 @@
- To change a shortcut, double click in the key combination and type the new keys.
+ To change a shortcut, double-click the key combination and type the new keys.
@@ -177,30 +177,30 @@
- The shortcut "%1" is already assigned to other function
+ The shortcut "%1" is already assigned to another functionFileComic
-
+ CRC error on page (%1): some of the pages will not be displayed correctly
-
+ Unknown error opening the file
-
+ 7z not found
-
-
+
+ Format not supported
@@ -209,7 +209,7 @@
GoToDialog
- Page :
+ Page:
@@ -225,7 +225,7 @@
- Total pages :
+ Total pages:
@@ -238,7 +238,7 @@
GoToFlowToolBar
- Page :
+ Page:
@@ -298,7 +298,7 @@
-
+ Restart is needed
@@ -359,7 +359,7 @@
- Scroll behaviour
+ Scroll behavior
@@ -394,7 +394,7 @@
- Click left or right half of the screen to turn pages.
+ Click the left or right half of the screen to turn pages.
@@ -511,32 +511,37 @@ If none is active, Escape does nothing.
-
- General
+
+ Bicubic
- Page Flow
+ General
- Image adjustment
+ Page Flow
+ Image adjustment
+
+
+
+ Appearance
-
+ Options
-
+ Comics directory
@@ -544,12 +549,12 @@ If none is active, Escape does nothing.
QObject
-
+ Select custom cover
-
+ Images (%1)
@@ -773,7 +778,7 @@ If none is active, Escape does nothing.
- Press 'O' to open comic.
+ Press 'O' to open a comic.
@@ -798,7 +803,7 @@ If none is active, Escape does nothing.
- Loading...please wait!
+ Loading... please wait!
@@ -926,7 +931,7 @@ If none is active, Escape does nothing.
- Use VSync (improve the image quality in fullscreen mode, worse performance)
+ Use VSync (improves image quality in fullscreen mode, but reduces performance)
@@ -989,18 +994,18 @@ If none is active, Escape does nothing.
-
-
-
+
+
+ Save current page
-
-
-
-
+
+
+
+ Extract page(s)
@@ -1016,8 +1021,8 @@ If none is active, Escape does nothing.
-
-
+
+ Open previous comic
@@ -1028,8 +1033,8 @@ If none is active, Escape does nothing.
-
-
+
+ Open next comic
@@ -1040,8 +1045,8 @@ If none is active, Escape does nothing.
-
-
+
+ Go to previous page
@@ -1052,8 +1057,8 @@ If none is active, Escape does nothing.
-
-
+
+ Go to next page
@@ -1154,7 +1159,7 @@ If none is active, Escape does nothing.
- Go to page ...
+ Go to page...
@@ -1180,6 +1185,8 @@ If none is active, Escape does nothing.
+
+ Magnifying glass
@@ -1285,222 +1292,217 @@ If none is active, Escape does nothing.
-
+ Open Comic
-
+ Comic files
-
+ Open folder
-
+ The current page is not ready. Please try again later.
-
+ Overwrite file?
-
+ The file already exists. Do you want to overwrite it?
-
+ The current page could not be saved.
-
+ The selected pages are not ready. Please try again later.
-
+ The current page could not be extracted.
-
+ Overwrite files?
-
+ Some files already exist. Do you want to overwrite them?
-
+ Some pages could not be extracted.
-
-
+
+ Comics
-
-
+
+ General
-
-
- Magnifiying glass
-
-
-
-
-
- Page adjustement
+
+
+ Page adjustment
+ Page adjustement
-
-
+
+ Reading
-
+ Toggle fullscreen mode
-
+ Hide/show toolbar
-
+ Size up magnifying glass
-
+ Size down magnifying glass
-
+ Zoom in magnifying glass
-
+ Zoom out magnifying glass
-
+ Reset magnifying glass
-
+ Toggle between fit to width and fit to height
-
+ Autoscroll down
-
+ Autoscroll up
-
+ Autoscroll forward, horizontal first
-
+ Autoscroll backward, horizontal first
-
+ Autoscroll forward, vertical first
-
+ Autoscroll backward, vertical first
-
+ Move down
-
+ Move up
-
+ Move left
-
+ Move right
-
+ Go to the first page
-
+ Go to the last page
-
+ Offset double page to the left
-
+ Offset double page to the right
-
+ There is a new version available
-
+ Do you want to download the new version?
-
+ Remind me in 14 days
-
+ Not now
@@ -1522,7 +1524,7 @@ If none is active, Escape does nothing.
YACReaderFieldEdit
-
+ Click to overwrite
@@ -1536,9 +1538,9 @@ If none is active, Escape does nothing.
YACReaderFieldPlainTextEdit
-
-
-
+
+
+ Click to overwrite
diff --git a/YACReader/yacreader_tr.ts b/YACReader/yacreader_tr.ts
index a4c44daf0..180c45ff1 100644
--- a/YACReader/yacreader_tr.ts
+++ b/YACReader/yacreader_tr.ts
@@ -24,22 +24,22 @@
Light
- Işık
+ AçıkDark
- Karanlık
+ KoyuCustom
- Gelenek
+ ÖzelRemove
- Kaldırmak
+ Kaldır
@@ -49,17 +49,17 @@
Light:
- Işık:
+ Açık:Dark:
- Karanlık:
+ Koyu:Custom:
- Kişisel:
+ Özel:
@@ -89,7 +89,7 @@
The current theme JSON could not be loaded.
- Geçerli tema JSON yüklenemedi.
+ Geçerli temanın JSON verisi yüklenemedi.
@@ -141,11 +141,11 @@
Click on any image to go to the bookmark
- Yer imine git
+ Yer imine gitmek için herhangi bir resme tıklayın
- Lastest Page
+ Latest PageSon Sayfa
@@ -162,17 +162,17 @@
Restore defaults
- Varsayılarları geri yükle
+ Varsayılanları geri yükle
- To change a shortcut, double click in the key combination and type the new keys.
+ To change a shortcut, double-click the key combination and type the new keys.Bir kısayolu değiştirmek için tuş kombinasyonuna çift tıklayın ve yeni tuşları girin.Shortcuts settings
- Kısayol oyarları
+ Kısayol ayarları
@@ -181,30 +181,30 @@
- The shortcut "%1" is already assigned to other function
- "%1" kısayolu bir başka işleve zaten atanmış
+ The shortcut "%1" is already assigned to another function
+ "%1" kısayolu zaten başka bir işleve atanmışFileComic
-
+ 7z not found7z bulunamadı
-
+ CRC error on page (%1): some of the pages will not be displayed correctly
- (%1). sayfada CRC hatası : bazı sayfalar düzgün görüntülenmeyecek
+ (%1). sayfada CRC hatası: bazı sayfalar düzgün görüntülenmeyecek
-
+ Unknown error opening the fileDosya açılırken bilinmeyen hata
-
-
+
+ Format not supportedBiçim desteklenmiyor
@@ -224,7 +224,7 @@
- Total pages :
+ Total pages: Toplam sayfa:
@@ -234,16 +234,16 @@
- Page :
- Sayfa :
+ Page:
+ Sayfa: GoToFlowToolBar
- Page :
- Sayfa :
+ Page:
+ Sayfa:
@@ -261,7 +261,7 @@
Changelog
-
+ Değişiklik günlüğü
@@ -279,12 +279,12 @@
Reset
- Yeniden başlat
+ SıfırlaMy comics path
- Çizgi Romanlarım
+ Çizgi romanlarımın yolu
@@ -309,7 +309,7 @@
Bilinear
- Çift doğrusal
+ Bilinear
@@ -317,7 +317,12 @@
Lanczos (daha kaliteli)
-
+
+ Bicubic
+
+
+
+ Image adjustmentResim ayarları
@@ -334,7 +339,7 @@
Image options
- Sayfa ayarları
+ Resim seçenekleri
@@ -342,12 +347,12 @@
Kontrast
-
+ AppearanceDış görünüş
-
+ OptionsAyarlar
@@ -372,7 +377,7 @@
Temizle
-
+ Comics directoryÇizgi roman konumu
@@ -382,12 +387,12 @@
Arka plan rengi
-
+ Page FlowSayfa akışı
-
+ GeneralGenel
@@ -397,19 +402,19 @@
Parlaklık
-
+ Restart is neededYeniden başlatılmalıQuick Navigation Mode
- Hızlı Gezinti Kipi
+ Hızlı Gezinti ModuDisplay
- Görüntülemek
+ Görüntü
@@ -438,23 +443,23 @@
- Scroll behaviour
+ Scroll behaviorKaydırma davranışıDisable scroll animations and smooth scrolling
- Kaydırma animasyonlarını ve düzgün kaydırmayı devre dışı bırakın
+ Kaydırma animasyonlarını ve düzgün kaydırmayı devre dışı bırakDo not turn page using scroll
- Kaydırmayı kullanarak sayfayı çevirmeyin
+ Kaydırmayı kullanarak sayfayı çevirmeUse single scroll step to turn page
- Sayfayı çevirmek için tek kaydırma adımını kullanın
+ Sayfayı çevirmek için tek kaydırma adımını kullan
@@ -473,7 +478,7 @@
- Click left or right half of the screen to turn pages.
+ Click the left or right half of the screen to turn pages.Sayfaları çevirmek için ekranın sol veya sağ yarısına tıklayın.
@@ -529,7 +534,7 @@ Hiçbiri etkin değilse Escape tuşu hiçbir şey yapmaz.
Disable mouse over activation
- Etkinleştirme üzerinde fareyi devre dışı bırak
+ Fare üzerine gelince etkinleştirmeyi devre dışı bırak
@@ -539,7 +544,7 @@ Hiçbiri etkin değilse Escape tuşu hiçbir şey yapmaz.
Enlarge images to fit width/height
- Genişliğe/yüksekliği sığmaları için resimleri genişlet
+ Resimleri genişliğe/yüksekliğe sığacak şekilde büyüt
@@ -557,20 +562,20 @@ Hiçbiri etkin değilse Escape tuşu hiçbir şey yapmaz.
7z lib not found
- 7z lib bulunamadı
+ 7z kütüphanesi bulunamadıunable to load 7z lib from ./utils
- ./utils içinden 7z lib yüklenemedi
+ ./utils içinden 7z kütüphanesi yüklenemedi
-
+ Select custom coverÖzel kapak seçin
-
+ Images (%1)Resimler (%1)
@@ -605,7 +610,7 @@ Hiçbiri etkin değilse Escape tuşu hiçbir şey yapmaz.
i
- Ben
+ i
@@ -620,32 +625,32 @@ Hiçbiri etkin değilse Escape tuşu hiçbir şey yapmaz.
Hold to flash the selected value in the UI (magenta / toggled / 0↔10). Releases restore the original.
- Kullanıcı arayüzünde seçilen değeri (macenta / geçişli / 0↔10) yanıp sönmek için basılı tutun. Sürümler orijinali geri yükler.
+ Kullanıcı arayüzünde seçilen değeri (macenta / geçişli / 0↔10) yanıp söndürmek için basılı tutun. Bırakıldığında orijinal değer geri yüklenir.Search…
- Aramak…
+ Ara…Light
- Işık
+ AçıkDark
- Karanlık
+ KoyuID:
- İD:
+ ID:Display name:
- Ekran adı:
+ Görünen ad:
@@ -700,7 +705,7 @@ Hiçbiri etkin değilse Escape tuşu hiçbir şey yapmaz.
true
- doğru
+ true
@@ -708,7 +713,7 @@ Hiçbiri etkin değilse Escape tuşu hiçbir şey yapmaz.
false
- YANLIŞ
+ false
@@ -787,8 +792,8 @@ Hiçbiri etkin değilse Escape tuşu hiçbir şey yapmaz.
- Press 'O' to open comic.
- 'O'ya basarak aç.
+ Press 'O' to open a comic.
+ Çizgi romanı açmak için 'O' tuşuna basın.
@@ -812,7 +817,7 @@ Hiçbiri etkin değilse Escape tuşu hiçbir şey yapmaz.
- Loading...please wait!
+ Loading... please wait!Yükleniyor... lütfen bekleyin!
@@ -836,7 +841,7 @@ Hiçbiri etkin değilse Escape tuşu hiçbir şey yapmaz.
Presets:
- Hazırlayan:
+ Ön ayarlar:
@@ -866,12 +871,12 @@ Hiçbiri etkin değilse Escape tuşu hiçbir şey yapmaz.
Show advanced settings
- Daha fazla ayar göster
+ Gelişmiş ayarları gösterCustom:
- Kişisel:
+ Özel:
@@ -891,22 +896,22 @@ Hiçbiri etkin değilse Escape tuşu hiçbir şey yapmaz.
Central gap
- Boş merkaz
+ Merkez boşluğuZoom
- Yakınlaş
+ ZoomY offset
- Y dengesi
+ Y ofsetiZ offset
- Z dengesi
+ Z ofseti
@@ -916,7 +921,7 @@ Hiçbiri etkin değilse Escape tuşu hiçbir şey yapmaz.
Visibility
- Görünülebilirlik
+ Görünürlük
@@ -940,8 +945,8 @@ Hiçbiri etkin değilse Escape tuşu hiçbir şey yapmaz.
- Use VSync (improve the image quality in fullscreen mode, worse performance)
- VSync kullan
+ Use VSync (improves image quality in fullscreen mode, but reduces performance)
+ VSync kullan (tam ekran modunda görüntü kalitesini artırır, performansı düşürür)
@@ -969,12 +974,12 @@ Hiçbiri etkin değilse Escape tuşu hiçbir şey yapmaz.
Open Folder
- Dosyayı Aç
+ Klasörü AçOpen image folder
- Resim dosyasınıaç
+ Resim klasörünü aç
@@ -1003,47 +1008,47 @@ Hiçbiri etkin değilse Escape tuşu hiçbir şey yapmaz.
-
-
-
+
+
+ Save current pageGeçerli sayfayı kaydet
-
-
-
-
+
+
+
+ Extract page(s)
-
+ Sayfa(ları) çıkarExtract page(s) from the original source
-
+ Sayfa(ları) özgün kaynaktan çıkarPrevious Comic
- Önce ki çizgi roman
+ Önceki çizgi roman
-
-
+
+ Open previous comicÖnceki çizgi romanı açNext Comic
- Sırada ki çizgi roman
+ Sıradaki çizgi roman
-
-
+
+ Open next comicSıradaki çizgi romanı aç
@@ -1054,8 +1059,8 @@ Hiçbiri etkin değilse Escape tuşu hiçbir şey yapmaz.
-
-
+
+ Go to previous pageÖnceki sayfaya dön
@@ -1066,10 +1071,10 @@ Hiçbiri etkin değilse Escape tuşu hiçbir şey yapmaz.
-
-
+
+ Go to next page
- Sonra ki sayfaya geç
+ Sonraki sayfaya geç
@@ -1079,22 +1084,22 @@ Hiçbiri etkin değilse Escape tuşu hiçbir şey yapmaz.
Fit image to height
- Uygun yüksekliğe getir
+ Resmi yüksekliğe sığdırFit Width
- Uygun Genişlik
+ Genişliğe SığdırFit image to width
- Görüntüyü sığdır
+ Resmi genişliğe sığdırShow full size
- Tam erken
+ Gerçek boyutu göster
@@ -1114,32 +1119,32 @@ Hiçbiri etkin değilse Escape tuşu hiçbir şey yapmaz.
Reset zoom
- Yakınlaştırmayı sıfırla
+ Zoom'u sıfırlaShow zoom slider
- Yakınlaştırma çubuğunu göster
+ Zoom çubuğunu gösterZoom+
- Yakınlaştır
+ Zoom+Zoom-
- Uzaklaştır
+ Zoom-Rotate image to the left
- Sayfayı sola yatır
+ Resmi sola döndürRotate image to the right
- Sayfayı sağa yator
+ Resmi sağa döndür
@@ -1154,12 +1159,12 @@ Hiçbiri etkin değilse Escape tuşu hiçbir şey yapmaz.
Double page manga mode
- Çift sayfa manga kipi
+ Çift sayfa manga moduReverse reading order in double page mode
- Çift sayfa kipinde ters okuma sırası
+ Çift sayfa modunda ters okuma sırası
@@ -1168,8 +1173,8 @@ Hiçbiri etkin değilse Escape tuşu hiçbir şey yapmaz.
- Go to page ...
- Sayfata git...
+ Go to page...
+ Sayfaya git...
@@ -1190,17 +1195,19 @@ Hiçbiri etkin değilse Escape tuşu hiçbir şey yapmaz.
Help, About YACReader
- YACReader hakkında yardım ve bilgi
+ Yardım, YACReader Hakkında
+
+ Magnifying glassBüyüteçSwitch Magnifying glass
- Büyüteç
+ Büyüteci aç/kapat
@@ -1299,222 +1306,217 @@ Hiçbiri etkin değilse Escape tuşu hiçbir şey yapmaz.
Pencere
-
+ Open ComicÇizgi Romanı Aç
-
+ Comic filesÇizgi Roman Dosyaları
-
+ Open folder
- Dosyayı aç
+ Klasörü aç
-
-
+
+ Comics
- Çizgi Roman
+ Çizgi Romanlar
-
+ Toggle fullscreen mode
- Tam ekran kipini aç/kapat
+ Tam ekran modunu aç/kapat
-
+ Hide/show toolbarAraç çubuğunu göster/gizle
-
-
+
+ GeneralGenel
-
+ Size up magnifying glassBüyüteci büyüt
-
+ Size down magnifying glassBüyüteci küçült
-
+ Zoom in magnifying glass
- Büyüteci yakınlaştır
+ Büyüteç zoom'unu artır
-
+ Zoom out magnifying glass
- Büyüteci uzaklaştır
+ Büyüteç zoom'unu azalt
-
+ Reset magnifying glassBüyüteci sıfırla
-
-
- Magnifiying glass
- Büyüteç
-
-
-
+ Toggle between fit to width and fit to heightGenişliğe sığdır ile yüksekliğe sığdır arasında geçiş yap
-
-
- Page adjustement
+
+
+ Page adjustment
+ Page adjustementSayfa ayarı
-
+ The current page is not ready. Please try again later.Geçerli sayfa henüz hazır değil. Lütfen daha sonra tekrar deneyin.
-
+ Overwrite file?
-
+ Dosyanın üzerine yazılsın mı?
-
+ The file already exists. Do you want to overwrite it?
-
+ Dosya zaten var. Üzerine yazmak istiyor musunuz?
-
+ The current page could not be saved.Geçerli sayfa kaydedilemedi.
-
+ The selected pages are not ready. Please try again later.Seçilen sayfalar henüz hazır değil. Lütfen daha sonra tekrar deneyin.
-
+ The current page could not be extracted.
-
+ Geçerli sayfa çıkarılamadı.
-
+ Overwrite files?
-
+ Dosyaların üzerine yazılsın mı?
-
+ Some files already exist. Do you want to overwrite them?
-
+ Bazı dosyalar zaten var. Üzerlerine yazmak istiyor musunuz?
-
+ Some pages could not be extracted.
-
+ Bazı sayfalar çıkarılamadı.
-
+ Autoscroll downOtomatik aşağı kaydır
-
+ Autoscroll upOtomatik yukarı kaydır
-
+ Autoscroll forward, horizontal firstOtomatik ileri kaydır, önce yatay
-
+ Autoscroll backward, horizontal firstOtomatik geri kaydır, önce yatay
-
+ Autoscroll forward, vertical firstOtomatik ileri kaydır, önce dikey
-
+ Autoscroll backward, vertical firstOtomatik geri kaydır, önce dikey
-
+ Move downAşağı git
-
+ Move upYukarı git
-
+ Move leftSola git
-
+ Move rightSağa git
-
+ Go to the first pageİlk sayfaya git
-
+ Go to the last pageEn son sayfaya git
-
+ Offset double page to the leftÇift sayfayı sola kaydır
-
+ Offset double page to the rightÇift sayfayı sağa kaydır
-
-
+
+ ReadingOkuma
-
+ There is a new version available
- Yeni versiyon mevcut
+ Yeni sürüm mevcut
-
+ Do you want to download the new version?
- Yeni versiyonu indirmek ister misin ?
+ Yeni sürümü indirmek ister misiniz?
-
+ Remind me in 14 days14 gün içinde hatırlat
-
+ Not nowŞimdi değil
@@ -1524,12 +1526,12 @@ Hiçbiri etkin değilse Escape tuşu hiçbir şey yapmaz.
Release notes are not available.
-
+ Sürüm notları kullanılamıyor.Previous versions
-
+ Önceki sürümler
@@ -1541,7 +1543,7 @@ Hiçbiri etkin değilse Escape tuşu hiçbir şey yapmaz.
-
+ Click to overwriteÜzerine yazmak için tıkla
@@ -1555,9 +1557,9 @@ Hiçbiri etkin değilse Escape tuşu hiçbir şey yapmaz.
-
-
-
+
+
+ Click to overwriteÜstüne yazmak için tıkla
@@ -1600,7 +1602,7 @@ Hiçbiri etkin değilse Escape tuşu hiçbir şey yapmaz.
Reset
- Yeniden başlat
+ Sıfırla
diff --git a/YACReader/yacreader_zh_CN.ts b/YACReader/yacreader_zh_CN.ts
index 253f2c22f..e121e3c3a 100644
--- a/YACReader/yacreader_zh_CN.ts
+++ b/YACReader/yacreader_zh_CN.ts
@@ -39,7 +39,7 @@
Remove
- 消除
+ 移除
@@ -59,7 +59,7 @@
Custom:
- 自定义:
+ 自定义:
@@ -136,7 +136,7 @@
Loading...
- 载入中...
+ 加载中...
@@ -145,7 +145,7 @@
- Lastest Page
+ Latest Page尾页
@@ -176,41 +176,37 @@
- The shortcut "%1" is already assigned to other function
+ The shortcut "%1" is already assigned to another function快捷键 "%1" 已被映射至其他功能
- To change a shortcut, double click in the key combination and type the new keys.
- 更改快捷键: 双击按键组合并输入新的映射.
+ To change a shortcut, double-click the key combination and type the new keys.
+ 更改快捷键:双击按键组合并输入新的映射。FileComic
-
-
+
+ Format not supported不支持的文件格式
-
+ 7z not found未找到 7z
-
+ Unknown error opening the file打开文件时出现未知错误
- Unsupported EPUB: %1
- 不支持的 EPUB 格式:%1
-
-
-
+ CRC error on page (%1): some of the pages will not be displayed correctly
- 第 %1 页 CRC 校验失败: 部分页面将无法正确显示
+ 第 %1 页 CRC 校验失败:部分页面将无法正确显示
@@ -228,8 +224,8 @@
- Total pages :
- 总页数:
+ Total pages:
+ 总页数:
@@ -238,16 +234,16 @@
- Page :
- 页码 :
+ Page:
+ 页码: GoToFlowToolBar
- Page :
- 页码 :
+ Page:
+ 页码:
@@ -278,7 +274,7 @@
Gamma
- Gamma值
+ Gamma 值
@@ -306,7 +302,7 @@
我的漫画路径
-
+ Image adjustment图像调整
@@ -333,7 +329,7 @@
Fit options
- 适应项
+ 适应选项
@@ -343,7 +339,7 @@
Display
- 展示
+ 显示
@@ -378,7 +374,7 @@
Only Back/Forward buttons can turn pages
- 只有后退/前进按钮可以翻页
+ 仅“后退”/“前进”按钮可用于翻页
@@ -387,7 +383,7 @@
- Click left or right half of the screen to turn pages.
+ Click the left or right half of the screen to turn pages.单击屏幕的左半部分或右半部分即可翻页。
@@ -424,7 +420,7 @@ If none is active, Escape does nothing.
按 Esc 键会取消以下第一个处于活动状态的模式:
1. 放大镜
-2. 字典
+2. 词典
3. 跳转到页面栏
4. 全屏
@@ -451,12 +447,12 @@ If none is active, Escape does nothing.
显示书页折叠阴影
-
+ Appearance外观
-
+ Options选项
@@ -478,10 +474,10 @@ If none is active, Escape does nothing.
Clear
- 清空
+ 清除
-
+ Comics directory漫画目录
@@ -502,13 +498,13 @@ If none is active, Escape does nothing.
- Scroll behaviour
+ Scroll behavior滚动效果Disable mouse over activation
- 禁用鼠标激活
+ 禁用鼠标悬停激活
@@ -528,7 +524,7 @@ If none is active, Escape does nothing.
Bilinear
- 双线性
+ 双线性(Bilinear)
@@ -536,12 +532,17 @@ If none is active, Escape does nothing.
Lanczos(质量更好)
-
+
+ Bicubic
+ 双三次(Bicubic)
+
+
+ Page Flow页面流
-
+ General常规
@@ -551,7 +552,7 @@ If none is active, Escape does nothing.
亮度
-
+ Restart is needed需要重启
@@ -566,15 +567,15 @@ If none is active, Escape does nothing.
unable to load 7z lib from ./utils
- 无法从 ./utils 载入 7z 库文件
+ 无法从 ./utils 加载 7z 库文件
-
+ Select custom cover选择自定义封面
-
+ Images (%1)图片 (%1)
@@ -609,7 +610,7 @@ If none is active, Escape does nothing.
i
- 我
+ i
@@ -624,7 +625,7 @@ If none is active, Escape does nothing.
Hold to flash the selected value in the UI (magenta / toggled / 0↔10). Releases restore the original.
- 按住可在 UI 中闪烁所选值(洋红色/切换/0↔10)。发布后恢复原样。
+ 按住可在界面中闪烁所选值(洋红色/切换/0↔10)。松开后恢复原样。
@@ -654,7 +655,7 @@ If none is active, Escape does nothing.
Variant:
- 颜色设置:
+ 变体:
@@ -664,7 +665,7 @@ If none is active, Escape does nothing.
Parameter
- 范围
+ 参数
@@ -791,13 +792,13 @@ If none is active, Escape does nothing.
Page not available!
- 页面不可用!
+ 页面不可用!
- Press 'O' to open comic.
- 按下 'O' 以打开漫画.
+ Press 'O' to open a comic.
+ 按下 'O' 以打开漫画。
@@ -807,7 +808,7 @@ If none is active, Escape does nothing.
Cover!
- 封面!
+ 封面!
@@ -827,12 +828,12 @@ If none is active, Escape does nothing.
Last page!
- 尾页!
+ 尾页!
- Loading...please wait!
- 载入中... 请稍候!
+ Loading... please wait!
+ 加载中...请稍候!
@@ -840,7 +841,7 @@ If none is active, Escape does nothing.
Presets:
- 预设:
+ 预设:
@@ -875,7 +876,7 @@ If none is active, Escape does nothing.
Custom:
- 自定义:
+ 自定义:
@@ -920,12 +921,12 @@ If none is active, Escape does nothing.
Visibility
- 透明度
+ 可见度Light
- 明亮
+ 光照
@@ -944,13 +945,13 @@ If none is active, Escape does nothing.
- Use VSync (improve the image quality in fullscreen mode, worse performance)
- 使用VSync (在全屏模式下提高图像质量, 性能更差)
+ Use VSync (improves image quality in fullscreen mode, but reduces performance)
+ 使用 VSync(在全屏模式下提高图像质量,但会降低性能)Performance:
- 性能:
+ 性能:
@@ -1004,7 +1005,7 @@ If none is active, Escape does nothing.
Clear
- 清空
+ 清除
@@ -1012,7 +1013,7 @@ If none is active, Escape does nothing.
关闭
-
+ Open Comic打开漫画
@@ -1024,12 +1025,12 @@ If none is active, Escape does nothing.
Zoom+
- 放大
+ 放大+Zoom-
- 缩小
+ 缩小-
@@ -1037,14 +1038,14 @@ If none is active, Escape does nothing.
打开图片文件夹
-
+ Size down magnifying glass减小放大镜尺寸
-
+ Zoom out magnifying glass
- 减小缩放级别
+ 减小放大镜显示内容
@@ -1057,7 +1058,7 @@ If none is active, Escape does nothing.
打开最近的漫画
-
+ Autoscroll up向上自动滚动
@@ -1067,7 +1068,7 @@ If none is active, Escape does nothing.
设置书签
-
+ Autoscroll forward, vertical first向前自动滚动,垂直优先
@@ -1078,14 +1079,14 @@ If none is active, Escape does nothing.
-
-
-
+
+
+ Save current page保存当前页面
-
+ Size up magnifying glass增大放大镜尺寸
@@ -1095,7 +1096,7 @@ If none is active, Escape does nothing.
双页模式
-
+ Move up向上移动
@@ -1110,13 +1111,13 @@ If none is active, Escape does nothing.
打开文件夹
-
-
+
+ Comics漫画
-
+ Offset double page to the right双页向右偏移
@@ -1126,29 +1127,29 @@ If none is active, Escape does nothing.
适应高度
-
+ Autoscroll backward, vertical first向后自动滚动,垂直优先
-
+ Comic files漫画文件
-
+ Not now现在不
-
+ Go to the first page转到第一页
-
-
+
+ Go to previous page转至上一页
@@ -1160,7 +1161,7 @@ If none is active, Escape does nothing.
Open the latest comic opened in the previous reading session
- 打开最近阅读漫画
+ 打开上次阅读会话中最后打开的漫画
@@ -1175,7 +1176,7 @@ If none is active, Escape does nothing.
Fit Width
- 适合宽度
+ 适应宽度
@@ -1188,19 +1189,21 @@ If none is active, Escape does nothing.
显示信息
-
+ Open folder打开文件夹
- Go to page ...
- 跳转至页面 ...
+ Go to page...
+ 跳转至页面...
-
-
- Magnifiying glass
+
+
+
+ Magnifying glass
+ Magnifiying glass放大镜
@@ -1209,24 +1212,24 @@ If none is active, Escape does nothing.
缩放图片以适应宽度
-
+ Toggle fullscreen mode切换全屏模式
-
+ Toggle between fit to width and fit to height
- 切换显示为"适应宽度"或"适应高度"
+ 切换显示为“适应宽度”或“适应高度”
-
+ Move right向右移动
-
+ Zoom in magnifying glass
- 增大缩放级别
+ 增大放大镜显示内容
@@ -1235,13 +1238,13 @@ If none is active, Escape does nothing.
最近打开的文件
-
+ Offset double page to the left双页向左偏移
-
-
+
+ Reading阅读
@@ -1251,14 +1254,14 @@ If none is active, Escape does nothing.
上一页(&P)
-
+ Autoscroll forward, horizontal first向前自动滚动,水平优先
-
-
+
+ Go to next page转至下一页
@@ -1273,24 +1276,24 @@ If none is active, Escape does nothing.
双页日漫模式
-
+ There is a new version available有新版本可用
-
+ Autoscroll down向下自动滚动
-
-
+
+ Open next comic打开下一个漫画
-
+ Remind me in 14 days14天后提醒我
@@ -1306,8 +1309,8 @@ If none is active, Escape does nothing.
-
-
+
+ Open previous comic打开上一个漫画
@@ -1323,10 +1326,10 @@ If none is active, Escape does nothing.
-
-
-
-
+
+
+
+ Extract page(s)提取页面
@@ -1368,72 +1371,72 @@ If none is active, Escape does nothing.
Show Dictionary
- 显示字典
+ 显示词典
-
+ The current page is not ready. Please try again later.当前页面尚未准备就绪。请稍后重试。
-
+ Overwrite file?覆盖文件?
-
+ The file already exists. Do you want to overwrite it?
- 文件已存在。是否要覆盖它?
+ 文件已存在。是否要覆盖它?
-
+ The current page could not be saved.无法保存当前页面。
-
+ The selected pages are not ready. Please try again later.所选页面尚未准备就绪。请稍后重试。
-
+ The current page could not be extracted.无法提取当前页面。
-
+ Overwrite files?覆盖文件?
-
+ Some files already exist. Do you want to overwrite them?
- 部分文件已存在。是否要覆盖它们?
+ 部分文件已存在。是否要覆盖它们?
-
+ Some pages could not be extracted.部分页面无法提取。
-
+ Reset magnifying glass重置放大镜
-
+ Move down向下移动
-
+ Move left向左移动Reverse reading order in double page mode
- 双页模式 (逆序阅读)
+ 双页模式(逆序阅读)
@@ -1443,12 +1446,12 @@ If none is active, Escape does nothing.
Clear open recent list
- 清空最近访问列表
+ 清除最近打开列表Help, About YACReader
- 帮助, 关于 YACReader
+ 帮助,关于 YACReader
@@ -1466,23 +1469,18 @@ If none is active, Escape does nothing.
显示全尺寸
-
+ Hide/show toolbar隐藏/显示 工具栏
-
-
- Magnifying glass
- 放大镜
- Edit shortcuts编辑快捷键
-
-
+
+ General常规
@@ -1492,9 +1490,10 @@ If none is active, Escape does nothing.
在当前页面设置书签
-
-
- Page adjustement
+
+
+ Page adjustment
+ Page adjustement页面调整
@@ -1503,14 +1502,14 @@ If none is active, Escape does nothing.
显示缩放滑块
-
+ Go to the last page转到最后一页
-
+ Do you want to download the new version?
- 你要下载新版本吗?
+ 您要下载新版本吗?
@@ -1518,7 +1517,7 @@ If none is active, Escape does nothing.
向右旋转图片
-
+ Autoscroll backward, horizontal first向后自动滚动,水平优先
@@ -1528,12 +1527,12 @@ If none is active, Escape does nothing.
Release notes are not available.
- 暂无发行说明。
+ 发行说明不可用。Previous versions
- 先前版本
+ 以前的版本
@@ -1545,7 +1544,7 @@ If none is active, Escape does nothing.
-
+ Click to overwrite点击以覆盖
@@ -1559,9 +1558,9 @@ If none is active, Escape does nothing.
-
-
-
+
+
+ Click to overwrite点击以覆盖
@@ -1612,7 +1611,7 @@ If none is active, Escape does nothing.
clear
- 清空
+ 清除
diff --git a/YACReader/yacreader_zh_HK.ts b/YACReader/yacreader_zh_HK.ts
index c4eba1828..ccd1a4f51 100644
--- a/YACReader/yacreader_zh_HK.ts
+++ b/YACReader/yacreader_zh_HK.ts
@@ -24,22 +24,22 @@
Light
- 亮度
+ 淺色Dark
- 黑暗的
+ 深色Custom
- 風俗
+ 自訂Remove
- 消除
+ 移除
@@ -49,22 +49,22 @@
Light:
- 光:
+ 淺色:Dark:
- 黑暗的:
+ 深色:Custom:
- 自定義:
+ 自訂:Import theme...
- 導入主題...
+ 匯入主題...
@@ -94,7 +94,7 @@
Import theme
- 導入主題
+ 匯入主題
@@ -122,14 +122,14 @@
Import failed
- 導入失敗
+ 匯入失敗BookmarksDialog
- Lastest Page
+ Latest Page尾頁
@@ -162,17 +162,17 @@
Restore defaults
- 恢復默認
+ 恢復預設
- To change a shortcut, double click in the key combination and type the new keys.
- 更改快捷鍵: 雙擊按鍵組合並輸入新的映射.
+ To change a shortcut, double-click the key combination and type the new keys.
+ 更改快捷鍵:雙擊按鍵組合並輸入新的映射。Shortcuts settings
- 快捷鍵設置
+ 快捷鍵設定
@@ -181,40 +181,40 @@
- The shortcut "%1" is already assigned to other function
- 快捷鍵 "%1" 已被映射至其他功能
+ The shortcut "%1" is already assigned to another function
+ 快捷鍵「%1」已被映射至其他功能FileComic
-
+ CRC error on page (%1): some of the pages will not be displayed correctly
- 第 %1 頁 CRC 校驗失敗: 部分頁面將無法正確顯示
+ 第 %1 頁 CRC 校驗失敗:部分頁面將無法正確顯示
-
+ Unknown error opening the file
- 打開檔時出現未知錯誤
+ 開啟檔案時出現未知錯誤
-
+ 7z not found未找到 7z
-
-
+
+ Format not supported
- 不支持的檔格式
+ 不支援的檔案格式GoToDialog
- Page :
- 頁碼 :
+ Page:
+ 頁碼:
@@ -229,8 +229,8 @@
- Total pages :
- 總頁數:
+ Total pages:
+ 總頁數:
@@ -242,8 +242,8 @@
GoToFlowToolBar
- Page :
- 頁碼 :
+ Page:
+ 頁碼:
@@ -266,7 +266,7 @@
Changelog
-
+ 更新日誌
@@ -299,7 +299,7 @@
Disable mouse over activation
- 禁用滑鼠啟動
+ 停用滑鼠懸停啟動
@@ -319,7 +319,7 @@
Bilinear
- 雙線性
+ Bilinear
@@ -327,7 +327,7 @@
Lanczos(品質更好)
-
+ Restart is needed需要重啟
@@ -339,7 +339,7 @@
Display
- 展示
+ 顯示
@@ -368,7 +368,7 @@
- Scroll behaviour
+ Scroll behavior滾動效果
@@ -403,7 +403,7 @@
- Click left or right half of the screen to turn pages.
+ Click the left or right half of the screen to turn pages.點擊螢幕的左半部或右半部即可翻頁。
@@ -440,7 +440,7 @@ If none is active, Escape does nothing.
按 Esc 鍵會取消下列第一個正在使用的模式:
1. 放大鏡
-2. 字典
+2. 詞典
3. 前往頁面列
4. 全螢幕
@@ -464,12 +464,12 @@ If none is active, Escape does nothing.
Gamma
- Gamma值
+ Gamma 值Reset
- 重置
+ 重設
@@ -479,7 +479,7 @@ If none is active, Escape does nothing.
Fit options
- 適應項
+ 適應選項
@@ -502,14 +502,19 @@ If none is active, Escape does nothing.
顯示書頁摺疊陰影
-
+
+ Bicubic
+
+
+
+ General常規
-
+ Appearance
- 外貌
+ 外觀
@@ -532,22 +537,22 @@ If none is active, Escape does nothing.
清空
-
+ Page Flow頁面流
-
+ Image adjustment
- 圖像調整
+ 影像調整
-
+ Options選項
-
+ Comics directory漫畫目錄
@@ -557,20 +562,20 @@ If none is active, Escape does nothing.
7z lib not found
- 未找到 7z 庫檔
+ 找不到 7z 函式庫unable to load 7z lib from ./utils
- 無法從 ./utils 載入 7z 庫檔
+ 無法從 ./utils 載入 7z 函式庫
-
+ Select custom cover選擇自訂封面
-
+ Images (%1)圖片 (%1)
@@ -605,7 +610,7 @@ If none is active, Escape does nothing.
i
- 我
+ i
@@ -620,7 +625,7 @@ If none is active, Escape does nothing.
Hold to flash the selected value in the UI (magenta / toggled / 0↔10). Releases restore the original.
- 按住可在 UI 中閃爍所選值(洋紅色/切換/0↔10)。發布後恢復原樣。
+ 按住可在 UI 中閃爍所選值(洋紅色/切換/0↔10)。放開後恢復原樣。
@@ -630,12 +635,12 @@ If none is active, Escape does nothing.
Light
- 亮度
+ 淺色Dark
- 黑暗的
+ 深色
@@ -655,27 +660,27 @@ If none is active, Escape does nothing.
Theme info
- 主題訊息
+ 主題資訊Parameter
- 範圍
+ 參數Value
- 價值
+ 值Save and apply
- 儲存並應用
+ 儲存並套用Export to file...
- 匯出到文件...
+ 匯出到檔案...
@@ -700,7 +705,7 @@ If none is active, Escape does nothing.
true
- 真的
+ true
@@ -708,12 +713,12 @@ If none is active, Escape does nothing.
false
- 錯誤的
+ falseDouble-click to toggle
- 按兩下切換
+ 雙擊切換
@@ -741,13 +746,13 @@ If none is active, Escape does nothing.
Save failed
- 保存失敗
+ 儲存失敗Could not open file for writing:
%1
- 無法開啟文件進行寫入:
+ 無法開啟檔案進行寫入:
%1
@@ -787,8 +792,8 @@ If none is active, Escape does nothing.
- Press 'O' to open comic.
- 按下 'O' 以打開漫畫.
+ Press 'O' to open a comic.
+ 按下 'O' 以開啟漫畫。
@@ -803,7 +808,7 @@ If none is active, Escape does nothing.
Error opening comic
- 打開漫畫時發生錯誤
+ 開啟漫畫時發生錯誤
@@ -812,23 +817,23 @@ If none is active, Escape does nothing.
- Loading...please wait!
- 載入中... 請稍候!
+ Loading... please wait!
+ 載入中... 請稍候!Page not available!
- 頁面不可用!
+ 頁面不可用!Cover!
- 封面!
+ 封面!Last page!
- 尾頁!
+ 尾頁!
@@ -836,7 +841,7 @@ If none is active, Escape does nothing.
Presets:
- 預設:
+ 預設:
@@ -866,12 +871,12 @@ If none is active, Escape does nothing.
Show advanced settings
- 顯示高級選項
+ 顯示進階選項Custom:
- 自定義:
+ 自訂:
@@ -896,7 +901,7 @@ If none is active, Escape does nothing.
Zoom
- 縮放
+ Zoom
@@ -916,12 +921,12 @@ If none is active, Escape does nothing.
Visibility
- 透明度
+ 可見度Light
- 亮度
+ 光照
@@ -931,22 +936,22 @@ If none is active, Escape does nothing.
Low Performance
- 低性能
+ 低效能High Performance
- 高性能
+ 高效能
- Use VSync (improve the image quality in fullscreen mode, worse performance)
- 使用VSync (在全屏模式下提高圖像品質, 性能更差)
+ Use VSync (improves image quality in fullscreen mode, but reduces performance)
+ 使用 VSync(在全螢幕模式下提高影像品質,但會降低效能)Performance:
- 性能:
+ 效能:
@@ -954,12 +959,12 @@ If none is active, Escape does nothing.
&Open
- 打開(&O)
+ 開啟(&O)Open a comic
- 打開漫畫
+ 開啟漫畫
@@ -969,22 +974,22 @@ If none is active, Escape does nothing.
Open Folder
- 打開檔夾
+ 開啟資料夾Open image folder
- 打開圖片檔夾
+ 開啟圖片資料夾Open latest comic
- 打開最近的漫畫
+ 開啟最近的漫畫Open the latest comic opened in the previous reading session
- 打開最近閱讀漫畫
+ 開啟上次閱讀工作階段中最後開啟的漫畫
@@ -994,34 +999,34 @@ If none is active, Escape does nothing.
Clear open recent list
- 清空最近訪問列表
+ 清除最近開啟清單Save
- 保存
+ 儲存
-
-
-
+
+
+ Save current page
- 保存當前頁面
+ 儲存目前頁面
-
-
-
-
+
+
+
+ Extract page(s)
-
+ 擷取頁面Extract page(s) from the original source
-
+ 從原始來源擷取頁面
@@ -1030,10 +1035,10 @@ If none is active, Escape does nothing.
-
-
+
+ Open previous comic
- 打開上一個漫畫
+ 開啟上一個漫畫
@@ -1042,10 +1047,10 @@ If none is active, Escape does nothing.
-
-
+
+ Open next comic
- 打開下一個漫畫
+ 開啟下一個漫畫
@@ -1054,8 +1059,8 @@ If none is active, Escape does nothing.
-
-
+
+ Go to previous page轉至上一頁
@@ -1066,8 +1071,8 @@ If none is active, Escape does nothing.
-
-
+
+ Go to next page轉至下一頁
@@ -1084,7 +1089,7 @@ If none is active, Escape does nothing.
Fit Width
- 適合寬度
+ 適應寬度
@@ -1114,22 +1119,22 @@ If none is active, Escape does nothing.
Reset zoom
- 重置縮放
+ 重設 ZoomShow zoom slider
- 顯示縮放滑塊
+ 顯示 Zoom 滑桿Zoom+
- 放大
+ Zoom+Zoom-
- 縮小
+ Zoom-
@@ -1159,7 +1164,7 @@ If none is active, Escape does nothing.
Reverse reading order in double page mode
- 雙頁模式 (逆序閱讀)
+ 雙頁模式(逆序閱讀)
@@ -1168,8 +1173,8 @@ If none is active, Escape does nothing.
- Go to page ...
- 跳轉至頁面 ...
+ Go to page...
+ 跳轉至頁面...
@@ -1190,10 +1195,12 @@ If none is active, Escape does nothing.
Help, About YACReader
- 幫助, 關於 YACReader
+ 幫助,關於 YACReader
+
+ Magnifying glass放大鏡
@@ -1205,12 +1212,12 @@ If none is active, Escape does nothing.
Set bookmark
- 設置書簽
+ 設定書簽Set a bookmark on the current page
- 在當前頁面設置書簽
+ 在目前頁面設定書簽
@@ -1220,7 +1227,7 @@ If none is active, Escape does nothing.
Show the bookmarks of the current comic
- 顯示當前漫畫的書簽
+ 顯示目前漫畫的書簽
@@ -1250,7 +1257,7 @@ If none is active, Escape does nothing.
Show Dictionary
- 顯示字典
+ 顯示詞典
@@ -1265,18 +1272,18 @@ If none is active, Escape does nothing.
&File
- 檔(&F)
+ 檔案(&F)Open recent
- 最近打開的檔
+ 最近開啟的檔案File
- 檔
+ 檔案
@@ -1286,7 +1293,7 @@ If none is active, Escape does nothing.
View
- 查看
+ 檢視
@@ -1296,225 +1303,220 @@ If none is active, Escape does nothing.
Window
- 窗口
+ 視窗
-
+ Open Comic
- 打開漫畫
+ 開啟漫畫
-
+ Comic files
- 漫畫檔
+ 漫畫檔案
-
+ Open folder
- 打開檔夾
+ 開啟資料夾
-
-
+
+ Comics漫畫
-
+ Toggle fullscreen mode
- 切換全屏模式
+ 切換全螢幕模式
-
+ Hide/show toolbar
- 隱藏/顯示 工具欄
+ 隱藏/顯示工具列
-
-
+
+ General常規
-
+ Size up magnifying glass增大放大鏡尺寸
-
+ Size down magnifying glass減小放大鏡尺寸
-
+ Zoom in magnifying glass
- 增大縮放級別
+ 增大放大鏡的 Zoom
-
+ Zoom out magnifying glass
- 減小縮放級別
+ 減小放大鏡的 Zoom
-
+ Reset magnifying glass重置放大鏡
-
-
- Magnifiying glass
- 放大鏡
-
-
-
+ Toggle between fit to width and fit to height
- 切換顯示為"適應寬度"或"適應高度"
+ 切換顯示為「適應寬度」或「適應高度」
-
-
- Page adjustement
+
+
+ Page adjustment
+ Page adjustement頁面調整
-
+ The current page is not ready. Please try again later.目前頁面尚未準備就緒。請稍後再試。
-
+ Overwrite file?
-
+ 要覆寫檔案嗎?
-
+ The file already exists. Do you want to overwrite it?
-
+ 檔案已存在。要覆寫嗎?
-
+ The current page could not be saved.無法儲存目前頁面。
-
+ The selected pages are not ready. Please try again later.所選頁面尚未準備就緒。請稍後再試。
-
+ The current page could not be extracted.
-
+ 無法擷取目前頁面。
-
+ Overwrite files?
-
+ 要覆寫這些檔案嗎?
-
+ Some files already exist. Do you want to overwrite them?
-
+ 部分檔案已存在。要覆寫它們嗎?
-
+ Some pages could not be extracted.
-
+ 部分頁面無法擷取。
-
+ Autoscroll down向下自動滾動
-
+ Autoscroll up向上自動滾動
-
+ Autoscroll forward, horizontal first
- 向前自動滾動,水準優先
+ 向前自動滾動,水平優先
-
+ Autoscroll backward, horizontal first
- 向後自動滾動,水準優先
+ 向後自動滾動,水平優先
-
+ Autoscroll forward, vertical first向前自動滾動,垂直優先
-
+ Autoscroll backward, vertical first向後自動滾動,垂直優先
-
+ Move down向下移動
-
+ Move up向上移動
-
+ Move left向左移動
-
+ Move right向右移動
-
+ Go to the first page轉到第一頁
-
+ Go to the last page轉到最後一頁
-
+ Offset double page to the left雙頁向左偏移
-
+ Offset double page to the right雙頁向右偏移
-
-
+
+ Reading閱讀
-
+ There is a new version available有新版本可用
-
+ Do you want to download the new version?
- 你要下載新版本嗎?
+ 您要下載新版本嗎?
-
+ Remind me in 14 days14天後提醒我
-
+ Not now現在不
@@ -1524,42 +1526,42 @@ If none is active, Escape does nothing.
Release notes are not available.
-
+ 版本資訊無法使用。Previous versions
-
+ 舊版本YACReaderFieldEdit
-
+ Click to overwrite點擊以覆蓋Restore to default
- 恢復默認
+ 恢復預設YACReaderFieldPlainTextEdit
-
-
-
+
+
+ Click to overwrite點擊以覆蓋Restore to default
- 恢復默認
+ 恢復預設
@@ -1567,7 +1569,7 @@ If none is active, Escape does nothing.
Save
- 保存
+ 儲存
@@ -1600,7 +1602,7 @@ If none is active, Escape does nothing.
Reset
- 重置
+ 重設
diff --git a/YACReader/yacreader_zh_TW.ts b/YACReader/yacreader_zh_TW.ts
index 310ed55e5..60e186948 100644
--- a/YACReader/yacreader_zh_TW.ts
+++ b/YACReader/yacreader_zh_TW.ts
@@ -24,22 +24,22 @@
Light
- 亮度
+ 淺色Dark
- 黑暗的
+ 深色Custom
- 風俗
+ 自訂Remove
- 消除
+ 移除
@@ -49,22 +49,22 @@
Light:
- 光:
+ 淺色:Dark:
- 黑暗的:
+ 深色:Custom:
- 自定義:
+ 自訂:Import theme...
- 導入主題...
+ 匯入主題...
@@ -94,7 +94,7 @@
Import theme
- 導入主題
+ 匯入主題
@@ -122,14 +122,14 @@
Import failed
- 導入失敗
+ 匯入失敗BookmarksDialog
- Lastest Page
+ Latest Page尾頁
@@ -162,59 +162,59 @@
Restore defaults
- 恢復默認
+ 恢復預設
- To change a shortcut, double click in the key combination and type the new keys.
- 更改快捷鍵: 雙擊按鍵組合並輸入新的映射.
+ To change a shortcut, double-click the key combination and type the new keys.
+ 更改快速鍵:雙擊按鍵組合並輸入新的映射。Shortcuts settings
- 快捷鍵設置
+ 快速鍵設定Shortcut in use
- 快捷鍵被佔用
+ 快速鍵被佔用
- The shortcut "%1" is already assigned to other function
- 快捷鍵 "%1" 已被映射至其他功能
+ The shortcut "%1" is already assigned to another function
+ 快速鍵「%1」已被映射至其他功能FileComic
-
+ CRC error on page (%1): some of the pages will not be displayed correctly
- 第 %1 頁 CRC 校驗失敗: 部分頁面將無法正確顯示
+ 第 %1 頁 CRC 校驗失敗:部分頁面將無法正確顯示
-
+ Unknown error opening the file
- 打開檔時出現未知錯誤
+ 開啟檔案時出現未知錯誤
-
+ 7z not found未找到 7z
-
-
+
+ Format not supported
- 不支持的檔格式
+ 不支援的檔案格式GoToDialog
- Page :
- 頁碼 :
+ Page:
+ 頁碼:
@@ -229,8 +229,8 @@
- Total pages :
- 總頁數:
+ Total pages:
+ 總頁數:
@@ -242,8 +242,8 @@
GoToFlowToolBar
- Page :
- 頁碼 :
+ Page:
+ 頁碼:
@@ -266,7 +266,7 @@
Changelog
-
+ 更新日誌
@@ -299,7 +299,7 @@
Disable mouse over activation
- 禁用滑鼠啟動
+ 停用滑鼠懸停啟動
@@ -319,7 +319,7 @@
Bilinear
- 雙線性
+ Bilinear
@@ -327,7 +327,7 @@
Lanczos(品質更好)
-
+ Restart is needed需要重啟
@@ -339,7 +339,7 @@
Display
- 展示
+ 顯示
@@ -368,7 +368,7 @@
- Scroll behaviour
+ Scroll behavior滾動效果
@@ -403,7 +403,7 @@
- Click left or right half of the screen to turn pages.
+ Click the left or right half of the screen to turn pages.點擊螢幕的左半部或右半部即可翻頁。
@@ -440,7 +440,7 @@ If none is active, Escape does nothing.
按 Esc 鍵會取消下列第一個正在使用的模式:
1. 放大鏡
-2. 字典
+2. 詞典
3. 前往頁面列
4. 全螢幕
@@ -464,12 +464,12 @@ If none is active, Escape does nothing.
Gamma
- Gamma值
+ Gamma 值Reset
- 重置
+ 重設
@@ -479,7 +479,7 @@ If none is active, Escape does nothing.
Fit options
- 適應項
+ 適應選項
@@ -502,14 +502,19 @@ If none is active, Escape does nothing.
顯示書頁折疊陰影
-
+
+ Bicubic
+
+
+
+ General常規
-
+ Appearance
- 外貌
+ 外觀
@@ -532,22 +537,22 @@ If none is active, Escape does nothing.
清空
-
+ Page Flow頁面流
-
+ Image adjustment
- 圖像調整
+ 影像調整
-
+ Options選項
-
+ Comics directory漫畫目錄
@@ -557,20 +562,20 @@ If none is active, Escape does nothing.
7z lib not found
- 未找到 7z 庫檔
+ 找不到 7z 函式庫unable to load 7z lib from ./utils
- 無法從 ./utils 載入 7z 庫檔
+ 無法從 ./utils 載入 7z 函式庫
-
+ Select custom cover選擇自訂封面
-
+ Images (%1)圖片 (%1)
@@ -605,7 +610,7 @@ If none is active, Escape does nothing.
i
- 我
+ i
@@ -620,7 +625,7 @@ If none is active, Escape does nothing.
Hold to flash the selected value in the UI (magenta / toggled / 0↔10). Releases restore the original.
- 按住可在 UI 中閃爍所選值(洋紅色/切換/0↔10)。發布後恢復原樣。
+ 按住可在 UI 中閃爍所選值(洋紅色/切換/0↔10)。放開後恢復原樣。
@@ -630,12 +635,12 @@ If none is active, Escape does nothing.
Light
- 亮度
+ 淺色Dark
- 黑暗的
+ 深色
@@ -655,27 +660,27 @@ If none is active, Escape does nothing.
Theme info
- 主題訊息
+ 主題資訊Parameter
- 範圍
+ 參數Value
- 價值
+ 值Save and apply
- 儲存並應用
+ 儲存並套用Export to file...
- 匯出到文件...
+ 匯出到檔案...
@@ -700,7 +705,7 @@ If none is active, Escape does nothing.
true
- 真的
+ true
@@ -708,12 +713,12 @@ If none is active, Escape does nothing.
false
- 錯誤的
+ falseDouble-click to toggle
- 按兩下切換
+ 雙擊切換
@@ -741,13 +746,13 @@ If none is active, Escape does nothing.
Save failed
- 保存失敗
+ 儲存失敗Could not open file for writing:
%1
- 無法開啟文件進行寫入:
+ 無法開啟檔案進行寫入:
%1
@@ -787,8 +792,8 @@ If none is active, Escape does nothing.
- Press 'O' to open comic.
- 按下 'O' 以打開漫畫.
+ Press 'O' to open a comic.
+ 按下 'O' 以開啟漫畫。
@@ -803,7 +808,7 @@ If none is active, Escape does nothing.
Error opening comic
- 打開漫畫時發生錯誤
+ 開啟漫畫時發生錯誤
@@ -812,23 +817,23 @@ If none is active, Escape does nothing.
- Loading...please wait!
- 載入中... 請稍候!
+ Loading... please wait!
+ 載入中... 請稍候!Page not available!
- 頁面不可用!
+ 頁面不可用!Cover!
- 封面!
+ 封面!Last page!
- 尾頁!
+ 尾頁!
@@ -836,7 +841,7 @@ If none is active, Escape does nothing.
Presets:
- 預設:
+ 預設:
@@ -866,12 +871,12 @@ If none is active, Escape does nothing.
Show advanced settings
- 顯示高級選項
+ 顯示進階選項Custom:
- 自定義:
+ 自訂:
@@ -896,7 +901,7 @@ If none is active, Escape does nothing.
Zoom
- 縮放
+ Zoom
@@ -916,12 +921,12 @@ If none is active, Escape does nothing.
Visibility
- 透明度
+ 可見度Light
- 亮度
+ 光照
@@ -931,22 +936,22 @@ If none is active, Escape does nothing.
Low Performance
- 低性能
+ 低效能High Performance
- 高性能
+ 高效能
- Use VSync (improve the image quality in fullscreen mode, worse performance)
- 使用VSync (在全屏模式下提高圖像品質, 性能更差)
+ Use VSync (improves image quality in fullscreen mode, but reduces performance)
+ 使用 VSync(在全螢幕模式下提高影像品質,但會降低效能)Performance:
- 性能:
+ 效能:
@@ -954,12 +959,12 @@ If none is active, Escape does nothing.
&Open
- 打開(&O)
+ 開啟(&O)Open a comic
- 打開漫畫
+ 開啟漫畫
@@ -969,22 +974,22 @@ If none is active, Escape does nothing.
Open Folder
- 打開檔夾
+ 開啟資料夾Open image folder
- 打開圖片檔夾
+ 開啟圖片資料夾Open latest comic
- 打開最近的漫畫
+ 開啟最近的漫畫Open the latest comic opened in the previous reading session
- 打開最近閱讀漫畫
+ 開啟上次閱讀工作階段中最後開啟的漫畫
@@ -994,34 +999,34 @@ If none is active, Escape does nothing.
Clear open recent list
- 清空最近訪問列表
+ 清除最近開啟清單Save
- 保存
+ 儲存
-
-
-
+
+
+ Save current page
- 保存當前頁面
+ 儲存目前頁面
-
-
-
-
+
+
+
+ Extract page(s)
-
+ 擷取頁面Extract page(s) from the original source
-
+ 從原始來源擷取頁面
@@ -1030,10 +1035,10 @@ If none is active, Escape does nothing.
-
-
+
+ Open previous comic
- 打開上一個漫畫
+ 開啟上一個漫畫
@@ -1042,10 +1047,10 @@ If none is active, Escape does nothing.
-
-
+
+ Open next comic
- 打開下一個漫畫
+ 開啟下一個漫畫
@@ -1054,8 +1059,8 @@ If none is active, Escape does nothing.
-
-
+
+ Go to previous page轉至上一頁
@@ -1066,8 +1071,8 @@ If none is active, Escape does nothing.
-
-
+
+ Go to next page轉至下一頁
@@ -1084,7 +1089,7 @@ If none is active, Escape does nothing.
Fit Width
- 適合寬度
+ 適應寬度
@@ -1114,22 +1119,22 @@ If none is active, Escape does nothing.
Reset zoom
- 重置縮放
+ 重設 ZoomShow zoom slider
- 顯示縮放滑塊
+ 顯示 Zoom 滑桿Zoom+
- 放大
+ Zoom+Zoom-
- 縮小
+ Zoom-
@@ -1159,7 +1164,7 @@ If none is active, Escape does nothing.
Reverse reading order in double page mode
- 雙頁模式 (逆序閱讀)
+ 雙頁模式(逆序閱讀)
@@ -1168,8 +1173,8 @@ If none is active, Escape does nothing.
- Go to page ...
- 跳轉至頁面 ...
+ Go to page...
+ 跳轉至頁面...
@@ -1190,10 +1195,12 @@ If none is active, Escape does nothing.
Help, About YACReader
- 幫助, 關於 YACReader
+ 幫助,關於 YACReader
+
+ Magnifying glass放大鏡
@@ -1205,12 +1212,12 @@ If none is active, Escape does nothing.
Set bookmark
- 設置書簽
+ 設定書簽Set a bookmark on the current page
- 在當前頁面設置書簽
+ 在目前頁面設定書簽
@@ -1220,12 +1227,12 @@ If none is active, Escape does nothing.
Show the bookmarks of the current comic
- 顯示當前漫畫的書簽
+ 顯示目前漫畫的書簽Show keyboard shortcuts
- 顯示鍵盤快捷鍵
+ 顯示鍵盤快速鍵
@@ -1250,7 +1257,7 @@ If none is active, Escape does nothing.
Show Dictionary
- 顯示字典
+ 顯示詞典
@@ -1260,23 +1267,23 @@ If none is active, Escape does nothing.
Edit shortcuts
- 編輯快捷鍵
+ 編輯快速鍵&File
- 檔(&F)
+ 檔案(&F)Open recent
- 最近打開的檔
+ 最近開啟的檔案File
- 檔
+ 檔案
@@ -1286,7 +1293,7 @@ If none is active, Escape does nothing.
View
- 查看
+ 檢視
@@ -1296,225 +1303,220 @@ If none is active, Escape does nothing.
Window
- 窗口
+ 視窗
-
+ Open Comic
- 打開漫畫
+ 開啟漫畫
-
+ Comic files
- 漫畫檔
+ 漫畫檔案
-
+ Open folder
- 打開檔夾
+ 開啟資料夾
-
-
+
+ Comics漫畫
-
+ Toggle fullscreen mode
- 切換全屏模式
+ 切換全螢幕模式
-
+ Hide/show toolbar
- 隱藏/顯示 工具欄
+ 隱藏/顯示工具列
-
-
+
+ General常規
-
+ Size up magnifying glass增大放大鏡尺寸
-
+ Size down magnifying glass減小放大鏡尺寸
-
+ Zoom in magnifying glass
- 增大縮放級別
+ 增大放大鏡的 Zoom
-
+ Zoom out magnifying glass
- 減小縮放級別
+ 減小放大鏡的 Zoom
-
+ Reset magnifying glass重置放大鏡
-
-
- Magnifiying glass
- 放大鏡
-
-
-
+ Toggle between fit to width and fit to height
- 切換顯示為"適應寬度"或"適應高度"
+ 切換顯示為「適應寬度」或「適應高度」
-
-
- Page adjustement
+
+
+ Page adjustment
+ Page adjustement頁面調整
-
+ The current page is not ready. Please try again later.目前頁面尚未準備就緒。請稍後再試。
-
+ Overwrite file?
-
+ 要覆寫檔案嗎?
-
+ The file already exists. Do you want to overwrite it?
-
+ 檔案已存在。要覆寫嗎?
-
+ The current page could not be saved.無法儲存目前頁面。
-
+ The selected pages are not ready. Please try again later.所選頁面尚未準備就緒。請稍後再試。
-
+ The current page could not be extracted.
-
+ 無法擷取目前頁面。
-
+ Overwrite files?
-
+ 要覆寫這些檔案嗎?
-
+ Some files already exist. Do you want to overwrite them?
-
+ 部分檔案已存在。要覆寫它們嗎?
-
+ Some pages could not be extracted.
-
+ 部分頁面無法擷取。
-
+ Autoscroll down向下自動滾動
-
+ Autoscroll up向上自動滾動
-
+ Autoscroll forward, horizontal first
- 向前自動滾動,水準優先
+ 向前自動滾動,水平優先
-
+ Autoscroll backward, horizontal first
- 向後自動滾動,水準優先
+ 向後自動滾動,水平優先
-
+ Autoscroll forward, vertical first向前自動滾動,垂直優先
-
+ Autoscroll backward, vertical first向後自動滾動,垂直優先
-
+ Move down向下移動
-
+ Move up向上移動
-
+ Move left向左移動
-
+ Move right向右移動
-
+ Go to the first page轉到第一頁
-
+ Go to the last page轉到最後一頁
-
+ Offset double page to the left雙頁向左偏移
-
+ Offset double page to the right雙頁向右偏移
-
-
+
+ Reading閱讀
-
+ There is a new version available有新版本可用
-
+ Do you want to download the new version?
- 你要下載新版本嗎?
+ 您要下載新版本嗎?
-
+ Remind me in 14 days14天後提醒我
-
+ Not now現在不
@@ -1524,42 +1526,42 @@ If none is active, Escape does nothing.
Release notes are not available.
-
+ 版本資訊無法使用。Previous versions
-
+ 舊版本YACReaderFieldEdit
-
+ Click to overwrite點擊以覆蓋Restore to default
- 恢復默認
+ 恢復預設YACReaderFieldPlainTextEdit
-
-
-
+
+
+ Click to overwrite點擊以覆蓋Restore to default
- 恢復默認
+ 恢復預設
@@ -1567,7 +1569,7 @@ If none is active, Escape does nothing.
Save
- 保存
+ 儲存
@@ -1587,12 +1589,12 @@ If none is active, Escape does nothing.
Edit shortcuts
- 編輯快捷鍵
+ 編輯快速鍵Shortcuts
- 快捷鍵
+ 快速鍵
@@ -1600,7 +1602,7 @@ If none is active, Escape does nothing.
Reset
- 重置
+ 重設
diff --git a/YACReaderLibrary/CMakeLists.txt b/YACReaderLibrary/CMakeLists.txt
index 8641fc102..7efb46aca 100644
--- a/YACReaderLibrary/CMakeLists.txt
+++ b/YACReaderLibrary/CMakeLists.txt
@@ -24,6 +24,16 @@ add_library(library_common STATIC
yacreader_libraries.cpp
comic_files_manager.h
comic_files_manager.cpp
+ staged_cover.h
+ staged_cover.cpp
+ placeholder_cover.h
+ placeholder_cover.cpp
+ library_comic_match.h
+ library_comic_match.cpp
+ folder_placeholder_match.h
+ folder_placeholder_match.cpp
+ tracked_issues.h
+ tracked_issues.cpp
ip_config_helper.h
ip_config_helper.cpp
libraries_update_coordinator.h
@@ -54,6 +64,16 @@ add_library(db_helper STATIC
db/data_base_management.cpp
db/reading_list.h
db/reading_list.cpp
+ db/list_entry.h
+ db/list_entry.cpp
+ db/placeholder.h
+ db/placeholder.cpp
+ db/comic_item.h
+ db/comic_item.cpp
+ db/smart_list.h
+ db/smart_list.cpp
+ db/list_folder.h
+ db/list_folder.cpp
db/query_lexer.h
db/query_lexer.cpp
db/search_field_registry.h
@@ -98,6 +118,7 @@ qt_add_executable(YACReaderLibrary WIN32
folder_management_coordinator.cpp
library_database_maintenance_coordinator.h
library_database_maintenance_coordinator.cpp
+ library_maintenance_dialog.h
library_repair_coordinator.h
library_repair_coordinator.cpp
library_management_coordinator.h
@@ -115,6 +136,20 @@ qt_add_executable(YACReaderLibrary WIN32
options_dialog.cpp
search_syntax_dialog.h
search_syntax_dialog.cpp
+ search_filters_menu.h
+ search_filters_menu.cpp
+ smart_list_dialog.h
+ smart_list_dialog.cpp
+ new_list_folder_dialog.h
+ new_list_folder_dialog.cpp
+ move_to_folder_dialog.h
+ move_to_folder_dialog.cpp
+ reading_list_page.h
+ reading_list_page.cpp
+ delayed_progress_dialog.h
+ delayed_progress_dialog.cpp
+ empty_list_folder_widget.h
+ empty_list_folder_widget.cpp
export_library_dialog.h
export_library_dialog.cpp
import_library_dialog.h
@@ -135,10 +170,10 @@ qt_add_executable(YACReaderLibrary WIN32
trayicon_controller.cpp
yacreader_content_views_manager.h
yacreader_content_views_manager.cpp
- yacreader_main_toolbar.h
- yacreader_main_toolbar.cpp
comics_view.h
comics_view.cpp
+ comic_info_object.h
+ comic_info_object.cpp
content_view_state.h
comics_view_transition.h
comics_view_transition.cpp
@@ -164,8 +199,7 @@ qt_add_executable(YACReaderLibrary WIN32
db/folder_model.cpp
db/comic_model.h
db/comic_model.cpp
- db/comic_item.h
- db/comic_item.cpp
+ db/comic_record.h
db/reading_list_model.h
db/reading_list_model.cpp
db/reading_list_item.h
@@ -215,6 +249,28 @@ if(APPLE)
)
endif()
+target_sources(YACReaderLibrary PRIVATE
+ library_header/header_model.h
+ library_header/header_model.cpp
+ library_header/library_header.h
+ library_header/library_header.cpp
+ library_header/view_selector.h
+ library_header/view_selector.cpp
+ library_header/window_frame.h
+ library_header/window_frame.cpp
+)
+if(WIN32)
+ target_sources(YACReaderLibrary PRIVATE
+ library_header/caption_buttons.h
+ library_header/caption_buttons.cpp
+ library_header/window_frame_windows.cpp
+ )
+endif()
+if(APPLE)
+ target_sources(YACReaderLibrary PRIVATE library_header/window_frame_macos.mm)
+ set_source_files_properties(library_header/window_frame_macos.mm PROPERTIES COMPILE_OPTIONS "-fobjc-arc")
+endif()
+
target_include_directories(YACReaderLibrary PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/db
@@ -306,6 +362,10 @@ set(yacreaderlibrary_image_files
${PROJECT_SOURCE_DIR}/images/noLibrariesIcon.svg
${PROJECT_SOURCE_DIR}/images/noLibrariesLine.svg
${PROJECT_SOURCE_DIR}/images/notCover.png
+ ${PROJECT_SOURCE_DIR}/images/unavailableCover.png
+ ${PROJECT_SOURCE_DIR}/images/unavailableCover.svg
+ ${PROJECT_SOURCE_DIR}/images/comics_view_table/deletedComic.svg
+ ${PROJECT_SOURCE_DIR}/images/comics_view_table/placeholderComic.svg
${PROJECT_SOURCE_DIR}/images/library_dialogs/openLibrary.svg
${PROJECT_SOURCE_DIR}/images/metadata_dialog/previousCoverPage.svg
${PROJECT_SOURCE_DIR}/images/readingRibbon.svg
@@ -346,6 +406,8 @@ set(yacreaderlibrary_image_files
${PROJECT_SOURCE_DIR}/images/lists/default_2.svg
${PROJECT_SOURCE_DIR}/images/lists/label_template.svg
${PROJECT_SOURCE_DIR}/images/lists/list.svg
+ ${PROJECT_SOURCE_DIR}/images/lists/smart_list.svg
+ ${PROJECT_SOURCE_DIR}/images/empty_container/smart_list_card.svg
${PROJECT_SOURCE_DIR}/images/clearSearchNew.svg
${PROJECT_SOURCE_DIR}/images/iconSearchNew.svg
)
@@ -371,6 +433,7 @@ set(yacreaderlibrary_qml_files
${CMAKE_CURRENT_SOURCE_DIR}/qml/EmptyInfoView.qml
${CMAKE_CURRENT_SOURCE_DIR}/qml/FolderCover.qml
${CMAKE_CURRENT_SOURCE_DIR}/qml/FolderGridDelegate.qml
+ ${CMAKE_CURRENT_SOURCE_DIR}/qml/ListGridDelegate.qml
${CMAKE_CURRENT_SOURCE_DIR}/qml/FolderInfoView.qml
${CMAKE_CURRENT_SOURCE_DIR}/qml/SelectedComicsInfoView.qml
${CMAKE_CURRENT_SOURCE_DIR}/qml/LibraryInfoView.qml
@@ -401,6 +464,7 @@ set(yacreaderlibrary_qml_translation_files
${CMAKE_CURRENT_SOURCE_DIR}/qml/EmptyInfoView.qml
${CMAKE_CURRENT_SOURCE_DIR}/qml/FolderCover.qml
${CMAKE_CURRENT_SOURCE_DIR}/qml/FolderGridDelegate.qml
+ ${CMAKE_CURRENT_SOURCE_DIR}/qml/ListGridDelegate.qml
${CMAKE_CURRENT_SOURCE_DIR}/qml/FolderInfoView.qml
${CMAKE_CURRENT_SOURCE_DIR}/qml/SelectedComicsInfoView.qml
${CMAKE_CURRENT_SOURCE_DIR}/qml/LibraryInfoView.qml
@@ -503,22 +567,29 @@ if(APPLE)
endif()
# Translations
+set(yacreaderlibrary_translation_source_targets
+ YACReaderLibrary
+ library_common
+ db_helper
+ comic_backend
+ common_gui
+ custom_widgets_library
+ shortcuts_library
+ comic_vine
+ organize_files
+ cbl
+ cbl_gui
+)
qt_add_translations(YACReaderLibrary
- SOURCE_TARGETS
- YACReaderLibrary
- db_helper
- comic_backend
- common_gui
- custom_widgets_library
- shortcuts_library
- comic_vine
- organize_files
+ SOURCE_TARGETS ${yacreaderlibrary_translation_source_targets}
# Keep extraction scoped to targets used by this app and add the QML files
# directly so qsTr() strings in QML are collected too.
+ LUPDATE_OPTIONS -no-obsolete
TS_FILES
yacreaderlibrary_es.ts
yacreaderlibrary_ru.ts
yacreaderlibrary_pt.ts
+ yacreaderlibrary_pt_PT.ts
yacreaderlibrary_fr.ts
yacreaderlibrary_nl.ts
yacreaderlibrary_tr.ts
@@ -528,13 +599,20 @@ qt_add_translations(YACReaderLibrary
yacreaderlibrary_zh_HK.ts
yacreaderlibrary_it.ts
yacreaderlibrary_ko.ts
- yacreaderlibrary_source.ts
yacreaderlibrary_en.ts
SOURCES
${yacreaderlibrary_qml_translation_files}
)
+qt_add_lupdate(
+ SOURCE_TARGETS ${yacreaderlibrary_translation_source_targets}
+ OPTIONS -no-obsolete
+ TS_FILES yacreaderlibrary_source.ts
+ SOURCES ${yacreaderlibrary_qml_translation_files}
+ LUPDATE_TARGET YACReaderLibrary_source_lupdate
+)
target_link_libraries(YACReaderLibrary PRIVATE
+ Qt6::Concurrent
Qt6::Core
Qt6::Gui
Qt6::GuiPrivate
@@ -556,6 +634,7 @@ target_link_libraries(YACReaderLibrary PRIVATE
server
comic_vine
organize_files
+ cbl_gui
cbx_backend
concurrent_queue
worker
@@ -578,9 +657,9 @@ endif()
if(WIN32)
target_sources(YACReaderLibrary PRIVATE icon.rc)
if(MSVC)
- target_link_options(YACReaderLibrary PRIVATE "/MANIFESTINPUT:${PROJECT_SOURCE_DIR}/cmake/windows/yacreader.manifest")
+ target_sources(YACReaderLibrary PRIVATE ${PROJECT_SOURCE_DIR}/cmake/windows/yacreader.manifest)
endif()
- target_link_libraries(YACReaderLibrary PRIVATE oleaut32 ole32 shell32 user32)
+ target_link_libraries(YACReaderLibrary PRIVATE oleaut32 ole32 shell32 user32 dwmapi)
endif()
if(APPLE)
diff --git a/YACReaderLibrary/add_label_dialog.cpp b/YACReaderLibrary/add_label_dialog.cpp
index b8399a847..acb86d27e 100644
--- a/YACReaderLibrary/add_label_dialog.cpp
+++ b/YACReaderLibrary/add_label_dialog.cpp
@@ -5,18 +5,18 @@
namespace {
const char *labelColorNames[] = {
- QT_TR_NOOP("red"),
- QT_TR_NOOP("orange"),
- QT_TR_NOOP("yellow"),
- QT_TR_NOOP("green"),
- QT_TR_NOOP("cyan"),
- QT_TR_NOOP("blue"),
- QT_TR_NOOP("violet"),
- QT_TR_NOOP("purple"),
- QT_TR_NOOP("pink"),
- QT_TR_NOOP("white"),
- QT_TR_NOOP("light"),
- QT_TR_NOOP("dark")
+ QT_TRANSLATE_NOOP("AddLabelDialog", "red"),
+ QT_TRANSLATE_NOOP("AddLabelDialog", "orange"),
+ QT_TRANSLATE_NOOP("AddLabelDialog", "yellow"),
+ QT_TRANSLATE_NOOP("AddLabelDialog", "green"),
+ QT_TRANSLATE_NOOP("AddLabelDialog", "cyan"),
+ QT_TRANSLATE_NOOP("AddLabelDialog", "blue"),
+ QT_TRANSLATE_NOOP("AddLabelDialog", "violet"),
+ QT_TRANSLATE_NOOP("AddLabelDialog", "purple"),
+ QT_TRANSLATE_NOOP("AddLabelDialog", "pink"),
+ QT_TRANSLATE_NOOP("AddLabelDialog", "white"),
+ QT_TRANSLATE_NOOP("AddLabelDialog", "light"),
+ QT_TRANSLATE_NOOP("AddLabelDialog", "dark")
};
}
diff --git a/YACReaderLibrary/add_library_dialog.cpp b/YACReaderLibrary/add_library_dialog.cpp
index 557cf5287..f63101ff7 100644
--- a/YACReaderLibrary/add_library_dialog.cpp
+++ b/YACReaderLibrary/add_library_dialog.cpp
@@ -13,12 +13,12 @@ AddLibraryDialog::AddLibraryDialog(QWidget *parent)
void AddLibraryDialog::setupUI()
{
- textLabel = new QLabel(tr("Comics folder : "));
+ textLabel = new QLabel(tr("Comics folder: "));
path = new QLineEdit;
textLabel->setBuddy(path);
connect(path, &QLineEdit::textChanged, this, &AddLibraryDialog::pathSetted);
- nameLabel = new QLabel(tr("Library name : "));
+ nameLabel = new QLabel(tr("Library name: "));
nameEdit = new QLineEdit;
nameLabel->setBuddy(nameEdit);
connect(nameEdit, &QLineEdit::textChanged, this, &AddLibraryDialog::nameSetted);
diff --git a/YACReaderLibrary/cbl/CMakeLists.txt b/YACReaderLibrary/cbl/CMakeLists.txt
new file mode 100644
index 000000000..6e17bb625
--- /dev/null
+++ b/YACReaderLibrary/cbl/CMakeLists.txt
@@ -0,0 +1,55 @@
+# CBL reading lists: reading, matching, and importing; no GUI, so the server can use it
+
+add_library(cbl STATIC
+ cbl_reader.h
+ cbl_reader.cpp
+ cbl_matcher.h
+ cbl_matcher.cpp
+ cbl_importer.h
+ cbl_importer.cpp
+ cbl_list_match.h
+ cbl_list_match.cpp
+ cbl_writer.h
+ cbl_writer.cpp
+)
+target_include_directories(cbl PUBLIC
+ ${CMAKE_CURRENT_SOURCE_DIR}
+)
+yacreader_apply_build_options(cbl)
+target_link_libraries(cbl PUBLIC
+ Qt6::Core
+ Qt6::Sql
+ db_helper
+ library_common
+ QsLog
+)
+
+# The import dialog of YACReaderLibrary
+if(NOT BUILD_SERVER_STANDALONE)
+ add_library(cbl_gui STATIC
+ cbl_import_model.h
+ cbl_import_model.cpp
+ cbl_entry_detail.h
+ cbl_entry_detail.cpp
+ cbl_entries_view.h
+ cbl_entries_view.cpp
+ cbl_find_comic_page.h
+ cbl_find_comic_page.cpp
+ cbl_import_dialog.h
+ cbl_import_dialog.cpp
+ cbl_match_comic_dialog.h
+ cbl_match_comic_dialog.cpp
+ )
+ target_include_directories(cbl_gui PUBLIC
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ )
+ yacreader_apply_build_options(cbl_gui)
+ target_link_libraries(cbl_gui PUBLIC
+ Qt6::Core
+ Qt6::Widgets
+ Qt6::Sql
+ cbl
+ naturalsort
+ QsLog
+ )
+endif()
diff --git a/YACReaderLibrary/cbl/cbl_entries_view.cpp b/YACReaderLibrary/cbl/cbl_entries_view.cpp
new file mode 100644
index 000000000..e89656a2e
--- /dev/null
+++ b/YACReaderLibrary/cbl/cbl_entries_view.cpp
@@ -0,0 +1,145 @@
+#include "cbl_entries_view.h"
+
+#include "cbl_import_model.h"
+
+#include
+#include
+#include
+#include
+
+#include
+
+namespace {
+
+class CblEntriesDelegate : public QStyledItemDelegate
+{
+public:
+ explicit CblEntriesDelegate(CblEntriesView *view)
+ : QStyledItemDelegate(view), view(view)
+ {
+ }
+
+ void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
+ {
+ if (CblImportModel::isDetail(index))
+ return;
+
+ QStyledItemDelegate::paint(painter, option, index);
+ if (index.column() != CblImportModel::ActionColumn || !view->isHovered(index))
+ return;
+
+ QStyleOptionButton button;
+ button.initFrom(view);
+ button.rect = view->findButtonRect(option.rect);
+ button.text = view->findButtonText();
+ button.state = QStyle::State_Enabled | QStyle::State_Raised;
+ view->style()->drawControl(QStyle::CE_PushButton, &button, painter, view);
+ }
+
+ QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override
+ {
+ if (CblImportModel::isDetail(index)) {
+ const auto *widget = view->indexWidget(index.siblingAtColumn(0));
+ return widget != nullptr ? QSize(0, widget->sizeHint().height()) : QSize(0, 0);
+ }
+
+ auto size = QStyledItemDelegate::sizeHint(option, index);
+ // every row is as tall as the button, so the button fits on any row it shows on
+ if (index.column() == CblImportModel::ActionColumn) {
+ const auto button = view->findButtonRect(QRect(0, 0, 1000, 1000));
+ size.setWidth(button.width() + 8);
+ size.setHeight(std::max(size.height(), button.height() + 2));
+ }
+ return size;
+ }
+
+private:
+ CblEntriesView *view;
+};
+
+}
+
+CblEntriesView::CblEntriesView(QWidget *parent)
+ : QTreeView(parent)
+{
+ setMouseTracking(true);
+ setUniformRowHeights(false);
+ setItemDelegate(new CblEntriesDelegate(this));
+}
+
+bool CblEntriesView::isHovered(const QModelIndex &index) const
+{
+ return hovered.isValid() && !CblImportModel::isDetail(index) && index.row() == hovered.row() && index.parent() == hovered.parent();
+}
+
+QString CblEntriesView::findButtonText() const
+{
+ return tr("Find comic ›");
+}
+
+QRect CblEntriesView::findButtonRect(const QRect &cell) const
+{
+ QStyleOptionButton button;
+ button.initFrom(this);
+ button.text = findButtonText();
+ const auto textSize = fontMetrics().size(Qt::TextShowMnemonic, button.text);
+ auto size = style()->sizeFromContents(QStyle::CT_PushButton, &button, textSize, this);
+ size.setHeight(std::min(size.height(), cell.height() - 2));
+ QRect rect(QPoint(0, 0), size);
+ rect.moveCenter(cell.center());
+ rect.moveRight(cell.right() - 4);
+ return rect;
+}
+
+void CblEntriesView::drawBranches(QPainter *painter, const QRect &rect, const QModelIndex &index) const
+{
+ // the indentation of a detail would show the selection of its entry
+ if (CblImportModel::isDetail(index)) {
+ painter->fillRect(rect, palette().base());
+ return;
+ }
+ if (isExpanded(index) || isHovered(index))
+ QTreeView::drawBranches(painter, rect, index);
+}
+
+void CblEntriesView::mouseMoveEvent(QMouseEvent *event)
+{
+ const auto index = indexAt(event->position().toPoint());
+ setHovered(CblImportModel::isDetail(index) ? QModelIndex() : index);
+ QTreeView::mouseMoveEvent(event);
+}
+
+void CblEntriesView::mousePressEvent(QMouseEvent *event)
+{
+ const auto position = event->position().toPoint();
+ const auto index = indexAt(position);
+ if (event->button() == Qt::LeftButton && index.isValid() && !CblImportModel::isDetail(index) && index.column() == CblImportModel::ActionColumn &&
+ findButtonRect(visualRect(index)).contains(position)) {
+ emit findComicRequested(index);
+ event->accept();
+ return;
+ }
+ QTreeView::mousePressEvent(event);
+}
+
+void CblEntriesView::leaveEvent(QEvent *event)
+{
+ setHovered(QModelIndex());
+ QTreeView::leaveEvent(event);
+}
+
+void CblEntriesView::setHovered(const QModelIndex &index)
+{
+ if (index.isValid() == hovered.isValid() && (!index.isValid() || (index.row() == hovered.row() && index.parent() == hovered.parent())))
+ return;
+
+ const auto previous = hovered;
+ hovered = index.isValid() ? QPersistentModelIndex(index.siblingAtColumn(0)) : QPersistentModelIndex();
+ // the branch area is outside the cells, so the whole rows are repainted
+ for (const auto &row : { QModelIndex(previous), QModelIndex(hovered) }) {
+ if (row.isValid()) {
+ const auto rect = visualRect(row);
+ viewport()->update(QRect(0, rect.top(), viewport()->width(), rect.height()));
+ }
+ }
+}
diff --git a/YACReaderLibrary/cbl/cbl_entries_view.h b/YACReaderLibrary/cbl/cbl_entries_view.h
new file mode 100644
index 000000000..d598720f9
--- /dev/null
+++ b/YACReaderLibrary/cbl/cbl_entries_view.h
@@ -0,0 +1,35 @@
+#ifndef CBL_ENTRIES_VIEW_H
+#define CBL_ENTRIES_VIEW_H
+
+#include
+#include
+
+// the entries of a CBL import: the expand arrow and the "Find comic" button show on the row under the mouse; a detail
+// row shows its index widget across all columns
+class CblEntriesView : public QTreeView
+{
+ Q_OBJECT
+
+public:
+ explicit CblEntriesView(QWidget *parent = nullptr);
+
+ bool isHovered(const QModelIndex &index) const;
+ QRect findButtonRect(const QRect &cell) const;
+ QString findButtonText() const;
+
+signals:
+ void findComicRequested(const QModelIndex &index);
+
+protected:
+ void drawBranches(QPainter *painter, const QRect &rect, const QModelIndex &index) const override;
+ void mouseMoveEvent(QMouseEvent *event) override;
+ void mousePressEvent(QMouseEvent *event) override;
+ void leaveEvent(QEvent *event) override;
+
+private:
+ void setHovered(const QModelIndex &index);
+
+ QPersistentModelIndex hovered;
+};
+
+#endif
diff --git a/YACReaderLibrary/cbl/cbl_entry_detail.cpp b/YACReaderLibrary/cbl/cbl_entry_detail.cpp
new file mode 100644
index 000000000..ef0a90869
--- /dev/null
+++ b/YACReaderLibrary/cbl/cbl_entry_detail.cpp
@@ -0,0 +1,362 @@
+#include "cbl_entry_detail.h"
+
+#include "qnaturalsorting.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+namespace {
+
+enum Field {
+ SeriesField,
+ NumberField,
+ VolumeField,
+ YearField,
+ FormatField,
+ ComicVineField,
+ FieldCount,
+};
+
+using Values = std::array;
+using Differences = std::array;
+
+QString fieldName(int field)
+{
+ switch (field) {
+ case SeriesField:
+ return CblEntryDetail::tr("Series");
+ case NumberField:
+ return CblEntryDetail::tr("Number");
+ case VolumeField:
+ return CblEntryDetail::tr("Volume");
+ case YearField:
+ return CblEntryDetail::tr("Year");
+ case FormatField:
+ return CblEntryDetail::tr("Format");
+ case ComicVineField:
+ return CblEntryDetail::tr("ComicVine ID");
+ }
+ return { };
+}
+
+Values bookValues(const CblBook &book)
+{
+ const auto year = cblBookYear(book);
+ return { book.series, book.number, book.volume, year > 0 ? QString::number(year) : QString(), book.format.isEmpty() ? book.issueType : book.format, cblComicVineIssueId(book) };
+}
+
+Values metadataValues(const CblLibraryMetadata &metadata)
+{
+ const auto year = cblYearOfDate(metadata.date);
+ return { metadata.series, metadata.number, metadata.volume, year > 0 ? QString::number(year) : QString(), metadata.format, metadata.comicVineId };
+}
+
+bool differentText(const QString &a, const QString &b)
+{
+ return !a.trimmed().isEmpty() && !b.trimmed().isEmpty() && a.simplified().compare(b.simplified(), Qt::CaseInsensitive) != 0;
+}
+
+// a value that is not known on one side is not different
+Differences differences(const CblBook &book, const CblLibraryMetadata &metadata)
+{
+ const auto bookYear = cblBookYear(book);
+ const auto year = cblYearOfDate(metadata.date);
+ return { differentText(book.series, metadata.series),
+ !book.number.trimmed().isEmpty() && !metadata.number.trimmed().isEmpty() && !cblSameNumber(book.number, metadata.number),
+ differentText(book.volume, cblEffectiveVolume(metadata)),
+ bookYear > 0 && year > 0 && bookYear != year,
+ differentText(book.format.isEmpty() ? book.issueType : book.format, metadata.format),
+ differentText(cblComicVineIssueId(book), metadata.comicVineId) };
+}
+
+QString metadataTitle(const CblLibraryMetadata &metadata)
+{
+ return metadata.number.isEmpty() ? metadata.series : metadata.series + QStringLiteral(" #") + metadata.number;
+}
+
+QString coverPath(const QString &libraryDataPath, const CblCandidate &candidate)
+{
+ if (const auto *comic = std::get_if(&candidate))
+ return comic->hash.isEmpty() ? QString() : QDir(libraryDataPath).filePath(QStringLiteral("covers/") + comic->hash + QStringLiteral(".jpg"));
+ const auto &placeholder = std::get(candidate);
+ return placeholder.coverKey.isEmpty() ? QString() : QDir(libraryDataPath).filePath(QStringLiteral("covers/placeholders/") + placeholder.coverKey + QStringLiteral(".jpg"));
+}
+
+bool hasFile(const CblCandidate &candidate)
+{
+ const auto *comic = std::get_if(&candidate);
+ return comic != nullptr && comic->hasFile;
+}
+
+const CblLibraryMetadata &metadataOf(const CblCandidate &candidate)
+{
+ return std::visit([](const auto &item) -> const CblLibraryMetadata & { return item; }, candidate);
+}
+
+// the distinct non-empty values, in natural order
+QStringList distinctValues(QStringList values)
+{
+ values.removeAll(QString());
+ values.removeDuplicates();
+ std::sort(values.begin(), values.end(), naturalSortLessThanCI);
+ return values;
+}
+
+// one column next to the book: a comic, a possible comic, or the closest series
+struct Subject {
+ QString title;
+ QString subtitle;
+ std::optional candidate;
+ bool showsCover = false;
+ QString coverPath;
+ Values values;
+ Differences different { };
+};
+
+QLabel *mutedLabel(const QString &text, QWidget *parent)
+{
+ auto label = new QLabel(text, parent);
+ label->setForegroundRole(QPalette::PlaceholderText);
+ return label;
+}
+
+QLabel *boldLabel(const QString &text, QWidget *parent)
+{
+ auto label = new QLabel(text, parent);
+ auto font = label->font();
+ font.setBold(true);
+ label->setFont(font);
+ return label;
+}
+
+}
+
+QString cblMatchLevelText(CblMatchLevel level)
+{
+ switch (level) {
+ case CblMatchLevel::ComicVineId:
+ return CblEntryDetail::tr("ComicVine ID");
+ case CblMatchLevel::SameSeries:
+ return CblEntryDetail::tr("series and number");
+ case CblMatchLevel::SeriesWithoutVolume:
+ return CblEntryDetail::tr("series without the volume, and number");
+ case CblMatchLevel::LettersAndDigits:
+ return CblEntryDetail::tr("letters and digits of the series, and number");
+ case CblMatchLevel::SeriesWithYear:
+ return CblEntryDetail::tr("series with its year, and number");
+ }
+ return { };
+}
+
+QColor cblWarningColor(const QPalette &palette)
+{
+ const bool dark = palette.color(QPalette::Base).lightness() < 128;
+ return dark ? QColor(0xE0, 0xA0, 0x30) : QColor(0xB2, 0x6B, 0x00);
+}
+
+QString cblBookDetails(const CblBook &book)
+{
+ QStringList parts;
+ if (!book.volume.isEmpty())
+ parts.append(CblEntryDetail::tr("volume %1").arg(book.volume));
+ if (const auto year = cblBookYear(book); year > 0)
+ parts.append(QString::number(year));
+ return parts.join(QStringLiteral(", "));
+}
+
+CblEntryDetail::CblEntryDetail(const CblImportEntry &entry, int row, int entryCount, const QList &closestSeries, const QString &libraryDataPath, const QColor &warningColor, QWidget *parent)
+ : QWidget(parent)
+{
+ setAutoFillBackground(true);
+ setBackgroundRole(QPalette::Base);
+
+ QList subjects;
+ QString footer;
+
+ const auto subjectOf = [&](const CblCandidate &candidate, const QString &title) {
+ Subject subject;
+ subject.title = title;
+ if (const auto *comic = std::get_if(&candidate); comic != nullptr && comic->hasFile)
+ subject.subtitle = CblImportModel::fileName(*comic);
+ else
+ subject.subtitle = metadataTitle(metadataOf(candidate));
+ subject.candidate = candidate;
+ subject.showsCover = true;
+ subject.coverPath = coverPath(libraryDataPath, candidate);
+ subject.values = metadataValues(metadataOf(candidate));
+ subject.different = differences(entry.book, metadataOf(candidate));
+ return subject;
+ };
+
+ if (entry.choice) {
+ subjects.append(subjectOf(*entry.choice, hasFile(*entry.choice) ? tr("Comic in your library") : tr("Missing in your library")));
+ subjects.last().candidate.reset();
+ if (entry.changed)
+ footer = tr("Changed by you.");
+ else if (entry.current == entry.choice)
+ footer = tr("The entry has this comic.");
+ else if (hasFile(*entry.choice))
+ footer = tr("Matched by %1.").arg(cblMatchLevelText(entry.match.level.value_or(CblMatchLevel::SameSeries)));
+ else
+ footer = tr("Your library already has this comic as missing. Matched by %1.").arg(cblMatchLevelText(entry.match.level.value_or(CblMatchLevel::SameSeries)));
+ } else if (!entry.match.candidates.isEmpty()) {
+ for (int i = 0; i < entry.match.candidates.size(); ++i)
+ subjects.append(subjectOf(entry.match.candidates.at(i), tr("Possible comic %1").arg(i + 1)));
+ if (entry.changed)
+ footer = tr("Changed by you: it is imported as missing.");
+ else if (entry.match.level == CblMatchLevel::ComicVineId)
+ footer = tr("The comics have the same ComicVine ID.");
+ else if (entry.match.candidates.size() == 1)
+ footer = tr("The comic has the same series and number, but its volume or year is different.");
+ else
+ footer = tr("The comics have the same series and number.");
+ footer += QLatin1Char(' ') + tr("Values that are different from the file are in amber.");
+ } else {
+ if (!closestSeries.isEmpty()) {
+ const auto &first = closestSeries.first();
+ Subject subject;
+ subject.title = tr("Closest series in your library");
+ subject.showsCover = true;
+ subject.coverPath = coverPath(libraryDataPath, first);
+ const auto folder = first.path.section('/', 0, -2).mid(1);
+ subject.subtitle = folder.isEmpty() ? tr("Library") : folder;
+
+ QStringList numbers;
+ QStringList volumes;
+ QStringList years;
+ QStringList formats;
+ for (const auto &comic : closestSeries) {
+ numbers.append(comic.number);
+ volumes.append(cblEffectiveVolume(comic));
+ const auto year = cblYearOfDate(comic.date);
+ years.append(year > 0 ? QString::number(year) : QString());
+ formats.append(comic.format);
+ }
+ numbers = distinctValues(numbers);
+ volumes = distinctValues(volumes);
+ years = distinctValues(years);
+ formats = distinctValues(formats);
+
+ constexpr int shownNumbers = 12;
+ auto numbersText = numbers.mid(0, shownNumbers).join(QStringLiteral(", "));
+ if (numbers.size() > shownNumbers)
+ numbersText += QStringLiteral(", …");
+ const auto contains = [](const QStringList &values, const QString &value, const std::function &same) {
+ return value.trimmed().isEmpty() || values.isEmpty() || std::any_of(values.cbegin(), values.cend(), [&](const QString &other) { return same(value, other); });
+ };
+ const auto sameText = [](const QString &a, const QString &b) { return !differentText(a, b); };
+ const auto bookYear = cblBookYear(entry.book);
+
+ subject.values = { first.series, numbers.isEmpty() ? QString() : tr("Has %1").arg(numbersText), volumes.join(QStringLiteral(", ")), years.join(QStringLiteral(", ")), formats.join(QStringLiteral(", ")), QString() };
+ subject.different = { differentText(entry.book.series, first.series),
+ !contains(numbers, entry.book.number, cblSameNumber),
+ !contains(volumes, entry.book.volume, sameText),
+ !contains(years, bookYear > 0 ? QString::number(bookYear) : QString(), sameText),
+ !contains(formats, entry.book.format, sameText),
+ false };
+ subjects.append(subject);
+ }
+
+ QStringList tried;
+ if (!cblComicVineIssueId(entry.book).isEmpty())
+ tried.append(cblMatchLevelText(CblMatchLevel::ComicVineId));
+ if (!entry.book.series.trimmed().isEmpty()) {
+ for (const auto level : { CblMatchLevel::SameSeries, CblMatchLevel::SeriesWithoutVolume, CblMatchLevel::LettersAndDigits, CblMatchLevel::SeriesWithYear })
+ tried.append(cblMatchLevelText(level));
+ }
+ footer = entry.changed ? tr("Changed by you: it is imported as missing.") : tr("No comic found. Tried: %1.").arg(tried.join(QStringLiteral("; ")));
+ }
+
+ const auto warningStyle = QStringLiteral("color: %1;").arg(warningColor.name());
+
+ grid = new QGridLayout;
+ grid->setHorizontalSpacing(24);
+ grid->setVerticalSpacing(4);
+
+ auto bookHeader = new QVBoxLayout;
+ bookHeader->setSpacing(0);
+ bookHeader->addStretch();
+ bookHeader->addWidget(boldLabel(tr("In the file"), this));
+ bookHeader->addWidget(mutedLabel(tr("Entry %1 of %2").arg(row + 1).arg(entryCount), this));
+ grid->addLayout(bookHeader, 0, 1, Qt::AlignBottom);
+
+ for (int i = 0; i < subjects.size(); ++i) {
+ const auto &subject = subjects.at(i);
+ auto header = new QHBoxLayout;
+ header->setSpacing(8);
+ if (subject.showsCover) {
+ auto cover = new QLabel(this);
+ cover->setFixedSize(36, 54);
+ cover->setAlignment(Qt::AlignCenter);
+ QPixmap pixmap;
+ if (!subject.coverPath.isEmpty() && pixmap.load(subject.coverPath))
+ cover->setPixmap(pixmap.scaled(cover->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
+ else
+ cover->setStyleSheet(QStringLiteral("background-color: palette(mid);"));
+ header->addWidget(cover);
+ }
+
+ auto texts = new QVBoxLayout;
+ texts->setSpacing(0);
+ texts->addWidget(boldLabel(subject.title, this));
+ texts->addWidget(mutedLabel(subject.subtitle, this));
+ if (subject.candidate) {
+ const auto candidate = *subject.candidate;
+ auto button = new QPushButton(hasFile(candidate) ? tr("Use this comic") : tr("Use this missing comic"), this);
+ button->setAutoDefault(false);
+ connect(button, &QPushButton::clicked, this, [this, candidate] { emit candidateChosen(candidate); });
+ texts->addWidget(button, 0, Qt::AlignLeft);
+ }
+ header->addLayout(texts);
+ header->addStretch();
+ grid->addLayout(header, 0, i + 2, Qt::AlignBottom);
+ }
+
+ const auto book = bookValues(entry.book);
+ for (int field = 0; field < FieldCount; ++field) {
+ const auto anyDifferent = std::any_of(subjects.cbegin(), subjects.cend(), [field](const Subject &subject) { return subject.different[field]; });
+ auto name = new QLabel(anyDifferent ? fieldName(field) + QStringLiteral(" ≠") : fieldName(field), this);
+ if (anyDifferent)
+ name->setStyleSheet(warningStyle);
+ else
+ name->setForegroundRole(QPalette::PlaceholderText);
+ labelsWidth = std::max(labelsWidth, name->sizeHint().width());
+ grid->addWidget(name, field + 1, 0);
+ grid->addWidget(new QLabel(book[field].isEmpty() ? QStringLiteral("—") : book[field], this), field + 1, 1);
+
+ for (int i = 0; i < subjects.size(); ++i) {
+ const auto &value = subjects.at(i).values[field];
+ const auto text = value.isEmpty() ? QStringLiteral("—") : value;
+ auto label = subjects.at(i).different[field] ? new QLabel(text, this) : mutedLabel(text, this);
+ if (subjects.at(i).different[field])
+ label->setStyleSheet(warningStyle);
+ grid->addWidget(label, field + 1, i + 2);
+ }
+ }
+ grid->setColumnStretch(subjects.size() + 2, 1);
+
+ auto footerLabel = mutedLabel(footer, this);
+ footerLabel->setWordWrap(true);
+
+ auto layout = new QVBoxLayout(this);
+ layout->setContentsMargins(12, 8, 12, 10);
+ layout->addLayout(grid);
+ layout->addSpacing(6);
+ layout->addWidget(footerLabel);
+}
+
+void CblEntryDetail::alignLibraryColumns(int x)
+{
+ const auto left = layout()->contentsMargins().left();
+ grid->setColumnMinimumWidth(0, labelsWidth);
+ grid->setColumnMinimumWidth(1, std::max(0, x - left - labelsWidth - 2 * grid->horizontalSpacing()));
+}
diff --git a/YACReaderLibrary/cbl/cbl_entry_detail.h b/YACReaderLibrary/cbl/cbl_entry_detail.h
new file mode 100644
index 000000000..75f0cbea4
--- /dev/null
+++ b/YACReaderLibrary/cbl/cbl_entry_detail.h
@@ -0,0 +1,35 @@
+#ifndef CBL_ENTRY_DETAIL_H
+#define CBL_ENTRY_DETAIL_H
+
+#include "cbl_import_model.h"
+
+#include
+
+class QGridLayout;
+
+// the book of an entry next to what it refers to: its choice, its possible comics, or the closest series
+class CblEntryDetail : public QWidget
+{
+ Q_OBJECT
+
+public:
+ CblEntryDetail(const CblImportEntry &entry, int row, int entryCount, const QList &closestSeries, const QString &libraryDataPath, const QColor &warningColor, QWidget *parent = nullptr);
+
+ // the first library column starts at `x`, so it can sit under a column of the table
+ void alignLibraryColumns(int x);
+
+signals:
+ void candidateChosen(const CblCandidate &candidate);
+
+private:
+ QGridLayout *grid;
+ int labelsWidth = 0;
+};
+
+// what the UI says about matching
+QString cblMatchLevelText(CblMatchLevel level);
+QString cblBookDetails(const CblBook &book);
+// amber, for problems and values that are different
+QColor cblWarningColor(const QPalette &palette);
+
+#endif
diff --git a/YACReaderLibrary/cbl/cbl_find_comic_page.cpp b/YACReaderLibrary/cbl/cbl_find_comic_page.cpp
new file mode 100644
index 000000000..5185f2bff
--- /dev/null
+++ b/YACReaderLibrary/cbl/cbl_find_comic_page.cpp
@@ -0,0 +1,418 @@
+#include "cbl_find_comic_page.h"
+
+#include "QsLog.h"
+#include "cbl_entry_detail.h"
+#include "data_base_management.h"
+#include "db_helper.h"
+#include "qnaturalsorting.h"
+#include "search_query.h"
+#include "yacreader_global.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+namespace {
+
+enum ComicColumn {
+ FileColumn,
+ SeriesColumn,
+ NumberColumn,
+ VolumeColumn,
+ YearColumn,
+};
+
+constexpr int FolderIdRole = Qt::UserRole + 1;
+constexpr int ComicPositionRole = Qt::UserRole + 2;
+
+CblLibraryComic libraryComic(const ComicDB &comic)
+{
+ CblLibraryComic result;
+ result.id = comic.info.id;
+ result.hasFile = true;
+ result.path = comic.file.path;
+ result.hash = comic.info.hash;
+ result.series = comic.info.series.toString();
+ result.number = comic.info.number.toString();
+ result.volume = comic.info.volume.toString();
+ result.date = comic.info.date.toString();
+ result.format = comic.info.format.toString();
+ result.comicVineId = comic.info.comicVineID.toString();
+ return result;
+}
+
+bool different(const QString &a, const QString &b)
+{
+ return !a.trimmed().isEmpty() && !b.trimmed().isEmpty() && a.simplified().compare(b.simplified(), Qt::CaseInsensitive) != 0;
+}
+
+}
+
+CblFindComicPage::CblFindComicPage(const QString &libraryPath, Mode mode, QWidget *parent)
+ : QWidget(parent), libraryPath(libraryPath), mode(mode)
+{
+ auto backButton = new QPushButton(tr("‹ All entries"));
+ connect(backButton, &QPushButton::clicked, this, &CblFindComicPage::backRequested);
+
+ entryLabel = new QLabel;
+ entryLabel->setTextFormat(Qt::RichText);
+ problemLabel = new QLabel;
+
+ auto entryTexts = new QVBoxLayout;
+ entryTexts->setSpacing(0);
+ entryTexts->addWidget(entryLabel);
+ entryTexts->addWidget(problemLabel);
+
+ previousButton = new QPushButton(QStringLiteral("‹"));
+ nextButton = new QPushButton(QStringLiteral("›"));
+ for (auto *button : { previousButton, nextButton })
+ button->setFixedWidth(button->sizeHint().height() * 3 / 2);
+ previousButton->setToolTip(tr("Previous problem"));
+ nextButton->setToolTip(tr("Next problem"));
+ problemCountLabel = new QLabel;
+ connect(previousButton, &QPushButton::clicked, this, &CblFindComicPage::previousProblemRequested);
+ connect(nextButton, &QPushButton::clicked, this, &CblFindComicPage::nextProblemRequested);
+
+ auto header = new QHBoxLayout;
+ header->addWidget(backButton);
+ header->addSpacing(12);
+ header->addLayout(entryTexts, 1);
+ header->addWidget(previousButton);
+ header->addWidget(problemCountLabel);
+ header->addWidget(nextButton);
+
+ foldersModel = new QStandardItemModel(this);
+ foldersModel->setHorizontalHeaderLabels({ tr("Folders") });
+ foldersView = new QTreeView;
+ foldersView->setModel(foldersModel);
+ foldersView->setEditTriggers(QAbstractItemView::NoEditTriggers);
+ connect(foldersView->selectionModel(), &QItemSelectionModel::currentChanged, this, [this](const QModelIndex ¤t) {
+ if (current.isValid()) {
+ searchEdit->clear();
+ showFolder(current.data(FolderIdRole).toULongLong());
+ }
+ });
+
+ breadcrumbLabel = new QLabel;
+ breadcrumbLabel->setTextFormat(Qt::RichText);
+ searchEdit = new QLineEdit;
+ searchEdit->setPlaceholderText(tr("Search the whole library"));
+ searchEdit->setClearButtonEnabled(true);
+ searchEdit->setMinimumWidth(260);
+ searchTimer = new QTimer(this);
+ searchTimer->setSingleShot(true);
+ searchTimer->setInterval(300);
+ connect(searchTimer, &QTimer::timeout, this, &CblFindComicPage::search);
+ connect(searchEdit, &QLineEdit::textEdited, searchTimer, qOverload<>(&QTimer::start));
+ connect(searchEdit, &QLineEdit::textChanged, this, [this](const QString &text) {
+ if (!text.isEmpty())
+ return;
+ if (onlyFolderId)
+ showFolder(*onlyFolderId);
+ else if (foldersView->currentIndex().isValid())
+ showFolder(foldersView->currentIndex().data(FolderIdRole).toULongLong());
+ });
+
+ auto comicsHeader = new QHBoxLayout;
+ comicsHeader->addWidget(breadcrumbLabel, 1);
+ comicsHeader->addWidget(searchEdit);
+
+ comicsModel = new QStandardItemModel(this);
+ comicsModel->setHorizontalHeaderLabels({ tr("File"), tr("Series"), tr("No."), tr("Volume"), tr("Year") });
+ comicsView = new QTreeView;
+ comicsView->setModel(comicsModel);
+ comicsView->setRootIsDecorated(false);
+ comicsView->setUniformRowHeights(true);
+ comicsView->setAlternatingRowColors(true);
+ comicsView->setEditTriggers(QAbstractItemView::NoEditTriggers);
+ comicsView->setSelectionBehavior(QAbstractItemView::SelectRows);
+ comicsView->header()->setSectionResizeMode(FileColumn, QHeaderView::Stretch);
+ comicsView->header()->setSectionResizeMode(SeriesColumn, QHeaderView::Stretch);
+ comicsView->header()->setStretchLastSection(false);
+ connect(comicsView, &QTreeView::doubleClicked, this, &CblFindComicPage::chooseSelectedComic);
+ connect(comicsView->selectionModel(), &QItemSelectionModel::selectionChanged, this, [this] {
+ useButton->setEnabled(comicsView->selectionModel()->hasSelection());
+ });
+
+ auto comicsPane = new QWidget;
+ auto comicsLayout = new QVBoxLayout(comicsPane);
+ comicsLayout->setContentsMargins(0, 0, 0, 0);
+ comicsLayout->addLayout(comicsHeader);
+ comicsLayout->addWidget(comicsView, 1);
+
+ auto splitter = new QSplitter;
+ splitter->addWidget(foldersView);
+ splitter->addWidget(comicsPane);
+ splitter->setStretchFactor(1, 1);
+ splitter->setSizes({ 240, 700 });
+
+ nextProblemCheck = new QCheckBox(tr("Go to the next problem after I choose"));
+ nextProblemCheck->setChecked(true);
+ useButton = new QPushButton(tr("Use this comic"));
+ useButton->setAutoDefault(false);
+ useButton->setEnabled(false);
+ connect(useButton, &QPushButton::clicked, this, &CblFindComicPage::chooseSelectedComic);
+
+ for (auto *button : { backButton, previousButton, nextButton })
+ button->setAutoDefault(false);
+
+ auto bottom = new QHBoxLayout;
+ bottom->addWidget(nextProblemCheck);
+ bottom->addStretch();
+ if (mode == Mode::OneEntry) {
+ for (QWidget *widget : std::initializer_list { backButton, previousButton, problemCountLabel, nextButton, nextProblemCheck })
+ widget->hide();
+ auto cancelButton = new QPushButton(tr("Cancel"));
+ cancelButton->setAutoDefault(false);
+ connect(cancelButton, &QPushButton::clicked, this, &CblFindComicPage::backRequested);
+ bottom->addWidget(cancelButton);
+ }
+ bottom->addWidget(useButton);
+
+ auto layout = new QVBoxLayout(this);
+ layout->setContentsMargins(0, 0, 0, 0);
+ layout->addLayout(header);
+ layout->addWidget(splitter, 1);
+ layout->addLayout(bottom);
+}
+
+std::optional> CblFindComicPage::loadFolders(QSqlDatabase &db)
+{
+ QSqlQuery query(db);
+ query.setForwardOnly(true);
+ if (!query.exec("SELECT id, parentId, name, path FROM folder WHERE id <> 1")) {
+ QLOG_ERROR() << "Unable to load the folders:" << query.lastError().text();
+ return std::nullopt;
+ }
+
+ QList folders;
+ while (query.next())
+ folders.append({ query.value(0).toULongLong(), query.value(1).toULongLong(), query.value(2).toString(), query.value(3).toString() });
+ std::sort(folders.begin(), folders.end(), [](const CblLibraryFolder &a, const CblLibraryFolder &b) { return naturalSortLessThanCI(a.name, b.name); });
+ return folders;
+}
+
+QString CblFindComicPage::startFolderPath(const CblImportEntry &entry, const CblMatcher &matcher)
+{
+ QString comicPath;
+ const auto pathOf = [](const CblCandidate &candidate) {
+ const auto *comic = std::get_if(&candidate);
+ return comic != nullptr ? comic->path : QString();
+ };
+ if (entry.choice)
+ comicPath = pathOf(*entry.choice);
+ for (int i = 0; comicPath.isEmpty() && i < entry.match.candidates.size(); ++i)
+ comicPath = pathOf(entry.match.candidates.at(i));
+ if (comicPath.isEmpty()) {
+ const auto closest = matcher.closestSeries(entry.book);
+ for (int i = 0; comicPath.isEmpty() && i < closest.size(); ++i)
+ comicPath = closest.at(i).path;
+ }
+ return comicPath.section('/', 0, -2);
+}
+
+void CblFindComicPage::setFolders(const QList &folders)
+{
+ foldersModel->removeRows(0, foldersModel->rowCount());
+ folderItems.clear();
+ folderIdsByPath.clear();
+
+ for (const auto &folder : folders) {
+ auto item = new QStandardItem(folder.name);
+ item->setData(folder.id, FolderIdRole);
+ folderItems.insert(folder.id, item);
+ folderIdsByPath.insert(folder.path, folder.id);
+ }
+ // the folders come sorted by name, so the children keep that order
+ for (const auto &folder : folders) {
+ auto *item = folderItems.value(folder.id);
+ if (auto *parentItem = folderItems.value(folder.parentId))
+ parentItem->appendRow(item);
+ else
+ foldersModel->appendRow(item);
+ }
+}
+
+void CblFindComicPage::limitToFolder(qulonglong folderId)
+{
+ onlyFolderId = folderId;
+ foldersView->hide();
+ problemLabel->hide();
+ searchEdit->setPlaceholderText(tr("Search in this folder"));
+}
+
+void CblFindComicPage::setWarningColor(const QColor &color)
+{
+ warningColor = color;
+ problemLabel->setStyleSheet(QStringLiteral("color: %1;").arg(color.name()));
+}
+
+void CblFindComicPage::showEntry(const CblImportEntry &shownEntry, int row, int entryCount, CblImportStatus status, const QString &folderPath, int problem, int problemCount, bool hasPrevious, bool hasNext)
+{
+ entry = shownEntry;
+
+ const auto details = cblBookDetails(entry.book);
+ auto text = QStringLiteral("") + CblImportModel::bookTitle(entry.book).toHtmlEscaped() + QStringLiteral("");
+ if (mode == Mode::ImportEntries)
+ text = tr("Entry %1 of %2").arg(row + 1).arg(entryCount) + QStringLiteral(" · ") + text;
+ if (!details.isEmpty())
+ text += QStringLiteral(" (") + details.toHtmlEscaped() + QStringLiteral(")");
+ entryLabel->setText(text);
+
+ if (mode == Mode::OneEntry)
+ status = CblImportStatus::Missing;
+ switch (status) {
+ case CblImportStatus::Missing:
+ problemLabel->setText(tr("Missing: no comic found. Browse or search the library."));
+ break;
+ case CblImportStatus::Ambiguous:
+ problemLabel->setText(tr("Ambiguous: %n possible comic(s). Browse or search the library.", "", entry.match.candidates.size()));
+ break;
+ case CblImportStatus::Matched:
+ case CblImportStatus::Changed:
+ problemLabel->setText(tr("Browse or search the library to choose another comic."));
+ break;
+ }
+
+ problemCountLabel->setText(problem >= 0 ? tr("Problem %1 of %2").arg(problem + 1).arg(problemCount) : tr("%n problem(s)", "", problemCount));
+ previousButton->setEnabled(hasPrevious);
+ nextButton->setEnabled(hasNext);
+
+ searchEdit->clear();
+ if (onlyFolderId) {
+ showFolder(*onlyFolderId);
+ return;
+ }
+ const auto folderId = folderIdsByPath.value(folderPath);
+ if (auto *item = folderItems.value(folderId)) {
+ const auto index = item->index();
+ foldersView->scrollTo(index);
+ if (foldersView->currentIndex() == index)
+ showFolder(folderId);
+ else
+ foldersView->setCurrentIndex(index);
+ } else {
+ foldersView->setCurrentIndex(QModelIndex());
+ breadcrumbLabel->setText(tr("Library"));
+ showComics({ });
+ }
+}
+
+void CblFindComicPage::showFolder(qulonglong folderId)
+{
+ breadcrumbLabel->setText(breadcrumb(folderItems.value(folderId)));
+
+ QList folderComics;
+ QString connectionName;
+ {
+ auto db = DataBaseManagement::loadDatabase(YACReader::LibraryPaths::libraryDataPath(libraryPath));
+ connectionName = db.connectionName();
+ if (db.isOpen())
+ folderComics = DBHelper::getComicsFromParent(folderId, db);
+ }
+ if (!connectionName.isEmpty())
+ QSqlDatabase::removeDatabase(connectionName);
+ showComics(folderComics);
+}
+
+void CblFindComicPage::search()
+{
+ const auto text = searchEdit->text().trimmed();
+ if (text.isEmpty())
+ return;
+
+ QList found;
+ bool parsed = true;
+ QString connectionName;
+ {
+ auto db = DataBaseManagement::loadDatabase(YACReader::LibraryPaths::libraryDataPath(libraryPath));
+ connectionName = db.connectionName();
+ if (db.isOpen()) {
+ try {
+ auto query = onlyFolderId ? comicsSearchQuery(db, text, *onlyFolderId) : comicsSearchQuery(db, text);
+ const auto indexes = ComicDB::columnIndexes(query.record(), "c", "ci");
+ while (query.next())
+ found.append(ComicDB::fromQuery(query, indexes));
+ } catch (const std::exception &) {
+ parsed = false;
+ }
+ }
+ }
+ if (!connectionName.isEmpty())
+ QSqlDatabase::removeDatabase(connectionName);
+ // an incomplete search text cannot be parsed; the previous results stay
+ if (!parsed)
+ return;
+
+ breadcrumbLabel->setText(tr("Search results"));
+ showComics(found);
+}
+
+void CblFindComicPage::showComics(const QList &shownComics)
+{
+ comics = shownComics;
+ comicsModel->removeRows(0, comicsModel->rowCount());
+ useButton->setEnabled(false);
+
+ const auto bookYear = cblBookYear(entry.book);
+ for (int i = 0; i < comics.size(); ++i) {
+ const auto comic = libraryComic(comics.at(i));
+ const auto year = cblYearOfDate(comic.date);
+ const QList> values = {
+ { comics.at(i).file.fileName, false },
+ { comic.series, different(entry.book.series, comic.series) },
+ { comic.number, !entry.book.number.trimmed().isEmpty() && !comic.number.trimmed().isEmpty() && !cblSameNumber(entry.book.number, comic.number) },
+ { comic.volume, different(entry.book.volume, cblEffectiveVolume(comic)) },
+ { year > 0 ? QString::number(year) : QString(), bookYear > 0 && year > 0 && bookYear != year },
+ };
+
+ QList items;
+ for (const auto &[value, isDifferent] : values) {
+ auto item = new QStandardItem(value);
+ item->setData(i, ComicPositionRole);
+ if (isDifferent && warningColor.isValid())
+ item->setForeground(warningColor);
+ items.append(item);
+ }
+ comicsModel->appendRow(items);
+ }
+ comicsView->header()->resizeSections(QHeaderView::ResizeToContents);
+ comicsView->header()->setSectionResizeMode(FileColumn, QHeaderView::Stretch);
+ comicsView->header()->setSectionResizeMode(SeriesColumn, QHeaderView::Stretch);
+}
+
+void CblFindComicPage::chooseSelectedComic()
+{
+ const auto selected = comicsView->selectionModel()->selectedRows();
+ if (selected.isEmpty())
+ return;
+ const auto position = selected.constFirst().data(ComicPositionRole).toInt();
+ if (position < 0 || position >= comics.size())
+ return;
+ emit comicChosen(libraryComic(comics.at(position)), nextProblemCheck->isChecked());
+}
+
+QString CblFindComicPage::breadcrumb(QStandardItem *folder) const
+{
+ if (folder == nullptr)
+ return tr("Library");
+
+ QStringList parts { QStringLiteral("") + folder->text().toHtmlEscaped() + QStringLiteral("") };
+ for (auto *item = folder->parent(); item != nullptr; item = item->parent())
+ parts.prepend(item->text().toHtmlEscaped());
+ parts.prepend(tr("Library").toHtmlEscaped());
+ return parts.join(QStringLiteral(" / "));
+}
diff --git a/YACReaderLibrary/cbl/cbl_find_comic_page.h b/YACReaderLibrary/cbl/cbl_find_comic_page.h
new file mode 100644
index 000000000..6e16cbb6f
--- /dev/null
+++ b/YACReaderLibrary/cbl/cbl_find_comic_page.h
@@ -0,0 +1,93 @@
+#ifndef CBL_FIND_COMIC_PAGE_H
+#define CBL_FIND_COMIC_PAGE_H
+
+#include "cbl_import_model.h"
+#include "comic_db.h"
+
+#include
+
+#include
+
+class CblMatcher;
+class QCheckBox;
+class QLabel;
+class QLineEdit;
+class QSqlDatabase;
+class QPushButton;
+class QStandardItem;
+class QStandardItemModel;
+class QTimer;
+class QTreeView;
+
+struct CblLibraryFolder {
+ qulonglong id = 0;
+ qulonglong parentId = 0;
+ QString name;
+ QString path;
+};
+
+// browse the folders or search the library to choose the comic of one entry
+class CblFindComicPage : public QWidget
+{
+ Q_OBJECT
+
+public:
+ // OneEntry has no entry list to go back to and no other problems; backRequested is its Cancel
+ enum class Mode {
+ ImportEntries,
+ OneEntry,
+ };
+
+ CblFindComicPage(const QString &libraryPath, Mode mode = Mode::ImportEntries, QWidget *parent = nullptr);
+
+ // every folder but the root, sorted by name
+ static std::optional> loadFolders(QSqlDatabase &db);
+ // the folder of the comic the entry refers to, else of its first possible comic, else of the closest series
+ static QString startFolderPath(const CblImportEntry &entry, const CblMatcher &matcher);
+
+ void setFolders(const QList &folders);
+ // only the comics of the folder are shown and searched, without the folder tree; after setFolders
+ void limitToFolder(qulonglong folderId);
+ void setWarningColor(const QColor &color);
+ // `problem` is the position of the entry among the problems, or -1 when it is not one
+ void showEntry(const CblImportEntry &entry, int row, int entryCount, CblImportStatus status, const QString &folderPath, int problem, int problemCount, bool hasPrevious, bool hasNext);
+
+signals:
+ void backRequested();
+ void previousProblemRequested();
+ void nextProblemRequested();
+ void comicChosen(const CblLibraryComic &comic, bool goToNextProblem);
+
+private:
+ void showFolder(qulonglong folderId);
+ void search();
+ void showComics(const QList &comics);
+ void chooseSelectedComic();
+ QString breadcrumb(QStandardItem *folder) const;
+
+ QString libraryPath;
+ Mode mode;
+ std::optional onlyFolderId;
+ CblImportEntry entry;
+ QColor warningColor;
+ QList comics;
+
+ QLabel *entryLabel;
+ QLabel *problemLabel;
+ QLabel *problemCountLabel;
+ QPushButton *previousButton;
+ QPushButton *nextButton;
+ QTreeView *foldersView;
+ QStandardItemModel *foldersModel;
+ QHash folderItems;
+ QHash folderIdsByPath;
+ QLabel *breadcrumbLabel;
+ QLineEdit *searchEdit;
+ QTimer *searchTimer;
+ QTreeView *comicsView;
+ QStandardItemModel *comicsModel;
+ QCheckBox *nextProblemCheck;
+ QPushButton *useButton;
+};
+
+#endif
diff --git a/YACReaderLibrary/cbl/cbl_import_dialog.cpp b/YACReaderLibrary/cbl/cbl_import_dialog.cpp
new file mode 100644
index 000000000..3becd50a8
--- /dev/null
+++ b/YACReaderLibrary/cbl/cbl_import_dialog.cpp
@@ -0,0 +1,696 @@
+#include "cbl_import_dialog.h"
+
+#include "QsLog.h"
+#include "cbl_entries_view.h"
+#include "cbl_entry_detail.h"
+#include "cbl_find_comic_page.h"
+#include "cbl_importer.h"
+#include "data_base_management.h"
+#include "library_comic_match.h"
+#include "yacreader_global.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+struct CblImportFile {
+ QString filePath;
+ std::optional readError;
+ bool libraryError = false;
+ CblReadingList list;
+ QList entries;
+ QStringList readingListNames;
+ std::shared_ptr matcher;
+ QList folders;
+};
+
+namespace {
+
+// the other filter button ids are the statuses; QButtonGroup reads -1 as "choose an id", so it cannot mean all
+constexpr int AllEntriesFilter = int(CblImportStatus::Changed) + 1;
+
+std::optional loadTopLevelReadingListNames(QSqlDatabase &db)
+{
+ QSqlQuery query(db);
+ query.setForwardOnly(true);
+ if (!query.exec("SELECT name FROM reading_list WHERE parentId IS NULL")) {
+ QLOG_ERROR() << "Unable to load the reading list names:" << query.lastError().text();
+ return std::nullopt;
+ }
+
+ QStringList names;
+ while (query.next())
+ names.append(query.value(0).toString());
+ return names;
+}
+
+CblImportFile loadCblImportFile(const QString &filePath, const QString &libraryPath)
+{
+ CblImportFile result;
+ result.filePath = filePath;
+
+ auto read = readCbl(filePath);
+ if (const auto *error = std::get_if(&read)) {
+ QLOG_WARN() << "Unable to read the reading list" << filePath << error->detail;
+ result.readError = *error;
+ return result;
+ }
+ result.list = std::get(std::move(read));
+
+ QString connectionName;
+ {
+ auto db = DataBaseManagement::loadDatabase(YACReader::LibraryPaths::libraryDataPath(libraryPath));
+ connectionName = db.connectionName();
+
+ const auto comics = db.isOpen() ? CblMatcher::loadLibraryComics(db) : std::nullopt;
+ const auto placeholders = db.isOpen() ? CblMatcher::loadLibraryPlaceholders(db) : std::nullopt;
+ const auto names = db.isOpen() ? loadTopLevelReadingListNames(db) : std::nullopt;
+ const auto folders = db.isOpen() ? CblFindComicPage::loadFolders(db) : std::nullopt;
+ if (!comics || !placeholders || !names || !folders) {
+ result.libraryError = true;
+ } else {
+ result.readingListNames = *names;
+ result.folders = *folders;
+ result.matcher = std::make_shared(*comics, *placeholders);
+ result.entries.reserve(result.list.books.size());
+ for (const auto &book : std::as_const(result.list.books)) {
+ CblImportEntry entry;
+ entry.book = book;
+ entry.match = result.matcher->match(book);
+ entry.choice = entry.match.accepted;
+ result.entries.append(entry);
+ }
+ }
+ }
+ if (!connectionName.isEmpty())
+ QSqlDatabase::removeDatabase(connectionName);
+ return result;
+}
+
+// the comics of a list entry are comics with a file: an entry that has one keeps it as its match
+CblImportFile loadCblListEntries(const QList &listEntries, const QString &libraryPath)
+{
+ CblImportFile result;
+
+ QString connectionName;
+ {
+ auto db = DataBaseManagement::loadDatabase(YACReader::LibraryPaths::libraryDataPath(libraryPath));
+ connectionName = db.connectionName();
+
+ const auto comics = db.isOpen() ? CblMatcher::loadLibraryComics(db) : std::nullopt;
+ const auto folders = db.isOpen() ? CblFindComicPage::loadFolders(db) : std::nullopt;
+ if (!comics || !folders) {
+ result.libraryError = true;
+ } else {
+ result.folders = *folders;
+ QList withFile;
+ std::copy_if(comics->cbegin(), comics->cend(), std::back_inserter(withFile), [](const CblLibraryComic &comic) { return comic.hasFile; });
+ result.matcher = std::make_shared(withFile);
+ result.entries.reserve(listEntries.size());
+ for (const auto &listEntry : listEntries) {
+ CblImportEntry entry;
+ entry.book = listEntry.book;
+ entry.current = listEntry.current;
+ const auto *comic = std::get_if(&listEntry.current);
+ if (comic != nullptr && comic->hasFile) {
+ entry.match.accepted = listEntry.current;
+ entry.match.candidates = { listEntry.current };
+ } else {
+ entry.match = result.matcher->match(listEntry.book);
+ }
+ entry.choice = entry.match.accepted;
+ result.entries.append(entry);
+ }
+ }
+ }
+ if (!connectionName.isEmpty())
+ QSqlDatabase::removeDatabase(connectionName);
+ return result;
+}
+
+// checkable buttons that look like the segments of a filter; colours come from the palette, which the theme drives
+QString filterButtonsStyleSheet()
+{
+ return QStringLiteral(
+ "QPushButton {"
+ " border: 1px solid palette(mid);"
+ " border-radius: 4px;"
+ " padding: 4px 14px;"
+ " background-color: palette(button);"
+ " color: palette(button-text);"
+ "}"
+ "QPushButton:hover { background-color: palette(midlight); }"
+ "QPushButton:checked {"
+ " background-color: palette(highlight);"
+ " color: palette(highlighted-text);"
+ " border-color: palette(highlight);"
+ "}");
+}
+
+}
+
+CblImportDialog::CblImportDialog(const QStringList &filePaths, const QString &libraryPath, QWidget *parent)
+ : CblImportDialog(libraryPath, parent)
+{
+ this->filePaths = filePaths;
+ loadCurrentFile();
+}
+
+CblImportDialog::CblImportDialog(const QString &listName, const QList &entries, const QString &libraryPath, QWidget *parent)
+ : CblImportDialog(libraryPath, parent)
+{
+ listEntries = entries;
+ this->listName = listName;
+ setWindowTitle(tr("Match to library comics — %1").arg(listName));
+ nameRow->hide();
+ nameConflictLabel->hide();
+ skipButton->hide();
+ importButton->setText(tr("Apply"));
+ loadListEntries();
+}
+
+CblImportDialog::CblImportDialog(const QString &libraryPath, QWidget *parent)
+ : QDialog(parent), libraryPath(libraryPath)
+{
+ model = new CblImportModel(this);
+ filterModel = new CblImportFilterModel(this);
+ filterModel->setSourceModel(model);
+
+ pages = new QStackedWidget;
+ messagePage = createMessagePage();
+ entriesPage = createEntriesPage();
+ findPage = new CblFindComicPage(libraryPath);
+ pages->addWidget(messagePage);
+ pages->addWidget(entriesPage);
+ pages->addWidget(findPage);
+ connect(findPage, &CblFindComicPage::backRequested, this, &CblImportDialog::showEntries);
+ connect(findPage, &CblFindComicPage::previousProblemRequested, this, [this] { showProblem(false); });
+ connect(findPage, &CblFindComicPage::nextProblemRequested, this, [this] { showProblem(true); });
+ connect(findPage, &CblFindComicPage::comicChosen, this, [this](const CblLibraryComic &comic, bool goToNextProblem) {
+ model->choose(findRow, comic);
+ if (!goToNextProblem || !showProblem(true))
+ showEntries();
+ });
+
+ summaryLabel = new QLabel;
+
+ closeButton = new QPushButton(tr("Close"));
+ skipButton = new QPushButton(tr("Skip"));
+ importButton = new QPushButton(tr("Import"));
+ // Return in the name or filter field must not import
+ for (auto *button : { closeButton, skipButton, importButton })
+ button->setAutoDefault(false);
+ connect(closeButton, &QPushButton::clicked, this, &QDialog::reject);
+ connect(skipButton, &QPushButton::clicked, this, &CblImportDialog::nextFile);
+ connect(importButton, &QPushButton::clicked, this, [this] {
+ if (listEntries)
+ applyListChanges();
+ else
+ importCurrentFile();
+ });
+
+ bottomBar = new QWidget;
+ auto buttons = new QHBoxLayout(bottomBar);
+ buttons->setContentsMargins(0, 0, 0, 0);
+ buttons->addWidget(summaryLabel);
+ buttons->addStretch();
+ buttons->addWidget(closeButton);
+ buttons->addWidget(skipButton);
+ buttons->addWidget(importButton);
+
+ auto layout = new QVBoxLayout(this);
+ layout->addWidget(pages, 1);
+ layout->addWidget(bottomBar);
+
+ resize(1000, 700);
+}
+
+CblImportDialog::~CblImportDialog() = default;
+
+QWidget *CblImportDialog::createMessagePage()
+{
+ auto page = new QWidget;
+ auto layout = new QVBoxLayout(page);
+
+ messageLabel = new QLabel;
+ messageLabel->setAlignment(Qt::AlignCenter);
+ messageLabel->setWordWrap(true);
+ layout->addWidget(messageLabel);
+
+ return page;
+}
+
+QWidget *CblImportDialog::createEntriesPage()
+{
+ auto page = new QWidget;
+ auto layout = new QVBoxLayout(page);
+ layout->setContentsMargins(0, 0, 0, 0);
+
+ nameEdit = new QLineEdit;
+ connect(nameEdit, &QLineEdit::textChanged, this, &CblImportDialog::updateNameConflict);
+ auto nameLabel = new QLabel(tr("&Name:"));
+ nameLabel->setBuddy(nameEdit);
+ nameConflictLabel = new QLabel(tr("A reading list with this name already exists. Import creates another one."));
+
+ nameRow = new QWidget;
+ auto nameLayout = new QHBoxLayout(nameRow);
+ nameLayout->setContentsMargins(0, 0, 0, 0);
+ nameLayout->addWidget(nameLabel);
+ nameLayout->addWidget(nameEdit, 1);
+
+ filterButtons = new QButtonGroup(this);
+ auto filterButtonsWidget = new QWidget;
+ auto filterButtonsLayout = new QHBoxLayout(filterButtonsWidget);
+ filterButtonsLayout->setContentsMargins(0, 0, 0, 0);
+ for (const auto id : { AllEntriesFilter, int(CblImportStatus::Matched), int(CblImportStatus::Ambiguous), int(CblImportStatus::Missing), int(CblImportStatus::Changed) }) {
+ auto button = new QPushButton;
+ button->setCheckable(true);
+ button->setAutoDefault(false);
+ filterButtons->addButton(button, id);
+ filterButtonList.append(button);
+ filterButtonsLayout->addWidget(button);
+ }
+ filterButtons->button(AllEntriesFilter)->setChecked(true);
+ filterButtonsWidget->setStyleSheet(filterButtonsStyleSheet());
+ connect(filterButtons, &QButtonGroup::idClicked, this, [this](int id) {
+ filterModel->setStatusFilter(id == AllEntriesFilter ? std::nullopt : std::optional(CblImportStatus(id)));
+ });
+
+ filterEdit = new QLineEdit;
+ filterEdit->setPlaceholderText(tr("Filter entries"));
+ filterEdit->setClearButtonEnabled(true);
+ connect(filterEdit, &QLineEdit::textChanged, filterModel, &QSortFilterProxyModel::setFilterFixedString);
+
+ auto filterRow = new QHBoxLayout;
+ filterRow->addWidget(filterButtonsWidget);
+ filterRow->addStretch();
+ filterRow->addWidget(filterEdit);
+
+ entriesView = new CblEntriesView;
+ entriesView->setModel(filterModel);
+ entriesView->setAlternatingRowColors(true);
+ entriesView->setSelectionMode(QAbstractItemView::ExtendedSelection);
+ entriesView->setSelectionBehavior(QAbstractItemView::SelectRows);
+ entriesView->setEditTriggers(QAbstractItemView::NoEditTriggers);
+ entriesView->header()->setStretchLastSection(false);
+ entriesView->header()->setSectionResizeMode(CblImportModel::NumberColumn, QHeaderView::ResizeToContents);
+ entriesView->header()->setSectionResizeMode(CblImportModel::BookColumn, QHeaderView::Stretch);
+ entriesView->header()->setSectionResizeMode(CblImportModel::ComicColumn, QHeaderView::Stretch);
+ entriesView->header()->setSectionResizeMode(CblImportModel::StatusColumn, QHeaderView::ResizeToContents);
+ entriesView->header()->setSectionResizeMode(CblImportModel::ActionColumn, QHeaderView::ResizeToContents);
+ connect(entriesView, &QTreeView::expanded, this, &CblImportDialog::createDetail);
+ connect(entriesView->header(), &QHeaderView::sectionResized, this, [this] {
+ for (auto *detail : entriesView->viewport()->findChildren())
+ alignDetail(detail);
+ });
+ connect(entriesView, &CblEntriesView::findComicRequested, this, [this](const QModelIndex &index) {
+ showFind(filterModel->mapToSource(index).row());
+ });
+ entriesView->setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(entriesView, &QTreeView::customContextMenuRequested, this, &CblImportDialog::showEntriesMenu);
+ connect(model, &QAbstractItemModel::dataChanged, this, &CblImportDialog::updateCounts);
+ // a detail can change its own entry, so it is replaced after its signal returns
+ connect(model, &QAbstractItemModel::dataChanged, this, [this](const QModelIndex &topLeft, const QModelIndex &bottomRight) {
+ QList changed;
+ for (int row = topLeft.row(); row <= bottomRight.row(); ++row) {
+ const auto index = filterModel->mapFromSource(model->index(row, 0));
+ if (index.isValid() && entriesView->isExpanded(index))
+ changed.append(index);
+ }
+ if (!changed.isEmpty())
+ QMetaObject::invokeMethod(this, [this, changed] {
+ for (const auto &index : changed) {
+ if (index.isValid() && entriesView->isExpanded(index))
+ createDetail(index);
+ } }, Qt::QueuedConnection);
+ });
+
+ layout->addWidget(nameRow);
+ layout->addWidget(nameConflictLabel);
+ layout->addLayout(filterRow);
+ layout->addWidget(entriesView, 1);
+
+ return page;
+}
+
+void CblImportDialog::loadCurrentFile()
+{
+ file.reset();
+ findRow = -1;
+ updateTitle();
+ showMessage(tr("Matching the books of %1 with your library...").arg(QFileInfo(filePaths.at(currentFile)).fileName()));
+ skipButton->setEnabled(false);
+
+ auto result = std::make_shared();
+ auto thread = QThread::create([result, filePath = filePaths.at(currentFile), libraryPath = libraryPath] {
+ *result = loadCblImportFile(filePath, libraryPath);
+ });
+ connect(thread, &QThread::finished, thread, &QObject::deleteLater);
+ connect(thread, &QThread::finished, this, [this, result] { showFile(*result); });
+ thread->start();
+}
+
+void CblImportDialog::loadListEntries()
+{
+ showMessage(tr("Matching the entries of %1 with your library...").arg(listName));
+
+ auto result = std::make_shared();
+ auto thread = QThread::create([result, entries = *listEntries, libraryPath = libraryPath] {
+ *result = loadCblListEntries(entries, libraryPath);
+ });
+ connect(thread, &QThread::finished, thread, &QObject::deleteLater);
+ connect(thread, &QThread::finished, this, [this, result] { showFile(*result); });
+ thread->start();
+}
+
+void CblImportDialog::applyListChanges()
+{
+ if (!file || !listEntries)
+ return;
+
+ QList changes;
+ QList matched;
+ QSet listIds;
+ auto listKind = ListKind::ReadingList;
+ for (int row = 0; row < model->rowCount(); ++row) {
+ const auto &entry = model->entries().at(row);
+ const auto &listEntry = listEntries->at(row);
+ listIds.insert(listEntry.listId);
+ listKind = listEntry.id.listKind;
+
+ const auto *chosen = entry.choice ? std::get_if(&*entry.choice) : nullptr;
+ const auto *currentComic = std::get_if(&listEntry.current);
+ if (chosen != nullptr && chosen->hasFile) {
+ if (currentComic != nullptr && currentComic->id == chosen->id)
+ continue;
+ changes.append({ listEntry.id, chosen->id });
+ if (const auto *placeholder = std::get_if(&listEntry.current))
+ matched.append(placeholder->id);
+ else if (!currentComic->hasFile)
+ matched.append(currentComic->id);
+ } else if (!entry.choice && currentComic != nullptr && currentComic->hasFile) {
+ changes.append({ listEntry.id, std::nullopt });
+ }
+ }
+ if (changes.isEmpty()) {
+ accept();
+ return;
+ }
+
+ const auto dataPath = YACReader::LibraryPaths::libraryDataPath(libraryPath);
+ std::optional otherLists;
+ QString connectionName;
+ {
+ auto db = DataBaseManagement::loadDatabase(dataPath);
+ connectionName = db.connectionName();
+ if (db.isOpen())
+ otherLists = otherListsWithEntriesOf(matched, listKind, listIds, db);
+ }
+ if (!connectionName.isEmpty())
+ QSqlDatabase::removeDatabase(connectionName);
+
+ if (otherLists && !otherLists->isEmpty()) {
+ const auto answer = QMessageBox::question(this,
+ tr("Match to library comics"),
+ tr("This also gives comics to entries in %n other list(s): %1", "", otherLists->size()).arg(otherLists->join(QStringLiteral(", "))),
+ QMessageBox::Apply | QMessageBox::Cancel,
+ QMessageBox::Apply);
+ if (answer != QMessageBox::Apply)
+ return;
+ }
+
+ bool applied = false;
+ if (otherLists) {
+ ComicMatchFiles files;
+ {
+ auto db = DataBaseManagement::loadDatabase(dataPath);
+ connectionName = db.connectionName();
+ db.transaction();
+ applied = applyCblListChanges(changes, dataPath, db, files) && db.commit();
+ if (!applied)
+ db.rollback();
+ }
+ QSqlDatabase::removeDatabase(connectionName);
+ finishComicMatchFiles(dataPath, files, applied);
+ }
+
+ if (!applied) {
+ QMessageBox::critical(this, tr("Match to library comics"), tr("The changes could not be applied. Nothing was changed."));
+ return;
+ }
+ emit listEntriesChanged();
+ accept();
+}
+
+void CblImportDialog::showFile(const CblImportFile &loaded)
+{
+ skipButton->setEnabled(true);
+
+ if (loaded.readError) {
+ switch (loaded.readError->kind) {
+ case CblReadError::CannotOpen:
+ showMessage(tr("The file cannot be opened."));
+ break;
+ case CblReadError::Malformed:
+ showMessage(tr("The file is damaged (line %1, column %2).").arg(loaded.readError->line).arg(loaded.readError->column));
+ break;
+ case CblReadError::NotAReadingList:
+ showMessage(tr("The file is not a reading list."));
+ break;
+ }
+ return;
+ }
+ if (loaded.libraryError) {
+ showMessage(tr("The library cannot be read."));
+ return;
+ }
+
+ file = std::make_unique(loaded);
+ model->setWarningColor(warningColor());
+ findPage->setWarningColor(warningColor());
+ nameConflictLabel->setStyleSheet(QStringLiteral("color: %1;").arg(warningColor().name()));
+ findPage->setFolders(file->folders);
+ model->setEntries(file->entries);
+ filterEdit->clear();
+ filterButtons->button(AllEntriesFilter)->click();
+ updateCounts();
+ if (!listEntries) {
+ nameEdit->setText(cblListName(file->list, file->filePath));
+ updateNameConflict();
+ }
+
+ showEntries();
+ importButton->setEnabled(true);
+ summaryLabel->setVisible(true);
+}
+
+void CblImportDialog::showEntries()
+{
+ pages->setCurrentWidget(entriesPage);
+ bottomBar->setVisible(true);
+
+ if (findRow >= 0 && findRow < model->rowCount()) {
+ const auto index = filterModel->mapFromSource(model->index(findRow, 0));
+ if (index.isValid()) {
+ entriesView->scrollTo(index);
+ entriesView->setCurrentIndex(index);
+ }
+ }
+}
+
+void CblImportDialog::showFind(int row)
+{
+ if (!file || row < 0 || row >= model->rowCount())
+ return;
+
+ findRow = row;
+ const auto &entry = model->entries().at(row);
+ const auto problems = model->problemRows();
+ const auto problem = problems.indexOf(row);
+ const bool hasPrevious = !problems.isEmpty() && problems.constFirst() < row;
+ const bool hasNext = !problems.isEmpty() && problems.constLast() > row;
+
+ findPage->showEntry(entry, row, model->rowCount(), model->status(row), CblFindComicPage::startFolderPath(entry, *file->matcher), problem, problems.size(), hasPrevious, hasNext);
+ pages->setCurrentWidget(findPage);
+ bottomBar->setVisible(false);
+}
+
+bool CblImportDialog::showProblem(bool forward)
+{
+ const auto problems = model->problemRows();
+ if (forward) {
+ const auto next = std::find_if(problems.cbegin(), problems.cend(), [this](int row) { return row > findRow; });
+ if (next == problems.cend())
+ return false;
+ showFind(*next);
+ } else {
+ const auto previous = std::find_if(problems.crbegin(), problems.crend(), [this](int row) { return row < findRow; });
+ if (previous == problems.crend())
+ return false;
+ showFind(*previous);
+ }
+ return true;
+}
+
+void CblImportDialog::alignDetail(CblEntryDetail *detail) const
+{
+ // a detail is a child row, so its widget starts two indentations in: the root decoration and its own level
+ const auto *header = entriesView->header();
+ const auto detailLeft = header->sectionViewportPosition(CblImportModel::NumberColumn) + 2 * entriesView->indentation();
+ detail->alignLibraryColumns(header->sectionViewportPosition(CblImportModel::ComicColumn) - detailLeft);
+}
+
+void CblImportDialog::createDetail(const QModelIndex &index)
+{
+ if (!file || !index.isValid() || CblImportModel::isDetail(index))
+ return;
+
+ const auto row = filterModel->mapToSource(index).row();
+ const auto &entry = model->entries().at(row);
+ const auto closest = entry.choice || !entry.match.candidates.isEmpty() ? QList() : file->matcher->closestSeries(entry.book);
+ auto detail = new CblEntryDetail(entry, row, model->rowCount(), closest, YACReader::LibraryPaths::libraryDataPath(libraryPath), warningColor());
+ connect(detail, &CblEntryDetail::candidateChosen, this, [this, row](const CblCandidate &candidate) { model->choose(row, candidate); });
+
+ alignDetail(detail);
+ entriesView->setFirstColumnSpanned(0, index, true);
+ entriesView->setIndexWidget(filterModel->index(0, 0, index), detail);
+ entriesView->doItemsLayout();
+}
+
+void CblImportDialog::showMessage(const QString &message)
+{
+ messageLabel->setText(message);
+ pages->setCurrentWidget(messagePage);
+ bottomBar->setVisible(true);
+ importButton->setEnabled(false);
+ summaryLabel->setVisible(false);
+}
+
+void CblImportDialog::importCurrentFile()
+{
+ if (!file)
+ return;
+
+ auto name = nameEdit->text().trimmed();
+ if (name.isEmpty())
+ name = cblListName(file->list, file->filePath);
+
+ std::optional id;
+ QString connectionName;
+ {
+ auto db = DataBaseManagement::loadDatabase(YACReader::LibraryPaths::libraryDataPath(libraryPath));
+ connectionName = db.connectionName();
+ if (db.isOpen())
+ id = importCblReadingList(name, file->list, model->choices(), db);
+ }
+ if (!connectionName.isEmpty())
+ QSqlDatabase::removeDatabase(connectionName);
+
+ if (!id) {
+ QMessageBox::critical(this, tr("Import reading lists"), tr("The reading list could not be imported. Nothing was written."));
+ return;
+ }
+
+ emit readingListImported(*id, name);
+ nextFile();
+}
+
+void CblImportDialog::nextFile()
+{
+ if (currentFile + 1 >= filePaths.size()) {
+ accept();
+ return;
+ }
+
+ ++currentFile;
+ loadCurrentFile();
+}
+
+void CblImportDialog::updateTitle()
+{
+ const auto fileName = QFileInfo(filePaths.at(currentFile)).fileName();
+ if (filePaths.size() > 1)
+ setWindowTitle(tr("Import reading lists — %1 (%2 of %3)").arg(fileName).arg(currentFile + 1).arg(filePaths.size()));
+ else
+ setWindowTitle(tr("Import reading lists — %1").arg(fileName));
+
+ skipButton->setVisible(currentFile + 1 < filePaths.size());
+}
+
+void CblImportDialog::updateCounts()
+{
+ const auto total = model->rowCount();
+ const QStringList texts = {
+ tr("All (%1)").arg(total),
+ tr("Matched (%1)").arg(model->count(CblImportStatus::Matched)),
+ tr("Ambiguous (%1)").arg(model->count(CblImportStatus::Ambiguous)),
+ tr("Missing (%1)").arg(model->count(CblImportStatus::Missing)),
+ tr("Changed by you (%1)").arg(model->count(CblImportStatus::Changed)),
+ };
+ for (int i = 0; i < filterButtonList.size(); ++i)
+ filterButtonList.at(i)->setText(texts.at(i));
+
+ summaryLabel->setText(tr("%1 of %n have a comic", "", total).arg(model->withComicCount()));
+}
+
+void CblImportDialog::updateNameConflict()
+{
+ const bool exists = file && file->readingListNames.contains(nameEdit->text().trimmed(), Qt::CaseInsensitive);
+ nameConflictLabel->setVisible(exists);
+}
+
+void CblImportDialog::showEntriesMenu(const QPoint &position)
+{
+ const auto rows = selectedRows();
+ if (rows.isEmpty())
+ return;
+
+ const bool anyChanged = std::any_of(rows.cbegin(), rows.cend(), [this](int row) { return model->status(row) == CblImportStatus::Changed; });
+ const bool anyCanBeMissing = std::any_of(rows.cbegin(), rows.cend(), [this](int row) {
+ return model->entries().at(row).choice || model->status(row) == CblImportStatus::Ambiguous;
+ });
+
+ QMenu menu(this);
+ if (rows.size() == 1) {
+ auto find = menu.addAction(tr("Find comic..."));
+ connect(find, &QAction::triggered, this, [this, row = rows.constFirst()] { showFind(row); });
+ menu.addSeparator();
+ }
+ auto original = menu.addAction(tr("Use the original match"));
+ original->setEnabled(anyChanged);
+ connect(original, &QAction::triggered, this, [this, rows] { model->useOriginalMatch(rows); });
+ auto missing = menu.addAction(listEntries ? tr("Make missing") : tr("Import as missing"));
+ missing->setEnabled(anyCanBeMissing);
+ connect(missing, &QAction::triggered, this, [this, rows] { model->importAsMissing(rows); });
+ menu.exec(entriesView->viewport()->mapToGlobal(position));
+}
+
+QList CblImportDialog::selectedRows() const
+{
+ QList rows;
+ for (const auto &index : entriesView->selectionModel()->selectedRows())
+ rows.append(filterModel->mapToSource(index).row());
+ return rows;
+}
+
+QColor CblImportDialog::warningColor() const
+{
+ return cblWarningColor(palette());
+}
diff --git a/YACReaderLibrary/cbl/cbl_import_dialog.h b/YACReaderLibrary/cbl/cbl_import_dialog.h
new file mode 100644
index 000000000..b219ab6ef
--- /dev/null
+++ b/YACReaderLibrary/cbl/cbl_import_dialog.h
@@ -0,0 +1,93 @@
+#ifndef CBL_IMPORT_DIALOG_H
+#define CBL_IMPORT_DIALOG_H
+
+#include "cbl_import_model.h"
+#include "cbl_list_match.h"
+#include "library_ids.h"
+
+#include
+
+#include
+
+class CblEntriesView;
+class CblEntryDetail;
+class CblFindComicPage;
+class QButtonGroup;
+class QLabel;
+class QLineEdit;
+class QPushButton;
+class QStackedWidget;
+
+struct CblImportFile;
+
+// imports the files one after the other; each Import writes the reading list of the current file. In a list, it
+// matches the entries of a reading list or label (or some of them) to library comics, and Apply writes the changes.
+class CblImportDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ CblImportDialog(const QStringList &filePaths, const QString &libraryPath, QWidget *parent = nullptr);
+ CblImportDialog(const QString &listName, const QList &entries, const QString &libraryPath, QWidget *parent = nullptr);
+ ~CblImportDialog() override;
+
+signals:
+ void readingListImported(ReadingListId id, const QString &name);
+ void listEntriesChanged();
+
+private:
+ explicit CblImportDialog(const QString &libraryPath, QWidget *parent);
+ void loadListEntries();
+ void applyListChanges();
+ QWidget *createMessagePage();
+ QWidget *createEntriesPage();
+ void loadCurrentFile();
+ void showFile(const CblImportFile &file);
+ void showMessage(const QString &message);
+ void showEntries();
+ void showFind(int row);
+ // the next or previous Ambiguous or Missing entry; false when there is none
+ bool showProblem(bool forward);
+ void createDetail(const QModelIndex &index);
+ void alignDetail(CblEntryDetail *detail) const;
+ void importCurrentFile();
+ void nextFile();
+ void updateTitle();
+ void updateCounts();
+ void updateNameConflict();
+ void showEntriesMenu(const QPoint &position);
+ QList selectedRows() const;
+ QColor warningColor() const;
+
+ QStringList filePaths;
+ // set in a list
+ std::optional> listEntries;
+ QString listName;
+ QString libraryPath;
+ int currentFile = 0;
+ // the entry of the Find comic page
+ int findRow = -1;
+ std::unique_ptr file;
+
+ QStackedWidget *pages;
+ QWidget *messagePage;
+ QLabel *messageLabel;
+ QWidget *entriesPage;
+ QWidget *nameRow;
+ QLineEdit *nameEdit;
+ QLabel *nameConflictLabel;
+ QButtonGroup *filterButtons;
+ QList filterButtonList;
+ QLineEdit *filterEdit;
+ CblEntriesView *entriesView;
+ CblFindComicPage *findPage;
+ QWidget *bottomBar;
+ CblImportModel *model;
+ CblImportFilterModel *filterModel;
+ QLabel *summaryLabel;
+ QPushButton *closeButton;
+ QPushButton *skipButton;
+ QPushButton *importButton;
+};
+
+#endif
diff --git a/YACReaderLibrary/cbl/cbl_import_model.cpp b/YACReaderLibrary/cbl/cbl_import_model.cpp
new file mode 100644
index 000000000..bddb4a3b0
--- /dev/null
+++ b/YACReaderLibrary/cbl/cbl_import_model.cpp
@@ -0,0 +1,284 @@
+#include "cbl_import_model.h"
+
+#include
+
+#include
+
+// the internal id of an entry is 0; the one of a detail is the row of its entry plus one
+static bool isDetailIndex(const QModelIndex &index)
+{
+ return index.isValid() && index.internalId() != 0;
+}
+
+CblImportModel::CblImportModel(QObject *parent)
+ : QAbstractItemModel(parent)
+{
+}
+
+QModelIndex CblImportModel::index(int row, int column, const QModelIndex &parent) const
+{
+ if (column < 0 || column >= ColumnCount)
+ return { };
+ if (!parent.isValid())
+ return row >= 0 && row < importEntries.size() ? createIndex(row, column, quintptr(0)) : QModelIndex();
+ if (isDetailIndex(parent) || row != 0)
+ return { };
+ return createIndex(0, column, quintptr(parent.row() + 1));
+}
+
+QModelIndex CblImportModel::parent(const QModelIndex &child) const
+{
+ if (!isDetailIndex(child))
+ return { };
+ return createIndex(int(child.internalId() - 1), 0, quintptr(0));
+}
+
+int CblImportModel::rowCount(const QModelIndex &parent) const
+{
+ if (!parent.isValid())
+ return importEntries.size();
+ return !isDetailIndex(parent) && parent.column() == 0 ? 1 : 0;
+}
+
+int CblImportModel::columnCount(const QModelIndex &) const
+{
+ return ColumnCount;
+}
+
+Qt::ItemFlags CblImportModel::flags(const QModelIndex &index) const
+{
+ if (isDetailIndex(index))
+ return Qt::ItemIsEnabled;
+ return QAbstractItemModel::flags(index);
+}
+
+// also for the indexes of a proxy model
+bool CblImportModel::isDetail(const QModelIndex &index)
+{
+ return index.isValid() && index.parent().isValid();
+}
+
+QVariant CblImportModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid() || isDetailIndex(index) || index.row() >= importEntries.size())
+ return { };
+
+ const auto &entry = importEntries.at(index.row());
+ const auto entryStatus = status(index.row());
+
+ if (role == StatusRole)
+ return int(entryStatus);
+
+ if (role == Qt::DisplayRole) {
+ switch (index.column()) {
+ case NumberColumn:
+ return index.row() + 1;
+ case BookColumn:
+ return bookTitle(entry.book);
+ case ComicColumn:
+ return comicText(entry);
+ case StatusColumn:
+ switch (entryStatus) {
+ case CblImportStatus::Matched:
+ return tr("Matched");
+ case CblImportStatus::Ambiguous:
+ return tr("Ambiguous");
+ case CblImportStatus::Missing:
+ return tr("Missing");
+ case CblImportStatus::Changed:
+ return tr("Changed");
+ }
+ }
+ return { };
+ }
+
+ if (role == Qt::FontRole && index.column() == ComicColumn && comicTextIsNote(entry)) {
+ QFont font;
+ font.setItalic(true);
+ return font;
+ }
+
+ if (role == Qt::ForegroundRole && index.column() == StatusColumn && warningColor.isValid() &&
+ (entryStatus == CblImportStatus::Ambiguous || entryStatus == CblImportStatus::Missing))
+ return warningColor;
+
+ return { };
+}
+
+QVariant CblImportModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (orientation != Qt::Horizontal || role != Qt::DisplayRole)
+ return { };
+
+ switch (section) {
+ case NumberColumn:
+ return tr("#");
+ case BookColumn:
+ return tr("In the file");
+ case ComicColumn:
+ return tr("Comic in your library");
+ case StatusColumn:
+ return tr("Status");
+ case ActionColumn:
+ return QString();
+ }
+ return { };
+}
+
+void CblImportModel::setEntries(const QList &entries)
+{
+ beginResetModel();
+ importEntries = entries;
+ endResetModel();
+}
+
+void CblImportModel::setWarningColor(const QColor &color)
+{
+ warningColor = color;
+ if (!importEntries.isEmpty())
+ emit dataChanged(index(0, StatusColumn), index(int(importEntries.size()) - 1, StatusColumn), { Qt::ForegroundRole });
+}
+
+CblImportStatus CblImportModel::status(int row) const
+{
+ const auto &entry = importEntries.at(row);
+ if (entry.changed)
+ return CblImportStatus::Changed;
+ if (entry.choice)
+ return CblImportStatus::Matched;
+ return entry.match.candidates.isEmpty() ? CblImportStatus::Missing : CblImportStatus::Ambiguous;
+}
+
+int CblImportModel::count(CblImportStatus status) const
+{
+ int result = 0;
+ for (int row = 0; row < importEntries.size(); ++row) {
+ if (this->status(row) == status)
+ ++result;
+ }
+ return result;
+}
+
+int CblImportModel::withComicCount() const
+{
+ return std::count_if(importEntries.cbegin(), importEntries.cend(), [](const CblImportEntry &entry) {
+ const auto *comic = entry.choice ? std::get_if(&*entry.choice) : nullptr;
+ return comic != nullptr && comic->hasFile;
+ });
+}
+
+QList CblImportModel::problemRows() const
+{
+ QList rows;
+ for (int row = 0; row < importEntries.size(); ++row) {
+ const auto rowStatus = status(row);
+ if (rowStatus == CblImportStatus::Ambiguous || rowStatus == CblImportStatus::Missing)
+ rows.append(row);
+ }
+ return rows;
+}
+
+QList> CblImportModel::choices() const
+{
+ QList> result;
+ result.reserve(importEntries.size());
+ for (const auto &entry : importEntries)
+ result.append(entry.choice);
+ return result;
+}
+
+void CblImportModel::choose(int row, const CblCandidate &candidate)
+{
+ changeRows({ row }, [&candidate](CblImportEntry &entry) {
+ entry.choice = candidate;
+ entry.changed = entry.match.accepted != entry.choice;
+ });
+}
+
+void CblImportModel::useOriginalMatch(const QList &rows)
+{
+ changeRows(rows, [](CblImportEntry &entry) {
+ entry.choice = entry.match.accepted;
+ entry.changed = false;
+ });
+}
+
+void CblImportModel::importAsMissing(const QList &rows)
+{
+ changeRows(rows, [](CblImportEntry &entry) {
+ entry.choice.reset();
+ // a book that found nothing is already missing, so nothing changes
+ entry.changed = entry.match.accepted || !entry.match.candidates.isEmpty();
+ });
+}
+
+QString CblImportModel::bookTitle(const CblBook &book)
+{
+ if (book.series.isEmpty())
+ return book.fileName;
+
+ auto title = book.series;
+ if (!book.number.isEmpty())
+ title += QStringLiteral(" #") + book.number;
+ const auto &volume = book.volume.isEmpty() ? book.year : book.volume;
+ if (!volume.isEmpty())
+ title += QStringLiteral(" (") + volume + QStringLiteral(")");
+ return title;
+}
+
+QString CblImportModel::fileName(const CblLibraryComic &comic)
+{
+ return comic.path.section('/', -1);
+}
+
+QString CblImportModel::comicText(const CblImportEntry &entry) const
+{
+ if (entry.choice) {
+ const auto *comic = std::get_if(&*entry.choice);
+ if (comic != nullptr && comic->hasFile)
+ return fileName(*comic);
+ return tr("Already missing in your library");
+ }
+
+ if (!entry.changed && !entry.match.candidates.isEmpty())
+ return tr("%n possible comic(s)", "", entry.match.candidates.size());
+ return tr("Not in library");
+}
+
+bool CblImportModel::comicTextIsNote(const CblImportEntry &entry) const
+{
+ const auto *comic = entry.choice ? std::get_if(&*entry.choice) : nullptr;
+ return comic == nullptr || !comic->hasFile;
+}
+
+void CblImportModel::changeRows(const QList &rows, const std::function &change)
+{
+ for (const auto row : rows) {
+ if (row < 0 || row >= importEntries.size())
+ continue;
+ change(importEntries[row]);
+ emit dataChanged(index(row, 0), index(row, ColumnCount - 1));
+ }
+}
+
+CblImportFilterModel::CblImportFilterModel(QObject *parent)
+ : QSortFilterProxyModel(parent)
+{
+ setFilterCaseSensitivity(Qt::CaseInsensitive);
+ setFilterKeyColumn(-1);
+}
+
+void CblImportFilterModel::setStatusFilter(std::optional status)
+{
+ statusFilter = status;
+ invalidateRowsFilter();
+}
+
+bool CblImportFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
+{
+ if (sourceParent.isValid())
+ return true;
+ if (statusFilter && sourceModel()->index(sourceRow, 0, sourceParent).data(CblImportModel::StatusRole).toInt() != int(*statusFilter))
+ return false;
+ return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
+}
diff --git a/YACReaderLibrary/cbl/cbl_import_model.h b/YACReaderLibrary/cbl/cbl_import_model.h
new file mode 100644
index 000000000..bbad9cdc5
--- /dev/null
+++ b/YACReaderLibrary/cbl/cbl_import_model.h
@@ -0,0 +1,106 @@
+#ifndef CBL_IMPORT_MODEL_H
+#define CBL_IMPORT_MODEL_H
+
+#include "cbl_matcher.h"
+#include "cbl_reader.h"
+
+#include
+#include
+#include
+
+#include
+#include
+
+struct CblImportEntry {
+ CblBook book;
+ CblBookMatch match;
+ // what the import uses; it starts as the accepted match
+ std::optional choice;
+ bool changed = false;
+ // in a list: what the entry refers to when the view opens
+ std::optional current;
+};
+
+enum class CblImportStatus {
+ Matched,
+ Ambiguous,
+ Missing,
+ Changed,
+};
+
+// one row per entry; each entry has one child row, which holds its detail
+class CblImportModel : public QAbstractItemModel
+{
+ Q_OBJECT
+
+public:
+ enum Column {
+ NumberColumn,
+ BookColumn,
+ ComicColumn,
+ StatusColumn,
+ ActionColumn,
+ ColumnCount,
+ };
+
+ enum Roles {
+ StatusRole = Qt::UserRole + 1,
+ };
+
+ explicit CblImportModel(QObject *parent = nullptr);
+
+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
+ QModelIndex parent(const QModelIndex &child) const override;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+ Qt::ItemFlags flags(const QModelIndex &index) const override;
+ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
+
+ void setEntries(const QList &entries);
+ const QList &entries() const { return importEntries; }
+ void setWarningColor(const QColor &color);
+
+ static bool isDetail(const QModelIndex &index);
+ CblImportStatus status(int row) const;
+ int count(CblImportStatus status) const;
+ // the entries whose choice is a comic with a file
+ int withComicCount() const;
+ // the Ambiguous and Missing entries, in order
+ QList problemRows() const;
+ QList> choices() const;
+
+ void choose(int row, const CblCandidate &candidate);
+ void useOriginalMatch(const QList &rows);
+ void importAsMissing(const QList &rows);
+
+ static QString bookTitle(const CblBook &book);
+ static QString fileName(const CblLibraryComic &comic);
+
+private:
+ QString comicText(const CblImportEntry &entry) const;
+ bool comicTextIsNote(const CblImportEntry &entry) const;
+ void changeRows(const QList &rows, const std::function &change);
+
+ QList importEntries;
+ QColor warningColor;
+};
+
+class CblImportFilterModel : public QSortFilterProxyModel
+{
+ Q_OBJECT
+
+public:
+ explicit CblImportFilterModel(QObject *parent = nullptr);
+
+ // nothing shows every status
+ void setStatusFilter(std::optional status);
+
+protected:
+ bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
+
+private:
+ std::optional statusFilter;
+};
+
+#endif
diff --git a/YACReaderLibrary/cbl/cbl_importer.cpp b/YACReaderLibrary/cbl/cbl_importer.cpp
new file mode 100644
index 000000000..611f3f42b
--- /dev/null
+++ b/YACReaderLibrary/cbl/cbl_importer.cpp
@@ -0,0 +1,144 @@
+#include "cbl_importer.h"
+
+#include "QsLog.h"
+#include "db_helper.h"
+#include "overloaded.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace {
+
+QVariant text(const QString &value)
+{
+ const auto trimmed = value.trimmed();
+ return trimmed.isEmpty() ? QVariant() : QVariant(trimmed);
+}
+
+QString comicVineIssueId(const CblBook &book)
+{
+ static const QRegularExpression digits(R"(^\d+$)");
+ for (const auto &ids : book.databases) {
+ const bool comicVine = ids.name.compare(QLatin1StringView("cv"), Qt::CaseInsensitive) == 0 || ids.name.compare(QLatin1StringView("comicvine"), Qt::CaseInsensitive) == 0;
+ if (comicVine && digits.match(ids.issueId.trimmed()).hasMatch())
+ return ids.issueId.trimmed();
+ }
+ return { };
+}
+
+// dd/mm/yyyy with the parts that are known; a year alone never gets a day or month
+QVariant date(const CblBook &book)
+{
+ const auto parts = book.coverDate.trimmed().split('-');
+ if (parts.size() == 3 && parts.at(0).toInt() > 0)
+ return QString("%1/%2/%3").arg(parts.at(2), parts.at(1), parts.at(0));
+ if (parts.size() == 2 && parts.at(0).toInt() > 0)
+ return QString("/%1/%2").arg(parts.at(1), parts.at(0));
+
+ const auto year = book.year.trimmed().toInt();
+ if (year > 0)
+ return QString("//%1").arg(year);
+ return { };
+}
+
+// the books of one import that are the same issue; empty when a book cannot be compared
+QString sameIssueKey(const CblBook &book)
+{
+ if (const auto id = comicVineIssueId(book); !id.isEmpty())
+ return "cv:" + id;
+ if (book.series.trimmed().isEmpty() && book.number.trimmed().isEmpty())
+ return { };
+ return QStringList { book.series.trimmed().toCaseFolded(), book.number.trimmed().toCaseFolded(), book.volume.trimmed().toCaseFolded(), book.year.trimmed() }.join(QChar(0x1F));
+}
+
+}
+
+Placeholder placeholderFromCblBook(const CblBook &book)
+{
+ Placeholder placeholder;
+ placeholder.series = text(book.series);
+ placeholder.number = text(book.number);
+ placeholder.volume = text(book.volume);
+ placeholder.format = text(book.format);
+ placeholder.date = date(book);
+ placeholder.comicVineID = text(comicVineIssueId(book));
+ return placeholder;
+}
+
+QString cblListName(const CblReadingList &list, const QString &filePath)
+{
+ const auto name = list.name.trimmed();
+ return name.isEmpty() ? QFileInfo(filePath).completeBaseName() : name;
+}
+
+std::optional importCblReadingList(const QString &name, const CblReadingList &list, const QList> &choices, QSqlDatabase &db)
+{
+ const auto &books = list.books;
+ if (books.size() != choices.size())
+ return std::nullopt;
+ const auto sourceFormat = list.format == CblReadingList::Json ? QStringLiteral("json") : QStringLiteral("xml");
+
+ db.transaction();
+ const auto fail = [&db](const QSqlQuery &query) -> std::optional {
+ QLOG_ERROR() << "Unable to import a CBL reading list:" << query.lastError().text();
+ db.rollback();
+ return std::nullopt;
+ };
+
+ QSqlQuery readingList(db);
+ readingList.prepare("INSERT INTO reading_list (name) VALUES (:name)");
+ readingList.bindValue(":name", name);
+ if (!readingList.exec())
+ return fail(readingList);
+ const auto readingListId = ReadingListId(readingList.lastInsertId().toULongLong());
+
+ QSqlQuery entry(db);
+ entry.prepare("INSERT INTO reading_list_entry (reading_list_id, ordering, comic_info_id, placeholder_id, source, source_format) "
+ "VALUES (:reading_list_id, :ordering, :comic_info_id, :placeholder_id, :source, :source_format)");
+
+ QHash newPlaceholders;
+ for (qsizetype i = 0; i < books.size(); i++) {
+ QVariant comicInfoId;
+ QVariant placeholderId;
+ if (const auto &choice = choices.at(i)) {
+ std::visit(overloaded {
+ [&](const CblLibraryComic &comic) { comicInfoId = comic.id.value(); },
+ [&](const CblLibraryPlaceholder &placeholder) { placeholderId = placeholder.id.value(); },
+ },
+ *choice);
+ } else {
+ const auto key = sameIssueKey(books.at(i));
+ std::optional created;
+ if (!key.isEmpty() && newPlaceholders.contains(key)) {
+ created = newPlaceholders.value(key);
+ } else {
+ created = DBHelper::insertPlaceholder(placeholderFromCblBook(books.at(i)), db);
+ if (!created) {
+ db.rollback();
+ return std::nullopt;
+ }
+ if (!key.isEmpty())
+ newPlaceholders.insert(key, *created);
+ }
+ placeholderId = created->value();
+ }
+
+ entry.bindValue(":reading_list_id", readingListId.value());
+ entry.bindValue(":ordering", i);
+ entry.bindValue(":comic_info_id", comicInfoId);
+ entry.bindValue(":placeholder_id", placeholderId);
+ const bool keepSource = !books.at(i).source.isEmpty();
+ entry.bindValue(":source", keepSource ? QVariant(books.at(i).source) : QVariant());
+ entry.bindValue(":source_format", keepSource ? QVariant(sourceFormat) : QVariant());
+ if (!entry.exec())
+ return fail(entry);
+ }
+
+ if (!db.commit())
+ return fail(readingList);
+ return readingListId;
+}
diff --git a/YACReaderLibrary/cbl/cbl_importer.h b/YACReaderLibrary/cbl/cbl_importer.h
new file mode 100644
index 000000000..92837f637
--- /dev/null
+++ b/YACReaderLibrary/cbl/cbl_importer.h
@@ -0,0 +1,29 @@
+#ifndef CBL_IMPORTER_H
+#define CBL_IMPORTER_H
+
+#include "cbl_matcher.h"
+#include "cbl_reader.h"
+#include "library_ids.h"
+#include "placeholder.h"
+
+#include
+#include
+
+#include
+
+class QSqlDatabase;
+
+// the placeholder of a book that matched nothing: series, number, volume, format, the date (cover date, else a
+// year alone as "//yyyy"), and the Comic Vine issue id; the other provider ids stay in the source of its entries
+Placeholder placeholderFromCblBook(const CblBook &book);
+
+// the name in the file, or else the file name without its extension
+QString cblListName(const CblReadingList &list, const QString &filePath);
+
+// creates a top-level reading list `name` with one entry per book of `list`, in order, in one transaction; `choices[i]`
+// is what book i refers to. A book without a choice gets a new placeholder, which the books of this import that are the
+// same issue share (the same Comic Vine id, or else the same series, number, volume, and year). Every entry keeps the
+// source of its book. Nothing is written when any step fails.
+std::optional importCblReadingList(const QString &name, const CblReadingList &list, const QList> &choices, QSqlDatabase &db);
+
+#endif
diff --git a/YACReaderLibrary/cbl/cbl_list_match.cpp b/YACReaderLibrary/cbl/cbl_list_match.cpp
new file mode 100644
index 000000000..8e5868a00
--- /dev/null
+++ b/YACReaderLibrary/cbl/cbl_list_match.cpp
@@ -0,0 +1,173 @@
+#include "cbl_list_match.h"
+
+#include "QsLog.h"
+#include "cbl_importer.h"
+#include "db_helper.h"
+
+#include
+#include
+#include
+#include
+
+namespace {
+
+// the entry as it is now
+struct StoredEntry {
+ QVariant comicInfoId;
+ QVariant placeholderId;
+ bool hasFile = false;
+ std::optional source;
+};
+
+std::optional loadEntry(ListItemId id, QSqlDatabase &db)
+{
+ const bool label = id.listKind == ListKind::Label;
+ QSqlQuery query(db);
+ query.prepare(QString("SELECT e.comic_info_id, e.placeholder_id, EXISTS (SELECT 1 FROM comic WHERE comicInfoId = e.comic_info_id), %1 FROM %2 e WHERE e.id = :id")
+ .arg(label ? "NULL, NULL" : "e.source, e.source_format", label ? "label_entry" : "reading_list_entry"));
+ query.bindValue(":id", id.id);
+ if (!query.exec()) {
+ QLOG_ERROR() << "Unable to read a list entry:" << query.lastError().text();
+ return std::nullopt;
+ }
+ if (!query.next())
+ return std::nullopt;
+
+ StoredEntry entry;
+ entry.comicInfoId = query.value(0);
+ entry.placeholderId = query.value(1);
+ entry.hasFile = query.value(2).toBool();
+ if (!query.value(3).isNull())
+ entry.source = EntrySource { query.value(3).toString(), query.value(4).toString() };
+ return entry;
+}
+
+bool makeMissing(ListItemId id, const StoredEntry &entry, QSqlDatabase &db)
+{
+ Placeholder placeholder;
+ const auto book = entry.source ? readCblBook(entry.source->text, entry.source->format) : std::nullopt;
+ if (book) {
+ placeholder = placeholderFromCblBook(*book);
+ } else {
+ const auto info = DBHelper::loadComicInfo(ComicInfoId(entry.comicInfoId.toULongLong()), db);
+ if (!info)
+ return false;
+ static_cast(placeholder) = *info;
+ }
+
+ const auto placeholderId = DBHelper::insertPlaceholder(placeholder, db);
+ if (!placeholderId)
+ return false;
+
+ QSqlQuery update(db);
+ update.prepare(QString("UPDATE %1 SET comic_info_id = NULL, placeholder_id = :placeholder_id, last_known_name = NULL, last_known_path = NULL WHERE id = :id")
+ .arg(id.listKind == ListKind::Label ? "label_entry" : "reading_list_entry"));
+ update.bindValue(":placeholder_id", placeholderId->value());
+ update.bindValue(":id", id.id);
+ if (!update.exec() || update.numRowsAffected() != 1) {
+ QLOG_ERROR() << "Unable to make an entry missing:" << update.lastError().text();
+ return false;
+ }
+ return true;
+}
+
+}
+
+CblListEntry cblListEntry(const ComicRecord &record, const ListEntry &entry)
+{
+ CblListEntry result;
+ result.id = entry.id;
+ result.listId = entry.listId;
+
+ const auto &metadata = recordMetadata(record);
+ if (const auto *placeholder = recordPlaceholder(record)) {
+ CblLibraryPlaceholder current;
+ static_cast(current) = cblLibraryMetadata(metadata);
+ current.id = placeholder->id;
+ current.coverKey = placeholder->coverKey.value_or(QString());
+ result.current = current;
+ } else if (const auto *info = recordInfo(record)) {
+ CblLibraryComic current;
+ static_cast(current) = cblLibraryMetadata(metadata);
+ current.id = info->id;
+ current.hash = info->hash;
+ if (const auto *comic = recordComic(record)) {
+ current.hasFile = true;
+ current.path = comic->file.path;
+ }
+ result.current = current;
+ }
+
+ const auto book = entry.source ? readCblBook(entry.source->text, entry.source->format) : std::nullopt;
+ result.book = book.value_or(cblBookOf(metadata));
+ return result;
+}
+
+CblExportBook cblExportBook(const ComicRecord &record, const ListEntry &entry)
+{
+ CblExportBook result;
+ result.book = cblListEntry(record, entry).book;
+ if (entry.source) {
+ result.source = entry.source->text;
+ result.sourceFormat = entry.source->format;
+ }
+ return result;
+}
+
+CblBook cblBookOf(const DescriptiveComicMetadata &metadata)
+{
+ CblBook book;
+ book.series = metadata.series.toString();
+ book.number = metadata.number.toString();
+ book.volume = metadata.volume.toString();
+ book.format = metadata.format.toString();
+ if (const auto year = cblYearOfDate(metadata.date.toString()); year > 0)
+ book.year = QString::number(year);
+ // a full d/m/yyyy date as yyyy-mm-dd
+ if (const auto parts = metadata.date.toString().split('/'); parts.size() == 3) {
+ if (const QDate date(parts[2].toInt(), parts[1].toInt(), parts[0].toInt()); date.isValid())
+ book.coverDate = date.toString(Qt::ISODate);
+ }
+ if (const auto issueId = metadata.comicVineID.toString().trimmed(); !issueId.isEmpty())
+ book.databases.append({ QStringLiteral("cv"), QString(), issueId });
+ return book;
+}
+
+CblLibraryMetadata cblLibraryMetadata(const DescriptiveComicMetadata &metadata)
+{
+ CblLibraryMetadata result;
+ result.series = metadata.series.toString();
+ result.number = metadata.number.toString();
+ result.volume = metadata.volume.toString();
+ result.date = metadata.date.toString();
+ result.format = metadata.format.toString();
+ result.comicVineId = metadata.comicVineID.toString();
+ return result;
+}
+
+bool applyCblListChanges(const QList &changes, const QString &libraryDataPath, QSqlDatabase &db, ComicMatchFiles &files)
+{
+ for (const auto &change : changes) {
+ const auto entry = loadEntry(change.entry, db);
+ if (!entry)
+ return false;
+
+ bool applied = true;
+ if (change.comic) {
+ const auto comicInfoId = *change.comic;
+ if (!entry->placeholderId.isNull())
+ applied = matchPlaceholderToComic(PlaceholderId(entry->placeholderId.toULongLong()), comicInfoId, libraryDataPath, db, files);
+ else if (entry->comicInfoId.toULongLong() == comicInfoId.value())
+ continue;
+ else if (!entry->hasFile)
+ applied = matchDeletedComicToComic(ComicInfoId(entry->comicInfoId.toULongLong()), comicInfoId, db, files);
+ else
+ applied = giveComicToEntry(change.entry, comicInfoId, db);
+ } else if (entry->placeholderId.isNull() && entry->hasFile) {
+ applied = makeMissing(change.entry, *entry, db);
+ }
+ if (!applied)
+ return false;
+ }
+ return true;
+}
diff --git a/YACReaderLibrary/cbl/cbl_list_match.h b/YACReaderLibrary/cbl/cbl_list_match.h
new file mode 100644
index 000000000..172349061
--- /dev/null
+++ b/YACReaderLibrary/cbl/cbl_list_match.h
@@ -0,0 +1,46 @@
+#ifndef CBL_LIST_MATCH_H
+#define CBL_LIST_MATCH_H
+
+#include "cbl_matcher.h"
+#include "cbl_reader.h"
+#include "cbl_writer.h"
+#include "comic_record.h"
+#include "library_comic_match.h"
+
+#include
+
+#include
+
+class QSqlDatabase;
+
+// an entry of a reading list or label in the match view
+struct CblListEntry {
+ ListItemId id { };
+ qulonglong listId = 0;
+ // what it refers to when the view opens: a comic (with or without a file) or a placeholder
+ CblCandidate current;
+ // its CBL book, else the values of what it refers to
+ CblBook book;
+};
+
+CblListEntry cblListEntry(const ComicRecord &record, const ListEntry &entry);
+// an entry of a reading list in an export: its book and its source
+CblExportBook cblExportBook(const ComicRecord &record, const ListEntry &entry);
+// the values of a comic or missing entry as a book, for matching and the views
+CblBook cblBookOf(const DescriptiveComicMetadata &metadata);
+CblLibraryMetadata cblLibraryMetadata(const DescriptiveComicMetadata &metadata);
+
+// what Apply does to one entry
+struct CblListChange {
+ ListItemId entry { };
+ // the comic with a file that it gets; nothing makes it missing
+ std::optional comic;
+};
+
+// In order, each change uses what its entry refers to when it runs: a missing entry or deleted comic is matched (every
+// entry of it, in every list), a comic entry only changes itself, and "make missing" gives a comic entry a new
+// placeholder from its CBL source, else from the comic's metadata. No transaction: the caller runs it in its own.
+// False when an entry is gone or a write fails.
+bool applyCblListChanges(const QList &changes, const QString &libraryDataPath, QSqlDatabase &db, ComicMatchFiles &files);
+
+#endif
diff --git a/YACReaderLibrary/cbl/cbl_match_comic_dialog.cpp b/YACReaderLibrary/cbl/cbl_match_comic_dialog.cpp
new file mode 100644
index 000000000..3879b8064
--- /dev/null
+++ b/YACReaderLibrary/cbl/cbl_match_comic_dialog.cpp
@@ -0,0 +1,107 @@
+#include "cbl_match_comic_dialog.h"
+
+#include "cbl_entry_detail.h"
+#include "cbl_find_comic_page.h"
+#include "cbl_list_match.h"
+#include "data_base_management.h"
+#include "descriptive_comic_metadata.h"
+#include "yacreader_global.h"
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+namespace {
+
+struct LoadedLibrary {
+ bool error = false;
+ QList folders;
+ QString startFolderPath;
+};
+
+LoadedLibrary loadLibrary(const CblBook &book, const QString &libraryPath)
+{
+ LoadedLibrary result;
+ QString connectionName;
+ {
+ auto db = DataBaseManagement::loadDatabase(YACReader::LibraryPaths::libraryDataPath(libraryPath));
+ connectionName = db.connectionName();
+
+ const auto comics = db.isOpen() ? CblMatcher::loadLibraryComics(db) : std::nullopt;
+ const auto folders = db.isOpen() ? CblFindComicPage::loadFolders(db) : std::nullopt;
+ if (!comics || !folders) {
+ result.error = true;
+ } else {
+ result.folders = *folders;
+ // only comics with a file can be chosen
+ QList withFile;
+ std::copy_if(comics->cbegin(), comics->cend(), std::back_inserter(withFile), [](const CblLibraryComic &comic) { return comic.hasFile; });
+ const CblMatcher matcher(withFile);
+ CblImportEntry entry;
+ entry.book = book;
+ entry.match = matcher.match(book);
+ result.startFolderPath = CblFindComicPage::startFolderPath(entry, matcher);
+ }
+ }
+ if (!connectionName.isEmpty())
+ QSqlDatabase::removeDatabase(connectionName);
+ return result;
+}
+
+}
+
+CblMatchComicDialog::CblMatchComicDialog(const DescriptiveComicMetadata &metadata, const QString &libraryPath, std::optional folderId, QWidget *parent)
+ : QDialog(parent)
+{
+ setWindowTitle(tr("Match to library comic"));
+ resize(960, 600);
+
+ messageLabel = new QLabel(tr("Loading the library..."));
+ messageLabel->setAlignment(Qt::AlignCenter);
+ messageLabel->setWordWrap(true);
+
+ findPage = new CblFindComicPage(libraryPath, CblFindComicPage::Mode::OneEntry);
+ findPage->setWarningColor(cblWarningColor(palette()));
+ connect(findPage, &CblFindComicPage::backRequested, this, &QDialog::reject);
+ connect(findPage, &CblFindComicPage::comicChosen, this, [this](const CblLibraryComic &comic) {
+ chosen = comic;
+ accept();
+ });
+
+ pages = new QStackedWidget;
+ pages->addWidget(messageLabel);
+ pages->addWidget(findPage);
+
+ auto layout = new QVBoxLayout(this);
+ layout->addWidget(pages);
+
+ const auto book = cblBookOf(metadata);
+ auto result = std::make_shared();
+ auto thread = QThread::create([result, book, libraryPath] { *result = loadLibrary(book, libraryPath); });
+ connect(thread, &QThread::finished, thread, &QObject::deleteLater);
+ connect(thread, &QThread::finished, this, [this, result, book, folderId] {
+ if (result->error) {
+ messageLabel->setText(tr("The library cannot be read."));
+ return;
+ }
+ CblImportEntry entry;
+ entry.book = book;
+ findPage->setFolders(result->folders);
+ if (folderId)
+ findPage->limitToFolder(*folderId);
+ findPage->showEntry(entry, 0, 1, CblImportStatus::Missing, result->startFolderPath, -1, 0, false, false);
+ pages->setCurrentWidget(findPage);
+ });
+ thread->start();
+}
+
+std::optional CblMatchComicDialog::chosenComic() const
+{
+ return chosen;
+}
diff --git a/YACReaderLibrary/cbl/cbl_match_comic_dialog.h b/YACReaderLibrary/cbl/cbl_match_comic_dialog.h
new file mode 100644
index 000000000..579a78b9d
--- /dev/null
+++ b/YACReaderLibrary/cbl/cbl_match_comic_dialog.h
@@ -0,0 +1,34 @@
+#ifndef CBL_MATCH_COMIC_DIALOG_H
+#define CBL_MATCH_COMIC_DIALOG_H
+
+#include "cbl_matcher.h"
+
+#include
+
+#include
+
+class CblFindComicPage;
+class QLabel;
+class QStackedWidget;
+struct DescriptiveComicMetadata;
+
+// chooses the library comic of one missing entry or deleted comic, with the Find comic page of the import; with a
+// folder, only among the comics of that folder
+class CblMatchComicDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ CblMatchComicDialog(const DescriptiveComicMetadata &metadata, const QString &libraryPath, std::optional folderId = std::nullopt, QWidget *parent = nullptr);
+
+ // set when the dialog is accepted
+ std::optional chosenComic() const;
+
+private:
+ QStackedWidget *pages;
+ QLabel *messageLabel;
+ CblFindComicPage *findPage;
+ std::optional chosen;
+};
+
+#endif
diff --git a/YACReaderLibrary/cbl/cbl_matcher.cpp b/YACReaderLibrary/cbl/cbl_matcher.cpp
new file mode 100644
index 000000000..e20c77041
--- /dev/null
+++ b/YACReaderLibrary/cbl/cbl_matcher.cpp
@@ -0,0 +1,397 @@
+#include "cbl_matcher.h"
+
+#include "QsLog.h"
+#include "folder_placeholder_match.h"
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+namespace {
+
+// "v1", "Vol. 2", "Volume 3" inside a series name
+const QRegularExpression &volumeInName()
+{
+ static const QRegularExpression expression(R"(\bv(ol(ume)?)?\.?\s?\d+\b\s*)", QRegularExpression::CaseInsensitiveOption);
+ return expression;
+}
+
+// "Batman (2016)": Comic Vine names a volume by its start year
+const QRegularExpression &yearInName()
+{
+ static const QRegularExpression expression(R"(^(.*\S)\s*\((\d{4})\)\s*$)");
+ return expression;
+}
+
+QString sameSeries(const QString &series)
+{
+ return series.simplified().toCaseFolded();
+}
+
+QString seriesWithoutVolume(const QString &series)
+{
+ auto result = series;
+ result.remove(volumeInName());
+ return sameSeries(result);
+}
+
+// letters and digits of any script, without the words "the" and "and"; only Latin letters lose their accents,
+// because in other scripts a mark can make another letter (ボ and ホ, й and и)
+QString lettersAndDigits(const QString &series)
+{
+ const auto decomposed = seriesWithoutVolume(series).normalized(QString::NormalizationForm_KD);
+ QString result;
+ QString word;
+ const auto endWord = [&] {
+ if (word != QLatin1StringView("the") && word != QLatin1StringView("and"))
+ result += word;
+ word.clear();
+ };
+ auto latinBase = false;
+ for (const auto c : decomposed) {
+ if (c.isMark()) {
+ if (!latinBase)
+ word += c;
+ continue;
+ }
+ latinBase = c.script() == QChar::Script_Latin;
+ if (c.isLetterOrNumber())
+ word += c;
+ else
+ endWord();
+ }
+ endWord();
+ return result;
+}
+
+// numbers are text; only the case and leading zeros do not count
+QString number(const QString &number)
+{
+ static const QRegularExpression leadingZeros(R"(^(-?)0+(?=\d))");
+ auto result = number.trimmed().toCaseFolded();
+ result.replace(leadingZeros, QStringLiteral("\\1"));
+ return result;
+}
+
+int yearOfDate(const QString &date)
+{
+ const auto year = date.section('/', -1).trimmed().toInt();
+ return year > 0 ? year : 0;
+}
+
+int bookYear(const CblBook &book)
+{
+ auto year = book.year.toInt();
+ if (year <= 0)
+ year = book.coverDate.section('-', 0, 0).toInt();
+ return year > 0 ? year : 0;
+}
+
+// the volume, or the year in a "Series (Year)" name
+QString effectiveVolume(const CblLibraryMetadata &metadata)
+{
+ if (!metadata.volume.trimmed().isEmpty())
+ return metadata.volume.trimmed();
+ const auto year = yearInName().match(metadata.series);
+ return year.hasMatch() ? year.captured(2) : QString();
+}
+
+QString key(const QString &series, const QString &issueNumber)
+{
+ return series + QChar(0x1F) + number(issueNumber);
+}
+
+// the keys of a series at a name level; "Series (Year)" gives one key per known year
+QStringList seriesKeys(CblMatchLevel level, const QString &series, const QString &volume)
+{
+ switch (level) {
+ case CblMatchLevel::SameSeries:
+ return { sameSeries(series) };
+ case CblMatchLevel::SeriesWithoutVolume:
+ return { seriesWithoutVolume(series) };
+ case CblMatchLevel::LettersAndDigits:
+ return { lettersAndDigits(series) };
+ case CblMatchLevel::SeriesWithYear: {
+ QStringList keys;
+ const auto year = yearInName().match(series);
+ if (year.hasMatch())
+ keys.append(lettersAndDigits(year.captured(1)) + QChar(0x1F) + year.captured(2));
+ if (!volume.trimmed().isEmpty())
+ keys.append(lettersAndDigits(series) + QChar(0x1F) + volume.trimmed().toCaseFolded());
+ return keys;
+ }
+ case CblMatchLevel::ComicVineId:
+ break;
+ }
+ return { };
+}
+
+constexpr CblMatchLevel nameLevels[] = { CblMatchLevel::SameSeries, CblMatchLevel::SeriesWithoutVolume, CblMatchLevel::LettersAndDigits, CblMatchLevel::SeriesWithYear };
+
+bool isComicVine(const CblDatabaseIds &ids)
+{
+ return ids.name.compare(QLatin1StringView("cv"), Qt::CaseInsensitive) == 0 || ids.name.compare(QLatin1StringView("comicvine"), Qt::CaseInsensitive) == 0;
+}
+
+template
+CblMatcher::Index buildIndex(const QList &items)
+{
+ CblMatcher::Index index;
+ for (qsizetype i = 0; i < items.size(); i++) {
+ const auto &item = items.at(i);
+ if (const auto id = item.comicVineId.trimmed(); !id.isEmpty())
+ index.byComicVineId[id].append(i);
+ if (item.series.trimmed().isEmpty())
+ continue;
+ for (int level = 0; level < 4; level++) {
+ for (const auto &seriesKey : seriesKeys(nameLevels[level], item.series, item.volume)) {
+ auto &positions = index.byName[level][key(seriesKey, item.number)];
+ if (!positions.contains(i))
+ positions.append(i);
+ if (level < 3)
+ index.bySeries[level][seriesKey].append(i);
+ }
+ }
+ }
+ return index;
+}
+
+// keeps the items that pass, unless none does
+template
+void narrow(QList &items, const std::function &passes)
+{
+ QList passing;
+ for (const auto &item : std::as_const(items)) {
+ if (passes(item))
+ passing.append(item);
+ }
+ if (!passing.isEmpty())
+ items = passing;
+}
+
+// a comic with a file wins over a deleted one
+void preferFiles(QList &comics)
+{
+ narrow(comics, [](const CblLibraryComic &comic) { return comic.hasFile; });
+}
+
+void preferFiles(QList &)
+{
+}
+
+template
+void breakTies(const CblBook &book, QList &items)
+{
+ if (const auto volume = book.volume.trimmed().toCaseFolded(); !volume.isEmpty())
+ narrow(items, [&](const Item &item) { return effectiveVolume(item).toCaseFolded() == volume; });
+ if (const auto year = bookYear(book); year > 0)
+ narrow(items, [&](const Item &item) { const auto itemYear = yearOfDate(item.date); return itemYear > 0 && std::abs(itemYear - year) <= 1; });
+ if (const auto format = book.format.trimmed(); !format.isEmpty())
+ narrow(items, [&](const Item &item) { return item.format.trimmed().compare(format, Qt::CaseInsensitive) == 0; });
+ preferFiles(items);
+}
+
+// the volume is the same or the year is within one, and no known volume or year disagrees
+bool agrees(const CblBook &book, const CblLibraryMetadata &metadata)
+{
+ const auto bookVolume = book.volume.trimmed();
+ const auto itemVolume = effectiveVolume(metadata);
+ const auto volumeKnown = !bookVolume.isEmpty() && !itemVolume.isEmpty();
+ const auto volumeSame = volumeKnown && bookVolume.compare(itemVolume, Qt::CaseInsensitive) == 0;
+
+ const auto year = bookYear(book);
+ const auto itemYear = yearOfDate(metadata.date);
+ const auto yearKnown = year > 0 && itemYear > 0;
+ const auto yearClose = yearKnown && std::abs(year - itemYear) <= 1;
+
+ return (volumeSame || yearClose) && (!volumeKnown || volumeSame) && (!yearKnown || yearClose);
+}
+
+// the match of `book` among `items`, or nothing when no item fits it
+template
+std::optional matchIn(const CblBook &book, const QList &items, const CblMatcher::Index &index)
+{
+ const auto result = [&](const QList &found, CblMatchLevel level, bool needsAgreement) {
+ QList fitting;
+ for (const auto position : found)
+ fitting.append(items.at(position));
+ if (needsAgreement)
+ breakTies(book, fitting);
+ else
+ preferFiles(fitting);
+
+ CblBookMatch match;
+ match.level = level;
+ for (const auto &item : std::as_const(fitting))
+ match.candidates.append(item);
+ if (fitting.size() == 1 && (!needsAgreement || agrees(book, fitting.first())))
+ match.accepted = fitting.first();
+ return match;
+ };
+
+ QList found;
+ for (const auto &ids : book.databases) {
+ if (!isComicVine(ids) || ids.issueId.isEmpty())
+ continue;
+ for (const auto position : index.byComicVineId.value(ids.issueId)) {
+ if (!found.contains(position))
+ found.append(position);
+ }
+ }
+ if (!found.isEmpty())
+ return result(found, CblMatchLevel::ComicVineId, false);
+
+ if (book.series.trimmed().isEmpty())
+ return std::nullopt;
+
+ for (int level = 0; level < 4; level++) {
+ for (const auto &seriesKey : seriesKeys(nameLevels[level], book.series, book.volume)) {
+ for (const auto position : index.byName[level].value(key(seriesKey, book.number))) {
+ if (!found.contains(position))
+ found.append(position);
+ }
+ }
+ if (!found.isEmpty())
+ return result(found, nameLevels[level], true);
+ }
+ return std::nullopt;
+}
+
+}
+
+CblMatcher::CblMatcher(const QList &comics, const QList &placeholders)
+ : comics(comics), placeholders(placeholders), comicIndex(buildIndex(comics)), placeholderIndex(buildIndex(placeholders))
+{
+}
+
+QList CblMatcher::closestSeries(const CblBook &book) const
+{
+ if (book.series.trimmed().isEmpty())
+ return { };
+
+ for (int level = 0; level < 3; level++) {
+ QList found;
+ for (const auto &seriesKey : seriesKeys(nameLevels[level], book.series, book.volume)) {
+ for (const auto position : comicIndex.bySeries[level].value(seriesKey))
+ found.append(comics.at(position));
+ }
+ if (found.isEmpty())
+ continue;
+ if (const auto volume = book.volume.trimmed().toCaseFolded(); !volume.isEmpty())
+ narrow(found, [&](const CblLibraryComic &comic) { return effectiveVolume(comic).toCaseFolded() == volume; });
+ return found;
+ }
+ return { };
+}
+
+// an accepted comic wins; a comic that is only a candidate does not hide a placeholder of the same issue
+CblBookMatch CblMatcher::match(const CblBook &book) const
+{
+ const auto comicMatch = matchIn(book, comics, comicIndex);
+ if (comicMatch && comicMatch->accepted)
+ return *comicMatch;
+
+ const auto placeholderMatch = matchIn(book, placeholders, placeholderIndex);
+ if (!comicMatch)
+ return placeholderMatch.value_or(CblBookMatch { });
+ if (!placeholderMatch)
+ return *comicMatch;
+
+ auto result = *comicMatch;
+ result.candidates += placeholderMatch->candidates;
+ if (placeholderMatch->accepted) {
+ result.accepted = placeholderMatch->accepted;
+ result.level = placeholderMatch->level;
+ }
+ return result;
+}
+
+std::optional> CblMatcher::loadLibraryComics(QSqlDatabase &db)
+{
+ QSqlQuery query(db);
+ query.setForwardOnly(true);
+ if (!query.exec("SELECT ci.id, (SELECT c.path FROM comic c WHERE c.comicInfoId = ci.id ORDER BY c.id LIMIT 1), ci.series, ci.number, ci.volume, ci.date, ci.format, ci.comicVineID, ci.hash "
+ "FROM comic_info ci "
+ "WHERE COALESCE(ci.series, '') <> '' OR COALESCE(ci.comicVineID, '') <> ''")) {
+ QLOG_ERROR() << "Unable to load comics for CBL matching:" << query.lastError().text();
+ return std::nullopt;
+ }
+
+ QList comics;
+ while (query.next()) {
+ CblLibraryComic comic;
+ comic.id = ComicInfoId(query.value(0).toULongLong());
+ comic.hasFile = !query.value(1).isNull();
+ comic.path = query.value(1).toString();
+ comic.series = query.value(2).toString();
+ comic.number = query.value(3).toString();
+ comic.volume = query.value(4).toString();
+ comic.date = query.value(5).toString();
+ comic.format = query.value(6).toString();
+ comic.comicVineId = query.value(7).toString();
+ comic.hash = query.value(8).toString();
+ comics.append(comic);
+ }
+ return comics;
+}
+
+std::optional> CblMatcher::loadLibraryPlaceholders(QSqlDatabase &db)
+{
+ QSqlQuery query(db);
+ query.setForwardOnly(true);
+ if (!query.exec("SELECT p.id, p.series, p.number, p.volume, p.date, p.format, p.comicVineID, p.coverKey "
+ "FROM placeholder p "
+ "WHERE COALESCE(p.series, '') <> '' OR COALESCE(p.comicVineID, '') <> ''")) {
+ QLOG_ERROR() << "Unable to load placeholders for CBL matching:" << query.lastError().text();
+ return std::nullopt;
+ }
+
+ QList placeholders;
+ while (query.next()) {
+ CblLibraryPlaceholder placeholder;
+ placeholder.id = PlaceholderId(query.value(0).toULongLong());
+ placeholder.series = query.value(1).toString();
+ placeholder.number = query.value(2).toString();
+ placeholder.volume = query.value(3).toString();
+ placeholder.date = query.value(4).toString();
+ placeholder.format = query.value(5).toString();
+ placeholder.comicVineId = query.value(6).toString();
+ placeholder.coverKey = query.value(7).toString();
+ placeholders.append(placeholder);
+ }
+ return placeholders;
+}
+
+QString cblComicVineIssueId(const CblBook &book)
+{
+ for (const auto &ids : book.databases) {
+ if (isComicVine(ids) && !ids.issueId.isEmpty())
+ return ids.issueId;
+ }
+ return { };
+}
+
+int cblBookYear(const CblBook &book)
+{
+ return bookYear(book);
+}
+
+int cblYearOfDate(const QString &date)
+{
+ return yearOfDate(date);
+}
+
+QString cblEffectiveVolume(const CblLibraryMetadata &metadata)
+{
+ return effectiveVolume(metadata);
+}
+
+bool cblSameNumber(const QString &a, const QString &b)
+{
+ return sameIssueNumber(a, b);
+}
diff --git a/YACReaderLibrary/cbl/cbl_matcher.h b/YACReaderLibrary/cbl/cbl_matcher.h
new file mode 100644
index 000000000..a565d23c1
--- /dev/null
+++ b/YACReaderLibrary/cbl/cbl_matcher.h
@@ -0,0 +1,107 @@
+#ifndef CBL_MATCHER_H
+#define CBL_MATCHER_H
+
+#include "cbl_reader.h"
+#include "library_ids.h"
+
+#include
+#include
+#include
+
+#include
+#include
+
+class QSqlDatabase;
+
+// the fields matching reads, from a comic_info or a placeholder
+struct CblLibraryMetadata {
+ QString series;
+ QString number;
+ QString volume;
+ QString date;
+ QString format;
+ QString comicVineId;
+
+ friend bool operator==(const CblLibraryMetadata &, const CblLibraryMetadata &) = default;
+};
+
+// deleted comics (no file) are included
+struct CblLibraryComic : CblLibraryMetadata {
+ ComicInfoId id;
+ bool hasFile = false;
+ // the path of one of its comics (the lowest id); empty when it has no file
+ QString path;
+ QString hash;
+
+ friend bool operator==(const CblLibraryComic &, const CblLibraryComic &) = default;
+};
+
+struct CblLibraryPlaceholder : CblLibraryMetadata {
+ PlaceholderId id;
+ QString coverKey;
+
+ friend bool operator==(const CblLibraryPlaceholder &, const CblLibraryPlaceholder &) = default;
+};
+
+using CblCandidate = std::variant;
+
+// from the most to the least certain; the first level that finds a candidate is used
+enum class CblMatchLevel {
+ ComicVineId,
+ SameSeries,
+ SeriesWithoutVolume,
+ LettersAndDigits,
+ SeriesWithYear,
+};
+
+struct CblBookMatch {
+ // what the import uses without asking the user
+ std::optional accepted;
+ // what fits the book at `level`, best first; the user chooses when nothing is accepted
+ QList candidates;
+ std::optional level;
+};
+
+// an accepted comic wins; else an accepted placeholder; the candidates list the comics, then the placeholders
+class CblMatcher
+{
+public:
+ CblMatcher(const QList &comics, const QList &placeholders = { });
+
+ CblBookMatch match(const CblBook &book) const;
+ // the comics of the series that the name levels find for the book series with any number (the same series, then
+ // without the volume, then letters and digits); only those of the book's volume when there are any
+ QList