/* * GooCanvas. Copyright (C) 2005 Damon Chaplin. * Released under the GNU LGPL license. See COPYING for details. * * goocanvaspolylineview.c - view for polyline item. */ /** * SECTION:goocanvaspolylineview * @Title: GooCanvasPolylineView * @Short_Description: a view for a #GooCanvasPolyline item. * * #GooCanvasPolylineView represents a view of a #GooCanvasPolyline item. * * It implements the #GooCanvasItemView interface, so you can use the * #GooCanvasItemView functions such as goo_canvas_item_view_get_item() * and goo_canvas_item_view_get_bounds(). * * Applications do not normally need to create item views themselves, as * they are created automatically by #GooCanvasView when needed. * * To respond to events such as mouse clicks in the ellipse view you can * connect to one of the #GooCanvasItemView signals such as * #GooCanvasItemView::button-press-event. You can connect to these signals * when the view is created. (See goo_canvas_view_get_item_view() and * #GooCanvasView::item-view-created.) */ #include #include #include "goocanvasview.h" #include "goocanvaspolylineview.h" #include "goocanvaspolyline.h" void _goo_canvas_polyline_reconfigure_arrows (GooCanvasPolyline *polyline); static void canvas_item_view_interface_init (GooCanvasItemViewIface *iface); G_DEFINE_TYPE_WITH_CODE (GooCanvasPolylineView, goo_canvas_polyline_view, GOO_TYPE_CANVAS_ITEM_VIEW_SIMPLE, G_IMPLEMENT_INTERFACE (GOO_TYPE_CANVAS_ITEM_VIEW, canvas_item_view_interface_init)) static void goo_canvas_polyline_view_class_init (GooCanvasPolylineViewClass *klass) { } static void goo_canvas_polyline_view_init (GooCanvasPolylineView *polyline_view) { } /** * goo_canvas_polyline_view_new: * @canvas_view: the canvas view. * @parent_view: the parent view. * @polyline: the polyline item. * * Creates a new #GooCanvasPolylineView for the given #GooCanvasPolyline item. * * This is not normally used by application code, as the views are created * automatically by #GooCanvasView. * * Returns: a new #GooCanvasPolylineView. **/ GooCanvasItemView* goo_canvas_polyline_view_new (GooCanvasView *canvas_view, GooCanvasItemView *parent_view, GooCanvasPolyline *polyline) { GooCanvasItemViewSimple *view; view = g_object_new (GOO_TYPE_CANVAS_POLYLINE_VIEW, NULL); view->canvas_view = canvas_view; view->parent_view = parent_view; view->item = g_object_ref (polyline); goo_canvas_item_view_simple_setup_accessibility (view); g_signal_connect (polyline, "changed", G_CALLBACK (goo_canvas_item_view_simple_item_changed), view); return (GooCanvasItemView*) view; } static void goo_canvas_polyline_view_create_path (GooCanvasPolyline *polyline, cairo_t *cr) { GooCanvasPolylineArrowData *arrow = polyline->arrow_data; gint i; cairo_new_path (cr); /* If there is an arrow at the start of the polyline, we need to move the start of the line slightly to avoid drawing over the arrow tip. */ if (polyline->start_arrow) cairo_move_to (cr, arrow->line_start[0], arrow->line_start[1]); else cairo_move_to (cr, polyline->coords[0], polyline->coords[1]); if (polyline->end_arrow) { gint last_point = polyline->num_points - 1; if (!polyline->close_path) last_point--; for (i = 1; i <= last_point; i++) cairo_line_to (cr, polyline->coords[i * 2], polyline->coords[i * 2 + 1]); cairo_line_to (cr, arrow->line_end[0], arrow->line_end[1]); } else { for (i = 1; i < polyline->num_points; i++) cairo_line_to (cr, polyline->coords[i * 2], polyline->coords[i * 2 + 1]); if (polyline->close_path) cairo_close_path (cr); } } static void goo_canvas_polyline_view_create_start_arrow_path (GooCanvasPolyline *polyline, cairo_t *cr) { GooCanvasPolylineArrowData *arrow = polyline->arrow_data; gint i; cairo_new_path (cr); cairo_move_to (cr, arrow->start_arrow_coords[0], arrow->start_arrow_coords[1]); for (i = 1; i < NUM_ARROW_POINTS; i++) { cairo_line_to (cr, arrow->start_arrow_coords[i * 2], arrow->start_arrow_coords[i * 2 + 1]); } cairo_close_path (cr); } static void goo_canvas_polyline_view_create_end_arrow_path (GooCanvasPolyline *polyline, cairo_t *cr) { GooCanvasPolylineArrowData *arrow = polyline->arrow_data; gint i; cairo_new_path (cr); cairo_move_to (cr, arrow->end_arrow_coords[0], arrow->end_arrow_coords[1]); for (i = 1; i < NUM_ARROW_POINTS; i++) { cairo_line_to (cr, arrow->end_arrow_coords[i * 2], arrow->end_arrow_coords[i * 2 + 1]); } cairo_close_path (cr); } static GooCanvasItemView* goo_canvas_polyline_view_get_item_view_at (GooCanvasItemView *view, gdouble x, gdouble y, cairo_t *cr, gboolean is_pointer_event, gboolean parent_visible) { GooCanvasItemViewSimple *simple_view = (GooCanvasItemViewSimple*) view; GooCanvasItemSimple *simple = simple_view->item; GooCanvasPolyline *polyline = (GooCanvasPolyline*) simple; GooCanvasItemView *found_view = NULL; double user_x = x, user_y = y; GooCanvasPointerEvents pointer_events = GOO_CANVAS_EVENTS_ALL; GooCanvasStyle *style = simple->style; gboolean do_stroke = TRUE; if (simple_view->flags & GOO_CANVAS_ITEM_VIEW_NEED_UPDATE) goo_canvas_item_view_ensure_updated (view); if (polyline->num_points == 0) return NULL; /* Check if the item should receive events. */ if (is_pointer_event) { if (simple->pointer_events == GOO_CANVAS_EVENTS_NONE) return NULL; if (simple->pointer_events & GOO_CANVAS_EVENTS_VISIBLE_MASK && (!parent_visible || simple->visibility == GOO_CANVAS_ITEM_INVISIBLE || (simple->visibility == GOO_CANVAS_ITEM_VISIBLE_ABOVE_THRESHOLD && simple_view->canvas_view->scale < simple->visibility_threshold))) return NULL; pointer_events = simple->pointer_events; } cairo_save (cr); if (simple->transform) cairo_transform (cr, simple->transform); if (simple_view->transform) cairo_transform (cr, simple_view->transform); cairo_device_to_user (cr, &user_x, &user_y); goo_canvas_polyline_view_create_path (polyline, cr); if (goo_canvas_item_simple_check_in_path (simple, user_x, user_y, cr, pointer_events)) found_view = view; if (style && (style->mask & GOO_CANVAS_STYLE_STROKE_PATTERN) && style->stroke_pattern == NULL) do_stroke = FALSE; /* Check the arrows, if the polyline has them. */ if (!found_view && (polyline->start_arrow || polyline->end_arrow) && (pointer_events & GOO_CANVAS_EVENTS_STROKE_MASK) && (!(pointer_events & GOO_CANVAS_EVENTS_PAINTED_MASK) || do_stroke)) { /* We use the stroke pattern to match the style of the line. */ goo_canvas_item_simple_set_stroke_options (simple, cr); if (polyline->start_arrow) { goo_canvas_polyline_view_create_start_arrow_path (polyline, cr); if (cairo_in_fill (cr, user_x, user_y)) found_view = view; } if (!found_view && polyline->end_arrow) { goo_canvas_polyline_view_create_end_arrow_path (polyline, cr); if (cairo_in_fill (cr, user_x, user_y)) found_view = view; } } cairo_restore (cr); return found_view; } static void goo_canvas_polyline_view_compute_bounds (GooCanvasPolylineView *view, GooCanvasPolyline *polyline, cairo_t *cr, GooCanvasBounds *bounds) { GooCanvasItemSimple *simple = GOO_CANVAS_ITEM_SIMPLE (polyline); GooCanvasBounds tmp_bounds; goo_canvas_polyline_view_create_path (polyline, cr); goo_canvas_item_simple_get_path_bounds (simple, cr, bounds); /* Add on the arrows, if required. */ if (polyline->start_arrow || polyline->end_arrow) { /* We use the stroke pattern to match the style of the line. */ goo_canvas_item_simple_set_stroke_options (simple, cr); if (polyline->start_arrow) { goo_canvas_polyline_view_create_start_arrow_path (polyline, cr); cairo_fill_extents (cr, &tmp_bounds.x1, &tmp_bounds.y1, &tmp_bounds.x2, &tmp_bounds.y2); goo_canvas_item_simple_user_bounds_to_device (simple, cr, &tmp_bounds); bounds->x1 = MIN (bounds->x1, tmp_bounds.x1); bounds->y1 = MIN (bounds->y1, tmp_bounds.y1); bounds->x2 = MAX (bounds->x2, tmp_bounds.x2); bounds->y2 = MAX (bounds->y2, tmp_bounds.y2); } if (polyline->end_arrow) { goo_canvas_polyline_view_create_end_arrow_path (polyline, cr); cairo_fill_extents (cr, &tmp_bounds.x1, &tmp_bounds.y1, &tmp_bounds.x2, &tmp_bounds.y2); goo_canvas_item_simple_user_bounds_to_device (simple, cr, &tmp_bounds); bounds->x1 = MIN (bounds->x1, tmp_bounds.x1); bounds->y1 = MIN (bounds->y1, tmp_bounds.y1); bounds->x2 = MAX (bounds->x2, tmp_bounds.x2); bounds->y2 = MAX (bounds->y2, tmp_bounds.y2); } } } static void goo_canvas_polyline_view_update (GooCanvasItemView *view, gboolean entire_tree, cairo_t *cr, GooCanvasBounds *bounds) { GooCanvasItemViewSimple *simple_view = (GooCanvasItemViewSimple*) view; GooCanvasPolylineView *polyline_view = (GooCanvasPolylineView*) view; GooCanvasItemSimple *simple = simple_view->item; GooCanvasPolyline *polyline = (GooCanvasPolyline*) simple; if (entire_tree || (simple_view->flags & GOO_CANVAS_ITEM_VIEW_NEED_UPDATE)) { simple_view->flags &= ~GOO_CANVAS_ITEM_VIEW_NEED_UPDATE; if (polyline->reconfigure_arrows) _goo_canvas_polyline_reconfigure_arrows (polyline); cairo_save (cr); if (simple->transform) cairo_transform (cr, simple->transform); if (simple_view->transform) cairo_transform (cr, simple_view->transform); /* Request a redraw of the existing bounds. */ goo_canvas_view_request_redraw (simple_view->canvas_view, &simple_view->bounds); /* Compute the new bounds. */ goo_canvas_polyline_view_compute_bounds (polyline_view, polyline, cr, &simple_view->bounds); /* Request a redraw of the new bounds. */ goo_canvas_view_request_redraw (simple_view->canvas_view, &simple_view->bounds); cairo_restore (cr); } *bounds = simple_view->bounds; } static void goo_canvas_polyline_view_paint (GooCanvasItemView *view, cairo_t *cr, GooCanvasBounds *bounds, gdouble scale) { GooCanvasItemViewSimple *simple_view = (GooCanvasItemViewSimple*) view; GooCanvasItemSimple *simple = simple_view->item; GooCanvasPolyline *polyline = (GooCanvasPolyline*) simple; if (polyline->num_points == 0) return; /* Check if the item should be visible. */ if (simple->visibility == GOO_CANVAS_ITEM_INVISIBLE || (simple->visibility == GOO_CANVAS_ITEM_VISIBLE_ABOVE_THRESHOLD && scale < simple->visibility_threshold)) return; cairo_save (cr); if (simple->transform) cairo_transform (cr, simple->transform); if (simple_view->transform) cairo_transform (cr, simple_view->transform); goo_canvas_polyline_view_create_path (polyline, cr); goo_canvas_item_simple_paint_path (simple, cr); /* Paint the arrows, if required. */ if (polyline->start_arrow || polyline->end_arrow) { /* We use the stroke pattern to match the style of the line. */ goo_canvas_item_simple_set_stroke_options (simple, cr); if (polyline->start_arrow) { goo_canvas_polyline_view_create_start_arrow_path (polyline, cr); cairo_fill (cr); } if (polyline->end_arrow) { goo_canvas_polyline_view_create_end_arrow_path (polyline, cr); cairo_fill (cr); } } cairo_restore (cr); } static void canvas_item_view_interface_init (GooCanvasItemViewIface *iface) { iface->get_item_view_at = goo_canvas_polyline_view_get_item_view_at; iface->update = goo_canvas_polyline_view_update; iface->paint = goo_canvas_polyline_view_paint; }