[Date Prev][Date Next] [Thread Prev][Thread Next]
[Thread Index]
[Date Index]
[Author Index]
Re: GtkCellLayout interface
- From: Torsten Schoenfeld <kaffeetisch gmx de>
- To: gtk-perl-list gnome org
- Subject: Re: GtkCellLayout interface
- Date: Sun, 30 Dec 2007 18:25:52 +0100
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]