pcb 4.1.1
An interactive printed circuit board layout editor.

gtkhid-gdk.c

Go to the documentation of this file.
00001 
00034 #ifdef HAVE_CONFIG_H
00035 #include "config.h"
00036 #endif
00037 
00038 #include <stdio.h>
00039 
00040 #include "crosshair.h"
00041 #include "clip.h"
00042 #include "../hidint.h"
00043 #include "gui.h"
00044 #include "hid/common/draw_helpers.h"
00045 
00046 #ifdef HAVE_LIBDMALLOC
00047 #include <dmalloc.h>
00048 #endif
00049 
00050 extern HID ghid_hid;
00051 extern HID_DRAW ghid_graphics;
00052 
00053 /* Sets priv->u_gc to the "right" GC to use (wrt mask or window)
00054 */
00055 #define USE_GC(gc) if (!use_gc(gc)) return
00056 
00057 static enum mask_mode cur_mask = HID_MASK_OFF;
00058 static int mask_seq = 0;
00059 
00060 typedef struct render_priv {
00061   GdkGC *bg_gc;
00062   GdkGC *offlimits_gc;
00063   GdkGC *mask_gc;
00064   GdkGC *u_gc;
00065   GdkGC *grid_gc;
00066   bool clip;
00067   GdkRectangle clip_rect;
00068   int attached_invalidate_depth;
00069   int mark_invalidate_depth;
00070 
00071   /* Feature for leading the user to a particular location */
00072   guint lead_user_timeout;
00073   GTimer *lead_user_timer;
00074   bool lead_user;
00075   Coord lead_user_radius;
00076   Coord lead_user_x;
00077   Coord lead_user_y;
00078 
00079   hidGC crosshair_gc;
00080 } render_priv;
00081 
00082 
00083 typedef struct hid_gc_struct
00084 {
00085   HID *me_pointer;
00086   GdkGC *gc;
00087 
00088   gchar *colorname;
00089   Coord width;
00090   gint cap, join;
00091   gchar xor_mask;
00092   gint mask_seq;
00093 }
00094 hid_gc_struct;
00095 
00096 
00097 static void draw_lead_user (render_priv *priv);
00098 
00099 
00100 int
00101 ghid_set_layer (const char *name, int group, int empty)
00102 {
00103   int idx = group;
00104   if (idx >= 0 && idx < max_group)
00105     {
00106       int n = PCB->LayerGroups.Number[group];
00107       for (idx = 0; idx < n-1; idx ++)
00108         {
00109           int ni = PCB->LayerGroups.Entries[group][idx];
00110           if (ni >= 0 && ni < max_copper_layer + SILK_LAYER
00111               && PCB->Data->Layer[ni].On)
00112             break;
00113         }
00114       idx = PCB->LayerGroups.Entries[group][idx];
00115     }
00116 
00117   if (idx >= 0 && idx < max_copper_layer + SILK_LAYER)
00118     return /*pinout ? 1 : */ PCB->Data->Layer[idx].On;
00119   if (idx < 0)
00120     {
00121       switch (SL_TYPE (idx))
00122         {
00123         case SL_INVISIBLE:
00124           return /* pinout ? 0 : */ PCB->InvisibleObjectsOn;
00125         case SL_MASK:
00126           if (SL_MYSIDE (idx) /*&& !pinout */ )
00127             return TEST_FLAG (SHOWMASKFLAG, PCB);
00128           return 0;
00129         case SL_SILK:
00130           if (SL_MYSIDE (idx) /*|| pinout */ )
00131             return PCB->ElementOn;
00132           return 0;
00133         case SL_ASSY:
00134           return 0;
00135         case SL_PDRILL:
00136         case SL_UDRILL:
00137           return 1;
00138         case SL_RATS:
00139           return PCB->RatOn;
00140         }
00141     }
00142   return 0;
00143 }
00144 
00145 void
00146 ghid_destroy_gc (hidGC gc)
00147 {
00148   if (gc->gc)
00149     g_object_unref (gc->gc);
00150   g_free (gc);
00151 }
00152 
00153 hidGC
00154 ghid_make_gc (void)
00155 {
00156   hidGC rv;
00157 
00158   rv = g_new0 (hid_gc_struct, 1);
00159   rv->me_pointer = &ghid_hid;
00160   rv->colorname = Settings.BackgroundColor;
00161   return rv;
00162 }
00163 
00170 static void
00171 set_clip (render_priv *priv, GdkGC *gc)
00172 {
00173   if (gc == NULL)
00174     return;
00175 
00176   if (priv->clip)
00177     gdk_gc_set_clip_rectangle (gc, &priv->clip_rect);
00178   else
00179     gdk_gc_set_clip_mask (gc, NULL);
00180 }
00181 
00185 void
00186 ghid_draw_grid (BoxType * region)
00187 {
00188   static GdkPoint *points = 0;
00189   static int npoints = 0;
00190   Coord x1, y1, x2, y2, x, y;
00191   int n, i;
00192   render_priv *priv = gport->render_priv;
00193 
00194   if (!Settings.DrawGrid)
00195     return; /* grid hidden */
00196   if (Vz (PCB->Grid) < MIN_GRID_DISTANCE)
00197     return; /* zoomed out too far, points to close together */
00198   if (!priv->grid_gc) /* create a graphics context if we don't have one */
00199   {
00200     if (gdk_color_parse (Settings.GridColor, &gport->grid_color))
00201     {
00202       gport->grid_color.red ^= gport->bg_color.red;
00203       gport->grid_color.green ^= gport->bg_color.green;
00204       gport->grid_color.blue ^= gport->bg_color.blue;
00205       gdk_color_alloc (gport->colormap, &gport->grid_color);
00206     }
00207     priv->grid_gc = gdk_gc_new (gport->drawable);
00208     gdk_gc_set_function (priv->grid_gc, GDK_XOR);
00209     gdk_gc_set_foreground (priv->grid_gc, &gport->grid_color);
00210     gdk_gc_set_clip_origin (priv->grid_gc, 0, 0);
00211     set_clip (priv, priv->grid_gc);
00212   } /* end if (!priv->grid_gc) */
00213   x1 = GridFit (SIDE_X (gport->view.x0), PCB->Grid, PCB->GridOffsetX);
00214   y1 = GridFit (SIDE_Y (gport->view.y0), PCB->Grid, PCB->GridOffsetY);
00215   x2 = GridFit (SIDE_X (gport->view.x0 + gport->view.width - 1),
00216                 PCB->Grid, PCB->GridOffsetX);
00217   y2 = GridFit (SIDE_Y (gport->view.y0 + gport->view.height - 1),
00218                 PCB->Grid, PCB->GridOffsetY);
00219   if (x1 > x2)
00220     {
00221       Coord tmp = x1;
00222       x1 = x2;
00223       x2 = tmp;
00224     }
00225   if (y1 > y2)
00226     {
00227       Coord tmp = y1;
00228       y1 = y2;
00229       y2 = tmp;
00230     }
00231   
00232   /* The bounding points could have been outside of the drawing area */
00233   if (Vx (x1) < 0)    x1 += PCB->Grid;
00234   if (Vy (y1) < 0)    y1 += PCB->Grid;
00235   if (Vx (x2) >= gport->width)    x2 -= PCB->Grid;
00236   if (Vy (y2) >= gport->height)    y2 -= PCB->Grid;
00237   
00238   n = (x2 - x1) / PCB->Grid + 1; /* Number of points in one row */
00239   if (n > npoints)
00240     { /* [n]points are static, reallocate if we need more memory */
00241       npoints = n + 10;
00242       points = (GdkPoint *)realloc (points, npoints * sizeof (GdkPoint));
00243     }
00244   n = 0;
00245   for (x = x1; x <= x2; x += PCB->Grid)
00246     { /* compute all the x coordinates */
00247       points[n].x = Vx (x);
00248       n++;
00249     }
00250   if (n == 0)
00251     return;
00252   for (y = y1; y <= y2; y += PCB->Grid)
00253     { /* reuse the row of points at each y */
00254       for (i = 0; i < n; i++)   points[i].y = Vy (y);
00255       /* draw all the points in a row for a given y */
00256       gdk_draw_points (gport->drawable, priv->grid_gc, points, n);
00257     }
00258 }
00259 
00260 /* ------------------------------------------------------------ */
00261 static void
00262 ghid_draw_bg_image (void)
00263 {
00264   static GdkPixbuf *pixbuf;
00265   GdkInterpType interp_type;
00266   gint x, y, w, h, w_src, h_src;
00267   static gint w_scaled, h_scaled;
00268   render_priv *priv = gport->render_priv;
00269 
00270   if (!ghidgui->bg_pixbuf)
00271     return;
00272 
00273   w = PCB->MaxWidth / gport->view.coord_per_px;
00274   h = PCB->MaxHeight / gport->view.coord_per_px;
00275   x = gport->view.x0 / gport->view.coord_per_px;
00276   y = gport->view.y0 / gport->view.coord_per_px;
00277 
00278   if (w_scaled != w || h_scaled != h)
00279     {
00280       if (pixbuf)
00281         g_object_unref (G_OBJECT (pixbuf));
00282 
00283       w_src = gdk_pixbuf_get_width (ghidgui->bg_pixbuf);
00284       h_src = gdk_pixbuf_get_height (ghidgui->bg_pixbuf);
00285       if (w > w_src && h > h_src)
00286         interp_type = GDK_INTERP_NEAREST;
00287       else
00288         interp_type = GDK_INTERP_BILINEAR;
00289 
00290       pixbuf =
00291         gdk_pixbuf_scale_simple (ghidgui->bg_pixbuf, w, h, interp_type);
00292       w_scaled = w;
00293       h_scaled = h;
00294     }
00295   if (pixbuf)
00296     gdk_pixbuf_render_to_drawable (pixbuf, gport->drawable, priv->bg_gc,
00297                                    x, y, 0, 0,
00298                                    w - x, h - y, GDK_RGB_DITHER_NORMAL, 0, 0);
00299 }
00300 
00301 #define WHICH_GC(gc) (cur_mask == HID_MASK_CLEAR ? priv->mask_gc : (gc)->gc)
00302 
00303 void
00304 ghid_use_mask (enum mask_mode mode)
00305 {
00306   static int mask_seq_id = 0;
00307   GdkColor color;
00308   render_priv *priv = gport->render_priv;
00309 
00310   if (!gport->pixmap)
00311     return;
00312   if (mode == cur_mask)
00313     return;
00314   switch (mode)
00315     {
00316     case HID_MASK_OFF:
00317       gport->drawable = gport->pixmap;
00318       mask_seq = 0;
00319       break;
00320 
00321     case HID_MASK_BEFORE:
00322       /* The HID asks not to receive this mask type, so warn if we get it */
00323       g_return_if_reached ();
00324 
00325     case HID_MASK_CLEAR:
00326       if (!gport->mask)
00327         gport->mask = gdk_pixmap_new (0, gport->width, gport->height, 1);
00328       gport->drawable = gport->mask;
00329       mask_seq = 0;
00330       if (!priv->mask_gc)
00331         {
00332           priv->mask_gc = gdk_gc_new (gport->drawable);
00333           gdk_gc_set_clip_origin (priv->mask_gc, 0, 0);
00334           set_clip (priv, priv->mask_gc);
00335         }
00336       color.pixel = 1;
00337       gdk_gc_set_foreground (priv->mask_gc, &color);
00338       gdk_draw_rectangle (gport->drawable, priv->mask_gc, TRUE, 0, 0,
00339                           gport->width, gport->height);
00340       color.pixel = 0;
00341       gdk_gc_set_foreground (priv->mask_gc, &color);
00342       break;
00343 
00344     case HID_MASK_AFTER:
00345       mask_seq_id++;
00346       if (!mask_seq_id)
00347         mask_seq_id = 1;
00348       mask_seq = mask_seq_id;
00349 
00350       gport->drawable = gport->pixmap;
00351       break;
00352 
00353     }
00354   cur_mask = mode;
00355 }
00356 
00357 
00358 typedef struct
00359 {
00360   int color_set;
00361   GdkColor color;
00362   int xor_set;
00363   GdkColor xor_color;
00364 } ColorCache;
00365 
00366 
00367   /* Config helper functions for when the user changes color preferences.
00368      |  set_special colors used in the gtkhid.
00369    */
00370 static void
00371 set_special_grid_color (void)
00372 {
00373   render_priv *priv = gport->render_priv;
00374 
00375   if (!gport->colormap)
00376     return;
00377   gport->grid_color.red ^= gport->bg_color.red;
00378   gport->grid_color.green ^= gport->bg_color.green;
00379   gport->grid_color.blue ^= gport->bg_color.blue;
00380   gdk_color_alloc (gport->colormap, &gport->grid_color);
00381   if (priv->grid_gc)
00382     gdk_gc_set_foreground (priv->grid_gc, &gport->grid_color);
00383 }
00384 
00385 void
00386 ghid_set_special_colors (HID_Attribute * ha)
00387 {
00388   render_priv *priv = gport->render_priv;
00389 
00390   if (!ha->name || !ha->value)
00391     return;
00392   if (!strcmp (ha->name, "background-color") && priv->bg_gc)
00393     {
00394       ghid_map_color_string (*(char **) ha->value, &gport->bg_color);
00395       gdk_gc_set_foreground (priv->bg_gc, &gport->bg_color);
00396       set_special_grid_color ();
00397     }
00398   else if (!strcmp (ha->name, "off-limit-color") && priv->offlimits_gc)
00399     {
00400       ghid_map_color_string (*(char **) ha->value, &gport->offlimits_color);
00401       gdk_gc_set_foreground (priv->offlimits_gc, &gport->offlimits_color);
00402     }
00403   else if (!strcmp (ha->name, "grid-color") && priv->grid_gc)
00404     {
00405       ghid_map_color_string (*(char **) ha->value, &gport->grid_color);
00406       set_special_grid_color ();
00407     }
00408 }
00409 
00410 void
00411 ghid_set_color (hidGC gc, const char *name)
00412 {
00413   static void *cache = 0;
00414   hidval cval;
00415 
00416   if (name == NULL)
00417     {
00418       fprintf (stderr, "%s():  name = NULL, setting to magenta\n",
00419                __FUNCTION__);
00420       name = "magenta";
00421     }
00422 
00423   gc->colorname = (char *) name;
00424   if (!gc->gc)
00425     return;
00426   if (gport->colormap == 0)
00427     gport->colormap = gtk_widget_get_colormap (gport->top_window);
00428 
00429   if (strcmp (name, "erase") == 0)
00430     {
00431       gdk_gc_set_foreground (gc->gc, &gport->bg_color);
00432     }
00433   else if (strcmp (name, "drill") == 0)
00434     {
00435       gdk_gc_set_foreground (gc->gc, &gport->offlimits_color);
00436     }
00437   else
00438     {
00439       ColorCache *cc;
00440       if (hid_cache_color (0, name, &cval, &cache))
00441         cc = (ColorCache *) cval.ptr;
00442       else
00443         {
00444           cc = (ColorCache *) malloc (sizeof (ColorCache));
00445           memset (cc, 0, sizeof (*cc));
00446           cval.ptr = cc;
00447           hid_cache_color (1, name, &cval, &cache);
00448         }
00449 
00450       if (!cc->color_set)
00451         {
00452           if (gdk_color_parse (name, &cc->color))
00453             gdk_color_alloc (gport->colormap, &cc->color);
00454           else
00455             gdk_color_white (gport->colormap, &cc->color);
00456           cc->color_set = 1;
00457         }
00458       if (gc->xor_mask)
00459         {
00460           if (!cc->xor_set)
00461             {
00462               cc->xor_color.red = cc->color.red ^ gport->bg_color.red;
00463               cc->xor_color.green = cc->color.green ^ gport->bg_color.green;
00464               cc->xor_color.blue = cc->color.blue ^ gport->bg_color.blue;
00465               gdk_color_alloc (gport->colormap, &cc->xor_color);
00466               cc->xor_set = 1;
00467             }
00468           gdk_gc_set_foreground (gc->gc, &cc->xor_color);
00469         }
00470       else
00471         {
00472           gdk_gc_set_foreground (gc->gc, &cc->color);
00473         }
00474     }
00475 }
00476 
00477 void
00478 ghid_set_line_cap (hidGC gc, EndCapStyle style)
00479 {
00480   render_priv *priv = gport->render_priv;
00481 
00482   switch (style)
00483     {
00484     case Trace_Cap:
00485     case Round_Cap:
00486       gc->cap = GDK_CAP_ROUND;
00487       gc->join = GDK_JOIN_ROUND;
00488       break;
00489     case Square_Cap:
00490     case Beveled_Cap:
00491       gc->cap = GDK_CAP_PROJECTING;
00492       gc->join = GDK_JOIN_MITER;
00493       break;
00494     }
00495   if (gc->gc)
00496     gdk_gc_set_line_attributes (WHICH_GC (gc),
00497                                 Vz (gc->width), GDK_LINE_SOLID,
00498                                 (GdkCapStyle)gc->cap, (GdkJoinStyle)gc->join);
00499 }
00500 
00501 void
00502 ghid_set_line_width (hidGC gc, Coord width)
00503 {
00504   render_priv *priv = gport->render_priv;
00505 
00506   gc->width = width;
00507   if (gc->gc)
00508     gdk_gc_set_line_attributes (WHICH_GC (gc),
00509                                 Vz (gc->width), GDK_LINE_SOLID,
00510                                 (GdkCapStyle)gc->cap, (GdkJoinStyle)gc->join);
00511 }
00512 
00513 void
00514 ghid_set_draw_xor (hidGC gc, int xor_mask)
00515 {
00516   gc->xor_mask = xor_mask;
00517   if (!gc->gc)
00518     return;
00519   gdk_gc_set_function (gc->gc, xor_mask ? GDK_XOR : GDK_COPY);
00520   ghid_set_color (gc, gc->colorname);
00521 }
00522 
00523 static int
00524 use_gc (hidGC gc)
00525 {
00526   render_priv *priv = gport->render_priv;
00527   GdkWindow *window = gtk_widget_get_window (gport->top_window);
00528 
00529   if (gc->me_pointer != &ghid_hid)
00530     {
00531       fprintf (stderr, "Fatal: GC from another HID passed to GTK HID\n");
00532       abort ();
00533     }
00534 
00535   if (!gport->pixmap)
00536     return 0;
00537   if (!gc->gc)
00538     {
00539       gc->gc = gdk_gc_new (window);
00540       ghid_set_color (gc, gc->colorname);
00541       ghid_set_line_width (gc, gc->width);
00542       ghid_set_line_cap (gc, (EndCapStyle)gc->cap);
00543       ghid_set_draw_xor (gc, gc->xor_mask);
00544       gdk_gc_set_clip_origin (gc->gc, 0, 0);
00545     }
00546   if (gc->mask_seq != mask_seq)
00547     {
00548       if (mask_seq)
00549         gdk_gc_set_clip_mask (gc->gc, gport->mask);
00550       else
00551         set_clip (priv, gc->gc);
00552       gc->mask_seq = mask_seq;
00553     }
00554   priv->u_gc = WHICH_GC (gc);
00555   return 1;
00556 }
00557 
00558 void
00559 ghid_draw_line (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
00560 {
00561   double dx1, dy1, dx2, dy2;
00562   render_priv *priv = gport->render_priv;
00563 
00564   dx1 = Vx ((double) x1);
00565   dy1 = Vy ((double) y1);
00566   dx2 = Vx ((double) x2);
00567   dy2 = Vy ((double) y2);
00568 
00569   if (!ClipLine (0, 0, gport->width, gport->height,
00570                  &dx1, &dy1, &dx2, &dy2, gc->width / gport->view.coord_per_px))
00571     return;
00572 
00573   USE_GC (gc);
00574   gdk_draw_line (gport->drawable, priv->u_gc, dx1, dy1, dx2, dy2);
00575 }
00576 
00577 void
00578 ghid_draw_arc (hidGC gc, Coord cx, Coord cy,
00579                Coord xradius, Coord yradius, Angle start_angle, Angle delta_angle)
00580 {
00581   gint vrx, vry;
00582   gint w, h, radius;
00583   render_priv *priv = gport->render_priv;
00584 
00585   w = gport->width * gport->view.coord_per_px;
00586   h = gport->height * gport->view.coord_per_px;
00587   radius = (xradius > yradius) ? xradius : yradius;
00588   if (SIDE_X (cx) < gport->view.x0 - radius
00589       || SIDE_X (cx) > gport->view.x0 + w + radius
00590       || SIDE_Y (cy) < gport->view.y0 - radius
00591       || SIDE_Y (cy) > gport->view.y0 + h + radius)
00592     return;
00593 
00594   USE_GC (gc);
00595   vrx = Vz (xradius);
00596   vry = Vz (yradius);
00597 
00598   if (gport->view.flip_x)
00599     {
00600       start_angle = 180 - start_angle;
00601       delta_angle = -delta_angle;
00602     }
00603   if (gport->view.flip_y)
00604     {
00605       start_angle = -start_angle;
00606       delta_angle = -delta_angle;
00607     }
00608   /* make sure we fall in the -180 to +180 range */
00609   start_angle = NormalizeAngle (start_angle);
00610   if (start_angle >= 180)  start_angle -= 360;
00611 
00612   gdk_draw_arc (gport->drawable, priv->u_gc, 0,
00613                 Vx (cx) - vrx, Vy (cy) - vry,
00614                 vrx * 2, vry * 2, (start_angle + 180) * 64, delta_angle * 64);
00615 }
00616 
00617 void
00618 ghid_draw_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
00619 {
00620   gint w, h, lw;
00621   render_priv *priv = gport->render_priv;
00622 
00623   lw = gc->width;
00624   w = gport->width * gport->view.coord_per_px;
00625   h = gport->height * gport->view.coord_per_px;
00626 
00627   if ((SIDE_X (x1) < gport->view.x0 - lw
00628        && SIDE_X (x2) < gport->view.x0 - lw)
00629       || (SIDE_X (x1) > gport->view.x0 + w + lw
00630           && SIDE_X (x2) > gport->view.x0 + w + lw)
00631       || (SIDE_Y (y1) < gport->view.y0 - lw
00632           && SIDE_Y (y2) < gport->view.y0 - lw)
00633       || (SIDE_Y (y1) > gport->view.y0 + h + lw
00634           && SIDE_Y (y2) > gport->view.y0 + h + lw))
00635     return;
00636 
00637   x1 = Vx (x1);
00638   y1 = Vy (y1);
00639   x2 = Vx (x2);
00640   y2 = Vy (y2);
00641 
00642   if (x1 > x2)
00643     {
00644       gint xt = x1;
00645       x1 = x2;
00646       x2 = xt;
00647     }
00648   if (y1 > y2)
00649     {
00650       gint yt = y1;
00651       y1 = y2;
00652       y2 = yt;
00653     }
00654 
00655   USE_GC (gc);
00656   gdk_draw_rectangle (gport->drawable, priv->u_gc, FALSE,
00657                       x1, y1, x2 - x1 + 1, y2 - y1 + 1);
00658 }
00659 
00660 
00661 void
00662 ghid_fill_circle (hidGC gc, Coord cx, Coord cy, Coord radius)
00663 {
00664   gint w, h, vr;
00665   render_priv *priv = gport->render_priv;
00666 
00667   w = gport->width * gport->view.coord_per_px;
00668   h = gport->height * gport->view.coord_per_px;
00669   if (SIDE_X (cx) < gport->view.x0 - radius
00670       || SIDE_X (cx) > gport->view.x0 + w + radius
00671       || SIDE_Y (cy) < gport->view.y0 - radius
00672       || SIDE_Y (cy) > gport->view.y0 + h + radius)
00673     return;
00674 
00675   USE_GC (gc);
00676   vr = Vz (radius);
00677   gdk_draw_arc (gport->drawable, priv->u_gc, TRUE,
00678                 Vx (cx) - vr, Vy (cy) - vr, vr * 2, vr * 2, 0, 360 * 64);
00679 }
00680 
00681 void
00682 ghid_fill_polygon (hidGC gc, int n_coords, Coord *x, Coord *y)
00683 {
00684   static GdkPoint *points = 0;
00685   static int npoints = 0;
00686   int i;
00687   render_priv *priv = gport->render_priv;
00688   USE_GC (gc);
00689 
00690   if (npoints < n_coords)
00691     {
00692       npoints = n_coords + 1;
00693       points = (GdkPoint *)realloc (points, npoints * sizeof (GdkPoint));
00694     }
00695   for (i = 0; i < n_coords; i++)
00696     {
00697       points[i].x = Vx (x[i]);
00698       points[i].y = Vy (y[i]);
00699     }
00700   gdk_draw_polygon (gport->drawable, priv->u_gc, 1, points, n_coords);
00701 }
00702 
00703 void
00704 ghid_fill_rect (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
00705 {
00706   gint w, h, lw, xx, yy;
00707   render_priv *priv = gport->render_priv;
00708 
00709   lw = gc->width;
00710   w = gport->width * gport->view.coord_per_px;
00711   h = gport->height * gport->view.coord_per_px;
00712 
00713   if ((SIDE_X (x1) < gport->view.x0 - lw
00714        && SIDE_X (x2) < gport->view.x0 - lw)
00715       || (SIDE_X (x1) > gport->view.x0 + w + lw
00716           && SIDE_X (x2) > gport->view.x0 + w + lw)
00717       || (SIDE_Y (y1) < gport->view.y0 - lw
00718           && SIDE_Y (y2) < gport->view.y0 - lw)
00719       || (SIDE_Y (y1) > gport->view.y0 + h + lw
00720           && SIDE_Y (y2) > gport->view.y0 + h + lw))
00721     return;
00722 
00723   x1 = Vx (x1);
00724   y1 = Vy (y1);
00725   x2 = Vx (x2);
00726   y2 = Vy (y2);
00727   if (x2 < x1)
00728     {
00729       xx = x1;
00730       x1 = x2;
00731       x2 = xx;
00732     }
00733   if (y2 < y1)
00734     {
00735       yy = y1;
00736       y1 = y2;
00737       y2 = yy;
00738     }
00739   USE_GC (gc);
00740   gdk_draw_rectangle (gport->drawable, priv->u_gc, TRUE,
00741                       x1, y1, x2 - x1 + 1, y2 - y1 + 1);
00742 }
00743 
00747 static void
00748 redraw_region (GdkRectangle *rect)
00749 {
00750   int eleft, eright, etop, ebottom;
00751   BoxType region; /* section to draw in PCB coordinates */
00752   render_priv *priv = gport->render_priv;
00753 
00754   if (!gport->pixmap)
00755     return;
00756 
00757   if (rect != NULL)
00758     { /* draw the region passed as an argument */
00759       priv->clip_rect = *rect;
00760       priv->clip = true;
00761     }
00762   else
00763     { /* specified region was null, draw the entire area */
00764       priv->clip_rect.x = 0;
00765       priv->clip_rect.y = 0;
00766       priv->clip_rect.width = gport->width;
00767       priv->clip_rect.height = gport->height;
00768       priv->clip = false;
00769     }
00770 
00771   /* set the clip to prevent changes to anything outside the region */
00772   set_clip (priv, priv->bg_gc);
00773   set_clip (priv, priv->offlimits_gc);
00774   set_clip (priv, priv->mask_gc);
00775   set_clip (priv, priv->grid_gc);
00776 
00777   /* Compute the PCB coordinates of the area to redraw */
00778   /* Find the upper and lower corners of the drawing area */
00779   region.X1 = MIN(Px(priv->clip_rect.x),
00780                   Px(priv->clip_rect.x + priv->clip_rect.width + 1));
00781   region.Y1 = MIN(Py(priv->clip_rect.y),
00782                   Py(priv->clip_rect.y + priv->clip_rect.height + 1));
00783   region.X2 = MAX(Px(priv->clip_rect.x),
00784                   Px(priv->clip_rect.x + priv->clip_rect.width + 1));
00785   region.Y2 = MAX(Py(priv->clip_rect.y),
00786                   Py(priv->clip_rect.y + priv->clip_rect.height + 1));
00787 
00788   /* Restrict the drawing region to inside the PCB area */
00789   region.X1 = MAX (0, MIN (PCB->MaxWidth,  region.X1));
00790   region.X2 = MAX (0, MIN (PCB->MaxWidth,  region.X2));
00791   region.Y1 = MAX (0, MIN (PCB->MaxHeight, region.Y1));
00792   region.Y2 = MAX (0, MIN (PCB->MaxHeight, region.Y2));
00793 
00794   /* Compute the viewport coordinates of the edges of the PCB area */
00795   eleft = Vx (0);
00796   eright = Vx (PCB->MaxWidth);
00797   etop = Vy (0);
00798   ebottom = Vy (PCB->MaxHeight);
00799   if (eleft > eright)
00800     {
00801       int tmp = eleft;
00802       eleft = eright;
00803       eright = tmp;
00804     }
00805   if (etop > ebottom)
00806     {
00807       int tmp = etop;
00808       etop = ebottom;
00809       ebottom = tmp;
00810     }
00811 
00812   /* If the PCB isn't filling the entire screen, draw the dead area around it */
00813   if (eleft > 0) /* draw dead area on the left side */
00814     gdk_draw_rectangle (gport->drawable, priv->offlimits_gc,
00815                         1, 0, 0, eleft, gport->height);
00816   else
00817     eleft = 0;
00818   if (eright < gport->width) /* draw dead area on the right side */
00819     gdk_draw_rectangle (gport->drawable, priv->offlimits_gc,
00820                         1, eright, 0, gport->width - eright, gport->height);
00821   else
00822     eright = gport->width;
00823   if (etop > 0) /* draw dead area on the top */
00824     gdk_draw_rectangle (gport->drawable, priv->offlimits_gc,
00825                         1, eleft, 0, eright - eleft + 1, etop);
00826   else
00827     etop = 0;
00828   if (ebottom < gport->height) /* draw dead area on the bottom */
00829     gdk_draw_rectangle (gport->drawable, priv->offlimits_gc,
00830                         1, eleft, ebottom, eright - eleft + 1,
00831                         gport->height - ebottom);
00832   else
00833     ebottom = gport->height;
00834 
00835   /* Draw the PCB background color */
00836   gdk_draw_rectangle (gport->drawable, priv->bg_gc, 1,
00837                       eleft, etop, eright - eleft + 1, ebottom - etop + 1);
00838 
00839   ghid_draw_bg_image();
00840 
00841   /* Draw all of the PCB stuff, elements, traces, etc. */
00842   hid_expose_callback (&ghid_hid, &region, 0);
00843   
00844   ghid_graphics.draw_grid (&region);
00845 
00846   /* In some cases we are called with the crosshair still off */
00847   if (priv->attached_invalidate_depth == 0)
00848     DrawAttached (priv->crosshair_gc);
00849 
00850   /* In some cases we are called with the mark still off */
00851   if (priv->mark_invalidate_depth == 0)
00852     DrawMark (priv->crosshair_gc);
00853 
00854   draw_lead_user (priv);
00855 
00856   /* Turn clipping off */
00857   priv->clip = false;
00858 
00859   /* Reset the clip for bg_gc, as it is used outside this function */
00860   gdk_gc_set_clip_mask (priv->bg_gc, NULL);
00861 }
00862 
00863 void
00864 ghid_invalidate_lr (Coord left, Coord right, Coord top, Coord bottom)
00865 {
00866   Coord dleft, dright, dtop, dbottom;
00867   Coord minx, maxx, miny, maxy;
00868   GdkRectangle rect;
00869 
00870   dleft = Vx (left);
00871   dright = Vx (right);
00872   dtop = Vy (top);
00873   dbottom = Vy (bottom);
00874 
00875   minx = MIN (dleft, dright);
00876   maxx = MAX (dleft, dright);
00877   miny = MIN (dtop, dbottom);
00878   maxy = MAX (dtop, dbottom);
00879 
00880   rect.x = minx;
00881   rect.y = miny;
00882   rect.width = maxx - minx;
00883   rect.height = maxy - miny;
00884 
00885   redraw_region (&rect);
00886   ghid_screen_update ();
00887 }
00888 
00889 
00890 void
00891 ghid_invalidate_all ()
00892 {
00893   redraw_region (NULL);
00894   ghid_screen_update ();
00895 }
00896 
00897 void
00898 ghid_notify_crosshair_change (bool changes_complete)
00899 {
00900   render_priv *priv = gport->render_priv;
00901 
00902   /* We sometimes get called before the GUI is up */
00903   if (gport->drawing_area == NULL)
00904     return;
00905 
00906   if (changes_complete)
00907     priv->attached_invalidate_depth --;
00908 
00909   if (priv->attached_invalidate_depth < 0)
00910     {
00911       priv->attached_invalidate_depth = 0;
00912       /* A mismatch of changes_complete == false and == true notifications
00913        * is not expected to occur, but we will try to handle it gracefully.
00914        * As we know the crosshair will have been shown already, we must
00915        * repaint the entire view to be sure not to leave an artaefact.
00916        */
00917       ghid_invalidate_all ();
00918       return;
00919     }
00920 
00921   if (priv->attached_invalidate_depth == 0)
00922     DrawAttached (priv->crosshair_gc);
00923 
00924   if (!changes_complete)
00925     {
00926       priv->attached_invalidate_depth ++;
00927     }
00928   else if (gport->drawing_area != NULL)
00929     {
00930       /* Queue a GTK expose when changes are complete */
00931       ghid_draw_area_update (gport, NULL);
00932     }
00933 }
00934 
00935 void
00936 ghid_notify_mark_change (bool changes_complete)
00937 {
00938   render_priv *priv = gport->render_priv;
00939 
00940   /* We sometimes get called before the GUI is up */
00941   if (gport->drawing_area == NULL)
00942     return;
00943 
00944   if (changes_complete)
00945     priv->mark_invalidate_depth --;
00946 
00947   if (priv->mark_invalidate_depth < 0)
00948     {
00949       priv->mark_invalidate_depth = 0;
00950       /* A mismatch of changes_complete == false and == true notifications
00951        * is not expected to occur, but we will try to handle it gracefully.
00952        * As we know the mark will have been shown already, we must
00953        * repaint the entire view to be sure not to leave an artaefact.
00954        */
00955       ghid_invalidate_all ();
00956       return;
00957     }
00958 
00959   if (priv->mark_invalidate_depth == 0)
00960     DrawMark (priv->crosshair_gc);
00961 
00962   if (!changes_complete)
00963     {
00964       priv->mark_invalidate_depth ++;
00965     }
00966   else if (gport->drawing_area != NULL)
00967     {
00968       /* Queue a GTK expose when changes are complete */
00969       ghid_draw_area_update (gport, NULL);
00970     }
00971 }
00972 
00973 static void
00974 draw_right_cross (GdkGC *xor_gc, gint x, gint y)
00975 {
00976   GdkWindow *window = gtk_widget_get_window (gport->drawing_area);
00977 
00978   gdk_draw_line (window, xor_gc, x, 0, x, gport->height);
00979   gdk_draw_line (window, xor_gc, 0, y, gport->width, y);
00980 }
00981 
00982 static void
00983 draw_slanted_cross (GdkGC *xor_gc, gint x, gint y)
00984 {
00985   GdkWindow *window = gtk_widget_get_window (gport->drawing_area);
00986   gint x0, y0, x1, y1;
00987 
00988   x0 = x + (gport->height - y);
00989   x0 = MAX(0, MIN (x0, gport->width));
00990   x1 = x - y;
00991   x1 = MAX(0, MIN (x1, gport->width));
00992   y0 = y + (gport->width - x);
00993   y0 = MAX(0, MIN (y0, gport->height));
00994   y1 = y - x;
00995   y1 = MAX(0, MIN (y1, gport->height));
00996   gdk_draw_line (window, xor_gc, x0, y0, x1, y1);
00997 
00998   x0 = x - (gport->height - y);
00999   x0 = MAX(0, MIN (x0, gport->width));
01000   x1 = x + y;
01001   x1 = MAX(0, MIN (x1, gport->width));
01002   y0 = y + x;
01003   y0 = MAX(0, MIN (y0, gport->height));
01004   y1 = y - (gport->width - x);
01005   y1 = MAX(0, MIN (y1, gport->height));
01006   gdk_draw_line (window, xor_gc, x0, y0, x1, y1);
01007 }
01008 
01009 static void
01010 draw_dozen_cross (GdkGC *xor_gc, gint x, gint y)
01011 {
01012   GdkWindow *window = gtk_widget_get_window (gport->drawing_area);
01013   gint x0, y0, x1, y1;
01014   gdouble tan60 = sqrt (3);
01015 
01016   x0 = x + (gport->height - y) / tan60;
01017   x0 = MAX(0, MIN (x0, gport->width));
01018   x1 = x - y / tan60;
01019   x1 = MAX(0, MIN (x1, gport->width));
01020   y0 = y + (gport->width - x) * tan60;
01021   y0 = MAX(0, MIN (y0, gport->height));
01022   y1 = y - x * tan60;
01023   y1 = MAX(0, MIN (y1, gport->height));
01024   gdk_draw_line (window, xor_gc, x0, y0, x1, y1);
01025 
01026   x0 = x + (gport->height - y) * tan60;
01027   x0 = MAX(0, MIN (x0, gport->width));
01028   x1 = x - y * tan60;
01029   x1 = MAX(0, MIN (x1, gport->width));
01030   y0 = y + (gport->width - x) / tan60;
01031   y0 = MAX(0, MIN (y0, gport->height));
01032   y1 = y - x / tan60;
01033   y1 = MAX(0, MIN (y1, gport->height));
01034   gdk_draw_line (window, xor_gc, x0, y0, x1, y1);
01035 
01036   x0 = x - (gport->height - y) / tan60;
01037   x0 = MAX(0, MIN (x0, gport->width));
01038   x1 = x + y / tan60;
01039   x1 = MAX(0, MIN (x1, gport->width));
01040   y0 = y + x * tan60;
01041   y0 = MAX(0, MIN (y0, gport->height));
01042   y1 = y - (gport->width - x) * tan60;
01043   y1 = MAX(0, MIN (y1, gport->height));
01044   gdk_draw_line (window, xor_gc, x0, y0, x1, y1);
01045 
01046   x0 = x - (gport->height - y) * tan60;
01047   x0 = MAX(0, MIN (x0, gport->width));
01048   x1 = x + y * tan60;
01049   x1 = MAX(0, MIN (x1, gport->width));
01050   y0 = y + x / tan60;
01051   y0 = MAX(0, MIN (y0, gport->height));
01052   y1 = y - (gport->width - x) / tan60;
01053   y1 = MAX(0, MIN (y1, gport->height));
01054   gdk_draw_line (window, xor_gc, x0, y0, x1, y1);
01055 }
01056 
01057 static void
01058 draw_crosshair (render_priv *priv)
01059 {
01060   GdkWindow *window = gtk_widget_get_window (gport->drawing_area);
01061   GtkStyle *style = gtk_widget_get_style (gport->drawing_area);
01062   gint x, y;
01063   static GdkGC *xor_gc;
01064   static GdkColor cross_color;
01065 
01066   if (gport->crosshair_x < 0 || ghidgui->creating || !gport->has_entered)
01067     return;
01068 
01069   if (!xor_gc)
01070     {
01071       xor_gc = gdk_gc_new (window);
01072       gdk_gc_copy (xor_gc, style->white_gc);
01073       gdk_gc_set_function (xor_gc, GDK_XOR);
01074       gdk_gc_set_clip_origin (xor_gc, 0, 0);
01075       set_clip (priv, xor_gc);
01076       /* FIXME: when CrossColor changed from config */
01077       ghid_map_color_string (Settings.CrossColor, &cross_color);
01078     }
01079 
01080   gdk_gc_set_foreground (xor_gc, &cross_color);
01081 
01082   x = DRAW_X (gport->crosshair_x);
01083   y = DRAW_Y (gport->crosshair_y);
01084 
01085   draw_right_cross (xor_gc, x, y);
01086   if (Crosshair.shape == Union_Jack_Crosshair_Shape)
01087     draw_slanted_cross (xor_gc, x, y);
01088   if (Crosshair.shape == Dozen_Crosshair_Shape)
01089     draw_dozen_cross (xor_gc, x, y);
01090 }
01091 
01092 void
01093 ghid_init_renderer (int *argc, char ***argv, GHidPort *port)
01094 {
01095   /* Init any GC's required */
01096   port->render_priv = g_new0 (render_priv, 1);
01097   port->render_priv->crosshair_gc = gui->graphics->make_gc ();
01098 }
01099 
01100 void
01101 ghid_shutdown_renderer (GHidPort *port)
01102 {
01103   render_priv *priv = port->render_priv;
01104 
01105   gui->graphics->destroy_gc (priv->crosshair_gc);
01106   ghid_cancel_lead_user ();
01107   g_free (port->render_priv);
01108   port->render_priv = NULL;
01109 }
01110 
01111 void
01112 ghid_init_drawing_widget (GtkWidget *widget, GHidPort *port)
01113 {
01114 }
01115 
01116 void
01117 ghid_drawing_area_configure_hook (GHidPort *port)
01118 {
01119   static int done_once = 0;
01120   render_priv *priv = port->render_priv;
01121 
01122   if (!done_once)
01123     {
01124       priv->bg_gc = gdk_gc_new (port->drawable);
01125       gdk_gc_set_foreground (priv->bg_gc, &port->bg_color);
01126       gdk_gc_set_clip_origin (priv->bg_gc, 0, 0);
01127 
01128       priv->offlimits_gc = gdk_gc_new (port->drawable);
01129       gdk_gc_set_foreground (priv->offlimits_gc, &port->offlimits_color);
01130       gdk_gc_set_clip_origin (priv->offlimits_gc, 0, 0);
01131       done_once = 1;
01132     }
01133 
01134   if (port->mask)
01135     {
01136       g_object_unref (port->mask);
01137       port->mask = gdk_pixmap_new (0, port->width, port->height, 1);
01138     }
01139 }
01140 
01141 void
01142 ghid_screen_update (void)
01143 {
01144   render_priv *priv = gport->render_priv;
01145   GdkWindow *window = gtk_widget_get_window (gport->drawing_area);
01146 
01147   if (gport->pixmap == NULL)
01148     return;
01149 
01150   gdk_draw_drawable (window, priv->bg_gc, gport->pixmap,
01151                      0, 0, 0, 0, gport->width, gport->height);
01152   draw_crosshair (priv);
01153 }
01154 
01155 gboolean
01156 ghid_drawing_area_expose_cb (GtkWidget *widget,
01157                              GdkEventExpose *ev,
01158                              GHidPort *port)
01159 {
01160   render_priv *priv = port->render_priv;
01161   GdkWindow *window = gtk_widget_get_window (gport->drawing_area);
01162 
01163   gdk_draw_drawable (window, priv->bg_gc, port->pixmap,
01164                      ev->area.x, ev->area.y, ev->area.x, ev->area.y,
01165                      ev->area.width, ev->area.height);
01166   draw_crosshair (priv);
01167   return FALSE;
01168 }
01169 
01170 void
01171 ghid_port_drawing_realize_cb (GtkWidget *widget, gpointer data)
01172 {
01173 }
01174 
01175 gboolean
01176 ghid_pinout_preview_expose (GtkWidget *widget,
01177                             GdkEventExpose *ev)
01178 {
01179   GhidPinoutPreview *pinout = GHID_PINOUT_PREVIEW (widget);
01180   GdkWindow *window = gtk_widget_get_window (widget);
01181   GdkDrawable *save_drawable;
01182   GtkAllocation allocation;
01183   view_data save_view;
01184   int save_width, save_height;
01185   Coord save_max_width;
01186   Coord save_max_height;
01187   double xz, yz;
01188   render_priv *priv = gport->render_priv;
01189 
01190   /* Setup drawable and zoom factor for drawing routines
01191    */
01192   save_drawable = gport->drawable;
01193   save_view = gport->view;
01194   save_width = gport->width;
01195   save_height = gport->height;
01196   save_max_width = PCB->MaxWidth;
01197   save_max_height = PCB->MaxHeight;
01198 
01199   gtk_widget_get_allocation (widget, &allocation);
01200   xz = (double) pinout->x_max / allocation.width;
01201   yz = (double) pinout->y_max / allocation.height;
01202   if (xz > yz)
01203     gport->view.coord_per_px = xz;
01204   else
01205     gport->view.coord_per_px = yz;
01206 
01207   gport->drawable = window;
01208   gport->width = allocation.width;
01209   gport->height = allocation.height;
01210   gport->view.width = allocation.width * gport->view.coord_per_px;
01211   gport->view.height = allocation.height * gport->view.coord_per_px;
01212   gport->view.x0 = (pinout->x_max - gport->view.width) / 2;
01213   gport->view.y0 = (pinout->y_max - gport->view.height) / 2;
01214   PCB->MaxWidth =  pinout->x_max;
01215   PCB->MaxHeight = pinout->y_max;
01216 
01217   /* clear background */
01218   gdk_draw_rectangle (window, priv->bg_gc, TRUE,
01219                       0, 0, allocation.width, allocation.height);
01220 
01221   /* call the drawing routine */
01222   hid_expose_callback (&ghid_hid, NULL, pinout->element);
01223 
01224   gport->drawable = save_drawable;
01225   gport->view = save_view;
01226   gport->width = save_width;
01227   gport->height = save_height;
01228   PCB->MaxWidth = save_max_width;
01229   PCB->MaxHeight = save_max_height;
01230 
01231   return FALSE;
01232 }
01233 
01234 GdkPixmap *
01235 ghid_render_pixmap (int cx, int cy, double zoom, int width, int height, int depth)
01236 {
01237   GdkPixmap *pixmap;
01238   GdkDrawable *save_drawable;
01239   view_data save_view;
01240   int save_width, save_height;
01241   BoxType region;
01242   render_priv *priv = gport->render_priv;
01243 
01244   save_drawable = gport->drawable;
01245   save_view = gport->view;
01246   save_width = gport->width;
01247   save_height = gport->height;
01248 
01249   pixmap = gdk_pixmap_new (NULL, width, height, depth);
01250 
01251   /* Setup drawable and zoom factor for drawing routines
01252    */
01253 
01254   gport->drawable = pixmap;
01255   gport->view.coord_per_px = zoom;
01256   gport->width = width;
01257   gport->height = height;
01258   gport->view.width = width * gport->view.coord_per_px;
01259   gport->view.height = height * gport->view.coord_per_px;
01260   gport->view.x0 = gport->view.flip_x ? PCB->MaxWidth - cx : cx;
01261   gport->view.x0 -= gport->view.height / 2;
01262   gport->view.y0 = gport->view.flip_y ? PCB->MaxHeight - cy : cy;
01263   gport->view.y0 -= gport->view.width  / 2;
01264 
01265   /* clear background */
01266   gdk_draw_rectangle (pixmap, priv->bg_gc, TRUE, 0, 0, width, height);
01267 
01268   /* call the drawing routine */
01269   region.X1 = MIN(Px(0), Px(gport->width + 1));
01270   region.Y1 = MIN(Py(0), Py(gport->height + 1));
01271   region.X2 = MAX(Px(0), Px(gport->width + 1));
01272   region.Y2 = MAX(Py(0), Py(gport->height + 1));
01273 
01274   region.X1 = MAX (0, MIN (PCB->MaxWidth,  region.X1));
01275   region.X2 = MAX (0, MIN (PCB->MaxWidth,  region.X2));
01276   region.Y1 = MAX (0, MIN (PCB->MaxHeight, region.Y1));
01277   region.Y2 = MAX (0, MIN (PCB->MaxHeight, region.Y2));
01278 
01279   hid_expose_callback (&ghid_hid, &region, NULL);
01280 
01281   gport->drawable = save_drawable;
01282   gport->view = save_view;
01283   gport->width = save_width;
01284   gport->height = save_height;
01285 
01286   return pixmap;
01287 }
01288 
01289 HID_DRAW *
01290 ghid_request_debug_draw (void)
01291 {
01292   /* No special setup requirements, drawing goes into
01293    * the backing pixmap. */
01294   return ghid_hid.graphics;
01295 }
01296 
01297 void
01298 ghid_flush_debug_draw (void)
01299 {
01300   ghid_screen_update ();
01301   gdk_flush ();
01302 }
01303 
01304 void
01305 ghid_finish_debug_draw (void)
01306 {
01307   ghid_flush_debug_draw ();
01308   /* No special tear down requirements
01309    */
01310 }
01311 
01312 bool
01313 ghid_event_to_pcb_coords (int event_x, int event_y, Coord *pcb_x, Coord *pcb_y)
01314 {
01315   *pcb_x = EVENT_TO_PCB_X (event_x);
01316   *pcb_y = EVENT_TO_PCB_Y (event_y);
01317 
01318   return true;
01319 }
01320 
01321 bool
01322 ghid_pcb_to_event_coords (Coord pcb_x, Coord pcb_y, int *event_x, int *event_y)
01323 {
01324   *event_x = DRAW_X (pcb_x);
01325   *event_y = DRAW_Y (pcb_y);
01326 
01327   return true;
01328 }
01329 
01330 
01331 #define LEAD_USER_WIDTH           0.2          /* millimeters */
01332 #define LEAD_USER_PERIOD          (1000 / 5)   /* 5fps (in ms) */
01333 #define LEAD_USER_VELOCITY        3.           /* millimeters per second */
01334 #define LEAD_USER_ARC_COUNT       3
01335 #define LEAD_USER_ARC_SEPARATION  3.           /* millimeters */
01336 #define LEAD_USER_INITIAL_RADIUS  10.          /* millimetres */
01337 #define LEAD_USER_COLOR_R         1.
01338 #define LEAD_USER_COLOR_G         1.
01339 #define LEAD_USER_COLOR_B         0.
01340 
01341 static void
01342 draw_lead_user (render_priv *priv)
01343 {
01344   GdkWindow *window = gtk_widget_get_window (gport->drawing_area);
01345   GtkStyle *style = gtk_widget_get_style (gport->drawing_area);
01346   int i;
01347   Coord radius = priv->lead_user_radius;
01348   Coord width = MM_TO_COORD (LEAD_USER_WIDTH);
01349   Coord separation = MM_TO_COORD (LEAD_USER_ARC_SEPARATION);
01350   static GdkGC *lead_gc = NULL;
01351   GdkColor lead_color;
01352 
01353   if (!priv->lead_user)
01354     return;
01355 
01356   if (lead_gc == NULL)
01357     {
01358       lead_gc = gdk_gc_new (window);
01359       gdk_gc_copy (lead_gc, style->white_gc);
01360       gdk_gc_set_function (lead_gc, GDK_XOR);
01361       gdk_gc_set_clip_origin (lead_gc, 0, 0);
01362       lead_color.pixel = 0;
01363       lead_color.red   = (int)(65535. * LEAD_USER_COLOR_R);
01364       lead_color.green = (int)(65535. * LEAD_USER_COLOR_G);
01365       lead_color.blue  = (int)(65535. * LEAD_USER_COLOR_B);
01366       gdk_color_alloc (gport->colormap, &lead_color);
01367       gdk_gc_set_foreground (lead_gc, &lead_color);
01368     }
01369 
01370   set_clip (priv, lead_gc);
01371   gdk_gc_set_line_attributes (lead_gc, Vz (width),
01372                               GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
01373 
01374   /* arcs at the approrpriate radii */
01375 
01376   for (i = 0; i < LEAD_USER_ARC_COUNT; i++, radius -= separation)
01377     {
01378       if (radius < width)
01379         radius += MM_TO_COORD (LEAD_USER_INITIAL_RADIUS);
01380 
01381       /* Draw an arc at radius */
01382       gdk_draw_arc (gport->drawable, lead_gc, FALSE,
01383                     Vx (priv->lead_user_x - radius),
01384                     Vy (priv->lead_user_y - radius),
01385                     Vz (2. * radius), Vz (2. * radius),
01386                     0, 360 * 64);
01387     }
01388 }
01389 
01390 gboolean
01391 lead_user_cb (gpointer data)
01392 {
01393   render_priv *priv = data;
01394   Coord step;
01395   double elapsed_time;
01396 
01397   /* Queue a redraw */
01398   ghid_invalidate_all ();
01399 
01400   /* Update radius */
01401   elapsed_time = g_timer_elapsed (priv->lead_user_timer, NULL);
01402   g_timer_start (priv->lead_user_timer);
01403 
01404   step = MM_TO_COORD (LEAD_USER_VELOCITY * elapsed_time);
01405   if (priv->lead_user_radius > step)
01406     priv->lead_user_radius -= step;
01407   else
01408     priv->lead_user_radius = MM_TO_COORD (LEAD_USER_INITIAL_RADIUS);
01409 
01410   return TRUE;
01411 }
01412 
01413 void
01414 ghid_lead_user_to_location (Coord x, Coord y)
01415 {
01416   render_priv *priv = gport->render_priv;
01417 
01418   ghid_cancel_lead_user ();
01419 
01420   priv->lead_user = true;
01421   priv->lead_user_x = x;
01422   priv->lead_user_y = y;
01423   priv->lead_user_radius = MM_TO_COORD (LEAD_USER_INITIAL_RADIUS);
01424   priv->lead_user_timeout = g_timeout_add (LEAD_USER_PERIOD, lead_user_cb, priv);
01425   priv->lead_user_timer = g_timer_new ();
01426 }
01427 
01428 void
01429 ghid_cancel_lead_user (void)
01430 {
01431   render_priv *priv = gport->render_priv;
01432 
01433   if (priv->lead_user_timeout)
01434     g_source_remove (priv->lead_user_timeout);
01435 
01436   if (priv->lead_user_timer)
01437     g_timer_destroy (priv->lead_user_timer);
01438 
01439   if (priv->lead_user)
01440     ghid_invalidate_all ();
01441 
01442   priv->lead_user_timeout = 0;
01443   priv->lead_user_timer = NULL;
01444   priv->lead_user = false;
01445 }