/* -*- c-file-style: "ruby"; indent-tabs-mode: nil -*- */
/************************************************

  rbgtkactiongroup.c -

  $Author: ggc $
  $Date: 2007/07/13 16:07:31 $

  Copyright (C) 2004-2006 Masao Mutoh
************************************************/

#include "global.h"

#if GTK_CHECK_VERSION(2,4,0)

static ID id_action_procs;
static ID id_toggle_action_procs;

#define _SELF(self) (GTK_ACTION_GROUP(RVAL2GOBJ(self)))
#define RVAL2WIDGET(w) (GTK_WIDGET(RVAL2GOBJ(w)))

static VALUE
actiongroup_initialize(VALUE self, VALUE name)
{
    G_INITIALIZE(self, gtk_action_group_new(RVAL2CSTR(name)));
    return Qnil;
}

/* Defined as properties
const gchar* gtk_action_group_get_name      (GtkActionGroup *action_group);
gboolean    gtk_action_group_get_sensitive  (GtkActionGroup *action_group);
void        gtk_action_group_set_sensitive  (GtkActionGroup *action_group,
                                             gboolean sensitive);
gboolean    gtk_action_group_get_visible    (GtkActionGroup *action_group);
void        gtk_action_group_set_visible    (GtkActionGroup *action_group,
                                             gboolean visible);
*/

static VALUE
actiongroup_get_action(VALUE self, VALUE action_name)
{
    return GOBJ2RVAL(gtk_action_group_get_action(_SELF(self), RVAL2CSTR(action_name)));
}

static VALUE
actiongroup_list_actions(VALUE self)
{
    return GLIST2ARYF(gtk_action_group_list_actions(_SELF(self)));
}

static VALUE
actiongroup_add_action(int argc, VALUE *argv, VALUE self)
{
    VALUE action, accelerator;

    rb_scan_args(argc, argv, "11", &action, &accelerator);

    gtk_action_group_add_action_with_accel(_SELF(self),
                                           GTK_ACTION(RVAL2GOBJ(action)),
                                           NIL_P(accelerator) ? NULL : RVAL2CSTR(accelerator));
    G_CHILD_ADD(self, action);

    return self;
}

static VALUE
actiongroup_remove_action(VALUE self, VALUE action)
{
    gtk_action_group_remove_action(_SELF(self), GTK_ACTION(RVAL2GOBJ(action)));
    G_CHILD_REMOVE(self, action);
    return self;
}

static void
activate_action(GtkAction *action, VALUE self)
{
    VALUE action_procs = rb_ivar_get(self, id_action_procs);
    VALUE proc = rb_hash_aref(action_procs, CSTR2RVAL(gtk_action_get_name(action)));
    if (! NIL_P(proc))
        rb_funcall(proc, id_call, 2, self, GOBJ2RVAL(action));
}

static VALUE
actiongroup_add_actions(VALUE self, VALUE entries)
{
    int i;
    VALUE action_procs;
    guint n_entries = (guint)RARRAY_LEN(entries);
    GtkActionEntry* gentries = g_new0(GtkActionEntry, n_entries);

    if (rb_ivar_defined(self, id_action_procs) == Qtrue){
        action_procs = rb_ivar_get(self, id_action_procs);
    } else {
        action_procs = rb_hash_new();
    }

    for (i = 0; i < n_entries; i++){
        VALUE entry;
        int size;

	entry = RARRAY_PTR(entries)[i];
	size = RARRAY_LEN(entry);
        if (size < 1)
            rb_raise(rb_eArgError, "wrong array parameter");

        gentries[i].name = RVAL2CSTR_ACCEPT_NIL(RARRAY_PTR(entry)[0]);
        gentries[i].callback = G_CALLBACK(activate_action);

        if (size < 2) continue;

        if (NIL_P(RARRAY_PTR(entry)[1])){
            gentries[i].stock_id = NULL;
        } else if (SYMBOL_P(RARRAY_PTR(entry)[1])){
            gentries[i].stock_id = rb_id2name(SYM2ID(RARRAY_PTR(entry)[1]));
        } else if (TYPE(RARRAY_PTR(entry)[1]) == T_STRING){
            gentries[i].stock_id = RVAL2CSTR(RARRAY_PTR(entry)[1]);
        } else{
            rb_raise(rb_eArgError,
		     "invalid argument %s (expect Symbol or String)",
                     rb_class2name(CLASS_OF(RARRAY_PTR(entry)[1])));
        }

        if (size < 3) continue;
        gentries[i].label = RVAL2CSTR_ACCEPT_NIL(RARRAY_PTR(entry)[2]);
        if (size < 4) continue;
        gentries[i].accelerator = RVAL2CSTR_ACCEPT_NIL(RARRAY_PTR(entry)[3]);
        if (size < 4) continue;
        gentries[i].tooltip = RVAL2CSTR_ACCEPT_NIL(RARRAY_PTR(entry)[4]);
        if (size < 5) continue;
        rb_hash_aset(action_procs, RARRAY_PTR(entry)[0], RARRAY_PTR(entry)[5]);
    }
    rb_ivar_set(self, id_action_procs, action_procs);
    gtk_action_group_add_actions(_SELF(self), gentries, n_entries,
				 (gpointer)self);
    g_free(gentries);

    return self;
}

static void
activate_toggle_action(GtkAction *action, VALUE self)
{
    VALUE action_procs = rb_ivar_get(self, id_toggle_action_procs);
    VALUE proc = rb_hash_aref(action_procs, CSTR2RVAL(gtk_action_get_name(action)));
    if (! NIL_P(proc))
        rb_funcall(proc, id_call, 2, self, GOBJ2RVAL(action));
}

static VALUE
actiongroup_add_toggle_actions(VALUE self, VALUE entries)
{
    int i;
    VALUE toggle_action_procs;
    guint n_entries = (guint)RARRAY_LEN(entries);
    GtkToggleActionEntry* gentries = g_new0(GtkToggleActionEntry, n_entries);

    if (rb_ivar_defined(self, id_toggle_action_procs) == Qtrue){
        toggle_action_procs = rb_ivar_get(self, id_toggle_action_procs);
    } else {
        toggle_action_procs = rb_hash_new();
    }

    for (i = 0; i < n_entries; i++){
        VALUE entry;
	int size;

	entry = RARRAY_PTR(entries)[i];
	size = RARRAY_LEN(entry);
        if (size < 1)
            rb_raise(rb_eArgError, "wrong array parameter");

        gentries[i].name = RVAL2CSTR_ACCEPT_NIL(RARRAY_PTR(entry)[0]);
        gentries[i].callback = G_CALLBACK(activate_toggle_action);

        if (size < 2) continue;

        if (NIL_P(RARRAY_PTR(entry)[1])){
            gentries[i].stock_id = NULL;
        } else if (SYMBOL_P(RARRAY_PTR(entry)[1])){
            gentries[i].stock_id = rb_id2name(SYM2ID(RARRAY_PTR(entry)[1]));
        } else if (TYPE(RARRAY_PTR(entry)[1]) == T_STRING){
            gentries[i].stock_id = RVAL2CSTR(RARRAY_PTR(entry)[1]);
        } else{
            rb_raise(rb_eArgError, "invalid argument %s (expect Symbol or String)",
                     rb_class2name(CLASS_OF(RARRAY_PTR(entry)[1])));
        }

        if (size < 3) continue;
        gentries[i].label = RVAL2CSTR_ACCEPT_NIL(RARRAY_PTR(entry)[2]);
        if (size < 4) continue;
        gentries[i].accelerator = RVAL2CSTR_ACCEPT_NIL(RARRAY_PTR(entry)[3]);
        if (size < 4) continue;
        gentries[i].tooltip = RVAL2CSTR_ACCEPT_NIL(RARRAY_PTR(entry)[4]);
        if (size < 5) continue;
        rb_hash_aset(toggle_action_procs,
		     RARRAY_PTR(entry)[0], RARRAY_PTR(entry)[5]);
        if (size < 6) continue;
        gentries[i].is_active = RVAL2CBOOL(RARRAY_PTR(entry)[6]);
    }
    rb_ivar_set(self, id_toggle_action_procs, toggle_action_procs);
    gtk_action_group_add_toggle_actions(_SELF(self), gentries, n_entries,
					(gpointer)self);
    g_free(gentries);

    return self;
}

static void
activate_radio_action(GtkAction *action, GtkRadioAction *current, VALUE func)
{
    if (! NIL_P(func))
        rb_funcall(func, id_call, 2, GOBJ2RVAL(action), GOBJ2RVAL(current));
}

static VALUE
actiongroup_add_radio_actions(int argc, VALUE *argv, VALUE self)
{
    VALUE entries, value, proc;
    int i;
    guint n_entries;
    GtkRadioActionEntry* gentries;

    rb_scan_args(argc, argv, "12", &entries, &value, &proc);

    if (NIL_P(value)) value = -1;
    if (NIL_P(proc) && rb_block_given_p()){
        proc = rb_block_proc();
        G_RELATIVE(self, proc);
    }
    n_entries = (guint)RARRAY_LEN(entries);
    gentries = g_new0(GtkRadioActionEntry, n_entries);

    for (i = 0; i < n_entries; i++){
        VALUE entry;
	int size;

	entry = RARRAY_PTR(entries)[i];
	size = RARRAY_LEN(entry);
        if (size < 1)
            rb_raise(rb_eArgError, "wrong array parameter");

        gentries[i].name = RVAL2CSTR_ACCEPT_NIL(RARRAY_PTR(entry)[0]);

        if (size < 2) continue;

        if (NIL_P(RARRAY_PTR(entry)[1])){
            gentries[i].stock_id = NULL;
        } else if (SYMBOL_P(RARRAY_PTR(entry)[1])){
            gentries[i].stock_id = rb_id2name(SYM2ID(RARRAY_PTR(entry)[1]));
        } else if (TYPE(RARRAY_PTR(entry)[1]) == T_STRING){
            gentries[i].stock_id = RVAL2CSTR(RARRAY_PTR(entry)[1]);
        } else{
            rb_raise(rb_eArgError,
		     "invalid argument %s (expect Symbol or String)",
                     rb_class2name(CLASS_OF(RARRAY_PTR(entry)[1])));
        }

        if (size < 3) continue;
        gentries[i].label = RVAL2CSTR_ACCEPT_NIL(RARRAY_PTR(entry)[2]);
        if (size < 4) continue;
        gentries[i].accelerator = RVAL2CSTR_ACCEPT_NIL(RARRAY_PTR(entry)[3]);
        if (size < 4) continue;
        gentries[i].tooltip = RVAL2CSTR_ACCEPT_NIL(RARRAY_PTR(entry)[4]);
        if (size < 5) continue;
        gentries[i].value = NUM2INT(RARRAY_PTR(entry)[5]);
    }
    gtk_action_group_add_radio_actions(_SELF(self), gentries,
				       n_entries, NUM2INT(value),
                                       G_CALLBACK(activate_radio_action),
				       (gpointer)proc);
    g_free(gentries);

    return self;
}

static gchar*
translate_func(const gchar *path, gpointer func)
{
    VALUE ret = rb_funcall((VALUE)func, id_call, 1, CSTR2RVAL(path));
    return RVAL2CSTR(ret);
}

static VALUE
actiongroup_set_translate_func(VALUE self)
{
    VALUE func = rb_block_proc();
    G_RELATIVE(self, func);
    gtk_action_group_set_translate_func(_SELF(self), 
                                        (GtkTranslateFunc)translate_func, 
                                        (gpointer)func, NULL);
    return self;
}

static VALUE
actiongroup_set_translation_domain(VALUE self, VALUE domain)
{
    gtk_action_group_set_translation_domain(_SELF(self), 
                                            NIL_P(domain) ? (const gchar*)NULL : RVAL2CSTR(domain));
    return self;
}

#if GTK_CHECK_VERSION(2,6,0)
static VALUE
actiongroup_translate_string(VALUE self, VALUE str)
{
    return CSTR2RVAL(gtk_action_group_translate_string(_SELF(self), RVAL2CSTR(str)));
}
#endif

static void
action_group_mark(void *p)
{
    GtkActionGroup *group;
    GList *actions, *node;

    group = GTK_ACTION_GROUP(p);
    actions = gtk_action_group_list_actions(group);
    for (node = actions; node; node = g_list_next(node)) {
	GtkWidget *action = node->data;
	rbgobj_gc_mark_instance(action);
    }
    g_list_free(actions);
}
#endif

void 
Init_gtk_actiongroup()
{
#if GTK_CHECK_VERSION(2,4,0)
    VALUE gActionGroup;

    gActionGroup = G_DEF_CLASS_WITH_GC_FUNC(GTK_TYPE_ACTION_GROUP, "ActionGroup",
					    mGtk, action_group_mark, NULL);

    id_action_procs = rb_intern("@action_procs");
    id_toggle_action_procs = rb_intern("@toggle_action_procs");

    rb_define_method(gActionGroup, "initialize", actiongroup_initialize, 1);
    rb_define_method(gActionGroup, "get_action", actiongroup_get_action, 1);
    rb_define_method(gActionGroup, "actions", actiongroup_list_actions, 0);
    rb_define_method(gActionGroup, "add_action", actiongroup_add_action, -1);
    rb_define_method(gActionGroup, "remove_action", actiongroup_remove_action, 1);
    rb_define_method(gActionGroup, "add_actions", actiongroup_add_actions, 1);
    rb_define_method(gActionGroup, "add_toggle_actions", actiongroup_add_toggle_actions, 1);
    rb_define_method(gActionGroup, "add_radio_actions", actiongroup_add_radio_actions, -1);
    rb_define_method(gActionGroup, "set_translate_func", actiongroup_set_translate_func, 0);
    rb_define_method(gActionGroup, "set_translation_domain", actiongroup_set_translation_domain, 1);
#if GTK_CHECK_VERSION(2,6,0)
    rb_define_method(gActionGroup, "translate_string", actiongroup_translate_string, 1);
#endif
    G_DEF_SETTERS(gActionGroup);
#endif
}
