Mergin several old merge requests
authorKlaus Ethgen <Klaus@Ethgen.ch>
Sat, 17 Jul 2021 10:31:25 +0000 (11:31 +0100)
committerKlaus Ethgen <Klaus@Ethgen.ch>
Sat, 17 Jul 2021 10:31:25 +0000 (11:31 +0100)
That merge requests got implemented in the past. This just completes the
merges to have a correct display in git.

* commit '8abb834383bd':
  fixed uvbuntu url

* commit 'b2044632dfb8':
  Update Slovak translation

* commit 'eaf4dcdd76e4':
  Generate sRGB thumbnails for images with embedded color profile data

* commit 'cfb686d65005':
  Fix doc building with newer yelp

* commit '75794b0d6592':
  dupe: Eliminate O(n^2) code in dupe_files_add_queue_cb()
  dupe: Optimize memory handling in dupe_files_add_queue_cb()
  dupe: Avoid O(n) operations in dupe_files_add_queue_cb()

* commit '319f58d9c1d7':
  Use binary units for sizes, not decimal values.

* commit '0436ed321b88':
  Add image/webp mime type

* commit '8eb3ee5d331b':
  Update Dutch translation (nl_nl)

* commit '1424f6b6831b':
  Fix spelling mistake emdedded -> embedded

* commit '7ea866b2aa83':
  Cast `struct sockaddr_un*` properly
  Use POSIX options for tail and tr

* commit '865a8ba3888f':
  Updated Catalan translation

* commit 'f4fad426aed9':
  updated german translation
  updated german translation

* commit '0d981a84c8cb':
  fixed wrong spelled words

* commit '121db0ea48ef':
  fixed spelling mistakes

* commit '3c363439f022':
  New pop-up menu action "Copy image to clipboard"

* commit '18ad1ee9552a':
  Fix building on GTK2 again, adding #ifdefs for gtk_label_set_[x,y]align

* commit '50ccf27c1a5e':
  Use gtk_label_set_[x,y]align instead of gtk_misc_set_alignment

* commit 'e108b2e5c089':
  Add keymap_template.h to geeqie_SOURCES too

* commit '546dbdfa2dd7':
  Fix building with --enable-debug-flags

* commit 'c113dd9a8a29':
  Fix mistakes in Czech translation

* commit 'f6e88fcd6955':
  Update Slovak translation

* commit '635569789cf5':
  Slightly improve plugin template

44 files changed:
CHECKLIST.md [new file with mode: 0644]
CODING [deleted file]
CODING.md [new file with mode: 0644]
HACKING [deleted file]
README.md
appimages/README.md [new file with mode: 0644]
doc/Makefile.am
doc/create-doxygen-lua-api.sh [new file with mode: 0755]
doc/docbook/GuideIndex.xml
doc/docbook/GuideOptionsAdvanced.xml [new file with mode: 0644]
doc/docbook/GuideOptionsMain.xml
doc/docbook/GuideOtherSoftware.xml [new file with mode: 0644]
doc/docbook/GuideReference.xml
doc/docbook/GuideReferenceKeyboardShortcuts.xml
doc/docbook/GuideReferenceLuaAPI.xml [new file with mode: 0644]
doc/docbook/GuideReferenceStandards.xml
doxygen.conf
org.geeqie.Geeqie.appdata.xml.in
po/sk.po
scripts/generate-appimage.sh [new file with mode: 0755]
src/Makefile.am
src/desktop_file.c
src/dupe.c
src/editors.c
src/exiv2.cc
src/image-load.c
src/image_load_external.c [new file with mode: 0644]
src/image_load_external.h [new file with mode: 0644]
src/layout.c
src/layout_util.c
src/lua.c
src/main.c
src/main.h
src/misc.c
src/options.c
src/options.h
src/preferences.c
src/preferences.h
src/rcfile.c
src/typedefs.h
src/utilops.c
src/utilops.h
src/window.c
web/index.html

diff --git a/CHECKLIST.md b/CHECKLIST.md
new file mode 100644 (file)
index 0000000..a02f11e
--- /dev/null
@@ -0,0 +1,31 @@
+## A checklist to be used after Geeqie has been updated
+
+Before compiling the sources, carry out the following actions when necessary:
+
+* Update ```org.geeqie.Geeqie.appdata.xml.in``` with the latest released version and date
+* If source files have been added or removed from ```.src/``` directory, resync ```./po/POTFILES.in```   
+```
+cd ./po  
+./regen_potfiles.sh | patch -p0  
+```
+* Keep translations in sync with the code  
+```
+cd ./po  
+make update-po  
+```  
+* Update the the timezone database  
+```
+./scripts/zonedetect/create_timezone_database
+```
+
+After compiling the sources, carry out the following actions when necessary:  
+
+* Generate a new AppImage (note that this should be run on a **20.04 system**)  
+```
+./scripts/generate-appimage.sh <location of GeeqieWeb/appimages folder>
+```
+* Copy Help html files to ```<location of GeeqieWeb/help folder>```
+* Copy ```geeqie.desktop``` to ```<location of GeeqieWeb/ folder>```  
+* Copy ```org.geeqie.Geeqie.appdata.xml``` to ```<location of GeeqieWeb/ folder>```  
+* Upload to GeeqieWeb  
+
diff --git a/CODING b/CODING
deleted file mode 100644 (file)
index 9a8cbc1..0000000
--- a/CODING
+++ /dev/null
@@ -1,228 +0,0 @@
-Log Window:
-
-DEBUG_0()
-Use DEBUG_0() only for temporary debugging i.e. not in code in the repository.
-The user will then not see irrelevant debug output when the default
-debug level = 0 is used.
-
-log_printf()
-If the first word of the message is "error" or "warning" (case insensitive)
-the message will be color-coded appropriately.
-
-
-GTKInspector:
-
-DEBUG_NAME(widget)
-For use with the GTKInspector to provide a visual indication of where objects are declared.
-
-Sample command line call:
-GTK_DEBUG=interactive src/geeqie
-
---------------------------------------------------------------------------------
-
-GPL header, in every file, like this:
-
-/** \file
- * \short Short description of this file.
- * \author Author1
- * \author Author2
- *
- * Optionally detailed description of this file
- * on more lines.
- */
-
-/*
- *  This file is a part of Geeqie project (http://www.geeqie.org/).
- *  Copyright (C) 2008 - 2016 The Geeqie Team
- *
- *  This program is free software; you can redistribute it and/or modify it
- *  under the terms of the GNU General Public License as published by the Free
- *  Software Foundation; either version 2 of the License, or (at your option)
- *  any later version.
- *
- *  This program is distributed in the hope that it will be useful, but WITHOUT
- *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
- *  more details.
- */
-
---------------------------------------------------------------------------------
-
-git change-log:
-
-If referencing a Geeqie GitHub issue, include the issue number in the summary line.
-Start with a short summary in the first line (without a dot at the end) followed
-by a empty line.
-
-If referencing a Geeqie GitHub issue, include a hyperlink to the GitHub issue page
-in the message body.
-Use whole sentences begins with Capital letter. For each
-modification use new line. Or you can write the theme, colon and then every
-change on new line, begin with "- ".
-
-See also: http://www.tpope.net/node/106
-
-Example:
-
-   I did some bugfixes
-
-   There was the bug that something was wrong. I fixed it.
-
-   Library:
-   - the interface was modified
-   - new functions were added
-
-Also please use your full name and a working e-mail address as author for any contribution.
-
---------------------------------------------------------------------------------
-
-sources:
-
-Indentation: tabs
-Names of variables & functions:         small_letters
-      of defines:               CAPITAL_LETTERS
-
-Try to use explicit variable and function names.
-
-Try not to use macros.
-Use EITHER "struct foo" OR "foo"; never both
-
-
-Conditions, cycles:
-
-if (<cond>)
-       {
-       <command>;
-       ...
-       <command>;
-       }
-else
-       {
-       <command>;
-       ...
-       <command>;
-       }
-
-if (<cond_very_very_very_very_very_very_very_very_very_long> &&
-    <cond2very_very_very_very_very_very_very_very_very_long>)
-    <the_only_command>;
-
-switch (<var>)
-       {
-       case 0:
-               <command>;
-               <command>;
-               break;
-       case 1:
-               <command>; break;
-       }
-
-for (i = 0; i <= 10; i++)
-       {
-       <command>;
-       ...
-       <command>;
-       }
-
-
-Functions:
-
-gint bar(<var_def>, <var_def>, <var_def>)
-{
-       <command>;
-       ...
-       <command>;
-
-       return 0; // i.e. SUCCESS; if error, you must return minus <err_no>
-}
-
-void bar2(void)
-{
-       <command>;
-       ...
-       <command>;
-}
-
-Pragma: (Indentation 2 spaces)
-
-#ifdef ENABLE_NLS
-#  undef _
-#  define _(String) (String)
-#endif /* ENABLE_NLS */
-
-Headers:
-
-#ifndef _FILENAME_H
-
---------------------------------------------------------------------------------
-
-Use spaces around every operator (except ".", "->", "++" and "--");
-        unary operator '*' and '&' are missing the space from right;
-        (and also unary '-').
-As you can see above, parentheses are closed to inside, i.e. " (blah blah) "
-    In "function(<var>)" there are no space before '('.
-You MAY use more tabs/spaces than you OUGHT TO (according to this CodingStyle), if
-        it makes your code nicer in being vertically indented.
-Variables declarations should be followed by a blank line and should always be
-at the start of the block.
-
---------------------------------------------------------------------------------
-
-Use glib types when possible (ie. gint and gchar instead of int and char).
-Use glib functions when possible (ie. g_ascii_isspace() instead of isspace()).
-Check if used functions are not deprecated.
-
---------------------------------------------------------------------------------
-
-Documentation:
-
-Use American, rather than British English, spelling. This will facilitate consistent
-text searches. User text may be translated via the en_GB.po file.
-
-To document the code use the following rules to allow extraction with doxygen.
-Do not save with comments. Not all comments have to be doxygen comments.
-
-- Use C comments in plain C files and use C++ comments in C++ files for one line
-  comments.
-- Use '/**' (note the two asterisks) to start comments to be extracted by
-  doxygen and start every following line with " *".
-- Use '@' to indicate doxygen keywords/commands (see below).
-- Use the '@deprecated' command to tell if the function is subject to be deleted
-  or to a  complete rewrite.
-
-Example:
-
-To document functions or big structures:
-   /**
-    * @brief This is a short description of the function.
-    *
-    * This function does ...
-    *
-    * @param x1 This is the first parameter named x1
-    * @param y1 This is the second parameter named y1
-    * @return What the function returns
-    *    You can extend that return description (or anything else) by indenting the
-    *    following lines until the next empty line or the next keyword/command.
-    * @see Cross reference
-    */
-
-To document members of a structure that have to be documented (use it at least
-for big structures) use the '/**<' format:
-   int counter; /**< This counter counts images */
-
-Document TODO or FIXME comments as:
- /**
- * @todo
-or
- /**
- * @FIXME 
-For further documentation about doxygen see
-http://www.stack.nl/~dimitri/doxygen/manual.html. For the possible commands you
-can use see http://www.stack.nl/~dimitri/doxygen/commands.html.
-
-But in case just think about that the documentation is for other developers not
-for the end user. So keep the focus.
-
-The file ./scripts/doxygen-help.sh may be used to integrate access to the
-Doxygen files into a code editor.
diff --git a/CODING.md b/CODING.md
new file mode 100644 (file)
index 0000000..2e57b8c
--- /dev/null
+++ b/CODING.md
@@ -0,0 +1,254 @@
+[Log Window](#log-window)  
+[GPL header](#gpl-header)  
+[Git change log](#git-change-log)  
+[Sources](#sources)  
+[Documentation](#documentation)  
+
+-----------
+
+
+# <a name='log-window'>
+# Log Window
+
+`DEBUG_0()`  
+Use `DEBUG_0()` only for temporary debugging i.e. not in code in the repository.
+The user will then not see irrelevant debug output when the default
+`debug level = 0` is used.
+
+`log_printf()`  
+If the first word of the message is "error" or "warning" (case insensitive) the message will be color-coded appropriately.
+
+
+`DEBUG_NAME(widget)`  
+For use with the [GTKInspector](https://wiki.gnome.org/action/show/Projects/GTK/Inspector?action=show&redirect=Projects%2FGTK%2B%2FInspector) to provide a visual indication of where objects are declared.
+
+Sample command line call:  
+`GTK_DEBUG=interactive src/geeqie`
+
+--------------------------------------------------------------------------------
+
+# <a name='gpl-header'>
+# GPL header
+
+Include a header in every file, like this:  
+
+    /** @file  
+    * @brief Short description of this file.  
+    * @author Author1  
+    * @author Author2  
+    *  
+    * Optionally detailed description of this file    
+    * on more lines.    
+    */    
+
+    /*  
+    *  This file is a part of Geeqie project (http://www.geeqie.org/).  
+    *  Copyright (C) 2008 - 2021 The Geeqie Team  
+    *  
+    *  This program is free software; you can redistribute it and/or modify it  
+    *  under the terms of the GNU General Public License as published by the Free  
+    *  Software Foundation; either version 2 of the License, or (at your option)  
+    *  any later version.  
+    *  
+    *  This program is distributed in the hope that it will be useful, but WITHOUT  
+    *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or  
+    *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for  
+    *  more details.  
+    */  
+
+-------------
+
+# <a name='git-change-log'>
+# git change-log
+
+If referencing a Geeqie GitHub issue, include the issue number in the summary line. Start with a short summary in the first line (without a dot at the end) followed by a empty line.
+
+If referencing a Geeqie GitHub issue, include a hyperlink to the GitHub issue webpage in the message body. Use whole sentences beginning with Capital letter. For each modification use a new line. Or you can write the theme, colon and then every change on new line, begin with "- ".
+
+See also [A Note About Git Commit Messages](http://www.tpope.net/node/106)
+
+Example:
+
+   `I did some bugfixes`
+
+   `There was the bug that something was wrong. I fixed it.`
+
+  ` Library:`  
+   `- the interface was modified`  
+  `- new functions were added`
+
+Also please use your full name and a working e-mail address as author for any contribution.
+
+--------------------------------------------------------------------------------
+
+# <a name='sources'>
+# Sources
+
+Indentation: tabs at 4 spaces
+       
+       
+Names:
+
+- of variables & functions:     small\_letters  
+- of defines:           CAPITAL\_LETTERS
+
+Try to use explicit variable and function names.  
+Try not to use macros.  
+Use EITHER "struct foo" OR "foo"; never both
+
+
+Conditions, cycles:  
+
+    if (<cond>)
+       {
+       <command>;
+       ...
+       <command>;
+       }
+    else
+       {
+       <command>;
+       ...
+       <command>;
+       }
+
+    if (<cond_very_very_very_very_very_very_very_very_very_long> &&
+    <cond2very_very_very_very_very_very_very_very_very_long>)
+    <the_only_command>;
+
+    switch (<var>)
+       {
+       case 0:
+               <command>;
+                   <command>;
+                   break;
+       case 1:
+               <command>; break;
+       }
+
+    for (i = 0; i <= 10; i++)
+       {
+       <command>;
+       ...
+       <command>;
+       }
+
+Functions:
+
+    gint bar(<var_def>, <var_def>, <var_def>)
+    {
+       <command>;
+       ...
+       <command>;
+
+       return 0; // i.e. SUCCESS; if error, you must return minus <err_no>
+    }
+
+    void bar2(void)
+    {
+       <command>;
+       ...
+       <command>;
+    }
+
+Pragma: (Indentation 2 spaces)
+
+    #ifdef ENABLE_NLS
+    #  undef _
+    #  define _(String) (String)
+    #endif /* ENABLE_NLS */
+
+Headers:
+
+    #ifndef _FILENAME_H
+
+
+Use spaces around every operator (except ".", "->", "++" and "--").   
+Unary operator '*' and '&' are missing the space from right, (and also unary '-').
+
+As you can see above, parentheses are closed to inside, i.e. " (blah blah) "  
+In "`function(<var>)`" there is no space before the '('.  
+You MAY use more tabs/spaces than you OUGHT TO (according to this CodingStyle), if it makes your code nicer in being vertically indented.  
+Variables declarations should be followed by a blank line and should always be at the start of the block.  
+
+
+Use glib types when possible (ie. gint and gchar instead of int and char).
+Use glib functions when possible (ie. `g_ascii_isspace()` instead of `isspace()`).
+Check if used functions are not deprecated.
+
+--------------------------------------------------------------------------------
+
+# <a name='documentation'>
+# Documentation
+
+Use American, rather than British English, spelling. This will facilitate consistent
+text searches. User text may be translated via the en_GB.po file.
+
+To document the code use the following rules to allow extraction with doxygen.
+Do not save with comments. Not all comments have to be doxygen comments.
+
+- Use C comments in plain C files and use C++ comments in C++ files for one line
+  comments.
+- Use '/**' (note the two asterisks) to start comments to be extracted by
+  doxygen and start every following line with " *".
+- Use '@' to indicate doxygen keywords/commands (see below).
+- Use the '@deprecated' command to tell if the function is subject to be deleted
+  or to a  complete rewrite.
+
+Example:
+
+To document functions or big structures:
+
+    /**
+     * @brief This is a short description of the function.
+     *
+     * This function does ...
+     *
+     * @param x1 This is the first parameter named x1
+     * @param y1 This is the second parameter named y1
+     * @return What the function returns
+     *    You can extend that return description (or anything else) by indenting the
+     *    following lines until the next empty line or the next keyword/command.
+     * @see Cross reference
+     */
+
+To document members of a structure that have to be documented (use it at least
+for big structures) use the `/**<` format:  
+`int counter; /**< This counter counts images */`
+
+Document TODO or FIXME comments as:  
+
+    /**  
+    * @todo
+   
+or 
+    /**  
+    * @FIXME   
+
+For further documentation about doxygen see the [Doxygen Manual](https://www.doxygen.nl/index.html).  
+For the possible commands you may use, see [Doxygen Special Commands](https://www.doxygen.nl/manual/commands.html).
+
+The file `./scripts/doxygen-help.sh` may be used to integrate access to the Doxygen files into a code editor.
+
+The following environment variables may be set to personalize the Doxygen output:  
+`DOCDIR=<output folder>`  
+`SRCDIR=<the folder above ./src>`  
+`PROJECT=`  
+`VERSION=`  
+`PLANTUML_JAR_PATH=`  
+`INLINE_SOURCES=<YES|NO>`  
+`STRIP_CODE_COMMENTS=<YES|NO>`  
+
+Ref: [INLINE\_SOURCES](https://www.doxygen.nl/manual/config.html#cfg_inline_sources)  
+Ref: [STRIP\_CODE\_COMMENTS](https://www.doxygen.nl/manual/config.html#cfg_strip_code_comments)
+
+To include diagrams (if any) in the Doxygen output, the following are required to be installed. The installation process will vary between distributions:  
+[The PlantUML jar](https://plantuml.com/download)  
+`sudo apt install default-jre`  
+`sudo apt install texlive-font-utils`
+
+-------------
+
+But in case just think about that the documentation is for other developers not
+for the end user. So keep the focus.
diff --git a/HACKING b/HACKING
deleted file mode 100644 (file)
index d7e27b7..0000000
--- a/HACKING
+++ /dev/null
@@ -1,21 +0,0 @@
-A brief overview for those that wish to work with the source.
-
-The Makefiles and configure script are generated by the autogen.sh script,
-usually only distributed with snapshot releases. Running autogen.sh requires
-automake and autoconf. GNU gettext may also be needed by autogen.sh.
-
-Git commits _require_ an explicit log message. Think it will be used in the
-release Changelog.
-
-Coders should respect general coding style (see CODING).
-
-Coders, please resync po/POTFILES.in if you add or remove source files from src/
-directory (using regen_potfiles.sh script in po/) and re-run make update-po when
-appropriate to keep translations in sync with the code.
-
-Maintainers, don't forget to run make update-po before releases.
-
-The scripts folder contains a script for generating the timezone database.
-The database will need updating occasionally.
-
-Translators, please have a look at po/README.
index 4ef910c..35d2ec4 100644 (file)
--- a/README.md
+++ b/README.md
@@ -103,7 +103,15 @@ Animated GIFs are supported.
 # <a name="downloading"></a>
 ## Downloading
 
-Geeqie is available as a package with some distributions, however Geeqie is stable and you may compile the latest version from sources.
+Geeqie is available:  
+
+* as a package with some distributions.
+
+* as a [flatpak](https://flathub.org/apps/details/org.geeqie.Geeqie) from the [Flathub site](https://flathub.org/home).
+
+* as an [AppImage](https://cclark.uk/Geeqie).
+
+However Geeqie is stable and you may compile the latest version from sources.
 
 There are two scripts which will download and compile the sources for you.
 
@@ -122,8 +130,6 @@ Either: `git clone git://www.geeqie.org/geeqie.git`
 
 Or: `git clone http://www.geeqie.org/git/geeqie.git`
 
-Also [Geeqie is available as a flatpak](https://flathub.org/apps/details/org.geeqie.Geeqie) from the [Flathub site](https://flathub.org/home).
-
 ## Manual Installation
 
 List compile options: `./autogen.sh --help`
diff --git a/appimages/README.md b/appimages/README.md
new file mode 100644 (file)
index 0000000..778ecb7
--- /dev/null
@@ -0,0 +1,47 @@
+# How to create AppImages for Geeqie
+
+## Download the required tools:
+
+Download the `linuxdeploy` tool. At the time of writing, this is:  
+
+`wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage`
+
+
+Move this file to `$HOME/bin` or somewhere else in your `$PATH` and make executable.
+
+
+## Generate the executable
+
+`cd <your working area>`
+
+Download Geeqie sources:  
+`git clone git://www.geeqie.org/geeqie.git`  
+`cd geeqie`  
+
+If a run has already been made, remove any existing targets:  
+`rm -r  <target dir>/AppDir`  
+`sudo rm -rf doc/html`  
+
+Create a fresh target directory:  
+`mkdir <target dir>/AppDir`
+
+Generate the Geeqie executable:  
+`sudo make maintainer-clean`  
+`./autogen.sh --prefix="/usr/"`  
+`make -j`  
+`make install DESTDIR=<full path to target dir>/AppDir`  
+
+## Generate the AppImage
+
+`cd <target dir>`  
+`linuxdeploy-x86_64.AppImage \`  
+`--appdir ./AppDir --output appimage \`  
+`--desktop-file ./AppDir/usr/share/applications/geeqie.desktop \`  
+`--icon-file ./AppDir/usr/share/pixmaps/geeqie.png \`  
+`--executable ./AppDir/usr/bin/geeqie`  
+
+## Rename AppImage
+If required, rename the AppImage executable - e.g.:  
+`mv ./Geeqie-v1.6-x86_64.AppImage $(./Geeqie-v1.6-x86_64.AppImage -v | sed 's/git//' | sed 's/-.* /-/' | sed 's/ /-v/' | sed 's/-GTK3//').AppImage`
+
+The script `./scripts/generate-appimage.sh` automates this process.
index 78b9623..a09a809 100644 (file)
@@ -5,7 +5,7 @@ helpdir = @htmldir@
 EXTRA_DIST = docbook2html.sh docbook icons
 
 html/GuideIndex.html: docbook/GuideIndex.xml
-       rm -rf html; mkdir html; cp $(srcdir)/icons/* html/
+       rm -rf html; mkdir html; cp $(srcdir)/icons/* html/; ./create-doxygen-lua-api.sh
        if [ -x "$(GNOME_DOC_TOOL)" ]; then \
                "$(GNOME_DOC_TOOL)" html -i -o html/ $(srcdir)/docbook/GuideIndex.xml ; \
        else \
@@ -19,10 +19,12 @@ install-data-hook: html
                $(MKDIR_P) "$(DESTDIR)$(helpdir)" || exit 1; \
                cd $(srcdir)/html; for f in * ; do $(INSTALL_DATA) "$$f" "$(DESTDIR)$(helpdir)/$$f" ; done; \
                ln -s -f GuideIndex.html "$(DESTDIR)$(helpdir)/index.html" ; \
+               $(MKDIR_P) "$(DESTDIR)$(helpdir)/lua-api" || exit 1; \
+               cd ./lua-api/html; for f in * ; do $(INSTALL_DATA) "$$f" "$(DESTDIR)$(helpdir)/lua-api/$$f" ; done; \
        fi
 
 uninstall-hook:
-       rm "$(DESTDIR)$(helpdir)/"*
+       rm -r "$(DESTDIR)$(helpdir)/"*
 
 clean-local:
        rm -rf html
diff --git a/doc/create-doxygen-lua-api.sh b/doc/create-doxygen-lua-api.sh
new file mode 100755 (executable)
index 0000000..38c92ed
--- /dev/null
@@ -0,0 +1,99 @@
+#!/bin/bash
+
+#**********************************************************************
+# Copyright (C) 2021 - The Geeqie Team
+#
+# Author: Colin Clark
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#**********************************************************************
+#
+# This script will create the Lua API html document, which is part of
+# the Geeqie Help file.
+#
+# It is run during the generation of the help files.
+#
+# The generated Lua html files are placed in doc/html/lua-api
+#
+# The doxygen.conf file is modified to extract only those comments
+# that are useful as part of an API description.
+#
+#**********************************************************************
+
+export PROJECT="Geeqie"
+export VERSION=$(git tag --list v[1-9]* | tail -1)
+export SRCDIR="$PWD/.."
+export DOCDIR="$PWD/html/lua-api"
+
+TMPFILE=$(mktemp) || exit 1
+
+# Modify the Geeqie doxygen.conf file to produce
+# only the data needed for the lua API document
+awk '
+BEGIN {
+       FILE_PATTERNS_found = "FALSE"
+}
+{
+       if (FILE_PATTERNS_found == "TRUE")
+               {
+               if ($0 ~ /\\/)
+                       {
+                       next
+                       }
+               else
+                       {
+                       FILE_PATTERNS_found = "FALSE"
+                       }
+               }
+       if ($1 == SHOW_INCLUDE_FILES)
+               {
+               {print "SHOW_INCLUDE_FILES = NO"}
+               }
+       else if ($1 == "FILE_PATTERNS")
+               {
+               print "FILE_PATTERNS = lua.c"
+               FILE_PATTERNS_found = "TRUE"
+               next
+               }
+       else if ($1 == "EXCLUDE_SYMBOLS")
+               {
+               print "EXCLUDE_SYMBOLS = L \\"
+               print "lua_callvalue \\"
+               print "lua_check_exif \\"
+               print "lua_check_image \\"
+               print "lua_init \\"
+               print "_XOPEN_SOURCE \\"
+               print "LUA_register_global \\"
+               print "LUA_register_meta"
+               }
+       else if ($1 == "SOURCE_BROWSER")
+               {
+               print "SOURCE_BROWSER = NO"
+               }
+       else if ($1 == "HAVE_DOT")
+               {
+               {print "HAVE_DOT = NO"}
+               }
+       else
+               {
+               {print}
+               }
+}
+' ../doxygen.conf > $TMPFILE
+
+doxygen $TMPFILE
+
+rm $TMPFILE
+
index 0f76e27..986b8ef 100644 (file)
@@ -72,6 +72,7 @@
   <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="GuideOptionsMain.xml" />\r
   <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="GuideOptionsAdditional.xml" />\r
   <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="GuideReference.xml" />\r
+  <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="GuideOtherSoftware.xml" />\r
   <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="GuideFaq.xml" />\r
   <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="GuideLicence.xml" />\r
   <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="GuideCredits.xml" />\r
diff --git a/doc/docbook/GuideOptionsAdvanced.xml b/doc/docbook/GuideOptionsAdvanced.xml
new file mode 100644 (file)
index 0000000..e02a562
--- /dev/null
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<section id="GuideOptionsAdvanced">\r
+  <title>External preview extraction</title>\r
+  <para>\r
+    The intention of this feature is to give the user the possibility to display image previews of files\r
+    that the standard libraries cannot decode.\r
+    <para />\r
+    An example is the .dng files produced by LG V30 cameras. Neither\r
+    <code>exiv2</code>\r
+    nor\r
+    <code>libraw</code>\r
+    can\r
+    extract a preview, but the command line program\r
+    <code>dcraw</code>\r
+    can.\r
+    <para />\r
+    This feature allows a work-around until the standard libraries provide a solution.\r
+    <para />\r
+    Two command files are required: one to identify which files to process, and one to extract or decode the preview image.\r
+    <para />\r
+    The format for the identification tool is:\r
+    <para />\r
+    <pre>\r
+      <programlisting xml:space="preserve">\r
+        Parameter 1: (input) full path name to the current image.\r
+        <para />\r
+        Returns: 0 for file match, any other value for no match.\r
+      </programlisting>\r
+    </pre>\r
+    <para />\r
+    The format for the extraction tool is:\r
+    <para />\r
+    <pre>\r
+      <programlisting xml:space="preserve">\r
+        Parameter 1: (input) full path name to the current image.\r
+        <para />\r
+        Parameter 2: (output) a temporary file name generated by Geeqie. The tool should load this file with the decoded image.\r
+        <para />\r
+        Returns: not used.\r
+      </programlisting>\r
+    </pre>\r
+    <para />\r
+    This is an example of an identification tool using a shell script:\r
+    <para />\r
+    <pre>\r
+      <programlisting xml:space="preserve">#! /bin/bash\r
+\r
+        filename=$(basename -- "$1")\r
+        extension="${filename##*.}"\r
+\r
+        shopt -s nocasematch\r
+        if [[ $extension == "DNG" ]]\r
+        then\r
+            cameramodel=$(exiv2 -K Exif.Image.UniqueCameraModel -Pt "$1" )\r
+            if [[ $cameramodel  == "LG-H930" ]]\r
+            then\r
+                exit 0\r
+            else\r
+                exit 1\r
+            fi\r
+        else\r
+            exit 1\r
+        fi</programlisting>\r
+    </pre>\r
+  </para>\r
+  <para>\r
+    This is an example of an extraction/decode tool using a shell script:\r
+    <pre>\r
+      <programlisting xml:space="preserve">#! /bin/bash\r
+        dcraw -e -c   "$1" > "$2"</programlisting>\r
+    </pre>\r
+    <para />\r
+    Alternatively:\r
+    <pre>\r
+      <programlisting xml:space="preserve">#! /bin/bash\r
+        gm convert "$1" "$2"</programlisting>\r
+    </pre>\r
+  </para>\r
+  <para>\r
+    If the decode tool requires an output file with a particular extension, use this method:\r
+    <pre>\r
+      <programlisting xml:space="preserve">#! /bin/bash\r
+        tmpfile=$(mktemp --tmpdir=$tempdir geeqie_tmp_XXXXXX.jpg)\r
+        gm convert "$1" $tmpfile\r
+        mv $tmpfile "$2"</programlisting>\r
+    </pre>\r
+  </para>\r
+</section>\r
index 9b927dd..3f7feb6 100644 (file)
@@ -33,6 +33,7 @@
     <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="GuideOptionsStereo.xml" />\r
     <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="GuideOptionsBehavior.xml" />\r
     <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="GuideOptionsToolbar.xml" />\r
+    <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="GuideOptionsAdvanced.xml" />\r
     <para />\r
 \r
 </chapter>\r
diff --git a/doc/docbook/GuideOtherSoftware.xml b/doc/docbook/GuideOtherSoftware.xml
new file mode 100644 (file)
index 0000000..52765dd
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<chapter id="GuideOtherSoftware">\r
+  <title id="titleGuideOtherSoftware">Other Software</title>\r
+  <para>This is a list of other software that users might find useful in conjunction with Geeqie.</para>\r
+  <section id="Entangle">\r
+    <title>Entangle</title>\r
+    <para>\r
+      Tethered photography:\r
+      <ulink url="https://entangle-photo.org/">https://entangle-photo.org/</ulink>\r
+    </para>\r
+  </section>\r
+  <section id="Photini">\r
+    <title>Photini</title>\r
+    <para>\r
+      Digital photograph metadata editor:\r
+      <ulink url="https://photini.readthedocs.io/en/latest/">https://photini.readthedocs.io/en/latest/</ulink>\r
+    </para>\r
+  </section>\r
+</chapter>\r
index a8c62f3..88f936d 100644 (file)
@@ -6,6 +6,7 @@
   <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="GuideReferenceThumbnails.xml" />\r
   <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="GuideReferenceMetadata.xml" />\r
   <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="GuideReferenceLua.xml" />\r
+  <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="GuideReferenceLuaAPI.xml" />\r
   <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="GuideReferenceConfig.xml" />\r
   <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="GuideReferenceFileDates.xml" />\r
   <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="GuideReferenceLIRC.xml" />\r
index 3a1aecf..34b7ad8 100644 (file)
             <entry>\r
               <code>\r
                 Ctrl +\r
-                <keycap>Keypad +</keycap>\r
+                <keycap>=</keycap>\r
               </code>\r
             </entry>\r
             <entry />\r
             <entry>\r
               <code>\r
                 Ctrl +\r
-                <keycap>Keypad -</keycap>\r
+                <keycap>-</keycap>\r
               </code>\r
             </entry>\r
             <entry />\r
diff --git a/doc/docbook/GuideReferenceLuaAPI.xml b/doc/docbook/GuideReferenceLuaAPI.xml
new file mode 100644 (file)
index 0000000..5d36e66
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<section id="GuideReferenceLuaAPI">\r
+  <title>Lua API</title>\r
+  <para>\r
+    <ulink url="./lua-api/lua_8c.html">The Lua API document</ulink>\r
+  </para>\r
+</section>\r
index bb43b57..e6676b9 100644 (file)
@@ -11,7 +11,7 @@
   </para>\r
   <para>\r
     XMP:\r
-    <ulink url="http://www.adobe.com/content/dam/Adobe/en/devnet/xmp/pdfs/XMPSpecificationPart3.pdf" />\r
+    <ulink url="https://wwwimages2.adobe.com/content/dam/acom/en/devnet/xmp/pdfs/XMP%20SDK%20Release%20cc-2016-08/XMPSpecificationPart3.pdf" />\r
   </para>\r
   <para>\r
     IPTC4XMP:\r
@@ -19,7 +19,7 @@
   </para>\r
   <para>\r
     Pango mark up:\r
-    <ulink url="https://developer.gnome.org/pango/stable/PangoMarkupFormat.html" />\r
+    <ulink url="https://developer.gnome.org/pygtk/stable/pango-markup-language.html" />\r
   </para>\r
   <para>\r
     Thumbnails:\r
index d952f4b..074a2d7 100644 (file)
@@ -1001,14 +1001,14 @@ SOURCE_BROWSER         = YES
 # classes and enums directly into the documentation.
 # The default value is: NO.
 
-INLINE_SOURCES         = NO
+INLINE_SOURCES         = $(INLINE_SOURCES)
 
 # Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
 # special comment blocks from generated source code fragments. Normal C, C++ and
 # Fortran comments will always remain visible.
 # The default value is: YES.
 
-STRIP_CODE_COMMENTS    = YES
+STRIP_CODE_COMMENTS    = $(STRIP_CODE_COMMENTS)
 
 # If the REFERENCED_BY_RELATION tag is set to YES then for each documented
 # function all documented functions referencing it will be listed.
@@ -2435,7 +2435,7 @@ DIAFILE_DIRS           =
 # generate a warning when it encounters a \startuml command in this case and
 # will not generate output for the diagram.
 
-PLANTUML_JAR_PATH      =
+PLANTUML_JAR_PATH      = $(PLANTUML_JAR_PATH)
 
 # When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
 # configuration file for plantuml.
index da33191..cb5a0b0 100644 (file)
@@ -23,7 +23,7 @@
     </screenshot>
   </screenshots>
   <releases>
-    <release version="v1.5.1" date="2019-08-20"></release>
+    <release version="v1.6" date="2020-12-02"></release>
   </releases>
   <keywords>
     <keyword>image</keyword>
index 56cac61..76a4dad 100644 (file)
--- a/po/sk.po
+++ b/po/sk.po
@@ -10,7 +10,7 @@ msgstr ""
 "Project-Id-Version: geeqie-1.3\n"
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2021-05-01 10:24+0100\n"
-"PO-Revision-Date: 2021-05-16 09:37+0200\n"
+"PO-Revision-Date: 2021-06-26 22:12+0200\n"
 "Last-Translator: Jose Riha <jose1711@gmail.com>\n"
 "Language-Team: Peter Tuhársky <tuharsky@misbb.sk>\n"
 "Language: sk_SK\n"
@@ -1215,7 +1215,7 @@ msgstr "Tlač..."
 # src/collect-table.c:1818 src/dupe.c:2170
 #: ../src/collect-table.c:2236 ../src/dupe.c:4894 ../src/img-view.c:1521
 msgid "Dropped list includes folders."
-msgstr "Odstraňovaný zoznam obsahuje priečinky."
+msgstr "Umiestnený zoznam obsahuje priečinky."
 
 # src/collect-table.c:1820 src/dupe.c:2172
 #: ../src/collect-table.c:2238 ../src/dupe.c:4896 ../src/img-view.c:1523
@@ -5971,9 +5971,8 @@ msgstr "Použiť uložené pozície okien aj pre nové okná"
 
 # src/preferences.c:782
 #: ../src/preferences.c:2307
-#, fuzzy
 msgid "Remember window workspace"
-msgstr "Pamätať si pozície okien"
+msgstr "Pamätať si plochu"
 
 # src/preferences.c:784
 #: ../src/preferences.c:2311
@@ -6410,13 +6409,12 @@ msgstr ""
 
 # src/collect-dlg.c:206
 #: ../src/preferences.c:3355
-#, fuzzy
 msgid "Circular selection lists"
-msgstr "Zbierka už existuje"
+msgstr "Výbery v nekonečnej slučke"
 
 #: ../src/preferences.c:3357
 msgid "Traverse selection lists in a circular manner"
-msgstr ""
+msgstr "Prechádzať zoznamy s výberom v nekonečnej slučke"
 
 #: ../src/preferences.c:3359
 msgid "Save marks on exit"
diff --git a/scripts/generate-appimage.sh b/scripts/generate-appimage.sh
new file mode 100755 (executable)
index 0000000..0b7639a
--- /dev/null
@@ -0,0 +1,65 @@
+#! /bin/bash
+#**********************************************************************
+# Copyright (C) 2021 - The Geeqie Team
+#
+# Author: Colin Clark
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#**********************************************************************
+#
+# This script will generate a Geeqie AppImage.
+#
+# It must be run from the base Geeqie folder.
+# The single parameter is the directory where the AppDir
+# will be created.
+#
+#**********************************************************************
+
+
+if [[ ! -f geeqie.spec.in ]] || [[ ! -d .git ]]
+then
+       echo "This is not a Geeqie folder"
+       exit 1
+fi
+
+target_dir=$(realpath $1)
+
+if [[ $? -ne 0 ]]
+then
+       echo "No target dir specified"
+       exit 1
+fi
+
+rm -rf "$target_dir"/AppDir
+mkdir "$target_dir"/AppDir
+
+sudo rm -rf doc/html
+
+sudo make maintainer-clean
+./autogen.sh --prefix="/usr/"
+make -j
+make install DESTDIR="$target_dir"/AppDir
+
+export VERSION=$(git tag | tail -1)
+
+cd $target_dir
+
+linuxdeploy-x86_64.AppImage \
+--appdir ./AppDir --output appimage \
+--desktop-file ./AppDir/usr/share/applications/geeqie.desktop \
+--icon-file ./AppDir/usr/share/pixmaps/geeqie.png \
+--executable ./AppDir/usr/bin/geeqie
+
+mv ./Geeqie-$VERSION-x86_64.AppImage $(./Geeqie-$VERSION-x86_64.AppImage -v | sed 's/git//' | sed 's/-.* /-/' | sed 's/ /-v/' | sed 's/-GTK3//').AppImage
index bff64e1..339af63 100644 (file)
@@ -191,6 +191,8 @@ geeqie_SOURCES = \
        image_load_tiff.h\
        image_load_dds.c\
        image_load_dds.h\
+       image_load_external.c\
+       image_load_external.h\
        image_load_collection.c\
        image_load_collection.h\
        image_load_pdf.c\
index 4142666..c74cc88 100644 (file)
@@ -381,7 +381,7 @@ static void editor_list_window_edit_cb(GtkWidget *widget, gpointer data)
 
 static void editor_list_window_new_cb(GtkWidget *widget, gpointer data)
 {
-       editor_window_new(DESKTOP_FILE_TEMPLATE, _("new.desktop"));
+       editor_window_new(desktop_file_template, _("new.desktop"));
 }
 
 static void editor_list_window_help_cb(GtkWidget *widget, gpointer data)
index 4002d50..f9c624e 100644 (file)
@@ -128,6 +128,7 @@ static GtkWidget *dupe_menu_popup_second(DupeWindow *dw, DupeItem *di);
 static void dupe_dnd_init(DupeWindow *dw);
 
 static void dupe_notify_cb(FileData *fd, NotifyType type, gpointer data);
+static void delete_finished_cb(gboolean success, const gchar *dest_path, gpointer data);
 
 static GtkWidget *submenu_add_export(GtkWidget *menu, GtkWidget **menu_item, GCallback func, gpointer data);
 static void dupe_pop_menu_export_cb(GtkWidget *widget, gpointer data);
@@ -1849,6 +1850,7 @@ static void dupe_array_check(DupeWindow *dw )
        DupeMatchType mask = dw->match_mask;
        param_match_mask = dw->match_mask;
        guint out_match_index;
+       gboolean match_found = FALSE;;
 
        if (!dw->list) return;
 
@@ -1894,7 +1896,27 @@ static void dupe_array_check(DupeWindow *dw )
                                                continue;
                                                }
                                        }
-                               if (g_array_binary_search(array_set2, di1, dupe_match_binary_search_cb, &out_match_index))
+
+#if ((GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION >= 62))
+                               match_found = g_array_binary_search(array_set2, di1, dupe_match_binary_search_cb, &out_match_index);
+#else
+                               gint i;
+
+                               match_found = FALSE;
+                               for(i=0; i < array_set2->len; i++)
+                                       {
+                                       di2 = g_array_index(array_set2,  gpointer, i);
+                                       check_result = dupe_match_check(di1, di2, dw);
+                                       if (check_result == DUPE_MATCH)
+                                               {
+                                               match_found = TRUE;
+                                               out_match_index = i;
+                                               break;
+                                               }
+                                       }
+#endif
+
+                               if (match_found)
                                        {
                                        di2 = g_array_index(array_set2, gpointer, out_match_index);
 
@@ -3361,7 +3383,7 @@ static void dupe_menu_delete_cb(GtkWidget *widget, gpointer data)
        DupeWindow *dw = data;
 
        options->file_ops.safe_delete_enable = FALSE;
-       file_util_delete(NULL, dupe_listview_get_selection(dw, dw->listview), dw->window);
+       file_util_delete_notify_done(NULL, dupe_listview_get_selection(dw, dw->listview), dw->window, delete_finished_cb, dw);
 }
 
 static void dupe_menu_move_to_trash_cb(GtkWidget *widget, gpointer data)
@@ -3369,7 +3391,7 @@ static void dupe_menu_move_to_trash_cb(GtkWidget *widget, gpointer data)
        DupeWindow *dw = data;
 
        options->file_ops.safe_delete_enable = TRUE;
-       file_util_delete(NULL, dupe_listview_get_selection(dw, dw->listview), dw->window);
+       file_util_delete_notify_done(NULL, dupe_listview_get_selection(dw, dw->listview), dw->window, delete_finished_cb, dw);
 }
 
 static void dupe_menu_copy_path_cb(GtkWidget *widget, gpointer data)
@@ -5113,7 +5135,7 @@ static void dupe_notify_cb(FileData *fd, NotifyType type, gpointer data)
                case FILEDATA_CHANGE_COPY:
                        break;
                case FILEDATA_CHANGE_DELETE:
-                       while (dupe_item_remove_by_path(dw, fd->path));
+                       /* Update the UI only once, after the operation finishes */
                        break;
                case FILEDATA_CHANGE_UNSPECIFIED:
                case FILEDATA_CHANGE_WRITE_METADATA:
@@ -5122,6 +5144,29 @@ static void dupe_notify_cb(FileData *fd, NotifyType type, gpointer data)
 
 }
 
+/**
+ * @brief Refresh window after a file delete operation
+ * @param success (ud->phase != UTILITY_PHASE_CANCEL) #file_util_dialog_run
+ * @param dest_path Not used
+ * @param data #DupeWindow
+ * 
+ * If the window is refreshed after each file of a large set is deleted,
+ * the UI slows to an unacceptable level. The #FileUtilDoneFunc is used
+ * to call this function once, when the entire delete operation is completed.
+ */
+static void delete_finished_cb(gboolean success, const gchar *dest_path, gpointer data)
+{
+       DupeWindow *dw = data;
+       GList *work;
+
+       if (!success)
+               {
+               return;
+               }
+
+       dupe_window_remove_selection(dw, dw->listview);
+}
+
 /*
  *-------------------------------------------------------------------
  * Export duplicates data
index b2ce46d..902e736 100644 (file)
@@ -458,7 +458,7 @@ GList *editor_get_desktop_files(void)
        else
                xdg_data_dirs = g_strdup("/usr/share");
 
-       all_dirs = g_strconcat(get_rc_dir(), ":", GQ_APP_DIR, ":", xdg_data_home_get(), ":", xdg_data_dirs, NULL);
+       all_dirs = g_strconcat(get_rc_dir(), ":", gq_app_dir, ":", xdg_data_home_get(), ":", xdg_data_dirs, NULL);
 
        g_free(xdg_data_dirs);
 
index f1f7290..f876127 100644 (file)
@@ -451,7 +451,7 @@ void exif_init(void)
        bind_textdomain_codeset (EXV_PACKAGE, "UTF-8");
 #endif
 
-#if EXIV2_TEST_VERSION(0,27,4)
+#ifdef EXV_ENABLE_BMFF
        Exiv2::enableBMFF(TRUE);
 #endif
 }
index 0ddf32c..07588cb 100644 (file)
@@ -27,6 +27,7 @@
 #include "image_load_tiff.h"
 #include "image_load_dds.h"
 #include "image_load_djvu.h"
+#include "image_load_external.h"
 #include "image_load_pdf.h"
 #include "image_load_psd.h"
 #include "image_load_heif.h"
@@ -36,6 +37,7 @@
 #include "image_load_j2k.h"
 #include "image_load_libraw.h"
 #include "image_load_svgz.h"
+#include "misc.h"
 
 #include "exif.h"
 #include "filedata.h"
 #define IMAGE_LOADER_READ_BUFFER_SIZE_DEFAULT  4096
 #define IMAGE_LOADER_IDLE_READ_LOOP_COUNT_DEFAULT      1
 
-
+/**
+ * @page diagrams Diagrams
+ * @section image_load_overview Image Load Overview
+ * @startuml
+ * object image_change
+ * object image_change_complete
+ * object image_load_begin
+ * object image_loader_start
+ * object image_loader_start_thread
+ * object image_loader_start_idle
+ * object image_loader_setup_source
+ * object image_loader_thread_run
+ * object image_loader_begin
+ * object image_loader_setuploader
+ * circle "il->memory_mapped"
+ * object exif_get_preview_
+ * object exif_get_preview
+ * object libraw_get_preview
+ * 
+ * image_change : image.c
+ * image_change_complete : image.c
+ * image_load_begin : image.c
+ * image_loader_start : image_load.c
+ * image_loader_start_thread : image_load.c
+ * image_loader_start_idle : image_load.c
+ * image_loader_thread_run : image_load.c
+ * image_loader_begin : image_load.c
+ * image_loader_setuploader : image_load.c
+ * image_loader_setuploader : -
+ * image_loader_setuploader : Select backend using magic
+ * image_loader_setup_source : image_load.c
+ * exif_get_preview : exiv2.cc
+ * exif_get_preview : EXIV2_TEST_VERSION(0,17,90)
+ * exif_get_preview_ : exif.c
+ * exif_get_preview_ : -
+ * exif_get_preview_ : If exiv2 not installed
+ * libraw_get_preview : image_load_libraw.c
+ * 
+ * image_change --> image_change_complete
+ * image_change_complete --> image_load_begin
+ * image_load_begin --> image_loader_start
+ * image_loader_start --> image_loader_start_thread
+ * image_loader_start --> image_loader_start_idle : Obsolete - no threads version
+ * image_loader_start_thread --> image_loader_thread_run
+ * image_loader_start_thread --> image_loader_setup_source
+ * image_loader_setup_source --> exif_get_preview_
+ * image_loader_setup_source --> exif_get_preview
+ * image_loader_setup_source --> libraw_get_preview : Try libraw if exiv2 fails
+ * exif_get_preview_ ..> "il->memory_mapped"
+ * exif_get_preview ..> "il->memory_mapped"
+ * libraw_get_preview ..> "il->memory_mapped"
+ * image_loader_thread_run --> image_loader_begin
+ * image_loader_begin --> image_loader_setuploader
+ * "il->memory_mapped" ..> image_loader_setuploader
+ * note left of "il->memory_mapped" : Points to first byte of embedded jpeg (#FFD8)\n if preview found, otherwise to first byte of file
+ * @enduml
+ */
+ /**
+  * @file
+  * @ref image_load_overview "Image Load Overview"
+  */
+  
 /**************************************************************************************/
 /* image loader class */
 
@@ -629,7 +692,31 @@ static void image_loader_setup_loader(ImageLoader *il)
        gchar *format;
 #endif
 
+       gint external_preview = 1;
+
        g_mutex_lock(il->data_mutex);
+
+       if (options->external_preview.enable)
+               {
+               gchar *cmd_line;
+               gchar *tilde_filename;
+
+               tilde_filename = expand_tilde(options->external_preview.select);
+
+               cmd_line = g_strdup_printf("\"%s\" \"%s\"" , tilde_filename, il->fd->path);
+
+               external_preview = runcmd(cmd_line);
+               g_free(cmd_line);
+               g_free(tilde_filename);
+               }
+
+       if (external_preview == 0)
+               {
+               DEBUG_1("Using custom external loader");
+               image_loader_backend_set_external(&il->backend);
+               }
+       else
+
 #ifdef HAVE_FFMPEGTHUMBNAILER
        if (il->fd->format_class == FORMAT_CLASS_VIDEO)
                {
@@ -651,7 +738,8 @@ static void image_loader_setup_loader(ImageLoader *il)
        if (il->bytes_total >= 12 &&
                ((memcmp(il->mapped_file + 4, "ftypheic", 8) == 0) ||
                (memcmp(il->mapped_file + 4, "ftypmsf1", 8) == 0) ||
-               (memcmp(il->mapped_file + 4, "ftypmif1", 8) == 0)))
+               (memcmp(il->mapped_file + 4, "ftypmif1", 8) == 0) ||
+               (memcmp(il->mapped_file + 4, "ftypavif", 8) == 0)))
                {
                DEBUG_1("Using custom heif loader");
                image_loader_backend_set_heif(&il->backend);
@@ -972,7 +1060,16 @@ static gboolean image_loader_setup_source(ImageLoader *il)
 
                        if (il->mapped_file)
                                {
-                               il->preview = IMAGE_LOADER_PREVIEW_EXIF;
+                               /* Both exiv2 and libraw sometimes return a pointer to a file
+                                * section that is not a jpeg */
+                               if (!(il->mapped_file[0] == 0xFF && il->mapped_file[1] == 0xD8))
+                                       {
+                                       il->mapped_file = NULL;
+                                       }
+                               else
+                                       {
+                                       il->preview = IMAGE_LOADER_PREVIEW_EXIF;
+                                       }
                                }
                        }
 
@@ -983,7 +1080,14 @@ static gboolean image_loader_setup_source(ImageLoader *il)
 
                        if (il->mapped_file)
                                {
-                               il->preview = IMAGE_LOADER_PREVIEW_LIBRAW;
+                               if (!(il->mapped_file[0] == 0xFF && il->mapped_file[1] == 0xD8))
+                                       {
+                                       il->mapped_file = NULL;
+                                       }
+                               else
+                                       {
+                                       il->preview = IMAGE_LOADER_PREVIEW_LIBRAW;
+                                       }
                                }
                        }
 
diff --git a/src/image_load_external.c b/src/image_load_external.c
new file mode 100644 (file)
index 0000000..7eb1770
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2021 - The Geeqie Team
+ *
+ * Author: Colin Clark
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "main.h"
+
+#include "image-load.h"
+#include "image_load_external.h"
+
+#include "misc.h"
+#include "ui_fileops.h"
+
+typedef struct _ImageLoaderExternal ImageLoaderExternal;
+struct _ImageLoaderExternal {
+       ImageLoaderBackendCbAreaUpdated area_updated_cb;
+       ImageLoaderBackendCbSize size_cb;
+       ImageLoaderBackendCbAreaPrepared area_prepared_cb;
+       gpointer data;
+       GdkPixbuf *pixbuf;
+       guint requested_width;
+       guint requested_height;
+       gboolean abort;
+};
+
+static gboolean image_loader_external_load(gpointer loader, const guchar *buf, gsize count, GError **error)
+{
+       ImageLoaderExternal *ld = (ImageLoaderExternal *) loader;
+       ImageLoader *il = ld->data;
+       gchar *cmd_line;
+       gchar *randname;
+       gchar *tilde_filename;
+
+       tilde_filename = expand_tilde(options->external_preview.extract);
+
+       randname = g_strdup("/tmp/geeqie_external_preview_XXXXXX");
+       g_mkstemp(randname);
+
+       cmd_line = g_strdup_printf("\"%s\" \"%s\" \"%s\"" , tilde_filename, il->fd->path, randname);
+
+       runcmd(cmd_line);
+
+       ld->pixbuf = gdk_pixbuf_new_from_file(randname, NULL);
+
+       ld->area_updated_cb(loader, 0, 0, gdk_pixbuf_get_width(ld->pixbuf), gdk_pixbuf_get_height(ld->pixbuf), ld->data);
+
+       g_free(cmd_line);
+       unlink_file(randname);
+       g_free(randname);
+       g_free(tilde_filename);
+
+       return TRUE;
+}
+
+static gpointer image_loader_external_new(ImageLoaderBackendCbAreaUpdated area_updated_cb, ImageLoaderBackendCbSize size_cb, ImageLoaderBackendCbAreaPrepared area_prepared_cb, gpointer data)
+{
+       ImageLoaderExternal *loader = g_new0(ImageLoaderExternal, 1);
+       loader->area_updated_cb = area_updated_cb;
+       loader->size_cb = size_cb;
+       loader->area_prepared_cb = area_prepared_cb;
+       loader->data = data;
+       return (gpointer) loader;
+}
+
+static void image_loader_external_set_size(gpointer loader, int width, int height)
+{
+       ImageLoaderExternal *ld = (ImageLoaderExternal *) loader;
+       ld->requested_width = width;
+       ld->requested_height = height;
+}
+
+static GdkPixbuf* image_loader_external_get_pixbuf(gpointer loader)
+{
+       ImageLoaderExternal *ld = (ImageLoaderExternal *) loader;
+       return ld->pixbuf;
+}
+
+static gchar* image_loader_external_get_format_name(gpointer loader)
+{
+       return g_strdup("external");
+}
+
+static gchar** image_loader_external_get_format_mime_types(gpointer loader)
+{
+       static gchar *mime[] = {"application/octet-stream", NULL};
+       return g_strdupv(mime);
+}
+
+static gboolean image_loader_external_close(gpointer loader, GError **error)
+{
+       return TRUE;
+}
+
+static void image_loader_external_abort(gpointer loader)
+{
+       ImageLoaderExternal *ld = (ImageLoaderExternal *) loader;
+       ld->abort = TRUE;
+}
+
+static void image_loader_external_free(gpointer loader)
+{
+       ImageLoaderExternal *ld = (ImageLoaderExternal *) loader;
+       if (ld->pixbuf) g_object_unref(ld->pixbuf);
+       g_free(ld);
+}
+
+void image_loader_backend_set_external(ImageLoaderBackend *funcs)
+{
+       funcs->loader_new = image_loader_external_new;
+       funcs->set_size = image_loader_external_set_size;
+       funcs->load = image_loader_external_load;
+       funcs->write = NULL;
+       funcs->get_pixbuf = image_loader_external_get_pixbuf;
+       funcs->close = image_loader_external_close;
+       funcs->abort = image_loader_external_abort;
+       funcs->free = image_loader_external_free;
+       funcs->get_format_name = image_loader_external_get_format_name;
+       funcs->get_format_mime_types = image_loader_external_get_format_mime_types;
+}
+
+
+/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
diff --git a/src/image_load_external.h b/src/image_load_external.h
new file mode 100644 (file)
index 0000000..a6929d8
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2021 - The Geeqie Team
+ *
+ * Author: Colin Clark
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef IMAGE_LOAD_EXTERNAL_H
+#define IMAGE_LOAD_EXTERNAL_H
+
+void image_loader_backend_set_external(ImageLoaderBackend *funcs);
+
+#endif
+/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
index 26b12e6..37fe728 100644 (file)
@@ -660,6 +660,10 @@ void layout_status_update_progress(LayoutWindow *lw, gdouble val, const gchar *t
 void layout_status_update_info(LayoutWindow *lw, const gchar *text)
 {
        gchar *buf = NULL;
+       gint hrs;
+       gint min;
+       gdouble sec;
+       GString *delay;
 
        if (!layout_valid(&lw)) return;
 
@@ -674,22 +678,40 @@ void layout_status_update_info(LayoutWindow *lw, const gchar *text)
                        {
                        guint s;
                        gint64 s_bytes = 0;
-                       const gchar *ss;
+                       gchar *ss;
 
                        if (layout_image_slideshow_active(lw))
                                {
+
                                if (!layout_image_slideshow_paused(lw))
                                        {
-                                       ss = _(" Slideshow");
+                                       delay = g_string_new(_(" Slideshow ["));
                                        }
                                else
                                        {
-                                       ss = _(" Paused");
+                                       delay = g_string_new(_(" Paused ["));
+                                       }
+                               hrs = options->slideshow.delay / (36000);
+                               min = (options->slideshow.delay -(36000 * hrs))/600;
+                               sec = (gdouble)(options->slideshow.delay -(36000 * hrs)-(min * 600)) / 10;
+
+                               if (hrs > 0)
+                                       {
+                                       g_string_append_printf(delay, "%dh ", hrs);
+                                       }
+                               if (min > 0)
+                                       {
+                                       g_string_append_printf(delay, "%dm ", min);
                                        }
+                               g_string_append_printf(delay, "%.1fs]", sec);
+
+                               ss = g_strdup(delay->str);
+
+                               g_string_free(delay, TRUE);
                                }
                        else
                                {
-                               ss = "";
+                               ss = g_strdup("");
                                }
 
                        s = layout_selection_count(lw, &s_bytes);
@@ -703,16 +725,19 @@ void layout_status_update_info(LayoutWindow *lw, const gchar *text)
                                buf = g_strdup_printf(_("%s, %d files (%s, %d)%s"), b, n, sb, s, ss);
                                g_free(b);
                                g_free(sb);
+                               g_free(ss);
                                }
                        else if (n > 0)
                                {
                                gchar *b = text_from_size_abrev(n_bytes);
                                buf = g_strdup_printf(_("%s, %d files%s"), b, n, ss);
                                g_free(b);
+                               g_free(ss);
                                }
                        else
                                {
                                buf = g_strdup_printf(_("%d files%s"), n, ss);
+                               g_free(ss);
                                }
 
                        text = buf;
@@ -879,7 +904,7 @@ static void layout_status_setup(LayoutWindow *lw, GtkWidget *box, gboolean small
 
        lw->info_status = layout_status_label(NULL, lw->info_box, TRUE, 0, (!small_format));
        DEBUG_NAME(lw->info_status);
-       gtk_widget_set_tooltip_text(GTK_WIDGET(lw->info_status), _("Folder contents (files selected)"));
+       gtk_widget_set_tooltip_text(GTK_WIDGET(lw->info_status), _("Folder contents (files selected)\nSlideshow [time interval]"));
 
        if (small_format)
                {
@@ -2780,6 +2805,12 @@ void layout_write_attributes(LayoutOptions *layout, GString *outstr, gint indent
        WRITE_NL(); WRITE_INT(*layout, log_window.w);
        WRITE_NL(); WRITE_INT(*layout, log_window.h);
 
+       WRITE_NL(); WRITE_INT(*layout, preferences_window.x);
+       WRITE_NL(); WRITE_INT(*layout, preferences_window.y);
+       WRITE_NL(); WRITE_INT(*layout, preferences_window.w);
+       WRITE_NL(); WRITE_INT(*layout, preferences_window.h);
+       WRITE_NL(); WRITE_INT(*layout, preferences_window.page_number);
+
        WRITE_NL(); WRITE_INT(*layout, search_window.x);
        WRITE_NL(); WRITE_INT(*layout, search_window.y);
        WRITE_NL(); WRITE_INT(*layout, search_window.w);
@@ -2883,6 +2914,12 @@ void layout_load_attributes(LayoutOptions *layout, const gchar **attribute_names
                if (READ_INT(*layout, log_window.w)) continue;
                if (READ_INT(*layout, log_window.h)) continue;
 
+               if (READ_INT(*layout, preferences_window.x)) continue;
+               if (READ_INT(*layout, preferences_window.y)) continue;
+               if (READ_INT(*layout, preferences_window.w)) continue;
+               if (READ_INT(*layout, preferences_window.h)) continue;
+               if (READ_INT(*layout, preferences_window.page_number)) continue;
+
                if (READ_INT(*layout, search_window.x)) continue;
                if (READ_INT(*layout, search_window.y)) continue;
                if (READ_INT(*layout, search_window.w)) continue;
index b24f6e2..a9c7da7 100644 (file)
@@ -602,7 +602,7 @@ static void layout_menu_write_rotate(GtkToggleAction *action, gpointer data, gbo
                        }
 
                rotation = g_strdup_printf("%d", fd_n->user_orientation);
-               command = g_strconcat(GQ_BIN_DIR, "/geeqie-rotate -r ", rotation,
+               command = g_strconcat(gq_bin_dir, "/geeqie-rotate -r ", rotation,
                                                                keep_date ? " -t \"" : " \"", fd_n->path, "\"", NULL);
                cmdstatus = runcmd(command);
                run_result = WEXITSTATUS(cmdstatus);
@@ -661,7 +661,7 @@ static void layout_menu_config_cb(GtkAction *action, gpointer data)
        LayoutWindow *lw = data;
 
        layout_exit_fullscreen(lw);
-       show_config_window();
+       show_config_window(lw);
 }
 
 static void layout_menu_editors_cb(GtkAction *action, gpointer data)
@@ -2557,8 +2557,8 @@ static GtkActionEntry menu_entries[] = {
   { "HistogramModeCycle",NULL,                 N_("Cycle through histogram mo_des"),   "J",                    N_("Cycle through histogram modes"),    CB(layout_menu_histogram_toggle_mode_cb) },
   { "HideTools",       PIXBUF_INLINE_ICON_HIDETOOLS,   N_("_Hide file list"),                  "<control>H",           N_("Hide file list"),                   CB(layout_menu_hide_cb) },
   { "SlideShowPause",  GTK_STOCK_MEDIA_PAUSE,  N_("_Pause slideshow"),                 "P",                    N_("Pause slideshow"),                  CB(layout_menu_slideshow_pause_cb) },
-  { "SlideShowFaster", GTK_STOCK_FILE, N_("Faster"),           "<control>KP_Add",                      N_("Slideshow Faster"),                         CB(layout_menu_slideshow_faster_cb) },
-  { "SlideShowSlower", GTK_STOCK_FILE, N_("Slower"),           "<control>KP_Subtract",                 N_("Slideshow Slower"),                         CB(layout_menu_slideshow_slower_cb) },
+  { "SlideShowFaster", GTK_STOCK_FILE, N_("Faster"),           "<control>equal",                       N_("Slideshow Faster"),                         CB(layout_menu_slideshow_faster_cb) },
+  { "SlideShowSlower", GTK_STOCK_FILE, N_("Slower"),           "<control>minus",                       N_("Slideshow Slower"),                         CB(layout_menu_slideshow_slower_cb) },
   { "Refresh",         GTK_STOCK_REFRESH,      N_("_Refresh"),                         "R",                    N_("Refresh"),                          CB(layout_menu_refresh_cb) },
   { "HelpContents",    GTK_STOCK_HELP,         N_("_Contents"),                        "F1",                   N_("Contents"),                         CB(layout_menu_help_cb) },
   { "HelpSearch",      NULL,           N_("On-line help search"),                      NULL,                   N_("On-line help search"),                              CB(layout_menu_help_search_cb) },
index 384037f..cb0eb65 100644 (file)
--- a/src/lua.c
+++ b/src/lua.c
 #include "ui_fileops.h"
 #include "exif.h"
 
+/**
+ * @file
+ * User API consists of the following namespaces:
+ * 
+ * @link image_methods Image:@endlink basic image information
+ *
+ * <b>Collection</b>: not implemented
+ * 
+ * @link exif_methods <exif-structure>:get_datum() @endlink get single exif parameter
+ *
+ */
+
 static lua_State *L; /** The LUA object needed for all operations (NOTE: That is
                       * a upper-case variable to match the documentation!) */
 
@@ -69,6 +81,13 @@ static FileData *lua_check_image(lua_State *L, int index)
        return *fd;
 }
 
+/**
+ * @brief Get exif structure of selected image
+ * @param L 
+ * @returns An @ref ExifData data structure containing the entire exif data
+ * 
+ * To be used in conjunction with @link lua_exif_get_datum <exif-structure>:get_datum() @endlink
+ */
 static int lua_image_get_exif(lua_State *L)
 {
        FileData *fd;
@@ -87,6 +106,13 @@ static int lua_image_get_exif(lua_State *L)
        return 1;
 }
 
+/**
+ * @brief Get full path of selected image
+ * @param L 
+ * @returns char The full path of the file, including filename and extension
+ * 
+ * 
+ */
 static int lua_image_get_path(lua_State *L)
 {
        FileData *fd;
@@ -96,6 +122,13 @@ static int lua_image_get_path(lua_State *L)
        return 1;
 }
 
+/**
+ * @brief Get full filename of selected image
+ * @param L 
+ * @returns char The full filename including extension
+ * 
+ * 
+ */
 static int lua_image_get_name(lua_State *L)
 {
        FileData *fd;
@@ -105,6 +138,13 @@ static int lua_image_get_name(lua_State *L)
        return 1;
 }
 
+/**
+ * @brief Get file extension of selected image
+ * @param L 
+ * @returns char The file extension including preceding dot
+ * 
+ * 
+ */
 static int lua_image_get_extension(lua_State *L)
 {
        FileData *fd;
@@ -114,6 +154,14 @@ static int lua_image_get_extension(lua_State *L)
        return 1;
 }
 
+/**
+ * @brief Get file date of selected image
+ * @param L 
+ * @returns time_t The file date in Unix timestamp format.
+ * 
+ * time_t - signed integer which represents the number of seconds since
+ * the start of the Unix epoch: midnight UTC of January 1, 1970
+ */
 static int lua_image_get_date(lua_State *L)
 {
        FileData *fd;
@@ -123,6 +171,13 @@ static int lua_image_get_date(lua_State *L)
        return 1;
 }
 
+/**
+ * @brief Get file size of selected image
+ * @param L 
+ * @returns integer The file size in bytes
+ * 
+ * 
+ */
 static int lua_image_get_size(lua_State *L)
 {
        FileData *fd;
@@ -132,6 +187,15 @@ static int lua_image_get_size(lua_State *L)
        return 1;
 }
 
+/**
+ * @brief Get marks of selected image
+ * @param L 
+ * @returns unsigned integer Bit map of marks set
+ * 
+ * Bit 0 == Mark 1 etc.
+ * 
+ * 
+ */
 static int lua_image_get_marks(lua_State *L)
 {
        FileData *fd;
@@ -150,7 +214,21 @@ static ExifData *lua_check_exif(lua_State *L, int index)
        return *exif;
 }
 
-/* Interface for EXIF data */
+/**
+ * @brief Interface for EXIF data
+ * @param L 
+ * @returns <i>return</i> A single exif tag extracted from a structure output by the @link lua_image_get_exif Image:get_exif() @endlink command
+ * 
+ * e.g. \n
+ * exif_structure = Image:get_exif(); \n
+ * DateTimeDigitized = exif_structure:get_datum("Exif.Photo.DateTimeDigitized");
+ *
+ * Where <i>return</i> is: \n
+ * Exif.Photo.DateTimeOriginal = signed integer time_t \n
+ * Exif.Photo.DateTimeDigitized = signed integer time_t \n
+ * otherwise char
+ * 
+ */
 static int lua_exif_get_datum(lua_State *L)
 {
        const gchar *key;
@@ -206,6 +284,37 @@ static int lua_exif_get_datum(lua_State *L)
        return 1;
 }
 
+/**
+ * @brief  <b>Image:</b> metatable and methods \n
+ * Call by e.g. \n
+ * path_name = @link lua_image_get_path Image:getpath() @endlink \n
+ * where the keyword <b>Image</b> represents the currently selected image
+ */
+static const luaL_Reg image_methods[] = {
+               {"get_path", lua_image_get_path},
+               {"get_name", lua_image_get_name},
+               {"get_extension", lua_image_get_extension},
+               {"get_date", lua_image_get_date},
+               {"get_size", lua_image_get_size},
+               {"get_exif", lua_image_get_exif},
+               {"get_marks", lua_image_get_marks},
+               {NULL, NULL}
+};
+
+/**
+ * @brief  <b>exif:</b> table and methods \n
+ * Call by e.g. \n
+ * @link lua_exif_get_datum <exif-structure>:get_datum() @endlink \n
+ * where <exif-structure> is the output of @link lua_image_get_exif Image:get_exif() @endlink
+ * 
+ * exif_structure = Image:get_exif(); \n
+ * DateTimeDigitized = exif_structure:get_datum("Exif.Photo.DateTimeDigitized");
+ */
+static const luaL_Reg exif_methods[] = {
+               {"get_datum", lua_exif_get_datum},
+               {NULL, NULL}
+};
+
 /**
  * @brief Initialize the lua interpreter.
  */
@@ -219,17 +328,6 @@ void lua_init(void)
                        {NULL, NULL}
        };
 
-       /* The Image metatable and methodes */
-       static const luaL_Reg image_methods[] = {
-                       {"get_path", lua_image_get_path},
-                       {"get_name", lua_image_get_name},
-                       {"get_extension", lua_image_get_extension},
-                       {"get_date", lua_image_get_date},
-                       {"get_size", lua_image_get_size},
-                       {"get_exif", lua_image_get_exif},
-                       {"get_marks", lua_image_get_marks},
-                       {NULL, NULL}
-       };
        LUA_register_global(L, "Image", image_methods);
        luaL_newmetatable(L, "Image");
        LUA_register_meta(L, meta_methods);
@@ -242,11 +340,6 @@ void lua_init(void)
        lua_pop(L, 1);
        lua_pop(L, 1);
 
-       /* The Exif table and methodes */
-       static const luaL_Reg exif_methods[] = {
-                       {"get_datum", lua_exif_get_datum},
-                       {NULL, NULL}
-       };
        LUA_register_global(L, "Exif", exif_methods);
        luaL_newmetatable(L, "Exif");
        LUA_register_meta(L, meta_methods);
index 14c0a7f..fd3343e 100644 (file)
@@ -42,6 +42,7 @@
 #include "layout.h"
 #include "layout_image.h"
 #include "layout_util.h"
+#include "misc.h"
 #include "options.h"
 #include "remote.h"
 #include "secure_save.h"
 gboolean thumb_format_changed = FALSE;
 static RemoteConnection *remote_connection = NULL;
 
+gchar *gq_prefix;
+gchar *gq_localedir;
+gchar *gq_helpdir;
+gchar *gq_htmldir;
+gchar *gq_app_dir;
+gchar *gq_bin_dir;
+gchar *desktop_file_template;
+
 /*
  *-----------------------------------------------------------------------------
  * keyboard functions
@@ -609,7 +618,7 @@ static gboolean parse_command_line_for_clutter_option(gint argc, gchar *argv[])
 static void setup_env_path(void)
 {
        const gchar *old_path = g_getenv("PATH");
-       gchar *path = g_strconcat(GQ_BIN_DIR, ":", old_path, NULL);
+       gchar *path = g_strconcat(gq_bin_dir, ":", old_path, NULL);
         g_setenv("PATH", path, TRUE);
        g_free(path);
 }
@@ -921,6 +930,46 @@ static void setup_sigbus_handler(void)
 #endif
 }
 
+/**
+ * @brief Set up the application paths
+ * 
+ * This function is required for use of AppImages. AppImages are
+ * relocatable, and therefore cannot use fixed paths to various components.
+ * These paths were originally #defines created during compilation.
+ * They are now variables, all defined relative to one level above the
+ * directory that the executable is run from.
+ */
+static void create_application_paths()
+{
+       gchar buf[1024];
+       gchar *dirname;
+       gchar *basename;
+       gchar *tmp;
+
+       memset(buf, 0, sizeof(buf));
+       if (readlink("/proc/self/exe", buf, sizeof(buf) - 1) < 0)
+               {
+               /* There was an error. Perhaps the path does not exist
+                * or the buffer is not big enough. */
+               log_printf("Can't get path from /proc/self/exe");
+               exit(1);
+               }
+
+       dirname = g_path_get_dirname(buf); // default is /usr/bin/
+       gq_prefix = g_path_get_dirname(dirname);
+
+       gq_localedir = g_build_filename(gq_prefix, "share", "locale", NULL);
+       tmp = g_build_filename(gq_prefix, "share", "doc", NULL);
+       gq_helpdir = g_strconcat(tmp, G_DIR_SEPARATOR_S, "geeqie-", VERSION, NULL);
+       gq_htmldir = g_build_filename(gq_helpdir, "html", NULL);
+       gq_app_dir = g_build_filename(gq_prefix, "share", "geeqie", NULL);
+       gq_bin_dir = g_build_filename(gq_prefix, "lib", "geeqie", NULL);
+       desktop_file_template = g_build_filename(gq_app_dir, "template.desktop", NULL);
+
+       g_free(tmp);
+       g_free(dirname);
+}
+
 gint main(gint argc, gchar *argv[])
 {
        CollectionData *first_collection = NULL;
@@ -942,11 +991,13 @@ gint main(gint argc, gchar *argv[])
        /* init execution time counter (debug only) */
        init_exec_time();
 
+       create_application_paths();
+
        /* setup locale, i18n */
        setlocale(LC_ALL, "");
 
 #ifdef ENABLE_NLS
-       bindtextdomain(PACKAGE, GQ_LOCALEDIR);
+       bindtextdomain(PACKAGE, gq_localedir);
        bind_textdomain_codeset(PACKAGE, "UTF-8");
        textdomain(PACKAGE);
 #endif
@@ -985,6 +1036,7 @@ gint main(gint argc, gchar *argv[])
                if (gtk_clutter_init(&argc, &argv) != CLUTTER_INIT_SUCCESS)
                        {
                        log_printf("Can't initialize clutter-gtk.\nStart Geeqie with the option \"geeqie --disable-clutter\"");
+                       runcmd("zenity --error --title=\"Geeqie\" --text \"Can't initialize clutter-gtk.\n\nStart Geeqie with the option:\n geeqie --disable-clutter\" --width=300");
                        exit(1);
                        }
                }
index 957331a..61cc21d 100644 (file)
 #include "debug.h"
 #include "options.h"
 
-#define DESKTOP_FILE_TEMPLATE GQ_APP_DIR "/template.desktop"
-
 #define TIMEZONE_DATABASE GQ_WEBSITE"downloads/timezone21.bin"
 
 #define HELP_SEARCH_ENGINE "https://duckduckgo.com/?q=site:geeqie.org/help "
 
 extern gboolean thumb_format_changed;
 
+extern gchar *gq_prefix;
+extern gchar *gq_localedir;
+extern gchar *gq_helpdir;
+extern gchar *gq_htmldir;
+extern gchar *gq_app_dir;
+extern gchar *gq_bin_dir;
+extern gchar *desktop_file_template;
+
 void keyboard_scroll_calc(gint *x, gint *y, GdkEventKey *event);
 gint key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data);
 
index 89763ba..d8b1440 100644 (file)
@@ -178,7 +178,7 @@ gchar *decode_geo_parameters(const gchar *input_text)
        gchar *message;
        gchar *dir;
 
-       message = decode_geo_script(GQ_BIN_DIR, input_text);
+       message = decode_geo_script(gq_bin_dir, input_text);
        if (strstr(message, "Error"))
                {
                g_free(message);
index 0cf6092..09cc272 100644 (file)
@@ -330,6 +330,8 @@ LayoutOptions *init_layout_options(LayoutOptions *options)
        options->bars_state.hidden = FALSE;
        options->log_window.w = 520;
        options->log_window.h = 400;
+       options->preferences_window.w = 700;
+       options->preferences_window.h = 600;
        options->split_pane_sync = FALSE;
        options->workspace = -1;
        return options;
index 702889b..c1f9256 100644 (file)
@@ -310,6 +310,13 @@ struct _ConfOptions
                } tmp;
        } stereo;
 
+       /* External preview extraction */
+       struct {
+               gboolean enable;
+               gchar *select; /**< path to executable */
+               gchar *extract; /**< path to executable */
+       } external_preview;
+
        /**
         * @struct cp_mv_rn
         * copy move rename
index cf72e35..d0d34a2 100644 (file)
@@ -137,6 +137,8 @@ static GtkWidget *safe_delete_path_entry;
 static GtkWidget *color_profile_input_file_entry[COLOR_PROFILE_INPUTS];
 static GtkWidget *color_profile_input_name_entry[COLOR_PROFILE_INPUTS];
 static GtkWidget *color_profile_screen_file_entry;
+static GtkWidget *external_preview_select_entry;
+static GtkWidget *external_preview_extract_entry;
 
 static GtkWidget *sidecar_ext_entry;
 static GtkWidget *help_search_engine_entry;
@@ -434,6 +436,10 @@ static void config_window_apply(void)
        options->hide_window_in_fullscreen = c_options->hide_window_in_fullscreen;
        config_entry_to_option(help_search_engine_entry, &options->help_search_engine, NULL);
 
+       options->external_preview.enable = c_options->external_preview.enable;
+       config_entry_to_option(external_preview_select_entry, &options->external_preview.select, NULL);
+       config_entry_to_option(external_preview_extract_entry, &options->external_preview.extract, NULL);
+
        options->read_metadata_in_idle = c_options->read_metadata_in_idle;
 
        options->star_rating.star = c_options->star_rating.star;
@@ -510,7 +516,8 @@ static void config_window_help_cb(GtkWidget *widget, gpointer data)
        "GuideOptionsStereo.html",
        "GuideOptionsBehavior.html",
        "GuideOptionsToolbar.html",
-       "GuideOptionsToolbar.html"
+       "GuideOptionsToolbar.html",
+       "GuideOptionsAdvanced.html"
        };
 
        i = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
@@ -526,8 +533,28 @@ static gboolean config_window_delete(GtkWidget *widget, GdkEventAny *event, gpoi
 static void config_window_ok_cb(GtkWidget *widget, gpointer data)
 {
        LayoutWindow *lw;
+       GtkNotebook *notebook = data;
+       GdkWindow *window;
+       gint x;
+       gint y;
+       gint w;
+       gint h;
+       gint page_number;
+
        lw = layout_window_list->data;
 
+       window = gtk_widget_get_window(widget);
+       gdk_window_get_root_origin(window, &x, &y);
+       w = gdk_window_get_width(window);
+       h = gdk_window_get_height(window);
+       page_number = gtk_notebook_get_current_page(notebook);
+
+       lw->options.preferences_window.x = x;
+       lw->options.preferences_window.y = y;
+       lw->options.preferences_window.w = w;
+       lw->options.preferences_window.h = h;
+       lw->options.preferences_window.page_number = page_number;
+
        config_window_apply();
        layout_util_sync(lw);
        save_options(options);
@@ -3567,6 +3594,92 @@ static void config_tab_toolbar_status(GtkWidget *notebook)
        gtk_widget_show(vbox);
 }
 
+/* advanced tab */
+static gint extension_sort_cb(gconstpointer a, gconstpointer b)
+{
+       return g_strcmp0((gchar *)a, (gchar *)b);
+}
+
+static void config_tab_advanced(GtkWidget *notebook)
+{
+       GtkWidget *vbox;
+       GtkWidget *group;
+       GSList *formats_list;
+       GList *extensions_list = NULL;
+       gchar **extensions;
+       GtkWidget *tabcomp;
+       GdkPixbufFormat *fm;
+       gint i;
+       GString *types_string = g_string_new(NULL);
+
+       vbox = scrolled_notebook_page(notebook, _("Advanced"));
+       group = pref_group_new(vbox, FALSE, _("External preview extraction"), GTK_ORIENTATION_VERTICAL);
+
+       pref_checkbox_new_int(group, _("Use external preview extraction -  Requires restart"), options->external_preview.enable, &c_options->external_preview.enable);
+
+       pref_spacer(group, PREF_PAD_GROUP);
+
+       formats_list = gdk_pixbuf_get_formats();
+
+       while (formats_list)
+               {
+               fm = formats_list->data;
+               extensions = gdk_pixbuf_format_get_extensions(fm);
+
+               i = 0;
+               while (extensions[i])
+                       {
+                       extensions_list = g_list_insert_sorted(extensions_list, g_strdup(extensions[i]), extension_sort_cb);
+                       i++;
+                       }
+
+               g_strfreev(extensions);
+               formats_list = formats_list->next;
+               }
+
+       while (extensions_list)
+               {
+               if (types_string->len == 0)
+                       {
+                       types_string = g_string_append(types_string, extensions_list->data);
+                       }
+               else
+                       {
+                       types_string = g_string_append(types_string, ", ");
+                       types_string = g_string_append(types_string, extensions_list->data);
+                       }
+
+               extensions_list = extensions_list->next;
+               }
+
+       types_string = g_string_prepend(types_string, _("Usable file types:\n"));
+       pref_label_new(group, types_string->str);
+       GtkWidget *types_string_label = gtk_label_new(types_string->str);
+       gtk_label_set_line_wrap(GTK_LABEL(types_string_label), TRUE);
+
+       pref_spacer(group, PREF_PAD_GROUP);
+
+       group = pref_group_new(vbox, FALSE, _("File identification tool"), GTK_ORIENTATION_VERTICAL);
+       external_preview_select_entry = gtk_entry_new();
+       tabcomp = tab_completion_new(&external_preview_select_entry, options->external_preview.select, NULL, NULL, NULL, NULL);
+       tab_completion_add_select_button(external_preview_select_entry, _("Select file identification tool"), FALSE);
+       gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
+       gtk_widget_show(tabcomp);
+
+       group = pref_group_new(vbox, FALSE, _("Preview extraction tool"), GTK_ORIENTATION_VERTICAL);
+       external_preview_extract_entry = gtk_entry_new();
+       tabcomp = tab_completion_new(&external_preview_extract_entry, options->external_preview.extract, NULL, NULL, NULL, NULL);
+       tab_completion_add_select_button(external_preview_extract_entry, _("Select preview extraction tool"), FALSE);
+       gtk_box_pack_start(GTK_BOX(group), tabcomp, TRUE, TRUE, 0);
+       gtk_widget_show(tabcomp);
+
+       gtk_widget_show(vbox);
+
+       g_slist_free(formats_list);
+       string_list_free(extensions_list);
+       g_string_free(types_string, TRUE);
+}
+
 /* stereo tab */
 static void config_tab_stereo(GtkWidget *notebook)
 {
@@ -3645,7 +3758,7 @@ static void config_tab_stereo(GtkWidget *notebook)
 }
 
 /* Main preferences window */
-static void config_window_create(void)
+static void config_window_create(LayoutWindow *lw)
 {
        GtkWidget *win_vbox;
        GtkWidget *hbox;
@@ -3660,7 +3773,15 @@ static void config_window_create(void)
        gtk_window_set_type_hint(GTK_WINDOW(configwindow), GDK_WINDOW_TYPE_HINT_DIALOG);
        g_signal_connect(G_OBJECT(configwindow), "delete_event",
                         G_CALLBACK(config_window_delete), NULL);
-       gtk_window_set_default_size(GTK_WINDOW(configwindow), CONFIG_WINDOW_DEF_WIDTH, CONFIG_WINDOW_DEF_HEIGHT);
+       if (options->save_dialog_window_positions)
+               {
+               gtk_window_resize(GTK_WINDOW(configwindow), lw->options.preferences_window.w, lw->options.preferences_window.h);
+               gtk_window_move(GTK_WINDOW(configwindow), lw->options.preferences_window.x, lw->options.preferences_window.y);
+               }
+       else
+               {
+               gtk_window_set_default_size(GTK_WINDOW(configwindow), CONFIG_WINDOW_DEF_WIDTH, CONFIG_WINDOW_DEF_HEIGHT);
+               }
        gtk_window_set_resizable(GTK_WINDOW(configwindow), TRUE);
        gtk_container_set_border_width(GTK_CONTAINER(configwindow), PREF_PAD_BORDER);
 
@@ -3686,6 +3807,9 @@ static void config_window_create(void)
        config_tab_behavior(notebook);
        config_tab_toolbar_main(notebook);
        config_tab_toolbar_status(notebook);
+       config_tab_advanced(notebook);
+
+       gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), lw->options.preferences_window.page_number);
 
        hbox = gtk_hbutton_box_new();
        gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
@@ -3700,7 +3824,7 @@ static void config_window_create(void)
        gtk_widget_show(button);
 
        button = pref_button_new(NULL, GTK_STOCK_OK, NULL, FALSE,
-                                G_CALLBACK(config_window_ok_cb), NULL);
+                                G_CALLBACK(config_window_ok_cb), notebook);
        gtk_container_add(GTK_CONTAINER(hbox), button);
        gtk_widget_set_can_default(button, TRUE);
        gtk_widget_grab_default(button);
@@ -3730,7 +3854,7 @@ static void config_window_create(void)
  *-----------------------------------------------------------------------------
  */
 
-void show_config_window(void)
+void show_config_window(LayoutWindow *lw)
 {
        if (configwindow)
                {
@@ -3738,7 +3862,7 @@ void show_config_window(void)
                return;
                }
 
-       config_window_create();
+       config_window_create(lw);
 }
 
 /*
@@ -3783,7 +3907,7 @@ void show_about_window(LayoutWindow *lw)
        g_free(basename);
 
        authors[0] = NULL;
-       path = g_build_filename(GQ_HELPDIR, "AUTHORS", NULL);
+       path = g_build_filename(gq_helpdir, "AUTHORS", NULL);
        fp = fopen(path, "r");
        if (fp)
                {
index e910dc5..840d5dd 100644 (file)
@@ -23,7 +23,7 @@
 #define PREFERENCES_H
 
 
-void show_config_window(void);
+void show_config_window(LayoutWindow *lw);
 void show_about_window(LayoutWindow *lw);
 
 /**
index cac00af..7f3d384 100644 (file)
@@ -353,6 +353,10 @@ static void write_global_attributes(GString *outstr, gint indent)
        WRITE_NL(); WRITE_BOOL(*options, marks_save);
        WRITE_NL(); WRITE_CHAR(*options, help_search_engine);
 
+       WRITE_NL(); WRITE_BOOL(*options, external_preview.enable);
+       WRITE_NL(); WRITE_CHAR(*options, external_preview.select);
+       WRITE_NL(); WRITE_CHAR(*options, external_preview.extract);
+
        WRITE_NL(); WRITE_BOOL(*options, with_rename);
        WRITE_NL(); WRITE_BOOL(*options, collections_on_top);
        WRITE_NL(); WRITE_BOOL(*options, hide_window_in_fullscreen);
@@ -786,6 +790,10 @@ static gboolean load_global_params(const gchar **attribute_names, const gchar **
                if (READ_BOOL(*options, marks_save)) continue;
                if (READ_CHAR(*options, help_search_engine)) continue;
 
+               if (READ_BOOL(*options, external_preview.enable)) continue;
+               if (READ_CHAR(*options, external_preview.select)) continue;
+               if (READ_CHAR(*options, external_preview.extract)) continue;
+
                if (READ_BOOL(*options, collections_on_top)) continue;
                if (READ_BOOL(*options, hide_window_in_fullscreen)) continue;
 
index ad4c1a3..d9c5e04 100644 (file)
@@ -710,6 +710,14 @@ struct _LayoutOptions
                gint y;
        } log_window;
 
+       struct {
+               gint w;
+               gint h;
+               gint x;
+               gint y;
+               gint page_number;
+       } preferences_window;
+
        struct {
                gint w;
                gint h;
index e65430c..222c95b 100644 (file)
@@ -2180,8 +2180,7 @@ static void file_util_mark_ungrouped_files(GList *work)
                }
 }
 
-
-static void file_util_delete_full(FileData *source_fd, GList *flist, GtkWidget *parent, UtilityPhase phase)
+static void file_util_delete_full(FileData *source_fd, GList *flist, GtkWidget *parent, UtilityPhase phase, FileUtilDoneFunc done_func, gpointer done_data)
 {
        UtilityData *ud;
        GList *ungrouped = NULL;
@@ -2216,6 +2215,8 @@ static void file_util_delete_full(FileData *source_fd, GList *flist, GtkWidget *
        ud->flist = flist;
        ud->content_list = NULL;
        ud->parent = parent;
+       ud->done_data = done_data;
+       ud->done_func = done_func;
 
        ud->details_func = file_util_details_dialog;
        if(options->file_ops.safe_delete_enable)
@@ -3032,7 +3033,12 @@ static gboolean file_util_write_metadata_first(UtilityType type, UtilityPhase ph
 
 void file_util_delete(FileData *source_fd, GList *source_list, GtkWidget *parent)
 {
-       file_util_delete_full(source_fd, source_list, parent, options->file_ops.confirm_delete ? UTILITY_PHASE_START : UTILITY_PHASE_ENTERING);
+       file_util_delete_full(source_fd, source_list, parent, options->file_ops.confirm_delete ? UTILITY_PHASE_START : UTILITY_PHASE_ENTERING, NULL, NULL);
+}
+
+void file_util_delete_notify_done(FileData *source_fd, GList *source_list, GtkWidget *parent, FileUtilDoneFunc done_func, gpointer done_data)
+{
+       file_util_delete_full(source_fd, source_list, parent, options->file_ops.confirm_delete ? UTILITY_PHASE_START : UTILITY_PHASE_ENTERING, done_func, done_data);
 }
 
 void file_util_write_metadata(FileData *source_fd, GList *source_list, GtkWidget *parent, gboolean force_dialog, FileUtilDoneFunc done_func, gpointer done_data)
index 43a6b2a..1a5eca4 100644 (file)
@@ -43,6 +43,7 @@ GenericDialog *file_util_warning_dialog(const gchar *heading, const gchar *messa
 /* all functions takes over the filelist and frees it when done */
 
 void file_util_delete(FileData *source_fd, GList *source_list, GtkWidget *parent);
+void file_util_delete_notify_done(FileData *source_fd, GList *source_list, GtkWidget *parent, FileUtilDoneFunc done_func, gpointer done_data);
 void file_util_move(FileData *source_fd, GList *source_list, const gchar *dest_path, GtkWidget *parent);
 void file_util_copy(FileData *source_fd, GList *source_list, const gchar *dest_path, GtkWidget *parent);
 void file_util_rename(FileData *source_fd, GList *source_list, GtkWidget *parent);
index 43d63b5..7495759 100644 (file)
@@ -246,7 +246,7 @@ void help_window_show(const gchar *key)
 
        if (key && strstr(key, ".html") != 0)
                {
-               path = g_build_filename(GQ_HTMLDIR, key, NULL);
+               path = g_build_filename(gq_htmldir, key, NULL);
                if (!isfile(path))
                        {
                        if (g_strcmp0(key, "index.html") == 0)
@@ -272,18 +272,18 @@ void help_window_show(const gchar *key)
 
        if (!strcmp(key, "release_notes"))
                {
-               path = g_build_filename(GQ_HELPDIR, "README.html", NULL);
+               path = g_build_filename(gq_helpdir, "README.html", NULL);
                if (isfile(path))
                        {
                        g_free(path);
-                       path = g_build_filename("file://", GQ_HELPDIR, "README.html", NULL);
+                       path = g_build_filename("file://", gq_helpdir, "README.html", NULL);
                        help_browser_run(path);
                        g_free(path);
                        }
                else
                        {
                        g_free(path);
-                       path = g_build_filename(GQ_HELPDIR, "README.md", NULL);
+                       path = g_build_filename(gq_helpdir, "README.md", NULL);
                        help_window = help_window_new(_("Help"), "help", path, key);
                        g_free(path);
 
@@ -293,18 +293,18 @@ void help_window_show(const gchar *key)
                }
        else
                {
-               path = g_build_filename(GQ_HELPDIR, "ChangeLog.html", NULL);
+               path = g_build_filename(gq_helpdir, "ChangeLog.html", NULL);
                if (isfile(path))
                        {
                        g_free(path);
-                       path = g_build_filename("file://", GQ_HELPDIR, "ChangeLog.html", NULL);
+                       path = g_build_filename("file://", gq_helpdir, "ChangeLog.html", NULL);
                        help_browser_run(path);
                        g_free(path);
                        }
                else
                        {
                        g_free(path);
-                       path = g_build_filename(GQ_HELPDIR, "ChangeLog", NULL);
+                       path = g_build_filename(gq_helpdir, "ChangeLog", NULL);
                        help_window = help_window_new(_("Help"), "help", path, key);
                        g_free(path);
 
index 73f60b2..57e4a41 100644 (file)
@@ -27,7 +27,7 @@
             <option value="https://apps.fedoraproject.org/packages/geeqie">
                 Fedora
             </option>
-            <option value="https://packages.ubuntu.com/eoan/geeqie">
+            <option value="https://packages.ubuntu.com/geeqie">
                 Ubuntu
             </option>
             <option value="https://community.linuxmint.com/software/view/gqview">
         </script>
         <p>MacOS X <a href="https://formulae.brew.sh/formula/geeqie">Homebrew Formulae</a></p>
         <p>Solaris <a href="https://www.opencsw.org/package/geeqie/">Openscw.org Solaris 10 &amp; 11</a></p>
+        <p>Geeqie is also available:</p>
+        <ul><li>as a <a href="https://flathub.org/apps/details/org.geeqie.Geeqie">Flatpak</a> from the <a href="https://flathub.org/home">Flathub site</a></li>
+        <li>as an <a href="https://cclark.uk/Geeqie">AppImage</a></li></ul>
        <p>The source tar for Geeqie v1.5 is available <a href="/geeqie-1.5.tar.xz">here.</a> (Signature also available under <a href="/geeqie-1.5.tar.xz.asc">geeqie-1.5.tar.xz.asc</a>)</p>
         <p>However if you are compiling from sources it is recommended that you get the latest sources from the
         <a href="/cgi-bin/gitweb.cgi?p=geeqie.git">Geeqie repository.</a></p>Geeqie is stable, there
         are frequent updates, and compiling is a quick and painless task. The instructions for <a href=
         "installing.html">downloading and installing</a> are easy to follow.
         <p>For Ubuntu and other Debian-based systems, <a href="http://www.geeqie.org/web/geeqie-install-debian.sh"> this script</a> will download Geeqie sources, all dependencies, and compile and install Geeqie. You may also select which optional libraries to install.</p>
-       <p>Also <a href="https://flathub.org/apps/details/org.geeqie.Geeqie">Geeqie is available as a flatpak</a> from the <a href="https://flathub.org/home">Flathub site</a>.</p>
         <h4>Support</h4>
        <p>If you need help or have questions about Geeqie, just <a href="mailto:geeqie@freelists.org">send a message</a> to the <a href="https://www.freelists.org/list/geeqie">mailing list</a>.
        <form action="https://www.freelists.org/cgi-bin/subscription.cgi" method="post">