1 /* ============================================================
2  *
3  * This file is a part of digiKam project
4  * https://www.digikam.org
5  *
6  * Date        : 2009-09-08
7  * Description : global macros, variables and flags
8  *
9  * Copyright (C) 2009-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
10  *
11  * This program is free software; you can redistribute it
12  * and/or modify it under the terms of the GNU General
13  * Public License as published by the Free Software Foundation;
14  * either version 2, or (at your option)
15  * any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * ============================================================ */
23 
24 #include "digikam_globals.h"
25 
26 // Qt includes
27 
28 #include <QObject>
29 #include <QDir>
30 #include <QList>
31 #include <QImageReader>
32 #include <QImageWriter>
33 #include <QByteArray>
34 #include <QShortcut>
35 #include <QApplication>
36 #include <QStandardPaths>
37 #include <QLibrary>
38 #include <QSysInfo>
39 #include <QMimeType>
40 #include <QMimeDatabase>
41 
42 // KDE includes
43 
44 #include <klocalizedstring.h>
45 
46 // Local includes
47 
48 #include "digikam_config.h"
49 #include "digikam_debug.h"
50 #include "drawdecoder.h"
51 #include "rawcameradlg.h"
52 
53 // Windows includes
54 
55 #ifdef HAVE_DRMINGW
56 #   include <windows.h>
57 #endif
58 
59 namespace Digikam
60 {
61 
defineShortcut(QWidget * const w,const QKeySequence & key,const QObject * receiver,const char * slot)62 QShortcut* defineShortcut(QWidget* const w, const QKeySequence& key, const QObject* receiver, const char* slot)
63 {
64     QShortcut* const s = new QShortcut(w);
65     s->setKey(key);
66     s->setContext(Qt::WidgetWithChildrenShortcut);
67     QObject::connect(s, SIGNAL(activated()), receiver, slot);
68 
69     return s;
70 }
71 
supportedImageMimeTypes(QIODevice::OpenModeFlag mode,QString & allTypes)72 QStringList supportedImageMimeTypes(QIODevice::OpenModeFlag mode, QString& allTypes)
73 {
74     QStringList       formats;
75     QList<QByteArray> supported;
76 
77     switch (mode)
78     {
79         case QIODevice::ReadOnly:
80         {
81             supported = QImageReader::supportedImageFormats();
82             break;
83         }
84 
85         case QIODevice::WriteOnly:
86         {
87             supported = QImageWriter::supportedImageFormats();
88             break;
89         }
90 
91         case QIODevice::ReadWrite:
92         {
93             supported = QImageWriter::supportedImageFormats() + QImageReader::supportedImageFormats();
94             break;
95         }
96 
97         default:
98         {
99             qCDebug(DIGIKAM_GENERAL_LOG) << "Unsupported mode!";
100             break;
101         }
102     }
103 
104     foreach (const QByteArray& frm, supported)
105     {
106         if (QString::fromLatin1(frm).contains(QLatin1String("tif"),  Qt::CaseInsensitive) ||
107             QString::fromLatin1(frm).contains(QLatin1String("tiff"), Qt::CaseInsensitive))
108         {
109             continue;
110         }
111 
112         if (QString::fromLatin1(frm).contains(QLatin1String("jpg"),  Qt::CaseInsensitive) ||
113             QString::fromLatin1(frm).contains(QLatin1String("jpeg"), Qt::CaseInsensitive))
114         {
115             continue;
116         }
117 
118 #ifdef HAVE_JASPER
119 
120         if (QString::fromLatin1(frm).contains(QLatin1String("jp2"),  Qt::CaseInsensitive) ||
121             QString::fromLatin1(frm).contains(QLatin1String("j2k"),  Qt::CaseInsensitive) ||
122             QString::fromLatin1(frm).contains(QLatin1String("jpx"),  Qt::CaseInsensitive) ||
123             QString::fromLatin1(frm).contains(QLatin1String("jpc"),  Qt::CaseInsensitive) ||
124             QString::fromLatin1(frm).contains(QLatin1String("pgx"),  Qt::CaseInsensitive))
125         {
126             continue;
127         }
128 
129 #endif // HAVE_JASPER
130 
131 #ifdef HAVE_X265
132 
133         if (QString::fromLatin1(frm).contains(QLatin1String("heic"), Qt::CaseInsensitive) ||
134             QString::fromLatin1(frm).contains(QLatin1String("heif"), Qt::CaseInsensitive))
135         {
136             continue;
137         }
138 
139 #endif // HAVE_X265
140 
141 #ifdef HAVE_IMAGE_MAGICK
142 
143         if (QString::fromLatin1(frm).contains(QLatin1String("fts"),  Qt::CaseInsensitive) ||
144             QString::fromLatin1(frm).contains(QLatin1String("fit"),  Qt::CaseInsensitive) ||
145             QString::fromLatin1(frm).contains(QLatin1String("fits"), Qt::CaseInsensitive))
146         {
147             continue;
148         }
149 
150 #endif // HAVE_IMAGE_MAGICK
151 
152         formats.append(i18n("%1 Image (%2)", QString::fromLatin1(frm).toUpper(), QLatin1String("*.") + QLatin1String(frm)));
153         allTypes.append(QString::fromLatin1("*.%1 ").arg(QLatin1String(frm)));
154     }
155 
156     formats.append(i18n("TIFF Image (*.tiff *.tif)"));
157     allTypes.append(QLatin1String("*.tiff *.tif "));
158     formats.append(i18n("JPEG Image (*.jpg *.jpeg *.jpe)"));
159     allTypes.append(QLatin1String("*.jpg *.jpeg *.jpe "));
160 
161 #ifdef HAVE_JASPER
162 
163     formats.append(i18n("JPEG2000 Image (*.jp2 *.j2k *.jpx *.pgx)"));
164     allTypes.append(QLatin1String("*.jp2 *.j2k *.jpx *.pgx "));
165 
166 #endif // HAVE_JASPER
167 
168     formats << i18n("Progressive Graphics file (*.pgf)");
169     allTypes.append(QLatin1String("*.pgf "));
170 
171 #ifdef HAVE_X265
172 
173     formats << i18n("High Efficiency Image Coding (*.heic *.heif)");
174     allTypes.append(QLatin1String("*.heic *.heif "));
175 
176 #endif // HAVE_X265
177 
178 #ifdef HAVE_IMAGE_MAGICK
179 
180     formats << i18n("Flexible Image Transport System (*.fts *.fit *.fits)");
181     allTypes.append(QLatin1String("*.fts *.fit *.fits "));
182 
183 #endif // HAVE_IMAGE_MAGICK
184 
185     if (mode != QIODevice::WriteOnly)
186     {
187         formats << i18n("Raw Images (%1)", DRawDecoder::rawFiles());
188         allTypes.append(DRawDecoder::rawFiles());
189         formats << i18n("All supported files (%1)", allTypes);
190     }
191 
192     return formats;
193 }
194 
isReadableImageFile(const QString & filePath)195 bool isReadableImageFile(const QString& filePath)
196 {
197     QFileInfo info(filePath);
198 
199     if (info.isFile() && !info.isSymLink() && !info.isDir() && !info.isRoot())
200     {
201         QString path    = info.absoluteFilePath();
202         QMimeType mtype = QMimeDatabase().mimeTypeForFile(path);
203         QString suffix  = info.suffix().toUpper();
204 
205         // Add extra check of the image extensions that are still
206         // unknown in older Qt versions or have an application mime type.
207 
208         if (mtype.name().startsWith(QLatin1String("image/")) ||
209             (suffix == QLatin1String("PGF"))                 ||
210             (suffix == QLatin1String("KRA"))                 ||
211             (suffix == QLatin1String("CR3"))                 ||
212             (suffix == QLatin1String("HEIC"))                ||
213             (suffix == QLatin1String("HEIF"))                ||
214             DRawDecoder::rawFiles().contains(suffix))
215         {
216             return true;
217         }
218     }
219 
220     return false;
221 }
222 
showRawCameraList()223 void showRawCameraList()
224 {
225     RawCameraDlg* const dlg = new RawCameraDlg(qApp->activeWindow());
226     dlg->show();
227 }
228 
isRunningInAppImageBundle()229 bool isRunningInAppImageBundle()
230 {
231     QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
232 
233     if (env.contains(QLatin1String("APPIMAGE_ORIGINAL_LD_LIBRARY_PATH")) &&
234         env.contains(QLatin1String("APPIMAGE_ORIGINAL_QT_PLUGIN_PATH"))  &&
235         env.contains(QLatin1String("APPIMAGE_ORIGINAL_XDG_DATA_DIRS"))   &&
236         env.contains(QLatin1String("APPIMAGE_ORIGINAL_PATH")))
237     {
238         return true;
239     }
240 
241     return false;
242 }
243 
adjustedEnvironmentForAppImage()244 QProcessEnvironment adjustedEnvironmentForAppImage()
245 {
246     QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
247 
248     // If we are running into AppImage bundle, switch env var to the right values.
249 
250     if (isRunningInAppImageBundle())
251     {
252         qCDebug(DIGIKAM_GENERAL_LOG) << "Adjusting environment variables for AppImage bundle";
253 
254         if (!env.value(QLatin1String("APPIMAGE_ORIGINAL_LD_LIBRARY_PATH")).isEmpty())
255         {
256             env.insert(QLatin1String("LD_LIBRARY_PATH"),
257                        env.value(QLatin1String("APPIMAGE_ORIGINAL_LD_LIBRARY_PATH")));
258         }
259         else
260         {
261             env.remove(QLatin1String("LD_LIBRARY_PATH"));
262         }
263 
264         if (!env.value(QLatin1String("APPIMAGE_ORIGINAL_QT_PLUGIN_PATH")).isEmpty())
265         {
266             env.insert(QLatin1String("QT_PLUGIN_PATH"),
267                        env.value(QLatin1String("APPIMAGE_ORIGINAL_QT_PLUGIN_PATH")));
268         }
269         else
270         {
271             env.remove(QLatin1String("QT_PLUGIN_PATH"));
272         }
273 
274         if (!env.value(QLatin1String("APPIMAGE_ORIGINAL_XDG_DATA_DIRS")).isEmpty())
275         {
276             env.insert(QLatin1String("XDG_DATA_DIRS"),
277                        env.value(QLatin1String("APPIMAGE_ORIGINAL_XDG_DATA_DIRS")));
278         }
279         else
280         {
281             env.remove(QLatin1String("XDG_DATA_DIRS"));
282         }
283 
284         if (!env.value(QLatin1String("APPIMAGE_ORIGINAL_PATH")).isEmpty())
285         {
286             env.insert(QLatin1String("PATH"),
287                        env.value(QLatin1String("APPIMAGE_ORIGINAL_PATH")));
288         }
289         else
290         {
291             env.remove(QLatin1String("PATH"));
292         }
293 
294         if (!env.value(QLatin1String("APPIMAGE_ORIGINAL_KDE_FULL_SESSION")).isEmpty())
295         {
296             env.insert(QLatin1String("KDE_FULL_SESSION"),
297                        env.value(QLatin1String("APPIMAGE_ORIGINAL_KDE_FULL_SESSION")));
298         }
299         else
300         {
301             env.remove(QLatin1String("KDE_FULL_SESSION"));
302         }
303 
304         if (!env.value(QLatin1String("APPIMAGE_ORIGINAL_DESKTOP_SESSION")).isEmpty())
305         {
306             env.insert(QLatin1String("DESKTOP_SESSION"),
307                        env.value(QLatin1String("APPIMAGE_ORIGINAL_DESKTOP_SESSION")));
308         }
309         else
310         {
311             env.remove(QLatin1String("DESKTOP_SESSION"));
312         }
313 
314         if (!env.value(QLatin1String("APPIMAGE_ORIGINAL_XDG_CURRENT_DESKTOP")).isEmpty())
315         {
316             env.insert(QLatin1String("XDG_CURRENT_DESKTOP"),
317                        env.value(QLatin1String("APPIMAGE_ORIGINAL_XDG_CURRENT_DESKTOP")));
318         }
319         else
320         {
321             env.remove(QLatin1String("XDG_CURRENT_DESKTOP"));
322         }
323 
324         if (!env.value(QLatin1String("APPIMAGE_ORIGINAL_XDG_SESSION_DESKTOP")).isEmpty())
325         {
326             env.insert(QLatin1String("XDG_SESSION_DESKTOP"),
327                        env.value(QLatin1String("APPIMAGE_ORIGINAL_XDG_SESSION_DESKTOP")));
328         }
329         else
330         {
331             env.remove(QLatin1String("XDG_SESSION_DESKTOP"));
332         }
333     }
334 
335     return env;
336 }
337 
tryInitDrMingw()338 void tryInitDrMingw()
339 {
340 
341 #ifdef HAVE_DRMINGW
342 
343     qCDebug(DIGIKAM_GENERAL_LOG) << "Loading DrMinGw run-time...";
344 
345     QRegExp versionRegExp(QLatin1String("(\\d+[.]*\\d*)"));
346     QSysInfo::productVersion().indexOf(versionRegExp);
347     double version = versionRegExp.capturedTexts().constFirst().toDouble();
348 
349     if  (
350          ((version < 2000.0) && (version < 10.0)) ||
351          ((version > 2000.0) && (version < 2016.0))
352         )
353     {
354         qCDebug(DIGIKAM_GENERAL_LOG) << "DrMinGw: unsupported Windows version" << version;
355         return;
356     }
357 
358     QString appPath = QCoreApplication::applicationDirPath();
359     QString excFile = QDir::toNativeSeparators(appPath + QLatin1String("/exchndl.dll"));
360 
361     HMODULE hModExc = LoadLibraryW((LPCWSTR)excFile.utf16());
362 
363     if (!hModExc)
364     {
365         qCDebug(DIGIKAM_GENERAL_LOG) << "DrMinGw: cannot init crash handler dll.";
366         return;
367     }
368 
369     // No need to call ExcHndlInit since the crash handler is installed on DllMain
370 
371     auto myExcHndlSetLogFileNameA = reinterpret_cast<BOOL (APIENTRY*)(const char*)>
372                                         (GetProcAddress(hModExc, "ExcHndlSetLogFileNameA"));
373 
374     if (!myExcHndlSetLogFileNameA)
375     {
376         qCDebug(DIGIKAM_GENERAL_LOG) << "DrMinGw: cannot init customized crash file.";
377         return;
378     }
379 
380     // Set the log file path to %LocalAppData%\digikam_crash.log
381 
382     QString logPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
383     QString logFile = QDir::toNativeSeparators(logPath + QLatin1String("/digikam_crash.log"));
384     myExcHndlSetLogFileNameA(logFile.toLocal8Bit().data());
385 
386     qCDebug(DIGIKAM_GENERAL_LOG) << "DrMinGw run-time loaded.";
387     qCDebug(DIGIKAM_GENERAL_LOG) << "DrMinGw crash-file will be located at: " << logFile;
388 
389 #endif // HAVE_DRMINGW
390 
391 }
392 
toolButtonStyleSheet()393 QString toolButtonStyleSheet()
394 {
395     return QLatin1String("QToolButton { padding: 1px; background-color: "
396                          "  qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, "
397                          "  stop: 0 rgba(100, 100, 100, 50%), "
398                          "  stop: 1 rgba(170, 170, 170, 50%)); "
399                          "border: 1px solid rgba(170, 170, 170, 10%); } "
400 
401                          "QToolButton:hover { border-color: white; } "
402 
403                          "QToolButton:pressed { background-color: "
404                          "  qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, "
405                          "  stop: 0 rgba(40, 40, 40, 50%), "
406                          "  stop: 1 rgba(90, 90, 90, 50%)); "
407                          "border-color: white; } "
408 
409                          "QToolButton:checked { background-color: "
410                          "  qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, "
411                          "  stop: 0 rgba(40, 40, 40, 50%), "
412                          "  stop: 1 rgba(90, 90, 90, 50%)); } "
413 
414                          "QToolButton:disabled { background-color: "
415                          "  qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, "
416                          "  stop: 0 rgba(40, 40, 40, 50%), "
417                          "  stop: 1 rgba(50, 50, 50, 50%)); }");
418 }
419 
macOSBundlePrefix()420 QString macOSBundlePrefix()
421 {
422     return QString::fromUtf8("/Applications/digiKam.org/digikam.app/Contents/");
423 }
424 
425 } // namespace Digikam
426