1 /*!
2  * \file src/crosshair.c
3  *
4  * \brief Crosshair stuff.
5  *
6  * <hr>
7  *
8  * <h1><b>Copyright.</b></h1>\n
9  *
10  * PCB, interactive printed circuit board design
11  *
12  * Copyright (C) 1994,1995,1996 Thomas Nau
13  *
14  * This program is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License along
25  * with this program; if not, write to the Free Software Foundation, Inc.,
26  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27  *
28  * Contact addresses for paper mail and Email:
29  * Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
30  * Thomas.Nau@rz.uni-ulm.de
31  */
32 
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36 
37 #include <memory.h>
38 #include <math.h>
39 
40 #include "global.h"
41 #include "hid_draw.h"
42 
43 #include "crosshair.h"
44 
45 #include "buffer.h"
46 #include "data.h"
47 #include "draw.h"
48 #include "error.h"
49 #include "line.h"
50 #include "misc.h"
51 #include "mymem.h"
52 #include "search.h"
53 #include "polygon.h"
54 
55 #ifdef HAVE_LIBDMALLOC
56 #include <dmalloc.h>
57 #endif
58 
59 typedef struct
60 {
61   int x, y;
62 } point;
63 
64 
65 /*!
66  * \brief Make a copy of the pin structure, moved to the correct
67  * position
68  */
69 static void
thindraw_moved_pv(hidGC gc,PinType * pv,Coord x,Coord y)70 thindraw_moved_pv (hidGC gc, PinType *pv, Coord x, Coord y)
71 {
72   PinType moved_pv = *pv;
73   moved_pv.X += x;
74   moved_pv.Y += y;
75 
76   gui->graphics->thindraw_pcb_pv (gc, gc, &moved_pv, true, false);
77 }
78 
79 /*!
80  * \brief Draw a dashed line.
81  */
82 static void
draw_dashed_line(hidGC gc,Coord x1,Coord y1,Coord x2,Coord y2)83 draw_dashed_line (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2)
84 {
85   /*! \todo we need a real geometrical library, using double here is
86    * plain wrong. */
87   double dx = x2-x1;
88   double dy = y2-y1;
89   double len_squared = dx*dx + dy*dy;
90   int n;
91   const int segs = 11; /* must be odd */
92 
93   if (len_squared < 1000000)
94   {
95     /*! \todo line too short, just draw it -> magic value;
96      * with a proper geo lib this would be gone anyway. */
97     gui->graphics->draw_line (gc, x1, y1, x2, y2);
98     return;
99   }
100 
101   /* first seg is drawn from x1, y1 with no rounding error due to n-1 == 0 */
102   for (n = 1; n < segs; n += 2)
103     gui->graphics->draw_line (gc,
104                               x1 + (dx * (double) (n-1) / (double) segs),
105                               y1 + (dy * (double) (n-1) / (double) segs),
106                               x1 + (dx * (double) n / (double) segs),
107                               y1 + (dy * (double) n / (double) segs));
108 
109   /* make sure the last segment is drawn properly to x2 and y2,
110    * don't leave room for rounding errors. */
111   gui->graphics->draw_line (gc,
112                             x2 - (dx / (double) segs),
113                             y2 - (dy / (double) segs),
114                             x2,
115                             y2);
116 }
117 
118 /*!
119  * \brief Creates a tmp polygon with coordinates converted to screen
120  * system.
121  */
122 static void
XORPolygon(hidGC gc,PolygonType * polygon,Coord dx,Coord dy,int dash_last)123 XORPolygon (hidGC gc, PolygonType *polygon, Coord dx, Coord dy, int dash_last)
124 {
125   Cardinal i;
126   for (i = 0; i < polygon->PointN; i++)
127     {
128       Cardinal next = next_contour_point (polygon, i);
129 
130       if (next == 0)
131         { /* last line: sometimes the implicit closing line */
132           if (i == 1) /* corner case: don't draw two lines on top of
133                        * each other - with XOR it looks bad */
134             continue;
135 
136         if (dash_last)
137           {
138             draw_dashed_line (gc,
139                               polygon->Points[i].X + dx,
140                               polygon->Points[i].Y + dy,
141                               polygon->Points[next].X + dx,
142                               polygon->Points[next].Y + dy);
143             break; /* skip normal line draw below */
144           }
145         }
146 
147       /* normal contour line */
148       gui->graphics->draw_line (gc,
149                                 polygon->Points[i].X + dx,
150                                 polygon->Points[i].Y + dy,
151                                 polygon->Points[next].X + dx,
152                                 polygon->Points[next].Y + dy);
153     }
154 }
155 
156 /*!
157  * \brief Draws the outline of an arc.
158  */
159 static void
XORDrawAttachedArc(hidGC gc,Coord thick)160 XORDrawAttachedArc (hidGC gc, Coord thick)
161 {
162   ArcType arc;
163   BoxType *bx;
164   Coord wx, wy;
165   Angle sa, dir;
166   Coord wid = thick / 2;
167 
168   wx = Crosshair.X - Crosshair.AttachedBox.Point1.X;
169   wy = Crosshair.Y - Crosshair.AttachedBox.Point1.Y;
170   if (wx == 0 && wy == 0)
171     return;
172   arc.X = Crosshair.AttachedBox.Point1.X;
173   arc.Y = Crosshair.AttachedBox.Point1.Y;
174   if (XOR (Crosshair.AttachedBox.otherway, abs (wy) > abs (wx)))
175     {
176       arc.X = Crosshair.AttachedBox.Point1.X + abs (wy) * SGNZ (wx);
177       sa = (wx >= 0) ? 0 : 180;
178 #ifdef ARC45
179       if (abs (wy) >= 2 * abs (wx))
180 	dir = (SGNZ (wx) == SGNZ (wy)) ? 45 : -45;
181       else
182 #endif
183 	dir = (SGNZ (wx) == SGNZ (wy)) ? 90 : -90;
184     }
185   else
186     {
187       arc.Y = Crosshair.AttachedBox.Point1.Y + abs (wx) * SGNZ (wy);
188       sa = (wy >= 0) ? -90 : 90;
189 #ifdef ARC45
190       if (abs (wx) >= 2 * abs (wy))
191 	dir = (SGNZ (wx) == SGNZ (wy)) ? -45 : 45;
192       else
193 #endif
194 	dir = (SGNZ (wx) == SGNZ (wy)) ? -90 : 90;
195       wy = wx;
196     }
197   wy = abs (wy);
198   arc.StartAngle = sa;
199   arc.Delta = dir;
200   arc.Width = arc.Height = wy;
201   bx = GetArcEnds (&arc);
202   /*  sa = sa - 180; */
203   gui->graphics->draw_arc (gc, arc.X, arc.Y, wy + wid, wy + wid, sa, dir);
204   if (wid > pixel_slop)
205     {
206       gui->graphics->draw_arc (gc, arc.X, arc.Y, wy - wid, wy - wid, sa, dir);
207       gui->graphics->draw_arc (gc, bx->X1, bx->Y1, wid, wid, sa,      -180 * SGN (dir));
208       gui->graphics->draw_arc (gc, bx->X2, bx->Y2, wid, wid, sa + dir, 180 * SGN (dir));
209     }
210 }
211 
212 /*!
213  * \brief Draws the outline of a line.
214  */
215 static void
XORDrawAttachedLine(hidGC gc,Coord x1,Coord y1,Coord x2,Coord y2,Coord thick)216 XORDrawAttachedLine (hidGC gc, Coord x1, Coord y1, Coord x2, Coord y2, Coord thick)
217 {
218   Coord dx, dy, ox, oy;
219   double h;
220 
221   dx = x2 - x1;
222   dy = y2 - y1;
223   if (dx != 0 || dy != 0)
224     h = 0.5 * thick / hypot (dx, dy);
225   else
226     h = 0.0;
227   ox = dy * h + 0.5 * SGN (dy);
228   oy = -(dx * h + 0.5 * SGN (dx));
229   gui->graphics->draw_line (gc, x1 + ox, y1 + oy, x2 + ox, y2 + oy);
230   if (abs (ox) >= pixel_slop || abs (oy) >= pixel_slop)
231     {
232       Angle angle = atan2 (dx, dy) * 57.295779;
233       gui->graphics->draw_line (gc, x1 - ox, y1 - oy, x2 - ox, y2 - oy);
234       gui->graphics->draw_arc (gc, x1, y1, thick / 2, thick / 2, angle - 180, 180);
235       gui->graphics->draw_arc (gc, x2, y2, thick / 2, thick / 2, angle, 180);
236     }
237 }
238 
239 /*!
240  * \brief Draws the elements of a loaded circuit which is to be merged
241  * in.
242  */
243 static void
XORDrawElement(hidGC gc,ElementType * Element,Coord DX,Coord DY)244 XORDrawElement (hidGC gc, ElementType *Element, Coord DX, Coord DY)
245 {
246   /* if no silkscreen, draw the bounding box */
247   if (Element->ArcN == 0 && Element->LineN == 0)
248     {
249       gui->graphics->draw_line (gc,
250                                 DX + Element->BoundingBox.X1,
251                                 DY + Element->BoundingBox.Y1,
252                                 DX + Element->BoundingBox.X1,
253                                 DY + Element->BoundingBox.Y2);
254       gui->graphics->draw_line (gc,
255                                 DX + Element->BoundingBox.X1,
256                                 DY + Element->BoundingBox.Y2,
257                                 DX + Element->BoundingBox.X2,
258                                 DY + Element->BoundingBox.Y2);
259       gui->graphics->draw_line (gc,
260                                 DX + Element->BoundingBox.X2,
261                                 DY + Element->BoundingBox.Y2,
262                                 DX + Element->BoundingBox.X2,
263                                 DY + Element->BoundingBox.Y1);
264       gui->graphics->draw_line (gc,
265                                 DX + Element->BoundingBox.X2,
266                                 DY + Element->BoundingBox.Y1,
267                                 DX + Element->BoundingBox.X1,
268                                 DY + Element->BoundingBox.Y1);
269     }
270   else
271     {
272       ELEMENTLINE_LOOP (Element);
273       {
274         gui->graphics->draw_line (gc,
275                                   DX + line->Point1.X,
276                                   DY + line->Point1.Y,
277                                   DX + line->Point2.X,
278                                   DY + line->Point2.Y);
279       }
280       END_LOOP;
281 
282       /* arc coordinates and angles have to be converted to X11 notation */
283       ARC_LOOP (Element);
284       {
285         gui->graphics->draw_arc (gc,
286                                  DX + arc->X,
287                                  DY + arc->Y,
288                                  arc->Width, arc->Height, arc->StartAngle, arc->Delta);
289       }
290       END_LOOP;
291     }
292   /* pin coordinates and angles have to be converted to X11 notation */
293   PIN_LOOP (Element);
294   {
295     thindraw_moved_pv (gc, pin, DX, DY);
296   }
297   END_LOOP;
298 
299   /* pads */
300   PAD_LOOP (Element);
301   {
302     if (PCB->InvisibleObjectsOn ||
303         (TEST_FLAG (ONSOLDERFLAG, pad) != 0) == Settings.ShowBottomSide)
304       {
305         /* Make a copy of the pad structure, moved to the correct position */
306         PadType moved_pad = *pad;
307         moved_pad.Point1.X += DX; moved_pad.Point1.Y += DY;
308         moved_pad.Point2.X += DX; moved_pad.Point2.Y += DY;
309 
310         gui->graphics->thindraw_pcb_pad (gc, &moved_pad, false, false);
311       }
312   }
313   END_LOOP;
314   /* mark */
315   gui->graphics->draw_line (gc,
316                             Element->MarkX + DX - EMARK_SIZE,
317                             Element->MarkY + DY,
318                             Element->MarkX + DX,
319                             Element->MarkY + DY - EMARK_SIZE);
320   gui->graphics->draw_line (gc,
321                             Element->MarkX + DX + EMARK_SIZE,
322                             Element->MarkY + DY,
323                             Element->MarkX + DX,
324                             Element->MarkY + DY - EMARK_SIZE);
325   gui->graphics->draw_line (gc,
326                             Element->MarkX + DX - EMARK_SIZE,
327                             Element->MarkY + DY,
328                             Element->MarkX + DX,
329                             Element->MarkY + DY + EMARK_SIZE);
330   gui->graphics->draw_line (gc,
331                             Element->MarkX + DX + EMARK_SIZE,
332                             Element->MarkY + DY,
333                             Element->MarkX + DX,
334                             Element->MarkY + DY + EMARK_SIZE);
335 }
336 
337 /*!
338  * \brief Draws all visible and attached objects of the pastebuffer.
339  */
340 static void
XORDrawBuffer(hidGC gc,BufferType * Buffer)341 XORDrawBuffer (hidGC gc, BufferType *Buffer)
342 {
343   Cardinal i;
344   Coord x, y;
345 
346   /* set offset */
347   x = Crosshair.X - Buffer->X;
348   y = Crosshair.Y - Buffer->Y;
349 
350   /* draw all visible layers */
351   for (i = 0; i < max_copper_layer + SILK_LAYER; i++)
352     if (PCB->Data->Layer[i].On)
353       {
354 	LayerType *layer = &Buffer->Data->Layer[i];
355 
356 	LINE_LOOP (layer);
357 	{
358 /*
359 				XORDrawAttachedLine(x +line->Point1.X,
360 					y +line->Point1.Y, x +line->Point2.X,
361 					y +line->Point2.Y, line->Thickness);
362 */
363 	gui->graphics->draw_line (gc,
364 	                          x + line->Point1.X, y + line->Point1.Y,
365 	                          x + line->Point2.X, y + line->Point2.Y);
366 	}
367 	END_LOOP;
368 	ARC_LOOP (layer);
369 	{
370 	  gui->graphics->draw_arc (gc,
371 	                           x + arc->X,
372 	                           y + arc->Y,
373 	                           arc->Width,
374 	                           arc->Height, arc->StartAngle, arc->Delta);
375 	}
376 	END_LOOP;
377 	TEXT_LOOP (layer);
378 	{
379 	  BoxType *box = &text->BoundingBox;
380 	  gui->graphics->draw_rect (gc,
381 	                            x + box->X1, y + box->Y1, x + box->X2, y + box->Y2);
382 	}
383 	END_LOOP;
384 	/* the tmp polygon has n+1 points because the first
385 	 * and the last one are set to the same coordinates
386 	 */
387 	POLYGON_LOOP (layer);
388 	{
389 	  XORPolygon (gc, polygon, x, y, 0);
390 	}
391 	END_LOOP;
392       }
393 
394   /* draw elements if visible */
395   if (PCB->PinOn && PCB->ElementOn)
396     ELEMENT_LOOP (Buffer->Data);
397   {
398     if (FRONT (element) || PCB->InvisibleObjectsOn)
399       XORDrawElement (gc, element, x, y);
400   }
401   END_LOOP;
402 
403   /* and the vias */
404   if (PCB->ViaOn)
405     VIA_LOOP (Buffer->Data);
406   {
407     thindraw_moved_pv (gc, via, x, y);
408   }
409   END_LOOP;
410 }
411 
412 /*!
413  * \brief Draws the rubberband to insert points into polygons/lines/...
414  */
415 static void
XORDrawInsertPointObject(hidGC gc)416 XORDrawInsertPointObject (hidGC gc)
417 {
418   LineType *line = (LineType *) Crosshair.AttachedObject.Ptr2;
419   PointType *point = (PointType *) Crosshair.AttachedObject.Ptr3;
420 
421   if (Crosshair.AttachedObject.Type != NO_TYPE)
422     {
423       gui->graphics->draw_line (gc, point->X, point->Y, line->Point1.X, line->Point1.Y);
424       gui->graphics->draw_line (gc, point->X, point->Y, line->Point2.X, line->Point2.Y);
425     }
426 }
427 
428 /*!
429  * \brief Draws the attached object while in MOVE_MODE or COPY_MODE.
430  */
431 static void
XORDrawMoveOrCopyObject(hidGC gc)432 XORDrawMoveOrCopyObject (hidGC gc)
433 {
434   RubberbandType *ptr;
435   Cardinal i;
436   Coord dx = Crosshair.X - Crosshair.AttachedObject.X,
437     dy = Crosshair.Y - Crosshair.AttachedObject.Y;
438 
439   switch (Crosshair.AttachedObject.Type)
440     {
441     case VIA_TYPE:
442       {
443         PinType *via = (PinType *) Crosshair.AttachedObject.Ptr1;
444         thindraw_moved_pv (gc, via, dx, dy);
445         break;
446       }
447 
448     case LINE_TYPE:
449       {
450 	LineType *line = (LineType *) Crosshair.AttachedObject.Ptr2;
451 
452 	XORDrawAttachedLine (gc, line->Point1.X + dx, line->Point1.Y + dy,
453 	                         line->Point2.X + dx, line->Point2.Y + dy,
454 	                     line->Thickness);
455 	break;
456       }
457 
458     case ARC_TYPE:
459       {
460 	ArcType *Arc = (ArcType *) Crosshair.AttachedObject.Ptr2;
461 
462 	gui->graphics->draw_arc (gc,
463 	                         Arc->X + dx,
464 	                         Arc->Y + dy,
465 	                         Arc->Width, Arc->Height, Arc->StartAngle, Arc->Delta);
466 	break;
467       }
468 
469     case POLYGON_TYPE:
470       {
471 	PolygonType *polygon =
472 	  (PolygonType *) Crosshair.AttachedObject.Ptr2;
473 
474 	/* the tmp polygon has n+1 points because the first
475 	 * and the last one are set to the same coordinates
476 	 */
477 	XORPolygon (gc, polygon, dx, dy, 0);
478 	break;
479       }
480 
481     case LINEPOINT_TYPE:
482       {
483 	LineType *line;
484 	PointType *point;
485 
486 	line = (LineType *) Crosshair.AttachedObject.Ptr2;
487 	point = (PointType *) Crosshair.AttachedObject.Ptr3;
488 	if (point == &line->Point1)
489 	  XORDrawAttachedLine (gc, point->X + dx, point->Y + dy,
490 	                       line->Point2.X, line->Point2.Y, line->Thickness);
491 	else
492 	  XORDrawAttachedLine (gc, point->X + dx, point->Y + dy,
493 			       line->Point1.X, line->Point1.Y, line->Thickness);
494 	break;
495       }
496 
497     case POLYGONPOINT_TYPE:
498       {
499 	PolygonType *polygon;
500 	PointType *point;
501 	Cardinal point_idx, prev, next;
502 
503 	polygon = (PolygonType *) Crosshair.AttachedObject.Ptr2;
504 	point = (PointType *) Crosshair.AttachedObject.Ptr3;
505 	point_idx = polygon_point_idx (polygon, point);
506 
507 	/* get previous and following point */
508 	prev = prev_contour_point (polygon, point_idx);
509 	next = next_contour_point (polygon, point_idx);
510 
511 	/* draw the two segments */
512 	gui->graphics->draw_line (gc,
513 	                          polygon->Points[prev].X, polygon->Points[prev].Y,
514 	                          point->X + dx, point->Y + dy);
515 	gui->graphics->draw_line (gc,
516 	                          point->X + dx, point->Y + dy,
517 	                          polygon->Points[next].X, polygon->Points[next].Y);
518 	break;
519       }
520 
521     case ELEMENTNAME_TYPE:
522       {
523 	/* locate the element "mark" and draw an association line from crosshair to it */
524 	ElementType *element =
525 	  (ElementType *) Crosshair.AttachedObject.Ptr1;
526 
527 	gui->graphics->draw_line (gc,
528 	                          element->MarkX,
529 	                          element->MarkY, Crosshair.X, Crosshair.Y);
530 	/* fall through to move the text as a box outline */
531       }
532     case TEXT_TYPE:
533       {
534 	TextType *text = (TextType *) Crosshair.AttachedObject.Ptr2;
535 	BoxType *box = &text->BoundingBox;
536 	gui->graphics->draw_rect (gc,
537 	                          box->X1 + dx,
538 	                          box->Y1 + dy, box->X2 + dx, box->Y2 + dy);
539 	break;
540       }
541 
542       /* pin/pad movements result in moving an element */
543     case PAD_TYPE:
544     case PIN_TYPE:
545     case ELEMENT_TYPE:
546       XORDrawElement (gc, (ElementType *) Crosshair.AttachedObject.Ptr2, dx, dy);
547       break;
548     }
549 
550   /* draw the attached rubberband lines too */
551   i = Crosshair.AttachedObject.RubberbandN;
552   ptr = Crosshair.AttachedObject.Rubberband;
553   while (i)
554     {
555       PointType *point1, *point2;
556 
557       if (TEST_FLAG (VIAFLAG, ptr->Line))
558 	{
559 	  /* this is a rat going to a polygon.  do not draw for rubberband */;
560 	}
561       else if (TEST_FLAG (RUBBERENDFLAG, ptr->Line))
562 	{
563 	  /* 'point1' is always the fix-point */
564 	  if (ptr->MovedPoint == &ptr->Line->Point1)
565 	    {
566 	      point1 = &ptr->Line->Point2;
567 	      point2 = &ptr->Line->Point1;
568 	    }
569 	  else
570 	    {
571 	      point1 = &ptr->Line->Point1;
572 	      point2 = &ptr->Line->Point2;
573 	    }
574 	  XORDrawAttachedLine (gc, point1->X, point1->Y,
575 	                       point2->X + dx, point2->Y + dy,
576 	                       ptr->Line->Thickness);
577 	}
578       else if (ptr->MovedPoint == &ptr->Line->Point1)
579 	XORDrawAttachedLine (gc,
580 	                     ptr->Line->Point1.X + dx,
581 	                     ptr->Line->Point1.Y + dy,
582 	                     ptr->Line->Point2.X + dx,
583 	                     ptr->Line->Point2.Y + dy, ptr->Line->Thickness);
584 
585       ptr++;
586       i--;
587     }
588 }
589 
590 /*!
591  * \brief Draws additional stuff that follows the crosshair.
592  */
593 void
DrawAttached(hidGC gc)594 DrawAttached (hidGC gc)
595 {
596   gui->graphics->set_color (gc, Settings.CrosshairColor);
597   gui->graphics->set_draw_xor (gc, 1);
598   gui->graphics->set_line_cap (gc, Trace_Cap);
599   gui->graphics->set_line_width (gc, 1);
600 
601   switch (Settings.Mode)
602     {
603     case VIA_MODE:
604       {
605         /* Make a dummy via structure to draw from */
606         PinType via;
607         via.X = Crosshair.X;
608         via.Y = Crosshair.Y;
609         via.Thickness = Settings.ViaThickness;
610         via.Clearance = 2 * Settings.Keepaway;
611         via.DrillingHole = Settings.ViaDrillingHole;
612         via.Mask = 0;
613         via.Flags = NoFlags ();
614 
615         gui->graphics->thindraw_pcb_pv (gc, gc, &via, true, false);
616 
617         if (TEST_FLAG (SHOWDRCFLAG, PCB))
618           {
619             Coord mask_r = Settings.ViaThickness / 2 + PCB->Bloat;
620             gui->graphics->set_color (gc, Settings.CrossColor);
621             gui->graphics->set_line_cap (gc, Round_Cap);
622             gui->graphics->set_line_width (gc, 0);
623             gui->graphics->draw_arc (gc, via.X, via.Y, mask_r, mask_r, 0, 360);
624             gui->graphics->set_color (gc, Settings.CrosshairColor);
625           }
626         break;
627       }
628 
629       /* the attached line is used by both LINEMODE, POLYGON_MODE and POLYGONHOLE_MODE*/
630     case POLYGON_MODE:
631     case POLYGONHOLE_MODE:
632       /* draw only if starting point is set */
633       if (Crosshair.AttachedLine.State != STATE_FIRST)
634         gui->graphics->draw_line (gc,
635                                   Crosshair.AttachedLine.Point1.X,
636                                   Crosshair.AttachedLine.Point1.Y,
637                                   Crosshair.AttachedLine.Point2.X,
638                                   Crosshair.AttachedLine.Point2.Y);
639 
640       /* draw attached polygon only if in POLYGON_MODE or POLYGONHOLE_MODE */
641       if (Crosshair.AttachedPolygon.PointN > 1)
642 	{
643 	  XORPolygon (gc, &Crosshair.AttachedPolygon, 0, 0, 1);
644 	}
645       break;
646 
647     case ARC_MODE:
648       if (Crosshair.AttachedBox.State != STATE_FIRST)
649 	{
650 	  XORDrawAttachedArc (gc, Settings.LineThickness);
651 	  if (TEST_FLAG (SHOWDRCFLAG, PCB))
652 	    {
653 	      gui->graphics->set_color (gc, Settings.CrossColor);
654 	      XORDrawAttachedArc (gc, Settings.LineThickness + 2 * PCB->Bloat);
655 	      gui->graphics->set_color (gc, Settings.CrosshairColor);
656 	    }
657 
658 	}
659       break;
660 
661     case LINE_MODE:
662       /* draw only if starting point exists and the line has length */
663       if (Crosshair.AttachedLine.State != STATE_FIRST &&
664 	  Crosshair.AttachedLine.draw)
665 	{
666 	  XORDrawAttachedLine (gc, Crosshair.AttachedLine.Point1.X,
667 	                           Crosshair.AttachedLine.Point1.Y,
668 	                           Crosshair.AttachedLine.Point2.X,
669 	                           Crosshair.AttachedLine.Point2.Y,
670 	                           PCB->RatDraw ? 10 : Settings.LineThickness);
671 	  /* draw two lines ? */
672 	  if (PCB->Clipping)
673 	    XORDrawAttachedLine (gc, Crosshair.AttachedLine.Point2.X,
674 	                             Crosshair.AttachedLine.Point2.Y,
675 	                         Crosshair.X, Crosshair.Y,
676 	                         PCB->RatDraw ? 10 : Settings.LineThickness);
677 	  if (TEST_FLAG (SHOWDRCFLAG, PCB))
678 	    {
679 	      gui->graphics->set_color (gc, Settings.CrossColor);
680 	      XORDrawAttachedLine (gc, Crosshair.AttachedLine.Point1.X,
681 	                               Crosshair.AttachedLine.Point1.Y,
682 	                           Crosshair.AttachedLine.Point2.X,
683 	                           Crosshair.AttachedLine.Point2.Y,
684 	                           PCB->RatDraw ? 10 : Settings.LineThickness
685 	                           + 2 * PCB->Bloat);
686 	      if (PCB->Clipping)
687 		XORDrawAttachedLine (gc, Crosshair.AttachedLine.Point2.X,
688 		                         Crosshair.AttachedLine.Point2.Y,
689 		                     Crosshair.X, Crosshair.Y,
690 		                     PCB->RatDraw ? 10 : Settings.
691 		                     LineThickness + 2 * PCB->Bloat);
692 	      gui->graphics->set_color (gc, Settings.CrosshairColor);
693 	    }
694 	}
695       break;
696 
697     case PASTEBUFFER_MODE:
698       XORDrawBuffer (gc, PASTEBUFFER);
699       break;
700 
701     case COPY_MODE:
702     case MOVE_MODE:
703       XORDrawMoveOrCopyObject (gc);
704       break;
705 
706     case INSERTPOINT_MODE:
707       XORDrawInsertPointObject (gc);
708       break;
709     }
710 
711   /* an attached box does not depend on a special mode */
712   if (Crosshair.AttachedBox.State == STATE_SECOND ||
713       Crosshair.AttachedBox.State == STATE_THIRD)
714     {
715       Coord x1, y1, x2, y2;
716 
717       x1 = Crosshair.AttachedBox.Point1.X;
718       y1 = Crosshair.AttachedBox.Point1.Y;
719       x2 = Crosshair.AttachedBox.Point2.X;
720       y2 = Crosshair.AttachedBox.Point2.Y;
721       gui->graphics->draw_rect (gc, x1, y1, x2, y2);
722     }
723 }
724 
725 
726 /*!
727  * \brief Draw the marker position.
728  */
729 void
DrawMark(hidGC gc)730 DrawMark (hidGC gc)
731 {
732   gui->graphics->set_color (gc, Settings.CrosshairColor);
733   gui->graphics->set_draw_xor (gc, 1);
734   gui->graphics->set_line_cap (gc, Trace_Cap);
735   gui->graphics->set_line_width (gc, 1);
736 
737   /* Mark is not drawn when it is not set */
738   if (!Marked.status)
739     return;
740 
741   gui->graphics->draw_line (gc,
742                   Marked.X - MARK_SIZE,
743                   Marked.Y - MARK_SIZE,
744                   Marked.X + MARK_SIZE, Marked.Y + MARK_SIZE);
745   gui->graphics->draw_line (gc,
746                   Marked.X + MARK_SIZE,
747                   Marked.Y - MARK_SIZE,
748                   Marked.X - MARK_SIZE, Marked.Y + MARK_SIZE);
749 }
750 
751 /*!
752  * \brief Returns the nearest grid-point to the given Coord.
753  */
754 Coord
GridFit(Coord x,Coord grid_spacing,Coord grid_offset)755 GridFit (Coord x, Coord grid_spacing, Coord grid_offset)
756 {
757   x -= grid_offset;
758   x = grid_spacing * round ((double) x / grid_spacing);
759   x += grid_offset;
760   return x;
761 }
762 
763 
764 /*!
765  * \brief Notify the GUI that data relating to the crosshair is being
766  * changed.
767  *
768  * The argument passed is false to notify "changes are about to happen",
769  * and true to notify "changes have finished".
770  *
771  * Each call with a 'false' parameter must be matched with a following one
772  * with a 'true' parameter. Unmatched 'true' calls are currently not permitted,
773  * but might be allowed in the future.
774  *
775  * GUIs should not complain if they receive extra calls with 'true' as parameter.
776  * They should initiate a redraw of the crosshair attached objects - which may
777  * (if necessary) mean repainting the whole screen if the GUI hasn't tracked the
778  * location of existing attached drawing.
779  */
780 void
notify_crosshair_change(bool changes_complete)781 notify_crosshair_change (bool changes_complete)
782 {
783   if (gui->notify_crosshair_change)
784     gui->notify_crosshair_change (changes_complete);
785 }
786 
787 
788 /*!
789  * \brief Notify the GUI that data relating to the mark is being changed.
790  *
791  * The argument passed is false to notify "changes are about to happen",
792  * and true to notify "changes have finished".
793  *
794  * Each call with a 'false' parameter must be matched with a following one
795  * with a 'true' parameter. Unmatched 'true' calls are currently not permitted,
796  * but might be allowed in the future.
797  *
798  * GUIs should not complain if they receive extra calls with 'true' as parameter.
799  * They should initiate a redraw of the mark - which may (if necessary) mean
800  * repainting the whole screen if the GUI hasn't tracked the mark's location.
801  */
802 void
notify_mark_change(bool changes_complete)803 notify_mark_change (bool changes_complete)
804 {
805   if (gui->notify_mark_change)
806     gui->notify_mark_change (changes_complete);
807 }
808 
809 
810 /*!
811  * \brief Convenience for plugins using the old {Hide,Restore}Crosshair
812  * API.
813  *
814  * This links up to notify the GUI of the expected changes using the new APIs.
815  *
816  * Use of this old API is deprecated, as the names don't necessarily reflect
817  * what all GUIs may do in response to the notifications. Keeping these APIs
818  * is aimed at easing transition to the newer API, they will emit a harmless
819  * warning at the time of their first use.
820  *
821  */
822 void
HideCrosshair(void)823 HideCrosshair (void)
824 {
825   static bool warned_old_api = false;
826   if (!warned_old_api)
827     {
828       Message (_("WARNING: A plugin is using the deprecated API HideCrosshair().\n"
829                  "         This API may be removed in a future release of PCB.\n"));
830       warned_old_api = true;
831     }
832 
833   notify_crosshair_change (false);
834   notify_mark_change (false);
835 }
836 
837 void
RestoreCrosshair(void)838 RestoreCrosshair (void)
839 {
840   static bool warned_old_api = false;
841   if (!warned_old_api)
842     {
843       Message (_("WARNING: A plugin is using the deprecated API RestoreCrosshair().\n"
844                  "         This API may be removed in a future release of PCB.\n"));
845       warned_old_api = true;
846     }
847 
848   notify_crosshair_change (true);
849   notify_mark_change (true);
850 }
851 
852 /*!
853  * \brief Returns the square of the given number.
854  */
855 static double
square(double x)856 square (double x)
857 {
858   return x * x;
859 }
860 
861 static double
crosshair_sq_dist(CrosshairType * crosshair,Coord x,Coord y)862 crosshair_sq_dist (CrosshairType *crosshair, Coord x, Coord y)
863 {
864   return square (x - crosshair->X) + square (y - crosshair->Y);
865 }
866 
867 struct snap_data {
868   CrosshairType *crosshair;
869   double nearest_sq_dist;
870   bool nearest_is_grid;
871   Coord x, y;
872 };
873 
874 /*!
875  * \brief Snap to a given location if it is the closest thing we found
876  * so far.
877  *
878  * If "prefer_to_grid" is set, the passed location will take preference
879  * over a closer grid points we already snapped to UNLESS the user is
880  * pressing the SHIFT key. If the SHIFT key is pressed, the closest object
881  * (including grid points), is always preferred.
882  */
883 static void
check_snap_object(struct snap_data * snap_data,Coord x,Coord y,bool prefer_to_grid)884 check_snap_object (struct snap_data *snap_data, Coord x, Coord y,
885                    bool prefer_to_grid)
886 {
887   double sq_dist;
888 
889   sq_dist = crosshair_sq_dist (snap_data->crosshair, x, y);
890   if (sq_dist <= snap_data->nearest_sq_dist ||
891       (prefer_to_grid && snap_data->nearest_is_grid && !gui->shift_is_pressed()))
892     {
893       snap_data->x = x;
894       snap_data->y = y;
895       snap_data->nearest_sq_dist = sq_dist;
896       snap_data->nearest_is_grid = false;
897     }
898 }
899 
900 static void
check_snap_offgrid_line(struct snap_data * snap_data,Coord nearest_grid_x,Coord nearest_grid_y)901 check_snap_offgrid_line (struct snap_data *snap_data,
902                          Coord nearest_grid_x,
903                          Coord nearest_grid_y)
904 {
905   void *ptr1, *ptr2, *ptr3;
906   int ans;
907   LineType *line;
908   Coord try_x, try_y;
909   double dx, dy;
910   double dist;
911 
912   if (!TEST_FLAG (SNAPPINFLAG, PCB))
913     return;
914 
915   /* Code to snap at some sensible point along a line */
916   /* Pick the nearest grid-point in the x or y direction
917    * to align with, then adjust until we hit the line
918    */
919   ans = SearchObjectByLocation (LINE_TYPE, &ptr1, &ptr2, &ptr3,
920                                 Crosshair.X, Crosshair.Y, PCB->Grid / 2);
921 
922 
923   if (ans == NO_TYPE)
924     return;
925 
926   line = (LineType *)ptr2;
927 
928   /* Allow snapping to off-grid lines when drawing new lines (on
929    * the same layer), and when moving a line end-point
930    * (but don't snap to the same line)
931    */
932   if ((Settings.Mode != LINE_MODE || CURRENT != ptr1) &&
933       (Settings.Mode != MOVE_MODE ||
934        Crosshair.AttachedObject.Ptr1 != ptr1 ||
935        Crosshair.AttachedObject.Type != LINEPOINT_TYPE ||
936        Crosshair.AttachedObject.Ptr2 == line))
937     return;
938 
939   dx = line->Point2.X - line->Point1.X;
940   dy = line->Point2.Y - line->Point1.Y;
941 
942   /* Try snapping along the X axis */
943   if (dy != 0.)
944     {
945       /* Move in the X direction until we hit the line */
946       try_x = (nearest_grid_y - line->Point1.Y) / dy * dx + line->Point1.X;
947       try_y = nearest_grid_y;
948       check_snap_object (snap_data, try_x, try_y, true);
949     }
950 
951   /* Try snapping along the Y axis */
952   if (dx != 0.)
953     {
954       try_x = nearest_grid_x;
955       try_y = (nearest_grid_x - line->Point1.X) / dx * dy + line->Point1.Y;
956       check_snap_object (snap_data, try_x, try_y, true);
957     }
958 
959   if (dx != dy) /* If line not parallel with dX = dY direction.. */
960     {
961       /* Try snapping diagonally towards the line in the dX = dY direction */
962 
963       if (dy == 0)
964         dist = line->Point1.Y - nearest_grid_y;
965       else
966         dist = ((line->Point1.X - nearest_grid_x) -
967                 (line->Point1.Y - nearest_grid_y) * dx / dy) / (1 - dx / dy);
968 
969       try_x = nearest_grid_x + dist;
970       try_y = nearest_grid_y + dist;
971 
972       check_snap_object (snap_data, try_x, try_y, true);
973     }
974 
975   if (dx != -dy) /* If line not parallel with dX = -dY direction.. */
976     {
977       /* Try snapping diagonally towards the line in the dX = -dY direction */
978 
979       if (dy == 0)
980         dist = nearest_grid_y - line->Point1.Y;
981       else
982         dist = ((line->Point1.X - nearest_grid_x) -
983                 (line->Point1.Y - nearest_grid_y) * dx / dy) / (1 + dx / dy);
984 
985       try_x = nearest_grid_x + dist;
986       try_y = nearest_grid_y - dist;
987 
988       check_snap_object (snap_data, try_x, try_y, true);
989     }
990 }
991 
992 /*!
993  * \brief Recalculates the passed coordinates to fit the current grid
994  * setting.
995  */
996 void
FitCrosshairIntoGrid(Coord X,Coord Y)997 FitCrosshairIntoGrid (Coord X, Coord Y)
998 {
999   Coord nearest_grid_x, nearest_grid_y;
1000   void *ptr1, *ptr2, *ptr3;
1001   struct snap_data snap_data;
1002   int ans;
1003 
1004   /* limit the crosshair location to the board area */
1005   Crosshair.X = CLAMP (X, Crosshair.MinX, Crosshair.MaxX);
1006   Crosshair.Y = CLAMP (Y, Crosshair.MinY, Crosshair.MaxY);
1007 
1008   /*
1009    * GRID SNAPPING
1010    *
1011    * First figure out where the nearest grid point that would snap to is. Then
1012    * if we don't find an object that we would prefer, snap to the grid point.
1013    *
1014    */
1015 
1016   /* if we're drawing rats, don't snap to the grid */
1017   if (PCB->RatDraw)
1018     {
1019     /* set the "nearest grid" somewhere off the board so that everything else
1020      is closer */
1021       nearest_grid_x = -MIL_TO_COORD (6);
1022       nearest_grid_y = -MIL_TO_COORD (6);
1023     }
1024   else
1025     {
1026     /* not drawing rats */
1027     /* find the closest grid point */
1028       nearest_grid_x = GridFit (Crosshair.X, PCB->Grid, PCB->GridOffsetX);
1029       nearest_grid_y = GridFit (Crosshair.Y, PCB->Grid, PCB->GridOffsetY);
1030 
1031     /* if something is marked and we're forcing orthogonal moves... */
1032       if (Marked.status && TEST_FLAG (ORTHOMOVEFLAG, PCB))
1033 	{
1034 	  Coord dx = Crosshair.X - Marked.X;
1035 	  Coord dy = Crosshair.Y - Marked.Y;
1036       /* restrict motion along the x or y axis
1037        this assumes we're always snapped to a grid point... */
1038 	  if (ABS (dx) > ABS (dy))
1039 	    nearest_grid_y = Marked.Y;
1040 	  else
1041 	    nearest_grid_x = Marked.X;
1042 	}
1043 
1044     }
1045 
1046   /* The snap_data structure contains all of the information about where we're
1047    * going to snap to. */
1048   snap_data.crosshair = &Crosshair;
1049   snap_data.nearest_sq_dist =
1050     crosshair_sq_dist (&Crosshair, nearest_grid_x, nearest_grid_y);
1051   snap_data.nearest_is_grid = true;
1052   snap_data.x = nearest_grid_x;
1053   snap_data.y = nearest_grid_y;
1054 
1055   ans = NO_TYPE;
1056   /* if we're not drawing rats, check for elements first */
1057   if (!PCB->RatDraw)
1058     ans = SearchObjectByLocation (ELEMENT_TYPE, &ptr1, &ptr2, &ptr3,
1059                                   Crosshair.X, Crosshair.Y, PCB->Grid / 2);
1060 
1061   if (ans & ELEMENT_TYPE)
1062     {
1063     /* if we found an element, check to see if we should snap to it */
1064       ElementType *el = (ElementType *) ptr1;
1065       check_snap_object (&snap_data, el->MarkX, el->MarkY, false);
1066     }
1067 
1068   /* try snapping to a pad if we're drawing rats, or pad snapping is turned on */
1069   ans = NO_TYPE;
1070   if (PCB->RatDraw || TEST_FLAG (SNAPPINFLAG, PCB))
1071     ans = SearchObjectByLocation (PAD_TYPE, &ptr1, &ptr2, &ptr3,
1072                                   Crosshair.X, Crosshair.Y, PCB->Grid / 2);
1073 
1074   /* Avoid self-snapping when moving */
1075   if (ans != NO_TYPE &&
1076       Settings.Mode == MOVE_MODE &&
1077       Crosshair.AttachedObject.Type == ELEMENT_TYPE &&
1078       ptr1 == Crosshair.AttachedObject.Ptr1)
1079     ans = NO_TYPE;
1080 
1081   if (ans != NO_TYPE &&                  /* we found a pad */
1082       ( Settings.Mode == LINE_MODE ||    /* we're in line drawing mode, or... */
1083         Settings.Mode == ARC_MODE  ||    /* we're in arc drawing mode, or... */
1084        (Settings.Mode == MOVE_MODE &&    /* we're moving something and ... */
1085         ((Crosshair.AttachedObject.Type == LINEPOINT_TYPE) || /* it's a line */
1086          (Crosshair.AttachedObject.Type == ARCPOINT_TYPE))))) /* it's an arc */
1087     {
1088     /* we found a pad and we're drawing or moving a line */
1089       PadType *pad = (PadType *) ptr2;
1090       LayerType *desired_layer;
1091       Cardinal desired_group;
1092       Cardinal bottom_group, top_group;
1093       int found_our_layer = false;
1094 
1095     /* the layer we'd like to snap to, start with the current layer */
1096       desired_layer = CURRENT;
1097       if (Settings.Mode == MOVE_MODE &&
1098           Crosshair.AttachedObject.Type == LINEPOINT_TYPE)
1099         {
1100       /* if we're moving something, the desired layer is the layer it's on */
1101           desired_layer = (LayerType *)Crosshair.AttachedObject.Ptr1;
1102         }
1103 
1104     /* pads must be on the top or bottom sides.
1105      * find layer groups of the top and bottom sides */
1106       top_group = GetLayerGroupNumberBySide (TOP_SIDE);
1107       bottom_group = GetLayerGroupNumberBySide (BOTTOM_SIDE);
1108       desired_group = TEST_FLAG (ONSOLDERFLAG, pad) ? bottom_group : top_group;
1109 
1110       GROUP_LOOP (PCB->Data, desired_group);
1111       {
1112         if (layer == desired_layer)
1113           {
1114         /* the layer the pad is on is the same as the layer of the line
1115          * that we're moving. */
1116             found_our_layer = true;
1117             break;
1118           }
1119       }
1120       END_LOOP;
1121 
1122       if (found_our_layer == false)
1123     /* different layers, so don't snap */
1124         ans = NO_TYPE;
1125     }
1126 
1127   if (ans != NO_TYPE)
1128     {
1129     /* we found a pad we want to snap to, check to see if we should */
1130       PadType *pad = (PadType *)ptr2;
1131       check_snap_object (&snap_data, pad->Point1.X + (pad->Point2.X - pad->Point1.X) / 2,
1132                                      pad->Point1.Y + (pad->Point2.Y - pad->Point1.Y) / 2,
1133                          true);
1134     }
1135 
1136   /* try snapping to a pin
1137    * similar to snapping to a pad, but without the layer restriction */
1138   ans = NO_TYPE;
1139   if (PCB->RatDraw || TEST_FLAG (SNAPPINFLAG, PCB))
1140     ans = SearchObjectByLocation (PIN_TYPE, &ptr1, &ptr2, &ptr3,
1141                                   Crosshair.X, Crosshair.Y, PCB->Grid / 2);
1142 
1143   /* Avoid self-snapping when moving */
1144   if (ans != NO_TYPE &&
1145       Settings.Mode == MOVE_MODE &&
1146       Crosshair.AttachedObject.Type == ELEMENT_TYPE &&
1147       ptr1 == Crosshair.AttachedObject.Ptr1)
1148     ans = NO_TYPE;
1149 
1150   if (ans != NO_TYPE)
1151     {
1152     /* we found a pin, try to snap to it */
1153       PinType *pin = (PinType *)ptr2;
1154       check_snap_object (&snap_data, pin->X, pin->Y, true);
1155     }
1156 
1157   /* if snapping to pins and pads is turned on, try snapping to vias */
1158   ans = NO_TYPE;
1159   if (TEST_FLAG (SNAPPINFLAG, PCB))
1160     ans = SearchObjectByLocation (VIA_TYPE, &ptr1, &ptr2, &ptr3,
1161                                   Crosshair.X, Crosshair.Y, PCB->Grid / 2);
1162 
1163   /* Avoid snapping vias to any other vias */
1164   if (Settings.Mode == MOVE_MODE &&
1165       Crosshair.AttachedObject.Type == VIA_TYPE &&
1166       (ans & PIN_TYPES))
1167     ans = NO_TYPE;
1168 
1169   if (ans != NO_TYPE)
1170     {
1171     /* found a via, try snapping to it */
1172       PinType *pin = (PinType *)ptr2;
1173       check_snap_object (&snap_data, pin->X, pin->Y, true);
1174     }
1175 
1176   /* try snapping to the end points of lines and arcs */
1177   ans = NO_TYPE;
1178   if (TEST_FLAG (SNAPPINFLAG, PCB))
1179     ans = SearchObjectByLocation (LINEPOINT_TYPE | ARCPOINT_TYPE,
1180                                   &ptr1, &ptr2, &ptr3,
1181                                   Crosshair.X, Crosshair.Y, PCB->Grid / 2);
1182 
1183   if (ans != NO_TYPE)
1184     {
1185     /* found an end point, try to snap to it */
1186       PointType *pnt = (PointType *)ptr3;
1187       check_snap_object (&snap_data, pnt->X, pnt->Y, true);
1188     }
1189 
1190   /* try snapping to a point on a line that's not on the grid */
1191   check_snap_offgrid_line (&snap_data, nearest_grid_x, nearest_grid_y);
1192 
1193   /* try snapping to a point defining a polygon */
1194   ans = NO_TYPE;
1195   if (TEST_FLAG (SNAPPINFLAG, PCB))
1196     ans = SearchObjectByLocation (POLYGONPOINT_TYPE, &ptr1, &ptr2, &ptr3,
1197                                   Crosshair.X, Crosshair.Y, PCB->Grid / 2);
1198 
1199   if (ans != NO_TYPE)
1200     {
1201     /* found a polygon point, try snapping to it */
1202       PointType *pnt = (PointType *)ptr3;
1203       check_snap_object (&snap_data, pnt->X, pnt->Y, true);
1204     }
1205 
1206   /* if snap_data.[x,y] are >= 0, then we found something,
1207    * so move the crosshair */
1208   if (snap_data.x >= 0 && snap_data.y >= 0)
1209     {
1210       Crosshair.X = snap_data.x;
1211       Crosshair.Y = snap_data.y;
1212     }
1213 
1214   /* If we're in arrow mode and we're over the end point of a line,
1215    * change the cursor to the "draped box" to indicate that clicking would
1216    * grab the line endpoint */
1217   if (Settings.Mode == ARROW_MODE)
1218     {
1219       ans = SearchObjectByLocation (LINEPOINT_TYPE | ARCPOINT_TYPE,
1220                                     &ptr1, &ptr2, &ptr3,
1221                                     Crosshair.X, Crosshair.Y, PCB->Grid / 2);
1222       if (ans == NO_TYPE)
1223         hid_action("PointCursor");
1224       else if (!TEST_FLAG(SELECTEDFLAG, (LineType *)ptr2))
1225         hid_actionl("PointCursor","True", NULL);
1226     }
1227 
1228   if (Settings.Mode == LINE_MODE
1229       && Crosshair.AttachedLine.State != STATE_FIRST
1230       && TEST_FLAG (AUTODRCFLAG, PCB))
1231     EnforceLineDRC ();
1232 
1233   gui->set_crosshair (Crosshair.X, Crosshair.Y, HID_SC_DO_NOTHING);
1234 }
1235 
1236 /*!
1237  * \brief Move crosshair absolute.
1238  *
1239  * \return true if the crosshair was moved from its existing position.
1240  */
1241 bool
MoveCrosshairAbsolute(Coord X,Coord Y)1242 MoveCrosshairAbsolute (Coord X, Coord Y)
1243 {
1244   Coord old_x = Crosshair.X;
1245   Coord old_y = Crosshair.Y;
1246 
1247   FitCrosshairIntoGrid (X, Y);
1248 
1249   if (Crosshair.X != old_x || Crosshair.Y != old_y)
1250     {
1251       Coord new_x = Crosshair.X;
1252       Coord new_y = Crosshair.Y;
1253 
1254       /* back up to old position to notify the GUI
1255        * (which might want to erase the old crosshair) */
1256       Crosshair.X = old_x;
1257       Crosshair.Y = old_y;
1258       notify_crosshair_change (false); /* Our caller notifies when it has done */
1259 
1260       /* now move forward again */
1261       Crosshair.X = new_x;
1262       Crosshair.Y = new_y;
1263       return true;
1264     }
1265   return false;
1266 }
1267 
1268 /*!
1269  * \brief Update the valid range for the crosshair cursor
1270  */
1271 
1272 void
crosshair_update_range(void)1273 crosshair_update_range(void)
1274 {
1275   Coord xmin, xmax, ymin, ymax;
1276   BoxType *box;
1277 
1278   /* limit the crosshair to being on the working area */
1279   xmin = 0;  xmax = PCB->MaxWidth;
1280   ymin = 0;  ymax = PCB->MaxHeight;
1281 
1282   /* limit the crosshair so attached objects stay on the working area */
1283   /* note: There are presently two ways this is done in the code, the first uses
1284    *       the pastebuffer bounding box, the second uses the attached object
1285    *       bounding box.
1286    */
1287   if (Settings.Mode == PASTEBUFFER_MODE) {
1288 	SetBufferBoundingBox(PASTEBUFFER);
1289     xmin = MAX(xmin, PASTEBUFFER->X - PASTEBUFFER->BoundingBox.X1);
1290     ymin = MAX(ymin, PASTEBUFFER->Y - PASTEBUFFER->BoundingBox.Y1);
1291     xmax = MIN(xmax, PCB->MaxWidth - (PASTEBUFFER->BoundingBox.X2 - PASTEBUFFER->X));
1292     ymax = MIN(ymax, PCB->MaxHeight - (PASTEBUFFER->BoundingBox.Y2 - PASTEBUFFER->Y));
1293   }
1294 
1295   /* if there's an attached object, */
1296   if (Crosshair.AttachedObject.Type != NO_TYPE) {
1297     box = GetObjectBoundingBox (Crosshair.AttachedObject.Type,
1298                                 Crosshair.AttachedObject.Ptr1,
1299                                 Crosshair.AttachedObject.Ptr2,
1300                                 Crosshair.AttachedObject.Ptr3);
1301     xmin = MAX(xmin, Crosshair.AttachedObject.X - box->X1);
1302     ymin = MAX(ymin, Crosshair.AttachedObject.Y - box->Y1);
1303     xmax = MIN(xmax, PCB->MaxWidth - (box->X2 - Crosshair.AttachedObject.X));
1304     ymax = MIN(ymax, PCB->MaxHeight - (box->Y2 - Crosshair.AttachedObject.Y));
1305   }
1306 
1307   SetCrosshairRange(xmin, ymin, xmax, ymax);
1308 
1309 }
1310 
1311 /*!
1312  * \brief Sets the valid range for the crosshair cursor.
1313  */
1314 void
SetCrosshairRange(Coord MinX,Coord MinY,Coord MaxX,Coord MaxY)1315 SetCrosshairRange (Coord MinX, Coord MinY, Coord MaxX, Coord MaxY)
1316 {
1317   Crosshair.MinX = MAX (0, MinX);
1318   Crosshair.MinY = MAX (0, MinY);
1319   Crosshair.MaxX = MIN (PCB->MaxWidth, MaxX);
1320   Crosshair.MaxY = MIN (PCB->MaxHeight, MaxY);
1321 
1322   /* force update of position */
1323   FitCrosshairIntoGrid (Crosshair.X, Crosshair.Y);
1324 }
1325 
1326 /*!
1327  * \brief Initializes crosshair stuff.
1328  *
1329  * Clears the struct, allocates to graphical contexts.
1330  */
1331 void
InitCrosshair(void)1332 InitCrosshair (void)
1333 {
1334   /* set initial shape */
1335   Crosshair.shape = Basic_Crosshair_Shape;
1336 
1337   /* set default limits */
1338   Crosshair.MinX = Crosshair.MinY = 0;
1339   Crosshair.MaxX = PCB->MaxWidth;
1340   Crosshair.MaxY = PCB->MaxHeight;
1341 
1342   /* clear the mark */
1343   Marked.status = false;
1344 }
1345 
1346 /*!
1347  * \brief Exits crosshair routines, release GCs.
1348  */
1349 void
DestroyCrosshair(void)1350 DestroyCrosshair (void)
1351 {
1352   FreePolygonMemory (&Crosshair.AttachedPolygon);
1353 }
1354