action.c: UpdatePackage() reloads from disk
[geda-pcb/whiteaudio.git] / src / crosshair.c
blobf0526b85c542adf8e070f5227743682759b5371f
1 /*
2 * COPYRIGHT
4 * PCB, interactive printed circuit board design
5 * Copyright (C) 1994,1995,1996 Thomas Nau
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 * Contact addresses for paper mail and Email:
22 * Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
23 * Thomas.Nau@rz.uni-ulm.de
28 /* crosshair stuff
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
35 #include <memory.h>
36 #include <math.h>
38 #include "global.h"
40 #include "crosshair.h"
41 #include "data.h"
42 #include "draw.h"
43 #include "error.h"
44 #include "line.h"
45 #include "misc.h"
46 #include "mymem.h"
47 #include "search.h"
48 #include "polygon.h"
50 #ifdef HAVE_LIBDMALLOC
51 #include <dmalloc.h>
52 #endif
54 typedef struct
56 int x, y;
57 } point;
59 /* ---------------------------------------------------------------------------
60 * some local prototypes
62 static void XORPolygon (PolygonType *, Coord, Coord);
63 static void XORDrawElement (ElementType *, Coord, Coord);
64 static void XORDrawBuffer (BufferType *);
65 static void XORDrawInsertPointObject (void);
66 static void XORDrawMoveOrCopyObject (void);
67 static void XORDrawAttachedLine (Coord, Coord, Coord, Coord, Coord);
68 static void XORDrawAttachedArc (Coord);
70 static void
71 thindraw_moved_pv (PinType *pv, Coord x, Coord y)
73 /* Make a copy of the pin structure, moved to the correct position */
74 PinType moved_pv = *pv;
75 moved_pv.X += x;
76 moved_pv.Y += y;
78 gui->thindraw_pcb_pv (Crosshair.GC, Crosshair.GC, &moved_pv, true, false);
81 /* ---------------------------------------------------------------------------
82 * creates a tmp polygon with coordinates converted to screen system
84 static void
85 XORPolygon (PolygonType *polygon, Coord dx, Coord dy)
87 Cardinal i;
88 for (i = 0; i < polygon->PointN; i++)
90 Cardinal next = next_contour_point (polygon, i);
91 gui->draw_line (Crosshair.GC,
92 polygon->Points[i].X + dx,
93 polygon->Points[i].Y + dy,
94 polygon->Points[next].X + dx,
95 polygon->Points[next].Y + dy);
99 /*-----------------------------------------------------------
100 * Draws the outline of an arc
102 static void
103 XORDrawAttachedArc (Coord thick)
105 ArcType arc;
106 BoxType *bx;
107 Coord wx, wy;
108 Angle sa, dir;
109 Coord wid = thick / 2;
111 wx = Crosshair.X - Crosshair.AttachedBox.Point1.X;
112 wy = Crosshair.Y - Crosshair.AttachedBox.Point1.Y;
113 if (wx == 0 && wy == 0)
114 return;
115 arc.X = Crosshair.AttachedBox.Point1.X;
116 arc.Y = Crosshair.AttachedBox.Point1.Y;
117 if (XOR (Crosshair.AttachedBox.otherway, abs (wy) > abs (wx)))
119 arc.X = Crosshair.AttachedBox.Point1.X + abs (wy) * SGNZ (wx);
120 sa = (wx >= 0) ? 0 : 180;
121 #ifdef ARC45
122 if (abs (wy) >= 2 * abs (wx))
123 dir = (SGNZ (wx) == SGNZ (wy)) ? 45 : -45;
124 else
125 #endif
126 dir = (SGNZ (wx) == SGNZ (wy)) ? 90 : -90;
128 else
130 arc.Y = Crosshair.AttachedBox.Point1.Y + abs (wx) * SGNZ (wy);
131 sa = (wy >= 0) ? -90 : 90;
132 #ifdef ARC45
133 if (abs (wx) >= 2 * abs (wy))
134 dir = (SGNZ (wx) == SGNZ (wy)) ? -45 : 45;
135 else
136 #endif
137 dir = (SGNZ (wx) == SGNZ (wy)) ? -90 : 90;
138 wy = wx;
140 wy = abs (wy);
141 arc.StartAngle = sa;
142 arc.Delta = dir;
143 arc.Width = arc.Height = wy;
144 bx = GetArcEnds (&arc);
145 /* sa = sa - 180; */
146 gui->draw_arc (Crosshair.GC, arc.X, arc.Y, wy + wid, wy + wid, sa, dir);
147 if (wid > pixel_slop)
149 gui->draw_arc (Crosshair.GC, arc.X, arc.Y, wy - wid, wy - wid, sa, dir);
150 gui->draw_arc (Crosshair.GC, bx->X1, bx->Y1,
151 wid, wid, sa, -180 * SGN (dir));
152 gui->draw_arc (Crosshair.GC, bx->X2, bx->Y2,
153 wid, wid, sa + dir, 180 * SGN (dir));
157 /*-----------------------------------------------------------
158 * Draws the outline of a line
160 static void
161 XORDrawAttachedLine (Coord x1, Coord y1, Coord x2, Coord y2, Coord thick)
163 Coord dx, dy, ox, oy;
164 double h;
166 dx = x2 - x1;
167 dy = y2 - y1;
168 if (dx != 0 || dy != 0)
169 h = 0.5 * thick / sqrt (SQUARE (dx) + SQUARE (dy));
170 else
171 h = 0.0;
172 ox = dy * h + 0.5 * SGN (dy);
173 oy = -(dx * h + 0.5 * SGN (dx));
174 gui->draw_line (Crosshair.GC, x1 + ox, y1 + oy, x2 + ox, y2 + oy);
175 if (abs (ox) >= pixel_slop || abs (oy) >= pixel_slop)
177 Angle angle = atan2 (dx, dy) * 57.295779;
178 gui->draw_line (Crosshair.GC, x1 - ox, y1 - oy, x2 - ox, y2 - oy);
179 gui->draw_arc (Crosshair.GC,
180 x1, y1, thick / 2, thick / 2, angle - 180, 180);
181 gui->draw_arc (Crosshair.GC, x2, y2, thick / 2, thick / 2, angle, 180);
185 /* ---------------------------------------------------------------------------
186 * draws the elements of a loaded circuit which is to be merged in
188 static void
189 XORDrawElement (ElementType *Element, Coord DX, Coord DY)
191 /* if no silkscreen, draw the bounding box */
192 if (Element->ArcN == 0 && Element->LineN == 0)
194 gui->draw_line (Crosshair.GC,
195 DX + Element->BoundingBox.X1,
196 DY + Element->BoundingBox.Y1,
197 DX + Element->BoundingBox.X1,
198 DY + Element->BoundingBox.Y2);
199 gui->draw_line (Crosshair.GC,
200 DX + Element->BoundingBox.X1,
201 DY + Element->BoundingBox.Y2,
202 DX + Element->BoundingBox.X2,
203 DY + Element->BoundingBox.Y2);
204 gui->draw_line (Crosshair.GC,
205 DX + Element->BoundingBox.X2,
206 DY + Element->BoundingBox.Y2,
207 DX + Element->BoundingBox.X2,
208 DY + Element->BoundingBox.Y1);
209 gui->draw_line (Crosshair.GC,
210 DX + Element->BoundingBox.X2,
211 DY + Element->BoundingBox.Y1,
212 DX + Element->BoundingBox.X1,
213 DY + Element->BoundingBox.Y1);
215 else
217 ELEMENTLINE_LOOP (Element);
219 gui->draw_line (Crosshair.GC,
220 DX + line->Point1.X,
221 DY + line->Point1.Y,
222 DX + line->Point2.X, DY + line->Point2.Y);
224 END_LOOP;
226 /* arc coordinates and angles have to be converted to X11 notation */
227 ARC_LOOP (Element);
229 gui->draw_arc (Crosshair.GC,
230 DX + arc->X,
231 DY + arc->Y,
232 arc->Width, arc->Height, arc->StartAngle, arc->Delta);
234 END_LOOP;
236 /* pin coordinates and angles have to be converted to X11 notation */
237 PIN_LOOP (Element);
239 thindraw_moved_pv (pin, DX, DY);
241 END_LOOP;
243 /* pads */
244 PAD_LOOP (Element);
246 if (PCB->InvisibleObjectsOn ||
247 (TEST_FLAG (ONSOLDERFLAG, pad) != 0) == Settings.ShowSolderSide)
249 /* Make a copy of the pad structure, moved to the correct position */
250 PadType moved_pad = *pad;
251 moved_pad.Point1.X += DX; moved_pad.Point1.Y += DY;
252 moved_pad.Point2.X += DX; moved_pad.Point2.Y += DY;
254 gui->thindraw_pcb_pad (Crosshair.GC, &moved_pad, false, false);
257 END_LOOP;
258 /* mark */
259 gui->draw_line (Crosshair.GC,
260 Element->MarkX + DX - EMARK_SIZE,
261 Element->MarkY + DY,
262 Element->MarkX + DX, Element->MarkY + DY - EMARK_SIZE);
263 gui->draw_line (Crosshair.GC,
264 Element->MarkX + DX + EMARK_SIZE,
265 Element->MarkY + DY,
266 Element->MarkX + DX, Element->MarkY + DY - EMARK_SIZE);
267 gui->draw_line (Crosshair.GC,
268 Element->MarkX + DX - EMARK_SIZE,
269 Element->MarkY + DY,
270 Element->MarkX + DX, Element->MarkY + DY + EMARK_SIZE);
271 gui->draw_line (Crosshair.GC,
272 Element->MarkX + DX + EMARK_SIZE,
273 Element->MarkY + DY,
274 Element->MarkX + DX, Element->MarkY + DY + EMARK_SIZE);
277 /* ---------------------------------------------------------------------------
278 * draws all visible and attached objects of the pastebuffer
280 static void
281 XORDrawBuffer (BufferType *Buffer)
283 Cardinal i;
284 Coord x, y;
286 /* set offset */
287 x = Crosshair.X - Buffer->X;
288 y = Crosshair.Y - Buffer->Y;
290 /* draw all visible layers */
291 for (i = 0; i < max_copper_layer + 2; i++)
292 if (PCB->Data->Layer[i].On)
294 LayerType *layer = &Buffer->Data->Layer[i];
296 LINE_LOOP (layer);
299 XORDrawAttachedLine(x +line->Point1.X,
300 y +line->Point1.Y, x +line->Point2.X,
301 y +line->Point2.Y, line->Thickness);
303 gui->draw_line (Crosshair.GC,
304 x + line->Point1.X, y + line->Point1.Y,
305 x + line->Point2.X, y + line->Point2.Y);
307 END_LOOP;
308 ARC_LOOP (layer);
310 gui->draw_arc (Crosshair.GC,
311 x + arc->X,
312 y + arc->Y,
313 arc->Width,
314 arc->Height, arc->StartAngle, arc->Delta);
316 END_LOOP;
317 TEXT_LOOP (layer);
319 BoxType *box = &text->BoundingBox;
320 gui->draw_rect (Crosshair.GC,
321 x + box->X1, y + box->Y1, x + box->X2, y + box->Y2);
323 END_LOOP;
324 /* the tmp polygon has n+1 points because the first
325 * and the last one are set to the same coordinates
327 POLYGON_LOOP (layer);
329 XORPolygon (polygon, x, y);
331 END_LOOP;
334 /* draw elements if visible */
335 if (PCB->PinOn && PCB->ElementOn)
336 ELEMENT_LOOP (Buffer->Data);
338 if (FRONT (element) || PCB->InvisibleObjectsOn)
339 XORDrawElement (element, x, y);
341 END_LOOP;
343 /* and the vias */
344 if (PCB->ViaOn)
345 VIA_LOOP (Buffer->Data);
347 thindraw_moved_pv (via, x, y);
349 END_LOOP;
352 /* ---------------------------------------------------------------------------
353 * draws the rubberband to insert points into polygons/lines/...
355 static void
356 XORDrawInsertPointObject (void)
358 LineType *line = (LineType *) Crosshair.AttachedObject.Ptr2;
359 PointType *point = (PointType *) Crosshair.AttachedObject.Ptr3;
361 if (Crosshair.AttachedObject.Type != NO_TYPE)
363 gui->draw_line (Crosshair.GC,
364 point->X, point->Y, line->Point1.X, line->Point1.Y);
365 gui->draw_line (Crosshair.GC,
366 point->X, point->Y, line->Point2.X, line->Point2.Y);
370 /* ---------------------------------------------------------------------------
371 * draws the attached object while in MOVE_MODE or COPY_MODE
373 static void
374 XORDrawMoveOrCopyObject (void)
376 RubberbandType *ptr;
377 Cardinal i;
378 Coord dx = Crosshair.X - Crosshair.AttachedObject.X,
379 dy = Crosshair.Y - Crosshair.AttachedObject.Y;
381 switch (Crosshair.AttachedObject.Type)
383 case VIA_TYPE:
385 PinType *via = (PinType *) Crosshair.AttachedObject.Ptr1;
386 thindraw_moved_pv (via, dx, dy);
387 break;
390 case LINE_TYPE:
392 LineType *line = (LineType *) Crosshair.AttachedObject.Ptr2;
394 XORDrawAttachedLine (line->Point1.X + dx, line->Point1.Y + dy,
395 line->Point2.X + dx, line->Point2.Y + dy,
396 line->Thickness);
397 break;
400 case ARC_TYPE:
402 ArcType *Arc = (ArcType *) Crosshair.AttachedObject.Ptr2;
404 gui->draw_arc (Crosshair.GC,
405 Arc->X + dx,
406 Arc->Y + dy,
407 Arc->Width, Arc->Height, Arc->StartAngle, Arc->Delta);
408 break;
411 case POLYGON_TYPE:
413 PolygonType *polygon =
414 (PolygonType *) Crosshair.AttachedObject.Ptr2;
416 /* the tmp polygon has n+1 points because the first
417 * and the last one are set to the same coordinates
419 XORPolygon (polygon, dx, dy);
420 break;
423 case LINEPOINT_TYPE:
425 LineType *line;
426 PointType *point;
428 line = (LineType *) Crosshair.AttachedObject.Ptr2;
429 point = (PointType *) Crosshair.AttachedObject.Ptr3;
430 if (point == &line->Point1)
431 XORDrawAttachedLine (point->X + dx,
432 point->Y + dy, line->Point2.X,
433 line->Point2.Y, line->Thickness);
434 else
435 XORDrawAttachedLine (point->X + dx,
436 point->Y + dy, line->Point1.X,
437 line->Point1.Y, line->Thickness);
438 break;
441 case POLYGONPOINT_TYPE:
443 PolygonType *polygon;
444 PointType *point;
445 Cardinal point_idx, prev, next;
447 polygon = (PolygonType *) Crosshair.AttachedObject.Ptr2;
448 point = (PointType *) Crosshair.AttachedObject.Ptr3;
449 point_idx = polygon_point_idx (polygon, point);
451 /* get previous and following point */
452 prev = prev_contour_point (polygon, point_idx);
453 next = next_contour_point (polygon, point_idx);
455 /* draw the two segments */
456 gui->draw_line (Crosshair.GC,
457 polygon->Points[prev].X, polygon->Points[prev].Y,
458 point->X + dx, point->Y + dy);
459 gui->draw_line (Crosshair.GC,
460 point->X + dx, point->Y + dy,
461 polygon->Points[next].X, polygon->Points[next].Y);
462 break;
465 case ELEMENTNAME_TYPE:
467 /* locate the element "mark" and draw an association line from crosshair to it */
468 ElementType *element =
469 (ElementType *) Crosshair.AttachedObject.Ptr1;
471 gui->draw_line (Crosshair.GC,
472 element->MarkX,
473 element->MarkY, Crosshair.X, Crosshair.Y);
474 /* fall through to move the text as a box outline */
476 case TEXT_TYPE:
478 TextType *text = (TextType *) Crosshair.AttachedObject.Ptr2;
479 BoxType *box = &text->BoundingBox;
480 gui->draw_rect (Crosshair.GC,
481 box->X1 + dx,
482 box->Y1 + dy, box->X2 + dx, box->Y2 + dy);
483 break;
486 /* pin/pad movements result in moving an element */
487 case PAD_TYPE:
488 case PIN_TYPE:
489 case ELEMENT_TYPE:
490 XORDrawElement ((ElementType *) Crosshair.AttachedObject.Ptr2, dx, dy);
491 break;
494 /* draw the attached rubberband lines too */
495 i = Crosshair.AttachedObject.RubberbandN;
496 ptr = Crosshair.AttachedObject.Rubberband;
497 while (i)
499 PointType *point1, *point2;
501 if (TEST_FLAG (VIAFLAG, ptr->Line))
503 /* this is a rat going to a polygon. do not draw for rubberband */;
505 else if (TEST_FLAG (RUBBERENDFLAG, ptr->Line))
507 /* 'point1' is always the fix-point */
508 if (ptr->MovedPoint == &ptr->Line->Point1)
510 point1 = &ptr->Line->Point2;
511 point2 = &ptr->Line->Point1;
513 else
515 point1 = &ptr->Line->Point1;
516 point2 = &ptr->Line->Point2;
518 XORDrawAttachedLine (point1->X,
519 point1->Y, point2->X + dx,
520 point2->Y + dy, ptr->Line->Thickness);
522 else if (ptr->MovedPoint == &ptr->Line->Point1)
523 XORDrawAttachedLine (ptr->Line->Point1.X + dx,
524 ptr->Line->Point1.Y + dy,
525 ptr->Line->Point2.X + dx,
526 ptr->Line->Point2.Y + dy, ptr->Line->Thickness);
528 ptr++;
529 i--;
533 /* ---------------------------------------------------------------------------
534 * draws additional stuff that follows the crosshair
536 void
537 DrawAttached (void)
539 switch (Settings.Mode)
541 case VIA_MODE:
543 /* Make a dummy via structure to draw from */
544 PinType via;
545 via.X = Crosshair.X;
546 via.Y = Crosshair.Y;
547 via.Thickness = Settings.ViaThickness;
548 via.Clearance = 2 * Settings.Keepaway;
549 via.DrillingHole = Settings.ViaDrillingHole;
550 via.Mask = 0;
551 via.Flags = NoFlags ();
553 gui->thindraw_pcb_pv (Crosshair.GC, Crosshair.GC, &via, true, false);
555 if (TEST_FLAG (SHOWDRCFLAG, PCB))
557 /* XXX: Naughty cheat - use the mask to draw DRC clearance! */
558 via.Mask = Settings.ViaThickness + PCB->Bloat * 2;
559 gui->set_color (Crosshair.GC, Settings.CrossColor);
560 gui->thindraw_pcb_pv (Crosshair.GC, Crosshair.GC, &via, false, true);
561 gui->set_color (Crosshair.GC, Settings.CrosshairColor);
563 break;
566 /* the attached line is used by both LINEMODE, POLYGON_MODE and POLYGONHOLE_MODE*/
567 case POLYGON_MODE:
568 case POLYGONHOLE_MODE:
569 /* draw only if starting point is set */
570 if (Crosshair.AttachedLine.State != STATE_FIRST)
571 gui->draw_line (Crosshair.GC,
572 Crosshair.AttachedLine.Point1.X,
573 Crosshair.AttachedLine.Point1.Y,
574 Crosshair.AttachedLine.Point2.X,
575 Crosshair.AttachedLine.Point2.Y);
577 /* draw attached polygon only if in POLYGON_MODE or POLYGONHOLE_MODE */
578 if (Crosshair.AttachedPolygon.PointN > 1)
580 XORPolygon (&Crosshair.AttachedPolygon, 0, 0);
582 break;
584 case ARC_MODE:
585 if (Crosshair.AttachedBox.State != STATE_FIRST)
587 XORDrawAttachedArc (Settings.LineThickness);
588 if (TEST_FLAG (SHOWDRCFLAG, PCB))
590 gui->set_color (Crosshair.GC, Settings.CrossColor);
591 XORDrawAttachedArc (Settings.LineThickness +
592 2 * (PCB->Bloat + 1));
593 gui->set_color (Crosshair.GC, Settings.CrosshairColor);
597 break;
599 case LINE_MODE:
600 /* draw only if starting point exists and the line has length */
601 if (Crosshair.AttachedLine.State != STATE_FIRST &&
602 Crosshair.AttachedLine.draw)
604 XORDrawAttachedLine (Crosshair.AttachedLine.Point1.X,
605 Crosshair.AttachedLine.Point1.Y,
606 Crosshair.AttachedLine.Point2.X,
607 Crosshair.AttachedLine.Point2.Y,
608 PCB->RatDraw ? 10 : Settings.LineThickness);
609 /* draw two lines ? */
610 if (PCB->Clipping)
611 XORDrawAttachedLine (Crosshair.AttachedLine.Point2.X,
612 Crosshair.AttachedLine.Point2.Y,
613 Crosshair.X, Crosshair.Y,
614 PCB->RatDraw ? 10 : Settings.LineThickness);
615 if (TEST_FLAG (SHOWDRCFLAG, PCB))
617 gui->set_color (Crosshair.GC, Settings.CrossColor);
618 XORDrawAttachedLine (Crosshair.AttachedLine.Point1.X,
619 Crosshair.AttachedLine.Point1.Y,
620 Crosshair.AttachedLine.Point2.X,
621 Crosshair.AttachedLine.Point2.Y,
622 PCB->RatDraw ? 10 : Settings.LineThickness
623 + 2 * (PCB->Bloat + 1));
624 if (PCB->Clipping)
625 XORDrawAttachedLine (Crosshair.AttachedLine.Point2.X,
626 Crosshair.AttachedLine.Point2.Y,
627 Crosshair.X, Crosshair.Y,
628 PCB->RatDraw ? 10 : Settings.
629 LineThickness + 2 * (PCB->Bloat + 1));
630 gui->set_color (Crosshair.GC, Settings.CrosshairColor);
633 break;
635 case PASTEBUFFER_MODE:
636 XORDrawBuffer (PASTEBUFFER);
637 break;
639 case COPY_MODE:
640 case MOVE_MODE:
641 XORDrawMoveOrCopyObject ();
642 break;
644 case INSERTPOINT_MODE:
645 XORDrawInsertPointObject ();
646 break;
649 /* an attached box does not depend on a special mode */
650 if (Crosshair.AttachedBox.State == STATE_SECOND ||
651 Crosshair.AttachedBox.State == STATE_THIRD)
653 Coord x1, y1, x2, y2;
655 x1 = Crosshair.AttachedBox.Point1.X;
656 y1 = Crosshair.AttachedBox.Point1.Y;
657 x2 = Crosshair.AttachedBox.Point2.X;
658 y2 = Crosshair.AttachedBox.Point2.Y;
659 gui->draw_rect (Crosshair.GC, x1, y1, x2, y2);
664 /* --------------------------------------------------------------------------
665 * draw the marker position
667 void
668 DrawMark (void)
670 /* Mark is not drawn when it is not set */
671 if (!Marked.status)
672 return;
674 gui->draw_line (Crosshair.GC,
675 Marked.X - MARK_SIZE,
676 Marked.Y - MARK_SIZE,
677 Marked.X + MARK_SIZE, Marked.Y + MARK_SIZE);
678 gui->draw_line (Crosshair.GC,
679 Marked.X + MARK_SIZE,
680 Marked.Y - MARK_SIZE,
681 Marked.X - MARK_SIZE, Marked.Y + MARK_SIZE);
684 /* ---------------------------------------------------------------------------
685 * Returns the nearest grid-point to the given Coord
687 Coord
688 GridFit (Coord x, Coord grid_spacing, Coord grid_offset)
690 x -= grid_offset;
691 x = grid_spacing * round ((double) x / grid_spacing);
692 x += grid_offset;
693 return x;
697 /* ---------------------------------------------------------------------------
698 * notify the GUI that data relating to the crosshair is being changed.
700 * The argument passed is false to notify "changes are about to happen",
701 * and true to notify "changes have finished".
703 * Each call with a 'false' parameter must be matched with a following one
704 * with a 'true' parameter. Unmatched 'true' calls are currently not permitted,
705 * but might be allowed in the future.
707 * GUIs should not complain if they receive extra calls with 'true' as parameter.
708 * They should initiate a redraw of the crosshair attached objects - which may
709 * (if necessary) mean repainting the whole screen if the GUI hasn't tracked the
710 * location of existing attached drawing.
712 void
713 notify_crosshair_change (bool changes_complete)
715 if (gui->notify_crosshair_change)
716 gui->notify_crosshair_change (changes_complete);
720 /* ---------------------------------------------------------------------------
721 * notify the GUI that data relating to the mark is being changed.
723 * The argument passed is false to notify "changes are about to happen",
724 * and true to notify "changes have finished".
726 * Each call with a 'false' parameter must be matched with a following one
727 * with a 'true' parameter. Unmatched 'true' calls are currently not permitted,
728 * but might be allowed in the future.
730 * GUIs should not complain if they receive extra calls with 'true' as parameter.
731 * They should initiate a redraw of the mark - which may (if necessary) mean
732 * repainting the whole screen if the GUI hasn't tracked the mark's location.
734 void
735 notify_mark_change (bool changes_complete)
737 if (gui->notify_mark_change)
738 gui->notify_mark_change (changes_complete);
742 /* ---------------------------------------------------------------------------
743 * Convenience for plugins using the old {Hide,Restore}Crosshair API.
744 * This links up to notify the GUI of the expected changes using the new APIs.
746 * Use of this old API is deprecated, as the names don't necessarily reflect
747 * what all GUIs may do in response to the notifications. Keeping these APIs
748 * is aimed at easing transition to the newer API, they will emit a harmless
749 * warning at the time of their first use.
752 void
753 HideCrosshair (void)
755 static bool warned_old_api = false;
756 if (!warned_old_api)
758 Message (_("WARNING: A plugin is using the deprecated API HideCrosshair().\n"
759 " This API may be removed in a future release of PCB.\n"));
760 warned_old_api = true;
763 notify_crosshair_change (false);
764 notify_mark_change (false);
767 void
768 RestoreCrosshair (void)
770 static bool warned_old_api = false;
771 if (!warned_old_api)
773 Message (_("WARNING: A plugin is using the deprecated API RestoreCrosshair().\n"
774 " This API may be removed in a future release of PCB.\n"));
775 warned_old_api = true;
778 notify_crosshair_change (true);
779 notify_mark_change (true);
782 /* ---------------------------------------------------------------------------
783 * Returns the square of the given number
785 static double
786 square (double x)
788 return x * x;
791 static double
792 crosshair_sq_dist (CrosshairType *crosshair, Coord x, Coord y)
794 return square (x - crosshair->X) + square (y - crosshair->Y);
797 struct snap_data {
798 CrosshairType *crosshair;
799 double nearest_sq_dist;
800 bool nearest_is_grid;
801 Coord x, y;
804 /* Snap to a given location if it is the closest thing we found so far.
805 * If "prefer_to_grid" is set, the passed location will take preference
806 * over a closer grid points we already snapped to UNLESS the user is
807 * pressing the SHIFT key. If the SHIFT key is pressed, the closest object
808 * (including grid points), is always preferred.
810 static void
811 check_snap_object (struct snap_data *snap_data, Coord x, Coord y,
812 bool prefer_to_grid)
814 double sq_dist;
816 sq_dist = crosshair_sq_dist (snap_data->crosshair, x, y);
817 if (sq_dist <= snap_data->nearest_sq_dist ||
818 (prefer_to_grid && snap_data->nearest_is_grid && !gui->shift_is_pressed()))
820 snap_data->x = x;
821 snap_data->y = y;
822 snap_data->nearest_sq_dist = sq_dist;
823 snap_data->nearest_is_grid = false;
827 static void
828 check_snap_offgrid_line (struct snap_data *snap_data,
829 Coord nearest_grid_x,
830 Coord nearest_grid_y)
832 void *ptr1, *ptr2, *ptr3;
833 int ans;
834 LineType *line;
835 Coord try_x, try_y;
836 double dx, dy;
837 double dist;
839 if (!TEST_FLAG (SNAPPINFLAG, PCB))
840 return;
842 /* Code to snap at some sensible point along a line */
843 /* Pick the nearest grid-point in the x or y direction
844 * to align with, then adjust until we hit the line
846 ans = SearchScreenGridSlop (Crosshair.X, Crosshair.Y,
847 LINE_TYPE, &ptr1, &ptr2, &ptr3);
849 if (ans == NO_TYPE)
850 return;
852 line = (LineType *)ptr2;
854 /* Allow snapping to off-grid lines when drawing new lines (on
855 * the same layer), and when moving a line end-point
856 * (but don't snap to the same line)
858 if ((Settings.Mode != LINE_MODE || CURRENT != ptr1) &&
859 (Settings.Mode != MOVE_MODE ||
860 Crosshair.AttachedObject.Ptr1 != ptr1 ||
861 Crosshair.AttachedObject.Type != LINEPOINT_TYPE ||
862 Crosshair.AttachedObject.Ptr2 == line))
863 return;
865 dx = line->Point2.X - line->Point1.X;
866 dy = line->Point2.Y - line->Point1.Y;
868 /* Try snapping along the X axis */
869 if (dy != 0.)
871 /* Move in the X direction until we hit the line */
872 try_x = (nearest_grid_y - line->Point1.Y) / dy * dx + line->Point1.X;
873 try_y = nearest_grid_y;
874 check_snap_object (snap_data, try_x, try_y, true);
877 /* Try snapping along the Y axis */
878 if (dx != 0.)
880 try_x = nearest_grid_x;
881 try_y = (nearest_grid_x - line->Point1.X) / dx * dy + line->Point1.Y;
882 check_snap_object (snap_data, try_x, try_y, true);
885 if (dx != dy) /* If line not parallel with dX = dY direction.. */
887 /* Try snapping diagonally towards the line in the dX = dY direction */
889 if (dy == 0)
890 dist = line->Point1.Y - nearest_grid_y;
891 else
892 dist = ((line->Point1.X - nearest_grid_x) -
893 (line->Point1.Y - nearest_grid_y) * dx / dy) / (1 - dx / dy);
895 try_x = nearest_grid_x + dist;
896 try_y = nearest_grid_y + dist;
898 check_snap_object (snap_data, try_x, try_y, true);
901 if (dx != -dy) /* If line not parallel with dX = -dY direction.. */
903 /* Try snapping diagonally towards the line in the dX = -dY direction */
905 if (dy == 0)
906 dist = nearest_grid_y - line->Point1.Y;
907 else
908 dist = ((line->Point1.X - nearest_grid_x) -
909 (line->Point1.Y - nearest_grid_y) * dx / dy) / (1 + dx / dy);
911 try_x = nearest_grid_x + dist;
912 try_y = nearest_grid_y - dist;
914 check_snap_object (snap_data, try_x, try_y, true);
918 /* ---------------------------------------------------------------------------
919 * recalculates the passed coordinates to fit the current grid setting
921 void
922 FitCrosshairIntoGrid (Coord X, Coord Y)
924 Coord nearest_grid_x, nearest_grid_y;
925 void *ptr1, *ptr2, *ptr3;
926 struct snap_data snap_data;
927 int ans;
929 Crosshair.X = CLAMP (X, Crosshair.MinX, Crosshair.MaxX);
930 Crosshair.Y = CLAMP (Y, Crosshair.MinY, Crosshair.MaxY);
932 if (PCB->RatDraw)
934 nearest_grid_x = -MIL_TO_COORD (6);
935 nearest_grid_y = -MIL_TO_COORD (6);
937 else
939 nearest_grid_x = GridFit (Crosshair.X, PCB->Grid, PCB->GridOffsetX);
940 nearest_grid_y = GridFit (Crosshair.Y, PCB->Grid, PCB->GridOffsetY);
942 if (Marked.status && TEST_FLAG (ORTHOMOVEFLAG, PCB))
944 Coord dx = Crosshair.X - Marked.X;
945 Coord dy = Crosshair.Y - Marked.Y;
946 if (ABS (dx) > ABS (dy))
947 nearest_grid_y = Marked.Y;
948 else
949 nearest_grid_x = Marked.X;
954 snap_data.crosshair = &Crosshair;
955 snap_data.nearest_sq_dist =
956 crosshair_sq_dist (&Crosshair, nearest_grid_x, nearest_grid_y);
957 snap_data.nearest_is_grid = true;
958 snap_data.x = nearest_grid_x;
959 snap_data.y = nearest_grid_y;
961 ans = NO_TYPE;
962 if (!PCB->RatDraw)
963 ans = SearchScreenGridSlop (Crosshair.X, Crosshair.Y,
964 ELEMENT_TYPE, &ptr1, &ptr2, &ptr3);
966 if (ans & ELEMENT_TYPE)
968 ElementType *el = (ElementType *) ptr1;
969 check_snap_object (&snap_data, el->MarkX, el->MarkY, false);
972 ans = NO_TYPE;
973 if (PCB->RatDraw || TEST_FLAG (SNAPPINFLAG, PCB))
974 ans = SearchScreenGridSlop (Crosshair.X, Crosshair.Y,
975 PAD_TYPE, &ptr1, &ptr2, &ptr3);
977 /* Avoid self-snapping when moving */
978 if (ans != NO_TYPE &&
979 Settings.Mode == MOVE_MODE &&
980 Crosshair.AttachedObject.Type == ELEMENT_TYPE &&
981 ptr1 == Crosshair.AttachedObject.Ptr1)
982 ans = NO_TYPE;
984 if (ans != NO_TYPE &&
985 ( Settings.Mode == LINE_MODE ||
986 (Settings.Mode == MOVE_MODE &&
987 Crosshair.AttachedObject.Type == LINEPOINT_TYPE)))
989 PadType *pad = (PadType *) ptr2;
990 LayerType *desired_layer;
991 Cardinal desired_group;
992 Cardinal SLayer, CLayer;
993 int found_our_layer = false;
995 desired_layer = CURRENT;
996 if (Settings.Mode == MOVE_MODE &&
997 Crosshair.AttachedObject.Type == LINEPOINT_TYPE)
999 desired_layer = (LayerType *)Crosshair.AttachedObject.Ptr1;
1002 /* find layer groups of the component side and solder side */
1003 SLayer = GetLayerGroupNumberByNumber (solder_silk_layer);
1004 CLayer = GetLayerGroupNumberByNumber (component_silk_layer);
1005 desired_group = TEST_FLAG (ONSOLDERFLAG, pad) ? SLayer : CLayer;
1007 GROUP_LOOP (PCB->Data, desired_group);
1009 if (layer == desired_layer)
1011 found_our_layer = true;
1012 break;
1015 END_LOOP;
1017 if (found_our_layer == false)
1018 ans = NO_TYPE;
1021 if (ans != NO_TYPE)
1023 PadType *pad = (PadType *)ptr2;
1024 check_snap_object (&snap_data, pad->Point1.X + (pad->Point2.X - pad->Point1.X) / 2,
1025 pad->Point1.Y + (pad->Point2.Y - pad->Point1.Y) / 2,
1026 true);
1029 ans = NO_TYPE;
1030 if (PCB->RatDraw || TEST_FLAG (SNAPPINFLAG, PCB))
1031 ans = SearchScreenGridSlop (Crosshair.X, Crosshair.Y,
1032 PIN_TYPE, &ptr1, &ptr2, &ptr3);
1034 /* Avoid self-snapping when moving */
1035 if (ans != NO_TYPE &&
1036 Settings.Mode == MOVE_MODE &&
1037 Crosshair.AttachedObject.Type == ELEMENT_TYPE &&
1038 ptr1 == Crosshair.AttachedObject.Ptr1)
1039 ans = NO_TYPE;
1041 if (ans != NO_TYPE)
1043 PinType *pin = (PinType *)ptr2;
1044 check_snap_object (&snap_data, pin->X, pin->Y, true);
1047 ans = NO_TYPE;
1048 if (TEST_FLAG (SNAPPINFLAG, PCB))
1049 ans = SearchScreenGridSlop (Crosshair.X, Crosshair.Y,
1050 VIA_TYPE, &ptr1, &ptr2, &ptr3);
1052 /* Avoid snapping vias to any other vias */
1053 if (Settings.Mode == MOVE_MODE &&
1054 Crosshair.AttachedObject.Type == VIA_TYPE &&
1055 (ans & PIN_TYPES))
1056 ans = NO_TYPE;
1058 if (ans != NO_TYPE)
1060 PinType *pin = (PinType *)ptr2;
1061 check_snap_object (&snap_data, pin->X, pin->Y, true);
1064 ans = NO_TYPE;
1065 if (TEST_FLAG (SNAPPINFLAG, PCB))
1066 ans = SearchScreenGridSlop (Crosshair.X, Crosshair.Y,
1067 LINEPOINT_TYPE | ARCPOINT_TYPE,
1068 &ptr1, &ptr2, &ptr3);
1070 if (ans != NO_TYPE)
1072 PointType *pnt = (PointType *)ptr3;
1073 check_snap_object (&snap_data, pnt->X, pnt->Y, true);
1076 check_snap_offgrid_line (&snap_data, nearest_grid_x, nearest_grid_y);
1078 ans = NO_TYPE;
1079 if (TEST_FLAG (SNAPPINFLAG, PCB))
1080 ans = SearchScreenGridSlop (Crosshair.X, Crosshair.Y,
1081 POLYGONPOINT_TYPE, &ptr1, &ptr2, &ptr3);
1083 if (ans != NO_TYPE)
1085 PointType *pnt = (PointType *)ptr3;
1086 check_snap_object (&snap_data, pnt->X, pnt->Y, true);
1089 if (snap_data.x >= 0 && snap_data.y >= 0)
1091 Crosshair.X = snap_data.x;
1092 Crosshair.Y = snap_data.y;
1095 if (Settings.Mode == ARROW_MODE)
1097 ans = SearchScreenGridSlop (Crosshair.X, Crosshair.Y,
1098 LINEPOINT_TYPE | ARCPOINT_TYPE,
1099 &ptr1, &ptr2, &ptr3);
1100 if (ans == NO_TYPE)
1101 hid_action("PointCursor");
1102 else if (!TEST_FLAG(SELECTEDFLAG, (LineType *)ptr2))
1103 hid_actionl("PointCursor","True", NULL);
1106 if (Settings.Mode == LINE_MODE
1107 && Crosshair.AttachedLine.State != STATE_FIRST
1108 && TEST_FLAG (AUTODRCFLAG, PCB))
1109 EnforceLineDRC ();
1111 gui->set_crosshair (Crosshair.X, Crosshair.Y, HID_SC_DO_NOTHING);
1114 /* ---------------------------------------------------------------------------
1115 * move crosshair relative (has to be switched off)
1117 void
1118 MoveCrosshairRelative (Coord DeltaX, Coord DeltaY)
1120 FitCrosshairIntoGrid (Crosshair.X + DeltaX, Crosshair.Y + DeltaY);
1123 /* ---------------------------------------------------------------------------
1124 * move crosshair absolute
1125 * return true if the crosshair was moved from its existing position
1127 bool
1128 MoveCrosshairAbsolute (Coord X, Coord Y)
1130 Coord x, y, z;
1131 x = Crosshair.X;
1132 y = Crosshair.Y;
1133 FitCrosshairIntoGrid (X, Y);
1134 if (Crosshair.X != x || Crosshair.Y != y)
1136 /* back up to old position to notify the GUI
1137 * (which might want to erase the old crosshair) */
1138 z = Crosshair.X;
1139 Crosshair.X = x;
1140 x = z;
1141 z = Crosshair.Y;
1142 Crosshair.Y = y;
1143 notify_crosshair_change (false); /* Our caller notifies when it has done */
1144 /* now move forward again */
1145 Crosshair.X = x;
1146 Crosshair.Y = z;
1147 return (true);
1149 return (false);
1152 /* ---------------------------------------------------------------------------
1153 * sets the valid range for the crosshair cursor
1155 void
1156 SetCrosshairRange (Coord MinX, Coord MinY, Coord MaxX, Coord MaxY)
1158 Crosshair.MinX = MAX (0, MinX);
1159 Crosshair.MinY = MAX (0, MinY);
1160 Crosshair.MaxX = MIN (PCB->MaxWidth, MaxX);
1161 Crosshair.MaxY = MIN (PCB->MaxHeight, MaxY);
1163 /* force update of position */
1164 MoveCrosshairRelative (0, 0);
1167 /* ---------------------------------------------------------------------------
1168 * initializes crosshair stuff
1169 * clears the struct, allocates to graphical contexts
1171 void
1172 InitCrosshair (void)
1174 Crosshair.GC = gui->make_gc ();
1176 gui->set_color (Crosshair.GC, Settings.CrosshairColor);
1177 gui->set_draw_xor (Crosshair.GC, 1);
1178 gui->set_line_cap (Crosshair.GC, Trace_Cap);
1179 gui->set_line_width (Crosshair.GC, 1);
1181 /* set initial shape */
1182 Crosshair.shape = Basic_Crosshair_Shape;
1184 /* set default limits */
1185 Crosshair.MinX = Crosshair.MinY = 0;
1186 Crosshair.MaxX = PCB->MaxWidth;
1187 Crosshair.MaxY = PCB->MaxHeight;
1189 /* clear the mark */
1190 Marked.status = false;
1193 /* ---------------------------------------------------------------------------
1194 * exits crosshair routines, release GCs
1196 void
1197 DestroyCrosshair (void)
1199 FreePolygonMemory (&Crosshair.AttachedPolygon);
1200 gui->destroy_gc (Crosshair.GC);