Pass an explicit graphics context to DrawAttached and DrawMark
[geda-pcb/pcjc2.git] / src / hid / gtk / gtkhid-gdk.c
blobdcaf26dcc6aa5b00d4fe446ca1a294ab65141235
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 hidGC crosshair_gc;
46 } render_priv;
49 typedef struct hid_gc_struct
51 HID *me_pointer;
52 GdkGC *gc;
54 gchar *colorname;
55 Coord width;
56 gint cap, join;
57 gchar xor_mask;
58 gint mask_seq;
60 hid_gc_struct;
63 static void draw_lead_user (render_priv *priv);
66 int
67 ghid_set_layer (const char *name, int group, int empty)
69 int idx = group;
70 if (idx >= 0 && idx < max_group)
72 int n = PCB->LayerGroups.Number[group];
73 for (idx = 0; idx < n-1; idx ++)
75 int ni = PCB->LayerGroups.Entries[group][idx];
76 if (ni >= 0 && ni < max_copper_layer + 2
77 && PCB->Data->Layer[ni].On)
78 break;
80 idx = PCB->LayerGroups.Entries[group][idx];
83 if (idx >= 0 && idx < max_copper_layer + 2)
84 return /*pinout ? 1 : */ PCB->Data->Layer[idx].On;
85 if (idx < 0)
87 switch (SL_TYPE (idx))
89 case SL_INVISIBLE:
90 return /* pinout ? 0 : */ PCB->InvisibleObjectsOn;
91 case SL_MASK:
92 if (SL_MYSIDE (idx) /*&& !pinout */ )
93 return TEST_FLAG (SHOWMASKFLAG, PCB);
94 return 0;
95 case SL_SILK:
96 if (SL_MYSIDE (idx) /*|| pinout */ )
97 return PCB->ElementOn;
98 return 0;
99 case SL_ASSY:
100 return 0;
101 case SL_PDRILL:
102 case SL_UDRILL:
103 return 1;
104 case SL_RATS:
105 return PCB->RatOn;
108 return 0;
111 void
112 ghid_destroy_gc (hidGC gc)
114 if (gc->gc)
115 g_object_unref (gc->gc);
116 g_free (gc);
119 hidGC
120 ghid_make_gc (void)
122 hidGC rv;
124 rv = g_new0 (hid_gc_struct, 1);
125 rv->me_pointer = &ghid_hid;
126 rv->colorname = Settings.BackgroundColor;
127 return rv;
130 static void
131 set_clip (render_priv *priv, GdkGC *gc)
133 if (gc == NULL)
134 return;
136 if (priv->clip)
137 gdk_gc_set_clip_rectangle (gc, &priv->clip_rect);
138 else
139 gdk_gc_set_clip_mask (gc, NULL);
142 static void
143 ghid_draw_grid (void)
145 static GdkPoint *points = 0;
146 static int npoints = 0;
147 Coord x1, y1, x2, y2, x, y;
148 int n, i;
149 render_priv *priv = gport->render_priv;
151 if (!Settings.DrawGrid)
152 return;
153 if (Vz (PCB->Grid) < MIN_GRID_DISTANCE)
154 return;
155 if (!priv->grid_gc)
157 if (gdk_color_parse (Settings.GridColor, &gport->grid_color))
159 gport->grid_color.red ^= gport->bg_color.red;
160 gport->grid_color.green ^= gport->bg_color.green;
161 gport->grid_color.blue ^= gport->bg_color.blue;
162 gdk_color_alloc (gport->colormap, &gport->grid_color);
164 priv->grid_gc = gdk_gc_new (gport->drawable);
165 gdk_gc_set_function (priv->grid_gc, GDK_XOR);
166 gdk_gc_set_foreground (priv->grid_gc, &gport->grid_color);
167 gdk_gc_set_clip_origin (priv->grid_gc, 0, 0);
168 set_clip (priv, priv->grid_gc);
170 x1 = GridFit (SIDE_X (gport->view.x0), PCB->Grid, PCB->GridOffsetX);
171 y1 = GridFit (SIDE_Y (gport->view.y0), PCB->Grid, PCB->GridOffsetY);
172 x2 = GridFit (SIDE_X (gport->view.x0 + gport->view.width - 1),
173 PCB->Grid, PCB->GridOffsetX);
174 y2 = GridFit (SIDE_Y (gport->view.y0 + gport->view.height - 1),
175 PCB->Grid, PCB->GridOffsetY);
176 if (x1 > x2)
178 Coord tmp = x1;
179 x1 = x2;
180 x2 = tmp;
182 if (y1 > y2)
184 Coord tmp = y1;
185 y1 = y2;
186 y2 = tmp;
188 if (Vx (x1) < 0)
189 x1 += PCB->Grid;
190 if (Vy (y1) < 0)
191 y1 += PCB->Grid;
192 if (Vx (x2) >= gport->width)
193 x2 -= PCB->Grid;
194 if (Vy (y2) >= gport->height)
195 y2 -= PCB->Grid;
196 n = (x2 - x1) / PCB->Grid + 1;
197 if (n > npoints)
199 npoints = n + 10;
200 points = (GdkPoint *)realloc (points, npoints * sizeof (GdkPoint));
202 n = 0;
203 for (x = x1; x <= x2; x += PCB->Grid)
205 points[n].x = Vx (x);
206 n++;
208 if (n == 0)
209 return;
210 for (y = y1; y <= y2; y += PCB->Grid)
212 for (i = 0; i < n; i++)
213 points[i].y = Vy (y);
214 gdk_draw_points (gport->drawable, priv->grid_gc, points, n);
218 /* ------------------------------------------------------------ */
219 static void
220 ghid_draw_bg_image (void)
222 static GdkPixbuf *pixbuf;
223 GdkInterpType interp_type;
224 gint x, y, w, h, w_src, h_src;
225 static gint w_scaled, h_scaled;
226 render_priv *priv = gport->render_priv;
228 if (!ghidgui->bg_pixbuf)
229 return;
231 w = PCB->MaxWidth / gport->view.coord_per_px;
232 h = PCB->MaxHeight / gport->view.coord_per_px;
233 x = gport->view.x0 / gport->view.coord_per_px;
234 y = gport->view.y0 / gport->view.coord_per_px;
236 if (w_scaled != w || h_scaled != h)
238 if (pixbuf)
239 g_object_unref (G_OBJECT (pixbuf));
241 w_src = gdk_pixbuf_get_width (ghidgui->bg_pixbuf);
242 h_src = gdk_pixbuf_get_height (ghidgui->bg_pixbuf);
243 if (w > w_src && h > h_src)
244 interp_type = GDK_INTERP_NEAREST;
245 else
246 interp_type = GDK_INTERP_BILINEAR;
248 pixbuf =
249 gdk_pixbuf_scale_simple (ghidgui->bg_pixbuf, w, h, interp_type);
250 w_scaled = w;
251 h_scaled = h;
253 if (pixbuf)
254 gdk_pixbuf_render_to_drawable (pixbuf, gport->drawable, priv->bg_gc,
255 x, y, 0, 0,
256 w - x, h - y, GDK_RGB_DITHER_NORMAL, 0, 0);
259 #define WHICH_GC(gc) (cur_mask == HID_MASK_CLEAR ? priv->mask_gc : (gc)->gc)
261 void
262 ghid_use_mask (enum mask_mode mode)
264 static int mask_seq_id = 0;
265 GdkColor color;
266 render_priv *priv = gport->render_priv;
268 if (!gport->pixmap)
269 return;
270 if (mode == cur_mask)
271 return;
272 switch (mode)
274 case HID_MASK_OFF:
275 gport->drawable = gport->pixmap;
276 mask_seq = 0;
277 break;
279 case HID_MASK_BEFORE:
280 /* The HID asks not to receive this mask type, so warn if we get it */
281 g_return_if_reached ();
283 case HID_MASK_CLEAR:
284 if (!gport->mask)
285 gport->mask = gdk_pixmap_new (0, gport->width, gport->height, 1);
286 gport->drawable = gport->mask;
287 mask_seq = 0;
288 if (!priv->mask_gc)
290 priv->mask_gc = gdk_gc_new (gport->drawable);
291 gdk_gc_set_clip_origin (priv->mask_gc, 0, 0);
292 set_clip (priv, priv->mask_gc);
294 color.pixel = 1;
295 gdk_gc_set_foreground (priv->mask_gc, &color);
296 gdk_draw_rectangle (gport->drawable, priv->mask_gc, TRUE, 0, 0,
297 gport->width, gport->height);
298 color.pixel = 0;
299 gdk_gc_set_foreground (priv->mask_gc, &color);
300 break;
302 case HID_MASK_AFTER:
303 mask_seq_id++;
304 if (!mask_seq_id)
305 mask_seq_id = 1;
306 mask_seq = mask_seq_id;
308 gport->drawable = gport->pixmap;
309 break;
312 cur_mask = mode;
316 typedef struct
318 int color_set;
319 GdkColor color;
320 int xor_set;
321 GdkColor xor_color;
322 } ColorCache;
325 /* Config helper functions for when the user changes color preferences.
326 | set_special colors used in the gtkhid.
328 static void
329 set_special_grid_color (void)
331 render_priv *priv = gport->render_priv;
333 if (!gport->colormap)
334 return;
335 gport->grid_color.red ^= gport->bg_color.red;
336 gport->grid_color.green ^= gport->bg_color.green;
337 gport->grid_color.blue ^= gport->bg_color.blue;
338 gdk_color_alloc (gport->colormap, &gport->grid_color);
339 if (priv->grid_gc)
340 gdk_gc_set_foreground (priv->grid_gc, &gport->grid_color);
343 void
344 ghid_set_special_colors (HID_Attribute * ha)
346 render_priv *priv = gport->render_priv;
348 if (!ha->name || !ha->value)
349 return;
350 if (!strcmp (ha->name, "background-color") && priv->bg_gc)
352 ghid_map_color_string (*(char **) ha->value, &gport->bg_color);
353 gdk_gc_set_foreground (priv->bg_gc, &gport->bg_color);
354 set_special_grid_color ();
356 else if (!strcmp (ha->name, "off-limit-color") && priv->offlimits_gc)
358 ghid_map_color_string (*(char **) ha->value, &gport->offlimits_color);
359 gdk_gc_set_foreground (priv->offlimits_gc, &gport->offlimits_color);
361 else if (!strcmp (ha->name, "grid-color") && priv->grid_gc)
363 ghid_map_color_string (*(char **) ha->value, &gport->grid_color);
364 set_special_grid_color ();
368 void
369 ghid_set_color (hidGC gc, const char *name)
371 static void *cache = 0;
372 hidval cval;
374 if (name == NULL)
376 fprintf (stderr, "%s(): name = NULL, setting to magenta\n",
377 __FUNCTION__);
378 name = "magenta";
381 gc->colorname = (char *) name;
382 if (!gc->gc)
383 return;
384 if (gport->colormap == 0)
385 gport->colormap = gtk_widget_get_colormap (gport->top_window);
387 if (strcmp (name, "erase") == 0)
389 gdk_gc_set_foreground (gc->gc, &gport->bg_color);
391 else if (strcmp (name, "drill") == 0)
393 gdk_gc_set_foreground (gc->gc, &gport->offlimits_color);
395 else
397 ColorCache *cc;
398 if (hid_cache_color (0, name, &cval, &cache))
399 cc = (ColorCache *) cval.ptr;
400 else
402 cc = (ColorCache *) malloc (sizeof (ColorCache));
403 memset (cc, 0, sizeof (*cc));
404 cval.ptr = cc;
405 hid_cache_color (1, name, &cval, &cache);
408 if (!cc->color_set)
410 if (gdk_color_parse (name, &cc->color))
411 gdk_color_alloc (gport->colormap, &cc->color);
412 else
413 gdk_color_white (gport->colormap, &cc->color);
414 cc->color_set = 1;
416 if (gc->xor_mask)
418 if (!cc->xor_set)
420 cc->xor_color.red = cc->color.red ^ gport->bg_color.red;
421 cc->xor_color.green = cc->color.green ^ gport->bg_color.green;
422 cc->xor_color.blue = cc->color.blue ^ gport->bg_color.blue;
423 gdk_color_alloc (gport->colormap, &cc->xor_color);
424 cc->xor_set = 1;
426 gdk_gc_set_foreground (gc->gc, &cc->xor_color);
428 else
430 gdk_gc_set_foreground (gc->gc, &cc->color);
435 void
436 ghid_set_line_cap (hidGC gc, EndCapStyle style)
438 render_priv *priv = gport->render_priv;
440 switch (style)
442 case Trace_Cap:
443 case Round_Cap:
444 gc->cap = GDK_CAP_ROUND;
445 gc->join = GDK_JOIN_ROUND;
446 break;
447 case Square_Cap:
448 case Beveled_Cap:
449 gc->cap = GDK_CAP_PROJECTING;
450 gc->join = GDK_JOIN_MITER;
451 break;
453 if (gc->gc)
454 gdk_gc_set_line_attributes (WHICH_GC (gc),
455 Vz (gc->width), GDK_LINE_SOLID,
456 (GdkCapStyle)gc->cap, (GdkJoinStyle)gc->join);
459 void
460 ghid_set_line_width (hidGC gc, Coord width)
462 render_priv *priv = gport->render_priv;
464 gc->width = width;
465 if (gc->gc)
466 gdk_gc_set_line_attributes (WHICH_GC (gc),
467 Vz (gc->width), GDK_LINE_SOLID,
468 (GdkCapStyle)gc->cap, (GdkJoinStyle)gc->join);
471 void
472 ghid_set_draw_xor (hidGC gc, int xor_mask)
474 gc->xor_mask = xor_mask;
475 if (!gc->gc)
476 return;
477 gdk_gc_set_function (gc->gc, xor_mask ? GDK_XOR : GDK_COPY);
478 ghid_set_color (gc, gc->colorname);
481 static int
482 use_gc (hidGC gc)
484 render_priv *priv = gport->render_priv;
485 GdkWindow *window = gtk_widget_get_window (gport->top_window);
487 if (gc->me_pointer != &ghid_hid)
489 fprintf (stderr, "Fatal: GC from another HID passed to GTK HID\n");
490 abort ();
493 if (!gport->pixmap)
494 return 0;
495 if (!gc->gc)
497 gc->gc = gdk_gc_new (window);
498 ghid_set_color (gc, gc->colorname);
499 ghid_set_line_width (gc, gc->width);
500 ghid_set_line_cap (gc, (EndCapStyle)gc->cap);
501 ghid_set_draw_xor (gc, gc->xor_mask);
502 gdk_gc_set_clip_origin (gc->gc, 0, 0);
504 if (gc->mask_seq != mask_seq)
506 if (mask_seq)
507 gdk_gc_set_clip_mask (gc->gc, gport->mask);
508 else
509 set_clip (priv, gc->gc);
510 gc->mask_seq = mask_seq;
512 priv->u_gc = WHICH_GC (gc);
513 return 1;
516 void
517 ghid_draw_line (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
519 double dx1, dy1, dx2, dy2;
520 render_priv *priv = gport->render_priv;
522 dx1 = Vx ((double) x1);
523 dy1 = Vy ((double) y1);
524 dx2 = Vx ((double) x2);
525 dy2 = Vy ((double) y2);
527 if (!ClipLine (0, 0, gport->width, gport->height,
528 &dx1, &dy1, &dx2, &dy2, gc->width / gport->view.coord_per_px))
529 return;
531 USE_GC (gc);
532 gdk_draw_line (gport->drawable, priv->u_gc, dx1, dy1, dx2, dy2);
535 void
536 ghid_draw_arc (hidGC gc, Coord cx, Coord cy,
537 Coord xradius, Coord yradius, Angle start_angle, Angle delta_angle)
539 gint vrx, vry;
540 gint w, h, radius;
541 render_priv *priv = gport->render_priv;
543 w = gport->width * gport->view.coord_per_px;
544 h = gport->height * gport->view.coord_per_px;
545 radius = (xradius > yradius) ? xradius : yradius;
546 if (SIDE_X (cx) < gport->view.x0 - radius
547 || SIDE_X (cx) > gport->view.x0 + w + radius
548 || SIDE_Y (cy) < gport->view.y0 - radius
549 || SIDE_Y (cy) > gport->view.y0 + h + radius)
550 return;
552 USE_GC (gc);
553 vrx = Vz (xradius);
554 vry = Vz (yradius);
556 if (gport->view.flip_x)
558 start_angle = 180 - start_angle;
559 delta_angle = -delta_angle;
561 if (gport->view.flip_y)
563 start_angle = -start_angle;
564 delta_angle = -delta_angle;
566 /* make sure we fall in the -180 to +180 range */
567 start_angle = NormalizeAngle (start_angle);
568 if (start_angle >= 180) start_angle -= 360;
570 gdk_draw_arc (gport->drawable, priv->u_gc, 0,
571 Vx (cx) - vrx, Vy (cy) - vry,
572 vrx * 2, vry * 2, (start_angle + 180) * 64, delta_angle * 64);
575 void
576 ghid_draw_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
578 gint w, h, lw;
579 render_priv *priv = gport->render_priv;
581 lw = gc->width;
582 w = gport->width * gport->view.coord_per_px;
583 h = gport->height * gport->view.coord_per_px;
585 if ((SIDE_X (x1) < gport->view.x0 - lw
586 && SIDE_X (x2) < gport->view.x0 - lw)
587 || (SIDE_X (x1) > gport->view.x0 + w + lw
588 && SIDE_X (x2) > gport->view.x0 + w + lw)
589 || (SIDE_Y (y1) < gport->view.y0 - lw
590 && SIDE_Y (y2) < gport->view.y0 - lw)
591 || (SIDE_Y (y1) > gport->view.y0 + h + lw
592 && SIDE_Y (y2) > gport->view.y0 + h + lw))
593 return;
595 x1 = Vx (x1);
596 y1 = Vy (y1);
597 x2 = Vx (x2);
598 y2 = Vy (y2);
600 if (x1 > x2)
602 gint xt = x1;
603 x1 = x2;
604 x2 = xt;
606 if (y1 > y2)
608 gint yt = y1;
609 y1 = y2;
610 y2 = yt;
613 USE_GC (gc);
614 gdk_draw_rectangle (gport->drawable, priv->u_gc, FALSE,
615 x1, y1, x2 - x1 + 1, y2 - y1 + 1);
619 void
620 ghid_fill_circle (hidGC gc, Coord cx, Coord cy, Coord radius)
622 gint w, h, vr;
623 render_priv *priv = gport->render_priv;
625 w = gport->width * gport->view.coord_per_px;
626 h = gport->height * gport->view.coord_per_px;
627 if (SIDE_X (cx) < gport->view.x0 - radius
628 || SIDE_X (cx) > gport->view.x0 + w + radius
629 || SIDE_Y (cy) < gport->view.y0 - radius
630 || SIDE_Y (cy) > gport->view.y0 + h + radius)
631 return;
633 USE_GC (gc);
634 vr = Vz (radius);
635 gdk_draw_arc (gport->drawable, priv->u_gc, TRUE,
636 Vx (cx) - vr, Vy (cy) - vr, vr * 2, vr * 2, 0, 360 * 64);
639 void
640 ghid_fill_polygon (hidGC gc, int n_coords, Coord *x, Coord *y)
642 static GdkPoint *points = 0;
643 static int npoints = 0;
644 int i;
645 render_priv *priv = gport->render_priv;
646 USE_GC (gc);
648 if (npoints < n_coords)
650 npoints = n_coords + 1;
651 points = (GdkPoint *)realloc (points, npoints * sizeof (GdkPoint));
653 for (i = 0; i < n_coords; i++)
655 points[i].x = Vx (x[i]);
656 points[i].y = Vy (y[i]);
658 gdk_draw_polygon (gport->drawable, priv->u_gc, 1, points, n_coords);
661 void
662 ghid_fill_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
664 gint w, h, lw, xx, yy;
665 render_priv *priv = gport->render_priv;
667 lw = gc->width;
668 w = gport->width * gport->view.coord_per_px;
669 h = gport->height * gport->view.coord_per_px;
671 if ((SIDE_X (x1) < gport->view.x0 - lw
672 && SIDE_X (x2) < gport->view.x0 - lw)
673 || (SIDE_X (x1) > gport->view.x0 + w + lw
674 && SIDE_X (x2) > gport->view.x0 + w + lw)
675 || (SIDE_Y (y1) < gport->view.y0 - lw
676 && SIDE_Y (y2) < gport->view.y0 - lw)
677 || (SIDE_Y (y1) > gport->view.y0 + h + lw
678 && SIDE_Y (y2) > gport->view.y0 + h + lw))
679 return;
681 x1 = Vx (x1);
682 y1 = Vy (y1);
683 x2 = Vx (x2);
684 y2 = Vy (y2);
685 if (x2 < x1)
687 xx = x1;
688 x1 = x2;
689 x2 = xx;
691 if (y2 < y1)
693 yy = y1;
694 y1 = y2;
695 y2 = yy;
697 USE_GC (gc);
698 gdk_draw_rectangle (gport->drawable, priv->u_gc, TRUE,
699 x1, y1, x2 - x1 + 1, y2 - y1 + 1);
702 static void
703 redraw_region (GdkRectangle *rect)
705 int eleft, eright, etop, ebottom;
706 BoxType region;
707 render_priv *priv = gport->render_priv;
709 if (!gport->pixmap)
710 return;
712 if (rect != NULL)
714 priv->clip_rect = *rect;
715 priv->clip = true;
717 else
719 priv->clip_rect.x = 0;
720 priv->clip_rect.y = 0;
721 priv->clip_rect.width = gport->width;
722 priv->clip_rect.height = gport->height;
723 priv->clip = false;
726 set_clip (priv, priv->bg_gc);
727 set_clip (priv, priv->offlimits_gc);
728 set_clip (priv, priv->mask_gc);
729 set_clip (priv, priv->grid_gc);
731 region.X1 = MIN(Px(priv->clip_rect.x),
732 Px(priv->clip_rect.x + priv->clip_rect.width + 1));
733 region.Y1 = MIN(Py(priv->clip_rect.y),
734 Py(priv->clip_rect.y + priv->clip_rect.height + 1));
735 region.X2 = MAX(Px(priv->clip_rect.x),
736 Px(priv->clip_rect.x + priv->clip_rect.width + 1));
737 region.Y2 = MAX(Py(priv->clip_rect.y),
738 Py(priv->clip_rect.y + priv->clip_rect.height + 1));
740 region.X1 = MAX (0, MIN (PCB->MaxWidth, region.X1));
741 region.X2 = MAX (0, MIN (PCB->MaxWidth, region.X2));
742 region.Y1 = MAX (0, MIN (PCB->MaxHeight, region.Y1));
743 region.Y2 = MAX (0, MIN (PCB->MaxHeight, region.Y2));
745 eleft = Vx (0);
746 eright = Vx (PCB->MaxWidth);
747 etop = Vy (0);
748 ebottom = Vy (PCB->MaxHeight);
749 if (eleft > eright)
751 int tmp = eleft;
752 eleft = eright;
753 eright = tmp;
755 if (etop > ebottom)
757 int tmp = etop;
758 etop = ebottom;
759 ebottom = tmp;
762 if (eleft > 0)
763 gdk_draw_rectangle (gport->drawable, priv->offlimits_gc,
764 1, 0, 0, eleft, gport->height);
765 else
766 eleft = 0;
767 if (eright < gport->width)
768 gdk_draw_rectangle (gport->drawable, priv->offlimits_gc,
769 1, eright, 0, gport->width - eright, gport->height);
770 else
771 eright = gport->width;
772 if (etop > 0)
773 gdk_draw_rectangle (gport->drawable, priv->offlimits_gc,
774 1, eleft, 0, eright - eleft + 1, etop);
775 else
776 etop = 0;
777 if (ebottom < gport->height)
778 gdk_draw_rectangle (gport->drawable, priv->offlimits_gc,
779 1, eleft, ebottom, eright - eleft + 1,
780 gport->height - ebottom);
781 else
782 ebottom = gport->height;
784 gdk_draw_rectangle (gport->drawable, priv->bg_gc, 1,
785 eleft, etop, eright - eleft + 1, ebottom - etop + 1);
787 ghid_draw_bg_image();
789 hid_expose_callback (&ghid_hid, &region, 0);
790 ghid_draw_grid ();
792 /* In some cases we are called with the crosshair still off */
793 if (priv->attached_invalidate_depth == 0)
794 DrawAttached (priv->crosshair_gc);
796 /* In some cases we are called with the mark still off */
797 if (priv->mark_invalidate_depth == 0)
798 DrawMark (priv->crosshair_gc);
800 draw_lead_user (priv);
802 priv->clip = false;
804 /* Rest the clip for bg_gc, as it is used outside this function */
805 gdk_gc_set_clip_mask (priv->bg_gc, NULL);
808 void
809 ghid_invalidate_lr (int left, int right, int top, int bottom)
811 int dleft, dright, dtop, dbottom;
812 int minx, maxx, miny, maxy;
813 GdkRectangle rect;
815 dleft = Vx (left);
816 dright = Vx (right);
817 dtop = Vy (top);
818 dbottom = Vy (bottom);
820 minx = MIN (dleft, dright);
821 maxx = MAX (dleft, dright);
822 miny = MIN (dtop, dbottom);
823 maxy = MAX (dtop, dbottom);
825 rect.x = minx;
826 rect.y = miny;
827 rect.width = maxx - minx;
828 rect.height = maxy - miny;
830 redraw_region (&rect);
831 ghid_screen_update ();
835 void
836 ghid_invalidate_all ()
838 redraw_region (NULL);
839 ghid_screen_update ();
842 void
843 ghid_notify_crosshair_change (bool changes_complete)
845 render_priv *priv = gport->render_priv;
847 /* We sometimes get called before the GUI is up */
848 if (gport->drawing_area == NULL)
849 return;
851 if (changes_complete)
852 priv->attached_invalidate_depth --;
854 if (priv->attached_invalidate_depth < 0)
856 priv->attached_invalidate_depth = 0;
857 /* A mismatch of changes_complete == false and == true notifications
858 * is not expected to occur, but we will try to handle it gracefully.
859 * As we know the crosshair will have been shown already, we must
860 * repaint the entire view to be sure not to leave an artaefact.
862 ghid_invalidate_all ();
863 return;
866 if (priv->attached_invalidate_depth == 0)
867 DrawAttached (priv->crosshair_gc);
869 if (!changes_complete)
871 priv->attached_invalidate_depth ++;
873 else if (gport->drawing_area != NULL)
875 /* Queue a GTK expose when changes are complete */
876 ghid_draw_area_update (gport, NULL);
880 void
881 ghid_notify_mark_change (bool changes_complete)
883 render_priv *priv = gport->render_priv;
885 /* We sometimes get called before the GUI is up */
886 if (gport->drawing_area == NULL)
887 return;
889 if (changes_complete)
890 priv->mark_invalidate_depth --;
892 if (priv->mark_invalidate_depth < 0)
894 priv->mark_invalidate_depth = 0;
895 /* A mismatch of changes_complete == false and == true notifications
896 * is not expected to occur, but we will try to handle it gracefully.
897 * As we know the mark will have been shown already, we must
898 * repaint the entire view to be sure not to leave an artaefact.
900 ghid_invalidate_all ();
901 return;
904 if (priv->mark_invalidate_depth == 0)
905 DrawMark (priv->crosshair_gc);
907 if (!changes_complete)
909 priv->mark_invalidate_depth ++;
911 else if (gport->drawing_area != NULL)
913 /* Queue a GTK expose when changes are complete */
914 ghid_draw_area_update (gport, NULL);
918 static void
919 draw_right_cross (GdkGC *xor_gc, gint x, gint y)
921 GdkWindow *window = gtk_widget_get_window (gport->drawing_area);
923 gdk_draw_line (window, xor_gc, x, 0, x, gport->height);
924 gdk_draw_line (window, xor_gc, 0, y, gport->width, y);
927 static void
928 draw_slanted_cross (GdkGC *xor_gc, gint x, gint y)
930 GdkWindow *window = gtk_widget_get_window (gport->drawing_area);
931 gint x0, y0, x1, y1;
933 x0 = x + (gport->height - y);
934 x0 = MAX(0, MIN (x0, gport->width));
935 x1 = x - y;
936 x1 = MAX(0, MIN (x1, gport->width));
937 y0 = y + (gport->width - x);
938 y0 = MAX(0, MIN (y0, gport->height));
939 y1 = y - x;
940 y1 = MAX(0, MIN (y1, gport->height));
941 gdk_draw_line (window, xor_gc, x0, y0, x1, y1);
943 x0 = x - (gport->height - y);
944 x0 = MAX(0, MIN (x0, gport->width));
945 x1 = x + y;
946 x1 = MAX(0, MIN (x1, gport->width));
947 y0 = y + x;
948 y0 = MAX(0, MIN (y0, gport->height));
949 y1 = y - (gport->width - x);
950 y1 = MAX(0, MIN (y1, gport->height));
951 gdk_draw_line (window, xor_gc, x0, y0, x1, y1);
954 static void
955 draw_dozen_cross (GdkGC *xor_gc, gint x, gint y)
957 GdkWindow *window = gtk_widget_get_window (gport->drawing_area);
958 gint x0, y0, x1, y1;
959 gdouble tan60 = sqrt (3);
961 x0 = x + (gport->height - y) / tan60;
962 x0 = MAX(0, MIN (x0, gport->width));
963 x1 = x - y / tan60;
964 x1 = MAX(0, MIN (x1, gport->width));
965 y0 = y + (gport->width - x) * tan60;
966 y0 = MAX(0, MIN (y0, gport->height));
967 y1 = y - x * tan60;
968 y1 = MAX(0, MIN (y1, gport->height));
969 gdk_draw_line (window, xor_gc, x0, y0, x1, y1);
971 x0 = x + (gport->height - y) * tan60;
972 x0 = MAX(0, MIN (x0, gport->width));
973 x1 = x - y * tan60;
974 x1 = MAX(0, MIN (x1, gport->width));
975 y0 = y + (gport->width - x) / tan60;
976 y0 = MAX(0, MIN (y0, gport->height));
977 y1 = y - x / tan60;
978 y1 = MAX(0, MIN (y1, gport->height));
979 gdk_draw_line (window, xor_gc, x0, y0, x1, y1);
981 x0 = x - (gport->height - y) / tan60;
982 x0 = MAX(0, MIN (x0, gport->width));
983 x1 = x + y / tan60;
984 x1 = MAX(0, MIN (x1, gport->width));
985 y0 = y + x * tan60;
986 y0 = MAX(0, MIN (y0, gport->height));
987 y1 = y - (gport->width - x) * tan60;
988 y1 = MAX(0, MIN (y1, gport->height));
989 gdk_draw_line (window, xor_gc, x0, y0, x1, y1);
991 x0 = x - (gport->height - y) * tan60;
992 x0 = MAX(0, MIN (x0, gport->width));
993 x1 = x + y * tan60;
994 x1 = MAX(0, MIN (x1, gport->width));
995 y0 = y + x / tan60;
996 y0 = MAX(0, MIN (y0, gport->height));
997 y1 = y - (gport->width - x) / tan60;
998 y1 = MAX(0, MIN (y1, gport->height));
999 gdk_draw_line (window, xor_gc, x0, y0, x1, y1);
1002 static void
1003 draw_crosshair (render_priv *priv)
1005 GdkWindow *window = gtk_widget_get_window (gport->drawing_area);
1006 GtkStyle *style = gtk_widget_get_style (gport->drawing_area);
1007 gint x, y;
1008 static GdkGC *xor_gc;
1009 static GdkColor cross_color;
1011 if (gport->crosshair_x < 0 || ghidgui->creating || !gport->has_entered)
1012 return;
1014 if (!xor_gc)
1016 xor_gc = gdk_gc_new (window);
1017 gdk_gc_copy (xor_gc, style->white_gc);
1018 gdk_gc_set_function (xor_gc, GDK_XOR);
1019 gdk_gc_set_clip_origin (xor_gc, 0, 0);
1020 set_clip (priv, xor_gc);
1021 /* FIXME: when CrossColor changed from config */
1022 ghid_map_color_string (Settings.CrossColor, &cross_color);
1025 gdk_gc_set_foreground (xor_gc, &cross_color);
1027 x = DRAW_X (gport->crosshair_x);
1028 y = DRAW_Y (gport->crosshair_y);
1030 draw_right_cross (xor_gc, x, y);
1031 if (Crosshair.shape == Union_Jack_Crosshair_Shape)
1032 draw_slanted_cross (xor_gc, x, y);
1033 if (Crosshair.shape == Dozen_Crosshair_Shape)
1034 draw_dozen_cross (xor_gc, x, y);
1037 void
1038 ghid_init_renderer (int *argc, char ***argv, GHidPort *port)
1040 /* Init any GC's required */
1041 port->render_priv = g_new0 (render_priv, 1);
1042 port->render_priv->crosshair_gc = gui->graphics->make_gc ();
1045 void
1046 ghid_shutdown_renderer (GHidPort *port)
1048 render_priv *priv = port->render_priv;
1050 gui->graphics->destroy_gc (priv->crosshair_gc);
1051 ghid_cancel_lead_user ();
1052 g_free (port->render_priv);
1053 port->render_priv = NULL;
1056 void
1057 ghid_init_drawing_widget (GtkWidget *widget, GHidPort *port)
1061 void
1062 ghid_drawing_area_configure_hook (GHidPort *port)
1064 static int done_once = 0;
1065 render_priv *priv = port->render_priv;
1067 if (!done_once)
1069 priv->bg_gc = gdk_gc_new (port->drawable);
1070 gdk_gc_set_foreground (priv->bg_gc, &port->bg_color);
1071 gdk_gc_set_clip_origin (priv->bg_gc, 0, 0);
1073 priv->offlimits_gc = gdk_gc_new (port->drawable);
1074 gdk_gc_set_foreground (priv->offlimits_gc, &port->offlimits_color);
1075 gdk_gc_set_clip_origin (priv->offlimits_gc, 0, 0);
1076 done_once = 1;
1079 if (port->mask)
1081 gdk_pixmap_unref (port->mask);
1082 port->mask = gdk_pixmap_new (0, port->width, port->height, 1);
1086 void
1087 ghid_screen_update (void)
1089 render_priv *priv = gport->render_priv;
1090 GdkWindow *window = gtk_widget_get_window (gport->drawing_area);
1092 if (gport->pixmap == NULL)
1093 return;
1095 gdk_draw_drawable (window, priv->bg_gc, gport->pixmap,
1096 0, 0, 0, 0, gport->width, gport->height);
1097 draw_crosshair (priv);
1100 gboolean
1101 ghid_drawing_area_expose_cb (GtkWidget *widget,
1102 GdkEventExpose *ev,
1103 GHidPort *port)
1105 render_priv *priv = port->render_priv;
1106 GdkWindow *window = gtk_widget_get_window (gport->drawing_area);
1108 gdk_draw_drawable (window, priv->bg_gc, port->pixmap,
1109 ev->area.x, ev->area.y, ev->area.x, ev->area.y,
1110 ev->area.width, ev->area.height);
1111 draw_crosshair (priv);
1112 return FALSE;
1115 void
1116 ghid_port_drawing_realize_cb (GtkWidget *widget, gpointer data)
1120 gboolean
1121 ghid_pinout_preview_expose (GtkWidget *widget,
1122 GdkEventExpose *ev)
1124 GhidPinoutPreview *pinout = GHID_PINOUT_PREVIEW (widget);
1125 GdkWindow *window = gtk_widget_get_window (widget);
1126 GdkDrawable *save_drawable;
1127 GtkAllocation allocation;
1128 view_data save_view;
1129 int save_width, save_height;
1130 Coord save_max_width;
1131 Coord save_max_height;
1132 double xz, yz;
1133 render_priv *priv = gport->render_priv;
1135 /* Setup drawable and zoom factor for drawing routines
1137 save_drawable = gport->drawable;
1138 save_view = gport->view;
1139 save_width = gport->width;
1140 save_height = gport->height;
1141 save_max_width = PCB->MaxWidth;
1142 save_max_height = PCB->MaxHeight;
1144 gtk_widget_get_allocation (widget, &allocation);
1145 xz = (double) pinout->x_max / allocation.width;
1146 yz = (double) pinout->y_max / allocation.height;
1147 if (xz > yz)
1148 gport->view.coord_per_px = xz;
1149 else
1150 gport->view.coord_per_px = yz;
1152 gport->drawable = window;
1153 gport->width = allocation.width;
1154 gport->height = allocation.height;
1155 gport->view.width = allocation.width * gport->view.coord_per_px;
1156 gport->view.height = allocation.height * gport->view.coord_per_px;
1157 gport->view.x0 = (pinout->x_max - gport->view.width) / 2;
1158 gport->view.y0 = (pinout->y_max - gport->view.height) / 2;
1159 PCB->MaxWidth = pinout->x_max;
1160 PCB->MaxHeight = pinout->y_max;
1162 /* clear background */
1163 gdk_draw_rectangle (window, priv->bg_gc, TRUE,
1164 0, 0, allocation.width, allocation.height);
1166 /* call the drawing routine */
1167 hid_expose_callback (&ghid_hid, NULL, pinout->element);
1169 gport->drawable = save_drawable;
1170 gport->view = save_view;
1171 gport->width = save_width;
1172 gport->height = save_height;
1173 PCB->MaxWidth = save_max_width;
1174 PCB->MaxHeight = save_max_height;
1176 return FALSE;
1179 GdkPixmap *
1180 ghid_render_pixmap (int cx, int cy, double zoom, int width, int height, int depth)
1182 GdkPixmap *pixmap;
1183 GdkDrawable *save_drawable;
1184 view_data save_view;
1185 int save_width, save_height;
1186 BoxType region;
1187 render_priv *priv = gport->render_priv;
1189 save_drawable = gport->drawable;
1190 save_view = gport->view;
1191 save_width = gport->width;
1192 save_height = gport->height;
1194 pixmap = gdk_pixmap_new (NULL, width, height, depth);
1196 /* Setup drawable and zoom factor for drawing routines
1199 gport->drawable = pixmap;
1200 gport->view.coord_per_px = zoom;
1201 gport->width = width;
1202 gport->height = height;
1203 gport->view.width = width * gport->view.coord_per_px;
1204 gport->view.height = height * gport->view.coord_per_px;
1205 gport->view.x0 = gport->view.flip_x ? PCB->MaxWidth - cx : cx;
1206 gport->view.x0 -= gport->view.height / 2;
1207 gport->view.y0 = gport->view.flip_y ? PCB->MaxHeight - cy : cy;
1208 gport->view.y0 -= gport->view.width / 2;
1210 /* clear background */
1211 gdk_draw_rectangle (pixmap, priv->bg_gc, TRUE, 0, 0, width, height);
1213 /* call the drawing routine */
1214 region.X1 = MIN(Px(0), Px(gport->width + 1));
1215 region.Y1 = MIN(Py(0), Py(gport->height + 1));
1216 region.X2 = MAX(Px(0), Px(gport->width + 1));
1217 region.Y2 = MAX(Py(0), Py(gport->height + 1));
1219 region.X1 = MAX (0, MIN (PCB->MaxWidth, region.X1));
1220 region.X2 = MAX (0, MIN (PCB->MaxWidth, region.X2));
1221 region.Y1 = MAX (0, MIN (PCB->MaxHeight, region.Y1));
1222 region.Y2 = MAX (0, MIN (PCB->MaxHeight, region.Y2));
1224 hid_expose_callback (&ghid_hid, &region, NULL);
1226 gport->drawable = save_drawable;
1227 gport->view = save_view;
1228 gport->width = save_width;
1229 gport->height = save_height;
1231 return pixmap;
1234 HID_DRAW *
1235 ghid_request_debug_draw (void)
1237 /* No special setup requirements, drawing goes into
1238 * the backing pixmap. */
1239 return ghid_hid.graphics;
1242 void
1243 ghid_flush_debug_draw (void)
1245 ghid_screen_update ();
1246 gdk_flush ();
1249 void
1250 ghid_finish_debug_draw (void)
1252 ghid_flush_debug_draw ();
1253 /* No special tear down requirements
1257 bool
1258 ghid_event_to_pcb_coords (int event_x, int event_y, Coord *pcb_x, Coord *pcb_y)
1260 *pcb_x = EVENT_TO_PCB_X (event_x);
1261 *pcb_y = EVENT_TO_PCB_Y (event_y);
1263 return true;
1266 bool
1267 ghid_pcb_to_event_coords (Coord pcb_x, Coord pcb_y, int *event_x, int *event_y)
1269 *event_x = DRAW_X (pcb_x);
1270 *event_y = DRAW_Y (pcb_y);
1272 return true;
1276 #define LEAD_USER_WIDTH 0.2 /* millimeters */
1277 #define LEAD_USER_PERIOD (1000 / 5) /* 5fps (in ms) */
1278 #define LEAD_USER_VELOCITY 3. /* millimeters per second */
1279 #define LEAD_USER_ARC_COUNT 3
1280 #define LEAD_USER_ARC_SEPARATION 3. /* millimeters */
1281 #define LEAD_USER_INITIAL_RADIUS 10. /* millimetres */
1282 #define LEAD_USER_COLOR_R 1.
1283 #define LEAD_USER_COLOR_G 1.
1284 #define LEAD_USER_COLOR_B 0.
1286 static void
1287 draw_lead_user (render_priv *priv)
1289 GdkWindow *window = gtk_widget_get_window (gport->drawing_area);
1290 GtkStyle *style = gtk_widget_get_style (gport->drawing_area);
1291 int i;
1292 Coord radius = priv->lead_user_radius;
1293 Coord width = MM_TO_COORD (LEAD_USER_WIDTH);
1294 Coord separation = MM_TO_COORD (LEAD_USER_ARC_SEPARATION);
1295 static GdkGC *lead_gc = NULL;
1296 GdkColor lead_color;
1298 if (!priv->lead_user)
1299 return;
1301 if (lead_gc == NULL)
1303 lead_gc = gdk_gc_new (window);
1304 gdk_gc_copy (lead_gc, style->white_gc);
1305 gdk_gc_set_function (lead_gc, GDK_XOR);
1306 gdk_gc_set_clip_origin (lead_gc, 0, 0);
1307 lead_color.pixel = 0;
1308 lead_color.red = (int)(65535. * LEAD_USER_COLOR_R);
1309 lead_color.green = (int)(65535. * LEAD_USER_COLOR_G);
1310 lead_color.blue = (int)(65535. * LEAD_USER_COLOR_B);
1311 gdk_color_alloc (gport->colormap, &lead_color);
1312 gdk_gc_set_foreground (lead_gc, &lead_color);
1315 set_clip (priv, lead_gc);
1316 gdk_gc_set_line_attributes (lead_gc, Vz (width),
1317 GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1319 /* arcs at the approrpriate radii */
1321 for (i = 0; i < LEAD_USER_ARC_COUNT; i++, radius -= separation)
1323 if (radius < width)
1324 radius += MM_TO_COORD (LEAD_USER_INITIAL_RADIUS);
1326 /* Draw an arc at radius */
1327 gdk_draw_arc (gport->drawable, lead_gc, FALSE,
1328 Vx (priv->lead_user_x - radius),
1329 Vy (priv->lead_user_y - radius),
1330 Vz (2. * radius), Vz (2. * radius),
1331 0, 360 * 64);
1335 gboolean
1336 lead_user_cb (gpointer data)
1338 render_priv *priv = data;
1339 Coord step;
1340 double elapsed_time;
1342 /* Queue a redraw */
1343 ghid_invalidate_all ();
1345 /* Update radius */
1346 elapsed_time = g_timer_elapsed (priv->lead_user_timer, NULL);
1347 g_timer_start (priv->lead_user_timer);
1349 step = MM_TO_COORD (LEAD_USER_VELOCITY * elapsed_time);
1350 if (priv->lead_user_radius > step)
1351 priv->lead_user_radius -= step;
1352 else
1353 priv->lead_user_radius = MM_TO_COORD (LEAD_USER_INITIAL_RADIUS);
1355 return TRUE;
1358 void
1359 ghid_lead_user_to_location (Coord x, Coord y)
1361 render_priv *priv = gport->render_priv;
1363 ghid_cancel_lead_user ();
1365 priv->lead_user = true;
1366 priv->lead_user_x = x;
1367 priv->lead_user_y = y;
1368 priv->lead_user_radius = MM_TO_COORD (LEAD_USER_INITIAL_RADIUS);
1369 priv->lead_user_timeout = g_timeout_add (LEAD_USER_PERIOD, lead_user_cb, priv);
1370 priv->lead_user_timer = g_timer_new ();
1373 void
1374 ghid_cancel_lead_user (void)
1376 render_priv *priv = gport->render_priv;
1378 if (priv->lead_user_timeout)
1379 g_source_remove (priv->lead_user_timeout);
1381 if (priv->lead_user_timer)
1382 g_timer_destroy (priv->lead_user_timer);
1384 if (priv->lead_user)
1385 ghid_invalidate_all ();
1387 priv->lead_user_timeout = 0;
1388 priv->lead_user_timer = NULL;
1389 priv->lead_user = false;