Remove change log like comments from file headers
[geda-pcb/pcjc2.git] / src / action.c
blob665ae91ea8c77ad92af642d6b0ea75aca59f12c1
1 /*
2 * COPYRIGHT
4 * PCB, interactive printed circuit board design
5 * Copyright (C) 1994,1995,1996 Thomas Nau
6 * Copyright (C) 1997, 1998, 1999, 2000, 2001 Harry Eaton
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 * Contact addresses for paper mail and Email:
23 * Harry Eaton, 6697 Buttonhole Ct, Columbia, MD 21044, USA
24 * haceaton@aplcomm.jhuapl.edu
28 /* action routines for output window
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
35 #include "global.h"
37 #include "action.h"
38 #include "autoplace.h"
39 #include "autoroute.h"
40 #include "buffer.h"
41 #include "change.h"
42 #include "command.h"
43 #include "copy.h"
44 #include "create.h"
45 #include "crosshair.h"
46 #include "data.h"
47 #include "draw.h"
48 #include "error.h"
49 #include "file.h"
50 #include "find.h"
51 #include "hid.h"
52 #include "insert.h"
53 #include "line.h"
54 #include "mymem.h"
55 #include "misc.h"
56 #include "mirror.h"
57 #include "move.h"
58 #include "polygon.h"
59 /*#include "print.h"*/
60 #include "rats.h"
61 #include "remove.h"
62 #include "report.h"
63 #include "rotate.h"
64 #include "rubberband.h"
65 #include "search.h"
66 #include "select.h"
67 #include "set.h"
68 #include "thermal.h"
69 #include "undo.h"
70 #include "rtree.h"
71 #include "macro.h"
72 #include "pcb-printf.h"
74 #include <assert.h>
75 #include <stdlib.h> /* rand() */
77 #ifdef HAVE_LIBDMALLOC
78 #include <dmalloc.h>
79 #endif
81 /* for fork() and friends */
82 #ifdef HAVE_UNISTD_H
83 #include <unistd.h>
84 #endif
86 #ifdef HAVE_SYS_WAIT_H
87 #include <sys/wait.h>
88 #endif
90 /* ---------------------------------------------------------------------------
91 * some local types
93 typedef enum
95 F_AddSelected,
96 F_All,
97 F_AllConnections,
98 F_AllRats,
99 F_AllUnusedPins,
100 F_Arc,
101 F_Arrow,
102 F_Block,
103 F_Description,
104 F_Cancel,
105 F_Center,
106 F_Clear,
107 F_ClearAndRedraw,
108 F_ClearList,
109 F_Close,
110 F_Connection,
111 F_Convert,
112 F_Copy,
113 F_CycleClip,
114 F_CycleCrosshair,
115 F_DeleteRats,
116 F_Drag,
117 F_DrillReport,
118 F_Element,
119 F_ElementByName,
120 F_ElementConnections,
121 F_ElementToBuffer,
122 F_Escape,
123 F_Find,
124 F_FlipElement,
125 F_FoundPins,
126 F_Grid,
127 F_InsertPoint,
128 F_Layer,
129 F_Layout,
130 F_LayoutAs,
131 F_LayoutToBuffer,
132 F_Line,
133 F_LineSize,
134 F_Lock,
135 F_Mirror,
136 F_Move,
137 F_NameOnPCB,
138 F_Netlist,
139 F_NetByName,
140 F_None,
141 F_Notify,
142 F_Object,
143 F_ObjectByName,
144 F_PasteBuffer,
145 F_PadByName,
146 F_PinByName,
147 F_PinOrPadName,
148 F_Pinout,
149 F_Polygon,
150 F_PolygonHole,
151 F_PreviousPoint,
152 F_RatsNest,
153 F_Rectangle,
154 F_Redraw,
155 F_Release,
156 F_Revert,
157 F_Remove,
158 F_RemoveSelected,
159 F_Report,
160 F_Reset,
161 F_ResetLinesAndPolygons,
162 F_ResetPinsViasAndPads,
163 F_Restore,
164 F_Rotate,
165 F_Save,
166 F_Selected,
167 F_SelectedArcs,
168 F_SelectedElements,
169 F_SelectedLines,
170 F_SelectedNames,
171 F_SelectedObjects,
172 F_SelectedPads,
173 F_SelectedPins,
174 F_SelectedTexts,
175 F_SelectedVias,
176 F_SelectedRats,
177 F_Stroke,
178 F_Text,
179 F_TextByName,
180 F_TextScale,
181 F_Thermal,
182 F_ToLayout,
183 F_ToggleAllDirections,
184 F_ToggleAutoDRC,
185 F_ToggleClearLine,
186 F_ToggleFullPoly,
187 F_ToggleGrid,
188 F_ToggleHideNames,
189 F_ToggleMask,
190 F_ToggleName,
191 F_ToggleObject,
192 F_ToggleShowDRC,
193 F_ToggleLiveRoute,
194 F_ToggleRubberBandMode,
195 F_ToggleStartDirection,
196 F_ToggleSnapPin,
197 F_ToggleThindraw,
198 F_ToggleLockNames,
199 F_ToggleOnlyNames,
200 F_ToggleThindrawPoly,
201 F_ToggleOrthoMove,
202 F_ToggleLocalRef,
203 F_ToggleCheckPlanes,
204 F_ToggleUniqueNames,
205 F_Via,
206 F_ViaByName,
207 F_Value,
208 F_ViaDrillingHole,
209 F_ViaSize,
210 F_Zoom
212 FunctionID;
214 typedef struct /* used to identify subfunctions */
216 char *Identifier;
217 FunctionID ID;
219 FunctionType, *FunctionTypePtr;
221 /* --------------------------------------------------------------------------- */
223 /* %start-doc actions 00delta
225 Many actions take a @code{delta} parameter as the last parameter,
226 which is an amount to change something. That @code{delta} may include
227 units, as an additional parameter, such as @code{Action(Object,5,mm)}.
228 If no units are specified, the default is PCB's native units
229 (currently 1/100 mil). Also, if the delta is prefixed by @code{+} or
230 @code{-}, the size is increased or decreased by that amount.
231 Otherwise, the size size is set to the given amount.
233 @example
234 Action(Object,5,mil)
235 Action(Object,+0.5,mm)
236 Action(Object,-1)
237 @end example
239 Actions which take a @code{delta} parameter which do not accept all
240 these options will specify what they do take.
242 %end-doc */
244 /* %start-doc actions 00objects
246 Many actions act on indicated objects on the board. They will have
247 parameters like @code{ToggleObject} or @code{SelectedVias} to indicate
248 what group of objects they act on. Unless otherwise specified, these
249 parameters are defined as follows:
251 @table @code
253 @item Object
254 @itemx ToggleObject
255 Affects the object under the mouse pointer. If this action is invoked
256 from a menu or script, the user will be prompted to click on an
257 object, which is then the object affected.
259 @item Selected
260 @itemx SelectedObjects
262 Affects all objects which are currently selected. At least, all
263 selected objects for which the given action makes sense.
265 @item SelectedPins
266 @itemx SelectedVias
267 @itemx Selected@var{Type}
268 @itemx @i{etc}
269 Affects all objects which are both selected and of the @var{Type} specified.
271 @end table
273 %end-doc */
275 /* %start-doc actions 00macros
277 @macro pinshapes
279 Pins, pads, and vias can have various shapes. All may be round. Pins
280 and pads may be square (obviously "square" pads are usually
281 rectangular). Pins and vias may be octagonal. When you change a
282 shape flag of an element, you actually change all of its pins and
283 pads.
285 Note that the square flag takes precedence over the octagon flag,
286 thus, if both the square and octagon flags are set, the object is
287 square. When the square flag is cleared, the pins and pads will be
288 either round or, if the octagon flag is set, octagonal.
290 @end macro
292 %end-doc */
294 /* ---------------------------------------------------------------------------
295 * some local identifiers
297 static PointType InsertedPoint;
298 static LayerTypePtr lastLayer;
299 static struct
301 PolygonTypePtr poly;
302 LineType line;
304 fake;
306 static struct
308 Coord X, Y;
309 Cardinal Buffer;
310 bool Click;
311 bool Moving; /* selected type clicked on */
312 int Hit; /* move type clicked on */
313 void *ptr1;
314 void *ptr2;
315 void *ptr3;
317 Note;
319 static int defer_updates = 0;
320 static int defer_needs_update = 0;
322 static Cardinal polyIndex = 0;
323 static bool saved_mode = false;
324 #ifdef HAVE_LIBSTROKE
325 static bool mid_stroke = false;
326 static BoxType StrokeBox;
327 #endif
328 static FunctionType Functions[] = {
329 {"AddSelected", F_AddSelected},
330 {"All", F_All},
331 {"AllConnections", F_AllConnections},
332 {"AllRats", F_AllRats},
333 {"AllUnusedPins", F_AllUnusedPins},
334 {"Arc", F_Arc},
335 {"Arrow", F_Arrow},
336 {"Block", F_Block},
337 {"Description", F_Description},
338 {"Cancel", F_Cancel},
339 {"Center", F_Center},
340 {"Clear", F_Clear},
341 {"ClearAndRedraw", F_ClearAndRedraw},
342 {"ClearList", F_ClearList},
343 {"Close", F_Close},
344 {"Connection", F_Connection},
345 {"Convert", F_Convert},
346 {"Copy", F_Copy},
347 {"CycleClip", F_CycleClip},
348 {"CycleCrosshair", F_CycleCrosshair},
349 {"DeleteRats", F_DeleteRats},
350 {"Drag", F_Drag},
351 {"DrillReport", F_DrillReport},
352 {"Element", F_Element},
353 {"ElementByName", F_ElementByName},
354 {"ElementConnections", F_ElementConnections},
355 {"ElementToBuffer", F_ElementToBuffer},
356 {"Escape", F_Escape},
357 {"Find", F_Find},
358 {"FlipElement", F_FlipElement},
359 {"FoundPins", F_FoundPins},
360 {"Grid", F_Grid},
361 {"InsertPoint", F_InsertPoint},
362 {"Layer", F_Layer},
363 {"Layout", F_Layout},
364 {"LayoutAs", F_LayoutAs},
365 {"LayoutToBuffer", F_LayoutToBuffer},
366 {"Line", F_Line},
367 {"LineSize", F_LineSize},
368 {"Lock", F_Lock},
369 {"Mirror", F_Mirror},
370 {"Move", F_Move},
371 {"NameOnPCB", F_NameOnPCB},
372 {"Netlist", F_Netlist},
373 {"NetByName", F_NetByName},
374 {"None", F_None},
375 {"Notify", F_Notify},
376 {"Object", F_Object},
377 {"ObjectByName", F_ObjectByName},
378 {"PasteBuffer", F_PasteBuffer},
379 {"PadByName", F_PadByName},
380 {"PinByName", F_PinByName},
381 {"PinOrPadName", F_PinOrPadName},
382 {"Pinout", F_Pinout},
383 {"Polygon", F_Polygon},
384 {"PolygonHole", F_PolygonHole},
385 {"PreviousPoint", F_PreviousPoint},
386 {"RatsNest", F_RatsNest},
387 {"Rectangle", F_Rectangle},
388 {"Redraw", F_Redraw},
389 {"Release", F_Release},
390 {"Remove", F_Remove},
391 {"RemoveSelected", F_RemoveSelected},
392 {"Report", F_Report},
393 {"Reset", F_Reset},
394 {"ResetLinesAndPolygons", F_ResetLinesAndPolygons},
395 {"ResetPinsViasAndPads", F_ResetPinsViasAndPads},
396 {"Restore", F_Restore},
397 {"Revert", F_Revert},
398 {"Rotate", F_Rotate},
399 {"Save", F_Save},
400 {"Selected", F_Selected},
401 {"SelectedArcs", F_SelectedArcs},
402 {"SelectedElements", F_SelectedElements},
403 {"SelectedLines", F_SelectedLines},
404 {"SelectedNames", F_SelectedNames},
405 {"SelectedObjects", F_SelectedObjects},
406 {"SelectedPins", F_SelectedPins},
407 {"SelectedPads", F_SelectedPads},
408 {"SelectedRats", F_SelectedRats},
409 {"SelectedTexts", F_SelectedTexts},
410 {"SelectedVias", F_SelectedVias},
411 {"Stroke", F_Stroke},
412 {"Text", F_Text},
413 {"TextByName", F_TextByName},
414 {"TextScale", F_TextScale},
415 {"Thermal", F_Thermal},
416 {"ToLayout", F_ToLayout},
417 {"Toggle45Degree", F_ToggleAllDirections},
418 {"ToggleClearLine", F_ToggleClearLine},
419 {"ToggleFullPoly", F_ToggleFullPoly},
420 {"ToggleGrid", F_ToggleGrid},
421 {"ToggleMask", F_ToggleMask},
422 {"ToggleName", F_ToggleName},
423 {"ToggleObject", F_ToggleObject},
424 {"ToggleRubberBandMode", F_ToggleRubberBandMode},
425 {"ToggleStartDirection", F_ToggleStartDirection},
426 {"ToggleSnapPin", F_ToggleSnapPin},
427 {"ToggleThindraw", F_ToggleThindraw},
428 {"ToggleThindrawPoly", F_ToggleThindrawPoly},
429 {"ToggleLockNames", F_ToggleLockNames},
430 {"ToggleOnlyNames", F_ToggleOnlyNames},
431 {"ToggleHideNames", F_ToggleHideNames},
432 {"ToggleCheckPlanes", F_ToggleCheckPlanes},
433 {"ToggleLocalRef", F_ToggleLocalRef},
434 {"ToggleOrthoMove", F_ToggleOrthoMove},
435 {"ToggleShowDRC", F_ToggleShowDRC},
436 {"ToggleLiveRoute", F_ToggleLiveRoute},
437 {"ToggleAutoDRC", F_ToggleAutoDRC},
438 {"ToggleUniqueNames", F_ToggleUniqueNames},
439 {"Value", F_Value},
440 {"Via", F_Via},
441 {"ViaByName", F_ViaByName},
442 {"ViaSize", F_ViaSize},
443 {"ViaDrillingHole", F_ViaDrillingHole},
444 {"Zoom", F_Zoom}
447 /* ---------------------------------------------------------------------------
448 * some local routines
450 static int GetFunctionID (String);
451 static void AdjustAttachedBox (void);
452 static void NotifyLine (void);
453 static void NotifyBlock (void);
454 static void NotifyMode (void);
455 static void ClearWarnings (void);
456 #ifdef HAVE_LIBSTROKE
457 static void FinishStroke (void);
458 extern void stroke_init (void);
459 extern void stroke_record (int x, int y);
460 extern int stroke_trans (char *s);
461 #endif
462 static void ChangeFlag (char *, char *, int, char *);
464 #define ARG(n) (argc > (n) ? argv[n] : NULL)
466 #ifdef HAVE_LIBSTROKE
468 /* ---------------------------------------------------------------------------
469 * FinishStroke - try to recognize the stroke sent
471 void
472 FinishStroke (void)
474 char msg[255];
475 int type;
476 unsigned long num;
477 void *ptr1, *ptr2, *ptr3;
479 mid_stroke = false;
480 if (stroke_trans (msg))
482 num = atoi (msg);
483 switch (num)
485 case 456:
486 if (Settings.Mode == LINE_MODE)
488 SetMode (LINE_MODE);
490 break;
491 case 9874123:
492 case 74123:
493 case 987412:
494 case 8741236:
495 case 874123:
496 RotateScreenObject (StrokeBox.X1, StrokeBox.Y1, SWAP_IDENT ? 1 : 3);
497 break;
498 case 7896321:
499 case 786321:
500 case 789632:
501 case 896321:
502 RotateScreenObject (StrokeBox.X1, StrokeBox.Y1, SWAP_IDENT ? 3 : 1);
503 break;
504 case 258:
505 SetMode (LINE_MODE);
506 break;
507 case 852:
508 SetMode (ARROW_MODE);
509 break;
510 case 1478963:
511 ActionUndo ("");
512 break;
513 case 147423:
514 case 147523:
515 case 1474123:
516 Redo (true);
517 break;
518 case 148963:
519 case 147863:
520 case 147853:
521 case 145863:
522 SetMode (VIA_MODE);
523 break;
524 case 951:
525 case 9651:
526 case 9521:
527 case 9621:
528 case 9851:
529 case 9541:
530 case 96521:
531 case 96541:
532 case 98541:
533 SetZoom (1000); /* special zoom extents */
534 break;
535 case 159:
536 case 1269:
537 case 1259:
538 case 1459:
539 case 1569:
540 case 1589:
541 case 12569:
542 case 12589:
543 case 14589:
545 Coord x = (StrokeBox.X1 + StrokeBox.X2) / 2;
546 Coord y = (StrokeBox.Y1 + StrokeBox.Y2) / 2;
547 double z;
548 /* XXX: PCB->MaxWidth and PCB->MaxHeight may be the wrong
549 * divisors below. The old code WAS broken, but this
550 * replacement has not been tested for correctness.
554 log (fabs (StrokeBox.X2 - StrokeBox.X1) / PCB->MaxWidth) /
555 log (2.0);
557 MAX (z,
559 log (fabs (StrokeBox.Y2 - StrokeBox.Y1) / PCB->MaxHeight) /
560 log (2.0));
561 SetZoom (z);
563 CenterDisplay (x, y);
564 break;
567 default:
568 Message (_("Unknown stroke %s\n"), msg);
569 break;
572 else
573 gui->beep ();
575 #endif
577 /* ---------------------------------------------------------------------------
578 * Clear warning color from pins/pads
580 static void
581 ClearWarnings ()
583 Settings.RatWarn = false;
584 ALLPIN_LOOP (PCB->Data);
586 if (TEST_FLAG (WARNFLAG, pin))
588 CLEAR_FLAG (WARNFLAG, pin);
589 DrawPin (pin);
592 ENDALL_LOOP;
593 ALLPAD_LOOP (PCB->Data);
595 if (TEST_FLAG (WARNFLAG, pad))
597 CLEAR_FLAG (WARNFLAG, pad);
598 DrawPad (pad);
601 ENDALL_LOOP;
602 Draw ();
605 static void
606 click_cb (hidval hv)
608 if (Note.Click)
610 notify_crosshair_change (false);
611 Note.Click = false;
612 if (Note.Moving && !gui->shift_is_pressed ())
614 Note.Buffer = Settings.BufferNumber;
615 SetBufferNumber (MAX_BUFFER - 1);
616 ClearBuffer (PASTEBUFFER);
617 AddSelectedToBuffer (PASTEBUFFER, Note.X, Note.Y, true);
618 SaveUndoSerialNumber ();
619 RemoveSelected ();
620 SaveMode ();
621 saved_mode = true;
622 SetMode (PASTEBUFFER_MODE);
624 else if (Note.Hit && !gui->shift_is_pressed ())
626 SaveMode ();
627 saved_mode = true;
628 SetMode (gui->control_is_pressed ()? COPY_MODE : MOVE_MODE);
629 Crosshair.AttachedObject.Ptr1 = Note.ptr1;
630 Crosshair.AttachedObject.Ptr2 = Note.ptr2;
631 Crosshair.AttachedObject.Ptr3 = Note.ptr3;
632 Crosshair.AttachedObject.Type = Note.Hit;
633 AttachForCopy (Note.X, Note.Y);
635 else
637 BoxType box;
639 Note.Hit = 0;
640 Note.Moving = false;
641 SaveUndoSerialNumber ();
642 box.X1 = -MAX_COORD;
643 box.Y1 = -MAX_COORD;
644 box.X2 = MAX_COORD;
645 box.Y2 = MAX_COORD;
646 /* unselect first if shift key not down */
647 if (!gui->shift_is_pressed () && SelectBlock (&box, false))
648 SetChangedFlag (true);
649 NotifyBlock ();
650 Crosshair.AttachedBox.Point1.X = Note.X;
651 Crosshair.AttachedBox.Point1.Y = Note.Y;
653 notify_crosshair_change (true);
657 static void
658 ReleaseMode (void)
660 BoxType box;
662 if (Note.Click)
664 BoxType box;
666 box.X1 = -MAX_COORD;
667 box.Y1 = -MAX_COORD;
668 box.X2 = MAX_COORD;
669 box.Y2 = MAX_COORD;
671 Note.Click = false; /* inhibit timer action */
672 SaveUndoSerialNumber ();
673 /* unselect first if shift key not down */
674 if (!gui->shift_is_pressed ())
676 if (SelectBlock (&box, false))
677 SetChangedFlag (true);
678 if (Note.Moving)
680 Note.Moving = 0;
681 Note.Hit = 0;
682 return;
685 RestoreUndoSerialNumber ();
686 if (SelectObject ())
687 SetChangedFlag (true);
688 Note.Hit = 0;
689 Note.Moving = 0;
691 else if (Note.Moving)
693 RestoreUndoSerialNumber ();
694 NotifyMode ();
695 ClearBuffer (PASTEBUFFER);
696 SetBufferNumber (Note.Buffer);
697 Note.Moving = false;
698 Note.Hit = 0;
700 else if (Note.Hit)
702 NotifyMode ();
703 Note.Hit = 0;
705 else if (Settings.Mode == ARROW_MODE)
707 box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
708 Crosshair.AttachedBox.Point2.X);
709 box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
710 Crosshair.AttachedBox.Point2.Y);
711 box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
712 Crosshair.AttachedBox.Point2.X);
713 box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
714 Crosshair.AttachedBox.Point2.Y);
715 RestoreUndoSerialNumber ();
716 if (SelectBlock (&box, true))
717 SetChangedFlag (true);
718 else if (Bumped)
719 IncrementUndoSerialNumber ();
720 Crosshair.AttachedBox.State = STATE_FIRST;
722 if (saved_mode)
723 RestoreMode ();
724 saved_mode = false;
727 /* ---------------------------------------------------------------------------
728 * get function ID of passed string
730 #define HSIZE 257
731 static char function_hash[HSIZE];
732 static int hash_initted = 0;
734 static int
735 hashfunc(String s)
737 int i = 0;
738 while (*s)
740 i ^= i >> 16;
741 i = (i * 13) ^ (unsigned char)tolower((int) *s);
742 s ++;
744 i = (unsigned int)i % HSIZE;
745 return i;
748 static int
749 GetFunctionID (String Ident)
751 int i, h;
753 if (Ident == 0)
754 return -1;
756 if (!hash_initted)
758 hash_initted = 1;
759 if (HSIZE < ENTRIES (Functions) * 2)
761 fprintf(stderr, _("Error: function hash size too small (%d vs %lu at %s:%d)\n"),
762 HSIZE, (unsigned long) ENTRIES (Functions)*2, __FILE__, __LINE__);
763 exit(1);
765 if (ENTRIES (Functions) > 254)
767 /* Change 'char' to 'int' and remove this when we get to 256
768 strings to hash. */
769 fprintf(stderr, _("Error: function hash type too small (%d vs %lu at %s:%d)\n"),
770 256, (unsigned long) ENTRIES (Functions), __FILE__, __LINE__);
771 exit(1);
774 for (i=ENTRIES (Functions)-1; i>=0; i--)
776 h = hashfunc (Functions[i].Identifier);
777 while (function_hash[h])
778 h = (h + 1) % HSIZE;
779 function_hash[h] = i + 1;
783 i = hashfunc (Ident);
784 while (1)
786 /* We enforce the "hash table bigger than function table" rule,
787 so we know there will be at least one zero entry to find. */
788 if (!function_hash[i])
789 return (-1);
790 if (!strcasecmp (Ident, Functions[function_hash[i]-1].Identifier))
791 return ((int) Functions[function_hash[i]-1].ID);
792 i = (i + 1) % HSIZE;
796 /* ---------------------------------------------------------------------------
797 * set new coordinates if in 'RECTANGLE' mode
798 * the cursor shape is also adjusted
800 static void
801 AdjustAttachedBox (void)
803 if (Settings.Mode == ARC_MODE)
805 Crosshair.AttachedBox.otherway = gui->shift_is_pressed ();
806 return;
808 switch (Crosshair.AttachedBox.State)
810 case STATE_SECOND: /* one corner is selected */
812 /* update coordinates */
813 Crosshair.AttachedBox.Point2.X = Crosshair.X;
814 Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
815 break;
820 /* ---------------------------------------------------------------------------
821 * adjusts the objects which are to be created like attached lines...
823 void
824 AdjustAttachedObjects (void)
826 PointTypePtr pnt;
827 switch (Settings.Mode)
829 /* update at least an attached block (selection) */
830 case NO_MODE:
831 case ARROW_MODE:
832 if (Crosshair.AttachedBox.State)
834 Crosshair.AttachedBox.Point2.X = Crosshair.X;
835 Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
837 break;
839 /* rectangle creation mode */
840 case RECTANGLE_MODE:
841 case ARC_MODE:
842 AdjustAttachedBox ();
843 break;
845 /* polygon creation mode */
846 case POLYGON_MODE:
847 case POLYGONHOLE_MODE:
848 AdjustAttachedLine ();
849 break;
850 /* line creation mode */
851 case LINE_MODE:
852 if (PCB->RatDraw || PCB->Clipping == 0)
853 AdjustAttachedLine ();
854 else
855 AdjustTwoLine (PCB->Clipping - 1);
856 break;
857 /* point insertion mode */
858 case INSERTPOINT_MODE:
859 pnt = AdjustInsertPoint ();
860 if (pnt)
861 InsertedPoint = *pnt;
862 break;
863 case ROTATE_MODE:
864 break;
868 /* ---------------------------------------------------------------------------
869 * creates points of a line
871 static void
872 NotifyLine (void)
874 int type = NO_TYPE;
875 void *ptr1, *ptr2, *ptr3;
877 if (!Marked.status || TEST_FLAG (LOCALREFFLAG, PCB))
878 SetLocalRef (Crosshair.X, Crosshair.Y, true);
879 switch (Crosshair.AttachedLine.State)
881 case STATE_FIRST: /* first point */
882 if (PCB->RatDraw && SearchScreen (Crosshair.X, Crosshair.Y,
883 PAD_TYPE | PIN_TYPE, &ptr1, &ptr1,
884 &ptr1) == NO_TYPE)
886 gui->beep ();
887 break;
889 if (TEST_FLAG (AUTODRCFLAG, PCB) && Settings.Mode == LINE_MODE)
891 type = SearchScreen (Crosshair.X, Crosshair.Y,
892 PIN_TYPE | PAD_TYPE | VIA_TYPE, &ptr1, &ptr2,
893 &ptr3);
894 LookupConnection (Crosshair.X, Crosshair.Y, true, 1,
895 FOUNDFLAG);
897 if (type == PIN_TYPE || type == VIA_TYPE)
899 Crosshair.AttachedLine.Point1.X =
900 Crosshair.AttachedLine.Point2.X = ((PinTypePtr) ptr2)->X;
901 Crosshair.AttachedLine.Point1.Y =
902 Crosshair.AttachedLine.Point2.Y = ((PinTypePtr) ptr2)->Y;
904 else if (type == PAD_TYPE)
906 PadTypePtr pad = (PadTypePtr) ptr2;
907 double d1 = Distance (Crosshair.X, Crosshair.Y, pad->Point1.X, pad->Point1.Y);
908 double d2 = Distance (Crosshair.X, Crosshair.Y, pad->Point2.X, pad->Point2.Y);
909 if (d2 < d1)
911 Crosshair.AttachedLine.Point1 =
912 Crosshair.AttachedLine.Point2 = pad->Point2;
914 else
916 Crosshair.AttachedLine.Point1 =
917 Crosshair.AttachedLine.Point2 = pad->Point1;
920 else
922 Crosshair.AttachedLine.Point1.X =
923 Crosshair.AttachedLine.Point2.X = Crosshair.X;
924 Crosshair.AttachedLine.Point1.Y =
925 Crosshair.AttachedLine.Point2.Y = Crosshair.Y;
927 Crosshair.AttachedLine.State = STATE_SECOND;
928 break;
930 case STATE_SECOND:
931 /* fall through to third state too */
932 lastLayer = CURRENT;
933 default: /* all following points */
934 Crosshair.AttachedLine.State = STATE_THIRD;
935 break;
939 /* ---------------------------------------------------------------------------
940 * create first or second corner of a marked block
942 static void
943 NotifyBlock (void)
945 notify_crosshair_change (false);
946 switch (Crosshair.AttachedBox.State)
948 case STATE_FIRST: /* setup first point */
949 Crosshair.AttachedBox.Point1.X =
950 Crosshair.AttachedBox.Point2.X = Crosshair.X;
951 Crosshair.AttachedBox.Point1.Y =
952 Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
953 Crosshair.AttachedBox.State = STATE_SECOND;
954 break;
956 case STATE_SECOND: /* setup second point */
957 Crosshair.AttachedBox.State = STATE_THIRD;
958 break;
960 notify_crosshair_change (true);
964 /* ---------------------------------------------------------------------------
966 * does what's appropriate for the current mode setting. This normally
967 * means creation of an object at the current crosshair location.
969 * new created objects are added to the create undo list of course
971 static void
972 NotifyMode (void)
974 void *ptr1, *ptr2, *ptr3;
975 int type;
977 if (Settings.RatWarn)
978 ClearWarnings ();
979 switch (Settings.Mode)
981 case ARROW_MODE:
983 int test;
984 hidval hv;
986 Note.Click = true;
987 /* do something after click time */
988 gui->add_timer (click_cb, CLICK_TIME, hv);
990 /* see if we clicked on something already selected
991 * (Note.Moving) or clicked on a MOVE_TYPE
992 * (Note.Hit)
994 for (test = (SELECT_TYPES | MOVE_TYPES) & ~RATLINE_TYPE;
995 test; test &= ~type)
997 type = SearchScreen (Note.X, Note.Y, test, &ptr1, &ptr2, &ptr3);
998 if (!Note.Hit && (type & MOVE_TYPES) &&
999 !TEST_FLAG (LOCKFLAG, (PinTypePtr) ptr2))
1001 Note.Hit = type;
1002 Note.ptr1 = ptr1;
1003 Note.ptr2 = ptr2;
1004 Note.ptr3 = ptr3;
1006 if (!Note.Moving && (type & SELECT_TYPES) &&
1007 TEST_FLAG (SELECTEDFLAG, (PinTypePtr) ptr2))
1008 Note.Moving = true;
1009 if ((Note.Hit && Note.Moving) || type == NO_TYPE)
1010 break;
1012 break;
1015 case VIA_MODE:
1017 PinTypePtr via;
1019 if (!PCB->ViaOn)
1021 Message (_("You must turn via visibility on before\n"
1022 "you can place vias\n"));
1023 break;
1025 if ((via = CreateNewVia (PCB->Data, Note.X, Note.Y,
1026 Settings.ViaThickness, 2 * Settings.Keepaway,
1027 0, Settings.ViaDrillingHole, NULL,
1028 NoFlags ())) != NULL)
1030 AddObjectToCreateUndoList (VIA_TYPE, via, via, via);
1031 if (gui->shift_is_pressed ())
1032 ChangeObjectThermal (VIA_TYPE, via, via, via, PCB->ThermStyle);
1033 IncrementUndoSerialNumber ();
1034 DrawVia (via);
1035 Draw ();
1037 break;
1040 case ARC_MODE:
1042 switch (Crosshair.AttachedBox.State)
1044 case STATE_FIRST:
1045 Crosshair.AttachedBox.Point1.X =
1046 Crosshair.AttachedBox.Point2.X = Note.X;
1047 Crosshair.AttachedBox.Point1.Y =
1048 Crosshair.AttachedBox.Point2.Y = Note.Y;
1049 Crosshair.AttachedBox.State = STATE_SECOND;
1050 break;
1052 case STATE_SECOND:
1053 case STATE_THIRD:
1055 ArcTypePtr arc;
1056 Coord wx, wy;
1057 Angle sa, dir;
1059 wx = Note.X - Crosshair.AttachedBox.Point1.X;
1060 wy = Note.Y - Crosshair.AttachedBox.Point1.Y;
1061 if (XOR (Crosshair.AttachedBox.otherway, abs (wy) > abs (wx)))
1063 Crosshair.AttachedBox.Point2.X =
1064 Crosshair.AttachedBox.Point1.X + abs (wy) * SGNZ (wx);
1065 sa = (wx >= 0) ? 0 : 180;
1066 #ifdef ARC45
1067 if (abs (wy) / 2 >= abs (wx))
1068 dir = (SGNZ (wx) == SGNZ (wy)) ? 45 : -45;
1069 else
1070 #endif
1071 dir = (SGNZ (wx) == SGNZ (wy)) ? 90 : -90;
1073 else
1075 Crosshair.AttachedBox.Point2.Y =
1076 Crosshair.AttachedBox.Point1.Y + abs (wx) * SGNZ (wy);
1077 sa = (wy >= 0) ? -90 : 90;
1078 #ifdef ARC45
1079 if (abs (wx) / 2 >= abs (wy))
1080 dir = (SGNZ (wx) == SGNZ (wy)) ? -45 : 45;
1081 else
1082 #endif
1083 dir = (SGNZ (wx) == SGNZ (wy)) ? -90 : 90;
1084 wy = wx;
1086 if (abs (wy) > 0 && (arc = CreateNewArcOnLayer (CURRENT,
1087 Crosshair.
1088 AttachedBox.
1089 Point2.X,
1090 Crosshair.
1091 AttachedBox.
1092 Point2.Y,
1093 abs (wy),
1094 abs (wy),
1096 dir,
1097 Settings.
1098 LineThickness,
1099 2 * Settings.
1100 Keepaway,
1101 MakeFlags
1102 (TEST_FLAG
1103 (CLEARNEWFLAG,
1104 PCB) ?
1105 CLEARLINEFLAG :
1106 0))))
1108 BoxTypePtr bx;
1110 bx = GetArcEnds (arc);
1111 Crosshair.AttachedBox.Point1.X =
1112 Crosshair.AttachedBox.Point2.X = bx->X2;
1113 Crosshair.AttachedBox.Point1.Y =
1114 Crosshair.AttachedBox.Point2.Y = bx->Y2;
1115 AddObjectToCreateUndoList (ARC_TYPE, CURRENT, arc, arc);
1116 IncrementUndoSerialNumber ();
1117 addedLines++;
1118 DrawArc (CURRENT, arc);
1119 Draw ();
1120 Crosshair.AttachedBox.State = STATE_THIRD;
1122 break;
1125 break;
1127 case LOCK_MODE:
1129 type = SearchScreen (Note.X, Note.Y, LOCK_TYPES, &ptr1, &ptr2, &ptr3);
1130 if (type == ELEMENT_TYPE)
1132 ElementTypePtr element = (ElementTypePtr) ptr2;
1134 TOGGLE_FLAG (LOCKFLAG, element);
1135 PIN_LOOP (element);
1137 TOGGLE_FLAG (LOCKFLAG, pin);
1138 CLEAR_FLAG (SELECTEDFLAG, pin);
1140 END_LOOP;
1141 PAD_LOOP (element);
1143 TOGGLE_FLAG (LOCKFLAG, pad);
1144 CLEAR_FLAG (SELECTEDFLAG, pad);
1146 END_LOOP;
1147 CLEAR_FLAG (SELECTEDFLAG, element);
1148 /* always re-draw it since I'm too lazy
1149 * to tell if a selected flag changed
1151 DrawElement (element);
1152 Draw ();
1153 hid_actionl ("Report", "Object", NULL);
1155 else if (type != NO_TYPE)
1157 TextTypePtr thing = (TextTypePtr) ptr3;
1158 TOGGLE_FLAG (LOCKFLAG, thing);
1159 if (TEST_FLAG (LOCKFLAG, thing)
1160 && TEST_FLAG (SELECTEDFLAG, thing))
1162 /* this is not un-doable since LOCK isn't */
1163 CLEAR_FLAG (SELECTEDFLAG, thing);
1164 DrawObject (type, ptr1, ptr2);
1165 Draw ();
1167 hid_actionl ("Report", "Object", NULL);
1169 break;
1171 case THERMAL_MODE:
1173 if (((type
1175 SearchScreen (Note.X, Note.Y, PIN_TYPES, &ptr1, &ptr2,
1176 &ptr3)) != NO_TYPE)
1177 && !TEST_FLAG (HOLEFLAG, (PinTypePtr) ptr3))
1179 if (gui->shift_is_pressed ())
1181 int tstyle = GET_THERM (INDEXOFCURRENT, (PinTypePtr) ptr3);
1182 tstyle++;
1183 if (tstyle > 5)
1184 tstyle = 1;
1185 ChangeObjectThermal (type, ptr1, ptr2, ptr3, tstyle);
1187 else if (GET_THERM (INDEXOFCURRENT, (PinTypePtr) ptr3))
1188 ChangeObjectThermal (type, ptr1, ptr2, ptr3, 0);
1189 else
1190 ChangeObjectThermal (type, ptr1, ptr2, ptr3, PCB->ThermStyle);
1192 break;
1195 case LINE_MODE:
1196 /* do update of position */
1197 NotifyLine ();
1198 if (Crosshair.AttachedLine.State != STATE_THIRD)
1199 break;
1201 /* Remove anchor if clicking on start point;
1202 * this means we can't paint 0 length lines
1203 * which could be used for square SMD pads.
1204 * Instead use a very small delta, or change
1205 * the file after saving.
1207 if (Crosshair.X == Crosshair.AttachedLine.Point1.X
1208 && Crosshair.Y == Crosshair.AttachedLine.Point1.Y)
1210 SetMode (LINE_MODE);
1211 break;
1214 if (PCB->RatDraw)
1216 RatTypePtr line;
1217 if ((line = AddNet ()))
1219 addedLines++;
1220 AddObjectToCreateUndoList (RATLINE_TYPE, line, line, line);
1221 IncrementUndoSerialNumber ();
1222 DrawRat (line);
1223 Crosshair.AttachedLine.Point1.X =
1224 Crosshair.AttachedLine.Point2.X;
1225 Crosshair.AttachedLine.Point1.Y =
1226 Crosshair.AttachedLine.Point2.Y;
1227 Draw ();
1229 break;
1231 else
1232 /* create line if both ends are determined && length != 0 */
1234 LineTypePtr line;
1235 int maybe_found_flag;
1237 if (PCB->Clipping
1238 && Crosshair.AttachedLine.Point1.X ==
1239 Crosshair.AttachedLine.Point2.X
1240 && Crosshair.AttachedLine.Point1.Y ==
1241 Crosshair.AttachedLine.Point2.Y
1242 && (Crosshair.AttachedLine.Point2.X != Note.X
1243 || Crosshair.AttachedLine.Point2.Y != Note.Y))
1245 /* We will only need to paint the second line segment.
1246 Since we only check for vias on the first segment,
1247 swap them so the non-empty segment is the first segment. */
1248 Crosshair.AttachedLine.Point2.X = Note.X;
1249 Crosshair.AttachedLine.Point2.Y = Note.Y;
1252 if (TEST_FLAG (AUTODRCFLAG, PCB)
1253 && ! TEST_SILK_LAYER (CURRENT))
1254 maybe_found_flag = FOUNDFLAG;
1255 else
1256 maybe_found_flag = 0;
1258 if ((Crosshair.AttachedLine.Point1.X !=
1259 Crosshair.AttachedLine.Point2.X
1260 || Crosshair.AttachedLine.Point1.Y !=
1261 Crosshair.AttachedLine.Point2.Y)
1262 && (line =
1263 CreateDrawnLineOnLayer (CURRENT,
1264 Crosshair.AttachedLine.Point1.X,
1265 Crosshair.AttachedLine.Point1.Y,
1266 Crosshair.AttachedLine.Point2.X,
1267 Crosshair.AttachedLine.Point2.Y,
1268 Settings.LineThickness,
1269 2 * Settings.Keepaway,
1270 MakeFlags (maybe_found_flag |
1271 (TEST_FLAG
1272 (CLEARNEWFLAG,
1273 PCB) ? CLEARLINEFLAG :
1274 0)))) != NULL)
1276 PinTypePtr via;
1278 addedLines++;
1279 AddObjectToCreateUndoList (LINE_TYPE, CURRENT, line, line);
1280 DrawLine (CURRENT, line);
1281 /* place a via if vias are visible, the layer is
1282 in a new group since the last line and there
1283 isn't a pin already here */
1284 if (PCB->ViaOn && GetLayerGroupNumberByPointer (CURRENT) !=
1285 GetLayerGroupNumberByPointer (lastLayer) &&
1286 SearchObjectByLocation (PIN_TYPES, &ptr1, &ptr2, &ptr3,
1287 Crosshair.AttachedLine.Point1.X,
1288 Crosshair.AttachedLine.Point1.Y,
1289 Settings.ViaThickness / 2) ==
1290 NO_TYPE
1291 && (via =
1292 CreateNewVia (PCB->Data,
1293 Crosshair.AttachedLine.Point1.X,
1294 Crosshair.AttachedLine.Point1.Y,
1295 Settings.ViaThickness,
1296 2 * Settings.Keepaway, 0,
1297 Settings.ViaDrillingHole, NULL,
1298 NoFlags ())) != NULL)
1300 AddObjectToCreateUndoList (VIA_TYPE, via, via, via);
1301 DrawVia (via);
1303 /* copy the coordinates */
1304 Crosshair.AttachedLine.Point1.X =
1305 Crosshair.AttachedLine.Point2.X;
1306 Crosshair.AttachedLine.Point1.Y =
1307 Crosshair.AttachedLine.Point2.Y;
1308 IncrementUndoSerialNumber ();
1309 lastLayer = CURRENT;
1311 if (PCB->Clipping && (Note.X != Crosshair.AttachedLine.Point2.X
1312 || Note.Y !=
1313 Crosshair.AttachedLine.Point2.Y)
1314 && (line =
1315 CreateDrawnLineOnLayer (CURRENT,
1316 Crosshair.AttachedLine.Point2.X,
1317 Crosshair.AttachedLine.Point2.Y,
1318 Note.X, Note.Y,
1319 Settings.LineThickness,
1320 2 * Settings.Keepaway,
1321 MakeFlags ((TEST_FLAG
1322 (AUTODRCFLAG,
1323 PCB) ? FOUNDFLAG : 0) |
1324 (TEST_FLAG
1325 (CLEARNEWFLAG,
1326 PCB) ? CLEARLINEFLAG :
1327 0)))) != NULL)
1329 addedLines++;
1330 AddObjectToCreateUndoList (LINE_TYPE, CURRENT, line, line);
1331 IncrementUndoSerialNumber ();
1332 DrawLine (CURRENT, line);
1333 /* move to new start point */
1334 Crosshair.AttachedLine.Point1.X = Note.X;
1335 Crosshair.AttachedLine.Point1.Y = Note.Y;
1336 Crosshair.AttachedLine.Point2.X = Note.X;
1337 Crosshair.AttachedLine.Point2.Y = Note.Y;
1338 if (TEST_FLAG (SWAPSTARTDIRFLAG, PCB))
1340 PCB->Clipping ^= 3;
1343 Draw ();
1345 break;
1347 case RECTANGLE_MODE:
1348 /* do update of position */
1349 NotifyBlock ();
1351 /* create rectangle if both corners are determined
1352 * and width, height are != 0
1354 if (Crosshair.AttachedBox.State == STATE_THIRD &&
1355 Crosshair.AttachedBox.Point1.X != Crosshair.AttachedBox.Point2.X &&
1356 Crosshair.AttachedBox.Point1.Y != Crosshair.AttachedBox.Point2.Y)
1358 PolygonTypePtr polygon;
1360 int flags = CLEARPOLYFLAG;
1361 if (TEST_FLAG (NEWFULLPOLYFLAG, PCB))
1362 flags |= FULLPOLYFLAG;
1363 if ((polygon = CreateNewPolygonFromRectangle (CURRENT,
1364 Crosshair.
1365 AttachedBox.Point1.X,
1366 Crosshair.
1367 AttachedBox.Point1.Y,
1368 Crosshair.
1369 AttachedBox.Point2.X,
1370 Crosshair.
1371 AttachedBox.Point2.Y,
1372 MakeFlags
1373 (flags))) !=
1374 NULL)
1376 AddObjectToCreateUndoList (POLYGON_TYPE, CURRENT,
1377 polygon, polygon);
1378 IncrementUndoSerialNumber ();
1379 DrawPolygon (CURRENT, polygon);
1380 Draw ();
1383 /* reset state to 'first corner' */
1384 Crosshair.AttachedBox.State = STATE_FIRST;
1386 break;
1388 case TEXT_MODE:
1390 char *string;
1392 if ((string = gui->prompt_for (_("Enter text:"), "")) != NULL)
1394 if (strlen(string) > 0)
1396 TextTypePtr text;
1397 int flag = CLEARLINEFLAG;
1399 if (GetLayerGroupNumberByNumber (INDEXOFCURRENT) ==
1400 GetLayerGroupNumberByNumber (solder_silk_layer))
1401 flag |= ONSOLDERFLAG;
1402 if ((text = CreateNewText (CURRENT, &PCB->Font, Note.X,
1403 Note.Y, 0, Settings.TextScale,
1404 string, MakeFlags (flag))) != NULL)
1406 AddObjectToCreateUndoList (TEXT_TYPE, CURRENT, text, text);
1407 IncrementUndoSerialNumber ();
1408 DrawText (CURRENT, text);
1409 Draw ();
1412 free (string);
1414 break;
1417 case POLYGON_MODE:
1419 PointTypePtr points = Crosshair.AttachedPolygon.Points;
1420 Cardinal n = Crosshair.AttachedPolygon.PointN;
1422 /* do update of position; use the 'LINE_MODE' mechanism */
1423 NotifyLine ();
1425 /* check if this is the last point of a polygon */
1426 if (n >= 3 &&
1427 points->X == Crosshair.AttachedLine.Point2.X &&
1428 points->Y == Crosshair.AttachedLine.Point2.Y)
1430 CopyAttachedPolygonToLayer ();
1431 Draw ();
1432 break;
1435 /* create new point if it's the first one or if it's
1436 * different to the last one
1438 if (!n ||
1439 points[n - 1].X != Crosshair.AttachedLine.Point2.X ||
1440 points[n - 1].Y != Crosshair.AttachedLine.Point2.Y)
1442 CreateNewPointInPolygon (&Crosshair.AttachedPolygon,
1443 Crosshair.AttachedLine.Point2.X,
1444 Crosshair.AttachedLine.Point2.Y);
1446 /* copy the coordinates */
1447 Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X;
1448 Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y;
1450 break;
1453 case POLYGONHOLE_MODE:
1455 switch (Crosshair.AttachedObject.State)
1457 /* first notify, lookup object */
1458 case STATE_FIRST:
1459 Crosshair.AttachedObject.Type =
1460 SearchScreen (Note.X, Note.Y, POLYGON_TYPE,
1461 &Crosshair.AttachedObject.Ptr1,
1462 &Crosshair.AttachedObject.Ptr2,
1463 &Crosshair.AttachedObject.Ptr3);
1465 if (Crosshair.AttachedObject.Type != NO_TYPE)
1467 if (TEST_FLAG (LOCKFLAG, (PolygonTypePtr)
1468 Crosshair.AttachedObject.Ptr2))
1470 Message (_("Sorry, the object is locked\n"));
1471 Crosshair.AttachedObject.Type = NO_TYPE;
1472 break;
1474 else
1475 Crosshair.AttachedObject.State = STATE_SECOND;
1477 break;
1479 /* second notify, insert new point into object */
1480 case STATE_SECOND:
1482 PointTypePtr points = Crosshair.AttachedPolygon.Points;
1483 Cardinal n = Crosshair.AttachedPolygon.PointN;
1484 POLYAREA *original, *new_hole, *result;
1485 FlagType Flags;
1487 /* do update of position; use the 'LINE_MODE' mechanism */
1488 NotifyLine ();
1490 /* check if this is the last point of a polygon */
1491 if (n >= 3 &&
1492 points->X == Crosshair.AttachedLine.Point2.X &&
1493 points->Y == Crosshair.AttachedLine.Point2.Y)
1495 /* Create POLYAREAs from the original polygon
1496 * and the new hole polygon */
1497 original = PolygonToPoly ((PolygonType *)Crosshair.AttachedObject.Ptr2);
1498 new_hole = PolygonToPoly (&Crosshair.AttachedPolygon);
1500 /* Subtract the hole from the original polygon shape */
1501 poly_Boolean_free (original, new_hole, &result, PBO_SUB);
1503 /* Convert the resulting polygon(s) into a new set of nodes
1504 * and place them on the page. Delete the original polygon.
1506 SaveUndoSerialNumber ();
1507 Flags = ((PolygonType *)Crosshair.AttachedObject.Ptr2)->Flags;
1508 PolyToPolygonsOnLayer (PCB->Data, (LayerType *)Crosshair.AttachedObject.Ptr1,
1509 result, Flags);
1510 RemoveObject (POLYGON_TYPE,
1511 Crosshair.AttachedObject.Ptr1,
1512 Crosshair.AttachedObject.Ptr2,
1513 Crosshair.AttachedObject.Ptr3);
1514 RestoreUndoSerialNumber ();
1515 IncrementUndoSerialNumber ();
1516 Draw ();
1518 /* reset state of attached line */
1519 memset (&Crosshair.AttachedPolygon, 0, sizeof (PolygonType));
1520 Crosshair.AttachedLine.State = STATE_FIRST;
1521 addedLines = 0;
1523 break;
1526 /* create new point if it's the first one or if it's
1527 * different to the last one
1529 if (!n ||
1530 points[n - 1].X != Crosshair.AttachedLine.Point2.X ||
1531 points[n - 1].Y != Crosshair.AttachedLine.Point2.Y)
1533 CreateNewPointInPolygon (&Crosshair.AttachedPolygon,
1534 Crosshair.AttachedLine.Point2.X,
1535 Crosshair.AttachedLine.Point2.Y);
1537 /* copy the coordinates */
1538 Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X;
1539 Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y;
1541 break;
1545 break;
1548 case PASTEBUFFER_MODE:
1550 TextType estr[MAX_ELEMENTNAMES];
1551 ElementTypePtr e = 0;
1553 if (gui->shift_is_pressed ())
1555 int type =
1556 SearchScreen (Note.X, Note.Y, ELEMENT_TYPE, &ptr1, &ptr2,
1557 &ptr3);
1558 if (type == ELEMENT_TYPE)
1560 e = (ElementTypePtr) ptr1;
1561 if (e)
1563 int i;
1565 memcpy (estr, e->Name,
1566 MAX_ELEMENTNAMES * sizeof (TextType));
1567 for (i = 0; i < MAX_ELEMENTNAMES; ++i)
1568 estr[i].TextString = estr[i].TextString ? strdup(estr[i].TextString) : NULL;
1569 RemoveElement (e);
1573 if (CopyPastebufferToLayout (Note.X, Note.Y))
1574 SetChangedFlag (true);
1575 if (e)
1577 int type =
1578 SearchScreen (Note.X, Note.Y, ELEMENT_TYPE, &ptr1, &ptr2,
1579 &ptr3);
1580 if (type == ELEMENT_TYPE && ptr1)
1582 int i, save_n;
1583 e = (ElementTypePtr) ptr1;
1585 save_n = NAME_INDEX (PCB);
1587 for (i = 0; i < MAX_ELEMENTNAMES; i++)
1589 if (i == save_n)
1590 EraseElementName (e);
1591 r_delete_entry (PCB->Data->name_tree[i],
1592 (BoxType *) & (e->Name[i]));
1593 memcpy (&(e->Name[i]), &(estr[i]), sizeof (TextType));
1594 e->Name[i].Element = e;
1595 SetTextBoundingBox (&PCB->Font, &(e->Name[i]));
1596 r_insert_entry (PCB->Data->name_tree[i],
1597 (BoxType *) & (e->Name[i]), 0);
1598 if (i == save_n)
1599 DrawElementName (e);
1603 break;
1606 case REMOVE_MODE:
1607 if ((type =
1608 SearchScreen (Note.X, Note.Y, REMOVE_TYPES, &ptr1, &ptr2,
1609 &ptr3)) != NO_TYPE)
1611 if (TEST_FLAG (LOCKFLAG, (LineTypePtr) ptr2))
1613 Message (_("Sorry, the object is locked\n"));
1614 break;
1616 if (type == ELEMENT_TYPE)
1618 RubberbandTypePtr ptr;
1619 int i;
1621 Crosshair.AttachedObject.RubberbandN = 0;
1622 LookupRatLines (type, ptr1, ptr2, ptr3);
1623 ptr = Crosshair.AttachedObject.Rubberband;
1624 for (i = 0; i < Crosshair.AttachedObject.RubberbandN; i++)
1626 if (PCB->RatOn)
1627 EraseRat ((RatTypePtr) ptr->Line);
1628 if (TEST_FLAG (RUBBERENDFLAG, ptr->Line))
1629 MoveObjectToRemoveUndoList (RATLINE_TYPE,
1630 ptr->Line, ptr->Line,
1631 ptr->Line);
1632 else
1633 TOGGLE_FLAG (RUBBERENDFLAG, ptr->Line); /* only remove line once */
1634 ptr++;
1637 RemoveObject (type, ptr1, ptr2, ptr3);
1638 IncrementUndoSerialNumber ();
1639 SetChangedFlag (true);
1641 break;
1643 case ROTATE_MODE:
1644 RotateScreenObject (Note.X, Note.Y,
1645 gui->shift_is_pressed ()? (SWAP_IDENT ?
1646 1 : 3)
1647 : (SWAP_IDENT ? 3 : 1));
1648 break;
1650 /* both are almost the same */
1651 case COPY_MODE:
1652 case MOVE_MODE:
1653 switch (Crosshair.AttachedObject.State)
1655 /* first notify, lookup object */
1656 case STATE_FIRST:
1658 int types = (Settings.Mode == COPY_MODE) ?
1659 COPY_TYPES : MOVE_TYPES;
1661 Crosshair.AttachedObject.Type =
1662 SearchScreen (Note.X, Note.Y, types,
1663 &Crosshair.AttachedObject.Ptr1,
1664 &Crosshair.AttachedObject.Ptr2,
1665 &Crosshair.AttachedObject.Ptr3);
1666 if (Crosshair.AttachedObject.Type != NO_TYPE)
1668 if (Settings.Mode == MOVE_MODE &&
1669 TEST_FLAG (LOCKFLAG, (PinTypePtr)
1670 Crosshair.AttachedObject.Ptr2))
1672 Message (_("Sorry, the object is locked\n"));
1673 Crosshair.AttachedObject.Type = NO_TYPE;
1675 else
1676 AttachForCopy (Note.X, Note.Y);
1678 break;
1681 /* second notify, move or copy object */
1682 case STATE_SECOND:
1683 if (Settings.Mode == COPY_MODE)
1684 CopyObject (Crosshair.AttachedObject.Type,
1685 Crosshair.AttachedObject.Ptr1,
1686 Crosshair.AttachedObject.Ptr2,
1687 Crosshair.AttachedObject.Ptr3,
1688 Note.X - Crosshair.AttachedObject.X,
1689 Note.Y - Crosshair.AttachedObject.Y);
1690 else
1692 MoveObjectAndRubberband (Crosshair.AttachedObject.Type,
1693 Crosshair.AttachedObject.Ptr1,
1694 Crosshair.AttachedObject.Ptr2,
1695 Crosshair.AttachedObject.Ptr3,
1696 Note.X - Crosshair.AttachedObject.X,
1697 Note.Y - Crosshair.AttachedObject.Y);
1698 SetLocalRef (0, 0, false);
1700 SetChangedFlag (true);
1702 /* reset identifiers */
1703 Crosshair.AttachedObject.Type = NO_TYPE;
1704 Crosshair.AttachedObject.State = STATE_FIRST;
1705 break;
1707 break;
1709 /* insert a point into a polygon/line/... */
1710 case INSERTPOINT_MODE:
1711 switch (Crosshair.AttachedObject.State)
1713 /* first notify, lookup object */
1714 case STATE_FIRST:
1715 Crosshair.AttachedObject.Type =
1716 SearchScreen (Note.X, Note.Y, INSERT_TYPES,
1717 &Crosshair.AttachedObject.Ptr1,
1718 &Crosshair.AttachedObject.Ptr2,
1719 &Crosshair.AttachedObject.Ptr3);
1721 if (Crosshair.AttachedObject.Type != NO_TYPE)
1723 if (TEST_FLAG (LOCKFLAG, (PolygonTypePtr)
1724 Crosshair.AttachedObject.Ptr2))
1726 Message (_("Sorry, the object is locked\n"));
1727 Crosshair.AttachedObject.Type = NO_TYPE;
1728 break;
1730 else
1732 /* get starting point of nearest segment */
1733 if (Crosshair.AttachedObject.Type == POLYGON_TYPE)
1735 fake.poly =
1736 (PolygonTypePtr) Crosshair.AttachedObject.Ptr2;
1737 polyIndex =
1738 GetLowestDistancePolygonPoint (fake.poly, Note.X,
1739 Note.Y);
1740 fake.line.Point1 = fake.poly->Points[polyIndex];
1741 fake.line.Point2 = fake.poly->Points[
1742 prev_contour_point (fake.poly, polyIndex)];
1743 Crosshair.AttachedObject.Ptr2 = &fake.line;
1746 Crosshair.AttachedObject.State = STATE_SECOND;
1747 InsertedPoint = *AdjustInsertPoint ();
1750 break;
1752 /* second notify, insert new point into object */
1753 case STATE_SECOND:
1754 if (Crosshair.AttachedObject.Type == POLYGON_TYPE)
1755 InsertPointIntoObject (POLYGON_TYPE,
1756 Crosshair.AttachedObject.Ptr1, fake.poly,
1757 &polyIndex,
1758 InsertedPoint.X, InsertedPoint.Y, false, false);
1759 else
1760 InsertPointIntoObject (Crosshair.AttachedObject.Type,
1761 Crosshair.AttachedObject.Ptr1,
1762 Crosshair.AttachedObject.Ptr2,
1763 &polyIndex,
1764 InsertedPoint.X, InsertedPoint.Y, false, false);
1765 SetChangedFlag (true);
1767 /* reset identifiers */
1768 Crosshair.AttachedObject.Type = NO_TYPE;
1769 Crosshair.AttachedObject.State = STATE_FIRST;
1770 break;
1772 break;
1777 /* --------------------------------------------------------------------------- */
1779 static const char atomic_syntax[] = "Atomic(Save|Restore|Close|Block)";
1781 static const char atomic_help[] = "Save or restore the undo serial number.";
1783 /* %start-doc actions Atomic
1785 This action allows making multiple-action bindings into an atomic
1786 operation that will be undone by a single Undo command. For example,
1787 to optimize rat lines, you'd delete the rats and re-add them. To
1788 group these into a single undo, you'd want the deletions and the
1789 additions to have the same undo serial number. So, you @code{Save},
1790 delete the rats, @code{Restore}, add the rats - using the same serial
1791 number as the deletes, then @code{Block}, which checks to see if the
1792 deletions or additions actually did anything. If not, the serial
1793 number is set to the saved number, as there's nothing to undo. If
1794 something did happen, the serial number is incremented so that these
1795 actions are counted as a single undo step.
1797 @table @code
1799 @item Save
1800 Saves the undo serial number.
1802 @item Restore
1803 Returns it to the last saved number.
1805 @item Close
1806 Sets it to 1 greater than the last save.
1808 @item Block
1809 Does a Restore if there was nothing to undo, else does a Close.
1811 @end table
1813 %end-doc */
1815 static int
1816 ActionAtomic (int argc, char **argv, Coord x, Coord y)
1818 if (argc != 1)
1819 AFAIL (atomic);
1821 switch (GetFunctionID (argv[0]))
1823 case F_Save:
1824 SaveUndoSerialNumber ();
1825 break;
1826 case F_Restore:
1827 RestoreUndoSerialNumber ();
1828 break;
1829 case F_Close:
1830 RestoreUndoSerialNumber ();
1831 IncrementUndoSerialNumber ();
1832 break;
1833 case F_Block:
1834 RestoreUndoSerialNumber ();
1835 if (Bumped)
1836 IncrementUndoSerialNumber ();
1837 break;
1839 return 0;
1842 /* -------------------------------------------------------------------------- */
1844 static const char drc_syntax[] = "DRC()";
1846 static const char drc_help[] = "Invoke the DRC check.";
1848 /* %start-doc actions DRC
1850 Note that the design rule check uses the current board rule settings,
1851 not the current style settings.
1853 %end-doc */
1855 static int
1856 ActionDRCheck (int argc, char **argv, Coord x, Coord y)
1858 int count;
1860 if (gui->drc_gui == NULL || gui->drc_gui->log_drc_overview)
1862 Message (_("%m+Rules are minspace %$mS, minoverlap %$mS "
1863 "minwidth %$mS, minsilk %$mS\n"
1864 "min drill %$mS, min annular ring %$mS\n"),
1865 Settings.grid_unit->allow,
1866 PCB->Bloat, PCB->Shrink,
1867 PCB->minWid, PCB->minSlk,
1868 PCB->minDrill, PCB->minRing);
1870 count = DRCAll ();
1871 if (gui->drc_gui == NULL || gui->drc_gui->log_drc_overview)
1873 if (count == 0)
1874 Message (_("No DRC problems found.\n"));
1875 else if (count > 0)
1876 Message (_("Found %d design rule errors.\n"), count);
1877 else
1878 Message (_("Aborted DRC after %d design rule errors.\n"), -count);
1880 return 0;
1883 /* -------------------------------------------------------------------------- */
1885 static const char dumplibrary_syntax[] = "DumpLibrary()";
1887 static const char dumplibrary_help[] =
1888 "Display the entire contents of the libraries.";
1890 /* %start-doc actions DumpLibrary
1893 %end-doc */
1895 static int
1896 ActionDumpLibrary (int argc, char **argv, Coord x, Coord y)
1898 int i, j;
1900 printf ("**** Do not count on this format. It will change ****\n\n");
1901 printf ("MenuN = %d\n", Library.MenuN);
1902 printf ("MenuMax = %d\n", Library.MenuMax);
1903 for (i = 0; i < Library.MenuN; i++)
1905 printf ("Library #%d:\n", i);
1906 printf (" EntryN = %d\n", Library.Menu[i].EntryN);
1907 printf (" EntryMax = %d\n", Library.Menu[i].EntryMax);
1908 printf (" Name = \"%s\"\n", UNKNOWN (Library.Menu[i].Name));
1909 printf (" directory = \"%s\"\n",
1910 UNKNOWN (Library.Menu[i].directory));
1911 printf (" Style = \"%s\"\n", UNKNOWN (Library.Menu[i].Style));
1912 printf (" flag = %d\n", Library.Menu[i].flag);
1914 for (j = 0; j < Library.Menu[i].EntryN; j++)
1916 printf (" #%4d: ", j);
1917 if (Library.Menu[i].Entry[j].Template == (char *) -1)
1919 printf ("newlib: \"%s\"\n",
1920 UNKNOWN (Library.Menu[i].Entry[j].ListEntry));
1922 else
1924 printf ("\"%s\", \"%s\", \"%s\", \"%s\", \"%s\"\n",
1925 UNKNOWN (Library.Menu[i].Entry[j].ListEntry),
1926 UNKNOWN (Library.Menu[i].Entry[j].Template),
1927 UNKNOWN (Library.Menu[i].Entry[j].Package),
1928 UNKNOWN (Library.Menu[i].Entry[j].Value),
1929 UNKNOWN (Library.Menu[i].Entry[j].Description));
1934 return 0;
1937 /* -------------------------------------------------------------------------- */
1939 static const char flip_syntax[] = "Flip(Object|Selected|SelectedElements)";
1941 static const char flip_help[] =
1942 "Flip an element to the opposite side of the board.";
1944 /* %start-doc actions Flip
1946 Note that the location of the element will be symmetric about the
1947 cursor location; i.e. if the part you are pointing at will still be at
1948 the same spot once the element is on the other side. When flipping
1949 multiple elements, this retains their positions relative to each
1950 other, not their absolute positions on the board.
1952 %end-doc */
1954 static int
1955 ActionFlip (int argc, char **argv, Coord x, Coord y)
1957 char *function = ARG (0);
1958 ElementTypePtr element;
1959 void *ptrtmp;
1960 int err = 0;
1962 if (function)
1964 switch (GetFunctionID (function))
1966 case F_Object:
1967 if ((SearchScreen (x, y, ELEMENT_TYPE,
1968 &ptrtmp, &ptrtmp, &ptrtmp)) != NO_TYPE)
1970 element = (ElementTypePtr) ptrtmp;
1971 ChangeElementSide (element, 2 * Crosshair.Y - PCB->MaxHeight);
1972 IncrementUndoSerialNumber ();
1973 Draw ();
1975 break;
1976 case F_Selected:
1977 case F_SelectedElements:
1978 ChangeSelectedElementSide ();
1979 break;
1980 default:
1981 err = 1;
1982 break;
1984 if (!err)
1985 return 0;
1988 AFAIL (flip);
1991 /* -------------------------------------------------------------------------- */
1993 static const char message_syntax[] = "Message(message)";
1995 static const char message_help[] = "Writes a message to the log window.";
1997 /* %start-doc actions Message
1999 This action displays a message to the log window. This action is primarily
2000 provided for use by other programs which may interface with PCB. If
2001 multiple arguments are given, each one is sent to the log window
2002 followed by a newline.
2004 %end-doc */
2006 static int
2007 ActionMessage (int argc, char **argv, Coord x, Coord y)
2009 int i;
2011 if (argc < 1)
2012 AFAIL (message);
2014 for (i = 0; i < argc; i++)
2016 Message (argv[i]);
2017 Message ("\n");
2020 return 0;
2024 /* -------------------------------------------------------------------------- */
2026 static const char setthermal_syntax[] =
2027 "SetThermal(Object|SelectedPins|SelectedVias|Selected, Style)";
2029 static const char setthermal_help[] =
2030 "Set the thermal (on the current layer) of pins or vias to the given style.\n"
2031 "Style = 0 means no thermal.\n"
2032 "Style = 1 has diagonal fingers with sharp edges.\n"
2033 "Style = 2 has horizontal and vertical fingers with sharp edges.\n"
2034 "Style = 3 is a solid connection to the plane."
2035 "Style = 4 has diagonal fingers with rounded edges.\n"
2036 "Style = 5 has horizontal and vertical fingers with rounded edges.\n";
2038 /* %start-doc actions SetThermal
2040 This changes how/whether pins or vias connect to any rectangle or polygon
2041 on the current layer. The first argument can specify one object, or all
2042 selected pins, or all selected vias, or all selected pins and vias.
2043 The second argument specifies the style of connection.
2044 There are 5 possibilities:
2045 0 - no connection,
2046 1 - 45 degree fingers with sharp edges,
2047 2 - horizontal & vertical fingers with sharp edges,
2048 3 - solid connection,
2049 4 - 45 degree fingers with rounded corners,
2050 5 - horizontal & vertical fingers with rounded corners.
2052 Pins and Vias may have thermals whether or not there is a polygon available
2053 to connect with. However, they will have no effect without the polygon.
2054 %end-doc */
2056 static int
2057 ActionSetThermal (int argc, char **argv, Coord x, Coord y)
2059 char *function = ARG (0);
2060 char *style = ARG (1);
2061 void *ptr1, *ptr2, *ptr3;
2062 int type, kind;
2063 int err = 0;
2065 if (function && *function && style && *style)
2067 bool absolute;
2069 kind = GetValue (style, NULL, &absolute);
2070 if (absolute)
2071 switch (GetFunctionID (function))
2073 case F_Object:
2074 if ((type =
2075 SearchScreen (Crosshair.X, Crosshair.Y, CHANGETHERMAL_TYPES,
2076 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
2078 ChangeObjectThermal (type, ptr1, ptr2, ptr3, kind);
2079 IncrementUndoSerialNumber ();
2080 Draw ();
2082 break;
2083 case F_SelectedPins:
2084 ChangeSelectedThermals (PIN_TYPE, kind);
2085 break;
2086 case F_SelectedVias:
2087 ChangeSelectedThermals (VIA_TYPE, kind);
2088 break;
2089 case F_Selected:
2090 case F_SelectedElements:
2091 ChangeSelectedThermals (CHANGETHERMAL_TYPES, kind);
2092 break;
2093 default:
2094 err = 1;
2095 break;
2097 else
2098 err = 1;
2099 if (!err)
2100 return 0;
2103 AFAIL (setthermal);
2106 /* ---------------------------------------------------------------------------
2107 * !!! no action routine !!!
2109 * event handler to set the cursor according to the X pointer position
2110 * called from inside main.c
2112 void
2113 EventMoveCrosshair (int ev_x, int ev_y)
2115 #ifdef HAVE_LIBSTROKE
2116 if (mid_stroke)
2118 StrokeBox.X2 = ev_x;
2119 StrokeBox.Y2 = ev_y;
2120 stroke_record (ev_x, ev_y);
2121 return;
2123 #endif /* HAVE_LIBSTROKE */
2124 if (MoveCrosshairAbsolute (ev_x, ev_y))
2126 /* update object position and cursor location */
2127 AdjustAttachedObjects ();
2128 notify_crosshair_change (true);
2132 /* --------------------------------------------------------------------------- */
2134 static const char setvalue_syntax[] =
2135 "SetValue(Grid|Line|LineSize|Text|TextScale|ViaDrillingHole|Via|ViaSize, delta)";
2137 static const char setvalue_help[] =
2138 "Change various board-wide values and sizes.";
2140 /* %start-doc actions SetValue
2142 @table @code
2144 @item ViaDrillingHole
2145 Changes the diameter of the drill for new vias.
2147 @item Grid
2148 Sets the grid spacing.
2150 @item Line
2151 @item LineSize
2152 Changes the thickness of new lines.
2154 @item Via
2155 @item ViaSize
2156 Changes the diameter of new vias.
2158 @item Text
2159 @item TextScale
2160 Changes the size of new text.
2162 @end table
2164 %end-doc */
2166 static int
2167 ActionSetValue (int argc, char **argv, Coord x, Coord y)
2169 char *function = ARG (0);
2170 char *val = ARG (1);
2171 char *units = ARG (2);
2172 bool absolute; /* flag for 'absolute' value */
2173 double value;
2174 int text_scale;
2175 int err = 0;
2177 if (function && val)
2179 value = GetValue (val, units, &absolute);
2180 switch (GetFunctionID (function))
2182 case F_ViaDrillingHole:
2183 SetViaDrillingHole (absolute ? value :
2184 value + Settings.ViaDrillingHole,
2185 false);
2186 hid_action ("RouteStylesChanged");
2187 break;
2189 case F_Grid:
2190 if (absolute)
2191 SetGrid (value, false);
2192 else
2194 /* On the way down, short against the minimum
2195 * PCB drawing unit */
2196 if ((value + PCB->Grid) < 1)
2197 SetGrid (1, false);
2198 else if (PCB->Grid == 1)
2199 SetGrid (value, false);
2200 else
2201 SetGrid (value + PCB->Grid, false);
2203 break;
2205 case F_LineSize:
2206 case F_Line:
2207 SetLineSize (absolute ? value : value + Settings.LineThickness);
2208 hid_action ("RouteStylesChanged");
2209 break;
2211 case F_Via:
2212 case F_ViaSize:
2213 SetViaSize (absolute ? value : value + Settings.ViaThickness, false);
2214 hid_action ("RouteStylesChanged");
2215 break;
2217 case F_Text:
2218 case F_TextScale:
2219 text_scale = value / (double)FONT_CAPHEIGHT * 100.;
2220 if (!absolute)
2221 text_scale += Settings.TextScale;
2222 SetTextScale (text_scale);
2223 break;
2224 default:
2225 err = 1;
2226 break;
2228 if (!err)
2229 return 0;
2232 AFAIL (setvalue);
2236 /* --------------------------------------------------------------------------- */
2238 static const char quit_syntax[] = "Quit()";
2240 static const char quit_help[] = "Quits the application after confirming.";
2242 /* %start-doc actions Quit
2244 If you have unsaved changes, you will be prompted to confirm (or
2245 save) before quitting.
2247 %end-doc */
2249 static int
2250 ActionQuit (int argc, char **argv, Coord x, Coord y)
2252 char *force = ARG (0);
2253 if (force && strcasecmp (force, "force") == 0)
2255 PCB->Changed = 0;
2256 exit (0);
2258 if (!PCB->Changed || gui->close_confirm_dialog () == HID_CLOSE_CONFIRM_OK)
2259 QuitApplication ();
2260 return 1;
2263 /* --------------------------------------------------------------------------- */
2265 static const char connection_syntax[] =
2266 "Connection(Find|ResetLinesAndPolygons|ResetPinsAndVias|Reset)";
2268 static const char connection_help[] =
2269 "Searches connections of the object at the cursor position.";
2271 /* %start-doc actions Connection
2273 Connections found with this action will be highlighted in the
2274 ``connected-color'' color and will have the ``found'' flag set.
2276 @table @code
2278 @item Find
2279 The net under the cursor is ``found''.
2281 @item ResetLinesAndPolygons
2282 Any ``found'' lines and polygons are marked ``not found''.
2284 @item ResetPinsAndVias
2285 Any ``found'' pins and vias are marked ``not found''.
2287 @item Reset
2288 All ``found'' objects are marked ``not found''.
2290 @end table
2292 %end-doc */
2294 static int
2295 ActionConnection (int argc, char **argv, Coord x, Coord y)
2297 char *function = ARG (0);
2298 if (function)
2300 switch (GetFunctionID (function))
2302 case F_Find:
2304 gui->get_coords (_("Click on a connection"), &x, &y);
2305 LookupConnection (x, y, true, 1, FOUNDFLAG);
2306 break;
2309 case F_ResetLinesAndPolygons:
2310 if (ResetFoundLinesAndPolygons (true))
2312 IncrementUndoSerialNumber ();
2313 Draw ();
2315 break;
2317 case F_ResetPinsViasAndPads:
2318 if (ResetFoundPinsViasAndPads (true))
2320 IncrementUndoSerialNumber ();
2321 Draw ();
2323 break;
2325 case F_Reset:
2326 if (ResetConnections (true))
2328 IncrementUndoSerialNumber ();
2329 Draw ();
2331 break;
2333 return 0;
2336 AFAIL (connection);
2339 /* --------------------------------------------------------------------------- */
2341 static const char disperseelements_syntax[] =
2342 "DisperseElements(All|Selected)";
2344 static const char disperseelements_help[] = "Disperses elements.";
2346 /* %start-doc actions DisperseElements
2348 Normally this is used when starting a board, by selecting all elements
2349 and then dispersing them. This scatters the elements around the board
2350 so that you can pick individual ones, rather than have all the
2351 elements at the same 0,0 coordinate and thus impossible to choose
2352 from.
2354 %end-doc */
2356 #define GAP MIL_TO_COORD(100)
2358 static int
2359 ActionDisperseElements (int argc, char **argv, Coord x, Coord y)
2361 char *function = ARG (0);
2362 Coord minx = GAP,
2363 miny = GAP,
2364 maxy = GAP,
2365 dx, dy;
2366 int all = 0, bad = 0;
2368 if (!function || !*function)
2370 bad = 1;
2372 else
2374 switch (GetFunctionID (function))
2376 case F_All:
2377 all = 1;
2378 break;
2380 case F_Selected:
2381 all = 0;
2382 break;
2384 default:
2385 bad = 1;
2389 if (bad)
2391 AFAIL (disperseelements);
2395 ELEMENT_LOOP (PCB->Data);
2398 * If we want to disperse selected elements, maybe we need smarter
2399 * code here to avoid putting components on top of others which
2400 * are not selected. For now, I'm assuming that this is typically
2401 * going to be used either with a brand new design or a scratch
2402 * design holding some new components
2404 if (!TEST_FLAG (LOCKFLAG, element) && (all || TEST_FLAG (SELECTEDFLAG, element)))
2407 /* figure out how much to move the element */
2408 dx = minx - element->BoundingBox.X1;
2410 /* snap to the grid */
2411 dx -= (element->MarkX + dx) % PCB->Grid;
2414 * and add one grid size so we make sure we always space by GAP or
2415 * more
2417 dx += PCB->Grid;
2419 /* Figure out if this row has room. If not, start a new row */
2420 if (GAP + element->BoundingBox.X2 + dx > PCB->MaxWidth)
2422 miny = maxy + GAP;
2423 minx = GAP;
2426 /* figure out how much to move the element */
2427 dx = minx - element->BoundingBox.X1;
2428 dy = miny - element->BoundingBox.Y1;
2430 /* snap to the grid */
2431 dx -= (element->MarkX + dx) % PCB->Grid;
2432 dx += PCB->Grid;
2433 dy -= (element->MarkY + dy) % PCB->Grid;
2434 dy += PCB->Grid;
2436 /* move the element */
2437 MoveElementLowLevel (PCB->Data, element, dx, dy);
2439 /* and add to the undo list so we can undo this operation */
2440 AddObjectToMoveUndoList (ELEMENT_TYPE, NULL, NULL, element, dx, dy);
2442 /* keep track of how tall this row is */
2443 minx += element->BoundingBox.X2 - element->BoundingBox.X1 + GAP;
2444 if (maxy < element->BoundingBox.Y2)
2446 maxy = element->BoundingBox.Y2;
2451 END_LOOP;
2453 /* done with our action so increment the undo # */
2454 IncrementUndoSerialNumber ();
2456 Redraw ();
2457 SetChangedFlag (true);
2459 return 0;
2462 #undef GAP
2464 /* --------------------------------------------------------------------------- */
2466 static const char display_syntax[] =
2467 "Display(NameOnPCB|Description|Value)\n"
2468 "Display(Grid|Redraw)\n"
2469 "Display(CycleClip|CycleCrosshair|Toggle45Degree|ToggleStartDirection)\n"
2470 "Display(ToggleGrid|ToggleRubberBandMode|ToggleUniqueNames)\n"
2471 "Display(ToggleMask|ToggleName|ToggleClearLine|ToggleFullPoly|ToggleSnapPin)\n"
2472 "Display(ToggleThindraw|ToggleThindrawPoly|ToggleOrthoMove|ToggleLocalRef)\n"
2473 "Display(ToggleCheckPlanes|ToggleShowDRC|ToggleAutoDRC)\n"
2474 "Display(ToggleLiveRoute|LockNames|OnlyNames)\n"
2475 "Display(Pinout|PinOrPadName)";
2477 static const char display_help[] = "Several display-related actions.";
2479 /* %start-doc actions Display
2481 @table @code
2483 @item NameOnPCB
2484 @item Description
2485 @item Value
2486 Specify whether all elements show their name, description, or value.
2488 @item Redraw
2489 Redraw the whole board.
2491 @item Toggle45Degree
2492 When clear, lines can be drawn at any angle. When set, lines are
2493 restricted to multiples of 45 degrees and requested lines may be
2494 broken up according to the clip setting.
2496 @item CycleClip
2497 Changes the way lines are restricted to 45 degree increments. The
2498 various settings are: straight only, orthogonal then angled, and angled
2499 then orthogonal. If AllDirections is set, this action disables it.
2501 @item CycleCrosshair
2502 Changes crosshair drawing. Crosshair may accept form of 4-ray,
2503 8-ray and 12-ray cross.
2505 @item ToggleRubberBandMode
2506 If set, moving an object moves all the lines attached to it too.
2508 @item ToggleStartDirection
2509 If set, each time you set a point in a line, the Clip toggles between
2510 orth-angle and angle-ortho.
2512 @item ToggleUniqueNames
2513 If set, you will not be permitted to change the name of an element to
2514 match that of another element.
2516 @item ToggleSnapPin
2517 If set, pin centers and pad end points are treated as additional grid
2518 points that the cursor can snap to.
2520 @item ToggleLocalRef
2521 If set, the mark is automatically set to the beginning of any move, so
2522 you can see the relative distance you've moved.
2524 @item ToggleThindraw
2525 If set, objects on the screen are drawn as outlines (lines are drawn
2526 as center-lines). This lets you see line endpoints hidden under pins,
2527 for example.
2529 @item ToggleThindrawPoly
2530 If set, polygons on the screen are drawn as outlines.
2532 @item ToggleShowDRC
2533 If set, pending objects (i.e. lines you're in the process of drawing)
2534 will be drawn with an outline showing how far away from other copper
2535 you need to be.
2537 @item ToggleLiveRoute
2538 If set, the progress of the autorouter will be visible on the screen.
2540 @item ToggleAutoDRC
2541 If set, you will not be permitted to make connections which violate
2542 the current DRC and netlist settings.
2544 @item ToggleCheckPlanes
2545 If set, lines and arcs aren't drawn, which usually leaves just the
2546 polygons. If you also disable all but the layer you're interested in,
2547 this allows you to check for isolated regions.
2549 @item ToggleOrthoMove
2550 If set, the crosshair is only allowed to move orthogonally from its
2551 previous position. I.e. you can move an element or line up, down,
2552 left, or right, but not up+left or down+right.
2554 @item ToggleName
2555 Selects whether the pinouts show the pin names or the pin numbers.
2557 @item ToggleLockNames
2558 If set, text will ignore left mouse clicks and actions that work on
2559 objects under the mouse. You can still select text with a lasso (left
2560 mouse drag) and perform actions on the selection.
2562 @item ToggleOnlyNames
2563 If set, only text will be sensitive for mouse clicks and actions that
2564 work on objects under the mouse. You can still select other objects
2565 with a lasso (left mouse drag) and perform actions on the selection.
2567 @item ToggleMask
2568 Turns the solder mask on or off.
2570 @item ToggleClearLine
2571 When set, the clear-line flag causes new lines and arcs to have their
2572 ``clear polygons'' flag set, so they won't be electrically connected
2573 to any polygons they overlap.
2575 @item ToggleFullPoly
2576 When set, the full-poly flag causes new polygons to have their
2577 ``full polygon'' flag set, so all parts of them will be displayed
2578 instead of only the biggest one.
2580 @item ToggleGrid
2581 Resets the origin of the current grid to be wherever the mouse pointer
2582 is (not where the crosshair currently is). If you provide two numbers
2583 after this, the origin is set to that coordinate.
2585 @item Grid
2586 Toggles whether the grid is displayed or not.
2588 @item Pinout
2589 Causes the pinout of the element indicated by the cursor to be
2590 displayed, usually in a separate window.
2592 @item PinOrPadName
2593 Toggles whether the names of pins, pads, or (yes) vias will be
2594 displayed. If the cursor is over an element, all of its pins and pads
2595 are affected.
2597 @end table
2599 %end-doc */
2601 static enum crosshair_shape
2602 CrosshairShapeIncrement (enum crosshair_shape shape)
2604 switch(shape)
2606 case Basic_Crosshair_Shape:
2607 shape = Union_Jack_Crosshair_Shape;
2608 break;
2609 case Union_Jack_Crosshair_Shape:
2610 shape = Dozen_Crosshair_Shape;
2611 break;
2612 case Dozen_Crosshair_Shape:
2613 shape = Crosshair_Shapes_Number;
2614 break;
2615 case Crosshair_Shapes_Number:
2616 shape = Basic_Crosshair_Shape;
2617 break;
2619 return shape;
2622 static int
2623 ActionDisplay (int argc, char **argv, Coord childX, Coord childY)
2625 char *function, *str_dir;
2626 int id;
2627 int err = 0;
2629 function = ARG (0);
2630 str_dir = ARG (1);
2632 if (function && (!str_dir || !*str_dir))
2634 switch (id = GetFunctionID (function))
2637 /* redraw layout */
2638 case F_ClearAndRedraw:
2639 case F_Redraw:
2640 Redraw ();
2641 break;
2643 /* change the displayed name of elements */
2644 case F_Value:
2645 case F_NameOnPCB:
2646 case F_Description:
2647 ELEMENT_LOOP (PCB->Data);
2649 EraseElementName (element);
2651 END_LOOP;
2652 CLEAR_FLAG (DESCRIPTIONFLAG | NAMEONPCBFLAG, PCB);
2653 switch (id)
2655 case F_Value:
2656 break;
2657 case F_NameOnPCB:
2658 SET_FLAG (NAMEONPCBFLAG, PCB);
2659 break;
2660 case F_Description:
2661 SET_FLAG (DESCRIPTIONFLAG, PCB);
2662 break;
2664 ELEMENT_LOOP (PCB->Data);
2666 DrawElementName (element);
2668 END_LOOP;
2669 Draw ();
2670 break;
2672 /* toggle line-adjust flag */
2673 case F_ToggleAllDirections:
2674 TOGGLE_FLAG (ALLDIRECTIONFLAG, PCB);
2675 AdjustAttachedObjects ();
2676 break;
2678 case F_CycleClip:
2679 notify_crosshair_change (false);
2680 if (TEST_FLAG (ALLDIRECTIONFLAG, PCB))
2682 TOGGLE_FLAG (ALLDIRECTIONFLAG, PCB);
2683 PCB->Clipping = 0;
2685 else
2686 PCB->Clipping = (PCB->Clipping + 1) % 3;
2687 AdjustAttachedObjects ();
2688 notify_crosshair_change (true);
2689 break;
2691 case F_CycleCrosshair:
2692 notify_crosshair_change (false);
2693 Crosshair.shape = CrosshairShapeIncrement(Crosshair.shape);
2694 if (Crosshair_Shapes_Number == Crosshair.shape)
2695 Crosshair.shape = Basic_Crosshair_Shape;
2696 notify_crosshair_change (true);
2697 break;
2699 case F_ToggleRubberBandMode:
2700 notify_crosshair_change (false);
2701 TOGGLE_FLAG (RUBBERBANDFLAG, PCB);
2702 notify_crosshair_change (true);
2703 break;
2705 case F_ToggleStartDirection:
2706 notify_crosshair_change (false);
2707 TOGGLE_FLAG (SWAPSTARTDIRFLAG, PCB);
2708 notify_crosshair_change (true);
2709 break;
2711 case F_ToggleUniqueNames:
2712 TOGGLE_FLAG (UNIQUENAMEFLAG, PCB);
2713 break;
2715 case F_ToggleSnapPin:
2716 notify_crosshair_change (false);
2717 TOGGLE_FLAG (SNAPPINFLAG, PCB);
2718 notify_crosshair_change (true);
2719 break;
2721 case F_ToggleLocalRef:
2722 TOGGLE_FLAG (LOCALREFFLAG, PCB);
2723 break;
2725 case F_ToggleThindraw:
2726 TOGGLE_FLAG (THINDRAWFLAG, PCB);
2727 Redraw ();
2728 break;
2730 case F_ToggleThindrawPoly:
2731 TOGGLE_FLAG (THINDRAWPOLYFLAG, PCB);
2732 Redraw ();
2733 break;
2735 case F_ToggleLockNames:
2736 TOGGLE_FLAG (LOCKNAMESFLAG, PCB);
2737 CLEAR_FLAG (ONLYNAMESFLAG, PCB);
2738 break;
2740 case F_ToggleOnlyNames:
2741 TOGGLE_FLAG (ONLYNAMESFLAG, PCB);
2742 CLEAR_FLAG (LOCKNAMESFLAG, PCB);
2743 break;
2745 case F_ToggleHideNames:
2746 TOGGLE_FLAG (HIDENAMESFLAG, PCB);
2747 Redraw ();
2748 break;
2750 case F_ToggleShowDRC:
2751 TOGGLE_FLAG (SHOWDRCFLAG, PCB);
2752 break;
2754 case F_ToggleLiveRoute:
2755 TOGGLE_FLAG (LIVEROUTEFLAG, PCB);
2756 break;
2758 case F_ToggleAutoDRC:
2759 notify_crosshair_change (false);
2760 TOGGLE_FLAG (AUTODRCFLAG, PCB);
2761 if (TEST_FLAG (AUTODRCFLAG, PCB) && Settings.Mode == LINE_MODE)
2763 if (ResetConnections (true))
2765 IncrementUndoSerialNumber ();
2766 Draw ();
2768 if (Crosshair.AttachedLine.State != STATE_FIRST)
2769 LookupConnection (Crosshair.AttachedLine.Point1.X,
2770 Crosshair.AttachedLine.Point1.Y, true, 1,
2771 FOUNDFLAG);
2773 notify_crosshair_change (true);
2774 break;
2776 case F_ToggleCheckPlanes:
2777 TOGGLE_FLAG (CHECKPLANESFLAG, PCB);
2778 Redraw ();
2779 break;
2781 case F_ToggleOrthoMove:
2782 TOGGLE_FLAG (ORTHOMOVEFLAG, PCB);
2783 break;
2785 case F_ToggleName:
2786 TOGGLE_FLAG (SHOWNUMBERFLAG, PCB);
2787 Redraw ();
2788 break;
2790 case F_ToggleMask:
2791 TOGGLE_FLAG (SHOWMASKFLAG, PCB);
2792 Redraw ();
2793 break;
2795 case F_ToggleClearLine:
2796 TOGGLE_FLAG (CLEARNEWFLAG, PCB);
2797 break;
2799 case F_ToggleFullPoly:
2800 TOGGLE_FLAG (NEWFULLPOLYFLAG, PCB);
2801 break;
2803 /* shift grid alignment */
2804 case F_ToggleGrid:
2806 Coord oldGrid = PCB->Grid;
2808 PCB->Grid = 1;
2809 if (MoveCrosshairAbsolute (Crosshair.X, Crosshair.Y))
2810 notify_crosshair_change (true); /* first notify was in MoveCrosshairAbs */
2811 SetGrid (oldGrid, true);
2813 break;
2815 /* toggle displaying of the grid */
2816 case F_Grid:
2817 Settings.DrawGrid = !Settings.DrawGrid;
2818 Redraw ();
2819 break;
2821 /* display the pinout of an element */
2822 case F_Pinout:
2824 ElementTypePtr element;
2825 void *ptrtmp;
2826 Coord x, y;
2828 gui->get_coords (_("Click on an element"), &x, &y);
2829 if ((SearchScreen
2830 (x, y, ELEMENT_TYPE, &ptrtmp,
2831 &ptrtmp, &ptrtmp)) != NO_TYPE)
2833 element = (ElementTypePtr) ptrtmp;
2834 gui->show_item (element);
2836 break;
2839 /* toggle displaying of pin/pad/via names */
2840 case F_PinOrPadName:
2842 void *ptr1, *ptr2, *ptr3;
2844 switch (SearchScreen (Crosshair.X, Crosshair.Y,
2845 ELEMENT_TYPE | PIN_TYPE | PAD_TYPE |
2846 VIA_TYPE, (void **) &ptr1, (void **) &ptr2,
2847 (void **) &ptr3))
2849 case ELEMENT_TYPE:
2850 PIN_LOOP ((ElementTypePtr) ptr1);
2852 if (TEST_FLAG (DISPLAYNAMEFLAG, pin))
2853 ErasePinName (pin);
2854 else
2855 DrawPinName (pin);
2856 AddObjectToFlagUndoList (PIN_TYPE, ptr1, pin, pin);
2857 TOGGLE_FLAG (DISPLAYNAMEFLAG, pin);
2859 END_LOOP;
2860 PAD_LOOP ((ElementTypePtr) ptr1);
2862 if (TEST_FLAG (DISPLAYNAMEFLAG, pad))
2863 ErasePadName (pad);
2864 else
2865 DrawPadName (pad);
2866 AddObjectToFlagUndoList (PAD_TYPE, ptr1, pad, pad);
2867 TOGGLE_FLAG (DISPLAYNAMEFLAG, pad);
2869 END_LOOP;
2870 SetChangedFlag (true);
2871 IncrementUndoSerialNumber ();
2872 Draw ();
2873 break;
2875 case PIN_TYPE:
2876 if (TEST_FLAG (DISPLAYNAMEFLAG, (PinTypePtr) ptr2))
2877 ErasePinName ((PinTypePtr) ptr2);
2878 else
2879 DrawPinName ((PinTypePtr) ptr2);
2880 AddObjectToFlagUndoList (PIN_TYPE, ptr1, ptr2, ptr3);
2881 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PinTypePtr) ptr2);
2882 SetChangedFlag (true);
2883 IncrementUndoSerialNumber ();
2884 Draw ();
2885 break;
2887 case PAD_TYPE:
2888 if (TEST_FLAG (DISPLAYNAMEFLAG, (PadTypePtr) ptr2))
2889 ErasePadName ((PadTypePtr) ptr2);
2890 else
2891 DrawPadName ((PadTypePtr) ptr2);
2892 AddObjectToFlagUndoList (PAD_TYPE, ptr1, ptr2, ptr3);
2893 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PadTypePtr) ptr2);
2894 SetChangedFlag (true);
2895 IncrementUndoSerialNumber ();
2896 Draw ();
2897 break;
2898 case VIA_TYPE:
2899 if (TEST_FLAG (DISPLAYNAMEFLAG, (PinTypePtr) ptr2))
2900 EraseViaName ((PinTypePtr) ptr2);
2901 else
2902 DrawViaName ((PinTypePtr) ptr2);
2903 AddObjectToFlagUndoList (VIA_TYPE, ptr1, ptr2, ptr3);
2904 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PinTypePtr) ptr2);
2905 SetChangedFlag (true);
2906 IncrementUndoSerialNumber ();
2907 Draw ();
2908 break;
2910 break;
2912 default:
2913 err = 1;
2916 else if (function && str_dir)
2918 switch (GetFunctionID (function))
2920 case F_ToggleGrid:
2921 if (argc > 2)
2923 PCB->GridOffsetX = GetValue (argv[1], NULL, NULL);
2924 PCB->GridOffsetY = GetValue (argv[2], NULL, NULL);
2925 if (Settings.DrawGrid)
2926 Redraw ();
2928 break;
2930 default:
2931 err = 1;
2932 break;
2936 if (!err)
2937 return 0;
2939 AFAIL (display);
2942 /* --------------------------------------------------------------------------- */
2944 static const char mode_syntax[] =
2945 "Mode(Arc|Arrow|Copy|InsertPoint|Line|Lock|Move|None|PasteBuffer)\n"
2946 "Mode(Polygon|Rectangle|Remove|Rotate|Text|Thermal|Via)\n"
2947 "Mode(Notify|Release|Cancel|Stroke)\n"
2948 "Mode(Save|Restore)";
2950 static const char mode_help[] = "Change or use the tool mode.";
2952 /* %start-doc actions Mode
2954 @table @code
2956 @item Arc
2957 @itemx Arrow
2958 @itemx Copy
2959 @itemx InsertPoint
2960 @itemx Line
2961 @itemx Lock
2962 @itemx Move
2963 @itemx None
2964 @itemx PasteBuffer
2965 @itemx Polygon
2966 @itemx Rectangle
2967 @itemx Remove
2968 @itemx Rotate
2969 @itemx Text
2970 @itemx Thermal
2971 @itemx Via
2972 Select the indicated tool.
2974 @item Notify
2975 Called when you press the mouse button, or move the mouse.
2977 @item Release
2978 Called when you release the mouse button.
2980 @item Cancel
2981 Cancels any pending tool activity, allowing you to restart elsewhere.
2982 For example, this allows you to start a new line rather than attach a
2983 line to the previous line.
2985 @item Escape
2986 Similar to Cancel but calling this action a second time will return
2987 to the Arrow tool.
2989 @item Stroke
2990 If your @code{pcb} was built with libstroke, this invokes the stroke
2991 input method. If not, this will restart a drawing mode if you were
2992 drawing, else it will select objects.
2994 @item Save
2995 Remembers the current tool.
2997 @item Restore
2998 Restores the tool to the last saved tool.
3000 @end table
3002 %end-doc */
3004 static int
3005 ActionMode (int argc, char **argv, Coord x, Coord y)
3007 char *function = ARG (0);
3009 if (function)
3011 Note.X = Crosshair.X;
3012 Note.Y = Crosshair.Y;
3013 notify_crosshair_change (false);
3014 switch (GetFunctionID (function))
3016 case F_Arc:
3017 SetMode (ARC_MODE);
3018 break;
3019 case F_Arrow:
3020 SetMode (ARROW_MODE);
3021 break;
3022 case F_Copy:
3023 SetMode (COPY_MODE);
3024 break;
3025 case F_InsertPoint:
3026 SetMode (INSERTPOINT_MODE);
3027 break;
3028 case F_Line:
3029 SetMode (LINE_MODE);
3030 break;
3031 case F_Lock:
3032 SetMode (LOCK_MODE);
3033 break;
3034 case F_Move:
3035 SetMode (MOVE_MODE);
3036 break;
3037 case F_None:
3038 SetMode (NO_MODE);
3039 break;
3040 case F_Cancel:
3042 int saved_mode = Settings.Mode;
3043 SetMode (NO_MODE);
3044 SetMode (saved_mode);
3046 break;
3047 case F_Escape:
3049 switch (Settings.Mode)
3051 case VIA_MODE:
3052 case PASTEBUFFER_MODE:
3053 case TEXT_MODE:
3054 case ROTATE_MODE:
3055 case REMOVE_MODE:
3056 case MOVE_MODE:
3057 case COPY_MODE:
3058 case INSERTPOINT_MODE:
3059 case RUBBERBANDMOVE_MODE:
3060 case THERMAL_MODE:
3061 case LOCK_MODE:
3062 SetMode (NO_MODE);
3063 SetMode (ARROW_MODE);
3064 break;
3066 case LINE_MODE:
3067 if (Crosshair.AttachedLine.State == STATE_FIRST)
3068 SetMode (ARROW_MODE);
3069 else
3071 SetMode (NO_MODE);
3072 SetMode (LINE_MODE);
3074 break;
3076 case RECTANGLE_MODE:
3077 if (Crosshair.AttachedBox.State == STATE_FIRST)
3078 SetMode (ARROW_MODE);
3079 else
3081 SetMode (NO_MODE);
3082 SetMode (RECTANGLE_MODE);
3084 break;
3086 case POLYGON_MODE:
3087 if (Crosshair.AttachedLine.State == STATE_FIRST)
3088 SetMode (ARROW_MODE);
3089 else
3091 SetMode (NO_MODE);
3092 SetMode (POLYGON_MODE);
3094 break;
3096 case POLYGONHOLE_MODE:
3097 if (Crosshair.AttachedLine.State == STATE_FIRST)
3098 SetMode (ARROW_MODE);
3099 else
3101 SetMode (NO_MODE);
3102 SetMode (POLYGONHOLE_MODE);
3104 break;
3106 case ARC_MODE:
3107 if (Crosshair.AttachedBox.State == STATE_FIRST)
3108 SetMode (ARROW_MODE);
3109 else
3111 SetMode (NO_MODE);
3112 SetMode (ARC_MODE);
3114 break;
3116 case ARROW_MODE:
3117 break;
3119 default:
3120 break;
3123 break;
3125 case F_Notify:
3126 NotifyMode ();
3127 break;
3128 case F_PasteBuffer:
3129 SetMode (PASTEBUFFER_MODE);
3130 break;
3131 case F_Polygon:
3132 SetMode (POLYGON_MODE);
3133 break;
3134 case F_PolygonHole:
3135 SetMode (POLYGONHOLE_MODE);
3136 break;
3137 #ifndef HAVE_LIBSTROKE
3138 case F_Release:
3139 ReleaseMode ();
3140 break;
3141 #else
3142 case F_Release:
3143 if (mid_stroke)
3144 FinishStroke ();
3145 else
3146 ReleaseMode ();
3147 break;
3148 #endif
3149 case F_Remove:
3150 SetMode (REMOVE_MODE);
3151 break;
3152 case F_Rectangle:
3153 SetMode (RECTANGLE_MODE);
3154 break;
3155 case F_Rotate:
3156 SetMode (ROTATE_MODE);
3157 break;
3158 case F_Stroke:
3159 #ifdef HAVE_LIBSTROKE
3160 mid_stroke = true;
3161 StrokeBox.X1 = Crosshair.X;
3162 StrokeBox.Y1 = Crosshair.Y;
3163 break;
3164 #else
3165 /* Handle middle mouse button restarts of drawing mode. If not in
3166 | a drawing mode, middle mouse button will select objects.
3168 if (Settings.Mode == LINE_MODE
3169 && Crosshair.AttachedLine.State != STATE_FIRST)
3171 SetMode (LINE_MODE);
3173 else if (Settings.Mode == ARC_MODE
3174 && Crosshair.AttachedBox.State != STATE_FIRST)
3175 SetMode (ARC_MODE);
3176 else if (Settings.Mode == RECTANGLE_MODE
3177 && Crosshair.AttachedBox.State != STATE_FIRST)
3178 SetMode (RECTANGLE_MODE);
3179 else if (Settings.Mode == POLYGON_MODE
3180 && Crosshair.AttachedLine.State != STATE_FIRST)
3181 SetMode (POLYGON_MODE);
3182 else
3184 SaveMode ();
3185 saved_mode = true;
3186 SetMode (ARROW_MODE);
3187 NotifyMode ();
3189 break;
3190 #endif
3191 case F_Text:
3192 SetMode (TEXT_MODE);
3193 break;
3194 case F_Thermal:
3195 SetMode (THERMAL_MODE);
3196 break;
3197 case F_Via:
3198 SetMode (VIA_MODE);
3199 break;
3201 case F_Restore: /* restore the last saved mode */
3202 RestoreMode ();
3203 break;
3205 case F_Save: /* save currently selected mode */
3206 SaveMode ();
3207 break;
3209 notify_crosshair_change (true);
3210 return 0;
3213 AFAIL (mode);
3216 /* --------------------------------------------------------------------------- */
3218 static const char removeselected_syntax[] = "RemoveSelected()";
3220 static const char removeselected_help[] = "Removes any selected objects.";
3222 /* %start-doc actions RemoveSelected
3224 %end-doc */
3226 static int
3227 ActionRemoveSelected (int argc, char **argv, Coord x, Coord y)
3229 if (RemoveSelected ())
3230 SetChangedFlag (true);
3231 return 0;
3234 /* --------------------------------------------------------------------------- */
3236 static const char renumber_syntax[] = "Renumber()\n"
3237 "Renumber(filename)";
3239 static const char renumber_help[] =
3240 "Renumber all elements. The changes will be recorded to filename\n"
3241 "for use in backannotating these changes to the schematic.";
3243 /* %start-doc actions Renumber
3245 %end-doc */
3247 static int
3248 ActionRenumber (int argc, char **argv, Coord x, Coord y)
3250 bool changed = false;
3251 ElementTypePtr *element_list;
3252 ElementTypePtr *locked_element_list;
3253 unsigned int i, j, k, cnt, lock_cnt;
3254 unsigned int tmpi;
3255 size_t sz;
3256 char *tmps;
3257 char *name;
3258 FILE *out;
3259 static char * default_file = NULL;
3260 size_t cnt_list_sz = 100;
3261 struct _cnt_list
3263 char *name;
3264 unsigned int cnt;
3265 } *cnt_list;
3266 char **was, **is, *pin;
3267 unsigned int c_cnt = 0;
3268 int unique, ok;
3269 int free_name = 0;
3271 if (argc < 1)
3274 * We deal with the case where name already exists in this
3275 * function so the GUI doesn't need to deal with it
3277 name = gui->fileselect (_("Save Renumber Annotation File As ..."),
3278 _("Choose a file to record the renumbering to.\n"
3279 "This file may be used to back annotate the\n"
3280 "change to the schematics.\n"),
3281 default_file, ".eco", "eco",
3284 free_name = 1;
3286 else
3287 name = argv[0];
3289 if (default_file)
3291 free (default_file);
3292 default_file = NULL;
3295 if (name && *name)
3297 default_file = strdup (name);
3300 if ((out = fopen (name, "r")))
3302 fclose (out);
3303 if (!gui->confirm_dialog (_("File exists! Ok to overwrite?"), 0))
3305 if (free_name && name)
3306 free (name);
3307 return 0;
3311 if ((out = fopen (name, "w")) == NULL)
3313 Message (_("Could not open %s\n"), name);
3314 if (free_name && name)
3315 free (name);
3316 return 1;
3319 if (free_name && name)
3320 free (name);
3322 fprintf (out, "*COMMENT* PCB Annotation File\n");
3323 fprintf (out, "*FILEVERSION* 20061031\n");
3326 * Make a first pass through all of the elements and sort them out
3327 * by location on the board. While here we also collect a list of
3328 * locked elements.
3330 * We'll actually renumber things in the 2nd pass.
3332 element_list = (ElementType **)calloc (PCB->Data->ElementN, sizeof (ElementTypePtr));
3333 locked_element_list = (ElementType **)calloc (PCB->Data->ElementN, sizeof (ElementTypePtr));
3334 was = (char **)calloc (PCB->Data->ElementN, sizeof (char *));
3335 is = (char **)calloc (PCB->Data->ElementN, sizeof (char *));
3336 if (element_list == NULL || locked_element_list == NULL || was == NULL
3337 || is == NULL)
3339 fprintf (stderr, "calloc() failed in %s\n", __FUNCTION__);
3340 exit (1);
3344 cnt = 0;
3345 lock_cnt = 0;
3346 ELEMENT_LOOP (PCB->Data);
3348 if (TEST_FLAG (LOCKFLAG, element->Name) || TEST_FLAG (LOCKFLAG, element))
3351 * add to the list of locked elements which we won't try to
3352 * renumber and whose reference designators are now reserved.
3354 pcb_fprintf (out,
3355 "*WARN* Element \"%s\" at %$md is locked and will not be renumbered.\n",
3356 UNKNOWN (NAMEONPCB_NAME (element)), element->MarkX, element->MarkY);
3357 locked_element_list[lock_cnt] = element;
3358 lock_cnt++;
3361 else
3363 /* count of devices which will be renumbered */
3364 cnt++;
3366 /* search for correct position in the list */
3367 i = 0;
3368 while (element_list[i] && element->MarkY > element_list[i]->MarkY)
3369 i++;
3372 * We have found the position where we have the first element that
3373 * has the same Y value or a lower Y value. Now move forward if
3374 * needed through the X values
3376 while (element_list[i]
3377 && element->MarkY == element_list[i]->MarkY
3378 && element->MarkX > element_list[i]->MarkX)
3379 i++;
3381 for (j = cnt - 1; j > i; j--)
3383 element_list[j] = element_list[j - 1];
3385 element_list[i] = element;
3388 END_LOOP;
3392 * Now that the elements are sorted by board position, we go through
3393 * and renumber them.
3397 * turn off the flag which requires unique names so it doesn't get
3398 * in our way. When we're done with the renumber we will have unique
3399 * names.
3401 unique = TEST_FLAG (UNIQUENAMEFLAG, PCB);
3402 CLEAR_FLAG (UNIQUENAMEFLAG, PCB);
3404 cnt_list = (struct _cnt_list *)calloc (cnt_list_sz, sizeof (struct _cnt_list));
3405 for (i = 0; i < cnt; i++)
3407 /* If there is no refdes, maybe just spit out a warning */
3408 if (NAMEONPCB_NAME (element_list[i]))
3410 /* figure out the prefix */
3411 tmps = strdup (NAMEONPCB_NAME (element_list[i]));
3412 j = 0;
3413 while (tmps[j] && (tmps[j] < '0' || tmps[j] > '9')
3414 && tmps[j] != '?')
3415 j++;
3416 tmps[j] = '\0';
3418 /* check the counter for this prefix */
3419 for (j = 0;
3420 cnt_list[j].name && (strcmp (cnt_list[j].name, tmps) != 0)
3421 && j < cnt_list_sz; j++);
3423 /* grow the list if needed */
3424 if (j == cnt_list_sz)
3426 cnt_list_sz += 100;
3427 cnt_list = (struct _cnt_list *)realloc (cnt_list, cnt_list_sz);
3428 if (cnt_list == NULL)
3430 fprintf (stderr, "realloc failed() in %s\n", __FUNCTION__);
3431 exit (1);
3433 /* zero out the memory that we added */
3434 for (tmpi = j; tmpi < cnt_list_sz; tmpi++)
3436 cnt_list[tmpi].name = NULL;
3437 cnt_list[tmpi].cnt = 0;
3442 * start a new counter if we don't have a counter for this
3443 * prefix
3445 if (!cnt_list[j].name)
3447 cnt_list[j].name = strdup (tmps);
3448 cnt_list[j].cnt = 0;
3452 * check to see if the new refdes is already used by a
3453 * locked element
3457 ok = 1;
3458 cnt_list[j].cnt++;
3459 free (tmps);
3461 /* space for the prefix plus 1 digit plus the '\0' */
3462 sz = strlen (cnt_list[j].name) + 2;
3464 /* and 1 more per extra digit needed to hold the number */
3465 tmpi = cnt_list[j].cnt;
3466 while (tmpi > 10)
3468 sz++;
3469 tmpi = tmpi / 10;
3471 tmps = (char *)malloc (sz * sizeof (char));
3472 sprintf (tmps, "%s%d", cnt_list[j].name, cnt_list[j].cnt);
3475 * now compare to the list of reserved (by locked
3476 * elements) names
3478 for (k = 0; k < lock_cnt; k++)
3480 if (strcmp
3481 (UNKNOWN (NAMEONPCB_NAME (locked_element_list[k])),
3482 tmps) == 0)
3484 ok = 0;
3485 break;
3490 while (!ok);
3492 if (strcmp (tmps, NAMEONPCB_NAME (element_list[i])) != 0)
3494 fprintf (out, "*RENAME* \"%s\" \"%s\"\n",
3495 NAMEONPCB_NAME (element_list[i]), tmps);
3497 /* add this rename to our table of renames so we can update the netlist */
3498 was[c_cnt] = strdup (NAMEONPCB_NAME (element_list[i]));
3499 is[c_cnt] = strdup (tmps);
3500 c_cnt++;
3502 AddObjectToChangeNameUndoList (ELEMENT_TYPE, NULL, NULL,
3503 element_list[i],
3504 NAMEONPCB_NAME (element_list
3505 [i]));
3507 ChangeObjectName (ELEMENT_TYPE, element_list[i], NULL, NULL,
3508 tmps);
3509 changed = true;
3511 /* we don't free tmps in this case because it is used */
3513 else
3514 free (tmps);
3516 else
3518 pcb_fprintf (out, "*WARN* Element at %$md has no name.\n",
3519 element_list[i]->MarkX, element_list[i]->MarkY);
3524 fclose (out);
3526 /* restore the unique flag setting */
3527 if (unique)
3528 SET_FLAG (UNIQUENAMEFLAG, PCB);
3530 if (changed)
3533 /* update the netlist */
3534 AddNetlistLibToUndoList (&(PCB->NetlistLib));
3536 /* iterate over each net */
3537 for (i = 0; i < PCB->NetlistLib.MenuN; i++)
3540 /* iterate over each pin on the net */
3541 for (j = 0; j < PCB->NetlistLib.Menu[i].EntryN; j++)
3544 /* figure out the pin number part from strings like U3-21 */
3545 tmps = strdup (PCB->NetlistLib.Menu[i].Entry[j].ListEntry);
3546 for (k = 0; tmps[k] && tmps[k] != '-'; k++);
3547 tmps[k] = '\0';
3548 pin = tmps + k + 1;
3550 /* iterate over the list of changed reference designators */
3551 for (k = 0; k < c_cnt; k++)
3554 * if the pin needs to change, change it and quit
3555 * searching in the list.
3557 if (strcmp (tmps, was[k]) == 0)
3559 free (PCB->NetlistLib.Menu[i].Entry[j].ListEntry);
3560 PCB->NetlistLib.Menu[i].Entry[j].ListEntry =
3561 (char *)malloc ((strlen (is[k]) + strlen (pin) +
3562 2) * sizeof (char));
3563 sprintf (PCB->NetlistLib.Menu[i].Entry[j].ListEntry,
3564 "%s-%s", is[k], pin);
3565 k = c_cnt;
3569 free (tmps);
3572 for (k = 0; k < c_cnt; k++)
3574 free (was[k]);
3575 free (is[k]);
3578 NetlistChanged (0);
3579 IncrementUndoSerialNumber ();
3580 SetChangedFlag (true);
3583 free (locked_element_list);
3584 free (element_list);
3585 free (cnt_list);
3586 return 0;
3590 /* --------------------------------------------------------------------------- */
3592 static const char ripup_syntax[] = "RipUp(All|Selected|Element)";
3594 static const char ripup_help[] =
3595 "Ripup auto-routed tracks, or convert an element to parts.";
3597 /* %start-doc actions RipUp
3599 @table @code
3601 @item All
3602 Removes all lines and vias which were created by the autorouter.
3604 @item Selected
3605 Removes all selected lines and vias which were created by the
3606 autorouter.
3608 @item Element
3609 Converts the element under the cursor to parts (vias and lines). Note
3610 that this uses the highest numbered paste buffer.
3612 @end table
3614 %end-doc */
3616 static int
3617 ActionRipUp (int argc, char **argv, Coord x, Coord y)
3619 char *function = ARG (0);
3620 bool changed = false;
3622 if (function)
3624 switch (GetFunctionID (function))
3626 case F_All:
3627 ALLLINE_LOOP (PCB->Data);
3629 if (TEST_FLAG (AUTOFLAG, line) && !TEST_FLAG (LOCKFLAG, line))
3631 RemoveObject (LINE_TYPE, layer, line, line);
3632 changed = true;
3635 ENDALL_LOOP;
3636 ALLARC_LOOP (PCB->Data);
3638 if (TEST_FLAG (AUTOFLAG, arc) && !TEST_FLAG (LOCKFLAG, arc))
3640 RemoveObject (ARC_TYPE, layer, arc, arc);
3641 changed = true;
3644 ENDALL_LOOP;
3645 VIA_LOOP (PCB->Data);
3647 if (TEST_FLAG (AUTOFLAG, via) && !TEST_FLAG (LOCKFLAG, via))
3649 RemoveObject (VIA_TYPE, via, via, via);
3650 changed = true;
3653 END_LOOP;
3655 if (changed)
3657 IncrementUndoSerialNumber ();
3658 SetChangedFlag (true);
3660 break;
3661 case F_Selected:
3662 VISIBLELINE_LOOP (PCB->Data);
3664 if (TEST_FLAGS (AUTOFLAG | SELECTEDFLAG, line)
3665 && !TEST_FLAG (LOCKFLAG, line))
3667 RemoveObject (LINE_TYPE, layer, line, line);
3668 changed = true;
3671 ENDALL_LOOP;
3672 if (PCB->ViaOn)
3673 VIA_LOOP (PCB->Data);
3675 if (TEST_FLAGS (AUTOFLAG | SELECTEDFLAG, via)
3676 && !TEST_FLAG (LOCKFLAG, via))
3678 RemoveObject (VIA_TYPE, via, via, via);
3679 changed = true;
3682 END_LOOP;
3683 if (changed)
3685 IncrementUndoSerialNumber ();
3686 SetChangedFlag (true);
3688 break;
3689 case F_Element:
3691 void *ptr1, *ptr2, *ptr3;
3693 if (SearchScreen (Crosshair.X, Crosshair.Y, ELEMENT_TYPE,
3694 &ptr1, &ptr2, &ptr3) != NO_TYPE)
3696 Note.Buffer = Settings.BufferNumber;
3697 SetBufferNumber (MAX_BUFFER - 1);
3698 ClearBuffer (PASTEBUFFER);
3699 CopyObjectToBuffer (PASTEBUFFER->Data, PCB->Data,
3700 ELEMENT_TYPE, ptr1, ptr2, ptr3);
3701 SmashBufferElement (PASTEBUFFER);
3702 PASTEBUFFER->X = 0;
3703 PASTEBUFFER->Y = 0;
3704 SaveUndoSerialNumber ();
3705 EraseObject (ELEMENT_TYPE, ptr1, ptr1);
3706 MoveObjectToRemoveUndoList (ELEMENT_TYPE, ptr1, ptr2, ptr3);
3707 RestoreUndoSerialNumber ();
3708 CopyPastebufferToLayout (0, 0);
3709 SetBufferNumber (Note.Buffer);
3710 SetChangedFlag (true);
3713 break;
3716 return 0;
3719 /* --------------------------------------------------------------------------- */
3721 static const char addrats_syntax[] = "AddRats(AllRats|SelectedRats|Close)";
3723 static const char addrats_help[] = "Add one or more rat lines to the board.";
3725 /* %start-doc actions AddRats
3727 @table @code
3729 @item AllRats
3730 Create rat lines for all loaded nets that aren't already connected on
3731 with copper.
3733 @item SelectedRats
3734 Similarly, but only add rat lines for nets connected to selected pins
3735 and pads.
3737 @item Close
3738 Selects the shortest unselected rat on the board.
3740 @end table
3742 %end-doc */
3744 static int
3745 ActionAddRats (int argc, char **argv, Coord x, Coord y)
3747 char *function = ARG (0);
3748 RatTypePtr shorty;
3749 float len, small;
3751 if (function)
3753 if (Settings.RatWarn)
3754 ClearWarnings ();
3755 switch (GetFunctionID (function))
3757 case F_AllRats:
3758 if (AddAllRats (false, NULL))
3759 SetChangedFlag (true);
3760 break;
3761 case F_SelectedRats:
3762 case F_Selected:
3763 if (AddAllRats (true, NULL))
3764 SetChangedFlag (true);
3765 break;
3766 case F_Close:
3767 small = SQUARE (MAX_COORD);
3768 shorty = NULL;
3769 RAT_LOOP (PCB->Data);
3771 if (TEST_FLAG (SELECTEDFLAG, line))
3772 continue;
3773 len = SQUARE (line->Point1.X - line->Point2.X) +
3774 SQUARE (line->Point1.Y - line->Point2.Y);
3775 if (len < small)
3777 small = len;
3778 shorty = line;
3781 END_LOOP;
3782 if (shorty)
3784 AddObjectToFlagUndoList (RATLINE_TYPE, shorty, shorty, shorty);
3785 SET_FLAG (SELECTEDFLAG, shorty);
3786 DrawRat (shorty);
3787 Draw ();
3788 CenterDisplay ((shorty->Point2.X + shorty->Point1.X) / 2,
3789 (shorty->Point2.Y + shorty->Point1.Y) / 2);
3791 break;
3794 return 0;
3797 /* --------------------------------------------------------------------------- */
3799 static const char delete_syntax[] =
3800 "Delete(Object|Selected)\n"
3801 "Delete(AllRats|SelectedRats)";
3803 static const char delete_help[] = "Delete stuff.";
3805 /* %start-doc actions Delete
3807 %end-doc */
3809 static int
3810 ActionDelete (int argc, char **argv, Coord x, Coord y)
3812 char *function = ARG (0);
3813 int id = GetFunctionID (function);
3815 Note.X = Crosshair.X;
3816 Note.Y = Crosshair.Y;
3818 if (id == -1) /* no arg */
3820 if (RemoveSelected() == false)
3821 id = F_Object;
3824 switch (id)
3826 case F_Object:
3827 SaveMode();
3828 SetMode(REMOVE_MODE);
3829 NotifyMode();
3830 RestoreMode();
3831 break;
3832 case F_Selected:
3833 RemoveSelected();
3834 break;
3835 case F_AllRats:
3836 if (DeleteRats (false))
3837 SetChangedFlag (true);
3838 break;
3839 case F_SelectedRats:
3840 if (DeleteRats (true))
3841 SetChangedFlag (true);
3842 break;
3845 return 0;
3848 /* --------------------------------------------------------------------------- */
3850 static const char deleterats_syntax[] =
3851 "DeleteRats(AllRats|Selected|SelectedRats)";
3853 static const char deleterats_help[] = "Delete rat lines.";
3855 /* %start-doc actions DeleteRats
3857 %end-doc */
3859 static int
3860 ActionDeleteRats (int argc, char **argv, Coord x, Coord y)
3862 char *function = ARG (0);
3863 if (function)
3865 if (Settings.RatWarn)
3866 ClearWarnings ();
3867 switch (GetFunctionID (function))
3869 case F_AllRats:
3870 if (DeleteRats (false))
3871 SetChangedFlag (true);
3872 break;
3873 case F_SelectedRats:
3874 case F_Selected:
3875 if (DeleteRats (true))
3876 SetChangedFlag (true);
3877 break;
3880 return 0;
3883 /* --------------------------------------------------------------------------- */
3885 static const char autoplace_syntax[] = "AutoPlaceSelected()";
3887 static const char autoplace_help[] = "Auto-place selected components.";
3889 /* %start-doc actions AutoPlaceSelected
3891 Attempts to re-arrange the selected components such that the nets
3892 connecting them are minimized. Note that you cannot undo this.
3894 %end-doc */
3896 static int
3897 ActionAutoPlaceSelected (int argc, char **argv, Coord x, Coord y)
3899 hid_action("Busy");
3900 if (gui->confirm_dialog (_("Auto-placement can NOT be undone.\n"
3901 "Do you want to continue anyway?\n"), 0))
3903 if (AutoPlaceSelected ())
3904 SetChangedFlag (true);
3906 return 0;
3909 /* --------------------------------------------------------------------------- */
3911 static const char autoroute_syntax[] = "AutoRoute(AllRats|SelectedRats)";
3913 static const char autoroute_help[] = "Auto-route some or all rat lines.";
3915 /* %start-doc actions AutoRoute
3917 @table @code
3919 @item AllRats
3920 Attempt to autoroute all rats.
3922 @item SelectedRats
3923 Attempt to autoroute the selected rats.
3925 @end table
3927 Before autorouting, it's important to set up a few things. First,
3928 make sure any layers you aren't using are disabled, else the
3929 autorouter may use them. Next, make sure the current line and via
3930 styles are set accordingly. Last, make sure "new lines clear
3931 polygons" is set, in case you eventually want to add a copper pour.
3933 Autorouting takes a while. During this time, the program may not be
3934 responsive.
3936 %end-doc */
3938 static int
3939 ActionAutoRoute (int argc, char **argv, Coord x, Coord y)
3941 char *function = ARG (0);
3942 hid_action("Busy");
3943 if (function) /* one parameter */
3945 switch (GetFunctionID (function))
3947 case F_AllRats:
3948 if (AutoRoute (false))
3949 SetChangedFlag (true);
3950 break;
3951 case F_SelectedRats:
3952 case F_Selected:
3953 if (AutoRoute (true))
3954 SetChangedFlag (true);
3955 break;
3958 return 0;
3961 /* --------------------------------------------------------------------------- */
3963 static const char markcrosshair_syntax[] =
3964 "MarkCrosshair()\n"
3965 "MarkCrosshair(Center)";
3967 static const char markcrosshair_help[] = "Set/Reset the Crosshair mark.";
3969 /* %start-doc actions MarkCrosshair
3971 The ``mark'' is a small X-shaped target on the display which is
3972 treated like a second origin (the normal origin is the upper let
3973 corner of the board). The GUI will display a second set of
3974 coordinates for this mark, which tells you how far you are from it.
3976 If no argument is given, the mark is toggled - disabled if it was
3977 enabled, or enabled at the current cursor position of disabled. If
3978 the @code{Center} argument is given, the mark is moved to the current
3979 cursor location.
3981 %end-doc */
3983 static int
3984 ActionMarkCrosshair (int argc, char **argv, Coord x, Coord y)
3986 char *function = ARG (0);
3987 if (!function || !*function)
3989 if (Marked.status)
3991 notify_mark_change (false);
3992 Marked.status = false;
3993 notify_mark_change (true);
3995 else
3997 notify_mark_change (false);
3998 Marked.status = false;
3999 Marked.status = true;
4000 Marked.X = Crosshair.X;
4001 Marked.Y = Crosshair.Y;
4002 notify_mark_change (true);
4005 else if (GetFunctionID (function) == F_Center)
4007 notify_mark_change (false);
4008 Marked.status = true;
4009 Marked.X = Crosshair.X;
4010 Marked.Y = Crosshair.Y;
4011 notify_mark_change (true);
4013 return 0;
4016 /* --------------------------------------------------------------------------- */
4018 static const char changesize_syntax[] =
4019 "ChangeSize(Object, delta)\n"
4020 "ChangeSize(SelectedObjects|Selected, delta)\n"
4021 "ChangeSize(SelectedLines|SelectedPins|SelectedVias, delta)\n"
4022 "ChangeSize(SelectedPads|SelectedTexts|SelectedNames, delta)\n"
4023 "ChangeSize(SelectedElements, delta)";
4025 static const char changesize_help[] = "Changes the size of objects.";
4027 /* %start-doc actions ChangeSize
4029 For lines and arcs, this changes the width. For pins and vias, this
4030 changes the overall diameter of the copper annulus. For pads, this
4031 changes the width and, indirectly, the length. For texts and names,
4032 this changes the scaling factor. For elements, this changes the width
4033 of the silk layer lines and arcs for this element.
4035 %end-doc */
4037 static int
4038 ActionChangeSize (int argc, char **argv, Coord x, Coord y)
4040 char *function = ARG (0);
4041 char *delta = ARG (1);
4042 char *units = ARG (2);
4043 bool absolute; /* indicates if absolute size is given */
4044 Coord value;
4046 if (function && delta)
4048 value = GetValue (delta, units, &absolute);
4049 switch (GetFunctionID (function))
4051 case F_Object:
4053 int type;
4054 void *ptr1, *ptr2, *ptr3;
4056 if ((type =
4057 SearchScreen (Crosshair.X, Crosshair.Y, CHANGESIZE_TYPES,
4058 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4059 if (TEST_FLAG (LOCKFLAG, (PinTypePtr) ptr2))
4060 Message (_("Sorry, the object is locked\n"));
4061 if (ChangeObjectSize (type, ptr1, ptr2, ptr3, value, absolute))
4062 SetChangedFlag (true);
4063 break;
4066 case F_SelectedVias:
4067 if (ChangeSelectedSize (VIA_TYPE, value, absolute))
4068 SetChangedFlag (true);
4069 break;
4071 case F_SelectedPins:
4072 if (ChangeSelectedSize (PIN_TYPE, value, absolute))
4073 SetChangedFlag (true);
4074 break;
4076 case F_SelectedPads:
4077 if (ChangeSelectedSize (PAD_TYPE, value, absolute))
4078 SetChangedFlag (true);
4079 break;
4081 case F_SelectedArcs:
4082 if (ChangeSelectedSize (ARC_TYPE, value, absolute))
4083 SetChangedFlag (true);
4084 break;
4086 case F_SelectedLines:
4087 if (ChangeSelectedSize (LINE_TYPE, value, absolute))
4088 SetChangedFlag (true);
4089 break;
4091 case F_SelectedTexts:
4092 if (ChangeSelectedSize (TEXT_TYPE, value, absolute))
4093 SetChangedFlag (true);
4094 break;
4096 case F_SelectedNames:
4097 if (ChangeSelectedSize (ELEMENTNAME_TYPE, value, absolute))
4098 SetChangedFlag (true);
4099 break;
4101 case F_SelectedElements:
4102 if (ChangeSelectedSize (ELEMENT_TYPE, value, absolute))
4103 SetChangedFlag (true);
4104 break;
4106 case F_Selected:
4107 case F_SelectedObjects:
4108 if (ChangeSelectedSize (CHANGESIZE_TYPES, value, absolute))
4109 SetChangedFlag (true);
4110 break;
4113 return 0;
4116 /* --------------------------------------------------------------------------- */
4118 static const char changedrillsize_syntax[] =
4119 "ChangeDrillSize(Object, delta)\n"
4120 "ChangeDrillSize(SelectedPins|SelectedVias|Selected|SelectedObjects, delta)";
4122 static const char changedrillsize_help[] =
4123 "Changes the drilling hole size of objects.";
4125 /* %start-doc actions ChangeDrillSize
4127 %end-doc */
4129 static int
4130 ActionChange2ndSize (int argc, char **argv, Coord x, Coord y)
4132 char *function = ARG (0);
4133 char *delta = ARG (1);
4134 char *units = ARG (2);
4135 bool absolute;
4136 Coord value;
4138 if (function && delta)
4140 value = GetValue (delta, units, &absolute);
4141 switch (GetFunctionID (function))
4143 case F_Object:
4145 int type;
4146 void *ptr1, *ptr2, *ptr3;
4148 gui->get_coords (_("Select an Object"), &x, &y);
4149 if ((type =
4150 SearchScreen (x, y, CHANGE2NDSIZE_TYPES,
4151 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4152 if (ChangeObject2ndSize
4153 (type, ptr1, ptr2, ptr3, value, absolute, true))
4154 SetChangedFlag (true);
4155 break;
4158 case F_SelectedVias:
4159 if (ChangeSelected2ndSize (VIA_TYPE, value, absolute))
4160 SetChangedFlag (true);
4161 break;
4163 case F_SelectedPins:
4164 if (ChangeSelected2ndSize (PIN_TYPE, value, absolute))
4165 SetChangedFlag (true);
4166 break;
4167 case F_Selected:
4168 case F_SelectedObjects:
4169 if (ChangeSelected2ndSize (PIN_TYPES, value, absolute))
4170 SetChangedFlag (true);
4171 break;
4174 return 0;
4177 /* --------------------------------------------------------------------------- */
4179 static const char changeclearsize_syntax[] =
4180 "ChangeClearSize(Object, delta)\n"
4181 "ChangeClearSize(SelectedPins|SelectedPads|SelectedVias, delta)\n"
4182 "ChangeClearSize(SelectedLines|SelectedArcs, delta\n"
4183 "ChangeClearSize(Selected|SelectedObjects, delta)";
4185 static const char changeclearsize_help[] =
4186 "Changes the clearance size of objects.";
4188 /* %start-doc actions ChangeClearSize
4190 If the solder mask is currently showing, this action changes the
4191 solder mask clearance. If the mask is not showing, this action
4192 changes the polygon clearance.
4194 %end-doc */
4196 static int
4197 ActionChangeClearSize (int argc, char **argv, Coord x, Coord y)
4199 char *function = ARG (0);
4200 char *delta = ARG (1);
4201 char *units = ARG (2);
4202 bool absolute;
4203 Coord value;
4205 if (function && delta)
4207 value = 2 * GetValue (delta, units, &absolute);
4208 switch (GetFunctionID (function))
4210 case F_Object:
4212 int type;
4213 void *ptr1, *ptr2, *ptr3;
4215 gui->get_coords (_("Select an Object"), &x, &y);
4216 if ((type =
4217 SearchScreen (x, y,
4218 CHANGECLEARSIZE_TYPES, &ptr1, &ptr2,
4219 &ptr3)) != NO_TYPE)
4220 if (ChangeObjectClearSize (type, ptr1, ptr2, ptr3, value, absolute))
4221 SetChangedFlag (true);
4222 break;
4224 case F_SelectedVias:
4225 if (ChangeSelectedClearSize (VIA_TYPE, value, absolute))
4226 SetChangedFlag (true);
4227 break;
4228 case F_SelectedPads:
4229 if (ChangeSelectedClearSize (PAD_TYPE, value, absolute))
4230 SetChangedFlag (true);
4231 break;
4232 case F_SelectedPins:
4233 if (ChangeSelectedClearSize (PIN_TYPE, value, absolute))
4234 SetChangedFlag (true);
4235 break;
4236 case F_SelectedLines:
4237 if (ChangeSelectedClearSize (LINE_TYPE, value, absolute))
4238 SetChangedFlag (true);
4239 break;
4240 case F_SelectedArcs:
4241 if (ChangeSelectedClearSize (ARC_TYPE, value, absolute))
4242 SetChangedFlag (true);
4243 break;
4244 case F_Selected:
4245 case F_SelectedObjects:
4246 if (ChangeSelectedClearSize (CHANGECLEARSIZE_TYPES, value, absolute))
4247 SetChangedFlag (true);
4248 break;
4251 return 0;
4254 /* --------------------------------------------------------------------------- */
4256 static const char minmaskgap_syntax[] =
4257 "MinMaskGap(delta)\n"
4258 "MinMaskGap(Selected, delta)";
4260 static const char minmaskgap_help[] =
4261 "Ensures the mask is a minimum distance from pins and pads.";
4263 /* %start-doc actions MinMaskGap
4265 Checks all specified pins and/or pads, and increases the mask if
4266 needed to ensure a minimum distance between the pin or pad edge and
4267 the mask edge.
4269 %end-doc */
4271 static int
4272 ActionMinMaskGap (int argc, char **argv, Coord x, Coord y)
4274 char *function = ARG (0);
4275 char *delta = ARG (1);
4276 char *units = ARG (2);
4277 bool absolute;
4278 Coord value;
4279 int flags;
4281 if (!function)
4282 return 1;
4283 if (strcasecmp (function, "Selected") == 0)
4284 flags = SELECTEDFLAG;
4285 else
4287 units = delta;
4288 delta = function;
4289 flags = 0;
4291 value = 2 * GetValue (delta, units, &absolute);
4293 SaveUndoSerialNumber ();
4294 ELEMENT_LOOP (PCB->Data);
4296 PIN_LOOP (element);
4298 if (!TEST_FLAGS (flags, pin))
4299 continue;
4300 if (pin->Mask < pin->Thickness + value)
4302 ChangeObjectMaskSize (PIN_TYPE, element, pin, 0,
4303 pin->Thickness + value, 1);
4304 RestoreUndoSerialNumber ();
4307 END_LOOP;
4308 PAD_LOOP (element);
4310 if (!TEST_FLAGS (flags, pad))
4311 continue;
4312 if (pad->Mask < pad->Thickness + value)
4314 ChangeObjectMaskSize (PAD_TYPE, element, pad, 0,
4315 pad->Thickness + value, 1);
4316 RestoreUndoSerialNumber ();
4319 END_LOOP;
4321 END_LOOP;
4322 VIA_LOOP (PCB->Data);
4324 if (!TEST_FLAGS (flags, via))
4325 continue;
4326 if (via->Mask && via->Mask < via->Thickness + value)
4328 ChangeObjectMaskSize (VIA_TYPE, via, 0, 0, via->Thickness + value, 1);
4329 RestoreUndoSerialNumber ();
4332 END_LOOP;
4333 RestoreUndoSerialNumber ();
4334 IncrementUndoSerialNumber ();
4335 return 0;
4338 /* --------------------------------------------------------------------------- */
4340 static const char mincleargap_syntax[] =
4341 "MinClearGap(delta)\n"
4342 "MinClearGap(Selected, delta)";
4344 static const char mincleargap_help[] =
4345 "Ensures that polygons are a minimum distance from objects.";
4347 /* %start-doc actions MinClearGap
4349 Checks all specified objects, and increases the polygon clearance if
4350 needed to ensure a minimum distance between their edges and the
4351 polygon edges.
4353 %end-doc */
4355 static int
4356 ActionMinClearGap (int argc, char **argv, Coord x, Coord y)
4358 char *function = ARG (0);
4359 char *delta = ARG (1);
4360 char *units = ARG (2);
4361 bool absolute;
4362 Coord value;
4363 int flags;
4365 if (!function)
4366 return 1;
4367 if (strcasecmp (function, "Selected") == 0)
4368 flags = SELECTEDFLAG;
4369 else
4371 units = delta;
4372 delta = function;
4373 flags = 0;
4375 value = 2 * GetValue (delta, units, &absolute);
4377 SaveUndoSerialNumber ();
4378 ELEMENT_LOOP (PCB->Data);
4380 PIN_LOOP (element);
4382 if (!TEST_FLAGS (flags, pin))
4383 continue;
4384 if (pin->Clearance < value)
4386 ChangeObjectClearSize (PIN_TYPE, element, pin, 0,
4387 value, 1);
4388 RestoreUndoSerialNumber ();
4391 END_LOOP;
4392 PAD_LOOP (element);
4394 if (!TEST_FLAGS (flags, pad))
4395 continue;
4396 if (pad->Clearance < value)
4398 ChangeObjectClearSize (PAD_TYPE, element, pad, 0,
4399 value, 1);
4400 RestoreUndoSerialNumber ();
4403 END_LOOP;
4405 END_LOOP;
4406 VIA_LOOP (PCB->Data);
4408 if (!TEST_FLAGS (flags, via))
4409 continue;
4410 if (via->Clearance < value)
4412 ChangeObjectClearSize (VIA_TYPE, via, 0, 0, value, 1);
4413 RestoreUndoSerialNumber ();
4416 END_LOOP;
4417 ALLLINE_LOOP (PCB->Data);
4419 if (!TEST_FLAGS (flags, line))
4420 continue;
4421 if (line->Clearance < value)
4423 ChangeObjectClearSize (LINE_TYPE, layer, line, 0, value, 1);
4424 RestoreUndoSerialNumber ();
4427 ENDALL_LOOP;
4428 ALLARC_LOOP (PCB->Data);
4430 if (!TEST_FLAGS (flags, arc))
4431 continue;
4432 if (arc->Clearance < value)
4434 ChangeObjectClearSize (ARC_TYPE, layer, arc, 0, value, 1);
4435 RestoreUndoSerialNumber ();
4438 ENDALL_LOOP;
4439 RestoreUndoSerialNumber ();
4440 IncrementUndoSerialNumber ();
4441 return 0;
4444 /* --------------------------------------------------------------------------- */
4446 static const char changepinname_syntax[] =
4447 "ChangePinName(ElementName,PinNumber,PinName)";
4449 static const char changepinname_help[] =
4450 "Sets the name of a specific pin on a specific element.";
4452 /* %start-doc actions ChangePinName
4454 This can be especially useful for annotating pin names from a
4455 schematic to the layout without requiring knowledge of the pcb file
4456 format.
4458 @example
4459 ChangePinName(U3, 7, VCC)
4460 @end example
4462 %end-doc */
4464 static int
4465 ActionChangePinName (int argc, char **argv, Coord x, Coord y)
4467 int changed = 0;
4468 char *refdes, *pinnum, *pinname;
4470 if (argc != 3)
4472 AFAIL (changepinname);
4475 refdes = argv[0];
4476 pinnum = argv[1];
4477 pinname = argv[2];
4479 ELEMENT_LOOP (PCB->Data);
4481 if (NSTRCMP (refdes, NAMEONPCB_NAME (element)) == 0)
4483 PIN_LOOP (element);
4485 if (NSTRCMP (pinnum, pin->Number) == 0)
4487 AddObjectToChangeNameUndoList (PIN_TYPE, NULL, NULL,
4488 pin, pin->Name);
4490 * Note: we can't free() pin->Name first because
4491 * it is used in the undo list
4493 pin->Name = strdup (pinname);
4494 SetChangedFlag (true);
4495 changed = 1;
4498 END_LOOP;
4500 PAD_LOOP (element);
4502 if (NSTRCMP (pinnum, pad->Number) == 0)
4504 AddObjectToChangeNameUndoList (PAD_TYPE, NULL, NULL,
4505 pad, pad->Name);
4507 * Note: we can't free() pad->Name first because
4508 * it is used in the undo list
4510 pad->Name = strdup (pinname);
4511 SetChangedFlag (true);
4512 changed = 1;
4515 END_LOOP;
4518 END_LOOP;
4520 * done with our action so increment the undo # if we actually
4521 * changed anything
4523 if (changed)
4525 if (defer_updates)
4526 defer_needs_update = 1;
4527 else
4529 IncrementUndoSerialNumber ();
4530 gui->invalidate_all ();
4534 return 0;
4537 /* --------------------------------------------------------------------------- */
4539 static const char changename_syntax[] =
4540 "ChangeName(Object)\n"
4541 "ChangeName(Layout|Layer)";
4543 static const char changename_help[] = "Sets the name of objects.";
4545 /* %start-doc actions ChangeName
4547 @table @code
4549 @item Object
4550 Changes the name of the element under the cursor.
4552 @item Layout
4553 Changes the name of the layout. This is printed on the fab drawings.
4555 @item Layer
4556 Changes the name of the currently active layer.
4558 @end table
4560 %end-doc */
4563 ActionChangeName (int argc, char **argv, Coord x, Coord y)
4565 char *function = ARG (0);
4566 char *name;
4568 if (function)
4570 switch (GetFunctionID (function))
4572 /* change the name of an object */
4573 case F_Object:
4575 int type;
4576 void *ptr1, *ptr2, *ptr3;
4578 gui->get_coords (_("Select an Object"), &x, &y);
4579 if ((type =
4580 SearchScreen (x, y, CHANGENAME_TYPES,
4581 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4583 SaveUndoSerialNumber ();
4584 if (QueryInputAndChangeObjectName (type, ptr1, ptr2, ptr3))
4586 SetChangedFlag (true);
4587 if (type == ELEMENT_TYPE)
4589 RubberbandTypePtr ptr;
4590 int i;
4592 RestoreUndoSerialNumber ();
4593 Crosshair.AttachedObject.RubberbandN = 0;
4594 LookupRatLines (type, ptr1, ptr2, ptr3);
4595 ptr = Crosshair.AttachedObject.Rubberband;
4596 for (i = 0; i < Crosshair.AttachedObject.RubberbandN;
4597 i++, ptr++)
4599 if (PCB->RatOn)
4600 EraseRat ((RatTypePtr) ptr->Line);
4601 MoveObjectToRemoveUndoList (RATLINE_TYPE,
4602 ptr->Line, ptr->Line,
4603 ptr->Line);
4605 IncrementUndoSerialNumber ();
4606 Draw ();
4610 break;
4613 /* change the layout's name */
4614 case F_Layout:
4615 name =
4616 gui->prompt_for (_("Enter the layout name:"), EMPTY (PCB->Name));
4617 /* NB: ChangeLayoutName takes ownership of the passed memory */
4618 if (name && ChangeLayoutName (name))
4619 SetChangedFlag (true);
4620 break;
4622 /* change the name of the active layer */
4623 case F_Layer:
4624 name = gui->prompt_for (_("Enter the layer name:"),
4625 EMPTY (CURRENT->Name));
4626 /* NB: ChangeLayerName takes ownership of the passed memory */
4627 if (name && ChangeLayerName (CURRENT, name))
4628 SetChangedFlag (true);
4629 break;
4632 return 0;
4636 /* --------------------------------------------------------------------------- */
4638 static const char morphpolygon_syntax[] = "MorphPolygon(Object|Selected)";
4640 static const char morphpolygon_help[] =
4641 "Converts dead polygon islands into separate polygons.";
4643 /* %start-doc actions MorphPolygon
4645 If a polygon is divided into unconnected "islands", you can use
4646 this command to convert the otherwise disappeared islands into
4647 separate polygons. Be sure the cursor is over a portion of the
4648 polygon that remains visible. Very small islands that may flake
4649 off are automatically deleted.
4651 %end-doc */
4653 static int
4654 ActionMorphPolygon (int argc, char **argv, Coord x, Coord y)
4656 char *function = ARG (0);
4657 if (function)
4659 switch (GetFunctionID (function))
4661 case F_Object:
4663 int type;
4664 void *ptr1, *ptr2, *ptr3;
4666 gui->get_coords (_("Select an Object"), &x, &y);
4667 if ((type = SearchScreen (x, y, POLYGON_TYPE,
4668 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4670 MorphPolygon ((LayerType *) ptr1, (PolygonType *) ptr3);
4671 Draw ();
4672 IncrementUndoSerialNumber ();
4674 break;
4676 case F_Selected:
4677 case F_SelectedObjects:
4678 ALLPOLYGON_LOOP (PCB->Data);
4680 if (TEST_FLAG (SELECTEDFLAG, polygon))
4681 MorphPolygon (layer, polygon);
4683 ENDALL_LOOP;
4684 Draw ();
4685 IncrementUndoSerialNumber ();
4686 break;
4689 return 0;
4692 /* --------------------------------------------------------------------------- */
4694 static const char togglehidename_syntax[] =
4695 "ToggleHideName(Object|SelectedElements)";
4697 static const char togglehidename_help[] =
4698 "Toggles the visibility of element names.";
4700 /* %start-doc actions ToggleHideName
4702 If names are hidden you won't see them on the screen and they will not
4703 appear on the silk layer when you print the layout.
4705 %end-doc */
4707 static int
4708 ActionToggleHideName (int argc, char **argv, Coord x, Coord y)
4710 char *function = ARG (0);
4711 if (function && PCB->ElementOn)
4713 switch (GetFunctionID (function))
4715 case F_Object:
4717 int type;
4718 void *ptr1, *ptr2, *ptr3;
4720 gui->get_coords (_("Select an Object"), &x, &y);
4721 if ((type = SearchScreen (x, y, ELEMENT_TYPE,
4722 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4724 AddObjectToFlagUndoList (type, ptr1, ptr2, ptr3);
4725 EraseElementName ((ElementTypePtr) ptr2);
4726 TOGGLE_FLAG (HIDENAMEFLAG, (ElementTypePtr) ptr2);
4727 DrawElementName ((ElementTypePtr) ptr2);
4728 Draw ();
4729 IncrementUndoSerialNumber ();
4731 break;
4733 case F_SelectedElements:
4734 case F_Selected:
4736 bool changed = false;
4737 ELEMENT_LOOP (PCB->Data);
4739 if ((TEST_FLAG (SELECTEDFLAG, element) ||
4740 TEST_FLAG (SELECTEDFLAG,
4741 &NAMEONPCB_TEXT (element)))
4742 && (FRONT (element) || PCB->InvisibleObjectsOn))
4744 AddObjectToFlagUndoList (ELEMENT_TYPE, element,
4745 element, element);
4746 EraseElementName (element);
4747 TOGGLE_FLAG (HIDENAMEFLAG, element);
4748 DrawElementName (element);
4749 changed = true;
4752 END_LOOP;
4753 if (changed)
4755 Draw ();
4756 IncrementUndoSerialNumber ();
4761 return 0;
4764 /* --------------------------------------------------------------------------- */
4766 static const char changejoin_syntax[] =
4767 "ChangeJoin(ToggleObject|SelectedLines|SelectedArcs|Selected)";
4769 static const char changejoin_help[] =
4770 "Changes the join (clearance through polygons) of objects.";
4772 /* %start-doc actions ChangeJoin
4774 The join flag determines whether a line or arc, drawn to intersect a
4775 polygon, electrically connects to the polygon or not. When joined,
4776 the line/arc is simply drawn over the polygon, making an electrical
4777 connection. When not joined, a gap is drawn between the line and the
4778 polygon, insulating them from each other.
4780 %end-doc */
4782 static int
4783 ActionChangeJoin (int argc, char **argv, Coord x, Coord y)
4785 char *function = ARG (0);
4786 if (function)
4788 switch (GetFunctionID (function))
4790 case F_ToggleObject:
4791 case F_Object:
4793 int type;
4794 void *ptr1, *ptr2, *ptr3;
4796 gui->get_coords (_("Select an Object"), &x, &y);
4797 if ((type =
4798 SearchScreen (x, y, CHANGEJOIN_TYPES,
4799 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4800 if (ChangeObjectJoin (type, ptr1, ptr2, ptr3))
4801 SetChangedFlag (true);
4802 break;
4805 case F_SelectedLines:
4806 if (ChangeSelectedJoin (LINE_TYPE))
4807 SetChangedFlag (true);
4808 break;
4810 case F_SelectedArcs:
4811 if (ChangeSelectedJoin (ARC_TYPE))
4812 SetChangedFlag (true);
4813 break;
4815 case F_Selected:
4816 case F_SelectedObjects:
4817 if (ChangeSelectedJoin (CHANGEJOIN_TYPES))
4818 SetChangedFlag (true);
4819 break;
4822 return 0;
4825 /* --------------------------------------------------------------------------- */
4827 static const char changesquare_syntax[] =
4828 "ChangeSquare(ToggleObject)\n"
4829 "ChangeSquare(SelectedElements|SelectedPins)\n"
4830 "ChangeSquare(Selected|SelectedObjects)";
4832 static const char changesquare_help[] =
4833 "Changes the square flag of pins and pads.";
4835 /* %start-doc actions ChangeSquare
4837 Note that @code{Pins} means both pins and pads.
4839 @pinshapes
4841 %end-doc */
4843 static int
4844 ActionChangeSquare (int argc, char **argv, Coord x, Coord y)
4846 char *function = ARG (0);
4847 if (function)
4849 switch (GetFunctionID (function))
4851 case F_ToggleObject:
4852 case F_Object:
4854 int type;
4855 void *ptr1, *ptr2, *ptr3;
4857 gui->get_coords (_("Select an Object"), &x, &y);
4858 if ((type =
4859 SearchScreen (x, y, CHANGESQUARE_TYPES,
4860 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4861 if (ChangeObjectSquare (type, ptr1, ptr2, ptr3))
4862 SetChangedFlag (true);
4863 break;
4866 case F_SelectedElements:
4867 if (ChangeSelectedSquare (ELEMENT_TYPE))
4868 SetChangedFlag (true);
4869 break;
4871 case F_SelectedPins:
4872 if (ChangeSelectedSquare (PIN_TYPE | PAD_TYPE))
4873 SetChangedFlag (true);
4874 break;
4876 case F_Selected:
4877 case F_SelectedObjects:
4878 if (ChangeSelectedSquare (PIN_TYPE | PAD_TYPE))
4879 SetChangedFlag (true);
4880 break;
4883 return 0;
4886 /* --------------------------------------------------------------------------- */
4888 static const char setsquare_syntax[] =
4889 "SetSquare(ToggleObject|SelectedElements|SelectedPins)";
4891 static const char setsquare_help[] = "sets the square-flag of objects.";
4893 /* %start-doc actions SetSquare
4895 Note that @code{Pins} means pins and pads.
4897 @pinshapes
4899 %end-doc */
4901 static int
4902 ActionSetSquare (int argc, char **argv, Coord x, Coord y)
4904 char *function = ARG (0);
4905 if (function && *function)
4907 switch (GetFunctionID (function))
4909 case F_ToggleObject:
4910 case F_Object:
4912 int type;
4913 void *ptr1, *ptr2, *ptr3;
4915 gui->get_coords (_("Select an Object"), &x, &y);
4916 if ((type =
4917 SearchScreen (x, y, CHANGESQUARE_TYPES,
4918 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4919 if (SetObjectSquare (type, ptr1, ptr2, ptr3))
4920 SetChangedFlag (true);
4921 break;
4924 case F_SelectedElements:
4925 if (SetSelectedSquare (ELEMENT_TYPE))
4926 SetChangedFlag (true);
4927 break;
4929 case F_SelectedPins:
4930 if (SetSelectedSquare (PIN_TYPE | PAD_TYPE))
4931 SetChangedFlag (true);
4932 break;
4934 case F_Selected:
4935 case F_SelectedObjects:
4936 if (SetSelectedSquare (PIN_TYPE | PAD_TYPE))
4937 SetChangedFlag (true);
4938 break;
4941 return 0;
4944 /* --------------------------------------------------------------------------- */
4946 static const char clearsquare_syntax[] =
4947 "ClearSquare(ToggleObject|SelectedElements|SelectedPins)";
4949 static const char clearsquare_help[] =
4950 "Clears the square-flag of pins and pads.";
4952 /* %start-doc actions ClearSquare
4954 Note that @code{Pins} means pins and pads.
4956 @pinshapes
4958 %end-doc */
4960 static int
4961 ActionClearSquare (int argc, char **argv, Coord x, Coord y)
4963 char *function = ARG (0);
4964 if (function && *function)
4966 switch (GetFunctionID (function))
4968 case F_ToggleObject:
4969 case F_Object:
4971 int type;
4972 void *ptr1, *ptr2, *ptr3;
4974 gui->get_coords (_("Select an Object"), &x, &y);
4975 if ((type =
4976 SearchScreen (x, y, CHANGESQUARE_TYPES,
4977 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4978 if (ClrObjectSquare (type, ptr1, ptr2, ptr3))
4979 SetChangedFlag (true);
4980 break;
4983 case F_SelectedElements:
4984 if (ClrSelectedSquare (ELEMENT_TYPE))
4985 SetChangedFlag (true);
4986 break;
4988 case F_SelectedPins:
4989 if (ClrSelectedSquare (PIN_TYPE | PAD_TYPE))
4990 SetChangedFlag (true);
4991 break;
4993 case F_Selected:
4994 case F_SelectedObjects:
4995 if (ClrSelectedSquare (PIN_TYPE | PAD_TYPE))
4996 SetChangedFlag (true);
4997 break;
5000 return 0;
5003 /* --------------------------------------------------------------------------- */
5005 static const char changeoctagon_syntax[] =
5006 "ChangeOctagon(Object|ToggleObject|SelectedObjects|Selected)\n"
5007 "ChangeOctagon(SelectedElements|SelectedPins|SelectedVias)";
5009 static const char changeoctagon_help[] =
5010 "Changes the octagon-flag of pins and vias.";
5012 /* %start-doc actions ChangeOctagon
5014 @pinshapes
5016 %end-doc */
5018 static int
5019 ActionChangeOctagon (int argc, char **argv, Coord x, Coord y)
5021 char *function = ARG (0);
5022 if (function)
5024 switch (GetFunctionID (function))
5026 case F_ToggleObject:
5027 case F_Object:
5029 int type;
5030 void *ptr1, *ptr2, *ptr3;
5032 gui->get_coords (_("Select an Object"), &x, &y);
5033 if ((type =
5034 SearchScreen (x, y, CHANGEOCTAGON_TYPES,
5035 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5036 if (ChangeObjectOctagon (type, ptr1, ptr2, ptr3))
5037 SetChangedFlag (true);
5038 break;
5041 case F_SelectedElements:
5042 if (ChangeSelectedOctagon (ELEMENT_TYPE))
5043 SetChangedFlag (true);
5044 break;
5046 case F_SelectedPins:
5047 if (ChangeSelectedOctagon (PIN_TYPE))
5048 SetChangedFlag (true);
5049 break;
5051 case F_SelectedVias:
5052 if (ChangeSelectedOctagon (VIA_TYPE))
5053 SetChangedFlag (true);
5054 break;
5056 case F_Selected:
5057 case F_SelectedObjects:
5058 if (ChangeSelectedOctagon (PIN_TYPES))
5059 SetChangedFlag (true);
5060 break;
5063 return 0;
5066 /* --------------------------------------------------------------------------- */
5068 static const char setoctagon_syntax[] =
5069 "SetOctagon(Object|ToggleObject|SelectedElements|Selected)";
5071 static const char setoctagon_help[] = "Sets the octagon-flag of objects.";
5073 /* %start-doc actions SetOctagon
5075 @pinshapes
5077 %end-doc */
5079 static int
5080 ActionSetOctagon (int argc, char **argv, Coord x, Coord y)
5082 char *function = ARG (0);
5083 if (function)
5085 switch (GetFunctionID (function))
5087 case F_ToggleObject:
5088 case F_Object:
5090 int type;
5091 void *ptr1, *ptr2, *ptr3;
5093 gui->get_coords (_("Select an Object"), &x, &y);
5094 if ((type =
5095 SearchScreen (x, y, CHANGEOCTAGON_TYPES,
5096 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5097 if (SetObjectOctagon (type, ptr1, ptr2, ptr3))
5098 SetChangedFlag (true);
5099 break;
5102 case F_SelectedElements:
5103 if (SetSelectedOctagon (ELEMENT_TYPE))
5104 SetChangedFlag (true);
5105 break;
5107 case F_SelectedPins:
5108 if (SetSelectedOctagon (PIN_TYPE))
5109 SetChangedFlag (true);
5110 break;
5112 case F_SelectedVias:
5113 if (SetSelectedOctagon (VIA_TYPE))
5114 SetChangedFlag (true);
5115 break;
5117 case F_Selected:
5118 case F_SelectedObjects:
5119 if (SetSelectedOctagon (PIN_TYPES))
5120 SetChangedFlag (true);
5121 break;
5124 return 0;
5127 /* --------------------------------------------------------------------------- */
5129 static const char clearoctagon_syntax[] =
5130 "ClearOctagon(ToggleObject|Object|SelectedObjects|Selected)\n"
5131 "ClearOctagon(SelectedElements|SelectedPins|SelectedVias)";
5133 static const char clearoctagon_help[] =
5134 "Clears the octagon-flag of pins and vias.";
5136 /* %start-doc actions ClearOctagon
5138 @pinshapes
5140 %end-doc */
5142 static int
5143 ActionClearOctagon (int argc, char **argv, Coord x, Coord y)
5145 char *function = ARG (0);
5146 if (function)
5148 switch (GetFunctionID (function))
5150 case F_ToggleObject:
5151 case F_Object:
5153 int type;
5154 void *ptr1, *ptr2, *ptr3;
5156 gui->get_coords (_("Select an Object"), &x, &y);
5157 if ((type =
5158 SearchScreen (Crosshair.X, Crosshair.Y, CHANGEOCTAGON_TYPES,
5159 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5160 if (ClrObjectOctagon (type, ptr1, ptr2, ptr3))
5161 SetChangedFlag (true);
5162 break;
5165 case F_SelectedElements:
5166 if (ClrSelectedOctagon (ELEMENT_TYPE))
5167 SetChangedFlag (true);
5168 break;
5170 case F_SelectedPins:
5171 if (ClrSelectedOctagon (PIN_TYPE))
5172 SetChangedFlag (true);
5173 break;
5175 case F_SelectedVias:
5176 if (ClrSelectedOctagon (VIA_TYPE))
5177 SetChangedFlag (true);
5178 break;
5180 case F_Selected:
5181 case F_SelectedObjects:
5182 if (ClrSelectedOctagon (PIN_TYPES))
5183 SetChangedFlag (true);
5184 break;
5187 return 0;
5190 /* --------------------------------------------------------------------------- */
5192 static const char changehold_syntax[] =
5193 "ChangeHole(ToggleObject|Object|SelectedVias|Selected)";
5195 static const char changehold_help[] = "Changes the hole flag of objects.";
5197 /* %start-doc actions ChangeHole
5199 The "hole flag" of a via determines whether the via is a
5200 plated-through hole (not set), or an unplated hole (set).
5202 %end-doc */
5204 static int
5205 ActionChangeHole (int argc, char **argv, Coord x, Coord y)
5207 char *function = ARG (0);
5208 if (function)
5210 switch (GetFunctionID (function))
5212 case F_ToggleObject:
5213 case F_Object:
5215 int type;
5216 void *ptr1, *ptr2, *ptr3;
5218 gui->get_coords (_("Select an Object"), &x, &y);
5219 if ((type = SearchScreen (x, y, VIA_TYPE,
5220 &ptr1, &ptr2, &ptr3)) != NO_TYPE
5221 && ChangeHole ((PinTypePtr) ptr3))
5222 IncrementUndoSerialNumber ();
5223 break;
5226 case F_SelectedVias:
5227 case F_Selected:
5228 if (ChangeSelectedHole ())
5229 SetChangedFlag (true);
5230 break;
5233 return 0;
5236 /* --------------------------------------------------------------------------- */
5238 static const char changepaste_syntax[] =
5239 "ChangePaste(ToggleObject|Object|SelectedPads|Selected)";
5241 static const char changepaste_help[] = "Changes the no paste flag of objects.";
5243 /* %start-doc actions ChangePaste
5245 The "no paste flag" of a pad determines whether the solderpaste
5246 stencil will have an opening for the pad (no set) or if there wil be
5247 no solderpaste on the pad (set). This is used for things such as
5248 fiducial pads.
5250 %end-doc */
5252 static int
5253 ActionChangePaste (int argc, char **argv, Coord x, Coord y)
5255 char *function = ARG (0);
5256 if (function)
5258 switch (GetFunctionID (function))
5260 case F_ToggleObject:
5261 case F_Object:
5263 int type;
5264 void *ptr1, *ptr2, *ptr3;
5266 gui->get_coords (_("Select an Object"), &x, &y);
5267 if ((type = SearchScreen (x, y, PAD_TYPE,
5268 &ptr1, &ptr2, &ptr3)) != NO_TYPE
5269 && ChangePaste ((PadTypePtr) ptr3))
5270 IncrementUndoSerialNumber ();
5271 break;
5274 case F_SelectedPads:
5275 case F_Selected:
5276 if (ChangeSelectedPaste ())
5277 SetChangedFlag (true);
5278 break;
5281 return 0;
5284 /* --------------------------------------------------------------------------- */
5286 static const char select_syntax[] =
5287 "Select(Object|ToggleObject)\n"
5288 "Select(All|Block|Connection)\n"
5289 "Select(ElementByName|ObjectByName|PadByName|PinByName)\n"
5290 "Select(ElementByName|ObjectByName|PadByName|PinByName, Name)\n"
5291 "Select(TextByName|ViaByName|NetByName)\n"
5292 "Select(TextByName|ViaByName|NetByName, Name)\n"
5293 "Select(Convert)";
5295 static const char select_help[] = "Toggles or sets the selection.";
5297 /* %start-doc actions Select
5299 @table @code
5301 @item ElementByName
5302 @item ObjectByName
5303 @item PadByName
5304 @item PinByName
5305 @item TextByName
5306 @item ViaByName
5307 @item NetByName
5309 These all rely on having a regular expression parser built into
5310 @code{pcb}. If the name is not specified then the user is prompted
5311 for a pattern, and all objects that match the pattern and are of the
5312 type specified are selected.
5314 @item Object
5315 @item ToggleObject
5316 Selects the object under the cursor.
5318 @item Block
5319 Selects all objects in a rectangle indicated by the cursor.
5321 @item All
5322 Selects all objects on the board.
5324 @item Connection
5325 Selects all connections with the ``found'' flag set.
5327 @item Convert
5328 Converts the selected objects to an element. This uses the highest
5329 numbered paste buffer.
5331 @end table
5333 %end-doc */
5335 static int
5336 ActionSelect (int argc, char **argv, Coord x, Coord y)
5338 char *function = ARG (0);
5339 if (function)
5341 switch (GetFunctionID (function))
5343 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5344 int type;
5345 /* select objects by their names */
5346 case F_ElementByName:
5347 type = ELEMENT_TYPE;
5348 goto commonByName;
5349 case F_ObjectByName:
5350 type = ALL_TYPES;
5351 goto commonByName;
5352 case F_PadByName:
5353 type = PAD_TYPE;
5354 goto commonByName;
5355 case F_PinByName:
5356 type = PIN_TYPE;
5357 goto commonByName;
5358 case F_TextByName:
5359 type = TEXT_TYPE;
5360 goto commonByName;
5361 case F_ViaByName:
5362 type = VIA_TYPE;
5363 goto commonByName;
5364 case F_NetByName:
5365 type = NET_TYPE;
5366 goto commonByName;
5368 commonByName:
5370 char *pattern = ARG (1);
5372 if (pattern
5373 || (pattern =
5374 gui->prompt_for (_("Enter pattern:"), "")) != NULL)
5376 if (SelectObjectByName (type, pattern, true))
5377 SetChangedFlag (true);
5378 if (ARG (1) == NULL)
5379 free (pattern);
5381 break;
5383 #endif /* defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP) */
5385 /* select a single object */
5386 case F_ToggleObject:
5387 case F_Object:
5388 if (SelectObject ())
5389 SetChangedFlag (true);
5390 break;
5392 /* all objects in block */
5393 case F_Block:
5395 BoxType box;
5397 box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
5398 Crosshair.AttachedBox.Point2.X);
5399 box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
5400 Crosshair.AttachedBox.Point2.Y);
5401 box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
5402 Crosshair.AttachedBox.Point2.X);
5403 box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
5404 Crosshair.AttachedBox.Point2.Y);
5405 notify_crosshair_change (false);
5406 NotifyBlock ();
5407 if (Crosshair.AttachedBox.State == STATE_THIRD &&
5408 SelectBlock (&box, true))
5410 SetChangedFlag (true);
5411 Crosshair.AttachedBox.State = STATE_FIRST;
5413 notify_crosshair_change (true);
5414 break;
5417 /* select all visible objects */
5418 case F_All:
5420 BoxType box;
5422 box.X1 = -MAX_COORD;
5423 box.Y1 = -MAX_COORD;
5424 box.X2 = MAX_COORD;
5425 box.Y2 = MAX_COORD;
5426 if (SelectBlock (&box, true))
5427 SetChangedFlag (true);
5428 break;
5431 /* all found connections */
5432 case F_Connection:
5433 if (SelectConnection (true))
5435 Draw ();
5436 IncrementUndoSerialNumber ();
5437 SetChangedFlag (true);
5439 break;
5441 case F_Convert:
5443 Coord x, y;
5444 Note.Buffer = Settings.BufferNumber;
5445 SetBufferNumber (MAX_BUFFER - 1);
5446 ClearBuffer (PASTEBUFFER);
5447 gui->get_coords (_("Select the Element's Mark Location"), &x, &y);
5448 x = GridFit (x, PCB->Grid, PCB->GridOffsetX);
5449 y = GridFit (y, PCB->Grid, PCB->GridOffsetY);
5450 AddSelectedToBuffer (PASTEBUFFER, x, y, true);
5451 SaveUndoSerialNumber ();
5452 RemoveSelected ();
5453 ConvertBufferToElement (PASTEBUFFER);
5454 RestoreUndoSerialNumber ();
5455 CopyPastebufferToLayout (x, y);
5456 SetBufferNumber (Note.Buffer);
5458 break;
5460 default:
5461 AFAIL (select);
5462 break;
5465 return 0;
5468 /* FLAG(have_regex,FlagHaveRegex,0) */
5470 FlagHaveRegex (int parm)
5472 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5473 return 1;
5474 #else
5475 return 0;
5476 #endif
5479 /* --------------------------------------------------------------------------- */
5481 static const char unselect_syntax[] =
5482 "Unselect(All|Block|Connection)\n"
5483 "Unselect(ElementByName|ObjectByName|PadByName|PinByName)\n"
5484 "Unselect(ElementByName|ObjectByName|PadByName|PinByName, Name)\n"
5485 "Unselect(TextByName|ViaByName)\n"
5486 "Unselect(TextByName|ViaByName, Name)\n";
5488 static const char unselect_help[] =
5489 "Unselects the object at the pointer location or the specified objects.";
5491 /* %start-doc actions Unselect
5493 @table @code
5495 @item All
5496 Unselect all objects.
5498 @item Block
5499 Unselect all objects in a rectangle given by the cursor.
5501 @item Connection
5502 Unselect all connections with the ``found'' flag set.
5504 @item ElementByName
5505 @item ObjectByName
5506 @item PadByName
5507 @item PinByName
5508 @item TextByName
5509 @item ViaByName
5511 These all rely on having a regular expression parser built into
5512 @code{pcb}. If the name is not specified then the user is prompted
5513 for a pattern, and all objects that match the pattern and are of the
5514 type specified are unselected.
5517 @end table
5519 %end-doc */
5521 static int
5522 ActionUnselect (int argc, char **argv, Coord x, Coord y)
5524 char *function = ARG (0);
5525 if (function)
5527 switch (GetFunctionID (function))
5529 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5530 int type;
5531 /* select objects by their names */
5532 case F_ElementByName:
5533 type = ELEMENT_TYPE;
5534 goto commonByName;
5535 case F_ObjectByName:
5536 type = ALL_TYPES;
5537 goto commonByName;
5538 case F_PadByName:
5539 type = PAD_TYPE;
5540 goto commonByName;
5541 case F_PinByName:
5542 type = PIN_TYPE;
5543 goto commonByName;
5544 case F_TextByName:
5545 type = TEXT_TYPE;
5546 goto commonByName;
5547 case F_ViaByName:
5548 type = VIA_TYPE;
5549 goto commonByName;
5550 case F_NetByName:
5551 type = NET_TYPE;
5552 goto commonByName;
5554 commonByName:
5556 char *pattern = ARG (1);
5558 if (pattern
5559 || (pattern =
5560 gui->prompt_for (_("Enter pattern:"), "")) != NULL)
5562 if (SelectObjectByName (type, pattern, false))
5563 SetChangedFlag (true);
5564 if (ARG (1) == NULL)
5565 free (pattern);
5567 break;
5569 #endif /* defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP) */
5571 /* all objects in block */
5572 case F_Block:
5574 BoxType box;
5576 box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
5577 Crosshair.AttachedBox.Point2.X);
5578 box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
5579 Crosshair.AttachedBox.Point2.Y);
5580 box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
5581 Crosshair.AttachedBox.Point2.X);
5582 box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
5583 Crosshair.AttachedBox.Point2.Y);
5584 notify_crosshair_change (false);
5585 NotifyBlock ();
5586 if (Crosshair.AttachedBox.State == STATE_THIRD &&
5587 SelectBlock (&box, false))
5589 SetChangedFlag (true);
5590 Crosshair.AttachedBox.State = STATE_FIRST;
5592 notify_crosshair_change (true);
5593 break;
5596 /* unselect all visible objects */
5597 case F_All:
5599 BoxType box;
5601 box.X1 = -MAX_COORD;
5602 box.Y1 = -MAX_COORD;
5603 box.X2 = MAX_COORD;
5604 box.Y2 = MAX_COORD;
5605 if (SelectBlock (&box, false))
5606 SetChangedFlag (true);
5607 break;
5610 /* all found connections */
5611 case F_Connection:
5612 if (SelectConnection (false))
5614 Draw ();
5615 IncrementUndoSerialNumber ();
5616 SetChangedFlag (true);
5618 break;
5620 default:
5621 AFAIL (unselect);
5622 break;
5626 return 0;
5629 /* --------------------------------------------------------------------------- */
5631 static const char saveto_syntax[] =
5632 "SaveTo(Layout|LayoutAs,filename)\n"
5633 "SaveTo(AllConnections|AllUnusedPins|ElementConnections,filename)\n"
5634 "SaveTo(PasteBuffer,filename)";
5636 static const char saveto_help[] = "Saves data to a file.";
5638 /* %start-doc actions SaveTo
5640 @table @code
5642 @item Layout
5643 Saves the current layout.
5645 @item LayoutAs
5646 Saves the current layout, and remembers the filename used.
5648 @item AllConnections
5649 Save all connections to a file.
5651 @item AllUnusedPins
5652 List all unused pins to a file.
5654 @item ElementConnections
5655 Save connections to the element at the cursor to a file.
5657 @item PasteBuffer
5658 Save the content of the active Buffer to a file. This is the graphical way to create a footprint.
5660 @end table
5662 %end-doc */
5664 static int
5665 ActionSaveTo (int argc, char **argv, Coord x, Coord y)
5667 char *function;
5668 char *name;
5670 function = argv[0];
5671 name = argv[1];
5673 if (strcasecmp (function, "Layout") == 0)
5675 if (SavePCB (PCB->Filename) == 0)
5676 SetChangedFlag (false);
5677 return 0;
5680 if (argc != 2)
5681 AFAIL (saveto);
5683 if (strcasecmp (function, "LayoutAs") == 0)
5685 if (SavePCB (name) == 0)
5687 SetChangedFlag (false);
5688 free (PCB->Filename);
5689 PCB->Filename = strdup (name);
5690 if (gui->notify_filename_changed != NULL)
5691 gui->notify_filename_changed ();
5693 return 0;
5696 if (strcasecmp (function, "AllConnections") == 0)
5698 FILE *fp;
5699 bool result;
5700 if ((fp = CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
5702 LookupConnectionsToAllElements (fp);
5703 fclose (fp);
5704 SetChangedFlag (true);
5706 return 0;
5709 if (strcasecmp (function, "AllUnusedPins") == 0)
5711 FILE *fp;
5712 bool result;
5713 if ((fp = CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
5715 LookupUnusedPins (fp);
5716 fclose (fp);
5717 SetChangedFlag (true);
5719 return 0;
5722 if (strcasecmp (function, "ElementConnections") == 0)
5724 ElementTypePtr element;
5725 void *ptrtmp;
5726 FILE *fp;
5727 bool result;
5729 if ((SearchScreen (Crosshair.X, Crosshair.Y, ELEMENT_TYPE,
5730 &ptrtmp, &ptrtmp, &ptrtmp)) != NO_TYPE)
5732 element = (ElementTypePtr) ptrtmp;
5733 if ((fp =
5734 CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
5736 LookupElementConnections (element, fp);
5737 fclose (fp);
5738 SetChangedFlag (true);
5741 return 0;
5744 if (strcasecmp (function, "PasteBuffer") == 0)
5746 return SaveBufferElements (name);
5749 AFAIL (saveto);
5752 /* --------------------------------------------------------------------------- */
5754 static const char savesettings_syntax[] =
5755 "SaveSettings()\n"
5756 "SaveSettings(local)";
5758 static const char savesettings_help[] = "Saves settings.";
5760 /* %start-doc actions SaveSettings
5762 If you pass no arguments, the settings are stored in
5763 @code{$HOME/.pcb/settings}. If you pass the word @code{local} they're
5764 saved in @code{./pcb.settings}.
5766 %end-doc */
5768 static int
5769 ActionSaveSettings (int argc, char **argv, Coord x, Coord y)
5771 int locally = argc > 0 ? (strncasecmp (argv[0], "local", 5) == 0) : 0;
5772 hid_save_settings (locally);
5773 return 0;
5776 /* --------------------------------------------------------------------------- */
5778 static const char loadfrom_syntax[] =
5779 "LoadFrom(Layout|LayoutToBuffer|ElementToBuffer|Netlist|Revert,filename)";
5781 static const char loadfrom_help[] = "Load layout data from a file.";
5783 /* %start-doc actions LoadFrom
5785 This action assumes you know what the filename is. The various GUIs
5786 should have a similar @code{Load} action where the filename is
5787 optional, and will provide their own file selection mechanism to let
5788 you choose the file name.
5790 @table @code
5792 @item Layout
5793 Loads an entire PCB layout, replacing the current one.
5795 @item LayoutToBuffer
5796 Loads an entire PCB layout to the paste buffer.
5798 @item ElementToBuffer
5799 Loads the given element file into the paste buffer. Element files
5800 contain only a single @code{Element} definition, such as the
5801 ``newlib'' library uses.
5803 @item Netlist
5804 Loads a new netlist, replacing any current netlist.
5806 @item Revert
5807 Re-loads the current layout from its disk file, reverting any changes
5808 you may have made.
5810 @end table
5812 %end-doc */
5814 static int
5815 ActionLoadFrom (int argc, char **argv, Coord x, Coord y)
5817 char *function;
5818 char *name;
5820 if (argc < 2)
5821 AFAIL (loadfrom);
5823 function = argv[0];
5824 name = argv[1];
5826 if (strcasecmp (function, "ElementToBuffer") == 0)
5828 notify_crosshair_change (false);
5829 if (LoadElementToBuffer (PASTEBUFFER, name, true))
5830 SetMode (PASTEBUFFER_MODE);
5831 notify_crosshair_change (true);
5834 else if (strcasecmp (function, "LayoutToBuffer") == 0)
5836 notify_crosshair_change (false);
5837 if (LoadLayoutToBuffer (PASTEBUFFER, name))
5838 SetMode (PASTEBUFFER_MODE);
5839 notify_crosshair_change (true);
5842 else if (strcasecmp (function, "Layout") == 0)
5844 if (!PCB->Changed ||
5845 gui->confirm_dialog (_("OK to override layout data?"), 0))
5846 LoadPCB (name);
5849 else if (strcasecmp (function, "Netlist") == 0)
5851 if (PCB->Netlistname)
5852 free (PCB->Netlistname);
5853 PCB->Netlistname = StripWhiteSpaceAndDup (name);
5854 FreeLibraryMemory (&PCB->NetlistLib);
5855 if (!ImportNetlist (PCB->Netlistname))
5856 NetlistChanged (1);
5858 else if (strcasecmp (function, "Revert") == 0 && PCB->Filename
5859 && (!PCB->Changed
5860 || gui->confirm_dialog (_("OK to override changes?"), 0)))
5862 RevertPCB ();
5865 return 0;
5868 /* --------------------------------------------------------------------------- */
5870 static const char new_syntax[] = "New([name])";
5872 static const char new_help[] = "Starts a new layout.";
5874 /* %start-doc actions New
5876 If a name is not given, one is prompted for.
5878 %end-doc */
5880 static int
5881 ActionNew (int argc, char **argv, Coord x, Coord y)
5883 char *name = ARG (0);
5885 if (!PCB->Changed || gui->confirm_dialog (_("OK to clear layout data?"), 0))
5887 if (name)
5888 name = strdup (name);
5889 else
5890 name = gui->prompt_for (_("Enter the layout name:"), "");
5892 if (!name)
5893 return 1;
5895 notify_crosshair_change (false);
5896 /* do emergency saving
5897 * clear the old struct and allocate memory for the new one
5899 if (PCB->Changed && Settings.SaveInTMP)
5900 SaveInTMP ();
5901 RemovePCB (PCB);
5902 PCB = NULL;
5903 PCB = CreateNewPCB (true);
5904 PCB->Data->LayerN = DEF_LAYER;
5905 CreateNewPCBPost (PCB, 1);
5907 /* setup the new name and reset some values to default */
5908 free (PCB->Name);
5909 PCB->Name = name;
5911 ResetStackAndVisibility ();
5912 SetCrosshairRange (0, 0, PCB->MaxWidth, PCB->MaxHeight);
5913 CenterDisplay (PCB->MaxWidth / 2, PCB->MaxHeight / 2);
5914 Redraw ();
5916 hid_action ("PCBChanged");
5917 notify_crosshair_change (true);
5918 return 0;
5920 return 1;
5923 /* ---------------------------------------------------------------------------
5924 * no operation, just for testing purposes
5925 * syntax: Bell(volume)
5927 void
5928 ActionBell (char *volume)
5930 gui->beep ();
5933 /* --------------------------------------------------------------------------- */
5935 static const char pastebuffer_syntax[] =
5936 "PasteBuffer(AddSelected|Clear|1..MAX_BUFFER)\n"
5937 "PasteBuffer(Rotate, 1..3)\n"
5938 "PasteBuffer(Convert|Save|Restore|Mirror)\n"
5939 "PasteBuffer(ToLayout, X, Y, units)";
5941 static const char pastebuffer_help[] =
5942 "Various operations on the paste buffer.";
5944 /* %start-doc actions PasteBuffer
5946 There are a number of paste buffers; the actual limit is a
5947 compile-time constant @code{MAX_BUFFER} in @file{globalconst.h}. It
5948 is currently @code{5}. One of these is the ``current'' paste buffer,
5949 often referred to as ``the'' paste buffer.
5951 @table @code
5953 @item AddSelected
5954 Copies the selected objects to the current paste buffer.
5956 @item Clear
5957 Remove all objects from the current paste buffer.
5959 @item Convert
5960 Convert the current paste buffer to an element. Vias are converted to
5961 pins, lines are converted to pads.
5963 @item Restore
5964 Convert any elements in the paste buffer back to vias and lines.
5966 @item Mirror
5967 Flip all objects in the paste buffer vertically (up/down flip). To mirror
5968 horizontally, combine this with rotations.
5970 @item Rotate
5971 Rotates the current buffer. The number to pass is 1..3, where 1 means
5972 90 degrees counter clockwise, 2 means 180 degrees, and 3 means 90
5973 degrees clockwise (270 CCW).
5975 @item Save
5976 Saves any elements in the current buffer to the indicated file.
5978 @item ToLayout
5979 Pastes any elements in the current buffer to the indicated X, Y
5980 coordinates in the layout. The @code{X} and @code{Y} are treated like
5981 @code{delta} is for many other objects. For each, if it's prefixed by
5982 @code{+} or @code{-}, then that amount is relative to the last
5983 location. Otherwise, it's absolute. Units can be
5984 @code{mil} or @code{mm}; if unspecified, units are PCB's internal
5985 units, currently 1/100 mil.
5988 @item 1..MAX_BUFFER
5989 Selects the given buffer to be the current paste buffer.
5991 @end table
5993 %end-doc */
5995 static int
5996 ActionPasteBuffer (int argc, char **argv, Coord x, Coord y)
5998 char *function = argc ? argv[0] : (char *)"";
5999 char *sbufnum = argc > 1 ? argv[1] : (char *)"";
6000 char *name;
6001 static char *default_file = NULL;
6002 int free_name = 0;
6004 notify_crosshair_change (false);
6005 if (function)
6007 switch (GetFunctionID (function))
6009 /* clear contents of paste buffer */
6010 case F_Clear:
6011 ClearBuffer (PASTEBUFFER);
6012 break;
6014 /* copies objects to paste buffer */
6015 case F_AddSelected:
6016 AddSelectedToBuffer (PASTEBUFFER, 0, 0, false);
6017 break;
6019 /* converts buffer contents into an element */
6020 case F_Convert:
6021 ConvertBufferToElement (PASTEBUFFER);
6022 break;
6024 /* break up element for editing */
6025 case F_Restore:
6026 SmashBufferElement (PASTEBUFFER);
6027 break;
6029 /* Mirror buffer */
6030 case F_Mirror:
6031 MirrorBuffer (PASTEBUFFER);
6032 break;
6034 case F_Rotate:
6035 if (sbufnum)
6037 RotateBuffer (PASTEBUFFER, (BYTE) atoi (sbufnum));
6038 SetCrosshairRangeToBuffer ();
6040 break;
6042 case F_Save:
6043 if (PASTEBUFFER->Data->ElementN == 0)
6045 Message (_("Buffer has no elements!\n"));
6046 break;
6048 free_name = 0;
6049 if (argc <= 1)
6051 name = gui->fileselect (_("Save Paste Buffer As ..."),
6052 _("Choose a file to save the contents of the\n"
6053 "paste buffer to.\n"),
6054 default_file, ".fp", "footprint",
6057 if (default_file)
6059 free (default_file);
6060 default_file = NULL;
6062 if ( name && *name)
6064 default_file = strdup (name);
6066 free_name = 1;
6069 else
6070 name = argv[1];
6073 FILE *exist;
6075 if ((exist = fopen (name, "r")))
6077 fclose (exist);
6078 if (gui->
6079 confirm_dialog (_("File exists! Ok to overwrite?"), 0))
6080 SaveBufferElements (name);
6082 else
6083 SaveBufferElements (name);
6085 if (free_name && name)
6086 free (name);
6088 break;
6090 case F_ToLayout:
6092 static Coord oldx = 0, oldy = 0;
6093 Coord x, y;
6094 bool absolute;
6096 if (argc == 1)
6098 x = y = 0;
6100 else if (argc == 3 || argc == 4)
6102 x = GetValue (ARG (1), ARG (3), &absolute);
6103 if (!absolute)
6104 x += oldx;
6105 y = GetValue (ARG (2), ARG (3), &absolute);
6106 if (!absolute)
6107 y += oldy;
6109 else
6111 notify_crosshair_change (true);
6112 AFAIL (pastebuffer);
6115 oldx = x;
6116 oldy = y;
6117 if (CopyPastebufferToLayout (x, y))
6118 SetChangedFlag (true);
6120 break;
6122 /* set number */
6123 default:
6125 int number = atoi (function);
6127 /* correct number */
6128 if (number)
6129 SetBufferNumber (number - 1);
6134 notify_crosshair_change (true);
6135 return 0;
6138 /* --------------------------------------------------------------------------- */
6140 static const char undo_syntax[] = "Undo()\n"
6141 "Undo(ClearList)";
6143 static const char undo_help[] = "Undo recent changes.";
6145 /* %start-doc actions Undo
6147 The unlimited undo feature of @code{Pcb} allows you to recover from
6148 most operations that materially affect you work. Calling
6149 @code{Undo()} without any parameter recovers from the last (non-undo)
6150 operation. @code{ClearList} is used to release the allocated
6151 memory. @code{ClearList} is called whenever a new layout is started or
6152 loaded. See also @code{Redo} and @code{Atomic}.
6154 Note that undo groups operations by serial number; changes with the
6155 same serial number will be undone (or redone) as a group. See
6156 @code{Atomic}.
6158 %end-doc */
6160 static int
6161 ActionUndo (int argc, char **argv, Coord x, Coord y)
6163 char *function = ARG (0);
6164 if (!function || !*function)
6166 /* don't allow undo in the middle of an operation */
6167 if (Settings.Mode != POLYGONHOLE_MODE &&
6168 Crosshair.AttachedObject.State != STATE_FIRST)
6169 return 1;
6170 if (Crosshair.AttachedBox.State != STATE_FIRST
6171 && Settings.Mode != ARC_MODE)
6172 return 1;
6173 /* undo the last operation */
6175 notify_crosshair_change (false);
6176 if ((Settings.Mode == POLYGON_MODE ||
6177 Settings.Mode == POLYGONHOLE_MODE) &&
6178 Crosshair.AttachedPolygon.PointN)
6180 GoToPreviousPoint ();
6181 notify_crosshair_change (true);
6182 return 0;
6184 /* move anchor point if undoing during line creation */
6185 if (Settings.Mode == LINE_MODE)
6187 if (Crosshair.AttachedLine.State == STATE_SECOND)
6189 if (TEST_FLAG (AUTODRCFLAG, PCB))
6190 Undo (true); /* undo the connection find */
6191 Crosshair.AttachedLine.State = STATE_FIRST;
6192 SetLocalRef (0, 0, false);
6193 notify_crosshair_change (true);
6194 return 0;
6196 if (Crosshair.AttachedLine.State == STATE_THIRD)
6198 int type;
6199 void *ptr1, *ptr3, *ptrtmp;
6200 LineTypePtr ptr2;
6201 /* this search is guaranteed to succeed */
6202 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6203 &ptrtmp, &ptr3,
6204 Crosshair.AttachedLine.Point1.X,
6205 Crosshair.AttachedLine.Point1.Y, 0);
6206 ptr2 = (LineTypePtr) ptrtmp;
6208 /* save both ends of line */
6209 Crosshair.AttachedLine.Point2.X = ptr2->Point1.X;
6210 Crosshair.AttachedLine.Point2.Y = ptr2->Point1.Y;
6211 if ((type = Undo (true)))
6212 SetChangedFlag (true);
6213 /* check that the undo was of the right type */
6214 if ((type & UNDO_CREATE) == 0)
6216 /* wrong undo type, restore anchor points */
6217 Crosshair.AttachedLine.Point2.X =
6218 Crosshair.AttachedLine.Point1.X;
6219 Crosshair.AttachedLine.Point2.Y =
6220 Crosshair.AttachedLine.Point1.Y;
6221 notify_crosshair_change (true);
6222 return 0;
6224 /* move to new anchor */
6225 Crosshair.AttachedLine.Point1.X =
6226 Crosshair.AttachedLine.Point2.X;
6227 Crosshair.AttachedLine.Point1.Y =
6228 Crosshair.AttachedLine.Point2.Y;
6229 /* check if an intermediate point was removed */
6230 if (type & UNDO_REMOVE)
6232 /* this search should find the restored line */
6233 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6234 &ptrtmp,
6235 &ptr3,
6236 Crosshair.AttachedLine.Point2.X,
6237 Crosshair.AttachedLine.Point2.Y, 0);
6238 ptr2 = (LineTypePtr) ptrtmp;
6239 if (TEST_FLAG (AUTODRCFLAG, PCB))
6241 /* undo loses FOUNDFLAG */
6242 SET_FLAG(FOUNDFLAG, ptr2);
6243 DrawLine (CURRENT, ptr2);
6245 Crosshair.AttachedLine.Point1.X =
6246 Crosshair.AttachedLine.Point2.X = ptr2->Point2.X;
6247 Crosshair.AttachedLine.Point1.Y =
6248 Crosshair.AttachedLine.Point2.Y = ptr2->Point2.Y;
6250 FitCrosshairIntoGrid (Crosshair.X, Crosshair.Y);
6251 AdjustAttachedObjects ();
6252 if (--addedLines == 0)
6254 Crosshair.AttachedLine.State = STATE_SECOND;
6255 lastLayer = CURRENT;
6257 else
6259 /* this search is guaranteed to succeed too */
6260 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6261 &ptrtmp,
6262 &ptr3,
6263 Crosshair.AttachedLine.Point1.X,
6264 Crosshair.AttachedLine.Point1.Y, 0);
6265 ptr2 = (LineTypePtr) ptrtmp;
6266 lastLayer = (LayerTypePtr) ptr1;
6268 notify_crosshair_change (true);
6269 return 0;
6272 if (Settings.Mode == ARC_MODE)
6274 if (Crosshair.AttachedBox.State == STATE_SECOND)
6276 Crosshair.AttachedBox.State = STATE_FIRST;
6277 notify_crosshair_change (true);
6278 return 0;
6280 if (Crosshair.AttachedBox.State == STATE_THIRD)
6282 void *ptr1, *ptr2, *ptr3;
6283 BoxTypePtr bx;
6284 /* guaranteed to succeed */
6285 SearchObjectByLocation (ARC_TYPE, &ptr1, &ptr2, &ptr3,
6286 Crosshair.AttachedBox.Point1.X,
6287 Crosshair.AttachedBox.Point1.Y, 0);
6288 bx = GetArcEnds ((ArcTypePtr) ptr2);
6289 Crosshair.AttachedBox.Point1.X =
6290 Crosshair.AttachedBox.Point2.X = bx->X1;
6291 Crosshair.AttachedBox.Point1.Y =
6292 Crosshair.AttachedBox.Point2.Y = bx->Y1;
6293 AdjustAttachedObjects ();
6294 if (--addedLines == 0)
6295 Crosshair.AttachedBox.State = STATE_SECOND;
6298 /* undo the last destructive operation */
6299 if (Undo (true))
6300 SetChangedFlag (true);
6302 else if (function)
6304 switch (GetFunctionID (function))
6306 /* clear 'undo objects' list */
6307 case F_ClearList:
6308 ClearUndoList (false);
6309 break;
6312 notify_crosshair_change (true);
6313 return 0;
6316 /* --------------------------------------------------------------------------- */
6318 static const char redo_syntax[] = "Redo()";
6320 static const char redo_help[] = "Redo recent \"undo\" operations.";
6322 /* %start-doc actions Redo
6324 This routine allows you to recover from the last undo command. You
6325 might want to do this if you thought that undo was going to revert
6326 something other than what it actually did (in case you are confused
6327 about which operations are un-doable), or if you have been backing up
6328 through a long undo list and over-shoot your stopping point. Any
6329 change that is made since the undo in question will trim the redo
6330 list. For example if you add ten lines, then undo three of them you
6331 could use redo to put them back, but if you move a line on the board
6332 before performing the redo, you will lose the ability to "redo" the
6333 three "undone" lines.
6335 %end-doc */
6337 static int
6338 ActionRedo (int argc, char **argv, Coord x, Coord y)
6340 if (((Settings.Mode == POLYGON_MODE ||
6341 Settings.Mode == POLYGONHOLE_MODE) &&
6342 Crosshair.AttachedPolygon.PointN) ||
6343 Crosshair.AttachedLine.State == STATE_SECOND)
6344 return 1;
6345 notify_crosshair_change (false);
6346 if (Redo (true))
6348 SetChangedFlag (true);
6349 if (Settings.Mode == LINE_MODE &&
6350 Crosshair.AttachedLine.State != STATE_FIRST)
6352 LineType *line = g_list_last (CURRENT->Line)->data;
6353 Crosshair.AttachedLine.Point1.X =
6354 Crosshair.AttachedLine.Point2.X = line->Point2.X;
6355 Crosshair.AttachedLine.Point1.Y =
6356 Crosshair.AttachedLine.Point2.Y = line->Point2.Y;
6357 addedLines++;
6360 notify_crosshair_change (true);
6361 return 0;
6364 /* --------------------------------------------------------------------------- */
6366 static const char polygon_syntax[] = "Polygon(Close|PreviousPoint)";
6368 static const char polygon_help[] = "Some polygon related stuff.";
6370 /* %start-doc actions Polygon
6372 Polygons need a special action routine to make life easier.
6374 @table @code
6376 @item Close
6377 Creates the final segment of the polygon. This may fail if clipping
6378 to 45 degree lines is switched on, in which case a warning is issued.
6380 @item PreviousPoint
6381 Resets the newly entered corner to the previous one. The Undo action
6382 will call Polygon(PreviousPoint) when appropriate to do so.
6384 @end table
6386 %end-doc */
6388 static int
6389 ActionPolygon (int argc, char **argv, Coord x, Coord y)
6391 char *function = ARG (0);
6392 if (function && Settings.Mode == POLYGON_MODE)
6394 notify_crosshair_change (false);
6395 switch (GetFunctionID (function))
6397 /* close open polygon if possible */
6398 case F_Close:
6399 ClosePolygon ();
6400 break;
6402 /* go back to the previous point */
6403 case F_PreviousPoint:
6404 GoToPreviousPoint ();
6405 break;
6407 notify_crosshair_change (true);
6409 return 0;
6412 /* --------------------------------------------------------------------------- */
6414 static const char routestyle_syntax[] = "RouteStyle(1|2|3|4)";
6416 static const char routestyle_help[] =
6417 "Copies the indicated routing style into the current sizes.";
6419 /* %start-doc actions RouteStyle
6421 %end-doc */
6423 static int
6424 ActionRouteStyle (int argc, char **argv, Coord x, Coord y)
6426 char *str = ARG (0);
6427 RouteStyleType *rts;
6428 int number;
6430 if (str)
6432 number = atoi (str);
6433 if (number > 0 && number <= NUM_STYLES)
6435 rts = &PCB->RouteStyle[number - 1];
6436 SetLineSize (rts->Thick);
6437 SetViaSize (rts->Diameter, true);
6438 SetViaDrillingHole (rts->Hole, true);
6439 SetKeepawayWidth (rts->Keepaway);
6440 hid_action("RouteStylesChanged");
6443 return 0;
6447 /* --------------------------------------------------------------------------- */
6449 static const char moveobject_syntax[] = "MoveObject(X,Y,dim)";
6451 static const char moveobject_help[] = "Moves the object under the crosshair.";
6453 /* %start-doc actions MoveObject
6455 The @code{X} and @code{Y} are treated like @code{delta} is for many
6456 other objects. For each, if it's prefixed by @code{+} or @code{-},
6457 then that amount is relative. Otherwise, it's absolute. Units can be
6458 @code{mil} or @code{mm}; if unspecified, units are PCB's internal
6459 units, currently 1/100 mil.
6461 %end-doc */
6463 static int
6464 ActionMoveObject (int argc, char **argv, Coord x, Coord y)
6466 char *x_str = ARG (0);
6467 char *y_str = ARG (1);
6468 char *units = ARG (2);
6469 Coord nx, ny;
6470 bool absolute1, absolute2;
6471 void *ptr1, *ptr2, *ptr3;
6472 int type;
6474 ny = GetValue (y_str, units, &absolute1);
6475 nx = GetValue (x_str, units, &absolute2);
6477 type = SearchScreen (x, y, MOVE_TYPES, &ptr1, &ptr2, &ptr3);
6478 if (type == NO_TYPE)
6480 Message (_("Nothing found under crosshair\n"));
6481 return 1;
6483 if (absolute1)
6484 nx -= x;
6485 if (absolute2)
6486 ny -= y;
6487 Crosshair.AttachedObject.RubberbandN = 0;
6488 if (TEST_FLAG (RUBBERBANDFLAG, PCB))
6489 LookupRubberbandLines (type, ptr1, ptr2, ptr3);
6490 if (type == ELEMENT_TYPE)
6491 LookupRatLines (type, ptr1, ptr2, ptr3);
6492 MoveObjectAndRubberband (type, ptr1, ptr2, ptr3, nx, ny);
6493 SetChangedFlag (true);
6494 return 0;
6497 /* --------------------------------------------------------------------------- */
6499 static const char movetocurrentlayer_syntax[] =
6500 "MoveToCurrentLayer(Object|SelectedObjects)";
6502 static const char movetocurrentlayer_help[] =
6503 "Moves objects to the current layer.";
6505 /* %start-doc actions MoveToCurrentLayer
6507 Note that moving an element from a component layer to a solder layer,
6508 or from solder to component, won't automatically flip it. Use the
6509 @code{Flip()} action to do that.
6511 %end-doc */
6513 static int
6514 ActionMoveToCurrentLayer (int argc, char **argv, Coord x, Coord y)
6516 char *function = ARG (0);
6517 if (function)
6519 switch (GetFunctionID (function))
6521 case F_Object:
6523 int type;
6524 void *ptr1, *ptr2, *ptr3;
6526 gui->get_coords (_("Select an Object"), &x, &y);
6527 if ((type =
6528 SearchScreen (x, y, MOVETOLAYER_TYPES,
6529 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
6530 if (MoveObjectToLayer (type, ptr1, ptr2, ptr3, CURRENT, false))
6531 SetChangedFlag (true);
6532 break;
6535 case F_SelectedObjects:
6536 case F_Selected:
6537 if (MoveSelectedObjectsToLayer (CURRENT))
6538 SetChangedFlag (true);
6539 break;
6542 return 0;
6546 static const char setsame_syntax[] = "SetSame()";
6548 static const char setsame_help[] =
6549 "Sets current layer and sizes to match indicated item.";
6551 /* %start-doc actions SetSame
6553 When invoked over any line, arc, polygon, or via, this changes the
6554 current layer to be the layer that item is on, and changes the current
6555 sizes (thickness, keepaway, drill, etc) according to that item.
6557 %end-doc */
6559 static int
6560 ActionSetSame (int argc, char **argv, Coord x, Coord y)
6562 void *ptr1, *ptr2, *ptr3;
6563 int type;
6564 LayerTypePtr layer = CURRENT;
6566 type = SearchScreen (x, y, CLONE_TYPES, &ptr1, &ptr2, &ptr3);
6567 /* set layer current and size from line or arc */
6568 switch (type)
6570 case LINE_TYPE:
6571 notify_crosshair_change (false);
6572 Settings.LineThickness = ((LineTypePtr) ptr2)->Thickness;
6573 Settings.Keepaway = ((LineTypePtr) ptr2)->Clearance / 2;
6574 layer = (LayerTypePtr) ptr1;
6575 if (Settings.Mode != LINE_MODE)
6576 SetMode (LINE_MODE);
6577 notify_crosshair_change (true);
6578 hid_action ("RouteStylesChanged");
6579 break;
6581 case ARC_TYPE:
6582 notify_crosshair_change (false);
6583 Settings.LineThickness = ((ArcTypePtr) ptr2)->Thickness;
6584 Settings.Keepaway = ((ArcTypePtr) ptr2)->Clearance / 2;
6585 layer = (LayerTypePtr) ptr1;
6586 if (Settings.Mode != ARC_MODE)
6587 SetMode (ARC_MODE);
6588 notify_crosshair_change (true);
6589 hid_action ("RouteStylesChanged");
6590 break;
6592 case POLYGON_TYPE:
6593 layer = (LayerTypePtr) ptr1;
6594 break;
6596 case VIA_TYPE:
6597 notify_crosshair_change (false);
6598 Settings.ViaThickness = ((PinTypePtr) ptr2)->Thickness;
6599 Settings.ViaDrillingHole = ((PinTypePtr) ptr2)->DrillingHole;
6600 Settings.Keepaway = ((PinTypePtr) ptr2)->Clearance / 2;
6601 if (Settings.Mode != VIA_MODE)
6602 SetMode (VIA_MODE);
6603 notify_crosshair_change (true);
6604 hid_action ("RouteStylesChanged");
6605 break;
6607 default:
6608 return 1;
6610 if (layer != CURRENT)
6612 ChangeGroupVisibility (GetLayerNumber (PCB->Data, layer), true, true);
6613 Redraw ();
6615 return 0;
6619 /* --------------------------------------------------------------------------- */
6621 static const char setflag_syntax[] =
6622 "SetFlag(Object|Selected|SelectedObjects, flag)\n"
6623 "SetFlag(SelectedLines|SelectedPins|SelectedVias, flag)\n"
6624 "SetFlag(SelectedPads|SelectedTexts|SelectedNames, flag)\n"
6625 "SetFlag(SelectedElements, flag)\n"
6626 "flag = square | octagon | thermal | join";
6628 static const char setflag_help[] = "Sets flags on objects.";
6630 /* %start-doc actions SetFlag
6632 Turns the given flag on, regardless of its previous setting. See
6633 @code{ChangeFlag}.
6635 @example
6636 SetFlag(SelectedPins,thermal)
6637 @end example
6639 %end-doc */
6641 static int
6642 ActionSetFlag (int argc, char **argv, Coord x, Coord y)
6644 char *function = ARG (0);
6645 char *flag = ARG (1);
6646 ChangeFlag (function, flag, 1, "SetFlag");
6647 return 0;
6650 /* --------------------------------------------------------------------------- */
6652 static const char clrflag_syntax[] =
6653 "ClrFlag(Object|Selected|SelectedObjects, flag)\n"
6654 "ClrFlag(SelectedLines|SelectedPins|SelectedVias, flag)\n"
6655 "ClrFlag(SelectedPads|SelectedTexts|SelectedNames, flag)\n"
6656 "ClrFlag(SelectedElements, flag)\n"
6657 "flag = square | octagon | thermal | join";
6659 static const char clrflag_help[] = "Clears flags on objects.";
6661 /* %start-doc actions ClrFlag
6663 Turns the given flag off, regardless of its previous setting. See
6664 @code{ChangeFlag}.
6666 @example
6667 ClrFlag(SelectedLines,join)
6668 @end example
6670 %end-doc */
6672 static int
6673 ActionClrFlag (int argc, char **argv, Coord x, Coord y)
6675 char *function = ARG (0);
6676 char *flag = ARG (1);
6677 ChangeFlag (function, flag, 0, "ClrFlag");
6678 return 0;
6681 /* --------------------------------------------------------------------------- */
6683 static const char changeflag_syntax[] =
6684 "ChangeFlag(Object|Selected|SelectedObjects, flag, value)\n"
6685 "ChangeFlag(SelectedLines|SelectedPins|SelectedVias, flag, value)\n"
6686 "ChangeFlag(SelectedPads|SelectedTexts|SelectedNames, flag, value)\n"
6687 "ChangeFlag(SelectedElements, flag, value)\n"
6688 "flag = square | octagon | thermal | join\n"
6689 "value = 0 | 1";
6691 static const char changeflag_help[] = "Sets or clears flags on objects.";
6693 /* %start-doc actions ChangeFlag
6695 Toggles the given flag on the indicated object(s). The flag may be
6696 one of the flags listed above (square, octagon, thermal, join). The
6697 value may be the number 0 or 1. If the value is 0, the flag is
6698 cleared. If the value is 1, the flag is set.
6700 %end-doc */
6702 static int
6703 ActionChangeFlag (int argc, char **argv, Coord x, Coord y)
6705 char *function = ARG (0);
6706 char *flag = ARG (1);
6707 int value = argc > 2 ? atoi (argv[2]) : -1;
6708 if (value != 0 && value != 1)
6709 AFAIL (changeflag);
6711 ChangeFlag (function, flag, value, "ChangeFlag");
6712 return 0;
6716 static void
6717 ChangeFlag (char *what, char *flag_name, int value, char *cmd_name)
6719 bool (*set_object) (int, void *, void *, void *);
6720 bool (*set_selected) (int);
6722 if (NSTRCMP (flag_name, "square") == 0)
6724 set_object = value ? SetObjectSquare : ClrObjectSquare;
6725 set_selected = value ? SetSelectedSquare : ClrSelectedSquare;
6727 else if (NSTRCMP (flag_name, "octagon") == 0)
6729 set_object = value ? SetObjectOctagon : ClrObjectOctagon;
6730 set_selected = value ? SetSelectedOctagon : ClrSelectedOctagon;
6732 else if (NSTRCMP (flag_name, "join") == 0)
6734 /* Note: these are backwards, because the flag is "clear" but
6735 the command is "join". */
6736 set_object = value ? ClrObjectJoin : SetObjectJoin;
6737 set_selected = value ? ClrSelectedJoin : SetSelectedJoin;
6739 else
6741 Message (_("%s(): Flag \"%s\" is not valid\n"), cmd_name, flag_name);
6742 return;
6745 switch (GetFunctionID (what))
6747 case F_Object:
6749 int type;
6750 void *ptr1, *ptr2, *ptr3;
6752 if ((type =
6753 SearchScreen (Crosshair.X, Crosshair.Y, CHANGESIZE_TYPES,
6754 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
6755 if (TEST_FLAG (LOCKFLAG, (PinTypePtr) ptr2))
6756 Message (_("Sorry, the object is locked\n"));
6757 if (set_object (type, ptr1, ptr2, ptr3))
6758 SetChangedFlag (true);
6759 break;
6762 case F_SelectedVias:
6763 if (set_selected (VIA_TYPE))
6764 SetChangedFlag (true);
6765 break;
6767 case F_SelectedPins:
6768 if (set_selected (PIN_TYPE))
6769 SetChangedFlag (true);
6770 break;
6772 case F_SelectedPads:
6773 if (set_selected (PAD_TYPE))
6774 SetChangedFlag (true);
6775 break;
6777 case F_SelectedLines:
6778 if (set_selected (LINE_TYPE))
6779 SetChangedFlag (true);
6780 break;
6782 case F_SelectedTexts:
6783 if (set_selected (TEXT_TYPE))
6784 SetChangedFlag (true);
6785 break;
6787 case F_SelectedNames:
6788 if (set_selected (ELEMENTNAME_TYPE))
6789 SetChangedFlag (true);
6790 break;
6792 case F_SelectedElements:
6793 if (set_selected (ELEMENT_TYPE))
6794 SetChangedFlag (true);
6795 break;
6797 case F_Selected:
6798 case F_SelectedObjects:
6799 if (set_selected (CHANGESIZE_TYPES))
6800 SetChangedFlag (true);
6801 break;
6805 /* --------------------------------------------------------------------------- */
6807 static const char executefile_syntax[] = "ExecuteFile(filename)";
6809 static const char executefile_help[] = "Run actions from the given file.";
6811 /* %start-doc actions ExecuteFile
6813 Lines starting with @code{#} are ignored.
6815 %end-doc */
6817 static int
6818 ActionExecuteFile (int argc, char **argv, Coord x, Coord y)
6820 FILE *fp;
6821 char *fname;
6822 char line[256];
6823 int n = 0;
6824 char *sp;
6826 if (argc != 1)
6827 AFAIL (executefile);
6829 fname = argv[0];
6831 if ((fp = fopen (fname, "r")) == NULL)
6833 fprintf (stderr, _("Could not open actions file \"%s\".\n"), fname);
6834 return 1;
6837 defer_updates = 1;
6838 defer_needs_update = 0;
6839 while (fgets (line, sizeof (line), fp) != NULL)
6841 n++;
6842 sp = line;
6844 /* eat the trailing newline */
6845 while (*sp && *sp != '\r' && *sp != '\n')
6846 sp++;
6847 *sp = '\0';
6849 /* eat leading spaces and tabs */
6850 sp = line;
6851 while (*sp && (*sp == ' ' || *sp == '\t'))
6852 sp++;
6855 * if we have anything left and its not a comment line
6856 * then execute it
6859 if (*sp && *sp != '#')
6861 /*Message ("%s : line %-3d : \"%s\"\n", fname, n, sp);*/
6862 hid_parse_actions (sp);
6866 defer_updates = 0;
6867 if (defer_needs_update)
6869 IncrementUndoSerialNumber ();
6870 gui->invalidate_all ();
6872 fclose (fp);
6873 return 0;
6876 /* --------------------------------------------------------------------------- */
6878 static int
6879 ActionPSCalib (int argc, char **argv, Coord x, Coord y)
6881 HID *ps = hid_find_exporter ("ps");
6882 ps->calibrate (0.0,0.0);
6883 return 0;
6886 /* --------------------------------------------------------------------------- */
6888 static ElementType *element_cache = NULL;
6890 static ElementType *
6891 find_element_by_refdes (char *refdes)
6893 if (element_cache
6894 && NAMEONPCB_NAME(element_cache)
6895 && strcmp (NAMEONPCB_NAME(element_cache), refdes) == 0)
6896 return element_cache;
6898 ELEMENT_LOOP (PCB->Data);
6900 if (NAMEONPCB_NAME(element)
6901 && strcmp (NAMEONPCB_NAME(element), refdes) == 0)
6903 element_cache = element;
6904 return element_cache;
6907 END_LOOP;
6908 return NULL;
6911 static AttributeType *
6912 lookup_attr (AttributeListTypePtr list, const char *name)
6914 int i;
6915 for (i=0; i<list->Number; i++)
6916 if (strcmp (list->List[i].name, name) == 0)
6917 return & list->List[i];
6918 return NULL;
6921 static void
6922 delete_attr (AttributeListTypePtr list, AttributeType *attr)
6924 int idx = attr - list->List;
6925 if (idx < 0 || idx >= list->Number)
6926 return;
6927 if (list->Number - idx > 1)
6928 memmove (attr, attr+1, (list->Number - idx - 1) * sizeof(AttributeType));
6929 list->Number --;
6932 /* ---------------------------------------------------------------- */
6933 static const char elementlist_syntax[] = "ElementList(Start|Done|Need,<refdes>,<footprint>,<value>)";
6935 static const char elementlist_help[] = "Adds the given element if it doesn't already exist.";
6937 /* %start-doc actions elementlist
6939 @table @code
6941 @item Start
6942 Indicates the start of an element list; call this before any Need
6943 actions.
6945 @item Need
6946 Searches the board for an element with a matching refdes.
6948 If found, the value and footprint are updated.
6950 If not found, a new element is created with the given footprint and value.
6952 @item Done
6953 Compares the list of elements needed since the most recent
6954 @code{start} with the list of elements actually on the board. Any
6955 elements that weren't listed are selected, so that the user may delete
6956 them.
6958 @end table
6960 %end-doc */
6962 static int number_of_footprints_not_found;
6964 static int
6965 parse_layout_attribute_units (char *name, int def)
6967 const char *as = AttributeGet (PCB, name);
6968 if (!as)
6969 return def;
6970 return GetValue (as, NULL, NULL);
6973 static int
6974 ActionElementList (int argc, char **argv, Coord x, Coord y)
6976 ElementType *e = NULL;
6977 char *refdes, *value, *footprint, *old;
6978 char *args[3];
6979 char *function = argv[0];
6981 #ifdef DEBUG
6982 printf("Entered ActionElementList, executing function %s\n", function);
6983 #endif
6985 if (strcasecmp (function, "start") == 0)
6987 ELEMENT_LOOP (PCB->Data);
6989 CLEAR_FLAG (FOUNDFLAG, element);
6991 END_LOOP;
6992 element_cache = NULL;
6993 number_of_footprints_not_found = 0;
6994 return 0;
6997 if (strcasecmp (function, "done") == 0)
6999 ELEMENT_LOOP (PCB->Data);
7001 if (TEST_FLAG (FOUNDFLAG, element))
7003 CLEAR_FLAG (FOUNDFLAG, element);
7005 else if (! EMPTY_STRING_P (NAMEONPCB_NAME (element)))
7007 /* Unnamed elements should remain untouched */
7008 SET_FLAG (SELECTEDFLAG, element);
7011 END_LOOP;
7012 if (number_of_footprints_not_found > 0)
7013 gui->confirm_dialog ("Not all requested footprints were found.\n"
7014 "See the message log for details",
7015 "Ok", NULL);
7016 return 0;
7019 if (strcasecmp (function, "need") != 0)
7020 AFAIL (elementlist);
7022 if (argc != 4)
7023 AFAIL (elementlist);
7025 argc --;
7026 argv ++;
7028 refdes = ARG(0);
7029 footprint = ARG(1);
7030 value = ARG(2);
7032 args[0] = footprint;
7033 args[1] = refdes;
7034 args[2] = value;
7036 #ifdef DEBUG
7037 printf(" ... footprint = %s\n", footprint);
7038 printf(" ... refdes = %s\n", refdes);
7039 printf(" ... value = %s\n", value);
7040 #endif
7042 e = find_element_by_refdes (refdes);
7044 if (!e)
7046 Coord nx, ny, d;
7048 #ifdef DEBUG
7049 printf(" ... Footprint not on board, need to add it.\n");
7050 #endif
7051 /* Not on board, need to add it. */
7052 if (LoadFootprint(argc, args, x, y))
7054 number_of_footprints_not_found ++;
7055 return 1;
7058 nx = PCB->MaxWidth / 2;
7059 ny = PCB->MaxHeight / 2;
7060 d = MIN (PCB->MaxWidth, PCB->MaxHeight) / 10;
7062 nx = parse_layout_attribute_units ("import::newX", nx);
7063 ny = parse_layout_attribute_units ("import::newY", ny);
7064 d = parse_layout_attribute_units ("import::disperse", d);
7066 if (d > 0)
7068 nx += rand () % (d*2) - d;
7069 ny += rand () % (d*2) - d;
7072 if (nx < 0)
7073 nx = 0;
7074 if (nx >= PCB->MaxWidth)
7075 nx = PCB->MaxWidth - 1;
7076 if (ny < 0)
7077 ny = 0;
7078 if (ny >= PCB->MaxHeight)
7079 ny = PCB->MaxHeight - 1;
7081 /* Place components onto center of board. */
7082 if (CopyPastebufferToLayout (nx, ny))
7083 SetChangedFlag (true);
7086 else if (e && strcmp (DESCRIPTION_NAME(e), footprint) != 0)
7088 #ifdef DEBUG
7089 printf(" ... Footprint on board, but different from footprint loaded.\n");
7090 #endif
7091 int er, pr, i;
7092 Coord mx, my;
7093 ElementType *pe;
7095 /* Different footprint, we need to swap them out. */
7096 if (LoadFootprint(argc, args, x, y))
7098 number_of_footprints_not_found ++;
7099 return 1;
7102 er = ElementOrientation (e);
7103 pe = PASTEBUFFER->Data->Element->data;
7104 if (!FRONT (e))
7105 MirrorElementCoordinates (PASTEBUFFER->Data, pe, pe->MarkY*2 - PCB->MaxHeight);
7106 pr = ElementOrientation (pe);
7108 mx = e->MarkX;
7109 my = e->MarkY;
7111 if (er != pr)
7112 RotateElementLowLevel (PASTEBUFFER->Data, pe, pe->MarkX, pe->MarkY, (er-pr+4)%4);
7114 for (i=0; i<MAX_ELEMENTNAMES; i++)
7116 pe->Name[i].X = e->Name[i].X - mx + pe->MarkX ;
7117 pe->Name[i].Y = e->Name[i].Y - my + pe->MarkY ;
7118 pe->Name[i].Direction = e->Name[i].Direction;
7119 pe->Name[i].Scale = e->Name[i].Scale;
7122 RemoveElement (e);
7124 if (CopyPastebufferToLayout (mx, my))
7125 SetChangedFlag (true);
7128 /* Now reload footprint */
7129 element_cache = NULL;
7130 e = find_element_by_refdes (refdes);
7132 old = ChangeElementText (PCB, PCB->Data, e, NAMEONPCB_INDEX, strdup (refdes));
7133 if (old)
7134 free(old);
7135 old = ChangeElementText (PCB, PCB->Data, e, VALUE_INDEX, strdup (value));
7136 if (old)
7137 free(old);
7139 SET_FLAG (FOUNDFLAG, e);
7141 #ifdef DEBUG
7142 printf(" ... Leaving ActionElementList.\n");
7143 #endif
7145 return 0;
7148 /* ---------------------------------------------------------------- */
7149 static const char elementsetattr_syntax[] = "ElementSetAttr(refdes,name[,value])";
7151 static const char elementsetattr_help[] = "Sets or clears an element-specific attribute.";
7153 /* %start-doc actions elementsetattr
7155 If a value is specified, the named attribute is added (if not already
7156 present) or changed (if it is) to the given value. If the value is
7157 not specified, the given attribute is removed if present.
7159 %end-doc */
7161 static int
7162 ActionElementSetAttr (int argc, char **argv, Coord x, Coord y)
7164 ElementType *e = NULL;
7165 char *refdes, *name, *value;
7166 AttributeType *attr;
7168 if (argc < 2)
7170 AFAIL (changepinname);
7173 refdes = argv[0];
7174 name = argv[1];
7175 value = ARG(2);
7177 ELEMENT_LOOP (PCB->Data);
7179 if (NSTRCMP (refdes, NAMEONPCB_NAME (element)) == 0)
7181 e = element;
7182 break;
7185 END_LOOP;
7187 if (!e)
7189 Message(_("Cannot change attribute of %s - element not found\n"), refdes);
7190 return 1;
7193 attr = lookup_attr (&e->Attributes, name);
7195 if (attr && value)
7197 free (attr->value);
7198 attr->value = strdup (value);
7200 if (attr && ! value)
7202 delete_attr (& e->Attributes, attr);
7204 if (!attr && value)
7206 CreateNewAttribute (& e->Attributes, name, value);
7209 return 0;
7212 /* ---------------------------------------------------------------- */
7213 static const char execcommand_syntax[] = "ExecCommand(command)";
7215 static const char execcommand_help[] = "Runs a command.";
7217 /* %start-doc actions execcommand
7219 Runs the given command, which is a system executable.
7221 %end-doc */
7223 static int
7224 ActionExecCommand (int argc, char **argv, Coord x, Coord y)
7226 char *command;
7228 if (argc < 1)
7230 AFAIL (execcommand);
7233 command = ARG(0);
7235 if (system (command))
7236 return 1;
7237 return 0;
7240 /* ---------------------------------------------------------------- */
7242 static int
7243 pcb_spawnvp (char **argv)
7245 #ifdef HAVE__SPAWNVP
7246 int result = _spawnvp (_P_WAIT, argv[0], (const char * const *) argv);
7247 if (result == -1)
7248 return 1;
7249 else
7250 return 0;
7251 #else
7252 int pid;
7253 pid = fork ();
7254 if (pid < 0)
7256 /* error */
7257 Message(_("Cannot fork!"));
7258 return 1;
7260 else if (pid == 0)
7262 /* Child */
7263 execvp (argv[0], argv);
7264 exit(1);
7266 else
7268 int rv;
7269 /* Parent */
7270 wait (&rv);
7272 return 0;
7273 #endif
7276 /* ---------------------------------------------------------------- */
7278 * Creates a new temporary file name. Hopefully the operating system
7279 * provides a mkdtemp() function to securily create a temporary
7280 * directory with mode 0700. If so then that directory is created and
7281 * the returned string is made up of the directory plus the name
7282 * variable. For example:
7284 * tempfile_name_new ("myfile") might return
7285 * "/var/tmp/pcb.123456/myfile".
7287 * If mkdtemp() is not available then 'name' is ignored and the
7288 * insecure tmpnam() function is used.
7290 * Files/names created with tempfile_name_new() should be unlinked
7291 * with tempfile_unlink to make sure the temporary directory is also
7292 * removed when mkdtemp() is used.
7294 static char *
7295 tempfile_name_new (char * name)
7297 char *tmpfile = NULL;
7298 #ifdef HAVE_MKDTEMP
7299 char *tmpdir, *mytmpdir;
7300 size_t len;
7301 #endif
7303 assert ( name != NULL );
7305 #ifdef HAVE_MKDTEMP
7306 #define TEMPLATE "pcb.XXXXXXXX"
7309 tmpdir = getenv ("TMPDIR");
7311 /* FIXME -- what about win32? */
7312 if (tmpdir == NULL) {
7313 tmpdir = "/tmp";
7316 mytmpdir = (char *) malloc (sizeof(char) *
7317 (strlen (tmpdir) +
7319 strlen (TEMPLATE) +
7320 1));
7321 if (mytmpdir == NULL) {
7322 fprintf (stderr, "%s(): malloc failed()\n", __FUNCTION__);
7323 exit (1);
7326 *mytmpdir = '\0';
7327 (void)strcat (mytmpdir, tmpdir);
7328 (void)strcat (mytmpdir, PCB_DIR_SEPARATOR_S);
7329 (void)strcat (mytmpdir, TEMPLATE);
7330 if (mkdtemp (mytmpdir) == NULL) {
7331 fprintf (stderr, "%s(): mkdtemp (\"%s\") failed\n", __FUNCTION__, mytmpdir);
7332 free (mytmpdir);
7333 return NULL;
7337 len = strlen (mytmpdir) + /* the temp directory name */
7338 1 + /* the directory sep. */
7339 strlen (name) + /* the file name */
7340 1 /* the \0 termination */
7343 tmpfile = (char *) malloc (sizeof (char) * len);
7345 *tmpfile = '\0';
7346 (void)strcat (tmpfile, mytmpdir);
7347 (void)strcat (tmpfile, PCB_DIR_SEPARATOR_S);
7348 (void)strcat (tmpfile, name);
7350 free (mytmpdir);
7351 #undef TEMPLATE
7352 #else
7354 * tmpnam() uses a static buffer so strdup() the result right away
7355 * in case someone decides to create multiple temp names.
7357 tmpfile = strdup (tmpnam (NULL));
7358 #ifdef __WIN32__
7360 /* Guile doesn't like \ separators */
7361 char *c;
7362 for (c = tmpfile; *c; c++)
7363 if (*c == '\\')
7364 *c = '/';
7366 #endif
7367 #endif
7369 return tmpfile;
7372 /* ---------------------------------------------------------------- */
7374 * Unlink a temporary file. If we have mkdtemp() then our temp file
7375 * lives in a temporary directory and we need to remove that directory
7376 * too.
7378 static int
7379 tempfile_unlink (char * name)
7381 #ifdef DEBUG
7382 /* SDB says: Want to keep old temp files for examiniation when debugging */
7383 return 0;
7384 #endif
7386 #ifdef HAVE_MKDTEMP
7387 int e, rc2 = 0;
7388 char *dname;
7390 unlink (name);
7391 /* it is possible that the file was never created so it is OK if the
7392 unlink fails */
7394 /* now figure out the directory name to remove */
7395 e = strlen (name) - 1;
7396 while (e > 0 && name[e] != PCB_DIR_SEPARATOR_C) {e--;}
7398 dname = strdup (name);
7399 dname[e] = '\0';
7402 * at this point, e *should* point to the end of the directory part
7403 * but lets make sure.
7405 if (e > 0) {
7406 rc2 = rmdir (dname);
7407 if (rc2 != 0) {
7408 perror (dname);
7411 } else {
7412 fprintf (stderr, _("%s(): Unable to determine temp directory name from the temp file\n"),
7413 __FUNCTION__);
7414 fprintf (stderr, "%s(): \"%s\"\n",
7415 __FUNCTION__, name);
7416 rc2 = -1;
7419 /* name was allocated with malloc */
7420 free (dname);
7421 free (name);
7424 * FIXME - should also return -1 if the temp file exists and was not
7425 * removed.
7427 if (rc2 != 0) {
7428 return -1;
7431 #else
7432 int rc = unlink (name);
7434 if (rc != 0) {
7435 fprintf (stderr, _("Failed to unlink \"%s\"\n"), name);
7436 free (name);
7437 return rc;
7439 free (name);
7441 #endif
7443 return 0;
7446 /* ---------------------------------------------------------------- */
7447 static const char import_syntax[] =
7448 "Import()\n"
7449 "Import([gnetlist|make[,source,source,...]])\n"
7450 "Import(setnewpoint[,(mark|center|X,Y)])\n"
7451 "Import(setdisperse,D,units)\n";
7453 static const char import_help[] = "Import schematics.";
7455 /* %start-doc actions Import
7457 Imports element and netlist data from the schematics (or some other
7458 source). The first parameter, which is optional, is the mode. If not
7459 specified, the @code{import::mode} attribute in the PCB is used.
7460 @code{gnetlist} means gnetlist is used to obtain the information from
7461 the schematics. @code{make} invokes @code{make}, assuming the user
7462 has a @code{Makefile} in the current directory. The @code{Makefile}
7463 will be invoked with the following variables set:
7465 @table @code
7467 @item PCB
7468 The name of the .pcb file
7470 @item SRCLIST
7471 A space-separated list of source files
7473 @item OUT
7474 The name of the file in which to put the command script, which may
7475 contain any @pcb{} actions. By default, this is a temporary file
7476 selected by @pcb{}, but if you specify an @code{import::outfile}
7477 attribute, that file name is used instead (and not automatically
7478 deleted afterwards).
7480 @end table
7482 The target specified to be built is the first of these that apply:
7484 @itemize @bullet
7486 @item
7487 The target specified by an @code{import::target} attribute.
7489 @item
7490 The output file specified by an @code{import::outfile} attribute.
7492 @item
7493 If nothing else is specified, the target is @code{pcb_import}.
7495 @end itemize
7497 If you specify an @code{import::makefile} attribute, then "-f <that
7498 file>" will be added to the command line.
7500 If you specify the mode, you may also specify the source files
7501 (schematics). If you do not specify any, the list of schematics is
7502 obtained by reading the @code{import::src@var{N}} attributes (like
7503 @code{import::src0}, @code{import::src1}, etc).
7505 For compatibility with future extensions to the import file format,
7506 the generated file @emph{must not} start with the two characters
7507 @code{#%}.
7509 If a temporary file is needed the @code{TMPDIR} environment variable
7510 is used to select its location.
7512 Note that the programs @code{gnetlist} and @code{make} may be
7513 overridden by the user via the @code{make-program} and @code{gnetlist}
7514 @code{pcb} settings (i.e. in @code{~/.pcb/settings} or on the command
7515 line).
7517 If @pcb{} cannot determine which schematic(s) to import from, the GUI
7518 is called to let user choose (see @code{ImportGUI()}).
7520 Note that Import() doesn't delete anything - after an Import, elements
7521 which shouldn't be on the board are selected and may be removed once
7522 it's determined that the deletion is appropriate.
7524 If @code{Import()} is called with @code{setnewpoint}, then the location
7525 of new components can be specified. This is where parts show up when
7526 they're added to the board. The default is the center of the board.
7528 @table @code
7530 @item Import(setnewpoint)
7532 Prompts the user to click on the board somewhere, uses that point. If
7533 called by a hotkey, uses the current location of the crosshair.
7535 @item Import(setnewpoint,mark)
7537 Uses the location of the mark. If no mark is present, the point is
7538 not changed.
7540 @item Import(setnewpoint,center)
7542 Resets the point to the center of the board.
7544 @item Import(setnewpoint,X,Y,units)
7546 Sets the point to the specific coordinates given. Example:
7547 @code{Import(setnewpoint,50,25,mm)}
7549 @end table
7551 Note that the X and Y locations are stored in attributes named
7552 @code{import::newX} and @code{import::newY} so you could change them
7553 manually if you wished.
7555 Calling @code{Import(setdisperse,D,units)} sets how much the newly
7556 placed elements are dispersed relative to the set point. For example,
7557 @code{Import(setdisperse,10,mm)} will offset each part randomly up to
7558 10mm away from the point. The default dispersion is 1/10th of the
7559 smallest board dimension. Dispersion is saved in the
7560 @code{import::disperse} attribute.
7562 %end-doc */
7564 static int
7565 ActionImport (int argc, char **argv, Coord x, Coord y)
7567 char *mode;
7568 char **sources = NULL;
7569 int nsources = 0;
7571 #ifdef DEBUG
7572 printf("ActionImport: =========== Entering ActionImport ============\n");
7573 #endif
7575 mode = ARG (0);
7577 if (mode && strcasecmp (mode, "setdisperse") == 0)
7579 char *ds, *units;
7580 char buf[50];
7582 ds = ARG (1);
7583 units = ARG (2);
7584 if (!ds)
7586 const char *as = AttributeGet (PCB, "import::disperse");
7587 ds = gui->prompt_for(_("Enter dispersion:"), as ? as : "0");
7589 if (units)
7591 sprintf(buf, "%s%s", ds, units);
7592 AttributePut (PCB, "import::disperse", buf);
7594 else
7595 AttributePut (PCB, "import::disperse", ds);
7596 if (ARG (1) == NULL)
7597 free (ds);
7598 return 0;
7601 if (mode && strcasecmp (mode, "setnewpoint") == 0)
7603 const char *xs, *ys, *units;
7604 Coord x, y;
7605 char buf[50];
7607 xs = ARG (1);
7608 ys = ARG (2);
7609 units = ARG (3);
7611 if (!xs)
7613 gui->get_coords (_("Click on a location"), &x, &y);
7615 else if (strcasecmp (xs, "center") == 0)
7617 AttributeRemove (PCB, "import::newX");
7618 AttributeRemove (PCB, "import::newY");
7619 return 0;
7621 else if (strcasecmp (xs, "mark") == 0)
7623 if (!Marked.status)
7624 return 0;
7626 x = Marked.X;
7627 y = Marked.Y;
7629 else if (ys)
7631 x = GetValue (xs, units, NULL);
7632 y = GetValue (ys, units, NULL);
7634 else
7636 Message (_("Bad syntax for Import(setnewpoint)"));
7637 return 1;
7640 pcb_sprintf (buf, "%$ms", x);
7641 AttributePut (PCB, "import::newX", buf);
7642 pcb_sprintf (buf, "%$ms", y);
7643 AttributePut (PCB, "import::newY", buf);
7644 return 0;
7647 if (! mode)
7648 mode = AttributeGet (PCB, "import::mode");
7649 if (! mode)
7650 mode = "gnetlist";
7652 if (argc > 1)
7654 sources = argv + 1;
7655 nsources = argc - 1;
7658 if (! sources)
7660 char sname[40];
7661 char *src;
7663 nsources = -1;
7664 do {
7665 nsources ++;
7666 sprintf(sname, "import::src%d", nsources);
7667 src = AttributeGet (PCB, sname);
7668 } while (src);
7670 if (nsources > 0)
7672 sources = (char **) malloc ((nsources + 1) * sizeof (char *));
7673 nsources = -1;
7674 do {
7675 nsources ++;
7676 sprintf(sname, "import::src%d", nsources);
7677 src = AttributeGet (PCB, sname);
7678 sources[nsources] = src;
7679 } while (src);
7683 if (! sources)
7685 /* Replace .pcb with .sch and hope for the best. */
7686 char *pcbname = PCB->Filename;
7687 char *schname;
7688 char *dot, *slash, *bslash;
7690 if (!pcbname)
7691 return hid_action("ImportGUI");
7693 schname = (char *) malloc (strlen(pcbname) + 5);
7694 strcpy (schname, pcbname);
7695 dot = strchr (schname, '.');
7696 slash = strchr (schname, '/');
7697 bslash = strchr (schname, '\\');
7698 if (dot && slash && dot < slash)
7699 dot = NULL;
7700 if (dot && bslash && dot < bslash)
7701 dot = NULL;
7702 if (dot)
7703 *dot = 0;
7704 strcat (schname, ".sch");
7706 if (access (schname, F_OK))
7708 free (schname);
7709 return hid_action("ImportGUI");
7712 sources = (char **) malloc (2 * sizeof (char *));
7713 sources[0] = schname;
7714 sources[1] = NULL;
7715 nsources = 1;
7718 if (strcasecmp (mode, "gnetlist") == 0)
7720 char *tmpfile = tempfile_name_new ("gnetlist_output");
7721 char **cmd;
7722 int i;
7724 if (tmpfile == NULL) {
7725 Message (_("Could not create temp file"));
7726 return 1;
7729 cmd = (char **) malloc ((7 + nsources) * sizeof (char *));
7730 cmd[0] = Settings.GnetlistProgram;
7731 cmd[1] = "-g";
7732 cmd[2] = "pcbfwd";
7733 cmd[3] = "-o";
7734 cmd[4] = tmpfile;
7735 cmd[5] = "--";
7736 for (i=0; i<nsources; i++)
7737 cmd[6+i] = sources[i];
7738 cmd[6+nsources] = NULL;
7740 #ifdef DEBUG
7741 printf("ActionImport: =========== About to run gnetlist ============\n");
7742 printf("%s %s %s %s %s %s %s ...\n",
7743 cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6]);
7744 #endif
7746 if (pcb_spawnvp (cmd))
7748 unlink (tmpfile);
7749 return 1;
7752 #ifdef DEBUG
7753 printf("ActionImport: =========== About to run ActionExecuteFile, file = %s ============\n", tmpfile);
7754 #endif
7756 cmd[0] = tmpfile;
7757 cmd[1] = NULL;
7758 ActionExecuteFile (1, cmd, 0, 0);
7760 free (cmd);
7761 tempfile_unlink (tmpfile);
7763 else if (strcasecmp (mode, "make") == 0)
7765 int must_free_tmpfile = 0;
7766 char *tmpfile;
7767 char *cmd[10];
7768 int i;
7769 char *srclist;
7770 int srclen;
7771 char *user_outfile = NULL;
7772 char *user_makefile = NULL;
7773 char *user_target = NULL;
7776 user_outfile = AttributeGet (PCB, "import::outfile");
7777 user_makefile = AttributeGet (PCB, "import::makefile");
7778 user_target = AttributeGet (PCB, "import::target");
7779 if (user_outfile && !user_target)
7780 user_target = user_outfile;
7782 if (user_outfile)
7783 tmpfile = user_outfile;
7784 else
7786 tmpfile = tempfile_name_new ("gnetlist_output");
7787 if (tmpfile == NULL) {
7788 Message (_("Could not create temp file"));
7789 return 1;
7791 must_free_tmpfile = 1;
7794 srclen = sizeof("SRCLIST=") + 2;
7795 for (i=0; i<nsources; i++)
7796 srclen += strlen (sources[i]) + 2;
7797 srclist = (char *) malloc (srclen);
7798 strcpy (srclist, "SRCLIST=");
7799 for (i=0; i<nsources; i++)
7801 if (i)
7802 strcat (srclist, " ");
7803 strcat (srclist, sources[i]);
7806 cmd[0] = Settings.MakeProgram;
7807 cmd[1] = "-s";
7808 cmd[2] = Concat ("PCB=", PCB->Filename, NULL);
7809 cmd[3] = srclist;
7810 cmd[4] = Concat ("OUT=", tmpfile, NULL);
7811 i = 5;
7812 if (user_makefile)
7814 cmd[i++] = "-f";
7815 cmd[i++] = user_makefile;
7817 cmd[i++] = user_target ? user_target : (char *)"pcb_import";
7818 cmd[i++] = NULL;
7820 if (pcb_spawnvp (cmd))
7822 if (must_free_tmpfile)
7823 unlink (tmpfile);
7824 free (cmd[2]);
7825 free (cmd[3]);
7826 free (cmd[4]);
7827 return 1;
7830 cmd[0] = tmpfile;
7831 cmd[1] = NULL;
7832 ActionExecuteFile (1, cmd, 0, 0);
7834 free (cmd[2]);
7835 free (cmd[3]);
7836 free (cmd[4]);
7837 if (must_free_tmpfile)
7838 tempfile_unlink (tmpfile);
7840 else
7842 Message (_("Unknown import mode: %s\n"), mode);
7843 return 1;
7846 DeleteRats (false);
7847 AddAllRats (false, NULL);
7849 #ifdef DEBUG
7850 printf("ActionImport: =========== Leaving ActionImport ============\n");
7851 #endif
7853 return 0;
7856 /* ------------------------------------------------------------ */
7858 static const char attributes_syntax[] =
7859 "Attributes(Layout|Layer|Element)\n"
7860 "Attributes(Layer,layername)";
7862 static const char attributes_help[] =
7863 "Let the user edit the attributes of the layout, current or given\n"
7864 "layer, or selected element.";
7866 /* %start-doc actions Attributes
7868 This just pops up a dialog letting the user edit the attributes of the
7869 pcb, an element, or a layer.
7871 %end-doc */
7874 static int
7875 ActionAttributes (int argc, char **argv, Coord x, Coord y)
7877 char *function = ARG (0);
7878 char *layername = ARG (1);
7879 char *buf;
7881 if (!function)
7882 AFAIL (attributes);
7884 if (!gui->edit_attributes)
7886 Message (_("This GUI doesn't support Attribute Editing\n"));
7887 return 1;
7890 switch (GetFunctionID (function))
7892 case F_Layout:
7894 gui->edit_attributes("Layout Attributes", &(PCB->Attributes));
7895 return 0;
7898 case F_Layer:
7900 LayerType *layer = CURRENT;
7901 if (layername)
7903 int i;
7904 layer = NULL;
7905 for (i=0; i<max_copper_layer; i++)
7906 if (strcmp (PCB->Data->Layer[i].Name, layername) == 0)
7908 layer = & (PCB->Data->Layer[i]);
7909 break;
7911 if (layer == NULL)
7913 Message (_("No layer named %s\n"), layername);
7914 return 1;
7917 buf = (char *) malloc (strlen (layer->Name) + strlen ("Layer X Attributes"));
7918 sprintf (buf, "Layer %s Attributes", layer->Name);
7919 gui->edit_attributes(buf, &(layer->Attributes));
7920 free (buf);
7921 return 0;
7924 case F_Element:
7926 int n_found = 0;
7927 ElementType *e = NULL;
7928 ELEMENT_LOOP (PCB->Data);
7930 if (TEST_FLAG (SELECTEDFLAG, element))
7932 e = element;
7933 n_found ++;
7936 END_LOOP;
7937 if (n_found > 1)
7939 Message (_("Too many elements selected\n"));
7940 return 1;
7942 if (n_found == 0)
7944 void *ptrtmp;
7945 gui->get_coords (_("Click on an element"), &x, &y);
7946 if ((SearchScreen
7947 (x, y, ELEMENT_TYPE, &ptrtmp,
7948 &ptrtmp, &ptrtmp)) != NO_TYPE)
7949 e = (ElementTypePtr) ptrtmp;
7950 else
7952 Message (_("No element found there\n"));
7953 return 1;
7957 if (NAMEONPCB_NAME(e))
7959 buf = (char *) malloc (strlen (NAMEONPCB_NAME(e)) + strlen ("Element X Attributes"));
7960 sprintf(buf, "Element %s Attributes", NAMEONPCB_NAME(e));
7962 else
7964 buf = strdup ("Unnamed Element Attributes");
7966 gui->edit_attributes(buf, &(e->Attributes));
7967 free (buf);
7968 break;
7971 default:
7972 AFAIL (attributes);
7975 return 0;
7978 /* --------------------------------------------------------------------------- */
7980 HID_Action action_action_list[] = {
7981 {"AddRats", 0, ActionAddRats,
7982 addrats_help, addrats_syntax}
7984 {"Attributes", 0, ActionAttributes,
7985 attributes_help, attributes_syntax}
7987 {"Atomic", 0, ActionAtomic,
7988 atomic_help, atomic_syntax}
7990 {"AutoPlaceSelected", 0, ActionAutoPlaceSelected,
7991 autoplace_help, autoplace_syntax}
7993 {"AutoRoute", 0, ActionAutoRoute,
7994 autoroute_help, autoroute_syntax}
7996 {"ChangeClearSize", 0, ActionChangeClearSize,
7997 changeclearsize_help, changeclearsize_syntax}
7999 {"ChangeDrillSize", 0, ActionChange2ndSize,
8000 changedrillsize_help, changedrillsize_syntax}
8002 {"ChangeHole", 0, ActionChangeHole,
8003 changehold_help, changehold_syntax}
8005 {"ChangeJoin", 0, ActionChangeJoin,
8006 changejoin_help, changejoin_syntax}
8008 {"ChangeName", 0, ActionChangeName,
8009 changename_help, changename_syntax}
8011 {"ChangePaste", 0, ActionChangePaste,
8012 changepaste_help, changepaste_syntax}
8014 {"ChangePinName", 0, ActionChangePinName,
8015 changepinname_help, changepinname_syntax}
8017 {"ChangeSize", 0, ActionChangeSize,
8018 changesize_help, changesize_syntax}
8020 {"ChangeSquare", 0, ActionChangeSquare,
8021 changesquare_help, changesquare_syntax}
8023 {"ChangeOctagon", 0, ActionChangeOctagon,
8024 changeoctagon_help, changeoctagon_syntax}
8026 {"ClearSquare", 0, ActionClearSquare,
8027 clearsquare_help, clearsquare_syntax}
8029 {"ClearOctagon", 0, ActionClearOctagon,
8030 clearoctagon_help, clearoctagon_syntax}
8032 {"Connection", 0, ActionConnection,
8033 connection_help, connection_syntax}
8035 {"Delete", 0, ActionDelete,
8036 delete_help, delete_syntax}
8038 {"DeleteRats", 0, ActionDeleteRats,
8039 deleterats_help, deleterats_syntax}
8041 {"DisperseElements", 0, ActionDisperseElements,
8042 disperseelements_help, disperseelements_syntax}
8044 {"Display", 0, ActionDisplay,
8045 display_help, display_syntax}
8047 {"DRC", 0, ActionDRCheck,
8048 drc_help, drc_syntax}
8050 {"DumpLibrary", 0, ActionDumpLibrary,
8051 dumplibrary_help, dumplibrary_syntax}
8053 {"ExecuteFile", 0, ActionExecuteFile,
8054 executefile_help, executefile_syntax}
8056 {"Flip", N_("Click on Object or Flip Point"), ActionFlip,
8057 flip_help, flip_syntax}
8059 {"LoadFrom", 0, ActionLoadFrom,
8060 loadfrom_help, loadfrom_syntax}
8062 {"MarkCrosshair", 0, ActionMarkCrosshair,
8063 markcrosshair_help, markcrosshair_syntax}
8065 {"Message", 0, ActionMessage,
8066 message_help, message_syntax}
8068 {"MinMaskGap", 0, ActionMinMaskGap,
8069 minmaskgap_help, minmaskgap_syntax}
8071 {"MinClearGap", 0, ActionMinClearGap,
8072 mincleargap_help, mincleargap_syntax}
8074 {"Mode", 0, ActionMode,
8075 mode_help, mode_syntax}
8077 {"MorphPolygon", 0, ActionMorphPolygon,
8078 morphpolygon_help, morphpolygon_syntax}
8080 {"PasteBuffer", 0, ActionPasteBuffer,
8081 pastebuffer_help, pastebuffer_syntax}
8083 {"Quit", 0, ActionQuit,
8084 quit_help, quit_syntax}
8086 {"RemoveSelected", 0, ActionRemoveSelected,
8087 removeselected_help, removeselected_syntax}
8089 {"Renumber", 0, ActionRenumber,
8090 renumber_help, renumber_syntax}
8092 {"RipUp", 0, ActionRipUp,
8093 ripup_help, ripup_syntax}
8095 {"Select", 0, ActionSelect,
8096 select_help, select_syntax}
8098 {"Unselect", 0, ActionUnselect,
8099 unselect_help, unselect_syntax}
8101 {"SaveSettings", 0, ActionSaveSettings,
8102 savesettings_help, savesettings_syntax}
8104 {"SaveTo", 0, ActionSaveTo,
8105 saveto_help, saveto_syntax}
8107 {"SetSquare", 0, ActionSetSquare,
8108 setsquare_help, setsquare_syntax}
8110 {"SetOctagon", 0, ActionSetOctagon,
8111 setoctagon_help, setoctagon_syntax}
8113 {"SetThermal", 0, ActionSetThermal,
8114 setthermal_help, setthermal_syntax}
8116 {"SetValue", 0, ActionSetValue,
8117 setvalue_help, setvalue_syntax}
8119 {"ToggleHideName", 0, ActionToggleHideName,
8120 togglehidename_help, togglehidename_syntax}
8122 {"Undo", 0, ActionUndo,
8123 undo_help, undo_syntax}
8125 {"Redo", 0, ActionRedo,
8126 redo_help, redo_syntax}
8128 {"SetSame", N_("Select item to use attributes from"), ActionSetSame,
8129 setsame_help, setsame_syntax}
8131 {"SetFlag", 0, ActionSetFlag,
8132 setflag_help, setflag_syntax}
8134 {"ClrFlag", 0, ActionClrFlag,
8135 clrflag_help, clrflag_syntax}
8137 {"ChangeFlag", 0, ActionChangeFlag,
8138 changeflag_help, changeflag_syntax}
8140 {"Polygon", 0, ActionPolygon,
8141 polygon_help, polygon_syntax}
8143 {"RouteStyle", 0, ActionRouteStyle,
8144 routestyle_help, routestyle_syntax}
8146 {"MoveObject", N_("Select an Object"), ActionMoveObject,
8147 moveobject_help, moveobject_syntax}
8149 {"MoveToCurrentLayer", 0, ActionMoveToCurrentLayer,
8150 movetocurrentlayer_help, movetocurrentlayer_syntax}
8152 {"New", 0, ActionNew,
8153 new_help, new_syntax}
8155 {"pscalib", 0, ActionPSCalib}
8157 {"ElementList", 0, ActionElementList,
8158 elementlist_help, elementlist_syntax}
8160 {"ElementSetAttr", 0, ActionElementSetAttr,
8161 elementsetattr_help, elementsetattr_syntax}
8163 {"ExecCommand", 0, ActionExecCommand,
8164 execcommand_help, execcommand_syntax}
8166 {"Import", 0, ActionImport,
8167 import_help, import_syntax}
8171 REGISTER_ACTIONS (action_action_list)