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 #include "go-graph-widget.h" -#include +#include +#include #include #include #include #include +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);