Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package pqiv for openSUSE:Factory checked in 
at 2022-12-06 14:24:00
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/pqiv (Old)
 and      /work/SRC/openSUSE:Factory/.pqiv.new.1835 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "pqiv"

Tue Dec  6 14:24:00 2022 rev:2 rq:1040472 version:2.12

Changes:
--------
--- /work/SRC/openSUSE:Factory/pqiv/pqiv.changes        2020-08-20 
22:35:10.752159531 +0200
+++ /work/SRC/openSUSE:Factory/.pqiv.new.1835/pqiv.changes      2022-12-06 
14:24:16.862090099 +0100
@@ -1,0 +2,13 @@
+Mon Dec  5 20:58:50 UTC 2022 - Dirk Müller <[email protected]>
+
+- update to 2.12:
+  * Fix external image filters (Fixes #182)
+  * Fix support for best interpolation quality (Fixes #139)
+  * Fix wrap-around in shuffled image view (Fixes #176)
+  * Fix max-depth behavior if the argument is a file (Fixes #170)
+  * Allow keybinding of special keys with shift modifier
+  * Add --auto-montage-mode (Fixes #181)
+  * Replace GTimeVal with GDateTime for glib 2.62 support
+  * Add an sxiv-like marks system
+
+-------------------------------------------------------------------

Old:
----
  pqiv-2.11.tar.gz

New:
----
  pqiv-2.12.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ pqiv.spec ++++++
--- /var/tmp/diff_new_pack.79ReBP/_old  2022-12-06 14:24:17.578096204 +0100
+++ /var/tmp/diff_new_pack.79ReBP/_new  2022-12-06 14:24:17.586096272 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package pqiv
 #
-# Copyright (c) 2020 SUSE LLC
+# Copyright (c) 2022 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -17,7 +17,7 @@
 
 
 Name:           pqiv
-Version:        2.11
+Version:        2.12
 Release:        0
 Summary:        Minimalist image viewer
 License:        GPL-3.0-or-later

++++++ pqiv-2.11.tar.gz -> pqiv-2.12.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pqiv-2.11/.github/workflows/ci.yml 
new/pqiv-2.12/.github/workflows/ci.yml
--- old/pqiv-2.11/.github/workflows/ci.yml      1970-01-01 01:00:00.000000000 
+0100
+++ new/pqiv-2.12/.github/workflows/ci.yml      2020-11-29 12:51:17.000000000 
+0100
@@ -0,0 +1,18 @@
+name: CI build
+
+on: [push]
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+
+    steps:
+    - uses: actions/checkout@v1
+    - name: prepare
+      run: sudo apt-get update && sudo apt-get install build-essential 
libgtk-3-dev libmagickwand-dev libarchive-dev libpoppler-glib-dev 
libavformat-dev libavcodec-dev libswscale-dev libavutil-dev libwebp-dev
+    - name: configure
+      run: ./configure
+    - name: make
+      run: make
+    - name: check
+      run: ./pqiv --version
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pqiv-2.11/README.markdown 
new/pqiv-2.12/README.markdown
--- old/pqiv-2.11/README.markdown       2018-11-25 16:23:58.000000000 +0100
+++ new/pqiv-2.12/README.markdown       2020-11-29 12:51:17.000000000 +0100
@@ -11,7 +11,7 @@
 
 It comes with support for animations, slideshows, transparency, VIM-like key
 bindings, automated loading of new images as they appear, external image
-filters, image preloading, and much more.
+filters, marks, image preloading, and much more.
 
 pqiv started as a Python rewrite of qiv avoiding imlib, but evolved into a much
 more powerful tool. Today, pqiv stands for powerful quick image viewer.
@@ -33,6 +33,7 @@
  * Comes with an interactive montage mode (a.k.a. "image grid")
  * Customizable key-bindings with support for VIM-like key sequences, action
    cycling and binding multiple actions to a single key
+ * Mark/unmark images and pipe the list of marked images to an external script
 
 Installation
 ------------
@@ -44,11 +45,7 @@
 You can also use precompiled and packaged versions of pqiv. Note that the
 distribution packages are usually somewhat out of date:
 
- * Statically linked
-   [nightly builds for Linux and 
Windows](https://intern.pberndt.com/pqiv_builds/)
-   ![Build status](https://intern.pberndt.com/pqiv_builds/ci.php)
- * Dynamically linked
-   [nightly builds for Debian, Ubuntu, SUSE and 
Fedora](https://build.opensuse.org/package/show/home:phillipberndt/pqiv)
+ * [Nightly builds for Debian, Ubuntu, SUSE and 
Fedora](https://build.opensuse.org/package/show/home:phillipberndt/pqiv)
    thanks to the OpenSUSE build service
  * [Arch AUR package](https://aur.archlinux.org/packages/pqiv/)
    ([Git version](https://aur.archlinux.org/packages/pqiv-git/))
@@ -86,13 +83,19 @@
 
 For macOS, have a look at the `pqiv.app` target of the Makefile, too.
 
+pqiv can be linked statically, though GTK only supports static linking in
+GTK 2.x; in early versions of GTK 3.x it was fairly simple to still link
+statically.
+
+Windows builds are supported and work in GTK 2.x, it is recommended to use
+[MXE](https://mxe.cc/) for cross-compiling.
+
 Thanks
 ------
 
 This program uses Martin Pool's natsort algorithm
 <https://www.github.com/sourcefrog/natsort/>.
 
-
 Contributors
 ------------
 
@@ -101,6 +104,8 @@
  * J. Paul Reed
  * Chen Jonh L
  * Anton Älgmyr
+ * Christian Garbs
+ * Kanon Kubose
 
 Contributors to pqiv ≤ 1.0 were:
 
@@ -173,6 +178,19 @@
 Changelog
 ---------
 
+pqiv 2.12
+ * Fix external image filters (Fixes #182)
+ * Fix support for `best` interpolation quality (Fixes #139)
+ * Fix wrap-around in shuffled image view (Fixes #176)
+ * Fix max-depth behavior if the argument is a file (Fixes #170)
+ * Allow keybinding of special keys with shift modifier
+ * Add `--auto-montage-mode` (Fixes #181)
+ * Replace GTimeVal with GDateTime for glib 2.62 support
+ * Add an sxiv-like marks system
+
+<details>
+<summary>Click to expand changelog for old pqiv versions</summary>
+
 pqiv 2.11
  * Added negate (color inversion) mode (bound to `n`, `--negate`)
  * Rebound `a` (hardlink image) to `c-a` by default (See #124)
@@ -186,9 +204,6 @@
  * Work around GTK bug resulting in crash due to invalid free()
  * Improve autotools compatibility of the configure script (See #135)
 
-<details>
-<summary>Click to expand changelog for old pqiv versions</summary>
-
 pqiv 2.10.4
  * Fix output of `montage_mode_shift_y_rows()` in key bindings
  * Update the info text when the background pattern is cycled
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pqiv-2.11/backends/gdkpixbuf.c 
new/pqiv-2.12/backends/gdkpixbuf.c
--- old/pqiv-2.11/backends/gdkpixbuf.c  2018-11-25 16:23:58.000000000 +0100
+++ new/pqiv-2.12/backends/gdkpixbuf.c  2020-11-29 12:51:17.000000000 +0100
@@ -32,7 +32,16 @@
        // for the current, previous and next image.
        GdkPixbufAnimation *pixbuf_animation;
        GdkPixbufAnimationIter *animation_iter;
+
+#if GLIB_CHECK_VERSION(2, 62, 0)
+/* Glib 2.62 marks GTimeVal deprecated, but GdkPixbuf does not have an 
equivalent API
+ * for the replacement structure yet. */
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+#endif
        GTimeVal animation_time;
+#if GLIB_CHECK_VERSION(2, 62, 0)
+G_GNUC_END_IGNORE_DEPRECATIONS
+#endif
 } file_private_data_gdkpixbuf_t;
 
 BOSNode *file_type_gdkpixbuf_alloc(load_images_state_t state, file_t *file) 
{/*{{{*/
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pqiv-2.11/pqiv.1 new/pqiv-2.12/pqiv.1
--- old/pqiv-2.11/pqiv.1        2018-11-25 16:23:58.000000000 +0100
+++ new/pqiv-2.12/pqiv.1        2020-11-29 12:51:17.000000000 +0100
@@ -1,5 +1,5 @@
 .\" vim:filetype=groff
-.TH pqiv 1 "November 2018" "2.11"
+.TH pqiv 1 "November 2020" "2.12"
 .SH NAME
 pqiv \- powerful quick image viewer
 .\"
@@ -62,7 +62,7 @@
 .TP
 .BR \-r ", " \-\-additional\-from\-stdin
 Read additional filenames/folders from the standard input. This option
-conflicts with \fB\-\-commands\-from\-stdin\fR.
+conflicts with \fB\-\-actions\-from\-stdin\fR.
 .\"
 .TP
 .BR \-s ", " \-\-slideshow
@@ -109,7 +109,7 @@
 commands. The \fBACTIONS\fR feature (see below) allows one to bind further 
keys to
 other commands. \fICOMMAND\fR is executed using the default shell processor.
 `$1' is substituted with the current file name. Unless \fICOMMAND\fR begins 
with
-`|', if `$1' is not present, the file name is appended to the command line.
+`|' or `-', if `$1' is not present, the file name is appended to the command 
line.
 .RS
 .PP
 If \fICOMMAND\fR begins with `>', its standard output is displayed in a popup 
window.
@@ -117,6 +117,9 @@
 If \fICOMMAND\fR begins with `|', the current image is piped to its standard
 input, and its standard output is loaded as an image. This can be used to e.g.
 process images.
+.PP
+If \fICOMMAND\fR begins with `-', a list of currently marked images is piped 
to its
+standard input.
 .RE
 .\"
 .TP
@@ -142,6 +145,11 @@
 option, both situations result in an empty \fBpqiv\fR window being shown.
 .\"
 .TP
+.BR \-\-auto\-montage\-mode
+Automatically enter montage mode if \fBpqiv\fR is started with more than one
+image.
+.\"
+.TP
 .BR \-\-background\-pattern=\fIPATTERN\fR
 \fBpqiv\fR draws a checkerboard as transparent images' background. Use this 
option
 to alternatively use a white or black background. Valid values are 
\fIcheckerboard\fR,
@@ -180,7 +188,7 @@
 .RE
 .\"
 .TP
-.BR \-\--box\-colors=\fIFOREGROUND\ COLOR:BACKGROUND\ COLOR\fR
+.BR \-\-box\-colors=\fIFOREGROUND\ COLOR:BACKGROUND\ COLOR\fR
 Customize the colors used to draw the info box and montage mode borders. Colors
 can be specified either as a comma separated list of RBG-values in the range
 from 0 to 255 or as a hexvalue, e.g., #aabbcc. The default value is
@@ -198,8 +206,25 @@
 supply a comma separated list of backends here. Non-available backends are
 silently ignored. Disabling backends you don't want will speed up recursive
 loading significantly, especially if you disable the archive backend.
-Available backends are archive, archive_cbx, libav, gdkpixbuf, poppler,
-spectre, webp and wand.
+Available backends are:
+.RS
+.IP archive 14
+generic archive file support
+.IP archive_cbx
+*.cb? comic book archive support
+.IP libav
+video support, works with ffmpeg as well
+.IP gdkpixbuf
+images
+.IP poppler
+PDF
+.IP spectre
+PS/EPS
+.IP wand
+ImageMagick, various formats, e.g. PSD
+.IP webp
+WebP format
+.RE
 .\"
 .TP
 .BR \-\-disable\-scaling
@@ -395,6 +420,9 @@
 definition by prepending a backslash. Useful in conjunction with
 \fBsend_keys(STRING)\fR to set up cyclic bindings.
 .TP
+.BR clear_marks()
+Clear all marks.
+.TP
 .BR command(STRING)
 Execute the given shell command. The syntax of the argument is the same as for
 the \fB\-\-command\-1\fR option.
@@ -614,6 +642,9 @@
 .BR toggle_info_box()
 Toggle the visibility of the info box.
 .TP
+.BR toggle_mark()
+Toggle the current image's mark.
+.TP
 .BR toggle_negate_mode(INT)
 Toggle negate (color inversion) mode: 0 to toggle, 1 to enable, 2 to disable.
 .TP
@@ -648,6 +679,9 @@
 Toggle \fImontage\fR mode, an interactive image selection mode. Use cursor keys
 or your mouse to select an image and Return to return to single image view. Use
 \fIg\fR to quickly navigate to a thumbnail.
+.IP o
+Toggle a mark on an image. Use \fIctrl-R\fR to reset all marks. Used in 
conjunction
+with commands starting with a \fI-\fR.
 .IP q
 Quit \fBpqiv\fR
 .IP r
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pqiv-2.11/pqiv.c new/pqiv-2.12/pqiv.c
--- old/pqiv-2.11/pqiv.c        2018-11-25 16:23:58.000000000 +0100
+++ new/pqiv-2.12/pqiv.c        2020-11-29 12:51:17.000000000 +0100
@@ -16,7 +16,8 @@
  *
  */
 
-#define _XOPEN_SOURCE 600
+#define _XOPEN_SOURCE 700
+#define _GNU_SOURCE
 
 #include "pqiv.h"
 #include "lib/config_parser.h"
@@ -312,6 +313,9 @@
 #ifndef CONFIGURED_WITHOUT_ACTIONS
 gboolean option_actions_from_stdin = FALSE;
 gboolean option_status_output = FALSE;
+#ifndef CONFIGURED_WITHOUT_MONTAGE_MODE
+gboolean option_auto_montage_mode = FALSE;
+#endif
 #else
 static const gboolean option_actions_from_stdin = FALSE;
 #endif
@@ -421,6 +425,9 @@
        { "action", 0, 0, G_OPTION_ARG_CALLBACK, &option_action_callback, 
"Perform a given action", "ACTION" },
        { "actions-from-stdin", 0, 0, G_OPTION_ARG_NONE, 
&option_actions_from_stdin, "Read actions from stdin", NULL },
        { "allow-empty-window", 0, 0, G_OPTION_ARG_NONE, 
&option_allow_empty_window, "Show pqiv/do not quit even though no files are 
loaded", NULL },
+#ifndef CONFIGURED_WITHOUT_MONTAGE_MODE
+       { "auto-montage-mode", 0, 0, G_OPTION_ARG_NONE, 
&option_auto_montage_mode, "Automatically enter montage mode if multiple images 
are opened", NULL },
+#endif
 #endif
        { "background-pattern", 0, 0, G_OPTION_ARG_CALLBACK, 
&options_background_pattern_callback, "Set the background pattern to use for 
transparent images", "PATTERN" },
 #ifndef CONFIGURED_WITHOUT_ACTIONS
@@ -540,6 +547,10 @@
        { DEFAULT, KEY_BINDING_VALUE(0 , 0                , GDK_KEY_i           
     ), ACTION_TOGGLE_INFO_BOX                 , { 0   }},
        { DEFAULT, KEY_BINDING_VALUE(0 , 0                , GDK_KEY_j           
     ), ACTION_JUMP_DIALOG                     , { 0   }},
        { DEFAULT, KEY_BINDING_VALUE(0 , 0                , GDK_KEY_m           
     ), ACTION_MONTAGE_MODE_ENTER              , { 0   }},
+#ifndef CONFIGURED_WITHOUT_EXTERNAL_COMMANDS
+       { DEFAULT, KEY_BINDING_VALUE(0 , 0                , GDK_KEY_o           
     ), ACTION_TOGGLE_MARK                     , { 0   }},
+       { DEFAULT, KEY_BINDING_VALUE(0 , GDK_CONTROL_MASK , GDK_KEY_o           
     ), ACTION_CLEAR_MARKS                     , { 0   }},
+#endif
        { DEFAULT, KEY_BINDING_VALUE(0 , 0                , GDK_KEY_s           
     ), ACTION_TOGGLE_SLIDESHOW                , { 0   }},
        { DEFAULT, KEY_BINDING_VALUE(0 , 0                , GDK_KEY_b           
     ), ACTION_TOGGLE_BACKGROUND_PATTERN       , { 0   }},
        { DEFAULT, KEY_BINDING_VALUE(0 , 0                , GDK_KEY_n           
     ), ACTION_TOGGLE_NEGATE_MODE              , { 0   }},
@@ -592,6 +603,10 @@
 
        { MONTAGE, KEY_BINDING_VALUE(0 , 0                , GDK_KEY_Return      
     ), ACTION_MONTAGE_MODE_RETURN_PROCEED     , { 0   }},
        { MONTAGE, KEY_BINDING_VALUE(0 , 0                , GDK_KEY_Escape      
     ), ACTION_MONTAGE_MODE_RETURN_CANCEL      , { 0   }},
+#ifndef CONFIGURED_WITHOUT_EXTERNAL_COMMANDS
+       { MONTAGE, KEY_BINDING_VALUE(0 , 0                , GDK_KEY_o           
     ), ACTION_TOGGLE_MARK                     , { 0   }},
+       { MONTAGE, KEY_BINDING_VALUE(0 , GDK_CONTROL_MASK , GDK_KEY_o           
     ), ACTION_CLEAR_MARKS                     , { 0   }},
+#endif
        { MONTAGE, KEY_BINDING_VALUE(0 , 0                , GDK_KEY_m           
     ), ACTION_MONTAGE_MODE_RETURN_CANCEL      , { 0   }},
        { MONTAGE, KEY_BINDING_VALUE(0 , 0                , GDK_KEY_f           
     ), ACTION_TOGGLE_FULLSCREEN               , { 0   }},
        { MONTAGE, KEY_BINDING_VALUE(0 , 0                , GDK_KEY_g           
     ), ACTION_MONTAGE_MODE_FOLLOW             , { .pcharptr = 
(char*)montage_mode_default_keys }},
@@ -705,6 +720,8 @@
        { "move_window", PARAMETER_2SHORT },
        { "toggle_background_pattern", PARAMETER_INT },
        { "toggle_negate_mode", PARAMETER_INT },
+       { "toggle_mark", PARAMETER_NONE },
+       { "clear_marks", PARAMETER_NONE },
        { NULL, 0 }
 };
 /* }}} */
@@ -780,11 +797,17 @@
 gboolean window_auto_hide_cursor_callback(gpointer user_data);
 #ifndef CONFIGURED_WITHOUT_ACTIONS
 gboolean handle_input_event_timeout_callback(gpointer user_data);
+void queue_action(pqiv_action_t action_id, pqiv_action_parameter_t parameter);
 #endif
 #ifndef CONFIGURED_WITHOUT_MONTAGE_MODE
 gboolean montage_window_get_move_cursor_target(int, int, int, int*, int*, 
int*, BOSNode **);
 void montage_window_move_cursor(int, int, int);
 #endif
+#ifndef CONFIGURED_WITHOUT_EXTERNAL_COMMANDS
+void toggle_mark();
+void clear_marks();
+GString *get_all_marked();
+#endif
 // }}}
 /* Helper functions {{{ */
 gboolean strv_contains(const gchar * const *strv, const gchar *str) {
@@ -1228,12 +1251,25 @@
        }
 #endif
 }
+
+static void parse_configuration_file_nolocale(const gchar *config_file) {
+       gchar *old_locale = g_strdup(setlocale(LC_NUMERIC, NULL));
+       setlocale(LC_NUMERIC, "C");
+
+       config_parser_parse_file(config_file, 
parse_configuration_file_callback);
+
+       setlocale(LC_NUMERIC, old_locale);
+       g_free(old_locale);
+
+       return;
+}
+
 void parse_configuration_file() {/*{{{*/
        // Check for a configuration file
        const gchar *env_config_file = g_getenv("PQIVRC_PATH");
        if(env_config_file) {
                if(g_file_test(env_config_file, G_FILE_TEST_EXISTS)) {
-                       config_parser_parse_file(env_config_file, 
parse_configuration_file_callback);
+                       parse_configuration_file_nolocale(env_config_file);
                }
                return;
        }
@@ -1260,7 +1296,7 @@
        gchar *config_file_name;
        while((config_file_name = g_queue_pop_head(test_dirs))) {
                if(g_file_test(config_file_name, G_FILE_TEST_EXISTS)) {
-                       config_parser_parse_file(config_file_name, 
parse_configuration_file_callback);
+                       parse_configuration_file_nolocale(config_file_name);
                        g_free(config_file_name);
                        break;
                }
@@ -1577,10 +1613,6 @@
                        // first (unless sorting is enabled)
                        load_images_handle_parameter(param, 
BROWSE_ORIGINAL_PARAMETER, 0, recursion_folder_stack);
 
-                       // Decrease depth such that the following recursive 
invocations
-                       // will again have depth 0 (this is the base directory, 
after all)
-                       depth -= 1;
-
                        // Replace param with the containing directory's name
                        original_parameter = param;
                        param = g_path_get_dirname(param);
@@ -1728,10 +1760,16 @@
                                if(param_file) {
                                        GFileInfo *file_info = 
g_file_query_info(param_file, G_FILE_ATTRIBUTE_TIME_MODIFIED, 
G_FILE_QUERY_INFO_NONE, NULL, NULL);
                                        if(file_info) {
+#if GLIB_CHECK_VERSION(2, 62, 0)
+                                               GDateTime *result = 
g_file_info_get_modification_date_time(file_info);
+                                               file->sort_name = 
g_strdup_printf("%zu;%s", g_date_time_to_unix(result), file->display_name);
+                                               g_date_time_unref(result);
+#else
                                                GTimeVal result;
                                                
g_file_info_get_modification_time(file_info, &result);
-                                               g_object_unref(file_info);
                                                file->sort_name = 
g_strdup_printf("%lu;%s", result.tv_sec, file->display_name);
+#endif
+                                               g_object_unref(file_info);
                                        }
                                        g_object_unref(param_file);
                                }
@@ -1748,6 +1786,14 @@
                // Check if one of the file type handlers can handle this file
                BOSNode *new_node = 
load_images_handle_parameter_find_handler(param, state, file, 
load_images_file_filter_info);
                if(new_node && new_node != FALSE_POINTER) {
+#if !defined(CONFIGURED_WITHOUT_MONTAGE_MODE) && 
!defined(CONFIGURED_WITHOUT_ACTIONS)
+                       // Automatically enter montage mode
+                       if(option_auto_montage_mode && 
bostree_node_count(file_tree) == 2) {
+                               pqiv_action_parameter_t empty_param = { .pint = 
0 };
+                               queue_action(ACTION_MONTAGE_MODE_ENTER, 
empty_param);
+                               option_auto_montage_mode = FALSE;
+                       }
+#endif
                        if(!current_file_node && main_window_visible) {
                                current_file_node = 
bostree_node_weak_ref(new_node);
                                
g_idle_add((GSourceFunc)absolute_image_movement, 
bostree_node_weak_ref(new_node));
@@ -3084,76 +3130,74 @@
                }
 
                // The list isn't long enough to provide us with the desired 
image.
-               if(shuffled_images_list_length < bostree_node_count(file_tree)) 
{
-                       // If not all images have been viewed, expand it
-                       while(movement != 0) {
-                               BOSNode *next_candidate, *chosen_candidate;
-                               // We select one random list element and then 
choose the sequentially next
-                               // until we find one that has not been chosen 
yet. Walking sequentially
-                               // after chosing one random integer index still 
generates a
-                               // equidistributed permutation.
-                               // This is O(n^2), since we must in the worst 
case lookup n-1 elements
-                               // in a list of already chosen ones, but I 
think that this still is a
-                               // better choice than to store an additional 
boolean in each file_t,
-                               // which would make this O(n).
-                               next_candidate = chosen_candidate = 
bostree_select(file_tree, g_random_int_range(0, count));
+               // If not all images have been viewed, expand it
+               while(shuffled_images_list_length < 
bostree_node_count(file_tree) && movement != 0) {
+                       BOSNode *next_candidate, *chosen_candidate;
+                       // We select one random list element and then choose 
the sequentially next
+                       // until we find one that has not been chosen yet. 
Walking sequentially
+                       // after chosing one random integer index still 
generates a
+                       // equidistributed permutation.
+                       // This is O(n^2), since we must in the worst case 
lookup n-1 elements
+                       // in a list of already chosen ones, but I think that 
this still is a
+                       // better choice than to store an additional boolean in 
each file_t,
+                       // which would make this O(n).
+                       next_candidate = chosen_candidate = 
bostree_select(file_tree, g_random_int_range(0, count));
+                       if(!next_candidate) {
+                               // All images have gone.
+                               return current_file_node;
+                       }
+                       while(g_list_find_custom(shuffled_images_list, 
next_candidate, (GCompareFunc)relative_image_pointer_shuffle_list_cmp)) {
+                               next_candidate = 
bostree_next_node(next_candidate);
                                if(!next_candidate) {
-                                       // All images have gone.
-                                       return current_file_node;
+                                       next_candidate = 
bostree_select(file_tree, 0);
                                }
-                               while(g_list_find_custom(shuffled_images_list, 
next_candidate, (GCompareFunc)relative_image_pointer_shuffle_list_cmp)) {
-                                       next_candidate = 
bostree_next_node(next_candidate);
-                                       if(!next_candidate) {
-                                               next_candidate = 
bostree_select(file_tree, 0);
-                                       }
-                                       if(next_candidate == chosen_candidate) {
-                                               // This ought not happen :/
-                                               g_warn_if_reached();
-                                               current_shuffled_image = NULL;
-                                               movement = 0;
-                                       }
+                               if(next_candidate == chosen_candidate) {
+                                       // This ought not happen :/
+                                       g_warn_if_reached();
+                                       current_shuffled_image = NULL;
+                                       movement = 0;
+                                       break;
                                }
+                       }
 
-                               // If this is the start of a cycle and the 
current image has
-                               // been selected again by chance, jump one 
image ahead.
-                               if((shuffled_images_list == NULL || 
shuffled_images_list->data == NULL) && next_candidate == current_file_node && 
bostree_node_count(file_tree) > 1) {
-                                       next_candidate = 
bostree_next_node(next_candidate);
-                                       if(!next_candidate) {
-                                               next_candidate = 
bostree_select(file_tree, 0);
-                                       }
+                       // If this is the start of a cycle and the current 
image has
+                       // been selected again by chance, jump one image ahead.
+                       if((shuffled_images_list == NULL || 
shuffled_images_list->data == NULL) && next_candidate == current_file_node && 
bostree_node_count(file_tree) > 1) {
+                               next_candidate = 
bostree_next_node(next_candidate);
+                               if(!next_candidate) {
+                                       next_candidate = 
bostree_select(file_tree, 0);
                                }
+                       }
 
-                               if(movement > 0) {
-                                       shuffled_images_list = 
g_list_append(shuffled_images_list, 
relative_image_pointer_shuffle_list_create(next_candidate));
-                                       movement--;
-                                       shuffled_images_list_length++;
-                                       current_shuffled_image = 
g_list_last(shuffled_images_list);
-                               }
-                               else if(movement < 0) {
-                                       shuffled_images_list = 
g_list_prepend(shuffled_images_list, 
relative_image_pointer_shuffle_list_create(next_candidate));
-                                       movement++;
-                                       shuffled_images_list_length++;
-                                       current_shuffled_image = 
g_list_first(shuffled_images_list);
-                               }
+                       if(movement > 0) {
+                               shuffled_images_list = 
g_list_append(shuffled_images_list, 
relative_image_pointer_shuffle_list_create(next_candidate));
+                               movement--;
+                               shuffled_images_list_length++;
+                               current_shuffled_image = 
g_list_last(shuffled_images_list);
+                       }
+                       else if(movement < 0) {
+                               shuffled_images_list = 
g_list_prepend(shuffled_images_list, 
relative_image_pointer_shuffle_list_create(next_candidate));
+                               movement++;
+                               shuffled_images_list_length++;
+                               current_shuffled_image = 
g_list_first(shuffled_images_list);
                        }
                }
-               else {
-                       // If all images have been used, wrap around the list's 
end
-                       while(movement) {
-                               current_shuffled_image = movement > 0 ? 
g_list_first(shuffled_images_list) : g_list_last(shuffled_images_list);
-                               movement = movement > 0 ? movement - 1 : 
movement + 1;
-
-                               if(movement > 0) {
-                                       while(movement && 
g_list_next(current_shuffled_image)) {
-                                               current_shuffled_image = 
g_list_next(current_shuffled_image);
-                                               movement--;
-                                       }
+
+               // If all images have been used, wrap around the list's end
+               while(movement) {
+                       current_shuffled_image = movement > 0 ? 
g_list_first(shuffled_images_list) : g_list_last(shuffled_images_list);
+                       movement = movement > 0 ? movement - 1 : movement + 1;
+
+                       if(movement > 0) {
+                               while(movement && 
g_list_next(current_shuffled_image)) {
+                                       current_shuffled_image = 
g_list_next(current_shuffled_image);
+                                       movement--;
                                }
-                               else if(movement < 0) {
-                                       while(movement && 
g_list_previous(current_shuffled_image)) {
-                                               current_shuffled_image = 
g_list_previous(current_shuffled_image);
-                                               movement++;
-                                       }
+                       }
+                       else if(movement < 0) {
+                               while(movement && 
g_list_previous(current_shuffled_image)) {
+                                       current_shuffled_image = 
g_list_previous(current_shuffled_image);
+                                       movement++;
                                }
                        }
                }
@@ -3530,20 +3574,53 @@
                // Reminder: Do not free the others, they are string constants
                g_free(argv[2]);
        }
-       else if(external_filter[0] == '|') {
-               // Pipe image into program, read image from its stdout
+       else if(external_filter[0] == '-') {
+               GString *marklist = get_all_marked();
                argv[2] = external_filter + 1;
                GPid child_pid;
                gint child_stdin;
-               gint child_stdout;
-               BOSNode *current_file_node_at_start = 
bostree_node_weak_ref(current_file_node);
                if(!g_spawn_async_with_pipes(NULL, argv, NULL,
-                       // In win32, the G_SPAWN_DO_NOT_REAP_CHILD is required 
to get the process handle
+                       G_SPAWN_DO_NOT_REAP_CHILD,
+                       NULL, NULL, &child_pid, &child_stdin, NULL, NULL, 
&error_pointer)
+               ) {
+                       g_printerr("Failed execute external command `%s': 
%s\n", argv[2], error_pointer->message);
+                       g_clear_error(&error_pointer);
+               }
+               else {
+                       if(write(child_stdin, marklist->str, marklist->len) == 
-1) {
+                               g_printerr("Failed writing to stdin of %s\n", 
argv[2]);
+                       }
+                       close(child_stdin);
+                       gint status = 0;  // When left uninitialized, external 
command reports exiting due to signal 95 (exit statuses have been 233, 201, 
217). Why?
                        #ifdef _WIN32
-                               G_SPAWN_DO_NOT_REAP_CHILD,
+                               WaitForSingleObject(child_pid, INFINITE);
+                               DWORD exit_code = 0;
+                               GetExitCodeProcess(child_pid, &exit_code);
+                               status = (gint)exit_code;
                        #else
-                               0,
+                               waitpid(child_pid, &status, 0);
                        #endif
+                       g_spawn_close_pid(child_pid);
+
+                       if (!WIFEXITED(status)) {
+                               if (WIFSIGNALED(status)) {
+                                       g_printerr("External command exited due 
to signal %d (exit status: %d)\n", WTERMSIG(status), WEXITSTATUS(status));
+                               }
+                               else {
+                                       g_printerr("External command failed 
with exit status %d\n", WEXITSTATUS(status));
+                               }
+                       }
+               }
+               g_string_free(marklist, TRUE);
+       }
+       else if(external_filter[0] == '|') {
+               // Pipe image into program, read image from its stdout
+               argv[2] = external_filter + 1;
+               GPid child_pid;
+               gint child_stdin;
+               gint child_stdout;
+               BOSNode *current_file_node_at_start = 
bostree_node_weak_ref(current_file_node);
+               if(!g_spawn_async_with_pipes(NULL, argv, NULL, 
G_SPAWN_DO_NOT_REAP_CHILD,
                        NULL, NULL, &child_pid, &child_stdin, &child_stdout, 
NULL, &error_pointer)
                ) {
                        g_printerr("Failed execute external command `%s': 
%s\n", argv[2], error_pointer->message);
@@ -4227,12 +4304,13 @@
 
        // Update info text
        if(!option_hide_info_box) {
-               current_info_text = g_strdup_printf("%s (%dx%d) %03.2f%% 
[%d/%d]", display_name,
+               current_info_text = g_strdup_printf("%s (%dx%d) %03.2f%% 
[%d/%d]%s", display_name,
                        CURRENT_FILE->width,
                        CURRENT_FILE->height,
                        current_scale_level * 100.,
                        (unsigned int)(bostree_rank(current_file_node) + 1),
-                       (unsigned int)(bostree_node_count(file_tree)));
+                       (unsigned int)(bostree_node_count(file_tree)),
+                       CURRENT_FILE->marked ? " [m]" : "");
 
                if(action != NULL) {
                        gchar *old_info_text = current_info_text;
@@ -4767,6 +4845,21 @@
                                cairo_stroke(cr_arg);
                        }
 
+                       // Marks
+                       if(thumb_file->marked) {
+                               int markx = 
cairo_image_surface_get_width(thumb_file->thumbnail);
+                               int marky = 
cairo_image_surface_get_height(thumb_file->thumbnail);
+                               cairo_save(cr_arg);
+                               cairo_rectangle(cr_arg, markx - 5, marky - 5, 
markx + 1, marky + 1);
+                               cairo_set_source_rgb(cr_arg, 0, 0, 0);
+                               cairo_set_line_width(cr_arg, 1);
+                               cairo_stroke_preserve(cr_arg);
+                               cairo_set_source_rgb(cr_arg, 1, 0, 1);
+                               cairo_set_operator(cr_arg, 
CAIRO_OPERATOR_DIFFERENCE);
+                               cairo_fill(cr_arg);
+                               cairo_restore(cr_arg);
+                       }
+
                        cairo_restore(cr_arg);
                }
                else if(top_left_id + draw_now == selection_rank) {
@@ -6003,7 +6096,7 @@
                        break;
 
                case ACTION_SET_INTERPOLATION_QUALITY:
-                       if(parameter.pint > BEST || parameter.pint < 0) {
+                       if(parameter.pint > BEST + 1 || parameter.pint < 0) {
                                g_printerr("Interpolation quality `%d' not 
supported.\n", parameter.pint);
                        }
                        else if(parameter.pint == 0) {
@@ -6545,6 +6638,15 @@
                        gtk_widget_queue_draw(GTK_WIDGET(main_window));
                        break;
 #endif // without montage
+#ifndef CONFIGURED_WITHOUT_EXTERNAL_COMMANDS
+               case ACTION_TOGGLE_MARK:
+                       toggle_mark();
+                       break;
+
+               case ACTION_CLEAR_MARKS:
+                       clear_marks();
+                       break;
+#endif // without external commands
 
                default:
                        break;
@@ -6668,7 +6770,6 @@
 
        // Filter unwanted state variables out
        state &= gtk_accelerator_get_default_mod_mask();
-       state &= ~GDK_SHIFT_MASK;
        key_binding_value = KEY_BINDING_VALUE(is_mouse, state, keycode);
 
 #ifndef CONFIGURED_WITHOUT_ACTIONS
@@ -6736,7 +6837,12 @@
 #endif
 }/*}}}*/
 gboolean window_key_press_callback(GtkWidget *widget, GdkEventKey *event, 
gpointer user_data) {/*{{{*/
-       handle_input_event(KEY_BINDING_VALUE(0, event->state, event->keyval));
+       GdkKeymap *keymap = 
gdk_keymap_get_for_display(gtk_widget_get_display(GTK_WIDGET(main_window)));
+       guint keyval;
+       GdkModifierType consumed;
+
+       gdk_keymap_translate_keyboard_state(keymap, event->hardware_keycode, 
event->state, event->group, &keyval, NULL, NULL, &consumed);
+       handle_input_event(KEY_BINDING_VALUE(0, event->state & ~consumed, 
keyval));
        return FALSE;
 }/*}}}*/
 void window_center_mouse() {/*{{{*/
@@ -7465,8 +7571,11 @@
 
                                                guint keyval = *scan;
                                                if(keyboard_state & 
GDK_SHIFT_MASK) {
-                                                       keyval = 
gdk_keyval_to_upper(keyval);
-                                                       keyboard_state &= 
~GDK_SHIFT_MASK;
+                                                       guint upper_keyval = 
gdk_keyval_to_upper(keyval);
+                                                       if (upper_keyval != 
keyval) {
+                                                               keyval = 
upper_keyval;
+                                                               keyboard_state 
&= ~GDK_SHIFT_MASK;
+                                                       }
                                                }
                                                keyboard_key_value = 
KEY_BINDING_VALUE(0, keyboard_state, keyval);
                                                #define 
PARSE_KEY_BINDINGS_BIND(keyboard_key_value) \
@@ -7520,8 +7629,11 @@
                                                        break;
                                                }
                                                if(keyboard_state & 
GDK_SHIFT_MASK) {
-                                                       keyval = 
gdk_keyval_to_upper(keyval);
-                                                       keyboard_state &= 
~GDK_SHIFT_MASK;
+                                                       guint upper_keyval = 
gdk_keyval_to_upper(keyval);
+                                                       if (upper_keyval != 
keyval) {
+                                                               keyval = 
upper_keyval;
+                                                               keyboard_state 
&= ~GDK_SHIFT_MASK;
+                                                       }
                                                }
                                                keyboard_key_value = 
KEY_BINDING_VALUE(0, keyboard_state, keyval);
                                                
PARSE_KEY_BINDINGS_BIND(keyboard_key_value);
@@ -7959,6 +8071,7 @@
                load_images_thread_update_info_text(NULL);
        }
 #endif
+
        return NULL;
 }/*}}}*/
 gboolean inner_main(void *user_data) {/*{{{*/
@@ -7987,6 +8100,48 @@
 
        return FALSE;
 }/*}}}*/
+
+/* Marks system functions {{{ */
+#ifndef CONFIGURED_WITHOUT_EXTERNAL_COMMANDS
+void clear_marks() {/*{{{*/
+       D_LOCK(file_tree);
+       for(BOSNode *iter = bostree_select(file_tree, 0); iter; iter = 
bostree_next_node(iter)) {
+               FILE(iter)->marked = FALSE;
+       }
+       D_UNLOCK(file_tree);
+       if(application_mode == DEFAULT) {
+               update_info_text("Cleared all marks");
+               info_text_queue_redraw();
+       }
+}/*}}}*/
+void toggle_mark() {/*{{{*/
+       if(application_mode == DEFAULT) {
+               FILE(current_file_node)->marked = 
!FILE(current_file_node)->marked;
+               update_info_text(NULL);
+               info_text_queue_redraw();
+       }
+       #ifndef CONFIGURED_WITHOUT_MONTAGE_MODE
+       else if(application_mode == MONTAGE) {
+               FILE(montage_window_control.selected_node)->marked = 
!FILE(montage_window_control.selected_node)->marked;
+       }
+       #endif
+}/*}}}*/
+GString *get_all_marked() {/*{{{*/
+       GString *result = g_string_new(NULL);
+
+       D_LOCK(file_tree);
+       for(BOSNode *iter = bostree_select(file_tree, 0); iter; iter = 
bostree_next_node(iter)) {
+               file_t *file = FILE(iter);
+               if(file->marked) {
+                       g_string_append_printf(result, "%s\n", file->file_name);
+               }
+       }
+       D_UNLOCK(file_tree);
+       return result;
+}/*}}}*/
+#endif
+// }}}
+
 int main(int argc, char *argv[]) {
        #ifdef DEBUG
                #ifndef _WIN32
@@ -8094,5 +8249,4 @@
 
        return 0;
 }
-
 // vim:noet ts=4 sw=4 tw=0 fdm=marker
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pqiv-2.11/pqiv.h new/pqiv-2.12/pqiv.h
--- old/pqiv-2.11/pqiv.h        2018-11-25 16:23:58.000000000 +0100
+++ new/pqiv-2.12/pqiv.h        2020-11-29 12:51:17.000000000 +0100
@@ -29,7 +29,7 @@
 #include "lib/bostree.h"
 
 #ifndef PQIV_VERSION
-#define PQIV_VERSION "2.11"
+#define PQIV_VERSION "2.12"
 #endif
 
 #define FILE_FLAGS_ANIMATION      (guint)(1)
@@ -106,6 +106,11 @@
 
        // File-type specific data, allocated and freed by the file type 
handlers
        void *private;
+
+       // TRUE if file is marked
+#ifndef CONFIGURED_WITHOUT_EXTERNAL_COMMANDS
+       gboolean marked;
+#endif
 };
 // }}}
 // Definition of the built-in file types {{{
@@ -285,6 +290,8 @@
        ACTION_MOVE_WINDOW,
        ACTION_TOGGLE_BACKGROUND_PATTERN,
        ACTION_TOGGLE_NEGATE_MODE,
+       ACTION_TOGGLE_MARK,
+       ACTION_CLEAR_MARKS,
 } pqiv_action_t;
 
 typedef union {

Reply via email to