pcb 4.1.1
An interactive printed circuit board layout editor.

action.c

Go to the documentation of this file.
00001 
00035 #ifdef HAVE_CONFIG_H
00036 #include "config.h"
00037 #endif
00038 
00039 #include "global.h"
00040 
00041 #include "action.h"
00042 #include "autoplace.h"
00043 #include "autoroute.h"
00044 #include "buffer.h"
00045 #include "change.h"
00046 #include "copy.h"
00047 #include "create.h"
00048 #include "crosshair.h"
00049 #include "data.h"
00050 #include "draw.h"
00051 #include "error.h"
00052 #include "file.h"
00053 #include "find.h"
00054 #include "hid.h"
00055 #include "insert.h"
00056 #include "line.h"
00057 #include "mymem.h"
00058 #include "misc.h"
00059 #include "mirror.h"
00060 #include "move.h"
00061 #include "polygon.h"
00062 /*#include "print.h"*/
00063 #include "rats.h"
00064 #include "remove.h"
00065 #include "report.h"
00066 #include "rotate.h"
00067 #include "rubberband.h"
00068 #include "search.h"
00069 #include "select.h"
00070 #include "set.h"
00071 #include "thermal.h"
00072 #include "undo.h"
00073 #include "rtree.h"
00074 #include "macro.h"
00075 #include "pcb-printf.h"
00076 
00077 #include <assert.h>
00078 #include <stdlib.h> /* rand() */
00079 
00080 #ifdef HAVE_LIBDMALLOC
00081 #include <dmalloc.h>
00082 #endif
00083 
00084 /* for fork() and friends */
00085 #ifdef HAVE_UNISTD_H
00086 #include <unistd.h>
00087 #endif
00088 
00089 #ifdef HAVE_SYS_WAIT_H
00090 #include <sys/wait.h>
00091 #endif
00092 
00093 /* ---------------------------------------------------------------------------
00094  * some local types
00095  */
00096 typedef enum
00097 {
00098   F_AddSelected,
00099   F_All,
00100   F_AllConnections,
00101   F_AllRats,
00102   F_AllUnusedPins,
00103   F_Arc,
00104   F_Arrow,
00105   F_Block,
00106   F_Description,
00107   F_Cancel,
00108   F_Center,
00109   F_Clear,
00110   F_ClearAndRedraw,
00111   F_ClearList,
00112   F_Close,
00113   F_Found,
00114   F_Connection,
00115   F_Convert,
00116   F_Copy,
00117   F_CycleClip,
00118   F_CycleCrosshair,
00119   F_DeleteRats,
00120   F_Drag,
00121   F_DrillReport,
00122   F_Element,
00123   F_ElementByName,
00124   F_ElementConnections,
00125   F_ElementToBuffer,
00126   F_Escape,
00127   F_Find,
00128   F_FlipElement,
00129   F_FoundPins,
00130   F_Grid,
00131   F_InsertPoint,
00132   F_Layer,
00133   F_Layout,
00134   F_LayoutAs,
00135   F_LayoutToBuffer,
00136   F_Line,
00137   F_LineSize,
00138   F_Lock,
00139   F_Mirror,
00140   F_Move,
00141   F_NameOnPCB,
00142   F_Netlist,
00143   F_NetByName,
00144   F_None,
00145   F_Notify,
00146   F_Object,
00147   F_ObjectByName,
00148   F_PasteBuffer,
00149   F_PadByName,
00150   F_PinByName,
00151   F_PinOrPadName,
00152   F_Pinout,
00153   F_Polygon,
00154   F_PolygonHole,
00155   F_PreviousPoint,
00156   F_RatsNest,
00157   F_Rectangle,
00158   F_Redraw,
00159   F_Release,
00160   F_Revert,
00161   F_Remove,
00162   F_RemoveSelected,
00163   F_Report,
00164   F_Reset,
00165   F_ResetLinesAndPolygons,
00166   F_ResetPinsViasAndPads,
00167   F_Restore,
00168   F_Rotate,
00169   F_Save,
00170   F_Selected,
00171   F_SelectedArcs,
00172   F_SelectedElements,
00173   F_SelectedLines,
00174   F_SelectedNames,
00175   F_SelectedObjects,
00176   F_SelectedPads,
00177   F_SelectedPins,
00178   F_SelectedTexts,
00179   F_SelectedVias,
00180   F_SelectedRats,
00181   F_Stroke,
00182   F_Text,
00183   F_TextByName,
00184   F_TextScale,
00185   F_Thermal,
00186   F_ToLayout,
00187   F_ToggleAllDirections,
00188   F_ToggleAutoDRC,
00189   F_ToggleClearLine,
00190   F_ToggleFullPoly,
00191   F_ToggleGrid,
00192   F_ToggleHideNames,
00193   F_ToggleMask,
00194   F_ToggleName,
00195   F_ToggleObject,
00196   F_ToggleShowDRC,
00197   F_ToggleLiveRoute,
00198   F_ToggleRubberBandMode,
00199   F_ToggleStartDirection,
00200   F_ToggleSnapPin,
00201   F_ToggleThindraw,
00202   F_ToggleLockNames,
00203   F_ToggleOnlyNames,
00204   F_ToggleThindrawPoly,
00205   F_ToggleOrthoMove,
00206   F_ToggleLocalRef,
00207   F_ToggleCheckPlanes,
00208   F_ToggleUniqueNames,
00209   F_Via,
00210   F_ViaByName,
00211   F_Value,
00212   F_ViaDrillingHole,
00213   F_ViaSize,
00214   F_Zoom,
00215   F_ThroughHole,
00216   F_BuriedVias,
00217   F_ToggleAutoBuriedVias
00218 }
00219 FunctionID;
00220 
00221 typedef struct                  /* used to identify subfunctions */
00222 {
00223   char *Identifier;
00224   FunctionID ID;
00225 }
00226 FunctionType;
00227 
00228 /* --------------------------------------------------------------------------- */
00229 
00230 /* %start-doc actions 00delta
00231 
00232 Many actions take a @code{delta} parameter as the last parameter,
00233 which is an amount to change something.  That @code{delta} may include
00234 units, as an additional parameter, such as @code{Action(Object,5,mm)}.
00235 If no units are specified, the default is PCB's native units
00236 (currently 1/100 mil).  Also, if the delta is prefixed by @code{+} or
00237 @code{-}, the size is increased or decreased by that amount.
00238 Otherwise, the size size is set to the given amount.
00239 
00240 @example
00241 Action(Object,5,mil)
00242 Action(Object,+0.5,mm)
00243 Action(Object,-1)
00244 @end example
00245 
00246 Actions which take a @code{delta} parameter which do not accept all
00247 these options will specify what they do take.
00248 
00249 %end-doc */
00250 
00251 /* %start-doc actions 00objects
00252 
00253 Many actions act on indicated objects on the board.  They will have
00254 parameters like @code{ToggleObject} or @code{SelectedVias} to indicate
00255 what group of objects they act on.  Unless otherwise specified, these
00256 parameters are defined as follows:
00257 
00258 @table @code
00259 
00260 @item Object
00261 @itemx ToggleObject
00262 Affects the object under the mouse pointer.  If this action is invoked
00263 from a menu or script, the user will be prompted to click on an
00264 object, which is then the object affected.
00265 
00266 @item Selected
00267 @itemx SelectedObjects
00268 
00269 Affects all objects which are currently selected.  At least, all
00270 selected objects for which the given action makes sense.
00271 
00272 @item SelectedPins
00273 @itemx SelectedVias
00274 @itemx Selected@var{Type}
00275 @itemx @i{etc}
00276 Affects all objects which are both selected and of the @var{Type} specified.
00277 
00278 @end table
00279 
00280 %end-doc */
00281 
00282 /*  %start-doc actions 00macros
00283 
00284 @macro pinshapes
00285 
00286 Pins, pads, and vias can have various shapes.  All may be round.  Pins
00287 and pads may be square (obviously "square" pads are usually
00288 rectangular).  Pins and vias may be octagonal.  When you change a
00289 shape flag of an element, you actually change all of its pins and
00290 pads.
00291 
00292 Note that the square flag takes precedence over the octagon flag,
00293 thus, if both the square and octagon flags are set, the object is
00294 square.  When the square flag is cleared, the pins and pads will be
00295 either round or, if the octagon flag is set, octagonal.
00296 
00297 @end macro
00298 
00299 %end-doc */
00300 
00301 /* ---------------------------------------------------------------------------
00302  * some local identifiers
00303  */
00304 static PointType InsertedPoint;
00305 static LayerType *lastLayer;
00306 static struct
00307 {
00308   PolygonType *poly;
00309   LineType line;
00310 }
00311 fake;
00312 
00313 static struct
00314 {
00315   Coord X, Y;
00316   Cardinal Buffer;
00317   bool Click;
00318   bool Moving;          /* selected type clicked on */
00319   int Hit;                      /* move type clicked on */
00320   void *ptr1;
00321   void *ptr2;
00322   void *ptr3;
00323 }
00324 Note;
00325 
00326 static int defer_updates = 0;
00327 static int defer_needs_update = 0;
00328 
00329 static Cardinal polyIndex = 0;
00330 static bool saved_mode = false;
00331 #ifdef HAVE_LIBSTROKE
00332 static bool mid_stroke = false;
00333 static BoxType StrokeBox;
00334 #endif
00335 static FunctionType Functions[] = {
00336   {"AddSelected", F_AddSelected},
00337   {"All", F_All},
00338   {"AllConnections", F_AllConnections},
00339   {"AllRats", F_AllRats},
00340   {"AllUnusedPins", F_AllUnusedPins},
00341   {"Arc", F_Arc},
00342   {"Arrow", F_Arrow},
00343   {"Block", F_Block},
00344   {"Description", F_Description},
00345   {"Cancel", F_Cancel},
00346   {"Center", F_Center},
00347   {"Clear", F_Clear},
00348   {"ClearAndRedraw", F_ClearAndRedraw},
00349   {"ClearList", F_ClearList},
00350   {"Close", F_Close},
00351   {"Found", F_Found},
00352   {"Connection", F_Connection},
00353   {"Convert", F_Convert},
00354   {"Copy", F_Copy},
00355   {"CycleClip", F_CycleClip},
00356   {"CycleCrosshair", F_CycleCrosshair},
00357   {"DeleteRats", F_DeleteRats},
00358   {"Drag", F_Drag},
00359   {"DrillReport", F_DrillReport},
00360   {"Element", F_Element},
00361   {"ElementByName", F_ElementByName},
00362   {"ElementConnections", F_ElementConnections},
00363   {"ElementToBuffer", F_ElementToBuffer},
00364   {"Escape", F_Escape},
00365   {"Find", F_Find},
00366   {"FlipElement", F_FlipElement},
00367   {"FoundPins", F_FoundPins},
00368   {"Grid", F_Grid},
00369   {"InsertPoint", F_InsertPoint},
00370   {"Layer", F_Layer},
00371   {"Layout", F_Layout},
00372   {"LayoutAs", F_LayoutAs},
00373   {"LayoutToBuffer", F_LayoutToBuffer},
00374   {"Line", F_Line},
00375   {"LineSize", F_LineSize},
00376   {"Lock", F_Lock},
00377   {"Mirror", F_Mirror},
00378   {"Move", F_Move},
00379   {"NameOnPCB", F_NameOnPCB},
00380   {"Netlist", F_Netlist},
00381   {"NetByName", F_NetByName},
00382   {"None", F_None},
00383   {"Notify", F_Notify},
00384   {"Object", F_Object},
00385   {"ObjectByName", F_ObjectByName},
00386   {"PasteBuffer", F_PasteBuffer},
00387   {"PadByName", F_PadByName},
00388   {"PinByName", F_PinByName},
00389   {"PinOrPadName", F_PinOrPadName},
00390   {"Pinout", F_Pinout},
00391   {"Polygon", F_Polygon},
00392   {"PolygonHole", F_PolygonHole},
00393   {"PreviousPoint", F_PreviousPoint},
00394   {"RatsNest", F_RatsNest},
00395   {"Rectangle", F_Rectangle},
00396   {"Redraw", F_Redraw},
00397   {"Release", F_Release},
00398   {"Remove", F_Remove},
00399   {"RemoveSelected", F_RemoveSelected},
00400   {"Report", F_Report},
00401   {"Reset", F_Reset},
00402   {"ResetLinesAndPolygons", F_ResetLinesAndPolygons},
00403   {"ResetPinsViasAndPads", F_ResetPinsViasAndPads},
00404   {"Restore", F_Restore},
00405   {"Revert", F_Revert},
00406   {"Rotate", F_Rotate},
00407   {"Save", F_Save},
00408   {"Selected", F_Selected},
00409   {"SelectedArcs", F_SelectedArcs},
00410   {"SelectedElements", F_SelectedElements},
00411   {"SelectedLines", F_SelectedLines},
00412   {"SelectedNames", F_SelectedNames},
00413   {"SelectedObjects", F_SelectedObjects},
00414   {"SelectedPins", F_SelectedPins},
00415   {"SelectedPads", F_SelectedPads},
00416   {"SelectedRats", F_SelectedRats},
00417   {"SelectedTexts", F_SelectedTexts},
00418   {"SelectedVias", F_SelectedVias},
00419   {"Stroke", F_Stroke},
00420   {"Text", F_Text},
00421   {"TextByName", F_TextByName},
00422   {"TextScale", F_TextScale},
00423   {"Thermal", F_Thermal},
00424   {"ToLayout", F_ToLayout},
00425   {"Toggle45Degree", F_ToggleAllDirections},
00426   {"ToggleClearLine", F_ToggleClearLine},
00427   {"ToggleFullPoly", F_ToggleFullPoly},
00428   {"ToggleGrid", F_ToggleGrid},
00429   {"ToggleMask", F_ToggleMask},
00430   {"ToggleName", F_ToggleName},
00431   {"ToggleObject", F_ToggleObject},
00432   {"ToggleRubberBandMode", F_ToggleRubberBandMode},
00433   {"ToggleStartDirection", F_ToggleStartDirection},
00434   {"ToggleSnapPin", F_ToggleSnapPin},
00435   {"ToggleThindraw", F_ToggleThindraw},
00436   {"ToggleThindrawPoly", F_ToggleThindrawPoly},
00437   {"ToggleLockNames", F_ToggleLockNames},
00438   {"ToggleOnlyNames", F_ToggleOnlyNames},
00439   {"ToggleHideNames", F_ToggleHideNames},
00440   {"ToggleCheckPlanes", F_ToggleCheckPlanes},
00441   {"ToggleLocalRef", F_ToggleLocalRef},
00442   {"ToggleOrthoMove", F_ToggleOrthoMove},
00443   {"ToggleShowDRC", F_ToggleShowDRC},
00444   {"ToggleLiveRoute", F_ToggleLiveRoute},
00445   {"ToggleAutoDRC", F_ToggleAutoDRC},
00446   {"ToggleUniqueNames", F_ToggleUniqueNames},
00447   {"Value", F_Value},
00448   {"Via", F_Via},
00449   {"ViaByName", F_ViaByName},
00450   {"ViaSize", F_ViaSize},
00451   {"ViaDrillingHole", F_ViaDrillingHole},
00452   {"Zoom", F_Zoom},
00453   {"ThroughHole", F_ThroughHole},
00454   {"TH", F_ThroughHole},
00455   {"BuriedVias", F_BuriedVias},
00456   {"ToggleAutoBuriedVias", F_ToggleAutoBuriedVias}
00457 };
00458 
00459 /* ---------------------------------------------------------------------------
00460  * some local routines
00461  */
00462 static int GetFunctionID (String);
00463 static void AdjustAttachedBox (void);
00464 static void NotifyLine (void);
00465 static void NotifyBlock (void);
00466 static void NotifyMode (void);
00467 static void ClearWarnings (void);
00468 #ifdef HAVE_LIBSTROKE
00469 static void FinishStroke (void);
00470 extern void stroke_init (void);
00471 extern void stroke_record (int x, int y);
00472 extern int stroke_trans (char *s);
00473 #endif
00474 static void ChangeFlag (char *, char *, int, char *);
00475 
00476 #define ARG(n) (argc > (n) ? argv[n] : NULL)
00477 
00478 #ifdef HAVE_LIBSTROKE
00479 
00483 void
00484 FinishStroke (void)
00485 {
00486   char msg[255];
00487   int type;
00488   unsigned long num;
00489   void *ptr1, *ptr2, *ptr3;
00490 
00491   mid_stroke = false;
00492   if (stroke_trans (msg))
00493     {
00494       num = atoi (msg);
00495       switch (num)
00496         {
00497         case 456:
00498           if (Settings.Mode == LINE_MODE)
00499             {
00500               SetMode (LINE_MODE);
00501             }
00502           break;
00503         case 9874123:
00504         case 74123:
00505         case 987412:
00506         case 8741236:
00507         case 874123:
00508           RotateScreenObject (StrokeBox.X1, StrokeBox.Y1, SWAP_IDENT ? 1 : 3);
00509           break;
00510         case 7896321:
00511         case 786321:
00512         case 789632:
00513         case 896321:
00514           RotateScreenObject (StrokeBox.X1, StrokeBox.Y1, SWAP_IDENT ? 3 : 1);
00515           break;
00516         case 258:
00517           SetMode (LINE_MODE);
00518           break;
00519         case 852:
00520           SetMode (ARROW_MODE);
00521           break;
00522         case 1478963:
00523           ActionUndo ("");
00524           break;
00525         case 147423:
00526         case 147523:
00527         case 1474123:
00528           Redo (true);
00529           break;
00530         case 148963:
00531         case 147863:
00532         case 147853:
00533         case 145863:
00534           SetMode (VIA_MODE);
00535           break;
00536         case 951:
00537         case 9651:
00538         case 9521:
00539         case 9621:
00540         case 9851:
00541         case 9541:
00542         case 96521:
00543         case 96541:
00544         case 98541:
00545           /* XXX: FIXME: Call a zoom-extents action */
00546           break;
00547         case 159:
00548         case 1269:
00549         case 1259:
00550         case 1459:
00551         case 1569:
00552         case 1589:
00553         case 12569:
00554         case 12589:
00555         case 14589:
00556           /* XXX: FIXME: Zoom to fit the box StrokeBox.[X1,Y1] - StrokeBox.[X2,Y2] */
00557           break;
00558 
00559         default:
00560           Message (_("Unknown stroke %s\n"), msg);
00561           break;
00562         }
00563     }
00564   else
00565     gui->beep ();
00566 }
00567 #endif
00568 
00572 static void
00573 ClearWarnings ()
00574 {
00575   Settings.RatWarn = false;
00576   ALLPIN_LOOP (PCB->Data);
00577   {
00578     if (TEST_FLAG (WARNFLAG, pin))
00579       {
00580         CLEAR_FLAG (WARNFLAG, pin);
00581         DrawPin (pin);
00582       }
00583   }
00584   ENDALL_LOOP;
00585   ALLPAD_LOOP (PCB->Data);
00586   {
00587     if (TEST_FLAG (WARNFLAG, pad))
00588       {
00589         CLEAR_FLAG (WARNFLAG, pad);
00590         DrawPad (pad);
00591       }
00592   }
00593   ENDALL_LOOP;
00594   Draw ();
00595 }
00596 
00604 static void
00605 click_cb (hidval hv)
00606 {
00607   if (Note.Click)
00608     {
00609       notify_crosshair_change (false);
00610       Note.Click = false;
00611       if (Note.Moving && !gui->shift_is_pressed ())
00612         {
00613           Note.Buffer = Settings.BufferNumber;
00614           SetBufferNumber (MAX_BUFFER - 1);
00615           ClearBuffer (PASTEBUFFER);
00616           AddSelectedToBuffer (PASTEBUFFER, Note.X, Note.Y, true);
00617           SaveUndoSerialNumber ();
00618           RemoveSelected ();
00619           SaveMode ();
00620           saved_mode = true;
00621           SetMode (PASTEBUFFER_MODE);
00622         }
00623       else if (Note.Hit && !gui->shift_is_pressed ())
00624         {
00625           SaveMode ();
00626           saved_mode = true;
00627           SetMode (gui->control_is_pressed ()? COPY_MODE : MOVE_MODE);
00628           Crosshair.AttachedObject.Ptr1 = Note.ptr1;
00629           Crosshair.AttachedObject.Ptr2 = Note.ptr2;
00630           Crosshair.AttachedObject.Ptr3 = Note.ptr3;
00631           Crosshair.AttachedObject.Type = Note.Hit;
00632           AttachForCopy (Note.X, Note.Y);
00633         }
00634       else
00635         {
00636           BoxType box;
00637 
00638           Note.Hit = 0;
00639           Note.Moving = false;
00640           SaveUndoSerialNumber ();
00641           box.X1 = -MAX_COORD;
00642           box.Y1 = -MAX_COORD;
00643           box.X2 = MAX_COORD;
00644           box.Y2 = MAX_COORD;
00645           /* unselect first if shift key not down */
00646           if (!gui->shift_is_pressed () && SelectBlock (&box, false))
00647             SetChangedFlag (true);
00648           NotifyBlock ();
00649           Crosshair.AttachedBox.Point1.X = Note.X;
00650           Crosshair.AttachedBox.Point1.Y = Note.Y;
00651         }
00652       notify_crosshair_change (true);
00653     }
00654 }
00655 
00660 static void
00661 ReleaseMode (void)
00662 {
00663   BoxType box;
00664 
00665   if (Note.Click)
00666     {
00667       BoxType box;
00668 
00669       box.X1 = -MAX_COORD;
00670       box.Y1 = -MAX_COORD;
00671       box.X2 = MAX_COORD;
00672       box.Y2 = MAX_COORD;
00673 
00674       Note.Click = false;       /* inhibit timer action */
00675       SaveUndoSerialNumber ();
00676       /* unselect first if shift key not down */
00677       if (!gui->shift_is_pressed ())
00678         {
00679           if (SelectBlock (&box, false))
00680             SetChangedFlag (true);
00681           if (Note.Moving)
00682             {
00683               Note.Moving = 0;
00684               Note.Hit = 0;
00685               return;
00686             }
00687         }
00688         /* Restore the SN so that if we select something the deselect/select combo
00689          gets the same SN. */
00690         RestoreUndoSerialNumber();
00691         if (SelectObject ())
00692             SetChangedFlag (true);
00693         else
00694         /* We didn't select anything new, so, the deselection should get its
00695          own SN. */
00696             IncrementUndoSerialNumber();
00697       Note.Hit = 0;
00698       Note.Moving = 0;
00699     }
00700   else if (Note.Moving)
00701     {
00702       RestoreUndoSerialNumber ();
00703       NotifyMode ();
00704       ClearBuffer (PASTEBUFFER);
00705       SetBufferNumber (Note.Buffer);
00706       Note.Moving = false;
00707       Note.Hit = 0;
00708     }
00709   else if (Note.Hit)
00710     {
00711       NotifyMode ();
00712       Note.Hit = 0;
00713     }
00714   else if (Settings.Mode == ARROW_MODE)
00715     {
00716       box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
00717                     Crosshair.AttachedBox.Point2.X);
00718       box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
00719                     Crosshair.AttachedBox.Point2.Y);
00720       box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
00721                     Crosshair.AttachedBox.Point2.X);
00722       box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
00723                     Crosshair.AttachedBox.Point2.Y);
00724       RestoreUndoSerialNumber ();
00725       if (SelectBlock (&box, true))
00726         SetChangedFlag (true);
00727       else if (Bumped)
00728         IncrementUndoSerialNumber ();
00729       Crosshair.AttachedBox.State = STATE_FIRST;
00730     }
00731   if (saved_mode)
00732     RestoreMode ();
00733   saved_mode = false;
00734 }
00735 
00736 #define HSIZE 257
00737 static char function_hash[HSIZE];
00738 static int hash_initted = 0;
00739 
00740 static int
00741 hashfunc(String s)
00742 {
00743   int i = 0;
00744   while (*s)
00745     {
00746       i ^= i >> 16;
00747       i = (i * 13) ^ (unsigned char)tolower((int) *s);
00748       s ++;
00749     }
00750   i = (unsigned int)i % HSIZE;
00751   return i;
00752 }
00753 
00757 static int
00758 GetFunctionID (String Ident)
00759 {
00760   int i, h;
00761 
00762   if (Ident == 0)
00763     return -1;
00764 
00765   if (!hash_initted)
00766     {
00767       hash_initted = 1;
00768       if (HSIZE < ENTRIES (Functions) * 2)
00769         {
00770           fprintf(stderr, _("Error: function hash size too small (%d vs %lu at %s:%d)\n"),
00771                   HSIZE, (unsigned long) ENTRIES (Functions)*2, __FILE__,  __LINE__);
00772           exit(1);
00773         }
00774       if (ENTRIES (Functions) > 254)
00775         {
00776           /* Change 'char' to 'int' and remove this when we get to 256
00777              strings to hash. */
00778           fprintf(stderr, _("Error: function hash type too small (%d vs %lu at %s:%d)\n"),
00779                   256, (unsigned long) ENTRIES (Functions), __FILE__,  __LINE__);
00780           exit(1);
00781           
00782         }
00783       for (i=ENTRIES (Functions)-1; i>=0; i--)
00784         {
00785           h = hashfunc (Functions[i].Identifier);
00786           while (function_hash[h])
00787             h = (h + 1) % HSIZE;
00788           function_hash[h] = i + 1;
00789         }
00790     }
00791 
00792   i = hashfunc (Ident);
00793   while (1)
00794     {
00795       /* We enforce the "hash table bigger than function table" rule,
00796          so we know there will be at least one zero entry to find.  */
00797       if (!function_hash[i])
00798         return (-1);
00799       if (!strcasecmp (Ident, Functions[function_hash[i]-1].Identifier))
00800         return ((int) Functions[function_hash[i]-1].ID);
00801       i = (i + 1) % HSIZE;
00802     }
00803 }
00804 
00810 static void
00811 AdjustAttachedBox (void)
00812 {
00813   if (Settings.Mode == ARC_MODE)
00814     {
00815       Crosshair.AttachedBox.otherway = gui->shift_is_pressed ();
00816       return;
00817     }
00818   switch (Crosshair.AttachedBox.State)
00819     {
00820     case STATE_SECOND:          /* one corner is selected */
00821       {
00822         /* update coordinates */
00823         Crosshair.AttachedBox.Point2.X = Crosshair.X;
00824         Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
00825         break;
00826       }
00827     }
00828 }
00829 
00834 void
00835 AdjustAttachedObjects (void)
00836 {
00837   PointType *pnt;
00838   switch (Settings.Mode)
00839     {
00840       /* update at least an attached block (selection) */
00841     case NO_MODE:
00842     case ARROW_MODE:
00843       if (Crosshair.AttachedBox.State)
00844         {
00845           Crosshair.AttachedBox.Point2.X = Crosshair.X;
00846           Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
00847         }
00848       break;
00849 
00850       /* rectangle creation mode */
00851     case RECTANGLE_MODE:
00852     case ARC_MODE:
00853       AdjustAttachedBox ();
00854       break;
00855 
00856       /* polygon creation mode */
00857     case POLYGON_MODE:
00858     case POLYGONHOLE_MODE:
00859       AdjustAttachedLine ();
00860       break;
00861       /* line creation mode */
00862     case LINE_MODE:
00863       if (PCB->RatDraw || PCB->Clipping == 0)
00864         AdjustAttachedLine ();
00865       else
00866         AdjustTwoLine (PCB->Clipping - 1);
00867       break;
00868       /* point insertion mode */
00869     case INSERTPOINT_MODE:
00870       pnt = AdjustInsertPoint ();
00871       if (pnt)
00872         InsertedPoint = *pnt;
00873       break;
00874     case ROTATE_MODE:
00875       break;
00876     }
00877 }
00878 
00882 static void
00883 NotifyLine (void)
00884 {
00885   int type = NO_TYPE;
00886   void *ptr1, *ptr2, *ptr3;
00887 
00888   if (!Marked.status || TEST_FLAG (LOCALREFFLAG, PCB))
00889     SetLocalRef (Crosshair.X, Crosshair.Y, true);
00890   switch (Crosshair.AttachedLine.State)
00891     {
00892     case STATE_FIRST:           /* first point */
00893       if (PCB->RatDraw && SearchScreen (Crosshair.X, Crosshair.Y,
00894                                         PAD_TYPE | PIN_TYPE, &ptr1, &ptr1,
00895                                         &ptr1) == NO_TYPE)
00896         {
00897           gui->beep ();
00898           break;
00899         }
00900       if (TEST_FLAG (AUTODRCFLAG, PCB) && Settings.Mode == LINE_MODE)
00901         {
00902           type = SearchScreen (Crosshair.X, Crosshair.Y,
00903                                PIN_TYPE | PAD_TYPE | VIA_TYPE, &ptr1, &ptr2,
00904                                &ptr3);
00905           LookupConnection (Crosshair.X, Crosshair.Y, true, 1, CONNECTEDFLAG, false);
00906           LookupConnection (Crosshair.X, Crosshair.Y, true, 1, FOUNDFLAG, true);
00907         }
00908       if (type == PIN_TYPE || type == VIA_TYPE)
00909         {
00910           Crosshair.AttachedLine.Point1.X =
00911             Crosshair.AttachedLine.Point2.X = ((PinType *) ptr2)->X;
00912           Crosshair.AttachedLine.Point1.Y =
00913             Crosshair.AttachedLine.Point2.Y = ((PinType *) ptr2)->Y;
00914         }
00915       else if (type == PAD_TYPE)
00916         {
00917           PadType *pad = (PadType *) ptr2;
00918           double d1 = Distance (Crosshair.X, Crosshair.Y, pad->Point1.X, pad->Point1.Y);
00919           double d2 = Distance (Crosshair.X, Crosshair.Y, pad->Point2.X, pad->Point2.Y);
00920           if (d2 < d1)
00921             {
00922               Crosshair.AttachedLine.Point1 =
00923                 Crosshair.AttachedLine.Point2 = pad->Point2;
00924             }
00925           else
00926             {
00927               Crosshair.AttachedLine.Point1 =
00928                 Crosshair.AttachedLine.Point2 = pad->Point1;
00929             }
00930         }
00931       else
00932         {
00933           Crosshair.AttachedLine.Point1.X =
00934             Crosshair.AttachedLine.Point2.X = Crosshair.X;
00935           Crosshair.AttachedLine.Point1.Y =
00936             Crosshair.AttachedLine.Point2.Y = Crosshair.Y;
00937         }
00938       Crosshair.AttachedLine.State = STATE_SECOND;
00939       break;
00940 
00941     case STATE_SECOND:
00942       /* fall through to third state too */
00943       lastLayer = CURRENT;
00944     default:                    /* all following points */
00945       Crosshair.AttachedLine.State = STATE_THIRD;
00946       break;
00947     }
00948 }
00949 
00953 static void
00954 NotifyBlock (void)
00955 {
00956   notify_crosshair_change (false);
00957   switch (Crosshair.AttachedBox.State)
00958     {
00959     case STATE_FIRST:           /* setup first point */
00960       Crosshair.AttachedBox.Point1.X =
00961         Crosshair.AttachedBox.Point2.X = Crosshair.X;
00962       Crosshair.AttachedBox.Point1.Y =
00963         Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
00964       Crosshair.AttachedBox.State = STATE_SECOND;
00965       break;
00966 
00967     case STATE_SECOND:          /* setup second point */
00968       Crosshair.AttachedBox.State = STATE_THIRD;
00969       break;
00970     }
00971   notify_crosshair_change (true);
00972 }
00973 
00974 
00985 static void
00986 NotifyMode (void)
00987 {
00988   void *ptr1, *ptr2, *ptr3;
00989   int type;
00990 
00991   if (Settings.RatWarn)
00992     ClearWarnings ();
00993   switch (Settings.Mode)
00994     {
00995     case ARROW_MODE:
00996       {
00997         int test;
00998         hidval hv;
00999 
01000         Note.Click = true;
01001         /* do something after click time */
01002         gui->add_timer (click_cb, CLICK_TIME, hv);
01003 
01004         /* see if we clicked on something already selected
01005          * (Note.Moving) or clicked on a MOVE_TYPE
01006          * (Note.Hit)
01007          */
01008         for (test = (SELECT_TYPES | MOVE_TYPES) & ~RATLINE_TYPE;
01009              test; test &= ~type)
01010           {
01011             type = SearchScreen (Note.X, Note.Y, test, &ptr1, &ptr2, &ptr3);
01012             if (!Note.Hit && (type & MOVE_TYPES) &&
01013                 !TEST_FLAG (LOCKFLAG, (PinType *) ptr2))
01014               {
01015                 Note.Hit = type;
01016                 Note.ptr1 = ptr1;
01017                 Note.ptr2 = ptr2;
01018                 Note.ptr3 = ptr3;
01019               }
01020             if (!Note.Moving && (type & SELECT_TYPES) &&
01021                 TEST_FLAG (SELECTEDFLAG, (PinType *) ptr2))
01022               Note.Moving = true;
01023             if ((Note.Hit && Note.Moving) || type == NO_TYPE)
01024               break;
01025           }
01026         break;
01027       }
01028 
01029     case VIA_MODE:
01030       {
01031         PinType *via;
01032 
01033         if (!PCB->ViaOn)
01034           {
01035             Message (_("You must turn via visibility on before\n"
01036                        "you can place vias\n"));
01037             break;
01038           }
01039         if ((via = CreateNewVia (PCB->Data, Note.X, Note.Y,
01040                                  Settings.ViaThickness, 2 * Settings.Keepaway,
01041                                  0, Settings.ViaDrillingHole, NULL,
01042                                  NoFlags ())) != NULL)
01043           {
01044             AddObjectToCreateUndoList (VIA_TYPE, via, via, via);
01045             if (gui->shift_is_pressed ())
01046               ChangeObjectThermal (VIA_TYPE, via, via, via, PCB->ThermStyle);
01047             IncrementUndoSerialNumber ();
01048             DrawVia (via);
01049             Draw ();
01050           }
01051         break;
01052       }
01053 
01054     case ARC_MODE:
01055       {
01056         switch (Crosshair.AttachedBox.State)
01057           {
01058           case STATE_FIRST:
01059             Crosshair.AttachedBox.Point1.X =
01060               Crosshair.AttachedBox.Point2.X = Note.X;
01061             Crosshair.AttachedBox.Point1.Y =
01062               Crosshair.AttachedBox.Point2.Y = Note.Y;
01063             Crosshair.AttachedBox.State = STATE_SECOND;
01064             break;
01065 
01066           case STATE_SECOND:
01067           case STATE_THIRD:
01068             {
01069               ArcType *arc;
01070               Coord wx, wy;
01071               Angle sa, dir;
01072 
01073               wx = Note.X - Crosshair.AttachedBox.Point1.X;
01074               wy = Note.Y - Crosshair.AttachedBox.Point1.Y;
01075               if (XOR (Crosshair.AttachedBox.otherway, abs (wy) > abs (wx)))
01076                 {
01077                   Crosshair.AttachedBox.Point2.X =
01078                     Crosshair.AttachedBox.Point1.X + abs (wy) * SGNZ (wx);
01079                   sa = (wx >= 0) ? 0 : 180;
01080 #ifdef ARC45
01081                   if (abs (wy) / 2 >= abs (wx))
01082                     dir = (SGNZ (wx) == SGNZ (wy)) ? 45 : -45;
01083                   else
01084 #endif
01085                     dir = (SGNZ (wx) == SGNZ (wy)) ? 90 : -90;
01086                 }
01087               else
01088                 {
01089                   Crosshair.AttachedBox.Point2.Y =
01090                     Crosshair.AttachedBox.Point1.Y + abs (wx) * SGNZ (wy);
01091                   sa = (wy >= 0) ? -90 : 90;
01092 #ifdef ARC45
01093                   if (abs (wx) / 2 >= abs (wy))
01094                     dir = (SGNZ (wx) == SGNZ (wy)) ? -45 : 45;
01095                   else
01096 #endif
01097                     dir = (SGNZ (wx) == SGNZ (wy)) ? -90 : 90;
01098                   wy = wx;
01099                 }
01100               if (abs (wy) > 0 && (arc = CreateNewArcOnLayer (CURRENT,
01101                                                               Crosshair.
01102                                                               AttachedBox.
01103                                                               Point2.X,
01104                                                               Crosshair.
01105                                                               AttachedBox.
01106                                                               Point2.Y,
01107                                                               abs (wy),
01108                                                               abs (wy),
01109                                                               sa,
01110                                                               dir,
01111                                                               Settings.
01112                                                               LineThickness,
01113                                                               2 * Settings.
01114                                                               Keepaway,
01115                                                               MakeFlags
01116                                                               (TEST_FLAG
01117                                                                (CLEARNEWFLAG,
01118                                                                 PCB) ?
01119                                                                CLEARLINEFLAG :
01120                                                                0))))
01121                 {
01122                   BoxType *bx;
01123 
01124                   bx = GetArcEnds (arc);
01125                   Crosshair.AttachedBox.Point1.X =
01126                     Crosshair.AttachedBox.Point2.X = bx->X2;
01127                   Crosshair.AttachedBox.Point1.Y =
01128                     Crosshair.AttachedBox.Point2.Y = bx->Y2;
01129                   AddObjectToCreateUndoList (ARC_TYPE, CURRENT, arc, arc);
01130                   IncrementUndoSerialNumber ();
01131                   addedLines++;
01132                   DrawArc (CURRENT, arc);
01133                   Draw ();
01134                   Crosshair.AttachedBox.State = STATE_THIRD;
01135                 }
01136               break;
01137             }
01138           }
01139         break;
01140       }
01141     case LOCK_MODE:
01142       {
01143         type = SearchScreen (Note.X, Note.Y, LOCK_TYPES, &ptr1, &ptr2, &ptr3);
01144         if (type == ELEMENT_TYPE)
01145           {
01146             ElementType *element = (ElementType *) ptr2;
01147 
01148             TOGGLE_FLAG (LOCKFLAG, element);
01149             PIN_LOOP (element);
01150             {
01151               TOGGLE_FLAG (LOCKFLAG, pin);
01152               CLEAR_FLAG (SELECTEDFLAG, pin);
01153             }
01154             END_LOOP;
01155             PAD_LOOP (element);
01156             {
01157               TOGGLE_FLAG (LOCKFLAG, pad);
01158               CLEAR_FLAG (SELECTEDFLAG, pad);
01159             }
01160             END_LOOP;
01161             CLEAR_FLAG (SELECTEDFLAG, element);
01162             /* always re-draw it since I'm too lazy
01163              * to tell if a selected flag changed
01164              */
01165             DrawElement (element);
01166             Draw ();
01167             SetChangedFlag (true);
01168             hid_actionl ("Report", "Object", NULL);
01169           }
01170         else if (type != NO_TYPE)
01171           {
01172             TextType *thing = (TextType *) ptr3;
01173             TOGGLE_FLAG (LOCKFLAG, thing);
01174             if (TEST_FLAG (LOCKFLAG, thing)
01175                 && TEST_FLAG (SELECTEDFLAG, thing))
01176               {
01177                 /* this is not un-doable since LOCK isn't */
01178                 CLEAR_FLAG (SELECTEDFLAG, thing);
01179                 DrawObject (type, ptr1, ptr2);
01180                 Draw ();
01181               }
01182             SetChangedFlag (true);
01183             hid_actionl ("Report", "Object", NULL);
01184           }
01185         break;
01186       }
01187     case THERMAL_MODE:
01188       {
01189         if (((type
01190               =
01191               SearchScreen (Note.X, Note.Y, PIN_TYPES, &ptr1, &ptr2,
01192                             &ptr3)) != NO_TYPE)
01193             && !TEST_FLAG (HOLEFLAG, (PinType *) ptr3))
01194           {
01195             if (gui->shift_is_pressed ())
01196               {
01197                 int tstyle = GET_THERM (INDEXOFCURRENT, (PinType *) ptr3);
01198                 tstyle++;
01199                 if (tstyle > 5)
01200                   tstyle = 1;
01201                 ChangeObjectThermal (type, ptr1, ptr2, ptr3, tstyle);
01202               }
01203             else if (GET_THERM (INDEXOFCURRENT, (PinType *) ptr3))
01204               ChangeObjectThermal (type, ptr1, ptr2, ptr3, 0);
01205             else
01206               ChangeObjectThermal (type, ptr1, ptr2, ptr3, PCB->ThermStyle);
01207           }
01208         break;
01209       }
01210 
01211     case LINE_MODE:
01212       /* do update of position */
01213       NotifyLine ();
01214       if (Crosshair.AttachedLine.State != STATE_THIRD)
01215         break;
01216 
01217       /* Remove anchor if clicking on start point;
01218        * this means we can't paint 0 length lines
01219        * which could be used for square SMD pads.
01220        * Instead use a very small delta, or change
01221        * the file after saving.
01222        */
01223       if (Crosshair.X == Crosshair.AttachedLine.Point1.X
01224           && Crosshair.Y == Crosshair.AttachedLine.Point1.Y)
01225         {
01226           SetMode (LINE_MODE);
01227           break;
01228         }
01229 
01230       if (PCB->RatDraw)
01231         {
01232           RatType *line;
01233           if ((line = AddNet ()))
01234             {
01235               addedLines++;
01236               AddObjectToCreateUndoList (RATLINE_TYPE, line, line, line);
01237               IncrementUndoSerialNumber ();
01238               DrawRat (line);
01239               Crosshair.AttachedLine.Point1.X =
01240                 Crosshair.AttachedLine.Point2.X;
01241               Crosshair.AttachedLine.Point1.Y =
01242                 Crosshair.AttachedLine.Point2.Y;
01243               Draw ();
01244             }
01245           break;
01246         }
01247       else
01248         /* create line if both ends are determined && length != 0 */
01249         {
01250           LineType *line;
01251           int line_flags = 0;
01252 
01253           if (TEST_FLAG (AUTODRCFLAG, PCB) && !TEST_SILK_LAYER (CURRENT))
01254             line_flags |= CONNECTEDFLAG | FOUNDFLAG;
01255 
01256           if (TEST_FLAG (CLEARNEWFLAG, PCB))
01257             line_flags |= CLEARLINEFLAG;
01258 
01259           if (PCB->Clipping
01260               && Crosshair.AttachedLine.Point1.X ==
01261               Crosshair.AttachedLine.Point2.X
01262               && Crosshair.AttachedLine.Point1.Y ==
01263               Crosshair.AttachedLine.Point2.Y
01264               && (Crosshair.AttachedLine.Point2.X != Note.X
01265                   || Crosshair.AttachedLine.Point2.Y != Note.Y))
01266             {
01267               /* We will only need to paint the second line segment.
01268                  Since we only check for vias on the first segment,
01269                  swap them so the non-empty segment is the first segment. */
01270               Crosshair.AttachedLine.Point2.X = Note.X;
01271               Crosshair.AttachedLine.Point2.Y = Note.Y;
01272             }
01273 
01274           if ((Crosshair.AttachedLine.Point1.X !=
01275                Crosshair.AttachedLine.Point2.X
01276                || Crosshair.AttachedLine.Point1.Y !=
01277                Crosshair.AttachedLine.Point2.Y))
01278             {
01279               PinType *via;
01280               Cardinal layer_from, layer_to;
01281 
01282               if ((line =
01283                   CreateDrawnLineOnLayer (CURRENT,
01284                                           Crosshair.AttachedLine.Point1.X,
01285                                           Crosshair.AttachedLine.Point1.Y,
01286                                           Crosshair.AttachedLine.Point2.X,
01287                                           Crosshair.AttachedLine.Point2.Y,
01288                                           Settings.LineThickness,
01289                                           2 * Settings.Keepaway,
01290                                           MakeFlags (line_flags))) != NULL)
01291                 {
01292                   addedLines++;
01293                   AddObjectToCreateUndoList (LINE_TYPE, CURRENT, line, line);
01294                   DrawLine (CURRENT, line);
01295                 }
01296               /* place a via if vias are visible, the layer is
01297                  in a new group since the last line and there
01298                  isn't a pin already here */
01299               if (TEST_FLAG (AUTOBURIEDVIASFLAG, PCB))
01300                 {
01301                   layer_from = GetLayerNumber (PCB->Data, lastLayer);
01302                   layer_to = GetLayerNumber (PCB->Data, CURRENT);
01303                 }
01304               else
01305                 {
01306                   layer_from = 0;
01307                   layer_to = 0;
01308                 }
01309 
01310               if (PCB->ViaOn && GetLayerGroupNumberByPointer (CURRENT) !=
01311                   GetLayerGroupNumberByPointer (lastLayer) &&
01312                   SearchObjectByLocation (PIN_TYPES, &ptr1, &ptr2, &ptr3,
01313                                           Crosshair.AttachedLine.Point1.X,
01314                                           Crosshair.AttachedLine.Point1.Y,
01315                                           Settings.ViaThickness / 2) ==
01316                   NO_TYPE
01317                   && (via =
01318                       CreateNewViaEx (PCB->Data,
01319                                     Crosshair.AttachedLine.Point1.X,
01320                                     Crosshair.AttachedLine.Point1.Y,
01321                                     Settings.ViaThickness,
01322                                     2 * Settings.Keepaway, 0,
01323                                     Settings.ViaDrillingHole, NULL,
01324                                     NoFlags (), layer_from, layer_to)) != NULL)
01325                 {
01326                   AddObjectToCreateUndoList (VIA_TYPE, via, via, via);
01327                   DrawVia (via);
01328                 }
01329               /* copy the coordinates */
01330               Crosshair.AttachedLine.Point1.X =
01331                 Crosshair.AttachedLine.Point2.X;
01332               Crosshair.AttachedLine.Point1.Y =
01333                 Crosshair.AttachedLine.Point2.Y;
01334               IncrementUndoSerialNumber ();
01335               lastLayer = CURRENT;
01336             }
01337           if (PCB->Clipping && (Note.X != Crosshair.AttachedLine.Point2.X
01338                                 || Note.Y !=
01339                                 Crosshair.AttachedLine.Point2.Y))
01340             {
01341               if ((line =
01342                   CreateDrawnLineOnLayer (CURRENT,
01343                                           Crosshair.AttachedLine.Point2.X,
01344                                           Crosshair.AttachedLine.Point2.Y,
01345                                           Note.X, Note.Y,
01346                                           Settings.LineThickness,
01347                                           2 * Settings.Keepaway,
01348                                           MakeFlags (line_flags))) != NULL)
01349                 {
01350                   addedLines++;
01351                   AddObjectToCreateUndoList (LINE_TYPE, CURRENT, line, line);
01352                   IncrementUndoSerialNumber ();
01353                   DrawLine (CURRENT, line);
01354                 }
01355               /* move to new start point */
01356               Crosshair.AttachedLine.Point1.X = Note.X;
01357               Crosshair.AttachedLine.Point1.Y = Note.Y;
01358               Crosshair.AttachedLine.Point2.X = Note.X;
01359               Crosshair.AttachedLine.Point2.Y = Note.Y;
01360               if (TEST_FLAG (SWAPSTARTDIRFLAG, PCB))
01361                 {
01362                   PCB->Clipping ^= 3;
01363                 }
01364             }
01365           if (TEST_FLAG (AUTODRCFLAG, PCB) && !TEST_SILK_LAYER (CURRENT))
01366             LookupConnection (Note.X, Note.Y, true, 1, CONNECTEDFLAG, false);
01367           Draw ();
01368         }
01369       break;
01370 
01371     case RECTANGLE_MODE:
01372       /* do update of position */
01373       NotifyBlock ();
01374 
01375       /* create rectangle if both corners are determined 
01376        * and width, height are != 0
01377        */
01378       if (Crosshair.AttachedBox.State == STATE_THIRD &&
01379           Crosshair.AttachedBox.Point1.X != Crosshair.AttachedBox.Point2.X &&
01380           Crosshair.AttachedBox.Point1.Y != Crosshair.AttachedBox.Point2.Y)
01381         {
01382           PolygonType *polygon;
01383 
01384           int flags = CLEARPOLYFLAG;
01385           if (TEST_FLAG (NEWFULLPOLYFLAG, PCB))
01386             flags |= FULLPOLYFLAG;
01387           if ((polygon = CreateNewPolygonFromRectangle (CURRENT,
01388                                                         Crosshair.
01389                                                         AttachedBox.Point1.X,
01390                                                         Crosshair.
01391                                                         AttachedBox.Point1.Y,
01392                                                         Crosshair.
01393                                                         AttachedBox.Point2.X,
01394                                                         Crosshair.
01395                                                         AttachedBox.Point2.Y,
01396                                                         MakeFlags
01397                                                         (flags))) !=
01398               NULL)
01399             {
01400               AddObjectToCreateUndoList (POLYGON_TYPE, CURRENT,
01401                                          polygon, polygon);
01402               IncrementUndoSerialNumber ();
01403               DrawPolygon (CURRENT, polygon);
01404               Draw ();
01405             }
01406 
01407           /* reset state to 'first corner' */
01408           Crosshair.AttachedBox.State = STATE_FIRST;
01409         }
01410       break;
01411 
01412     case TEXT_MODE:
01413       {
01414         char *string;
01415 
01416         if ((string = gui->prompt_for (_("Enter text:"), "")) != NULL)
01417           {
01418             if (strlen(string) > 0)
01419               {
01420                 TextType *text;
01421                 int flag = CLEARLINEFLAG;
01422 
01423                 if (GetLayerGroupNumberByNumber (INDEXOFCURRENT) ==
01424                     GetLayerGroupNumberBySide (BOTTOM_SIDE))
01425                   flag |= ONSOLDERFLAG;
01426                 if ((text = CreateNewText (CURRENT, &PCB->Font, Note.X,
01427                                            Note.Y, 0, Settings.TextScale,
01428                                            string, MakeFlags (flag))) != NULL)
01429                   {
01430                     AddObjectToCreateUndoList (TEXT_TYPE, CURRENT, text, text);
01431                     IncrementUndoSerialNumber ();
01432                     DrawText (CURRENT, text);
01433                     Draw ();
01434                   }
01435                 }
01436             free (string);
01437           }
01438         break;
01439       }
01440 
01441     case POLYGON_MODE:
01442       {
01443         PointType *points = Crosshair.AttachedPolygon.Points;
01444         Cardinal n = Crosshair.AttachedPolygon.PointN;
01445 
01446         /* do update of position; use the 'LINE_MODE' mechanism */
01447         NotifyLine ();
01448 
01449         /* check if this is the last point of a polygon */
01450         if (n >= 3 &&
01451             points->X == Crosshair.AttachedLine.Point2.X &&
01452             points->Y == Crosshair.AttachedLine.Point2.Y)
01453           {
01454             CopyAttachedPolygonToLayer ();
01455             Draw ();
01456             break;
01457           }
01458 
01459         /* create new point if it's the first one or if it's
01460          * different to the last one
01461          */
01462         if (!n ||
01463             points[n - 1].X != Crosshair.AttachedLine.Point2.X ||
01464             points[n - 1].Y != Crosshair.AttachedLine.Point2.Y)
01465           {
01466             CreateNewPointInPolygon (&Crosshair.AttachedPolygon,
01467                                      Crosshair.AttachedLine.Point2.X,
01468                                      Crosshair.AttachedLine.Point2.Y);
01469 
01470             /* copy the coordinates */
01471             Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X;
01472             Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y;
01473           }
01474         break;
01475       }
01476 
01477     case POLYGONHOLE_MODE:
01478       {
01479         switch (Crosshair.AttachedObject.State)
01480           {
01481             /* first notify, lookup object */
01482           case STATE_FIRST:
01483             Crosshair.AttachedObject.Type =
01484               SearchScreen (Note.X, Note.Y, POLYGON_TYPE,
01485                             &Crosshair.AttachedObject.Ptr1,
01486                             &Crosshair.AttachedObject.Ptr2,
01487                             &Crosshair.AttachedObject.Ptr3);
01488 
01489           if (Crosshair.AttachedObject.Type == NO_TYPE)
01490             {
01491               Message (_("The first point of a polygon hole must be on a polygon.\n"));
01492               break; /* don't start doing anything if clicked outside of polys */
01493             }
01494 
01495           if (TEST_FLAG(LOCKFLAG, (PolygonType *) Crosshair.AttachedObject.Ptr2))
01496             {
01497               Message (_("Sorry, the object is locked\n"));
01498               Crosshair.AttachedObject.Type = NO_TYPE;
01499               break;
01500             }
01501           else
01502             Crosshair.AttachedObject.State = STATE_SECOND;
01503             /* Fall thru: first click is also the first point of the
01504              * poly hole. */
01505 
01506             /* second notify, insert new point into object */
01507           case STATE_SECOND:
01508             {
01509               PointType *points = Crosshair.AttachedPolygon.Points;
01510               Cardinal n = Crosshair.AttachedPolygon.PointN;
01511               POLYAREA *original, *new_hole, *result;
01512               FlagType Flags;
01513 
01514               /* do update of position; use the 'LINE_MODE' mechanism */
01515               NotifyLine ();
01516 
01517               /* check if this is the last point of a polygon */
01518               if (n >= 3 &&
01519                   points->X == Crosshair.AttachedLine.Point2.X &&
01520                   points->Y == Crosshair.AttachedLine.Point2.Y)
01521                 {
01522                   /* Create POLYAREAs from the original polygon
01523                    * and the new hole polygon */
01524                   original = PolygonToPoly ((PolygonType *)Crosshair.AttachedObject.Ptr2);
01525                   new_hole = PolygonToPoly (&Crosshair.AttachedPolygon);
01526 
01527                   /* Subtract the hole from the original polygon shape */
01528                   poly_Boolean_free (original, new_hole, &result, PBO_SUB);
01529 
01530                   /* Convert the resulting polygon(s) into a new set of nodes
01531                    * and place them on the page. Delete the original polygon.
01532                    */
01533                   SaveUndoSerialNumber ();
01534                   Flags = ((PolygonType *)Crosshair.AttachedObject.Ptr2)->Flags;
01535                   PolyToPolygonsOnLayer (PCB->Data, (LayerType *)Crosshair.AttachedObject.Ptr1,
01536                                          result, Flags);
01537                   RemoveObject (POLYGON_TYPE,
01538                                 Crosshair.AttachedObject.Ptr1,
01539                                 Crosshair.AttachedObject.Ptr2,
01540                                 Crosshair.AttachedObject.Ptr3);
01541                   RestoreUndoSerialNumber ();
01542                   IncrementUndoSerialNumber ();
01543                   Draw ();
01544 
01545                 /* reset state of attached line */
01546                 memset (&Crosshair.AttachedPolygon, 0, sizeof (PolygonType));
01547                 Crosshair.AttachedObject.State = STATE_FIRST;
01548                 addedLines = 0;
01549 
01550                   break;
01551                 }
01552 
01553               /* create new point if it's the first one or if it's
01554                * different to the last one
01555                */
01556               if (!n ||
01557                   points[n - 1].X != Crosshair.AttachedLine.Point2.X ||
01558                   points[n - 1].Y != Crosshair.AttachedLine.Point2.Y)
01559                 {
01560                   CreateNewPointInPolygon (&Crosshair.AttachedPolygon,
01561                                            Crosshair.AttachedLine.Point2.X,
01562                                            Crosshair.AttachedLine.Point2.Y);
01563 
01564                   /* copy the coordinates */
01565                   Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X;
01566                   Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y;
01567                 }
01568               break;
01569             }
01570           }
01571 
01572         break;
01573       }
01574 
01575     case PASTEBUFFER_MODE:
01576       {
01577         TextType estr[MAX_ELEMENTNAMES];
01578         ElementType *e = 0;
01579 
01580         if (gui->shift_is_pressed ())
01581           {
01582             int type =
01583               SearchScreen (Note.X, Note.Y, ELEMENT_TYPE, &ptr1, &ptr2,
01584                             &ptr3);
01585             if (type == ELEMENT_TYPE)
01586               {
01587                 e = (ElementType *) ptr1;
01588                 if (e)
01589                   {
01590                     int i;
01591 
01592                     memcpy (estr, e->Name,
01593                             MAX_ELEMENTNAMES * sizeof (TextType));
01594                     for (i = 0; i < MAX_ELEMENTNAMES; ++i)
01595                       estr[i].TextString = estr[i].TextString ? strdup(estr[i].TextString) : NULL;
01596                     RemoveElement (e);
01597                   }
01598               }
01599           }
01600         if (CopyPastebufferToLayout (Note.X, Note.Y))
01601           SetChangedFlag (true);
01602         if (e)
01603           {
01604             int type =
01605               SearchScreen (Note.X, Note.Y, ELEMENT_TYPE, &ptr1, &ptr2,
01606                             &ptr3);
01607             if (type == ELEMENT_TYPE && ptr1)
01608               {
01609                 int i, save_n;
01610                 e = (ElementType *) ptr1;
01611 
01612                 save_n = NAME_INDEX (PCB);
01613 
01614                 for (i = 0; i < MAX_ELEMENTNAMES; i++)
01615                   {
01616                     if (i == save_n)
01617                       EraseElementName (e);
01618                     r_delete_entry (PCB->Data->name_tree[i],
01619                                     (BoxType *) & (e->Name[i]));
01620                     memcpy (&(e->Name[i]), &(estr[i]), sizeof (TextType));
01621                     e->Name[i].Element = e;
01622                     SetTextBoundingBox (&PCB->Font, &(e->Name[i]));
01623                     r_insert_entry (PCB->Data->name_tree[i],
01624                                     (BoxType *) & (e->Name[i]), 0);
01625                     if (i == save_n)
01626                       DrawElementName (e);
01627                   }
01628               }
01629           }
01630         break;
01631       }
01632 
01633     case REMOVE_MODE:
01634       if ((type =
01635            SearchScreen (Note.X, Note.Y, REMOVE_TYPES, &ptr1, &ptr2,
01636                          &ptr3)) != NO_TYPE)
01637         {
01638           if (TEST_FLAG (LOCKFLAG, (LineType *) ptr2))
01639             {
01640               Message (_("Sorry, the object is locked\n"));
01641               break;
01642             }
01643           if (type == ELEMENT_TYPE)
01644             {
01645               RubberbandType *ptr;
01646               int i;
01647 
01648               Crosshair.AttachedObject.RubberbandN = 0;
01649               LookupRatLines (type, ptr1, ptr2, ptr3);
01650               ptr = Crosshair.AttachedObject.Rubberband;
01651               for (i = 0; i < Crosshair.AttachedObject.RubberbandN; i++)
01652                 {
01653                   if (PCB->RatOn)
01654                     EraseRat ((RatType *) ptr->Line);
01655                   if (TEST_FLAG (RUBBERENDFLAG, ptr->Line))
01656                     MoveObjectToRemoveUndoList (RATLINE_TYPE,
01657                                                 ptr->Line, ptr->Line,
01658                                                 ptr->Line);
01659                   else
01660                     TOGGLE_FLAG (RUBBERENDFLAG, ptr->Line); /* only remove line once */
01661                   ptr++;
01662                 }
01663             }
01664           RemoveObject (type, ptr1, ptr2, ptr3);
01665           IncrementUndoSerialNumber ();
01666           SetChangedFlag (true);
01667         }
01668       break;
01669 
01670     case ROTATE_MODE:
01671       RotateScreenObject (Note.X, Note.Y,
01672                           gui->shift_is_pressed ()? (SWAP_IDENT ?
01673                                                      1 : 3)
01674                           : (SWAP_IDENT ? 3 : 1));
01675       break;
01676 
01677       /* both are almost the same */
01678     case COPY_MODE:
01679     case MOVE_MODE:
01680       switch (Crosshair.AttachedObject.State)
01681         {
01682           /* first notify, lookup object */
01683         case STATE_FIRST:
01684           {
01685             int types = (Settings.Mode == COPY_MODE) ?
01686               COPY_TYPES : MOVE_TYPES;
01687 
01688             Crosshair.AttachedObject.Type =
01689               SearchScreen (Note.X, Note.Y, types,
01690                             &Crosshair.AttachedObject.Ptr1,
01691                             &Crosshair.AttachedObject.Ptr2,
01692                             &Crosshair.AttachedObject.Ptr3);
01693             if (Crosshair.AttachedObject.Type != NO_TYPE)
01694               {
01695                 if (Settings.Mode == MOVE_MODE &&
01696                     TEST_FLAG (LOCKFLAG, (PinType *)
01697                                Crosshair.AttachedObject.Ptr2))
01698                   {
01699                     Message (_("Sorry, the object is locked\n"));
01700                     Crosshair.AttachedObject.Type = NO_TYPE;
01701                   }
01702                 else
01703                   AttachForCopy (Note.X, Note.Y);
01704               }
01705             break;
01706           }
01707 
01708           /* second notify, move or copy object */
01709         case STATE_SECOND:
01710           if (Settings.Mode == COPY_MODE)
01711             CopyObject (Crosshair.AttachedObject.Type,
01712                         Crosshair.AttachedObject.Ptr1,
01713                         Crosshair.AttachedObject.Ptr2,
01714                         Crosshair.AttachedObject.Ptr3,
01715                         Note.X - Crosshair.AttachedObject.X,
01716                         Note.Y - Crosshair.AttachedObject.Y);
01717           else
01718             {
01719               MoveObjectAndRubberband (Crosshair.AttachedObject.Type,
01720                                        Crosshair.AttachedObject.Ptr1,
01721                                        Crosshair.AttachedObject.Ptr2,
01722                                        Crosshair.AttachedObject.Ptr3,
01723                                        Note.X - Crosshair.AttachedObject.X,
01724                                        Note.Y - Crosshair.AttachedObject.Y);
01725               SetLocalRef (0, 0, false);
01726             }
01727           SetChangedFlag (true);
01728 
01729           /* reset identifiers */
01730           Crosshair.AttachedObject.Type = NO_TYPE;
01731           Crosshair.AttachedObject.State = STATE_FIRST;
01732           break;
01733         }
01734       break;
01735 
01736       /* insert a point into a polygon/line/... */
01737     case INSERTPOINT_MODE:
01738       switch (Crosshair.AttachedObject.State)
01739         {
01740           /* first notify, lookup object */
01741         case STATE_FIRST:
01742           Crosshair.AttachedObject.Type =
01743             SearchScreen (Note.X, Note.Y, INSERT_TYPES,
01744                           &Crosshair.AttachedObject.Ptr1,
01745                           &Crosshair.AttachedObject.Ptr2,
01746                           &Crosshair.AttachedObject.Ptr3);
01747 
01748           if (Crosshair.AttachedObject.Type != NO_TYPE)
01749             {
01750               if (TEST_FLAG (LOCKFLAG, (PolygonType *)
01751                              Crosshair.AttachedObject.Ptr2))
01752                 {
01753                   Message (_("Sorry, the object is locked\n"));
01754                   Crosshair.AttachedObject.Type = NO_TYPE;
01755                   break;
01756                 }
01757               else
01758                 {
01759                   /* get starting point of nearest segment */
01760                   if (Crosshair.AttachedObject.Type == POLYGON_TYPE)
01761                     {
01762                       fake.poly =
01763                         (PolygonType *) Crosshair.AttachedObject.Ptr2;
01764                       polyIndex =
01765                         GetLowestDistancePolygonPoint (fake.poly, Note.X,
01766                                                        Note.Y);
01767                       fake.line.Point1 = fake.poly->Points[polyIndex];
01768                       fake.line.Point2 = fake.poly->Points[
01769                           prev_contour_point (fake.poly, polyIndex)];
01770                       Crosshair.AttachedObject.Ptr2 = &fake.line;
01771 
01772                     }
01773                   Crosshair.AttachedObject.State = STATE_SECOND;
01774                   InsertedPoint = *AdjustInsertPoint ();
01775                 }
01776             }
01777           break;
01778 
01779           /* second notify, insert new point into object */
01780         case STATE_SECOND:
01781           if (Crosshair.AttachedObject.Type == POLYGON_TYPE)
01782             InsertPointIntoObject (POLYGON_TYPE,
01783                                    Crosshair.AttachedObject.Ptr1, fake.poly,
01784                                    &polyIndex,
01785                                    InsertedPoint.X, InsertedPoint.Y, false, false);
01786           else
01787             InsertPointIntoObject (Crosshair.AttachedObject.Type,
01788                                    Crosshair.AttachedObject.Ptr1,
01789                                    Crosshair.AttachedObject.Ptr2,
01790                                    &polyIndex,
01791                                    InsertedPoint.X, InsertedPoint.Y, false, false);
01792           SetChangedFlag (true);
01793 
01794           /* reset identifiers */
01795           Crosshair.AttachedObject.Type = NO_TYPE;
01796           Crosshair.AttachedObject.State = STATE_FIRST;
01797           break;
01798         }
01799       break;
01800     }
01801 }
01802 
01803 
01804 /* --------------------------------------------------------------------------- */
01805 
01806 static const char atomic_syntax[] = N_("Atomic(Save|Restore|Close|Block)");
01807 
01808 static const char atomic_help[] = N_("Save or restore the undo serial number.");
01809 
01810 /* %start-doc actions Atomic
01811 
01812 This action allows making multiple-action bindings into an atomic
01813 operation that will be undone by a single Undo command.  For example,
01814 to optimize rat lines, you'd delete the rats and re-add them.  To
01815 group these into a single undo, you'd want the deletions and the
01816 additions to have the same undo serial number.  So, you @code{Save},
01817 delete the rats, @code{Restore}, add the rats - using the same serial
01818 number as the deletes, then @code{Block}, which checks to see if the
01819 deletions or additions actually did anything.  If not, the serial
01820 number is set to the saved number, as there's nothing to undo.  If
01821 something did happen, the serial number is incremented so that these
01822 actions are counted as a single undo step.
01823 
01824 @table @code
01825 
01826 @item Save
01827 Saves the undo serial number.
01828 
01829 @item Restore
01830 Returns it to the last saved number.
01831 
01832 @item Close
01833 Sets it to 1 greater than the last save.
01834 
01835 @item Block
01836 Does a Restore if there was nothing to undo, else does a Close.
01837 
01838 @end table
01839 
01840 %end-doc */
01841 
01842 static int
01843 ActionAtomic (int argc, char **argv, Coord x, Coord y)
01844 {
01845   if (argc != 1)
01846     AFAIL (atomic);
01847 
01848   switch (GetFunctionID (argv[0]))
01849     {
01850     case F_Save:
01851       SaveUndoSerialNumber ();
01852       break;
01853     case F_Restore:
01854       RestoreUndoSerialNumber ();
01855       break;
01856     case F_Close:
01857       RestoreUndoSerialNumber ();
01858       IncrementUndoSerialNumber ();
01859       break;
01860     case F_Block:
01861       RestoreUndoSerialNumber ();
01862       if (Bumped)
01863         IncrementUndoSerialNumber ();
01864       break;
01865     }
01866   return 0;
01867 }
01868 
01869 /* -------------------------------------------------------------------------- */
01870 
01871 static const char drc_syntax[] = N_("DRC()");
01872 
01873 static const char drc_help[] = N_("Invoke the DRC check.");
01874 
01875 /* %start-doc actions DRC
01876 
01877 Note that the design rule check uses the current board rule settings,
01878 not the current style settings.
01879 
01880 %end-doc */
01881 
01882 static int
01883 ActionDRCheck (int argc, char **argv, Coord x, Coord y)
01884 {
01885   int count;
01886 
01887   if (gui->drc_gui == NULL || gui->drc_gui->log_drc_overview)
01888     {
01889       Message (_("%m+Rules are minspace %$mS, minoverlap %$mS "
01890                  "minwidth %$mS, minsilk %$mS\n"
01891                  "min drill %$mS, min annular ring %$mS\n"),
01892                Settings.grid_unit->allow,
01893                PCB->Bloat, PCB->Shrink,
01894                PCB->minWid, PCB->minSlk,
01895                PCB->minDrill, PCB->minRing);
01896     }
01897   count = DRCAll ();
01898   if (gui->drc_gui == NULL || gui->drc_gui->log_drc_overview)
01899     {
01900       if (count == 0)
01901         Message (_("No DRC problems found.\n"));
01902       else if (count > 0)
01903         Message (_("Found %d design rule errors.\n"), count);
01904       else
01905         Message (_("Aborted DRC after %d design rule errors.\n"), -count);
01906     }
01907   return 0;
01908 }
01909 
01910 /* -------------------------------------------------------------------------- */
01911 
01912 static const char dumplibrary_syntax[] = N_("DumpLibrary()");
01913 
01914 static const char dumplibrary_help[] =
01915   N_("Display the entire contents of the libraries.");
01916 
01917 /* %start-doc actions DumpLibrary
01918 
01919 
01920 %end-doc */
01921 
01922 static int
01923 ActionDumpLibrary (int argc, char **argv, Coord x, Coord y)
01924 {
01925   int i, j;
01926 
01927   printf ("**** Do not count on this format.  It will change ****\n\n");
01928   printf ("MenuN   = %d\n", (int) Library.MenuN);
01929   printf ("MenuMax = %d\n", (int) Library.MenuMax);
01930   for (i = 0; i < Library.MenuN; i++)
01931     {
01932       printf ("Library #%d:\n", i);
01933       printf ("    EntryN    = %d\n", (int) Library.Menu[i].EntryN);
01934       printf ("    EntryMax  = %d\n", (int) Library.Menu[i].EntryMax);
01935       printf ("    Name      = \"%s\"\n", UNKNOWN (Library.Menu[i].Name));
01936       printf ("    directory = \"%s\"\n",
01937               UNKNOWN (Library.Menu[i].directory));
01938       printf ("    Style     = \"%s\"\n", UNKNOWN (Library.Menu[i].Style));
01939       printf ("    flag      = %d\n", Library.Menu[i].flag);
01940 
01941       for (j = 0; j < Library.Menu[i].EntryN; j++)
01942         {
01943           printf ("    #%4d: ", j);
01944           if (Library.Menu[i].Entry[j].Template == (char *) -1)
01945             {
01946               printf ("newlib: \"%s\"\n",
01947                       UNKNOWN (Library.Menu[i].Entry[j].ListEntry));
01948             }
01949           else
01950             {
01951               printf ("\"%s\", \"%s\", \"%s\", \"%s\", \"%s\"\n",
01952                       UNKNOWN (Library.Menu[i].Entry[j].ListEntry),
01953                       UNKNOWN (Library.Menu[i].Entry[j].Template),
01954                       UNKNOWN (Library.Menu[i].Entry[j].Package),
01955                       UNKNOWN (Library.Menu[i].Entry[j].Value),
01956                       UNKNOWN (Library.Menu[i].Entry[j].Description));
01957             }
01958         }
01959     }
01960 
01961   return 0;
01962 }
01963 
01964 /* -------------------------------------------------------------------------- */
01965 
01966 static const char flip_syntax[] = N_("Flip(Object|Selected|SelectedElements)");
01967 
01968 static const char flip_help[] =
01969   N_("Flip an element to the opposite side of the board.");
01970 
01971 /* %start-doc actions Flip
01972 
01973 Note that the location of the element will be symmetric about the
01974 cursor location; i.e. if the part you are pointing at will still be at
01975 the same spot once the element is on the other side.  When flipping
01976 multiple elements, this retains their positions relative to each
01977 other, not their absolute positions on the board.
01978 
01979 %end-doc */
01980 
01981 static int
01982 ActionFlip (int argc, char **argv, Coord x, Coord y)
01983 {
01984   char *function = ARG (0);
01985   ElementType *element;
01986   void *ptrtmp;
01987   int err = 0;
01988 
01989   if (function)
01990     {
01991       switch (GetFunctionID (function))
01992         {
01993         case F_Object:
01994           if ((SearchScreen (x, y, ELEMENT_TYPE,
01995                              &ptrtmp, &ptrtmp, &ptrtmp)) != NO_TYPE)
01996             {
01997               element = (ElementType *) ptrtmp;
01998               ChangeElementSide (element, 2 * Crosshair.Y - PCB->MaxHeight);
01999               IncrementUndoSerialNumber ();
02000               Draw ();
02001             }
02002           break;
02003         case F_Selected:
02004         case F_SelectedElements:
02005           ChangeSelectedElementSide ();
02006           break;
02007         default:
02008           err = 1;
02009           break;
02010         }
02011       if (!err)
02012         return 0;
02013     }
02014 
02015   AFAIL (flip);
02016 }
02017 
02018 /* -------------------------------------------------------------------------- */
02019 
02020 static const char message_syntax[] = N_("Message(message)");
02021 
02022 static const char message_help[] = N_("Writes a message to the log window.");
02023 
02024 /* %start-doc actions Message
02025 
02026 This action displays a message to the log window.  This action is primarily
02027 provided for use by other programs which may interface with PCB.  If
02028 multiple arguments are given, each one is sent to the log window
02029 followed by a newline.
02030 
02031 %end-doc */
02032 
02033 static int
02034 ActionMessage (int argc, char **argv, Coord x, Coord y)
02035 {
02036   int i;
02037 
02038   if (argc < 1)
02039     AFAIL (message);
02040 
02041   for (i = 0; i < argc; i++)
02042     {
02043       Message (argv[i]);
02044       Message ("\n");
02045     }
02046 
02047   return 0;
02048 }
02049 
02050 
02051 /* -------------------------------------------------------------------------- */
02052 
02053 static const char setthermal_syntax[] =
02054   "SetThermal(Object|SelectedPins|SelectedVias|Selected, Style)";
02055 
02056 static const char setthermal_help[] =
02057   N_("Set the thermal (on the current layer) of pins or vias to the given style.\n"
02058   "Style = 0 means no thermal.\n"
02059   "Style = 1 has diagonal fingers with sharp edges.\n"
02060   "Style = 2 has horizontal and vertical fingers with sharp edges.\n"
02061   "Style = 3 is a solid connection to the plane.\n"
02062   "Style = 4 has diagonal fingers with rounded edges.\n"
02063   "Style = 5 has horizontal and vertical fingers with rounded edges.\n");
02064 
02065 /* %start-doc actions SetThermal
02066 
02067 This changes how/whether pins or vias connect to any rectangle or polygon
02068 on the current layer. The first argument can specify one object, or all
02069 selected pins, or all selected vias, or all selected pins and vias.
02070 The second argument specifies the style of connection.
02071 There are 5 possibilities:
02072 0 - no connection,
02073 1 - 45 degree fingers with sharp edges,
02074 2 - horizontal & vertical fingers with sharp edges,
02075 3 - solid connection,
02076 4 - 45 degree fingers with rounded corners,
02077 5 - horizontal & vertical fingers with rounded corners.
02078 
02079 Pins and Vias may have thermals whether or not there is a polygon available 
02080 to connect with. However, they will have no effect without the polygon.
02081 %end-doc */
02082 
02083 static int
02084 ActionSetThermal (int argc, char **argv, Coord x, Coord y)
02085 {
02086   char *function = ARG (0);
02087   char *style = ARG (1);
02088   void *ptr1, *ptr2, *ptr3;
02089   int type, kind;
02090   int err = 0;
02091 
02092   if (function && *function)
02093     {
02094       bool absolute;
02095 
02096       if ( ! style || ! *style)
02097         {
02098           kind = PCB->ThermStyle;
02099           absolute = true;
02100         }
02101       else
02102         kind = GetUnitlessValue (style, &absolute);
02103 
02104       /* To allow relative values we could search for the first selected
02105          item and make 'kind' relative to that, but that's not too useful
02106          and requires quite some code. For example there's no
02107          GetFirstSelectedPin() function available. Let's postpone this
02108          functionality, there are more urgent things to do. */
02109 
02110       if (absolute)
02111         switch (GetFunctionID (function))
02112           {
02113           case F_Object:
02114             if ((type =
02115                  SearchScreen (Crosshair.X, Crosshair.Y, CHANGETHERMAL_TYPES,
02116                                &ptr1, &ptr2, &ptr3)) != NO_TYPE)
02117               {
02118                 ChangeObjectThermal (type, ptr1, ptr2, ptr3, kind);
02119                 IncrementUndoSerialNumber ();
02120                 Draw ();
02121               }
02122             break;
02123           case F_SelectedPins:
02124             ChangeSelectedThermals (PIN_TYPE, kind);
02125             break;
02126           case F_SelectedVias:
02127             ChangeSelectedThermals (VIA_TYPE, kind);
02128             break;
02129           case F_Selected:
02130           case F_SelectedElements:
02131             ChangeSelectedThermals (CHANGETHERMAL_TYPES, kind);
02132             break;
02133           default:
02134             err = 1;
02135             break;
02136           }
02137       else
02138         err = 1;
02139     }
02140   else
02141     err = 1;
02142 
02143   if (err)
02144     AFAIL (setthermal);
02145 
02146   return 0;
02147 }
02148 
02155 void
02156 EventMoveCrosshair (int ev_x, int ev_y)
02157 {
02158 #ifdef HAVE_LIBSTROKE
02159   if (mid_stroke)
02160     {
02161       StrokeBox.X2 = ev_x;
02162       StrokeBox.Y2 = ev_y;
02163       stroke_record (ev_x, ev_y);
02164       return;
02165     }
02166 #endif /* HAVE_LIBSTROKE */
02167   if (MoveCrosshairAbsolute (ev_x, ev_y))
02168     {
02169       /* update object position and cursor location */
02170       AdjustAttachedObjects ();
02171       notify_crosshair_change (true);
02172     }
02173 }
02174 
02175 /* --------------------------------------------------------------------------- */
02176 
02177 static const char setvalue_syntax[] =
02178   N_("SetValue(Grid|Line|LineSize|Text|TextScale|ViaDrillingHole|Via|ViaSize, "
02179       "delta)");
02180 
02181 static const char setvalue_help[] =
02182   N_("Change various board-wide values and sizes.");
02183 
02184 /* %start-doc actions SetValue
02185 
02186 @table @code
02187 
02188 @item ViaDrillingHole
02189 Changes the diameter of the drill for new vias.
02190 
02191 @item Grid
02192 Sets the grid spacing.
02193 
02194 @item Line
02195 @item LineSize
02196 Changes the thickness of new lines.
02197 
02198 @item Via
02199 @item ViaSize
02200 Changes the diameter of new vias.
02201 
02202 @item Text
02203 @item TextScale
02204 Changes the size of new text.
02205 
02206 @end table
02207 
02208 %end-doc */
02209 
02210 static int
02211 ActionSetValue (int argc, char **argv, Coord x, Coord y)
02212 {
02213   char *function = ARG (0);
02214   char *val = ARG (1);
02215   char *units = ARG (2);
02216   bool absolute;                        /* flag for 'absolute' value */
02217   double value;
02218   int text_scale;
02219   int err = 0;
02220 
02221   if (function && val)
02222     {
02223       value = GetValue (val, units, &absolute);
02224       switch (GetFunctionID (function))
02225         {
02226         case F_ViaDrillingHole:
02227           SetViaDrillingHole (absolute ? value :
02228                                 value + Settings.ViaDrillingHole,
02229                               false);
02230           hid_action ("RouteStylesChanged");
02231           break;
02232 
02233         case F_Grid:
02234           if (absolute)
02235             SetGrid (value, false);
02236           else
02237             {
02238               if (value == 0)
02239                 value = val[0] == '-' ? -Settings.increments->grid
02240                                       :  Settings.increments->grid;
02241               /* On the way down, short against the minimum 
02242                * PCB drawing unit */
02243               if ((value + PCB->Grid) < 1)
02244                 SetGrid (1, false);
02245               else if (PCB->Grid == 1)
02246                 SetGrid (value, false);
02247               else
02248                 SetGrid (value + PCB->Grid, false);
02249             }
02250           break;
02251 
02252         case F_LineSize:
02253         case F_Line:
02254           if (!absolute && value == 0)
02255             value = val[0] == '-' ? -Settings.increments->line
02256                                   :  Settings.increments->line;
02257           SetLineSize (absolute ? value : value + Settings.LineThickness);
02258           hid_action ("RouteStylesChanged");
02259           break;
02260 
02261         case F_Via:
02262         case F_ViaSize:
02263           SetViaSize (absolute ? value : value + Settings.ViaThickness, false);
02264           hid_action ("RouteStylesChanged");
02265           break;
02266 
02267         case F_Text:
02268         case F_TextScale:
02269           text_scale = value / (double)FONT_CAPHEIGHT * 100.;
02270           if (!absolute)
02271             text_scale += Settings.TextScale;
02272           SetTextScale (text_scale);
02273           break;
02274         default:
02275           err = 1;
02276           break;
02277         }
02278       if (!err)
02279         return 0;
02280     }
02281 
02282   AFAIL (setvalue);
02283 }
02284 
02285 
02286 /* --------------------------------------------------------------------------- */
02287 
02288 static const char quit_syntax[] = N_("Quit()");
02289 
02290 static const char quit_help[] = N_("Quits the application after confirming.");
02291 
02292 /* %start-doc actions Quit
02293 
02294 If you have unsaved changes, you will be prompted to confirm (or
02295 save) before quitting.
02296 
02297 %end-doc */
02298 
02299 static int
02300 ActionQuit (int argc, char **argv, Coord x, Coord y)
02301 {
02302   char *force = ARG (0);
02303   if (force && strcasecmp (force, "force") == 0)
02304     {
02305       PCB->Changed = 0;
02306       exit (0);
02307     }
02308   if (!PCB->Changed || gui->close_confirm_dialog () == HID_CLOSE_CONFIRM_OK)
02309     QuitApplication ();
02310   return 1;
02311 }
02312 
02313 /* --------------------------------------------------------------------------- */
02314 
02315 static const char connection_syntax[] =
02316   N_("Connection(Find|ResetLinesAndPolygons|ResetPinsAndVias|Reset)");
02317 
02318 static const char connection_help[] =
02319   N_("Searches connections of the object at the cursor position.");
02320 
02321 /* %start-doc actions Connection
02322 
02323 Connections found with this action will be highlighted in the
02324 ``connected-color'' color and will have the ``found'' flag set.
02325 
02326 @table @code
02327 
02328 @item Find
02329 The net under the cursor is ``found''.
02330 
02331 @item ResetLinesAndPolygons
02332 Any ``found'' lines and polygons are marked ``not found''.
02333 
02334 @item ResetPinsAndVias
02335 Any ``found'' pins and vias are marked ``not found''.
02336 
02337 @item Reset
02338 All ``found'' objects are marked ``not found''.
02339 
02340 @end table
02341 
02342 %end-doc */
02343 
02344 static int
02345 ActionConnection (int argc, char **argv, Coord x, Coord y)
02346 {
02347   char *function = ARG (0);
02348   if (function)
02349     {
02350       switch (GetFunctionID (function))
02351         {
02352         case F_Find:
02353           {
02354             gui->get_coords (_("Click on a connection"), &x, &y);
02355             LookupConnection (x, y, true, 1, CONNECTEDFLAG, false);
02356             LookupConnection (x, y, true, 1, FOUNDFLAG, true);
02357             break;
02358           }
02359 
02360         case F_ResetLinesAndPolygons:
02361           if (ClearFlagOnLinesAndPolygons (true, CONNECTEDFLAG | FOUNDFLAG))
02362             {
02363               IncrementUndoSerialNumber ();
02364               Draw ();
02365             }
02366           break;
02367 
02368         case F_ResetPinsViasAndPads:
02369           if (ClearFlagOnPinsViasAndPads (true, CONNECTEDFLAG | FOUNDFLAG))
02370             {
02371               IncrementUndoSerialNumber ();
02372               Draw ();
02373             }
02374           break;
02375 
02376         case F_Reset:
02377           if (ClearFlagOnAllObjects (true, CONNECTEDFLAG | FOUNDFLAG))
02378             {
02379               IncrementUndoSerialNumber ();
02380               Draw ();
02381             }
02382           break;
02383         }
02384       return 0;
02385     }
02386 
02387   AFAIL (connection);
02388 }
02389 
02390 /* --------------------------------------------------------------------------- */
02391 
02392 static const char disperseelements_syntax[] =
02393   N_("DisperseElements(All|Selected)");
02394 
02395 static const char disperseelements_help[] = N_("Disperses elements.");
02396 
02397 /* %start-doc actions DisperseElements
02398 
02399 Normally this is used when starting a board, by selecting all elements
02400 and then dispersing them.  This scatters the elements around the board
02401 so that you can pick individual ones, rather than have all the
02402 elements at the same 0,0 coordinate and thus impossible to choose
02403 from.
02404 
02405 %end-doc */
02406 
02407 #define GAP MIL_TO_COORD(100)
02408 
02409 static int
02410 ActionDisperseElements (int argc, char **argv, Coord x, Coord y)
02411 {
02412   char *function = ARG (0);
02413   Coord minx = GAP,
02414     miny = GAP,
02415     maxy = GAP,
02416     dx, dy;
02417   int all = 0, bad = 0;
02418 
02419   if (!function || !*function)
02420     {
02421       bad = 1;
02422     }
02423   else
02424     {
02425       switch (GetFunctionID (function))
02426         {
02427         case F_All:
02428           all = 1;
02429           break;
02430 
02431         case F_Selected:
02432           all = 0;
02433           break;
02434 
02435         default:
02436           bad = 1;
02437         }
02438     }
02439 
02440   if (bad)
02441     {
02442       AFAIL (disperseelements);
02443     }
02444 
02445 
02446   ELEMENT_LOOP (PCB->Data);
02447   {
02448     /* 
02449      * If we want to disperse selected elements, maybe we need smarter
02450      * code here to avoid putting components on top of others which
02451      * are not selected.  For now, I'm assuming that this is typically
02452      * going to be used either with a brand new design or a scratch
02453      * design holding some new components
02454      */
02455     if (!TEST_FLAG (LOCKFLAG, element) && (all || TEST_FLAG (SELECTEDFLAG, element)))
02456       {
02457 
02458         /* figure out how much to move the element */
02459         dx = minx - element->BoundingBox.X1;
02460 
02461         /* snap to the grid */
02462         dx -= (element->MarkX + dx) % PCB->Grid;
02463 
02464         /* 
02465          * and add one grid size so we make sure we always space by GAP or
02466          * more
02467          */
02468         dx += PCB->Grid;
02469 
02470         /* Figure out if this row has room.  If not, start a new row */
02471         if (GAP + element->BoundingBox.X2 + dx > PCB->MaxWidth)
02472           {
02473             miny = maxy + GAP;
02474             minx = GAP;
02475           }
02476 
02477         /* figure out how much to move the element */
02478         dx = minx - element->BoundingBox.X1;
02479         dy = miny - element->BoundingBox.Y1;
02480 
02481         /* snap to the grid */
02482         dx -= (element->MarkX + dx) % PCB->Grid;
02483         dx += PCB->Grid;
02484         dy -= (element->MarkY + dy) % PCB->Grid;
02485         dy += PCB->Grid;
02486 
02487         /* move the element */
02488         MoveElementLowLevel (PCB->Data, element, dx, dy);
02489 
02490         /* and add to the undo list so we can undo this operation */
02491         AddObjectToMoveUndoList (ELEMENT_TYPE, NULL, NULL, element, dx, dy);
02492 
02493         /* keep track of how tall this row is */
02494         minx += element->BoundingBox.X2 - element->BoundingBox.X1 + GAP;
02495         if (maxy < element->BoundingBox.Y2)
02496           {
02497             maxy = element->BoundingBox.Y2;
02498           }
02499       }
02500 
02501   }
02502   END_LOOP;
02503 
02504   /* done with our action so increment the undo # */
02505   IncrementUndoSerialNumber ();
02506 
02507   Redraw ();
02508   SetChangedFlag (true);
02509 
02510   return 0;
02511 }
02512 
02513 #undef GAP
02514 
02515 /* --------------------------------------------------------------------------- */
02516 
02517 static const char display_syntax[] =
02518   N_("Display(NameOnPCB|Description|Value)\n"
02519   "Display(Grid|Redraw)\n"
02520   "Display(CycleClip|CycleCrosshair|Toggle45Degree|ToggleStartDirection)\n"
02521   "Display(ToggleGrid|ToggleRubberBandMode|ToggleUniqueNames)\n"
02522   "Display(ToggleMask|ToggleName|ToggleClearLine|ToggleFullPoly|ToggleSnapPin)\n"
02523   "Display(ToggleThindraw|ToggleThindrawPoly|ToggleOrthoMove|ToggleLocalRef)\n"
02524   "Display(ToggleCheckPlanes|ToggleShowDRC|ToggleAutoDRC)\n"
02525   "Display(ToggleLiveRoute|LockNames|OnlyNames)\n"
02526   "Display(Pinout|PinOrPadName)");
02527 
02528 static const char display_help[] = N_("Several display-related actions.");
02529 
02530 /* %start-doc actions Display
02531 
02532 @table @code
02533 
02534 @item NameOnPCB
02535 @item Description
02536 @item Value
02537 Specify whether all elements show their name, description, or value.
02538 
02539 @item Redraw
02540 Redraw the whole board.
02541 
02542 @item Toggle45Degree
02543 When clear, lines can be drawn at any angle.  When set, lines are
02544 restricted to multiples of 45 degrees and requested lines may be
02545 broken up according to the clip setting.
02546 
02547 @item CycleClip
02548 Changes the way lines are restricted to 45 degree increments.  The
02549 various settings are: straight only, orthogonal then angled, and angled
02550 then orthogonal.  If AllDirections is set, this action disables it.
02551 
02552 @item CycleCrosshair
02553 Changes crosshair drawing.  Crosshair may accept form of 4-ray,
02554 8-ray and 12-ray cross.
02555 
02556 @item ToggleRubberBandMode
02557 If set, moving an object moves all the lines attached to it too.
02558 
02559 @item ToggleStartDirection
02560 If set, each time you set a point in a line, the Clip toggles between
02561 orth-angle and angle-ortho.
02562 
02563 @item ToggleUniqueNames
02564 If set, you will not be permitted to change the name of an element to
02565 match that of another element.
02566 
02567 @item ToggleSnapPin
02568 If set, pin centers and pad end points are treated as additional grid
02569 points that the cursor can snap to.
02570 
02571 @item ToggleLocalRef
02572 If set, the mark is automatically set to the beginning of any move, so
02573 you can see the relative distance you've moved.
02574 
02575 @item ToggleThindraw
02576 If set, objects on the screen are drawn as outlines (lines are drawn
02577 as center-lines).  This lets you see line endpoints hidden under pins,
02578 for example.
02579 
02580 @item ToggleThindrawPoly
02581 If set, polygons on the screen are drawn as outlines.
02582 
02583 @item ToggleShowDRC
02584 If set, pending objects (i.e. lines you're in the process of drawing)
02585 will be drawn with an outline showing how far away from other copper
02586 you need to be.
02587 
02588 @item ToggleLiveRoute
02589 If set, the progress of the autorouter will be visible on the screen.
02590 
02591 @item ToggleAutoDRC
02592 If set, you will not be permitted to make connections which violate
02593 the current DRC and netlist settings.
02594 
02595 @item ToggleCheckPlanes
02596 If set, lines and arcs aren't drawn, which usually leaves just the
02597 polygons.  If you also disable all but the layer you're interested in,
02598 this allows you to check for isolated regions.
02599 
02600 @item ToggleOrthoMove
02601 If set, the crosshair is only allowed to move orthogonally from its
02602 previous position.  I.e. you can move an element or line up, down,
02603 left, or right, but not up+left or down+right.
02604 
02605 @item ToggleName
02606 Selects whether the pinouts show the pin names or the pin numbers.
02607 
02608 @item ToggleLockNames
02609 If set, text will ignore left mouse clicks and actions that work on
02610 objects under the mouse. You can still select text with a lasso (left
02611 mouse drag) and perform actions on the selection.
02612 
02613 @item ToggleOnlyNames
02614 If set, only text will be sensitive for mouse clicks and actions that
02615 work on objects under the mouse. You can still select other objects
02616 with a lasso (left mouse drag) and perform actions on the selection.
02617 
02618 @item ToggleMask
02619 Turns the solder mask on or off.
02620 
02621 @item ToggleClearLine
02622 When set, the clear-line flag causes new lines and arcs to have their
02623 ``clear polygons'' flag set, so they won't be electrically connected
02624 to any polygons they overlap.
02625 
02626 @item ToggleFullPoly
02627 When set, the full-poly flag causes new polygons to have their
02628 ``full polygon'' flag set, so all parts of them will be displayed
02629 instead of only the biggest one.
02630 
02631 @item ToggleGrid
02632 Resets the origin of the current grid to be wherever the mouse pointer
02633 is (not where the crosshair currently is).  If you provide two numbers
02634 after this, the origin is set to that coordinate.
02635 
02636 @item Grid
02637 Toggles whether the grid is displayed or not.
02638 
02639 @item Pinout
02640 Causes the pinout of the element indicated by the cursor to be
02641 displayed, usually in a separate window.
02642 
02643 @item PinOrPadName
02644 Toggles whether the names of pins, pads, or (yes) vias will be
02645 displayed.  If the cursor is over an element, all of its pins and pads
02646 are affected.
02647 
02648 @item ToggleAutoBuriedVias
02649 If set, automatically created vias are buried vias.
02650 
02651 @end table
02652 
02653 %end-doc */
02654 
02655 static enum crosshair_shape
02656 CrosshairShapeIncrement (enum crosshair_shape shape)
02657 {
02658   switch(shape)
02659     {
02660     case Basic_Crosshair_Shape:
02661       shape = Union_Jack_Crosshair_Shape;
02662       break;
02663     case Union_Jack_Crosshair_Shape:
02664       shape = Dozen_Crosshair_Shape;
02665       break;
02666     case Dozen_Crosshair_Shape:
02667       shape = Crosshair_Shapes_Number;
02668       break;
02669     case Crosshair_Shapes_Number:
02670       shape = Basic_Crosshair_Shape;
02671       break;
02672     }
02673   return shape;
02674 }
02675 
02676 static int
02677 ActionDisplay (int argc, char **argv, Coord childX, Coord childY)
02678 {
02679   char *function, *str_dir;
02680   int id;
02681   int err = 0;
02682 
02683   function = ARG (0);
02684   str_dir = ARG (1);
02685 
02686   if (function && (!str_dir || !*str_dir))
02687     {
02688       switch (id = GetFunctionID (function))
02689         {
02690 
02691           /* redraw layout */
02692         case F_ClearAndRedraw:
02693         case F_Redraw:
02694           Redraw ();
02695           break;
02696 
02697           /* change the displayed name of elements */
02698         case F_Value:
02699         case F_NameOnPCB:
02700         case F_Description:
02701           ELEMENT_LOOP (PCB->Data);
02702           {
02703             EraseElementName (element);
02704           }
02705           END_LOOP;
02706           CLEAR_FLAG (DESCRIPTIONFLAG | NAMEONPCBFLAG, PCB);
02707           switch (id)
02708             {
02709             case F_Value:
02710               break;
02711             case F_NameOnPCB:
02712               SET_FLAG (NAMEONPCBFLAG, PCB);
02713               break;
02714             case F_Description:
02715               SET_FLAG (DESCRIPTIONFLAG, PCB);
02716               break;
02717             }
02718           ELEMENT_LOOP (PCB->Data);
02719           {
02720             DrawElementName (element);
02721           }
02722           END_LOOP;
02723           Draw ();
02724           break;
02725 
02726           /* toggle line-adjust flag */
02727         case F_ToggleAllDirections:
02728           TOGGLE_FLAG (ALLDIRECTIONFLAG, PCB);
02729           AdjustAttachedObjects ();
02730           break;
02731 
02732         case F_CycleClip:
02733           notify_crosshair_change (false);
02734           if (TEST_FLAG (ALLDIRECTIONFLAG, PCB))
02735             {
02736               TOGGLE_FLAG (ALLDIRECTIONFLAG, PCB);
02737               PCB->Clipping = 0;
02738             }
02739           else
02740             PCB->Clipping = (PCB->Clipping + 1) % 3;
02741           AdjustAttachedObjects ();
02742           notify_crosshair_change (true);
02743           break;
02744 
02745         case F_CycleCrosshair:
02746           notify_crosshair_change (false);
02747           Crosshair.shape = CrosshairShapeIncrement(Crosshair.shape);
02748           if (Crosshair_Shapes_Number == Crosshair.shape)
02749             Crosshair.shape = Basic_Crosshair_Shape;
02750           notify_crosshair_change (true);
02751           break;
02752 
02753         case F_ToggleRubberBandMode:
02754           notify_crosshair_change (false);
02755           TOGGLE_FLAG (RUBBERBANDFLAG, PCB);
02756           notify_crosshair_change (true);
02757           break;
02758 
02759         case F_ToggleAutoBuriedVias:
02760           notify_crosshair_change (false);
02761           TOGGLE_FLAG (AUTOBURIEDVIASFLAG, PCB);
02762           notify_crosshair_change (true);
02763           break;
02764 
02765         case F_ToggleStartDirection:
02766           notify_crosshair_change (false);
02767           TOGGLE_FLAG (SWAPSTARTDIRFLAG, PCB);
02768           notify_crosshair_change (true);
02769           break;
02770 
02771         case F_ToggleUniqueNames:
02772           TOGGLE_FLAG (UNIQUENAMEFLAG, PCB);
02773           break;
02774 
02775         case F_ToggleSnapPin:
02776           notify_crosshair_change (false);
02777           TOGGLE_FLAG (SNAPPINFLAG, PCB);
02778           notify_crosshair_change (true);
02779           break;
02780 
02781         case F_ToggleLocalRef:
02782           TOGGLE_FLAG (LOCALREFFLAG, PCB);
02783           break;
02784 
02785         case F_ToggleThindraw:
02786           TOGGLE_FLAG (THINDRAWFLAG, PCB);
02787           Redraw ();
02788           break;
02789 
02790         case F_ToggleThindrawPoly:
02791           TOGGLE_FLAG (THINDRAWPOLYFLAG, PCB);
02792           Redraw ();
02793           break;
02794 
02795         case F_ToggleLockNames:
02796           TOGGLE_FLAG (LOCKNAMESFLAG, PCB);
02797           CLEAR_FLAG (ONLYNAMESFLAG, PCB);
02798           break;
02799 
02800         case F_ToggleOnlyNames:
02801           TOGGLE_FLAG (ONLYNAMESFLAG, PCB);
02802           CLEAR_FLAG (LOCKNAMESFLAG, PCB);
02803           break;
02804 
02805         case F_ToggleHideNames:
02806           TOGGLE_FLAG (HIDENAMESFLAG, PCB);
02807           Redraw ();
02808           break;
02809 
02810         case F_ToggleShowDRC:
02811           TOGGLE_FLAG (SHOWDRCFLAG, PCB);
02812           break;
02813 
02814         case F_ToggleLiveRoute:
02815           TOGGLE_FLAG (LIVEROUTEFLAG, PCB);
02816           break;
02817 
02818         case F_ToggleAutoDRC:
02819           notify_crosshair_change (false);
02820           TOGGLE_FLAG (AUTODRCFLAG, PCB);
02821           if (TEST_FLAG (AUTODRCFLAG, PCB) && Settings.Mode == LINE_MODE)
02822             {
02823               if (ClearFlagOnAllObjects (true, CONNECTEDFLAG | FOUNDFLAG))
02824                 {
02825                   IncrementUndoSerialNumber ();
02826                   Draw ();
02827                 }
02828               if (Crosshair.AttachedLine.State != STATE_FIRST)
02829                 {
02830                   LookupConnection (Crosshair.AttachedLine.Point1.X,
02831                                     Crosshair.AttachedLine.Point1.Y,
02832                                     true, 1, CONNECTEDFLAG, false);
02833                   LookupConnection (Crosshair.AttachedLine.Point1.X,
02834                                     Crosshair.AttachedLine.Point1.Y,
02835                                     true, 1, FOUNDFLAG, true);
02836                 }
02837             }
02838           notify_crosshair_change (true);
02839           break;
02840 
02841         case F_ToggleCheckPlanes:
02842           TOGGLE_FLAG (CHECKPLANESFLAG, PCB);
02843           Redraw ();
02844           break;
02845 
02846         case F_ToggleOrthoMove:
02847           TOGGLE_FLAG (ORTHOMOVEFLAG, PCB);
02848           break;
02849 
02850         case F_ToggleName:
02851           TOGGLE_FLAG (SHOWNUMBERFLAG, PCB);
02852           Redraw ();
02853           break;
02854 
02855         case F_ToggleMask:
02856           TOGGLE_FLAG (SHOWMASKFLAG, PCB);
02857           Redraw ();
02858           break;
02859 
02860         case F_ToggleClearLine:
02861           TOGGLE_FLAG (CLEARNEWFLAG, PCB);
02862           break;
02863 
02864         case F_ToggleFullPoly:
02865           TOGGLE_FLAG (NEWFULLPOLYFLAG, PCB);
02866           break;
02867 
02868           /* shift grid alignment */
02869         case F_ToggleGrid:
02870           {
02871             Coord oldGrid = PCB->Grid;
02872 
02873             PCB->Grid = 1;
02874             if (MoveCrosshairAbsolute (Crosshair.X, Crosshair.Y))
02875               notify_crosshair_change (true);   /* first notify was in MoveCrosshairAbs */
02876             SetGrid (oldGrid, true);
02877           }
02878           break;
02879 
02880           /* toggle displaying of the grid */
02881         case F_Grid:
02882           Settings.DrawGrid = !Settings.DrawGrid;
02883           Redraw ();
02884           break;
02885 
02886           /* display the pinout of an element */
02887         case F_Pinout:
02888           {
02889             ElementType *element;
02890             void *ptrtmp;
02891             Coord x, y;
02892 
02893             gui->get_coords (_("Click on an element"), &x, &y);
02894             if ((SearchScreen
02895                  (x, y, ELEMENT_TYPE, &ptrtmp,
02896                   &ptrtmp, &ptrtmp)) != NO_TYPE)
02897               {
02898                 element = (ElementType *) ptrtmp;
02899                 gui->show_item (element);
02900               }
02901             break;
02902           }
02903 
02904           /* toggle displaying of pin/pad/via names */
02905         case F_PinOrPadName:
02906           {
02907             void *ptr1, *ptr2, *ptr3;
02908             Coord x, y;
02909 
02910             gui->get_coords(_("Click on an element"), &x, &y);
02911 
02912             switch (SearchScreen (x, y,
02913                                   ELEMENT_TYPE | PIN_TYPE | PAD_TYPE |
02914                                   VIA_TYPE, (void **) &ptr1, (void **) &ptr2,
02915                                   (void **) &ptr3))
02916               {
02917               case ELEMENT_TYPE:
02918                 PIN_LOOP ((ElementType *) ptr1);
02919                 {
02920                   if (TEST_FLAG (DISPLAYNAMEFLAG, pin))
02921                     ErasePinName (pin);
02922                   else
02923                     DrawPinName (pin);
02924                   AddObjectToFlagUndoList (PIN_TYPE, ptr1, pin, pin);
02925                   TOGGLE_FLAG (DISPLAYNAMEFLAG, pin);
02926                 }
02927                 END_LOOP;
02928                 PAD_LOOP ((ElementType *) ptr1);
02929                 {
02930                   if (TEST_FLAG (DISPLAYNAMEFLAG, pad))
02931                     ErasePadName (pad);
02932                   else
02933                     DrawPadName (pad);
02934                   AddObjectToFlagUndoList (PAD_TYPE, ptr1, pad, pad);
02935                   TOGGLE_FLAG (DISPLAYNAMEFLAG, pad);
02936                 }
02937                 END_LOOP;
02938                 SetChangedFlag (true);
02939                 IncrementUndoSerialNumber ();
02940                 Draw ();
02941                 break;
02942 
02943               case PIN_TYPE:
02944                 if (TEST_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2))
02945                   ErasePinName ((PinType *) ptr2);
02946                 else
02947                   DrawPinName ((PinType *) ptr2);
02948                 AddObjectToFlagUndoList (PIN_TYPE, ptr1, ptr2, ptr3);
02949                 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2);
02950                 SetChangedFlag (true);
02951                 IncrementUndoSerialNumber ();
02952                 Draw ();
02953                 break;
02954 
02955               case PAD_TYPE:
02956                 if (TEST_FLAG (DISPLAYNAMEFLAG, (PadType *) ptr2))
02957                   ErasePadName ((PadType *) ptr2);
02958                 else
02959                   DrawPadName ((PadType *) ptr2);
02960                 AddObjectToFlagUndoList (PAD_TYPE, ptr1, ptr2, ptr3);
02961                 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PadType *) ptr2);
02962                 SetChangedFlag (true);
02963                 IncrementUndoSerialNumber ();
02964                 Draw ();
02965                 break;
02966               case VIA_TYPE:
02967                 if (TEST_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2))
02968                   EraseViaName ((PinType *) ptr2);
02969                 else
02970                   DrawViaName ((PinType *) ptr2);
02971                 AddObjectToFlagUndoList (VIA_TYPE, ptr1, ptr2, ptr3);
02972                 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2);
02973                 SetChangedFlag (true);
02974                 IncrementUndoSerialNumber ();
02975                 Draw ();
02976                 break;
02977               }
02978             break;
02979           }
02980         default:
02981           err = 1;
02982         }
02983     }
02984   else if (function && str_dir)
02985     {
02986       switch (GetFunctionID (function))
02987         {
02988         case F_ToggleGrid:
02989           if (argc > 2)
02990             {
02991               PCB->GridOffsetX = GetValue (argv[1], NULL, NULL);
02992               PCB->GridOffsetY = GetValue (argv[2], NULL, NULL);
02993               if (Settings.DrawGrid)
02994                 Redraw ();
02995             }
02996           break;
02997 
02998         default:
02999           err = 1;
03000           break;
03001         }
03002     }
03003   else
03004     err = 1;
03005 
03006   if (err)
03007     AFAIL (display);
03008 
03009   return 0;
03010 }
03011 
03012 /* --------------------------------------------------------------------------- */
03013 
03014 static const char mode_syntax[] =
03015   N_("Mode(Arc|Arrow|Copy|InsertPoint|Line|Lock|Move|None|PasteBuffer)\n"
03016   "Mode(Polygon|Rectangle|Remove|Rotate|Text|Thermal|Via)\n"
03017   "Mode(Notify|Release|Cancel|Stroke)\n"
03018   "Mode(Save|Restore)");
03019 
03020 static const char mode_help[] = N_("Change or use the tool mode.");
03021 
03022 /* %start-doc actions Mode
03023 
03024 @table @code
03025 
03026 @item Arc
03027 @itemx Arrow
03028 @itemx Copy
03029 @itemx InsertPoint
03030 @itemx Line
03031 @itemx Lock
03032 @itemx Move
03033 @itemx None
03034 @itemx PasteBuffer
03035 @itemx Polygon
03036 @itemx Rectangle
03037 @itemx Remove
03038 @itemx Rotate
03039 @itemx Text
03040 @itemx Thermal
03041 @itemx Via
03042 Select the indicated tool.
03043 
03044 @item Notify
03045 Called when you press the mouse button, or move the mouse.
03046 
03047 @item Release
03048 Called when you release the mouse button.
03049 
03050 @item Cancel
03051 Cancels any pending tool activity, allowing you to restart elsewhere.
03052 For example, this allows you to start a new line rather than attach a
03053 line to the previous line.
03054 
03055 @item Escape
03056 Similar to Cancel but calling this action a second time will return
03057 to the Arrow tool.
03058 
03059 @item Stroke
03060 If your @code{pcb} was built with libstroke, this invokes the stroke
03061 input method.  If not, this will restart a drawing mode if you were
03062 drawing, else it will select objects.
03063 
03064 @item Save
03065 Remembers the current tool.
03066 
03067 @item Restore
03068 Restores the tool to the last saved tool.
03069 
03070 @end table
03071 
03072 %end-doc */
03073 
03074 static int
03075 ActionMode (int argc, char **argv, Coord x, Coord y)
03076 {
03077   char *function = ARG (0);
03078 
03079   if (function)
03080     {
03081       Note.X = Crosshair.X;
03082       Note.Y = Crosshair.Y;
03083       notify_crosshair_change (false);
03084       switch (GetFunctionID (function))
03085         {
03086         case F_Arc:
03087           SetMode (ARC_MODE);
03088           break;
03089         case F_Arrow:
03090           SetMode (ARROW_MODE);
03091           break;
03092         case F_Copy:
03093           SetMode (COPY_MODE);
03094           break;
03095         case F_InsertPoint:
03096           SetMode (INSERTPOINT_MODE);
03097           break;
03098         case F_Line:
03099           SetMode (LINE_MODE);
03100           break;
03101         case F_Lock:
03102           SetMode (LOCK_MODE);
03103           break;
03104         case F_Move:
03105           SetMode (MOVE_MODE);
03106           break;
03107         case F_None:
03108           SetMode (NO_MODE);
03109           break;
03110         case F_Cancel:
03111           {
03112             int saved_mode = Settings.Mode;
03113             SetMode (NO_MODE);
03114             SetMode (saved_mode);
03115           }
03116           break;
03117         case F_Escape:
03118           {
03119             switch (Settings.Mode)
03120               {
03121               case VIA_MODE:
03122               case PASTEBUFFER_MODE:
03123               case TEXT_MODE:
03124               case ROTATE_MODE:
03125               case REMOVE_MODE:
03126               case MOVE_MODE:
03127               case COPY_MODE:
03128               case INSERTPOINT_MODE:
03129               case RUBBERBANDMOVE_MODE:
03130               case THERMAL_MODE:
03131               case LOCK_MODE:
03132                 SetMode (NO_MODE);
03133                 SetMode (ARROW_MODE);
03134                 break;
03135 
03136               case LINE_MODE:
03137                 if (Crosshair.AttachedLine.State == STATE_FIRST)
03138                   SetMode (ARROW_MODE);
03139                 else
03140                   {
03141                     SetMode (NO_MODE);
03142                     SetMode (LINE_MODE);
03143                   }
03144                 break;
03145 
03146               case RECTANGLE_MODE:
03147                 if (Crosshair.AttachedBox.State == STATE_FIRST)
03148                   SetMode (ARROW_MODE);
03149                 else
03150                   {
03151                     SetMode (NO_MODE);
03152                     SetMode (RECTANGLE_MODE);
03153                   }
03154                 break;
03155           
03156               case POLYGON_MODE:
03157                 if (Crosshair.AttachedLine.State == STATE_FIRST)
03158                   SetMode (ARROW_MODE);
03159                 else
03160                   {
03161                     SetMode (NO_MODE);
03162                     SetMode (POLYGON_MODE);
03163                   }
03164                 break;
03165 
03166               case POLYGONHOLE_MODE:
03167                 if (Crosshair.AttachedLine.State == STATE_FIRST)
03168                   SetMode (ARROW_MODE);
03169                 else
03170                   {
03171                     SetMode (NO_MODE);
03172                     SetMode (POLYGONHOLE_MODE);
03173                   }
03174                 break;
03175 
03176               case ARC_MODE:
03177                 if (Crosshair.AttachedBox.State == STATE_FIRST)
03178                   SetMode (ARROW_MODE);
03179                 else
03180                   {
03181                     SetMode (NO_MODE);
03182                     SetMode (ARC_MODE);
03183                   }
03184                 break;
03185                 
03186               case ARROW_MODE:
03187                 break;
03188                 
03189               default:
03190                 break;
03191               }
03192           }
03193           break;
03194           
03195         case F_Notify:
03196           NotifyMode ();
03197           break;
03198         case F_PasteBuffer:
03199           SetMode (PASTEBUFFER_MODE);
03200           break;
03201         case F_Polygon:
03202           SetMode (POLYGON_MODE);
03203           break;
03204         case F_PolygonHole:
03205           SetMode (POLYGONHOLE_MODE);
03206           break;
03207 #ifndef HAVE_LIBSTROKE
03208         case F_Release:
03209           ReleaseMode ();
03210           break;
03211 #else
03212         case F_Release:
03213           if (mid_stroke)
03214             FinishStroke ();
03215           else
03216             ReleaseMode ();
03217           break;
03218 #endif
03219         case F_Remove:
03220           SetMode (REMOVE_MODE);
03221           break;
03222         case F_Rectangle:
03223           SetMode (RECTANGLE_MODE);
03224           break;
03225         case F_Rotate:
03226           SetMode (ROTATE_MODE);
03227           break;
03228         case F_Stroke:
03229 #ifdef HAVE_LIBSTROKE
03230           mid_stroke = true;
03231           StrokeBox.X1 = Crosshair.X;
03232           StrokeBox.Y1 = Crosshair.Y;
03233           break;
03234 #else
03235           /* Handle middle mouse button restarts of drawing mode.  If not in
03236              |  a drawing mode, middle mouse button will select objects.
03237            */
03238           if (Settings.Mode == LINE_MODE
03239               && Crosshair.AttachedLine.State != STATE_FIRST)
03240             {
03241               SetMode (LINE_MODE);
03242             }
03243           else if (Settings.Mode == ARC_MODE
03244                    && Crosshair.AttachedBox.State != STATE_FIRST)
03245             SetMode (ARC_MODE);
03246           else if (Settings.Mode == RECTANGLE_MODE
03247                    && Crosshair.AttachedBox.State != STATE_FIRST)
03248             SetMode (RECTANGLE_MODE);
03249           else if (Settings.Mode == POLYGON_MODE
03250                    && Crosshair.AttachedLine.State != STATE_FIRST)
03251             SetMode (POLYGON_MODE);
03252           else
03253             {
03254               SaveMode ();
03255               saved_mode = true;
03256               SetMode (ARROW_MODE);
03257               NotifyMode ();
03258             }
03259           break;
03260 #endif
03261         case F_Text:
03262           SetMode (TEXT_MODE);
03263           break;
03264         case F_Thermal:
03265           SetMode (THERMAL_MODE);
03266           break;
03267         case F_Via:
03268           SetMode (VIA_MODE);
03269           break;
03270 
03271         case F_Restore: /* restore the last saved mode */
03272           RestoreMode ();
03273           break;
03274 
03275         case F_Save:            /* save currently selected mode */
03276           SaveMode ();
03277           break;
03278         }
03279       notify_crosshair_change (true);
03280       return 0;
03281     }
03282 
03283   AFAIL (mode);
03284 }
03285 
03286 /* --------------------------------------------------------------------------- */
03287 
03288 static const char removeselected_syntax[] = N_("RemoveSelected()");
03289 
03290 static const char removeselected_help[] = N_("Removes any selected objects.");
03291 
03292 /* %start-doc actions RemoveSelected
03293 
03294 %end-doc */
03295 
03296 static int
03297 ActionRemoveSelected (int argc, char **argv, Coord x, Coord y)
03298 {
03299   if (RemoveSelected ())
03300     SetChangedFlag (true);
03301   return 0;
03302 }
03303 
03304 /* --------------------------------------------------------------------------- */
03305 
03306 static const char renumber_syntax[] = N_("Renumber()\n"
03307                                       "Renumber(filename)");
03308 
03309 static const char renumber_help[] =
03310   N_("Renumber all elements.  The changes will be recorded to filename\n"
03311   "for use in backannotating these changes to the schematic.");
03312 
03313 /* %start-doc actions Renumber
03314 
03315 %end-doc */
03316 
03317 static int
03318 ActionRenumber (int argc, char **argv, Coord x, Coord y)
03319 {
03320   bool changed = false;
03321   ElementType **element_list;
03322   ElementType **locked_element_list;
03323   unsigned int i, j, k, cnt, lock_cnt;
03324   unsigned int tmpi;
03325   size_t sz;
03326   char *tmps;
03327   char *name;
03328   FILE *out;
03329   static char * default_file = NULL;
03330   size_t cnt_list_sz = 100;
03331   struct _cnt_list
03332   {
03333     char *name;
03334     unsigned int cnt;
03335   } *cnt_list;
03336   char **was, **is, *pin;
03337   unsigned int c_cnt = 0;
03338   int unique, ok;
03339   int free_name = 0;
03340 
03341   if (argc < 1)
03342     {
03343       /*
03344        * We deal with the case where name already exists in this
03345        * function so the GUI doesn't need to deal with it 
03346        */
03347       name = gui->fileselect (_("Save Renumber Annotation File As ..."),
03348                               _("Choose a file to record the renumbering to.\n"
03349                                 "This file may be used to back annotate the\n"
03350                                 "change to the schematics.\n"),
03351                               default_file, ".eco", "eco",
03352                               0);
03353 
03354       free_name = 1;
03355     }
03356   else
03357     name = argv[0];
03358 
03359   if (default_file)
03360     {
03361       free (default_file);
03362       default_file = NULL;
03363     }
03364 
03365   if (name && *name)
03366     {
03367       default_file = strdup (name);
03368     }
03369 
03370   if ((out = fopen (name, "r")))
03371     {
03372       fclose (out);
03373       if (!gui->confirm_dialog (_("File exists!  Ok to overwrite?"), 0))
03374         {
03375           if (free_name && name)
03376             free (name);
03377           return 0;
03378         }
03379     }
03380 
03381   if ((out = fopen (name, "w")) == NULL)
03382     {
03383       Message (_("Could not open %s\n"), name);
03384       if (free_name && name)
03385         free (name);
03386       return 1;
03387     }
03388   
03389   if (free_name && name)
03390     free (name);
03391 
03392   fprintf (out, "*COMMENT* PCB Annotation File\n");
03393   fprintf (out, "*FILEVERSION* 20061031\n");
03394 
03395   /*
03396    * Make a first pass through all of the elements and sort them out
03397    * by location on the board.  While here we also collect a list of
03398    * locked elements.
03399    *
03400    * We'll actually renumber things in the 2nd pass.
03401    */
03402   element_list = (ElementType **)calloc (PCB->Data->ElementN, sizeof (ElementType *));
03403   locked_element_list = (ElementType **)calloc (PCB->Data->ElementN, sizeof (ElementType *));
03404   was = (char **)calloc (PCB->Data->ElementN, sizeof (char *));
03405   is = (char **)calloc (PCB->Data->ElementN, sizeof (char *));
03406   if (element_list == NULL || locked_element_list == NULL || was == NULL
03407       || is == NULL)
03408     {
03409       fprintf (stderr, "calloc() failed in %s\n", __FUNCTION__);
03410       exit (1);
03411     }
03412 
03413 
03414   cnt = 0;
03415   lock_cnt = 0;
03416   ELEMENT_LOOP (PCB->Data);
03417   {
03418     if (TEST_FLAG (LOCKFLAG, element->Name) || TEST_FLAG (LOCKFLAG, element))
03419       {
03420         /* 
03421          * add to the list of locked elements which we won't try to
03422          * renumber and whose reference designators are now reserved.
03423          */
03424         pcb_fprintf (out,
03425                      "*WARN* Element \"%s\" at %$md is locked and will not be renumbered.\n",
03426                       UNKNOWN (NAMEONPCB_NAME (element)), element->MarkX, element->MarkY);
03427         locked_element_list[lock_cnt] = element;
03428         lock_cnt++;
03429       }
03430 
03431     else
03432       {
03433         /* count of devices which will be renumbered */
03434         cnt++;
03435 
03436         /* search for correct position in the list */
03437         i = 0;
03438         while (element_list[i] && element->MarkY > element_list[i]->MarkY)
03439           i++;
03440 
03441         /* 
03442          * We have found the position where we have the first element that
03443          * has the same Y value or a lower Y value.  Now move forward if
03444          * needed through the X values
03445          */
03446         while (element_list[i]
03447                && element->MarkY == element_list[i]->MarkY
03448                && element->MarkX > element_list[i]->MarkX)
03449           i++;
03450 
03451         for (j = cnt - 1; j > i; j--)
03452           {
03453             element_list[j] = element_list[j - 1];
03454           }
03455         element_list[i] = element;
03456       }
03457   }
03458   END_LOOP;
03459 
03460 
03461   /* 
03462    * Now that the elements are sorted by board position, we go through
03463    * and renumber them.
03464    */
03465 
03466   /* 
03467    * turn off the flag which requires unique names so it doesn't get
03468    * in our way.  When we're done with the renumber we will have unique
03469    * names.
03470    */
03471   unique = TEST_FLAG (UNIQUENAMEFLAG, PCB);
03472   CLEAR_FLAG (UNIQUENAMEFLAG, PCB);
03473 
03474   cnt_list = (struct _cnt_list *)calloc (cnt_list_sz, sizeof (struct _cnt_list));
03475   for (i = 0; i < cnt; i++)
03476     {
03477       /* If there is no refdes, maybe just spit out a warning */
03478       if (NAMEONPCB_NAME (element_list[i]))
03479         {
03480           /* figure out the prefix */
03481           tmps = strdup (NAMEONPCB_NAME (element_list[i]));
03482           j = 0;
03483           while (tmps[j] && (tmps[j] < '0' || tmps[j] > '9')
03484                  && tmps[j] != '?')
03485             j++;
03486           tmps[j] = '\0';
03487 
03488           /* check the counter for this prefix */
03489           for (j = 0;
03490                cnt_list[j].name && (strcmp (cnt_list[j].name, tmps) != 0)
03491                && j < cnt_list_sz; j++);
03492 
03493           /* grow the list if needed */
03494           if (j == cnt_list_sz)
03495             {
03496               cnt_list_sz += 100;
03497               cnt_list = (struct _cnt_list *)realloc (cnt_list, cnt_list_sz);
03498               if (cnt_list == NULL)
03499                 {
03500                   fprintf (stderr, _("realloc() failed in %s()\n"), __FUNCTION__);
03501                   exit (1);
03502                 }
03503               /* zero out the memory that we added */
03504               for (tmpi = j; tmpi < cnt_list_sz; tmpi++)
03505                 {
03506                   cnt_list[tmpi].name = NULL;
03507                   cnt_list[tmpi].cnt = 0;
03508                 }
03509             }
03510 
03511           /* 
03512            * start a new counter if we don't have a counter for this
03513            * prefix 
03514            */
03515           if (!cnt_list[j].name)
03516             {
03517               cnt_list[j].name = strdup (tmps);
03518               cnt_list[j].cnt = 0;
03519             }
03520 
03521           /*
03522            * check to see if the new refdes is already used by a
03523            * locked element
03524            */
03525           do
03526             {
03527               ok = 1;
03528               cnt_list[j].cnt++;
03529               free (tmps);
03530 
03531               /* space for the prefix plus 1 digit plus the '\0' */
03532               sz = strlen (cnt_list[j].name) + 2;
03533 
03534               /* and 1 more per extra digit needed to hold the number */
03535               tmpi = cnt_list[j].cnt;
03536               while (tmpi > 10)
03537                 {
03538                   sz++;
03539                   tmpi = tmpi / 10;
03540                 }
03541               tmps = (char *)malloc (sz * sizeof (char));
03542               sprintf (tmps, "%s%d", cnt_list[j].name, (int) cnt_list[j].cnt);
03543 
03544               /* 
03545                * now compare to the list of reserved (by locked
03546                * elements) names 
03547                */
03548               for (k = 0; k < lock_cnt; k++)
03549                 {
03550                   if (strcmp
03551                       (UNKNOWN (NAMEONPCB_NAME (locked_element_list[k])),
03552                        tmps) == 0)
03553                     {
03554                       ok = 0;
03555                       break;
03556                     }
03557                 }
03558 
03559             }
03560           while (!ok);
03561 
03562           if (strcmp (tmps, NAMEONPCB_NAME (element_list[i])) != 0)
03563             {
03564               fprintf (out, "*RENAME* \"%s\" \"%s\"\n",
03565                        NAMEONPCB_NAME (element_list[i]), tmps);
03566 
03567               /* add this rename to our table of renames so we can update the netlist */
03568               was[c_cnt] = strdup (NAMEONPCB_NAME (element_list[i]));
03569               is[c_cnt] = strdup (tmps);
03570               c_cnt++;
03571 
03572               AddObjectToChangeNameUndoList (ELEMENT_TYPE, NULL, NULL,
03573                                              element_list[i],
03574                                              NAMEONPCB_NAME (element_list
03575                                                              [i]));
03576 
03577               ChangeObjectName (ELEMENT_TYPE, element_list[i], NULL, NULL,
03578                                 tmps);
03579               changed = true;
03580 
03581               /* we don't free tmps in this case because it is used */
03582             }
03583           else
03584             free (tmps);
03585         }
03586       else
03587         {
03588           pcb_fprintf (out, "*WARN* Element at %$md has no name.\n",
03589                    element_list[i]->MarkX, element_list[i]->MarkY);
03590         }
03591 
03592     }
03593 
03594   fclose (out);
03595 
03596   /* restore the unique flag setting */
03597   if (unique)
03598     SET_FLAG (UNIQUENAMEFLAG, PCB);
03599 
03600   if (changed)
03601     {
03602 
03603       /* update the netlist */
03604       AddNetlistLibToUndoList (&(PCB->NetlistLib));
03605 
03606       /* iterate over each net */
03607       for (i = 0; i < PCB->NetlistLib.MenuN; i++)
03608         {
03609 
03610           /* iterate over each pin on the net */
03611           for (j = 0; j < PCB->NetlistLib.Menu[i].EntryN; j++)
03612             {
03613 
03614               /* figure out the pin number part from strings like U3-21 */
03615               tmps = strdup (PCB->NetlistLib.Menu[i].Entry[j].ListEntry);
03616               for (k = 0; tmps[k] && tmps[k] != '-'; k++);
03617               tmps[k] = '\0';
03618               pin = tmps + k + 1;
03619 
03620               /* iterate over the list of changed reference designators */
03621               for (k = 0; k < c_cnt; k++)
03622                 {
03623                   /*
03624                    * if the pin needs to change, change it and quit
03625                    * searching in the list. 
03626                    */
03627                   if (strcmp (tmps, was[k]) == 0)
03628                     {
03629                       free (PCB->NetlistLib.Menu[i].Entry[j].ListEntry);
03630                       PCB->NetlistLib.Menu[i].Entry[j].ListEntry =
03631                         (char *)malloc ((strlen (is[k]) + strlen (pin) +
03632                                  2) * sizeof (char));
03633                       sprintf (PCB->NetlistLib.Menu[i].Entry[j].ListEntry,
03634                                "%s-%s", is[k], pin);
03635                       k = c_cnt;
03636                     }
03637 
03638                 }
03639               free (tmps);
03640             }
03641         }
03642       for (k = 0; k < c_cnt; k++)
03643         {
03644           free (was[k]);
03645           free (is[k]);
03646         }
03647 
03648       NetlistChanged (0);
03649       IncrementUndoSerialNumber ();
03650       SetChangedFlag (true);
03651     }
03652 
03653   free (locked_element_list);
03654   free (element_list);
03655   free (cnt_list);
03656   free (is);
03657   free (was);
03658   return 0;
03659 }
03660 
03661 
03662 /* --------------------------------------------------------------------------- */
03663 
03664 static const char ripup_syntax[] = N_("RipUp(All|Selected|Element)");
03665 
03666 static const char ripup_help[] =
03667   N_("Ripup auto-routed tracks, or convert an element to parts.");
03668 
03669 /* %start-doc actions RipUp
03670 
03671 @table @code
03672 
03673 @item All
03674 Removes all lines and vias which were created by the autorouter.
03675 
03676 @item Selected
03677 Removes all selected lines and vias which were created by the
03678 autorouter.
03679 
03680 @item Element
03681 Converts the element under the cursor to parts (vias and lines).  Note
03682 that this uses the highest numbered paste buffer.
03683 
03684 @end table
03685 
03686 %end-doc */
03687 
03688 static int
03689 ActionRipUp (int argc, char **argv, Coord x, Coord y)
03690 {
03691   char *function = ARG (0);
03692   bool changed = false;
03693 
03694   if (function)
03695     {
03696       switch (GetFunctionID (function))
03697         {
03698         case F_All:
03699           ALLLINE_LOOP (PCB->Data);
03700           {
03701             if (TEST_FLAG (AUTOFLAG, line) && !TEST_FLAG (LOCKFLAG, line))
03702               {
03703                 RemoveObject (LINE_TYPE, layer, line, line);
03704                 changed = true;
03705               }
03706           }
03707           ENDALL_LOOP;
03708           ALLARC_LOOP (PCB->Data);
03709           {
03710             if (TEST_FLAG (AUTOFLAG, arc) && !TEST_FLAG (LOCKFLAG, arc))
03711               {
03712                 RemoveObject (ARC_TYPE, layer, arc, arc);
03713                 changed = true;
03714               }
03715           }
03716           ENDALL_LOOP;
03717           VIA_LOOP (PCB->Data);
03718           {
03719             if (TEST_FLAG (AUTOFLAG, via) && !TEST_FLAG (LOCKFLAG, via))
03720               {
03721                 RemoveObject (VIA_TYPE, via, via, via);
03722                 changed = true;
03723               }
03724           }
03725           END_LOOP;
03726 
03727           if (changed)
03728             {
03729               IncrementUndoSerialNumber ();
03730               SetChangedFlag (true);
03731             }
03732           break;
03733         case F_Selected:
03734           VISIBLELINE_LOOP (PCB->Data);
03735           {
03736             if (TEST_FLAGS (AUTOFLAG | SELECTEDFLAG, line)
03737                 && !TEST_FLAG (LOCKFLAG, line))
03738               {
03739                 RemoveObject (LINE_TYPE, layer, line, line);
03740                 changed = true;
03741               }
03742           }
03743           ENDALL_LOOP;
03744           if (PCB->ViaOn)
03745             VIA_LOOP (PCB->Data);
03746           {
03747             if (TEST_FLAGS (AUTOFLAG | SELECTEDFLAG, via)
03748                 && !TEST_FLAG (LOCKFLAG, via))
03749               {
03750                 RemoveObject (VIA_TYPE, via, via, via);
03751                 changed = true;
03752               }
03753           }
03754           END_LOOP;
03755           if (changed)
03756             {
03757               IncrementUndoSerialNumber ();
03758               SetChangedFlag (true);
03759             }
03760           break;
03761         case F_Element:
03762           {
03763             void *ptr1, *ptr2, *ptr3;
03764 
03765             if (SearchScreen (Crosshair.X, Crosshair.Y, ELEMENT_TYPE,
03766                               &ptr1, &ptr2, &ptr3) != NO_TYPE)
03767               {
03768                 Note.Buffer = Settings.BufferNumber;
03769                 SetBufferNumber (MAX_BUFFER - 1);
03770                 ClearBuffer (PASTEBUFFER);
03771                 CopyObjectToBuffer (PASTEBUFFER->Data, PCB->Data,
03772                                     ELEMENT_TYPE, ptr1, ptr2, ptr3);
03773                 SmashBufferElement (PASTEBUFFER);
03774                 PASTEBUFFER->X = 0;
03775                 PASTEBUFFER->Y = 0;
03776                 SaveUndoSerialNumber ();
03777                 EraseObject (ELEMENT_TYPE, ptr1, ptr1);
03778                 MoveObjectToRemoveUndoList (ELEMENT_TYPE, ptr1, ptr2, ptr3);
03779                 RestoreUndoSerialNumber ();
03780                 CopyPastebufferToLayout (0, 0);
03781                 SetBufferNumber (Note.Buffer);
03782                 SetChangedFlag (true);
03783               }
03784           }
03785           break;
03786         }
03787     }
03788   return 0;
03789 }
03790 
03791 /* --------------------------------------------------------------------------- */
03792 
03793 static const char addrats_syntax[] = N_("AddRats(AllRats|SelectedRats|Close)");
03794 
03795 static const char addrats_help[] =
03796   N_("Add one or more rat lines to the board.");
03797 
03798 /* %start-doc actions AddRats
03799 
03800 @table @code
03801 
03802 @item AllRats
03803 Create rat lines for all loaded nets that aren't already connected on
03804 with copper.
03805 
03806 @item SelectedRats
03807 Similarly, but only add rat lines for nets connected to selected pins
03808 and pads.
03809 
03810 @item Close
03811 Selects the shortest unselected rat on the board.
03812 
03813 @end table
03814 
03815 %end-doc */
03816 
03817 static int
03818 ActionAddRats (int argc, char **argv, Coord x, Coord y)
03819 {
03820   char *function = ARG (0);
03821   RatType *shorty;
03822   float len, small;
03823 
03824   if (function)
03825     {
03826       if (Settings.RatWarn)
03827         ClearWarnings ();
03828       switch (GetFunctionID (function))
03829         {
03830         case F_AllRats:
03831           if (AddAllRats (false, NULL))
03832             SetChangedFlag (true);
03833           break;
03834         case F_SelectedRats:
03835         case F_Selected:
03836           if (AddAllRats (true, NULL))
03837             SetChangedFlag (true);
03838           break;
03839         case F_Close:
03840           small = SQUARE (MAX_COORD);
03841           shorty = NULL;
03842           RAT_LOOP (PCB->Data);
03843           {
03844             if (TEST_FLAG (SELECTEDFLAG, line))
03845               continue;
03846             len = SQUARE (line->Point1.X - line->Point2.X) +
03847               SQUARE (line->Point1.Y - line->Point2.Y);
03848             if (len < small)
03849               {
03850                 small = len;
03851                 shorty = line;
03852               }
03853           }
03854           END_LOOP;
03855           if (shorty)
03856             {
03857               AddObjectToFlagUndoList (RATLINE_TYPE, shorty, shorty, shorty);
03858               SET_FLAG (SELECTEDFLAG, shorty);
03859               DrawRat (shorty);
03860               Draw ();
03861               CenterDisplay ((shorty->Point2.X + shorty->Point1.X) / 2,
03862                              (shorty->Point2.Y + shorty->Point1.Y) / 2,
03863                              false);
03864             }
03865           break;
03866         }
03867     }
03868   return 0;
03869 }
03870 
03871 /* --------------------------------------------------------------------------- */
03872 
03873 static const char delete_syntax[] =
03874   N_("Delete(Object|Selected)\n"
03875   "Delete(AllRats|SelectedRats)");
03876 
03877 static const char delete_help[] = N_("Delete stuff.");
03878 
03879 /* %start-doc actions Delete
03880 
03881 %end-doc */
03882 
03883 static int
03884 ActionDelete (int argc, char **argv, Coord x, Coord y)
03885 {
03886   char *function = ARG (0);
03887   int id = GetFunctionID (function);
03888 
03889   Note.X = Crosshair.X;
03890   Note.Y = Crosshair.Y;
03891 
03892   if (id == -1) /* no arg */
03893     {
03894       if (RemoveSelected() == false)
03895         id = F_Object;
03896     }
03897 
03898   switch (id)
03899     {
03900     case F_Object:
03901       SaveMode();
03902       SetMode(REMOVE_MODE);
03903       NotifyMode();
03904       RestoreMode();
03905       break;
03906     case F_Selected:
03907       RemoveSelected();
03908       break;
03909     case F_AllRats:
03910       if (DeleteRats (false))
03911         SetChangedFlag (true);
03912       break;
03913     case F_SelectedRats:
03914       if (DeleteRats (true))
03915         SetChangedFlag (true);
03916       break;
03917     }
03918 
03919   return 0;
03920 }
03921 
03922 /* --------------------------------------------------------------------------- */
03923 
03924 static const char deleterats_syntax[] =
03925   N_("DeleteRats(AllRats|Selected|SelectedRats)");
03926 
03927 static const char deleterats_help[] = N_("Delete rat lines.");
03928 
03929 /* %start-doc actions DeleteRats
03930 
03931 %end-doc */
03932 
03933 static int
03934 ActionDeleteRats (int argc, char **argv, Coord x, Coord y)
03935 {
03936   char *function = ARG (0);
03937   if (function)
03938     {
03939       if (Settings.RatWarn)
03940         ClearWarnings ();
03941       switch (GetFunctionID (function))
03942         {
03943         case F_AllRats:
03944           if (DeleteRats (false))
03945             SetChangedFlag (true);
03946           break;
03947         case F_SelectedRats:
03948         case F_Selected:
03949           if (DeleteRats (true))
03950             SetChangedFlag (true);
03951           break;
03952         }
03953     }
03954   return 0;
03955 }
03956 
03957 /* --------------------------------------------------------------------------- */
03958 
03959 static const char autoplace_syntax[] = N_("AutoPlaceSelected()");
03960 
03961 static const char autoplace_help[] = N_("Auto-place selected components.");
03962 
03963 /* %start-doc actions AutoPlaceSelected
03964 
03965 Attempts to re-arrange the selected components such that the nets
03966 connecting them are minimized.  Note that you cannot undo this.
03967 
03968 %end-doc */
03969 
03970 static int
03971 ActionAutoPlaceSelected (int argc, char **argv, Coord x, Coord y)
03972 {
03973   hid_action("Busy");
03974   if (gui->confirm_dialog (_("Auto-placement can NOT be undone.\n"
03975                              "Do you want to continue anyway?\n"), 0))
03976     {
03977       if (AutoPlaceSelected ())
03978         SetChangedFlag (true);
03979     }
03980   return 0;
03981 }
03982 
03983 /* --------------------------------------------------------------------------- */
03984 
03985 static const char autoroute_syntax[] = N_("AutoRoute(AllRats|SelectedRats)");
03986 
03987 static const char autoroute_help[] = N_("Auto-route some or all rat lines.");
03988 
03989 /* %start-doc actions AutoRoute
03990 
03991 @table @code
03992 
03993 @item AllRats
03994 Attempt to autoroute all rats.
03995 
03996 @item SelectedRats
03997 Attempt to autoroute the selected rats.
03998 
03999 @end table
04000 
04001 Before autorouting, it's important to set up a few things.  First,
04002 make sure any layers you aren't using are disabled, else the
04003 autorouter may use them.  Next, make sure the current line and via
04004 styles are set accordingly.  Last, make sure "new lines clear
04005 polygons" is set, in case you eventually want to add a copper pour.
04006 
04007 Autorouting takes a while.  During this time, the program may not be
04008 responsive.
04009 
04010 %end-doc */
04011 
04012 static int
04013 ActionAutoRoute (int argc, char **argv, Coord x, Coord y)
04014 {
04015   char *function = ARG (0);
04016   hid_action("Busy");
04017   if (function)                 /* one parameter */
04018     {
04019       switch (GetFunctionID (function))
04020         {
04021         case F_AllRats:
04022           if (AutoRoute (false))
04023             SetChangedFlag (true);
04024           break;
04025         case F_SelectedRats:
04026         case F_Selected:
04027           if (AutoRoute (true))
04028             SetChangedFlag (true);
04029           break;
04030         }
04031     }
04032   return 0;
04033 }
04034 
04035 /* --------------------------------------------------------------------------- */
04036 
04037 static const char markcrosshair_syntax[] =
04038   N_("MarkCrosshair()\n"
04039   "MarkCrosshair(Center)");
04040 
04041 static const char markcrosshair_help[] = N_("Set/Reset the Crosshair mark.");
04042 
04043 /* %start-doc actions MarkCrosshair
04044 
04045 The ``mark'' is a small X-shaped target on the display which is
04046 treated like a second origin (the normal origin is the upper let
04047 corner of the board).  The GUI will display a second set of
04048 coordinates for this mark, which tells you how far you are from it.
04049 
04050 If no argument is given, the mark is toggled - disabled if it was
04051 enabled, or enabled at the current cursor position of disabled.  If
04052 the @code{Center} argument is given, the mark is moved to the current
04053 cursor location.
04054 
04055 %end-doc */
04056 
04057 static int
04058 ActionMarkCrosshair (int argc, char **argv, Coord x, Coord y)
04059 {
04060   char *function = ARG (0);
04061   if (!function || !*function)
04062     {
04063       if (Marked.status)
04064         {
04065           notify_mark_change (false);
04066           Marked.status = false;
04067           notify_mark_change (true);
04068         }
04069       else
04070         {
04071           notify_mark_change (false);
04072           Marked.status = false;
04073           Marked.status = true;
04074           Marked.X = Crosshair.X;
04075           Marked.Y = Crosshair.Y;
04076           notify_mark_change (true);
04077         }
04078     }
04079   else if (GetFunctionID (function) == F_Center)
04080     {
04081       notify_mark_change (false);
04082       Marked.status = true;
04083       Marked.X = Crosshair.X;
04084       Marked.Y = Crosshair.Y;
04085       notify_mark_change (true);
04086     }
04087   return 0;
04088 }
04089 
04090 /* --------------------------------------------------------------------------- */
04091 
04092 static const char changesize_syntax[] =
04093   N_("ChangeSize(Object, delta)\n"
04094   "ChangeSize(SelectedObjects|Selected, delta)\n"
04095   "ChangeSize(SelectedLines|SelectedPins|SelectedVias, delta)\n"
04096   "ChangeSize(SelectedPads|SelectedTexts|SelectedNames, delta)\n"
04097   "ChangeSize(SelectedElements, delta)");
04098 
04099 static const char changesize_help[] = N_("Changes the size of objects.");
04100 
04101 /* %start-doc actions ChangeSize
04102 
04103 For lines and arcs, this changes the width.  For pins and vias, this
04104 changes the overall diameter of the copper annulus.  For pads, this
04105 changes the width and, indirectly, the length.  For texts and names,
04106 this changes the scaling factor.  For elements, this changes the width
04107 of the silk layer lines and arcs for this element.
04108 
04109 %end-doc */
04110 
04111 static int
04112 ActionChangeSize (int argc, char **argv, Coord x, Coord y)
04113 {
04114   char *function = ARG (0);
04115   char *delta = ARG (1);
04116   char *units = ARG (2);
04117   bool absolute;                        /* indicates if absolute size is given */
04118   Coord value;
04119 
04120   if (function && delta)
04121     {
04122       value = GetValue (delta, units, &absolute);
04123       if (value == 0)
04124         value = delta[0] == '-' ? -Settings.increments->size
04125                                 :  Settings.increments->size;
04126       switch (GetFunctionID (function))
04127         {
04128         case F_Object:
04129           {
04130             int type;
04131             void *ptr1, *ptr2, *ptr3;
04132 
04133             if ((type =
04134                  SearchScreen (Crosshair.X, Crosshair.Y, CHANGESIZE_TYPES,
04135                                &ptr1, &ptr2, &ptr3)) != NO_TYPE)
04136               if (TEST_FLAG (LOCKFLAG, (PinType *) ptr2))
04137                 Message (_("Sorry, the object is locked\n"));
04138             if (ChangeObjectSize (type, ptr1, ptr2, ptr3, value, absolute))
04139               SetChangedFlag (true);
04140             break;
04141           }
04142 
04143         case F_SelectedVias:
04144           if (ChangeSelectedSize (VIA_TYPE, value, absolute))
04145             SetChangedFlag (true);
04146           break;
04147 
04148         case F_SelectedPins:
04149           if (ChangeSelectedSize (PIN_TYPE, value, absolute))
04150             SetChangedFlag (true);
04151           break;
04152 
04153         case F_SelectedPads:
04154           if (ChangeSelectedSize (PAD_TYPE, value, absolute))
04155             SetChangedFlag (true);
04156           break;
04157 
04158         case F_SelectedArcs:
04159           if (ChangeSelectedSize (ARC_TYPE, value, absolute))
04160             SetChangedFlag (true);
04161           break;
04162 
04163         case F_SelectedLines:
04164           if (ChangeSelectedSize (LINE_TYPE, value, absolute))
04165             SetChangedFlag (true);
04166           break;
04167 
04168         case F_SelectedTexts:
04169           if (ChangeSelectedSize (TEXT_TYPE, value, absolute))
04170             SetChangedFlag (true);
04171           break;
04172 
04173         case F_SelectedNames:
04174           if (ChangeSelectedSize (ELEMENTNAME_TYPE, value, absolute))
04175             SetChangedFlag (true);
04176           break;
04177 
04178         case F_SelectedElements:
04179           if (ChangeSelectedSize (ELEMENT_TYPE, value, absolute))
04180             SetChangedFlag (true);
04181           break;
04182 
04183         case F_Selected:
04184         case F_SelectedObjects:
04185           if (ChangeSelectedSize (CHANGESIZE_TYPES, value, absolute))
04186             SetChangedFlag (true);
04187           break;
04188         }
04189     }
04190   return 0;
04191 }
04192 
04193 /* --------------------------------------------------------------------------- */
04194 
04195 static const char changedrillsize_syntax[] =
04196   N_("ChangeDrillSize(Object, delta)\n"
04197   "ChangeDrillSize(SelectedPins|SelectedVias|Selected|SelectedObjects, delta)");
04198 
04199 static const char changedrillsize_help[] =
04200   N_("Changes the drilling hole size of objects.");
04201 
04202 /* %start-doc actions ChangeDrillSize
04203 
04204 %end-doc */
04205 
04206 static int
04207 ActionChange2ndSize (int argc, char **argv, Coord x, Coord y)
04208 {
04209   char *function = ARG (0);
04210   char *delta = ARG (1);
04211   char *units = ARG (2);
04212   bool absolute;
04213   Coord value;
04214 
04215   if (function && delta)
04216     {
04217       value = GetValue (delta, units, &absolute);
04218       switch (GetFunctionID (function))
04219         {
04220         case F_Object:
04221           {
04222             int type;
04223             void *ptr1, *ptr2, *ptr3;
04224 
04225             gui->get_coords (_("Select an Object"), &x, &y);
04226             if ((type =
04227                  SearchScreen (x, y, CHANGE2NDSIZE_TYPES,
04228                                &ptr1, &ptr2, &ptr3)) != NO_TYPE)
04229               if (ChangeObject2ndSize
04230                   (type, ptr1, ptr2, ptr3, value, absolute, true))
04231                 SetChangedFlag (true);
04232             break;
04233           }
04234 
04235         case F_SelectedVias:
04236           if (ChangeSelected2ndSize (VIA_TYPE, value, absolute))
04237             SetChangedFlag (true);
04238           break;
04239 
04240         case F_SelectedPins:
04241           if (ChangeSelected2ndSize (PIN_TYPE, value, absolute))
04242             SetChangedFlag (true);
04243           break;
04244         case F_Selected:
04245         case F_SelectedObjects:
04246           if (ChangeSelected2ndSize (PIN_TYPES, value, absolute))
04247             SetChangedFlag (true);
04248           break;
04249         }
04250     }
04251   return 0;
04252 }
04253 
04254 /* --------------------------------------------------------------------------- */
04255 
04256 static const char changeclearsize_syntax[] =
04257   N_("ChangeClearSize(Object, delta)\n"
04258   "ChangeClearSize(SelectedPins|SelectedPads|SelectedVias, delta)\n"
04259   "ChangeClearSize(SelectedLines|SelectedArcs, delta\n"
04260   "ChangeClearSize(Selected|SelectedObjects, delta)");
04261 
04262 static const char changeclearsize_help[] =
04263   N_("Changes the clearance size of objects.");
04264 
04265 /* %start-doc actions ChangeClearSize
04266 
04267 If the solder mask is currently showing, this action changes the
04268 solder mask clearance.  If the mask is not showing, this action
04269 changes the polygon clearance.
04270 
04271 %end-doc */
04272 
04273 static int
04274 ActionChangeClearSize (int argc, char **argv, Coord x, Coord y)
04275 {
04276   char *function = ARG (0);
04277   char *delta = ARG (1);
04278   char *units = ARG (2);
04279   bool absolute;
04280   Coord value;
04281 
04282   if (function && delta)
04283     {
04284       value = 2 * GetValue (delta, units, &absolute);
04285       if (value == 0)
04286         value = delta[0] == '-' ? -Settings.increments->clear
04287                                 :  Settings.increments->clear;
04288       switch (GetFunctionID (function))
04289         {
04290         case F_Object:
04291           {
04292             int type;
04293             void *ptr1, *ptr2, *ptr3;
04294 
04295             gui->get_coords (_("Select an Object"), &x, &y);
04296             if ((type =
04297                  SearchScreen (x, y,
04298                                CHANGECLEARSIZE_TYPES, &ptr1, &ptr2,
04299                                &ptr3)) != NO_TYPE)
04300               if (ChangeObjectClearSize (type, ptr1, ptr2, ptr3, value, absolute))
04301                 SetChangedFlag (true);
04302             break;
04303           }
04304         case F_SelectedVias:
04305           if (ChangeSelectedClearSize (VIA_TYPE, value, absolute))
04306             SetChangedFlag (true);
04307           break;
04308         case F_SelectedPads:
04309           if (ChangeSelectedClearSize (PAD_TYPE, value, absolute))
04310             SetChangedFlag (true);
04311           break;
04312         case F_SelectedPins:
04313           if (ChangeSelectedClearSize (PIN_TYPE, value, absolute))
04314             SetChangedFlag (true);
04315           break;
04316         case F_SelectedLines:
04317           if (ChangeSelectedClearSize (LINE_TYPE, value, absolute))
04318             SetChangedFlag (true);
04319           break;
04320         case F_SelectedArcs:
04321           if (ChangeSelectedClearSize (ARC_TYPE, value, absolute))
04322             SetChangedFlag (true);
04323           break;
04324         case F_Selected:
04325         case F_SelectedObjects:
04326           if (ChangeSelectedClearSize (CHANGECLEARSIZE_TYPES, value, absolute))
04327             SetChangedFlag (true);
04328           break;
04329         }
04330     }
04331   return 0;
04332 }
04333 
04334 /* ---------------------------------------------------------------------------  */
04335 
04336 static const char minmaskgap_syntax[] =
04337   N_("MinMaskGap(delta)\n"
04338   "MinMaskGap(Selected, delta)");
04339 
04340 static const char minmaskgap_help[] =
04341   N_("Ensures the mask is a minimum distance from pins and pads.");
04342 
04343 /* %start-doc actions MinMaskGap
04344 
04345 Checks all specified pins and/or pads, and increases the mask if
04346 needed to ensure a minimum distance between the pin or pad edge and
04347 the mask edge.
04348 
04349 %end-doc */
04350 
04351 static int
04352 ActionMinMaskGap (int argc, char **argv, Coord x, Coord y)
04353 {
04354   char *function = ARG (0);
04355   char *delta = ARG (1);
04356   char *units = ARG (2);
04357   bool absolute;
04358   Coord value;
04359   Coord thickness;
04360   int flags;
04361 
04362   if (!function)
04363     return 1;
04364   if (strcasecmp (function, "Selected") == 0)
04365     flags = SELECTEDFLAG;
04366   else
04367     {
04368       units = delta;
04369       delta = function;
04370       flags = 0;
04371     }
04372   value = 2 * GetValue (delta, units, &absolute);
04373 
04374   SaveUndoSerialNumber ();
04375   ELEMENT_LOOP (PCB->Data);
04376   {
04377     PIN_LOOP (element);
04378     {
04379       if (!TEST_FLAGS (flags, pin) || ! pin->Mask) continue;
04380 
04381       thickness = pin->DrillingHole;
04382       
04383       if (pin->Thickness > thickness) thickness = pin->Thickness;
04384 
04385       thickness += value;
04386 
04387       if (pin->Mask < thickness)
04388         {
04389           ChangeObjectMaskSize (PIN_TYPE, element, pin, 0, thickness, 1);
04390           RestoreUndoSerialNumber ();
04391         }
04392     }
04393     END_LOOP;
04394     PAD_LOOP (element);
04395     {
04396       if (!TEST_FLAGS (flags, pad) || ! pad->Mask)
04397         continue;
04398       if (pad->Mask < pad->Thickness + value)
04399         {
04400           ChangeObjectMaskSize (PAD_TYPE, element, pad, 0,
04401                                 pad->Thickness + value, 1);
04402           RestoreUndoSerialNumber ();
04403         }
04404     }
04405     END_LOOP;
04406   }
04407   END_LOOP;
04408   VIA_LOOP (PCB->Data);
04409   {
04410     if (!TEST_FLAGS (flags, via) || ! via->Mask)
04411       continue;
04412 
04413     thickness = via->DrillingHole;
04414     if (via->Thickness > thickness)
04415       thickness = via->Thickness;
04416     thickness += value;
04417 
04418     if (via->Mask < thickness)
04419       {
04420         ChangeObjectMaskSize (VIA_TYPE, via, 0, 0, thickness, 1);
04421         RestoreUndoSerialNumber ();
04422       }
04423   }
04424   END_LOOP;
04425   RestoreUndoSerialNumber ();
04426   IncrementUndoSerialNumber ();
04427   return 0;
04428 }
04429 
04430 /* ---------------------------------------------------------------------------  */
04431 
04432 static const char mincleargap_syntax[] =
04433   N_("MinClearGap(delta)\n"
04434   "MinClearGap(Selected, delta)");
04435 
04436 static const char mincleargap_help[] =
04437   N_("Ensures that polygons are a minimum distance from objects.");
04438 
04439 /* %start-doc actions MinClearGap
04440 
04441 Checks all specified objects, and increases the polygon clearance if
04442 needed to ensure a minimum distance between their edges and the
04443 polygon edges.
04444 
04445 %end-doc */
04446 
04447 static int
04448 ActionMinClearGap (int argc, char **argv, Coord x, Coord y)
04449 {
04450   char *function = ARG (0);
04451   char *delta = ARG (1);
04452   char *units = ARG (2);
04453   bool absolute;
04454   Coord value;
04455   int flags;
04456 
04457   if (!function)
04458     return 1;
04459   if (strcasecmp (function, "Selected") == 0)
04460     flags = SELECTEDFLAG;
04461   else
04462     {
04463       units = delta;
04464       delta = function;
04465       flags = 0;
04466     }
04467   value = 2 * GetValue (delta, units, &absolute);
04468 
04469   SaveUndoSerialNumber ();
04470   ELEMENT_LOOP (PCB->Data);
04471   {
04472     PIN_LOOP (element);
04473     {
04474       if (!TEST_FLAGS (flags, pin))
04475         continue;
04476       if (pin->Clearance < value)
04477         {
04478           ChangeObjectClearSize (PIN_TYPE, element, pin, 0,
04479                                 value, 1);
04480           RestoreUndoSerialNumber ();
04481         }
04482     }
04483     END_LOOP;
04484     PAD_LOOP (element);
04485     {
04486       if (!TEST_FLAGS (flags, pad))
04487         continue;
04488       if (pad->Clearance < value)
04489         {
04490           ChangeObjectClearSize (PAD_TYPE, element, pad, 0,
04491                                 value, 1);
04492           RestoreUndoSerialNumber ();
04493         }
04494     }
04495     END_LOOP;
04496   }
04497   END_LOOP;
04498   VIA_LOOP (PCB->Data);
04499   {
04500     if (!TEST_FLAGS (flags, via))
04501       continue;
04502     if (via->Clearance < value)
04503       {
04504         ChangeObjectClearSize (VIA_TYPE, via, 0, 0, value, 1);
04505         RestoreUndoSerialNumber ();
04506       }
04507   }
04508   END_LOOP;
04509   ALLLINE_LOOP (PCB->Data);
04510   {
04511     if (!TEST_FLAGS (flags, line))
04512       continue;
04513     if (line->Clearance < value)
04514       {
04515         ChangeObjectClearSize (LINE_TYPE, layer, line, 0, value, 1);
04516         RestoreUndoSerialNumber ();
04517       }
04518   }
04519   ENDALL_LOOP;
04520   ALLARC_LOOP (PCB->Data);
04521   {
04522     if (!TEST_FLAGS (flags, arc))
04523       continue;
04524     if (arc->Clearance < value)
04525       {
04526         ChangeObjectClearSize (ARC_TYPE, layer, arc, 0, value, 1);
04527         RestoreUndoSerialNumber ();
04528       }
04529   }
04530   ENDALL_LOOP;
04531   RestoreUndoSerialNumber ();
04532   IncrementUndoSerialNumber ();
04533   return 0;
04534 }
04535 
04536 /* ---------------------------------------------------------------------------  */
04537 
04538 static const char changepinname_syntax[] =
04539   N_("ChangePinName(ElementName,PinNumber,PinName)");
04540 
04541 static const char changepinname_help[] =
04542   N_("Sets the name of a specific pin on a specific element.");
04543 
04544 /* %start-doc actions ChangePinName
04545 
04546 This can be especially useful for annotating pin names from a
04547 schematic to the layout without requiring knowledge of the pcb file
04548 format.
04549 
04550 @example
04551 ChangePinName(U3, 7, VCC)
04552 @end example
04553 
04554 %end-doc */
04555 
04556 static int
04557 ActionChangePinName (int argc, char **argv, Coord x, Coord y)
04558 {
04559   int changed = 0;
04560   char *refdes, *pinnum, *pinname;
04561 
04562   if (argc != 3)
04563     {
04564       AFAIL (changepinname);
04565     }
04566 
04567   refdes = argv[0];
04568   pinnum = argv[1];
04569   pinname = argv[2];
04570 
04571   ELEMENT_LOOP (PCB->Data);
04572   {
04573     if (NSTRCMP (refdes, NAMEONPCB_NAME (element)) == 0)
04574       {
04575         PIN_LOOP (element);
04576         {
04577           if (NSTRCMP (pinnum, pin->Number) == 0)
04578             {
04579               AddObjectToChangeNameUndoList (PIN_TYPE, NULL, NULL,
04580                                              pin, pin->Name);
04581               /*
04582                * Note:  we can't free() pin->Name first because 
04583                * it is used in the undo list
04584                */
04585               pin->Name = strdup (pinname);
04586               SetChangedFlag (true);
04587               changed = 1;
04588             }
04589         }
04590         END_LOOP;
04591 
04592         PAD_LOOP (element);
04593         {
04594           if (NSTRCMP (pinnum, pad->Number) == 0)
04595             {
04596               AddObjectToChangeNameUndoList (PAD_TYPE, NULL, NULL,
04597                                              pad, pad->Name);
04598               /* 
04599                * Note:  we can't free() pad->Name first because 
04600                * it is used in the undo list
04601                */
04602               pad->Name = strdup (pinname);
04603               SetChangedFlag (true);
04604               changed = 1;
04605             }
04606         }
04607         END_LOOP;
04608       }
04609   }
04610   END_LOOP;
04611   /* 
04612    * done with our action so increment the undo # if we actually
04613    * changed anything
04614    */
04615   if (changed)
04616     {
04617       if (defer_updates)
04618         defer_needs_update = 1;
04619       else
04620         {
04621           IncrementUndoSerialNumber ();
04622           gui->invalidate_all ();
04623         }
04624     }
04625 
04626   return 0;
04627 }
04628 
04629 /* --------------------------------------------------------------------------- */
04630 
04631 static const char changename_syntax[] =
04632   N_("ChangeName(Object)\n"
04633   "ChangeName(Layout|Layer)");
04634 
04635 static const char changename_help[] = N_("Sets the name of objects.");
04636 
04637 /* %start-doc actions ChangeName
04638 
04639 @table @code
04640 
04641 @item Object
04642 Changes the name of the element under the cursor.
04643 
04644 @item Layout
04645 Changes the name of the layout.  This is printed on the fab drawings.
04646 
04647 @item Layer
04648 Changes the name of the currently active layer.
04649 
04650 @end table
04651 
04652 %end-doc */
04653 
04654 int
04655 ActionChangeName (int argc, char **argv, Coord x, Coord y)
04656 {
04657   char *function = ARG (0);
04658   char *name;
04659 
04660   if (function)
04661     {
04662       switch (GetFunctionID (function))
04663         {
04664           /* change the name of an object */
04665         case F_Object:
04666           {
04667             int type;
04668             void *ptr1, *ptr2, *ptr3;
04669 
04670             gui->get_coords (_("Select an Object"), &x, &y);
04671             if ((type =
04672                  SearchScreen (x, y, CHANGENAME_TYPES,
04673                                &ptr1, &ptr2, &ptr3)) != NO_TYPE)
04674               {
04675                 SaveUndoSerialNumber ();
04676                 if (QueryInputAndChangeObjectName (type, ptr1, ptr2, ptr3))
04677                   {
04678                     SetChangedFlag (true);
04679                     if (type == ELEMENT_TYPE)
04680                       {
04681                         RubberbandType *ptr;
04682                         int i;
04683 
04684                         RestoreUndoSerialNumber ();
04685                         Crosshair.AttachedObject.RubberbandN = 0;
04686                         LookupRatLines (type, ptr1, ptr2, ptr3);
04687                         ptr = Crosshair.AttachedObject.Rubberband;
04688                         for (i = 0; i < Crosshair.AttachedObject.RubberbandN;
04689                              i++, ptr++)
04690                           {
04691                             if (PCB->RatOn)
04692                               EraseRat ((RatType *) ptr->Line);
04693                             MoveObjectToRemoveUndoList (RATLINE_TYPE,
04694                                                         ptr->Line, ptr->Line,
04695                                                         ptr->Line);
04696                           }
04697                         IncrementUndoSerialNumber ();
04698                         Draw ();
04699                       }
04700                   }
04701               }
04702             break;
04703           }
04704 
04705           /* change the layout's name */
04706         case F_Layout:
04707           name =
04708             gui->prompt_for (_("Enter the layout name:"), EMPTY (PCB->Name));
04709           /* NB: ChangeLayoutName takes ownership of the passed memory */
04710           if (name && ChangeLayoutName (name))
04711             SetChangedFlag (true);
04712           break;
04713 
04714           /* change the name of the active layer */
04715         case F_Layer:
04716           name = gui->prompt_for (_("Enter the layer name:"),
04717                                   EMPTY (CURRENT->Name));
04718           /* NB: ChangeLayerName takes ownership of the passed memory */
04719           if (name && ChangeLayerName (CURRENT, name))
04720             SetChangedFlag (true);
04721           break;
04722         }
04723     }
04724   return 0;
04725 }
04726 
04727 
04728 /* --------------------------------------------------------------------------- */
04729 
04730 static const char morphpolygon_syntax[] = N_("MorphPolygon(Object|Selected)");
04731 
04732 static const char morphpolygon_help[] =
04733   N_("Converts dead polygon islands into separate polygons.");
04734 
04735 /* %start-doc actions MorphPolygon 
04736 
04737 If a polygon is divided into unconnected "islands", you can use
04738 this command to convert the otherwise disappeared islands into
04739 separate polygons. Be sure the cursor is over a portion of the
04740 polygon that remains visible. Very small islands that may flake
04741 off are automatically deleted.
04742 
04743 %end-doc */
04744 
04745 static int
04746 ActionMorphPolygon (int argc, char **argv, Coord x, Coord y)
04747 {
04748   char *function = ARG (0);
04749   if (function)
04750     {
04751       switch (GetFunctionID (function))
04752         {
04753         case F_Object:
04754           {
04755             int type;
04756             void *ptr1, *ptr2, *ptr3;
04757 
04758             gui->get_coords (_("Select an Object"), &x, &y);
04759             if ((type = SearchScreen (x, y, POLYGON_TYPE,
04760                                       &ptr1, &ptr2, &ptr3)) != NO_TYPE)
04761               {
04762                 MorphPolygon ((LayerType *) ptr1, (PolygonType *) ptr3);
04763                 Draw ();
04764                 IncrementUndoSerialNumber ();
04765               }
04766             break;
04767           }
04768         case F_Selected:
04769         case F_SelectedObjects:
04770           ALLPOLYGON_LOOP (PCB->Data);
04771           {
04772             if (TEST_FLAG (SELECTEDFLAG, polygon))
04773               MorphPolygon (layer, polygon);
04774           }
04775           ENDALL_LOOP;
04776           Draw ();
04777           IncrementUndoSerialNumber ();
04778           break;
04779         }
04780     }
04781   return 0;
04782 }
04783 
04784 /* --------------------------------------------------------------------------- */
04785 
04786 static const char togglehidename_syntax[] =
04787   N_("ToggleHideName(Object|SelectedElements)");
04788 
04789 static const char togglehidename_help[] =
04790   N_("Toggles the visibility of element names.");
04791 
04792 /* %start-doc actions ToggleHideName
04793 
04794 If names are hidden you won't see them on the screen and they will not
04795 appear on the silk layer when you print the layout.
04796 
04797 %end-doc */
04798 
04799 static int
04800 ActionToggleHideName (int argc, char **argv, Coord x, Coord y)
04801 {
04802   char *function = ARG (0);
04803   if (function && PCB->ElementOn)
04804     {
04805       switch (GetFunctionID (function))
04806         {
04807         case F_Object:
04808           {
04809             int type;
04810             void *ptr1, *ptr2, *ptr3;
04811 
04812             gui->get_coords (_("Select an Object"), &x, &y);
04813             if ((type = SearchScreen (x, y, ELEMENT_TYPE,
04814                                       &ptr1, &ptr2, &ptr3)) != NO_TYPE)
04815               {
04816                 AddObjectToFlagUndoList (type, ptr1, ptr2, ptr3);
04817                 EraseElementName ((ElementType *) ptr2);
04818                 TOGGLE_FLAG (HIDENAMEFLAG, (ElementType *) ptr2);
04819                 DrawElementName ((ElementType *) ptr2);
04820                 Draw ();
04821                 IncrementUndoSerialNumber ();
04822               }
04823             break;
04824           }
04825         case F_SelectedElements:
04826         case F_Selected:
04827           {
04828             bool changed = false;
04829             ELEMENT_LOOP (PCB->Data);
04830             {
04831               if ((TEST_FLAG (SELECTEDFLAG, element) ||
04832                    TEST_FLAG (SELECTEDFLAG,
04833                               &NAMEONPCB_TEXT (element)))
04834                   && (FRONT (element) || PCB->InvisibleObjectsOn))
04835                 {
04836                   AddObjectToFlagUndoList (ELEMENT_TYPE, element,
04837                                            element, element);
04838                   EraseElementName (element);
04839                   TOGGLE_FLAG (HIDENAMEFLAG, element);
04840                   DrawElementName (element);
04841                   changed = true;
04842                 }
04843             }
04844             END_LOOP;
04845             if (changed)
04846               {
04847                 Draw ();
04848                 IncrementUndoSerialNumber ();
04849               }
04850           }
04851         }
04852     }
04853   return 0;
04854 }
04855 
04856 /* --------------------------------------------------------------------------- */
04857 
04858 static const char changejoin_syntax[] =
04859   N_("ChangeJoin(ToggleObject|SelectedLines|SelectedArcs|Selected)");
04860 
04861 static const char changejoin_help[] =
04862   N_("Changes the join (clearance through polygons) of objects.");
04863 
04864 /* %start-doc actions ChangeJoin
04865 
04866 The join flag determines whether a line or arc, drawn to intersect a
04867 polygon, electrically connects to the polygon or not.  When joined,
04868 the line/arc is simply drawn over the polygon, making an electrical
04869 connection.  When not joined, a gap is drawn between the line and the
04870 polygon, insulating them from each other.
04871 
04872 %end-doc */
04873 
04874 static int
04875 ActionChangeJoin (int argc, char **argv, Coord x, Coord y)
04876 {
04877   char *function = ARG (0);
04878   if (function)
04879     {
04880       switch (GetFunctionID (function))
04881         {
04882         case F_ToggleObject:
04883         case F_Object:
04884           {
04885             int type;
04886             void *ptr1, *ptr2, *ptr3;
04887 
04888             gui->get_coords (_("Select an Object"), &x, &y);
04889             if ((type =
04890                  SearchScreen (x, y, CHANGEJOIN_TYPES,
04891                                &ptr1, &ptr2, &ptr3)) != NO_TYPE)
04892               if (ChangeObjectJoin (type, ptr1, ptr2, ptr3))
04893                 SetChangedFlag (true);
04894             break;
04895           }
04896 
04897         case F_SelectedLines:
04898           if (ChangeSelectedJoin (LINE_TYPE))
04899             SetChangedFlag (true);
04900           break;
04901 
04902         case F_SelectedArcs:
04903           if (ChangeSelectedJoin (ARC_TYPE))
04904             SetChangedFlag (true);
04905           break;
04906 
04907         case F_Selected:
04908         case F_SelectedObjects:
04909           if (ChangeSelectedJoin (CHANGEJOIN_TYPES))
04910             SetChangedFlag (true);
04911           break;
04912         }
04913     }
04914   return 0;
04915 }
04916 
04917 /* --------------------------------------------------------------------------- */
04918 
04919 static const char changesquare_syntax[] =
04920   N_("ChangeSquare(ToggleObject)\n"
04921   "ChangeSquare(SelectedElements|SelectedPins)\n"
04922   "ChangeSquare(Selected|SelectedObjects)");
04923 
04924 static const char changesquare_help[] =
04925   N_("Changes the square flag of pins and pads.");
04926 
04927 /* %start-doc actions ChangeSquare
04928 
04929 Note that @code{Pins} means both pins and pads.
04930 
04931 @pinshapes
04932 
04933 %end-doc */
04934 
04935 static int
04936 ActionChangeSquare (int argc, char **argv, Coord x, Coord y)
04937 {
04938   char *function = ARG (0);
04939   if (function)
04940     {
04941       switch (GetFunctionID (function))
04942         {
04943         case F_ToggleObject:
04944         case F_Object:
04945           {
04946             int type;
04947             void *ptr1, *ptr2, *ptr3;
04948 
04949             gui->get_coords (_("Select an Object"), &x, &y);
04950             if ((type =
04951                  SearchScreen (x, y, CHANGESQUARE_TYPES,
04952                                &ptr1, &ptr2, &ptr3)) != NO_TYPE)
04953               if (ChangeObjectSquare (type, ptr1, ptr2, ptr3))
04954                 SetChangedFlag (true);
04955             break;
04956           }
04957 
04958         case F_SelectedElements:
04959           if (ChangeSelectedSquare (ELEMENT_TYPE))
04960             SetChangedFlag (true);
04961           break;
04962 
04963         case F_SelectedPins:
04964           if (ChangeSelectedSquare (PIN_TYPE | PAD_TYPE))
04965             SetChangedFlag (true);
04966           break;
04967 
04968         case F_Selected:
04969         case F_SelectedObjects:
04970           if (ChangeSelectedSquare (PIN_TYPE | PAD_TYPE))
04971             SetChangedFlag (true);
04972           break;
04973         }
04974     }
04975   return 0;
04976 }
04977 
04978 /* --------------------------------------------------------------------------- */
04979 
04980 static const char setsquare_syntax[] =
04981   N_("SetSquare(ToggleObject|SelectedElements|SelectedPins)");
04982 
04983 static const char setsquare_help[] = N_("sets the square-flag of objects.");
04984 
04985 /* %start-doc actions SetSquare
04986 
04987 Note that @code{Pins} means pins and pads.
04988 
04989 @pinshapes
04990 
04991 %end-doc */
04992 
04993 static int
04994 ActionSetSquare (int argc, char **argv, Coord x, Coord y)
04995 {
04996   char *function = ARG (0);
04997   if (function && *function)
04998     {
04999       switch (GetFunctionID (function))
05000         {
05001         case F_ToggleObject:
05002         case F_Object:
05003           {
05004             int type;
05005             void *ptr1, *ptr2, *ptr3;
05006 
05007             gui->get_coords (_("Select an Object"), &x, &y);
05008             if ((type =
05009                  SearchScreen (x, y, CHANGESQUARE_TYPES,
05010                                &ptr1, &ptr2, &ptr3)) != NO_TYPE)
05011               if (SetObjectSquare (type, ptr1, ptr2, ptr3))
05012                 SetChangedFlag (true);
05013             break;
05014           }
05015 
05016         case F_SelectedElements:
05017           if (SetSelectedSquare (ELEMENT_TYPE))
05018             SetChangedFlag (true);
05019           break;
05020 
05021         case F_SelectedPins:
05022           if (SetSelectedSquare (PIN_TYPE | PAD_TYPE))
05023             SetChangedFlag (true);
05024           break;
05025 
05026         case F_Selected:
05027         case F_SelectedObjects:
05028           if (SetSelectedSquare (PIN_TYPE | PAD_TYPE))
05029             SetChangedFlag (true);
05030           break;
05031         }
05032     }
05033   return 0;
05034 }
05035 
05036 /* --------------------------------------------------------------------------- */
05037 
05038 static const char clearsquare_syntax[] =
05039   N_("ClearSquare(ToggleObject|SelectedElements|SelectedPins)");
05040 
05041 static const char clearsquare_help[] =
05042   N_("Clears the square-flag of pins and pads.");
05043 
05044 /* %start-doc actions ClearSquare
05045 
05046 Note that @code{Pins} means pins and pads.
05047 
05048 @pinshapes
05049 
05050 %end-doc */
05051 
05052 static int
05053 ActionClearSquare (int argc, char **argv, Coord x, Coord y)
05054 {
05055   char *function = ARG (0);
05056   if (function && *function)
05057     {
05058       switch (GetFunctionID (function))
05059         {
05060         case F_ToggleObject:
05061         case F_Object:
05062           {
05063             int type;
05064             void *ptr1, *ptr2, *ptr3;
05065 
05066             gui->get_coords (_("Select an Object"), &x, &y);
05067             if ((type =
05068                  SearchScreen (x, y, CHANGESQUARE_TYPES,
05069                                &ptr1, &ptr2, &ptr3)) != NO_TYPE)
05070               if (ClrObjectSquare (type, ptr1, ptr2, ptr3))
05071                 SetChangedFlag (true);
05072             break;
05073           }
05074 
05075         case F_SelectedElements:
05076           if (ClrSelectedSquare (ELEMENT_TYPE))
05077             SetChangedFlag (true);
05078           break;
05079 
05080         case F_SelectedPins:
05081           if (ClrSelectedSquare (PIN_TYPE | PAD_TYPE))
05082             SetChangedFlag (true);
05083           break;
05084 
05085         case F_Selected:
05086         case F_SelectedObjects:
05087           if (ClrSelectedSquare (PIN_TYPE | PAD_TYPE))
05088             SetChangedFlag (true);
05089           break;
05090         }
05091     }
05092   return 0;
05093 }
05094 
05095 /* --------------------------------------------------------------------------- */
05096 
05097 static const char changeoctagon_syntax[] =
05098   N_("ChangeOctagon(Object|ToggleObject|SelectedObjects|Selected)\n"
05099   "ChangeOctagon(SelectedElements|SelectedPins|SelectedVias)");
05100 
05101 static const char changeoctagon_help[] =
05102   N_("Changes the octagon-flag of pins and vias.");
05103 
05104 /* %start-doc actions ChangeOctagon
05105 
05106 @pinshapes
05107 
05108 %end-doc */
05109 
05110 static int
05111 ActionChangeOctagon (int argc, char **argv, Coord x, Coord y)
05112 {
05113   char *function = ARG (0);
05114   if (function)
05115     {
05116       switch (GetFunctionID (function))
05117         {
05118         case F_ToggleObject:
05119         case F_Object:
05120           {
05121             int type;
05122             void *ptr1, *ptr2, *ptr3;
05123 
05124             gui->get_coords (_("Select an Object"), &x, &y);
05125             if ((type =
05126                  SearchScreen (x, y, CHANGEOCTAGON_TYPES,
05127                                &ptr1, &ptr2, &ptr3)) != NO_TYPE)
05128               if (ChangeObjectOctagon (type, ptr1, ptr2, ptr3))
05129                 SetChangedFlag (true);
05130             break;
05131           }
05132 
05133         case F_SelectedElements:
05134           if (ChangeSelectedOctagon (ELEMENT_TYPE))
05135             SetChangedFlag (true);
05136           break;
05137 
05138         case F_SelectedPins:
05139           if (ChangeSelectedOctagon (PIN_TYPE))
05140             SetChangedFlag (true);
05141           break;
05142 
05143         case F_SelectedVias:
05144           if (ChangeSelectedOctagon (VIA_TYPE))
05145             SetChangedFlag (true);
05146           break;
05147 
05148         case F_Selected:
05149         case F_SelectedObjects:
05150           if (ChangeSelectedOctagon (PIN_TYPES))
05151             SetChangedFlag (true);
05152           break;
05153         }
05154     }
05155   return 0;
05156 }
05157 
05158 /* --------------------------------------------------------------------------- */
05159 
05160 static const char setoctagon_syntax[] =
05161   N_("SetOctagon(Object|ToggleObject|SelectedElements|Selected)");
05162 
05163 static const char setoctagon_help[] = N_("Sets the octagon-flag of objects.");
05164 
05165 /* %start-doc actions SetOctagon
05166 
05167 @pinshapes
05168 
05169 %end-doc */
05170 
05171 static int
05172 ActionSetOctagon (int argc, char **argv, Coord x, Coord y)
05173 {
05174   char *function = ARG (0);
05175   if (function)
05176     {
05177       switch (GetFunctionID (function))
05178         {
05179         case F_ToggleObject:
05180         case F_Object:
05181           {
05182             int type;
05183             void *ptr1, *ptr2, *ptr3;
05184 
05185             gui->get_coords (_("Select an Object"), &x, &y);
05186             if ((type =
05187                  SearchScreen (x, y, CHANGEOCTAGON_TYPES,
05188                                &ptr1, &ptr2, &ptr3)) != NO_TYPE)
05189               if (SetObjectOctagon (type, ptr1, ptr2, ptr3))
05190                 SetChangedFlag (true);
05191             break;
05192           }
05193 
05194         case F_SelectedElements:
05195           if (SetSelectedOctagon (ELEMENT_TYPE))
05196             SetChangedFlag (true);
05197           break;
05198 
05199         case F_SelectedPins:
05200           if (SetSelectedOctagon (PIN_TYPE))
05201             SetChangedFlag (true);
05202           break;
05203 
05204         case F_SelectedVias:
05205           if (SetSelectedOctagon (VIA_TYPE))
05206             SetChangedFlag (true);
05207           break;
05208 
05209         case F_Selected:
05210         case F_SelectedObjects:
05211           if (SetSelectedOctagon (PIN_TYPES))
05212             SetChangedFlag (true);
05213           break;
05214         }
05215     }
05216   return 0;
05217 }
05218 
05219 /* --------------------------------------------------------------------------- */
05220 
05221 static const char clearoctagon_syntax[] =
05222   N_("ClearOctagon(ToggleObject|Object|SelectedObjects|Selected)\n"
05223   "ClearOctagon(SelectedElements|SelectedPins|SelectedVias)");
05224 
05225 static const char clearoctagon_help[] =
05226   N_("Clears the octagon-flag of pins and vias.");
05227 
05228 /* %start-doc actions ClearOctagon
05229 
05230 @pinshapes
05231 
05232 %end-doc */
05233 
05234 static int
05235 ActionClearOctagon (int argc, char **argv, Coord x, Coord y)
05236 {
05237   char *function = ARG (0);
05238   if (function)
05239     {
05240       switch (GetFunctionID (function))
05241         {
05242         case F_ToggleObject:
05243         case F_Object:
05244           {
05245             int type;
05246             void *ptr1, *ptr2, *ptr3;
05247 
05248             gui->get_coords (_("Select an Object"), &x, &y);
05249             if ((type =
05250                  SearchScreen (Crosshair.X, Crosshair.Y, CHANGEOCTAGON_TYPES,
05251                                &ptr1, &ptr2, &ptr3)) != NO_TYPE)
05252               if (ClrObjectOctagon (type, ptr1, ptr2, ptr3))
05253                 SetChangedFlag (true);
05254             break;
05255           }
05256 
05257         case F_SelectedElements:
05258           if (ClrSelectedOctagon (ELEMENT_TYPE))
05259             SetChangedFlag (true);
05260           break;
05261 
05262         case F_SelectedPins:
05263           if (ClrSelectedOctagon (PIN_TYPE))
05264             SetChangedFlag (true);
05265           break;
05266 
05267         case F_SelectedVias:
05268           if (ClrSelectedOctagon (VIA_TYPE))
05269             SetChangedFlag (true);
05270           break;
05271 
05272         case F_Selected:
05273         case F_SelectedObjects:
05274           if (ClrSelectedOctagon (PIN_TYPES))
05275             SetChangedFlag (true);
05276           break;
05277         }
05278     }
05279   return 0;
05280 }
05281 
05282 /* --------------------------------------------------------------------------- */
05283 
05284 static const char changehold_syntax[] =
05285   N_("ChangeHole(ToggleObject|Object|SelectedVias|Selected)");
05286 
05287 static const char changehold_help[] = N_("Changes the hole flag of objects.");
05288 
05289 /* %start-doc actions ChangeHole
05290 
05291 The "hole flag" of a via determines whether the via is a
05292 plated-through hole (not set), or an unplated hole (set).
05293 
05294 %end-doc */
05295 
05296 static int
05297 ActionChangeHole (int argc, char **argv, Coord x, Coord y)
05298 {
05299   char *function = ARG (0);
05300   if (function)
05301     {
05302       switch (GetFunctionID (function))
05303         {
05304         case F_ToggleObject:
05305         case F_Object:
05306           {
05307             int type;
05308             void *ptr1, *ptr2, *ptr3;
05309 
05310             gui->get_coords (_("Select an Object"), &x, &y);
05311             if ((type = SearchScreen (x, y, VIA_TYPE,
05312                                       &ptr1, &ptr2, &ptr3)) != NO_TYPE
05313                 && ChangeHole ((PinType *) ptr3))
05314               IncrementUndoSerialNumber ();
05315             break;
05316           }
05317 
05318         case F_SelectedVias:
05319         case F_Selected:
05320           if (ChangeSelectedHole ())
05321             SetChangedFlag (true);
05322           break;
05323         }
05324     }
05325   return 0;
05326 }
05327 
05328 /* --------------------------------------------------------------------------- */
05329 
05330 static const char changepaste_syntax[] =
05331   N_("ChangePaste(ToggleObject|Object|SelectedPads|Selected)");
05332 
05333 static const char changepaste_help[] =
05334   N_("Changes the no paste flag of objects.");
05335 
05336 /* %start-doc actions ChangePaste
05337 
05338 The "no paste flag" of a pad determines whether the solderpaste
05339  stencil will have an opening for the pad (no set) or if there wil be
05340  no solderpaste on the pad (set).  This is used for things such as
05341  fiducial pads.
05342 
05343 %end-doc */
05344 
05345 static int
05346 ActionChangePaste (int argc, char **argv, Coord x, Coord y)
05347 {
05348   char *function = ARG (0);
05349   if (function)
05350     {
05351       switch (GetFunctionID (function))
05352         {
05353         case F_ToggleObject:
05354         case F_Object:
05355           {
05356             int type;
05357             void *ptr1, *ptr2, *ptr3;
05358 
05359             gui->get_coords (_("Select an Object"), &x, &y);
05360             if ((type = SearchScreen (x, y, PAD_TYPE,
05361                                       &ptr1, &ptr2, &ptr3)) != NO_TYPE
05362                 && ChangePaste ((PadType *) ptr3))
05363               IncrementUndoSerialNumber ();
05364             break;
05365           }
05366 
05367         case F_SelectedPads:
05368         case F_Selected:
05369           if (ChangeSelectedPaste ())
05370             SetChangedFlag (true);
05371           break;
05372         }
05373     }
05374   return 0;
05375 }
05376 
05377 /* --------------------------------------------------------------------------- */
05378 
05379 static const char select_syntax[] =
05380   N_("Select(Object|ToggleObject)\n"
05381   "Select(All|Block|Connection|BuriedVias)\n"
05382   "Select(ElementByName|ObjectByName|PadByName|PinByName)\n"
05383   "Select(ElementByName|ObjectByName|PadByName|PinByName, Name)\n"
05384   "Select(TextByName|ViaByName|NetByName)\n"
05385   "Select(TextByName|ViaByName|NetByName, Name)\n"
05386   "Select(Convert)");
05387 
05388 static const char select_help[] = N_("Toggles or sets the selection.");
05389 
05390 /* %start-doc actions Select
05391 
05392 @table @code
05393 
05394 @item ElementByName
05395 @item ObjectByName
05396 @item PadByName
05397 @item PinByName
05398 @item TextByName
05399 @item ViaByName
05400 @item NetByName
05401 
05402 These all rely on having a regular expression parser built into
05403 @code{pcb}.  If the name is not specified then the user is prompted
05404 for a pattern, and all objects that match the pattern and are of the
05405 type specified are selected.
05406 
05407 @item Object
05408 @item ToggleObject
05409 Selects the object under the cursor.
05410 
05411 @item Block
05412 Selects all objects in a rectangle indicated by the cursor.
05413 
05414 @item All
05415 Selects all objects on the board.
05416 
05417 @item Found
05418 Selects all connections with the ``found'' flag set.
05419 
05420 @item Connection
05421 Selects all connections with the ``connected'' flag set.
05422 
05423 @item Connection
05424 Selects all blind and buried vias.
05425 
05426 @item Convert
05427 Converts the selected objects to an element.  This uses the highest
05428 numbered paste buffer.
05429 
05430 @end table
05431 
05432 %end-doc */
05433 
05434 static int
05435 ActionSelect (int argc, char **argv, Coord x, Coord y)
05436 {
05437   char *function = ARG (0);
05438   if (function)
05439     {
05440       switch (GetFunctionID (function))
05441         {
05442 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
05443           int type;
05444           /* select objects by their names */
05445         case F_ElementByName:
05446           type = ELEMENT_TYPE;
05447           goto commonByName;
05448         case F_ObjectByName:
05449           type = ALL_TYPES;
05450           goto commonByName;
05451         case F_PadByName:
05452           type = PAD_TYPE;
05453           goto commonByName;
05454         case F_PinByName:
05455           type = PIN_TYPE;
05456           goto commonByName;
05457         case F_TextByName:
05458           type = TEXT_TYPE;
05459           goto commonByName;
05460         case F_ViaByName:
05461           type = VIA_TYPE;
05462           goto commonByName;
05463         case F_NetByName:
05464           type = NET_TYPE;
05465           goto commonByName;
05466 
05467         commonByName:
05468           {
05469             char *pattern = ARG (1);
05470 
05471             if (pattern
05472                 || (pattern =
05473                     gui->prompt_for (_("Enter pattern:"), "")) != NULL)
05474               {
05475                 if (SelectObjectByName (type, pattern, true))
05476                   SetChangedFlag (true);
05477                 if (ARG (1) == NULL)
05478                   free (pattern);
05479               }
05480             break;
05481           }
05482 #endif /* defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP) */
05483 
05484           /* select a single object */
05485         case F_ToggleObject:
05486         case F_Object:
05487           if (SelectObject ())
05488             SetChangedFlag (true);
05489           break;
05490 
05491           /* all objects in block */
05492         case F_Block:
05493           {
05494             BoxType box;
05495 
05496             box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
05497                           Crosshair.AttachedBox.Point2.X);
05498             box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
05499                           Crosshair.AttachedBox.Point2.Y);
05500             box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
05501                           Crosshair.AttachedBox.Point2.X);
05502             box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
05503                           Crosshair.AttachedBox.Point2.Y);
05504             notify_crosshair_change (false);
05505             NotifyBlock ();
05506             if (Crosshair.AttachedBox.State == STATE_THIRD &&
05507                 SelectBlock (&box, true))
05508               {
05509                 SetChangedFlag (true);
05510                 Crosshair.AttachedBox.State = STATE_FIRST;
05511               }
05512             notify_crosshair_change (true);
05513             break;
05514           }
05515 
05516           /* select all visible objects */
05517         case F_All:
05518           {
05519             BoxType box;
05520 
05521             box.X1 = -MAX_COORD;
05522             box.Y1 = -MAX_COORD;
05523             box.X2 = MAX_COORD;
05524             box.Y2 = MAX_COORD;
05525             if (SelectBlock (&box, true))
05526               SetChangedFlag (true);
05527             break;
05528           }
05529 
05530           /* all logical connections */
05531         case F_Found:
05532           if (SelectByFlag (FOUNDFLAG, true))
05533             {
05534               Draw ();
05535               IncrementUndoSerialNumber ();
05536               SetChangedFlag (true);
05537             }
05538           break;
05539 
05540           /* all physical connections */
05541         case F_Connection:
05542           if (SelectByFlag (CONNECTEDFLAG, true))
05543             {
05544               Draw ();
05545               IncrementUndoSerialNumber ();
05546               SetChangedFlag (true);
05547             }
05548           break;
05549 
05550         case F_BuriedVias:
05551           if (SelectBuriedVias (true))
05552             {
05553               Draw ();
05554               IncrementUndoSerialNumber ();
05555               SetChangedFlag (true);
05556             }
05557           break;
05558 
05559         case F_Convert:
05560           {
05561             Coord x, y;
05562             Note.Buffer = Settings.BufferNumber;
05563             SetBufferNumber (MAX_BUFFER - 1);
05564             ClearBuffer (PASTEBUFFER);
05565             gui->get_coords (_("Select the Element's Mark Location"), &x, &y);
05566             x = GridFit (x, PCB->Grid, PCB->GridOffsetX);
05567             y = GridFit (y, PCB->Grid, PCB->GridOffsetY);
05568             AddSelectedToBuffer (PASTEBUFFER, x, y, true);
05569             SaveUndoSerialNumber ();
05570             RemoveSelected ();
05571             ConvertBufferToElement (PASTEBUFFER);
05572             RestoreUndoSerialNumber ();
05573             CopyPastebufferToLayout (x, y);
05574             SetBufferNumber (Note.Buffer);
05575           }
05576           break;
05577 
05578         default:
05579           AFAIL (select);
05580           break;
05581         }
05582     }
05583   return 0;
05584 }
05585 
05586 /* FLAG(have_regex,FlagHaveRegex,0) */
05587 int
05588 FlagHaveRegex (int parm)
05589 {
05590 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
05591   return 1;
05592 #else
05593   return 0;
05594 #endif
05595 }
05596 
05597 /* --------------------------------------------------------------------------- */
05598 
05599 static const char unselect_syntax[] =
05600   N_("Unselect(All|Block|Connection)\n"
05601   "Unselect(ElementByName|ObjectByName|PadByName|PinByName)\n"
05602   "Unselect(ElementByName|ObjectByName|PadByName|PinByName, Name)\n"
05603   "Unselect(TextByName|ViaByName)\n"
05604   "Unselect(TextByName|ViaByName, Name)\n");
05605 
05606 static const char unselect_help[] =
05607   N_("Unselects the object at the pointer location or the specified objects.");
05608 
05609 /* %start-doc actions Unselect
05610 
05611 @table @code
05612 
05613 @item All
05614 Unselect all objects.
05615 
05616 @item Block
05617 Unselect all objects in a rectangle given by the cursor.
05618 
05619 @item Connection
05620 Unselect all connections with the ``found'' flag set.
05621 
05622 @item ElementByName
05623 @item ObjectByName
05624 @item PadByName
05625 @item PinByName
05626 @item TextByName
05627 @item ViaByName
05628 
05629 These all rely on having a regular expression parser built into
05630 @code{pcb}.  If the name is not specified then the user is prompted
05631 for a pattern, and all objects that match the pattern and are of the
05632 type specified are unselected.
05633 
05634 
05635 @end table
05636 
05637 %end-doc */
05638 
05639 static int
05640 ActionUnselect (int argc, char **argv, Coord x, Coord y)
05641 {
05642   char *function = ARG (0);
05643   if (function)
05644     {
05645       switch (GetFunctionID (function))
05646         {
05647 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
05648           int type;
05649           /* select objects by their names */
05650         case F_ElementByName:
05651           type = ELEMENT_TYPE;
05652           goto commonByName;
05653         case F_ObjectByName:
05654           type = ALL_TYPES;
05655           goto commonByName;
05656         case F_PadByName:
05657           type = PAD_TYPE;
05658           goto commonByName;
05659         case F_PinByName:
05660           type = PIN_TYPE;
05661           goto commonByName;
05662         case F_TextByName:
05663           type = TEXT_TYPE;
05664           goto commonByName;
05665         case F_ViaByName:
05666           type = VIA_TYPE;
05667           goto commonByName;
05668         case F_NetByName:
05669           type = NET_TYPE;
05670           goto commonByName;
05671 
05672         commonByName:
05673           {
05674             char *pattern = ARG (1);
05675 
05676             if (pattern
05677                 || (pattern =
05678                     gui->prompt_for (_("Enter pattern:"), "")) != NULL)
05679               {
05680                 if (SelectObjectByName (type, pattern, false))
05681                   SetChangedFlag (true);
05682                 if (ARG (1) == NULL)
05683                   free (pattern);
05684               }
05685             break;
05686           }
05687 #endif /* defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP) */
05688 
05689           /* all objects in block */
05690         case F_Block:
05691           {
05692             BoxType box;
05693 
05694             box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
05695                           Crosshair.AttachedBox.Point2.X);
05696             box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
05697                           Crosshair.AttachedBox.Point2.Y);
05698             box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
05699                           Crosshair.AttachedBox.Point2.X);
05700             box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
05701                           Crosshair.AttachedBox.Point2.Y);
05702             notify_crosshair_change (false);
05703             NotifyBlock ();
05704             if (Crosshair.AttachedBox.State == STATE_THIRD &&
05705                 SelectBlock (&box, false))
05706               {
05707                 SetChangedFlag (true);
05708                 Crosshair.AttachedBox.State = STATE_FIRST;
05709               }
05710             notify_crosshair_change (true);
05711             break;
05712           }
05713 
05714           /* unselect all visible objects */
05715         case F_All:
05716           {
05717             BoxType box;
05718 
05719             box.X1 = -MAX_COORD;
05720             box.Y1 = -MAX_COORD;
05721             box.X2 = MAX_COORD;
05722             box.Y2 = MAX_COORD;
05723             if (SelectBlock (&box, false))
05724               SetChangedFlag (true);
05725             break;
05726           }
05727 
05728           /* all logical connections */
05729         case F_Found:
05730           if (SelectByFlag (FOUNDFLAG, false))
05731             {
05732               Draw ();
05733               IncrementUndoSerialNumber ();
05734               SetChangedFlag (true);
05735             }
05736           break;
05737 
05738           /* all physical connections */
05739         case F_Connection:
05740           if (SelectByFlag (CONNECTEDFLAG, false))
05741             {
05742               Draw ();
05743               IncrementUndoSerialNumber ();
05744               SetChangedFlag (true);
05745             }
05746           break;
05747 
05748         default:
05749           AFAIL (unselect);
05750           break;
05751 
05752         }
05753     }
05754   return 0;
05755 }
05756 
05757 /* --------------------------------------------------------------------------- */
05758 
05759 static const char saveto_syntax[] =
05760   N_("SaveTo(Layout|LayoutAs,filename)\n"
05761   "SaveTo(AllConnections|AllUnusedPins|ElementConnections,filename)\n"
05762   "SaveTo(PasteBuffer,filename)");
05763 
05764 static const char saveto_help[] = N_("Saves data to a file.");
05765 
05766 /* %start-doc actions SaveTo
05767 
05768 @table @code
05769 
05770 @item Layout
05771 Saves the current layout.
05772 
05773 @item LayoutAs
05774 Saves the current layout, and remembers the filename used.
05775 
05776 @item AllConnections
05777 Save all connections to a file.
05778 
05779 @item AllUnusedPins
05780 List all unused pins to a file.
05781 
05782 @item ElementConnections
05783 Save connections to the element at the cursor to a file.
05784 
05785 @item PasteBuffer
05786 Save the content of the active Buffer to a file. This is the graphical way to create a footprint.
05787 
05788 @end table
05789 
05790 %end-doc */
05791 
05792 static int
05793 ActionSaveTo (int argc, char **argv, Coord x, Coord y)
05794 {
05795   char *function;
05796   char *name;
05797 
05798   function = ARG (0);
05799   
05800   if ( ! function || strcasecmp (function, "Layout") == 0)
05801     {
05802       if (SavePCB (PCB->Filename) == 0)
05803         SetChangedFlag (false);
05804       return 0;
05805     }
05806 
05807   if (argc != 2)
05808     AFAIL (saveto);
05809 
05810   name = argv[1];
05811 
05812   if (strcasecmp (function, "LayoutAs") == 0)
05813     {
05814       if (SavePCB (name) == 0)
05815         {
05816           SetChangedFlag (false);
05817           free (PCB->Filename);
05818           PCB->Filename = strdup (name);
05819           if (gui->notify_filename_changed != NULL)
05820             gui->notify_filename_changed ();
05821         }
05822       return 0;
05823     }
05824 
05825   if (strcasecmp (function, "AllConnections") == 0)
05826     {
05827       FILE *fp;
05828       bool result;
05829       if ((fp = CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
05830         {
05831           LookupConnectionsToAllElements (fp);
05832           fclose (fp);
05833           SetChangedFlag (true);
05834         }
05835       return 0;
05836     }
05837 
05838   if (strcasecmp (function, "AllUnusedPins") == 0)
05839     {
05840       FILE *fp;
05841       bool result;
05842       if ((fp = CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
05843         {
05844           LookupUnusedPins (fp);
05845           fclose (fp);
05846           SetChangedFlag (true);
05847         }
05848       return 0;
05849     }
05850 
05851   if (strcasecmp (function, "ElementConnections") == 0)
05852     {
05853       ElementType *element;
05854       void *ptrtmp;
05855       FILE *fp;
05856       bool result;
05857 
05858       if ((SearchScreen (Crosshair.X, Crosshair.Y, ELEMENT_TYPE,
05859                          &ptrtmp, &ptrtmp, &ptrtmp)) != NO_TYPE)
05860         {
05861           element = (ElementType *) ptrtmp;
05862           if ((fp =
05863                CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
05864             {
05865               LookupElementConnections (element, fp);
05866               fclose (fp);
05867               SetChangedFlag (true);
05868             }
05869         }
05870       return 0;
05871     }
05872 
05873   if (strcasecmp (function, "PasteBuffer") == 0)
05874     {
05875       return SaveBufferElements (name);
05876     }
05877 
05878   AFAIL (saveto);
05879 }
05880 
05881 /* --------------------------------------------------------------------------- */
05882 
05883 static const char savesettings_syntax[] =
05884   N_("SaveSettings()\n"
05885   "SaveSettings(local)");
05886 
05887 static const char savesettings_help[] = N_("Saves settings.");
05888 
05889 /* %start-doc actions SaveSettings
05890 
05891 If you pass no arguments, the settings are stored in
05892 @code{$HOME/.pcb/settings}.  If you pass the word @code{local} they're
05893 saved in @code{./pcb.settings}.
05894 
05895 %end-doc */
05896 
05897 static int
05898 ActionSaveSettings (int argc, char **argv, Coord x, Coord y)
05899 {
05900   int locally = argc > 0 ? (strncasecmp (argv[0], "local", 5) == 0) : 0;
05901   hid_save_settings (locally);
05902   return 0;
05903 }
05904 
05905 /* --------------------------------------------------------------------------- */
05906 
05907 static const char loadfrom_syntax[] =
05908   N_("LoadFrom(Layout|LayoutToBuffer|ElementToBuffer|Netlist|Revert,filename)");
05909 
05910 static const char loadfrom_help[] = N_("Load layout data from a file.");
05911 
05912 /* %start-doc actions LoadFrom
05913 
05914 This action assumes you know what the filename is.  The various GUIs
05915 should have a similar @code{Load} action where the filename is
05916 optional, and will provide their own file selection mechanism to let
05917 you choose the file name.
05918 
05919 @table @code
05920 
05921 @item Layout
05922 Loads an entire PCB layout, replacing the current one.
05923 
05924 @item LayoutToBuffer
05925 Loads an entire PCB layout to the paste buffer.
05926 
05927 @item ElementToBuffer
05928 Loads the given element file into the paste buffer.  Element files
05929 contain only a single @code{Element} definition, such as the
05930 ``newlib'' library uses.
05931 
05932 @item Netlist
05933 Loads a new netlist, replacing any current netlist.
05934 
05935 @item Revert
05936 Re-loads the current layout from its disk file, reverting any changes
05937 you may have made.
05938 
05939 @end table
05940 
05941 %end-doc */
05942 
05943 static int
05944 ActionLoadFrom (int argc, char **argv, Coord x, Coord y)
05945 {
05946   char *function;
05947   char *name;
05948 
05949   if (argc < 2)
05950     AFAIL (loadfrom);
05951 
05952   function = argv[0];
05953   name = argv[1];
05954 
05955   if (strcasecmp (function, "ElementToBuffer") == 0)
05956     {
05957       notify_crosshair_change (false);
05958       if (LoadElementToBuffer (PASTEBUFFER, name, true))
05959         SetMode (PASTEBUFFER_MODE);
05960       notify_crosshair_change (true);
05961     }
05962 
05963   else if (strcasecmp (function, "LayoutToBuffer") == 0)
05964     {
05965       notify_crosshair_change (false);
05966       if (LoadLayoutToBuffer (PASTEBUFFER, name))
05967         SetMode (PASTEBUFFER_MODE);
05968       notify_crosshair_change (true);
05969     }
05970 
05971   else if (strcasecmp (function, "Layout") == 0)
05972     {
05973       if (!PCB->Changed ||
05974           gui->confirm_dialog (_("OK to override layout data?"), 0))
05975         LoadPCB (name);
05976     }
05977 
05978   else if (strcasecmp (function, "Netlist") == 0)
05979     {
05980       if (PCB->Netlistname)
05981         free (PCB->Netlistname);
05982       PCB->Netlistname = StripWhiteSpaceAndDup (name);
05983       FreeLibraryMemory (&PCB->NetlistLib);
05984       ImportNetlist (PCB->Netlistname);
05985       NetlistChanged (1);
05986     }
05987   else if (strcasecmp (function, "Revert") == 0 && PCB->Filename
05988            && (!PCB->Changed
05989                || gui->confirm_dialog (_("OK to override changes?"), 0)))
05990     {
05991       RevertPCB ();
05992     }
05993 
05994   return 0;
05995 }
05996 
05997 /* --------------------------------------------------------------------------- */
05998 
05999 static const char new_syntax[] = N_("New([name])");
06000 
06001 static const char new_help[] = N_("Starts a new layout.");
06002 
06003 /* %start-doc actions New
06004 
06005 If a name is not given, one is prompted for.
06006 
06007 %end-doc */
06008 
06009 static int
06010 ActionNew (int argc, char **argv, Coord x, Coord y)
06011 {
06012   char *name = ARG (0);
06013 
06014   if (!PCB->Changed || gui->confirm_dialog (_("OK to clear layout data?"), 0))
06015     {
06016       if (name)
06017         name = strdup (name);
06018       else
06019         name = gui->prompt_for (_("Enter the layout name:"), "");
06020 
06021       if (!name)
06022         return 1;
06023 
06024       notify_crosshair_change (false);
06025       /* do emergency saving
06026        * clear the old struct and allocate memory for the new one
06027        */
06028       if (PCB->Changed && Settings.SaveInTMP)
06029         SaveInTMP ();
06030       RemovePCB (PCB);
06031       PCB = NULL;
06032       PCB = CreateNewPCB ();
06033       CreateNewPCBPost (PCB, 1);
06034 
06035       /* setup the new name and reset some values to default */
06036       free (PCB->Name);
06037       PCB->Name = name;
06038 
06039       ResetStackAndVisibility ();
06040       CenterDisplay (PCB->MaxWidth / 2, PCB->MaxHeight / 2, false);
06041       Redraw ();
06042 
06043       hid_action ("PCBChanged");
06044       notify_crosshair_change (true);
06045       return 0;
06046     }
06047   return 1;
06048 }
06049 
06054 void
06055 ActionBell (char *volume)
06056 {
06057   gui->beep ();
06058 }
06059 
06060 /* --------------------------------------------------------------------------- */
06061 
06062 static const char pastebuffer_syntax[] =
06063   N_("PasteBuffer(AddSelected|Clear|1..MAX_BUFFER)\n"
06064   "PasteBuffer(Rotate, 1..3)\n"
06065   "PasteBuffer(Convert|Save|Restore|Mirror)\n"
06066   "PasteBuffer(ToLayout, X, Y, units)");
06067 
06068 static const char pastebuffer_help[] =
06069   N_("Various operations on the paste buffer.");
06070 
06071 /* %start-doc actions PasteBuffer
06072 
06073 There are a number of paste buffers; the actual limit is a
06074 compile-time constant @code{MAX_BUFFER} in @file{globalconst.h}.  It
06075 is currently @code{5}.  One of these is the ``current'' paste buffer,
06076 often referred to as ``the'' paste buffer.
06077 
06078 @table @code
06079 
06080 @item AddSelected
06081 Copies the selected objects to the current paste buffer.
06082 
06083 @item Clear
06084 Remove all objects from the current paste buffer.
06085 
06086 @item Convert
06087 Convert the current paste buffer to an element.  Vias are converted to
06088 pins, lines are converted to pads.
06089 
06090 @item Restore
06091 Convert any elements in the paste buffer back to vias and lines.
06092 
06093 @item Mirror
06094 Flip all objects in the paste buffer vertically (up/down flip).  To mirror
06095 horizontally, combine this with rotations.
06096 
06097 @item Rotate
06098 Rotates the current buffer.  The number to pass is 1..3, where 1 means
06099 90 degrees counter clockwise, 2 means 180 degrees, and 3 means 90
06100 degrees clockwise (270 CCW).
06101 
06102 @item Save
06103 Saves any elements in the current buffer to the indicated file.
06104 
06105 @item ToLayout
06106 Pastes any elements in the current buffer to the indicated X, Y
06107 coordinates in the layout.  The @code{X} and @code{Y} are treated like
06108 @code{delta} is for many other objects.  For each, if it's prefixed by
06109 @code{+} or @code{-}, then that amount is relative to the last
06110 location.  Otherwise, it's absolute.  Units can be
06111 @code{mil} or @code{mm}; if unspecified, units are PCB's internal
06112 units, currently 1/100 mil.
06113 
06114 
06115 @item 1..MAX_BUFFER
06116 Selects the given buffer to be the current paste buffer.
06117 
06118 @end table
06119 
06120 %end-doc */
06121 
06122 static int
06123 ActionPasteBuffer (int argc, char **argv, Coord x, Coord y)
06124 {
06125   char *function = argc ? argv[0] : (char *)"";
06126   char *sbufnum = argc > 1 ? argv[1] : (char *)"";
06127   char *name;
06128   static char *default_file = NULL;
06129   int free_name = 0;
06130 
06131   notify_crosshair_change (false);
06132   if (function)
06133     {
06134       switch (GetFunctionID (function))
06135         {
06136           /* clear contents of paste buffer */
06137         case F_Clear:
06138           ClearBuffer (PASTEBUFFER);
06139           break;
06140 
06141           /* copies objects to paste buffer */
06142         case F_AddSelected:
06143           AddSelectedToBuffer (PASTEBUFFER, 0, 0, false);
06144           break;
06145 
06146           /* converts buffer contents into an element */
06147         case F_Convert:
06148           ConvertBufferToElement (PASTEBUFFER);
06149           break;
06150 
06151           /* break up element for editing */
06152         case F_Restore:
06153           SmashBufferElement (PASTEBUFFER);
06154           break;
06155 
06156           /* Mirror buffer */
06157         case F_Mirror:
06158           MirrorBuffer (PASTEBUFFER);
06159           break;
06160 
06161         case F_Rotate:
06162           if (sbufnum)
06163             {
06164               RotateBuffer (PASTEBUFFER, (BYTE) atoi (sbufnum));
06165         crosshair_update_range();
06166             }
06167           break;
06168 
06169         case F_Save:
06170           if (PASTEBUFFER->Data->ElementN == 0)
06171             {
06172               Message (_("Buffer has no elements!\n"));
06173               break;
06174             }
06175           free_name = 0;
06176           if (argc <= 1)
06177             {
06178               name = gui->fileselect (_("Save Paste Buffer As ..."),
06179                                       _("Choose a file to save the contents of the\n"
06180                                         "paste buffer to.\n"),
06181                                       default_file, ".fp", "footprint",
06182                                       0);
06183 
06184               if (default_file)
06185                 {
06186                   free (default_file);
06187                   default_file = NULL;
06188                 }
06189               if ( name && *name)
06190                 {
06191                   default_file = strdup (name);
06192                 }
06193               free_name = 1;
06194             }
06195               
06196           else
06197             name = argv[1];
06198 
06199           {
06200             FILE *exist;
06201 
06202             if ((exist = fopen (name, "r")))
06203               {
06204                 fclose (exist);
06205                 if (gui->
06206                     confirm_dialog (_("File exists!  Ok to overwrite?"), 0))
06207                   SaveBufferElements (name);
06208               }
06209             else
06210               SaveBufferElements (name);
06211 
06212             if (free_name && name)
06213               free (name);
06214           }
06215           break;
06216 
06217         case F_ToLayout:
06218           {
06219             static Coord oldx = 0, oldy = 0;
06220             Coord x, y;
06221             bool absolute;
06222 
06223             if (argc == 1)
06224               {
06225                 x = y = 0;
06226               }
06227             else if (argc == 3 || argc == 4)
06228               {
06229                 x = GetValue (ARG (1), ARG (3), &absolute);
06230                 if (!absolute)
06231                   x += oldx;
06232                 y = GetValue (ARG (2), ARG (3), &absolute);
06233                 if (!absolute)
06234                   y += oldy;
06235               }
06236             else
06237               {
06238                 notify_crosshair_change (true);
06239                 AFAIL (pastebuffer);
06240               }
06241 
06242             oldx = x;
06243             oldy = y;
06244             if (CopyPastebufferToLayout (x, y))
06245               SetChangedFlag (true);
06246           }
06247           break;
06248 
06249           /* set number */
06250         default:
06251           {
06252             int number = atoi (function);
06253 
06254             /* correct number */
06255             if (number)
06256               SetBufferNumber (number - 1);
06257           }
06258         }
06259     }
06260 
06261   notify_crosshair_change (true);
06262   return 0;
06263 }
06264 
06265 /* --------------------------------------------------------------------------- */
06266 
06267 static const char undo_syntax[] = N_("Undo()\n"
06268                                   "Undo(ClearList)");
06269 
06270 static const char undo_help[] = N_("Undo recent changes.");
06271 
06272 /* %start-doc actions Undo
06273 
06274 The unlimited undo feature of @code{Pcb} allows you to recover from
06275 most operations that materially affect you work.  Calling
06276 @code{Undo()} without any parameter recovers from the last (non-undo)
06277 operation. @code{ClearList} is used to release the allocated
06278 memory. @code{ClearList} is called whenever a new layout is started or
06279 loaded. See also @code{Redo} and @code{Atomic}.
06280 
06281 Note that undo groups operations by serial number; changes with the
06282 same serial number will be undone (or redone) as a group.  See
06283 @code{Atomic}.
06284 
06285 %end-doc */
06286 
06287 static int
06288 ActionUndo (int argc, char **argv, Coord x, Coord y)
06289 {
06290   char *function = ARG (0);
06291   if (!function || !*function)
06292     {
06293       /* don't allow undo in the middle of an operation */
06294       if (Settings.Mode != POLYGONHOLE_MODE &&
06295           Crosshair.AttachedObject.State != STATE_FIRST)
06296         return 1;
06297       if (Crosshair.AttachedBox.State != STATE_FIRST
06298           && Settings.Mode != ARC_MODE)
06299         return 1;
06300       /* undo the last operation */
06301 
06302       notify_crosshair_change (false);
06303       if ((Settings.Mode == POLYGON_MODE ||
06304            Settings.Mode == POLYGONHOLE_MODE) &&
06305           Crosshair.AttachedPolygon.PointN)
06306         {
06307           GoToPreviousPoint ();
06308           notify_crosshair_change (true);
06309           return 0;
06310         }
06311       /* move anchor point if undoing during line creation */
06312       if (Settings.Mode == LINE_MODE)
06313         {
06314           if (Crosshair.AttachedLine.State == STATE_SECOND)
06315             {
06316               if (TEST_FLAG (AUTODRCFLAG, PCB))
06317                 Undo (true);    /* undo the connection find */
06318               Crosshair.AttachedLine.State = STATE_FIRST;
06319               SetLocalRef (0, 0, false);
06320               notify_crosshair_change (true);
06321               return 0;
06322             }
06323           if (Crosshair.AttachedLine.State == STATE_THIRD)
06324             {
06325               int type;
06326               void *ptr1, *ptr3, *ptrtmp;
06327               LineType *ptr2;
06328               /* this search is guaranteed to succeed */
06329               SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
06330                                       &ptrtmp, &ptr3,
06331                                       Crosshair.AttachedLine.Point1.X,
06332                                       Crosshair.AttachedLine.Point1.Y, 0);
06333               ptr2 = (LineType *) ptrtmp;
06334 
06335               /* save both ends of line */
06336               Crosshair.AttachedLine.Point2.X = ptr2->Point1.X;
06337               Crosshair.AttachedLine.Point2.Y = ptr2->Point1.Y;
06338               if ((type = Undo (true)))
06339                 SetChangedFlag (true);
06340               /* check that the undo was of the right type */
06341               if ((type & UNDO_CREATE) == 0)
06342                 {
06343                   /* wrong undo type, restore anchor points */
06344                   Crosshair.AttachedLine.Point2.X =
06345                     Crosshair.AttachedLine.Point1.X;
06346                   Crosshair.AttachedLine.Point2.Y =
06347                     Crosshair.AttachedLine.Point1.Y;
06348                   notify_crosshair_change (true);
06349                   return 0;
06350                 }
06351               /* move to new anchor */
06352               Crosshair.AttachedLine.Point1.X =
06353                 Crosshair.AttachedLine.Point2.X;
06354               Crosshair.AttachedLine.Point1.Y =
06355                 Crosshair.AttachedLine.Point2.Y;
06356               /* check if an intermediate point was removed */
06357               if (type & UNDO_REMOVE)
06358                 {
06359                   /* this search should find the restored line */
06360                   SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
06361                                           &ptrtmp,
06362                                           &ptr3,
06363                                           Crosshair.AttachedLine.Point2.X,
06364                                           Crosshair.AttachedLine.Point2.Y, 0);
06365                   ptr2 = (LineType *) ptrtmp;
06366                   if (TEST_FLAG (AUTODRCFLAG, PCB))
06367                     {
06368                       /* undo loses CONNECTEDFLAG and FOUNDFLAG */
06369                       SET_FLAG(CONNECTEDFLAG, ptr2);
06370                       SET_FLAG(FOUNDFLAG, ptr2);
06371                       DrawLine (CURRENT, ptr2);
06372                     }
06373                   Crosshair.AttachedLine.Point1.X =
06374                     Crosshair.AttachedLine.Point2.X = ptr2->Point2.X;
06375                   Crosshair.AttachedLine.Point1.Y =
06376                     Crosshair.AttachedLine.Point2.Y = ptr2->Point2.Y;
06377                 }
06378               FitCrosshairIntoGrid (Crosshair.X, Crosshair.Y);
06379               AdjustAttachedObjects ();
06380               if (--addedLines == 0)
06381                 {
06382                   Crosshair.AttachedLine.State = STATE_SECOND;
06383                   lastLayer = CURRENT;
06384                 }
06385               else
06386                 {
06387                   /* this search is guaranteed to succeed too */
06388                   SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
06389                                           &ptrtmp,
06390                                           &ptr3,
06391                                           Crosshair.AttachedLine.Point1.X,
06392                                           Crosshair.AttachedLine.Point1.Y, 0);
06393                   ptr2 = (LineType *) ptrtmp;
06394                   lastLayer = (LayerType *) ptr1;
06395                 }
06396               notify_crosshair_change (true);
06397               return 0;
06398             }
06399         }
06400       if (Settings.Mode == ARC_MODE)
06401         {
06402           if (Crosshair.AttachedBox.State == STATE_SECOND)
06403             {
06404               Crosshair.AttachedBox.State = STATE_FIRST;
06405               notify_crosshair_change (true);
06406               return 0;
06407             }
06408           if (Crosshair.AttachedBox.State == STATE_THIRD)
06409             {
06410               void *ptr1, *ptr2, *ptr3;
06411               BoxType *bx;
06412               /* guaranteed to succeed */
06413               SearchObjectByLocation (ARC_TYPE, &ptr1, &ptr2, &ptr3,
06414                                       Crosshair.AttachedBox.Point1.X,
06415                                       Crosshair.AttachedBox.Point1.Y, 0);
06416               bx = GetArcEnds ((ArcType *) ptr2);
06417               Crosshair.AttachedBox.Point1.X =
06418                 Crosshair.AttachedBox.Point2.X = bx->X1;
06419               Crosshair.AttachedBox.Point1.Y =
06420                 Crosshair.AttachedBox.Point2.Y = bx->Y1;
06421               AdjustAttachedObjects ();
06422               if (--addedLines == 0)
06423                 Crosshair.AttachedBox.State = STATE_SECOND;
06424             }
06425         }
06426       /* undo the last destructive operation */
06427       if (Undo (true))
06428         SetChangedFlag (true);
06429     }
06430   else if (function)
06431     {
06432       switch (GetFunctionID (function))
06433         {
06434           /* clear 'undo objects' list */
06435         case F_ClearList:
06436           ClearUndoList (false);
06437           break;
06438         }
06439     }
06440   notify_crosshair_change (true);
06441   return 0;
06442 }
06443 
06444 /* --------------------------------------------------------------------------- */
06445 
06446 static const char redo_syntax[] = N_("Redo()");
06447 
06448 static const char redo_help[] = N_("Redo recent \"undo\" operations.");
06449 
06450 /* %start-doc actions Redo
06451 
06452 This routine allows you to recover from the last undo command.  You
06453 might want to do this if you thought that undo was going to revert
06454 something other than what it actually did (in case you are confused
06455 about which operations are un-doable), or if you have been backing up
06456 through a long undo list and over-shoot your stopping point.  Any
06457 change that is made since the undo in question will trim the redo
06458 list.  For example if you add ten lines, then undo three of them you
06459 could use redo to put them back, but if you move a line on the board
06460 before performing the redo, you will lose the ability to "redo" the
06461 three "undone" lines.
06462 
06463 %end-doc */
06464 
06465 static int
06466 ActionRedo (int argc, char **argv, Coord x, Coord y)
06467 {
06468   if (((Settings.Mode == POLYGON_MODE ||
06469         Settings.Mode == POLYGONHOLE_MODE) &&
06470        Crosshair.AttachedPolygon.PointN) ||
06471       Crosshair.AttachedLine.State == STATE_SECOND)
06472     return 1;
06473   notify_crosshair_change (false);
06474   if (Redo (true))
06475     {
06476       SetChangedFlag (true);
06477       if (Settings.Mode == LINE_MODE &&
06478           Crosshair.AttachedLine.State != STATE_FIRST)
06479         {
06480           LineType *line = g_list_last (CURRENT->Line)->data;
06481           Crosshair.AttachedLine.Point1.X =
06482             Crosshair.AttachedLine.Point2.X = line->Point2.X;
06483           Crosshair.AttachedLine.Point1.Y =
06484             Crosshair.AttachedLine.Point2.Y = line->Point2.Y;
06485           addedLines++;
06486         }
06487     }
06488   notify_crosshair_change (true);
06489   return 0;
06490 }
06491 
06492 /* --------------------------------------------------------------------------- */
06493 
06494 static const char polygon_syntax[] = N_("Polygon(Close|PreviousPoint)");
06495 
06496 static const char polygon_help[] = N_("Some polygon related stuff.");
06497 
06498 /* %start-doc actions Polygon
06499 
06500 Polygons need a special action routine to make life easier.
06501 
06502 @table @code
06503 
06504 @item Close
06505 Creates the final segment of the polygon.  This may fail if clipping
06506 to 45 degree lines is switched on, in which case a warning is issued.
06507 
06508 @item PreviousPoint
06509 Resets the newly entered corner to the previous one. The Undo action
06510 will call Polygon(PreviousPoint) when appropriate to do so.
06511 
06512 @end table
06513 
06514 %end-doc */
06515 
06516 static int
06517 ActionPolygon (int argc, char **argv, Coord x, Coord y)
06518 {
06519   char *function = ARG (0);
06520   if (function && Settings.Mode == POLYGON_MODE)
06521     {
06522       notify_crosshair_change (false);
06523       switch (GetFunctionID (function))
06524         {
06525           /* close open polygon if possible */
06526         case F_Close:
06527           ClosePolygon ();
06528           break;
06529 
06530           /* go back to the previous point */
06531         case F_PreviousPoint:
06532           GoToPreviousPoint ();
06533           break;
06534         }
06535       notify_crosshair_change (true);
06536     }
06537   return 0;
06538 }
06539 
06540 /* --------------------------------------------------------------------------- */
06541 
06542 static const char routestyle_syntax[] = N_("RouteStyle(1|2|3|4)");
06543 
06544 static const char routestyle_help[] =
06545   N_("Copies the indicated routing style into the current sizes.");
06546 
06547 /* %start-doc actions RouteStyle
06548 
06549 %end-doc */
06550 
06551 static int
06552 ActionRouteStyle (int argc, char **argv, Coord x, Coord y)
06553 {
06554   char *str = ARG (0);
06555   RouteStyleType *rts;
06556   int number;
06557 
06558   if (str)
06559     {
06560       number = atoi (str);
06561       if (number > 0 && number <= NUM_STYLES)
06562         {
06563           rts = &PCB->RouteStyle[number - 1];
06564           SetLineSize (rts->Thick);
06565           SetViaSize (rts->Diameter, true);
06566           SetViaDrillingHole (rts->Hole, true);
06567           SetKeepawayWidth (rts->Keepaway);
06568           hid_action("RouteStylesChanged");
06569         }
06570     }
06571   return 0;
06572 }
06573 
06574 
06575 /* --------------------------------------------------------------------------- */
06576 
06577 static const char moveobject_syntax[] = N_("MoveObject(X,Y,dim)");
06578 
06579 static const char moveobject_help[] =
06580   N_("Moves the object under the crosshair.");
06581 
06582 /* %start-doc actions MoveObject
06583 
06584 The @code{X} and @code{Y} are treated like @code{delta} is for many
06585 other objects.  For each, if it's prefixed by @code{+} or @code{-},
06586 then that amount is relative.  Otherwise, it's absolute.  Units can be
06587 @code{mil} or @code{mm}; if unspecified, units are PCB's internal
06588 units, currently 1/100 mil.
06589 
06590 %end-doc */
06591 
06592 static int
06593 ActionMoveObject (int argc, char **argv, Coord x, Coord y)
06594 {
06595   char *x_str = ARG (0);
06596   char *y_str = ARG (1);
06597   char *units = ARG (2);
06598   Coord nx, ny;
06599   bool absolute1, absolute2;
06600   void *ptr1, *ptr2, *ptr3;
06601   int type;
06602 
06603   ny = GetValue (y_str, units, &absolute1);
06604   nx = GetValue (x_str, units, &absolute2);
06605 
06606   type = SearchScreen (x, y, MOVE_TYPES, &ptr1, &ptr2, &ptr3);
06607   if (type == NO_TYPE)
06608     {
06609       Message (_("Nothing found under crosshair\n"));
06610       return 1;
06611     }
06612   if (absolute1)
06613     nx -= x;
06614   if (absolute2)
06615     ny -= y;
06616   Crosshair.AttachedObject.RubberbandN = 0;
06617   if (TEST_FLAG (RUBBERBANDFLAG, PCB))
06618     LookupRubberbandLines (type, ptr1, ptr2, ptr3);
06619   if (type == ELEMENT_TYPE)
06620     LookupRatLines (type, ptr1, ptr2, ptr3);
06621   MoveObjectAndRubberband (type, ptr1, ptr2, ptr3, nx, ny);
06622   SetChangedFlag (true);
06623   return 0;
06624 }
06625 
06626 /* --------------------------------------------------------------------------- */
06627 
06628 static const char movetocurrentlayer_syntax[] =
06629   N_("MoveToCurrentLayer(Object|SelectedObjects)");
06630 
06631 static const char movetocurrentlayer_help[] =
06632   N_("Moves objects to the current layer.");
06633 
06634 /* %start-doc actions MoveToCurrentLayer
06635 
06636 Note that moving an element from a component layer to a solder layer,
06637 or from solder to component, won't automatically flip it.  Use the
06638 @code{Flip()} action to do that.
06639 
06640 %end-doc */
06641 
06642 static int
06643 ActionMoveToCurrentLayer (int argc, char **argv, Coord x, Coord y)
06644 {
06645   char *function = ARG (0);
06646   if (function)
06647     {
06648       switch (GetFunctionID (function))
06649         {
06650         case F_Object:
06651           {
06652             int type;
06653             void *ptr1, *ptr2, *ptr3;
06654 
06655             gui->get_coords (_("Select an Object"), &x, &y);
06656             if ((type =
06657                  SearchScreen (x, y, MOVETOLAYER_TYPES,
06658                                &ptr1, &ptr2, &ptr3)) != NO_TYPE)
06659               if (MoveObjectToLayer (type, ptr1, ptr2, ptr3, CURRENT, false))
06660                 SetChangedFlag (true);
06661             break;
06662           }
06663 
06664         case F_SelectedObjects:
06665         case F_Selected:
06666           if (MoveSelectedObjectsToLayer (CURRENT))
06667             SetChangedFlag (true);
06668           break;
06669         }
06670     }
06671   return 0;
06672 }
06673 
06674 
06675 static const char setsame_syntax[] = N_("SetSame()");
06676 
06677 static const char setsame_help[] =
06678   N_("Sets current layer and sizes to match indicated item.");
06679 
06680 /* %start-doc actions SetSame
06681 
06682 When invoked over any line, arc, polygon, or via, this changes the
06683 current layer to be the layer that item is on, and changes the current
06684 sizes (thickness, keepaway, drill, etc) according to that item.
06685 
06686 %end-doc */
06687 
06688 static int
06689 ActionSetSame (int argc, char **argv, Coord x, Coord y)
06690 {
06691   void *ptr1, *ptr2, *ptr3;
06692   int type;
06693   LayerType *layer = CURRENT;
06694 
06695   type = SearchScreen (x, y, CLONE_TYPES, &ptr1, &ptr2, &ptr3);
06696 /* set layer current and size from line or arc */
06697   switch (type)
06698     {
06699     case LINE_TYPE:
06700       notify_crosshair_change (false);
06701       Settings.LineThickness = ((LineType *) ptr2)->Thickness;
06702       Settings.Keepaway = ((LineType *) ptr2)->Clearance / 2;
06703       layer = (LayerType *) ptr1;
06704       if (Settings.Mode != LINE_MODE)
06705         SetMode (LINE_MODE);
06706       notify_crosshair_change (true);
06707       hid_action ("RouteStylesChanged");
06708       break;
06709 
06710     case ARC_TYPE:
06711       notify_crosshair_change (false);
06712       Settings.LineThickness = ((ArcType *) ptr2)->Thickness;
06713       Settings.Keepaway = ((ArcType *) ptr2)->Clearance / 2;
06714       layer = (LayerType *) ptr1;
06715       if (Settings.Mode != ARC_MODE)
06716         SetMode (ARC_MODE);
06717       notify_crosshair_change (true);
06718       hid_action ("RouteStylesChanged");
06719       break;
06720 
06721     case POLYGON_TYPE:
06722       layer = (LayerType *) ptr1;
06723       break;
06724 
06725     case VIA_TYPE:
06726       notify_crosshair_change (false);
06727       Settings.ViaThickness = ((PinType *) ptr2)->Thickness;
06728       Settings.ViaDrillingHole = ((PinType *) ptr2)->DrillingHole;
06729       Settings.Keepaway = ((PinType *) ptr2)->Clearance / 2;
06730       if (Settings.Mode != VIA_MODE)
06731         SetMode (VIA_MODE);
06732       notify_crosshair_change (true);
06733       hid_action ("RouteStylesChanged");
06734       break;
06735 
06736     default:
06737       return 1;
06738     }
06739   if (layer != CURRENT)
06740     {
06741       ChangeGroupVisibility (GetLayerNumber (PCB->Data, layer), true, true);
06742       Redraw ();
06743     }
06744   return 0;
06745 }
06746 
06747 
06748 /* --------------------------------------------------------------------------- */
06749 
06750 static const char setflag_syntax[] =
06751   N_("SetFlag(Object|Selected|SelectedObjects, flag)\n"
06752   "SetFlag(SelectedLines|SelectedPins|SelectedVias, flag)\n"
06753   "SetFlag(SelectedPads|SelectedTexts|SelectedNames, flag)\n"
06754   "SetFlag(SelectedElements, flag)\n"
06755   "flag = square | octagon | thermal | join");
06756 
06757 static const char setflag_help[] = N_("Sets flags on objects.");
06758 
06759 /* %start-doc actions SetFlag
06760 
06761 Turns the given flag on, regardless of its previous setting.  See
06762 @code{ChangeFlag}.
06763 
06764 @example
06765 SetFlag(SelectedPins,thermal)
06766 @end example
06767 
06768 %end-doc */
06769 
06770 static int
06771 ActionSetFlag (int argc, char **argv, Coord x, Coord y)
06772 {
06773   char *function = ARG (0);
06774   char *flag = ARG (1);
06775   ChangeFlag (function, flag, 1, "SetFlag");
06776   return 0;
06777 }
06778 
06779 /* --------------------------------------------------------------------------- */
06780 
06781 static const char clrflag_syntax[] =
06782   N_("ClrFlag(Object|Selected|SelectedObjects, flag)\n"
06783   "ClrFlag(SelectedLines|SelectedPins|SelectedVias, flag)\n"
06784   "ClrFlag(SelectedPads|SelectedTexts|SelectedNames, flag)\n"
06785   "ClrFlag(SelectedElements, flag)\n"
06786   "flag = square | octagon | thermal | join");
06787 
06788 static const char clrflag_help[] = N_("Clears flags on objects.");
06789 
06790 /* %start-doc actions ClrFlag
06791 
06792 Turns the given flag off, regardless of its previous setting.  See
06793 @code{ChangeFlag}.
06794 
06795 @example
06796 ClrFlag(SelectedLines,join)
06797 @end example
06798 
06799 %end-doc */
06800 
06801 static int
06802 ActionClrFlag (int argc, char **argv, Coord x, Coord y)
06803 {
06804   char *function = ARG (0);
06805   char *flag = ARG (1);
06806   ChangeFlag (function, flag, 0, "ClrFlag");
06807   return 0;
06808 }
06809 
06810 /* --------------------------------------------------------------------------- */
06811 
06812 static const char changeflag_syntax[] =
06813   N_("ChangeFlag(Object|Selected|SelectedObjects, flag, value)\n"
06814   "ChangeFlag(SelectedLines|SelectedPins|SelectedVias, flag, value)\n"
06815   "ChangeFlag(SelectedPads|SelectedTexts|SelectedNames, flag, value)\n"
06816   "ChangeFlag(SelectedElements, flag, value)\n"
06817   "flag = square | octagon | thermal | join\n"
06818   "value = 0 | 1");
06819 
06820 static const char changeflag_help[] = N_("Sets or clears flags on objects.");
06821 
06822 /* %start-doc actions ChangeFlag
06823 
06824 Toggles the given flag on the indicated object(s).  The flag may be
06825 one of the flags listed above (square, octagon, thermal, join).  The
06826 value may be the number 0 or 1.  If the value is 0, the flag is
06827 cleared.  If the value is 1, the flag is set.
06828 
06829 %end-doc */
06830 
06831 static int
06832 ActionChangeFlag (int argc, char **argv, Coord x, Coord y)
06833 {
06834   char *function = ARG (0);
06835   char *flag = ARG (1);
06836   int value = argc > 2 ? atoi (argv[2]) : -1;
06837   if (value != 0 && value != 1)
06838     AFAIL (changeflag);
06839 
06840   ChangeFlag (function, flag, value, "ChangeFlag");
06841   return 0;
06842 }
06843 
06844 
06845 static void
06846 ChangeFlag (char *what, char *flag_name, int value, char *cmd_name)
06847 {
06848   bool (*set_object) (int, void *, void *, void *);
06849   bool (*set_selected) (int);
06850 
06851   if (NSTRCMP (flag_name, "square") == 0)
06852     {
06853       set_object = value ? SetObjectSquare : ClrObjectSquare;
06854       set_selected = value ? SetSelectedSquare : ClrSelectedSquare;
06855     }
06856   else if (NSTRCMP (flag_name, "octagon") == 0)
06857     {
06858       set_object = value ? SetObjectOctagon : ClrObjectOctagon;
06859       set_selected = value ? SetSelectedOctagon : ClrSelectedOctagon;
06860     }
06861   else if (NSTRCMP (flag_name, "join") == 0)
06862     {
06863       /* Note: these are backwards, because the flag is "clear" but
06864          the command is "join".  */
06865       set_object = value ? ClrObjectJoin : SetObjectJoin;
06866       set_selected = value ? ClrSelectedJoin : SetSelectedJoin;
06867     }
06868   else
06869     {
06870       Message (_("%s():  Flag \"%s\" is not valid\n"), cmd_name, flag_name);
06871       return;
06872     }
06873 
06874   switch (GetFunctionID (what))
06875     {
06876     case F_Object:
06877       {
06878         int type;
06879         void *ptr1, *ptr2, *ptr3;
06880 
06881         if ((type =
06882              SearchScreen (Crosshair.X, Crosshair.Y, CHANGESIZE_TYPES,
06883                            &ptr1, &ptr2, &ptr3)) != NO_TYPE)
06884           if (TEST_FLAG (LOCKFLAG, (PinType *) ptr2))
06885             Message (_("Sorry, the object is locked\n"));
06886         if (set_object (type, ptr1, ptr2, ptr3))
06887           SetChangedFlag (true);
06888         break;
06889       }
06890 
06891     case F_SelectedVias:
06892       if (set_selected (VIA_TYPE))
06893         SetChangedFlag (true);
06894       break;
06895 
06896     case F_SelectedPins:
06897       if (set_selected (PIN_TYPE))
06898         SetChangedFlag (true);
06899       break;
06900 
06901     case F_SelectedPads:
06902       if (set_selected (PAD_TYPE))
06903         SetChangedFlag (true);
06904       break;
06905 
06906     case F_SelectedLines:
06907       if (set_selected (LINE_TYPE))
06908         SetChangedFlag (true);
06909       break;
06910 
06911     case F_SelectedTexts:
06912       if (set_selected (TEXT_TYPE))
06913         SetChangedFlag (true);
06914       break;
06915 
06916     case F_SelectedNames:
06917       if (set_selected (ELEMENTNAME_TYPE))
06918         SetChangedFlag (true);
06919       break;
06920 
06921     case F_SelectedElements:
06922       if (set_selected (ELEMENT_TYPE))
06923         SetChangedFlag (true);
06924       break;
06925 
06926     case F_Selected:
06927     case F_SelectedObjects:
06928       if (set_selected (CHANGESIZE_TYPES))
06929         SetChangedFlag (true);
06930       break;
06931     }
06932 }
06933 
06934 /* --------------------------------------------------------------------------- */
06935 
06936 static const char executefile_syntax[] = N_("ExecuteFile(filename)");
06937 
06938 static const char executefile_help[] = N_("Run actions from the given file.");
06939 
06940 /* %start-doc actions ExecuteFile
06941 
06942 Lines starting with @code{#} are ignored.
06943 
06944 %end-doc */
06945 
06946 static int
06947 ActionExecuteFile (int argc, char **argv, Coord x, Coord y)
06948 {
06949   FILE *fp;
06950   char *fname;
06951   char line[256];
06952   int n = 0;
06953   char *sp;
06954 
06955   if (argc != 1)
06956     AFAIL (executefile);
06957 
06958   fname = argv[0];
06959 
06960   if ((fp = fopen (fname, "r")) == NULL)
06961     {
06962       fprintf (stderr, _("Could not open actions file \"%s\".\n"), fname);
06963       return 1;
06964     }
06965 
06966   defer_updates = 1;
06967   defer_needs_update = 0;
06968   while (fgets (line, sizeof (line), fp) != NULL)
06969     {
06970       n++;
06971       sp = line;
06972 
06973       /* eat the trailing newline */
06974       while (*sp && *sp != '\r' && *sp != '\n')
06975         sp++;
06976       *sp = '\0';
06977 
06978       /* eat leading spaces and tabs */
06979       sp = line;
06980       while (*sp && (*sp == ' ' || *sp == '\t'))
06981         sp++;
06982 
06983       /* 
06984        * if we have anything left and its not a comment line
06985        * then execute it
06986        */
06987 
06988       if (*sp && *sp != '#')
06989         {
06990           /*Message ("%s : line %-3d : \"%s\"\n", fname, n, sp);*/
06991           hid_parse_actions (sp);
06992         }
06993     }
06994 
06995   defer_updates = 0;
06996   if (defer_needs_update)
06997     {
06998       IncrementUndoSerialNumber ();
06999       gui->invalidate_all ();
07000     }
07001   fclose (fp);
07002   return 0;
07003 }
07004 
07005 /* --------------------------------------------------------------------------- */
07006 
07007 static int
07008 ActionPSCalib (int argc, char **argv, Coord x, Coord y)
07009 {
07010   HID *ps = hid_find_exporter ("ps");
07011   ps->calibrate (0.0,0.0);
07012   return 0;
07013 }
07014 
07015 /* --------------------------------------------------------------------------- */
07016 
07017 static ElementType *element_cache = NULL;
07018 
07019 static ElementType *
07020 find_element_by_refdes (char *refdes)
07021 {
07022   if (element_cache
07023       && NAMEONPCB_NAME(element_cache)
07024       && strcmp (NAMEONPCB_NAME(element_cache), refdes) == 0)
07025     return element_cache;
07026 
07027   ELEMENT_LOOP (PCB->Data);
07028   {
07029     if (NAMEONPCB_NAME(element)
07030         && strcmp (NAMEONPCB_NAME(element), refdes) == 0)
07031       {
07032         element_cache = element;
07033         return element_cache;
07034       }
07035   }
07036   END_LOOP;
07037   return NULL;
07038 }
07039 
07040 static AttributeType *
07041 lookup_attr (AttributeListType *list, const char *name)
07042 {
07043   int i;
07044   for (i=0; i<list->Number; i++)
07045     if (strcmp (list->List[i].name, name) == 0)
07046       return & list->List[i];
07047   return NULL;
07048 }
07049 
07050 static void
07051 delete_attr (AttributeListType *list, AttributeType *attr)
07052 {
07053   int idx = attr - list->List;
07054   if (idx < 0 || idx >= list->Number)
07055     return;
07056   if (list->Number - idx > 1)
07057     memmove (attr, attr+1, (list->Number - idx - 1) * sizeof(AttributeType));
07058   list->Number --;
07059 }
07060 
07061 /* ---------------------------------------------------------------- */
07062 static const char elementlist_syntax[] =
07063   N_("ElementList(Start|Done|Need,<refdes>,<footprint>,<value>)");
07064 
07065 static const char elementlist_help[] =
07066   N_("Adds the given element if it doesn't already exist.");
07067 
07068 /* %start-doc actions elementlist
07069 
07070 @table @code
07071 
07072 @item Start
07073 Indicates the start of an element list; call this before any Need
07074 actions.
07075 
07076 @item Need
07077 Searches the board for an element with a matching refdes.
07078 
07079 If found, the value and footprint are updated.
07080 
07081 If not found, a new element is created with the given footprint and value.
07082 
07083 @item Done
07084 Compares the list of elements needed since the most recent
07085 @code{start} with the list of elements actually on the board.  Any
07086 elements that weren't listed are selected, so that the user may delete
07087 them.
07088 
07089 @end table
07090 
07091 %end-doc */
07092 
07093 static int number_of_footprints_not_found;
07094 
07095 static int
07096 parse_layout_attribute_units (char *name, int def)
07097 {
07098   const char *as = AttributeGet (PCB, name);
07099   if (!as)
07100     return def;
07101   return GetValue (as, NULL, NULL);
07102 }
07103 
07104 static int
07105 ActionElementList (int argc, char **argv, Coord x, Coord y)
07106 {
07107   ElementType *e = NULL;
07108   char *refdes, *value, *footprint, *old;
07109   char *args[3];
07110   char *function;
07111 
07112   if (argc < 1)
07113     AFAIL (elementlist);
07114 
07115   function = argv[0];
07116 
07117 #ifdef DEBUG
07118   printf("Entered ActionElementList, executing function %s\n", function);
07119 #endif
07120 
07121   if (strcasecmp (function, "start") == 0)
07122     {
07123       ELEMENT_LOOP (PCB->Data);
07124       {
07125         CLEAR_FLAG (FOUNDFLAG, element);
07126       }
07127       END_LOOP;
07128       element_cache = NULL;
07129       number_of_footprints_not_found = 0;
07130       return 0;
07131     }
07132 
07133   if (strcasecmp (function, "done") == 0)
07134     {
07135       ELEMENT_LOOP (PCB->Data);
07136       {
07137         if (TEST_FLAG (FOUNDFLAG, element))
07138           {
07139             CLEAR_FLAG (FOUNDFLAG, element);
07140           }
07141         else if (! EMPTY_STRING_P (NAMEONPCB_NAME (element)))
07142           {
07143             /* Unnamed elements should remain untouched */
07144             SET_FLAG (SELECTEDFLAG, element);
07145           }
07146       }
07147       END_LOOP;
07148       if (number_of_footprints_not_found > 0)
07149         gui->confirm_dialog (_("Not all requested footprints were found.\n"
07150                              "See the message log for details"),
07151                              "Ok", NULL);
07152       return 0;
07153     }
07154 
07155   if (strcasecmp (function, "need") != 0)
07156     AFAIL (elementlist);
07157 
07158   if (argc != 4)
07159     AFAIL (elementlist);
07160 
07161   argc --;
07162   argv ++;
07163 
07164   refdes = ARG(0);
07165   footprint = ARG(1);
07166   value = ARG(2);
07167 
07168   args[0] = footprint;
07169   args[1] = refdes;
07170   args[2] = value;
07171 
07172 #ifdef DEBUG
07173   printf("  ... footprint = %s\n", footprint);
07174   printf("  ... refdes = %s\n", refdes);
07175   printf("  ... value = %s\n", value);
07176 #endif
07177 
07178   e = find_element_by_refdes (refdes);
07179 
07180   if (!e)
07181     {
07182       Coord nx, ny, d;
07183 
07184 #ifdef DEBUG
07185       printf("  ... Footprint not on board, need to add it.\n");
07186 #endif
07187       /* Not on board, need to add it. */
07188       if (LoadFootprint(argc, args, x, y))
07189         {
07190           number_of_footprints_not_found ++;
07191           return 1;
07192         }
07193 
07194       nx = PCB->MaxWidth / 2;
07195       ny = PCB->MaxHeight / 2;
07196       d = MIN (PCB->MaxWidth, PCB->MaxHeight) / 10;
07197 
07198       nx = parse_layout_attribute_units ("import::newX", nx);
07199       ny = parse_layout_attribute_units ("import::newY", ny);
07200       d = parse_layout_attribute_units ("import::disperse", d);
07201 
07202       if (d > 0)
07203         {
07204           nx += rand () % (d*2) - d;
07205           ny += rand () % (d*2) - d;
07206         }
07207 
07208       if (nx < 0)
07209         nx = 0;
07210       if (nx >= PCB->MaxWidth)
07211         nx = PCB->MaxWidth - 1;
07212       if (ny < 0)
07213         ny = 0;
07214       if (ny >= PCB->MaxHeight)
07215         ny = PCB->MaxHeight - 1;
07216 
07217       /* Place components onto center of board. */
07218       if (CopyPastebufferToLayout (nx, ny))
07219         SetChangedFlag (true);
07220     }
07221 
07222   else if (e && DESCRIPTION_NAME(e) && strcmp (DESCRIPTION_NAME(e), footprint) != 0)
07223     {
07224       int er, pr, i;
07225       Coord mx, my;
07226       ElementType *pe;
07227 
07228 #ifdef DEBUG
07229       printf("  ... Footprint on board, but different from footprint loaded.\n");
07230 #endif
07231       /* Different footprint, we need to swap them out.  */
07232       if (LoadFootprint(argc, args, x, y))
07233         {
07234           number_of_footprints_not_found ++;
07235           return 1;
07236         }
07237 
07238       er = ElementOrientation (e);
07239       pe = PASTEBUFFER->Data->Element->data;
07240       if (!FRONT (e))
07241         MirrorElementCoordinates (PASTEBUFFER->Data, pe, pe->MarkY*2 - PCB->MaxHeight);
07242       pr = ElementOrientation (pe);
07243 
07244       mx = e->MarkX;
07245       my = e->MarkY;
07246 
07247       if (er != pr)
07248         RotateElementLowLevel (PASTEBUFFER->Data, pe, pe->MarkX, pe->MarkY, (er-pr+4)%4);
07249 
07250       for (i=0; i<MAX_ELEMENTNAMES; i++)
07251         {
07252           pe->Name[i].X = e->Name[i].X - mx + pe->MarkX ;
07253           pe->Name[i].Y = e->Name[i].Y - my + pe->MarkY ;
07254           pe->Name[i].Direction = e->Name[i].Direction;
07255           pe->Name[i].Scale = e->Name[i].Scale;
07256         }
07257 
07258       RemoveElement (e);
07259 
07260       if (CopyPastebufferToLayout (mx, my))
07261         SetChangedFlag (true);
07262     }
07263 
07264   /* Now reload footprint */
07265   element_cache = NULL;
07266   e = find_element_by_refdes (refdes);
07267 
07268   old = ChangeElementText (PCB, PCB->Data, e, NAMEONPCB_INDEX, strdup (refdes));
07269   if (old)
07270     free(old);
07271   old = ChangeElementText (PCB, PCB->Data, e, VALUE_INDEX, strdup (value));
07272   if (old)
07273     free(old);
07274 
07275   SET_FLAG (FOUNDFLAG, e);
07276 
07277 #ifdef DEBUG
07278   printf(" ... Leaving ActionElementList.\n");
07279 #endif
07280 
07281   return 0;
07282 }
07283 
07284 /* ---------------------------------------------------------------- */
07285 static const char elementsetattr_syntax[] =
07286   N_("ElementSetAttr(refdes,name[,value])");
07287 
07288 static const char elementsetattr_help[] =
07289   N_("Sets or clears an element-specific attribute.");
07290 
07291 /* %start-doc actions elementsetattr
07292 
07293 If a value is specified, the named attribute is added (if not already
07294 present) or changed (if it is) to the given value.  If the value is
07295 not specified, the given attribute is removed if present.
07296 
07297 %end-doc */
07298 
07299 static int
07300 ActionElementSetAttr (int argc, char **argv, Coord x, Coord y)
07301 {
07302   ElementType *e = NULL;
07303   char *refdes, *name, *value;
07304   AttributeType *attr;
07305 
07306   if (argc < 2)
07307     {
07308       AFAIL (elementsetattr);
07309     }
07310 
07311   refdes = argv[0];
07312   name = argv[1];
07313   value = ARG(2);
07314 
07315   ELEMENT_LOOP (PCB->Data);
07316   {
07317     if (NSTRCMP (refdes, NAMEONPCB_NAME (element)) == 0)
07318       {
07319         e = element;
07320         break;
07321       }
07322   }
07323   END_LOOP;
07324 
07325   if (!e)
07326     {
07327       Message(_("Cannot change attribute of %s - element not found\n"), refdes);
07328       return 1;
07329     }
07330 
07331   attr = lookup_attr (&e->Attributes, name);
07332 
07333   if (attr && value)
07334     {
07335       free (attr->value);
07336       attr->value = strdup (value);
07337     }
07338   if (attr && ! value)
07339     {
07340       delete_attr (& e->Attributes, attr);
07341     }
07342   if (!attr && value)
07343     {
07344       CreateNewAttribute (& e->Attributes, name, value);
07345     }
07346 
07347   return 0;
07348 }
07349 
07350 /* ---------------------------------------------------------------- */
07351 static const char execcommand_syntax[] = N_("ExecCommand(command)");
07352 
07353 static const char execcommand_help[] = N_("Runs a command.");
07354 
07355 /* %start-doc actions execcommand
07356 
07357 Runs the given command, which is a system executable.
07358 
07359 %end-doc */
07360 
07361 static int
07362 ActionExecCommand (int argc, char **argv, Coord x, Coord y)
07363 {
07364   char *command;
07365 
07366   if (argc < 1)
07367     {
07368       AFAIL (execcommand);
07369     }
07370 
07371   command = ARG(0);
07372 
07373   if (system (command))
07374     return 1;
07375   return 0;
07376 }
07377 
07378 /* ---------------------------------------------------------------- */
07379 
07380 static int
07381 pcb_spawnvp (char **argv)
07382 {
07383 #ifdef HAVE__SPAWNVP
07384   int result = _spawnvp (_P_WAIT, argv[0], (const char * const *) argv);
07385   if (result == -1)
07386     return 1;
07387   else
07388     return 0;
07389 #else
07390   int pid;
07391   pid = fork ();
07392   if (pid < 0)
07393     {
07394       /* error */
07395       Message(_("Cannot fork!"));
07396       return 1;
07397     }
07398   else if (pid == 0)
07399     {
07400       /* Child */
07401       execvp (argv[0], argv);
07402       exit(1);
07403     }
07404   else
07405     {
07406       int rv;
07407       /* Parent */
07408       wait (&rv);
07409     }
07410   return 0;
07411 #endif
07412 }
07413 
07414 /* ---------------------------------------------------------------- */
07415 
07435 static char *
07436 tempfile_name_new (char * name)
07437 {
07438   char *tmpfile = NULL;
07439 #ifdef HAVE_MKDTEMP
07440   char *tmpdir, *mytmpdir;
07441   size_t len;
07442 #endif
07443 
07444   assert ( name != NULL );
07445 
07446 #ifdef HAVE_MKDTEMP
07447 #define TEMPLATE "pcb.XXXXXXXX"
07448     
07449   
07450   tmpdir = getenv ("TMPDIR");
07451 
07452   /* FIXME -- what about win32? */
07453   if (tmpdir == NULL) {
07454     tmpdir = "/tmp";
07455   }
07456   
07457   mytmpdir = (char *) malloc (sizeof(char) * 
07458                               (strlen (tmpdir) + 
07459                                1 +
07460                                strlen (TEMPLATE) + 
07461                                1));
07462   if (mytmpdir == NULL) {
07463     fprintf (stderr, "%s(): malloc failed()\n", __FUNCTION__);
07464     exit (1);
07465   }
07466   
07467   *mytmpdir = '\0';
07468   (void)strcat (mytmpdir, tmpdir);
07469   (void)strcat (mytmpdir, PCB_DIR_SEPARATOR_S);
07470   (void)strcat (mytmpdir, TEMPLATE);
07471   if (mkdtemp (mytmpdir) == NULL) {
07472     fprintf (stderr, "%s():  mkdtemp (\"%s\") failed\n", __FUNCTION__, mytmpdir);
07473     free (mytmpdir);
07474     return NULL;
07475   }
07476 
07477 
07478   len = strlen (mytmpdir) + /* the temp directory name */
07479     1 +                     /* the directory sep. */
07480     strlen (name) +         /* the file name */
07481     1                       /* the \0 termination */
07482     ;
07483 
07484   tmpfile = (char *) malloc (sizeof (char) * len);
07485 
07486   *tmpfile = '\0';
07487   (void)strcat (tmpfile, mytmpdir);
07488   (void)strcat (tmpfile, PCB_DIR_SEPARATOR_S);
07489   (void)strcat (tmpfile, name);
07490   
07491   free (mytmpdir);
07492 #undef TEMPLATE
07493 #else
07494   /*
07495    * tmpnam() uses a static buffer so strdup() the result right away
07496    * in case someone decides to create multiple temp names.
07497    */
07498   tmpfile = strdup (tmpnam (NULL));
07499 #ifdef __WIN32__
07500     {
07501       /* Guile doesn't like \ separators */
07502       char *c;
07503       for (c = tmpfile; *c; c++)
07504         if (*c == '\\')
07505           *c = '/';
07506     }
07507 #endif
07508 #endif
07509 
07510   return tmpfile;
07511 }
07512 
07513 /* ---------------------------------------------------------------- */
07514 
07521 static int
07522 tempfile_unlink (char * name)
07523 {
07524 #ifdef DEBUG
07525     /* SDB says:  Want to keep old temp files for examiniation when debugging */
07526   return 0;
07527 #else /* DEBUG */
07528 
07529 #ifdef HAVE_MKDTEMP
07530   int e, rc2 = 0;
07531   char *dname;
07532 
07533   unlink (name);
07534   /* it is possible that the file was never created so it is OK if the
07535      unlink fails */
07536 
07537   /* now figure out the directory name to remove */
07538   e = strlen (name) - 1;
07539   while (e > 0 && name[e] != PCB_DIR_SEPARATOR_C) {e--;}
07540   
07541   dname = strdup (name);
07542   dname[e] = '\0';
07543 
07544   /* 
07545    * at this point, e *should* point to the end of the directory part 
07546    * but lets make sure.
07547    */
07548   if (e > 0) {
07549     rc2 = rmdir (dname);
07550     if (rc2 != 0) {
07551       perror (dname);
07552     }
07553 
07554   } else {
07555     fprintf (stderr, _("%s():  Unable to determine temp directory name from the temp file\n"),
07556              __FUNCTION__);
07557     fprintf (stderr, "%s():  \"%s\"\n", 
07558              __FUNCTION__, name);
07559     rc2 = -1;
07560   }
07561 
07562   /* name was allocated with malloc */
07563   free (dname);
07564   free (name);
07565 
07566   /*
07567    * FIXME - should also return -1 if the temp file exists and was not
07568    * removed.  
07569    */
07570   if (rc2 != 0) {
07571     return -1;
07572   }
07573 
07574 #else /* HAVE_MKDTEMP */
07575   int rc = unlink (name);
07576 
07577   if (rc != 0) {
07578     fprintf (stderr, _("Failed to unlink \"%s\"\n"), name);
07579     free (name);
07580     return rc;
07581   }
07582   free (name);
07583 
07584 #endif /* HAVE_MKDTEMP */
07585 #endif /* DEBUG */
07586 
07587   return 0;
07588 }
07589 
07590 /* ---------------------------------------------------------------- */
07591 static const char import_syntax[] =
07592   N_("Import()\n"
07593   "Import([gnetlist|make[,source,source,...]])\n"
07594   "Import(setnewpoint[,(mark|center|X,Y)])\n"
07595   "Import(setdisperse,D,units)\n");
07596 
07597 static const char import_help[] = N_("Import schematics.");
07598 
07599 /* %start-doc actions Import
07600 
07601 Imports element and netlist data from the schematics (or some other
07602 source).  The first parameter, which is optional, is the mode.  If not
07603 specified, the @code{import::mode} attribute in the PCB is used.
07604 @code{gnetlist} means gnetlist is used to obtain the information from
07605 the schematics.  @code{make} invokes @code{make}, assuming the user
07606 has a @code{Makefile} in the current directory.  The @code{Makefile}
07607 will be invoked with the following variables set:
07608 
07609 @table @code
07610 
07611 @item PCB
07612 The name of the .pcb file
07613 
07614 @item SRCLIST
07615 A space-separated list of source files
07616 
07617 @item OUT
07618 The name of the file in which to put the command script, which may
07619 contain any @pcb{} actions.  By default, this is a temporary file
07620 selected by @pcb{}, but if you specify an @code{import::outfile}
07621 attribute, that file name is used instead (and not automatically
07622 deleted afterwards).
07623 
07624 @end table
07625 
07626 The target specified to be built is the first of these that apply:
07627 
07628 @itemize @bullet
07629 
07630 @item
07631 The target specified by an @code{import::target} attribute.
07632 
07633 @item
07634 The output file specified by an @code{import::outfile} attribute.
07635 
07636 @item
07637 If nothing else is specified, the target is @code{pcb_import}.
07638 
07639 @end itemize
07640 
07641 If you specify an @code{import::makefile} attribute, then "-f <that
07642 file>" will be added to the command line.
07643 
07644 If you specify the mode, you may also specify the source files
07645 (schematics).  If you do not specify any, the list of schematics is
07646 obtained by reading the @code{import::src@var{N}} attributes (like
07647 @code{import::src0}, @code{import::src1}, etc).
07648 
07649 For compatibility with future extensions to the import file format,
07650 the generated file @emph{must not} start with the two characters
07651 @code{#%}.
07652 
07653 If a temporary file is needed the @code{TMPDIR} environment variable
07654 is used to select its location.
07655 
07656 Note that the programs @code{gnetlist} and @code{make} may be
07657 overridden by the user via the @code{make-program} and @code{gnetlist}
07658 @code{pcb} settings (i.e. in @code{~/.pcb/settings} or on the command
07659 line).
07660 
07661 If @pcb{} cannot determine which schematic(s) to import from, the GUI
07662 is called to let user choose (see @code{ImportGUI()}).
07663 
07664 Note that Import() doesn't delete anything - after an Import, elements
07665 which shouldn't be on the board are selected and may be removed once
07666 it's determined that the deletion is appropriate.
07667 
07668 If @code{Import()} is called with @code{setnewpoint}, then the location
07669 of new components can be specified.  This is where parts show up when
07670 they're added to the board.  The default is the center of the board.
07671 
07672 @table @code
07673 
07674 @item Import(setnewpoint)
07675 
07676 Prompts the user to click on the board somewhere, uses that point.  If
07677 called by a hotkey, uses the current location of the crosshair.
07678 
07679 @item Import(setnewpoint,mark)
07680 
07681 Uses the location of the mark.  If no mark is present, the point is
07682 not changed.
07683 
07684 @item Import(setnewpoint,center)
07685 
07686 Resets the point to the center of the board.
07687 
07688 @item Import(setnewpoint,X,Y,units)
07689 
07690 Sets the point to the specific coordinates given.  Example:
07691 @code{Import(setnewpoint,50,25,mm)}
07692 
07693 @end table
07694 
07695 Note that the X and Y locations are stored in attributes named
07696 @code{import::newX} and @code{import::newY} so you could change them
07697 manually if you wished.
07698 
07699 Calling @code{Import(setdisperse,D,units)} sets how much the newly
07700 placed elements are dispersed relative to the set point.  For example,
07701 @code{Import(setdisperse,10,mm)} will offset each part randomly up to
07702 10mm away from the point.  The default dispersion is 1/10th of the
07703 smallest board dimension.  Dispersion is saved in the
07704 @code{import::disperse} attribute.
07705 
07706 %end-doc */
07707 
07708 static int
07709 ActionImport (int argc, char **argv, Coord x, Coord y)
07710 {
07711   char *mode;
07712   char **sources = NULL;
07713   int nsources = 0;
07714 
07715 #ifdef DEBUG
07716       printf("ActionImport:  ===========  Entering ActionImport  ============\n");
07717 #endif
07718 
07719   mode = ARG (0);
07720 
07721   if (mode && strcasecmp (mode, "setdisperse") == 0)
07722     {
07723       char *ds, *units;
07724       char buf[50];
07725 
07726       ds = ARG (1);
07727       units = ARG (2);
07728       if (!ds)
07729         {
07730           const char *as = AttributeGet (PCB, "import::disperse");
07731           ds = gui->prompt_for(_("Enter dispersion:"), as ? as : "0");
07732         }
07733       if (units)
07734         {
07735           sprintf(buf, "%s%s", ds, units);
07736           AttributePut (PCB, "import::disperse", buf);
07737         }
07738       else
07739         AttributePut (PCB, "import::disperse", ds);
07740       if (ARG (1) == NULL)
07741         free (ds);
07742       return 0;
07743     }
07744 
07745   if (mode && strcasecmp (mode, "setnewpoint") == 0)
07746     {
07747       const char *xs, *ys, *units;
07748       Coord x, y;
07749       char buf[50];
07750 
07751       xs = ARG (1);
07752       ys = ARG (2);
07753       units = ARG (3);
07754 
07755       if (!xs)
07756         {
07757           gui->get_coords (_("Click on a location"), &x, &y);
07758         }
07759       else if (strcasecmp (xs, "center") == 0)
07760         {
07761           AttributeRemove (PCB, "import::newX");
07762           AttributeRemove (PCB, "import::newY");
07763           return 0;
07764         }
07765       else if (strcasecmp (xs, "mark") == 0)
07766         {
07767           if (!Marked.status)
07768             return 0;
07769 
07770           x = Marked.X;
07771           y = Marked.Y;
07772         }
07773       else if (ys)
07774         {
07775           x = GetValue (xs, units, NULL);
07776           y = GetValue (ys, units, NULL);
07777         }
07778       else
07779         {
07780           Message (_("Bad syntax for Import(setnewpoint)"));
07781           return 1;
07782         }
07783 
07784       pcb_snprintf (buf, sizeof (buf), "%$ms", x);
07785       AttributePut (PCB, "import::newX", buf);
07786       pcb_snprintf (buf, sizeof (buf), "%$ms", y);
07787       AttributePut (PCB, "import::newY", buf);
07788       return 0;
07789     }
07790 
07791   if (! mode)
07792     mode = AttributeGet (PCB, "import::mode");
07793   if (! mode)
07794     mode = "gnetlist";
07795 
07796   if (argc > 1)
07797     {
07798       sources = argv + 1;
07799       nsources = argc - 1;
07800     }
07801 
07802   if (! sources)
07803     {
07804       char sname[40];
07805       char *src;
07806 
07807       nsources = -1;
07808       do {
07809         nsources ++;
07810         sprintf(sname, "import::src%d", nsources);
07811         src = AttributeGet (PCB, sname);
07812       } while (src);
07813 
07814       if (nsources > 0)
07815         {
07816           sources = (char **) malloc ((nsources + 1) * sizeof (char *));
07817           nsources = -1;
07818           do {
07819             nsources ++;
07820             sprintf(sname, "import::src%d", nsources);
07821             src = AttributeGet (PCB, sname);
07822             sources[nsources] = src;
07823           } while (src);
07824         }
07825     }
07826 
07827   if (! sources)
07828     {
07829       /* Replace .pcb with .sch and hope for the best.  */
07830       char *pcbname = PCB->Filename;
07831       char *schname;
07832       char *dot, *slash, *bslash;
07833 
07834       if (!pcbname)
07835         return hid_action("ImportGUI");
07836 
07837       schname = (char *) malloc (strlen(pcbname) + 5);
07838       strcpy (schname, pcbname);
07839       dot = strchr (schname, '.');
07840       slash = strchr (schname, '/');
07841       bslash = strchr (schname, '\\');
07842       if (dot && slash && dot < slash)
07843         dot = NULL;
07844       if (dot && bslash && dot < bslash)
07845         dot = NULL;
07846       if (dot)
07847         *dot = 0;
07848       strcat (schname, ".sch");
07849 
07850       if (access (schname, F_OK))
07851         {
07852           free (schname);
07853           return hid_action("ImportGUI");
07854         }
07855 
07856       sources = (char **) malloc (2 * sizeof (char *));
07857       sources[0] = schname;
07858       sources[1] = NULL;
07859       nsources = 1;
07860     }
07861 
07862   if (strcasecmp (mode, "gnetlist") == 0)
07863     {
07864       char *tmpfile = tempfile_name_new ("gnetlist_output");
07865       char **cmd;
07866       int i;
07867 
07868       if (tmpfile == NULL) {
07869         Message (_("Could not create temp file"));
07870         return 1;
07871       }
07872 
07873       cmd = (char **) malloc ((7 + nsources) * sizeof (char *));
07874       cmd[0] =  Settings.GnetlistProgram;
07875       cmd[1] = "-g";
07876       cmd[2] = "pcbfwd";
07877       cmd[3] = "-o";
07878       cmd[4] = tmpfile;
07879       cmd[5] = "--";
07880       for (i=0; i<nsources; i++)
07881         cmd[6+i] = sources[i];
07882       cmd[6+nsources] = NULL;
07883 
07884 #ifdef DEBUG
07885       printf("ActionImport:  ===========  About to run gnetlist  ============\n");
07886       printf("%s %s %s %s %s %s %s ...\n", 
07887              cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6]);
07888 #endif
07889 
07890       if (pcb_spawnvp (cmd))
07891         {
07892           unlink (tmpfile);
07893           return 1;
07894         }
07895 
07896 #ifdef DEBUG
07897       printf("ActionImport:  ===========  About to run ActionExecuteFile, file = %s  ============\n", tmpfile);
07898 #endif
07899 
07900       cmd[0] = tmpfile;
07901       cmd[1] = NULL;
07902       ActionExecuteFile (1, cmd, 0, 0);
07903 
07904       free (cmd);
07905       tempfile_unlink (tmpfile);
07906     }
07907   else if (strcasecmp (mode, "make") == 0)
07908     {
07909       int must_free_tmpfile = 0;
07910       char *tmpfile;
07911       char *cmd[10];
07912       int i;
07913       char *srclist;
07914       int srclen;
07915       char *user_outfile = NULL;
07916       char *user_makefile = NULL;
07917       char *user_target = NULL;
07918 
07919 
07920       user_outfile = AttributeGet (PCB, "import::outfile");
07921       user_makefile = AttributeGet (PCB, "import::makefile");
07922       user_target = AttributeGet (PCB, "import::target");
07923       if (user_outfile && !user_target)
07924         user_target = user_outfile;
07925 
07926       if (user_outfile)
07927         tmpfile = user_outfile;
07928       else
07929         {
07930           tmpfile = tempfile_name_new ("gnetlist_output");
07931           if (tmpfile == NULL) {
07932             Message (_("Could not create temp file"));
07933             free (sources);
07934             return 1;
07935           }
07936           must_free_tmpfile = 1;
07937         }
07938 
07939       srclen = sizeof("SRCLIST=") + 2;
07940       for (i=0; i<nsources; i++)
07941         srclen += strlen (sources[i]) + 2;
07942       srclist = (char *) malloc (srclen);
07943       strcpy (srclist, "SRCLIST=");
07944       for (i=0; i<nsources; i++)
07945         {
07946           if (i)
07947             strcat (srclist, " ");
07948           strcat (srclist, sources[i]);
07949         }
07950       
07951       cmd[0] = Settings.MakeProgram;
07952       cmd[1] = "-s";
07953       cmd[2] = Concat ("PCB=", PCB->Filename, NULL);
07954       cmd[3] = srclist;
07955       cmd[4] = Concat ("OUT=", tmpfile, NULL);
07956       i = 5;
07957       if (user_makefile)
07958         {
07959           cmd[i++] = "-f";
07960           cmd[i++] = user_makefile;
07961         }
07962       cmd[i++] = user_target ? user_target : (char *)"pcb_import";
07963       cmd[i++] = NULL;
07964 
07965       if (pcb_spawnvp (cmd))
07966         {
07967           if (must_free_tmpfile)
07968             unlink (tmpfile);
07969           free (cmd[2]);
07970           free (cmd[3]);
07971           free (cmd[4]);
07972           return 1;
07973         }
07974 
07975       cmd[0] = tmpfile;
07976       cmd[1] = NULL;
07977       ActionExecuteFile (1, cmd, 0, 0);
07978 
07979       free (cmd[2]);
07980       free (cmd[3]);
07981       free (cmd[4]);
07982       if (must_free_tmpfile)
07983         tempfile_unlink (tmpfile);
07984     }
07985   else
07986     {
07987       Message (_("Unknown import mode: %s\n"), mode);
07988       return 1;
07989     }
07990 
07991   DeleteRats (false);
07992   AddAllRats (false, NULL);
07993 
07994 #ifdef DEBUG
07995       printf("ActionImport:  ===========  Leaving ActionImport  ============\n");
07996 #endif
07997 
07998   return 0;
07999 }
08000 
08001 /* ------------------------------------------------------------ */
08002 
08003 static const char attributes_syntax[] =
08004   N_("Attributes(Layout|Layer|Element)\n"
08005   "Attributes(Layer,layername)");
08006 
08007 static const char attributes_help[] =
08008   N_("Let the user edit the attributes of the layout, current or given\n"
08009   "layer, or selected element.");
08010 
08011 /* %start-doc actions Attributes
08012 
08013 This just pops up a dialog letting the user edit the attributes of the
08014 pcb, an element, or a layer.
08015 
08016 %end-doc */
08017 
08018 
08019 static int
08020 ActionAttributes (int argc, char **argv, Coord x, Coord y)
08021 {
08022   char *function = ARG (0);
08023   char *layername = ARG (1);
08024   char *buf;
08025 
08026   if (!function)
08027     AFAIL (attributes);
08028 
08029   if (!gui->edit_attributes)
08030     {
08031       Message (_("This GUI doesn't support Attribute Editing\n"));
08032       return 1;
08033     }
08034 
08035   switch (GetFunctionID (function))
08036     {
08037     case F_Layout:
08038       {
08039         gui->edit_attributes(_("Layout Attributes"), &(PCB->Attributes));
08040         return 0;
08041       }
08042 
08043     case F_Layer:
08044       {
08045         LayerType *layer = CURRENT;
08046         if (layername)
08047           {
08048             int i;
08049             layer = NULL;
08050             for (i=0; i<max_copper_layer; i++)
08051               if (strcmp (PCB->Data->Layer[i].Name, layername) == 0)
08052                 {
08053                   layer = & (PCB->Data->Layer[i]);
08054                   break;
08055                 }
08056             if (layer == NULL)
08057               {
08058                 Message (_("No layer named %s\n"), layername);
08059                 return 1;
08060               }
08061           }
08062         buf = (char *) malloc (strlen (layer->Name) +
08063             strlen (_("Layer %s Attributes")));
08064         sprintf (buf, _("Layer %s Attributes"), layer->Name);
08065         gui->edit_attributes(buf, &(layer->Attributes));
08066         free (buf);
08067         return 0;
08068       }
08069 
08070     case F_Element:
08071       {
08072         int n_found = 0;
08073         ElementType *e = NULL;
08074         ELEMENT_LOOP (PCB->Data);
08075         {
08076           if (TEST_FLAG (SELECTEDFLAG, element))
08077             {
08078               e = element;
08079               n_found ++;
08080             }
08081         }
08082         END_LOOP;
08083         if (n_found > 1)
08084           {
08085             Message (_("Too many elements selected\n"));
08086             return 1;
08087           }
08088         if (n_found == 0)
08089           {
08090             void *ptrtmp;
08091             gui->get_coords (_("Click on an element"), &x, &y);
08092             if ((SearchScreen
08093                  (x, y, ELEMENT_TYPE, &ptrtmp,
08094                   &ptrtmp, &ptrtmp)) != NO_TYPE)
08095               e = (ElementType *) ptrtmp;
08096             else
08097               {
08098                 Message (_("No element found there\n"));
08099                 return 1;
08100               }
08101           }
08102 
08103         if (NAMEONPCB_NAME(e))
08104           {
08105             buf = (char *) malloc (strlen (NAMEONPCB_NAME(e)) +
08106                 strlen (_("Element %s Attributes")));
08107             sprintf(buf, _("Element %s Attributes"), NAMEONPCB_NAME(e));
08108           }
08109         else
08110           {
08111             buf = strdup (_("Unnamed Element Attributes"));
08112           }
08113         gui->edit_attributes(buf, &(e->Attributes));
08114         free (buf);
08115         break;
08116       }
08117 
08118     default:
08119       AFAIL (attributes);
08120     }
08121 
08122   return 0;
08123 }
08124 
08125 /* --------------------------------------------------------------------------- */
08126 
08127 static const char setvialayers_syntax[] =
08128   N_("SetViaLayers(Object|SelectedVias|Selected[,ThroughHole|TH])\n"
08129      "SetViaLayers(Object|SelectedVias|Selected,from,to)\n"
08130      "SetViaLayers(Object|SelectedVias|Selected,[c|-|from],[c|-|to])"
08131      );
08132 
08133 static const char setvialayers_help[] =
08134   N_("Sets starting and ending layer for burried/blind/standard vias.");
08135 
08136 /* %start-doc actions setvialayers
08137 
08138 Specifies layers, which are connected by via.
08139 
08140 @table @code
08141 
08142 @item TH|ThroughHole
08143 The vias will be set as through-hole, connecting all layers
08144 
08145 @item from
08146 layer name or layer number of the first layer to be connected by via; "-" stands for unchanged, "c" stands for currently selected layer
08147 
08148 @item to
08149 layer name or layer number of the last layer to be connected by via; "-" stands for unchanged, "c" stands for currently selected layer
08150 
08151 @end table
08152 
08153 If no parameter us used, dialog is displayed (if implemented in the respective GUI HID).
08154 
08155 
08156 %end-doc */
08157 
08158 static bool
08159 identify_layer (char *layer_name, Cardinal *layer_no)
08160 {
08161   int layer;
08162 
08163   if (strcmp (layer_name, "-") == 0)
08164     {
08165       *layer_no = -1;
08166       return true;
08167     }
08168 
08169   if (strcmp (layer_name, "c") == 0)
08170     {
08171       if ((unsigned int)INDEXOFCURRENT < max_copper_layer)
08172         {
08173           *layer_no = INDEXOFCURRENT;
08174           return true;
08175         }
08176     }
08177 
08178   layer = SearchLayerByName (PCB->Data, layer_name);
08179   if (layer == -1)
08180     {
08181       if (sscanf (layer_name, "%d", &layer) != 1)
08182         layer = -1;
08183     }
08184 
08185   if (layer != -1)
08186     *layer_no = layer;
08187 
08188   return (layer != -1);
08189 }
08190 
08191 static int
08192 ActionSetViaLayers (int argc, char **argv, Coord x, Coord y)
08193 {
08194   char *function = ARG (0);
08195   char *layername_from = ARG (1);
08196   char *layername_to = ARG (2);
08197   Cardinal layer_from ;
08198   Cardinal layer_to = -1;
08199 
08200   if (!function)
08201     AFAIL (setvialayers);
08202 
08203   if ( /* !gui->edit_attributes  &&*/ argc < 2)
08204     {
08205       Message (_("This GUI doesn't support Via Layers editing\n"));
08206       return 1;
08207     }
08208 
08209   if (GetFunctionID (layername_from) == F_ThroughHole)
08210     {
08211       layer_from = 0;
08212       layer_to = 0;
08213     }
08214   else
08215     {
08216       if (!identify_layer (layername_from, &layer_from)
08217           || !identify_layer (layername_to, &layer_to))
08218         {
08219           Message (_("Sorry, wrong layers specified.\n"));
08220           return 1;
08221         }
08222     }
08223 
08224   /* ensure that layer_from < layer_to */
08225   if (layer_from != -1
08226       && layer_to != -1
08227       && layer_from > layer_to)
08228     {
08229       int tmp;
08230 
08231       tmp = layer_from;
08232       layer_from = layer_to;
08233       layer_to = tmp;
08234     }
08235 
08236   if (layer_to != -1)
08237     layer_to = min (layer_to, max_copper_layer-1);
08238 
08239   switch (GetFunctionID (function))
08240     {
08241     case F_Object:
08242       {
08243         int type;
08244         void *ptr1, *ptr2, *ptr3;
08245 
08246         if ((type =
08247              SearchScreen (Crosshair.X, Crosshair.Y, VIA_TYPE,
08248                            &ptr1, &ptr2, &ptr3)) != NO_TYPE)
08249           {
08250             if (TEST_FLAG (LOCKFLAG, (PinType *) ptr1))
08251               Message (_("Sorry, the object is locked\n"));
08252             else
08253               {
08254                 if (ChangeObjectViaLayers (ptr1, ptr2, ptr3, layer_from, layer_to))
08255                   {
08256                     SetChangedFlag (true);
08257                   }
08258               }
08259           }
08260         break;
08261     case F_SelectedVias:
08262     case F_Selected:
08263         if (ChangeSelectedViaLayers (layer_from, layer_to))
08264           {
08265             SetChangedFlag (true);
08266           }
08267         break;
08268       }
08269   }
08270 
08271   return 0;
08272 }
08273 /* --------------------------------------------------------------------------- */
08274 
08275 HID_Action action_action_list[] = {
08276   {"AddRats", 0, ActionAddRats,
08277    addrats_help, addrats_syntax}
08278   ,
08279   {"Attributes", 0, ActionAttributes,
08280    attributes_help, attributes_syntax}
08281   ,
08282   {"Atomic", 0, ActionAtomic,
08283    atomic_help, atomic_syntax}
08284   ,
08285   {"AutoPlaceSelected", 0, ActionAutoPlaceSelected,
08286    autoplace_help, autoplace_syntax}
08287   ,
08288   {"AutoRoute", 0, ActionAutoRoute,
08289    autoroute_help, autoroute_syntax}
08290   ,
08291   {"ChangeClearSize", 0, ActionChangeClearSize,
08292    changeclearsize_help, changeclearsize_syntax}
08293   ,
08294   {"ChangeDrillSize", 0, ActionChange2ndSize,
08295    changedrillsize_help, changedrillsize_syntax}
08296   ,
08297   {"ChangeHole", 0, ActionChangeHole,
08298    changehold_help, changehold_syntax}
08299   ,
08300   {"ChangeJoin", 0, ActionChangeJoin,
08301    changejoin_help, changejoin_syntax}
08302   ,
08303   {"ChangeName", 0, ActionChangeName,
08304    changename_help, changename_syntax}
08305   ,
08306   {"ChangePaste", 0, ActionChangePaste,
08307    changepaste_help, changepaste_syntax}
08308   ,
08309   {"ChangePinName", 0, ActionChangePinName,
08310    changepinname_help, changepinname_syntax}
08311   ,
08312   {"ChangeSize", 0, ActionChangeSize,
08313    changesize_help, changesize_syntax}
08314   ,
08315   {"ChangeSquare", 0, ActionChangeSquare,
08316    changesquare_help, changesquare_syntax}
08317   ,
08318   {"ChangeOctagon", 0, ActionChangeOctagon,
08319    changeoctagon_help, changeoctagon_syntax}
08320   ,
08321   {"ClearSquare", 0, ActionClearSquare,
08322    clearsquare_help, clearsquare_syntax}
08323   ,
08324   {"ClearOctagon", 0, ActionClearOctagon,
08325    clearoctagon_help, clearoctagon_syntax}
08326   ,
08327   {"Connection", 0, ActionConnection,
08328    connection_help, connection_syntax}
08329   ,
08330   {"Delete", 0, ActionDelete,
08331    delete_help, delete_syntax}
08332   ,
08333   {"DeleteRats", 0, ActionDeleteRats,
08334    deleterats_help, deleterats_syntax}
08335   ,
08336   {"DisperseElements", 0, ActionDisperseElements,
08337    disperseelements_help, disperseelements_syntax}
08338   ,
08339   {"Display", 0, ActionDisplay,
08340    display_help, display_syntax}
08341   ,
08342   {"DRC", 0, ActionDRCheck,
08343    drc_help, drc_syntax}
08344   ,
08345   {"DumpLibrary", 0, ActionDumpLibrary,
08346    dumplibrary_help, dumplibrary_syntax}
08347   ,
08348   {"ExecuteFile", 0, ActionExecuteFile,
08349    executefile_help, executefile_syntax}
08350   ,
08351   {"Flip", N_("Click on Object or Flip Point"), ActionFlip,
08352    flip_help, flip_syntax}
08353   ,
08354   {"LoadFrom", 0, ActionLoadFrom,
08355    loadfrom_help, loadfrom_syntax}
08356   ,
08357   {"MarkCrosshair", 0, ActionMarkCrosshair,
08358    markcrosshair_help, markcrosshair_syntax}
08359   ,
08360   {"Message", 0, ActionMessage,
08361    message_help, message_syntax}
08362   ,
08363   {"MinMaskGap", 0, ActionMinMaskGap,
08364    minmaskgap_help, minmaskgap_syntax}
08365   ,
08366   {"MinClearGap", 0, ActionMinClearGap,
08367    mincleargap_help, mincleargap_syntax}
08368   ,
08369   {"Mode", 0, ActionMode,
08370    mode_help, mode_syntax}
08371   ,
08372   {"MorphPolygon", 0, ActionMorphPolygon,
08373    morphpolygon_help, morphpolygon_syntax}
08374   ,
08375   {"PasteBuffer", 0, ActionPasteBuffer,
08376    pastebuffer_help, pastebuffer_syntax}
08377   ,
08378   {"Quit", 0, ActionQuit,
08379    quit_help, quit_syntax}
08380   ,
08381   {"RemoveSelected", 0, ActionRemoveSelected,
08382    removeselected_help, removeselected_syntax}
08383   ,
08384   {"Renumber", 0, ActionRenumber,
08385    renumber_help, renumber_syntax}
08386   ,
08387   {"RipUp", 0, ActionRipUp,
08388    ripup_help, ripup_syntax}
08389   ,
08390   {"Select", 0, ActionSelect,
08391    select_help, select_syntax}
08392   ,
08393   {"Unselect", 0, ActionUnselect,
08394    unselect_help, unselect_syntax}
08395   ,
08396   {"SaveSettings", 0, ActionSaveSettings,
08397    savesettings_help, savesettings_syntax}
08398   ,
08399   {"SaveTo", 0, ActionSaveTo,
08400    saveto_help, saveto_syntax}
08401   ,
08402   {"SetSquare", 0, ActionSetSquare,
08403    setsquare_help, setsquare_syntax}
08404   ,
08405   {"SetOctagon", 0, ActionSetOctagon,
08406    setoctagon_help, setoctagon_syntax}
08407   ,
08408   {"SetThermal", 0, ActionSetThermal,
08409    setthermal_help, setthermal_syntax}
08410   ,
08411   {"SetValue", 0, ActionSetValue,
08412    setvalue_help, setvalue_syntax}
08413   ,
08414   {"ToggleHideName", 0, ActionToggleHideName,
08415    togglehidename_help, togglehidename_syntax}
08416   ,
08417   {"Undo", 0, ActionUndo,
08418    undo_help, undo_syntax}
08419   ,
08420   {"Redo", 0, ActionRedo,
08421    redo_help, redo_syntax}
08422   ,
08423   {"SetSame", N_("Select item to use attributes from"), ActionSetSame,
08424    setsame_help, setsame_syntax}
08425   ,
08426   {"SetFlag", 0, ActionSetFlag,
08427    setflag_help, setflag_syntax}
08428   ,
08429   {"ClrFlag", 0, ActionClrFlag,
08430    clrflag_help, clrflag_syntax}
08431   ,
08432   {"ChangeFlag", 0, ActionChangeFlag,
08433    changeflag_help, changeflag_syntax}
08434   ,
08435   {"Polygon", 0, ActionPolygon,
08436    polygon_help, polygon_syntax}
08437   ,
08438   {"RouteStyle", 0, ActionRouteStyle,
08439    routestyle_help, routestyle_syntax}
08440   ,
08441   {"MoveObject", N_("Select an Object"), ActionMoveObject,
08442    moveobject_help, moveobject_syntax}
08443   ,
08444   {"MoveToCurrentLayer", 0, ActionMoveToCurrentLayer,
08445    movetocurrentlayer_help, movetocurrentlayer_syntax}
08446   ,
08447   {"New", 0, ActionNew,
08448    new_help, new_syntax}
08449   ,
08450   {"pscalib", 0, ActionPSCalib}
08451   ,
08452   {"ElementList", 0, ActionElementList,
08453    elementlist_help, elementlist_syntax}
08454   ,
08455   {"ElementSetAttr", 0, ActionElementSetAttr,
08456    elementsetattr_help, elementsetattr_syntax}
08457   ,
08458   {"ExecCommand", 0, ActionExecCommand,
08459    execcommand_help, execcommand_syntax}
08460   ,
08461   {"Import", 0, ActionImport,
08462    import_help, import_syntax}
08463   ,
08464   {"SetViaLayers", 0, ActionSetViaLayers,
08465    setvialayers_help, setvialayers_syntax}
08466   ,
08467 };
08468 
08469 REGISTER_ACTIONS (action_action_list)
08470