1 /*!
2  * \file src/action.c
3  *
4  * \brief Action routines for output window.
5  *
6  * <hr>
7  *
8  * <h1><b>Copyright.</b></h1>\n
9  *
10  * PCB, interactive printed circuit board design
11  *
12  * Copyright (C) 1994,1995,1996 Thomas Nau
13  *
14  * Copyright (C) 1997, 1998, 1999, 2000, 2001 Harry Eaton
15  *
16  * This program is free software; you can redistribute it and/or modify
17  * it under the terms of the GNU General Public License as published by
18  * the Free Software Foundation; either version 2 of the License, or
19  * (at your option) any later version.
20  *
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  *
26  * You should have received a copy of the GNU General Public License along
27  * with this program; if not, write to the Free Software Foundation, Inc.,
28  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
29  *
30  * Contact addresses for paper mail and Email:
31  * Harry Eaton, 6697 Buttonhole Ct, Columbia, MD 21044, USA
32  * haceaton@aplcomm.jhuapl.edu
33  */
34 
35 #ifdef HAVE_CONFIG_H
36 #include "config.h"
37 #endif
38 
39 #include "global.h"
40 
41 #include "action.h"
42 #include "autoplace.h"
43 #include "autoroute.h"
44 #include "buffer.h"
45 #include "change.h"
46 #include "copy.h"
47 #include "create.h"
48 #include "crosshair.h"
49 #include "data.h"
50 #include "draw.h"
51 #include "error.h"
52 #include "file.h"
53 #include "find.h"
54 #include "flags.h"
55 #include "hid.h"
56 #include "insert.h"
57 #include "line.h"
58 #include "mymem.h"
59 #include "misc.h"
60 #include "mirror.h"
61 #include "move.h"
62 #include "polygon.h"
63 /*#include "print.h"*/
64 #include "rats.h"
65 #include "remove.h"
66 #include "report.h"
67 #include "rotate.h"
68 #include "rubberband.h"
69 #include "search.h"
70 #include "select.h"
71 #include "set.h"
72 #include "thermal.h"
73 #include "undo.h"
74 #include "rtree.h"
75 #include "macro.h"
76 #include "pcb-printf.h"
77 
78 #include <assert.h>
79 #include <stdlib.h> /* rand() */
80 
81 #ifdef HAVE_LIBDMALLOC
82 #include <dmalloc.h>
83 #endif
84 
85 /* for fork() and friends */
86 #ifdef HAVE_UNISTD_H
87 #include <unistd.h>
88 #endif
89 
90 #ifdef HAVE_SYS_WAIT_H
91 #include <sys/wait.h>
92 #endif
93 
94 /* ---------------------------------------------------------------------------
95  * some local types
96  */
97 typedef enum
98 {
99   F_AddSelected,
100   F_All,
101   F_AllConnections,
102   F_AllRats,
103   F_AllUnusedPins,
104   F_Arc,
105   F_Arrow,
106   F_Block,
107   F_Description,
108   F_Cancel,
109   F_Center,
110   F_Clear,
111   F_ClearAndRedraw,
112   F_ClearList,
113   F_Close,
114   F_Found,
115   F_Connection,
116   F_Convert,
117   F_Copy,
118   F_CycleClip,
119   F_CycleCrosshair,
120   F_DeleteRats,
121   F_Drag,
122   F_DrillReport,
123   F_Element,
124   F_ElementByName,
125   F_ElementConnections,
126   F_ElementToBuffer,
127   F_Escape,
128   F_Find,
129   F_FlipElement,
130   F_FoundPins,
131   F_Grid,
132   F_InsertPoint,
133   F_Layer,
134   F_Layout,
135   F_LayoutAs,
136   F_LayoutToBuffer,
137   F_Line,
138   F_LineSize,
139   F_Lock,
140   F_Mirror,
141   F_Move,
142   F_NameOnPCB,
143   F_Netlist,
144   F_NetByName,
145   F_None,
146   F_Notify,
147   F_Object,
148   F_ObjectByName,
149   F_PasteBuffer,
150   F_PadByName,
151   F_PinByName,
152   F_PinOrPadName,
153   F_Pinout,
154   F_Polygon,
155   F_PolygonHole,
156   F_PreviousPoint,
157   F_RatsNest,
158   F_Rectangle,
159   F_Redraw,
160   F_Release,
161   F_Revert,
162   F_Remove,
163   F_RemoveSelected,
164   F_Report,
165   F_Reset,
166   F_ResetLinesAndPolygons,
167   F_ResetPinsViasAndPads,
168   F_Restore,
169   F_Rotate,
170   F_Save,
171   F_Selected,
172   F_SelectedArcs,
173   F_SelectedElements,
174   F_SelectedLines,
175   F_SelectedNames,
176   F_SelectedObjects,
177   F_SelectedPads,
178   F_SelectedPins,
179   F_SelectedTexts,
180   F_SelectedVias,
181   F_SelectedRats,
182   F_Stroke,
183   F_Text,
184   F_TextByName,
185   F_TextScale,
186   F_Thermal,
187   F_ToLayout,
188   F_ToggleAllDirections,
189   F_ToggleAutoDRC,
190   F_ToggleClearLine,
191   F_ToggleFullPoly,
192   F_ToggleGrid,
193   F_ToggleHideNames,
194   F_ToggleMask,
195   F_ToggleName,
196   F_ToggleObject,
197   F_ToggleShowDRC,
198   F_ToggleLiveRoute,
199   F_ToggleRubberBandMode,
200   F_ToggleStartDirection,
201   F_ToggleSnapPin,
202   F_ToggleThindraw,
203   F_ToggleLockNames,
204   F_ToggleOnlyNames,
205   F_ToggleThindrawPoly,
206   F_ToggleOrthoMove,
207   F_ToggleLocalRef,
208   F_ToggleCheckPlanes,
209   F_ToggleUniqueNames,
210   F_Via,
211   F_ViaByName,
212   F_Value,
213   F_ViaDrillingHole,
214   F_ViaSize,
215   F_Zoom,
216   F_ThroughHole,
217   F_BuriedVias,
218   F_ToggleAutoBuriedVias
219 }
220 FunctionID;
221 
222 typedef struct			/* used to identify subfunctions */
223 {
224   char *Identifier;
225   FunctionID ID;
226 }
227 FunctionType;
228 
229 /* --------------------------------------------------------------------------- */
230 
231 /* %start-doc actions 00delta
232 
233 Many actions take a @code{delta} parameter as the last parameter,
234 which is an amount to change something.  That @code{delta} may include
235 units, as an additional parameter, such as @code{Action(Object,5,mm)}.
236 If no units are specified, the default is PCB's native units
237 (currently 1/100 mil).  Also, if the delta is prefixed by @code{+} or
238 @code{-}, the size is increased or decreased by that amount.
239 Otherwise, the size size is set to the given amount.
240 
241 @example
242 Action(Object,5,mil)
243 Action(Object,+0.5,mm)
244 Action(Object,-1)
245 @end example
246 
247 Actions which take a @code{delta} parameter which do not accept all
248 these options will specify what they do take.
249 
250 %end-doc */
251 
252 /* %start-doc actions 00objects
253 
254 Many actions act on indicated objects on the board.  They will have
255 parameters like @code{ToggleObject} or @code{SelectedVias} to indicate
256 what group of objects they act on.  Unless otherwise specified, these
257 parameters are defined as follows:
258 
259 @table @code
260 
261 @item Object
262 @itemx ToggleObject
263 Affects the object under the mouse pointer.  If this action is invoked
264 from a menu or script, the user will be prompted to click on an
265 object, which is then the object affected.
266 
267 @item Selected
268 @itemx SelectedObjects
269 
270 Affects all objects which are currently selected.  At least, all
271 selected objects for which the given action makes sense.
272 
273 @item SelectedPins
274 @itemx SelectedVias
275 @itemx Selected@var{Type}
276 @itemx @i{etc}
277 Affects all objects which are both selected and of the @var{Type} specified.
278 
279 @end table
280 
281 %end-doc */
282 
283 /*  %start-doc actions 00macros
284 
285 @macro pinshapes
286 
287 Pins, pads, and vias can have various shapes.  All may be round.  Pins
288 and pads may be square (obviously "square" pads are usually
289 rectangular).  Pins and vias may be octagonal.  When you change a
290 shape flag of an element, you actually change all of its pins and
291 pads.
292 
293 Note that the square flag takes precedence over the octagon flag,
294 thus, if both the square and octagon flags are set, the object is
295 square.  When the square flag is cleared, the pins and pads will be
296 either round or, if the octagon flag is set, octagonal.
297 
298 @end macro
299 
300 %end-doc */
301 
302 /* ---------------------------------------------------------------------------
303  * some local identifiers
304  */
305 static PointType InsertedPoint;
306 static LayerType *lastLayer;
307 static struct
308 {
309   PolygonType *poly;
310   LineType line;
311 }
312 fake;
313 
314 static struct
315 {
316   Coord X, Y;
317   Cardinal Buffer;
318   bool Click;
319   bool Moving;		/* selected type clicked on */
320   int Hit;			/* move type clicked on */
321   void *ptr1;
322   void *ptr2;
323   void *ptr3;
324 }
325 Note;
326 
327 static int defer_updates = 0;
328 static int defer_needs_update = 0;
329 
330 static Cardinal polyIndex = 0;
331 static bool saved_mode = false;
332 #ifdef HAVE_LIBSTROKE
333 static bool mid_stroke = false;
334 static BoxType StrokeBox;
335 #endif
336 static FunctionType Functions[] = {
337   {"AddSelected", F_AddSelected},
338   {"All", F_All},
339   {"AllConnections", F_AllConnections},
340   {"AllRats", F_AllRats},
341   {"AllUnusedPins", F_AllUnusedPins},
342   {"Arc", F_Arc},
343   {"Arrow", F_Arrow},
344   {"Block", F_Block},
345   {"Description", F_Description},
346   {"Cancel", F_Cancel},
347   {"Center", F_Center},
348   {"Clear", F_Clear},
349   {"ClearAndRedraw", F_ClearAndRedraw},
350   {"ClearList", F_ClearList},
351   {"Close", F_Close},
352   {"Found", F_Found},
353   {"Connection", F_Connection},
354   {"Convert", F_Convert},
355   {"Copy", F_Copy},
356   {"CycleClip", F_CycleClip},
357   {"CycleCrosshair", F_CycleCrosshair},
358   {"DeleteRats", F_DeleteRats},
359   {"Drag", F_Drag},
360   {"DrillReport", F_DrillReport},
361   {"Element", F_Element},
362   {"ElementByName", F_ElementByName},
363   {"ElementConnections", F_ElementConnections},
364   {"ElementToBuffer", F_ElementToBuffer},
365   {"Escape", F_Escape},
366   {"Find", F_Find},
367   {"FlipElement", F_FlipElement},
368   {"FoundPins", F_FoundPins},
369   {"Grid", F_Grid},
370   {"InsertPoint", F_InsertPoint},
371   {"Layer", F_Layer},
372   {"Layout", F_Layout},
373   {"LayoutAs", F_LayoutAs},
374   {"LayoutToBuffer", F_LayoutToBuffer},
375   {"Line", F_Line},
376   {"LineSize", F_LineSize},
377   {"Lock", F_Lock},
378   {"Mirror", F_Mirror},
379   {"Move", F_Move},
380   {"NameOnPCB", F_NameOnPCB},
381   {"Netlist", F_Netlist},
382   {"NetByName", F_NetByName},
383   {"None", F_None},
384   {"Notify", F_Notify},
385   {"Object", F_Object},
386   {"ObjectByName", F_ObjectByName},
387   {"PasteBuffer", F_PasteBuffer},
388   {"PadByName", F_PadByName},
389   {"PinByName", F_PinByName},
390   {"PinOrPadName", F_PinOrPadName},
391   {"Pinout", F_Pinout},
392   {"Polygon", F_Polygon},
393   {"PolygonHole", F_PolygonHole},
394   {"PreviousPoint", F_PreviousPoint},
395   {"RatsNest", F_RatsNest},
396   {"Rectangle", F_Rectangle},
397   {"Redraw", F_Redraw},
398   {"Release", F_Release},
399   {"Remove", F_Remove},
400   {"RemoveSelected", F_RemoveSelected},
401   {"Report", F_Report},
402   {"Reset", F_Reset},
403   {"ResetLinesAndPolygons", F_ResetLinesAndPolygons},
404   {"ResetPinsViasAndPads", F_ResetPinsViasAndPads},
405   {"Restore", F_Restore},
406   {"Revert", F_Revert},
407   {"Rotate", F_Rotate},
408   {"Save", F_Save},
409   {"Selected", F_Selected},
410   {"SelectedArcs", F_SelectedArcs},
411   {"SelectedElements", F_SelectedElements},
412   {"SelectedLines", F_SelectedLines},
413   {"SelectedNames", F_SelectedNames},
414   {"SelectedObjects", F_SelectedObjects},
415   {"SelectedPins", F_SelectedPins},
416   {"SelectedPads", F_SelectedPads},
417   {"SelectedRats", F_SelectedRats},
418   {"SelectedTexts", F_SelectedTexts},
419   {"SelectedVias", F_SelectedVias},
420   {"Stroke", F_Stroke},
421   {"Text", F_Text},
422   {"TextByName", F_TextByName},
423   {"TextScale", F_TextScale},
424   {"Thermal", F_Thermal},
425   {"ToLayout", F_ToLayout},
426   {"Toggle45Degree", F_ToggleAllDirections},
427   {"ToggleClearLine", F_ToggleClearLine},
428   {"ToggleFullPoly", F_ToggleFullPoly},
429   {"ToggleGrid", F_ToggleGrid},
430   {"ToggleMask", F_ToggleMask},
431   {"ToggleName", F_ToggleName},
432   {"ToggleObject", F_ToggleObject},
433   {"ToggleRubberBandMode", F_ToggleRubberBandMode},
434   {"ToggleStartDirection", F_ToggleStartDirection},
435   {"ToggleSnapPin", F_ToggleSnapPin},
436   {"ToggleThindraw", F_ToggleThindraw},
437   {"ToggleThindrawPoly", F_ToggleThindrawPoly},
438   {"ToggleLockNames", F_ToggleLockNames},
439   {"ToggleOnlyNames", F_ToggleOnlyNames},
440   {"ToggleHideNames", F_ToggleHideNames},
441   {"ToggleCheckPlanes", F_ToggleCheckPlanes},
442   {"ToggleLocalRef", F_ToggleLocalRef},
443   {"ToggleOrthoMove", F_ToggleOrthoMove},
444   {"ToggleShowDRC", F_ToggleShowDRC},
445   {"ToggleLiveRoute", F_ToggleLiveRoute},
446   {"ToggleAutoDRC", F_ToggleAutoDRC},
447   {"ToggleUniqueNames", F_ToggleUniqueNames},
448   {"Value", F_Value},
449   {"Via", F_Via},
450   {"ViaByName", F_ViaByName},
451   {"ViaSize", F_ViaSize},
452   {"ViaDrillingHole", F_ViaDrillingHole},
453   {"Zoom", F_Zoom},
454   {"ThroughHole", F_ThroughHole},
455   {"TH", F_ThroughHole},
456   {"BuriedVias", F_BuriedVias},
457   {"ToggleAutoBuriedVias", F_ToggleAutoBuriedVias}
458 };
459 
460 /* ---------------------------------------------------------------------------
461  * some local routines
462  */
463 static int GetFunctionID (String);
464 static void AdjustAttachedBox (void);
465 static void NotifyLine (void);
466 static void NotifyBlock (void);
467 static void NotifyMode (void);
468 static void ClearWarnings (void);
469 #ifdef HAVE_LIBSTROKE
470 static void FinishStroke (void);
471 extern void stroke_init (void);
472 extern void stroke_record (int x, int y);
473 extern int stroke_trans (char *s);
474 #endif
475 static void ChangeFlag (char *, char *, int, char *);
476 
477 #ifdef HAVE_LIBSTROKE
478 
479 /*!
480  * \brief Try to recognize the stroke sent.
481  */
482 void
FinishStroke(void)483 FinishStroke (void)
484 {
485   char msg[255];
486   int type;
487   unsigned long num;
488   void *ptr1, *ptr2, *ptr3;
489 
490   mid_stroke = false;
491   if (stroke_trans (msg))
492     {
493       num = atoi (msg);
494       switch (num)
495 	{
496 	case 456:
497 	  if (Settings.Mode == LINE_MODE)
498 	    {
499 	      SetMode (LINE_MODE);
500 	    }
501 	  break;
502 	case 9874123:
503 	case 74123:
504 	case 987412:
505 	case 8741236:
506 	case 874123:
507 	  RotateScreenObject (StrokeBox.X1, StrokeBox.Y1, SWAP_IDENT ? 1 : 3);
508 	  break;
509 	case 7896321:
510 	case 786321:
511 	case 789632:
512 	case 896321:
513 	  RotateScreenObject (StrokeBox.X1, StrokeBox.Y1, SWAP_IDENT ? 3 : 1);
514 	  break;
515 	case 258:
516 	  SetMode (LINE_MODE);
517 	  break;
518 	case 852:
519 	  SetMode (ARROW_MODE);
520 	  break;
521 	case 1478963:
522 	  ActionUndo ("");
523 	  break;
524 	case 147423:
525 	case 147523:
526 	case 1474123:
527 	  Redo (true);
528 	  break;
529 	case 148963:
530 	case 147863:
531 	case 147853:
532 	case 145863:
533 	  SetMode (VIA_MODE);
534 	  break;
535 	case 951:
536 	case 9651:
537 	case 9521:
538 	case 9621:
539 	case 9851:
540 	case 9541:
541 	case 96521:
542 	case 96541:
543 	case 98541:
544 	  /* XXX: FIXME: Call a zoom-extents action */
545 	  break;
546 	case 159:
547 	case 1269:
548 	case 1259:
549 	case 1459:
550 	case 1569:
551 	case 1589:
552 	case 12569:
553 	case 12589:
554 	case 14589:
555 	  /* XXX: FIXME: Zoom to fit the box StrokeBox.[X1,Y1] - StrokeBox.[X2,Y2] */
556 	  break;
557 
558 	default:
559 	  Message (_("Unknown stroke %s\n"), msg);
560 	  break;
561 	}
562     }
563   else
564     gui->beep ();
565 }
566 #endif
567 
568 /*!
569  * \brief Clear warning color from pins/pads.
570  */
571 static void
ClearWarnings()572 ClearWarnings ()
573 {
574   Settings.RatWarn = false;
575   ALLPIN_LOOP (PCB->Data);
576   {
577     if (TEST_FLAG (WARNFLAG, pin))
578       {
579 	CLEAR_FLAG (WARNFLAG, pin);
580 	DrawPin (pin);
581       }
582   }
583   ENDALL_LOOP;
584   ALLPAD_LOOP (PCB->Data);
585   {
586     if (TEST_FLAG (WARNFLAG, pad))
587       {
588 	CLEAR_FLAG (WARNFLAG, pad);
589 	DrawPad (pad);
590       }
591   }
592   ENDALL_LOOP;
593   Draw ();
594 }
595 
596 /*!
597  * \brief Click callback.
598  *
599  * This is called a clicktime after a mouse down, to we can distinguish
600  * between short clicks (typically: select or create something) and long
601  * clicks. Long clicks typically drag something.
602  */
603 static void
click_cb(hidval hv)604 click_cb (hidval hv)
605 {
606   if (Note.Click)
607     {
608       notify_crosshair_change (false);
609       Note.Click = false;
610       if (Note.Moving && !gui->shift_is_pressed ())
611 	{
612 	  Note.Buffer = Settings.BufferNumber;
613 	  SetBufferNumber (MAX_BUFFER - 1);
614 	  ClearBuffer (PASTEBUFFER);
615 	  AddSelectedToBuffer (PASTEBUFFER, Note.X, Note.Y, true);
616 	  SaveUndoSerialNumber ();
617 	  RemoveSelected ();
618 	  SaveMode ();
619 	  saved_mode = true;
620 	  SetMode (PASTEBUFFER_MODE);
621 	}
622       else if (Note.Hit && !gui->shift_is_pressed ())
623 	{
624 	  SaveMode ();
625 	  saved_mode = true;
626 	  SetMode (gui->control_is_pressed ()? COPY_MODE : MOVE_MODE);
627 	  Crosshair.AttachedObject.Ptr1 = Note.ptr1;
628 	  Crosshair.AttachedObject.Ptr2 = Note.ptr2;
629 	  Crosshair.AttachedObject.Ptr3 = Note.ptr3;
630 	  Crosshair.AttachedObject.Type = Note.Hit;
631 	  AttachForCopy (Note.X, Note.Y);
632 	}
633       else
634 	{
635 	  BoxType box;
636 
637 	  Note.Hit = 0;
638 	  Note.Moving = false;
639 	  SaveUndoSerialNumber ();
640 	  box.X1 = -MAX_COORD;
641 	  box.Y1 = -MAX_COORD;
642 	  box.X2 = MAX_COORD;
643 	  box.Y2 = MAX_COORD;
644 	  /* unselect first if shift key not down */
645 	  if (!gui->shift_is_pressed () && SelectBlock (&box, false))
646 	    SetChangedFlag (true);
647 	  NotifyBlock ();
648 	  Crosshair.AttachedBox.Point1.X = Note.X;
649 	  Crosshair.AttachedBox.Point1.Y = Note.Y;
650 	}
651       notify_crosshair_change (true);
652     }
653 }
654 
655 /*!
656  * \brief This is typically called when the mouse has moved or the mouse
657  * button was released.
658  */
659 static void
ReleaseMode(void)660 ReleaseMode (void)
661 {
662   BoxType box;
663 
664   if (Note.Click)
665     {
666       BoxType box;
667 
668       box.X1 = -MAX_COORD;
669       box.Y1 = -MAX_COORD;
670       box.X2 = MAX_COORD;
671       box.Y2 = MAX_COORD;
672 
673       Note.Click = false;	/* inhibit timer action */
674       SaveUndoSerialNumber ();
675       /* unselect first if shift key not down */
676       if (!gui->shift_is_pressed ())
677 	{
678 	  if (SelectBlock (&box, false))
679 	    SetChangedFlag (true);
680 	  if (Note.Moving)
681 	    {
682 	      Note.Moving = 0;
683 	      Note.Hit = 0;
684 	      return;
685 	    }
686 	}
687         /* Restore the SN so that if we select something the deselect/select combo
688          gets the same SN. */
689         RestoreUndoSerialNumber();
690         if (SelectObject ())
691             SetChangedFlag (true);
692         else
693         /* We didn't select anything new, so, the deselection should get its
694          own SN. */
695             IncrementUndoSerialNumber();
696       Note.Hit = 0;
697       Note.Moving = 0;
698     }
699   else if (Note.Moving)
700     {
701       RestoreUndoSerialNumber ();
702       NotifyMode ();
703       ClearBuffer (PASTEBUFFER);
704       SetBufferNumber (Note.Buffer);
705       Note.Moving = false;
706       Note.Hit = 0;
707     }
708   else if (Note.Hit)
709     {
710       NotifyMode ();
711       Note.Hit = 0;
712     }
713   else if (Settings.Mode == ARROW_MODE)
714     {
715       box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
716 		    Crosshair.AttachedBox.Point2.X);
717       box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
718 		    Crosshair.AttachedBox.Point2.Y);
719       box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
720 		    Crosshair.AttachedBox.Point2.X);
721       box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
722 		    Crosshair.AttachedBox.Point2.Y);
723       RestoreUndoSerialNumber ();
724       if (SelectBlock (&box, true))
725 	SetChangedFlag (true);
726       else if (Bumped)
727 	IncrementUndoSerialNumber ();
728       Crosshair.AttachedBox.State = STATE_FIRST;
729     }
730   if (saved_mode)
731     RestoreMode ();
732   saved_mode = false;
733 }
734 
735 #define HSIZE 257
736 static char function_hash[HSIZE];
737 static int hash_initted = 0;
738 
739 static int
hashfunc(String s)740 hashfunc(String s)
741 {
742   int i = 0;
743   while (*s)
744     {
745       i ^= i >> 16;
746       i = (i * 13) ^ (unsigned char)tolower((int) *s);
747       s ++;
748     }
749   i = (unsigned int)i % HSIZE;
750   return i;
751 }
752 
753 /*!
754  * \brief Get function ID of passed string.
755  */
756 static int
GetFunctionID(String Ident)757 GetFunctionID (String Ident)
758 {
759   int i, h;
760 
761   if (Ident == 0)
762     return -1;
763 
764   if (!hash_initted)
765     {
766       hash_initted = 1;
767       if (HSIZE < ENTRIES (Functions) * 2)
768 	{
769 	  fprintf(stderr, _("Error: function hash size too small (%d vs %lu at %s:%d)\n"),
770 		  HSIZE, (unsigned long) ENTRIES (Functions)*2, __FILE__,  __LINE__);
771 	  exit(1);
772 	}
773       if (ENTRIES (Functions) > 254)
774 	{
775 	  /* Change 'char' to 'int' and remove this when we get to 256
776 	     strings to hash. */
777 	  fprintf(stderr, _("Error: function hash type too small (%d vs %lu at %s:%d)\n"),
778 		  256, (unsigned long) ENTRIES (Functions), __FILE__,  __LINE__);
779 	  exit(1);
780 
781 	}
782       for (i=ENTRIES (Functions)-1; i>=0; i--)
783 	{
784 	  h = hashfunc (Functions[i].Identifier);
785 	  while (function_hash[h])
786 	    h = (h + 1) % HSIZE;
787 	  function_hash[h] = i + 1;
788 	}
789     }
790 
791   i = hashfunc (Ident);
792   while (1)
793     {
794       /* We enforce the "hash table bigger than function table" rule,
795 	 so we know there will be at least one zero entry to find.  */
796       if (!function_hash[i])
797 	return (-1);
798       if (!strcasecmp (Ident, Functions[function_hash[i]-1].Identifier))
799 	return ((int) Functions[function_hash[i]-1].ID);
800       i = (i + 1) % HSIZE;
801     }
802 }
803 
804 /*!
805  * \brief Set new coordinates if in 'RECTANGLE' mode.
806  *
807  * The cursor shape is also adjusted.
808  */
809 static void
AdjustAttachedBox(void)810 AdjustAttachedBox (void)
811 {
812   if (Settings.Mode == ARC_MODE)
813     {
814       Crosshair.AttachedBox.otherway = gui->shift_is_pressed ();
815       return;
816     }
817   switch (Crosshair.AttachedBox.State)
818     {
819     case STATE_SECOND:		/* one corner is selected */
820       {
821 	/* update coordinates */
822 	Crosshair.AttachedBox.Point2.X = Crosshair.X;
823 	Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
824 	break;
825       }
826     }
827 }
828 
829 /*!
830  * \brief Adjusts the objects which are to be created like attached
831  * lines.
832  */
833 void
AdjustAttachedObjects(void)834 AdjustAttachedObjects (void)
835 {
836   PointType *pnt;
837   switch (Settings.Mode)
838     {
839       /* update at least an attached block (selection) */
840     case NO_MODE:
841     case ARROW_MODE:
842       if (Crosshair.AttachedBox.State)
843 	{
844 	  Crosshair.AttachedBox.Point2.X = Crosshair.X;
845 	  Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
846 	}
847       break;
848 
849       /* rectangle creation mode */
850     case RECTANGLE_MODE:
851     case ARC_MODE:
852       AdjustAttachedBox ();
853       break;
854 
855       /* polygon creation mode */
856     case POLYGON_MODE:
857     case POLYGONHOLE_MODE:
858       AdjustAttachedLine ();
859       break;
860       /* line creation mode */
861     case LINE_MODE:
862       if (PCB->RatDraw || PCB->Clipping == 0)
863 	AdjustAttachedLine ();
864       else
865 	AdjustTwoLine (PCB->Clipping - 1);
866       break;
867       /* point insertion mode */
868     case INSERTPOINT_MODE:
869       pnt = AdjustInsertPoint ();
870       if (pnt)
871 	InsertedPoint = *pnt;
872       break;
873     case ROTATE_MODE:
874       break;
875     }
876 }
877 
878 /*!
879  * \brief Creates points of a line.
880  */
881 static void
NotifyLine(void)882 NotifyLine (void)
883 {
884   int type = NO_TYPE;
885   void *ptr1, *ptr2, *ptr3;
886 
887   if (!Marked.status || TEST_FLAG (LOCALREFFLAG, PCB))
888     SetLocalRef (Crosshair.X, Crosshair.Y, true);
889   switch (Crosshair.AttachedLine.State)
890     {
891     case STATE_FIRST:		/* first point */
892       if (PCB->RatDraw && SearchScreen (Crosshair.X, Crosshair.Y,
893 					PAD_TYPE | PIN_TYPE, &ptr1, &ptr1,
894 					&ptr1) == NO_TYPE)
895 	{
896 	  gui->beep ();
897 	  break;
898 	}
899       if (TEST_FLAG (AUTODRCFLAG, PCB) && Settings.Mode == LINE_MODE)
900 	{
901 	  type = SearchScreen (Crosshair.X, Crosshair.Y,
902 			       PIN_TYPE | PAD_TYPE | VIA_TYPE, &ptr1, &ptr2,
903 			       &ptr3);
904 	  LookupConnection (Crosshair.X, Crosshair.Y, true, 1, CONNECTEDFLAG, false);
905 	  LookupConnection (Crosshair.X, Crosshair.Y, true, 1, FOUNDFLAG, true);
906 	}
907       if (type == PIN_TYPE || type == VIA_TYPE)
908 	{
909 	  Crosshair.AttachedLine.Point1.X =
910 	    Crosshair.AttachedLine.Point2.X = ((PinType *) ptr2)->X;
911 	  Crosshair.AttachedLine.Point1.Y =
912 	    Crosshair.AttachedLine.Point2.Y = ((PinType *) ptr2)->Y;
913 	}
914       else if (type == PAD_TYPE)
915 	{
916 	  PadType *pad = (PadType *) ptr2;
917 	  double d1 = Distance (Crosshair.X, Crosshair.Y, pad->Point1.X, pad->Point1.Y);
918 	  double d2 = Distance (Crosshair.X, Crosshair.Y, pad->Point2.X, pad->Point2.Y);
919 	  if (d2 < d1)
920 	    {
921 	      Crosshair.AttachedLine.Point1 =
922 		Crosshair.AttachedLine.Point2 = pad->Point2;
923 	    }
924 	  else
925 	    {
926 	      Crosshair.AttachedLine.Point1 =
927 		Crosshair.AttachedLine.Point2 = pad->Point1;
928 	    }
929 	}
930       else
931 	{
932 	  Crosshair.AttachedLine.Point1.X =
933 	    Crosshair.AttachedLine.Point2.X = Crosshair.X;
934 	  Crosshair.AttachedLine.Point1.Y =
935 	    Crosshair.AttachedLine.Point2.Y = Crosshair.Y;
936 	}
937       Crosshair.AttachedLine.State = STATE_SECOND;
938       break;
939 
940     case STATE_SECOND:
941       /* fall through to third state too */
942       lastLayer = CURRENT;
943     default:			/* all following points */
944       Crosshair.AttachedLine.State = STATE_THIRD;
945       break;
946     }
947 }
948 
949 /*!
950  * \brief Create first or second corner of a marked block.
951  */
952 static void
NotifyBlock(void)953 NotifyBlock (void)
954 {
955   notify_crosshair_change (false);
956   switch (Crosshair.AttachedBox.State)
957     {
958     case STATE_FIRST:		/* setup first point */
959       Crosshair.AttachedBox.Point1.X =
960 	Crosshair.AttachedBox.Point2.X = Crosshair.X;
961       Crosshair.AttachedBox.Point1.Y =
962 	Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
963       Crosshair.AttachedBox.State = STATE_SECOND;
964       break;
965 
966     case STATE_SECOND:		/* setup second point */
967       Crosshair.AttachedBox.State = STATE_THIRD;
968       break;
969     }
970   notify_crosshair_change (true);
971 }
972 
973 
974 /*!
975  * \brief This is called after every mode change, like mouse button pressed,
976  * mouse button released, dragging something started or a different tool
977  * selected.
978  *
979  * It does what's appropriate for the current mode setting.
980  * This can also mean creation of an object at the current crosshair location.
981  *
982  * New created objects are added to the create undo list of course.
983  */
984 static void
NotifyMode(void)985 NotifyMode (void)
986 {
987   void *ptr1, *ptr2, *ptr3;
988   int type;
989 
990   if (Settings.RatWarn)
991     ClearWarnings ();
992   switch (Settings.Mode)
993     {
994     case ARROW_MODE:
995       {
996 	int test;
997 	hidval hv;
998 
999 	Note.Click = true;
1000 	/* do something after click time */
1001 	gui->add_timer (click_cb, CLICK_TIME, hv);
1002 
1003 	/* see if we clicked on something already selected
1004 	 * (Note.Moving) or clicked on a MOVE_TYPE
1005 	 * (Note.Hit)
1006 	 */
1007 	for (test = (SELECT_TYPES | MOVE_TYPES) & ~RATLINE_TYPE;
1008 	     test; test &= ~type)
1009 	  {
1010 	    type = SearchScreen (Note.X, Note.Y, test, &ptr1, &ptr2, &ptr3);
1011 	    if (!Note.Hit && (type & MOVE_TYPES) &&
1012 		!TEST_FLAG (LOCKFLAG, (PinType *) ptr2))
1013 	      {
1014 		Note.Hit = type;
1015 		Note.ptr1 = ptr1;
1016 		Note.ptr2 = ptr2;
1017 		Note.ptr3 = ptr3;
1018 	      }
1019 	    if (!Note.Moving && (type & SELECT_TYPES) &&
1020 		TEST_FLAG (SELECTEDFLAG, (PinType *) ptr2))
1021 	      Note.Moving = true;
1022 	    if ((Note.Hit && Note.Moving) || type == NO_TYPE)
1023 	      break;
1024 	  }
1025 	break;
1026       }
1027 
1028     case VIA_MODE:
1029       {
1030 	PinType *via;
1031 
1032 	if (!PCB->ViaOn)
1033 	  {
1034 	    Message (_("You must turn via visibility on before\n"
1035 		       "you can place vias\n"));
1036 	    break;
1037 	  }
1038 	if ((via = CreateNewVia (PCB->Data, Note.X, Note.Y,
1039 				 Settings.ViaThickness, 2 * Settings.Keepaway,
1040 				 Settings.ViaMaskAperture, Settings.ViaDrillingHole, NULL,
1041 				 NoFlags ())) != NULL)
1042 	  {
1043 	    AddObjectToCreateUndoList (VIA_TYPE, via, via, via);
1044 	    if (gui->shift_is_pressed ())
1045 	      ChangeObjectThermal (VIA_TYPE, via, via, via, PCB->ThermStyle);
1046 	    IncrementUndoSerialNumber ();
1047 	    DrawVia (via);
1048 	    Draw ();
1049 	  }
1050 	break;
1051       }
1052 
1053     case ARC_MODE:
1054       {
1055 	switch (Crosshair.AttachedBox.State)
1056 	  {
1057 	  case STATE_FIRST:
1058 	    Crosshair.AttachedBox.Point1.X =
1059 	      Crosshair.AttachedBox.Point2.X = Note.X;
1060 	    Crosshair.AttachedBox.Point1.Y =
1061 	      Crosshair.AttachedBox.Point2.Y = Note.Y;
1062 	    Crosshair.AttachedBox.State = STATE_SECOND;
1063 	    break;
1064 
1065 	  case STATE_SECOND:
1066 	  case STATE_THIRD:
1067 	    {
1068 	      ArcType *arc;
1069 	      Coord wx, wy;
1070 	      Angle sa, dir;
1071 
1072 	      wx = Note.X - Crosshair.AttachedBox.Point1.X;
1073 	      wy = Note.Y - Crosshair.AttachedBox.Point1.Y;
1074 	      if (XOR (Crosshair.AttachedBox.otherway, abs (wy) > abs (wx)))
1075 		{
1076 		  Crosshair.AttachedBox.Point2.X =
1077 		    Crosshair.AttachedBox.Point1.X + abs (wy) * SGNZ (wx);
1078 		  sa = (wx >= 0) ? 0 : 180;
1079 #ifdef ARC45
1080 		  if (abs (wy) / 2 >= abs (wx))
1081 		    dir = (SGNZ (wx) == SGNZ (wy)) ? 45 : -45;
1082 		  else
1083 #endif
1084 		    dir = (SGNZ (wx) == SGNZ (wy)) ? 90 : -90;
1085 		}
1086 	      else
1087 		{
1088 		  Crosshair.AttachedBox.Point2.Y =
1089 		    Crosshair.AttachedBox.Point1.Y + abs (wx) * SGNZ (wy);
1090 		  sa = (wy >= 0) ? -90 : 90;
1091 #ifdef ARC45
1092 		  if (abs (wx) / 2 >= abs (wy))
1093 		    dir = (SGNZ (wx) == SGNZ (wy)) ? -45 : 45;
1094 		  else
1095 #endif
1096 		    dir = (SGNZ (wx) == SGNZ (wy)) ? -90 : 90;
1097 		  wy = wx;
1098 		}
1099 	      if (abs (wy) > 0 && (arc = CreateNewArcOnLayer (CURRENT,
1100 							      Crosshair.
1101 							      AttachedBox.
1102 							      Point2.X,
1103 							      Crosshair.
1104 							      AttachedBox.
1105 							      Point2.Y,
1106 							      abs (wy),
1107 							      abs (wy),
1108 							      sa,
1109 							      dir,
1110 							      Settings.
1111 							      LineThickness,
1112 							      2 * Settings.
1113 							      Keepaway,
1114 							      MakeFlags
1115 							      (TEST_FLAG
1116 							       (CLEARNEWFLAG,
1117 								PCB) ?
1118 							       CLEARLINEFLAG :
1119 							       0))))
1120 		{
1121 		  BoxType *bx;
1122 
1123 		  bx = GetArcEnds (arc);
1124 		  Crosshair.AttachedBox.Point1.X =
1125 		    Crosshair.AttachedBox.Point2.X = bx->X2;
1126 		  Crosshair.AttachedBox.Point1.Y =
1127 		    Crosshair.AttachedBox.Point2.Y = bx->Y2;
1128 		  AddObjectToCreateUndoList (ARC_TYPE, CURRENT, arc, arc);
1129 		  IncrementUndoSerialNumber ();
1130 		  addedLines++;
1131 		  DrawArc (CURRENT, arc);
1132 		  Draw ();
1133 		  Crosshair.AttachedBox.State = STATE_THIRD;
1134 		}
1135 	      break;
1136 	    }
1137 	  }
1138 	break;
1139       }
1140     case LOCK_MODE:
1141       {
1142 	type = SearchScreen (Note.X, Note.Y, LOCK_TYPES, &ptr1, &ptr2, &ptr3);
1143 	if (type == ELEMENT_TYPE)
1144 	  {
1145 	    ElementType *element = (ElementType *) ptr2;
1146 
1147 	    TOGGLE_FLAG (LOCKFLAG, element);
1148 	    PIN_LOOP (element);
1149 	    {
1150 	      TOGGLE_FLAG (LOCKFLAG, pin);
1151 	      CLEAR_FLAG (SELECTEDFLAG, pin);
1152 	    }
1153 	    END_LOOP;
1154 	    PAD_LOOP (element);
1155 	    {
1156 	      TOGGLE_FLAG (LOCKFLAG, pad);
1157 	      CLEAR_FLAG (SELECTEDFLAG, pad);
1158 	    }
1159 	    END_LOOP;
1160 	    CLEAR_FLAG (SELECTEDFLAG, element);
1161 	    /* always re-draw it since I'm too lazy
1162 	     * to tell if a selected flag changed
1163 	     */
1164 	    DrawElement (element);
1165 	    Draw ();
1166 	    SetChangedFlag (true);
1167 	    hid_actionl ("Report", "Object", NULL);
1168 	  }
1169 	else if (type != NO_TYPE)
1170 	  {
1171 	    TextType *thing = (TextType *) ptr3;
1172 	    TOGGLE_FLAG (LOCKFLAG, thing);
1173 	    if (TEST_FLAG (LOCKFLAG, thing)
1174 		&& TEST_FLAG (SELECTEDFLAG, thing))
1175 	      {
1176 		/* this is not un-doable since LOCK isn't */
1177 		CLEAR_FLAG (SELECTEDFLAG, thing);
1178 		DrawObject (type, ptr1, ptr2);
1179 		Draw ();
1180 	      }
1181 	    SetChangedFlag (true);
1182 	    hid_actionl ("Report", "Object", NULL);
1183 	  }
1184 	break;
1185       }
1186     case THERMAL_MODE:
1187       {
1188 	if (((type
1189 	      =
1190 	      SearchScreen (Note.X, Note.Y, PIN_TYPES, &ptr1, &ptr2,
1191 			    &ptr3)) != NO_TYPE)
1192 	    && !TEST_FLAG (HOLEFLAG, (PinType *) ptr3))
1193 	  {
1194 	    if (gui->shift_is_pressed ())
1195 	      {
1196 		int tstyle = GET_THERM (INDEXOFCURRENT, (PinType *) ptr3);
1197 		tstyle++;
1198 		if (tstyle > 5)
1199 		  tstyle = 1;
1200 		ChangeObjectThermal (type, ptr1, ptr2, ptr3, tstyle);
1201 	      }
1202 	    else if (GET_THERM (INDEXOFCURRENT, (PinType *) ptr3))
1203 	      ChangeObjectThermal (type, ptr1, ptr2, ptr3, 0);
1204 	    else
1205 	      ChangeObjectThermal (type, ptr1, ptr2, ptr3, PCB->ThermStyle);
1206 	  }
1207 	break;
1208       }
1209 
1210     case LINE_MODE:
1211       /* do update of position */
1212       NotifyLine ();
1213       if (Crosshair.AttachedLine.State != STATE_THIRD)
1214 	break;
1215 
1216       /* Remove anchor if clicking on start point;
1217        * this means we can't paint 0 length lines
1218        * which could be used for square SMD pads.
1219        * Instead use a very small delta, or change
1220        * the file after saving.
1221        */
1222       if (Crosshair.X == Crosshair.AttachedLine.Point1.X
1223 	  && Crosshair.Y == Crosshair.AttachedLine.Point1.Y)
1224 	{
1225 	  SetMode (LINE_MODE);
1226 	  break;
1227 	}
1228 
1229       if (PCB->RatDraw)
1230 	{
1231 	  RatType *line;
1232 	  if ((line = AddNet ()))
1233 	    {
1234 	      addedLines++;
1235 	      AddObjectToCreateUndoList (RATLINE_TYPE, line, line, line);
1236 	      IncrementUndoSerialNumber ();
1237 	      DrawRat (line);
1238 	      Crosshair.AttachedLine.Point1.X =
1239 		Crosshair.AttachedLine.Point2.X;
1240 	      Crosshair.AttachedLine.Point1.Y =
1241 		Crosshair.AttachedLine.Point2.Y;
1242 	      Draw ();
1243 	    }
1244 	  break;
1245 	}
1246       else
1247 	/* create line if both ends are determined && length != 0 */
1248 	{
1249 	  LineType *line;
1250 	  int line_flags = 0;
1251 
1252 	  if (TEST_FLAG (AUTODRCFLAG, PCB) && !TEST_SILK_LAYER (CURRENT))
1253 	    line_flags |= CONNECTEDFLAG | FOUNDFLAG;
1254 
1255 	  if (TEST_FLAG (CLEARNEWFLAG, PCB))
1256 	    line_flags |= CLEARLINEFLAG;
1257 
1258 	  if (PCB->Clipping
1259 	      && Crosshair.AttachedLine.Point1.X ==
1260 	      Crosshair.AttachedLine.Point2.X
1261 	      && Crosshair.AttachedLine.Point1.Y ==
1262 	      Crosshair.AttachedLine.Point2.Y
1263 	      && (Crosshair.AttachedLine.Point2.X != Note.X
1264 		  || Crosshair.AttachedLine.Point2.Y != Note.Y))
1265 	    {
1266 	      /* We will only need to paint the second line segment.
1267 	         Since we only check for vias on the first segment,
1268 	         swap them so the non-empty segment is the first segment. */
1269 	      Crosshair.AttachedLine.Point2.X = Note.X;
1270 	      Crosshair.AttachedLine.Point2.Y = Note.Y;
1271 	    }
1272 
1273 	  if ((Crosshair.AttachedLine.Point1.X !=
1274 	       Crosshair.AttachedLine.Point2.X
1275 	       || Crosshair.AttachedLine.Point1.Y !=
1276 	       Crosshair.AttachedLine.Point2.Y))
1277             {
1278               PinType *via;
1279 	      Cardinal layer_from, layer_to;
1280 
1281               if ((line =
1282 		  CreateDrawnLineOnLayer (CURRENT,
1283 					  Crosshair.AttachedLine.Point1.X,
1284 					  Crosshair.AttachedLine.Point1.Y,
1285 					  Crosshair.AttachedLine.Point2.X,
1286 					  Crosshair.AttachedLine.Point2.Y,
1287 					  Settings.LineThickness,
1288 					  2 * Settings.Keepaway,
1289 					  MakeFlags (line_flags))) != NULL)
1290                 {
1291                   addedLines++;
1292                   AddObjectToCreateUndoList (LINE_TYPE, CURRENT, line, line);
1293                   DrawLine (CURRENT, line);
1294                 }
1295 	      /* place a via if vias are visible, the layer is
1296 	         in a new group since the last line and there
1297 	         isn't a pin already here */
1298 	      if (TEST_FLAG (AUTOBURIEDVIASFLAG, PCB))
1299 		{
1300 		  layer_from = GetLayerNumber (PCB->Data, lastLayer);
1301 		  layer_to = GetLayerNumber (PCB->Data, CURRENT);
1302 		}
1303 	      else
1304 		{
1305 		  layer_from = 0;
1306 		  layer_to = 0;
1307 		}
1308 
1309 	      if (PCB->ViaOn && GetLayerGroupNumberByPointer (CURRENT) !=
1310 		  GetLayerGroupNumberByPointer (lastLayer) &&
1311 		  SearchObjectByLocation (PIN_TYPES, &ptr1, &ptr2, &ptr3,
1312 					  Crosshair.AttachedLine.Point1.X,
1313 					  Crosshair.AttachedLine.Point1.Y,
1314 					  Settings.ViaThickness / 2) ==
1315 		  NO_TYPE
1316 		  && (via =
1317 		      CreateNewViaEx (PCB->Data,
1318 				    Crosshair.AttachedLine.Point1.X,
1319 				    Crosshair.AttachedLine.Point1.Y,
1320 				    Settings.ViaThickness,
1321 				    2 * Settings.Keepaway, Settings.ViaMaskAperture,
1322 				    Settings.ViaDrillingHole, NULL,
1323 				    NoFlags (), layer_from, layer_to)) != NULL)
1324 		{
1325 		  AddObjectToCreateUndoList (VIA_TYPE, via, via, via);
1326 		  DrawVia (via);
1327 		}
1328 	      /* copy the coordinates */
1329 	      Crosshair.AttachedLine.Point1.X =
1330 		Crosshair.AttachedLine.Point2.X;
1331 	      Crosshair.AttachedLine.Point1.Y =
1332 		Crosshair.AttachedLine.Point2.Y;
1333 	      IncrementUndoSerialNumber ();
1334 	      lastLayer = CURRENT;
1335 	    }
1336 	  if (PCB->Clipping && (Note.X != Crosshair.AttachedLine.Point2.X
1337 				|| Note.Y !=
1338 				Crosshair.AttachedLine.Point2.Y))
1339             {
1340               if ((line =
1341 		  CreateDrawnLineOnLayer (CURRENT,
1342 					  Crosshair.AttachedLine.Point2.X,
1343 					  Crosshair.AttachedLine.Point2.Y,
1344 					  Note.X, Note.Y,
1345 					  Settings.LineThickness,
1346 					  2 * Settings.Keepaway,
1347 					  MakeFlags (line_flags))) != NULL)
1348                 {
1349                   addedLines++;
1350                   AddObjectToCreateUndoList (LINE_TYPE, CURRENT, line, line);
1351                   IncrementUndoSerialNumber ();
1352                   DrawLine (CURRENT, line);
1353                 }
1354 	      /* move to new start point */
1355 	      Crosshair.AttachedLine.Point1.X = Note.X;
1356 	      Crosshair.AttachedLine.Point1.Y = Note.Y;
1357 	      Crosshair.AttachedLine.Point2.X = Note.X;
1358 	      Crosshair.AttachedLine.Point2.Y = Note.Y;
1359 	      if (TEST_FLAG (SWAPSTARTDIRFLAG, PCB))
1360 		{
1361 		  PCB->Clipping ^= 3;
1362 		}
1363 	    }
1364 	  if (TEST_FLAG (AUTODRCFLAG, PCB) && !TEST_SILK_LAYER (CURRENT))
1365 	    LookupConnection (Note.X, Note.Y, true, 1, CONNECTEDFLAG, false);
1366 	  Draw ();
1367 	}
1368       break;
1369 
1370     case RECTANGLE_MODE:
1371       /* do update of position */
1372       NotifyBlock ();
1373 
1374       /* create rectangle if both corners are determined
1375        * and width, height are != 0
1376        */
1377       if (Crosshair.AttachedBox.State == STATE_THIRD &&
1378 	  Crosshair.AttachedBox.Point1.X != Crosshair.AttachedBox.Point2.X &&
1379 	  Crosshair.AttachedBox.Point1.Y != Crosshair.AttachedBox.Point2.Y)
1380 	{
1381 	  PolygonType *polygon;
1382 
1383 	  int flags = CLEARPOLYFLAG;
1384 	  if (TEST_FLAG (NEWFULLPOLYFLAG, PCB))
1385 	    flags |= FULLPOLYFLAG;
1386 	  if ((polygon = CreateNewPolygonFromRectangle (CURRENT,
1387 							Crosshair.
1388 							AttachedBox.Point1.X,
1389 							Crosshair.
1390 							AttachedBox.Point1.Y,
1391 							Crosshair.
1392 							AttachedBox.Point2.X,
1393 							Crosshair.
1394 							AttachedBox.Point2.Y,
1395 							MakeFlags
1396 							(flags))) !=
1397 	      NULL)
1398 	    {
1399 	      AddObjectToCreateUndoList (POLYGON_TYPE, CURRENT,
1400 					 polygon, polygon);
1401 	      IncrementUndoSerialNumber ();
1402 	      DrawPolygon (CURRENT, polygon);
1403 	      Draw ();
1404 	    }
1405 
1406 	  /* reset state to 'first corner' */
1407 	  Crosshair.AttachedBox.State = STATE_FIRST;
1408 	}
1409       break;
1410 
1411     case TEXT_MODE:
1412       {
1413 	char *string;
1414 
1415 	if ((string = gui->prompt_for (_("Enter text:"), "")) != NULL)
1416 	  {
1417 	    if (strlen(string) > 0)
1418 	      {
1419 		TextType *text;
1420 		int flag = CLEARLINEFLAG;
1421 
1422 		if (GetLayerGroupNumberByNumber (INDEXOFCURRENT) ==
1423 		    GetLayerGroupNumberBySide (BOTTOM_SIDE))
1424 		  flag |= ONSOLDERFLAG;
1425 		if ((text = CreateNewText (CURRENT, &PCB->Font, Note.X,
1426 					   Note.Y, 0, Settings.TextScale,
1427 					   string, MakeFlags (flag))) != NULL)
1428 		  {
1429 		    AddObjectToCreateUndoList (TEXT_TYPE, CURRENT, text, text);
1430 		    IncrementUndoSerialNumber ();
1431 		    DrawText (CURRENT, text);
1432 		    Draw ();
1433 		  }
1434 		}
1435 	    free (string);
1436 	  }
1437 	break;
1438       }
1439 
1440     case POLYGON_MODE:
1441       {
1442 	PointType *points = Crosshair.AttachedPolygon.Points;
1443 	Cardinal n = Crosshair.AttachedPolygon.PointN;
1444 
1445 	/* do update of position; use the 'LINE_MODE' mechanism */
1446 	NotifyLine ();
1447 
1448 	/* check if this is the last point of a polygon */
1449 	if (n >= 3 &&
1450 	    points->X == Crosshair.AttachedLine.Point2.X &&
1451 	    points->Y == Crosshair.AttachedLine.Point2.Y)
1452 	  {
1453 	    CopyAttachedPolygonToLayer ();
1454 	    Draw ();
1455 	    break;
1456 	  }
1457 
1458 	/* create new point if it's the first one or if it's
1459 	 * different to the last one
1460 	 */
1461 	if (!n ||
1462 	    points[n - 1].X != Crosshair.AttachedLine.Point2.X ||
1463 	    points[n - 1].Y != Crosshair.AttachedLine.Point2.Y)
1464 	  {
1465 	    CreateNewPointInPolygon (&Crosshair.AttachedPolygon,
1466 				     Crosshair.AttachedLine.Point2.X,
1467 				     Crosshair.AttachedLine.Point2.Y);
1468 
1469 	    /* copy the coordinates */
1470 	    Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X;
1471 	    Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y;
1472 	  }
1473 	break;
1474       }
1475 
1476     case POLYGONHOLE_MODE:
1477       {
1478 	switch (Crosshair.AttachedObject.State)
1479 	  {
1480 	    /* first notify, lookup object */
1481 	  case STATE_FIRST:
1482 	    Crosshair.AttachedObject.Type =
1483 	      SearchScreen (Note.X, Note.Y, POLYGON_TYPE,
1484 			    &Crosshair.AttachedObject.Ptr1,
1485 			    &Crosshair.AttachedObject.Ptr2,
1486 			    &Crosshair.AttachedObject.Ptr3);
1487 
1488           if (Crosshair.AttachedObject.Type == NO_TYPE)
1489             {
1490               Message (_("The first point of a polygon hole must be on a polygon.\n"));
1491               break; /* don't start doing anything if clicked outside of polys */
1492             }
1493 
1494           if (TEST_FLAG(LOCKFLAG, (PolygonType *) Crosshair.AttachedObject.Ptr2))
1495             {
1496               Message (_("Sorry, the object is locked\n"));
1497               Crosshair.AttachedObject.Type = NO_TYPE;
1498               break;
1499             }
1500           else
1501             Crosshair.AttachedObject.State = STATE_SECOND;
1502             /* Fall thru: first click is also the first point of the
1503              * poly hole. */
1504 
1505             /* second notify, insert new point into object */
1506           case STATE_SECOND:
1507             {
1508 	      PointType *points = Crosshair.AttachedPolygon.Points;
1509 	      Cardinal n = Crosshair.AttachedPolygon.PointN;
1510 	      POLYAREA *original, *new_hole, *result;
1511 	      FlagType Flags;
1512 
1513 	      /* do update of position; use the 'LINE_MODE' mechanism */
1514 	      NotifyLine ();
1515 
1516 	      /* check if this is the last point of a polygon */
1517 	      if (n >= 3 &&
1518 		  points->X == Crosshair.AttachedLine.Point2.X &&
1519 		  points->Y == Crosshair.AttachedLine.Point2.Y)
1520 		{
1521 		  /* Create POLYAREAs from the original polygon
1522 		   * and the new hole polygon */
1523 		  original = PolygonToPoly ((PolygonType *)Crosshair.AttachedObject.Ptr2);
1524 		  new_hole = PolygonToPoly (&Crosshair.AttachedPolygon);
1525 
1526 		  /* Subtract the hole from the original polygon shape */
1527 		  poly_Boolean_free (original, new_hole, &result, PBO_SUB);
1528 
1529 		  /* Convert the resulting polygon(s) into a new set of nodes
1530 		   * and place them on the page. Delete the original polygon.
1531 		   */
1532 		  SaveUndoSerialNumber ();
1533 		  Flags = ((PolygonType *)Crosshair.AttachedObject.Ptr2)->Flags;
1534 		  PolyToPolygonsOnLayer (PCB->Data, (LayerType *)Crosshair.AttachedObject.Ptr1,
1535 					 result, Flags);
1536 		  RemoveObject (POLYGON_TYPE,
1537 				Crosshair.AttachedObject.Ptr1,
1538 				Crosshair.AttachedObject.Ptr2,
1539 				Crosshair.AttachedObject.Ptr3);
1540 		  RestoreUndoSerialNumber ();
1541 		  IncrementUndoSerialNumber ();
1542 		  Draw ();
1543 
1544 		/* reset state of attached line */
1545 		memset (&Crosshair.AttachedPolygon, 0, sizeof (PolygonType));
1546 		Crosshair.AttachedObject.State = STATE_FIRST;
1547 		addedLines = 0;
1548 
1549 		  break;
1550 		}
1551 
1552 	      /* create new point if it's the first one or if it's
1553 	       * different to the last one
1554 	       */
1555 	      if (!n ||
1556 		  points[n - 1].X != Crosshair.AttachedLine.Point2.X ||
1557 		  points[n - 1].Y != Crosshair.AttachedLine.Point2.Y)
1558 		{
1559 		  CreateNewPointInPolygon (&Crosshair.AttachedPolygon,
1560 					   Crosshair.AttachedLine.Point2.X,
1561 					   Crosshair.AttachedLine.Point2.Y);
1562 
1563 		  /* copy the coordinates */
1564 		  Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X;
1565 		  Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y;
1566 		}
1567 	      break;
1568 	    }
1569 	  }
1570 
1571 	break;
1572       }
1573 
1574     case PASTEBUFFER_MODE:
1575       {
1576 	TextType estr[MAX_ELEMENTNAMES];
1577 	ElementType *e = 0;
1578 
1579 	if (gui->shift_is_pressed ())
1580 	  {
1581 	    int type =
1582 	      SearchScreen (Note.X, Note.Y, ELEMENT_TYPE, &ptr1, &ptr2,
1583 			    &ptr3);
1584 	    if (type == ELEMENT_TYPE)
1585 	      {
1586 		e = (ElementType *) ptr1;
1587 		if (e)
1588 		  {
1589 		    int i;
1590 
1591 		    memcpy (estr, e->Name,
1592 			    MAX_ELEMENTNAMES * sizeof (TextType));
1593 		    for (i = 0; i < MAX_ELEMENTNAMES; ++i)
1594 		      estr[i].TextString = estr[i].TextString ? strdup(estr[i].TextString) : NULL;
1595 		    RemoveElement (e);
1596 		  }
1597 	      }
1598 	  }
1599 	if (CopyPastebufferToLayout (Note.X, Note.Y))
1600 	  SetChangedFlag (true);
1601 	if (e)
1602 	  {
1603 	    int type =
1604 	      SearchScreen (Note.X, Note.Y, ELEMENT_TYPE, &ptr1, &ptr2,
1605 			    &ptr3);
1606 	    if (type == ELEMENT_TYPE && ptr1)
1607 	      {
1608 		int i, save_n;
1609 		e = (ElementType *) ptr1;
1610 
1611 		save_n = NAME_INDEX (PCB);
1612 
1613 		for (i = 0; i < MAX_ELEMENTNAMES; i++)
1614 		  {
1615 		    if (i == save_n)
1616 		      EraseElementName (e);
1617 		    r_delete_entry (PCB->Data->name_tree[i],
1618 				    (BoxType *) & (e->Name[i]));
1619 		    memcpy (&(e->Name[i]), &(estr[i]), sizeof (TextType));
1620 		    e->Name[i].Element = e;
1621 		    SetTextBoundingBox (&PCB->Font, &(e->Name[i]));
1622 		    r_insert_entry (PCB->Data->name_tree[i],
1623 				    (BoxType *) & (e->Name[i]), 0);
1624 		    if (i == save_n)
1625 		      DrawElementName (e);
1626 		  }
1627 	      }
1628 	  }
1629 	break;
1630       }
1631 
1632     case REMOVE_MODE:
1633       if ((type =
1634 	   SearchScreen (Note.X, Note.Y, REMOVE_TYPES, &ptr1, &ptr2,
1635 			 &ptr3)) != NO_TYPE)
1636 	{
1637 	  if (TEST_FLAG (LOCKFLAG, (LineType *) ptr2))
1638 	    {
1639 	      Message (_("Sorry, the object is locked\n"));
1640 	      break;
1641 	    }
1642 	  if (type == ELEMENT_TYPE)
1643 	    {
1644 	      RubberbandType *ptr;
1645 	      int i;
1646 
1647 	      Crosshair.AttachedObject.RubberbandN = 0;
1648 	      LookupRatLines (type, ptr1, ptr2, ptr3);
1649 	      ptr = Crosshair.AttachedObject.Rubberband;
1650 	      for (i = 0; i < Crosshair.AttachedObject.RubberbandN; i++)
1651 		{
1652 		  if (PCB->RatOn)
1653 		    EraseRat ((RatType *) ptr->Line);
1654                   if (TEST_FLAG (RUBBERENDFLAG, ptr->Line))
1655 		    MoveObjectToRemoveUndoList (RATLINE_TYPE,
1656 					        ptr->Line, ptr->Line,
1657 					        ptr->Line);
1658                   else
1659                     TOGGLE_FLAG (RUBBERENDFLAG, ptr->Line); /* only remove line once */
1660 		  ptr++;
1661 		}
1662 	    }
1663 	  RemoveObject (type, ptr1, ptr2, ptr3);
1664 	  IncrementUndoSerialNumber ();
1665 	  SetChangedFlag (true);
1666 	}
1667       break;
1668 
1669     case ROTATE_MODE:
1670       RotateScreenObject (Note.X, Note.Y,
1671 			  gui->shift_is_pressed ()? (SWAP_IDENT ?
1672 						     1 : 3)
1673 			  : (SWAP_IDENT ? 3 : 1));
1674       break;
1675 
1676       /* both are almost the same */
1677     case COPY_MODE:
1678     case MOVE_MODE:
1679       switch (Crosshair.AttachedObject.State)
1680 	{
1681 	  /* first notify, lookup object */
1682 	case STATE_FIRST:
1683 	  {
1684 	    int types = (Settings.Mode == COPY_MODE) ?
1685 	      COPY_TYPES : MOVE_TYPES;
1686 
1687 	    Crosshair.AttachedObject.Type =
1688 	      SearchScreen (Note.X, Note.Y, types,
1689 			    &Crosshair.AttachedObject.Ptr1,
1690 			    &Crosshair.AttachedObject.Ptr2,
1691 			    &Crosshair.AttachedObject.Ptr3);
1692 	    if (Crosshair.AttachedObject.Type != NO_TYPE)
1693 	      {
1694 		if (Settings.Mode == MOVE_MODE &&
1695 		    TEST_FLAG (LOCKFLAG, (PinType *)
1696 			       Crosshair.AttachedObject.Ptr2))
1697 		  {
1698 		    Message (_("Sorry, the object is locked\n"));
1699 		    Crosshair.AttachedObject.Type = NO_TYPE;
1700 		  }
1701 		else
1702 		  AttachForCopy (Note.X, Note.Y);
1703 	      }
1704 	    break;
1705 	  }
1706 
1707 	  /* second notify, move or copy object */
1708 	case STATE_SECOND:
1709 	  if (Settings.Mode == COPY_MODE)
1710 	    CopyObject (Crosshair.AttachedObject.Type,
1711 			Crosshair.AttachedObject.Ptr1,
1712 			Crosshair.AttachedObject.Ptr2,
1713 			Crosshair.AttachedObject.Ptr3,
1714 			Note.X - Crosshair.AttachedObject.X,
1715 			Note.Y - Crosshair.AttachedObject.Y);
1716 	  else
1717 	    {
1718 	      MoveObjectAndRubberband (Crosshair.AttachedObject.Type,
1719 				       Crosshair.AttachedObject.Ptr1,
1720 				       Crosshair.AttachedObject.Ptr2,
1721 				       Crosshair.AttachedObject.Ptr3,
1722 				       Note.X - Crosshair.AttachedObject.X,
1723 				       Note.Y - Crosshair.AttachedObject.Y);
1724 	      SetLocalRef (0, 0, false);
1725 	    }
1726 	  SetChangedFlag (true);
1727 
1728 	  /* reset identifiers */
1729 	  Crosshair.AttachedObject.Type = NO_TYPE;
1730 	  Crosshair.AttachedObject.State = STATE_FIRST;
1731 	  break;
1732 	}
1733       break;
1734 
1735       /* insert a point into a polygon/line/... */
1736     case INSERTPOINT_MODE:
1737       switch (Crosshair.AttachedObject.State)
1738 	{
1739 	  /* first notify, lookup object */
1740 	case STATE_FIRST:
1741 	  Crosshair.AttachedObject.Type =
1742 	    SearchScreen (Note.X, Note.Y, INSERT_TYPES,
1743 			  &Crosshair.AttachedObject.Ptr1,
1744 			  &Crosshair.AttachedObject.Ptr2,
1745 			  &Crosshair.AttachedObject.Ptr3);
1746 
1747 	  if (Crosshair.AttachedObject.Type != NO_TYPE)
1748 	    {
1749 	      if (TEST_FLAG (LOCKFLAG, (PolygonType *)
1750 			     Crosshair.AttachedObject.Ptr2))
1751 		{
1752 		  Message (_("Sorry, the object is locked\n"));
1753 		  Crosshair.AttachedObject.Type = NO_TYPE;
1754 		  break;
1755 		}
1756 	      else
1757 		{
1758 		  /* get starting point of nearest segment */
1759 		  if (Crosshair.AttachedObject.Type == POLYGON_TYPE)
1760 		    {
1761 		      fake.poly =
1762 			(PolygonType *) Crosshair.AttachedObject.Ptr2;
1763 		      polyIndex =
1764 			GetLowestDistancePolygonPoint (fake.poly, Note.X,
1765 						       Note.Y);
1766 		      fake.line.Point1 = fake.poly->Points[polyIndex];
1767 		      fake.line.Point2 = fake.poly->Points[
1768 			  prev_contour_point (fake.poly, polyIndex)];
1769 		      Crosshair.AttachedObject.Ptr2 = &fake.line;
1770 
1771 		    }
1772 		  Crosshair.AttachedObject.State = STATE_SECOND;
1773 		  InsertedPoint = *AdjustInsertPoint ();
1774 		}
1775 	    }
1776 	  break;
1777 
1778 	  /* second notify, insert new point into object */
1779 	case STATE_SECOND:
1780 	  if (Crosshair.AttachedObject.Type == POLYGON_TYPE)
1781 	    InsertPointIntoObject (POLYGON_TYPE,
1782 				   Crosshair.AttachedObject.Ptr1, fake.poly,
1783 				   &polyIndex,
1784 				   InsertedPoint.X, InsertedPoint.Y, false, false);
1785 	  else
1786 	    InsertPointIntoObject (Crosshair.AttachedObject.Type,
1787 				   Crosshair.AttachedObject.Ptr1,
1788 				   Crosshair.AttachedObject.Ptr2,
1789 				   &polyIndex,
1790 				   InsertedPoint.X, InsertedPoint.Y, false, false);
1791 	  SetChangedFlag (true);
1792 
1793 	  /* reset identifiers */
1794 	  Crosshair.AttachedObject.Type = NO_TYPE;
1795 	  Crosshair.AttachedObject.State = STATE_FIRST;
1796 	  break;
1797 	}
1798       break;
1799     }
1800 }
1801 
1802 
1803 /* --------------------------------------------------------------------------- */
1804 
1805 static const char atomic_syntax[] = N_("Atomic(Save|Restore|Close|Block)");
1806 
1807 static const char atomic_help[] = N_("Save or restore the undo serial number.");
1808 
1809 /* %start-doc actions Atomic
1810 
1811 This action allows making multiple-action bindings into an atomic
1812 operation that will be undone by a single Undo command.  For example,
1813 to optimize rat lines, you'd delete the rats and re-add them.  To
1814 group these into a single undo, you'd want the deletions and the
1815 additions to have the same undo serial number.  So, you @code{Save},
1816 delete the rats, @code{Restore}, add the rats - using the same serial
1817 number as the deletes, then @code{Block}, which checks to see if the
1818 deletions or additions actually did anything.  If not, the serial
1819 number is set to the saved number, as there's nothing to undo.  If
1820 something did happen, the serial number is incremented so that these
1821 actions are counted as a single undo step.
1822 
1823 @table @code
1824 
1825 @item Save
1826 Saves the undo serial number.
1827 
1828 @item Restore
1829 Returns it to the last saved number.
1830 
1831 @item Close
1832 Sets it to 1 greater than the last save.
1833 
1834 @item Block
1835 Does a Restore if there was nothing to undo, else does a Close.
1836 
1837 @end table
1838 
1839 %end-doc */
1840 
1841 static int
ActionAtomic(int argc,char ** argv,Coord x,Coord y)1842 ActionAtomic (int argc, char **argv, Coord x, Coord y)
1843 {
1844   if (argc != 1)
1845     AFAIL (atomic);
1846 
1847   switch (GetFunctionID (argv[0]))
1848     {
1849     case F_Save:
1850       SaveUndoSerialNumber ();
1851       break;
1852     case F_Restore:
1853       RestoreUndoSerialNumber ();
1854       break;
1855     case F_Close:
1856       RestoreUndoSerialNumber ();
1857       IncrementUndoSerialNumber ();
1858       break;
1859     case F_Block:
1860       RestoreUndoSerialNumber ();
1861       if (Bumped)
1862 	IncrementUndoSerialNumber ();
1863       break;
1864     }
1865   return 0;
1866 }
1867 
1868 
1869 /* -------------------------------------------------------------------------- */
1870 
1871 static const char dumplibrary_syntax[] = N_("DumpLibrary()");
1872 
1873 static const char dumplibrary_help[] =
1874   N_("Display the entire contents of the libraries.");
1875 
1876 /* %start-doc actions DumpLibrary
1877 
1878 
1879 %end-doc */
1880 
1881 static int
ActionDumpLibrary(int argc,char ** argv,Coord x,Coord y)1882 ActionDumpLibrary (int argc, char **argv, Coord x, Coord y)
1883 {
1884   int i, j;
1885 
1886   printf ("**** Do not count on this format.  It will change ****\n\n");
1887   printf ("MenuN   = %d\n", (int) Library.MenuN);
1888   printf ("MenuMax = %d\n", (int) Library.MenuMax);
1889   for (i = 0; i < Library.MenuN; i++)
1890     {
1891       printf ("Library #%d:\n", i);
1892       printf ("    EntryN    = %d\n", (int) Library.Menu[i].EntryN);
1893       printf ("    EntryMax  = %d\n", (int) Library.Menu[i].EntryMax);
1894       printf ("    Name      = \"%s\"\n", UNKNOWN (Library.Menu[i].Name));
1895       printf ("    directory = \"%s\"\n",
1896 	      UNKNOWN (Library.Menu[i].directory));
1897       printf ("    Style     = \"%s\"\n", UNKNOWN (Library.Menu[i].Style));
1898       printf ("    flag      = %d\n", Library.Menu[i].flag);
1899 
1900       for (j = 0; j < Library.Menu[i].EntryN; j++)
1901 	{
1902 	  printf ("    #%4d: ", j);
1903 	  if (Library.Menu[i].Entry[j].Template == (char *) -1)
1904 	    {
1905 	      printf ("newlib: \"%s\"\n",
1906 		      UNKNOWN (Library.Menu[i].Entry[j].ListEntry));
1907 	    }
1908 	  else
1909 	    {
1910 	      printf ("\"%s\", \"%s\", \"%s\", \"%s\", \"%s\"\n",
1911 		      UNKNOWN (Library.Menu[i].Entry[j].ListEntry),
1912 		      UNKNOWN (Library.Menu[i].Entry[j].Template),
1913 		      UNKNOWN (Library.Menu[i].Entry[j].Package),
1914 		      UNKNOWN (Library.Menu[i].Entry[j].Value),
1915 		      UNKNOWN (Library.Menu[i].Entry[j].Description));
1916 	    }
1917 	}
1918     }
1919 
1920   return 0;
1921 }
1922 
1923 /* -------------------------------------------------------------------------- */
1924 
1925 static const char flip_syntax[] = N_("Flip(Object|Selected|SelectedElements)");
1926 
1927 static const char flip_help[] =
1928   N_("Flip an element to the opposite side of the board.");
1929 
1930 /* %start-doc actions Flip
1931 
1932 Note that the location of the element will be symmetric about the
1933 cursor location; i.e. if the part you are pointing at will still be at
1934 the same spot once the element is on the other side.  When flipping
1935 multiple elements, this retains their positions relative to each
1936 other, not their absolute positions on the board.
1937 
1938 %end-doc */
1939 
1940 static int
ActionFlip(int argc,char ** argv,Coord x,Coord y)1941 ActionFlip (int argc, char **argv, Coord x, Coord y)
1942 {
1943   char *function = ARG (0);
1944   ElementType *element;
1945   void *ptrtmp;
1946   int err = 0;
1947 
1948   if (function)
1949     {
1950       switch (GetFunctionID (function))
1951 	{
1952 	case F_Object:
1953 	  if ((SearchScreen (x, y, ELEMENT_TYPE,
1954 			     &ptrtmp, &ptrtmp, &ptrtmp)) != NO_TYPE)
1955 	    {
1956 	      element = (ElementType *) ptrtmp;
1957 	      ChangeElementSide (element, 2 * Crosshair.Y - PCB->MaxHeight);
1958 	      IncrementUndoSerialNumber ();
1959 	      Draw ();
1960 	    }
1961 	  break;
1962 	case F_Selected:
1963 	case F_SelectedElements:
1964 	  ChangeSelectedElementSide ();
1965 	  break;
1966 	default:
1967 	  err = 1;
1968 	  break;
1969 	}
1970       if (!err)
1971 	return 0;
1972     }
1973 
1974   AFAIL (flip);
1975 }
1976 
1977 /* -------------------------------------------------------------------------- */
1978 
1979 static const char message_syntax[] = N_("Message(message)");
1980 
1981 static const char message_help[] = N_("Writes a message to the log window.");
1982 
1983 /* %start-doc actions Message
1984 
1985 This action displays a message to the log window.  This action is primarily
1986 provided for use by other programs which may interface with PCB.  If
1987 multiple arguments are given, each one is sent to the log window
1988 followed by a newline.
1989 
1990 %end-doc */
1991 
1992 static int
ActionMessage(int argc,char ** argv,Coord x,Coord y)1993 ActionMessage (int argc, char **argv, Coord x, Coord y)
1994 {
1995   int i;
1996 
1997   if (argc < 1)
1998     AFAIL (message);
1999 
2000   for (i = 0; i < argc; i++)
2001     {
2002       Message (argv[i]);
2003       Message ("\n");
2004     }
2005 
2006   return 0;
2007 }
2008 
2009 
2010 /* -------------------------------------------------------------------------- */
2011 
2012 static const char setthermal_syntax[] =
2013   "SetThermal(Object|SelectedPins|SelectedVias|Selected, Style)";
2014 
2015 static const char setthermal_help[] =
2016   N_("Set the thermal (on the current layer) of pins or vias to the given style.\n"
2017   "Style = 0 means no thermal.\n"
2018   "Style = 1 has diagonal fingers with sharp edges.\n"
2019   "Style = 2 has horizontal and vertical fingers with sharp edges.\n"
2020   "Style = 3 is a solid connection to the plane.\n"
2021   "Style = 4 has diagonal fingers with rounded edges.\n"
2022   "Style = 5 has horizontal and vertical fingers with rounded edges.\n");
2023 
2024 /* %start-doc actions SetThermal
2025 
2026 This changes how/whether pins or vias connect to any rectangle or polygon
2027 on the current layer. The first argument can specify one object, or all
2028 selected pins, or all selected vias, or all selected pins and vias.
2029 The second argument specifies the style of connection.
2030 There are 5 possibilities:
2031 0 - no connection,
2032 1 - 45 degree fingers with sharp edges,
2033 2 - horizontal & vertical fingers with sharp edges,
2034 3 - solid connection,
2035 4 - 45 degree fingers with rounded corners,
2036 5 - horizontal & vertical fingers with rounded corners.
2037 
2038 Pins and Vias may have thermals whether or not there is a polygon available
2039 to connect with. However, they will have no effect without the polygon.
2040 %end-doc */
2041 
2042 static int
ActionSetThermal(int argc,char ** argv,Coord x,Coord y)2043 ActionSetThermal (int argc, char **argv, Coord x, Coord y)
2044 {
2045   char *function = ARG (0);
2046   char *style = ARG (1);
2047   void *ptr1, *ptr2, *ptr3;
2048   int type, kind;
2049   int err = 0;
2050 
2051   if (function && *function)
2052     {
2053       bool absolute;
2054 
2055       if ( ! style || ! *style)
2056 	{
2057 	  kind = PCB->ThermStyle;
2058 	  absolute = true;
2059 	}
2060       else
2061 	kind = GetUnitlessValue (style, &absolute);
2062 
2063       /* To allow relative values we could search for the first selected
2064 	 item and make 'kind' relative to that, but that's not too useful
2065 	 and requires quite some code. For example there's no
2066 	 GetFirstSelectedPin() function available. Let's postpone this
2067 	 functionality, there are more urgent things to do. */
2068 
2069       if (absolute)
2070 	switch (GetFunctionID (function))
2071 	  {
2072 	  case F_Object:
2073 	    if ((type =
2074 		 SearchScreen (Crosshair.X, Crosshair.Y, CHANGETHERMAL_TYPES,
2075 			       &ptr1, &ptr2, &ptr3)) != NO_TYPE)
2076 	      {
2077 		ChangeObjectThermal (type, ptr1, ptr2, ptr3, kind);
2078 		IncrementUndoSerialNumber ();
2079 		Draw ();
2080 	      }
2081 	    break;
2082 	  case F_SelectedPins:
2083 	    ChangeSelectedThermals (PIN_TYPE, kind);
2084 	    break;
2085 	  case F_SelectedVias:
2086 	    ChangeSelectedThermals (VIA_TYPE, kind);
2087 	    break;
2088 	  case F_Selected:
2089 	  case F_SelectedElements:
2090 	    ChangeSelectedThermals (CHANGETHERMAL_TYPES, kind);
2091 	    break;
2092 	  default:
2093 	    err = 1;
2094 	    break;
2095 	  }
2096       else
2097 	err = 1;
2098     }
2099   else
2100     err = 1;
2101 
2102   if (err)
2103     AFAIL (setthermal);
2104 
2105   return 0;
2106 }
2107 
2108 /*!
2109  * \brief Event handler to set the cursor according to the X pointer
2110  * position called from inside main.c.
2111  *
2112  * \warning !!! no action routine !!!
2113  */
2114 void
EventMoveCrosshair(int ev_x,int ev_y)2115 EventMoveCrosshair (int ev_x, int ev_y)
2116 {
2117 #ifdef HAVE_LIBSTROKE
2118   if (mid_stroke)
2119     {
2120       StrokeBox.X2 = ev_x;
2121       StrokeBox.Y2 = ev_y;
2122       stroke_record (ev_x, ev_y);
2123       return;
2124     }
2125 #endif /* HAVE_LIBSTROKE */
2126   if (MoveCrosshairAbsolute (ev_x, ev_y))
2127     {
2128       /* update object position and cursor location */
2129       AdjustAttachedObjects ();
2130       notify_crosshair_change (true);
2131     }
2132 }
2133 
2134 /* --------------------------------------------------------------------------- */
2135 
2136 static const char setvalue_syntax[] =
2137   N_("SetValue(Grid|Line|LineSize|Text|TextScale|ViaDrillingHole|Via|ViaSize, "
2138       "delta)");
2139 
2140 static const char setvalue_help[] =
2141   N_("Change various board-wide values and sizes.");
2142 
2143 /* %start-doc actions SetValue
2144 
2145 @table @code
2146 
2147 @item ViaDrillingHole
2148 Changes the diameter of the drill for new vias.
2149 
2150 @item Grid
2151 Sets the grid spacing.
2152 
2153 @item Line
2154 @item LineSize
2155 Changes the thickness of new lines.
2156 
2157 @item Via
2158 @item ViaSize
2159 Changes the diameter of new vias.
2160 
2161 @item Text
2162 @item TextScale
2163 Changes the size of new text.
2164 
2165 @end table
2166 
2167 %end-doc */
2168 
2169 static int
ActionSetValue(int argc,char ** argv,Coord x,Coord y)2170 ActionSetValue (int argc, char **argv, Coord x, Coord y)
2171 {
2172   char *function = ARG (0);
2173   char *val = ARG (1);
2174   char *units = ARG (2);
2175   bool absolute;			/* flag for 'absolute' value */
2176   double value;
2177   int text_scale;
2178   int err = 0;
2179 
2180   if (function && val)
2181     {
2182       value = GetValue (val, units, &absolute);
2183       switch (GetFunctionID (function))
2184 	{
2185 	case F_ViaDrillingHole:
2186 	  SetViaDrillingHole (absolute ? value :
2187                                 value + Settings.ViaDrillingHole,
2188 			      false);
2189 	  hid_action ("RouteStylesChanged");
2190 	  break;
2191 
2192 	case F_Grid:
2193 	  if (absolute)
2194 	    SetGrid (value, false);
2195 	  else
2196 	    {
2197               if (value == 0)
2198                 value = val[0] == '-' ? -Settings.increments->grid
2199                                       :  Settings.increments->grid;
2200               /* On the way down, short against the minimum
2201                * PCB drawing unit */
2202               if ((value + PCB->Grid) < 1)
2203                 SetGrid (1, false);
2204               else if (PCB->Grid == 1)
2205                 SetGrid (value, false);
2206               else
2207                 SetGrid (value + PCB->Grid, false);
2208 	    }
2209 	  break;
2210 
2211 	case F_LineSize:
2212 	case F_Line:
2213           if (!absolute && value == 0)
2214             value = val[0] == '-' ? -Settings.increments->line
2215                                   :  Settings.increments->line;
2216 	  SetLineSize (absolute ? value : value + Settings.LineThickness);
2217 	  hid_action ("RouteStylesChanged");
2218 	  break;
2219 
2220 	case F_Via:
2221 	case F_ViaSize:
2222 	  SetViaSize (absolute ? value : value + Settings.ViaThickness, false);
2223 	  hid_action ("RouteStylesChanged");
2224 	  break;
2225 
2226 	case F_Text:
2227 	case F_TextScale:
2228 	  text_scale = value / (double)FONT_CAPHEIGHT * 100.;
2229 	  if (!absolute)
2230 	    text_scale += Settings.TextScale;
2231 	  SetTextScale (text_scale);
2232 	  break;
2233 	default:
2234 	  err = 1;
2235 	  break;
2236 	}
2237       if (!err)
2238 	return 0;
2239     }
2240 
2241   AFAIL (setvalue);
2242 }
2243 
2244 
2245 /* --------------------------------------------------------------------------- */
2246 
2247 static const char quit_syntax[] = N_("Quit()");
2248 
2249 static const char quit_help[] = N_("Quits the application after confirming.");
2250 
2251 /* %start-doc actions Quit
2252 
2253 If you have unsaved changes, you will be prompted to confirm (or
2254 save) before quitting.
2255 
2256 %end-doc */
2257 
2258 static int
ActionQuit(int argc,char ** argv,Coord x,Coord y)2259 ActionQuit (int argc, char **argv, Coord x, Coord y)
2260 {
2261   char *force = ARG (0);
2262   if (force && strcasecmp (force, "force") == 0)
2263     {
2264       PCB->Changed = 0;
2265       exit (0);
2266     }
2267   if (!PCB->Changed || gui->close_confirm_dialog () == HID_CLOSE_CONFIRM_OK)
2268     QuitApplication ();
2269   return 1;
2270 }
2271 
2272 /* --------------------------------------------------------------------------- */
2273 
2274 static const char connection_syntax[] =
2275   N_("Connection(Find|ResetLinesAndPolygons|ResetPinsAndVias|Reset)");
2276 
2277 static const char connection_help[] =
2278   N_("Searches connections of the object at the cursor position.");
2279 
2280 /* %start-doc actions Connection
2281 
2282 Connections found with this action will be highlighted in the
2283 ``connected-color'' color and will have the ``found'' flag set.
2284 
2285 @table @code
2286 
2287 @item Find
2288 The net under the cursor is ``found''.
2289 
2290 @item ResetLinesAndPolygons
2291 Any ``found'' lines and polygons are marked ``not found''.
2292 
2293 @item ResetPinsAndVias
2294 Any ``found'' pins and vias are marked ``not found''.
2295 
2296 @item Reset
2297 All ``found'' objects are marked ``not found''.
2298 
2299 @end table
2300 
2301 %end-doc */
2302 
2303 static int
ActionConnection(int argc,char ** argv,Coord x,Coord y)2304 ActionConnection (int argc, char **argv, Coord x, Coord y)
2305 {
2306   char *function = ARG (0);
2307   if (function)
2308     {
2309       switch (GetFunctionID (function))
2310 	{
2311 	case F_Find:
2312 	  {
2313 	    gui->get_coords (_("Click on a connection"), &x, &y);
2314 	    LookupConnection (x, y, true, 1, CONNECTEDFLAG, false);
2315 	    LookupConnection (x, y, true, 1, FOUNDFLAG, true);
2316 	    break;
2317 	  }
2318 
2319 	case F_ResetLinesAndPolygons:
2320 	  if (ClearFlagOnLinesAndPolygons (CONNECTEDFLAG | FOUNDFLAG, true))
2321 	    {
2322 	      IncrementUndoSerialNumber ();
2323 	      Draw ();
2324 	    }
2325 	  break;
2326 
2327 	case F_ResetPinsViasAndPads:
2328 	  if (ClearFlagOnPinsViasAndPads (CONNECTEDFLAG | FOUNDFLAG, true))
2329 	    {
2330 	      IncrementUndoSerialNumber ();
2331 	      Draw ();
2332 	    }
2333 	  break;
2334 
2335 	case F_Reset:
2336 	  if (ClearFlagOnAllObjects (CONNECTEDFLAG | FOUNDFLAG, true))
2337 	    {
2338 	      IncrementUndoSerialNumber ();
2339 	      Draw ();
2340 	    }
2341 	  break;
2342 	}
2343       return 0;
2344     }
2345 
2346   AFAIL (connection);
2347 }
2348 
2349 /* --------------------------------------------------------------------------- */
2350 
2351 static const char disperseelements_syntax[] =
2352   N_("DisperseElements(All|Selected)");
2353 
2354 static const char disperseelements_help[] = N_("Disperses elements.");
2355 
2356 /* %start-doc actions DisperseElements
2357 
2358 Normally this is used when starting a board, by selecting all elements
2359 and then dispersing them.  This scatters the elements around the board
2360 so that you can pick individual ones, rather than have all the
2361 elements at the same 0,0 coordinate and thus impossible to choose
2362 from.
2363 
2364 %end-doc */
2365 
2366 #define GAP MIL_TO_COORD(100)
2367 
2368 static int
ActionDisperseElements(int argc,char ** argv,Coord x,Coord y)2369 ActionDisperseElements (int argc, char **argv, Coord x, Coord y)
2370 {
2371   char *function = ARG (0);
2372   Coord minx = GAP,
2373     miny = GAP,
2374     maxy = GAP,
2375     dx, dy;
2376   int all = 0, bad = 0;
2377 
2378   if (!function || !*function)
2379     {
2380       bad = 1;
2381     }
2382   else
2383     {
2384       switch (GetFunctionID (function))
2385 	{
2386 	case F_All:
2387 	  all = 1;
2388 	  break;
2389 
2390 	case F_Selected:
2391 	  all = 0;
2392 	  break;
2393 
2394 	default:
2395 	  bad = 1;
2396 	}
2397     }
2398 
2399   if (bad)
2400     {
2401       AFAIL (disperseelements);
2402     }
2403 
2404 
2405   ELEMENT_LOOP (PCB->Data);
2406   {
2407     /*
2408      * If we want to disperse selected elements, maybe we need smarter
2409      * code here to avoid putting components on top of others which
2410      * are not selected.  For now, I'm assuming that this is typically
2411      * going to be used either with a brand new design or a scratch
2412      * design holding some new components
2413      */
2414     if (!TEST_FLAG (LOCKFLAG, element) && (all || TEST_FLAG (SELECTEDFLAG, element)))
2415       {
2416 
2417 	/* figure out how much to move the element */
2418 	dx = minx - element->BoundingBox.X1;
2419 
2420 	/* snap to the grid */
2421 	dx -= (element->MarkX + dx) % PCB->Grid;
2422 
2423 	/*
2424 	 * and add one grid size so we make sure we always space by GAP or
2425 	 * more
2426 	 */
2427 	dx += PCB->Grid;
2428 
2429 	/* Figure out if this row has room.  If not, start a new row */
2430 	if (GAP + element->BoundingBox.X2 + dx > PCB->MaxWidth)
2431 	  {
2432 	    miny = maxy + GAP;
2433 	    minx = GAP;
2434 	  }
2435 
2436 	/* figure out how much to move the element */
2437 	dx = minx - element->BoundingBox.X1;
2438 	dy = miny - element->BoundingBox.Y1;
2439 
2440 	/* snap to the grid */
2441 	dx -= (element->MarkX + dx) % PCB->Grid;
2442 	dx += PCB->Grid;
2443 	dy -= (element->MarkY + dy) % PCB->Grid;
2444 	dy += PCB->Grid;
2445 
2446 	/* move the element */
2447 	MoveElementLowLevel (PCB->Data, element, dx, dy);
2448 
2449 	/* and add to the undo list so we can undo this operation */
2450 	AddObjectToMoveUndoList (ELEMENT_TYPE, NULL, NULL, element, dx, dy);
2451 
2452 	/* keep track of how tall this row is */
2453 	minx += element->BoundingBox.X2 - element->BoundingBox.X1 + GAP;
2454 	if (maxy < element->BoundingBox.Y2)
2455 	  {
2456 	    maxy = element->BoundingBox.Y2;
2457 	  }
2458       }
2459 
2460   }
2461   END_LOOP;
2462 
2463   /* done with our action so increment the undo # */
2464   IncrementUndoSerialNumber ();
2465 
2466   Redraw ();
2467   SetChangedFlag (true);
2468 
2469   return 0;
2470 }
2471 
2472 #undef GAP
2473 
2474 /* --------------------------------------------------------------------------- */
2475 
2476 static const char display_syntax[] =
2477   N_("Display(NameOnPCB|Description|Value)\n"
2478   "Display(Grid|Redraw)\n"
2479   "Display(CycleClip|CycleCrosshair|Toggle45Degree|ToggleStartDirection)\n"
2480   "Display(ToggleGrid|ToggleRubberBandMode|ToggleUniqueNames)\n"
2481   "Display(ToggleMask|ToggleName|ToggleClearLine|ToggleFullPoly|ToggleSnapPin)\n"
2482   "Display(ToggleThindraw|ToggleThindrawPoly|ToggleOrthoMove|ToggleLocalRef)\n"
2483   "Display(ToggleCheckPlanes|ToggleShowDRC|ToggleAutoDRC)\n"
2484   "Display(ToggleLiveRoute|LockNames|OnlyNames)\n"
2485   "Display(Pinout|PinOrPadName)");
2486 
2487 static const char display_help[] = N_("Several display-related actions.");
2488 
2489 /* %start-doc actions Display
2490 
2491 @table @code
2492 
2493 @item NameOnPCB
2494 @item Description
2495 @item Value
2496 Specify whether all elements show their name, description, or value.
2497 
2498 @item Redraw
2499 Redraw the whole board.
2500 
2501 @item Toggle45Degree
2502 When clear, lines can be drawn at any angle.  When set, lines are
2503 restricted to multiples of 45 degrees and requested lines may be
2504 broken up according to the clip setting.
2505 
2506 @item CycleClip
2507 Changes the way lines are restricted to 45 degree increments.  The
2508 various settings are: straight only, orthogonal then angled, and angled
2509 then orthogonal.  If AllDirections is set, this action disables it.
2510 
2511 @item CycleCrosshair
2512 Changes crosshair drawing.  Crosshair may accept form of 4-ray,
2513 8-ray and 12-ray cross.
2514 
2515 @item ToggleRubberBandMode
2516 If set, moving an object moves all the lines attached to it too.
2517 
2518 @item ToggleStartDirection
2519 If set, each time you set a point in a line, the Clip toggles between
2520 orth-angle and angle-ortho.
2521 
2522 @item ToggleUniqueNames
2523 If set, you will not be permitted to change the name of an element to
2524 match that of another element.
2525 
2526 @item ToggleSnapPin
2527 If set, pin centers and pad end points are treated as additional grid
2528 points that the cursor can snap to.
2529 
2530 @item ToggleLocalRef
2531 If set, the mark is automatically set to the beginning of any move, so
2532 you can see the relative distance you've moved.
2533 
2534 @item ToggleThindraw
2535 If set, objects on the screen are drawn as outlines (lines are drawn
2536 as center-lines).  This lets you see line endpoints hidden under pins,
2537 for example.
2538 
2539 @item ToggleThindrawPoly
2540 If set, polygons on the screen are drawn as outlines.
2541 
2542 @item ToggleShowDRC
2543 If set, pending objects (i.e. lines you're in the process of drawing)
2544 will be drawn with an outline showing how far away from other copper
2545 you need to be.
2546 
2547 @item ToggleLiveRoute
2548 If set, the progress of the autorouter will be visible on the screen.
2549 
2550 @item ToggleAutoDRC
2551 If set, you will not be permitted to make connections which violate
2552 the current DRC and netlist settings.
2553 
2554 @item ToggleCheckPlanes
2555 If set, lines and arcs aren't drawn, which usually leaves just the
2556 polygons.  If you also disable all but the layer you're interested in,
2557 this allows you to check for isolated regions.
2558 
2559 @item ToggleOrthoMove
2560 If set, the crosshair is only allowed to move orthogonally from its
2561 previous position.  I.e. you can move an element or line up, down,
2562 left, or right, but not up+left or down+right.
2563 
2564 @item ToggleName
2565 Selects whether the pinouts show the pin names or the pin numbers.
2566 
2567 @item ToggleLockNames
2568 If set, text will ignore left mouse clicks and actions that work on
2569 objects under the mouse. You can still select text with a lasso (left
2570 mouse drag) and perform actions on the selection.
2571 
2572 @item ToggleOnlyNames
2573 If set, only text will be sensitive for mouse clicks and actions that
2574 work on objects under the mouse. You can still select other objects
2575 with a lasso (left mouse drag) and perform actions on the selection.
2576 
2577 @item ToggleMask
2578 Turns the solder mask on or off.
2579 
2580 @item ToggleClearLine
2581 When set, the clear-line flag causes new lines and arcs to have their
2582 ``clear polygons'' flag set, so they won't be electrically connected
2583 to any polygons they overlap.
2584 
2585 @item ToggleFullPoly
2586 When set, the full-poly flag causes new polygons to have their
2587 ``full polygon'' flag set, so all parts of them will be displayed
2588 instead of only the biggest one.
2589 
2590 @item ToggleGrid
2591 Resets the origin of the current grid to be wherever the mouse pointer
2592 is (not where the crosshair currently is).  If you provide two numbers
2593 after this, the origin is set to that coordinate.
2594 
2595 @item Grid
2596 Toggles whether the grid is displayed or not.
2597 
2598 @item Pinout
2599 Causes the pinout of the element indicated by the cursor to be
2600 displayed, usually in a separate window.
2601 
2602 @item PinOrPadName
2603 Toggles whether the names of pins, pads, or (yes) vias will be
2604 displayed.  If the cursor is over an element, all of its pins and pads
2605 are affected.
2606 
2607 @item ToggleAutoBuriedVias
2608 If set, automatically created vias are buried vias.
2609 
2610 @end table
2611 
2612 %end-doc */
2613 
2614 static enum crosshair_shape
CrosshairShapeIncrement(enum crosshair_shape shape)2615 CrosshairShapeIncrement (enum crosshair_shape shape)
2616 {
2617   switch(shape)
2618     {
2619     case Basic_Crosshair_Shape:
2620       shape = Union_Jack_Crosshair_Shape;
2621       break;
2622     case Union_Jack_Crosshair_Shape:
2623       shape = Dozen_Crosshair_Shape;
2624       break;
2625     case Dozen_Crosshair_Shape:
2626       shape = Crosshair_Shapes_Number;
2627       break;
2628     case Crosshair_Shapes_Number:
2629       shape = Basic_Crosshair_Shape;
2630       break;
2631     }
2632   return shape;
2633 }
2634 
2635 static int
ActionDisplay(int argc,char ** argv,Coord childX,Coord childY)2636 ActionDisplay (int argc, char **argv, Coord childX, Coord childY)
2637 {
2638   char *function, *str_dir;
2639   int id;
2640   int err = 0;
2641 
2642   function = ARG (0);
2643   str_dir = ARG (1);
2644 
2645   if (function && (!str_dir || !*str_dir))
2646     {
2647       switch (id = GetFunctionID (function))
2648 	{
2649 
2650 	  /* redraw layout */
2651 	case F_ClearAndRedraw:
2652 	case F_Redraw:
2653 	  Redraw ();
2654 	  break;
2655 
2656 	  /* change the displayed name of elements */
2657 	case F_Value:
2658 	case F_NameOnPCB:
2659 	case F_Description:
2660 	  ELEMENT_LOOP (PCB->Data);
2661 	  {
2662 	    EraseElementName (element);
2663 	  }
2664 	  END_LOOP;
2665 	  CLEAR_FLAG (DESCRIPTIONFLAG | NAMEONPCBFLAG, PCB);
2666 	  switch (id)
2667 	    {
2668 	    case F_Value:
2669 	      break;
2670 	    case F_NameOnPCB:
2671 	      SET_FLAG (NAMEONPCBFLAG, PCB);
2672 	      break;
2673 	    case F_Description:
2674 	      SET_FLAG (DESCRIPTIONFLAG, PCB);
2675 	      break;
2676 	    }
2677 	  ELEMENT_LOOP (PCB->Data);
2678 	  {
2679 	    DrawElementName (element);
2680 	  }
2681 	  END_LOOP;
2682 	  Draw ();
2683 	  break;
2684 
2685 	  /* toggle line-adjust flag */
2686 	case F_ToggleAllDirections:
2687 	  TOGGLE_FLAG (ALLDIRECTIONFLAG, PCB);
2688 	  AdjustAttachedObjects ();
2689 	  break;
2690 
2691 	case F_CycleClip:
2692 	  notify_crosshair_change (false);
2693 	  if (TEST_FLAG (ALLDIRECTIONFLAG, PCB))
2694 	    {
2695 	      TOGGLE_FLAG (ALLDIRECTIONFLAG, PCB);
2696 	      PCB->Clipping = 0;
2697 	    }
2698 	  else
2699 	    PCB->Clipping = (PCB->Clipping + 1) % 3;
2700 	  AdjustAttachedObjects ();
2701 	  notify_crosshair_change (true);
2702 	  break;
2703 
2704 	case F_CycleCrosshair:
2705 	  notify_crosshair_change (false);
2706 	  Crosshair.shape = CrosshairShapeIncrement(Crosshair.shape);
2707 	  if (Crosshair_Shapes_Number == Crosshair.shape)
2708 	    Crosshair.shape = Basic_Crosshair_Shape;
2709 	  notify_crosshair_change (true);
2710 	  break;
2711 
2712 	case F_ToggleRubberBandMode:
2713 	  notify_crosshair_change (false);
2714 	  TOGGLE_FLAG (RUBBERBANDFLAG, PCB);
2715 	  notify_crosshair_change (true);
2716 	  break;
2717 
2718 	case F_ToggleAutoBuriedVias:
2719 	  notify_crosshair_change (false);
2720 	  TOGGLE_FLAG (AUTOBURIEDVIASFLAG, PCB);
2721 	  notify_crosshair_change (true);
2722 	  break;
2723 
2724 	case F_ToggleStartDirection:
2725 	  notify_crosshair_change (false);
2726 	  TOGGLE_FLAG (SWAPSTARTDIRFLAG, PCB);
2727 	  notify_crosshair_change (true);
2728 	  break;
2729 
2730 	case F_ToggleUniqueNames:
2731 	  TOGGLE_FLAG (UNIQUENAMEFLAG, PCB);
2732 	  break;
2733 
2734 	case F_ToggleSnapPin:
2735 	  notify_crosshair_change (false);
2736 	  TOGGLE_FLAG (SNAPPINFLAG, PCB);
2737 	  notify_crosshair_change (true);
2738 	  break;
2739 
2740 	case F_ToggleLocalRef:
2741 	  TOGGLE_FLAG (LOCALREFFLAG, PCB);
2742 	  break;
2743 
2744 	case F_ToggleThindraw:
2745 	  TOGGLE_FLAG (THINDRAWFLAG, PCB);
2746 	  Redraw ();
2747 	  break;
2748 
2749 	case F_ToggleThindrawPoly:
2750 	  TOGGLE_FLAG (THINDRAWPOLYFLAG, PCB);
2751 	  Redraw ();
2752 	  break;
2753 
2754 	case F_ToggleLockNames:
2755 	  TOGGLE_FLAG (LOCKNAMESFLAG, PCB);
2756 	  CLEAR_FLAG (ONLYNAMESFLAG, PCB);
2757 	  break;
2758 
2759 	case F_ToggleOnlyNames:
2760 	  TOGGLE_FLAG (ONLYNAMESFLAG, PCB);
2761 	  CLEAR_FLAG (LOCKNAMESFLAG, PCB);
2762 	  break;
2763 
2764 	case F_ToggleHideNames:
2765 	  TOGGLE_FLAG (HIDENAMESFLAG, PCB);
2766 	  Redraw ();
2767 	  break;
2768 
2769 	case F_ToggleShowDRC:
2770 	  TOGGLE_FLAG (SHOWDRCFLAG, PCB);
2771 	  break;
2772 
2773 	case F_ToggleLiveRoute:
2774 	  TOGGLE_FLAG (LIVEROUTEFLAG, PCB);
2775 	  break;
2776 
2777 	case F_ToggleAutoDRC:
2778 	  notify_crosshair_change (false);
2779 	  TOGGLE_FLAG (AUTODRCFLAG, PCB);
2780 	  if (TEST_FLAG (AUTODRCFLAG, PCB) && Settings.Mode == LINE_MODE)
2781 	    {
2782 	      if (ClearFlagOnAllObjects (CONNECTEDFLAG | FOUNDFLAG, true))
2783 		{
2784 		  IncrementUndoSerialNumber ();
2785 		  Draw ();
2786 		}
2787 	      if (Crosshair.AttachedLine.State != STATE_FIRST)
2788 		{
2789 		  LookupConnection (Crosshair.AttachedLine.Point1.X,
2790 		                    Crosshair.AttachedLine.Point1.Y,
2791 		                    true, 1, CONNECTEDFLAG, false);
2792 		  LookupConnection (Crosshair.AttachedLine.Point1.X,
2793 		                    Crosshair.AttachedLine.Point1.Y,
2794 		                    true, 1, FOUNDFLAG, true);
2795 		}
2796 	    }
2797 	  notify_crosshair_change (true);
2798 	  break;
2799 
2800 	case F_ToggleCheckPlanes:
2801 	  TOGGLE_FLAG (CHECKPLANESFLAG, PCB);
2802 	  Redraw ();
2803 	  break;
2804 
2805 	case F_ToggleOrthoMove:
2806 	  TOGGLE_FLAG (ORTHOMOVEFLAG, PCB);
2807 	  break;
2808 
2809 	case F_ToggleName:
2810 	  TOGGLE_FLAG (SHOWNUMBERFLAG, PCB);
2811 	  Redraw ();
2812 	  break;
2813 
2814 	case F_ToggleMask:
2815 	  TOGGLE_FLAG (SHOWMASKFLAG, PCB);
2816 	  Redraw ();
2817 	  break;
2818 
2819 	case F_ToggleClearLine:
2820 	  TOGGLE_FLAG (CLEARNEWFLAG, PCB);
2821 	  break;
2822 
2823 	case F_ToggleFullPoly:
2824 	  TOGGLE_FLAG (NEWFULLPOLYFLAG, PCB);
2825 	  break;
2826 
2827 	  /* shift grid alignment */
2828 	case F_ToggleGrid:
2829 	  {
2830 	    Coord oldGrid = PCB->Grid;
2831 
2832 	    PCB->Grid = 1;
2833 	    if (MoveCrosshairAbsolute (Crosshair.X, Crosshair.Y))
2834 	      notify_crosshair_change (true);	/* first notify was in MoveCrosshairAbs */
2835 	    SetGrid (oldGrid, true);
2836 	  }
2837 	  break;
2838 
2839 	  /* toggle displaying of the grid */
2840 	case F_Grid:
2841 	  Settings.DrawGrid = !Settings.DrawGrid;
2842 	  Redraw ();
2843 	  break;
2844 
2845 	  /* display the pinout of an element */
2846 	case F_Pinout:
2847 	  {
2848 	    ElementType *element;
2849 	    void *ptrtmp;
2850 	    Coord x, y;
2851 
2852 	    gui->get_coords (_("Click on an element"), &x, &y);
2853 	    if ((SearchScreen
2854 		 (x, y, ELEMENT_TYPE, &ptrtmp,
2855 		  &ptrtmp, &ptrtmp)) != NO_TYPE)
2856 	      {
2857 		element = (ElementType *) ptrtmp;
2858 		gui->show_item (element);
2859 	      }
2860 	    break;
2861 	  }
2862 
2863 	  /* toggle displaying of pin/pad/via names */
2864 	case F_PinOrPadName:
2865 	  {
2866 	    void *ptr1, *ptr2, *ptr3;
2867 	    Coord x, y;
2868 
2869 	    gui->get_coords(_("Click on an element"), &x, &y);
2870 
2871 	    switch (SearchScreen (x, y,
2872 				  ELEMENT_TYPE | PIN_TYPE | PAD_TYPE |
2873 				  VIA_TYPE, (void **) &ptr1, (void **) &ptr2,
2874 				  (void **) &ptr3))
2875 	      {
2876 	      case ELEMENT_TYPE:
2877 		PIN_LOOP ((ElementType *) ptr1);
2878 		{
2879 		  if (TEST_FLAG (DISPLAYNAMEFLAG, pin))
2880 		    ErasePinName (pin);
2881 		  else
2882 		    DrawPinName (pin);
2883 		  AddObjectToFlagUndoList (PIN_TYPE, ptr1, pin, pin);
2884 		  TOGGLE_FLAG (DISPLAYNAMEFLAG, pin);
2885 		}
2886 		END_LOOP;
2887 		PAD_LOOP ((ElementType *) ptr1);
2888 		{
2889 		  if (TEST_FLAG (DISPLAYNAMEFLAG, pad))
2890 		    ErasePadName (pad);
2891 		  else
2892 		    DrawPadName (pad);
2893 		  AddObjectToFlagUndoList (PAD_TYPE, ptr1, pad, pad);
2894 		  TOGGLE_FLAG (DISPLAYNAMEFLAG, pad);
2895 		}
2896 		END_LOOP;
2897 		SetChangedFlag (true);
2898 		IncrementUndoSerialNumber ();
2899 		Draw ();
2900 		break;
2901 
2902 	      case PIN_TYPE:
2903 		if (TEST_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2))
2904 		  ErasePinName ((PinType *) ptr2);
2905 		else
2906 		  DrawPinName ((PinType *) ptr2);
2907 		AddObjectToFlagUndoList (PIN_TYPE, ptr1, ptr2, ptr3);
2908 		TOGGLE_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2);
2909 		SetChangedFlag (true);
2910 		IncrementUndoSerialNumber ();
2911 		Draw ();
2912 		break;
2913 
2914 	      case PAD_TYPE:
2915 		if (TEST_FLAG (DISPLAYNAMEFLAG, (PadType *) ptr2))
2916 		  ErasePadName ((PadType *) ptr2);
2917 		else
2918 		  DrawPadName ((PadType *) ptr2);
2919 		AddObjectToFlagUndoList (PAD_TYPE, ptr1, ptr2, ptr3);
2920 		TOGGLE_FLAG (DISPLAYNAMEFLAG, (PadType *) ptr2);
2921 		SetChangedFlag (true);
2922 		IncrementUndoSerialNumber ();
2923 		Draw ();
2924 		break;
2925 	      case VIA_TYPE:
2926 		if (TEST_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2))
2927 		  EraseViaName ((PinType *) ptr2);
2928 		else
2929 		  DrawViaName ((PinType *) ptr2);
2930 		AddObjectToFlagUndoList (VIA_TYPE, ptr1, ptr2, ptr3);
2931 		TOGGLE_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2);
2932 		SetChangedFlag (true);
2933 		IncrementUndoSerialNumber ();
2934 		Draw ();
2935 		break;
2936 	      }
2937 	    break;
2938 	  }
2939 	default:
2940 	  err = 1;
2941 	}
2942     }
2943   else if (function && str_dir)
2944     {
2945       switch (GetFunctionID (function))
2946 	{
2947 	case F_ToggleGrid:
2948 	  if (argc > 2)
2949 	    {
2950 	      PCB->GridOffsetX = GetValue (argv[1], NULL, NULL);
2951 	      PCB->GridOffsetY = GetValue (argv[2], NULL, NULL);
2952 	      if (Settings.DrawGrid)
2953 		Redraw ();
2954 	    }
2955 	  break;
2956 
2957 	default:
2958 	  err = 1;
2959 	  break;
2960 	}
2961     }
2962   else
2963     err = 1;
2964 
2965   if (err)
2966     AFAIL (display);
2967 
2968   return 0;
2969 }
2970 
2971 /* --------------------------------------------------------------------------- */
2972 
2973 static const char mode_syntax[] =
2974   N_("Mode(Arc|Arrow|Copy|InsertPoint|Line|Lock|Move|None|PasteBuffer)\n"
2975   "Mode(Polygon|Rectangle|Remove|Rotate|Text|Thermal|Via)\n"
2976   "Mode(Notify|Release|Cancel|Stroke)\n"
2977   "Mode(Save|Restore)");
2978 
2979 static const char mode_help[] = N_("Change or use the tool mode.");
2980 
2981 /* %start-doc actions Mode
2982 
2983 @table @code
2984 
2985 @item Arc
2986 @itemx Arrow
2987 @itemx Copy
2988 @itemx InsertPoint
2989 @itemx Line
2990 @itemx Lock
2991 @itemx Move
2992 @itemx None
2993 @itemx PasteBuffer
2994 @itemx Polygon
2995 @itemx Rectangle
2996 @itemx Remove
2997 @itemx Rotate
2998 @itemx Text
2999 @itemx Thermal
3000 @itemx Via
3001 Select the indicated tool.
3002 
3003 @item Notify
3004 Called when you press the mouse button, or move the mouse.
3005 
3006 @item Release
3007 Called when you release the mouse button.
3008 
3009 @item Cancel
3010 Cancels any pending tool activity, allowing you to restart elsewhere.
3011 For example, this allows you to start a new line rather than attach a
3012 line to the previous line.
3013 
3014 @item Escape
3015 Similar to Cancel but calling this action a second time will return
3016 to the Arrow tool.
3017 
3018 @item Stroke
3019 If your @code{pcb} was built with libstroke, this invokes the stroke
3020 input method.  If not, this will restart a drawing mode if you were
3021 drawing, else it will select objects.
3022 
3023 @item Save
3024 Remembers the current tool.
3025 
3026 @item Restore
3027 Restores the tool to the last saved tool.
3028 
3029 @end table
3030 
3031 %end-doc */
3032 
3033 static int
ActionMode(int argc,char ** argv,Coord x,Coord y)3034 ActionMode (int argc, char **argv, Coord x, Coord y)
3035 {
3036   char *function = ARG (0);
3037 
3038   if (function)
3039     {
3040       Note.X = Crosshair.X;
3041       Note.Y = Crosshair.Y;
3042       notify_crosshair_change (false);
3043       switch (GetFunctionID (function))
3044 	{
3045 	case F_Arc:
3046 	  SetMode (ARC_MODE);
3047 	  break;
3048 	case F_Arrow:
3049 	  SetMode (ARROW_MODE);
3050 	  break;
3051 	case F_Copy:
3052 	  SetMode (COPY_MODE);
3053 	  break;
3054 	case F_InsertPoint:
3055 	  SetMode (INSERTPOINT_MODE);
3056 	  break;
3057 	case F_Line:
3058 	  SetMode (LINE_MODE);
3059 	  break;
3060 	case F_Lock:
3061 	  SetMode (LOCK_MODE);
3062 	  break;
3063 	case F_Move:
3064 	  SetMode (MOVE_MODE);
3065 	  break;
3066 	case F_None:
3067 	  SetMode (NO_MODE);
3068 	  break;
3069 	case F_Cancel:
3070 	  {
3071 	    int saved_mode = Settings.Mode;
3072 	    SetMode (NO_MODE);
3073 	    SetMode (saved_mode);
3074 	  }
3075 	  break;
3076 	case F_Escape:
3077 	  {
3078 	    switch (Settings.Mode)
3079 	      {
3080 	      case VIA_MODE:
3081 	      case PASTEBUFFER_MODE:
3082 	      case TEXT_MODE:
3083 	      case ROTATE_MODE:
3084 	      case REMOVE_MODE:
3085 	      case MOVE_MODE:
3086 	      case COPY_MODE:
3087 	      case INSERTPOINT_MODE:
3088 	      case RUBBERBANDMOVE_MODE:
3089 	      case THERMAL_MODE:
3090 	      case LOCK_MODE:
3091 		SetMode (NO_MODE);
3092 		SetMode (ARROW_MODE);
3093 		break;
3094 
3095 	      case LINE_MODE:
3096 		if (Crosshair.AttachedLine.State == STATE_FIRST)
3097 		  SetMode (ARROW_MODE);
3098 		else
3099 		  {
3100 		    SetMode (NO_MODE);
3101 		    SetMode (LINE_MODE);
3102 		  }
3103 		break;
3104 
3105 	      case RECTANGLE_MODE:
3106 		if (Crosshair.AttachedBox.State == STATE_FIRST)
3107 		  SetMode (ARROW_MODE);
3108 		else
3109 		  {
3110 		    SetMode (NO_MODE);
3111 		    SetMode (RECTANGLE_MODE);
3112 		  }
3113 		break;
3114 
3115 	      case POLYGON_MODE:
3116 		if (Crosshair.AttachedLine.State == STATE_FIRST)
3117 		  SetMode (ARROW_MODE);
3118 		else
3119 		  {
3120 		    SetMode (NO_MODE);
3121 		    SetMode (POLYGON_MODE);
3122 		  }
3123 		break;
3124 
3125 	      case POLYGONHOLE_MODE:
3126 		if (Crosshair.AttachedLine.State == STATE_FIRST)
3127 		  SetMode (ARROW_MODE);
3128 		else
3129 		  {
3130 		    SetMode (NO_MODE);
3131 		    SetMode (POLYGONHOLE_MODE);
3132 		  }
3133 		break;
3134 
3135 	      case ARC_MODE:
3136 		if (Crosshair.AttachedBox.State == STATE_FIRST)
3137 		  SetMode (ARROW_MODE);
3138 		else
3139 		  {
3140 		    SetMode (NO_MODE);
3141 		    SetMode (ARC_MODE);
3142 		  }
3143 		break;
3144 
3145 	      case ARROW_MODE:
3146 		break;
3147 
3148 	      default:
3149 		break;
3150 	      }
3151 	  }
3152 	  break;
3153 
3154 	case F_Notify:
3155 	  NotifyMode ();
3156 	  break;
3157 	case F_PasteBuffer:
3158 	  SetMode (PASTEBUFFER_MODE);
3159 	  break;
3160 	case F_Polygon:
3161 	  SetMode (POLYGON_MODE);
3162 	  break;
3163 	case F_PolygonHole:
3164 	  SetMode (POLYGONHOLE_MODE);
3165 	  break;
3166 #ifndef HAVE_LIBSTROKE
3167 	case F_Release:
3168 	  ReleaseMode ();
3169 	  break;
3170 #else
3171 	case F_Release:
3172 	  if (mid_stroke)
3173 	    FinishStroke ();
3174 	  else
3175 	    ReleaseMode ();
3176 	  break;
3177 #endif
3178 	case F_Remove:
3179 	  SetMode (REMOVE_MODE);
3180 	  break;
3181 	case F_Rectangle:
3182 	  SetMode (RECTANGLE_MODE);
3183 	  break;
3184 	case F_Rotate:
3185 	  SetMode (ROTATE_MODE);
3186 	  break;
3187 	case F_Stroke:
3188 #ifdef HAVE_LIBSTROKE
3189 	  mid_stroke = true;
3190 	  StrokeBox.X1 = Crosshair.X;
3191 	  StrokeBox.Y1 = Crosshair.Y;
3192 	  break;
3193 #else
3194 	  /* Handle middle mouse button restarts of drawing mode.  If not in
3195 	     |  a drawing mode, middle mouse button will select objects.
3196 	   */
3197 	  if (Settings.Mode == LINE_MODE
3198 	      && Crosshair.AttachedLine.State != STATE_FIRST)
3199 	    {
3200 	      SetMode (LINE_MODE);
3201 	    }
3202 	  else if (Settings.Mode == ARC_MODE
3203 		   && Crosshair.AttachedBox.State != STATE_FIRST)
3204 	    SetMode (ARC_MODE);
3205 	  else if (Settings.Mode == RECTANGLE_MODE
3206 		   && Crosshair.AttachedBox.State != STATE_FIRST)
3207 	    SetMode (RECTANGLE_MODE);
3208 	  else if (Settings.Mode == POLYGON_MODE
3209 		   && Crosshair.AttachedLine.State != STATE_FIRST)
3210 	    SetMode (POLYGON_MODE);
3211 	  else
3212 	    {
3213 	      SaveMode ();
3214 	      saved_mode = true;
3215 	      SetMode (ARROW_MODE);
3216 	      NotifyMode ();
3217 	    }
3218 	  break;
3219 #endif
3220 	case F_Text:
3221 	  SetMode (TEXT_MODE);
3222 	  break;
3223 	case F_Thermal:
3224 	  SetMode (THERMAL_MODE);
3225 	  break;
3226 	case F_Via:
3227 	  SetMode (VIA_MODE);
3228 	  break;
3229 
3230 	case F_Restore:	/* restore the last saved mode */
3231 	  RestoreMode ();
3232 	  break;
3233 
3234 	case F_Save:		/* save currently selected mode */
3235 	  SaveMode ();
3236 	  break;
3237 	}
3238       notify_crosshair_change (true);
3239       return 0;
3240     }
3241 
3242   AFAIL (mode);
3243 }
3244 
3245 /* --------------------------------------------------------------------------- */
3246 
3247 static const char removeselected_syntax[] = N_("RemoveSelected()");
3248 
3249 static const char removeselected_help[] = N_("Removes any selected objects.");
3250 
3251 /* %start-doc actions RemoveSelected
3252 
3253 %end-doc */
3254 
3255 static int
ActionRemoveSelected(int argc,char ** argv,Coord x,Coord y)3256 ActionRemoveSelected (int argc, char **argv, Coord x, Coord y)
3257 {
3258   if (RemoveSelected ())
3259     SetChangedFlag (true);
3260   return 0;
3261 }
3262 
3263 /* --------------------------------------------------------------------------- */
3264 
3265 static const char renumber_syntax[] = N_("Renumber()\n"
3266                                       "Renumber(filename)");
3267 
3268 static const char renumber_help[] =
3269   N_("Renumber all elements.  The changes will be recorded to filename\n"
3270   "for use in backannotating these changes to the schematic.");
3271 
3272 /* %start-doc actions Renumber
3273 
3274 %end-doc */
3275 
3276 static int
ActionRenumber(int argc,char ** argv,Coord x,Coord y)3277 ActionRenumber (int argc, char **argv, Coord x, Coord y)
3278 {
3279   bool changed = false;
3280   ElementType **element_list;
3281   ElementType **locked_element_list;
3282   unsigned int i, j, k, cnt, lock_cnt;
3283   unsigned int tmpi;
3284   size_t sz;
3285   char *tmps;
3286   char *name;
3287   FILE *out;
3288   static char * default_file = NULL;
3289   size_t cnt_list_sz = 100;
3290   struct _cnt_list
3291   {
3292     char *name;
3293     unsigned int cnt;
3294   } *cnt_list;
3295   char **was, **is, *pin;
3296   unsigned int c_cnt = 0;
3297   int unique, ok;
3298   int free_name = 0;
3299 
3300   if (argc < 1)
3301     {
3302       /*
3303        * We deal with the case where name already exists in this
3304        * function so the GUI doesn't need to deal with it
3305        */
3306       name = gui->fileselect (_("Save Renumber Annotation File As ..."),
3307 			      _("Choose a file to record the renumbering to.\n"
3308 				"This file may be used to back annotate the\n"
3309 				"change to the schematics.\n"),
3310 			      default_file, ".eco", "eco",
3311 			      0);
3312 
3313       free_name = 1;
3314     }
3315   else
3316     name = argv[0];
3317 
3318   if (default_file)
3319     {
3320       free (default_file);
3321       default_file = NULL;
3322     }
3323 
3324   if (name && *name)
3325     {
3326       default_file = strdup (name);
3327     }
3328 
3329   if ((out = fopen (name, "r")))
3330     {
3331       fclose (out);
3332       if (!gui->confirm_dialog (_("File exists!  Ok to overwrite?"), 0))
3333 	{
3334 	  if (free_name && name)
3335 	    free (name);
3336 	  return 0;
3337 	}
3338     }
3339 
3340   if ((out = fopen (name, "w")) == NULL)
3341     {
3342       Message (_("Could not open %s\n"), name);
3343       if (free_name && name)
3344 	free (name);
3345       return 1;
3346     }
3347 
3348   if (free_name && name)
3349     free (name);
3350 
3351   fprintf (out, "*COMMENT* PCB Annotation File\n");
3352   fprintf (out, "*FILEVERSION* 20061031\n");
3353 
3354   /*
3355    * Make a first pass through all of the elements and sort them out
3356    * by location on the board.  While here we also collect a list of
3357    * locked elements.
3358    *
3359    * We'll actually renumber things in the 2nd pass.
3360    */
3361   element_list = (ElementType **)calloc (PCB->Data->ElementN, sizeof (ElementType *));
3362   locked_element_list = (ElementType **)calloc (PCB->Data->ElementN, sizeof (ElementType *));
3363   was = (char **)calloc (PCB->Data->ElementN, sizeof (char *));
3364   is = (char **)calloc (PCB->Data->ElementN, sizeof (char *));
3365   if (element_list == NULL || locked_element_list == NULL || was == NULL
3366       || is == NULL)
3367     {
3368       fprintf (stderr, "calloc() failed in %s\n", __FUNCTION__);
3369       exit (1);
3370     }
3371 
3372 
3373   cnt = 0;
3374   lock_cnt = 0;
3375   ELEMENT_LOOP (PCB->Data);
3376   {
3377     if (TEST_FLAG (LOCKFLAG, element->Name) || TEST_FLAG (LOCKFLAG, element))
3378       {
3379 	/*
3380 	 * add to the list of locked elements which we won't try to
3381 	 * renumber and whose reference designators are now reserved.
3382 	 */
3383 	pcb_fprintf (out,
3384 		     "*WARN* Element \"%s\" at %$md is locked and will not be renumbered.\n",
3385 		      UNKNOWN (NAMEONPCB_NAME (element)), element->MarkX, element->MarkY);
3386 	locked_element_list[lock_cnt] = element;
3387 	lock_cnt++;
3388       }
3389 
3390     else
3391       {
3392 	/* count of devices which will be renumbered */
3393 	cnt++;
3394 
3395 	/* search for correct position in the list */
3396 	i = 0;
3397 	while (element_list[i] && element->MarkY > element_list[i]->MarkY)
3398 	  i++;
3399 
3400 	/*
3401 	 * We have found the position where we have the first element that
3402 	 * has the same Y value or a lower Y value.  Now move forward if
3403 	 * needed through the X values
3404 	 */
3405 	while (element_list[i]
3406 	       && element->MarkY == element_list[i]->MarkY
3407 	       && element->MarkX > element_list[i]->MarkX)
3408 	  i++;
3409 
3410 	for (j = cnt - 1; j > i; j--)
3411 	  {
3412 	    element_list[j] = element_list[j - 1];
3413 	  }
3414 	element_list[i] = element;
3415       }
3416   }
3417   END_LOOP;
3418 
3419 
3420   /*
3421    * Now that the elements are sorted by board position, we go through
3422    * and renumber them.
3423    */
3424 
3425   /*
3426    * turn off the flag which requires unique names so it doesn't get
3427    * in our way.  When we're done with the renumber we will have unique
3428    * names.
3429    */
3430   unique = TEST_FLAG (UNIQUENAMEFLAG, PCB);
3431   CLEAR_FLAG (UNIQUENAMEFLAG, PCB);
3432 
3433   cnt_list = (struct _cnt_list *)calloc (cnt_list_sz, sizeof (struct _cnt_list));
3434   for (i = 0; i < cnt; i++)
3435     {
3436       /* If there is no refdes, maybe just spit out a warning */
3437       if (NAMEONPCB_NAME (element_list[i]))
3438 	{
3439 	  /* figure out the prefix */
3440 	  tmps = strdup (NAMEONPCB_NAME (element_list[i]));
3441 	  j = 0;
3442 	  while (tmps[j] && (tmps[j] < '0' || tmps[j] > '9')
3443 		 && tmps[j] != '?')
3444 	    j++;
3445 	  tmps[j] = '\0';
3446 
3447 	  /* check the counter for this prefix */
3448 	  for (j = 0;
3449 	       cnt_list[j].name && (strcmp (cnt_list[j].name, tmps) != 0)
3450 	       && j < cnt_list_sz; j++);
3451 
3452 	  /* grow the list if needed */
3453 	  if (j == cnt_list_sz)
3454 	    {
3455 	      cnt_list_sz += 100;
3456 	      cnt_list = (struct _cnt_list *)realloc (cnt_list, cnt_list_sz);
3457 	      if (cnt_list == NULL)
3458 		{
3459 		  fprintf (stderr, _("realloc() failed in %s()\n"), __FUNCTION__);
3460 		  exit (1);
3461 		}
3462 	      /* zero out the memory that we added */
3463 	      for (tmpi = j; tmpi < cnt_list_sz; tmpi++)
3464 		{
3465 		  cnt_list[tmpi].name = NULL;
3466 		  cnt_list[tmpi].cnt = 0;
3467 		}
3468 	    }
3469 
3470 	  /*
3471 	   * start a new counter if we don't have a counter for this
3472 	   * prefix
3473 	   */
3474 	  if (!cnt_list[j].name)
3475 	    {
3476 	      cnt_list[j].name = strdup (tmps);
3477 	      cnt_list[j].cnt = 0;
3478 	    }
3479 
3480 	  /*
3481 	   * check to see if the new refdes is already used by a
3482 	   * locked element
3483 	   */
3484 	  do
3485 	    {
3486 	      ok = 1;
3487 	      cnt_list[j].cnt++;
3488 	      free (tmps);
3489 
3490 	      /* space for the prefix plus 1 digit plus the '\0' */
3491 	      sz = strlen (cnt_list[j].name) + 2;
3492 
3493 	      /* and 1 more per extra digit needed to hold the number */
3494 	      tmpi = cnt_list[j].cnt;
3495 	      while (tmpi > 10)
3496 		{
3497 		  sz++;
3498 		  tmpi = tmpi / 10;
3499 		}
3500 	      tmps = (char *)malloc (sz * sizeof (char));
3501 	      sprintf (tmps, "%s%d", cnt_list[j].name, (int) cnt_list[j].cnt);
3502 
3503 	      /*
3504 	       * now compare to the list of reserved (by locked
3505 	       * elements) names
3506 	       */
3507 	      for (k = 0; k < lock_cnt; k++)
3508 		{
3509 		  if (strcmp
3510 		      (UNKNOWN (NAMEONPCB_NAME (locked_element_list[k])),
3511 		       tmps) == 0)
3512 		    {
3513 		      ok = 0;
3514 		      break;
3515 		    }
3516 		}
3517 
3518 	    }
3519 	  while (!ok);
3520 
3521 	  if (strcmp (tmps, NAMEONPCB_NAME (element_list[i])) != 0)
3522 	    {
3523 	      fprintf (out, "*RENAME* \"%s\" \"%s\"\n",
3524 		       NAMEONPCB_NAME (element_list[i]), tmps);
3525 
3526 	      /* add this rename to our table of renames so we can update the netlist */
3527 	      was[c_cnt] = strdup (NAMEONPCB_NAME (element_list[i]));
3528 	      is[c_cnt] = strdup (tmps);
3529 	      c_cnt++;
3530 
3531 	      AddObjectToChangeNameUndoList (ELEMENT_TYPE, NULL, NULL,
3532 					     element_list[i],
3533 					     NAMEONPCB_NAME (element_list
3534 							     [i]));
3535 
3536 	      ChangeObjectName (ELEMENT_TYPE, element_list[i], NULL, NULL,
3537 				tmps);
3538 	      changed = true;
3539 
3540 	      /* we don't free tmps in this case because it is used */
3541 	    }
3542 	  else
3543 	    free (tmps);
3544 	}
3545       else
3546 	{
3547 	  pcb_fprintf (out, "*WARN* Element at %$md has no name.\n",
3548 		   element_list[i]->MarkX, element_list[i]->MarkY);
3549 	}
3550 
3551     }
3552 
3553   fclose (out);
3554 
3555   /* restore the unique flag setting */
3556   if (unique)
3557     SET_FLAG (UNIQUENAMEFLAG, PCB);
3558 
3559   if (changed)
3560     {
3561 
3562       /* update the netlist */
3563       AddNetlistLibToUndoList (&(PCB->NetlistLib));
3564 
3565       /* iterate over each net */
3566       for (i = 0; i < PCB->NetlistLib.MenuN; i++)
3567 	{
3568 
3569 	  /* iterate over each pin on the net */
3570 	  for (j = 0; j < PCB->NetlistLib.Menu[i].EntryN; j++)
3571 	    {
3572 
3573 	      /* figure out the pin number part from strings like U3-21 */
3574 	      tmps = strdup (PCB->NetlistLib.Menu[i].Entry[j].ListEntry);
3575 	      for (k = 0; tmps[k] && tmps[k] != '-'; k++);
3576 	      tmps[k] = '\0';
3577 	      pin = tmps + k + 1;
3578 
3579 	      /* iterate over the list of changed reference designators */
3580 	      for (k = 0; k < c_cnt; k++)
3581 		{
3582 		  /*
3583 		   * if the pin needs to change, change it and quit
3584 		   * searching in the list.
3585 		   */
3586 		  if (strcmp (tmps, was[k]) == 0)
3587 		    {
3588 		      free (PCB->NetlistLib.Menu[i].Entry[j].ListEntry);
3589 		      PCB->NetlistLib.Menu[i].Entry[j].ListEntry =
3590 			(char *)malloc ((strlen (is[k]) + strlen (pin) +
3591 				 2) * sizeof (char));
3592 		      sprintf (PCB->NetlistLib.Menu[i].Entry[j].ListEntry,
3593 			       "%s-%s", is[k], pin);
3594 		      k = c_cnt;
3595 		    }
3596 
3597 		}
3598 	      free (tmps);
3599 	    }
3600 	}
3601       for (k = 0; k < c_cnt; k++)
3602 	{
3603 	  free (was[k]);
3604 	  free (is[k]);
3605 	}
3606 
3607       NetlistChanged (0);
3608       IncrementUndoSerialNumber ();
3609       SetChangedFlag (true);
3610     }
3611 
3612   free (locked_element_list);
3613   free (element_list);
3614   free (cnt_list);
3615   free (is);
3616   free (was);
3617   return 0;
3618 }
3619 
3620 
3621 /* --------------------------------------------------------------------------- */
3622 
3623 static const char ripup_syntax[] = N_("RipUp(All|Selected|Element)");
3624 
3625 static const char ripup_help[] =
3626   N_("Ripup auto-routed tracks, or convert an element to parts.");
3627 
3628 /* %start-doc actions RipUp
3629 
3630 @table @code
3631 
3632 @item All
3633 Removes all lines and vias which were created by the autorouter.
3634 
3635 @item Selected
3636 Removes all selected lines and vias which were created by the
3637 autorouter.
3638 
3639 @item Element
3640 Converts the element under the cursor to parts (vias and lines).  Note
3641 that this uses the highest numbered paste buffer.
3642 
3643 @end table
3644 
3645 %end-doc */
3646 
3647 static int
ActionRipUp(int argc,char ** argv,Coord x,Coord y)3648 ActionRipUp (int argc, char **argv, Coord x, Coord y)
3649 {
3650   char *function = ARG (0);
3651   bool changed = false;
3652 
3653   if (function)
3654     {
3655       switch (GetFunctionID (function))
3656 	{
3657 	case F_All:
3658 	  ALLLINE_LOOP (PCB->Data);
3659 	  {
3660 	    if (TEST_FLAG (AUTOFLAG, line) && !TEST_FLAG (LOCKFLAG, line))
3661 	      {
3662 		RemoveObject (LINE_TYPE, layer, line, line);
3663 		changed = true;
3664 	      }
3665 	  }
3666 	  ENDALL_LOOP;
3667 	  ALLARC_LOOP (PCB->Data);
3668 	  {
3669 	    if (TEST_FLAG (AUTOFLAG, arc) && !TEST_FLAG (LOCKFLAG, arc))
3670 	      {
3671 		RemoveObject (ARC_TYPE, layer, arc, arc);
3672 		changed = true;
3673 	      }
3674 	  }
3675 	  ENDALL_LOOP;
3676 	  VIA_LOOP (PCB->Data);
3677 	  {
3678 	    if (TEST_FLAG (AUTOFLAG, via) && !TEST_FLAG (LOCKFLAG, via))
3679 	      {
3680 		RemoveObject (VIA_TYPE, via, via, via);
3681 		changed = true;
3682 	      }
3683 	  }
3684 	  END_LOOP;
3685 
3686 	  if (changed)
3687 	    {
3688 	      IncrementUndoSerialNumber ();
3689 	      SetChangedFlag (true);
3690 	    }
3691 	  break;
3692 	case F_Selected:
3693 	  VISIBLELINE_LOOP (PCB->Data);
3694 	  {
3695 	    if (TEST_FLAGS (AUTOFLAG | SELECTEDFLAG, line)
3696 		&& !TEST_FLAG (LOCKFLAG, line))
3697 	      {
3698 		RemoveObject (LINE_TYPE, layer, line, line);
3699 		changed = true;
3700 	      }
3701 	  }
3702 	  ENDALL_LOOP;
3703 	  if (PCB->ViaOn)
3704 	    VIA_LOOP (PCB->Data);
3705 	  {
3706 	    if (TEST_FLAGS (AUTOFLAG | SELECTEDFLAG, via)
3707 		&& !TEST_FLAG (LOCKFLAG, via))
3708 	      {
3709 		RemoveObject (VIA_TYPE, via, via, via);
3710 		changed = true;
3711 	      }
3712 	  }
3713 	  END_LOOP;
3714 	  if (changed)
3715 	    {
3716 	      IncrementUndoSerialNumber ();
3717 	      SetChangedFlag (true);
3718 	    }
3719 	  break;
3720 	case F_Element:
3721 	  {
3722 	    void *ptr1, *ptr2, *ptr3;
3723 
3724 	    if (SearchScreen (Crosshair.X, Crosshair.Y, ELEMENT_TYPE,
3725 			      &ptr1, &ptr2, &ptr3) != NO_TYPE)
3726 	      {
3727 		Note.Buffer = Settings.BufferNumber;
3728 		SetBufferNumber (MAX_BUFFER - 1);
3729 		ClearBuffer (PASTEBUFFER);
3730 		CopyObjectToBuffer (PASTEBUFFER->Data, PCB->Data,
3731 				    ELEMENT_TYPE, ptr1, ptr2, ptr3);
3732 		SmashBufferElement (PASTEBUFFER);
3733 		PASTEBUFFER->X = 0;
3734 		PASTEBUFFER->Y = 0;
3735 		SaveUndoSerialNumber ();
3736 		EraseObject (ELEMENT_TYPE, ptr1, ptr1);
3737 		MoveObjectToRemoveUndoList (ELEMENT_TYPE, ptr1, ptr2, ptr3);
3738 		RestoreUndoSerialNumber ();
3739 		CopyPastebufferToLayout (0, 0);
3740 		SetBufferNumber (Note.Buffer);
3741 		SetChangedFlag (true);
3742 	      }
3743 	  }
3744 	  break;
3745 	}
3746     }
3747   return 0;
3748 }
3749 
3750 /* --------------------------------------------------------------------------- */
3751 
3752 static const char addrats_syntax[] = N_("AddRats(AllRats|SelectedRats|Close)");
3753 
3754 static const char addrats_help[] =
3755   N_("Add one or more rat lines to the board.");
3756 
3757 /* %start-doc actions AddRats
3758 
3759 @table @code
3760 
3761 @item AllRats
3762 Create rat lines for all loaded nets that aren't already connected on
3763 with copper.
3764 
3765 @item SelectedRats
3766 Similarly, but only add rat lines for nets connected to selected pins
3767 and pads.
3768 
3769 @item Close
3770 Selects the shortest unselected rat on the board.
3771 
3772 @end table
3773 
3774 %end-doc */
3775 
3776 static int
ActionAddRats(int argc,char ** argv,Coord x,Coord y)3777 ActionAddRats (int argc, char **argv, Coord x, Coord y)
3778 {
3779   char *function = ARG (0);
3780   RatType *shorty;
3781   float len, small;
3782 
3783   if (function)
3784     {
3785       if (Settings.RatWarn)
3786 	ClearWarnings ();
3787       switch (GetFunctionID (function))
3788 	{
3789 	case F_AllRats:
3790 	  if (AddAllRats (false, NULL))
3791 	    SetChangedFlag (true);
3792 	  break;
3793 	case F_SelectedRats:
3794 	case F_Selected:
3795 	  if (AddAllRats (true, NULL))
3796 	    SetChangedFlag (true);
3797 	  break;
3798 	case F_Close:
3799 	  small = SQUARE (MAX_COORD);
3800 	  shorty = NULL;
3801 	  RAT_LOOP (PCB->Data);
3802 	  {
3803 	    if (TEST_FLAG (SELECTEDFLAG, line))
3804 	      continue;
3805 	    len = SQUARE (line->Point1.X - line->Point2.X) +
3806 	      SQUARE (line->Point1.Y - line->Point2.Y);
3807 	    if (len < small)
3808 	      {
3809 		small = len;
3810 		shorty = line;
3811 	      }
3812 	  }
3813 	  END_LOOP;
3814 	  if (shorty)
3815 	    {
3816 	      AddObjectToFlagUndoList (RATLINE_TYPE, shorty, shorty, shorty);
3817 	      SET_FLAG (SELECTEDFLAG, shorty);
3818 	      DrawRat (shorty);
3819 	      Draw ();
3820 	      CenterDisplay ((shorty->Point2.X + shorty->Point1.X) / 2,
3821 			     (shorty->Point2.Y + shorty->Point1.Y) / 2,
3822                              false);
3823 	    }
3824 	  break;
3825 	}
3826     }
3827   return 0;
3828 }
3829 
3830 /* --------------------------------------------------------------------------- */
3831 
3832 static const char delete_syntax[] =
3833   N_("Delete(Object|Selected)\n"
3834   "Delete(AllRats|SelectedRats)");
3835 
3836 static const char delete_help[] = N_("Delete stuff.");
3837 
3838 /* %start-doc actions Delete
3839 
3840 %end-doc */
3841 
3842 static int
ActionDelete(int argc,char ** argv,Coord x,Coord y)3843 ActionDelete (int argc, char **argv, Coord x, Coord y)
3844 {
3845   char *function = ARG (0);
3846   int id = GetFunctionID (function);
3847 
3848   Note.X = Crosshair.X;
3849   Note.Y = Crosshair.Y;
3850 
3851   if (id == -1) /* no arg */
3852     {
3853       if (RemoveSelected() == false)
3854 	id = F_Object;
3855     }
3856 
3857   switch (id)
3858     {
3859     case F_Object:
3860       SaveMode();
3861       SetMode(REMOVE_MODE);
3862       NotifyMode();
3863       RestoreMode();
3864       break;
3865     case F_Selected:
3866       RemoveSelected();
3867       break;
3868     case F_AllRats:
3869       if (DeleteRats (false))
3870 	SetChangedFlag (true);
3871       break;
3872     case F_SelectedRats:
3873       if (DeleteRats (true))
3874 	SetChangedFlag (true);
3875       break;
3876     }
3877 
3878   return 0;
3879 }
3880 
3881 /* --------------------------------------------------------------------------- */
3882 
3883 static const char deleterats_syntax[] =
3884   N_("DeleteRats(AllRats|Selected|SelectedRats)");
3885 
3886 static const char deleterats_help[] = N_("Delete rat lines.");
3887 
3888 /* %start-doc actions DeleteRats
3889 
3890 %end-doc */
3891 
3892 static int
ActionDeleteRats(int argc,char ** argv,Coord x,Coord y)3893 ActionDeleteRats (int argc, char **argv, Coord x, Coord y)
3894 {
3895   char *function = ARG (0);
3896   if (function)
3897     {
3898       if (Settings.RatWarn)
3899 	ClearWarnings ();
3900       switch (GetFunctionID (function))
3901 	{
3902 	case F_AllRats:
3903 	  if (DeleteRats (false))
3904 	    SetChangedFlag (true);
3905 	  break;
3906 	case F_SelectedRats:
3907 	case F_Selected:
3908 	  if (DeleteRats (true))
3909 	    SetChangedFlag (true);
3910 	  break;
3911 	}
3912     }
3913   return 0;
3914 }
3915 
3916 /* --------------------------------------------------------------------------- */
3917 
3918 static const char autoplace_syntax[] = N_("AutoPlaceSelected()");
3919 
3920 static const char autoplace_help[] = N_("Auto-place selected components.");
3921 
3922 /* %start-doc actions AutoPlaceSelected
3923 
3924 Attempts to re-arrange the selected components such that the nets
3925 connecting them are minimized.  Note that you cannot undo this.
3926 
3927 %end-doc */
3928 
3929 static int
ActionAutoPlaceSelected(int argc,char ** argv,Coord x,Coord y)3930 ActionAutoPlaceSelected (int argc, char **argv, Coord x, Coord y)
3931 {
3932   hid_action("Busy");
3933   if (gui->confirm_dialog (_("Auto-placement can NOT be undone.\n"
3934 			     "Do you want to continue anyway?\n"), 0))
3935     {
3936       if (AutoPlaceSelected ())
3937 	SetChangedFlag (true);
3938     }
3939   return 0;
3940 }
3941 
3942 /* --------------------------------------------------------------------------- */
3943 
3944 static const char autoroute_syntax[] = N_("AutoRoute(AllRats|SelectedRats)");
3945 
3946 static const char autoroute_help[] = N_("Auto-route some or all rat lines.");
3947 
3948 /* %start-doc actions AutoRoute
3949 
3950 @table @code
3951 
3952 @item AllRats
3953 Attempt to autoroute all rats.
3954 
3955 @item SelectedRats
3956 Attempt to autoroute the selected rats.
3957 
3958 @end table
3959 
3960 Before autorouting, it's important to set up a few things.  First,
3961 make sure any layers you aren't using are disabled, else the
3962 autorouter may use them.  Next, make sure the current line and via
3963 styles are set accordingly.  Last, make sure "new lines clear
3964 polygons" is set, in case you eventually want to add a copper pour.
3965 
3966 Autorouting takes a while.  During this time, the program may not be
3967 responsive.
3968 
3969 %end-doc */
3970 
3971 static int
ActionAutoRoute(int argc,char ** argv,Coord x,Coord y)3972 ActionAutoRoute (int argc, char **argv, Coord x, Coord y)
3973 {
3974   char *function = ARG (0);
3975   hid_action("Busy");
3976   if (function)			/* one parameter */
3977     {
3978       switch (GetFunctionID (function))
3979 	{
3980 	case F_AllRats:
3981 	  if (AutoRoute (false))
3982 	    SetChangedFlag (true);
3983 	  break;
3984 	case F_SelectedRats:
3985 	case F_Selected:
3986 	  if (AutoRoute (true))
3987 	    SetChangedFlag (true);
3988 	  break;
3989 	}
3990     }
3991   return 0;
3992 }
3993 
3994 /* --------------------------------------------------------------------------- */
3995 
3996 static const char markcrosshair_syntax[] =
3997   N_("MarkCrosshair()\n"
3998   "MarkCrosshair(Center)");
3999 
4000 static const char markcrosshair_help[] = N_("Set/Reset the Crosshair mark.");
4001 
4002 /* %start-doc actions MarkCrosshair
4003 
4004 The ``mark'' is a small X-shaped target on the display which is
4005 treated like a second origin (the normal origin is the upper let
4006 corner of the board).  The GUI will display a second set of
4007 coordinates for this mark, which tells you how far you are from it.
4008 
4009 If no argument is given, the mark is toggled - disabled if it was
4010 enabled, or enabled at the current cursor position of disabled.  If
4011 the @code{Center} argument is given, the mark is moved to the current
4012 cursor location.
4013 
4014 %end-doc */
4015 
4016 static int
ActionMarkCrosshair(int argc,char ** argv,Coord x,Coord y)4017 ActionMarkCrosshair (int argc, char **argv, Coord x, Coord y)
4018 {
4019   char *function = ARG (0);
4020   if (!function || !*function)
4021     {
4022       if (Marked.status)
4023 	{
4024 	  notify_mark_change (false);
4025 	  Marked.status = false;
4026 	  notify_mark_change (true);
4027 	}
4028       else
4029 	{
4030 	  notify_mark_change (false);
4031 	  Marked.status = false;
4032 	  Marked.status = true;
4033 	  Marked.X = Crosshair.X;
4034 	  Marked.Y = Crosshair.Y;
4035 	  notify_mark_change (true);
4036 	}
4037     }
4038   else if (GetFunctionID (function) == F_Center)
4039     {
4040       notify_mark_change (false);
4041       Marked.status = true;
4042       Marked.X = Crosshair.X;
4043       Marked.Y = Crosshair.Y;
4044       notify_mark_change (true);
4045     }
4046   return 0;
4047 }
4048 
4049 /* --------------------------------------------------------------------------- */
4050 
4051 static const char changesize_syntax[] =
4052   N_("ChangeSize(Object, delta)\n"
4053   "ChangeSize(SelectedObjects|Selected, delta)\n"
4054   "ChangeSize(SelectedLines|SelectedPins|SelectedVias, delta)\n"
4055   "ChangeSize(SelectedPads|SelectedTexts|SelectedNames, delta)\n"
4056   "ChangeSize(SelectedElements, delta)");
4057 
4058 static const char changesize_help[] = N_("Changes the size of objects.");
4059 
4060 /* %start-doc actions ChangeSize
4061 
4062 For lines and arcs, this changes the width.  For pins and vias, this
4063 changes the overall diameter of the copper annulus.  For pads, this
4064 changes the width and, indirectly, the length.  For texts and names,
4065 this changes the scaling factor.  For elements, this changes the width
4066 of the silk layer lines and arcs for this element.
4067 
4068 %end-doc */
4069 
4070 static int
ActionChangeSize(int argc,char ** argv,Coord x,Coord y)4071 ActionChangeSize (int argc, char **argv, Coord x, Coord y)
4072 {
4073   char *function = ARG (0);
4074   char *delta = ARG (1);
4075   char *units = ARG (2);
4076   bool absolute;			/* indicates if absolute size is given */
4077   Coord value;
4078 
4079   if (function && delta)
4080     {
4081       value = GetValue (delta, units, &absolute);
4082       if (value == 0)
4083         value = delta[0] == '-' ? -Settings.increments->size
4084                                 :  Settings.increments->size;
4085       switch (GetFunctionID (function))
4086 	{
4087 	case F_Object:
4088 	  {
4089 	    int type;
4090 	    void *ptr1, *ptr2, *ptr3;
4091 
4092 	    if ((type =
4093 		 SearchScreen (Crosshair.X, Crosshair.Y, CHANGESIZE_TYPES,
4094 			       &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4095 	      if (TEST_FLAG (LOCKFLAG, (PinType *) ptr2))
4096 		Message (_("Sorry, the object is locked\n"));
4097 	    if (ChangeObjectSize (type, ptr1, ptr2, ptr3, value, absolute))
4098 	      SetChangedFlag (true);
4099 	    break;
4100 	  }
4101 
4102 	case F_SelectedVias:
4103 	  if (ChangeSelectedSize (VIA_TYPE, value, absolute))
4104 	    SetChangedFlag (true);
4105 	  break;
4106 
4107 	case F_SelectedPins:
4108 	  if (ChangeSelectedSize (PIN_TYPE, value, absolute))
4109 	    SetChangedFlag (true);
4110 	  break;
4111 
4112 	case F_SelectedPads:
4113 	  if (ChangeSelectedSize (PAD_TYPE, value, absolute))
4114 	    SetChangedFlag (true);
4115 	  break;
4116 
4117 	case F_SelectedArcs:
4118 	  if (ChangeSelectedSize (ARC_TYPE, value, absolute))
4119 	    SetChangedFlag (true);
4120 	  break;
4121 
4122 	case F_SelectedLines:
4123 	  if (ChangeSelectedSize (LINE_TYPE, value, absolute))
4124 	    SetChangedFlag (true);
4125 	  break;
4126 
4127 	case F_SelectedTexts:
4128 	  if (ChangeSelectedSize (TEXT_TYPE, value, absolute))
4129 	    SetChangedFlag (true);
4130 	  break;
4131 
4132 	case F_SelectedNames:
4133 	  if (ChangeSelectedSize (ELEMENTNAME_TYPE, value, absolute))
4134 	    SetChangedFlag (true);
4135 	  break;
4136 
4137 	case F_SelectedElements:
4138 	  if (ChangeSelectedSize (ELEMENT_TYPE, value, absolute))
4139 	    SetChangedFlag (true);
4140 	  break;
4141 
4142 	case F_Selected:
4143 	case F_SelectedObjects:
4144 	  if (ChangeSelectedSize (CHANGESIZE_TYPES, value, absolute))
4145 	    SetChangedFlag (true);
4146 	  break;
4147 	}
4148     }
4149   return 0;
4150 }
4151 
4152 /* --------------------------------------------------------------------------- */
4153 
4154 static const char changedrillsize_syntax[] =
4155   N_("ChangeDrillSize(Object, delta)\n"
4156   "ChangeDrillSize(SelectedPins|SelectedVias|Selected|SelectedObjects, delta)");
4157 
4158 static const char changedrillsize_help[] =
4159   N_("Changes the drilling hole size of objects.");
4160 
4161 /* %start-doc actions ChangeDrillSize
4162 
4163 %end-doc */
4164 
4165 static int
ActionChange2ndSize(int argc,char ** argv,Coord x,Coord y)4166 ActionChange2ndSize (int argc, char **argv, Coord x, Coord y)
4167 {
4168   char *function = ARG (0);
4169   char *delta = ARG (1);
4170   char *units = ARG (2);
4171   bool absolute;
4172   Coord value;
4173 
4174   if (function && delta)
4175     {
4176       value = GetValue (delta, units, &absolute);
4177       switch (GetFunctionID (function))
4178 	{
4179 	case F_Object:
4180 	  {
4181 	    int type;
4182 	    void *ptr1, *ptr2, *ptr3;
4183 
4184 	    gui->get_coords (_("Select an Object"), &x, &y);
4185 	    if ((type =
4186 		 SearchScreen (x, y, CHANGE2NDSIZE_TYPES,
4187 			       &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4188 	      if (ChangeObject2ndSize
4189 		  (type, ptr1, ptr2, ptr3, value, absolute, true))
4190 		SetChangedFlag (true);
4191 	    break;
4192 	  }
4193 
4194 	case F_SelectedVias:
4195 	  if (ChangeSelected2ndSize (VIA_TYPE, value, absolute))
4196 	    SetChangedFlag (true);
4197 	  break;
4198 
4199 	case F_SelectedPins:
4200 	  if (ChangeSelected2ndSize (PIN_TYPE, value, absolute))
4201 	    SetChangedFlag (true);
4202 	  break;
4203 	case F_Selected:
4204 	case F_SelectedObjects:
4205 	  if (ChangeSelected2ndSize (PIN_TYPES, value, absolute))
4206 	    SetChangedFlag (true);
4207 	  break;
4208 	}
4209     }
4210   return 0;
4211 }
4212 
4213 /* --------------------------------------------------------------------------- */
4214 
4215 static const char changeclearsize_syntax[] =
4216   N_("ChangeClearSize(Object, delta)\n"
4217   "ChangeClearSize(SelectedPins|SelectedPads|SelectedVias, delta)\n"
4218   "ChangeClearSize(SelectedLines|SelectedArcs, delta\n"
4219   "ChangeClearSize(Selected|SelectedObjects, delta)");
4220 
4221 static const char changeclearsize_help[] =
4222   N_("Changes the clearance size of objects.");
4223 
4224 /* %start-doc actions ChangeClearSize
4225 
4226 If the solder mask is currently showing, this action changes the
4227 solder mask clearance.  If the mask is not showing, this action
4228 changes the polygon clearance.
4229 
4230 %end-doc */
4231 
4232 static int
ActionChangeClearSize(int argc,char ** argv,Coord x,Coord y)4233 ActionChangeClearSize (int argc, char **argv, Coord x, Coord y)
4234 {
4235   char *function = ARG (0);
4236   char *delta = ARG (1);
4237   char *units = ARG (2);
4238   bool absolute;
4239   Coord value;
4240 
4241   if (function && delta)
4242     {
4243       value = 2 * GetValue (delta, units, &absolute);
4244       if ((value == 0) && !absolute)
4245         value = delta[0] == '-' ? -Settings.increments->clear
4246                                 :  Settings.increments->clear;
4247       switch (GetFunctionID (function))
4248 	{
4249 	case F_Object:
4250 	  {
4251 	    int type;
4252 	    void *ptr1, *ptr2, *ptr3;
4253 
4254 	    gui->get_coords (_("Select an Object"), &x, &y);
4255 	    if ((type =
4256 		 SearchScreen (x, y,
4257 			       CHANGECLEARSIZE_TYPES, &ptr1, &ptr2,
4258 			       &ptr3)) != NO_TYPE)
4259 	      if (ChangeObjectClearSize (type, ptr1, ptr2, ptr3, value, absolute))
4260 		SetChangedFlag (true);
4261 	    break;
4262 	  }
4263 	case F_SelectedVias:
4264 	  if (ChangeSelectedClearSize (VIA_TYPE, value, absolute))
4265 	    SetChangedFlag (true);
4266 	  break;
4267 	case F_SelectedPads:
4268 	  if (ChangeSelectedClearSize (PAD_TYPE, value, absolute))
4269 	    SetChangedFlag (true);
4270 	  break;
4271 	case F_SelectedPins:
4272 	  if (ChangeSelectedClearSize (PIN_TYPE, value, absolute))
4273 	    SetChangedFlag (true);
4274 	  break;
4275 	case F_SelectedLines:
4276 	  if (ChangeSelectedClearSize (LINE_TYPE, value, absolute))
4277 	    SetChangedFlag (true);
4278 	  break;
4279 	case F_SelectedArcs:
4280 	  if (ChangeSelectedClearSize (ARC_TYPE, value, absolute))
4281 	    SetChangedFlag (true);
4282 	  break;
4283 	case F_Selected:
4284 	case F_SelectedObjects:
4285 	  if (ChangeSelectedClearSize (CHANGECLEARSIZE_TYPES, value, absolute))
4286 	    SetChangedFlag (true);
4287 	  break;
4288 	}
4289     }
4290   return 0;
4291 }
4292 
4293 /* ---------------------------------------------------------------------------  */
4294 
4295 static const char minmaskgap_syntax[] =
4296   N_("MinMaskGap(delta)\n"
4297   "MinMaskGap(Selected, delta)");
4298 
4299 static const char minmaskgap_help[] =
4300   N_("Ensures the mask is a minimum distance from pins and pads.");
4301 
4302 /* %start-doc actions MinMaskGap
4303 
4304 Checks all specified pins and/or pads, and increases the mask if
4305 needed to ensure a minimum distance between the pin or pad edge and
4306 the mask edge.
4307 
4308 %end-doc */
4309 
4310 static int
ActionMinMaskGap(int argc,char ** argv,Coord x,Coord y)4311 ActionMinMaskGap (int argc, char **argv, Coord x, Coord y)
4312 {
4313   char *function = ARG (0);
4314   char *delta = ARG (1);
4315   char *units = ARG (2);
4316   bool absolute;
4317   Coord value;
4318   Coord thickness;
4319   int flags;
4320 
4321   if (!function)
4322     return 1;
4323   if (strcasecmp (function, "Selected") == 0)
4324     flags = SELECTEDFLAG;
4325   else
4326     {
4327       units = delta;
4328       delta = function;
4329       flags = 0;
4330     }
4331   value = 2 * GetValue (delta, units, &absolute);
4332 
4333   SaveUndoSerialNumber ();
4334   ELEMENT_LOOP (PCB->Data);
4335   {
4336     PIN_LOOP (element);
4337     {
4338       if (!TEST_FLAGS (flags, pin) || ! pin->Mask) continue;
4339 
4340       thickness = pin->DrillingHole;
4341 
4342       if (pin->Thickness > thickness) thickness = pin->Thickness;
4343 
4344       thickness += value;
4345 
4346       if (pin->Mask < thickness)
4347 	{
4348 	  ChangeObjectMaskSize (PIN_TYPE, element, pin, 0, thickness, 1);
4349 	  RestoreUndoSerialNumber ();
4350 	}
4351     }
4352     END_LOOP;
4353     PAD_LOOP (element);
4354     {
4355       if (!TEST_FLAGS (flags, pad) || ! pad->Mask)
4356 	continue;
4357       if (pad->Mask < pad->Thickness + value)
4358 	{
4359 	  ChangeObjectMaskSize (PAD_TYPE, element, pad, 0,
4360 				pad->Thickness + value, 1);
4361 	  RestoreUndoSerialNumber ();
4362 	}
4363     }
4364     END_LOOP;
4365   }
4366   END_LOOP;
4367   VIA_LOOP (PCB->Data);
4368   {
4369     if (!TEST_FLAGS (flags, via) || ! via->Mask)
4370       continue;
4371 
4372     thickness = via->DrillingHole;
4373     if (via->Thickness > thickness)
4374       thickness = via->Thickness;
4375     thickness += value;
4376 
4377     if (via->Mask < thickness)
4378       {
4379 	ChangeObjectMaskSize (VIA_TYPE, via, 0, 0, thickness, 1);
4380 	RestoreUndoSerialNumber ();
4381       }
4382   }
4383   END_LOOP;
4384   RestoreUndoSerialNumber ();
4385   IncrementUndoSerialNumber ();
4386   return 0;
4387 }
4388 
4389 /* ---------------------------------------------------------------------------  */
4390 
4391 static const char mincleargap_syntax[] =
4392   N_("MinClearGap(delta)\n"
4393   "MinClearGap(Selected, delta)");
4394 
4395 static const char mincleargap_help[] =
4396   N_("Ensures that polygons are a minimum distance from objects.");
4397 
4398 /* %start-doc actions MinClearGap
4399 
4400 Checks all specified objects, and increases the polygon clearance if
4401 needed to ensure a minimum distance between their edges and the
4402 polygon edges.
4403 
4404 %end-doc */
4405 
4406 static int
ActionMinClearGap(int argc,char ** argv,Coord x,Coord y)4407 ActionMinClearGap (int argc, char **argv, Coord x, Coord y)
4408 {
4409   char *function = ARG (0);
4410   char *delta = ARG (1);
4411   char *units = ARG (2);
4412   bool absolute;
4413   Coord value;
4414   int flags;
4415 
4416   if (!function)
4417     return 1;
4418   if (strcasecmp (function, "Selected") == 0)
4419     flags = SELECTEDFLAG;
4420   else
4421     {
4422       units = delta;
4423       delta = function;
4424       flags = 0;
4425     }
4426   value = 2 * GetValue (delta, units, &absolute);
4427 
4428   SaveUndoSerialNumber ();
4429   ELEMENT_LOOP (PCB->Data);
4430   {
4431     PIN_LOOP (element);
4432     {
4433       if (!TEST_FLAGS (flags, pin))
4434 	continue;
4435       if (pin->Clearance < value)
4436 	{
4437 	  ChangeObjectClearSize (PIN_TYPE, element, pin, 0,
4438 				value, 1);
4439 	  RestoreUndoSerialNumber ();
4440 	}
4441     }
4442     END_LOOP;
4443     PAD_LOOP (element);
4444     {
4445       if (!TEST_FLAGS (flags, pad))
4446 	continue;
4447       if (pad->Clearance < value)
4448 	{
4449 	  ChangeObjectClearSize (PAD_TYPE, element, pad, 0,
4450 				value, 1);
4451 	  RestoreUndoSerialNumber ();
4452 	}
4453     }
4454     END_LOOP;
4455   }
4456   END_LOOP;
4457   VIA_LOOP (PCB->Data);
4458   {
4459     if (!TEST_FLAGS (flags, via))
4460       continue;
4461     if (via->Clearance < value)
4462       {
4463 	ChangeObjectClearSize (VIA_TYPE, via, 0, 0, value, 1);
4464 	RestoreUndoSerialNumber ();
4465       }
4466   }
4467   END_LOOP;
4468   ALLLINE_LOOP (PCB->Data);
4469   {
4470     if (!TEST_FLAGS (flags, line))
4471       continue;
4472     if (line->Clearance < value)
4473       {
4474 	ChangeObjectClearSize (LINE_TYPE, layer, line, 0, value, 1);
4475 	RestoreUndoSerialNumber ();
4476       }
4477   }
4478   ENDALL_LOOP;
4479   ALLARC_LOOP (PCB->Data);
4480   {
4481     if (!TEST_FLAGS (flags, arc))
4482       continue;
4483     if (arc->Clearance < value)
4484       {
4485 	ChangeObjectClearSize (ARC_TYPE, layer, arc, 0, value, 1);
4486 	RestoreUndoSerialNumber ();
4487       }
4488   }
4489   ENDALL_LOOP;
4490   RestoreUndoSerialNumber ();
4491   IncrementUndoSerialNumber ();
4492   return 0;
4493 }
4494 
4495 /* ---------------------------------------------------------------------------  */
4496 
4497 static const char changepinname_syntax[] =
4498   N_("ChangePinName(ElementName,PinNumber,PinName)");
4499 
4500 static const char changepinname_help[] =
4501   N_("Sets the name of a specific pin on a specific element.");
4502 
4503 /* %start-doc actions ChangePinName
4504 
4505 This can be especially useful for annotating pin names from a
4506 schematic to the layout without requiring knowledge of the pcb file
4507 format.
4508 
4509 @example
4510 ChangePinName(U3, 7, VCC)
4511 @end example
4512 
4513 %end-doc */
4514 
4515 static int
ActionChangePinName(int argc,char ** argv,Coord x,Coord y)4516 ActionChangePinName (int argc, char **argv, Coord x, Coord y)
4517 {
4518   int changed = 0;
4519   char *refdes, *pinnum, *pinname;
4520 
4521   if (argc != 3)
4522     {
4523       AFAIL (changepinname);
4524     }
4525 
4526   refdes = argv[0];
4527   pinnum = argv[1];
4528   pinname = argv[2];
4529 
4530   ELEMENT_LOOP (PCB->Data);
4531   {
4532     if (NSTRCMP (refdes, NAMEONPCB_NAME (element)) == 0)
4533       {
4534 	PIN_LOOP (element);
4535 	{
4536 	  if (NSTRCMP (pinnum, pin->Number) == 0)
4537 	    {
4538 	      AddObjectToChangeNameUndoList (PIN_TYPE, NULL, NULL,
4539 					     pin, pin->Name);
4540 	      /*
4541 	       * Note:  we can't free() pin->Name first because
4542 	       * it is used in the undo list
4543 	       */
4544 	      pin->Name = strdup (pinname);
4545 	      SetChangedFlag (true);
4546 	      changed = 1;
4547 	    }
4548 	}
4549 	END_LOOP;
4550 
4551 	PAD_LOOP (element);
4552 	{
4553 	  if (NSTRCMP (pinnum, pad->Number) == 0)
4554 	    {
4555 	      AddObjectToChangeNameUndoList (PAD_TYPE, NULL, NULL,
4556 					     pad, pad->Name);
4557 	      /*
4558 	       * Note:  we can't free() pad->Name first because
4559 	       * it is used in the undo list
4560 	       */
4561 	      pad->Name = strdup (pinname);
4562 	      SetChangedFlag (true);
4563 	      changed = 1;
4564 	    }
4565 	}
4566 	END_LOOP;
4567       }
4568   }
4569   END_LOOP;
4570   /*
4571    * done with our action so increment the undo # if we actually
4572    * changed anything
4573    */
4574   if (changed)
4575     {
4576       if (defer_updates)
4577 	defer_needs_update = 1;
4578       else
4579 	{
4580 	  IncrementUndoSerialNumber ();
4581 	  gui->invalidate_all ();
4582 	}
4583     }
4584 
4585   return 0;
4586 }
4587 
4588 /* --------------------------------------------------------------------------- */
4589 
4590 static const char changename_syntax[] =
4591   N_("ChangeName(Object)\n"
4592   "ChangeName(Layout|Layer)");
4593 
4594 static const char changename_help[] = N_("Sets the name of objects.");
4595 
4596 /* %start-doc actions ChangeName
4597 
4598 @table @code
4599 
4600 @item Object
4601 Changes the name of the element under the cursor.
4602 
4603 @item Layout
4604 Changes the name of the layout.  This is printed on the fab drawings.
4605 
4606 @item Layer
4607 Changes the name of the currently active layer.
4608 
4609 @end table
4610 
4611 %end-doc */
4612 
4613 int
ActionChangeName(int argc,char ** argv,Coord x,Coord y)4614 ActionChangeName (int argc, char **argv, Coord x, Coord y)
4615 {
4616   char *function = ARG (0);
4617   char *name;
4618 
4619   if (function)
4620     {
4621       switch (GetFunctionID (function))
4622 	{
4623 	  /* change the name of an object */
4624 	case F_Object:
4625 	  {
4626 	    int type;
4627 	    void *ptr1, *ptr2, *ptr3;
4628 
4629 	    gui->get_coords (_("Select an Object"), &x, &y);
4630 	    if ((type =
4631 		 SearchScreen (x, y, CHANGENAME_TYPES,
4632 			       &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4633 	      {
4634 		SaveUndoSerialNumber ();
4635 		if (QueryInputAndChangeObjectName (type, ptr1, ptr2, ptr3))
4636 		  {
4637 		    SetChangedFlag (true);
4638 		    if (type == ELEMENT_TYPE)
4639 		      {
4640 			RubberbandType *ptr;
4641 			int i;
4642 
4643 			RestoreUndoSerialNumber ();
4644 			Crosshair.AttachedObject.RubberbandN = 0;
4645 			LookupRatLines (type, ptr1, ptr2, ptr3);
4646 			ptr = Crosshair.AttachedObject.Rubberband;
4647 			for (i = 0; i < Crosshair.AttachedObject.RubberbandN;
4648 			     i++, ptr++)
4649 			  {
4650 			    if (PCB->RatOn)
4651 			      EraseRat ((RatType *) ptr->Line);
4652 			    MoveObjectToRemoveUndoList (RATLINE_TYPE,
4653 							ptr->Line, ptr->Line,
4654 							ptr->Line);
4655 			  }
4656 			IncrementUndoSerialNumber ();
4657 			Draw ();
4658 		      }
4659 		  }
4660 	      }
4661 	    break;
4662 	  }
4663 
4664 	  /* change the layout's name */
4665 	case F_Layout:
4666 	  name =
4667 	    gui->prompt_for (_("Enter the layout name:"), EMPTY (PCB->Name));
4668 	  /* NB: ChangeLayoutName takes ownership of the passed memory */
4669 	  if (name && ChangeLayoutName (name))
4670 	    SetChangedFlag (true);
4671 	  break;
4672 
4673 	  /* change the name of the active layer */
4674 	case F_Layer:
4675 	  name = gui->prompt_for (_("Enter the layer name:"),
4676 				  EMPTY (CURRENT->Name));
4677 	  /* NB: ChangeLayerName takes ownership of the passed memory */
4678 	  if (name && ChangeLayerName (CURRENT, name))
4679 	    SetChangedFlag (true);
4680 	  break;
4681 	}
4682     }
4683   return 0;
4684 }
4685 
4686 
4687 /* --------------------------------------------------------------------------- */
4688 
4689 static const char morphpolygon_syntax[] = N_("MorphPolygon(Object|Selected)");
4690 
4691 static const char morphpolygon_help[] =
4692   N_("Converts dead polygon islands into separate polygons.");
4693 
4694 /* %start-doc actions MorphPolygon
4695 
4696 If a polygon is divided into unconnected "islands", you can use
4697 this command to convert the otherwise disappeared islands into
4698 separate polygons. Be sure the cursor is over a portion of the
4699 polygon that remains visible. Very small islands that may flake
4700 off are automatically deleted.
4701 
4702 %end-doc */
4703 
4704 static int
ActionMorphPolygon(int argc,char ** argv,Coord x,Coord y)4705 ActionMorphPolygon (int argc, char **argv, Coord x, Coord y)
4706 {
4707   char *function = ARG (0);
4708   if (function)
4709     {
4710       switch (GetFunctionID (function))
4711 	{
4712 	case F_Object:
4713 	  {
4714 	    int type;
4715 	    void *ptr1, *ptr2, *ptr3;
4716 
4717 	    gui->get_coords (_("Select an Object"), &x, &y);
4718 	    if ((type = SearchScreen (x, y, POLYGON_TYPE,
4719 				      &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4720 	      {
4721 		MorphPolygon ((LayerType *) ptr1, (PolygonType *) ptr3);
4722 		Draw ();
4723 		IncrementUndoSerialNumber ();
4724 	      }
4725 	    break;
4726 	  }
4727 	case F_Selected:
4728 	case F_SelectedObjects:
4729 	  ALLPOLYGON_LOOP (PCB->Data);
4730 	  {
4731 	    if (TEST_FLAG (SELECTEDFLAG, polygon))
4732 	      MorphPolygon (layer, polygon);
4733 	  }
4734 	  ENDALL_LOOP;
4735 	  Draw ();
4736 	  IncrementUndoSerialNumber ();
4737 	  break;
4738 	}
4739     }
4740   return 0;
4741 }
4742 
4743 /* --------------------------------------------------------------------------- */
4744 
4745 static const char togglehidename_syntax[] =
4746   N_("ToggleHideName(Object|SelectedElements)");
4747 
4748 static const char togglehidename_help[] =
4749   N_("Toggles the visibility of element names.");
4750 
4751 /* %start-doc actions ToggleHideName
4752 
4753 If names are hidden you won't see them on the screen and they will not
4754 appear on the silk layer when you print the layout.
4755 
4756 %end-doc */
4757 
4758 static int
ActionToggleHideName(int argc,char ** argv,Coord x,Coord y)4759 ActionToggleHideName (int argc, char **argv, Coord x, Coord y)
4760 {
4761   char *function = ARG (0);
4762   if (function && PCB->ElementOn)
4763     {
4764       switch (GetFunctionID (function))
4765 	{
4766 	case F_Object:
4767 	  {
4768 	    int type;
4769 	    void *ptr1, *ptr2, *ptr3;
4770 
4771 	    gui->get_coords (_("Select an Object"), &x, &y);
4772 	    if ((type = SearchScreen (x, y, ELEMENT_TYPE,
4773 				      &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4774 	      {
4775 		AddObjectToFlagUndoList (type, ptr1, ptr2, ptr3);
4776 		EraseElementName ((ElementType *) ptr2);
4777 		TOGGLE_FLAG (HIDENAMEFLAG, (ElementType *) ptr2);
4778 		DrawElementName ((ElementType *) ptr2);
4779 		Draw ();
4780 		IncrementUndoSerialNumber ();
4781 	      }
4782 	    break;
4783 	  }
4784 	case F_SelectedElements:
4785 	case F_Selected:
4786 	  {
4787 	    bool changed = false;
4788 	    ELEMENT_LOOP (PCB->Data);
4789 	    {
4790 	      if ((TEST_FLAG (SELECTEDFLAG, element) ||
4791 		   TEST_FLAG (SELECTEDFLAG,
4792 			      &NAMEONPCB_TEXT (element)))
4793 		  && (FRONT (element) || PCB->InvisibleObjectsOn))
4794 		{
4795 		  AddObjectToFlagUndoList (ELEMENT_TYPE, element,
4796 					   element, element);
4797 		  EraseElementName (element);
4798 		  TOGGLE_FLAG (HIDENAMEFLAG, element);
4799 		  DrawElementName (element);
4800 		  changed = true;
4801 		}
4802 	    }
4803 	    END_LOOP;
4804 	    if (changed)
4805 	      {
4806 		Draw ();
4807 		IncrementUndoSerialNumber ();
4808 	      }
4809 	  }
4810 	}
4811     }
4812   return 0;
4813 }
4814 
4815 /* --------------------------------------------------------------------------- */
4816 
4817 static const char changejoin_syntax[] =
4818   N_("ChangeJoin(ToggleObject|SelectedLines|SelectedArcs|Selected)");
4819 
4820 static const char changejoin_help[] =
4821   N_("Changes the join (clearance through polygons) of objects.");
4822 
4823 /* %start-doc actions ChangeJoin
4824 
4825 The join flag determines whether a line or arc, drawn to intersect a
4826 polygon, electrically connects to the polygon or not.  When joined,
4827 the line/arc is simply drawn over the polygon, making an electrical
4828 connection.  When not joined, a gap is drawn between the line and the
4829 polygon, insulating them from each other.
4830 
4831 %end-doc */
4832 
4833 static int
ActionChangeJoin(int argc,char ** argv,Coord x,Coord y)4834 ActionChangeJoin (int argc, char **argv, Coord x, Coord y)
4835 {
4836   char *function = ARG (0);
4837   if (function)
4838     {
4839       switch (GetFunctionID (function))
4840 	{
4841 	case F_ToggleObject:
4842 	case F_Object:
4843 	  {
4844 	    int type;
4845 	    void *ptr1, *ptr2, *ptr3;
4846 
4847 	    gui->get_coords (_("Select an Object"), &x, &y);
4848 	    if ((type =
4849 		 SearchScreen (x, y, CHANGEJOIN_TYPES,
4850 			       &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4851 	      if (ChangeObjectJoin (type, ptr1, ptr2, ptr3))
4852 		SetChangedFlag (true);
4853 	    break;
4854 	  }
4855 
4856 	case F_SelectedLines:
4857 	  if (ChangeSelectedJoin (LINE_TYPE))
4858 	    SetChangedFlag (true);
4859 	  break;
4860 
4861 	case F_SelectedArcs:
4862 	  if (ChangeSelectedJoin (ARC_TYPE))
4863 	    SetChangedFlag (true);
4864 	  break;
4865 
4866 	case F_Selected:
4867 	case F_SelectedObjects:
4868 	  if (ChangeSelectedJoin (CHANGEJOIN_TYPES))
4869 	    SetChangedFlag (true);
4870 	  break;
4871 	}
4872     }
4873   return 0;
4874 }
4875 
4876 /* --------------------------------------------------------------------------- */
4877 
4878 static const char changesquare_syntax[] =
4879   N_("ChangeSquare(ToggleObject)\n"
4880   "ChangeSquare(SelectedElements|SelectedPins)\n"
4881   "ChangeSquare(Selected|SelectedObjects)");
4882 
4883 static const char changesquare_help[] =
4884   N_("Changes the square flag of pins and pads.");
4885 
4886 /* %start-doc actions ChangeSquare
4887 
4888 Note that @code{Pins} means both pins and pads.
4889 
4890 @pinshapes
4891 
4892 %end-doc */
4893 
4894 static int
ActionChangeSquare(int argc,char ** argv,Coord x,Coord y)4895 ActionChangeSquare (int argc, char **argv, Coord x, Coord y)
4896 {
4897   char *function = ARG (0);
4898   if (function)
4899     {
4900       switch (GetFunctionID (function))
4901 	{
4902 	case F_ToggleObject:
4903 	case F_Object:
4904 	  {
4905 	    int type;
4906 	    void *ptr1, *ptr2, *ptr3;
4907 
4908 	    gui->get_coords (_("Select an Object"), &x, &y);
4909 	    if ((type =
4910 		 SearchScreen (x, y, CHANGESQUARE_TYPES,
4911 			       &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4912 	      if (ChangeObjectSquare (type, ptr1, ptr2, ptr3))
4913 		SetChangedFlag (true);
4914 	    break;
4915 	  }
4916 
4917 	case F_SelectedElements:
4918 	  if (ChangeSelectedSquare (ELEMENT_TYPE))
4919 	    SetChangedFlag (true);
4920 	  break;
4921 
4922 	case F_SelectedPins:
4923 	  if (ChangeSelectedSquare (PIN_TYPE | PAD_TYPE))
4924 	    SetChangedFlag (true);
4925 	  break;
4926 
4927 	case F_Selected:
4928 	case F_SelectedObjects:
4929 	  if (ChangeSelectedSquare (PIN_TYPE | PAD_TYPE))
4930 	    SetChangedFlag (true);
4931 	  break;
4932 	}
4933     }
4934   return 0;
4935 }
4936 
4937 /* --------------------------------------------------------------------------- */
4938 
4939 static const char setsquare_syntax[] =
4940   N_("SetSquare(ToggleObject|SelectedElements|SelectedPins)");
4941 
4942 static const char setsquare_help[] = N_("sets the square-flag of objects.");
4943 
4944 /* %start-doc actions SetSquare
4945 
4946 Note that @code{Pins} means pins and pads.
4947 
4948 @pinshapes
4949 
4950 %end-doc */
4951 
4952 static int
ActionSetSquare(int argc,char ** argv,Coord x,Coord y)4953 ActionSetSquare (int argc, char **argv, Coord x, Coord y)
4954 {
4955   char *function = ARG (0);
4956   if (function && *function)
4957     {
4958       switch (GetFunctionID (function))
4959 	{
4960 	case F_ToggleObject:
4961 	case F_Object:
4962 	  {
4963 	    int type;
4964 	    void *ptr1, *ptr2, *ptr3;
4965 
4966 	    gui->get_coords (_("Select an Object"), &x, &y);
4967 	    if ((type =
4968 		 SearchScreen (x, y, CHANGESQUARE_TYPES,
4969 			       &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4970 	      if (SetObjectSquare (type, ptr1, ptr2, ptr3))
4971 		SetChangedFlag (true);
4972 	    break;
4973 	  }
4974 
4975 	case F_SelectedElements:
4976 	  if (SetSelectedSquare (ELEMENT_TYPE))
4977 	    SetChangedFlag (true);
4978 	  break;
4979 
4980 	case F_SelectedPins:
4981 	  if (SetSelectedSquare (PIN_TYPE | PAD_TYPE))
4982 	    SetChangedFlag (true);
4983 	  break;
4984 
4985 	case F_Selected:
4986 	case F_SelectedObjects:
4987 	  if (SetSelectedSquare (PIN_TYPE | PAD_TYPE))
4988 	    SetChangedFlag (true);
4989 	  break;
4990 	}
4991     }
4992   return 0;
4993 }
4994 
4995 /* --------------------------------------------------------------------------- */
4996 
4997 static const char clearsquare_syntax[] =
4998   N_("ClearSquare(ToggleObject|SelectedElements|SelectedPins)");
4999 
5000 static const char clearsquare_help[] =
5001   N_("Clears the square-flag of pins and pads.");
5002 
5003 /* %start-doc actions ClearSquare
5004 
5005 Note that @code{Pins} means pins and pads.
5006 
5007 @pinshapes
5008 
5009 %end-doc */
5010 
5011 static int
ActionClearSquare(int argc,char ** argv,Coord x,Coord y)5012 ActionClearSquare (int argc, char **argv, Coord x, Coord y)
5013 {
5014   char *function = ARG (0);
5015   if (function && *function)
5016     {
5017       switch (GetFunctionID (function))
5018 	{
5019 	case F_ToggleObject:
5020 	case F_Object:
5021 	  {
5022 	    int type;
5023 	    void *ptr1, *ptr2, *ptr3;
5024 
5025 	    gui->get_coords (_("Select an Object"), &x, &y);
5026 	    if ((type =
5027 		 SearchScreen (x, y, CHANGESQUARE_TYPES,
5028 			       &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5029 	      if (ClrObjectSquare (type, ptr1, ptr2, ptr3))
5030 		SetChangedFlag (true);
5031 	    break;
5032 	  }
5033 
5034 	case F_SelectedElements:
5035 	  if (ClrSelectedSquare (ELEMENT_TYPE))
5036 	    SetChangedFlag (true);
5037 	  break;
5038 
5039 	case F_SelectedPins:
5040 	  if (ClrSelectedSquare (PIN_TYPE | PAD_TYPE))
5041 	    SetChangedFlag (true);
5042 	  break;
5043 
5044 	case F_Selected:
5045 	case F_SelectedObjects:
5046 	  if (ClrSelectedSquare (PIN_TYPE | PAD_TYPE))
5047 	    SetChangedFlag (true);
5048 	  break;
5049 	}
5050     }
5051   return 0;
5052 }
5053 
5054 /* --------------------------------------------------------------------------- */
5055 
5056 static const char changeoctagon_syntax[] =
5057   N_("ChangeOctagon(Object|ToggleObject|SelectedObjects|Selected)\n"
5058   "ChangeOctagon(SelectedElements|SelectedPins|SelectedVias)");
5059 
5060 static const char changeoctagon_help[] =
5061   N_("Changes the octagon-flag of pins and vias.");
5062 
5063 /* %start-doc actions ChangeOctagon
5064 
5065 @pinshapes
5066 
5067 %end-doc */
5068 
5069 static int
ActionChangeOctagon(int argc,char ** argv,Coord x,Coord y)5070 ActionChangeOctagon (int argc, char **argv, Coord x, Coord y)
5071 {
5072   char *function = ARG (0);
5073   if (function)
5074     {
5075       switch (GetFunctionID (function))
5076 	{
5077 	case F_ToggleObject:
5078 	case F_Object:
5079 	  {
5080 	    int type;
5081 	    void *ptr1, *ptr2, *ptr3;
5082 
5083 	    gui->get_coords (_("Select an Object"), &x, &y);
5084 	    if ((type =
5085 		 SearchScreen (x, y, CHANGEOCTAGON_TYPES,
5086 			       &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5087 	      if (ChangeObjectOctagon (type, ptr1, ptr2, ptr3))
5088 		SetChangedFlag (true);
5089 	    break;
5090 	  }
5091 
5092 	case F_SelectedElements:
5093 	  if (ChangeSelectedOctagon (ELEMENT_TYPE))
5094 	    SetChangedFlag (true);
5095 	  break;
5096 
5097 	case F_SelectedPins:
5098 	  if (ChangeSelectedOctagon (PIN_TYPE))
5099 	    SetChangedFlag (true);
5100 	  break;
5101 
5102 	case F_SelectedVias:
5103 	  if (ChangeSelectedOctagon (VIA_TYPE))
5104 	    SetChangedFlag (true);
5105 	  break;
5106 
5107 	case F_Selected:
5108 	case F_SelectedObjects:
5109 	  if (ChangeSelectedOctagon (PIN_TYPES))
5110 	    SetChangedFlag (true);
5111 	  break;
5112 	}
5113     }
5114   return 0;
5115 }
5116 
5117 /* --------------------------------------------------------------------------- */
5118 
5119 static const char setoctagon_syntax[] =
5120   N_("SetOctagon(Object|ToggleObject|SelectedElements|Selected)");
5121 
5122 static const char setoctagon_help[] = N_("Sets the octagon-flag of objects.");
5123 
5124 /* %start-doc actions SetOctagon
5125 
5126 @pinshapes
5127 
5128 %end-doc */
5129 
5130 static int
ActionSetOctagon(int argc,char ** argv,Coord x,Coord y)5131 ActionSetOctagon (int argc, char **argv, Coord x, Coord y)
5132 {
5133   char *function = ARG (0);
5134   if (function)
5135     {
5136       switch (GetFunctionID (function))
5137 	{
5138 	case F_ToggleObject:
5139 	case F_Object:
5140 	  {
5141 	    int type;
5142 	    void *ptr1, *ptr2, *ptr3;
5143 
5144 	    gui->get_coords (_("Select an Object"), &x, &y);
5145 	    if ((type =
5146 		 SearchScreen (x, y, CHANGEOCTAGON_TYPES,
5147 			       &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5148 	      if (SetObjectOctagon (type, ptr1, ptr2, ptr3))
5149 		SetChangedFlag (true);
5150 	    break;
5151 	  }
5152 
5153 	case F_SelectedElements:
5154 	  if (SetSelectedOctagon (ELEMENT_TYPE))
5155 	    SetChangedFlag (true);
5156 	  break;
5157 
5158 	case F_SelectedPins:
5159 	  if (SetSelectedOctagon (PIN_TYPE))
5160 	    SetChangedFlag (true);
5161 	  break;
5162 
5163 	case F_SelectedVias:
5164 	  if (SetSelectedOctagon (VIA_TYPE))
5165 	    SetChangedFlag (true);
5166 	  break;
5167 
5168 	case F_Selected:
5169 	case F_SelectedObjects:
5170 	  if (SetSelectedOctagon (PIN_TYPES))
5171 	    SetChangedFlag (true);
5172 	  break;
5173 	}
5174     }
5175   return 0;
5176 }
5177 
5178 /* --------------------------------------------------------------------------- */
5179 
5180 static const char clearoctagon_syntax[] =
5181   N_("ClearOctagon(ToggleObject|Object|SelectedObjects|Selected)\n"
5182   "ClearOctagon(SelectedElements|SelectedPins|SelectedVias)");
5183 
5184 static const char clearoctagon_help[] =
5185   N_("Clears the octagon-flag of pins and vias.");
5186 
5187 /* %start-doc actions ClearOctagon
5188 
5189 @pinshapes
5190 
5191 %end-doc */
5192 
5193 static int
ActionClearOctagon(int argc,char ** argv,Coord x,Coord y)5194 ActionClearOctagon (int argc, char **argv, Coord x, Coord y)
5195 {
5196   char *function = ARG (0);
5197   if (function)
5198     {
5199       switch (GetFunctionID (function))
5200 	{
5201 	case F_ToggleObject:
5202 	case F_Object:
5203 	  {
5204 	    int type;
5205 	    void *ptr1, *ptr2, *ptr3;
5206 
5207 	    gui->get_coords (_("Select an Object"), &x, &y);
5208 	    if ((type =
5209 		 SearchScreen (Crosshair.X, Crosshair.Y, CHANGEOCTAGON_TYPES,
5210 			       &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5211 	      if (ClrObjectOctagon (type, ptr1, ptr2, ptr3))
5212 		SetChangedFlag (true);
5213 	    break;
5214 	  }
5215 
5216 	case F_SelectedElements:
5217 	  if (ClrSelectedOctagon (ELEMENT_TYPE))
5218 	    SetChangedFlag (true);
5219 	  break;
5220 
5221 	case F_SelectedPins:
5222 	  if (ClrSelectedOctagon (PIN_TYPE))
5223 	    SetChangedFlag (true);
5224 	  break;
5225 
5226 	case F_SelectedVias:
5227 	  if (ClrSelectedOctagon (VIA_TYPE))
5228 	    SetChangedFlag (true);
5229 	  break;
5230 
5231 	case F_Selected:
5232 	case F_SelectedObjects:
5233 	  if (ClrSelectedOctagon (PIN_TYPES))
5234 	    SetChangedFlag (true);
5235 	  break;
5236 	}
5237     }
5238   return 0;
5239 }
5240 
5241 /* --------------------------------------------------------------------------- */
5242 
5243 static const char changehold_syntax[] =
5244   N_("ChangeHole(ToggleObject|Object|SelectedVias|Selected)");
5245 
5246 static const char changehold_help[] = N_("Changes the hole flag of objects.");
5247 
5248 /* %start-doc actions ChangeHole
5249 
5250 The "hole flag" of a via determines whether the via is a
5251 plated-through hole (not set), or an unplated hole (set).
5252 
5253 %end-doc */
5254 
5255 static int
ActionChangeHole(int argc,char ** argv,Coord x,Coord y)5256 ActionChangeHole (int argc, char **argv, Coord x, Coord y)
5257 {
5258   char *function = ARG (0);
5259   if (function)
5260     {
5261       switch (GetFunctionID (function))
5262 	{
5263 	case F_ToggleObject:
5264 	case F_Object:
5265 	  {
5266 	    int type;
5267 	    void *ptr1, *ptr2, *ptr3;
5268 
5269 	    gui->get_coords (_("Select an Object"), &x, &y);
5270 	    if ((type = SearchScreen (x, y, VIA_TYPE,
5271 				      &ptr1, &ptr2, &ptr3)) != NO_TYPE
5272 		&& ChangeHole ((PinType *) ptr3))
5273 	      IncrementUndoSerialNumber ();
5274 	    break;
5275 	  }
5276 
5277 	case F_SelectedVias:
5278 	case F_Selected:
5279 	  if (ChangeSelectedHole ())
5280 	    SetChangedFlag (true);
5281 	  break;
5282 	}
5283     }
5284   return 0;
5285 }
5286 
5287 /* --------------------------------------------------------------------------- */
5288 
5289 static const char changepaste_syntax[] =
5290   N_("ChangePaste(ToggleObject|Object|SelectedPads|Selected)");
5291 
5292 static const char changepaste_help[] =
5293   N_("Changes the no paste flag of objects.");
5294 
5295 /* %start-doc actions ChangePaste
5296 
5297 The "no paste flag" of a pad determines whether the solderpaste
5298  stencil will have an opening for the pad (no set) or if there wil be
5299  no solderpaste on the pad (set).  This is used for things such as
5300  fiducial pads.
5301 
5302 %end-doc */
5303 
5304 static int
ActionChangePaste(int argc,char ** argv,Coord x,Coord y)5305 ActionChangePaste (int argc, char **argv, Coord x, Coord y)
5306 {
5307   char *function = ARG (0);
5308   if (function)
5309     {
5310       switch (GetFunctionID (function))
5311 	{
5312 	case F_ToggleObject:
5313 	case F_Object:
5314 	  {
5315 	    int type;
5316 	    void *ptr1, *ptr2, *ptr3;
5317 
5318 	    gui->get_coords (_("Select an Object"), &x, &y);
5319 	    if ((type = SearchScreen (x, y, PAD_TYPE,
5320 				      &ptr1, &ptr2, &ptr3)) != NO_TYPE
5321 		&& ChangePaste ((PadType *) ptr3))
5322 	      IncrementUndoSerialNumber ();
5323 	    break;
5324 	  }
5325 
5326 	case F_SelectedPads:
5327 	case F_Selected:
5328 	  if (ChangeSelectedPaste ())
5329 	    SetChangedFlag (true);
5330 	  break;
5331 	}
5332     }
5333   return 0;
5334 }
5335 
5336 /* --------------------------------------------------------------------------- */
5337 
5338 static const char select_syntax[] =
5339   N_("Select(Object|ToggleObject)\n"
5340   "Select(All|Block|Connection|BuriedVias)\n"
5341   "Select(ElementByName|ObjectByName|PadByName|PinByName)\n"
5342   "Select(ElementByName|ObjectByName|PadByName|PinByName, Name)\n"
5343   "Select(TextByName|ViaByName|NetByName)\n"
5344   "Select(TextByName|ViaByName|NetByName, Name)\n"
5345   "Select(Convert)");
5346 
5347 static const char select_help[] = N_("Toggles or sets the selection.");
5348 
5349 /* %start-doc actions Select
5350 
5351 @table @code
5352 
5353 @item ElementByName
5354 @item ObjectByName
5355 @item PadByName
5356 @item PinByName
5357 @item TextByName
5358 @item ViaByName
5359 @item NetByName
5360 
5361 These all rely on having a regular expression parser built into
5362 @code{pcb}.  If the name is not specified then the user is prompted
5363 for a pattern, and all objects that match the pattern and are of the
5364 type specified are selected.
5365 
5366 @item Object
5367 @item ToggleObject
5368 Selects the object under the cursor.
5369 
5370 @item Block
5371 Selects all objects in a rectangle indicated by the cursor.
5372 
5373 @item All
5374 Selects all objects on the board.
5375 
5376 @item Found
5377 Selects all connections with the ``found'' flag set.
5378 
5379 @item Connection
5380 Selects all connections with the ``connected'' flag set.
5381 
5382 @item Connection
5383 Selects all blind and buried vias.
5384 
5385 @item Convert
5386 Converts the selected objects to an element.  This uses the highest
5387 numbered paste buffer.
5388 
5389 @end table
5390 
5391 %end-doc */
5392 
5393 static int
ActionSelect(int argc,char ** argv,Coord x,Coord y)5394 ActionSelect (int argc, char **argv, Coord x, Coord y)
5395 {
5396   char *function = ARG (0);
5397   if (function)
5398     {
5399       switch (GetFunctionID (function))
5400 	{
5401 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5402 	  int type;
5403 	  /* select objects by their names */
5404 	case F_ElementByName:
5405 	  type = ELEMENT_TYPE;
5406 	  goto commonByName;
5407 	case F_ObjectByName:
5408 	  type = ALL_TYPES;
5409 	  goto commonByName;
5410 	case F_PadByName:
5411 	  type = PAD_TYPE;
5412 	  goto commonByName;
5413 	case F_PinByName:
5414 	  type = PIN_TYPE;
5415 	  goto commonByName;
5416 	case F_TextByName:
5417 	  type = TEXT_TYPE;
5418 	  goto commonByName;
5419 	case F_ViaByName:
5420 	  type = VIA_TYPE;
5421 	  goto commonByName;
5422 	case F_NetByName:
5423 	  type = NET_TYPE;
5424 	  goto commonByName;
5425 
5426 	commonByName:
5427 	  {
5428 	    char *pattern = ARG (1);
5429 
5430 	    if (pattern
5431 		|| (pattern =
5432 		    gui->prompt_for (_("Enter pattern:"), "")) != NULL)
5433 	      {
5434 		if (SelectObjectByName (type, pattern, true))
5435 		  SetChangedFlag (true);
5436 		if (ARG (1) == NULL)
5437 		  free (pattern);
5438 	      }
5439 	    break;
5440 	  }
5441 #endif /* defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP) */
5442 
5443 	  /* select a single object */
5444 	case F_ToggleObject:
5445 	case F_Object:
5446 	  if (SelectObject ())
5447 	    SetChangedFlag (true);
5448 	  break;
5449 
5450 	  /* all objects in block */
5451 	case F_Block:
5452 	  {
5453 	    BoxType box;
5454 
5455 	    box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
5456 			  Crosshair.AttachedBox.Point2.X);
5457 	    box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
5458 			  Crosshair.AttachedBox.Point2.Y);
5459 	    box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
5460 			  Crosshair.AttachedBox.Point2.X);
5461 	    box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
5462 			  Crosshair.AttachedBox.Point2.Y);
5463 	    notify_crosshair_change (false);
5464 	    NotifyBlock ();
5465 	    if (Crosshair.AttachedBox.State == STATE_THIRD &&
5466 		SelectBlock (&box, true))
5467 	      {
5468 		SetChangedFlag (true);
5469 		Crosshair.AttachedBox.State = STATE_FIRST;
5470 	      }
5471 	    notify_crosshair_change (true);
5472 	    break;
5473 	  }
5474 
5475 	  /* select all visible objects */
5476 	case F_All:
5477 	  {
5478 	    BoxType box;
5479 
5480 	    box.X1 = -MAX_COORD;
5481 	    box.Y1 = -MAX_COORD;
5482 	    box.X2 = MAX_COORD;
5483 	    box.Y2 = MAX_COORD;
5484 	    if (SelectBlock (&box, true))
5485 	      SetChangedFlag (true);
5486 	    break;
5487 	  }
5488 
5489 	  /* all logical connections */
5490 	case F_Found:
5491 	  if (SelectByFlag (FOUNDFLAG, true))
5492 	    {
5493               Draw ();
5494 	      IncrementUndoSerialNumber ();
5495 	      SetChangedFlag (true);
5496 	    }
5497 	  break;
5498 
5499 	  /* all physical connections */
5500 	case F_Connection:
5501 	  if (SelectByFlag (CONNECTEDFLAG, true))
5502 	    {
5503               Draw ();
5504 	      IncrementUndoSerialNumber ();
5505 	      SetChangedFlag (true);
5506 	    }
5507 	  break;
5508 
5509 	case F_BuriedVias:
5510 	  if (SelectBuriedVias (true))
5511 	    {
5512 	      Draw ();
5513 	      IncrementUndoSerialNumber ();
5514 	      SetChangedFlag (true);
5515 	    }
5516 	  break;
5517 
5518 	case F_Convert:
5519 	  {
5520 	    Coord x, y;
5521 	    Note.Buffer = Settings.BufferNumber;
5522 	    SetBufferNumber (MAX_BUFFER - 1);
5523 	    ClearBuffer (PASTEBUFFER);
5524 	    gui->get_coords (_("Select the Element's Mark Location"), &x, &y);
5525 	    x = GridFit (x, PCB->Grid, PCB->GridOffsetX);
5526 	    y = GridFit (y, PCB->Grid, PCB->GridOffsetY);
5527 	    AddSelectedToBuffer (PASTEBUFFER, x, y, true);
5528 	    SaveUndoSerialNumber ();
5529 	    RemoveSelected ();
5530 	    ConvertBufferToElement (PASTEBUFFER);
5531 	    RestoreUndoSerialNumber ();
5532 	    CopyPastebufferToLayout (x, y);
5533 	    SetBufferNumber (Note.Buffer);
5534 	  }
5535 	  break;
5536 
5537 	default:
5538 	  AFAIL (select);
5539 	  break;
5540 	}
5541     }
5542   return 0;
5543 }
5544 
5545 /* FLAG(have_regex,FlagHaveRegex,0) */
5546 int
FlagHaveRegex(int parm)5547 FlagHaveRegex (int parm)
5548 {
5549 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5550   return 1;
5551 #else
5552   return 0;
5553 #endif
5554 }
5555 
5556 /* --------------------------------------------------------------------------- */
5557 
5558 static const char unselect_syntax[] =
5559   N_("Unselect(All|Block|Connection)\n"
5560   "Unselect(ElementByName|ObjectByName|PadByName|PinByName)\n"
5561   "Unselect(ElementByName|ObjectByName|PadByName|PinByName, Name)\n"
5562   "Unselect(TextByName|ViaByName)\n"
5563   "Unselect(TextByName|ViaByName, Name)\n");
5564 
5565 static const char unselect_help[] =
5566   N_("Unselects the object at the pointer location or the specified objects.");
5567 
5568 /* %start-doc actions Unselect
5569 
5570 @table @code
5571 
5572 @item All
5573 Unselect all objects.
5574 
5575 @item Block
5576 Unselect all objects in a rectangle given by the cursor.
5577 
5578 @item Connection
5579 Unselect all connections with the ``found'' flag set.
5580 
5581 @item ElementByName
5582 @item ObjectByName
5583 @item PadByName
5584 @item PinByName
5585 @item TextByName
5586 @item ViaByName
5587 
5588 These all rely on having a regular expression parser built into
5589 @code{pcb}.  If the name is not specified then the user is prompted
5590 for a pattern, and all objects that match the pattern and are of the
5591 type specified are unselected.
5592 
5593 
5594 @end table
5595 
5596 %end-doc */
5597 
5598 static int
ActionUnselect(int argc,char ** argv,Coord x,Coord y)5599 ActionUnselect (int argc, char **argv, Coord x, Coord y)
5600 {
5601   char *function = ARG (0);
5602   if (function)
5603     {
5604       switch (GetFunctionID (function))
5605 	{
5606 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5607 	  int type;
5608 	  /* select objects by their names */
5609 	case F_ElementByName:
5610 	  type = ELEMENT_TYPE;
5611 	  goto commonByName;
5612 	case F_ObjectByName:
5613 	  type = ALL_TYPES;
5614 	  goto commonByName;
5615 	case F_PadByName:
5616 	  type = PAD_TYPE;
5617 	  goto commonByName;
5618 	case F_PinByName:
5619 	  type = PIN_TYPE;
5620 	  goto commonByName;
5621 	case F_TextByName:
5622 	  type = TEXT_TYPE;
5623 	  goto commonByName;
5624 	case F_ViaByName:
5625 	  type = VIA_TYPE;
5626 	  goto commonByName;
5627 	case F_NetByName:
5628 	  type = NET_TYPE;
5629 	  goto commonByName;
5630 
5631 	commonByName:
5632 	  {
5633 	    char *pattern = ARG (1);
5634 
5635 	    if (pattern
5636 		|| (pattern =
5637 		    gui->prompt_for (_("Enter pattern:"), "")) != NULL)
5638 	      {
5639 		if (SelectObjectByName (type, pattern, false))
5640 		  SetChangedFlag (true);
5641 		if (ARG (1) == NULL)
5642 		  free (pattern);
5643 	      }
5644 	    break;
5645 	  }
5646 #endif /* defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP) */
5647 
5648 	  /* all objects in block */
5649 	case F_Block:
5650 	  {
5651 	    BoxType box;
5652 
5653 	    box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
5654 			  Crosshair.AttachedBox.Point2.X);
5655 	    box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
5656 			  Crosshair.AttachedBox.Point2.Y);
5657 	    box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
5658 			  Crosshair.AttachedBox.Point2.X);
5659 	    box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
5660 			  Crosshair.AttachedBox.Point2.Y);
5661 	    notify_crosshair_change (false);
5662 	    NotifyBlock ();
5663 	    if (Crosshair.AttachedBox.State == STATE_THIRD &&
5664 		SelectBlock (&box, false))
5665 	      {
5666 		SetChangedFlag (true);
5667 		Crosshair.AttachedBox.State = STATE_FIRST;
5668 	      }
5669 	    notify_crosshair_change (true);
5670 	    break;
5671 	  }
5672 
5673 	  /* unselect all visible objects */
5674 	case F_All:
5675 	  {
5676 	    BoxType box;
5677 
5678 	    box.X1 = -MAX_COORD;
5679 	    box.Y1 = -MAX_COORD;
5680 	    box.X2 = MAX_COORD;
5681 	    box.Y2 = MAX_COORD;
5682 	    if (SelectBlock (&box, false))
5683 	      SetChangedFlag (true);
5684 	    break;
5685 	  }
5686 
5687 	  /* all logical connections */
5688 	case F_Found:
5689 	  if (SelectByFlag (FOUNDFLAG, false))
5690 	    {
5691               Draw ();
5692 	      IncrementUndoSerialNumber ();
5693 	      SetChangedFlag (true);
5694 	    }
5695 	  break;
5696 
5697 	  /* all physical connections */
5698 	case F_Connection:
5699 	  if (SelectByFlag (CONNECTEDFLAG, false))
5700 	    {
5701               Draw ();
5702 	      IncrementUndoSerialNumber ();
5703 	      SetChangedFlag (true);
5704 	    }
5705 	  break;
5706 
5707 	default:
5708 	  AFAIL (unselect);
5709 	  break;
5710 
5711 	}
5712     }
5713   return 0;
5714 }
5715 
5716 /* --------------------------------------------------------------------------- */
5717 
5718 static const char saveto_syntax[] =
5719   N_("SaveTo(Layout|LayoutAs,filename)\n"
5720   "SaveTo(AllConnections|AllUnusedPins|ElementConnections,filename)\n"
5721   "SaveTo(PasteBuffer,filename)");
5722 
5723 static const char saveto_help[] = N_("Saves data to a file.");
5724 
5725 /* %start-doc actions SaveTo
5726 
5727 @table @code
5728 
5729 @item Layout
5730 Saves the current layout.
5731 
5732 @item LayoutAs
5733 Saves the current layout, and remembers the filename used.
5734 
5735 @item AllConnections
5736 Save all connections to a file.
5737 
5738 @item AllUnusedPins
5739 List all unused pins to a file.
5740 
5741 @item ElementConnections
5742 Save connections to the element at the cursor to a file.
5743 
5744 @item PasteBuffer
5745 Save the content of the active Buffer to a file. This is the graphical way to create a footprint.
5746 
5747 @end table
5748 
5749 %end-doc */
5750 
5751 static int
ActionSaveTo(int argc,char ** argv,Coord x,Coord y)5752 ActionSaveTo (int argc, char **argv, Coord x, Coord y)
5753 {
5754   char *function;
5755   char *name;
5756 
5757   function = ARG (0);
5758 
5759   if ( ! function || strcasecmp (function, "Layout") == 0)
5760     {
5761       if (SavePCB (PCB->Filename) == 0)
5762         SetChangedFlag (false);
5763       return 0;
5764     }
5765 
5766   if (argc != 2)
5767     AFAIL (saveto);
5768 
5769   name = argv[1];
5770 
5771   if (strcasecmp (function, "LayoutAs") == 0)
5772     {
5773       if (SavePCB (name) == 0)
5774         {
5775           SetChangedFlag (false);
5776           free (PCB->Filename);
5777           PCB->Filename = strdup (name);
5778           if (gui->notify_filename_changed != NULL)
5779             gui->notify_filename_changed ();
5780         }
5781       return 0;
5782     }
5783 
5784   if (strcasecmp (function, "AllConnections") == 0)
5785     {
5786       FILE *fp;
5787       bool result;
5788       if ((fp = CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
5789 	{
5790 	  LookupConnectionsToAllElements (fp);
5791 	  fclose (fp);
5792 	  SetChangedFlag (true);
5793 	}
5794       return 0;
5795     }
5796 
5797   if (strcasecmp (function, "AllUnusedPins") == 0)
5798     {
5799       FILE *fp;
5800       bool result;
5801       if ((fp = CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
5802 	{
5803 	  LookupUnusedPins (fp);
5804 	  fclose (fp);
5805 	  SetChangedFlag (true);
5806 	}
5807       return 0;
5808     }
5809 
5810   if (strcasecmp (function, "ElementConnections") == 0)
5811     {
5812       ElementType *element;
5813       void *ptrtmp;
5814       FILE *fp;
5815       bool result;
5816 
5817       if ((SearchScreen (Crosshair.X, Crosshair.Y, ELEMENT_TYPE,
5818 			 &ptrtmp, &ptrtmp, &ptrtmp)) != NO_TYPE)
5819 	{
5820 	  element = (ElementType *) ptrtmp;
5821 	  if ((fp =
5822 	       CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
5823 	    {
5824 	      LookupElementConnections (element, fp);
5825 	      fclose (fp);
5826 	      SetChangedFlag (true);
5827 	    }
5828 	}
5829       return 0;
5830     }
5831 
5832   if (strcasecmp (function, "PasteBuffer") == 0)
5833     {
5834       return SaveBufferElements (name);
5835     }
5836 
5837   AFAIL (saveto);
5838 }
5839 
5840 /* --------------------------------------------------------------------------- */
5841 
5842 static const char savesettings_syntax[] =
5843   N_("SaveSettings()\n"
5844   "SaveSettings(local)");
5845 
5846 static const char savesettings_help[] = N_("Saves settings.");
5847 
5848 /* %start-doc actions SaveSettings
5849 
5850 If you pass no arguments, the settings are stored in
5851 @code{$HOME/.pcb/settings}.  If you pass the word @code{local} they're
5852 saved in @code{./pcb.settings}.
5853 
5854 %end-doc */
5855 
5856 static int
ActionSaveSettings(int argc,char ** argv,Coord x,Coord y)5857 ActionSaveSettings (int argc, char **argv, Coord x, Coord y)
5858 {
5859   int locally = argc > 0 ? (strncasecmp (argv[0], "local", 5) == 0) : 0;
5860   hid_save_settings (locally);
5861   return 0;
5862 }
5863 
5864 /* --------------------------------------------------------------------------- */
5865 
5866 static const char loadfrom_syntax[] =
5867   N_("LoadFrom(Layout|LayoutToBuffer|ElementToBuffer|Netlist|Revert,filename)");
5868 
5869 static const char loadfrom_help[] = N_("Load layout data from a file.");
5870 
5871 /* %start-doc actions LoadFrom
5872 
5873 This action assumes you know what the filename is.  The various GUIs
5874 should have a similar @code{Load} action where the filename is
5875 optional, and will provide their own file selection mechanism to let
5876 you choose the file name.
5877 
5878 @table @code
5879 
5880 @item Layout
5881 Loads an entire PCB layout, replacing the current one.
5882 
5883 @item LayoutToBuffer
5884 Loads an entire PCB layout to the paste buffer.
5885 
5886 @item ElementToBuffer
5887 Loads the given element file into the paste buffer.  Element files
5888 contain only a single @code{Element} definition, such as the
5889 ``newlib'' library uses.
5890 
5891 @item Netlist
5892 Loads a new netlist, replacing any current netlist.
5893 
5894 @item Revert
5895 Re-loads the current layout from its disk file, reverting any changes
5896 you may have made.
5897 
5898 @end table
5899 
5900 %end-doc */
5901 
5902 static int
ActionLoadFrom(int argc,char ** argv,Coord x,Coord y)5903 ActionLoadFrom (int argc, char **argv, Coord x, Coord y)
5904 {
5905   char *function;
5906   char *name;
5907 
5908   if (argc < 2)
5909     AFAIL (loadfrom);
5910 
5911   function = argv[0];
5912   name = argv[1];
5913 
5914   if (strcasecmp (function, "ElementToBuffer") == 0)
5915     {
5916       notify_crosshair_change (false);
5917       if (LoadElementToBuffer (PASTEBUFFER, name, true))
5918 	SetMode (PASTEBUFFER_MODE);
5919       notify_crosshair_change (true);
5920     }
5921 
5922   else if (strcasecmp (function, "LayoutToBuffer") == 0)
5923     {
5924       notify_crosshair_change (false);
5925       if (LoadLayoutToBuffer (PASTEBUFFER, name))
5926 	SetMode (PASTEBUFFER_MODE);
5927       notify_crosshair_change (true);
5928     }
5929 
5930   else if (strcasecmp (function, "Layout") == 0)
5931     {
5932       if (!PCB->Changed ||
5933 	  gui->confirm_dialog (_("OK to override layout data?"), 0))
5934 	LoadPCB (name);
5935     }
5936 
5937   else if (strcasecmp (function, "Netlist") == 0)
5938     {
5939       if (PCB->Netlistname)
5940 	free (PCB->Netlistname);
5941       PCB->Netlistname = StripWhiteSpaceAndDup (name);
5942       FreeLibraryMemory (&PCB->NetlistLib);
5943       ImportNetlist (PCB->Netlistname);
5944       NetlistChanged (1);
5945     }
5946   else if (strcasecmp (function, "Revert") == 0 && PCB->Filename
5947 	   && (!PCB->Changed
5948 	       || gui->confirm_dialog (_("OK to override changes?"), 0)))
5949     {
5950       RevertPCB ();
5951     }
5952 
5953   return 0;
5954 }
5955 
5956 /* --------------------------------------------------------------------------- */
5957 
5958 static const char new_syntax[] = N_("New([name])");
5959 
5960 static const char new_help[] = N_("Starts a new layout.");
5961 
5962 /* %start-doc actions New
5963 
5964 If a name is not given, one is prompted for.
5965 
5966 %end-doc */
5967 
5968 static int
ActionNew(int argc,char ** argv,Coord x,Coord y)5969 ActionNew (int argc, char **argv, Coord x, Coord y)
5970 {
5971   char *name = ARG (0);
5972 
5973   if (!PCB->Changed || gui->confirm_dialog (_("OK to clear layout data?"), 0))
5974     {
5975       if (name)
5976 	name = strdup (name);
5977       else
5978 	name = gui->prompt_for (_("Enter the layout name:"), "");
5979 
5980       if (!name)
5981         return 1;
5982 
5983       notify_crosshair_change (false);
5984       /* do emergency saving
5985        * clear the old struct and allocate memory for the new one
5986        */
5987       if (PCB->Changed && Settings.SaveInTMP)
5988 	SaveInTMP ();
5989       RemovePCB (PCB);
5990       PCB = NULL;
5991       PCB = CreateNewPCB ();
5992       CreateNewPCBPost (PCB, 1);
5993 
5994       /* setup the new name and reset some values to default */
5995       free (PCB->Name);
5996       PCB->Name = name;
5997 
5998       ResetStackAndVisibility ();
5999       CenterDisplay (PCB->MaxWidth / 2, PCB->MaxHeight / 2, false);
6000       Redraw ();
6001 
6002       hid_action ("PCBChanged");
6003       notify_crosshair_change (true);
6004       return 0;
6005     }
6006   return 1;
6007 }
6008 
6009 /*!
6010  * \brief No operation, just for testing purposes.
6011  * syntax: Bell(volume)
6012  */
6013 void
ActionBell(char * volume)6014 ActionBell (char *volume)
6015 {
6016   gui->beep ();
6017 }
6018 
6019 /* --------------------------------------------------------------------------- */
6020 
6021 static const char pastebuffer_syntax[] =
6022   N_("PasteBuffer(AddSelected|Clear|1..MAX_BUFFER)\n"
6023   "PasteBuffer(Rotate, 1..3)\n"
6024   "PasteBuffer(Convert|Save|Restore|Mirror)\n"
6025   "PasteBuffer(ToLayout, X, Y, units)");
6026 
6027 static const char pastebuffer_help[] =
6028   N_("Various operations on the paste buffer.");
6029 
6030 /* %start-doc actions PasteBuffer
6031 
6032 There are a number of paste buffers; the actual limit is a
6033 compile-time constant @code{MAX_BUFFER} in @file{globalconst.h}.  It
6034 is currently @code{5}.  One of these is the ``current'' paste buffer,
6035 often referred to as ``the'' paste buffer.
6036 
6037 @table @code
6038 
6039 @item AddSelected
6040 Copies the selected objects to the current paste buffer.
6041 
6042 @item Clear
6043 Remove all objects from the current paste buffer.
6044 
6045 @item Convert
6046 Convert the current paste buffer to an element.  Vias are converted to
6047 pins, lines are converted to pads.
6048 
6049 @item Restore
6050 Convert any elements in the paste buffer back to vias and lines.
6051 
6052 @item Mirror
6053 Flip all objects in the paste buffer vertically (up/down flip).  To mirror
6054 horizontally, combine this with rotations.
6055 
6056 @item Rotate
6057 Rotates the current buffer.  The number to pass is 1..3, where 1 means
6058 90 degrees counter clockwise, 2 means 180 degrees, and 3 means 90
6059 degrees clockwise (270 CCW).
6060 
6061 @item Save
6062 Saves any elements in the current buffer to the indicated file.
6063 
6064 @item ToLayout
6065 Pastes any elements in the current buffer to the indicated X, Y
6066 coordinates in the layout.  The @code{X} and @code{Y} are treated like
6067 @code{delta} is for many other objects.  For each, if it's prefixed by
6068 @code{+} or @code{-}, then that amount is relative to the last
6069 location.  Otherwise, it's absolute.  Units can be
6070 @code{mil} or @code{mm}; if unspecified, units are PCB's internal
6071 units, currently 1/100 mil.
6072 
6073 
6074 @item 1..MAX_BUFFER
6075 Selects the given buffer to be the current paste buffer.
6076 
6077 @end table
6078 
6079 %end-doc */
6080 
6081 static int
ActionPasteBuffer(int argc,char ** argv,Coord x,Coord y)6082 ActionPasteBuffer (int argc, char **argv, Coord x, Coord y)
6083 {
6084   char *function = argc ? argv[0] : (char *)"";
6085   char *sbufnum = argc > 1 ? argv[1] : (char *)"";
6086   char *name;
6087   static char *default_file = NULL;
6088   int free_name = 0;
6089 
6090   notify_crosshair_change (false);
6091   if (function)
6092     {
6093       switch (GetFunctionID (function))
6094 	{
6095 	  /* clear contents of paste buffer */
6096 	case F_Clear:
6097 	  ClearBuffer (PASTEBUFFER);
6098 	  break;
6099 
6100 	  /* copies objects to paste buffer */
6101 	case F_AddSelected:
6102 	  AddSelectedToBuffer (PASTEBUFFER, 0, 0, false);
6103 	  break;
6104 
6105 	  /* converts buffer contents into an element */
6106 	case F_Convert:
6107 	  ConvertBufferToElement (PASTEBUFFER);
6108 	  break;
6109 
6110 	  /* break up element for editing */
6111 	case F_Restore:
6112 	  SmashBufferElement (PASTEBUFFER);
6113 	  break;
6114 
6115 	  /* Mirror buffer */
6116 	case F_Mirror:
6117 	  MirrorBuffer (PASTEBUFFER);
6118 	  break;
6119 
6120 	case F_Rotate:
6121 	  if (sbufnum)
6122 	    {
6123 	      RotateBuffer (PASTEBUFFER, (BYTE) atoi (sbufnum));
6124         crosshair_update_range();
6125 	    }
6126 	  break;
6127 
6128 	case F_Save:
6129 	  if (PASTEBUFFER->Data->ElementN == 0)
6130 	    {
6131 	      Message (_("Buffer has no elements!\n"));
6132 	      break;
6133 	    }
6134 	  free_name = 0;
6135 	  if (argc <= 1)
6136 	    {
6137 	      name = gui->fileselect (_("Save Paste Buffer As ..."),
6138 				      _("Choose a file to save the contents of the\n"
6139 					"paste buffer to.\n"),
6140 				      default_file, ".fp", "footprint",
6141 				      0);
6142 
6143 	      if (default_file)
6144 		{
6145 		  free (default_file);
6146 		  default_file = NULL;
6147 		}
6148 	      if ( name && *name)
6149 		{
6150 		  default_file = strdup (name);
6151 		}
6152 	      free_name = 1;
6153 	    }
6154 
6155 	  else
6156 	    name = argv[1];
6157 
6158 	  {
6159 	    FILE *exist;
6160 
6161 	    if ((exist = fopen (name, "r")))
6162 	      {
6163 		fclose (exist);
6164 		if (gui->
6165 		    confirm_dialog (_("File exists!  Ok to overwrite?"), 0))
6166 		  SaveBufferElements (name);
6167 	      }
6168 	    else
6169 	      SaveBufferElements (name);
6170 
6171 	    if (free_name && name)
6172 	      free (name);
6173 	  }
6174 	  break;
6175 
6176 	case F_ToLayout:
6177 	  {
6178 	    static Coord oldx = 0, oldy = 0;
6179 	    Coord x, y;
6180 	    bool absolute;
6181 
6182 	    if (argc == 1)
6183 	      {
6184 		x = y = 0;
6185 	      }
6186 	    else if (argc == 3 || argc == 4)
6187 	      {
6188 		x = GetValue (ARG (1), ARG (3), &absolute);
6189 		if (!absolute)
6190 		  x += oldx;
6191 		y = GetValue (ARG (2), ARG (3), &absolute);
6192 		if (!absolute)
6193 		  y += oldy;
6194 	      }
6195 	    else
6196 	      {
6197 		notify_crosshair_change (true);
6198 		AFAIL (pastebuffer);
6199 	      }
6200 
6201 	    oldx = x;
6202 	    oldy = y;
6203 	    if (CopyPastebufferToLayout (x, y))
6204 	      SetChangedFlag (true);
6205 	  }
6206 	  break;
6207 
6208 	  /* set number */
6209 	default:
6210 	  {
6211 	    int number = atoi (function);
6212 
6213 	    /* correct number */
6214 	    if (number)
6215 	      SetBufferNumber (number - 1);
6216 	  }
6217 	}
6218     }
6219 
6220   notify_crosshair_change (true);
6221   return 0;
6222 }
6223 
6224 /* --------------------------------------------------------------------------- */
6225 
6226 static const char undo_syntax[] = N_("Undo()\n"
6227                                   "Undo(ClearList)");
6228 
6229 static const char undo_help[] = N_("Undo recent changes.");
6230 
6231 /* %start-doc actions Undo
6232 
6233 The unlimited undo feature of @code{Pcb} allows you to recover from
6234 most operations that materially affect you work.  Calling
6235 @code{Undo()} without any parameter recovers from the last (non-undo)
6236 operation. @code{ClearList} is used to release the allocated
6237 memory. @code{ClearList} is called whenever a new layout is started or
6238 loaded. See also @code{Redo} and @code{Atomic}.
6239 
6240 Note that undo groups operations by serial number; changes with the
6241 same serial number will be undone (or redone) as a group.  See
6242 @code{Atomic}.
6243 
6244 %end-doc */
6245 
6246 static int
ActionUndo(int argc,char ** argv,Coord x,Coord y)6247 ActionUndo (int argc, char **argv, Coord x, Coord y)
6248 {
6249   char *function = ARG (0);
6250   if (!function || !*function)
6251     {
6252       /* don't allow undo in the middle of an operation */
6253       if (Settings.Mode != POLYGONHOLE_MODE &&
6254 	  Crosshair.AttachedObject.State != STATE_FIRST)
6255 	return 1;
6256       if (Crosshair.AttachedBox.State != STATE_FIRST
6257 	  && Settings.Mode != ARC_MODE)
6258 	return 1;
6259       /* undo the last operation */
6260 
6261       notify_crosshair_change (false);
6262       if ((Settings.Mode == POLYGON_MODE ||
6263            Settings.Mode == POLYGONHOLE_MODE) &&
6264           Crosshair.AttachedPolygon.PointN)
6265 	{
6266 	  GoToPreviousPoint ();
6267 	  notify_crosshair_change (true);
6268 	  return 0;
6269 	}
6270       /* move anchor point if undoing during line creation */
6271       if (Settings.Mode == LINE_MODE)
6272 	{
6273 	  if (Crosshair.AttachedLine.State == STATE_SECOND)
6274 	    {
6275 	      if (TEST_FLAG (AUTODRCFLAG, PCB))
6276 		Undo (true);	/* undo the connection find */
6277 	      Crosshair.AttachedLine.State = STATE_FIRST;
6278 	      SetLocalRef (0, 0, false);
6279 	      notify_crosshair_change (true);
6280 	      return 0;
6281 	    }
6282 	  if (Crosshair.AttachedLine.State == STATE_THIRD)
6283 	    {
6284 	      int type;
6285 	      void *ptr1, *ptr3, *ptrtmp;
6286 	      LineType *ptr2;
6287 	      /* this search is guaranteed to succeed */
6288 	      SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6289 				      &ptrtmp, &ptr3,
6290 				      Crosshair.AttachedLine.Point1.X,
6291 				      Crosshair.AttachedLine.Point1.Y, 0);
6292 	      ptr2 = (LineType *) ptrtmp;
6293 
6294 	      /* save both ends of line */
6295 	      Crosshair.AttachedLine.Point2.X = ptr2->Point1.X;
6296 	      Crosshair.AttachedLine.Point2.Y = ptr2->Point1.Y;
6297 	      if ((type = Undo (true)))
6298 		SetChangedFlag (true);
6299 	      /* check that the undo was of the right type */
6300 	      if ((type & UNDO_CREATE) == 0)
6301 		{
6302 		  /* wrong undo type, restore anchor points */
6303 		  Crosshair.AttachedLine.Point2.X =
6304 		    Crosshair.AttachedLine.Point1.X;
6305 		  Crosshair.AttachedLine.Point2.Y =
6306 		    Crosshair.AttachedLine.Point1.Y;
6307 		  notify_crosshair_change (true);
6308 		  return 0;
6309 		}
6310 	      /* move to new anchor */
6311 	      Crosshair.AttachedLine.Point1.X =
6312 		Crosshair.AttachedLine.Point2.X;
6313 	      Crosshair.AttachedLine.Point1.Y =
6314 		Crosshair.AttachedLine.Point2.Y;
6315 	      /* check if an intermediate point was removed */
6316 	      if (type & UNDO_REMOVE)
6317 		{
6318 		  /* this search should find the restored line */
6319 		  SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6320 					  &ptrtmp,
6321 					  &ptr3,
6322 					  Crosshair.AttachedLine.Point2.X,
6323 					  Crosshair.AttachedLine.Point2.Y, 0);
6324 		  ptr2 = (LineType *) ptrtmp;
6325 	          if (TEST_FLAG (AUTODRCFLAG, PCB))
6326 		    {
6327 		      /* undo loses CONNECTEDFLAG and FOUNDFLAG */
6328 		      SET_FLAG(CONNECTEDFLAG, ptr2);
6329 		      SET_FLAG(FOUNDFLAG, ptr2);
6330 		      DrawLine (CURRENT, ptr2);
6331 		    }
6332 		  Crosshair.AttachedLine.Point1.X =
6333 		    Crosshair.AttachedLine.Point2.X = ptr2->Point2.X;
6334 		  Crosshair.AttachedLine.Point1.Y =
6335 		    Crosshair.AttachedLine.Point2.Y = ptr2->Point2.Y;
6336 		}
6337 	      FitCrosshairIntoGrid (Crosshair.X, Crosshair.Y);
6338 	      AdjustAttachedObjects ();
6339 	      if (--addedLines == 0)
6340 		{
6341 		  Crosshair.AttachedLine.State = STATE_SECOND;
6342 		  lastLayer = CURRENT;
6343 		}
6344 	      else
6345 		{
6346 		  /* this search is guaranteed to succeed too */
6347 		  SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6348 					  &ptrtmp,
6349 					  &ptr3,
6350 					  Crosshair.AttachedLine.Point1.X,
6351 					  Crosshair.AttachedLine.Point1.Y, 0);
6352 		  ptr2 = (LineType *) ptrtmp;
6353 		  lastLayer = (LayerType *) ptr1;
6354 		}
6355 	      notify_crosshair_change (true);
6356 	      return 0;
6357 	    }
6358 	}
6359       if (Settings.Mode == ARC_MODE)
6360 	{
6361 	  if (Crosshair.AttachedBox.State == STATE_SECOND)
6362 	    {
6363 	      Crosshair.AttachedBox.State = STATE_FIRST;
6364 	      notify_crosshair_change (true);
6365 	      return 0;
6366 	    }
6367 	  if (Crosshair.AttachedBox.State == STATE_THIRD)
6368 	    {
6369 	      void *ptr1, *ptr2, *ptr3;
6370 	      BoxType *bx;
6371 	      /* guaranteed to succeed */
6372 	      SearchObjectByLocation (ARC_TYPE, &ptr1, &ptr2, &ptr3,
6373 				      Crosshair.AttachedBox.Point1.X,
6374 				      Crosshair.AttachedBox.Point1.Y, 0);
6375 	      bx = GetArcEnds ((ArcType *) ptr2);
6376 	      Crosshair.AttachedBox.Point1.X =
6377 		Crosshair.AttachedBox.Point2.X = bx->X1;
6378 	      Crosshair.AttachedBox.Point1.Y =
6379 		Crosshair.AttachedBox.Point2.Y = bx->Y1;
6380 	      AdjustAttachedObjects ();
6381 	      if (--addedLines == 0)
6382 		Crosshair.AttachedBox.State = STATE_SECOND;
6383 	    }
6384 	}
6385       /* undo the last destructive operation */
6386       if (Undo (true))
6387 	SetChangedFlag (true);
6388     }
6389   else if (function)
6390     {
6391       switch (GetFunctionID (function))
6392 	{
6393 	  /* clear 'undo objects' list */
6394 	case F_ClearList:
6395 	  ClearUndoList (false);
6396 	  break;
6397 	}
6398     }
6399   notify_crosshair_change (true);
6400   return 0;
6401 }
6402 
6403 /* --------------------------------------------------------------------------- */
6404 
6405 static const char redo_syntax[] = N_("Redo()");
6406 
6407 static const char redo_help[] = N_("Redo recent \"undo\" operations.");
6408 
6409 /* %start-doc actions Redo
6410 
6411 This routine allows you to recover from the last undo command.  You
6412 might want to do this if you thought that undo was going to revert
6413 something other than what it actually did (in case you are confused
6414 about which operations are un-doable), or if you have been backing up
6415 through a long undo list and over-shoot your stopping point.  Any
6416 change that is made since the undo in question will trim the redo
6417 list.  For example if you add ten lines, then undo three of them you
6418 could use redo to put them back, but if you move a line on the board
6419 before performing the redo, you will lose the ability to "redo" the
6420 three "undone" lines.
6421 
6422 %end-doc */
6423 
6424 static int
ActionRedo(int argc,char ** argv,Coord x,Coord y)6425 ActionRedo (int argc, char **argv, Coord x, Coord y)
6426 {
6427   if (((Settings.Mode == POLYGON_MODE ||
6428         Settings.Mode == POLYGONHOLE_MODE) &&
6429        Crosshair.AttachedPolygon.PointN) ||
6430       Crosshair.AttachedLine.State == STATE_SECOND)
6431     return 1;
6432   notify_crosshair_change (false);
6433   if (Redo (true))
6434     {
6435       SetChangedFlag (true);
6436       if (Settings.Mode == LINE_MODE &&
6437 	  Crosshair.AttachedLine.State != STATE_FIRST)
6438 	{
6439 	  LineType *line = g_list_last (CURRENT->Line)->data;
6440 	  Crosshair.AttachedLine.Point1.X =
6441 	    Crosshair.AttachedLine.Point2.X = line->Point2.X;
6442 	  Crosshair.AttachedLine.Point1.Y =
6443 	    Crosshair.AttachedLine.Point2.Y = line->Point2.Y;
6444 	  addedLines++;
6445 	}
6446     }
6447   notify_crosshair_change (true);
6448   return 0;
6449 }
6450 
6451 /* --------------------------------------------------------------------------- */
6452 
6453 static const char polygon_syntax[] = N_("Polygon(Close|PreviousPoint)");
6454 
6455 static const char polygon_help[] = N_("Some polygon related stuff.");
6456 
6457 /* %start-doc actions Polygon
6458 
6459 Polygons need a special action routine to make life easier.
6460 
6461 @table @code
6462 
6463 @item Close
6464 Creates the final segment of the polygon.  This may fail if clipping
6465 to 45 degree lines is switched on, in which case a warning is issued.
6466 
6467 @item PreviousPoint
6468 Resets the newly entered corner to the previous one. The Undo action
6469 will call Polygon(PreviousPoint) when appropriate to do so.
6470 
6471 @end table
6472 
6473 %end-doc */
6474 
6475 static int
ActionPolygon(int argc,char ** argv,Coord x,Coord y)6476 ActionPolygon (int argc, char **argv, Coord x, Coord y)
6477 {
6478   char *function = ARG (0);
6479   if (function && Settings.Mode == POLYGON_MODE)
6480     {
6481       notify_crosshair_change (false);
6482       switch (GetFunctionID (function))
6483 	{
6484 	  /* close open polygon if possible */
6485 	case F_Close:
6486 	  ClosePolygon ();
6487 	  break;
6488 
6489 	  /* go back to the previous point */
6490 	case F_PreviousPoint:
6491 	  GoToPreviousPoint ();
6492 	  break;
6493 	}
6494       notify_crosshair_change (true);
6495     }
6496   return 0;
6497 }
6498 
6499 /* --------------------------------------------------------------------------- */
6500 
6501 static const char routestyle_syntax[] = N_("RouteStyle(1|2|3|4)");
6502 
6503 static const char routestyle_help[] =
6504   N_("Copies the indicated routing style into the current sizes.");
6505 
6506 /* %start-doc actions RouteStyle
6507 
6508 %end-doc */
6509 
6510 static int
ActionRouteStyle(int argc,char ** argv,Coord x,Coord y)6511 ActionRouteStyle (int argc, char **argv, Coord x, Coord y)
6512 {
6513   char *str = ARG (0);
6514   RouteStyleType *rts;
6515   int number;
6516 
6517   if (str)
6518     {
6519       number = atoi (str);
6520       if (number > 0 && number <= NUM_STYLES)
6521 	{
6522 	  rts = &PCB->RouteStyle[number - 1];
6523 	  SetLineSize (rts->Thick);
6524 	  SetViaSize (rts->Diameter, true);
6525 	  SetViaDrillingHole (rts->Hole, true);
6526 	  SetKeepawayWidth (rts->Keepaway);
6527     SetViaMaskAperture(rts->ViaMask);
6528 	  hid_action("RouteStylesChanged");
6529 	}
6530     }
6531   return 0;
6532 }
6533 
6534 
6535 /* --------------------------------------------------------------------------- */
6536 
6537 static const char moveobject_syntax[] = N_("MoveObject(X,Y,dim)");
6538 
6539 static const char moveobject_help[] =
6540   N_("Moves the object under the crosshair.");
6541 
6542 /* %start-doc actions MoveObject
6543 
6544 The @code{X} and @code{Y} are treated like @code{delta} is for many
6545 other objects.  For each, if it's prefixed by @code{+} or @code{-},
6546 then that amount is relative.  Otherwise, it's absolute.  Units can be
6547 @code{mil} or @code{mm}; if unspecified, units are PCB's internal
6548 units, currently 1/100 mil.
6549 
6550 %end-doc */
6551 
6552 static int
ActionMoveObject(int argc,char ** argv,Coord x,Coord y)6553 ActionMoveObject (int argc, char **argv, Coord x, Coord y)
6554 {
6555   char *x_str = ARG (0);
6556   char *y_str = ARG (1);
6557   char *units = ARG (2);
6558   Coord nx, ny;
6559   bool absolute1, absolute2;
6560   void *ptr1, *ptr2, *ptr3;
6561   int type;
6562 
6563   ny = GetValue (y_str, units, &absolute1);
6564   nx = GetValue (x_str, units, &absolute2);
6565 
6566   type = SearchScreen (x, y, MOVE_TYPES, &ptr1, &ptr2, &ptr3);
6567   if (type == NO_TYPE)
6568     {
6569       Message (_("Nothing found under crosshair\n"));
6570       return 1;
6571     }
6572   if (absolute1)
6573     nx -= x;
6574   if (absolute2)
6575     ny -= y;
6576   Crosshair.AttachedObject.RubberbandN = 0;
6577   if (TEST_FLAG (RUBBERBANDFLAG, PCB))
6578     LookupRubberbandLines (type, ptr1, ptr2, ptr3);
6579   if (type == ELEMENT_TYPE)
6580     LookupRatLines (type, ptr1, ptr2, ptr3);
6581   MoveObjectAndRubberband (type, ptr1, ptr2, ptr3, nx, ny);
6582   SetChangedFlag (true);
6583   return 0;
6584 }
6585 
6586 /* --------------------------------------------------------------------------- */
6587 
6588 static const char movetocurrentlayer_syntax[] =
6589   N_("MoveToCurrentLayer(Object|SelectedObjects)");
6590 
6591 static const char movetocurrentlayer_help[] =
6592   N_("Moves objects to the current layer.");
6593 
6594 /* %start-doc actions MoveToCurrentLayer
6595 
6596 Note that moving an element from a component layer to a solder layer,
6597 or from solder to component, won't automatically flip it.  Use the
6598 @code{Flip()} action to do that.
6599 
6600 %end-doc */
6601 
6602 static int
ActionMoveToCurrentLayer(int argc,char ** argv,Coord x,Coord y)6603 ActionMoveToCurrentLayer (int argc, char **argv, Coord x, Coord y)
6604 {
6605   char *function = ARG (0);
6606   if (function)
6607     {
6608       switch (GetFunctionID (function))
6609 	{
6610 	case F_Object:
6611 	  {
6612 	    int type;
6613 	    void *ptr1, *ptr2, *ptr3;
6614 
6615 	    gui->get_coords (_("Select an Object"), &x, &y);
6616 	    if ((type =
6617 		 SearchScreen (x, y, MOVETOLAYER_TYPES,
6618 			       &ptr1, &ptr2, &ptr3)) != NO_TYPE)
6619 	      if (MoveObjectToLayer (type, ptr1, ptr2, ptr3, CURRENT, false))
6620 		SetChangedFlag (true);
6621 	    break;
6622 	  }
6623 
6624 	case F_SelectedObjects:
6625 	case F_Selected:
6626 	  if (MoveSelectedObjectsToLayer (CURRENT))
6627 	    SetChangedFlag (true);
6628 	  break;
6629 	}
6630     }
6631   return 0;
6632 }
6633 
6634 
6635 static const char setsame_syntax[] = N_("SetSame()");
6636 
6637 static const char setsame_help[] =
6638   N_("Sets current layer and sizes to match indicated item.");
6639 
6640 /* %start-doc actions SetSame
6641 
6642 When invoked over any line, arc, polygon, or via, this changes the
6643 current layer to be the layer that item is on, and changes the current
6644 sizes (thickness, keepaway, drill, etc) according to that item.
6645 
6646 %end-doc */
6647 
6648 static int
ActionSetSame(int argc,char ** argv,Coord x,Coord y)6649 ActionSetSame (int argc, char **argv, Coord x, Coord y)
6650 {
6651   void *ptr1, *ptr2, *ptr3;
6652   int type;
6653   LayerType *layer = CURRENT;
6654 
6655   type = SearchScreen (x, y, CLONE_TYPES, &ptr1, &ptr2, &ptr3);
6656 /* set layer current and size from line or arc */
6657   switch (type)
6658     {
6659     case LINE_TYPE:
6660       notify_crosshair_change (false);
6661       Settings.LineThickness = ((LineType *) ptr2)->Thickness;
6662       Settings.Keepaway = ((LineType *) ptr2)->Clearance / 2;
6663       layer = (LayerType *) ptr1;
6664       if (Settings.Mode != LINE_MODE)
6665 	SetMode (LINE_MODE);
6666       notify_crosshair_change (true);
6667       hid_action ("RouteStylesChanged");
6668       break;
6669 
6670     case ARC_TYPE:
6671       notify_crosshair_change (false);
6672       Settings.LineThickness = ((ArcType *) ptr2)->Thickness;
6673       Settings.Keepaway = ((ArcType *) ptr2)->Clearance / 2;
6674       layer = (LayerType *) ptr1;
6675       if (Settings.Mode != ARC_MODE)
6676 	SetMode (ARC_MODE);
6677       notify_crosshair_change (true);
6678       hid_action ("RouteStylesChanged");
6679       break;
6680 
6681     case POLYGON_TYPE:
6682       layer = (LayerType *) ptr1;
6683       break;
6684 
6685     case VIA_TYPE:
6686       notify_crosshair_change (false);
6687       Settings.ViaThickness = ((PinType *) ptr2)->Thickness;
6688       Settings.ViaDrillingHole = ((PinType *) ptr2)->DrillingHole;
6689       Settings.Keepaway = ((PinType *) ptr2)->Clearance / 2;
6690       if (Settings.Mode != VIA_MODE)
6691 	SetMode (VIA_MODE);
6692       notify_crosshair_change (true);
6693       hid_action ("RouteStylesChanged");
6694       break;
6695 
6696     default:
6697       return 1;
6698     }
6699   if (layer != CURRENT)
6700     {
6701       ChangeGroupVisibility (GetLayerNumber (PCB->Data, layer), true, true);
6702       Redraw ();
6703     }
6704   return 0;
6705 }
6706 
6707 
6708 /* --------------------------------------------------------------------------- */
6709 
6710 static const char setflag_syntax[] =
6711   N_("SetFlag(Object|Selected|SelectedObjects, flag)\n"
6712   "SetFlag(SelectedLines|SelectedPins|SelectedVias, flag)\n"
6713   "SetFlag(SelectedPads|SelectedTexts|SelectedNames, flag)\n"
6714   "SetFlag(SelectedElements, flag)\n"
6715   "flag = square | octagon | thermal | join");
6716 
6717 static const char setflag_help[] = N_("Sets flags on objects.");
6718 
6719 /* %start-doc actions SetFlag
6720 
6721 Turns the given flag on, regardless of its previous setting.  See
6722 @code{ChangeFlag}.
6723 
6724 @example
6725 SetFlag(SelectedPins,thermal)
6726 @end example
6727 
6728 %end-doc */
6729 
6730 static int
ActionSetFlag(int argc,char ** argv,Coord x,Coord y)6731 ActionSetFlag (int argc, char **argv, Coord x, Coord y)
6732 {
6733   char *function = ARG (0);
6734   char *flag = ARG (1);
6735   ChangeFlag (function, flag, 1, "SetFlag");
6736   return 0;
6737 }
6738 
6739 /* --------------------------------------------------------------------------- */
6740 
6741 static const char clrflag_syntax[] =
6742   N_("ClrFlag(Object|Selected|SelectedObjects, flag)\n"
6743   "ClrFlag(SelectedLines|SelectedPins|SelectedVias, flag)\n"
6744   "ClrFlag(SelectedPads|SelectedTexts|SelectedNames, flag)\n"
6745   "ClrFlag(SelectedElements, flag)\n"
6746   "flag = square | octagon | thermal | join");
6747 
6748 static const char clrflag_help[] = N_("Clears flags on objects.");
6749 
6750 /* %start-doc actions ClrFlag
6751 
6752 Turns the given flag off, regardless of its previous setting.  See
6753 @code{ChangeFlag}.
6754 
6755 @example
6756 ClrFlag(SelectedLines,join)
6757 @end example
6758 
6759 %end-doc */
6760 
6761 static int
ActionClrFlag(int argc,char ** argv,Coord x,Coord y)6762 ActionClrFlag (int argc, char **argv, Coord x, Coord y)
6763 {
6764   char *function = ARG (0);
6765   char *flag = ARG (1);
6766   ChangeFlag (function, flag, 0, "ClrFlag");
6767   return 0;
6768 }
6769 
6770 /* --------------------------------------------------------------------------- */
6771 
6772 static const char changeflag_syntax[] =
6773   N_("ChangeFlag(Object|Selected|SelectedObjects, flag, value)\n"
6774   "ChangeFlag(SelectedLines|SelectedPins|SelectedVias, flag, value)\n"
6775   "ChangeFlag(SelectedPads|SelectedTexts|SelectedNames, flag, value)\n"
6776   "ChangeFlag(SelectedElements, flag, value)\n"
6777   "flag = square | octagon | thermal | join\n"
6778   "value = 0 | 1");
6779 
6780 static const char changeflag_help[] = N_("Sets or clears flags on objects.");
6781 
6782 /* %start-doc actions ChangeFlag
6783 
6784 Toggles the given flag on the indicated object(s).  The flag may be
6785 one of the flags listed above (square, octagon, thermal, join).  The
6786 value may be the number 0 or 1.  If the value is 0, the flag is
6787 cleared.  If the value is 1, the flag is set.
6788 
6789 %end-doc */
6790 
6791 static int
ActionChangeFlag(int argc,char ** argv,Coord x,Coord y)6792 ActionChangeFlag (int argc, char **argv, Coord x, Coord y)
6793 {
6794   char *function = ARG (0);
6795   char *flag = ARG (1);
6796   int value = argc > 2 ? atoi (argv[2]) : -1;
6797   if (value != 0 && value != 1)
6798     AFAIL (changeflag);
6799 
6800   ChangeFlag (function, flag, value, "ChangeFlag");
6801   return 0;
6802 }
6803 
6804 
6805 static void
ChangeFlag(char * what,char * flag_name,int value,char * cmd_name)6806 ChangeFlag (char *what, char *flag_name, int value, char *cmd_name)
6807 {
6808   bool (*set_object) (int, void *, void *, void *);
6809   bool (*set_selected) (int);
6810 
6811   if (NSTRCMP (flag_name, "square") == 0)
6812     {
6813       set_object = value ? SetObjectSquare : ClrObjectSquare;
6814       set_selected = value ? SetSelectedSquare : ClrSelectedSquare;
6815     }
6816   else if (NSTRCMP (flag_name, "octagon") == 0)
6817     {
6818       set_object = value ? SetObjectOctagon : ClrObjectOctagon;
6819       set_selected = value ? SetSelectedOctagon : ClrSelectedOctagon;
6820     }
6821   else if (NSTRCMP (flag_name, "join") == 0)
6822     {
6823       /* Note: these are backwards, because the flag is "clear" but
6824          the command is "join".  */
6825       set_object = value ? ClrObjectJoin : SetObjectJoin;
6826       set_selected = value ? ClrSelectedJoin : SetSelectedJoin;
6827     }
6828   else
6829     {
6830       Message (_("%s():  Flag \"%s\" is not valid\n"), cmd_name, flag_name);
6831       return;
6832     }
6833 
6834   switch (GetFunctionID (what))
6835     {
6836     case F_Object:
6837       {
6838 	int type;
6839 	void *ptr1, *ptr2, *ptr3;
6840 
6841 	if ((type =
6842 	     SearchScreen (Crosshair.X, Crosshair.Y, CHANGESIZE_TYPES,
6843 			   &ptr1, &ptr2, &ptr3)) != NO_TYPE)
6844 	  if (TEST_FLAG (LOCKFLAG, (PinType *) ptr2))
6845 	    Message (_("Sorry, the object is locked\n"));
6846 	if (set_object (type, ptr1, ptr2, ptr3))
6847 	  SetChangedFlag (true);
6848 	break;
6849       }
6850 
6851     case F_SelectedVias:
6852       if (set_selected (VIA_TYPE))
6853 	SetChangedFlag (true);
6854       break;
6855 
6856     case F_SelectedPins:
6857       if (set_selected (PIN_TYPE))
6858 	SetChangedFlag (true);
6859       break;
6860 
6861     case F_SelectedPads:
6862       if (set_selected (PAD_TYPE))
6863 	SetChangedFlag (true);
6864       break;
6865 
6866     case F_SelectedLines:
6867       if (set_selected (LINE_TYPE))
6868 	SetChangedFlag (true);
6869       break;
6870 
6871     case F_SelectedTexts:
6872       if (set_selected (TEXT_TYPE))
6873 	SetChangedFlag (true);
6874       break;
6875 
6876     case F_SelectedNames:
6877       if (set_selected (ELEMENTNAME_TYPE))
6878 	SetChangedFlag (true);
6879       break;
6880 
6881     case F_SelectedElements:
6882       if (set_selected (ELEMENT_TYPE))
6883 	SetChangedFlag (true);
6884       break;
6885 
6886     case F_Selected:
6887     case F_SelectedObjects:
6888       if (set_selected (CHANGESIZE_TYPES))
6889 	SetChangedFlag (true);
6890       break;
6891     }
6892 }
6893 
6894 /* --------------------------------------------------------------------------- */
6895 
6896 static const char executefile_syntax[] = N_("ExecuteFile(filename)");
6897 
6898 static const char executefile_help[] = N_("Run actions from the given file.");
6899 
6900 /* %start-doc actions ExecuteFile
6901 
6902 Lines starting with @code{#} are ignored.
6903 
6904 %end-doc */
6905 
6906 static int
ActionExecuteFile(int argc,char ** argv,Coord x,Coord y)6907 ActionExecuteFile (int argc, char **argv, Coord x, Coord y)
6908 {
6909   FILE *fp;
6910   char *fname;
6911   char line[256];
6912   int n = 0;
6913   char *sp;
6914 
6915   if (argc != 1)
6916     AFAIL (executefile);
6917 
6918   fname = argv[0];
6919 
6920   if ((fp = fopen (fname, "r")) == NULL)
6921     {
6922       fprintf (stderr, _("Could not open actions file \"%s\".\n"), fname);
6923       return 1;
6924     }
6925 
6926   defer_updates = 1;
6927   defer_needs_update = 0;
6928   while (fgets (line, sizeof (line), fp) != NULL)
6929     {
6930       n++;
6931       sp = line;
6932 
6933       /* eat the trailing newline */
6934       while (*sp && *sp != '\r' && *sp != '\n')
6935 	sp++;
6936       *sp = '\0';
6937 
6938       /* eat leading spaces and tabs */
6939       sp = line;
6940       while (*sp && (*sp == ' ' || *sp == '\t'))
6941 	sp++;
6942 
6943       /*
6944        * if we have anything left and its not a comment line
6945        * then execute it
6946        */
6947 
6948       if (*sp && *sp != '#')
6949 	{
6950 	  /*Message ("%s : line %-3d : \"%s\"\n", fname, n, sp);*/
6951 	  hid_parse_actions (sp);
6952 	}
6953     }
6954 
6955   defer_updates = 0;
6956   if (defer_needs_update)
6957     {
6958       IncrementUndoSerialNumber ();
6959       gui->invalidate_all ();
6960     }
6961   fclose (fp);
6962   return 0;
6963 }
6964 
6965 /* --------------------------------------------------------------------------- */
6966 
6967 static int
ActionPSCalib(int argc,char ** argv,Coord x,Coord y)6968 ActionPSCalib (int argc, char **argv, Coord x, Coord y)
6969 {
6970   HID *ps = hid_find_exporter ("ps");
6971   ps->calibrate (0.0,0.0);
6972   return 0;
6973 }
6974 
6975 /* --------------------------------------------------------------------------- */
6976 
6977 static ElementType *element_cache = NULL;
6978 
6979 static ElementType *
find_element_by_refdes(char * refdes)6980 find_element_by_refdes (char *refdes)
6981 {
6982   if (element_cache
6983       && NAMEONPCB_NAME(element_cache)
6984       && strcmp (NAMEONPCB_NAME(element_cache), refdes) == 0)
6985     return element_cache;
6986 
6987   ELEMENT_LOOP (PCB->Data);
6988   {
6989     if (NAMEONPCB_NAME(element)
6990 	&& strcmp (NAMEONPCB_NAME(element), refdes) == 0)
6991       {
6992 	element_cache = element;
6993 	return element_cache;
6994       }
6995   }
6996   END_LOOP;
6997   return NULL;
6998 }
6999 
7000 static AttributeType *
lookup_attr(AttributeListType * list,const char * name)7001 lookup_attr (AttributeListType *list, const char *name)
7002 {
7003   int i;
7004   for (i=0; i<list->Number; i++)
7005     if (strcmp (list->List[i].name, name) == 0)
7006       return & list->List[i];
7007   return NULL;
7008 }
7009 
7010 static void
delete_attr(AttributeListType * list,AttributeType * attr)7011 delete_attr (AttributeListType *list, AttributeType *attr)
7012 {
7013   int idx = attr - list->List;
7014   if (idx < 0 || idx >= list->Number)
7015     return;
7016   if (list->Number - idx > 1)
7017     memmove (attr, attr+1, (list->Number - idx - 1) * sizeof(AttributeType));
7018   list->Number --;
7019 }
7020 
7021 /* ---------------------------------------------------------------- */
7022 static const char elementlist_syntax[] =
7023   N_("ElementList(Start|Done|Need,<refdes>,<footprint>,<value>)");
7024 
7025 static const char elementlist_help[] =
7026   N_("Adds the given element if it doesn't already exist.");
7027 
7028 /* %start-doc actions elementlist
7029 
7030 @table @code
7031 
7032 @item Start
7033 Indicates the start of an element list; call this before any Need
7034 actions.
7035 
7036 @item Need
7037 Searches the board for an element with a matching refdes.
7038 
7039 If found, the value and footprint are updated.
7040 
7041 If not found, a new element is created with the given footprint and value.
7042 
7043 @item Done
7044 Compares the list of elements needed since the most recent
7045 @code{start} with the list of elements actually on the board.  Any
7046 elements that weren't listed are selected, so that the user may delete
7047 them.
7048 
7049 @end table
7050 
7051 %end-doc */
7052 
7053 static int number_of_footprints_not_found;
7054 
7055 static int
parse_layout_attribute_units(char * name,int def)7056 parse_layout_attribute_units (char *name, int def)
7057 {
7058   const char *as = AttributeGet (PCB, name);
7059   if (!as)
7060     return def;
7061   return GetValue (as, NULL, NULL);
7062 }
7063 
7064 static int
ActionElementList(int argc,char ** argv,Coord x,Coord y)7065 ActionElementList (int argc, char **argv, Coord x, Coord y)
7066 {
7067   ElementType *e = NULL;
7068   char *refdes, *value, *footprint, *old;
7069   char *args[3];
7070   char *function;
7071 
7072   if (argc < 1)
7073     AFAIL (elementlist);
7074 
7075   function = argv[0];
7076 
7077 #ifdef DEBUG
7078   printf("Entered ActionElementList, executing function %s\n", function);
7079 #endif
7080 
7081   if (strcasecmp (function, "start") == 0)
7082     {
7083       ELEMENT_LOOP (PCB->Data);
7084       {
7085 	CLEAR_FLAG (FOUNDFLAG, element);
7086       }
7087       END_LOOP;
7088       element_cache = NULL;
7089       number_of_footprints_not_found = 0;
7090       return 0;
7091     }
7092 
7093   if (strcasecmp (function, "done") == 0)
7094     {
7095       ELEMENT_LOOP (PCB->Data);
7096       {
7097 	if (TEST_FLAG (FOUNDFLAG, element))
7098 	  {
7099 	    CLEAR_FLAG (FOUNDFLAG, element);
7100 	  }
7101 	else if (! EMPTY_STRING_P (NAMEONPCB_NAME (element)))
7102 	  {
7103 	    /* Unnamed elements should remain untouched */
7104 	    SET_FLAG (SELECTEDFLAG, element);
7105 	  }
7106       }
7107       END_LOOP;
7108       if (number_of_footprints_not_found > 0)
7109 	gui->confirm_dialog (_("Not all requested footprints were found.\n"
7110 			     "See the message log for details"),
7111 			     "Ok", NULL);
7112       return 0;
7113     }
7114 
7115   if (strcasecmp (function, "need") != 0)
7116     AFAIL (elementlist);
7117 
7118   if (argc != 4)
7119     AFAIL (elementlist);
7120 
7121   argc --;
7122   argv ++;
7123 
7124   refdes = ARG(0);
7125   footprint = ARG(1);
7126   value = ARG(2);
7127 
7128   args[0] = footprint;
7129   args[1] = refdes;
7130   args[2] = value;
7131 
7132 #ifdef DEBUG
7133   printf("  ... footprint = %s\n", footprint);
7134   printf("  ... refdes = %s\n", refdes);
7135   printf("  ... value = %s\n", value);
7136 #endif
7137 
7138   e = find_element_by_refdes (refdes);
7139 
7140   if (!e)
7141     {
7142       Coord nx, ny, d;
7143 
7144 #ifdef DEBUG
7145       printf("  ... Footprint not on board, need to add it.\n");
7146 #endif
7147       /* Not on board, need to add it. */
7148       if (LoadFootprint(argc, args, x, y))
7149 	{
7150 	  number_of_footprints_not_found ++;
7151 	  return 1;
7152 	}
7153 
7154       nx = PCB->MaxWidth / 2;
7155       ny = PCB->MaxHeight / 2;
7156       d = MIN (PCB->MaxWidth, PCB->MaxHeight) / 10;
7157 
7158       nx = parse_layout_attribute_units ("import::newX", nx);
7159       ny = parse_layout_attribute_units ("import::newY", ny);
7160       d = parse_layout_attribute_units ("import::disperse", d);
7161 
7162       if (d > 0)
7163 	{
7164 	  nx += rand () % (d*2) - d;
7165 	  ny += rand () % (d*2) - d;
7166 	}
7167 
7168       if (nx < 0)
7169 	nx = 0;
7170       if (nx >= PCB->MaxWidth)
7171 	nx = PCB->MaxWidth - 1;
7172       if (ny < 0)
7173 	ny = 0;
7174       if (ny >= PCB->MaxHeight)
7175 	ny = PCB->MaxHeight - 1;
7176 
7177       /* Place components onto center of board. */
7178       if (CopyPastebufferToLayout (nx, ny))
7179 	SetChangedFlag (true);
7180     }
7181 
7182   else if (e && DESCRIPTION_NAME(e) && strcmp (DESCRIPTION_NAME(e), footprint) != 0)
7183     {
7184       int er, pr, i;
7185       Coord mx, my;
7186       ElementType *pe;
7187 
7188 #ifdef DEBUG
7189       printf("  ... Footprint on board, but different from footprint loaded.\n");
7190 #endif
7191       /* Different footprint, we need to swap them out.  */
7192       if (LoadFootprint(argc, args, x, y))
7193 	{
7194 	  number_of_footprints_not_found ++;
7195 	  return 1;
7196 	}
7197 
7198       er = ElementOrientation (e);
7199       pe = PASTEBUFFER->Data->Element->data;
7200       if (!FRONT (e))
7201 	MirrorElementCoordinates (PASTEBUFFER->Data, pe, pe->MarkY*2 - PCB->MaxHeight);
7202       pr = ElementOrientation (pe);
7203 
7204       mx = e->MarkX;
7205       my = e->MarkY;
7206 
7207       if (er != pr)
7208 	RotateElementLowLevel (PASTEBUFFER->Data, pe, pe->MarkX, pe->MarkY, (er-pr+4)%4);
7209 
7210       for (i=0; i<MAX_ELEMENTNAMES; i++)
7211 	{
7212 	  pe->Name[i].X = e->Name[i].X - mx + pe->MarkX ;
7213 	  pe->Name[i].Y = e->Name[i].Y - my + pe->MarkY ;
7214 	  pe->Name[i].Direction = e->Name[i].Direction;
7215 	  pe->Name[i].Scale = e->Name[i].Scale;
7216 	}
7217 
7218       RemoveElement (e);
7219 
7220       if (CopyPastebufferToLayout (mx, my))
7221 	SetChangedFlag (true);
7222     }
7223 
7224   /* Now reload footprint */
7225   element_cache = NULL;
7226   e = find_element_by_refdes (refdes);
7227 
7228   old = ChangeElementText (PCB, PCB->Data, e, NAMEONPCB_INDEX, strdup (refdes));
7229   if (old)
7230     free(old);
7231   old = ChangeElementText (PCB, PCB->Data, e, VALUE_INDEX, strdup (value));
7232   if (old)
7233     free(old);
7234 
7235   SET_FLAG (FOUNDFLAG, e);
7236 
7237 #ifdef DEBUG
7238   printf(" ... Leaving ActionElementList.\n");
7239 #endif
7240 
7241   return 0;
7242 }
7243 
7244 /* ---------------------------------------------------------------- */
7245 static const char elementsetattr_syntax[] =
7246   N_("ElementSetAttr(refdes,name[,value])");
7247 
7248 static const char elementsetattr_help[] =
7249   N_("Sets or clears an element-specific attribute.");
7250 
7251 /* %start-doc actions elementsetattr
7252 
7253 If a value is specified, the named attribute is added (if not already
7254 present) or changed (if it is) to the given value.  If the value is
7255 not specified, the given attribute is removed if present.
7256 
7257 %end-doc */
7258 
7259 static int
ActionElementSetAttr(int argc,char ** argv,Coord x,Coord y)7260 ActionElementSetAttr (int argc, char **argv, Coord x, Coord y)
7261 {
7262   ElementType *e = NULL;
7263   char *refdes, *name, *value;
7264   AttributeType *attr;
7265 
7266   if (argc < 2)
7267     {
7268       AFAIL (elementsetattr);
7269     }
7270 
7271   refdes = argv[0];
7272   name = argv[1];
7273   value = ARG(2);
7274 
7275   ELEMENT_LOOP (PCB->Data);
7276   {
7277     if (NSTRCMP (refdes, NAMEONPCB_NAME (element)) == 0)
7278       {
7279 	e = element;
7280 	break;
7281       }
7282   }
7283   END_LOOP;
7284 
7285   if (!e)
7286     {
7287       Message(_("Cannot change attribute of %s - element not found\n"), refdes);
7288       return 1;
7289     }
7290 
7291   attr = lookup_attr (&e->Attributes, name);
7292 
7293   if (attr && value)
7294     {
7295       free (attr->value);
7296       attr->value = strdup (value);
7297     }
7298   if (attr && ! value)
7299     {
7300       delete_attr (& e->Attributes, attr);
7301     }
7302   if (!attr && value)
7303     {
7304       CreateNewAttribute (& e->Attributes, name, value);
7305     }
7306 
7307   return 0;
7308 }
7309 
7310 /* ---------------------------------------------------------------- */
7311 static const char execcommand_syntax[] = N_("ExecCommand(command)");
7312 
7313 static const char execcommand_help[] = N_("Runs a command.");
7314 
7315 /* %start-doc actions execcommand
7316 
7317 Runs the given command, which is a system executable.
7318 
7319 %end-doc */
7320 
7321 static int
ActionExecCommand(int argc,char ** argv,Coord x,Coord y)7322 ActionExecCommand (int argc, char **argv, Coord x, Coord y)
7323 {
7324   char *command;
7325 
7326   if (argc < 1)
7327     {
7328       AFAIL (execcommand);
7329     }
7330 
7331   command = ARG(0);
7332 
7333   if (system (command))
7334     return 1;
7335   return 0;
7336 }
7337 
7338 /* ---------------------------------------------------------------- */
7339 
7340 static int
pcb_spawnvp(char ** argv)7341 pcb_spawnvp (char **argv)
7342 {
7343 #ifdef HAVE__SPAWNVP
7344   int result = _spawnvp (_P_WAIT, argv[0], (const char * const *) argv);
7345   if (result == -1)
7346     return 1;
7347   else
7348     return 0;
7349 #else
7350   int pid;
7351   pid = fork ();
7352   if (pid < 0)
7353     {
7354       /* error */
7355       Message(_("Cannot fork!"));
7356       return 1;
7357     }
7358   else if (pid == 0)
7359     {
7360       /* Child */
7361       execvp (argv[0], argv);
7362       exit(1);
7363     }
7364   else
7365     {
7366       int rv;
7367       /* Parent */
7368       wait (&rv);
7369     }
7370   return 0;
7371 #endif
7372 }
7373 
7374 /* ---------------------------------------------------------------- */
7375 
7376 /*!
7377  * \brief Creates a new temporary file name.
7378  *
7379  * Hopefully the operating system provides a mkdtemp() function to
7380  * securily create a temporary directory with mode 0700.\n
7381  * If so then that directory is created and the returned string is made
7382  * up of the directory plus the name variable.\n
7383  * For example:\n
7384  *
7385  * tempfile_name_new ("myfile") might return
7386  * "/var/tmp/pcb.123456/myfile".
7387  *
7388  * If mkdtemp() is not available then 'name' is ignored and the
7389  * insecure tmpnam() function is used.
7390  *
7391  * Files/names created with tempfile_name_new() should be unlinked
7392  * with tempfile_unlink to make sure the temporary directory is also
7393  * removed when mkdtemp() is used.
7394  */
7395 static char *
tempfile_name_new(char * name)7396 tempfile_name_new (char * name)
7397 {
7398   char *tmpfile = NULL;
7399 #ifdef HAVE_MKDTEMP
7400   char *tmpdir, *mytmpdir;
7401   size_t len;
7402 #endif
7403 
7404   assert ( name != NULL );
7405 
7406 #ifdef HAVE_MKDTEMP
7407 #define TEMPLATE "pcb.XXXXXXXX"
7408 
7409 
7410   tmpdir = getenv ("TMPDIR");
7411 
7412   /* FIXME -- what about win32? */
7413   if (tmpdir == NULL) {
7414     tmpdir = "/tmp";
7415   }
7416 
7417   mytmpdir = (char *) malloc (sizeof(char) *
7418 			      (strlen (tmpdir) +
7419 			       1 +
7420 			       strlen (TEMPLATE) +
7421 			       1));
7422   if (mytmpdir == NULL) {
7423     fprintf (stderr, "%s(): malloc failed()\n", __FUNCTION__);
7424     exit (1);
7425   }
7426 
7427   *mytmpdir = '\0';
7428   (void)strcat (mytmpdir, tmpdir);
7429   (void)strcat (mytmpdir, PCB_DIR_SEPARATOR_S);
7430   (void)strcat (mytmpdir, TEMPLATE);
7431   if (mkdtemp (mytmpdir) == NULL) {
7432     fprintf (stderr, "%s():  mkdtemp (\"%s\") failed\n", __FUNCTION__, mytmpdir);
7433     free (mytmpdir);
7434     return NULL;
7435   }
7436 
7437 
7438   len = strlen (mytmpdir) + /* the temp directory name */
7439     1 +                     /* the directory sep. */
7440     strlen (name) +         /* the file name */
7441     1                       /* the \0 termination */
7442     ;
7443 
7444   tmpfile = (char *) malloc (sizeof (char) * len);
7445 
7446   *tmpfile = '\0';
7447   (void)strcat (tmpfile, mytmpdir);
7448   (void)strcat (tmpfile, PCB_DIR_SEPARATOR_S);
7449   (void)strcat (tmpfile, name);
7450 
7451   free (mytmpdir);
7452 #undef TEMPLATE
7453 #else
7454   /*
7455    * tmpnam() uses a static buffer so strdup() the result right away
7456    * in case someone decides to create multiple temp names.
7457    */
7458   tmpfile = strdup (tmpnam (NULL));
7459 #ifdef __WIN32__
7460     {
7461       /* Guile doesn't like \ separators */
7462       char *c;
7463       for (c = tmpfile; *c; c++)
7464 	if (*c == '\\')
7465 	  *c = '/';
7466     }
7467 #endif
7468 #endif
7469 
7470   return tmpfile;
7471 }
7472 
7473 /* ---------------------------------------------------------------- */
7474 
7475 /*!
7476  * \brief Unlink a temporary file.
7477  *
7478  * If we have mkdtemp() then our temp file lives in a temporary
7479  * directory and we need to remove that directory too.
7480  */
7481 static int
tempfile_unlink(char * name)7482 tempfile_unlink (char * name)
7483 {
7484 #ifdef DEBUG
7485     /* SDB says:  Want to keep old temp files for examiniation when debugging */
7486   return 0;
7487 #else /* DEBUG */
7488 
7489 #ifdef HAVE_MKDTEMP
7490   int e, rc2 = 0;
7491   char *dname;
7492 
7493   unlink (name);
7494   /* it is possible that the file was never created so it is OK if the
7495      unlink fails */
7496 
7497   /* now figure out the directory name to remove */
7498   e = strlen (name) - 1;
7499   while (e > 0 && name[e] != PCB_DIR_SEPARATOR_C) {e--;}
7500 
7501   dname = strdup (name);
7502   dname[e] = '\0';
7503 
7504   /*
7505    * at this point, e *should* point to the end of the directory part
7506    * but lets make sure.
7507    */
7508   if (e > 0) {
7509     rc2 = rmdir (dname);
7510     if (rc2 != 0) {
7511       perror (dname);
7512     }
7513 
7514   } else {
7515     fprintf (stderr, _("%s():  Unable to determine temp directory name from the temp file\n"),
7516 	     __FUNCTION__);
7517     fprintf (stderr, "%s():  \"%s\"\n",
7518 	     __FUNCTION__, name);
7519     rc2 = -1;
7520   }
7521 
7522   /* name was allocated with malloc */
7523   free (dname);
7524   free (name);
7525 
7526   /*
7527    * FIXME - should also return -1 if the temp file exists and was not
7528    * removed.
7529    */
7530   if (rc2 != 0) {
7531     return -1;
7532   }
7533 
7534 #else /* HAVE_MKDTEMP */
7535   int rc = unlink (name);
7536 
7537   if (rc != 0) {
7538     fprintf (stderr, _("Failed to unlink \"%s\"\n"), name);
7539     free (name);
7540     return rc;
7541   }
7542   free (name);
7543 
7544 #endif /* HAVE_MKDTEMP */
7545 #endif /* DEBUG */
7546 
7547   return 0;
7548 }
7549 
7550 /* ---------------------------------------------------------------- */
7551 static const char import_syntax[] =
7552   N_("Import()\n"
7553   "Import([gnetlist|make[,source,source,...]])\n"
7554   "Import(setnewpoint[,(mark|center|X,Y)])\n"
7555   "Import(setdisperse,D,units)\n");
7556 
7557 static const char import_help[] = N_("Import schematics.");
7558 
7559 /* %start-doc actions Import
7560 
7561 Imports element and netlist data from the schematics (or some other
7562 source).  The first parameter, which is optional, is the mode.  If not
7563 specified, the @code{import::mode} attribute in the PCB is used.
7564 @code{gnetlist} means gnetlist is used to obtain the information from
7565 the schematics.  @code{make} invokes @code{make}, assuming the user
7566 has a @code{Makefile} in the current directory.  The @code{Makefile}
7567 will be invoked with the following variables set:
7568 
7569 @table @code
7570 
7571 @item PCB
7572 The name of the .pcb file
7573 
7574 @item SRCLIST
7575 A space-separated list of source files
7576 
7577 @item OUT
7578 The name of the file in which to put the command script, which may
7579 contain any @pcb{} actions.  By default, this is a temporary file
7580 selected by @pcb{}, but if you specify an @code{import::outfile}
7581 attribute, that file name is used instead (and not automatically
7582 deleted afterwards).
7583 
7584 @end table
7585 
7586 The target specified to be built is the first of these that apply:
7587 
7588 @itemize @bullet
7589 
7590 @item
7591 The target specified by an @code{import::target} attribute.
7592 
7593 @item
7594 The output file specified by an @code{import::outfile} attribute.
7595 
7596 @item
7597 If nothing else is specified, the target is @code{pcb_import}.
7598 
7599 @end itemize
7600 
7601 If you specify an @code{import::makefile} attribute, then "-f <that
7602 file>" will be added to the command line.
7603 
7604 If you specify the mode, you may also specify the source files
7605 (schematics).  If you do not specify any, the list of schematics is
7606 obtained by reading the @code{import::src@var{N}} attributes (like
7607 @code{import::src0}, @code{import::src1}, etc).
7608 
7609 For compatibility with future extensions to the import file format,
7610 the generated file @emph{must not} start with the two characters
7611 @code{#%}.
7612 
7613 If a temporary file is needed the @code{TMPDIR} environment variable
7614 is used to select its location.
7615 
7616 Note that the programs @code{gnetlist} and @code{make} may be
7617 overridden by the user via the @code{make-program} and @code{gnetlist}
7618 @code{pcb} settings (i.e. in @code{~/.pcb/settings} or on the command
7619 line).
7620 
7621 If @pcb{} cannot determine which schematic(s) to import from, the GUI
7622 is called to let user choose (see @code{ImportGUI()}).
7623 
7624 Note that Import() doesn't delete anything - after an Import, elements
7625 which shouldn't be on the board are selected and may be removed once
7626 it's determined that the deletion is appropriate.
7627 
7628 If @code{Import()} is called with @code{setnewpoint}, then the location
7629 of new components can be specified.  This is where parts show up when
7630 they're added to the board.  The default is the center of the board.
7631 
7632 @table @code
7633 
7634 @item Import(setnewpoint)
7635 
7636 Prompts the user to click on the board somewhere, uses that point.  If
7637 called by a hotkey, uses the current location of the crosshair.
7638 
7639 @item Import(setnewpoint,mark)
7640 
7641 Uses the location of the mark.  If no mark is present, the point is
7642 not changed.
7643 
7644 @item Import(setnewpoint,center)
7645 
7646 Resets the point to the center of the board.
7647 
7648 @item Import(setnewpoint,X,Y,units)
7649 
7650 Sets the point to the specific coordinates given.  Example:
7651 @code{Import(setnewpoint,50,25,mm)}
7652 
7653 @end table
7654 
7655 Note that the X and Y locations are stored in attributes named
7656 @code{import::newX} and @code{import::newY} so you could change them
7657 manually if you wished.
7658 
7659 Calling @code{Import(setdisperse,D,units)} sets how much the newly
7660 placed elements are dispersed relative to the set point.  For example,
7661 @code{Import(setdisperse,10,mm)} will offset each part randomly up to
7662 10mm away from the point.  The default dispersion is 1/10th of the
7663 smallest board dimension.  Dispersion is saved in the
7664 @code{import::disperse} attribute.
7665 
7666 %end-doc */
7667 
7668 static int
ActionImport(int argc,char ** argv,Coord x,Coord y)7669 ActionImport (int argc, char **argv, Coord x, Coord y)
7670 {
7671   char *mode;
7672   char **sources = NULL;
7673   int nsources = 0;
7674 
7675 #ifdef DEBUG
7676       printf("ActionImport:  ===========  Entering ActionImport  ============\n");
7677 #endif
7678 
7679   mode = ARG (0);
7680 
7681   if (mode && strcasecmp (mode, "setdisperse") == 0)
7682     {
7683       char *ds, *units;
7684       char buf[50];
7685 
7686       ds = ARG (1);
7687       units = ARG (2);
7688       if (!ds)
7689 	{
7690 	  const char *as = AttributeGet (PCB, "import::disperse");
7691 	  ds = gui->prompt_for(_("Enter dispersion:"), as ? as : "0");
7692 	}
7693       if (units)
7694 	{
7695 	  sprintf(buf, "%s%s", ds, units);
7696 	  AttributePut (PCB, "import::disperse", buf);
7697 	}
7698       else
7699 	AttributePut (PCB, "import::disperse", ds);
7700       if (ARG (1) == NULL)
7701         free (ds);
7702       return 0;
7703     }
7704 
7705   if (mode && strcasecmp (mode, "setnewpoint") == 0)
7706     {
7707       const char *xs, *ys, *units;
7708       Coord x, y;
7709       char buf[50];
7710 
7711       xs = ARG (1);
7712       ys = ARG (2);
7713       units = ARG (3);
7714 
7715       if (!xs)
7716 	{
7717 	  gui->get_coords (_("Click on a location"), &x, &y);
7718 	}
7719       else if (strcasecmp (xs, "center") == 0)
7720 	{
7721 	  AttributeRemove (PCB, "import::newX");
7722 	  AttributeRemove (PCB, "import::newY");
7723 	  return 0;
7724 	}
7725       else if (strcasecmp (xs, "mark") == 0)
7726 	{
7727 	  if (!Marked.status)
7728 	    return 0;
7729 
7730 	  x = Marked.X;
7731 	  y = Marked.Y;
7732 	}
7733       else if (ys)
7734 	{
7735 	  x = GetValue (xs, units, NULL);
7736 	  y = GetValue (ys, units, NULL);
7737 	}
7738       else
7739 	{
7740 	  Message (_("Bad syntax for Import(setnewpoint)"));
7741 	  return 1;
7742 	}
7743 
7744       pcb_snprintf (buf, sizeof (buf), "%$ms", x);
7745       AttributePut (PCB, "import::newX", buf);
7746       pcb_snprintf (buf, sizeof (buf), "%$ms", y);
7747       AttributePut (PCB, "import::newY", buf);
7748       return 0;
7749     }
7750 
7751   if (! mode)
7752     mode = AttributeGet (PCB, "import::mode");
7753   if (! mode)
7754     mode = "gnetlist";
7755 
7756   if (argc > 1)
7757     {
7758       sources = argv + 1;
7759       nsources = argc - 1;
7760     }
7761 
7762   if (! sources)
7763     {
7764       char sname[40];
7765       char *src;
7766 
7767       nsources = -1;
7768       do {
7769 	nsources ++;
7770 	sprintf(sname, "import::src%d", nsources);
7771 	src = AttributeGet (PCB, sname);
7772       } while (src);
7773 
7774       if (nsources > 0)
7775 	{
7776 	  sources = (char **) malloc ((nsources + 1) * sizeof (char *));
7777 	  nsources = -1;
7778 	  do {
7779 	    nsources ++;
7780 	    sprintf(sname, "import::src%d", nsources);
7781 	    src = AttributeGet (PCB, sname);
7782 	    sources[nsources] = src;
7783 	  } while (src);
7784 	}
7785     }
7786 
7787   if (! sources)
7788     {
7789       /* Replace .pcb with .sch and hope for the best.  */
7790       char *pcbname = PCB->Filename;
7791       char *schname;
7792       char *dot, *slash, *bslash;
7793 
7794       if (!pcbname)
7795 	return hid_action("ImportGUI");
7796 
7797       schname = (char *) malloc (strlen(pcbname) + 5);
7798       strcpy (schname, pcbname);
7799       dot = strchr (schname, '.');
7800       slash = strchr (schname, '/');
7801       bslash = strchr (schname, '\\');
7802       if (dot && slash && dot < slash)
7803 	dot = NULL;
7804       if (dot && bslash && dot < bslash)
7805 	dot = NULL;
7806       if (dot)
7807 	*dot = 0;
7808       strcat (schname, ".sch");
7809 
7810       if (access (schname, F_OK))
7811         {
7812           free (schname);
7813           return hid_action("ImportGUI");
7814         }
7815 
7816       sources = (char **) malloc (2 * sizeof (char *));
7817       sources[0] = schname;
7818       sources[1] = NULL;
7819       nsources = 1;
7820     }
7821 
7822   if (strcasecmp (mode, "gnetlist") == 0)
7823     {
7824       char *tmpfile = tempfile_name_new ("gnetlist_output");
7825       char **cmd;
7826       int i;
7827 
7828       if (tmpfile == NULL) {
7829 	Message (_("Could not create temp file"));
7830 	return 1;
7831       }
7832 
7833       cmd = (char **) malloc ((7 + nsources) * sizeof (char *));
7834       cmd[0] =  Settings.GnetlistProgram;
7835       cmd[1] = "-g";
7836       cmd[2] = "pcbfwd";
7837       cmd[3] = "-o";
7838       cmd[4] = tmpfile;
7839       cmd[5] = "--";
7840       for (i=0; i<nsources; i++)
7841 	cmd[6+i] = sources[i];
7842       cmd[6+nsources] = NULL;
7843 
7844 #ifdef DEBUG
7845       printf("ActionImport:  ===========  About to run gnetlist  ============\n");
7846       printf("%s %s %s %s %s %s %s ...\n",
7847 	     cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6]);
7848 #endif
7849 
7850       if (pcb_spawnvp (cmd))
7851 	{
7852 	  unlink (tmpfile);
7853 	  return 1;
7854 	}
7855 
7856 #ifdef DEBUG
7857       printf("ActionImport:  ===========  About to run ActionExecuteFile, file = %s  ============\n", tmpfile);
7858 #endif
7859 
7860       cmd[0] = tmpfile;
7861       cmd[1] = NULL;
7862       ActionExecuteFile (1, cmd, 0, 0);
7863 
7864       free (cmd);
7865       tempfile_unlink (tmpfile);
7866     }
7867   else if (strcasecmp (mode, "make") == 0)
7868     {
7869       int must_free_tmpfile = 0;
7870       char *tmpfile;
7871       char *cmd[10];
7872       int i;
7873       char *srclist;
7874       int srclen;
7875       char *user_outfile = NULL;
7876       char *user_makefile = NULL;
7877       char *user_target = NULL;
7878 
7879 
7880       user_outfile = AttributeGet (PCB, "import::outfile");
7881       user_makefile = AttributeGet (PCB, "import::makefile");
7882       user_target = AttributeGet (PCB, "import::target");
7883       if (user_outfile && !user_target)
7884 	user_target = user_outfile;
7885 
7886       if (user_outfile)
7887 	tmpfile = user_outfile;
7888       else
7889 	{
7890 	  tmpfile = tempfile_name_new ("gnetlist_output");
7891 	  if (tmpfile == NULL) {
7892 	    Message (_("Could not create temp file"));
7893             free (sources);
7894 	    return 1;
7895 	  }
7896 	  must_free_tmpfile = 1;
7897 	}
7898 
7899       srclen = sizeof("SRCLIST=") + 2;
7900       for (i=0; i<nsources; i++)
7901 	srclen += strlen (sources[i]) + 2;
7902       srclist = (char *) malloc (srclen);
7903       strcpy (srclist, "SRCLIST=");
7904       for (i=0; i<nsources; i++)
7905 	{
7906 	  if (i)
7907 	    strcat (srclist, " ");
7908 	  strcat (srclist, sources[i]);
7909 	}
7910 
7911       cmd[0] = Settings.MakeProgram;
7912       cmd[1] = "-s";
7913       cmd[2] = Concat ("PCB=", PCB->Filename, NULL);
7914       cmd[3] = srclist;
7915       cmd[4] = Concat ("OUT=", tmpfile, NULL);
7916       i = 5;
7917       if (user_makefile)
7918 	{
7919 	  cmd[i++] = "-f";
7920 	  cmd[i++] = user_makefile;
7921 	}
7922       cmd[i++] = user_target ? user_target : (char *)"pcb_import";
7923       cmd[i++] = NULL;
7924 
7925       if (pcb_spawnvp (cmd))
7926 	{
7927 	  if (must_free_tmpfile)
7928 	    unlink (tmpfile);
7929 	  free (cmd[2]);
7930 	  free (cmd[3]);
7931 	  free (cmd[4]);
7932 	  return 1;
7933 	}
7934 
7935       cmd[0] = tmpfile;
7936       cmd[1] = NULL;
7937       ActionExecuteFile (1, cmd, 0, 0);
7938 
7939       free (cmd[2]);
7940       free (cmd[3]);
7941       free (cmd[4]);
7942       if (must_free_tmpfile)
7943 	tempfile_unlink (tmpfile);
7944     }
7945   else
7946     {
7947       Message (_("Unknown import mode: %s\n"), mode);
7948       return 1;
7949     }
7950 
7951   DeleteRats (false);
7952   AddAllRats (false, NULL);
7953 
7954 #ifdef DEBUG
7955       printf("ActionImport:  ===========  Leaving ActionImport  ============\n");
7956 #endif
7957 
7958   return 0;
7959 }
7960 
7961 /* ------------------------------------------------------------ */
7962 
7963 static const char attributes_syntax[] =
7964   N_("Attributes(Layout|Layer|Element)\n"
7965   "Attributes(Layer,layername)");
7966 
7967 static const char attributes_help[] =
7968   N_("Let the user edit the attributes of the layout, current or given\n"
7969   "layer, or selected element.");
7970 
7971 /* %start-doc actions Attributes
7972 
7973 This just pops up a dialog letting the user edit the attributes of the
7974 pcb, an element, or a layer.
7975 
7976 %end-doc */
7977 
7978 
7979 static int
ActionAttributes(int argc,char ** argv,Coord x,Coord y)7980 ActionAttributes (int argc, char **argv, Coord x, Coord y)
7981 {
7982   char *function = ARG (0);
7983   char *layername = ARG (1);
7984   char *buf;
7985 
7986   if (!function)
7987     AFAIL (attributes);
7988 
7989   if (!gui->edit_attributes)
7990     {
7991       Message (_("This GUI doesn't support Attribute Editing\n"));
7992       return 1;
7993     }
7994 
7995   switch (GetFunctionID (function))
7996     {
7997     case F_Layout:
7998       {
7999 	gui->edit_attributes(_("Layout Attributes"), &(PCB->Attributes));
8000 	return 0;
8001       }
8002 
8003     case F_Layer:
8004       {
8005 	LayerType *layer = CURRENT;
8006 	if (layername)
8007 	  {
8008 	    int i;
8009 	    layer = NULL;
8010 	    for (i=0; i<max_copper_layer; i++)
8011 	      if (strcmp (PCB->Data->Layer[i].Name, layername) == 0)
8012 		{
8013 		  layer = & (PCB->Data->Layer[i]);
8014 		  break;
8015 		}
8016 	    if (layer == NULL)
8017 	      {
8018 		Message (_("No layer named %s\n"), layername);
8019 		return 1;
8020 	      }
8021 	  }
8022 	buf = (char *) malloc (strlen (layer->Name) +
8023 	    strlen (_("Layer %s Attributes")));
8024 	sprintf (buf, _("Layer %s Attributes"), layer->Name);
8025 	gui->edit_attributes(buf, &(layer->Attributes));
8026 	free (buf);
8027 	return 0;
8028       }
8029 
8030     case F_Element:
8031       {
8032 	int n_found = 0;
8033 	ElementType *e = NULL;
8034 	ELEMENT_LOOP (PCB->Data);
8035 	{
8036 	  if (TEST_FLAG (SELECTEDFLAG, element))
8037 	    {
8038 	      e = element;
8039 	      n_found ++;
8040 	    }
8041 	}
8042 	END_LOOP;
8043 	if (n_found > 1)
8044 	  {
8045 	    Message (_("Too many elements selected\n"));
8046 	    return 1;
8047 	  }
8048 	if (n_found == 0)
8049 	  {
8050 	    void *ptrtmp;
8051 	    gui->get_coords (_("Click on an element"), &x, &y);
8052 	    if ((SearchScreen
8053 		 (x, y, ELEMENT_TYPE, &ptrtmp,
8054 		  &ptrtmp, &ptrtmp)) != NO_TYPE)
8055 	      e = (ElementType *) ptrtmp;
8056 	    else
8057 	      {
8058 		Message (_("No element found there\n"));
8059 		return 1;
8060 	      }
8061 	  }
8062 
8063 	if (NAMEONPCB_NAME(e))
8064 	  {
8065 	    buf = (char *) malloc (strlen (NAMEONPCB_NAME(e)) +
8066 		strlen (_("Element %s Attributes")));
8067 	    sprintf(buf, _("Element %s Attributes"), NAMEONPCB_NAME(e));
8068 	  }
8069 	else
8070 	  {
8071 	    buf = strdup (_("Unnamed Element Attributes"));
8072 	  }
8073 	gui->edit_attributes(buf, &(e->Attributes));
8074 	free (buf);
8075 	break;
8076       }
8077 
8078     default:
8079       AFAIL (attributes);
8080     }
8081 
8082   return 0;
8083 }
8084 
8085 /* --------------------------------------------------------------------------- */
8086 
8087 static const char setvialayers_syntax[] =
8088   N_("SetViaLayers(Object|SelectedVias|Selected[,ThroughHole|TH])\n"
8089      "SetViaLayers(Object|SelectedVias|Selected,from,to)\n"
8090      "SetViaLayers(Object|SelectedVias|Selected,[c|-|from],[c|-|to])"
8091      );
8092 
8093 static const char setvialayers_help[] =
8094   N_("Sets starting and ending layer for burried/blind/standard vias.");
8095 
8096 /* %start-doc actions setvialayers
8097 
8098 Specifies layers, which are connected by via.
8099 
8100 @table @code
8101 
8102 @item TH|ThroughHole
8103 The vias will be set as through-hole, connecting all layers
8104 
8105 @item from
8106 layer name or layer number of the first layer to be connected by via; "-" stands for unchanged, "c" stands for currently selected layer
8107 
8108 @item to
8109 layer name or layer number of the last layer to be connected by via; "-" stands for unchanged, "c" stands for currently selected layer
8110 
8111 @end table
8112 
8113 If no parameter us used, dialog is displayed (if implemented in the respective GUI HID).
8114 
8115 
8116 %end-doc */
8117 
8118 static bool
identify_layer(char * layer_name,Cardinal * layer_no)8119 identify_layer (char *layer_name, Cardinal *layer_no)
8120 {
8121   int layer;
8122 
8123   if (strcmp (layer_name, "-") == 0)
8124     {
8125       *layer_no = -1;
8126       return true;
8127     }
8128 
8129   if (strcmp (layer_name, "c") == 0)
8130     {
8131       if ((unsigned int)INDEXOFCURRENT < max_copper_layer)
8132         {
8133           *layer_no = INDEXOFCURRENT;
8134           return true;
8135         }
8136     }
8137 
8138   layer = SearchLayerByName (PCB->Data, layer_name);
8139   if (layer == -1)
8140     {
8141       if (sscanf (layer_name, "%d", &layer) != 1)
8142         layer = -1;
8143     }
8144 
8145   if (layer != -1)
8146     *layer_no = layer;
8147 
8148   return (layer != -1);
8149 }
8150 
8151 static int
ActionSetViaLayers(int argc,char ** argv,Coord x,Coord y)8152 ActionSetViaLayers (int argc, char **argv, Coord x, Coord y)
8153 {
8154   char *function = ARG (0);
8155   char *layername_from = ARG (1);
8156   char *layername_to = ARG (2);
8157   Cardinal layer_from ;
8158   Cardinal layer_to = -1;
8159 
8160   if (!function)
8161     AFAIL (setvialayers);
8162 
8163   if ( /* !gui->edit_attributes  &&*/ argc < 2)
8164     {
8165       Message (_("This GUI doesn't support Via Layers editing\n"));
8166       return 1;
8167     }
8168 
8169   if (GetFunctionID (layername_from) == F_ThroughHole)
8170     {
8171       layer_from = 0;
8172       layer_to = 0;
8173     }
8174   else
8175     {
8176       if (!identify_layer (layername_from, &layer_from)
8177           || !identify_layer (layername_to, &layer_to))
8178 	{
8179 	  Message (_("Sorry, wrong layers specified.\n"));
8180           return 1;
8181 	}
8182     }
8183 
8184   /* ensure that layer_from < layer_to */
8185   if (layer_from != -1
8186       && layer_to != -1
8187       && layer_from > layer_to)
8188     {
8189       int tmp;
8190 
8191       tmp = layer_from;
8192       layer_from = layer_to;
8193       layer_to = tmp;
8194     }
8195 
8196   if (layer_to != -1)
8197     layer_to = min (layer_to, max_copper_layer-1);
8198 
8199   switch (GetFunctionID (function))
8200     {
8201     case F_Object:
8202       {
8203 	int type;
8204 	void *ptr1, *ptr2, *ptr3;
8205 
8206 	if ((type =
8207 	     SearchScreen (Crosshair.X, Crosshair.Y, VIA_TYPE,
8208 			   &ptr1, &ptr2, &ptr3)) != NO_TYPE)
8209 	  {
8210 	    if (TEST_FLAG (LOCKFLAG, (PinType *) ptr1))
8211 	      Message (_("Sorry, the object is locked\n"));
8212 	    else
8213 	      {
8214 	        if (ChangeObjectViaLayers (ptr1, ptr2, ptr3, layer_from, layer_to))
8215 		  {
8216 		    SetChangedFlag (true);
8217 		  }
8218 	      }
8219 	  }
8220 	break;
8221     case F_SelectedVias:
8222     case F_Selected:
8223 	if (ChangeSelectedViaLayers (layer_from, layer_to))
8224 	  {
8225 	    SetChangedFlag (true);
8226 	  }
8227 	break;
8228       }
8229   }
8230 
8231   return 0;
8232 }
8233 /* --------------------------------------------------------------------------- */
8234 
8235 HID_Action action_action_list[] = {
8236   {"AddRats", 0, ActionAddRats,
8237    addrats_help, addrats_syntax}
8238   ,
8239   {"Attributes", 0, ActionAttributes,
8240    attributes_help, attributes_syntax}
8241   ,
8242   {"Atomic", 0, ActionAtomic,
8243    atomic_help, atomic_syntax}
8244   ,
8245   {"AutoPlaceSelected", 0, ActionAutoPlaceSelected,
8246    autoplace_help, autoplace_syntax}
8247   ,
8248   {"AutoRoute", 0, ActionAutoRoute,
8249    autoroute_help, autoroute_syntax}
8250   ,
8251   {"ChangeClearSize", 0, ActionChangeClearSize,
8252    changeclearsize_help, changeclearsize_syntax}
8253   ,
8254   {"ChangeDrillSize", 0, ActionChange2ndSize,
8255    changedrillsize_help, changedrillsize_syntax}
8256   ,
8257   {"ChangeHole", 0, ActionChangeHole,
8258    changehold_help, changehold_syntax}
8259   ,
8260   {"ChangeJoin", 0, ActionChangeJoin,
8261    changejoin_help, changejoin_syntax}
8262   ,
8263   {"ChangeName", 0, ActionChangeName,
8264    changename_help, changename_syntax}
8265   ,
8266   {"ChangePaste", 0, ActionChangePaste,
8267    changepaste_help, changepaste_syntax}
8268   ,
8269   {"ChangePinName", 0, ActionChangePinName,
8270    changepinname_help, changepinname_syntax}
8271   ,
8272   {"ChangeSize", 0, ActionChangeSize,
8273    changesize_help, changesize_syntax}
8274   ,
8275   {"ChangeSquare", 0, ActionChangeSquare,
8276    changesquare_help, changesquare_syntax}
8277   ,
8278   {"ChangeOctagon", 0, ActionChangeOctagon,
8279    changeoctagon_help, changeoctagon_syntax}
8280   ,
8281   {"ClearSquare", 0, ActionClearSquare,
8282    clearsquare_help, clearsquare_syntax}
8283   ,
8284   {"ClearOctagon", 0, ActionClearOctagon,
8285    clearoctagon_help, clearoctagon_syntax}
8286   ,
8287   {"Connection", 0, ActionConnection,
8288    connection_help, connection_syntax}
8289   ,
8290   {"Delete", 0, ActionDelete,
8291    delete_help, delete_syntax}
8292   ,
8293   {"DeleteRats", 0, ActionDeleteRats,
8294    deleterats_help, deleterats_syntax}
8295   ,
8296   {"DisperseElements", 0, ActionDisperseElements,
8297    disperseelements_help, disperseelements_syntax}
8298   ,
8299   {"Display", 0, ActionDisplay,
8300    display_help, display_syntax}
8301   ,
8302   {"DumpLibrary", 0, ActionDumpLibrary,
8303    dumplibrary_help, dumplibrary_syntax}
8304   ,
8305   {"ExecuteFile", 0, ActionExecuteFile,
8306    executefile_help, executefile_syntax}
8307   ,
8308   {"Flip", N_("Click on Object or Flip Point"), ActionFlip,
8309    flip_help, flip_syntax}
8310   ,
8311   {"LoadFrom", 0, ActionLoadFrom,
8312    loadfrom_help, loadfrom_syntax}
8313   ,
8314   {"MarkCrosshair", 0, ActionMarkCrosshair,
8315    markcrosshair_help, markcrosshair_syntax}
8316   ,
8317   {"Message", 0, ActionMessage,
8318    message_help, message_syntax}
8319   ,
8320   {"MinMaskGap", 0, ActionMinMaskGap,
8321    minmaskgap_help, minmaskgap_syntax}
8322   ,
8323   {"MinClearGap", 0, ActionMinClearGap,
8324    mincleargap_help, mincleargap_syntax}
8325   ,
8326   {"Mode", 0, ActionMode,
8327    mode_help, mode_syntax}
8328   ,
8329   {"MorphPolygon", 0, ActionMorphPolygon,
8330    morphpolygon_help, morphpolygon_syntax}
8331   ,
8332   {"PasteBuffer", 0, ActionPasteBuffer,
8333    pastebuffer_help, pastebuffer_syntax}
8334   ,
8335   {"Quit", 0, ActionQuit,
8336    quit_help, quit_syntax}
8337   ,
8338   {"RemoveSelected", 0, ActionRemoveSelected,
8339    removeselected_help, removeselected_syntax}
8340   ,
8341   {"Renumber", 0, ActionRenumber,
8342    renumber_help, renumber_syntax}
8343   ,
8344   {"RipUp", 0, ActionRipUp,
8345    ripup_help, ripup_syntax}
8346   ,
8347   {"Select", 0, ActionSelect,
8348    select_help, select_syntax}
8349   ,
8350   {"Unselect", 0, ActionUnselect,
8351    unselect_help, unselect_syntax}
8352   ,
8353   {"SaveSettings", 0, ActionSaveSettings,
8354    savesettings_help, savesettings_syntax}
8355   ,
8356   {"SaveTo", 0, ActionSaveTo,
8357    saveto_help, saveto_syntax}
8358   ,
8359   {"SetSquare", 0, ActionSetSquare,
8360    setsquare_help, setsquare_syntax}
8361   ,
8362   {"SetOctagon", 0, ActionSetOctagon,
8363    setoctagon_help, setoctagon_syntax}
8364   ,
8365   {"SetThermal", 0, ActionSetThermal,
8366    setthermal_help, setthermal_syntax}
8367   ,
8368   {"SetValue", 0, ActionSetValue,
8369    setvalue_help, setvalue_syntax}
8370   ,
8371   {"ToggleHideName", 0, ActionToggleHideName,
8372    togglehidename_help, togglehidename_syntax}
8373   ,
8374   {"Undo", 0, ActionUndo,
8375    undo_help, undo_syntax}
8376   ,
8377   {"Redo", 0, ActionRedo,
8378    redo_help, redo_syntax}
8379   ,
8380   {"SetSame", N_("Select item to use attributes from"), ActionSetSame,
8381    setsame_help, setsame_syntax}
8382   ,
8383   {"SetFlag", 0, ActionSetFlag,
8384    setflag_help, setflag_syntax}
8385   ,
8386   {"ClrFlag", 0, ActionClrFlag,
8387    clrflag_help, clrflag_syntax}
8388   ,
8389   {"ChangeFlag", 0, ActionChangeFlag,
8390    changeflag_help, changeflag_syntax}
8391   ,
8392   {"Polygon", 0, ActionPolygon,
8393    polygon_help, polygon_syntax}
8394   ,
8395   {"RouteStyle", 0, ActionRouteStyle,
8396    routestyle_help, routestyle_syntax}
8397   ,
8398   {"MoveObject", N_("Select an Object"), ActionMoveObject,
8399    moveobject_help, moveobject_syntax}
8400   ,
8401   {"MoveToCurrentLayer", 0, ActionMoveToCurrentLayer,
8402    movetocurrentlayer_help, movetocurrentlayer_syntax}
8403   ,
8404   {"New", 0, ActionNew,
8405    new_help, new_syntax}
8406   ,
8407   {"pscalib", 0, ActionPSCalib}
8408   ,
8409   {"ElementList", 0, ActionElementList,
8410    elementlist_help, elementlist_syntax}
8411   ,
8412   {"ElementSetAttr", 0, ActionElementSetAttr,
8413    elementsetattr_help, elementsetattr_syntax}
8414   ,
8415   {"ExecCommand", 0, ActionExecCommand,
8416    execcommand_help, execcommand_syntax}
8417   ,
8418   {"Import", 0, ActionImport,
8419    import_help, import_syntax}
8420   ,
8421   {"SetViaLayers", 0, ActionSetViaLayers,
8422    setvialayers_help, setvialayers_syntax}
8423   ,
8424 };
8425 
8426 REGISTER_ACTIONS (action_action_list)
8427 
8428