(no commit message)
[geda-pcb/pcjc2.git] / src / misc.c
blob9a4d9ceab6c09ca8399d7f3161a8bdf628a55640
1 /*
2 * COPYRIGHT
4 * PCB, interactive printed circuit board design
5 * Copyright (C) 1994,1995,1996,2004,2006 Thomas Nau
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 * Contact addresses for paper mail and Email:
22 * Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
23 * Thomas.Nau@rz.uni-ulm.de
28 /* misc functions used by several modules
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
35 #include <stdlib.h>
36 #include <stdarg.h>
37 #ifdef HAVE_STRING_H
38 #include <string.h>
39 #endif
40 #include <memory.h>
41 #include <ctype.h>
42 #include <signal.h>
43 #include <sys/param.h>
44 #include <sys/stat.h>
45 #include <sys/types.h>
46 #include <math.h>
47 #ifdef HAVE_UNISTD_H
48 #include <unistd.h>
49 #endif
50 #ifdef HAVE_PWD_H
51 #include <pwd.h>
52 #endif
54 #include "global.h"
56 #include "box.h"
57 #include "crosshair.h"
58 #include "create.h"
59 #include "data.h"
60 #include "draw.h"
61 #include "file.h"
62 #include "error.h"
63 #include "mymem.h"
64 #include "misc.h"
65 #include "move.h"
66 #include "pcb-printf.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 /* forward declarations */
82 static char *BumpName (char *);
83 static void GetGridLockCoordinates (int, void *, void *, void *,
84 Coord *, Coord *);
87 /* Local variables */
89 /*
90 * Used by SaveStackAndVisibility() and
91 * RestoreStackAndVisibility()
94 static struct
96 bool ElementOn, InvisibleObjectsOn, PinOn, ViaOn, RatOn;
97 int LayerStack[MAX_LAYER];
98 bool LayerOn[MAX_LAYER];
99 int cnt;
100 } SavedStack;
102 /* Distance() should be used so that there is only one
103 * place to deal with overflow/precision errors
105 double
106 Distance (double x1, double y1, double x2, double y2)
108 double delta_x = (x2 - x1);
109 double delta_y = (y2 - y1);
110 return sqrt(delta_x * delta_x + delta_y * delta_y);
113 /* Bring an angle into [0, 360) range */
114 Angle
115 NormalizeAngle (Angle a)
117 while (a < 0)
118 a += 360.0;
119 while (a >= 360.0)
120 a -= 360.0;
121 return a;
124 /* Get Value returns a numeric value passed from the string and sets the
125 * bool variable absolute to false if it leads with a +/- character
127 double
128 GetValue (const char *val, const char *units, bool * absolute)
130 return GetValueEx(val, units, absolute, NULL, "cmil");
133 double
134 GetValueEx (const char *val, const char *units, bool * absolute, UnitList extra_units, const char *default_unit)
136 double value;
137 int n = -1;
138 bool scaled = 0;
139 bool dummy;
141 /* Allow NULL to be passed for absolute */
142 if(absolute == NULL)
143 absolute = &dummy;
145 /* if the first character is a sign we have to add the
146 * value to the current one
148 if (*val == '=')
150 *absolute = true;
151 if (sscanf (val+1, "%lf%n", &value, &n) < 1)
152 return 0;
153 n++;
155 else
157 if (isdigit ((int) *val))
158 *absolute = true;
159 else
160 *absolute = false;
161 if (sscanf (val, "%lf%n", &value, &n) < 1)
162 return 0;
164 if (!units && n > 0)
165 units = val + n;
167 while (units && *units == ' ')
168 units ++;
170 if (units && *units)
172 int i;
173 const Unit *unit = get_unit_struct (units);
174 if (unit != NULL)
176 value = unit_to_coord (unit, value);
177 scaled = 1;
179 if (extra_units)
181 for (i = 0; *extra_units[i].suffix; ++i)
183 if (strncmp (units, extra_units[i].suffix, strlen(extra_units[i].suffix)) == 0)
185 value *= extra_units[i].scale;
186 if (extra_units[i].flags & UNIT_PERCENT)
187 value /= 100.0;
188 scaled = 1;
193 /* Apply default unit */
194 if (!scaled && default_unit && *default_unit)
196 int i;
197 const Unit *unit = get_unit_struct (default_unit);
198 if (extra_units)
199 for (i = 0; *extra_units[i].suffix; ++i)
200 if (strcmp (extra_units[i].suffix, default_unit) == 0)
202 value *= extra_units[i].scale;
203 if (extra_units[i].flags & UNIT_PERCENT)
204 value /= 100.0;
205 scaled = 1;
207 if (!scaled && unit != NULL)
208 value = unit_to_coord (unit, value);
211 return value;
214 /* ---------------------------------------------------------------------------
215 * sets the bounding box of a point (which is silly)
217 void
218 SetPointBoundingBox (PointType *Pnt)
220 Pnt->X2 = Pnt->X + 1;
221 Pnt->Y2 = Pnt->Y + 1;
224 /* ---------------------------------------------------------------------------
225 * sets the bounding box of a pin or via
227 void
228 SetPinBoundingBox (PinType *Pin)
230 Coord width;
232 /* the bounding box covers the extent of influence
233 * so it must include the clearance values too
235 width = MAX (Pin->Clearance + PIN_SIZE (Pin), Pin->Mask) / 2;
237 /* Adjust for our discrete polygon approximation */
238 width = (double)width * POLY_CIRC_RADIUS_ADJ + 0.5;
240 Pin->BoundingBox.X1 = Pin->X - width;
241 Pin->BoundingBox.Y1 = Pin->Y - width;
242 Pin->BoundingBox.X2 = Pin->X + width;
243 Pin->BoundingBox.Y2 = Pin->Y + width;
244 close_box(&Pin->BoundingBox);
247 /* ---------------------------------------------------------------------------
248 * sets the bounding box of a pad
250 void
251 SetPadBoundingBox (PadType *Pad)
253 Coord width;
254 Coord deltax;
255 Coord deltay;
257 /* the bounding box covers the extent of influence
258 * so it must include the clearance values too
260 width = (Pad->Thickness + Pad->Clearance + 1) / 2;
261 width = MAX (width, (Pad->Mask + 1) / 2);
262 deltax = Pad->Point2.X - Pad->Point1.X;
263 deltay = Pad->Point2.Y - Pad->Point1.Y;
265 if (TEST_FLAG (SQUAREFLAG, Pad) && deltax != 0 && deltay != 0)
267 /* slanted square pad */
268 double theta;
269 Coord btx, bty;
271 /* T is a vector half a thickness long, in the direction of
272 one of the corners. */
273 theta = atan2 (deltay, deltax);
274 btx = width * cos (theta + M_PI/4) * sqrt(2.0);
275 bty = width * sin (theta + M_PI/4) * sqrt(2.0);
278 Pad->BoundingBox.X1 = MIN (MIN (Pad->Point1.X - btx, Pad->Point1.X - bty),
279 MIN (Pad->Point2.X + btx, Pad->Point2.X + bty));
280 Pad->BoundingBox.X2 = MAX (MAX (Pad->Point1.X - btx, Pad->Point1.X - bty),
281 MAX (Pad->Point2.X + btx, Pad->Point2.X + bty));
282 Pad->BoundingBox.Y1 = MIN (MIN (Pad->Point1.Y + btx, Pad->Point1.Y - bty),
283 MIN (Pad->Point2.Y - btx, Pad->Point2.Y + bty));
284 Pad->BoundingBox.Y2 = MAX (MAX (Pad->Point1.Y + btx, Pad->Point1.Y - bty),
285 MAX (Pad->Point2.Y - btx, Pad->Point2.Y + bty));
287 else
289 /* Adjust for our discrete polygon approximation */
290 width = (double)width * POLY_CIRC_RADIUS_ADJ + 0.5;
292 Pad->BoundingBox.X1 = MIN (Pad->Point1.X, Pad->Point2.X) - width;
293 Pad->BoundingBox.X2 = MAX (Pad->Point1.X, Pad->Point2.X) + width;
294 Pad->BoundingBox.Y1 = MIN (Pad->Point1.Y, Pad->Point2.Y) - width;
295 Pad->BoundingBox.Y2 = MAX (Pad->Point1.Y, Pad->Point2.Y) + width;
297 close_box(&Pad->BoundingBox);
300 /* ---------------------------------------------------------------------------
301 * sets the bounding box of a line
303 void
304 SetLineBoundingBox (LineType *Line)
306 Coord width = (Line->Thickness + Line->Clearance + 1) / 2;
308 /* Adjust for our discrete polygon approximation */
309 width = (double)width * POLY_CIRC_RADIUS_ADJ + 0.5;
311 Line->BoundingBox.X1 = MIN (Line->Point1.X, Line->Point2.X) - width;
312 Line->BoundingBox.X2 = MAX (Line->Point1.X, Line->Point2.X) + width;
313 Line->BoundingBox.Y1 = MIN (Line->Point1.Y, Line->Point2.Y) - width;
314 Line->BoundingBox.Y2 = MAX (Line->Point1.Y, Line->Point2.Y) + width;
315 close_box(&Line->BoundingBox);
316 SetPointBoundingBox (&Line->Point1);
317 SetPointBoundingBox (&Line->Point2);
320 /* ---------------------------------------------------------------------------
321 * sets the bounding box of a polygons
323 void
324 SetPolygonBoundingBox (PolygonType *Polygon)
326 Polygon->BoundingBox.X1 = Polygon->BoundingBox.Y1 = MAX_COORD;
327 Polygon->BoundingBox.X2 = Polygon->BoundingBox.Y2 = 0;
328 POLYGONPOINT_LOOP (Polygon);
330 MAKEMIN (Polygon->BoundingBox.X1, point->X);
331 MAKEMIN (Polygon->BoundingBox.Y1, point->Y);
332 MAKEMAX (Polygon->BoundingBox.X2, point->X);
333 MAKEMAX (Polygon->BoundingBox.Y2, point->Y);
335 /* boxes don't include the lower right corner */
336 close_box(&Polygon->BoundingBox);
337 END_LOOP;
340 /* ---------------------------------------------------------------------------
341 * sets the bounding box of an elements
343 void
344 SetElementBoundingBox (DataType *Data, ElementType *Element,
345 FontType *Font)
347 BoxType *box, *vbox;
349 if (Data && Data->element_tree)
350 r_delete_entry (Data->element_tree, (BoxType *) Element);
351 /* first update the text objects */
352 ELEMENTTEXT_LOOP (Element);
354 if (Data && Data->name_tree[n])
355 r_delete_entry (Data->name_tree[n], (BoxType *) text);
356 SetTextBoundingBox (Font, text);
357 if (Data && !Data->name_tree[n])
358 Data->name_tree[n] = r_create_tree (NULL, 0, 0);
359 if (Data)
360 r_insert_entry (Data->name_tree[n], (BoxType *) text, 0);
362 END_LOOP;
364 /* do not include the elementnames bounding box which
365 * is handled separately
367 box = &Element->BoundingBox;
368 vbox = &Element->VBox;
369 box->X1 = box->Y1 = MAX_COORD;
370 box->X2 = box->Y2 = 0;
371 ELEMENTLINE_LOOP (Element);
373 SetLineBoundingBox (line);
374 MAKEMIN (box->X1, line->Point1.X - (line->Thickness + 1) / 2);
375 MAKEMIN (box->Y1, line->Point1.Y - (line->Thickness + 1) / 2);
376 MAKEMIN (box->X1, line->Point2.X - (line->Thickness + 1) / 2);
377 MAKEMIN (box->Y1, line->Point2.Y - (line->Thickness + 1) / 2);
378 MAKEMAX (box->X2, line->Point1.X + (line->Thickness + 1) / 2);
379 MAKEMAX (box->Y2, line->Point1.Y + (line->Thickness + 1) / 2);
380 MAKEMAX (box->X2, line->Point2.X + (line->Thickness + 1) / 2);
381 MAKEMAX (box->Y2, line->Point2.Y + (line->Thickness + 1) / 2);
383 END_LOOP;
384 ARC_LOOP (Element);
386 SetArcBoundingBox (arc);
387 MAKEMIN (box->X1, arc->BoundingBox.X1);
388 MAKEMIN (box->Y1, arc->BoundingBox.Y1);
389 MAKEMAX (box->X2, arc->BoundingBox.X2);
390 MAKEMAX (box->Y2, arc->BoundingBox.Y2);
392 END_LOOP;
393 *vbox = *box;
394 PIN_LOOP (Element);
396 if (Data && Data->pin_tree)
397 r_delete_entry (Data->pin_tree, (BoxType *) pin);
398 SetPinBoundingBox (pin);
399 if (Data)
401 if (!Data->pin_tree)
402 Data->pin_tree = r_create_tree (NULL, 0, 0);
403 r_insert_entry (Data->pin_tree, (BoxType *) pin, 0);
405 MAKEMIN (box->X1, pin->BoundingBox.X1);
406 MAKEMIN (box->Y1, pin->BoundingBox.Y1);
407 MAKEMAX (box->X2, pin->BoundingBox.X2);
408 MAKEMAX (box->Y2, pin->BoundingBox.Y2);
409 MAKEMIN (vbox->X1, pin->X - pin->Thickness / 2);
410 MAKEMIN (vbox->Y1, pin->Y - pin->Thickness / 2);
411 MAKEMAX (vbox->X2, pin->X + pin->Thickness / 2);
412 MAKEMAX (vbox->Y2, pin->Y + pin->Thickness / 2);
414 END_LOOP;
415 PAD_LOOP (Element);
417 if (Data && Data->pad_tree)
418 r_delete_entry (Data->pad_tree, (BoxType *) pad);
419 SetPadBoundingBox (pad);
420 if (Data)
422 if (!Data->pad_tree)
423 Data->pad_tree = r_create_tree (NULL, 0, 0);
424 r_insert_entry (Data->pad_tree, (BoxType *) pad, 0);
426 MAKEMIN (box->X1, pad->BoundingBox.X1);
427 MAKEMIN (box->Y1, pad->BoundingBox.Y1);
428 MAKEMAX (box->X2, pad->BoundingBox.X2);
429 MAKEMAX (box->Y2, pad->BoundingBox.Y2);
430 MAKEMIN (vbox->X1,
431 MIN (pad->Point1.X, pad->Point2.X) - pad->Thickness / 2);
432 MAKEMIN (vbox->Y1,
433 MIN (pad->Point1.Y, pad->Point2.Y) - pad->Thickness / 2);
434 MAKEMAX (vbox->X2,
435 MAX (pad->Point1.X, pad->Point2.X) + pad->Thickness / 2);
436 MAKEMAX (vbox->Y2,
437 MAX (pad->Point1.Y, pad->Point2.Y) + pad->Thickness / 2);
439 END_LOOP;
440 /* now we set the EDGE2FLAG of the pad if Point2
441 * is closer to the outside edge than Point1
443 PAD_LOOP (Element);
445 if (pad->Point1.Y == pad->Point2.Y)
447 /* horizontal pad */
448 if (box->X2 - pad->Point2.X < pad->Point1.X - box->X1)
449 SET_FLAG (EDGE2FLAG, pad);
450 else
451 CLEAR_FLAG (EDGE2FLAG, pad);
453 else
455 /* vertical pad */
456 if (box->Y2 - pad->Point2.Y < pad->Point1.Y - box->Y1)
457 SET_FLAG (EDGE2FLAG, pad);
458 else
459 CLEAR_FLAG (EDGE2FLAG, pad);
462 END_LOOP;
464 /* mark pins with component orientation */
465 if ((box->X2 - box->X1) > (box->Y2 - box->Y1))
467 PIN_LOOP (Element);
469 SET_FLAG (EDGE2FLAG, pin);
471 END_LOOP;
473 else
475 PIN_LOOP (Element);
477 CLEAR_FLAG (EDGE2FLAG, pin);
479 END_LOOP;
481 close_box(box);
482 close_box(vbox);
483 if (Data && !Data->element_tree)
484 Data->element_tree = r_create_tree (NULL, 0, 0);
485 if (Data)
486 r_insert_entry (Data->element_tree, box, 0);
489 /* ---------------------------------------------------------------------------
490 * creates the bounding box of a text object
492 void
493 SetTextBoundingBox (FontType *FontPtr, TextType *Text)
495 SymbolType *symbol = FontPtr->Symbol;
496 unsigned char *s = (unsigned char *) Text->TextString;
497 int i;
498 int space;
500 Coord minx, miny, maxx, maxy, tx;
501 Coord min_final_radius;
502 Coord min_unscaled_radius;
503 bool first_time = true;
505 minx = miny = maxx = maxy = tx = 0;
507 /* Calculate the bounding box based on the larger of the thicknesses
508 * the text might clamped at on silk or copper layers.
510 min_final_radius = MAX (PCB->minWid, PCB->minSlk) / 2;
512 /* Pre-adjust the line radius for the fact we are initially computing the
513 * bounds of the un-scaled text, and the thickness clamping applies to
514 * scaled text.
516 min_unscaled_radius = UNSCALE_TEXT (min_final_radius, Text->Scale);
518 /* calculate size of the bounding box */
519 for (; s && *s; s++)
521 if (*s <= MAX_FONTPOSITION && symbol[*s].Valid)
523 LineType *line = symbol[*s].Line;
524 for (i = 0; i < symbol[*s].LineN; line++, i++)
526 /* Clamp the width of text lines at the minimum thickness.
527 * NB: Divide 4 in thickness calculation is comprised of a factor
528 * of 1/2 to get a radius from the center-line, and a factor
529 * of 1/2 because some stupid reason we render our glyphs
530 * at half their defined stroke-width.
532 Coord unscaled_radius = MAX (min_unscaled_radius, line->Thickness / 4);
534 if (first_time)
536 minx = maxx = line->Point1.X;
537 miny = maxy = line->Point1.Y;
538 first_time = false;
541 minx = MIN (minx, line->Point1.X - unscaled_radius + tx);
542 miny = MIN (miny, line->Point1.Y - unscaled_radius);
543 minx = MIN (minx, line->Point2.X - unscaled_radius + tx);
544 miny = MIN (miny, line->Point2.Y - unscaled_radius);
545 maxx = MAX (maxx, line->Point1.X + unscaled_radius + tx);
546 maxy = MAX (maxy, line->Point1.Y + unscaled_radius);
547 maxx = MAX (maxx, line->Point2.X + unscaled_radius + tx);
548 maxy = MAX (maxy, line->Point2.Y + unscaled_radius);
550 space = symbol[*s].Delta;
552 else
554 BoxType *ds = &FontPtr->DefaultSymbol;
555 Coord w = ds->X2 - ds->X1;
557 minx = MIN (minx, ds->X1 + tx);
558 miny = MIN (miny, ds->Y1);
559 minx = MIN (minx, ds->X2 + tx);
560 miny = MIN (miny, ds->Y2);
561 maxx = MAX (maxx, ds->X1 + tx);
562 maxy = MAX (maxy, ds->Y1);
563 maxx = MAX (maxx, ds->X2 + tx);
564 maxy = MAX (maxy, ds->Y2);
566 space = w / 5;
568 tx += symbol[*s].Width + space;
571 /* scale values */
572 minx = SCALE_TEXT (minx, Text->Scale);
573 miny = SCALE_TEXT (miny, Text->Scale);
574 maxx = SCALE_TEXT (maxx, Text->Scale);
575 maxy = SCALE_TEXT (maxy, Text->Scale);
577 /* set upper-left and lower-right corner;
578 * swap coordinates if necessary (origin is already in 'swapped')
579 * and rotate box
582 if (TEST_FLAG (ONSOLDERFLAG, Text))
584 Text->BoundingBox.X1 = Text->X + minx;
585 Text->BoundingBox.Y1 = Text->Y - miny;
586 Text->BoundingBox.X2 = Text->X + maxx;
587 Text->BoundingBox.Y2 = Text->Y - maxy;
588 RotateBoxLowLevel (&Text->BoundingBox, Text->X, Text->Y,
589 (4 - Text->Direction) & 0x03);
591 else
593 Text->BoundingBox.X1 = Text->X + minx;
594 Text->BoundingBox.Y1 = Text->Y + miny;
595 Text->BoundingBox.X2 = Text->X + maxx;
596 Text->BoundingBox.Y2 = Text->Y + maxy;
597 RotateBoxLowLevel (&Text->BoundingBox,
598 Text->X, Text->Y, Text->Direction);
601 /* the bounding box covers the extent of influence
602 * so it must include the clearance values too
604 Text->BoundingBox.X1 -= PCB->Bloat;
605 Text->BoundingBox.Y1 -= PCB->Bloat;
606 Text->BoundingBox.X2 += PCB->Bloat;
607 Text->BoundingBox.Y2 += PCB->Bloat;
608 close_box(&Text->BoundingBox);
611 /* ---------------------------------------------------------------------------
612 * returns true if data area is empty
614 bool
615 IsDataEmpty (DataType *Data)
617 bool hasNoObjects;
618 Cardinal i;
620 hasNoObjects = (Data->ViaN == 0);
621 hasNoObjects &= (Data->ElementN == 0);
622 for (i = 0; i < max_copper_layer + 2; i++)
623 hasNoObjects = hasNoObjects &&
624 Data->Layer[i].LineN == 0 &&
625 Data->Layer[i].ArcN == 0 &&
626 Data->Layer[i].TextN == 0 && Data->Layer[i].PolygonN == 0;
627 return (hasNoObjects);
631 FlagIsDataEmpty (int parm)
633 int i = IsDataEmpty (PCB->Data);
634 return parm ? !i : i;
637 /* FLAG(DataEmpty,FlagIsDataEmpty,0) */
638 /* FLAG(DataNonEmpty,FlagIsDataEmpty,1) */
640 bool
641 IsLayerEmpty (LayerType *layer)
643 return (layer->LineN == 0
644 && layer->TextN == 0
645 && layer->PolygonN == 0
646 && layer->ArcN == 0);
649 bool
650 IsLayerNumEmpty (int num)
652 return IsLayerEmpty (PCB->Data->Layer+num);
655 bool
656 IsLayerGroupEmpty (int num)
658 int i;
659 for (i=0; i<PCB->LayerGroups.Number[num]; i++)
660 if (!IsLayerNumEmpty (PCB->LayerGroups.Entries[num][i]))
661 return false;
662 return true;
665 bool
666 IsPasteEmpty (int side)
668 bool paste_empty = true;
669 ALLPAD_LOOP (PCB->Data);
671 if (ON_SIDE (pad, side) && !TEST_FLAG (NOPASTEFLAG, pad) && pad->Mask > 0)
673 paste_empty = false;
674 break;
677 ENDALL_LOOP;
678 return paste_empty;
682 typedef struct
684 int nplated;
685 int nunplated;
686 } HoleCountStruct;
688 static int
689 hole_counting_callback (const BoxType * b, void *cl)
691 PinType *pin = (PinType *) b;
692 HoleCountStruct *hcs = (HoleCountStruct *) cl;
693 if (TEST_FLAG (HOLEFLAG, pin))
694 hcs->nunplated++;
695 else
696 hcs->nplated++;
697 return 1;
700 /* ---------------------------------------------------------------------------
701 * counts the number of plated and unplated holes in the design within
702 * a given area of the board. To count for the whole board, pass NULL
703 * within_area.
705 void
706 CountHoles (int *plated, int *unplated, const BoxType *within_area)
708 HoleCountStruct hcs = {0, 0};
710 r_search (PCB->Data->pin_tree, within_area, NULL, hole_counting_callback, &hcs);
711 r_search (PCB->Data->via_tree, within_area, NULL, hole_counting_callback, &hcs);
713 if (plated != NULL) *plated = hcs.nplated;
714 if (unplated != NULL) *unplated = hcs.nunplated;
718 /* ---------------------------------------------------------------------------
719 * gets minimum and maximum coordinates
720 * returns NULL if layout is empty
722 BoxType *
723 GetDataBoundingBox (DataType *Data)
725 static BoxType box;
726 /* FIX ME: use r_search to do this much faster */
728 /* preset identifiers with highest and lowest possible values */
729 box.X1 = box.Y1 = MAX_COORD;
730 box.X2 = box.Y2 = -MAX_COORD;
732 /* now scan for the lowest/highest X and Y coordinate */
733 VIA_LOOP (Data);
735 box.X1 = MIN (box.X1, via->X - via->Thickness / 2);
736 box.Y1 = MIN (box.Y1, via->Y - via->Thickness / 2);
737 box.X2 = MAX (box.X2, via->X + via->Thickness / 2);
738 box.Y2 = MAX (box.Y2, via->Y + via->Thickness / 2);
740 END_LOOP;
741 ELEMENT_LOOP (Data);
743 box.X1 = MIN (box.X1, element->BoundingBox.X1);
744 box.Y1 = MIN (box.Y1, element->BoundingBox.Y1);
745 box.X2 = MAX (box.X2, element->BoundingBox.X2);
746 box.Y2 = MAX (box.Y2, element->BoundingBox.Y2);
748 TextType *text = &NAMEONPCB_TEXT (element);
749 box.X1 = MIN (box.X1, text->BoundingBox.X1);
750 box.Y1 = MIN (box.Y1, text->BoundingBox.Y1);
751 box.X2 = MAX (box.X2, text->BoundingBox.X2);
752 box.Y2 = MAX (box.Y2, text->BoundingBox.Y2);
755 END_LOOP;
756 ALLLINE_LOOP (Data);
758 box.X1 = MIN (box.X1, line->Point1.X - line->Thickness / 2);
759 box.Y1 = MIN (box.Y1, line->Point1.Y - line->Thickness / 2);
760 box.X1 = MIN (box.X1, line->Point2.X - line->Thickness / 2);
761 box.Y1 = MIN (box.Y1, line->Point2.Y - line->Thickness / 2);
762 box.X2 = MAX (box.X2, line->Point1.X + line->Thickness / 2);
763 box.Y2 = MAX (box.Y2, line->Point1.Y + line->Thickness / 2);
764 box.X2 = MAX (box.X2, line->Point2.X + line->Thickness / 2);
765 box.Y2 = MAX (box.Y2, line->Point2.Y + line->Thickness / 2);
767 ENDALL_LOOP;
768 ALLARC_LOOP (Data);
770 box.X1 = MIN (box.X1, arc->BoundingBox.X1);
771 box.Y1 = MIN (box.Y1, arc->BoundingBox.Y1);
772 box.X2 = MAX (box.X2, arc->BoundingBox.X2);
773 box.Y2 = MAX (box.Y2, arc->BoundingBox.Y2);
775 ENDALL_LOOP;
776 ALLTEXT_LOOP (Data);
778 box.X1 = MIN (box.X1, text->BoundingBox.X1);
779 box.Y1 = MIN (box.Y1, text->BoundingBox.Y1);
780 box.X2 = MAX (box.X2, text->BoundingBox.X2);
781 box.Y2 = MAX (box.Y2, text->BoundingBox.Y2);
783 ENDALL_LOOP;
784 ALLPOLYGON_LOOP (Data);
786 box.X1 = MIN (box.X1, polygon->BoundingBox.X1);
787 box.Y1 = MIN (box.Y1, polygon->BoundingBox.Y1);
788 box.X2 = MAX (box.X2, polygon->BoundingBox.X2);
789 box.Y2 = MAX (box.Y2, polygon->BoundingBox.Y2);
791 ENDALL_LOOP;
792 return (IsDataEmpty (Data) ? NULL : &box);
795 /* ---------------------------------------------------------------------------
796 * centers the displayed PCB around the specified point (X,Y)
798 void
799 CenterDisplay (Coord X, Coord Y)
801 Coord save_grid = PCB->Grid;
802 PCB->Grid = 1;
803 if (MoveCrosshairAbsolute (X, Y))
804 notify_crosshair_change (true);
805 gui->set_crosshair (Crosshair.X, Crosshair.Y, HID_SC_WARP_POINTER);
806 PCB->Grid = save_grid;
809 /* ---------------------------------------------------------------------------
810 * transforms symbol coordinates so that the left edge of each symbol
811 * is at the zero position. The y coordinates are moved so that min(y) = 0
814 void
815 SetFontInfo (FontType *Ptr)
817 Cardinal i, j;
818 SymbolType *symbol;
819 LineType *line;
820 Coord totalminy = MAX_COORD;
822 /* calculate cell with and height (is at least DEFAULT_CELLSIZE)
823 * maximum cell width and height
824 * minimum x and y position of all lines
826 Ptr->MaxWidth = DEFAULT_CELLSIZE;
827 Ptr->MaxHeight = DEFAULT_CELLSIZE;
828 for (i = 0, symbol = Ptr->Symbol; i <= MAX_FONTPOSITION; i++, symbol++)
830 Coord minx, miny, maxx, maxy;
832 /* next one if the index isn't used or symbol is empty (SPACE) */
833 if (!symbol->Valid || !symbol->LineN)
834 continue;
836 minx = miny = MAX_COORD;
837 maxx = maxy = 0;
838 for (line = symbol->Line, j = symbol->LineN; j; j--, line++)
840 minx = MIN (minx, line->Point1.X);
841 miny = MIN (miny, line->Point1.Y);
842 minx = MIN (minx, line->Point2.X);
843 miny = MIN (miny, line->Point2.Y);
844 maxx = MAX (maxx, line->Point1.X);
845 maxy = MAX (maxy, line->Point1.Y);
846 maxx = MAX (maxx, line->Point2.X);
847 maxy = MAX (maxy, line->Point2.Y);
850 /* move symbol to left edge */
851 for (line = symbol->Line, j = symbol->LineN; j; j--, line++)
852 MOVE_LINE_LOWLEVEL (line, -minx, 0);
854 /* set symbol bounding box with a minimum cell size of (1,1) */
855 symbol->Width = maxx - minx + 1;
856 symbol->Height = maxy + 1;
858 /* check total min/max */
859 Ptr->MaxWidth = MAX (Ptr->MaxWidth, symbol->Width);
860 Ptr->MaxHeight = MAX (Ptr->MaxHeight, symbol->Height);
861 totalminy = MIN (totalminy, miny);
864 /* move coordinate system to the upper edge (lowest y on screen) */
865 for (i = 0, symbol = Ptr->Symbol; i <= MAX_FONTPOSITION; i++, symbol++)
866 if (symbol->Valid)
868 symbol->Height -= totalminy;
869 for (line = symbol->Line, j = symbol->LineN; j; j--, line++)
870 MOVE_LINE_LOWLEVEL (line, 0, -totalminy);
873 /* setup the box for the default symbol */
874 Ptr->DefaultSymbol.X1 = Ptr->DefaultSymbol.Y1 = 0;
875 Ptr->DefaultSymbol.X2 = Ptr->DefaultSymbol.X1 + Ptr->MaxWidth;
876 Ptr->DefaultSymbol.Y2 = Ptr->DefaultSymbol.Y1 + Ptr->MaxHeight;
879 static Coord
880 GetNum (char **s, const char *default_unit)
882 /* Read value */
883 Coord ret_val = GetValueEx (*s, NULL, NULL, NULL, default_unit);
884 /* Advance pointer */
885 while(isalnum(**s) || **s == '.')
886 (*s)++;
887 return ret_val;
890 /*! \brief Serializes the route style list
891 * \par Function Description
892 * Right now n_styles should always be set to NUM_STYLES,
893 * since that is the number of route styles ParseRouteString()
894 * expects to parse.
896 char *
897 make_route_string (RouteStyleType rs[], int n_styles)
899 GString *str = g_string_new ("");
900 gint i;
902 for (i = 0; i < n_styles; ++i)
904 char *r_string = pcb_g_strdup_printf ("%s,%mc,%mc,%mc,%mc", rs[i].Name,
905 rs[i].Thick, rs[i].Diameter,
906 rs[i].Hole, rs[i].Keepaway);
907 if (i > 0)
908 g_string_append_c (str, ':');
909 g_string_append (str, r_string);
911 return g_string_free (str, FALSE);
914 /* ----------------------------------------------------------------------
915 * parses the routes definition string which is a colon separated list of
916 * comma separated Name, Dimension, Dimension, Dimension, Dimension
917 * e.g. Signal,20,40,20,10:Power,40,60,28,10:...
920 ParseRouteString (char *s, RouteStyleType *routeStyle, const char *default_unit)
922 int i, style;
923 char Name[256];
925 memset (routeStyle, 0, NUM_STYLES * sizeof (RouteStyleType));
926 for (style = 0; style < NUM_STYLES; style++, routeStyle++)
928 while (*s && isspace ((int) *s))
929 s++;
930 for (i = 0; *s && *s != ','; i++)
931 Name[i] = *s++;
932 Name[i] = '\0';
933 routeStyle->Name = strdup (Name);
934 if (!isdigit ((int) *++s))
935 goto error;
936 routeStyle->Thick = GetNum (&s, default_unit);
937 while (*s && isspace ((int) *s))
938 s++;
939 if (*s++ != ',')
940 goto error;
941 while (*s && isspace ((int) *s))
942 s++;
943 if (!isdigit ((int) *s))
944 goto error;
945 routeStyle->Diameter = GetNum (&s, default_unit);
946 while (*s && isspace ((int) *s))
947 s++;
948 if (*s++ != ',')
949 goto error;
950 while (*s && isspace ((int) *s))
951 s++;
952 if (!isdigit ((int) *s))
953 goto error;
954 routeStyle->Hole = GetNum (&s, default_unit);
955 /* for backwards-compatibility, we use a 10-mil default
956 * for styles which omit the keepaway specification. */
957 if (*s != ',')
958 routeStyle->Keepaway = MIL_TO_COORD(10);
959 else
961 s++;
962 while (*s && isspace ((int) *s))
963 s++;
964 if (!isdigit ((int) *s))
965 goto error;
966 routeStyle->Keepaway = GetNum (&s, default_unit);
967 while (*s && isspace ((int) *s))
968 s++;
970 if (style < NUM_STYLES - 1)
972 while (*s && isspace ((int) *s))
973 s++;
974 if (*s++ != ':')
975 goto error;
978 return (0);
980 error:
981 memset (routeStyle, 0, NUM_STYLES * sizeof (RouteStyleType));
982 return (1);
985 /* ----------------------------------------------------------------------
986 * parses the group definition string which is a colon separated list of
987 * comma separated layer numbers (1,2,b:4,6,8,t)
990 ParseGroupString (char *group_string, LayerGroupType *LayerGroup, int *LayerN)
992 char *s;
993 int group, member, layer;
994 bool c_set = false, /* flags for the two special layers to */
995 s_set = false; /* provide a default setting for old formats */
996 int groupnum[MAX_LAYER + 2];
998 *LayerN = 0;
1000 /* Deterimine the maximum layer number */
1001 for (s = group_string; s && *s; s++)
1003 while (*s && isspace ((int) *s))
1004 s++;
1006 switch (*s)
1008 case 'c':
1009 case 'C':
1010 case 't':
1011 case 'T':
1012 case 's':
1013 case 'S':
1014 case 'b':
1015 case 'B':
1016 break;
1018 default:
1019 if (!isdigit ((int) *s))
1020 goto error;
1021 *LayerN = MAX (*LayerN, atoi (s));
1022 break;
1025 while (*++s && isdigit ((int) *s));
1027 /* ignore white spaces and check for separator */
1028 while (*s && isspace ((int) *s))
1029 s++;
1031 if (*s == '\0')
1032 break;
1034 if (*s != ':' && *s != ',')
1035 goto error;
1038 /* clear struct */
1039 memset (LayerGroup, 0, sizeof (LayerGroupType));
1041 /* Clear assignments */
1042 for (layer = 0; layer < MAX_LAYER + 2; layer++)
1043 groupnum[layer] = -1;
1045 /* loop over all groups */
1046 for (s = group_string, group = 0;
1047 s && *s && group < *LayerN;
1048 group++)
1050 while (*s && isspace ((int) *s))
1051 s++;
1053 /* loop over all group members */
1054 for (member = 0; *s; s++)
1056 /* ignore white spaces and get layernumber */
1057 while (*s && isspace ((int) *s))
1058 s++;
1059 switch (*s)
1061 case 'c':
1062 case 'C':
1063 case 't':
1064 case 'T':
1065 layer = *LayerN + TOP_SILK_LAYER;
1066 c_set = true;
1067 break;
1069 case 's':
1070 case 'S':
1071 case 'b':
1072 case 'B':
1073 layer = *LayerN + BOTTOM_SILK_LAYER;
1074 s_set = true;
1075 break;
1077 default:
1078 layer = atoi (s) - 1;
1079 break;
1081 if (layer > *LayerN + MAX (BOTTOM_SILK_LAYER, TOP_SILK_LAYER) ||
1082 member >= *LayerN + 1)
1083 goto error;
1084 groupnum[layer] = group;
1085 LayerGroup->Entries[group][member++] = layer;
1086 while (*++s && isdigit ((int) *s));
1088 /* ignore white spaces and check for separator */
1089 while (*s && isspace ((int) *s))
1090 s++;
1091 if (!*s || *s == ':')
1092 break;
1094 LayerGroup->Number[group] = member;
1095 if (*s == ':')
1096 s++;
1099 /* If no explicit solder or component layer group was found in the layer
1100 * group string, make group 0 the bottom side, and group 1 the top side.
1101 * This is done by assigning the relevant silkscreen layers to those groups.
1103 if (!s_set)
1104 LayerGroup->Entries[0][LayerGroup->Number[0]++] = *LayerN + BOTTOM_SILK_LAYER;
1105 if (!c_set)
1106 LayerGroup->Entries[1][LayerGroup->Number[1]++] = *LayerN + TOP_SILK_LAYER;
1108 /* Assign a unique layer group to each layer that was not explicitly
1109 * assigned a particular group by its presence in the layer group string.
1111 for (layer = 0; layer < *LayerN && group < *LayerN; layer++)
1112 if (groupnum[layer] == -1)
1114 LayerGroup->Entries[group][0] = layer;
1115 LayerGroup->Number[group] = 1;
1116 group++;
1118 return (0);
1120 /* reset structure on error */
1121 error:
1122 memset (LayerGroup, 0, sizeof (LayerGroupType));
1123 return (1);
1126 /* ---------------------------------------------------------------------------
1127 * quits application
1129 void
1130 QuitApplication (void)
1133 * save data if necessary. It not needed, then don't trigger EmergencySave
1134 * via our atexit() registering of EmergencySave(). We presumeably wanted to
1135 * exit here and thus it is not an emergency.
1137 if (PCB->Changed && Settings.SaveInTMP)
1138 EmergencySave ();
1139 else
1140 DisableEmergencySave ();
1142 /* Free up memory allocated to the PCB. Why bother when we're about to exit ?
1143 * Because it removes some false positives from heap bug detectors such as
1144 * lib dmalloc.
1146 FreePCBMemory(PCB);
1148 exit (0);
1151 /* ---------------------------------------------------------------------------
1152 * creates a filename from a template
1153 * %f is replaced by the filename
1154 * %p by the searchpath
1156 char *
1157 EvaluateFilename (char *Template, char *Path, char *Filename, char *Parameter)
1159 static DynamicStringType command;
1160 char *p;
1162 if (Settings.verbose)
1164 printf ("EvaluateFilename:\n");
1165 printf ("\tTemplate: \033[33m%s\033[0m\n", Template);
1166 printf ("\tPath: \033[33m%s\033[0m\n", Path);
1167 printf ("\tFilename: \033[33m%s\033[0m\n", Filename);
1168 printf ("\tParameter: \033[33m%s\033[0m\n", Parameter);
1171 DSClearString (&command);
1173 for (p = Template; p && *p; p++)
1175 /* copy character or add string to command */
1176 if (*p == '%'
1177 && (*(p + 1) == 'f' || *(p + 1) == 'p' || *(p + 1) == 'a'))
1178 switch (*(++p))
1180 case 'a':
1181 DSAddString (&command, Parameter);
1182 break;
1183 case 'f':
1184 DSAddString (&command, Filename);
1185 break;
1186 case 'p':
1187 DSAddString (&command, Path);
1188 break;
1190 else
1191 DSAddCharacter (&command, *p);
1193 DSAddCharacter (&command, '\0');
1194 if (Settings.verbose)
1195 printf ("EvaluateFilename: \033[32m%s\033[0m\n", command.Data);
1197 return strdup (command.Data);
1200 /* ---------------------------------------------------------------------------
1201 * concatenates directory and filename if directory != NULL,
1202 * expands them with a shell and returns the found name(s) or NULL
1204 char *
1205 ExpandFilename (char *Dirname, char *Filename)
1207 static DynamicStringType answer;
1208 char *command;
1209 FILE *pipe;
1210 int c;
1212 /* allocate memory for commandline and build it */
1213 DSClearString (&answer);
1214 if (Dirname)
1216 command = (char *)calloc (strlen (Filename) + strlen (Dirname) + 7,
1217 sizeof (char));
1218 sprintf (command, "echo %s/%s", Dirname, Filename);
1220 else
1222 command = (char *)calloc (strlen (Filename) + 6, sizeof (char));
1223 sprintf (command, "echo %s", Filename);
1226 /* execute it with shell */
1227 if ((pipe = popen (command, "r")) != NULL)
1229 /* discard all but the first returned line */
1230 for (;;)
1232 if ((c = fgetc (pipe)) == EOF || c == '\n' || c == '\r')
1233 break;
1234 else
1235 DSAddCharacter (&answer, c);
1238 free (command);
1239 return (pclose (pipe) ? NULL : answer.Data);
1242 /* couldn't be expanded by the shell */
1243 PopenErrorMessage (command);
1244 free (command);
1245 return (NULL);
1249 /* ---------------------------------------------------------------------------
1250 * returns the layer number for the passed pointer
1253 GetLayerNumber (DataType *Data, LayerType *Layer)
1255 int i;
1257 for (i = 0; i < MAX_LAYER + 2; i++)
1258 if (Layer == &Data->Layer[i])
1259 break;
1260 return (i);
1263 /* ---------------------------------------------------------------------------
1264 * move layer (number is passed in) to top of layerstack
1266 static void
1267 PushOnTopOfLayerStack (int NewTop)
1269 int i;
1271 /* ignore silk and other extra layers */
1272 if (NewTop < max_copper_layer)
1274 /* first find position of passed one */
1275 for (i = 0; i < max_copper_layer; i++)
1276 if (LayerStack[i] == NewTop)
1277 break;
1279 /* bring this element to the top of the stack */
1280 for (; i; i--)
1281 LayerStack[i] = LayerStack[i - 1];
1282 LayerStack[0] = NewTop;
1287 /* ----------------------------------------------------------------------
1288 * changes the visibility of all layers in a group
1289 * returns the number of changed layers
1292 ChangeGroupVisibility (int Layer, bool On, bool ChangeStackOrder)
1294 int group, i, changed = 1; /* at least the current layer changes */
1296 /* Warning: these special case values must agree with what gui-top-window.c
1297 | thinks the are.
1300 if (Settings.verbose)
1301 printf ("ChangeGroupVisibility(Layer=%d, On=%d, ChangeStackOrder=%d)\n",
1302 Layer, On, ChangeStackOrder);
1304 /* decrement 'i' to keep stack in order of layergroup */
1305 group = GetLayerGroupNumberByNumber (Layer);
1306 for (i = PCB->LayerGroups.Number[group]; i;)
1308 int layer = PCB->LayerGroups.Entries[group][--i];
1310 /* don't count the passed member of the group */
1311 if (layer != Layer && layer < max_copper_layer)
1313 PCB->Data->Layer[layer].On = On;
1315 /* push layer on top of stack if switched on */
1316 if (On && ChangeStackOrder)
1317 PushOnTopOfLayerStack (layer);
1318 changed++;
1322 /* change at least the passed layer */
1323 PCB->Data->Layer[Layer].On = On;
1324 if (On && ChangeStackOrder)
1325 PushOnTopOfLayerStack (Layer);
1327 /* update control panel and exit */
1328 hid_action ("LayersChanged");
1329 return (changed);
1332 /* ----------------------------------------------------------------------
1333 * Given a string description of a layer stack, adjust the layer stack
1334 * to correspond.
1337 void
1338 LayerStringToLayerStack (char *s)
1340 static int listed_layers = 0;
1341 int l = strlen (s);
1342 char **args;
1343 int i, argn, lno;
1344 int prev_sep = 1;
1346 s = strdup (s);
1347 args = (char **) malloc (l * sizeof (char *));
1348 argn = 0;
1350 for (i=0; i<l; i++)
1352 switch (s[i])
1354 case ' ':
1355 case '\t':
1356 case ',':
1357 case ';':
1358 case ':':
1359 prev_sep = 1;
1360 s[i] = '\0';
1361 break;
1362 default:
1363 if (prev_sep)
1364 args[argn++] = s+i;
1365 prev_sep = 0;
1366 break;
1370 for (i = 0; i < max_copper_layer + 2; i++)
1372 if (i < max_copper_layer)
1373 LayerStack[i] = i;
1374 PCB->Data->Layer[i].On = false;
1376 PCB->ElementOn = false;
1377 PCB->InvisibleObjectsOn = false;
1378 PCB->PinOn = false;
1379 PCB->ViaOn = false;
1380 PCB->RatOn = false;
1381 CLEAR_FLAG (SHOWMASKFLAG, PCB);
1382 Settings.ShowBottomSide = 0;
1384 for (i=argn-1; i>=0; i--)
1386 if (strcasecmp (args[i], "rats") == 0)
1387 PCB->RatOn = true;
1388 else if (strcasecmp (args[i], "invisible") == 0)
1389 PCB->InvisibleObjectsOn = true;
1390 else if (strcasecmp (args[i], "pins") == 0)
1391 PCB->PinOn = true;
1392 else if (strcasecmp (args[i], "vias") == 0)
1393 PCB->ViaOn = true;
1394 else if (strcasecmp (args[i], "elements") == 0
1395 || strcasecmp (args[i], "silk") == 0)
1396 PCB->ElementOn = true;
1397 else if (strcasecmp (args[i], "mask") == 0)
1398 SET_FLAG (SHOWMASKFLAG, PCB);
1399 else if (strcasecmp (args[i], "solderside") == 0)
1400 Settings.ShowBottomSide = 1;
1401 else if (isdigit ((int) args[i][0]))
1403 lno = atoi (args[i]);
1404 ChangeGroupVisibility (lno, true, true);
1406 else
1408 int found = 0;
1409 for (lno = 0; lno < max_copper_layer; lno++)
1410 if (strcasecmp (args[i], PCB->Data->Layer[lno].Name) == 0)
1412 ChangeGroupVisibility (lno, true, true);
1413 found = 1;
1414 break;
1416 if (!found)
1418 fprintf(stderr, _("Warning: layer \"%s\" not known\n"), args[i]);
1419 if (!listed_layers)
1421 fprintf (stderr, _("Named layers in this board are:\n"));
1422 listed_layers = 1;
1423 for (lno=0; lno < max_copper_layer; lno ++)
1424 fprintf(stderr, "\t%s\n", PCB->Data->Layer[lno].Name);
1425 fprintf(stderr, _("Also: component, solder, rats, invisible, "
1426 "pins, vias, elements or silk, mask, solderside.\n"));
1433 /* ---------------------------------------------------------------------------
1434 * returns the layergroup number for the passed pointer
1437 GetLayerGroupNumberByPointer (LayerType *Layer)
1439 return (GetLayerGroupNumberByNumber (GetLayerNumber (PCB->Data, Layer)));
1442 /* ---------------------------------------------------------------------------
1443 * returns the layergroup number for the passed layernumber
1446 GetLayerGroupNumberByNumber (Cardinal Layer)
1448 int group, entry;
1450 for (group = 0; group < max_group; group++)
1451 for (entry = 0; entry < PCB->LayerGroups.Number[group]; entry++)
1452 if (PCB->LayerGroups.Entries[group][entry] == Layer)
1453 return (group);
1455 /* since every layer belongs to a group it is safe to return
1456 * the value without boundary checking
1458 return (group);
1461 /* ---------------------------------------------------------------------------
1462 * returns the layergroup number for the passed side (TOP_LAYER or BOTTOM_LAYER)
1465 GetLayerGroupNumberBySide (int side)
1467 /* Find the relavant board side layer group by determining the
1468 * layer group associated with the relevant side's silk-screen
1470 return GetLayerGroupNumberByNumber(
1471 side == TOP_SIDE ? top_silk_layer : bottom_silk_layer);
1474 /* ---------------------------------------------------------------------------
1475 * returns a pointer to an objects bounding box;
1476 * data is valid until the routine is called again
1478 BoxType *
1479 GetObjectBoundingBox (int Type, void *Ptr1, void *Ptr2, void *Ptr3)
1481 switch (Type)
1483 case LINE_TYPE:
1484 case ARC_TYPE:
1485 case TEXT_TYPE:
1486 case POLYGON_TYPE:
1487 case PAD_TYPE:
1488 case PIN_TYPE:
1489 case ELEMENTNAME_TYPE:
1490 return (BoxType *)Ptr2;
1491 case VIA_TYPE:
1492 case ELEMENT_TYPE:
1493 return (BoxType *)Ptr1;
1494 case POLYGONPOINT_TYPE:
1495 case LINEPOINT_TYPE:
1496 return (BoxType *)Ptr3;
1497 default:
1498 Message (_("Request for bounding box of unsupported type %d\n"), Type);
1499 return (BoxType *)Ptr2;
1503 /* ---------------------------------------------------------------------------
1504 * computes the bounding box of an arc
1506 void
1507 SetArcBoundingBox (ArcType *Arc)
1509 double ca1, ca2, sa1, sa2;
1510 double minx, maxx, miny, maxy;
1511 Angle ang1, ang2;
1512 Coord width;
1514 /* first put angles into standard form:
1515 * ang1 < ang2, both angles between 0 and 720 */
1516 Arc->Delta = CLAMP (Arc->Delta, -360, 360);
1518 if (Arc->Delta > 0)
1520 ang1 = NormalizeAngle (Arc->StartAngle);
1521 ang2 = NormalizeAngle (Arc->StartAngle + Arc->Delta);
1523 else
1525 ang1 = NormalizeAngle (Arc->StartAngle + Arc->Delta);
1526 ang2 = NormalizeAngle (Arc->StartAngle);
1528 if (ang1 > ang2)
1529 ang2 += 360;
1530 /* Make sure full circles aren't treated as zero-length arcs */
1531 if (Arc->Delta == 360 || Arc->Delta == -360)
1532 ang2 = ang1 + 360;
1534 /* calculate sines, cosines */
1535 sa1 = sin (M180 * ang1);
1536 ca1 = cos (M180 * ang1);
1537 sa2 = sin (M180 * ang2);
1538 ca2 = cos (M180 * ang2);
1540 minx = MIN (ca1, ca2);
1541 maxx = MAX (ca1, ca2);
1542 miny = MIN (sa1, sa2);
1543 maxy = MAX (sa1, sa2);
1545 /* Check for extreme angles */
1546 if ((ang1 <= 0 && ang2 >= 0) || (ang1 <= 360 && ang2 >= 360)) maxx = 1;
1547 if ((ang1 <= 90 && ang2 >= 90) || (ang1 <= 450 && ang2 >= 450)) maxy = 1;
1548 if ((ang1 <= 180 && ang2 >= 180) || (ang1 <= 540 && ang2 >= 540)) minx = -1;
1549 if ((ang1 <= 270 && ang2 >= 270) || (ang1 <= 630 && ang2 >= 630)) miny = -1;
1551 /* Finally, calcate bounds, converting sane geometry into pcb geometry */
1552 Arc->BoundingBox.X1 = Arc->X - Arc->Width * maxx;
1553 Arc->BoundingBox.X2 = Arc->X - Arc->Width * minx;
1554 Arc->BoundingBox.Y1 = Arc->Y + Arc->Height * miny;
1555 Arc->BoundingBox.Y2 = Arc->Y + Arc->Height * maxy;
1557 width = (Arc->Thickness + Arc->Clearance) / 2;
1559 /* Adjust for our discrete polygon approximation */
1560 width = (double)width * MAX (POLY_CIRC_RADIUS_ADJ, (1.0 + POLY_ARC_MAX_DEVIATION)) + 0.5;
1562 Arc->BoundingBox.X1 -= width;
1563 Arc->BoundingBox.X2 += width;
1564 Arc->BoundingBox.Y1 -= width;
1565 Arc->BoundingBox.Y2 += width;
1566 close_box(&Arc->BoundingBox);
1568 /* Update the arc end-points */
1569 Arc->Point1.X = Arc->X - (double)Arc->Width * ca1;
1570 Arc->Point1.Y = Arc->Y + (double)Arc->Height * sa1;
1571 Arc->Point2.X = Arc->X - (double)Arc->Width * ca2;
1572 Arc->Point2.Y = Arc->Y + (double)Arc->Height * sa2;
1575 /* ---------------------------------------------------------------------------
1576 * resets the layerstack setting
1578 void
1579 ResetStackAndVisibility (void)
1581 int top_group;
1582 Cardinal i;
1584 for (i = 0; i < max_copper_layer + 2; i++)
1586 if (i < max_copper_layer)
1587 LayerStack[i] = i;
1588 PCB->Data->Layer[i].On = true;
1590 PCB->ElementOn = true;
1591 PCB->InvisibleObjectsOn = true;
1592 PCB->PinOn = true;
1593 PCB->ViaOn = true;
1594 PCB->RatOn = true;
1596 /* Bring the component group to the front and make it active. */
1597 top_group = GetLayerGroupNumberBySide (TOP_SIDE);
1598 ChangeGroupVisibility (PCB->LayerGroups.Entries[top_group][0], 1, 1);
1601 /* ---------------------------------------------------------------------------
1602 * saves the layerstack setting
1604 void
1605 SaveStackAndVisibility (void)
1607 Cardinal i;
1608 static bool run = false;
1610 if (run == false)
1612 SavedStack.cnt = 0;
1613 run = true;
1616 if (SavedStack.cnt != 0)
1618 fprintf (stderr,
1619 "SaveStackAndVisibility() layerstack was already saved and not"
1620 "yet restored. cnt = %d\n", SavedStack.cnt);
1623 for (i = 0; i < max_copper_layer + 2; i++)
1625 if (i < max_copper_layer)
1626 SavedStack.LayerStack[i] = LayerStack[i];
1627 SavedStack.LayerOn[i] = PCB->Data->Layer[i].On;
1629 SavedStack.ElementOn = PCB->ElementOn;
1630 SavedStack.InvisibleObjectsOn = PCB->InvisibleObjectsOn;
1631 SavedStack.PinOn = PCB->PinOn;
1632 SavedStack.ViaOn = PCB->ViaOn;
1633 SavedStack.RatOn = PCB->RatOn;
1634 SavedStack.cnt++;
1637 /* ---------------------------------------------------------------------------
1638 * restores the layerstack setting
1640 void
1641 RestoreStackAndVisibility (void)
1643 Cardinal i;
1645 if (SavedStack.cnt == 0)
1647 fprintf (stderr, "RestoreStackAndVisibility() layerstack has not"
1648 " been saved. cnt = %d\n", SavedStack.cnt);
1649 return;
1651 else if (SavedStack.cnt != 1)
1653 fprintf (stderr, "RestoreStackAndVisibility() layerstack save count is"
1654 " wrong. cnt = %d\n", SavedStack.cnt);
1657 for (i = 0; i < max_copper_layer + 2; i++)
1659 if (i < max_copper_layer)
1660 LayerStack[i] = SavedStack.LayerStack[i];
1661 PCB->Data->Layer[i].On = SavedStack.LayerOn[i];
1663 PCB->ElementOn = SavedStack.ElementOn;
1664 PCB->InvisibleObjectsOn = SavedStack.InvisibleObjectsOn;
1665 PCB->PinOn = SavedStack.PinOn;
1666 PCB->ViaOn = SavedStack.ViaOn;
1667 PCB->RatOn = SavedStack.RatOn;
1669 SavedStack.cnt--;
1672 /* ----------------------------------------------------------------------
1673 * returns pointer to current working directory. If 'path' is not
1674 * NULL, then the current working directory is copied to the array
1675 * pointed to by 'path'
1677 char *
1678 GetWorkingDirectory (char *path)
1680 #ifdef HAVE_GETCWD
1681 return getcwd (path, MAXPATHLEN);
1682 #else
1683 /* seems that some BSD releases lack of a prototype for getwd() */
1684 return getwd (path);
1685 #endif
1689 /* ---------------------------------------------------------------------------
1690 * writes a string to the passed file pointer
1691 * some special characters are quoted
1693 void
1694 CreateQuotedString (DynamicStringType *DS, char *S)
1696 DSClearString (DS);
1697 DSAddCharacter (DS, '"');
1698 while (*S)
1700 if (*S == '"' || *S == '\\')
1701 DSAddCharacter (DS, '\\');
1702 DSAddCharacter (DS, *S++);
1704 DSAddCharacter (DS, '"');
1707 BoxType *
1708 GetArcEnds (ArcType *Arc)
1710 static BoxType box;
1711 box.X1 = Arc->X - Arc->Width * cos (Arc->StartAngle * M180);
1712 box.Y1 = Arc->Y + Arc->Height * sin (Arc->StartAngle * M180);
1713 box.X2 = Arc->X - Arc->Width * cos ((Arc->StartAngle + Arc->Delta) * M180);
1714 box.Y2 = Arc->Y + Arc->Height * sin ((Arc->StartAngle + Arc->Delta) * M180);
1715 return &box;
1719 /* doesn't this belong in change.c ?? */
1720 void
1721 ChangeArcAngles (LayerType *Layer, ArcType *a,
1722 Angle new_sa, Angle new_da)
1724 if (new_da >= 360)
1726 new_da = 360;
1727 new_sa = 0;
1729 RestoreToPolygon (PCB->Data, ARC_TYPE, Layer, a);
1730 r_delete_entry (Layer->arc_tree, (BoxType *) a);
1731 AddObjectToChangeAnglesUndoList (ARC_TYPE, a, a, a);
1732 a->StartAngle = new_sa;
1733 a->Delta = new_da;
1734 SetArcBoundingBox (a);
1735 r_insert_entry (Layer->arc_tree, (BoxType *) a, 0);
1736 ClearFromPolygon (PCB->Data, ARC_TYPE, Layer, a);
1739 static char *
1740 BumpName (char *Name)
1742 int num;
1743 char c, *start;
1744 static char temp[256];
1746 start = Name;
1747 /* seek end of string */
1748 while (*Name != 0)
1749 Name++;
1750 /* back up to potential number */
1751 for (Name--; isdigit ((int) *Name); Name--);
1752 Name++;
1753 if (*Name)
1754 num = atoi (Name) + 1;
1755 else
1756 num = 1;
1757 c = *Name;
1758 *Name = 0;
1759 sprintf (temp, "%s%d", start, num);
1760 /* if this is not our string, put back the blown character */
1761 if (start != temp)
1762 *Name = c;
1763 return (temp);
1767 * make a unique name for the name on board
1768 * this can alter the contents of the input string
1770 char *
1771 UniqueElementName (DataType *Data, char *Name)
1773 bool unique = true;
1774 /* null strings are ok */
1775 if (!Name || !*Name)
1776 return (Name);
1778 for (;;)
1780 ELEMENT_LOOP (Data);
1782 if (NAMEONPCB_NAME (element) &&
1783 NSTRCMP (NAMEONPCB_NAME (element), Name) == 0)
1785 Name = BumpName (Name);
1786 unique = false;
1787 break;
1790 END_LOOP;
1791 if (unique)
1792 return (Name);
1793 unique = true;
1797 static void
1798 GetGridLockCoordinates (int type, void *ptr1,
1799 void *ptr2, void *ptr3, Coord * x,
1800 Coord * y)
1802 switch (type)
1804 case VIA_TYPE:
1805 *x = ((PinType *) ptr2)->X;
1806 *y = ((PinType *) ptr2)->Y;
1807 break;
1808 case LINE_TYPE:
1809 *x = ((LineType *) ptr2)->Point1.X;
1810 *y = ((LineType *) ptr2)->Point1.Y;
1811 break;
1812 case TEXT_TYPE:
1813 case ELEMENTNAME_TYPE:
1814 *x = ((TextType *) ptr2)->X;
1815 *y = ((TextType *) ptr2)->Y;
1816 break;
1817 case ELEMENT_TYPE:
1818 *x = ((ElementType *) ptr2)->MarkX;
1819 *y = ((ElementType *) ptr2)->MarkY;
1820 break;
1821 case POLYGON_TYPE:
1822 *x = ((PolygonType *) ptr2)->Points[0].X;
1823 *y = ((PolygonType *) ptr2)->Points[0].Y;
1824 break;
1826 case LINEPOINT_TYPE:
1827 case POLYGONPOINT_TYPE:
1828 *x = ((PointType *) ptr3)->X;
1829 *y = ((PointType *) ptr3)->Y;
1830 break;
1831 case ARC_TYPE:
1833 BoxType *box;
1835 box = GetArcEnds ((ArcType *) ptr2);
1836 *x = box->X1;
1837 *y = box->Y1;
1838 break;
1843 void
1844 AttachForCopy (Coord PlaceX, Coord PlaceY)
1846 BoxType *box;
1847 Coord mx = 0, my = 0;
1849 Crosshair.AttachedObject.RubberbandN = 0;
1850 if (! TEST_FLAG (SNAPPINFLAG, PCB))
1852 /* dither the grab point so that the mark, center, etc
1853 * will end up on a grid coordinate
1855 GetGridLockCoordinates (Crosshair.AttachedObject.Type,
1856 Crosshair.AttachedObject.Ptr1,
1857 Crosshair.AttachedObject.Ptr2,
1858 Crosshair.AttachedObject.Ptr3, &mx, &my);
1859 mx = GridFit (mx, PCB->Grid, PCB->GridOffsetX) - mx;
1860 my = GridFit (my, PCB->Grid, PCB->GridOffsetY) - my;
1862 Crosshair.AttachedObject.X = PlaceX - mx;
1863 Crosshair.AttachedObject.Y = PlaceY - my;
1864 if (!Marked.status || TEST_FLAG (LOCALREFFLAG, PCB))
1865 SetLocalRef (PlaceX - mx, PlaceY - my, true);
1866 Crosshair.AttachedObject.State = STATE_SECOND;
1868 /* get boundingbox of object and set cursor range */
1869 box = GetObjectBoundingBox (Crosshair.AttachedObject.Type,
1870 Crosshair.AttachedObject.Ptr1,
1871 Crosshair.AttachedObject.Ptr2,
1872 Crosshair.AttachedObject.Ptr3);
1873 SetCrosshairRange (Crosshair.AttachedObject.X - box->X1,
1874 Crosshair.AttachedObject.Y - box->Y1,
1875 PCB->MaxWidth - (box->X2 - Crosshair.AttachedObject.X),
1876 PCB->MaxHeight - (box->Y2 - Crosshair.AttachedObject.Y));
1878 /* get all attached objects if necessary */
1879 if ((Settings.Mode != COPY_MODE) && TEST_FLAG (RUBBERBANDFLAG, PCB))
1880 LookupRubberbandLines (Crosshair.AttachedObject.Type,
1881 Crosshair.AttachedObject.Ptr1,
1882 Crosshair.AttachedObject.Ptr2,
1883 Crosshair.AttachedObject.Ptr3);
1884 if (Settings.Mode != COPY_MODE &&
1885 (Crosshair.AttachedObject.Type == ELEMENT_TYPE ||
1886 Crosshair.AttachedObject.Type == VIA_TYPE ||
1887 Crosshair.AttachedObject.Type == LINE_TYPE ||
1888 Crosshair.AttachedObject.Type == LINEPOINT_TYPE))
1889 LookupRatLines (Crosshair.AttachedObject.Type,
1890 Crosshair.AttachedObject.Ptr1,
1891 Crosshair.AttachedObject.Ptr2,
1892 Crosshair.AttachedObject.Ptr3);
1896 * Return nonzero if the given file exists and is readable.
1899 FileExists (const char *name)
1901 FILE *f;
1902 f = fopen (name, "r");
1903 if (f)
1905 fclose (f);
1906 return 1;
1908 return 0;
1911 char *
1912 Concat (const char *first, ...)
1914 char *rv;
1915 int len;
1916 va_list a;
1918 len = strlen (first);
1919 rv = (char *) malloc (len + 1);
1920 strcpy (rv, first);
1922 va_start (a, first);
1923 while (1)
1925 const char *s = va_arg (a, const char *);
1926 if (!s)
1927 break;
1928 len += strlen (s);
1929 rv = (char *) realloc (rv, len + 1);
1930 strcat (rv, s);
1932 va_end (a);
1933 return rv;
1937 mem_any_set (unsigned char *ptr, int bytes)
1939 while (bytes--)
1940 if (*ptr++)
1941 return 1;
1942 return 0;
1945 /* This just fills in a FlagType with current flags. */
1946 FlagType
1947 MakeFlags (unsigned int flags)
1949 FlagType rv;
1950 memset (&rv, 0, sizeof (rv));
1951 rv.f = flags;
1952 return rv;
1955 /* This converts old flag bits (from saved PCB files) to new format. */
1956 FlagType
1957 OldFlags (unsigned int flags)
1959 FlagType rv;
1960 int i, f;
1961 memset (&rv, 0, sizeof (rv));
1962 /* If we move flag bits around, this is where we map old bits to them. */
1963 rv.f = flags & 0xffff;
1964 f = 0x10000;
1965 for (i = 0; i < 8; i++)
1967 /* use the closest thing to the old thermal style */
1968 if (flags & f)
1969 rv.t[i / 2] |= (1 << (4 * (i % 2)));
1970 f <<= 1;
1972 return rv;
1975 FlagType
1976 AddFlags (FlagType flag, unsigned int flags)
1978 flag.f |= flags;
1979 return flag;
1982 FlagType
1983 MaskFlags (FlagType flag, unsigned int flags)
1985 flag.f &= ~flags;
1986 return flag;
1989 /***********************************************************************
1990 * Layer Group Functions
1994 MoveLayerToGroup (int layer, int group)
1996 int prev, i, j;
1998 if (layer < 0 || layer > max_copper_layer + 1)
1999 return -1;
2000 prev = GetLayerGroupNumberByNumber (layer);
2001 if ((layer == bottom_silk_layer
2002 && group == GetLayerGroupNumberByNumber (top_silk_layer))
2003 || (layer == top_silk_layer
2004 && group == GetLayerGroupNumberByNumber (bottom_silk_layer))
2005 || (group < 0 || group >= max_group) || (prev == group))
2006 return prev;
2008 /* Remove layer from prev group */
2009 for (j = i = 0; i < PCB->LayerGroups.Number[prev]; i++)
2010 if (PCB->LayerGroups.Entries[prev][i] != layer)
2011 PCB->LayerGroups.Entries[prev][j++] = PCB->LayerGroups.Entries[prev][i];
2012 PCB->LayerGroups.Number[prev]--;
2014 /* Add layer to new group. */
2015 i = PCB->LayerGroups.Number[group]++;
2016 PCB->LayerGroups.Entries[group][i] = layer;
2018 return group;
2021 char *
2022 LayerGroupsToString (LayerGroupType *lg)
2024 #if MAX_LAYER < 9998
2025 /* Allows for layer numbers 0..9999 */
2026 static char buf[(MAX_LAYER + 2) * 5 + 1];
2027 #endif
2028 char *cp = buf;
2029 char sep = 0;
2030 int group, entry;
2031 for (group = 0; group < max_group; group++)
2032 if (PCB->LayerGroups.Number[group])
2034 if (sep)
2035 *cp++ = ':';
2036 sep = 1;
2037 for (entry = 0; entry < PCB->LayerGroups.Number[group]; entry++)
2039 int layer = PCB->LayerGroups.Entries[group][entry];
2040 if (layer == top_silk_layer)
2042 *cp++ = 'c';
2044 else if (layer == bottom_silk_layer)
2046 *cp++ = 's';
2048 else
2050 sprintf (cp, "%d", layer + 1);
2051 while (*++cp)
2054 if (entry != PCB->LayerGroups.Number[group] - 1)
2055 *cp++ = ',';
2058 *cp++ = 0;
2059 return buf;
2062 char *
2063 pcb_author (void)
2065 #ifdef HAVE_GETPWUID
2066 static struct passwd *pwentry;
2067 static char *fab_author = 0;
2069 if (!fab_author)
2071 if (Settings.FabAuthor && Settings.FabAuthor[0])
2072 fab_author = Settings.FabAuthor;
2073 else
2075 int len;
2076 char *comma, *gecos;
2078 /* ID the user. */
2079 pwentry = getpwuid (getuid ());
2080 gecos = pwentry->pw_gecos;
2081 comma = strchr (gecos, ',');
2082 if (comma)
2083 len = comma - gecos;
2084 else
2085 len = strlen (gecos);
2086 fab_author = (char *)malloc (len + 1);
2087 if (!fab_author)
2089 perror ("pcb: out of memory.\n");
2090 exit (-1);
2092 memcpy (fab_author, gecos, len);
2093 fab_author[len] = 0;
2096 return fab_author;
2097 #else
2098 return "Unknown";
2099 #endif
2103 char *
2104 AttributeGetFromList (AttributeListType *list, char *name)
2106 int i;
2107 for (i=0; i<list->Number; i++)
2108 if (strcmp (name, list->List[i].name) == 0)
2109 return list->List[i].value;
2110 return NULL;
2114 AttributePutToList (AttributeListType *list, const char *name, const char *value, int replace)
2116 int i;
2118 /* If we're allowed to replace an existing attribute, see if we
2119 can. */
2120 if (replace)
2122 for (i=0; i<list->Number; i++)
2123 if (strcmp (name, list->List[i].name) == 0)
2125 free (list->List[i].value);
2126 list->List[i].value = STRDUP (value);
2127 return 1;
2131 /* At this point, we're going to need to add a new attribute to the
2132 list. See if there's room. */
2133 if (list->Number >= list->Max)
2135 list->Max += 10;
2136 list->List = (AttributeType *) realloc (list->List,
2137 list->Max * sizeof (AttributeType));
2140 /* Now add the new attribute. */
2141 i = list->Number;
2142 list->List[i].name = STRDUP (name);
2143 list->List[i].value = STRDUP (value);
2144 list->Number ++;
2145 return 0;
2148 void
2149 AttributeRemoveFromList(AttributeListType *list, char *name)
2151 int i, j;
2152 for (i=0; i<list->Number; i++)
2153 if (strcmp (name, list->List[i].name) == 0)
2155 free (list->List[i].name);
2156 free (list->List[i].value);
2157 for (j=i; j<list->Number-i; j++)
2158 list->List[j] = list->List[j+1];
2159 list->Number --;
2163 /* In future all use of this should be supplanted by
2164 * pcb-printf and %mr/%m# spec */
2165 const char *
2166 c_dtostr (double d)
2168 static char buf[100];
2169 int i, f;
2170 char *bufp = buf;
2172 if (d < 0)
2174 *bufp++ = '-';
2175 d = -d;
2177 d += 0.0000005; /* rounding */
2178 i = floor (d);
2179 d -= i;
2180 sprintf (bufp, "%d", i);
2181 bufp += strlen (bufp);
2182 *bufp++ = '.';
2184 f = floor (d * 1000000.0);
2185 sprintf (bufp, "%06d", f);
2186 return buf;
2189 void
2190 r_delete_element (DataType * data, ElementType * element)
2192 r_delete_entry (data->element_tree, (BoxType *) element);
2193 PIN_LOOP (element);
2195 r_delete_entry (data->pin_tree, (BoxType *) pin);
2197 END_LOOP;
2198 PAD_LOOP (element);
2200 r_delete_entry (data->pad_tree, (BoxType *) pad);
2202 END_LOOP;
2203 ELEMENTTEXT_LOOP (element);
2205 r_delete_entry (data->name_tree[n], (BoxType *) text);
2207 END_LOOP;
2211 /* ---------------------------------------------------------------------------
2212 * Returns a string that has a bunch of information about the program.
2213 * Can be used for things like "about" dialog boxes.
2216 char *
2217 GetInfoString (void)
2219 HID **hids;
2220 int i;
2221 static DynamicStringType info;
2222 static int first_time = 1;
2224 #define TAB " "
2226 if (first_time)
2228 first_time = 0;
2229 DSAddString (&info,
2230 _("This is PCB, an interactive\n"
2231 "printed circuit board editor\n"
2232 "version "));
2233 DSAddString (&info,
2234 VERSION "\n\n"
2235 "Compiled on " __DATE__ " at " __TIME__ "\n\n"
2236 "by harry eaton\n\n"
2237 "Copyright (C) Thomas Nau 1994, 1995, 1996, 1997\n"
2238 "Copyright (C) harry eaton 1998-2007\n"
2239 "Copyright (C) C. Scott Ananian 2001\n"
2240 "Copyright (C) DJ Delorie 2003, 2004, 2005, 2006, 2007, 2008\n"
2241 "Copyright (C) Dan McMahill 2003, 2004, 2005, 2006, 2007, 2008\n\n");
2242 DSAddString (&info,
2243 _("It is licensed under the terms of the GNU\n"
2244 "General Public License version 2\n"
2245 "See the LICENSE file for more information\n\n"
2246 "For more information see:\n"));
2247 DSAddString (&info, _("PCB homepage: "));
2248 DSAddString (&info, "http://pcb.geda-project.org\n");
2249 DSAddString (&info, _("gEDA homepage: "));
2250 DSAddString (&info, "http://www.geda-project.org\n");
2251 DSAddString (&info, _("gEDA Wiki: "));
2252 DSAddString (&info, "http://wiki.geda-project.org\n");
2254 DSAddString (&info, _("\n----- Compile Time Options -----\n"));
2255 hids = hid_enumerate ();
2256 DSAddString (&info, _("GUI:\n"));
2257 for (i = 0; hids[i]; i++)
2259 if (hids[i]->gui)
2261 DSAddString (&info, TAB);
2262 DSAddString (&info, hids[i]->name);
2263 DSAddString (&info, " : ");
2264 DSAddString (&info, hids[i]->description);
2265 DSAddString (&info, "\n");
2269 DSAddString (&info, _("Exporters:\n"));
2270 for (i = 0; hids[i]; i++)
2272 if (hids[i]->exporter)
2274 DSAddString (&info, TAB);
2275 DSAddString (&info, hids[i]->name);
2276 DSAddString (&info, " : ");
2277 DSAddString (&info, hids[i]->description);
2278 DSAddString (&info, "\n");
2282 DSAddString (&info, _("Printers:\n"));
2283 for (i = 0; hids[i]; i++)
2285 if (hids[i]->printer)
2287 DSAddString (&info, TAB);
2288 DSAddString (&info, hids[i]->name);
2289 DSAddString (&info, " : ");
2290 DSAddString (&info, hids[i]->description);
2291 DSAddString (&info, "\n");
2295 #undef TAB
2297 return info.Data;
2300 /* ---------------------------------------------------------------------------
2301 * mkdir() implentation, mostly for plugins, which don't have our config.h.
2304 #ifdef MKDIR_IS_PCBMKDIR
2305 #error "Don't know how to create a directory on this system."
2306 #endif
2309 pcb_mkdir (const char *path, int mode)
2311 return MKDIR (path, mode);
2314 /* ---------------------------------------------------------------------------
2315 * Returns a best guess about the orientation of an element. The
2316 * value corresponds to the rotation; a difference is the right value
2317 * to pass to RotateElementLowLevel. However, the actual value is no
2318 * indication of absolute rotation; only relative rotation is
2319 * meaningful.
2322 int
2323 ElementOrientation (ElementType *e)
2325 Coord pin1x, pin1y, pin2x, pin2y, dx, dy;
2326 bool found_pin1 = 0;
2327 bool found_pin2 = 0;
2329 /* in case we don't find pin 1 or 2, make sure we have initialized these variables */
2330 pin1x = 0;
2331 pin1y = 0;
2332 pin2x = 0;
2333 pin2y = 0;
2335 PIN_LOOP (e);
2337 if (NSTRCMP (pin->Number, "1") == 0)
2339 pin1x = pin->X;
2340 pin1y = pin->Y;
2341 found_pin1 = 1;
2343 else if (NSTRCMP (pin->Number, "2") == 0)
2345 pin2x = pin->X;
2346 pin2y = pin->Y;
2347 found_pin2 = 1;
2350 END_LOOP;
2352 PAD_LOOP (e);
2354 if (NSTRCMP (pad->Number, "1") == 0)
2356 pin1x = (pad->Point1.X + pad->Point2.X) / 2;
2357 pin1y = (pad->Point1.Y + pad->Point2.Y) / 2;
2358 found_pin1 = 1;
2360 else if (NSTRCMP (pad->Number, "2") == 0)
2362 pin2x = (pad->Point1.X + pad->Point2.X) / 2;
2363 pin2y = (pad->Point1.Y + pad->Point2.Y) / 2;
2364 found_pin2 = 1;
2367 END_LOOP;
2369 if (found_pin1 && found_pin2)
2371 dx = pin2x - pin1x;
2372 dy = pin2y - pin1y;
2374 else if (found_pin1 && (pin1x || pin1y))
2376 dx = pin1x;
2377 dy = pin1y;
2379 else if (found_pin2 && (pin2x || pin2y))
2381 dx = pin2x;
2382 dy = pin2y;
2384 else
2385 return 0;
2387 if (abs(dx) > abs(dy))
2388 return dx > 0 ? 0 : 2;
2389 return dy > 0 ? 3 : 1;
2393 ActionListRotations(int argc, char **argv, Coord x, Coord y)
2395 ELEMENT_LOOP (PCB->Data);
2397 printf("%d %s\n", ElementOrientation(element), NAMEONPCB_NAME(element));
2399 END_LOOP;
2401 return 0;
2404 HID_Action misc_action_list[] = {
2405 {"ListRotations", 0, ActionListRotations,
2406 0,0},
2409 REGISTER_ACTIONS (misc_action_list)