Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ Go to [this repository](https://github.com/ScanTailor-Advanced/scantailor-libs-b

**Windows – supported OS versions (issue #101):** Release builds follow upstream **Qt** and **toolchain** support policies; **legacy Windows** (for example Windows 7) is **not** exercised in this project’s CI and is **best-effort only**. You may need an **older tagged release** or a **self-built** binary against an older Qt/MSVC stack. Community reports (including compatibility tips) are welcome in the issue tracker.

**Linux – Wayland (issue #97):** If you see rendering issues (blank or corrupted windows) when running under Wayland, try starting the application with `QT_QPA_PLATFORM=xcb` to use the X11 compatibility layer.
**Linux – Wayland (issue #97):** On Qt5 builds, if `XDG_SESSION_TYPE` is `wayland` and `QT_QPA_PLATFORM` is not set, the application defaults to the X11 (`xcb`) platform plugin to avoid broken dialogs and painting. Set **`SCANTAILOR_NO_XCB_FALLBACK=1`** in the environment to keep native Wayland and, if needed, set `QT_QPA_PLATFORM=wayland` or `QT_QPA_PLATFORM=xcb` yourself.

**Linux – Flatpak / Flathub ([issue #105](https://github.com/ScanTailor-Advanced/scantailor-advanced/issues/105)):**

Expand Down
20 changes: 18 additions & 2 deletions src/app/MainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <QDir>
#include <QFileDialog>
#include <QFileSystemModel>
#include <QMenu>
#include <QMessageBox>
#include <QResource>
#include <QScrollBar>
Expand Down Expand Up @@ -301,6 +302,7 @@ MainWindow::MainWindow()
});

connect(actionFixDpi, SIGNAL(triggered(bool)), SLOT(fixDpiDialogRequested()));
connect(actionReverseTwoPageOrder, SIGNAL(triggered(bool)), SLOT(toggleTwoPageSpreadReadingOrder()));
connect(actionRelinking, SIGNAL(triggered(bool)), SLOT(showRelinkingDialog()));
#ifdef ENABLE_DEBUG_FEATURES
connect(actionDebug, SIGNAL(toggled(bool)), SLOT(debugToggled(bool)));
Expand Down Expand Up @@ -1016,7 +1018,8 @@ void MainWindow::pageContextMenuRequested(const PageInfo& pageInfo_, const QPoin
goToPage(pageInfo.id());
}

QMenu menu;
// Parent widget helps correct multi-monitor placement (issue #75).
QMenu menu(thumbView);

auto& iconProvider = IconProvider::getInstance();
QAction* insBefore = menu.addAction(iconProvider.getIcon("insert-before"), tr("Insert before ..."));
Expand All @@ -1036,12 +1039,24 @@ void MainWindow::pageContextMenuRequested(const PageInfo& pageInfo_, const QPoin
}
} // MainWindow::pageContextMenuRequested

void MainWindow::toggleTwoPageSpreadReadingOrder() {
if (!isProjectLoaded() || !m_pages) {
return;
}
const Qt::LayoutDirection nextDir
= (m_pages->layoutDirection() == Qt::LeftToRight) ? Qt::RightToLeft : Qt::LeftToRight;
m_pages->setLayoutDirection(nextDir);
m_outFileNameGen.setLayoutDirection(nextDir);
resetThumbSequence(currentPageOrderProvider(), ThumbnailSequence::KEEP_SELECTION);
invalidateAllThumbnails();
}

void MainWindow::pastLastPageContextMenuRequested(const QPoint& screenPos) {
if (!isProjectLoaded()) {
return;
}

QMenu menu;
QMenu menu(thumbView);
menu.addAction(IconProvider::getInstance().getIcon("insert-here"), tr("Insert here ..."));

if (menu.exec(screenPos)) {
Expand Down Expand Up @@ -1579,6 +1594,7 @@ void MainWindow::updateProjectActions() {
actionSaveProjectAs->setEnabled(loaded);
actionFixDpi->setEnabled(loaded);
actionRelinking->setEnabled(loaded);
actionReverseTwoPageOrder->setEnabled(loaded);
}

bool MainWindow::isBatchProcessingInProgress() const {
Expand Down
2 changes: 2 additions & 0 deletions src/app/MainWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ class MainWindow : public QMainWindow, private FilterUiInterface, private Ui::Ma

void pastLastPageContextMenuRequested(const QPoint& screenPos);

void toggleTwoPageSpreadReadingOrder();

void thumbViewFocusToggled(bool checked);

void thumbViewScrolled();
Expand Down
15 changes: 15 additions & 0 deletions src/app/MainWindow.ui
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
</widget>
<addaction name="actionFixDpi"/>
<addaction name="actionRelinking"/>
<addaction name="actionReverseTwoPageOrder"/>
<addaction name="separator"/>
<addaction name="actionDebug"/>
<addaction name="separator"/>
Expand Down Expand Up @@ -1214,6 +1215,20 @@ QToolButton:pressed {
<bool>false</bool>
</property>
</action>
<action name="actionReverseTwoPageOrder">
<property name="text">
<string>Reverse two-page spread order</string>
</property>
<property name="toolTip">
<string>Swap left/right page order for two-page scans (e.g. Japanese book reading order).</string>
</property>
<property name="shortcut">
<string notr="true"/>
</property>
<property name="autoRepeat">
<bool>false</bool>
</property>
</action>
<action name="actionSwitchFilter1">
<property name="text">
<string>Switch filter to orientation</string>
Expand Down
6 changes: 6 additions & 0 deletions src/app/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@

int main(int argc, char* argv[]) {
#if QT_VERSION_MAJOR == 5
// Issue #97: Qt5 on Wayland can corrupt dialogs; use X11 unless opted out.
if (!qEnvironmentVariableIsSet("SCANTAILOR_NO_XCB_FALLBACK")) {
if (qgetenv("XDG_SESSION_TYPE") == "wayland" && qEnvironmentVariableIsEmpty("QT_QPA_PLATFORM")) {
qputenv("QT_QPA_PLATFORM", QByteArrayLiteral("xcb"));
}
}
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
Expand Down
2 changes: 2 additions & 0 deletions src/core/OutputFileNameGenerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ class OutputFileNameGenerator {

void performRelinking(const AbstractRelinker& relinker);

void setLayoutDirection(Qt::LayoutDirection dir) { m_layoutDirection = dir; }

Qt::LayoutDirection layoutDirection() const { return m_layoutDirection; }

const QString& outDir() const { return m_outDir; }
Expand Down
8 changes: 8 additions & 0 deletions src/core/ProjectPages.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,14 @@ Qt::LayoutDirection ProjectPages::layoutDirection() const {
}
}

void ProjectPages::setLayoutDirection(const Qt::LayoutDirection dir) {
{
QMutexLocker locker(&m_mutex);
initSubPagesInOrder(dir);
}
emit modified();
}

void ProjectPages::initSubPagesInOrder(const Qt::LayoutDirection layoutDirection) {
if (layoutDirection == Qt::LeftToRight) {
m_subPagesInOrder[0] = PageId::LEFT_PAGE;
Expand Down
5 changes: 5 additions & 0 deletions src/core/ProjectPages.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ class ProjectPages : public QObject {

Qt::LayoutDirection layoutDirection() const;

/**
* \brief Swap logical order of left/right sub-pages for two-page layouts (issue #62).
*/
void setLayoutDirection(Qt::LayoutDirection dir);

PageSequence toPageSequence(PageView view) const;

void listRelinkablePaths(const VirtualFunction<void, const RelinkablePath&>& sink) const;
Expand Down
15 changes: 9 additions & 6 deletions src/core/filters/deskew/ApplyDialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,25 +33,28 @@ ApplyDialog::ApplyDialog(QWidget* parent, const PageId& curPage, const PageSelec
ApplyDialog::~ApplyDialog() = default;

void ApplyDialog::onSubmit() {
const bool applyDeskew = applyDeskewCheckBox->isChecked();
const bool applyOblique = applyObliqueCheckBox->isChecked();

std::set<PageId> pages;
// thisPageRB is intentionally not handled.
if (allPagesRB->isChecked()) {
m_pages.selectAll().swap(pages);
emit appliedToAllPages(pages);
emit appliedToAllPages(pages, applyDeskew, applyOblique);
} else if (thisPageAndFollowersRB->isChecked()) {
m_pages.selectPagePlusFollowers(m_curPage).swap(pages);
emit appliedTo(pages);
emit appliedTo(pages, applyDeskew, applyOblique);
} else if (selectedPagesRB->isChecked()) {
emit appliedTo(m_selectedPages);
emit appliedTo(m_selectedPages, applyDeskew, applyOblique);
} else if (everyOtherRB->isChecked()) {
m_pages.selectEveryOther(m_curPage).swap(pages);
emit appliedTo(pages);
emit appliedTo(pages, applyDeskew, applyOblique);
} else if (thisEveryOtherRB->isChecked()) {
m_pages.selectThisPageAndFollowingEveryOther(m_curPage).swap(pages);
emit appliedTo(pages);
emit appliedTo(pages, applyDeskew, applyOblique);
} else if (everyOtherSelectedRB->isChecked()) {
m_pages.selectEveryOtherInSubsetFromPage(m_curPage, m_selectedPages).swap(pages);
emit appliedTo(pages);
emit appliedTo(pages, applyDeskew, applyOblique);
}
accept();
} // ApplyDialog::onSubmit
Expand Down
4 changes: 2 additions & 2 deletions src/core/filters/deskew/ApplyDialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ class ApplyDialog : public QDialog, private Ui::ApplyDialog {

signals:

void appliedTo(const std::set<PageId>& pages);
void appliedTo(const std::set<PageId>& pages, bool applyDeskew, bool applyOblique);

void appliedToAllPages(const std::set<PageId>& pages);
void appliedToAllPages(const std::set<PageId>& pages, bool applyDeskew, bool applyOblique);

private slots:

Expand Down
29 changes: 29 additions & 0 deletions src/core/filters/deskew/ApplyDialog.ui
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,35 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="applyPartsGroupBox">
<property name="title">
<string>Apply parameters</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_apply_parts">
<item>
<widget class="QCheckBox" name="applyDeskewCheckBox">
<property name="text">
<string>Deskew angle and mode</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="applyObliqueCheckBox">
<property name="text">
<string>Oblique angle and mode</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
Expand Down
4 changes: 3 additions & 1 deletion src/core/filters/deskew/CacheDrivenTask.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ void CacheDrivenTask::process(const PageInfo& pageInfo,
const ImageTransformation& xform) {
const Dependencies deps(xform.preCropArea(), xform.preRotation());
std::unique_ptr<Params> params(m_settings->getPageParams(pageInfo.id()));
if (!params || (!deps.matches(params->dependencies()) && (params->mode() == MODE_AUTO))) {
if (!params
|| (!deps.matches(params->dependencies())
&& ((params->mode() == MODE_AUTO) || (params->obliqueMode() == MODE_AUTO)))) {
if (auto* thumbCol = dynamic_cast<ThumbnailCollector*>(collector)) {
thumbCol->processThumbnail(std::unique_ptr<QGraphicsItem>(new IncompleteThumbnail(
thumbCol->thumbnailCache(), thumbCol->maxLogicalThumbSize(), pageInfo.imageId(), xform)));
Expand Down
Loading