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/) -  - * 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 {
