/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */

#include "egg-action-group.h"
#include "egg-toggle-action.h"
#include "egg-radio-action.h"
#include "egg-entry-action.h"
#include "egg-history-action.h"
#include "gobject-utils.h"
#include "intl.h"

static void egg_action_group_init       (EggActionGroup *self);
static void egg_action_group_class_init (EggActionGroupClass *class);

static GObjectClass *parent_class = NULL;
static void       egg_action_group_dispose         (GObject *object);
static EggAction *egg_action_group_real_get_action (EggActionGroup *self,
						    const gchar *name);

KZ_OBJECT_GET_TYPE(egg_action_group, "EggActionGroup", EggActionGroup,
		   egg_action_group_class_init, egg_action_group_init,
		   G_TYPE_OBJECT)

static void
egg_action_group_class_init (EggActionGroupClass *class)
{
	GObjectClass *object_class;

	object_class = G_OBJECT_CLASS(class);
	parent_class = g_type_class_peek_parent(class);

	object_class->dispose = egg_action_group_dispose;
	class->get_action = egg_action_group_real_get_action;
}

static void
egg_action_group_init (EggActionGroup *self)
{
	self->name = NULL;
	self->actions = g_hash_table_new_full(g_str_hash, g_str_equal,
					      (GDestroyNotify) g_free,
					      (GDestroyNotify) g_object_unref);
	self->accel_group = NULL;
}

static void
egg_action_group_dispose (GObject *object)
{
	EggActionGroup *self;

	self = EGG_ACTION_GROUP(object);

	g_free(self->name);
	self->name = NULL;

	g_hash_table_destroy(self->actions);
	self->actions = NULL;

	if (self->accel_group)
		g_object_unref(self->accel_group);
	self->accel_group = NULL;

	if (G_OBJECT_CLASS (parent_class)->dispose) {
		G_OBJECT_CLASS (parent_class)->dispose(object);
	}
}

/**
 * egg_action_group_new:
 * @name: the name of the action group
 *
 * Creates a new EggActionGroup object.
 *
 * Returns: the new EggActionGroup
 */
EggActionGroup *
egg_action_group_new(const gchar *name)
{
	EggActionGroup *self;

	self = g_object_new(EGG_TYPE_ACTION_GROUP, NULL);
	self->name = g_strdup(name);

	return self;
}

void
egg_action_group_set_accel_group (EggActionGroup *self,
				  GtkAccelGroup *accel_group)
{
	g_return_if_fail(EGG_IS_ACTION_GROUP(self));

	if (self->accel_group)
		g_object_unref(self->accel_group);
	self->accel_group = NULL;

	if (accel_group && GTK_IS_ACCEL_GROUP(accel_group))
		self->accel_group = g_object_ref(accel_group);
}

static EggAction *
egg_action_group_real_get_action (EggActionGroup *self,
				  const gchar *action_name)
{
	return g_hash_table_lookup(self->actions, action_name);
}

/**
 * egg_action_group_get_name:
 * @action_group: the action group
 *
 * Returns: the name of the EggActionGroup
 */
const gchar *
egg_action_group_get_name (EggActionGroup *action_group)
{
	g_return_val_if_fail (EGG_IS_ACTION_GROUP(action_group), NULL);

	return action_group->name;
}

/**
 * egg_action_group_get_action:
 * @action_group: the action group
 * @action_name: the name of the action
 *
 * This function looks up an action in the action group by name.
 *
 * Returns: the action, or NULL if no action by that name exists
 */
EggAction *
egg_action_group_get_action (EggActionGroup *action_group,
			     const gchar *action_name)
{
	g_return_val_if_fail(EGG_IS_ACTION_GROUP(action_group), NULL);
	g_return_val_if_fail(EGG_ACTION_GROUP_GET_CLASS(action_group)->get_action != NULL, NULL);

	return (*EGG_ACTION_GROUP_GET_CLASS(action_group)->get_action)
		(action_group, action_name);
}

/**
 * egg_action_group_add_action:
 * @action_group: the action group
 * @action: an action
 *
 * This function adds an action object to the action group.
 */
void
egg_action_group_add_action (EggActionGroup *action_group,
			     EggAction *action)
{
	g_return_if_fail(EGG_IS_ACTION_GROUP(action_group));
	g_return_if_fail(EGG_IS_ACTION(action));
	g_return_if_fail(action->name != NULL);

	g_hash_table_insert(action_group->actions, g_strdup(action->name),
			    g_object_ref(action));
}

/**
 * egg_action_group_removes_action:
 * @action_group: the action group
 * @action: an action
 *
 * This function removes an action object to the action group.
 */
void
egg_action_group_remove_action (EggActionGroup *action_group,
				EggAction *action)
{
	g_return_if_fail(EGG_IS_ACTION_GROUP(action_group));
	g_return_if_fail(EGG_IS_ACTION(action));
	g_return_if_fail(action->name != NULL);

	/* extra protection to make sure action->name is valid */
	g_object_ref(action);
	g_hash_table_remove(action_group->actions, action->name);
	g_object_unref(action);
}

static void
add_single_action (gpointer key, gpointer value, gpointer user_data)
{
	GList **list = user_data;

	*list = g_list_prepend(*list, value);
}

/**
 * egg_action_group_list_actions:
 * @action_group: the action group
 *
 * Lists the actions in the action group.
 *
 * Returns: an allocated list of the action objects in the action group
 */
GList *
egg_action_group_list_actions (EggActionGroup *action_group)
{
	GList *actions = NULL;
  
	g_hash_table_foreach(action_group->actions,
			     add_single_action, &actions);

	return g_list_reverse(actions);
}


/**
 * egg_action_group_add_actions:
 * @action_group: the action group
 * @entries: an array of action descriptions
 * @n_entries: the number of entries
 *
 * This is a convenience routine to create a number of actions and add
 * them to the action group.  Each member of the array describes an
 * action to create.
 */
void
egg_action_group_add_actions (EggActionGroup *action_group,
			      EggActionGroupEntry *entries,
			      guint n_entries)
{
	guint i;

	for (i = 0; i < n_entries; i++)
	{
		EggAction *action;
		GType action_type;

		switch (entries[i].entry_type)
		{
		case NORMAL_ACTION:
			action_type = EGG_TYPE_ACTION;
			break;
		case TOGGLE_ACTION:
			action_type = EGG_TYPE_TOGGLE_ACTION;
			break;
		case RADIO_ACTION:
			action_type = EGG_TYPE_RADIO_ACTION;
			break;
		case ENTRY_ACTION:
			action_type = EGG_TYPE_ENTRY_ACTION;
			break;
		case HISTORY_ACTION:
			action_type = EGG_TYPE_HISTORY_ACTION;
			break;
		default:
			g_warning("unsupported action type");
			action_type = EGG_TYPE_ACTION;
		}

		action = g_object_new(action_type,
				      "name", entries[i].name,
				      "label", _(entries[i].label),
				      "tooltip", _(entries[i].tooltip),
				      "stock_id", entries[i].stock_id,
				      NULL);

		if (entries[i].entry_type == RADIO_ACTION &&
		    entries[i].extra_data != NULL)
		{
			EggAction *radio_action;
			GSList *group;

			radio_action =
				egg_action_group_get_action(
					EGG_ACTION_GROUP(action_group),
					entries[i].extra_data);
			if (radio_action)
			{
				group = egg_radio_action_get_group(
					EGG_RADIO_ACTION(radio_action));
				egg_radio_action_set_group(
					EGG_RADIO_ACTION(action), group);
			}
			else
			{
				g_warning(G_STRLOC " could not look up `%s'",
					  entries[i].extra_data);
			}
		}

		egg_action_group_add_action(action_group, action);

		if (entries[i].callback)
		{
			guint accel_key = 0;
			GdkModifierType accel_mods = 0;
			gchar *accel_path;

			g_signal_connect(action, "activate",
					 entries[i].callback,
					 entries[i].user_data);

			/* set the accel path for the menu item */
			accel_path = g_strconcat("<Actions>/",
						 action_group->name,
						 "/",
						 entries[i].name,
						 NULL);

			if (entries[i].accelerator)
				gtk_accelerator_parse(entries[i].accelerator,
						      &accel_key,
						      &accel_mods);
			if (accel_key)
				gtk_accel_map_add_entry(accel_path, accel_key,
							accel_mods);

			if (action_group->accel_group)
				egg_action_set_accelerator(action,
							   action_group->accel_group,
							   accel_path);
			g_free(accel_path);
		}

		g_object_unref(action);
	}
}
