Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
1074073
qi: Replace QApplication::desktop() with primaryScreen()
sjg20 Mar 8, 2026
213973d
qi: Replace buttonClicked(int) with idClicked(int)
sjg20 Mar 8, 2026
894cfa9
utils: Replace QRegExp with QRegularExpression
sjg20 Mar 8, 2026
3f9a9d9
maxview: Replace QRegExp with QRegularExpression
sjg20 Mar 8, 2026
14e08de
qscanner: Replace QRegExp with QRegularExpression
sjg20 Mar 8, 2026
fe2ada9
qi: Replace QRegExp with QRegularExpression in FileIOSupporter
sjg20 Mar 8, 2026
bbe42e2
pro: Add Qt6 poppler support
sjg20 Mar 8, 2026
ac6fcd4
qi: Replace QDesktopWidget with QScreen
sjg20 Mar 8, 2026
6940507
qt6: Remove stale qregexp.h includes
sjg20 Mar 8, 2026
2c8a9de
qt6: Remove unused QLinkedList include
sjg20 Mar 8, 2026
62698ce
qt6: Replace QMatrix with QTransform
sjg20 Mar 8, 2026
a9d4b82
qt6: Replace setMargin() with setContentsMargins()
sjg20 Mar 8, 2026
1c73427
qt6: Replace deprecated QDateTime time_t methods
sjg20 Mar 8, 2026
097e884
qt6: Wrap char[] in QString for QVariant conversion
sjg20 Mar 8, 2026
5b114da
qt6: Replace viewOptions() with initViewItemOption()
sjg20 Mar 8, 2026
f94172f
qt6: Replace QStringList forward declaration with include
sjg20 Mar 8, 2026
9666c76
qt6: Update Poppler API for unique_ptr returns
sjg20 Mar 8, 2026
5f495d4
qt6: Add statemachine module for QKeyEventTransition
sjg20 Mar 8, 2026
7ba7d18
qt6: Fix state-machine includes and UI connections
sjg20 Mar 8, 2026
368a6df
qt6: Remove unused qtextcodec.h include
sjg20 Mar 8, 2026
a5aeae3
qt6: Replace QPrinter enums with QPageLayout/QPageSize
sjg20 Mar 8, 2026
c7d3825
qt6: Cast qsizetype to int in sprintf() format
sjg20 Mar 8, 2026
24d9a68
qt6: Use operator| for key combinations
sjg20 Mar 9, 2026
83e6950
qt6: Fix deprecated QDropEvent and QAbstractItemView calls
sjg20 Mar 9, 2026
f38d2b4
qt6: Cast qsizetype to int/unsigned in format strings
sjg20 Mar 9, 2026
06a513b
qt6: Fix isTopLevel() and QTranslator::load() warnings
sjg20 Mar 9, 2026
e759db7
qt6: Replace deprecated QMessageBox overloads
sjg20 Mar 9, 2026
9c7429d
qt6: Guard initViewItemOption() with a version check
sjg20 Mar 9, 2026
e4c0ece
qt6: Adapt colours for dark mode support
sjg20 Mar 8, 2026
e736077
dark: Add inverted XPM icons for dark mode
sjg20 Mar 9, 2026
30ffa42
qt6: Add dark mode icon support
sjg20 Mar 9, 2026
918fa34
Manually edit some dark icons
sjg20 Mar 9, 2026
051f012
build: Fix GNUmakefile issues and clean up server.pro
sjg20 Mar 9, 2026
27f91f0
GNUmakefile: Add phony paperman target and dark-icons
sjg20 Mar 9, 2026
c2cdd5c
ci: Add a Qt6 build-and-test job
sjg20 Mar 9, 2026
9993d22
setup: Default to Qt6 and add --qt5 option
sjg20 Mar 9, 2026
09f380e
doc: Add Qt6 build instructions and CI documentation
sjg20 Mar 9, 2026
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
40 changes: 40 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,46 @@ jobs:
- name: Run parallel conversion test
run: scripts/test_parallel.sh

qt6:
name: Qt6 desktop app + server + C++ tests
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4

- name: Install Qt6/C++ dependencies
run: |
sudo apt-get update -qq
sudo apt-get install -y \
build-essential \
qmake6 qt6-base-dev qt6-base-dev-tools libqt6sql6-sqlite \
libpoppler-qt6-dev libpodofo-dev \
qt6-scxml-dev libqt6statemachine6 \
libtiff-dev libsane-dev libjpeg-dev zlib1g-dev \
imagemagick tesseract-ocr tesseract-ocr-eng \
python3-reportlab python3-pil python3-numpy \
poppler-utils

- name: Build desktop app
run: qmake6 paperman.pro -o Makefile.qt6 && make -f Makefile.qt6 -j$(nproc)

- name: Build server
run: |
qmake6 paperman-server.pro -o server.mk
echo '#define SERVER_BUILD_DATE "CI"' > builddate.h
make -f server.mk -j$(nproc)

- name: Generate test data
run: python3 scripts/make_test_files.py

- name: Run Qt unit tests
run: QT_QPA_PLATFORM=offscreen ./paperman -t

- name: Run page-fetch integration test
run: scripts/test_page_fetch.sh

- name: Run parallel conversion test
run: scripts/test_parallel.sh

flutter:
name: Flutter app + widget tests
runs-on: ubuntu-24.04
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ moc_*.cpp
moc_*.h
*.obj
/Makefile
/Makefile.qt6
/paperman
/paperman-server
/Makefile.server
Expand All @@ -41,3 +42,4 @@ doc/_build/
/test/files/
install-*
/server.mk
.stamp
19 changes: 17 additions & 2 deletions GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ BUILDDIR = $(DOCDIR)/_build

all: paperman paperman-server app docs

paperman:
$(MAKE) -f Makefile $@

dark-icons:
python3 scripts/invert_xpm.py

.PHONY: dark-icons paperman

# Targets handled here
.PHONY: builddate.h
builddate.h:
Expand Down Expand Up @@ -53,7 +61,8 @@ app: app-apk app-linux
rm -f $(DART_DEFINES)

GH_REMOTE ?= gh
DEB_VERSION := $(shell head -1 debian/changelog.in | sed -n 's/.*(\([^)]*\)).*/\1/p' | sed 's/VENDOR_VERSION//')
rparen := )
DEB_VERSION := $(shell head -1 debian/changelog.in | sed -n 's/.*(\([^$(rparen)]*\)).*/\1/p' | sed 's/VENDOR_VERSION//')
DEB_UPSTREAM := $(firstword $(subst -, ,$(DEB_VERSION)))
RELEASE_TAG = v$(DEB_UPSTREAM)
RELEASE_DEBS = $(wildcard ../release/all/paperman_$(DEB_VERSION)_*.deb)
Expand Down Expand Up @@ -119,6 +128,7 @@ app-publish: app-aab
app-upload: app-apk
rclone copy $(APP_APK) gdrive:apps/

server.mk: ;
-include server.mk

app-scp: app-apk app-scp-only
Expand Down Expand Up @@ -216,10 +226,15 @@ app-clean:
rm -f $(DART_DEFINES)

clean: app-clean docs-clean
$(MAKE) -f Makefile clean
-test -f Makefile && $(MAKE) -f Makefile clean
-test -f Makefile.server && $(MAKE) -f Makefile.server clean
rm -f paperman paperman-server builddate.h Makefile.server *.o moc_*.cpp moc_predefs.h

distclean: app-clean docs-clean
rm -f paperman paperman-server builddate.h *.o moc_*.cpp moc_predefs.h
rm -f Makefile Makefile.qt6 Makefile.server server.mk .qmake.stash
rm -rf .obj .moc .ui

docs-clean:
rm -rf $(BUILDDIR)

Expand Down
7 changes: 3 additions & 4 deletions desk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ X-Comment: On Debian GNU/Linux systems, the complete text of the GNU General
#include <QBitArray>
#include <QDateTime>
#include <QDebug>
#include <QLinkedList>
#include <QStandardPaths>
#include <QtGlobal>

Expand Down Expand Up @@ -830,7 +829,7 @@ err_info *Desk::checksum()
image_size = image.byteCount();
#endif
md5_buffer((const char *)image.bits(), image_size, md5);
sprintf(line, "%d %s %d %d %x %x %x %x\n", f->filename().length(),
sprintf(line, "%d %s %d %d %x %x %x %x\n", (int)f->filename().length(),
qPrintable(f->filename()), pagenum, image_size,
md5 [0], md5 [1], md5 [2], md5 [3]);
stream << line;
Expand Down Expand Up @@ -1841,7 +1840,7 @@ err_info *Desk::scan_file (QString dir_name, QFileInfo *fi,

// check for this file in the old list
of = find_file (old, fname);
if (of && (unsigned)of->time != fi->lastModified ().toTime_t ())
if (of && (unsigned)of->time != fi->lastModified ().toSecsSinceEpoch())
of = 0;

// if no match, create a new entry
Expand Down Expand Up @@ -1888,7 +1887,7 @@ err_info *Desk::scan_file (QString dir_name, QFileInfo *fi,

of->max = 0;
of->pixmap = 0;
of->time = fi->lastModified ().toTime_t ();
of->time = fi->lastModified ().toSecsSinceEpoch();
of->order = -1;
of->ipos = QPoint (0, 0);
}
Expand Down
3 changes: 1 addition & 2 deletions desktopdelegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -433,9 +433,8 @@ void Desktopdelegate::paint (QPainter *painter, const QStyleOptionViewItem &opti
painter->setFont (f);
if (measure.multiple)
{
painter->setPen (Qt::black);
style->drawItemText (painter, measure.pagenameRect, Qt::AlignHCenter,
style->standardPalette (), false, measure.pagename);
style->standardPalette (), false, measure.pagename, QPalette::WindowText);
// printf ("%s\n", measure.title.latin1 ());

#ifdef OUTLINE
Expand Down
4 changes: 2 additions & 2 deletions desktopmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ QVariant Desktopmodel::data(const QModelIndex &index, int role) const

case Role_message :
if (f->err ())
return f->err ()->errstr;
return QString(f->err ()->errstr);
else
{
QString str;
Expand Down Expand Up @@ -269,7 +269,7 @@ QVariant Desktopmodel::data(const QModelIndex &index, int role) const

case Role_error :
if (f->err ())
return f->err ()->errstr;
return QString(f->err ()->errstr);
break;
}

Expand Down
47 changes: 39 additions & 8 deletions desktopview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,14 @@ Desktopview::Desktopview (QWidget *parent)

connect (this, SIGNAL (indexesMoved (const QModelIndexList &)),
this, SLOT (slotIndexesMoved (const QModelIndexList &)));
setStyleSheet ("QListView { background : lightgray }");
QFont font = viewOptions().font;
setStyleSheet ("QListView { background : palette(mid); }");
QStyleOptionViewItem viewOpt;
#if QT_VERSION >= 0x060000
initViewItemOption(&viewOpt);
#else
viewOpt = viewOptions();
#endif
QFont font = viewOpt.font;
font.bold();
_measure = new Measure(QApplication::style(), font);
}
Expand Down Expand Up @@ -213,7 +219,12 @@ QModelIndex Desktopview::indexAt (const QPoint &in_point) const
{
QModelIndex ind;
QModelIndex parent = rootIndex ();;
QStyleOptionViewItem opt = viewOptions ();
QStyleOptionViewItem opt;
#if QT_VERSION >= 0x060000
initViewItemOption(&opt);
#else
opt = viewOptions();
#endif
Desktopdelegate *del = (Desktopdelegate *)itemDelegate ();
QPoint point = in_point;

Expand Down Expand Up @@ -260,7 +271,11 @@ void Desktopview::dropEvent (QDropEvent* event)
QAbstractItemModel *model = this->model ();

setCursor(Qt::ArrowCursor);
QModelIndex ind, dest = indexAt (event->pos ());
#if QT_VERSION >= 0x060000
QModelIndex ind, dest = indexAt (event->position().toPoint());
#else
QModelIndex ind, dest = indexAt (event->pos());
#endif
QModelIndexList list = getSelectedList (true);
Desktopmodel *dmodel = _modelconv->getDesktopmodel (model);

Expand Down Expand Up @@ -366,7 +381,11 @@ void Desktopview::checkAutoscroll (QPoint pos)

void Desktopview::mouseMoveEvent (QMouseEvent *event)
{
checkAutoscroll (event->pos ());
#if QT_VERSION >= 0x060000
checkAutoscroll (event->position().toPoint());
#else
checkAutoscroll (event->pos());
#endif
QListView::mouseMoveEvent (event);
}

Expand Down Expand Up @@ -432,7 +451,11 @@ void Desktopview::dragMoveEvent (QDragMoveEvent *event)
QListView::dragMoveEvent (event);
QAbstractItemModel *model = this->model ();

QPoint pos = event->pos ();
#if QT_VERSION >= 0x060000
QPoint pos = event->position().toPoint();
#else
QPoint pos = event->pos();
#endif
QModelIndex dest = indexAt (pos);
QModelIndexList list = selectedIndexes ();

Expand Down Expand Up @@ -460,7 +483,11 @@ void Desktopview::dragMoveEvent (QDragMoveEvent *event)
update (dest);
}

checkAutoscroll (event->pos ());
#if QT_VERSION >= 0x060000
checkAutoscroll (event->position().toPoint());
#else
checkAutoscroll (event->pos());
#endif
}


Expand Down Expand Up @@ -549,7 +576,11 @@ void Desktopview::scrollToLast (void)

// find out delegate and ask its size
QStyleOptionViewItem opt;
QAbstractItemDelegate *del = itemDelegate (ind);
#if QT_VERSION >= 0x060000
QAbstractItemDelegate *del = itemDelegateForIndex(ind);
#else
QAbstractItemDelegate *del = itemDelegate(ind);
#endif
QSize size = del->sizeHint (opt, ind);

// finally get the item's position
Expand Down
9 changes: 6 additions & 3 deletions desktopwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ X-Comment: On Debian GNU/Linux systems, the complete text of the GNU General
#include <QCheckBox>
#include <QFileDialog>
#include <QKeyEventTransition>
#include <QStateMachine>
#include <QState>
#include <QMenu>
#include <QMessageBox>
#include <QSettings>
Expand Down Expand Up @@ -400,7 +402,7 @@ void Desktopwidget::addAction (QAction *&act, const char *text, const char *slot
if (image)
{
QIcon icon;
QString str = QString (":/images/images/%1").arg (image);
QString str = utilIconPath() + image;

icon.addPixmap (QPixmap(str), QIcon::Normal, QIcon::Off);
act->setIcon (icon);
Expand Down Expand Up @@ -746,15 +748,15 @@ void Desktopwidget::specialView(const QString& prompt)
{
_toolbar->searchLabel->setText(prompt);
_toolbar->setSearchEnabled(true);
_view->setStyleSheet("QListView { background: lightblue; }");
_view->setStyleSheet("QListView { background: palette(alternate-base); }");
_dir->setEnabled(false);
_main->getMainwindow()->setSearchEnabled(false);
updateActions();
}

void Desktopwidget::normalView()
{
_view->setStyleSheet("QListView { background: lightgray; }");
_view->setStyleSheet("QListView { background: palette(mid); }");
_dir->setEnabled(true);
_toolbar->setFilterEnabled(true);
_toolbar->setSearchEnabled(false);
Expand Down Expand Up @@ -1617,6 +1619,7 @@ Toolbar::Toolbar(QWidget* parent, Qt::WindowFlags fl)
: QFrame(parent, fl)
{
setupUi(this);
utilUpdateIcons(this);

// When ESC is pressed, clear the field
QStateMachine *machine = new QStateMachine(this);
Expand Down
3 changes: 2 additions & 1 deletion dirview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,11 @@ Dirview::Dirview (QWidget *parent)
// _dir->setDropIndicatorShown (true); - defaults to this
setDragEnabled (true);
setAcceptDrops (true);
setStyleSheet ("QTreeView { background: palette(base); color: palette(text); }");

// We can't use shortcuts here as they conflict with main view
_search = new QAction ("&Search folder", this);
_search->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_S));
_search->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_S));
_new = new QAction ("&New subdirectory", this);
_rename = new QAction ("&Rename", this);
_delete = new QAction ("&Delete", this);
Expand Down
29 changes: 25 additions & 4 deletions doc/develop.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,17 @@ Qt5/C++ dependencies (desktop app + server):
libtiff-dev libjpeg-dev libsane-dev zlib1g-dev \
imagemagick tesseract-ocr tesseract-ocr-eng

Qt6/C++ dependencies (alternative to Qt5):

.. code:: bash

sudo apt-get install -y \
build-essential qmake6 qt6-base-dev qt6-base-dev-tools \
libqt6sql6-sqlite libpoppler-qt6-dev libpodofo-dev \
qt6-scxml-dev libqt6statemachine6 \
libtiff-dev libjpeg-dev libsane-dev zlib1g-dev \
imagemagick tesseract-ocr tesseract-ocr-eng

Python packages for demo-asset generation and documentation:

.. code:: bash
Expand All @@ -44,13 +55,20 @@ the full details.
Building
--------

Generate the qmake Makefile and build:
Generate the qmake Makefile and build with Qt5:

.. code:: bash

qmake "CONFIG+=test" paperman.pro
make

Or with Qt6:

.. code:: bash

qmake6 "CONFIG+=test" paperman.pro -o Makefile.qt6
make -f Makefile.qt6

The ``CONFIG+=test`` flag compiles in the built-in test suites. Without it
the ``-t`` option is not available.

Expand Down Expand Up @@ -138,16 +156,19 @@ The most useful targets during development:
make clean # Remove all build artefacts

Continuous Integration
---------------------
----------------------

A GitHub Actions workflow (``.github/workflows/ci.yml``) runs on every push
to ``master`` and on pull requests. It has three parallel jobs:
to ``master`` and on pull requests. It has four parallel jobs:

**qt** — Desktop app, server and C++ tests
**qt** — Desktop app, server and C++ tests (Qt5)
Installs Qt5/C++ dependencies, builds ``paperman`` and
``paperman-server``, generates test data, then runs the Qt unit tests,
page-fetch integration test and parallel conversion test.

**qt6** — Desktop app, server and C++ tests (Qt6)
Same as **qt** but builds against Qt6 instead of Qt5.

**flutter** — Flutter app and widget tests
Sets up Java 21 and Flutter 3.41.1, generates demo assets, runs
``flutter analyze`` and ``flutter test``, then builds a release APK
Expand Down
Loading