Fix bug with expanding text.
[geda-pcb/pcjc2.git] / src / misc.c
blob8c84ab0e53823f118568d5ec6f956f0cf8a0663c
1 /*!
2 * \file src/misc.c
4 * \brief Misc functions used by several modules.
6 * <hr>
8 * <h1><b>Copyright.</b></h1>\n
10 * PCB, interactive printed circuit board design
12 * Copyright (C) 1994,1995,1996,2004,2006 Thomas Nau
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 * Contact addresses for paper mail and Email:
30 * Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
32 * Thomas.Nau@rz.uni-ulm.de
35 #ifdef HAVE_CONFIG_H
36 #include "config.h"
37 #endif
39 #include <stdlib.h>
40 #include <stdarg.h>
41 #ifdef HAVE_STRING_H
42 #include <string.h>
43 #endif
44 #include <memory.h>
45 #include <ctype.h>
46 #include <signal.h>
47 #include <sys/param.h>
48 #include <sys/stat.h>
49 #include <sys/types.h>
50 #include <math.h>
51 #ifdef HAVE_UNISTD_H
52 #include <unistd.h>
53 #endif
54 #ifdef HAVE_PWD_H
55 #include <pwd.h>
56 #endif
58 #include "global.h"
60 #include "box.h"
61 #include "crosshair.h"
62 #include "create.h"
63 #include "data.h"
64 #include "draw.h"
65 #include "file.h"
66 #include "error.h"
67 #include "mymem.h"
68 #include "misc.h"
69 #include "move.h"
70 #include "pcb-printf.h"
71 #include "polygon.h"
72 #include "remove.h"
73 #include "rtree.h"
74 #include "rotate.h"
75 #include "rubberband.h"
76 #include "search.h"
77 #include "set.h"
78 #include "undo.h"
79 #include "action.h"
81 #ifdef HAVE_LIBDMALLOC
82 #include <dmalloc.h>
83 #endif
85 /* forward declarations */
86 static char *BumpName (char *);
87 static void GetGridLockCoordinates (int, void *, void *, void *,
88 Coord *, Coord *);
91 /* Local variables */
93 /*!
94 * \brief Used by SaveStackAndVisibility() and
95 * RestoreStackAndVisibility().
97 static struct
99 bool ElementOn, InvisibleObjectsOn, PinOn, ViaOn, RatOn;
100 int LayerStack[MAX_ALL_LAYER];
101 bool LayerOn[MAX_ALL_LAYER];
102 int cnt;
103 } SavedStack;
106 * \brief Distance() should be used so that there is only one place to
107 * deal with overflow/precision errors.
109 double
110 Distance (double x1, double y1, double x2, double y2)
112 return hypot(x2 - x1, y2 - y1);
116 * \brief Bring an angle into [0, 360] range.
118 Angle
119 NormalizeAngle (Angle a)
121 while (a < 0)
122 a += 360.0;
123 while (a >= 360.0)
124 a -= 360.0;
125 return a;
129 * \brief GetValue() returns a numeric value passed from the string and
130 * sets the bool variable absolute to false if it leads with a +/-
131 * character.
133 double
134 GetValue (const char *val, const char *units, bool * absolute)
136 return GetValueEx(val, units, absolute, NULL, "cmil");
139 double
140 GetValueEx (const char *val, const char *units, bool * absolute, UnitList extra_units, const char *default_unit)
142 double value;
143 int n = -1;
144 bool scaled = 0;
145 bool dummy;
147 /* Allow NULL to be passed for absolute */
148 if(absolute == NULL)
149 absolute = &dummy;
151 /* if the first character is a sign we have to add the
152 * value to the current one
154 if (*val == '=')
156 *absolute = true;
157 if (sscanf (val+1, "%lf%n", &value, &n) < 1)
158 return 0;
159 n++;
161 else
163 if (isdigit ((int) *val))
164 *absolute = true;
165 else
166 *absolute = false;
167 if (sscanf (val, "%lf%n", &value, &n) < 1)
168 return 0;
170 if (!units && n > 0)
171 units = val + n;
173 while (units && *units == ' ')
174 units ++;
176 if (units && *units)
178 int i;
179 const Unit *unit = get_unit_struct (units);
180 if (unit != NULL)
182 value = unit_to_coord (unit, value);
183 scaled = 1;
185 if (extra_units)
187 for (i = 0; *extra_units[i].suffix; ++i)
189 if (strncmp (units, extra_units[i].suffix, strlen(extra_units[i].suffix)) == 0)
191 value *= extra_units[i].scale;
192 if (extra_units[i].flags & UNIT_PERCENT)
193 value /= 100.0;
194 scaled = 1;
199 /* Apply default unit */
200 if (!scaled && default_unit && *default_unit)
202 int i;
203 const Unit *unit = get_unit_struct (default_unit);
204 if (extra_units)
205 for (i = 0; *extra_units[i].suffix; ++i)
206 if (strcmp (extra_units[i].suffix, default_unit) == 0)
208 value *= extra_units[i].scale;
209 if (extra_units[i].flags & UNIT_PERCENT)
210 value /= 100.0;
211 scaled = 1;
213 if (!scaled && unit != NULL)
214 value = unit_to_coord (unit, value);
217 return value;
221 * \brief Extract a unit-less value from a string.
223 * \param val String containing the value to be read.
225 * \param absolute Returns wether the returned value is an absolute one.
227 * \return The value read, with sign.
229 * This is the same as GetValue() and GetValueEx(), but totally ignoring units.
230 * Typical application is a list selector, like the type of thermal to apply
231 * to a pin.
233 double GetUnitlessValue (const char *val, bool *absolute) {
234 double value;
236 if (*val == '=')
238 *absolute = true;
239 val++;
241 else
243 if (isdigit ((int) *val))
244 *absolute = true;
245 else
246 *absolute = false;
249 if (sscanf (val, "%lf", &value) < 1)
250 return 0.;
252 return value;
256 * \brief Sets the bounding box of a point (which is silly).
258 void
259 SetPointBoundingBox (PointType *Pnt)
261 Pnt->X2 = Pnt->X + 1;
262 Pnt->Y2 = Pnt->Y + 1;
266 * \brief Sets the bounding box of a pin or via.
268 void
269 SetPinBoundingBox (PinType *Pin)
271 Coord width;
273 /* the bounding box covers the extent of influence
274 * so it must include the clearance values too
276 width = MAX (Pin->Clearance + PIN_SIZE (Pin), Pin->Mask) / 2;
278 /* Adjust for our discrete polygon approximation */
279 width = (double)width * POLY_CIRC_RADIUS_ADJ + 0.5;
281 Pin->BoundingBox.X1 = Pin->X - width;
282 Pin->BoundingBox.Y1 = Pin->Y - width;
283 Pin->BoundingBox.X2 = Pin->X + width;
284 Pin->BoundingBox.Y2 = Pin->Y + width;
285 close_box(&Pin->BoundingBox);
289 * \brief Sets the bounding box of a pad.
291 void
292 SetPadBoundingBox (PadType *Pad)
294 Coord width;
295 Coord deltax;
296 Coord deltay;
298 /* the bounding box covers the extent of influence
299 * so it must include the clearance values too
301 width = (Pad->Thickness + Pad->Clearance + 1) / 2;
302 width = MAX (width, (Pad->Mask + 1) / 2);
303 deltax = Pad->Point2.X - Pad->Point1.X;
304 deltay = Pad->Point2.Y - Pad->Point1.Y;
306 if (TEST_FLAG (SQUAREFLAG, Pad) && deltax != 0 && deltay != 0)
308 /* slanted square pad */
309 double theta;
310 Coord btx, bty;
312 /* T is a vector half a thickness long, in the direction of
313 one of the corners. */
314 theta = atan2 (deltay, deltax);
315 btx = width * cos (theta + M_PI/4) * sqrt(2.0);
316 bty = width * sin (theta + M_PI/4) * sqrt(2.0);
319 Pad->BoundingBox.X1 = MIN (MIN (Pad->Point1.X - btx, Pad->Point1.X - bty),
320 MIN (Pad->Point2.X + btx, Pad->Point2.X + bty));
321 Pad->BoundingBox.X2 = MAX (MAX (Pad->Point1.X - btx, Pad->Point1.X - bty),
322 MAX (Pad->Point2.X + btx, Pad->Point2.X + bty));
323 Pad->BoundingBox.Y1 = MIN (MIN (Pad->Point1.Y + btx, Pad->Point1.Y - bty),
324 MIN (Pad->Point2.Y - btx, Pad->Point2.Y + bty));
325 Pad->BoundingBox.Y2 = MAX (MAX (Pad->Point1.Y + btx, Pad->Point1.Y - bty),
326 MAX (Pad->Point2.Y - btx, Pad->Point2.Y + bty));
328 else
330 /* Adjust for our discrete polygon approximation */
331 width = (double)width * POLY_CIRC_RADIUS_ADJ + 0.5;
333 Pad->BoundingBox.X1 = MIN (Pad->Point1.X, Pad->Point2.X) - width;
334 Pad->BoundingBox.X2 = MAX (Pad->Point1.X, Pad->Point2.X) + width;
335 Pad->BoundingBox.Y1 = MIN (Pad->Point1.Y, Pad->Point2.Y) - width;
336 Pad->BoundingBox.Y2 = MAX (Pad->Point1.Y, Pad->Point2.Y) + width;
338 close_box(&Pad->BoundingBox);
342 * \brief Sets the bounding box of a line.
344 void
345 SetLineBoundingBox (LineType *Line)
347 Coord width = (Line->Thickness + Line->Clearance + 1) / 2;
349 /* Adjust for our discrete polygon approximation */
350 width = (double)width * POLY_CIRC_RADIUS_ADJ + 0.5;
352 Line->BoundingBox.X1 = MIN (Line->Point1.X, Line->Point2.X) - width;
353 Line->BoundingBox.X2 = MAX (Line->Point1.X, Line->Point2.X) + width;
354 Line->BoundingBox.Y1 = MIN (Line->Point1.Y, Line->Point2.Y) - width;
355 Line->BoundingBox.Y2 = MAX (Line->Point1.Y, Line->Point2.Y) + width;
356 close_box(&Line->BoundingBox);
357 SetPointBoundingBox (&Line->Point1);
358 SetPointBoundingBox (&Line->Point2);
362 * \brief Sets the bounding box of a polygons.
364 void
365 SetPolygonBoundingBox (PolygonType *Polygon)
367 Polygon->BoundingBox.X1 = Polygon->BoundingBox.Y1 = MAX_COORD;
368 Polygon->BoundingBox.X2 = Polygon->BoundingBox.Y2 = 0;
369 POLYGONPOINT_LOOP (Polygon);
371 MAKEMIN (Polygon->BoundingBox.X1, point->X);
372 MAKEMIN (Polygon->BoundingBox.Y1, point->Y);
373 MAKEMAX (Polygon->BoundingBox.X2, point->X);
374 MAKEMAX (Polygon->BoundingBox.Y2, point->Y);
376 /* boxes don't include the lower right corner */
377 close_box(&Polygon->BoundingBox);
378 END_LOOP;
382 * \brief Sets the bounding box of an element.
384 void
385 SetElementBoundingBox (DataType *Data, ElementType *Element,
386 FontType *Font)
388 BoxType *box, *vbox;
390 if (Data && Data->element_tree)
391 r_delete_entry (Data->element_tree, (BoxType *) Element);
392 /* first update the text objects */
393 ELEMENTTEXT_LOOP (Element);
395 if (Data && Data->name_tree[n])
396 r_delete_entry (Data->name_tree[n], (BoxType *) text);
397 SetTextBoundingBox (Font, text);
398 if (Data && !Data->name_tree[n])
399 Data->name_tree[n] = r_create_tree (NULL, 0, 0);
400 if (Data)
401 r_insert_entry (Data->name_tree[n], (BoxType *) text, 0);
403 END_LOOP;
405 /* do not include the elementnames bounding box which
406 * is handled separately
408 box = &Element->BoundingBox;
409 vbox = &Element->VBox;
410 box->X1 = box->Y1 = MAX_COORD;
411 box->X2 = box->Y2 = 0;
412 ELEMENTLINE_LOOP (Element);
414 SetLineBoundingBox (line);
415 MAKEMIN (box->X1, line->Point1.X - (line->Thickness + 1) / 2);
416 MAKEMIN (box->Y1, line->Point1.Y - (line->Thickness + 1) / 2);
417 MAKEMIN (box->X1, line->Point2.X - (line->Thickness + 1) / 2);
418 MAKEMIN (box->Y1, line->Point2.Y - (line->Thickness + 1) / 2);
419 MAKEMAX (box->X2, line->Point1.X + (line->Thickness + 1) / 2);
420 MAKEMAX (box->Y2, line->Point1.Y + (line->Thickness + 1) / 2);
421 MAKEMAX (box->X2, line->Point2.X + (line->Thickness + 1) / 2);
422 MAKEMAX (box->Y2, line->Point2.Y + (line->Thickness + 1) / 2);
424 END_LOOP;
425 ARC_LOOP (Element);
427 SetArcBoundingBox (arc);
428 MAKEMIN (box->X1, arc->BoundingBox.X1);
429 MAKEMIN (box->Y1, arc->BoundingBox.Y1);
430 MAKEMAX (box->X2, arc->BoundingBox.X2);
431 MAKEMAX (box->Y2, arc->BoundingBox.Y2);
433 END_LOOP;
434 *vbox = *box;
435 PIN_LOOP (Element);
437 if (Data && Data->pin_tree)
438 r_delete_entry (Data->pin_tree, (BoxType *) pin);
439 SetPinBoundingBox (pin);
440 if (Data)
442 if (!Data->pin_tree)
443 Data->pin_tree = r_create_tree (NULL, 0, 0);
444 r_insert_entry (Data->pin_tree, (BoxType *) pin, 0);
446 MAKEMIN (box->X1, pin->BoundingBox.X1);
447 MAKEMIN (box->Y1, pin->BoundingBox.Y1);
448 MAKEMAX (box->X2, pin->BoundingBox.X2);
449 MAKEMAX (box->Y2, pin->BoundingBox.Y2);
450 MAKEMIN (vbox->X1, pin->X - pin->Thickness / 2);
451 MAKEMIN (vbox->Y1, pin->Y - pin->Thickness / 2);
452 MAKEMAX (vbox->X2, pin->X + pin->Thickness / 2);
453 MAKEMAX (vbox->Y2, pin->Y + pin->Thickness / 2);
455 END_LOOP;
456 PAD_LOOP (Element);
458 if (Data && Data->pad_tree)
459 r_delete_entry (Data->pad_tree, (BoxType *) pad);
460 SetPadBoundingBox (pad);
461 if (Data)
463 if (!Data->pad_tree)
464 Data->pad_tree = r_create_tree (NULL, 0, 0);
465 r_insert_entry (Data->pad_tree, (BoxType *) pad, 0);
467 MAKEMIN (box->X1, pad->BoundingBox.X1);
468 MAKEMIN (box->Y1, pad->BoundingBox.Y1);
469 MAKEMAX (box->X2, pad->BoundingBox.X2);
470 MAKEMAX (box->Y2, pad->BoundingBox.Y2);
471 MAKEMIN (vbox->X1,
472 MIN (pad->Point1.X, pad->Point2.X) - pad->Thickness / 2);
473 MAKEMIN (vbox->Y1,
474 MIN (pad->Point1.Y, pad->Point2.Y) - pad->Thickness / 2);
475 MAKEMAX (vbox->X2,
476 MAX (pad->Point1.X, pad->Point2.X) + pad->Thickness / 2);
477 MAKEMAX (vbox->Y2,
478 MAX (pad->Point1.Y, pad->Point2.Y) + pad->Thickness / 2);
480 END_LOOP;
481 /* now we set the EDGE2FLAG of the pad if Point2
482 * is closer to the outside edge than Point1
484 PAD_LOOP (Element);
486 if (pad->Point1.Y == pad->Point2.Y)
488 /* horizontal pad */
489 if (box->X2 - pad->Point2.X < pad->Point1.X - box->X1)
490 SET_FLAG (EDGE2FLAG, pad);
491 else
492 CLEAR_FLAG (EDGE2FLAG, pad);
494 else
496 /* vertical pad */
497 if (box->Y2 - pad->Point2.Y < pad->Point1.Y - box->Y1)
498 SET_FLAG (EDGE2FLAG, pad);
499 else
500 CLEAR_FLAG (EDGE2FLAG, pad);
503 END_LOOP;
505 /* mark pins with component orientation */
506 if ((box->X2 - box->X1) > (box->Y2 - box->Y1))
508 PIN_LOOP (Element);
510 SET_FLAG (EDGE2FLAG, pin);
512 END_LOOP;
514 else
516 PIN_LOOP (Element);
518 CLEAR_FLAG (EDGE2FLAG, pin);
520 END_LOOP;
522 close_box(box);
523 close_box(vbox);
524 if (Data && !Data->element_tree)
525 Data->element_tree = r_create_tree (NULL, 0, 0);
526 if (Data)
527 r_insert_entry (Data->element_tree, box, 0);
531 * \brief Creates the bounding box of a text object.
533 void
534 SetTextBoundingBox (FontType *FontPtr, TextType *Text)
536 SymbolType *symbol = FontPtr->Symbol;
537 unsigned char *s = (unsigned char *) Text->TextString;
538 int i;
539 int space;
541 Coord minx, miny, maxx, maxy, tx;
542 Coord min_final_radius;
543 Coord min_unscaled_radius;
544 bool first_time = true;
546 minx = miny = maxx = maxy = tx = 0;
548 /* Calculate the bounding box based on the larger of the thicknesses
549 * the text might clamped at on silk or copper layers.
551 min_final_radius = MAX (PCB->minWid, PCB->minSlk) / 2;
553 /* Pre-adjust the line radius for the fact we are initially computing the
554 * bounds of the un-scaled text, and the thickness clamping applies to
555 * scaled text.
557 min_unscaled_radius = UNSCALE_TEXT (min_final_radius, Text->Scale);
559 /* calculate size of the bounding box */
560 for (; s && *s; s++)
562 if (*s <= MAX_FONTPOSITION && symbol[*s].Valid)
564 LineType *line = symbol[*s].Line;
565 for (i = 0; i < symbol[*s].LineN; line++, i++)
567 /* Clamp the width of text lines at the minimum thickness.
568 * NB: Divide 4 in thickness calculation is comprised of a factor
569 * of 1/2 to get a radius from the center-line, and a factor
570 * of 1/2 because some stupid reason we render our glyphs
571 * at half their defined stroke-width.
573 Coord unscaled_radius = MAX (min_unscaled_radius, line->Thickness / 4);
575 if (first_time)
577 minx = maxx = line->Point1.X;
578 miny = maxy = line->Point1.Y;
579 first_time = false;
582 minx = MIN (minx, line->Point1.X - unscaled_radius + tx);
583 miny = MIN (miny, line->Point1.Y - unscaled_radius);
584 minx = MIN (minx, line->Point2.X - unscaled_radius + tx);
585 miny = MIN (miny, line->Point2.Y - unscaled_radius);
586 maxx = MAX (maxx, line->Point1.X + unscaled_radius + tx);
587 maxy = MAX (maxy, line->Point1.Y + unscaled_radius);
588 maxx = MAX (maxx, line->Point2.X + unscaled_radius + tx);
589 maxy = MAX (maxy, line->Point2.Y + unscaled_radius);
591 space = symbol[*s].Delta;
593 else
595 BoxType *ds = &FontPtr->DefaultSymbol;
596 Coord w = ds->X2 - ds->X1;
598 minx = MIN (minx, ds->X1 + tx);
599 miny = MIN (miny, ds->Y1);
600 minx = MIN (minx, ds->X2 + tx);
601 miny = MIN (miny, ds->Y2);
602 maxx = MAX (maxx, ds->X1 + tx);
603 maxy = MAX (maxy, ds->Y1);
604 maxx = MAX (maxx, ds->X2 + tx);
605 maxy = MAX (maxy, ds->Y2);
607 space = w / 5;
609 tx += symbol[*s].Width + space;
612 /* scale values */
613 minx = SCALE_TEXT (minx, Text->Scale);
614 miny = SCALE_TEXT (miny, Text->Scale);
615 maxx = SCALE_TEXT (maxx, Text->Scale);
616 maxy = SCALE_TEXT (maxy, Text->Scale);
618 /* set upper-left and lower-right corner;
619 * swap coordinates if necessary (origin is already in 'swapped')
620 * and rotate box
623 if (TEST_FLAG (ONSOLDERFLAG, Text))
625 Text->BoundingBox.X1 = Text->X + minx;
626 Text->BoundingBox.Y1 = Text->Y - miny;
627 Text->BoundingBox.X2 = Text->X + maxx;
628 Text->BoundingBox.Y2 = Text->Y - maxy;
629 RotateBoxLowLevel (&Text->BoundingBox, Text->X, Text->Y,
630 (4 - Text->Direction) & 0x03);
632 else
634 Text->BoundingBox.X1 = Text->X + minx;
635 Text->BoundingBox.Y1 = Text->Y + miny;
636 Text->BoundingBox.X2 = Text->X + maxx;
637 Text->BoundingBox.Y2 = Text->Y + maxy;
638 RotateBoxLowLevel (&Text->BoundingBox,
639 Text->X, Text->Y, Text->Direction);
642 /* the bounding box covers the extent of influence
643 * so it must include the clearance values too
645 Text->BoundingBox.X1 -= PCB->Bloat;
646 Text->BoundingBox.Y1 -= PCB->Bloat;
647 Text->BoundingBox.X2 += PCB->Bloat;
648 Text->BoundingBox.Y2 += PCB->Bloat;
649 close_box(&Text->BoundingBox);
653 * \brief Returns true if data area is empty.
655 bool
656 IsDataEmpty (DataType *Data)
658 bool hasNoObjects;
659 Cardinal i;
661 hasNoObjects = (Data->ViaN == 0);
662 hasNoObjects &= (Data->ElementN == 0);
663 for (i = 0; i < max_copper_layer + SILK_LAYER; i++)
664 hasNoObjects = hasNoObjects &&
665 Data->Layer[i].LineN == 0 &&
666 Data->Layer[i].ArcN == 0 &&
667 Data->Layer[i].TextN == 0 && Data->Layer[i].PolygonN == 0;
668 return (hasNoObjects);
672 FlagIsDataEmpty (int parm)
674 int i = IsDataEmpty (PCB->Data);
675 return parm ? !i : i;
678 /* FLAG(DataEmpty,FlagIsDataEmpty,0) */
679 /* FLAG(DataNonEmpty,FlagIsDataEmpty,1) */
681 bool
682 IsLayerEmpty (LayerType *layer)
684 return (layer->LineN == 0
685 && layer->TextN == 0
686 && layer->PolygonN == 0
687 && layer->ArcN == 0);
690 bool
691 IsLayerNumEmpty (int num)
693 return IsLayerEmpty (PCB->Data->Layer+num);
696 bool
697 IsLayerGroupEmpty (int num)
699 int i;
700 for (i=0; i<PCB->LayerGroups.Number[num]; i++)
701 if (!IsLayerNumEmpty (PCB->LayerGroups.Entries[num][i]))
702 return false;
703 return true;
706 bool
707 IsPasteEmpty (int side)
709 bool paste_empty = true;
710 ALLPAD_LOOP (PCB->Data);
712 if (ON_SIDE (pad, side) && !TEST_FLAG (NOPASTEFLAG, pad) && pad->Mask > 0)
714 paste_empty = false;
715 break;
718 ENDALL_LOOP;
719 return paste_empty;
723 typedef struct
725 int nplated;
726 int nunplated;
727 } HoleCountStruct;
729 static int
730 hole_counting_callback (const BoxType * b, void *cl)
732 PinType *pin = (PinType *) b;
733 HoleCountStruct *hcs = (HoleCountStruct *) cl;
734 if (TEST_FLAG (HOLEFLAG, pin))
735 hcs->nunplated++;
736 else
737 hcs->nplated++;
738 return 1;
742 * \brief Counts the number of plated and unplated holes in the design
743 * within a given area of the board.
745 * To count for the whole board, pass NULL to the within_area.
747 void
748 CountHoles (int *plated, int *unplated, const BoxType *within_area)
750 HoleCountStruct hcs = {0, 0};
752 r_search (PCB->Data->pin_tree, within_area, NULL, hole_counting_callback, &hcs);
753 r_search (PCB->Data->via_tree, within_area, NULL, hole_counting_callback, &hcs);
755 if (plated != NULL) *plated = hcs.nplated;
756 if (unplated != NULL) *unplated = hcs.nunplated;
761 * \brief Gets minimum and maximum coordinates.
763 * \return NULL if layout is empty.
765 BoxType *
766 GetDataBoundingBox (DataType *Data)
768 static BoxType box;
769 /* FIX ME: use r_search to do this much faster */
771 /* preset identifiers with highest and lowest possible values */
772 box.X1 = box.Y1 = MAX_COORD;
773 box.X2 = box.Y2 = -MAX_COORD;
775 /* now scan for the lowest/highest X and Y coordinate */
776 VIA_LOOP (Data);
778 box.X1 = MIN (box.X1, via->X - via->Thickness / 2);
779 box.Y1 = MIN (box.Y1, via->Y - via->Thickness / 2);
780 box.X2 = MAX (box.X2, via->X + via->Thickness / 2);
781 box.Y2 = MAX (box.Y2, via->Y + via->Thickness / 2);
783 END_LOOP;
784 ELEMENT_LOOP (Data);
786 box.X1 = MIN (box.X1, element->BoundingBox.X1);
787 box.Y1 = MIN (box.Y1, element->BoundingBox.Y1);
788 box.X2 = MAX (box.X2, element->BoundingBox.X2);
789 box.Y2 = MAX (box.Y2, element->BoundingBox.Y2);
791 TextType *text = &NAMEONPCB_TEXT (element);
792 box.X1 = MIN (box.X1, text->BoundingBox.X1);
793 box.Y1 = MIN (box.Y1, text->BoundingBox.Y1);
794 box.X2 = MAX (box.X2, text->BoundingBox.X2);
795 box.Y2 = MAX (box.Y2, text->BoundingBox.Y2);
798 END_LOOP;
799 ALLLINE_LOOP (Data);
801 box.X1 = MIN (box.X1, line->Point1.X - line->Thickness / 2);
802 box.Y1 = MIN (box.Y1, line->Point1.Y - line->Thickness / 2);
803 box.X1 = MIN (box.X1, line->Point2.X - line->Thickness / 2);
804 box.Y1 = MIN (box.Y1, line->Point2.Y - line->Thickness / 2);
805 box.X2 = MAX (box.X2, line->Point1.X + line->Thickness / 2);
806 box.Y2 = MAX (box.Y2, line->Point1.Y + line->Thickness / 2);
807 box.X2 = MAX (box.X2, line->Point2.X + line->Thickness / 2);
808 box.Y2 = MAX (box.Y2, line->Point2.Y + line->Thickness / 2);
810 ENDALL_LOOP;
811 ALLARC_LOOP (Data);
813 box.X1 = MIN (box.X1, arc->BoundingBox.X1);
814 box.Y1 = MIN (box.Y1, arc->BoundingBox.Y1);
815 box.X2 = MAX (box.X2, arc->BoundingBox.X2);
816 box.Y2 = MAX (box.Y2, arc->BoundingBox.Y2);
818 ENDALL_LOOP;
819 ALLTEXT_LOOP (Data);
821 box.X1 = MIN (box.X1, text->BoundingBox.X1);
822 box.Y1 = MIN (box.Y1, text->BoundingBox.Y1);
823 box.X2 = MAX (box.X2, text->BoundingBox.X2);
824 box.Y2 = MAX (box.Y2, text->BoundingBox.Y2);
826 ENDALL_LOOP;
827 ALLPOLYGON_LOOP (Data);
829 box.X1 = MIN (box.X1, polygon->BoundingBox.X1);
830 box.Y1 = MIN (box.Y1, polygon->BoundingBox.Y1);
831 box.X2 = MAX (box.X2, polygon->BoundingBox.X2);
832 box.Y2 = MAX (box.Y2, polygon->BoundingBox.Y2);
834 ENDALL_LOOP;
835 return (IsDataEmpty (Data) ? NULL : &box);
839 * \brief Centers the displayed PCB around the specified point (X,Y),
840 * and move the crosshair there.
842 * If warp_pointer is true warp the pointer to the crosshair.
844 void
845 CenterDisplay (Coord X, Coord Y, bool warp_pointer)
847 Coord save_grid = PCB->Grid;
849 PCB->Grid = 1;
851 if (MoveCrosshairAbsolute (X, Y))
852 notify_crosshair_change (true);
854 if (warp_pointer)
855 gui->set_crosshair (Crosshair.X, Crosshair.Y,
856 HID_SC_CENTER_IN_VIEWPORT_AND_WARP_POINTER );
857 else
858 gui->set_crosshair (Crosshair.X, Crosshair.Y, HID_SC_CENTER_IN_VIEWPORT);
860 PCB->Grid = save_grid;
864 * \brief Transforms symbol coordinates so that the left edge of each
865 * symbol is at the zero position.
867 * The y coordinates are moved so that min(y) = 0.
870 void
871 SetFontInfo (FontType *Ptr)
873 Cardinal i, j;
874 SymbolType *symbol;
875 LineType *line;
876 Coord totalminy = MAX_COORD;
878 /* calculate cell with and height (is at least DEFAULT_CELLSIZE)
879 * maximum cell width and height
880 * minimum x and y position of all lines
882 Ptr->MaxWidth = DEFAULT_CELLSIZE;
883 Ptr->MaxHeight = DEFAULT_CELLSIZE;
884 for (i = 0, symbol = Ptr->Symbol; i <= MAX_FONTPOSITION; i++, symbol++)
886 Coord minx, miny, maxx, maxy;
888 /* next one if the index isn't used or symbol is empty (SPACE) */
889 if (!symbol->Valid || !symbol->LineN)
890 continue;
892 minx = miny = MAX_COORD;
893 maxx = maxy = 0;
894 for (line = symbol->Line, j = symbol->LineN; j; j--, line++)
896 minx = MIN (minx, line->Point1.X);
897 miny = MIN (miny, line->Point1.Y);
898 minx = MIN (minx, line->Point2.X);
899 miny = MIN (miny, line->Point2.Y);
900 maxx = MAX (maxx, line->Point1.X);
901 maxy = MAX (maxy, line->Point1.Y);
902 maxx = MAX (maxx, line->Point2.X);
903 maxy = MAX (maxy, line->Point2.Y);
906 /* move symbol to left edge */
907 for (line = symbol->Line, j = symbol->LineN; j; j--, line++)
908 MOVE_LINE_LOWLEVEL (line, -minx, 0);
910 /* set symbol bounding box with a minimum cell size of (1,1) */
911 symbol->Width = maxx - minx + 1;
912 symbol->Height = maxy + 1;
914 /* check total min/max */
915 Ptr->MaxWidth = MAX (Ptr->MaxWidth, symbol->Width);
916 Ptr->MaxHeight = MAX (Ptr->MaxHeight, symbol->Height);
917 totalminy = MIN (totalminy, miny);
920 /* move coordinate system to the upper edge (lowest y on screen) */
921 for (i = 0, symbol = Ptr->Symbol; i <= MAX_FONTPOSITION; i++, symbol++)
922 if (symbol->Valid)
924 symbol->Height -= totalminy;
925 for (line = symbol->Line, j = symbol->LineN; j; j--, line++)
926 MOVE_LINE_LOWLEVEL (line, 0, -totalminy);
929 /* setup the box for the default symbol */
930 Ptr->DefaultSymbol.X1 = Ptr->DefaultSymbol.Y1 = 0;
931 Ptr->DefaultSymbol.X2 = Ptr->DefaultSymbol.X1 + Ptr->MaxWidth;
932 Ptr->DefaultSymbol.Y2 = Ptr->DefaultSymbol.Y1 + Ptr->MaxHeight;
935 static Coord
936 GetNum (char **s, const char *default_unit)
938 /* Read value */
939 Coord ret_val = GetValueEx (*s, NULL, NULL, NULL, default_unit);
940 /* Advance pointer */
941 while(isalnum(**s) || **s == '.')
942 (*s)++;
943 return ret_val;
947 * \brief Serializes the route style list .
949 * Right now n_styles should always be set to NUM_STYLES,
950 * since that is the number of route styles ParseRouteString()
951 * expects to parse.
953 char *
954 make_route_string (RouteStyleType rs[], int n_styles)
956 GString *str = g_string_new ("");
957 gint i;
959 for (i = 0; i < n_styles; ++i)
961 char *r_string = pcb_g_strdup_printf ("%s,%$mr,%$mr,%$mr,%$mr", rs[i].Name,
962 rs[i].Thick, rs[i].Diameter,
963 rs[i].Hole, rs[i].Keepaway);
964 if (i > 0)
965 g_string_append_c (str, ':');
966 g_string_append (str, r_string);
967 g_free (r_string);
969 return g_string_free (str, FALSE);
973 * \brief Parses the routes definition string
975 * Which is a colon separated list of comma separated Name, Dimension,
976 * Dimension, Dimension, Dimension.
978 * \example Signal,20,40,20,10:Power,40,60,28,10:...
981 ParseRouteString (char *s, RouteStyleType *routeStyle, const char *default_unit)
983 int i, style;
984 char Name[256];
986 memset (routeStyle, 0, NUM_STYLES * sizeof (RouteStyleType));
987 for (style = 0; style < NUM_STYLES; style++, routeStyle++)
989 while (*s && isspace ((int) *s))
990 s++;
991 for (i = 0; *s && *s != ','; i++)
992 Name[i] = *s++;
993 Name[i] = '\0';
994 routeStyle->Name = strdup (Name);
995 if (!isdigit ((int) *++s))
996 goto error;
997 routeStyle->Thick = GetNum (&s, default_unit);
998 while (*s && isspace ((int) *s))
999 s++;
1000 if (*s++ != ',')
1001 goto error;
1002 while (*s && isspace ((int) *s))
1003 s++;
1004 if (!isdigit ((int) *s))
1005 goto error;
1006 routeStyle->Diameter = GetNum (&s, default_unit);
1007 while (*s && isspace ((int) *s))
1008 s++;
1009 if (*s++ != ',')
1010 goto error;
1011 while (*s && isspace ((int) *s))
1012 s++;
1013 if (!isdigit ((int) *s))
1014 goto error;
1015 routeStyle->Hole = GetNum (&s, default_unit);
1016 /* for backwards-compatibility, we use a 10-mil default
1017 * for styles which omit the keepaway specification. */
1018 if (*s != ',')
1019 routeStyle->Keepaway = MIL_TO_COORD(10);
1020 else
1022 s++;
1023 while (*s && isspace ((int) *s))
1024 s++;
1025 if (!isdigit ((int) *s))
1026 goto error;
1027 routeStyle->Keepaway = GetNum (&s, default_unit);
1028 while (*s && isspace ((int) *s))
1029 s++;
1031 if (style < NUM_STYLES - 1)
1033 while (*s && isspace ((int) *s))
1034 s++;
1035 if (*s++ != ':')
1036 goto error;
1039 return (0);
1041 error:
1042 memset (routeStyle, 0, NUM_STYLES * sizeof (RouteStyleType));
1043 return (1);
1047 * \brief Parses the group definition string.
1049 * Which is a colon separated list of comma separated layer numbers.
1051 * \example (1,2,b:4,6,8,t)
1054 ParseGroupString (char *group_string, LayerGroupType *LayerGroup, int *LayerN)
1056 char *s;
1057 int group, member, layer;
1058 bool c_set = false, /* flags for the two special layers to */
1059 s_set = false; /* provide a default setting for old formats */
1060 int groupnum[MAX_ALL_LAYER];
1062 *LayerN = 0;
1064 /* Deterimine the maximum layer number */
1065 for (s = group_string; s && *s; s++)
1067 while (*s && isspace ((int) *s))
1068 s++;
1070 switch (*s)
1072 case 'c':
1073 case 'C':
1074 case 't':
1075 case 'T':
1076 case 's':
1077 case 'S':
1078 case 'b':
1079 case 'B':
1080 break;
1082 default:
1083 if (!isdigit ((int) *s))
1084 goto error;
1085 *LayerN = MAX (*LayerN, atoi (s));
1086 break;
1089 while (*++s && isdigit ((int) *s));
1091 /* ignore white spaces and check for separator */
1092 while (*s && isspace ((int) *s))
1093 s++;
1095 if (*s == '\0')
1096 break;
1098 if (*s != ':' && *s != ',')
1099 goto error;
1102 /* clear struct */
1103 memset (LayerGroup, 0, sizeof (LayerGroupType));
1105 /* Clear assignments */
1106 for (layer = 0; layer < MAX_ALL_LAYER; layer++)
1107 groupnum[layer] = -1;
1109 /* loop over all groups */
1110 for (s = group_string, group = 0;
1111 s && *s && group < *LayerN;
1112 group++)
1114 while (*s && isspace ((int) *s))
1115 s++;
1117 /* loop over all group members */
1118 for (member = 0; *s; s++)
1120 /* ignore white spaces and get layernumber */
1121 while (*s && isspace ((int) *s))
1122 s++;
1123 switch (*s)
1125 case 'c':
1126 case 'C':
1127 case 't':
1128 case 'T':
1129 layer = *LayerN + TOP_SILK_LAYER;
1130 c_set = true;
1131 break;
1133 case 's':
1134 case 'S':
1135 case 'b':
1136 case 'B':
1137 layer = *LayerN + BOTTOM_SILK_LAYER;
1138 s_set = true;
1139 break;
1141 default:
1142 layer = atoi (s) - 1;
1143 break;
1145 if (layer > *LayerN + MAX (BOTTOM_SILK_LAYER, TOP_SILK_LAYER) ||
1146 member >= *LayerN + 1)
1147 goto error;
1148 groupnum[layer] = group;
1149 LayerGroup->Entries[group][member++] = layer;
1150 while (*++s && isdigit ((int) *s));
1152 /* ignore white spaces and check for separator */
1153 while (*s && isspace ((int) *s))
1154 s++;
1155 if (!*s || *s == ':')
1156 break;
1158 LayerGroup->Number[group] = member;
1159 if (*s == ':')
1160 s++;
1163 /* If no explicit solder or component layer group was found in the layer
1164 * group string, make group 0 the bottom side, and group 1 the top side.
1165 * This is done by assigning the relevant silkscreen layers to those groups.
1167 if (!s_set)
1168 LayerGroup->Entries[0][LayerGroup->Number[0]++] = *LayerN + BOTTOM_SILK_LAYER;
1169 if (!c_set)
1170 LayerGroup->Entries[1][LayerGroup->Number[1]++] = *LayerN + TOP_SILK_LAYER;
1172 /* Assign a unique layer group to each layer that was not explicitly
1173 * assigned a particular group by its presence in the layer group string.
1175 for (layer = 0; layer < *LayerN && group < *LayerN; layer++)
1176 if (groupnum[layer] == -1)
1178 LayerGroup->Entries[group][0] = layer;
1179 LayerGroup->Number[group] = 1;
1180 group++;
1182 return (0);
1184 /* reset structure on error */
1185 error:
1186 memset (LayerGroup, 0, sizeof (LayerGroupType));
1187 return (1);
1191 * \brief Sets up any remaining layer type guesses.
1193 void
1194 AssignDefaultLayerTypes()
1196 int num_found;
1197 Cardinal outline_layer = -1;
1200 * There can be only one outline layer. During parsing guess_layertype()
1201 * applied well known cases already, but as this function operates on a
1202 * single layer only, it might end up with more than one hit for the whole
1203 * file. Especially after loading an older layout without saved flags.
1205 num_found = 0;
1206 LAYER_TYPE_LOOP (PCB->Data, max_copper_layer, LT_OUTLINE)
1207 outline_layer = n;
1208 num_found++;
1209 END_LOOP;
1211 if (num_found != 1)
1212 /* No or duplicate outline! Try to find a layer which is named exactly
1213 "outline". */
1214 LAYER_TYPE_LOOP (PCB->Data, max_copper_layer, LT_OUTLINE)
1215 if ( ! strcasecmp (layer->Name, "outline"))
1217 outline_layer = n;
1218 num_found = 1;
1219 break;
1221 END_LOOP;
1223 if (num_found != 1)
1224 /* Next, try to find a layer which is named exactly "route". */
1225 LAYER_TYPE_LOOP (PCB->Data, max_copper_layer, LT_OUTLINE)
1226 if ( ! strcasecmp (layer->Name, "route"))
1228 outline_layer = n;
1229 num_found = 1;
1230 break;
1232 END_LOOP;
1234 if (num_found != 1)
1235 /* As last resort, take the first layer claiming to be outline. */
1236 LAYER_TYPE_LOOP (PCB->Data, max_copper_layer, LT_OUTLINE)
1237 outline_layer = n;
1238 num_found = 1;
1239 break;
1240 END_LOOP;
1242 /* Make sure our found outline layer is the only one. */
1243 LAYER_TYPE_LOOP (PCB->Data, max_copper_layer, LT_OUTLINE)
1244 if (n == outline_layer)
1245 layer->Type = LT_OUTLINE;
1246 else
1247 layer->Type = LT_ROUTE; /* best guess */
1248 END_LOOP;
1251 extern void pcb_main_uninit(void);
1254 * \brief Quits application.
1256 void
1257 QuitApplication (void)
1260 * save data if necessary. It not needed, then don't trigger EmergencySave
1261 * via our atexit() registering of EmergencySave(). We presumeably wanted to
1262 * exit here and thus it is not an emergency.
1264 if (PCB->Changed && Settings.SaveInTMP)
1265 EmergencySave ();
1266 else
1267 DisableEmergencySave ();
1269 if (gui->do_exit == NULL)
1271 pcb_main_uninit ();
1272 exit (0);
1274 else
1275 gui->do_exit (gui);
1279 * \brief Creates a filename from a template.
1281 * "%f" is replaced by the filename.
1283 * "%p" is replaced by the searchpath.
1285 char *
1286 EvaluateFilename (char *Template, char *Path, char *Filename, char *Parameter)
1288 static DynamicStringType command;
1289 char *p;
1291 if (Settings.verbose)
1293 printf ("EvaluateFilename:\n");
1294 printf ("\tTemplate: \033[33m%s\033[0m\n", Template);
1295 printf ("\tPath: \033[33m%s\033[0m\n", Path);
1296 printf ("\tFilename: \033[33m%s\033[0m\n", Filename);
1297 printf ("\tParameter: \033[33m%s\033[0m\n", Parameter);
1300 DSClearString (&command);
1302 for (p = Template; p && *p; p++)
1304 /* copy character or add string to command */
1305 if (*p == '%'
1306 && (*(p + 1) == 'f' || *(p + 1) == 'p' || *(p + 1) == 'a'))
1307 switch (*(++p))
1309 case 'a':
1310 DSAddString (&command, Parameter);
1311 break;
1312 case 'f':
1313 DSAddString (&command, Filename);
1314 break;
1315 case 'p':
1316 DSAddString (&command, Path);
1317 break;
1319 else
1320 DSAddCharacter (&command, *p);
1322 DSAddCharacter (&command, '\0');
1323 if (Settings.verbose)
1324 printf ("EvaluateFilename: \033[32m%s\033[0m\n", command.Data);
1326 return strdup (command.Data);
1330 * \brief Concatenates directory and filename.
1332 * If directory != NULL expands them with a shell and returns the found
1333 * name(s) or NULL.
1335 char *
1336 ExpandFilename (char *Dirname, char *Filename)
1338 static DynamicStringType answer;
1339 char *command;
1340 FILE *pipe;
1341 int c;
1343 /* allocate memory for commandline and build it */
1344 DSClearString (&answer);
1345 if (Dirname)
1347 command = (char *)calloc (strlen (Filename) + strlen (Dirname) + 7,
1348 sizeof (char));
1349 sprintf (command, "echo %s/%s", Dirname, Filename);
1351 else
1353 command = (char *)calloc (strlen (Filename) + 6, sizeof (char));
1354 sprintf (command, "echo %s", Filename);
1357 /* execute it with shell */
1358 if ((pipe = popen (command, "r")) != NULL)
1360 /* discard all but the first returned line */
1361 for (;;)
1363 if ((c = fgetc (pipe)) == EOF || c == '\n' || c == '\r')
1364 break;
1365 else
1366 DSAddCharacter (&answer, c);
1369 free (command);
1370 return (pclose (pipe) ? NULL : answer.Data);
1373 /* couldn't be expanded by the shell */
1374 PopenErrorMessage (command);
1375 free (command);
1376 return (NULL);
1381 * \brief Returns the layer number for the passed pointer.
1384 GetLayerNumber (DataType *Data, LayerType *Layer)
1386 int i;
1388 for (i = 0; i < MAX_ALL_LAYER; i++)
1389 if (Layer == &Data->Layer[i])
1390 break;
1391 return (i);
1395 * \brief Move layer (number is passed in) to top of layerstack.
1397 static void
1398 PushOnTopOfLayerStack (int NewTop)
1400 int i;
1402 /* ignore silk and other extra layers */
1403 if (NewTop < max_copper_layer)
1405 /* first find position of passed one */
1406 for (i = 0; i < max_copper_layer; i++)
1407 if (LayerStack[i] == NewTop)
1408 break;
1410 /* bring this element to the top of the stack */
1411 for (; i; i--)
1412 LayerStack[i] = LayerStack[i - 1];
1413 LayerStack[0] = NewTop;
1419 * \brief Changes the visibility of all layers in a group.
1421 * \return The number of changed layers.
1424 ChangeGroupVisibility (int Layer, bool On, bool ChangeStackOrder)
1426 int group, i, changed = 1; /* at least the current layer changes */
1428 /* Warning: these special case values must agree with what gui-top-window.c
1429 | thinks the are.
1432 if (Settings.verbose)
1433 printf ("ChangeGroupVisibility(Layer=%d, On=%d, ChangeStackOrder=%d)\n",
1434 Layer, On, ChangeStackOrder);
1436 /* decrement 'i' to keep stack in order of layergroup */
1437 group = GetLayerGroupNumberByNumber (Layer);
1438 for (i = PCB->LayerGroups.Number[group]; i;)
1440 int layer = PCB->LayerGroups.Entries[group][--i];
1442 /* don't count the passed member of the group */
1443 if (layer != Layer && layer < max_copper_layer)
1445 PCB->Data->Layer[layer].On = On;
1447 /* push layer on top of stack if switched on */
1448 if (On && ChangeStackOrder)
1449 PushOnTopOfLayerStack (layer);
1450 changed++;
1454 /* change at least the passed layer */
1455 PCB->Data->Layer[Layer].On = On;
1456 if (On && ChangeStackOrder)
1457 PushOnTopOfLayerStack (Layer);
1459 /* update control panel and exit */
1460 hid_action ("LayersChanged");
1461 return (changed);
1465 * \brief Given a string description of a layer stack, adjust the layer
1466 * stack to correspond.
1468 void
1469 LayerStringToLayerStack (char *s)
1471 static int listed_layers = 0;
1472 int l = strlen (s);
1473 char **args;
1474 int i, argn, lno;
1475 int prev_sep = 1;
1477 s = strdup (s);
1478 args = (char **) malloc (l * sizeof (char *));
1479 argn = 0;
1481 for (i=0; i<l; i++)
1483 switch (s[i])
1485 case ' ':
1486 case '\t':
1487 case ',':
1488 case ';':
1489 case ':':
1490 prev_sep = 1;
1491 s[i] = '\0';
1492 break;
1493 default:
1494 if (prev_sep)
1495 args[argn++] = s+i;
1496 prev_sep = 0;
1497 break;
1501 for (i = 0; i < max_copper_layer + SILK_LAYER; i++)
1503 if (i < max_copper_layer)
1504 LayerStack[i] = i;
1505 PCB->Data->Layer[i].On = false;
1507 PCB->ElementOn = false;
1508 PCB->InvisibleObjectsOn = false;
1509 PCB->PinOn = false;
1510 PCB->ViaOn = false;
1511 PCB->RatOn = false;
1512 CLEAR_FLAG (SHOWMASKFLAG, PCB);
1513 Settings.ShowBottomSide = 0;
1515 for (i=argn-1; i>=0; i--)
1517 if (strcasecmp (args[i], "rats") == 0)
1518 PCB->RatOn = true;
1519 else if (strcasecmp (args[i], "invisible") == 0)
1520 PCB->InvisibleObjectsOn = true;
1521 else if (strcasecmp (args[i], "pins") == 0)
1522 PCB->PinOn = true;
1523 else if (strcasecmp (args[i], "vias") == 0)
1524 PCB->ViaOn = true;
1525 else if (strcasecmp (args[i], "elements") == 0
1526 || strcasecmp (args[i], "silk") == 0)
1527 PCB->ElementOn = true;
1528 else if (strcasecmp (args[i], "mask") == 0)
1529 SET_FLAG (SHOWMASKFLAG, PCB);
1530 else if (strcasecmp (args[i], "solderside") == 0)
1531 Settings.ShowBottomSide = 1;
1532 else if (isdigit ((int) args[i][0]))
1534 lno = atoi (args[i]);
1535 ChangeGroupVisibility (lno, true, true);
1537 else
1539 int found = 0;
1540 for (lno = 0; lno < max_copper_layer; lno++)
1541 if (strcasecmp (args[i], PCB->Data->Layer[lno].Name) == 0)
1543 ChangeGroupVisibility (lno, true, true);
1544 found = 1;
1545 break;
1547 if (!found)
1549 fprintf(stderr, _("Warning: layer \"%s\" not known\n"), args[i]);
1550 if (!listed_layers)
1552 fprintf (stderr, _("Named layers in this board are:\n"));
1553 listed_layers = 1;
1554 for (lno=0; lno < max_copper_layer; lno ++)
1555 fprintf(stderr, "\t%s\n", PCB->Data->Layer[lno].Name);
1556 fprintf(stderr, _("Also: component, solder, rats, invisible, "
1557 "pins, vias, elements or silk, mask, solderside.\n"));
1565 * \brief Returns the layergroup number for the passed pointer.
1568 GetLayerGroupNumberByPointer (LayerType *Layer)
1570 return (GetLayerGroupNumberByNumber (GetLayerNumber (PCB->Data, Layer)));
1574 * \brief Returns the layergroup number for the passed layer number.
1577 GetLayerGroupNumberByNumber (Cardinal Layer)
1579 int group, entry;
1581 for (group = 0; group < max_group; group++)
1582 for (entry = 0; entry < PCB->LayerGroups.Number[group]; entry++)
1583 if (PCB->LayerGroups.Entries[group][entry] == Layer)
1584 return (group);
1586 /* since every layer belongs to a group it is safe to return
1587 * the value without boundary checking
1589 return (group);
1593 * \brief Returns the layergroup number for the passed side (TOP_SIDE or
1594 * BOTTOM_SIDE).
1597 GetLayerGroupNumberBySide (int side)
1599 /* Find the relavant board side layer group by determining the
1600 * layer group associated with the relevant side's silk-screen
1602 return GetLayerGroupNumberByNumber(
1603 side == TOP_SIDE ? top_silk_layer : bottom_silk_layer);
1607 * \brief Returns a pointer to an objects bounding box.
1609 * Data is valid until the routine is called again.
1611 BoxType *
1612 GetObjectBoundingBox (int Type, void *Ptr1, void *Ptr2, void *Ptr3)
1614 switch (Type)
1616 case LINE_TYPE:
1617 case ARC_TYPE:
1618 case TEXT_TYPE:
1619 case POLYGON_TYPE:
1620 case PAD_TYPE:
1621 case PIN_TYPE:
1622 case ELEMENTNAME_TYPE:
1623 return (BoxType *)Ptr2;
1624 case VIA_TYPE:
1625 case ELEMENT_TYPE:
1626 return (BoxType *)Ptr1;
1627 case POLYGONPOINT_TYPE:
1628 case LINEPOINT_TYPE:
1629 return (BoxType *)Ptr3;
1630 default:
1631 Message (_("Request for bounding box of unsupported type %d\n"), Type);
1632 return (BoxType *)Ptr2;
1637 * \brief Computes the bounding box of an arc.
1639 void
1640 SetArcBoundingBox (ArcType *Arc)
1642 double ca1, ca2, sa1, sa2;
1643 double minx, maxx, miny, maxy;
1644 Angle ang1, ang2;
1645 Coord width;
1647 /* first put angles into standard form:
1648 * ang1 < ang2, both angles between 0 and 720 */
1649 Arc->Delta = CLAMP (Arc->Delta, -360, 360);
1651 if (Arc->Delta > 0)
1653 ang1 = NormalizeAngle (Arc->StartAngle);
1654 ang2 = NormalizeAngle (Arc->StartAngle + Arc->Delta);
1656 else
1658 ang1 = NormalizeAngle (Arc->StartAngle + Arc->Delta);
1659 ang2 = NormalizeAngle (Arc->StartAngle);
1661 if (ang1 > ang2)
1662 ang2 += 360;
1663 /* Make sure full circles aren't treated as zero-length arcs */
1664 if (Arc->Delta == 360 || Arc->Delta == -360)
1665 ang2 = ang1 + 360;
1667 /* calculate sines, cosines */
1668 sa1 = sin (M180 * ang1);
1669 ca1 = cos (M180 * ang1);
1670 sa2 = sin (M180 * ang2);
1671 ca2 = cos (M180 * ang2);
1673 minx = MIN (ca1, ca2);
1674 maxx = MAX (ca1, ca2);
1675 miny = MIN (sa1, sa2);
1676 maxy = MAX (sa1, sa2);
1678 /* Check for extreme angles */
1679 if ((ang1 <= 0 && ang2 >= 0) || (ang1 <= 360 && ang2 >= 360)) maxx = 1;
1680 if ((ang1 <= 90 && ang2 >= 90) || (ang1 <= 450 && ang2 >= 450)) maxy = 1;
1681 if ((ang1 <= 180 && ang2 >= 180) || (ang1 <= 540 && ang2 >= 540)) minx = -1;
1682 if ((ang1 <= 270 && ang2 >= 270) || (ang1 <= 630 && ang2 >= 630)) miny = -1;
1684 /* Finally, calcate bounds, converting sane geometry into pcb geometry */
1685 Arc->BoundingBox.X1 = Arc->X - Arc->Width * maxx;
1686 Arc->BoundingBox.X2 = Arc->X - Arc->Width * minx;
1687 Arc->BoundingBox.Y1 = Arc->Y + Arc->Height * miny;
1688 Arc->BoundingBox.Y2 = Arc->Y + Arc->Height * maxy;
1690 width = (Arc->Thickness + Arc->Clearance) / 2;
1692 /* Adjust for our discrete polygon approximation */
1693 width = (double)width * MAX (POLY_CIRC_RADIUS_ADJ, (1.0 + POLY_ARC_MAX_DEVIATION)) + 0.5;
1695 Arc->BoundingBox.X1 -= width;
1696 Arc->BoundingBox.X2 += width;
1697 Arc->BoundingBox.Y1 -= width;
1698 Arc->BoundingBox.Y2 += width;
1699 close_box(&Arc->BoundingBox);
1701 /* Update the arc end-points */
1702 Arc->Point1.X = Arc->X - (double)Arc->Width * ca1;
1703 Arc->Point1.Y = Arc->Y + (double)Arc->Height * sa1;
1704 Arc->Point2.X = Arc->X - (double)Arc->Width * ca2;
1705 Arc->Point2.Y = Arc->Y + (double)Arc->Height * sa2;
1709 * \brief Resets the layerstack setting.
1711 void
1712 ResetStackAndVisibility (void)
1714 int top_group;
1715 Cardinal i;
1717 for (i = 0; i < max_copper_layer + SILK_LAYER; i++)
1719 if (i < max_copper_layer)
1720 LayerStack[i] = i;
1721 PCB->Data->Layer[i].On = true;
1723 PCB->ElementOn = true;
1724 PCB->InvisibleObjectsOn = true;
1725 PCB->PinOn = true;
1726 PCB->ViaOn = true;
1727 PCB->RatOn = true;
1729 /* Bring the component group to the front and make it active. */
1730 top_group = GetLayerGroupNumberBySide (TOP_SIDE);
1731 ChangeGroupVisibility (PCB->LayerGroups.Entries[top_group][0], 1, 1);
1735 * \brief Saves the layerstack setting.
1737 void
1738 SaveStackAndVisibility (void)
1740 Cardinal i;
1741 static bool run = false;
1743 if (run == false)
1745 SavedStack.cnt = 0;
1746 run = true;
1749 if (SavedStack.cnt != 0)
1751 fprintf (stderr,
1752 "SaveStackAndVisibility() layerstack was already saved and not"
1753 "yet restored. cnt = %d\n", SavedStack.cnt);
1756 for (i = 0; i < max_copper_layer + SILK_LAYER; i++)
1758 if (i < max_copper_layer)
1759 SavedStack.LayerStack[i] = LayerStack[i];
1760 SavedStack.LayerOn[i] = PCB->Data->Layer[i].On;
1762 SavedStack.ElementOn = PCB->ElementOn;
1763 SavedStack.InvisibleObjectsOn = PCB->InvisibleObjectsOn;
1764 SavedStack.PinOn = PCB->PinOn;
1765 SavedStack.ViaOn = PCB->ViaOn;
1766 SavedStack.RatOn = PCB->RatOn;
1767 SavedStack.cnt++;
1771 * \brief Restores the layerstack setting.
1773 void
1774 RestoreStackAndVisibility (void)
1776 Cardinal i;
1778 if (SavedStack.cnt == 0)
1780 fprintf (stderr, "RestoreStackAndVisibility() layerstack has not"
1781 " been saved. cnt = %d\n", SavedStack.cnt);
1782 return;
1784 else if (SavedStack.cnt != 1)
1786 fprintf (stderr, "RestoreStackAndVisibility() layerstack save count is"
1787 " wrong. cnt = %d\n", SavedStack.cnt);
1790 for (i = 0; i < max_copper_layer + SILK_LAYER; i++)
1792 if (i < max_copper_layer)
1793 LayerStack[i] = SavedStack.LayerStack[i];
1794 PCB->Data->Layer[i].On = SavedStack.LayerOn[i];
1796 PCB->ElementOn = SavedStack.ElementOn;
1797 PCB->InvisibleObjectsOn = SavedStack.InvisibleObjectsOn;
1798 PCB->PinOn = SavedStack.PinOn;
1799 PCB->ViaOn = SavedStack.ViaOn;
1800 PCB->RatOn = SavedStack.RatOn;
1802 SavedStack.cnt--;
1806 * \brief Returns pointer to current working directory.
1808 * If 'path' is not NULL, then the current working directory is copied
1809 * to the array pointed to by 'path'.
1811 char *
1812 GetWorkingDirectory (char *path)
1814 #ifdef HAVE_GETCWD
1815 return getcwd (path, MAXPATHLEN);
1816 #else
1817 /* seems that some BSD releases lack of a prototype for getwd() */
1818 return getwd (path);
1819 #endif
1824 * \brief Write a string to the passed file pointer.
1826 * Some special characters are quoted.
1828 void
1829 CreateQuotedString (DynamicStringType *DS, char *S)
1831 DSClearString (DS);
1832 DSAddCharacter (DS, '"');
1833 while (*S)
1835 if (*S == '"' || *S == '\\')
1836 DSAddCharacter (DS, '\\');
1837 DSAddCharacter (DS, *S++);
1839 DSAddCharacter (DS, '"');
1842 BoxType *
1843 GetArcEnds (ArcType *Arc)
1845 static BoxType box;
1846 box.X1 = Arc->X - Arc->Width * cos (Arc->StartAngle * M180);
1847 box.Y1 = Arc->Y + Arc->Height * sin (Arc->StartAngle * M180);
1848 box.X2 = Arc->X - Arc->Width * cos ((Arc->StartAngle + Arc->Delta) * M180);
1849 box.Y2 = Arc->Y + Arc->Height * sin ((Arc->StartAngle + Arc->Delta) * M180);
1850 return &box;
1855 * \todo Doesn't this belong in change.c ??
1857 void
1858 ChangeArcAngles (LayerType *Layer, ArcType *a,
1859 Angle new_sa, Angle new_da)
1861 if (new_da >= 360)
1863 new_da = 360;
1864 new_sa = 0;
1866 RestoreToPolygon (PCB->Data, ARC_TYPE, Layer, a);
1867 r_delete_entry (Layer->arc_tree, (BoxType *) a);
1868 AddObjectToChangeAnglesUndoList (ARC_TYPE, a, a, a);
1869 a->StartAngle = new_sa;
1870 a->Delta = new_da;
1871 SetArcBoundingBox (a);
1872 r_insert_entry (Layer->arc_tree, (BoxType *) a, 0);
1873 ClearFromPolygon (PCB->Data, ARC_TYPE, Layer, a);
1876 static char *
1877 BumpName (char *Name)
1879 int num;
1880 char c, *start;
1881 static char temp[256];
1883 start = Name;
1884 /* seek end of string */
1885 while (*Name != 0)
1886 Name++;
1887 /* back up to potential number */
1888 for (Name--; isdigit ((int) *Name); Name--);
1889 Name++;
1890 if (*Name)
1891 num = atoi (Name) + 1;
1892 else
1893 num = 1;
1894 c = *Name;
1895 *Name = 0;
1896 sprintf (temp, "%s%d", start, num);
1897 /* if this is not our string, put back the blown character */
1898 if (start != temp)
1899 *Name = c;
1900 return (temp);
1904 * \brief Make a unique name for the name on board.
1906 * This can alter the contents of the input string.
1908 char *
1909 UniqueElementName (DataType *Data, char *Name)
1911 bool unique = true;
1912 /* null strings are ok */
1913 if (!Name || !*Name)
1914 return (Name);
1916 for (;;)
1918 ELEMENT_LOOP (Data);
1920 if (NAMEONPCB_NAME (element) &&
1921 NSTRCMP (NAMEONPCB_NAME (element), Name) == 0)
1923 Name = BumpName (Name);
1924 unique = false;
1925 break;
1928 END_LOOP;
1929 if (unique)
1930 return (Name);
1931 unique = true;
1935 static void
1936 GetGridLockCoordinates (int type, void *ptr1,
1937 void *ptr2, void *ptr3, Coord * x,
1938 Coord * y)
1940 switch (type)
1942 case VIA_TYPE:
1943 *x = ((PinType *) ptr2)->X;
1944 *y = ((PinType *) ptr2)->Y;
1945 break;
1946 case LINE_TYPE:
1947 *x = ((LineType *) ptr2)->Point1.X;
1948 *y = ((LineType *) ptr2)->Point1.Y;
1949 break;
1950 case TEXT_TYPE:
1951 case ELEMENTNAME_TYPE:
1952 *x = ((TextType *) ptr2)->X;
1953 *y = ((TextType *) ptr2)->Y;
1954 break;
1955 case ELEMENT_TYPE:
1956 *x = ((ElementType *) ptr2)->MarkX;
1957 *y = ((ElementType *) ptr2)->MarkY;
1958 break;
1959 case POLYGON_TYPE:
1960 *x = ((PolygonType *) ptr2)->Points[0].X;
1961 *y = ((PolygonType *) ptr2)->Points[0].Y;
1962 break;
1964 case LINEPOINT_TYPE:
1965 case POLYGONPOINT_TYPE:
1966 *x = ((PointType *) ptr3)->X;
1967 *y = ((PointType *) ptr3)->Y;
1968 break;
1969 case ARC_TYPE:
1971 BoxType *box;
1973 box = GetArcEnds ((ArcType *) ptr2);
1974 *x = box->X1;
1975 *y = box->Y1;
1976 break;
1981 void
1982 AttachForCopy (Coord PlaceX, Coord PlaceY)
1984 BoxType *box;
1985 Coord mx = 0, my = 0;
1987 Crosshair.AttachedObject.RubberbandN = 0;
1988 if (! TEST_FLAG (SNAPPINFLAG, PCB))
1990 /* dither the grab point so that the mark, center, etc
1991 * will end up on a grid coordinate
1993 GetGridLockCoordinates (Crosshair.AttachedObject.Type,
1994 Crosshair.AttachedObject.Ptr1,
1995 Crosshair.AttachedObject.Ptr2,
1996 Crosshair.AttachedObject.Ptr3, &mx, &my);
1997 mx = GridFit (mx, PCB->Grid, PCB->GridOffsetX) - mx;
1998 my = GridFit (my, PCB->Grid, PCB->GridOffsetY) - my;
2000 Crosshair.AttachedObject.X = PlaceX - mx;
2001 Crosshair.AttachedObject.Y = PlaceY - my;
2002 if (!Marked.status || TEST_FLAG (LOCALREFFLAG, PCB))
2003 SetLocalRef (PlaceX - mx, PlaceY - my, true);
2004 Crosshair.AttachedObject.State = STATE_SECOND;
2006 /* get boundingbox of object and set cursor range */
2007 box = GetObjectBoundingBox (Crosshair.AttachedObject.Type,
2008 Crosshair.AttachedObject.Ptr1,
2009 Crosshair.AttachedObject.Ptr2,
2010 Crosshair.AttachedObject.Ptr3);
2011 SetCrosshairRange (Crosshair.AttachedObject.X - box->X1,
2012 Crosshair.AttachedObject.Y - box->Y1,
2013 PCB->MaxWidth - (box->X2 - Crosshair.AttachedObject.X),
2014 PCB->MaxHeight - (box->Y2 - Crosshair.AttachedObject.Y));
2016 /* get all attached objects if necessary */
2017 if ((Settings.Mode != COPY_MODE) && TEST_FLAG (RUBBERBANDFLAG, PCB))
2018 LookupRubberbandLines (Crosshair.AttachedObject.Type,
2019 Crosshair.AttachedObject.Ptr1,
2020 Crosshair.AttachedObject.Ptr2,
2021 Crosshair.AttachedObject.Ptr3);
2022 if (Settings.Mode != COPY_MODE &&
2023 (Crosshair.AttachedObject.Type == ELEMENT_TYPE ||
2024 Crosshair.AttachedObject.Type == VIA_TYPE ||
2025 Crosshair.AttachedObject.Type == LINE_TYPE ||
2026 Crosshair.AttachedObject.Type == LINEPOINT_TYPE))
2027 LookupRatLines (Crosshair.AttachedObject.Type,
2028 Crosshair.AttachedObject.Ptr1,
2029 Crosshair.AttachedObject.Ptr2,
2030 Crosshair.AttachedObject.Ptr3);
2034 * \brief Return nonzero if the given file exists and is readable.
2037 FileExists (const char *name)
2039 FILE *f;
2040 f = fopen (name, "r");
2041 if (f)
2043 fclose (f);
2044 return 1;
2046 return 0;
2049 char *
2050 Concat (const char *first, ...)
2052 char *rv;
2053 int len;
2054 va_list a;
2056 len = strlen (first);
2057 rv = (char *) malloc (len + 1);
2058 strcpy (rv, first);
2060 va_start (a, first);
2061 while (1)
2063 const char *s = va_arg (a, const char *);
2064 if (!s)
2065 break;
2066 len += strlen (s);
2067 rv = (char *) realloc (rv, len + 1);
2068 strcat (rv, s);
2070 va_end (a);
2071 return rv;
2075 mem_any_set (unsigned char *ptr, int bytes)
2077 while (bytes--)
2078 if (*ptr++)
2079 return 1;
2080 return 0;
2084 * \brief This just fills in a FlagType with current flags.
2086 FlagType
2087 MakeFlags (unsigned int flags)
2089 FlagType rv;
2090 memset (&rv, 0, sizeof (rv));
2091 rv.f = flags;
2092 return rv;
2096 * \brief This converts old flag bits (from saved PCB files) to new
2097 * format.
2099 FlagType
2100 OldFlags (unsigned int flags)
2102 FlagType rv;
2103 int i, f;
2104 memset (&rv, 0, sizeof (rv));
2105 /* If we move flag bits around, this is where we map old bits to them. */
2106 rv.f = flags & 0xffff;
2107 f = 0x10000;
2108 for (i = 0; i < 8; i++)
2110 /* use the closest thing to the old thermal style */
2111 if (flags & f)
2112 rv.t[i / 2] |= (1 << (4 * (i % 2)));
2113 f <<= 1;
2115 return rv;
2118 FlagType
2119 AddFlags (FlagType flag, unsigned int flags)
2121 flag.f |= flags;
2122 return flag;
2125 FlagType
2126 MaskFlags (FlagType flag, unsigned int flags)
2128 flag.f &= ~flags;
2129 return flag;
2132 /* Layer Group Functions. */
2135 * \brief Returns group actually moved to (i.e. either group or
2136 * previous).
2139 MoveLayerToGroup (int layer, int group)
2141 int prev, i, j;
2143 if (layer < 0 || layer > max_copper_layer + 1)
2144 return -1;
2145 prev = GetLayerGroupNumberByNumber (layer);
2146 if ((layer == bottom_silk_layer
2147 && group == GetLayerGroupNumberByNumber (top_silk_layer))
2148 || (layer == top_silk_layer
2149 && group == GetLayerGroupNumberByNumber (bottom_silk_layer))
2150 || (group < 0 || group >= max_group) || (prev == group))
2151 return prev;
2153 /* Remove layer from prev group */
2154 for (j = i = 0; i < PCB->LayerGroups.Number[prev]; i++)
2155 if (PCB->LayerGroups.Entries[prev][i] != layer)
2156 PCB->LayerGroups.Entries[prev][j++] = PCB->LayerGroups.Entries[prev][i];
2157 PCB->LayerGroups.Number[prev]--;
2159 /* Add layer to new group. */
2160 i = PCB->LayerGroups.Number[group]++;
2161 PCB->LayerGroups.Entries[group][i] = layer;
2163 return group;
2167 * \brief Returns pointer to private buffer.
2169 char *
2170 LayerGroupsToString (LayerGroupType *lg)
2172 #if MAX_ALL_LAYER < 9999
2173 /* Allows for layer numbers 0..9999 */
2174 static char buf[(MAX_ALL_LAYER) * 5 + 1];
2175 #endif
2176 char *cp = buf;
2177 char sep = 0;
2178 int group, entry;
2179 for (group = 0; group < max_group; group++)
2180 if (PCB->LayerGroups.Number[group])
2182 if (sep)
2183 *cp++ = ':';
2184 sep = 1;
2185 for (entry = 0; entry < PCB->LayerGroups.Number[group]; entry++)
2187 int layer = PCB->LayerGroups.Entries[group][entry];
2188 if (layer == top_silk_layer)
2190 *cp++ = 'c';
2192 else if (layer == bottom_silk_layer)
2194 *cp++ = 's';
2196 else
2198 sprintf (cp, "%d", layer + 1);
2199 while (*++cp)
2202 if (entry != PCB->LayerGroups.Number[group] - 1)
2203 *cp++ = ',';
2206 *cp++ = 0;
2207 return buf;
2210 char *
2211 pcb_author (void)
2213 #ifdef HAVE_GETPWUID
2214 static struct passwd *pwentry;
2215 static char *fab_author = 0;
2217 if (!fab_author)
2219 if (Settings.FabAuthor && Settings.FabAuthor[0])
2220 fab_author = Settings.FabAuthor;
2221 else
2223 int len;
2224 char *comma, *gecos;
2226 /* ID the user. */
2227 pwentry = getpwuid (getuid ());
2228 gecos = pwentry->pw_gecos;
2229 comma = strchr (gecos, ',');
2230 if (comma)
2231 len = comma - gecos;
2232 else
2233 len = strlen (gecos);
2234 fab_author = (char *)malloc (len + 1);
2235 if (!fab_author)
2237 perror ("pcb: out of memory.\n");
2238 exit (-1);
2240 memcpy (fab_author, gecos, len);
2241 fab_author[len] = 0;
2244 return fab_author;
2245 #else
2246 return "Unknown";
2247 #endif
2252 * \brief Returns NULL if the name isn't found, else the value for that
2253 * named attribute.
2255 char *
2256 AttributeGetFromList (AttributeListType *list, char *name)
2258 int i;
2259 for (i=0; i<list->Number; i++)
2260 if (strcmp (name, list->List[i].name) == 0)
2261 return list->List[i].value;
2262 return NULL;
2266 * \brief Adds an attribute to the list.
2268 * If the attribute already exists, whether it's replaced or a second
2269 * copy added depends on REPLACE.
2271 * \return Non-zero if an existing attribute was replaced.
2274 AttributePutToList (AttributeListType *list, const char *name, const char *value, int replace)
2276 int i;
2278 /* If we're allowed to replace an existing attribute, see if we
2279 can. */
2280 if (replace)
2282 for (i=0; i<list->Number; i++)
2283 if (strcmp (name, list->List[i].name) == 0)
2285 free (list->List[i].value);
2286 list->List[i].value = STRDUP (value);
2287 return 1;
2291 /* At this point, we're going to need to add a new attribute to the
2292 list. See if there's room. */
2293 if (list->Number >= list->Max)
2295 list->Max += 10;
2296 list->List = (AttributeType *) realloc (list->List,
2297 list->Max * sizeof (AttributeType));
2300 /* Now add the new attribute. */
2301 i = list->Number;
2302 list->List[i].name = STRDUP (name);
2303 list->List[i].value = STRDUP (value);
2304 list->Number ++;
2305 return 0;
2309 * \brief Remove an attribute by name.
2311 void
2312 AttributeRemoveFromList(AttributeListType *list, char *name)
2314 int i, j;
2315 for (i=0; i<list->Number; i++)
2316 if (strcmp (name, list->List[i].name) == 0)
2318 free (list->List[i].name);
2319 free (list->List[i].value);
2320 for (j=i; j<list->Number-i; j++)
2321 list->List[j] = list->List[j+1];
2322 list->Number --;
2327 * \todo In future all use of this should be supplanted by pcb-printf
2328 * and %mr/%m# spec.
2330 * These act like you'd expect, except always in the C locale.
2332 const char *
2333 c_dtostr (double d)
2335 static char buf[100];
2336 int i, f;
2337 char *bufp = buf;
2339 if (d < 0)
2341 *bufp++ = '-';
2342 d = -d;
2344 d += 0.0000005; /* rounding */
2345 i = floor (d);
2346 d -= i;
2347 sprintf (bufp, "%d", i);
2348 bufp += strlen (bufp);
2349 *bufp++ = '.';
2351 f = floor (d * 1000000.0);
2352 sprintf (bufp, "%06d", f);
2353 return buf;
2356 void
2357 r_delete_element (DataType * data, ElementType * element)
2359 r_delete_entry (data->element_tree, (BoxType *) element);
2360 PIN_LOOP (element);
2362 r_delete_entry (data->pin_tree, (BoxType *) pin);
2364 END_LOOP;
2365 PAD_LOOP (element);
2367 r_delete_entry (data->pad_tree, (BoxType *) pad);
2369 END_LOOP;
2370 ELEMENTTEXT_LOOP (element);
2372 r_delete_entry (data->name_tree[n], (BoxType *) text);
2374 END_LOOP;
2379 * \brief Returns a string that has a bunch of information about the
2380 * program.
2382 * Can be used for things like "about" dialog boxes.
2384 char *
2385 GetInfoString (void)
2387 HID **hids;
2388 int i;
2389 static DynamicStringType info;
2390 static int first_time = 1;
2392 #define TAB " "
2394 if (first_time)
2396 first_time = 0;
2397 DSAddString (&info,
2398 _("This is PCB, an interactive\n"
2399 "printed circuit board editor\n"
2400 "version "));
2401 DSAddString (&info,
2402 VERSION "\n\n"
2403 "Compiled on " __DATE__ " at " __TIME__ "\n\n"
2404 "by harry eaton\n\n"
2405 "Copyright (C) Thomas Nau 1994, 1995, 1996, 1997\n"
2406 "Copyright (C) harry eaton 1998-2007\n"
2407 "Copyright (C) C. Scott Ananian 2001\n"
2408 "Copyright (C) DJ Delorie 2003, 2004, 2005, 2006, 2007, 2008\n"
2409 "Copyright (C) Dan McMahill 2003, 2004, 2005, 2006, 2007, 2008\n\n");
2410 DSAddString (&info,
2411 _("It is licensed under the terms of the GNU\n"
2412 "General Public License version 2\n"
2413 "See the LICENSE file for more information\n\n"
2414 "For more information see:\n"));
2415 DSAddString (&info, _("PCB homepage: "));
2416 DSAddString (&info, "http://pcb.geda-project.org\n");
2417 DSAddString (&info, _("gEDA homepage: "));
2418 DSAddString (&info, "http://www.geda-project.org\n");
2419 DSAddString (&info, _("gEDA Wiki: "));
2420 DSAddString (&info, "http://wiki.geda-project.org\n");
2422 DSAddString (&info, _("\n----- Compile Time Options -----\n"));
2423 hids = hid_enumerate ();
2424 DSAddString (&info, _("GUI:\n"));
2425 for (i = 0; hids[i]; i++)
2427 if (hids[i]->gui)
2429 DSAddString (&info, TAB);
2430 DSAddString (&info, hids[i]->name);
2431 DSAddString (&info, " : ");
2432 DSAddString (&info, hids[i]->description);
2433 DSAddString (&info, "\n");
2437 DSAddString (&info, _("Exporters:\n"));
2438 for (i = 0; hids[i]; i++)
2440 if (hids[i]->exporter)
2442 DSAddString (&info, TAB);
2443 DSAddString (&info, hids[i]->name);
2444 DSAddString (&info, " : ");
2445 DSAddString (&info, hids[i]->description);
2446 DSAddString (&info, "\n");
2450 DSAddString (&info, _("Printers:\n"));
2451 for (i = 0; hids[i]; i++)
2453 if (hids[i]->printer)
2455 DSAddString (&info, TAB);
2456 DSAddString (&info, hids[i]->name);
2457 DSAddString (&info, " : ");
2458 DSAddString (&info, hids[i]->description);
2459 DSAddString (&info, "\n");
2463 #undef TAB
2465 return info.Data;
2468 #ifdef MKDIR_IS_PCBMKDIR
2469 #error "Don't know how to create a directory on this system."
2470 #endif
2473 * \brief mkdir() implentation, mostly for plugins, which don't have our
2474 * config.h.
2477 pcb_mkdir (const char *path, int mode)
2479 return MKDIR (path, mode);
2483 * \brief Returns a best guess about the orientation of an element.
2485 * The value corresponds to the rotation; a difference is the right
2486 * value to pass to RotateElementLowLevel.
2487 * However, the actual value is no indication of absolute rotation; only
2488 * relative rotation is meaningful.
2490 * \return a relative rotation for an element, useful only for comparing
2491 * two similar footprints.
2493 int
2494 ElementOrientation (ElementType *e)
2496 Coord pin1x, pin1y, pin2x, pin2y, dx, dy;
2497 bool found_pin1 = 0;
2498 bool found_pin2 = 0;
2500 /* in case we don't find pin 1 or 2, make sure we have initialized these variables */
2501 pin1x = 0;
2502 pin1y = 0;
2503 pin2x = 0;
2504 pin2y = 0;
2506 PIN_LOOP (e);
2508 if (NSTRCMP (pin->Number, "1") == 0)
2510 pin1x = pin->X;
2511 pin1y = pin->Y;
2512 found_pin1 = 1;
2514 else if (NSTRCMP (pin->Number, "2") == 0)
2516 pin2x = pin->X;
2517 pin2y = pin->Y;
2518 found_pin2 = 1;
2521 END_LOOP;
2523 PAD_LOOP (e);
2525 if (NSTRCMP (pad->Number, "1") == 0)
2527 pin1x = (pad->Point1.X + pad->Point2.X) / 2;
2528 pin1y = (pad->Point1.Y + pad->Point2.Y) / 2;
2529 found_pin1 = 1;
2531 else if (NSTRCMP (pad->Number, "2") == 0)
2533 pin2x = (pad->Point1.X + pad->Point2.X) / 2;
2534 pin2y = (pad->Point1.Y + pad->Point2.Y) / 2;
2535 found_pin2 = 1;
2538 END_LOOP;
2540 if (found_pin1 && found_pin2)
2542 dx = pin2x - pin1x;
2543 dy = pin2y - pin1y;
2545 else if (found_pin1 && (pin1x || pin1y))
2547 dx = pin1x;
2548 dy = pin1y;
2550 else if (found_pin2 && (pin2x || pin2y))
2552 dx = pin2x;
2553 dy = pin2y;
2555 else
2556 return 0;
2558 if (abs(dx) > abs(dy))
2559 return dx > 0 ? 0 : 2;
2560 return dy > 0 ? 3 : 1;
2564 ActionListRotations(int argc, char **argv, Coord x, Coord y)
2566 ELEMENT_LOOP (PCB->Data);
2568 printf("%d %s\n", ElementOrientation(element), NAMEONPCB_NAME(element));
2570 END_LOOP;
2572 return 0;
2575 HID_Action misc_action_list[] = {
2576 {"ListRotations", 0, ActionListRotations,
2577 0,0},
2580 REGISTER_ACTIONS (misc_action_list)