mirror of
https://gitlab.gnome.org/GNOME/gimp.git
synced 2025-07-04 01:43:24 +00:00
app: better use GimpMenuShell and shift around some code as virtual…
… function implementations. I am also preparing to create a new class GimpToolbar which will make good use of the interface too.
This commit is contained in:
parent
8a2d895ae8
commit
f492871e88
6 changed files with 798 additions and 618 deletions
|
@ -25,10 +25,17 @@
|
|||
|
||||
#include "widgets-types.h"
|
||||
|
||||
#include "libgimpwidgets/gimpwidgets.h"
|
||||
|
||||
#include "core/gimp.h"
|
||||
|
||||
#include "gimpaction.h"
|
||||
#include "gimpenumaction.h"
|
||||
#include "gimphelp-ids.h"
|
||||
#include "gimpmenu.h"
|
||||
#include "gimpmenushell.h"
|
||||
#include "gimpprocedureaction.h"
|
||||
#include "gimpradioaction.h"
|
||||
#include "gimpuimanager.h"
|
||||
|
||||
|
||||
|
@ -48,19 +55,71 @@
|
|||
*/
|
||||
|
||||
|
||||
struct _GimpMenuPrivate
|
||||
{
|
||||
GTree *submenus;
|
||||
GTree *placeholders;
|
||||
};
|
||||
|
||||
|
||||
static void gimp_menu_iface_init (GimpMenuShellInterface *iface);
|
||||
|
||||
static void gimp_menu_finalize (GObject *object);
|
||||
|
||||
static void gimp_menu_append (GimpMenuShell *shell,
|
||||
GMenuModel *model);
|
||||
static void gimp_menu_add_ui (GimpMenuShell *shell,
|
||||
const gchar **paths,
|
||||
const gchar *action_name,
|
||||
const gchar *placeholder_key,
|
||||
gboolean top);
|
||||
|
||||
static void gimp_menu_add_placeholder (GimpMenu *menu,
|
||||
const gchar *label);
|
||||
static void gimp_menu_add_action (GimpMenu *menu,
|
||||
const gchar *action_name,
|
||||
const gchar *placeholder_key,
|
||||
gboolean top,
|
||||
GtkRadioMenuItem **group);
|
||||
|
||||
static void gimp_menu_toggle_item_toggled (GtkWidget *item,
|
||||
GAction *action);
|
||||
|
||||
static void gimp_menu_notify_group_label (GimpRadioAction *action,
|
||||
const GParamSpec *pspec,
|
||||
GtkMenuItem *item);
|
||||
static void gimp_menu_toggle_action_toggled (GimpAction *action,
|
||||
GtkCheckMenuItem *item);
|
||||
static void gimp_menu_action_notify_sensitive (GimpAction *action,
|
||||
const GParamSpec *pspec,
|
||||
GtkCheckMenuItem *item);
|
||||
static void gimp_menu_action_notify_visible (GimpAction *action,
|
||||
const GParamSpec *pspec,
|
||||
GtkWidget *item);
|
||||
|
||||
static gboolean gimp_menu_copy_placeholders (gpointer key,
|
||||
gpointer item,
|
||||
GTree *placeholders);
|
||||
|
||||
static void gimp_menu_help_fun (const gchar *bogus_help_id,
|
||||
gpointer help_data);
|
||||
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (GimpMenu, gimp_menu, GTK_TYPE_MENU,
|
||||
G_IMPLEMENT_INTERFACE (GIMP_TYPE_MENU_SHELL, NULL))
|
||||
G_ADD_PRIVATE (GimpMenu)
|
||||
G_IMPLEMENT_INTERFACE (GIMP_TYPE_MENU_SHELL, gimp_menu_iface_init))
|
||||
|
||||
#define parent_class gimp_menu_parent_class
|
||||
|
||||
|
||||
/* private functions */
|
||||
/* Class functions */
|
||||
|
||||
static void
|
||||
gimp_menu_class_init (GimpMenuClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->finalize = gimp_menu_finalize;
|
||||
object_class->get_property = gimp_menu_shell_get_property;
|
||||
object_class->set_property = gimp_menu_shell_set_property;
|
||||
|
||||
|
@ -72,9 +131,203 @@ gimp_menu_init (GimpMenu *menu)
|
|||
{
|
||||
menu->priv = gimp_menu_get_instance_private (menu);
|
||||
|
||||
menu->priv->submenus = g_tree_new_full ((GCompareDataFunc) g_strcmp0, NULL,
|
||||
g_free, NULL);
|
||||
menu->priv->placeholders = g_tree_new_full ((GCompareDataFunc) g_strcmp0, NULL,
|
||||
g_free, NULL);
|
||||
|
||||
gimp_menu_shell_init (GIMP_MENU_SHELL (menu));
|
||||
|
||||
gimp_help_connect (GTK_WIDGET (menu), gimp_menu_help_fun,
|
||||
GIMP_HELP_MAIN, menu, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_menu_iface_init (GimpMenuShellInterface *iface)
|
||||
{
|
||||
iface->append = gimp_menu_append;
|
||||
iface->add_ui = gimp_menu_add_ui;
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_menu_finalize (GObject *object)
|
||||
{
|
||||
GimpMenu *menu = GIMP_MENU (object);
|
||||
|
||||
g_clear_pointer (&menu->priv->submenus, g_tree_unref);
|
||||
g_clear_pointer (&menu->priv->placeholders, g_tree_unref);
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_menu_append (GimpMenuShell *shell,
|
||||
GMenuModel *model)
|
||||
{
|
||||
static GtkRadioMenuItem *group = NULL;
|
||||
GimpMenu *menu = GIMP_MENU (shell);
|
||||
GimpUIManager *manager = gimp_menu_shell_get_manager (GIMP_MENU_SHELL (shell));
|
||||
gint n_items;
|
||||
|
||||
g_return_if_fail (GTK_IS_CONTAINER (shell));
|
||||
|
||||
n_items = g_menu_model_get_n_items (model);
|
||||
for (gint i = 0; i < n_items; i++)
|
||||
{
|
||||
GMenuModel *subsection;
|
||||
GMenuModel *submenu;
|
||||
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)
|
||||
{
|
||||
GtkWidget *item;
|
||||
|
||||
group = NULL;
|
||||
|
||||
item = gtk_separator_menu_item_new ();
|
||||
gtk_container_add (GTK_CONTAINER (shell), item);
|
||||
gtk_widget_show (item);
|
||||
|
||||
gimp_menu_append (shell, subsection);
|
||||
|
||||
item = gtk_separator_menu_item_new ();
|
||||
gtk_container_add (GTK_CONTAINER (shell), item);
|
||||
gtk_widget_show (item);
|
||||
}
|
||||
else if (submenu != NULL && label == NULL)
|
||||
{
|
||||
GApplication *app;
|
||||
GAction *action;
|
||||
const gchar *group_label;
|
||||
GtkWidget *subcontainer;
|
||||
GtkWidget *item;
|
||||
|
||||
group = NULL;
|
||||
|
||||
g_return_if_fail (action_name != NULL);
|
||||
|
||||
app = manager->gimp->app;
|
||||
action = g_action_map_lookup_action (G_ACTION_MAP (app), action_name + 4);
|
||||
|
||||
/* As a special case, when a submenu has no label, we expect it to
|
||||
* have an action attribute, which must be for a radio action. In such
|
||||
* a case, we'll use the radio actions' group label as submenu title.
|
||||
* See e.g.: menus/gradient-editor-menu.ui
|
||||
*/
|
||||
g_return_if_fail (GIMP_IS_RADIO_ACTION (action));
|
||||
|
||||
group_label = gimp_radio_action_get_group_label (GIMP_RADIO_ACTION (action));
|
||||
|
||||
item = gtk_menu_item_new_with_mnemonic (group_label);
|
||||
g_signal_connect_object (action, "notify::group-label",
|
||||
G_CALLBACK (gimp_menu_notify_group_label),
|
||||
item, 0);
|
||||
gtk_container_add (GTK_CONTAINER (shell), item);
|
||||
|
||||
subcontainer = gimp_menu_new (manager);
|
||||
gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), subcontainer);
|
||||
gimp_menu_append (GIMP_MENU_SHELL (subcontainer), submenu);
|
||||
gtk_widget_show (subcontainer);
|
||||
|
||||
g_tree_insert (menu->priv->submenus,
|
||||
gimp_menu_shell_make_canonical_path (group_label),
|
||||
subcontainer);
|
||||
}
|
||||
else if (submenu != NULL)
|
||||
{
|
||||
GtkWidget *subcontainer;
|
||||
GtkWidget *item;
|
||||
|
||||
group = NULL;
|
||||
|
||||
/* I don't show the item on purpose because
|
||||
* gimp_menu_append() will show the parent item if any of
|
||||
* the added actions are visible.
|
||||
*/
|
||||
item = gtk_menu_item_new_with_mnemonic (label);
|
||||
gtk_container_add (GTK_CONTAINER (shell), item);
|
||||
|
||||
subcontainer = gimp_menu_new (manager);
|
||||
gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), subcontainer);
|
||||
gimp_menu_append (GIMP_MENU_SHELL (subcontainer), submenu);
|
||||
gtk_widget_show (subcontainer);
|
||||
|
||||
g_tree_insert (menu->priv->submenus,
|
||||
gimp_menu_shell_make_canonical_path (label),
|
||||
subcontainer);
|
||||
}
|
||||
else if (action_name == NULL)
|
||||
{
|
||||
/* Special case: we use items with no action and a label as
|
||||
* placeholder which allows us to specify a placement in menus, which
|
||||
* might not be only top or bottom.
|
||||
*/
|
||||
g_return_if_fail (label != NULL);
|
||||
|
||||
group = NULL;
|
||||
|
||||
gimp_menu_add_placeholder (menu, label);
|
||||
}
|
||||
else
|
||||
{
|
||||
gimp_menu_add_action (menu, action_name, NULL, FALSE, &group);
|
||||
}
|
||||
|
||||
g_free (label);
|
||||
g_free (action_name);
|
||||
g_clear_object (&submenu);
|
||||
g_clear_object (&subsection);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_menu_add_ui (GimpMenuShell *shell,
|
||||
const gchar **paths,
|
||||
const gchar *action_name,
|
||||
const gchar *placeholder_key,
|
||||
gboolean top)
|
||||
{
|
||||
GimpMenu *menu = GIMP_MENU (shell);
|
||||
GimpUIManager *manager = gimp_menu_shell_get_manager (GIMP_MENU_SHELL (shell));
|
||||
|
||||
g_return_if_fail (paths != NULL);
|
||||
|
||||
if (paths[0] == NULL)
|
||||
{
|
||||
gimp_menu_add_action (menu, action_name, placeholder_key, top, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
GtkWidget *submenu = NULL;
|
||||
|
||||
submenu = g_tree_lookup (menu->priv->submenus, paths[0]);
|
||||
|
||||
if (submenu == NULL)
|
||||
{
|
||||
GtkWidget *item;
|
||||
|
||||
item = gtk_menu_item_new_with_mnemonic (paths[0]);
|
||||
gtk_container_add (GTK_CONTAINER (shell), item);
|
||||
|
||||
submenu = gimp_menu_new (manager);
|
||||
gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu);
|
||||
gtk_widget_show (submenu);
|
||||
|
||||
g_tree_insert (menu->priv->submenus, g_strdup (paths[0]), submenu);
|
||||
}
|
||||
|
||||
gimp_menu_add_ui (GIMP_MENU_SHELL (submenu), paths + 1,
|
||||
action_name, placeholder_key, top);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Public functions */
|
||||
|
||||
GtkWidget *
|
||||
|
@ -86,3 +339,349 @@ gimp_menu_new (GimpUIManager *manager)
|
|||
"manager", manager,
|
||||
NULL);
|
||||
}
|
||||
|
||||
void
|
||||
gimp_menu_merge (GimpMenu *menu,
|
||||
GimpMenu *menu2,
|
||||
gboolean top)
|
||||
{
|
||||
GList *children;
|
||||
GList *iter;
|
||||
|
||||
children = gtk_container_get_children (GTK_CONTAINER (menu2));
|
||||
iter = top ? g_list_last (children) : children;
|
||||
for (; iter; iter = top ? iter->prev : iter->next)
|
||||
{
|
||||
GtkWidget *item = iter->data;
|
||||
|
||||
g_object_ref (item);
|
||||
gtk_container_remove (GTK_CONTAINER (menu2), item);
|
||||
if (top)
|
||||
gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
|
||||
else
|
||||
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
|
||||
g_object_unref (item);
|
||||
}
|
||||
|
||||
g_tree_foreach (menu2->priv->placeholders,
|
||||
(GTraverseFunc) gimp_menu_copy_placeholders,
|
||||
menu->priv->placeholders);
|
||||
|
||||
gtk_widget_destroy (GTK_WIDGET (menu2));
|
||||
g_list_free (children);
|
||||
}
|
||||
|
||||
|
||||
/* Private functions */
|
||||
|
||||
static void
|
||||
gimp_menu_add_placeholder (GimpMenu *menu,
|
||||
const gchar *label)
|
||||
{
|
||||
GtkWidget *item;
|
||||
|
||||
/* Placeholders are inserted yet never shown, on purpose. */
|
||||
item = gtk_menu_item_new_with_mnemonic (label);
|
||||
gtk_container_add (GTK_CONTAINER (menu), item);
|
||||
|
||||
g_tree_insert (menu->priv->placeholders, g_strdup (label), item);
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_menu_add_action (GimpMenu *menu,
|
||||
const gchar *action_name,
|
||||
const gchar *placeholder_key,
|
||||
gboolean top,
|
||||
GtkRadioMenuItem **group)
|
||||
{
|
||||
GimpUIManager *manager;
|
||||
GApplication *app;
|
||||
GAction *action;
|
||||
const gchar *action_label;
|
||||
GtkWidget *item;
|
||||
GtkWidget *sibling = NULL;
|
||||
gboolean visible;
|
||||
|
||||
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));
|
||||
|
||||
action_label = gimp_action_get_label (GIMP_ACTION (action));
|
||||
g_return_if_fail (action_label != NULL);
|
||||
|
||||
if (GIMP_IS_TOGGLE_ACTION (action))
|
||||
{
|
||||
if (GIMP_IS_RADIO_ACTION (action))
|
||||
item = gtk_radio_menu_item_new_with_mnemonic_from_widget (group ? *group : NULL,
|
||||
action_label);
|
||||
else
|
||||
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)));
|
||||
|
||||
if (group)
|
||||
{
|
||||
if (GIMP_IS_RADIO_ACTION (action))
|
||||
*group = GTK_RADIO_MENU_ITEM (item);
|
||||
else
|
||||
*group = NULL;
|
||||
}
|
||||
|
||||
g_signal_connect (item, "toggled",
|
||||
G_CALLBACK (gimp_menu_toggle_item_toggled),
|
||||
action);
|
||||
g_signal_connect_object (action, "toggled",
|
||||
G_CALLBACK (gimp_menu_toggle_action_toggled),
|
||||
item, 0);
|
||||
}
|
||||
else if (GIMP_IS_PROCEDURE_ACTION (action) ||
|
||||
GIMP_IS_ENUM_ACTION (action))
|
||||
{
|
||||
item = gtk_menu_item_new_with_mnemonic (action_label);
|
||||
|
||||
if (group)
|
||||
*group = NULL;
|
||||
|
||||
g_signal_connect_swapped (item, "activate",
|
||||
G_CALLBACK (gimp_action_activate),
|
||||
action);
|
||||
}
|
||||
else
|
||||
{
|
||||
item = gtk_menu_item_new_with_mnemonic (action_label);
|
||||
|
||||
if (group)
|
||||
*group = NULL;
|
||||
|
||||
gtk_actionable_set_action_name (GTK_ACTIONABLE (item), action_name);
|
||||
}
|
||||
|
||||
gimp_action_set_proxy (GIMP_ACTION (action), item);
|
||||
|
||||
gtk_widget_set_sensitive (GTK_WIDGET (item),
|
||||
gimp_action_is_sensitive (GIMP_ACTION (action), NULL));
|
||||
g_signal_connect_object (action, "notify::sensitive",
|
||||
G_CALLBACK (gimp_menu_action_notify_sensitive),
|
||||
item, 0);
|
||||
|
||||
if (placeholder_key)
|
||||
{
|
||||
sibling = g_tree_lookup (menu->priv->placeholders, placeholder_key);
|
||||
|
||||
if (! sibling)
|
||||
g_warning ("%s: no placeholder item '%s'.", G_STRFUNC, placeholder_key);
|
||||
}
|
||||
|
||||
if (sibling)
|
||||
{
|
||||
GList *children;
|
||||
gint position = 0;
|
||||
|
||||
/* I am assuming that the order of the children list reflects the
|
||||
* position, though it is not clearly specified in the function docs. Yet
|
||||
* I could find no other function giving me the position of some child in
|
||||
* a container.
|
||||
*/
|
||||
children = gtk_container_get_children (GTK_CONTAINER (menu));
|
||||
|
||||
for (GList *iter = children; iter; iter = iter->next)
|
||||
{
|
||||
if (iter->data == sibling)
|
||||
break;
|
||||
position++;
|
||||
}
|
||||
if (! top)
|
||||
position++;
|
||||
|
||||
gtk_menu_shell_insert (GTK_MENU_SHELL (menu), item, position);
|
||||
|
||||
g_list_free (children);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (top)
|
||||
gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
|
||||
else
|
||||
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
|
||||
}
|
||||
|
||||
visible = gimp_action_is_visible (GIMP_ACTION (action));
|
||||
gtk_widget_set_visible (item, visible);
|
||||
if (visible && GTK_IS_MENU (menu))
|
||||
{
|
||||
GtkWidget *parent = gtk_menu_get_attach_widget (GTK_MENU (menu));
|
||||
|
||||
/* Note that this is not the container we must show, but the menu item
|
||||
* attached to the parent, in order not to leave empty submenus.
|
||||
*/
|
||||
if (parent != NULL &&
|
||||
G_TYPE_FROM_INSTANCE (parent) == GTK_TYPE_MENU_ITEM)
|
||||
gtk_widget_show (parent);
|
||||
}
|
||||
g_signal_connect_object (action, "notify::visible",
|
||||
G_CALLBACK (gimp_menu_action_notify_visible),
|
||||
item, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_menu_toggle_item_toggled (GtkWidget *item,
|
||||
GAction *action)
|
||||
{
|
||||
gboolean active = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (item));
|
||||
|
||||
g_signal_handlers_block_by_func (action,
|
||||
G_CALLBACK (gimp_menu_toggle_action_toggled),
|
||||
item);
|
||||
gimp_toggle_action_set_active (GIMP_TOGGLE_ACTION (action), active);
|
||||
g_signal_handlers_unblock_by_func (action,
|
||||
G_CALLBACK (gimp_menu_toggle_action_toggled),
|
||||
item);
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_menu_notify_group_label (GimpRadioAction *action,
|
||||
const GParamSpec *pspec,
|
||||
GtkMenuItem *item)
|
||||
{
|
||||
gtk_menu_item_set_use_underline (item, TRUE);
|
||||
gtk_menu_item_set_label (item, gimp_radio_action_get_group_label (action));
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_menu_toggle_action_toggled (GimpAction *action,
|
||||
GtkCheckMenuItem *item)
|
||||
{
|
||||
gboolean active = gimp_toggle_action_get_active (GIMP_TOGGLE_ACTION (action));
|
||||
|
||||
g_signal_handlers_block_by_func (item,
|
||||
G_CALLBACK (gimp_menu_toggle_item_toggled),
|
||||
action);
|
||||
gtk_check_menu_item_set_active (item, active);
|
||||
g_signal_handlers_unblock_by_func (item,
|
||||
G_CALLBACK (gimp_menu_toggle_item_toggled),
|
||||
action);
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_menu_action_notify_sensitive (GimpAction *action,
|
||||
const GParamSpec *pspec,
|
||||
GtkCheckMenuItem *item)
|
||||
{
|
||||
gtk_widget_set_sensitive (GTK_WIDGET (item),
|
||||
gimp_action_is_sensitive (action, NULL));
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_menu_action_notify_visible (GimpAction *action,
|
||||
const GParamSpec *pspec,
|
||||
GtkWidget *item)
|
||||
{
|
||||
GtkWidget *container;
|
||||
|
||||
gtk_widget_set_visible (item, gimp_action_is_visible (action));
|
||||
|
||||
container = gtk_widget_get_parent (item);
|
||||
if (gimp_action_is_visible (GIMP_ACTION (action)))
|
||||
{
|
||||
GtkWidget *widget = gtk_menu_get_attach_widget (GTK_MENU (container));
|
||||
|
||||
/* We must show the GtkMenuItem associated as submenu to the parent
|
||||
* container.
|
||||
*/
|
||||
if (G_TYPE_FROM_INSTANCE (widget) == GTK_TYPE_MENU_ITEM)
|
||||
gtk_widget_show (widget);
|
||||
}
|
||||
else
|
||||
{
|
||||
GList *children = gtk_container_get_children (GTK_CONTAINER (container));
|
||||
gboolean all_invisible = TRUE;
|
||||
|
||||
for (GList *iter = children; iter; iter = iter->next)
|
||||
{
|
||||
if (gtk_widget_get_visible (iter->data))
|
||||
{
|
||||
all_invisible = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
g_list_free (children);
|
||||
|
||||
if (all_invisible)
|
||||
{
|
||||
GtkWidget *widget;
|
||||
|
||||
/* No need to leave empty submenus. */
|
||||
widget = gtk_menu_get_attach_widget (GTK_MENU (container));
|
||||
if (G_TYPE_FROM_INSTANCE (widget) == GTK_TYPE_MENU_ITEM)
|
||||
gtk_widget_hide (widget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gimp_menu_copy_placeholders (gpointer key,
|
||||
gpointer item,
|
||||
GTree *placeholders)
|
||||
{
|
||||
g_tree_insert (placeholders, g_strdup ((gchar *) key), item);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_menu_help_fun (const gchar *bogus_help_id,
|
||||
gpointer help_data)
|
||||
{
|
||||
gchar *help_id = NULL;
|
||||
GimpMenu *menu;
|
||||
Gimp *gimp;
|
||||
GtkWidget *item;
|
||||
gchar *help_domain = NULL;
|
||||
gchar *help_string = NULL;
|
||||
gchar *domain_separator;
|
||||
|
||||
g_return_if_fail (GIMP_IS_MENU (help_data));
|
||||
|
||||
menu = GIMP_MENU (help_data);
|
||||
gimp = gimp_menu_shell_get_manager (GIMP_MENU_SHELL (menu))->gimp;
|
||||
|
||||
g_return_if_fail (GIMP_IS_GIMP (gimp));
|
||||
|
||||
item = gtk_menu_shell_get_selected_item (GTK_MENU_SHELL (menu));
|
||||
|
||||
if (item)
|
||||
help_id = g_object_get_qdata (G_OBJECT (item), GIMP_HELP_ID);
|
||||
|
||||
if (help_id == NULL || strlen (help_id) == 0)
|
||||
help_id = (gchar *) bogus_help_id;
|
||||
|
||||
help_id = g_strdup (help_id);
|
||||
|
||||
domain_separator = strchr (help_id, '?');
|
||||
|
||||
if (domain_separator)
|
||||
{
|
||||
*domain_separator = '\0';
|
||||
|
||||
help_domain = g_strdup (help_id);
|
||||
help_string = g_strdup (domain_separator + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
help_string = g_strdup (help_id);
|
||||
}
|
||||
|
||||
gimp_help (gimp, NULL, help_domain, help_string);
|
||||
|
||||
g_free (help_domain);
|
||||
g_free (help_string);
|
||||
g_free (help_id);
|
||||
}
|
||||
|
|
|
@ -50,5 +50,9 @@ GType gimp_menu_get_type (void) G_GNUC_CONST;
|
|||
|
||||
GtkWidget * gimp_menu_new (GimpUIManager *manager);
|
||||
|
||||
void gimp_menu_merge (GimpMenu *menu,
|
||||
GimpMenu *menu2,
|
||||
gboolean top);
|
||||
|
||||
|
||||
#endif /* __GIMP_MENU_H__ */
|
||||
|
|
|
@ -27,8 +27,13 @@
|
|||
|
||||
#include "widgets-types.h"
|
||||
|
||||
#include "core/gimp.h"
|
||||
|
||||
#include "gimpaction.h"
|
||||
#include "gimpmenu.h"
|
||||
#include "gimpmenubar.h"
|
||||
#include "gimpmenushell.h"
|
||||
#include "gimpradioaction.h"
|
||||
#include "gimpuimanager.h"
|
||||
|
||||
|
||||
|
@ -53,11 +58,15 @@ enum
|
|||
struct _GimpMenuBarPrivate
|
||||
{
|
||||
GMenuModel *model;
|
||||
|
||||
GTree *menus;
|
||||
};
|
||||
|
||||
|
||||
/* local function prototypes */
|
||||
|
||||
static void gimp_menu_bar_iface_init (GimpMenuShellInterface *iface);
|
||||
|
||||
static void gimp_menu_bar_constructed (GObject *object);
|
||||
static void gimp_menu_bar_dispose (GObject *object);
|
||||
static void gimp_menu_bar_set_property (GObject *object,
|
||||
|
@ -69,15 +78,23 @@ static void gimp_menu_bar_get_property (GObject *object
|
|||
GValue *value,
|
||||
GParamSpec *pspec);
|
||||
|
||||
static void gimp_menu_bar_append (GimpMenuShell *shell,
|
||||
GMenuModel *model);
|
||||
static void gimp_menu_bar_add_ui (GimpMenuShell *shell,
|
||||
const gchar **paths,
|
||||
const gchar *action_name,
|
||||
const gchar *placeholder_key,
|
||||
gboolean top);
|
||||
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (GimpMenuBar, gimp_menu_bar, GTK_TYPE_MENU_BAR,
|
||||
G_ADD_PRIVATE (GimpMenuBar)
|
||||
G_IMPLEMENT_INTERFACE (GIMP_TYPE_MENU_SHELL, NULL))
|
||||
G_IMPLEMENT_INTERFACE (GIMP_TYPE_MENU_SHELL, gimp_menu_bar_iface_init))
|
||||
|
||||
#define parent_class gimp_menu_bar_parent_class
|
||||
|
||||
|
||||
/* private functions */
|
||||
/* Class functions */
|
||||
|
||||
static void
|
||||
gimp_menu_bar_class_init (GimpMenuBarClass *klass)
|
||||
|
@ -99,11 +116,21 @@ gimp_menu_bar_class_init (GimpMenuBarClass *klass)
|
|||
G_PARAM_CONSTRUCT));
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_menu_bar_iface_init (GimpMenuShellInterface *iface)
|
||||
{
|
||||
iface->append = gimp_menu_bar_append;
|
||||
iface->add_ui = gimp_menu_bar_add_ui;
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_menu_bar_init (GimpMenuBar *bar)
|
||||
{
|
||||
bar->priv = gimp_menu_bar_get_instance_private (bar);
|
||||
|
||||
bar->priv->menus = g_tree_new_full ((GCompareDataFunc) g_strcmp0, NULL,
|
||||
g_free, NULL);
|
||||
|
||||
gimp_menu_shell_init (GIMP_MENU_SHELL (bar));
|
||||
}
|
||||
|
||||
|
@ -119,6 +146,7 @@ gimp_menu_bar_dispose (GObject *object)
|
|||
GimpMenuBar *bar = GIMP_MENU_BAR (object);
|
||||
|
||||
g_clear_object (&bar->priv->model);
|
||||
g_clear_pointer (&bar->priv->menus, g_tree_unref);
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->dispose (object);
|
||||
}
|
||||
|
@ -164,6 +192,115 @@ gimp_menu_bar_get_property (GObject *object,
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_menu_bar_append (GimpMenuShell *shell,
|
||||
GMenuModel *model)
|
||||
{
|
||||
GimpMenuBar *bar = GIMP_MENU_BAR (shell);
|
||||
GimpUIManager *manager = gimp_menu_shell_get_manager (GIMP_MENU_SHELL (shell));
|
||||
gint n_items;
|
||||
|
||||
g_return_if_fail (GTK_IS_CONTAINER (shell));
|
||||
|
||||
n_items = g_menu_model_get_n_items (model);
|
||||
for (gint i = 0; i < n_items; i++)
|
||||
{
|
||||
GMenuModel *subsection;
|
||||
GMenuModel *submenu;
|
||||
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 (submenu != NULL)
|
||||
{
|
||||
GtkWidget *subcontainer;
|
||||
GtkWidget *item;
|
||||
|
||||
g_return_if_fail (label != NULL);
|
||||
|
||||
/* I don't show the item on purpose because
|
||||
* gimp_menu_append() will show the parent item if any of
|
||||
* the added actions are visible.
|
||||
*/
|
||||
item = gtk_menu_item_new_with_mnemonic (label);
|
||||
gtk_container_add (GTK_CONTAINER (shell), item);
|
||||
|
||||
subcontainer = gimp_menu_new (manager);
|
||||
gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), subcontainer);
|
||||
GIMP_MENU_SHELL_GET_INTERFACE (subcontainer)->append (GIMP_MENU_SHELL (subcontainer), submenu);
|
||||
gtk_widget_show (subcontainer);
|
||||
|
||||
g_tree_insert (bar->priv->menus,
|
||||
gimp_menu_shell_make_canonical_path (label),
|
||||
subcontainer);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (subsection != NULL)
|
||||
g_warning ("%s: unexpected subsection. Only menus are allowed in menu bar's root.",
|
||||
G_STRFUNC);
|
||||
if (action_name == NULL)
|
||||
g_warning ("%s: unexpected placeholder '%s'. Only menus are allowed in menu bar's root.",
|
||||
G_STRFUNC, label);
|
||||
else
|
||||
g_warning ("%s: unexpected action '%s'. Only menus are allowed in menu bar's root.",
|
||||
G_STRFUNC, action_name);
|
||||
}
|
||||
g_free (label);
|
||||
g_free (action_name);
|
||||
g_clear_object (&submenu);
|
||||
g_clear_object (&subsection);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_menu_bar_add_ui (GimpMenuShell *shell,
|
||||
const gchar **paths,
|
||||
const gchar *action_name,
|
||||
const gchar *placeholder_key,
|
||||
gboolean top)
|
||||
{
|
||||
GimpMenuBar *bar = GIMP_MENU_BAR (shell);
|
||||
GimpUIManager *manager = gimp_menu_shell_get_manager (GIMP_MENU_SHELL (shell));
|
||||
|
||||
g_return_if_fail (paths != NULL);
|
||||
|
||||
if (paths[0] == NULL)
|
||||
{
|
||||
g_warning ("%s: unexpected action '%s'. Only menus are allowed in menu bar's root.",
|
||||
G_STRFUNC, action_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
GtkWidget *menu = NULL;
|
||||
|
||||
menu = g_tree_lookup (bar->priv->menus, paths[0]);
|
||||
|
||||
if (menu == NULL)
|
||||
{
|
||||
GtkWidget *item;
|
||||
|
||||
item = gtk_menu_item_new_with_mnemonic (paths[0]);
|
||||
gtk_container_add (GTK_CONTAINER (shell), item);
|
||||
|
||||
menu = gimp_menu_new (manager);
|
||||
gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), menu);
|
||||
gtk_widget_show (menu);
|
||||
|
||||
g_tree_insert (bar->priv->menus, g_strdup (paths[0]), menu);
|
||||
}
|
||||
|
||||
GIMP_MENU_SHELL_GET_INTERFACE (menu)->add_ui (GIMP_MENU_SHELL (menu),
|
||||
paths + 1,
|
||||
action_name,
|
||||
placeholder_key, top);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Public functions */
|
||||
|
||||
|
|
|
@ -24,20 +24,11 @@
|
|||
#include <gtk/gtk.h>
|
||||
|
||||
#include "libgimpbase/gimpbase.h"
|
||||
#include "libgimpwidgets/gimpwidgets.h"
|
||||
|
||||
#include "widgets-types.h"
|
||||
|
||||
#include "core/gimp.h"
|
||||
|
||||
#include "gimpaction.h"
|
||||
#include "gimpenumaction.h"
|
||||
#include "gimphelp-ids.h"
|
||||
#include "gimpmenu.h"
|
||||
#include "gimpmenushell.h"
|
||||
#include "gimpprocedureaction.h"
|
||||
#include "gimpradioaction.h"
|
||||
#include "gimptoggleaction.h"
|
||||
#include "gimpuimanager.h"
|
||||
|
||||
|
||||
|
@ -50,9 +41,6 @@ struct _GimpMenuShellPrivate
|
|||
GimpUIManager *manager;
|
||||
gchar *update_signal;
|
||||
|
||||
GTree *submenus;
|
||||
GTree *placeholders;
|
||||
|
||||
GRegex *path_regex;
|
||||
};
|
||||
|
||||
|
@ -61,8 +49,6 @@ static GimpMenuShellPrivate *
|
|||
gimp_menu_shell_get_private (GimpMenuShell *menu_shell);
|
||||
static void gimp_menu_shell_private_finalize (GimpMenuShellPrivate *priv);
|
||||
|
||||
static void gimp_menu_shell_help_fun (const gchar *bogus_help_id,
|
||||
gpointer help_data);
|
||||
static void gimp_menu_shell_ui_added (GimpUIManager *manager,
|
||||
const gchar *path,
|
||||
const gchar *action_name,
|
||||
|
@ -70,50 +56,15 @@ static void gimp_menu_shell_ui_added (GimpUIManager *m
|
|||
gboolean top,
|
||||
GimpMenuShell *shell);
|
||||
|
||||
static void gimp_menu_shell_toggle_item_toggled (GtkWidget *item,
|
||||
GAction *action);
|
||||
static void gimp_menu_shell_action_activate (GtkMenuItem *item,
|
||||
GimpAction *action);
|
||||
|
||||
static void gimp_menu_shell_notify_group_label (GimpRadioAction *action,
|
||||
const GParamSpec *pspec,
|
||||
GtkMenuItem *item);
|
||||
static void gimp_menu_shell_toggle_action_toggled (GimpAction *action,
|
||||
GtkCheckMenuItem *item);
|
||||
static void gimp_menu_shell_action_notify_sensitive (GimpAction *action,
|
||||
const GParamSpec *pspec,
|
||||
GtkCheckMenuItem *item);
|
||||
static void gimp_menu_shell_action_notify_visible (GimpAction *action,
|
||||
const GParamSpec *pspec,
|
||||
GtkWidget *item);
|
||||
|
||||
static void gimp_menu_shell_append_model_drop_top (GimpMenuShell *shell,
|
||||
GMenuModel *model);
|
||||
static void gimp_menu_shell_append_model (GimpMenuShell *shell,
|
||||
GMenuModel *model);
|
||||
static void gimp_menu_shell_add_ui (GimpMenuShell *shell,
|
||||
const gchar **paths,
|
||||
const gchar *action_name,
|
||||
const gchar *placeholder_key,
|
||||
gboolean top);
|
||||
static void gimp_menu_shell_add_action (GimpMenuShell *shell,
|
||||
const gchar *action_name,
|
||||
const gchar *placeholder_key,
|
||||
gboolean top,
|
||||
GtkRadioMenuItem **group);
|
||||
static void gimp_menu_shell_add_placeholder (GimpMenuShell *shell,
|
||||
const gchar *label);
|
||||
|
||||
static gchar ** gimp_menu_shell_break_path (GimpMenuShell *shell,
|
||||
const gchar *path);
|
||||
static gchar * gimp_menu_shell_make_canonical_path (const gchar *path);
|
||||
|
||||
static gboolean gimp_menu_shell_copy_placeholders (gpointer key,
|
||||
gpointer item,
|
||||
GTree *placeholders);
|
||||
|
||||
|
||||
G_DEFINE_INTERFACE (GimpMenuShell, gimp_menu_shell, GTK_TYPE_MENU_SHELL)
|
||||
G_DEFINE_INTERFACE (GimpMenuShell, gimp_menu_shell, G_TYPE_OBJECT)
|
||||
|
||||
|
||||
static void
|
||||
|
@ -145,7 +96,7 @@ gimp_menu_shell_fill (GimpMenuShell *shell,
|
|||
if (drop_top_submenu)
|
||||
gimp_menu_shell_append_model_drop_top (shell, model);
|
||||
else
|
||||
gimp_menu_shell_append_model (shell, model);
|
||||
GIMP_MENU_SHELL_GET_INTERFACE (shell)->append (shell, model);
|
||||
|
||||
if (update_signal != NULL)
|
||||
{
|
||||
|
@ -169,37 +120,6 @@ gimp_menu_shell_fill (GimpMenuShell *shell,
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
gimp_menu_shell_merge (GimpMenuShell *shell,
|
||||
GimpMenuShell *shell2,
|
||||
gboolean top)
|
||||
{
|
||||
GList *children;
|
||||
GList *iter;
|
||||
|
||||
children = gtk_container_get_children (GTK_CONTAINER (shell2));
|
||||
iter = top ? g_list_last (children) : children;
|
||||
for (; iter; iter = top ? iter->prev : iter->next)
|
||||
{
|
||||
GtkWidget *item = iter->data;
|
||||
|
||||
g_object_ref (item);
|
||||
gtk_container_remove (GTK_CONTAINER (shell2), item);
|
||||
if (top)
|
||||
gtk_menu_shell_prepend (GTK_MENU_SHELL (shell), item);
|
||||
else
|
||||
gtk_menu_shell_append (GTK_MENU_SHELL (shell), item);
|
||||
g_object_unref (item);
|
||||
}
|
||||
|
||||
g_tree_foreach (GET_PRIVATE (shell2)->placeholders,
|
||||
(GTraverseFunc) gimp_menu_shell_copy_placeholders,
|
||||
GET_PRIVATE (shell)->placeholders);
|
||||
|
||||
gtk_widget_destroy (GTK_WIDGET (shell2));
|
||||
g_list_free (children);
|
||||
}
|
||||
|
||||
|
||||
/* Protected functions. */
|
||||
|
||||
|
@ -212,18 +132,8 @@ gimp_menu_shell_init (GimpMenuShell *shell)
|
|||
|
||||
priv = GET_PRIVATE (shell);
|
||||
|
||||
priv->submenus = g_tree_new_full ((GCompareDataFunc) g_strcmp0, NULL,
|
||||
g_free, NULL);
|
||||
priv->placeholders = g_tree_new_full ((GCompareDataFunc) g_strcmp0, NULL,
|
||||
g_free, NULL);
|
||||
priv->update_signal = NULL;
|
||||
priv->path_regex = g_regex_new ("/+", 0, 0, NULL);
|
||||
|
||||
gimp_help_connect (GTK_WIDGET (shell),
|
||||
gimp_menu_shell_help_fun,
|
||||
GIMP_HELP_MAIN,
|
||||
shell,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -284,6 +194,26 @@ gimp_menu_shell_set_property (GObject *object,
|
|||
}
|
||||
}
|
||||
|
||||
GimpUIManager *
|
||||
gimp_menu_shell_get_manager (GimpMenuShell *shell)
|
||||
{
|
||||
return GET_PRIVATE (shell)->manager;
|
||||
}
|
||||
|
||||
gchar *
|
||||
gimp_menu_shell_make_canonical_path (const gchar *path)
|
||||
{
|
||||
gchar **split_path;
|
||||
gchar *canon_path;
|
||||
|
||||
/* The first underscore of each path item is a mnemonic. */
|
||||
split_path = g_strsplit (path, "_", 2);
|
||||
canon_path = g_strjoinv ("", split_path);
|
||||
g_strfreev (split_path);
|
||||
|
||||
return canon_path;
|
||||
}
|
||||
|
||||
|
||||
/* Private functions */
|
||||
|
||||
|
@ -314,67 +244,12 @@ gimp_menu_shell_get_private (GimpMenuShell *menu_shell)
|
|||
static void
|
||||
gimp_menu_shell_private_finalize (GimpMenuShellPrivate *priv)
|
||||
{
|
||||
g_tree_unref (priv->submenus);
|
||||
g_tree_unref (priv->placeholders);
|
||||
|
||||
g_free (priv->update_signal);
|
||||
g_clear_pointer (&priv->path_regex, g_regex_unref);
|
||||
|
||||
g_slice_free (GimpMenuShellPrivate, priv);
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_menu_shell_help_fun (const gchar *bogus_help_id,
|
||||
gpointer help_data)
|
||||
{
|
||||
GimpMenuShellPrivate *priv;
|
||||
gchar *help_id = NULL;
|
||||
GimpMenuShell *shell;
|
||||
Gimp *gimp;
|
||||
GtkWidget *item;
|
||||
gchar *help_domain = NULL;
|
||||
gchar *help_string = NULL;
|
||||
gchar *domain_separator;
|
||||
|
||||
g_return_if_fail (GIMP_IS_MENU_SHELL (help_data));
|
||||
|
||||
shell = GIMP_MENU_SHELL (help_data);
|
||||
priv = GET_PRIVATE (shell);
|
||||
gimp = priv->manager->gimp;
|
||||
|
||||
g_return_if_fail (GIMP_IS_GIMP (gimp));
|
||||
|
||||
item = gtk_menu_shell_get_selected_item (GTK_MENU_SHELL (shell));
|
||||
|
||||
if (item)
|
||||
help_id = g_object_get_qdata (G_OBJECT (item), GIMP_HELP_ID);
|
||||
|
||||
if (help_id == NULL || strlen (help_id) == 0)
|
||||
help_id = (gchar *) bogus_help_id;
|
||||
|
||||
help_id = g_strdup (help_id);
|
||||
|
||||
domain_separator = strchr (help_id, '?');
|
||||
|
||||
if (domain_separator)
|
||||
{
|
||||
*domain_separator = '\0';
|
||||
|
||||
help_domain = g_strdup (help_id);
|
||||
help_string = g_strdup (domain_separator + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
help_string = g_strdup (help_id);
|
||||
}
|
||||
|
||||
gimp_help (gimp, NULL, help_domain, help_string);
|
||||
|
||||
g_free (help_domain);
|
||||
g_free (help_string);
|
||||
g_free (help_id);
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_menu_shell_ui_added (GimpUIManager *manager,
|
||||
const gchar *path,
|
||||
|
@ -385,114 +260,15 @@ gimp_menu_shell_ui_added (GimpUIManager *manager,
|
|||
{
|
||||
gchar **paths = gimp_menu_shell_break_path (shell, path);
|
||||
|
||||
gimp_menu_shell_add_ui (shell, (const gchar **) paths,
|
||||
action_name, placeholder_key, top);
|
||||
if (GIMP_MENU_SHELL_GET_INTERFACE (shell)->add_ui)
|
||||
GIMP_MENU_SHELL_GET_INTERFACE (shell)->add_ui (shell,
|
||||
(const gchar **) paths,
|
||||
action_name,
|
||||
placeholder_key, top);
|
||||
|
||||
g_strfreev (paths);
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_menu_shell_toggle_item_toggled (GtkWidget *item,
|
||||
GAction *action)
|
||||
{
|
||||
gboolean active = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (item));
|
||||
|
||||
g_signal_handlers_block_by_func (action,
|
||||
G_CALLBACK (gimp_menu_shell_toggle_action_toggled),
|
||||
item);
|
||||
gimp_toggle_action_set_active (GIMP_TOGGLE_ACTION (action), active);
|
||||
g_signal_handlers_unblock_by_func (action,
|
||||
G_CALLBACK (gimp_menu_shell_toggle_action_toggled),
|
||||
item);
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_menu_shell_action_activate (GtkMenuItem *item,
|
||||
GimpAction *action)
|
||||
{
|
||||
gimp_action_activate (action);
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_menu_shell_notify_group_label (GimpRadioAction *action,
|
||||
const GParamSpec *pspec,
|
||||
GtkMenuItem *item)
|
||||
{
|
||||
gtk_menu_item_set_use_underline (item, TRUE);
|
||||
gtk_menu_item_set_label (item, gimp_radio_action_get_group_label (action));
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_menu_shell_toggle_action_toggled (GimpAction *action,
|
||||
GtkCheckMenuItem *item)
|
||||
{
|
||||
gboolean active = gimp_toggle_action_get_active (GIMP_TOGGLE_ACTION (action));
|
||||
|
||||
g_signal_handlers_block_by_func (item,
|
||||
G_CALLBACK (gimp_menu_shell_toggle_item_toggled),
|
||||
action);
|
||||
gtk_check_menu_item_set_active (item, active);
|
||||
g_signal_handlers_unblock_by_func (item,
|
||||
G_CALLBACK (gimp_menu_shell_toggle_item_toggled),
|
||||
action);
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_menu_shell_action_notify_sensitive (GimpAction *action,
|
||||
const GParamSpec *pspec,
|
||||
GtkCheckMenuItem *item)
|
||||
{
|
||||
gtk_widget_set_sensitive (GTK_WIDGET (item),
|
||||
gimp_action_is_sensitive (action, NULL));
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_menu_shell_action_notify_visible (GimpAction *action,
|
||||
const GParamSpec *pspec,
|
||||
GtkWidget *item)
|
||||
{
|
||||
GtkWidget *container;
|
||||
|
||||
gtk_widget_set_visible (item, gimp_action_is_visible (action));
|
||||
|
||||
container = gtk_widget_get_parent (item);
|
||||
if (gimp_action_is_visible (GIMP_ACTION (action)))
|
||||
{
|
||||
GtkWidget *widget = gtk_menu_get_attach_widget (GTK_MENU (container));
|
||||
|
||||
/* We must show the GtkMenuItem associated as submenu to the parent
|
||||
* container.
|
||||
*/
|
||||
if (G_TYPE_FROM_INSTANCE (widget) == GTK_TYPE_MENU_ITEM)
|
||||
gtk_widget_show (widget);
|
||||
}
|
||||
else
|
||||
{
|
||||
GList *children = gtk_container_get_children (GTK_CONTAINER (container));
|
||||
gboolean all_invisible = TRUE;
|
||||
|
||||
for (GList *iter = children; iter; iter = iter->next)
|
||||
{
|
||||
if (gtk_widget_get_visible (iter->data))
|
||||
{
|
||||
all_invisible = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
g_list_free (children);
|
||||
|
||||
if (all_invisible)
|
||||
{
|
||||
GtkWidget *widget;
|
||||
|
||||
/* No need to leave empty submenus. */
|
||||
widget = gtk_menu_get_attach_widget (GTK_MENU (container));
|
||||
if (G_TYPE_FROM_INSTANCE (widget) == GTK_TYPE_MENU_ITEM)
|
||||
gtk_widget_hide (widget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_menu_shell_append_model_drop_top (GimpMenuShell *shell,
|
||||
GMenuModel *model)
|
||||
|
@ -504,334 +280,11 @@ gimp_menu_shell_append_model_drop_top (GimpMenuShell *shell,
|
|||
if (g_menu_model_get_n_items (model) == 1)
|
||||
submenu = g_menu_model_get_item_link (model, 0, G_MENU_LINK_SUBMENU);
|
||||
|
||||
gimp_menu_shell_append_model (shell, submenu != NULL ? submenu : model);
|
||||
GIMP_MENU_SHELL_GET_INTERFACE (shell)->append (shell, submenu != NULL ? submenu : model);
|
||||
|
||||
g_clear_object (&submenu);
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_menu_shell_append_model (GimpMenuShell *shell,
|
||||
GMenuModel *model)
|
||||
{
|
||||
GimpMenuShellPrivate *priv;
|
||||
static GtkRadioMenuItem *group = NULL;
|
||||
gint n_items;
|
||||
|
||||
g_return_if_fail (GTK_IS_CONTAINER (shell));
|
||||
|
||||
priv = GET_PRIVATE (shell);
|
||||
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 (GTK_CONTAINER (shell), item);
|
||||
gtk_widget_show (item);
|
||||
|
||||
gimp_menu_shell_append_model (shell, subsection);
|
||||
|
||||
item = gtk_separator_menu_item_new ();
|
||||
gtk_container_add (GTK_CONTAINER (shell), item);
|
||||
gtk_widget_show (item);
|
||||
}
|
||||
else if (submenu != NULL && label == NULL)
|
||||
{
|
||||
GApplication *app;
|
||||
GAction *action;
|
||||
const gchar *group_label;
|
||||
GtkWidget *subcontainer;
|
||||
GtkWidget *item;
|
||||
|
||||
group = NULL;
|
||||
|
||||
g_return_if_fail (action_name != NULL);
|
||||
|
||||
app = priv->manager->gimp->app;
|
||||
action = g_action_map_lookup_action (G_ACTION_MAP (app), action_name + 4);
|
||||
|
||||
/* As a special case, when a submenu has no label, we expect it to
|
||||
* have an action attribute, which must be for a radio action. In such
|
||||
* a case, we'll use the radio actions' group label as submenu title.
|
||||
* See e.g.: menus/gradient-editor-menu.ui
|
||||
*/
|
||||
g_return_if_fail (GIMP_IS_RADIO_ACTION (action));
|
||||
|
||||
group_label = gimp_radio_action_get_group_label (GIMP_RADIO_ACTION (action));
|
||||
|
||||
item = gtk_menu_item_new_with_mnemonic (group_label);
|
||||
g_signal_connect_object (action, "notify::group-label",
|
||||
G_CALLBACK (gimp_menu_shell_notify_group_label),
|
||||
item, 0);
|
||||
gtk_container_add (GTK_CONTAINER (shell), item);
|
||||
|
||||
subcontainer = gimp_menu_new (priv->manager);
|
||||
gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), subcontainer);
|
||||
gimp_menu_shell_append_model (GIMP_MENU_SHELL (subcontainer), submenu);
|
||||
gtk_widget_show (subcontainer);
|
||||
|
||||
g_tree_insert (priv->submenus,
|
||||
gimp_menu_shell_make_canonical_path (group_label),
|
||||
subcontainer);
|
||||
}
|
||||
else if (submenu != NULL)
|
||||
{
|
||||
GtkWidget *subcontainer;
|
||||
GtkWidget *item;
|
||||
|
||||
group = NULL;
|
||||
|
||||
/* I don't show the item on purpose because
|
||||
* gimp_menu_shell_append_model() will show the parent item if any of
|
||||
* the added actions are visible.
|
||||
*/
|
||||
item = gtk_menu_item_new_with_mnemonic (label);
|
||||
gtk_container_add (GTK_CONTAINER (shell), item);
|
||||
|
||||
subcontainer = gimp_menu_new (priv->manager);
|
||||
gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), subcontainer);
|
||||
gimp_menu_shell_append_model (GIMP_MENU_SHELL (subcontainer), submenu);
|
||||
gtk_widget_show (subcontainer);
|
||||
|
||||
g_tree_insert (priv->submenus,
|
||||
gimp_menu_shell_make_canonical_path (label),
|
||||
subcontainer);
|
||||
}
|
||||
else if (action_name == NULL)
|
||||
{
|
||||
/* Special case: we use items with no action and a label as
|
||||
* placeholder which allows us to specify a placement in menus, which
|
||||
* might not be only top or bottom.
|
||||
*/
|
||||
g_return_if_fail (label != NULL);
|
||||
|
||||
group = NULL;
|
||||
|
||||
gimp_menu_shell_add_placeholder (shell, label);
|
||||
}
|
||||
else
|
||||
{
|
||||
gimp_menu_shell_add_action (shell, action_name, NULL, FALSE, &group);
|
||||
}
|
||||
g_free (label);
|
||||
g_free (action_name);
|
||||
g_clear_object (&submenu);
|
||||
g_clear_object (&subsection);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_menu_shell_add_ui (GimpMenuShell *shell,
|
||||
const gchar **paths,
|
||||
const gchar *action_name,
|
||||
const gchar *placeholder_key,
|
||||
gboolean top)
|
||||
{
|
||||
g_return_if_fail (paths != NULL);
|
||||
|
||||
if (paths[0] == NULL)
|
||||
{
|
||||
gimp_menu_shell_add_action (shell, action_name, placeholder_key, top, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
GimpMenuShellPrivate *priv = GET_PRIVATE (shell);
|
||||
GtkWidget *submenu = NULL;
|
||||
|
||||
submenu = g_tree_lookup (priv->submenus, paths[0]);
|
||||
|
||||
if (submenu == NULL)
|
||||
{
|
||||
GtkWidget *item;
|
||||
|
||||
item = gtk_menu_item_new_with_mnemonic (paths[0]);
|
||||
gtk_container_add (GTK_CONTAINER (shell), item);
|
||||
|
||||
submenu = gimp_menu_new (priv->manager);
|
||||
gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu);
|
||||
gtk_widget_show (submenu);
|
||||
|
||||
g_tree_insert (priv->submenus, g_strdup (paths[0]), submenu);
|
||||
}
|
||||
|
||||
gimp_menu_shell_add_ui (GIMP_MENU_SHELL (submenu), paths + 1,
|
||||
action_name, placeholder_key, top);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_menu_shell_add_action (GimpMenuShell *shell,
|
||||
const gchar *action_name,
|
||||
const gchar *placeholder_key,
|
||||
gboolean top,
|
||||
GtkRadioMenuItem **group)
|
||||
{
|
||||
GimpMenuShellPrivate *priv;
|
||||
GApplication *app;
|
||||
GAction *action;
|
||||
const gchar *action_label;
|
||||
GtkWidget *item;
|
||||
GtkWidget *sibling = NULL;
|
||||
gboolean visible;
|
||||
|
||||
g_return_if_fail (GIMP_IS_MENU_SHELL (shell));
|
||||
|
||||
priv = GET_PRIVATE (shell);
|
||||
|
||||
app = priv->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));
|
||||
|
||||
action_label = gimp_action_get_label (GIMP_ACTION (action));
|
||||
g_return_if_fail (action_label != NULL);
|
||||
|
||||
if (GIMP_IS_TOGGLE_ACTION (action))
|
||||
{
|
||||
if (GIMP_IS_RADIO_ACTION (action))
|
||||
item = gtk_radio_menu_item_new_with_mnemonic_from_widget (group ? *group : NULL,
|
||||
action_label);
|
||||
else
|
||||
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)));
|
||||
|
||||
if (group)
|
||||
{
|
||||
if (GIMP_IS_RADIO_ACTION (action))
|
||||
*group = GTK_RADIO_MENU_ITEM (item);
|
||||
else
|
||||
*group = NULL;
|
||||
}
|
||||
|
||||
g_signal_connect (item, "toggled",
|
||||
G_CALLBACK (gimp_menu_shell_toggle_item_toggled),
|
||||
action);
|
||||
g_signal_connect_object (action, "toggled",
|
||||
G_CALLBACK (gimp_menu_shell_toggle_action_toggled),
|
||||
item, 0);
|
||||
}
|
||||
else if (GIMP_IS_PROCEDURE_ACTION (action) ||
|
||||
GIMP_IS_ENUM_ACTION (action))
|
||||
{
|
||||
item = gtk_menu_item_new_with_mnemonic (action_label);
|
||||
|
||||
if (group)
|
||||
*group = NULL;
|
||||
|
||||
g_signal_connect (item, "activate",
|
||||
G_CALLBACK (gimp_menu_shell_action_activate),
|
||||
action);
|
||||
}
|
||||
else
|
||||
{
|
||||
item = gtk_menu_item_new_with_mnemonic (action_label);
|
||||
|
||||
if (group)
|
||||
*group = NULL;
|
||||
|
||||
gtk_actionable_set_action_name (GTK_ACTIONABLE (item), action_name);
|
||||
}
|
||||
|
||||
gimp_action_set_proxy (GIMP_ACTION (action), item);
|
||||
|
||||
gtk_widget_set_sensitive (GTK_WIDGET (item),
|
||||
gimp_action_is_sensitive (GIMP_ACTION (action), NULL));
|
||||
g_signal_connect_object (action, "notify::sensitive",
|
||||
G_CALLBACK (gimp_menu_shell_action_notify_sensitive),
|
||||
item, 0);
|
||||
|
||||
if (placeholder_key)
|
||||
{
|
||||
sibling = g_tree_lookup (priv->placeholders, placeholder_key);
|
||||
|
||||
if (! sibling)
|
||||
g_warning ("%s: no placeholder item '%s'.", G_STRFUNC, placeholder_key);
|
||||
}
|
||||
|
||||
if (sibling)
|
||||
{
|
||||
GList *children;
|
||||
gint position = 0;
|
||||
|
||||
/* I am assuming that the order of the children list reflects the
|
||||
* position, though it is not clearly specified in the function docs. Yet
|
||||
* I could find no other function giving me the position of some child in
|
||||
* a container.
|
||||
*/
|
||||
children = gtk_container_get_children (GTK_CONTAINER (shell));
|
||||
|
||||
for (GList *iter = children; iter; iter = iter->next)
|
||||
{
|
||||
if (iter->data == sibling)
|
||||
break;
|
||||
position++;
|
||||
}
|
||||
if (! top)
|
||||
position++;
|
||||
|
||||
gtk_menu_shell_insert (GTK_MENU_SHELL (shell), item, position);
|
||||
|
||||
g_list_free (children);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (top)
|
||||
gtk_menu_shell_prepend (GTK_MENU_SHELL (shell), item);
|
||||
else
|
||||
gtk_menu_shell_append (GTK_MENU_SHELL (shell), item);
|
||||
}
|
||||
|
||||
visible = gimp_action_is_visible (GIMP_ACTION (action));
|
||||
gtk_widget_set_visible (item, visible);
|
||||
if (visible && GTK_IS_MENU (shell))
|
||||
{
|
||||
GtkWidget *parent = gtk_menu_get_attach_widget (GTK_MENU (shell));
|
||||
|
||||
/* Note that this is not the container we must show, but the menu item
|
||||
* attached to the parent, in order not to leave empty submenus.
|
||||
*/
|
||||
if (parent != NULL &&
|
||||
G_TYPE_FROM_INSTANCE (parent) == GTK_TYPE_MENU_ITEM)
|
||||
gtk_widget_show (parent);
|
||||
}
|
||||
g_signal_connect_object (action, "notify::visible",
|
||||
G_CALLBACK (gimp_menu_shell_action_notify_visible),
|
||||
item, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_menu_shell_add_placeholder (GimpMenuShell *shell,
|
||||
const gchar *label)
|
||||
{
|
||||
GimpMenuShellPrivate *priv = GET_PRIVATE (shell);
|
||||
GtkWidget *item;
|
||||
|
||||
/* Placeholders are inserted yet never shown, on purpose. */
|
||||
item = gtk_menu_item_new_with_mnemonic (label);
|
||||
gtk_container_add (GTK_CONTAINER (shell), item);
|
||||
|
||||
g_tree_insert (priv->placeholders, g_strdup (label), item);
|
||||
}
|
||||
|
||||
static gchar **
|
||||
gimp_menu_shell_break_path (GimpMenuShell *shell,
|
||||
const gchar *path)
|
||||
|
@ -867,26 +320,3 @@ gimp_menu_shell_break_path (GimpMenuShell *shell,
|
|||
|
||||
return paths;
|
||||
}
|
||||
|
||||
static gchar *
|
||||
gimp_menu_shell_make_canonical_path (const gchar *path)
|
||||
{
|
||||
gchar **split_path;
|
||||
gchar *canon_path;
|
||||
|
||||
/* The first underscore of each path item is a mnemonic. */
|
||||
split_path = g_strsplit (path, "_", 2);
|
||||
canon_path = g_strjoinv ("", split_path);
|
||||
g_strfreev (split_path);
|
||||
|
||||
return canon_path;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gimp_menu_shell_copy_placeholders (gpointer key,
|
||||
gpointer item,
|
||||
GTree *placeholders)
|
||||
{
|
||||
g_tree_insert (placeholders, g_strdup ((gchar *) key), item);
|
||||
return FALSE;
|
||||
}
|
||||
|
|
|
@ -41,6 +41,16 @@ typedef struct _GimpMenuShellInterface GimpMenuShellInterface;
|
|||
struct _GimpMenuShellInterface
|
||||
{
|
||||
GTypeInterface base_interface;
|
||||
|
||||
/* Virtual functions. */
|
||||
|
||||
void (* append) (GimpMenuShell *shell,
|
||||
GMenuModel *model);
|
||||
void (* add_ui) (GimpMenuShell *shell,
|
||||
const gchar **paths,
|
||||
const gchar *action_name,
|
||||
const gchar *placeholder_key,
|
||||
gboolean top);
|
||||
};
|
||||
|
||||
|
||||
|
@ -50,9 +60,6 @@ void gimp_menu_shell_fill (GimpMenuShell *shell,
|
|||
GMenuModel *model,
|
||||
const gchar *update_signal,
|
||||
gboolean drop_top_submenu);
|
||||
void gimp_menu_shell_merge (GimpMenuShell *shell,
|
||||
GimpMenuShell *shell2,
|
||||
gboolean top);
|
||||
|
||||
|
||||
/* Protected functions. */
|
||||
|
@ -68,5 +75,8 @@ void gimp_menu_shell_set_property (GObject *object,
|
|||
const GValue *value,
|
||||
GParamSpec *pspec);
|
||||
|
||||
GimpUIManager * gimp_menu_shell_get_manager (GimpMenuShell *shell);
|
||||
gchar * gimp_menu_shell_make_canonical_path (const gchar *path);
|
||||
|
||||
|
||||
#endif /* __GIMP_MENU_SHELL_H__ */
|
||||
|
|
|
@ -757,7 +757,7 @@ gimp_ui_manager_ui_popup_at_widget (GimpUIManager *manager,
|
|||
child_menu = gimp_menu_new (child_ui_manager);
|
||||
gimp_menu_shell_fill (GIMP_MENU_SHELL (child_menu), child_model, "ui-added", FALSE);
|
||||
|
||||
gimp_menu_shell_merge (GIMP_MENU_SHELL (menu), GIMP_MENU_SHELL (child_menu), TRUE);
|
||||
gimp_menu_merge (GIMP_MENU (menu), GIMP_MENU (child_menu), TRUE);
|
||||
}
|
||||
|
||||
if (popdown_func && popdown_data)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue