Index: Gtk2.pm =================================================================== RCS file: /cvsroot/gtk2-perl/gtk2-perl-xs/Gtk2/Gtk2.pm,v retrieving revision 1.113 diff -u -d -p -r1.113 Gtk2.pm --- Gtk2.pm 16 Dec 2007 19:54:29 -0000 1.113 +++ Gtk2.pm 30 Dec 2007 17:19:11 -0000 @@ -96,6 +96,12 @@ use overload '==' => \&Gtk2::Gdk::Atom::eq, fallback => 1; +package Gtk2::CellLayout::DataFunc; + +use overload + '&{}' => sub { \&Gtk2::CellLayout::DataFunc::invoke }, + fallback => 1; + package Gtk2::TreeSortable::IterCompareFunc; use overload Index: t/GtkCellLayoutIface.t =================================================================== RCS file: t/GtkCellLayoutIface.t diff -N t/GtkCellLayoutIface.t --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ t/GtkCellLayoutIface.t 30 Dec 2007 17:19:11 -0000 @@ -0,0 +1,111 @@ +#!/usr/bin/perl -w + +package CustomCellLayout; + +use strict; +use warnings; +use Glib ':constants'; +use Gtk2; + +use Test::More; + +use Glib::Object::Subclass + Gtk2::Widget::, + interfaces => [ qw(Gtk2::CellLayout) ], + ; + +sub PACK_START { + my ($self, $cell, $expand) = @_; + isa_ok ($self, __PACKAGE__); + isa_ok ($cell, 'Gtk2::CellRenderer'); + is ($expand, TRUE); +} + +sub PACK_END { + my ($self, $cell, $expand) = @_; + isa_ok ($self, __PACKAGE__); + isa_ok ($cell, 'Gtk2::CellRenderer'); + is ($expand, FALSE); +} + +sub CLEAR { + my ($self) = @_; + isa_ok ($self, __PACKAGE__); +} + +sub ADD_ATTRIBUTE { + my ($self, $cell, $attribute, $column) = @_; + isa_ok ($self, __PACKAGE__); + isa_ok ($cell, 'Gtk2::CellRenderer'); + is ($attribute, 'text'); + is ($column, 42); +} + +sub SET_CELL_DATA_FUNC { + my ($self, $cell, $func, $data) = @_; + isa_ok ($self, __PACKAGE__); + isa_ok ($cell, 'Gtk2::CellRenderer'); + isa_ok ($func, 'Gtk2::CellLayout::DataFunc'); + ok (defined $data); + + my $model = Gtk2::ListStore->new (qw/Glib::String/); + $func->($self, $cell, $model, $model->append (), $data); +} + +sub CLEAR_ATTRIBUTES { + my ($self, $cell) = @_; + isa_ok ($self, __PACKAGE__); + isa_ok ($cell, 'Gtk2::CellRenderer'); +} + +sub REORDER { + my ($self, $cell, $position) = @_; + isa_ok ($self, __PACKAGE__); + isa_ok ($cell, 'Gtk2::CellRenderer'); + is ($position, 42); +} + +sub GET_CELLS { + my ($self) = @_; + isa_ok ($self, __PACKAGE__); + $self->{cell_one} = Gtk2::CellRendererText->new; + $self->{cell_two} = Gtk2::CellRendererToggle->new; + return ($self->{cell_one}, $self->{cell_two}); +} + +package main; + +use strict; +use warnings; +use Glib ':constants'; +use Gtk2::TestHelper tests => 29; + +my $cell = Gtk2::CellRendererText->new (); + +my $layout = CustomCellLayout->new (); +$layout->pack_start ($cell, TRUE); +$layout->pack_end ($cell, FALSE); +$layout->clear (); +$layout->add_attribute ($cell, text => 42); +$layout->clear_attributes ($cell); +$layout->reorder ($cell, 42); + +SKIP: { + skip 'get_cells', 4 + unless Gtk2->CHECK_VERSION (2, 12, 0); + + my @cells = $layout->get_cells (); + is (scalar @cells, 2); + isa_ok ($cells[0], 'Gtk2::CellRendererText'); + isa_ok ($cells[1], 'Gtk2::CellRendererToggle'); +} + +my $callback = sub { + my ($cb_layout, $cb_cell, $model, $iter, $data) = @_; + is ($cb_layout, $layout); + is ($cb_cell, $cell); + isa_ok ($model, 'Gtk2::ListStore'); + isa_ok ($iter, 'Gtk2::TreeIter'); + is ($data, 'bla!'); +}; +$layout->set_cell_data_func ($cell, $callback, 'bla!'); Index: xs/GtkCellLayout.xs =================================================================== RCS file: /cvsroot/gtk2-perl/gtk2-perl-xs/Gtk2/xs/GtkCellLayout.xs,v retrieving revision 1.6 diff -u -d -p -r1.6 GtkCellLayout.xs --- xs/GtkCellLayout.xs 15 Sep 2007 14:33:02 -0000 1.6 +++ xs/GtkCellLayout.xs 30 Dec 2007 17:19:11 -0000 @@ -29,8 +29,252 @@ gtk2perl_cell_layout_data_func (GtkCellL tree_model, iter); } + +/* + GInterface support + */ + +#define GET_METHOD(obj, name) \ + HV * stash = gperl_object_stash_from_type (G_OBJECT_TYPE (obj)); \ + GV * slot = gv_fetchmethod (stash, name); + +#define METHOD_EXISTS (slot && GvCV (slot)) + +#define PREP(obj) \ + dSP; \ + ENTER; \ + SAVETMPS; \ + PUSHMARK (SP) ; \ + PUSHs (sv_2mortal (newSVGObject (G_OBJECT (obj)))); + +#define CALL \ + PUTBACK; \ + call_sv ((SV *) GvCV (slot), G_VOID | G_DISCARD); + +#define FINISH \ + FREETMPS; \ + LEAVE; + + +static void +gtk2perl_cell_layout_pack_start (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + gboolean expand) +{ + GET_METHOD (cell_layout, "PACK_START"); + + if (METHOD_EXISTS) { + PREP (cell_layout); + XPUSHs (sv_2mortal (newSVGtkCellRenderer (cell))); + XPUSHs (sv_2mortal (boolSV (expand))); + CALL; + FINISH; + } +} + +static void +gtk2perl_cell_layout_pack_end (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + gboolean expand) +{ + GET_METHOD (cell_layout, "PACK_END"); + + if (METHOD_EXISTS) { + PREP (cell_layout); + XPUSHs (sv_2mortal (newSVGtkCellRenderer (cell))); + XPUSHs (sv_2mortal (boolSV (expand))); + CALL; + FINISH; + } +} + +static void +gtk2perl_cell_layout_clear (GtkCellLayout *cell_layout) +{ + GET_METHOD (cell_layout, "CLEAR"); + + if (METHOD_EXISTS) { + PREP (cell_layout); + CALL; + FINISH; + } +} + +static void +gtk2perl_cell_layout_add_attribute (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + const gchar *attribute, + gint column) +{ + GET_METHOD (cell_layout, "ADD_ATTRIBUTE"); + + if (METHOD_EXISTS) { + PREP (cell_layout); + XPUSHs (sv_2mortal (newSVGtkCellRenderer (cell))); + XPUSHs (sv_2mortal (newSVGChar (attribute))); + XPUSHs (sv_2mortal (newSViv (column))); + CALL; + FINISH; + } +} + +/* The strategy for passing the function pointer to Perl land is the same as + * the one used in GtkTreeSortable.xs. */ + +typedef struct { + GtkCellLayoutDataFunc func; + gpointer data; + GtkDestroyNotify destroy; +} Gtk2PerlCellLayoutDataFunc; + +static void +create_callback (GtkCellLayoutDataFunc func, + gpointer data, + GtkDestroyNotify destroy, + SV **code_return, + SV **data_return) +{ + HV *stash; + gchar *sub; + CV *dummy_cv = NULL; + SV *code_sv, *data_sv; + Gtk2PerlCellLayoutDataFunc *wrapper; + + stash = gv_stashpv ("Gtk2::CellLayout::DataFunc", TRUE); + + sub = g_strdup_printf ("__gtk2perl_cell_layout_data_func_%p", data); + dummy_cv = newCONSTSUB (stash, sub, NULL); + g_free (sub); + + code_sv = sv_bless (newRV_noinc ((SV *) dummy_cv), stash); + + wrapper = g_new0 (Gtk2PerlCellLayoutDataFunc, 1); + wrapper->func = func; + wrapper->data = data; + wrapper->destroy = destroy; + + data_sv = newSViv (PTR2IV (wrapper)); + sv_magic ((SV *) dummy_cv, 0, PERL_MAGIC_ext, (const char *) data_sv, 0); + + *code_return = code_sv; + *data_return = data_sv; +} + +static void +gtk2perl_cell_layout_set_cell_data_func (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + GtkCellLayoutDataFunc func, + gpointer func_data, + GDestroyNotify destroy) +{ + GET_METHOD (cell_layout, "SET_CELL_DATA_FUNC"); + + if (METHOD_EXISTS) { + SV *code_sv, *data_sv; + PREP (cell_layout); + + create_callback (func, func_data, destroy, &code_sv, &data_sv); + + XPUSHs (sv_2mortal (newSVGtkCellRenderer (cell))); + XPUSHs (sv_2mortal (newSVsv (code_sv))); + XPUSHs (sv_2mortal (newSVsv (data_sv))); + + CALL; + FINISH; + } +} + +static void +gtk2perl_cell_layout_clear_attributes (GtkCellLayout *cell_layout, + GtkCellRenderer *cell) +{ + GET_METHOD (cell_layout, "CLEAR_ATTRIBUTES"); + + if (METHOD_EXISTS) { + PREP (cell_layout); + XPUSHs (sv_2mortal (newSVGtkCellRenderer (cell))); + CALL; + FINISH; + } +} + +static void +gtk2perl_cell_layout_reorder (GtkCellLayout *cell_layout, + GtkCellRenderer *cell, + gint position) +{ + GET_METHOD (cell_layout, "REORDER"); + + if (METHOD_EXISTS) { + PREP (cell_layout); + XPUSHs (sv_2mortal (newSVGtkCellRenderer (cell))); + XPUSHs (sv_2mortal (newSViv (position))); + CALL; + FINISH; + } +} + +#if GTK_CHECK_VERSION (2, 12, 0) + +static GList* +gtk2perl_cell_layout_get_cells (GtkCellLayout *cell_layout) +{ + GList * cells = NULL; + + GET_METHOD (cell_layout, "GET_CELLS"); + + if (METHOD_EXISTS) { + int count; + PREP (cell_layout); + PUTBACK; + count = call_sv ((SV *) GvCV (slot), G_ARRAY); + SPAGAIN; + while (count > 0) { + SV * sv = POPs; + cells = g_list_prepend (cells, SvGtkCellRenderer (sv)); + count--; + } + PUTBACK; + FINISH; + } + + return cells; +} + +#endif + +static void +gtk2perl_cell_layout_init (GtkCellLayoutIface * iface) +{ + iface->pack_start = gtk2perl_cell_layout_pack_start; + iface->pack_end = gtk2perl_cell_layout_pack_end; + iface->clear = gtk2perl_cell_layout_clear; + iface->add_attribute = gtk2perl_cell_layout_add_attribute; + iface->set_cell_data_func = gtk2perl_cell_layout_set_cell_data_func; + iface->clear_attributes = gtk2perl_cell_layout_clear_attributes; + iface->reorder = gtk2perl_cell_layout_reorder; +#if GTK_CHECK_VERSION (2, 12, 0) + iface->get_cells = gtk2perl_cell_layout_get_cells; +#endif +} + MODULE = Gtk2::CellLayout PACKAGE = Gtk2::CellLayout PREFIX = gtk_cell_layout_ +=for apidoc __hide__ +=cut +void +_ADD_INTERFACE (class, const char * target_class) + CODE: + { + static const GInterfaceInfo iface_info = { + (GInterfaceInitFunc) gtk2perl_cell_layout_init, + (GInterfaceFinalizeFunc) NULL, + (gpointer) NULL + }; + GType gtype = gperl_object_type_from_package (target_class); + g_type_add_interface_static (gtype, GTK_TYPE_CELL_LAYOUT, &iface_info); + } + void gtk_cell_layout_pack_start (GtkCellLayout *cell_layout, GtkCellRenderer *cell, gboolean expand); @@ -90,3 +334,32 @@ gtk_cell_layout_get_cells (GtkCellLayout g_list_free (result); #endif + +MODULE = Gtk2::CellLayout PACKAGE = Gtk2::CellLayout::DataFunc + +void +invoke (GtkCellLayout *cell_layout, GtkCellRenderer *cell, GtkTreeModel *tree_model, GtkTreeIter *iter, SV *data) + PREINIT: + Gtk2PerlCellLayoutDataFunc *wrapper; + CODE: + wrapper = INT2PTR (Gtk2PerlCellLayoutDataFunc*, SvIV (data)); + if (!wrapper || !wrapper->func) + croak ("Invalid user data passed to the data func"); + wrapper->func (cell_layout, cell, tree_model, iter, wrapper->data); + +void +DESTROY (SV *code) + PREINIT: + MAGIC *mg; + Gtk2PerlCellLayoutDataFunc *wrapper; + CODE: + if (!gperl_sv_defined (code) || !SvROK (code) || !(mg = mg_find (SvRV (code), PERL_MAGIC_ext))) + return; + + wrapper = INT2PTR (Gtk2PerlCellLayoutDataFunc*, SvIV ((SV *) mg->mg_ptr)); + if (wrapper && wrapper->destroy) + wrapper->destroy (wrapper->data); + + sv_unmagic (SvRV (code), PERL_MAGIC_ext); + if (wrapper) + g_free (wrapper);