Board outline polygon generation
[geda-pcb/pcjc2.git] / src / hid / gtk / gtkhid-gdk.c
blob493f25cac3cf85122f434b403418704d21d8e1be
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;
18 extern HID_DRAW ghid_graphics;
20 /* Sets priv->u_gc to the "right" GC to use (wrt mask or window)
22 #define USE_GC(gc) if (!use_gc(gc)) return
24 static enum mask_mode cur_mask = HID_MASK_OFF;
25 static int mask_seq = 0;
27 typedef struct render_priv {
28 GdkGC *bg_gc;
29 GdkGC *offlimits_gc;
30 GdkGC *mask_gc;
31 GdkGC *u_gc;
32 GdkGC *grid_gc;
33 bool clip;
34 GdkRectangle clip_rect;
35 int attached_invalidate_depth;
36 int mark_invalidate_depth;
38 /* Feature for leading the user to a particular location */
39 guint lead_user_timeout;
40 GTimer *lead_user_timer;
41 bool lead_user;
42 Coord lead_user_radius;
43 Coord lead_user_x;
44 Coord lead_user_y;
46 hidGC crosshair_gc;
47 } render_priv;
50 typedef struct gtk_gc_struct
52 struct hid_gc_struct hid_gc; /* Parent */
54 GdkGC *gdk_gc;
56 gchar *colorname;
57 Coord width;
58 gint cap, join;
59 gchar xor_mask;
60 gint mask_seq;
61 } *gtkGC;
64 static void draw_lead_user (render_priv *priv);
67 int
68 ghid_set_layer (const char *name, int group, int empty)
70 int idx = group;
71 if (idx >= 0 && idx < max_group)
73 int n = PCB->LayerGroups.Number[group];
74 for (idx = 0; idx < n-1; idx ++)
76 int ni = PCB->LayerGroups.Entries[group][idx];
77 if (ni >= 0 && ni < max_copper_layer + EXTRA_LAYERS
78 && PCB->Data->Layer[ni].On)
79 break;
81 idx = PCB->LayerGroups.Entries[group][idx];
84 if (idx >= 0 && idx < max_copper_layer + EXTRA_LAYERS)
85 return /*pinout ? 1 : */ PCB->Data->Layer[idx].On;
86 if (idx < 0)
88 switch (SL_TYPE (idx))
90 case SL_INVISIBLE:
91 return /* pinout ? 0 : */ PCB->InvisibleObjectsOn;
92 case SL_MASK:
93 if (SL_MYSIDE (idx) /*&& !pinout */ )
94 return TEST_FLAG (SHOWMASKFLAG, PCB);
95 return 0;
96 case SL_SILK:
97 if (SL_MYSIDE (idx) /*|| pinout */ )
98 return PCB->ElementOn;
99 return 0;
100 case SL_ASSY:
101 return 0;
102 case SL_PDRILL:
103 case SL_UDRILL:
104 return 1;
105 case SL_RATS:
106 return PCB->RatOn;
109 return 0;
112 void
113 ghid_destroy_gc (hidGC gc)
115 gtkGC gtk_gc = (gtkGC)gc;
117 if (gtk_gc->gdk_gc)
118 g_object_unref (gtk_gc->gdk_gc);
119 g_free (gc);
122 hidGC
123 ghid_make_gc (void)
125 hidGC gc = (hidGC)g_new0 (struct gtk_gc_struct, 1);
126 gtkGC gtk_gc = (gtkGC)gc;
128 gc->hid = &ghid_hid;
129 gc->hid_draw = &ghid_graphics;
131 gtk_gc->colorname = Settings.BackgroundColor;
133 return gc;
136 static void
137 set_clip (render_priv *priv, GdkGC *gc)
139 if (gc == NULL)
140 return;
142 if (priv->clip)
143 gdk_gc_set_clip_rectangle (gc, &priv->clip_rect);
144 else
145 gdk_gc_set_clip_mask (gc, NULL);
148 static void
149 ghid_draw_grid (void)
151 static GdkPoint *points = 0;
152 static int npoints = 0;
153 Coord x1, y1, x2, y2, x, y;
154 int n, i;
155 render_priv *priv = gport->render_priv;
157 if (!Settings.DrawGrid)
158 return;
159 if (Vz (PCB->Grid) < MIN_GRID_DISTANCE)
160 return;
161 if (!priv->grid_gc)
163 if (gdk_color_parse (Settings.GridColor, &gport->grid_color))
165 gport->grid_color.red ^= gport->bg_color.red;
166 gport->grid_color.green ^= gport->bg_color.green;
167 gport->grid_color.blue ^= gport->bg_color.blue;
168 gdk_color_alloc (gport->colormap, &gport->grid_color);
170 priv->grid_gc = gdk_gc_new (gport->drawable);
171 gdk_gc_set_function (priv->grid_gc, GDK_XOR);
172 gdk_gc_set_foreground (priv->grid_gc, &gport->grid_color);
173 gdk_gc_set_clip_origin (priv->grid_gc, 0, 0);
174 set_clip (priv, priv->grid_gc);
176 x1 = GridFit (SIDE_X (gport->view.x0), PCB->Grid, PCB->GridOffsetX);
177 y1 = GridFit (SIDE_Y (gport->view.y0), PCB->Grid, PCB->GridOffsetY);
178 x2 = GridFit (SIDE_X (gport->view.x0 + gport->view.width - 1),
179 PCB->Grid, PCB->GridOffsetX);
180 y2 = GridFit (SIDE_Y (gport->view.y0 + gport->view.height - 1),
181 PCB->Grid, PCB->GridOffsetY);
182 if (x1 > x2)
184 Coord tmp = x1;
185 x1 = x2;
186 x2 = tmp;
188 if (y1 > y2)
190 Coord tmp = y1;
191 y1 = y2;
192 y2 = tmp;
194 if (Vx (x1) < 0)
195 x1 += PCB->Grid;
196 if (Vy (y1) < 0)
197 y1 += PCB->Grid;
198 if (Vx (x2) >= gport->width)
199 x2 -= PCB->Grid;
200 if (Vy (y2) >= gport->height)
201 y2 -= PCB->Grid;
202 n = (x2 - x1) / PCB->Grid + 1;
203 if (n > npoints)
205 npoints = n + 10;
206 points = (GdkPoint *)realloc (points, npoints * sizeof (GdkPoint));
208 n = 0;
209 for (x = x1; x <= x2; x += PCB->Grid)
211 points[n].x = Vx (x);
212 n++;
214 if (n == 0)
215 return;
216 for (y = y1; y <= y2; y += PCB->Grid)
218 for (i = 0; i < n; i++)
219 points[i].y = Vy (y);
220 gdk_draw_points (gport->drawable, priv->grid_gc, points, n);
224 /* ------------------------------------------------------------ */
225 static void
226 ghid_draw_bg_image (void)
228 static GdkPixbuf *pixbuf;
229 GdkInterpType interp_type;
230 gint x, y, w, h, w_src, h_src;
231 static gint w_scaled, h_scaled;
232 render_priv *priv = gport->render_priv;
234 if (!ghidgui->bg_pixbuf)
235 return;
237 w = PCB->MaxWidth / gport->view.coord_per_px;
238 h = PCB->MaxHeight / gport->view.coord_per_px;
239 x = gport->view.x0 / gport->view.coord_per_px;
240 y = gport->view.y0 / gport->view.coord_per_px;
242 if (w_scaled != w || h_scaled != h)
244 if (pixbuf)
245 g_object_unref (G_OBJECT (pixbuf));
247 w_src = gdk_pixbuf_get_width (ghidgui->bg_pixbuf);
248 h_src = gdk_pixbuf_get_height (ghidgui->bg_pixbuf);
249 if (w > w_src && h > h_src)
250 interp_type = GDK_INTERP_NEAREST;
251 else
252 interp_type = GDK_INTERP_BILINEAR;
254 pixbuf =
255 gdk_pixbuf_scale_simple (ghidgui->bg_pixbuf, w, h, interp_type);
256 w_scaled = w;
257 h_scaled = h;
259 if (pixbuf)
260 gdk_pixbuf_render_to_drawable (pixbuf, gport->drawable, priv->bg_gc,
261 x, y, 0, 0,
262 w - x, h - y, GDK_RGB_DITHER_NORMAL, 0, 0);
265 #define WHICH_GC(gtk_gc) (cur_mask == HID_MASK_CLEAR ? priv->mask_gc : (gtk_gc)->gdk_gc)
267 void
268 ghid_use_mask (enum mask_mode mode)
270 static int mask_seq_id = 0;
271 GdkColor color;
272 render_priv *priv = gport->render_priv;
274 if (!gport->pixmap)
275 return;
276 if (mode == cur_mask)
277 return;
278 switch (mode)
280 case HID_MASK_OFF:
281 gport->drawable = gport->pixmap;
282 mask_seq = 0;
283 break;
285 case HID_MASK_BEFORE:
286 /* The HID asks not to receive this mask type, so warn if we get it */
287 g_return_if_reached ();
289 case HID_MASK_CLEAR:
290 if (!gport->mask)
291 gport->mask = gdk_pixmap_new (0, gport->width, gport->height, 1);
292 gport->drawable = gport->mask;
293 mask_seq = 0;
294 if (!priv->mask_gc)
296 priv->mask_gc = gdk_gc_new (gport->drawable);
297 gdk_gc_set_clip_origin (priv->mask_gc, 0, 0);
298 set_clip (priv, priv->mask_gc);
300 color.pixel = 1;
301 gdk_gc_set_foreground (priv->mask_gc, &color);
302 gdk_draw_rectangle (gport->drawable, priv->mask_gc, TRUE, 0, 0,
303 gport->width, gport->height);
304 color.pixel = 0;
305 gdk_gc_set_foreground (priv->mask_gc, &color);
306 break;
308 case HID_MASK_AFTER:
309 mask_seq_id++;
310 if (!mask_seq_id)
311 mask_seq_id = 1;
312 mask_seq = mask_seq_id;
314 gport->drawable = gport->pixmap;
315 break;
318 cur_mask = mode;
322 typedef struct
324 int color_set;
325 GdkColor color;
326 int xor_set;
327 GdkColor xor_color;
328 } ColorCache;
331 /* Config helper functions for when the user changes color preferences.
332 | set_special colors used in the gtkhid.
334 static void
335 set_special_grid_color (void)
337 render_priv *priv = gport->render_priv;
339 if (!gport->colormap)
340 return;
341 gport->grid_color.red ^= gport->bg_color.red;
342 gport->grid_color.green ^= gport->bg_color.green;
343 gport->grid_color.blue ^= gport->bg_color.blue;
344 gdk_color_alloc (gport->colormap, &gport->grid_color);
345 if (priv->grid_gc)
346 gdk_gc_set_foreground (priv->grid_gc, &gport->grid_color);
349 void
350 ghid_set_special_colors (HID_Attribute * ha)
352 render_priv *priv = gport->render_priv;
354 if (!ha->name || !ha->value)
355 return;
356 if (!strcmp (ha->name, "background-color") && priv->bg_gc)
358 ghid_map_color_string (*(char **) ha->value, &gport->bg_color);
359 gdk_gc_set_foreground (priv->bg_gc, &gport->bg_color);
360 set_special_grid_color ();
362 else if (!strcmp (ha->name, "off-limit-color") && priv->offlimits_gc)
364 ghid_map_color_string (*(char **) ha->value, &gport->offlimits_color);
365 gdk_gc_set_foreground (priv->offlimits_gc, &gport->offlimits_color);
367 else if (!strcmp (ha->name, "grid-color") && priv->grid_gc)
369 ghid_map_color_string (*(char **) ha->value, &gport->grid_color);
370 set_special_grid_color ();
374 void
375 ghid_set_color (hidGC gc, const char *name)
377 gtkGC gtk_gc = (gtkGC)gc;
378 static void *cache = 0;
379 hidval cval;
381 if (name == NULL)
383 fprintf (stderr, "%s(): name = NULL, setting to magenta\n",
384 __FUNCTION__);
385 name = "magenta";
388 gtk_gc->colorname = (char *) name;
389 if (!gtk_gc->gdk_gc)
390 return;
391 if (gport->colormap == 0)
392 gport->colormap = gtk_widget_get_colormap (gport->top_window);
394 if (strcmp (name, "erase") == 0)
396 gdk_gc_set_foreground (gtk_gc->gdk_gc, &gport->bg_color);
398 else if (strcmp (name, "drill") == 0)
400 gdk_gc_set_foreground (gtk_gc->gdk_gc, &gport->offlimits_color);
402 else
404 ColorCache *cc;
405 if (hid_cache_color (0, name, &cval, &cache))
406 cc = (ColorCache *) cval.ptr;
407 else
409 cc = (ColorCache *) malloc (sizeof (ColorCache));
410 memset (cc, 0, sizeof (*cc));
411 cval.ptr = cc;
412 hid_cache_color (1, name, &cval, &cache);
415 if (!cc->color_set)
417 if (gdk_color_parse (name, &cc->color))
418 gdk_color_alloc (gport->colormap, &cc->color);
419 else
420 gdk_color_white (gport->colormap, &cc->color);
421 cc->color_set = 1;
423 if (gtk_gc->xor_mask)
425 if (!cc->xor_set)
427 cc->xor_color.red = cc->color.red ^ gport->bg_color.red;
428 cc->xor_color.green = cc->color.green ^ gport->bg_color.green;
429 cc->xor_color.blue = cc->color.blue ^ gport->bg_color.blue;
430 gdk_color_alloc (gport->colormap, &cc->xor_color);
431 cc->xor_set = 1;
433 gdk_gc_set_foreground (gtk_gc->gdk_gc, &cc->xor_color);
435 else
437 gdk_gc_set_foreground (gtk_gc->gdk_gc, &cc->color);
442 void
443 ghid_set_line_cap (hidGC gc, EndCapStyle style)
445 gtkGC gtk_gc = (gtkGC)gc;
446 render_priv *priv = gport->render_priv;
448 switch (style)
450 case Trace_Cap:
451 case Round_Cap:
452 gtk_gc->cap = GDK_CAP_ROUND;
453 gtk_gc->join = GDK_JOIN_ROUND;
454 break;
455 case Square_Cap:
456 case Beveled_Cap:
457 gtk_gc->cap = GDK_CAP_PROJECTING;
458 gtk_gc->join = GDK_JOIN_MITER;
459 break;
461 if (gtk_gc->gdk_gc)
462 gdk_gc_set_line_attributes (WHICH_GC (gtk_gc),
463 Vz (gtk_gc->width), GDK_LINE_SOLID,
464 (GdkCapStyle)gtk_gc->cap, (GdkJoinStyle)gtk_gc->join);
467 void
468 ghid_set_line_width (hidGC gc, Coord width)
470 gtkGC gtk_gc = (gtkGC)gc;
471 render_priv *priv = gport->render_priv;
473 gtk_gc->width = width;
474 if (gtk_gc->gdk_gc)
475 gdk_gc_set_line_attributes (WHICH_GC (gtk_gc),
476 Vz (gtk_gc->width), GDK_LINE_SOLID,
477 (GdkCapStyle)gtk_gc->cap, (GdkJoinStyle)gtk_gc->join);
480 void
481 ghid_set_draw_xor (hidGC gc, int xor_mask)
483 gtkGC gtk_gc = (gtkGC)gc;
485 gtk_gc->xor_mask = xor_mask;
486 if (!gtk_gc->gdk_gc)
487 return;
488 gdk_gc_set_function (gtk_gc->gdk_gc, xor_mask ? GDK_XOR : GDK_COPY);
489 ghid_set_color (gc, gtk_gc->colorname);
492 static int
493 use_gc (hidGC gc)
495 gtkGC gtk_gc = (gtkGC)gc;
496 render_priv *priv = gport->render_priv;
497 GdkWindow *window = gtk_widget_get_window (gport->top_window);
499 if (gc->hid != &ghid_hid)
501 fprintf (stderr, "Fatal: GC from another HID passed to GTK HID\n");
502 abort ();
505 if (!gport->pixmap)
506 return 0;
507 if (!gtk_gc->gdk_gc)
509 gtk_gc->gdk_gc = gdk_gc_new (window);
510 ghid_set_color (gc, gtk_gc->colorname);
511 ghid_set_line_width (gc, gtk_gc->width);
512 ghid_set_line_cap (gc, (EndCapStyle)gtk_gc->cap);
513 ghid_set_draw_xor (gc, gtk_gc->xor_mask);
514 gdk_gc_set_clip_origin (gtk_gc->gdk_gc, 0, 0);
516 if (gtk_gc->mask_seq != mask_seq)
518 if (mask_seq)
519 gdk_gc_set_clip_mask (gtk_gc->gdk_gc, gport->mask);
520 else
521 set_clip (priv, gtk_gc->gdk_gc);
522 gtk_gc->mask_seq = mask_seq;
524 priv->u_gc = WHICH_GC (gtk_gc);
525 return 1;
528 void
529 ghid_draw_line (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
531 gtkGC gtk_gc = (gtkGC)gc;
532 double dx1, dy1, dx2, dy2;
533 render_priv *priv = gport->render_priv;
535 dx1 = Vx ((double) x1);
536 dy1 = Vy ((double) y1);
537 dx2 = Vx ((double) x2);
538 dy2 = Vy ((double) y2);
540 if (!ClipLine (0, 0, gport->width, gport->height,
541 &dx1, &dy1, &dx2, &dy2, gtk_gc->width / gport->view.coord_per_px))
542 return;
544 USE_GC (gc);
545 gdk_draw_line (gport->drawable, priv->u_gc, dx1, dy1, dx2, dy2);
548 void
549 ghid_draw_arc (hidGC gc, Coord cx, Coord cy,
550 Coord xradius, Coord yradius, Angle start_angle, Angle delta_angle)
552 gint vrx, vry;
553 gint w, h, radius;
554 render_priv *priv = gport->render_priv;
556 w = gport->width * gport->view.coord_per_px;
557 h = gport->height * gport->view.coord_per_px;
558 radius = (xradius > yradius) ? xradius : yradius;
559 if (SIDE_X (cx) < gport->view.x0 - radius
560 || SIDE_X (cx) > gport->view.x0 + w + radius
561 || SIDE_Y (cy) < gport->view.y0 - radius
562 || SIDE_Y (cy) > gport->view.y0 + h + radius)
563 return;
565 USE_GC (gc);
566 vrx = Vz (xradius);
567 vry = Vz (yradius);
569 if (gport->view.flip_x)
571 start_angle = 180 - start_angle;
572 delta_angle = -delta_angle;
574 if (gport->view.flip_y)
576 start_angle = -start_angle;
577 delta_angle = -delta_angle;
579 /* make sure we fall in the -180 to +180 range */
580 start_angle = NormalizeAngle (start_angle);
581 if (start_angle >= 180) start_angle -= 360;
583 gdk_draw_arc (gport->drawable, priv->u_gc, 0,
584 Vx (cx) - vrx, Vy (cy) - vry,
585 vrx * 2, vry * 2, (start_angle + 180) * 64, delta_angle * 64);
588 void
589 ghid_draw_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
591 gtkGC gtk_gc = (gtkGC)gc;
592 gint w, h, lw;
593 render_priv *priv = gport->render_priv;
595 lw = gtk_gc->width;
596 w = gport->width * gport->view.coord_per_px;
597 h = gport->height * gport->view.coord_per_px;
599 if ((SIDE_X (x1) < gport->view.x0 - lw
600 && SIDE_X (x2) < gport->view.x0 - lw)
601 || (SIDE_X (x1) > gport->view.x0 + w + lw
602 && SIDE_X (x2) > gport->view.x0 + w + lw)
603 || (SIDE_Y (y1) < gport->view.y0 - lw
604 && SIDE_Y (y2) < gport->view.y0 - lw)
605 || (SIDE_Y (y1) > gport->view.y0 + h + lw
606 && SIDE_Y (y2) > gport->view.y0 + h + lw))
607 return;
609 x1 = Vx (x1);
610 y1 = Vy (y1);
611 x2 = Vx (x2);
612 y2 = Vy (y2);
614 if (x1 > x2)
616 gint xt = x1;
617 x1 = x2;
618 x2 = xt;
620 if (y1 > y2)
622 gint yt = y1;
623 y1 = y2;
624 y2 = yt;
627 USE_GC (gc);
628 gdk_draw_rectangle (gport->drawable, priv->u_gc, FALSE,
629 x1, y1, x2 - x1 + 1, y2 - y1 + 1);
633 void
634 ghid_fill_circle (hidGC gc, Coord cx, Coord cy, Coord radius)
636 gint w, h, vr;
637 render_priv *priv = gport->render_priv;
639 w = gport->width * gport->view.coord_per_px;
640 h = gport->height * gport->view.coord_per_px;
641 if (SIDE_X (cx) < gport->view.x0 - radius
642 || SIDE_X (cx) > gport->view.x0 + w + radius
643 || SIDE_Y (cy) < gport->view.y0 - radius
644 || SIDE_Y (cy) > gport->view.y0 + h + radius)
645 return;
647 USE_GC (gc);
648 vr = Vz (radius);
649 gdk_draw_arc (gport->drawable, priv->u_gc, TRUE,
650 Vx (cx) - vr, Vy (cy) - vr, vr * 2, vr * 2, 0, 360 * 64);
653 void
654 ghid_fill_polygon (hidGC gc, int n_coords, Coord *x, Coord *y)
656 static GdkPoint *points = 0;
657 static int npoints = 0;
658 int i;
659 render_priv *priv = gport->render_priv;
660 USE_GC (gc);
662 if (npoints < n_coords)
664 npoints = n_coords + 1;
665 points = (GdkPoint *)realloc (points, npoints * sizeof (GdkPoint));
667 for (i = 0; i < n_coords; i++)
669 points[i].x = Vx (x[i]);
670 points[i].y = Vy (y[i]);
672 gdk_draw_polygon (gport->drawable, priv->u_gc, 1, points, n_coords);
675 void
676 ghid_fill_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
678 gtkGC gtk_gc = (gtkGC)gc;
679 gint w, h, lw, xx, yy;
680 render_priv *priv = gport->render_priv;
682 lw = gtk_gc->width;
683 w = gport->width * gport->view.coord_per_px;
684 h = gport->height * gport->view.coord_per_px;
686 if ((SIDE_X (x1) < gport->view.x0 - lw
687 && SIDE_X (x2) < gport->view.x0 - lw)
688 || (SIDE_X (x1) > gport->view.x0 + w + lw
689 && SIDE_X (x2) > gport->view.x0 + w + lw)
690 || (SIDE_Y (y1) < gport->view.y0 - lw
691 && SIDE_Y (y2) < gport->view.y0 - lw)
692 || (SIDE_Y (y1) > gport->view.y0 + h + lw
693 && SIDE_Y (y2) > gport->view.y0 + h + lw))
694 return;
696 x1 = Vx (x1);
697 y1 = Vy (y1);
698 x2 = Vx (x2);
699 y2 = Vy (y2);
700 if (x2 < x1)
702 xx = x1;
703 x1 = x2;
704 x2 = xx;
706 if (y2 < y1)
708 yy = y1;
709 y1 = y2;
710 y2 = yy;
712 USE_GC (gc);
713 gdk_draw_rectangle (gport->drawable, priv->u_gc, TRUE,
714 x1, y1, x2 - x1 + 1, y2 - y1 + 1);
717 static void
718 redraw_region (GdkRectangle *rect)
720 int eleft, eright, etop, ebottom;
721 BoxType region;
722 render_priv *priv = gport->render_priv;
724 if (!gport->pixmap)
725 return;
727 if (rect != NULL)
729 priv->clip_rect = *rect;
730 priv->clip = true;
732 else
734 priv->clip_rect.x = 0;
735 priv->clip_rect.y = 0;
736 priv->clip_rect.width = gport->width;
737 priv->clip_rect.height = gport->height;
738 priv->clip = false;
741 set_clip (priv, priv->bg_gc);
742 set_clip (priv, priv->offlimits_gc);
743 set_clip (priv, priv->mask_gc);
744 set_clip (priv, priv->grid_gc);
746 region.X1 = MIN(Px(priv->clip_rect.x),
747 Px(priv->clip_rect.x + priv->clip_rect.width + 1));
748 region.Y1 = MIN(Py(priv->clip_rect.y),
749 Py(priv->clip_rect.y + priv->clip_rect.height + 1));
750 region.X2 = MAX(Px(priv->clip_rect.x),
751 Px(priv->clip_rect.x + priv->clip_rect.width + 1));
752 region.Y2 = MAX(Py(priv->clip_rect.y),
753 Py(priv->clip_rect.y + priv->clip_rect.height + 1));
755 region.X1 = MAX (0, MIN (PCB->MaxWidth, region.X1));
756 region.X2 = MAX (0, MIN (PCB->MaxWidth, region.X2));
757 region.Y1 = MAX (0, MIN (PCB->MaxHeight, region.Y1));
758 region.Y2 = MAX (0, MIN (PCB->MaxHeight, region.Y2));
760 eleft = Vx (0);
761 eright = Vx (PCB->MaxWidth);
762 etop = Vy (0);
763 ebottom = Vy (PCB->MaxHeight);
764 if (eleft > eright)
766 int tmp = eleft;
767 eleft = eright;
768 eright = tmp;
770 if (etop > ebottom)
772 int tmp = etop;
773 etop = ebottom;
774 ebottom = tmp;
777 if (eleft > 0)
778 gdk_draw_rectangle (gport->drawable, priv->offlimits_gc,
779 1, 0, 0, eleft, gport->height);
780 else
781 eleft = 0;
782 if (eright < gport->width)
783 gdk_draw_rectangle (gport->drawable, priv->offlimits_gc,
784 1, eright, 0, gport->width - eright, gport->height);
785 else
786 eright = gport->width;
787 if (etop > 0)
788 gdk_draw_rectangle (gport->drawable, priv->offlimits_gc,
789 1, eleft, 0, eright - eleft + 1, etop);
790 else
791 etop = 0;
792 if (ebottom < gport->height)
793 gdk_draw_rectangle (gport->drawable, priv->offlimits_gc,
794 1, eleft, ebottom, eright - eleft + 1,
795 gport->height - ebottom);
796 else
797 ebottom = gport->height;
799 gdk_draw_rectangle (gport->drawable, priv->bg_gc, 1,
800 eleft, etop, eright - eleft + 1, ebottom - etop + 1);
802 ghid_draw_bg_image();
804 hid_expose_callback (&ghid_graphics, &region, 0);
805 ghid_draw_grid ();
807 /* In some cases we are called with the crosshair still off */
808 if (priv->attached_invalidate_depth == 0)
809 DrawAttached (priv->crosshair_gc);
811 /* In some cases we are called with the mark still off */
812 if (priv->mark_invalidate_depth == 0)
813 DrawMark (priv->crosshair_gc);
815 draw_lead_user (priv);
817 priv->clip = false;
819 /* Rest the clip for bg_gc, as it is used outside this function */
820 gdk_gc_set_clip_mask (priv->bg_gc, NULL);
823 void
824 ghid_invalidate_lr (int left, int right, int top, int bottom)
826 int dleft, dright, dtop, dbottom;
827 int minx, maxx, miny, maxy;
828 GdkRectangle rect;
830 dleft = Vx (left);
831 dright = Vx (right);
832 dtop = Vy (top);
833 dbottom = Vy (bottom);
835 minx = MIN (dleft, dright);
836 maxx = MAX (dleft, dright);
837 miny = MIN (dtop, dbottom);
838 maxy = MAX (dtop, dbottom);
840 rect.x = minx;
841 rect.y = miny;
842 rect.width = maxx - minx;
843 rect.height = maxy - miny;
845 redraw_region (&rect);
846 ghid_screen_update ();
850 void
851 ghid_invalidate_all ()
853 redraw_region (NULL);
854 ghid_screen_update ();
857 void
858 ghid_notify_crosshair_change (bool changes_complete)
860 render_priv *priv = gport->render_priv;
862 /* We sometimes get called before the GUI is up */
863 if (gport->drawing_area == NULL)
864 return;
866 if (changes_complete)
867 priv->attached_invalidate_depth --;
869 if (priv->attached_invalidate_depth < 0)
871 priv->attached_invalidate_depth = 0;
872 /* A mismatch of changes_complete == false and == true notifications
873 * is not expected to occur, but we will try to handle it gracefully.
874 * As we know the crosshair will have been shown already, we must
875 * repaint the entire view to be sure not to leave an artaefact.
877 ghid_invalidate_all ();
878 return;
881 if (priv->attached_invalidate_depth == 0)
882 DrawAttached (priv->crosshair_gc);
884 if (!changes_complete)
886 priv->attached_invalidate_depth ++;
888 else if (gport->drawing_area != NULL)
890 /* Queue a GTK expose when changes are complete */
891 ghid_draw_area_update (gport, NULL);
895 void
896 ghid_notify_mark_change (bool changes_complete)
898 render_priv *priv = gport->render_priv;
900 /* We sometimes get called before the GUI is up */
901 if (gport->drawing_area == NULL)
902 return;
904 if (changes_complete)
905 priv->mark_invalidate_depth --;
907 if (priv->mark_invalidate_depth < 0)
909 priv->mark_invalidate_depth = 0;
910 /* A mismatch of changes_complete == false and == true notifications
911 * is not expected to occur, but we will try to handle it gracefully.
912 * As we know the mark will have been shown already, we must
913 * repaint the entire view to be sure not to leave an artaefact.
915 ghid_invalidate_all ();
916 return;
919 if (priv->mark_invalidate_depth == 0)
920 DrawMark (priv->crosshair_gc);
922 if (!changes_complete)
924 priv->mark_invalidate_depth ++;
926 else if (gport->drawing_area != NULL)
928 /* Queue a GTK expose when changes are complete */
929 ghid_draw_area_update (gport, NULL);
933 static void
934 draw_right_cross (GdkGC *xor_gc, gint x, gint y)
936 GdkWindow *window = gtk_widget_get_window (gport->drawing_area);
938 gdk_draw_line (window, xor_gc, x, 0, x, gport->height);
939 gdk_draw_line (window, xor_gc, 0, y, gport->width, y);
942 static void
943 draw_slanted_cross (GdkGC *xor_gc, gint x, gint y)
945 GdkWindow *window = gtk_widget_get_window (gport->drawing_area);
946 gint x0, y0, x1, y1;
948 x0 = x + (gport->height - y);
949 x0 = MAX(0, MIN (x0, gport->width));
950 x1 = x - y;
951 x1 = MAX(0, MIN (x1, gport->width));
952 y0 = y + (gport->width - x);
953 y0 = MAX(0, MIN (y0, gport->height));
954 y1 = y - x;
955 y1 = MAX(0, MIN (y1, gport->height));
956 gdk_draw_line (window, xor_gc, x0, y0, x1, y1);
958 x0 = x - (gport->height - y);
959 x0 = MAX(0, MIN (x0, gport->width));
960 x1 = x + y;
961 x1 = MAX(0, MIN (x1, gport->width));
962 y0 = y + x;
963 y0 = MAX(0, MIN (y0, gport->height));
964 y1 = y - (gport->width - x);
965 y1 = MAX(0, MIN (y1, gport->height));
966 gdk_draw_line (window, xor_gc, x0, y0, x1, y1);
969 static void
970 draw_dozen_cross (GdkGC *xor_gc, gint x, gint y)
972 GdkWindow *window = gtk_widget_get_window (gport->drawing_area);
973 gint x0, y0, x1, y1;
974 gdouble tan60 = sqrt (3);
976 x0 = x + (gport->height - y) / tan60;
977 x0 = MAX(0, MIN (x0, gport->width));
978 x1 = x - y / tan60;
979 x1 = MAX(0, MIN (x1, gport->width));
980 y0 = y + (gport->width - x) * tan60;
981 y0 = MAX(0, MIN (y0, gport->height));
982 y1 = y - x * tan60;
983 y1 = MAX(0, MIN (y1, gport->height));
984 gdk_draw_line (window, xor_gc, x0, y0, x1, y1);
986 x0 = x + (gport->height - y) * tan60;
987 x0 = MAX(0, MIN (x0, gport->width));
988 x1 = x - y * tan60;
989 x1 = MAX(0, MIN (x1, gport->width));
990 y0 = y + (gport->width - x) / tan60;
991 y0 = MAX(0, MIN (y0, gport->height));
992 y1 = y - x / tan60;
993 y1 = MAX(0, MIN (y1, gport->height));
994 gdk_draw_line (window, xor_gc, x0, y0, x1, y1);
996 x0 = x - (gport->height - y) / tan60;
997 x0 = MAX(0, MIN (x0, gport->width));
998 x1 = x + y / tan60;
999 x1 = MAX(0, MIN (x1, gport->width));
1000 y0 = y + x * tan60;
1001 y0 = MAX(0, MIN (y0, gport->height));
1002 y1 = y - (gport->width - x) * tan60;
1003 y1 = MAX(0, MIN (y1, gport->height));
1004 gdk_draw_line (window, xor_gc, x0, y0, x1, y1);
1006 x0 = x - (gport->height - y) * tan60;
1007 x0 = MAX(0, MIN (x0, gport->width));
1008 x1 = x + y * tan60;
1009 x1 = MAX(0, MIN (x1, gport->width));
1010 y0 = y + x / tan60;
1011 y0 = MAX(0, MIN (y0, gport->height));
1012 y1 = y - (gport->width - x) / tan60;
1013 y1 = MAX(0, MIN (y1, gport->height));
1014 gdk_draw_line (window, xor_gc, x0, y0, x1, y1);
1017 static void
1018 draw_crosshair (render_priv *priv)
1020 GdkWindow *window = gtk_widget_get_window (gport->drawing_area);
1021 GtkStyle *style = gtk_widget_get_style (gport->drawing_area);
1022 gint x, y;
1023 static GdkGC *xor_gc;
1024 static GdkColor cross_color;
1026 if (gport->crosshair_x < 0 || ghidgui->creating || !gport->has_entered)
1027 return;
1029 if (!xor_gc)
1031 xor_gc = gdk_gc_new (window);
1032 gdk_gc_copy (xor_gc, style->white_gc);
1033 gdk_gc_set_function (xor_gc, GDK_XOR);
1034 gdk_gc_set_clip_origin (xor_gc, 0, 0);
1035 set_clip (priv, xor_gc);
1036 /* FIXME: when CrossColor changed from config */
1037 ghid_map_color_string (Settings.CrossColor, &cross_color);
1040 gdk_gc_set_foreground (xor_gc, &cross_color);
1042 x = DRAW_X (gport->crosshair_x);
1043 y = DRAW_Y (gport->crosshair_y);
1045 draw_right_cross (xor_gc, x, y);
1046 if (Crosshair.shape == Union_Jack_Crosshair_Shape)
1047 draw_slanted_cross (xor_gc, x, y);
1048 if (Crosshair.shape == Dozen_Crosshair_Shape)
1049 draw_dozen_cross (xor_gc, x, y);
1052 void
1053 ghid_init_renderer (int *argc, char ***argv, GHidPort *port)
1055 /* Init any GC's required */
1056 port->render_priv = g_new0 (render_priv, 1);
1057 port->render_priv->crosshair_gc = hid_draw_make_gc (&ghid_graphics);
1060 void
1061 ghid_shutdown_renderer (GHidPort *port)
1063 render_priv *priv = port->render_priv;
1065 hid_draw_destroy_gc (priv->crosshair_gc);
1066 ghid_cancel_lead_user ();
1067 g_free (port->render_priv);
1068 port->render_priv = NULL;
1071 void
1072 ghid_init_drawing_widget (GtkWidget *widget, GHidPort *port)
1076 void
1077 ghid_drawing_area_configure_hook (GHidPort *port)
1079 static int done_once = 0;
1080 render_priv *priv = port->render_priv;
1082 if (!done_once)
1084 priv->bg_gc = gdk_gc_new (port->drawable);
1085 gdk_gc_set_foreground (priv->bg_gc, &port->bg_color);
1086 gdk_gc_set_clip_origin (priv->bg_gc, 0, 0);
1088 priv->offlimits_gc = gdk_gc_new (port->drawable);
1089 gdk_gc_set_foreground (priv->offlimits_gc, &port->offlimits_color);
1090 gdk_gc_set_clip_origin (priv->offlimits_gc, 0, 0);
1091 done_once = 1;
1094 if (port->mask)
1096 g_object_unref (port->mask);
1097 port->mask = gdk_pixmap_new (0, port->width, port->height, 1);
1101 void
1102 ghid_screen_update (void)
1104 render_priv *priv = gport->render_priv;
1105 GdkWindow *window = gtk_widget_get_window (gport->drawing_area);
1107 if (gport->pixmap == NULL)
1108 return;
1110 gdk_draw_drawable (window, priv->bg_gc, gport->pixmap,
1111 0, 0, 0, 0, gport->width, gport->height);
1112 draw_crosshair (priv);
1115 gboolean
1116 ghid_drawing_area_expose_cb (GtkWidget *widget,
1117 GdkEventExpose *ev,
1118 GHidPort *port)
1120 render_priv *priv = port->render_priv;
1121 GdkWindow *window = gtk_widget_get_window (gport->drawing_area);
1123 gdk_draw_drawable (window, priv->bg_gc, port->pixmap,
1124 ev->area.x, ev->area.y, ev->area.x, ev->area.y,
1125 ev->area.width, ev->area.height);
1126 draw_crosshair (priv);
1127 return FALSE;
1130 void
1131 ghid_port_drawing_realize_cb (GtkWidget *widget, gpointer data)
1135 gboolean
1136 ghid_pinout_preview_expose (GtkWidget *widget,
1137 GdkEventExpose *ev)
1139 GhidPinoutPreview *pinout = GHID_PINOUT_PREVIEW (widget);
1140 GdkWindow *window = gtk_widget_get_window (widget);
1141 GdkDrawable *save_drawable;
1142 GtkAllocation allocation;
1143 view_data save_view;
1144 int save_width, save_height;
1145 Coord save_max_width;
1146 Coord save_max_height;
1147 double xz, yz;
1148 render_priv *priv = gport->render_priv;
1150 /* Setup drawable and zoom factor for drawing routines
1152 save_drawable = gport->drawable;
1153 save_view = gport->view;
1154 save_width = gport->width;
1155 save_height = gport->height;
1156 save_max_width = PCB->MaxWidth;
1157 save_max_height = PCB->MaxHeight;
1159 gtk_widget_get_allocation (widget, &allocation);
1160 xz = (double) pinout->x_max / allocation.width;
1161 yz = (double) pinout->y_max / allocation.height;
1162 if (xz > yz)
1163 gport->view.coord_per_px = xz;
1164 else
1165 gport->view.coord_per_px = yz;
1167 gport->drawable = window;
1168 gport->width = allocation.width;
1169 gport->height = allocation.height;
1170 gport->view.width = allocation.width * gport->view.coord_per_px;
1171 gport->view.height = allocation.height * gport->view.coord_per_px;
1172 gport->view.x0 = (pinout->x_max - gport->view.width) / 2;
1173 gport->view.y0 = (pinout->y_max - gport->view.height) / 2;
1174 PCB->MaxWidth = pinout->x_max;
1175 PCB->MaxHeight = pinout->y_max;
1177 /* clear background */
1178 gdk_draw_rectangle (window, priv->bg_gc, TRUE,
1179 0, 0, allocation.width, allocation.height);
1181 /* call the drawing routine */
1182 hid_expose_callback (&ghid_graphics, NULL, pinout->element);
1184 gport->drawable = save_drawable;
1185 gport->view = save_view;
1186 gport->width = save_width;
1187 gport->height = save_height;
1188 PCB->MaxWidth = save_max_width;
1189 PCB->MaxHeight = save_max_height;
1191 return FALSE;
1194 GdkPixmap *
1195 ghid_render_pixmap (int cx, int cy, double zoom, int width, int height, int depth)
1197 GdkPixmap *pixmap;
1198 GdkDrawable *save_drawable;
1199 view_data save_view;
1200 int save_width, save_height;
1201 BoxType region;
1202 render_priv *priv = gport->render_priv;
1204 save_drawable = gport->drawable;
1205 save_view = gport->view;
1206 save_width = gport->width;
1207 save_height = gport->height;
1209 pixmap = gdk_pixmap_new (NULL, width, height, depth);
1211 /* Setup drawable and zoom factor for drawing routines
1214 gport->drawable = pixmap;
1215 gport->view.coord_per_px = zoom;
1216 gport->width = width;
1217 gport->height = height;
1218 gport->view.width = width * gport->view.coord_per_px;
1219 gport->view.height = height * gport->view.coord_per_px;
1220 gport->view.x0 = gport->view.flip_x ? PCB->MaxWidth - cx : cx;
1221 gport->view.x0 -= gport->view.height / 2;
1222 gport->view.y0 = gport->view.flip_y ? PCB->MaxHeight - cy : cy;
1223 gport->view.y0 -= gport->view.width / 2;
1225 /* clear background */
1226 gdk_draw_rectangle (pixmap, priv->bg_gc, TRUE, 0, 0, width, height);
1228 /* call the drawing routine */
1229 region.X1 = MIN(Px(0), Px(gport->width + 1));
1230 region.Y1 = MIN(Py(0), Py(gport->height + 1));
1231 region.X2 = MAX(Px(0), Px(gport->width + 1));
1232 region.Y2 = MAX(Py(0), Py(gport->height + 1));
1234 region.X1 = MAX (0, MIN (PCB->MaxWidth, region.X1));
1235 region.X2 = MAX (0, MIN (PCB->MaxWidth, region.X2));
1236 region.Y1 = MAX (0, MIN (PCB->MaxHeight, region.Y1));
1237 region.Y2 = MAX (0, MIN (PCB->MaxHeight, region.Y2));
1239 hid_expose_callback (&ghid_graphics, &region, NULL);
1241 gport->drawable = save_drawable;
1242 gport->view = save_view;
1243 gport->width = save_width;
1244 gport->height = save_height;
1246 return pixmap;
1249 HID_DRAW *
1250 ghid_request_debug_draw (void)
1252 /* No special setup requirements, drawing goes into
1253 * the backing pixmap. */
1254 return &ghid_graphics;
1257 void
1258 ghid_flush_debug_draw (void)
1260 ghid_screen_update ();
1261 gdk_flush ();
1264 void
1265 ghid_finish_debug_draw (void)
1267 ghid_flush_debug_draw ();
1268 /* No special tear down requirements
1272 bool
1273 ghid_event_to_pcb_coords (int event_x, int event_y, Coord *pcb_x, Coord *pcb_y)
1275 *pcb_x = EVENT_TO_PCB_X (event_x);
1276 *pcb_y = EVENT_TO_PCB_Y (event_y);
1278 return true;
1281 bool
1282 ghid_pcb_to_event_coords (Coord pcb_x, Coord pcb_y, int *event_x, int *event_y)
1284 *event_x = DRAW_X (pcb_x);
1285 *event_y = DRAW_Y (pcb_y);
1287 return true;
1291 #define LEAD_USER_WIDTH 0.2 /* millimeters */
1292 #define LEAD_USER_PERIOD (1000 / 5) /* 5fps (in ms) */
1293 #define LEAD_USER_VELOCITY 3. /* millimeters per second */
1294 #define LEAD_USER_ARC_COUNT 3
1295 #define LEAD_USER_ARC_SEPARATION 3. /* millimeters */
1296 #define LEAD_USER_INITIAL_RADIUS 10. /* millimetres */
1297 #define LEAD_USER_COLOR_R 1.
1298 #define LEAD_USER_COLOR_G 1.
1299 #define LEAD_USER_COLOR_B 0.
1301 static void
1302 draw_lead_user (render_priv *priv)
1304 GdkWindow *window = gtk_widget_get_window (gport->drawing_area);
1305 GtkStyle *style = gtk_widget_get_style (gport->drawing_area);
1306 int i;
1307 Coord radius = priv->lead_user_radius;
1308 Coord width = MM_TO_COORD (LEAD_USER_WIDTH);
1309 Coord separation = MM_TO_COORD (LEAD_USER_ARC_SEPARATION);
1310 static GdkGC *lead_gc = NULL;
1311 GdkColor lead_color;
1313 if (!priv->lead_user)
1314 return;
1316 if (lead_gc == NULL)
1318 lead_gc = gdk_gc_new (window);
1319 gdk_gc_copy (lead_gc, style->white_gc);
1320 gdk_gc_set_function (lead_gc, GDK_XOR);
1321 gdk_gc_set_clip_origin (lead_gc, 0, 0);
1322 lead_color.pixel = 0;
1323 lead_color.red = (int)(65535. * LEAD_USER_COLOR_R);
1324 lead_color.green = (int)(65535. * LEAD_USER_COLOR_G);
1325 lead_color.blue = (int)(65535. * LEAD_USER_COLOR_B);
1326 gdk_color_alloc (gport->colormap, &lead_color);
1327 gdk_gc_set_foreground (lead_gc, &lead_color);
1330 set_clip (priv, lead_gc);
1331 gdk_gc_set_line_attributes (lead_gc, Vz (width),
1332 GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1334 /* arcs at the approrpriate radii */
1336 for (i = 0; i < LEAD_USER_ARC_COUNT; i++, radius -= separation)
1338 if (radius < width)
1339 radius += MM_TO_COORD (LEAD_USER_INITIAL_RADIUS);
1341 /* Draw an arc at radius */
1342 gdk_draw_arc (gport->drawable, lead_gc, FALSE,
1343 Vx (priv->lead_user_x - radius),
1344 Vy (priv->lead_user_y - radius),
1345 Vz (2. * radius), Vz (2. * radius),
1346 0, 360 * 64);
1350 gboolean
1351 lead_user_cb (gpointer data)
1353 render_priv *priv = data;
1354 Coord step;
1355 double elapsed_time;
1357 /* Queue a redraw */
1358 ghid_invalidate_all ();
1360 /* Update radius */
1361 elapsed_time = g_timer_elapsed (priv->lead_user_timer, NULL);
1362 g_timer_start (priv->lead_user_timer);
1364 step = MM_TO_COORD (LEAD_USER_VELOCITY * elapsed_time);
1365 if (priv->lead_user_radius > step)
1366 priv->lead_user_radius -= step;
1367 else
1368 priv->lead_user_radius = MM_TO_COORD (LEAD_USER_INITIAL_RADIUS);
1370 return TRUE;
1373 void
1374 ghid_lead_user_to_location (Coord x, Coord y)
1376 render_priv *priv = gport->render_priv;
1378 ghid_cancel_lead_user ();
1380 priv->lead_user = true;
1381 priv->lead_user_x = x;
1382 priv->lead_user_y = y;
1383 priv->lead_user_radius = MM_TO_COORD (LEAD_USER_INITIAL_RADIUS);
1384 priv->lead_user_timeout = g_timeout_add (LEAD_USER_PERIOD, lead_user_cb, priv);
1385 priv->lead_user_timer = g_timer_new ();
1388 void
1389 ghid_cancel_lead_user (void)
1391 render_priv *priv = gport->render_priv;
1393 if (priv->lead_user_timeout)
1394 g_source_remove (priv->lead_user_timeout);
1396 if (priv->lead_user_timer)
1397 g_timer_destroy (priv->lead_user_timer);
1399 if (priv->lead_user)
1400 ghid_invalidate_all ();
1402 priv->lead_user_timeout = 0;
1403 priv->lead_user_timer = NULL;
1404 priv->lead_user = false;