Re-ordered all references in the style selector dialog to have one consistent ordering.
[geda-pcb/whiteaudio.git] / src / action.c
blob1923cf975586bd761e84ffdd5fe5833761d7c90b
1 /* 15 Oct 2008 Ineiev: add CycleCrosshair action */
3 /*
4 * COPYRIGHT
6 * PCB, interactive printed circuit board design
7 * Copyright (C) 1994,1995,1996 Thomas Nau
8 * Copyright (C) 1997, 1998, 1999, 2000, 2001 Harry Eaton
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24 * Contact addresses for paper mail and Email:
25 * Harry Eaton, 6697 Buttonhole Ct, Columbia, MD 21044, USA
26 * haceaton@aplcomm.jhuapl.edu
30 /* action routines for output window
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
37 #include "global.h"
39 #include "action.h"
40 #include "autoplace.h"
41 #include "autoroute.h"
42 #include "buffer.h"
43 #include "change.h"
44 #include "command.h"
45 #include "copy.h"
46 #include "create.h"
47 #include "crosshair.h"
48 #include "data.h"
49 #include "draw.h"
50 #include "error.h"
51 #include "file.h"
52 #include "find.h"
53 #include "hid.h"
54 #include "insert.h"
55 #include "line.h"
56 #include "mymem.h"
57 #include "misc.h"
58 #include "mirror.h"
59 #include "move.h"
60 #include "polygon.h"
61 /*#include "print.h"*/
62 #include "rats.h"
63 #include "remove.h"
64 #include "report.h"
65 #include "rotate.h"
66 #include "rubberband.h"
67 #include "search.h"
68 #include "select.h"
69 #include "set.h"
70 #include "thermal.h"
71 #include "undo.h"
72 #include "rtree.h"
73 #include "macro.h"
74 #include "pcb-printf.h"
76 #include <assert.h>
77 #include <stdlib.h> /* rand() */
79 #ifdef HAVE_LIBDMALLOC
80 #include <dmalloc.h>
81 #endif
83 /* for fork() and friends */
84 #ifdef HAVE_UNISTD_H
85 #include <unistd.h>
86 #endif
88 #ifdef HAVE_SYS_WAIT_H
89 #include <sys/wait.h>
90 #endif
92 /* ---------------------------------------------------------------------------
93 * some local types
95 typedef enum
97 F_AddSelected,
98 F_All,
99 F_AllConnections,
100 F_AllRats,
101 F_AllUnusedPins,
102 F_Arc,
103 F_Arrow,
104 F_Block,
105 F_Description,
106 F_Cancel,
107 F_Center,
108 F_Clear,
109 F_ClearAndRedraw,
110 F_ClearList,
111 F_Close,
112 F_Connection,
113 F_Convert,
114 F_Copy,
115 F_CycleClip,
116 F_CycleCrosshair,
117 F_DeleteRats,
118 F_Drag,
119 F_DrillReport,
120 F_Element,
121 F_ElementByName,
122 F_ElementConnections,
123 F_ElementToBuffer,
124 F_Escape,
125 F_Find,
126 F_FlipElement,
127 F_FoundPins,
128 F_Grid,
129 F_InsertPoint,
130 F_Layer,
131 F_Layout,
132 F_LayoutAs,
133 F_LayoutToBuffer,
134 F_Line,
135 F_LineSize,
136 F_Lock,
137 F_Mirror,
138 F_Move,
139 F_NameOnPCB,
140 F_Netlist,
141 F_NetByName,
142 F_None,
143 F_Notify,
144 F_Object,
145 F_ObjectByName,
146 F_PasteBuffer,
147 F_PadByName,
148 F_PinByName,
149 F_PinOrPadName,
150 F_Pinout,
151 F_Polygon,
152 F_PolygonHole,
153 F_PreviousPoint,
154 F_RatsNest,
155 F_Rectangle,
156 F_Redraw,
157 F_Release,
158 F_Revert,
159 F_Remove,
160 F_RemoveSelected,
161 F_Report,
162 F_Reset,
163 F_ResetLinesAndPolygons,
164 F_ResetPinsViasAndPads,
165 F_Restore,
166 F_Rotate,
167 F_Save,
168 F_Selected,
169 F_SelectedArcs,
170 F_SelectedElements,
171 F_SelectedLines,
172 F_SelectedNames,
173 F_SelectedObjects,
174 F_SelectedPads,
175 F_SelectedPins,
176 F_SelectedTexts,
177 F_SelectedVias,
178 F_SelectedRats,
179 F_Stroke,
180 F_Text,
181 F_TextByName,
182 F_TextScale,
183 F_Thermal,
184 F_ToLayout,
185 F_ToggleAllDirections,
186 F_ToggleAutoDRC,
187 F_ToggleClearLine,
188 F_ToggleFullPoly,
189 F_ToggleGrid,
190 F_ToggleHideNames,
191 F_ToggleMask,
192 F_ToggleName,
193 F_ToggleObject,
194 F_ToggleShowDRC,
195 F_ToggleLiveRoute,
196 F_ToggleRubberBandMode,
197 F_ToggleStartDirection,
198 F_ToggleSnapPin,
199 F_ToggleThindraw,
200 F_ToggleLockNames,
201 F_ToggleOnlyNames,
202 F_ToggleThindrawPoly,
203 F_ToggleOrthoMove,
204 F_ToggleLocalRef,
205 F_ToggleCheckPlanes,
206 F_ToggleUniqueNames,
207 F_Via,
208 F_ViaByName,
209 F_Value,
210 F_ViaDrillingHole,
211 F_ViaSize,
212 F_Zoom
214 FunctionID;
216 typedef struct /* used to identify subfunctions */
218 char *Identifier;
219 FunctionID ID;
221 FunctionType, *FunctionTypePtr;
223 /* --------------------------------------------------------------------------- */
225 /* %start-doc actions 00delta
227 Many actions take a @code{delta} parameter as the last parameter,
228 which is an amount to change something. That @code{delta} may include
229 units, as an additional parameter, such as @code{Action(Object,5,mm)}.
230 If no units are specified, the default is PCB's native units
231 (currently 1/100 mil). Also, if the delta is prefixed by @code{+} or
232 @code{-}, the size is increased or decreased by that amount.
233 Otherwise, the size size is set to the given amount.
235 @example
236 Action(Object,5,mil)
237 Action(Object,+0.5,mm)
238 Action(Object,-1)
239 @end example
241 Actions which take a @code{delta} parameter which do not accept all
242 these options will specify what they do take.
244 %end-doc */
246 /* %start-doc actions 00objects
248 Many actions act on indicated objects on the board. They will have
249 parameters like @code{ToggleObject} or @code{SelectedVias} to indicate
250 what group of objects they act on. Unless otherwise specified, these
251 parameters are defined as follows:
253 @table @code
255 @item Object
256 @itemx ToggleObject
257 Affects the object under the mouse pointer. If this action is invoked
258 from a menu or script, the user will be prompted to click on an
259 object, which is then the object affected.
261 @item Selected
262 @itemx SelectedObjects
264 Affects all objects which are currently selected. At least, all
265 selected objects for which the given action makes sense.
267 @item SelectedPins
268 @itemx SelectedVias
269 @itemx Selected@var{Type}
270 @itemx @i{etc}
271 Affects all objects which are both selected and of the @var{Type} specified.
273 @end table
275 %end-doc */
277 /* %start-doc actions 00macros
279 @macro pinshapes
281 Pins, pads, and vias can have various shapes. All may be round. Pins
282 and pads may be square (obviously "square" pads are usually
283 rectangular). Pins and vias may be octagonal. When you change a
284 shape flag of an element, you actually change all of its pins and
285 pads.
287 Note that the square flag takes precedence over the octagon flag,
288 thus, if both the square and octagon flags are set, the object is
289 square. When the square flag is cleared, the pins and pads will be
290 either round or, if the octagon flag is set, octagonal.
292 @end macro
294 %end-doc */
296 /* ---------------------------------------------------------------------------
297 * some local identifiers
299 static PointType InsertedPoint;
300 static LayerTypePtr lastLayer;
301 static struct
303 PolygonTypePtr poly;
304 LineType line;
306 fake;
308 static struct
310 Coord X, Y;
311 Cardinal Buffer;
312 bool Click;
313 bool Moving; /* selected type clicked on */
314 int Hit; /* move type clicked on */
315 void *ptr1;
316 void *ptr2;
317 void *ptr3;
319 Note;
321 static int defer_updates = 0;
322 static int defer_needs_update = 0;
324 static Cardinal polyIndex = 0;
325 static bool saved_mode = false;
326 #ifdef HAVE_LIBSTROKE
327 static bool mid_stroke = false;
328 static BoxType StrokeBox;
329 #endif
330 static FunctionType Functions[] = {
331 {"AddSelected", F_AddSelected},
332 {"All", F_All},
333 {"AllConnections", F_AllConnections},
334 {"AllRats", F_AllRats},
335 {"AllUnusedPins", F_AllUnusedPins},
336 {"Arc", F_Arc},
337 {"Arrow", F_Arrow},
338 {"Block", F_Block},
339 {"Description", F_Description},
340 {"Cancel", F_Cancel},
341 {"Center", F_Center},
342 {"Clear", F_Clear},
343 {"ClearAndRedraw", F_ClearAndRedraw},
344 {"ClearList", F_ClearList},
345 {"Close", F_Close},
346 {"Connection", F_Connection},
347 {"Convert", F_Convert},
348 {"Copy", F_Copy},
349 {"CycleClip", F_CycleClip},
350 {"CycleCrosshair", F_CycleCrosshair},
351 {"DeleteRats", F_DeleteRats},
352 {"Drag", F_Drag},
353 {"DrillReport", F_DrillReport},
354 {"Element", F_Element},
355 {"ElementByName", F_ElementByName},
356 {"ElementConnections", F_ElementConnections},
357 {"ElementToBuffer", F_ElementToBuffer},
358 {"Escape", F_Escape},
359 {"Find", F_Find},
360 {"FlipElement", F_FlipElement},
361 {"FoundPins", F_FoundPins},
362 {"Grid", F_Grid},
363 {"InsertPoint", F_InsertPoint},
364 {"Layer", F_Layer},
365 {"Layout", F_Layout},
366 {"LayoutAs", F_LayoutAs},
367 {"LayoutToBuffer", F_LayoutToBuffer},
368 {"Line", F_Line},
369 {"LineSize", F_LineSize},
370 {"Lock", F_Lock},
371 {"Mirror", F_Mirror},
372 {"Move", F_Move},
373 {"NameOnPCB", F_NameOnPCB},
374 {"Netlist", F_Netlist},
375 {"NetByName", F_NetByName},
376 {"None", F_None},
377 {"Notify", F_Notify},
378 {"Object", F_Object},
379 {"ObjectByName", F_ObjectByName},
380 {"PasteBuffer", F_PasteBuffer},
381 {"PadByName", F_PadByName},
382 {"PinByName", F_PinByName},
383 {"PinOrPadName", F_PinOrPadName},
384 {"Pinout", F_Pinout},
385 {"Polygon", F_Polygon},
386 {"PolygonHole", F_PolygonHole},
387 {"PreviousPoint", F_PreviousPoint},
388 {"RatsNest", F_RatsNest},
389 {"Rectangle", F_Rectangle},
390 {"Redraw", F_Redraw},
391 {"Release", F_Release},
392 {"Remove", F_Remove},
393 {"RemoveSelected", F_RemoveSelected},
394 {"Report", F_Report},
395 {"Reset", F_Reset},
396 {"ResetLinesAndPolygons", F_ResetLinesAndPolygons},
397 {"ResetPinsViasAndPads", F_ResetPinsViasAndPads},
398 {"Restore", F_Restore},
399 {"Revert", F_Revert},
400 {"Rotate", F_Rotate},
401 {"Save", F_Save},
402 {"Selected", F_Selected},
403 {"SelectedArcs", F_SelectedArcs},
404 {"SelectedElements", F_SelectedElements},
405 {"SelectedLines", F_SelectedLines},
406 {"SelectedNames", F_SelectedNames},
407 {"SelectedObjects", F_SelectedObjects},
408 {"SelectedPins", F_SelectedPins},
409 {"SelectedPads", F_SelectedPads},
410 {"SelectedRats", F_SelectedRats},
411 {"SelectedTexts", F_SelectedTexts},
412 {"SelectedVias", F_SelectedVias},
413 {"Stroke", F_Stroke},
414 {"Text", F_Text},
415 {"TextByName", F_TextByName},
416 {"TextScale", F_TextScale},
417 {"Thermal", F_Thermal},
418 {"ToLayout", F_ToLayout},
419 {"Toggle45Degree", F_ToggleAllDirections},
420 {"ToggleClearLine", F_ToggleClearLine},
421 {"ToggleFullPoly", F_ToggleFullPoly},
422 {"ToggleGrid", F_ToggleGrid},
423 {"ToggleMask", F_ToggleMask},
424 {"ToggleName", F_ToggleName},
425 {"ToggleObject", F_ToggleObject},
426 {"ToggleRubberBandMode", F_ToggleRubberBandMode},
427 {"ToggleStartDirection", F_ToggleStartDirection},
428 {"ToggleSnapPin", F_ToggleSnapPin},
429 {"ToggleThindraw", F_ToggleThindraw},
430 {"ToggleThindrawPoly", F_ToggleThindrawPoly},
431 {"ToggleLockNames", F_ToggleLockNames},
432 {"ToggleOnlyNames", F_ToggleOnlyNames},
433 {"ToggleHideNames", F_ToggleHideNames},
434 {"ToggleCheckPlanes", F_ToggleCheckPlanes},
435 {"ToggleLocalRef", F_ToggleLocalRef},
436 {"ToggleOrthoMove", F_ToggleOrthoMove},
437 {"ToggleShowDRC", F_ToggleShowDRC},
438 {"ToggleLiveRoute", F_ToggleLiveRoute},
439 {"ToggleAutoDRC", F_ToggleAutoDRC},
440 {"ToggleUniqueNames", F_ToggleUniqueNames},
441 {"Value", F_Value},
442 {"Via", F_Via},
443 {"ViaByName", F_ViaByName},
444 {"ViaSize", F_ViaSize},
445 {"ViaDrillingHole", F_ViaDrillingHole},
446 {"Zoom", F_Zoom}
449 /* ---------------------------------------------------------------------------
450 * some local routines
452 static int GetFunctionID (String);
453 static void AdjustAttachedBox (void);
454 static void NotifyLine (void);
455 static void NotifyBlock (void);
456 static void NotifyMode (void);
457 static void ClearWarnings (void);
458 #ifdef HAVE_LIBSTROKE
459 static void FinishStroke (void);
460 extern void stroke_init (void);
461 extern void stroke_record (int x, int y);
462 extern int stroke_trans (char *s);
463 #endif
464 static void ChangeFlag (char *, char *, int, char *);
466 #define ARG(n) (argc > (n) ? argv[n] : NULL)
468 #ifdef HAVE_LIBSTROKE
470 /* ---------------------------------------------------------------------------
471 * FinishStroke - try to recognize the stroke sent
473 void
474 FinishStroke (void)
476 char msg[255];
477 int type;
478 unsigned long num;
479 void *ptr1, *ptr2, *ptr3;
481 mid_stroke = false;
482 if (stroke_trans (msg))
484 num = atoi (msg);
485 switch (num)
487 case 456:
488 if (Settings.Mode == LINE_MODE)
490 SetMode (LINE_MODE);
492 break;
493 case 9874123:
494 case 74123:
495 case 987412:
496 case 8741236:
497 case 874123:
498 RotateScreenObject (StrokeBox.X1, StrokeBox.Y1, SWAP_IDENT ? 1 : 3);
499 break;
500 case 7896321:
501 case 786321:
502 case 789632:
503 case 896321:
504 RotateScreenObject (StrokeBox.X1, StrokeBox.Y1, SWAP_IDENT ? 3 : 1);
505 break;
506 case 258:
507 SetMode (LINE_MODE);
508 break;
509 case 852:
510 SetMode (ARROW_MODE);
511 break;
512 case 1478963:
513 ActionUndo ("");
514 break;
515 case 147423:
516 case 147523:
517 case 1474123:
518 Redo (true);
519 break;
520 case 148963:
521 case 147863:
522 case 147853:
523 case 145863:
524 SetMode (VIA_MODE);
525 break;
526 case 951:
527 case 9651:
528 case 9521:
529 case 9621:
530 case 9851:
531 case 9541:
532 case 96521:
533 case 96541:
534 case 98541:
535 SetZoom (1000); /* special zoom extents */
536 break;
537 case 159:
538 case 1269:
539 case 1259:
540 case 1459:
541 case 1569:
542 case 1589:
543 case 12569:
544 case 12589:
545 case 14589:
547 Coord x = (StrokeBox.X1 + StrokeBox.X2) / 2;
548 Coord y = (StrokeBox.Y1 + StrokeBox.Y2) / 2;
549 double z;
550 /* XXX: PCB->MaxWidth and PCB->MaxHeight may be the wrong
551 * divisors below. The old code WAS broken, but this
552 * replacement has not been tested for correctness.
556 log (fabs (StrokeBox.X2 - StrokeBox.X1) / PCB->MaxWidth) /
557 log (2.0);
559 MAX (z,
561 log (fabs (StrokeBox.Y2 - StrokeBox.Y1) / PCB->MaxHeight) /
562 log (2.0));
563 SetZoom (z);
565 CenterDisplay (x, y);
566 break;
569 default:
570 Message (_("Unknown stroke %s\n"), msg);
571 break;
574 else
575 gui->beep ();
577 #endif
579 /* ---------------------------------------------------------------------------
580 * Clear warning color from pins/pads
582 static void
583 ClearWarnings ()
585 Settings.RatWarn = false;
586 ALLPIN_LOOP (PCB->Data);
588 if (TEST_FLAG (WARNFLAG, pin))
590 CLEAR_FLAG (WARNFLAG, pin);
591 DrawPin (pin);
594 ENDALL_LOOP;
595 ALLPAD_LOOP (PCB->Data);
597 if (TEST_FLAG (WARNFLAG, pad))
599 CLEAR_FLAG (WARNFLAG, pad);
600 DrawPad (pad);
603 ENDALL_LOOP;
604 Draw ();
607 static void
608 click_cb (hidval hv)
610 if (Note.Click)
612 notify_crosshair_change (false);
613 Note.Click = false;
614 if (Note.Moving && !gui->shift_is_pressed ())
616 Note.Buffer = Settings.BufferNumber;
617 SetBufferNumber (MAX_BUFFER - 1);
618 ClearBuffer (PASTEBUFFER);
619 AddSelectedToBuffer (PASTEBUFFER, Note.X, Note.Y, true);
620 SaveUndoSerialNumber ();
621 RemoveSelected ();
622 SaveMode ();
623 saved_mode = true;
624 SetMode (PASTEBUFFER_MODE);
626 else if (Note.Hit && !gui->shift_is_pressed ())
628 SaveMode ();
629 saved_mode = true;
630 SetMode (gui->control_is_pressed ()? COPY_MODE : MOVE_MODE);
631 Crosshair.AttachedObject.Ptr1 = Note.ptr1;
632 Crosshair.AttachedObject.Ptr2 = Note.ptr2;
633 Crosshair.AttachedObject.Ptr3 = Note.ptr3;
634 Crosshair.AttachedObject.Type = Note.Hit;
635 AttachForCopy (Note.X, Note.Y);
637 else
639 BoxType box;
641 Note.Hit = 0;
642 Note.Moving = false;
643 SaveUndoSerialNumber ();
644 box.X1 = -MAX_COORD;
645 box.Y1 = -MAX_COORD;
646 box.X2 = MAX_COORD;
647 box.Y2 = MAX_COORD;
648 /* unselect first if shift key not down */
649 if (!gui->shift_is_pressed () && SelectBlock (&box, false))
650 SetChangedFlag (true);
651 NotifyBlock ();
652 Crosshair.AttachedBox.Point1.X = Note.X;
653 Crosshair.AttachedBox.Point1.Y = Note.Y;
655 notify_crosshair_change (true);
659 static void
660 ReleaseMode (void)
662 BoxType box;
664 if (Note.Click)
666 BoxType box;
668 box.X1 = -MAX_COORD;
669 box.Y1 = -MAX_COORD;
670 box.X2 = MAX_COORD;
671 box.Y2 = MAX_COORD;
673 Note.Click = false; /* inhibit timer action */
674 SaveUndoSerialNumber ();
675 /* unselect first if shift key not down */
676 if (!gui->shift_is_pressed ())
678 if (SelectBlock (&box, false))
679 SetChangedFlag (true);
680 if (Note.Moving)
682 Note.Moving = 0;
683 Note.Hit = 0;
684 return;
687 RestoreUndoSerialNumber ();
688 if (SelectObject ())
689 SetChangedFlag (true);
690 Note.Hit = 0;
691 Note.Moving = 0;
693 else if (Note.Moving)
695 RestoreUndoSerialNumber ();
696 NotifyMode ();
697 ClearBuffer (PASTEBUFFER);
698 SetBufferNumber (Note.Buffer);
699 Note.Moving = false;
700 Note.Hit = 0;
702 else if (Note.Hit)
704 NotifyMode ();
705 Note.Hit = 0;
707 else if (Settings.Mode == ARROW_MODE)
709 box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
710 Crosshair.AttachedBox.Point2.X);
711 box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
712 Crosshair.AttachedBox.Point2.Y);
713 box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
714 Crosshair.AttachedBox.Point2.X);
715 box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
716 Crosshair.AttachedBox.Point2.Y);
717 RestoreUndoSerialNumber ();
718 if (SelectBlock (&box, true))
719 SetChangedFlag (true);
720 else if (Bumped)
721 IncrementUndoSerialNumber ();
722 Crosshair.AttachedBox.State = STATE_FIRST;
724 if (saved_mode)
725 RestoreMode ();
726 saved_mode = false;
729 /* ---------------------------------------------------------------------------
730 * get function ID of passed string
732 #define HSIZE 257
733 static char function_hash[HSIZE];
734 static int hash_initted = 0;
736 static int
737 hashfunc(String s)
739 int i = 0;
740 while (*s)
742 i ^= i >> 16;
743 i = (i * 13) ^ (unsigned char)tolower((int) *s);
744 s ++;
746 i = (unsigned int)i % HSIZE;
747 return i;
750 static int
751 GetFunctionID (String Ident)
753 int i, h;
755 if (Ident == 0)
756 return -1;
758 if (!hash_initted)
760 hash_initted = 1;
761 if (HSIZE < ENTRIES (Functions) * 2)
763 fprintf(stderr, _("Error: function hash size too small (%d vs %lu at %s:%d)\n"),
764 HSIZE, (unsigned long) ENTRIES (Functions)*2, __FILE__, __LINE__);
765 exit(1);
767 if (ENTRIES (Functions) > 254)
769 /* Change 'char' to 'int' and remove this when we get to 256
770 strings to hash. */
771 fprintf(stderr, _("Error: function hash type too small (%d vs %lu at %s:%d)\n"),
772 256, (unsigned long) ENTRIES (Functions), __FILE__, __LINE__);
773 exit(1);
776 for (i=ENTRIES (Functions)-1; i>=0; i--)
778 h = hashfunc (Functions[i].Identifier);
779 while (function_hash[h])
780 h = (h + 1) % HSIZE;
781 function_hash[h] = i + 1;
785 i = hashfunc (Ident);
786 while (1)
788 /* We enforce the "hash table bigger than function table" rule,
789 so we know there will be at least one zero entry to find. */
790 if (!function_hash[i])
791 return (-1);
792 if (!strcasecmp (Ident, Functions[function_hash[i]-1].Identifier))
793 return ((int) Functions[function_hash[i]-1].ID);
794 i = (i + 1) % HSIZE;
798 /* ---------------------------------------------------------------------------
799 * set new coordinates if in 'RECTANGLE' mode
800 * the cursor shape is also adjusted
802 static void
803 AdjustAttachedBox (void)
805 if (Settings.Mode == ARC_MODE)
807 Crosshair.AttachedBox.otherway = gui->shift_is_pressed ();
808 return;
810 switch (Crosshair.AttachedBox.State)
812 case STATE_SECOND: /* one corner is selected */
814 /* update coordinates */
815 Crosshair.AttachedBox.Point2.X = Crosshair.X;
816 Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
817 break;
822 /* ---------------------------------------------------------------------------
823 * adjusts the objects which are to be created like attached lines...
825 void
826 AdjustAttachedObjects (void)
828 PointTypePtr pnt;
829 switch (Settings.Mode)
831 /* update at least an attached block (selection) */
832 case NO_MODE:
833 case ARROW_MODE:
834 if (Crosshair.AttachedBox.State)
836 Crosshair.AttachedBox.Point2.X = Crosshair.X;
837 Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
839 break;
841 /* rectangle creation mode */
842 case RECTANGLE_MODE:
843 case ARC_MODE:
844 AdjustAttachedBox ();
845 break;
847 /* polygon creation mode */
848 case POLYGON_MODE:
849 case POLYGONHOLE_MODE:
850 AdjustAttachedLine ();
851 break;
852 /* line creation mode */
853 case LINE_MODE:
854 if (PCB->RatDraw || PCB->Clipping == 0)
855 AdjustAttachedLine ();
856 else
857 AdjustTwoLine (PCB->Clipping - 1);
858 break;
859 /* point insertion mode */
860 case INSERTPOINT_MODE:
861 pnt = AdjustInsertPoint ();
862 if (pnt)
863 InsertedPoint = *pnt;
864 break;
865 case ROTATE_MODE:
866 break;
870 /* ---------------------------------------------------------------------------
871 * creates points of a line
873 static void
874 NotifyLine (void)
876 int type = NO_TYPE;
877 void *ptr1, *ptr2, *ptr3;
879 if (!Marked.status || TEST_FLAG (LOCALREFFLAG, PCB))
880 SetLocalRef (Crosshair.X, Crosshair.Y, true);
881 switch (Crosshair.AttachedLine.State)
883 case STATE_FIRST: /* first point */
884 if (PCB->RatDraw && SearchScreen (Crosshair.X, Crosshair.Y,
885 PAD_TYPE | PIN_TYPE, &ptr1, &ptr1,
886 &ptr1) == NO_TYPE)
888 gui->beep ();
889 break;
891 if (TEST_FLAG (AUTODRCFLAG, PCB) && Settings.Mode == LINE_MODE)
893 type = SearchScreen (Crosshair.X, Crosshair.Y,
894 PIN_TYPE | PAD_TYPE | VIA_TYPE, &ptr1, &ptr2,
895 &ptr3);
896 LookupConnection (Crosshair.X, Crosshair.Y, true, 1,
897 FOUNDFLAG);
899 if (type == PIN_TYPE || type == VIA_TYPE)
901 Crosshair.AttachedLine.Point1.X =
902 Crosshair.AttachedLine.Point2.X = ((PinTypePtr) ptr2)->X;
903 Crosshair.AttachedLine.Point1.Y =
904 Crosshair.AttachedLine.Point2.Y = ((PinTypePtr) ptr2)->Y;
906 else if (type == PAD_TYPE)
908 PadTypePtr pad = (PadTypePtr) ptr2;
909 double d1 = Distance (Crosshair.X, Crosshair.Y, pad->Point1.X, pad->Point1.Y);
910 double d2 = Distance (Crosshair.X, Crosshair.Y, pad->Point2.X, pad->Point2.Y);
911 if (d2 < d1)
913 Crosshair.AttachedLine.Point1 =
914 Crosshair.AttachedLine.Point2 = pad->Point2;
916 else
918 Crosshair.AttachedLine.Point1 =
919 Crosshair.AttachedLine.Point2 = pad->Point1;
922 else
924 Crosshair.AttachedLine.Point1.X =
925 Crosshair.AttachedLine.Point2.X = Crosshair.X;
926 Crosshair.AttachedLine.Point1.Y =
927 Crosshair.AttachedLine.Point2.Y = Crosshair.Y;
929 Crosshair.AttachedLine.State = STATE_SECOND;
930 break;
932 case STATE_SECOND:
933 /* fall through to third state too */
934 lastLayer = CURRENT;
935 default: /* all following points */
936 Crosshair.AttachedLine.State = STATE_THIRD;
937 break;
941 /* ---------------------------------------------------------------------------
942 * create first or second corner of a marked block
944 static void
945 NotifyBlock (void)
947 notify_crosshair_change (false);
948 switch (Crosshair.AttachedBox.State)
950 case STATE_FIRST: /* setup first point */
951 Crosshair.AttachedBox.Point1.X =
952 Crosshair.AttachedBox.Point2.X = Crosshair.X;
953 Crosshair.AttachedBox.Point1.Y =
954 Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
955 Crosshair.AttachedBox.State = STATE_SECOND;
956 break;
958 case STATE_SECOND: /* setup second point */
959 Crosshair.AttachedBox.State = STATE_THIRD;
960 break;
962 notify_crosshair_change (true);
966 /* ---------------------------------------------------------------------------
968 * does what's appropriate for the current mode setting. This normally
969 * means creation of an object at the current crosshair location.
971 * new created objects are added to the create undo list of course
973 static void
974 NotifyMode (void)
976 void *ptr1, *ptr2, *ptr3;
977 int type;
979 if (Settings.RatWarn)
980 ClearWarnings ();
981 switch (Settings.Mode)
983 case ARROW_MODE:
985 int test;
986 hidval hv;
988 Note.Click = true;
989 /* do something after click time */
990 gui->add_timer (click_cb, CLICK_TIME, hv);
992 /* see if we clicked on something already selected
993 * (Note.Moving) or clicked on a MOVE_TYPE
994 * (Note.Hit)
996 for (test = (SELECT_TYPES | MOVE_TYPES) & ~RATLINE_TYPE;
997 test; test &= ~type)
999 type = SearchScreen (Note.X, Note.Y, test, &ptr1, &ptr2, &ptr3);
1000 if (!Note.Hit && (type & MOVE_TYPES) &&
1001 !TEST_FLAG (LOCKFLAG, (PinTypePtr) ptr2))
1003 Note.Hit = type;
1004 Note.ptr1 = ptr1;
1005 Note.ptr2 = ptr2;
1006 Note.ptr3 = ptr3;
1008 if (!Note.Moving && (type & SELECT_TYPES) &&
1009 TEST_FLAG (SELECTEDFLAG, (PinTypePtr) ptr2))
1010 Note.Moving = true;
1011 if ((Note.Hit && Note.Moving) || type == NO_TYPE)
1012 break;
1014 break;
1017 case VIA_MODE:
1019 PinTypePtr via;
1021 if (!PCB->ViaOn)
1023 Message (_("You must turn via visibility on before\n"
1024 "you can place vias\n"));
1025 break;
1027 if ((via = CreateNewVia (PCB->Data, Note.X, Note.Y,
1028 Settings.ViaThickness, 2 * Settings.Keepaway,
1029 0, Settings.ViaDrillingHole, NULL,
1030 NoFlags ())) != NULL)
1032 AddObjectToCreateUndoList (VIA_TYPE, via, via, via);
1033 if (gui->shift_is_pressed ())
1034 ChangeObjectThermal (VIA_TYPE, via, via, via, PCB->ThermStyle);
1035 IncrementUndoSerialNumber ();
1036 DrawVia (via);
1037 Draw ();
1039 break;
1042 case ARC_MODE:
1044 switch (Crosshair.AttachedBox.State)
1046 case STATE_FIRST:
1047 Crosshair.AttachedBox.Point1.X =
1048 Crosshair.AttachedBox.Point2.X = Note.X;
1049 Crosshair.AttachedBox.Point1.Y =
1050 Crosshair.AttachedBox.Point2.Y = Note.Y;
1051 Crosshair.AttachedBox.State = STATE_SECOND;
1052 break;
1054 case STATE_SECOND:
1055 case STATE_THIRD:
1057 ArcTypePtr arc;
1058 Coord wx, wy;
1059 Angle sa, dir;
1061 wx = Note.X - Crosshair.AttachedBox.Point1.X;
1062 wy = Note.Y - Crosshair.AttachedBox.Point1.Y;
1063 if (XOR (Crosshair.AttachedBox.otherway, abs (wy) > abs (wx)))
1065 Crosshair.AttachedBox.Point2.X =
1066 Crosshair.AttachedBox.Point1.X + abs (wy) * SGNZ (wx);
1067 sa = (wx >= 0) ? 0 : 180;
1068 #ifdef ARC45
1069 if (abs (wy) / 2 >= abs (wx))
1070 dir = (SGNZ (wx) == SGNZ (wy)) ? 45 : -45;
1071 else
1072 #endif
1073 dir = (SGNZ (wx) == SGNZ (wy)) ? 90 : -90;
1075 else
1077 Crosshair.AttachedBox.Point2.Y =
1078 Crosshair.AttachedBox.Point1.Y + abs (wx) * SGNZ (wy);
1079 sa = (wy >= 0) ? -90 : 90;
1080 #ifdef ARC45
1081 if (abs (wx) / 2 >= abs (wy))
1082 dir = (SGNZ (wx) == SGNZ (wy)) ? -45 : 45;
1083 else
1084 #endif
1085 dir = (SGNZ (wx) == SGNZ (wy)) ? -90 : 90;
1086 wy = wx;
1088 if (abs (wy) > 0 && (arc = CreateNewArcOnLayer (CURRENT,
1089 Crosshair.
1090 AttachedBox.
1091 Point2.X,
1092 Crosshair.
1093 AttachedBox.
1094 Point2.Y,
1095 abs (wy),
1096 abs (wy),
1098 dir,
1099 Settings.
1100 LineThickness,
1101 2 * Settings.
1102 Keepaway,
1103 MakeFlags
1104 (TEST_FLAG
1105 (CLEARNEWFLAG,
1106 PCB) ?
1107 CLEARLINEFLAG :
1108 0))))
1110 BoxTypePtr bx;
1112 bx = GetArcEnds (arc);
1113 Crosshair.AttachedBox.Point1.X =
1114 Crosshair.AttachedBox.Point2.X = bx->X2;
1115 Crosshair.AttachedBox.Point1.Y =
1116 Crosshair.AttachedBox.Point2.Y = bx->Y2;
1117 AddObjectToCreateUndoList (ARC_TYPE, CURRENT, arc, arc);
1118 IncrementUndoSerialNumber ();
1119 addedLines++;
1120 DrawArc (CURRENT, arc);
1121 Draw ();
1122 Crosshair.AttachedBox.State = STATE_THIRD;
1124 break;
1127 break;
1129 case LOCK_MODE:
1131 type = SearchScreen (Note.X, Note.Y, LOCK_TYPES, &ptr1, &ptr2, &ptr3);
1132 if (type == ELEMENT_TYPE)
1134 ElementTypePtr element = (ElementTypePtr) ptr2;
1136 TOGGLE_FLAG (LOCKFLAG, element);
1137 PIN_LOOP (element);
1139 TOGGLE_FLAG (LOCKFLAG, pin);
1140 CLEAR_FLAG (SELECTEDFLAG, pin);
1142 END_LOOP;
1143 PAD_LOOP (element);
1145 TOGGLE_FLAG (LOCKFLAG, pad);
1146 CLEAR_FLAG (SELECTEDFLAG, pad);
1148 END_LOOP;
1149 CLEAR_FLAG (SELECTEDFLAG, element);
1150 /* always re-draw it since I'm too lazy
1151 * to tell if a selected flag changed
1153 DrawElement (element);
1154 Draw ();
1155 hid_actionl ("Report", "Object", NULL);
1157 else if (type != NO_TYPE)
1159 TextTypePtr thing = (TextTypePtr) ptr3;
1160 TOGGLE_FLAG (LOCKFLAG, thing);
1161 if (TEST_FLAG (LOCKFLAG, thing)
1162 && TEST_FLAG (SELECTEDFLAG, thing))
1164 /* this is not un-doable since LOCK isn't */
1165 CLEAR_FLAG (SELECTEDFLAG, thing);
1166 DrawObject (type, ptr1, ptr2);
1167 Draw ();
1169 hid_actionl ("Report", "Object", NULL);
1171 break;
1173 case THERMAL_MODE:
1175 if (((type
1177 SearchScreen (Note.X, Note.Y, PIN_TYPES, &ptr1, &ptr2,
1178 &ptr3)) != NO_TYPE)
1179 && !TEST_FLAG (HOLEFLAG, (PinTypePtr) ptr3))
1181 if (gui->shift_is_pressed ())
1183 int tstyle = GET_THERM (INDEXOFCURRENT, (PinTypePtr) ptr3);
1184 tstyle++;
1185 if (tstyle > 5)
1186 tstyle = 1;
1187 ChangeObjectThermal (type, ptr1, ptr2, ptr3, tstyle);
1189 else if (GET_THERM (INDEXOFCURRENT, (PinTypePtr) ptr3))
1190 ChangeObjectThermal (type, ptr1, ptr2, ptr3, 0);
1191 else
1192 ChangeObjectThermal (type, ptr1, ptr2, ptr3, PCB->ThermStyle);
1194 break;
1197 case LINE_MODE:
1198 /* do update of position */
1199 NotifyLine ();
1200 if (Crosshair.AttachedLine.State != STATE_THIRD)
1201 break;
1203 /* Remove anchor if clicking on start point;
1204 * this means we can't paint 0 length lines
1205 * which could be used for square SMD pads.
1206 * Instead use a very small delta, or change
1207 * the file after saving.
1209 if (Crosshair.X == Crosshair.AttachedLine.Point1.X
1210 && Crosshair.Y == Crosshair.AttachedLine.Point1.Y)
1212 SetMode (LINE_MODE);
1213 break;
1216 if (PCB->RatDraw)
1218 RatTypePtr line;
1219 if ((line = AddNet ()))
1221 addedLines++;
1222 AddObjectToCreateUndoList (RATLINE_TYPE, line, line, line);
1223 IncrementUndoSerialNumber ();
1224 DrawRat (line);
1225 Crosshair.AttachedLine.Point1.X =
1226 Crosshair.AttachedLine.Point2.X;
1227 Crosshair.AttachedLine.Point1.Y =
1228 Crosshair.AttachedLine.Point2.Y;
1229 Draw ();
1231 break;
1233 else
1234 /* create line if both ends are determined && length != 0 */
1236 LineTypePtr line;
1237 int maybe_found_flag;
1239 if (PCB->Clipping
1240 && Crosshair.AttachedLine.Point1.X ==
1241 Crosshair.AttachedLine.Point2.X
1242 && Crosshair.AttachedLine.Point1.Y ==
1243 Crosshair.AttachedLine.Point2.Y
1244 && (Crosshair.AttachedLine.Point2.X != Note.X
1245 || Crosshair.AttachedLine.Point2.Y != Note.Y))
1247 /* We will only need to paint the second line segment.
1248 Since we only check for vias on the first segment,
1249 swap them so the non-empty segment is the first segment. */
1250 Crosshair.AttachedLine.Point2.X = Note.X;
1251 Crosshair.AttachedLine.Point2.Y = Note.Y;
1254 if (TEST_FLAG (AUTODRCFLAG, PCB)
1255 && ! TEST_SILK_LAYER (CURRENT))
1256 maybe_found_flag = FOUNDFLAG;
1257 else
1258 maybe_found_flag = 0;
1260 if ((Crosshair.AttachedLine.Point1.X !=
1261 Crosshair.AttachedLine.Point2.X
1262 || Crosshair.AttachedLine.Point1.Y !=
1263 Crosshair.AttachedLine.Point2.Y)
1264 && (line =
1265 CreateDrawnLineOnLayer (CURRENT,
1266 Crosshair.AttachedLine.Point1.X,
1267 Crosshair.AttachedLine.Point1.Y,
1268 Crosshair.AttachedLine.Point2.X,
1269 Crosshair.AttachedLine.Point2.Y,
1270 Settings.LineThickness,
1271 2 * Settings.Keepaway,
1272 MakeFlags (maybe_found_flag |
1273 (TEST_FLAG
1274 (CLEARNEWFLAG,
1275 PCB) ? CLEARLINEFLAG :
1276 0)))) != NULL)
1278 PinTypePtr via;
1280 addedLines++;
1281 AddObjectToCreateUndoList (LINE_TYPE, CURRENT, line, line);
1282 DrawLine (CURRENT, line);
1283 /* place a via if vias are visible, the layer is
1284 in a new group since the last line and there
1285 isn't a pin already here */
1286 if (PCB->ViaOn && GetLayerGroupNumberByPointer (CURRENT) !=
1287 GetLayerGroupNumberByPointer (lastLayer) &&
1288 SearchObjectByLocation (PIN_TYPES, &ptr1, &ptr2, &ptr3,
1289 Crosshair.AttachedLine.Point1.X,
1290 Crosshair.AttachedLine.Point1.Y,
1291 Settings.ViaThickness / 2) ==
1292 NO_TYPE
1293 && (via =
1294 CreateNewVia (PCB->Data,
1295 Crosshair.AttachedLine.Point1.X,
1296 Crosshair.AttachedLine.Point1.Y,
1297 Settings.ViaThickness,
1298 2 * Settings.Keepaway, 0,
1299 Settings.ViaDrillingHole, NULL,
1300 NoFlags ())) != NULL)
1302 AddObjectToCreateUndoList (VIA_TYPE, via, via, via);
1303 DrawVia (via);
1305 /* copy the coordinates */
1306 Crosshair.AttachedLine.Point1.X =
1307 Crosshair.AttachedLine.Point2.X;
1308 Crosshair.AttachedLine.Point1.Y =
1309 Crosshair.AttachedLine.Point2.Y;
1310 IncrementUndoSerialNumber ();
1311 lastLayer = CURRENT;
1313 if (PCB->Clipping && (Note.X != Crosshair.AttachedLine.Point2.X
1314 || Note.Y !=
1315 Crosshair.AttachedLine.Point2.Y)
1316 && (line =
1317 CreateDrawnLineOnLayer (CURRENT,
1318 Crosshair.AttachedLine.Point2.X,
1319 Crosshair.AttachedLine.Point2.Y,
1320 Note.X, Note.Y,
1321 Settings.LineThickness,
1322 2 * Settings.Keepaway,
1323 MakeFlags ((TEST_FLAG
1324 (AUTODRCFLAG,
1325 PCB) ? FOUNDFLAG : 0) |
1326 (TEST_FLAG
1327 (CLEARNEWFLAG,
1328 PCB) ? CLEARLINEFLAG :
1329 0)))) != NULL)
1331 addedLines++;
1332 AddObjectToCreateUndoList (LINE_TYPE, CURRENT, line, line);
1333 IncrementUndoSerialNumber ();
1334 DrawLine (CURRENT, line);
1335 /* move to new start point */
1336 Crosshair.AttachedLine.Point1.X = Note.X;
1337 Crosshair.AttachedLine.Point1.Y = Note.Y;
1338 Crosshair.AttachedLine.Point2.X = Note.X;
1339 Crosshair.AttachedLine.Point2.Y = Note.Y;
1340 if (TEST_FLAG (SWAPSTARTDIRFLAG, PCB))
1342 PCB->Clipping ^= 3;
1345 Draw ();
1347 break;
1349 case RECTANGLE_MODE:
1350 /* do update of position */
1351 NotifyBlock ();
1353 /* create rectangle if both corners are determined
1354 * and width, height are != 0
1356 if (Crosshair.AttachedBox.State == STATE_THIRD &&
1357 Crosshair.AttachedBox.Point1.X != Crosshair.AttachedBox.Point2.X &&
1358 Crosshair.AttachedBox.Point1.Y != Crosshair.AttachedBox.Point2.Y)
1360 PolygonTypePtr polygon;
1362 int flags = CLEARPOLYFLAG;
1363 if (TEST_FLAG (NEWFULLPOLYFLAG, PCB))
1364 flags |= FULLPOLYFLAG;
1365 if ((polygon = CreateNewPolygonFromRectangle (CURRENT,
1366 Crosshair.
1367 AttachedBox.Point1.X,
1368 Crosshair.
1369 AttachedBox.Point1.Y,
1370 Crosshair.
1371 AttachedBox.Point2.X,
1372 Crosshair.
1373 AttachedBox.Point2.Y,
1374 MakeFlags
1375 (flags))) !=
1376 NULL)
1378 AddObjectToCreateUndoList (POLYGON_TYPE, CURRENT,
1379 polygon, polygon);
1380 IncrementUndoSerialNumber ();
1381 DrawPolygon (CURRENT, polygon);
1382 Draw ();
1385 /* reset state to 'first corner' */
1386 Crosshair.AttachedBox.State = STATE_FIRST;
1388 break;
1390 case TEXT_MODE:
1392 char *string;
1394 if ((string = gui->prompt_for (_("Enter text:"), "")) != NULL)
1396 if (strlen(string) > 0)
1398 TextTypePtr text;
1399 int flag = CLEARLINEFLAG;
1401 if (GetLayerGroupNumberByNumber (INDEXOFCURRENT) ==
1402 GetLayerGroupNumberByNumber (solder_silk_layer))
1403 flag |= ONSOLDERFLAG;
1404 if ((text = CreateNewText (CURRENT, &PCB->Font, Note.X,
1405 Note.Y, 0, Settings.TextScale,
1406 string, MakeFlags (flag))) != NULL)
1408 AddObjectToCreateUndoList (TEXT_TYPE, CURRENT, text, text);
1409 IncrementUndoSerialNumber ();
1410 DrawText (CURRENT, text);
1411 Draw ();
1414 free (string);
1416 break;
1419 case POLYGON_MODE:
1421 PointTypePtr points = Crosshair.AttachedPolygon.Points;
1422 Cardinal n = Crosshair.AttachedPolygon.PointN;
1424 /* do update of position; use the 'LINE_MODE' mechanism */
1425 NotifyLine ();
1427 /* check if this is the last point of a polygon */
1428 if (n >= 3 &&
1429 points->X == Crosshair.AttachedLine.Point2.X &&
1430 points->Y == Crosshair.AttachedLine.Point2.Y)
1432 CopyAttachedPolygonToLayer ();
1433 Draw ();
1434 break;
1437 /* create new point if it's the first one or if it's
1438 * different to the last one
1440 if (!n ||
1441 points[n - 1].X != Crosshair.AttachedLine.Point2.X ||
1442 points[n - 1].Y != Crosshair.AttachedLine.Point2.Y)
1444 CreateNewPointInPolygon (&Crosshair.AttachedPolygon,
1445 Crosshair.AttachedLine.Point2.X,
1446 Crosshair.AttachedLine.Point2.Y);
1448 /* copy the coordinates */
1449 Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X;
1450 Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y;
1452 break;
1455 case POLYGONHOLE_MODE:
1457 switch (Crosshair.AttachedObject.State)
1459 /* first notify, lookup object */
1460 case STATE_FIRST:
1461 Crosshair.AttachedObject.Type =
1462 SearchScreen (Note.X, Note.Y, POLYGON_TYPE,
1463 &Crosshair.AttachedObject.Ptr1,
1464 &Crosshair.AttachedObject.Ptr2,
1465 &Crosshair.AttachedObject.Ptr3);
1467 if (Crosshair.AttachedObject.Type != NO_TYPE)
1469 if (TEST_FLAG (LOCKFLAG, (PolygonTypePtr)
1470 Crosshair.AttachedObject.Ptr2))
1472 Message (_("Sorry, the object is locked\n"));
1473 Crosshair.AttachedObject.Type = NO_TYPE;
1474 break;
1476 else
1477 Crosshair.AttachedObject.State = STATE_SECOND;
1479 break;
1481 /* second notify, insert new point into object */
1482 case STATE_SECOND:
1484 PointTypePtr points = Crosshair.AttachedPolygon.Points;
1485 Cardinal n = Crosshair.AttachedPolygon.PointN;
1486 POLYAREA *original, *new_hole, *result;
1487 FlagType Flags;
1489 /* do update of position; use the 'LINE_MODE' mechanism */
1490 NotifyLine ();
1492 /* check if this is the last point of a polygon */
1493 if (n >= 3 &&
1494 points->X == Crosshair.AttachedLine.Point2.X &&
1495 points->Y == Crosshair.AttachedLine.Point2.Y)
1497 /* Create POLYAREAs from the original polygon
1498 * and the new hole polygon */
1499 original = PolygonToPoly ((PolygonType *)Crosshair.AttachedObject.Ptr2);
1500 new_hole = PolygonToPoly (&Crosshair.AttachedPolygon);
1502 /* Subtract the hole from the original polygon shape */
1503 poly_Boolean_free (original, new_hole, &result, PBO_SUB);
1505 /* Convert the resulting polygon(s) into a new set of nodes
1506 * and place them on the page. Delete the original polygon.
1508 SaveUndoSerialNumber ();
1509 Flags = ((PolygonType *)Crosshair.AttachedObject.Ptr2)->Flags;
1510 PolyToPolygonsOnLayer (PCB->Data, (LayerType *)Crosshair.AttachedObject.Ptr1,
1511 result, Flags);
1512 RemoveObject (POLYGON_TYPE,
1513 Crosshair.AttachedObject.Ptr1,
1514 Crosshair.AttachedObject.Ptr2,
1515 Crosshair.AttachedObject.Ptr3);
1516 RestoreUndoSerialNumber ();
1517 IncrementUndoSerialNumber ();
1518 Draw ();
1520 /* reset state of attached line */
1521 memset (&Crosshair.AttachedPolygon, 0, sizeof (PolygonType));
1522 Crosshair.AttachedLine.State = STATE_FIRST;
1523 addedLines = 0;
1525 break;
1528 /* create new point if it's the first one or if it's
1529 * different to the last one
1531 if (!n ||
1532 points[n - 1].X != Crosshair.AttachedLine.Point2.X ||
1533 points[n - 1].Y != Crosshair.AttachedLine.Point2.Y)
1535 CreateNewPointInPolygon (&Crosshair.AttachedPolygon,
1536 Crosshair.AttachedLine.Point2.X,
1537 Crosshair.AttachedLine.Point2.Y);
1539 /* copy the coordinates */
1540 Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X;
1541 Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y;
1543 break;
1547 break;
1550 case PASTEBUFFER_MODE:
1552 TextType estr[MAX_ELEMENTNAMES];
1553 ElementTypePtr e = 0;
1555 if (gui->shift_is_pressed ())
1557 int type =
1558 SearchScreen (Note.X, Note.Y, ELEMENT_TYPE, &ptr1, &ptr2,
1559 &ptr3);
1560 if (type == ELEMENT_TYPE)
1562 e = (ElementTypePtr) ptr1;
1563 if (e)
1565 int i;
1567 memcpy (estr, e->Name,
1568 MAX_ELEMENTNAMES * sizeof (TextType));
1569 for (i = 0; i < MAX_ELEMENTNAMES; ++i)
1570 estr[i].TextString = estr[i].TextString ? strdup(estr[i].TextString) : NULL;
1571 RemoveElement (e);
1575 if (CopyPastebufferToLayout (Note.X, Note.Y))
1576 SetChangedFlag (true);
1577 if (e)
1579 int type =
1580 SearchScreen (Note.X, Note.Y, ELEMENT_TYPE, &ptr1, &ptr2,
1581 &ptr3);
1582 if (type == ELEMENT_TYPE && ptr1)
1584 int i, save_n;
1585 e = (ElementTypePtr) ptr1;
1587 save_n = NAME_INDEX (PCB);
1589 for (i = 0; i < MAX_ELEMENTNAMES; i++)
1591 if (i == save_n)
1592 EraseElementName (e);
1593 r_delete_entry (PCB->Data->name_tree[i],
1594 (BoxType *) & (e->Name[i]));
1595 memcpy (&(e->Name[i]), &(estr[i]), sizeof (TextType));
1596 e->Name[i].Element = e;
1597 SetTextBoundingBox (&PCB->Font, &(e->Name[i]));
1598 r_insert_entry (PCB->Data->name_tree[i],
1599 (BoxType *) & (e->Name[i]), 0);
1600 if (i == save_n)
1601 DrawElementName (e);
1605 break;
1608 case REMOVE_MODE:
1609 if ((type =
1610 SearchScreen (Note.X, Note.Y, REMOVE_TYPES, &ptr1, &ptr2,
1611 &ptr3)) != NO_TYPE)
1613 if (TEST_FLAG (LOCKFLAG, (LineTypePtr) ptr2))
1615 Message (_("Sorry, the object is locked\n"));
1616 break;
1618 if (type == ELEMENT_TYPE)
1620 RubberbandTypePtr ptr;
1621 int i;
1623 Crosshair.AttachedObject.RubberbandN = 0;
1624 LookupRatLines (type, ptr1, ptr2, ptr3);
1625 ptr = Crosshair.AttachedObject.Rubberband;
1626 for (i = 0; i < Crosshair.AttachedObject.RubberbandN; i++)
1628 if (PCB->RatOn)
1629 EraseRat ((RatTypePtr) ptr->Line);
1630 if (TEST_FLAG (RUBBERENDFLAG, ptr->Line))
1631 MoveObjectToRemoveUndoList (RATLINE_TYPE,
1632 ptr->Line, ptr->Line,
1633 ptr->Line);
1634 else
1635 TOGGLE_FLAG (RUBBERENDFLAG, ptr->Line); /* only remove line once */
1636 ptr++;
1639 RemoveObject (type, ptr1, ptr2, ptr3);
1640 IncrementUndoSerialNumber ();
1641 SetChangedFlag (true);
1643 break;
1645 case ROTATE_MODE:
1646 RotateScreenObject (Note.X, Note.Y,
1647 gui->shift_is_pressed ()? (SWAP_IDENT ?
1648 1 : 3)
1649 : (SWAP_IDENT ? 3 : 1));
1650 break;
1652 /* both are almost the same */
1653 case COPY_MODE:
1654 case MOVE_MODE:
1655 switch (Crosshair.AttachedObject.State)
1657 /* first notify, lookup object */
1658 case STATE_FIRST:
1660 int types = (Settings.Mode == COPY_MODE) ?
1661 COPY_TYPES : MOVE_TYPES;
1663 Crosshair.AttachedObject.Type =
1664 SearchScreen (Note.X, Note.Y, types,
1665 &Crosshair.AttachedObject.Ptr1,
1666 &Crosshair.AttachedObject.Ptr2,
1667 &Crosshair.AttachedObject.Ptr3);
1668 if (Crosshair.AttachedObject.Type != NO_TYPE)
1670 if (Settings.Mode == MOVE_MODE &&
1671 TEST_FLAG (LOCKFLAG, (PinTypePtr)
1672 Crosshair.AttachedObject.Ptr2))
1674 Message (_("Sorry, the object is locked\n"));
1675 Crosshair.AttachedObject.Type = NO_TYPE;
1677 else
1678 AttachForCopy (Note.X, Note.Y);
1680 break;
1683 /* second notify, move or copy object */
1684 case STATE_SECOND:
1685 if (Settings.Mode == COPY_MODE)
1686 CopyObject (Crosshair.AttachedObject.Type,
1687 Crosshair.AttachedObject.Ptr1,
1688 Crosshair.AttachedObject.Ptr2,
1689 Crosshair.AttachedObject.Ptr3,
1690 Note.X - Crosshair.AttachedObject.X,
1691 Note.Y - Crosshair.AttachedObject.Y);
1692 else
1694 MoveObjectAndRubberband (Crosshair.AttachedObject.Type,
1695 Crosshair.AttachedObject.Ptr1,
1696 Crosshair.AttachedObject.Ptr2,
1697 Crosshair.AttachedObject.Ptr3,
1698 Note.X - Crosshair.AttachedObject.X,
1699 Note.Y - Crosshair.AttachedObject.Y);
1700 SetLocalRef (0, 0, false);
1702 SetChangedFlag (true);
1704 /* reset identifiers */
1705 Crosshair.AttachedObject.Type = NO_TYPE;
1706 Crosshair.AttachedObject.State = STATE_FIRST;
1707 break;
1709 break;
1711 /* insert a point into a polygon/line/... */
1712 case INSERTPOINT_MODE:
1713 switch (Crosshair.AttachedObject.State)
1715 /* first notify, lookup object */
1716 case STATE_FIRST:
1717 Crosshair.AttachedObject.Type =
1718 SearchScreen (Note.X, Note.Y, INSERT_TYPES,
1719 &Crosshair.AttachedObject.Ptr1,
1720 &Crosshair.AttachedObject.Ptr2,
1721 &Crosshair.AttachedObject.Ptr3);
1723 if (Crosshair.AttachedObject.Type != NO_TYPE)
1725 if (TEST_FLAG (LOCKFLAG, (PolygonTypePtr)
1726 Crosshair.AttachedObject.Ptr2))
1728 Message (_("Sorry, the object is locked\n"));
1729 Crosshair.AttachedObject.Type = NO_TYPE;
1730 break;
1732 else
1734 /* get starting point of nearest segment */
1735 if (Crosshair.AttachedObject.Type == POLYGON_TYPE)
1737 fake.poly =
1738 (PolygonTypePtr) Crosshair.AttachedObject.Ptr2;
1739 polyIndex =
1740 GetLowestDistancePolygonPoint (fake.poly, Note.X,
1741 Note.Y);
1742 fake.line.Point1 = fake.poly->Points[polyIndex];
1743 fake.line.Point2 = fake.poly->Points[
1744 prev_contour_point (fake.poly, polyIndex)];
1745 Crosshair.AttachedObject.Ptr2 = &fake.line;
1748 Crosshair.AttachedObject.State = STATE_SECOND;
1749 InsertedPoint = *AdjustInsertPoint ();
1752 break;
1754 /* second notify, insert new point into object */
1755 case STATE_SECOND:
1756 if (Crosshair.AttachedObject.Type == POLYGON_TYPE)
1757 InsertPointIntoObject (POLYGON_TYPE,
1758 Crosshair.AttachedObject.Ptr1, fake.poly,
1759 &polyIndex,
1760 InsertedPoint.X, InsertedPoint.Y, false, false);
1761 else
1762 InsertPointIntoObject (Crosshair.AttachedObject.Type,
1763 Crosshair.AttachedObject.Ptr1,
1764 Crosshair.AttachedObject.Ptr2,
1765 &polyIndex,
1766 InsertedPoint.X, InsertedPoint.Y, false, false);
1767 SetChangedFlag (true);
1769 /* reset identifiers */
1770 Crosshair.AttachedObject.Type = NO_TYPE;
1771 Crosshair.AttachedObject.State = STATE_FIRST;
1772 break;
1774 break;
1779 /* --------------------------------------------------------------------------- */
1781 static const char atomic_syntax[] = "Atomic(Save|Restore|Close|Block)";
1783 static const char atomic_help[] = "Save or restore the undo serial number.";
1785 /* %start-doc actions Atomic
1787 This action allows making multiple-action bindings into an atomic
1788 operation that will be undone by a single Undo command. For example,
1789 to optimize rat lines, you'd delete the rats and re-add them. To
1790 group these into a single undo, you'd want the deletions and the
1791 additions to have the same undo serial number. So, you @code{Save},
1792 delete the rats, @code{Restore}, add the rats - using the same serial
1793 number as the deletes, then @code{Block}, which checks to see if the
1794 deletions or additions actually did anything. If not, the serial
1795 number is set to the saved number, as there's nothing to undo. If
1796 something did happen, the serial number is incremented so that these
1797 actions are counted as a single undo step.
1799 @table @code
1801 @item Save
1802 Saves the undo serial number.
1804 @item Restore
1805 Returns it to the last saved number.
1807 @item Close
1808 Sets it to 1 greater than the last save.
1810 @item Block
1811 Does a Restore if there was nothing to undo, else does a Close.
1813 @end table
1815 %end-doc */
1817 static int
1818 ActionAtomic (int argc, char **argv, Coord x, Coord y)
1820 if (argc != 1)
1821 AFAIL (atomic);
1823 switch (GetFunctionID (argv[0]))
1825 case F_Save:
1826 SaveUndoSerialNumber ();
1827 break;
1828 case F_Restore:
1829 RestoreUndoSerialNumber ();
1830 break;
1831 case F_Close:
1832 RestoreUndoSerialNumber ();
1833 IncrementUndoSerialNumber ();
1834 break;
1835 case F_Block:
1836 RestoreUndoSerialNumber ();
1837 if (Bumped)
1838 IncrementUndoSerialNumber ();
1839 break;
1841 return 0;
1844 /* -------------------------------------------------------------------------- */
1846 static const char drc_syntax[] = "DRC()";
1848 static const char drc_help[] = "Invoke the DRC check.";
1850 /* %start-doc actions DRC
1852 Note that the design rule check uses the current board rule settings,
1853 not the current style settings.
1855 %end-doc */
1857 static int
1858 ActionDRCheck (int argc, char **argv, Coord x, Coord y)
1860 int count;
1862 if (gui->drc_gui == NULL || gui->drc_gui->log_drc_overview)
1864 Message (_("%m+Rules are minspace %$mS, minoverlap %$mS "
1865 "minwidth %$mS, minsilk %$mS\n"
1866 "min drill %$mS, min annular ring %$mS\n"),
1867 Settings.grid_unit->allow,
1868 PCB->Bloat, PCB->Shrink,
1869 PCB->minWid, PCB->minSlk,
1870 PCB->minDrill, PCB->minRing);
1872 count = DRCAll ();
1873 if (gui->drc_gui == NULL || gui->drc_gui->log_drc_overview)
1875 if (count == 0)
1876 Message (_("No DRC problems found.\n"));
1877 else if (count > 0)
1878 Message (_("Found %d design rule errors.\n"), count);
1879 else
1880 Message (_("Aborted DRC after %d design rule errors.\n"), -count);
1882 return 0;
1885 /* -------------------------------------------------------------------------- */
1887 static const char dumplibrary_syntax[] = "DumpLibrary()";
1889 static const char dumplibrary_help[] =
1890 "Display the entire contents of the libraries.";
1892 /* %start-doc actions DumpLibrary
1895 %end-doc */
1897 static int
1898 ActionDumpLibrary (int argc, char **argv, Coord x, Coord y)
1900 int i, j;
1902 printf ("**** Do not count on this format. It will change ****\n\n");
1903 printf ("MenuN = %d\n", Library.MenuN);
1904 printf ("MenuMax = %d\n", Library.MenuMax);
1905 for (i = 0; i < Library.MenuN; i++)
1907 printf ("Library #%d:\n", i);
1908 printf (" EntryN = %d\n", Library.Menu[i].EntryN);
1909 printf (" EntryMax = %d\n", Library.Menu[i].EntryMax);
1910 printf (" Name = \"%s\"\n", UNKNOWN (Library.Menu[i].Name));
1911 printf (" directory = \"%s\"\n",
1912 UNKNOWN (Library.Menu[i].directory));
1913 printf (" Style = \"%s\"\n", UNKNOWN (Library.Menu[i].Style));
1914 printf (" flag = %d\n", Library.Menu[i].flag);
1916 for (j = 0; j < Library.Menu[i].EntryN; j++)
1918 printf (" #%4d: ", j);
1919 if (Library.Menu[i].Entry[j].Template == (char *) -1)
1921 printf ("newlib: \"%s\"\n",
1922 UNKNOWN (Library.Menu[i].Entry[j].ListEntry));
1924 else
1926 printf ("\"%s\", \"%s\", \"%s\", \"%s\", \"%s\"\n",
1927 UNKNOWN (Library.Menu[i].Entry[j].ListEntry),
1928 UNKNOWN (Library.Menu[i].Entry[j].Template),
1929 UNKNOWN (Library.Menu[i].Entry[j].Package),
1930 UNKNOWN (Library.Menu[i].Entry[j].Value),
1931 UNKNOWN (Library.Menu[i].Entry[j].Description));
1936 return 0;
1939 /* -------------------------------------------------------------------------- */
1941 static const char flip_syntax[] = "Flip(Object|Selected|SelectedElements)";
1943 static const char flip_help[] =
1944 "Flip an element to the opposite side of the board.";
1946 /* %start-doc actions Flip
1948 Note that the location of the element will be symmetric about the
1949 cursor location; i.e. if the part you are pointing at will still be at
1950 the same spot once the element is on the other side. When flipping
1951 multiple elements, this retains their positions relative to each
1952 other, not their absolute positions on the board.
1954 %end-doc */
1956 static int
1957 ActionFlip (int argc, char **argv, Coord x, Coord y)
1959 char *function = ARG (0);
1960 ElementTypePtr element;
1961 void *ptrtmp;
1962 int err = 0;
1964 if (function)
1966 switch (GetFunctionID (function))
1968 case F_Object:
1969 if ((SearchScreen (x, y, ELEMENT_TYPE,
1970 &ptrtmp, &ptrtmp, &ptrtmp)) != NO_TYPE)
1972 element = (ElementTypePtr) ptrtmp;
1973 ChangeElementSide (element, 2 * Crosshair.Y - PCB->MaxHeight);
1974 IncrementUndoSerialNumber ();
1975 Draw ();
1977 break;
1978 case F_Selected:
1979 case F_SelectedElements:
1980 ChangeSelectedElementSide ();
1981 break;
1982 default:
1983 err = 1;
1984 break;
1986 if (!err)
1987 return 0;
1990 AFAIL (flip);
1993 /* -------------------------------------------------------------------------- */
1995 static const char message_syntax[] = "Message(message)";
1997 static const char message_help[] = "Writes a message to the log window.";
1999 /* %start-doc actions Message
2001 This action displays a message to the log window. This action is primarily
2002 provided for use by other programs which may interface with PCB. If
2003 multiple arguments are given, each one is sent to the log window
2004 followed by a newline.
2006 %end-doc */
2008 static int
2009 ActionMessage (int argc, char **argv, Coord x, Coord y)
2011 int i;
2013 if (argc < 1)
2014 AFAIL (message);
2016 for (i = 0; i < argc; i++)
2018 Message (argv[i]);
2019 Message ("\n");
2022 return 0;
2026 /* -------------------------------------------------------------------------- */
2028 static const char setthermal_syntax[] =
2029 "SetThermal(Object|SelectedPins|SelectedVias|Selected, Style)";
2031 static const char setthermal_help[] =
2032 "Set the thermal (on the current layer) of pins or vias to the given style.\n"
2033 "Style = 0 means no thermal.\n"
2034 "Style = 1 has diagonal fingers with sharp edges.\n"
2035 "Style = 2 has horizontal and vertical fingers with sharp edges.\n"
2036 "Style = 3 is a solid connection to the plane."
2037 "Style = 4 has diagonal fingers with rounded edges.\n"
2038 "Style = 5 has horizontal and vertical fingers with rounded edges.\n";
2040 /* %start-doc actions SetThermal
2042 This changes how/whether pins or vias connect to any rectangle or polygon
2043 on the current layer. The first argument can specify one object, or all
2044 selected pins, or all selected vias, or all selected pins and vias.
2045 The second argument specifies the style of connection.
2046 There are 5 possibilities:
2047 0 - no connection,
2048 1 - 45 degree fingers with sharp edges,
2049 2 - horizontal & vertical fingers with sharp edges,
2050 3 - solid connection,
2051 4 - 45 degree fingers with rounded corners,
2052 5 - horizontal & vertical fingers with rounded corners.
2054 Pins and Vias may have thermals whether or not there is a polygon available
2055 to connect with. However, they will have no effect without the polygon.
2056 %end-doc */
2058 static int
2059 ActionSetThermal (int argc, char **argv, Coord x, Coord y)
2061 char *function = ARG (0);
2062 char *style = ARG (1);
2063 void *ptr1, *ptr2, *ptr3;
2064 int type, kind;
2065 int err = 0;
2067 if (function && *function && style && *style)
2069 bool absolute;
2071 kind = GetValue (style, NULL, &absolute);
2072 if (absolute)
2073 switch (GetFunctionID (function))
2075 case F_Object:
2076 if ((type =
2077 SearchScreen (Crosshair.X, Crosshair.Y, CHANGETHERMAL_TYPES,
2078 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
2080 ChangeObjectThermal (type, ptr1, ptr2, ptr3, kind);
2081 IncrementUndoSerialNumber ();
2082 Draw ();
2084 break;
2085 case F_SelectedPins:
2086 ChangeSelectedThermals (PIN_TYPE, kind);
2087 break;
2088 case F_SelectedVias:
2089 ChangeSelectedThermals (VIA_TYPE, kind);
2090 break;
2091 case F_Selected:
2092 case F_SelectedElements:
2093 ChangeSelectedThermals (CHANGETHERMAL_TYPES, kind);
2094 break;
2095 default:
2096 err = 1;
2097 break;
2099 else
2100 err = 1;
2101 if (!err)
2102 return 0;
2105 AFAIL (setthermal);
2108 /* ---------------------------------------------------------------------------
2109 * !!! no action routine !!!
2111 * event handler to set the cursor according to the X pointer position
2112 * called from inside main.c
2114 void
2115 EventMoveCrosshair (int ev_x, int ev_y)
2117 #ifdef HAVE_LIBSTROKE
2118 if (mid_stroke)
2120 StrokeBox.X2 = ev_x;
2121 StrokeBox.Y2 = ev_y;
2122 stroke_record (ev_x, ev_y);
2123 return;
2125 #endif /* HAVE_LIBSTROKE */
2126 if (MoveCrosshairAbsolute (ev_x, ev_y))
2128 /* update object position and cursor location */
2129 AdjustAttachedObjects ();
2130 notify_crosshair_change (true);
2134 /* --------------------------------------------------------------------------- */
2136 static const char setvalue_syntax[] =
2137 "SetValue(Grid|Line|LineSize|Text|TextScale|ViaDrillingHole|Via|ViaSize, delta)";
2139 static const char setvalue_help[] =
2140 "Change various board-wide values and sizes.";
2142 /* %start-doc actions SetValue
2144 @table @code
2146 @item ViaDrillingHole
2147 Changes the diameter of the drill for new vias.
2149 @item Grid
2150 Sets the grid spacing.
2152 @item Line
2153 @item LineSize
2154 Changes the thickness of new lines.
2156 @item Via
2157 @item ViaSize
2158 Changes the diameter of new vias.
2160 @item Text
2161 @item TextScale
2162 Changes the size of new text.
2164 @end table
2166 %end-doc */
2168 static int
2169 ActionSetValue (int argc, char **argv, Coord x, Coord y)
2171 char *function = ARG (0);
2172 char *val = ARG (1);
2173 char *units = ARG (2);
2174 bool absolute; /* flag for 'absolute' value */
2175 double value;
2176 int err = 0;
2178 if (function && val)
2180 value = GetValue (val, units, &absolute);
2181 switch (GetFunctionID (function))
2183 case F_ViaDrillingHole:
2184 SetViaDrillingHole (absolute ? value :
2185 value + Settings.ViaDrillingHole,
2186 false);
2187 hid_action ("RouteStylesChanged");
2188 break;
2190 case F_Grid:
2191 if (absolute)
2192 SetGrid (value, false);
2193 else
2195 /* On the way down, short against the minimum
2196 * PCB drawing unit */
2197 if ((value + PCB->Grid) < 1)
2198 SetGrid (1, false);
2199 else if (PCB->Grid == 1)
2200 SetGrid (value, false);
2201 else
2202 SetGrid (value + PCB->Grid, false);
2204 break;
2206 case F_LineSize:
2207 case F_Line:
2208 SetLineSize (absolute ? value : value + Settings.LineThickness);
2209 hid_action ("RouteStylesChanged");
2210 break;
2212 case F_Via:
2213 case F_ViaSize:
2214 SetViaSize (absolute ? value : value + Settings.ViaThickness, false);
2215 hid_action ("RouteStylesChanged");
2216 break;
2218 case F_Text:
2219 case F_TextScale:
2220 value /= 45;
2221 SetTextScale (absolute ? value : value + Settings.TextScale);
2222 break;
2223 default:
2224 err = 1;
2225 break;
2227 if (!err)
2228 return 0;
2231 AFAIL (setvalue);
2235 /* --------------------------------------------------------------------------- */
2237 static const char quit_syntax[] = "Quit()";
2239 static const char quit_help[] = "Quits the application after confirming.";
2241 /* %start-doc actions Quit
2243 If you have unsaved changes, you will be prompted to confirm (or
2244 save) before quitting.
2246 %end-doc */
2248 static int
2249 ActionQuit (int argc, char **argv, Coord x, Coord y)
2251 char *force = ARG (0);
2252 if (force && strcasecmp (force, "force") == 0)
2254 PCB->Changed = 0;
2255 exit (0);
2257 if (!PCB->Changed || gui->close_confirm_dialog () == HID_CLOSE_CONFIRM_OK)
2258 QuitApplication ();
2259 return 1;
2262 /* --------------------------------------------------------------------------- */
2264 static const char connection_syntax[] =
2265 "Connection(Find|ResetLinesAndPolygons|ResetPinsAndVias|Reset)";
2267 static const char connection_help[] =
2268 "Searches connections of the object at the cursor position.";
2270 /* %start-doc actions Connection
2272 Connections found with this action will be highlighted in the
2273 ``connected-color'' color and will have the ``found'' flag set.
2275 @table @code
2277 @item Find
2278 The net under the cursor is ``found''.
2280 @item ResetLinesAndPolygons
2281 Any ``found'' lines and polygons are marked ``not found''.
2283 @item ResetPinsAndVias
2284 Any ``found'' pins and vias are marked ``not found''.
2286 @item Reset
2287 All ``found'' objects are marked ``not found''.
2289 @end table
2291 %end-doc */
2293 static int
2294 ActionConnection (int argc, char **argv, Coord x, Coord y)
2296 char *function = ARG (0);
2297 if (function)
2299 switch (GetFunctionID (function))
2301 case F_Find:
2303 gui->get_coords (_("Click on a connection"), &x, &y);
2304 LookupConnection (x, y, true, 1, FOUNDFLAG);
2305 break;
2308 case F_ResetLinesAndPolygons:
2309 if (ResetFoundLinesAndPolygons (true))
2311 IncrementUndoSerialNumber ();
2312 Draw ();
2314 break;
2316 case F_ResetPinsViasAndPads:
2317 if (ResetFoundPinsViasAndPads (true))
2319 IncrementUndoSerialNumber ();
2320 Draw ();
2322 break;
2324 case F_Reset:
2325 if (ResetConnections (true))
2327 IncrementUndoSerialNumber ();
2328 Draw ();
2330 break;
2332 return 0;
2335 AFAIL (connection);
2338 /* --------------------------------------------------------------------------- */
2340 static const char disperseelements_syntax[] =
2341 "DisperseElements(All|Selected)";
2343 static const char disperseelements_help[] = "Disperses elements.";
2345 /* %start-doc actions DisperseElements
2347 Normally this is used when starting a board, by selecting all elements
2348 and then dispersing them. This scatters the elements around the board
2349 so that you can pick individual ones, rather than have all the
2350 elements at the same 0,0 coordinate and thus impossible to choose
2351 from.
2353 %end-doc */
2355 #define GAP MIL_TO_COORD(100)
2357 static int
2358 ActionDisperseElements (int argc, char **argv, Coord x, Coord y)
2360 char *function = ARG (0);
2361 Coord minx = GAP,
2362 miny = GAP,
2363 maxy = GAP,
2364 dx, dy;
2365 int all = 0, bad = 0;
2367 if (!function || !*function)
2369 bad = 1;
2371 else
2373 switch (GetFunctionID (function))
2375 case F_All:
2376 all = 1;
2377 break;
2379 case F_Selected:
2380 all = 0;
2381 break;
2383 default:
2384 bad = 1;
2388 if (bad)
2390 AFAIL (disperseelements);
2394 ELEMENT_LOOP (PCB->Data);
2397 * If we want to disperse selected elements, maybe we need smarter
2398 * code here to avoid putting components on top of others which
2399 * are not selected. For now, I'm assuming that this is typically
2400 * going to be used either with a brand new design or a scratch
2401 * design holding some new components
2403 if (!TEST_FLAG (LOCKFLAG, element) && (all || TEST_FLAG (SELECTEDFLAG, element)))
2406 /* figure out how much to move the element */
2407 dx = minx - element->BoundingBox.X1;
2409 /* snap to the grid */
2410 dx -= (element->MarkX + dx) % PCB->Grid;
2413 * and add one grid size so we make sure we always space by GAP or
2414 * more
2416 dx += PCB->Grid;
2418 /* Figure out if this row has room. If not, start a new row */
2419 if (GAP + element->BoundingBox.X2 + dx > PCB->MaxWidth)
2421 miny = maxy + GAP;
2422 minx = GAP;
2425 /* figure out how much to move the element */
2426 dx = minx - element->BoundingBox.X1;
2427 dy = miny - element->BoundingBox.Y1;
2429 /* snap to the grid */
2430 dx -= (element->MarkX + dx) % PCB->Grid;
2431 dx += PCB->Grid;
2432 dy -= (element->MarkY + dy) % PCB->Grid;
2433 dy += PCB->Grid;
2435 /* move the element */
2436 MoveElementLowLevel (PCB->Data, element, dx, dy);
2438 /* and add to the undo list so we can undo this operation */
2439 AddObjectToMoveUndoList (ELEMENT_TYPE, NULL, NULL, element, dx, dy);
2441 /* keep track of how tall this row is */
2442 minx += element->BoundingBox.X2 - element->BoundingBox.X1 + GAP;
2443 if (maxy < element->BoundingBox.Y2)
2445 maxy = element->BoundingBox.Y2;
2450 END_LOOP;
2452 /* done with our action so increment the undo # */
2453 IncrementUndoSerialNumber ();
2455 Redraw ();
2456 SetChangedFlag (true);
2458 return 0;
2461 #undef GAP
2463 /* --------------------------------------------------------------------------- */
2465 static const char display_syntax[] =
2466 "Display(NameOnPCB|Description|Value)\n"
2467 "Display(Grid|Redraw)\n"
2468 "Display(CycleClip|CycleCrosshair|Toggle45Degree|ToggleStartDirection)\n"
2469 "Display(ToggleGrid|ToggleRubberBandMode|ToggleUniqueNames)\n"
2470 "Display(ToggleMask|ToggleName|ToggleClearLine|ToggleFullPoly|ToggleSnapPin)\n"
2471 "Display(ToggleThindraw|ToggleThindrawPoly|ToggleOrthoMove|ToggleLocalRef)\n"
2472 "Display(ToggleCheckPlanes|ToggleShowDRC|ToggleAutoDRC)\n"
2473 "Display(ToggleLiveRoute|LockNames|OnlyNames)\n"
2474 "Display(Pinout|PinOrPadName)";
2476 static const char display_help[] = "Several display-related actions.";
2478 /* %start-doc actions Display
2480 @table @code
2482 @item NameOnPCB
2483 @item Description
2484 @item Value
2485 Specify whether all elements show their name, description, or value.
2487 @item Redraw
2488 Redraw the whole board.
2490 @item Toggle45Degree
2491 When clear, lines can be drawn at any angle. When set, lines are
2492 restricted to multiples of 45 degrees and requested lines may be
2493 broken up according to the clip setting.
2495 @item CycleClip
2496 Changes the way lines are restricted to 45 degree increments. The
2497 various settings are: straight only, orthogonal then angled, and angled
2498 then orthogonal. If AllDirections is set, this action disables it.
2500 @item CycleCrosshair
2501 Changes crosshair drawing. Crosshair may accept form of 4-ray,
2502 8-ray and 12-ray cross.
2504 @item ToggleRubberBandMode
2505 If set, moving an object moves all the lines attached to it too.
2507 @item ToggleStartDirection
2508 If set, each time you set a point in a line, the Clip toggles between
2509 orth-angle and angle-ortho.
2511 @item ToggleUniqueNames
2512 If set, you will not be permitted to change the name of an element to
2513 match that of another element.
2515 @item ToggleSnapPin
2516 If set, pin centers and pad end points are treated as additional grid
2517 points that the cursor can snap to.
2519 @item ToggleLocalRef
2520 If set, the mark is automatically set to the beginning of any move, so
2521 you can see the relative distance you've moved.
2523 @item ToggleThindraw
2524 If set, objects on the screen are drawn as outlines (lines are drawn
2525 as center-lines). This lets you see line endpoints hidden under pins,
2526 for example.
2528 @item ToggleThindrawPoly
2529 If set, polygons on the screen are drawn as outlines.
2531 @item ToggleShowDRC
2532 If set, pending objects (i.e. lines you're in the process of drawing)
2533 will be drawn with an outline showing how far away from other copper
2534 you need to be.
2536 @item ToggleLiveRoute
2537 If set, the progress of the autorouter will be visible on the screen.
2539 @item ToggleAutoDRC
2540 If set, you will not be permitted to make connections which violate
2541 the current DRC and netlist settings.
2543 @item ToggleCheckPlanes
2544 If set, lines and arcs aren't drawn, which usually leaves just the
2545 polygons. If you also disable all but the layer you're interested in,
2546 this allows you to check for isolated regions.
2548 @item ToggleOrthoMove
2549 If set, the crosshair is only allowed to move orthogonally from its
2550 previous position. I.e. you can move an element or line up, down,
2551 left, or right, but not up+left or down+right.
2553 @item ToggleName
2554 Selects whether the pinouts show the pin names or the pin numbers.
2556 @item ToggleLockNames
2557 If set, text will ignore left mouse clicks and actions that work on
2558 objects under the mouse. You can still select text with a lasso (left
2559 mouse drag) and perform actions on the selection.
2561 @item ToggleOnlyNames
2562 If set, only text will be sensitive for mouse clicks and actions that
2563 work on objects under the mouse. You can still select other objects
2564 with a lasso (left mouse drag) and perform actions on the selection.
2566 @item ToggleMask
2567 Turns the solder mask on or off.
2569 @item ToggleClearLine
2570 When set, the clear-line flag causes new lines and arcs to have their
2571 ``clear polygons'' flag set, so they won't be electrically connected
2572 to any polygons they overlap.
2574 @item ToggleFullPoly
2575 When set, the full-poly flag causes new polygons to have their
2576 ``full polygon'' flag set, so all parts of them will be displayed
2577 instead of only the biggest one.
2579 @item ToggleGrid
2580 Resets the origin of the current grid to be wherever the mouse pointer
2581 is (not where the crosshair currently is). If you provide two numbers
2582 after this, the origin is set to that coordinate.
2584 @item Grid
2585 Toggles whether the grid is displayed or not.
2587 @item Pinout
2588 Causes the pinout of the element indicated by the cursor to be
2589 displayed, usually in a separate window.
2591 @item PinOrPadName
2592 Toggles whether the names of pins, pads, or (yes) vias will be
2593 displayed. If the cursor is over an element, all of its pins and pads
2594 are affected.
2596 @end table
2598 %end-doc */
2600 static enum crosshair_shape
2601 CrosshairShapeIncrement (enum crosshair_shape shape)
2603 switch(shape)
2605 case Basic_Crosshair_Shape:
2606 shape = Union_Jack_Crosshair_Shape;
2607 break;
2608 case Union_Jack_Crosshair_Shape:
2609 shape = Dozen_Crosshair_Shape;
2610 break;
2611 case Dozen_Crosshair_Shape:
2612 shape = Crosshair_Shapes_Number;
2613 break;
2614 case Crosshair_Shapes_Number:
2615 shape = Basic_Crosshair_Shape;
2616 break;
2618 return shape;
2621 static int
2622 ActionDisplay (int argc, char **argv, Coord childX, Coord childY)
2624 char *function, *str_dir;
2625 int id;
2626 int err = 0;
2628 function = ARG (0);
2629 str_dir = ARG (1);
2631 if (function && (!str_dir || !*str_dir))
2633 switch (id = GetFunctionID (function))
2636 /* redraw layout */
2637 case F_ClearAndRedraw:
2638 case F_Redraw:
2639 Redraw ();
2640 break;
2642 /* change the displayed name of elements */
2643 case F_Value:
2644 case F_NameOnPCB:
2645 case F_Description:
2646 ELEMENT_LOOP (PCB->Data);
2648 EraseElementName (element);
2650 END_LOOP;
2651 CLEAR_FLAG (DESCRIPTIONFLAG | NAMEONPCBFLAG, PCB);
2652 switch (id)
2654 case F_Value:
2655 break;
2656 case F_NameOnPCB:
2657 SET_FLAG (NAMEONPCBFLAG, PCB);
2658 break;
2659 case F_Description:
2660 SET_FLAG (DESCRIPTIONFLAG, PCB);
2661 break;
2663 ELEMENT_LOOP (PCB->Data);
2665 DrawElementName (element);
2667 END_LOOP;
2668 Draw ();
2669 break;
2671 /* toggle line-adjust flag */
2672 case F_ToggleAllDirections:
2673 TOGGLE_FLAG (ALLDIRECTIONFLAG, PCB);
2674 AdjustAttachedObjects ();
2675 break;
2677 case F_CycleClip:
2678 notify_crosshair_change (false);
2679 if TEST_FLAG
2680 (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 = CreateNewPCB (true);
5903 PCB->Data->LayerN = DEF_LAYER;
5904 CreateNewPCBPost (PCB, 1);
5906 /* setup the new name and reset some values to default */
5907 free (PCB->Name);
5908 PCB->Name = name;
5910 ResetStackAndVisibility ();
5911 SetCrosshairRange (0, 0, PCB->MaxWidth, PCB->MaxHeight);
5912 CenterDisplay (PCB->MaxWidth / 2, PCB->MaxHeight / 2);
5913 Redraw ();
5915 hid_action ("PCBChanged");
5916 notify_crosshair_change (true);
5917 return 0;
5919 return 1;
5922 /* ---------------------------------------------------------------------------
5923 * no operation, just for testing purposes
5924 * syntax: Bell(volume)
5926 void
5927 ActionBell (char *volume)
5929 gui->beep ();
5932 /* --------------------------------------------------------------------------- */
5934 static const char pastebuffer_syntax[] =
5935 "PasteBuffer(AddSelected|Clear|1..MAX_BUFFER)\n"
5936 "PasteBuffer(Rotate, 1..3)\n"
5937 "PasteBuffer(Convert|Save|Restore|Mirror)\n"
5938 "PasteBuffer(ToLayout, X, Y, units)";
5940 static const char pastebuffer_help[] =
5941 "Various operations on the paste buffer.";
5943 /* %start-doc actions PasteBuffer
5945 There are a number of paste buffers; the actual limit is a
5946 compile-time constant @code{MAX_BUFFER} in @file{globalconst.h}. It
5947 is currently @code{5}. One of these is the ``current'' paste buffer,
5948 often referred to as ``the'' paste buffer.
5950 @table @code
5952 @item AddSelected
5953 Copies the selected objects to the current paste buffer.
5955 @item Clear
5956 Remove all objects from the current paste buffer.
5958 @item Convert
5959 Convert the current paste buffer to an element. Vias are converted to
5960 pins, lines are converted to pads.
5962 @item Restore
5963 Convert any elements in the paste buffer back to vias and lines.
5965 @item Mirror
5966 Flip all objects in the paste buffer vertically (up/down flip). To mirror
5967 horizontally, combine this with rotations.
5969 @item Rotate
5970 Rotates the current buffer. The number to pass is 1..3, where 1 means
5971 90 degrees counter clockwise, 2 means 180 degrees, and 3 means 90
5972 degrees clockwise (270 CCW).
5974 @item Save
5975 Saves any elements in the current buffer to the indicated file.
5977 @item ToLayout
5978 Pastes any elements in the current buffer to the indicated X, Y
5979 coordinates in the layout. The @code{X} and @code{Y} are treated like
5980 @code{delta} is for many other objects. For each, if it's prefixed by
5981 @code{+} or @code{-}, then that amount is relative to the last
5982 location. Otherwise, it's absolute. Units can be
5983 @code{mil} or @code{mm}; if unspecified, units are PCB's internal
5984 units, currently 1/100 mil.
5987 @item 1..MAX_BUFFER
5988 Selects the given buffer to be the current paste buffer.
5990 @end table
5992 %end-doc */
5994 static int
5995 ActionPasteBuffer (int argc, char **argv, Coord x, Coord y)
5997 char *function = argc ? argv[0] : (char *)"";
5998 char *sbufnum = argc > 1 ? argv[1] : (char *)"";
5999 char *name;
6000 static char *default_file = NULL;
6001 int free_name = 0;
6003 notify_crosshair_change (false);
6004 if (function)
6006 switch (GetFunctionID (function))
6008 /* clear contents of paste buffer */
6009 case F_Clear:
6010 ClearBuffer (PASTEBUFFER);
6011 break;
6013 /* copies objects to paste buffer */
6014 case F_AddSelected:
6015 AddSelectedToBuffer (PASTEBUFFER, 0, 0, false);
6016 break;
6018 /* converts buffer contents into an element */
6019 case F_Convert:
6020 ConvertBufferToElement (PASTEBUFFER);
6021 break;
6023 /* break up element for editing */
6024 case F_Restore:
6025 SmashBufferElement (PASTEBUFFER);
6026 break;
6028 /* Mirror buffer */
6029 case F_Mirror:
6030 MirrorBuffer (PASTEBUFFER);
6031 break;
6033 case F_Rotate:
6034 if (sbufnum)
6036 RotateBuffer (PASTEBUFFER, (BYTE) atoi (sbufnum));
6037 SetCrosshairRangeToBuffer ();
6039 break;
6041 case F_Save:
6042 if (PASTEBUFFER->Data->ElementN == 0)
6044 Message (_("Buffer has no elements!\n"));
6045 break;
6047 free_name = 0;
6048 if (argc <= 1)
6050 name = gui->fileselect (_("Save Paste Buffer As ..."),
6051 _("Choose a file to save the contents of the\n"
6052 "paste buffer to.\n"),
6053 default_file, ".fp", "footprint",
6056 if (default_file)
6058 free (default_file);
6059 default_file = NULL;
6061 if ( name && *name)
6063 default_file = strdup (name);
6065 free_name = 1;
6068 else
6069 name = argv[1];
6072 FILE *exist;
6074 if ((exist = fopen (name, "r")))
6076 fclose (exist);
6077 if (gui->
6078 confirm_dialog (_("File exists! Ok to overwrite?"), 0))
6079 SaveBufferElements (name);
6081 else
6082 SaveBufferElements (name);
6084 if (free_name && name)
6085 free (name);
6087 break;
6089 case F_ToLayout:
6091 static Coord oldx = 0, oldy = 0;
6092 Coord x, y;
6093 bool absolute;
6095 if (argc == 1)
6097 x = y = 0;
6099 else if (argc == 3 || argc == 4)
6101 x = GetValue (ARG (1), ARG (3), &absolute);
6102 if (!absolute)
6103 x += oldx;
6104 y = GetValue (ARG (2), ARG (3), &absolute);
6105 if (!absolute)
6106 y += oldy;
6108 else
6110 notify_crosshair_change (true);
6111 AFAIL (pastebuffer);
6114 oldx = x;
6115 oldy = y;
6116 if (CopyPastebufferToLayout (x, y))
6117 SetChangedFlag (true);
6119 break;
6121 /* set number */
6122 default:
6124 int number = atoi (function);
6126 /* correct number */
6127 if (number)
6128 SetBufferNumber (number - 1);
6133 notify_crosshair_change (true);
6134 return 0;
6137 /* --------------------------------------------------------------------------- */
6139 static const char undo_syntax[] = "Undo()\n"
6140 "Undo(ClearList)";
6142 static const char undo_help[] = "Undo recent changes.";
6144 /* %start-doc actions Undo
6146 The unlimited undo feature of @code{Pcb} allows you to recover from
6147 most operations that materially affect you work. Calling
6148 @code{Undo()} without any parameter recovers from the last (non-undo)
6149 operation. @code{ClearList} is used to release the allocated
6150 memory. @code{ClearList} is called whenever a new layout is started or
6151 loaded. See also @code{Redo} and @code{Atomic}.
6153 Note that undo groups operations by serial number; changes with the
6154 same serial number will be undone (or redone) as a group. See
6155 @code{Atomic}.
6157 %end-doc */
6159 static int
6160 ActionUndo (int argc, char **argv, Coord x, Coord y)
6162 char *function = ARG (0);
6163 if (!function || !*function)
6165 /* don't allow undo in the middle of an operation */
6166 if (Settings.Mode != POLYGONHOLE_MODE &&
6167 Crosshair.AttachedObject.State != STATE_FIRST)
6168 return 1;
6169 if (Crosshair.AttachedBox.State != STATE_FIRST
6170 && Settings.Mode != ARC_MODE)
6171 return 1;
6172 /* undo the last operation */
6174 notify_crosshair_change (false);
6175 if ((Settings.Mode == POLYGON_MODE ||
6176 Settings.Mode == POLYGONHOLE_MODE) &&
6177 Crosshair.AttachedPolygon.PointN)
6179 GoToPreviousPoint ();
6180 notify_crosshair_change (true);
6181 return 0;
6183 /* move anchor point if undoing during line creation */
6184 if (Settings.Mode == LINE_MODE)
6186 if (Crosshair.AttachedLine.State == STATE_SECOND)
6188 if (TEST_FLAG (AUTODRCFLAG, PCB))
6189 Undo (true); /* undo the connection find */
6190 Crosshair.AttachedLine.State = STATE_FIRST;
6191 SetLocalRef (0, 0, false);
6192 notify_crosshair_change (true);
6193 return 0;
6195 if (Crosshair.AttachedLine.State == STATE_THIRD)
6197 int type;
6198 void *ptr1, *ptr3, *ptrtmp;
6199 LineTypePtr ptr2;
6200 /* this search is guaranteed to succeed */
6201 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6202 &ptrtmp, &ptr3,
6203 Crosshair.AttachedLine.Point1.X,
6204 Crosshair.AttachedLine.Point1.Y, 0);
6205 ptr2 = (LineTypePtr) ptrtmp;
6207 /* save both ends of line */
6208 Crosshair.AttachedLine.Point2.X = ptr2->Point1.X;
6209 Crosshair.AttachedLine.Point2.Y = ptr2->Point1.Y;
6210 if ((type = Undo (true)))
6211 SetChangedFlag (true);
6212 /* check that the undo was of the right type */
6213 if ((type & UNDO_CREATE) == 0)
6215 /* wrong undo type, restore anchor points */
6216 Crosshair.AttachedLine.Point2.X =
6217 Crosshair.AttachedLine.Point1.X;
6218 Crosshair.AttachedLine.Point2.Y =
6219 Crosshair.AttachedLine.Point1.Y;
6220 notify_crosshair_change (true);
6221 return 0;
6223 /* move to new anchor */
6224 Crosshair.AttachedLine.Point1.X =
6225 Crosshair.AttachedLine.Point2.X;
6226 Crosshair.AttachedLine.Point1.Y =
6227 Crosshair.AttachedLine.Point2.Y;
6228 /* check if an intermediate point was removed */
6229 if (type & UNDO_REMOVE)
6231 /* this search should find the restored line */
6232 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6233 &ptrtmp,
6234 &ptr3,
6235 Crosshair.AttachedLine.Point2.X,
6236 Crosshair.AttachedLine.Point2.Y, 0);
6237 ptr2 = (LineTypePtr) ptrtmp;
6238 if (TEST_FLAG (AUTODRCFLAG, PCB))
6240 /* undo loses FOUNDFLAG */
6241 SET_FLAG(FOUNDFLAG, ptr2);
6242 DrawLine (CURRENT, ptr2);
6244 Crosshair.AttachedLine.Point1.X =
6245 Crosshair.AttachedLine.Point2.X = ptr2->Point2.X;
6246 Crosshair.AttachedLine.Point1.Y =
6247 Crosshair.AttachedLine.Point2.Y = ptr2->Point2.Y;
6249 FitCrosshairIntoGrid (Crosshair.X, Crosshair.Y);
6250 AdjustAttachedObjects ();
6251 if (--addedLines == 0)
6253 Crosshair.AttachedLine.State = STATE_SECOND;
6254 lastLayer = CURRENT;
6256 else
6258 /* this search is guaranteed to succeed too */
6259 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6260 &ptrtmp,
6261 &ptr3,
6262 Crosshair.AttachedLine.Point1.X,
6263 Crosshair.AttachedLine.Point1.Y, 0);
6264 ptr2 = (LineTypePtr) ptrtmp;
6265 lastLayer = (LayerTypePtr) ptr1;
6267 notify_crosshair_change (true);
6268 return 0;
6271 if (Settings.Mode == ARC_MODE)
6273 if (Crosshair.AttachedBox.State == STATE_SECOND)
6275 Crosshair.AttachedBox.State = STATE_FIRST;
6276 notify_crosshair_change (true);
6277 return 0;
6279 if (Crosshair.AttachedBox.State == STATE_THIRD)
6281 void *ptr1, *ptr2, *ptr3;
6282 BoxTypePtr bx;
6283 /* guaranteed to succeed */
6284 SearchObjectByLocation (ARC_TYPE, &ptr1, &ptr2, &ptr3,
6285 Crosshair.AttachedBox.Point1.X,
6286 Crosshair.AttachedBox.Point1.Y, 0);
6287 bx = GetArcEnds ((ArcTypePtr) ptr2);
6288 Crosshair.AttachedBox.Point1.X =
6289 Crosshair.AttachedBox.Point2.X = bx->X1;
6290 Crosshair.AttachedBox.Point1.Y =
6291 Crosshair.AttachedBox.Point2.Y = bx->Y1;
6292 AdjustAttachedObjects ();
6293 if (--addedLines == 0)
6294 Crosshair.AttachedBox.State = STATE_SECOND;
6297 /* undo the last destructive operation */
6298 if (Undo (true))
6299 SetChangedFlag (true);
6301 else if (function)
6303 switch (GetFunctionID (function))
6305 /* clear 'undo objects' list */
6306 case F_ClearList:
6307 ClearUndoList (false);
6308 break;
6311 notify_crosshair_change (true);
6312 return 0;
6315 /* --------------------------------------------------------------------------- */
6317 static const char redo_syntax[] = "Redo()";
6319 static const char redo_help[] = "Redo recent \"undo\" operations.";
6321 /* %start-doc actions Redo
6323 This routine allows you to recover from the last undo command. You
6324 might want to do this if you thought that undo was going to revert
6325 something other than what it actually did (in case you are confused
6326 about which operations are un-doable), or if you have been backing up
6327 through a long undo list and over-shoot your stopping point. Any
6328 change that is made since the undo in question will trim the redo
6329 list. For example if you add ten lines, then undo three of them you
6330 could use redo to put them back, but if you move a line on the board
6331 before performing the redo, you will lose the ability to "redo" the
6332 three "undone" lines.
6334 %end-doc */
6336 static int
6337 ActionRedo (int argc, char **argv, Coord x, Coord y)
6339 if (((Settings.Mode == POLYGON_MODE ||
6340 Settings.Mode == POLYGONHOLE_MODE) &&
6341 Crosshair.AttachedPolygon.PointN) ||
6342 Crosshair.AttachedLine.State == STATE_SECOND)
6343 return 1;
6344 notify_crosshair_change (false);
6345 if (Redo (true))
6347 SetChangedFlag (true);
6348 if (Settings.Mode == LINE_MODE &&
6349 Crosshair.AttachedLine.State != STATE_FIRST)
6351 LineType *line = g_list_last (CURRENT->Line)->data;
6352 Crosshair.AttachedLine.Point1.X =
6353 Crosshair.AttachedLine.Point2.X = line->Point2.X;
6354 Crosshair.AttachedLine.Point1.Y =
6355 Crosshair.AttachedLine.Point2.Y = line->Point2.Y;
6356 addedLines++;
6359 notify_crosshair_change (true);
6360 return 0;
6363 /* --------------------------------------------------------------------------- */
6365 static const char polygon_syntax[] = "Polygon(Close|PreviousPoint)";
6367 static const char polygon_help[] = "Some polygon related stuff.";
6369 /* %start-doc actions Polygon
6371 Polygons need a special action routine to make life easier.
6373 @table @code
6375 @item Close
6376 Creates the final segment of the polygon. This may fail if clipping
6377 to 45 degree lines is switched on, in which case a warning is issued.
6379 @item PreviousPoint
6380 Resets the newly entered corner to the previous one. The Undo action
6381 will call Polygon(PreviousPoint) when appropriate to do so.
6383 @end table
6385 %end-doc */
6387 static int
6388 ActionPolygon (int argc, char **argv, Coord x, Coord y)
6390 char *function = ARG (0);
6391 if (function && Settings.Mode == POLYGON_MODE)
6393 notify_crosshair_change (false);
6394 switch (GetFunctionID (function))
6396 /* close open polygon if possible */
6397 case F_Close:
6398 ClosePolygon ();
6399 break;
6401 /* go back to the previous point */
6402 case F_PreviousPoint:
6403 GoToPreviousPoint ();
6404 break;
6406 notify_crosshair_change (true);
6408 return 0;
6411 /* --------------------------------------------------------------------------- */
6413 static const char routestyle_syntax[] = "RouteStyle(1|2|3|4)";
6415 static const char routestyle_help[] =
6416 "Copies the indicated routing style into the current sizes.";
6418 /* %start-doc actions RouteStyle
6420 %end-doc */
6422 static int
6423 ActionRouteStyle (int argc, char **argv, Coord x, Coord y)
6425 char *str = ARG (0);
6426 RouteStyleType *rts;
6427 int number;
6429 if (str)
6431 number = atoi (str);
6432 if (number > 0 && number <= NUM_STYLES)
6434 rts = &PCB->RouteStyle[number - 1];
6435 SetLineSize (rts->Thick);
6436 SetViaSize (rts->Diameter, true);
6437 SetViaDrillingHole (rts->Hole, true);
6438 SetKeepawayWidth (rts->Keepaway);
6439 hid_action("RouteStylesChanged");
6442 return 0;
6446 /* --------------------------------------------------------------------------- */
6448 static const char moveobject_syntax[] = "MoveObject(X,Y,dim)";
6450 static const char moveobject_help[] = "Moves the object under the crosshair.";
6452 /* %start-doc actions MoveObject
6454 The @code{X} and @code{Y} are treated like @code{delta} is for many
6455 other objects. For each, if it's prefixed by @code{+} or @code{-},
6456 then that amount is relative. Otherwise, it's absolute. Units can be
6457 @code{mil} or @code{mm}; if unspecified, units are PCB's internal
6458 units, currently 1/100 mil.
6460 %end-doc */
6462 static int
6463 ActionMoveObject (int argc, char **argv, Coord x, Coord y)
6465 char *x_str = ARG (0);
6466 char *y_str = ARG (1);
6467 char *units = ARG (2);
6468 Coord nx, ny;
6469 bool absolute1, absolute2;
6470 void *ptr1, *ptr2, *ptr3;
6471 int type;
6473 ny = GetValue (y_str, units, &absolute1);
6474 nx = GetValue (x_str, units, &absolute2);
6476 type = SearchScreen (x, y, MOVE_TYPES, &ptr1, &ptr2, &ptr3);
6477 if (type == NO_TYPE)
6479 Message (_("Nothing found under crosshair\n"));
6480 return 1;
6482 if (absolute1)
6483 nx -= x;
6484 if (absolute2)
6485 ny -= y;
6486 Crosshair.AttachedObject.RubberbandN = 0;
6487 if (TEST_FLAG (RUBBERBANDFLAG, PCB))
6488 LookupRubberbandLines (type, ptr1, ptr2, ptr3);
6489 if (type == ELEMENT_TYPE)
6490 LookupRatLines (type, ptr1, ptr2, ptr3);
6491 MoveObjectAndRubberband (type, ptr1, ptr2, ptr3, nx, ny);
6492 SetChangedFlag (true);
6493 return 0;
6496 /* --------------------------------------------------------------------------- */
6498 static const char movetocurrentlayer_syntax[] =
6499 "MoveToCurrentLayer(Object|SelectedObjects)";
6501 static const char movetocurrentlayer_help[] =
6502 "Moves objects to the current layer.";
6504 /* %start-doc actions MoveToCurrentLayer
6506 Note that moving an element from a component layer to a solder layer,
6507 or from solder to component, won't automatically flip it. Use the
6508 @code{Flip()} action to do that.
6510 %end-doc */
6512 static int
6513 ActionMoveToCurrentLayer (int argc, char **argv, Coord x, Coord y)
6515 char *function = ARG (0);
6516 if (function)
6518 switch (GetFunctionID (function))
6520 case F_Object:
6522 int type;
6523 void *ptr1, *ptr2, *ptr3;
6525 gui->get_coords (_("Select an Object"), &x, &y);
6526 if ((type =
6527 SearchScreen (x, y, MOVETOLAYER_TYPES,
6528 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
6529 if (MoveObjectToLayer (type, ptr1, ptr2, ptr3, CURRENT, false))
6530 SetChangedFlag (true);
6531 break;
6534 case F_SelectedObjects:
6535 case F_Selected:
6536 if (MoveSelectedObjectsToLayer (CURRENT))
6537 SetChangedFlag (true);
6538 break;
6541 return 0;
6545 static const char setsame_syntax[] = "SetSame()";
6547 static const char setsame_help[] =
6548 "Sets current layer and sizes to match indicated item.";
6550 /* %start-doc actions SetSame
6552 When invoked over any line, arc, polygon, or via, this changes the
6553 current layer to be the layer that item is on, and changes the current
6554 sizes (thickness, keepaway, drill, etc) according to that item.
6556 %end-doc */
6558 static int
6559 ActionSetSame (int argc, char **argv, Coord x, Coord y)
6561 void *ptr1, *ptr2, *ptr3;
6562 int type;
6563 LayerTypePtr layer = CURRENT;
6565 type = SearchScreen (x, y, CLONE_TYPES, &ptr1, &ptr2, &ptr3);
6566 /* set layer current and size from line or arc */
6567 switch (type)
6569 case LINE_TYPE:
6570 notify_crosshair_change (false);
6571 Settings.LineThickness = ((LineTypePtr) ptr2)->Thickness;
6572 Settings.Keepaway = ((LineTypePtr) ptr2)->Clearance / 2;
6573 layer = (LayerTypePtr) ptr1;
6574 if (Settings.Mode != LINE_MODE)
6575 SetMode (LINE_MODE);
6576 notify_crosshair_change (true);
6577 hid_action ("RouteStylesChanged");
6578 break;
6580 case ARC_TYPE:
6581 notify_crosshair_change (false);
6582 Settings.LineThickness = ((ArcTypePtr) ptr2)->Thickness;
6583 Settings.Keepaway = ((ArcTypePtr) ptr2)->Clearance / 2;
6584 layer = (LayerTypePtr) ptr1;
6585 if (Settings.Mode != ARC_MODE)
6586 SetMode (ARC_MODE);
6587 notify_crosshair_change (true);
6588 hid_action ("RouteStylesChanged");
6589 break;
6591 case POLYGON_TYPE:
6592 layer = (LayerTypePtr) ptr1;
6593 break;
6595 case VIA_TYPE:
6596 notify_crosshair_change (false);
6597 Settings.ViaThickness = ((PinTypePtr) ptr2)->Thickness;
6598 Settings.ViaDrillingHole = ((PinTypePtr) ptr2)->DrillingHole;
6599 Settings.Keepaway = ((PinTypePtr) ptr2)->Clearance / 2;
6600 if (Settings.Mode != VIA_MODE)
6601 SetMode (VIA_MODE);
6602 notify_crosshair_change (true);
6603 hid_action ("RouteStylesChanged");
6604 break;
6606 default:
6607 return 1;
6609 if (layer != CURRENT)
6611 ChangeGroupVisibility (GetLayerNumber (PCB->Data, layer), true, true);
6612 Redraw ();
6614 return 0;
6618 /* --------------------------------------------------------------------------- */
6620 static const char setflag_syntax[] =
6621 "SetFlag(Object|Selected|SelectedObjects, flag)\n"
6622 "SetFlag(SelectedLines|SelectedPins|SelectedVias, flag)\n"
6623 "SetFlag(SelectedPads|SelectedTexts|SelectedNames, flag)\n"
6624 "SetFlag(SelectedElements, flag)\n"
6625 "flag = square | octagon | thermal | join";
6627 static const char setflag_help[] = "Sets flags on objects.";
6629 /* %start-doc actions SetFlag
6631 Turns the given flag on, regardless of its previous setting. See
6632 @code{ChangeFlag}.
6634 @example
6635 SetFlag(SelectedPins,thermal)
6636 @end example
6638 %end-doc */
6640 static int
6641 ActionSetFlag (int argc, char **argv, Coord x, Coord y)
6643 char *function = ARG (0);
6644 char *flag = ARG (1);
6645 ChangeFlag (function, flag, 1, "SetFlag");
6646 return 0;
6649 /* --------------------------------------------------------------------------- */
6651 static const char clrflag_syntax[] =
6652 "ClrFlag(Object|Selected|SelectedObjects, flag)\n"
6653 "ClrFlag(SelectedLines|SelectedPins|SelectedVias, flag)\n"
6654 "ClrFlag(SelectedPads|SelectedTexts|SelectedNames, flag)\n"
6655 "ClrFlag(SelectedElements, flag)\n"
6656 "flag = square | octagon | thermal | join";
6658 static const char clrflag_help[] = "Clears flags on objects.";
6660 /* %start-doc actions ClrFlag
6662 Turns the given flag off, regardless of its previous setting. See
6663 @code{ChangeFlag}.
6665 @example
6666 ClrFlag(SelectedLines,join)
6667 @end example
6669 %end-doc */
6671 static int
6672 ActionClrFlag (int argc, char **argv, Coord x, Coord y)
6674 char *function = ARG (0);
6675 char *flag = ARG (1);
6676 ChangeFlag (function, flag, 0, "ClrFlag");
6677 return 0;
6680 /* --------------------------------------------------------------------------- */
6682 static const char changeflag_syntax[] =
6683 "ChangeFlag(Object|Selected|SelectedObjects, flag, value)\n"
6684 "ChangeFlag(SelectedLines|SelectedPins|SelectedVias, flag, value)\n"
6685 "ChangeFlag(SelectedPads|SelectedTexts|SelectedNames, flag, value)\n"
6686 "ChangeFlag(SelectedElements, flag, value)\n"
6687 "flag = square | octagon | thermal | join\n"
6688 "value = 0 | 1";
6690 static const char changeflag_help[] = "Sets or clears flags on objects.";
6692 /* %start-doc actions ChangeFlag
6694 Toggles the given flag on the indicated object(s). The flag may be
6695 one of the flags listed above (square, octagon, thermal, join). The
6696 value may be the number 0 or 1. If the value is 0, the flag is
6697 cleared. If the value is 1, the flag is set.
6699 %end-doc */
6701 static int
6702 ActionChangeFlag (int argc, char **argv, Coord x, Coord y)
6704 char *function = ARG (0);
6705 char *flag = ARG (1);
6706 int value = argc > 2 ? atoi (argv[2]) : -1;
6707 if (value != 0 && value != 1)
6708 AFAIL (changeflag);
6710 ChangeFlag (function, flag, value, "ChangeFlag");
6711 return 0;
6715 static void
6716 ChangeFlag (char *what, char *flag_name, int value, char *cmd_name)
6718 bool (*set_object) (int, void *, void *, void *);
6719 bool (*set_selected) (int);
6721 if (NSTRCMP (flag_name, "square") == 0)
6723 set_object = value ? SetObjectSquare : ClrObjectSquare;
6724 set_selected = value ? SetSelectedSquare : ClrSelectedSquare;
6726 else if (NSTRCMP (flag_name, "octagon") == 0)
6728 set_object = value ? SetObjectOctagon : ClrObjectOctagon;
6729 set_selected = value ? SetSelectedOctagon : ClrSelectedOctagon;
6731 else if (NSTRCMP (flag_name, "join") == 0)
6733 /* Note: these are backwards, because the flag is "clear" but
6734 the command is "join". */
6735 set_object = value ? ClrObjectJoin : SetObjectJoin;
6736 set_selected = value ? ClrSelectedJoin : SetSelectedJoin;
6738 else
6740 Message (_("%s(): Flag \"%s\" is not valid\n"), cmd_name, flag_name);
6741 return;
6744 switch (GetFunctionID (what))
6746 case F_Object:
6748 int type;
6749 void *ptr1, *ptr2, *ptr3;
6751 if ((type =
6752 SearchScreen (Crosshair.X, Crosshair.Y, CHANGESIZE_TYPES,
6753 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
6754 if (TEST_FLAG (LOCKFLAG, (PinTypePtr) ptr2))
6755 Message (_("Sorry, the object is locked\n"));
6756 if (set_object (type, ptr1, ptr2, ptr3))
6757 SetChangedFlag (true);
6758 break;
6761 case F_SelectedVias:
6762 if (set_selected (VIA_TYPE))
6763 SetChangedFlag (true);
6764 break;
6766 case F_SelectedPins:
6767 if (set_selected (PIN_TYPE))
6768 SetChangedFlag (true);
6769 break;
6771 case F_SelectedPads:
6772 if (set_selected (PAD_TYPE))
6773 SetChangedFlag (true);
6774 break;
6776 case F_SelectedLines:
6777 if (set_selected (LINE_TYPE))
6778 SetChangedFlag (true);
6779 break;
6781 case F_SelectedTexts:
6782 if (set_selected (TEXT_TYPE))
6783 SetChangedFlag (true);
6784 break;
6786 case F_SelectedNames:
6787 if (set_selected (ELEMENTNAME_TYPE))
6788 SetChangedFlag (true);
6789 break;
6791 case F_SelectedElements:
6792 if (set_selected (ELEMENT_TYPE))
6793 SetChangedFlag (true);
6794 break;
6796 case F_Selected:
6797 case F_SelectedObjects:
6798 if (set_selected (CHANGESIZE_TYPES))
6799 SetChangedFlag (true);
6800 break;
6804 /* --------------------------------------------------------------------------- */
6806 static const char executefile_syntax[] = "ExecuteFile(filename)";
6808 static const char executefile_help[] = "Run actions from the given file.";
6810 /* %start-doc actions ExecuteFile
6812 Lines starting with @code{#} are ignored.
6814 %end-doc */
6816 static int
6817 ActionExecuteFile (int argc, char **argv, Coord x, Coord y)
6819 FILE *fp;
6820 char *fname;
6821 char line[256];
6822 int n = 0;
6823 char *sp;
6825 if (argc != 1)
6826 AFAIL (executefile);
6828 fname = argv[0];
6830 if ((fp = fopen (fname, "r")) == NULL)
6832 fprintf (stderr, _("Could not open actions file \"%s\".\n"), fname);
6833 return 1;
6836 defer_updates = 1;
6837 defer_needs_update = 0;
6838 while (fgets (line, sizeof (line), fp) != NULL)
6840 n++;
6841 sp = line;
6843 /* eat the trailing newline */
6844 while (*sp && *sp != '\r' && *sp != '\n')
6845 sp++;
6846 *sp = '\0';
6848 /* eat leading spaces and tabs */
6849 sp = line;
6850 while (*sp && (*sp == ' ' || *sp == '\t'))
6851 sp++;
6854 * if we have anything left and its not a comment line
6855 * then execute it
6858 if (*sp && *sp != '#')
6860 /*Message ("%s : line %-3d : \"%s\"\n", fname, n, sp);*/
6861 hid_parse_actions (sp);
6865 defer_updates = 0;
6866 if (defer_needs_update)
6868 IncrementUndoSerialNumber ();
6869 gui->invalidate_all ();
6871 fclose (fp);
6872 return 0;
6875 /* --------------------------------------------------------------------------- */
6877 static int
6878 ActionPSCalib (int argc, char **argv, Coord x, Coord y)
6880 HID *ps = hid_find_exporter ("ps");
6881 ps->calibrate (0.0,0.0);
6882 return 0;
6885 /* --------------------------------------------------------------------------- */
6887 static ElementType *element_cache = NULL;
6889 static ElementType *
6890 find_element_by_refdes (char *refdes)
6892 if (element_cache
6893 && NAMEONPCB_NAME(element_cache)
6894 && strcmp (NAMEONPCB_NAME(element_cache), refdes) == 0)
6895 return element_cache;
6897 ELEMENT_LOOP (PCB->Data);
6899 if (NAMEONPCB_NAME(element)
6900 && strcmp (NAMEONPCB_NAME(element), refdes) == 0)
6902 element_cache = element;
6903 return element_cache;
6906 END_LOOP;
6907 return NULL;
6910 static AttributeType *
6911 lookup_attr (AttributeListTypePtr list, const char *name)
6913 int i;
6914 for (i=0; i<list->Number; i++)
6915 if (strcmp (list->List[i].name, name) == 0)
6916 return & list->List[i];
6917 return NULL;
6920 static void
6921 delete_attr (AttributeListTypePtr list, AttributeType *attr)
6923 int idx = attr - list->List;
6924 if (idx < 0 || idx >= list->Number)
6925 return;
6926 if (list->Number - idx > 1)
6927 memmove (attr, attr+1, (list->Number - idx - 1) * sizeof(AttributeType));
6928 list->Number --;
6931 /* ---------------------------------------------------------------- */
6932 static const char elementlist_syntax[] = "ElementList(Start|Done|Need,<refdes>,<footprint>,<value>)";
6934 static const char elementlist_help[] = "Adds the given element if it doesn't already exist.";
6936 /* %start-doc actions elementlist
6938 @table @code
6940 @item Start
6941 Indicates the start of an element list; call this before any Need
6942 actions.
6944 @item Need
6945 Searches the board for an element with a matching refdes.
6947 If found, the value and footprint are updated.
6949 If not found, a new element is created with the given footprint and value.
6951 @item Done
6952 Compares the list of elements needed since the most recent
6953 @code{start} with the list of elements actually on the board. Any
6954 elements that weren't listed are selected, so that the user may delete
6955 them.
6957 @end table
6959 %end-doc */
6961 static int number_of_footprints_not_found;
6963 static int
6964 parse_layout_attribute_units (char *name, int def)
6966 const char *as = AttributeGet (PCB, name);
6967 if (!as)
6968 return def;
6969 return GetValue (as, NULL, NULL);
6972 static int
6973 ActionElementList (int argc, char **argv, Coord x, Coord y)
6975 ElementType *e = NULL;
6976 char *refdes, *value, *footprint, *old;
6977 char *args[3];
6978 char *function = argv[0];
6980 #ifdef DEBUG
6981 printf("Entered ActionElementList, executing function %s\n", function);
6982 #endif
6984 if (strcasecmp (function, "start") == 0)
6986 ELEMENT_LOOP (PCB->Data);
6988 CLEAR_FLAG (FOUNDFLAG, element);
6990 END_LOOP;
6991 element_cache = NULL;
6992 number_of_footprints_not_found = 0;
6993 return 0;
6996 if (strcasecmp (function, "done") == 0)
6998 ELEMENT_LOOP (PCB->Data);
7000 if (TEST_FLAG (FOUNDFLAG, element))
7002 CLEAR_FLAG (FOUNDFLAG, element);
7004 else if (! EMPTY_STRING_P (NAMEONPCB_NAME (element)))
7006 /* Unnamed elements should remain untouched */
7007 SET_FLAG (SELECTEDFLAG, element);
7010 END_LOOP;
7011 if (number_of_footprints_not_found > 0)
7012 gui->confirm_dialog ("Not all requested footprints were found.\n"
7013 "See the message log for details",
7014 "Ok", NULL);
7015 return 0;
7018 if (strcasecmp (function, "need") != 0)
7019 AFAIL (elementlist);
7021 if (argc != 4)
7022 AFAIL (elementlist);
7024 argc --;
7025 argv ++;
7027 refdes = ARG(0);
7028 footprint = ARG(1);
7029 value = ARG(2);
7031 args[0] = footprint;
7032 args[1] = refdes;
7033 args[2] = value;
7035 #ifdef DEBUG
7036 printf(" ... footprint = %s\n", footprint);
7037 printf(" ... refdes = %s\n", refdes);
7038 printf(" ... value = %s\n", value);
7039 #endif
7041 e = find_element_by_refdes (refdes);
7043 if (!e)
7045 Coord nx, ny, d;
7047 #ifdef DEBUG
7048 printf(" ... Footprint not on board, need to add it.\n");
7049 #endif
7050 /* Not on board, need to add it. */
7051 if (LoadFootprint(argc, args, x, y))
7053 number_of_footprints_not_found ++;
7054 return 1;
7057 nx = PCB->MaxWidth / 2;
7058 ny = PCB->MaxHeight / 2;
7059 d = MIN (PCB->MaxWidth, PCB->MaxHeight) / 10;
7061 nx = parse_layout_attribute_units ("import::newX", nx);
7062 ny = parse_layout_attribute_units ("import::newY", ny);
7063 d = parse_layout_attribute_units ("import::disperse", d);
7065 if (d > 0)
7067 nx += rand () % (d*2) - d;
7068 ny += rand () % (d*2) - d;
7071 if (nx < 0)
7072 nx = 0;
7073 if (nx >= PCB->MaxWidth)
7074 nx = PCB->MaxWidth - 1;
7075 if (ny < 0)
7076 ny = 0;
7077 if (ny >= PCB->MaxHeight)
7078 ny = PCB->MaxHeight - 1;
7080 /* Place components onto center of board. */
7081 if (CopyPastebufferToLayout (nx, ny))
7082 SetChangedFlag (true);
7085 else if (e && strcmp (DESCRIPTION_NAME(e), footprint) != 0)
7087 #ifdef DEBUG
7088 printf(" ... Footprint on board, but different from footprint loaded.\n");
7089 #endif
7090 int er, pr, i;
7091 Coord mx, my;
7092 ElementType *pe;
7094 /* Different footprint, we need to swap them out. */
7095 if (LoadFootprint(argc, args, x, y))
7097 number_of_footprints_not_found ++;
7098 return 1;
7101 er = ElementOrientation (e);
7102 pe = PASTEBUFFER->Data->Element->data;
7103 if (!FRONT (e))
7104 MirrorElementCoordinates (PASTEBUFFER->Data, pe, pe->MarkY*2 - PCB->MaxHeight);
7105 pr = ElementOrientation (pe);
7107 mx = e->MarkX;
7108 my = e->MarkY;
7110 if (er != pr)
7111 RotateElementLowLevel (PASTEBUFFER->Data, pe, pe->MarkX, pe->MarkY, (er-pr+4)%4);
7113 for (i=0; i<MAX_ELEMENTNAMES; i++)
7115 pe->Name[i].X = e->Name[i].X - mx + pe->MarkX ;
7116 pe->Name[i].Y = e->Name[i].Y - my + pe->MarkY ;
7117 pe->Name[i].Direction = e->Name[i].Direction;
7118 pe->Name[i].Scale = e->Name[i].Scale;
7121 RemoveElement (e);
7123 if (CopyPastebufferToLayout (mx, my))
7124 SetChangedFlag (true);
7127 /* Now reload footprint */
7128 element_cache = NULL;
7129 e = find_element_by_refdes (refdes);
7131 old = ChangeElementText (PCB, PCB->Data, e, NAMEONPCB_INDEX, strdup (refdes));
7132 if (old)
7133 free(old);
7134 old = ChangeElementText (PCB, PCB->Data, e, VALUE_INDEX, strdup (value));
7135 if (old)
7136 free(old);
7138 SET_FLAG (FOUNDFLAG, e);
7140 #ifdef DEBUG
7141 printf(" ... Leaving ActionElementList.\n");
7142 #endif
7144 return 0;
7147 /* ---------------------------------------------------------------- */
7148 static const char elementsetattr_syntax[] = "ElementSetAttr(refdes,name[,value])";
7150 static const char elementsetattr_help[] = "Sets or clears an element-specific attribute.";
7152 /* %start-doc actions elementsetattr
7154 If a value is specified, the named attribute is added (if not already
7155 present) or changed (if it is) to the given value. If the value is
7156 not specified, the given attribute is removed if present.
7158 %end-doc */
7160 static int
7161 ActionElementSetAttr (int argc, char **argv, Coord x, Coord y)
7163 ElementType *e = NULL;
7164 char *refdes, *name, *value;
7165 AttributeType *attr;
7167 if (argc < 2)
7169 AFAIL (changepinname);
7172 refdes = argv[0];
7173 name = argv[1];
7174 value = ARG(2);
7176 ELEMENT_LOOP (PCB->Data);
7178 if (NSTRCMP (refdes, NAMEONPCB_NAME (element)) == 0)
7180 e = element;
7181 break;
7184 END_LOOP;
7186 if (!e)
7188 Message(_("Cannot change attribute of %s - element not found\n"), refdes);
7189 return 1;
7192 attr = lookup_attr (&e->Attributes, name);
7194 if (attr && value)
7196 free (attr->value);
7197 attr->value = strdup (value);
7199 if (attr && ! value)
7201 delete_attr (& e->Attributes, attr);
7203 if (!attr && value)
7205 CreateNewAttribute (& e->Attributes, name, value);
7208 return 0;
7211 /* ---------------------------------------------------------------- */
7212 static const char execcommand_syntax[] = "ExecCommand(command)";
7214 static const char execcommand_help[] = "Runs a command.";
7216 /* %start-doc actions execcommand
7218 Runs the given command, which is a system executable.
7220 %end-doc */
7222 static int
7223 ActionExecCommand (int argc, char **argv, Coord x, Coord y)
7225 char *command;
7227 if (argc < 1)
7229 AFAIL (execcommand);
7232 command = ARG(0);
7234 if (system (command))
7235 return 1;
7236 return 0;
7239 /* ---------------------------------------------------------------- */
7241 static int
7242 pcb_spawnvp (char **argv)
7244 #ifdef HAVE__SPAWNVP
7245 int result = _spawnvp (_P_WAIT, argv[0], (const char * const *) argv);
7246 if (result == -1)
7247 return 1;
7248 else
7249 return 0;
7250 #else
7251 int pid;
7252 pid = fork ();
7253 if (pid < 0)
7255 /* error */
7256 Message(_("Cannot fork!"));
7257 return 1;
7259 else if (pid == 0)
7261 /* Child */
7262 execvp (argv[0], argv);
7263 exit(1);
7265 else
7267 int rv;
7268 /* Parent */
7269 wait (&rv);
7271 return 0;
7272 #endif
7275 /* ---------------------------------------------------------------- */
7277 * Creates a new temporary file name. Hopefully the operating system
7278 * provides a mkdtemp() function to securily create a temporary
7279 * directory with mode 0700. If so then that directory is created and
7280 * the returned string is made up of the directory plus the name
7281 * variable. For example:
7283 * tempfile_name_new ("myfile") might return
7284 * "/var/tmp/pcb.123456/myfile".
7286 * If mkdtemp() is not available then 'name' is ignored and the
7287 * insecure tmpnam() function is used.
7289 * Files/names created with tempfile_name_new() should be unlinked
7290 * with tempfile_unlink to make sure the temporary directory is also
7291 * removed when mkdtemp() is used.
7293 static char *
7294 tempfile_name_new (char * name)
7296 char *tmpfile = NULL;
7297 #ifdef HAVE_MKDTEMP
7298 char *tmpdir, *mytmpdir;
7299 size_t len;
7300 #endif
7302 assert ( name != NULL );
7304 #ifdef HAVE_MKDTEMP
7305 #define TEMPLATE "pcb.XXXXXXXX"
7308 tmpdir = getenv ("TMPDIR");
7310 /* FIXME -- what about win32? */
7311 if (tmpdir == NULL) {
7312 tmpdir = "/tmp";
7315 mytmpdir = (char *) malloc (sizeof(char) *
7316 (strlen (tmpdir) +
7318 strlen (TEMPLATE) +
7319 1));
7320 if (mytmpdir == NULL) {
7321 fprintf (stderr, "%s(): malloc failed()\n", __FUNCTION__);
7322 exit (1);
7325 *mytmpdir = '\0';
7326 (void)strcat (mytmpdir, tmpdir);
7327 (void)strcat (mytmpdir, PCB_DIR_SEPARATOR_S);
7328 (void)strcat (mytmpdir, TEMPLATE);
7329 if (mkdtemp (mytmpdir) == NULL) {
7330 fprintf (stderr, "%s(): mkdtemp (\"%s\") failed\n", __FUNCTION__, mytmpdir);
7331 free (mytmpdir);
7332 return NULL;
7336 len = strlen (mytmpdir) + /* the temp directory name */
7337 1 + /* the directory sep. */
7338 strlen (name) + /* the file name */
7339 1 /* the \0 termination */
7342 tmpfile = (char *) malloc (sizeof (char) * len);
7344 *tmpfile = '\0';
7345 (void)strcat (tmpfile, mytmpdir);
7346 (void)strcat (tmpfile, PCB_DIR_SEPARATOR_S);
7347 (void)strcat (tmpfile, name);
7349 free (mytmpdir);
7350 #undef TEMPLATE
7351 #else
7353 * tmpnam() uses a static buffer so strdup() the result right away
7354 * in case someone decides to create multiple temp names.
7356 tmpfile = strdup (tmpnam (NULL));
7357 #endif
7359 return tmpfile;
7362 /* ---------------------------------------------------------------- */
7364 * Unlink a temporary file. If we have mkdtemp() then our temp file
7365 * lives in a temporary directory and we need to remove that directory
7366 * too.
7368 static int
7369 tempfile_unlink (char * name)
7371 #ifdef DEBUG
7372 /* SDB says: Want to keep old temp files for examiniation when debugging */
7373 return 0;
7374 #endif
7376 #ifdef HAVE_MKDTEMP
7377 int e, rc2 = 0;
7378 char *dname;
7380 unlink (name);
7381 /* it is possible that the file was never created so it is OK if the
7382 unlink fails */
7384 /* now figure out the directory name to remove */
7385 e = strlen (name) - 1;
7386 while (e > 0 && name[e] != PCB_DIR_SEPARATOR_C) {e--;}
7388 dname = strdup (name);
7389 dname[e] = '\0';
7392 * at this point, e *should* point to the end of the directory part
7393 * but lets make sure.
7395 if (e > 0) {
7396 rc2 = rmdir (dname);
7397 if (rc2 != 0) {
7398 perror (dname);
7401 } else {
7402 fprintf (stderr, _("%s(): Unable to determine temp directory name from the temp file\n"),
7403 __FUNCTION__);
7404 fprintf (stderr, "%s(): \"%s\"\n",
7405 __FUNCTION__, name);
7406 rc2 = -1;
7409 /* name was allocated with malloc */
7410 free (dname);
7411 free (name);
7414 * FIXME - should also return -1 if the temp file exists and was not
7415 * removed.
7417 if (rc2 != 0) {
7418 return -1;
7421 #else
7422 int rc = unlink (name);
7424 if (rc != 0) {
7425 fprintf (stderr, _("Failed to unlink \"%s\"\n"), name);
7426 free (name);
7427 return rc;
7429 free (name);
7431 #endif
7433 return 0;
7436 /* ---------------------------------------------------------------- */
7437 static const char import_syntax[] =
7438 "Import()\n"
7439 "Import([gnetlist|make[,source,source,...]])\n"
7440 "Import(setnewpoint[,(mark|center|X,Y)])\n"
7441 "Import(setdisperse,D,units)\n";
7443 static const char import_help[] = "Import schematics.";
7445 /* %start-doc actions Import
7447 Imports element and netlist data from the schematics (or some other
7448 source). The first parameter, which is optional, is the mode. If not
7449 specified, the @code{import::mode} attribute in the PCB is used.
7450 @code{gnetlist} means gnetlist is used to obtain the information from
7451 the schematics. @code{make} invokes @code{make}, assuming the user
7452 has a @code{Makefile} in the current directory. The @code{Makefile}
7453 will be invoked with the following variables set:
7455 @table @code
7457 @item PCB
7458 The name of the .pcb file
7460 @item SRCLIST
7461 A space-separated list of source files
7463 @item OUT
7464 The name of the file in which to put the command script, which may
7465 contain any @pcb{} actions. By default, this is a temporary file
7466 selected by @pcb{}, but if you specify an @code{import::outfile}
7467 attribute, that file name is used instead (and not automatically
7468 deleted afterwards).
7470 @end table
7472 The target specified to be built is the first of these that apply:
7474 @itemize @bullet
7476 @item
7477 The target specified by an @code{import::target} attribute.
7479 @item
7480 The output file specified by an @code{import::outfile} attribute.
7482 @item
7483 If nothing else is specified, the target is @code{pcb_import}.
7485 @end itemize
7487 If you specify an @code{import::makefile} attribute, then "-f <that
7488 file>" will be added to the command line.
7490 If you specify the mode, you may also specify the source files
7491 (schematics). If you do not specify any, the list of schematics is
7492 obtained by reading the @code{import::src@var{N}} attributes (like
7493 @code{import::src0}, @code{import::src1}, etc).
7495 For compatibility with future extensions to the import file format,
7496 the generated file @emph{must not} start with the two characters
7497 @code{#%}.
7499 If a temporary file is needed the @code{TMPDIR} environment variable
7500 is used to select its location.
7502 Note that the programs @code{gnetlist} and @code{make} may be
7503 overridden by the user via the @code{make-program} and @code{gnetlist}
7504 @code{pcb} settings (i.e. in @code{~/.pcb/settings} or on the command
7505 line).
7507 If @pcb{} cannot determine which schematic(s) to import from, the GUI
7508 is called to let user choose (see @code{ImportGUI()}).
7510 Note that Import() doesn't delete anything - after an Import, elements
7511 which shouldn't be on the board are selected and may be removed once
7512 it's determined that the deletion is appropriate.
7514 If @code{Import()} is called with @code{setnewpoint}, then the location
7515 of new components can be specified. This is where parts show up when
7516 they're added to the board. The default is the center of the board.
7518 @table @code
7520 @item Import(setnewpoint)
7522 Prompts the user to click on the board somewhere, uses that point. If
7523 called by a hotkey, uses the current location of the crosshair.
7525 @item Import(setnewpoint,mark)
7527 Uses the location of the mark. If no mark is present, the point is
7528 not changed.
7530 @item Import(setnewpoint,center)
7532 Resets the point to the center of the board.
7534 @item Import(setnewpoint,X,Y,units)
7536 Sets the point to the specific coordinates given. Example:
7537 @code{Import(setnewpoint,50,25,mm)}
7539 @end table
7541 Note that the X and Y locations are stored in attributes named
7542 @code{import::newX} and @code{import::newY} so you could change them
7543 manually if you wished.
7545 Calling @code{Import(setdisperse,D,units)} sets how much the newly
7546 placed elements are dispersed relative to the set point. For example,
7547 @code{Import(setdisperse,10,mm)} will offset each part randomly up to
7548 10mm away from the point. The default dispersion is 1/10th of the
7549 smallest board dimension. Dispersion is saved in the
7550 @code{import::disperse} attribute.
7552 %end-doc */
7554 static int
7555 ActionImport (int argc, char **argv, Coord x, Coord y)
7557 char *mode;
7558 char **sources = NULL;
7559 int nsources = 0;
7561 #ifdef DEBUG
7562 printf("ActionImport: =========== Entering ActionImport ============\n");
7563 #endif
7565 mode = ARG (0);
7567 if (mode && strcasecmp (mode, "setdisperse") == 0)
7569 char *ds, *units;
7570 char buf[50];
7572 ds = ARG (1);
7573 units = ARG (2);
7574 if (!ds)
7576 const char *as = AttributeGet (PCB, "import::disperse");
7577 ds = gui->prompt_for(_("Enter dispersion:"), as ? as : "0");
7579 if (units)
7581 sprintf(buf, "%s%s", ds, units);
7582 AttributePut (PCB, "import::disperse", buf);
7584 else
7585 AttributePut (PCB, "import::disperse", ds);
7586 if (ARG (1) == NULL)
7587 free (ds);
7588 return 0;
7591 if (mode && strcasecmp (mode, "setnewpoint") == 0)
7593 const char *xs, *ys, *units;
7594 Coord x, y;
7595 char buf[50];
7597 xs = ARG (1);
7598 ys = ARG (2);
7599 units = ARG (3);
7601 if (!xs)
7603 gui->get_coords (_("Click on a location"), &x, &y);
7605 else if (strcasecmp (xs, "center") == 0)
7607 AttributeRemove (PCB, "import::newX");
7608 AttributeRemove (PCB, "import::newY");
7609 return 0;
7611 else if (strcasecmp (xs, "mark") == 0)
7613 if (Marked.status)
7615 x = Marked.X;
7616 y = Marked.Y;
7619 else if (ys)
7621 x = GetValue (xs, units, NULL);
7622 y = GetValue (ys, units, NULL);
7624 else
7626 Message (_("Bad syntax for Import(setnewpoint)"));
7627 return 1;
7630 pcb_sprintf (buf, "%$ms", x);
7631 AttributePut (PCB, "import::newX", buf);
7632 pcb_sprintf (buf, "%$ms", y);
7633 AttributePut (PCB, "import::newY", buf);
7634 return 0;
7637 if (! mode)
7638 mode = AttributeGet (PCB, "import::mode");
7639 if (! mode)
7640 mode = "gnetlist";
7642 if (argc > 1)
7644 sources = argv + 1;
7645 nsources = argc - 1;
7648 if (! sources)
7650 char sname[40];
7651 char *src;
7653 nsources = -1;
7654 do {
7655 nsources ++;
7656 sprintf(sname, "import::src%d", nsources);
7657 src = AttributeGet (PCB, sname);
7658 } while (src);
7660 if (nsources > 0)
7662 sources = (char **) malloc ((nsources + 1) * sizeof (char *));
7663 nsources = -1;
7664 do {
7665 nsources ++;
7666 sprintf(sname, "import::src%d", nsources);
7667 src = AttributeGet (PCB, sname);
7668 sources[nsources] = src;
7669 } while (src);
7673 if (! sources)
7675 /* Replace .pcb with .sch and hope for the best. */
7676 char *pcbname = PCB->Filename;
7677 char *schname;
7678 char *dot, *slash, *bslash;
7680 if (!pcbname)
7681 return hid_action("ImportGUI");
7683 schname = (char *) malloc (strlen(pcbname) + 5);
7684 strcpy (schname, pcbname);
7685 dot = strchr (schname, '.');
7686 slash = strchr (schname, '/');
7687 bslash = strchr (schname, '\\');
7688 if (dot && slash && dot < slash)
7689 dot = NULL;
7690 if (dot && bslash && dot < bslash)
7691 dot = NULL;
7692 if (dot)
7693 *dot = 0;
7694 strcat (schname, ".sch");
7696 if (access (schname, F_OK))
7697 return hid_action("ImportGUI");
7699 sources = (char **) malloc (2 * sizeof (char *));
7700 sources[0] = schname;
7701 sources[1] = NULL;
7702 nsources = 1;
7705 if (strcasecmp (mode, "gnetlist") == 0)
7707 char *tmpfile = tempfile_name_new ("gnetlist_output");
7708 char **cmd;
7709 int i;
7711 if (tmpfile == NULL) {
7712 Message (_("Could not create temp file"));
7713 return 1;
7716 cmd = (char **) malloc ((7 + nsources) * sizeof (char *));
7717 cmd[0] = Settings.GnetlistProgram;
7718 cmd[1] = "-g";
7719 cmd[2] = "pcbfwd";
7720 cmd[3] = "-o";
7721 cmd[4] = tmpfile;
7722 cmd[5] = "--";
7723 for (i=0; i<nsources; i++)
7724 cmd[6+i] = sources[i];
7725 cmd[6+nsources] = NULL;
7727 #ifdef DEBUG
7728 printf("ActionImport: =========== About to run gnetlist ============\n");
7729 printf("%s %s %s %s %s %s %s ...\n",
7730 cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6]);
7731 #endif
7733 if (pcb_spawnvp (cmd))
7735 unlink (tmpfile);
7736 return 1;
7739 #ifdef DEBUG
7740 printf("ActionImport: =========== About to run ActionExecuteFile, file = %s ============\n", tmpfile);
7741 #endif
7743 cmd[0] = tmpfile;
7744 cmd[1] = NULL;
7745 ActionExecuteFile (1, cmd, 0, 0);
7747 free (cmd);
7748 tempfile_unlink (tmpfile);
7750 else if (strcasecmp (mode, "make") == 0)
7752 int must_free_tmpfile = 0;
7753 char *tmpfile;
7754 char *cmd[10];
7755 int i;
7756 char *srclist;
7757 int srclen;
7758 char *user_outfile = NULL;
7759 char *user_makefile = NULL;
7760 char *user_target = NULL;
7763 user_outfile = AttributeGet (PCB, "import::outfile");
7764 user_makefile = AttributeGet (PCB, "import::makefile");
7765 user_target = AttributeGet (PCB, "import::target");
7766 if (user_outfile && !user_target)
7767 user_target = user_outfile;
7769 if (user_outfile)
7770 tmpfile = user_outfile;
7771 else
7773 tmpfile = tempfile_name_new ("gnetlist_output");
7774 if (tmpfile == NULL) {
7775 Message (_("Could not create temp file"));
7776 return 1;
7778 must_free_tmpfile = 1;
7781 srclen = sizeof("SRCLIST=") + 2;
7782 for (i=0; i<nsources; i++)
7783 srclen += strlen (sources[i]) + 2;
7784 srclist = (char *) malloc (srclen);
7785 strcpy (srclist, "SRCLIST=");
7786 for (i=0; i<nsources; i++)
7788 if (i)
7789 strcat (srclist, " ");
7790 strcat (srclist, sources[i]);
7793 cmd[0] = Settings.MakeProgram;
7794 cmd[1] = "-s";
7795 cmd[2] = Concat ("PCB=", PCB->Filename, NULL);
7796 cmd[3] = srclist;
7797 cmd[4] = Concat ("OUT=", tmpfile, NULL);
7798 i = 5;
7799 if (user_makefile)
7801 cmd[i++] = "-f";
7802 cmd[i++] = user_makefile;
7804 cmd[i++] = user_target ? user_target : (char *)"pcb_import";
7805 cmd[i++] = NULL;
7807 if (pcb_spawnvp (cmd))
7809 if (must_free_tmpfile)
7810 unlink (tmpfile);
7811 free (cmd[2]);
7812 free (cmd[3]);
7813 free (cmd[4]);
7814 return 1;
7817 cmd[0] = tmpfile;
7818 cmd[1] = NULL;
7819 ActionExecuteFile (1, cmd, 0, 0);
7821 free (cmd[2]);
7822 free (cmd[3]);
7823 free (cmd[4]);
7824 if (must_free_tmpfile)
7825 tempfile_unlink (tmpfile);
7827 else
7829 Message (_("Unknown import mode: %s\n"), mode);
7830 return 1;
7833 DeleteRats (false);
7834 AddAllRats (false, NULL);
7836 #ifdef DEBUG
7837 printf("ActionImport: =========== Leaving ActionImport ============\n");
7838 #endif
7840 return 0;
7843 /* ------------------------------------------------------------ */
7845 static const char attributes_syntax[] =
7846 "Attributes(Layout|Layer|Element)\n"
7847 "Attributes(Layer,layername)";
7849 static const char attributes_help[] =
7850 "Let the user edit the attributes of the layout, current or given\n"
7851 "layer, or selected element.";
7853 /* %start-doc actions Attributes
7855 This just pops up a dialog letting the user edit the attributes of the
7856 pcb, an element, or a layer.
7858 %end-doc */
7861 static int
7862 ActionAttributes (int argc, char **argv, Coord x, Coord y)
7864 char *function = ARG (0);
7865 char *layername = ARG (1);
7866 char *buf;
7868 if (!function)
7869 AFAIL (attributes);
7871 if (!gui->edit_attributes)
7873 Message (_("This GUI doesn't support Attribute Editing\n"));
7874 return 1;
7877 switch (GetFunctionID (function))
7879 case F_Layout:
7881 gui->edit_attributes("Layout Attributes", &(PCB->Attributes));
7882 return 0;
7885 case F_Layer:
7887 LayerType *layer = CURRENT;
7888 if (layername)
7890 int i;
7891 layer = NULL;
7892 for (i=0; i<max_copper_layer; i++)
7893 if (strcmp (PCB->Data->Layer[i].Name, layername) == 0)
7895 layer = & (PCB->Data->Layer[i]);
7896 break;
7898 if (layer == NULL)
7900 Message (_("No layer named %s\n"), layername);
7901 return 1;
7904 buf = (char *) malloc (strlen (layer->Name) + strlen ("Layer X Attributes"));
7905 sprintf (buf, "Layer %s Attributes", layer->Name);
7906 gui->edit_attributes(buf, &(layer->Attributes));
7907 free (buf);
7908 return 0;
7911 case F_Element:
7913 int n_found = 0;
7914 ElementType *e = NULL;
7915 ELEMENT_LOOP (PCB->Data);
7917 if (TEST_FLAG (SELECTEDFLAG, element))
7919 e = element;
7920 n_found ++;
7923 END_LOOP;
7924 if (n_found > 1)
7926 Message (_("Too many elements selected\n"));
7927 return 1;
7929 if (n_found == 0)
7931 void *ptrtmp;
7932 gui->get_coords (_("Click on an element"), &x, &y);
7933 if ((SearchScreen
7934 (x, y, ELEMENT_TYPE, &ptrtmp,
7935 &ptrtmp, &ptrtmp)) != NO_TYPE)
7936 e = (ElementTypePtr) ptrtmp;
7937 else
7939 Message (_("No element found there\n"));
7940 return 1;
7944 if (NAMEONPCB_NAME(e))
7946 buf = (char *) malloc (strlen (NAMEONPCB_NAME(e)) + strlen ("Element X Attributes"));
7947 sprintf(buf, "Element %s Attributes", NAMEONPCB_NAME(e));
7949 else
7951 buf = strdup ("Unnamed Element Attributes");
7953 gui->edit_attributes(buf, &(e->Attributes));
7954 free (buf);
7955 break;
7958 default:
7959 AFAIL (attributes);
7962 return 0;
7965 /* --------------------------------------------------------------------------- */
7967 HID_Action action_action_list[] = {
7968 {"AddRats", 0, ActionAddRats,
7969 addrats_help, addrats_syntax}
7971 {"Attributes", 0, ActionAttributes,
7972 attributes_help, attributes_syntax}
7974 {"Atomic", 0, ActionAtomic,
7975 atomic_help, atomic_syntax}
7977 {"AutoPlaceSelected", 0, ActionAutoPlaceSelected,
7978 autoplace_help, autoplace_syntax}
7980 {"AutoRoute", 0, ActionAutoRoute,
7981 autoroute_help, autoroute_syntax}
7983 {"ChangeClearSize", 0, ActionChangeClearSize,
7984 changeclearsize_help, changeclearsize_syntax}
7986 {"ChangeDrillSize", 0, ActionChange2ndSize,
7987 changedrillsize_help, changedrillsize_syntax}
7989 {"ChangeHole", 0, ActionChangeHole,
7990 changehold_help, changehold_syntax}
7992 {"ChangeJoin", 0, ActionChangeJoin,
7993 changejoin_help, changejoin_syntax}
7995 {"ChangeName", 0, ActionChangeName,
7996 changename_help, changename_syntax}
7998 {"ChangePaste", 0, ActionChangePaste,
7999 changepaste_help, changepaste_syntax}
8001 {"ChangePinName", 0, ActionChangePinName,
8002 changepinname_help, changepinname_syntax}
8004 {"ChangeSize", 0, ActionChangeSize,
8005 changesize_help, changesize_syntax}
8007 {"ChangeSquare", 0, ActionChangeSquare,
8008 changesquare_help, changesquare_syntax}
8010 {"ChangeOctagon", 0, ActionChangeOctagon,
8011 changeoctagon_help, changeoctagon_syntax}
8013 {"ClearSquare", 0, ActionClearSquare,
8014 clearsquare_help, clearsquare_syntax}
8016 {"ClearOctagon", 0, ActionClearOctagon,
8017 clearoctagon_help, clearoctagon_syntax}
8019 {"Connection", 0, ActionConnection,
8020 connection_help, connection_syntax}
8022 {"Delete", 0, ActionDelete,
8023 delete_help, delete_syntax}
8025 {"DeleteRats", 0, ActionDeleteRats,
8026 deleterats_help, deleterats_syntax}
8028 {"DisperseElements", 0, ActionDisperseElements,
8029 disperseelements_help, disperseelements_syntax}
8031 {"Display", 0, ActionDisplay,
8032 display_help, display_syntax}
8034 {"DRC", 0, ActionDRCheck,
8035 drc_help, drc_syntax}
8037 {"DumpLibrary", 0, ActionDumpLibrary,
8038 dumplibrary_help, dumplibrary_syntax}
8040 {"ExecuteFile", 0, ActionExecuteFile,
8041 executefile_help, executefile_syntax}
8043 {"Flip", N_("Click on Object or Flip Point"), ActionFlip,
8044 flip_help, flip_syntax}
8046 {"LoadFrom", 0, ActionLoadFrom,
8047 loadfrom_help, loadfrom_syntax}
8049 {"MarkCrosshair", 0, ActionMarkCrosshair,
8050 markcrosshair_help, markcrosshair_syntax}
8052 {"Message", 0, ActionMessage,
8053 message_help, message_syntax}
8055 {"MinMaskGap", 0, ActionMinMaskGap,
8056 minmaskgap_help, minmaskgap_syntax}
8058 {"MinClearGap", 0, ActionMinClearGap,
8059 mincleargap_help, mincleargap_syntax}
8061 {"Mode", 0, ActionMode,
8062 mode_help, mode_syntax}
8064 {"MorphPolygon", 0, ActionMorphPolygon,
8065 morphpolygon_help, morphpolygon_syntax}
8067 {"PasteBuffer", 0, ActionPasteBuffer,
8068 pastebuffer_help, pastebuffer_syntax}
8070 {"Quit", 0, ActionQuit,
8071 quit_help, quit_syntax}
8073 {"RemoveSelected", 0, ActionRemoveSelected,
8074 removeselected_help, removeselected_syntax}
8076 {"Renumber", 0, ActionRenumber,
8077 renumber_help, renumber_syntax}
8079 {"RipUp", 0, ActionRipUp,
8080 ripup_help, ripup_syntax}
8082 {"Select", 0, ActionSelect,
8083 select_help, select_syntax}
8085 {"Unselect", 0, ActionUnselect,
8086 unselect_help, unselect_syntax}
8088 {"SaveSettings", 0, ActionSaveSettings,
8089 savesettings_help, savesettings_syntax}
8091 {"SaveTo", 0, ActionSaveTo,
8092 saveto_help, saveto_syntax}
8094 {"SetSquare", 0, ActionSetSquare,
8095 setsquare_help, setsquare_syntax}
8097 {"SetOctagon", 0, ActionSetOctagon,
8098 setoctagon_help, setoctagon_syntax}
8100 {"SetThermal", 0, ActionSetThermal,
8101 setthermal_help, setthermal_syntax}
8103 {"SetValue", 0, ActionSetValue,
8104 setvalue_help, setvalue_syntax}
8106 {"ToggleHideName", 0, ActionToggleHideName,
8107 togglehidename_help, togglehidename_syntax}
8109 {"Undo", 0, ActionUndo,
8110 undo_help, undo_syntax}
8112 {"Redo", 0, ActionRedo,
8113 redo_help, redo_syntax}
8115 {"SetSame", N_("Select item to use attributes from"), ActionSetSame,
8116 setsame_help, setsame_syntax}
8118 {"SetFlag", 0, ActionSetFlag,
8119 setflag_help, setflag_syntax}
8121 {"ClrFlag", 0, ActionClrFlag,
8122 clrflag_help, clrflag_syntax}
8124 {"ChangeFlag", 0, ActionChangeFlag,
8125 changeflag_help, changeflag_syntax}
8127 {"Polygon", 0, ActionPolygon,
8128 polygon_help, polygon_syntax}
8130 {"RouteStyle", 0, ActionRouteStyle,
8131 routestyle_help, routestyle_syntax}
8133 {"MoveObject", N_("Select an Object"), ActionMoveObject,
8134 moveobject_help, moveobject_syntax}
8136 {"MoveToCurrentLayer", 0, ActionMoveToCurrentLayer,
8137 movetocurrentlayer_help, movetocurrentlayer_syntax}
8139 {"New", 0, ActionNew,
8140 new_help, new_syntax}
8142 {"pscalib", 0, ActionPSCalib}
8144 {"ElementList", 0, ActionElementList,
8145 elementlist_help, elementlist_syntax}
8147 {"ElementSetAttr", 0, ActionElementSetAttr,
8148 elementsetattr_help, elementsetattr_syntax}
8150 {"ExecCommand", 0, ActionExecCommand,
8151 execcommand_help, execcommand_syntax}
8153 {"Import", 0, ActionImport,
8154 import_help, import_syntax}
8158 REGISTER_ACTIONS (action_action_list)