diff options
Diffstat (limited to 'src/goocanvas/src')
24 files changed, 3821 insertions, 532 deletions
diff --git a/src/goocanvas/src/goocanvas.c b/src/goocanvas/src/goocanvas.c index 2eb41b3..f59bbdf 100644 --- a/src/goocanvas/src/goocanvas.c +++ b/src/goocanvas/src/goocanvas.c @@ -105,6 +105,17 @@ #include "goocanvasmarshal.h" +#define GOO_CANVAS_GET_PRIVATE(canvas) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((canvas), GOO_TYPE_CANVAS, GooCanvasPrivate)) + +typedef struct _GooCanvasPrivate GooCanvasPrivate; +struct _GooCanvasPrivate { + GooCanvasItem *static_root_item; + GooCanvasItemModel *static_root_item_model; + gint window_x, window_y; +}; + + enum { PROP_0, @@ -125,7 +136,8 @@ enum { PROP_BACKGROUND_COLOR, PROP_BACKGROUND_COLOR_RGB, PROP_INTEGER_LAYOUT, - PROP_CLEAR_BACKGROUND + PROP_CLEAR_BACKGROUND, + PROP_REDRAW_WHEN_SCROLLED }; enum { @@ -134,6 +146,7 @@ enum { LAST_SIGNAL }; + static guint canvas_signals[LAST_SIGNAL] = { 0 }; static void goo_canvas_dispose (GObject *object); @@ -188,6 +201,11 @@ static void goo_canvas_forall (GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data); +static gboolean goo_canvas_query_tooltip (GtkWidget *widget, + gint x, + gint y, + gboolean keyboard_tip, + GtkTooltip *tooltip); static void goo_canvas_set_scale_internal (GooCanvas *canvas, gdouble scale_x, @@ -201,6 +219,9 @@ static void reconfigure_canvas (GooCanvas *canvas, gboolean redraw_if_needed); static void goo_canvas_update_automatic_bounds (GooCanvas *canvas); +static void goo_canvas_convert_to_static_item_space (GooCanvas *canvas, + gdouble *x, + gdouble *y); G_DEFINE_TYPE (GooCanvas, goo_canvas, GTK_TYPE_CONTAINER) @@ -217,6 +238,8 @@ goo_canvas_class_init (GooCanvasClass *klass) GtkWidgetClass *widget_class = (GtkWidgetClass*) klass; GtkContainerClass *container_class = (GtkContainerClass*) klass; + g_type_class_add_private (gobject_class, sizeof (GooCanvasPrivate)); + gobject_class->dispose = goo_canvas_dispose; gobject_class->finalize = goo_canvas_finalize; gobject_class->get_property = goo_canvas_get_property; @@ -241,6 +264,7 @@ goo_canvas_class_init (GooCanvasClass *klass) widget_class->focus_in_event = goo_canvas_focus_in; widget_class->focus_out_event = goo_canvas_focus_out; widget_class->grab_broken_event = goo_canvas_grab_broken; + widget_class->query_tooltip = goo_canvas_query_tooltip; container_class->remove = goo_canvas_remove; container_class->forall = goo_canvas_forall; @@ -393,6 +417,13 @@ goo_canvas_class_init (GooCanvasClass *klass) TRUE, G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, PROP_REDRAW_WHEN_SCROLLED, + g_param_spec_boolean ("redraw-when-scrolled", + _("Redraw When Scrolled"), + _("If the canvas is completely redrawn when scrolled, to reduce the flicker of static items"), + FALSE, + G_PARAM_READWRITE)); + /** * GooCanvas::set-scroll-adjustments * @canvas: the canvas. @@ -443,6 +474,8 @@ goo_canvas_class_init (GooCanvasClass *klass) static void goo_canvas_init (GooCanvas *canvas) { + GooCanvasPrivate *priv = GOO_CANVAS_GET_PRIVATE (canvas); + /* We set GTK_CAN_FOCUS by default, so it works as people expect. Though developers can turn this off if not needed for efficiency. */ GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS); @@ -455,6 +488,7 @@ goo_canvas_init (GooCanvas *canvas) canvas->crossing_event.type = GDK_LEAVE_NOTIFY; canvas->anchor = GTK_ANCHOR_NORTH_WEST; canvas->clear_background = TRUE; + canvas->redraw_when_scrolled = FALSE; /* Set the default bounds to a reasonable size. */ canvas->bounds.x1 = 0.0; @@ -485,6 +519,14 @@ goo_canvas_init (GooCanvas *canvas) time. Apps can set their own root item if required. */ canvas->root_item = goo_canvas_group_new (NULL, NULL); goo_canvas_item_set_canvas (canvas->root_item, canvas); + + priv->static_root_item = goo_canvas_group_new (NULL, NULL); + goo_canvas_item_set_canvas (priv->static_root_item, canvas); + goo_canvas_item_set_is_static (priv->static_root_item, TRUE); + priv->static_root_item_model = NULL; + + priv->window_x = 0; + priv->window_y = 0; } @@ -509,6 +551,7 @@ static void goo_canvas_dispose (GObject *object) { GooCanvas *canvas = (GooCanvas*) object; + GooCanvasPrivate *priv = GOO_CANVAS_GET_PRIVATE (canvas); if (canvas->model_to_item) { @@ -528,6 +571,18 @@ goo_canvas_dispose (GObject *object) canvas->root_item_model = NULL; } + if (priv->static_root_item) + { + g_object_unref (priv->static_root_item); + priv->static_root_item = NULL; + } + + if (priv->static_root_item_model) + { + g_object_unref (priv->static_root_item_model); + priv->static_root_item_model = NULL; + } + if (canvas->idle_id) { g_source_remove (canvas->idle_id); @@ -703,6 +758,9 @@ goo_canvas_get_property (GObject *object, case PROP_CLEAR_BACKGROUND: g_value_set_boolean (value, canvas->clear_background); break; + case PROP_REDRAW_WHEN_SCROLLED: + g_value_set_boolean (value, canvas->redraw_when_scrolled); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -806,6 +864,9 @@ goo_canvas_set_property (GObject *object, case PROP_CLEAR_BACKGROUND: canvas->clear_background = g_value_get_boolean (value); break; + case PROP_REDRAW_WHEN_SCROLLED: + canvas->redraw_when_scrolled = g_value_get_boolean (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -954,6 +1015,170 @@ goo_canvas_set_root_item (GooCanvas *canvas, /** + * goo_canvas_get_static_root_item: + * @canvas: a #GooCanvas. + * + * Gets the static root item of the canvas. + * + * Static items are exactly the same as ordinary canvas items, except that + * they do not move or change size when the canvas is scrolled or the scale + * changes. + * + * Static items are added to the static root item in exactly the same way that + * ordinary items are added to the root item. + * + * Returns: the static root item, or %NULL. + **/ +GooCanvasItem* +goo_canvas_get_static_root_item (GooCanvas *canvas) +{ + g_return_val_if_fail (GOO_IS_CANVAS (canvas), NULL); + + return GOO_CANVAS_GET_PRIVATE (canvas)->static_root_item; +} + + +/** + * goo_canvas_set_static_root_item: + * @canvas: a #GooCanvas. + * @item: the static root item. + * + * Sets the static root item. Any existing static items are removed. + * + * Static items are exactly the same as ordinary canvas items, except that + * they do not move or change size when the canvas is scrolled or the scale + * changes. + * + * Static items are added to the static root item in exactly the same way that + * ordinary items are added to the root item. + **/ +void +goo_canvas_set_static_root_item (GooCanvas *canvas, + GooCanvasItem *item) +{ + GooCanvasPrivate *priv; + + g_return_if_fail (GOO_IS_CANVAS (canvas)); + g_return_if_fail (GOO_IS_CANVAS_ITEM (item)); + + priv = GOO_CANVAS_GET_PRIVATE (canvas); + + if (priv->static_root_item == item) + return; + + /* Remove any current model. */ + if (priv->static_root_item_model) + { + g_object_unref (priv->static_root_item_model); + priv->static_root_item_model = NULL; + } + + if (priv->static_root_item) + g_object_unref (priv->static_root_item); + + priv->static_root_item = g_object_ref (item); + goo_canvas_item_set_canvas (priv->static_root_item, canvas); + goo_canvas_item_set_is_static (priv->static_root_item, TRUE); + + canvas->need_update = TRUE; + + if (GTK_WIDGET_REALIZED (canvas)) + goo_canvas_update (canvas); + + gtk_widget_queue_draw (GTK_WIDGET (canvas)); +} + + +/** + * goo_canvas_get_static_root_item_model: + * @canvas: a #GooCanvas. + * + * Gets the static root item model of the canvas. + * + * Static item models are exactly the same as ordinary item models, except that + * the corresponding items do not move or change size when the canvas is + * scrolled or the scale changes. + * + * Static items models are added to the static root item model in exactly the + * same way that ordinary item models are added to the root item model. + * + * Returns: the static root item model, or %NULL. + **/ +GooCanvasItemModel* +goo_canvas_get_static_root_item_model (GooCanvas *canvas) +{ + g_return_val_if_fail (GOO_IS_CANVAS (canvas), NULL); + + return GOO_CANVAS_GET_PRIVATE (canvas)->static_root_item_model; +} + + +/** + * goo_canvas_set_static_root_item_model: + * @canvas: a #GooCanvas. + * @model: the static root item model. + * + * Sets the static root item model. Any existing static item models are + * removed. + * + * Static item models are exactly the same as ordinary item models, except that + * the corresponding items do not move or change size when the canvas is + * scrolled or the scale changes. + * + * Static items models are added to the static root item model in exactly the + * same way that ordinary item models are added to the root item model. + **/ +void +goo_canvas_set_static_root_item_model (GooCanvas *canvas, + GooCanvasItemModel *model) +{ + GooCanvasPrivate *priv; + + g_return_if_fail (GOO_IS_CANVAS (canvas)); + g_return_if_fail (GOO_IS_CANVAS_ITEM_MODEL (model)); + + priv = GOO_CANVAS_GET_PRIVATE (canvas); + + if (priv->static_root_item_model == model) + return; + + if (priv->static_root_item_model) + { + g_object_unref (priv->static_root_item_model); + priv->static_root_item_model = NULL; + } + + if (priv->static_root_item) + { + g_object_unref (priv->static_root_item); + priv->static_root_item = NULL; + } + + if (model) + { + priv->static_root_item_model = g_object_ref (model); + + /* Create a hierarchy of canvas items for all the items in the model. */ + priv->static_root_item = goo_canvas_create_item (canvas, model); + } + else + { + /* The model has been reset so we go back to a default root group. */ + priv->static_root_item = goo_canvas_group_new (NULL, NULL); + } + + goo_canvas_item_set_canvas (priv->static_root_item, canvas); + goo_canvas_item_set_is_static (priv->static_root_item, TRUE); + canvas->need_update = TRUE; + + if (GTK_WIDGET_REALIZED (canvas)) + goo_canvas_update (canvas); + + gtk_widget_queue_draw (GTK_WIDGET (canvas)); +} + + +/** * goo_canvas_get_item: * @canvas: a #GooCanvas. * @model: a #GooCanvasItemModel. @@ -1014,19 +1239,30 @@ goo_canvas_get_item_at (GooCanvas *canvas, gdouble y, gboolean is_pointer_event) { + GooCanvasPrivate *priv; cairo_t *cr; GooCanvasItem *result = NULL; - GList *list; + GList *list = NULL; g_return_val_if_fail (GOO_IS_CANVAS (canvas), NULL); - /* If no root item is set, just return NULL. */ - if (!canvas->root_item) - return NULL; - + priv = GOO_CANVAS_GET_PRIVATE (canvas); cr = goo_canvas_create_cairo_context (canvas); - list = goo_canvas_item_get_items_at (canvas->root_item, x, y, cr, - is_pointer_event, TRUE, NULL); + + if (canvas->root_item) + list = goo_canvas_item_get_items_at (canvas->root_item, x, y, cr, + is_pointer_event, TRUE, NULL); + + if (!list && priv->static_root_item) + { + gdouble static_x = x, static_y = y; + + goo_canvas_convert_to_static_item_space (canvas, &static_x, &static_y); + list = goo_canvas_item_get_items_at (priv->static_root_item, + static_x, static_y, cr, + is_pointer_event, TRUE, NULL); + } + cairo_destroy (cr); /* We just return the top item in the list. */ @@ -1059,18 +1295,29 @@ goo_canvas_get_items_at (GooCanvas *canvas, gdouble y, gboolean is_pointer_event) { + GooCanvasPrivate *priv; cairo_t *cr; - GList *result; + GList *result = NULL; g_return_val_if_fail (GOO_IS_CANVAS (canvas), NULL); - /* If no root item is set, just return NULL. */ - if (!canvas->root_item) - return NULL; - + priv = GOO_CANVAS_GET_PRIVATE (canvas); cr = goo_canvas_create_cairo_context (canvas); - result = goo_canvas_item_get_items_at (canvas->root_item, x, y, cr, - is_pointer_event, TRUE, NULL); + + if (canvas->root_item) + result = goo_canvas_item_get_items_at (canvas->root_item, x, y, cr, + is_pointer_event, TRUE, NULL); + + if (priv->static_root_item) + { + gdouble static_x = x, static_y = y; + + goo_canvas_convert_to_static_item_space (canvas, &static_x, &static_y); + result = goo_canvas_item_get_items_at (priv->static_root_item, + static_x, static_y, cr, + is_pointer_event, TRUE, result); + } + cairo_destroy (cr); return result; @@ -1181,6 +1428,7 @@ static void goo_canvas_realize (GtkWidget *widget) { GooCanvas *canvas; + GooCanvasPrivate *priv; GdkWindowAttr attributes; gint attributes_mask; gint width_pixels, height_pixels; @@ -1189,6 +1437,7 @@ goo_canvas_realize (GtkWidget *widget) g_return_if_fail (GOO_IS_CANVAS (widget)); canvas = GOO_CANVAS (widget); + priv = GOO_CANVAS_GET_PRIVATE (canvas); GTK_WIDGET_SET_FLAGS (canvas, GTK_REALIZED); attributes.window_type = GDK_WINDOW_CHILD; @@ -1228,6 +1477,9 @@ goo_canvas_realize (GtkWidget *widget) | GDK_FOCUS_CHANGE_MASK | gtk_widget_get_events (widget); + priv->window_x = attributes.x; + priv->window_y = attributes.y; + canvas->canvas_window = gdk_window_new (widget->window, &attributes, attributes_mask); gdk_window_set_user_data (canvas->canvas_window, widget); @@ -1440,6 +1692,74 @@ recalculate_scales (GooCanvas *canvas) } +static void +request_static_redraw (GooCanvas *canvas, + const GooCanvasBounds *bounds) +{ + GooCanvasPrivate *priv = GOO_CANVAS_GET_PRIVATE (canvas); + GdkRectangle rect; + + if (!GTK_WIDGET_DRAWABLE (canvas) || (bounds->x1 == bounds->x2)) + return; + + /* We subtract one from the left & top edges, in case anti-aliasing makes + the drawing use an extra pixel. */ + rect.x = (double) bounds->x1 - priv->window_x - 1; + rect.y = (double) bounds->y1 - priv->window_y - 1; + + /* We add an extra one here for the same reason. (The other extra one is to + round up to the next pixel.) And one for luck! */ + rect.width = (double) bounds->x2 - priv->window_x - rect.x + 2 + 1; + rect.height = (double) bounds->y2 - priv->window_y - rect.y + 2 + 1; + + gdk_window_invalidate_rect (canvas->canvas_window, &rect, FALSE); +} + + +/* This requests a redraw of all the toplevel static items at their current + position, but redraws them at their given new position. + We redraw one item at a time to avoid GTK+ merging the rectangles into + one big one. */ +static void +redraw_static_items_at_position (GooCanvas *canvas, + gint x, + gint y) +{ + GooCanvasPrivate *priv = GOO_CANVAS_GET_PRIVATE (canvas); + GooCanvasBounds bounds; + GooCanvasItem *item; + gint n_children, i, window_x_copy, window_y_copy; + + if (!priv->static_root_item) + return; + + window_x_copy = priv->window_x; + window_y_copy = priv->window_y; + + n_children = goo_canvas_item_get_n_children (priv->static_root_item); + for (i = 0; i < n_children; i++) + { + item = goo_canvas_item_get_child (priv->static_root_item, i); + + /* Get the bounds of all the static items, relative to the window. */ + goo_canvas_item_get_bounds (item, &bounds); + + /* Request a redraw of the old position. */ + request_static_redraw (canvas, &bounds); + + /* Redraw the item in its new position. */ + priv->window_x = x; + priv->window_y = y; + + gdk_window_process_updates (canvas->canvas_window, TRUE); + + /* Now reset the window position. */ + priv->window_x = window_x_copy; + priv->window_y = window_y_copy; + } +} + + /* This makes sure the canvas is all set up correctly, i.e. the scrollbar adjustments are set, the canvas x & y offsets are calculated, and the canvas window is sized. */ @@ -1648,18 +1968,52 @@ static void goo_canvas_adjustment_value_changed (GtkAdjustment *adjustment, GooCanvas *canvas) { + GooCanvasPrivate *priv = GOO_CANVAS_GET_PRIVATE (canvas); AtkObject *accessible; if (!canvas->freeze_count && GTK_WIDGET_REALIZED (canvas)) { + if (canvas->redraw_when_scrolled) + { + /* Map the temporary window to stop the canvas window being scrolled. + When it is unmapped the entire canvas will be redrawn. */ + if (GTK_WIDGET_MAPPED (canvas)) + gdk_window_show (canvas->tmp_window); + } + else + { + /* Redraw the area currently occupied by the static items. But + draw the static items in their new position. This stops them + from being "dragged" when the window is scrolled. */ + redraw_static_items_at_position (canvas, + -canvas->hadjustment->value, + -canvas->hadjustment->value); + + /* Move the static items to the new position. */ + priv->window_x = -canvas->hadjustment->value; + priv->window_y = -canvas->vadjustment->value; + } + gdk_window_move (canvas->canvas_window, - canvas->hadjustment->value, - canvas->vadjustment->value); - - /* If this is callback from a signal for one of the scrollbars, process - updates here for smoother scrolling. */ - if (adjustment) - gdk_window_process_updates (canvas->canvas_window, TRUE); + + if (canvas->redraw_when_scrolled) + { + /* Unmap the temporary window, causing the entire canvas to be + redrawn. */ + if (GTK_WIDGET_MAPPED (canvas)) + gdk_window_hide (canvas->tmp_window); + } + else + { + /* Process updates here for smoother scrolling. */ + gdk_window_process_updates (canvas->canvas_window, TRUE); + + /* Now ensure the static items are redrawn in their new position. */ + redraw_static_items_at_position (canvas, priv->window_x, + priv->window_y); + } /* Notify any accessibility modules that the view has changed. */ accessible = gtk_widget_get_accessible (GTK_WIDGET (canvas)); @@ -1865,6 +2219,10 @@ goo_canvas_scroll_to_item (GooCanvas *canvas, GooCanvasBounds bounds; gdouble hvalue, vvalue; + /* We can't scroll to static items. */ + if (goo_canvas_item_get_is_static (item)) + return; + goo_canvas_item_get_bounds (item, &bounds); goo_canvas_convert_to_pixels (canvas, &bounds.x1, &bounds.y1); @@ -2095,7 +2453,8 @@ static void goo_canvas_update_internal (GooCanvas *canvas, cairo_t *cr) { - GooCanvasBounds bounds; + GooCanvasPrivate *priv = GOO_CANVAS_GET_PRIVATE (canvas); + GooCanvasBounds bounds, static_bounds; /* It is possible that processing the first set of updates causes other updates to be scheduled, so we loop round until all are done. Items @@ -2108,6 +2467,10 @@ goo_canvas_update_internal (GooCanvas *canvas, canvas->need_entire_subtree_update = FALSE; if (canvas->root_item) goo_canvas_item_update (canvas->root_item, entire_tree, cr, &bounds); + + if (priv->static_root_item) + goo_canvas_item_update (priv->static_root_item, entire_tree, cr, + &static_bounds); } /* If the bounds are automatically-calculated, update them now. */ @@ -2189,12 +2552,13 @@ goo_canvas_request_update (GooCanvas *canvas) /** * goo_canvas_request_redraw: * @canvas: a #GooCanvas. - * @bounds: the bounds to redraw. + * @bounds: the bounds to redraw, in device space. * * This function is only intended to be used by subclasses of #GooCanvas or * #GooCanvasItem implementations. * - * Requests that the given bounds be redrawn. + * Requests that the given bounds be redrawn. The bounds must be in the canvas + * coordinate space. **/ void goo_canvas_request_redraw (GooCanvas *canvas, @@ -2224,6 +2588,58 @@ goo_canvas_request_redraw (GooCanvas *canvas, } +/** + * goo_canvas_request_item_redraw: + * @canvas: a #GooCanvas. + * @bounds: the bounds of the item to redraw. + * @is_static: if the item is static. + * + * This function is only intended to be used by subclasses of #GooCanvas or + * #GooCanvasItem implementations. + * + * Requests that the given bounds be redrawn. If @is_static is %TRUE the bounds + * are assumed to be in the static item coordinate space, otherwise they are + * assumed to be in the canvas coordinate space. + * + * If @is_static is %FALSE this function behaves the same as + * goo_canvas_request_redraw(). + **/ +void +goo_canvas_request_item_redraw (GooCanvas *canvas, + const GooCanvasBounds *bounds, + gboolean is_static) +{ + if (is_static) + request_static_redraw (canvas, bounds); + else + goo_canvas_request_redraw (canvas, bounds); +} + + +static void +paint_static_items (GooCanvas *canvas, + GdkEventExpose *event, + cairo_t *cr) +{ + GooCanvasPrivate *priv = GOO_CANVAS_GET_PRIVATE (canvas); + GooCanvasBounds static_bounds; + double static_x_offset, static_y_offset; + + cairo_save (cr); + cairo_identity_matrix (cr); + static_x_offset = floor (canvas->hadjustment->value); + static_y_offset = floor (canvas->vadjustment->value); + cairo_translate (cr, static_x_offset, static_y_offset); + /* FIXME: Uses pixels at present - use canvas units instead? */ + static_bounds.x1 = event->area.x - static_x_offset; + static_bounds.y1 = event->area.y - static_y_offset; + static_bounds.x2 = event->area.width + static_bounds.x1; + static_bounds.y2 = event->area.height + static_bounds.y1; + goo_canvas_item_paint (priv->static_root_item, cr, &static_bounds, 1.0); + cairo_restore (cr); +} + + static gboolean goo_canvas_expose_event (GtkWidget *widget, GdkEventExpose *event) @@ -2231,6 +2647,7 @@ goo_canvas_expose_event (GtkWidget *widget, GooCanvas *canvas = GOO_CANVAS (widget); GooCanvasBounds bounds, root_item_bounds; cairo_t *cr; + double x1, y1, x2, y2; if (!canvas->root_item) return FALSE; @@ -2282,17 +2699,26 @@ goo_canvas_expose_event (GtkWidget *widget, || (root_item_bounds.y2 > canvas->bounds.y2 && canvas->bounds.y2 < bounds.y2)) { + /* Clip to the intersection of the canvas bounds and the expose + bounds, to avoid cairo's 16-bit limits. */ + x1 = MAX (canvas->bounds.x1, bounds.x1); + y1 = MAX (canvas->bounds.y1, bounds.y1); + x2 = MIN (canvas->bounds.x2, bounds.x2); + y2 = MIN (canvas->bounds.y2, bounds.y2); + cairo_new_path (cr); - cairo_move_to (cr, canvas->bounds.x1, canvas->bounds.y1); - cairo_line_to (cr, canvas->bounds.x2, canvas->bounds.y1); - cairo_line_to (cr, canvas->bounds.x2, canvas->bounds.y2); - cairo_line_to (cr, canvas->bounds.x1, canvas->bounds.y2); + cairo_move_to (cr, x1, y1); + cairo_line_to (cr, x2, y1); + cairo_line_to (cr, x2, y2); + cairo_line_to (cr, x1, y2); cairo_close_path (cr); cairo_clip (cr); } goo_canvas_item_paint (canvas->root_item, cr, &bounds, canvas->scale); + paint_static_items (canvas, event, cr); + cairo_destroy (cr); GTK_WIDGET_CLASS (goo_canvas_parent_class)->expose_event (widget, event); @@ -2386,6 +2812,15 @@ initialize_crossing_event (GooCanvas *canvas, crossing_event->state = event->crossing.state; break; + case GDK_SCROLL: + crossing_event->time = event->scroll.time; + crossing_event->x = event->scroll.x; + crossing_event->y = event->scroll.y; + crossing_event->x_root = event->scroll.x_root; + crossing_event->y_root = event->scroll.y_root; + crossing_event->state = event->scroll.state; + break; + default: /* It must be a button press/release event. */ crossing_event->time = event->button.time; @@ -2501,6 +2936,12 @@ emit_pointer_event (GooCanvas *canvas, x_root = &event.crossing.x_root; y_root = &event.crossing.y_root; break; + case GDK_SCROLL: + x = &event.scroll.x; + y = &event.scroll.y; + x_root = &event.scroll.x_root; + y_root = &event.scroll.y_root; + break; default: /* It must be a button press/release event. */ x = &event.button.x; @@ -2517,6 +2958,10 @@ emit_pointer_event (GooCanvas *canvas, /* Convert to the canvas coordinate space. */ goo_canvas_convert_from_pixels (canvas, x, y); + /* Convert to static item space, if necessary. */ + if (target_item && goo_canvas_item_get_is_static (target_item)) + goo_canvas_convert_to_static_item_space (canvas, x, y); + /* Copy to the x_root & y_root fields. */ *x_root = *x; *y_root = *y; @@ -2708,6 +3153,14 @@ goo_canvas_scroll (GtkWidget *widget, GtkAdjustment *adj; gdouble delta, new_value; + if (event->window == canvas->canvas_window) + { + /* See if the current item wants the scroll event. */ + update_pointer_item (canvas, (GdkEvent*) event); + if (emit_pointer_event (canvas, "scroll_event", (GdkEvent*) event)) + return TRUE; + } + if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN) adj = canvas->vadjustment; else @@ -3132,22 +3585,23 @@ goo_canvas_convert_from_window_pixels (GooCanvas *canvas, } -/** - * goo_canvas_convert_to_item_space: - * @canvas: a #GooCanvas. - * @item: a #GooCanvasItem. - * @x: a pointer to the x coordinate to convert. - * @y: a pointer to the y coordinate to convert. - * - * Converts a coordinate from the canvas coordinate space to the given - * item's coordinate space, applying all transformation matrices including the - * item's own transformation matrix, if it has one. - **/ -void -goo_canvas_convert_to_item_space (GooCanvas *canvas, - GooCanvasItem *item, - gdouble *x, - gdouble *y) +/* Converts from the canvas coordinate space to the static item coordinate + space, i.e. in pixels from the top-left of the viewport window. */ +static void +goo_canvas_convert_to_static_item_space (GooCanvas *canvas, + gdouble *x, + gdouble *y) +{ + *x = ((*x - canvas->bounds.x1) * canvas->device_to_pixels_x) + + canvas->canvas_x_offset - canvas->hadjustment->value; + *y = ((*y - canvas->bounds.y1) * canvas->device_to_pixels_y) + + canvas->canvas_y_offset - canvas->vadjustment->value; +} + + +static void +get_transform_to_item_space (GooCanvasItem *item, + cairo_matrix_t *transform) { GooCanvasItem *tmp = item, *parent, *child; GList *list = NULL, *l; @@ -3176,8 +3630,31 @@ goo_canvas_convert_to_item_space (GooCanvas *canvas, } g_list_free (list); - /* Now convert the coordinates. */ - cairo_matrix_transform_point (&inverse, x, y); + *transform = inverse; +} + + +/** + * goo_canvas_convert_to_item_space: + * @canvas: a #GooCanvas. + * @item: a #GooCanvasItem. + * @x: a pointer to the x coordinate to convert. + * @y: a pointer to the y coordinate to convert. + * + * Converts a coordinate from the canvas coordinate space to the given + * item's coordinate space, applying all transformation matrices including the + * item's own transformation matrix, if it has one. + **/ +void +goo_canvas_convert_to_item_space (GooCanvas *canvas, + GooCanvasItem *item, + gdouble *x, + gdouble *y) +{ + cairo_matrix_t transform; + + get_transform_to_item_space (item, &transform); + cairo_matrix_transform_point (&transform, x, y); } @@ -3219,7 +3696,7 @@ goo_canvas_convert_from_item_space (GooCanvas *canvas, &item_transform); if (has_transform) { - cairo_matrix_multiply (&transform, &transform, &item_transform); + cairo_matrix_multiply (&transform, &item_transform, &transform); } } g_list_free (list); @@ -3229,6 +3706,56 @@ goo_canvas_convert_from_item_space (GooCanvas *canvas, } +/** + * goo_canvas_convert_bounds_to_item_space: + * @canvas: a #GooCanvas. + * @item: a #GooCanvasItem. + * @bounds: the bounds in canvas coordinate space, to be converted. + * + * Converts the given bounds in the canvas coordinate space to a bounding box + * in item space. This is useful in the item paint() methods to convert the + * bounds to be painted to the item's coordinate space. + **/ +void +goo_canvas_convert_bounds_to_item_space (GooCanvas *canvas, + GooCanvasItem *item, + GooCanvasBounds *bounds) +{ + GooCanvasBounds tmp_bounds = *bounds, tmp_bounds2 = *bounds; + cairo_matrix_t transform; + + get_transform_to_item_space (item, &transform); + + /* Convert the top-left and bottom-right corners to device coords. */ + cairo_matrix_transform_point (&transform, &tmp_bounds.x1, &tmp_bounds.y1); + cairo_matrix_transform_point (&transform, &tmp_bounds.x2, &tmp_bounds.y2); + + /* Now convert the top-right and bottom-left corners. */ + cairo_matrix_transform_point (&transform, &tmp_bounds2.x1, &tmp_bounds2.y2); + cairo_matrix_transform_point (&transform, &tmp_bounds2.x2, &tmp_bounds2.y1); + + /* Calculate the minimum x coordinate seen and put in x1. */ + bounds->x1 = MIN (tmp_bounds.x1, tmp_bounds.x2); + bounds->x1 = MIN (bounds->x1, tmp_bounds2.x1); + bounds->x1 = MIN (bounds->x1, tmp_bounds2.x2); + + /* Calculate the maximum x coordinate seen and put in x2. */ + bounds->x2 = MAX (tmp_bounds.x1, tmp_bounds.x2); + bounds->x2 = MAX (bounds->x2, tmp_bounds2.x1); + bounds->x2 = MAX (bounds->x2, tmp_bounds2.x2); + + /* Calculate the minimum y coordinate seen and put in y1. */ + bounds->y1 = MIN (tmp_bounds.y1, tmp_bounds.y2); + bounds->y1 = MIN (bounds->y1, tmp_bounds2.y1); + bounds->y1 = MIN (bounds->y1, tmp_bounds2.y2); + + /* Calculate the maximum y coordinate seen and put in y2. */ + bounds->y2 = MAX (tmp_bounds.y1, tmp_bounds.y2); + bounds->y2 = MAX (bounds->y2, tmp_bounds2.y1); + bounds->y2 = MAX (bounds->y2, tmp_bounds2.y2); +} + + /* * Keyboard focus navigation. */ @@ -3796,3 +4323,49 @@ goo_canvas_remove (GtkContainer *container, } } } + + +static gboolean +goo_canvas_query_tooltip (GtkWidget *widget, + gint x, + gint y, + gboolean keyboard_tip, + GtkTooltip *tooltip) +{ + GooCanvas *canvas = (GooCanvas*) widget; + GooCanvasItem *item = canvas->pointer_item, *parent; + gdouble item_x = x, item_y = y; + gboolean tip_set = FALSE, has_transform; + cairo_matrix_t transform; + + if (!item) + return FALSE; + + /* Convert from pixels to the item's coordinate space. */ + goo_canvas_convert_from_pixels (canvas, &item_x, &item_y); + goo_canvas_convert_to_item_space (canvas, item, &item_x, &item_y); + + for (;;) + { + g_signal_emit_by_name (item, "query-tooltip", item_x, item_y, + keyboard_tip, tooltip, &tip_set); + if (tip_set) + return TRUE; + + parent = goo_canvas_item_get_parent (item); + if (!parent) + break; + + /* Convert x & y to the parent's coordinate space. */ + has_transform = goo_canvas_item_get_transform_for_child (parent, item, + &transform); + if (has_transform) + cairo_matrix_transform_point (&transform, &item_x, &item_y); + + item = parent; + } + + /* We call the parent method in case the canvas itself has a tooltip set. */ + return GTK_WIDGET_CLASS (goo_canvas_parent_class)->query_tooltip (widget, x, y, keyboard_tip, tooltip); +} + diff --git a/src/goocanvas/src/goocanvas.h b/src/goocanvas/src/goocanvas.h index 56e38ff..c68b0c2 100644 --- a/src/goocanvas/src/goocanvas.h +++ b/src/goocanvas/src/goocanvas.h @@ -10,6 +10,7 @@ #include <gtk/gtk.h> #include <goocanvasenumtypes.h> #include <goocanvasellipse.h> +#include <goocanvasgrid.h> #include <goocanvasgroup.h> #include <goocanvasimage.h> #include <goocanvaspath.h> @@ -84,6 +85,10 @@ struct _GooCanvas /* This is TRUE if the background is cleared before painting the canvas. */ guint clear_background : 1; + /* This is TRUE if the canvas is completely redrawn when scrolled. It is + useful when there are sticky items to reduce flicker, but is slower. */ + guint redraw_when_scrolled : 1; + /* This is the padding around the automatic bounds. */ gdouble bounds_padding; @@ -204,6 +209,14 @@ GooCanvasItemModel* goo_canvas_get_root_item_model (GooCanvas *canvas); void goo_canvas_set_root_item_model (GooCanvas *canvas, GooCanvasItemModel *model); +GooCanvasItem* goo_canvas_get_static_root_item (GooCanvas *canvas); +void goo_canvas_set_static_root_item (GooCanvas *canvas, + GooCanvasItem *item); + +GooCanvasItemModel* goo_canvas_get_static_root_item_model (GooCanvas *canvas); +void goo_canvas_set_static_root_item_model (GooCanvas *canvas, + GooCanvasItemModel *model); + GooCanvasItem* goo_canvas_get_item (GooCanvas *canvas, GooCanvasItemModel *model); GooCanvasItem* goo_canvas_get_item_at (GooCanvas *canvas, @@ -265,6 +278,9 @@ void goo_canvas_convert_from_item_space (GooCanvas *canvas, GooCanvasItem *item, gdouble *x, gdouble *y); +void goo_canvas_convert_bounds_to_item_space (GooCanvas *canvas, + GooCanvasItem *item, + GooCanvasBounds *bounds); /* @@ -299,6 +315,9 @@ void goo_canvas_update (GooCanvas *canvas); void goo_canvas_request_update (GooCanvas *canvas); void goo_canvas_request_redraw (GooCanvas *canvas, const GooCanvasBounds *bounds); +void goo_canvas_request_item_redraw (GooCanvas *canvas, + const GooCanvasBounds *bounds, + gboolean is_static); gdouble goo_canvas_get_default_line_width (GooCanvas *canvas); diff --git a/src/goocanvas/src/goocanvasatk.c b/src/goocanvas/src/goocanvasatk.c index f32bb64..5c8c774 100644 --- a/src/goocanvas/src/goocanvasatk.c +++ b/src/goocanvas/src/goocanvasatk.c @@ -20,6 +20,7 @@ typedef AtkGObjectAccessible GooCanvasItemAccessible; typedef AtkGObjectAccessibleClass GooCanvasItemAccessibleClass; +#define GOO_TYPE_CANVAS_ITEM_ACCESSIBLE (goo_canvas_item_accessible_get_type ()) #define GOO_IS_CANVAS_ITEM_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), goo_canvas_item_accessible_get_type ())) static void goo_canvas_item_accessible_component_interface_init (AtkComponentIface *iface); @@ -50,15 +51,19 @@ goo_canvas_item_accessible_get_item_extents (GooCanvasItem *item, /* Get the bounds in device units. */ goo_canvas_item_get_bounds (item, &bounds); - /* Convert to pixels within the entire canvas. */ - goo_canvas_convert_to_pixels (canvas, &bounds.x1, &bounds.y1); - goo_canvas_convert_to_pixels (canvas, &bounds.x2, &bounds.y2); - - /* Convert to pixels within the visible window. */ - bounds.x1 -= canvas->hadjustment->value; - bounds.y1 -= canvas->vadjustment->value; - bounds.x2 -= canvas->hadjustment->value; - bounds.y2 -= canvas->vadjustment->value; + /* Static items are in pixels so don't need converting. */ + if (!goo_canvas_item_get_is_static (item)) + { + /* Convert to pixels within the entire canvas. */ + goo_canvas_convert_to_pixels (canvas, &bounds.x1, &bounds.y1); + goo_canvas_convert_to_pixels (canvas, &bounds.x2, &bounds.y2); + + /* Convert to pixels within the visible window. */ + bounds.x1 -= canvas->hadjustment->value; + bounds.y1 -= canvas->vadjustment->value; + bounds.x2 -= canvas->hadjustment->value; + bounds.y2 -= canvas->vadjustment->value; + } /* Round up or down to integers. */ rect->x = floor (bounds.x1); @@ -491,7 +496,7 @@ typedef AtkGObjectAccessibleClass GooCanvasWidgetAccessibleClass; #define GOO_IS_CANVAS_WIDGET_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), goo_canvas_widget_accessible_get_type ())) G_DEFINE_TYPE (GooCanvasWidgetAccessible, goo_canvas_widget_accessible, - GOO_TYPE_CANVAS_ITEM) + GOO_TYPE_CANVAS_ITEM_ACCESSIBLE) static void @@ -570,7 +575,7 @@ goo_canvas_widget_accessible_init (GooCanvasWidgetAccessible *accessible) } -AtkObject* +static AtkObject* goo_canvas_widget_accessible_new (GObject *object) { AtkObject *accessible; diff --git a/src/goocanvas/src/goocanvasellipse.c b/src/goocanvas/src/goocanvasellipse.c index 7453a4c..6653f50 100644 --- a/src/goocanvas/src/goocanvasellipse.c +++ b/src/goocanvas/src/goocanvasellipse.c @@ -23,6 +23,10 @@ * * To get or set the properties of an existing #GooCanvasEllipse, use * g_object_get() and g_object_set(). + * + * The ellipse can be specified either with the "center-x", "center-y", + * "radius-x" and "radius-y" properties, or with the "x", "y", "width" and + * "height" properties. */ #include <config.h> #include <math.h> @@ -37,7 +41,12 @@ enum { PROP_CENTER_X, PROP_CENTER_Y, PROP_RADIUS_X, - PROP_RADIUS_Y + PROP_RADIUS_Y, + + PROP_X, + PROP_Y, + PROP_WIDTH, + PROP_HEIGHT }; @@ -92,6 +101,36 @@ goo_canvas_ellipse_install_common_properties (GObjectClass *gobject_class) _("The vertical radius of the ellipse"), 0.0, G_MAXDOUBLE, 0.0, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_X, + g_param_spec_double ("x", + "X", + _("The x coordinate of the left side of the ellipse"), + -G_MAXDOUBLE, + G_MAXDOUBLE, 0.0, + G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_Y, + g_param_spec_double ("y", + "Y", + _("The y coordinate of the top of the ellipse"), + -G_MAXDOUBLE, + G_MAXDOUBLE, 0.0, + G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_WIDTH, + g_param_spec_double ("width", + _("Width"), + _("The width of the ellipse"), + 0.0, G_MAXDOUBLE, 0.0, + G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_HEIGHT, + g_param_spec_double ("height", + _("Height"), + _("The height of the ellipse"), + 0.0, G_MAXDOUBLE, 0.0, + G_PARAM_READWRITE)); } @@ -224,6 +263,18 @@ goo_canvas_ellipse_get_common_property (GObject *object, case PROP_RADIUS_Y: g_value_set_double (value, ellipse_data->radius_y); break; + case PROP_X: + g_value_set_double (value, ellipse_data->center_x - ellipse_data->radius_x); + break; + case PROP_Y: + g_value_set_double (value, ellipse_data->center_y - ellipse_data->radius_y); + break; + case PROP_WIDTH: + g_value_set_double (value, 2.0 * ellipse_data->radius_x); + break; + case PROP_HEIGHT: + g_value_set_double (value, 2.0 * ellipse_data->radius_y); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -251,19 +302,55 @@ goo_canvas_ellipse_set_common_property (GObject *object, const GValue *value, GParamSpec *pspec) { + gdouble x, y; + switch (prop_id) { case PROP_CENTER_X: ellipse_data->center_x = g_value_get_double (value); + g_object_notify (object, "x"); break; case PROP_CENTER_Y: ellipse_data->center_y = g_value_get_double (value); + g_object_notify (object, "y"); break; case PROP_RADIUS_X: ellipse_data->radius_x = g_value_get_double (value); + g_object_notify (object, "width"); break; case PROP_RADIUS_Y: ellipse_data->radius_y = g_value_get_double (value); + g_object_notify (object, "height"); + break; + case PROP_X: + ellipse_data->center_x = g_value_get_double (value) + ellipse_data->radius_x; + g_object_notify (object, "center-x"); + break; + case PROP_Y: + ellipse_data->center_y = g_value_get_double (value) + ellipse_data->radius_y; + g_object_notify (object, "center-y"); + break; + case PROP_WIDTH: + /* Calculate the current x coordinate. */ + x = ellipse_data->center_x - ellipse_data->radius_x; + /* Calculate the new radius_x, which is half the width. */ + ellipse_data->radius_x = g_value_get_double (value) / 2.0; + /* Now calculate the new center_x. */ + ellipse_data->center_x = x + ellipse_data->radius_x; + + g_object_notify (object, "center-x"); + g_object_notify (object, "radius-x"); + break; + case PROP_HEIGHT: + /* Calculate the current y coordinate. */ + y = ellipse_data->center_y - ellipse_data->radius_y; + /* Calculate the new radius_y, which is half the height. */ + ellipse_data->radius_y = g_value_get_double (value) / 2.0; + /* Now calculate the new center_y. */ + ellipse_data->center_y = y + ellipse_data->radius_y; + + g_object_notify (object, "center-y"); + g_object_notify (object, "radius-y"); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -356,6 +443,10 @@ canvas_item_interface_init (GooCanvasItemIface *iface) * To get or set the properties of an existing #GooCanvasEllipseModel, use * g_object_get() and g_object_set(). * + * The ellipse can be specified either with the "center-x", "center-y", + * "radius-x" and "radius-y" properties, or with the "x", "y", "width" and + * "height" properties. + * * To respond to events such as mouse clicks on the ellipse you must connect * to the signal handlers of the corresponding #GooCanvasEllipse objects. * (See goo_canvas_get_item() and #GooCanvas::item-created.) diff --git a/src/goocanvas/src/goocanvasgrid.c b/src/goocanvas/src/goocanvasgrid.c new file mode 100644 index 0000000..80190dd --- /dev/null +++ b/src/goocanvas/src/goocanvasgrid.c @@ -0,0 +1,1170 @@ +/* + * GooCanvas. Copyright (C) 2005-8 Damon Chaplin. + * Released under the GNU LGPL license. See COPYING for details. + * + * goocanvasgrid.c - a grid item. + */ + +/** + * SECTION:goocanvasgrid + * @Title: GooCanvasGrid + * @Short_Description: a grid item. + * + * GooCanvasGrid represents a grid item. + * A grid consists of a number of equally-spaced horizontal and vertical + * grid lines, plus an optional border. + * + * It is a subclass of #GooCanvasItemSimple and so inherits all of the style + * properties such as "stroke-color", "fill-color" and "line-width". + * + * It also implements the #GooCanvasItem interface, so you can use the + * #GooCanvasItem functions such as goo_canvas_item_raise() and + * goo_canvas_item_rotate(). + * + * To create a #GooCanvasGrid use goo_canvas_grid_new(). + * + * To get or set the properties of an existing #GooCanvasGrid, use + * g_object_get() and g_object_set(). + * + * The grid's position and size is specified with the #GooCanvasGrid:x, + * #GooCanvasGrid:y, #GooCanvasGrid:width and #GooCanvasGrid:height properties. + * + * The #GooCanvasGrid:x-step and #GooCanvasGrid:y-step properties specify the + * distance between grid lines. The #GooCanvasGrid:x-offset and + * #GooCanvasGrid:y-offset properties specify the distance before the first + * grid lines. + * + * The horizontal or vertical grid lines can be hidden using the + * #GooCanvasGrid:show-horz-grid-lines and #GooCanvasGrid:show-vert-grid-lines + * properties. + * + * The width of the border can be set using the #GooCanvasGrid:border-width + * property. The border is drawn outside the area specified with the + * #GooCanvasGrid:x, #GooCanvasGrid:y, #GooCanvasGrid:width and + * #GooCanvasGrid:height properties. + * + * Other properties allow the colors and widths of the grid lines to be set. + * The grid line color and width properties override the standard + * #GooCanvasItemSimple:stroke-color and #GooCanvasItemSimple:line-width + * properties, enabling different styles for horizontal and vertical grid lines. + */ +#include <config.h> +#include <math.h> +#include <glib/gi18n-lib.h> +#include <gtk/gtk.h> +#include "goocanvasprivate.h" +#include "goocanvas.h" + + +enum { + PROP_0, + + PROP_X, + PROP_Y, + PROP_WIDTH, + PROP_HEIGHT, + PROP_X_STEP, + PROP_Y_STEP, + PROP_X_OFFSET, + PROP_Y_OFFSET, + PROP_HORZ_GRID_LINE_WIDTH, + PROP_VERT_GRID_LINE_WIDTH, + PROP_HORZ_GRID_LINE_PATTERN, + PROP_VERT_GRID_LINE_PATTERN, + PROP_BORDER_WIDTH, + PROP_BORDER_PATTERN, + PROP_SHOW_HORZ_GRID_LINES, + PROP_SHOW_VERT_GRID_LINES, + PROP_VERT_GRID_LINES_ON_TOP, + + /* Convenience properties. */ + PROP_HORZ_GRID_LINE_COLOR, + PROP_HORZ_GRID_LINE_COLOR_RGBA, + PROP_HORZ_GRID_LINE_PIXBUF, + PROP_VERT_GRID_LINE_COLOR, + PROP_VERT_GRID_LINE_COLOR_RGBA, + PROP_VERT_GRID_LINE_PIXBUF, + PROP_BORDER_COLOR, + PROP_BORDER_COLOR_RGBA, + PROP_BORDER_PIXBUF +}; + + +GooCanvasItemIface *goo_canvas_grid_parent_iface; + +static void canvas_item_interface_init (GooCanvasItemIface *iface); + +G_DEFINE_TYPE_WITH_CODE (GooCanvasGrid, goo_canvas_grid, + GOO_TYPE_CANVAS_ITEM_SIMPLE, + G_IMPLEMENT_INTERFACE (GOO_TYPE_CANVAS_ITEM, + canvas_item_interface_init)) + + +static void +goo_canvas_grid_install_common_properties (GObjectClass *gobject_class) +{ + g_object_class_install_property (gobject_class, PROP_X, + g_param_spec_double ("x", + "X", + _("The x coordinate of the grid"), + -G_MAXDOUBLE, + G_MAXDOUBLE, 0.0, + G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_Y, + g_param_spec_double ("y", + "Y", + _("The y coordinate of the grid"), + -G_MAXDOUBLE, + G_MAXDOUBLE, 0.0, + G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_WIDTH, + g_param_spec_double ("width", + _("Width"), + _("The width of the grid"), + 0.0, G_MAXDOUBLE, 0.0, + G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_HEIGHT, + g_param_spec_double ("height", + _("Height"), + _("The height of the grid"), + 0.0, G_MAXDOUBLE, 0.0, + G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_X_STEP, + g_param_spec_double ("x-step", + "X Step", + _("The distance between the vertical grid lines"), + 0.0, G_MAXDOUBLE, 10.0, + G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_Y_STEP, + g_param_spec_double ("y-step", + "Y Step", + _("The distance between the horizontal grid lines"), + 0.0, G_MAXDOUBLE, 10.0, + G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_X_OFFSET, + g_param_spec_double ("x-offset", + "X Offset", + _("The distance before the first vertical grid line"), + 0.0, G_MAXDOUBLE, 0.0, + G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_Y_OFFSET, + g_param_spec_double ("y-offset", + "Y Offset", + _("The distance before the first horizontal grid line"), + 0.0, G_MAXDOUBLE, 0.0, + G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_HORZ_GRID_LINE_WIDTH, + g_param_spec_double ("horz-grid-line-width", + _("Horizontal Grid Line Width"), + _("The width of the horizontal grid lines"), + -G_MAXDOUBLE, + G_MAXDOUBLE, -1.0, + G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_VERT_GRID_LINE_WIDTH, + g_param_spec_double ("vert-grid-line-width", + _("Vertical Grid Line Width"), + _("The width of the vertical grid lines"), + -G_MAXDOUBLE, + G_MAXDOUBLE, -1.0, + G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_HORZ_GRID_LINE_PATTERN, + g_param_spec_boxed ("horz-grid-line-pattern", + _("Horizontal Grid Line Pattern"), + _("The cairo pattern to paint the horizontal grid lines with"), + GOO_TYPE_CAIRO_PATTERN, + G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_VERT_GRID_LINE_PATTERN, + g_param_spec_boxed ("vert-grid-line-pattern", + _("Vertical Grid Line Pattern"), + _("The cairo pattern to paint the vertical grid lines with"), + GOO_TYPE_CAIRO_PATTERN, + G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_BORDER_WIDTH, + g_param_spec_double ("border-width", + _("Border Width"), + _("The width of the border around the grid"), + -G_MAXDOUBLE, + G_MAXDOUBLE, -1.0, + G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_BORDER_PATTERN, + g_param_spec_boxed ("border-pattern", + _("Border Pattern"), + _("The cairo pattern to paint the border with"), + GOO_TYPE_CAIRO_PATTERN, + G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_SHOW_HORZ_GRID_LINES, + g_param_spec_boolean ("show-horz-grid-lines", + _("Show Horizontal Grid Lines"), + _("If the horizontal grid lines are shown"), + TRUE, + G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_SHOW_VERT_GRID_LINES, + g_param_spec_boolean ("show-vert-grid-lines", + _("Show Vertical Grid Lines"), + _("If the vertical grid lines are shown"), + TRUE, + G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_VERT_GRID_LINES_ON_TOP, + g_param_spec_boolean ("vert-grid-lines-on-top", + _("Vertical Grid Lines On Top"), + _("If the vertical grid lines are painted above the horizontal grid lines"), + FALSE, + G_PARAM_READWRITE)); + + + /* Convenience properties - some are writable only. */ + g_object_class_install_property (gobject_class, PROP_HORZ_GRID_LINE_COLOR, + g_param_spec_string ("horz-grid-line-color", + _("Horizontal Grid Line Color"), + _("The color to use for the horizontal grid lines"), + NULL, + G_PARAM_WRITABLE)); + + g_object_class_install_property (gobject_class, PROP_HORZ_GRID_LINE_COLOR_RGBA, + g_param_spec_uint ("horz-grid-line-color-rgba", + _("Horizontal Grid Line Color RGBA"), + _("The color to use for the horizontal grid lines, specified as a 32-bit integer value"), + 0, G_MAXUINT, 0, + G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_HORZ_GRID_LINE_PIXBUF, + g_param_spec_object ("horz-grid-line-pixbuf", + _("Horizontal Grid Line Pixbuf"), + _("The pixbuf to use to draw the horizontal grid lines"), + GDK_TYPE_PIXBUF, + G_PARAM_WRITABLE)); + + g_object_class_install_property (gobject_class, PROP_VERT_GRID_LINE_COLOR, + g_param_spec_string ("vert-grid-line-color", + _("Vertical Grid Line Color"), + _("The color to use for the vertical grid lines"), + NULL, + G_PARAM_WRITABLE)); + + g_object_class_install_property (gobject_class, PROP_VERT_GRID_LINE_COLOR_RGBA, + g_param_spec_uint ("vert-grid-line-color-rgba", + _("Vertical Grid Line Color RGBA"), + _("The color to use for the vertical grid lines, specified as a 32-bit integer value"), + 0, G_MAXUINT, 0, + G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_VERT_GRID_LINE_PIXBUF, + g_param_spec_object ("vert-grid-line-pixbuf", + _("Vertical Grid Line Pixbuf"), + _("The pixbuf to use to draw the vertical grid lines"), + GDK_TYPE_PIXBUF, + G_PARAM_WRITABLE)); + + g_object_class_install_property (gobject_class, PROP_BORDER_COLOR, + g_param_spec_string ("border-color", + _("Border Color"), + _("The color to use for the border"), + NULL, + G_PARAM_WRITABLE)); + + g_object_class_install_property (gobject_class, PROP_BORDER_COLOR_RGBA, + g_param_spec_uint ("border-color-rgba", + _("Border Color RGBA"), + _("The color to use for the border, specified as a 32-bit integer value"), + 0, G_MAXUINT, 0, + G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_BORDER_PIXBUF, + g_param_spec_object ("border-pixbuf", + _("Border Pixbuf"), + _("The pixbuf to use to draw the border"), + GDK_TYPE_PIXBUF, + G_PARAM_WRITABLE)); +} + + +/* This initializes the common grid data. */ +static void +goo_canvas_grid_init_data (GooCanvasGridData *grid_data) +{ + grid_data->x = 0.0; + grid_data->y = 0.0; + grid_data->width = 0.0; + grid_data->height = 0.0; + grid_data->x_step = 10.0; + grid_data->y_step = 10.0; + grid_data->x_offset = 0.0; + grid_data->y_offset = 0.0; + grid_data->horz_grid_line_width = -1.0; + grid_data->vert_grid_line_width = -1.0; + grid_data->horz_grid_line_pattern = NULL; + grid_data->vert_grid_line_pattern = NULL; + grid_data->border_width = -1.0; + grid_data->border_pattern = NULL; + grid_data->show_horz_grid_lines = TRUE; + grid_data->show_vert_grid_lines = TRUE; + grid_data->vert_grid_lines_on_top = FALSE; +} + + +/* This frees the contents of the grid data, but not the struct itself. */ +static void +goo_canvas_grid_free_data (GooCanvasGridData *grid_data) +{ + +} + + +static void +goo_canvas_grid_init (GooCanvasGrid *grid) +{ + grid->grid_data = g_slice_new0 (GooCanvasGridData); + goo_canvas_grid_init_data (grid->grid_data); +} + + +/** + * goo_canvas_grid_new: + * @parent: the parent item, or %NULL. If a parent is specified, it will assume + * ownership of the item, and the item will automatically be freed when it is + * removed from the parent. Otherwise call g_object_unref() to free it. + * @x: the x coordinate of the left of the grid. + * @y: the y coordinate of the top of the grid. + * @width: the width of the grid. + * @height: the height of the grid. + * @x_step: the distance between the vertical grid lines. + * @y_step: the distance between the horizontal grid lines. + * @x_offset: the distance before the first vertical grid line. + * @y_offset: the distance before the first horizontal grid line. + * @...: optional pairs of property names and values, and a terminating %NULL. + * + * Creates a new grid item. + * + * <!--PARAMETERS--> + * + * Here's an example showing how to create a grid: + * + * <informalexample><programlisting> + * GooCanvasItem *grid = goo_canvas_grid_new (mygroup, 100.0, 100.0, 400.0, 200.0, + * 20.0, 20.0, 10.0, 10.0, + * "horz-grid-line-width", 4.0, + * "horz-grid-line-color", "yellow", + * "vert-grid-line-width", 2.0, + * "vert-grid-line-color", "red", + * "border-width", 3.0, + * "border-color", "white", + * "fill-color", "blue", + * NULL); + * </programlisting></informalexample> + * + * Returns: a new grid item. + **/ +GooCanvasItem* +goo_canvas_grid_new (GooCanvasItem *parent, + gdouble x, + gdouble y, + gdouble width, + gdouble height, + gdouble x_step, + gdouble y_step, + gdouble x_offset, + gdouble y_offset, + ...) +{ + GooCanvasItem *item; + GooCanvasGrid *grid; + GooCanvasGridData *grid_data; + va_list var_args; + const char *first_property; + + item = g_object_new (GOO_TYPE_CANVAS_GRID, NULL); + grid = (GooCanvasGrid*) item; + + grid_data = grid->grid_data; + grid_data->x = x; + grid_data->y = y; + grid_data->width = width; + grid_data->height = height; + grid_data->x_step = x_step; + grid_data->y_step = y_step; + grid_data->x_offset = x_offset; + grid_data->y_offset = y_offset; + + va_start (var_args, y_offset); + first_property = va_arg (var_args, char*); + if (first_property) + g_object_set_valist (G_OBJECT (item), first_property, var_args); + va_end (var_args); + + if (parent) + { + goo_canvas_item_add_child (parent, item, -1); + g_object_unref (item); + } + + return item; +} + + +static void +goo_canvas_grid_finalize (GObject *object) +{ + GooCanvasItemSimple *simple = (GooCanvasItemSimple*) object; + GooCanvasGrid *grid = (GooCanvasGrid*) object; + + /* Free our data if we didn't have a model. (If we had a model it would + have been reset in dispose() and simple_data will be NULL.) */ + if (simple->simple_data) + { + goo_canvas_grid_free_data (grid->grid_data); + g_slice_free (GooCanvasGridData, grid->grid_data); + } + grid->grid_data = NULL; + + G_OBJECT_CLASS (goo_canvas_grid_parent_class)->finalize (object); +} + + +static void +goo_canvas_grid_get_common_property (GObject *object, + GooCanvasGridData *grid_data, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) + { + case PROP_X: + g_value_set_double (value, grid_data->x); + break; + case PROP_Y: + g_value_set_double (value, grid_data->y); + break; + case PROP_WIDTH: + g_value_set_double (value, grid_data->width); + break; + case PROP_HEIGHT: + g_value_set_double (value, grid_data->height); + break; + case PROP_X_STEP: + g_value_set_double (value, grid_data->x_step); + break; + case PROP_Y_STEP: + g_value_set_double (value, grid_data->y_step); + break; + case PROP_X_OFFSET: + g_value_set_double (value, grid_data->x_offset); + break; + case PROP_Y_OFFSET: + g_value_set_double (value, grid_data->y_offset); + break; + case PROP_HORZ_GRID_LINE_WIDTH: + g_value_set_double (value, grid_data->horz_grid_line_width); + break; + case PROP_VERT_GRID_LINE_WIDTH: + g_value_set_double (value, grid_data->vert_grid_line_width); + break; + case PROP_HORZ_GRID_LINE_PATTERN: + g_value_set_boxed (value, grid_data->horz_grid_line_pattern); + break; + case PROP_VERT_GRID_LINE_PATTERN: + g_value_set_boxed (value, grid_data->vert_grid_line_pattern); + break; + case PROP_BORDER_WIDTH: + g_value_set_double (value, grid_data->border_width); + break; + case PROP_BORDER_PATTERN: + g_value_set_boxed (value, grid_data->border_pattern); + break; + case PROP_SHOW_HORZ_GRID_LINES: + g_value_set_boolean (value, grid_data->show_horz_grid_lines); + break; + case PROP_SHOW_VERT_GRID_LINES: + g_value_set_boolean (value, grid_data->show_vert_grid_lines); + break; + case PROP_VERT_GRID_LINES_ON_TOP: + g_value_set_boolean (value, grid_data->vert_grid_lines_on_top); + break; + + /* Convenience properties. */ + case PROP_HORZ_GRID_LINE_COLOR_RGBA: + goo_canvas_get_rgba_value_from_pattern (grid_data->horz_grid_line_pattern, value); + break; + case PROP_VERT_GRID_LINE_COLOR_RGBA: + goo_canvas_get_rgba_value_from_pattern (grid_data->vert_grid_line_pattern, value); + break; + case PROP_BORDER_COLOR_RGBA: + goo_canvas_get_rgba_value_from_pattern (grid_data->border_pattern, value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static void +goo_canvas_grid_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GooCanvasGrid *grid = (GooCanvasGrid*) object; + + goo_canvas_grid_get_common_property (object, grid->grid_data, + prop_id, value, pspec); +} + + +static void +goo_canvas_grid_set_common_property (GObject *object, + GooCanvasGridData *grid_data, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) + { + case PROP_X: + grid_data->x = g_value_get_double (value); + break; + case PROP_Y: + grid_data->y = g_value_get_double (value); + break; + case PROP_WIDTH: + grid_data->width = g_value_get_double (value); + break; + case PROP_HEIGHT: + grid_data->height = g_value_get_double (value); + break; + case PROP_X_STEP: + grid_data->x_step = g_value_get_double (value); + break; + case PROP_Y_STEP: + grid_data->y_step = g_value_get_double (value); + break; + case PROP_X_OFFSET: + grid_data->x_offset = g_value_get_double (value); + break; + case PROP_Y_OFFSET: + grid_data->y_offset = g_value_get_double (value); + break; + case PROP_HORZ_GRID_LINE_WIDTH: + grid_data->horz_grid_line_width = g_value_get_double (value); + break; + case PROP_VERT_GRID_LINE_WIDTH: + grid_data->vert_grid_line_width = g_value_get_double (value); + break; + case PROP_HORZ_GRID_LINE_PATTERN: + cairo_pattern_destroy (grid_data->horz_grid_line_pattern); + grid_data->horz_grid_line_pattern = g_value_get_boxed (value); + cairo_pattern_reference (grid_data->horz_grid_line_pattern); + break; + case PROP_VERT_GRID_LINE_PATTERN: + cairo_pattern_destroy (grid_data->vert_grid_line_pattern); + grid_data->vert_grid_line_pattern = g_value_get_boxed (value); + cairo_pattern_reference (grid_data->vert_grid_line_pattern); + break; + case PROP_BORDER_WIDTH: + grid_data->border_width = g_value_get_double (value); + break; + case PROP_BORDER_PATTERN: + cairo_pattern_destroy (grid_data->border_pattern); + grid_data->border_pattern = g_value_get_boxed (value); + cairo_pattern_reference (grid_data->border_pattern); + break; + case PROP_SHOW_HORZ_GRID_LINES: + grid_data->show_horz_grid_lines = g_value_get_boolean (value); + break; + case PROP_SHOW_VERT_GRID_LINES: + grid_data->show_vert_grid_lines = g_value_get_boolean (value); + break; + case PROP_VERT_GRID_LINES_ON_TOP: + grid_data->vert_grid_lines_on_top = g_value_get_boolean (value); + break; + + /* Convenience properties. */ + case PROP_HORZ_GRID_LINE_COLOR: + cairo_pattern_destroy (grid_data->horz_grid_line_pattern); + grid_data->horz_grid_line_pattern = goo_canvas_create_pattern_from_color_value (value); + break; + case PROP_HORZ_GRID_LINE_COLOR_RGBA: + cairo_pattern_destroy (grid_data->horz_grid_line_pattern); + grid_data->horz_grid_line_pattern = goo_canvas_create_pattern_from_rgba_value (value); + break; + case PROP_HORZ_GRID_LINE_PIXBUF: + cairo_pattern_destroy (grid_data->horz_grid_line_pattern); + grid_data->horz_grid_line_pattern = goo_canvas_create_pattern_from_pixbuf_value (value); + break; + + case PROP_VERT_GRID_LINE_COLOR: + cairo_pattern_destroy (grid_data->vert_grid_line_pattern); + grid_data->vert_grid_line_pattern = goo_canvas_create_pattern_from_color_value (value); + break; + case PROP_VERT_GRID_LINE_COLOR_RGBA: + cairo_pattern_destroy (grid_data->vert_grid_line_pattern); + grid_data->vert_grid_line_pattern = goo_canvas_create_pattern_from_rgba_value (value); + break; + case PROP_VERT_GRID_LINE_PIXBUF: + cairo_pattern_destroy (grid_data->vert_grid_line_pattern); + grid_data->vert_grid_line_pattern = goo_canvas_create_pattern_from_pixbuf_value (value); + break; + + case PROP_BORDER_COLOR: + cairo_pattern_destroy (grid_data->border_pattern); + grid_data->border_pattern = goo_canvas_create_pattern_from_color_value (value); + break; + case PROP_BORDER_COLOR_RGBA: + cairo_pattern_destroy (grid_data->border_pattern); + grid_data->border_pattern = goo_canvas_create_pattern_from_rgba_value (value); + break; + case PROP_BORDER_PIXBUF: + cairo_pattern_destroy (grid_data->border_pattern); + grid_data->border_pattern = goo_canvas_create_pattern_from_pixbuf_value (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static void +goo_canvas_grid_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GooCanvasItemSimple *simple = (GooCanvasItemSimple*) object; + GooCanvasGrid *grid = (GooCanvasGrid*) object; + + if (simple->model) + { + g_warning ("Can't set property of a canvas item with a model - set the model property instead"); + return; + } + + goo_canvas_grid_set_common_property (object, grid->grid_data, + prop_id, value, pspec); + goo_canvas_item_simple_changed (simple, TRUE); +} + + +static void +goo_canvas_grid_update (GooCanvasItemSimple *simple, + cairo_t *cr) +{ + GooCanvasGrid *grid = (GooCanvasGrid*) simple; + GooCanvasGridData *grid_data = grid->grid_data; + gdouble border_width = 0.0; + + /* We can quickly compute the bounds as being just the grid's size + plus the border width around each edge. */ + if (grid_data->border_width > 0.0) + border_width = grid_data->border_width; + + simple->bounds.x1 = grid_data->x - border_width; + simple->bounds.y1 = grid_data->y - border_width; + simple->bounds.x2 = grid_data->x + grid_data->width + border_width; + simple->bounds.y2 = grid_data->y + grid_data->height + border_width; +} + + +static gdouble +calculate_start_position (gdouble start_pos, + gdouble step, + gdouble redraw_start_pos, + gdouble line_width) +{ + gdouble n = 0.0, result; + + /* We want the first position where pos + line_width/2 >= redraw_start_pos. + i.e. start_pos + (n * step) + (line_width / 2) >= redraw_start_pos, + or (n * step) >= redraw_start_pos - start_pos - (line_width / 2), + or n >= (redraw_start_pos - start_pos - (line_width / 2) / step). */ + if (step > 0.0) + n = ceil (((redraw_start_pos - start_pos - (line_width / 2.0))) / step); + + if (n <= 0.0) + result = start_pos; + else + result = start_pos + (n * step); + + return result; +} + + +static void +paint_vertical_lines (GooCanvasItemSimple *simple, + cairo_t *cr, + const GooCanvasBounds *bounds) +{ + GooCanvasItemSimpleData *simple_data = simple->simple_data; + GooCanvasGrid *grid = (GooCanvasGrid*) simple; + GooCanvasGridData *grid_data = grid->grid_data; + double x, max_x, max_y, max_bounds_x, line_width; + gboolean has_stroke; + + if (!grid_data->show_vert_grid_lines) + return; + + max_x = grid_data->x + grid_data->width; + max_y = grid_data->y + grid_data->height; + + has_stroke = goo_canvas_style_set_stroke_options (simple_data->style, cr); + line_width = goo_canvas_item_simple_get_line_width (simple); + + /* If the grid's vertical grid line pattern/color has been set, use that. + If not, and we don't have a stroke color just return. */ + if (grid_data->vert_grid_line_pattern) + cairo_set_source (cr, grid_data->vert_grid_line_pattern); + else if (!has_stroke) + return; + + /* If the grid's vertical grid line width has been set, use that. */ + if (grid_data->vert_grid_line_width > 0.0) + { + line_width = grid_data->vert_grid_line_width; + cairo_set_line_width (cr, line_width); + } + + cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT); + + /* Calculate the first grid line that intersects the bounds to redraw. */ + x = calculate_start_position (grid_data->x + grid_data->x_offset, + grid_data->x_step, bounds->x1, line_width); + + /* Calculate the last possible line position. */ + max_bounds_x = bounds->x2 + (line_width / 2.0); + max_x = MIN (max_x, max_bounds_x); + + /* Add on a tiny fraction of step to avoid any double comparison issues. */ + max_x += grid_data->x_step * 0.00001; + + while (x <= max_x) + { + cairo_move_to (cr, x, grid_data->y); + cairo_line_to (cr, x, max_y); + cairo_stroke (cr); + + /* Avoid an infinite loop. */ + if (grid_data->x_step <= 0.0) + break; + + x += grid_data->x_step; + } +} + + +static void +paint_horizontal_lines (GooCanvasItemSimple *simple, + cairo_t *cr, + const GooCanvasBounds *bounds) +{ + GooCanvasItemSimpleData *simple_data = simple->simple_data; + GooCanvasGrid *grid = (GooCanvasGrid*) simple; + GooCanvasGridData *grid_data = grid->grid_data; + double y, max_x, max_y, max_bounds_y, line_width; + gboolean has_stroke; + + if (!grid_data->show_horz_grid_lines) + return; + + max_x = grid_data->x + grid_data->width; + max_y = grid_data->y + grid_data->height; + + has_stroke = goo_canvas_style_set_stroke_options (simple_data->style, cr); + line_width = goo_canvas_item_simple_get_line_width (simple); + + /* If the grid's horizontal grid line pattern/color has been set, use that. + If not, and we don't have a stroke color just return. */ + if (grid_data->horz_grid_line_pattern) + cairo_set_source (cr, grid_data->horz_grid_line_pattern); + else if (!has_stroke) + return; + + /* If the grid's horizontal grid line width has been set, use that. */ + if (grid_data->horz_grid_line_width > 0.0) + { + line_width = grid_data->horz_grid_line_width; + cairo_set_line_width (cr, grid_data->horz_grid_line_width); + } + + cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT); + + /* Calculate the first grid line that intersects the bounds to redraw. */ + y = calculate_start_position (grid_data->y + grid_data->y_offset, + grid_data->y_step, bounds->y1, line_width); + + /* Calculate the last possible line position. */ + max_bounds_y = bounds->y2 + (line_width / 2.0); + max_y = MIN (max_y, max_bounds_y); + + /* Add on a tiny fraction of step to avoid any double comparison issues. */ + max_y += grid_data->y_step * 0.00001; + + while (y <= max_y) + { + cairo_move_to (cr, grid_data->x, y); + cairo_line_to (cr, max_x, y); + cairo_stroke (cr); + + /* Avoid an infinite loop. */ + if (grid_data->y_step <= 0.0) + break; + + y += grid_data->y_step; + } +} + + +static void +goo_canvas_grid_paint (GooCanvasItemSimple *simple, + cairo_t *cr, + const GooCanvasBounds *bounds) +{ + GooCanvasItemSimpleData *simple_data = simple->simple_data; + GooCanvasGrid *grid = (GooCanvasGrid*) simple; + GooCanvasGridData *grid_data = grid->grid_data; + GooCanvasBounds redraw_bounds = *bounds; + gdouble half_border_width; + + /* Paint the background in the fill pattern/color, if one is set. */ + if (goo_canvas_style_set_fill_options (simple_data->style, cr)) + { + cairo_rectangle (cr, grid_data->x, grid_data->y, + grid_data->width, grid_data->height); + cairo_fill (cr); + } + + /* Clip to the grid's area while painting the grid lines. */ + cairo_save (cr); + cairo_rectangle (cr, grid_data->x, grid_data->y, + grid_data->width, grid_data->height); + cairo_clip (cr); + + /* Convert the bounds to be redrawn from device space to item space. */ + goo_canvas_convert_bounds_to_item_space (simple->canvas, + (GooCanvasItem*) simple, + &redraw_bounds); + + /* Paint the grid lines, in the required order. */ + if (grid_data->vert_grid_lines_on_top) + { + paint_horizontal_lines (simple, cr, &redraw_bounds); + paint_vertical_lines (simple, cr, &redraw_bounds); + } + else + { + paint_vertical_lines (simple, cr, &redraw_bounds); + paint_horizontal_lines (simple, cr, &redraw_bounds); + } + + cairo_restore (cr); + + /* Paint the border. */ + if (grid_data->border_width > 0) + { + if (grid_data->border_pattern) + cairo_set_source (cr, grid_data->border_pattern); + else + goo_canvas_style_set_stroke_options (simple_data->style, cr); + + cairo_set_line_width (cr, grid_data->border_width); + half_border_width = grid_data->border_width / 2.0; + cairo_rectangle (cr, grid_data->x - half_border_width, + grid_data->y - half_border_width, + grid_data->width + grid_data->border_width, + grid_data->height + grid_data->border_width); + cairo_stroke (cr); + } +} + + +static void +goo_canvas_grid_set_model (GooCanvasItem *item, + GooCanvasItemModel *model) +{ + GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item; + GooCanvasGrid *grid = (GooCanvasGrid*) item; + GooCanvasGridModel *gmodel = (GooCanvasGridModel*) model; + + /* If our grid_data was allocated, free it. */ + if (!simple->model) + { + goo_canvas_grid_free_data (grid->grid_data); + g_slice_free (GooCanvasGridData, grid->grid_data); + } + + /* Now use the new model's grid_data instead. */ + grid->grid_data = &gmodel->grid_data; + + /* Let the parent class do the rest. */ + goo_canvas_grid_parent_iface->set_model (item, model); +} + + +static void +canvas_item_interface_init (GooCanvasItemIface *iface) +{ + iface->set_model = goo_canvas_grid_set_model; +} + + +static void +goo_canvas_grid_class_init (GooCanvasGridClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass*) klass; + GooCanvasItemSimpleClass *simple_class = (GooCanvasItemSimpleClass*) klass; + + goo_canvas_grid_parent_iface = g_type_interface_peek (goo_canvas_grid_parent_class, GOO_TYPE_CANVAS_ITEM); + + gobject_class->finalize = goo_canvas_grid_finalize; + + gobject_class->get_property = goo_canvas_grid_get_property; + gobject_class->set_property = goo_canvas_grid_set_property; + + simple_class->simple_update = goo_canvas_grid_update; + simple_class->simple_paint = goo_canvas_grid_paint; + + goo_canvas_grid_install_common_properties (gobject_class); +} + + + +/** + * SECTION:goocanvasgridmodel + * @Title: GooCanvasGridModel + * @Short_Description: a model for grid items. + * + * GooCanvasGridModel represents a model for grid items. + * A grid consists of a number of equally-spaced horizontal and vertical + * grid lines, plus an optional border. + * + * It is a subclass of #GooCanvasItemModelSimple and so inherits all of the + * style properties such as "stroke-color", "fill-color" and "line-width". + * + * It also implements the #GooCanvasItemModel interface, so you can use the + * #GooCanvasItemModel functions such as goo_canvas_item_model_raise() and + * goo_canvas_item_model_rotate(). + * + * To create a #GooCanvasGridModel use goo_canvas_grid_model_new(). + * + * To get or set the properties of an existing #GooCanvasGridModel, use + * g_object_get() and g_object_set(). + * + * To respond to events such as mouse clicks on the grid you must connect + * to the signal handlers of the corresponding #GooCanvasGrid objects. + * (See goo_canvas_get_item() and #GooCanvas::item-created.) + * + * The grid's position and size is specified with the #GooCanvasGridModel:x, + * #GooCanvasGridModel:y, #GooCanvasGridModel:width and + * #GooCanvasGridModel:height properties. + * + * The #GooCanvasGridModel:x-step and #GooCanvasGridModel:y-step properties + * specify the distance between grid lines. The #GooCanvasGridModel:x-offset + * and #GooCanvasGridModel:y-offset properties specify the distance before the + * first grid lines. + * + * The horizontal or vertical grid lines can be hidden using the + * #GooCanvasGridModel:show-horz-grid-lines and + * #GooCanvasGridModel:show-vert-grid-lines properties. + * + * The width of the border can be set using the #GooCanvasGridModel:border-width + * property. The border is drawn outside the area specified with the + * #GooCanvasGridModel:x, #GooCanvasGridModel:y, #GooCanvasGridModel:width and + * #GooCanvasGridModel:height properties. + * + * Other properties allow the colors and widths of the grid lines to be set. + * The grid line color and width properties override the standard + * #GooCanvasItemModelSimple:stroke-color and + * #GooCanvasItemModelSimple:line-width properties, enabling different styles + * for horizontal and vertical grid lines. + */ + +GooCanvasItemModelIface *goo_canvas_grid_model_parent_iface; + +static void item_model_interface_init (GooCanvasItemModelIface *iface); + +G_DEFINE_TYPE_WITH_CODE (GooCanvasGridModel, goo_canvas_grid_model, + GOO_TYPE_CANVAS_ITEM_MODEL_SIMPLE, + G_IMPLEMENT_INTERFACE (GOO_TYPE_CANVAS_ITEM_MODEL, + item_model_interface_init)) + + +static void +goo_canvas_grid_model_init (GooCanvasGridModel *gmodel) +{ + goo_canvas_grid_init_data (&gmodel->grid_data); +} + + +/** + * goo_canvas_grid_model_new: + * @parent: the parent model, or %NULL. If a parent is specified, it will assume + * ownership of the item, and the item will automatically be freed when it is + * removed from the parent. Otherwise call g_object_unref() to free it. + * @x: the x coordinate of the left of the grid. + * @y: the y coordinate of the top of the grid. + * @width: the width of the grid. + * @height: the height of the grid. + * @x_step: the distance between the vertical grid lines. + * @y_step: the distance between the horizontal grid lines. + * @x_offset: the distance before the first vertical grid line. + * @y_offset: the distance before the first horizontal grid line. + * @...: optional pairs of property names and values, and a terminating %NULL. + * + * Creates a new grid model. + * + * <!--PARAMETERS--> + * + * Here's an example showing how to create a grid: + * + * <informalexample><programlisting> + * GooCanvasItemModel *grid = goo_canvas_grid_model_new (mygroup, 100.0, 100.0, 400.0, 200.0, + * 20.0, 20.0, 10.0, 10.0, + * "horz-grid-line-width", 4.0, + * "horz-grid-line-color", "yellow", + * "vert-grid-line-width", 2.0, + * "vert-grid-line-color", "red", + * "border-width", 3.0, + * "border-color", "white", + * "fill-color", "blue", + * NULL); + * </programlisting></informalexample> + * + * Returns: a new grid model. + **/ +GooCanvasItemModel* +goo_canvas_grid_model_new (GooCanvasItemModel *parent, + gdouble x, + gdouble y, + gdouble width, + gdouble height, + gdouble x_step, + gdouble y_step, + gdouble x_offset, + gdouble y_offset, + ...) +{ + GooCanvasItemModel *model; + GooCanvasGridModel *gmodel; + GooCanvasGridData *grid_data; + const char *first_property; + va_list var_args; + + model = g_object_new (GOO_TYPE_CANVAS_GRID_MODEL, NULL); + gmodel = (GooCanvasGridModel*) model; + + grid_data = &gmodel->grid_data; + grid_data->x = x; + grid_data->y = y; + grid_data->width = width; + grid_data->height = height; + grid_data->x_step = x_step; + grid_data->y_step = y_step; + grid_data->x_offset = x_offset; + grid_data->y_offset = y_offset; + + va_start (var_args, y_offset); + first_property = va_arg (var_args, char*); + if (first_property) + g_object_set_valist ((GObject*) model, first_property, var_args); + va_end (var_args); + + if (parent) + { + goo_canvas_item_model_add_child (parent, model, -1); + g_object_unref (model); + } + + return model; +} + + +static void +goo_canvas_grid_model_finalize (GObject *object) +{ + GooCanvasGridModel *gmodel = (GooCanvasGridModel*) object; + + goo_canvas_grid_free_data (&gmodel->grid_data); + + G_OBJECT_CLASS (goo_canvas_grid_model_parent_class)->finalize (object); +} + + +static void +goo_canvas_grid_model_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GooCanvasGridModel *gmodel = (GooCanvasGridModel*) object; + + goo_canvas_grid_get_common_property (object, &gmodel->grid_data, + prop_id, value, pspec); +} + + +static void +goo_canvas_grid_model_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GooCanvasGridModel *gmodel = (GooCanvasGridModel*) object; + + goo_canvas_grid_set_common_property (object, &gmodel->grid_data, + prop_id, value, pspec); + g_signal_emit_by_name (gmodel, "changed", TRUE); +} + + +static GooCanvasItem* +goo_canvas_grid_model_create_item (GooCanvasItemModel *model, + GooCanvas *canvas) +{ + GooCanvasItem *item; + + item = g_object_new (GOO_TYPE_CANVAS_GRID, NULL); + goo_canvas_item_set_model (item, model); + + return item; +} + + +static void +item_model_interface_init (GooCanvasItemModelIface *iface) +{ + iface->create_item = goo_canvas_grid_model_create_item; +} + + +static void +goo_canvas_grid_model_class_init (GooCanvasGridModelClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass*) klass; + + goo_canvas_grid_model_parent_iface = g_type_interface_peek (goo_canvas_grid_model_parent_class, GOO_TYPE_CANVAS_ITEM_MODEL); + + gobject_class->finalize = goo_canvas_grid_model_finalize; + + gobject_class->get_property = goo_canvas_grid_model_get_property; + gobject_class->set_property = goo_canvas_grid_model_set_property; + + goo_canvas_grid_install_common_properties (gobject_class); +} + + diff --git a/src/goocanvas/src/goocanvasgrid.h b/src/goocanvas/src/goocanvasgrid.h new file mode 100644 index 0000000..fa70e2e --- /dev/null +++ b/src/goocanvas/src/goocanvasgrid.h @@ -0,0 +1,153 @@ +/* + * GooCanvas. Copyright (C) 2005-8 Damon Chaplin. + * Released under the GNU LGPL license. See COPYING for details. + * + * goocanvasgrid.h - a grid item. + */ +#ifndef __GOO_CANVAS_GRID_H__ +#define __GOO_CANVAS_GRID_H__ + +#include <gtk/gtk.h> +#include "goocanvasitemsimple.h" + +G_BEGIN_DECLS + + +/* This is the data used by both model and view classes. */ +typedef struct _GooCanvasGridData GooCanvasGridData; +struct _GooCanvasGridData +{ + /* The area that the grid covers. */ + gdouble x, y, width, height; + + /* The distance between grid lines. */ + gdouble x_step, y_step; + + /* The offset before the first grid line. */ + gdouble x_offset, y_offset; + + /* The widths of the grid lines, or -ve to use item's stroke width. */ + gdouble horz_grid_line_width, vert_grid_line_width; + + /* The color/pattern for the grid lines, or NULL to use the stroke color. */ + cairo_pattern_t *horz_grid_line_pattern, *vert_grid_line_pattern; + + /* The width of the border around the grid, or -1 for no border. */ + gdouble border_width; + + /* The color/pattern for the border, or NULL to use the stroke color. */ + cairo_pattern_t *border_pattern; + + /* If the horizontal and vertical grid lines should be shown. */ + guint show_horz_grid_lines : 1; + guint show_vert_grid_lines : 1; + + /* If vertical grid lines are drawn on top. */ + guint vert_grid_lines_on_top : 1; +}; + + +#define GOO_TYPE_CANVAS_GRID (goo_canvas_grid_get_type ()) +#define GOO_CANVAS_GRID(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GOO_TYPE_CANVAS_GRID, GooCanvasGrid)) +#define GOO_CANVAS_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GOO_TYPE_CANVAS_GRID, GooCanvasGridClass)) +#define GOO_IS_CANVAS_GRID(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GOO_TYPE_CANVAS_GRID)) +#define GOO_IS_CANVAS_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GOO_TYPE_CANVAS_GRID)) +#define GOO_CANVAS_GRID_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GOO_TYPE_CANVAS_GRID, GooCanvasGridClass)) + + +typedef struct _GooCanvasGrid GooCanvasGrid; +typedef struct _GooCanvasGridClass GooCanvasGridClass; + +/** + * GooCanvasGrid + * + * The #GooCanvasGrid-struct struct contains private data only. + */ +struct _GooCanvasGrid +{ + GooCanvasItemSimple parent_object; + + GooCanvasGridData *grid_data; +}; + +struct _GooCanvasGridClass +{ + GooCanvasItemSimpleClass parent_class; + + /*< private >*/ + + /* Padding for future expansion */ + void (*_goo_canvas_reserved1) (void); + void (*_goo_canvas_reserved2) (void); + void (*_goo_canvas_reserved3) (void); + void (*_goo_canvas_reserved4) (void); +}; + + +GType goo_canvas_grid_get_type (void) G_GNUC_CONST; +GooCanvasItem* goo_canvas_grid_new (GooCanvasItem *parent, + gdouble x, + gdouble y, + gdouble width, + gdouble height, + gdouble x_step, + gdouble y_step, + gdouble x_offset, + gdouble y_offset, + ...); + + + +#define GOO_TYPE_CANVAS_GRID_MODEL (goo_canvas_grid_model_get_type ()) +#define GOO_CANVAS_GRID_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GOO_TYPE_CANVAS_GRID_MODEL, GooCanvasGridModel)) +#define GOO_CANVAS_GRID_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GOO_TYPE_CANVAS_GRID_MODEL, GooCanvasGridModelClass)) +#define GOO_IS_CANVAS_GRID_MODEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GOO_TYPE_CANVAS_GRID_MODEL)) +#define GOO_IS_CANVAS_GRID_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GOO_TYPE_CANVAS_GRID_MODEL)) +#define GOO_CANVAS_GRID_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GOO_TYPE_CANVAS_GRID_MODEL, GooCanvasGridModelClass)) + + +typedef struct _GooCanvasGridModel GooCanvasGridModel; +typedef struct _GooCanvasGridModelClass GooCanvasGridModelClass; + +/** + * GooCanvasGridModel + * + * The #GooCanvasGridModel-struct struct contains private data only. + */ +struct _GooCanvasGridModel +{ + GooCanvasItemModelSimple parent_object; + + GooCanvasGridData grid_data; +}; + +struct _GooCanvasGridModelClass +{ + GooCanvasItemModelSimpleClass parent_class; + + /*< private >*/ + + /* Padding for future expansion */ + void (*_goo_canvas_reserved1) (void); + void (*_goo_canvas_reserved2) (void); + void (*_goo_canvas_reserved3) (void); + void (*_goo_canvas_reserved4) (void); +}; + + +GType goo_canvas_grid_model_get_type (void) G_GNUC_CONST; +GooCanvasItemModel* goo_canvas_grid_model_new (GooCanvasItemModel *parent, + gdouble x, + gdouble y, + gdouble width, + gdouble height, + gdouble x_step, + gdouble y_step, + gdouble x_offset, + gdouble y_offset, + ...); + + +G_END_DECLS + +#endif /* __GOO_CANVAS_GRID_H__ */ diff --git a/src/goocanvas/src/goocanvasgroup.c b/src/goocanvas/src/goocanvasgroup.c index c668fb3..0f709ac 100644 --- a/src/goocanvas/src/goocanvasgroup.c +++ b/src/goocanvas/src/goocanvasgroup.c @@ -25,6 +25,9 @@ * goo_canvas_item_rotate(), and the properties such as "visibility" and * "pointer-events". * + * If the #GooCanvasGroup:width and #GooCanvasGroup:height properties are + * set to positive values then the group is clipped to the given size. + * * To create a #GooCanvasGroup use goo_canvas_group_new(). * * To get or set the properties of an existing #GooCanvasGroup, use @@ -40,9 +43,38 @@ #include "goocanvasmarshal.h" #include "goocanvasatk.h" +typedef struct _GooCanvasGroupPrivate GooCanvasGroupPrivate; +struct _GooCanvasGroupPrivate { + gdouble x; + gdouble y; + gdouble width; + gdouble height; +}; + +#define GOO_CANVAS_GROUP_GET_PRIVATE(group) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((group), GOO_TYPE_CANVAS_GROUP, GooCanvasGroupPrivate)) +#define GOO_CANVAS_GROUP_MODEL_GET_PRIVATE(group) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((group), GOO_TYPE_CANVAS_GROUP_MODEL, GooCanvasGroupPrivate)) + +enum { + PROP_0, + + PROP_X, + PROP_Y, + PROP_WIDTH, + PROP_HEIGHT +}; static void goo_canvas_group_dispose (GObject *object); static void goo_canvas_group_finalize (GObject *object); +static void goo_canvas_group_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static void goo_canvas_group_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); static void canvas_item_interface_init (GooCanvasItemIface *iface); G_DEFINE_TYPE_WITH_CODE (GooCanvasGroup, goo_canvas_group, @@ -50,14 +82,53 @@ G_DEFINE_TYPE_WITH_CODE (GooCanvasGroup, goo_canvas_group, G_IMPLEMENT_INTERFACE (GOO_TYPE_CANVAS_ITEM, canvas_item_interface_init)) +static void +goo_canvas_group_install_common_properties (GObjectClass *gobject_class) +{ + g_object_class_install_property (gobject_class, PROP_X, + g_param_spec_double ("x", + "X", + _("The x coordinate of the group"), + -G_MAXDOUBLE, + G_MAXDOUBLE, 0.0, + G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_Y, + g_param_spec_double ("y", + "Y", + _("The y coordinate of the group"), + -G_MAXDOUBLE, + G_MAXDOUBLE, 0.0, + G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_WIDTH, + g_param_spec_double ("width", + _("Width"), + _("The width of the group, or -1 to use the default width"), + -G_MAXDOUBLE, + G_MAXDOUBLE, -1.0, + G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_HEIGHT, + g_param_spec_double ("height", + _("Height"), + _("The height of the group, or -1 to use the default height"), + -G_MAXDOUBLE, + G_MAXDOUBLE, -1.0, + G_PARAM_READWRITE)); +} static void goo_canvas_group_class_init (GooCanvasGroupClass *klass) { GObjectClass *gobject_class = (GObjectClass*) klass; + g_type_class_add_private (gobject_class, sizeof (GooCanvasGroupPrivate)); + gobject_class->dispose = goo_canvas_group_dispose; gobject_class->finalize = goo_canvas_group_finalize; + gobject_class->get_property = goo_canvas_group_get_property; + gobject_class->set_property = goo_canvas_group_set_property; /* Register our accessible factory, but only if accessibility is enabled. */ if (!ATK_IS_NO_OP_OBJECT_FACTORY (atk_registry_get_factory (atk_get_default_registry (), GTK_TYPE_WIDGET))) @@ -66,13 +137,22 @@ goo_canvas_group_class_init (GooCanvasGroupClass *klass) GOO_TYPE_CANVAS_GROUP, goo_canvas_item_accessible_factory_get_type ()); } + + goo_canvas_group_install_common_properties (gobject_class); } static void goo_canvas_group_init (GooCanvasGroup *group) { + GooCanvasGroupPrivate* priv = GOO_CANVAS_GROUP_GET_PRIVATE (group); + group->items = g_ptr_array_sized_new (8); + + priv->x = 0.0; + priv->y = 0.0; + priv->width = -1.0; + priv->height = -1.0; } @@ -146,12 +226,113 @@ goo_canvas_group_finalize (GObject *object) } +/* Gets the private data to use, from the model or from the item itself. */ +static GooCanvasGroupPrivate* +goo_canvas_group_get_private (GooCanvasGroup *group) +{ + GooCanvasItemSimple *simple = (GooCanvasItemSimple*) group; + + if (simple->model) + return GOO_CANVAS_GROUP_MODEL_GET_PRIVATE (simple->model); + else + return GOO_CANVAS_GROUP_GET_PRIVATE (group); +} + + +static void +goo_canvas_group_get_common_property (GObject *object, + GooCanvasGroupPrivate *priv, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) + { + case PROP_X: + g_value_set_double (value, priv->x); + break; + case PROP_Y: + g_value_set_double (value, priv->y); + break; + case PROP_WIDTH: + g_value_set_double (value, priv->width); + break; + case PROP_HEIGHT: + g_value_set_double (value, priv->height); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +goo_canvas_group_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GooCanvasGroup *group = (GooCanvasGroup*) object; + GooCanvasGroupPrivate *priv = goo_canvas_group_get_private (group); + + goo_canvas_group_get_common_property (object, priv, prop_id, value, pspec); +} + +static void +goo_canvas_group_set_common_property (GObject *object, + GooCanvasGroupPrivate *priv, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) + { + case PROP_X: + priv->x = g_value_get_double (value); + break; + case PROP_Y: + priv->y = g_value_get_double (value); + break; + case PROP_WIDTH: + priv->width = g_value_get_double (value); + break; + case PROP_HEIGHT: + priv->height = g_value_get_double (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +goo_canvas_group_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GooCanvasItemSimple *simple = (GooCanvasItemSimple*) object; + GooCanvasGroup *group = (GooCanvasGroup*) object; + GooCanvasGroupPrivate *priv = goo_canvas_group_get_private (group); + + if (simple->model) + { + g_warning ("Can't set property of a canvas item with a model - set the model property instead"); + return; + } + + goo_canvas_group_set_common_property (object, priv, prop_id, value, pspec); + goo_canvas_item_simple_changed (simple, TRUE); +} + static void goo_canvas_group_add_child (GooCanvasItem *item, GooCanvasItem *child, gint position) { + GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item; GooCanvasGroup *group = (GooCanvasGroup*) item; + AtkObject *atk_obj, *child_atk_obj; g_object_ref (child); @@ -166,6 +347,16 @@ goo_canvas_group_add_child (GooCanvasItem *item, } goo_canvas_item_set_parent (child, item); + goo_canvas_item_set_is_static (child, simple->simple_data->is_static); + + /* Emit the "children_changed" ATK signal, if ATK is enabled. */ + atk_obj = atk_gobject_accessible_for_object (G_OBJECT (item)); + if (!ATK_IS_NO_OP_OBJECT (atk_obj)) + { + child_atk_obj = atk_gobject_accessible_for_object (G_OBJECT (child)); + g_signal_emit_by_name (atk_obj, "children_changed::add", + position, child_atk_obj); + } goo_canvas_item_request_update (item); } @@ -186,7 +377,8 @@ goo_canvas_group_move_child (GooCanvasItem *item, if (simple->canvas) { goo_canvas_item_get_bounds (child, &bounds); - goo_canvas_request_redraw (simple->canvas, &bounds); + goo_canvas_request_item_redraw (simple->canvas, &bounds, + simple->simple_data->is_static); } goo_canvas_util_ptr_array_move (group->items, old_position, new_position); @@ -203,6 +395,7 @@ goo_canvas_group_remove_child (GooCanvasItem *item, GooCanvasGroup *group = (GooCanvasGroup*) item; GooCanvasItem *child; GooCanvasBounds bounds; + AtkObject *atk_obj, *child_atk_obj; g_return_if_fail (child_num < group->items->len); @@ -211,14 +404,24 @@ goo_canvas_group_remove_child (GooCanvasItem *item, if (simple->canvas) { goo_canvas_item_get_bounds (child, &bounds); - goo_canvas_request_redraw (simple->canvas, &bounds); + goo_canvas_request_item_redraw (simple->canvas, &bounds, + simple->simple_data->is_static); } - goo_canvas_item_set_parent (child, NULL); - g_object_unref (child); + /* Emit the "children_changed" ATK signal, if ATK is enabled. */ + atk_obj = atk_gobject_accessible_for_object (G_OBJECT (item)); + if (!ATK_IS_NO_OP_OBJECT (atk_obj)) + { + child_atk_obj = atk_gobject_accessible_for_object (G_OBJECT (child)); + g_signal_emit_by_name (atk_obj, "children_changed::remove", + child_num, child_atk_obj); + } g_ptr_array_remove_index (group->items, child_num); + goo_canvas_item_set_parent (child, NULL); + g_object_unref (child); + goo_canvas_item_request_update (item); } @@ -238,7 +441,9 @@ goo_canvas_group_get_child (GooCanvasItem *item, { GooCanvasGroup *group = (GooCanvasGroup*) item; - return group->items->pdata[child_num]; + if (child_num < group->items->len) + return group->items->pdata[child_num]; + return NULL; } @@ -252,6 +457,9 @@ goo_canvas_group_set_canvas (GooCanvasItem *item, GooCanvasGroup *group = (GooCanvasGroup*) item; gint i; + if (simple->canvas == canvas) + return; + simple->canvas = canvas; /* Recursively set the canvas of all child items. */ @@ -264,6 +472,29 @@ goo_canvas_group_set_canvas (GooCanvasItem *item, static void +goo_canvas_group_set_is_static (GooCanvasItem *item, + gboolean is_static) +{ + GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item; + GooCanvasItemSimpleData *simple_data = simple->simple_data; + GooCanvasGroup *group = (GooCanvasGroup*) item; + gint i; + + if (simple_data->is_static == is_static) + return; + + simple_data->is_static = is_static; + + /* Recursively set the canvas of all child items. */ + for (i = 0; i < group->items->len; i++) + { + GooCanvasItem *item = group->items->pdata[i]; + goo_canvas_item_set_is_static (item, is_static); + } +} + + +static void on_model_child_added (GooCanvasGroupModel *model, gint position, GooCanvasGroup *group) @@ -353,6 +584,7 @@ goo_canvas_group_update (GooCanvasItem *item, { GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item; GooCanvasGroup *group = (GooCanvasGroup*) item; + GooCanvasGroupPrivate *priv = goo_canvas_group_get_private (group); GooCanvasBounds child_bounds; gboolean initial_bounds = TRUE; gint i; @@ -372,35 +604,38 @@ goo_canvas_group_update (GooCanvasItem *item, cairo_save (cr); if (simple->simple_data->transform) - cairo_transform (cr, simple->simple_data->transform); + cairo_transform (cr, simple->simple_data->transform); + + cairo_translate (cr, priv->x, priv->y); for (i = 0; i < group->items->len; i++) - { - GooCanvasItem *child = group->items->pdata[i]; - - goo_canvas_item_update (child, entire_tree, cr, &child_bounds); - - /* If the child has non-empty bounds, compute the union. */ - if (child_bounds.x1 < child_bounds.x2 - && child_bounds.y1 < child_bounds.y2) - { - if (initial_bounds) - { - simple->bounds.x1 = child_bounds.x1; - simple->bounds.y1 = child_bounds.y1; - simple->bounds.x2 = child_bounds.x2; - simple->bounds.y2 = child_bounds.y2; - initial_bounds = FALSE; - } - else - { - simple->bounds.x1 = MIN (simple->bounds.x1, child_bounds.x1); - simple->bounds.y1 = MIN (simple->bounds.y1, child_bounds.y1); - simple->bounds.x2 = MAX (simple->bounds.x2, child_bounds.x2); - simple->bounds.y2 = MAX (simple->bounds.y2, child_bounds.y2); - } - } - } + { + GooCanvasItem *child = group->items->pdata[i]; + + goo_canvas_item_update (child, entire_tree, cr, &child_bounds); + + /* If the child has non-empty bounds, compute the union. */ + if (child_bounds.x1 < child_bounds.x2 + && child_bounds.y1 < child_bounds.y2) + { + if (initial_bounds) + { + simple->bounds.x1 = child_bounds.x1; + simple->bounds.y1 = child_bounds.y1; + simple->bounds.x2 = child_bounds.x2; + simple->bounds.y2 = child_bounds.y2; + initial_bounds = FALSE; + } + else + { + simple->bounds.x1 = MIN (simple->bounds.x1, child_bounds.x1); + simple->bounds.y1 = MIN (simple->bounds.y1, child_bounds.y1); + simple->bounds.x2 = MAX (simple->bounds.x2, child_bounds.x2); + simple->bounds.y2 = MAX (simple->bounds.y2, child_bounds.y2); + } + } + } + cairo_restore (cr); } @@ -420,6 +655,7 @@ goo_canvas_group_get_items_at (GooCanvasItem *item, GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item; GooCanvasItemSimpleData *simple_data = simple->simple_data; GooCanvasGroup *group = (GooCanvasGroup*) item; + GooCanvasGroupPrivate *priv = goo_canvas_group_get_private (group); gboolean visible = parent_visible; int i; @@ -447,6 +683,8 @@ goo_canvas_group_get_items_at (GooCanvasItem *item, if (simple_data->transform) cairo_transform (cr, simple_data->transform); + cairo_translate (cr, priv->x, priv->y); + /* If the group has a clip path, check if the point is inside it. */ if (simple_data->clip_path_commands) { @@ -462,6 +700,19 @@ goo_canvas_group_get_items_at (GooCanvasItem *item, } } + if (priv->width > 0.0 && priv->height > 0.0) + { + double user_x = x, user_y = y; + + cairo_device_to_user (cr, &user_x, &user_y); + if (user_x < 0.0 || user_x >= priv->width + || user_y < 0.0 || user_y >= priv->height) + { + cairo_restore (cr); + return found_items; + } + } + /* Step up from the bottom of the children to the top, adding any items found to the start of the list. */ for (i = 0; i < group->items->len; i++) @@ -487,6 +738,7 @@ goo_canvas_group_paint (GooCanvasItem *item, GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item; GooCanvasItemSimpleData *simple_data = simple->simple_data; GooCanvasGroup *group = (GooCanvasGroup*) item; + GooCanvasGroupPrivate *priv = goo_canvas_group_get_private (group); gint i; /* Skip the item if the bounds don't intersect the expose rectangle. */ @@ -505,6 +757,8 @@ goo_canvas_group_paint (GooCanvasItem *item, if (simple_data->transform) cairo_transform (cr, simple_data->transform); + cairo_translate (cr, priv->x, priv->y); + /* Clip with the group's clip path, if it is set. */ if (simple_data->clip_path_commands) { @@ -513,6 +767,12 @@ goo_canvas_group_paint (GooCanvasItem *item, cairo_clip (cr); } + if (priv->width > 0.0 && priv->height > 0.0) + { + cairo_rectangle (cr, 0.0, 0.0, priv->width, priv->height); + cairo_clip (cr); + } + for (i = 0; i < group->items->len; i++) { GooCanvasItem *child = group->items->pdata[i]; @@ -539,6 +799,7 @@ canvas_item_interface_init (GooCanvasItemIface *iface) iface->paint = goo_canvas_group_paint; iface->set_model = goo_canvas_group_set_model; + iface->set_is_static = goo_canvas_group_set_is_static; } @@ -574,6 +835,14 @@ canvas_item_interface_init (GooCanvasItemIface *iface) static void item_model_interface_init (GooCanvasItemModelIface *iface); static void goo_canvas_group_model_dispose (GObject *object); static void goo_canvas_group_model_finalize (GObject *object); +static void goo_canvas_group_model_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static void goo_canvas_group_model_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); G_DEFINE_TYPE_WITH_CODE (GooCanvasGroupModel, goo_canvas_group_model, GOO_TYPE_CANVAS_ITEM_MODEL_SIMPLE, @@ -585,16 +854,27 @@ static void goo_canvas_group_model_class_init (GooCanvasGroupModelClass *klass) { GObjectClass *gobject_class = (GObjectClass*) klass; + g_type_class_add_private (gobject_class, sizeof (GooCanvasGroupPrivate)); gobject_class->dispose = goo_canvas_group_model_dispose; gobject_class->finalize = goo_canvas_group_model_finalize; + gobject_class->get_property = goo_canvas_group_model_get_property; + gobject_class->set_property = goo_canvas_group_model_set_property; + + goo_canvas_group_install_common_properties (gobject_class); } static void goo_canvas_group_model_init (GooCanvasGroupModel *gmodel) { + GooCanvasGroupPrivate *priv = GOO_CANVAS_GROUP_MODEL_GET_PRIVATE (gmodel); gmodel->children = g_ptr_array_sized_new (8); + + priv->x = 0.0; + priv->y = 0.0; + priv->width = -1.0; + priv->height = -1.0; } @@ -667,6 +947,28 @@ goo_canvas_group_model_finalize (GObject *object) G_OBJECT_CLASS (goo_canvas_group_model_parent_class)->finalize (object); } +static void goo_canvas_group_model_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GooCanvasGroupModel *model = (GooCanvasGroupModel*) object; + GooCanvasGroupPrivate *priv = GOO_CANVAS_GROUP_MODEL_GET_PRIVATE (model); + + goo_canvas_group_get_common_property (object, priv, prop_id, value, pspec); +} + +static void goo_canvas_group_model_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GooCanvasGroupModel *model = (GooCanvasGroupModel*) object; + GooCanvasGroupPrivate *priv = GOO_CANVAS_GROUP_MODEL_GET_PRIVATE (model); + + goo_canvas_group_set_common_property (object, priv, prop_id, value, pspec); + g_signal_emit_by_name (model, "changed", TRUE); +} extern void _goo_canvas_item_model_emit_child_added (GooCanvasItemModel *model, gint position); @@ -719,11 +1021,12 @@ goo_canvas_group_model_remove_child (GooCanvasItemModel *model, child = gmodel->children->pdata[child_num]; goo_canvas_item_model_set_parent (child, NULL); - g_object_unref (child); g_ptr_array_remove_index (gmodel->children, child_num); g_signal_emit_by_name (gmodel, "child-removed", child_num); + + g_object_unref (child); } @@ -742,7 +1045,9 @@ goo_canvas_group_model_get_child (GooCanvasItemModel *model, { GooCanvasGroupModel *gmodel = (GooCanvasGroupModel*) model; - return gmodel->children->pdata[child_num]; + if (child_num < gmodel->children->len) + return gmodel->children->pdata[child_num]; + return NULL; } diff --git a/src/goocanvas/src/goocanvasitem.c b/src/goocanvas/src/goocanvasitem.c index 4e1e095..57db899 100644 --- a/src/goocanvas/src/goocanvasitem.c +++ b/src/goocanvas/src/goocanvasitem.c @@ -48,6 +48,8 @@ enum { GRAB_BROKEN_EVENT, CHILD_NOTIFY, ANIMATION_FINISHED, + SCROLL_EVENT, + QUERY_TOOLTIP, LAST_SIGNAL }; @@ -101,7 +103,7 @@ goo_canvas_item_base_init (gpointer g_iface) { static GObjectNotifyContext cpn_context = { 0, NULL, NULL }; static gboolean initialized = FALSE; - + if (!initialized) { GType iface_type = G_TYPE_FROM_INTERFACE (g_iface); @@ -118,8 +120,9 @@ goo_canvas_item_base_init (gpointer g_iface) * GooCanvasItem::enter-notify-event * @item: the item that received the signal. * @target_item: the target of the event. - * @event: the event data, with coordinates translated to canvas - * coordinates. + * @event: the event data. The x & y fields contain the mouse position + * in the item's coordinate space. The x_root & y_root fields contain + * the same coordinates converted to the canvas coordinate space. * * Emitted when the mouse enters an item. * @@ -142,8 +145,9 @@ goo_canvas_item_base_init (gpointer g_iface) * GooCanvasItem::leave-notify-event * @item: the item that received the signal. * @target_item: the target of the event. - * @event: the event data, with coordinates translated to canvas - * coordinates. + * @event: the event data. The x & y fields contain the mouse position + * in the item's coordinate space. The x_root & y_root fields contain + * the same coordinates converted to the canvas coordinate space. * * Emitted when the mouse leaves an item. * @@ -166,8 +170,9 @@ goo_canvas_item_base_init (gpointer g_iface) * GooCanvasItem::motion-notify-event * @item: the item that received the signal. * @target_item: the target of the event. - * @event: the event data, with coordinates translated to canvas - * coordinates. + * @event: the event data. The x & y fields contain the mouse position + * in the item's coordinate space. The x_root & y_root fields contain + * the same coordinates converted to the canvas coordinate space. * * Emitted when the mouse moves within an item. * @@ -190,8 +195,9 @@ goo_canvas_item_base_init (gpointer g_iface) * GooCanvasItem::button-press-event * @item: the item that received the signal. * @target_item: the target of the event. - * @event: the event data, with coordinates translated to canvas - * coordinates. + * @event: the event data. The x & y fields contain the mouse position + * in the item's coordinate space. The x_root & y_root fields contain + * the same coordinates converted to the canvas coordinate space. * * Emitted when a mouse button is pressed in an item. * @@ -214,8 +220,9 @@ goo_canvas_item_base_init (gpointer g_iface) * GooCanvasItem::button-release-event * @item: the item that received the signal. * @target_item: the target of the event. - * @event: the event data, with coordinates translated to canvas - * coordinates. + * @event: the event data. The x & y fields contain the mouse position + * in the item's coordinate space. The x_root & y_root fields contain + * the same coordinates converted to the canvas coordinate space. * * Emitted when a mouse button is released in an item. * @@ -331,6 +338,37 @@ goo_canvas_item_base_init (gpointer g_iface) GOO_TYPE_CANVAS_ITEM, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); + /** + * GooCanvasItem::query-tooltip: + * @item: the item which received the signal. + * @x: the x coordinate of the mouse. + * @y: the y coordinate of the mouse. + * @keyboard_mode: %TRUE if the tooltip was triggered using the keyboard. + * @tooltip: a #GtkTooltip. + * + * Emitted when the mouse has paused over the item for a certain amount + * of time, or the tooltip was requested via the keyboard. + * + * Note that if @keyboard_mode is %TRUE, the values of @x and @y are + * undefined and should not be used. + * + * If the item wants to display a tooltip it should update @tooltip + * and return %TRUE. + * + * Returns: %TRUE if the item has set a tooltip to show. + */ + canvas_item_signals[QUERY_TOOLTIP] = + g_signal_new ("query-tooltip", + iface_type, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GooCanvasItemIface, query_tooltip), + goo_canvas_boolean_handled_accumulator, NULL, + goo_canvas_marshal_BOOLEAN__DOUBLE_DOUBLE_BOOLEAN_OBJECT, + G_TYPE_BOOLEAN, 4, + G_TYPE_DOUBLE, + G_TYPE_DOUBLE, + G_TYPE_BOOLEAN, + GTK_TYPE_TOOLTIP); /** * GooCanvasItem::grab-broken-event @@ -362,7 +400,7 @@ goo_canvas_item_base_init (gpointer g_iface) * @pspec: the #GParamSpec of the changed child property. * * Emitted for each child property that has changed. - * The signal's detail holds the property name. + * The signal's detail holds the property name. */ canvas_item_signals[CHILD_NOTIFY] = g_signal_new ("child_notify", @@ -374,7 +412,6 @@ goo_canvas_item_base_init (gpointer g_iface) G_TYPE_NONE, 1, G_TYPE_PARAM); - /** * GooCanvasItem::animation-finished * @item: the item that received the signal. @@ -392,6 +429,33 @@ goo_canvas_item_base_init (gpointer g_iface) G_TYPE_NONE, 1, G_TYPE_BOOLEAN); + /** + * GooCanvasItem::scroll-event + * @item: the item that received the signal. + * @target_item: the target of the event. + * @event: the event data. The x & y fields contain the mouse position + * in the item's coordinate space. The x_root & y_root fields contain + * the same coordinates converted to the canvas coordinate space. + * + * Emitted when a button in the 4 to 7 range is pressed. Wheel mice are + * usually configured to generate button press events for buttons 4 and 5 + * when the wheel is turned in an item. + * + * Returns: %TRUE to stop the signal emission, or %FALSE to let it + * continue. + */ + canvas_item_signals[SCROLL_EVENT] = + g_signal_new ("scroll_event", + iface_type, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GooCanvasItemIface, + scroll_event), + goo_canvas_boolean_handled_accumulator, NULL, + goo_canvas_marshal_BOOLEAN__OBJECT_BOXED, + G_TYPE_BOOLEAN, 2, + GOO_TYPE_CANVAS_ITEM, + GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); + g_object_interface_install_property (g_iface, g_param_spec_object ("parent", _("Parent"), @@ -452,6 +516,13 @@ goo_canvas_item_base_init (gpointer g_iface) FALSE, G_PARAM_READWRITE)); + g_object_interface_install_property (g_iface, + g_param_spec_string ("tooltip", + _("Tooltip"), + _("The tooltip to display for the item"), + NULL, + G_PARAM_READWRITE)); + _goo_canvas_style_init (); initialized = TRUE; @@ -462,9 +533,9 @@ goo_canvas_item_base_init (gpointer g_iface) /** * goo_canvas_item_get_canvas: * @item: a #GooCanvasItem. - * + * * Returns the #GooCanvas containing the given #GooCanvasItem. - * + * * Returns: the #GooCanvas. **/ GooCanvas* @@ -491,7 +562,7 @@ goo_canvas_item_get_canvas (GooCanvasItem *item) * goo_canvas_item_set_canvas: * @item: a #GooCanvasItem. * @canvas: a #GooCanvas - * + * * This function is only intended to be used when implementing new canvas * items, specifically container items such as #GooCanvasGroup. * @@ -514,7 +585,7 @@ goo_canvas_item_set_canvas (GooCanvasItem *item, * @child: the item to add. * @position: the position of the item, or -1 to place it last (at the top of * the stacking order). - * + * * Adds a child item to a container item at the given stack position. **/ void @@ -536,7 +607,7 @@ goo_canvas_item_add_child (GooCanvasItem *item, * @item: a container item. * @old_position: the current position of the child item. * @new_position: the new position of the child item. - * + * * Moves a child item to a new stack position within the container. **/ void @@ -556,7 +627,7 @@ goo_canvas_item_move_child (GooCanvasItem *item, * goo_canvas_item_remove_child: * @item: a container item. * @child_num: the position of the child item to remove. - * + * * Removes the child item at the given position. **/ void @@ -575,9 +646,9 @@ goo_canvas_item_remove_child (GooCanvasItem *item, * goo_canvas_item_find_child: * @item: a container item. * @child: the child item to find. - * + * * Attempts to find the given child item with the container's stack. - * + * * Returns: the position of the given @child item, or -1 if it isn't found. **/ gint @@ -602,9 +673,9 @@ goo_canvas_item_find_child (GooCanvasItem *item, /** * goo_canvas_item_is_container: * @item: an item. - * + * * Tests to see if the given item is a container. - * + * * Returns: %TRUE if the item is a container. **/ gboolean @@ -619,9 +690,9 @@ goo_canvas_item_is_container (GooCanvasItem *item) /** * goo_canvas_item_get_n_children: * @item: a container item. - * + * * Gets the number of children of the container. - * + * * Returns: the number of children. **/ gint @@ -637,10 +708,11 @@ goo_canvas_item_get_n_children (GooCanvasItem *item) * goo_canvas_item_get_child: * @item: a container item. * @child_num: the position of a child in the container's stack. - * + * * Gets the child item at the given stack position. - * - * Returns: the child item at the given stack position. + * + * Returns: the child item at the given stack position, or %NULL if @child_num + * is out of range. **/ GooCanvasItem* goo_canvas_item_get_child (GooCanvasItem *item, @@ -655,9 +727,9 @@ goo_canvas_item_get_child (GooCanvasItem *item, /** * goo_canvas_item_get_parent: * @item: an item. - * + * * Gets the parent of the given item. - * + * * Returns: the parent item, or %NULL if the item has no parent. **/ GooCanvasItem* @@ -673,7 +745,7 @@ goo_canvas_item_get_parent (GooCanvasItem *item) * goo_canvas_item_set_parent: * @item: an item. * @parent: the new parent item. - * + * * This function is only intended to be used when implementing new canvas * items (specifically container items such as #GooCanvasGroup). * It sets the parent of the child item. @@ -693,9 +765,52 @@ goo_canvas_item_set_parent (GooCanvasItem *item, /** - * goo_canvas_item_remove: + * goo_canvas_item_get_is_static: * @item: an item. + * + * Returns %TRUE if the item is static. Static items do not move or change + * size when the canvas is scrolled or the scale changes. * + * Returns: %TRUE if the item is static. + **/ +gboolean +goo_canvas_item_get_is_static (GooCanvasItem *item) +{ + GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item); + + if (iface->get_is_static) + return iface->get_is_static (item); + return FALSE; +} + + +/** + * goo_canvas_item_set_is_static: + * @item: an item. + * @is_static: if the item is static. + * + * Notifies the item that it is static. Static items do not move or change + * size when the canvas is scrolled or the scale changes. + * + * Container items such as #GooCanvasGroup should call this function when + * children are added, to notify children whether they are static or not. + * Containers should also pass on any changes in their own status to children. + **/ +void +goo_canvas_item_set_is_static (GooCanvasItem *item, + gboolean is_static) +{ + GooCanvasItemIface *iface = GOO_CANVAS_ITEM_GET_IFACE (item); + + if (iface->set_is_static) + iface->set_is_static (item, is_static); +} + + +/** + * goo_canvas_item_remove: + * @item: an item. + * * Removes an item from its parent. If the item is in a canvas it will be * removed. * @@ -724,7 +839,7 @@ goo_canvas_item_remove (GooCanvasItem *item) * @item: an item. * @above: the item to raise @item above, or %NULL to raise @item to the top * of the stack. - * + * * Raises an item in the stacking order. **/ void @@ -767,7 +882,7 @@ goo_canvas_item_raise (GooCanvasItem *item, * @item: an item. * @below: the item to lower @item below, or %NULL to lower @item to the * bottom of the stack. - * + * * Lowers an item in the stacking order. **/ void @@ -809,9 +924,9 @@ goo_canvas_item_lower (GooCanvasItem *item, * goo_canvas_item_get_transform: * @item: an item. * @transform: the place to store the transform. - * + * * Gets the transformation matrix of an item. - * + * * Returns: %TRUE if a transform is set. **/ gboolean @@ -829,11 +944,11 @@ goo_canvas_item_get_transform (GooCanvasItem *item, * @item: an item. * @child: a child of @item. * @transform: the place to store the transform. - * + * * Gets the transformation matrix of an item combined with any special * transform needed for the given child. These special transforms are used * by layout items such as #GooCanvasTable. - * + * * Returns: %TRUE if a transform is set. **/ gboolean @@ -859,7 +974,7 @@ goo_canvas_item_get_transform_for_child (GooCanvasItem *item, * @item: an item. * @transform: the new transformation matrix, or %NULL to reset the * transformation to the identity matrix. - * + * * Sets the transformation matrix of an item. **/ void @@ -877,13 +992,13 @@ goo_canvas_item_set_transform (GooCanvasItem *item, * @y: returns the y coordinate of the origin of the item's coordinate space. * @scale: returns the scale of the item. * @rotation: returns the clockwise rotation of the item, in degrees (0-360). - * + * * This function can be used to get the position, scale and rotation of an * item, providing that the item has a simple transformation matrix * (e.g. set with goo_canvas_item_set_simple_transform(), or using a * combination of simple translate, scale and rotate operations). If the item * has a complex transformation matrix the results will be incorrect. - * + * * Returns: %TRUE if a transform is set. **/ gboolean @@ -932,7 +1047,7 @@ goo_canvas_item_get_simple_transform (GooCanvasItem *item, * @y: the y coordinate of the origin of the item's coordinate space. * @scale: the scale of the item. * @rotation: the clockwise rotation of the item, in degrees. - * + * * A convenience function to set the item's transformation matrix. **/ void @@ -957,7 +1072,7 @@ goo_canvas_item_set_simple_transform (GooCanvasItem *item, * @item: an item. * @tx: the amount to move the origin in the horizontal direction. * @ty: the amount to move the origin in the vertical direction. - * + * * Translates the origin of the item's coordinate system by the given amounts. **/ void @@ -979,7 +1094,7 @@ goo_canvas_item_translate (GooCanvasItem *item, * @item: an item. * @sx: the amount to scale the horizontal axis. * @sy: the amount to scale the vertical axis. - * + * * Scales the item's coordinate system by the given amounts. **/ void @@ -1002,7 +1117,7 @@ goo_canvas_item_scale (GooCanvasItem *item, * @degrees: the clockwise angle of rotation. * @cx: the x coordinate of the origin of the rotation. * @cy: the y coordinate of the origin of the rotation. - * + * * Rotates the item's coordinate system by the given amount, about the given * origin. **/ @@ -1030,7 +1145,7 @@ goo_canvas_item_rotate (GooCanvasItem *item, * @degrees: the skew angle. * @cx: the x coordinate of the origin of the skew transform. * @cy: the y coordinate of the origin of the skew transform. - * + * * Skews the item's coordinate system along the x axis by the given amount, * about the given origin. **/ @@ -1059,7 +1174,7 @@ goo_canvas_item_skew_x (GooCanvasItem *item, * @degrees: the skew angle. * @cx: the x coordinate of the origin of the skew transform. * @cy: the y coordinate of the origin of the skew transform. - * + * * Skews the item's coordinate system along the y axis by the given amount, * about the given origin. **/ @@ -1085,10 +1200,10 @@ goo_canvas_item_skew_y (GooCanvasItem *item, /** * goo_canvas_item_get_style: * @item: an item. - * + * * Gets the item's style. If the item doesn't have its own style it will return * its parent's style. - * + * * Returns: the item's style. **/ GooCanvasStyle* @@ -1104,7 +1219,7 @@ goo_canvas_item_get_style (GooCanvasItem *item) * goo_canvas_item_set_style: * @item: an item. * @style: a style. - * + * * Sets the item's style, by copying the properties from the given style. **/ void @@ -1342,7 +1457,7 @@ _goo_canvas_item_animate_internal (GooCanvasItem *item, * second). * @step_time: the time between each animation step, in milliseconds. * @type: specifies what happens when the animation finishes. - * + * * Animates an item from its current position to the given offsets, scale * and rotation. **/ @@ -1365,7 +1480,7 @@ goo_canvas_item_animate (GooCanvasItem *item, /** * goo_canvas_item_stop_animation: * @item: an item. - * + * * Stops any current animation for the given item, leaving it at its current * position. **/ @@ -1384,7 +1499,7 @@ goo_canvas_item_stop_animation (GooCanvasItem *item) /** * goo_canvas_item_request_update: * @item: a #GooCanvasItem. - * + * * This function is only intended to be used when implementing new canvas * items. * @@ -1407,7 +1522,7 @@ goo_canvas_item_request_update (GooCanvasItem *item) * goo_canvas_item_get_bounds: * @item: a #GooCanvasItem. * @bounds: a #GooCanvasBounds to return the bounds in. - * + * * Gets the bounds of the item. * * Note that the bounds includes the entire fill and stroke extents of the @@ -1434,12 +1549,12 @@ goo_canvas_item_get_bounds (GooCanvasItem *item, * @parent_is_visible: %TRUE if the parent item is visible (which * implies that all ancestors are also visible). * @found_items: the list of items found so far. - * + * * This function is only intended to be used when implementing new canvas * items, specifically container items such as #GooCanvasGroup. * * It gets the items at the given point. - * + * * Returns: the @found_items list, with any more found items added onto * the start of the list, leaving the top item first. **/ @@ -1465,7 +1580,7 @@ goo_canvas_item_get_items_at (GooCanvasItem *item, /** * goo_canvas_item_is_visible: * @item: a #GooCanvasItem. - * + * * Checks if the item is visible. * * This entails checking the item's own visibility setting, as well as those @@ -1473,7 +1588,7 @@ goo_canvas_item_get_items_at (GooCanvasItem *item, * * Note that the item may be scrolled off the screen and so may not * be actually visible to the user. - * + * * Returns: %TRUE if the item is visible. **/ gboolean @@ -1498,9 +1613,9 @@ goo_canvas_item_is_visible (GooCanvasItem *item) /** * goo_canvas_item_get_model: * @item: a #GooCanvasItem. - * + * * Gets the model of the given canvas item. - * + * * Returns: the item's model, or %NULL if it has no model. **/ GooCanvasItemModel* @@ -1516,7 +1631,7 @@ goo_canvas_item_get_model (GooCanvasItem *item) * goo_canvas_item_set_model: * @item: a #GooCanvasItem. * @model: a #GooCanvasItemModel. - * + * * Sets the model of the given canvas item. **/ void @@ -1533,7 +1648,7 @@ goo_canvas_item_set_model (GooCanvasItem *item, /** * goo_canvas_item_ensure_updated: * @item: a #GooCanvasItem. - * + * * This function is only intended to be used when implementing new canvas * items. * @@ -1557,7 +1672,7 @@ goo_canvas_item_ensure_updated (GooCanvasItem *item) * @entire_tree: if the entire subtree should be updated. * @cr: a cairo context. * @bounds: a #GooCanvasBounds to return the new bounds in. - * + * * This function is only intended to be used when implementing new canvas * items, specifically container items such as #GooCanvasGroup. * @@ -1579,10 +1694,10 @@ goo_canvas_item_update (GooCanvasItem *item, * goo_canvas_item_paint: * @item: a #GooCanvasItem. * @cr: a cairo context. - * @bounds: the bounds that need to be repainted. + * @bounds: the bounds that need to be repainted, in device space. * @scale: the scale to use to determine whether an item should be painted. * See #GooCanvasItem:visibility-threshold. - * + * * This function is only intended to be used when implementing new canvas * items, specifically container items such as #GooCanvasGroup. * @@ -1609,12 +1724,12 @@ goo_canvas_item_paint (GooCanvasItem *item, * @cr: a cairo context. * @requested_area: a #GooCanvasBounds to return the requested area in, in the * parent's coordinate space. - * + * * This function is only intended to be used when implementing new canvas * items, specifically layout items such as #GooCanvasTable. * * It gets the requested area of a child item. - * + * * Returns: %TRUE if the item should be allocated space. **/ gboolean @@ -1633,14 +1748,14 @@ goo_canvas_item_get_requested_area (GooCanvasItem *item, * @item: a #GooCanvasItem. * @cr: a cairo context. * @width: the width that the item may be allocated. - * + * * This function is only intended to be used when implementing new canvas * items, specifically layout items such as #GooCanvasTable. * * It gets the requested height of a child item, assuming it is allocated the * given width. This is useful for text items whose requested height may change * depending on the allocated width. - * + * * Returns: the requested height of the item, given the allocated width, * or %-1 if the item doesn't support this method or its height doesn't * change when allocated different widths. @@ -1671,7 +1786,7 @@ goo_canvas_item_get_requested_height (GooCanvasItem *item, * the device coordinate space. * @y_offset: the y offset of the allocated area from the requested area in * the device coordinate space. - * + * * This function is only intended to be used when implementing new canvas * items, specifically layout items such as #GooCanvasTable. * @@ -2014,7 +2129,7 @@ _goo_canvas_item_set_child_properties_internal (GObject *object, * @child: a child #GooCanvasItem. * @property_name: the name of the child property to get. * @value: a location to return the value. - * + * * Gets a child property of @child. **/ void @@ -2038,7 +2153,7 @@ goo_canvas_item_get_child_property (GooCanvasItem *item, * @child: a child #GooCanvasItem. * @property_name: the name of the child property to set. * @value: the value to set the property to. - * + * * Sets a child property of @child. **/ void @@ -2062,7 +2177,7 @@ goo_canvas_item_set_child_property (GooCanvasItem *item, * @child: a child #GooCanvasItem. * @var_args: pairs of property names and value pointers, and a terminating * %NULL. - * + * * Gets the values of one or more child properties of @child. **/ void @@ -2082,7 +2197,7 @@ goo_canvas_item_get_child_properties_valist (GooCanvasItem *item, * @item: a #GooCanvasItem. * @child: a child #GooCanvasItem. * @var_args: pairs of property names and values, and a terminating %NULL. - * + * * Sets the values of one or more child properties of @child. **/ void @@ -2102,7 +2217,7 @@ goo_canvas_item_set_child_properties_valist (GooCanvasItem *item, * @item: a #GooCanvasItem. * @child: a child #GooCanvasItem. * @...: pairs of property names and value pointers, and a terminating %NULL. - * + * * Gets the values of one or more child properties of @child. **/ void @@ -2111,7 +2226,7 @@ goo_canvas_item_get_child_properties (GooCanvasItem *item, ...) { va_list var_args; - + va_start (var_args, child); goo_canvas_item_get_child_properties_valist (item, child, var_args); va_end (var_args); @@ -2123,7 +2238,7 @@ goo_canvas_item_get_child_properties (GooCanvasItem *item, * @item: a #GooCanvasItem. * @child: a child #GooCanvasItem. * @...: pairs of property names and values, and a terminating %NULL. - * + * * Sets the values of one or more child properties of @child. **/ void @@ -2132,7 +2247,7 @@ goo_canvas_item_set_child_properties (GooCanvasItem *item, ...) { va_list var_args; - + va_start (var_args, child); goo_canvas_item_set_child_properties_valist (item, child, var_args); va_end (var_args); @@ -2145,11 +2260,11 @@ goo_canvas_item_set_child_properties (GooCanvasItem *item, * @iclass: a #GObjectClass * @property_id: the id for the property * @pspec: the #GParamSpec for the property - * + * * This function is only intended to be used when implementing new canvas * items, specifically layout container items such as #GooCanvasTable. * - * It installs a child property on a canvas item class. + * It installs a child property on a canvas item class. **/ void goo_canvas_item_class_install_child_property (GObjectClass *iclass, @@ -2203,7 +2318,7 @@ goo_canvas_item_class_find_child_property (GObjectClass *iclass, * goo_canvas_item_class_list_child_properties: * @iclass: a #GObjectClass * @n_properties: location to return the number of child properties found - * @returns: a newly allocated array of #GParamSpec*. The array must be + * @returns: a newly allocated array of #GParamSpec*. The array must be * freed with g_free(). * * This function is only intended to be used when implementing new canvas diff --git a/src/goocanvas/src/goocanvasitem.h b/src/goocanvas/src/goocanvasitem.h index 5ac307c..76c61ec 100644 --- a/src/goocanvas/src/goocanvasitem.h +++ b/src/goocanvas/src/goocanvasitem.h @@ -55,7 +55,6 @@ GType goo_canvas_bounds_get_type (void) G_GNUC_CONST; #define GOO_TYPE_CANVAS_ITEM (goo_canvas_item_get_type ()) #define GOO_CANVAS_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GOO_TYPE_CANVAS_ITEM, GooCanvasItem)) -#define GOO_CANVAS_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GOO_TYPE_CANVAS_ITEM, GooCanvasItemClass)) #define GOO_IS_CANVAS_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GOO_TYPE_CANVAS_ITEM)) #define GOO_CANVAS_ITEM_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GOO_TYPE_CANVAS_ITEM, GooCanvasItemIface)) @@ -110,6 +109,8 @@ typedef struct _GooCanvasItem GooCanvasItem; * @get_style: gets the item's style. * @set_style: sets the item's style. * @is_visible: returns %TRUE if the item is currently visible. + * @get_is_static: returns %TRUE if the item is static. + * @set_is_static: notifies the item whether it is static or not. * @get_requested_height: returns the requested height of the item, * given a particular allocated width, using the parent's coordinate space. * @get_model: gets the model that the canvas item is viewing. @@ -126,7 +127,10 @@ typedef struct _GooCanvasItem GooCanvasItem; * @key_release_event: signal emitted when a key is released. * @grab_broken_event: signal emitted when a grab that the item has is lost. * @child_notify: signal emitted when a child property is changed. + * @query_tooltip: signal emitted to query the tooltip of an item. * @animation_finished: signal emitted when the item's animation has finished. + * @scroll_event: signal emitted when the mouse wheel is activated within + * the item. * * #GooCanvasItemIFace holds the virtual methods that make up the * #GooCanvasItem interface. @@ -271,28 +275,30 @@ struct _GooCanvasItemIface GdkEventGrabBroken *event); void (* child_notify) (GooCanvasItem *item, GParamSpec *pspec); - - /*< private >*/ - - /* We might use this in future to support tooltips. */ gboolean (* query_tooltip) (GooCanvasItem *item, gdouble x, gdouble y, gboolean keyboard_tooltip, - gpointer /*GtkTooltip*/ *tooltip); + GtkTooltip *tooltip); + + gboolean (* get_is_static) (GooCanvasItem *item); + void (* set_is_static) (GooCanvasItem *item, + gboolean is_static); void (* animation_finished) (GooCanvasItem *item, gboolean stopped); + gboolean (* scroll_event) (GooCanvasItem *item, + GooCanvasItem *target, + GdkEventScroll *event); + + /*< private >*/ + /* Padding for future expansion */ void (*_goo_canvas_reserved1) (void); void (*_goo_canvas_reserved2) (void); void (*_goo_canvas_reserved3) (void); void (*_goo_canvas_reserved4) (void); - void (*_goo_canvas_reserved5) (void); - void (*_goo_canvas_reserved6) (void); - void (*_goo_canvas_reserved7) (void); - void (*_goo_canvas_reserved8) (void); }; @@ -449,6 +455,9 @@ void goo_canvas_item_allocate_area (GooCanvasItem *item, gdouble x_offset, gdouble y_offset); +gboolean goo_canvas_item_get_is_static (GooCanvasItem *item); +void goo_canvas_item_set_is_static (GooCanvasItem *item, + gboolean is_static); /* diff --git a/src/goocanvas/src/goocanvasitemmodel.c b/src/goocanvas/src/goocanvasitemmodel.c index a2e559a..6f2de83 100644 --- a/src/goocanvas/src/goocanvasitemmodel.c +++ b/src/goocanvas/src/goocanvasitemmodel.c @@ -98,7 +98,7 @@ goo_canvas_item_model_base_init (gpointer g_iface) { static GObjectNotifyContext cpn_context = { 0, NULL, NULL }; static gboolean initialized = FALSE; - + if (!initialized) { GType iface_type = G_TYPE_FROM_INTERFACE (g_iface); @@ -184,7 +184,7 @@ goo_canvas_item_model_base_init (gpointer g_iface) * @pspec: the #GParamSpec of the changed child property. * * Emitted for each child property that has changed. - * The signal's detail holds the property name. + * The signal's detail holds the property name. */ item_model_signals[CHILD_NOTIFY] = g_signal_new ("child_notify", @@ -213,6 +213,7 @@ goo_canvas_item_model_base_init (gpointer g_iface) G_TYPE_NONE, 1, G_TYPE_BOOLEAN); + g_object_interface_install_property (g_iface, g_param_spec_object ("parent", _("Parent"), @@ -273,6 +274,13 @@ goo_canvas_item_model_base_init (gpointer g_iface) FALSE, G_PARAM_READWRITE)); + g_object_interface_install_property (g_iface, + g_param_spec_string ("tooltip", + _("Tooltip"), + _("The tooltip to display for the item"), + NULL, + G_PARAM_READWRITE)); + _goo_canvas_style_init (); initialized = TRUE; @@ -286,7 +294,7 @@ goo_canvas_item_model_base_init (gpointer g_iface) * @child: the child to add. * @position: the position of the child, or -1 to place it last (at the top of * the stacking order). - * + * * Adds a child at the given stack position. **/ void @@ -308,7 +316,7 @@ goo_canvas_item_model_add_child (GooCanvasItemModel *model, * @model: an item model. * @old_position: the current position of the child. * @new_position: the new position of the child. - * + * * Moves a child to a new stack position. **/ void @@ -328,7 +336,7 @@ goo_canvas_item_model_move_child (GooCanvasItemModel *model, * goo_canvas_item_model_remove_child: * @model: an item model. * @child_num: the position of the child to remove. - * + * * Removes the child at the given position. **/ void @@ -347,9 +355,9 @@ goo_canvas_item_model_remove_child (GooCanvasItemModel *model, * goo_canvas_item_model_find_child: * @model: an item model. * @child: the child to find. - * + * * Attempts to find the given child with the container's stack. - * + * * Returns: the position of the given @child, or -1 if it isn't found. **/ gint @@ -374,9 +382,9 @@ goo_canvas_item_model_find_child (GooCanvasItemModel *model, /** * goo_canvas_item_model_is_container: * @model: an item model. - * + * * Tests to see if the given item model is a container. - * + * * Returns: %TRUE if the item model is a container. **/ gboolean @@ -391,9 +399,9 @@ goo_canvas_item_model_is_container (GooCanvasItemModel *model) /** * goo_canvas_item_model_get_n_children: * @model: an item model. - * + * * Gets the number of children of the container. - * + * * Returns: the number of children. **/ gint @@ -409,10 +417,11 @@ goo_canvas_item_model_get_n_children (GooCanvasItemModel *model) * goo_canvas_item_model_get_child: * @model: an item model. * @child_num: the position of a child in the container's stack. - * + * * Gets the child at the given stack position. - * - * Returns: the child at the given stack position. + * + * Returns: the child at the given stack position, or %NULL if @child_num + * is out of range. **/ GooCanvasItemModel* goo_canvas_item_model_get_child (GooCanvasItemModel *model, @@ -427,9 +436,9 @@ goo_canvas_item_model_get_child (GooCanvasItemModel *model, /** * goo_canvas_item_model_get_parent: * @model: an item model. - * + * * Gets the parent of the given model. - * + * * Returns: the parent model, or %NULL if the model has no parent. **/ GooCanvasItemModel* @@ -443,7 +452,7 @@ goo_canvas_item_model_get_parent (GooCanvasItemModel *model) * goo_canvas_item_model_set_parent: * @model: an item model. * @parent: the new parent item model. - * + * * This function is only intended to be used when implementing new canvas * item models (specifically container models such as #GooCanvasGroupModel). * It sets the parent of the child model. @@ -465,7 +474,7 @@ goo_canvas_item_model_set_parent (GooCanvasItemModel *model, /** * goo_canvas_item_model_remove: * @model: an item model. - * + * * Removes a model from its parent. If the model is in a canvas it will be * removed. * @@ -494,7 +503,7 @@ goo_canvas_item_model_remove (GooCanvasItemModel *model) * @model: an item model. * @above: the item model to raise @model above, or %NULL to raise @model to the top * of the stack. - * + * * Raises a model in the stacking order. **/ void @@ -537,7 +546,7 @@ goo_canvas_item_model_raise (GooCanvasItemModel *model, * @model: an item model. * @below: the item model to lower @model below, or %NULL to lower @model to the * bottom of the stack. - * + * * Lowers a model in the stacking order. **/ void @@ -579,9 +588,9 @@ goo_canvas_item_model_lower (GooCanvasItemModel *model, * goo_canvas_item_model_get_transform: * @model: an item model. * @transform: the place to store the transform. - * + * * Gets the transformation matrix of an item model. - * + * * Returns: %TRUE if a transform is set. **/ gboolean @@ -599,7 +608,7 @@ goo_canvas_item_model_get_transform (GooCanvasItemModel *model, * @model: an item model. * @transform: the new transformation matrix, or %NULL to reset the * transformation to the identity matrix. - * + * * Sets the transformation matrix of an item model. **/ void @@ -617,13 +626,13 @@ goo_canvas_item_model_set_transform (GooCanvasItemModel *model, * @y: returns the y coordinate of the origin of the model's coordinate space. * @scale: returns the scale of the model. * @rotation: returns the clockwise rotation of the model, in degrees (0-360). - * + * * This function can be used to get the position, scale and rotation of an * item model, providing that the model has a simple transformation matrix * (e.g. set with goo_canvas_item_model_set_simple_transform(), or using a * combination of simple translate, scale and rotate operations). If the model * has a complex transformation matrix the results will be incorrect. - * + * * Returns: %TRUE if a transform is set. **/ gboolean @@ -672,7 +681,7 @@ goo_canvas_item_model_get_simple_transform (GooCanvasItemModel *model, * @y: the y coordinate of the origin of the model's coordinate space. * @scale: the scale of the model. * @rotation: the clockwise rotation of the model, in degrees. - * + * * A convenience function to set the item model's transformation matrix. **/ void @@ -697,7 +706,7 @@ goo_canvas_item_model_set_simple_transform (GooCanvasItemModel *model, * @model: an item model. * @tx: the amount to move the origin in the horizontal direction. * @ty: the amount to move the origin in the vertical direction. - * + * * Translates the origin of the model's coordinate system by the given amounts. **/ void @@ -719,7 +728,7 @@ goo_canvas_item_model_translate (GooCanvasItemModel *model, * @model: an item model. * @sx: the amount to scale the horizontal axis. * @sy: the amount to scale the vertical axis. - * + * * Scales the model's coordinate system by the given amounts. **/ void @@ -742,7 +751,7 @@ goo_canvas_item_model_scale (GooCanvasItemModel *model, * @degrees: the clockwise angle of rotation. * @cx: the x coordinate of the origin of the rotation. * @cy: the y coordinate of the origin of the rotation. - * + * * Rotates the model's coordinate system by the given amount, about the given * origin. **/ @@ -770,7 +779,7 @@ goo_canvas_item_model_rotate (GooCanvasItemModel *model, * @degrees: the skew angle. * @cx: the x coordinate of the origin of the skew transform. * @cy: the y coordinate of the origin of the skew transform. - * + * * Skews the model's coordinate system along the x axis by the given amount, * about the given origin. **/ @@ -799,7 +808,7 @@ goo_canvas_item_model_skew_x (GooCanvasItemModel *model, * @degrees: the skew angle. * @cx: the x coordinate of the origin of the skew transform. * @cy: the y coordinate of the origin of the skew transform. - * + * * Skews the model's coordinate system along the y axis by the given amount, * about the given origin. **/ @@ -825,10 +834,10 @@ goo_canvas_item_model_skew_y (GooCanvasItemModel *model, /** * goo_canvas_item_model_get_style: * @model: an item model. - * + * * Gets the model's style. If the model doesn't have its own style it will * return its parent's style. - * + * * Returns: the model's style. **/ GooCanvasStyle* @@ -844,7 +853,7 @@ goo_canvas_item_model_get_style (GooCanvasItemModel *model) * goo_canvas_item_model_set_style: * @model: an item model. * @style: a style. - * + * * Sets the model's style, by copying the properties from the given style. **/ void @@ -885,7 +894,7 @@ extern void _goo_canvas_item_animate_internal (GooCanvasItem *item, * second). * @step_time: the time between each animation step, in milliseconds. * @type: specifies what happens when the animation finishes. - * + * * Animates a model from its current position to the given offsets, scale * and rotation. **/ @@ -908,7 +917,7 @@ goo_canvas_item_model_animate (GooCanvasItemModel *model, /** * goo_canvas_item_model_stop_animation: * @model: an item model. - * + * * Stops any current animation for the given model, leaving it at its current * position. **/ @@ -939,7 +948,7 @@ extern void _goo_canvas_item_set_child_properties_internal (GObject *object, GOb * @child: a child #GooCanvasItemModel. * @property_name: the name of the child property to get. * @value: a location to return the value. - * + * * Gets a child property of @child. **/ void @@ -963,7 +972,7 @@ goo_canvas_item_model_get_child_property (GooCanvasItemModel *model, * @child: a child #GooCanvasItemModel. * @property_name: the name of the child property to set. * @value: the value to set the property to. - * + * * Sets a child property of @child. **/ void @@ -987,7 +996,7 @@ goo_canvas_item_model_set_child_property (GooCanvasItemModel *model, * @child: a child #GooCanvasItemModel. * @var_args: pairs of property names and value pointers, and a terminating * %NULL. - * + * * Gets the values of one or more child properties of @child. **/ void @@ -1007,7 +1016,7 @@ goo_canvas_item_model_get_child_properties_valist (GooCanvasItemModel *model, * @model: a #GooCanvasItemModel. * @child: a child #GooCanvasItemModel. * @var_args: pairs of property names and values, and a terminating %NULL. - * + * * Sets the values of one or more child properties of @child. **/ void @@ -1027,7 +1036,7 @@ goo_canvas_item_model_set_child_properties_valist (GooCanvasItemModel *model, * @model: a #GooCanvasItemModel. * @child: a child #GooCanvasItemModel. * @...: pairs of property names and value pointers, and a terminating %NULL. - * + * * Gets the values of one or more child properties of @child. **/ void @@ -1036,7 +1045,7 @@ goo_canvas_item_model_get_child_properties (GooCanvasItemModel *model, ...) { va_list var_args; - + va_start (var_args, child); goo_canvas_item_model_get_child_properties_valist (model, child, var_args); va_end (var_args); @@ -1048,7 +1057,7 @@ goo_canvas_item_model_get_child_properties (GooCanvasItemModel *model, * @model: a #GooCanvasItemModel. * @child: a child #GooCanvasItemModel. * @...: pairs of property names and values, and a terminating %NULL. - * + * * Sets the values of one or more child properties of @child. **/ void @@ -1057,7 +1066,7 @@ goo_canvas_item_model_set_child_properties (GooCanvasItemModel *model, ...) { va_list var_args; - + va_start (var_args, child); goo_canvas_item_model_set_child_properties_valist (model, child, var_args); va_end (var_args); @@ -1070,12 +1079,12 @@ goo_canvas_item_model_set_child_properties (GooCanvasItemModel *model, * @mclass: a #GObjectClass * @property_id: the id for the property * @pspec: the #GParamSpec for the property - * + * * This function is only intended to be used when implementing new canvas * item models, specifically layout container item models such as * #GooCanvasTableModel. * - * It installs a child property on a canvas item class. + * It installs a child property on a canvas item class. **/ void goo_canvas_item_model_class_install_child_property (GObjectClass *mclass, @@ -1130,7 +1139,7 @@ goo_canvas_item_model_class_find_child_property (GObjectClass *mclass, * goo_canvas_item_model_class_list_child_properties: * @mclass: a #GObjectClass * @n_properties: location to return the number of child properties found - * @returns: a newly allocated array of #GParamSpec*. The array must be + * @returns: a newly allocated array of #GParamSpec*. The array must be * freed with g_free(). * * This function is only intended to be used when implementing new canvas diff --git a/src/goocanvas/src/goocanvasitemmodel.h b/src/goocanvas/src/goocanvasitemmodel.h index ebdf179..c32f7ef 100644 --- a/src/goocanvas/src/goocanvasitemmodel.h +++ b/src/goocanvas/src/goocanvasitemmodel.h @@ -15,7 +15,6 @@ G_BEGIN_DECLS #define GOO_TYPE_CANVAS_ITEM_MODEL (goo_canvas_item_model_get_type ()) #define GOO_CANVAS_ITEM_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GOO_TYPE_CANVAS_ITEM_MODEL, GooCanvasItemModel)) -#define GOO_CANVAS_ITEM_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GOO_TYPE_CANVAS_ITEM_MODEL, GooCanvasItemModelClass)) #define GOO_IS_CANVAS_ITEM_MODEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GOO_TYPE_CANVAS_ITEM_MODEL)) #define GOO_CANVAS_ITEM_MODEL_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GOO_TYPE_CANVAS_ITEM_MODEL, GooCanvasItemModelIface)) @@ -149,7 +148,6 @@ struct _GooCanvasItemModelIface void (*_goo_canvas_reserved5) (void); void (*_goo_canvas_reserved6) (void); void (*_goo_canvas_reserved7) (void); - void (*_goo_canvas_reserved8) (void); }; diff --git a/src/goocanvas/src/goocanvasitemsimple.c b/src/goocanvas/src/goocanvasitemsimple.c index ff5fbdb..46544ed 100644 --- a/src/goocanvas/src/goocanvasitemsimple.c +++ b/src/goocanvas/src/goocanvasitemsimple.c @@ -79,7 +79,8 @@ enum { PROP_DESCRIPTION, PROP_CAN_FOCUS, PROP_CLIP_PATH, - PROP_CLIP_FILL_RULE + PROP_CLIP_FILL_RULE, + PROP_TOOLTIP }; static gboolean accessibility_enabled = FALSE; @@ -287,6 +288,9 @@ goo_canvas_item_simple_install_common_properties (GObjectClass *gobject_class) g_object_class_override_property (gobject_class, PROP_CAN_FOCUS, "can-focus"); + g_object_class_override_property (gobject_class, PROP_TOOLTIP, + "tooltip"); + /** * GooCanvasItemSimple:clip-path * @@ -432,27 +436,6 @@ goo_canvas_item_simple_finalize (GObject *object) } -static guint -convert_color (double red, double green, double blue, double alpha) -{ - guint red_byte, green_byte, blue_byte, alpha_byte; - - red_byte = red * 256; - red_byte -= red_byte >> 8; - - green_byte = green * 256; - green_byte -= green_byte >> 8; - - blue_byte = blue * 256; - blue_byte -= blue_byte >> 8; - - alpha_byte = alpha * 256; - alpha_byte -= alpha_byte >> 8; - - return (red_byte << 24) + (green_byte << 16) + (blue_byte << 8) + alpha_byte; -} - - static void goo_canvas_item_simple_get_common_property (GObject *object, GooCanvasItemSimpleData *simple_data, @@ -465,9 +448,6 @@ goo_canvas_item_simple_get_common_property (GObject *object, GValue *svalue; gdouble line_width = 2.0; gchar *font = NULL; - cairo_pattern_t *pattern; - double red, green, blue, alpha; - guint rgba = 0; switch (prop_id) { @@ -539,29 +519,15 @@ goo_canvas_item_simple_get_common_property (GObject *object, /* Convenience properties. */ case PROP_STROKE_COLOR_RGBA: svalue = goo_canvas_style_get_property (style, goo_canvas_style_stroke_pattern_id); - if (svalue && svalue->data[0].v_pointer) - { - pattern = svalue->data[0].v_pointer; - if (cairo_pattern_get_type (pattern) == CAIRO_PATTERN_TYPE_SOLID) - { - cairo_pattern_get_rgba (pattern, &red, &green, &blue, &alpha); - rgba = convert_color (red, green, blue, alpha); - } - } - g_value_set_uint (value, rgba); + if (svalue) + goo_canvas_get_rgba_value_from_pattern (svalue->data[0].v_pointer, + value); break; case PROP_FILL_COLOR_RGBA: svalue = goo_canvas_style_get_property (style, goo_canvas_style_fill_pattern_id); - if (svalue && svalue->data[0].v_pointer) - { - pattern = svalue->data[0].v_pointer; - if (cairo_pattern_get_type (pattern) == CAIRO_PATTERN_TYPE_SOLID) - { - cairo_pattern_get_rgba (pattern, &red, &green, &blue, &alpha); - rgba = convert_color (red, green, blue, alpha); - } - } - g_value_set_uint (value, rgba); + if (svalue) + goo_canvas_get_rgba_value_from_pattern (svalue->data[0].v_pointer, + value); break; /* Other properties. */ @@ -583,6 +549,9 @@ goo_canvas_item_simple_get_common_property (GObject *object, case PROP_CLIP_FILL_RULE: g_value_set_enum (value, simple_data->clip_fill_rule); break; + case PROP_TOOLTIP: + g_value_set_string (value, simple_data->tooltip); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -630,10 +599,6 @@ goo_canvas_item_simple_set_common_property (GObject *object, GParamSpec *pspec) { GooCanvasStyle *style; - GdkColor color = { 0, 0, 0, 0, }; - guint rgba, red, green, blue, alpha; - GdkPixbuf *pixbuf; - cairo_surface_t *surface; cairo_pattern_t *pattern; gboolean recompute_bounds = FALSE; cairo_matrix_t *transform; @@ -722,74 +687,29 @@ goo_canvas_item_simple_set_common_property (GObject *object, /* Convenience properties. */ case PROP_STROKE_COLOR: - if (g_value_get_string (value)) - gdk_color_parse (g_value_get_string (value), &color); - pattern = cairo_pattern_create_rgb (color.red / 65535.0, - color.green / 65535.0, - color.blue / 65535.0); - g_value_init (&tmpval, GOO_TYPE_CAIRO_PATTERN); - g_value_take_boxed (&tmpval, pattern); - goo_canvas_style_set_property (style, goo_canvas_style_stroke_pattern_id, &tmpval); - g_value_unset (&tmpval); + pattern = goo_canvas_create_pattern_from_color_value (value); + goo_canvas_set_style_property_from_pattern (style, goo_canvas_style_stroke_pattern_id, pattern); break; case PROP_STROKE_COLOR_RGBA: - rgba = g_value_get_uint (value); - red = (rgba >> 24) & 0xFF; - green = (rgba >> 16) & 0xFF; - blue = (rgba >> 8) & 0xFF; - alpha = (rgba) & 0xFF; - pattern = cairo_pattern_create_rgba (red / 255.0, green / 255.0, - blue / 255.0, alpha / 255.0); - g_value_init (&tmpval, GOO_TYPE_CAIRO_PATTERN); - g_value_take_boxed (&tmpval, pattern); - goo_canvas_style_set_property (style, goo_canvas_style_stroke_pattern_id, &tmpval); - g_value_unset (&tmpval); + pattern = goo_canvas_create_pattern_from_rgba_value (value); + goo_canvas_set_style_property_from_pattern (style, goo_canvas_style_stroke_pattern_id, pattern); break; case PROP_STROKE_PIXBUF: - pixbuf = g_value_get_object (value); - surface = goo_canvas_cairo_surface_from_pixbuf (pixbuf); - pattern = cairo_pattern_create_for_surface (surface); - cairo_surface_destroy (surface); - cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT); - g_value_init (&tmpval, GOO_TYPE_CAIRO_PATTERN); - g_value_take_boxed (&tmpval, pattern); - goo_canvas_style_set_property (style, goo_canvas_style_stroke_pattern_id, &tmpval); - g_value_unset (&tmpval); + pattern = goo_canvas_create_pattern_from_pixbuf_value (value); + goo_canvas_set_style_property_from_pattern (style, goo_canvas_style_stroke_pattern_id, pattern); break; + case PROP_FILL_COLOR: - if (g_value_get_string (value)) - gdk_color_parse (g_value_get_string (value), &color); - pattern = cairo_pattern_create_rgb (color.red / 65535.0, - color.green / 65535.0, - color.blue / 65535.0); - g_value_init (&tmpval, GOO_TYPE_CAIRO_PATTERN); - g_value_take_boxed (&tmpval, pattern); - goo_canvas_style_set_property (style, goo_canvas_style_fill_pattern_id, &tmpval); - g_value_unset (&tmpval); + pattern = goo_canvas_create_pattern_from_color_value (value); + goo_canvas_set_style_property_from_pattern (style, goo_canvas_style_fill_pattern_id, pattern); break; case PROP_FILL_COLOR_RGBA: - rgba = g_value_get_uint (value); - red = (rgba >> 24) & 0xFF; - green = (rgba >> 16) & 0xFF; - blue = (rgba >> 8) & 0xFF; - alpha = (rgba) & 0xFF; - pattern = cairo_pattern_create_rgba (red / 255.0, green / 255.0, - blue / 255.0, alpha / 255.0); - g_value_init (&tmpval, GOO_TYPE_CAIRO_PATTERN); - g_value_take_boxed (&tmpval, pattern); - goo_canvas_style_set_property (style, goo_canvas_style_fill_pattern_id, &tmpval); - g_value_unset (&tmpval); + pattern = goo_canvas_create_pattern_from_rgba_value (value); + goo_canvas_set_style_property_from_pattern (style, goo_canvas_style_fill_pattern_id, pattern); break; case PROP_FILL_PIXBUF: - pixbuf = g_value_get_object (value); - surface = goo_canvas_cairo_surface_from_pixbuf (pixbuf); - pattern = cairo_pattern_create_for_surface (surface); - cairo_surface_destroy (surface); - cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT); - g_value_init (&tmpval, GOO_TYPE_CAIRO_PATTERN); - g_value_take_boxed (&tmpval, pattern); - goo_canvas_style_set_property (style, goo_canvas_style_fill_pattern_id, &tmpval); - g_value_unset (&tmpval); + pattern = goo_canvas_create_pattern_from_pixbuf_value (value); + goo_canvas_set_style_property_from_pattern (style, goo_canvas_style_fill_pattern_id, pattern); break; /* Other properties. */ @@ -821,6 +741,9 @@ goo_canvas_item_simple_set_common_property (GObject *object, simple_data->clip_fill_rule = g_value_get_enum (value); recompute_bounds = TRUE; break; + case PROP_TOOLTIP: + simple_data->tooltip = g_value_dup_string (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -930,6 +853,9 @@ void goo_canvas_item_simple_changed (GooCanvasItemSimple *item, gboolean recompute_bounds) { + GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item; + GooCanvasItemSimpleData *simple_data = simple->simple_data; + if (recompute_bounds) { item->need_entire_subtree_update = TRUE; @@ -945,7 +871,7 @@ goo_canvas_item_simple_changed (GooCanvasItemSimple *item, else { if (item->canvas) - goo_canvas_request_redraw (item->canvas, &item->bounds); + goo_canvas_request_item_redraw (item->canvas, &item->bounds, simple_data->is_static); } } @@ -1151,6 +1077,27 @@ goo_canvas_item_simple_is_visible (GooCanvasItem *item) } +static gboolean +goo_canvas_item_simple_get_is_static (GooCanvasItem *item) +{ + GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item; + GooCanvasItemSimpleData *simple_data = simple->simple_data; + + return simple_data->is_static; +} + + +static void +goo_canvas_item_simple_set_is_static (GooCanvasItem *item, + gboolean is_static) +{ + GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item; + GooCanvasItemSimpleData *simple_data = simple->simple_data; + + simple_data->is_static = is_static; +} + + /** * goo_canvas_item_simple_check_style: * @item: a #GooCanvasItemSimple. @@ -1258,7 +1205,7 @@ goo_canvas_item_simple_update (GooCanvasItem *item, if (entire_tree || simple->need_update) { /* Request a redraw of the existing bounds. */ - goo_canvas_request_redraw (simple->canvas, &simple->bounds); + goo_canvas_request_item_redraw (simple->canvas, &simple->bounds, simple_data->is_static); cairo_save (cr); if (simple_data->transform) @@ -1285,7 +1232,7 @@ goo_canvas_item_simple_update (GooCanvasItem *item, cairo_restore (cr); /* Request a redraw of the new bounds. */ - goo_canvas_request_redraw (simple->canvas, &simple->bounds); + goo_canvas_request_item_redraw (simple->canvas, &simple->bounds, simple_data->is_static); } *bounds = simple->bounds; @@ -1302,6 +1249,9 @@ goo_canvas_item_simple_get_requested_area (GooCanvasItem *item, cairo_matrix_t matrix; double x_offset, y_offset; + /* Request a redraw of the existing bounds. */ + goo_canvas_request_item_redraw (simple->canvas, &simple->bounds, simple_data->is_static); + cairo_save (cr); if (simple_data->transform) cairo_transform (cr, simple_data->transform); @@ -1370,6 +1320,7 @@ goo_canvas_item_simple_allocate_area (GooCanvasItem *item, gdouble y_offset) { GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item; + GooCanvasItemSimpleData *simple_data = simple->simple_data; /* Simple items can't resize at all, so we just adjust the bounds x & y positions here, and let the item be clipped if necessary. */ @@ -1377,6 +1328,9 @@ goo_canvas_item_simple_allocate_area (GooCanvasItem *item, simple->bounds.y1 += y_offset; simple->bounds.x2 += x_offset; simple->bounds.y2 += y_offset; + + /* Request a redraw of the new bounds. */ + goo_canvas_request_item_redraw (simple->canvas, &simple->bounds, simple_data->is_static); } @@ -1542,6 +1496,26 @@ goo_canvas_item_simple_set_model_internal (GooCanvasItem *item, } +static gboolean +goo_canvas_item_simple_query_tooltip (GooCanvasItem *item, + gdouble x, + gdouble y, + gboolean keyboard_tip, + GtkTooltip *tooltip) +{ + GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item; + GooCanvasItemSimpleData *simple_data = simple->simple_data; + + if (simple_data->tooltip) + { + gtk_tooltip_set_markup (tooltip, simple_data->tooltip); + return TRUE; + } + + return FALSE; +} + + static void canvas_item_interface_init (GooCanvasItemIface *iface) { @@ -1562,9 +1536,13 @@ canvas_item_interface_init (GooCanvasItemIface *iface) iface->get_style = goo_canvas_item_simple_get_style; iface->set_style = goo_canvas_item_simple_set_style; iface->is_visible = goo_canvas_item_simple_is_visible; + iface->get_is_static = goo_canvas_item_simple_get_is_static; + iface->set_is_static = goo_canvas_item_simple_set_is_static; iface->get_model = goo_canvas_item_simple_get_model; iface->set_model = goo_canvas_item_simple_set_model_internal; + + iface->query_tooltip = goo_canvas_item_simple_query_tooltip; } @@ -1692,7 +1670,8 @@ goo_canvas_item_simple_get_path_bounds (GooCanvasItemSimple *item, * This function is intended to be used by subclasses of #GooCanvasItemSimple, * typically in their update() or get_requested_area() methods. * - * It converts the item's bounds to a bounding box in device space. + * It converts the item's bounds to a bounding box in the canvas (device) + * coordinate space. **/ void goo_canvas_item_simple_user_bounds_to_device (GooCanvasItemSimple *item, diff --git a/src/goocanvas/src/goocanvasitemsimple.h b/src/goocanvas/src/goocanvasitemsimple.h index 73486c8..39ad00f 100644 --- a/src/goocanvas/src/goocanvasitemsimple.h +++ b/src/goocanvas/src/goocanvasitemsimple.h @@ -22,6 +22,7 @@ G_BEGIN_DECLS * @transform: the transformation matrix of the item, or %NULL. * @clip_path_commands: an array of #GooCanvasPathCommand specifying the clip * path of the item, or %NULL. + * @tooltip: the item's tooltip. * @visibility_threshold: the threshold scale setting at which to show the item * (if the @visibility setting is set to %VISIBLE_ABOVE_THRESHOLD). * @visibility: the #GooCanvasItemVisibility setting specifying whether the @@ -32,6 +33,7 @@ G_BEGIN_DECLS * @own_style: if the item has its own style, rather than using its parent's. * @clip_fill_rule: the #cairo_fill_rule_t setting specifying the fill rule * used for the clip path. + * @is_static: if the item is static. * * This is the data common to both the model and view classes. */ @@ -41,9 +43,6 @@ struct _GooCanvasItemSimpleData GooCanvasStyle *style; cairo_matrix_t *transform; GArray *clip_path_commands; - - /*< private >*/ - /* We will store tooltips here in future. */ gchar *tooltip; /*< public >*/ @@ -53,10 +52,11 @@ struct _GooCanvasItemSimpleData guint can_focus : 1; guint own_style : 1; guint clip_fill_rule : 4; + guint is_static : 1; /*< private >*/ /* We might use this in future for a cache setting - never/always/visible. */ - guint cache_setting : 3; + guint cache_setting : 2; /* We might need this for tooltips in future. */ guint has_tooltip : 1; }; diff --git a/src/goocanvas/src/goocanvasmarshal.c b/src/goocanvas/src/goocanvasmarshal.c index 18beee7..da2931f 100644 --- a/src/goocanvas/src/goocanvasmarshal.c +++ b/src/goocanvas/src/goocanvasmarshal.c @@ -208,3 +208,48 @@ goo_canvas_marshal_BOOLEAN__OBJECT_BOXED (GClosure *closure, g_value_set_boolean (return_value, v_return); } +/* BOOLEAN:DOUBLE,DOUBLE,BOOLEAN,OBJECT (./goocanvasmarshal.list:8) */ +void +goo_canvas_marshal_BOOLEAN__DOUBLE_DOUBLE_BOOLEAN_OBJECT (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef gboolean (*GMarshalFunc_BOOLEAN__DOUBLE_DOUBLE_BOOLEAN_OBJECT) (gpointer data1, + gdouble arg_1, + gdouble arg_2, + gboolean arg_3, + gpointer arg_4, + gpointer data2); + register GMarshalFunc_BOOLEAN__DOUBLE_DOUBLE_BOOLEAN_OBJECT callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + gboolean v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 5); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_BOOLEAN__DOUBLE_DOUBLE_BOOLEAN_OBJECT) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + g_marshal_value_peek_double (param_values + 1), + g_marshal_value_peek_double (param_values + 2), + g_marshal_value_peek_boolean (param_values + 3), + g_marshal_value_peek_object (param_values + 4), + data2); + + g_value_set_boolean (return_value, v_return); +} + diff --git a/src/goocanvas/src/goocanvasmarshal.h b/src/goocanvas/src/goocanvasmarshal.h index 5a3a65d..fe94ec6 100644 --- a/src/goocanvas/src/goocanvasmarshal.h +++ b/src/goocanvas/src/goocanvasmarshal.h @@ -47,6 +47,14 @@ extern void goo_canvas_marshal_BOOLEAN__OBJECT_BOXED (GClosure *closure, gpointer invocation_hint, gpointer marshal_data); +/* BOOLEAN:DOUBLE,DOUBLE,BOOLEAN,OBJECT (./goocanvasmarshal.list:8) */ +extern void goo_canvas_marshal_BOOLEAN__DOUBLE_DOUBLE_BOOLEAN_OBJECT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + G_END_DECLS #endif /* __goo_canvas_marshal_MARSHAL_H__ */ diff --git a/src/goocanvas/src/goocanvaspath.c b/src/goocanvas/src/goocanvaspath.c index d6af970..0254a66 100644 --- a/src/goocanvas/src/goocanvaspath.c +++ b/src/goocanvas/src/goocanvaspath.c @@ -33,26 +33,21 @@ #include <glib/gi18n-lib.h> #include <gtk/gtk.h> #include "goocanvaspath.h" +#include "goocanvas.h" enum { PROP_0, PROP_DATA, + + PROP_X, + PROP_Y, + PROP_WIDTH, + PROP_HEIGHT }; static void canvas_item_interface_init (GooCanvasItemIface *iface); -static void goo_canvas_path_finalize (GObject *object); -static void goo_canvas_path_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec); -static void goo_canvas_path_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec); -static void goo_canvas_path_create_path (GooCanvasItemSimple *simple, - cairo_t *cr); G_DEFINE_TYPE_WITH_CODE (GooCanvasPath, goo_canvas_path, GOO_TYPE_CANVAS_ITEM_SIMPLE, @@ -76,23 +71,36 @@ goo_canvas_path_install_common_properties (GObjectClass *gobject_class) _("The sequence of path commands"), NULL, G_PARAM_WRITABLE)); -} - - -static void -goo_canvas_path_class_init (GooCanvasPathClass *klass) -{ - GObjectClass *gobject_class = (GObjectClass*) klass; - GooCanvasItemSimpleClass *simple_class = (GooCanvasItemSimpleClass*) klass; - - gobject_class->finalize = goo_canvas_path_finalize; - gobject_class->get_property = goo_canvas_path_get_property; - gobject_class->set_property = goo_canvas_path_set_property; - - simple_class->simple_create_path = goo_canvas_path_create_path; - - goo_canvas_path_install_common_properties (gobject_class); + g_object_class_install_property (gobject_class, PROP_X, + g_param_spec_double ("x", + "X", + _("The x coordinate of the path"), + -G_MAXDOUBLE, + G_MAXDOUBLE, 0.0, + G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_Y, + g_param_spec_double ("y", + "Y", + _("The y coordinate of the path"), + -G_MAXDOUBLE, + G_MAXDOUBLE, 0.0, + G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_WIDTH, + g_param_spec_double ("width", + _("Width"), + _("The width of the path"), + 0.0, G_MAXDOUBLE, 0.0, + G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_HEIGHT, + g_param_spec_double ("height", + _("Height"), + _("The height of the path"), + 0.0, G_MAXDOUBLE, 0.0, + G_PARAM_READWRITE)); } @@ -200,16 +208,164 @@ goo_canvas_path_finalize (GObject *object) G_OBJECT_CLASS (goo_canvas_path_parent_class)->finalize (object); } +static void +goo_canvas_path_common_get_extent (GooCanvas *canvas, + GooCanvasPathData *path_data, + GooCanvasBounds *bounds) +{ + cairo_t *cr; + + cr = goo_canvas_create_cairo_context (canvas); + goo_canvas_create_path (path_data->path_commands, cr); + cairo_fill_extents (cr, &bounds->x1, &bounds->y1, &bounds->x2, &bounds->y2); + cairo_destroy (cr); +} + + +/* Moves all the absolute points in the command by the given amounts. + Relative points don't need to be moved. */ +static void +goo_canvas_path_move_command (GooCanvasPathCommand *cmd, + gdouble x_offset, + gdouble y_offset) +{ + switch (cmd->simple.type) + { + case GOO_CANVAS_PATH_MOVE_TO: + case GOO_CANVAS_PATH_CLOSE_PATH: + case GOO_CANVAS_PATH_LINE_TO: + case GOO_CANVAS_PATH_HORIZONTAL_LINE_TO: + case GOO_CANVAS_PATH_VERTICAL_LINE_TO: + if (!cmd->simple.relative) + { + cmd->simple.x += x_offset; + cmd->simple.y += y_offset; + } + break; + case GOO_CANVAS_PATH_CURVE_TO: + case GOO_CANVAS_PATH_SMOOTH_CURVE_TO: + case GOO_CANVAS_PATH_QUADRATIC_CURVE_TO: + case GOO_CANVAS_PATH_SMOOTH_QUADRATIC_CURVE_TO: + if (!cmd->curve.relative) + { + cmd->curve.x += x_offset; + cmd->curve.y += y_offset; + cmd->curve.x1 += x_offset; + cmd->curve.y1 += y_offset; + cmd->curve.x2 += x_offset; + cmd->curve.y2 += y_offset; + } + break; + case GOO_CANVAS_PATH_ELLIPTICAL_ARC: + if (!cmd->arc.relative) + { + cmd->arc.x += x_offset; + cmd->arc.y += y_offset; + } + break; + default: + g_assert_not_reached(); + break; + } +} + + +/* Scales all the points in the command by the given amounts. Absolute points + are scaled about the given origin. */ +static void +goo_canvas_path_scale_command (GooCanvasPathCommand *cmd, + gdouble x_origin, + gdouble y_origin, + gdouble x_scale, + gdouble y_scale) +{ + switch (cmd->simple.type) + { + case GOO_CANVAS_PATH_MOVE_TO: + case GOO_CANVAS_PATH_CLOSE_PATH: + case GOO_CANVAS_PATH_LINE_TO: + case GOO_CANVAS_PATH_HORIZONTAL_LINE_TO: + case GOO_CANVAS_PATH_VERTICAL_LINE_TO: + if (cmd->simple.relative) + { + cmd->simple.x *= x_scale; + cmd->simple.y *= y_scale; + } + else + { + cmd->simple.x = x_origin + (cmd->simple.x - x_origin) * x_scale; + cmd->simple.y = y_origin + (cmd->simple.y - y_origin) * y_scale; + } + break; + case GOO_CANVAS_PATH_CURVE_TO: + case GOO_CANVAS_PATH_SMOOTH_CURVE_TO: + case GOO_CANVAS_PATH_QUADRATIC_CURVE_TO: + case GOO_CANVAS_PATH_SMOOTH_QUADRATIC_CURVE_TO: + if (cmd->curve.relative) + { + cmd->curve.x *= x_scale; + cmd->curve.y *= y_scale; + cmd->curve.x1 *= x_scale; + cmd->curve.y1 *= y_scale; + cmd->curve.x2 *= x_scale; + cmd->curve.y2 *= y_scale; + } + else + { + cmd->curve.x = x_origin + (cmd->curve.x - x_origin) * x_scale; + cmd->curve.y = y_origin + (cmd->curve.y - y_origin) * y_scale; + cmd->curve.x1 = x_origin + (cmd->curve.x1 - x_origin) * x_scale; + cmd->curve.y1 = y_origin + (cmd->curve.y1 - y_origin) * y_scale; + cmd->curve.x2 = x_origin + (cmd->curve.x2 - x_origin) * x_scale; + cmd->curve.y2 = y_origin + (cmd->curve.y2 - y_origin) * y_scale; + } + break; + case GOO_CANVAS_PATH_ELLIPTICAL_ARC: + if (cmd->arc.relative) + { + cmd->arc.x *= x_scale; + cmd->arc.y *= y_scale; + } + else + { + cmd->arc.x = x_origin + (cmd->arc.x - x_origin) * x_scale; + cmd->arc.y = y_origin + (cmd->arc.y - y_origin) * y_scale; + } + break; + default: + g_assert_not_reached(); + break; + } +} static void goo_canvas_path_get_common_property (GObject *object, + GooCanvas *canvas, GooCanvasPathData *path_data, guint prop_id, GValue *value, GParamSpec *pspec) { + GooCanvasBounds extent; + switch (prop_id) { + case PROP_X: + goo_canvas_path_common_get_extent (canvas, path_data, &extent); + g_value_set_double (value, extent.x1); + break; + case PROP_Y: + goo_canvas_path_common_get_extent (canvas, path_data, &extent); + g_value_set_double (value, extent.y1); + break; + case PROP_WIDTH: + goo_canvas_path_common_get_extent (canvas, path_data, &extent); + g_value_set_double (value, extent.x2 - extent.x1); + break; + case PROP_HEIGHT: + goo_canvas_path_common_get_extent (canvas, path_data, &extent); + g_value_set_double (value, extent.y2 - extent.y1); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -223,26 +379,113 @@ goo_canvas_path_get_property (GObject *object, GValue *value, GParamSpec *pspec) { + GooCanvasItemSimple *simple = (GooCanvasItemSimple*) object; GooCanvasPath *path = (GooCanvasPath*) object; - goo_canvas_path_get_common_property (object, path->path_data, prop_id, - value, pspec); + goo_canvas_path_get_common_property (object, simple->canvas, + path->path_data, prop_id, value, pspec); } static void goo_canvas_path_set_common_property (GObject *object, + GooCanvas *canvas, GooCanvasPathData *path_data, guint prop_id, const GValue *value, GParamSpec *pspec) { + GooCanvasBounds extent; + GooCanvasPathCommand *cmd; + gdouble x_offset, y_offset, x_scale, y_scale; + guint i; + switch (prop_id) { case PROP_DATA: if (path_data->path_commands) g_array_free (path_data->path_commands, TRUE); path_data->path_commands = goo_canvas_parse_path_data (g_value_get_string (value)); + g_object_notify (object, "x"); + g_object_notify (object, "y"); + g_object_notify (object, "width"); + g_object_notify (object, "height"); + break; + case PROP_X: + if (path_data->path_commands->len > 0) + { + /* Calculate the x offset from the current position. */ + goo_canvas_path_common_get_extent (canvas, path_data, &extent); + x_offset = g_value_get_double (value) - extent.x1; + + /* Add the offset to all the absolute x coordinates. */ + for (i = 0; i < path_data->path_commands->len; i++) + { + cmd = &g_array_index (path_data->path_commands, + GooCanvasPathCommand, i); + goo_canvas_path_move_command (cmd, x_offset, 0.0); + } + g_object_notify (object, "data"); + } + break; + case PROP_Y: + if (path_data->path_commands->len > 0) + { + /* Calculate the y offset from the current position. */ + goo_canvas_path_common_get_extent (canvas, path_data, &extent); + y_offset = g_value_get_double (value) - extent.y1; + + /* Add the offset to all the absolute y coordinates. */ + for (i = 0; i < path_data->path_commands->len; i++) + { + cmd = &g_array_index (path_data->path_commands, + GooCanvasPathCommand, i); + goo_canvas_path_move_command (cmd, 0.0, y_offset); + } + g_object_notify (object, "data"); + } + break; + case PROP_WIDTH: + if (path_data->path_commands->len >= 2) + { + goo_canvas_path_common_get_extent (canvas, path_data, &extent); + if (extent.x2 - extent.x1 != 0.0) + { + /* Calculate the amount to scale the path. */ + x_scale = g_value_get_double (value) / (extent.x2 - extent.x1); + + /* Scale the x coordinates, relative to the left-most point. */ + for (i = 0; i < path_data->path_commands->len; i++) + { + cmd = &g_array_index (path_data->path_commands, + GooCanvasPathCommand, i); + goo_canvas_path_scale_command (cmd, extent.x1, 0.0, + x_scale, 1.0); + } + g_object_notify (object, "data"); + } + } + break; + case PROP_HEIGHT: + if (path_data->path_commands->len >= 2) + { + goo_canvas_path_common_get_extent (canvas, path_data, &extent); + if (extent.y2 - extent.y1 != 0.0) + { + /* Calculate the amount to scale the polyline. */ + y_scale = g_value_get_double (value) / (extent.y2 - extent.y1); + + /* Scale the y coordinates, relative to the top-most point. */ + for (i = 0; i < path_data->path_commands->len; i++) + { + cmd = &g_array_index (path_data->path_commands, + GooCanvasPathCommand, i); + goo_canvas_path_scale_command (cmd, 0.0, extent.y1, + 1.0, y_scale); + } + g_object_notify (object, "data"); + } + } break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -266,8 +509,8 @@ goo_canvas_path_set_property (GObject *object, return; } - goo_canvas_path_set_common_property (object, path->path_data, prop_id, - value, pspec); + goo_canvas_path_set_common_property (object, simple->canvas, path->path_data, + prop_id, value, pspec); goo_canvas_item_simple_changed (simple, TRUE); } @@ -282,6 +525,34 @@ goo_canvas_path_create_path (GooCanvasItemSimple *simple, } +static gboolean +goo_canvas_path_is_item_at (GooCanvasItemSimple *simple, + gdouble x, + gdouble y, + cairo_t *cr, + gboolean is_pointer_event) +{ + GooCanvasItemSimpleData *simple_data = simple->simple_data; + GooCanvasPointerEvents pointer_events = GOO_CANVAS_EVENTS_ALL; + gboolean do_fill; + + /* By default only check the fill if a fill color/pattern is specified. */ + do_fill = goo_canvas_style_set_fill_options (simple_data->style, cr); + if (!do_fill) + pointer_events &= ~GOO_CANVAS_EVENTS_FILL_MASK; + + /* If is_pointer_event is set use the pointer_events property instead. */ + if (is_pointer_event) + pointer_events = simple_data->pointer_events; + + goo_canvas_path_create_path (simple, cr); + if (goo_canvas_item_simple_check_in_path (simple, x, y, cr, pointer_events)) + return TRUE; + + return FALSE; +} + + static void goo_canvas_path_set_model (GooCanvasItem *item, GooCanvasItemModel *model) @@ -313,6 +584,24 @@ canvas_item_interface_init (GooCanvasItemIface *iface) } +static void +goo_canvas_path_class_init (GooCanvasPathClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass*) klass; + GooCanvasItemSimpleClass *simple_class = (GooCanvasItemSimpleClass*) klass; + + gobject_class->finalize = goo_canvas_path_finalize; + + gobject_class->get_property = goo_canvas_path_get_property; + gobject_class->set_property = goo_canvas_path_set_property; + + simple_class->simple_create_path = goo_canvas_path_create_path; + simple_class->simple_is_item_at = goo_canvas_path_is_item_at; + + goo_canvas_path_install_common_properties (gobject_class); +} + + /** * SECTION:goocanvaspathmodel * @Title: GooCanvasPathModel @@ -478,8 +767,8 @@ goo_canvas_path_model_get_property (GObject *object, { GooCanvasPathModel *pmodel = (GooCanvasPathModel*) object; - goo_canvas_path_get_common_property (object, &pmodel->path_data, prop_id, - value, pspec); + goo_canvas_path_get_common_property (object, NULL, &pmodel->path_data, + prop_id, value, pspec); } @@ -491,8 +780,8 @@ goo_canvas_path_model_set_property (GObject *object, { GooCanvasPathModel *pmodel = (GooCanvasPathModel*) object; - goo_canvas_path_set_common_property (object, &pmodel->path_data, prop_id, - value, pspec); + goo_canvas_path_set_common_property (object, NULL, &pmodel->path_data, + prop_id, value, pspec); g_signal_emit_by_name (pmodel, "changed", TRUE); } diff --git a/src/goocanvas/src/goocanvaspolyline.c b/src/goocanvas/src/goocanvaspolyline.c index c78a2b7..94fc8c4 100644 --- a/src/goocanvas/src/goocanvaspolyline.c +++ b/src/goocanvas/src/goocanvaspolyline.c @@ -117,20 +117,16 @@ enum { PROP_END_ARROW, PROP_ARROW_LENGTH, PROP_ARROW_WIDTH, - PROP_ARROW_TIP_LENGTH + PROP_ARROW_TIP_LENGTH, + + PROP_X, + PROP_Y, + PROP_WIDTH, + PROP_HEIGHT }; -static void goo_canvas_polyline_finalize (GObject *object); static void canvas_item_interface_init (GooCanvasItemIface *iface); -static void goo_canvas_polyline_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec); -static void goo_canvas_polyline_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec); G_DEFINE_TYPE_WITH_CODE (GooCanvasPolyline, goo_canvas_polyline, GOO_TYPE_CANVAS_ITEM_SIMPLE, @@ -189,6 +185,36 @@ goo_canvas_polyline_install_common_properties (GObjectClass *gobject_class) _("The length of the arrow tip, as a multiple of the line width"), 0.0, G_MAXDOUBLE, 4.0, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_X, + g_param_spec_double ("x", + "X", + _("The x coordinate of the left-most point of the polyline"), + -G_MAXDOUBLE, + G_MAXDOUBLE, 0.0, + G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_Y, + g_param_spec_double ("y", + "Y", + _("The y coordinate of the top-most point of the polyline"), + -G_MAXDOUBLE, + G_MAXDOUBLE, 0.0, + G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_WIDTH, + g_param_spec_double ("width", + _("Width"), + _("The width of the polyline"), + 0.0, G_MAXDOUBLE, 0.0, + G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_HEIGHT, + g_param_spec_double ("height", + _("Height"), + _("The height of the polyline"), + 0.0, G_MAXDOUBLE, 0.0, + G_PARAM_READWRITE)); } @@ -221,6 +247,32 @@ goo_canvas_polyline_finalize (GObject *object) static void +goo_canvas_polyline_get_extent (GooCanvasPolylineData *polyline_data, + GooCanvasBounds *bounds) +{ + guint i; + + if (polyline_data->num_points == 0) + { + bounds->x1 = bounds->y1 = bounds->x2 = bounds->y2 = 0.0; + } + else + { + bounds->x1 = bounds->x2 = polyline_data->coords[0]; + bounds->y1 = bounds->y2 = polyline_data->coords[1]; + + for (i = 1; i < polyline_data->num_points; i++) + { + bounds->x1 = MIN (bounds->x1, polyline_data->coords[2 * i]); + bounds->y1 = MIN (bounds->y1, polyline_data->coords[2 * i + 1]); + bounds->x2 = MAX (bounds->x2, polyline_data->coords[2 * i]); + bounds->y2 = MAX (bounds->y2, polyline_data->coords[2 * i + 1]); + } + } +} + + +static void goo_canvas_polyline_get_common_property (GObject *object, GooCanvasPolylineData *polyline_data, guint prop_id, @@ -228,6 +280,7 @@ goo_canvas_polyline_get_common_property (GObject *object, GParamSpec *pspec) { GooCanvasPoints *points; + GooCanvasBounds extent; switch (prop_id) { @@ -266,6 +319,22 @@ goo_canvas_polyline_get_common_property (GObject *object, g_value_set_double (value, polyline_data->arrow_data ? polyline_data->arrow_data->arrow_tip_length : 4.0); break; + case PROP_X: + goo_canvas_polyline_get_extent (polyline_data, &extent); + g_value_set_double (value, extent.x1); + break; + case PROP_Y: + goo_canvas_polyline_get_extent (polyline_data, &extent); + g_value_set_double (value, extent.y1); + break; + case PROP_WIDTH: + goo_canvas_polyline_get_extent (polyline_data, &extent); + g_value_set_double (value, extent.x2 - extent.x1); + break; + case PROP_HEIGHT: + goo_canvas_polyline_get_extent (polyline_data, &extent); + g_value_set_double (value, extent.y2 - extent.y1); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -437,6 +506,9 @@ goo_canvas_polyline_set_common_property (GObject *object, GParamSpec *pspec) { GooCanvasPoints *points; + GooCanvasBounds extent; + gdouble x_offset, y_offset, x_scale, y_scale; + guint i; switch (prop_id) { @@ -461,6 +533,10 @@ goo_canvas_polyline_set_common_property (GObject *object, polyline_data->num_points * 2 * sizeof (double)); } polyline_data->reconfigure_arrows = TRUE; + g_object_notify (object, "x"); + g_object_notify (object, "y"); + g_object_notify (object, "width"); + g_object_notify (object, "height"); break; case PROP_CLOSE_PATH: polyline_data->close_path = g_value_get_boolean (value); @@ -489,6 +565,68 @@ goo_canvas_polyline_set_common_property (GObject *object, polyline_data->arrow_data->arrow_tip_length = g_value_get_double (value); polyline_data->reconfigure_arrows = TRUE; break; + case PROP_X: + if (polyline_data->num_points > 0) + { + /* Calculate the x offset from the current position. */ + goo_canvas_polyline_get_extent (polyline_data, &extent); + x_offset = g_value_get_double (value) - extent.x1; + + /* Add the offset to all the x coordinates. */ + for (i = 0; i < polyline_data->num_points; i++) + polyline_data->coords[2 * i] += x_offset; + + g_object_notify (object, "points"); + } + break; + case PROP_Y: + if (polyline_data->num_points > 0) + { + /* Calculate the y offset from the current position. */ + goo_canvas_polyline_get_extent (polyline_data, &extent); + y_offset = g_value_get_double (value) - extent.y1; + + /* Add the offset to all the y coordinates. */ + for (i = 0; i < polyline_data->num_points; i++) + polyline_data->coords[2 * i + 1] += y_offset; + + g_object_notify (object, "points"); + } + break; + case PROP_WIDTH: + if (polyline_data->num_points >= 2) + { + goo_canvas_polyline_get_extent (polyline_data, &extent); + if (extent.x2 - extent.x1 != 0.0) + { + /* Calculate the amount to scale the polyline. */ + x_scale = g_value_get_double (value) / (extent.x2 - extent.x1); + + /* Scale the x coordinates, relative to the left-most point. */ + for (i = 0; i < polyline_data->num_points; i++) + polyline_data->coords[2 * i] = extent.x1 + (polyline_data->coords[2 * i] - extent.x1) * x_scale; + + g_object_notify (object, "points"); + } + } + break; + case PROP_HEIGHT: + if (polyline_data->num_points >= 2) + { + goo_canvas_polyline_get_extent (polyline_data, &extent); + if (extent.y2 - extent.y1 != 0.0) + { + /* Calculate the amount to scale the polyline. */ + y_scale = g_value_get_double (value) / (extent.y2 - extent.y1); + + /* Scale the y coordinates, relative to the top-most point. */ + for (i = 0; i < polyline_data->num_points; i++) + polyline_data->coords[2 * i + 1] = extent.y1 + (polyline_data->coords[2 * i + 1] - extent.y1) * y_scale; + + g_object_notify (object, "points"); + } + } + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -769,6 +907,10 @@ goo_canvas_polyline_is_item_at (GooCanvasItemSimple *simple, if (is_pointer_event) pointer_events = simple_data->pointer_events; + /* If the path isn't closed, we never check the fill. */ + if (!(polyline_data->close_path && polyline_data->num_points > 2)) + pointer_events &= ~GOO_CANVAS_EVENTS_FILL_MASK; + goo_canvas_polyline_create_path (polyline, cr); if (goo_canvas_item_simple_check_in_path (simple, x, y, cr, pointer_events)) return TRUE; diff --git a/src/goocanvas/src/goocanvasprivate.h b/src/goocanvas/src/goocanvasprivate.h index 3e58377..423f37e 100644 --- a/src/goocanvas/src/goocanvasprivate.h +++ b/src/goocanvas/src/goocanvasprivate.h @@ -8,6 +8,7 @@ #define __GOO_CANVAS_PRIVATE_H__ #include <gtk/gtk.h> +#include "goocanvasstyle.h" G_BEGIN_DECLS @@ -30,6 +31,22 @@ gint goo_canvas_util_ptr_array_find_index (GPtrArray *ptr_array, cairo_pattern_t* goo_canvas_cairo_pattern_from_pixbuf (GdkPixbuf *pixbuf); cairo_surface_t* goo_canvas_cairo_surface_from_pixbuf (GdkPixbuf *pixbuf); +guint goo_canvas_convert_colors_to_rgba (double red, + double green, + double blue, + double alpha); + +void goo_canvas_get_rgba_value_from_pattern (cairo_pattern_t *pattern, + GValue *value); + +void goo_canvas_set_style_property_from_pattern (GooCanvasStyle *style, + GQuark property_id, + cairo_pattern_t *pattern); + +cairo_pattern_t* goo_canvas_create_pattern_from_color_value (const GValue *value); +cairo_pattern_t* goo_canvas_create_pattern_from_rgba_value (const GValue *value); +cairo_pattern_t* goo_canvas_create_pattern_from_pixbuf_value (const GValue *value); + gboolean goo_canvas_boolean_handled_accumulator (GSignalInvocationHint *ihint, GValue *return_accu, diff --git a/src/goocanvas/src/goocanvasrect.c b/src/goocanvas/src/goocanvasrect.c index e899546..0af1021 100644 --- a/src/goocanvas/src/goocanvasrect.c +++ b/src/goocanvas/src/goocanvasrect.c @@ -360,7 +360,6 @@ goo_canvas_rect_create_path (GooCanvasItemSimple *simple, /* Draw the plain rectangle. */ cairo_rectangle (cr, rect_data->x, rect_data->y, rect_data->width, rect_data->height); - cairo_close_path (cr); } } diff --git a/src/goocanvas/src/goocanvastable.c b/src/goocanvas/src/goocanvastable.c index 8ff2fe0..d20240b 100644 --- a/src/goocanvas/src/goocanvastable.c +++ b/src/goocanvas/src/goocanvastable.c @@ -51,6 +51,8 @@ enum { PROP_0, + PROP_X, + PROP_Y, PROP_WIDTH, PROP_HEIGHT, PROP_ROW_SPACING, @@ -164,6 +166,10 @@ struct _GooCanvasTableLayoutData GooCanvasTableDimensionLayoutData *dldata[2]; GooCanvasTableChildLayoutData *children; + /* Position of the table */ + gdouble x; + gdouble y; + /* This is TRUE if we are rounding everything to the nearest integer. */ gboolean integer_layout; @@ -219,20 +225,11 @@ static void goo_canvas_table_install_common_properties (GObjectClass *gobject_class, InstallChildPropertyFunc install_child_property) { - g_object_class_install_property (gobject_class, PROP_WIDTH, - g_param_spec_double ("width", - _("Width"), - _("The requested width of the table, or -1 to use the default width"), - -G_MAXDOUBLE, - G_MAXDOUBLE, -1.0, - G_PARAM_READWRITE)); - g_object_class_install_property (gobject_class, PROP_HEIGHT, - g_param_spec_double ("height", - _("Height"), - _("The requested height of the table, or -1 to use the default height"), - -G_MAXDOUBLE, - G_MAXDOUBLE, -1.0, - G_PARAM_READWRITE)); + /* Override from GooCanvasGroup */ + g_object_class_override_property (gobject_class, PROP_X, "x"); + g_object_class_override_property (gobject_class, PROP_Y, "y"); + g_object_class_override_property (gobject_class, PROP_WIDTH, "width"); + g_object_class_override_property (gobject_class, PROP_HEIGHT, "height"); /* FIXME: Support setting individual row/col spacing. */ g_object_class_install_property (gobject_class, PROP_ROW_SPACING, @@ -292,101 +289,101 @@ goo_canvas_table_install_common_properties (GObjectClass *gobject_class, * Child properties. */ install_child_property (gobject_class, CHILD_PROP_LEFT_PADDING, - g_param_spec_double ("left-padding", - _("Left Padding"), + g_param_spec_double ("left-padding", + _("Left Padding"), _("Extra space to add to the left of the item"), 0.0, G_MAXDOUBLE, 0.0, G_PARAM_READWRITE)); install_child_property (gobject_class, CHILD_PROP_RIGHT_PADDING, - g_param_spec_double ("right-padding", - _("Right Padding"), + g_param_spec_double ("right-padding", + _("Right Padding"), _("Extra space to add to the right of the item"), 0.0, G_MAXDOUBLE, 0.0, G_PARAM_READWRITE)); install_child_property (gobject_class, CHILD_PROP_TOP_PADDING, - g_param_spec_double ("top-padding", - _("Top Padding"), + g_param_spec_double ("top-padding", + _("Top Padding"), _("Extra space to add above the item"), 0.0, G_MAXDOUBLE, 0.0, G_PARAM_READWRITE)); install_child_property (gobject_class, CHILD_PROP_BOTTOM_PADDING, - g_param_spec_double ("bottom-padding", - _("Bottom Padding"), + g_param_spec_double ("bottom-padding", + _("Bottom Padding"), _("Extra space to add below the item"), 0.0, G_MAXDOUBLE, 0.0, G_PARAM_READWRITE)); install_child_property (gobject_class, CHILD_PROP_X_ALIGN, - g_param_spec_double ("x-align", - _("X Align"), + g_param_spec_double ("x-align", + _("X Align"), _("The horizontal position of the item within its allocated space. 0.0 is left-aligned, 1.0 is right-aligned"), 0.0, 1.0, 0.5, G_PARAM_READWRITE)); install_child_property (gobject_class, CHILD_PROP_Y_ALIGN, - g_param_spec_double ("y-align", - _("Y Align"), + g_param_spec_double ("y-align", + _("Y Align"), _("The vertical position of the item within its allocated space. 0.0 is top-aligned, 1.0 is bottom-aligned"), 0.0, 1.0, 0.5, G_PARAM_READWRITE)); install_child_property (gobject_class, CHILD_PROP_ROW, - g_param_spec_uint ("row", - _("Row"), + g_param_spec_uint ("row", + _("Row"), _("The row to place the item in"), 0, 65535, 0, G_PARAM_READWRITE)); install_child_property (gobject_class, CHILD_PROP_COLUMN, - g_param_spec_uint ("column", - _("Column"), + g_param_spec_uint ("column", + _("Column"), _("The column to place the item in"), 0, 65535, 0, G_PARAM_READWRITE)); install_child_property (gobject_class, CHILD_PROP_ROWS, - g_param_spec_uint ("rows", - _("Rows"), + g_param_spec_uint ("rows", + _("Rows"), _("The number of rows that the item spans"), 0, 65535, 1, G_PARAM_READWRITE)); install_child_property (gobject_class, CHILD_PROP_COLUMNS, - g_param_spec_uint ("columns", - _("Columns"), + g_param_spec_uint ("columns", + _("Columns"), _("The number of columns that the item spans"), 0, 65535, 1, G_PARAM_READWRITE)); install_child_property (gobject_class, CHILD_PROP_X_EXPAND, - g_param_spec_boolean ("x-expand", - _("X Expand"), + g_param_spec_boolean ("x-expand", + _("X Expand"), _("If the item expands horizontally as the table expands"), FALSE, G_PARAM_READWRITE)); install_child_property (gobject_class, CHILD_PROP_X_FILL, - g_param_spec_boolean ("x-fill", - _("X Fill"), + g_param_spec_boolean ("x-fill", + _("X Fill"), _("If the item fills all horizontal allocated space"), FALSE, G_PARAM_READWRITE)); install_child_property (gobject_class, CHILD_PROP_X_SHRINK, - g_param_spec_boolean ("x-shrink", - _("X Shrink"), + g_param_spec_boolean ("x-shrink", + _("X Shrink"), _("If the item can shrink smaller than its requested size horizontally"), FALSE, G_PARAM_READWRITE)); install_child_property (gobject_class, CHILD_PROP_Y_EXPAND, - g_param_spec_boolean ("y-expand", - _("Y Expand"), + g_param_spec_boolean ("y-expand", + _("Y Expand"), _("If the item expands vertically as the table expands"), FALSE, G_PARAM_READWRITE)); install_child_property (gobject_class, CHILD_PROP_Y_FILL, - g_param_spec_boolean ("y-fill", - _("Y Fill"), + g_param_spec_boolean ("y-fill", + _("Y Fill"), _("If the item fills all vertical allocated space"), FALSE, G_PARAM_READWRITE)); install_child_property (gobject_class, CHILD_PROP_Y_SHRINK, - g_param_spec_boolean ("y-shrink", - _("Y Shrink"), + g_param_spec_boolean ("y-shrink", + _("Y Shrink"), _("If the item can shrink smaller than its requested size vertically"), FALSE, G_PARAM_READWRITE)); @@ -430,6 +427,9 @@ goo_canvas_table_init_data (GooCanvasTableData *table_data) table_data->children = g_array_new (0, 0, sizeof (GooCanvasTableChild)); table_data->layout_data = g_slice_new (GooCanvasTableLayoutData); + table_data->layout_data->x = 0.0; + table_data->layout_data->y = 0.0; + table_data->layout_data->children = NULL; for (d = 0; d < 2; d++) { @@ -473,7 +473,7 @@ goo_canvas_table_init (GooCanvasTable *table) * ownership of the item, and the item will automatically be freed when it is * removed from the parent. Otherwise call g_object_unref() to free it. * @...: optional pairs of property names and values, and a terminating %NULL. - * + * * Creates a new table item. * * <!--PARAMETERS--> @@ -483,13 +483,13 @@ goo_canvas_table_init (GooCanvasTable *table) * * <informalexample><programlisting> * GooCanvasItem *table, *square, *circle, *triangle; - * + * * table = goo_canvas_table_new (root, * "row-spacing", 4.0, * "column-spacing", 4.0, * NULL); * goo_canvas_item_translate (table, 400, 200); - * + * * square = goo_canvas_rect_new (table, 0.0, 0.0, 50.0, 50.0, * "fill-color", "red", * NULL); @@ -497,7 +497,7 @@ goo_canvas_table_init (GooCanvasTable *table) * "row", 0, * "column", 0, * NULL); - * + * * circle = goo_canvas_ellipse_new (table, 0.0, 0.0, 25.0, 25.0, * "fill-color", "blue", * NULL); @@ -505,7 +505,7 @@ goo_canvas_table_init (GooCanvasTable *table) * "row", 0, * "column", 1, * NULL); - * + * * triangle = goo_canvas_polyline_new (table, TRUE, 3, * 25.0, 0.0, 0.0, 50.0, 50.0, 50.0, * "fill-color", "yellow", @@ -515,7 +515,7 @@ goo_canvas_table_init (GooCanvasTable *table) * "column", 2, * NULL); * </programlisting></informalexample> - * + * * Returns: a new table item. **/ GooCanvasItem* @@ -572,6 +572,12 @@ goo_canvas_table_get_common_property (GObject *object, { switch (prop_id) { + case PROP_X: + g_value_set_double (value, table_data->layout_data->x); + break; + case PROP_Y: + g_value_set_double (value, table_data->layout_data->y); + break; case PROP_WIDTH: g_value_set_double (value, table_data->width); break; @@ -618,7 +624,7 @@ goo_canvas_table_get_property (GObject *object, GooCanvasTable *table = (GooCanvasTable*) object; goo_canvas_table_get_common_property (object, table->table_data, - prop_id, value, pspec); + prop_id, value, pspec); } @@ -633,6 +639,12 @@ goo_canvas_table_set_common_property (GObject *object, switch (prop_id) { + case PROP_X: + table_data->layout_data->x = g_value_get_double (value); + break; + case PROP_Y: + table_data->layout_data->y = g_value_get_double (value); + break; case PROP_WIDTH: table_data->width = g_value_get_double (value); break; @@ -1438,7 +1450,7 @@ goo_canvas_table_size_request_pass2 (GooCanvasTable *table, GooCanvasTableDimensionLayoutData *dldata = layout_data->dldata[d]; gdouble max_size = 0.0; gint i; - + if (table_data->dimensions[d].homogeneous) { /* Calculate the maximum row or column size. */ @@ -1464,11 +1476,11 @@ goo_canvas_table_size_request_pass3 (GooCanvasTable *table, GooCanvasTableDimensionLayoutData *dldata; GooCanvasTableChild *child; gint i, j; - + for (i = 0; i < table_data->children->len; i++) { child = &g_array_index (table_data->children, GooCanvasTableChild, i); - + if (layout_data->children[i].requested_size[HORZ] <= 0.0) continue; @@ -1488,7 +1500,7 @@ goo_canvas_table_size_request_pass3 (GooCanvasTable *table, if (j < end) total_space += dldata[j].spacing; } - + /* If we need to request more space for this child to fill its requisition, then divide up the needed space amongst the columns it spans, favoring expandable columns if any. */ @@ -1514,7 +1526,7 @@ goo_canvas_table_size_request_pass3 (GooCanvasTable *table, n_expand = child->size[d]; force_expand = TRUE; } - + if (layout_data->integer_layout) { for (j = start; j <= end; j++) @@ -1552,7 +1564,7 @@ goo_canvas_table_size_allocate_init (GooCanvasTable *table, GooCanvasTableDimension *dimension = &table_data->dimensions[d]; GooCanvasTableDimensionLayoutData *dldata = layout_data->dldata[d]; gint i; - + /* Set the initial allocation, by copying over the requisition. Also set the final expand & shrink flags. */ for (i = 0; i < dimension->size; i++) @@ -1570,7 +1582,7 @@ goo_canvas_table_size_allocate_pass1 (GooCanvasTable *table, GooCanvasTableDimensionLayoutData *dldata; gdouble total_size, size_to_allocate, natural_size, extra, old_extra; gint i, nexpand, nshrink; - + /* If we were allocated more space than we requested * then we have to expand any expandable rows and columns * to fill in the extra space. @@ -1620,7 +1632,7 @@ goo_canvas_table_size_allocate_pass1 (GooCanvasTable *table, size_to_allocate = total_size; for (i = 0; i + 1 < dimension->size; i++) size_to_allocate -= dldata[i].spacing; - + if (layout_data->integer_layout) { gint n_elements = dimension->size; @@ -1669,14 +1681,14 @@ goo_canvas_table_size_allocate_pass1 (GooCanvasTable *table, } } } - + /* Check to see if we were allocated less width than we requested, * then shrink until we fit the size give. */ if (natural_size > total_size) { gint total_nshrink = nshrink; - + extra = natural_size - total_size; while (total_nshrink > 0 && extra > 0) { @@ -1778,7 +1790,7 @@ goo_canvas_table_size_allocate_pass3 (GooCanvasTable *table, if (requested_width <= 0.0) continue; - + start_column = child->start[HORZ]; end_column = child->start[HORZ] + child->size[HORZ] - 1; x = columns[start_column].start + layout_data->children[i].start_pad[HORZ]; @@ -1800,7 +1812,7 @@ goo_canvas_table_size_allocate_pass3 (GooCanvasTable *table, if (layout_data->integer_layout) x = floor (x + 0.5); } - + if (!(child->flags[VERT] & GOO_CANVAS_TABLE_CHILD_FILL)) { height = MIN (max_height, requested_height); @@ -1929,11 +1941,14 @@ goo_canvas_table_get_requested_area (GooCanvasItem *item, GooCanvasItemSimpleData *simple_data = simple->simple_data; GooCanvasTable *table = (GooCanvasTable*) item; GooCanvasTableData *table_data = table->table_data; - GooCanvasTableLayoutData *layout_data; + GooCanvasTableLayoutData *layout_data = table_data->layout_data; GooCanvasTableDimensionLayoutData *rows, *columns; gdouble width = 0.0, height = 0.0; gint row, column, end; - + + /* Request a redraw of the existing bounds */ + goo_canvas_request_item_redraw (simple->canvas, &simple->bounds, simple_data->is_static); + /* We reset the bounds to 0, just in case we are hidden or aren't allocated any area. */ simple->bounds.x1 = simple->bounds.x2 = 0.0; @@ -1950,6 +1965,8 @@ goo_canvas_table_get_requested_area (GooCanvasItem *item, if (simple_data->transform) cairo_transform (cr, simple_data->transform); + cairo_translate (cr, layout_data->x, layout_data->y); + /* Initialize the layout data, get the requested sizes of all children, and set the expand, shrink and empty flags. */ goo_canvas_table_init_layout_data (table); @@ -1961,7 +1978,6 @@ goo_canvas_table_get_requested_area (GooCanvasItem *item, goo_canvas_table_size_request_pass3 (table, HORZ); goo_canvas_table_size_request_pass2 (table, HORZ); - layout_data = table_data->layout_data; rows = layout_data->dldata[VERT]; columns = layout_data->dldata[HORZ]; @@ -1973,7 +1989,7 @@ goo_canvas_table_get_requested_area (GooCanvasItem *item, width += columns[column].spacing; } width += (layout_data->border_width + layout_data->border_spacing[HORZ] + layout_data->grid_line_width[VERT]) * 2.0; - + /* Save the natural size, so we know if we have to clip children. */ layout_data->natural_size[HORZ] = width; @@ -2046,6 +2062,8 @@ goo_canvas_table_get_requested_height (GooCanvasItem *item, if (simple_data->transform) cairo_transform (cr, simple_data->transform); + cairo_translate (cr, layout_data->x, layout_data->y); + /* Convert the width from the parent's coordinate space. Note that we only need to support a simple scale operation here. */ if (simple_data->transform) @@ -2129,12 +2147,14 @@ goo_canvas_table_allocate_area (GooCanvasItem *item, -(allocated_area->y1 - requested_area->y1)); if (simple_data->transform) cairo_transform (cr, simple_data->transform); + cairo_translate (cr, layout_data->x, layout_data->y); goo_canvas_table_update_requested_heights (item, cr); cairo_restore (cr); cairo_save (cr); if (simple_data->transform) cairo_transform (cr, simple_data->transform); + cairo_translate (cr, layout_data->x, layout_data->y); /* Calculate the table's bounds. */ simple->bounds.x1 = simple->bounds.y1 = 0.0; @@ -2154,6 +2174,8 @@ goo_canvas_table_allocate_area (GooCanvasItem *item, layout_data->children = NULL; cairo_restore (cr); + + goo_canvas_request_item_redraw (simple->canvas, &simple->bounds, simple_data->is_static); } @@ -2193,6 +2215,7 @@ goo_canvas_table_paint (GooCanvasItem *item, { GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item; GooCanvasItemSimpleData *simple_data = simple->simple_data; + GooCanvasStyle *style = simple_data->style; GooCanvasGroup *group = (GooCanvasGroup*) item; GooCanvasTable *table = (GooCanvasTable*) item; GooCanvasTableData *table_data = table->table_data; @@ -2210,7 +2233,8 @@ goo_canvas_table_paint (GooCanvasItem *item, gdouble frame_width, frame_height; gdouble line_start, line_end; gdouble spacing, half_spacing_before, half_spacing_after; - gboolean old_grid_line_visibility = FALSE, cur_grid_line_visibility; + gboolean old_grid_line_visibility = FALSE; + gboolean cur_grid_line_visibility; /* Skip the item if the bounds don't intersect the expose rectangle. */ if (simple->bounds.x1 > bounds->x2 || simple->bounds.x2 < bounds->x1 @@ -2227,6 +2251,7 @@ goo_canvas_table_paint (GooCanvasItem *item, cairo_save (cr); if (simple_data->transform) cairo_transform (cr, simple_data->transform); + cairo_translate (cr, layout_data->x, layout_data->y); /* Clip with the table's clip path, if it is set. */ if (simple_data->clip_path_commands) @@ -2249,10 +2274,6 @@ goo_canvas_table_paint (GooCanvasItem *item, frame_width = MAX (layout_data->allocated_size[HORZ], layout_data->natural_size[HORZ]); frame_height = MAX (layout_data->allocated_size[VERT], layout_data->natural_size[VERT]); - /* Save current line width, line cap etc. for drawing items after having - drawn grid lines */ - cairo_save (cr); - /* Draw border and grid lines */ if (check_clip) { @@ -2264,7 +2285,26 @@ goo_canvas_table_paint (GooCanvasItem *item, cairo_clip (cr); } - cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT); + /* Save current line width, line cap etc. for drawing items after having + drawn grid lines */ + cairo_save (cr); + + /* Fill the table, if desired. */ + if (goo_canvas_style_set_fill_options (style, cr)) + { + cairo_rectangle (cr, + layout_data->border_width + vert_grid_line_width, + layout_data->border_width + horz_grid_line_width, + layout_data->allocated_size[HORZ] - 2 * (layout_data->border_width + vert_grid_line_width), + layout_data->allocated_size[VERT] - 2 * (layout_data->border_width + horz_grid_line_width)); + cairo_fill (cr); + } + + /* We use the style for the stroke color, but the line cap style and line + width are overridden here. */ + goo_canvas_style_set_stroke_options (style, cr); + + cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT); /* Horizontal grid lines */ if (horz_grid_line_width > 0.0) @@ -2320,7 +2360,7 @@ goo_canvas_table_paint (GooCanvasItem *item, old_grid_line_visibility = cur_grid_line_visibility; } } - + cairo_stroke (cr); } @@ -2444,7 +2484,7 @@ goo_canvas_table_paint (GooCanvasItem *item, for (j = start_row; j <= end_row; j++) if (rows[j].shrink) clip = TRUE; - + /* Only clip the child if it may have been shrunk. */ if (clip) { @@ -2515,6 +2555,7 @@ goo_canvas_table_get_items_at (GooCanvasItem *item, cairo_save (cr); if (simple_data->transform) cairo_transform (cr, simple_data->transform); + cairo_translate (cr, layout_data->x, layout_data->y); cairo_device_to_user (cr, &user_x, &user_y); @@ -2730,7 +2771,7 @@ goo_canvas_table_model_init (GooCanvasTableModel *tmodel) * assume ownership of the item, and the item will automatically be freed when * it is removed from the parent. Otherwise call g_object_unref() to free it. * @...: optional pairs of property names and values, and a terminating %NULL. - * + * * Creates a new table model. * * <!--PARAMETERS--> @@ -2772,7 +2813,7 @@ goo_canvas_table_model_init (GooCanvasTableModel *tmodel) * "column", 2, * NULL); * </programlisting></informalexample> - * + * * Returns: a new table model. **/ GooCanvasItemModel* diff --git a/src/goocanvas/src/goocanvastext.c b/src/goocanvas/src/goocanvastext.c index 9a245cd..5b100ae 100644 --- a/src/goocanvas/src/goocanvastext.c +++ b/src/goocanvas/src/goocanvastext.c @@ -19,6 +19,10 @@ * #GooCanvasItem functions such as goo_canvas_item_raise() and * goo_canvas_item_rotate(). * + * The #GooCanvasText:width and #GooCanvasText:height properties specify the + * area of the item. If it exceeds that area because there is too much text, + * it is clipped. The properties can be set to -1 to disable clipping. + * * To create a #GooCanvasText use goo_canvas_text_new(). * * To get or set the properties of an existing #GooCanvasText, use @@ -30,6 +34,15 @@ #include "goocanvastext.h" #include "goocanvas.h" +typedef struct _GooCanvasTextPrivate GooCanvasTextPrivate; +struct _GooCanvasTextPrivate { + gdouble height; +}; + +#define GOO_CANVAS_TEXT_GET_PRIVATE(text) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((text), GOO_TYPE_CANVAS_TEXT, GooCanvasTextPrivate)) +#define GOO_CANVAS_TEXT_MODEL_GET_PRIVATE(text) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((text), GOO_TYPE_CANVAS_TEXT_MODEL, GooCanvasTextPrivate)) enum { PROP_0, @@ -37,6 +50,7 @@ enum { PROP_X, PROP_Y, PROP_WIDTH, + PROP_HEIGHT, PROP_TEXT, PROP_USE_MARKUP, PROP_ANCHOR, @@ -45,6 +59,15 @@ enum { PROP_WRAP }; +static PangoLayout* +goo_canvas_text_create_layout (GooCanvasItemSimpleData *simple_data, + GooCanvasTextData *text_data, + gdouble layout_width, + cairo_t *cr, + GooCanvasBounds *bounds, + gdouble *origin_x_return, + gdouble *origin_y_return); + static void goo_canvas_text_finalize (GObject *object); static void canvas_item_interface_init (GooCanvasItemIface *iface); static void goo_canvas_text_get_property (GObject *object, @@ -121,6 +144,15 @@ goo_canvas_text_install_common_properties (GObjectClass *gobject_class) G_MAXDOUBLE, -1.0, G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, PROP_HEIGHT, + g_param_spec_double ("height", + _("Height"), + _("The height to use to layout the text, or -1 to use the natural height"), + -G_MAXDOUBLE, + G_MAXDOUBLE, -1.0, + G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_ANCHOR, g_param_spec_enum ("anchor", _("Anchor"), @@ -142,6 +174,8 @@ goo_canvas_text_install_common_properties (GObjectClass *gobject_class) static void goo_canvas_text_init (GooCanvasText *text) { + GooCanvasTextPrivate *priv = GOO_CANVAS_TEXT_GET_PRIVATE (text); + text->text_data = g_slice_new0 (GooCanvasTextData); text->text_data->width = -1.0; text->text_data->anchor = GTK_ANCHOR_NW; @@ -149,6 +183,8 @@ goo_canvas_text_init (GooCanvasText *text) text->text_data->wrap = PANGO_WRAP_WORD; text->layout_width = -1.0; + + priv->height = -1.0; } @@ -243,9 +279,23 @@ goo_canvas_text_finalize (GObject *object) } +/* Gets the private data to use, from the model or from the item itself. */ +static GooCanvasTextPrivate* +goo_canvas_text_get_private (GooCanvasText *text) +{ + GooCanvasItemSimple *simple = (GooCanvasItemSimple*) text; + + if (simple->model) + return GOO_CANVAS_TEXT_MODEL_GET_PRIVATE (simple->model); + else + return GOO_CANVAS_TEXT_GET_PRIVATE (text); +} + + static void goo_canvas_text_get_common_property (GObject *object, GooCanvasTextData *text_data, + GooCanvasTextPrivate *priv, guint prop_id, GValue *value, GParamSpec *pspec) @@ -261,6 +311,9 @@ goo_canvas_text_get_common_property (GObject *object, case PROP_WIDTH: g_value_set_double (value, text_data->width); break; + case PROP_HEIGHT: + g_value_set_double (value, priv->height); + break; case PROP_TEXT: g_value_set_string (value, text_data->text); break; @@ -293,15 +346,17 @@ goo_canvas_text_get_property (GObject *object, GParamSpec *pspec) { GooCanvasText *text = (GooCanvasText*) object; + GooCanvasTextPrivate *priv = goo_canvas_text_get_private (text); - goo_canvas_text_get_common_property (object, text->text_data, prop_id, - value, pspec); + goo_canvas_text_get_common_property (object, text->text_data, priv, + prop_id, value, pspec); } static void goo_canvas_text_set_common_property (GObject *object, GooCanvasTextData *text_data, + GooCanvasTextPrivate *priv, guint prop_id, const GValue *value, GParamSpec *pspec) @@ -317,6 +372,9 @@ goo_canvas_text_set_common_property (GObject *object, case PROP_WIDTH: text_data->width = g_value_get_double (value); break; + case PROP_HEIGHT: + priv->height = g_value_get_double (value); + break; case PROP_TEXT: g_free (text_data->text); text_data->text = g_value_dup_string (value); @@ -351,6 +409,7 @@ goo_canvas_text_set_property (GObject *object, { GooCanvasItemSimple *simple = (GooCanvasItemSimple*) object; GooCanvasText *text = (GooCanvasText*) object; + GooCanvasTextPrivate *priv = goo_canvas_text_get_private (text); if (simple->model) { @@ -358,7 +417,7 @@ goo_canvas_text_set_property (GObject *object, return; } - goo_canvas_text_set_common_property (object, text->text_data, prop_id, + goo_canvas_text_set_common_property (object, text->text_data, priv, prop_id, value, pspec); goo_canvas_item_simple_changed (simple, TRUE); } @@ -409,7 +468,6 @@ goo_canvas_text_create_layout (GooCanvasItemSimpleData *simple_data, font_options = cairo_font_options_create (); cairo_font_options_set_hint_metrics (font_options, hint_metrics); - cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_NONE); pango_cairo_context_set_font_options (context, font_options); cairo_font_options_destroy (font_options); @@ -501,8 +559,8 @@ goo_canvas_text_create_layout (GooCanvasItemSimpleData *simple_data, } } - bounds->x2 = origin_x + logical_width; - bounds->y2 = origin_y + logical_height; + bounds->x2 = bounds->x1 + logical_width; + bounds->y2 = bounds->y1 + logical_height; /* Now adjust it to take into account the ink bounds. Calculate how far the ink rect extends outside each edge of the logical rect and adjust @@ -535,6 +593,7 @@ goo_canvas_text_update (GooCanvasItemSimple *simple, cairo_t *cr) { GooCanvasText *text = (GooCanvasText*) simple; + GooCanvasTextPrivate *priv = goo_canvas_text_get_private (text); PangoLayout *layout; /* Initialize the layout width to the text item's specified width property. @@ -547,6 +606,10 @@ goo_canvas_text_update (GooCanvasItemSimple *simple, text->layout_width, cr, &simple->bounds, NULL, NULL); g_object_unref (layout); + + /* If the height is set, use that. */ + if (priv->height > 0.0) + simple->bounds.y2 = simple->bounds.y1 + priv->height; } @@ -574,6 +637,7 @@ goo_canvas_text_is_item_at (GooCanvasItemSimple *simple, { GooCanvasItemSimpleData *simple_data = simple->simple_data; GooCanvasText *text = (GooCanvasText*) simple; + GooCanvasTextPrivate *priv = goo_canvas_text_get_private (text); PangoLayout *layout; GooCanvasBounds bounds; PangoLayoutIter *iter; @@ -592,6 +656,10 @@ goo_canvas_text_is_item_at (GooCanvasItemSimple *simple, && goo_canvas_text_is_unpainted (simple_data->style)) return FALSE; + /* Check if the point is outside the clipped height. */ + if (priv->height > 0.0 && y > priv->height) + return FALSE; + layout = goo_canvas_text_create_layout (simple_data, text->text_data, text->layout_width, cr, &bounds, &origin_x, &origin_y); @@ -644,6 +712,7 @@ goo_canvas_text_paint (GooCanvasItemSimple *simple, const GooCanvasBounds *bounds) { GooCanvasText *text = (GooCanvasText*) simple; + GooCanvasTextPrivate *priv = goo_canvas_text_get_private (text); PangoLayout *layout; GooCanvasBounds layout_bounds; gdouble origin_x, origin_y; @@ -659,8 +728,18 @@ goo_canvas_text_paint (GooCanvasItemSimple *simple, text->layout_width, cr, &layout_bounds, &origin_x, &origin_y); + cairo_save (cr); + + if (priv->height > 0.0) + { + cairo_rectangle (cr, origin_x, origin_y, + text->layout_width, priv->height); + cairo_clip (cr); + } cairo_move_to (cr, origin_x, origin_y); pango_cairo_show_layout (cr, layout); + + cairo_restore (cr); g_object_unref (layout); } @@ -673,6 +752,7 @@ goo_canvas_text_get_requested_height (GooCanvasItem *item, GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item; GooCanvasItemSimpleData *simple_data = simple->simple_data; GooCanvasText *text = (GooCanvasText*) item; + GooCanvasTextPrivate *priv = goo_canvas_text_get_private (text); PangoLayout *layout; gdouble height; @@ -693,15 +773,23 @@ goo_canvas_text_get_requested_height (GooCanvasItem *item, if (simple_data->transform) text->layout_width /= simple_data->transform->xx; - /* Create layout with given width. */ - layout = goo_canvas_text_create_layout (simple_data, text->text_data, - text->layout_width, cr, - &simple->bounds, NULL, NULL); - g_object_unref (layout); + if (priv->height < 0.0) + { + /* Create layout with given width. */ + layout = goo_canvas_text_create_layout (simple_data, text->text_data, + text->layout_width, cr, + &simple->bounds, NULL, NULL); + g_object_unref (layout); + + height = simple->bounds.y2 - simple->bounds.y1; + } + else + { + height = priv->height; + } - /* Convert to the parent's coordinate space. As above, we only need to + /* Convert to the parent's coordinate space. As above, we only need to support a simple scale operation here. */ - height = simple->bounds.y2 - simple->bounds.y1; if (simple_data->transform) height *= simple_data->transform->yy; @@ -783,6 +871,8 @@ goo_canvas_text_class_init (GooCanvasTextClass *klass) GObjectClass *gobject_class = (GObjectClass*) klass; GooCanvasItemSimpleClass *simple_class = (GooCanvasItemSimpleClass*) klass; + g_type_class_add_private (gobject_class, sizeof (GooCanvasTextPrivate)); + gobject_class->finalize = goo_canvas_text_finalize; gobject_class->get_property = goo_canvas_text_get_property; @@ -843,6 +933,8 @@ goo_canvas_text_model_class_init (GooCanvasTextModelClass *klass) { GObjectClass *gobject_class = (GObjectClass*) klass; + g_type_class_add_private (gobject_class, sizeof (GooCanvasTextPrivate)); + gobject_class->finalize = goo_canvas_text_model_finalize; gobject_class->get_property = goo_canvas_text_model_get_property; @@ -855,10 +947,14 @@ goo_canvas_text_model_class_init (GooCanvasTextModelClass *klass) static void goo_canvas_text_model_init (GooCanvasTextModel *tmodel) { + GooCanvasTextPrivate *priv = GOO_CANVAS_TEXT_MODEL_GET_PRIVATE (tmodel); + tmodel->text_data.width = -1.0; tmodel->text_data.anchor = GTK_ANCHOR_NW; tmodel->text_data.ellipsize = PANGO_ELLIPSIZE_NONE; tmodel->text_data.wrap = PANGO_WRAP_WORD; + + priv->height = -1.0; } @@ -952,9 +1048,10 @@ goo_canvas_text_model_get_property (GObject *object, GParamSpec *pspec) { GooCanvasTextModel *tmodel = (GooCanvasTextModel*) object; + GooCanvasTextPrivate *priv = GOO_CANVAS_TEXT_MODEL_GET_PRIVATE (tmodel); - goo_canvas_text_get_common_property (object, &tmodel->text_data, prop_id, - value, pspec); + goo_canvas_text_get_common_property (object, &tmodel->text_data, priv, + prop_id, value, pspec); } @@ -965,9 +1062,10 @@ goo_canvas_text_model_set_property (GObject *object, GParamSpec *pspec) { GooCanvasTextModel *tmodel = (GooCanvasTextModel*) object; + GooCanvasTextPrivate *priv = GOO_CANVAS_TEXT_MODEL_GET_PRIVATE (tmodel); - goo_canvas_text_set_common_property (object, &tmodel->text_data, prop_id, - value, pspec); + goo_canvas_text_set_common_property (object, &tmodel->text_data, priv, + prop_id, value, pspec); g_signal_emit_by_name (tmodel, "changed", TRUE); } diff --git a/src/goocanvas/src/goocanvasutils.c b/src/goocanvas/src/goocanvasutils.c index 3b14387..ca07559 100644 --- a/src/goocanvas/src/goocanvasutils.c +++ b/src/goocanvas/src/goocanvasutils.c @@ -366,7 +366,7 @@ goo_canvas_line_dash_get_type (void) if (cairo_line_dash_type == 0) cairo_line_dash_type = g_boxed_type_register_static - ("GooCairoLineDash", + ("GooCanvasLineDash", (GBoxedCopyFunc) goo_canvas_line_dash_ref, (GBoxedFreeFunc) goo_canvas_line_dash_unref); @@ -1192,3 +1192,103 @@ goo_canvas_bounds_get_type (void) } +/* Converts red, green, blue and alpha doubles to an RGBA guint. */ +guint +goo_canvas_convert_colors_to_rgba (double red, + double green, + double blue, + double alpha) +{ + guint red_byte, green_byte, blue_byte, alpha_byte; + + red_byte = red * 256; + red_byte -= red_byte >> 8; + + green_byte = green * 256; + green_byte -= green_byte >> 8; + + blue_byte = blue * 256; + blue_byte -= blue_byte >> 8; + + alpha_byte = alpha * 256; + alpha_byte -= alpha_byte >> 8; + + return (red_byte << 24) + (green_byte << 16) + (blue_byte << 8) + alpha_byte; +} + + +void +goo_canvas_get_rgba_value_from_pattern (cairo_pattern_t *pattern, + GValue *value) +{ + double red, green, blue, alpha; + guint rgba = 0; + + if (pattern && cairo_pattern_get_type (pattern) == CAIRO_PATTERN_TYPE_SOLID) + { + cairo_pattern_get_rgba (pattern, &red, &green, &blue, &alpha); + rgba = goo_canvas_convert_colors_to_rgba (red, green, blue, alpha); + } + g_value_set_uint (value, rgba); +} + + +/* Sets a style property to the given pattern, taking ownership of it. */ +void +goo_canvas_set_style_property_from_pattern (GooCanvasStyle *style, + GQuark property_id, + cairo_pattern_t *pattern) +{ + GValue tmpval = { 0 }; + + g_value_init (&tmpval, GOO_TYPE_CAIRO_PATTERN); + g_value_take_boxed (&tmpval, pattern); + goo_canvas_style_set_property (style, property_id, &tmpval); + g_value_unset (&tmpval); +} + + +cairo_pattern_t* +goo_canvas_create_pattern_from_color_value (const GValue *value) +{ + GdkColor color = { 0, 0, 0, 0, }; + + if (g_value_get_string (value)) + gdk_color_parse (g_value_get_string (value), &color); + + return cairo_pattern_create_rgb (color.red / 65535.0, + color.green / 65535.0, + color.blue / 65535.0); +} + + +cairo_pattern_t* +goo_canvas_create_pattern_from_rgba_value (const GValue *value) +{ + guint rgba, red, green, blue, alpha; + + rgba = g_value_get_uint (value); + red = (rgba >> 24) & 0xFF; + green = (rgba >> 16) & 0xFF; + blue = (rgba >> 8) & 0xFF; + alpha = (rgba) & 0xFF; + + return cairo_pattern_create_rgba (red / 255.0, green / 255.0, + blue / 255.0, alpha / 255.0); +} + + +cairo_pattern_t* +goo_canvas_create_pattern_from_pixbuf_value (const GValue *value) +{ + GdkPixbuf *pixbuf; + cairo_surface_t *surface; + cairo_pattern_t *pattern; + + pixbuf = g_value_get_object (value); + surface = goo_canvas_cairo_surface_from_pixbuf (pixbuf); + pattern = cairo_pattern_create_for_surface (surface); + cairo_surface_destroy (surface); + cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT); + return pattern; +} diff --git a/src/goocanvas/src/goocanvasutils.h b/src/goocanvas/src/goocanvasutils.h index fa1ce93..7f158d4 100644 --- a/src/goocanvas/src/goocanvasutils.h +++ b/src/goocanvas/src/goocanvasutils.h @@ -86,7 +86,7 @@ typedef enum GOO_CANVAS_ITEM_HIDDEN = 0, GOO_CANVAS_ITEM_INVISIBLE = 1, GOO_CANVAS_ITEM_VISIBLE = 2, - GOO_CANVAS_ITEM_VISIBLE_ABOVE_THRESHOLD = 3, + GOO_CANVAS_ITEM_VISIBLE_ABOVE_THRESHOLD = 3 } GooCanvasItemVisibility; @@ -206,6 +206,92 @@ struct _GooCanvasLineDash double dash_offset; }; + +/* These are here so we can document the cairo type wrappers - don't use. */ +#if 0 +typedef cairo_antialias_t GooCairoAntialias; +typedef cairo_fill_rule_t GooCairoFillRule; +typedef cairo_hint_metrics_t GooCairoHintMetrics; +typedef cairo_line_cap_t GooCairoLineCap; +typedef cairo_line_join_t GooCairoLineJoin; +typedef cairo_operator_t GooCairoOperator; +typedef cairo_matrix_t GooCairoMatrix; +typedef cairo_pattern_t GooCairoPattern; +#endif + +/** + * GooCairoAntialias + * + * #GooCairoAntialias is simply a wrapper for the #cairo_antialias_t type, + * allowing it to be used for #GObject properties. + * + * See the #cairo_antialias_t documentation. + */ + +/** + * GooCairoFillRule + * + * #GooCairoFillRule is simply a wrapper for the #cairo_fill_rule_t type, + * allowing it to be used for #GObject properties. + * + * See the #cairo_fill_rule_t documentation. + */ + +/** + * GooCairoHintMetrics + * + * #GooCairoHintMetrics is simply a wrapper for the #cairo_hint_metrics_t type, + * allowing it to be used for #GObject properties. + * + * See the #cairo_hint_metrics_t documentation. + */ + +/** + * GooCairoLineCap + * + * #GooCairoLineCap is simply a wrapper for the #cairo_line_cap_t type, + * allowing it to be used for #GObject properties. + * + * See the #cairo_line_cap_t documentation. + */ + +/** + * GooCairoLineJoin + * + * #GooCairoLineJoin is simply a wrapper for the #cairo_line_join_t type, + * allowing it to be used for #GObject properties. + * + * See the #cairo_line_join_t documentation. + */ + +/** + * GooCairoOperator + * + * #GooCairoOperator is simply a wrapper for the #cairo_operator_t type, + * allowing it to be used for #GObject properties. + * + * See the #cairo_operator_t documentation. + */ + +/** + * GooCairoMatrix + * + * #GooCairoMatrix is simply a wrapper for the #cairo_matrix_t type, + * allowing it to be used for #GObject properties. + * + * See the #cairo_matrix_t documentation. + */ + +/** + * GooCairoPattern + * + * #GooCairoPattern is simply a wrapper for the #cairo_pattern_t type, + * allowing it to be used for #GObject properties. + * + * See the #cairo_pattern_t documentation. + */ + + #define GOO_TYPE_CANVAS_LINE_DASH (goo_canvas_line_dash_get_type ()) GType goo_canvas_line_dash_get_type (void) G_GNUC_CONST; GooCanvasLineDash* goo_canvas_line_dash_new (gint num_dashes, diff --git a/src/goocanvas/src/goocanvaswidget.c b/src/goocanvas/src/goocanvaswidget.c index 974892f..e8ddacc 100644 --- a/src/goocanvas/src/goocanvaswidget.c +++ b/src/goocanvas/src/goocanvaswidget.c @@ -12,6 +12,10 @@ * * GooCanvasWidget provides support for placing any GtkWidget in the canvas. * + * The #GooCanvasWidget:width and #GooCanvasWidget:height properties specify + * the widget's size. If either of them is -1, then the requested size of the + * widget is used instead, which is the default for both width and height. + * * Note that there are a number of limitations in the use of #GooCanvasWidget: * * <itemizedlist><listitem><para> @@ -26,6 +30,8 @@ * </para></listitem><listitem><para> * It doesn't have a model/view variant like the other standard items, * so it can only be used in a simple canvas without a model. + * </para></listitem><listitem><para> + * It can't be made a static item. * </para></listitem></itemizedlist> */ #include <config.h> @@ -153,6 +159,73 @@ goo_canvas_widget_new (GooCanvasItem *parent, } +/* Returns the anchor position, within the given width. */ +static gdouble +goo_canvas_widget_anchor_horizontal_pos (GtkAnchorType anchor, + gdouble width) +{ + switch(anchor) + { + case GTK_ANCHOR_N: + case GTK_ANCHOR_CENTER: + case GTK_ANCHOR_S: + return width / 2.0; + case GTK_ANCHOR_NE: + case GTK_ANCHOR_E: + case GTK_ANCHOR_SE: + return width; + default: + return 0.0; + } +} + + +/* Returns the anchor position, within the given height. */ +static gdouble +goo_canvas_widget_anchor_vertical_pos (GtkAnchorType anchor, + gdouble height) +{ + switch (anchor) + { + case GTK_ANCHOR_W: + case GTK_ANCHOR_CENTER: + case GTK_ANCHOR_E: + return height / 2.0; + case GTK_ANCHOR_SW: + case GTK_ANCHOR_S: + case GTK_ANCHOR_SE: + return height; + default: + return 0.0; + } +} + + +/* Returns the size to use for the widget, either the item's width & height + properties or the widget's own requested width & height. */ +static void +goo_canvas_widget_get_widget_size (GooCanvasWidget *witem, + gdouble *width, + gdouble *height) +{ + GtkRequisition requisition; + + if (witem->widget) + { + /* Get the widget's requested size, if we need it. */ + if (witem->width < 0 || witem->height < 0) + gtk_widget_size_request (witem->widget, &requisition); + + *width = witem->width < 0 ? requisition.width : witem->width; + *height = witem->height < 0 ? requisition.height : witem->height; + } + else + { + *width = *height = 0.0; + } +} + + static void goo_canvas_widget_set_widget (GooCanvasWidget *witem, GtkWidget *widget) @@ -347,53 +420,19 @@ goo_canvas_widget_update (GooCanvasItemSimple *simple, cairo_t *cr) { GooCanvasWidget *witem = (GooCanvasWidget*) simple; - GtkRequisition requisition; gdouble width, height; if (witem->widget) { - /* Compute the new bounds. */ - if (witem->width < 0 || witem->height < 0) - { - gtk_widget_size_request (witem->widget, &requisition); - } + goo_canvas_widget_get_widget_size (witem, &width, &height); simple->bounds.x1 = witem->x; simple->bounds.y1 = witem->y; - width = witem->width < 0 ? requisition.width : witem->width; - height = witem->height < 0 ? requisition.height : witem->height; - - switch (witem->anchor) - { - case GTK_ANCHOR_N: - case GTK_ANCHOR_CENTER: - case GTK_ANCHOR_S: - simple->bounds.x1 -= width / 2.0; - break; - case GTK_ANCHOR_NE: - case GTK_ANCHOR_E: - case GTK_ANCHOR_SE: - simple->bounds.x1 -= width; - break; - default: - break; - } - switch (witem->anchor) - { - case GTK_ANCHOR_W: - case GTK_ANCHOR_CENTER: - case GTK_ANCHOR_E: - simple->bounds.y1 -= height / 2.0; - break; - case GTK_ANCHOR_SW: - case GTK_ANCHOR_S: - case GTK_ANCHOR_SE: - simple->bounds.y1 -= height; - break; - default: - break; - } + simple->bounds.x1 -= + goo_canvas_widget_anchor_horizontal_pos (witem->anchor, width); + simple->bounds.y1 -= + goo_canvas_widget_anchor_vertical_pos (witem->anchor, height); simple->bounds.x2 = simple->bounds.x1 + width; simple->bounds.y2 = simple->bounds.y1 + height; @@ -544,6 +583,7 @@ goo_canvas_widget_class_init (GooCanvasWidgetClass *klass) G_MAXDOUBLE, -1.0, G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, PROP_ANCHOR, g_param_spec_enum ("anchor", _("Anchor"), @@ -555,5 +595,3 @@ goo_canvas_widget_class_init (GooCanvasWidgetClass *klass) g_object_class_override_property (gobject_class, PROP_VISIBILITY, "visibility"); } - - |