[planner] Fix crash when using zoom-to-fit (bug #550559)



commit 8e4fab5a5585e3e33b402c348ab3b5958eab8a8c
Author: Maurice van der Pot <griffon26 kfk4ever com>
Date:   Mon Oct 26 23:04:41 2009 +0100

    Fix crash when using zoom-to-fit (bug #550559)
    
    The problem was caused by calling gnome_canvas_get_scroll_offsets() from
    gantt_chart_reflow_idle(). The scroll offsets were used to prevent scrolling in
    some cases when the duration of a task was being dragged. Unfortunately the
    returned scroll offsets seemed to not always be up-to-date, eventually
    resulting in a crash.
    
    This patch uses a dummy canvas item to extend the scroll region while dragging
    the duration of a task instead of relying on gnome_canvas_get_scroll_offsets().

 src/Makefile.am           |    2 +
 src/dummy-canvas-item.c   |  362 +++++++++++++++++++++++++++++++++++++++++++++
 src/dummy-canvas-item.h   |   86 +++++++++++
 src/planner-gantt-chart.c |   30 ----
 src/planner-gantt-row.c   |   21 +++
 5 files changed, 471 insertions(+), 30 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index f53c42c..2d08246 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -31,6 +31,8 @@ libplannerapp_la_SOURCES = 			\
 	$(built_sources)		\
 	eel-canvas-rect.c		\
 	eel-canvas-rect.h		\
+	dummy-canvas-item.c		\
+	dummy-canvas-item.h		\
 	planner-application.c		\
 	planner-application.h		\
 	planner-assignment-model.c	\
diff --git a/src/dummy-canvas-item.c b/src/dummy-canvas-item.c
new file mode 100644
index 0000000..5d71c33
--- /dev/null
+++ b/src/dummy-canvas-item.c
@@ -0,0 +1,362 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * Copyright (C) 2009 Maurice van der Pot <griffon26 kfk4ever com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temgle Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+  @NOTATION@
+ */
+
+/* Dummy item type for GnomeCanvas widget
+ *
+ * This dummy item can be used to manipulate the bounding box returned by the
+ * get_bounds function of the canvas root. It has no visible shape, but is
+ * included in bounding box calculations.
+ *
+ * Author: Maurice van der Pot <griffon26 kfk4ever com>
+ */
+
+#include <config.h>
+#include <math.h>
+#include <string.h>
+#include <libgnomecanvas/gnome-canvas-util.h>
+#include "dummy-canvas-item.h"
+
+
+enum {
+	PROP_0,
+	PROP_X1,
+	PROP_Y1,
+	PROP_X2,
+	PROP_Y2
+};
+
+
+static void gnome_canvas_dummy_class_init   (GnomeCanvasDummyClass *class);
+static void gnome_canvas_dummy_init         (GnomeCanvasDummy      *dummy);
+static void gnome_canvas_dummy_destroy      (GtkObject             *object);
+static void gnome_canvas_dummy_set_property (GObject               *object,
+					     guint                  param_id,
+					     const GValue          *value,
+					     GParamSpec            *pspec);
+static void gnome_canvas_dummy_get_property (GObject               *object,
+					     guint                  param_id,
+					     GValue                *value,
+					     GParamSpec            *pspec);
+
+static void   gnome_canvas_dummy_update      (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags);
+static void   gnome_canvas_dummy_realize     (GnomeCanvasItem *item);
+static void   gnome_canvas_dummy_unrealize   (GnomeCanvasItem *item);
+static void   gnome_canvas_dummy_draw        (GnomeCanvasItem *item, GdkDrawable *drawable,
+					     int x, int y, int width, int height);
+static double gnome_canvas_dummy_point       (GnomeCanvasItem *item, double x, double y,
+					     int cx, int cy, GnomeCanvasItem **actual_item);
+static void   gnome_canvas_dummy_bounds      (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2);
+static void   gnome_canvas_dummy_render      (GnomeCanvasItem *item, GnomeCanvasBuf *buf);
+
+
+static GnomeCanvasItemClass *parent_class;
+
+
+GType
+gnome_canvas_dummy_get_type (void)
+{
+	static GType dummy_type;
+
+	if (!dummy_type) {
+		static const GTypeInfo object_info = {
+			sizeof (GnomeCanvasDummyClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) gnome_canvas_dummy_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,			/* class_data */
+			sizeof (GnomeCanvasDummy),
+			0,			/* n_preallocs */
+			(GInstanceInitFunc) gnome_canvas_dummy_init,
+			NULL			/* value_table */
+		};
+
+		dummy_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasDummy",
+						     &object_info, 0);
+	}
+
+	return dummy_type;
+}
+
+static void
+gnome_canvas_dummy_class_init (GnomeCanvasDummyClass *class)
+{
+	GObjectClass *gobject_class;
+	GtkObjectClass *object_class;
+	GnomeCanvasItemClass *item_class;
+
+	gobject_class = (GObjectClass *) class;
+	object_class = (GtkObjectClass *) class;
+	item_class = (GnomeCanvasItemClass *) class;
+
+	parent_class = g_type_class_peek_parent (class);
+
+	gobject_class->set_property = gnome_canvas_dummy_set_property;
+	gobject_class->get_property = gnome_canvas_dummy_get_property;
+
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_X1,
+                 g_param_spec_double ("x1", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_Y1,
+                 g_param_spec_double ("y1", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_X2,
+                 g_param_spec_double ("x2", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_Y2,
+                 g_param_spec_double ("y2", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+	object_class->destroy = gnome_canvas_dummy_destroy;
+
+	item_class->update = gnome_canvas_dummy_update;
+	item_class->realize = gnome_canvas_dummy_realize;
+	item_class->unrealize = gnome_canvas_dummy_unrealize;
+	item_class->draw = gnome_canvas_dummy_draw;
+	item_class->point = gnome_canvas_dummy_point;
+	item_class->bounds = gnome_canvas_dummy_bounds;
+
+	item_class->render = gnome_canvas_dummy_render;
+}
+
+static void
+gnome_canvas_dummy_init (GnomeCanvasDummy *dummy)
+{
+	dummy->x1 = 0.0;
+	dummy->y1 = 0.0;
+	dummy->x2 = 0.0;
+	dummy->y2 = 0.0;
+}
+
+static void
+gnome_canvas_dummy_destroy (GtkObject *object)
+{
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (GNOME_IS_CANVAS_DUMMY (object));
+
+	/* remember, destroy can be run multiple times! */
+
+	if (GTK_OBJECT_CLASS (parent_class)->destroy)
+		(* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+/* Computes the bounding box of the dummy object.
+ */
+static void
+get_bounds (GnomeCanvasDummy *dummy, double *bx1, double *by1, double *bx2, double *by2)
+{
+	GnomeCanvasItem *item;
+	gdouble          wx1, wy1, wx2, wy2;
+	gint             cx1, cy1, cx2, cy2;
+
+	item = GNOME_CANVAS_ITEM(dummy);
+
+	wx1 = dummy->x1;
+	wy1 = dummy->y1;
+	wx2 = dummy->x2;
+	wy2 = dummy->y2;
+
+	gnome_canvas_item_i2w (item, &wx1, &wy1);
+	gnome_canvas_item_i2w (item, &wx2, &wy2);
+	gnome_canvas_w2c (item->canvas, wx1, wy1, &cx1, &cy1);
+	gnome_canvas_w2c (item->canvas, wx2, wy2, &cx2, &cy2);
+
+	*bx1 = cx1;
+	*by1 = cy1;
+	*bx2 = cx2;
+	*by2 = cy2;
+}
+
+/* Computes the bounding box of the dummy, in canvas coordinates.
+ */
+static void
+get_bounds_canvas (GnomeCanvasDummy *dummy, double *bx1, double *by1, double *bx2, double *by2, double affine[6])
+{
+	ArtDRect bbox_world;
+	ArtDRect bbox_canvas;
+
+	get_bounds (dummy, &bbox_world.x0, &bbox_world.y0, &bbox_world.x1, &bbox_world.y1);
+
+	art_drect_affine_transform (&bbox_canvas, &bbox_world, affine);
+	/* include 1 pixel of fudge */
+	*bx1 = bbox_canvas.x0 - 1;
+	*by1 = bbox_canvas.y0 - 1;
+	*bx2 = bbox_canvas.x1 + 1;
+	*by2 = bbox_canvas.y1 + 1;
+}
+
+static void
+gnome_canvas_dummy_set_property (GObject              *object,
+				guint                 param_id,
+				const GValue         *value,
+				GParamSpec           *pspec)
+{
+	GnomeCanvasItem *item;
+	GnomeCanvasDummy *dummy;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (GNOME_IS_CANVAS_DUMMY (object));
+
+	item = GNOME_CANVAS_ITEM (object);
+	dummy = GNOME_CANVAS_DUMMY (object);
+
+	switch (param_id) {
+	case PROP_X1:
+		dummy->x1 = g_value_get_double (value);
+		gnome_canvas_item_request_update (item);
+		break;
+
+	case PROP_Y1:
+		dummy->y1 = g_value_get_double (value);
+		gnome_canvas_item_request_update (item);
+		break;
+
+	case PROP_X2:
+		dummy->x2 = g_value_get_double (value);
+		gnome_canvas_item_request_update (item);
+		break;
+
+	case PROP_Y2:
+		dummy->y2 = g_value_get_double (value);
+		gnome_canvas_item_request_update (item);
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	}
+}
+
+static void
+gnome_canvas_dummy_get_property (GObject              *object,
+				guint                 param_id,
+				GValue               *value,
+				GParamSpec           *pspec)
+{
+	GnomeCanvasDummy *dummy;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (GNOME_IS_CANVAS_DUMMY (object));
+
+	dummy = GNOME_CANVAS_DUMMY (object);
+
+	switch (param_id) {
+	case PROP_X1:
+		g_value_set_double (value,  dummy->x1);
+		break;
+
+	case PROP_Y1:
+		g_value_set_double (value,  dummy->y1);
+		break;
+
+	case PROP_X2:
+		g_value_set_double (value,  dummy->x2);
+		break;
+
+	case PROP_Y2:
+		g_value_set_double (value,  dummy->y2);
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	}
+}
+
+static void
+gnome_canvas_dummy_render (GnomeCanvasItem *item,
+			     GnomeCanvasBuf *buf)
+{
+}
+
+static void
+gnome_canvas_dummy_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
+{
+	GnomeCanvasDummy *dummy;
+	double x1, y1, x2, y2;
+
+	dummy = GNOME_CANVAS_DUMMY (item);
+
+	if (parent_class->update)
+		(* parent_class->update) (item, affine, clip_path, flags);
+
+	if (item->canvas->aa) {
+		g_assert(FALSE);
+	} else {
+		get_bounds_canvas (dummy, &x1, &y1, &x2, &y2, affine);
+		gnome_canvas_update_bbox (item, x1, y1, x2, y2);
+	}
+}
+
+static void
+gnome_canvas_dummy_realize (GnomeCanvasItem *item)
+{
+	if (parent_class->realize)
+		(* parent_class->realize) (item);
+}
+
+static void
+gnome_canvas_dummy_unrealize (GnomeCanvasItem *item)
+{
+	if (parent_class->unrealize)
+		(* parent_class->unrealize) (item);
+}
+
+static void
+gnome_canvas_dummy_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
+			int x, int y, int width, int height)
+{
+}
+
+static double
+gnome_canvas_dummy_point (GnomeCanvasItem *item, double x, double y,
+			 int cx, int cy, GnomeCanvasItem **actual_item)
+{
+	*actual_item = item;
+
+	/* Always return a distance > 0 so we don't receive events */
+	return 1.0;
+}
+
+static void
+gnome_canvas_dummy_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+	GnomeCanvasDummy *dummy;
+
+	dummy = GNOME_CANVAS_DUMMY (item);
+
+	get_bounds (dummy, x1, y1, x2, y2);
+}
diff --git a/src/dummy-canvas-item.h b/src/dummy-canvas-item.h
new file mode 100644
index 0000000..b375882
--- /dev/null
+++ b/src/dummy-canvas-item.h
@@ -0,0 +1,86 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * Copyright (C) 2009 Maurice van der Pot <griffon26 kfk4ever com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temgle Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+  @NOTATION@
+ */
+
+/* Dummy item type for GnomeCanvas widget
+ *
+ * This dummy item can be used to manipulate the bounding box returned by the
+ * get_bounds function of the canvas root. It has no visible shape, but is
+ * included in bounding box calculations.
+ *
+ * Author: Maurice van der Pot <griffon26 kfk4ever com>
+ */
+
+#ifndef GNOME_CANVAS_DUMMY_H
+#define GNOME_CANVAS_DUMMY_H
+
+
+#include <libgnomecanvas/gnome-canvas.h>
+
+
+G_BEGIN_DECLS
+
+/* Base class for rectangle and ellipse item types.  These are defined by their top-left and
+ * bottom-right corners.  Rectangles and ellipses share the following arguments:
+ *
+ * name			type		read/write	description
+ * ------------------------------------------------------------------------------------------
+ * x1			double		RW		Leftmost coordinate of rectangle
+ * y1			double		RW		Topmost coordinate of rectangle
+ * x2			double		RW		Rightmost coordinate of rectangle
+ * y2			double		RW		Bottommost coordinate of rectangle
+ */
+
+#define GNOME_TYPE_CANVAS_DUMMY            (gnome_canvas_dummy_get_type ())
+#define GNOME_CANVAS_DUMMY(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_CANVAS_DUMMY, GnomeCanvasDummy))
+#define GNOME_CANVAS_DUMMY_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_DUMMY, GnomeCanvasDummyClass))
+#define GNOME_IS_CANVAS_DUMMY(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNOME_TYPE_CANVAS_DUMMY))
+#define GNOME_IS_CANVAS_DUMMY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_DUMMY))
+#define GNOME_CANVAS_DUMMY_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GNOME_TYPE_CANVAS_DUMMY, GnomeCanvasDummyClass))
+
+
+typedef struct _GnomeCanvasDummy GnomeCanvasDummy;
+typedef struct _GnomeCanvasDummyClass GnomeCanvasDummyClass;
+
+struct _GnomeCanvasDummy {
+	GnomeCanvasItem item;
+
+        double x1;
+        double x2;
+        double y1;
+        double y2;
+
+};
+
+struct _GnomeCanvasDummyClass {
+	GnomeCanvasItemClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType gnome_canvas_dummy_get_type (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif
diff --git a/src/planner-gantt-chart.c b/src/planner-gantt-chart.c
index a050ce8..a0aabd3 100644
--- a/src/planner-gantt-chart.c
+++ b/src/planner-gantt-chart.c
@@ -189,11 +189,6 @@ gantt_chart_add_relation                                (PlannerGanttChart  *cha
 							 TreeNode           *task,
 							 TreeNode           *predecessor,
 							 MrpRelationType     type);
-static void        gantt_chart_get_visible_region       (PlannerGanttChart *chart,
-							 gdouble            *x1,
-							 gdouble            *y1,
-							 gdouble            *x2,
-							 gdouble            *y2);
 static void        gantt_chart_set_scroll_region        (PlannerGanttChart  *chart,
 							 gdouble             x1,
 							 gdouble             y1,
@@ -967,7 +962,6 @@ gantt_chart_reflow_idle (PlannerGanttChart *chart)
 	PlannerGanttChartPriv *priv;
 	mrptime                t1, t2;
 	gdouble                x1, y1, x2, y2;
-	gdouble                vx1, vy1, vx2, vy2;
 	gdouble                width, height;
 	gdouble                bx1, bx2;
 	GtkAllocation          allocation;
@@ -1011,11 +1005,6 @@ gantt_chart_reflow_idle (PlannerGanttChart *chart)
 
 	x2 = x1 + width;
 
-	gantt_chart_get_visible_region(chart, &vx1, &vy1, &vx2, &vy2);
-
-	x2 = MAX (x2, vx2);
-	y2 = MAX (y2, vy2);
-
 	gantt_chart_set_scroll_region (chart,
 				       x1,
 				       y1,
@@ -1671,25 +1660,6 @@ gantt_chart_tree_traverse (TreeNode *node, TreeFunc func, gpointer data)
 }
 
 static void
-gantt_chart_get_visible_region (PlannerGanttChart *chart,
-			       gdouble            *x1,
-			       gdouble            *y1,
-			       gdouble            *x2,
-			       gdouble            *y2)
-{
-	GnomeCanvas *canvas;
-	gint cx, cy;
-
-	canvas = chart->priv->canvas;
-
-	gnome_canvas_get_scroll_offsets(canvas, &cx, &cy);
-	gnome_canvas_c2w(canvas, cx, cy, x1, y1);
-
-	*x2 = *x1 + GTK_WIDGET(canvas)->allocation.width;
-	*y2 = *y1 + GTK_WIDGET(canvas)->allocation.height;
-}
-
-static void
 gantt_chart_set_scroll_region (PlannerGanttChart *chart,
 			       gdouble            x1,
 			       gdouble            y1,
diff --git a/src/planner-gantt-row.c b/src/planner-gantt-row.c
index f4631eb..5b40e43 100644
--- a/src/planner-gantt-row.c
+++ b/src/planner-gantt-row.c
@@ -37,6 +37,7 @@
 #include "planner-gantt-chart.h"
 #include "planner-canvas-line.h"
 #include "eel-canvas-rect.h"
+#include "dummy-canvas-item.h"
 #include "planner-scale-utils.h"
 #include "planner-task-tree.h"
 #include "planner-task-popup.h"
@@ -287,6 +288,8 @@ static gdouble            drag_item_wx_min;
 static gdouble            drag_item_wx_max;
 static gdouble            drag_item_wy_min;
 static gdouble            drag_item_wy_max;
+static GnomeCanvasItem   *scroll_region_extender;
+static gdouble            scroll_region_max_x;
 static gdouble            drag_wx1, drag_wy1;
 static GnomeCanvasPoints *drag_points = NULL;
 static GnomeCanvasItem   *target_item;
@@ -2363,6 +2366,10 @@ gantt_row_drag_item_to_pointer (PlannerGanttRow *row, gboolean scroll)
 		gnome_canvas_item_set (drag_item,
 				       "x2", wx2,
 				       NULL);
+		scroll_region_max_x = MAX(wx2, scroll_region_max_x);
+		gnome_canvas_item_set (scroll_region_extender,
+				       "x2", scroll_region_max_x,
+				       NULL);
 		break;
 
 	case STATE_DRAG_COMPLETE:
@@ -2634,6 +2641,15 @@ gantt_row_event (GnomeCanvasItem *item, GdkEvent *event)
 									   "width_pixels", 1,
 									   NULL);
 					gnome_canvas_item_hide (drag_item);
+					scroll_region_extender = gnome_canvas_item_new (gnome_canvas_root (item->canvas),
+									   gnome_canvas_dummy_get_type(),
+									   "x1", wx2,
+									   "y1", wy1,
+									   "x2", wx2,
+									   "y2", wy2,
+									   NULL);
+					gnome_canvas_item_hide (scroll_region_extender);
+					scroll_region_max_x = wx2;
 
 					/* set limits */
 					drag_item_wx_min = wx1;
@@ -2834,6 +2850,9 @@ gantt_row_event (GnomeCanvasItem *item, GdkEvent *event)
 		else {
 			gnome_canvas_item_raise_to_top (drag_item);
 			gnome_canvas_item_show (drag_item);
+			if(priv->state & STATE_DRAG_DURATION) {
+				gnome_canvas_item_show (scroll_region_extender);
+			}
 
 			gantt_row_drag_item_to_pointer (row, FALSE);
 		}
@@ -2894,6 +2913,8 @@ gantt_row_event (GnomeCanvasItem *item, GdkEvent *event)
 			*/
 			gtk_object_destroy (GTK_OBJECT (drag_item));
 			drag_item = NULL;
+			gtk_object_destroy (GTK_OBJECT (scroll_region_extender));
+			scroll_region_extender = NULL;
 
 			planner_gantt_chart_status_updated (chart, NULL);
 		}



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