Fix lockup when a scroll event is received outside the drawing area
[geda-pcb/pcjc2.git] / src / misc.c
blob5c437549afcf38ba92d47ac68fbd67110ab3404a
1 /*
2 * COPYRIGHT
4 * PCB, interactive printed circuit board design
5 * Copyright (C) 1994,1995,1996,2004,2006 Thomas Nau
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 * Contact addresses for paper mail and Email:
22 * Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
23 * Thomas.Nau@rz.uni-ulm.de
28 /* misc functions used by several modules
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
35 #include <stdlib.h>
36 #include <stdarg.h>
37 #ifdef HAVE_STRING_H
38 #include <string.h>
39 #endif
40 #include <memory.h>
41 #include <ctype.h>
42 #include <signal.h>
43 #include <sys/param.h>
44 #include <sys/stat.h>
45 #include <sys/types.h>
46 #include <math.h>
47 #ifdef HAVE_UNISTD_H
48 #include <unistd.h>
49 #endif
50 #ifdef HAVE_PWD_H
51 #include <pwd.h>
52 #endif
54 #include "global.h"
56 #include "box.h"
57 #include "crosshair.h"
58 #include "create.h"
59 #include "data.h"
60 #include "draw.h"
61 #include "file.h"
62 #include "error.h"
63 #include "mymem.h"
64 #include "misc.h"
65 #include "move.h"
66 #include "pcb-printf.h"
67 #include "polygon.h"
68 #include "remove.h"
69 #include "rtree.h"
70 #include "rotate.h"
71 #include "rubberband.h"
72 #include "search.h"
73 #include "set.h"
74 #include "undo.h"
75 #include "action.h"
77 #ifdef HAVE_LIBDMALLOC
78 #include <dmalloc.h>
79 #endif
81 /* forward declarations */
82 static char *BumpName (char *);
83 static void GetGridLockCoordinates (int, void *, void *, void *,
84 Coord *, Coord *);
87 /* Local variables */
89 /*
90 * Used by SaveStackAndVisibility() and
91 * RestoreStackAndVisibility()
94 static struct
96 bool ElementOn, InvisibleObjectsOn, PinOn, ViaOn, RatOn;
97 int LayerStack[MAX_LAYER];
98 bool LayerOn[MAX_LAYER];
99 int cnt;
100 } SavedStack;
102 /* Distance() should be used so that there is only one
103 * place to deal with overflow/precision errors
105 double
106 Distance (double x1, double y1, double x2, double y2)
108 double delta_x = (x2 - x1);
109 double delta_y = (y2 - y1);
110 return sqrt(delta_x * delta_x + delta_y * delta_y);
113 /* Bring an angle into [0, 360) range */
114 Angle
115 NormalizeAngle (Angle a)
117 while (a < 0)
118 a += 360.0;
119 while (a >= 360.0)
120 a -= 360.0;
121 return a;
124 /* Get Value returns a numeric value passed from the string and sets the
125 * bool variable absolute to false if it leads with a +/- character
127 double
128 GetValue (const char *val, const char *units, bool * absolute)
130 return GetValueEx(val, units, absolute, NULL, "cmil");
133 double
134 GetValueEx (const char *val, const char *units, bool * absolute, UnitList extra_units, const char *default_unit)
136 double value;
137 int n = -1;
138 bool scaled = 0;
139 bool dummy;
141 /* Allow NULL to be passed for absolute */
142 if(absolute == NULL)
143 absolute = &dummy;
145 /* if the first character is a sign we have to add the
146 * value to the current one
148 if (*val == '=')
150 *absolute = true;
151 if (sscanf (val+1, "%lf%n", &value, &n) < 1)
152 return 0;
153 n++;
155 else
157 if (isdigit ((int) *val))
158 *absolute = true;
159 else
160 *absolute = false;
161 if (sscanf (val, "%lf%n", &value, &n) < 1)
162 return 0;
164 if (!units && n > 0)
165 units = val + n;
167 while (units && *units == ' ')
168 units ++;
170 if (units && *units)
172 int i;
173 const Unit *unit = get_unit_struct (units);
174 if (unit != NULL)
176 value = unit_to_coord (unit, value);
177 scaled = 1;
179 if (extra_units)
181 for (i = 0; *extra_units[i].suffix; ++i)
183 if (strncmp (units, extra_units[i].suffix, strlen(extra_units[i].suffix)) == 0)
185 value *= extra_units[i].scale;
186 if (extra_units[i].flags & UNIT_PERCENT)
187 value /= 100.0;
188 scaled = 1;
193 /* Apply default unit */
194 if (!scaled && default_unit && *default_unit)
196 int i;
197 const Unit *unit = get_unit_struct (default_unit);
198 if (extra_units)
199 for (i = 0; *extra_units[i].suffix; ++i)
200 if (strcmp (extra_units[i].suffix, default_unit) == 0)
202 value *= extra_units[i].scale;
203 if (extra_units[i].flags & UNIT_PERCENT)
204 value /= 100.0;
205 scaled = 1;
207 if (!scaled && unit != NULL)
208 value = unit_to_coord (unit, value);
211 return value;
214 /* ---------------------------------------------------------------------------
215 * sets the bounding box of a point (which is silly)
217 void
218 SetPointBoundingBox (PointType *Pnt)
220 Pnt->X2 = Pnt->X + 1;
221 Pnt->Y2 = Pnt->Y + 1;
224 /* ---------------------------------------------------------------------------
225 * sets the bounding box of a pin or via
227 void
228 SetPinBoundingBox (PinType *Pin)
230 Coord width;
232 /* the bounding box covers the extent of influence
233 * so it must include the clearance values too
235 width = MAX (Pin->Clearance + PIN_SIZE (Pin), Pin->Mask) / 2;
237 /* Adjust for our discrete polygon approximation */
238 width = (double)width * POLY_CIRC_RADIUS_ADJ + 0.5;
240 Pin->BoundingBox.X1 = Pin->X - width;
241 Pin->BoundingBox.Y1 = Pin->Y - width;
242 Pin->BoundingBox.X2 = Pin->X + width;
243 Pin->BoundingBox.Y2 = Pin->Y + width;
244 close_box(&Pin->BoundingBox);
247 /* ---------------------------------------------------------------------------
248 * sets the bounding box of a pad
250 void
251 SetPadBoundingBox (PadType *Pad)
253 Coord width;
254 Coord deltax;
255 Coord deltay;
257 /* the bounding box covers the extent of influence
258 * so it must include the clearance values too
260 width = (Pad->Thickness + Pad->Clearance + 1) / 2;
261 width = MAX (width, (Pad->Mask + 1) / 2);
262 deltax = Pad->Point2.X - Pad->Point1.X;
263 deltay = Pad->Point2.Y - Pad->Point1.Y;
265 if (TEST_FLAG (SQUAREFLAG, Pad) && deltax != 0 && deltay != 0)
267 /* slanted square pad */
268 double theta;
269 Coord btx, bty;
271 /* T is a vector half a thickness long, in the direction of
272 one of the corners. */
273 theta = atan2 (deltay, deltax);
274 btx = width * cos (theta + M_PI/4) * sqrt(2.0);
275 bty = width * sin (theta + M_PI/4) * sqrt(2.0);
278 Pad->BoundingBox.X1 = MIN (MIN (Pad->Point1.X - btx, Pad->Point1.X - bty),
279 MIN (Pad->Point2.X + btx, Pad->Point2.X + bty));
280 Pad->BoundingBox.X2 = MAX (MAX (Pad->Point1.X - btx, Pad->Point1.X - bty),
281 MAX (Pad->Point2.X + btx, Pad->Point2.X + bty));
282 Pad->BoundingBox.Y1 = MIN (MIN (Pad->Point1.Y + btx, Pad->Point1.Y - bty),
283 MIN (Pad->Point2.Y - btx, Pad->Point2.Y + bty));
284 Pad->BoundingBox.Y2 = MAX (MAX (Pad->Point1.Y + btx, Pad->Point1.Y - bty),
285 MAX (Pad->Point2.Y - btx, Pad->Point2.Y + bty));
287 else
289 /* Adjust for our discrete polygon approximation */
290 width = (double)width * POLY_CIRC_RADIUS_ADJ + 0.5;
292 Pad->BoundingBox.X1 = MIN (Pad->Point1.X, Pad->Point2.X) - width;
293 Pad->BoundingBox.X2 = MAX (Pad->Point1.X, Pad->Point2.X) + width;
294 Pad->BoundingBox.Y1 = MIN (Pad->Point1.Y, Pad->Point2.Y) - width;
295 Pad->BoundingBox.Y2 = MAX (Pad->Point1.Y, Pad->Point2.Y) + width;
297 close_box(&Pad->BoundingBox);
300 /* ---------------------------------------------------------------------------
301 * sets the bounding box of a line
303 void
304 SetLineBoundingBox (LineType *Line)
306 Coord width = (Line->Thickness + Line->Clearance + 1) / 2;
308 /* Adjust for our discrete polygon approximation */
309 width = (double)width * POLY_CIRC_RADIUS_ADJ + 0.5;
311 Line->BoundingBox.X1 = MIN (Line->Point1.X, Line->Point2.X) - width;
312 Line->BoundingBox.X2 = MAX (Line->Point1.X, Line->Point2.X) + width;
313 Line->BoundingBox.Y1 = MIN (Line->Point1.Y, Line->Point2.Y) - width;
314 Line->BoundingBox.Y2 = MAX (Line->Point1.Y, Line->Point2.Y) + width;
315 close_box(&Line->BoundingBox);
316 SetPointBoundingBox (&Line->Point1);
317 SetPointBoundingBox (&Line->Point2);
320 /* ---------------------------------------------------------------------------
321 * sets the bounding box of a polygons
323 void
324 SetPolygonBoundingBox (PolygonType *Polygon)
326 Polygon->BoundingBox.X1 = Polygon->BoundingBox.Y1 = MAX_COORD;
327 Polygon->BoundingBox.X2 = Polygon->BoundingBox.Y2 = 0;
328 POLYGONPOINT_LOOP (Polygon);
330 MAKEMIN (Polygon->BoundingBox.X1, point->X);
331 MAKEMIN (Polygon->BoundingBox.Y1, point->Y);
332 MAKEMAX (Polygon->BoundingBox.X2, point->X);
333 MAKEMAX (Polygon->BoundingBox.Y2, point->Y);
335 /* boxes don't include the lower right corner */
336 close_box(&Polygon->BoundingBox);
337 END_LOOP;
340 /* ---------------------------------------------------------------------------
341 * sets the bounding box of an elements
343 void
344 SetElementBoundingBox (DataType *Data, ElementType *Element,
345 FontType *Font)
347 BoxType *box, *vbox;
349 if (Data && Data->element_tree)
350 r_delete_entry (Data->element_tree, (BoxType *) Element);
351 /* first update the text objects */
352 ELEMENTTEXT_LOOP (Element);
354 if (Data && Data->name_tree[n])
355 r_delete_entry (Data->name_tree[n], (BoxType *) text);
356 SetTextBoundingBox (Font, text);
357 if (Data && !Data->name_tree[n])
358 Data->name_tree[n] = r_create_tree (NULL, 0, 0);
359 if (Data)
360 r_insert_entry (Data->name_tree[n], (BoxType *) text, 0);
362 END_LOOP;
364 /* do not include the elementnames bounding box which
365 * is handled separately
367 box = &Element->BoundingBox;
368 vbox = &Element->VBox;
369 box->X1 = box->Y1 = MAX_COORD;
370 box->X2 = box->Y2 = 0;
371 ELEMENTLINE_LOOP (Element);
373 SetLineBoundingBox (line);
374 MAKEMIN (box->X1, line->Point1.X - (line->Thickness + 1) / 2);
375 MAKEMIN (box->Y1, line->Point1.Y - (line->Thickness + 1) / 2);
376 MAKEMIN (box->X1, line->Point2.X - (line->Thickness + 1) / 2);
377 MAKEMIN (box->Y1, line->Point2.Y - (line->Thickness + 1) / 2);
378 MAKEMAX (box->X2, line->Point1.X + (line->Thickness + 1) / 2);
379 MAKEMAX (box->Y2, line->Point1.Y + (line->Thickness + 1) / 2);
380 MAKEMAX (box->X2, line->Point2.X + (line->Thickness + 1) / 2);
381 MAKEMAX (box->Y2, line->Point2.Y + (line->Thickness + 1) / 2);
383 END_LOOP;
384 ARC_LOOP (Element);
386 SetArcBoundingBox (arc);
387 MAKEMIN (box->X1, arc->BoundingBox.X1);
388 MAKEMIN (box->Y1, arc->BoundingBox.Y1);
389 MAKEMAX (box->X2, arc->BoundingBox.X2);
390 MAKEMAX (box->Y2, arc->BoundingBox.Y2);
392 END_LOOP;
393 *vbox = *box;
394 PIN_LOOP (Element);
396 if (Data && Data->pin_tree)
397 r_delete_entry (Data->pin_tree, (BoxType *) pin);
398 SetPinBoundingBox (pin);
399 if (Data)
401 if (!Data->pin_tree)
402 Data->pin_tree = r_create_tree (NULL, 0, 0);
403 r_insert_entry (Data->pin_tree, (BoxType *) pin, 0);
405 MAKEMIN (box->X1, pin->BoundingBox.X1);
406 MAKEMIN (box->Y1, pin->BoundingBox.Y1);
407 MAKEMAX (box->X2, pin->BoundingBox.X2);
408 MAKEMAX (box->Y2, pin->BoundingBox.Y2);
409 MAKEMIN (vbox->X1, pin->X - pin->Thickness / 2);
410 MAKEMIN (vbox->Y1, pin->Y - pin->Thickness / 2);
411 MAKEMAX (vbox->X2, pin->X + pin->Thickness / 2);
412 MAKEMAX (vbox->Y2, pin->Y + pin->Thickness / 2);
414 END_LOOP;
415 PAD_LOOP (Element);
417 if (Data && Data->pad_tree)
418 r_delete_entry (Data->pad_tree, (BoxType *) pad);
419 SetPadBoundingBox (pad);
420 if (Data)
422 if (!Data->pad_tree)
423 Data->pad_tree = r_create_tree (NULL, 0, 0);
424 r_insert_entry (Data->pad_tree, (BoxType *) pad, 0);
426 MAKEMIN (box->X1, pad->BoundingBox.X1);
427 MAKEMIN (box->Y1, pad->BoundingBox.Y1);
428 MAKEMAX (box->X2, pad->BoundingBox.X2);
429 MAKEMAX (box->Y2, pad->BoundingBox.Y2);
430 MAKEMIN (vbox->X1,
431 MIN (pad->Point1.X, pad->Point2.X) - pad->Thickness / 2);
432 MAKEMIN (vbox->Y1,
433 MIN (pad->Point1.Y, pad->Point2.Y) - pad->Thickness / 2);
434 MAKEMAX (vbox->X2,
435 MAX (pad->Point1.X, pad->Point2.X) + pad->Thickness / 2);
436 MAKEMAX (vbox->Y2,
437 MAX (pad->Point1.Y, pad->Point2.Y) + pad->Thickness / 2);
439 END_LOOP;
440 /* now we set the EDGE2FLAG of the pad if Point2
441 * is closer to the outside edge than Point1
443 PAD_LOOP (Element);
445 if (pad->Point1.Y == pad->Point2.Y)
447 /* horizontal pad */
448 if (box->X2 - pad->Point2.X < pad->Point1.X - box->X1)
449 SET_FLAG (EDGE2FLAG, pad);
450 else
451 CLEAR_FLAG (EDGE2FLAG, pad);
453 else
455 /* vertical pad */
456 if (box->Y2 - pad->Point2.Y < pad->Point1.Y - box->Y1)
457 SET_FLAG (EDGE2FLAG, pad);
458 else
459 CLEAR_FLAG (EDGE2FLAG, pad);
462 END_LOOP;
464 /* mark pins with component orientation */
465 if ((box->X2 - box->X1) > (box->Y2 - box->Y1))
467 PIN_LOOP (Element);
469 SET_FLAG (EDGE2FLAG, pin);
471 END_LOOP;
473 else
475 PIN_LOOP (Element);
477 CLEAR_FLAG (EDGE2FLAG, pin);
479 END_LOOP;
481 close_box(box);
482 close_box(vbox);
483 if (Data && !Data->element_tree)
484 Data->element_tree = r_create_tree (NULL, 0, 0);
485 if (Data)
486 r_insert_entry (Data->element_tree, box, 0);
489 /* ---------------------------------------------------------------------------
490 * creates the bounding box of a text object
492 void
493 SetTextBoundingBox (FontType *FontPtr, TextType *Text)
495 SymbolType *symbol = FontPtr->Symbol;
496 unsigned char *s = (unsigned char *) Text->TextString;
497 int i;
498 int space;
500 Coord minx, miny, maxx, maxy, tx;
501 Coord min_final_radius;
502 Coord min_unscaled_radius;
503 bool first_time = true;
505 minx = miny = maxx = maxy = tx = 0;
507 /* Calculate the bounding box based on the larger of the thicknesses
508 * the text might clamped at on silk or copper layers.
510 min_final_radius = MAX (PCB->minWid, PCB->minSlk) / 2;
512 /* Pre-adjust the line radius for the fact we are initially computing the
513 * bounds of the un-scaled text, and the thickness clamping applies to
514 * scaled text.
516 min_unscaled_radius = UNSCALE_TEXT (min_final_radius, Text->Scale);
518 /* calculate size of the bounding box */
519 for (; s && *s; s++)
521 if (*s <= MAX_FONTPOSITION && symbol[*s].Valid)
523 LineType *line = symbol[*s].Line;
524 for (i = 0; i < symbol[*s].LineN; line++, i++)
526 /* Clamp the width of text lines at the minimum thickness.
527 * NB: Divide 4 in thickness calculation is comprised of a factor
528 * of 1/2 to get a radius from the center-line, and a factor
529 * of 1/2 because some stupid reason we render our glyphs
530 * at half their defined stroke-width.
532 Coord unscaled_radius = MAX (min_unscaled_radius, line->Thickness / 4);
534 if (first_time)
536 minx = maxx = line->Point1.X;
537 miny = maxy = line->Point1.Y;
538 first_time = false;
541 minx = MIN (minx, line->Point1.X - unscaled_radius + tx);
542 miny = MIN (miny, line->Point1.Y - unscaled_radius);
543 minx = MIN (minx, line->Point2.X - unscaled_radius + tx);
544 miny = MIN (miny, line->Point2.Y - unscaled_radius);
545 maxx = MAX (maxx, line->Point1.X + unscaled_radius + tx);
546 maxy = MAX (maxy, line->Point1.Y + unscaled_radius);
547 maxx = MAX (maxx, line->Point2.X + unscaled_radius + tx);
548 maxy = MAX (maxy, line->Point2.Y + unscaled_radius);
550 space = symbol[*s].Delta;
552 else
554 BoxType *ds = &FontPtr->DefaultSymbol;
555 Coord w = ds->X2 - ds->X1;
557 minx = MIN (minx, ds->X1 + tx);
558 miny = MIN (miny, ds->Y1);
559 minx = MIN (minx, ds->X2 + tx);
560 miny = MIN (miny, ds->Y2);
561 maxx = MAX (maxx, ds->X1 + tx);
562 maxy = MAX (maxy, ds->Y1);
563 maxx = MAX (maxx, ds->X2 + tx);
564 maxy = MAX (maxy, ds->Y2);
566 space = w / 5;
568 tx += symbol[*s].Width + space;
571 /* scale values */
572 minx = SCALE_TEXT (minx, Text->Scale);
573 miny = SCALE_TEXT (miny, Text->Scale);
574 maxx = SCALE_TEXT (maxx, Text->Scale);
575 maxy = SCALE_TEXT (maxy, Text->Scale);
577 /* set upper-left and lower-right corner;
578 * swap coordinates if necessary (origin is already in 'swapped')
579 * and rotate box
582 if (TEST_FLAG (ONSOLDERFLAG, Text))
584 Text->BoundingBox.X1 = Text->X + minx;
585 Text->BoundingBox.Y1 = Text->Y - miny;
586 Text->BoundingBox.X2 = Text->X + maxx;
587 Text->BoundingBox.Y2 = Text->Y - maxy;
588 RotateBoxLowLevel (&Text->BoundingBox, Text->X, Text->Y,
589 (4 - Text->Direction) & 0x03);
591 else
593 Text->BoundingBox.X1 = Text->X + minx;
594 Text->BoundingBox.Y1 = Text->Y + miny;
595 Text->BoundingBox.X2 = Text->X + maxx;
596 Text->BoundingBox.Y2 = Text->Y + maxy;
597 RotateBoxLowLevel (&Text->BoundingBox,
598 Text->X, Text->Y, Text->Direction);
601 /* the bounding box covers the extent of influence
602 * so it must include the clearance values too
604 Text->BoundingBox.X1 -= PCB->Bloat;
605 Text->BoundingBox.Y1 -= PCB->Bloat;
606 Text->BoundingBox.X2 += PCB->Bloat;
607 Text->BoundingBox.Y2 += PCB->Bloat;
608 close_box(&Text->BoundingBox);
611 /* ---------------------------------------------------------------------------
612 * returns true if data area is empty
614 bool
615 IsDataEmpty (DataType *Data)
617 bool hasNoObjects;
618 Cardinal i;
620 hasNoObjects = (Data->ViaN == 0);
621 hasNoObjects &= (Data->ElementN == 0);
622 for (i = 0; i < max_copper_layer + 2; i++)
623 hasNoObjects = hasNoObjects &&
624 Data->Layer[i].LineN == 0 &&
625 Data->Layer[i].ArcN == 0 &&
626 Data->Layer[i].TextN == 0 && Data->Layer[i].PolygonN == 0;
627 return (hasNoObjects);
631 FlagIsDataEmpty (int parm)
633 int i = IsDataEmpty (PCB->Data);
634 return parm ? !i : i;
637 /* FLAG(DataEmpty,FlagIsDataEmpty,0) */
638 /* FLAG(DataNonEmpty,FlagIsDataEmpty,1) */
640 bool
641 IsLayerEmpty (LayerType *layer)
643 return (layer->LineN == 0
644 && layer->TextN == 0
645 && layer->PolygonN == 0
646 && layer->ArcN == 0);
649 bool
650 IsLayerNumEmpty (int num)
652 return IsLayerEmpty (PCB->Data->Layer+num);
655 bool
656 IsLayerGroupEmpty (int num)
658 int i;
659 for (i=0; i<PCB->LayerGroups.Number[num]; i++)
660 if (!IsLayerNumEmpty (PCB->LayerGroups.Entries[num][i]))
661 return false;
662 return true;
665 bool
666 IsPasteEmpty (int side)
668 bool paste_empty = true;
669 ALLPAD_LOOP (PCB->Data);
671 if (ON_SIDE (pad, side) && !TEST_FLAG (NOPASTEFLAG, pad) && pad->Mask > 0)
673 paste_empty = false;
674 break;
677 ENDALL_LOOP;
678 return paste_empty;
682 typedef struct
684 int nplated;
685 int nunplated;
686 } HoleCountStruct;
688 static int
689 hole_counting_callback (const BoxType * b, void *cl)
691 PinType *pin = (PinType *) b;
692 HoleCountStruct *hcs = (HoleCountStruct *) cl;
693 if (TEST_FLAG (HOLEFLAG, pin))
694 hcs->nunplated++;
695 else
696 hcs->nplated++;
697 return 1;
700 /* ---------------------------------------------------------------------------
701 * counts the number of plated and unplated holes in the design within
702 * a given area of the board. To count for the whole board, pass NULL
703 * within_area.
705 void
706 CountHoles (int *plated, int *unplated, const BoxType *within_area)
708 HoleCountStruct hcs = {0, 0};
710 r_search (PCB->Data->pin_tree, within_area, NULL, hole_counting_callback, &hcs);
711 r_search (PCB->Data->via_tree, within_area, NULL, hole_counting_callback, &hcs);
713 if (plated != NULL) *plated = hcs.nplated;
714 if (unplated != NULL) *unplated = hcs.nunplated;
718 /* ---------------------------------------------------------------------------
719 * gets minimum and maximum coordinates
720 * returns NULL if layout is empty
722 BoxType *
723 GetDataBoundingBox (DataType *Data)
725 static BoxType box;
726 /* FIX ME: use r_search to do this much faster */
728 /* preset identifiers with highest and lowest possible values */
729 box.X1 = box.Y1 = MAX_COORD;
730 box.X2 = box.Y2 = -MAX_COORD;
732 /* now scan for the lowest/highest X and Y coordinate */
733 VIA_LOOP (Data);
735 box.X1 = MIN (box.X1, via->X - via->Thickness / 2);
736 box.Y1 = MIN (box.Y1, via->Y - via->Thickness / 2);
737 box.X2 = MAX (box.X2, via->X + via->Thickness / 2);
738 box.Y2 = MAX (box.Y2, via->Y + via->Thickness / 2);
740 END_LOOP;
741 ELEMENT_LOOP (Data);
743 box.X1 = MIN (box.X1, element->BoundingBox.X1);
744 box.Y1 = MIN (box.Y1, element->BoundingBox.Y1);
745 box.X2 = MAX (box.X2, element->BoundingBox.X2);
746 box.Y2 = MAX (box.Y2, element->BoundingBox.Y2);
748 TextType *text = &NAMEONPCB_TEXT (element);
749 box.X1 = MIN (box.X1, text->BoundingBox.X1);
750 box.Y1 = MIN (box.Y1, text->BoundingBox.Y1);
751 box.X2 = MAX (box.X2, text->BoundingBox.X2);
752 box.Y2 = MAX (box.Y2, text->BoundingBox.Y2);
755 END_LOOP;
756 ALLLINE_LOOP (Data);
758 box.X1 = MIN (box.X1, line->Point1.X - line->Thickness / 2);
759 box.Y1 = MIN (box.Y1, line->Point1.Y - line->Thickness / 2);
760 box.X1 = MIN (box.X1, line->Point2.X - line->Thickness / 2);
761 box.Y1 = MIN (box.Y1, line->Point2.Y - line->Thickness / 2);
762 box.X2 = MAX (box.X2, line->Point1.X + line->Thickness / 2);
763 box.Y2 = MAX (box.Y2, line->Point1.Y + line->Thickness / 2);
764 box.X2 = MAX (box.X2, line->Point2.X + line->Thickness / 2);
765 box.Y2 = MAX (box.Y2, line->Point2.Y + line->Thickness / 2);
767 ENDALL_LOOP;
768 ALLARC_LOOP (Data);
770 box.X1 = MIN (box.X1, arc->BoundingBox.X1);
771 box.Y1 = MIN (box.Y1, arc->BoundingBox.Y1);
772 box.X2 = MAX (box.X2, arc->BoundingBox.X2);
773 box.Y2 = MAX (box.Y2, arc->BoundingBox.Y2);
775 ENDALL_LOOP;
776 ALLTEXT_LOOP (Data);
778 box.X1 = MIN (box.X1, text->BoundingBox.X1);
779 box.Y1 = MIN (box.Y1, text->BoundingBox.Y1);
780 box.X2 = MAX (box.X2, text->BoundingBox.X2);
781 box.Y2 = MAX (box.Y2, text->BoundingBox.Y2);
783 ENDALL_LOOP;
784 ALLPOLYGON_LOOP (Data);
786 box.X1 = MIN (box.X1, polygon->BoundingBox.X1);
787 box.Y1 = MIN (box.Y1, polygon->BoundingBox.Y1);
788 box.X2 = MAX (box.X2, polygon->BoundingBox.X2);
789 box.Y2 = MAX (box.Y2, polygon->BoundingBox.Y2);
791 ENDALL_LOOP;
792 return (IsDataEmpty (Data) ? NULL : &box);
795 /* ---------------------------------------------------------------------------
796 * centers the displayed PCB around the specified point (X,Y)
798 void
799 CenterDisplay (Coord X, Coord Y)
801 Coord save_grid = PCB->Grid;
802 PCB->Grid = 1;
803 if (MoveCrosshairAbsolute (X, Y))
804 notify_crosshair_change (true);
805 gui->set_crosshair (Crosshair.X, Crosshair.Y, HID_SC_WARP_POINTER);
806 PCB->Grid = save_grid;
809 /* ---------------------------------------------------------------------------
810 * transforms symbol coordinates so that the left edge of each symbol
811 * is at the zero position. The y coordinates are moved so that min(y) = 0
814 void
815 SetFontInfo (FontType *Ptr)
817 Cardinal i, j;
818 SymbolType *symbol;
819 LineType *line;
820 Coord totalminy = MAX_COORD;
822 /* calculate cell with and height (is at least DEFAULT_CELLSIZE)
823 * maximum cell width and height
824 * minimum x and y position of all lines
826 Ptr->MaxWidth = DEFAULT_CELLSIZE;
827 Ptr->MaxHeight = DEFAULT_CELLSIZE;
828 for (i = 0, symbol = Ptr->Symbol; i <= MAX_FONTPOSITION; i++, symbol++)
830 Coord minx, miny, maxx, maxy;
832 /* next one if the index isn't used or symbol is empty (SPACE) */
833 if (!symbol->Valid || !symbol->LineN)
834 continue;
836 minx = miny = MAX_COORD;
837 maxx = maxy = 0;
838 for (line = symbol->Line, j = symbol->LineN; j; j--, line++)
840 minx = MIN (minx, line->Point1.X);
841 miny = MIN (miny, line->Point1.Y);
842 minx = MIN (minx, line->Point2.X);
843 miny = MIN (miny, line->Point2.Y);
844 maxx = MAX (maxx, line->Point1.X);
845 maxy = MAX (maxy, line->Point1.Y);
846 maxx = MAX (maxx, line->Point2.X);
847 maxy = MAX (maxy, line->Point2.Y);
850 /* move symbol to left edge */
851 for (line = symbol->Line, j = symbol->LineN; j; j--, line++)
852 MOVE_LINE_LOWLEVEL (line, -minx, 0);
854 /* set symbol bounding box with a minimum cell size of (1,1) */
855 symbol->Width = maxx - minx + 1;
856 symbol->Height = maxy + 1;
858 /* check total min/max */
859 Ptr->MaxWidth = MAX (Ptr->MaxWidth, symbol->Width);
860 Ptr->MaxHeight = MAX (Ptr->MaxHeight, symbol->Height);
861 totalminy = MIN (totalminy, miny);
864 /* move coordinate system to the upper edge (lowest y on screen) */
865 for (i = 0, symbol = Ptr->Symbol; i <= MAX_FONTPOSITION; i++, symbol++)
866 if (symbol->Valid)
868 symbol->Height -= totalminy;
869 for (line = symbol->Line, j = symbol->LineN; j; j--, line++)
870 MOVE_LINE_LOWLEVEL (line, 0, -totalminy);
873 /* setup the box for the default symbol */
874 Ptr->DefaultSymbol.X1 = Ptr->DefaultSymbol.Y1 = 0;
875 Ptr->DefaultSymbol.X2 = Ptr->DefaultSymbol.X1 + Ptr->MaxWidth;
876 Ptr->DefaultSymbol.Y2 = Ptr->DefaultSymbol.Y1 + Ptr->MaxHeight;
879 static Coord
880 GetNum (char **s, const char *default_unit)
882 /* Read value */
883 Coord ret_val = GetValueEx (*s, NULL, NULL, NULL, default_unit);
884 /* Advance pointer */
885 while(isalnum(**s) || **s == '.')
886 (*s)++;
887 return ret_val;
890 /*! \brief Serializes the route style list
891 * \par Function Description
892 * Right now n_styles should always be set to NUM_STYLES,
893 * since that is the number of route styles ParseRouteString()
894 * expects to parse.
896 char *
897 make_route_string (RouteStyleType rs[], int n_styles)
899 GString *str = g_string_new ("");
900 gint i;
902 for (i = 0; i < n_styles; ++i)
904 char *r_string = pcb_g_strdup_printf ("%s,%mc,%mc,%mc,%mc", rs[i].Name,
905 rs[i].Thick, rs[i].Diameter,
906 rs[i].Hole, rs[i].Keepaway);
907 if (i > 0)
908 g_string_append_c (str, ':');
909 g_string_append (str, r_string);
911 return g_string_free (str, FALSE);
914 /* ----------------------------------------------------------------------
915 * parses the routes definition string which is a colon separated list of
916 * comma separated Name, Dimension, Dimension, Dimension, Dimension
917 * e.g. Signal,20,40,20,10:Power,40,60,28,10:...
920 ParseRouteString (char *s, RouteStyleType *routeStyle, const char *default_unit)
922 int i, style;
923 char Name[256];
925 memset (routeStyle, 0, NUM_STYLES * sizeof (RouteStyleType));
926 for (style = 0; style < NUM_STYLES; style++, routeStyle++)
928 while (*s && isspace ((int) *s))
929 s++;
930 for (i = 0; *s && *s != ','; i++)
931 Name[i] = *s++;
932 Name[i] = '\0';
933 routeStyle->Name = strdup (Name);
934 if (!isdigit ((int) *++s))
935 goto error;
936 routeStyle->Thick = GetNum (&s, default_unit);
937 while (*s && isspace ((int) *s))
938 s++;
939 if (*s++ != ',')
940 goto error;
941 while (*s && isspace ((int) *s))
942 s++;
943 if (!isdigit ((int) *s))
944 goto error;
945 routeStyle->Diameter = GetNum (&s, default_unit);
946 while (*s && isspace ((int) *s))
947 s++;
948 if (*s++ != ',')
949 goto error;
950 while (*s && isspace ((int) *s))
951 s++;
952 if (!isdigit ((int) *s))
953 goto error;
954 routeStyle->Hole = GetNum (&s, default_unit);
955 /* for backwards-compatibility, we use a 10-mil default
956 * for styles which omit the keepaway specification. */
957 if (*s != ',')
958 routeStyle->Keepaway = MIL_TO_COORD(10);
959 else
961 s++;
962 while (*s && isspace ((int) *s))
963 s++;
964 if (!isdigit ((int) *s))
965 goto error;
966 routeStyle->Keepaway = GetNum (&s, default_unit);
967 while (*s && isspace ((int) *s))
968 s++;
970 if (style < NUM_STYLES - 1)
972 while (*s && isspace ((int) *s))
973 s++;
974 if (*s++ != ':')
975 goto error;
978 return (0);
980 error:
981 memset (routeStyle, 0, NUM_STYLES * sizeof (RouteStyleType));
982 return (1);
985 /* ----------------------------------------------------------------------
986 * parses the group definition string which is a colon separated list of
987 * comma separated layer numbers (1,2,b:4,6,8,t)
990 ParseGroupString (char *s, LayerGroupType *LayerGroup, int LayerN)
992 int group, member, layer;
993 bool c_set = false, /* flags for the two special layers to */
994 s_set = false; /* provide a default setting for old formats */
995 int groupnum[MAX_LAYER + 2];
997 /* clear struct */
998 memset (LayerGroup, 0, sizeof (LayerGroupType));
1000 /* Clear assignments */
1001 for (layer = 0; layer < MAX_LAYER + 2; layer++)
1002 groupnum[layer] = -1;
1004 /* loop over all groups */
1005 for (group = 0; s && *s && group < LayerN; group++)
1007 while (*s && isspace ((int) *s))
1008 s++;
1010 /* loop over all group members */
1011 for (member = 0; *s; s++)
1013 /* ignore white spaces and get layernumber */
1014 while (*s && isspace ((int) *s))
1015 s++;
1016 switch (*s)
1018 case 'c':
1019 case 'C':
1020 case 't':
1021 case 'T':
1022 layer = LayerN + COMPONENT_LAYER;
1023 c_set = true;
1024 break;
1026 case 's':
1027 case 'S':
1028 case 'b':
1029 case 'B':
1030 layer = LayerN + SOLDER_LAYER;
1031 s_set = true;
1032 break;
1034 default:
1035 if (!isdigit ((int) *s))
1036 goto error;
1037 layer = atoi (s) - 1;
1038 break;
1040 if (layer > LayerN + MAX (SOLDER_LAYER, COMPONENT_LAYER) ||
1041 member >= LayerN + 1)
1042 goto error;
1043 groupnum[layer] = group;
1044 LayerGroup->Entries[group][member++] = layer;
1045 while (*++s && isdigit ((int) *s));
1047 /* ignore white spaces and check for separator */
1048 while (*s && isspace ((int) *s))
1049 s++;
1050 if (!*s || *s == ':')
1051 break;
1052 if (*s != ',')
1053 goto error;
1055 LayerGroup->Number[group] = member;
1056 if (*s == ':')
1057 s++;
1059 if (!s_set)
1060 LayerGroup->Entries[SOLDER_LAYER][LayerGroup->Number[SOLDER_LAYER]++] =
1061 LayerN + SOLDER_LAYER;
1062 if (!c_set)
1063 LayerGroup->
1064 Entries[COMPONENT_LAYER][LayerGroup->Number[COMPONENT_LAYER]++] =
1065 LayerN + COMPONENT_LAYER;
1067 for (layer = 0; layer < LayerN && group < LayerN; layer++)
1068 if (groupnum[layer] == -1)
1070 LayerGroup->Entries[group][0] = layer;
1071 LayerGroup->Number[group] = 1;
1072 group++;
1074 return (0);
1076 /* reset structure on error */
1077 error:
1078 memset (LayerGroup, 0, sizeof (LayerGroupType));
1079 return (1);
1082 /* ---------------------------------------------------------------------------
1083 * quits application
1085 void
1086 QuitApplication (void)
1089 * save data if necessary. It not needed, then don't trigger EmergencySave
1090 * via our atexit() registering of EmergencySave(). We presumeably wanted to
1091 * exit here and thus it is not an emergency.
1093 if (PCB->Changed && Settings.SaveInTMP)
1094 EmergencySave ();
1095 else
1096 DisableEmergencySave ();
1098 /* Free up memory allocated to the PCB. Why bother when we're about to exit ?
1099 * Because it removes some false positives from heap bug detectors such as
1100 * lib dmalloc.
1102 FreePCBMemory(PCB);
1104 exit (0);
1107 /* ---------------------------------------------------------------------------
1108 * creates a filename from a template
1109 * %f is replaced by the filename
1110 * %p by the searchpath
1112 char *
1113 EvaluateFilename (char *Template, char *Path, char *Filename, char *Parameter)
1115 static DynamicStringType command;
1116 char *p;
1118 if (Settings.verbose)
1120 printf ("EvaluateFilename:\n");
1121 printf ("\tTemplate: \033[33m%s\033[0m\n", Template);
1122 printf ("\tPath: \033[33m%s\033[0m\n", Path);
1123 printf ("\tFilename: \033[33m%s\033[0m\n", Filename);
1124 printf ("\tParameter: \033[33m%s\033[0m\n", Parameter);
1127 DSClearString (&command);
1129 for (p = Template; p && *p; p++)
1131 /* copy character or add string to command */
1132 if (*p == '%'
1133 && (*(p + 1) == 'f' || *(p + 1) == 'p' || *(p + 1) == 'a'))
1134 switch (*(++p))
1136 case 'a':
1137 DSAddString (&command, Parameter);
1138 break;
1139 case 'f':
1140 DSAddString (&command, Filename);
1141 break;
1142 case 'p':
1143 DSAddString (&command, Path);
1144 break;
1146 else
1147 DSAddCharacter (&command, *p);
1149 DSAddCharacter (&command, '\0');
1150 if (Settings.verbose)
1151 printf ("EvaluateFilename: \033[32m%s\033[0m\n", command.Data);
1153 return strdup (command.Data);
1156 /* ---------------------------------------------------------------------------
1157 * concatenates directory and filename if directory != NULL,
1158 * expands them with a shell and returns the found name(s) or NULL
1160 char *
1161 ExpandFilename (char *Dirname, char *Filename)
1163 static DynamicStringType answer;
1164 char *command;
1165 FILE *pipe;
1166 int c;
1168 /* allocate memory for commandline and build it */
1169 DSClearString (&answer);
1170 if (Dirname)
1172 command = (char *)calloc (strlen (Filename) + strlen (Dirname) + 7,
1173 sizeof (char));
1174 sprintf (command, "echo %s/%s", Dirname, Filename);
1176 else
1178 command = (char *)calloc (strlen (Filename) + 6, sizeof (char));
1179 sprintf (command, "echo %s", Filename);
1182 /* execute it with shell */
1183 if ((pipe = popen (command, "r")) != NULL)
1185 /* discard all but the first returned line */
1186 for (;;)
1188 if ((c = fgetc (pipe)) == EOF || c == '\n' || c == '\r')
1189 break;
1190 else
1191 DSAddCharacter (&answer, c);
1194 free (command);
1195 return (pclose (pipe) ? NULL : answer.Data);
1198 /* couldn't be expanded by the shell */
1199 PopenErrorMessage (command);
1200 free (command);
1201 return (NULL);
1205 /* ---------------------------------------------------------------------------
1206 * returns the layer number for the passed pointer
1209 GetLayerNumber (DataType *Data, LayerType *Layer)
1211 int i;
1213 for (i = 0; i < MAX_LAYER + 2; i++)
1214 if (Layer == &Data->Layer[i])
1215 break;
1216 return (i);
1219 /* ---------------------------------------------------------------------------
1220 * move layer (number is passed in) to top of layerstack
1222 static void
1223 PushOnTopOfLayerStack (int NewTop)
1225 int i;
1227 /* ignore silk layers */
1228 if (NewTop < max_copper_layer)
1230 /* first find position of passed one */
1231 for (i = 0; i < max_copper_layer; i++)
1232 if (LayerStack[i] == NewTop)
1233 break;
1235 /* bring this element to the top of the stack */
1236 for (; i; i--)
1237 LayerStack[i] = LayerStack[i - 1];
1238 LayerStack[0] = NewTop;
1243 /* ----------------------------------------------------------------------
1244 * changes the visibility of all layers in a group
1245 * returns the number of changed layers
1248 ChangeGroupVisibility (int Layer, bool On, bool ChangeStackOrder)
1250 int group, i, changed = 1; /* at least the current layer changes */
1252 /* Warning: these special case values must agree with what gui-top-window.c
1253 | thinks the are.
1256 if (Settings.verbose)
1257 printf ("ChangeGroupVisibility(Layer=%d, On=%d, ChangeStackOrder=%d)\n",
1258 Layer, On, ChangeStackOrder);
1260 /* decrement 'i' to keep stack in order of layergroup */
1261 if ((group = GetGroupOfLayer (Layer)) < max_group)
1262 for (i = PCB->LayerGroups.Number[group]; i;)
1264 int layer = PCB->LayerGroups.Entries[group][--i];
1266 /* don't count the passed member of the group */
1267 if (layer != Layer && layer < max_copper_layer)
1269 PCB->Data->Layer[layer].On = On;
1271 /* push layer on top of stack if switched on */
1272 if (On && ChangeStackOrder)
1273 PushOnTopOfLayerStack (layer);
1274 changed++;
1278 /* change at least the passed layer */
1279 PCB->Data->Layer[Layer].On = On;
1280 if (On && ChangeStackOrder)
1281 PushOnTopOfLayerStack (Layer);
1283 /* update control panel and exit */
1284 hid_action ("LayersChanged");
1285 return (changed);
1288 /* ----------------------------------------------------------------------
1289 * Given a string description of a layer stack, adjust the layer stack
1290 * to correspond.
1293 void
1294 LayerStringToLayerStack (char *s)
1296 static int listed_layers = 0;
1297 int l = strlen (s);
1298 char **args;
1299 int i, argn, lno;
1300 int prev_sep = 1;
1302 s = strdup (s);
1303 args = (char **) malloc (l * sizeof (char *));
1304 argn = 0;
1306 for (i=0; i<l; i++)
1308 switch (s[i])
1310 case ' ':
1311 case '\t':
1312 case ',':
1313 case ';':
1314 case ':':
1315 prev_sep = 1;
1316 s[i] = '\0';
1317 break;
1318 default:
1319 if (prev_sep)
1320 args[argn++] = s+i;
1321 prev_sep = 0;
1322 break;
1326 for (i = 0; i < max_copper_layer + 2; i++)
1328 if (i < max_copper_layer)
1329 LayerStack[i] = i;
1330 PCB->Data->Layer[i].On = false;
1332 PCB->ElementOn = false;
1333 PCB->InvisibleObjectsOn = false;
1334 PCB->PinOn = false;
1335 PCB->ViaOn = false;
1336 PCB->RatOn = false;
1337 CLEAR_FLAG (SHOWMASKFLAG, PCB);
1338 Settings.ShowSolderSide = 0;
1340 for (i=argn-1; i>=0; i--)
1342 if (strcasecmp (args[i], "rats") == 0)
1343 PCB->RatOn = true;
1344 else if (strcasecmp (args[i], "invisible") == 0)
1345 PCB->InvisibleObjectsOn = true;
1346 else if (strcasecmp (args[i], "pins") == 0)
1347 PCB->PinOn = true;
1348 else if (strcasecmp (args[i], "vias") == 0)
1349 PCB->ViaOn = true;
1350 else if (strcasecmp (args[i], "elements") == 0
1351 || strcasecmp (args[i], "silk") == 0)
1352 PCB->ElementOn = true;
1353 else if (strcasecmp (args[i], "mask") == 0)
1354 SET_FLAG (SHOWMASKFLAG, PCB);
1355 else if (strcasecmp (args[i], "solderside") == 0)
1356 Settings.ShowSolderSide = 1;
1357 else if (isdigit ((int) args[i][0]))
1359 lno = atoi (args[i]);
1360 ChangeGroupVisibility (lno, true, true);
1362 else
1364 int found = 0;
1365 for (lno = 0; lno < max_copper_layer; lno++)
1366 if (strcasecmp (args[i], PCB->Data->Layer[lno].Name) == 0)
1368 ChangeGroupVisibility (lno, true, true);
1369 found = 1;
1370 break;
1372 if (!found)
1374 fprintf(stderr, "Warning: layer \"%s\" not known\n", args[i]);
1375 if (!listed_layers)
1377 fprintf (stderr, "Named layers in this board are:\n");
1378 listed_layers = 1;
1379 for (lno=0; lno < max_copper_layer; lno ++)
1380 fprintf(stderr, "\t%s\n", PCB->Data->Layer[lno].Name);
1381 fprintf(stderr, "Also: component, solder, rats, invisible, pins, vias, elements or silk, mask, solderside.\n");
1388 /* ----------------------------------------------------------------------
1389 * lookup the group to which a layer belongs to
1390 * returns max_group if no group is found, or is
1391 * passed Layer is equal to max_copper_layer
1394 GetGroupOfLayer (int Layer)
1396 int group, i;
1398 if (Layer == max_copper_layer)
1399 return max_group;
1400 for (group = 0; group < max_group; group++)
1401 for (i = 0; i < PCB->LayerGroups.Number[group]; i++)
1402 if (PCB->LayerGroups.Entries[group][i] == Layer)
1403 return (group);
1404 return max_group;
1408 /* ---------------------------------------------------------------------------
1409 * returns the layergroup number for the passed pointer
1412 GetLayerGroupNumberByPointer (LayerType *Layer)
1414 return (GetLayerGroupNumberByNumber (GetLayerNumber (PCB->Data, Layer)));
1417 /* ---------------------------------------------------------------------------
1418 * returns the layergroup number for the passed layernumber
1421 GetLayerGroupNumberByNumber (Cardinal Layer)
1423 int group, entry;
1425 for (group = 0; group < max_group; group++)
1426 for (entry = 0; entry < PCB->LayerGroups.Number[group]; entry++)
1427 if (PCB->LayerGroups.Entries[group][entry] == Layer)
1428 return (group);
1430 /* since every layer belongs to a group it is safe to return
1431 * the value without boundary checking
1433 return (group);
1436 /* ---------------------------------------------------------------------------
1437 * returns a pointer to an objects bounding box;
1438 * data is valid until the routine is called again
1440 BoxType *
1441 GetObjectBoundingBox (int Type, void *Ptr1, void *Ptr2, void *Ptr3)
1443 switch (Type)
1445 case LINE_TYPE:
1446 case ARC_TYPE:
1447 case TEXT_TYPE:
1448 case POLYGON_TYPE:
1449 case PAD_TYPE:
1450 case PIN_TYPE:
1451 case ELEMENTNAME_TYPE:
1452 return (BoxType *)Ptr2;
1453 case VIA_TYPE:
1454 case ELEMENT_TYPE:
1455 return (BoxType *)Ptr1;
1456 case POLYGONPOINT_TYPE:
1457 case LINEPOINT_TYPE:
1458 return (BoxType *)Ptr3;
1459 default:
1460 Message ("Request for bounding box of unsupported type %d\n", Type);
1461 return (BoxType *)Ptr2;
1465 /* ---------------------------------------------------------------------------
1466 * computes the bounding box of an arc
1468 void
1469 SetArcBoundingBox (ArcType *Arc)
1471 double ca1, ca2, sa1, sa2;
1472 double minx, maxx, miny, maxy;
1473 Angle ang1, ang2;
1474 Coord width;
1476 /* first put angles into standard form:
1477 * ang1 < ang2, both angles between 0 and 720 */
1478 Arc->Delta = CLAMP (Arc->Delta, -360, 360);
1480 if (Arc->Delta > 0)
1482 ang1 = NormalizeAngle (Arc->StartAngle);
1483 ang2 = NormalizeAngle (Arc->StartAngle + Arc->Delta);
1485 else
1487 ang1 = NormalizeAngle (Arc->StartAngle + Arc->Delta);
1488 ang2 = NormalizeAngle (Arc->StartAngle);
1490 if (ang1 > ang2)
1491 ang2 += 360;
1492 /* Make sure full circles aren't treated as zero-length arcs */
1493 if (Arc->Delta == 360 || Arc->Delta == -360)
1494 ang2 = ang1 + 360;
1496 /* calculate sines, cosines */
1497 sa1 = sin (M180 * ang1);
1498 ca1 = cos (M180 * ang1);
1499 sa2 = sin (M180 * ang2);
1500 ca2 = cos (M180 * ang2);
1502 minx = MIN (ca1, ca2);
1503 maxx = MAX (ca1, ca2);
1504 miny = MIN (sa1, sa2);
1505 maxy = MAX (sa1, sa2);
1507 /* Check for extreme angles */
1508 if ((ang1 <= 0 && ang2 >= 0) || (ang1 <= 360 && ang2 >= 360)) maxx = 1;
1509 if ((ang1 <= 90 && ang2 >= 90) || (ang1 <= 450 && ang2 >= 450)) maxy = 1;
1510 if ((ang1 <= 180 && ang2 >= 180) || (ang1 <= 540 && ang2 >= 540)) minx = -1;
1511 if ((ang1 <= 270 && ang2 >= 270) || (ang1 <= 630 && ang2 >= 630)) miny = -1;
1513 /* Finally, calcate bounds, converting sane geometry into pcb geometry */
1514 Arc->BoundingBox.X1 = Arc->X - Arc->Width * maxx;
1515 Arc->BoundingBox.X2 = Arc->X - Arc->Width * minx;
1516 Arc->BoundingBox.Y1 = Arc->Y + Arc->Height * miny;
1517 Arc->BoundingBox.Y2 = Arc->Y + Arc->Height * maxy;
1519 width = (Arc->Thickness + Arc->Clearance) / 2;
1521 /* Adjust for our discrete polygon approximation */
1522 width = (double)width * MAX (POLY_CIRC_RADIUS_ADJ, (1.0 + POLY_ARC_MAX_DEVIATION)) + 0.5;
1524 Arc->BoundingBox.X1 -= width;
1525 Arc->BoundingBox.X2 += width;
1526 Arc->BoundingBox.Y1 -= width;
1527 Arc->BoundingBox.Y2 += width;
1528 close_box(&Arc->BoundingBox);
1530 /* Update the arc end-points */
1531 Arc->Point1.X = Arc->X - (double)Arc->Width * ca1;
1532 Arc->Point1.Y = Arc->Y + (double)Arc->Height * sa1;
1533 Arc->Point2.X = Arc->X - (double)Arc->Width * ca2;
1534 Arc->Point2.Y = Arc->Y + (double)Arc->Height * sa2;
1537 /* ---------------------------------------------------------------------------
1538 * resets the layerstack setting
1540 void
1541 ResetStackAndVisibility (void)
1543 int comp_group;
1544 Cardinal i;
1546 for (i = 0; i < max_copper_layer + 2; i++)
1548 if (i < max_copper_layer)
1549 LayerStack[i] = i;
1550 PCB->Data->Layer[i].On = true;
1552 PCB->ElementOn = true;
1553 PCB->InvisibleObjectsOn = true;
1554 PCB->PinOn = true;
1555 PCB->ViaOn = true;
1556 PCB->RatOn = true;
1558 /* Bring the component group to the front and make it active. */
1559 comp_group = GetLayerGroupNumberByNumber (component_silk_layer);
1560 ChangeGroupVisibility (PCB->LayerGroups.Entries[comp_group][0], 1, 1);
1563 /* ---------------------------------------------------------------------------
1564 * saves the layerstack setting
1566 void
1567 SaveStackAndVisibility (void)
1569 Cardinal i;
1570 static bool run = false;
1572 if (run == false)
1574 SavedStack.cnt = 0;
1575 run = true;
1578 if (SavedStack.cnt != 0)
1580 fprintf (stderr,
1581 "SaveStackAndVisibility() layerstack was already saved and not"
1582 "yet restored. cnt = %d\n", SavedStack.cnt);
1585 for (i = 0; i < max_copper_layer + 2; i++)
1587 if (i < max_copper_layer)
1588 SavedStack.LayerStack[i] = LayerStack[i];
1589 SavedStack.LayerOn[i] = PCB->Data->Layer[i].On;
1591 SavedStack.ElementOn = PCB->ElementOn;
1592 SavedStack.InvisibleObjectsOn = PCB->InvisibleObjectsOn;
1593 SavedStack.PinOn = PCB->PinOn;
1594 SavedStack.ViaOn = PCB->ViaOn;
1595 SavedStack.RatOn = PCB->RatOn;
1596 SavedStack.cnt++;
1599 /* ---------------------------------------------------------------------------
1600 * restores the layerstack setting
1602 void
1603 RestoreStackAndVisibility (void)
1605 Cardinal i;
1607 if (SavedStack.cnt == 0)
1609 fprintf (stderr, "RestoreStackAndVisibility() layerstack has not"
1610 " been saved. cnt = %d\n", SavedStack.cnt);
1611 return;
1613 else if (SavedStack.cnt != 1)
1615 fprintf (stderr, "RestoreStackAndVisibility() layerstack save count is"
1616 " wrong. cnt = %d\n", SavedStack.cnt);
1619 for (i = 0; i < max_copper_layer + 2; i++)
1621 if (i < max_copper_layer)
1622 LayerStack[i] = SavedStack.LayerStack[i];
1623 PCB->Data->Layer[i].On = SavedStack.LayerOn[i];
1625 PCB->ElementOn = SavedStack.ElementOn;
1626 PCB->InvisibleObjectsOn = SavedStack.InvisibleObjectsOn;
1627 PCB->PinOn = SavedStack.PinOn;
1628 PCB->ViaOn = SavedStack.ViaOn;
1629 PCB->RatOn = SavedStack.RatOn;
1631 SavedStack.cnt--;
1634 /* ----------------------------------------------------------------------
1635 * returns pointer to current working directory. If 'path' is not
1636 * NULL, then the current working directory is copied to the array
1637 * pointed to by 'path'
1639 char *
1640 GetWorkingDirectory (char *path)
1642 #ifdef HAVE_GETCWD
1643 return getcwd (path, MAXPATHLEN);
1644 #else
1645 /* seems that some BSD releases lack of a prototype for getwd() */
1646 return getwd (path);
1647 #endif
1651 /* ---------------------------------------------------------------------------
1652 * writes a string to the passed file pointer
1653 * some special characters are quoted
1655 void
1656 CreateQuotedString (DynamicStringType *DS, char *S)
1658 DSClearString (DS);
1659 DSAddCharacter (DS, '"');
1660 while (*S)
1662 if (*S == '"' || *S == '\\')
1663 DSAddCharacter (DS, '\\');
1664 DSAddCharacter (DS, *S++);
1666 DSAddCharacter (DS, '"');
1669 BoxType *
1670 GetArcEnds (ArcType *Arc)
1672 static BoxType box;
1673 box.X1 = Arc->X - Arc->Width * cos (Arc->StartAngle * M180);
1674 box.Y1 = Arc->Y + Arc->Height * sin (Arc->StartAngle * M180);
1675 box.X2 = Arc->X - Arc->Width * cos ((Arc->StartAngle + Arc->Delta) * M180);
1676 box.Y2 = Arc->Y + Arc->Height * sin ((Arc->StartAngle + Arc->Delta) * M180);
1677 return &box;
1681 /* doesn't this belong in change.c ?? */
1682 void
1683 ChangeArcAngles (LayerType *Layer, ArcType *a,
1684 Angle new_sa, Angle new_da)
1686 if (new_da >= 360)
1688 new_da = 360;
1689 new_sa = 0;
1691 RestoreToPolygon (PCB->Data, ARC_TYPE, Layer, a);
1692 r_delete_entry (Layer->arc_tree, (BoxType *) a);
1693 AddObjectToChangeAnglesUndoList (ARC_TYPE, a, a, a);
1694 a->StartAngle = new_sa;
1695 a->Delta = new_da;
1696 SetArcBoundingBox (a);
1697 r_insert_entry (Layer->arc_tree, (BoxType *) a, 0);
1698 ClearFromPolygon (PCB->Data, ARC_TYPE, Layer, a);
1701 static char *
1702 BumpName (char *Name)
1704 int num;
1705 char c, *start;
1706 static char temp[256];
1708 start = Name;
1709 /* seek end of string */
1710 while (*Name != 0)
1711 Name++;
1712 /* back up to potential number */
1713 for (Name--; isdigit ((int) *Name); Name--);
1714 Name++;
1715 if (*Name)
1716 num = atoi (Name) + 1;
1717 else
1718 num = 1;
1719 c = *Name;
1720 *Name = 0;
1721 sprintf (temp, "%s%d", start, num);
1722 /* if this is not our string, put back the blown character */
1723 if (start != temp)
1724 *Name = c;
1725 return (temp);
1729 * make a unique name for the name on board
1730 * this can alter the contents of the input string
1732 char *
1733 UniqueElementName (DataType *Data, char *Name)
1735 bool unique = true;
1736 /* null strings are ok */
1737 if (!Name || !*Name)
1738 return (Name);
1740 for (;;)
1742 ELEMENT_LOOP (Data);
1744 if (NAMEONPCB_NAME (element) &&
1745 NSTRCMP (NAMEONPCB_NAME (element), Name) == 0)
1747 Name = BumpName (Name);
1748 unique = false;
1749 break;
1752 END_LOOP;
1753 if (unique)
1754 return (Name);
1755 unique = true;
1759 static void
1760 GetGridLockCoordinates (int type, void *ptr1,
1761 void *ptr2, void *ptr3, Coord * x,
1762 Coord * y)
1764 switch (type)
1766 case VIA_TYPE:
1767 *x = ((PinType *) ptr2)->X;
1768 *y = ((PinType *) ptr2)->Y;
1769 break;
1770 case LINE_TYPE:
1771 *x = ((LineType *) ptr2)->Point1.X;
1772 *y = ((LineType *) ptr2)->Point1.Y;
1773 break;
1774 case TEXT_TYPE:
1775 case ELEMENTNAME_TYPE:
1776 *x = ((TextType *) ptr2)->X;
1777 *y = ((TextType *) ptr2)->Y;
1778 break;
1779 case ELEMENT_TYPE:
1780 *x = ((ElementType *) ptr2)->MarkX;
1781 *y = ((ElementType *) ptr2)->MarkY;
1782 break;
1783 case POLYGON_TYPE:
1784 *x = ((PolygonType *) ptr2)->Points[0].X;
1785 *y = ((PolygonType *) ptr2)->Points[0].Y;
1786 break;
1788 case LINEPOINT_TYPE:
1789 case POLYGONPOINT_TYPE:
1790 *x = ((PointType *) ptr3)->X;
1791 *y = ((PointType *) ptr3)->Y;
1792 break;
1793 case ARC_TYPE:
1795 BoxType *box;
1797 box = GetArcEnds ((ArcType *) ptr2);
1798 *x = box->X1;
1799 *y = box->Y1;
1800 break;
1805 void
1806 AttachForCopy (Coord PlaceX, Coord PlaceY)
1808 BoxType *box;
1809 Coord mx = 0, my = 0;
1811 Crosshair.AttachedObject.RubberbandN = 0;
1812 if (! TEST_FLAG (SNAPPINFLAG, PCB))
1814 /* dither the grab point so that the mark, center, etc
1815 * will end up on a grid coordinate
1817 GetGridLockCoordinates (Crosshair.AttachedObject.Type,
1818 Crosshair.AttachedObject.Ptr1,
1819 Crosshair.AttachedObject.Ptr2,
1820 Crosshair.AttachedObject.Ptr3, &mx, &my);
1821 mx = GridFit (mx, PCB->Grid, PCB->GridOffsetX) - mx;
1822 my = GridFit (my, PCB->Grid, PCB->GridOffsetY) - my;
1824 Crosshair.AttachedObject.X = PlaceX - mx;
1825 Crosshair.AttachedObject.Y = PlaceY - my;
1826 if (!Marked.status || TEST_FLAG (LOCALREFFLAG, PCB))
1827 SetLocalRef (PlaceX - mx, PlaceY - my, true);
1828 Crosshair.AttachedObject.State = STATE_SECOND;
1830 /* get boundingbox of object and set cursor range */
1831 box = GetObjectBoundingBox (Crosshair.AttachedObject.Type,
1832 Crosshair.AttachedObject.Ptr1,
1833 Crosshair.AttachedObject.Ptr2,
1834 Crosshair.AttachedObject.Ptr3);
1835 SetCrosshairRange (Crosshair.AttachedObject.X - box->X1,
1836 Crosshair.AttachedObject.Y - box->Y1,
1837 PCB->MaxWidth - (box->X2 - Crosshair.AttachedObject.X),
1838 PCB->MaxHeight - (box->Y2 - Crosshair.AttachedObject.Y));
1840 /* get all attached objects if necessary */
1841 if ((Settings.Mode != COPY_MODE) && TEST_FLAG (RUBBERBANDFLAG, PCB))
1842 LookupRubberbandLines (Crosshair.AttachedObject.Type,
1843 Crosshair.AttachedObject.Ptr1,
1844 Crosshair.AttachedObject.Ptr2,
1845 Crosshair.AttachedObject.Ptr3);
1846 if (Settings.Mode != COPY_MODE &&
1847 (Crosshair.AttachedObject.Type == ELEMENT_TYPE ||
1848 Crosshair.AttachedObject.Type == VIA_TYPE ||
1849 Crosshair.AttachedObject.Type == LINE_TYPE ||
1850 Crosshair.AttachedObject.Type == LINEPOINT_TYPE))
1851 LookupRatLines (Crosshair.AttachedObject.Type,
1852 Crosshair.AttachedObject.Ptr1,
1853 Crosshair.AttachedObject.Ptr2,
1854 Crosshair.AttachedObject.Ptr3);
1858 * Return nonzero if the given file exists and is readable.
1861 FileExists (const char *name)
1863 FILE *f;
1864 f = fopen (name, "r");
1865 if (f)
1867 fclose (f);
1868 return 1;
1870 return 0;
1873 char *
1874 Concat (const char *first, ...)
1876 char *rv;
1877 int len;
1878 va_list a;
1880 len = strlen (first);
1881 rv = (char *) malloc (len + 1);
1882 strcpy (rv, first);
1884 va_start (a, first);
1885 while (1)
1887 const char *s = va_arg (a, const char *);
1888 if (!s)
1889 break;
1890 len += strlen (s);
1891 rv = (char *) realloc (rv, len + 1);
1892 strcat (rv, s);
1894 va_end (a);
1895 return rv;
1899 mem_any_set (unsigned char *ptr, int bytes)
1901 while (bytes--)
1902 if (*ptr++)
1903 return 1;
1904 return 0;
1907 /* This just fills in a FlagType with current flags. */
1908 FlagType
1909 MakeFlags (unsigned int flags)
1911 FlagType rv;
1912 memset (&rv, 0, sizeof (rv));
1913 rv.f = flags;
1914 return rv;
1917 /* This converts old flag bits (from saved PCB files) to new format. */
1918 FlagType
1919 OldFlags (unsigned int flags)
1921 FlagType rv;
1922 int i, f;
1923 memset (&rv, 0, sizeof (rv));
1924 /* If we move flag bits around, this is where we map old bits to them. */
1925 rv.f = flags & 0xffff;
1926 f = 0x10000;
1927 for (i = 0; i < 8; i++)
1929 /* use the closest thing to the old thermal style */
1930 if (flags & f)
1931 rv.t[i / 2] |= (1 << (4 * (i % 2)));
1932 f <<= 1;
1934 return rv;
1937 FlagType
1938 AddFlags (FlagType flag, unsigned int flags)
1940 flag.f |= flags;
1941 return flag;
1944 FlagType
1945 MaskFlags (FlagType flag, unsigned int flags)
1947 flag.f &= ~flags;
1948 return flag;
1951 /***********************************************************************
1952 * Layer Group Functions
1956 MoveLayerToGroup (int layer, int group)
1958 int prev, i, j;
1960 if (layer < 0 || layer > max_copper_layer + 1)
1961 return -1;
1962 prev = GetLayerGroupNumberByNumber (layer);
1963 if ((layer == solder_silk_layer
1964 && group == GetLayerGroupNumberByNumber (component_silk_layer))
1965 || (layer == component_silk_layer
1966 && group == GetLayerGroupNumberByNumber (solder_silk_layer))
1967 || (group < 0 || group >= max_group) || (prev == group))
1968 return prev;
1970 /* Remove layer from prev group */
1971 for (j = i = 0; i < PCB->LayerGroups.Number[prev]; i++)
1972 if (PCB->LayerGroups.Entries[prev][i] != layer)
1973 PCB->LayerGroups.Entries[prev][j++] = PCB->LayerGroups.Entries[prev][i];
1974 PCB->LayerGroups.Number[prev]--;
1976 /* Add layer to new group. */
1977 i = PCB->LayerGroups.Number[group]++;
1978 PCB->LayerGroups.Entries[group][i] = layer;
1980 return group;
1983 char *
1984 LayerGroupsToString (LayerGroupType *lg)
1986 #if MAX_LAYER < 9998
1987 /* Allows for layer numbers 0..9999 */
1988 static char buf[(MAX_LAYER + 2) * 5 + 1];
1989 #endif
1990 char *cp = buf;
1991 char sep = 0;
1992 int group, entry;
1993 for (group = 0; group < max_group; group++)
1994 if (PCB->LayerGroups.Number[group])
1996 if (sep)
1997 *cp++ = ':';
1998 sep = 1;
1999 for (entry = 0; entry < PCB->LayerGroups.Number[group]; entry++)
2001 int layer = PCB->LayerGroups.Entries[group][entry];
2002 if (layer == component_silk_layer)
2004 *cp++ = 'c';
2006 else if (layer == solder_silk_layer)
2008 *cp++ = 's';
2010 else
2012 sprintf (cp, "%d", layer + 1);
2013 while (*++cp)
2016 if (entry != PCB->LayerGroups.Number[group] - 1)
2017 *cp++ = ',';
2020 *cp++ = 0;
2021 return buf;
2024 char *
2025 pcb_author (void)
2027 #ifdef HAVE_GETPWUID
2028 static struct passwd *pwentry;
2029 static char *fab_author = 0;
2031 if (!fab_author)
2033 if (Settings.FabAuthor && Settings.FabAuthor[0])
2034 fab_author = Settings.FabAuthor;
2035 else
2037 int len;
2038 char *comma, *gecos;
2040 /* ID the user. */
2041 pwentry = getpwuid (getuid ());
2042 gecos = pwentry->pw_gecos;
2043 comma = strchr (gecos, ',');
2044 if (comma)
2045 len = comma - gecos;
2046 else
2047 len = strlen (gecos);
2048 fab_author = (char *)malloc (len + 1);
2049 if (!fab_author)
2051 perror ("pcb: out of memory.\n");
2052 exit (-1);
2054 memcpy (fab_author, gecos, len);
2055 fab_author[len] = 0;
2058 return fab_author;
2059 #else
2060 return "Unknown";
2061 #endif
2065 char *
2066 AttributeGetFromList (AttributeListType *list, char *name)
2068 int i;
2069 for (i=0; i<list->Number; i++)
2070 if (strcmp (name, list->List[i].name) == 0)
2071 return list->List[i].value;
2072 return NULL;
2076 AttributePutToList (AttributeListType *list, const char *name, const char *value, int replace)
2078 int i;
2080 /* If we're allowed to replace an existing attribute, see if we
2081 can. */
2082 if (replace)
2084 for (i=0; i<list->Number; i++)
2085 if (strcmp (name, list->List[i].name) == 0)
2087 free (list->List[i].value);
2088 list->List[i].value = STRDUP (value);
2089 return 1;
2093 /* At this point, we're going to need to add a new attribute to the
2094 list. See if there's room. */
2095 if (list->Number >= list->Max)
2097 list->Max += 10;
2098 list->List = (AttributeType *) realloc (list->List,
2099 list->Max * sizeof (AttributeType));
2102 /* Now add the new attribute. */
2103 i = list->Number;
2104 list->List[i].name = STRDUP (name);
2105 list->List[i].value = STRDUP (value);
2106 list->Number ++;
2107 return 0;
2110 void
2111 AttributeRemoveFromList(AttributeListType *list, char *name)
2113 int i, j;
2114 for (i=0; i<list->Number; i++)
2115 if (strcmp (name, list->List[i].name) == 0)
2117 free (list->List[i].name);
2118 free (list->List[i].value);
2119 for (j=i; j<list->Number-i; j++)
2120 list->List[j] = list->List[j+1];
2121 list->Number --;
2125 /* In future all use of this should be supplanted by
2126 * pcb-printf and %mr/%m# spec */
2127 const char *
2128 c_dtostr (double d)
2130 static char buf[100];
2131 int i, f;
2132 char *bufp = buf;
2134 if (d < 0)
2136 *bufp++ = '-';
2137 d = -d;
2139 d += 0.0000005; /* rounding */
2140 i = floor (d);
2141 d -= i;
2142 sprintf (bufp, "%d", i);
2143 bufp += strlen (bufp);
2144 *bufp++ = '.';
2146 f = floor (d * 1000000.0);
2147 sprintf (bufp, "%06d", f);
2148 return buf;
2151 void
2152 r_delete_element (DataType * data, ElementType * element)
2154 r_delete_entry (data->element_tree, (BoxType *) element);
2155 PIN_LOOP (element);
2157 r_delete_entry (data->pin_tree, (BoxType *) pin);
2159 END_LOOP;
2160 PAD_LOOP (element);
2162 r_delete_entry (data->pad_tree, (BoxType *) pad);
2164 END_LOOP;
2165 ELEMENTTEXT_LOOP (element);
2167 r_delete_entry (data->name_tree[n], (BoxType *) text);
2169 END_LOOP;
2173 /* ---------------------------------------------------------------------------
2174 * Returns a string that has a bunch of information about the program.
2175 * Can be used for things like "about" dialog boxes.
2178 char *
2179 GetInfoString (void)
2181 HID **hids;
2182 int i;
2183 static DynamicStringType info;
2184 static int first_time = 1;
2186 #define TAB " "
2188 if (first_time)
2190 first_time = 0;
2191 DSAddString (&info, "This is PCB, an interactive\n");
2192 DSAddString (&info, "printed circuit board editor\n");
2193 DSAddString (&info, "version ");
2194 DSAddString (&info, VERSION);
2195 DSAddString (&info, "\n\n");
2196 DSAddString (&info, "Compiled on " __DATE__ " at " __TIME__);
2197 DSAddString (&info, "\n\n" "by harry eaton\n\n");
2198 DSAddString (&info,
2199 "Copyright (C) Thomas Nau 1994, 1995, 1996, 1997\n");
2200 DSAddString (&info, "Copyright (C) harry eaton 1998-2007\n");
2201 DSAddString (&info, "Copyright (C) C. Scott Ananian 2001\n");
2202 DSAddString (&info,
2203 "Copyright (C) DJ Delorie 2003, 2004, 2005, 2006, 2007, 2008\n");
2204 DSAddString (&info,
2205 "Copyright (C) Dan McMahill 2003, 2004, 2005, 2006, 2007, 2008\n\n");
2206 DSAddString (&info, "It is licensed under the terms of the GNU\n");
2207 DSAddString (&info, "General Public License version 2\n");
2208 DSAddString (&info, "See the LICENSE file for more information\n\n");
2209 DSAddString (&info, "For more information see:\n\n");
2210 DSAddString (&info, "PCB homepage: http://pcb.geda-project.org\n");
2211 DSAddString (&info, "gEDA homepage: http://www.geda-project.org\n");
2212 DSAddString (&info, "gEDA Wiki: http://wiki.geda-project.org\n");
2214 DSAddString (&info, "----- Compile Time Options -----\n");
2215 hids = hid_enumerate ();
2216 DSAddString (&info, "GUI:\n");
2217 for (i = 0; hids[i]; i++)
2219 if (hids[i]->gui)
2221 DSAddString (&info, TAB);
2222 DSAddString (&info, hids[i]->name);
2223 DSAddString (&info, " : ");
2224 DSAddString (&info, hids[i]->description);
2225 DSAddString (&info, "\n");
2229 DSAddString (&info, "Exporters:\n");
2230 for (i = 0; hids[i]; i++)
2232 if (hids[i]->exporter)
2234 DSAddString (&info, TAB);
2235 DSAddString (&info, hids[i]->name);
2236 DSAddString (&info, " : ");
2237 DSAddString (&info, hids[i]->description);
2238 DSAddString (&info, "\n");
2242 DSAddString (&info, "Printers:\n");
2243 for (i = 0; hids[i]; i++)
2245 if (hids[i]->printer)
2247 DSAddString (&info, TAB);
2248 DSAddString (&info, hids[i]->name);
2249 DSAddString (&info, " : ");
2250 DSAddString (&info, hids[i]->description);
2251 DSAddString (&info, "\n");
2255 #undef TAB
2257 return info.Data;
2260 /* ---------------------------------------------------------------------------
2261 * mkdir() implentation, mostly for plugins, which don't have our config.h.
2264 #ifdef MKDIR_IS_PCBMKDIR
2265 #error "Don't know how to create a directory on this system."
2266 #endif
2269 pcb_mkdir (const char *path, int mode)
2271 return MKDIR (path, mode);
2274 /* ---------------------------------------------------------------------------
2275 * Returns a best guess about the orientation of an element. The
2276 * value corresponds to the rotation; a difference is the right value
2277 * to pass to RotateElementLowLevel. However, the actual value is no
2278 * indication of absolute rotation; only relative rotation is
2279 * meaningful.
2282 int
2283 ElementOrientation (ElementType *e)
2285 Coord pin1x, pin1y, pin2x, pin2y, dx, dy;
2286 bool found_pin1 = 0;
2287 bool found_pin2 = 0;
2289 /* in case we don't find pin 1 or 2, make sure we have initialized these variables */
2290 pin1x = 0;
2291 pin1y = 0;
2292 pin2x = 0;
2293 pin2y = 0;
2295 PIN_LOOP (e);
2297 if (NSTRCMP (pin->Number, "1") == 0)
2299 pin1x = pin->X;
2300 pin1y = pin->Y;
2301 found_pin1 = 1;
2303 else if (NSTRCMP (pin->Number, "2") == 0)
2305 pin2x = pin->X;
2306 pin2y = pin->Y;
2307 found_pin2 = 1;
2310 END_LOOP;
2312 PAD_LOOP (e);
2314 if (NSTRCMP (pad->Number, "1") == 0)
2316 pin1x = (pad->Point1.X + pad->Point2.X) / 2;
2317 pin1y = (pad->Point1.Y + pad->Point2.Y) / 2;
2318 found_pin1 = 1;
2320 else if (NSTRCMP (pad->Number, "2") == 0)
2322 pin2x = (pad->Point1.X + pad->Point2.X) / 2;
2323 pin2y = (pad->Point1.Y + pad->Point2.Y) / 2;
2324 found_pin2 = 1;
2327 END_LOOP;
2329 if (found_pin1 && found_pin2)
2331 dx = pin2x - pin1x;
2332 dy = pin2y - pin1y;
2334 else if (found_pin1 && (pin1x || pin1y))
2336 dx = pin1x;
2337 dy = pin1y;
2339 else if (found_pin2 && (pin2x || pin2y))
2341 dx = pin2x;
2342 dy = pin2y;
2344 else
2345 return 0;
2347 if (abs(dx) > abs(dy))
2348 return dx > 0 ? 0 : 2;
2349 return dy > 0 ? 3 : 1;
2353 ActionListRotations(int argc, char **argv, Coord x, Coord y)
2355 ELEMENT_LOOP (PCB->Data);
2357 printf("%d %s\n", ElementOrientation(element), NAMEONPCB_NAME(element));
2359 END_LOOP;
2361 return 0;
2364 HID_Action misc_action_list[] = {
2365 {"ListRotations", 0, ActionListRotations,
2366 0,0},
2369 REGISTER_ACTIONS (misc_action_list)