Implement remote keyboard actions
authorColin Clark <colin.clark@cclark.uk>
Fri, 11 Aug 2023 15:08:05 +0000 (16:08 +0100)
committerColin Clark <colin.clark@cclark.uk>
Fri, 11 Aug 2023 15:08:05 +0000 (16:08 +0100)
Enable keyboard actions to be initiated from the command line.

Additional remote commands:
geeqie --remote --action:<ACTION>
geeqie --remote --action-list

e.g.
geeqie --remote --action:ZoomIn

The action codes can also be found in /Edit/Preferences/Keyboard.

doc/docbook/CommandLineOptions.xml
doc/docbook/GuideReference.xml
doc/docbook/GuideReferenceRemoteKeyboardActions.xml [new file with mode: 0644]
geeqie.1
src/remote.cc

index 6de2c61..7288257 100644 (file)
@@ -7,8 +7,8 @@
 <refmeta>
 <refentrytitle>GEEQIE</refentrytitle>
 <manvolnum>1</manvolnum>
-<refmiscinfo class='source'>June 2023</refmiscinfo>
-<refmiscinfo class='manual'>Geeqie 2.1 GTK3</refmiscinfo>
+<refmiscinfo class='source'>August 2023</refmiscinfo>
+<refmiscinfo class='manual'>Geeqie 2.1+git20230810-c41eb43c GTK3</refmiscinfo>
 </refmeta>
 <refnamediv>
 <refname>Geeqie</refname>
@@ -30,7 +30,7 @@ zooming, panning, thumbnails and sorting images into collections.</para>
 
 <para>Generated for version:</para>
 
-<para>Geeqie 2.1</para>
+<para>Geeqie 2.1+git20230810-c41eb43c</para>
 
 <refsect2 id='valid_options'><title>Valid options:</title>
 <variablelist remap='TP'>
@@ -145,6 +145,18 @@ zooming, panning, thumbnails and sorting images into collections.</para>
 
 <refsect2 id='remote_command_list'><title>Remote command list:</title>
 <variablelist remap='TP'>
+  <varlistentry>
+  <term><emphasis role='strong' remap='B'>--action</emphasis>:&lt;ACTION&gt;</term>
+  <listitem>
+<para>execute keyboard action (See Help/Reference/Remote Keyboard Actions)</para>
+  </listitem>
+  </varlistentry>
+  <varlistentry>
+  <term><emphasis role='strong' remap='B'>--action-list</emphasis></term>
+  <listitem>
+<para>list available keyboard actions (some are redundant)</para>
+  </listitem>
+  </varlistentry>
   <varlistentry>
   <term><emphasis role='strong' remap='B'>-b</emphasis> <emphasis role='strong' remap='B'>--back</emphasis></term>
   <listitem>
index 706196f..fb14986 100644 (file)
@@ -2,6 +2,7 @@
 <chapter id="GuideReference">
   <title>Reference</title>
   <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="GuideReferenceCommandLine.xml" />
+  <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="GuideReferenceRemoteKeyboardActions.xml" />
   <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="GuideReferenceKeyboardShortcuts.xml" />
   <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="GuideReferenceThumbnails.xml" />
   <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="GuideReferenceMetadata.xml" />
diff --git a/doc/docbook/GuideReferenceRemoteKeyboardActions.xml b/doc/docbook/GuideReferenceRemoteKeyboardActions.xml
new file mode 100644 (file)
index 0000000..ee3c0e9
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<section id="GuideReferenceRemoteKeyboardActions">
+  <title id="titleGuideReferenceRemoteKeyboardActions">Remote Keyboard Actions</title>
+  <para>
+    The command:
+    <para />
+    <code>geeqie --remote --action:&lt;action&gt;</code>
+    <para />
+    can be used to simulate keyboard actions. An example is:
+    <para />
+    <code>geeqie --remote --action:"ZoomIn"</code>
+    <para />
+    The actions available are shown in the Edit/Preferences/Keyboard dialog. The two end columns are relevant. The final component of the Accel column is the action code that should be used. The Tooltip column provides a description of the action. Both columns can be sorted by clicking the header.
+    <para />
+    The available action codes may also be listed by:
+    <para />
+    <code>geeqie --remote --action-list</code>
+  </para>
+</section>
index a6ca723..5fc6bc5 100644 (file)
--- a/geeqie.1
+++ b/geeqie.1
@@ -1,5 +1,5 @@
 .\" DO NOT MODIFY THIS FILE!  It was generated by help2man 1.49.3.
-.TH GEEQIE "1" "June 2023" "Geeqie 2.1 GTK3" "User Commands"
+.TH GEEQIE "1" "August 2023" "Geeqie 2.1+git20230810-c41eb43c GTK3" "User Commands"
 .SH NAME
 Geeqie - GTK based multiformat image viewer
 .SH SYNOPSIS
@@ -11,7 +11,7 @@ zooming, panning, thumbnails and sorting images into collections.
 
 Generated for version:
 .PP
-Geeqie 2.1
+Geeqie 2.1+git20230810\-c41eb43c
 .SS "Valid options:"
 .TP
 \fB\-\-blank\fR
@@ -68,6 +68,12 @@ turn on debug output
 filter debug output
 .SS "Remote command list:"
 .TP
+\fB\-\-action\fR:<ACTION>
+execute keyboard action (See Help/Reference/Remote Keyboard Actions)
+.TP
+\fB\-\-action\-list\fR
+list available keyboard actions (some are redundant)
+.TP
 \fB\-b\fR   \fB\-\-back\fR
 previous image
 .TP
index fdf0341..70a6561 100644 (file)
@@ -36,6 +36,7 @@
 #include "pixbuf-renderer.h"
 #include "slideshow.h"
 #include "ui-fileops.h"
+#include "utilops.h"
 #include "rcfile.h"
 #include "view-file.h"
 
@@ -1528,6 +1529,147 @@ static void gr_list_add(const gchar *text, GIOChannel *, gpointer data)
                }
 }
 
+static void gr_action(const gchar *text, GIOChannel *, gpointer)
+{
+       GtkAction *action;
+
+       if (!layout_valid(&lw_id))
+               {
+               return;
+               }
+
+       if (g_strstr_len(text, -1, ".desktop") != nullptr)
+               {
+               file_util_start_editor_from_filelist(text, layout_selection_list(lw_id), layout_get_path(lw_id), lw_id->window);
+               }
+       else
+               {
+               action = gtk_action_group_get_action(lw_id->action_group, text);
+               if (action)
+                       {
+                       gtk_action_activate(action);
+                       }
+               else
+                       {
+                       log_printf("Action %s unknown", text);
+                       }
+               }
+}
+
+static gint simple_sort(gconstpointer a, gconstpointer b)
+{
+       return g_strcmp0((gchar *)a, (gchar *)b);
+}
+
+static void gr_action_list(const gchar *, GIOChannel *channel, gpointer)
+{
+       const gchar *accel_path;
+       gchar *action_list;
+       gchar *action_name;
+       gchar *comment_list;
+       gchar *label;
+       gchar *tooltip;
+       gint max_length = 0;
+       GList *actions;
+       GList *groups;
+       GList *list_final = nullptr;
+       GList *list = nullptr;
+       GList *work;
+       GString *out_string = g_string_new(nullptr);
+       GtkAction *action;
+
+       if (!layout_valid(&lw_id))
+               {
+               return;
+               }
+
+       groups = gtk_ui_manager_get_action_groups(lw_id->ui_manager);
+       while (groups)
+               {
+               actions = gtk_action_group_list_actions(GTK_ACTION_GROUP(groups->data));
+               while (actions)
+                       {
+                       action = GTK_ACTION(actions->data);
+                       accel_path = gtk_action_get_accel_path(action);
+
+                               if (accel_path && gtk_accel_map_lookup_entry(accel_path, nullptr))
+                                       {
+                                       g_object_get(action, "tooltip", &tooltip, "label", &label, NULL);
+
+                                       action_name = g_path_get_basename(accel_path);
+
+                                       /* Used for output column padding */
+                                       if (g_utf8_strlen(action_name, -1) > max_length)
+                                               {
+                                               max_length = g_utf8_strlen(action_name, -1);
+                                               }
+
+                                       /* Tooltips with newlines affect output format */
+                                       if (tooltip && (g_strstr_len(tooltip, -1, "\n") == nullptr) )
+                                               {
+                                               list = g_list_prepend(list, g_strdup(tooltip));
+                                               }
+                                       else
+                                               {
+                                               list = g_list_prepend(list, g_strdup(label));
+                                               }
+
+                                       list = g_list_prepend(list, g_strdup(action_name));
+
+                                       g_free(action_name);
+                                       g_free(label);
+                                       g_free(tooltip);
+                                       }
+
+                       actions = actions->next;
+                       }
+
+               groups = groups->next;
+               }
+
+       /* Pad the action names to the same column for readable output */
+       work = list;
+       while (work)
+               {
+               /* Menu actions are irrelevant */
+               if (g_strstr_len(static_cast<gchar *>(work->data), -1, "Menu") == nullptr)
+                       {
+                       action_list = g_strdup_printf("%-*s", max_length + 4, static_cast<gchar *>(work->data));
+
+                       work=work->next;
+
+                       comment_list = static_cast<gchar *>(work->data);
+                       list_final = g_list_prepend(list_final, g_strconcat(action_list, comment_list, nullptr));
+
+                       g_free(action_list);
+                       }
+               else
+                       {
+                       work = work->next;
+                       }
+               work = work->next;
+               }
+
+       string_list_free(list);
+
+       list_final = g_list_sort(list_final, simple_sort);
+
+       work = list_final;
+       while (work)
+               {
+               out_string = g_string_append(out_string, static_cast<gchar *>(work->data) );
+               out_string = g_string_append(out_string, "\n");
+               work = work->next;
+               }
+
+       string_list_free(list_final);
+
+       g_io_channel_write_chars(channel, out_string->str, -1, nullptr, nullptr);
+       g_io_channel_write_chars(channel, "<gq_end_of_command>", -1, nullptr, nullptr);
+
+       g_string_free(out_string, TRUE);
+}
+
 static void gr_raise(const gchar *, GIOChannel *, gpointer)
 {
        if (layout_valid(&lw_id))
@@ -1598,6 +1740,8 @@ struct RemoteCommandEntry {
 
 static RemoteCommandEntry remote_commands[] = {
        /* short, long                  callback,               extra, prefer, parameter, description */
+       { nullptr, "--action:",          gr_action,            TRUE,  FALSE, N_("<ACTION>"), N_("execute keyboard action (See Help/Reference/Remote Keyboard Actions)") },
+       { nullptr, "--action-list",          gr_action_list,    FALSE,  FALSE, nullptr, N_("list available keyboard actions (some are redundant)") },
        { "-b", "--back",               gr_image_prev,          FALSE, FALSE, nullptr, N_("previous image") },
        { nullptr, "--close-window",       gr_close_window,        FALSE, FALSE, nullptr, N_("close window") },
        { nullptr, "--config-load:",       gr_config_load,         TRUE,  FALSE, N_("<FILE>|layout ID"), N_("load configuration from FILE") },