app, menus: very early prototype for a GimpMenu.

This demonstrates a first version of our replacing menu, using GAction and
GMenuModel. I had to make our own subclass of GtkMenu to process the model (from
a .ui XML file) for the following reasons:

* gtk_menu_new_from_model() doesn't support tooltips, which is a feature we use
  quite extensively in GIMP: with all our filters, being able to give a longer
  description is often useful; moreover we use tooltips to give hints about why
  a menu item is deactivated as well.
  Unfortunately it looks like GTK doesn't consider this lack as a problem and
  don't plan on adding tooltip support.
  See: https://gitlab.gnome.org/GNOME/gtk/-/issues/785
* I won't to avoid copying action's label and icons in the .ui file. This only
  duplicates strings and would be a source of issues each time we change
  action's strings (we'd have to do it in 2 places, which someone will
  inevitably forget).

Now it still has various issues:

* The syncing between actions and menu items need to be cleaned up. It's still
  in early demo code.
* It uses directly some Gtk*Action code because GimpRadioAction and
  GimpToggleAction are not directly related right now (only through their
  parents).
* gtk_application_set_menubar() might still be necessary on macOS as I think
  it's what enables the native menu system on this OS. It means that we'll have
  to edit the menu model to add back the labels (as this function does not
  extract these from the linked action since GAction has no label or icon
  concept).
* Icons are not taken into account right now.
* I'll have to verify if GimpAction with proxy work (but my guess is that right
  now, it won't).
* Action's active state is not synced with menu item active state right now.
* Various actions are inserted live, such as opened images, opened views,
  recently opened images, and so on. This needs to be implemented back.
* Plug-ins need to be able to create their own menu item into this new menu.
* For all these various reasons, I'm keeping the old menu around, for the sake
  of comparison, until the time the new one becomes feature-full.

Part of this commit is inspired by !558 and obsoletes this MR.
This commit is contained in:
Jehan 2023-02-07 13:59:20 +01:00
parent b6a99077dc
commit 342ad2c37f
8 changed files with 1331 additions and 23 deletions

View file

@ -50,6 +50,7 @@
#include "widgets/gimpdockcolumns.h"
#include "widgets/gimpdockcontainer.h"
#include "widgets/gimphelp-ids.h"
#include "widgets/gimpmenu.h"
#include "widgets/gimpmenufactory.h"
#include "widgets/gimpsessioninfo.h"
#include "widgets/gimpsessioninfo-aux.h"
@ -116,6 +117,7 @@ struct _GimpImageWindowPrivate
GimpDisplayShell *active_shell;
GtkWidget *main_vbox;
GtkWidget *new_menubar;
GtkWidget *menubar;
GtkWidget *hbox;
GtkWidget *left_hpane;
@ -347,6 +349,9 @@ gimp_image_window_constructed (GObject *object)
GimpImageWindowPrivate *private = GIMP_IMAGE_WINDOW_GET_PRIVATE (window);
GimpMenuFactory *menu_factory;
GimpGuiConfig *config;
GtkBuilder *builder;
GMenuModel *model;
gchar *filename;
G_OBJECT_CLASS (parent_class)->constructed (object);
@ -384,12 +389,24 @@ gimp_image_window_constructed (GObject *object)
gtk_widget_show (private->main_vbox);
/* Create the menubar */
#ifndef GDK_WINDOWING_QUARTZ
filename = g_build_filename (gimp_data_directory (), "menus",
"image-menu.ui", NULL);
builder = gtk_builder_new_from_file (filename);
model = G_MENU_MODEL (gtk_builder_get_object (builder, "/image-menubar"));
#ifdef GDK_WINDOWING_QUARTZ
/* macOS has its native menubar system, and this should support it. It means
* that we won't have tooltips on macOS menu though.
* TODO: Since the .ui file has no title/labels, I should edit the model to
* extract titles from actions.
*/
gtk_application_set_menubar (private->gimp->app, G_MENU_MODEL (model));
#else
/* TODO: this is the old menubar system. It should go away soon. */
private->menubar = gimp_ui_manager_get_widget (private->menubar_manager,
"/image-menubar");
#endif /* !GDK_WINDOWING_QUARTZ */
if (private->menubar)
{
gtk_box_pack_start (GTK_BOX (private->main_vbox),
private->menubar, FALSE, FALSE, 0);
@ -410,7 +427,33 @@ gimp_image_window_constructed (GObject *object)
g_signal_connect (private->menubar, "key-press-event",
G_CALLBACK (gimp_image_window_shell_events),
window);
}
private->new_menubar = gimp_menu_new (model, private->gimp);
gtk_box_pack_start (GTK_BOX (private->main_vbox),
private->new_menubar, FALSE, FALSE, 0);
/* make sure we can activate accels even if the menubar is invisible
* (see https://bugzilla.gnome.org/show_bug.cgi?id=137151)
*/
g_signal_connect (private->new_menubar, "can-activate-accel",
G_CALLBACK (gtk_true),
NULL);
/* active display callback */
g_signal_connect (private->new_menubar, "button-press-event",
G_CALLBACK (gimp_image_window_shell_events),
window);
g_signal_connect (private->new_menubar, "button-release-event",
G_CALLBACK (gimp_image_window_shell_events),
window);
g_signal_connect (private->new_menubar, "key-press-event",
G_CALLBACK (gimp_image_window_shell_events),
window);
#endif /* !GDK_WINDOWING_QUARTZ */
g_object_unref (builder);
g_free (filename);
/* Create the hbox that contains docks and images */
private->hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
@ -1347,6 +1390,8 @@ gimp_image_window_set_show_menubar (GimpImageWindow *window,
if (private->menubar)
gtk_widget_set_visible (private->menubar, show);
if (private->new_menubar)
gtk_widget_set_visible (private->new_menubar, show);
}
gboolean

View file

@ -272,6 +272,8 @@ libappwidgets_a_sources = \
gimplayermodecombobox.h \
gimplayertreeview.c \
gimplayertreeview.h \
gimpmenu.c \
gimpmenu.h \
gimpmenudock.c \
gimpmenudock.h \
gimpmenufactory.c \

308
app/widgets/gimpmenu.c Normal file
View file

@ -0,0 +1,308 @@
/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* gimpmenu.c
* Copyright (C) 2022 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 "widgets-types.h"
#include "core/gimp.h"
#include "gimpaction.h"
#include "gimpmenu.h"
#include "gimpradioaction.h"
#include "gimptoggleaction.h"
/**
* GimpMenu:
*
* Our own menu widget.
*
* We cannot use the simpler gtk_menu_new_from_model() because it lacks
* tooltip support and unfortunately GTK does not plan to implement this:
* https://gitlab.gnome.org/GNOME/gtk/-/issues/785
* This is why we need to implement our own GimpMenu subclass.
*/
enum
{
PROP_0,
PROP_GIMP,
PROP_MODEL
};
struct _GimpMenuPrivate
{
Gimp *gimp;
GMenuModel *model;
};
/* local function prototypes */
static void gimp_menu_dispose (GObject *object);
static void gimp_menu_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void gimp_menu_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void gimp_menu_update (GimpMenu *menu,
GtkContainer *container,
GMenuModel *model);
static void gimp_menu_radio_item_toggled (GtkWidget *item,
GAction *action);
G_DEFINE_TYPE_WITH_PRIVATE (GimpMenu, gimp_menu, GTK_TYPE_MENU_BAR)
#define parent_class gimp_menu_parent_class
/* private functions */
static void
gimp_menu_class_init (GimpMenuClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = gimp_menu_dispose;
object_class->get_property = gimp_menu_get_property;
object_class->set_property = gimp_menu_set_property;
g_object_class_install_property (object_class, PROP_GIMP,
g_param_spec_object ("gimp",
NULL, NULL,
GIMP_TYPE_GIMP,
GIMP_PARAM_WRITABLE |
G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (object_class, PROP_MODEL,
g_param_spec_object ("model",
NULL, NULL,
G_TYPE_MENU_MODEL,
GIMP_PARAM_READWRITE));
}
static void
gimp_menu_init (GimpMenu *menu)
{
menu->priv = gimp_menu_get_instance_private (menu);
}
static void
gimp_menu_dispose (GObject *object)
{
GimpMenu *menu = GIMP_MENU (object);
g_clear_object (&menu->priv->model);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
gimp_menu_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GimpMenu *menu = GIMP_MENU (object);
switch (property_id)
{
case PROP_MODEL:
menu->priv->model = g_value_dup_object (value);
gimp_menu_update (menu, NULL, NULL);
break;
case PROP_GIMP:
menu->priv->gimp = g_value_get_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_menu_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GimpMenu *menu = GIMP_MENU (object);
switch (property_id)
{
case PROP_MODEL:
g_value_set_object (value, menu->priv->model);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
/* Public functions */
GtkWidget *
gimp_menu_new (GMenuModel *model,
Gimp *gimp)
{
g_return_val_if_fail (GIMP_IS_GIMP (gimp) && G_IS_MENU_MODEL (model), NULL);
return g_object_new (GIMP_TYPE_MENU,
"model", model,
"gimp", gimp,
NULL);
}
/* Private functions */
static void
gimp_menu_update (GimpMenu *menu,
GtkContainer *container,
GMenuModel *model)
{
static GtkRadioMenuItem *group = NULL;
gint n_items;
if (container == NULL)
{
container = GTK_CONTAINER (menu);
model = menu->priv->model;
}
n_items = g_menu_model_get_n_items (model);
for (gint i = 0; i < n_items; i++)
{
GMenuModel *subsection;
GMenuModel *submenu;
GtkWidget *item;
gchar *label = NULL;
gchar *action_name = NULL;
subsection = g_menu_model_get_item_link (model, i, G_MENU_LINK_SECTION);
submenu = 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);
g_menu_model_get_item_attribute (model, i, G_MENU_ATTRIBUTE_ACTION, "s", &action_name);
if (subsection != NULL)
{
group = NULL;
item = gtk_separator_menu_item_new ();
gtk_container_add (container, item);
gtk_widget_show (item);
gimp_menu_update (menu, container, subsection);
item = gtk_separator_menu_item_new ();
gtk_container_add (container, item);
gtk_widget_show (item);
}
else if (submenu != NULL)
{
GtkWidget *subcontainer;
group = NULL;
item = gtk_menu_item_new_with_mnemonic (label);
gtk_container_add (container, item);
gtk_widget_show (item);
subcontainer = gtk_menu_new ();
gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), subcontainer);
gtk_widget_show (subcontainer);
gimp_menu_update (menu, GTK_CONTAINER (subcontainer), submenu);
}
else
{
GAction *action;
const gchar *action_label;
g_return_if_fail (g_str_has_prefix (action_name, "app."));
action = g_action_map_lookup_action (G_ACTION_MAP (menu->priv->gimp->app), action_name + 4);
g_return_if_fail (GIMP_IS_ACTION (action));
action_label = gimp_action_get_label (GIMP_ACTION (action));
g_return_if_fail (action_label != NULL);
if (GIMP_IS_TOGGLE_ACTION (action))
{
item = gtk_check_menu_item_new_with_mnemonic (action_label);
gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item),
gimp_toggle_action_get_active (GIMP_TOGGLE_ACTION (action)));
group = NULL;
}
else if (GIMP_IS_RADIO_ACTION (action))
{
item = gtk_radio_menu_item_new_with_mnemonic_from_widget (group, action_label);
gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item),
/* TODO: see comment in gimp_menu_radio_item_toggled(). */
gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
group = GTK_RADIO_MENU_ITEM (item);
g_signal_connect (item, "toggled",
G_CALLBACK (gimp_menu_radio_item_toggled),
action);
}
else
{
item = gtk_menu_item_new_with_mnemonic (action_label);
group = NULL;
}
gtk_actionable_set_action_name (GTK_ACTIONABLE (item), action_name);
if (gimp_action_get_tooltip (GIMP_ACTION (action)))
gtk_widget_set_tooltip_text (item,
gimp_action_get_tooltip (GIMP_ACTION (action)));
gtk_container_add (container, item);
gtk_widget_show (item);
}
g_free (label);
g_free (action_name);
}
}
static void
gimp_menu_radio_item_toggled (GtkWidget *item,
GAction *action)
{
gboolean active = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (item));
/* TODO: when we remove GtkAction dependency, GimpRadioAction should become a
* child of GimpToggleAction, and therefore, we'll be able to use
* gimp_toggle_action_set_active().
*/
gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), active);
}

55
app/widgets/gimpmenu.h Normal file
View file

@ -0,0 +1,55 @@
/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* gimpmenu.h
* Copyright (C) 2022 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 __GIMP_MENU_H__
#define __GIMP_MENU_H__
#define GIMP_TYPE_MENU (gimp_menu_get_type ())
#define GIMP_MENU(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_MENU, GimpMenu))
#define GIMP_MENU_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_MENU, GimpMenuClass))
#define GIMP_IS_MENU(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GIMP_TYPE_MENU))
#define GIMP_IS_MENU_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_MENU))
#define GIMP_MENU_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_MENU, GimpMenuClass))
typedef struct _GimpMenuPrivate GimpMenuPrivate;
typedef struct _GimpMenuClass GimpMenuClass;
struct _GimpMenu
{
GtkMenuBar parent_instance;
GimpMenuPrivate *priv;
};
struct _GimpMenuClass
{
GtkMenuBarClass parent_class;
};
GType gimp_menu_get_type (void) G_GNUC_CONST;
GtkWidget * gimp_menu_new (GMenuModel *model,
Gimp *gimp);
#endif /* __GIMP_MENU_H__ */

View file

@ -137,6 +137,7 @@ libappwidgets_sources = [
'gimplayermodebox.c',
'gimplayermodecombobox.c',
'gimplayertreeview.c',
'gimpmenu.c',
'gimpmenudock.c',
'gimpmenufactory.c',
'gimpmessagebox.c',

View file

@ -210,6 +210,7 @@ typedef struct _GimpLanguageStore GimpLanguageStore;
typedef struct _GimpLayerModeBox GimpLayerModeBox;
typedef struct _GimpLayerModeComboBox GimpLayerModeComboBox;
typedef struct _GimpMessageBox GimpMessageBox;
typedef struct _GimpMenu GimpMenu;
typedef struct _GimpMeter GimpMeter;
typedef struct _GimpModifiersEditor GimpModifiersEditor;
typedef struct _GimpOverlayBox GimpOverlayBox;

872
menus/image-menu.ui Normal file
View file

@ -0,0 +1,872 @@
<?xml version="1.0" encoding="utf-8"?>
<interface>
<menu id="/image-menubar">
<!-- File menu -->
<submenu>
<attribute name="label" translatable="yes" context="file-action">_File</attribute>
<section>
<item><attribute name="action">app.image-new</attribute></item>
<submenu>
<attribute name="label" translatable="yes" context="file-action">Crea_te</attribute>
<item><attribute name="action">app.edit-paste-as-new-image</attribute></item>
<!-- TODO: "From Clipboard" and "From webpage", probably from plug-ins. -->
</submenu>
<item><attribute name="action">app.file-open</attribute></item>
<item><attribute name="action">app.file-open-as-layers</attribute></item>
<item><attribute name="action">app.file-open-location</attribute></item>
<submenu>
<attribute name="label" translatable="yes" context="file-action">Open _Recent</attribute>
<section>
<!-- TODO: in there the recent documents. -->
</section>
<item><attribute name="action">app.dialogs-document-history</attribute></item>
</submenu>
<!-- TODO: the debug-menu is hidden by default for stable releases -->
<submenu>
<attribute name="label" translatable="yes">_Debug</attribute>
<item><attribute name="action">app.debug-gtk-inspector</attribute></item>
<section>
<item><attribute name="action">app.debug-mem-profile</attribute></item>
<item><attribute name="action">app.debug-benchmark-projection</attribute></item>
<item><attribute name="action">app.debug-show-image-graph</attribute></item>
</section>
<section>
<item><attribute name="action">app.debug-dump-items</attribute></item>
<item><attribute name="action">app.debug-dump-managers</attribute></item>
<item><attribute name="action">app.debug-dump-keyboard-shortcuts</attribute></item>
<item><attribute name="action">app.debug-dump-attached-data</attribute></item>
</section>
</submenu>
</section>
<section>
<item><attribute name="action">app.file-save</attribute></item>
<item><attribute name="action">app.file-save-as</attribute></item>
<item><attribute name="action">app.file-save-a-copy</attribute></item>
<item><attribute name="action">app.file-revert</attribute></item>
</section>
<section>
<!-- TODO: overwrite and export same? -->
<item><attribute name="action">app.file-overwrite</attribute></item>
<item><attribute name="action">app.file-export</attribute></item>
<item><attribute name="action">app.file-export-as</attribute></item>
<item><attribute name="action">app.file-create-template</attribute></item>
</section>
<!-- TODO: print and send by email missing. -->
<section>
<item><attribute name="action">app.file-copy-location</attribute></item>
<item><attribute name="action">app.file-show-in-file-manager</attribute></item>
</section>
<section>
<item><attribute name="action">app.view-close</attribute></item>
<item><attribute name="action">app.file-close-all</attribute></item>
<item><attribute name="action">app.file-quit</attribute></item>
</section>
</submenu>
<!-- Edit menu -->
<submenu>
<attribute name="label" translatable="yes" context="edit-action">_Edit</attribute>
<section>
<item><attribute name="action">app.edit-undo</attribute></item>
<item><attribute name="action">app.edit-redo</attribute></item>
<item><attribute name="action">app.dialogs-undo-history</attribute></item>
</section>
<section>
<item><attribute name="action">app.edit-cut</attribute></item>
<item><attribute name="action">app.edit-copy</attribute></item>
<item><attribute name="action">app.edit-copy-visible</attribute></item>
<item><attribute name="action">app.edit-paste</attribute></item>
<item><attribute name="action">app.edit-paste-in-place</attribute></item>
<submenu>
<attribute name="label" translatable="yes" context="edit-action">Paste _as</attribute>
<item><attribute name="action">app.edit-paste-merged</attribute></item>
<item><attribute name="action">app.edit-paste-merged-in-place</attribute></item>
<item><attribute name="action">app.edit-paste-into</attribute></item>
<item><attribute name="action">app.edit-paste-into-in-place</attribute></item>
<item><attribute name="action">app.edit-paste-as-new-image-short</attribute></item>
</submenu>
<submenu>
<attribute name="label" translatable="yes" context="edit-action">_Buffer</attribute>
<item><attribute name="action">app.edit-named-cut</attribute></item>
<item><attribute name="action">app.edit-named-copy</attribute></item>
<item><attribute name="action">app.edit-named-copy-visible</attribute></item>
<item><attribute name="action">app.edit-named-paste</attribute></item>
</submenu>
</section>
<section>
<item><attribute name="action">app.edit-clear</attribute></item>
<item><attribute name="action">app.edit-fill-fg</attribute></item>
<item><attribute name="action">app.edit-fill-bg</attribute></item>
<item><attribute name="action">app.edit-fill-pattern</attribute></item>
<item><attribute name="action">app.select-fill</attribute></item>
<item><attribute name="action">app.vectors-fill</attribute></item>
<item><attribute name="action">app.select-stroke</attribute></item>
<item><attribute name="action">app.vectors-stroke</attribute></item>
</section>
<section>
<item><attribute name="action">app.dialogs-preferences</attribute></item>
<item><attribute name="action">app.dialogs-extensions</attribute></item>
<item><attribute name="action">app.dialogs-input-devices</attribute></item>
<item><attribute name="action">app.dialogs-keyboard-shortcuts</attribute></item>
<item><attribute name="action">app.dialogs-module-dialog</attribute></item>
<!-- TODO: missing "Units", probably from plug-in. -->
</section>
</submenu>
<!-- Select menu -->
<submenu>
<attribute name="label" translatable="yes" context="select-action">_Select</attribute>
<section>
<item><attribute name="action">app.select-all</attribute></item>
<item><attribute name="action">app.select-none</attribute></item>
<item><attribute name="action">app.select-invert</attribute></item>
<item><attribute name="action">app.select-float</attribute></item>
<item><attribute name="action">app.tools-by-color-select-short</attribute></item>
<item><attribute name="action">app.vectors-selection-from-vectors</attribute></item>
<item><attribute name="action">app.dialogs-selection-editor</attribute></item>
<item><attribute name="action">app.select-feather</attribute></item>
<item><attribute name="action">app.select-sharpen</attribute></item>
<item><attribute name="action">app.select-shrink</attribute></item>
<item><attribute name="action">app.select-grow</attribute></item>
<item><attribute name="action">app.select-border</attribute></item>
<item><attribute name="action">app.select-flood</attribute></item>
<!-- TODO: missing Distort and Rounded Rect, prob from plug-ins. -->
</section>
<section>
<item><attribute name="action">app.quick-mask-toggle</attribute></item>
<item><attribute name="action">app.select-save</attribute></item>
<item><attribute name="action">app.vectors-selection-to-vectors-short</attribute></item>
</section>
</submenu>
<!-- View menu -->
<submenu>
<attribute name="label" translatable="yes" context="view-action">_View</attribute>
<item><attribute name="action">app.view-new</attribute></item>
<item><attribute name="action">app.view-show-all</attribute></item>
<item><attribute name="action">app.view-dot-for-dot</attribute></item>
<submenu>
<!-- TODO: Zoom label shows current zoom value. -->
<attribute name="label" translatable="yes" context="view-action">_Zoom</attribute>
<section>
<item><attribute name="action">app.view-zoom-revert</attribute></item>
<item><attribute name="action">app.view-zoom-out</attribute></item>
<item><attribute name="action">app.view-zoom-in</attribute></item>
<item><attribute name="action">app.view-zoom-fit-in</attribute></item>
<item><attribute name="action">app.view-zoom-fill</attribute></item>
<item><attribute name="action">app.view-zoom-selection</attribute></item>
</section>
<section>
<item><attribute name="action">app.view-zoom-16-1</attribute></item>
<item><attribute name="action">app.view-zoom-8-1</attribute></item>
<item><attribute name="action">app.view-zoom-4-1</attribute></item>
<item><attribute name="action">app.view-zoom-2-1</attribute></item>
<item><attribute name="action">app.view-zoom-1-1</attribute></item>
<item><attribute name="action">app.view-zoom-1-2</attribute></item>
<item><attribute name="action">app.view-zoom-1-4</attribute></item>
<item><attribute name="action">app.view-zoom-1-8</attribute></item>
<item><attribute name="action">app.view-zoom-1-16</attribute></item>
</section>
<section>
<item><attribute name="action">app.view-zoom-other</attribute></item>
</section>
</submenu>
<submenu>
<!-- TODO: shows the current rotation angle. -->
<attribute name="label" translatable="yes" context="view-action">_Flip &amp; Rotate</attribute>
<section>
<item><attribute name="action">app.view-reset</attribute></item>
</section>
<section>
<item><attribute name="action">app.view-flip-horizontally</attribute></item>
<item><attribute name="action">app.view-flip-vertically</attribute></item>
</section>
<section>
<item><attribute name="action">app.view-rotate-15</attribute></item>
<item><attribute name="action">app.view-rotate-345</attribute></item>
<item><attribute name="action">app.view-rotate-90</attribute></item>
<item><attribute name="action">app.view-rotate-270</attribute></item>
<item><attribute name="action">app.view-rotate-180</attribute></item>
</section>
<section>
<item><attribute name="action">app.view-rotate-other</attribute></item>
</section>
</submenu>
<item><attribute name="action">app.view-scroll-center</attribute></item>
<section>
<item><attribute name="action">app.view-shrink-wrap</attribute></item>
<item><attribute name="action">app.view-fullscreen</attribute></item>
<submenu>
<attribute name="label" translatable="yes" context="view-action">Move to Screen</attribute>
<item><attribute name="action">app.view-open-display</attribute></item>
<!-- TODO: screen list. -->
</submenu>
</section>
<section>
<item><attribute name="action">app.view-navigation-window</attribute></item>
<item><attribute name="action">app.view-display-filters</attribute></item>
<submenu>
<attribute name="label" translatable="yes" context="view-action">_Color Management</attribute>
<section>
<item><attribute name="action">app.view-color-management-enable</attribute></item>
<item><attribute name="action">app.view-color-management-softproof</attribute></item>
</section>
<section>
<submenu>
<attribute name="label" translatable="yes" context="view-action">Display _Rendering Intent</attribute>
<item><attribute name="action">app.view-display-intent-perceptual</attribute></item>
<item><attribute name="action">app.view-display-intent-relative-colorimetric</attribute></item>
<item><attribute name="action">app.view-display-intent-saturation</attribute></item>
<item><attribute name="action">app.view-display-intent-absolute-colorimetric</attribute></item>
</submenu>
<item><attribute name="action">app.view-display-black-point-compensation</attribute></item>
</section>
<section>
<item><attribute name="action">app.view-softproof-gamut-check</attribute></item>
<item><attribute name="action">app.view-color-management-reset</attribute></item>
</section>
</submenu>
</section>
<section>
<item><attribute name="action">app.view-show-selection</attribute></item>
<item><attribute name="action">app.view-show-layer-boundary</attribute></item>
<item><attribute name="action">app.view-show-canvas-boundary</attribute></item>
<item><attribute name="action">app.view-show-guides</attribute></item>
<item><attribute name="action">app.view-show-grid</attribute></item>
<item><attribute name="action">app.view-show-sample-points</attribute></item>
</section>
<section>
<item><attribute name="action">app.view-snap-to-guides</attribute></item>
<item><attribute name="action">app.view-snap-to-grid</attribute></item>
<item><attribute name="action">app.view-snap-to-canvas</attribute></item>
<item><attribute name="action">app.view-snap-to-vectors</attribute></item>
</section>
<section>
<submenu>
<attribute name="label" translatable="yes" context="view-action">_Padding color</attribute>
<section>
<item><attribute name="action">app.view-padding-color-theme</attribute></item>
<item><attribute name="action">app.view-padding-color-light-check</attribute></item>
<item><attribute name="action">app.view-padding-color-dark-check</attribute></item>
<item><attribute name="action">app.view-padding-color-custom</attribute></item>
</section>
<section>
<item><attribute name="action">app.view-padding-color-in-show-all</attribute></item>
</section>
<section>
<item><attribute name="action">app.view-padding-color-prefs</attribute></item>
</section>
</submenu>
<item><attribute name="action">app.view-show-menubar</attribute></item>
<item><attribute name="action">app.view-show-rulers</attribute></item>
<item><attribute name="action">app.view-show-scrollbars</attribute></item>
<item><attribute name="action">app.view-show-statusbar</attribute></item>
</section>
</submenu>
<!-- Image menu -->
<submenu>
<attribute name="label" translatable="yes" context="image-action">_Image</attribute>
<section>
<item><attribute name="action">app.image-duplicate</attribute></item>
</section>
<section>
<submenu>
<attribute name="label" translatable="yes" context="image-action">_Mode</attribute>
<item><attribute name="action">app.image-convert-rgb</attribute></item>
<item><attribute name="action">app.image-convert-grayscale</attribute></item>
<item><attribute name="action">app.image-convert-indexed</attribute></item>
</submenu>
<submenu>
<attribute name="label" translatable="yes" context="image-action">_Encoding</attribute>
<section>
<item><attribute name="action">app.image-convert-u8</attribute></item>
<item><attribute name="action">app.image-convert-u16</attribute></item>
<item><attribute name="action">app.image-convert-u32</attribute></item>
<item><attribute name="action">app.image-convert-half</attribute></item>
<item><attribute name="action">app.image-convert-float</attribute></item>
<!-- TODO: this should be invisible except for special cases? -->
<item><attribute name="action">app.image-convert-double</attribute></item>
</section>
<section>
<item><attribute name="action">app.image-convert-linear</attribute></item>
<item><attribute name="action">app.image-convert-non-linear</attribute></item>
<!-- TODO: this should be invisible except for special cases? -->
<item><attribute name="action">app.image-convert-perceptual</attribute></item>
</section>
</submenu>
<submenu>
<attribute name="label" translatable="yes" context="image-action">Color Ma_nagement</attribute>
<section>
<item><attribute name="action">app.image-color-profile-use-srgb</attribute></item>
</section>
<section>
<item><attribute name="action">app.image-color-profile-assign</attribute></item>
<item><attribute name="action">app.image-color-profile-convert</attribute></item>
<item><attribute name="action">app.image-color-profile-discard</attribute></item>
</section>
<section>
<item><attribute name="action">app.image-color-profile-save</attribute></item>
</section>
<section>
<item><attribute name="action">app.image-softproof-profile</attribute></item>
</section>
<section>
<submenu>
<attribute name="label" translatable="yes" context="image-action">Soft-Proofing Re_ndering Intent</attribute>
<item><attribute name="action">app.image-softproof-intent-perceptual</attribute></item>
<item><attribute name="action">app.image-softproof-intent-relative-colorimetric</attribute></item>
<item><attribute name="action">app.image-softproof-intent-saturation</attribute></item>
<item><attribute name="action">app.image-softproof-intent-absolute-colorimetric</attribute></item>
</submenu>
<item><attribute name="action">app.image-softproof-black-point-compensation</attribute></item>
</section>
</submenu>
</section>
<section>
<submenu>
<attribute name="label" translatable="yes" context="image-action">_Transform</attribute>
<section>
<item><attribute name="action">app.image-flip-horizontal</attribute></item>
<item><attribute name="action">app.image-flip-vertical</attribute></item>
</section>
<section>
<item><attribute name="action">app.image-rotate-90</attribute></item>
<item><attribute name="action">app.image-rotate-270</attribute></item>
<item><attribute name="action">app.image-rotate-180</attribute></item>
<item><attribute name="action">app.tools-rotate-image-arbitrary</attribute></item>
</section>
</submenu>
<item><attribute name="action">app.image-resize</attribute></item>
<item><attribute name="action">app.image-resize-to-layers</attribute></item>
<item><attribute name="action">app.image-resize-to-selection</attribute></item>
<item><attribute name="action">app.image-print-size</attribute></item>
<item><attribute name="action">app.image-scale</attribute></item>
</section>
<section>
<item><attribute name="action">app.image-crop-to-selection</attribute></item>
<item><attribute name="action">app.image-crop-to-content</attribute></item>
</section>
<!-- TODO: missing (prob from plug-ins): Slice using guides, zealous crop -->
<section>
<item><attribute name="action">app.image-merge-layers</attribute></item>
<item><attribute name="action">app.image-flatten</attribute></item>
<!-- TODO: missing (prob from plug-ins): align visible layers -->
</section>
<section>
<submenu>
<attribute name="label" translatable="yes" context="image-action">_Guides</attribute>
<!-- TODO: missing (prob from plug-ins): all guides actions -->
</submenu>
<item><attribute name="action">app.image-configure-grid</attribute></item>
</section>
<section>
<item><attribute name="action">app.image-properties</attribute></item>
<!-- TODO: missing (prob from plug-ins): metadata -->
</section>
</submenu>
<!-- Layer menu -->
<submenu>
<attribute name="label" translatable="yes" context="layers-action">_Layer</attribute>
<section>
<item><attribute name="action">app.layers-new</attribute></item>
<item><attribute name="action">app.layers-new-from-visible</attribute></item>
<item><attribute name="action">app.layers-new-group</attribute></item>
<item><attribute name="action">app.layers-duplicate</attribute></item>
<!-- TODO: visible only when relevant? -->
<item><attribute name="action">app.layers-anchor</attribute></item>
<item><attribute name="action">app.layers-merge-down</attribute></item>
<!-- TODO: visible only when relevant? -->
<item><attribute name="action">app.layers-merge-group</attribute></item>
<item><attribute name="action">app.layers-delete</attribute></item>
</section>
<section>
<!-- TODO: all 3 visible only when relevant? -->
<item><attribute name="action">app.layers-text-discard</attribute></item>
<item><attribute name="action">app.layers-text-to-vectors</attribute></item>
<item><attribute name="action">app.layers-text-along-vectors</attribute></item>
</section>
<section>
<submenu>
<attribute name="label" translatable="yes" context="layers-action">Stac_k</attribute>
<section>
<item><attribute name="action">app.layers-select-previous</attribute></item>
<item><attribute name="action">app.layers-select-next</attribute></item>
<item><attribute name="action">app.layers-select-top</attribute></item>
<item><attribute name="action">app.layers-select-bottom</attribute></item>
</section>
<section>
<item><attribute name="action">app.layers-raise</attribute></item>
<item><attribute name="action">app.layers-lower</attribute></item>
<item><attribute name="action">app.layers-raise-to-top</attribute></item>
<item><attribute name="action">app.layers-lower-to-bottom</attribute></item>
</section>
<!-- TODO: miss Reverse Layer Order -->
</submenu>
<submenu>
<attribute name="label" translatable="yes" context="layers-action">_Mask</attribute>
<section>
<item><attribute name="action">app.layers-mask-add</attribute></item>
<item><attribute name="action">app.layers-mask-apply</attribute></item>
<item><attribute name="action">app.layers-mask-delete</attribute></item>
</section>
<section>
<item><attribute name="action">app.layers-mask-show</attribute></item>
<item><attribute name="action">app.layers-mask-edit</attribute></item>
<item><attribute name="action">app.layers-mask-disable</attribute></item>
</section>
<section>
<item><attribute name="action">app.layers-mask-selection-replace</attribute></item>
<item><attribute name="action">app.layers-mask-selection-add</attribute></item>
<item><attribute name="action">app.layers-mask-selection-subtract</attribute></item>
<item><attribute name="action">app.layers-mask-selection-intersect</attribute></item>
</section>
</submenu>
<submenu>
<attribute name="label" translatable="yes" context="layers-action">Tr_ansparency</attribute>
<section>
<item><attribute name="action">app.layers-alpha-add</attribute></item>
<item><attribute name="action">app.layers-alpha-remove</attribute></item>
<item><attribute name="action">app.filters-color-to-alpha</attribute></item>
<item><attribute name="action">app.filters-semi-flatten</attribute></item>
<item><attribute name="action">app.filters-threshold-alpha</attribute></item>
</section>
<section>
<item><attribute name="action">app.layers-alpha-selection-replace</attribute></item>
<item><attribute name="action">app.layers-alpha-selection-add</attribute></item>
<item><attribute name="action">app.layers-alpha-selection-subtract</attribute></item>
<item><attribute name="action">app.layers-alpha-selection-intersect</attribute></item>
</section>
</submenu>
<submenu>
<attribute name="label" translatable="yes" context="layers-action">_Transform</attribute>
<section>
<item><attribute name="action">app.drawable-flip-horizontal</attribute></item>
<item><attribute name="action">app.drawable-flip-vertical</attribute></item>
</section>
<section>
<item><attribute name="action">app.drawable-rotate-90</attribute></item>
<item><attribute name="action">app.drawable-rotate-270</attribute></item>
<item><attribute name="action">app.drawable-rotate-180</attribute></item>
<item><attribute name="action">app.tools-rotate-arbitrary</attribute></item>
</section>
<item><attribute name="action">app.filters-offset</attribute></item>
</submenu>
</section>
<section>
<item><attribute name="action">app.layers-resize</attribute></item>
<item><attribute name="action">app.layers-resize-to-image</attribute></item>
<item><attribute name="action">app.layers-scale</attribute></item>
<item><attribute name="action">app.layers-crop-to-selection</attribute></item>
<item><attribute name="action">app.layers-crop-to-content</attribute></item>
</section>
</submenu>
<!-- Colors Menu -->
<submenu>
<attribute name="label" translatable="yes" context="image-action">_Colors</attribute>
<section>
<item><attribute name="action">app.filters-color-balance</attribute></item>
<item><attribute name="action">app.filters-color-temperature</attribute></item>
<item><attribute name="action">app.filters-hue-chroma</attribute></item>
<item><attribute name="action">app.filters-hue-saturation</attribute></item>
<item><attribute name="action">app.filters-saturation</attribute></item>
<item><attribute name="action">app.filters-exposure</attribute></item>
<item><attribute name="action">app.filters-shadows-highlights</attribute></item>
<item><attribute name="action">app.filters-brightness-contrast</attribute></item>
<item><attribute name="action">app.filters-levels</attribute></item>
<item><attribute name="action">app.filters-curves</attribute></item>
</section>
<section>
<item><attribute name="action">app.filters-invert-perceptual</attribute></item>
<item><attribute name="action">app.filters-invert-linear</attribute></item>
<item><attribute name="action">app.filters-invert-value</attribute></item>
</section>
<section>
<submenu>
<attribute name="label" translatable="yes" context="image-action">_Auto</attribute>
<item><attribute name="action">app.drawable-equalize</attribute></item>
<item><attribute name="action">app.drawable-levels-stretch</attribute></item>
<item><attribute name="action">app.filters-stretch-contrast</attribute></item>
<item><attribute name="action">app.filters-stretch-contrast-hsv</attribute></item>
<item><attribute name="action">app.filters-color-enhance</attribute></item>
</submenu>
<submenu>
<attribute name="label" translatable="yes" context="image-action">C_omponents</attribute>
<item><attribute name="action">app.filters-channel-mixer</attribute></item>
<item><attribute name="action">app.filters-component-extract</attribute></item>
<item><attribute name="action">app.filters-mono-mixer</attribute></item>
<!-- TODO Missing: compose, decompose, recompose -->
</submenu>
<submenu>
<attribute name="label" translatable="yes" context="image-action">D_esaturate</attribute>
<item><attribute name="action">app.filters-c2g</attribute></item>
<item><attribute name="action">app.filters-desaturate</attribute></item>
<item><attribute name="action">app.filters-mono-mixer</attribute></item>
<item><attribute name="action">app.filters-sepia</attribute></item>
</submenu>
<submenu>
<attribute name="label" translatable="yes" context="image-action">_Map</attribute>
<!-- TODO: missing 2 plug-ins -->
<section>
<item><attribute name="action">app.filters-alien-map</attribute></item>
<item><attribute name="action">app.filters-color-exchange</attribute></item>
<item><attribute name="action">app.filters-color-rotate</attribute></item>
</section>
<!-- TODO: missing 3 plug-ins -->
</submenu>
<submenu>
<attribute name="label" translatable="yes" context="image-action">_Tone Mapping</attribute>
<item><attribute name="action">app.filters-fattal-2002</attribute></item>
<item><attribute name="action">app.filters-mantiuk-2006</attribute></item>
<item><attribute name="action">app.filters-reinhard-2005</attribute></item>
<item><attribute name="action">app.filters-stress</attribute></item>
<!-- TODO: missing 2 plug-ins -->
</submenu>
<submenu>
<attribute name="label" translatable="yes" context="image-action">I_nfo</attribute>
<item><attribute name="action">app.dialogs-histogram</attribute></item>
<!-- TODO: missing 3 plug-ins -->
</submenu>
</section>
<section>
<item><attribute name="action">app.filters-threshold</attribute></item>
<item><attribute name="action">app.filters-colorize</attribute></item>
<item><attribute name="action">app.filters-posterize</attribute></item>
<item><attribute name="action">app.filters-color-to-alpha</attribute></item>
<item><attribute name="action">app.filters-dither</attribute></item>
<item><attribute name="action">app.filters-rgb-clip</attribute></item>
<!-- TODO: missing 1 plug-in -->
</section>
</submenu>
<!-- Tools Menu -->
<submenu>
<attribute name="label" translatable="yes" context="tools-action">_Tools</attribute>
<section>
<submenu>
<attribute name="label" translatable="yes" context="tools-action">_Selection Tools</attribute>
<item><attribute name="action">app.tools-rect-select</attribute></item>
<item><attribute name="action">app.tools-ellipse-select</attribute></item>
<item><attribute name="action">app.tools-free-select</attribute></item>
<item><attribute name="action">app.tools-foreground-select</attribute></item>
<item><attribute name="action">app.tools-fuzzy-select</attribute></item>
<item><attribute name="action">app.tools-by-color-select</attribute></item>
<item><attribute name="action">app.tools-iscissors</attribute></item>
<!-- Enable when the tool leaves the playground
<item><attribute name="action">app.tools-paint-select</attribute></item>
-->
</submenu>
<submenu>
<attribute name="label" translatable="yes" context="tools-action">_Paint Tools</attribute>
<item><attribute name="action">app.tools-bucket-fill</attribute></item>
<item><attribute name="action">app.tools-gradient</attribute></item>
<item><attribute name="action">app.tools-pencil</attribute></item>
<item><attribute name="action">app.tools-paintbrush</attribute></item>
<item><attribute name="action">app.tools-eraser</attribute></item>
<item><attribute name="action">app.tools-airbrush</attribute></item>
<item><attribute name="action">app.tools-ink</attribute></item>
<item><attribute name="action">app.tools-mypaint-brush</attribute></item>
<item><attribute name="action">app.tools-clone</attribute></item>
<item><attribute name="action">app.tools-heal</attribute></item>
<item><attribute name="action">app.tools-perspective-clone</attribute></item>
<item><attribute name="action">app.tools-convolve</attribute></item>
<item><attribute name="action">app.tools-smudge</attribute></item>
<item><attribute name="action">app.tools-dodge-burn</attribute></item>
</submenu>
<submenu>
<attribute name="label" translatable="yes" context="tools-action">_Transform Tools</attribute>
<item><attribute name="action">app.tools-align</attribute></item>
<item><attribute name="action">app.tools-move</attribute></item>
<item><attribute name="action">app.tools-crop</attribute></item>
<item><attribute name="action">app.tools-rotate</attribute></item>
<item><attribute name="action">app.tools-scale</attribute></item>
<item><attribute name="action">app.tools-shear</attribute></item>
<item><attribute name="action">app.tools-perspective</attribute></item>
<item><attribute name="action">app.tools-transform-3d</attribute></item>
<item><attribute name="action">app.tools-unified-transform</attribute></item>
<item><attribute name="action">app.tools-handle-transform</attribute></item>
<item><attribute name="action">app.tools-flip</attribute></item>
<item><attribute name="action">app.tools-cage</attribute></item>
<item><attribute name="action">app.tools-warp</attribute></item>
<!-- Enable when the tool leaves the playground
<item><attribute name="action">app.tools-n-point-deformation</attribute></item>
-->
</submenu>
<item><attribute name="action">app.tools-vector</attribute></item>
<item><attribute name="action">app.tools-text</attribute></item>
</section>
<section>
<item><attribute name="action">app.tools-color-picker</attribute></item>
<item><attribute name="action">app.tools-measure</attribute></item>
<item><attribute name="action">app.tools-zoom</attribute></item>
</section>
<section>
<item><attribute name="action">app.dialogs-toolbox</attribute></item>
<item><attribute name="action">app.context-colors-default</attribute></item>
<item><attribute name="action">app.context-colors-swap</attribute></item>
</section>
</submenu>
<!-- Filters menu -->
<submenu>
<attribute name="label" translatable="yes" context="filters-action">Filte_rs</attribute>
<section>
<item><attribute name="action">app.filters-repeat</attribute></item>
<item><attribute name="action">app.filters-reshow</attribute></item>
<submenu>
<attribute name="label" translatable="yes" context="filters-action">Recently Used</attribute>
<!-- TODO: fill -->
</submenu>
<item><attribute name="action">app.plug-in-reset-all</attribute></item>
</section>
<section>
<submenu>
<attribute name="label" translatable="yes" context="filters-action">_Blur</attribute>
<item><attribute name="action">app.filters-focus-blur</attribute></item>
<item><attribute name="action">app.filters-gaussian-blur</attribute></item>
<item><attribute name="action">app.filters-lens-blur</attribute></item>
<item><attribute name="action">app.filters-mean-curvature-blur</attribute></item>
<item><attribute name="action">app.filters-median-blur</attribute></item>
<item><attribute name="action">app.filters-pixelize</attribute></item>
<item><attribute name="action">app.filters-gaussian-blur-selective</attribute></item>
<item><attribute name="action">app.filters-variable-blur</attribute></item>
<item><attribute name="action">app.filters-motion-blur-circular</attribute></item>
<item><attribute name="action">app.filters-motion-blur-linear</attribute></item>
<item><attribute name="action">app.filters-motion-blur-zoom</attribute></item>
<!-- TODO Miss: tileable blur -->
</submenu>
<submenu>
<attribute name="label" translatable="yes" context="filters-action">En_hance</attribute>
<item><attribute name="action">app.filters-antialias</attribute></item>
<item><attribute name="action">app.filters-deinterlace</attribute></item>
<item><attribute name="action">app.filters-high-pass</attribute></item>
<item><attribute name="action">app.filters-noise-reduction</attribute></item>
<item><attribute name="action">app.filters-red-eye-removal</attribute></item>
<item><attribute name="action">app.filters-snn-mean</attribute></item>
<item><attribute name="action">app.filters-unsharp-mask</attribute></item>
<!-- TODO Miss: several plug-ins -->
</submenu>
<submenu>
<attribute name="label" translatable="yes" context="filters-action">_Distorts</attribute>
<item><attribute name="action">app.filters-apply-lens</attribute></item>
<item><attribute name="action">app.filters-emboss</attribute></item>
<item><attribute name="action">app.filters-engrave</attribute></item>
<item><attribute name="action">app.filters-lens-distortion</attribute></item>
<item><attribute name="action">app.filters-kaleidoscope</attribute></item>
<item><attribute name="action">app.filters-mosaic</attribute></item>
<item><attribute name="action">app.filters-newsprint</attribute></item>
<item><attribute name="action">app.filters-polar-coordinates</attribute></item>
<item><attribute name="action">app.filters-ripple</attribute></item>
<item><attribute name="action">app.filters-shift</attribute></item>
<item><attribute name="action">app.filters-spherize</attribute></item>
<item><attribute name="action">app.filters-value-propagate</attribute></item>
<item><attribute name="action">app.filters-video-degradation</attribute></item>
<item><attribute name="action">app.filters-waves</attribute></item>
<item><attribute name="action">app.filters-whirl-pinch</attribute></item>
<item><attribute name="action">app.filters-wind</attribute></item>
<!-- TODO Miss: 3 plug-ins -->
</submenu>
<submenu>
<attribute name="label" translatable="yes" context="filters-action">_Light and Shadow</attribute>
<section>
<item><attribute name="action">app.filters-bloom</attribute></item>
<item><attribute name="action">app.filters-supernova</attribute></item>
<item><attribute name="action">app.filters-lens-flare</attribute></item>
<!-- TODO Miss: 3 plug-ins -->
</section>
<section>
<item><attribute name="action">app.filters-dropshadow</attribute></item>
<item><attribute name="action">app.filters-long-shadow</attribute></item>
<item><attribute name="action">app.filters-vignette</attribute></item>
<!-- TODO Miss: 3 plug-ins -->
</section>
</submenu>
<submenu>
<attribute name="label" translatable="yes" context="filters-action">_Noise</attribute>
<item><attribute name="action">app.filters-noise-cie-lch</attribute></item>
<item><attribute name="action">app.filters-noise-hsv</attribute></item>
<item><attribute name="action">app.filters-noise-hurl</attribute></item>
<item><attribute name="action">app.filters-noise-pick</attribute></item>
<item><attribute name="action">app.filters-noise-rgb</attribute></item>
<item><attribute name="action">app.filters-noise-slur</attribute></item>
<item><attribute name="action">app.filters-noise-spread</attribute></item>
</submenu>
<submenu>
<attribute name="label" translatable="yes" context="filters-action">Edge-De_tect</attribute>
<item><attribute name="action">app.filters-difference-of-gaussians</attribute></item>
<item><attribute name="action">app.filters-edge</attribute></item>
<item><attribute name="action">app.filters-edge-laplace</attribute></item>
<item><attribute name="action">app.filters-edge-neon</attribute></item>
<item><attribute name="action">app.filters-edge-sobel</attribute></item>
<item><attribute name="action">app.filters-image-gradient</attribute></item>
</submenu>
<submenu>
<attribute name="label" translatable="yes" context="filters-action">_Generic</attribute>
<item><attribute name="action">app.filters-convolution-matrix</attribute></item>
<item><attribute name="action">app.filters-distance-map</attribute></item>
<item><attribute name="action">app.tools-gegl</attribute></item>
<item><attribute name="action">app.filters-gegl-graph</attribute></item>
<item><attribute name="action">app.filters-normal-map</attribute></item>
<item><attribute name="action">app.filters-dilate</attribute></item>
<item><attribute name="action">app.filters-erode</attribute></item>
</submenu>
<submenu>
<attribute name="label" translatable="yes" context="filters-action">C_ombine</attribute>
<!-- TODO Miss: all plug-ins -->
</submenu>
<submenu>
<attribute name="label" translatable="yes" context="filters-action">_Artistic</attribute>
<item><attribute name="action">app.filters-apply-canvas</attribute></item>
<item><attribute name="action">app.filters-cartoon</attribute></item>
<item><attribute name="action">app.filters-cubism</attribute></item>
<item><attribute name="action">app.filters-tile-glass</attribute></item>
<item><attribute name="action">app.filters-oilify</attribute></item>
<item><attribute name="action">app.filters-photocopy</attribute></item>
<item><attribute name="action">app.filters-slic</attribute></item>
<item><attribute name="action">app.filters-softglow</attribute></item>
<item><attribute name="action">app.filters-waterpixels</attribute></item>
<!-- TODO Miss: various plug-ins -->
</submenu>
<submenu>
<attribute name="label" translatable="yes" context="filters-action">_Decor</attribute>
<!-- TODO Miss: all plug-ins -->
</submenu>
<submenu>
<attribute name="label" translatable="yes" context="filters-action">_Map</attribute>
<item><attribute name="action">app.filters-bump-map</attribute></item>
<item><attribute name="action">app.filters-displace</attribute></item>
<item><attribute name="action">app.filters-fractal-trace</attribute></item>
<item><attribute name="action">app.filters-illusion</attribute></item>
<item><attribute name="action">app.filters-little-planet</attribute></item>
<item><attribute name="action">app.filters-panorama-projection</attribute></item>
<item><attribute name="action">app.filters-recursive-transform</attribute></item>
<item><attribute name="action">app.filters-tile-paper</attribute></item>
<item><attribute name="action">app.filters-tile-seamless</attribute></item>
<!-- TODO Miss: various plug-ins -->
</submenu>
<submenu>
<attribute name="label" translatable="yes" context="filters-action">_Render</attribute>
<!-- TODO Miss: Fractals/ dir -->
<submenu>
<attribute name="label" translatable="yes" context="filters-action">N_oise</attribute>
<item><attribute name="action">app.filters-noise-cell</attribute></item>
<item><attribute name="action">app.filters-noise-perlin</attribute></item>
<item><attribute name="action">app.filters-plasma</attribute></item>
<item><attribute name="action">app.filters-noise-simplex</attribute></item>
<item><attribute name="action">app.filters-noise-solid</attribute></item>
<!-- TODO Miss: diff clouds plug-in -->
</submenu>
<submenu>
<attribute name="label" translatable="yes" context="filters-action">_Pattern</attribute>
<item><attribute name="action">app.filters-bayer-matrix</attribute></item>
<item><attribute name="action">app.filters-checkerboard</attribute></item>
<item><attribute name="action">app.filters-diffraction-patterns</attribute></item>
<item><attribute name="action">app.filters-grid</attribute></item>
<item><attribute name="action">app.filters-linear-sinusoid</attribute></item>
<item><attribute name="action">app.filters-maze</attribute></item>
<item><attribute name="action">app.filters-sinus</attribute></item>
<item><attribute name="action">app.filters-spiral</attribute></item>
<!-- TODO Miss: various plug-ins -->
</submenu>
<!-- TODO Miss: various plug-ins -->
</submenu>
<submenu>
<attribute name="label" translatable="yes" context="filters-action">_Web</attribute>
<item><attribute name="action">app.filters-semi-flatten</attribute></item>
<!-- TODO Miss: 1 plug-in -->
</submenu>
<!-- TODO Miss: Animation and Development folders. -->
</section>
</submenu>
<submenu>
<attribute name="label" translatable="yes" context="windows-action">_Windows</attribute>
<section>
<submenu>
<attribute name="label" translatable="yes" context="windows-action">_Recently Closed Dialogs</attribute>
<!-- TODO: fill recently closed dialogs -->
</submenu>
<submenu>
<attribute name="label" translatable="yes" context="windows-action">_Dockable Dialogs</attribute>
<section>
<item><attribute name="action">app.dialogs-tool-options</attribute></item>
<item><attribute name="action">app.dialogs-device-status</attribute></item>
</section>
<section>
<item><attribute name="action">app.dialogs-layers</attribute></item>
<item><attribute name="action">app.dialogs-channels</attribute></item>
<item><attribute name="action">app.dialogs-vectors</attribute></item>
<item><attribute name="action">app.dialogs-indexed-palette</attribute></item>
<item><attribute name="action">app.dialogs-histogram</attribute></item>
<item><attribute name="action">app.dialogs-selection-editor</attribute></item>
<item><attribute name="action">app.dialogs-navigation</attribute></item>
<item><attribute name="action">app.dialogs-undo-history</attribute></item>
<item><attribute name="action">app.dialogs-cursor</attribute></item>
<item><attribute name="action">app.dialogs-sample-points</attribute></item>
<item><attribute name="action">app.dialogs-symmetry</attribute></item>
</section>
<section>
<item><attribute name="action">app.dialogs-colors</attribute></item>
<item><attribute name="action">app.dialogs-brushes</attribute></item>
<item><attribute name="action">app.dialogs-dynamics</attribute></item>
<item><attribute name="action">app.dialogs-mypaint-brushes</attribute></item>
<item><attribute name="action">app.dialogs-patterns</attribute></item>
<item><attribute name="action">app.dialogs-gradients</attribute></item>
<item><attribute name="action">app.dialogs-palettes</attribute></item>
<item><attribute name="action">app.dialogs-fonts</attribute></item>
<item><attribute name="action">app.dialogs-tool-presets</attribute></item>
<item><attribute name="action">app.dialogs-buffers</attribute></item>
</section>
<section>
<item><attribute name="action">app.dialogs-images</attribute></item>
<item><attribute name="action">app.dialogs-document-history</attribute></item>
<item><attribute name="action">app.dialogs-templates</attribute></item>
<item><attribute name="action">app.dialogs-error-console</attribute></item>
<item><attribute name="action">app.dialogs-dashboard</attribute></item>
</section>
</submenu>
<item><attribute name="action">app.dialogs-toolbox</attribute></item>
</section>
<!-- TODO: list opened images. -->
<section>
<item><attribute name="action">app.windows-hide-docks</attribute></item>
<item><attribute name="action">app.windows-show-tabs</attribute></item>
<submenu>
<attribute name="label" translatable="yes" context="windows-action">_Tabs Position</attribute>
<item><attribute name="action">app.windows-tabs-position-top</attribute></item>
<item><attribute name="action">app.windows-tabs-position-bottom</attribute></item>
<item><attribute name="action">app.windows-tabs-position-left</attribute></item>
<item><attribute name="action">app.windows-tabs-position-right</attribute></item>
</submenu>
<item><attribute name="action">app.windows-use-single-window-mode</attribute></item>
</section>
</submenu>
<submenu>
<attribute name="label" translatable="yes" context="help-action">_Help</attribute>
<section>
<item><attribute name="action">app.help-help</attribute></item>
<item><attribute name="action">app.help-context-help</attribute></item>
<item><attribute name="action">app.dialogs-tips</attribute></item>
<item><attribute name="action">app.dialogs-about</attribute></item>
<item><attribute name="action">app.dialogs-welcome</attribute></item>
</section>
<section>
<item><attribute name="action">app.dialogs-action-search</attribute></item>
</section>
<!-- TODO: Missing various actions. -->
</submenu>
</menu>
</interface>

View file

@ -39,6 +39,14 @@ install_data(menus_files,
install_dir: menus_dir,
)
ui_menus_files = files(
'image-menu.ui',
)
install_data(ui_menus_files,
install_dir: menus_dir,
)
unstable_menus_args = stable ? [] : [ '--stringparam', 'unstable-menus', 'yes' ]
menus_built_files = []
@ -74,4 +82,20 @@ if xmllint.found()
build_by_default: true,
install: false
)
# XXX: no DTD validation as GtkBuilder UI format does not have a DTD (as far as
# we could find).
custom_target('validate_ui_menus',
command: [
xmllint,
'--output', '@OUTPUT@',
'--path', meson.current_source_dir(),
ui_menus_files, menus_built_files
],
# The output file is only useful as a flag file, so that the command
# knows if it has been run already.
output: [ 'validate_ui_menus-output.xml' ],
build_by_default: true,
install: false
)
endif