The attached two patches extend the goffice graph widget to support more
sizing modes and make gnumeric take advantage of that feature by
supporting the display of graphs in separate windows with various sizing
modes.

This patch slightly resembles a patch I sent some months ago to this
mailing list but that wasn't really of production quality.

Comments appreciated.

-- 
Christian Neumair <[EMAIL PROTECTED]>
Index: src/Makefile.am
===================================================================
RCS file: /cvs/gnome/gnumeric/src/Makefile.am,v
retrieving revision 1.395
diff -u -p -r1.395 Makefile.am
--- src/Makefile.am	7 Apr 2006 16:16:23 -0000	1.395
+++ src/Makefile.am	17 Apr 2006 21:07:20 -0000
@@ -212,6 +212,8 @@ GNUMERIC_BASE =					\
 	sheet-object-widget.h			\
 	sheet-style.c				\
 	sheet-style.h				\
+	gnm-graph-window.c			\
+	gnm-graph-window.h			\
 	gnm-plugin.c				\
 	gnm-plugin.h				\
 	solver.h				\
Index: src/gnm-graph-window.c
===================================================================
RCS file: src/gnm-graph-window.c
diff -N src/gnm-graph-window.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/gnm-graph-window.c	17 Apr 2006 21:07:21 -0000
@@ -0,0 +1,289 @@
+#include <gnumeric-config.h>
+
+#include "gnm-graph-window.h"
+
+#include <string.h>
+
+#include <glib/gi18n.h>
+
+#include <gtk/gtkcombobox.h>
+#include <gtk/gtklayout.h>
+#include <gtk/gtktoolbar.h>
+#include <gtk/gtktoolbutton.h>
+#include <gtk/gtktoolitem.h>
+#include <gtk/gtkscrolledwindow.h>
+#include <gtk/gtkstock.h>
+#include <gtk/gtkvbox.h>
+#include <gtk/gtkwindow.h>
+
+#include <goffice/gtk/go-graph-widget.h>
+
+#define ZOOM_IN(x) *x = CLAMP(*x+1, ZOOM_LEVEL_25, ZOOM_LEVEL_200)
+#define ZOOM_OUT(x) *x = CLAMP(*x-1, ZOOM_LEVEL_25, ZOOM_LEVEL_200)
+#define ZOOM_100(x) *x = ZOOM_LEVEL_100
+#define ZOOM_FIT(x) *x = ZOOM_LEVEL_FIT
+
+struct _GnmGraphWindow {
+	GtkWindow parent;
+
+	GtkWidget *vbox;
+
+	GtkWidget *toolbar;
+	GtkWidget *size_combo;
+
+	GtkWidget *scrolled_window;
+
+	GtkWidget *graph;
+	double graph_height;
+	double graph_width;
+
+	gboolean is_fullscreen;
+};
+
+struct _GnmGraphWindowClass {
+	GtkWindowClass parent_class;
+};
+
+/* keep in sync with gnm_graph_window_init */
+typedef enum {
+	CHART_SIZE_FIT = 0,
+	CHART_SIZE_FIT_WIDTH,
+	CHART_SIZE_FIT_HEIGHT,
+	/* separator */
+	CHART_SIZE_100 = 4,
+	CHART_SIZE_125,
+	CHART_SIZE_150,
+	CHART_SIZE_200,
+	CHART_SIZE_300,
+	CHART_SIZE_500,
+} ChartSize;
+
+G_DEFINE_TYPE (GnmGraphWindow, gnm_graph_window, GTK_TYPE_WINDOW);
+#define parent_class gnm_graph_window_parent_class
+
+static void
+fullscreen_button_clicked (GtkToolButton  *button,
+			   GnmGraphWindow *window)
+{
+	if (!window->is_fullscreen) {
+		gtk_window_fullscreen (GTK_WINDOW (window));
+		gtk_tool_button_set_stock_id (button, GTK_STOCK_LEAVE_FULLSCREEN);
+	} else {
+		gtk_window_unfullscreen (GTK_WINDOW (window));
+		gtk_tool_button_set_stock_id (button, GTK_STOCK_FULLSCREEN);
+	}
+
+	window->is_fullscreen = !window->is_fullscreen;
+}
+
+static void
+update_graph_sizing_mode (GnmGraphWindow *window)
+{
+	int height, width;
+	gboolean obey_ratio;
+	GOGraphWidgetSizeMode size_mode;
+	ChartSize size;
+
+	g_assert (IS_GO_GRAPH_WIDGET (window->graph));
+
+	obey_ratio = FALSE;
+
+	size = gtk_combo_box_get_active (GTK_COMBO_BOX (window->size_combo));
+	switch (size) {
+		case CHART_SIZE_FIT:
+			size_mode = GO_GRAPH_WIDGET_SIZE_MODE_FIT;
+			height = width = -1;
+			obey_ratio = TRUE;
+			break;
+
+		case CHART_SIZE_FIT_WIDTH:
+			size_mode = GO_GRAPH_WIDGET_SIZE_MODE_FIT_WIDTH;
+			height = width = -1;
+			obey_ratio = TRUE;
+			break;
+
+		case CHART_SIZE_FIT_HEIGHT:
+			size_mode = GO_GRAPH_WIDGET_SIZE_MODE_FIT_HEIGHT;
+			height = width = -1;
+			obey_ratio = TRUE;
+			break;
+
+		case CHART_SIZE_100:
+			size_mode = GO_GRAPH_WIDGET_SIZE_MODE_FIXED_SIZE;
+			width =  window->graph_width;
+			height = window->graph_height;
+			break;
+
+		case CHART_SIZE_125:
+			size_mode = GO_GRAPH_WIDGET_SIZE_MODE_FIXED_SIZE;
+			width =  window->graph_width * 1.25;
+			height = window->graph_height * 1.25;
+			break;
+
+		case CHART_SIZE_150:
+			size_mode = GO_GRAPH_WIDGET_SIZE_MODE_FIXED_SIZE;
+			width =  window->graph_width * 1.50;
+			height = window->graph_height * 1.50;
+			break;
+
+		case CHART_SIZE_200:
+			size_mode = GO_GRAPH_WIDGET_SIZE_MODE_FIXED_SIZE;
+			width =  window->graph_width * 2.0;
+			height = window->graph_height * 2.0;
+			break;
+
+		case CHART_SIZE_300:
+			size_mode = GO_GRAPH_WIDGET_SIZE_MODE_FIXED_SIZE;
+			width =  window->graph_width * 3.0;
+			height = window->graph_height * 3.0;
+			break;
+
+		case CHART_SIZE_500:
+			size_mode = GO_GRAPH_WIDGET_SIZE_MODE_FIXED_SIZE;
+			width = window->graph_width * 5.0;
+			height = window->graph_height * 5.0;
+			break;
+
+		default:
+			g_assert_not_reached ();
+			return;
+	}
+
+	g_object_set (window->graph,
+		      "aspect-ratio", obey_ratio ? window->graph_height / window->graph_width : 0.0,
+		      NULL);
+	go_graph_widget_set_size_mode (GO_GRAPH_WIDGET (window->graph), size_mode, width, height);
+}
+
+static gboolean
+size_combo_is_row_separator (GtkTreeModel *model,
+			     GtkTreeIter *iter,
+			     G_GNUC_UNUSED gpointer data)
+{
+	gboolean is_sep;
+	char *str;
+
+	gtk_tree_model_get (model, iter, 0, &str, -1);
+	is_sep = (strcmp (str, "SEPARATOR") == 0);
+
+	g_free (str);
+
+	return is_sep;
+}
+
+static void
+gnm_graph_window_init (GnmGraphWindow *window)
+{
+	GtkToolItem *item;
+	unsigned int i;
+
+	/* these indexes match the ChartSize enum */
+	static char const * chart_sizes[] = {
+		N_("Fit"),
+		N_("Fit Width"),
+		N_("Fit Height"),
+		"SEPARATOR",
+		N_("100%"),
+		N_("125%"),
+		N_("150%"),
+		N_("200%"),
+		N_("300%"),
+		N_("500%")
+	};
+
+	window->vbox = gtk_vbox_new (FALSE, 0);
+	gtk_widget_show (GTK_WIDGET (window->vbox));
+	gtk_container_add (GTK_CONTAINER (window), window->vbox);
+
+	window->toolbar = gtk_toolbar_new ();
+	gtk_widget_show (GTK_WIDGET (window->toolbar));
+	gtk_box_pack_start (GTK_BOX (window->vbox), window->toolbar, FALSE, FALSE, 0);
+
+	window->scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+	gtk_widget_show (GTK_WIDGET (window->scrolled_window));
+	gtk_container_add (GTK_CONTAINER (window->vbox), window->scrolled_window);
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (window->scrolled_window),
+					GTK_POLICY_NEVER, GTK_POLICY_NEVER);
+
+	item = gtk_tool_item_new ();
+	gtk_widget_show (GTK_WIDGET (item));
+	gtk_toolbar_insert (GTK_TOOLBAR (window->toolbar), item, -1);
+
+	window->size_combo = gtk_combo_box_new_text ();
+	for (i = 0; i < G_N_ELEMENTS (chart_sizes); i++)
+		gtk_combo_box_append_text (GTK_COMBO_BOX (window->size_combo), _(chart_sizes[i]));
+	gtk_widget_set_sensitive (window->size_combo, FALSE);
+	gtk_widget_show (window->size_combo);
+	gtk_combo_box_set_active (GTK_COMBO_BOX (window->size_combo), CHART_SIZE_FIT);
+	gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (window->size_combo),
+					      size_combo_is_row_separator, NULL, NULL);
+	gtk_container_add (GTK_CONTAINER (item), window->size_combo);
+	g_signal_connect_swapped (window->size_combo, "changed",
+				  G_CALLBACK (update_graph_sizing_mode), window);
+
+	item = gtk_tool_button_new_from_stock (GTK_STOCK_FULLSCREEN);
+	gtk_widget_show (GTK_WIDGET (item));
+	gtk_toolbar_insert (GTK_TOOLBAR (window->toolbar), item, -1);
+	g_signal_connect (item, "clicked",
+			  G_CALLBACK (fullscreen_button_clicked), window);
+
+	gtk_window_set_title (GTK_WINDOW (window), "Chart Viewer");
+}
+
+static void
+gnm_graph_window_class_init (GnmGraphWindowClass *class)
+{
+}
+
+static void
+gnm_graph_window_set_graph (GnmGraphWindow *window,
+			    GogGraph       *graph,
+			    gdouble         graph_width,
+			    gdouble         graph_height)
+{
+	GtkRequisition toolbar_requisition;
+	GogGraph *old_graph =
+		window->graph != NULL ?
+		go_graph_widget_get_graph (GO_GRAPH_WIDGET (window->graph)) :
+		NULL;
+
+	if (graph == old_graph)
+		return;
+
+	if (old_graph != NULL) {
+		gtk_container_remove (GTK_CONTAINER (window->scrolled_window), window->graph);
+		g_object_unref (window->graph);
+		window->graph = NULL;
+	}
+
+	if (graph != NULL) {
+		window->graph = go_graph_widget_new (graph);
+		gtk_widget_show (window->graph);
+		gtk_container_add (GTK_CONTAINER (window->scrolled_window), window->graph);
+
+		gtk_widget_size_request (window->toolbar, &toolbar_requisition);
+		gtk_window_set_default_size (GTK_WINDOW (window), 
+					     (int) graph_width,
+					     (int) graph_height + toolbar_requisition.height);
+
+		window->graph_width = graph_width;
+		window->graph_height = graph_height;
+
+		/* ensure that the aspect ratio is updated */
+		gtk_widget_set_sensitive (window->size_combo, TRUE);
+		g_signal_emit_by_name (window->size_combo, "changed");
+	}
+}
+
+GtkWidget *
+gnm_graph_window_new (GogGraph *graph,
+		      gdouble   graph_width,
+		      gdouble   graph_height)
+{
+	GtkWidget *ret;
+
+	ret = g_object_new (gnm_graph_window_get_type (), NULL);
+	gnm_graph_window_set_graph (GNM_GRAPH_WINDOW (ret), graph, graph_width, graph_height);
+
+	return ret;
+}
Index: src/gnm-graph-window.h
===================================================================
RCS file: src/gnm-graph-window.h
diff -N src/gnm-graph-window.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/gnm-graph-window.h	17 Apr 2006 21:07:21 -0000
@@ -0,0 +1,23 @@
+#ifndef GNM_GRAPH_WINDOW_H
+#define GNM_GRAPH_WINDOW_H
+
+#include <gtk/gtkwidget.h>
+#include <goffice/graph/gog-graph.h>
+
+#define GNM_TYPE_GRAPH_WINDOW             (gnm_graph_window_get_type ())
+#define GNM_GRAPH_WINDOW(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNM_TYPE_GRAPH_WINDOW, GnmGraphWindow))
+#define GNM_GRAPH_WINDOW_CLASS(vtable)    (G_TYPE_CHECK_CLASS_CAST ((vtable), GNM_TYPE_GRAPH_WINDOW, GnmGraphWindowClass))
+#define IS_GNM_GRAPH_WINDOW(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNM_TYPE_GRAPH_WINDOW))
+#define IS_GNM_GRAPH_WINDOW_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), GNM_TYPE_GRAPH_WINDOW))
+#define GNM_GRAPH_WINDOW_GET_CLASS(inst)  (G_TYPE_INSTANCE_GET_CLASS ((inst), GNM_TYPE_GRAPH_WINDOW, GnmGraphWindowClass))
+
+typedef struct _GnmGraphWindow      GnmGraphWindow;
+typedef struct _GnmGraphWindowClass GnmGraphWindowClass;
+
+GType      gnm_graph_window_get_type (void);
+
+GtkWidget *gnm_graph_window_new (GogGraph *graph,
+				 double    graph_width,
+				 double    graph_height);
+
+#endif /* GNM_GRAPH_WINDOW_H */
Index: src/sheet-object-graph.c
===================================================================
RCS file: /cvs/gnome/gnumeric/src/sheet-object-graph.c,v
retrieving revision 1.75
diff -u -p -r1.75 sheet-object-graph.c
--- src/sheet-object-graph.c	12 Dec 2005 20:13:43 -0000	1.75
+++ src/sheet-object-graph.c	17 Apr 2006 21:07:24 -0000
@@ -30,6 +30,7 @@
 #include "str.h"
 #include "gui-util.h"
 #include "gui-file.h"
+#include "gnm-graph-window.h"
 #include "style-color.h"
 #include "sheet-object-impl.h"
 #include "workbook-edit.h"
@@ -53,6 +54,7 @@
 #include <goffice/utils/go-glib-extras.h>
 #include <goffice/utils/go-format.h>
 #include <goffice/app/go-cmd-context.h>
+#include <goffice/gtk/go-graph-widget.h>
 
 #include <gsf/gsf-impl-utils.h>
 #include <gsf/gsf-utils.h>
@@ -61,6 +63,7 @@
 #include <gdk/gdkkeysyms.h>
 #include <gtk/gtkimagemenuitem.h>
 #include <gtk/gtkstock.h>
+#include <gtk/gtkwindow.h>
 #include <goffice/cut-n-paste/foocanvas/foo-canvas-line.h>
 #include <goffice/cut-n-paste/foocanvas/foo-canvas-rect-ellipse.h>
 #include <goffice/cut-n-paste/foocanvas/foo-canvas-polygon.h>
@@ -322,14 +325,42 @@ out:
 }
 
 static void
+sog_cb_open_in_new_window (SheetObject *so, SheetControl *sc)
+{
+ 	SheetObjectGraph *sog = SHEET_OBJECT_GRAPH (so);
+	SheetControlGUI *scg = SHEET_CONTROL_GUI (sc);
+ 	GtkWidget *window;
+	double coords[4];
+ 
+ 	g_return_if_fail (sog != NULL);
+
+	scg_object_anchor_to_coords (scg, sheet_object_get_anchor (so), coords);
+ 	window = gnm_graph_window_new (sog->graph,
+				       floor (fabs (coords[2] - coords[0]) + 0.5),
+				       floor (fabs (coords[3] - coords[1]) + 0.5));
+ 	gtk_window_present (GTK_WINDOW (window));
+	g_signal_connect (window, "delete-event",
+			  G_CALLBACK (gtk_widget_destroy),
+			  NULL);
+}
+
+static void
 gnm_sog_populate_menu (SheetObject *so, GPtrArray *actions)
 {
-	static SheetObjectAction const sog_action =
-		{ GTK_STOCK_SAVE_AS, N_("_Save as image"), NULL, 0, sog_cb_save_as };
+ 	static SheetObjectAction const sog_actions[] = {
+ 		{ GTK_STOCK_SAVE_AS, N_("_Save as Image"),      NULL, 0, sog_cb_save_as },
+ 		{ NULL,              N_("Open in _New Window"), NULL, 0, sog_cb_open_in_new_window }
+ 	};
+
+ 	unsigned int i;
+
 	SHEET_OBJECT_CLASS (parent_klass)->populate_menu (so, actions);
-	go_ptr_array_insert (actions, (gpointer) &sog_action, 1);
-}
 
+ 	for (i = 0; i < G_N_ELEMENTS (sog_actions); i++)
+ 		go_ptr_array_insert (actions, (gpointer) (sog_actions + i), 1 + i);
+ 
+}
+  
 static gboolean
 gnm_sog_read_xml_dom (SheetObject *so, char const *typename,
 				 XmlParseContext const *ctxt, xmlNodePtr tree)
Index: goffice/gtk/go-graph-widget.c
===================================================================
RCS file: /cvs/gnome/goffice/goffice/gtk/go-graph-widget.c,v
retrieving revision 1.4
diff -u -p -r1.4 go-graph-widget.c
--- goffice/gtk/go-graph-widget.c	18 Nov 2005 15:50:56 -0000	1.4
+++ goffice/gtk/go-graph-widget.c	17 Apr 2006 20:25:14 -0000
@@ -20,56 +20,106 @@
 
 #include <goffice-config.h>
 #include "go-graph-widget.h"
-#include <gtk/gtkdrawingarea.h>
+#include <gtk/gtklayout.h>
+#include <goffice/graph/gog-graph.h>
 #include <goffice/graph/gog-object.h>
 #include <goffice/graph/gog-renderer.h>
 #include <goffice/utils/go-math.h>
 
 #include <gsf/gsf-impl-utils.h>
 
+static void go_graph_widget_request_update (GOGraphWidget *w);
+
 enum {
 	GRAPH_WIDGET_PROP_0,
 	GRAPH_WIDGET_PROP_ASPECT_RATIO,
+	GRAPH_WIDGET_PROP_GRAPH
 };
 
 struct  _GOGraphWidget{
-	GtkDrawingArea	base;
+	GtkLayout base;
 
 	GogRenderer *renderer;
 	GogGraph *graph;
 	GogChart *chart; /* first chart created on init */
-	double aspect_ratio, width, height, xoffset, yoffset;
+	double aspect_ratio;
+	guint width, height, xoffset, yoffset;
+	int requested_width, requested_height;
+	int button_press_x, button_press_y;
+	gboolean button_pressed;
 
-	/* Idle handler ID */
-	guint idle_id;
+	GOGraphWidgetSizeMode size_mode;
 };
 
-typedef GtkDrawingAreaClass GOGraphWidgetClass;
+struct _GOGraphWidgetClass {
+	GtkLayoutClass base_class;
+};
 
 static GtkWidgetClass *graph_parent_klass;
 
-/* Size allocation handler for the widget */
 static void
-go_graph_widget_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
+update_image_rect (GOGraphWidget *gw,
+		   GtkAllocation allocation)
 {
-	GOGraphWidget *w = GO_GRAPH_WIDGET (widget);
-	w->width = allocation->width;
-	w->height = allocation->height;
-	if (w->aspect_ratio > 0.) {
-		if (w->height > w->width * w->aspect_ratio) {
-			w->yoffset = (w->height - w->width * w->aspect_ratio) / 2.;
-			w->height = w->width * w->aspect_ratio;
-			w->xoffset = 0;
+	gw->width = gw->height = -1;
+
+	switch (gw->size_mode) {
+		case GO_GRAPH_WIDGET_SIZE_MODE_FIT:
+			gw->width = allocation.width;
+			gw->height = allocation.height;
+			break;
+
+		case GO_GRAPH_WIDGET_SIZE_MODE_FIT_WIDTH:
+			gw->width = allocation.width;
+			break;
+
+		case GO_GRAPH_WIDGET_SIZE_MODE_FIT_HEIGHT:
+			gw->height = allocation.height;
+			break;
+
+		case GO_GRAPH_WIDGET_SIZE_MODE_FIXED_SIZE:
+			gw->width = gw->requested_width;
+			gw->height = gw->requested_height;
+			break;
+
+		default:
+			g_assert_not_reached ();
+	}
+
+	if (gw->aspect_ratio > 0.) {
+		g_assert (gw->size_mode != GO_GRAPH_WIDGET_SIZE_MODE_FIXED_SIZE);
+
+		if ((gw->size_mode == GO_GRAPH_WIDGET_SIZE_MODE_FIT &&
+		     gw->height > gw->width * gw->aspect_ratio) ||
+		    gw->size_mode == GO_GRAPH_WIDGET_SIZE_MODE_FIT_WIDTH) {
+			gw->height = gw->width * gw->aspect_ratio;
 		} else {
-			w->xoffset = (w->width - w->height / w->aspect_ratio) / 2.;
-			w->width = w->height / w->aspect_ratio;
-			w->yoffset = 0;
+			gw->width = gw->height / gw->aspect_ratio;
 		}
 	}
-	gog_renderer_update (w->renderer, w->width, w->height, 1.0);
-	graph_parent_klass->size_allocate (widget, allocation);
+
+	gw->yoffset = MAX (0, (int) (allocation.height - gw->height) / 2);
+	gw->xoffset = MAX (0, (int) (allocation.width - gw->width) / 2);
+
+	gog_renderer_update (gw->renderer, gw->width, gw->height, 1.0);
 }
 
+
+/* Size allocation handler for the widget */
+static void
+go_graph_widget_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
+{
+	GOGraphWidget *w = GO_GRAPH_WIDGET (widget);
+
+	update_image_rect (GO_GRAPH_WIDGET (widget), *allocation);
+
+	GTK_LAYOUT (widget)->width = w->width + w->xoffset;
+	GTK_LAYOUT (widget)->height = w->height + w->yoffset;
+
+	GTK_WIDGET_CLASS (graph_parent_klass)->size_allocate (widget, allocation);
+}
+
+
 static gboolean
 go_graph_widget_expose_event (GtkWidget *widget, GdkEventExpose *event)
 {
@@ -78,8 +128,9 @@ go_graph_widget_expose_event (GtkWidget 
 	GdkRectangle display_rect, draw_rect;
 	GdkRegion *draw_region;
 
-	if (w->idle_id)
-		return TRUE;
+	if (event->window != GTK_LAYOUT (widget)->bin_window)
+		return FALSE;
+
 	pixbuf = gog_renderer_get_pixbuf (w->renderer);
 	display_rect.x = w->xoffset;
 	display_rect.y = w->yoffset;
@@ -89,7 +140,7 @@ go_graph_widget_expose_event (GtkWidget 
 	gdk_region_intersect (draw_region, event->region);
 	if (!gdk_region_empty (draw_region)) {
 		gdk_region_get_clipbox (draw_region, &draw_rect);
-		gdk_draw_pixbuf (widget->window, NULL, pixbuf,
+		gdk_draw_pixbuf (GTK_LAYOUT (widget)->bin_window, NULL, pixbuf,
 			/* pixbuf 0, 0 is at pix_rect.x, pix_rect.y */
 			     draw_rect.x - display_rect.x,
 			     draw_rect.y - display_rect.y,
@@ -103,6 +154,80 @@ go_graph_widget_expose_event (GtkWidget 
 	return FALSE;
 }
 
+static gboolean
+go_graph_widget_button_press_event (GtkWidget *widget,
+				    GdkEventButton *event)
+{
+	GOGraphWidget *gw = GO_GRAPH_WIDGET (widget);
+
+	if (event->type == GDK_BUTTON_PRESS) {
+		gw->button_pressed = TRUE;
+
+		gdk_window_get_pointer (widget->window,
+					&gw->button_press_x,
+					&gw->button_press_y,
+					NULL);
+	}
+
+	if (GTK_WIDGET_CLASS (graph_parent_klass)->button_press_event != NULL) {
+		return (GTK_WIDGET_CLASS (graph_parent_klass))->button_press_event (widget, event);
+	}
+
+	return FALSE;
+}
+
+static gboolean
+go_graph_widget_button_release_event (GtkWidget *widget,
+				      GdkEventButton *event)
+{
+	GOGraphWidget *gw = GO_GRAPH_WIDGET (widget);
+
+	if (event->type == GDK_BUTTON_RELEASE) {
+		gw->button_pressed = FALSE;
+	}
+
+	if (GTK_WIDGET_CLASS (graph_parent_klass)->button_release_event != NULL) {
+		return (GTK_WIDGET_CLASS (graph_parent_klass))->button_release_event (widget, event);
+	}
+
+	return FALSE;
+}
+
+static gboolean
+go_graph_widget_motion_notify_event (GtkWidget *widget,
+				     GdkEventMotion *event)
+{
+	GOGraphWidget *gw = GO_GRAPH_WIDGET (widget);
+	int x, y;
+	double newval;
+
+	if (gw->button_pressed) {
+		gdk_window_get_pointer (widget->window,
+					&x, &y, NULL);
+
+		if (GTK_LAYOUT (gw)->hadjustment != NULL) {
+			newval = gtk_adjustment_get_value (GTK_LAYOUT (gw)->hadjustment) - (x - gw->button_press_x);
+			newval = CLAMP (newval, 0, GTK_LAYOUT (gw)->hadjustment->upper - GTK_LAYOUT (gw)->hadjustment->page_size);
+			gtk_adjustment_set_value (GTK_LAYOUT (gw)->hadjustment, newval);
+		}
+
+		if (GTK_LAYOUT (gw)->vadjustment != NULL) {
+			newval = gtk_adjustment_get_value (GTK_LAYOUT (gw)->vadjustment) - (y - gw->button_press_y);
+			newval = CLAMP (newval, 0, GTK_LAYOUT (gw)->vadjustment->upper - GTK_LAYOUT (gw)->vadjustment->page_size);
+			gtk_adjustment_set_value (GTK_LAYOUT (gw)->vadjustment, newval);
+		}
+
+		gw->button_press_x = x;
+		gw->button_press_y = y;
+	}
+
+	if (GTK_WIDGET_CLASS (graph_parent_klass)->motion_notify_event != NULL) {
+		return (GTK_WIDGET_CLASS (graph_parent_klass))->motion_notify_event (widget, event);
+	}
+
+	return FALSE;
+}
+
 static void
 go_graph_widget_finalize (GObject *object)
 {
@@ -113,6 +238,12 @@ go_graph_widget_finalize (GObject *objec
 }
 
 static void
+go_graph_widget_request_update (GOGraphWidget *w)
+{
+	update_image_rect (w, GTK_WIDGET (w)->allocation);
+}
+
+static void
 go_graph_widget_set_property (GObject *obj, guint param_id,
 			     GValue const *value, GParamSpec *pspec)
 {
@@ -121,7 +252,12 @@ go_graph_widget_set_property (GObject *o
 	switch (param_id) {
 	case GRAPH_WIDGET_PROP_ASPECT_RATIO :
 		w->aspect_ratio = g_value_get_double (value);
-		w->xoffset = w->yoffset = 0.;
+		break;
+	case GRAPH_WIDGET_PROP_GRAPH :
+		w->graph = (GogGraph *) g_value_dup_object (value);
+		w->renderer = gog_renderer_new_for_pixbuf (w->graph);
+		g_signal_connect_swapped (w->renderer, "request_update",
+			G_CALLBACK (go_graph_widget_request_update), w);
 		break;
 
 	default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
@@ -140,6 +276,9 @@ go_graph_widget_get_property (GObject *o
 	case GRAPH_WIDGET_PROP_ASPECT_RATIO :
 		g_value_set_double (value, w->aspect_ratio);
 		break;
+	case GRAPH_WIDGET_PROP_GRAPH :
+		g_value_set_object (value, w->graph);
+		break;
 
 	default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
 		 break;
@@ -159,66 +298,114 @@ go_graph_widget_class_init (GOGraphWidge
 	object_class->set_property = go_graph_widget_set_property;
 	widget_class->size_allocate = go_graph_widget_size_allocate;
 	widget_class->expose_event = go_graph_widget_expose_event;
+	widget_class->button_press_event = go_graph_widget_button_press_event;
+	widget_class->button_release_event = go_graph_widget_button_release_event;
+	widget_class->motion_notify_event = go_graph_widget_motion_notify_event;
 	g_object_class_install_property (object_class,
 		GRAPH_WIDGET_PROP_ASPECT_RATIO,
 		g_param_spec_double ("aspect-ratio", "aspect-ratio",
 			"Aspect ratio for rendering the graph, used only if greater than 0.",
-			-G_MAXDOUBLE, G_MAXDOUBLE, -1., G_PARAM_READWRITE));
-}
-
-static gint
-idle_handler (GOGraphWidget *w)
-{
-	GDK_THREADS_ENTER ();
-
-	gog_renderer_update (w->renderer, w->width, w->height, 1.0);
-
-	/* Reset idle id */
-	w->idle_id = 0;
-	gtk_widget_queue_draw (GTK_WIDGET (w));
-
-	GDK_THREADS_LEAVE ();
-
-	return FALSE;
+			-G_MAXDOUBLE, G_MAXDOUBLE, -1., G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	g_object_class_install_property (object_class,
+		GRAPH_WIDGET_PROP_GRAPH,
+		g_param_spec_object ("graph", "graph",
+			"The graph to render.",
+			gog_graph_get_type (), 
+			G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 }
 
 static void
-go_graph_widget_request_update (GOGraphWidget *w)
+go_graph_widget_init (GOGraphWidget *w)
 {
-	if (!w->idle_id)
-		w->idle_id = g_idle_add_full (GDK_PRIORITY_REDRAW - 20,
-						   (GSourceFunc) idle_handler, w, NULL);
+	gtk_widget_add_events (GTK_WIDGET (w), GDK_POINTER_MOTION_MASK |
+					       GDK_BUTTON_PRESS_MASK |
+					       GDK_BUTTON_RELEASE_MASK);
 }
 
-static void
-go_graph_widget_init (GOGraphWidget *w)
-{
-	w->graph = (GogGraph *) g_object_new (GOG_GRAPH_TYPE, NULL);
-	w->renderer = gog_renderer_new_for_pixbuf (w->graph);
-	g_signal_connect_swapped (w->renderer, "request_update",
-		G_CALLBACK (go_graph_widget_request_update), w);
-	/* by default, create one chart and add it to the graph */
-	w->chart = (GogChart *) 
-			gog_object_add_by_name (GOG_OBJECT (w->graph), "Chart", NULL);
-	w->idle_id = 0;
+/**
+ * go_graph_widget_set_size_mode :
+ *
+ * Sets the size mode of the #GOGraphWidget.
+ * It is used to determine the size and position of the drawn
+ * chart. The following sizing modes are supported:
+ *
+ * GO_GRAPH_WIDGET_SIZE_MODE_FIT, aspect ratio set.
+ * The aspect ratio is guaranteed to be maintained,
+ * i.e. the graph is never squeezed, and will
+ * always fit into the visible area.  
+ *
+ * GO_GRAPH_WIDGET_SIZE_MODE_FIT, no aspect ratio set.
+ * The aspect ratio is adapted to make the graph
+ * exactly fit into the visible area.
+ *
+ * GO_GRAPH_WIDGET_SIZE_MODE_FIT_WIDTH, aspect ratio set.
+ * The aspect ratio is guaranteed to be maintained,
+ * i.e. the graph is never squezzed, and will
+ * always occupy the whole width of the visible area.
+ *
+ * GO_GRAPH_WIDGET_SIZE_MODE_FIT_HEIGHT, aspect ratio set.
+ * The aspect ratio is guaranteed to be maintained,
+ * i.e. the graph is never squezzed, and will
+ * always occupy the whole height of the visible area.
+ *
+ * GO_GRAPH_WIDGET_SIZE_MODE_FIT_FIXED_SIZE, no aspect ratio set.
+ * The graph will occupy the area specified by width/height,
+ * its aspect ratio will be determined by height/width.
+ */
+void
+go_graph_widget_set_size_mode (GOGraphWidget         *widget,
+			       GOGraphWidgetSizeMode  size_mode,
+			       int                    width,
+			       int                    height)
+{
+	g_return_if_fail (IS_GO_GRAPH_WIDGET (widget));
+	g_return_if_fail (size_mode >= GO_GRAPH_WIDGET_SIZE_MODE_FIT &&
+			  size_mode <= GO_GRAPH_WIDGET_SIZE_MODE_FIXED_SIZE);
+	g_return_if_fail (!(width >= 0 && height < 0));
+	g_return_if_fail (!(width < 0 && height >= 0));
+	g_return_if_fail (!(width >= 0 && size_mode != GO_GRAPH_WIDGET_SIZE_MODE_FIXED_SIZE));
+	g_return_if_fail (!(width < 0 && size_mode == GO_GRAPH_WIDGET_SIZE_MODE_FIXED_SIZE));
+
+	widget->size_mode = size_mode;
+	widget->requested_width = width;
+	widget->requested_height = height;
+
+	update_image_rect (widget, GTK_WIDGET (widget)->allocation);
 }
 
 /**
  * go_graph_widget_new :
  * 
- * Creates a new #GOGraphWidget with an embedded #GogGraph. Also add a #GogChart inside
- * graph.
+ * Creates a new #GOGraphWidget with an embedded #GogGraph.
+ * If graph is NULL, the graph will be auto-created, and a
+ * #GogChart will be added to the graph.
  * Returns the newly created #GOGraphWidget.
  **/
 GtkWidget *
-go_graph_widget_new (void)
+go_graph_widget_new (GogGraph *graph)
 {
-	return GTK_WIDGET (g_object_new (GO_GRAPH_WIDGET_TYPE, NULL));
+	GtkWidget *ret;
+	gboolean self_owned = FALSE;
+
+	if (graph == NULL) {
+		self_owned = TRUE;
+
+		graph = (GogGraph *) g_object_new (GOG_GRAPH_TYPE, NULL);
+		gog_object_add_by_name (GOG_OBJECT (graph), "Chart", NULL);
+	}
+
+	ret = GTK_WIDGET (g_object_new (GO_GRAPH_WIDGET_TYPE, "graph", graph, NULL));
+	go_graph_widget_set_size_mode (GO_GRAPH_WIDGET (ret), GO_GRAPH_WIDGET_SIZE_MODE_FIT, -1, -1);
+
+	if (self_owned)
+		g_object_unref (G_OBJECT (graph));
+
+	return ret;
 }
 
 GSF_CLASS (GOGraphWidget, go_graph_widget,
 	   go_graph_widget_class_init, go_graph_widget_init,
-	   gtk_drawing_area_get_type ())
+	   gtk_layout_get_type ())
 
 /**
  * go_graph_widget_get_graph :
@@ -231,17 +418,4 @@ go_graph_widget_get_graph (GOGraphWidget
 {
 	g_return_val_if_fail (IS_GO_GRAPH_WIDGET (widget), NULL);
 	return widget->graph;
-}
-
-/**
- * go_graph_widget_get_chart :
- * @widget : #GOGraphWidget
- * 
- * Returns the #GogChart created by go_graph_widget_new().
- **/
-GogChart *
-go_graph_widget_get_chart (GOGraphWidget *widget)
-{
-	g_return_val_if_fail (IS_GO_GRAPH_WIDGET (widget), NULL);
-	return widget->chart;
 }
Index: goffice/gtk/go-graph-widget.h
===================================================================
RCS file: /cvs/gnome/goffice/goffice/gtk/go-graph-widget.h,v
retrieving revision 1.2
diff -u -p -r1.2 go-graph-widget.h
--- goffice/gtk/go-graph-widget.h	8 Aug 2005 08:57:00 -0000	1.2
+++ goffice/gtk/go-graph-widget.h	17 Apr 2006 20:25:14 -0000
@@ -32,10 +32,25 @@ G_BEGIN_DECLS
 #define GO_GRAPH_WIDGET(o)	(G_TYPE_CHECK_INSTANCE_CAST ((o), GO_GRAPH_WIDGET_TYPE, GOGraphWidget))
 #define IS_GO_GRAPH_WIDGET(o)	(G_TYPE_CHECK_INSTANCE_TYPE ((o), GO_GRAPH_WIDGET_TYPE))
 
-typedef struct _GOGraphWidget GOGraphWidget;
+typedef struct _GOGraphWidget      GOGraphWidget;
+typedef struct _GOGraphWidgetClass GOGraphWidgetClass;
+
+typedef enum {
+	GO_GRAPH_WIDGET_SIZE_MODE_FIT = 0,
+	GO_GRAPH_WIDGET_SIZE_MODE_FIT_WIDTH,
+	GO_GRAPH_WIDGET_SIZE_MODE_FIT_HEIGHT,
+	GO_GRAPH_WIDGET_SIZE_MODE_FIXED_SIZE
+} GOGraphWidgetSizeMode;
+
+GType go_graph_widget_reference_direction_get_type (void);
 
 GType go_graph_widget_get_type (void);
-GtkWidget *go_graph_widget_new (void);
+GtkWidget *go_graph_widget_new (GogGraph *graph);
+
+void go_graph_widget_set_size_mode (GOGraphWidget         *widget,
+				    GOGraphWidgetSizeMode  size_mode,
+				    int                    width,
+				    int                    height);
 
 GogGraph *go_graph_widget_get_graph (GOGraphWidget *widget);
 GogChart *go_graph_widget_get_chart (GOGraphWidget *widget);
_______________________________________________
gnumeric-list mailing list
[email protected]
http://mail.gnome.org/mailman/listinfo/gnumeric-list

Reply via email to