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 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>

/*
GtkEditable's insert-text signal uses an integer pointer as a write-through
parameter; unlike GtkWidget's size-request signal, we can't just pass an
editable object, because an integer is an integral type.

   void user_function  (GtkEditable *editable,
                        gchar *new_text,
                        gint new_text_length,
                        gint *position,  <<=== that's the problem
                        gpointer user_data);
*/
static void
gtk2perl_editable_insert_text_marshal (GClosure * closure,
                                       GValue * return_value,
                                       guint n_param_values,
                                       const GValue * param_values,
                                       gpointer invocation_hint,
                                       gpointer marshal_data)
{
	STRLEN len;
	gint * position_p;
	SV * string, * position;
	dGPERL_CLOSURE_MARSHAL_ARGS;

	GPERL_CLOSURE_MARSHAL_INIT (closure, marshal_data);

	PERL_UNUSED_VAR (return_value);
	PERL_UNUSED_VAR (n_param_values);
	PERL_UNUSED_VAR (invocation_hint);

	ENTER;
	SAVETMPS;

	PUSHMARK (SP);

	GPERL_CLOSURE_MARSHAL_PUSH_INSTANCE (param_values);

	/* string and position are cleaned up manually further down, so they
	 * don't need sv_2mortal. */

	/* new_text */
	string = newSVGChar (g_value_get_string (param_values+1));
	XPUSHs (string);

	/* text length is redundant, but documented.  it doesn't hurt
	 * anything to include it, but would be a doc hassle to omit it. */
	XPUSHs (sv_2mortal (newSViv (g_value_get_int (param_values+2))));

	/* insert position */
	position_p = g_value_get_pointer (param_values+3);
	position = newSViv (*position_p);
	XPUSHs (position);

	GPERL_CLOSURE_MARSHAL_PUSH_DATA;

	PUTBACK;

	GPERL_CLOSURE_MARSHAL_CALL (G_ARRAY);

	/* refresh the param_values with whatever changes the callback may
	 * have made.  values returned on the stack take precedence over
	 * modifications to @_. */
	if (count == 2) {
		SV * sv;
		/* get the values off the end of the stack.  why do my
		 * attempts to use ST() result in segfaults? */
		*position_p = POPi;
		sv = POPs;
		sv_utf8_upgrade (sv);
		g_value_set_string ((GValue*)param_values+1, SvPV (sv, len));
		g_value_set_int ((GValue*)param_values+2, len);
		PUTBACK;

	} else if (count == 0) {
		/* returned no values, then refresh string and position
		 * params from the callback's args, which may have been
		 * modified. */
		sv_utf8_upgrade (string);
		g_value_set_string ((GValue*)param_values+1,
		                    SvPV (string, len));
		g_value_set_int ((GValue*)param_values+2, len);
		*position_p = SvIV (position);
		if (*position_p < 0)
			*position_p = 0;

	} else {
		/* NOTE: croaking here can cause bad things to happen to the
		 * app, because croaking in signal handlers is bad juju. */
		croak ("an insert-text signal handler must either return"
		       " two items (text and position)\nor return no items"
		       " and possibly modify its @_ parameters.\n"
		       "  callback returned %d items", count);
	}

	/*
	 * clean up
	 */
	SvREFCNT_dec (string);
	SvREFCNT_dec (position);

	PUTBACK;
	FREETMPS;
	LEAVE;
}


MODULE = Gtk2::Editable	PACKAGE = Gtk2::Editable	PREFIX = gtk_editable_

=for position post_signals

The C<insert-text> signal handler can optionally alter the text to be
inserted.  It may

=over 4

=item

Return no values for no change.  Be sure to end with an empty
C<return>.

    sub my_insert_text_handler {
      my ($widget, $text, $len, $pos, $userdata) = @_;
      print "inserting '$text' at char position '$pos'\n";
      return;  # no values
    }

=item

Return two values C<($text, $pos)> which are the new text and
character position.

    sub my_insert_text_handler {
      my ($widget, $text, $len, $pos, $userdata) = @_;
      return (uc($text), $pos);  # force to upper case
    }

=item

Return no values and modify the text in C<$_[1]> and/or position in
C<$_[3]>.  For example,

    sub my_insert_text_handler {
      $_[1] = uc($_[1]);   # force to upper case
      $_[3] = 0;           # force position to the start
      return;  # no values
    }

=back

Note that currently in a Perl subclass of a C<Gtk2::Editable> widget,
a class closure (ie. class default signal handler) for C<insert-text>
does not work this way.  It instead sees the C level C<($text, $len,
$pos_pointer)>, where C<$pos_pointer> is a machine address and cannot
be used easily.  Hopefully this will change in the future.
A C<signal_chain_from_overridden> with the args as passed works, but
for anything else the suggestion is to use a C<signal_connect>
instead.

=cut

BOOT:
	gperl_signal_set_marshaller_for (GTK_TYPE_EDITABLE, "insert_text",
	                                 gtk2perl_editable_insert_text_marshal);

void
gtk_editable_select_region (editable, start, end)
	GtkEditable *editable
	gint start
	gint end


 ## returns an empty list if there is no selection
=for apidoc
=for signature (start, end) = $editable->get_selection_bounds
Returns integers, start and end.
=cut
void
gtk_editable_get_selection_bounds (editable)
	GtkEditable *editable
    PREINIT:
	gint start;
	gint end;
    PPCODE:
	if (!gtk_editable_get_selection_bounds (editable, &start, &end))
		XSRETURN_EMPTY;
	EXTEND (SP, 2);
	PUSHs (sv_2mortal (newSViv (start)));
	PUSHs (sv_2mortal (newSViv (end)));

=for apidoc
=for signature new_position = $editable->insert_text (new_text, position)
=cut
 ## returns position of next char after inserted text
gint
gtk_editable_insert_text (editable, new_text, ...)
	GtkEditable *editable
	gchar *new_text
    PREINIT:
	gint new_text_length;
	gint position;
    CODE:
	if (items == 3) {
		new_text_length = strlen (new_text);
		position = SvIV (ST (2));
	} else if (items == 4) {
		new_text_length = SvIV (ST (2));
		position = SvIV (ST (3));
	} else {
		croak ("Usage: Gtk2::Editable::insert_text(editable, new_text, position)");
	}

	gtk_editable_insert_text (editable, new_text,
				  new_text_length, &position);
	RETVAL = position;
    OUTPUT:
	RETVAL

void
gtk_editable_delete_text (editable, start_pos, end_pos)
	GtkEditable *editable
	gint start_pos
	gint end_pos

gchar_own *
gtk_editable_get_chars (editable, start_pos, end_pos)
	GtkEditable *editable
	gint start_pos
	gint end_pos

void
gtk_editable_cut_clipboard (editable)
	GtkEditable *editable

void
gtk_editable_copy_clipboard (editable)
	GtkEditable *editable

void
gtk_editable_paste_clipboard (editable)
	GtkEditable *editable

void
gtk_editable_delete_selection (editable)
	GtkEditable *editable

void
gtk_editable_set_position (editable, position)
	GtkEditable *editable
	gint position

gint
gtk_editable_get_position (editable)
	GtkEditable *editable

void
gtk_editable_set_editable (editable, is_editable)
	GtkEditable *editable
	gboolean is_editable

gboolean
gtk_editable_get_editable (editable)
	GtkEditable *editable