The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
/*
 * Copyright (C) 2003 by the gtk2-perl team (see the file AUTHORS)
 * 
 * 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.1 of the License, 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 LICENSE file in the top level of this distribution
 * for the complete license terms.
 *
 */

#include "gnome2perl.h"
#include <gperl_marshal.h>

static void
gnome2perl_popup_menu_activate_func (GtkObject *object,
                                     gpointer data,
                                     GtkWidget *for_widget)
{
	/* We stored the SV in the widget.  Get it. */
	SV *callback = g_object_get_data (G_OBJECT (object), "gnome2perl_popup_menu_callback");

	if (callback) {
		dGPERL_CALLBACK_MARSHAL_SP;
#ifdef PERL_IMPLICIT_CONTEXT
		PERL_SET_CONTEXT (callback);
		SPAGAIN;
#endif

		ENTER;
		SAVETMPS;

		PUSHMARK (SP);

		EXTEND (SP, 3);
		PUSHs (sv_2mortal (newSVGtkObject (object)));
		PUSHs (sv_2mortal (newSVsv (data)));
		PUSHs (sv_2mortal (newSVGtkWidget (for_widget)));

		PUTBACK;

		call_sv (callback, G_DISCARD);

		FREETMPS;
		LEAVE;
	}
}

static void
gnome2perl_popup_menu_activate_func_destroy (SV *callback)
{
	/* FIXME: Any idea how to destroy the callback?
	SvREFCNT_dec (callback); */
}

/* ------------------------------------------------------------------------- */

/*
 * this was originally SvGnomeUIInfo in Gtk-Perl-0.7008/Gnome/xs/Gnome.xs
 */
void
gnome2perl_parse_uiinfo_sv (SV * sv,
                            GnomeUIInfo * info)
{
	g_assert (sv != NULL);
	g_assert (info != NULL);

	if (!SvOK (sv))
		return; /* fail silently if undef */
	if ((!SvRV (sv)) ||
	    (SvTYPE (SvRV (sv)) != SVt_PVHV && SvTYPE (SvRV (sv)) != SVt_PVAV))
		croak ("GnomeUIInfo must be a hash or array reference");

	if (SvTYPE (SvRV (sv)) == SVt_PVHV) {
		HV *h = (HV*) SvRV (sv);
		SV **s;
		if ((s = hv_fetch (h, "type", 4, 0)) && SvOK (*s))
			info->type = SvGnomeUIInfoType(*s);
		if ((s = hv_fetch (h, "label", 5, 0)) && SvOK (*s))
			info->label = SvGChar (*s);
		if ((s = hv_fetch (h, "hint", 4, 0)) && SvOK (*s))
			info->hint = SvGChar (*s);
		if ((s = hv_fetch (h, "widget", 6, 0)) && SvOK (*s))
			info->widget = SvGtkWidget (*s);

		/* 'subtree' and 'callback' are also allowed - they
                   have the bonus that we know what you mean if you
                   use them */
		if ((s = hv_fetch(h, "moreinfo", 8, 0)) && SvOK(*s)) {
			info->moreinfo = *s;
		} else if ((s = hv_fetch(h, "subtree", 7, 0)) && SvOK(*s)) {
			if (info->type != GNOME_APP_UI_SUBTREE &&
			    info->type != GNOME_APP_UI_SUBTREE_STOCK)
				croak ("'subtree' argument specified, but "
				       "GnomeUIInfo type is not 'subtree'");
			info->moreinfo = *s;
		} else if ((s = hv_fetch(h, "callback", 8, 0)) && SvOK(*s)) {
			if ((info->type != GNOME_APP_UI_ITEM) &&
			    (info->type != GNOME_APP_UI_ITEM_CONFIGURABLE) &&
			    (info->type != GNOME_APP_UI_TOGGLEITEM))
				croak ("'callback' argument specified, but "
				       "GnomeUIInfo type is not an item type");
			info->moreinfo = *s;
		}

		if ((s = hv_fetch(h, "pixmap_type", 11, 0)) && SvOK(*s))
			info->pixmap_type = SvGnomeUIPixmapType(*s);
		if ((s = hv_fetch(h, "pixmap_info", 11, 0)) && SvOK(*s))
			/* stock ids have no non-ascii (i hope), and this should
			 * allow actual pixmap data through, too */
			info->pixmap_info = SvPV_nolen (*s);
		if ((s = hv_fetch(h, "accelerator_key", 15, 0)) && SvOK(*s)) /* keysym */
			info->accelerator_key = SvIV(*s);
		if ((s = hv_fetch(h, "ac_mods", 7, 0)) && SvOK(*s))
			info->ac_mods = SvGdkModifierType(*s);
	} else { /* As in Python - it's an array of:
		    type, label, hint, moreinfo, pixmap_type, pixmap_info,
		    accelerator_key, modifiers */
		AV *a = (AV*)SvRV (sv);
		SV **s;
		if ((s = av_fetch (a, 0, 0)) && SvOK (*s))
			info->type = SvGnomeUIInfoType (*s);
		if ((s = av_fetch (a, 1, 0)) && SvOK (*s))
			info->label = SvGChar (*s);
		if ((s = av_fetch (a, 2, 0)) && SvOK (*s))
			info->hint = SvGChar (*s);
		if ((s = av_fetch (a, 3, 0)) && SvOK (*s))
			info->moreinfo = *s;
		if ((s = av_fetch (a, 4, 0)) && SvOK (*s))
			info->pixmap_type = SvGnomeUIPixmapType (*s);
		if ((s = av_fetch (a, 5, 0)) && SvOK (*s))
			info->pixmap_info = SvPV_nolen (*s);
		if ((s = av_fetch (a, 6, 0)) && SvOK (*s)) /* keysym */
			info->accelerator_key = SvIV (*s);
		if ((s = av_fetch (a, 7, 0)) && SvOK (*s))
			info->ac_mods = SvGdkModifierType (*s);	  

#		define GNOME2PERL_WIDGET_ARRAY_INDEX 8

		if ((s = av_fetch (a, GNOME2PERL_WIDGET_ARRAY_INDEX, 0)) && SvOK (*s))
			info->widget = SvGtkWidget (*s);
	}

	/* Decide what to do with the moreinfo */
	switch (info->type) {
	    case GNOME_APP_UI_SUBTREE:
	    case GNOME_APP_UI_SUBTREE_STOCK:
	    case GNOME_APP_UI_RADIOITEMS:
		if (info->moreinfo == NULL)
			croak ("GnomeUIInfo type requires a 'moreinfo' or "
			       "'subtree' argument, but none was specified");
		/* Now we can recurse */
		/* Hope user_data doesn't get mangled... */
		info->user_data = info->moreinfo;
		info->moreinfo =
		  gnome2perl_svrv_to_uiinfo_tree (info->moreinfo,
		                                  "'subtree' or 'moreinfo'");
		break;

	    case GNOME_APP_UI_HELP:
		if (info->moreinfo == NULL)
			croak("GnomeUIInfo type requires a 'moreinfo' argument, "
			      "but none was specified");
		/* It's just a string */
		info->moreinfo = SvGChar ((SV*)info->moreinfo);
		break;

	    case GNOME_APP_UI_ITEM:
	    case GNOME_APP_UI_ITEM_CONFIGURABLE:
	    case GNOME_APP_UI_TOGGLEITEM:
		if (info->moreinfo) {
			/* We simply swap moreinfo and user_data here so that
			   the GnomePopupMenu functions don't see the SV but our
			   custom marshaller.  GnomeAppHelper isn't directly
			   affected since we use the GnomeUIBuilderData thingy
			   there anyway. */
			info->user_data = info->moreinfo;
			info->moreinfo = gnome2perl_popup_menu_activate_func;
		}
		break;

	    default:
		/* Do nothing */
		break;
	}
}

GnomeUIInfo *
gnome2perl_svrv_to_uiinfo_tree (SV* sv, char * name)
{
	AV * av;
	int i, count;
	GnomeUIInfo * infos;

	g_assert (sv != NULL);
	if ((!SvOK (sv)) || (!SvRV (sv)) || (SvTYPE (SvRV (sv)) != SVt_PVAV))
		croak ("%s must be a reference to an array of Gnome UI "
		       "Info Entries", name);

	av = (AV*)SvRV (sv);
	/* add one to turn from last index to length... */
	count = av_len (av) + 1;
	infos = gperl_alloc_temp (sizeof(GnomeUIInfo) * (count+1));
	for (i = 0; i < count; i++) {
		SV ** svp = av_fetch (av, i, FALSE);
		gnome2perl_parse_uiinfo_sv (*svp, infos + i);
	}
	/* and stick another one on the end of the array as the terminator */
	infos[count].type = GNOME_APP_UI_ENDOFINFO;
	
	return infos;
}


GnomeUIInfo *
SvGnomeUIInfo (SV * sv)
{
	return gnome2perl_svrv_to_uiinfo_tree (sv, "variable");
}

static void
gnome2perl_refill_info_common (SV *data, GnomeUIInfo *info)
{
	if (info->widget) {
		if (SvTYPE(SvRV(data)) == SVt_PVHV) {
			hv_store ((HV*)SvRV(data), "widget", 6,
			          newSVGtkWidget (info->widget), FALSE);
		} else {
			av_store ((AV*)SvRV(data), GNOME2PERL_WIDGET_ARRAY_INDEX,
			          newSVGtkWidget (info->widget));
		}
	}
}


static void
gnome2perl_refill_info (SV *data, GnomeUIInfo *info)
{
	gnome2perl_refill_info_common (data, info);

	switch (info->type) {
	    case GNOME_APP_UI_SUBTREE:
	    case GNOME_APP_UI_SUBTREE_STOCK:
	    case GNOME_APP_UI_RADIOITEMS:
		/* in gnome2perl_parse_uiinfo_sv, we stashed a pointer to
		 * the SV reference to the subtree array in info->user_data.
		 * it should still be there, provided there's no mangling
		 * in the library. */
		gnome2perl_refill_infos (info->user_data, info->moreinfo);
		break;

	    default:
		break;
	}
}

static void
gnome2perl_refill_info_popup (SV *data, GnomeUIInfo *info)
{
	gnome2perl_refill_info_common (data, info);

	switch (info->type) {
	    case GNOME_APP_UI_SUBTREE:
	    case GNOME_APP_UI_SUBTREE_STOCK:
	    case GNOME_APP_UI_RADIOITEMS:
		/* in gnome2perl_parse_uiinfo_sv, we stashed a pointer to
		 * the SV reference to the subtree array in info->user_data.
		 * it should still be there, provided there's no mangling
		 * in the library. */
		gnome2perl_refill_infos_popup (info->user_data, info->moreinfo);
		break;

	    case GNOME_APP_UI_ITEM:
	    case GNOME_APP_UI_ITEM_CONFIGURABLE:
	    case GNOME_APP_UI_TOGGLEITEM:
		/* user_data contains the SV.  Store it in the widget so that
		   the custom marshaller can recover and call it. */
		if (info->user_data)
			g_object_set_data_full (
			  G_OBJECT (info->widget),
			  "gnome2perl_popup_menu_callback",
			  info->user_data,
			  (GDestroyNotify)
			    gnome2perl_popup_menu_activate_func_destroy);
		break;

	    default:
		break;
	}
}

void
gnome2perl_refill_infos (SV *data, GnomeUIInfo *infos)
{
	int i, count;
	AV* a = (AV*)SvRV (data);
	count = av_len(a) + 1;
	for (i = 0; i < count; i++) {
		SV** s = av_fetch(a, i, 0);
		gnome2perl_refill_info (*s, infos + i);
	}
}

void
gnome2perl_refill_infos_popup (SV *data, GnomeUIInfo *infos)
{
	int i, count;
	AV* a = (AV*)SvRV (data);
	count = av_len(a) + 1;
	for (i = 0; i < count; i++) {
		SV** s = av_fetch(a, i, 0);
		gnome2perl_refill_info_popup (*s, infos + i);
	}
}

static void
gnome2perl_ui_signal_connect (GnomeUIInfo * uiinfo,
                              const char * signal_name,
                              GnomeUIBuilderData * uibdata)
{
	/* We're using user_data here instead of moreinfo because we swapped
	   them in gnome2perl_parse_uiinfo_sv. */
	if (uiinfo->user_data)
		gperl_signal_connect (newSVGObject (G_OBJECT (uiinfo->widget)),
		                      (char*) signal_name,
		                      uiinfo->user_data,
		                      NULL,
		                      G_SIGNAL_RUN_FIRST);
}

static GnomeUIBuilderData
ui_builder_data = {
	gnome2perl_ui_signal_connect,
	NULL,
	FALSE,
	NULL,
	(GtkDestroyNotify) gperl_callback_destroy
};

MODULE = Gnome2::AppHelper	PACKAGE = Gnome2	PREFIX = gnome_

=for object Gnome2::AppHelper

=for apidoc

=head1 GnomeUIInfo

In Gnome2 GnomeUIInfo's are often used as a convenient way to create GUI's.  In
Perl, GnomeUIInfo's are always references to arrays of items.  Items can either
be references to hashs or references to arrays:

=over

=item Hash Reference

When using hash references, items are specified by giving key-value pairs.  A
typical example:

  { type => "item", label => "Quit", callback => sub { exit(0); } }

For the list of valid keys, see below.

=item Array References

When using array references, items are a list of the following keys, in this
order:

  type,
  label,
  hint,
  moreinfo,
  pixmap_type,
  pixmap_info,
  accelerator_key and
  modifiers.

The example from above would become:

  [ "item", "Item", undef, sub { exit(0); },
    undef, undef, undef, undef ]

=back

To create multi-level structures, you use the "subtree" type and the "subtree"
key, as in the following example:

  {
    type => "subtree",
    label => "Radio Items",
    subtree => [
      {
        type => "radioitems",
        moreinfo => [
          {
            type => "item",
            label => "A"
          },
          {
            type => "item",
            label => "B"
          },
          {
            type => "item",
            label => "C"
          },
          {
            type => "item",
            label => "D"
          },
          {
            type => "item",
            label => "E"
          }
        ]
      }
    ]
  }

=cut

## void gnome_accelerators_sync (void) 
void
gnome_accelerators_sync (class)
    C_ARGS:
	/*void*/

MODULE = Gnome2::AppHelper	PACKAGE = Gtk2::MenuShell	PREFIX = gnome_app_

=for object Gnome2::AppHelper
=cut

### void gnome_app_fill_menu (GtkMenuShell *menu_shell, GnomeUIInfo *uiinfo, GtkAccelGroup *accel_group, gboolean uline_accels, gint pos) 
### void gnome_app_fill_menu_with_data (GtkMenuShell *menu_shell, GnomeUIInfo *uiinfo, GtkAccelGroup *accel_group, gboolean uline_accels, gint pos, gpointer user_data) 
### void gnome_app_fill_menu_custom (GtkMenuShell *menu_shell, GnomeUIInfo *uiinfo, GnomeUIBuilderData *uibdata, GtkAccelGroup *accel_group, gboolean uline_accels, gint pos) 
void
gnome_app_fill_menu (menu_shell, uiinfo, accel_group, uline_accels, pos)
	GtkMenuShell *menu_shell
	GnomeUIInfo *uiinfo
	GtkAccelGroup *accel_group
	gboolean uline_accels
	gint pos
    CODE:
	gnome_app_fill_menu_custom (menu_shell, uiinfo, &ui_builder_data, accel_group, uline_accels, pos);
	gnome2perl_refill_infos (ST (1), uiinfo);

=for apidoc

Returns the GtkWidget and the position associated with the path.

=cut
##  GtkWidget *gnome_app_find_menu_pos (GtkWidget *parent, const gchar *path, gint *pos)
void
gnome_app_find_menu_pos (parent, path)
	GtkWidget *parent
	const gchar *path
    PREINIT:
	gint pos;
	GtkWidget *widget;
    PPCODE:
	EXTEND (sp, 2);
	widget = gnome_app_find_menu_pos (parent, path, &pos);
	PUSHs (sv_2mortal (newSVGtkWidget (widget)));
	PUSHs (sv_2mortal (newSViv (pos)));

MODULE = Gnome2::AppHelper	PACKAGE = Gtk2::Toolbar	PREFIX = gnome_app_

=for object Gnome2::AppHelper
=cut

## void gnome_app_fill_toolbar (GtkToolbar *toolbar, GnomeUIInfo *uiinfo, GtkAccelGroup *accel_group) 
### void gnome_app_fill_toolbar_with_data (GtkToolbar *toolbar, GnomeUIInfo *uiinfo, GtkAccelGroup *accel_group, gpointer user_data) 
### void gnome_app_fill_toolbar_custom (GtkToolbar *toolbar, GnomeUIInfo *uiinfo, GnomeUIBuilderData *uibdata, GtkAccelGroup *accel_group) 
void
gnome_app_fill_toolbar (toolbar, uiinfo, accel_group)
	GtkToolbar *toolbar
	GnomeUIInfo *uiinfo
	GtkAccelGroup *accel_group
    CODE:
	gnome_app_fill_toolbar_custom (toolbar, uiinfo, &ui_builder_data, accel_group);
	gnome2perl_refill_infos (ST (1), uiinfo);

MODULE = Gnome2::AppHelper	PACKAGE = Gnome2::App	PREFIX = gnome_app_

#### void gnome_app_ui_configure_configurable (GnomeUIInfo* uiinfo) 
##void
##gnome_app_ui_configure_configurable (uiinfo)
##	GnomeUIInfo* uiinfo

## void gnome_app_create_menus (GnomeApp *app, GnomeUIInfo *uiinfo) 
### void gnome_app_create_menus_interp (GnomeApp *app, GnomeUIInfo *uiinfo, GtkCallbackMarshal relay_func, gpointer data, GtkDestroyNotify destroy_func) 
### void gnome_app_create_menus_with_data (GnomeApp *app, GnomeUIInfo *uiinfo, gpointer user_data) 
### void gnome_app_create_menus_custom (GnomeApp *app, GnomeUIInfo *uiinfo, GnomeUIBuilderData *uibdata) 
## void gnome_app_create_toolbar (GnomeApp *app, GnomeUIInfo *uiinfo) 
### void gnome_app_create_toolbar_interp (GnomeApp *app, GnomeUIInfo *uiinfo, GtkCallbackMarshal relay_func, gpointer data, GtkDestroyNotify destroy_func) 
### void gnome_app_create_toolbar_with_data (GnomeApp *app, GnomeUIInfo *uiinfo, gpointer user_data) 
### void gnome_app_create_toolbar_custom (GnomeApp *app, GnomeUIInfo *uiinfo, GnomeUIBuilderData *uibdata) 
void
gnome_app_create_menus (app, uiinfo)
	GnomeApp *app
	GnomeUIInfo *uiinfo
    ALIAS:
	create_toolbar = 1
    CODE:
	if (ix == 0)
		gnome_app_create_menus_custom (app, uiinfo, &ui_builder_data);
	else
		gnome_app_create_toolbar_custom (app, uiinfo, &ui_builder_data);

	gnome2perl_refill_infos (ST (1), uiinfo);

## void gnome_app_insert_menus (GnomeApp *app, const gchar *path, GnomeUIInfo *menuinfo) 
### void gnome_app_insert_menus_custom (GnomeApp *app, const gchar *path, GnomeUIInfo *uiinfo, GnomeUIBuilderData *uibdata) 
### void gnome_app_insert_menus_with_data (GnomeApp *app, const gchar *path, GnomeUIInfo *menuinfo, gpointer data) 
### void gnome_app_insert_menus_interp (GnomeApp *app, const gchar *path, GnomeUIInfo *menuinfo, GtkCallbackMarshal relay_func, gpointer data, GtkDestroyNotify destroy_func) 
void
gnome_app_insert_menus (app, path, menuinfo)
	GnomeApp *app
	const gchar *path
	GnomeUIInfo *menuinfo
    CODE:
	gnome_app_insert_menus_custom (app, path, menuinfo, &ui_builder_data);
	gnome2perl_refill_infos (ST (2), menuinfo);
	

## void gnome_app_remove_menus (GnomeApp *app, const gchar *path, gint items) 
void
gnome_app_remove_menus (app, path, items)
	GnomeApp *app
	const gchar *path
	gint items

## void gnome_app_remove_menu_range (GnomeApp *app, const gchar *path, gint start, gint items) 
void
gnome_app_remove_menu_range (app, path, start, items)
	GnomeApp *app
	const gchar *path
	gint start
	gint items

## void gnome_app_install_menu_hints (GnomeApp *app, GnomeUIInfo *uiinfo) 
void
gnome_app_install_menu_hints (app, uiinfo)
	GnomeApp *app
	GnomeUIInfo *uiinfo

## void gnome_app_setup_toolbar (GtkToolbar *toolbar, BonoboDockItem *dock_item) 
void
gnome_app_setup_toolbar (class, toolbar, dock_item)
	GtkToolbar *toolbar
	BonoboDockItem *dock_item
    C_ARGS:
	toolbar, dock_item

MODULE = Gnome2::AppHelper	PACKAGE = Gnome2::AppBar	PREFIX = gnome_app_

## void gnome_app_install_appbar_menu_hints (GnomeAppBar* appbar, GnomeUIInfo* uiinfo) 
void
gnome_app_install_menu_hints (appbar, uiinfo)
	GnomeAppBar* appbar
	GnomeUIInfo* uiinfo
    CODE:
	gnome_app_install_appbar_menu_hints (appbar, uiinfo);

MODULE = Gnome2::AppHelper	PACKAGE = Gtk2::Statusbar	PREFIX = gnome_app_

=for object Gnome2::AppHelper
=cut

## void gnome_app_install_statusbar_menu_hints (GtkStatusbar* bar, GnomeUIInfo* uiinfo) 
void
gnome_app_install_menu_hints (bar, uiinfo)
	GtkStatusbar* bar
	GnomeUIInfo* uiinfo
    CODE:
	gnome_app_install_statusbar_menu_hints (bar, uiinfo);