[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

Re: GtkCellLayout interface



On Sun, 2007-12-30 at 03:23 -0500, muppet wrote:

> ...like this.  The patch is smaller than the example code, which is  
> lifted from GtkCellView in gtk+ HEAD.

And here's an updated patch that implements the callback handling for
SET_SELL_DATA_FUNC, adds a unit test, and fixes a few small issues (like
using boolSV instead of newSViv for gbooleans, and fixing the return
stack handling in GET_CELLS).

-- 
Bye,
-Torsten
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);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]