Fixup draw.c not to special case based on HID name, use flags instead
[geda-pcb/pcjc2.git] / src / misc.c
blobb760709cec1fb8e58338772af7c28832dde34d77
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;
215 * \brief Extract a unit-less value from a string.
217 * \param val String containing the value to be read.
219 * \param absolute Returns wether the returned value is an absolute one.
221 * \return The value read, with sign.
223 * This is the same as GetValue() and GetValueEX(), but totally ignoring units.
224 * Typical application is a list selector, like the type of thermal to apply
225 * to a pin.
227 double GetUnitlessValue (const char *val, bool *absolute) {
228 double value;
230 if (*val == '=')
232 *absolute = true;
233 val++;
235 else
237 if (isdigit ((int) *val))
238 *absolute = true;
239 else
240 *absolute = false;
243 if (sscanf (val, "%lf", &value) < 1)
244 return 0.;
246 return value;
249 /* ---------------------------------------------------------------------------
250 * sets the bounding box of a point (which is silly)
252 void
253 SetPointBoundingBox (PointType *Pnt)
255 Pnt->X2 = Pnt->X + 1;
256 Pnt->Y2 = Pnt->Y + 1;
259 /* ---------------------------------------------------------------------------
260 * sets the bounding box of a pin or via
262 void
263 SetPinBoundingBox (PinType *Pin)
265 Coord width;
267 /* the bounding box covers the extent of influence
268 * so it must include the clearance values too
270 width = MAX (Pin->Clearance + PIN_SIZE (Pin), Pin->Mask) / 2;
272 /* Adjust for our discrete polygon approximation */
273 width = (double)width * POLY_CIRC_RADIUS_ADJ + 0.5;
275 Pin->BoundingBox.X1 = Pin->X - width;
276 Pin->BoundingBox.Y1 = Pin->Y - width;
277 Pin->BoundingBox.X2 = Pin->X + width;
278 Pin->BoundingBox.Y2 = Pin->Y + width;
279 close_box(&Pin->BoundingBox);
282 /* ---------------------------------------------------------------------------
283 * sets the bounding box of a pad
285 void
286 SetPadBoundingBox (PadType *Pad)
288 Coord width;
289 Coord deltax;
290 Coord deltay;
292 /* the bounding box covers the extent of influence
293 * so it must include the clearance values too
295 width = (Pad->Thickness + Pad->Clearance + 1) / 2;
296 width = MAX (width, (Pad->Mask + 1) / 2);
297 deltax = Pad->Point2.X - Pad->Point1.X;
298 deltay = Pad->Point2.Y - Pad->Point1.Y;
300 if (TEST_FLAG (SQUAREFLAG, Pad) && deltax != 0 && deltay != 0)
302 /* slanted square pad */
303 double theta;
304 Coord btx, bty;
306 /* T is a vector half a thickness long, in the direction of
307 one of the corners. */
308 theta = atan2 (deltay, deltax);
309 btx = width * cos (theta + M_PI/4) * sqrt(2.0);
310 bty = width * sin (theta + M_PI/4) * sqrt(2.0);
313 Pad->BoundingBox.X1 = MIN (MIN (Pad->Point1.X - btx, Pad->Point1.X - bty),
314 MIN (Pad->Point2.X + btx, Pad->Point2.X + bty));
315 Pad->BoundingBox.X2 = MAX (MAX (Pad->Point1.X - btx, Pad->Point1.X - bty),
316 MAX (Pad->Point2.X + btx, Pad->Point2.X + bty));
317 Pad->BoundingBox.Y1 = MIN (MIN (Pad->Point1.Y + btx, Pad->Point1.Y - bty),
318 MIN (Pad->Point2.Y - btx, Pad->Point2.Y + bty));
319 Pad->BoundingBox.Y2 = MAX (MAX (Pad->Point1.Y + btx, Pad->Point1.Y - bty),
320 MAX (Pad->Point2.Y - btx, Pad->Point2.Y + bty));
322 else
324 /* Adjust for our discrete polygon approximation */
325 width = (double)width * POLY_CIRC_RADIUS_ADJ + 0.5;
327 Pad->BoundingBox.X1 = MIN (Pad->Point1.X, Pad->Point2.X) - width;
328 Pad->BoundingBox.X2 = MAX (Pad->Point1.X, Pad->Point2.X) + width;
329 Pad->BoundingBox.Y1 = MIN (Pad->Point1.Y, Pad->Point2.Y) - width;
330 Pad->BoundingBox.Y2 = MAX (Pad->Point1.Y, Pad->Point2.Y) + width;
332 close_box(&Pad->BoundingBox);
335 /* ---------------------------------------------------------------------------
336 * sets the bounding box of a line
338 void
339 SetLineBoundingBox (LineType *Line)
341 Coord width = (Line->Thickness + Line->Clearance + 1) / 2;
343 /* Adjust for our discrete polygon approximation */
344 width = (double)width * POLY_CIRC_RADIUS_ADJ + 0.5;
346 Line->BoundingBox.X1 = MIN (Line->Point1.X, Line->Point2.X) - width;
347 Line->BoundingBox.X2 = MAX (Line->Point1.X, Line->Point2.X) + width;
348 Line->BoundingBox.Y1 = MIN (Line->Point1.Y, Line->Point2.Y) - width;
349 Line->BoundingBox.Y2 = MAX (Line->Point1.Y, Line->Point2.Y) + width;
350 close_box(&Line->BoundingBox);
351 SetPointBoundingBox (&Line->Point1);
352 SetPointBoundingBox (&Line->Point2);
355 /* ---------------------------------------------------------------------------
356 * sets the bounding box of a polygons
358 void
359 SetPolygonBoundingBox (PolygonType *Polygon)
361 Polygon->BoundingBox.X1 = Polygon->BoundingBox.Y1 = MAX_COORD;
362 Polygon->BoundingBox.X2 = Polygon->BoundingBox.Y2 = 0;
363 POLYGONPOINT_LOOP (Polygon);
365 MAKEMIN (Polygon->BoundingBox.X1, point->X);
366 MAKEMIN (Polygon->BoundingBox.Y1, point->Y);
367 MAKEMAX (Polygon->BoundingBox.X2, point->X);
368 MAKEMAX (Polygon->BoundingBox.Y2, point->Y);
370 /* boxes don't include the lower right corner */
371 close_box(&Polygon->BoundingBox);
372 END_LOOP;
375 /* ---------------------------------------------------------------------------
376 * sets the bounding box of an elements
378 void
379 SetElementBoundingBox (DataType *Data, ElementType *Element,
380 FontType *Font)
382 BoxType *box, *vbox;
384 if (Data && Data->element_tree)
385 r_delete_entry (Data->element_tree, (BoxType *) Element);
386 /* first update the text objects */
387 ELEMENTTEXT_LOOP (Element);
389 if (Data && Data->name_tree[n])
390 r_delete_entry (Data->name_tree[n], (BoxType *) text);
391 SetTextBoundingBox (Font, text);
392 if (Data && !Data->name_tree[n])
393 Data->name_tree[n] = r_create_tree (NULL, 0, 0);
394 if (Data)
395 r_insert_entry (Data->name_tree[n], (BoxType *) text, 0);
397 END_LOOP;
399 /* do not include the elementnames bounding box which
400 * is handled separately
402 box = &Element->BoundingBox;
403 vbox = &Element->VBox;
404 box->X1 = box->Y1 = MAX_COORD;
405 box->X2 = box->Y2 = 0;
406 ELEMENTLINE_LOOP (Element);
408 SetLineBoundingBox (line);
409 MAKEMIN (box->X1, line->Point1.X - (line->Thickness + 1) / 2);
410 MAKEMIN (box->Y1, line->Point1.Y - (line->Thickness + 1) / 2);
411 MAKEMIN (box->X1, line->Point2.X - (line->Thickness + 1) / 2);
412 MAKEMIN (box->Y1, line->Point2.Y - (line->Thickness + 1) / 2);
413 MAKEMAX (box->X2, line->Point1.X + (line->Thickness + 1) / 2);
414 MAKEMAX (box->Y2, line->Point1.Y + (line->Thickness + 1) / 2);
415 MAKEMAX (box->X2, line->Point2.X + (line->Thickness + 1) / 2);
416 MAKEMAX (box->Y2, line->Point2.Y + (line->Thickness + 1) / 2);
418 END_LOOP;
419 ARC_LOOP (Element);
421 SetArcBoundingBox (arc);
422 MAKEMIN (box->X1, arc->BoundingBox.X1);
423 MAKEMIN (box->Y1, arc->BoundingBox.Y1);
424 MAKEMAX (box->X2, arc->BoundingBox.X2);
425 MAKEMAX (box->Y2, arc->BoundingBox.Y2);
427 END_LOOP;
428 *vbox = *box;
429 PIN_LOOP (Element);
431 if (Data && Data->pin_tree)
432 r_delete_entry (Data->pin_tree, (BoxType *) pin);
433 SetPinBoundingBox (pin);
434 if (Data)
436 if (!Data->pin_tree)
437 Data->pin_tree = r_create_tree (NULL, 0, 0);
438 r_insert_entry (Data->pin_tree, (BoxType *) pin, 0);
440 MAKEMIN (box->X1, pin->BoundingBox.X1);
441 MAKEMIN (box->Y1, pin->BoundingBox.Y1);
442 MAKEMAX (box->X2, pin->BoundingBox.X2);
443 MAKEMAX (box->Y2, pin->BoundingBox.Y2);
444 MAKEMIN (vbox->X1, pin->X - pin->Thickness / 2);
445 MAKEMIN (vbox->Y1, pin->Y - pin->Thickness / 2);
446 MAKEMAX (vbox->X2, pin->X + pin->Thickness / 2);
447 MAKEMAX (vbox->Y2, pin->Y + pin->Thickness / 2);
449 END_LOOP;
450 PAD_LOOP (Element);
452 if (Data && Data->pad_tree)
453 r_delete_entry (Data->pad_tree, (BoxType *) pad);
454 SetPadBoundingBox (pad);
455 if (Data)
457 if (!Data->pad_tree)
458 Data->pad_tree = r_create_tree (NULL, 0, 0);
459 r_insert_entry (Data->pad_tree, (BoxType *) pad, 0);
461 MAKEMIN (box->X1, pad->BoundingBox.X1);
462 MAKEMIN (box->Y1, pad->BoundingBox.Y1);
463 MAKEMAX (box->X2, pad->BoundingBox.X2);
464 MAKEMAX (box->Y2, pad->BoundingBox.Y2);
465 MAKEMIN (vbox->X1,
466 MIN (pad->Point1.X, pad->Point2.X) - pad->Thickness / 2);
467 MAKEMIN (vbox->Y1,
468 MIN (pad->Point1.Y, pad->Point2.Y) - pad->Thickness / 2);
469 MAKEMAX (vbox->X2,
470 MAX (pad->Point1.X, pad->Point2.X) + pad->Thickness / 2);
471 MAKEMAX (vbox->Y2,
472 MAX (pad->Point1.Y, pad->Point2.Y) + pad->Thickness / 2);
474 END_LOOP;
475 /* now we set the EDGE2FLAG of the pad if Point2
476 * is closer to the outside edge than Point1
478 PAD_LOOP (Element);
480 if (pad->Point1.Y == pad->Point2.Y)
482 /* horizontal pad */
483 if (box->X2 - pad->Point2.X < pad->Point1.X - box->X1)
484 SET_FLAG (EDGE2FLAG, pad);
485 else
486 CLEAR_FLAG (EDGE2FLAG, pad);
488 else
490 /* vertical pad */
491 if (box->Y2 - pad->Point2.Y < pad->Point1.Y - box->Y1)
492 SET_FLAG (EDGE2FLAG, pad);
493 else
494 CLEAR_FLAG (EDGE2FLAG, pad);
497 END_LOOP;
499 /* mark pins with component orientation */
500 if ((box->X2 - box->X1) > (box->Y2 - box->Y1))
502 PIN_LOOP (Element);
504 SET_FLAG (EDGE2FLAG, pin);
506 END_LOOP;
508 else
510 PIN_LOOP (Element);
512 CLEAR_FLAG (EDGE2FLAG, pin);
514 END_LOOP;
516 close_box(box);
517 close_box(vbox);
518 if (Data && !Data->element_tree)
519 Data->element_tree = r_create_tree (NULL, 0, 0);
520 if (Data)
521 r_insert_entry (Data->element_tree, box, 0);
524 /* ---------------------------------------------------------------------------
525 * creates the bounding box of a text object
527 void
528 SetTextBoundingBox (FontType *FontPtr, TextType *Text)
530 SymbolType *symbol = FontPtr->Symbol;
531 unsigned char *s = (unsigned char *) Text->TextString;
532 int i;
533 int space;
535 Coord minx, miny, maxx, maxy, tx;
536 Coord min_final_radius;
537 Coord min_unscaled_radius;
538 bool first_time = true;
540 minx = miny = maxx = maxy = tx = 0;
542 /* Calculate the bounding box based on the larger of the thicknesses
543 * the text might clamped at on silk or copper layers.
545 min_final_radius = MAX (PCB->minWid, PCB->minSlk) / 2;
547 /* Pre-adjust the line radius for the fact we are initially computing the
548 * bounds of the un-scaled text, and the thickness clamping applies to
549 * scaled text.
551 min_unscaled_radius = UNSCALE_TEXT (min_final_radius, Text->Scale);
553 /* calculate size of the bounding box */
554 for (; s && *s; s++)
556 if (*s <= MAX_FONTPOSITION && symbol[*s].Valid)
558 LineType *line = symbol[*s].Line;
559 for (i = 0; i < symbol[*s].LineN; line++, i++)
561 /* Clamp the width of text lines at the minimum thickness.
562 * NB: Divide 4 in thickness calculation is comprised of a factor
563 * of 1/2 to get a radius from the center-line, and a factor
564 * of 1/2 because some stupid reason we render our glyphs
565 * at half their defined stroke-width.
567 Coord unscaled_radius = MAX (min_unscaled_radius, line->Thickness / 4);
569 if (first_time)
571 minx = maxx = line->Point1.X;
572 miny = maxy = line->Point1.Y;
573 first_time = false;
576 minx = MIN (minx, line->Point1.X - unscaled_radius + tx);
577 miny = MIN (miny, line->Point1.Y - unscaled_radius);
578 minx = MIN (minx, line->Point2.X - unscaled_radius + tx);
579 miny = MIN (miny, line->Point2.Y - unscaled_radius);
580 maxx = MAX (maxx, line->Point1.X + unscaled_radius + tx);
581 maxy = MAX (maxy, line->Point1.Y + unscaled_radius);
582 maxx = MAX (maxx, line->Point2.X + unscaled_radius + tx);
583 maxy = MAX (maxy, line->Point2.Y + unscaled_radius);
585 space = symbol[*s].Delta;
587 else
589 BoxType *ds = &FontPtr->DefaultSymbol;
590 Coord w = ds->X2 - ds->X1;
592 minx = MIN (minx, ds->X1 + tx);
593 miny = MIN (miny, ds->Y1);
594 minx = MIN (minx, ds->X2 + tx);
595 miny = MIN (miny, ds->Y2);
596 maxx = MAX (maxx, ds->X1 + tx);
597 maxy = MAX (maxy, ds->Y1);
598 maxx = MAX (maxx, ds->X2 + tx);
599 maxy = MAX (maxy, ds->Y2);
601 space = w / 5;
603 tx += symbol[*s].Width + space;
606 /* scale values */
607 minx = SCALE_TEXT (minx, Text->Scale);
608 miny = SCALE_TEXT (miny, Text->Scale);
609 maxx = SCALE_TEXT (maxx, Text->Scale);
610 maxy = SCALE_TEXT (maxy, Text->Scale);
612 /* set upper-left and lower-right corner;
613 * swap coordinates if necessary (origin is already in 'swapped')
614 * and rotate box
617 if (TEST_FLAG (ONSOLDERFLAG, Text))
619 Text->BoundingBox.X1 = Text->X + minx;
620 Text->BoundingBox.Y1 = Text->Y - miny;
621 Text->BoundingBox.X2 = Text->X + maxx;
622 Text->BoundingBox.Y2 = Text->Y - maxy;
623 RotateBoxLowLevel (&Text->BoundingBox, Text->X, Text->Y,
624 (4 - Text->Direction) & 0x03);
626 else
628 Text->BoundingBox.X1 = Text->X + minx;
629 Text->BoundingBox.Y1 = Text->Y + miny;
630 Text->BoundingBox.X2 = Text->X + maxx;
631 Text->BoundingBox.Y2 = Text->Y + maxy;
632 RotateBoxLowLevel (&Text->BoundingBox,
633 Text->X, Text->Y, Text->Direction);
636 /* the bounding box covers the extent of influence
637 * so it must include the clearance values too
639 Text->BoundingBox.X1 -= PCB->Bloat;
640 Text->BoundingBox.Y1 -= PCB->Bloat;
641 Text->BoundingBox.X2 += PCB->Bloat;
642 Text->BoundingBox.Y2 += PCB->Bloat;
643 close_box(&Text->BoundingBox);
646 /* ---------------------------------------------------------------------------
647 * returns true if data area is empty
649 bool
650 IsDataEmpty (DataType *Data)
652 bool hasNoObjects;
653 Cardinal i;
655 hasNoObjects = (Data->ViaN == 0);
656 hasNoObjects &= (Data->ElementN == 0);
657 for (i = 0; i < max_copper_layer + 2; i++)
658 hasNoObjects = hasNoObjects &&
659 Data->Layer[i].LineN == 0 &&
660 Data->Layer[i].ArcN == 0 &&
661 Data->Layer[i].TextN == 0 && Data->Layer[i].PolygonN == 0;
662 return (hasNoObjects);
666 FlagIsDataEmpty (int parm)
668 int i = IsDataEmpty (PCB->Data);
669 return parm ? !i : i;
672 /* FLAG(DataEmpty,FlagIsDataEmpty,0) */
673 /* FLAG(DataNonEmpty,FlagIsDataEmpty,1) */
675 bool
676 IsLayerEmpty (LayerType *layer)
678 return (layer->LineN == 0
679 && layer->TextN == 0
680 && layer->PolygonN == 0
681 && layer->ArcN == 0);
684 bool
685 IsLayerNumEmpty (int num)
687 return IsLayerEmpty (PCB->Data->Layer+num);
690 bool
691 IsLayerGroupEmpty (int num)
693 int i;
694 for (i=0; i<PCB->LayerGroups.Number[num]; i++)
695 if (!IsLayerNumEmpty (PCB->LayerGroups.Entries[num][i]))
696 return false;
697 return true;
700 bool
701 IsPasteEmpty (int side)
703 bool paste_empty = true;
704 ALLPAD_LOOP (PCB->Data);
706 if (ON_SIDE (pad, side) && !TEST_FLAG (NOPASTEFLAG, pad) && pad->Mask > 0)
708 paste_empty = false;
709 break;
712 ENDALL_LOOP;
713 return paste_empty;
717 typedef struct
719 int nplated;
720 int nunplated;
721 } HoleCountStruct;
723 static int
724 hole_counting_callback (const BoxType * b, void *cl)
726 PinType *pin = (PinType *) b;
727 HoleCountStruct *hcs = (HoleCountStruct *) cl;
728 if (TEST_FLAG (HOLEFLAG, pin))
729 hcs->nunplated++;
730 else
731 hcs->nplated++;
732 return 1;
735 /* ---------------------------------------------------------------------------
736 * counts the number of plated and unplated holes in the design within
737 * a given area of the board. To count for the whole board, pass NULL
738 * within_area.
740 void
741 CountHoles (int *plated, int *unplated, const BoxType *within_area)
743 HoleCountStruct hcs = {0, 0};
745 r_search (PCB->Data->pin_tree, within_area, NULL, hole_counting_callback, &hcs);
746 r_search (PCB->Data->via_tree, within_area, NULL, hole_counting_callback, &hcs);
748 if (plated != NULL) *plated = hcs.nplated;
749 if (unplated != NULL) *unplated = hcs.nunplated;
753 /* ---------------------------------------------------------------------------
754 * gets minimum and maximum coordinates
755 * returns NULL if layout is empty
757 BoxType *
758 GetDataBoundingBox (DataType *Data)
760 static BoxType box;
761 /* FIX ME: use r_search to do this much faster */
763 /* preset identifiers with highest and lowest possible values */
764 box.X1 = box.Y1 = MAX_COORD;
765 box.X2 = box.Y2 = -MAX_COORD;
767 /* now scan for the lowest/highest X and Y coordinate */
768 VIA_LOOP (Data);
770 box.X1 = MIN (box.X1, via->X - via->Thickness / 2);
771 box.Y1 = MIN (box.Y1, via->Y - via->Thickness / 2);
772 box.X2 = MAX (box.X2, via->X + via->Thickness / 2);
773 box.Y2 = MAX (box.Y2, via->Y + via->Thickness / 2);
775 END_LOOP;
776 ELEMENT_LOOP (Data);
778 box.X1 = MIN (box.X1, element->BoundingBox.X1);
779 box.Y1 = MIN (box.Y1, element->BoundingBox.Y1);
780 box.X2 = MAX (box.X2, element->BoundingBox.X2);
781 box.Y2 = MAX (box.Y2, element->BoundingBox.Y2);
783 TextType *text = &NAMEONPCB_TEXT (element);
784 box.X1 = MIN (box.X1, text->BoundingBox.X1);
785 box.Y1 = MIN (box.Y1, text->BoundingBox.Y1);
786 box.X2 = MAX (box.X2, text->BoundingBox.X2);
787 box.Y2 = MAX (box.Y2, text->BoundingBox.Y2);
790 END_LOOP;
791 ALLLINE_LOOP (Data);
793 box.X1 = MIN (box.X1, line->Point1.X - line->Thickness / 2);
794 box.Y1 = MIN (box.Y1, line->Point1.Y - line->Thickness / 2);
795 box.X1 = MIN (box.X1, line->Point2.X - line->Thickness / 2);
796 box.Y1 = MIN (box.Y1, line->Point2.Y - line->Thickness / 2);
797 box.X2 = MAX (box.X2, line->Point1.X + line->Thickness / 2);
798 box.Y2 = MAX (box.Y2, line->Point1.Y + line->Thickness / 2);
799 box.X2 = MAX (box.X2, line->Point2.X + line->Thickness / 2);
800 box.Y2 = MAX (box.Y2, line->Point2.Y + line->Thickness / 2);
802 ENDALL_LOOP;
803 ALLARC_LOOP (Data);
805 box.X1 = MIN (box.X1, arc->BoundingBox.X1);
806 box.Y1 = MIN (box.Y1, arc->BoundingBox.Y1);
807 box.X2 = MAX (box.X2, arc->BoundingBox.X2);
808 box.Y2 = MAX (box.Y2, arc->BoundingBox.Y2);
810 ENDALL_LOOP;
811 ALLTEXT_LOOP (Data);
813 box.X1 = MIN (box.X1, text->BoundingBox.X1);
814 box.Y1 = MIN (box.Y1, text->BoundingBox.Y1);
815 box.X2 = MAX (box.X2, text->BoundingBox.X2);
816 box.Y2 = MAX (box.Y2, text->BoundingBox.Y2);
818 ENDALL_LOOP;
819 ALLPOLYGON_LOOP (Data);
821 box.X1 = MIN (box.X1, polygon->BoundingBox.X1);
822 box.Y1 = MIN (box.Y1, polygon->BoundingBox.Y1);
823 box.X2 = MAX (box.X2, polygon->BoundingBox.X2);
824 box.Y2 = MAX (box.Y2, polygon->BoundingBox.Y2);
826 ENDALL_LOOP;
827 return (IsDataEmpty (Data) ? NULL : &box);
830 /* ---------------------------------------------------------------------------
831 * centers the displayed PCB around the specified point (X,Y)
833 void
834 CenterDisplay (Coord X, Coord Y)
836 Coord save_grid = PCB->Grid;
837 PCB->Grid = 1;
838 if (MoveCrosshairAbsolute (X, Y))
839 notify_crosshair_change (true);
840 gui->set_crosshair (Crosshair.X, Crosshair.Y, HID_SC_WARP_POINTER);
841 PCB->Grid = save_grid;
844 /* ---------------------------------------------------------------------------
845 * transforms symbol coordinates so that the left edge of each symbol
846 * is at the zero position. The y coordinates are moved so that min(y) = 0
849 void
850 SetFontInfo (FontType *Ptr)
852 Cardinal i, j;
853 SymbolType *symbol;
854 LineType *line;
855 Coord totalminy = MAX_COORD;
857 /* calculate cell with and height (is at least DEFAULT_CELLSIZE)
858 * maximum cell width and height
859 * minimum x and y position of all lines
861 Ptr->MaxWidth = DEFAULT_CELLSIZE;
862 Ptr->MaxHeight = DEFAULT_CELLSIZE;
863 for (i = 0, symbol = Ptr->Symbol; i <= MAX_FONTPOSITION; i++, symbol++)
865 Coord minx, miny, maxx, maxy;
867 /* next one if the index isn't used or symbol is empty (SPACE) */
868 if (!symbol->Valid || !symbol->LineN)
869 continue;
871 minx = miny = MAX_COORD;
872 maxx = maxy = 0;
873 for (line = symbol->Line, j = symbol->LineN; j; j--, line++)
875 minx = MIN (minx, line->Point1.X);
876 miny = MIN (miny, line->Point1.Y);
877 minx = MIN (minx, line->Point2.X);
878 miny = MIN (miny, line->Point2.Y);
879 maxx = MAX (maxx, line->Point1.X);
880 maxy = MAX (maxy, line->Point1.Y);
881 maxx = MAX (maxx, line->Point2.X);
882 maxy = MAX (maxy, line->Point2.Y);
885 /* move symbol to left edge */
886 for (line = symbol->Line, j = symbol->LineN; j; j--, line++)
887 MOVE_LINE_LOWLEVEL (line, -minx, 0);
889 /* set symbol bounding box with a minimum cell size of (1,1) */
890 symbol->Width = maxx - minx + 1;
891 symbol->Height = maxy + 1;
893 /* check total min/max */
894 Ptr->MaxWidth = MAX (Ptr->MaxWidth, symbol->Width);
895 Ptr->MaxHeight = MAX (Ptr->MaxHeight, symbol->Height);
896 totalminy = MIN (totalminy, miny);
899 /* move coordinate system to the upper edge (lowest y on screen) */
900 for (i = 0, symbol = Ptr->Symbol; i <= MAX_FONTPOSITION; i++, symbol++)
901 if (symbol->Valid)
903 symbol->Height -= totalminy;
904 for (line = symbol->Line, j = symbol->LineN; j; j--, line++)
905 MOVE_LINE_LOWLEVEL (line, 0, -totalminy);
908 /* setup the box for the default symbol */
909 Ptr->DefaultSymbol.X1 = Ptr->DefaultSymbol.Y1 = 0;
910 Ptr->DefaultSymbol.X2 = Ptr->DefaultSymbol.X1 + Ptr->MaxWidth;
911 Ptr->DefaultSymbol.Y2 = Ptr->DefaultSymbol.Y1 + Ptr->MaxHeight;
914 static Coord
915 GetNum (char **s, const char *default_unit)
917 /* Read value */
918 Coord ret_val = GetValueEx (*s, NULL, NULL, NULL, default_unit);
919 /* Advance pointer */
920 while(isalnum(**s) || **s == '.')
921 (*s)++;
922 return ret_val;
925 /*! \brief Serializes the route style list
926 * \par Function Description
927 * Right now n_styles should always be set to NUM_STYLES,
928 * since that is the number of route styles ParseRouteString()
929 * expects to parse.
931 char *
932 make_route_string (RouteStyleType rs[], int n_styles)
934 GString *str = g_string_new ("");
935 gint i;
937 for (i = 0; i < n_styles; ++i)
939 char *r_string = pcb_g_strdup_printf ("%s,%mc,%mc,%mc,%mc", rs[i].Name,
940 rs[i].Thick, rs[i].Diameter,
941 rs[i].Hole, rs[i].Keepaway);
942 if (i > 0)
943 g_string_append_c (str, ':');
944 g_string_append (str, r_string);
946 return g_string_free (str, FALSE);
949 /* ----------------------------------------------------------------------
950 * parses the routes definition string which is a colon separated list of
951 * comma separated Name, Dimension, Dimension, Dimension, Dimension
952 * e.g. Signal,20,40,20,10:Power,40,60,28,10:...
955 ParseRouteString (char *s, RouteStyleType *routeStyle, const char *default_unit)
957 int i, style;
958 char Name[256];
960 memset (routeStyle, 0, NUM_STYLES * sizeof (RouteStyleType));
961 for (style = 0; style < NUM_STYLES; style++, routeStyle++)
963 while (*s && isspace ((int) *s))
964 s++;
965 for (i = 0; *s && *s != ','; i++)
966 Name[i] = *s++;
967 Name[i] = '\0';
968 routeStyle->Name = strdup (Name);
969 if (!isdigit ((int) *++s))
970 goto error;
971 routeStyle->Thick = GetNum (&s, default_unit);
972 while (*s && isspace ((int) *s))
973 s++;
974 if (*s++ != ',')
975 goto error;
976 while (*s && isspace ((int) *s))
977 s++;
978 if (!isdigit ((int) *s))
979 goto error;
980 routeStyle->Diameter = GetNum (&s, default_unit);
981 while (*s && isspace ((int) *s))
982 s++;
983 if (*s++ != ',')
984 goto error;
985 while (*s && isspace ((int) *s))
986 s++;
987 if (!isdigit ((int) *s))
988 goto error;
989 routeStyle->Hole = GetNum (&s, default_unit);
990 /* for backwards-compatibility, we use a 10-mil default
991 * for styles which omit the keepaway specification. */
992 if (*s != ',')
993 routeStyle->Keepaway = MIL_TO_COORD(10);
994 else
996 s++;
997 while (*s && isspace ((int) *s))
998 s++;
999 if (!isdigit ((int) *s))
1000 goto error;
1001 routeStyle->Keepaway = GetNum (&s, default_unit);
1002 while (*s && isspace ((int) *s))
1003 s++;
1005 if (style < NUM_STYLES - 1)
1007 while (*s && isspace ((int) *s))
1008 s++;
1009 if (*s++ != ':')
1010 goto error;
1013 return (0);
1015 error:
1016 memset (routeStyle, 0, NUM_STYLES * sizeof (RouteStyleType));
1017 return (1);
1020 /* ----------------------------------------------------------------------
1021 * parses the group definition string which is a colon separated list of
1022 * comma separated layer numbers (1,2,b:4,6,8,t)
1025 ParseGroupString (char *group_string, LayerGroupType *LayerGroup, int *LayerN)
1027 char *s;
1028 int group, member, layer;
1029 bool c_set = false, /* flags for the two special layers to */
1030 s_set = false; /* provide a default setting for old formats */
1031 int groupnum[MAX_LAYER + 2];
1033 *LayerN = 0;
1035 /* Deterimine the maximum layer number */
1036 for (s = group_string; s && *s; s++)
1038 while (*s && isspace ((int) *s))
1039 s++;
1041 switch (*s)
1043 case 'c':
1044 case 'C':
1045 case 't':
1046 case 'T':
1047 case 's':
1048 case 'S':
1049 case 'b':
1050 case 'B':
1051 break;
1053 default:
1054 if (!isdigit ((int) *s))
1055 goto error;
1056 *LayerN = MAX (*LayerN, atoi (s));
1057 break;
1060 while (*++s && isdigit ((int) *s));
1062 /* ignore white spaces and check for separator */
1063 while (*s && isspace ((int) *s))
1064 s++;
1066 if (*s == '\0')
1067 break;
1069 if (*s != ':' && *s != ',')
1070 goto error;
1073 /* clear struct */
1074 memset (LayerGroup, 0, sizeof (LayerGroupType));
1076 /* Clear assignments */
1077 for (layer = 0; layer < MAX_LAYER + 2; layer++)
1078 groupnum[layer] = -1;
1080 /* loop over all groups */
1081 for (s = group_string, group = 0;
1082 s && *s && group < *LayerN;
1083 group++)
1085 while (*s && isspace ((int) *s))
1086 s++;
1088 /* loop over all group members */
1089 for (member = 0; *s; s++)
1091 /* ignore white spaces and get layernumber */
1092 while (*s && isspace ((int) *s))
1093 s++;
1094 switch (*s)
1096 case 'c':
1097 case 'C':
1098 case 't':
1099 case 'T':
1100 layer = *LayerN + TOP_SILK_LAYER;
1101 c_set = true;
1102 break;
1104 case 's':
1105 case 'S':
1106 case 'b':
1107 case 'B':
1108 layer = *LayerN + BOTTOM_SILK_LAYER;
1109 s_set = true;
1110 break;
1112 default:
1113 layer = atoi (s) - 1;
1114 break;
1116 if (layer > *LayerN + MAX (BOTTOM_SILK_LAYER, TOP_SILK_LAYER) ||
1117 member >= *LayerN + 1)
1118 goto error;
1119 groupnum[layer] = group;
1120 LayerGroup->Entries[group][member++] = layer;
1121 while (*++s && isdigit ((int) *s));
1123 /* ignore white spaces and check for separator */
1124 while (*s && isspace ((int) *s))
1125 s++;
1126 if (!*s || *s == ':')
1127 break;
1129 LayerGroup->Number[group] = member;
1130 if (*s == ':')
1131 s++;
1134 /* If no explicit solder or component layer group was found in the layer
1135 * group string, make group 0 the bottom side, and group 1 the top side.
1136 * This is done by assigning the relevant silkscreen layers to those groups.
1138 if (!s_set)
1139 LayerGroup->Entries[0][LayerGroup->Number[0]++] = *LayerN + BOTTOM_SILK_LAYER;
1140 if (!c_set)
1141 LayerGroup->Entries[1][LayerGroup->Number[1]++] = *LayerN + TOP_SILK_LAYER;
1143 /* Assign a unique layer group to each layer that was not explicitly
1144 * assigned a particular group by its presence in the layer group string.
1146 for (layer = 0; layer < *LayerN && group < *LayerN; layer++)
1147 if (groupnum[layer] == -1)
1149 LayerGroup->Entries[group][0] = layer;
1150 LayerGroup->Number[group] = 1;
1151 group++;
1153 return (0);
1155 /* reset structure on error */
1156 error:
1157 memset (LayerGroup, 0, sizeof (LayerGroupType));
1158 return (1);
1161 /* ---------------------------------------------------------------------------
1162 * quits application
1164 void
1165 QuitApplication (void)
1168 * save data if necessary. It not needed, then don't trigger EmergencySave
1169 * via our atexit() registering of EmergencySave(). We presumeably wanted to
1170 * exit here and thus it is not an emergency.
1172 if (PCB->Changed && Settings.SaveInTMP)
1173 EmergencySave ();
1174 else
1175 DisableEmergencySave ();
1177 /* Free up memory allocated to the PCB. Why bother when we're about to exit ?
1178 * Because it removes some false positives from heap bug detectors such as
1179 * lib dmalloc.
1181 FreePCBMemory(PCB);
1183 exit (0);
1186 /* ---------------------------------------------------------------------------
1187 * creates a filename from a template
1188 * %f is replaced by the filename
1189 * %p by the searchpath
1191 char *
1192 EvaluateFilename (char *Template, char *Path, char *Filename, char *Parameter)
1194 static DynamicStringType command;
1195 char *p;
1197 if (Settings.verbose)
1199 printf ("EvaluateFilename:\n");
1200 printf ("\tTemplate: \033[33m%s\033[0m\n", Template);
1201 printf ("\tPath: \033[33m%s\033[0m\n", Path);
1202 printf ("\tFilename: \033[33m%s\033[0m\n", Filename);
1203 printf ("\tParameter: \033[33m%s\033[0m\n", Parameter);
1206 DSClearString (&command);
1208 for (p = Template; p && *p; p++)
1210 /* copy character or add string to command */
1211 if (*p == '%'
1212 && (*(p + 1) == 'f' || *(p + 1) == 'p' || *(p + 1) == 'a'))
1213 switch (*(++p))
1215 case 'a':
1216 DSAddString (&command, Parameter);
1217 break;
1218 case 'f':
1219 DSAddString (&command, Filename);
1220 break;
1221 case 'p':
1222 DSAddString (&command, Path);
1223 break;
1225 else
1226 DSAddCharacter (&command, *p);
1228 DSAddCharacter (&command, '\0');
1229 if (Settings.verbose)
1230 printf ("EvaluateFilename: \033[32m%s\033[0m\n", command.Data);
1232 return strdup (command.Data);
1235 /* ---------------------------------------------------------------------------
1236 * concatenates directory and filename if directory != NULL,
1237 * expands them with a shell and returns the found name(s) or NULL
1239 char *
1240 ExpandFilename (char *Dirname, char *Filename)
1242 static DynamicStringType answer;
1243 char *command;
1244 FILE *pipe;
1245 int c;
1247 /* allocate memory for commandline and build it */
1248 DSClearString (&answer);
1249 if (Dirname)
1251 command = (char *)calloc (strlen (Filename) + strlen (Dirname) + 7,
1252 sizeof (char));
1253 sprintf (command, "echo %s/%s", Dirname, Filename);
1255 else
1257 command = (char *)calloc (strlen (Filename) + 6, sizeof (char));
1258 sprintf (command, "echo %s", Filename);
1261 /* execute it with shell */
1262 if ((pipe = popen (command, "r")) != NULL)
1264 /* discard all but the first returned line */
1265 for (;;)
1267 if ((c = fgetc (pipe)) == EOF || c == '\n' || c == '\r')
1268 break;
1269 else
1270 DSAddCharacter (&answer, c);
1273 free (command);
1274 return (pclose (pipe) ? NULL : answer.Data);
1277 /* couldn't be expanded by the shell */
1278 PopenErrorMessage (command);
1279 free (command);
1280 return (NULL);
1284 /* ---------------------------------------------------------------------------
1285 * returns the layer number for the passed pointer
1288 GetLayerNumber (DataType *Data, LayerType *Layer)
1290 int i;
1292 for (i = 0; i < MAX_LAYER + 2; i++)
1293 if (Layer == &Data->Layer[i])
1294 break;
1295 return (i);
1298 /* ---------------------------------------------------------------------------
1299 * move layer (number is passed in) to top of layerstack
1301 static void
1302 PushOnTopOfLayerStack (int NewTop)
1304 int i;
1306 /* ignore silk and other extra layers */
1307 if (NewTop < max_copper_layer)
1309 /* first find position of passed one */
1310 for (i = 0; i < max_copper_layer; i++)
1311 if (LayerStack[i] == NewTop)
1312 break;
1314 /* bring this element to the top of the stack */
1315 for (; i; i--)
1316 LayerStack[i] = LayerStack[i - 1];
1317 LayerStack[0] = NewTop;
1322 /* ----------------------------------------------------------------------
1323 * changes the visibility of all layers in a group
1324 * returns the number of changed layers
1327 ChangeGroupVisibility (int Layer, bool On, bool ChangeStackOrder)
1329 int group, i, changed = 1; /* at least the current layer changes */
1331 /* Warning: these special case values must agree with what gui-top-window.c
1332 | thinks the are.
1335 if (Settings.verbose)
1336 printf ("ChangeGroupVisibility(Layer=%d, On=%d, ChangeStackOrder=%d)\n",
1337 Layer, On, ChangeStackOrder);
1339 /* decrement 'i' to keep stack in order of layergroup */
1340 group = GetLayerGroupNumberByNumber (Layer);
1341 for (i = PCB->LayerGroups.Number[group]; i;)
1343 int layer = PCB->LayerGroups.Entries[group][--i];
1345 /* don't count the passed member of the group */
1346 if (layer != Layer && layer < max_copper_layer)
1348 PCB->Data->Layer[layer].On = On;
1350 /* push layer on top of stack if switched on */
1351 if (On && ChangeStackOrder)
1352 PushOnTopOfLayerStack (layer);
1353 changed++;
1357 /* change at least the passed layer */
1358 PCB->Data->Layer[Layer].On = On;
1359 if (On && ChangeStackOrder)
1360 PushOnTopOfLayerStack (Layer);
1362 /* update control panel and exit */
1363 hid_action ("LayersChanged");
1364 return (changed);
1367 /* ----------------------------------------------------------------------
1368 * Given a string description of a layer stack, adjust the layer stack
1369 * to correspond.
1372 void
1373 LayerStringToLayerStack (char *s)
1375 static int listed_layers = 0;
1376 int l = strlen (s);
1377 char **args;
1378 int i, argn, lno;
1379 int prev_sep = 1;
1381 s = strdup (s);
1382 args = (char **) malloc (l * sizeof (char *));
1383 argn = 0;
1385 for (i=0; i<l; i++)
1387 switch (s[i])
1389 case ' ':
1390 case '\t':
1391 case ',':
1392 case ';':
1393 case ':':
1394 prev_sep = 1;
1395 s[i] = '\0';
1396 break;
1397 default:
1398 if (prev_sep)
1399 args[argn++] = s+i;
1400 prev_sep = 0;
1401 break;
1405 for (i = 0; i < max_copper_layer + 2; i++)
1407 if (i < max_copper_layer)
1408 LayerStack[i] = i;
1409 PCB->Data->Layer[i].On = false;
1411 PCB->ElementOn = false;
1412 PCB->InvisibleObjectsOn = false;
1413 PCB->PinOn = false;
1414 PCB->ViaOn = false;
1415 PCB->RatOn = false;
1416 CLEAR_FLAG (SHOWMASKFLAG, PCB);
1417 Settings.ShowBottomSide = 0;
1419 for (i=argn-1; i>=0; i--)
1421 if (strcasecmp (args[i], "rats") == 0)
1422 PCB->RatOn = true;
1423 else if (strcasecmp (args[i], "invisible") == 0)
1424 PCB->InvisibleObjectsOn = true;
1425 else if (strcasecmp (args[i], "pins") == 0)
1426 PCB->PinOn = true;
1427 else if (strcasecmp (args[i], "vias") == 0)
1428 PCB->ViaOn = true;
1429 else if (strcasecmp (args[i], "elements") == 0
1430 || strcasecmp (args[i], "silk") == 0)
1431 PCB->ElementOn = true;
1432 else if (strcasecmp (args[i], "mask") == 0)
1433 SET_FLAG (SHOWMASKFLAG, PCB);
1434 else if (strcasecmp (args[i], "solderside") == 0)
1435 Settings.ShowBottomSide = 1;
1436 else if (isdigit ((int) args[i][0]))
1438 lno = atoi (args[i]);
1439 ChangeGroupVisibility (lno, true, true);
1441 else
1443 int found = 0;
1444 for (lno = 0; lno < max_copper_layer; lno++)
1445 if (strcasecmp (args[i], PCB->Data->Layer[lno].Name) == 0)
1447 ChangeGroupVisibility (lno, true, true);
1448 found = 1;
1449 break;
1451 if (!found)
1453 fprintf(stderr, _("Warning: layer \"%s\" not known\n"), args[i]);
1454 if (!listed_layers)
1456 fprintf (stderr, _("Named layers in this board are:\n"));
1457 listed_layers = 1;
1458 for (lno=0; lno < max_copper_layer; lno ++)
1459 fprintf(stderr, "\t%s\n", PCB->Data->Layer[lno].Name);
1460 fprintf(stderr, _("Also: component, solder, rats, invisible, "
1461 "pins, vias, elements or silk, mask, solderside.\n"));
1468 /* ---------------------------------------------------------------------------
1469 * returns the layergroup number for the passed pointer
1472 GetLayerGroupNumberByPointer (LayerType *Layer)
1474 return (GetLayerGroupNumberByNumber (GetLayerNumber (PCB->Data, Layer)));
1477 /* ---------------------------------------------------------------------------
1478 * returns the layergroup number for the passed layernumber
1481 GetLayerGroupNumberByNumber (Cardinal Layer)
1483 int group, entry;
1485 for (group = 0; group < max_group; group++)
1486 for (entry = 0; entry < PCB->LayerGroups.Number[group]; entry++)
1487 if (PCB->LayerGroups.Entries[group][entry] == Layer)
1488 return (group);
1490 /* since every layer belongs to a group it is safe to return
1491 * the value without boundary checking
1493 return (group);
1496 /* ---------------------------------------------------------------------------
1497 * returns the layergroup number for the passed side (TOP_LAYER or BOTTOM_LAYER)
1500 GetLayerGroupNumberBySide (int side)
1502 /* Find the relavant board side layer group by determining the
1503 * layer group associated with the relevant side's silk-screen
1505 return GetLayerGroupNumberByNumber(
1506 side == TOP_SIDE ? top_silk_layer : bottom_silk_layer);
1509 /* ---------------------------------------------------------------------------
1510 * returns a pointer to an objects bounding box;
1511 * data is valid until the routine is called again
1513 BoxType *
1514 GetObjectBoundingBox (int Type, void *Ptr1, void *Ptr2, void *Ptr3)
1516 switch (Type)
1518 case LINE_TYPE:
1519 case ARC_TYPE:
1520 case TEXT_TYPE:
1521 case POLYGON_TYPE:
1522 case PAD_TYPE:
1523 case PIN_TYPE:
1524 case ELEMENTNAME_TYPE:
1525 return (BoxType *)Ptr2;
1526 case VIA_TYPE:
1527 case ELEMENT_TYPE:
1528 return (BoxType *)Ptr1;
1529 case POLYGONPOINT_TYPE:
1530 case LINEPOINT_TYPE:
1531 return (BoxType *)Ptr3;
1532 default:
1533 Message (_("Request for bounding box of unsupported type %d\n"), Type);
1534 return (BoxType *)Ptr2;
1538 /* ---------------------------------------------------------------------------
1539 * computes the bounding box of an arc
1541 void
1542 SetArcBoundingBox (ArcType *Arc)
1544 double ca1, ca2, sa1, sa2;
1545 double minx, maxx, miny, maxy;
1546 Angle ang1, ang2;
1547 Coord width;
1549 /* first put angles into standard form:
1550 * ang1 < ang2, both angles between 0 and 720 */
1551 Arc->Delta = CLAMP (Arc->Delta, -360, 360);
1553 if (Arc->Delta > 0)
1555 ang1 = NormalizeAngle (Arc->StartAngle);
1556 ang2 = NormalizeAngle (Arc->StartAngle + Arc->Delta);
1558 else
1560 ang1 = NormalizeAngle (Arc->StartAngle + Arc->Delta);
1561 ang2 = NormalizeAngle (Arc->StartAngle);
1563 if (ang1 > ang2)
1564 ang2 += 360;
1565 /* Make sure full circles aren't treated as zero-length arcs */
1566 if (Arc->Delta == 360 || Arc->Delta == -360)
1567 ang2 = ang1 + 360;
1569 /* calculate sines, cosines */
1570 sa1 = sin (M180 * ang1);
1571 ca1 = cos (M180 * ang1);
1572 sa2 = sin (M180 * ang2);
1573 ca2 = cos (M180 * ang2);
1575 minx = MIN (ca1, ca2);
1576 maxx = MAX (ca1, ca2);
1577 miny = MIN (sa1, sa2);
1578 maxy = MAX (sa1, sa2);
1580 /* Check for extreme angles */
1581 if ((ang1 <= 0 && ang2 >= 0) || (ang1 <= 360 && ang2 >= 360)) maxx = 1;
1582 if ((ang1 <= 90 && ang2 >= 90) || (ang1 <= 450 && ang2 >= 450)) maxy = 1;
1583 if ((ang1 <= 180 && ang2 >= 180) || (ang1 <= 540 && ang2 >= 540)) minx = -1;
1584 if ((ang1 <= 270 && ang2 >= 270) || (ang1 <= 630 && ang2 >= 630)) miny = -1;
1586 /* Finally, calcate bounds, converting sane geometry into pcb geometry */
1587 Arc->BoundingBox.X1 = Arc->X - Arc->Width * maxx;
1588 Arc->BoundingBox.X2 = Arc->X - Arc->Width * minx;
1589 Arc->BoundingBox.Y1 = Arc->Y + Arc->Height * miny;
1590 Arc->BoundingBox.Y2 = Arc->Y + Arc->Height * maxy;
1592 width = (Arc->Thickness + Arc->Clearance) / 2;
1594 /* Adjust for our discrete polygon approximation */
1595 width = (double)width * MAX (POLY_CIRC_RADIUS_ADJ, (1.0 + POLY_ARC_MAX_DEVIATION)) + 0.5;
1597 Arc->BoundingBox.X1 -= width;
1598 Arc->BoundingBox.X2 += width;
1599 Arc->BoundingBox.Y1 -= width;
1600 Arc->BoundingBox.Y2 += width;
1601 close_box(&Arc->BoundingBox);
1603 /* Update the arc end-points */
1604 Arc->Point1.X = Arc->X - (double)Arc->Width * ca1;
1605 Arc->Point1.Y = Arc->Y + (double)Arc->Height * sa1;
1606 Arc->Point2.X = Arc->X - (double)Arc->Width * ca2;
1607 Arc->Point2.Y = Arc->Y + (double)Arc->Height * sa2;
1610 /* ---------------------------------------------------------------------------
1611 * resets the layerstack setting
1613 void
1614 ResetStackAndVisibility (void)
1616 int top_group;
1617 Cardinal i;
1619 for (i = 0; i < max_copper_layer + 2; i++)
1621 if (i < max_copper_layer)
1622 LayerStack[i] = i;
1623 PCB->Data->Layer[i].On = true;
1625 PCB->ElementOn = true;
1626 PCB->InvisibleObjectsOn = true;
1627 PCB->PinOn = true;
1628 PCB->ViaOn = true;
1629 PCB->RatOn = true;
1631 /* Bring the component group to the front and make it active. */
1632 top_group = GetLayerGroupNumberBySide (TOP_SIDE);
1633 ChangeGroupVisibility (PCB->LayerGroups.Entries[top_group][0], 1, 1);
1636 /* ---------------------------------------------------------------------------
1637 * saves the layerstack setting
1639 void
1640 SaveStackAndVisibility (void)
1642 Cardinal i;
1643 static bool run = false;
1645 if (run == false)
1647 SavedStack.cnt = 0;
1648 run = true;
1651 if (SavedStack.cnt != 0)
1653 fprintf (stderr,
1654 "SaveStackAndVisibility() layerstack was already saved and not"
1655 "yet restored. cnt = %d\n", SavedStack.cnt);
1658 for (i = 0; i < max_copper_layer + 2; i++)
1660 if (i < max_copper_layer)
1661 SavedStack.LayerStack[i] = LayerStack[i];
1662 SavedStack.LayerOn[i] = PCB->Data->Layer[i].On;
1664 SavedStack.ElementOn = PCB->ElementOn;
1665 SavedStack.InvisibleObjectsOn = PCB->InvisibleObjectsOn;
1666 SavedStack.PinOn = PCB->PinOn;
1667 SavedStack.ViaOn = PCB->ViaOn;
1668 SavedStack.RatOn = PCB->RatOn;
1669 SavedStack.cnt++;
1672 /* ---------------------------------------------------------------------------
1673 * restores the layerstack setting
1675 void
1676 RestoreStackAndVisibility (void)
1678 Cardinal i;
1680 if (SavedStack.cnt == 0)
1682 fprintf (stderr, "RestoreStackAndVisibility() layerstack has not"
1683 " been saved. cnt = %d\n", SavedStack.cnt);
1684 return;
1686 else if (SavedStack.cnt != 1)
1688 fprintf (stderr, "RestoreStackAndVisibility() layerstack save count is"
1689 " wrong. cnt = %d\n", SavedStack.cnt);
1692 for (i = 0; i < max_copper_layer + 2; i++)
1694 if (i < max_copper_layer)
1695 LayerStack[i] = SavedStack.LayerStack[i];
1696 PCB->Data->Layer[i].On = SavedStack.LayerOn[i];
1698 PCB->ElementOn = SavedStack.ElementOn;
1699 PCB->InvisibleObjectsOn = SavedStack.InvisibleObjectsOn;
1700 PCB->PinOn = SavedStack.PinOn;
1701 PCB->ViaOn = SavedStack.ViaOn;
1702 PCB->RatOn = SavedStack.RatOn;
1704 SavedStack.cnt--;
1707 /* ----------------------------------------------------------------------
1708 * returns pointer to current working directory. If 'path' is not
1709 * NULL, then the current working directory is copied to the array
1710 * pointed to by 'path'
1712 char *
1713 GetWorkingDirectory (char *path)
1715 #ifdef HAVE_GETCWD
1716 return getcwd (path, MAXPATHLEN);
1717 #else
1718 /* seems that some BSD releases lack of a prototype for getwd() */
1719 return getwd (path);
1720 #endif
1724 /* ---------------------------------------------------------------------------
1725 * writes a string to the passed file pointer
1726 * some special characters are quoted
1728 void
1729 CreateQuotedString (DynamicStringType *DS, char *S)
1731 DSClearString (DS);
1732 DSAddCharacter (DS, '"');
1733 while (*S)
1735 if (*S == '"' || *S == '\\')
1736 DSAddCharacter (DS, '\\');
1737 DSAddCharacter (DS, *S++);
1739 DSAddCharacter (DS, '"');
1742 BoxType *
1743 GetArcEnds (ArcType *Arc)
1745 static BoxType box;
1746 box.X1 = Arc->X - Arc->Width * cos (Arc->StartAngle * M180);
1747 box.Y1 = Arc->Y + Arc->Height * sin (Arc->StartAngle * M180);
1748 box.X2 = Arc->X - Arc->Width * cos ((Arc->StartAngle + Arc->Delta) * M180);
1749 box.Y2 = Arc->Y + Arc->Height * sin ((Arc->StartAngle + Arc->Delta) * M180);
1750 return &box;
1754 /* doesn't this belong in change.c ?? */
1755 void
1756 ChangeArcAngles (LayerType *Layer, ArcType *a,
1757 Angle new_sa, Angle new_da)
1759 if (new_da >= 360)
1761 new_da = 360;
1762 new_sa = 0;
1764 RestoreToPolygon (PCB->Data, ARC_TYPE, Layer, a);
1765 r_delete_entry (Layer->arc_tree, (BoxType *) a);
1766 AddObjectToChangeAnglesUndoList (ARC_TYPE, a, a, a);
1767 a->StartAngle = new_sa;
1768 a->Delta = new_da;
1769 SetArcBoundingBox (a);
1770 r_insert_entry (Layer->arc_tree, (BoxType *) a, 0);
1771 ClearFromPolygon (PCB->Data, ARC_TYPE, Layer, a);
1774 static char *
1775 BumpName (char *Name)
1777 int num;
1778 char c, *start;
1779 static char temp[256];
1781 start = Name;
1782 /* seek end of string */
1783 while (*Name != 0)
1784 Name++;
1785 /* back up to potential number */
1786 for (Name--; isdigit ((int) *Name); Name--);
1787 Name++;
1788 if (*Name)
1789 num = atoi (Name) + 1;
1790 else
1791 num = 1;
1792 c = *Name;
1793 *Name = 0;
1794 sprintf (temp, "%s%d", start, num);
1795 /* if this is not our string, put back the blown character */
1796 if (start != temp)
1797 *Name = c;
1798 return (temp);
1802 * make a unique name for the name on board
1803 * this can alter the contents of the input string
1805 char *
1806 UniqueElementName (DataType *Data, char *Name)
1808 bool unique = true;
1809 /* null strings are ok */
1810 if (!Name || !*Name)
1811 return (Name);
1813 for (;;)
1815 ELEMENT_LOOP (Data);
1817 if (NAMEONPCB_NAME (element) &&
1818 NSTRCMP (NAMEONPCB_NAME (element), Name) == 0)
1820 Name = BumpName (Name);
1821 unique = false;
1822 break;
1825 END_LOOP;
1826 if (unique)
1827 return (Name);
1828 unique = true;
1832 static void
1833 GetGridLockCoordinates (int type, void *ptr1,
1834 void *ptr2, void *ptr3, Coord * x,
1835 Coord * y)
1837 switch (type)
1839 case VIA_TYPE:
1840 *x = ((PinType *) ptr2)->X;
1841 *y = ((PinType *) ptr2)->Y;
1842 break;
1843 case LINE_TYPE:
1844 *x = ((LineType *) ptr2)->Point1.X;
1845 *y = ((LineType *) ptr2)->Point1.Y;
1846 break;
1847 case TEXT_TYPE:
1848 case ELEMENTNAME_TYPE:
1849 *x = ((TextType *) ptr2)->X;
1850 *y = ((TextType *) ptr2)->Y;
1851 break;
1852 case ELEMENT_TYPE:
1853 *x = ((ElementType *) ptr2)->MarkX;
1854 *y = ((ElementType *) ptr2)->MarkY;
1855 break;
1856 case POLYGON_TYPE:
1857 *x = ((PolygonType *) ptr2)->Points[0].X;
1858 *y = ((PolygonType *) ptr2)->Points[0].Y;
1859 break;
1861 case LINEPOINT_TYPE:
1862 case POLYGONPOINT_TYPE:
1863 *x = ((PointType *) ptr3)->X;
1864 *y = ((PointType *) ptr3)->Y;
1865 break;
1866 case ARC_TYPE:
1868 BoxType *box;
1870 box = GetArcEnds ((ArcType *) ptr2);
1871 *x = box->X1;
1872 *y = box->Y1;
1873 break;
1878 void
1879 AttachForCopy (Coord PlaceX, Coord PlaceY)
1881 BoxType *box;
1882 Coord mx = 0, my = 0;
1884 Crosshair.AttachedObject.RubberbandN = 0;
1885 if (! TEST_FLAG (SNAPPINFLAG, PCB))
1887 /* dither the grab point so that the mark, center, etc
1888 * will end up on a grid coordinate
1890 GetGridLockCoordinates (Crosshair.AttachedObject.Type,
1891 Crosshair.AttachedObject.Ptr1,
1892 Crosshair.AttachedObject.Ptr2,
1893 Crosshair.AttachedObject.Ptr3, &mx, &my);
1894 mx = GridFit (mx, PCB->Grid, PCB->GridOffsetX) - mx;
1895 my = GridFit (my, PCB->Grid, PCB->GridOffsetY) - my;
1897 Crosshair.AttachedObject.X = PlaceX - mx;
1898 Crosshair.AttachedObject.Y = PlaceY - my;
1899 if (!Marked.status || TEST_FLAG (LOCALREFFLAG, PCB))
1900 SetLocalRef (PlaceX - mx, PlaceY - my, true);
1901 Crosshair.AttachedObject.State = STATE_SECOND;
1903 /* get boundingbox of object and set cursor range */
1904 box = GetObjectBoundingBox (Crosshair.AttachedObject.Type,
1905 Crosshair.AttachedObject.Ptr1,
1906 Crosshair.AttachedObject.Ptr2,
1907 Crosshair.AttachedObject.Ptr3);
1908 SetCrosshairRange (Crosshair.AttachedObject.X - box->X1,
1909 Crosshair.AttachedObject.Y - box->Y1,
1910 PCB->MaxWidth - (box->X2 - Crosshair.AttachedObject.X),
1911 PCB->MaxHeight - (box->Y2 - Crosshair.AttachedObject.Y));
1913 /* get all attached objects if necessary */
1914 if ((Settings.Mode != COPY_MODE) && TEST_FLAG (RUBBERBANDFLAG, PCB))
1915 LookupRubberbandLines (Crosshair.AttachedObject.Type,
1916 Crosshair.AttachedObject.Ptr1,
1917 Crosshair.AttachedObject.Ptr2,
1918 Crosshair.AttachedObject.Ptr3);
1919 if (Settings.Mode != COPY_MODE &&
1920 (Crosshair.AttachedObject.Type == ELEMENT_TYPE ||
1921 Crosshair.AttachedObject.Type == VIA_TYPE ||
1922 Crosshair.AttachedObject.Type == LINE_TYPE ||
1923 Crosshair.AttachedObject.Type == LINEPOINT_TYPE))
1924 LookupRatLines (Crosshair.AttachedObject.Type,
1925 Crosshair.AttachedObject.Ptr1,
1926 Crosshair.AttachedObject.Ptr2,
1927 Crosshair.AttachedObject.Ptr3);
1931 * Return nonzero if the given file exists and is readable.
1934 FileExists (const char *name)
1936 FILE *f;
1937 f = fopen (name, "r");
1938 if (f)
1940 fclose (f);
1941 return 1;
1943 return 0;
1946 char *
1947 Concat (const char *first, ...)
1949 char *rv;
1950 int len;
1951 va_list a;
1953 len = strlen (first);
1954 rv = (char *) malloc (len + 1);
1955 strcpy (rv, first);
1957 va_start (a, first);
1958 while (1)
1960 const char *s = va_arg (a, const char *);
1961 if (!s)
1962 break;
1963 len += strlen (s);
1964 rv = (char *) realloc (rv, len + 1);
1965 strcat (rv, s);
1967 va_end (a);
1968 return rv;
1972 mem_any_set (unsigned char *ptr, int bytes)
1974 while (bytes--)
1975 if (*ptr++)
1976 return 1;
1977 return 0;
1980 /* This just fills in a FlagType with current flags. */
1981 FlagType
1982 MakeFlags (unsigned int flags)
1984 FlagType rv;
1985 memset (&rv, 0, sizeof (rv));
1986 rv.f = flags;
1987 return rv;
1990 /* This converts old flag bits (from saved PCB files) to new format. */
1991 FlagType
1992 OldFlags (unsigned int flags)
1994 FlagType rv;
1995 int i, f;
1996 memset (&rv, 0, sizeof (rv));
1997 /* If we move flag bits around, this is where we map old bits to them. */
1998 rv.f = flags & 0xffff;
1999 f = 0x10000;
2000 for (i = 0; i < 8; i++)
2002 /* use the closest thing to the old thermal style */
2003 if (flags & f)
2004 rv.t[i / 2] |= (1 << (4 * (i % 2)));
2005 f <<= 1;
2007 return rv;
2010 FlagType
2011 AddFlags (FlagType flag, unsigned int flags)
2013 flag.f |= flags;
2014 return flag;
2017 FlagType
2018 MaskFlags (FlagType flag, unsigned int flags)
2020 flag.f &= ~flags;
2021 return flag;
2024 /***********************************************************************
2025 * Layer Group Functions
2029 MoveLayerToGroup (int layer, int group)
2031 int prev, i, j;
2033 if (layer < 0 || layer > max_copper_layer + 1)
2034 return -1;
2035 prev = GetLayerGroupNumberByNumber (layer);
2036 if ((layer == bottom_silk_layer
2037 && group == GetLayerGroupNumberByNumber (top_silk_layer))
2038 || (layer == top_silk_layer
2039 && group == GetLayerGroupNumberByNumber (bottom_silk_layer))
2040 || (group < 0 || group >= max_group) || (prev == group))
2041 return prev;
2043 /* Remove layer from prev group */
2044 for (j = i = 0; i < PCB->LayerGroups.Number[prev]; i++)
2045 if (PCB->LayerGroups.Entries[prev][i] != layer)
2046 PCB->LayerGroups.Entries[prev][j++] = PCB->LayerGroups.Entries[prev][i];
2047 PCB->LayerGroups.Number[prev]--;
2049 /* Add layer to new group. */
2050 i = PCB->LayerGroups.Number[group]++;
2051 PCB->LayerGroups.Entries[group][i] = layer;
2053 return group;
2056 char *
2057 LayerGroupsToString (LayerGroupType *lg)
2059 #if MAX_LAYER < 9998
2060 /* Allows for layer numbers 0..9999 */
2061 static char buf[(MAX_LAYER + 2) * 5 + 1];
2062 #endif
2063 char *cp = buf;
2064 char sep = 0;
2065 int group, entry;
2066 for (group = 0; group < max_group; group++)
2067 if (PCB->LayerGroups.Number[group])
2069 if (sep)
2070 *cp++ = ':';
2071 sep = 1;
2072 for (entry = 0; entry < PCB->LayerGroups.Number[group]; entry++)
2074 int layer = PCB->LayerGroups.Entries[group][entry];
2075 if (layer == top_silk_layer)
2077 *cp++ = 'c';
2079 else if (layer == bottom_silk_layer)
2081 *cp++ = 's';
2083 else
2085 sprintf (cp, "%d", layer + 1);
2086 while (*++cp)
2089 if (entry != PCB->LayerGroups.Number[group] - 1)
2090 *cp++ = ',';
2093 *cp++ = 0;
2094 return buf;
2097 char *
2098 pcb_author (void)
2100 #ifdef HAVE_GETPWUID
2101 static struct passwd *pwentry;
2102 static char *fab_author = 0;
2104 if (!fab_author)
2106 if (Settings.FabAuthor && Settings.FabAuthor[0])
2107 fab_author = Settings.FabAuthor;
2108 else
2110 int len;
2111 char *comma, *gecos;
2113 /* ID the user. */
2114 pwentry = getpwuid (getuid ());
2115 gecos = pwentry->pw_gecos;
2116 comma = strchr (gecos, ',');
2117 if (comma)
2118 len = comma - gecos;
2119 else
2120 len = strlen (gecos);
2121 fab_author = (char *)malloc (len + 1);
2122 if (!fab_author)
2124 perror ("pcb: out of memory.\n");
2125 exit (-1);
2127 memcpy (fab_author, gecos, len);
2128 fab_author[len] = 0;
2131 return fab_author;
2132 #else
2133 return "Unknown";
2134 #endif
2138 char *
2139 AttributeGetFromList (AttributeListType *list, char *name)
2141 int i;
2142 for (i=0; i<list->Number; i++)
2143 if (strcmp (name, list->List[i].name) == 0)
2144 return list->List[i].value;
2145 return NULL;
2149 AttributePutToList (AttributeListType *list, const char *name, const char *value, int replace)
2151 int i;
2153 /* If we're allowed to replace an existing attribute, see if we
2154 can. */
2155 if (replace)
2157 for (i=0; i<list->Number; i++)
2158 if (strcmp (name, list->List[i].name) == 0)
2160 free (list->List[i].value);
2161 list->List[i].value = STRDUP (value);
2162 return 1;
2166 /* At this point, we're going to need to add a new attribute to the
2167 list. See if there's room. */
2168 if (list->Number >= list->Max)
2170 list->Max += 10;
2171 list->List = (AttributeType *) realloc (list->List,
2172 list->Max * sizeof (AttributeType));
2175 /* Now add the new attribute. */
2176 i = list->Number;
2177 list->List[i].name = STRDUP (name);
2178 list->List[i].value = STRDUP (value);
2179 list->Number ++;
2180 return 0;
2183 void
2184 AttributeRemoveFromList(AttributeListType *list, char *name)
2186 int i, j;
2187 for (i=0; i<list->Number; i++)
2188 if (strcmp (name, list->List[i].name) == 0)
2190 free (list->List[i].name);
2191 free (list->List[i].value);
2192 for (j=i; j<list->Number-i; j++)
2193 list->List[j] = list->List[j+1];
2194 list->Number --;
2198 /* In future all use of this should be supplanted by
2199 * pcb-printf and %mr/%m# spec */
2200 const char *
2201 c_dtostr (double d)
2203 static char buf[100];
2204 int i, f;
2205 char *bufp = buf;
2207 if (d < 0)
2209 *bufp++ = '-';
2210 d = -d;
2212 d += 0.0000005; /* rounding */
2213 i = floor (d);
2214 d -= i;
2215 sprintf (bufp, "%d", i);
2216 bufp += strlen (bufp);
2217 *bufp++ = '.';
2219 f = floor (d * 1000000.0);
2220 sprintf (bufp, "%06d", f);
2221 return buf;
2224 void
2225 r_delete_element (DataType * data, ElementType * element)
2227 r_delete_entry (data->element_tree, (BoxType *) element);
2228 PIN_LOOP (element);
2230 r_delete_entry (data->pin_tree, (BoxType *) pin);
2232 END_LOOP;
2233 PAD_LOOP (element);
2235 r_delete_entry (data->pad_tree, (BoxType *) pad);
2237 END_LOOP;
2238 ELEMENTTEXT_LOOP (element);
2240 r_delete_entry (data->name_tree[n], (BoxType *) text);
2242 END_LOOP;
2246 /* ---------------------------------------------------------------------------
2247 * Returns a string that has a bunch of information about the program.
2248 * Can be used for things like "about" dialog boxes.
2251 char *
2252 GetInfoString (void)
2254 HID **hids;
2255 int i;
2256 static DynamicStringType info;
2257 static int first_time = 1;
2259 #define TAB " "
2261 if (first_time)
2263 first_time = 0;
2264 DSAddString (&info,
2265 _("This is PCB, an interactive\n"
2266 "printed circuit board editor\n"
2267 "version "));
2268 DSAddString (&info,
2269 VERSION "\n\n"
2270 "Compiled on " __DATE__ " at " __TIME__ "\n\n"
2271 "by harry eaton\n\n"
2272 "Copyright (C) Thomas Nau 1994, 1995, 1996, 1997\n"
2273 "Copyright (C) harry eaton 1998-2007\n"
2274 "Copyright (C) C. Scott Ananian 2001\n"
2275 "Copyright (C) DJ Delorie 2003, 2004, 2005, 2006, 2007, 2008\n"
2276 "Copyright (C) Dan McMahill 2003, 2004, 2005, 2006, 2007, 2008\n\n");
2277 DSAddString (&info,
2278 _("It is licensed under the terms of the GNU\n"
2279 "General Public License version 2\n"
2280 "See the LICENSE file for more information\n\n"
2281 "For more information see:\n"));
2282 DSAddString (&info, _("PCB homepage: "));
2283 DSAddString (&info, "http://pcb.geda-project.org\n");
2284 DSAddString (&info, _("gEDA homepage: "));
2285 DSAddString (&info, "http://www.geda-project.org\n");
2286 DSAddString (&info, _("gEDA Wiki: "));
2287 DSAddString (&info, "http://wiki.geda-project.org\n");
2289 DSAddString (&info, _("\n----- Compile Time Options -----\n"));
2290 hids = hid_enumerate ();
2291 DSAddString (&info, _("GUI:\n"));
2292 for (i = 0; hids[i]; i++)
2294 if (hids[i]->gui)
2296 DSAddString (&info, TAB);
2297 DSAddString (&info, hids[i]->name);
2298 DSAddString (&info, " : ");
2299 DSAddString (&info, hids[i]->description);
2300 DSAddString (&info, "\n");
2304 DSAddString (&info, _("Exporters:\n"));
2305 for (i = 0; hids[i]; i++)
2307 if (hids[i]->exporter)
2309 DSAddString (&info, TAB);
2310 DSAddString (&info, hids[i]->name);
2311 DSAddString (&info, " : ");
2312 DSAddString (&info, hids[i]->description);
2313 DSAddString (&info, "\n");
2317 DSAddString (&info, _("Printers:\n"));
2318 for (i = 0; hids[i]; i++)
2320 if (hids[i]->printer)
2322 DSAddString (&info, TAB);
2323 DSAddString (&info, hids[i]->name);
2324 DSAddString (&info, " : ");
2325 DSAddString (&info, hids[i]->description);
2326 DSAddString (&info, "\n");
2330 #undef TAB
2332 return info.Data;
2335 /* ---------------------------------------------------------------------------
2336 * mkdir() implentation, mostly for plugins, which don't have our config.h.
2339 #ifdef MKDIR_IS_PCBMKDIR
2340 #error "Don't know how to create a directory on this system."
2341 #endif
2344 pcb_mkdir (const char *path, int mode)
2346 return MKDIR (path, mode);
2349 /* ---------------------------------------------------------------------------
2350 * Returns a best guess about the orientation of an element. The
2351 * value corresponds to the rotation; a difference is the right value
2352 * to pass to RotateElementLowLevel. However, the actual value is no
2353 * indication of absolute rotation; only relative rotation is
2354 * meaningful.
2357 int
2358 ElementOrientation (ElementType *e)
2360 Coord pin1x, pin1y, pin2x, pin2y, dx, dy;
2361 bool found_pin1 = 0;
2362 bool found_pin2 = 0;
2364 /* in case we don't find pin 1 or 2, make sure we have initialized these variables */
2365 pin1x = 0;
2366 pin1y = 0;
2367 pin2x = 0;
2368 pin2y = 0;
2370 PIN_LOOP (e);
2372 if (NSTRCMP (pin->Number, "1") == 0)
2374 pin1x = pin->X;
2375 pin1y = pin->Y;
2376 found_pin1 = 1;
2378 else if (NSTRCMP (pin->Number, "2") == 0)
2380 pin2x = pin->X;
2381 pin2y = pin->Y;
2382 found_pin2 = 1;
2385 END_LOOP;
2387 PAD_LOOP (e);
2389 if (NSTRCMP (pad->Number, "1") == 0)
2391 pin1x = (pad->Point1.X + pad->Point2.X) / 2;
2392 pin1y = (pad->Point1.Y + pad->Point2.Y) / 2;
2393 found_pin1 = 1;
2395 else if (NSTRCMP (pad->Number, "2") == 0)
2397 pin2x = (pad->Point1.X + pad->Point2.X) / 2;
2398 pin2y = (pad->Point1.Y + pad->Point2.Y) / 2;
2399 found_pin2 = 1;
2402 END_LOOP;
2404 if (found_pin1 && found_pin2)
2406 dx = pin2x - pin1x;
2407 dy = pin2y - pin1y;
2409 else if (found_pin1 && (pin1x || pin1y))
2411 dx = pin1x;
2412 dy = pin1y;
2414 else if (found_pin2 && (pin2x || pin2y))
2416 dx = pin2x;
2417 dy = pin2y;
2419 else
2420 return 0;
2422 if (abs(dx) > abs(dy))
2423 return dx > 0 ? 0 : 2;
2424 return dy > 0 ? 3 : 1;
2428 ActionListRotations(int argc, char **argv, Coord x, Coord y)
2430 ELEMENT_LOOP (PCB->Data);
2432 printf("%d %s\n", ElementOrientation(element), NAMEONPCB_NAME(element));
2434 END_LOOP;
2436 return 0;
2439 HID_Action misc_action_list[] = {
2440 {"ListRotations", 0, ActionListRotations,
2441 0,0},
2444 REGISTER_ACTIONS (misc_action_list)