Work around bug in Pango >= 1.44
[geda-gaf.git] / libgedacairo / edarenderer.c
blobf4f8c99112762389d3912f13bd6c352bf4a54a27
1 /* gEDA - GPL Electronic Design Automation
2 * libgedacairo - Rendering gEDA schematics with Cairo
3 * Copyright (C) 2010-2019 gEDA Contributors (see ChangeLog for details)
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #include <config.h>
22 #include <math.h>
23 #include <glib.h>
24 #include <glib-object.h>
25 #include <gdk/gdk.h>
26 #include <cairo.h>
27 #include <pango/pangocairo.h>
28 #include <gdk-pixbuf/gdk-pixbuf.h>
30 #include <libgeda/libgeda.h>
32 #include "edarenderer.h"
34 #include "edacairo.h"
35 #include "edapangorenderer.h"
37 /* We don't use gettext */
38 #define _(x) (x)
40 enum {
41 PROP_CAIRO_CONTEXT = 1,
42 PROP_PANGO_CONTEXT,
43 PROP_FONT_NAME,
44 PROP_COLOR_MAP,
45 PROP_OVERRIDE_COLOR,
46 PROP_GRIP_SIZE,
47 PROP_RENDER_FLAGS,
49 FLAG_HINTING = EDA_RENDERER_FLAG_HINTING,
50 FLAG_PICTURE_OUTLINE = EDA_RENDERER_FLAG_PICTURE_OUTLINE,
51 FLAG_TEXT_HIDDEN = EDA_RENDERER_FLAG_TEXT_HIDDEN,
52 FLAG_TEXT_OUTLINE = EDA_RENDERER_FLAG_TEXT_OUTLINE,
53 FLAG_TEXT_ORIGIN = EDA_RENDERER_FLAG_TEXT_ORIGIN,
55 GRIP_SQUARE,
56 GRIP_CIRCLE,
59 struct _EdaRendererPrivate
61 cairo_t *cr;
62 PangoContext *pc;
63 PangoLayout *pl;
64 EdaPangoRenderer *pr;
65 int pc_from_cr;
67 unsigned int flags;
68 gchar *font_name;
69 int override_color;
70 double grip_size;
72 GArray *color_map;
74 /* Cache of font metrics for different font sizes. */
75 GHashTable *metrics_cache;
78 static inline gboolean
79 EDA_RENDERER_CHECK_FLAG (EdaRenderer *r, int f) {
80 return r->priv->flags & f;
83 static inline void
84 EDA_RENDERER_SET_FLAG (EdaRenderer *r, int f, gboolean e) {
85 if (e) { r->priv->flags |= f; } else { r->priv->flags &= ~f; }
88 static inline unsigned int
89 EDA_RENDERER_CAIRO_FLAGS (EdaRenderer *r) {
90 return EDA_RENDERER_CHECK_FLAG (r, FLAG_HINTING) ? EDA_CAIRO_ENABLE_HINTS : 0;
92 static inline double
93 EDA_RENDERER_STROKE_WIDTH (EdaRenderer *r, double width) {
94 /* For now, the minimum line width possible is half the net width. */
95 return fmax (width, NET_WIDTH / 2);
98 #define DEFAULT_FONT_NAME "Arial"
99 #define GRIP_STROKE_COLOR SELECT_COLOR
100 #define GRIP_FILL_COLOR BACKGROUND_COLOR
101 #define TEXT_MARKER_SIZE 10
102 #define TEXT_MARKER_COLOR LOCK_COLOR
104 static GObject *eda_renderer_constructor (GType type,
105 guint n_construct_properties,
106 GObjectConstructParam *construct_params);
107 static void eda_renderer_finalize (GObject *object);
108 static void eda_renderer_dispose (GObject *object);
109 static void eda_renderer_set_property (GObject *object, guint property_id,
110 const GValue *value, GParamSpec *pspec);
111 static void eda_renderer_get_property (GObject *object, guint property_id,
112 GValue *value, GParamSpec *pspec);
113 static void eda_renderer_update_contexts (EdaRenderer *renderer, cairo_t *new_cr,
114 PangoContext *new_pc);
116 static void eda_renderer_set_color (EdaRenderer *renderer, int color);
117 static int eda_renderer_is_drawable (EdaRenderer *renderer, OBJECT *object);
118 static int eda_renderer_draw_hatch (EdaRenderer *renderer, OBJECT *object);
120 static void eda_renderer_default_draw (EdaRenderer *renderer, OBJECT *object);
121 static void eda_renderer_draw_list (EdaRenderer *renderer, GList *objects);
122 static void eda_renderer_draw_line (EdaRenderer *renderer, OBJECT *object);
123 static void eda_renderer_draw_pin (EdaRenderer *renderer, OBJECT *object);
124 static void eda_renderer_draw_net (EdaRenderer *renderer, OBJECT *object);
125 static void eda_renderer_draw_bus (EdaRenderer *renderer, OBJECT *object);
126 static void eda_renderer_draw_box (EdaRenderer *renderer, OBJECT *object);
127 static void eda_renderer_draw_arc (EdaRenderer *renderer, OBJECT *object);
128 static void eda_renderer_draw_circle (EdaRenderer *renderer, OBJECT *object);
129 static void eda_renderer_draw_path (EdaRenderer *renderer, OBJECT *object);
130 static void eda_renderer_draw_text (EdaRenderer *renderer, OBJECT *object);
131 static int eda_renderer_get_font_descent (EdaRenderer *renderer,
132 PangoFontDescription *desc);
133 static int eda_renderer_prepare_text (EdaRenderer *renderer, OBJECT *object);
134 static void eda_renderer_calc_text_position (EdaRenderer *renderer, OBJECT *object,
135 int descent, double *x, double *y);
136 static void eda_renderer_draw_picture (EdaRenderer *renderer, OBJECT *object);
137 static void eda_renderer_draw_complex (EdaRenderer *renderer, OBJECT *object);
139 static void eda_renderer_default_draw_grips (EdaRenderer *renderer, OBJECT *object);
140 /*static void eda_renderer_draw_grips_list (EdaRenderer *renderer, GList *objects);*/
141 static void eda_renderer_draw_grips_impl (EdaRenderer *renderer, int type, int n_grips, ...);
142 static void eda_renderer_draw_arc_grips (EdaRenderer *renderer, OBJECT *object);
143 static void eda_renderer_draw_path_grips (EdaRenderer *renderer, OBJECT *object);
144 static void eda_renderer_draw_text_grips (EdaRenderer *renderer, OBJECT *object);
146 static void eda_renderer_default_draw_cues (EdaRenderer *renderer, OBJECT *object);
147 static void eda_renderer_draw_cues_list (EdaRenderer *renderer, GList *objects);
148 static void eda_renderer_draw_end_cues (EdaRenderer *renderer, OBJECT *object,
149 int end);
150 static void eda_renderer_draw_mid_cues (EdaRenderer *renderer, OBJECT *object);
151 static void eda_renderer_draw_junction_cue (EdaRenderer *renderer, int x, int y,
152 int is_bus);
154 static int eda_renderer_default_get_user_bounds (EdaRenderer *renderer, OBJECT *object,
155 double *left, double *top,
156 double *right, double *bottom);
157 static int eda_renderer_get_text_user_bounds (EdaRenderer *renderer, OBJECT *object,
158 double *left, double *top,
159 double *right, double *bottom);
161 G_DEFINE_TYPE_WITH_PRIVATE (EdaRenderer, eda_renderer, G_TYPE_OBJECT)
163 GType
164 eda_renderer_flags_get_type ()
166 static const GFlagsValue values[] = {
167 {FLAG_HINTING, "hinting", _("Enable hinting")},
168 {FLAG_PICTURE_OUTLINE, "picture-outline", _("Picture outlines")},
169 {FLAG_TEXT_HIDDEN, "text-hidden", _("Hidden text")},
170 {FLAG_TEXT_OUTLINE, "text-outline", _("Text outlines")},
171 {FLAG_TEXT_ORIGIN, "text-origin", _("Text origins")},
172 {0, 0, 0},
174 static GType flags_type = 0;
175 if (flags_type == 0) {
176 flags_type = g_flags_register_static ("EdaRendererFlags",
177 values);
179 return flags_type;
182 static void
183 eda_renderer_class_init (EdaRendererClass *klass)
185 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
186 GParamFlags param_flags;
188 /* Register functions with base class */
189 gobject_class->constructor = eda_renderer_constructor;
190 gobject_class->finalize = eda_renderer_finalize;
191 gobject_class->dispose = eda_renderer_dispose;
192 gobject_class->set_property = eda_renderer_set_property;
193 gobject_class->get_property = eda_renderer_get_property;
195 /* Install default implementations of virtual public methods */
196 klass->draw = eda_renderer_default_draw;
197 klass->draw_grips = eda_renderer_default_draw_grips;
198 klass->draw_cues = eda_renderer_default_draw_cues;
199 klass->user_bounds = eda_renderer_default_get_user_bounds;
201 /* Install properties */
202 param_flags = (G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
203 G_PARAM_STATIC_BLURB);
205 g_object_class_install_property (gobject_class, PROP_CAIRO_CONTEXT,
206 g_param_spec_pointer ("cairo-context",
207 _("Cairo context"),
208 _("The Cairo context for rendering"),
209 param_flags));
210 g_object_class_install_property (gobject_class, PROP_PANGO_CONTEXT,
211 g_param_spec_pointer ("pango-context",
212 _("Pango context"),
213 _("The Pango context for text rendering"),
214 param_flags));
215 g_object_class_install_property (gobject_class, PROP_FONT_NAME,
216 g_param_spec_string ("font-name",
217 _("Font name"),
218 _("The name of the font to use for text rendering"),
219 DEFAULT_FONT_NAME,
220 param_flags));
221 g_object_class_install_property (gobject_class, PROP_COLOR_MAP,
222 g_param_spec_pointer ("color-map",
223 _("Color map"),
224 _("Map for determining colors from color indices"),
225 param_flags));
226 g_object_class_install_property (gobject_class, PROP_OVERRIDE_COLOR,
227 g_param_spec_int ("override-color",
228 _("Override color"),
229 _("Index of color to force used for all drawing."),
230 -1, G_MAXINT, -1,
231 param_flags));
232 g_object_class_install_property (gobject_class, PROP_GRIP_SIZE,
233 g_param_spec_double ("grip-size",
234 _("Grip size"),
235 _("Size in user coordinates to draw grips"),
236 0, G_MAXDOUBLE, 100,
237 param_flags));
238 g_object_class_install_property (gobject_class, PROP_RENDER_FLAGS,
239 g_param_spec_flags ("render-flags",
240 _("Rendering flags"),
241 _("Flags controlling rendering"),
242 EDA_TYPE_RENDERER_FLAGS,
243 FLAG_HINTING | FLAG_TEXT_ORIGIN,
244 param_flags));
247 static void
248 eda_renderer_init (EdaRenderer *renderer)
250 renderer->priv = eda_renderer_get_instance_private (renderer);
252 /* Set some sensible default options */
253 renderer->priv->font_name = g_strdup (DEFAULT_FONT_NAME);
254 renderer->priv->override_color = -1;
255 renderer->priv->grip_size = 100;
257 /* Font metrics are expensive to compute, so we need to cache them. */
258 renderer->priv->metrics_cache =
259 g_hash_table_new_full (g_int_hash, g_int_equal, g_free,
260 (GDestroyNotify) pango_font_metrics_unref);
263 static GObject *
264 eda_renderer_constructor (GType type,
265 guint n_construct_properties,
266 GObjectConstructParam *construct_params) {
267 GObject *object;
268 GObjectClass *parent_object_class;
270 parent_object_class = G_OBJECT_CLASS (eda_renderer_parent_class);
271 object = parent_object_class->constructor (type, n_construct_properties,
272 construct_params);
274 return object;
277 static void
278 eda_renderer_dispose (GObject *object)
280 EdaRenderer *renderer = (EdaRenderer *) object;
282 if (renderer->priv->pc != NULL) {
283 g_object_unref (renderer->priv->pc);
284 renderer->priv->pc = NULL;
287 if (renderer->priv->pl != NULL) {
288 g_object_unref (renderer->priv->pl);
289 renderer->priv->pl = NULL;
292 if (renderer->priv->pr != NULL) {
293 g_object_unref (renderer->priv->pr);
294 renderer->priv->pr = NULL;
297 /* Chain up to the parent class */
298 G_OBJECT_CLASS (eda_renderer_parent_class)->dispose (object);
301 static void
302 eda_renderer_finalize (GObject *object)
304 EdaRenderer *renderer = (EdaRenderer *) object;
306 g_hash_table_destroy (renderer->priv->metrics_cache);
307 renderer->priv->metrics_cache = NULL;
309 cairo_destroy (renderer->priv->cr);
310 renderer->priv->cr = NULL;
312 g_free (renderer->priv->font_name);
313 renderer->priv->font_name = NULL;
315 /* Chain up to the parent class */
316 G_OBJECT_CLASS (eda_renderer_parent_class)->finalize (object);
320 static void
321 eda_renderer_set_property (GObject *object, guint property_id,
322 const GValue *value, GParamSpec *pspec)
324 EdaRenderer *renderer = EDA_RENDERER (object);
326 switch (property_id) {
327 case PROP_CAIRO_CONTEXT:
328 eda_renderer_update_contexts (renderer,
329 (cairo_t *) g_value_get_pointer (value),
330 NULL);
331 break;
332 case PROP_PANGO_CONTEXT:
333 eda_renderer_update_contexts (renderer, NULL,
334 PANGO_CONTEXT (g_value_get_pointer (value)));
335 break;
336 case PROP_FONT_NAME:
337 if (renderer->priv->font_name != NULL)
338 g_free (renderer->priv->font_name);
339 renderer->priv->font_name = g_value_dup_string (value);
340 /* Clear font metrics cache */
341 g_hash_table_remove_all (renderer->priv->metrics_cache);
342 break;
343 case PROP_COLOR_MAP:
344 renderer->priv->color_map = g_value_get_pointer (value);
345 break;
346 case PROP_OVERRIDE_COLOR:
347 renderer->priv->override_color = g_value_get_int (value);
348 break;
349 case PROP_GRIP_SIZE:
350 renderer->priv->grip_size = g_value_get_double (value);
351 break;
352 case PROP_RENDER_FLAGS:
353 renderer->priv->flags = g_value_get_flags (value);
354 break;
355 default:
356 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
360 static void
361 eda_renderer_get_property (GObject *object, guint property_id,
362 GValue *value, GParamSpec *pspec)
364 EdaRenderer *renderer = EDA_RENDERER (object);
366 switch (property_id) {
367 case PROP_CAIRO_CONTEXT:
368 g_value_set_pointer (value, renderer->priv->cr);
369 break;
370 case PROP_PANGO_CONTEXT:
371 g_value_set_pointer (value, renderer->priv->pc);
372 break;
373 case PROP_FONT_NAME:
374 g_value_set_string (value, renderer->priv->font_name);
375 break;
376 case PROP_COLOR_MAP:
377 g_value_set_pointer (value, renderer->priv->color_map);
378 break;
379 case PROP_OVERRIDE_COLOR:
380 g_value_set_int (value, renderer->priv->override_color);
381 break;
382 case PROP_GRIP_SIZE:
383 g_value_set_double (value, renderer->priv->grip_size);
384 break;
385 case PROP_RENDER_FLAGS:
386 g_value_set_flags (value, renderer->priv->flags);
387 break;
388 default:
389 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
393 static void
394 eda_renderer_update_contexts (EdaRenderer *renderer, cairo_t *new_cr,
395 PangoContext *new_pc)
397 /* First figure out what's invalidated */
398 if (new_cr != NULL) {
399 cairo_destroy (renderer->priv->cr);
400 renderer->priv->cr = NULL;
401 if (renderer->priv->pr != NULL) {
402 g_object_unref (renderer->priv->pr);
403 renderer->priv->pr = NULL;
406 /* If the PangoContext was created from the previous Cairo
407 * context, it needs destroying too. */
408 if (renderer->priv->pc_from_cr) {
409 if (renderer->priv->pc != NULL) {
410 g_object_unref (renderer->priv->pc);
411 renderer->priv->pc = NULL;
413 if (renderer->priv->pl != NULL) {
414 g_object_unref (renderer->priv->pl);
415 renderer->priv->pl = NULL;
419 renderer->priv->cr = cairo_reference (new_cr);
422 if (new_pc != NULL) {
423 if (renderer->priv->pc != NULL) {
424 g_object_unref (G_OBJECT (renderer->priv->pc));
425 renderer->priv->pc = NULL;
427 if (renderer->priv->pl != NULL) {
428 g_object_unref (G_OBJECT (renderer->priv->pl));
429 renderer->priv->pl = NULL;
432 renderer->priv->pc = PANGO_CONTEXT (g_object_ref (G_OBJECT (new_pc)));
433 renderer->priv->pc_from_cr = 0;
436 /* Now recreate anything necessary */
437 if ((renderer->priv->pc == NULL) && (renderer->priv->cr != NULL)) {
438 cairo_save (renderer->priv->cr);
439 cairo_identity_matrix (renderer->priv->cr);
440 renderer->priv->pc = pango_cairo_create_context (renderer->priv->cr);
441 cairo_restore (renderer->priv->cr);
442 renderer->priv->pc_from_cr = 1;
445 if ((renderer->priv->pl == NULL) && (renderer->priv->pc != NULL)) {
446 renderer->priv->pl = pango_layout_new (renderer->priv->pc);
449 if ((renderer->priv->pr == NULL) && (renderer->priv->cr != NULL)) {
450 renderer->priv->pr =
451 (EdaPangoRenderer *) eda_pango_renderer_new (renderer->priv->cr);
455 /* ================================================================
456 * OBJECT DRAWING
457 * ================================================================ */
459 static void
460 eda_renderer_draw_list (EdaRenderer *renderer, GList *objects)
462 GList *iter;
464 for (iter = objects; iter != NULL; iter = g_list_next (iter)) {
465 eda_renderer_draw (renderer, (OBJECT *) iter->data);
469 void
470 eda_renderer_draw (EdaRenderer *renderer, OBJECT *object)
472 g_return_if_fail (EDA_IS_RENDERER(renderer));
474 EDA_RENDERER_GET_CLASS (renderer)->draw (renderer, object);
477 static void
478 eda_renderer_default_draw (EdaRenderer *renderer, OBJECT *object)
480 void (*draw_func)(EdaRenderer *, OBJECT *);
482 g_return_if_fail (object != NULL);
483 g_return_if_fail (renderer->priv->cr != NULL);
484 g_return_if_fail (renderer->priv->pl != NULL);
485 g_return_if_fail (renderer->priv->color_map != NULL);
487 if (!eda_renderer_is_drawable (renderer, object)) return;
489 switch (object->type) {
490 case OBJ_LINE: draw_func = eda_renderer_draw_line; break;
491 case OBJ_NET: draw_func = eda_renderer_draw_net; break;
492 case OBJ_BUS: draw_func = eda_renderer_draw_bus; break;
493 case OBJ_PIN: draw_func = eda_renderer_draw_pin; break;
494 case OBJ_BOX: draw_func = eda_renderer_draw_box; break;
495 case OBJ_ARC: draw_func = eda_renderer_draw_arc; break;
496 case OBJ_CIRCLE: draw_func = eda_renderer_draw_circle; break;
497 case OBJ_PATH: draw_func = eda_renderer_draw_path; break;
498 case OBJ_TEXT: draw_func = eda_renderer_draw_text; break;
499 case OBJ_PICTURE: draw_func = eda_renderer_draw_picture; break;
501 case OBJ_COMPLEX:
502 case OBJ_PLACEHOLDER: draw_func = eda_renderer_draw_complex; break;
504 default:
505 g_return_if_reached ();
508 eda_renderer_set_color (renderer, object->color);
509 draw_func (renderer, object);
512 static void
513 eda_renderer_set_color (EdaRenderer *renderer, int color)
515 if (renderer->priv->override_color != -1) {
516 color = renderer->priv->override_color;
518 eda_cairo_set_source_color (renderer->priv->cr, color,
519 renderer->priv->color_map);
522 static int
523 eda_renderer_is_drawable_color (EdaRenderer *renderer, int color,
524 int use_override)
526 GArray *map = renderer->priv->color_map;
527 /* Check for override color */
528 if ((renderer->priv->override_color >= 0) && use_override) {
529 color = renderer->priv->override_color;
531 /* If color index out of color map bounds, don't draw */
532 g_return_val_if_fail ((map != NULL), FALSE);
533 g_return_val_if_fail ((color >= 0) || (color < map->len), FALSE);
535 /* Otherwise, return enabled flag of object's color */
536 return (&g_array_index (map, COLOR, color))->enabled;
539 static int
540 eda_renderer_is_drawable (EdaRenderer *renderer, OBJECT *object)
542 int color = object->color;
543 /* Always attempt to draw complex objects */
544 if ((object->type == OBJ_COMPLEX) || (object->type == OBJ_PLACEHOLDER)) {
545 return TRUE;
547 return eda_renderer_is_drawable_color (renderer, color, TRUE);
550 static int
551 eda_renderer_draw_hatch (EdaRenderer *renderer, OBJECT *object)
553 void (*hatch_func)(void *, gint, gint, GArray *);
554 void *hatch_data;
555 GArray *fill_lines;
556 int i;
558 /* Horrible horrible hacks! */
559 switch (object->type) {
560 case OBJ_BOX:
561 hatch_func = (void *) m_hatch_box;
562 hatch_data = (void (*)(void *, gint, gint, GArray *)) object->box;
563 break;
564 case OBJ_CIRCLE:
565 hatch_func = (void *) m_hatch_circle;
566 hatch_data = (void (*)(void *, gint, gint, GArray *)) object->circle;
567 break;
568 case OBJ_PATH:
569 hatch_func = (void *) m_hatch_path;
570 hatch_data = (void (*)(void *, gint, gint, GArray *)) object->path;
571 break;
572 default:
573 g_return_val_if_reached (FALSE);
576 /* Handle solid and hollow fill types */
577 switch (object->fill_type) {
578 case FILLING_MESH:
579 case FILLING_HATCH:
580 break;
581 case FILLING_FILL:
582 return TRUE;
583 case FILLING_HOLLOW:
584 return FALSE;
585 default:
586 g_return_val_if_reached (FALSE);
589 /* Handle mesh and hatch fill types */
590 fill_lines = g_array_new (FALSE, FALSE, sizeof (LINE));
591 switch (object->fill_type) {
592 case FILLING_MESH:
593 hatch_func (hatch_data, object->fill_angle2, object->fill_pitch2, fill_lines);
594 /* Intentionally fall through */
595 case FILLING_HATCH:
596 hatch_func (hatch_data, object->fill_angle1, object->fill_pitch1, fill_lines);
597 break;
598 default:
599 break;
602 /* Draw fill pattern */
603 for (i = 0; i < fill_lines->len; i++) {
604 LINE *line = &g_array_index (fill_lines, LINE, i);
605 eda_cairo_line (renderer->priv->cr, EDA_RENDERER_CAIRO_FLAGS (renderer),
606 END_NONE, object->fill_width,
607 line->x[0], line->y[0], line->x[1], line->y[1]);
609 eda_cairo_stroke (renderer->priv->cr, EDA_RENDERER_CAIRO_FLAGS (renderer),
610 TYPE_SOLID, END_NONE,
611 EDA_RENDERER_STROKE_WIDTH (renderer, object->fill_width),
612 -1, -1);
614 g_array_free (fill_lines, TRUE);
615 return FALSE;
618 static void
619 eda_renderer_draw_complex (EdaRenderer *renderer, OBJECT *object)
621 /* Recurse */
622 eda_renderer_draw_list (renderer, object->complex->prim_objs);
625 static void
626 eda_renderer_draw_line (EdaRenderer *renderer, OBJECT *object)
628 eda_cairo_line (renderer->priv->cr, EDA_RENDERER_CAIRO_FLAGS (renderer),
629 object->line_end,
630 object->line_width,
631 object->line->x[0], object->line->y[0],
632 object->line->x[1], object->line->y[1]);
633 eda_cairo_stroke (renderer->priv->cr, EDA_RENDERER_CAIRO_FLAGS (renderer),
634 object->line_type,
635 object->line_end,
636 EDA_RENDERER_STROKE_WIDTH (renderer, object->line_width),
637 object->line_length,
638 object->line_space);
641 static void
642 eda_renderer_draw_net (EdaRenderer *renderer, OBJECT *object)
644 eda_cairo_line (renderer->priv->cr, EDA_RENDERER_CAIRO_FLAGS (renderer),
645 END_SQUARE, NET_WIDTH,
646 object->line->x[0], object->line->y[0],
647 object->line->x[1], object->line->y[1]);
648 eda_cairo_stroke (renderer->priv->cr, EDA_RENDERER_CAIRO_FLAGS (renderer),
649 TYPE_SOLID, END_SQUARE,
650 EDA_RENDERER_STROKE_WIDTH (renderer, NET_WIDTH),
651 -1, -1);
654 static void
655 eda_renderer_draw_bus (EdaRenderer *renderer, OBJECT *object)
657 eda_cairo_line (renderer->priv->cr, EDA_RENDERER_CAIRO_FLAGS (renderer),
658 END_SQUARE, BUS_WIDTH,
659 object->line->x[0], object->line->y[0],
660 object->line->x[1], object->line->y[1]);
661 eda_cairo_stroke (renderer->priv->cr, EDA_RENDERER_CAIRO_FLAGS (renderer),
662 TYPE_SOLID, END_SQUARE,
663 EDA_RENDERER_STROKE_WIDTH (renderer, BUS_WIDTH),
664 -1, -1);
667 static void
668 eda_renderer_draw_pin (EdaRenderer *renderer, OBJECT *object)
670 int width = 0;
671 switch (object->pin_type) {
672 case PIN_TYPE_NET:
673 width = PIN_WIDTH_NET;
674 break;
675 case PIN_TYPE_BUS:
676 width = PIN_WIDTH_BUS;
677 break;
678 default:
679 g_return_if_reached ();
682 eda_cairo_line (renderer->priv->cr, EDA_RENDERER_CAIRO_FLAGS (renderer),
683 END_SQUARE, width,
684 object->line->x[0], object->line->y[0],
685 object->line->x[1], object->line->y[1]);
686 eda_cairo_stroke (renderer->priv->cr, EDA_RENDERER_CAIRO_FLAGS (renderer),
687 TYPE_SOLID, END_SQUARE,
688 EDA_RENDERER_STROKE_WIDTH (renderer, width),
689 -1, -1);
692 static void
693 eda_renderer_draw_box (EdaRenderer *renderer, OBJECT *object)
695 int fill_solid = FALSE;
697 /* Hatch box */
698 fill_solid = eda_renderer_draw_hatch (renderer, object);
700 /* Draw outline of box */
701 eda_cairo_box (renderer->priv->cr, EDA_RENDERER_CAIRO_FLAGS (renderer),
702 object->line_width,
703 object->box->lower_x, object->box->lower_y,
704 object->box->upper_x, object->box->upper_y);
705 if (fill_solid) cairo_fill_preserve (renderer->priv->cr);
706 eda_cairo_stroke (renderer->priv->cr, EDA_RENDERER_CAIRO_FLAGS (renderer),
707 object->line_type, object->line_end,
708 EDA_RENDERER_STROKE_WIDTH (renderer, object->line_width),
709 object->line_length, object->line_space);
712 static void
713 eda_renderer_draw_arc (EdaRenderer *renderer, OBJECT *object)
715 eda_cairo_arc (renderer->priv->cr, EDA_RENDERER_CAIRO_FLAGS (renderer),
716 object->line_width,
717 object->arc->x,
718 object->arc->y,
719 object->arc->radius,
720 object->arc->start_angle,
721 object->arc->sweep_angle);
723 eda_cairo_stroke (renderer->priv->cr, EDA_RENDERER_CAIRO_FLAGS (renderer),
724 object->line_type,
725 object->line_end,
726 EDA_RENDERER_STROKE_WIDTH (renderer, object->line_width),
727 object->line_length,
728 object->line_space);
731 static void
732 eda_renderer_draw_circle (EdaRenderer *renderer, OBJECT *object)
734 int fill_solid = FALSE;
736 /* Hatch circle */
737 fill_solid = eda_renderer_draw_hatch (renderer, object);
739 /* Draw outline of circle */
740 eda_cairo_arc (renderer->priv->cr, EDA_RENDERER_CAIRO_FLAGS (renderer),
741 object->line_width,
742 object->circle->center_x, object->circle->center_y,
743 object->circle->radius, 0, 360);
744 if (fill_solid) cairo_fill_preserve (renderer->priv->cr);
745 eda_cairo_stroke (renderer->priv->cr, EDA_RENDERER_CAIRO_FLAGS (renderer),
746 object->line_type, object->line_end,
747 EDA_RENDERER_STROKE_WIDTH (renderer, object->line_width),
748 object->line_length, object->line_space);
751 static void
752 eda_renderer_draw_path (EdaRenderer *renderer, OBJECT *object)
754 int fill_solid = FALSE;
756 /* Hatch path */
757 fill_solid = eda_renderer_draw_hatch (renderer, object);
759 /* Draw outline of path */
760 eda_cairo_path (renderer->priv->cr, EDA_RENDERER_CAIRO_FLAGS (renderer),
761 object->line_width, object->path->num_sections,
762 object->path->sections);
764 if (fill_solid) cairo_fill_preserve (renderer->priv->cr);
765 eda_cairo_stroke (renderer->priv->cr, EDA_RENDERER_CAIRO_FLAGS (renderer),
766 object->line_type, object->line_end,
767 EDA_RENDERER_STROKE_WIDTH (renderer, object->line_width),
768 object->line_length, object->line_space);
771 static void
772 eda_renderer_draw_text (EdaRenderer *renderer, OBJECT *object)
774 double x, y;
775 double dummy = 0, small_dist = TEXT_MARKER_SIZE;
777 /* First check if this is hidden text. */
778 if (object->visibility == INVISIBLE
779 && !EDA_RENDERER_CHECK_FLAG (renderer, FLAG_TEXT_HIDDEN)) {
780 return;
783 /* Also, check that we actually need to display a string */
784 if (object->text->disp_string == NULL)
785 return;
787 /* If text outline mode is selected, draw an outline */
788 if (EDA_RENDERER_CHECK_FLAG (renderer, FLAG_TEXT_OUTLINE)) {
789 eda_cairo_box (renderer->priv->cr, EDA_RENDERER_CAIRO_FLAGS (renderer),
790 0, object->w_left, object->w_bottom,
791 object->w_right, object->w_top);
792 eda_cairo_stroke (renderer->priv->cr, EDA_RENDERER_CAIRO_FLAGS (renderer),
793 TYPE_SOLID, END_SQUARE,
794 EDA_RENDERER_STROKE_WIDTH (renderer, 0),
795 -1, -1);
796 return;
799 /* Otherwise, actually draw the text */
800 cairo_save (renderer->priv->cr);
801 if (eda_renderer_prepare_text (renderer, object)) {
802 eda_pango_renderer_show_layout (renderer->priv->pr, renderer->priv->pl,
803 0, 0);
804 cairo_restore (renderer->priv->cr);
805 } else {
806 cairo_restore (renderer->priv->cr);
807 return;
810 /* If the text is flagged invisible, and we're showing hidden text,
811 * draw a little "I". */
812 if (object->visibility != INVISIBLE)
813 return;
815 /* Check that color is enabled */
816 if (!eda_renderer_is_drawable_color (renderer, TEXT_MARKER_COLOR, FALSE))
817 return;
819 /* If the text marker is too tiny, don't draw it. */
820 if (EDA_RENDERER_CHECK_FLAG (renderer, FLAG_HINTING)) {
821 cairo_user_to_device_distance (renderer->priv->cr, &small_dist, &dummy);
822 if (small_dist < 1) return;
825 eda_renderer_set_color (renderer, TEXT_MARKER_COLOR);
827 /* Centre of marker is just below and to the right of the text
828 * object's origin. */
829 x = object->text->x + 2 * TEXT_MARKER_SIZE;
830 y = object->text->y - 2 * TEXT_MARKER_SIZE;
832 eda_cairo_line (renderer->priv->cr, EDA_RENDERER_CAIRO_FLAGS (renderer),
833 END_NONE, 0, /* Top */
834 x - TEXT_MARKER_SIZE, y + TEXT_MARKER_SIZE,
835 x + TEXT_MARKER_SIZE, y + TEXT_MARKER_SIZE);
836 eda_cairo_line (renderer->priv->cr, EDA_RENDERER_CAIRO_FLAGS (renderer),
837 END_NONE, 0, /* Vertical */
838 x, y + TEXT_MARKER_SIZE,
839 x, y - TEXT_MARKER_SIZE);
840 eda_cairo_line (renderer->priv->cr, EDA_RENDERER_CAIRO_FLAGS (renderer),
841 END_NONE, 0, /* Bottom */
842 x - TEXT_MARKER_SIZE, y - TEXT_MARKER_SIZE,
843 x + TEXT_MARKER_SIZE, y - TEXT_MARKER_SIZE);
844 eda_cairo_stroke (renderer->priv->cr, EDA_RENDERER_CAIRO_FLAGS (renderer),
845 TYPE_SOLID, END_NONE,
846 EDA_RENDERER_STROKE_WIDTH (renderer, 0),
847 -1, -1);
850 static int
851 eda_renderer_get_font_descent (EdaRenderer *renderer,
852 PangoFontDescription *desc)
854 PangoFontMetrics *metrics;
855 int size = pango_font_description_get_size (desc);
856 int *key;
858 /* Lookup the font size in the metrics cache, and get the metrics
859 * from there if available. Otherwise, calculate the metrics and
860 * cache them. */
861 metrics = g_hash_table_lookup (renderer->priv->metrics_cache, &size);
862 if (metrics == NULL) {
863 metrics = pango_context_get_metrics (renderer->priv->pc, desc, NULL);
864 key = g_new (int, 1);
865 *key = size;
866 g_hash_table_insert (renderer->priv->metrics_cache, key, metrics);
868 return pango_font_metrics_get_descent (metrics);
871 static int
872 eda_renderer_prepare_text (EdaRenderer *renderer, OBJECT *object)
874 double points_size, dx, dy;
875 int size, descent;
876 char *draw_string;
877 cairo_font_options_t *options;
878 PangoFontDescription *desc;
879 PangoAttrList *attrs;
881 points_size = o_text_get_font_size_in_points (object); /* FIXME */
882 size = lrint (points_size * PANGO_SCALE);
884 /* Set hinting as appropriate */
885 options = cairo_font_options_create ();
886 cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF);
887 if (EDA_RENDERER_CHECK_FLAG (renderer, FLAG_HINTING)) {
888 cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_MEDIUM);
889 } else {
890 cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
892 pango_cairo_context_set_font_options (renderer->priv->pc, options);
893 cairo_font_options_destroy (options);
895 pango_cairo_context_set_resolution (renderer->priv->pc, 1000);
897 /* Set font name and size, and obtain descent metric */
898 desc = pango_font_description_from_string (renderer->priv->font_name);
899 pango_font_description_set_size (desc, size);
900 pango_layout_set_font_description (renderer->priv->pl, desc);
901 descent = eda_renderer_get_font_descent (renderer, desc);
902 pango_font_description_free (desc);
904 /* Extract text to display and Pango text attributes, and then set
905 * up layout. */
906 if (!eda_pango_parse_overbars (object->text->disp_string, -1,
907 &attrs, &draw_string)) {
908 return FALSE;
910 pango_layout_set_text (renderer->priv->pl, draw_string, -1);
911 pango_layout_set_attributes (renderer->priv->pl, attrs);
912 g_free (draw_string);
913 pango_attr_list_unref (attrs);
915 /* Calculate text position. */
916 eda_renderer_calc_text_position (renderer, object, descent, &dx, &dy);
918 cairo_translate (renderer->priv->cr, object->text->x, object->text->y);
920 /* Special case turns upside-down text back upright */
921 if (object->text->angle != 180) {
922 cairo_rotate (renderer->priv->cr,
923 M_PI * object->text->angle / 180.);
926 cairo_scale (renderer->priv->cr, 1, -1);
927 cairo_translate (renderer->priv->cr, dx, dy);
929 if (EDA_RENDERER_CHECK_FLAG (renderer, FLAG_HINTING)) {
930 /* NB: Shift the position by 0.5px to match the hinting applied to single
931 * pixel wide lines. This means the text will sit correctly on top of
932 * the grid lines, and ensures consistency with other lines when the
933 * page view is zoomed out. */
934 dx = 0.5; dy = 0.5;
935 cairo_device_to_user_distance (renderer->priv->cr, &dx, &dy);
936 cairo_translate (renderer->priv->cr, dx, dy);
939 cairo_save (renderer->priv->cr);
940 cairo_identity_matrix (renderer->priv->cr);
941 pango_cairo_update_layout (renderer->priv->cr, renderer->priv->pl);
942 cairo_restore (renderer->priv->cr);
943 return TRUE;
946 /* Calculate position to draw text relative to text origin marker, in
947 * world coordinates. */
948 static void
949 eda_renderer_calc_text_position (EdaRenderer *renderer, OBJECT *object,
950 int descent, double *x, double *y)
952 PangoRectangle inked_rect, logical_rect;
953 double temp;
954 double y_lower, y_middle, y_upper;
955 double x_left, x_middle, x_right;
957 pango_layout_get_extents (renderer->priv->pl,
958 &inked_rect, &logical_rect);
960 x_left = 0;
961 x_middle = -logical_rect.width / 2.0;
962 x_right = -logical_rect.width;
964 /*! \note Ideally, we would be using just font / logical metrics for vertical
965 * alignment, however this way seems to be more backward compatible
966 * with the old gschem rendering.
968 * Lower alignment is at the baseline of the bottom text line, whereas
969 * middle and upper alignment is based upon the inked extents of the
970 * entire text block.
972 y_upper = -inked_rect.y; /* Top of inked extents */
973 y_middle = y_upper - inked_rect.height / 2.; /* Middle of inked extents */
974 y_lower = descent - logical_rect.height; /* Baseline of bottom line */
976 /* Special case flips attachment point to opposite corner when
977 * the text is rotated to 180 degrees, since the drawing code
978 * does not rotate the text to be shown upside down.
980 if (object->text->angle == 180) {
981 temp = y_lower; y_lower = y_upper; y_upper = temp;
982 temp = x_left; x_left = x_right; x_right = temp;
985 switch (object->text->alignment) {
986 default:
987 /* Fall through to LOWER_left case */
988 case LOWER_LEFT: *y = y_lower; *x = x_left; break;
989 case MIDDLE_LEFT: *y = y_middle; *x = x_left; break;
990 case UPPER_LEFT: *y = y_upper; *x = x_left; break;
991 case LOWER_MIDDLE: *y = y_lower; *x = x_middle; break;
992 case MIDDLE_MIDDLE: *y = y_middle; *x = x_middle; break;
993 case UPPER_MIDDLE: *y = y_upper; *x = x_middle; break;
994 case LOWER_RIGHT: *y = y_lower; *x = x_right; break;
995 case MIDDLE_RIGHT: *y = y_middle; *x = x_right; break;
996 case UPPER_RIGHT: *y = y_upper; *x = x_right; break;
999 *x /= PANGO_SCALE;
1000 *y /= PANGO_SCALE;
1003 static void
1004 eda_renderer_draw_picture (EdaRenderer *renderer, OBJECT *object)
1006 int swap_wh;
1007 double orig_width, orig_height;
1008 GdkPixbuf *pixbuf;
1010 /* Get a pixbuf. If image doesn't exist, libgeda should
1011 * provide a fallback image. */
1012 pixbuf = g_object_ref (object->picture->pixbuf);
1014 /* If no pixbuf was found, fall back to drawing an outline */
1015 if (pixbuf == NULL || EDA_RENDERER_CHECK_FLAG (renderer,
1016 FLAG_PICTURE_OUTLINE)) {
1017 eda_cairo_box (renderer->priv->cr, EDA_RENDERER_CAIRO_FLAGS (renderer),
1019 object->picture->lower_x, object->picture->lower_y,
1020 object->picture->upper_x, object->picture->upper_y);
1021 eda_cairo_stroke (renderer->priv->cr, EDA_RENDERER_CAIRO_FLAGS (renderer),
1022 TYPE_SOLID, END_SQUARE,
1023 EDA_RENDERER_STROKE_WIDTH (renderer, 0),
1024 -1, -1);
1025 return;
1028 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
1030 cairo_save (renderer->priv->cr);
1032 swap_wh = ((object->picture->angle == 90) || (object->picture->angle == 270));
1033 orig_width = swap_wh ? gdk_pixbuf_get_height (object->picture->pixbuf)
1034 : gdk_pixbuf_get_width (object->picture->pixbuf);
1035 orig_height = swap_wh ? gdk_pixbuf_get_width (object->picture->pixbuf)
1036 : gdk_pixbuf_get_height (object->picture->pixbuf);
1038 cairo_translate (renderer->priv->cr,
1039 object->picture->upper_x, object->picture->upper_y);
1040 cairo_scale (renderer->priv->cr,
1041 abs (object->picture->upper_x - object->picture->lower_x) / orig_width,
1042 - abs (object->picture->upper_y - object->picture->lower_y) / orig_height);
1044 /* Evil magic translates picture origin to the right position for a given rotation */
1045 switch (object->picture->angle) {
1046 case 0: break;
1047 case 90: cairo_translate (renderer->priv->cr, 0, orig_height); break;
1048 case 180: cairo_translate (renderer->priv->cr, orig_width, orig_height); break;
1049 case 270: cairo_translate (renderer->priv->cr, orig_width, 0 ); break;
1052 cairo_rotate (renderer->priv->cr, -object->picture->angle * M_PI / 180.);
1053 if (object->picture->mirrored) {
1054 cairo_translate (renderer->priv->cr, gdk_pixbuf_get_width (pixbuf), 0);
1055 cairo_scale (renderer->priv->cr, -1, 1);
1058 gdk_cairo_set_source_pixbuf (renderer->priv->cr,
1059 object->picture->pixbuf, 0,0);
1060 cairo_rectangle (renderer->priv->cr, 0, 0,
1061 gdk_pixbuf_get_width (object->picture->pixbuf),
1062 gdk_pixbuf_get_height (object->picture->pixbuf));
1064 cairo_clip (renderer->priv->cr);
1065 cairo_paint (renderer->priv->cr);
1067 cairo_restore (renderer->priv->cr);
1068 g_object_unref (pixbuf);
1071 /* ================================================================
1072 * GRIP DRAWING
1073 * ================================================================ */
1076 static void
1077 eda_renderer_draw_grips_list (EdaRenderer *renderer, GList *objects)
1079 GList *iter;
1080 for (iter = objects; iter != NULL; iter = g_list_next (iter)) {
1081 eda_renderer_draw_grips (renderer, (OBJECT *) iter->data);
1086 void
1087 eda_renderer_draw_grips (EdaRenderer *renderer, OBJECT *object)
1089 g_return_if_fail (EDA_IS_RENDERER (renderer));
1091 EDA_RENDERER_GET_CLASS (renderer)->draw_grips (renderer, object);
1094 static void
1095 eda_renderer_default_draw_grips (EdaRenderer *renderer, OBJECT *object)
1097 g_return_if_fail (object != NULL);
1098 g_return_if_fail (EDA_IS_RENDERER (renderer));
1099 g_return_if_fail (renderer->priv->cr != NULL);
1101 if (!eda_renderer_is_drawable (renderer, object))
1102 return;
1103 if (!eda_renderer_is_drawable_color (renderer, GRIP_STROKE_COLOR, FALSE))
1104 return;
1106 switch (object->type) {
1107 case OBJ_LINE:
1108 case OBJ_NET:
1109 case OBJ_BUS:
1110 case OBJ_PIN:
1111 eda_renderer_draw_grips_impl (renderer, GRIP_SQUARE, 2,
1112 object->line->x[0], object->line->y[0],
1113 object->line->x[1], object->line->y[1]);
1114 break;
1115 case OBJ_BOX:
1116 eda_renderer_draw_grips_impl (renderer, GRIP_SQUARE, 4,
1117 object->box->upper_x, object->box->upper_y,
1118 object->box->lower_x, object->box->upper_y,
1119 object->box->upper_x, object->box->lower_y,
1120 object->box->lower_x, object->box->lower_y);
1121 break;
1122 case OBJ_ARC:
1123 eda_renderer_draw_arc_grips (renderer, object);
1124 break;
1125 case OBJ_CIRCLE:
1126 /* Grip at bottom right of containing square */
1127 eda_renderer_draw_grips_impl (renderer, GRIP_SQUARE, 1,
1128 object->circle->center_x + object->circle->radius,
1129 object->circle->center_y - object->circle->radius);
1130 break;
1131 case OBJ_PATH:
1132 eda_renderer_draw_path_grips (renderer, object);
1133 break;
1134 case OBJ_TEXT:
1135 eda_renderer_draw_text_grips (renderer, object);
1136 break;
1137 case OBJ_PICTURE:
1138 eda_renderer_draw_grips_impl (renderer, GRIP_SQUARE, 4,
1139 object->picture->upper_x, object->picture->upper_y,
1140 object->picture->lower_x, object->picture->upper_y,
1141 object->picture->upper_x, object->picture->lower_y,
1142 object->picture->lower_x, object->picture->lower_y);
1143 break;
1144 case OBJ_COMPLEX:
1145 case OBJ_PLACEHOLDER:
1146 /* No grips */
1147 break;
1148 default:
1149 g_return_if_reached ();
1153 static void
1154 eda_renderer_draw_grips_impl (EdaRenderer *renderer, int type, int n_grips, ...)
1156 va_list coordinates;
1157 int i;
1159 va_start (coordinates, n_grips);
1160 for (i = 0; i < n_grips; i++) {
1161 int x = va_arg (coordinates, int);
1162 int y = va_arg (coordinates, int);
1164 switch (type) {
1165 case GRIP_SQUARE:
1166 eda_cairo_center_box (renderer->priv->cr, EDA_RENDERER_CAIRO_FLAGS (renderer),
1167 0, 0, x, y,
1168 renderer->priv->grip_size,
1169 renderer->priv->grip_size);
1170 break;
1171 case GRIP_CIRCLE:
1172 eda_cairo_center_arc (renderer->priv->cr, EDA_RENDERER_CAIRO_FLAGS (renderer),
1173 0, 0, x, y,
1174 renderer->priv->grip_size,
1175 0, 360);
1176 break;
1177 default:
1178 g_return_if_reached ();
1181 eda_cairo_set_source_color (renderer->priv->cr, GRIP_FILL_COLOR,
1182 renderer->priv->color_map);
1183 cairo_fill_preserve (renderer->priv->cr);
1185 eda_cairo_set_source_color (renderer->priv->cr, GRIP_STROKE_COLOR,
1186 renderer->priv->color_map);
1188 eda_cairo_stroke (renderer->priv->cr, EDA_RENDERER_CAIRO_FLAGS (renderer),
1189 TYPE_SOLID, END_NONE,
1190 0, -1, -1);
1192 va_end (coordinates);
1195 static void
1196 eda_renderer_draw_arc_grips (EdaRenderer *renderer, OBJECT *object)
1198 double radius, start_angle, sweep_angle;
1199 int x1, y1, x2, y2, x3, y3;
1202 * An arc has three grips:
1203 * <DL>
1204 * <DT>*</DT><DD>one at the center that allows changes on the
1205 * radius - at (<B>x</B>,<B>y</B>).
1206 * <DT>*</DT><DD>one at the start of the arc - at (<B>x1</B>,<B>y1</B>).
1207 * <DT>*</DT><DD>one at the end of the arc - at (<B>x2</B>,<B>y2</B>).
1210 x1 = object->arc->x;
1211 y1 = object->arc->y;
1213 radius = object->arc->radius;
1214 start_angle = object->arc->start_angle;
1215 sweep_angle = object->arc->sweep_angle;
1217 x2 = x1 + radius * cos ( start_angle * M_PI / 180);
1218 y2 = y1 + radius * sin ( start_angle * M_PI / 180);
1219 x3 = x1 + radius * cos ((start_angle + sweep_angle) * M_PI / 180);
1220 y3 = y1 + radius * sin ((start_angle + sweep_angle) * M_PI / 180);
1222 eda_renderer_draw_grips_impl (renderer, GRIP_SQUARE, 3,
1223 x1, y1, /* center */
1224 x2, y2, /* start_angle */
1225 x3, y3); /* end_angle */
1228 static void
1229 eda_renderer_draw_path_grips (EdaRenderer *renderer, OBJECT *object)
1231 int i, last_x = 0, last_y = 0, next_x, next_y;
1232 for (i = 0; i < object->path->num_sections; i++) {
1233 PATH_SECTION *section = object->path->sections + i;
1235 if (section->code != PATH_END) {
1236 next_x = section->x3;
1237 next_y = section->y3;
1240 switch (section->code) {
1241 case PATH_CURVETO:
1242 /* Two control point lines */
1243 eda_cairo_line (renderer->priv->cr, EDA_RENDERER_CAIRO_FLAGS (renderer),
1244 END_NONE, 0,
1245 last_x, last_y, section->x1, section->y1);
1246 eda_cairo_line (renderer->priv->cr, EDA_RENDERER_CAIRO_FLAGS (renderer),
1247 END_NONE, 0,
1248 next_x, next_y, section->x2, section->y2);
1249 eda_cairo_stroke (renderer->priv->cr, EDA_RENDERER_CAIRO_FLAGS (renderer),
1250 TYPE_SOLID, END_NONE,
1251 EDA_RENDERER_STROKE_WIDTH (renderer, 0), -1, -1);
1252 /* Two control point grips */
1253 eda_renderer_draw_grips_impl (renderer, GRIP_CIRCLE, 2,
1254 section->x1, section->y1,
1255 section->x2, section->y2);
1256 /* Deliberately fall through */
1257 case PATH_MOVETO:
1258 case PATH_MOVETO_OPEN:
1259 case PATH_LINETO:
1260 last_x = next_x;
1261 last_y = next_y;
1262 /* One control point grip */
1263 eda_renderer_draw_grips_impl (renderer, GRIP_SQUARE, 1,
1264 section->x3, section->y3);
1265 break;
1266 case PATH_END:
1267 break;
1272 static void
1273 eda_renderer_draw_text_grips (EdaRenderer *renderer, OBJECT *object)
1275 double dummy = 0, small_dist = TEXT_MARKER_SIZE;
1276 int x = object->text->x;
1277 int y = object->text->y;
1279 /* First check if this is hidden text. */
1280 if (object->visibility == INVISIBLE
1281 && !EDA_RENDERER_CHECK_FLAG (renderer, FLAG_TEXT_HIDDEN)) {
1282 return;
1285 /* Check that color is enabled */
1286 if (!eda_renderer_is_drawable_color (renderer, TEXT_MARKER_COLOR, FALSE))
1287 return;
1289 /* If the text marker is too tiny, don't draw it. */
1290 cairo_user_to_device_distance (renderer->priv->cr, &small_dist, &dummy);
1291 if (small_dist < 1) return;
1293 eda_cairo_set_source_color (renderer->priv->cr, TEXT_MARKER_COLOR,
1294 renderer->priv->color_map);
1296 eda_cairo_line (renderer->priv->cr, EDA_RENDERER_CAIRO_FLAGS (renderer),
1297 END_NONE, 0,
1298 x - TEXT_MARKER_SIZE, y - TEXT_MARKER_SIZE,
1299 x + TEXT_MARKER_SIZE, y + TEXT_MARKER_SIZE);
1300 eda_cairo_line (renderer->priv->cr, EDA_RENDERER_CAIRO_FLAGS (renderer),
1301 END_NONE, 0,
1302 x - TEXT_MARKER_SIZE, y + TEXT_MARKER_SIZE,
1303 x + TEXT_MARKER_SIZE, y - TEXT_MARKER_SIZE);
1304 eda_cairo_stroke (renderer->priv->cr, EDA_RENDERER_CAIRO_FLAGS (renderer),
1305 TYPE_SOLID, END_NONE,
1306 EDA_RENDERER_STROKE_WIDTH (renderer, 0),
1307 -1, -1);
1310 /* ================================================================
1311 * CUE DRAWING
1312 * ================================================================ */
1314 static void
1315 eda_renderer_draw_cues_list (EdaRenderer *renderer, GList *objects)
1317 GList *iter;
1319 for (iter = objects; iter != NULL; iter = g_list_next (iter)) {
1320 eda_renderer_draw_cues (renderer, (OBJECT *) iter->data);
1324 void
1325 eda_renderer_draw_cues (EdaRenderer *renderer, OBJECT *object)
1327 g_return_if_fail (EDA_IS_RENDERER (renderer));
1328 EDA_RENDERER_GET_CLASS (renderer)->draw_cues (renderer, object);
1331 static void
1332 eda_renderer_default_draw_cues (EdaRenderer *renderer, OBJECT *object)
1334 g_return_if_fail (object != NULL);
1335 g_return_if_fail (renderer->priv->cr != NULL);
1337 switch (object->type) {
1338 case OBJ_LINE:
1339 case OBJ_BOX:
1340 case OBJ_ARC:
1341 case OBJ_CIRCLE:
1342 case OBJ_PATH:
1343 case OBJ_TEXT:
1344 case OBJ_PICTURE:
1345 break;
1346 case OBJ_COMPLEX:
1347 case OBJ_PLACEHOLDER:
1348 /* Recurse */
1349 eda_renderer_draw_cues_list (renderer, object->complex->prim_objs);
1350 break;
1351 case OBJ_NET:
1352 case OBJ_BUS:
1353 eda_renderer_draw_mid_cues (renderer, object);
1354 eda_renderer_draw_end_cues (renderer, object, 0);
1355 eda_renderer_draw_end_cues (renderer, object, 1);
1356 break;
1357 case OBJ_PIN:
1358 g_return_if_fail ((object->whichend == 1) || (object->whichend == 0));
1359 eda_renderer_draw_end_cues (renderer, object, object->whichend);
1360 break;
1361 default:
1362 g_return_if_reached ();
1366 static void
1367 eda_renderer_draw_end_cues (EdaRenderer *renderer, OBJECT *object, int end)
1369 int x = object->line->x[end], y = object->line->y[end];
1370 int conn_count = 0;
1371 int conn_type = CONN_ENDPOINT;
1372 int is_bus = FALSE;
1373 GList *iter;
1375 /* We should never be at the unconnectable end of a pin */
1376 g_return_if_fail ((object->type != OBJ_PIN) || (object->whichend == end));
1378 /* Check whether the current object is a bus or bus pin */
1379 is_bus = ((object->type == OBJ_BUS)
1380 || ((object->type == OBJ_PIN)
1381 && (object->pin_type == PIN_TYPE_BUS)));
1383 for (iter = object->conn_list; iter != NULL; iter = g_list_next (iter)) {
1384 CONN *conn = (CONN *) iter->data;
1385 if ((conn->x != x) || (conn->y != y)) continue;
1387 /* Check whether the connected object is a bus or bus pin */
1388 is_bus |= ((conn->other_object->type == OBJ_BUS)
1389 || ((conn->other_object->type == OBJ_PIN)
1390 && (conn->other_object->pin_type == PIN_TYPE_BUS)));
1392 if (conn->type == CONN_MIDPOINT) {
1393 /* If it's a mid-line connection, we can stop already. */
1394 conn_type = CONN_MIDPOINT;
1395 break;
1398 conn_count++;
1401 /* Draw a midpoint, if necessary */
1402 if ((conn_type == CONN_MIDPOINT)
1403 || ((object->type == OBJ_NET) && (conn_count > 1))) {
1404 eda_renderer_draw_junction_cue (renderer, x, y, is_bus);
1405 return;
1408 /* Only things left to be drawn are end point cues */
1409 if (!eda_renderer_is_drawable_color (renderer, NET_ENDPOINT_COLOR, TRUE))
1410 return;
1411 eda_renderer_set_color (renderer, NET_ENDPOINT_COLOR);
1413 switch (object->type) {
1414 case OBJ_NET:
1415 case OBJ_PIN:
1416 /* If less than one thing was connected to this end of the net
1417 * segment or pin, draw box cue */
1418 if (conn_count > 0) break;
1420 eda_cairo_center_box (renderer->priv->cr,
1421 EDA_RENDERER_CAIRO_FLAGS (renderer),
1422 -1, -1, x, y, CUE_BOX_SIZE, CUE_BOX_SIZE);
1423 cairo_fill (renderer->priv->cr);
1424 break;
1426 case OBJ_BUS:
1427 break;
1428 default:
1429 g_return_if_reached ();
1433 static void
1434 eda_renderer_draw_mid_cues (EdaRenderer *renderer, OBJECT *object)
1436 GList *iter;
1437 for (iter = object->conn_list; iter != NULL; iter = g_list_next (iter)) {
1438 CONN *conn = (CONN *) iter->data;
1440 if (conn->type == CONN_MIDPOINT) {
1441 int is_bus = ((object->type == OBJ_BUS)
1442 || (conn->other_object->type == OBJ_BUS)
1443 || ((conn->other_object->type == OBJ_PIN)
1444 && (conn->other_object->pin_type == PIN_TYPE_BUS)));
1445 eda_renderer_draw_junction_cue (renderer, conn->x, conn->y, is_bus);
1450 static void
1451 eda_renderer_draw_junction_cue (EdaRenderer *renderer, int x, int y, int is_bus)
1453 double width = (is_bus ? BUS_WIDTH : NET_WIDTH);
1454 double radius = (is_bus ? JUNCTION_CUE_SIZE_BUS : JUNCTION_CUE_SIZE_NET) / 2.0;
1456 if (!eda_renderer_is_drawable_color (renderer, JUNCTION_COLOR, 1)) {
1457 return;
1460 eda_cairo_center_arc (renderer->priv->cr, EDA_RENDERER_CAIRO_FLAGS (renderer),
1461 width, -1, x, y, radius, 0, 360);
1462 eda_renderer_set_color (renderer, JUNCTION_COLOR);
1463 cairo_fill (renderer->priv->cr);
1466 /* ================================================================
1467 * RENDERED BOUNDS
1468 * ================================================================ */
1471 eda_renderer_get_user_bounds (EdaRenderer *renderer, OBJECT *object,
1472 double *left, double *top,
1473 double *right, double *bottom)
1475 g_return_val_if_fail (EDA_IS_RENDERER (renderer), FALSE);
1477 return EDA_RENDERER_GET_CLASS (renderer)->user_bounds (renderer, object,
1478 left, top,
1479 right, bottom);
1482 static int
1483 eda_renderer_default_get_user_bounds (EdaRenderer *renderer, OBJECT *object,
1484 double *left, double *top,
1485 double *right, double *bottom)
1487 g_return_val_if_fail ((object != NULL), FALSE);
1488 g_return_val_if_fail ((renderer->priv->cr != NULL), FALSE);
1490 switch (object->type) {
1491 case OBJ_TEXT:
1492 return eda_renderer_get_text_user_bounds (renderer, object,
1493 left, top, right, bottom);
1494 case OBJ_LINE:
1495 case OBJ_BOX:
1496 case OBJ_ARC:
1497 case OBJ_CIRCLE:
1498 case OBJ_PATH:
1499 case OBJ_PICTURE:
1500 case OBJ_COMPLEX:
1501 case OBJ_PLACEHOLDER:
1502 case OBJ_NET:
1503 case OBJ_BUS:
1504 case OBJ_PIN:
1505 /* No rendered bounds available for most OBJECT types. */
1506 return FALSE;
1507 default:
1508 g_return_val_if_reached (FALSE);
1512 static int
1513 eda_renderer_get_text_user_bounds (EdaRenderer *renderer, OBJECT *object,
1514 double *left, double *top,
1515 double *right, double *bottom)
1517 PangoRectangle inked_rect, logical_rect;
1518 PangoAttrList *attr_list;
1519 PangoAttrIterator *attr_iterator;
1520 gboolean has_overbars;
1522 /* First check if this is hidden text. */
1523 if (object->visibility == INVISIBLE
1524 && !EDA_RENDERER_CHECK_FLAG (renderer, FLAG_TEXT_HIDDEN)) {
1525 return FALSE;
1528 /* Also, check that we actually need to display a string */
1529 if (object->text->disp_string == NULL)
1530 return FALSE;
1532 cairo_save (renderer->priv->cr);
1534 /* Set up the text and check it worked. */
1535 if (!eda_renderer_prepare_text (renderer, object))
1536 return FALSE;
1538 /* Figure out the bounds, send them back. Note that Pango thinks in
1539 * device coordinates, but we need world coordinates. */
1540 pango_layout_get_pixel_extents (renderer->priv->pl,
1541 &inked_rect, &logical_rect);
1543 /* The logic for factoring overbar/strikethrough into the text
1544 * extents is hard-coded into Pango, so we need to simulate this
1545 * as good as we can here. */
1546 attr_list = pango_layout_get_attributes (renderer->priv->pl);
1547 attr_iterator = pango_attr_list_get_iterator (attr_list);
1548 has_overbars = pango_attr_iterator_next (attr_iterator);
1549 pango_attr_iterator_destroy (attr_iterator);
1550 if (has_overbars) {
1551 /* Since we can't access the values which we'd need for getting
1552 * the height exactly right and since the overbar goes up *almost*
1553 * to zero, just pretend it does go up to zero. */
1554 inked_rect.height += inked_rect.y;
1555 inked_rect.y = 0;
1556 /* The overbar reaches out beyond the ink dimensions to the left
1557 * and right. Checking whether the left-/rightmost letter is
1558 * actually covered by an overbar would be overkill, so just
1559 * assume the whole logical width is covered by the overbar. */
1560 inked_rect.x = logical_rect.x;
1561 inked_rect.width = logical_rect.width;
1564 *left = (double) inked_rect.x;
1565 *top = (double) inked_rect.y;
1566 *right = (double) inked_rect.x + inked_rect.width;
1567 *bottom = (double) inked_rect.y + inked_rect.height;
1568 cairo_user_to_device (renderer->priv->cr, left, top);
1569 cairo_user_to_device (renderer->priv->cr, right, bottom);
1571 cairo_restore (renderer->priv->cr);
1573 cairo_device_to_user (renderer->priv->cr, left, top);
1574 cairo_device_to_user (renderer->priv->cr, right, bottom);
1576 return TRUE;
1580 /* ================================================================
1581 * MISCELLANEOUS (CREATION, DESTRUCTION, ACCESSORS)
1582 * ================================================================ */
1584 EdaRenderer *
1585 eda_renderer_new (cairo_t *cr, PangoContext *pc)
1587 return g_object_new (EDA_TYPE_RENDERER,
1588 "cairo-context", cr,
1589 "pango-context", pc,
1590 NULL);
1593 void
1594 eda_renderer_destroy (EdaRenderer *renderer)
1596 g_object_unref (G_OBJECT (renderer));
1599 cairo_t *
1600 eda_renderer_get_cairo_context (EdaRenderer *renderer)
1602 cairo_t *cr;
1603 g_return_val_if_fail (EDA_IS_RENDERER (renderer), NULL);
1604 g_object_get (G_OBJECT (renderer), "cairo-context", &cr, NULL);
1605 return cr;
1608 gboolean
1609 eda_renderer_get_hinting_enabled (EdaRenderer *renderer)
1611 g_return_val_if_fail (EDA_IS_RENDERER (renderer), FALSE);
1612 return EDA_RENDERER_CHECK_FLAG (renderer, FLAG_HINTING);
1615 GArray *
1616 eda_renderer_get_color_map (EdaRenderer *renderer)
1618 GArray *map = NULL;
1619 g_return_val_if_fail (EDA_IS_RENDERER (renderer), NULL);
1620 g_object_get (G_OBJECT (renderer), "color-map", &map, NULL);
1621 return map;
1624 void
1625 eda_renderer_set_color_map (EdaRenderer *renderer, GArray *map)
1627 g_return_if_fail (EDA_IS_RENDERER (renderer));
1628 g_object_set (G_OBJECT (renderer), "color-map", map, NULL);
1632 eda_renderer_get_cairo_flags (EdaRenderer *renderer)
1634 g_return_val_if_fail (EDA_IS_RENDERER (renderer), 0);
1635 return EDA_RENDERER_CAIRO_FLAGS (renderer);