app: serialize and deserialize the actions' accelerators.

gtk_accel_map_load()/gtk_accel_map_save() are not working with the new
GAction-based code. GtkApplication does not seem to have helper functions to
simply load and save accelerators in a file, so we just implement it ourselves.

A few things are missing right now, namely:

- On parsing, it doesn't handle any kind of duplicate accelerators (possible
  especially if someone edited the new shortcutsrc manually).
- On reading, maybe we should only write down the changed (from defaults)
  actions, while keeping the old ones commented-out, as menurc used to be. This
  is actually useful info both for debugging or even for users who want to look
  at this file and see what they changed.
- We should add import code to transform the menurc into shortcutsrc when
  updating GIMP, otherwise all custom shortcuts would get lost.

There is still the question on whether we should add the group name too. I think
we should, expecially for plug-in's procedure actions, though right now these
are just added in the GtkApplication's main action group anyway. I'll see later
to refine this.
This commit is contained in:
Jehan 2023-04-07 13:42:08 +02:00
parent 0278480f87
commit 94ebb4f821
6 changed files with 327 additions and 15 deletions

View File

@ -561,9 +561,6 @@ gui_restore_after_callback (Gimp *gimp,
NULL);
}
if (gui_config->restore_accels)
menus_restore (gimp);
ui_configurer = g_object_new (GIMP_TYPE_UI_CONFIGURER,
"gimp", gimp,
NULL);
@ -571,6 +568,9 @@ gui_restore_after_callback (Gimp *gimp,
image_ui_manager = menus_get_image_manager_singleton (gimp);
gimp_ui_manager_update (image_ui_manager, gimp);
if (gui_config->restore_accels)
menus_restore (gimp);
/* Check that every accelerator is unique. */
gui_check_unique_accelerators (gimp);

View File

@ -26,6 +26,8 @@ libappmenus_a_SOURCES = \
image-menu.h \
plug-in-menus.c \
plug-in-menus.h \
shortcuts-rc.c \
shortcuts-rc.h \
tool-options-menu.c \
tool-options-menu.h \
window-menu.c \

View File

@ -39,6 +39,7 @@
#include "image-menu.h"
#include "menus.h"
#include "plug-in-menus.h"
#include "shortcuts-rc.h"
#include "tool-options-menu.h"
#include "gimp-intl.h"
@ -413,19 +414,19 @@ menus_exit (Gimp *gimp)
void
menus_restore (Gimp *gimp)
{
GFile *file;
gchar *filename;
GFile *file;
GError *error = NULL;
g_return_if_fail (GIMP_IS_GIMP (gimp));
file = gimp_directory_file ("menurc", NULL);
file = gimp_directory_file ("shortcutsrc", NULL);
if (gimp->be_verbose)
g_print ("Parsing '%s'\n", gimp_file_get_utf8_name (file));
filename = g_file_get_path (file);
gtk_accel_map_load (filename);
g_free (filename);
if (! shortcuts_rc_parse (GTK_APPLICATION (gimp->app), file, &error))
g_printerr ("Failed reading '%s': %s\n",
g_file_peek_path (file), error->message);
g_object_unref (file);
}
@ -434,24 +435,25 @@ void
menus_save (Gimp *gimp,
gboolean always_save)
{
GFile *file;
gchar *filename;
GFile *file;
GError *error = NULL;
g_return_if_fail (GIMP_IS_GIMP (gimp));
if (menurc_deleted && ! always_save)
return;
file = gimp_directory_file ("menurc", NULL);
file = gimp_directory_file ("shortcutsrc", NULL);
if (gimp->be_verbose)
g_print ("Writing '%s'\n", gimp_file_get_utf8_name (file));
filename = g_file_get_path (file);
gtk_accel_map_save (filename);
g_free (filename);
if (! shortcuts_rc_write (GTK_APPLICATION (gimp->app), file, &error))
g_printerr ("Failed writing to '%s': %s\n",
g_file_peek_path (file), error->message);
g_object_unref (file);
g_clear_error (&error);
menurc_deleted = FALSE;
}

View File

@ -5,6 +5,7 @@ libappmenus_sources = [
'image-menu.c',
'menus.c',
'plug-in-menus.c',
'shortcuts-rc.c',
'tool-options-menu.c',
'window-menu.c',
'windows-menu.c',

274
app/menus/shortcuts-rc.c Normal file
View File

@ -0,0 +1,274 @@
/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* shortcuts-rc.c
* Copyright (C) 2023 Jehan
*
* 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 3 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, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gegl.h>
#include <gtk/gtk.h>
#include "libgimpbase/gimpbase.h"
#include "libgimpbase/gimpprotocol.h"
#include "libgimpconfig/gimpconfig.h"
#include "menus-types.h"
#include "shortcuts-rc.h"
#include "gimp-intl.h"
#define SHORTCUTS_RC_FILE_VERSION 1
/*
* All deserialize functions return G_TOKEN_LEFT_PAREN on success,
* or the GTokenType they would have expected but didn't get,
* or G_TOKEN_ERROR if the function already set an error itself.
*/
static GTokenType shortcuts_action_deserialize (GScanner *scanner,
GtkApplication *application);
enum
{
PROTOCOL_VERSION = 1,
FILE_VERSION,
ACTION,
};
gboolean
shortcuts_rc_parse (GtkApplication *application,
GFile *file,
GError **error)
{
GScanner *scanner;
gint protocol_version = GIMP_PROTOCOL_VERSION;
gint file_version = SHORTCUTS_RC_FILE_VERSION;
GTokenType token;
g_return_val_if_fail (GTK_IS_APPLICATION (application), FALSE);
g_return_val_if_fail (G_IS_FILE (file), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
scanner = gimp_scanner_new_file (file, error);
if (! scanner)
return FALSE;
g_scanner_scope_add_symbol (scanner, 0,
"protocol-version",
GINT_TO_POINTER (PROTOCOL_VERSION));
g_scanner_scope_add_symbol (scanner, 0,
"file-version",
GINT_TO_POINTER (FILE_VERSION));
g_scanner_scope_add_symbol (scanner, 0,
"action", GINT_TO_POINTER (ACTION));
token = G_TOKEN_LEFT_PAREN;
while (protocol_version == GIMP_PROTOCOL_VERSION &&
file_version == SHORTCUTS_RC_FILE_VERSION &&
g_scanner_peek_next_token (scanner) == token)
{
token = g_scanner_get_next_token (scanner);
switch (token)
{
case G_TOKEN_LEFT_PAREN:
token = G_TOKEN_SYMBOL;
break;
case G_TOKEN_SYMBOL:
switch (GPOINTER_TO_INT (scanner->value.v_symbol))
{
case PROTOCOL_VERSION:
token = G_TOKEN_INT;
if (gimp_scanner_parse_int (scanner, &protocol_version))
token = G_TOKEN_RIGHT_PAREN;
break;
case FILE_VERSION:
token = G_TOKEN_INT;
if (gimp_scanner_parse_int (scanner, &file_version))
token = G_TOKEN_RIGHT_PAREN;
break;
case ACTION:
g_scanner_set_scope (scanner, ACTION);
token = shortcuts_action_deserialize (scanner, application);
g_scanner_set_scope (scanner, 0);
break;
default:
break;
}
break;
case G_TOKEN_RIGHT_PAREN:
token = G_TOKEN_LEFT_PAREN;
break;
default: /* do nothing */
break;
}
}
if (protocol_version != GIMP_PROTOCOL_VERSION ||
file_version != SHORTCUTS_RC_FILE_VERSION ||
token != G_TOKEN_LEFT_PAREN)
{
if (protocol_version != GIMP_PROTOCOL_VERSION)
{
g_set_error (error,
GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_VERSION,
_("Skipping '%s': wrong GIMP protocol version."),
gimp_file_get_utf8_name (file));
}
else if (file_version != SHORTCUTS_RC_FILE_VERSION)
{
g_set_error (error,
GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_VERSION,
_("Skipping '%s': wrong shortcutsrc file format version."),
gimp_file_get_utf8_name (file));
}
else if (token != G_TOKEN_ERROR)
{
g_scanner_get_next_token (scanner);
g_scanner_unexp_token (scanner, token, NULL, NULL, NULL,
_("fatal parse error"), TRUE);
}
return FALSE;
}
gimp_scanner_unref (scanner);
return TRUE;
}
gboolean
shortcuts_rc_write (GtkApplication *application,
GFile *file,
GError **error)
{
GimpConfigWriter *writer;
gchar **actions;
g_return_val_if_fail (GTK_IS_APPLICATION (application), FALSE);
g_return_val_if_fail (G_IS_FILE (file), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
writer = gimp_config_writer_new_from_file (file,
FALSE,
"GIMP shortcutsrc\n\n"
"If you delete this file, all shortcuts "
"will be reset to defaults.",
error);
if (! writer)
return FALSE;
actions = g_action_group_list_actions (G_ACTION_GROUP (application));
gimp_config_writer_open (writer, "protocol-version");
gimp_config_writer_printf (writer, "%d", GIMP_PROTOCOL_VERSION);
gimp_config_writer_close (writer);
gimp_config_writer_open (writer, "file-version");
gimp_config_writer_printf (writer, "%d", SHORTCUTS_RC_FILE_VERSION);
gimp_config_writer_close (writer);
gimp_config_writer_linefeed (writer);
for (gint i = 0; actions[i] != NULL; i++)
{
gchar **accels;
gchar *detailed_name;
gimp_config_writer_open (writer, "action");
gimp_config_writer_string (writer, actions[i]);
detailed_name = g_strdup_printf ("app.%s", actions[i]);
accels = gtk_application_get_accels_for_action (application, detailed_name);
for (gint j = 0; accels[j]; j++)
gimp_config_writer_string (writer, accels[j]);
gimp_config_writer_close (writer);
g_strfreev (accels);
g_free (detailed_name);
}
g_strfreev (actions);
return gimp_config_writer_finish (writer, "end of shortcutsrc", error);
}
/* Private functions */
static GTokenType
shortcuts_action_deserialize (GScanner *scanner,
GtkApplication *application)
{
GStrvBuilder *builder;
gchar *action_name;
gchar *accel;
gchar **accels;
if (! gimp_scanner_parse_string (scanner, &action_name))
return G_TOKEN_STRING;
builder = g_strv_builder_new ();
while (gimp_scanner_parse_string (scanner, &accel))
{
g_strv_builder_add (builder, accel);
g_free (accel);
}
accels = g_strv_builder_end (builder);
if (g_action_group_has_action (G_ACTION_GROUP (application), action_name))
{
gchar *detailed_name;
detailed_name = g_strdup_printf ("app.%s", action_name);
gtk_application_set_accels_for_action (application, detailed_name,
(const gchar **) accels);
g_free (detailed_name);
}
else
{
/* Don't set a breaking error, just output on stderr, so that we can make a
* notice while still loading other actions.
*/
g_printerr ("INFO: not existing action '%s' was ignored from the shortcutsrc file.\n",
action_name);
}
g_strv_builder_unref (builder);
g_free (action_name);
g_strfreev (accels);
if (! gimp_scanner_parse_token (scanner, G_TOKEN_RIGHT_PAREN))
return G_TOKEN_RIGHT_PAREN;
return G_TOKEN_LEFT_PAREN;
}

33
app/menus/shortcuts-rc.h Normal file
View File

@ -0,0 +1,33 @@
/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* shortcuts-rc.h
* Copyright (C) 2023 Jehan
*
* 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 3 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, see <https://www.gnu.org/licenses/>.
*/
#ifndef __SHORTCUTS_RC_H__
#define __SHORTCUTS_RC_H__
gboolean shortcuts_rc_parse (GtkApplication *application,
GFile *file,
GError **error);
gboolean shortcuts_rc_write (GtkApplication *application,
GFile *file,
GError **error);
#endif /* __SHORTCUTS_RC_H__ */