Convert mask type to enum
[geda-pcb/pcjc2.git] / src / hid / gtk / gtkhid-gdk.c
blob6c159b411405f7e4e694dc0d2ae9cf40fe574840
1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
5 #include <stdio.h>
7 #include "crosshair.h"
8 #include "clip.h"
9 #include "../hidint.h"
10 #include "gui.h"
11 #include "hid/common/draw_helpers.h"
13 #ifdef HAVE_LIBDMALLOC
14 #include <dmalloc.h>
15 #endif
17 extern HID ghid_hid;
19 /* Sets priv->u_gc to the "right" GC to use (wrt mask or window)
21 #define USE_GC(gc) if (!use_gc(gc)) return
23 static enum mask_mode cur_mask = HID_MASK_OFF;
24 static int mask_seq = 0;
26 typedef struct render_priv {
27 GdkGC *bg_gc;
28 GdkGC *offlimits_gc;
29 GdkGC *mask_gc;
30 GdkGC *u_gc;
31 GdkGC *grid_gc;
32 bool clip;
33 GdkRectangle clip_rect;
34 int attached_invalidate_depth;
35 int mark_invalidate_depth;
37 /* Feature for leading the user to a particular location */
38 guint lead_user_timeout;
39 GTimer *lead_user_timer;
40 bool lead_user;
41 Coord lead_user_radius;
42 Coord lead_user_x;
43 Coord lead_user_y;
45 } render_priv;
48 typedef struct hid_gc_struct
50 HID *me_pointer;
51 GdkGC *gc;
53 gchar *colorname;
54 Coord width;
55 gint cap, join;
56 gchar xor_mask;
57 gint mask_seq;
59 hid_gc_struct;
62 static void draw_lead_user (render_priv *priv);
65 int
66 ghid_set_layer (const char *name, int group, int empty)
68 int idx = group;
69 if (idx >= 0 && idx < max_group)
71 int n = PCB->LayerGroups.Number[group];
72 for (idx = 0; idx < n-1; idx ++)
74 int ni = PCB->LayerGroups.Entries[group][idx];
75 if (ni >= 0 && ni < max_copper_layer + 2
76 && PCB->Data->Layer[ni].On)
77 break;
79 idx = PCB->LayerGroups.Entries[group][idx];
82 if (idx >= 0 && idx < max_copper_layer + 2)
83 return /*pinout ? 1 : */ PCB->Data->Layer[idx].On;
84 if (idx < 0)
86 switch (SL_TYPE (idx))
88 case SL_INVISIBLE:
89 return /* pinout ? 0 : */ PCB->InvisibleObjectsOn;
90 case SL_MASK:
91 if (SL_MYSIDE (idx) /*&& !pinout */ )
92 return TEST_FLAG (SHOWMASKFLAG, PCB);
93 return 0;
94 case SL_SILK:
95 if (SL_MYSIDE (idx) /*|| pinout */ )
96 return PCB->ElementOn;
97 return 0;
98 case SL_ASSY:
99 return 0;
100 case SL_PDRILL:
101 case SL_UDRILL:
102 return 1;
103 case SL_RATS:
104 return PCB->RatOn;
107 return 0;
110 void
111 ghid_destroy_gc (hidGC gc)
113 if (gc->gc)
114 g_object_unref (gc->gc);
115 g_free (gc);
118 hidGC
119 ghid_make_gc (void)
121 hidGC rv;
123 rv = g_new0 (hid_gc_struct, 1);
124 rv->me_pointer = &ghid_hid;
125 rv->colorname = Settings.BackgroundColor;
126 return rv;
129 static void
130 set_clip (render_priv *priv, GdkGC *gc)
132 if (gc == NULL)
133 return;
135 if (priv->clip)
136 gdk_gc_set_clip_rectangle (gc, &priv->clip_rect);
137 else
138 gdk_gc_set_clip_mask (gc, NULL);
141 static void
142 ghid_draw_grid (void)
144 static GdkPoint *points = 0;
145 static int npoints = 0;
146 Coord x1, y1, x2, y2, x, y;
147 int n, i;
148 render_priv *priv = gport->render_priv;
150 if (!Settings.DrawGrid)
151 return;
152 if (Vz (PCB->Grid) < MIN_GRID_DISTANCE)
153 return;
154 if (!priv->grid_gc)
156 if (gdk_color_parse (Settings.GridColor, &gport->grid_color))
158 gport->grid_color.red ^= gport->bg_color.red;
159 gport->grid_color.green ^= gport->bg_color.green;
160 gport->grid_color.blue ^= gport->bg_color.blue;
161 gdk_color_alloc (gport->colormap, &gport->grid_color);
163 priv->grid_gc = gdk_gc_new (gport->drawable);
164 gdk_gc_set_function (priv->grid_gc, GDK_XOR);
165 gdk_gc_set_foreground (priv->grid_gc, &gport->grid_color);
166 gdk_gc_set_clip_origin (priv->grid_gc, 0, 0);
167 set_clip (priv, priv->grid_gc);
169 x1 = GridFit (SIDE_X (gport->view.x0), PCB->Grid, PCB->GridOffsetX);
170 y1 = GridFit (SIDE_Y (gport->view.y0), PCB->Grid, PCB->GridOffsetY);
171 x2 = GridFit (SIDE_X (gport->view.x0 + gport->view.width - 1),
172 PCB->Grid, PCB->GridOffsetX);
173 y2 = GridFit (SIDE_Y (gport->view.y0 + gport->view.height - 1),
174 PCB->Grid, PCB->GridOffsetY);
175 if (x1 > x2)
177 Coord tmp = x1;
178 x1 = x2;
179 x2 = tmp;
181 if (y1 > y2)
183 Coord tmp = y1;
184 y1 = y2;
185 y2 = tmp;
187 if (Vx (x1) < 0)
188 x1 += PCB->Grid;
189 if (Vy (y1) < 0)
190 y1 += PCB->Grid;
191 if (Vx (x2) >= gport->width)
192 x2 -= PCB->Grid;
193 if (Vy (y2) >= gport->height)
194 y2 -= PCB->Grid;
195 n = (x2 - x1) / PCB->Grid + 1;
196 if (n > npoints)
198 npoints = n + 10;
199 points = (GdkPoint *)realloc (points, npoints * sizeof (GdkPoint));
201 n = 0;
202 for (x = x1; x <= x2; x += PCB->Grid)
204 points[n].x = Vx (x);
205 n++;
207 if (n == 0)
208 return;
209 for (y = y1; y <= y2; y += PCB->Grid)
211 for (i = 0; i < n; i++)
212 points[i].y = Vy (y);
213 gdk_draw_points (gport->drawable, priv->grid_gc, points, n);
217 /* ------------------------------------------------------------ */
218 static void
219 ghid_draw_bg_image (void)
221 static GdkPixbuf *pixbuf;
222 GdkInterpType interp_type;
223 gint x, y, w, h, w_src, h_src;
224 static gint w_scaled, h_scaled;
225 render_priv *priv = gport->render_priv;
227 if (!ghidgui->bg_pixbuf)
228 return;
230 w = PCB->MaxWidth / gport->view.coord_per_px;
231 h = PCB->MaxHeight / gport->view.coord_per_px;
232 x = gport->view.x0 / gport->view.coord_per_px;
233 y = gport->view.y0 / gport->view.coord_per_px;
235 if (w_scaled != w || h_scaled != h)
237 if (pixbuf)
238 g_object_unref (G_OBJECT (pixbuf));
240 w_src = gdk_pixbuf_get_width (ghidgui->bg_pixbuf);
241 h_src = gdk_pixbuf_get_height (ghidgui->bg_pixbuf);
242 if (w > w_src && h > h_src)
243 interp_type = GDK_INTERP_NEAREST;
244 else
245 interp_type = GDK_INTERP_BILINEAR;
247 pixbuf =
248 gdk_pixbuf_scale_simple (ghidgui->bg_pixbuf, w, h, interp_type);
249 w_scaled = w;
250 h_scaled = h;
252 if (pixbuf)
253 gdk_pixbuf_render_to_drawable (pixbuf, gport->drawable, priv->bg_gc,
254 x, y, 0, 0,
255 w - x, h - y, GDK_RGB_DITHER_NORMAL, 0, 0);
258 #define WHICH_GC(gc) (cur_mask == HID_MASK_CLEAR ? priv->mask_gc : (gc)->gc)
260 void
261 ghid_use_mask (enum mask_mode mode)
263 static int mask_seq_id = 0;
264 GdkColor color;
265 render_priv *priv = gport->render_priv;
267 if (!gport->pixmap)
268 return;
269 if (mode == cur_mask)
270 return;
271 switch (mode)
273 case HID_MASK_OFF:
274 gport->drawable = gport->pixmap;
275 mask_seq = 0;
276 break;
278 case HID_MASK_BEFORE:
279 /* The HID asks not to receive this mask type, so warn if we get it */
280 g_return_if_reached ();
282 case HID_MASK_CLEAR:
283 if (!gport->mask)
284 gport->mask = gdk_pixmap_new (0, gport->width, gport->height, 1);
285 gport->drawable = gport->mask;
286 mask_seq = 0;
287 if (!priv->mask_gc)
289 priv->mask_gc = gdk_gc_new (gport->drawable);
290 gdk_gc_set_clip_origin (priv->mask_gc, 0, 0);
291 set_clip (priv, priv->mask_gc);
293 color.pixel = 1;
294 gdk_gc_set_foreground (priv->mask_gc, &color);
295 gdk_draw_rectangle (gport->drawable, priv->mask_gc, TRUE, 0, 0,
296 gport->width, gport->height);
297 color.pixel = 0;
298 gdk_gc_set_foreground (priv->mask_gc, &color);
299 break;
301 case HID_MASK_AFTER:
302 mask_seq_id++;
303 if (!mask_seq_id)
304 mask_seq_id = 1;
305 mask_seq = mask_seq_id;
307 gport->drawable = gport->pixmap;
308 break;
311 cur_mask = mode;
315 typedef struct
317 int color_set;
318 GdkColor color;
319 int xor_set;
320 GdkColor xor_color;
321 } ColorCache;
324 /* Config helper functions for when the user changes color preferences.
325 | set_special colors used in the gtkhid.
327 static void
328 set_special_grid_color (void)
330 render_priv *priv = gport->render_priv;
332 if (!gport->colormap)
333 return;
334 gport->grid_color.red ^= gport->bg_color.red;
335 gport->grid_color.green ^= gport->bg_color.green;
336 gport->grid_color.blue ^= gport->bg_color.blue;
337 gdk_color_alloc (gport->colormap, &gport->grid_color);
338 if (priv->grid_gc)
339 gdk_gc_set_foreground (priv->grid_gc, &gport->grid_color);
342 void
343 ghid_set_special_colors (HID_Attribute * ha)
345 render_priv *priv = gport->render_priv;
347 if (!ha->name || !ha->value)
348 return;
349 if (!strcmp (ha->name, "background-color") && priv->bg_gc)
351 ghid_map_color_string (*(char **) ha->value, &gport->bg_color);
352 gdk_gc_set_foreground (priv->bg_gc, &gport->bg_color);
353 set_special_grid_color ();
355 else if (!strcmp (ha->name, "off-limit-color") && priv->offlimits_gc)
357 ghid_map_color_string (*(char **) ha->value, &gport->offlimits_color);
358 gdk_gc_set_foreground (priv->offlimits_gc, &gport->offlimits_color);
360 else if (!strcmp (ha->name, "grid-color") && priv->grid_gc)
362 ghid_map_color_string (*(char **) ha->value, &gport->grid_color);
363 set_special_grid_color ();
367 void
368 ghid_set_color (hidGC gc, const char *name)
370 static void *cache = 0;
371 hidval cval;
373 if (name == NULL)
375 fprintf (stderr, "%s(): name = NULL, setting to magenta\n",
376 __FUNCTION__);
377 name = "magenta";
380 gc->colorname = (char *) name;
381 if (!gc->gc)
382 return;
383 if (gport->colormap == 0)
384 gport->colormap = gtk_widget_get_colormap (gport->top_window);
386 if (strcmp (name, "erase") == 0)
388 gdk_gc_set_foreground (gc->gc, &gport->bg_color);
390 else if (strcmp (name, "drill") == 0)
392 gdk_gc_set_foreground (gc->gc, &gport->offlimits_color);
394 else
396 ColorCache *cc;
397 if (hid_cache_color (0, name, &cval, &cache))
398 cc = (ColorCache *) cval.ptr;
399 else
401 cc = (ColorCache *) malloc (sizeof (ColorCache));
402 memset (cc, 0, sizeof (*cc));
403 cval.ptr = cc;
404 hid_cache_color (1, name, &cval, &cache);
407 if (!cc->color_set)
409 if (gdk_color_parse (name, &cc->color))
410 gdk_color_alloc (gport->colormap, &cc->color);
411 else
412 gdk_color_white (gport->colormap, &cc->color);
413 cc->color_set = 1;
415 if (gc->xor_mask)
417 if (!cc->xor_set)
419 cc->xor_color.red = cc->color.red ^ gport->bg_color.red;
420 cc->xor_color.green = cc->color.green ^ gport->bg_color.green;
421 cc->xor_color.blue = cc->color.blue ^ gport->bg_color.blue;
422 gdk_color_alloc (gport->colormap, &cc->xor_color);
423 cc->xor_set = 1;
425 gdk_gc_set_foreground (gc->gc, &cc->xor_color);
427 else
429 gdk_gc_set_foreground (gc->gc, &cc->color);
434 void
435 ghid_set_line_cap (hidGC gc, EndCapStyle style)
437 render_priv *priv = gport->render_priv;
439 switch (style)
441 case Trace_Cap:
442 case Round_Cap:
443 gc->cap = GDK_CAP_ROUND;
444 gc->join = GDK_JOIN_ROUND;
445 break;
446 case Square_Cap:
447 case Beveled_Cap:
448 gc->cap = GDK_CAP_PROJECTING;
449 gc->join = GDK_JOIN_MITER;
450 break;
452 if (gc->gc)
453 gdk_gc_set_line_attributes (WHICH_GC (gc),
454 Vz (gc->width), GDK_LINE_SOLID,
455 (GdkCapStyle)gc->cap, (GdkJoinStyle)gc->join);
458 void
459 ghid_set_line_width (hidGC gc, Coord width)
461 render_priv *priv = gport->render_priv;
463 gc->width = width;
464 if (gc->gc)
465 gdk_gc_set_line_attributes (WHICH_GC (gc),
466 Vz (gc->width), GDK_LINE_SOLID,
467 (GdkCapStyle)gc->cap, (GdkJoinStyle)gc->join);
470 void
471 ghid_set_draw_xor (hidGC gc, int xor_mask)
473 gc->xor_mask = xor_mask;
474 if (!gc->gc)
475 return;
476 gdk_gc_set_function (gc->gc, xor_mask ? GDK_XOR : GDK_COPY);
477 ghid_set_color (gc, gc->colorname);
480 static int
481 use_gc (hidGC gc)
483 render_priv *priv = gport->render_priv;
484 GdkWindow *window = gtk_widget_get_window (gport->top_window);
486 if (gc->me_pointer != &ghid_hid)
488 fprintf (stderr, "Fatal: GC from another HID passed to GTK HID\n");
489 abort ();
492 if (!gport->pixmap)
493 return 0;
494 if (!gc->gc)
496 gc->gc = gdk_gc_new (window);
497 ghid_set_color (gc, gc->colorname);
498 ghid_set_line_width (gc, gc->width);
499 ghid_set_line_cap (gc, (EndCapStyle)gc->cap);
500 ghid_set_draw_xor (gc, gc->xor_mask);
501 gdk_gc_set_clip_origin (gc->gc, 0, 0);
503 if (gc->mask_seq != mask_seq)
505 if (mask_seq)
506 gdk_gc_set_clip_mask (gc->gc, gport->mask);
507 else
508 set_clip (priv, gc->gc);
509 gc->mask_seq = mask_seq;
511 priv->u_gc = WHICH_GC (gc);
512 return 1;
515 void
516 ghid_draw_line (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
518 double dx1, dy1, dx2, dy2;
519 render_priv *priv = gport->render_priv;
521 dx1 = Vx ((double) x1);
522 dy1 = Vy ((double) y1);
523 dx2 = Vx ((double) x2);
524 dy2 = Vy ((double) y2);
526 if (!ClipLine (0, 0, gport->width, gport->height,
527 &dx1, &dy1, &dx2, &dy2, gc->width / gport->view.coord_per_px))
528 return;
530 USE_GC (gc);
531 gdk_draw_line (gport->drawable, priv->u_gc, dx1, dy1, dx2, dy2);
534 void
535 ghid_draw_arc (hidGC gc, Coord cx, Coord cy,
536 Coord xradius, Coord yradius, Angle start_angle, Angle delta_angle)
538 gint vrx, vry;
539 gint w, h, radius;
540 render_priv *priv = gport->render_priv;
542 w = gport->width * gport->view.coord_per_px;
543 h = gport->height * gport->view.coord_per_px;
544 radius = (xradius > yradius) ? xradius : yradius;
545 if (SIDE_X (cx) < gport->view.x0 - radius
546 || SIDE_X (cx) > gport->view.x0 + w + radius
547 || SIDE_Y (cy) < gport->view.y0 - radius
548 || SIDE_Y (cy) > gport->view.y0 + h + radius)
549 return;
551 USE_GC (gc);
552 vrx = Vz (xradius);
553 vry = Vz (yradius);
555 if (gport->view.flip_x)
557 start_angle = 180 - start_angle;
558 delta_angle = -delta_angle;
560 if (gport->view.flip_y)
562 start_angle = -start_angle;
563 delta_angle = -delta_angle;
565 /* make sure we fall in the -180 to +180 range */
566 start_angle = NormalizeAngle (start_angle);
567 if (start_angle >= 180) start_angle -= 360;
569 gdk_draw_arc (gport->drawable, priv->u_gc, 0,
570 Vx (cx) - vrx, Vy (cy) - vry,
571 vrx * 2, vry * 2, (start_angle + 180) * 64, delta_angle * 64);
574 void
575 ghid_draw_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
577 gint w, h, lw;
578 render_priv *priv = gport->render_priv;
580 lw = gc->width;
581 w = gport->width * gport->view.coord_per_px;
582 h = gport->height * gport->view.coord_per_px;
584 if ((SIDE_X (x1) < gport->view.x0 - lw
585 && SIDE_X (x2) < gport->view.x0 - lw)
586 || (SIDE_X (x1) > gport->view.x0 + w + lw
587 && SIDE_X (x2) > gport->view.x0 + w + lw)
588 || (SIDE_Y (y1) < gport->view.y0 - lw
589 && SIDE_Y (y2) < gport->view.y0 - lw)
590 || (SIDE_Y (y1) > gport->view.y0 + h + lw
591 && SIDE_Y (y2) > gport->view.y0 + h + lw))
592 return;
594 x1 = Vx (x1);
595 y1 = Vy (y1);
596 x2 = Vx (x2);
597 y2 = Vy (y2);
599 if (x1 > x2)
601 gint xt = x1;
602 x1 = x2;
603 x2 = xt;
605 if (y1 > y2)
607 gint yt = y1;
608 y1 = y2;
609 y2 = yt;
612 USE_GC (gc);
613 gdk_draw_rectangle (gport->drawable, priv->u_gc, FALSE,
614 x1, y1, x2 - x1 + 1, y2 - y1 + 1);
618 void
619 ghid_fill_circle (hidGC gc, Coord cx, Coord cy, Coord radius)
621 gint w, h, vr;
622 render_priv *priv = gport->render_priv;
624 w = gport->width * gport->view.coord_per_px;
625 h = gport->height * gport->view.coord_per_px;
626 if (SIDE_X (cx) < gport->view.x0 - radius
627 || SIDE_X (cx) > gport->view.x0 + w + radius
628 || SIDE_Y (cy) < gport->view.y0 - radius
629 || SIDE_Y (cy) > gport->view.y0 + h + radius)
630 return;
632 USE_GC (gc);
633 vr = Vz (radius);
634 gdk_draw_arc (gport->drawable, priv->u_gc, TRUE,
635 Vx (cx) - vr, Vy (cy) - vr, vr * 2, vr * 2, 0, 360 * 64);
638 void
639 ghid_fill_polygon (hidGC gc, int n_coords, Coord *x, Coord *y)
641 static GdkPoint *points = 0;
642 static int npoints = 0;
643 int i;
644 render_priv *priv = gport->render_priv;
645 USE_GC (gc);
647 if (npoints < n_coords)
649 npoints = n_coords + 1;
650 points = (GdkPoint *)realloc (points, npoints * sizeof (GdkPoint));
652 for (i = 0; i < n_coords; i++)
654 points[i].x = Vx (x[i]);
655 points[i].y = Vy (y[i]);
657 gdk_draw_polygon (gport->drawable, priv->u_gc, 1, points, n_coords);
660 void
661 ghid_fill_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
663 gint w, h, lw, xx, yy;
664 render_priv *priv = gport->render_priv;
666 lw = gc->width;
667 w = gport->width * gport->view.coord_per_px;
668 h = gport->height * gport->view.coord_per_px;
670 if ((SIDE_X (x1) < gport->view.x0 - lw
671 && SIDE_X (x2) < gport->view.x0 - lw)
672 || (SIDE_X (x1) > gport->view.x0 + w + lw
673 && SIDE_X (x2) > gport->view.x0 + w + lw)
674 || (SIDE_Y (y1) < gport->view.y0 - lw
675 && SIDE_Y (y2) < gport->view.y0 - lw)
676 || (SIDE_Y (y1) > gport->view.y0 + h + lw
677 && SIDE_Y (y2) > gport->view.y0 + h + lw))
678 return;
680 x1 = Vx (x1);
681 y1 = Vy (y1);
682 x2 = Vx (x2);
683 y2 = Vy (y2);
684 if (x2 < x1)
686 xx = x1;
687 x1 = x2;
688 x2 = xx;
690 if (y2 < y1)
692 yy = y1;
693 y1 = y2;
694 y2 = yy;
696 USE_GC (gc);
697 gdk_draw_rectangle (gport->drawable, priv->u_gc, TRUE,
698 x1, y1, x2 - x1 + 1, y2 - y1 + 1);
701 static void
702 redraw_region (GdkRectangle *rect)
704 int eleft, eright, etop, ebottom;
705 BoxType region;
706 render_priv *priv = gport->render_priv;
708 if (!gport->pixmap)
709 return;
711 if (rect != NULL)
713 priv->clip_rect = *rect;
714 priv->clip = true;
716 else
718 priv->clip_rect.x = 0;
719 priv->clip_rect.y = 0;
720 priv->clip_rect.width = gport->width;
721 priv->clip_rect.height = gport->height;
722 priv->clip = false;
725 set_clip (priv, priv->bg_gc);
726 set_clip (priv, priv->offlimits_gc);
727 set_clip (priv, priv->mask_gc);
728 set_clip (priv, priv->grid_gc);
730 region.X1 = MIN(Px(priv->clip_rect.x),
731 Px(priv->clip_rect.x + priv->clip_rect.width + 1));
732 region.Y1 = MIN(Py(priv->clip_rect.y),
733 Py(priv->clip_rect.y + priv->clip_rect.height + 1));
734 region.X2 = MAX(Px(priv->clip_rect.x),
735 Px(priv->clip_rect.x + priv->clip_rect.width + 1));
736 region.Y2 = MAX(Py(priv->clip_rect.y),
737 Py(priv->clip_rect.y + priv->clip_rect.height + 1));
739 region.X1 = MAX (0, MIN (PCB->MaxWidth, region.X1));
740 region.X2 = MAX (0, MIN (PCB->MaxWidth, region.X2));
741 region.Y1 = MAX (0, MIN (PCB->MaxHeight, region.Y1));
742 region.Y2 = MAX (0, MIN (PCB->MaxHeight, region.Y2));
744 eleft = Vx (0);
745 eright = Vx (PCB->MaxWidth);
746 etop = Vy (0);
747 ebottom = Vy (PCB->MaxHeight);
748 if (eleft > eright)
750 int tmp = eleft;
751 eleft = eright;
752 eright = tmp;
754 if (etop > ebottom)
756 int tmp = etop;
757 etop = ebottom;
758 ebottom = tmp;
761 if (eleft > 0)
762 gdk_draw_rectangle (gport->drawable, priv->offlimits_gc,
763 1, 0, 0, eleft, gport->height);
764 else
765 eleft = 0;
766 if (eright < gport->width)
767 gdk_draw_rectangle (gport->drawable, priv->offlimits_gc,
768 1, eright, 0, gport->width - eright, gport->height);
769 else
770 eright = gport->width;
771 if (etop > 0)
772 gdk_draw_rectangle (gport->drawable, priv->offlimits_gc,
773 1, eleft, 0, eright - eleft + 1, etop);
774 else
775 etop = 0;
776 if (ebottom < gport->height)
777 gdk_draw_rectangle (gport->drawable, priv->offlimits_gc,
778 1, eleft, ebottom, eright - eleft + 1,
779 gport->height - ebottom);
780 else
781 ebottom = gport->height;
783 gdk_draw_rectangle (gport->drawable, priv->bg_gc, 1,
784 eleft, etop, eright - eleft + 1, ebottom - etop + 1);
786 ghid_draw_bg_image();
788 hid_expose_callback (&ghid_hid, &region, 0);
789 ghid_draw_grid ();
791 /* In some cases we are called with the crosshair still off */
792 if (priv->attached_invalidate_depth == 0)
793 DrawAttached ();
795 /* In some cases we are called with the mark still off */
796 if (priv->mark_invalidate_depth == 0)
797 DrawMark ();
799 draw_lead_user (priv);
801 priv->clip = false;
803 /* Rest the clip for bg_gc, as it is used outside this function */
804 gdk_gc_set_clip_mask (priv->bg_gc, NULL);
807 void
808 ghid_invalidate_lr (int left, int right, int top, int bottom)
810 int dleft, dright, dtop, dbottom;
811 int minx, maxx, miny, maxy;
812 GdkRectangle rect;
814 dleft = Vx (left);
815 dright = Vx (right);
816 dtop = Vy (top);
817 dbottom = Vy (bottom);
819 minx = MIN (dleft, dright);
820 maxx = MAX (dleft, dright);
821 miny = MIN (dtop, dbottom);
822 maxy = MAX (dtop, dbottom);
824 rect.x = minx;
825 rect.y = miny;
826 rect.width = maxx - minx;
827 rect.height = maxy - miny;
829 redraw_region (&rect);
830 ghid_screen_update ();
834 void
835 ghid_invalidate_all ()
837 redraw_region (NULL);
838 ghid_screen_update ();
841 void
842 ghid_notify_crosshair_change (bool changes_complete)
844 render_priv *priv = gport->render_priv;
846 /* We sometimes get called before the GUI is up */
847 if (gport->drawing_area == NULL)
848 return;
850 if (changes_complete)
851 priv->attached_invalidate_depth --;
853 if (priv->attached_invalidate_depth < 0)
855 priv->attached_invalidate_depth = 0;
856 /* A mismatch of changes_complete == false and == true notifications
857 * is not expected to occur, but we will try to handle it gracefully.
858 * As we know the crosshair will have been shown already, we must
859 * repaint the entire view to be sure not to leave an artaefact.
861 ghid_invalidate_all ();
862 return;
865 if (priv->attached_invalidate_depth == 0)
866 DrawAttached ();
868 if (!changes_complete)
870 priv->attached_invalidate_depth ++;
872 else if (gport->drawing_area != NULL)
874 /* Queue a GTK expose when changes are complete */
875 ghid_draw_area_update (gport, NULL);
879 void
880 ghid_notify_mark_change (bool changes_complete)
882 render_priv *priv = gport->render_priv;
884 /* We sometimes get called before the GUI is up */
885 if (gport->drawing_area == NULL)
886 return;
888 if (changes_complete)
889 priv->mark_invalidate_depth --;
891 if (priv->mark_invalidate_depth < 0)
893 priv->mark_invalidate_depth = 0;
894 /* A mismatch of changes_complete == false and == true notifications
895 * is not expected to occur, but we will try to handle it gracefully.
896 * As we know the mark will have been shown already, we must
897 * repaint the entire view to be sure not to leave an artaefact.
899 ghid_invalidate_all ();
900 return;
903 if (priv->mark_invalidate_depth == 0)
904 DrawMark ();
906 if (!changes_complete)
908 priv->mark_invalidate_depth ++;
910 else if (gport->drawing_area != NULL)
912 /* Queue a GTK expose when changes are complete */
913 ghid_draw_area_update (gport, NULL);
917 static void
918 draw_right_cross (GdkGC *xor_gc, gint x, gint y)
920 GdkWindow *window = gtk_widget_get_window (gport->drawing_area);
922 gdk_draw_line (window, xor_gc, x, 0, x, gport->height);
923 gdk_draw_line (window, xor_gc, 0, y, gport->width, y);
926 static void
927 draw_slanted_cross (GdkGC *xor_gc, gint x, gint y)
929 GdkWindow *window = gtk_widget_get_window (gport->drawing_area);
930 gint x0, y0, x1, y1;
932 x0 = x + (gport->height - y);
933 x0 = MAX(0, MIN (x0, gport->width));
934 x1 = x - y;
935 x1 = MAX(0, MIN (x1, gport->width));
936 y0 = y + (gport->width - x);
937 y0 = MAX(0, MIN (y0, gport->height));
938 y1 = y - x;
939 y1 = MAX(0, MIN (y1, gport->height));
940 gdk_draw_line (window, xor_gc, x0, y0, x1, y1);
942 x0 = x - (gport->height - y);
943 x0 = MAX(0, MIN (x0, gport->width));
944 x1 = x + y;
945 x1 = MAX(0, MIN (x1, gport->width));
946 y0 = y + x;
947 y0 = MAX(0, MIN (y0, gport->height));
948 y1 = y - (gport->width - x);
949 y1 = MAX(0, MIN (y1, gport->height));
950 gdk_draw_line (window, xor_gc, x0, y0, x1, y1);
953 static void
954 draw_dozen_cross (GdkGC *xor_gc, gint x, gint y)
956 GdkWindow *window = gtk_widget_get_window (gport->drawing_area);
957 gint x0, y0, x1, y1;
958 gdouble tan60 = sqrt (3);
960 x0 = x + (gport->height - y) / tan60;
961 x0 = MAX(0, MIN (x0, gport->width));
962 x1 = x - y / tan60;
963 x1 = MAX(0, MIN (x1, gport->width));
964 y0 = y + (gport->width - x) * tan60;
965 y0 = MAX(0, MIN (y0, gport->height));
966 y1 = y - x * tan60;
967 y1 = MAX(0, MIN (y1, gport->height));
968 gdk_draw_line (window, xor_gc, x0, y0, x1, y1);
970 x0 = x + (gport->height - y) * tan60;
971 x0 = MAX(0, MIN (x0, gport->width));
972 x1 = x - y * tan60;
973 x1 = MAX(0, MIN (x1, gport->width));
974 y0 = y + (gport->width - x) / tan60;
975 y0 = MAX(0, MIN (y0, gport->height));
976 y1 = y - x / tan60;
977 y1 = MAX(0, MIN (y1, gport->height));
978 gdk_draw_line (window, xor_gc, x0, y0, x1, y1);
980 x0 = x - (gport->height - y) / tan60;
981 x0 = MAX(0, MIN (x0, gport->width));
982 x1 = x + y / tan60;
983 x1 = MAX(0, MIN (x1, gport->width));
984 y0 = y + x * tan60;
985 y0 = MAX(0, MIN (y0, gport->height));
986 y1 = y - (gport->width - x) * tan60;
987 y1 = MAX(0, MIN (y1, gport->height));
988 gdk_draw_line (window, xor_gc, x0, y0, x1, y1);
990 x0 = x - (gport->height - y) * tan60;
991 x0 = MAX(0, MIN (x0, gport->width));
992 x1 = x + y * tan60;
993 x1 = MAX(0, MIN (x1, gport->width));
994 y0 = y + x / tan60;
995 y0 = MAX(0, MIN (y0, gport->height));
996 y1 = y - (gport->width - x) / tan60;
997 y1 = MAX(0, MIN (y1, gport->height));
998 gdk_draw_line (window, xor_gc, x0, y0, x1, y1);
1001 static void
1002 draw_crosshair (render_priv *priv)
1004 GdkWindow *window = gtk_widget_get_window (gport->drawing_area);
1005 GtkStyle *style = gtk_widget_get_style (gport->drawing_area);
1006 gint x, y;
1007 static GdkGC *xor_gc;
1008 static GdkColor cross_color;
1010 if (gport->crosshair_x < 0 || ghidgui->creating || !gport->has_entered)
1011 return;
1013 if (!xor_gc)
1015 xor_gc = gdk_gc_new (window);
1016 gdk_gc_copy (xor_gc, style->white_gc);
1017 gdk_gc_set_function (xor_gc, GDK_XOR);
1018 gdk_gc_set_clip_origin (xor_gc, 0, 0);
1019 set_clip (priv, xor_gc);
1020 /* FIXME: when CrossColor changed from config */
1021 ghid_map_color_string (Settings.CrossColor, &cross_color);
1024 gdk_gc_set_foreground (xor_gc, &cross_color);
1026 x = DRAW_X (gport->crosshair_x);
1027 y = DRAW_Y (gport->crosshair_y);
1029 draw_right_cross (xor_gc, x, y);
1030 if (Crosshair.shape == Union_Jack_Crosshair_Shape)
1031 draw_slanted_cross (xor_gc, x, y);
1032 if (Crosshair.shape == Dozen_Crosshair_Shape)
1033 draw_dozen_cross (xor_gc, x, y);
1036 void
1037 ghid_init_renderer (int *argc, char ***argv, GHidPort *port)
1039 /* Init any GC's required */
1040 port->render_priv = g_new0 (render_priv, 1);
1043 void
1044 ghid_shutdown_renderer (GHidPort *port)
1046 ghid_cancel_lead_user ();
1047 g_free (port->render_priv);
1048 port->render_priv = NULL;
1051 void
1052 ghid_init_drawing_widget (GtkWidget *widget, GHidPort *port)
1056 void
1057 ghid_drawing_area_configure_hook (GHidPort *port)
1059 static int done_once = 0;
1060 render_priv *priv = port->render_priv;
1062 if (!done_once)
1064 priv->bg_gc = gdk_gc_new (port->drawable);
1065 gdk_gc_set_foreground (priv->bg_gc, &port->bg_color);
1066 gdk_gc_set_clip_origin (priv->bg_gc, 0, 0);
1068 priv->offlimits_gc = gdk_gc_new (port->drawable);
1069 gdk_gc_set_foreground (priv->offlimits_gc, &port->offlimits_color);
1070 gdk_gc_set_clip_origin (priv->offlimits_gc, 0, 0);
1071 done_once = 1;
1074 if (port->mask)
1076 gdk_pixmap_unref (port->mask);
1077 port->mask = gdk_pixmap_new (0, port->width, port->height, 1);
1081 void
1082 ghid_screen_update (void)
1084 render_priv *priv = gport->render_priv;
1085 GdkWindow *window = gtk_widget_get_window (gport->drawing_area);
1087 if (gport->pixmap == NULL)
1088 return;
1090 gdk_draw_drawable (window, priv->bg_gc, gport->pixmap,
1091 0, 0, 0, 0, gport->width, gport->height);
1092 draw_crosshair (priv);
1095 gboolean
1096 ghid_drawing_area_expose_cb (GtkWidget *widget,
1097 GdkEventExpose *ev,
1098 GHidPort *port)
1100 render_priv *priv = port->render_priv;
1101 GdkWindow *window = gtk_widget_get_window (gport->drawing_area);
1103 gdk_draw_drawable (window, priv->bg_gc, port->pixmap,
1104 ev->area.x, ev->area.y, ev->area.x, ev->area.y,
1105 ev->area.width, ev->area.height);
1106 draw_crosshair (priv);
1107 return FALSE;
1110 void
1111 ghid_port_drawing_realize_cb (GtkWidget *widget, gpointer data)
1115 gboolean
1116 ghid_pinout_preview_expose (GtkWidget *widget,
1117 GdkEventExpose *ev)
1119 GhidPinoutPreview *pinout = GHID_PINOUT_PREVIEW (widget);
1120 GdkWindow *window = gtk_widget_get_window (widget);
1121 GdkDrawable *save_drawable;
1122 GtkAllocation allocation;
1123 view_data save_view;
1124 int save_width, save_height;
1125 Coord save_max_width;
1126 Coord save_max_height;
1127 double xz, yz;
1128 render_priv *priv = gport->render_priv;
1130 /* Setup drawable and zoom factor for drawing routines
1132 save_drawable = gport->drawable;
1133 save_view = gport->view;
1134 save_width = gport->width;
1135 save_height = gport->height;
1136 save_max_width = PCB->MaxWidth;
1137 save_max_height = PCB->MaxHeight;
1139 gtk_widget_get_allocation (widget, &allocation);
1140 xz = (double) pinout->x_max / allocation.width;
1141 yz = (double) pinout->y_max / allocation.height;
1142 if (xz > yz)
1143 gport->view.coord_per_px = xz;
1144 else
1145 gport->view.coord_per_px = yz;
1147 gport->drawable = window;
1148 gport->width = allocation.width;
1149 gport->height = allocation.height;
1150 gport->view.width = allocation.width * gport->view.coord_per_px;
1151 gport->view.height = allocation.height * gport->view.coord_per_px;
1152 gport->view.x0 = (pinout->x_max - gport->view.width) / 2;
1153 gport->view.y0 = (pinout->y_max - gport->view.height) / 2;
1154 PCB->MaxWidth = pinout->x_max;
1155 PCB->MaxHeight = pinout->y_max;
1157 /* clear background */
1158 gdk_draw_rectangle (window, priv->bg_gc, TRUE,
1159 0, 0, allocation.width, allocation.height);
1161 /* call the drawing routine */
1162 hid_expose_callback (&ghid_hid, NULL, &pinout->element);
1164 gport->drawable = save_drawable;
1165 gport->view = save_view;
1166 gport->width = save_width;
1167 gport->height = save_height;
1168 PCB->MaxWidth = save_max_width;
1169 PCB->MaxHeight = save_max_height;
1171 return FALSE;
1174 GdkPixmap *
1175 ghid_render_pixmap (int cx, int cy, double zoom, int width, int height, int depth)
1177 GdkPixmap *pixmap;
1178 GdkDrawable *save_drawable;
1179 view_data save_view;
1180 int save_width, save_height;
1181 BoxType region;
1182 render_priv *priv = gport->render_priv;
1184 save_drawable = gport->drawable;
1185 save_view = gport->view;
1186 save_width = gport->width;
1187 save_height = gport->height;
1189 pixmap = gdk_pixmap_new (NULL, width, height, depth);
1191 /* Setup drawable and zoom factor for drawing routines
1194 gport->drawable = pixmap;
1195 gport->view.coord_per_px = zoom;
1196 gport->width = width;
1197 gport->height = height;
1198 gport->view.width = width * gport->view.coord_per_px;
1199 gport->view.height = height * gport->view.coord_per_px;
1200 gport->view.x0 = gport->view.flip_x ? PCB->MaxWidth - cx : cx;
1201 gport->view.x0 -= gport->view.height / 2;
1202 gport->view.y0 = gport->view.flip_y ? PCB->MaxHeight - cy : cy;
1203 gport->view.y0 -= gport->view.width / 2;
1205 /* clear background */
1206 gdk_draw_rectangle (pixmap, priv->bg_gc, TRUE, 0, 0, width, height);
1208 /* call the drawing routine */
1209 region.X1 = MIN(Px(0), Px(gport->width + 1));
1210 region.Y1 = MIN(Py(0), Py(gport->height + 1));
1211 region.X2 = MAX(Px(0), Px(gport->width + 1));
1212 region.Y2 = MAX(Py(0), Py(gport->height + 1));
1214 region.X1 = MAX (0, MIN (PCB->MaxWidth, region.X1));
1215 region.X2 = MAX (0, MIN (PCB->MaxWidth, region.X2));
1216 region.Y1 = MAX (0, MIN (PCB->MaxHeight, region.Y1));
1217 region.Y2 = MAX (0, MIN (PCB->MaxHeight, region.Y2));
1219 hid_expose_callback (&ghid_hid, &region, NULL);
1221 gport->drawable = save_drawable;
1222 gport->view = save_view;
1223 gport->width = save_width;
1224 gport->height = save_height;
1226 return pixmap;
1229 HID *
1230 ghid_request_debug_draw (void)
1232 /* No special setup requirements, drawing goes into
1233 * the backing pixmap. */
1234 return &ghid_hid;
1237 void
1238 ghid_flush_debug_draw (void)
1240 ghid_screen_update ();
1241 gdk_flush ();
1244 void
1245 ghid_finish_debug_draw (void)
1247 ghid_flush_debug_draw ();
1248 /* No special tear down requirements
1252 bool
1253 ghid_event_to_pcb_coords (int event_x, int event_y, Coord *pcb_x, Coord *pcb_y)
1255 *pcb_x = EVENT_TO_PCB_X (event_x);
1256 *pcb_y = EVENT_TO_PCB_Y (event_y);
1258 return true;
1261 bool
1262 ghid_pcb_to_event_coords (Coord pcb_x, Coord pcb_y, int *event_x, int *event_y)
1264 *event_x = DRAW_X (pcb_x);
1265 *event_y = DRAW_Y (pcb_y);
1267 return true;
1271 #define LEAD_USER_WIDTH 0.2 /* millimeters */
1272 #define LEAD_USER_PERIOD (1000 / 5) /* 5fps (in ms) */
1273 #define LEAD_USER_VELOCITY 3. /* millimeters per second */
1274 #define LEAD_USER_ARC_COUNT 3
1275 #define LEAD_USER_ARC_SEPARATION 3. /* millimeters */
1276 #define LEAD_USER_INITIAL_RADIUS 10. /* millimetres */
1277 #define LEAD_USER_COLOR_R 1.
1278 #define LEAD_USER_COLOR_G 1.
1279 #define LEAD_USER_COLOR_B 0.
1281 static void
1282 draw_lead_user (render_priv *priv)
1284 GdkWindow *window = gtk_widget_get_window (gport->drawing_area);
1285 GtkStyle *style = gtk_widget_get_style (gport->drawing_area);
1286 int i;
1287 Coord radius = priv->lead_user_radius;
1288 Coord width = MM_TO_COORD (LEAD_USER_WIDTH);
1289 Coord separation = MM_TO_COORD (LEAD_USER_ARC_SEPARATION);
1290 static GdkGC *lead_gc = NULL;
1291 GdkColor lead_color;
1293 if (!priv->lead_user)
1294 return;
1296 if (lead_gc == NULL)
1298 lead_gc = gdk_gc_new (window);
1299 gdk_gc_copy (lead_gc, style->white_gc);
1300 gdk_gc_set_function (lead_gc, GDK_XOR);
1301 gdk_gc_set_clip_origin (lead_gc, 0, 0);
1302 lead_color.pixel = 0;
1303 lead_color.red = (int)(65535. * LEAD_USER_COLOR_R);
1304 lead_color.green = (int)(65535. * LEAD_USER_COLOR_G);
1305 lead_color.blue = (int)(65535. * LEAD_USER_COLOR_B);
1306 gdk_color_alloc (gport->colormap, &lead_color);
1307 gdk_gc_set_foreground (lead_gc, &lead_color);
1310 set_clip (priv, lead_gc);
1311 gdk_gc_set_line_attributes (lead_gc, Vz (width),
1312 GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1314 /* arcs at the approrpriate radii */
1316 for (i = 0; i < LEAD_USER_ARC_COUNT; i++, radius -= separation)
1318 if (radius < width)
1319 radius += MM_TO_COORD (LEAD_USER_INITIAL_RADIUS);
1321 /* Draw an arc at radius */
1322 gdk_draw_arc (gport->drawable, lead_gc, FALSE,
1323 Vx (priv->lead_user_x - radius),
1324 Vy (priv->lead_user_y - radius),
1325 Vz (2. * radius), Vz (2. * radius),
1326 0, 360 * 64);
1330 gboolean
1331 lead_user_cb (gpointer data)
1333 render_priv *priv = data;
1334 Coord step;
1335 double elapsed_time;
1337 /* Queue a redraw */
1338 ghid_invalidate_all ();
1340 /* Update radius */
1341 elapsed_time = g_timer_elapsed (priv->lead_user_timer, NULL);
1342 g_timer_start (priv->lead_user_timer);
1344 step = MM_TO_COORD (LEAD_USER_VELOCITY * elapsed_time);
1345 if (priv->lead_user_radius > step)
1346 priv->lead_user_radius -= step;
1347 else
1348 priv->lead_user_radius = MM_TO_COORD (LEAD_USER_INITIAL_RADIUS);
1350 return TRUE;
1353 void
1354 ghid_lead_user_to_location (Coord x, Coord y)
1356 render_priv *priv = gport->render_priv;
1358 ghid_cancel_lead_user ();
1360 priv->lead_user = true;
1361 priv->lead_user_x = x;
1362 priv->lead_user_y = y;
1363 priv->lead_user_radius = MM_TO_COORD (LEAD_USER_INITIAL_RADIUS);
1364 priv->lead_user_timeout = g_timeout_add (LEAD_USER_PERIOD, lead_user_cb, priv);
1365 priv->lead_user_timer = g_timer_new ();
1368 void
1369 ghid_cancel_lead_user (void)
1371 render_priv *priv = gport->render_priv;
1373 if (priv->lead_user_timeout)
1374 g_source_remove (priv->lead_user_timeout);
1376 if (priv->lead_user_timer)
1377 g_timer_destroy (priv->lead_user_timer);
1379 if (priv->lead_user)
1380 ghid_invalidate_all ();
1382 priv->lead_user_timeout = 0;
1383 priv->lead_user_timer = NULL;
1384 priv->lead_user = false;