select.c: Remove Draw() call from SelectConnection
[geda-pcb/leaky.git] / src / misc.c
blob2e471d04e9c258e0dc13e45cc7f603511851abbc
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 "box.h"
59 #include "crosshair.h"
60 #include "create.h"
61 #include "data.h"
62 #include "draw.h"
63 #include "file.h"
64 #include "error.h"
65 #include "mymem.h"
66 #include "misc.h"
67 #include "move.h"
68 #include "polygon.h"
69 #include "remove.h"
70 #include "rtree.h"
71 #include "rotate.h"
72 #include "rubberband.h"
73 #include "search.h"
74 #include "set.h"
75 #include "undo.h"
76 #include "action.h"
78 #ifdef HAVE_LIBDMALLOC
79 #include <dmalloc.h>
80 #endif
82 RCSID ("$Id$");
85 /* forward declarations */
86 static char *BumpName (char *);
87 static void RightAngles (int, float *, float *);
88 static void GetGridLockCoordinates (int, void *, void *, void *,
89 LocationType *, LocationType *);
92 /* Local variables */
94 /*
95 * Used by SaveStackAndVisibility() and
96 * RestoreStackAndVisibility()
99 static struct
101 bool ElementOn, InvisibleObjectsOn, PinOn, ViaOn, RatOn;
102 int LayerStack[MAX_LAYER];
103 bool LayerOn[MAX_LAYER];
104 int cnt;
105 } SavedStack;
107 /* Get Value returns a numeric value passed from the string and sets the
108 * bool variable absolute to false if it leads with a +/- character
110 float
111 GetValue (const char *val, const char *units, bool * absolute)
113 double value;
114 int n = -1;
116 /* if the first character is a sign we have to add the
117 * value to the current one
119 if (*val == '=')
121 *absolute = true;
122 sscanf (val+1, "%lf%n", &value, &n);
123 n++;
125 else
127 if (isdigit ((int) *val))
128 *absolute = true;
129 else
130 *absolute = false;
131 sscanf (val, "%lf%n", &value, &n);
133 if (!units && n > 0)
134 units = val + n;
136 if (units && *units)
138 if (strncasecmp (units, "mm", 2) == 0)
139 value *= MM_TO_COOR;
140 else if (strncasecmp (units, "cm", 2) == 0)
141 value *= MM_TO_COOR * 10;
142 else if (strncasecmp (units, "mil", 3) == 0)
143 value *= 100;
144 else if (strncasecmp (units, "in", 3) == 0)
145 value *= 100000;
147 return value;
150 /* ---------------------------------------------------------------------------
151 * sets the bounding box of a point (which is silly)
153 void
154 SetPointBoundingBox (PointTypePtr Pnt)
156 Pnt->X2 = Pnt->X + 1;
157 Pnt->Y2 = Pnt->Y + 1;
160 /* ---------------------------------------------------------------------------
161 * sets the bounding box of a pin or via
163 void
164 SetPinBoundingBox (PinTypePtr Pin)
166 BDimension width;
168 /* the bounding box covers the extent of influence
169 * so it must include the clearance values too
171 width = (Pin->Clearance + Pin->Thickness + 1) / 2;
172 width = MAX (width, (Pin->Mask + 1) / 2);
173 Pin->BoundingBox.X1 = Pin->X - width;
174 Pin->BoundingBox.Y1 = Pin->Y - width;
175 Pin->BoundingBox.X2 = Pin->X + width;
176 Pin->BoundingBox.Y2 = Pin->Y + width;
177 close_box(&Pin->BoundingBox);
180 /* ---------------------------------------------------------------------------
181 * sets the bounding box of a pad
183 void
184 SetPadBoundingBox (PadTypePtr Pad)
186 BDimension width;
187 BDimension deltax;
188 BDimension deltay;
190 /* the bounding box covers the extent of influence
191 * so it must include the clearance values too
193 width = (Pad->Thickness + Pad->Clearance + 1) / 2;
194 width = MAX (width, (Pad->Mask + 1) / 2);
195 deltax = Pad->Point2.X - Pad->Point1.X;
196 deltay = Pad->Point2.Y - Pad->Point1.Y;
198 if (TEST_FLAG (SQUAREFLAG, Pad) && deltax != 0 && deltay != 0)
200 /* slanted square pad */
201 float tx, ty, theta;
202 BDimension btx, bty;
204 theta = atan2 (deltay, deltax);
206 /* T is a vector half a thickness long, in the direction of
207 one of the corners. */
208 tx = width * cos (theta + M_PI/4) * sqrt(2.0);
209 ty = width * sin (theta + M_PI/4) * sqrt(2.0);
211 /* cast back to this integer type */
212 btx = tx;
213 bty = ty;
215 Pad->BoundingBox.X1 = MIN (MIN (Pad->Point1.X - btx, Pad->Point1.X - bty),
216 MIN (Pad->Point2.X + btx, Pad->Point2.X + bty));
217 Pad->BoundingBox.X2 = MAX (MAX (Pad->Point1.X - btx, Pad->Point1.X - bty),
218 MAX (Pad->Point2.X + btx, Pad->Point2.X + bty));
219 Pad->BoundingBox.Y1 = MIN (MIN (Pad->Point1.Y + btx, Pad->Point1.Y - bty),
220 MIN (Pad->Point2.Y - btx, Pad->Point2.Y + bty));
221 Pad->BoundingBox.Y2 = MAX (MAX (Pad->Point1.Y + btx, Pad->Point1.Y - bty),
222 MAX (Pad->Point2.Y - btx, Pad->Point2.Y + bty));
224 else
226 Pad->BoundingBox.X1 = MIN (Pad->Point1.X, Pad->Point2.X) - width;
227 Pad->BoundingBox.X2 = MAX (Pad->Point1.X, Pad->Point2.X) + width;
228 Pad->BoundingBox.Y1 = MIN (Pad->Point1.Y, Pad->Point2.Y) - width;
229 Pad->BoundingBox.Y2 = MAX (Pad->Point1.Y, Pad->Point2.Y) + width;
231 close_box(&Pad->BoundingBox);
234 /* ---------------------------------------------------------------------------
235 * sets the bounding box of a line
237 void
238 SetLineBoundingBox (LineTypePtr Line)
240 BDimension width;
242 width = (Line->Thickness + Line->Clearance + 1) / 2;
244 Line->BoundingBox.X1 = MIN (Line->Point1.X, Line->Point2.X) - width;
245 Line->BoundingBox.X2 = MAX (Line->Point1.X, Line->Point2.X) + width;
246 Line->BoundingBox.Y1 = MIN (Line->Point1.Y, Line->Point2.Y) - width;
247 Line->BoundingBox.Y2 = MAX (Line->Point1.Y, Line->Point2.Y) + width;
248 close_box(&Line->BoundingBox);
249 SetPointBoundingBox (&Line->Point1);
250 SetPointBoundingBox (&Line->Point2);
253 /* ---------------------------------------------------------------------------
254 * sets the bounding box of a polygons
256 void
257 SetPolygonBoundingBox (PolygonTypePtr Polygon)
259 Polygon->BoundingBox.X1 = Polygon->BoundingBox.Y1 = MAX_COORD;
260 Polygon->BoundingBox.X2 = Polygon->BoundingBox.Y2 = 0;
261 POLYGONPOINT_LOOP (Polygon);
263 MAKEMIN (Polygon->BoundingBox.X1, point->X);
264 MAKEMIN (Polygon->BoundingBox.Y1, point->Y);
265 MAKEMAX (Polygon->BoundingBox.X2, point->X);
266 MAKEMAX (Polygon->BoundingBox.Y2, point->Y);
268 /* boxes don't include the lower right corner */
269 close_box(&Polygon->BoundingBox);
270 END_LOOP;
273 /* ---------------------------------------------------------------------------
274 * sets the bounding box of an elements
276 void
277 SetElementBoundingBox (DataTypePtr Data, ElementTypePtr Element,
278 FontTypePtr Font)
280 BoxTypePtr box, vbox;
282 if (Data && Data->element_tree)
283 r_delete_entry (Data->element_tree, (BoxType *) Element);
284 /* first update the text objects */
285 ELEMENTTEXT_LOOP (Element);
287 if (Data && Data->name_tree[n])
288 r_delete_entry (Data->name_tree[n], (BoxType *) text);
289 SetTextBoundingBox (Font, text);
290 if (Data && !Data->name_tree[n])
291 Data->name_tree[n] = r_create_tree (NULL, 0, 0);
292 if (Data)
293 r_insert_entry (Data->name_tree[n], (BoxType *) text, 0);
295 END_LOOP;
297 /* do not include the elementnames bounding box which
298 * is handled separately
300 box = &Element->BoundingBox;
301 vbox = &Element->VBox;
302 box->X1 = box->Y1 = MAX_COORD;
303 box->X2 = box->Y2 = 0;
304 ELEMENTLINE_LOOP (Element);
306 SetLineBoundingBox (line);
307 MAKEMIN (box->X1, line->Point1.X - (line->Thickness + 1) / 2);
308 MAKEMIN (box->Y1, line->Point1.Y - (line->Thickness + 1) / 2);
309 MAKEMIN (box->X1, line->Point2.X - (line->Thickness + 1) / 2);
310 MAKEMIN (box->Y1, line->Point2.Y - (line->Thickness + 1) / 2);
311 MAKEMAX (box->X2, line->Point1.X + (line->Thickness + 1) / 2);
312 MAKEMAX (box->Y2, line->Point1.Y + (line->Thickness + 1) / 2);
313 MAKEMAX (box->X2, line->Point2.X + (line->Thickness + 1) / 2);
314 MAKEMAX (box->Y2, line->Point2.Y + (line->Thickness + 1) / 2);
316 END_LOOP;
317 ARC_LOOP (Element);
319 SetArcBoundingBox (arc);
320 MAKEMIN (box->X1, arc->BoundingBox.X1);
321 MAKEMIN (box->Y1, arc->BoundingBox.Y1);
322 MAKEMAX (box->X2, arc->BoundingBox.X2);
323 MAKEMAX (box->Y2, arc->BoundingBox.Y2);
325 END_LOOP;
326 *vbox = *box;
327 PIN_LOOP (Element);
329 if (Data && Data->pin_tree)
330 r_delete_entry (Data->pin_tree, (BoxType *) pin);
331 SetPinBoundingBox (pin);
332 if (Data)
334 if (!Data->pin_tree)
335 Data->pin_tree = r_create_tree (NULL, 0, 0);
336 r_insert_entry (Data->pin_tree, (BoxType *) pin, 0);
338 MAKEMIN (box->X1, pin->BoundingBox.X1);
339 MAKEMIN (box->Y1, pin->BoundingBox.Y1);
340 MAKEMAX (box->X2, pin->BoundingBox.X2);
341 MAKEMAX (box->Y2, pin->BoundingBox.Y2);
342 MAKEMIN (vbox->X1, pin->X - pin->Thickness / 2);
343 MAKEMIN (vbox->Y1, pin->Y - pin->Thickness / 2);
344 MAKEMAX (vbox->X2, pin->X + pin->Thickness / 2);
345 MAKEMAX (vbox->Y2, pin->Y + pin->Thickness / 2);
347 END_LOOP;
348 PAD_LOOP (Element);
350 if (Data && Data->pad_tree)
351 r_delete_entry (Data->pad_tree, (BoxType *) pad);
352 SetPadBoundingBox (pad);
353 if (Data)
355 if (!Data->pad_tree)
356 Data->pad_tree = r_create_tree (NULL, 0, 0);
357 r_insert_entry (Data->pad_tree, (BoxType *) pad, 0);
359 MAKEMIN (box->X1, pad->BoundingBox.X1);
360 MAKEMIN (box->Y1, pad->BoundingBox.Y1);
361 MAKEMAX (box->X2, pad->BoundingBox.X2);
362 MAKEMAX (box->Y2, pad->BoundingBox.Y2);
363 MAKEMIN (vbox->X1,
364 MIN (pad->Point1.X, pad->Point2.X) - pad->Thickness / 2);
365 MAKEMIN (vbox->Y1,
366 MIN (pad->Point1.Y, pad->Point2.Y) - pad->Thickness / 2);
367 MAKEMAX (vbox->X2,
368 MAX (pad->Point1.X, pad->Point2.X) + pad->Thickness / 2);
369 MAKEMAX (vbox->Y2,
370 MAX (pad->Point1.Y, pad->Point2.Y) + pad->Thickness / 2);
372 END_LOOP;
373 /* now we set the EDGE2FLAG of the pad if Point2
374 * is closer to the outside edge than Point1
376 PAD_LOOP (Element);
378 if (pad->Point1.Y == pad->Point2.Y)
380 /* horizontal pad */
381 if (box->X2 - pad->Point2.X < pad->Point1.X - box->X1)
382 SET_FLAG (EDGE2FLAG, pad);
383 else
384 CLEAR_FLAG (EDGE2FLAG, pad);
386 else
388 /* vertical pad */
389 if (box->Y2 - pad->Point2.Y < pad->Point1.Y - box->Y1)
390 SET_FLAG (EDGE2FLAG, pad);
391 else
392 CLEAR_FLAG (EDGE2FLAG, pad);
395 END_LOOP;
397 /* mark pins with component orientation */
398 if ((box->X2 - box->X1) > (box->Y2 - box->Y1))
400 PIN_LOOP (Element);
402 SET_FLAG (EDGE2FLAG, pin);
404 END_LOOP;
406 else
408 PIN_LOOP (Element);
410 CLEAR_FLAG (EDGE2FLAG, pin);
412 END_LOOP;
414 close_box(box);
415 close_box(vbox);
416 if (Data && !Data->element_tree)
417 Data->element_tree = r_create_tree (NULL, 0, 0);
418 if (Data)
419 r_insert_entry (Data->element_tree, box, 0);
422 /* ---------------------------------------------------------------------------
423 * creates the bounding box of a text object
425 void
426 SetTextBoundingBox (FontTypePtr FontPtr, TextTypePtr Text)
428 SymbolTypePtr symbol = FontPtr->Symbol;
429 unsigned char *s = (unsigned char *) Text->TextString;
430 LocationType width = 0, height = 0;
431 BDimension maxThick = 0;
432 int i;
434 /* calculate size of the bounding box */
435 for (; s && *s; s++)
436 if (*s <= MAX_FONTPOSITION && symbol[*s].Valid)
438 LineTypePtr line = symbol[*s].Line;
439 for (i = 0; i < symbol[*s].LineN; line++, i++)
440 if (line->Thickness > maxThick)
441 maxThick = line->Thickness;
442 width += symbol[*s].Width + symbol[*s].Delta;
443 height = MAX (height, (LocationType) symbol[*s].Height);
445 else
447 width +=
448 ((FontPtr->DefaultSymbol.X2 - FontPtr->DefaultSymbol.X1) * 6 / 5);
449 height = (FontPtr->DefaultSymbol.Y2 - FontPtr->DefaultSymbol.Y1);
452 /* scale values */
453 width *= Text->Scale / 100.;
454 height *= Text->Scale / 100.;
455 maxThick *= Text->Scale / 200.;
456 if (maxThick < 400)
457 maxThick = 400;
459 /* set upper-left and lower-right corner;
460 * swap coordinates if necessary (origin is already in 'swapped')
461 * and rotate box
463 Text->BoundingBox.X1 = Text->X;
464 Text->BoundingBox.Y1 = Text->Y;
465 if (TEST_FLAG (ONSOLDERFLAG, Text))
467 Text->BoundingBox.X1 -= maxThick;
468 Text->BoundingBox.Y1 -= SWAP_SIGN_Y (maxThick);
469 Text->BoundingBox.X2 =
470 Text->BoundingBox.X1 + SWAP_SIGN_X (width + maxThick);
471 Text->BoundingBox.Y2 =
472 Text->BoundingBox.Y1 + SWAP_SIGN_Y (height + 2 * maxThick);
473 RotateBoxLowLevel (&Text->BoundingBox, Text->X, Text->Y,
474 (4 - Text->Direction) & 0x03);
476 else
478 Text->BoundingBox.X1 -= maxThick;
479 Text->BoundingBox.Y1 -= maxThick;
480 Text->BoundingBox.X2 = Text->BoundingBox.X1 + width + maxThick;
481 Text->BoundingBox.Y2 = Text->BoundingBox.Y1 + height + 2 * maxThick;
482 RotateBoxLowLevel (&Text->BoundingBox,
483 Text->X, Text->Y, Text->Direction);
486 /* the bounding box covers the extent of influence
487 * so it must include the clearance values too
489 Text->BoundingBox.X1 -= PCB->Bloat;
490 Text->BoundingBox.Y1 -= PCB->Bloat;
491 Text->BoundingBox.X2 += PCB->Bloat;
492 Text->BoundingBox.Y2 += PCB->Bloat;
493 close_box(&Text->BoundingBox);
496 /* ---------------------------------------------------------------------------
497 * returns true if data area is empty
499 bool
500 IsDataEmpty (DataTypePtr Data)
502 bool hasNoObjects;
503 Cardinal i;
505 hasNoObjects = (Data->ViaN == 0);
506 hasNoObjects &= (Data->ElementN == 0);
507 for (i = 0; i < max_copper_layer + 2; i++)
508 hasNoObjects = hasNoObjects &&
509 Data->Layer[i].LineN == 0 &&
510 Data->Layer[i].ArcN == 0 &&
511 Data->Layer[i].TextN == 0 && Data->Layer[i].PolygonN == 0;
512 return (hasNoObjects);
516 FlagIsDataEmpty (int parm)
518 int i = IsDataEmpty (PCB->Data);
519 return parm ? !i : i;
522 /* FLAG(DataEmpty,FlagIsDataEmpty,0) */
523 /* FLAG(DataNonEmpty,FlagIsDataEmpty,1) */
525 /* ---------------------------------------------------------------------------
526 * gets minimum and maximum coordinates
527 * returns NULL if layout is empty
529 BoxTypePtr
530 GetDataBoundingBox (DataTypePtr Data)
532 static BoxType box;
533 /* FIX ME: use r_search to do this much faster */
535 /* preset identifiers with highest and lowest possible values */
536 box.X1 = box.Y1 = MAX_COORD;
537 box.X2 = box.Y2 = -MAX_COORD;
539 /* now scan for the lowest/highest X and Y coordinate */
540 VIA_LOOP (Data);
542 box.X1 = MIN (box.X1, via->X - via->Thickness / 2);
543 box.Y1 = MIN (box.Y1, via->Y - via->Thickness / 2);
544 box.X2 = MAX (box.X2, via->X + via->Thickness / 2);
545 box.Y2 = MAX (box.Y2, via->Y + via->Thickness / 2);
547 END_LOOP;
548 ELEMENT_LOOP (Data);
550 box.X1 = MIN (box.X1, element->BoundingBox.X1);
551 box.Y1 = MIN (box.Y1, element->BoundingBox.Y1);
552 box.X2 = MAX (box.X2, element->BoundingBox.X2);
553 box.Y2 = MAX (box.Y2, element->BoundingBox.Y2);
555 TextTypePtr text = &NAMEONPCB_TEXT (element);
556 box.X1 = MIN (box.X1, text->BoundingBox.X1);
557 box.Y1 = MIN (box.Y1, text->BoundingBox.Y1);
558 box.X2 = MAX (box.X2, text->BoundingBox.X2);
559 box.Y2 = MAX (box.Y2, text->BoundingBox.Y2);
562 END_LOOP;
563 ALLLINE_LOOP (Data);
565 box.X1 = MIN (box.X1, line->Point1.X - line->Thickness / 2);
566 box.Y1 = MIN (box.Y1, line->Point1.Y - line->Thickness / 2);
567 box.X1 = MIN (box.X1, line->Point2.X - line->Thickness / 2);
568 box.Y1 = MIN (box.Y1, line->Point2.Y - line->Thickness / 2);
569 box.X2 = MAX (box.X2, line->Point1.X + line->Thickness / 2);
570 box.Y2 = MAX (box.Y2, line->Point1.Y + line->Thickness / 2);
571 box.X2 = MAX (box.X2, line->Point2.X + line->Thickness / 2);
572 box.Y2 = MAX (box.Y2, line->Point2.Y + line->Thickness / 2);
574 ENDALL_LOOP;
575 ALLARC_LOOP (Data);
577 box.X1 = MIN (box.X1, arc->BoundingBox.X1);
578 box.Y1 = MIN (box.Y1, arc->BoundingBox.Y1);
579 box.X2 = MAX (box.X2, arc->BoundingBox.X2);
580 box.Y2 = MAX (box.Y2, arc->BoundingBox.Y2);
582 ENDALL_LOOP;
583 ALLTEXT_LOOP (Data);
585 box.X1 = MIN (box.X1, text->BoundingBox.X1);
586 box.Y1 = MIN (box.Y1, text->BoundingBox.Y1);
587 box.X2 = MAX (box.X2, text->BoundingBox.X2);
588 box.Y2 = MAX (box.Y2, text->BoundingBox.Y2);
590 ENDALL_LOOP;
591 ALLPOLYGON_LOOP (Data);
593 box.X1 = MIN (box.X1, polygon->BoundingBox.X1);
594 box.Y1 = MIN (box.Y1, polygon->BoundingBox.Y1);
595 box.X2 = MAX (box.X2, polygon->BoundingBox.X2);
596 box.Y2 = MAX (box.Y2, polygon->BoundingBox.Y2);
598 ENDALL_LOOP;
599 return (IsDataEmpty (Data) ? NULL : &box);
602 /* ---------------------------------------------------------------------------
603 * centers the displayed PCB around the specified point (X,Y)
604 * if Delta is false, X,Y are in absolute PCB coordinates
605 * if Delta is true, simply move the center by an amount X, Y in screen
606 * coordinates
608 void
609 CenterDisplay (LocationType X, LocationType Y, bool Delta)
611 double save_grid = PCB->Grid;
612 PCB->Grid = 1;
613 if (Delta)
615 MoveCrosshairRelative (X, Y);
617 else
619 if (MoveCrosshairAbsolute (X, Y))
621 RestoreCrosshair(false);
624 gui->set_crosshair (Crosshair.X, Crosshair.Y, HID_SC_WARP_POINTER);
625 PCB->Grid = save_grid;
628 /* ---------------------------------------------------------------------------
629 * transforms symbol coordinates so that the left edge of each symbol
630 * is at the zero position. The y coordinates are moved so that min(y) = 0
633 void
634 SetFontInfo (FontTypePtr Ptr)
636 Cardinal i, j;
637 SymbolTypePtr symbol;
638 LineTypePtr line;
639 LocationType totalminy = MAX_COORD;
641 /* calculate cell with and height (is at least DEFAULT_CELLSIZE)
642 * maximum cell width and height
643 * minimum x and y position of all lines
645 Ptr->MaxWidth = DEFAULT_CELLSIZE;
646 Ptr->MaxHeight = DEFAULT_CELLSIZE;
647 for (i = 0, symbol = Ptr->Symbol; i <= MAX_FONTPOSITION; i++, symbol++)
649 LocationType minx, miny, maxx, maxy;
651 /* next one if the index isn't used or symbol is empty (SPACE) */
652 if (!symbol->Valid || !symbol->LineN)
653 continue;
655 minx = miny = MAX_COORD;
656 maxx = maxy = 0;
657 for (line = symbol->Line, j = symbol->LineN; j; j--, line++)
659 minx = MIN (minx, line->Point1.X);
660 miny = MIN (miny, line->Point1.Y);
661 minx = MIN (minx, line->Point2.X);
662 miny = MIN (miny, line->Point2.Y);
663 maxx = MAX (maxx, line->Point1.X);
664 maxy = MAX (maxy, line->Point1.Y);
665 maxx = MAX (maxx, line->Point2.X);
666 maxy = MAX (maxy, line->Point2.Y);
669 /* move symbol to left edge */
670 for (line = symbol->Line, j = symbol->LineN; j; j--, line++)
671 MOVE_LINE_LOWLEVEL (line, -minx, 0);
673 /* set symbol bounding box with a minimum cell size of (1,1) */
674 symbol->Width = maxx - minx + 1;
675 symbol->Height = maxy + 1;
677 /* check total min/max */
678 Ptr->MaxWidth = MAX (Ptr->MaxWidth, symbol->Width);
679 Ptr->MaxHeight = MAX (Ptr->MaxHeight, symbol->Height);
680 totalminy = MIN (totalminy, miny);
683 /* move coordinate system to the upper edge (lowest y on screen) */
684 for (i = 0, symbol = Ptr->Symbol; i <= MAX_FONTPOSITION; i++, symbol++)
685 if (symbol->Valid)
687 symbol->Height -= totalminy;
688 for (line = symbol->Line, j = symbol->LineN; j; j--, line++)
689 MOVE_LINE_LOWLEVEL (line, 0, -totalminy);
692 /* setup the box for the default symbol */
693 Ptr->DefaultSymbol.X1 = Ptr->DefaultSymbol.Y1 = 0;
694 Ptr->DefaultSymbol.X2 = Ptr->DefaultSymbol.X1 + Ptr->MaxWidth;
695 Ptr->DefaultSymbol.Y2 = Ptr->DefaultSymbol.Y1 + Ptr->MaxHeight;
698 static void
699 GetNum (char **s, BDimension * num)
701 *num = atoi (*s);
702 while (isdigit ((int) **s))
703 (*s)++;
707 /* ----------------------------------------------------------------------
708 * parses the routes definition string which is a colon separated list of
709 * comma separated Name, Dimension, Dimension, Dimension, Dimension
710 * e.g. Signal,20,40,20,10:Power,40,60,28,10:...
713 ParseRouteString (char *s, RouteStyleTypePtr routeStyle, int scale)
715 int i, style;
716 char Name[256];
718 memset (routeStyle, 0, NUM_STYLES * sizeof (RouteStyleType));
719 for (style = 0; style < NUM_STYLES; style++, routeStyle++)
721 while (*s && isspace ((int) *s))
722 s++;
723 for (i = 0; *s && *s != ','; i++)
724 Name[i] = *s++;
725 Name[i] = '\0';
726 routeStyle->Name = strdup (Name);
727 if (!isdigit ((int) *++s))
728 goto error;
729 GetNum (&s, &routeStyle->Thick);
730 routeStyle->Thick *= scale;
731 while (*s && isspace ((int) *s))
732 s++;
733 if (*s++ != ',')
734 goto error;
735 while (*s && isspace ((int) *s))
736 s++;
737 if (!isdigit ((int) *s))
738 goto error;
739 GetNum (&s, &routeStyle->Diameter);
740 routeStyle->Diameter *= scale;
741 while (*s && isspace ((int) *s))
742 s++;
743 if (*s++ != ',')
744 goto error;
745 while (*s && isspace ((int) *s))
746 s++;
747 if (!isdigit ((int) *s))
748 goto error;
749 GetNum (&s, &routeStyle->Hole);
750 routeStyle->Hole *= scale;
751 /* for backwards-compatibility, we use a 10-mil default
752 * for styles which omit the keepaway specification. */
753 if (*s != ',')
754 routeStyle->Keepaway = 1000;
755 else
757 s++;
758 while (*s && isspace ((int) *s))
759 s++;
760 if (!isdigit ((int) *s))
761 goto error;
762 GetNum (&s, &routeStyle->Keepaway);
763 routeStyle->Keepaway *= scale;
764 while (*s && isspace ((int) *s))
765 s++;
767 if (style < NUM_STYLES - 1)
769 while (*s && isspace ((int) *s))
770 s++;
771 if (*s++ != ':')
772 goto error;
775 return (0);
777 error:
778 memset (routeStyle, 0, NUM_STYLES * sizeof (RouteStyleType));
779 return (1);
782 /* ----------------------------------------------------------------------
783 * parses the group definition string which is a colon separated list of
784 * comma separated layer numbers (1,2,b:4,6,8,t)
787 ParseGroupString (char *s, LayerGroupTypePtr LayerGroup, int LayerN)
789 int group, member, layer;
790 bool c_set = false, /* flags for the two special layers to */
791 s_set = false; /* provide a default setting for old formats */
792 int groupnum[MAX_LAYER + 2];
794 /* clear struct */
795 memset (LayerGroup, 0, sizeof (LayerGroupType));
797 /* Clear assignments */
798 for (layer = 0; layer < MAX_LAYER + 2; layer++)
799 groupnum[layer] = -1;
801 /* loop over all groups */
802 for (group = 0; s && *s && group < LayerN; group++)
804 while (*s && isspace ((int) *s))
805 s++;
807 /* loop over all group members */
808 for (member = 0; *s; s++)
810 /* ignore white spaces and get layernumber */
811 while (*s && isspace ((int) *s))
812 s++;
813 switch (*s)
815 case 'c':
816 case 'C':
817 layer = LayerN + COMPONENT_LAYER;
818 c_set = true;
819 break;
821 case 's':
822 case 'S':
823 layer = LayerN + SOLDER_LAYER;
824 s_set = true;
825 break;
827 default:
828 if (!isdigit ((int) *s))
829 goto error;
830 layer = atoi (s) - 1;
831 break;
833 if (layer > LayerN + MAX (SOLDER_LAYER, COMPONENT_LAYER) ||
834 member >= LayerN + 1)
835 goto error;
836 groupnum[layer] = group;
837 LayerGroup->Entries[group][member++] = layer;
838 while (*++s && isdigit ((int) *s));
840 /* ignore white spaces and check for separator */
841 while (*s && isspace ((int) *s))
842 s++;
843 if (!*s || *s == ':')
844 break;
845 if (*s != ',')
846 goto error;
848 LayerGroup->Number[group] = member;
849 if (*s == ':')
850 s++;
852 if (!s_set)
853 LayerGroup->Entries[SOLDER_LAYER][LayerGroup->Number[SOLDER_LAYER]++] =
854 LayerN + SOLDER_LAYER;
855 if (!c_set)
856 LayerGroup->
857 Entries[COMPONENT_LAYER][LayerGroup->Number[COMPONENT_LAYER]++] =
858 LayerN + COMPONENT_LAYER;
860 for (layer = 0; layer < LayerN && group < LayerN; layer++)
861 if (groupnum[layer] == -1)
863 LayerGroup->Entries[group][0] = layer;
864 LayerGroup->Number[group] = 1;
865 group++;
867 return (0);
869 /* reset structure on error */
870 error:
871 memset (LayerGroup, 0, sizeof (LayerGroupType));
872 return (1);
875 /* ---------------------------------------------------------------------------
876 * quits application
878 void
879 QuitApplication (void)
882 * save data if necessary. It not needed, then don't trigger EmergencySave
883 * via our atexit() registering of EmergencySave(). We presumeably wanted to
884 * exit here and thus it is not an emergency.
886 if (PCB->Changed && Settings.SaveInTMP)
887 EmergencySave ();
888 else
889 DisableEmergencySave ();
891 /* Free up memory allocated to the PCB. Why bother when we're about to exit ?
892 * Because it removes some false positives from heap bug detectors such as
893 * lib dmalloc.
895 FreePCBMemory(PCB);
897 exit (0);
900 /* ---------------------------------------------------------------------------
901 * creates a filename from a template
902 * %f is replaced by the filename
903 * %p by the searchpath
905 char *
906 EvaluateFilename (char *Template, char *Path, char *Filename, char *Parameter)
908 static DynamicStringType command;
909 char *p;
911 if (Settings.verbose)
913 printf ("EvaluateFilename:\n");
914 printf ("\tTemplate: \033[33m%s\033[0m\n", Template);
915 printf ("\tPath: \033[33m%s\033[0m\n", Path);
916 printf ("\tFilename: \033[33m%s\033[0m\n", Filename);
917 printf ("\tParameter: \033[33m%s\033[0m\n", Parameter);
920 DSClearString (&command);
922 for (p = Template; p && *p; p++)
924 /* copy character or add string to command */
925 if (*p == '%'
926 && (*(p + 1) == 'f' || *(p + 1) == 'p' || *(p + 1) == 'a'))
927 switch (*(++p))
929 case 'a':
930 DSAddString (&command, Parameter);
931 break;
932 case 'f':
933 DSAddString (&command, Filename);
934 break;
935 case 'p':
936 DSAddString (&command, Path);
937 break;
939 else
940 DSAddCharacter (&command, *p);
942 DSAddCharacter (&command, '\0');
943 if (Settings.verbose)
944 printf ("EvaluateFilename: \033[32m%s\033[0m\n", command.Data);
946 return strdup (command.Data);
949 /* ---------------------------------------------------------------------------
950 * concatenates directory and filename if directory != NULL,
951 * expands them with a shell and returns the found name(s) or NULL
953 char *
954 ExpandFilename (char *Dirname, char *Filename)
956 static DynamicStringType answer;
957 char *command;
958 FILE *pipe;
959 int c;
961 /* allocate memory for commandline and build it */
962 DSClearString (&answer);
963 if (Dirname)
965 command = calloc (strlen (Filename) + strlen (Dirname) + 7,
966 sizeof (char));
967 sprintf (command, "echo %s/%s", Dirname, Filename);
969 else
971 command = calloc (strlen (Filename) + 6, sizeof (char));
972 sprintf (command, "echo %s", Filename);
975 /* execute it with shell */
976 if ((pipe = popen (command, "r")) != NULL)
978 /* discard all but the first returned line */
979 for (;;)
981 if ((c = fgetc (pipe)) == EOF || c == '\n' || c == '\r')
982 break;
983 else
984 DSAddCharacter (&answer, c);
987 free (command);
988 return (pclose (pipe) ? NULL : answer.Data);
991 /* couldn't be expanded by the shell */
992 PopenErrorMessage (command);
993 free (command);
994 return (NULL);
998 /* ---------------------------------------------------------------------------
999 * returns the layer number for the passed pointer
1002 GetLayerNumber (DataTypePtr Data, LayerTypePtr Layer)
1004 int i;
1006 for (i = 0; i < MAX_LAYER + 2; i++)
1007 if (Layer == &Data->Layer[i])
1008 break;
1009 return (i);
1012 /* ---------------------------------------------------------------------------
1013 * move layer (number is passed in) to top of layerstack
1015 static void
1016 PushOnTopOfLayerStack (int NewTop)
1018 int i;
1020 /* ignore silk layers */
1021 if (NewTop < max_copper_layer)
1023 /* first find position of passed one */
1024 for (i = 0; i < max_copper_layer; i++)
1025 if (LayerStack[i] == NewTop)
1026 break;
1028 /* bring this element to the top of the stack */
1029 for (; i; i--)
1030 LayerStack[i] = LayerStack[i - 1];
1031 LayerStack[0] = NewTop;
1036 /* ----------------------------------------------------------------------
1037 * changes the visibility of all layers in a group
1038 * returns the number of changed layers
1041 ChangeGroupVisibility (int Layer, bool On, bool ChangeStackOrder)
1043 int group, i, changed = 1; /* at least the current layer changes */
1045 /* Warning: these special case values must agree with what gui-top-window.c
1046 | thinks the are.
1049 if (Settings.verbose)
1050 printf ("ChangeGroupVisibility(Layer=%d, On=%d, ChangeStackOrder=%d)\n",
1051 Layer, On, ChangeStackOrder);
1053 /* decrement 'i' to keep stack in order of layergroup */
1054 if ((group = GetGroupOfLayer (Layer)) < max_group)
1055 for (i = PCB->LayerGroups.Number[group]; i;)
1057 int layer = PCB->LayerGroups.Entries[group][--i];
1059 /* don't count the passed member of the group */
1060 if (layer != Layer && layer < max_copper_layer)
1062 PCB->Data->Layer[layer].On = On;
1064 /* push layer on top of stack if switched on */
1065 if (On && ChangeStackOrder)
1066 PushOnTopOfLayerStack (layer);
1067 changed++;
1071 /* change at least the passed layer */
1072 PCB->Data->Layer[Layer].On = On;
1073 if (On && ChangeStackOrder)
1074 PushOnTopOfLayerStack (Layer);
1076 /* update control panel and exit */
1077 hid_action ("LayersChanged");
1078 return (changed);
1081 /* ----------------------------------------------------------------------
1082 * Given a string description of a layer stack, adjust the layer stack
1083 * to correspond.
1086 void
1087 LayerStringToLayerStack (char *s)
1089 static int listed_layers = 0;
1090 int l = strlen (s);
1091 char **args;
1092 int i, argn, lno;
1093 int prev_sep = 1;
1095 s = strdup (s);
1096 args = (char **) malloc (l * sizeof (char *));
1097 argn = 0;
1099 for (i=0; i<l; i++)
1101 switch (s[i])
1103 case ' ':
1104 case '\t':
1105 case ',':
1106 case ';':
1107 case ':':
1108 prev_sep = 1;
1109 s[i] = '\0';
1110 break;
1111 default:
1112 if (prev_sep)
1113 args[argn++] = s+i;
1114 prev_sep = 0;
1115 break;
1119 for (i = 0; i < max_copper_layer + 2; i++)
1121 if (i < max_copper_layer)
1122 LayerStack[i] = i;
1123 PCB->Data->Layer[i].On = false;
1125 PCB->ElementOn = false;
1126 PCB->InvisibleObjectsOn = false;
1127 PCB->PinOn = false;
1128 PCB->ViaOn = false;
1129 PCB->RatOn = false;
1130 CLEAR_FLAG (SHOWMASKFLAG, PCB);
1131 Settings.ShowSolderSide = 0;
1133 for (i=argn-1; i>=0; i--)
1135 if (strcasecmp (args[i], "rats") == 0)
1136 PCB->RatOn = true;
1137 else if (strcasecmp (args[i], "invisible") == 0)
1138 PCB->InvisibleObjectsOn = true;
1139 else if (strcasecmp (args[i], "pins") == 0)
1140 PCB->PinOn = true;
1141 else if (strcasecmp (args[i], "vias") == 0)
1142 PCB->ViaOn = true;
1143 else if (strcasecmp (args[i], "elements") == 0
1144 || strcasecmp (args[i], "silk") == 0)
1145 PCB->ElementOn = true;
1146 else if (strcasecmp (args[i], "mask") == 0)
1147 SET_FLAG (SHOWMASKFLAG, PCB);
1148 else if (strcasecmp (args[i], "solderside") == 0)
1149 Settings.ShowSolderSide = 1;
1150 else if (isdigit ((int) args[i][0]))
1152 lno = atoi (args[i]);
1153 ChangeGroupVisibility (lno, true, true);
1155 else
1157 int found = 0;
1158 for (lno = 0; lno < max_copper_layer; lno++)
1159 if (strcasecmp (args[i], PCB->Data->Layer[lno].Name) == 0)
1161 ChangeGroupVisibility (lno, true, true);
1162 found = 1;
1163 break;
1165 if (!found)
1167 fprintf(stderr, "Warning: layer \"%s\" not known\n", args[i]);
1168 if (!listed_layers)
1170 fprintf (stderr, "Named layers in this board are:\n");
1171 listed_layers = 1;
1172 for (lno=0; lno < max_copper_layer; lno ++)
1173 fprintf(stderr, "\t%s\n", PCB->Data->Layer[lno].Name);
1174 fprintf(stderr, "Also: component, solder, rats, invisible, pins, vias, elements or silk, mask, solderside.\n");
1181 /* ----------------------------------------------------------------------
1182 * lookup the group to which a layer belongs to
1183 * returns max_group if no group is found, or is
1184 * passed Layer is equal to max_copper_layer
1187 GetGroupOfLayer (int Layer)
1189 int group, i;
1191 if (Layer == max_copper_layer)
1192 return max_group;
1193 for (group = 0; group < max_group; group++)
1194 for (i = 0; i < PCB->LayerGroups.Number[group]; i++)
1195 if (PCB->LayerGroups.Entries[group][i] == Layer)
1196 return (group);
1197 return max_group;
1201 /* ---------------------------------------------------------------------------
1202 * returns the layergroup number for the passed pointer
1205 GetLayerGroupNumberByPointer (LayerTypePtr Layer)
1207 return (GetLayerGroupNumberByNumber (GetLayerNumber (PCB->Data, Layer)));
1210 /* ---------------------------------------------------------------------------
1211 * returns the layergroup number for the passed layernumber
1214 GetLayerGroupNumberByNumber (Cardinal Layer)
1216 int group, entry;
1218 for (group = 0; group < max_group; group++)
1219 for (entry = 0; entry < PCB->LayerGroups.Number[group]; entry++)
1220 if (PCB->LayerGroups.Entries[group][entry] == Layer)
1221 return (group);
1223 /* since every layer belongs to a group it is safe to return
1224 * the value without boundary checking
1226 return (group);
1229 /* ---------------------------------------------------------------------------
1230 * returns a pointer to an objects bounding box;
1231 * data is valid until the routine is called again
1233 BoxTypePtr
1234 GetObjectBoundingBox (int Type, void *Ptr1, void *Ptr2, void *Ptr3)
1236 switch (Type)
1238 case LINE_TYPE:
1239 case ARC_TYPE:
1240 case TEXT_TYPE:
1241 case POLYGON_TYPE:
1242 case PAD_TYPE:
1243 case PIN_TYPE:
1244 case ELEMENTNAME_TYPE:
1245 return (BoxType *)Ptr2;
1246 case VIA_TYPE:
1247 case ELEMENT_TYPE:
1248 return (BoxType *)Ptr1;
1249 case POLYGONPOINT_TYPE:
1250 case LINEPOINT_TYPE:
1251 return (BoxType *)Ptr3;
1252 default:
1253 Message ("Request for bounding box of unsupported type %d\n", Type);
1254 return (BoxType *)Ptr2;
1258 /* ---------------------------------------------------------------------------
1259 * computes the bounding box of an arc
1261 void
1262 SetArcBoundingBox (ArcTypePtr Arc)
1264 register double ca1, ca2, sa1, sa2;
1265 double minx, maxx, miny, maxy;
1266 register LocationType ang1, ang2, delta, a;
1267 register LocationType width;
1269 /* first put angles into standard form */
1270 if (Arc->Delta > 360)
1271 Arc->Delta = 360;
1272 if (Arc->Delta < -360)
1273 Arc->Delta = -360;
1275 if (Arc->Delta > 0)
1277 ang1 = Arc->StartAngle;
1278 delta = Arc->Delta;
1280 else
1282 ang1 = Arc->StartAngle + Arc->Delta;
1283 delta = -Arc->Delta;
1285 if (ang1 < 0)
1286 ang1 = 360 - ((-ang1) % 360);
1287 else
1288 ang1 = ang1 % 360;
1290 ang2 = ang1 + delta;
1292 /* calculate sines, cosines */
1293 ca1 = M180 * (double) ang1;
1294 sa1 = sin (ca1);
1295 ca1 = cos (ca1);
1297 minx = maxx = ca1;
1298 miny = maxy = sa1;
1300 ca2 = M180 * (double) ang2;
1301 sa2 = sin (ca2);
1302 ca2 = cos (ca2);
1304 minx = MIN (minx, ca2);
1305 maxx = MAX (maxx, ca2);
1306 miny = MIN (miny, sa2);
1307 maxy = MAX (maxy, sa2);
1309 for (a = ang1 - ang1 % 90 + 90; a < ang2; a += 90)
1311 switch (a % 360)
1313 case 0:
1314 maxx = 1;
1315 break;
1316 case 90:
1317 maxy = 1;
1318 break;
1319 case 180:
1320 minx = -1;
1321 break;
1322 case 270:
1323 miny = -1;
1324 break;
1328 Arc->BoundingBox.X2 = Arc->X - Arc->Width * minx;
1330 Arc->BoundingBox.X1 = Arc->X - Arc->Width * maxx;
1332 Arc->BoundingBox.Y2 = Arc->Y + Arc->Height * maxy;
1334 Arc->BoundingBox.Y1 = Arc->Y + Arc->Height * miny;
1336 width = (Arc->Thickness + Arc->Clearance) / 2;
1337 Arc->BoundingBox.X1 -= width;
1338 Arc->BoundingBox.X2 += width;
1339 Arc->BoundingBox.Y1 -= width;
1340 Arc->BoundingBox.Y2 += width;
1341 close_box(&Arc->BoundingBox);
1344 /* ---------------------------------------------------------------------------
1345 * resets the layerstack setting
1347 void
1348 ResetStackAndVisibility (void)
1350 int comp_group;
1351 Cardinal i;
1353 for (i = 0; i < max_copper_layer + 2; i++)
1355 if (i < max_copper_layer)
1356 LayerStack[i] = i;
1357 PCB->Data->Layer[i].On = true;
1359 PCB->ElementOn = true;
1360 PCB->InvisibleObjectsOn = true;
1361 PCB->PinOn = true;
1362 PCB->ViaOn = true;
1363 PCB->RatOn = true;
1365 /* Bring the component group to the front and make it active. */
1366 comp_group = GetLayerGroupNumberByNumber (component_silk_layer);
1367 ChangeGroupVisibility (PCB->LayerGroups.Entries[comp_group][0], 1, 1);
1370 /* ---------------------------------------------------------------------------
1371 * saves the layerstack setting
1373 void
1374 SaveStackAndVisibility (void)
1376 Cardinal i;
1377 static bool run = false;
1379 if (run == false)
1381 SavedStack.cnt = 0;
1382 run = true;
1385 if (SavedStack.cnt != 0)
1387 fprintf (stderr,
1388 "SaveStackAndVisibility() layerstack was already saved and not"
1389 "yet restored. cnt = %d\n", SavedStack.cnt);
1392 for (i = 0; i < max_copper_layer + 2; i++)
1394 if (i < max_copper_layer)
1395 SavedStack.LayerStack[i] = LayerStack[i];
1396 SavedStack.LayerOn[i] = PCB->Data->Layer[i].On;
1398 SavedStack.ElementOn = PCB->ElementOn;
1399 SavedStack.InvisibleObjectsOn = PCB->InvisibleObjectsOn;
1400 SavedStack.PinOn = PCB->PinOn;
1401 SavedStack.ViaOn = PCB->ViaOn;
1402 SavedStack.RatOn = PCB->RatOn;
1403 SavedStack.cnt++;
1406 /* ---------------------------------------------------------------------------
1407 * restores the layerstack setting
1409 void
1410 RestoreStackAndVisibility (void)
1412 Cardinal i;
1414 if (SavedStack.cnt == 0)
1416 fprintf (stderr, "RestoreStackAndVisibility() layerstack has not"
1417 " been saved. cnt = %d\n", SavedStack.cnt);
1418 return;
1420 else if (SavedStack.cnt != 1)
1422 fprintf (stderr, "RestoreStackAndVisibility() layerstack save count is"
1423 " wrong. cnt = %d\n", SavedStack.cnt);
1426 for (i = 0; i < max_copper_layer + 2; i++)
1428 if (i < max_copper_layer)
1429 LayerStack[i] = SavedStack.LayerStack[i];
1430 PCB->Data->Layer[i].On = SavedStack.LayerOn[i];
1432 PCB->ElementOn = SavedStack.ElementOn;
1433 PCB->InvisibleObjectsOn = SavedStack.InvisibleObjectsOn;
1434 PCB->PinOn = SavedStack.PinOn;
1435 PCB->ViaOn = SavedStack.ViaOn;
1436 PCB->RatOn = SavedStack.RatOn;
1438 SavedStack.cnt--;
1441 /* ----------------------------------------------------------------------
1442 * returns pointer to current working directory. If 'path' is not
1443 * NULL, then the current working directory is copied to the array
1444 * pointed to by 'path'
1446 char *
1447 GetWorkingDirectory (char *path)
1449 #ifdef HAVE_GETCWD
1450 return getcwd (path, MAXPATHLEN);
1451 #else
1452 /* seems that some BSD releases lack of a prototype for getwd() */
1453 return getwd (path);
1454 #endif
1458 /* ---------------------------------------------------------------------------
1459 * writes a string to the passed file pointer
1460 * some special characters are quoted
1462 void
1463 CreateQuotedString (DynamicStringTypePtr DS, char *S)
1465 DSClearString (DS);
1466 DSAddCharacter (DS, '"');
1467 while (*S)
1469 if (*S == '"' || *S == '\\')
1470 DSAddCharacter (DS, '\\');
1471 DSAddCharacter (DS, *S++);
1473 DSAddCharacter (DS, '"');
1477 static void
1478 RightAngles (int Angle, float *cosa, float *sina)
1480 *cosa = (float) cos ((double) Angle * M180);
1481 *sina = (float) sin ((double) Angle * M180);
1484 BoxTypePtr
1485 GetArcEnds (ArcTypePtr Arc)
1487 static BoxType box;
1488 float ca, sa;
1490 RightAngles (Arc->StartAngle, &ca, &sa);
1491 box.X1 = Arc->X - Arc->Width * ca;
1492 box.Y1 = Arc->Y + Arc->Height * sa;
1493 RightAngles (Arc->StartAngle + Arc->Delta, &ca, &sa);
1494 box.X2 = Arc->X - Arc->Width * ca;
1495 box.Y2 = Arc->Y + Arc->Height * sa;
1496 return (&box);
1500 /* doesn't this belong in change.c ?? */
1501 void
1502 ChangeArcAngles (LayerTypePtr Layer, ArcTypePtr a,
1503 long int new_sa, long int new_da)
1505 if (new_da >= 360)
1507 new_da = 360;
1508 new_sa = 0;
1510 RestoreToPolygon (PCB->Data, ARC_TYPE, Layer, a);
1511 r_delete_entry (Layer->arc_tree, (BoxTypePtr) a);
1512 AddObjectToChangeAnglesUndoList (ARC_TYPE, a, a, a);
1513 a->StartAngle = new_sa;
1514 a->Delta = new_da;
1515 SetArcBoundingBox (a);
1516 r_insert_entry (Layer->arc_tree, (BoxTypePtr) a, 0);
1517 ClearFromPolygon (PCB->Data, ARC_TYPE, Layer, a);
1520 static char *
1521 BumpName (char *Name)
1523 int num;
1524 char c, *start;
1525 static char temp[256];
1527 start = Name;
1528 /* seek end of string */
1529 while (*Name != 0)
1530 Name++;
1531 /* back up to potential number */
1532 for (Name--; isdigit ((int) *Name); Name--);
1533 Name++;
1534 if (*Name)
1535 num = atoi (Name) + 1;
1536 else
1537 num = 1;
1538 c = *Name;
1539 *Name = 0;
1540 sprintf (temp, "%s%d", start, num);
1541 /* if this is not our string, put back the blown character */
1542 if (start != temp)
1543 *Name = c;
1544 return (temp);
1548 * make a unique name for the name on board
1549 * this can alter the contents of the input string
1551 char *
1552 UniqueElementName (DataTypePtr Data, char *Name)
1554 bool unique = true;
1555 /* null strings are ok */
1556 if (!Name || !*Name)
1557 return (Name);
1559 for (;;)
1561 ELEMENT_LOOP (Data);
1563 if (NAMEONPCB_NAME (element) &&
1564 NSTRCMP (NAMEONPCB_NAME (element), Name) == 0)
1566 Name = BumpName (Name);
1567 unique = false;
1568 break;
1571 END_LOOP;
1572 if (unique)
1573 return (Name);
1574 unique = true;
1578 static void
1579 GetGridLockCoordinates (int type, void *ptr1,
1580 void *ptr2, void *ptr3, LocationType * x,
1581 LocationType * y)
1583 switch (type)
1585 case VIA_TYPE:
1586 *x = ((PinTypePtr) ptr2)->X;
1587 *y = ((PinTypePtr) ptr2)->Y;
1588 break;
1589 case LINE_TYPE:
1590 *x = ((LineTypePtr) ptr2)->Point1.X;
1591 *y = ((LineTypePtr) ptr2)->Point1.Y;
1592 break;
1593 case TEXT_TYPE:
1594 case ELEMENTNAME_TYPE:
1595 *x = ((TextTypePtr) ptr2)->X;
1596 *y = ((TextTypePtr) ptr2)->Y;
1597 break;
1598 case ELEMENT_TYPE:
1599 *x = ((ElementTypePtr) ptr2)->MarkX;
1600 *y = ((ElementTypePtr) ptr2)->MarkY;
1601 break;
1602 case POLYGON_TYPE:
1603 *x = ((PolygonTypePtr) ptr2)->Points[0].X;
1604 *y = ((PolygonTypePtr) ptr2)->Points[0].Y;
1605 break;
1607 case LINEPOINT_TYPE:
1608 case POLYGONPOINT_TYPE:
1609 *x = ((PointTypePtr) ptr3)->X;
1610 *y = ((PointTypePtr) ptr3)->Y;
1611 break;
1612 case ARC_TYPE:
1614 BoxTypePtr box;
1616 box = GetArcEnds ((ArcTypePtr) ptr2);
1617 *x = box->X1;
1618 *y = box->Y1;
1619 break;
1624 void
1625 AttachForCopy (LocationType PlaceX, LocationType PlaceY)
1627 BoxTypePtr box;
1628 LocationType mx = 0, my = 0;
1630 Crosshair.AttachedObject.RubberbandN = 0;
1631 if (! TEST_FLAG (SNAPPINFLAG, PCB))
1633 /* dither the grab point so that the mark, center, etc
1634 * will end up on a grid coordinate
1636 GetGridLockCoordinates (Crosshair.AttachedObject.Type,
1637 Crosshair.AttachedObject.Ptr1,
1638 Crosshair.AttachedObject.Ptr2,
1639 Crosshair.AttachedObject.Ptr3, &mx, &my);
1640 mx = GRIDFIT_X (mx, PCB->Grid) - mx;
1641 my = GRIDFIT_Y (my, PCB->Grid) - my;
1643 Crosshair.AttachedObject.X = PlaceX - mx;
1644 Crosshair.AttachedObject.Y = PlaceY - my;
1645 if (!Marked.status || TEST_FLAG (LOCALREFFLAG, PCB))
1646 SetLocalRef (PlaceX - mx, PlaceY - my, true);
1647 Crosshair.AttachedObject.State = STATE_SECOND;
1649 /* get boundingbox of object and set cursor range */
1650 box = GetObjectBoundingBox (Crosshair.AttachedObject.Type,
1651 Crosshair.AttachedObject.Ptr1,
1652 Crosshair.AttachedObject.Ptr2,
1653 Crosshair.AttachedObject.Ptr3);
1654 SetCrosshairRange (Crosshair.AttachedObject.X - box->X1,
1655 Crosshair.AttachedObject.Y - box->Y1,
1656 PCB->MaxWidth - (box->X2 - Crosshair.AttachedObject.X),
1657 PCB->MaxHeight - (box->Y2 - Crosshair.AttachedObject.Y));
1659 /* get all attached objects if necessary */
1660 if ((Settings.Mode != COPY_MODE) && TEST_FLAG (RUBBERBANDFLAG, PCB))
1661 LookupRubberbandLines (Crosshair.AttachedObject.Type,
1662 Crosshair.AttachedObject.Ptr1,
1663 Crosshair.AttachedObject.Ptr2,
1664 Crosshair.AttachedObject.Ptr3);
1665 if (Settings.Mode != COPY_MODE &&
1666 (Crosshair.AttachedObject.Type == ELEMENT_TYPE ||
1667 Crosshair.AttachedObject.Type == VIA_TYPE ||
1668 Crosshair.AttachedObject.Type == LINE_TYPE ||
1669 Crosshair.AttachedObject.Type == LINEPOINT_TYPE))
1670 LookupRatLines (Crosshair.AttachedObject.Type,
1671 Crosshair.AttachedObject.Ptr1,
1672 Crosshair.AttachedObject.Ptr2,
1673 Crosshair.AttachedObject.Ptr3);
1677 * Return nonzero if the given file exists and is readable.
1680 FileExists (const char *name)
1682 FILE *f;
1683 f = fopen (name, "r");
1684 if (f)
1686 fclose (f);
1687 return 1;
1689 return 0;
1692 char *
1693 Concat (const char *first, ...)
1695 char *rv;
1696 int len;
1697 va_list a;
1699 len = strlen (first);
1700 rv = (char *) malloc (len + 1);
1701 strcpy (rv, first);
1703 va_start (a, first);
1704 while (1)
1706 const char *s = va_arg (a, const char *);
1707 if (!s)
1708 break;
1709 len += strlen (s);
1710 rv = (char *) realloc (rv, len + 1);
1711 strcat (rv, s);
1713 va_end (a);
1714 return rv;
1718 mem_any_set (unsigned char *ptr, int bytes)
1720 while (bytes--)
1721 if (*ptr++)
1722 return 1;
1723 return 0;
1726 /* This just fills in a FlagType with current flags. */
1727 FlagType
1728 MakeFlags (unsigned int flags)
1730 FlagType rv;
1731 memset (&rv, 0, sizeof (rv));
1732 rv.f = flags;
1733 return rv;
1736 /* This converts old flag bits (from saved PCB files) to new format. */
1737 FlagType
1738 OldFlags (unsigned int flags)
1740 FlagType rv;
1741 int i, f;
1742 memset (&rv, 0, sizeof (rv));
1743 /* If we move flag bits around, this is where we map old bits to them. */
1744 rv.f = flags & 0xffff;
1745 f = 0x10000;
1746 for (i = 0; i < 8; i++)
1748 /* use the closest thing to the old thermal style */
1749 if (flags & f)
1750 rv.t[i / 2] |= (1 << (4 * (i % 2)));
1751 f <<= 1;
1753 return rv;
1756 FlagType
1757 AddFlags (FlagType flag, unsigned int flags)
1759 flag.f |= flags;
1760 return flag;
1763 FlagType
1764 MaskFlags (FlagType flag, unsigned int flags)
1766 flag.f &= ~flags;
1767 return flag;
1770 /***********************************************************************
1771 * Layer Group Functions
1775 MoveLayerToGroup (int layer, int group)
1777 int prev, i, j;
1779 if (layer < 0 || layer > max_copper_layer + 1)
1780 return -1;
1781 prev = GetLayerGroupNumberByNumber (layer);
1782 if ((layer == solder_silk_layer
1783 && group == GetLayerGroupNumberByNumber (component_silk_layer))
1784 || (layer == component_silk_layer
1785 && group == GetLayerGroupNumberByNumber (solder_silk_layer))
1786 || (group < 0 || group >= max_group) || (prev == group))
1787 return prev;
1789 /* Remove layer from prev group */
1790 for (j = i = 0; i < PCB->LayerGroups.Number[prev]; i++)
1791 if (PCB->LayerGroups.Entries[prev][i] != layer)
1792 PCB->LayerGroups.Entries[prev][j++] = PCB->LayerGroups.Entries[prev][i];
1793 PCB->LayerGroups.Number[prev]--;
1795 /* Add layer to new group. */
1796 i = PCB->LayerGroups.Number[group]++;
1797 PCB->LayerGroups.Entries[group][i] = layer;
1799 return group;
1802 char *
1803 LayerGroupsToString (LayerGroupTypePtr lg)
1805 #if MAX_LAYER < 9998
1806 /* Allows for layer numbers 0..9999 */
1807 static char buf[(MAX_LAYER + 2) * 5 + 1];
1808 #endif
1809 char *cp = buf;
1810 char sep = 0;
1811 int group, entry;
1812 for (group = 0; group < max_group; group++)
1813 if (PCB->LayerGroups.Number[group])
1815 if (sep)
1816 *cp++ = ':';
1817 sep = 1;
1818 for (entry = 0; entry < PCB->LayerGroups.Number[group]; entry++)
1820 int layer = PCB->LayerGroups.Entries[group][entry];
1821 if (layer == component_silk_layer)
1823 *cp++ = 'c';
1825 else if (layer == solder_silk_layer)
1827 *cp++ = 's';
1829 else
1831 sprintf (cp, "%d", layer + 1);
1832 while (*++cp)
1835 if (entry != PCB->LayerGroups.Number[group] - 1)
1836 *cp++ = ',';
1839 *cp++ = 0;
1840 return buf;
1843 char *
1844 pcb_author (void)
1846 #ifdef HAVE_GETPWUID
1847 static struct passwd *pwentry;
1848 static char *fab_author = 0;
1850 if (!fab_author)
1852 if (Settings.FabAuthor && Settings.FabAuthor[0])
1853 fab_author = Settings.FabAuthor;
1854 else
1856 int len;
1857 char *comma, *gecos;
1859 /* ID the user. */
1860 pwentry = getpwuid (getuid ());
1861 gecos = pwentry->pw_gecos;
1862 comma = strchr (gecos, ',');
1863 if (comma)
1864 len = comma - gecos;
1865 else
1866 len = strlen (gecos);
1867 fab_author = malloc (len + 1);
1868 if (!fab_author)
1870 perror ("pcb: out of memory.\n");
1871 exit (-1);
1873 memcpy (fab_author, gecos, len);
1874 fab_author[len] = 0;
1877 return fab_author;
1878 #else
1879 return "Unknown";
1880 #endif
1884 char *
1885 AttributeGetFromList (AttributeListType *list, char *name)
1887 int i;
1888 for (i=0; i<list->Number; i++)
1889 if (strcmp (name, list->List[i].name) == 0)
1890 return list->List[i].value;
1891 return NULL;
1895 AttributePutToList (AttributeListType *list, const char *name, const char *value, int replace)
1897 int i;
1899 /* If we're allowed to replace an existing attribute, see if we
1900 can. */
1901 if (replace)
1903 for (i=0; i<list->Number; i++)
1904 if (strcmp (name, list->List[i].name) == 0)
1906 free (list->List[i].value);
1907 list->List[i].value = STRDUP (value);
1908 return 1;
1912 /* At this point, we're going to need to add a new attribute to the
1913 list. See if there's room. */
1914 if (list->Number >= list->Max)
1916 list->Max += 10;
1917 list->List = (AttributeType *) realloc (list->List,
1918 list->Max * sizeof (AttributeType));
1921 /* Now add the new attribute. */
1922 i = list->Number;
1923 list->List[i].name = STRDUP (name);
1924 list->List[i].value = STRDUP (value);
1925 list->Number ++;
1926 return 0;
1929 void
1930 AttributeRemoveFromList(AttributeListType *list, char *name)
1932 int i, j;
1933 for (i=0; i<list->Number; i++)
1934 if (strcmp (name, list->List[i].name) == 0)
1936 free (list->List[i].name);
1937 free (list->List[i].value);
1938 for (j=i; j<list->Number-i; j++)
1939 list->List[j] = list->List[j+1];
1940 list->Number --;
1946 const char *
1947 c_dtostr (double d)
1949 static char buf[100];
1950 int i, f;
1951 char *bufp = buf;
1953 if (d < 0)
1955 *bufp++ = '-';
1956 d = -d;
1958 d += 0.0000005; /* rounding */
1959 i = floor (d);
1960 d -= i;
1961 sprintf (bufp, "%d", i);
1962 bufp += strlen (bufp);
1963 *bufp++ = '.';
1965 f = floor (d * 1000000.0);
1966 sprintf (bufp, "%06d", f);
1967 return buf;
1970 double
1971 c_strtod (const char *s)
1973 double rv = 0;
1974 double sign = 1.0;
1975 double scale;
1977 /* leading whitespace */
1978 while (*s && (*s == ' ' || *s == '\t'))
1979 s++;
1981 /* optional sign */
1982 if (*s == '-')
1984 sign = -1.0;
1985 s++;
1987 else if (*s == '+')
1988 s++;
1990 /* integer portion */
1991 while (*s >= '0' && *s <= '9')
1993 rv *= 10.0;
1994 rv += *s - '0';
1995 s++;
1998 /* fractional portion */
1999 if (*s == '.')
2001 s++;
2002 scale = 0.1;
2003 while (*s >= '0' && *s <= '9')
2005 rv += (*s - '0') * scale;
2006 scale *= 0.1;
2007 s++;
2011 /* exponent */
2012 if (*s == 'E' || *s == 'e')
2014 int e;
2015 if (sscanf (s + 1, "%d", &e) == 1)
2017 scale = 1.0;
2018 while (e > 0)
2020 scale *= 10.0;
2021 e--;
2023 while (e < 0)
2025 scale *= 0.1;
2026 e++;
2028 rv *= scale;
2032 return rv * sign;
2035 void
2036 r_delete_element (DataType * data, ElementType * element)
2038 r_delete_entry (data->element_tree, (BoxType *) element);
2039 PIN_LOOP (element);
2041 r_delete_entry (data->pin_tree, (BoxType *) pin);
2043 END_LOOP;
2044 PAD_LOOP (element);
2046 r_delete_entry (data->pad_tree, (BoxType *) pad);
2048 END_LOOP;
2049 ELEMENTTEXT_LOOP (element);
2051 r_delete_entry (data->name_tree[n], (BoxType *) text);
2053 END_LOOP;
2057 /* ---------------------------------------------------------------------------
2058 * Returns a string that has a bunch of information about the program.
2059 * Can be used for things like "about" dialog boxes.
2062 char *
2063 GetInfoString (void)
2065 HID **hids;
2066 int i;
2067 static DynamicStringType info;
2068 static int first_time = 1;
2070 #define TAB " "
2072 if (first_time)
2074 first_time = 0;
2075 DSAddString (&info, "This is PCB, an interactive\n");
2076 DSAddString (&info, "printed circuit board editor\n");
2077 DSAddString (&info, "version ");
2078 DSAddString (&info, VERSION);
2079 DSAddString (&info, "\n\n");
2080 DSAddString (&info, "Compiled on " __DATE__ " at " __TIME__);
2081 DSAddString (&info, "\n\n" "by harry eaton\n\n");
2082 DSAddString (&info,
2083 "Copyright (C) Thomas Nau 1994, 1995, 1996, 1997\n");
2084 DSAddString (&info, "Copyright (C) harry eaton 1998-2007\n");
2085 DSAddString (&info, "Copyright (C) C. Scott Ananian 2001\n");
2086 DSAddString (&info,
2087 "Copyright (C) DJ Delorie 2003, 2004, 2005, 2006, 2007, 2008\n");
2088 DSAddString (&info,
2089 "Copyright (C) Dan McMahill 2003, 2004, 2005, 2006, 2007, 2008\n\n");
2090 DSAddString (&info, "It is licensed under the terms of the GNU\n");
2091 DSAddString (&info, "General Public License version 2\n");
2092 DSAddString (&info, "See the LICENSE file for more information\n\n");
2093 DSAddString (&info, "For more information see:\n\n");
2094 DSAddString (&info, "PCB homepage: http://pcb.gpleda.org\n");
2095 DSAddString (&info, "gEDA homepage: http://www.gpleda.org\n");
2096 DSAddString (&info,
2097 "gEDA Wiki: http://geda.seul.org/wiki/ \n\n");
2099 DSAddString (&info, "----- Compile Time Options -----\n");
2100 hids = hid_enumerate ();
2101 DSAddString (&info, "GUI:\n");
2102 for (i = 0; hids[i]; i++)
2104 if (hids[i]->gui)
2106 DSAddString (&info, TAB);
2107 DSAddString (&info, hids[i]->name);
2108 DSAddString (&info, " : ");
2109 DSAddString (&info, hids[i]->description);
2110 DSAddString (&info, "\n");
2114 DSAddString (&info, "Exporters:\n");
2115 for (i = 0; hids[i]; i++)
2117 if (hids[i]->exporter)
2119 DSAddString (&info, TAB);
2120 DSAddString (&info, hids[i]->name);
2121 DSAddString (&info, " : ");
2122 DSAddString (&info, hids[i]->description);
2123 DSAddString (&info, "\n");
2127 DSAddString (&info, "Printers:\n");
2128 for (i = 0; hids[i]; i++)
2130 if (hids[i]->printer)
2132 DSAddString (&info, TAB);
2133 DSAddString (&info, hids[i]->name);
2134 DSAddString (&info, " : ");
2135 DSAddString (&info, hids[i]->description);
2136 DSAddString (&info, "\n");
2140 #undef TAB
2142 return info.Data;
2145 /* ---------------------------------------------------------------------------
2146 * Returns a best guess about the orientation of an element. The
2147 * value corresponds to the rotation; a difference is the right value
2148 * to pass to RotateElementLowLevel. However, the actual value is no
2149 * indication of absolute rotation; only relative rotation is
2150 * meaningful.
2153 int
2154 ElementOrientation (ElementType *e)
2156 int pin1x, pin1y, pin2x, pin2y, dx, dy;
2157 int found_pin1 = 0;
2158 int found_pin2 = 0;
2160 /* in case we don't find pin 1 or 2, make sure we have initialized these variables */
2161 pin1x = 0;
2162 pin1y = 0;
2163 pin2x = 0;
2164 pin2y = 0;
2166 PIN_LOOP (e);
2168 if (NSTRCMP (pin->Number, "1") == 0)
2170 pin1x = pin->X;
2171 pin1y = pin->Y;
2172 found_pin1 = 1;
2174 else if (NSTRCMP (pin->Number, "2") == 0)
2176 pin2x = pin->X;
2177 pin2y = pin->Y;
2178 found_pin2 = 1;
2181 END_LOOP;
2183 PAD_LOOP (e);
2185 if (NSTRCMP (pad->Number, "1") == 0)
2187 pin1x = (pad->Point1.X + pad->Point2.X) / 2;
2188 pin1y = (pad->Point1.Y + pad->Point2.Y) / 2;
2189 found_pin1 = 1;
2191 else if (NSTRCMP (pad->Number, "2") == 0)
2193 pin2x = (pad->Point1.X + pad->Point2.X) / 2;
2194 pin2y = (pad->Point1.Y + pad->Point2.Y) / 2;
2195 found_pin2 = 1;
2198 END_LOOP;
2200 if (found_pin1 && found_pin2)
2202 dx = pin2x - pin1x;
2203 dy = pin2y - pin1y;
2205 else if (found_pin1 && (pin1x || pin1y))
2207 dx = pin1x;
2208 dy = pin1y;
2210 else if (found_pin2 && (pin2x || pin2y))
2212 dx = pin2x;
2213 dy = pin2y;
2215 else
2216 return 0;
2218 if (abs(dx) > abs(dy))
2219 return dx > 0 ? 0 : 2;
2220 return dy > 0 ? 3 : 1;
2224 ActionListRotations(int argc, char **argv, int x, int y)
2226 ELEMENT_LOOP (PCB->Data);
2228 printf("%d %s\n", ElementOrientation(element), NAMEONPCB_NAME(element));
2230 END_LOOP;
2232 return 0;
2235 HID_Action misc_action_list[] = {
2236 {"ListRotations", 0, ActionListRotations,
2237 0,0},
2240 REGISTER_ACTIONS (misc_action_list)