Applied patch from Tomaz Solc fixing a bug in the CenterDisplay function.
[geda-pcb/gde.git] / src / misc.c
blob65dfc0442c143526a73ea6294ebbdbdc44fd3d9f
1 /* $Id$ */
3 /*
4 * COPYRIGHT
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
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
37 #include <stdlib.h>
38 #include <stdarg.h>
39 #ifdef HAVE_STRING_H
40 #include <string.h>
41 #endif
42 #include <memory.h>
43 #include <ctype.h>
44 #include <signal.h>
45 #include <sys/param.h>
46 #include <sys/stat.h>
47 #include <sys/types.h>
48 #include <math.h>
49 #ifdef HAVE_UNISTD_H
50 #include <unistd.h>
51 #endif
52 #ifdef HAVE_PWD_H
53 #include <pwd.h>
54 #endif
56 #include "global.h"
58 #include "crosshair.h"
59 #include "create.h"
60 #include "data.h"
61 #include "draw.h"
62 #include "file.h"
63 #include "error.h"
64 #include "mymem.h"
65 #include "misc.h"
66 #include "move.h"
67 #include "polygon.h"
68 #include "remove.h"
69 #include "rtree.h"
70 #include "rotate.h"
71 #include "rubberband.h"
72 #include "search.h"
73 #include "set.h"
74 #include "undo.h"
75 #include "action.h"
77 #ifdef HAVE_LIBDMALLOC
78 #include <dmalloc.h>
79 #endif
81 RCSID ("$Id$");
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 *);
91 /* Local variables */
93 /*
94 * Used by SaveStackAndVisibility() and
95 * RestoreStackAndVisibility()
98 static struct
100 Boolean ElementOn, InvisibleObjectsOn, PinOn, ViaOn, RatOn;
101 int LayerStack[MAX_LAYER];
102 Boolean LayerOn[MAX_LAYER];
103 int cnt;
104 } SavedStack;
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
109 float
110 GetValue (char *val, char *units, Boolean * absolute)
112 float value;
114 /* if the first character is a sign we have to add the
115 * value to the current one
117 if (*val == '=')
119 *absolute = True;
120 value = atof (val + 1);
122 else
124 if (isdigit ((int) *val))
125 *absolute = True;
126 else
127 *absolute = False;
128 value = atof (val);
130 if (units && *units)
132 if (strncasecmp (units, "mm", 2) == 0)
133 value *= MM_TO_COOR;
134 else if (strncasecmp (units, "mil", 3) == 0)
135 value *= 100;
137 return value;
140 /* ---------------------------------------------------------------------------
141 * sets the bounding box of a point (which is silly)
143 void
144 SetPointBoundingBox (PointTypePtr Pnt)
146 Pnt->X2 = Pnt->X;
147 Pnt->Y2 = Pnt->Y;
150 /* ---------------------------------------------------------------------------
151 * sets the bounding box of a pin or via
153 void
154 SetPinBoundingBox (PinTypePtr Pin)
156 BDimension width;
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
172 void
173 SetPadBoundingBox (PadTypePtr Pad)
175 BDimension width;
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
191 void
192 SetLineBoundingBox (LineTypePtr Line)
194 BDimension width;
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
209 void
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);
221 END_LOOP;
224 /* ---------------------------------------------------------------------------
225 * sets the bounding box of an elements
227 void
228 SetElementBoundingBox (DataTypePtr Data, ElementTypePtr Element,
229 FontTypePtr Font)
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);
243 if (Data)
244 r_insert_entry (Data->name_tree[n], (BoxType *) text, 0);
246 END_LOOP;
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);
267 END_LOOP;
268 ARC_LOOP (Element);
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);
276 END_LOOP;
277 *vbox = *box;
278 PIN_LOOP (Element);
280 if (Data && Data->pin_tree)
281 r_delete_entry (Data->pin_tree, (BoxType *) pin);
282 SetPinBoundingBox (pin);
283 if (Data)
285 if (!Data->pin_tree)
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);
298 END_LOOP;
299 PAD_LOOP (Element);
301 if (Data && Data->pad_tree)
302 r_delete_entry (Data->pad_tree, (BoxType *) pad);
303 SetPadBoundingBox (pad);
304 if (Data)
306 if (!Data->pad_tree)
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);
314 MAKEMIN (vbox->X1,
315 MIN (pad->Point1.X, pad->Point2.X) - pad->Thickness / 2);
316 MAKEMIN (vbox->Y1,
317 MIN (pad->Point1.Y, pad->Point2.Y) - pad->Thickness / 2);
318 MAKEMAX (vbox->X2,
319 MAX (pad->Point1.X, pad->Point2.X) + pad->Thickness / 2);
320 MAKEMAX (vbox->Y2,
321 MAX (pad->Point1.Y, pad->Point2.Y) + pad->Thickness / 2);
323 END_LOOP;
324 /* now we set the EDGE2FLAG of the pad if Point2
325 * is closer to the outside edge than Point1
327 PAD_LOOP (Element);
329 if (pad->Point1.Y == pad->Point2.Y)
331 /* horizontal pad */
332 if (box->X2 - pad->Point2.X < pad->Point1.X - box->X1)
333 SET_FLAG (EDGE2FLAG, pad);
334 else
335 CLEAR_FLAG (EDGE2FLAG, pad);
337 else
339 /* vertical pad */
340 if (box->Y2 - pad->Point2.Y < pad->Point1.Y - box->Y1)
341 SET_FLAG (EDGE2FLAG, pad);
342 else
343 CLEAR_FLAG (EDGE2FLAG, pad);
346 END_LOOP;
348 /* mark pins with component orientation */
349 if ((box->X2 - box->X1) > (box->Y2 - box->Y1))
351 PIN_LOOP (Element);
353 SET_FLAG (EDGE2FLAG, pin);
355 END_LOOP;
357 else
359 PIN_LOOP (Element);
361 CLEAR_FLAG (EDGE2FLAG, pin);
363 END_LOOP;
365 if (Data && !Data->element_tree)
366 Data->element_tree = r_create_tree (NULL, 0, 0);
367 if (Data)
368 r_insert_entry (Data->element_tree, box, 0);
371 /* ---------------------------------------------------------------------------
372 * creates the bounding box of a text object
374 void
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;
381 int i;
383 /* calculate size of the bounding box */
384 for (; s && *s; s++)
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);
394 else
396 width +=
397 ((FontPtr->DefaultSymbol.X2 - FontPtr->DefaultSymbol.X1) * 6 / 5);
398 height = (FontPtr->DefaultSymbol.Y2 - FontPtr->DefaultSymbol.Y1);
401 /* scale values */
402 width *= Text->Scale / 100.;
403 height *= Text->Scale / 100.;
404 maxThick *= Text->Scale / 200.;
405 if (maxThick < 400)
406 maxThick = 400;
408 /* set upper-left and lower-right corner;
409 * swap coordinates if necessary (origin is already in 'swapped')
410 * and rotate box
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);
425 else
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
439 Boolean
440 IsDataEmpty (DataTypePtr Data)
442 Boolean hasNoObjects;
443 Cardinal i;
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
469 BoxTypePtr
470 GetDataBoundingBox (DataTypePtr Data)
472 static BoxType box;
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 */
479 VIA_LOOP (Data);
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);
486 END_LOOP;
487 ELEMENT_LOOP (Data);
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);
501 END_LOOP;
502 ALLLINE_LOOP (Data);
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);
513 ENDALL_LOOP;
514 ALLARC_LOOP (Data);
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);
521 ENDALL_LOOP;
522 ALLTEXT_LOOP (Data);
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);
529 ENDALL_LOOP;
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);
537 ENDALL_LOOP;
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
545 * coordinates
547 void
548 CenterDisplay (LocationType X, LocationType Y, Boolean Delta)
550 int save_grid = PCB->Grid;
551 PCB->Grid = 1;
552 if (Delta)
554 MoveCrosshairRelative (X, Y);
556 else
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
572 void
573 SetFontInfo (FontTypePtr Ptr)
575 Cardinal i, j;
576 SymbolTypePtr symbol;
577 LineTypePtr line;
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)
592 continue;
594 minx = miny = MAX_COORD;
595 maxx = maxy = 0;
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++)
624 if (symbol->Valid)
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;
637 static void
638 GetNum (char **s, BDimension * num)
640 *num = atoi (*s);
641 while (isdigit ((int) **s))
642 (*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)
654 int i, style;
655 char Name[256];
657 memset (routeStyle, 0, NUM_STYLES * sizeof (RouteStyleType));
658 for (style = 0; style < NUM_STYLES; style++, routeStyle++)
660 while (*s && isspace ((int) *s))
661 s++;
662 for (i = 0; *s && *s != ','; i++)
663 Name[i] = *s++;
664 Name[i] = '\0';
665 routeStyle->Name = MyStrdup (Name, "ParseRouteString()");
666 if (!isdigit ((int) *++s))
667 goto error;
668 GetNum (&s, &routeStyle->Thick);
669 routeStyle->Thick *= scale;
670 while (*s && isspace ((int) *s))
671 s++;
672 if (*s++ != ',')
673 goto error;
674 while (*s && isspace ((int) *s))
675 s++;
676 if (!isdigit ((int) *s))
677 goto error;
678 GetNum (&s, &routeStyle->Diameter);
679 routeStyle->Diameter *= scale;
680 while (*s && isspace ((int) *s))
681 s++;
682 if (*s++ != ',')
683 goto error;
684 while (*s && isspace ((int) *s))
685 s++;
686 if (!isdigit ((int) *s))
687 goto error;
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. */
692 if (*s != ',')
693 routeStyle->Keepaway = 1000;
694 else
696 s++;
697 while (*s && isspace ((int) *s))
698 s++;
699 if (!isdigit ((int) *s))
700 goto error;
701 GetNum (&s, &routeStyle->Keepaway);
702 routeStyle->Keepaway *= scale;
703 while (*s && isspace ((int) *s))
704 s++;
706 if (style < NUM_STYLES - 1)
708 while (*s && isspace ((int) *s))
709 s++;
710 if (*s++ != ':')
711 goto error;
714 return (0);
716 error:
717 memset (routeStyle, 0, NUM_STYLES * sizeof (RouteStyleType));
718 return (1);
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];
733 /* clear struct */
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))
744 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))
751 s++;
752 switch (*s)
754 case 'c':
755 case 'C':
756 layer = LayerN + COMPONENT_LAYER;
757 c_set = True;
758 break;
760 case 's':
761 case 'S':
762 layer = LayerN + SOLDER_LAYER;
763 s_set = True;
764 break;
766 default:
767 if (!isdigit ((int) *s))
768 goto error;
769 layer = atoi (s) - 1;
770 break;
772 if (layer > LayerN + MAX (SOLDER_LAYER, COMPONENT_LAYER) ||
773 member >= LayerN + 1)
774 goto error;
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))
781 s++;
782 if (!*s || *s == ':')
783 break;
784 if (*s != ',')
785 goto error;
787 LayerGroup->Number[group] = member;
788 if (*s == ':')
789 s++;
791 if (!s_set)
792 LayerGroup->Entries[SOLDER_LAYER][LayerGroup->Number[SOLDER_LAYER]++] =
793 LayerN + SOLDER_LAYER;
794 if (!c_set)
795 LayerGroup->
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;
804 group++;
806 return (0);
808 /* reset structure on error */
809 error:
810 memset (LayerGroup, 0, sizeof (LayerGroupType));
811 return (1);
814 /* ---------------------------------------------------------------------------
815 * quits application
817 void
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)
826 EmergencySave ();
827 else
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)
837 exit (0);
838 else
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
847 char *
848 EvaluateFilename (char *Template, char *Path, char *Filename, char *Parameter)
850 static DynamicStringType command;
851 char *p;
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 */
867 if (*p == '%'
868 && (*(p + 1) == 'f' || *(p + 1) == 'p' || *(p + 1) == 'a'))
869 switch (*(++p))
871 case 'a':
872 DSAddString (&command, Parameter);
873 break;
874 case 'f':
875 DSAddString (&command, Filename);
876 break;
877 case 'p':
878 DSAddString (&command, Path);
879 break;
881 else
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
895 char *
896 ExpandFilename (char *Dirname, char *Filename)
898 static DynamicStringType answer;
899 char *command;
900 FILE *pipe;
901 int c;
903 /* allocate memory for commandline and build it */
904 DSClearString (&answer);
905 if (Dirname)
907 command = MyCalloc (strlen (Filename) + strlen (Dirname) + 7,
908 sizeof (char), "ExpandFilename()");
909 sprintf (command, "echo %s/%s", Dirname, Filename);
911 else
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 */
921 for (;;)
923 if ((c = fgetc (pipe)) == EOF || c == '\n' || c == '\r')
924 break;
925 else
926 DSAddCharacter (&answer, c);
929 SaveFree (command);
930 return (pclose (pipe) ? NULL : answer.Data);
933 /* couldn't be expanded by the shell */
934 PopenErrorMessage (command);
935 SaveFree (command);
936 return (NULL);
940 /* ---------------------------------------------------------------------------
941 * returns the layer number for the passed pointer
944 GetLayerNumber (DataTypePtr Data, LayerTypePtr Layer)
946 int i;
948 for (i = 0; i < MAX_LAYER + 2; i++)
949 if (Layer == &Data->Layer[i])
950 break;
951 return (i);
954 /* ---------------------------------------------------------------------------
955 * move layer (number is passed in) to top of layerstack
957 static void
958 PushOnTopOfLayerStack (int NewTop)
960 int i;
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)
968 break;
970 /* bring this element to the top of the stack */
971 for (; i; i--)
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
988 | thinks the are.
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);
1009 changed++;
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");
1020 return (changed);
1023 /* ----------------------------------------------------------------------
1024 * Given a string description of a layer stack, adjust the layer stack
1025 * to correspond.
1028 void
1029 LayerStringToLayerStack (char *s)
1031 static int listed_layers = 0;
1032 int l = strlen (s);
1033 char **args;
1034 int i, argn, lno;
1035 int prev_sep = 1;
1037 s = strdup (s);
1038 args = (char **) malloc (l * sizeof (char *));
1039 argn = 0;
1041 for (i=0; i<l; i++)
1043 switch (s[i])
1045 case ' ':
1046 case '\t':
1047 case ',':
1048 case ';':
1049 case ':':
1050 prev_sep = 1;
1051 s[i] = '\0';
1052 break;
1053 default:
1054 if (prev_sep)
1055 args[argn++] = s+i;
1056 prev_sep = 0;
1057 break;
1061 for (i = 0; i < max_layer + 2; i++)
1063 if (i < max_layer)
1064 LayerStack[i] = i;
1065 PCB->Data->Layer[i].On = False;
1067 PCB->ElementOn = False;
1068 PCB->InvisibleObjectsOn = False;
1069 PCB->PinOn = False;
1070 PCB->ViaOn = False;
1071 PCB->RatOn = False;
1073 for (i=argn-1; i>=0; i--)
1075 if (strcasecmp (args[i], "rats") == 0)
1076 PCB->RatOn = True;
1077 else if (strcasecmp (args[i], "invisible") == 0)
1078 PCB->InvisibleObjectsOn = True;
1079 else if (strcasecmp (args[i], "pins") == 0)
1080 PCB->PinOn = True;
1081 else if (strcasecmp (args[i], "vias") == 0)
1082 PCB->ViaOn = True;
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);
1090 else
1092 int found = 0;
1093 for (lno = 0; lno < max_layer; lno++)
1094 if (strcasecmp (args[i], PCB->Data->Layer[lno].Name) == 0)
1096 ChangeGroupVisibility (lno, True, True);
1097 found = 1;
1098 break;
1100 if (!found)
1102 fprintf(stderr, "Warning: layer \"%s\" not known\n", args[i]);
1103 if (!listed_layers)
1105 fprintf (stderr, "Named layers in this board are:\n");
1106 listed_layers = 1;
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)
1122 int group, i;
1124 if (Layer == max_layer)
1125 return (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)
1129 return (group);
1130 return (max_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)
1149 int group, entry;
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)
1154 return (group);
1156 /* since every layer belongs to a group it is safe to return
1157 * the value without boundary checking
1159 return (group);
1162 /* ---------------------------------------------------------------------------
1163 * returns a pointer to an objects bounding box;
1164 * data is valid until the routine is called again
1166 BoxTypePtr
1167 GetObjectBoundingBox (int Type, void *Ptr1, void *Ptr2, void *Ptr3)
1169 static BoxType box;
1171 switch (Type)
1173 case VIA_TYPE:
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;
1181 break;
1184 case LINE_TYPE:
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;
1196 break;
1199 case ARC_TYPE:
1200 box = ((ArcTypePtr) Ptr2)->BoundingBox;
1201 break;
1203 case TEXT_TYPE:
1204 case ELEMENTNAME_TYPE:
1205 box = ((TextTypePtr) Ptr2)->BoundingBox;
1206 break;
1208 case POLYGON_TYPE:
1209 box = ((PolygonTypePtr) Ptr2)->BoundingBox;
1210 break;
1212 case ELEMENT_TYPE:
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;
1225 break;
1227 case PAD_TYPE:
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;
1239 break;
1242 case PIN_TYPE:
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;
1250 break;
1253 case LINEPOINT_TYPE:
1255 PointTypePtr point = (PointTypePtr) Ptr3;
1257 box.X1 = box.X2 = point->X;
1258 box.Y1 = box.Y2 = point->Y;
1259 break;
1262 case POLYGONPOINT_TYPE:
1264 PointTypePtr point = (PointTypePtr) Ptr3;
1266 box.X1 = box.X2 = point->X;
1267 box.Y1 = box.Y2 = point->Y;
1268 break;
1271 default:
1272 Message ("Request for bounding box of unsupported type %d\n", Type);
1273 box.X1 = box.X2 = box.Y1 = box.Y2 = 0;
1274 break;
1276 return (&box);
1279 /* ---------------------------------------------------------------------------
1280 * computes the bounding box of an arc
1282 void
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)
1291 Arc->Delta = 360;
1292 ang1 = Arc->StartAngle;
1293 ang2 = Arc->StartAngle + Arc->Delta;
1294 if (Arc->Delta < 0)
1296 LocationType temp;
1297 temp = ang1;
1298 ang1 = ang2;
1299 ang2 = temp;
1301 if (ang1 < 0)
1303 ang1 += 360;
1304 ang2 += 360;
1306 /* calculate sines, cosines */
1307 switch (ang1)
1309 case 0:
1310 ca1 = 1.0;
1311 sa1 = 0;
1312 break;
1313 case 90:
1314 ca1 = 0;
1315 sa1 = 1.0;
1316 break;
1317 case 180:
1318 ca1 = -1.0;
1319 sa1 = 0;
1320 break;
1321 case 270:
1322 ca1 = 0;
1323 sa1 = -1.0;
1324 break;
1325 default:
1326 ca1 = M180 * (double) ang1;
1327 sa1 = sin (ca1);
1328 ca1 = cos (ca1);
1330 switch (ang2)
1332 case 0:
1333 ca2 = 1.0;
1334 sa2 = 0;
1335 break;
1336 case 90:
1337 ca2 = 0;
1338 sa2 = 1.0;
1339 break;
1340 case 180:
1341 ca2 = -1.0;
1342 sa2 = 0;
1343 break;
1344 case 270:
1345 ca2 = 0;
1346 sa2 = -1.0;
1347 break;
1348 default:
1349 ca2 = M180 * (double) ang2;
1350 sa2 = sin (ca2);
1351 ca2 = cos (ca2);
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
1376 void
1377 ResetStackAndVisibility (void)
1379 int comp_group;
1380 Cardinal i;
1382 for (i = 0; i < max_layer + 2; i++)
1384 if (i < max_layer)
1385 LayerStack[i] = i;
1386 PCB->Data->Layer[i].On = True;
1388 PCB->ElementOn = True;
1389 PCB->InvisibleObjectsOn = True;
1390 PCB->PinOn = True;
1391 PCB->ViaOn = True;
1392 PCB->RatOn = 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
1402 void
1403 SaveStackAndVisibility (void)
1405 Cardinal i;
1406 static Boolean run = False;
1408 if (run == False)
1410 SavedStack.cnt = 0;
1411 run = True;
1414 if (SavedStack.cnt != 0)
1416 fprintf (stderr,
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++)
1423 if (i < max_layer)
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;
1432 SavedStack.cnt++;
1435 /* ---------------------------------------------------------------------------
1436 * restores the layerstack setting
1438 void
1439 RestoreStackAndVisibility (void)
1441 Cardinal i;
1443 if (SavedStack.cnt == 0)
1445 fprintf (stderr, "RestoreStackAndVisibility() layerstack has not"
1446 " been saved. cnt = %d\n", SavedStack.cnt);
1447 return;
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++)
1457 if (i < max_layer)
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;
1467 SavedStack.cnt--;
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'
1475 char *
1476 GetWorkingDirectory (char *path)
1478 #ifdef HAVE_GETCWD
1479 return getcwd (path, MAXPATHLEN);
1480 #else
1481 /* seems that some BSD releases lack of a prototype for getwd() */
1482 return getwd (path);
1483 #endif
1487 /* ---------------------------------------------------------------------------
1488 * writes a string to the passed file pointer
1489 * some special characters are quoted
1491 void
1492 CreateQuotedString (DynamicStringTypePtr DS, char *S)
1494 DSClearString (DS);
1495 DSAddCharacter (DS, '"');
1496 while (*S)
1498 if (*S == '"' || *S == '\\')
1499 DSAddCharacter (DS, '\\');
1500 DSAddCharacter (DS, *S++);
1502 DSAddCharacter (DS, '"');
1506 static void
1507 RightAngles (int Angle, float *cosa, float *sina)
1509 *cosa = (float) cos ((double) Angle * M180);
1510 *sina = (float) sin ((double) Angle * M180);
1513 BoxTypePtr
1514 GetArcEnds (ArcTypePtr Arc)
1516 static BoxType box;
1517 float ca, sa;
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;
1525 return (&box);
1529 /* doesn't this belong in change.c ?? */
1530 void
1531 ChangeArcAngles (LayerTypePtr Layer, ArcTypePtr a,
1532 long int new_sa, long int new_da)
1534 if (new_da >= 360)
1536 new_da = 360;
1537 new_sa = 0;
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;
1543 a->Delta = new_da;
1544 SetArcBoundingBox (a);
1545 r_insert_entry (Layer->arc_tree, (BoxTypePtr) a, 0);
1546 ClearFromPolygon (PCB->Data, ARC_TYPE, Layer, a);
1549 static char *
1550 BumpName (char *Name)
1552 int num;
1553 char c, *start;
1554 static char temp[256];
1556 start = Name;
1557 /* seek end of string */
1558 while (*Name != 0)
1559 Name++;
1560 /* back up to potential number */
1561 for (Name--; isdigit ((int) *Name); Name--);
1562 Name++;
1563 if (*Name)
1564 num = atoi (Name) + 1;
1565 else
1566 num = 1;
1567 c = *Name;
1568 *Name = 0;
1569 sprintf (temp, "%s%d", start, num);
1570 /* if this is not our string, put back the blown character */
1571 if (start != temp)
1572 *Name = c;
1573 return (temp);
1577 * make a unique name for the name on board
1578 * this can alter the contents of the input string
1580 char *
1581 UniqueElementName (DataTypePtr Data, char *Name)
1583 Boolean unique = True;
1584 /* null strings are ok */
1585 if (!Name || !*Name)
1586 return (Name);
1588 for (;;)
1590 ELEMENT_LOOP (Data);
1592 if (NAMEONPCB_NAME (element) &&
1593 NSTRCMP (NAMEONPCB_NAME (element), Name) == 0)
1595 Name = BumpName (Name);
1596 unique = False;
1597 break;
1600 END_LOOP;
1601 if (unique)
1602 return (Name);
1603 unique = True;
1607 static void
1608 GetGridLockCoordinates (int type, void *ptr1,
1609 void *ptr2, void *ptr3, LocationType * x,
1610 LocationType * y)
1612 switch (type)
1614 case VIA_TYPE:
1615 *x = ((PinTypePtr) ptr2)->X;
1616 *y = ((PinTypePtr) ptr2)->Y;
1617 break;
1618 case LINE_TYPE:
1619 *x = ((LineTypePtr) ptr2)->Point1.X;
1620 *y = ((LineTypePtr) ptr2)->Point1.Y;
1621 break;
1622 case TEXT_TYPE:
1623 case ELEMENTNAME_TYPE:
1624 *x = ((TextTypePtr) ptr2)->X;
1625 *y = ((TextTypePtr) ptr2)->Y;
1626 break;
1627 case ELEMENT_TYPE:
1628 *x = ((ElementTypePtr) ptr2)->MarkX;
1629 *y = ((ElementTypePtr) ptr2)->MarkY;
1630 break;
1631 case POLYGON_TYPE:
1632 *x = ((PolygonTypePtr) ptr2)->Points[0].X;
1633 *y = ((PolygonTypePtr) ptr2)->Points[0].Y;
1634 break;
1636 case LINEPOINT_TYPE:
1637 case POLYGONPOINT_TYPE:
1638 *x = ((PointTypePtr) ptr3)->X;
1639 *y = ((PointTypePtr) ptr3)->Y;
1640 break;
1641 case ARC_TYPE:
1643 BoxTypePtr box;
1645 box = GetArcEnds ((ArcTypePtr) ptr2);
1646 *x = box->X1;
1647 *y = box->Y1;
1648 break;
1653 void
1654 AttachForCopy (LocationType PlaceX, LocationType PlaceY)
1656 BoxTypePtr box;
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)
1711 FILE *f;
1712 f = fopen (name, "r");
1713 if (f)
1715 fclose (f);
1716 return 1;
1718 return 0;
1721 char *
1722 Concat (const char *first, ...)
1724 char *rv;
1725 int len;
1726 va_list a;
1728 len = strlen (first);
1729 rv = (char *) malloc (len + 1);
1730 strcpy (rv, first);
1732 va_start (a, first);
1733 while (1)
1735 const char *s = va_arg (a, const char *);
1736 if (!s)
1737 break;
1738 len += strlen (s);
1739 rv = (char *) realloc (rv, len + 1);
1740 strcat (rv, s);
1742 va_end (a);
1743 return rv;
1747 mem_any_set (unsigned char *ptr, int bytes)
1749 while (bytes--)
1750 if (*ptr++)
1751 return 1;
1752 return 0;
1755 /* This just fills in a FlagType with current flags. */
1756 FlagType
1757 MakeFlags (unsigned int flags)
1759 FlagType rv;
1760 memset (&rv, 0, sizeof (rv));
1761 rv.f = flags;
1762 return rv;
1765 /* This converts old flag bits (from saved PCB files) to new format. */
1766 FlagType
1767 OldFlags (unsigned int flags)
1769 FlagType rv;
1770 int i, f;
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;
1774 f = 0x10000;
1775 for (i = 0; i < 8; i++)
1777 /* use the closest thing to the old thermal style */
1778 if (flags & f)
1779 rv.t[i / 2] = (1 << (4 * (i % 2)));
1780 f <<= 1;
1782 return rv;
1785 FlagType
1786 AddFlags (FlagType flag, unsigned int flags)
1788 flag.f |= flags;
1789 return flag;
1792 FlagType
1793 MaskFlags (FlagType flag, unsigned int flags)
1795 flag.f &= ~flags;
1796 return flag;
1799 /***********************************************************************
1800 * Layer Group Functions
1804 MoveLayerToGroup (int layer, int group)
1806 int prev, i, j;
1808 if (layer < 0 || layer > max_layer + 1)
1809 return -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))
1816 return prev;
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;
1828 return group;
1831 char *
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];
1837 #endif
1838 char *cp = buf;
1839 char sep = 0;
1840 int group, entry;
1841 for (group = 0; group < max_layer; group++)
1842 if (PCB->LayerGroups.Number[group])
1844 if (sep)
1845 *cp++ = ':';
1846 sep = 1;
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)
1852 *cp++ = 'c';
1854 else if (layer == max_layer + SOLDER_LAYER)
1856 *cp++ = 's';
1858 else
1860 sprintf (cp, "%d", layer + 1);
1861 while (*++cp)
1864 if (entry != PCB->LayerGroups.Number[group] - 1)
1865 *cp++ = ',';
1868 *cp++ = 0;
1869 return buf;
1872 char *
1873 pcb_author (void)
1875 #ifdef HAVE_GETPWUID
1876 static struct passwd *pwentry;
1877 static char *fab_author = 0;
1879 if (!fab_author)
1881 if (Settings.FabAuthor && Settings.FabAuthor[0])
1882 fab_author = Settings.FabAuthor;
1883 else
1885 int len;
1886 char *comma, *gecos;
1888 /* ID the user. */
1889 pwentry = getpwuid (getuid ());
1890 gecos = pwentry->pw_gecos;
1891 comma = strchr (gecos, ',');
1892 if (comma)
1893 len = comma - gecos;
1894 else
1895 len = strlen (gecos);
1896 fab_author = malloc (len + 1);
1897 if (!fab_author)
1899 perror ("pcb: out of memory.\n");
1900 exit (-1);
1902 memcpy (fab_author, gecos, len);
1903 fab_author[len] = 0;
1906 return fab_author;
1907 #else
1908 return "Unknown";
1909 #endif
1913 const char *
1914 c_dtostr (double d)
1916 static char buf[100];
1917 int i, f;
1918 char *bufp = buf;
1920 if (d < 0)
1922 *bufp++ = '-';
1923 d = -d;
1925 d += 0.0000005; /* rounding */
1926 i = floor (d);
1927 d -= i;
1928 sprintf (bufp, "%d", i);
1929 bufp += strlen (bufp);
1930 *bufp++ = '.';
1932 f = floor (d * 1000000.0);
1933 sprintf (bufp, "%06d", f);
1934 return buf;
1937 double
1938 c_strtod (const char *s)
1940 double rv = 0;
1941 double sign = 1.0;
1942 double scale;
1944 /* leading whitespace */
1945 while (*s && (*s == ' ' || *s == '\t'))
1946 s++;
1948 /* optional sign */
1949 if (*s == '-')
1951 sign = -1.0;
1952 s++;
1954 else if (*s == '+')
1955 s++;
1957 /* integer portion */
1958 while (*s >= '0' && *s <= '9')
1960 rv *= 10.0;
1961 rv += *s - '0';
1962 s++;
1965 /* fractional portion */
1966 if (*s == '.')
1968 s++;
1969 scale = 0.1;
1970 while (*s >= '0' && *s <= '9')
1972 rv += (*s - '0') * scale;
1973 scale *= 0.1;
1974 s++;
1978 /* exponent */
1979 if (*s == 'E' || *s == 'e')
1981 int e;
1982 if (sscanf (s + 1, "%d", &e) == 1)
1984 scale = 1.0;
1985 while (e > 0)
1987 scale *= 10.0;
1988 e--;
1990 while (e < 0)
1992 scale *= 0.1;
1993 e++;
1995 rv *= scale;
1999 return rv * sign;
2002 void
2003 r_delete_element (DataType * data, ElementType * element)
2005 r_delete_entry (data->element_tree, (BoxType *) element);
2006 PIN_LOOP (element);
2008 r_delete_entry (data->pin_tree, (BoxType *) pin);
2010 END_LOOP;
2011 PAD_LOOP (element);
2013 r_delete_entry (data->pad_tree, (BoxType *) pad);
2015 END_LOOP;
2016 ELEMENTTEXT_LOOP (element);
2018 r_delete_entry (data->name_tree[n], (BoxType *) text);
2020 END_LOOP;
2024 /* ---------------------------------------------------------------------------
2025 * Returns a string that has a bunch of information about the program.
2026 * Can be used for things like "about" dialog boxes.
2029 char *
2030 GetInfoString (void)
2032 HID **hids;
2033 int i;
2034 static DynamicStringType info;
2035 static int first_time = 1;
2037 #define TAB " "
2039 if (first_time)
2041 first_time = 0;
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");
2049 DSAddString (&info,
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");
2053 DSAddString (&info,
2054 "Copyright (C) DJ Delorie 2003, 2004, 2005, 2006, 2007, 2008\n");
2055 DSAddString (&info,
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");
2063 DSAddString (&info,
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++)
2071 if (hids[i]->gui)
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");
2107 #undef TAB
2109 return info.Data;