aboutsummaryrefslogtreecommitdiffstats
path: root/qarma/image-preview.patch
diff options
context:
space:
mode:
Diffstat (limited to 'qarma/image-preview.patch')
-rw-r--r--qarma/image-preview.patch158
1 files changed, 158 insertions, 0 deletions
diff --git a/qarma/image-preview.patch b/qarma/image-preview.patch
new file mode 100644
index 0000000..01799bb
--- /dev/null
+++ b/qarma/image-preview.patch
@@ -0,0 +1,158 @@
+From 425ae90cae55f9136e0620c3165baf839e54bc0a Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Thomas=20L=C3=BCbking?= <thomas.luebking@gmail.com>
+Date: Fri, 26 Dec 2025 18:54:33 +0100
+Subject: [PATCH] add option or preview images in file dialogs
+
+closes #57
+---
+ Qarma.cpp | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 95 insertions(+), 1 deletion(-)
+
+diff --git a/Qarma.cpp b/Qarma.cpp
+index 3708360..c0f1f77 100644
+--- a/Qarma.cpp
++++ b/Qarma.cpp
+@@ -24,6 +24,7 @@
+ #include <QCheckBox>
+ #include <QColorDialog>
+ #include <QComboBox>
++#include <QCryptographicHash>
+ #include <QDate>
+ #include <QDBusConnection>
+ #include <QDBusConnectionInterface>
+@@ -34,6 +35,7 @@
+ #include <QFontDialog>
+ #include <QFormLayout>
+ #include <QIcon>
++#include <QImageReader>
+ #include <QInputDialog>
+ #include <QLabel>
+ #include <QLocale>
+@@ -42,12 +44,15 @@
+ #include <QProcess>
+ #include <QProgressDialog>
+ #include <QPropertyAnimation>
++#include <QProxyStyle>
+ #include <QPushButton>
+ #include <QScreen>
+ #include <QScrollBar>
+ #include <QSettings>
+ #include <QSlider>
+ #include <QSocketNotifier>
++#include <QSplitter>
++#include <QStandardPaths>
+ #include <QStringBuilder>
+ #include <QStringList>
+ #include <QTextBrowser>
+@@ -785,6 +790,83 @@ char Qarma::showMessage(const QStringList &args, char type)
+ return 0;
+ }
+
++QPixmap thumbnail(const QString &path, uint size)
++{
++ size = qMin(size, 1024u);
++ QImage thumb;
++ QImageReader thumbReader;
++ thumbReader.setFileName(path);
++ if (!thumbReader.canRead())
++ return QPixmap();
++
++ thumbReader.setQuality(50);
++ QSize sz = thumbReader.size();
++ QSize origSz = sz;
++ bool skipThumbnail = sz.width()*sz.height() < 1920*1200+1;
++
++ if (skipThumbnail) {
++ sz.scale(QSize(size,size), Qt::KeepAspectRatio);
++ thumbReader.setScaledSize(sz);
++ } else {
++ QFileInfo info(path);
++ QString canonicalPath = info.canonicalFilePath();
++ if (canonicalPath.isEmpty())
++ canonicalPath = info.absoluteFilePath();
++ QUrl url = QUrl::fromLocalFile(canonicalPath);
++ QCryptographicHash md5(QCryptographicHash::Md5);
++ md5.addData(QFile::encodeName(url.adjusted(QUrl::RemovePassword).url()));
++
++ QString folder;
++ uint tSize;
++ if (size <= 128) {
++ tSize = 128; folder = "normal/";
++ } else if (size <= 256) {
++ tSize = 256; folder = "large/";
++ } else if (size <= 512) {
++ tSize = 512; folder = "x-large/";
++ } else {
++ tSize = 1024; folder = "xx-large/";
++ }
++ Q_UNUSED(tSize);
++
++ const QString thumbPath = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) +
++ QLatin1String("/thumbnails/") + folder +
++ QString::fromLatin1(md5.result().toHex()) + QStringLiteral(".png");
++ QFileInfo tInfo(thumbPath);
++ if (tInfo.exists() && info.metadataChangeTime() <= tInfo.lastModified() && info.lastModified() <= tInfo.lastModified()) {
++ thumbReader.setFileName(thumbPath);
++ sz.scale(QSize(size,size), Qt::KeepAspectRatio);
++ thumbReader.setScaledSize(sz);
++ if (thumbReader.read(&thumb)) {
++ int w = thumb.text("Thumb::Image::Width").toInt();
++ int h = thumb.text("Thumb::Image::Height").toInt();
++ if (origSz == QSize(w, h))
++ return QPixmap::fromImage(thumb);
++ }
++ }
++ thumbReader.setFileName(path);
++// sz.scale(QSize(tSize,tSize), Qt::KeepAspectRatio); // in case we'll ever store the thumbnail
++ sz.scale(QSize(size,size), Qt::KeepAspectRatio);
++ thumbReader.setScaledSize(sz);
++ }
++ if (!thumbReader.read(&thumb))
++ return QPixmap();
++// if (skipThumbnail)
++ return QPixmap::fromImage(thumb);
++}
++
++class DblClckStyle : public QProxyStyle
++{
++ public:
++ int styleHint(StyleHint hint, const QStyleOption *option = nullptr,
++ const QWidget *widget = nullptr, QStyleHintReturn *returnData = nullptr) const override
++ {
++ if (hint == QStyle::SH_ItemView_ActivateItemOnSingleClick)
++ return false;
++ return QProxyStyle::styleHint(hint, option, widget, returnData);
++ }
++};
++
+ char Qarma::showFileSelection(const QStringList &args)
+ {
+ QFileDialog *dlg = new QFileDialog;
+@@ -827,6 +909,17 @@ char Qarma::showFileSelection(const QStringList &args)
+ if (idx > -1)
+ mimeFilter = mimeFilter.left(idx).trimmed() + " (" + mimeFilter.mid(idx+1).trimmed() + ")";
+ mimeFilters << mimeFilter;
++ } else if (args.at(i) == "--preview-images") {
++ READ_INT(size, UInt, "--preview-images must be followed by a positive number for the thumbnail size");
++ dlg->setOption(QFileDialog::DontUseNativeDialog);
++ if (QSplitter *splitter = dlg->findChild<QSplitter*>()) {
++ qApp->setStyle(new DblClckStyle);
++ QLabel *preview = new QLabel(splitter);
++ splitter->addWidget(preview);
++ connect(dlg, &QFileDialog::currentChanged, [=](const QString &path) {
++ preview->setPixmap(thumbnail(path, size));
++ });
++ }
+ }
+ else { WARN_UNKNOWN_ARG("--file-selection") }
+ }
+@@ -1815,7 +1908,8 @@ void Qarma::printHelp(const QString &category)
+ Help("--save", tr("Activate save mode")) <<
+ Help("--separator=SEPARATOR", tr("Set output separator character")) <<
+ Help("--confirm-overwrite", tr("Confirm file selection if filename already exists")) <<
+- Help("--file-filter=NAME | PATTERN1 PATTERN2 ...", tr("Sets a filename filter")));
++ Help("--file-filter=NAME | PATTERN1 PATTERN2 ...", tr("Sets a filename filter")) <<
++ Help("--preview-images=SIZE", "QARMA ONLY! " + tr("Show image thumbnails of SIZE")));
+ helpDict["list"] = CategoryHelp(tr("List Command\n %1 --list [Options] [Item1 ...]\nList Options").arg(applicationName()), HelpList() <<
+ Help("--text=TEXT", tr("Set the dialog text")) <<
+ Help("--column=COLUMN", tr("Set the column header")) <<