gtk2/gtk/gtkmodelmenuitem.c
Matthias Clasen 2398d7e900 Introduce gtkaccelmapprivate.h
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.
2011-12-19 12:51:11 -05:00

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);
}