app, menus: fix tool options menus.

My last commit message was slightly wrong. I had one last special case to
handle: the ability to only create sub-GimpMenuShell (i.e. at a specific path
other than the root).
We are using this ability in particular for tool options submenus.
This commit is contained in:
Jehan 2023-03-03 13:59:24 +01:00
parent 033aa8e71b
commit 57a0fd4dbf
10 changed files with 336 additions and 62 deletions

View File

@ -398,6 +398,7 @@ gimp_image_window_constructed (GObject *object)
gtk_application_set_menubar (private->gimp->app, G_MENU_MODEL (model));
#else
private->menubar = gimp_menu_bar_new (model, private->menubar_manager);
g_object_unref (model);
gtk_box_pack_start (GTK_BOX (private->main_vbox),
private->menubar, FALSE, FALSE, 0);

View File

@ -44,8 +44,6 @@ static void tool_options_menu_update_after (GimpUIManager *manager,
gpointer update_data,
const gchar *ui_path);
static void tool_options_menu_update_presets (GimpUIManager *manager,
guint merge_id,
const gchar *ui_path,
const gchar *menu_path,
const gchar *which_action,
GimpContainer *presets);
@ -96,7 +94,6 @@ tool_options_menu_update_after (GimpUIManager *manager,
{
GimpContext *context;
GimpToolInfo *tool_info;
guint merge_id;
context = gimp_get_user_context (manager->gimp);
tool_info = gimp_context_get_tool (context);
@ -104,61 +101,48 @@ tool_options_menu_update_after (GimpUIManager *manager,
if (! tool_info->presets)
return;
merge_id = gimp_ui_manager_new_merge_id (manager);
tool_options_menu_update_presets (manager, "Save Tool Preset",
"save", tool_info->presets);
g_object_set_data (G_OBJECT (manager), "tool-options-merge-id",
GUINT_TO_POINTER (merge_id));
tool_options_menu_update_presets (manager, "Restore Tool Preset",
"restore", tool_info->presets);
tool_options_menu_update_presets (manager, merge_id, ui_path,
"Save", "save",
tool_info->presets);
tool_options_menu_update_presets (manager, "Edit Tool Preset",
"edit", tool_info->presets);
tool_options_menu_update_presets (manager, merge_id, ui_path,
"Restore", "restore",
tool_info->presets);
tool_options_menu_update_presets (manager, merge_id, ui_path,
"Edit", "edit",
tool_info->presets);
tool_options_menu_update_presets (manager, merge_id, ui_path,
"Delete", "delete",
tool_info->presets);
tool_options_menu_update_presets (manager, "Delete Tool Preset",
"delete", tool_info->presets);
gimp_ui_manager_ensure_update (manager);
}
static void
tool_options_menu_update_presets (GimpUIManager *manager,
guint merge_id,
const gchar *ui_path,
const gchar *menu_path,
const gchar *which_action,
GimpContainer *presets)
{
gint n_children;
gint i;
gchar *action_prefix;
gint n_children;
gint i;
action_prefix = g_strdup_printf ("tool-options-%s-preset-", which_action);
gimp_ui_manager_remove_uis (manager, action_prefix);
n_children = gimp_container_get_n_children (presets);
for (i = 0; i < n_children; i++)
{
gchar *action_name;
gchar *path;
action_name = g_strdup_printf ("tool-options-%s-preset-%03d",
which_action, i);
path = g_strdup_printf ("%s/%s", ui_path, menu_path);
action_name = g_strdup_printf ("%s%03d", action_prefix, i);
path = g_strdup_printf ("/Tool Options Menu/%s", menu_path);
/* TODO GMenu: this is for the "/tool-options-popup/" which doesn't use
* our new infra with GAction/GMenu yet.
*/
gimp_ui_manager_add_ui (manager, merge_id,
path, action_name, action_name,
GTK_UI_MANAGER_MENUITEM,
FALSE);
gimp_ui_manager_add_ui2 (manager, path, action_name, NULL, FALSE);
g_free (action_name);
g_free (path);
}
g_free (action_prefix);
}

View File

@ -38,6 +38,8 @@
#include "gimpradioaction.h"
#include "gimpuimanager.h"
#define GIMP_MENU_ACTION_KEY "gimp-menu-action"
/**
* GimpMenu:
@ -73,6 +75,9 @@ static void gimp_menu_add_ui (GimpMenuShell *shel
const gchar *action_name,
const gchar *placeholder_key,
gboolean top);
static void gimp_menu_remove_ui (GimpMenuShell *shell,
const gchar **paths,
const gchar *action_name);
static void gimp_menu_add_placeholder (GimpMenu *menu,
const gchar *label);
@ -81,6 +86,8 @@ static void gimp_menu_add_action (GimpMenu *menu
const gchar *placeholder_key,
gboolean top,
GtkRadioMenuItem **group);
static void gimp_menu_remove_action (GimpMenu *menu,
const gchar *action_name);
static void gimp_menu_toggle_item_toggled (GtkWidget *item,
GAction *action);
@ -145,8 +152,9 @@ gimp_menu_init (GimpMenu *menu)
static void
gimp_menu_iface_init (GimpMenuShellInterface *iface)
{
iface->append = gimp_menu_append;
iface->add_ui = gimp_menu_add_ui;
iface->append = gimp_menu_append;
iface->add_ui = gimp_menu_add_ui;
iface->remove_ui = gimp_menu_remove_ui;
}
static void
@ -327,6 +335,31 @@ gimp_menu_add_ui (GimpMenuShell *shell,
}
}
static void
gimp_menu_remove_ui (GimpMenuShell *shell,
const gchar **paths,
const gchar *action_name)
{
GimpMenu *menu = GIMP_MENU (shell);
g_return_if_fail (paths != NULL);
if (paths[0] == NULL)
{
gimp_menu_remove_action (menu, action_name);
}
else
{
GtkWidget *submenu = NULL;
submenu = g_tree_lookup (menu->priv->submenus, paths[0]);
g_return_if_fail (submenu != NULL);
gimp_menu_remove_ui (GIMP_MENU_SHELL (submenu), paths + 1, action_name);
}
}
/* Public functions */
@ -466,6 +499,7 @@ gimp_menu_add_action (GimpMenu *menu,
}
gimp_action_set_proxy (GIMP_ACTION (action), item);
g_object_set_data (G_OBJECT (item), GIMP_MENU_ACTION_KEY, action);
gtk_widget_set_sensitive (GTK_WIDGET (item),
gimp_action_is_sensitive (GIMP_ACTION (action), NULL));
@ -532,6 +566,45 @@ gimp_menu_add_action (GimpMenu *menu,
item, 0);
}
static void
gimp_menu_remove_action (GimpMenu *menu,
const gchar *action_name)
{
GimpUIManager *manager;
GApplication *app;
GList *children;
GAction *action;
g_return_if_fail (GIMP_IS_MENU (menu));
manager = gimp_menu_shell_get_manager (GIMP_MENU_SHELL (menu));
app = manager->gimp->app;
if (g_str_has_prefix (action_name, "app."))
action = g_action_map_lookup_action (G_ACTION_MAP (app), action_name + 4);
else
action = g_action_map_lookup_action (G_ACTION_MAP (app), action_name);
g_return_if_fail (GIMP_IS_ACTION (action));
children = gtk_container_get_children (GTK_CONTAINER (menu));
for (GList *iter = children; iter; iter = iter->next)
{
GtkWidget *child = iter->data;
GAction *item_action;
item_action = g_object_get_data (G_OBJECT (child), GIMP_MENU_ACTION_KEY);
if (item_action == action)
{
gtk_widget_destroy (child);
break;
}
}
g_list_free (children);
}
static void
gimp_menu_toggle_item_toggled (GtkWidget *item,
GAction *action)

View File

@ -38,10 +38,11 @@ typedef struct _GimpMenuShellPrivate GimpMenuShellPrivate;
struct _GimpMenuShellPrivate
{
GimpUIManager *manager;
gchar *update_signal;
GimpUIManager *manager;
gchar *update_signal;
gchar **path_prefix;
GRegex *path_regex;
GRegex *path_regex;
};
@ -55,13 +56,20 @@ static void gimp_menu_shell_ui_added (GimpUIManager *m
const gchar *placeholder_key,
gboolean top,
GimpMenuShell *shell);
static void gimp_menu_shell_ui_removed (GimpUIManager *manager,
const gchar *path,
const gchar *action_name,
GimpMenuShell *shell);
static void gimp_menu_shell_append_model_drop_top (GimpMenuShell *shell,
GMenuModel *model);
static gchar ** gimp_menu_shell_break_path (GimpMenuShell *shell,
const gchar *path);
static gboolean gimp_menu_shell_is_subpath (GimpMenuShell *shell,
const gchar *path,
gchar ***paths,
gint *index);
G_DEFINE_INTERFACE (GimpMenuShell, gimp_menu_shell, G_TYPE_OBJECT)
@ -87,12 +95,20 @@ gimp_menu_shell_fill (GimpMenuShell *shell,
const gchar *update_signal,
gboolean drop_top_submenu)
{
GimpMenuShellPrivate *priv;
gchar **path_prefix;
g_return_if_fail (GTK_IS_CONTAINER (shell));
gtk_container_foreach (GTK_CONTAINER (shell),
(GtkCallback) gtk_widget_destroy,
NULL);
priv = GET_PRIVATE (shell);
g_clear_pointer (&priv->path_prefix, g_strfreev);
path_prefix = g_object_get_data (G_OBJECT (model), "gimp-ui-manager-model-paths");
priv->path_prefix = g_strdupv (path_prefix);
if (drop_top_submenu)
gimp_menu_shell_append_model_drop_top (shell, model);
else
@ -100,20 +116,24 @@ gimp_menu_shell_fill (GimpMenuShell *shell,
if (update_signal != NULL)
{
GimpMenuShellPrivate *priv = GET_PRIVATE (shell);
if (priv->update_signal != NULL)
{
g_free (priv->update_signal);
g_signal_handlers_disconnect_by_func (priv->manager,
G_CALLBACK (gimp_menu_shell_ui_added),
shell);
g_signal_handlers_disconnect_by_func (priv->manager,
G_CALLBACK (gimp_menu_shell_ui_removed),
shell);
}
priv->update_signal = g_strdup (update_signal);
g_signal_connect (priv->manager, update_signal,
G_CALLBACK (gimp_menu_shell_ui_added),
shell);
g_signal_connect_object (priv->manager, update_signal,
G_CALLBACK (gimp_menu_shell_ui_added),
shell, 0);
g_signal_connect_object (priv->manager, "ui-removed",
G_CALLBACK (gimp_menu_shell_ui_removed),
shell, 0);
gimp_ui_manager_foreach_ui (priv->manager,
(GimpUIMenuCallback) gimp_menu_shell_ui_added,
shell);
@ -133,6 +153,7 @@ gimp_menu_shell_init (GimpMenuShell *shell)
priv = GET_PRIVATE (shell);
priv->update_signal = NULL;
priv->path_prefix = NULL;
priv->path_regex = g_regex_new ("/+", 0, 0, NULL);
}
@ -245,6 +266,7 @@ static void
gimp_menu_shell_private_finalize (GimpMenuShellPrivate *priv)
{
g_free (priv->update_signal);
g_clear_pointer (&priv->path_prefix, g_strfreev);
g_clear_pointer (&priv->path_regex, g_regex_unref);
g_slice_free (GimpMenuShellPrivate, priv);
@ -258,17 +280,37 @@ gimp_menu_shell_ui_added (GimpUIManager *manager,
gboolean top,
GimpMenuShell *shell)
{
gchar **paths = gimp_menu_shell_break_path (shell, path);
gchar **paths = NULL;
gint paths_idx = 0;
if (GIMP_MENU_SHELL_GET_INTERFACE (shell)->add_ui)
if (GIMP_MENU_SHELL_GET_INTERFACE (shell)->add_ui &&
gimp_menu_shell_is_subpath (shell, path, &paths, &paths_idx))
GIMP_MENU_SHELL_GET_INTERFACE (shell)->add_ui (shell,
(const gchar **) paths,
(const gchar **) (paths + paths_idx),
action_name,
placeholder_key, top);
g_strfreev (paths);
}
static void
gimp_menu_shell_ui_removed (GimpUIManager *manager,
const gchar *path,
const gchar *action_name,
GimpMenuShell *shell)
{
gchar **paths = NULL;
gint paths_idx = 0;
if (GIMP_MENU_SHELL_GET_INTERFACE (shell)->remove_ui &&
gimp_menu_shell_is_subpath (shell, path, &paths, &paths_idx))
GIMP_MENU_SHELL_GET_INTERFACE (shell)->remove_ui (shell,
(const gchar **) paths + paths_idx,
action_name);
g_strfreev (paths);
}
static void
gimp_menu_shell_append_model_drop_top (GimpMenuShell *shell,
GMenuModel *model)
@ -278,7 +320,27 @@ gimp_menu_shell_append_model_drop_top (GimpMenuShell *shell,
g_return_if_fail (GTK_IS_CONTAINER (shell));
if (g_menu_model_get_n_items (model) == 1)
submenu = g_menu_model_get_item_link (model, 0, G_MENU_LINK_SUBMENU);
{
GimpMenuShellPrivate *priv = GET_PRIVATE (shell);
gchar *label = NULL;
submenu = g_menu_model_get_item_link (model, 0, G_MENU_LINK_SUBMENU);
if (submenu)
{
GStrvBuilder *paths_builder = g_strv_builder_new ();
g_menu_model_get_item_attribute (submenu, 0, G_MENU_ATTRIBUTE_LABEL, "s", &label);
g_strv_builder_addv (paths_builder, (const char **) priv->path_prefix);
g_strv_builder_add (paths_builder, label);
g_strfreev (priv->path_prefix);
priv->path_prefix = g_strv_builder_end (paths_builder);
g_strv_builder_unref (paths_builder);
}
g_free (label);
}
GIMP_MENU_SHELL_GET_INTERFACE (shell)->append (shell, submenu != NULL ? submenu : model);
@ -320,3 +382,35 @@ gimp_menu_shell_break_path (GimpMenuShell *shell,
return paths;
}
static gboolean
gimp_menu_shell_is_subpath (GimpMenuShell *shell,
const gchar *path,
gchar ***paths,
gint *index)
{
GimpMenuShellPrivate *priv;
gboolean is_subpath = TRUE;
*index = 0;
*paths = gimp_menu_shell_break_path (shell, path);
priv = GET_PRIVATE (shell);
if (priv->path_prefix)
{
*index = g_strv_length (priv->path_prefix);
is_subpath = FALSE;
if (*index <= g_strv_length (*paths))
{
gint i;
for (i = 0; i < *index; i ++)
if (g_strcmp0 (priv->path_prefix[i], (*paths)[i]) != 0)
break;
is_subpath = (i == *index);
}
}
return is_subpath;
}

View File

@ -51,6 +51,9 @@ struct _GimpMenuShellInterface
const gchar *action_name,
const gchar *placeholder_key,
gboolean top);
void (* remove_ui) (GimpMenuShell *shell,
const gchar **paths,
const gchar *action_name);
};

View File

@ -184,6 +184,7 @@ gimp_text_editor_new (const gchar *title,
toolbar_model = gimp_ui_manager_get_model (editor->ui_manager,
"/text-editor-toolbar");
toolbar = gimp_toolbar_new (toolbar_model, editor->ui_manager);
g_object_unref (toolbar_model);
if (toolbar)
{

View File

@ -398,7 +398,7 @@ gimp_tool_options_editor_save_clicked (GtkWidget *widget,
if (gtk_widget_get_sensitive (editor->p->restore_button) /* evil but correct */)
{
gimp_tool_options_editor_menu_popup (editor, widget,
"/tool-options-popup/Save");
"/tool-options-popup/Tool Options Menu/Save Tool Preset");
}
else
{
@ -413,7 +413,7 @@ gimp_tool_options_editor_restore_clicked (GtkWidget *widget,
GimpToolOptionsEditor *editor)
{
gimp_tool_options_editor_menu_popup (editor, widget,
"/tool-options-popup/Restore");
"/tool-options-popup/Tool Options Menu/Restore Tool Preset");
}
static void
@ -421,7 +421,7 @@ gimp_tool_options_editor_delete_clicked (GtkWidget *widget,
GimpToolOptionsEditor *editor)
{
gimp_tool_options_editor_menu_popup (editor, widget,
"/tool-options-popup/Delete");
"/tool-options-popup/Tool Options Menu/Delete Tool Preset");
}
static void

View File

@ -58,6 +58,7 @@ enum
SHOW_TOOLTIP,
HIDE_TOOLTIP,
UI_ADDED,
UI_REMOVED,
LAST_SIGNAL
};
@ -181,7 +182,15 @@ gimp_ui_manager_class_init (GimpUIManagerClass *klass)
G_TYPE_STRING,
G_TYPE_STRING,
G_TYPE_BOOLEAN);
manager_signals[UI_REMOVED] =
g_signal_new ("ui-removed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GimpUIManagerClass, ui_removed),
NULL, NULL, NULL,
G_TYPE_NONE, 2,
G_TYPE_STRING,
G_TYPE_STRING);
g_object_class_install_property (object_class, PROP_NAME,
g_param_spec_string ("name",
@ -286,7 +295,6 @@ gimp_ui_manager_finalize (GObject *object)
g_list_free_full (manager->ui_items,
(GDestroyNotify) gimp_ui_manager_menu_item_free);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
@ -513,10 +521,24 @@ gimp_ui_manager_get_model (GimpUIManager *manager,
{
GimpUIManagerUIEntry *entry;
GMenuModel *model;
gchar *root;
gchar *submenus;
gchar *filename;
gchar *full_basename;
GStrvBuilder *paths_builder = g_strv_builder_new ();
GStrv paths;
root = g_strdup (path);
submenus = strstr (root + 1, "/");
if (submenus != NULL)
{
*submenus = '\0';
if (++submenus == '\0')
submenus = NULL;
}
entry = gimp_ui_manager_entry_ensure (manager, path);
g_return_val_if_fail (entry != NULL, NULL);
full_basename = g_strconcat (entry->basename, ".ui", NULL);
filename = g_build_filename (gimp_data_directory (), "menus",
full_basename, NULL);
@ -527,13 +549,62 @@ gimp_ui_manager_get_model (GimpUIManager *manager,
entry->builder = gtk_builder_new_from_file (filename);
gimp_ui_manager_fill_model (manager,
G_MENU_MODEL (gtk_builder_get_object (entry->builder, path)));
G_MENU_MODEL (gtk_builder_get_object (entry->builder, root)));
}
model = G_MENU_MODEL (gtk_builder_get_object (entry->builder, path));
model = G_MENU_MODEL (gtk_builder_get_object (entry->builder, root));
g_free (filename);
g_free (full_basename);
g_return_val_if_fail (G_IS_MENU (model), NULL);
g_object_ref (model);
while (submenus != NULL)
{
const gchar *submenu = submenus;
gint n_items;
gint i;
submenus = strstr (submenu + 1, "/");
if (submenus != NULL)
{
*submenus = '\0';
if (++submenus == '\0')
submenus = NULL;
}
n_items = g_menu_model_get_n_items (model);
for (i = 0; i < n_items; i++)
{
GMenuModel *submodel;
gchar *label = NULL;
gchar *canon_label = NULL;
submodel = g_menu_model_get_item_link (model, i, G_MENU_LINK_SUBMENU);
g_menu_model_get_item_attribute (model, i, G_MENU_ATTRIBUTE_LABEL, "s", &label);
if (label)
canon_label = gimp_menu_shell_make_canonical_path (label);
if (submodel && g_strcmp0 (canon_label, submenu) == 0)
{
g_strv_builder_add (paths_builder, canon_label);
g_object_unref (model);
model = submodel;
g_free (label);
break;
}
g_clear_object (&submodel);
g_free (label);
g_free (canon_label);
}
g_return_val_if_fail (i < n_items, NULL);
}
paths = g_strv_builder_end (paths_builder);
g_strv_builder_unref (paths_builder);
g_object_set_data_full (model, "gimp-ui-manager-model-paths", paths, g_strfreev);
g_free (root);
return model;
}
@ -569,6 +640,30 @@ gimp_ui_manager_remove_ui (GimpUIManager *manager,
gtk_ui_manager_remove_ui ((GtkUIManager *) manager, merge_id);
}
void
gimp_ui_manager_remove_uis (GimpUIManager *manager,
const gchar *action_name_prefix)
{
GList *iter = manager->ui_items;
while (iter != NULL)
{
GimpUIManagerMenuItem *item = iter->data;
GList *current_iter = iter;
/* Increment nearly in case we delete the list item. */
iter = iter->next;
if (action_name_prefix == NULL ||
g_str_has_prefix (item->action_name, action_name_prefix))
{
g_signal_emit (manager, manager_signals[UI_REMOVED], 0, item->path, item->action_name);
manager->ui_items = g_list_remove_link (manager->ui_items, current_iter);
gimp_ui_manager_menu_item_free (item);
}
}
}
void
gimp_ui_manager_add_ui2 (GimpUIManager *manager,
const gchar *path,
@ -739,10 +834,13 @@ gimp_ui_manager_ui_popup_at_widget (GimpUIManager *manager,
g_return_if_fail (ui_path != NULL);
g_return_if_fail (GTK_IS_WIDGET (widget));
model = gimp_ui_manager_get_model (manager, ui_path);
menu = gimp_menu_new (manager);
gtk_menu_attach_to_widget (GTK_MENU (menu), widget, NULL);
model = gimp_ui_manager_get_model (manager, ui_path);
g_return_if_fail (model != NULL);
gimp_menu_shell_fill (GIMP_MENU_SHELL (menu), model, "ui-added", TRUE);
g_object_unref (model);
if (! menu)
return;
@ -756,6 +854,7 @@ gimp_ui_manager_ui_popup_at_widget (GimpUIManager *manager,
child_model = gimp_ui_manager_get_model (child_ui_manager, child_ui_path);
child_menu = gimp_menu_new (child_ui_manager);
gimp_menu_shell_fill (GIMP_MENU_SHELL (child_menu), child_model, "ui-added", FALSE);
g_object_unref (child_model);
gimp_menu_merge (GIMP_MENU (menu), GIMP_MENU (child_menu), TRUE);
}
@ -796,6 +895,7 @@ gimp_ui_manager_ui_popup_at_pointer (GimpUIManager *manager,
menu = gimp_menu_new (manager);
gtk_menu_attach_to_widget (GTK_MENU (menu), attached_widget, NULL);
gimp_menu_shell_fill (GIMP_MENU_SHELL (menu), model, "ui-added", TRUE);
g_object_unref (model);
if (! menu)
return;
@ -837,6 +937,7 @@ gimp_ui_manager_ui_popup_at_rect (GimpUIManager *manager,
menu = gimp_menu_new (manager);
gtk_menu_attach_to_widget (GTK_MENU (menu), attached_widget, NULL);
gimp_menu_shell_fill (GIMP_MENU_SHELL (menu), model, "ui-added", TRUE);
g_object_unref (model);
if (! menu)
return;
@ -1485,6 +1586,8 @@ gimp_ui_manager_fill_model (GimpUIManager *manager,
}
g_free (label);
g_free (action_name);
g_clear_object (&subsection);
g_clear_object (&submenu);
}
}

View File

@ -84,6 +84,9 @@ struct _GimpUIManagerClass
const gchar *action_name,
const gchar *placeholder,
gboolean top);
void (* ui_removed) (GimpUIManager *manager,
const gchar *path,
const gchar *action_name);
};
@ -123,6 +126,8 @@ void gimp_ui_manager_add_ui (GimpUIManager *manager,
gboolean top);
void gimp_ui_manager_remove_ui (GimpUIManager *manager,
guint merge_id);
void gimp_ui_manager_remove_uis (GimpUIManager *manager,
const gchar *action_name_prefix);
void gimp_ui_manager_add_ui2 (GimpUIManager *manager,
const gchar *path,
const gchar *action_name,

View File

@ -6,14 +6,24 @@
<attribute name="label" translatable="yes" context="tool-options-action">Tool Options Menu</attribute>
<attribute name="icon">gimp-tool-options</attribute>
<submenu>
<attribute name="label">Save</attribute>
<attribute name="label" translatable="yes" context="tool-options-action">_Save Tool Preset</attribute>
<attribute name="icon">document-save</attribute>
<section>
<item><attribute name="action">app.tool-options-save-new-preset</attribute></item>
</section>
</submenu>
<item><attribute name="action">app.tool-options-restore-preset-menu</attribute></item>
<item><attribute name="action">app.tool-options-edit-preset-menu</attribute></item>
<item><attribute name="action">app.tool-options-delete-preset-menu</attribute></item>
<submenu>
<attribute name="label" translatable="yes" context="tool-options-action">_Restore Tool Preset</attribute>
<attribute name="icon">document-revert</attribute>
</submenu>
<submenu>
<attribute name="label" translatable="yes" context="tool-options-action">E_dit Tool Preset</attribute>
<attribute name="icon">gtk-edit</attribute>
</submenu>
<submenu>
<attribute name="label" translatable="yes" context="tool-options-action">_Delete Tool Preset</attribute>
<attribute name="icon">edit-delete</attribute>
</submenu>
<section>
<item><attribute name="action">app.tool-options-reset</attribute></item>
<item><attribute name="action">app.tool-options-reset-all</attribute></item>