6 * PCB, interactive printed circuit board design
7 * Copyright (C) 1994,1995,1996,2004,2006 Thomas Nau
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 * Contact addresses for paper mail and Email:
24 * Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
25 * Thomas.Nau@rz.uni-ulm.de
30 /* misc functions used by several modules
45 #include <sys/param.h>
47 #include <sys/types.h>
58 #include "crosshair.h"
71 #include "rubberband.h"
77 #ifdef HAVE_LIBDMALLOC
84 /* forward declarations */
85 static char *BumpName (char *);
86 static void RightAngles (int, float *, float *);
87 static void GetGridLockCoordinates (int, void *, void *, void *,
88 LocationType
*, LocationType
*);
94 * Used by SaveStackAndVisibility() and
95 * RestoreStackAndVisibility()
100 Boolean ElementOn
, InvisibleObjectsOn
, PinOn
, ViaOn
, RatOn
;
101 int LayerStack
[MAX_LAYER
];
102 Boolean LayerOn
[MAX_LAYER
];
106 /* Get Value returns a numeric value passed from the string and sets the
107 * Boolean variable absolute to False if it leads with a +/- character
110 GetValue (char *val
, char *units
, Boolean
* absolute
)
114 /* if the first character is a sign we have to add the
115 * value to the current one
120 value
= atof (val
+ 1);
124 if (isdigit ((int) *val
))
132 if (strncasecmp (units
, "mm", 2) == 0)
134 else if (strncasecmp (units
, "mil", 3) == 0)
140 /* ---------------------------------------------------------------------------
141 * sets the bounding box of a point (which is silly)
144 SetPointBoundingBox (PointTypePtr Pnt
)
150 /* ---------------------------------------------------------------------------
151 * sets the bounding box of a pin or via
154 SetPinBoundingBox (PinTypePtr Pin
)
158 /* the bounding box covers the extent of influence
159 * so it must include the clearance values too
161 width
= (Pin
->Clearance
+ Pin
->Thickness
+ 1) / 2;
162 width
= MAX (width
, (Pin
->Mask
+ 1) / 2);
163 Pin
->BoundingBox
.X1
= Pin
->X
- width
;
164 Pin
->BoundingBox
.Y1
= Pin
->Y
- width
;
165 Pin
->BoundingBox
.X2
= Pin
->X
+ width
;
166 Pin
->BoundingBox
.Y2
= Pin
->Y
+ width
;
169 /* ---------------------------------------------------------------------------
170 * sets the bounding box of a pad
173 SetPadBoundingBox (PadTypePtr Pad
)
177 /* the bounding box covers the extent of influence
178 * so it must include the clearance values too
180 width
= (Pad
->Thickness
+ Pad
->Clearance
+ 1) / 2;
181 width
= MAX (width
, (Pad
->Mask
+ 1) / 2);
182 Pad
->BoundingBox
.X1
= MIN (Pad
->Point1
.X
, Pad
->Point2
.X
) - width
;
183 Pad
->BoundingBox
.X2
= MAX (Pad
->Point1
.X
, Pad
->Point2
.X
) + width
;
184 Pad
->BoundingBox
.Y1
= MIN (Pad
->Point1
.Y
, Pad
->Point2
.Y
) - width
;
185 Pad
->BoundingBox
.Y2
= MAX (Pad
->Point1
.Y
, Pad
->Point2
.Y
) + width
;
188 /* ---------------------------------------------------------------------------
189 * sets the bounding box of a line
192 SetLineBoundingBox (LineTypePtr Line
)
196 width
= (Line
->Thickness
+ Line
->Clearance
+ 1) / 2;
198 Line
->BoundingBox
.X1
= MIN (Line
->Point1
.X
, Line
->Point2
.X
) - width
;
199 Line
->BoundingBox
.X2
= MAX (Line
->Point1
.X
, Line
->Point2
.X
) + width
;
200 Line
->BoundingBox
.Y1
= MIN (Line
->Point1
.Y
, Line
->Point2
.Y
) - width
;
201 Line
->BoundingBox
.Y2
= MAX (Line
->Point1
.Y
, Line
->Point2
.Y
) + width
;
202 SetPointBoundingBox (&Line
->Point1
);
203 SetPointBoundingBox (&Line
->Point2
);
206 /* ---------------------------------------------------------------------------
207 * sets the bounding box of a polygons
210 SetPolygonBoundingBox (PolygonTypePtr Polygon
)
212 Polygon
->BoundingBox
.X1
= Polygon
->BoundingBox
.Y1
= MAX_COORD
;
213 Polygon
->BoundingBox
.X2
= Polygon
->BoundingBox
.Y2
= 0;
214 POLYGONPOINT_LOOP (Polygon
);
216 MAKEMIN (Polygon
->BoundingBox
.X1
, point
->X
);
217 MAKEMIN (Polygon
->BoundingBox
.Y1
, point
->Y
);
218 MAKEMAX (Polygon
->BoundingBox
.X2
, point
->X
);
219 MAKEMAX (Polygon
->BoundingBox
.Y2
, point
->Y
);
224 /* ---------------------------------------------------------------------------
225 * sets the bounding box of an elements
228 SetElementBoundingBox (DataTypePtr Data
, ElementTypePtr Element
,
231 BoxTypePtr box
, vbox
;
233 if (Data
&& Data
->element_tree
)
234 r_delete_entry (Data
->element_tree
, (BoxType
*) Element
);
235 /* first update the text objects */
236 ELEMENTTEXT_LOOP (Element
);
238 if (Data
&& Data
->name_tree
[n
])
239 r_delete_entry (Data
->name_tree
[n
], (BoxType
*) text
);
240 SetTextBoundingBox (Font
, text
);
241 if (Data
&& !Data
->name_tree
[n
])
242 Data
->name_tree
[n
] = r_create_tree (NULL
, 0, 0);
244 r_insert_entry (Data
->name_tree
[n
], (BoxType
*) text
, 0);
248 /* do not include the elementnames bounding box which
249 * is handled separately
251 box
= &Element
->BoundingBox
;
252 vbox
= &Element
->VBox
;
253 box
->X1
= box
->Y1
= MAX_COORD
;
254 box
->X2
= box
->Y2
= 0;
255 ELEMENTLINE_LOOP (Element
);
257 SetLineBoundingBox (line
);
258 MAKEMIN (box
->X1
, line
->Point1
.X
- (line
->Thickness
+ 1) / 2);
259 MAKEMIN (box
->Y1
, line
->Point1
.Y
- (line
->Thickness
+ 1) / 2);
260 MAKEMIN (box
->X1
, line
->Point2
.X
- (line
->Thickness
+ 1) / 2);
261 MAKEMIN (box
->Y1
, line
->Point2
.Y
- (line
->Thickness
+ 1) / 2);
262 MAKEMAX (box
->X2
, line
->Point1
.X
+ (line
->Thickness
+ 1) / 2);
263 MAKEMAX (box
->Y2
, line
->Point1
.Y
+ (line
->Thickness
+ 1) / 2);
264 MAKEMAX (box
->X2
, line
->Point2
.X
+ (line
->Thickness
+ 1) / 2);
265 MAKEMAX (box
->Y2
, line
->Point2
.Y
+ (line
->Thickness
+ 1) / 2);
270 SetArcBoundingBox (arc
);
271 MAKEMIN (box
->X1
, arc
->BoundingBox
.X1
);
272 MAKEMIN (box
->Y1
, arc
->BoundingBox
.Y1
);
273 MAKEMAX (box
->X2
, arc
->BoundingBox
.X2
);
274 MAKEMAX (box
->Y2
, arc
->BoundingBox
.Y2
);
280 if (Data
&& Data
->pin_tree
)
281 r_delete_entry (Data
->pin_tree
, (BoxType
*) pin
);
282 SetPinBoundingBox (pin
);
286 Data
->pin_tree
= r_create_tree (NULL
, 0, 0);
287 r_insert_entry (Data
->pin_tree
, (BoxType
*) pin
, 0);
289 MAKEMIN (box
->X1
, pin
->BoundingBox
.X1
);
290 MAKEMIN (box
->Y1
, pin
->BoundingBox
.Y1
);
291 MAKEMAX (box
->X2
, pin
->BoundingBox
.X2
);
292 MAKEMAX (box
->Y2
, pin
->BoundingBox
.Y2
);
293 MAKEMIN (vbox
->X1
, pin
->X
- pin
->Thickness
/ 2);
294 MAKEMIN (vbox
->Y1
, pin
->Y
- pin
->Thickness
/ 2);
295 MAKEMAX (vbox
->X2
, pin
->X
+ pin
->Thickness
/ 2);
296 MAKEMAX (vbox
->Y2
, pin
->Y
+ pin
->Thickness
/ 2);
301 if (Data
&& Data
->pad_tree
)
302 r_delete_entry (Data
->pad_tree
, (BoxType
*) pad
);
303 SetPadBoundingBox (pad
);
307 Data
->pad_tree
= r_create_tree (NULL
, 0, 0);
308 r_insert_entry (Data
->pad_tree
, (BoxType
*) pad
, 0);
310 MAKEMIN (box
->X1
, pad
->BoundingBox
.X1
);
311 MAKEMIN (box
->Y1
, pad
->BoundingBox
.Y1
);
312 MAKEMAX (box
->X2
, pad
->BoundingBox
.X2
);
313 MAKEMAX (box
->Y2
, pad
->BoundingBox
.Y2
);
315 MIN (pad
->Point1
.X
, pad
->Point2
.X
) - pad
->Thickness
/ 2);
317 MIN (pad
->Point1
.Y
, pad
->Point2
.Y
) - pad
->Thickness
/ 2);
319 MAX (pad
->Point1
.X
, pad
->Point2
.X
) + pad
->Thickness
/ 2);
321 MAX (pad
->Point1
.Y
, pad
->Point2
.Y
) + pad
->Thickness
/ 2);
324 /* now we set the EDGE2FLAG of the pad if Point2
325 * is closer to the outside edge than Point1
329 if (pad
->Point1
.Y
== pad
->Point2
.Y
)
332 if (box
->X2
- pad
->Point2
.X
< pad
->Point1
.X
- box
->X1
)
333 SET_FLAG (EDGE2FLAG
, pad
);
335 CLEAR_FLAG (EDGE2FLAG
, pad
);
340 if (box
->Y2
- pad
->Point2
.Y
< pad
->Point1
.Y
- box
->Y1
)
341 SET_FLAG (EDGE2FLAG
, pad
);
343 CLEAR_FLAG (EDGE2FLAG
, pad
);
348 /* mark pins with component orientation */
349 if ((box
->X2
- box
->X1
) > (box
->Y2
- box
->Y1
))
353 SET_FLAG (EDGE2FLAG
, pin
);
361 CLEAR_FLAG (EDGE2FLAG
, pin
);
365 if (Data
&& !Data
->element_tree
)
366 Data
->element_tree
= r_create_tree (NULL
, 0, 0);
368 r_insert_entry (Data
->element_tree
, box
, 0);
371 /* ---------------------------------------------------------------------------
372 * creates the bounding box of a text object
375 SetTextBoundingBox (FontTypePtr FontPtr
, TextTypePtr Text
)
377 SymbolTypePtr symbol
= FontPtr
->Symbol
;
378 unsigned char *s
= (unsigned char *) Text
->TextString
;
379 LocationType width
= 0, height
= 0;
380 BDimension maxThick
= 0;
383 /* calculate size of the bounding box */
385 if (*s
<= MAX_FONTPOSITION
&& symbol
[*s
].Valid
)
387 LineTypePtr line
= symbol
[*s
].Line
;
388 for (i
= 0; i
< symbol
[*s
].LineN
; line
++, i
++)
389 if (line
->Thickness
> maxThick
)
390 maxThick
= line
->Thickness
;
391 width
+= symbol
[*s
].Width
+ symbol
[*s
].Delta
;
392 height
= MAX (height
, (LocationType
) symbol
[*s
].Height
);
397 ((FontPtr
->DefaultSymbol
.X2
- FontPtr
->DefaultSymbol
.X1
) * 6 / 5);
398 height
= (FontPtr
->DefaultSymbol
.Y2
- FontPtr
->DefaultSymbol
.Y1
);
402 width
*= Text
->Scale
/ 100.;
403 height
*= Text
->Scale
/ 100.;
404 maxThick
*= Text
->Scale
/ 200.;
408 /* set upper-left and lower-right corner;
409 * swap coordinates if necessary (origin is already in 'swapped')
412 Text
->BoundingBox
.X1
= Text
->X
;
413 Text
->BoundingBox
.Y1
= Text
->Y
;
414 if (TEST_FLAG (ONSOLDERFLAG
, Text
))
416 Text
->BoundingBox
.X1
-= maxThick
;
417 Text
->BoundingBox
.Y1
-= SWAP_SIGN_Y (maxThick
);
418 Text
->BoundingBox
.X2
=
419 Text
->BoundingBox
.X1
+ SWAP_SIGN_X (width
+ maxThick
);
420 Text
->BoundingBox
.Y2
=
421 Text
->BoundingBox
.Y1
+ SWAP_SIGN_Y (height
+ 2 * maxThick
);
422 RotateBoxLowLevel (&Text
->BoundingBox
, Text
->X
, Text
->Y
,
423 (4 - Text
->Direction
) & 0x03);
427 Text
->BoundingBox
.X1
-= maxThick
;
428 Text
->BoundingBox
.Y1
-= maxThick
;
429 Text
->BoundingBox
.X2
= Text
->BoundingBox
.X1
+ width
+ maxThick
;
430 Text
->BoundingBox
.Y2
= Text
->BoundingBox
.Y1
+ height
+ 2 * maxThick
;
431 RotateBoxLowLevel (&Text
->BoundingBox
,
432 Text
->X
, Text
->Y
, Text
->Direction
);
436 /* ---------------------------------------------------------------------------
437 * returns True if data area is empty
440 IsDataEmpty (DataTypePtr Data
)
442 Boolean hasNoObjects
;
445 hasNoObjects
= (Data
->ViaN
== 0);
446 hasNoObjects
&= (Data
->ElementN
== 0);
447 for (i
= 0; i
< max_layer
+ 2; i
++)
448 hasNoObjects
= hasNoObjects
&&
449 Data
->Layer
[i
].LineN
== 0 &&
450 Data
->Layer
[i
].ArcN
== 0 &&
451 Data
->Layer
[i
].TextN
== 0 && Data
->Layer
[i
].PolygonN
== 0;
452 return (hasNoObjects
);
456 FlagIsDataEmpty (int parm
)
458 int i
= IsDataEmpty (PCB
->Data
);
459 return parm
? !i
: i
;
462 /* FLAG(DataEmpty,FlagIsDataEmpty,0) */
463 /* FLAG(DataNonEmpty,FlagIsDataEmpty,1) */
465 /* ---------------------------------------------------------------------------
466 * gets minimum and maximum coordinates
467 * returns NULL if layout is empty
470 GetDataBoundingBox (DataTypePtr Data
)
474 /* preset identifiers with highest and lowest possible values */
475 box
.X1
= box
.Y1
= MAX_COORD
;
476 box
.X2
= box
.Y2
= -MAX_COORD
;
478 /* now scan for the lowest/highest X and Y coordinate */
481 box
.X1
= MIN (box
.X1
, via
->X
- via
->Thickness
/ 2);
482 box
.Y1
= MIN (box
.Y1
, via
->Y
- via
->Thickness
/ 2);
483 box
.X2
= MAX (box
.X2
, via
->X
+ via
->Thickness
/ 2);
484 box
.Y2
= MAX (box
.Y2
, via
->Y
+ via
->Thickness
/ 2);
489 box
.X1
= MIN (box
.X1
, element
->BoundingBox
.X1
);
490 box
.Y1
= MIN (box
.Y1
, element
->BoundingBox
.Y1
);
491 box
.X2
= MAX (box
.X2
, element
->BoundingBox
.X2
);
492 box
.Y2
= MAX (box
.Y2
, element
->BoundingBox
.Y2
);
494 TextTypePtr text
= &NAMEONPCB_TEXT (element
);
495 box
.X1
= MIN (box
.X1
, text
->BoundingBox
.X1
);
496 box
.Y1
= MIN (box
.Y1
, text
->BoundingBox
.Y1
);
497 box
.X2
= MAX (box
.X2
, text
->BoundingBox
.X2
);
498 box
.Y2
= MAX (box
.Y2
, text
->BoundingBox
.Y2
);
504 box
.X1
= MIN (box
.X1
, line
->Point1
.X
- line
->Thickness
/ 2);
505 box
.Y1
= MIN (box
.Y1
, line
->Point1
.Y
- line
->Thickness
/ 2);
506 box
.X1
= MIN (box
.X1
, line
->Point2
.X
- line
->Thickness
/ 2);
507 box
.Y1
= MIN (box
.Y1
, line
->Point2
.Y
- line
->Thickness
/ 2);
508 box
.X2
= MAX (box
.X2
, line
->Point1
.X
+ line
->Thickness
/ 2);
509 box
.Y2
= MAX (box
.Y2
, line
->Point1
.Y
+ line
->Thickness
/ 2);
510 box
.X2
= MAX (box
.X2
, line
->Point2
.X
+ line
->Thickness
/ 2);
511 box
.Y2
= MAX (box
.Y2
, line
->Point2
.Y
+ line
->Thickness
/ 2);
516 box
.X1
= MIN (box
.X1
, arc
->BoundingBox
.X1
);
517 box
.Y1
= MIN (box
.Y1
, arc
->BoundingBox
.Y1
);
518 box
.X2
= MAX (box
.X2
, arc
->BoundingBox
.X2
);
519 box
.Y2
= MAX (box
.Y2
, arc
->BoundingBox
.Y2
);
524 box
.X1
= MIN (box
.X1
, text
->BoundingBox
.X1
);
525 box
.Y1
= MIN (box
.Y1
, text
->BoundingBox
.Y1
);
526 box
.X2
= MAX (box
.X2
, text
->BoundingBox
.X2
);
527 box
.Y2
= MAX (box
.Y2
, text
->BoundingBox
.Y2
);
530 ALLPOLYGON_LOOP (Data
);
532 box
.X1
= MIN (box
.X1
, polygon
->BoundingBox
.X1
);
533 box
.Y1
= MIN (box
.Y1
, polygon
->BoundingBox
.Y1
);
534 box
.X2
= MAX (box
.X2
, polygon
->BoundingBox
.X2
);
535 box
.Y2
= MAX (box
.Y2
, polygon
->BoundingBox
.Y2
);
538 return (IsDataEmpty (Data
) ? NULL
: &box
);
541 /* ---------------------------------------------------------------------------
542 * centers the displayed PCB around the specified point (X,Y)
543 * if Delta is false, X,Y are in absolute PCB coordinates
544 * if Delta is true, simply move the center by an amount X, Y in screen
548 CenterDisplay (LocationType X
, LocationType Y
, Boolean Delta
)
550 int save_grid
= PCB
->Grid
;
554 MoveCrosshairRelative (X
, Y
);
558 if (MoveCrosshairAbsolute (X
, Y
))
560 RestoreCrosshair(False
);
563 gui
->set_crosshair (Crosshair
.X
, Crosshair
.Y
, HID_SC_WARP_POINTER
);
564 PCB
->Grid
= save_grid
;
567 /* ---------------------------------------------------------------------------
568 * transforms symbol coordinates so that the left edge of each symbol
569 * is at the zero position. The y coordinates are moved so that min(y) = 0
573 SetFontInfo (FontTypePtr Ptr
)
576 SymbolTypePtr symbol
;
578 LocationType totalminy
= MAX_COORD
;
580 /* calculate cell with and height (is at least DEFAULT_CELLSIZE)
581 * maximum cell width and height
582 * minimum x and y position of all lines
584 Ptr
->MaxWidth
= DEFAULT_CELLSIZE
;
585 Ptr
->MaxHeight
= DEFAULT_CELLSIZE
;
586 for (i
= 0, symbol
= Ptr
->Symbol
; i
<= MAX_FONTPOSITION
; i
++, symbol
++)
588 LocationType minx
, miny
, maxx
, maxy
;
590 /* next one if the index isn't used or symbol is empty (SPACE) */
591 if (!symbol
->Valid
|| !symbol
->LineN
)
594 minx
= miny
= MAX_COORD
;
596 for (line
= symbol
->Line
, j
= symbol
->LineN
; j
; j
--, line
++)
598 minx
= MIN (minx
, line
->Point1
.X
);
599 miny
= MIN (miny
, line
->Point1
.Y
);
600 minx
= MIN (minx
, line
->Point2
.X
);
601 miny
= MIN (miny
, line
->Point2
.Y
);
602 maxx
= MAX (maxx
, line
->Point1
.X
);
603 maxy
= MAX (maxy
, line
->Point1
.Y
);
604 maxx
= MAX (maxx
, line
->Point2
.X
);
605 maxy
= MAX (maxy
, line
->Point2
.Y
);
608 /* move symbol to left edge */
609 for (line
= symbol
->Line
, j
= symbol
->LineN
; j
; j
--, line
++)
610 MOVE_LINE_LOWLEVEL (line
, -minx
, 0);
612 /* set symbol bounding box with a minimum cell size of (1,1) */
613 symbol
->Width
= maxx
- minx
+ 1;
614 symbol
->Height
= maxy
+ 1;
616 /* check total min/max */
617 Ptr
->MaxWidth
= MAX (Ptr
->MaxWidth
, symbol
->Width
);
618 Ptr
->MaxHeight
= MAX (Ptr
->MaxHeight
, symbol
->Height
);
619 totalminy
= MIN (totalminy
, miny
);
622 /* move coordinate system to the upper edge (lowest y on screen) */
623 for (i
= 0, symbol
= Ptr
->Symbol
; i
<= MAX_FONTPOSITION
; i
++, symbol
++)
626 symbol
->Height
-= totalminy
;
627 for (line
= symbol
->Line
, j
= symbol
->LineN
; j
; j
--, line
++)
628 MOVE_LINE_LOWLEVEL (line
, 0, -totalminy
);
631 /* setup the box for the default symbol */
632 Ptr
->DefaultSymbol
.X1
= Ptr
->DefaultSymbol
.Y1
= 0;
633 Ptr
->DefaultSymbol
.X2
= Ptr
->DefaultSymbol
.X1
+ Ptr
->MaxWidth
;
634 Ptr
->DefaultSymbol
.Y2
= Ptr
->DefaultSymbol
.Y1
+ Ptr
->MaxHeight
;
638 GetNum (char **s
, BDimension
* num
)
641 while (isdigit ((int) **s
))
646 /* ----------------------------------------------------------------------
647 * parses the routes definition string which is a colon separated list of
648 * comma separated Name, Dimension, Dimension, Dimension, Dimension
649 * e.g. Signal,20,40,20,10:Power,40,60,28,10:...
652 ParseRouteString (char *s
, RouteStyleTypePtr routeStyle
, int scale
)
657 memset (routeStyle
, 0, NUM_STYLES
* sizeof (RouteStyleType
));
658 for (style
= 0; style
< NUM_STYLES
; style
++, routeStyle
++)
660 while (*s
&& isspace ((int) *s
))
662 for (i
= 0; *s
&& *s
!= ','; i
++)
665 routeStyle
->Name
= MyStrdup (Name
, "ParseRouteString()");
666 if (!isdigit ((int) *++s
))
668 GetNum (&s
, &routeStyle
->Thick
);
669 routeStyle
->Thick
*= scale
;
670 while (*s
&& isspace ((int) *s
))
674 while (*s
&& isspace ((int) *s
))
676 if (!isdigit ((int) *s
))
678 GetNum (&s
, &routeStyle
->Diameter
);
679 routeStyle
->Diameter
*= scale
;
680 while (*s
&& isspace ((int) *s
))
684 while (*s
&& isspace ((int) *s
))
686 if (!isdigit ((int) *s
))
688 GetNum (&s
, &routeStyle
->Hole
);
689 routeStyle
->Hole
*= scale
;
690 /* for backwards-compatibility, we use a 10-mil default
691 * for styles which omit the keepaway specification. */
693 routeStyle
->Keepaway
= 1000;
697 while (*s
&& isspace ((int) *s
))
699 if (!isdigit ((int) *s
))
701 GetNum (&s
, &routeStyle
->Keepaway
);
702 routeStyle
->Keepaway
*= scale
;
703 while (*s
&& isspace ((int) *s
))
706 if (style
< NUM_STYLES
- 1)
708 while (*s
&& isspace ((int) *s
))
717 memset (routeStyle
, 0, NUM_STYLES
* sizeof (RouteStyleType
));
721 /* ----------------------------------------------------------------------
722 * parses the group definition string which is a colon separated list of
723 * comma separated layer numbers (1,2,b:4,6,8,t)
726 ParseGroupString (char *s
, LayerGroupTypePtr LayerGroup
, int LayerN
)
728 int group
, member
, layer
;
729 Boolean c_set
= False
, /* flags for the two special layers to */
730 s_set
= False
; /* provide a default setting for old formats */
731 int groupnum
[MAX_LAYER
+ 2];
734 memset (LayerGroup
, 0, sizeof (LayerGroupType
));
736 /* Clear assignments */
737 for (layer
= 0; layer
< MAX_LAYER
+ 2; layer
++)
738 groupnum
[layer
] = -1;
740 /* loop over all groups */
741 for (group
= 0; s
&& *s
&& group
< LayerN
; group
++)
743 while (*s
&& isspace ((int) *s
))
746 /* loop over all group members */
747 for (member
= 0; *s
; s
++)
749 /* ignore white spaces and get layernumber */
750 while (*s
&& isspace ((int) *s
))
756 layer
= LayerN
+ COMPONENT_LAYER
;
762 layer
= LayerN
+ SOLDER_LAYER
;
767 if (!isdigit ((int) *s
))
769 layer
= atoi (s
) - 1;
772 if (layer
> LayerN
+ MAX (SOLDER_LAYER
, COMPONENT_LAYER
) ||
773 member
>= LayerN
+ 1)
775 groupnum
[layer
] = group
;
776 LayerGroup
->Entries
[group
][member
++] = layer
;
777 while (*++s
&& isdigit ((int) *s
));
779 /* ignore white spaces and check for separator */
780 while (*s
&& isspace ((int) *s
))
782 if (!*s
|| *s
== ':')
787 LayerGroup
->Number
[group
] = member
;
792 LayerGroup
->Entries
[SOLDER_LAYER
][LayerGroup
->Number
[SOLDER_LAYER
]++] =
793 LayerN
+ SOLDER_LAYER
;
796 Entries
[COMPONENT_LAYER
][LayerGroup
->Number
[COMPONENT_LAYER
]++] =
797 LayerN
+ COMPONENT_LAYER
;
799 for (layer
= 0; layer
< LayerN
&& group
< LayerN
; layer
++)
800 if (groupnum
[layer
] == -1)
802 LayerGroup
->Entries
[group
][0] = layer
;
803 LayerGroup
->Number
[group
] = 1;
808 /* reset structure on error */
810 memset (LayerGroup
, 0, sizeof (LayerGroupType
));
814 /* ---------------------------------------------------------------------------
818 QuitApplication (void)
821 * save data if necessary. It not needed, then don't trigger EmergencySave
822 * via our atexit() registering of EmergencySave(). We presumeably wanted to
823 * exit here and thus it is not an emergency.
825 if (PCB
->Changed
&& Settings
.SaveInTMP
)
828 DisableEmergencySave ();
831 * if Settings.init_done is not > 0 then we haven't even called
832 * gtk_main() yet so gtk_main_quit() will give an error. In
833 * this case just set the flag to -1 and we will exit instead
834 * of calling gtk_main()
836 if (Settings
.init_done
> 0)
839 Settings
.init_done
= -1;
842 /* ---------------------------------------------------------------------------
843 * creates a filename from a template
844 * %f is replaced by the filename
845 * %p by the searchpath
848 EvaluateFilename (char *Template
, char *Path
, char *Filename
, char *Parameter
)
850 static DynamicStringType command
;
853 if (Settings
.verbose
)
855 printf ("EvaluateFilename:\n");
856 printf ("\tTemplate: \033[33m%s\033[0m\n", Template
);
857 printf ("\tPath: \033[33m%s\033[0m\n", Path
);
858 printf ("\tFilename: \033[33m%s\033[0m\n", Filename
);
859 printf ("\tParameter: \033[33m%s\033[0m\n", Parameter
);
862 DSClearString (&command
);
864 for (p
= Template
; p
&& *p
; p
++)
866 /* copy character or add string to command */
868 && (*(p
+ 1) == 'f' || *(p
+ 1) == 'p' || *(p
+ 1) == 'a'))
872 DSAddString (&command
, Parameter
);
875 DSAddString (&command
, Filename
);
878 DSAddString (&command
, Path
);
882 DSAddCharacter (&command
, *p
);
884 DSAddCharacter (&command
, '\0');
885 if (Settings
.verbose
)
886 printf ("EvaluateFilename: \033[32m%s\033[0m\n", command
.Data
);
888 return (MyStrdup (command
.Data
, "EvaluateFilename()"));
891 /* ---------------------------------------------------------------------------
892 * concatenates directory and filename if directory != NULL,
893 * expands them with a shell and returns the found name(s) or NULL
896 ExpandFilename (char *Dirname
, char *Filename
)
898 static DynamicStringType answer
;
903 /* allocate memory for commandline and build it */
904 DSClearString (&answer
);
907 command
= MyCalloc (strlen (Filename
) + strlen (Dirname
) + 7,
908 sizeof (char), "ExpandFilename()");
909 sprintf (command
, "echo %s/%s", Dirname
, Filename
);
913 command
= MyCalloc (strlen (Filename
) + 6, sizeof (char), "Expand()");
914 sprintf (command
, "echo %s", Filename
);
917 /* execute it with shell */
918 if ((pipe
= popen (command
, "r")) != NULL
)
920 /* discard all but the first returned line */
923 if ((c
= fgetc (pipe
)) == EOF
|| c
== '\n' || c
== '\r')
926 DSAddCharacter (&answer
, c
);
930 return (pclose (pipe
) ? NULL
: answer
.Data
);
933 /* couldn't be expanded by the shell */
934 PopenErrorMessage (command
);
940 /* ---------------------------------------------------------------------------
941 * returns the layer number for the passed pointer
944 GetLayerNumber (DataTypePtr Data
, LayerTypePtr Layer
)
948 for (i
= 0; i
< MAX_LAYER
+ 2; i
++)
949 if (Layer
== &Data
->Layer
[i
])
954 /* ---------------------------------------------------------------------------
955 * move layer (number is passed in) to top of layerstack
958 PushOnTopOfLayerStack (int NewTop
)
962 /* ignore COMPONENT_LAYER and SOLDER_LAYER */
963 if (NewTop
< max_layer
)
965 /* first find position of passed one */
966 for (i
= 0; i
< max_layer
; i
++)
967 if (LayerStack
[i
] == NewTop
)
970 /* bring this element to the top of the stack */
972 LayerStack
[i
] = LayerStack
[i
- 1];
973 LayerStack
[0] = NewTop
;
978 /* ----------------------------------------------------------------------
979 * changes the visibility of all layers in a group
980 * returns the number of changed layers
983 ChangeGroupVisibility (int Layer
, Boolean On
, Boolean ChangeStackOrder
)
985 int group
, i
, changed
= 1; /* at least the current layer changes */
987 /* Warning: these special case values must agree with what gui-top-window.c
991 if (Settings
.verbose
)
992 printf ("ChangeGroupVisibility(Layer=%d, On=%d, ChangeStackOrder=%d)\n",
993 Layer
, On
, ChangeStackOrder
);
995 /* decrement 'i' to keep stack in order of layergroup */
996 if ((group
= GetGroupOfLayer (Layer
)) < max_layer
)
997 for (i
= PCB
->LayerGroups
.Number
[group
]; i
;)
999 int layer
= PCB
->LayerGroups
.Entries
[group
][--i
];
1001 /* don't count the passed member of the group */
1002 if (layer
!= Layer
&& layer
< max_layer
)
1004 PCB
->Data
->Layer
[layer
].On
= On
;
1006 /* push layer on top of stack if switched on */
1007 if (On
&& ChangeStackOrder
)
1008 PushOnTopOfLayerStack (layer
);
1013 /* change at least the passed layer */
1014 PCB
->Data
->Layer
[Layer
].On
= On
;
1015 if (On
&& ChangeStackOrder
)
1016 PushOnTopOfLayerStack (Layer
);
1018 /* update control panel and exit */
1019 hid_action ("LayersChanged");
1023 /* ----------------------------------------------------------------------
1024 * Given a string description of a layer stack, adjust the layer stack
1029 LayerStringToLayerStack (char *s
)
1031 static int listed_layers
= 0;
1038 args
= (char **) malloc (l
* sizeof (char *));
1061 for (i
= 0; i
< max_layer
+ 2; i
++)
1065 PCB
->Data
->Layer
[i
].On
= False
;
1067 PCB
->ElementOn
= False
;
1068 PCB
->InvisibleObjectsOn
= False
;
1073 for (i
=argn
-1; i
>=0; i
--)
1075 if (strcasecmp (args
[i
], "rats") == 0)
1077 else if (strcasecmp (args
[i
], "invisible") == 0)
1078 PCB
->InvisibleObjectsOn
= True
;
1079 else if (strcasecmp (args
[i
], "pins") == 0)
1081 else if (strcasecmp (args
[i
], "vias") == 0)
1083 else if (strcasecmp (args
[i
], "elements") == 0)
1084 PCB
->ElementOn
= True
;
1085 else if (isdigit ((int) args
[i
][0]))
1087 lno
= atoi (args
[i
]);
1088 ChangeGroupVisibility (lno
, True
, True
);
1093 for (lno
= 0; lno
< max_layer
; lno
++)
1094 if (strcasecmp (args
[i
], PCB
->Data
->Layer
[lno
].Name
) == 0)
1096 ChangeGroupVisibility (lno
, True
, True
);
1102 fprintf(stderr
, "Warning: layer \"%s\" not known\n", args
[i
]);
1105 fprintf (stderr
, "Named layers in this board are:\n");
1107 for (lno
=0; lno
< max_layer
; lno
++)
1108 fprintf(stderr
, "\t%s\n", PCB
->Data
->Layer
[lno
].Name
);
1115 /* ----------------------------------------------------------------------
1116 * lookup the group to which a layer belongs to
1117 * returns max_layer if no group is found
1120 GetGroupOfLayer (int Layer
)
1124 if (Layer
== max_layer
)
1126 for (group
= 0; group
< max_layer
; group
++)
1127 for (i
= 0; i
< PCB
->LayerGroups
.Number
[group
]; i
++)
1128 if (PCB
->LayerGroups
.Entries
[group
][i
] == Layer
)
1134 /* ---------------------------------------------------------------------------
1135 * returns the layergroup number for the passed pointer
1138 GetLayerGroupNumberByPointer (LayerTypePtr Layer
)
1140 return (GetLayerGroupNumberByNumber (GetLayerNumber (PCB
->Data
, Layer
)));
1143 /* ---------------------------------------------------------------------------
1144 * returns the layergroup number for the passed layernumber
1147 GetLayerGroupNumberByNumber (Cardinal Layer
)
1151 for (group
= 0; group
< max_layer
; group
++)
1152 for (entry
= 0; entry
< PCB
->LayerGroups
.Number
[group
]; entry
++)
1153 if (PCB
->LayerGroups
.Entries
[group
][entry
] == Layer
)
1156 /* since every layer belongs to a group it is safe to return
1157 * the value without boundary checking
1162 /* ---------------------------------------------------------------------------
1163 * returns a pointer to an objects bounding box;
1164 * data is valid until the routine is called again
1167 GetObjectBoundingBox (int Type
, void *Ptr1
, void *Ptr2
, void *Ptr3
)
1175 PinTypePtr via
= (PinTypePtr
) Ptr1
;
1177 box
.X1
= via
->X
- via
->Thickness
/ 2;
1178 box
.Y1
= via
->Y
- via
->Thickness
/ 2;
1179 box
.X2
= via
->X
+ via
->Thickness
/ 2;
1180 box
.Y2
= via
->Y
+ via
->Thickness
/ 2;
1186 LineTypePtr line
= (LineTypePtr
) Ptr2
;
1188 box
.X1
= MIN (line
->Point1
.X
, line
->Point2
.X
);
1189 box
.Y1
= MIN (line
->Point1
.Y
, line
->Point2
.Y
);
1190 box
.X2
= MAX (line
->Point1
.X
, line
->Point2
.X
);
1191 box
.Y2
= MAX (line
->Point1
.Y
, line
->Point2
.Y
);
1192 box
.X1
-= line
->Thickness
/ 2;
1193 box
.Y1
-= line
->Thickness
/ 2;
1194 box
.X2
+= line
->Thickness
/ 2;
1195 box
.Y2
+= line
->Thickness
/ 2;
1200 box
= ((ArcTypePtr
) Ptr2
)->BoundingBox
;
1204 case ELEMENTNAME_TYPE
:
1205 box
= ((TextTypePtr
) Ptr2
)->BoundingBox
;
1209 box
= ((PolygonTypePtr
) Ptr2
)->BoundingBox
;
1213 box
= ((ElementTypePtr
) Ptr1
)->BoundingBox
;
1215 TextTypePtr text
= &NAMEONPCB_TEXT ((ElementTypePtr
) Ptr1
);
1216 if (text
->BoundingBox
.X1
< box
.X1
)
1217 box
.X1
= text
->BoundingBox
.X1
;
1218 if (text
->BoundingBox
.Y1
< box
.Y1
)
1219 box
.Y1
= text
->BoundingBox
.Y1
;
1220 if (text
->BoundingBox
.X2
> box
.X2
)
1221 box
.X2
= text
->BoundingBox
.X2
;
1222 if (text
->BoundingBox
.Y2
> box
.Y2
)
1223 box
.Y2
= text
->BoundingBox
.Y2
;
1229 PadTypePtr pad
= (PadTypePtr
) Ptr2
;
1231 box
.X1
= MIN (pad
->Point1
.X
, pad
->Point2
.X
);
1232 box
.Y1
= MIN (pad
->Point1
.Y
, pad
->Point2
.Y
);
1233 box
.X2
= MAX (pad
->Point1
.X
, pad
->Point2
.X
);
1234 box
.Y2
= MAX (pad
->Point1
.Y
, pad
->Point2
.Y
);
1235 box
.X1
-= pad
->Thickness
;
1236 box
.Y1
-= pad
->Thickness
;
1237 box
.Y2
+= pad
->Thickness
;
1238 box
.X2
+= pad
->Thickness
;
1244 PinTypePtr pin
= (PinTypePtr
) Ptr2
;
1246 box
.X1
= pin
->X
- pin
->Thickness
/ 2;
1247 box
.Y1
= pin
->Y
- pin
->Thickness
/ 2;
1248 box
.X2
= pin
->X
+ pin
->Thickness
/ 2;
1249 box
.Y2
= pin
->Y
+ pin
->Thickness
/ 2;
1253 case LINEPOINT_TYPE
:
1255 PointTypePtr point
= (PointTypePtr
) Ptr3
;
1257 box
.X1
= box
.X2
= point
->X
;
1258 box
.Y1
= box
.Y2
= point
->Y
;
1262 case POLYGONPOINT_TYPE
:
1264 PointTypePtr point
= (PointTypePtr
) Ptr3
;
1266 box
.X1
= box
.X2
= point
->X
;
1267 box
.Y1
= box
.Y2
= point
->Y
;
1272 Message ("Request for bounding box of unsupported type %d\n", Type
);
1273 box
.X1
= box
.X2
= box
.Y1
= box
.Y2
= 0;
1279 /* ---------------------------------------------------------------------------
1280 * computes the bounding box of an arc
1283 SetArcBoundingBox (ArcTypePtr Arc
)
1285 register double ca1
, ca2
, sa1
, sa2
;
1286 register LocationType ang1
, ang2
;
1287 register LocationType width
;
1289 /* first put angles into standard form */
1290 if (Arc
->Delta
> 360)
1292 ang1
= Arc
->StartAngle
;
1293 ang2
= Arc
->StartAngle
+ Arc
->Delta
;
1306 /* calculate sines, cosines */
1326 ca1
= M180
* (double) ang1
;
1349 ca2
= M180
* (double) ang2
;
1354 Arc
->BoundingBox
.X2
= Arc
->X
- Arc
->Width
*
1355 ((ang1
< 180 && ang2
> 180) ? -1 : MIN (ca1
, ca2
));
1357 Arc
->BoundingBox
.X1
= Arc
->X
- Arc
->Width
*
1358 ((ang1
< 360 && ang2
> 360) ? 1 : MAX (ca1
, ca2
));
1360 Arc
->BoundingBox
.Y2
= Arc
->Y
+ Arc
->Height
*
1361 ((ang1
< 90 && ang2
> 90) ? 1 : MAX (sa1
, sa2
));
1363 Arc
->BoundingBox
.Y1
= Arc
->Y
+ Arc
->Height
*
1364 ((ang1
< 270 && ang2
> 270) ? -1 : MIN (sa1
, sa2
));
1366 width
= (Arc
->Thickness
+ Arc
->Clearance
) / 2;
1367 Arc
->BoundingBox
.X1
-= width
;
1368 Arc
->BoundingBox
.X2
+= width
;
1369 Arc
->BoundingBox
.Y1
-= width
;
1370 Arc
->BoundingBox
.Y2
+= width
;
1373 /* ---------------------------------------------------------------------------
1374 * resets the layerstack setting
1377 ResetStackAndVisibility (void)
1382 for (i
= 0; i
< max_layer
+ 2; i
++)
1386 PCB
->Data
->Layer
[i
].On
= True
;
1388 PCB
->ElementOn
= True
;
1389 PCB
->InvisibleObjectsOn
= True
;
1394 /* Bring the component group to the front and make it active. */
1395 comp_group
= GetLayerGroupNumberByNumber (max_layer
+ COMPONENT_LAYER
);
1396 ChangeGroupVisibility (PCB
->LayerGroups
.Entries
[comp_group
][0], 1, 1);
1399 /* ---------------------------------------------------------------------------
1400 * saves the layerstack setting
1403 SaveStackAndVisibility (void)
1406 static Boolean run
= False
;
1414 if (SavedStack
.cnt
!= 0)
1417 "SaveStackAndVisibility() layerstack was already saved and not"
1418 "yet restored. cnt = %d\n", SavedStack
.cnt
);
1421 for (i
= 0; i
< max_layer
+ 2; i
++)
1424 SavedStack
.LayerStack
[i
] = LayerStack
[i
];
1425 SavedStack
.LayerOn
[i
] = PCB
->Data
->Layer
[i
].On
;
1427 SavedStack
.ElementOn
= PCB
->ElementOn
;
1428 SavedStack
.InvisibleObjectsOn
= PCB
->InvisibleObjectsOn
;
1429 SavedStack
.PinOn
= PCB
->PinOn
;
1430 SavedStack
.ViaOn
= PCB
->ViaOn
;
1431 SavedStack
.RatOn
= PCB
->RatOn
;
1435 /* ---------------------------------------------------------------------------
1436 * restores the layerstack setting
1439 RestoreStackAndVisibility (void)
1443 if (SavedStack
.cnt
== 0)
1445 fprintf (stderr
, "RestoreStackAndVisibility() layerstack has not"
1446 " been saved. cnt = %d\n", SavedStack
.cnt
);
1449 else if (SavedStack
.cnt
!= 1)
1451 fprintf (stderr
, "RestoreStackAndVisibility() layerstack save count is"
1452 " wrong. cnt = %d\n", SavedStack
.cnt
);
1455 for (i
= 0; i
< max_layer
+ 2; i
++)
1458 LayerStack
[i
] = SavedStack
.LayerStack
[i
];
1459 PCB
->Data
->Layer
[i
].On
= SavedStack
.LayerOn
[i
];
1461 PCB
->ElementOn
= SavedStack
.ElementOn
;
1462 PCB
->InvisibleObjectsOn
= SavedStack
.InvisibleObjectsOn
;
1463 PCB
->PinOn
= SavedStack
.PinOn
;
1464 PCB
->ViaOn
= SavedStack
.ViaOn
;
1465 PCB
->RatOn
= SavedStack
.RatOn
;
1470 /* ----------------------------------------------------------------------
1471 * returns pointer to current working directory. If 'path' is not
1472 * NULL, then the current working directory is copied to the array
1473 * pointed to by 'path'
1476 GetWorkingDirectory (char *path
)
1479 return getcwd (path
, MAXPATHLEN
);
1481 /* seems that some BSD releases lack of a prototype for getwd() */
1482 return getwd (path
);
1487 /* ---------------------------------------------------------------------------
1488 * writes a string to the passed file pointer
1489 * some special characters are quoted
1492 CreateQuotedString (DynamicStringTypePtr DS
, char *S
)
1495 DSAddCharacter (DS
, '"');
1498 if (*S
== '"' || *S
== '\\')
1499 DSAddCharacter (DS
, '\\');
1500 DSAddCharacter (DS
, *S
++);
1502 DSAddCharacter (DS
, '"');
1507 RightAngles (int Angle
, float *cosa
, float *sina
)
1509 *cosa
= (float) cos ((double) Angle
* M180
);
1510 *sina
= (float) sin ((double) Angle
* M180
);
1514 GetArcEnds (ArcTypePtr Arc
)
1519 RightAngles (Arc
->StartAngle
, &ca
, &sa
);
1520 box
.X1
= Arc
->X
- Arc
->Width
* ca
;
1521 box
.Y1
= Arc
->Y
+ Arc
->Height
* sa
;
1522 RightAngles (Arc
->StartAngle
+ Arc
->Delta
, &ca
, &sa
);
1523 box
.X2
= Arc
->X
- Arc
->Width
* ca
;
1524 box
.Y2
= Arc
->Y
+ Arc
->Height
* sa
;
1529 /* doesn't this belong in change.c ?? */
1531 ChangeArcAngles (LayerTypePtr Layer
, ArcTypePtr a
,
1532 long int new_sa
, long int new_da
)
1539 RestoreToPolygon (PCB
->Data
, ARC_TYPE
, Layer
, a
);
1540 r_delete_entry (Layer
->arc_tree
, (BoxTypePtr
) a
);
1541 AddObjectToChangeAnglesUndoList (ARC_TYPE
, a
, a
, a
);
1542 a
->StartAngle
= new_sa
;
1544 SetArcBoundingBox (a
);
1545 r_insert_entry (Layer
->arc_tree
, (BoxTypePtr
) a
, 0);
1546 ClearFromPolygon (PCB
->Data
, ARC_TYPE
, Layer
, a
);
1550 BumpName (char *Name
)
1554 static char temp
[256];
1557 /* seek end of string */
1560 /* back up to potential number */
1561 for (Name
--; isdigit ((int) *Name
); Name
--);
1564 num
= atoi (Name
) + 1;
1569 sprintf (temp
, "%s%d", start
, num
);
1570 /* if this is not our string, put back the blown character */
1577 * make a unique name for the name on board
1578 * this can alter the contents of the input string
1581 UniqueElementName (DataTypePtr Data
, char *Name
)
1583 Boolean unique
= True
;
1584 /* null strings are ok */
1585 if (!Name
|| !*Name
)
1590 ELEMENT_LOOP (Data
);
1592 if (NAMEONPCB_NAME (element
) &&
1593 NSTRCMP (NAMEONPCB_NAME (element
), Name
) == 0)
1595 Name
= BumpName (Name
);
1608 GetGridLockCoordinates (int type
, void *ptr1
,
1609 void *ptr2
, void *ptr3
, LocationType
* x
,
1615 *x
= ((PinTypePtr
) ptr2
)->X
;
1616 *y
= ((PinTypePtr
) ptr2
)->Y
;
1619 *x
= ((LineTypePtr
) ptr2
)->Point1
.X
;
1620 *y
= ((LineTypePtr
) ptr2
)->Point1
.Y
;
1623 case ELEMENTNAME_TYPE
:
1624 *x
= ((TextTypePtr
) ptr2
)->X
;
1625 *y
= ((TextTypePtr
) ptr2
)->Y
;
1628 *x
= ((ElementTypePtr
) ptr2
)->MarkX
;
1629 *y
= ((ElementTypePtr
) ptr2
)->MarkY
;
1632 *x
= ((PolygonTypePtr
) ptr2
)->Points
[0].X
;
1633 *y
= ((PolygonTypePtr
) ptr2
)->Points
[0].Y
;
1636 case LINEPOINT_TYPE
:
1637 case POLYGONPOINT_TYPE
:
1638 *x
= ((PointTypePtr
) ptr3
)->X
;
1639 *y
= ((PointTypePtr
) ptr3
)->Y
;
1645 box
= GetArcEnds ((ArcTypePtr
) ptr2
);
1654 AttachForCopy (LocationType PlaceX
, LocationType PlaceY
)
1657 LocationType mx
= 0, my
= 0;
1659 Crosshair
.AttachedObject
.RubberbandN
= 0;
1660 if (! TEST_FLAG (SNAPPINFLAG
, PCB
))
1662 /* dither the grab point so that the mark, center, etc
1663 * will end up on a grid coordinate
1665 GetGridLockCoordinates (Crosshair
.AttachedObject
.Type
,
1666 Crosshair
.AttachedObject
.Ptr1
,
1667 Crosshair
.AttachedObject
.Ptr2
,
1668 Crosshair
.AttachedObject
.Ptr3
, &mx
, &my
);
1669 mx
= GRIDFIT_X (mx
, PCB
->Grid
) - mx
;
1670 my
= GRIDFIT_Y (my
, PCB
->Grid
) - my
;
1672 Crosshair
.AttachedObject
.X
= PlaceX
- mx
;
1673 Crosshair
.AttachedObject
.Y
= PlaceY
- my
;
1674 if (!Marked
.status
|| TEST_FLAG (LOCALREFFLAG
, PCB
))
1675 SetLocalRef (PlaceX
- mx
, PlaceY
- my
, True
);
1676 Crosshair
.AttachedObject
.State
= STATE_SECOND
;
1678 /* get boundingbox of object and set cursor range */
1679 box
= GetObjectBoundingBox (Crosshair
.AttachedObject
.Type
,
1680 Crosshair
.AttachedObject
.Ptr1
,
1681 Crosshair
.AttachedObject
.Ptr2
,
1682 Crosshair
.AttachedObject
.Ptr3
);
1683 SetCrosshairRange (Crosshair
.AttachedObject
.X
- box
->X1
,
1684 Crosshair
.AttachedObject
.Y
- box
->Y1
,
1685 PCB
->MaxWidth
- (box
->X2
- Crosshair
.AttachedObject
.X
),
1686 PCB
->MaxHeight
- (box
->Y2
- Crosshair
.AttachedObject
.Y
));
1688 /* get all attached objects if necessary */
1689 if ((Settings
.Mode
!= COPY_MODE
) && TEST_FLAG (RUBBERBANDFLAG
, PCB
))
1690 LookupRubberbandLines (Crosshair
.AttachedObject
.Type
,
1691 Crosshair
.AttachedObject
.Ptr1
,
1692 Crosshair
.AttachedObject
.Ptr2
,
1693 Crosshair
.AttachedObject
.Ptr3
);
1694 if (Settings
.Mode
!= COPY_MODE
&&
1695 (Crosshair
.AttachedObject
.Type
== ELEMENT_TYPE
||
1696 Crosshair
.AttachedObject
.Type
== VIA_TYPE
||
1697 Crosshair
.AttachedObject
.Type
== LINE_TYPE
||
1698 Crosshair
.AttachedObject
.Type
== LINEPOINT_TYPE
))
1699 LookupRatLines (Crosshair
.AttachedObject
.Type
,
1700 Crosshair
.AttachedObject
.Ptr1
,
1701 Crosshair
.AttachedObject
.Ptr2
,
1702 Crosshair
.AttachedObject
.Ptr3
);
1706 * Return nonzero if the given file exists and is readable.
1709 FileExists (const char *name
)
1712 f
= fopen (name
, "r");
1722 Concat (const char *first
, ...)
1728 len
= strlen (first
);
1729 rv
= (char *) malloc (len
+ 1);
1732 va_start (a
, first
);
1735 const char *s
= va_arg (a
, const char *);
1739 rv
= (char *) realloc (rv
, len
+ 1);
1747 mem_any_set (unsigned char *ptr
, int bytes
)
1755 /* This just fills in a FlagType with current flags. */
1757 MakeFlags (unsigned int flags
)
1760 memset (&rv
, 0, sizeof (rv
));
1765 /* This converts old flag bits (from saved PCB files) to new format. */
1767 OldFlags (unsigned int flags
)
1771 memset (&rv
, 0, sizeof (rv
));
1772 /* If we move flag bits around, this is where we map old bits to them. */
1773 rv
.f
= flags
& 0xffff;
1775 for (i
= 0; i
< 8; i
++)
1777 /* use the closest thing to the old thermal style */
1779 rv
.t
[i
/ 2] = (1 << (4 * (i
% 2)));
1786 AddFlags (FlagType flag
, unsigned int flags
)
1793 MaskFlags (FlagType flag
, unsigned int flags
)
1799 /***********************************************************************
1800 * Layer Group Functions
1804 MoveLayerToGroup (int layer
, int group
)
1808 if (layer
< 0 || layer
> max_layer
+ 1)
1810 prev
= GetLayerGroupNumberByNumber (layer
);
1811 if ((layer
== max_layer
1812 && group
== GetLayerGroupNumberByNumber (max_layer
+ 1))
1813 || (layer
== max_layer
+ 1
1814 && group
== GetLayerGroupNumberByNumber (max_layer
))
1815 || (group
< 0 || group
>= max_layer
) || (prev
== group
))
1818 /* Remove layer from prev group */
1819 for (j
= i
= 0; i
< PCB
->LayerGroups
.Number
[prev
]; i
++)
1820 if (PCB
->LayerGroups
.Entries
[prev
][i
] != layer
)
1821 PCB
->LayerGroups
.Entries
[prev
][j
++] = PCB
->LayerGroups
.Entries
[prev
][i
];
1822 PCB
->LayerGroups
.Number
[prev
]--;
1824 /* Add layer to new group. */
1825 i
= PCB
->LayerGroups
.Number
[group
]++;
1826 PCB
->LayerGroups
.Entries
[group
][i
] = layer
;
1832 LayerGroupsToString (LayerGroupTypePtr lg
)
1834 #if MAX_LAYER < 9998
1835 /* Allows for layer numbers 0..9999 */
1836 static char buf
[(MAX_LAYER
+ 2) * 5 + 1];
1841 for (group
= 0; group
< max_layer
; group
++)
1842 if (PCB
->LayerGroups
.Number
[group
])
1847 for (entry
= 0; entry
< PCB
->LayerGroups
.Number
[group
]; entry
++)
1849 int layer
= PCB
->LayerGroups
.Entries
[group
][entry
];
1850 if (layer
== max_layer
+ COMPONENT_LAYER
)
1854 else if (layer
== max_layer
+ SOLDER_LAYER
)
1860 sprintf (cp
, "%d", layer
+ 1);
1864 if (entry
!= PCB
->LayerGroups
.Number
[group
] - 1)
1875 #ifdef HAVE_GETPWUID
1876 static struct passwd
*pwentry
;
1877 static char *fab_author
= 0;
1881 if (Settings
.FabAuthor
&& Settings
.FabAuthor
[0])
1882 fab_author
= Settings
.FabAuthor
;
1886 char *comma
, *gecos
;
1889 pwentry
= getpwuid (getuid ());
1890 gecos
= pwentry
->pw_gecos
;
1891 comma
= strchr (gecos
, ',');
1893 len
= comma
- gecos
;
1895 len
= strlen (gecos
);
1896 fab_author
= malloc (len
+ 1);
1899 perror ("pcb: out of memory.\n");
1902 memcpy (fab_author
, gecos
, len
);
1903 fab_author
[len
] = 0;
1916 static char buf
[100];
1925 d
+= 0.0000005; /* rounding */
1928 sprintf (bufp
, "%d", i
);
1929 bufp
+= strlen (bufp
);
1932 f
= floor (d
* 1000000.0);
1933 sprintf (bufp
, "%06d", f
);
1938 c_strtod (const char *s
)
1944 /* leading whitespace */
1945 while (*s
&& (*s
== ' ' || *s
== '\t'))
1957 /* integer portion */
1958 while (*s
>= '0' && *s
<= '9')
1965 /* fractional portion */
1970 while (*s
>= '0' && *s
<= '9')
1972 rv
+= (*s
- '0') * scale
;
1979 if (*s
== 'E' || *s
== 'e')
1982 if (sscanf (s
+ 1, "%d", &e
) == 1)
2003 r_delete_element (DataType
* data
, ElementType
* element
)
2005 r_delete_entry (data
->element_tree
, (BoxType
*) element
);
2008 r_delete_entry (data
->pin_tree
, (BoxType
*) pin
);
2013 r_delete_entry (data
->pad_tree
, (BoxType
*) pad
);
2016 ELEMENTTEXT_LOOP (element
);
2018 r_delete_entry (data
->name_tree
[n
], (BoxType
*) text
);
2024 /* ---------------------------------------------------------------------------
2025 * Returns a string that has a bunch of information about the program.
2026 * Can be used for things like "about" dialog boxes.
2030 GetInfoString (void)
2034 static DynamicStringType info
;
2035 static int first_time
= 1;
2042 DSAddString (&info
, "This is PCB, an interactive\n");
2043 DSAddString (&info
, "printed circuit board editor\n");
2044 DSAddString (&info
, "version ");
2045 DSAddString (&info
, VERSION
);
2046 DSAddString (&info
, "\n\n");
2047 DSAddString (&info
, "Compiled on " __DATE__
" at " __TIME__
);
2048 DSAddString (&info
, "\n\n" "by harry eaton\n\n");
2050 "Copyright (C) Thomas Nau 1994, 1995, 1996, 1997\n");
2051 DSAddString (&info
, "Copyright (C) harry eaton 1998-2007\n");
2052 DSAddString (&info
, "Copyright (C) C. Scott Ananian 2001\n");
2054 "Copyright (C) DJ Delorie 2003, 2004, 2005, 2006, 2007, 2008\n");
2056 "Copyright (C) Dan McMahill 2003, 2004, 2005, 2006, 2007, 2008\n\n");
2057 DSAddString (&info
, "It is licensed under the terms of the GNU\n");
2058 DSAddString (&info
, "General Public License version 2\n");
2059 DSAddString (&info
, "See the LICENSE file for more information\n\n");
2060 DSAddString (&info
, "For more information see:\n\n");
2061 DSAddString (&info
, "PCB homepage: http://pcb.sf.net\n");
2062 DSAddString (&info
, "gEDA homepage: http://www.geda.seul.org\n");
2064 "gEDA Wiki: http://geda.seul.org/dokuwiki/doku.php?id=geda\n\n");
2066 DSAddString (&info
, "----- Compile Time Options -----\n");
2067 hids
= hid_enumerate ();
2068 DSAddString (&info
, "GUI:\n");
2069 for (i
= 0; hids
[i
]; i
++)
2073 DSAddString (&info
, TAB
);
2074 DSAddString (&info
, hids
[i
]->name
);
2075 DSAddString (&info
, " : ");
2076 DSAddString (&info
, hids
[i
]->description
);
2077 DSAddString (&info
, "\n");
2081 DSAddString (&info
, "Exporters:\n");
2082 for (i
= 0; hids
[i
]; i
++)
2084 if (hids
[i
]->exporter
)
2086 DSAddString (&info
, TAB
);
2087 DSAddString (&info
, hids
[i
]->name
);
2088 DSAddString (&info
, " : ");
2089 DSAddString (&info
, hids
[i
]->description
);
2090 DSAddString (&info
, "\n");
2094 DSAddString (&info
, "Printers:\n");
2095 for (i
= 0; hids
[i
]; i
++)
2097 if (hids
[i
]->printer
)
2099 DSAddString (&info
, TAB
);
2100 DSAddString (&info
, hids
[i
]->name
);
2101 DSAddString (&info
, " : ");
2102 DSAddString (&info
, hids
[i
]->description
);
2103 DSAddString (&info
, "\n");