perl-Gtk2 r2118 - in trunk: . t xs



Author: tsch
Date: Tue Jan 20 19:47:19 2009
New Revision: 2118
URL: http://svn.gnome.org/viewvc/perl-Gtk2?rev=2118&view=rev

Log:
In gtk2perl_cell_renderer_start_editing, make sure we return an object with a
floating reference as is expected of a start_editing vfunc.  In the xsub for
gtk_cell_renderer_start_editing and in the fallback START_EDITING xsub, make
sure we sink any floating reference on the created cell editable in order to
comply with the usual rule of not passing floating references on to Perl code.
Patch by Kevin Ryde.


Modified:
   trunk/ChangeLog
   trunk/t/GtkCellRenderer.t
   trunk/xs/GtkCellRenderer.xs

Modified: trunk/t/GtkCellRenderer.t
==============================================================================
--- trunk/t/GtkCellRenderer.t	(original)
+++ trunk/t/GtkCellRenderer.t	Tue Jan 20 19:47:19 2009
@@ -2,7 +2,7 @@
 
 # $Id$
 
-use Gtk2::TestHelper tests => 13;
+use Gtk2::TestHelper tests => 20;
 use strict;
 
 package Mup::CellRendererPopup;
@@ -44,6 +44,23 @@
 sub on_activate { $hits_compat{activate}++;  shift->parent_activate (@_) }
 sub on_start_editing { $hits_compat{edit}++;  shift->parent_start_editing (@_) }
 
+package My::CellRendererNewEntry;
+use strict;
+use warnings;
+use Glib::Object::Subclass 'Gtk2::CellRendererText';
+my %hits_newentry;
+sub INIT_INSTANCE { $hits_newentry{init}++; }
+sub GET_SIZE { $hits_newentry{size}++;  shift->SUPER::GET_SIZE (@_) }
+sub RENDER { $hits_newentry{render}++;  shift->SUPER::RENDER (@_) }
+sub ACTIVATE { $hits_newentry{activate}++;  shift->SUPER::ACTIVATE (@_) }
+sub START_EDITING { $hits_newentry{edit}++;
+                    my $entry = Gtk2::Entry->new;
+                    $entry->signal_connect (destroy => \&_editable_destroy);
+                    return $entry;
+                  }
+sub _editable_destroy { $hits_newentry{editable_destroy}++ }
+ 
+
 ##########################################################################
 # driver code
 package main;
@@ -106,6 +123,25 @@
 	}, $model);
 $treeview->append_column ($column_compat);
 
+#
+# new entry cell renderer
+#
+my $renderer_newentry = My::CellRendererNewEntry->new;
+$renderer_newentry->set (mode => 'editable');
+my $column_newentry = Gtk2::TreeViewColumn->new_with_attributes
+    ('new-entry', $renderer_newentry, text => 0,);
+$treeview->append_column ($column_newentry);
+
+#
+# plain core CellRendererText
+#
+my $renderer_text = Gtk2::CellRendererText->new;
+$renderer_text->set (editable => 1);
+my $column_text = Gtk2::TreeViewColumn->new_with_attributes
+    ('core-text', $renderer_text, text => 0,);
+$treeview->append_column ($column_text);
+
+
 ##########################################################################
 
 $vbox->pack_start ($treeview, 1, 1, 0);
@@ -128,7 +164,33 @@
 
 $renderer->render ($window->window, $treeview, $rect, $rect, $rect, [qw(sorted prelit)]);
 ok(!$renderer->activate ($event, $treeview, "0", $rect, $rect, qw(selected)));
-isa_ok ($renderer->start_editing ($event, $treeview, "0", $rect, $rect, qw(selected)), "Gtk2::Entry");
+{
+  my $editable = $renderer->start_editing ($event, $treeview, "0", $rect, $rect, qw(selected));
+  isa_ok ($editable, "Gtk2::Entry");
+  my $destroyed = 0;
+  $editable->signal_connect (destroy => sub { $destroyed = 1 });
+  undef $editable;
+  is ($destroyed, 1,
+      'editable from start_editing using SUPER::START_EDITING destroyed when forgotten');
+}
+{
+  my $editable = $renderer_newentry->start_editing ($event, $treeview, "0", $rect, $rect, qw(selected));
+  isa_ok ($editable, "Gtk2::Entry");
+  my $destroyed = 0;
+  $editable->signal_connect (destroy => sub { $destroyed = 1 });
+  undef $editable;
+  is ($destroyed, 1,
+      'editable from start_editing using Gtk2::Entry->new destroyed when forgotten');
+}
+{
+  my $editable = $renderer_text->start_editing ($event, $treeview, "0", $rect, $rect, qw(selected));
+  isa_ok ($editable, "Gtk2::Entry");
+  my $destroyed = 0;
+  $editable->signal_connect (destroy => sub { $destroyed = 1 });
+  undef $editable;
+  is ($destroyed, 1,
+      'editable from start_editing on core GtkCellRendererText destroyed when forgotten');
+}
 
 $renderer->set_fixed_size (23, 42);
 is_deeply([$renderer->get_fixed_size], [23, 42]);
@@ -154,13 +216,24 @@
 	                       $column, 1);
 	$treeview->set_cursor (Gtk2::TreePath->new_from_string ('0'),
 	                       $column_compat, 1);
+	$treeview->set_cursor (Gtk2::TreePath->new_from_string ('0'),
+	                       $column_newentry, 1);
+
+        # and not editing any more
+	$treeview->set_cursor (Gtk2::TreePath->new_from_string ('0'),
+	                       $column_text, 0);
 };
 
 
 is_deeply ([ sort keys %hits ], [ qw/edit init render size/ ], 'callbacks encountered');
 is_deeply ([ sort keys %hits_compat ], [ qw/edit init render size/ ], 'callbacks encountered');
 
+# one start_editing programmatically, and one from set_cursor of $treeview
+is ($hits_newentry{edit}, 2);
+is ($hits_newentry{editable_destroy}, 2,
+   'the otherwise unreferenced GtkEntry should be destroyed');
+
 __END__
 
-Copyright (C) 2003-2005 by the gtk2-perl team (see the file AUTHORS for the
+Copyright (C) 2003-2005, 2009 by the gtk2-perl team (see the file AUTHORS for the
 full list).  See LICENSE for more information.

Modified: trunk/xs/GtkCellRenderer.xs
==============================================================================
--- trunk/xs/GtkCellRenderer.xs	(original)
+++ trunk/xs/GtkCellRenderer.xs	Tue Jan 20 19:47:19 2009
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003-2004 by the gtk2-perl team (see the file AUTHORS)
+ * Copyright (c) 2003-2004, 2009 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
@@ -272,22 +272,57 @@
 		sv = POPs;
 		if (gperl_sv_is_defined (sv)) {
 			editable = SvGtkCellEditable (sv);
-			/* if the object returned here was newly created by
-			 * the called code, then the wrapper (pointed to by
-			 * sv) is the owner of the object.  if there are no
-			 * other references to the wrapper, the call to
-			 * FREETMPS below will actually result in finalization
-			 * of the GObject, which means we'll return a bad
-			 * pointer to our caller.  to prevent this situation,
-			 * we need to add a ref to the wrapper (that is, the
-			 * blessed sv stored in the GObject, not the reference
-			 * sv) to keep it alive across FREETMPS and thus
-			 * prevent premature destruction.
+#if GLIB_CHECK_VERSION (2, 10, 0)
+			/* (*start_editing)() is basically a constructor and
+			 * as such should return an object with a floating
+			 * reference for the caller to take over.
+			 *
+			 * For GtkTreeView and GtkIconView for example that
+			 * ref is sunk when gtk_tree_view_put() or
+			 * gtk_icon_view_put() call gtk_widget_set_parent()
+			 * to add "editable" as one of their container
+			 * children.  (Eventually to be dereffed in the
+			 * usual way by gtk_container_remove() from
+			 * gtk_tree_view_remove_widget() or
+			 * gtk_icon_view_remove_widget() at the end of
+			 * editing.)
+			 *
+			 * Perl code constructors like Gtk2::Foo->new or
+			 * Glib::Object->new sink any initial floating
+			 * reference when making the wrapper (either if
+			 * constructing in the START_EDITING code or from
+			 * something made or wrapped previously).  So must
+			 * explicitly add a floating ref for GtkTreeView etc
+			 * to take over.
+			 *
+			 * If START_EDITING code gives a new object in "sv"
+			 * and it's used nowhere else then FREETMPS below
+			 * will SvREFCNT_dec it to zero and send it to the
+			 * usual Glib::Object::DESTROY.  If there wasn't a
+			 * floating ref added here on the GObject then that
+			 * GObject would be destroyed before we ever got to
+			 * return it.  With the extra floating ref the
+			 * wrapper converts to undead (ie. unused from perl
+			 * for the time being) and the GObject has a
+			 * refcount of 1 and the floating flag set.
+			 *
+			 * It's conceivable there could be a floating ref
+			 * already at this point.  That was the case in the
+			 * past from chained-up perl SUPER::START_EDITING
+			 * for instance.  Though it's abnormal let's assume
+			 * any floating ref here is meant for the caller to
+			 * take over and therefore should be left unchanged.
 			 */
-			if (G_OBJECT (editable)->ref_count == 1 &&
-			    SvREFCNT (SvRV (sv)) == 1) {
-				SvREFCNT_inc (SvRV (sv));
+			if (! g_object_is_floating (editable)) {
+				g_object_ref (editable);
+				g_object_force_floating (G_OBJECT (editable));
 			}
+#else
+			if (! GTK_OBJECT_FLOATING (editable)) {
+				gtk_object_ref (GTK_OBJECT (editable));
+				GTK_OBJECT_SET_FLAGS (editable, GTK_FLOATING);
+			}
+#endif
 		} else {
 			editable = NULL;
 		}
@@ -540,8 +575,26 @@
 	GdkRectangle         * cell_area
 	GtkCellRendererState   flags
 
+## gtk_cell_renderer_start_editing() is normally a constructor,
+## returning a widget with a floating ref ready for the caller to take
+## over.  But the generated typemap for "interface" objects like
+## GtkCellEditable only treats it as GObject and doesn't sink when
+## making the perl wrapper, so cast up to GtkWidget to get that.
+## GtkWidget is a requirement of GtkCellEditable, so "editable" is
+## certain to be a widget.
+##
+## The returned widget is normally about to be put in a container
+## anyway, which sinks any floating ref, but sink it now to follow the
+## general rule that wrapped widgets at the perl level don't have a
+## floating ref left.  In particular this means if you start_editing()
+## and then strike an error or otherwise never add it to a container
+## it won't be a memory leak.
+##
 ##GtkCellEditable* gtk_cell_renderer_start_editing (GtkCellRenderer *cell, GdkEvent *event, GtkWidget *widget, const gchar *path, GdkRectangle *background_area, GdkRectangle *cell_area, GtkCellRendererState flags)
-GtkCellEditable_ornull *
+=for apidoc
+=for signature celleditable or undef = $cell->start_editing ($event, $widget, $path, $background_area, $cell_area, $flags)
+=cut
+GtkWidget_ornull *
 gtk_cell_renderer_start_editing (cell, event, widget, path, background_area, cell_area, flags)
 	GtkCellRenderer      * cell
 	GdkEvent             * event
@@ -550,6 +603,10 @@
 	GdkRectangle         * background_area
 	GdkRectangle         * cell_area
 	GtkCellRendererState   flags
+CODE:
+	RETVAL = GTK_WIDGET (gtk_cell_renderer_start_editing (cell, event, widget, path, background_area, cell_area, flags));
+OUTPUT:
+	RETVAL
 
 #if GTK_CHECK_VERSION (2, 4, 0)
 
@@ -757,7 +814,12 @@
 								SvGdkRectangle_ornull (ST (5)),
 								SvGtkCellRendererState (ST (6)));
 			EXTEND (SP, 1);
-			PUSHs (sv_2mortal (newSVGtkCellEditable_ornull (editable)));
+			/* Note newSVGtkWidget here instead of
+			 * newSVGtkCellEditable so as to take ownership of
+			 * any floating ref.  See comments with
+			 * gtk_cell_renderer_start_editing() above.
+			 */
+			PUSHs (sv_2mortal (newSVGtkWidget_ornull (GTK_WIDGET (editable))));
 		}
 		break;
 	    default:



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