aboutsummaryrefslogtreecommitdiffstats
path: root/qarma/image-preview.patch
blob: 01799bb5bdce7bd8ff12367f5fc1d1b0a80ed82c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
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")) <<