forked from AuroraMiddleware/gtk
2398d7e900
Move internal accel map API there and update all users. Also, add an internal function to create an accel path for an action and parameter, and use it in gtkapplication.c and gtkmodelmenuitem.c instead of duplicating that code.
305 lines
9.9 KiB
C
305 lines
9.9 KiB
C
/*
|
|
* Copyright © 2011 Canonical Limited
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the licence, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*
|
|
* Author: Ryan Lortie <desrt@desrt.ca>
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "gtkmodelmenuitem.h"
|
|
|
|
#include "gtkaccelmapprivate.h"
|
|
#include "gtkmodelmenu.h"
|
|
|
|
struct _GtkModelMenuItem
|
|
{
|
|
GtkCheckMenuItem parent_instance;
|
|
|
|
GActionGroup *actions;
|
|
const gchar *action_name;
|
|
gboolean has_indicator;
|
|
gboolean can_activate;
|
|
GVariant *target;
|
|
};
|
|
|
|
typedef GtkCheckMenuItemClass GtkModelMenuItemClass;
|
|
|
|
static void gtk_model_menu_item_observer_iface_init (GActionObserverInterface *iface);
|
|
G_DEFINE_TYPE_WITH_CODE (GtkModelMenuItem, gtk_model_menu_item, GTK_TYPE_CHECK_MENU_ITEM,
|
|
G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_OBSERVER, gtk_model_menu_item_observer_iface_init))
|
|
|
|
static void
|
|
gtk_model_menu_item_activate (GtkMenuItem *menu_item)
|
|
{
|
|
GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (menu_item);
|
|
|
|
if (item->can_activate)
|
|
g_action_group_activate_action (item->actions, item->action_name, item->target);
|
|
}
|
|
|
|
static void
|
|
gtk_model_menu_item_toggle_size_request (GtkMenuItem *menu_item,
|
|
gint *requisition)
|
|
{
|
|
GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (menu_item);
|
|
|
|
if (item->has_indicator)
|
|
GTK_MENU_ITEM_CLASS (gtk_model_menu_item_parent_class)
|
|
->toggle_size_request (menu_item, requisition);
|
|
|
|
else
|
|
*requisition = 0;
|
|
}
|
|
|
|
static void
|
|
gtk_model_menu_item_draw_indicator (GtkCheckMenuItem *check_item,
|
|
cairo_t *cr)
|
|
{
|
|
GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (check_item);
|
|
|
|
if (item->has_indicator)
|
|
GTK_CHECK_MENU_ITEM_CLASS (gtk_model_menu_item_parent_class)
|
|
->draw_indicator (check_item, cr);
|
|
}
|
|
|
|
static void
|
|
gtk_model_menu_item_set_active (GtkModelMenuItem *item,
|
|
gboolean active)
|
|
{
|
|
GtkCheckMenuItem *checkitem = GTK_CHECK_MENU_ITEM (item);
|
|
|
|
if (gtk_check_menu_item_get_active (checkitem) != active)
|
|
{
|
|
_gtk_check_menu_item_set_active (checkitem, active);
|
|
g_object_notify (G_OBJECT (checkitem), "active");
|
|
gtk_check_menu_item_toggled (checkitem);
|
|
gtk_widget_queue_draw (GTK_WIDGET (item));
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_model_menu_item_action_added (GActionObserver *observer,
|
|
GActionObservable *observable,
|
|
const gchar *action_name,
|
|
const GVariantType *parameter_type,
|
|
gboolean enabled,
|
|
GVariant *state)
|
|
{
|
|
GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (observer);
|
|
|
|
/* we can only activate the item if we have the correct type of parameter */
|
|
item->can_activate = (item->target == NULL && parameter_type == NULL) ||
|
|
(item->target != NULL && parameter_type != NULL &&
|
|
g_variant_is_of_type (item->target, parameter_type));
|
|
|
|
if (item->can_activate)
|
|
{
|
|
if (item->target != NULL && state != NULL)
|
|
{
|
|
/* actions with states and targets are radios */
|
|
gboolean selected;
|
|
|
|
selected = g_variant_equal (state, item->target);
|
|
gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (item), TRUE);
|
|
gtk_model_menu_item_set_active (item, selected);
|
|
item->has_indicator = TRUE;
|
|
}
|
|
|
|
else if (state != NULL && g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN))
|
|
{
|
|
/* boolean state actions without target are checks */
|
|
gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (item), FALSE);
|
|
gtk_model_menu_item_set_active (item, g_variant_get_boolean (state));
|
|
item->has_indicator = TRUE;
|
|
}
|
|
|
|
else
|
|
{
|
|
/* stateless items are just plain actions */
|
|
gtk_model_menu_item_set_active (item, FALSE);
|
|
item->has_indicator = FALSE;
|
|
}
|
|
|
|
gtk_widget_set_sensitive (GTK_WIDGET (item), enabled);
|
|
gtk_widget_queue_resize (GTK_WIDGET (item));
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_model_menu_item_action_enabled_changed (GActionObserver *observer,
|
|
GActionObservable *observable,
|
|
const gchar *action_name,
|
|
gboolean enabled)
|
|
{
|
|
GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (observer);
|
|
|
|
if (!item->can_activate)
|
|
return;
|
|
|
|
gtk_widget_set_sensitive (GTK_WIDGET (item), item->can_activate && enabled);
|
|
}
|
|
|
|
static void
|
|
gtk_model_menu_item_action_state_changed (GActionObserver *observer,
|
|
GActionObservable *observable,
|
|
const gchar *action_name,
|
|
GVariant *state)
|
|
{
|
|
GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (observer);
|
|
|
|
if (!item->can_activate)
|
|
return;
|
|
|
|
if (item->target)
|
|
gtk_model_menu_item_set_active (item, g_variant_equal (state, item->target));
|
|
|
|
else if (g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN))
|
|
gtk_model_menu_item_set_active (item, g_variant_get_boolean (state));
|
|
}
|
|
|
|
static void
|
|
gtk_model_menu_item_action_removed (GActionObserver *observer,
|
|
GActionObservable *observable,
|
|
const gchar *action_name)
|
|
{
|
|
GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (observer);
|
|
|
|
if (!item->can_activate)
|
|
return;
|
|
|
|
gtk_widget_set_sensitive (GTK_WIDGET (item), FALSE);
|
|
gtk_model_menu_item_set_active (item, FALSE);
|
|
item->has_indicator = FALSE;
|
|
|
|
gtk_widget_queue_resize (GTK_WIDGET (item));
|
|
}
|
|
|
|
static void
|
|
gtk_model_menu_item_setup (GtkModelMenuItem *item,
|
|
GMenuModel *model,
|
|
gint item_index,
|
|
GActionObservable *actions,
|
|
GtkAccelGroup *accels)
|
|
{
|
|
GMenuAttributeIter *iter;
|
|
GMenuModel *submenu;
|
|
const gchar *key;
|
|
GVariant *value;
|
|
|
|
if ((submenu = g_menu_model_get_item_link (model, item_index, "submenu")))
|
|
{
|
|
gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), gtk_model_menu_create_menu (submenu, actions, accels));
|
|
g_object_unref (submenu);
|
|
}
|
|
|
|
iter = g_menu_model_iterate_item_attributes (model, item_index);
|
|
while (g_menu_attribute_iter_get_next (iter, &key, &value))
|
|
{
|
|
if (g_str_equal (key, "label") && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
|
|
gtk_menu_item_set_label (GTK_MENU_ITEM (item), g_variant_get_string (value, NULL));
|
|
|
|
else if (g_str_equal (key, "action") && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
|
|
item->action_name = g_variant_get_string (value, NULL);
|
|
|
|
else if (g_str_equal (key, "target"))
|
|
item->target = g_variant_ref (value);
|
|
|
|
g_variant_unref (value);
|
|
}
|
|
g_object_unref (iter);
|
|
|
|
gtk_menu_item_set_use_underline (GTK_MENU_ITEM (item), TRUE);
|
|
|
|
if (item->action_name)
|
|
{
|
|
const GVariantType *type;
|
|
gboolean enabled;
|
|
GVariant *state;
|
|
gchar *path;
|
|
|
|
/* observer already causes us to hold a hard ref on the group */
|
|
item->actions = G_ACTION_GROUP (actions);
|
|
|
|
g_action_observable_register_observer (actions, item->action_name, G_ACTION_OBSERVER (item));
|
|
|
|
if (g_action_group_query_action (G_ACTION_GROUP (actions), item->action_name, &enabled, &type, NULL, NULL, &state))
|
|
gtk_model_menu_item_action_added (G_ACTION_OBSERVER (item), actions, item->action_name, type, enabled, state);
|
|
|
|
else
|
|
gtk_widget_set_sensitive (GTK_WIDGET (item), FALSE);
|
|
|
|
if (state != NULL)
|
|
g_variant_unref (state);
|
|
|
|
path = _gtk_accel_path_for_action (item->action_name, item->target);
|
|
gtk_menu_item_set_accel_path (GTK_MENU_ITEM (item), path);
|
|
g_free (path);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_model_menu_item_finalize (GObject *object)
|
|
{
|
|
G_OBJECT_CLASS (gtk_model_menu_item_parent_class)
|
|
->finalize (object);
|
|
}
|
|
|
|
static void
|
|
gtk_model_menu_item_init (GtkModelMenuItem *item)
|
|
{
|
|
}
|
|
|
|
static void
|
|
gtk_model_menu_item_observer_iface_init (GActionObserverInterface *iface)
|
|
{
|
|
iface->action_added = gtk_model_menu_item_action_added;
|
|
iface->action_enabled_changed = gtk_model_menu_item_action_enabled_changed;
|
|
iface->action_state_changed = gtk_model_menu_item_action_state_changed;
|
|
iface->action_removed = gtk_model_menu_item_action_removed;
|
|
}
|
|
|
|
static void
|
|
gtk_model_menu_item_class_init (GtkModelMenuItemClass *class)
|
|
{
|
|
GtkCheckMenuItemClass *check_class = GTK_CHECK_MENU_ITEM_CLASS (class);
|
|
GtkMenuItemClass *item_class = GTK_MENU_ITEM_CLASS (class);
|
|
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
|
|
|
check_class->draw_indicator = gtk_model_menu_item_draw_indicator;
|
|
|
|
item_class->activate = gtk_model_menu_item_activate;
|
|
item_class->toggle_size_request = gtk_model_menu_item_toggle_size_request;
|
|
|
|
object_class->finalize = gtk_model_menu_item_finalize;
|
|
}
|
|
|
|
GtkMenuItem *
|
|
gtk_model_menu_item_new (GMenuModel *model,
|
|
gint item_index,
|
|
GActionObservable *actions,
|
|
GtkAccelGroup *accels)
|
|
{
|
|
GtkModelMenuItem *item;
|
|
|
|
item = g_object_new (GTK_TYPE_MODEL_MENU_ITEM, NULL);
|
|
|
|
gtk_model_menu_item_setup (item, model, item_index, actions, accels);
|
|
|
|
return GTK_MENU_ITEM (item);
|
|
}
|