The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
/*
 * Copyright (c) 2003-2005 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 Library 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 GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library 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.
 *
 * $Id$
 */

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


/*
 * yet another special case that isn't appropriate for either
 * GPerlClosure or GPerlCallback --- the menu position function has
 * mostly output parameters, so we need to change the callbacks's
 * signature for perl, getting multiple return values from the stack.
 * this one's easy, though.
 */

/* this is public so that other extensions which use GtkMenuPosFunc (e.g.
 * libgnomeui) don't need to reimplement it. */
void
gtk2perl_menu_position_func (GtkMenu * menu,
                             gint * x,
                             gint * y,
                             gboolean * push_in,
                             GPerlCallback * callback)
{
	int n;
	dGPERL_CALLBACK_MARSHAL_SP;

	GPERL_CALLBACK_MARSHAL_INIT (callback);

	ENTER;
	SAVETMPS;

	PUSHMARK (SP);

	EXTEND (SP, 3);
	PUSHs (sv_2mortal (newSVGtkMenu (menu)));
	PUSHs (sv_2mortal (newSViv (*x)));
	PUSHs (sv_2mortal (newSViv (*y)));
	if (callback->data)
		XPUSHs (sv_2mortal (newSVsv (callback->data)));

	PUTBACK;

	n = call_sv (callback->func, G_ARRAY);

	SPAGAIN;

	if (n < 2 || n > 3)
		croak ("menu position callback must return two integers "
		       "(x, and y) or two integers and a boolean (x, y, and "
		       "push_in)");

	/* POPs and POPi take things off the *end* of the stack! */
	if (n > 2) {
		SV *sv = POPs;
		*push_in = sv_2bool (sv);
	}
	if (n > 1) *y = POPi;
	if (n > 0) *x = POPi;

	PUTBACK;
	FREETMPS;
	LEAVE;
}

static GPerlCallback *
gtk2perl_menu_detach_func_create (SV *func, SV *data)
{
	GType param_types [2];
	param_types[0] = GTK_TYPE_WIDGET;
	param_types[1] = GTK_TYPE_MENU;
	return gperl_callback_new (func, data, G_N_ELEMENTS (param_types),
				   param_types, 0);
}

static void
gtk2perl_menu_detach_func (GtkWidget *attach_widget,
                           GtkMenu *menu)
{
	GPerlCallback *callback;

	callback = g_object_get_data (G_OBJECT (attach_widget),
	                              "__gtk2perl_menu_detach_func__");

	if (callback)
		gperl_callback_invoke (callback, NULL, attach_widget, menu);
}

MODULE = Gtk2::Menu	PACKAGE = Gtk2::Menu	PREFIX = gtk_menu_

GtkWidget*
gtk_menu_new (class)
    C_ARGS:
	/* void */

void
gtk_menu_popup (menu, parent_menu_shell, parent_menu_item, menu_pos_func, data, button, activate_time)
	GtkMenu	* menu
	GtkWidget_ornull * parent_menu_shell
	GtkWidget_ornull * parent_menu_item
	SV * menu_pos_func
	SV * data
	guint button
	guint activate_time
	###guint32 activate_time
    CODE:
	if (!gperl_sv_is_defined (menu_pos_func)) {
		gtk_menu_popup (menu, parent_menu_shell, parent_menu_item,
		                NULL, NULL, button, activate_time);
	} else {
		GPerlCallback * callback;
		/* we don't need to worry about the callback arg types since
		 * we already have to marshall this callback ourselves. */
		callback = gperl_callback_new (menu_pos_func, data, 0, NULL, 0);
		gtk_menu_popup (menu, parent_menu_shell, parent_menu_item,
		        (GtkMenuPositionFunc) gtk2perl_menu_position_func,
			callback, button, activate_time);
		/* The menu will store the callback we give it, and can
		 * conceivably invoke the callback multiple times
		 * (repositioning, changing screens, etc).  Each call to
		 * gtk_menu_popup() replaces the function pointer.  So,
		 * if we use a weak reference, we can leak multiple callbacks;
		 * if we use object data, we can clean up the ones we install
		 * and reinstall.  Not likely, of course, but there are
		 * pathological programmers out there. */
		g_object_set_data_full (G_OBJECT (menu), "_menu_pos_callback",
		                        callback,
		                        (GDestroyNotify)
		                             gperl_callback_destroy);
	}

void
gtk_menu_reposition (menu)
	GtkMenu	* menu

void
gtk_menu_popdown (menu)
	GtkMenu *menu

GtkWidget *
gtk_menu_get_active (menu)
	GtkMenu *menu

void
gtk_menu_set_active (menu, index)
	GtkMenu *menu
	guint index

void
gtk_menu_set_accel_group (menu, accel_group)
	GtkMenu	* menu
	GtkAccelGroup * accel_group

GtkAccelGroup*
gtk_menu_get_accel_group (menu)
	GtkMenu *menu

void
gtk_menu_set_accel_path (menu, accel_path)
	GtkMenu *menu
	const gchar *accel_path

void
gtk_menu_attach_to_widget (menu, attach_widget, detacher)
	GtkMenu *menu
	GtkWidget *attach_widget
	SV *detacher
    PREINIT:
	GPerlCallback *callback;
    CODE:
	callback = gtk2perl_menu_detach_func_create (detacher, NULL);

	g_object_set_data_full (G_OBJECT (attach_widget),
	                        "__gtk2perl_menu_detach_func__",
			        callback,
	                        (GDestroyNotify) gperl_callback_destroy);

	gtk_menu_attach_to_widget (menu,
	                           attach_widget,
	                           gtk2perl_menu_detach_func);

void
gtk_menu_detach (menu)
	GtkMenu *menu

GtkWidget *
gtk_menu_get_attach_widget (menu)
	GtkMenu	* menu

void
gtk_menu_set_tearoff_state (menu, torn_off)
	GtkMenu *menu
	gboolean torn_off

gboolean
gtk_menu_get_tearoff_state (menu)
	GtkMenu *menu

void
gtk_menu_set_title (menu, title)
	GtkMenu * menu
	const gchar * title

 ## void gtk_menu_reorder_child (GtkMenu *menu, GtkWidget *child, gint position)
void
gtk_menu_reorder_child (menu, child, position)
	GtkMenu *menu
	GtkWidget *child
	gint position

##gchar * gtk_menu_get_title (GtkMenu *menu)
const gchar *
gtk_menu_get_title (menu)
	GtkMenu * menu


#if GTK_CHECK_VERSION(2,2,0)

##void gtk_menu_set_screen (GtkMenu *menu, GdkScreen *screen)
void
gtk_menu_set_screen (menu, screen)
	GtkMenu   * menu
	GdkScreen * screen

#endif

#if GTK_CHECK_VERSION(2,4,0)

void gtk_menu_attach (GtkMenu *menu, GtkWidget *child, guint left_attach, guint right_attach, guint top_attach, guint bottom_attach);

void gtk_menu_set_monitor (GtkMenu *menu, gint monitor_num);

#endif

#if GTK_CHECK_VERSION(2,6,0)

##  GList* gtk_menu_get_for_attach_widget (GtkWidget *widget);
void
gtk_menu_get_for_attach_widget (class, widget)
	GtkWidget *widget
    PREINIT:
	GList *list, *i;
    PPCODE:
	list = gtk_menu_get_for_attach_widget (widget);
	for (i = list; i; i = i->next)
		XPUSHs (sv_2mortal (newSVGtkMenu (i->data)));

#endif

#if GTK_CHECK_VERSION (2, 14, 0)

const gchar* gtk_menu_get_accel_path (GtkMenu *menu);

gint gtk_menu_get_monitor (GtkMenu *menu);

#endif /* 2.14 */