select.c: Remove Draw() call from SelectConnection
[geda-pcb/leaky.git] / src / action.c
blob7a9c3f029fc027434f2f339b8845fa3735bbf447
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 "move.h"
59 #include "polygon.h"
60 /*#include "print.h"*/
61 #include "rats.h"
62 #include "remove.h"
63 #include "report.h"
64 #include "rotate.h"
65 #include "rubberband.h"
66 #include "search.h"
67 #include "select.h"
68 #include "set.h"
69 #include "thermal.h"
70 #include "undo.h"
71 #include "rtree.h"
72 #include "macro.h"
74 #include <assert.h>
76 #ifdef HAVE_LIBDMALLOC
77 #include <dmalloc.h>
78 #endif
80 /* for fork() and friends */
81 #ifdef HAVE_UNISTD_H
82 #include <unistd.h>
83 #endif
85 #ifdef HAVE_SYS_WAIT_H
86 #include <sys/wait.h>
87 #endif
89 /* ---------------------------------------------------------------------------
90 * some local types
92 typedef enum
94 F_AddSelected,
95 F_All,
96 F_AllConnections,
97 F_AllRats,
98 F_AllUnusedPins,
99 F_Arc,
100 F_Arrow,
101 F_Block,
102 F_Description,
103 F_Cancel,
104 F_Center,
105 F_Clear,
106 F_ClearAndRedraw,
107 F_ClearList,
108 F_Close,
109 F_Connection,
110 F_Convert,
111 F_Copy,
112 F_CycleClip,
113 F_CycleCrosshair,
114 F_DeleteRats,
115 F_Drag,
116 F_DrillReport,
117 F_Element,
118 F_ElementByName,
119 F_ElementConnections,
120 F_ElementToBuffer,
121 F_Escape,
122 F_Find,
123 F_FlipElement,
124 F_FoundPins,
125 F_Grid,
126 F_InsertPoint,
127 F_Layer,
128 F_Layout,
129 F_LayoutAs,
130 F_LayoutToBuffer,
131 F_Line,
132 F_LineSize,
133 F_Lock,
134 F_Mirror,
135 F_Move,
136 F_NameOnPCB,
137 F_Netlist,
138 F_None,
139 F_Notify,
140 F_Object,
141 F_ObjectByName,
142 F_PasteBuffer,
143 F_PadByName,
144 F_PinByName,
145 F_PinOrPadName,
146 F_Pinout,
147 F_Polygon,
148 F_PolygonHole,
149 F_PreviousPoint,
150 F_RatsNest,
151 F_Rectangle,
152 F_Redraw,
153 F_Release,
154 F_Revert,
155 F_Remove,
156 F_RemoveSelected,
157 F_Report,
158 F_Reset,
159 F_ResetLinesAndPolygons,
160 F_ResetPinsViasAndPads,
161 F_Restore,
162 F_Rotate,
163 F_Save,
164 F_Scroll,
165 F_Selected,
166 F_SelectedArcs,
167 F_SelectedElements,
168 F_SelectedLines,
169 F_SelectedNames,
170 F_SelectedObjects,
171 F_SelectedPads,
172 F_SelectedPins,
173 F_SelectedTexts,
174 F_SelectedVias,
175 F_SelectedRats,
176 F_Stroke,
177 F_Text,
178 F_TextByName,
179 F_TextScale,
180 F_Thermal,
181 F_ToLayout,
182 F_ToggleAllDirections,
183 F_ToggleAutoDRC,
184 F_ToggleClearLine,
185 F_ToggleFullPoly,
186 F_ToggleGrid,
187 F_ToggleHideNames,
188 F_ToggleMask,
189 F_ToggleName,
190 F_ToggleObject,
191 F_ToggleShowDRC,
192 F_ToggleLiveRoute,
193 F_ToggleRubberBandMode,
194 F_ToggleStartDirection,
195 F_ToggleSnapPin,
196 F_ToggleThindraw,
197 F_ToggleLockNames,
198 F_ToggleOnlyNames,
199 F_ToggleThindrawPoly,
200 F_ToggleOrthoMove,
201 F_ToggleLocalRef,
202 F_ToggleCheckPlanes,
203 F_ToggleUniqueNames,
204 F_Via,
205 F_ViaByName,
206 F_Value,
207 F_ViaDrillingHole,
208 F_ViaSize,
209 F_Zoom
211 FunctionID;
213 typedef struct /* used to identify subfunctions */
215 char *Identifier;
216 FunctionID ID;
218 FunctionType, *FunctionTypePtr;
220 /* --------------------------------------------------------------------------- */
222 /* %start-doc actions 00delta
224 Many actions take a @code{delta} parameter as the last parameter,
225 which is an amount to change something. That @code{delta} may include
226 units, as an additional parameter, such as @code{Action(Object,5,mm)}.
227 If no units are specified, the default is PCB's native units
228 (currently 1/100 mil). Also, if the delta is prefixed by @code{+} or
229 @code{-}, the size is increased or decreased by that amount.
230 Otherwise, the size size is set to the given amount.
232 @example
233 Action(Object,5,mil)
234 Action(Object,+0.5,mm)
235 Action(Object,-1)
236 @end example
238 Actions which take a @code{delta} parameter which do not accept all
239 these options will specify what they do take.
241 %end-doc */
243 /* %start-doc actions 00objects
245 Many actions act on indicated objects on the board. They will have
246 parameters like @code{ToggleObject} or @code{SelectedVias} to indicate
247 what group of objects they act on. Unless otherwise specified, these
248 parameters are defined as follows:
250 @table @code
252 @item Object
253 @itemx ToggleObject
254 Affects the object under the mouse pointer. If this action is invoked
255 from a menu or script, the user will be prompted to click on an
256 object, which is then the object affected.
258 @item Selected
259 @itemx SelectedObjects
261 Affects all objects which are currently selected. At least, all
262 selected objects for which the given action makes sense.
264 @item SelectedPins
265 @itemx SelectedVias
266 @itemx Selected@var{Type}
267 @itemx @i{etc}
268 Affects all objects which are both selected and of the @var{Type} specified.
270 @end table
272 %end-doc */
274 /* %start-doc actions 00macros
276 @macro pinshapes
278 Pins, pads, and vias can have various shapes. All may be round. Pins
279 and pads may be square (obviously "square" pads are usually
280 rectangular). Pins and vias may be octagonal. When you change a
281 shape flag of an element, you actually change all of its pins and
282 pads.
284 Note that the square flag takes precedence over the octagon flag,
285 thus, if both the square and octagon flags are set, the object is
286 square. When the square flag is cleared, the pins and pads will be
287 either round or, if the octagon flag is set, octagonal.
289 @end macro
291 %end-doc */
293 /* ---------------------------------------------------------------------------
294 * some local identifiers
296 static PointType InsertedPoint;
297 static LayerTypePtr lastLayer;
298 static struct
300 PolygonTypePtr poly;
301 LineType line;
303 fake;
305 static struct
307 int X;
308 int Y;
309 Cardinal Buffer;
310 bool Click;
311 bool Moving; /* selected type clicked on */
312 int Hit; /* move type clicked on */
313 void *ptr1;
314 void *ptr2;
315 void *ptr3;
317 Note;
319 static int defer_updates = 0;
320 static int defer_needs_update = 0;
322 static Cardinal polyIndex = 0;
323 static bool IgnoreMotionEvents = false;
324 static bool saved_mode = false;
325 #ifdef HAVE_LIBSTROKE
326 static bool mid_stroke = false;
327 static BoxType StrokeBox;
328 #endif
329 static FunctionType Functions[] = {
330 {"AddSelected", F_AddSelected},
331 {"All", F_All},
332 {"AllConnections", F_AllConnections},
333 {"AllRats", F_AllRats},
334 {"AllUnusedPins", F_AllUnusedPins},
335 {"Arc", F_Arc},
336 {"Arrow", F_Arrow},
337 {"Block", F_Block},
338 {"Description", F_Description},
339 {"Cancel", F_Cancel},
340 {"Center", F_Center},
341 {"Clear", F_Clear},
342 {"ClearAndRedraw", F_ClearAndRedraw},
343 {"ClearList", F_ClearList},
344 {"Close", F_Close},
345 {"Connection", F_Connection},
346 {"Convert", F_Convert},
347 {"Copy", F_Copy},
348 {"CycleClip", F_CycleClip},
349 {"CycleCrosshair", F_CycleCrosshair},
350 {"DeleteRats", F_DeleteRats},
351 {"Drag", F_Drag},
352 {"DrillReport", F_DrillReport},
353 {"Element", F_Element},
354 {"ElementByName", F_ElementByName},
355 {"ElementConnections", F_ElementConnections},
356 {"ElementToBuffer", F_ElementToBuffer},
357 {"Escape", F_Escape},
358 {"Find", F_Find},
359 {"FlipElement", F_FlipElement},
360 {"FoundPins", F_FoundPins},
361 {"Grid", F_Grid},
362 {"InsertPoint", F_InsertPoint},
363 {"Layer", F_Layer},
364 {"Layout", F_Layout},
365 {"LayoutAs", F_LayoutAs},
366 {"LayoutToBuffer", F_LayoutToBuffer},
367 {"Line", F_Line},
368 {"LineSize", F_LineSize},
369 {"Lock", F_Lock},
370 {"Mirror", F_Mirror},
371 {"Move", F_Move},
372 {"NameOnPCB", F_NameOnPCB},
373 {"Netlist", F_Netlist},
374 {"None", F_None},
375 {"Notify", F_Notify},
376 {"Object", F_Object},
377 {"ObjectByName", F_ObjectByName},
378 {"PasteBuffer", F_PasteBuffer},
379 {"PadByName", F_PadByName},
380 {"PinByName", F_PinByName},
381 {"PinOrPadName", F_PinOrPadName},
382 {"Pinout", F_Pinout},
383 {"Polygon", F_Polygon},
384 {"PolygonHole", F_PolygonHole},
385 {"PreviousPoint", F_PreviousPoint},
386 {"RatsNest", F_RatsNest},
387 {"Rectangle", F_Rectangle},
388 {"Redraw", F_Redraw},
389 {"Release", F_Release},
390 {"Remove", F_Remove},
391 {"RemoveSelected", F_RemoveSelected},
392 {"Report", F_Report},
393 {"Reset", F_Reset},
394 {"ResetLinesAndPolygons", F_ResetLinesAndPolygons},
395 {"ResetPinsViasAndPads", F_ResetPinsViasAndPads},
396 {"Restore", F_Restore},
397 {"Revert", F_Revert},
398 {"Rotate", F_Rotate},
399 {"Save", F_Save},
400 {"Scroll", F_Scroll},
401 {"Selected", F_Selected},
402 {"SelectedArcs", F_SelectedArcs},
403 {"SelectedElements", F_SelectedElements},
404 {"SelectedLines", F_SelectedLines},
405 {"SelectedNames", F_SelectedNames},
406 {"SelectedObjects", F_SelectedObjects},
407 {"SelectedPins", F_SelectedPins},
408 {"SelectedPads", F_SelectedPads},
409 {"SelectedRats", F_SelectedRats},
410 {"SelectedTexts", F_SelectedTexts},
411 {"SelectedVias", F_SelectedVias},
412 {"Stroke", F_Stroke},
413 {"Text", F_Text},
414 {"TextByName", F_TextByName},
415 {"TextScale", F_TextScale},
416 {"Thermal", F_Thermal},
417 {"ToLayout", F_ToLayout},
418 {"Toggle45Degree", F_ToggleAllDirections},
419 {"ToggleClearLine", F_ToggleClearLine},
420 {"ToggleFullPoly", F_ToggleFullPoly},
421 {"ToggleGrid", F_ToggleGrid},
422 {"ToggleMask", F_ToggleMask},
423 {"ToggleName", F_ToggleName},
424 {"ToggleObject", F_ToggleObject},
425 {"ToggleRubberBandMode", F_ToggleRubberBandMode},
426 {"ToggleStartDirection", F_ToggleStartDirection},
427 {"ToggleSnapPin", F_ToggleSnapPin},
428 {"ToggleThindraw", F_ToggleThindraw},
429 {"ToggleThindrawPoly", F_ToggleThindrawPoly},
430 {"ToggleLockNames", F_ToggleLockNames},
431 {"ToggleOnlyNames", F_ToggleOnlyNames},
432 {"ToggleHideNames", F_ToggleHideNames},
433 {"ToggleCheckPlanes", F_ToggleCheckPlanes},
434 {"ToggleLocalRef", F_ToggleLocalRef},
435 {"ToggleOrthoMove", F_ToggleOrthoMove},
436 {"ToggleShowDRC", F_ToggleShowDRC},
437 {"ToggleLiveRoute", F_ToggleLiveRoute},
438 {"ToggleAutoDRC", F_ToggleAutoDRC},
439 {"ToggleUniqueNames", F_ToggleUniqueNames},
440 {"Value", F_Value},
441 {"Via", F_Via},
442 {"ViaByName", F_ViaByName},
443 {"ViaSize", F_ViaSize},
444 {"ViaDrillingHole", F_ViaDrillingHole},
445 {"Zoom", F_Zoom}
448 /* ---------------------------------------------------------------------------
449 * some local routines
451 static int GetFunctionID (String);
452 static void AdjustAttachedBox (void);
453 static void NotifyLine (void);
454 static void NotifyBlock (void);
455 static void NotifyMode (void);
456 static void ClearWarnings (void);
457 #ifdef HAVE_LIBSTROKE
458 static void FinishStroke (void);
459 extern void stroke_init (void);
460 extern void stroke_record (int x, int y);
461 extern int stroke_trans (char *s);
462 #endif
463 static void ChangeFlag (char *, char *, int, char *);
465 #define ARG(n) (argc > (n) ? argv[n] : NULL)
467 #ifdef HAVE_LIBSTROKE
469 /* ---------------------------------------------------------------------------
470 * FinishStroke - try to recognize the stroke sent
472 void
473 FinishStroke (void)
475 char msg[255];
476 int type;
477 unsigned long num;
478 void *ptr1, *ptr2, *ptr3;
480 mid_stroke = false;
481 if (stroke_trans (msg))
483 num = atoi (msg);
484 switch (num)
486 case 456:
487 if (Settings.Mode == LINE_MODE)
489 SetMode (LINE_MODE);
491 break;
492 case 9874123:
493 case 74123:
494 case 987412:
495 case 8741236:
496 case 874123:
497 RotateScreenObject (StrokeBox.X1, StrokeBox.Y1, SWAP_IDENT ? 1 : 3);
498 break;
499 case 7896321:
500 case 786321:
501 case 789632:
502 case 896321:
503 RotateScreenObject (StrokeBox.X1, StrokeBox.Y1, SWAP_IDENT ? 3 : 1);
504 break;
505 case 258:
506 SetMode (LINE_MODE);
507 break;
508 case 852:
509 SetMode (ARROW_MODE);
510 break;
511 case 1478963:
512 ActionUndo ("");
513 break;
514 case 147423:
515 case 147523:
516 case 1474123:
517 Redo (true);
518 break;
519 case 148963:
520 case 147863:
521 case 147853:
522 case 145863:
523 SetMode (VIA_MODE);
524 break;
525 case 951:
526 case 9651:
527 case 9521:
528 case 9621:
529 case 9851:
530 case 9541:
531 case 96521:
532 case 96541:
533 case 98541:
534 SetZoom (1000); /* special zoom extents */
535 break;
536 case 159:
537 case 1269:
538 case 1259:
539 case 1459:
540 case 1569:
541 case 1589:
542 case 12569:
543 case 12589:
544 case 14589:
546 LocationType x = (StrokeBox.X1 + StrokeBox.X2) / 2;
547 LocationType y = (StrokeBox.Y1 + StrokeBox.Y2) / 2;
548 int z;
551 log (fabs (StrokeBox.X2 - StrokeBox.X1) / Output.Width) /
552 log (2.0);
554 MAX (z,
556 log (fabs (StrokeBox.Y2 - StrokeBox.Y1) / Output.Height) /
557 log (2.0));
558 SetZoom (z);
560 CenterDisplay (x, y, false);
561 break;
564 default:
565 Message (_("Unknown stroke %s\n"), msg);
566 break;
569 else
570 gui->beep ();
572 #endif
574 /* ---------------------------------------------------------------------------
575 * Clear warning color from pins/pads
577 static void
578 ClearWarnings ()
580 Settings.RatWarn = false;
581 ALLPIN_LOOP (PCB->Data);
583 if (TEST_FLAG (WARNFLAG, pin))
585 CLEAR_FLAG (WARNFLAG, pin);
586 DrawPin (pin, 0);
589 ENDALL_LOOP;
590 ALLPAD_LOOP (PCB->Data);
592 if (TEST_FLAG (WARNFLAG, pad))
594 CLEAR_FLAG (WARNFLAG, pad);
595 DrawPad (pad, 0);
598 ENDALL_LOOP;
599 Draw ();
602 static void
603 click_cb (hidval hv)
605 if (Note.Click)
607 Note.Click = false;
608 if (Note.Moving && !gui->shift_is_pressed ())
610 HideCrosshair (true);
611 Note.Buffer = Settings.BufferNumber;
612 SetBufferNumber (MAX_BUFFER - 1);
613 ClearBuffer (PASTEBUFFER);
614 AddSelectedToBuffer (PASTEBUFFER, Note.X, Note.Y, true);
615 SaveUndoSerialNumber ();
616 RemoveSelected ();
617 SaveMode ();
618 saved_mode = true;
619 SetMode (PASTEBUFFER_MODE);
620 RestoreCrosshair (true);
622 else if (Note.Hit && !gui->shift_is_pressed ())
624 HideCrosshair (true);
625 SaveMode ();
626 saved_mode = true;
627 SetMode (gui->control_is_pressed ()? COPY_MODE : MOVE_MODE);
628 Crosshair.AttachedObject.Ptr1 = Note.ptr1;
629 Crosshair.AttachedObject.Ptr2 = Note.ptr2;
630 Crosshair.AttachedObject.Ptr3 = Note.ptr3;
631 Crosshair.AttachedObject.Type = Note.Hit;
632 AttachForCopy (Note.X, Note.Y);
633 RestoreCrosshair (true);
635 else
637 BoxType box;
639 Note.Hit = 0;
640 Note.Moving = false;
641 HideCrosshair (true);
642 SaveUndoSerialNumber ();
643 box.X1 = -MAX_COORD;
644 box.Y1 = -MAX_COORD;
645 box.X2 = MAX_COORD;
646 box.Y2 = MAX_COORD;
647 /* unselect first if shift key not down */
648 if (!gui->shift_is_pressed () && SelectBlock (&box, false))
649 SetChangedFlag (true);
650 NotifyBlock ();
651 Crosshair.AttachedBox.Point1.X = Note.X;
652 Crosshair.AttachedBox.Point1.Y = Note.Y;
653 RestoreCrosshair (true);
658 static void
659 ReleaseMode (void)
661 BoxType box;
663 if (Note.Click)
665 BoxType box;
667 box.X1 = -MAX_COORD;
668 box.Y1 = -MAX_COORD;
669 box.X2 = MAX_COORD;
670 box.Y2 = MAX_COORD;
672 Note.Click = false; /* inhibit timer action */
673 SaveUndoSerialNumber ();
674 /* unselect first if shift key not down */
675 if (!gui->shift_is_pressed ())
677 if (SelectBlock (&box, false))
678 SetChangedFlag (true);
679 if (Note.Moving)
681 Note.Moving = 0;
682 Note.Hit = 0;
683 return;
686 RestoreUndoSerialNumber ();
687 if (SelectObject ())
688 SetChangedFlag (true);
689 Note.Hit = 0;
690 Note.Moving = 0;
692 else if (Note.Moving)
694 RestoreUndoSerialNumber ();
695 NotifyMode ();
696 ClearBuffer (PASTEBUFFER);
697 SetBufferNumber (Note.Buffer);
698 Note.Moving = false;
699 Note.Hit = 0;
701 else if (Note.Hit)
703 NotifyMode ();
704 Note.Hit = 0;
706 else if (Settings.Mode == ARROW_MODE)
708 box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
709 Crosshair.AttachedBox.Point2.X);
710 box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
711 Crosshair.AttachedBox.Point2.Y);
712 box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
713 Crosshair.AttachedBox.Point2.X);
714 box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
715 Crosshair.AttachedBox.Point2.Y);
716 RestoreUndoSerialNumber ();
717 if (SelectBlock (&box, true))
718 SetChangedFlag (true);
719 else if (Bumped)
720 IncrementUndoSerialNumber ();
721 Crosshair.AttachedBox.State = STATE_FIRST;
723 if (saved_mode)
724 RestoreMode ();
725 saved_mode = false;
728 /* ---------------------------------------------------------------------------
729 * get function ID of passed string
731 #define HSIZE 257
732 static char function_hash[HSIZE];
733 static int hash_initted = 0;
735 static int
736 hashfunc(String s)
738 int i = 0;
739 while (*s)
741 i ^= i >> 16;
742 i = (i * 13) ^ (unsigned char)tolower((int) *s);
743 s ++;
745 i = (unsigned int)i % HSIZE;
746 return i;
749 static int
750 GetFunctionID (String Ident)
752 int i, h;
754 if (Ident == 0)
755 return -1;
757 if (!hash_initted)
759 hash_initted = 1;
760 if (HSIZE < ENTRIES (Functions) * 2)
762 fprintf(stderr, _("Error: function hash size too small (%d vs %lu at %s:%d)\n"),
763 HSIZE, (unsigned long) ENTRIES (Functions)*2, __FILE__, __LINE__);
764 exit(1);
766 if (ENTRIES (Functions) > 254)
768 /* Change 'char' to 'int' and remove this when we get to 256
769 strings to hash. */
770 fprintf(stderr, _("Error: function hash type too small (%d vs %lu at %s:%d)\n"),
771 256, (unsigned long) ENTRIES (Functions), __FILE__, __LINE__);
772 exit(1);
775 for (i=ENTRIES (Functions)-1; i>=0; i--)
777 h = hashfunc (Functions[i].Identifier);
778 while (function_hash[h])
779 h = (h + 1) % HSIZE;
780 function_hash[h] = i + 1;
784 i = hashfunc (Ident);
785 while (1)
787 /* We enforce the "hash table bigger than function table" rule,
788 so we know there will be at least one zero entry to find. */
789 if (!function_hash[i])
790 return (-1);
791 if (!strcasecmp (Ident, Functions[function_hash[i]-1].Identifier))
792 return ((int) Functions[function_hash[i]-1].ID);
793 i = (i + 1) % HSIZE;
797 /* ---------------------------------------------------------------------------
798 * set new coordinates if in 'RECTANGLE' mode
799 * the cursor shape is also adjusted
801 static void
802 AdjustAttachedBox (void)
804 if (Settings.Mode == ARC_MODE)
806 Crosshair.AttachedBox.otherway = gui->shift_is_pressed ();
807 return;
809 switch (Crosshair.AttachedBox.State)
811 case STATE_SECOND: /* one corner is selected */
813 /* update coordinates */
814 Crosshair.AttachedBox.Point2.X = Crosshair.X;
815 Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
816 break;
821 /* ---------------------------------------------------------------------------
822 * adjusts the objects which are to be created like attached lines...
824 void
825 AdjustAttachedObjects (void)
827 PointTypePtr pnt;
828 switch (Settings.Mode)
830 /* update at least an attached block (selection) */
831 case NO_MODE:
832 case ARROW_MODE:
833 if (Crosshair.AttachedBox.State)
835 Crosshair.AttachedBox.Point2.X = Crosshair.X;
836 Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
838 break;
840 /* rectangle creation mode */
841 case RECTANGLE_MODE:
842 case ARC_MODE:
843 AdjustAttachedBox ();
844 break;
846 /* polygon creation mode */
847 case POLYGON_MODE:
848 case POLYGONHOLE_MODE:
849 AdjustAttachedLine ();
850 break;
851 /* line creation mode */
852 case LINE_MODE:
853 if (PCB->RatDraw || PCB->Clipping == 0)
854 AdjustAttachedLine ();
855 else
856 AdjustTwoLine (PCB->Clipping - 1);
857 break;
858 /* point insertion mode */
859 case INSERTPOINT_MODE:
860 pnt = AdjustInsertPoint ();
861 if (pnt)
862 InsertedPoint = *pnt;
863 break;
864 case ROTATE_MODE:
865 break;
869 /* ---------------------------------------------------------------------------
870 * creates points of a line
872 static void
873 NotifyLine (void)
875 int type = NO_TYPE;
876 void *ptr1, *ptr2, *ptr3;
878 if (!Marked.status || TEST_FLAG (LOCALREFFLAG, PCB))
879 SetLocalRef (Crosshair.X, Crosshair.Y, true);
880 switch (Crosshair.AttachedLine.State)
882 case STATE_FIRST: /* first point */
883 if (PCB->RatDraw && SearchScreen (Crosshair.X, Crosshair.Y,
884 PAD_TYPE | PIN_TYPE, &ptr1, &ptr1,
885 &ptr1) == NO_TYPE)
887 gui->beep ();
888 break;
890 if (TEST_FLAG (AUTODRCFLAG, PCB) && Settings.Mode == LINE_MODE)
892 type = SearchScreen (Crosshair.X, Crosshair.Y,
893 PIN_TYPE | PAD_TYPE | VIA_TYPE, &ptr1, &ptr2,
894 &ptr3);
895 LookupConnection (Crosshair.X, Crosshair.Y, true, TO_PCB (1),
896 FOUNDFLAG);
898 if (type == PIN_TYPE || type == VIA_TYPE)
900 Crosshair.AttachedLine.Point1.X =
901 Crosshair.AttachedLine.Point2.X = ((PinTypePtr) ptr2)->X;
902 Crosshair.AttachedLine.Point1.Y =
903 Crosshair.AttachedLine.Point2.Y = ((PinTypePtr) ptr2)->Y;
905 else if (type == PAD_TYPE)
907 PadTypePtr pad = (PadTypePtr) ptr2;
908 float d1, d2;
909 d1 = SQUARE (Crosshair.X - pad->Point1.X) +
910 SQUARE (Crosshair.Y - pad->Point1.Y);
911 d2 = SQUARE (Crosshair.X - pad->Point2.X) +
912 SQUARE (Crosshair.Y - pad->Point2.Y);
913 if (d2 < d1)
915 Crosshair.AttachedLine.Point1 =
916 Crosshair.AttachedLine.Point2 = pad->Point2;
918 else
920 Crosshair.AttachedLine.Point1 =
921 Crosshair.AttachedLine.Point2 = pad->Point1;
924 else
926 Crosshair.AttachedLine.Point1.X =
927 Crosshair.AttachedLine.Point2.X = Crosshair.X;
928 Crosshair.AttachedLine.Point1.Y =
929 Crosshair.AttachedLine.Point2.Y = Crosshair.Y;
931 Crosshair.AttachedLine.State = STATE_SECOND;
932 break;
934 case STATE_SECOND:
935 /* fall through to third state too */
936 lastLayer = CURRENT;
937 default: /* all following points */
938 Crosshair.AttachedLine.State = STATE_THIRD;
939 break;
943 /* ---------------------------------------------------------------------------
944 * create first or second corner of a marked block
946 static void
947 NotifyBlock (void)
949 HideCrosshair (true);
950 switch (Crosshair.AttachedBox.State)
952 case STATE_FIRST: /* setup first point */
953 Crosshair.AttachedBox.Point1.X =
954 Crosshair.AttachedBox.Point2.X = Crosshair.X;
955 Crosshair.AttachedBox.Point1.Y =
956 Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
957 Crosshair.AttachedBox.State = STATE_SECOND;
958 break;
960 case STATE_SECOND: /* setup second point */
961 Crosshair.AttachedBox.State = STATE_THIRD;
962 break;
964 RestoreCrosshair (true);
968 /* ---------------------------------------------------------------------------
970 * does what's appropriate for the current mode setting. This normally
971 * means creation of an object at the current crosshair location.
973 * new created objects are added to the create undo list of course
975 static void
976 NotifyMode (void)
978 void *ptr1, *ptr2, *ptr3;
979 int type;
981 if (Settings.RatWarn)
982 ClearWarnings ();
983 switch (Settings.Mode)
985 case ARROW_MODE:
987 int test;
988 hidval hv;
990 Note.Click = true;
991 /* do something after click time */
992 gui->add_timer (click_cb, CLICK_TIME, hv);
994 /* see if we clicked on something already selected
995 * (Note.Moving) or clicked on a MOVE_TYPE
996 * (Note.Hit)
998 for (test = (SELECT_TYPES | MOVE_TYPES) & ~RATLINE_TYPE;
999 test; test &= ~type)
1001 type = SearchScreen (Note.X, Note.Y, test, &ptr1, &ptr2, &ptr3);
1002 if (!Note.Hit && (type & MOVE_TYPES) &&
1003 !TEST_FLAG (LOCKFLAG, (PinTypePtr) ptr2))
1005 Note.Hit = type;
1006 Note.ptr1 = ptr1;
1007 Note.ptr2 = ptr2;
1008 Note.ptr3 = ptr3;
1010 if (!Note.Moving && (type & SELECT_TYPES) &&
1011 TEST_FLAG (SELECTEDFLAG, (PinTypePtr) ptr2))
1012 Note.Moving = true;
1013 if ((Note.Hit && Note.Moving) || type == NO_TYPE)
1014 break;
1016 break;
1019 case VIA_MODE:
1021 PinTypePtr via;
1023 if (!PCB->ViaOn)
1025 Message (_("You must turn via visibility on before\n"
1026 "you can place vias\n"));
1027 break;
1029 if ((via = CreateNewVia (PCB->Data, Note.X, Note.Y,
1030 Settings.ViaThickness, 2 * Settings.Keepaway,
1031 0, Settings.ViaDrillingHole, NULL,
1032 NoFlags ())) != NULL)
1034 AddObjectToCreateUndoList (VIA_TYPE, via, via, via);
1035 if (gui->shift_is_pressed ())
1036 ChangeObjectThermal (VIA_TYPE, via, via, via, PCB->ThermStyle);
1037 IncrementUndoSerialNumber ();
1038 DrawVia (via, 0);
1039 Draw ();
1041 break;
1044 case ARC_MODE:
1046 switch (Crosshair.AttachedBox.State)
1048 case STATE_FIRST:
1049 Crosshair.AttachedBox.Point1.X =
1050 Crosshair.AttachedBox.Point2.X = Note.X;
1051 Crosshair.AttachedBox.Point1.Y =
1052 Crosshair.AttachedBox.Point2.Y = Note.Y;
1053 Crosshair.AttachedBox.State = STATE_SECOND;
1054 break;
1056 case STATE_SECOND:
1057 case STATE_THIRD:
1059 ArcTypePtr arc;
1060 LocationType wx, wy;
1061 int sa, dir;
1063 wx = Note.X - Crosshair.AttachedBox.Point1.X;
1064 wy = Note.Y - Crosshair.AttachedBox.Point1.Y;
1065 if (XOR (Crosshair.AttachedBox.otherway, abs (wy) > abs (wx)))
1067 Crosshair.AttachedBox.Point2.X =
1068 Crosshair.AttachedBox.Point1.X + abs (wy) * SGNZ (wx);
1069 sa = (wx >= 0) ? 0 : 180;
1070 #ifdef ARC45
1071 if (abs (wy) / 2 >= abs (wx))
1072 dir = (SGNZ (wx) == SGNZ (wy)) ? 45 : -45;
1073 else
1074 #endif
1075 dir = (SGNZ (wx) == SGNZ (wy)) ? 90 : -90;
1077 else
1079 Crosshair.AttachedBox.Point2.Y =
1080 Crosshair.AttachedBox.Point1.Y + abs (wx) * SGNZ (wy);
1081 sa = (wy >= 0) ? -90 : 90;
1082 #ifdef ARC45
1083 if (abs (wx) / 2 >= abs (wy))
1084 dir = (SGNZ (wx) == SGNZ (wy)) ? -45 : 45;
1085 else
1086 #endif
1087 dir = (SGNZ (wx) == SGNZ (wy)) ? -90 : 90;
1088 wy = wx;
1090 if (abs (wy) > 0 && (arc = CreateNewArcOnLayer (CURRENT,
1091 Crosshair.
1092 AttachedBox.
1093 Point2.X,
1094 Crosshair.
1095 AttachedBox.
1096 Point2.Y,
1097 abs (wy),
1098 abs (wy),
1100 dir,
1101 Settings.
1102 LineThickness,
1103 2 * Settings.
1104 Keepaway,
1105 MakeFlags
1106 (TEST_FLAG
1107 (CLEARNEWFLAG,
1108 PCB) ?
1109 CLEARLINEFLAG :
1110 0))))
1112 BoxTypePtr bx;
1114 bx = GetArcEnds (arc);
1115 Crosshair.AttachedBox.Point1.X =
1116 Crosshair.AttachedBox.Point2.X = bx->X2;
1117 Crosshair.AttachedBox.Point1.Y =
1118 Crosshair.AttachedBox.Point2.Y = bx->Y2;
1119 AddObjectToCreateUndoList (ARC_TYPE, CURRENT, arc, arc);
1120 IncrementUndoSerialNumber ();
1121 addedLines++;
1122 DrawArc (CURRENT, arc, 0);
1123 Draw ();
1124 Crosshair.AttachedBox.State = STATE_THIRD;
1126 break;
1129 break;
1131 case LOCK_MODE:
1133 type = SearchScreen (Note.X, Note.Y, LOCK_TYPES, &ptr1, &ptr2, &ptr3);
1134 if (type == ELEMENT_TYPE)
1136 ElementTypePtr element = (ElementTypePtr) ptr2;
1138 TOGGLE_FLAG (LOCKFLAG, element);
1139 PIN_LOOP (element);
1141 TOGGLE_FLAG (LOCKFLAG, pin);
1142 CLEAR_FLAG (SELECTEDFLAG, pin);
1144 END_LOOP;
1145 PAD_LOOP (element);
1147 TOGGLE_FLAG (LOCKFLAG, pad);
1148 CLEAR_FLAG (SELECTEDFLAG, pad);
1150 END_LOOP;
1151 CLEAR_FLAG (SELECTEDFLAG, element);
1152 /* always re-draw it since I'm too lazy
1153 * to tell if a selected flag changed
1155 DrawElement (element, 0);
1156 Draw ();
1157 hid_actionl ("Report", "Object", NULL);
1159 else if (type != NO_TYPE)
1161 TextTypePtr thing = (TextTypePtr) ptr3;
1162 TOGGLE_FLAG (LOCKFLAG, thing);
1163 if (TEST_FLAG (LOCKFLAG, thing)
1164 && TEST_FLAG (SELECTEDFLAG, thing))
1166 /* this is not un-doable since LOCK isn't */
1167 CLEAR_FLAG (SELECTEDFLAG, thing);
1168 DrawObject (type, ptr1, ptr2, 0);
1169 Draw ();
1171 hid_actionl ("Report", "Object", NULL);
1173 break;
1175 case THERMAL_MODE:
1177 if (((type
1179 SearchScreen (Note.X, Note.Y, PIN_TYPES, &ptr1, &ptr2,
1180 &ptr3)) != NO_TYPE)
1181 && !TEST_FLAG (HOLEFLAG, (PinTypePtr) ptr3))
1183 if (gui->shift_is_pressed ())
1185 int tstyle = GET_THERM (INDEXOFCURRENT, (PinTypePtr) ptr3);
1186 tstyle++;
1187 if (tstyle > 5)
1188 tstyle = 1;
1189 ChangeObjectThermal (type, ptr1, ptr2, ptr3, tstyle);
1191 else if (GET_THERM (INDEXOFCURRENT, (PinTypePtr) ptr3))
1192 ChangeObjectThermal (type, ptr1, ptr2, ptr3, 0);
1193 else
1194 ChangeObjectThermal (type, ptr1, ptr2, ptr3, PCB->ThermStyle);
1196 break;
1199 case LINE_MODE:
1200 /* do update of position */
1201 NotifyLine ();
1202 if (Crosshair.AttachedLine.State != STATE_THIRD)
1203 break;
1205 /* Remove anchor if clicking on start point;
1206 * this means we can't paint 0 length lines
1207 * which could be used for square SMD pads.
1208 * Instead use a very small delta, or change
1209 * the file after saving.
1211 if (Crosshair.X == Crosshair.AttachedLine.Point1.X
1212 && Crosshair.Y == Crosshair.AttachedLine.Point1.Y)
1214 SetMode (LINE_MODE);
1215 break;
1218 if (PCB->RatDraw)
1220 RatTypePtr line;
1221 if ((line = AddNet ()))
1223 addedLines++;
1224 AddObjectToCreateUndoList (RATLINE_TYPE, line, line, line);
1225 IncrementUndoSerialNumber ();
1226 DrawRat (line, 0);
1227 Crosshair.AttachedLine.Point1.X =
1228 Crosshair.AttachedLine.Point2.X;
1229 Crosshair.AttachedLine.Point1.Y =
1230 Crosshair.AttachedLine.Point2.Y;
1231 Draw ();
1233 break;
1235 else
1236 /* create line if both ends are determined && length != 0 */
1238 LineTypePtr line;
1240 if (PCB->Clipping
1241 && Crosshair.AttachedLine.Point1.X ==
1242 Crosshair.AttachedLine.Point2.X
1243 && Crosshair.AttachedLine.Point1.Y ==
1244 Crosshair.AttachedLine.Point2.Y
1245 && (Crosshair.AttachedLine.Point2.X != Note.X
1246 || Crosshair.AttachedLine.Point2.Y != Note.Y))
1248 /* We will only need to paint the second line segment.
1249 Since we only check for vias on the first segment,
1250 swap them so the non-empty segment is the first segment. */
1251 Crosshair.AttachedLine.Point2.X = Note.X;
1252 Crosshair.AttachedLine.Point2.Y = Note.Y;
1255 if ((Crosshair.AttachedLine.Point1.X !=
1256 Crosshair.AttachedLine.Point2.X
1257 || Crosshair.AttachedLine.Point1.Y !=
1258 Crosshair.AttachedLine.Point2.Y)
1259 && (line =
1260 CreateDrawnLineOnLayer (CURRENT,
1261 Crosshair.AttachedLine.Point1.X,
1262 Crosshair.AttachedLine.Point1.Y,
1263 Crosshair.AttachedLine.Point2.X,
1264 Crosshair.AttachedLine.Point2.Y,
1265 Settings.LineThickness,
1266 2 * Settings.Keepaway,
1267 MakeFlags ((TEST_FLAG
1268 (AUTODRCFLAG,
1269 PCB) ? FOUNDFLAG : 0) |
1270 (TEST_FLAG
1271 (CLEARNEWFLAG,
1272 PCB) ? CLEARLINEFLAG :
1273 0)))) != NULL)
1275 PinTypePtr via;
1277 addedLines++;
1278 AddObjectToCreateUndoList (LINE_TYPE, CURRENT, line, line);
1279 DrawLine (CURRENT, line, 0);
1280 /* place a via if vias are visible, the layer is
1281 in a new group since the last line and there
1282 isn't a pin already here */
1283 if (PCB->ViaOn && GetLayerGroupNumberByPointer (CURRENT) !=
1284 GetLayerGroupNumberByPointer (lastLayer) &&
1285 SearchObjectByLocation (PIN_TYPES, &ptr1, &ptr2, &ptr3,
1286 Crosshair.AttachedLine.Point1.X,
1287 Crosshair.AttachedLine.Point1.Y,
1288 Settings.ViaThickness / 2) ==
1289 NO_TYPE
1290 && (via =
1291 CreateNewVia (PCB->Data,
1292 Crosshair.AttachedLine.Point1.X,
1293 Crosshair.AttachedLine.Point1.Y,
1294 Settings.ViaThickness,
1295 2 * Settings.Keepaway, 0,
1296 Settings.ViaDrillingHole, NULL,
1297 NoFlags ())) != NULL)
1299 AddObjectToCreateUndoList (VIA_TYPE, via, via, via);
1300 DrawVia (via, 0);
1302 /* copy the coordinates */
1303 Crosshair.AttachedLine.Point1.X =
1304 Crosshair.AttachedLine.Point2.X;
1305 Crosshair.AttachedLine.Point1.Y =
1306 Crosshair.AttachedLine.Point2.Y;
1307 IncrementUndoSerialNumber ();
1308 lastLayer = CURRENT;
1310 if (PCB->Clipping && (Note.X != Crosshair.AttachedLine.Point2.X
1311 || Note.Y !=
1312 Crosshair.AttachedLine.Point2.Y)
1313 && (line =
1314 CreateDrawnLineOnLayer (CURRENT,
1315 Crosshair.AttachedLine.Point2.X,
1316 Crosshair.AttachedLine.Point2.Y,
1317 Note.X, Note.Y,
1318 Settings.LineThickness,
1319 2 * Settings.Keepaway,
1320 MakeFlags ((TEST_FLAG
1321 (AUTODRCFLAG,
1322 PCB) ? FOUNDFLAG : 0) |
1323 (TEST_FLAG
1324 (CLEARNEWFLAG,
1325 PCB) ? CLEARLINEFLAG :
1326 0)))) != NULL)
1328 addedLines++;
1329 AddObjectToCreateUndoList (LINE_TYPE, CURRENT, line, line);
1330 IncrementUndoSerialNumber ();
1331 DrawLine (CURRENT, line, 0);
1332 /* move to new start point */
1333 Crosshair.AttachedLine.Point1.X = Note.X;
1334 Crosshair.AttachedLine.Point1.Y = Note.Y;
1335 Crosshair.AttachedLine.Point2.X = Note.X;
1336 Crosshair.AttachedLine.Point2.Y = Note.Y;
1337 if (TEST_FLAG (SWAPSTARTDIRFLAG, PCB))
1339 PCB->Clipping ^= 3;
1342 Draw ();
1344 break;
1346 case RECTANGLE_MODE:
1347 /* do update of position */
1348 NotifyBlock ();
1350 /* create rectangle if both corners are determined
1351 * and width, height are != 0
1353 if (Crosshair.AttachedBox.State == STATE_THIRD &&
1354 Crosshair.AttachedBox.Point1.X != Crosshair.AttachedBox.Point2.X &&
1355 Crosshair.AttachedBox.Point1.Y != Crosshair.AttachedBox.Point2.Y)
1357 PolygonTypePtr polygon;
1359 int flags = CLEARPOLYFLAG;
1360 if (TEST_FLAG (NEWFULLPOLYFLAG, PCB))
1361 flags |= FULLPOLYFLAG;
1362 if ((polygon = CreateNewPolygonFromRectangle (CURRENT,
1363 Crosshair.
1364 AttachedBox.Point1.X,
1365 Crosshair.
1366 AttachedBox.Point1.Y,
1367 Crosshair.
1368 AttachedBox.Point2.X,
1369 Crosshair.
1370 AttachedBox.Point2.Y,
1371 MakeFlags
1372 (flags))) !=
1373 NULL)
1375 AddObjectToCreateUndoList (POLYGON_TYPE, CURRENT,
1376 polygon, polygon);
1377 IncrementUndoSerialNumber ();
1378 DrawPolygon (CURRENT, polygon, 0);
1379 Draw ();
1382 /* reset state to 'first corner' */
1383 Crosshair.AttachedBox.State = STATE_FIRST;
1385 break;
1387 case TEXT_MODE:
1389 char *string;
1391 if ((string = gui->prompt_for (_("Enter text:"), "")) != NULL)
1393 if (strlen(string) > 0)
1395 TextTypePtr text;
1396 int flag = CLEARLINEFLAG;
1398 if (GetLayerGroupNumberByNumber (INDEXOFCURRENT) ==
1399 GetLayerGroupNumberByNumber (solder_silk_layer))
1400 flag |= ONSOLDERFLAG;
1401 if ((text = CreateNewText (CURRENT, &PCB->Font, Note.X,
1402 Note.Y, 0, Settings.TextScale,
1403 string, MakeFlags (flag))) != NULL)
1405 AddObjectToCreateUndoList (TEXT_TYPE, CURRENT, text, text);
1406 IncrementUndoSerialNumber ();
1407 DrawText (CURRENT, text, 0);
1408 Draw ();
1411 free (string);
1413 break;
1416 case POLYGON_MODE:
1418 PointTypePtr points = Crosshair.AttachedPolygon.Points;
1419 Cardinal n = Crosshair.AttachedPolygon.PointN;
1421 /* do update of position; use the 'LINE_MODE' mechanism */
1422 NotifyLine ();
1424 /* check if this is the last point of a polygon */
1425 if (n >= 3 &&
1426 points->X == Crosshair.AttachedLine.Point2.X &&
1427 points->Y == Crosshair.AttachedLine.Point2.Y)
1429 CopyAttachedPolygonToLayer ();
1430 Draw ();
1431 break;
1434 /* create new point if it's the first one or if it's
1435 * different to the last one
1437 if (!n ||
1438 points[n - 1].X != Crosshair.AttachedLine.Point2.X ||
1439 points[n - 1].Y != Crosshair.AttachedLine.Point2.Y)
1441 CreateNewPointInPolygon (&Crosshair.AttachedPolygon,
1442 Crosshair.AttachedLine.Point2.X,
1443 Crosshair.AttachedLine.Point2.Y);
1445 /* copy the coordinates */
1446 Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X;
1447 Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y;
1449 break;
1452 case POLYGONHOLE_MODE:
1454 switch (Crosshair.AttachedObject.State)
1456 /* first notify, lookup object */
1457 case STATE_FIRST:
1458 Crosshair.AttachedObject.Type =
1459 SearchScreen (Note.X, Note.Y, POLYGON_TYPE,
1460 &Crosshair.AttachedObject.Ptr1,
1461 &Crosshair.AttachedObject.Ptr2,
1462 &Crosshair.AttachedObject.Ptr3);
1464 if (Crosshair.AttachedObject.Type != NO_TYPE)
1466 if (TEST_FLAG (LOCKFLAG, (PolygonTypePtr)
1467 Crosshair.AttachedObject.Ptr2))
1469 Message (_("Sorry, the object is locked\n"));
1470 Crosshair.AttachedObject.Type = NO_TYPE;
1471 break;
1473 else
1474 Crosshair.AttachedObject.State = STATE_SECOND;
1476 break;
1478 /* second notify, insert new point into object */
1479 case STATE_SECOND:
1481 PointTypePtr points = Crosshair.AttachedPolygon.Points;
1482 Cardinal n = Crosshair.AttachedPolygon.PointN;
1483 POLYAREA *original, *new_hole, *result;
1484 FlagType Flags;
1486 /* do update of position; use the 'LINE_MODE' mechanism */
1487 NotifyLine ();
1489 /* check if this is the last point of a polygon */
1490 if (n >= 3 &&
1491 points->X == Crosshair.AttachedLine.Point2.X &&
1492 points->Y == Crosshair.AttachedLine.Point2.Y)
1494 /* Create POLYAREAs from the original polygon
1495 * and the new hole polygon */
1496 original = PolygonToPoly (Crosshair.AttachedObject.Ptr2);
1497 new_hole = PolygonToPoly (&Crosshair.AttachedPolygon);
1499 /* Subtract the hole from the original polygon shape */
1500 poly_Boolean_free (original, new_hole, &result, PBO_SUB);
1502 /* Convert the resulting polygon(s) into a new set of nodes
1503 * and place them on the page. Delete the original polygon.
1505 SaveUndoSerialNumber ();
1506 Flags = ((PolygonType *)Crosshair.AttachedObject.Ptr2)->Flags;
1507 PolyToPolygonsOnLayer (PCB->Data, Crosshair.AttachedObject.Ptr1,
1508 result, Flags);
1509 RemoveObject (POLYGON_TYPE,
1510 Crosshair.AttachedObject.Ptr1,
1511 Crosshair.AttachedObject.Ptr2,
1512 Crosshair.AttachedObject.Ptr3);
1513 RestoreUndoSerialNumber ();
1514 IncrementUndoSerialNumber ();
1515 Draw ();
1517 /* reset state of attached line */
1518 memset (&Crosshair.AttachedPolygon, 0, sizeof (PolygonType));
1519 Crosshair.AttachedLine.State = STATE_FIRST;
1520 addedLines = 0;
1522 break;
1525 /* create new point if it's the first one or if it's
1526 * different to the last one
1528 if (!n ||
1529 points[n - 1].X != Crosshair.AttachedLine.Point2.X ||
1530 points[n - 1].Y != Crosshair.AttachedLine.Point2.Y)
1532 CreateNewPointInPolygon (&Crosshair.AttachedPolygon,
1533 Crosshair.AttachedLine.Point2.X,
1534 Crosshair.AttachedLine.Point2.Y);
1536 /* copy the coordinates */
1537 Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X;
1538 Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y;
1540 break;
1544 break;
1547 case PASTEBUFFER_MODE:
1549 TextType estr[MAX_ELEMENTNAMES];
1550 ElementTypePtr e = 0;
1552 if (gui->shift_is_pressed ())
1554 int type =
1555 SearchScreen (Note.X, Note.Y, ELEMENT_TYPE, &ptr1, &ptr2,
1556 &ptr3);
1557 if (type == ELEMENT_TYPE)
1559 e = (ElementTypePtr) ptr1;
1560 if (e)
1562 int i;
1564 memcpy (estr, e->Name,
1565 MAX_ELEMENTNAMES * sizeof (TextType));
1566 for (i = 0; i < MAX_ELEMENTNAMES; ++i)
1567 estr[i].TextString = estr[i].TextString ? strdup(estr[i].TextString) : NULL;
1568 RemoveElement (e);
1572 if (CopyPastebufferToLayout (Note.X, Note.Y))
1573 SetChangedFlag (true);
1574 if (e)
1576 int type =
1577 SearchScreen (Note.X, Note.Y, ELEMENT_TYPE, &ptr1, &ptr2,
1578 &ptr3);
1579 if (type == ELEMENT_TYPE && ptr1)
1581 int i, save_n;
1582 e = (ElementTypePtr) ptr1;
1584 save_n = NAME_INDEX (PCB);
1586 for (i = 0; i < MAX_ELEMENTNAMES; i++)
1588 if (i == save_n)
1589 EraseElementName (e);
1590 r_delete_entry (PCB->Data->name_tree[i],
1591 (BoxType *) & (e->Name[i]));
1592 memcpy (&(e->Name[i]), &(estr[i]), sizeof (TextType));
1593 e->Name[i].Element = e;
1594 SetTextBoundingBox (&PCB->Font, &(e->Name[i]));
1595 r_insert_entry (PCB->Data->name_tree[i],
1596 (BoxType *) & (e->Name[i]), 0);
1597 if (i == save_n)
1598 DrawElementName (e, 0);
1602 break;
1605 case REMOVE_MODE:
1606 if ((type =
1607 SearchScreen (Note.X, Note.Y, REMOVE_TYPES, &ptr1, &ptr2,
1608 &ptr3)) != NO_TYPE)
1610 if (TEST_FLAG (LOCKFLAG, (LineTypePtr) ptr2))
1612 Message (_("Sorry, the object is locked\n"));
1613 break;
1615 if (type == ELEMENT_TYPE)
1617 RubberbandTypePtr ptr;
1618 int i;
1620 Crosshair.AttachedObject.RubberbandN = 0;
1621 LookupRatLines (type, ptr1, ptr2, ptr3);
1622 ptr = Crosshair.AttachedObject.Rubberband;
1623 for (i = 0; i < Crosshair.AttachedObject.RubberbandN; i++)
1625 if (PCB->RatOn)
1626 EraseRat ((RatTypePtr) ptr->Line);
1627 if (TEST_FLAG (RUBBERENDFLAG, ptr->Line))
1628 MoveObjectToRemoveUndoList (RATLINE_TYPE,
1629 ptr->Line, ptr->Line,
1630 ptr->Line);
1631 else
1632 TOGGLE_FLAG (RUBBERENDFLAG, ptr->Line); /* only remove line once */
1633 ptr++;
1636 RemoveObject (type, ptr1, ptr2, ptr3);
1637 IncrementUndoSerialNumber ();
1638 SetChangedFlag (true);
1640 break;
1642 case ROTATE_MODE:
1643 RotateScreenObject (Note.X, Note.Y,
1644 gui->shift_is_pressed ()? (SWAP_IDENT ?
1645 1 : 3)
1646 : (SWAP_IDENT ? 3 : 1));
1647 break;
1649 /* both are almost the same */
1650 case COPY_MODE:
1651 case MOVE_MODE:
1652 switch (Crosshair.AttachedObject.State)
1654 /* first notify, lookup object */
1655 case STATE_FIRST:
1657 int types = (Settings.Mode == COPY_MODE) ?
1658 COPY_TYPES : MOVE_TYPES;
1660 Crosshair.AttachedObject.Type =
1661 SearchScreen (Note.X, Note.Y, types,
1662 &Crosshair.AttachedObject.Ptr1,
1663 &Crosshair.AttachedObject.Ptr2,
1664 &Crosshair.AttachedObject.Ptr3);
1665 if (Crosshair.AttachedObject.Type != NO_TYPE)
1667 if (Settings.Mode == MOVE_MODE &&
1668 TEST_FLAG (LOCKFLAG, (PinTypePtr)
1669 Crosshair.AttachedObject.Ptr2))
1671 Message (_("Sorry, the object is locked\n"));
1672 Crosshair.AttachedObject.Type = NO_TYPE;
1674 else
1675 AttachForCopy (Note.X, Note.Y);
1677 break;
1680 /* second notify, move or copy object */
1681 case STATE_SECOND:
1682 if (Settings.Mode == COPY_MODE)
1683 CopyObject (Crosshair.AttachedObject.Type,
1684 Crosshair.AttachedObject.Ptr1,
1685 Crosshair.AttachedObject.Ptr2,
1686 Crosshair.AttachedObject.Ptr3,
1687 Note.X - Crosshair.AttachedObject.X,
1688 Note.Y - Crosshair.AttachedObject.Y);
1689 else
1691 MoveObjectAndRubberband (Crosshair.AttachedObject.Type,
1692 Crosshair.AttachedObject.Ptr1,
1693 Crosshair.AttachedObject.Ptr2,
1694 Crosshair.AttachedObject.Ptr3,
1695 Note.X - Crosshair.AttachedObject.X,
1696 Note.Y - Crosshair.AttachedObject.Y);
1697 SetLocalRef (0, 0, false);
1699 SetChangedFlag (true);
1701 /* reset identifiers */
1702 Crosshair.AttachedObject.Type = NO_TYPE;
1703 Crosshair.AttachedObject.State = STATE_FIRST;
1704 break;
1706 break;
1708 /* insert a point into a polygon/line/... */
1709 case INSERTPOINT_MODE:
1710 switch (Crosshair.AttachedObject.State)
1712 /* first notify, lookup object */
1713 case STATE_FIRST:
1714 Crosshair.AttachedObject.Type =
1715 SearchScreen (Note.X, Note.Y, INSERT_TYPES,
1716 &Crosshair.AttachedObject.Ptr1,
1717 &Crosshair.AttachedObject.Ptr2,
1718 &Crosshair.AttachedObject.Ptr3);
1720 if (Crosshair.AttachedObject.Type != NO_TYPE)
1722 if (TEST_FLAG (LOCKFLAG, (PolygonTypePtr)
1723 Crosshair.AttachedObject.Ptr2))
1725 Message (_("Sorry, the object is locked\n"));
1726 Crosshair.AttachedObject.Type = NO_TYPE;
1727 break;
1729 else
1731 /* get starting point of nearest segment */
1732 if (Crosshair.AttachedObject.Type == POLYGON_TYPE)
1734 fake.poly =
1735 (PolygonTypePtr) Crosshair.AttachedObject.Ptr2;
1736 polyIndex =
1737 GetLowestDistancePolygonPoint (fake.poly, Note.X,
1738 Note.Y);
1739 fake.line.Point1 = fake.poly->Points[polyIndex];
1740 fake.line.Point2 = fake.poly->Points[
1741 prev_contour_point (fake.poly, polyIndex)];
1742 Crosshair.AttachedObject.Ptr2 = &fake.line;
1745 Crosshair.AttachedObject.State = STATE_SECOND;
1746 InsertedPoint = *AdjustInsertPoint ();
1749 break;
1751 /* second notify, insert new point into object */
1752 case STATE_SECOND:
1753 if (Crosshair.AttachedObject.Type == POLYGON_TYPE)
1754 InsertPointIntoObject (POLYGON_TYPE,
1755 Crosshair.AttachedObject.Ptr1, fake.poly,
1756 &polyIndex,
1757 InsertedPoint.X, InsertedPoint.Y, false, false);
1758 else
1759 InsertPointIntoObject (Crosshair.AttachedObject.Type,
1760 Crosshair.AttachedObject.Ptr1,
1761 Crosshair.AttachedObject.Ptr2,
1762 &polyIndex,
1763 InsertedPoint.X, InsertedPoint.Y, false, false);
1764 SetChangedFlag (true);
1766 /* reset identifiers */
1767 Crosshair.AttachedObject.Type = NO_TYPE;
1768 Crosshair.AttachedObject.State = STATE_FIRST;
1769 break;
1771 break;
1776 /* --------------------------------------------------------------------------- */
1778 static const char atomic_syntax[] = "Atomic(Save|Restore|Close|Block)";
1780 static const char atomic_help[] = "Save or restore the undo serial number.";
1782 /* %start-doc actions Atomic
1784 This action allows making multiple-action bindings into an atomic
1785 operation that will be undone by a single Undo command. For example,
1786 to optimize rat lines, you'd delete the rats and re-add them. To
1787 group these into a single undo, you'd want the deletions and the
1788 additions to have the same undo serial number. So, you @code{Save},
1789 delete the rats, @code{Restore}, add the rats - using the same serial
1790 number as the deletes, then @code{Block}, which checks to see if the
1791 deletions or additions actually did anything. If not, the serial
1792 number is set to the saved number, as there's nothing to undo. If
1793 something did happen, the serial number is incremented so that these
1794 actions are counted as a single undo step.
1796 @table @code
1798 @item Save
1799 Saves the undo serial number.
1801 @item Restore
1802 Returns it to the last saved number.
1804 @item Close
1805 Sets it to 1 greater than the last save.
1807 @item Block
1808 Does a Restore if there was nothing to undo, else does a Close.
1810 @end table
1812 %end-doc */
1814 static int
1815 ActionAtomic (int argc, char **argv, int x, int y)
1817 if (argc != 1)
1818 AFAIL (atomic);
1820 switch (GetFunctionID (argv[0]))
1822 case F_Save:
1823 SaveUndoSerialNumber ();
1824 break;
1825 case F_Restore:
1826 RestoreUndoSerialNumber ();
1827 break;
1828 case F_Close:
1829 RestoreUndoSerialNumber ();
1830 IncrementUndoSerialNumber ();
1831 break;
1832 case F_Block:
1833 RestoreUndoSerialNumber ();
1834 if (Bumped)
1835 IncrementUndoSerialNumber ();
1836 break;
1838 return 0;
1841 /* -------------------------------------------------------------------------- */
1843 static const char drc_syntax[] = "DRC()";
1845 static const char drc_help[] = "Invoke the DRC check.";
1847 /* %start-doc actions DRC
1849 Note that the design rule check uses the current board rule settings,
1850 not the current style settings.
1852 %end-doc */
1854 static int
1855 ActionDRCheck (int argc, char **argv, int x, int y)
1857 int count;
1859 if (gui->drc_gui == NULL || gui->drc_gui->log_drc_overview)
1861 Message (_("Rules are minspace %d.%02d, minoverlap %d.%d "
1862 "minwidth %d.%02d, minsilk %d.%02d\n"
1863 "min drill %d.%02d, min annular ring %d.%02d\n"),
1864 (PCB->Bloat + 1) / 100, (PCB->Bloat + 1) % 100,
1865 PCB->Shrink / 100, PCB->Shrink % 100,
1866 PCB->minWid / 100, PCB->minWid % 100,
1867 PCB->minSlk / 100, PCB->minSlk % 100,
1868 PCB->minDrill / 100, PCB->minDrill % 100,
1869 PCB->minRing / 100, PCB->minRing % 100);
1871 HideCrosshair (true);
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 RestoreCrosshair (true);
1883 return 0;
1886 /* -------------------------------------------------------------------------- */
1888 static const char dumplibrary_syntax[] = "DumpLibrary()";
1890 static const char dumplibrary_help[] =
1891 "Display the entire contents of the libraries.";
1893 /* %start-doc actions DumpLibrary
1896 %end-doc */
1898 static int
1899 ActionDumpLibrary (int argc, char **argv, int x, int y)
1901 int i, j;
1903 printf ("**** Do not count on this format. It will change ****\n\n");
1904 printf ("MenuN = %d\n", Library.MenuN);
1905 printf ("MenuMax = %d\n", Library.MenuMax);
1906 for (i = 0; i < Library.MenuN; i++)
1908 printf ("Library #%d:\n", i);
1909 printf (" EntryN = %d\n", Library.Menu[i].EntryN);
1910 printf (" EntryMax = %d\n", Library.Menu[i].EntryMax);
1911 printf (" Name = \"%s\"\n", UNKNOWN (Library.Menu[i].Name));
1912 printf (" directory = \"%s\"\n",
1913 UNKNOWN (Library.Menu[i].directory));
1914 printf (" Style = \"%s\"\n", UNKNOWN (Library.Menu[i].Style));
1915 printf (" flag = %d\n", Library.Menu[i].flag);
1917 for (j = 0; j < Library.Menu[i].EntryN; j++)
1919 printf (" #%4d: ", j);
1920 if (Library.Menu[i].Entry[j].Template == (char *) -1)
1922 printf ("newlib: \"%s\"\n",
1923 UNKNOWN (Library.Menu[i].Entry[j].ListEntry));
1925 else
1927 printf ("\"%s\", \"%s\", \"%s\", \"%s\", \"%s\"\n",
1928 UNKNOWN (Library.Menu[i].Entry[j].ListEntry),
1929 UNKNOWN (Library.Menu[i].Entry[j].Template),
1930 UNKNOWN (Library.Menu[i].Entry[j].Package),
1931 UNKNOWN (Library.Menu[i].Entry[j].Value),
1932 UNKNOWN (Library.Menu[i].Entry[j].Description));
1937 return 0;
1940 /* -------------------------------------------------------------------------- */
1942 static const char flip_syntax[] = "Flip(Object|Selected|SelectedElements)";
1944 static const char flip_help[] =
1945 "Flip an element to the opposite side of the board.";
1947 /* %start-doc actions Flip
1949 Note that the location of the element will be symmetric about the
1950 cursor location; i.e. if the part you are pointing at will still be at
1951 the same spot once the element is on the other side. When flipping
1952 multiple elements, this retains their positions relative to each
1953 other, not their absolute positions on the board.
1955 %end-doc */
1957 static int
1958 ActionFlip (int argc, char **argv, int x, int y)
1960 char *function = ARG (0);
1961 ElementTypePtr element;
1962 void *ptrtmp;
1963 int err = 0;
1965 if (function)
1967 HideCrosshair (true);
1968 switch (GetFunctionID (function))
1970 case F_Object:
1971 if ((SearchScreen (x, y, ELEMENT_TYPE,
1972 &ptrtmp, &ptrtmp, &ptrtmp)) != NO_TYPE)
1974 element = (ElementTypePtr) ptrtmp;
1975 ChangeElementSide (element, 2 * Crosshair.Y - PCB->MaxHeight);
1976 IncrementUndoSerialNumber ();
1977 Draw ();
1979 break;
1980 case F_Selected:
1981 case F_SelectedElements:
1982 ChangeSelectedElementSide ();
1983 break;
1984 default:
1985 err = 1;
1986 break;
1988 RestoreCrosshair (true);
1989 if (!err)
1990 return 0;
1993 AFAIL (flip);
1996 /* -------------------------------------------------------------------------- */
1998 static const char message_syntax[] = "Message(message)";
2000 static const char message_help[] = "Writes a message to the log window.";
2002 /* %start-doc actions Message
2004 This action displays a message to the log window. This action is primarily
2005 provided for use by other programs which may interface with PCB. If
2006 multiple arguments are given, each one is sent to the log window
2007 followed by a newline.
2009 %end-doc */
2011 static int
2012 ActionMessage (int argc, char **argv, int x, int y)
2014 int i;
2016 if (argc < 1)
2017 AFAIL (message);
2019 for (i = 0; i < argc; i++)
2021 Message (argv[i]);
2022 Message ("\n");
2025 return 0;
2029 /* -------------------------------------------------------------------------- */
2031 static const char setthermal_syntax[] =
2032 "SetThermal(Object|SelectedPins|SelectedVias|Selected, Style)";
2034 static const char setthermal_help[] =
2035 "Set the thermal (on the current layer) of pins or vias to the given style.\n"
2036 "Style = 0 means no thermal.\n"
2037 "Style = 1 has diagonal fingers with sharp edges.\n"
2038 "Style = 2 has horizontal and vertical fingers with sharp edges.\n"
2039 "Style = 3 is a solid connection to the plane."
2040 "Style = 4 has diagonal fingers with rounded edges.\n"
2041 "Style = 5 has horizontal and vertical fingers with rounded edges.\n";
2043 /* %start-doc actions SetThermal
2045 This changes how/whether pins or vias connect to any rectangle or polygon
2046 on the current layer. The first argument can specify one object, or all
2047 selected pins, or all selected vias, or all selected pins and vias.
2048 The second argument specifies the style of connection.
2049 There are 5 possibilities:
2050 0 - no connection,
2051 1 - 45 degree fingers with sharp edges,
2052 2 - horizontal & vertical fingers with sharp edges,
2053 3 - solid connection,
2054 4 - 45 degree fingers with rounded corners,
2055 5 - horizontal & vertical fingers with rounded corners.
2057 Pins and Vias may have thermals whether or not there is a polygon available
2058 to connect with. However, they will have no effect without the polygon.
2059 %end-doc */
2061 static int
2062 ActionSetThermal (int argc, char **argv, int x, int y)
2064 char *function = ARG (0);
2065 char *style = ARG (1);
2066 void *ptr1, *ptr2, *ptr3;
2067 int type, kind;
2068 int err = 0;
2070 if (function && *function && style && *style)
2072 bool absolute;
2074 kind = GetValue (style, NULL, &absolute);
2075 HideCrosshair (true);
2076 if (absolute)
2077 switch (GetFunctionID (function))
2079 case F_Object:
2080 if ((type =
2081 SearchScreen (Crosshair.X, Crosshair.Y, CHANGETHERMAL_TYPES,
2082 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
2084 ChangeObjectThermal (type, ptr1, ptr2, ptr3, kind);
2085 IncrementUndoSerialNumber ();
2086 Draw ();
2088 break;
2089 case F_SelectedPins:
2090 ChangeSelectedThermals (PIN_TYPE, kind);
2091 break;
2092 case F_SelectedVias:
2093 ChangeSelectedThermals (VIA_TYPE, kind);
2094 break;
2095 case F_Selected:
2096 case F_SelectedElements:
2097 ChangeSelectedThermals (CHANGETHERMAL_TYPES, kind);
2098 break;
2099 default:
2100 err = 1;
2101 break;
2103 else
2104 err = 1;
2105 RestoreCrosshair (true);
2106 if (!err)
2107 return 0;
2110 AFAIL (setthermal);
2113 /* ---------------------------------------------------------------------------
2114 * action routine to move the X pointer relative to the current position
2115 * syntax: MovePointer(deltax,deltay)
2117 void
2118 ActionMovePointer (char *deltax, char *deltay)
2120 LocationType x, y, dx, dy;
2122 /* save old crosshair position */
2123 x = Crosshair.X;
2124 y = Crosshair.Y;
2125 dx = (LocationType) (atoi (deltax) * PCB->Grid);
2126 dy = (LocationType) (atoi (deltay) * PCB->Grid);
2127 MoveCrosshairRelative (TO_SCREEN_SIGN_X (dx), TO_SCREEN_SIGN_Y (dy));
2128 FitCrosshairIntoGrid (Crosshair.X, Crosshair.Y);
2129 /* restore crosshair for erasure */
2130 Crosshair.X = x;
2131 Crosshair.Y = y;
2132 HideCrosshair (false);
2133 MoveCrosshairRelative (TO_SCREEN_SIGN_X (dx), TO_SCREEN_SIGN_Y (dy));
2134 /* update object position and cursor location */
2135 AdjustAttachedObjects ();
2136 RestoreCrosshair (false);
2139 /* ---------------------------------------------------------------------------
2140 * !!! no action routine !!!
2142 * event handler to set the cursor according to the X pointer position
2143 * called from inside main.c
2145 void
2146 EventMoveCrosshair (int ev_x, int ev_y)
2148 #ifdef HAVE_LIBSTROKE
2149 if (mid_stroke)
2151 StrokeBox.X2 = TO_PCB_X (ev_x);
2152 StrokeBox.Y2 = TO_PCB_Y (ev_y);
2153 stroke_record (Event->x, ev_y);
2154 return;
2156 #endif /* HAVE_LIBSTROKE */
2157 /* ignore events that are caused by ActionMovePointer */
2158 if (!IgnoreMotionEvents)
2160 if (MoveCrosshairAbsolute (ev_x, ev_y))
2163 /* update object position and cursor location */
2164 AdjustAttachedObjects ();
2165 RestoreCrosshair (false);
2168 else
2169 IgnoreMotionEvents = false;
2172 /* --------------------------------------------------------------------------- */
2174 static const char setvalue_syntax[] =
2175 "SetValue(Grid|Line|LineSize|Text|TextScale|ViaDrillingHole|Via|ViaSize, delta)";
2177 static const char setvalue_help[] =
2178 "Change various board-wide values and sizes.";
2180 /* %start-doc actions SetValue
2182 @table @code
2184 @item ViaDrillingHole
2185 Changes the diameter of the drill for new vias.
2187 @item Grid
2188 Sets the grid spacing.
2190 @item Line
2191 @item LineSize
2192 Changes the thickness of new lines.
2194 @item Via
2195 @item ViaSize
2196 Changes the diameter of new vias.
2198 @item Text
2199 @item TextScale
2200 Changes the size of new text.
2202 @end table
2204 %end-doc */
2206 static int
2207 ActionSetValue (int argc, char **argv, int x, int y)
2209 char *function = ARG (0);
2210 char *val = ARG (1);
2211 char *units = ARG (2);
2212 bool absolute; /* flag for 'absolute' value */
2213 float value;
2214 int err = 0;
2216 if (function && val)
2218 HideCrosshair (true);
2219 value = GetValue (val, units, &absolute);
2220 switch (GetFunctionID (function))
2222 case F_ViaDrillingHole:
2223 SetViaDrillingHole (absolute ? value :
2224 value + Settings.ViaDrillingHole,
2225 false);
2226 hid_action ("RouteStylesChanged");
2227 break;
2229 case F_Grid:
2230 if (!absolute)
2232 if ((value == (int) value && PCB->Grid == (int) PCB->Grid)
2233 || (value != (int) value && PCB->Grid != (int) PCB->Grid)
2234 || PCB->Grid ==1)
2237 * On the way down short against the minimum
2238 * PCB drawing unit
2240 if ((value + PCB->Grid) < 1)
2241 SetGrid (1, false);
2242 else if (PCB->Grid == 1)
2243 SetGrid ( value, false);
2244 else
2245 SetGrid (value + PCB->Grid, false);
2248 else
2249 Message (_
2250 ("Don't combine metric/English grids like that!\n"));
2252 else
2253 SetGrid (value, false);
2254 break;
2256 case F_LineSize:
2257 case F_Line:
2258 SetLineSize (absolute ? value : value + Settings.LineThickness);
2259 hid_action ("RouteStylesChanged");
2260 break;
2262 case F_Via:
2263 case F_ViaSize:
2264 SetViaSize (absolute ? value : value + Settings.ViaThickness, false);
2265 hid_action ("RouteStylesChanged");
2266 break;
2268 case F_Text:
2269 case F_TextScale:
2270 value /= 45;
2271 SetTextScale (absolute ? value : value + Settings.TextScale);
2272 break;
2273 default:
2274 err = 1;
2275 break;
2277 RestoreCrosshair (true);
2278 if (!err)
2279 return 0;
2282 AFAIL (setvalue);
2286 /* --------------------------------------------------------------------------- */
2288 static const char quit_syntax[] = "Quit()";
2290 static const char quit_help[] = "Quits the application after confirming.";
2292 /* %start-doc actions Quit
2294 If you have unsaved changes, you will be prompted to confirm (or
2295 save) before quitting.
2297 %end-doc */
2299 static int
2300 ActionQuit (int argc, char **argv, int x, int y)
2302 char *force = ARG (0);
2303 if (force && strcasecmp (force, "force") == 0)
2305 PCB->Changed = 0;
2306 exit (0);
2308 if (!PCB->Changed || gui->close_confirm_dialog () == HID_CLOSE_CONFIRM_OK)
2309 QuitApplication ();
2310 return 1;
2313 /* --------------------------------------------------------------------------- */
2315 static const char connection_syntax[] =
2316 "Connection(Find|ResetLinesAndPolygons|ResetPinsAndVias|Reset)";
2318 static const char connection_help[] =
2319 "Searches connections of the object at the cursor position.";
2321 /* %start-doc actions Connection
2323 Connections found with this action will be highlighted in the
2324 ``connected-color'' color and will have the ``found'' flag set.
2326 @table @code
2328 @item Find
2329 The net under the cursor is ``found''.
2331 @item ResetLinesAndPolygons
2332 Any ``found'' lines and polygons are marked ``not found''.
2334 @item ResetPinsAndVias
2335 Any ``found'' pins and vias are marked ``not found''.
2337 @item Reset
2338 All ``found'' objects are marked ``not found''.
2340 @item Measure
2341 The net under the cursor is found and measured (the lengths of all
2342 line segments are added together)
2344 @end table
2346 %end-doc */
2348 static int
2349 ActionConnection (int argc, char **argv, int x, int y)
2351 char *function = ARG (0);
2352 if (function)
2354 HideCrosshair (true);
2355 switch (GetFunctionID (function))
2357 case F_Find:
2359 gui->get_coords (_("Click on a connection"), &x, &y);
2360 LookupConnection (x, y, true, 1, FOUNDFLAG);
2361 break;
2364 case F_ResetLinesAndPolygons:
2365 ResetFoundLinesAndPolygons (true);
2366 break;
2368 case F_ResetPinsViasAndPads:
2369 ResetFoundPinsViasAndPads (true);
2370 break;
2372 case F_Reset:
2373 SaveUndoSerialNumber ();
2374 ResetFoundPinsViasAndPads (true);
2375 RestoreUndoSerialNumber ();
2376 ResetFoundLinesAndPolygons (true);
2377 break;
2379 RestoreCrosshair (true);
2380 return 0;
2383 AFAIL (connection);
2386 /* --------------------------------------------------------------------------- */
2388 static const char disperseelements_syntax[] =
2389 "DisperseElements(All|Selected)";
2391 static const char disperseelements_help[] = "Disperses elements.";
2393 /* %start-doc actions DisperseElements
2395 Normally this is used when starting a board, by selecting all elements
2396 and then dispersing them. This scatters the elements around the board
2397 so that you can pick individual ones, rather than have all the
2398 elements at the same 0,0 coordinate and thus impossible to choose
2399 from.
2401 %end-doc */
2403 #define GAP 10000
2405 static int
2406 ActionDisperseElements (int argc, char **argv, int x, int y)
2408 char *function = ARG (0);
2409 long minx, miny, maxx, maxy, dx, dy;
2410 int all = 0, bad = 0;
2412 minx = GAP;
2413 miny = GAP;
2414 maxx = GAP;
2415 maxy = GAP;
2417 if (!function || !*function)
2419 bad = 1;
2421 else
2423 switch (GetFunctionID (function))
2425 case F_All:
2426 all = 1;
2427 break;
2429 case F_Selected:
2430 all = 0;
2431 break;
2433 default:
2434 bad = 1;
2438 if (bad)
2440 AFAIL (disperseelements);
2444 ELEMENT_LOOP (PCB->Data);
2447 * If we want to disperse selected elements, maybe we need smarter
2448 * code here to avoid putting components on top of others which
2449 * are not selected. For now, I'm assuming that this is typically
2450 * going to be used either with a brand new design or a scratch
2451 * design holding some new components
2453 if (!TEST_FLAG (LOCKFLAG, element) && (all || TEST_FLAG (SELECTEDFLAG, element)))
2456 /* figure out how much to move the element */
2457 dx = minx - element->BoundingBox.X1;
2459 /* snap to the grid */
2460 dx -= (element->MarkX + dx) % (long) (PCB->Grid);
2463 * and add one grid size so we make sure we always space by GAP or
2464 * more
2466 dx += (long) (PCB->Grid);
2468 /* Figure out if this row has room. If not, start a new row */
2469 if (GAP + element->BoundingBox.X2 + dx > PCB->MaxWidth)
2471 miny = maxy + GAP;
2472 minx = GAP;
2475 /* figure out how much to move the element */
2476 dx = minx - element->BoundingBox.X1;
2477 dy = miny - element->BoundingBox.Y1;
2479 /* snap to the grid */
2480 dx -= (element->MarkX + dx) % (long) (PCB->Grid);
2481 dx += (long) (PCB->Grid);
2482 dy -= (element->MarkY + dy) % (long) (PCB->Grid);
2483 dy += (long) (PCB->Grid);
2485 /* move the element */
2486 MoveElementLowLevel (PCB->Data, element, dx, dy);
2488 /* and add to the undo list so we can undo this operation */
2489 AddObjectToMoveUndoList (ELEMENT_TYPE, NULL, NULL, element, dx, dy);
2491 /* keep track of how tall this row is */
2492 minx += element->BoundingBox.X2 - element->BoundingBox.X1 + GAP;
2493 if (maxy < element->BoundingBox.Y2)
2495 maxy = element->BoundingBox.Y2;
2500 END_LOOP;
2502 /* done with our action so increment the undo # */
2503 IncrementUndoSerialNumber ();
2505 ClearAndRedrawOutput ();
2506 SetChangedFlag (true);
2508 return 0;
2511 #undef GAP
2513 /* --------------------------------------------------------------------------- */
2515 static const char display_syntax[] =
2516 "Display(NameOnPCB|Description|Value)\n"
2517 "Display(Grid|Redraw)\n"
2518 "Display(CycleClip|CycleCrosshair|Toggle45Degree|ToggleStartDirection)\n"
2519 "Display(ToggleGrid|ToggleRubberBandMode|ToggleUniqueNames)\n"
2520 "Display(ToggleMask|ToggleName|ToggleClearLine|ToggleFullPoly|ToggleSnapPin)\n"
2521 "Display(ToggleThindraw|ToggleThindrawPoly|ToggleOrthoMove|ToggleLocalRef)\n"
2522 "Display(ToggleCheckPlanes|ToggleShowDRC|ToggleAutoDRC)\n"
2523 "Display(ToggleLiveRoute|LockNames|OnlyNames)\n"
2524 "Display(Pinout|PinOrPadName)\n" "Display(Scroll, Direction)";
2526 static const char display_help[] = "Several display-related actions.";
2528 /* %start-doc actions Display
2530 @table @code
2532 @item NameOnPCB
2533 @item Description
2534 @item Value
2535 Specify whether all elements show their name, description, or value.
2537 @item Redraw
2538 Redraw the whole board.
2540 @item Toggle45Degree
2541 When clear, lines can be drawn at any angle. When set, lines are
2542 restricted to multiples of 45 degrees and requested lines may be
2543 broken up according to the clip setting.
2545 @item CycleClip
2546 Changes the way lines are restricted to 45 degree increments. The
2547 various settings are: straight only, orthogonal then angled, and angled
2548 then orthogonal. If AllDirections is set, this action disables it.
2550 @item CycleCrosshair
2551 Changes crosshair drawing. Crosshair may accept form of 4-ray,
2552 8-ray and 12-ray cross.
2554 @item ToggleRubberBandMode
2555 If set, moving an object moves all the lines attached to it too.
2557 @item ToggleStartDirection
2558 If set, each time you set a point in a line, the Clip toggles between
2559 orth-angle and angle-ortho.
2561 @item ToggleUniqueNames
2562 If set, you will not be permitted to change the name of an element to
2563 match that of another element.
2565 @item ToggleSnapPin
2566 If set, pin centers and pad end points are treated as additional grid
2567 points that the cursor can snap to.
2569 @item ToggleLocalRef
2570 If set, the mark is automatically set to the beginning of any move, so
2571 you can see the relative distance you've moved.
2573 @item ToggleThindraw
2574 If set, objects on the screen are drawn as outlines (lines are drawn
2575 as center-lines). This lets you see line endpoints hidden under pins,
2576 for example.
2578 @item ToggleThindrawPoly
2579 If set, polygons on the screen are drawn as outlines.
2581 @item ToggleShowDRC
2582 If set, pending objects (i.e. lines you're in the process of drawing)
2583 will be drawn with an outline showing how far away from other copper
2584 you need to be.
2586 @item ToggleLiveRoute
2587 If set, the progress of the autorouter will be visible on the screen.
2589 @item ToggleAutoDRC
2590 If set, you will not be permitted to make connections which violate
2591 the current DRC and netlist settings.
2593 @item ToggleCheckPlanes
2594 If set, lines and arcs aren't drawn, which usually leaves just the
2595 polygons. If you also disable all but the layer you're interested in,
2596 this allows you to check for isolated regions.
2598 @item ToggleOrthoMove
2599 If set, the crosshair is only allowed to move orthogonally from its
2600 previous position. I.e. you can move an element or line up, down,
2601 left, or right, but not up+left or down+right.
2603 @item ToggleName
2604 Selects whether the pinouts show the pin names or the pin numbers.
2606 @item ToggleMask
2607 Turns the solder mask on or off.
2609 @item ToggleClearLine
2610 When set, the clear-line flag causes new lines and arcs to have their
2611 ``clear polygons'' flag set, so they won't be electrically connected
2612 to any polygons they overlap.
2614 @item ToggleFullPoly
2615 When set, the full-poly flag causes new polygons to have their
2616 ``full polygon'' flag set, so all parts of them will be displayed
2617 instead of only the biggest one.
2619 @item ToggleGrid
2620 Resets the origin of the current grid to be wherever the mouse pointer
2621 is (not where the crosshair currently is). If you provide two numbers
2622 after this, the origin is set to that coordinate. The numbers are in
2623 PCB internal units, currently 1/100 mil.
2625 @item Grid
2626 Toggles whether the grid is displayed or not.
2628 @item Pinout
2629 Causes the pinout of the element indicated by the cursor to be
2630 displayed, usually in a separate window.
2632 @item PinOrPadName
2633 Toggles whether the names of pins, pads, or (yes) vias will be
2634 displayed. If the cursor is over an element, all of its pins and pads
2635 are affected.
2637 @item Step <direction> <amount> <units>
2638 Steps the crosshair in the given direction, with 1=down/left, 2=down,
2639 etc, according to the numeric keypad layout. If amount is not given,
2640 the crosshair steps along the grid.
2642 @end table
2644 %end-doc */
2646 static int
2647 ActionDisplay (int argc, char **argv, int childX, int childY)
2649 char *function, *str_dir;
2650 int id;
2651 int err = 0;
2653 function = ARG (0);
2654 str_dir = ARG (1);
2656 if (function && (!str_dir || !*str_dir))
2658 HideCrosshair (true);
2659 switch (id = GetFunctionID (function))
2662 /* redraw layout with clearing the background */
2663 case F_ClearAndRedraw:
2664 gui->invalidate_all();
2665 break;
2667 /* redraw layout without clearing the background */
2668 case F_Redraw:
2670 BoxType area;
2671 area.X1 = 0;
2672 area.Y1 = 0;
2673 area.X2 = Output.Width;
2674 area.Y2 = Output.Height;
2675 RedrawOutput (&area);
2676 break;
2679 /* change the displayed name of elements */
2680 case F_Value:
2681 case F_NameOnPCB:
2682 case F_Description:
2683 ELEMENT_LOOP (PCB->Data);
2685 EraseElementName (element);
2687 END_LOOP;
2688 CLEAR_FLAG (DESCRIPTIONFLAG | NAMEONPCBFLAG, PCB);
2689 switch (id)
2691 case F_Value:
2692 break;
2693 case F_NameOnPCB:
2694 SET_FLAG (NAMEONPCBFLAG, PCB);
2695 break;
2696 case F_Description:
2697 SET_FLAG (DESCRIPTIONFLAG, PCB);
2698 break;
2700 ELEMENT_LOOP (PCB->Data);
2702 DrawElementName (element, 0);
2704 END_LOOP;
2705 Draw ();
2706 break;
2708 /* toggle line-adjust flag */
2709 case F_ToggleAllDirections:
2710 TOGGLE_FLAG (ALLDIRECTIONFLAG, PCB);
2711 AdjustAttachedObjects ();
2712 break;
2714 case F_CycleClip:
2715 if TEST_FLAG
2716 (ALLDIRECTIONFLAG, PCB)
2718 TOGGLE_FLAG (ALLDIRECTIONFLAG, PCB);
2719 PCB->Clipping = 0;
2721 else
2722 PCB->Clipping = (PCB->Clipping + 1) % 3;
2723 AdjustAttachedObjects ();
2724 break;
2726 case F_CycleCrosshair:
2727 Crosshair.shape++;
2728 if (Crosshair_Shapes_Number == Crosshair.shape)
2729 Crosshair.shape = Basic_Crosshair_Shape;
2730 break;
2732 case F_ToggleRubberBandMode:
2733 TOGGLE_FLAG (RUBBERBANDFLAG, PCB);
2734 break;
2736 case F_ToggleStartDirection:
2737 TOGGLE_FLAG (SWAPSTARTDIRFLAG, PCB);
2738 break;
2740 case F_ToggleUniqueNames:
2741 TOGGLE_FLAG (UNIQUENAMEFLAG, PCB);
2742 break;
2744 case F_ToggleSnapPin:
2745 TOGGLE_FLAG (SNAPPINFLAG, PCB);
2746 break;
2748 case F_ToggleLocalRef:
2749 TOGGLE_FLAG (LOCALREFFLAG, PCB);
2750 break;
2752 case F_ToggleThindraw:
2753 TOGGLE_FLAG (THINDRAWFLAG, PCB);
2754 ClearAndRedrawOutput ();
2755 break;
2757 case F_ToggleThindrawPoly:
2758 TOGGLE_FLAG (THINDRAWPOLYFLAG, PCB);
2759 ClearAndRedrawOutput ();
2760 break;
2762 case F_ToggleLockNames:
2763 TOGGLE_FLAG (LOCKNAMESFLAG, PCB);
2764 CLEAR_FLAG (ONLYNAMESFLAG, PCB);
2765 break;
2767 case F_ToggleOnlyNames:
2768 TOGGLE_FLAG (ONLYNAMESFLAG, PCB);
2769 CLEAR_FLAG (LOCKNAMESFLAG, PCB);
2770 break;
2772 case F_ToggleHideNames:
2773 TOGGLE_FLAG (HIDENAMESFLAG, PCB);
2774 ClearAndRedrawOutput ();
2775 break;
2777 case F_ToggleShowDRC:
2778 TOGGLE_FLAG (SHOWDRCFLAG, PCB);
2779 break;
2781 case F_ToggleLiveRoute:
2782 TOGGLE_FLAG (LIVEROUTEFLAG, PCB);
2783 break;
2785 case F_ToggleAutoDRC:
2786 TOGGLE_FLAG (AUTODRCFLAG, PCB);
2787 if (TEST_FLAG (AUTODRCFLAG, PCB) && Settings.Mode == LINE_MODE)
2789 SaveUndoSerialNumber ();
2790 ResetFoundPinsViasAndPads (true);
2791 RestoreUndoSerialNumber ();
2792 ResetFoundLinesAndPolygons (true);
2793 if (Crosshair.AttachedLine.State != STATE_FIRST)
2794 LookupConnection (Crosshair.AttachedLine.Point1.X,
2795 Crosshair.AttachedLine.Point1.Y, true, 1,
2796 FOUNDFLAG);
2798 break;
2800 case F_ToggleCheckPlanes:
2801 TOGGLE_FLAG (CHECKPLANESFLAG, PCB);
2802 ClearAndRedrawOutput ();
2803 break;
2805 case F_ToggleOrthoMove:
2806 TOGGLE_FLAG (ORTHOMOVEFLAG, PCB);
2807 break;
2809 case F_ToggleName:
2810 TOGGLE_FLAG (SHOWNUMBERFLAG, PCB);
2811 UpdateAll ();
2812 break;
2814 case F_ToggleMask:
2815 TOGGLE_FLAG (SHOWMASKFLAG, PCB);
2816 UpdateAll ();
2817 break;
2819 case F_ToggleClearLine:
2820 TOGGLE_FLAG (CLEARNEWFLAG, PCB);
2821 break;
2823 case F_ToggleFullPoly:
2824 TOGGLE_FLAG (NEWFULLPOLYFLAG, PCB);
2825 break;
2827 /* shift grid alignment */
2828 case F_ToggleGrid:
2830 float oldGrid;
2832 oldGrid = PCB->Grid;
2833 PCB->Grid = 1.0;
2834 if (MoveCrosshairAbsolute (Crosshair.X, Crosshair.Y))
2835 RestoreCrosshair (false); /* was hidden by MoveCrosshairAbs */
2836 SetGrid (oldGrid, true);
2838 break;
2840 /* toggle displaying of the grid */
2841 case F_Grid:
2842 Settings.DrawGrid = !Settings.DrawGrid;
2843 UpdateAll ();
2844 break;
2846 /* display the pinout of an element */
2847 case F_Pinout:
2849 ElementTypePtr element;
2850 void *ptrtmp;
2851 int x, y;
2853 gui->get_coords (_("Click on an element"), &x, &y);
2854 if ((SearchScreen
2855 (x, y, ELEMENT_TYPE, &ptrtmp,
2856 &ptrtmp, &ptrtmp)) != NO_TYPE)
2858 element = (ElementTypePtr) ptrtmp;
2859 gui->show_item (element);
2861 break;
2864 /* toggle displaying of pin/pad/via names */
2865 case F_PinOrPadName:
2867 void *ptr1, *ptr2, *ptr3;
2869 switch (SearchScreen (Crosshair.X, Crosshair.Y,
2870 ELEMENT_TYPE | PIN_TYPE | PAD_TYPE |
2871 VIA_TYPE, (void **) &ptr1, (void **) &ptr2,
2872 (void **) &ptr3))
2874 case ELEMENT_TYPE:
2875 PIN_LOOP ((ElementTypePtr) ptr1);
2877 if (TEST_FLAG (DISPLAYNAMEFLAG, pin))
2878 ErasePinName (pin);
2879 else
2880 DrawPinName (pin, 0);
2881 AddObjectToFlagUndoList (PIN_TYPE, ptr1, pin, pin);
2882 TOGGLE_FLAG (DISPLAYNAMEFLAG, pin);
2884 END_LOOP;
2885 PAD_LOOP ((ElementTypePtr) ptr1);
2887 if (TEST_FLAG (DISPLAYNAMEFLAG, pad))
2888 ErasePadName (pad);
2889 else
2890 DrawPadName (pad, 0);
2891 AddObjectToFlagUndoList (PAD_TYPE, ptr1, pad, pad);
2892 TOGGLE_FLAG (DISPLAYNAMEFLAG, pad);
2894 END_LOOP;
2895 SetChangedFlag (true);
2896 IncrementUndoSerialNumber ();
2897 Draw ();
2898 break;
2900 case PIN_TYPE:
2901 if (TEST_FLAG (DISPLAYNAMEFLAG, (PinTypePtr) ptr2))
2902 ErasePinName ((PinTypePtr) ptr2);
2903 else
2904 DrawPinName ((PinTypePtr) ptr2, 0);
2905 AddObjectToFlagUndoList (PIN_TYPE, ptr1, ptr2, ptr3);
2906 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PinTypePtr) ptr2);
2907 SetChangedFlag (true);
2908 IncrementUndoSerialNumber ();
2909 Draw ();
2910 break;
2912 case PAD_TYPE:
2913 if (TEST_FLAG (DISPLAYNAMEFLAG, (PadTypePtr) ptr2))
2914 ErasePadName ((PadTypePtr) ptr2);
2915 else
2916 DrawPadName ((PadTypePtr) ptr2, 0);
2917 AddObjectToFlagUndoList (PAD_TYPE, ptr1, ptr2, ptr3);
2918 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PadTypePtr) ptr2);
2919 SetChangedFlag (true);
2920 IncrementUndoSerialNumber ();
2921 Draw ();
2922 break;
2923 case VIA_TYPE:
2924 if (TEST_FLAG (DISPLAYNAMEFLAG, (PinTypePtr) ptr2))
2925 EraseViaName ((PinTypePtr) ptr2);
2926 else
2927 DrawViaName ((PinTypePtr) ptr2, 0);
2928 AddObjectToFlagUndoList (VIA_TYPE, ptr1, ptr2, ptr3);
2929 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PinTypePtr) ptr2);
2930 SetChangedFlag (true);
2931 IncrementUndoSerialNumber ();
2932 Draw ();
2933 break;
2935 break;
2937 default:
2938 err = 1;
2940 RestoreCrosshair (true);
2942 else if (function && str_dir)
2944 switch (GetFunctionID (function))
2946 case F_ToggleGrid:
2947 if (argc > 2)
2949 /* FIXME: units */
2950 PCB->GridOffsetX = atoi (argv[1]);
2951 PCB->GridOffsetY = atoi (argv[2]);
2952 if (Settings.DrawGrid)
2953 UpdateAll ();
2955 break;
2957 default:
2958 err = 1;
2959 break;
2963 if (!err)
2964 return 0;
2966 AFAIL (display);
2969 /* --------------------------------------------------------------------------- */
2971 static const char mode_syntax[] =
2972 "Mode(Arc|Arrow|Copy|InsertPoint|Line|Lock|Move|None|PasteBuffer)\n"
2973 "Mode(Polygon|Rectangle|Remove|Rotate|Text|Thermal|Via)\n"
2974 "Mode(Notify|Release|Cancel|Stroke)\n" "Mode(Save|Restore)";
2976 static const char mode_help[] = "Change or use the tool mode.";
2978 /* %start-doc actions Mode
2980 @table @code
2982 @item Arc
2983 @itemx Arrow
2984 @itemx Copy
2985 @itemx InsertPoint
2986 @itemx Line
2987 @itemx Lock
2988 @itemx Move
2989 @itemx None
2990 @itemx PasteBuffer
2991 @itemx Polygon
2992 @itemx Rectangle
2993 @itemx Remove
2994 @itemx Rotate
2995 @itemx Text
2996 @itemx Thermal
2997 @itemx Via
2998 Select the indicated tool.
3000 @item Notify
3001 Called when you press the mouse button, or move the mouse.
3003 @item Release
3004 Called when you release the mouse button.
3006 @item Cancel
3007 Cancels any pending tool activity, allowing you to restart elsewhere.
3008 For example, this allows you to start a new line rather than attach a
3009 line to the previous line.
3011 @item Escape
3012 Similar to Cancel but calling this action a second time will return
3013 to the Arrow tool.
3015 @item Stroke
3016 If your @code{pcb} was built with libstroke, this invokes the stroke
3017 input method. If not, this will restart a drawing mode if you were
3018 drawing, else it will select objects.
3020 @item Save
3021 Remembers the current tool.
3023 @item Restore
3024 Restores the tool to the last saved tool.
3026 @end table
3028 %end-doc */
3030 static int
3031 ActionMode (int argc, char **argv, int x, int y)
3033 char *function = ARG (0);
3035 if (function)
3037 Note.X = Crosshair.X;
3038 Note.Y = Crosshair.Y;
3039 HideCrosshair (true);
3040 switch (GetFunctionID (function))
3042 case F_Arc:
3043 SetMode (ARC_MODE);
3044 break;
3045 case F_Arrow:
3046 SetMode (ARROW_MODE);
3047 break;
3048 case F_Copy:
3049 SetMode (COPY_MODE);
3050 break;
3051 case F_InsertPoint:
3052 SetMode (INSERTPOINT_MODE);
3053 break;
3054 case F_Line:
3055 SetMode (LINE_MODE);
3056 break;
3057 case F_Lock:
3058 SetMode (LOCK_MODE);
3059 break;
3060 case F_Move:
3061 SetMode (MOVE_MODE);
3062 break;
3063 case F_None:
3064 SetMode (NO_MODE);
3065 break;
3066 case F_Cancel:
3068 int saved_mode = Settings.Mode;
3069 SetMode (NO_MODE);
3070 SetMode (saved_mode);
3072 break;
3073 case F_Escape:
3075 switch (Settings.Mode)
3077 case VIA_MODE:
3078 case PASTEBUFFER_MODE:
3079 case TEXT_MODE:
3080 case ROTATE_MODE:
3081 case REMOVE_MODE:
3082 case MOVE_MODE:
3083 case COPY_MODE:
3084 case INSERTPOINT_MODE:
3085 case RUBBERBANDMOVE_MODE:
3086 case THERMAL_MODE:
3087 case LOCK_MODE:
3088 SetMode (NO_MODE);
3089 SetMode (ARROW_MODE);
3090 break;
3092 case LINE_MODE:
3093 if (Crosshair.AttachedLine.State == STATE_FIRST)
3094 SetMode (ARROW_MODE);
3095 else
3097 SetMode (NO_MODE);
3098 SetMode (LINE_MODE);
3100 break;
3102 case RECTANGLE_MODE:
3103 if (Crosshair.AttachedBox.State == STATE_FIRST)
3104 SetMode (ARROW_MODE);
3105 else
3107 SetMode (NO_MODE);
3108 SetMode (RECTANGLE_MODE);
3110 break;
3112 case POLYGON_MODE:
3113 if (Crosshair.AttachedLine.State == STATE_FIRST)
3114 SetMode (ARROW_MODE);
3115 else
3117 SetMode (NO_MODE);
3118 SetMode (POLYGON_MODE);
3120 break;
3122 case POLYGONHOLE_MODE:
3123 if (Crosshair.AttachedLine.State == STATE_FIRST)
3124 SetMode (ARROW_MODE);
3125 else
3127 SetMode (NO_MODE);
3128 SetMode (POLYGONHOLE_MODE);
3130 break;
3132 case ARC_MODE:
3133 if (Crosshair.AttachedBox.State == STATE_FIRST)
3134 SetMode (ARROW_MODE);
3135 else
3137 SetMode (NO_MODE);
3138 SetMode (ARC_MODE);
3140 break;
3142 case ARROW_MODE:
3143 break;
3145 default:
3146 break;
3149 break;
3151 case F_Notify:
3152 NotifyMode ();
3153 break;
3154 case F_PasteBuffer:
3155 SetMode (PASTEBUFFER_MODE);
3156 break;
3157 case F_Polygon:
3158 SetMode (POLYGON_MODE);
3159 break;
3160 case F_PolygonHole:
3161 SetMode (POLYGONHOLE_MODE);
3162 break;
3163 #ifndef HAVE_LIBSTROKE
3164 case F_Release:
3165 ReleaseMode ();
3166 break;
3167 #else
3168 case F_Release:
3169 if (mid_stroke)
3170 FinishStroke ();
3171 else
3172 ReleaseMode ();
3173 break;
3174 #endif
3175 case F_Remove:
3176 SetMode (REMOVE_MODE);
3177 break;
3178 case F_Rectangle:
3179 SetMode (RECTANGLE_MODE);
3180 break;
3181 case F_Rotate:
3182 SetMode (ROTATE_MODE);
3183 break;
3184 case F_Stroke:
3185 #ifdef HAVE_LIBSTROKE
3186 mid_stroke = true;
3187 StrokeBox.X1 = Crosshair.X;
3188 StrokeBox.Y1 = Crosshair.Y;
3189 break;
3190 #else
3191 /* Handle middle mouse button restarts of drawing mode. If not in
3192 | a drawing mode, middle mouse button will select objects.
3194 if (Settings.Mode == LINE_MODE
3195 && Crosshair.AttachedLine.State != STATE_FIRST)
3197 SetMode (LINE_MODE);
3199 else if (Settings.Mode == ARC_MODE
3200 && Crosshair.AttachedBox.State != STATE_FIRST)
3201 SetMode (ARC_MODE);
3202 else if (Settings.Mode == RECTANGLE_MODE
3203 && Crosshair.AttachedBox.State != STATE_FIRST)
3204 SetMode (RECTANGLE_MODE);
3205 else if (Settings.Mode == POLYGON_MODE
3206 && Crosshair.AttachedLine.State != STATE_FIRST)
3207 SetMode (POLYGON_MODE);
3208 else
3210 SaveMode ();
3211 saved_mode = true;
3212 SetMode (ARROW_MODE);
3213 NotifyMode ();
3215 break;
3216 #endif
3217 case F_Text:
3218 SetMode (TEXT_MODE);
3219 break;
3220 case F_Thermal:
3221 SetMode (THERMAL_MODE);
3222 break;
3223 case F_Via:
3224 SetMode (VIA_MODE);
3225 break;
3227 case F_Restore: /* restore the last saved mode */
3228 RestoreMode ();
3229 break;
3231 case F_Save: /* save currently selected mode */
3232 SaveMode ();
3233 break;
3235 RestoreCrosshair (true);
3236 return 0;
3239 AFAIL (mode);
3242 /* --------------------------------------------------------------------------- */
3244 static const char removeselected_syntax[] = "RemoveSelected()";
3246 static const char removeselected_help[] = "Removes any selected objects.";
3248 /* %start-doc actions RemoveSelected
3250 %end-doc */
3252 static int
3253 ActionRemoveSelected (int argc, char **argv, int x, int y)
3255 HideCrosshair (true);
3256 if (RemoveSelected ())
3257 SetChangedFlag (true);
3258 RestoreCrosshair (true);
3259 return 0;
3262 /* --------------------------------------------------------------------------- */
3264 static const char renumber_syntax[] = "Renumber()\n" "Renumber(filename)";
3266 static const char renumber_help[] =
3267 "Renumber all elements. The changes will be recorded to filename\n"
3268 "for use in backannotating these changes to the schematic.";
3270 /* %start-doc actions Renumber
3272 %end-doc */
3274 static int
3275 ActionRenumber (int argc, char **argv, int x, int y)
3277 bool changed = false;
3278 ElementTypePtr *element_list;
3279 ElementTypePtr *locked_element_list;
3280 unsigned int i, j, k, cnt, lock_cnt;
3281 unsigned int tmpi;
3282 size_t sz;
3283 char *tmps;
3284 char *name;
3285 FILE *out;
3286 static char * default_file = NULL;
3287 size_t cnt_list_sz = 100;
3288 struct _cnt_list
3290 char *name;
3291 unsigned int cnt;
3292 } *cnt_list;
3293 char **was, **is, *pin;
3294 unsigned int c_cnt = 0;
3295 int unique, ok;
3296 int free_name = 0;
3298 if (argc < 1)
3301 * We deal with the case where name already exists in this
3302 * function so the GUI doesn't need to deal with it
3304 name = gui->fileselect (_("Save Renumber Annotation File As ..."),
3305 _("Choose a file to record the renumbering to.\n"
3306 "This file may be used to back annotate the\n"
3307 "change to the schematics.\n"),
3308 default_file, ".eco", "eco",
3311 free_name = 1;
3313 else
3314 name = argv[0];
3316 if (default_file)
3318 free (default_file);
3319 default_file = NULL;
3322 if (name && *name)
3324 default_file = strdup (name);
3327 if ((out = fopen (name, "r")))
3329 fclose (out);
3330 if (!gui->confirm_dialog (_("File exists! Ok to overwrite?"), 0))
3332 if (free_name && name)
3333 free (name);
3334 return 0;
3338 if ((out = fopen (name, "w")) == NULL)
3340 Message (_("Could not open %s\n"), name);
3341 if (free_name && name)
3342 free (name);
3343 return 1;
3346 if (free_name && name)
3347 free (name);
3349 fprintf (out, "*COMMENT* PCB Annotation File\n");
3350 fprintf (out, "*FILEVERSION* 20061031\n");
3353 * Make a first pass through all of the elements and sort them out
3354 * by location on the board. While here we also collect a list of
3355 * locked elements.
3357 * We'll actually renumber things in the 2nd pass.
3359 element_list = calloc (PCB->Data->ElementN, sizeof (ElementTypePtr));
3360 locked_element_list = calloc (PCB->Data->ElementN, sizeof (ElementTypePtr));
3361 was = calloc (PCB->Data->ElementN, sizeof (char *));
3362 is = calloc (PCB->Data->ElementN, sizeof (char *));
3363 if (element_list == NULL || locked_element_list == NULL || was == NULL
3364 || is == NULL)
3366 fprintf (stderr, "calloc() failed in %s\n", __FUNCTION__);
3367 exit (1);
3371 cnt = 0;
3372 lock_cnt = 0;
3373 ELEMENT_LOOP (PCB->Data);
3375 if (TEST_FLAG (LOCKFLAG, element->Name) || TEST_FLAG (LOCKFLAG, element))
3378 * add to the list of locked elements which we won't try to
3379 * renumber and whose reference designators are now reserved.
3381 fprintf (out,
3382 "*WARN* Element \"%s\" at (%d,%d) is locked and will not be renumbered.\n",
3383 UNKNOWN (NAMEONPCB_NAME (element)), element->MarkX,
3384 element->MarkY);
3385 locked_element_list[lock_cnt] = element;
3386 lock_cnt++;
3389 else
3391 /* count of devices which will be renumbered */
3392 cnt++;
3394 /* search for correct position in the list */
3395 i = 0;
3396 while (element_list[i] && element->MarkY > element_list[i]->MarkY)
3397 i++;
3400 * We have found the position where we have the first element that
3401 * has the same Y value or a lower Y value. Now move forward if
3402 * needed through the X values
3404 while (element_list[i]
3405 && element->MarkY == element_list[i]->MarkY
3406 && element->MarkX > element_list[i]->MarkX)
3407 i++;
3409 for (j = cnt - 1; j > i; j--)
3411 element_list[j] = element_list[j - 1];
3413 element_list[i] = element;
3416 END_LOOP;
3420 * Now that the elements are sorted by board position, we go through
3421 * and renumber them.
3425 * turn off the flag which requires unique names so it doesn't get
3426 * in our way. When we're done with the renumber we will have unique
3427 * names.
3429 unique = TEST_FLAG (UNIQUENAMEFLAG, PCB);
3430 CLEAR_FLAG (UNIQUENAMEFLAG, PCB);
3432 cnt_list = calloc (cnt_list_sz, sizeof (struct _cnt_list));
3433 for (i = 0; i < cnt; i++)
3435 /* If there is no refdes, maybe just spit out a warning */
3436 if (NAMEONPCB_NAME (element_list[i]))
3438 /* figure out the prefix */
3439 tmps = strdup (NAMEONPCB_NAME (element_list[i]));
3440 j = 0;
3441 while (tmps[j] && (tmps[j] < '0' || tmps[j] > '9')
3442 && tmps[j] != '?')
3443 j++;
3444 tmps[j] = '\0';
3446 /* check the counter for this prefix */
3447 for (j = 0;
3448 cnt_list[j].name && (strcmp (cnt_list[j].name, tmps) != 0)
3449 && j < cnt_list_sz; j++);
3451 /* grow the list if needed */
3452 if (j == cnt_list_sz)
3454 cnt_list_sz += 100;
3455 cnt_list = realloc (cnt_list, cnt_list_sz);
3456 if (cnt_list == NULL)
3458 fprintf (stderr, "realloc failed() in %s\n", __FUNCTION__);
3459 exit (1);
3461 /* zero out the memory that we added */
3462 for (tmpi = j; tmpi < cnt_list_sz; tmpi++)
3464 cnt_list[tmpi].name = NULL;
3465 cnt_list[tmpi].cnt = 0;
3470 * start a new counter if we don't have a counter for this
3471 * prefix
3473 if (!cnt_list[j].name)
3475 cnt_list[j].name = strdup (tmps);
3476 cnt_list[j].cnt = 0;
3480 * check to see if the new refdes is already used by a
3481 * locked element
3485 ok = 1;
3486 cnt_list[j].cnt++;
3487 free (tmps);
3489 /* space for the prefix plus 1 digit plus the '\0' */
3490 sz = strlen (cnt_list[j].name) + 2;
3492 /* and 1 more per extra digit needed to hold the number */
3493 tmpi = cnt_list[j].cnt;
3494 while (tmpi > 10)
3496 sz++;
3497 tmpi = tmpi / 10;
3499 tmps = malloc (sz * sizeof (char));
3500 sprintf (tmps, "%s%d", cnt_list[j].name, cnt_list[j].cnt);
3503 * now compare to the list of reserved (by locked
3504 * elements) names
3506 for (k = 0; k < lock_cnt; k++)
3508 if (strcmp
3509 (UNKNOWN (NAMEONPCB_NAME (locked_element_list[k])),
3510 tmps) == 0)
3512 ok = 0;
3513 break;
3518 while (!ok);
3520 if (strcmp (tmps, NAMEONPCB_NAME (element_list[i])) != 0)
3522 fprintf (out, "*RENAME* \"%s\" \"%s\"\n",
3523 NAMEONPCB_NAME (element_list[i]), tmps);
3525 /* add this rename to our table of renames so we can update the netlist */
3526 was[c_cnt] = strdup (NAMEONPCB_NAME (element_list[i]));
3527 is[c_cnt] = strdup (tmps);
3528 c_cnt++;
3530 AddObjectToChangeNameUndoList (ELEMENT_TYPE, NULL, NULL,
3531 element_list[i],
3532 NAMEONPCB_NAME (element_list
3533 [i]));
3535 ChangeObjectName (ELEMENT_TYPE, element_list[i], NULL, NULL,
3536 tmps);
3537 changed = true;
3539 /* we don't free tmps in this case because it is used */
3541 else
3542 free (tmps);
3544 else
3546 fprintf (out, "*WARN* Element at (%d,%d) has no name.\n",
3547 element_list[i]->MarkX, element_list[i]->MarkY);
3552 fclose (out);
3554 /* restore the unique flag setting */
3555 if (unique)
3556 SET_FLAG (UNIQUENAMEFLAG, PCB);
3558 if (changed)
3561 /* update the netlist */
3562 AddNetlistLibToUndoList (&(PCB->NetlistLib));
3564 /* iterate over each net */
3565 for (i = 0; i < PCB->NetlistLib.MenuN; i++)
3568 /* iterate over each pin on the net */
3569 for (j = 0; j < PCB->NetlistLib.Menu[i].EntryN; j++)
3572 /* figure out the pin number part from strings like U3-21 */
3573 tmps = strdup (PCB->NetlistLib.Menu[i].Entry[j].ListEntry);
3574 for (k = 0; tmps[k] && tmps[k] != '-'; k++);
3575 tmps[k] = '\0';
3576 pin = tmps + k + 1;
3578 /* iterate over the list of changed reference designators */
3579 for (k = 0; k < c_cnt; k++)
3582 * if the pin needs to change, change it and quit
3583 * searching in the list.
3585 if (strcmp (tmps, was[k]) == 0)
3587 free (PCB->NetlistLib.Menu[i].Entry[j].ListEntry);
3588 PCB->NetlistLib.Menu[i].Entry[j].ListEntry =
3589 malloc ((strlen (is[k]) + strlen (pin) +
3590 2) * sizeof (char));
3591 sprintf (PCB->NetlistLib.Menu[i].Entry[j].ListEntry,
3592 "%s-%s", is[k], pin);
3593 k = c_cnt;
3597 free (tmps);
3600 for (k = 0; k < c_cnt; k++)
3602 free (was[k]);
3603 free (is[k]);
3606 NetlistChanged (0);
3607 IncrementUndoSerialNumber ();
3608 SetChangedFlag (true);
3611 free (locked_element_list);
3612 free (element_list);
3613 free (cnt_list);
3614 return 0;
3618 /* --------------------------------------------------------------------------- */
3620 static const char ripup_syntax[] = "RipUp(All|Selected|Element)";
3622 static const char ripup_help[] =
3623 "Ripup auto-routed tracks, or convert an element to parts.";
3625 /* %start-doc actions RipUp
3627 @table @code
3629 @item All
3630 Removes all lines and vias which were created by the autorouter.
3632 @item Selected
3633 Removes all selected lines and vias which were created by the
3634 autorouter.
3636 @item Element
3637 Converts the element under the cursor to parts (vias and lines). Note
3638 that this uses the highest numbered paste buffer.
3640 @end table
3642 %end-doc */
3644 static int
3645 ActionRipUp (int argc, char **argv, int x, int y)
3647 char *function = ARG (0);
3648 bool changed = false;
3650 if (function)
3652 HideCrosshair (true);
3653 switch (GetFunctionID (function))
3655 case F_All:
3656 ALLLINE_LOOP (PCB->Data);
3658 if (TEST_FLAG (AUTOFLAG, line) && !TEST_FLAG (LOCKFLAG, line))
3660 RemoveObject (LINE_TYPE, layer, line, line);
3661 changed = true;
3664 ENDALL_LOOP;
3665 VIA_LOOP (PCB->Data);
3667 if (TEST_FLAG (AUTOFLAG, via) && !TEST_FLAG (LOCKFLAG, via))
3669 RemoveObject (VIA_TYPE, via, via, via);
3670 changed = true;
3673 END_LOOP;
3675 if (changed)
3677 IncrementUndoSerialNumber ();
3678 SetChangedFlag (true);
3680 break;
3681 case F_Selected:
3682 VISIBLELINE_LOOP (PCB->Data);
3684 if (TEST_FLAGS (AUTOFLAG | SELECTEDFLAG, line)
3685 && !TEST_FLAG (LOCKFLAG, line))
3687 RemoveObject (LINE_TYPE, layer, line, line);
3688 changed = true;
3691 ENDALL_LOOP;
3692 if (PCB->ViaOn)
3693 VIA_LOOP (PCB->Data);
3695 if (TEST_FLAGS (AUTOFLAG | SELECTEDFLAG, via)
3696 && !TEST_FLAG (LOCKFLAG, via))
3698 RemoveObject (VIA_TYPE, via, via, via);
3699 changed = true;
3702 END_LOOP;
3703 if (changed)
3705 IncrementUndoSerialNumber ();
3706 SetChangedFlag (true);
3708 break;
3709 case F_Element:
3711 void *ptr1, *ptr2, *ptr3;
3713 if (SearchScreen (Crosshair.X, Crosshair.Y, ELEMENT_TYPE,
3714 &ptr1, &ptr2, &ptr3) != NO_TYPE)
3716 Note.Buffer = Settings.BufferNumber;
3717 SetBufferNumber (MAX_BUFFER - 1);
3718 ClearBuffer (PASTEBUFFER);
3719 CopyObjectToBuffer (PASTEBUFFER->Data, PCB->Data,
3720 ELEMENT_TYPE, ptr1, ptr2, ptr3);
3721 SmashBufferElement (PASTEBUFFER);
3722 PASTEBUFFER->X = 0;
3723 PASTEBUFFER->Y = 0;
3724 SaveUndoSerialNumber ();
3725 EraseObject (ELEMENT_TYPE, ptr1, ptr1);
3726 MoveObjectToRemoveUndoList (ELEMENT_TYPE, ptr1, ptr2, ptr3);
3727 RestoreUndoSerialNumber ();
3728 CopyPastebufferToLayout (0, 0);
3729 SetBufferNumber (Note.Buffer);
3730 SetChangedFlag (true);
3733 break;
3735 RestoreCrosshair (true);
3737 return 0;
3740 /* --------------------------------------------------------------------------- */
3742 static const char addrats_syntax[] = "AddRats(AllRats|SelectedRats|Close)";
3744 static const char addrats_help[] = "Add one or more rat lines to the board.";
3746 /* %start-doc actions AddRats
3748 @table @code
3750 @item AllRats
3751 Create rat lines for all loaded nets that aren't already connected on
3752 with copper.
3754 @item SelectedRats
3755 Similarly, but only add rat lines for nets connected to selected pins
3756 and pads.
3758 @item Close
3759 Selects the shortest unselected rat on the board.
3761 @end table
3763 %end-doc */
3765 static int
3766 ActionAddRats (int argc, char **argv, int x, int y)
3768 char *function = ARG (0);
3769 RatTypePtr shorty;
3770 float len, small;
3772 if (function)
3774 if (Settings.RatWarn)
3775 ClearWarnings ();
3776 HideCrosshair (true);
3777 switch (GetFunctionID (function))
3779 case F_AllRats:
3780 if (AddAllRats (false, NULL))
3781 SetChangedFlag (true);
3782 break;
3783 case F_SelectedRats:
3784 case F_Selected:
3785 if (AddAllRats (true, NULL))
3786 SetChangedFlag (true);
3787 break;
3788 case F_Close:
3789 small = SQUARE (MAX_COORD);
3790 shorty = NULL;
3791 RAT_LOOP (PCB->Data);
3793 if (TEST_FLAG (SELECTEDFLAG, line))
3794 continue;
3795 len = SQUARE (line->Point1.X - line->Point2.X) +
3796 SQUARE (line->Point1.Y - line->Point2.Y);
3797 if (len < small)
3799 small = len;
3800 shorty = line;
3803 END_LOOP;
3804 if (shorty)
3806 AddObjectToFlagUndoList (RATLINE_TYPE, shorty, shorty, shorty);
3807 SET_FLAG (SELECTEDFLAG, shorty);
3808 DrawRat (shorty, 0);
3809 Draw ();
3810 CenterDisplay ((shorty->Point2.X + shorty->Point1.X) / 2,
3811 (shorty->Point2.Y + shorty->Point1.Y) / 2,
3812 false);
3814 break;
3816 RestoreCrosshair (true);
3818 return 0;
3821 /* --------------------------------------------------------------------------- */
3823 static const char delete_syntax[] =
3824 "Delete(Object|Selected)\n"
3825 "Delete(AllRats|SelectedRats)"
3828 static const char delete_help[] = "Delete stuff.";
3830 /* %start-doc actions Delete
3832 %end-doc */
3834 static int
3835 ActionDelete (int argc, char **argv, int x, int y)
3837 char *function = ARG (0);
3838 int id = GetFunctionID (function);
3840 Note.X = Crosshair.X;
3841 Note.Y = Crosshair.Y;
3843 if (id == -1) /* no arg */
3845 if (RemoveSelected() == false)
3846 id = F_Object;
3849 HideCrosshair (true);
3850 switch (id)
3852 case F_Object:
3853 SaveMode();
3854 SetMode(REMOVE_MODE);
3855 NotifyMode();
3856 RestoreMode();
3857 break;
3858 case F_Selected:
3859 RemoveSelected();
3860 break;
3861 case F_AllRats:
3862 if (DeleteRats (false))
3863 SetChangedFlag (true);
3864 break;
3865 case F_SelectedRats:
3866 if (DeleteRats (true))
3867 SetChangedFlag (true);
3868 break;
3871 RestoreCrosshair (true);
3872 return 0;
3875 /* --------------------------------------------------------------------------- */
3877 static const char deleterats_syntax[] =
3878 "DeleteRats(AllRats|Selected|SelectedRats)";
3880 static const char deleterats_help[] = "Delete rat lines.";
3882 /* %start-doc actions DeleteRats
3884 %end-doc */
3886 static int
3887 ActionDeleteRats (int argc, char **argv, int x, int y)
3889 char *function = ARG (0);
3890 if (function)
3892 if (Settings.RatWarn)
3893 ClearWarnings ();
3894 HideCrosshair (true);
3895 switch (GetFunctionID (function))
3897 case F_AllRats:
3898 if (DeleteRats (false))
3899 SetChangedFlag (true);
3900 break;
3901 case F_SelectedRats:
3902 case F_Selected:
3903 if (DeleteRats (true))
3904 SetChangedFlag (true);
3905 break;
3907 RestoreCrosshair (true);
3909 return 0;
3912 /* --------------------------------------------------------------------------- */
3914 static const char autoplace_syntax[] = "AutoPlaceSelected()";
3916 static const char autoplace_help[] = "Auto-place selected components.";
3918 /* %start-doc actions AutoPlaceSelected
3920 Attempts to re-arrange the selected components such that the nets
3921 connecting them are minimized. Note that you cannot undo this.
3923 %end-doc */
3925 static int
3926 ActionAutoPlaceSelected (int argc, char **argv, int x, int y)
3928 hid_action("Busy");
3929 if (gui->confirm_dialog (_("Auto-placement can NOT be undone.\n"
3930 "Do you want to continue anyway?\n"), 0))
3932 HideCrosshair (true);
3933 if (AutoPlaceSelected ())
3934 SetChangedFlag (true);
3935 RestoreCrosshair (true);
3937 return 0;
3940 /* --------------------------------------------------------------------------- */
3942 static const char autoroute_syntax[] = "AutoRoute(AllRats|SelectedRats)";
3944 static const char autoroute_help[] = "Auto-route some or all rat lines.";
3946 /* %start-doc actions AutoRoute
3948 @table @code
3950 @item AllRats
3951 Attempt to autoroute all rats.
3953 @item SelectedRats
3954 Attempt to autoroute the selected rats.
3956 @end table
3958 Before autorouting, it's important to set up a few things. First,
3959 make sure any layers you aren't using are disabled, else the
3960 autorouter may use them. Next, make sure the current line and via
3961 styles are set accordingly. Last, make sure "new lines clear
3962 polygons" is set, in case you eventually want to add a copper pour.
3964 Autorouting takes a while. During this time, the program may not be
3965 responsive.
3967 %end-doc */
3969 static int
3970 ActionAutoRoute (int argc, char **argv, int x, int y)
3972 char *function = ARG (0);
3973 hid_action("Busy");
3974 if (function) /* one parameter */
3976 HideCrosshair (true);
3977 switch (GetFunctionID (function))
3979 case F_AllRats:
3980 if (AutoRoute (false))
3981 SetChangedFlag (true);
3982 break;
3983 case F_SelectedRats:
3984 case F_Selected:
3985 if (AutoRoute (true))
3986 SetChangedFlag (true);
3987 break;
3989 RestoreCrosshair (true);
3991 return 0;
3994 /* --------------------------------------------------------------------------- */
3996 static const char markcrosshair_syntax[] =
3997 "MarkCrosshair()\n" "MarkCrosshair(Center)";
3999 static const char markcrosshair_help[] = "Set/Reset the Crosshair mark";
4001 /* %start-doc actions MarkCrosshair
4003 The ``mark'' is a small X-shaped target on the display which is
4004 treated like a second origin (the normal origin is the upper let
4005 corner of the board). The GUI will display a second set of
4006 coordinates for this mark, which tells you how far you are from it.
4008 If no argument is given, the mark is toggled - disabled if it was
4009 enabled, or enabled at the current cursor position of disabled. If
4010 the @code{Center} argument is given, the mark is moved to the current
4011 cursor location.
4013 %end-doc */
4015 static int
4016 ActionMarkCrosshair (int argc, char **argv, int x, int y)
4018 char *function = ARG (0);
4019 if (!function || !*function)
4021 if (Marked.status)
4023 DrawMark (true);
4024 Marked.status = false;
4026 else
4028 Marked.status = true;
4029 Marked.X = Crosshair.X;
4030 Marked.Y = Crosshair.Y;
4031 DrawMark (false);
4034 else if (GetFunctionID (function) == F_Center)
4036 DrawMark (true);
4037 Marked.status = true;
4038 Marked.X = Crosshair.X;
4039 Marked.Y = Crosshair.Y;
4040 DrawMark (false);
4042 return 0;
4045 /* --------------------------------------------------------------------------- */
4047 static const char changesize_syntax[] =
4048 "ChangeSize(Object, delta)\n"
4049 "ChangeSize(SelectedObjects|Selected, delta)\n"
4050 "ChangeSize(SelectedLines|SelectedPins|SelectedVias, delta)\n"
4051 "ChangeSize(SelectedPads|SelectedTexts|SelectedNames, delta)\n"
4052 "ChangeSize(SelectedElements, delta)";
4054 static const char changesize_help[] = "Changes the size of objects.";
4056 /* %start-doc actions ChangeSize
4058 For lines and arcs, this changes the width. For pins and vias, this
4059 changes the overall diameter of the copper annulus. For pads, this
4060 changes the width and, indirectly, the length. For texts and names,
4061 this changes the scaling factor. For elements, this changes the width
4062 of the silk layer lines and arcs for this element.
4064 %end-doc */
4066 static int
4067 ActionChangeSize (int argc, char **argv, int x, int y)
4069 char *function = ARG (0);
4070 char *delta = ARG (1);
4071 char *units = ARG (2);
4072 bool absolute; /* indicates if absolute size is given */
4073 float value;
4075 if (function && delta)
4077 value = GetValue (delta, units, &absolute);
4078 HideCrosshair (true);
4079 switch (GetFunctionID (function))
4081 case F_Object:
4083 int type;
4084 void *ptr1, *ptr2, *ptr3;
4086 if ((type =
4087 SearchScreen (Crosshair.X, Crosshair.Y, CHANGESIZE_TYPES,
4088 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4089 if (TEST_FLAG (LOCKFLAG, (PinTypePtr) ptr2))
4090 Message (_("Sorry, the object is locked\n"));
4091 if (ChangeObjectSize (type, ptr1, ptr2, ptr3, value, absolute))
4092 SetChangedFlag (true);
4093 break;
4096 case F_SelectedVias:
4097 if (ChangeSelectedSize (VIA_TYPE, value, absolute))
4098 SetChangedFlag (true);
4099 break;
4101 case F_SelectedPins:
4102 if (ChangeSelectedSize (PIN_TYPE, value, absolute))
4103 SetChangedFlag (true);
4104 break;
4106 case F_SelectedPads:
4107 if (ChangeSelectedSize (PAD_TYPE, value, absolute))
4108 SetChangedFlag (true);
4109 break;
4111 case F_SelectedArcs:
4112 if (ChangeSelectedSize (ARC_TYPE, value, absolute))
4113 SetChangedFlag (true);
4114 break;
4116 case F_SelectedLines:
4117 if (ChangeSelectedSize (LINE_TYPE, value, absolute))
4118 SetChangedFlag (true);
4119 break;
4121 case F_SelectedTexts:
4122 if (ChangeSelectedSize (TEXT_TYPE, value, absolute))
4123 SetChangedFlag (true);
4124 break;
4126 case F_SelectedNames:
4127 if (ChangeSelectedSize (ELEMENTNAME_TYPE, value, absolute))
4128 SetChangedFlag (true);
4129 break;
4131 case F_SelectedElements:
4132 if (ChangeSelectedSize (ELEMENT_TYPE, value, absolute))
4133 SetChangedFlag (true);
4134 break;
4136 case F_Selected:
4137 case F_SelectedObjects:
4138 if (ChangeSelectedSize (CHANGESIZE_TYPES, value, absolute))
4139 SetChangedFlag (true);
4140 break;
4142 RestoreCrosshair (true);
4144 return 0;
4147 /* --------------------------------------------------------------------------- */
4149 static const char changedrillsize_syntax[] =
4150 "ChangeDrillSize(Object, delta)\n"
4151 "ChangeDrillSize(SelectedPins|SelectedVias|Selected|SelectedObjects, delta)";
4153 static const char changedrillsize_help[] =
4154 "Changes the drilling hole size of objects.";
4156 /* %start-doc actions ChangeDrillSize
4158 %end-doc */
4160 static int
4161 ActionChange2ndSize (int argc, char **argv, int x, int y)
4163 char *function = ARG (0);
4164 char *delta = ARG (1);
4165 char *units = ARG (2);
4166 bool absolute;
4167 float value;
4169 if (function && delta)
4171 value = GetValue (delta, units, &absolute);
4172 HideCrosshair (true);
4173 switch (GetFunctionID (function))
4175 case F_Object:
4177 int type;
4178 void *ptr1, *ptr2, *ptr3;
4180 gui->get_coords (_("Select an Object"), &x, &y);
4181 if ((type =
4182 SearchScreen (x, y, CHANGE2NDSIZE_TYPES,
4183 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4184 if (ChangeObject2ndSize
4185 (type, ptr1, ptr2, ptr3, value, absolute, true))
4186 SetChangedFlag (true);
4187 break;
4190 case F_SelectedVias:
4191 if (ChangeSelected2ndSize (VIA_TYPE, value, absolute))
4192 SetChangedFlag (true);
4193 break;
4195 case F_SelectedPins:
4196 if (ChangeSelected2ndSize (PIN_TYPE, value, absolute))
4197 SetChangedFlag (true);
4198 break;
4199 case F_Selected:
4200 case F_SelectedObjects:
4201 if (ChangeSelected2ndSize (PIN_TYPES, value, absolute))
4202 SetChangedFlag (true);
4203 break;
4205 RestoreCrosshair (true);
4207 return 0;
4210 /* --------------------------------------------------------------------------- */
4212 static const char changeclearsize_syntax[] =
4213 "ChangeClearSize(Object, delta)\n"
4214 "ChangeClearSize(SelectedPins|SelectedPads|SelectedVias, delta)\n"
4215 "ChangeClearSize(SelectedLines|SelectedArcs, delta\n"
4216 "ChangeClearSize(Selected|SelectedObjects, delta)";
4218 static const char changeclearsize_help[] =
4219 "Changes the clearance size of objects.";
4221 /* %start-doc actions ChangeClearSize
4223 If the solder mask is currently showing, this action changes the
4224 solder mask clearance. If the mask is not showing, this action
4225 changes the polygon clearance.
4227 %end-doc */
4229 static int
4230 ActionChangeClearSize (int argc, char **argv, int x, int y)
4232 char *function = ARG (0);
4233 char *delta = ARG (1);
4234 char *units = ARG (2);
4235 bool absolute;
4236 float value;
4238 if (function && delta)
4240 value = 2 * GetValue (delta, units, &absolute);
4241 HideCrosshair (true);
4242 switch (GetFunctionID (function))
4244 case F_Object:
4246 int type;
4247 void *ptr1, *ptr2, *ptr3;
4249 gui->get_coords (_("Select an Object"), &x, &y);
4250 if ((type =
4251 SearchScreen (x, y,
4252 CHANGECLEARSIZE_TYPES, &ptr1, &ptr2,
4253 &ptr3)) != NO_TYPE)
4254 if (ChangeObjectClearSize (type, ptr1, ptr2, ptr3, value, absolute))
4255 SetChangedFlag (true);
4256 break;
4258 case F_SelectedVias:
4259 if (ChangeSelectedClearSize (VIA_TYPE, value, absolute))
4260 SetChangedFlag (true);
4261 break;
4262 case F_SelectedPads:
4263 if (ChangeSelectedClearSize (PAD_TYPE, value, absolute))
4264 SetChangedFlag (true);
4265 break;
4266 case F_SelectedPins:
4267 if (ChangeSelectedClearSize (PIN_TYPE, value, absolute))
4268 SetChangedFlag (true);
4269 break;
4270 case F_SelectedLines:
4271 if (ChangeSelectedClearSize (LINE_TYPE, value, absolute))
4272 SetChangedFlag (true);
4273 break;
4274 case F_SelectedArcs:
4275 if (ChangeSelectedClearSize (ARC_TYPE, value, absolute))
4276 SetChangedFlag (true);
4277 break;
4278 case F_Selected:
4279 case F_SelectedObjects:
4280 if (ChangeSelectedClearSize (CHANGECLEARSIZE_TYPES, value, absolute))
4281 SetChangedFlag (true);
4282 break;
4284 RestoreCrosshair (true);
4286 return 0;
4289 /* --------------------------------------------------------------------------- */
4291 static const char minmaskgap_syntax[] =
4292 "MinMaskGap(delta)\n" "MinMaskGap(Selected, delta)";
4294 static const char minmaskgap_help[] =
4295 "Ensures the mask is a minimum distance from pins and pads.";
4297 /* %start-doc actions MinMaskGap
4299 Checks all specified pins and/or pads, and increases the mask if
4300 needed to ensure a minimum distance between the pin or pad edge and
4301 the mask edge.
4303 %end-doc */
4305 static int
4306 ActionMinMaskGap (int argc, char **argv, int x, int y)
4308 char *function = ARG (0);
4309 char *delta = ARG (1);
4310 char *units = ARG (2);
4311 bool absolute;
4312 int value;
4313 int flags;
4315 if (!function)
4316 return 1;
4317 if (strcasecmp (function, "Selected") == 0)
4318 flags = SELECTEDFLAG;
4319 else
4321 units = delta;
4322 delta = function;
4323 flags = 0;
4325 value = 2 * GetValue (delta, units, &absolute);
4327 HideCrosshair (true);
4328 SaveUndoSerialNumber ();
4329 ELEMENT_LOOP (PCB->Data);
4331 PIN_LOOP (element);
4333 if (!TEST_FLAGS (flags, pin))
4334 continue;
4335 if (pin->Mask < pin->Thickness + value)
4337 ChangeObjectMaskSize (PIN_TYPE, element, pin, 0,
4338 pin->Thickness + value, 1);
4339 RestoreUndoSerialNumber ();
4342 END_LOOP;
4343 PAD_LOOP (element);
4345 if (!TEST_FLAGS (flags, pad))
4346 continue;
4347 if (pad->Mask < pad->Thickness + value)
4349 ChangeObjectMaskSize (PAD_TYPE, element, pad, 0,
4350 pad->Thickness + value, 1);
4351 RestoreUndoSerialNumber ();
4354 END_LOOP;
4356 END_LOOP;
4357 VIA_LOOP (PCB->Data);
4359 if (!TEST_FLAGS (flags, via))
4360 continue;
4361 if (via->Mask && via->Mask < via->Thickness + value)
4363 ChangeObjectMaskSize (VIA_TYPE, via, 0, 0, via->Thickness + value, 1);
4364 RestoreUndoSerialNumber ();
4367 END_LOOP;
4368 RestoreUndoSerialNumber ();
4369 IncrementUndoSerialNumber ();
4370 return 0;
4373 /* --------------------------------------------------------------------------- */
4375 static const char mincleargap_syntax[] =
4376 "MinClearGap(delta)\n" "MinClearGap(Selected, delta)";
4378 static const char mincleargap_help[] =
4379 "Ensures that polygons are a minimum distance from objects.";
4381 /* %start-doc actions MinClearGap
4383 Checks all specified objects, and increases the polygon clearance if
4384 needed to ensure a minimum distance between their edges and the
4385 polygon edges.
4387 %end-doc */
4389 static int
4390 ActionMinClearGap (int argc, char **argv, int x, int y)
4392 char *function = ARG (0);
4393 char *delta = ARG (1);
4394 char *units = ARG (2);
4395 bool absolute;
4396 int value;
4397 int flags;
4399 if (!function)
4400 return 1;
4401 if (strcasecmp (function, "Selected") == 0)
4402 flags = SELECTEDFLAG;
4403 else
4405 units = delta;
4406 delta = function;
4407 flags = 0;
4409 value = 2 * GetValue (delta, units, &absolute);
4411 HideCrosshair (true);
4412 SaveUndoSerialNumber ();
4413 ELEMENT_LOOP (PCB->Data);
4415 PIN_LOOP (element);
4417 if (!TEST_FLAGS (flags, pin))
4418 continue;
4419 if (pin->Clearance < value)
4421 ChangeObjectClearSize (PIN_TYPE, element, pin, 0,
4422 value, 1);
4423 RestoreUndoSerialNumber ();
4426 END_LOOP;
4427 PAD_LOOP (element);
4429 if (!TEST_FLAGS (flags, pad))
4430 continue;
4431 if (pad->Clearance < value)
4433 ChangeObjectClearSize (PAD_TYPE, element, pad, 0,
4434 value, 1);
4435 RestoreUndoSerialNumber ();
4438 END_LOOP;
4440 END_LOOP;
4441 VIA_LOOP (PCB->Data);
4443 if (!TEST_FLAGS (flags, via))
4444 continue;
4445 if (via->Clearance < value)
4447 ChangeObjectClearSize (VIA_TYPE, via, 0, 0, value, 1);
4448 RestoreUndoSerialNumber ();
4451 END_LOOP;
4452 ALLLINE_LOOP (PCB->Data);
4454 if (!TEST_FLAGS (flags, line))
4455 continue;
4456 if (line->Clearance < value)
4458 ChangeObjectClearSize (LINE_TYPE, layer, line, 0, value, 1);
4459 RestoreUndoSerialNumber ();
4462 ENDALL_LOOP;
4463 ALLARC_LOOP (PCB->Data);
4465 if (!TEST_FLAGS (flags, arc))
4466 continue;
4467 if (arc->Clearance < value)
4469 ChangeObjectClearSize (ARC_TYPE, layer, arc, 0, value, 1);
4470 RestoreUndoSerialNumber ();
4473 ENDALL_LOOP;
4474 RestoreUndoSerialNumber ();
4475 IncrementUndoSerialNumber ();
4476 return 0;
4479 /* --------------------------------------------------------------------------- */
4481 static const char changepinname_syntax[] =
4482 "ChangePinName(ElementName,PinNumber,PinName)";
4484 static const char changepinname_help[] =
4485 "Sets the name of a specific pin on a specific element.";
4487 /* %start-doc actions ChangePinName
4489 This can be especially useful for annotating pin names from a
4490 schematic to the layout without requiring knowledge of the pcb file
4491 format.
4493 @example
4494 ChangePinName(U3, 7, VCC)
4495 @end example
4497 %end-doc */
4499 static int
4500 ActionChangePinName (int argc, char **argv, int x, int y)
4502 int changed = 0;
4503 char *refdes, *pinnum, *pinname;
4505 if (argc != 3)
4507 AFAIL (changepinname);
4510 refdes = argv[0];
4511 pinnum = argv[1];
4512 pinname = argv[2];
4514 ELEMENT_LOOP (PCB->Data);
4516 if (NSTRCMP (refdes, NAMEONPCB_NAME (element)) == 0)
4518 PIN_LOOP (element);
4520 if (NSTRCMP (pinnum, pin->Number) == 0)
4522 AddObjectToChangeNameUndoList (PIN_TYPE, NULL, NULL,
4523 pin, pin->Name);
4525 * Note: we can't free() pin->Name first because
4526 * it is used in the undo list
4528 pin->Name = strdup (pinname);
4529 SetChangedFlag (true);
4530 changed = 1;
4533 END_LOOP;
4535 PAD_LOOP (element);
4537 if (NSTRCMP (pinnum, pad->Number) == 0)
4539 AddObjectToChangeNameUndoList (PAD_TYPE, NULL, NULL,
4540 pad, pad->Name);
4542 * Note: we can't free() pad->Name first because
4543 * it is used in the undo list
4545 pad->Name = strdup (pinname);
4546 SetChangedFlag (true);
4547 changed = 1;
4550 END_LOOP;
4553 END_LOOP;
4555 * done with our action so increment the undo # if we actually
4556 * changed anything
4558 if (changed)
4560 if (defer_updates)
4561 defer_needs_update = 1;
4562 else
4564 IncrementUndoSerialNumber ();
4565 gui->invalidate_all ();
4569 return 0;
4572 /* --------------------------------------------------------------------------- */
4574 static const char changename_syntax[] =
4575 "ChangeName(Object)\n" "ChangeName(Layout|Layer)";
4577 static const char changename_help[] = "Sets the name of objects.";
4579 /* %start-doc actions ChangeName
4581 @table @code
4583 @item Object
4584 Changes the name of the element under the cursor.
4586 @item Layout
4587 Changes the name of the layout. This is printed on the fab drawings.
4589 @item Layer
4590 Changes the name of the currently active layer.
4592 @end table
4594 %end-doc */
4597 ActionChangeName (int argc, char **argv, int x, int y)
4599 char *function = ARG (0);
4600 char *name;
4602 if (function)
4604 HideCrosshair (true);
4605 switch (GetFunctionID (function))
4607 /* change the name of an object */
4608 case F_Object:
4610 int type;
4611 void *ptr1, *ptr2, *ptr3;
4613 gui->get_coords (_("Select an Object"), &x, &y);
4614 if ((type =
4615 SearchScreen (x, y, CHANGENAME_TYPES,
4616 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4618 SaveUndoSerialNumber ();
4619 if (QueryInputAndChangeObjectName (type, ptr1, ptr2, ptr3))
4621 SetChangedFlag (true);
4622 if (type == ELEMENT_TYPE)
4624 RubberbandTypePtr ptr;
4625 int i;
4627 RestoreUndoSerialNumber ();
4628 Crosshair.AttachedObject.RubberbandN = 0;
4629 LookupRatLines (type, ptr1, ptr2, ptr3);
4630 ptr = Crosshair.AttachedObject.Rubberband;
4631 for (i = 0; i < Crosshair.AttachedObject.RubberbandN;
4632 i++, ptr++)
4634 if (PCB->RatOn)
4635 EraseRat ((RatTypePtr) ptr->Line);
4636 MoveObjectToRemoveUndoList (RATLINE_TYPE,
4637 ptr->Line, ptr->Line,
4638 ptr->Line);
4640 IncrementUndoSerialNumber ();
4641 Draw ();
4645 break;
4648 /* change the layout's name */
4649 case F_Layout:
4650 name =
4651 gui->prompt_for (_("Enter the layout name:"), EMPTY (PCB->Name));
4652 /* NB: ChangeLayoutName takes ownership of the passed memory */
4653 if (name && ChangeLayoutName (name))
4654 SetChangedFlag (true);
4655 break;
4657 /* change the name of the active layer */
4658 case F_Layer:
4659 name = gui->prompt_for (_("Enter the layer name:"),
4660 EMPTY (CURRENT->Name));
4661 /* NB: ChangeLayerName takes ownership of the passed memory */
4662 if (name && ChangeLayerName (CURRENT, name))
4663 SetChangedFlag (true);
4664 break;
4666 RestoreCrosshair (true);
4668 return 0;
4672 /* --------------------------------------------------------------------------- */
4674 static const char morphpolygon_syntax[] = "MorphPolygon(Object|Selected)";
4676 static const char morphpolygon_help[] =
4677 "Converts dead polygon islands into separate polygons.";
4679 /* %start-doc actions MorphPolygon
4681 If a polygon is divided into unconnected "islands", you can use
4682 this command to convert the otherwise disappeared islands into
4683 separate polygons. Be sure the cursor is over a portion of the
4684 polygon that remains visible. Very small islands that may flake
4685 off are automatically deleted.
4687 %end-doc */
4689 static int
4690 ActionMorphPolygon (int argc, char **argv, int x, int y)
4692 char *function = ARG (0);
4693 if (function)
4695 HideCrosshair (true);
4696 switch (GetFunctionID (function))
4698 case F_Object:
4700 int type;
4701 void *ptr1, *ptr2, *ptr3;
4703 gui->get_coords (_("Select an Object"), &x, &y);
4704 if ((type = SearchScreen (x, y, POLYGON_TYPE,
4705 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4707 MorphPolygon ((LayerType *) ptr1, (PolygonType *) ptr3);
4708 Draw ();
4709 IncrementUndoSerialNumber ();
4711 break;
4713 case F_Selected:
4714 case F_SelectedObjects:
4715 ALLPOLYGON_LOOP (PCB->Data);
4717 if (TEST_FLAG (SELECTEDFLAG, polygon))
4718 MorphPolygon (layer, polygon);
4720 ENDALL_LOOP;
4721 Draw ();
4722 IncrementUndoSerialNumber ();
4723 break;
4726 return 0;
4729 /* --------------------------------------------------------------------------- */
4731 static const char togglehidename_syntax[] =
4732 "ToggleHideName(Object|SelectedElements)";
4734 static const char togglehidename_help[] =
4735 "Toggles the visibility of element names.";
4737 /* %start-doc actions ToggleHideName
4739 If names are hidden you won't see them on the screen and they will not
4740 appear on the silk layer when you print the layout.
4742 %end-doc */
4744 static int
4745 ActionToggleHideName (int argc, char **argv, int x, int y)
4747 char *function = ARG (0);
4748 if (function && PCB->ElementOn)
4750 HideCrosshair (true);
4751 switch (GetFunctionID (function))
4753 case F_Object:
4755 int type;
4756 void *ptr1, *ptr2, *ptr3;
4758 gui->get_coords (_("Select an Object"), &x, &y);
4759 if ((type = SearchScreen (x, y, ELEMENT_TYPE,
4760 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4762 AddObjectToFlagUndoList (type, ptr1, ptr2, ptr3);
4763 EraseElementName ((ElementTypePtr) ptr2);
4764 TOGGLE_FLAG (HIDENAMEFLAG, (ElementTypePtr) ptr2);
4765 DrawElementName ((ElementTypePtr) ptr2, 0);
4766 Draw ();
4767 IncrementUndoSerialNumber ();
4769 break;
4771 case F_SelectedElements:
4772 case F_Selected:
4774 bool changed = false;
4775 ELEMENT_LOOP (PCB->Data);
4777 if ((TEST_FLAG (SELECTEDFLAG, element) ||
4778 TEST_FLAG (SELECTEDFLAG,
4779 &NAMEONPCB_TEXT (element)))
4780 && (FRONT (element) || PCB->InvisibleObjectsOn))
4782 AddObjectToFlagUndoList (ELEMENT_TYPE, element,
4783 element, element);
4784 EraseElementName (element);
4785 TOGGLE_FLAG (HIDENAMEFLAG, element);
4786 DrawElementName (element, 0);
4787 changed = true;
4790 END_LOOP;
4791 if (changed)
4793 Draw ();
4794 IncrementUndoSerialNumber ();
4798 RestoreCrosshair (true);
4800 return 0;
4803 /* --------------------------------------------------------------------------- */
4805 static const char changejoin_syntax[] =
4806 "ChangeJoin(ToggleObject|SelectedLines|SelectedArcs|Selected)";
4808 static const char changejoin_help[] =
4809 "Changes the join (clearance through polygons) of objects.";
4811 /* %start-doc actions ChangeJoin
4813 The join flag determines whether a line or arc, drawn to intersect a
4814 polygon, electrically connects to the polygon or not. When joined,
4815 the line/arc is simply drawn over the polygon, making an electrical
4816 connection. When not joined, a gap is drawn between the line and the
4817 polygon, insulating them from each other.
4819 %end-doc */
4821 static int
4822 ActionChangeJoin (int argc, char **argv, int x, int y)
4824 char *function = ARG (0);
4825 if (function)
4827 HideCrosshair (true);
4828 switch (GetFunctionID (function))
4830 case F_ToggleObject:
4831 case F_Object:
4833 int type;
4834 void *ptr1, *ptr2, *ptr3;
4836 gui->get_coords (_("Select an Object"), &x, &y);
4837 if ((type =
4838 SearchScreen (x, y, CHANGEJOIN_TYPES,
4839 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4840 if (ChangeObjectJoin (type, ptr1, ptr2, ptr3))
4841 SetChangedFlag (true);
4842 break;
4845 case F_SelectedLines:
4846 if (ChangeSelectedJoin (LINE_TYPE))
4847 SetChangedFlag (true);
4848 break;
4850 case F_SelectedArcs:
4851 if (ChangeSelectedJoin (ARC_TYPE))
4852 SetChangedFlag (true);
4853 break;
4855 case F_Selected:
4856 case F_SelectedObjects:
4857 if (ChangeSelectedJoin (CHANGEJOIN_TYPES))
4858 SetChangedFlag (true);
4859 break;
4861 RestoreCrosshair (true);
4863 return 0;
4866 /* --------------------------------------------------------------------------- */
4868 static const char changesquare_syntax[] =
4869 "ChangeSquare(ToggleObject)\n"
4870 "ChangeSquare(SelectedElements|SelectedPins)\n"
4871 "ChangeSquare(Selected|SelectedObjects)";
4873 static const char changesquare_help[] =
4874 "Changes the square flag of pins and pads.";
4876 /* %start-doc actions ChangeSquare
4878 Note that @code{Pins} means both pins and pads.
4880 @pinshapes
4882 %end-doc */
4884 static int
4885 ActionChangeSquare (int argc, char **argv, int x, int y)
4887 char *function = ARG (0);
4888 if (function)
4890 HideCrosshair (true);
4891 switch (GetFunctionID (function))
4893 case F_ToggleObject:
4894 case F_Object:
4896 int type;
4897 void *ptr1, *ptr2, *ptr3;
4899 gui->get_coords (_("Select an Object"), &x, &y);
4900 if ((type =
4901 SearchScreen (x, y, CHANGESQUARE_TYPES,
4902 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4903 if (ChangeObjectSquare (type, ptr1, ptr2, ptr3))
4904 SetChangedFlag (true);
4905 break;
4908 case F_SelectedElements:
4909 if (ChangeSelectedSquare (ELEMENT_TYPE))
4910 SetChangedFlag (true);
4911 break;
4913 case F_SelectedPins:
4914 if (ChangeSelectedSquare (PIN_TYPE | PAD_TYPE))
4915 SetChangedFlag (true);
4916 break;
4918 case F_Selected:
4919 case F_SelectedObjects:
4920 if (ChangeSelectedSquare (PIN_TYPE | PAD_TYPE))
4921 SetChangedFlag (true);
4922 break;
4924 RestoreCrosshair (true);
4926 return 0;
4929 /* --------------------------------------------------------------------------- */
4931 static const char setsquare_syntax[] =
4932 "SetSquare(ToggleObject|SelectedElements|SelectedPins)";
4934 static const char setsquare_help[] = "sets the square-flag of objects.";
4936 /* %start-doc actions SetSquare
4938 Note that @code{Pins} means pins and pads.
4940 @pinshapes
4942 %end-doc */
4944 static int
4945 ActionSetSquare (int argc, char **argv, int x, int y)
4947 char *function = ARG (0);
4948 if (function && *function)
4950 /* HideCrosshair (true); */
4951 switch (GetFunctionID (function))
4953 case F_ToggleObject:
4954 case F_Object:
4956 int type;
4957 void *ptr1, *ptr2, *ptr3;
4959 gui->get_coords (_("Select an Object"), &x, &y);
4960 if ((type =
4961 SearchScreen (x, y, CHANGESQUARE_TYPES,
4962 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4963 if (SetObjectSquare (type, ptr1, ptr2, ptr3))
4964 SetChangedFlag (true);
4965 break;
4968 case F_SelectedElements:
4969 if (SetSelectedSquare (ELEMENT_TYPE))
4970 SetChangedFlag (true);
4971 break;
4973 case F_SelectedPins:
4974 if (SetSelectedSquare (PIN_TYPE | PAD_TYPE))
4975 SetChangedFlag (true);
4976 break;
4978 case F_Selected:
4979 case F_SelectedObjects:
4980 if (SetSelectedSquare (PIN_TYPE | PAD_TYPE))
4981 SetChangedFlag (true);
4982 break;
4984 /* RestoreCrosshair (true); */
4986 return 0;
4989 /* --------------------------------------------------------------------------- */
4991 static const char clearsquare_syntax[] =
4992 "ClearSquare(ToggleObject|SelectedElements|SelectedPins)";
4994 static const char clearsquare_help[] =
4995 "Clears the square-flag of pins and pads.";
4997 /* %start-doc actions ClearSquare
4999 Note that @code{Pins} means pins and pads.
5001 @pinshapes
5003 %end-doc */
5005 static int
5006 ActionClearSquare (int argc, char **argv, int x, int y)
5008 char *function = ARG (0);
5009 if (function && *function)
5011 /* HideCrosshair (true); */
5012 switch (GetFunctionID (function))
5014 case F_ToggleObject:
5015 case F_Object:
5017 int type;
5018 void *ptr1, *ptr2, *ptr3;
5020 gui->get_coords (_("Select an Object"), &x, &y);
5021 if ((type =
5022 SearchScreen (x, y, CHANGESQUARE_TYPES,
5023 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5024 if (ClrObjectSquare (type, ptr1, ptr2, ptr3))
5025 SetChangedFlag (true);
5026 break;
5029 case F_SelectedElements:
5030 if (ClrSelectedSquare (ELEMENT_TYPE))
5031 SetChangedFlag (true);
5032 break;
5034 case F_SelectedPins:
5035 if (ClrSelectedSquare (PIN_TYPE | PAD_TYPE))
5036 SetChangedFlag (true);
5037 break;
5039 case F_Selected:
5040 case F_SelectedObjects:
5041 if (ClrSelectedSquare (PIN_TYPE | PAD_TYPE))
5042 SetChangedFlag (true);
5043 break;
5045 /* RestoreCrosshair (true); */
5047 return 0;
5050 /* --------------------------------------------------------------------------- */
5052 static const char changeoctagon_syntax[] =
5053 "ChangeOctagon(Object|ToggleObject|SelectedObjects|Selected)\n"
5054 "ChangeOctagon(SelectedElements|SelectedPins|SelectedVias)";
5056 static const char changeoctagon_help[] =
5057 "Changes the octagon-flag of pins and vias.";
5059 /* %start-doc actions ChangeOctagon
5061 @pinshapes
5063 %end-doc */
5065 static int
5066 ActionChangeOctagon (int argc, char **argv, int x, int y)
5068 char *function = ARG (0);
5069 if (function)
5071 /* HideCrosshair (true); */
5072 switch (GetFunctionID (function))
5074 case F_ToggleObject:
5075 case F_Object:
5077 int type;
5078 void *ptr1, *ptr2, *ptr3;
5080 gui->get_coords (_("Select an Object"), &x, &y);
5081 if ((type =
5082 SearchScreen (x, y, CHANGEOCTAGON_TYPES,
5083 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5084 if (ChangeObjectOctagon (type, ptr1, ptr2, ptr3))
5085 SetChangedFlag (true);
5086 break;
5089 case F_SelectedElements:
5090 if (ChangeSelectedOctagon (ELEMENT_TYPE))
5091 SetChangedFlag (true);
5092 break;
5094 case F_SelectedPins:
5095 if (ChangeSelectedOctagon (PIN_TYPE))
5096 SetChangedFlag (true);
5097 break;
5099 case F_SelectedVias:
5100 if (ChangeSelectedOctagon (VIA_TYPE))
5101 SetChangedFlag (true);
5102 break;
5104 case F_Selected:
5105 case F_SelectedObjects:
5106 if (ChangeSelectedOctagon (PIN_TYPES))
5107 SetChangedFlag (true);
5108 break;
5110 /* RestoreCrosshair (true); */
5112 return 0;
5115 /* --------------------------------------------------------------------------- */
5117 static const char setoctagon_syntax[] =
5118 "SetOctagon(Object|ToggleObject|SelectedElements|Selected)";
5120 static const char setoctagon_help[] = "Sets the octagon-flag of objects.";
5122 /* %start-doc actions SetOctagon
5124 @pinshapes
5126 %end-doc */
5128 static int
5129 ActionSetOctagon (int argc, char **argv, int x, int y)
5131 char *function = ARG (0);
5132 if (function)
5134 /* HideCrosshair (true); */
5135 switch (GetFunctionID (function))
5137 case F_ToggleObject:
5138 case F_Object:
5140 int type;
5141 void *ptr1, *ptr2, *ptr3;
5143 gui->get_coords (_("Select an Object"), &x, &y);
5144 if ((type =
5145 SearchScreen (x, y, CHANGEOCTAGON_TYPES,
5146 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5147 if (SetObjectOctagon (type, ptr1, ptr2, ptr3))
5148 SetChangedFlag (true);
5149 break;
5152 case F_SelectedElements:
5153 if (SetSelectedOctagon (ELEMENT_TYPE))
5154 SetChangedFlag (true);
5155 break;
5157 case F_SelectedPins:
5158 if (SetSelectedOctagon (PIN_TYPE))
5159 SetChangedFlag (true);
5160 break;
5162 case F_SelectedVias:
5163 if (SetSelectedOctagon (VIA_TYPE))
5164 SetChangedFlag (true);
5165 break;
5167 case F_Selected:
5168 case F_SelectedObjects:
5169 if (SetSelectedOctagon (PIN_TYPES))
5170 SetChangedFlag (true);
5171 break;
5173 /* RestoreCrosshair (true); */
5175 return 0;
5178 /* --------------------------------------------------------------------------- */
5180 static const char clearoctagon_syntax[] =
5181 "ClearOctagon(ToggleObject|Object|SelectedObjects|Selected)\n"
5182 "ClearOctagon(SelectedElements|SelectedPins|SelectedVias)";
5184 static const char clearoctagon_help[] =
5185 "Clears the octagon-flag of pins and vias.";
5187 /* %start-doc actions ClearOctagon
5189 @pinshapes
5191 %end-doc */
5193 static int
5194 ActionClearOctagon (int argc, char **argv, int x, int y)
5196 char *function = ARG (0);
5197 if (function)
5199 /* HideCrosshair (true); */
5200 switch (GetFunctionID (function))
5202 case F_ToggleObject:
5203 case F_Object:
5205 int type;
5206 void *ptr1, *ptr2, *ptr3;
5208 gui->get_coords (_("Select an Object"), &x, &y);
5209 if ((type =
5210 SearchScreen (Crosshair.X, Crosshair.Y, CHANGEOCTAGON_TYPES,
5211 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5212 if (ClrObjectOctagon (type, ptr1, ptr2, ptr3))
5213 SetChangedFlag (true);
5214 break;
5217 case F_SelectedElements:
5218 if (ClrSelectedOctagon (ELEMENT_TYPE))
5219 SetChangedFlag (true);
5220 break;
5222 case F_SelectedPins:
5223 if (ClrSelectedOctagon (PIN_TYPE))
5224 SetChangedFlag (true);
5225 break;
5227 case F_SelectedVias:
5228 if (ClrSelectedOctagon (VIA_TYPE))
5229 SetChangedFlag (true);
5230 break;
5232 case F_Selected:
5233 case F_SelectedObjects:
5234 if (ClrSelectedOctagon (PIN_TYPES))
5235 SetChangedFlag (true);
5236 break;
5238 /* RestoreCrosshair (true); */
5240 return 0;
5243 /* --------------------------------------------------------------------------- */
5245 static const char changehold_syntax[] =
5246 "ChangeHole(ToggleObject|Object|SelectedVias|Selected)";
5248 static const char changehold_help[] = "Changes the hole flag of objects.";
5250 /* %start-doc actions ChangeHole
5252 The "hole flag" of a via determines whether the via is a
5253 plated-through hole (not set), or an unplated hole (set).
5255 %end-doc */
5257 static int
5258 ActionChangeHole (int argc, char **argv, int x, int y)
5260 char *function = ARG (0);
5261 if (function)
5263 /* HideCrosshair (true); */
5264 switch (GetFunctionID (function))
5266 case F_ToggleObject:
5267 case F_Object:
5269 int type;
5270 void *ptr1, *ptr2, *ptr3;
5272 gui->get_coords (_("Select an Object"), &x, &y);
5273 if ((type = SearchScreen (x, y, VIA_TYPE,
5274 &ptr1, &ptr2, &ptr3)) != NO_TYPE
5275 && ChangeHole ((PinTypePtr) ptr3))
5276 IncrementUndoSerialNumber ();
5277 break;
5280 case F_SelectedVias:
5281 case F_Selected:
5282 if (ChangeSelectedHole ())
5283 SetChangedFlag (true);
5284 break;
5286 /* RestoreCrosshair (true); */
5288 return 0;
5291 /* --------------------------------------------------------------------------- */
5293 static const char changepaste_syntax[] =
5294 "ChangePaste(ToggleObject|Object|SelectedPads|Selected)";
5296 static const char changepaste_help[] = "Changes the no paste flag of objects.";
5298 /* %start-doc actions ChangePaste
5300 The "no paste flag" of a pad determines whether the solderpaste
5301 stencil will have an opening for the pad (no set) or if there wil be
5302 no solderpaste on the pad (set). This is used for things such as
5303 fiducial pads.
5305 %end-doc */
5307 static int
5308 ActionChangePaste (int argc, char **argv, int x, int y)
5310 char *function = ARG (0);
5311 if (function)
5313 /* HideCrosshair (true); */
5314 switch (GetFunctionID (function))
5316 case F_ToggleObject:
5317 case F_Object:
5319 int type;
5320 void *ptr1, *ptr2, *ptr3;
5322 gui->get_coords (_("Select an Object"), &x, &y);
5323 if ((type = SearchScreen (x, y, PAD_TYPE,
5324 &ptr1, &ptr2, &ptr3)) != NO_TYPE
5325 && ChangePaste ((PadTypePtr) ptr3))
5326 IncrementUndoSerialNumber ();
5327 break;
5330 case F_SelectedPads:
5331 case F_Selected:
5332 if (ChangeSelectedPaste ())
5333 SetChangedFlag (true);
5334 break;
5336 /* RestoreCrosshair (true); */
5338 return 0;
5341 /* --------------------------------------------------------------------------- */
5343 static const char select_syntax[] =
5344 "Select(ToggleObject)\n"
5345 "Select(All|Block|Connection)\n"
5346 "Select(ElementByName|ObjectByName|PadByName|PinByName)\n"
5347 "Select(ElementByName|ObjectByName|PadByName|PinByName, Name)\n"
5348 "Select(TextByName|ViaByName)\n"
5349 "Select(TextByName|ViaByName, Name)\n" "Select(Convert)";
5351 static const char select_help[] = "Toggles or sets the selection";
5353 /* %start-doc actions Select
5355 @table @code
5357 @item ElementByName
5358 @item ObjectByName
5359 @item PadByName
5360 @item PinByName
5361 @item TextByName
5362 @item ViaByName
5364 These all rely on having a regular expression parser built into
5365 @code{pcb}. If the name is not specified then the user is prompted
5366 for a pattern, and all objects that match the pattern and are of the
5367 type specified are selected.
5369 @item Object
5370 @item ToggleObject
5371 Selects the object under the cursor.
5373 @item Block
5374 Selects all objects in a rectangle indicated by the cursor.
5376 @item All
5377 Selects all objects on the board.
5379 @item Connection
5380 Selects all connections with the ``found'' flag set.
5382 @item Convert
5383 Converts the selected objects to an element. This uses the highest
5384 numbered paste buffer.
5386 @end table
5388 %end-doc */
5390 static int
5391 ActionSelect (int argc, char **argv, int x, int y)
5393 char *function = ARG (0);
5394 if (function)
5397 HideCrosshair (true);
5398 switch (GetFunctionID (function))
5400 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5401 int type;
5402 /* select objects by their names */
5403 case F_ElementByName:
5404 type = ELEMENT_TYPE;
5405 goto commonByName;
5406 case F_ObjectByName:
5407 type = ALL_TYPES;
5408 goto commonByName;
5409 case F_PadByName:
5410 type = PAD_TYPE;
5411 goto commonByName;
5412 case F_PinByName:
5413 type = PIN_TYPE;
5414 goto commonByName;
5415 case F_TextByName:
5416 type = TEXT_TYPE;
5417 goto commonByName;
5418 case F_ViaByName:
5419 type = VIA_TYPE;
5420 goto commonByName;
5422 commonByName:
5424 char *pattern = ARG (1);
5426 if (pattern
5427 || (pattern =
5428 gui->prompt_for (_("Enter pattern:"), "")) != NULL)
5430 if (SelectObjectByName (type, pattern, true))
5431 SetChangedFlag (true);
5432 if (ARG (1) == NULL)
5433 free (pattern);
5435 break;
5437 #endif /* defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP) */
5439 /* select a single object */
5440 case F_ToggleObject:
5441 case F_Object:
5442 if (SelectObject ())
5443 SetChangedFlag (true);
5444 break;
5446 /* all objects in block */
5447 case F_Block:
5449 BoxType box;
5451 box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
5452 Crosshair.AttachedBox.Point2.X);
5453 box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
5454 Crosshair.AttachedBox.Point2.Y);
5455 box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
5456 Crosshair.AttachedBox.Point2.X);
5457 box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
5458 Crosshair.AttachedBox.Point2.Y);
5459 NotifyBlock ();
5460 if (Crosshair.AttachedBox.State == STATE_THIRD &&
5461 SelectBlock (&box, true))
5463 SetChangedFlag (true);
5464 Crosshair.AttachedBox.State = STATE_FIRST;
5466 break;
5469 /* select all visible objects */
5470 case F_All:
5472 BoxType box;
5474 box.X1 = -MAX_COORD;
5475 box.Y1 = -MAX_COORD;
5476 box.X2 = MAX_COORD;
5477 box.Y2 = MAX_COORD;
5478 if (SelectBlock (&box, true))
5479 SetChangedFlag (true);
5480 break;
5483 /* all found connections */
5484 case F_Connection:
5485 if (SelectConnection (true))
5487 Draw ();
5488 IncrementUndoSerialNumber ();
5489 SetChangedFlag (true);
5491 break;
5493 case F_Convert:
5495 int x, y;
5496 Note.Buffer = Settings.BufferNumber;
5497 SetBufferNumber (MAX_BUFFER - 1);
5498 ClearBuffer (PASTEBUFFER);
5499 gui->get_coords (_("Select the Element's Mark Location"), &x, &y);
5500 x = GRIDFIT_X (x, PCB->Grid);
5501 y = GRIDFIT_Y (y, PCB->Grid);
5502 AddSelectedToBuffer (PASTEBUFFER, x, y, true);
5503 SaveUndoSerialNumber ();
5504 RemoveSelected ();
5505 ConvertBufferToElement (PASTEBUFFER);
5506 RestoreUndoSerialNumber ();
5507 CopyPastebufferToLayout (x, y);
5508 SetBufferNumber (Note.Buffer);
5510 break;
5512 default:
5513 RestoreCrosshair (true);
5514 AFAIL (select);
5515 break;
5517 RestoreCrosshair (true);
5519 return 0;
5522 /* FLAG(have_regex,FlagHaveRegex,0) */
5524 FlagHaveRegex (int parm)
5526 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5527 return 1;
5528 #else
5529 return 0;
5530 #endif
5533 /* --------------------------------------------------------------------------- */
5535 static const char unselect_syntax[] =
5536 "Unselect(All|Block|Connection)\n"
5537 "Unselect(ElementByName|ObjectByName|PadByName|PinByName)\n"
5538 "Unselect(ElementByName|ObjectByName|PadByName|PinByName, Name)\n"
5539 "Unselect(TextByName|ViaByName)\n" "Unselect(TextByName|ViaByName, Name)\n";
5541 static const char unselect_help[] =
5542 "unselects the object at the pointer location or the specified objects";
5544 /* %start-doc actions Unselect
5546 @table @code
5548 @item All
5549 Unselect all objects.
5551 @item Block
5552 Unselect all objects in a rectangle given by the cursor.
5554 @item Connection
5555 Unselect all connections with the ``found'' flag set.
5557 @item ElementByName
5558 @item ObjectByName
5559 @item PadByName
5560 @item PinByName
5561 @item TextByName
5562 @item ViaByName
5564 These all rely on having a regular expression parser built into
5565 @code{pcb}. If the name is not specified then the user is prompted
5566 for a pattern, and all objects that match the pattern and are of the
5567 type specified are unselected.
5570 @end table
5572 %end-doc */
5574 static int
5575 ActionUnselect (int argc, char **argv, int x, int y)
5577 char *function = ARG (0);
5579 if (function)
5581 HideCrosshair (true);
5582 switch (GetFunctionID (function))
5584 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5585 int type;
5586 /* select objects by their names */
5587 case F_ElementByName:
5588 type = ELEMENT_TYPE;
5589 goto commonByName;
5590 case F_ObjectByName:
5591 type = ALL_TYPES;
5592 goto commonByName;
5593 case F_PadByName:
5594 type = PAD_TYPE;
5595 goto commonByName;
5596 case F_PinByName:
5597 type = PIN_TYPE;
5598 goto commonByName;
5599 case F_TextByName:
5600 type = TEXT_TYPE;
5601 goto commonByName;
5602 case F_ViaByName:
5603 type = VIA_TYPE;
5604 goto commonByName;
5606 commonByName:
5608 char *pattern = ARG (1);
5610 if (pattern
5611 || (pattern =
5612 gui->prompt_for (_("Enter pattern:"), "")) != NULL)
5614 if (SelectObjectByName (type, pattern, false))
5615 SetChangedFlag (true);
5616 if (ARG (1) == NULL)
5617 free (pattern);
5619 break;
5621 #endif /* defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP) */
5623 /* all objects in block */
5624 case F_Block:
5626 BoxType box;
5628 box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
5629 Crosshair.AttachedBox.Point2.X);
5630 box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
5631 Crosshair.AttachedBox.Point2.Y);
5632 box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
5633 Crosshair.AttachedBox.Point2.X);
5634 box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
5635 Crosshair.AttachedBox.Point2.Y);
5636 NotifyBlock ();
5637 if (Crosshair.AttachedBox.State == STATE_THIRD &&
5638 SelectBlock (&box, false))
5640 SetChangedFlag (true);
5641 Crosshair.AttachedBox.State = STATE_FIRST;
5643 break;
5646 /* unselect all visible objects */
5647 case F_All:
5649 BoxType box;
5651 box.X1 = -MAX_COORD;
5652 box.Y1 = -MAX_COORD;
5653 box.X2 = MAX_COORD;
5654 box.Y2 = MAX_COORD;
5655 if (SelectBlock (&box, false))
5656 SetChangedFlag (true);
5657 break;
5660 /* all found connections */
5661 case F_Connection:
5662 if (SelectConnection (false))
5664 Draw ();
5665 IncrementUndoSerialNumber ();
5666 SetChangedFlag (true);
5668 break;
5670 default:
5671 RestoreCrosshair (true);
5672 AFAIL (unselect);
5673 break;
5676 RestoreCrosshair (true);
5678 return 0;
5681 /* --------------------------------------------------------------------------- */
5683 static const char saveto_syntax[] =
5684 "SaveTo(Layout|LayoutAs,filename)\n"
5685 "SaveTo(AllConnections|AllUnusedPins|ElementConnections,filename)\n"
5686 "SaveTo(PasteBuffer,filename)";
5688 static const char saveto_help[] = "Saves data to a file.";
5690 /* %start-doc actions SaveTo
5692 @table @code
5694 @item Layout
5695 Saves the current layout.
5697 @item LayoutAs
5698 Saves the current layout, and remembers the filename used.
5700 @item AllConnections
5701 Save all connections to a file.
5703 @item AllUnusedPins
5704 List all unused pins to a file.
5706 @item ElementConnections
5707 Save connections to the element at the cursor to a file.
5709 @item PasteBuffer
5710 Save the content of the active Buffer to a file. This is the graphical way to create a footprint.
5712 @end table
5714 %end-doc */
5716 static int
5717 ActionSaveTo (int argc, char **argv, int x, int y)
5719 char *function;
5720 char *name;
5722 function = argv[0];
5723 name = argv[1];
5725 if (strcasecmp (function, "Layout") == 0)
5727 SavePCB (PCB->Filename);
5728 return 0;
5731 if (argc != 2)
5732 AFAIL (saveto);
5734 if (strcasecmp (function, "LayoutAs") == 0)
5736 free (PCB->Filename);
5737 PCB->Filename = strdup (name);
5738 SavePCB (PCB->Filename);
5739 return 0;
5742 if (strcasecmp (function, "AllConnections") == 0)
5744 FILE *fp;
5745 bool result;
5746 if ((fp = CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
5748 LookupConnectionsToAllElements (fp);
5749 fclose (fp);
5750 SetChangedFlag (true);
5752 return 0;
5755 if (strcasecmp (function, "AllUnusedPins") == 0)
5757 FILE *fp;
5758 bool result;
5759 if ((fp = CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
5761 LookupUnusedPins (fp);
5762 fclose (fp);
5763 SetChangedFlag (true);
5765 return 0;
5768 if (strcasecmp (function, "ElementConnections") == 0)
5770 ElementTypePtr element;
5771 void *ptrtmp;
5772 FILE *fp;
5773 bool result;
5775 if ((SearchScreen (Crosshair.X, Crosshair.Y, ELEMENT_TYPE,
5776 &ptrtmp, &ptrtmp, &ptrtmp)) != NO_TYPE)
5778 element = (ElementTypePtr) ptrtmp;
5779 if ((fp =
5780 CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
5782 LookupElementConnections (element, fp);
5783 fclose (fp);
5784 SetChangedFlag (true);
5787 return 0;
5790 if (strcasecmp (function, "PasteBuffer") == 0)
5792 return SaveBufferElements (name);
5795 AFAIL (saveto);
5798 /* --------------------------------------------------------------------------- */
5800 static const char savesettings_syntax[] =
5801 "SaveSettings()\n" "SaveSettings(local)";
5803 static const char savesettings_help[] = "Saves settings.";
5805 /* %start-doc actions SaveSettings
5807 If you pass no arguments, the settings are stored in
5808 @code{$HOME/.pcb/settings}. If you pass the word @code{local} they're
5809 saved in @code{./pcb.settings}.
5811 %end-doc */
5813 static int
5814 ActionSaveSettings (int argc, char **argv, int x, int y)
5816 int locally = argc > 0 ? (strncasecmp (argv[0], "local", 5) == 0) : 0;
5817 hid_save_settings (locally);
5818 return 0;
5821 /* --------------------------------------------------------------------------- */
5823 static const char loadfrom_syntax[] =
5824 "LoadFrom(Layout|LayoutToBuffer|ElementToBuffer|Netlist|Revert,filename)";
5826 static const char loadfrom_help[] = "Load layout data from a file.";
5828 /* %start-doc actions LoadFrom
5830 This action assumes you know what the filename is. The various GUIs
5831 should have a similar @code{Load} action where the filename is
5832 optional, and will provide their own file selection mechanism to let
5833 you choose the file name.
5835 @table @code
5837 @item Layout
5838 Loads an entire PCB layout, replacing the current one.
5840 @item LayoutToBuffer
5841 Loads an entire PCB layout to the paste buffer.
5843 @item ElementToBuffer
5844 Loads the given element file into the paste buffer. Element files
5845 contain only a single @code{Element} definition, such as the
5846 ``newlib'' library uses.
5848 @item Netlist
5849 Loads a new netlist, replacing any current netlist.
5851 @item Revert
5852 Re-loads the current layout from its disk file, reverting any changes
5853 you may have made.
5855 @end table
5857 %end-doc */
5859 static int
5860 ActionLoadFrom (int argc, char **argv, int x, int y)
5862 char *function;
5863 char *name;
5864 char fname[256];
5866 if (argc < 2)
5867 AFAIL (loadfrom);
5869 function = argv[0];
5870 name = argv[1];
5872 HideCrosshair (true);
5874 if (strcasecmp (function, "ElementToBuffer") == 0)
5876 if (LoadElementToBuffer (PASTEBUFFER, name, true))
5877 SetMode (PASTEBUFFER_MODE);
5880 else if (strcasecmp (function, "LayoutToBuffer") == 0)
5882 if (LoadLayoutToBuffer (PASTEBUFFER, name))
5883 SetMode (PASTEBUFFER_MODE);
5886 else if (strcasecmp (function, "Layout") == 0)
5888 if (!PCB->Changed ||
5889 gui->confirm_dialog (_("OK to override layout data?"), 0))
5890 LoadPCB (name);
5893 else if (strcasecmp (function, "Netlist") == 0)
5895 if (PCB->Netlistname)
5896 free (PCB->Netlistname);
5897 PCB->Netlistname = StripWhiteSpaceAndDup (name);
5898 FreeLibraryMemory (&PCB->NetlistLib);
5899 if (!ImportNetlist (PCB->Netlistname))
5900 NetlistChanged (1);
5902 else if (strcasecmp (function, "Revert") == 0 && PCB->Filename
5903 && (!PCB->Changed
5904 || gui->confirm_dialog (_("OK to override changes?"), 0)))
5906 strcpy (fname, PCB->Filename); /*Calling LoadPCB(PCB->Filename) changes the content of PCB->Filename. */
5907 LoadPCB (fname);
5910 RestoreCrosshair (true);
5911 return 0;
5914 /* --------------------------------------------------------------------------- */
5916 static const char new_syntax[] = "New([name])";
5918 static const char new_help[] = "Starts a new layout.";
5920 /* %start-doc actions New
5922 If a name is not given, one is prompted for.
5924 %end-doc */
5926 static int
5927 ActionNew (int argc, char **argv, int x, int y)
5929 char *name = ARG (0);
5931 HideCrosshair (true);
5932 if (!PCB->Changed || gui->confirm_dialog (_("OK to clear layout data?"), 0))
5934 if (name)
5935 name = strdup (name);
5936 else
5937 name = gui->prompt_for (_("Enter the layout name:"), "");
5939 if (!name)
5941 RestoreCrosshair(true);
5942 return 1;
5945 /* do emergency saving
5946 * clear the old struct and allocate memory for the new one
5948 if (PCB->Changed && Settings.SaveInTMP)
5949 SaveInTMP ();
5950 RemovePCB (PCB);
5951 PCB = CreateNewPCB (true);
5952 PCB->Data->LayerN = DEF_LAYER;
5953 CreateNewPCBPost (PCB, 1);
5955 /* setup the new name and reset some values to default */
5956 free (PCB->Name);
5957 PCB->Name = name;
5959 ResetStackAndVisibility ();
5960 CreateDefaultFont ();
5961 SetCrosshairRange (0, 0, PCB->MaxWidth, PCB->MaxHeight);
5962 CenterDisplay (PCB->MaxWidth / 2, PCB->MaxHeight / 2, false);
5963 ClearAndRedrawOutput ();
5965 hid_action ("PCBChanged");
5966 RestoreCrosshair(true);
5967 return 0;
5969 RestoreCrosshair (true);
5970 return 1;
5973 /* ---------------------------------------------------------------------------
5974 * no operation, just for testing purposes
5975 * syntax: Bell(volume)
5977 void
5978 ActionBell (char *volume)
5980 gui->beep ();
5983 /* --------------------------------------------------------------------------- */
5985 static const char pastebuffer_syntax[] =
5986 "PasteBuffer(AddSelected|Clear|1..MAX_BUFFER)\n"
5987 "PasteBuffer(Rotate, 1..3)\n"
5988 "PasteBuffer(Convert|Save|Restore|Mirror)\n"
5989 "PasteBuffer(ToLayout, X, Y, units)";
5991 static const char pastebuffer_help[] =
5992 "Various operations on the paste buffer.";
5994 /* %start-doc actions PasteBuffer
5996 There are a number of paste buffers; the actual limit is a
5997 compile-time constant @code{MAX_BUFFER} in @file{globalconst.h}. It
5998 is currently @code{5}. One of these is the ``current'' paste buffer,
5999 often referred to as ``the'' paste buffer.
6001 @table @code
6003 @item AddSelected
6004 Copies the selected objects to the current paste buffer.
6006 @item Clear
6007 Remove all objects from the current paste buffer.
6009 @item Convert
6010 Convert the current paste buffer to an element. Vias are converted to
6011 pins, lines are converted to pads.
6013 @item Restore
6014 Convert any elements in the paste buffer back to vias and lines.
6016 @item Mirror
6017 Flip all objects in the paste buffer vertically (up/down flip). To mirror
6018 horizontally, combine this with rotations.
6020 @item Rotate
6021 Rotates the current buffer. The number to pass is 1..3, where 1 means
6022 90 degrees counter clockwise, 2 means 180 degrees, and 3 means 90
6023 degrees clockwise (270 CCW).
6025 @item Save
6026 Saves any elements in the current buffer to the indicated file.
6028 @item ToLayout
6029 Pastes any elements in the current buffer to the indicated X, Y
6030 coordinates in the layout. The @code{X} and @code{Y} are treated like
6031 @code{delta} is for many other objects. For each, if it's prefixed by
6032 @code{+} or @code{-}, then that amount is relative to the last
6033 location. Otherwise, it's absolute. Units can be
6034 @code{mil} or @code{mm}; if unspecified, units are PCB's internal
6035 units, currently 1/100 mil.
6038 @item 1..MAX_BUFFER
6039 Selects the given buffer to be the current paste buffer.
6041 @end table
6043 %end-doc */
6045 static int
6046 ActionPasteBuffer (int argc, char **argv, int x, int y)
6048 char *function = argc ? argv[0] : "";
6049 char *sbufnum = argc > 1 ? argv[1] : "";
6050 char *name;
6051 static char *default_file = NULL;
6052 int free_name = 0;
6054 HideCrosshair (true);
6055 if (function)
6057 switch (GetFunctionID (function))
6059 /* clear contents of paste buffer */
6060 case F_Clear:
6061 ClearBuffer (PASTEBUFFER);
6062 break;
6064 /* copies objects to paste buffer */
6065 case F_AddSelected:
6066 AddSelectedToBuffer (PASTEBUFFER, 0, 0, false);
6067 break;
6069 /* converts buffer contents into an element */
6070 case F_Convert:
6071 ConvertBufferToElement (PASTEBUFFER);
6072 break;
6074 /* break up element for editing */
6075 case F_Restore:
6076 SmashBufferElement (PASTEBUFFER);
6077 break;
6079 /* Mirror buffer */
6080 case F_Mirror:
6081 MirrorBuffer (PASTEBUFFER);
6082 break;
6084 case F_Rotate:
6085 if (sbufnum)
6087 RotateBuffer (PASTEBUFFER, (BYTE) atoi (sbufnum));
6088 SetCrosshairRangeToBuffer ();
6090 break;
6092 case F_Save:
6093 if (PASTEBUFFER->Data->ElementN == 0)
6095 Message (_("Buffer has no elements!\n"));
6096 break;
6098 free_name = 0;
6099 if (argc <= 1)
6101 name = gui->fileselect (_("Save Paste Buffer As ..."),
6102 _("Choose a file to save the contents of the\n"
6103 "paste buffer to.\n"),
6104 default_file, ".fp", "footprint",
6107 if (default_file)
6109 free (default_file);
6110 default_file = NULL;
6112 if ( name && *name)
6114 default_file = strdup (name);
6116 free_name = 1;
6119 else
6120 name = argv[1];
6123 FILE *exist;
6125 if ((exist = fopen (name, "r")))
6127 fclose (exist);
6128 if (gui->
6129 confirm_dialog (_("File exists! Ok to overwrite?"), 0))
6130 SaveBufferElements (name);
6132 else
6133 SaveBufferElements (name);
6135 if (free_name && name)
6136 free (name);
6138 break;
6140 case F_ToLayout:
6142 static int oldx = 0, oldy = 0;
6143 int x, y;
6144 bool absolute;
6146 if (argc == 1)
6148 x = y = 0;
6150 else if (argc == 3 || argc == 4)
6152 x = GetValue (ARG (1), ARG (3), &absolute);
6153 if (!absolute)
6154 x += oldx;
6155 y = GetValue (ARG (2), ARG (3), &absolute);
6156 if (!absolute)
6157 y += oldy;
6159 else
6161 RestoreCrosshair (true);
6162 AFAIL (pastebuffer);
6165 oldx = x;
6166 oldy = y;
6167 if (CopyPastebufferToLayout (x, y))
6168 SetChangedFlag (true);
6170 break;
6172 /* set number */
6173 default:
6175 int number = atoi (function);
6177 /* correct number */
6178 if (number)
6179 SetBufferNumber (number - 1);
6184 RestoreCrosshair (true);
6185 return 0;
6188 /* --------------------------------------------------------------------------- */
6190 static const char undo_syntax[] = "Undo()\n" "Undo(ClearList)";
6192 static const char undo_help[] = "Undo recent changes.";
6194 /* %start-doc actions Undo
6196 The unlimited undo feature of @code{Pcb} allows you to recover from
6197 most operations that materially affect you work. Calling
6198 @code{Undo()} without any parameter recovers from the last (non-undo)
6199 operation. @code{ClearList} is used to release the allocated
6200 memory. @code{ClearList} is called whenever a new layout is started or
6201 loaded. See also @code{Redo} and @code{Atomic}.
6203 Note that undo groups operations by serial number; changes with the
6204 same serial number will be undone (or redone) as a group. See
6205 @code{Atomic}.
6207 %end-doc */
6209 static int
6210 ActionUndo (int argc, char **argv, int x, int y)
6212 char *function = ARG (0);
6213 if (!function || !*function)
6215 /* don't allow undo in the middle of an operation */
6216 if (Settings.Mode != POLYGONHOLE_MODE &&
6217 Crosshair.AttachedObject.State != STATE_FIRST)
6218 return 1;
6219 if (Crosshair.AttachedBox.State != STATE_FIRST
6220 && Settings.Mode != ARC_MODE)
6221 return 1;
6222 /* undo the last operation */
6224 HideCrosshair (true);
6225 if ((Settings.Mode == POLYGON_MODE ||
6226 Settings.Mode == POLYGONHOLE_MODE) &&
6227 Crosshair.AttachedPolygon.PointN)
6229 GoToPreviousPoint ();
6230 RestoreCrosshair (true);
6231 return 0;
6233 /* move anchor point if undoing during line creation */
6234 if (Settings.Mode == LINE_MODE)
6236 if (Crosshair.AttachedLine.State == STATE_SECOND)
6238 if (TEST_FLAG (AUTODRCFLAG, PCB))
6239 Undo (true); /* undo the connection find */
6240 Crosshair.AttachedLine.State = STATE_FIRST;
6241 SetLocalRef (0, 0, false);
6242 RestoreCrosshair (true);
6243 return 0;
6245 if (Crosshair.AttachedLine.State == STATE_THIRD)
6247 int type;
6248 void *ptr1, *ptr3, *ptrtmp;
6249 LineTypePtr ptr2;
6250 /* this search is guaranteed to succeed */
6251 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6252 &ptrtmp, &ptr3,
6253 Crosshair.AttachedLine.Point1.X,
6254 Crosshair.AttachedLine.Point1.Y, 0);
6255 ptr2 = (LineTypePtr) ptrtmp;
6257 /* save both ends of line */
6258 Crosshair.AttachedLine.Point2.X = ptr2->Point1.X;
6259 Crosshair.AttachedLine.Point2.Y = ptr2->Point1.Y;
6260 if ((type = Undo (true)))
6261 SetChangedFlag (true);
6262 /* check that the undo was of the right type */
6263 if ((type & UNDO_CREATE) == 0)
6265 /* wrong undo type, restore anchor points */
6266 Crosshair.AttachedLine.Point2.X =
6267 Crosshair.AttachedLine.Point1.X;
6268 Crosshair.AttachedLine.Point2.Y =
6269 Crosshair.AttachedLine.Point1.Y;
6270 RestoreCrosshair (true);
6271 return 0;
6273 /* move to new anchor */
6274 Crosshair.AttachedLine.Point1.X =
6275 Crosshair.AttachedLine.Point2.X;
6276 Crosshair.AttachedLine.Point1.Y =
6277 Crosshair.AttachedLine.Point2.Y;
6278 /* check if an intermediate point was removed */
6279 if (type & UNDO_REMOVE)
6281 /* this search should find the restored line */
6282 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6283 &ptrtmp,
6284 &ptr3,
6285 Crosshair.AttachedLine.Point2.X,
6286 Crosshair.AttachedLine.Point2.Y, 0);
6287 ptr2 = (LineTypePtr) ptrtmp;
6288 if (TEST_FLAG (AUTODRCFLAG, PCB))
6290 /* undo loses FOUNDFLAG */
6291 SET_FLAG(FOUNDFLAG, ptr2);
6292 DrawLine (CURRENT, ptr2, 0);
6294 Crosshair.AttachedLine.Point1.X =
6295 Crosshair.AttachedLine.Point2.X = ptr2->Point2.X;
6296 Crosshair.AttachedLine.Point1.Y =
6297 Crosshair.AttachedLine.Point2.Y = ptr2->Point2.Y;
6299 FitCrosshairIntoGrid (Crosshair.X, Crosshair.Y);
6300 AdjustAttachedObjects ();
6301 if (--addedLines == 0)
6303 Crosshair.AttachedLine.State = STATE_SECOND;
6304 lastLayer = CURRENT;
6306 else
6308 /* this search is guaranteed to succeed too */
6309 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6310 &ptrtmp,
6311 &ptr3,
6312 Crosshair.AttachedLine.Point1.X,
6313 Crosshair.AttachedLine.Point1.Y, 0);
6314 ptr2 = (LineTypePtr) ptrtmp;
6315 lastLayer = (LayerTypePtr) ptr1;
6317 RestoreCrosshair (true);
6318 return 0;
6321 if (Settings.Mode == ARC_MODE)
6323 if (Crosshair.AttachedBox.State == STATE_SECOND)
6325 Crosshair.AttachedBox.State = STATE_FIRST;
6326 RestoreCrosshair (true);
6327 return 0;
6329 if (Crosshair.AttachedBox.State == STATE_THIRD)
6331 void *ptr1, *ptr2, *ptr3;
6332 BoxTypePtr bx;
6333 /* guaranteed to succeed */
6334 SearchObjectByLocation (ARC_TYPE, &ptr1, &ptr2, &ptr3,
6335 Crosshair.AttachedBox.Point1.X,
6336 Crosshair.AttachedBox.Point1.Y, 0);
6337 bx = GetArcEnds ((ArcTypePtr) ptr2);
6338 Crosshair.AttachedBox.Point1.X =
6339 Crosshair.AttachedBox.Point2.X = bx->X1;
6340 Crosshair.AttachedBox.Point1.Y =
6341 Crosshair.AttachedBox.Point2.Y = bx->Y1;
6342 AdjustAttachedObjects ();
6343 if (--addedLines == 0)
6344 Crosshair.AttachedBox.State = STATE_SECOND;
6347 /* undo the last destructive operation */
6348 if (Undo (true))
6349 SetChangedFlag (true);
6351 else if (function)
6353 switch (GetFunctionID (function))
6355 /* clear 'undo objects' list */
6356 case F_ClearList:
6357 ClearUndoList (false);
6358 break;
6361 RestoreCrosshair (true);
6362 return 0;
6365 /* --------------------------------------------------------------------------- */
6367 static const char redo_syntax[] = "Redo()";
6369 static const char redo_help[] = "Redo recent \"undo\" operations.";
6371 /* %start-doc actions Redo
6373 This routine allows you to recover from the last undo command. You
6374 might want to do this if you thought that undo was going to revert
6375 something other than what it actually did (in case you are confused
6376 about which operations are un-doable), or if you have been backing up
6377 through a long undo list and over-shoot your stopping point. Any
6378 change that is made since the undo in question will trim the redo
6379 list. For example if you add ten lines, then undo three of them you
6380 could use redo to put them back, but if you move a line on the board
6381 before performing the redo, you will lose the ability to "redo" the
6382 three "undone" lines.
6384 %end-doc */
6386 static int
6387 ActionRedo (int argc, char **argv, int x, int y)
6389 if (((Settings.Mode == POLYGON_MODE ||
6390 Settings.Mode == POLYGONHOLE_MODE) &&
6391 Crosshair.AttachedPolygon.PointN) ||
6392 Crosshair.AttachedLine.State == STATE_SECOND)
6393 return 1;
6394 HideCrosshair (true);
6395 if (Redo (true))
6397 SetChangedFlag (true);
6398 if (Settings.Mode == LINE_MODE &&
6399 Crosshair.AttachedLine.State != STATE_FIRST)
6401 LineTypePtr line = &CURRENT->Line[CURRENT->LineN - 1];
6402 Crosshair.AttachedLine.Point1.X =
6403 Crosshair.AttachedLine.Point2.X = line->Point2.X;
6404 Crosshair.AttachedLine.Point1.Y =
6405 Crosshair.AttachedLine.Point2.Y = line->Point2.Y;
6406 addedLines++;
6409 RestoreCrosshair (true);
6410 return 0;
6413 /* --------------------------------------------------------------------------- */
6415 static const char polygon_syntax[] = "Polygon(Close|PreviousPoint)";
6417 static const char polygon_help[] = "Some polygon related stuff.";
6419 /* %start-doc actions Polygon
6421 Polygons need a special action routine to make life easier.
6423 @table @code
6425 @item Close
6426 Creates the final segment of the polygon. This may fail if clipping
6427 to 45 degree lines is switched on, in which case a warning is issued.
6429 @item PreviousPoint
6430 Resets the newly entered corner to the previous one. The Undo action
6431 will call Polygon(PreviousPoint) when appropriate to do so.
6433 @end table
6435 %end-doc */
6437 static int
6438 ActionPolygon (int argc, char **argv, int x, int y)
6440 char *function = ARG (0);
6441 if (function && Settings.Mode == POLYGON_MODE)
6443 HideCrosshair (true);
6444 switch (GetFunctionID (function))
6446 /* close open polygon if possible */
6447 case F_Close:
6448 ClosePolygon ();
6449 break;
6451 /* go back to the previous point */
6452 case F_PreviousPoint:
6453 GoToPreviousPoint ();
6454 break;
6456 RestoreCrosshair (true);
6458 return 0;
6461 /* --------------------------------------------------------------------------- */
6463 static const char routestyle_syntax[] = "RouteStyle(1|2|3|4)";
6465 static const char routestyle_help[] =
6466 "Copies the indicated routing style into the current sizes.";
6468 /* %start-doc actions RouteStyle
6470 %end-doc */
6472 static int
6473 ActionRouteStyle (int argc, char **argv, int x, int y)
6475 char *str = ARG (0);
6476 RouteStyleType *rts;
6477 int number;
6479 if (str)
6481 number = atoi (str);
6482 if (number > 0 && number <= NUM_STYLES)
6484 rts = &PCB->RouteStyle[number - 1];
6485 SetLineSize (rts->Thick);
6486 SetViaSize (rts->Diameter, true);
6487 SetViaDrillingHole (rts->Hole, true);
6488 SetKeepawayWidth (rts->Keepaway);
6489 hid_action("RouteStylesChanged");
6492 return 0;
6496 /* --------------------------------------------------------------------------- */
6498 static const char moveobject_syntax[] = "MoveObject(X,Y,dim)";
6500 static const char moveobject_help[] = "Moves the object under the crosshair.";
6502 /* %start-doc actions MoveObject
6504 The @code{X} and @code{Y} are treated like @code{delta} is for many
6505 other objects. For each, if it's prefixed by @code{+} or @code{-},
6506 then that amount is relative. Otherwise, it's absolute. Units can be
6507 @code{mil} or @code{mm}; if unspecified, units are PCB's internal
6508 units, currently 1/100 mil.
6510 %end-doc */
6512 static int
6513 ActionMoveObject (int argc, char **argv, int x, int y)
6515 char *x_str = ARG (0);
6516 char *y_str = ARG (1);
6517 char *units = ARG (2);
6518 LocationType nx, ny;
6519 bool absolute1, absolute2;
6520 void *ptr1, *ptr2, *ptr3;
6521 int type;
6523 ny = GetValue (y_str, units, &absolute1);
6524 nx = GetValue (x_str, units, &absolute2);
6526 type = SearchScreen (x, y, MOVE_TYPES, &ptr1, &ptr2, &ptr3);
6527 if (type == NO_TYPE)
6529 Message (_("Nothing found under crosshair\n"));
6530 return 1;
6532 if (absolute1)
6533 nx -= x;
6534 if (absolute2)
6535 ny -= y;
6536 Crosshair.AttachedObject.RubberbandN = 0;
6537 if (TEST_FLAG (RUBBERBANDFLAG, PCB))
6538 LookupRubberbandLines (type, ptr1, ptr2, ptr3);
6539 if (type == ELEMENT_TYPE)
6540 LookupRatLines (type, ptr1, ptr2, ptr3);
6541 MoveObjectAndRubberband (type, ptr1, ptr2, ptr3, nx, ny);
6542 SetChangedFlag (true);
6543 return 0;
6546 /* --------------------------------------------------------------------------- */
6548 static const char movetocurrentlayer_syntax[] =
6549 "MoveToCurrentLayer(Object|SelectedObjects)";
6551 static const char movetocurrentlayer_help[] =
6552 "Moves objects to the current layer.";
6554 /* %start-doc actions MoveToCurrentLayer
6556 Note that moving an element from a component layer to a solder layer,
6557 or from solder to component, won't automatically flip it. Use the
6558 @code{Flip()} action to do that.
6560 %end-doc */
6562 static int
6563 ActionMoveToCurrentLayer (int argc, char **argv, int x, int y)
6565 char *function = ARG (0);
6566 if (function)
6568 HideCrosshair (true);
6569 switch (GetFunctionID (function))
6571 case F_Object:
6573 int type;
6574 void *ptr1, *ptr2, *ptr3;
6576 gui->get_coords (_("Select an Object"), &x, &y);
6577 if ((type =
6578 SearchScreen (x, y, MOVETOLAYER_TYPES,
6579 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
6580 if (MoveObjectToLayer (type, ptr1, ptr2, ptr3, CURRENT, false))
6581 SetChangedFlag (true);
6582 break;
6585 case F_SelectedObjects:
6586 case F_Selected:
6587 if (MoveSelectedObjectsToLayer (CURRENT))
6588 SetChangedFlag (true);
6589 break;
6591 RestoreCrosshair (true);
6593 return 0;
6597 static const char setsame_syntax[] = "SetSame()";
6599 static const char setsame_help[] =
6600 "Sets current layer and sizes to match indicated item.";
6602 /* %start-doc actions SetSame
6604 When invoked over any line, arc, polygon, or via, this changes the
6605 current layer to be the layer that item is on, and changes the current
6606 sizes (thickness, keepaway, drill, etc) according to that item.
6608 %end-doc */
6610 static int
6611 ActionSetSame (int argc, char **argv, int x, int y)
6613 void *ptr1, *ptr2, *ptr3;
6614 int type;
6615 LayerTypePtr layer = CURRENT;
6617 type = SearchScreen (x, y, CLONE_TYPES, &ptr1, &ptr2, &ptr3);
6618 /* set layer current and size from line or arc */
6619 switch (type)
6621 case LINE_TYPE:
6622 HideCrosshair (true);
6623 Settings.LineThickness = ((LineTypePtr) ptr2)->Thickness;
6624 Settings.Keepaway = ((LineTypePtr) ptr2)->Clearance / 2;
6625 layer = (LayerTypePtr) ptr1;
6626 if (Settings.Mode != LINE_MODE)
6627 SetMode (LINE_MODE);
6628 RestoreCrosshair (true);
6629 hid_action ("RouteStylesChanged");
6630 break;
6632 case ARC_TYPE:
6633 HideCrosshair (true);
6634 Settings.LineThickness = ((ArcTypePtr) ptr2)->Thickness;
6635 Settings.Keepaway = ((ArcTypePtr) ptr2)->Clearance / 2;
6636 layer = (LayerTypePtr) ptr1;
6637 if (Settings.Mode != ARC_MODE)
6638 SetMode (ARC_MODE);
6639 RestoreCrosshair (true);
6640 hid_action ("RouteStylesChanged");
6641 break;
6643 case POLYGON_TYPE:
6644 layer = (LayerTypePtr) ptr1;
6645 break;
6647 case VIA_TYPE:
6648 HideCrosshair (true);
6649 Settings.ViaThickness = ((PinTypePtr) ptr2)->Thickness;
6650 Settings.ViaDrillingHole = ((PinTypePtr) ptr2)->DrillingHole;
6651 Settings.Keepaway = ((PinTypePtr) ptr2)->Clearance / 2;
6652 if (Settings.Mode != VIA_MODE)
6653 SetMode (VIA_MODE);
6654 RestoreCrosshair (true);
6655 hid_action ("RouteStylesChanged");
6656 break;
6658 default:
6659 return 1;
6661 if (layer != CURRENT)
6663 ChangeGroupVisibility (GetLayerNumber (PCB->Data, layer), true, true);
6664 ClearAndRedrawOutput ();
6666 return 0;
6670 /* --------------------------------------------------------------------------- */
6672 static const char setflag_syntax[] =
6673 "SetFlag(Object|Selected|SelectedObjects, flag)\n"
6674 "SetFlag(SelectedLines|SelectedPins|SelectedVias, flag)\n"
6675 "SetFlag(SelectedPads|SelectedTexts|SelectedNames, flag)\n"
6676 "SetFlag(SelectedElements, flag)\n"
6677 "flag = square | octagon | thermal | join";
6679 static const char setflag_help[] = "Sets flags on objects.";
6681 /* %start-doc actions SetFlag
6683 Turns the given flag on, regardless of its previous setting. See
6684 @code{ChangeFlag}.
6686 @example
6687 SetFlag(SelectedPins,thermal)
6688 @end example
6690 %end-doc */
6692 static int
6693 ActionSetFlag (int argc, char **argv, int x, int y)
6695 char *function = ARG (0);
6696 char *flag = ARG (1);
6697 ChangeFlag (function, flag, 1, "SetFlag");
6698 return 0;
6701 /* --------------------------------------------------------------------------- */
6703 static const char clrflag_syntax[] =
6704 "ClrFlag(Object|Selected|SelectedObjects, flag)\n"
6705 "ClrFlag(SelectedLines|SelectedPins|SelectedVias, flag)\n"
6706 "ClrFlag(SelectedPads|SelectedTexts|SelectedNames, flag)\n"
6707 "ClrFlag(SelectedElements, flag)\n"
6708 "flag = square | octagon | thermal | join";
6710 static const char clrflag_help[] = "Clears flags on objects.";
6712 /* %start-doc actions ClrFlag
6714 Turns the given flag off, regardless of its previous setting. See
6715 @code{ChangeFlag}.
6717 @example
6718 ClrFlag(SelectedLines,join)
6719 @end example
6721 %end-doc */
6723 static int
6724 ActionClrFlag (int argc, char **argv, int x, int y)
6726 char *function = ARG (0);
6727 char *flag = ARG (1);
6728 ChangeFlag (function, flag, 0, "ClrFlag");
6729 return 0;
6732 /* --------------------------------------------------------------------------- */
6734 static const char changeflag_syntax[] =
6735 "ChangeFlag(Object|Selected|SelectedObjects, flag, value)\n"
6736 "ChangeFlag(SelectedLines|SelectedPins|SelectedVias, flag, value)\n"
6737 "ChangeFlag(SelectedPads|SelectedTexts|SelectedNames, flag, value)\n"
6738 "ChangeFlag(SelectedElements, flag, value)\n"
6739 "flag = square | octagon | thermal | join\n" "value = 0 | 1";
6741 static const char changeflag_help[] = "Sets or clears flags on objects.";
6743 /* %start-doc actions ChangeFlag
6745 Toggles the given flag on the indicated object(s). The flag may be
6746 one of the flags listed above (square, octagon, thermal, join). The
6747 value may be the number 0 or 1. If the value is 0, the flag is
6748 cleared. If the value is 1, the flag is set.
6750 %end-doc */
6752 static int
6753 ActionChangeFlag (int argc, char **argv, int x, int y)
6755 char *function = ARG (0);
6756 char *flag = ARG (1);
6757 int value = argc > 2 ? atoi (argv[2]) : -1;
6758 if (value != 0 && value != 1)
6759 AFAIL (changeflag);
6761 ChangeFlag (function, flag, value, "ChangeFlag");
6762 return 0;
6766 static void
6767 ChangeFlag (char *what, char *flag_name, int value, char *cmd_name)
6769 bool (*set_object) (int, void *, void *, void *);
6770 bool (*set_selected) (int);
6772 if (NSTRCMP (flag_name, "square") == 0)
6774 set_object = value ? SetObjectSquare : ClrObjectSquare;
6775 set_selected = value ? SetSelectedSquare : ClrSelectedSquare;
6777 else if (NSTRCMP (flag_name, "octagon") == 0)
6779 set_object = value ? SetObjectOctagon : ClrObjectOctagon;
6780 set_selected = value ? SetSelectedOctagon : ClrSelectedOctagon;
6782 else if (NSTRCMP (flag_name, "join") == 0)
6784 /* Note: these are backwards, because the flag is "clear" but
6785 the command is "join". */
6786 set_object = value ? ClrObjectJoin : SetObjectJoin;
6787 set_selected = value ? ClrSelectedJoin : SetSelectedJoin;
6789 else
6791 Message (_("%s(): Flag \"%s\" is not valid\n"), cmd_name, flag_name);
6792 return;
6795 HideCrosshair (true);
6796 switch (GetFunctionID (what))
6798 case F_Object:
6800 int type;
6801 void *ptr1, *ptr2, *ptr3;
6803 if ((type =
6804 SearchScreen (Crosshair.X, Crosshair.Y, CHANGESIZE_TYPES,
6805 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
6806 if (TEST_FLAG (LOCKFLAG, (PinTypePtr) ptr2))
6807 Message (_("Sorry, the object is locked\n"));
6808 if (set_object (type, ptr1, ptr2, ptr3))
6809 SetChangedFlag (true);
6810 break;
6813 case F_SelectedVias:
6814 if (set_selected (VIA_TYPE))
6815 SetChangedFlag (true);
6816 break;
6818 case F_SelectedPins:
6819 if (set_selected (PIN_TYPE))
6820 SetChangedFlag (true);
6821 break;
6823 case F_SelectedPads:
6824 if (set_selected (PAD_TYPE))
6825 SetChangedFlag (true);
6826 break;
6828 case F_SelectedLines:
6829 if (set_selected (LINE_TYPE))
6830 SetChangedFlag (true);
6831 break;
6833 case F_SelectedTexts:
6834 if (set_selected (TEXT_TYPE))
6835 SetChangedFlag (true);
6836 break;
6838 case F_SelectedNames:
6839 if (set_selected (ELEMENTNAME_TYPE))
6840 SetChangedFlag (true);
6841 break;
6843 case F_SelectedElements:
6844 if (set_selected (ELEMENT_TYPE))
6845 SetChangedFlag (true);
6846 break;
6848 case F_Selected:
6849 case F_SelectedObjects:
6850 if (set_selected (CHANGESIZE_TYPES))
6851 SetChangedFlag (true);
6852 break;
6854 RestoreCrosshair (true);
6858 /* --------------------------------------------------------------------------- */
6860 static const char executefile_syntax[] = "ExecuteFile(filename)";
6862 static const char executefile_help[] = "Run actions from the given file.";
6864 /* %start-doc actions ExecuteFile
6866 Lines starting with @code{#} are ignored.
6868 %end-doc */
6870 static int
6871 ActionExecuteFile (int argc, char **argv, int x, int y)
6873 FILE *fp;
6874 char *fname;
6875 char line[256];
6876 int n = 0;
6877 char *sp;
6879 if (argc != 1)
6880 AFAIL (executefile);
6882 fname = argv[0];
6884 if ((fp = fopen (fname, "r")) == NULL)
6886 fprintf (stderr, _("Could not open actions file \"%s\".\n"), fname);
6887 return 1;
6890 defer_updates = 1;
6891 defer_needs_update = 0;
6892 while (fgets (line, sizeof (line), fp) != NULL)
6894 n++;
6895 sp = line;
6897 /* eat the trailing newline */
6898 while (*sp && *sp != '\r' && *sp != '\n')
6899 sp++;
6900 *sp = '\0';
6902 /* eat leading spaces and tabs */
6903 sp = line;
6904 while (*sp && (*sp == ' ' || *sp == '\t'))
6905 sp++;
6908 * if we have anything left and its not a comment line
6909 * then execute it
6912 if (*sp && *sp != '#')
6914 /*Message ("%s : line %-3d : \"%s\"\n", fname, n, sp);*/
6915 hid_parse_actions (sp);
6919 defer_updates = 0;
6920 if (defer_needs_update)
6922 IncrementUndoSerialNumber ();
6923 gui->invalidate_all ();
6925 fclose (fp);
6926 return 0;
6929 /* --------------------------------------------------------------------------- */
6931 static int
6932 ActionPSCalib (int argc, char **argv, int x, int y)
6934 HID *ps = hid_find_exporter ("ps");
6935 ps->calibrate (0.0,0.0);
6936 return 0;
6939 /* --------------------------------------------------------------------------- */
6941 static ElementType *element_cache = NULL;
6943 static ElementType *
6944 find_element_by_refdes (char *refdes)
6946 if (element_cache
6947 && NAMEONPCB_NAME(element_cache)
6948 && strcmp (NAMEONPCB_NAME(element_cache), refdes) == 0)
6949 return element_cache;
6951 ELEMENT_LOOP (PCB->Data);
6953 if (NAMEONPCB_NAME(element)
6954 && strcmp (NAMEONPCB_NAME(element), refdes) == 0)
6956 element_cache = element;
6957 return element_cache;
6960 END_LOOP;
6961 return NULL;
6964 static AttributeType *
6965 lookup_attr (AttributeListTypePtr list, const char *name)
6967 int i;
6968 for (i=0; i<list->Number; i++)
6969 if (strcmp (list->List[i].name, name) == 0)
6970 return & list->List[i];
6971 return NULL;
6974 static void
6975 delete_attr (AttributeListTypePtr list, AttributeType *attr)
6977 int idx = attr - list->List;
6978 if (idx < 0 || idx >= list->Number)
6979 return;
6980 if (list->Number - idx > 1)
6981 memmove (attr, attr+1, (list->Number - idx - 1) * sizeof(AttributeType));
6982 list->Number --;
6985 /* ---------------------------------------------------------------- */
6986 static const char elementlist_syntax[] = "ElementList(Start|Done|Need,<refdes>,<footprint>,<value>)";
6988 static const char elementlist_help[] = "Adds the given element if it doesn't already exist.";
6990 /* %start-doc actions elementlist
6992 @table @code
6994 @item Start
6995 Indicates the start of an element list; call this before any Need
6996 actions.
6998 @item Need
6999 Searches the board for an element with a matching refdes.
7001 If found, the value and footprint are updated.
7003 If not found, a new element is created with the given footprint and value.
7005 @item Done
7006 Compares the list of elements needed since the most recent
7007 @code{start} with the list of elements actually on the board. Any
7008 elements that weren't listed are selected, so that the user may delete
7009 them.
7011 @end table
7013 %end-doc */
7015 static int
7016 parse_layout_attribute_units (char *name, int def)
7018 const char *as, *units = NULL;
7019 int n = 0, v;
7020 bool absolute;
7022 as = AttributeGet (PCB, name);
7023 if (!as)
7024 return def;
7026 sscanf (as, "%d%n", &v, &n);
7027 units = as + n;
7028 if (! *units)
7029 units = NULL;
7030 v = GetValue (as, units, &absolute);
7031 return v;
7034 static int
7035 ActionElementList (int argc, char **argv, int x, int y)
7037 ElementType *e = NULL;
7038 char *refdes, *value, *footprint, *old;
7039 char *args[3];
7040 char *function = argv[0];
7042 #ifdef DEBUG
7043 printf("Entered ActionElementList, executing function %s\n", function);
7044 #endif
7046 if (strcasecmp (function, "start") == 0)
7048 ELEMENT_LOOP (PCB->Data);
7050 CLEAR_FLAG (FOUNDFLAG, element);
7052 END_LOOP;
7053 element_cache = NULL;
7054 return 0;
7057 if (strcasecmp (function, "done") == 0)
7059 ELEMENT_LOOP (PCB->Data);
7061 if (TEST_FLAG (FOUNDFLAG, element))
7063 CLEAR_FLAG (FOUNDFLAG, element);
7065 else if (! EMPTY_STRING_P (NAMEONPCB_NAME (element)))
7067 /* Unnamed elements should remain untouched */
7068 SET_FLAG (SELECTEDFLAG, element);
7071 END_LOOP;
7072 return 0;
7075 if (strcasecmp (function, "need") != 0)
7076 AFAIL (elementlist);
7078 if (argc != 4)
7079 AFAIL (elementlist);
7081 argc --;
7082 argv ++;
7084 refdes = ARG(0);
7085 footprint = ARG(1);
7086 value = ARG(2);
7088 args[0] = footprint;
7089 args[1] = refdes;
7090 args[2] = value;
7092 #ifdef DEBUG
7093 printf(" ... footprint = %s\n", footprint);
7094 printf(" ... refdes = %s\n", refdes);
7095 printf(" ... value = %s\n", value);
7096 #endif
7098 e = find_element_by_refdes (refdes);
7100 if (!e)
7102 int nx, ny, d;
7104 #ifdef DEBUG
7105 printf(" ... Footprint not on board, need to add it.\n");
7106 #endif
7107 /* Not on board, need to add it. */
7108 if (LoadFootprint(argc, args, x, y))
7109 return 1;
7111 nx = PCB->MaxWidth / 2;
7112 ny = PCB->MaxHeight / 2;
7113 d = MIN (PCB->MaxWidth, PCB->MaxHeight) / 10;
7115 nx = parse_layout_attribute_units ("import::newX", nx);
7116 ny = parse_layout_attribute_units ("import::newY", ny);
7117 d = parse_layout_attribute_units ("import::disperse", d);
7119 if (d > 0)
7121 nx += random () % (d*2) - d;
7122 ny += random () % (d*2) - d;
7125 if (nx < 0)
7126 nx = 0;
7127 if (nx >= PCB->MaxWidth)
7128 nx = PCB->MaxWidth - 1;
7129 if (ny < 0)
7130 ny = 0;
7131 if (ny >= PCB->MaxHeight)
7132 ny = PCB->MaxHeight - 1;
7134 /* Place components onto center of board. */
7135 if (CopyPastebufferToLayout (nx, ny))
7136 SetChangedFlag (true);
7139 else if (e && strcmp (DESCRIPTION_NAME(e), footprint) != 0)
7141 #ifdef DEBUG
7142 printf(" ... Footprint on board, but different from footprint loaded.\n");
7143 #endif
7144 int er, pr, i;
7145 LocationType mx, my;
7146 ElementType *pe;
7148 /* Different footprint, we need to swap them out. */
7149 if (LoadFootprint(argc, args, x, y))
7150 return 1;
7152 er = ElementOrientation (e);
7153 pe = & PASTEBUFFER->Data->Element[0];
7154 pr = ElementOrientation (pe);
7156 mx = e->MarkX;
7157 my = e->MarkY;
7159 if (er != pr)
7160 RotateElementLowLevel (PASTEBUFFER->Data, pe, pe->MarkX, pe->MarkY, (er-pr+4)%4);
7162 for (i=0; i<MAX_ELEMENTNAMES; i++)
7164 pe->Name[i].X = e->Name[i].X - mx + pe->MarkX ;
7165 pe->Name[i].Y = e->Name[i].Y - my + pe->MarkY ;
7166 pe->Name[i].Direction = e->Name[i].Direction;
7167 pe->Name[i].Scale = e->Name[i].Scale;
7170 RemoveElement (e);
7172 if (CopyPastebufferToLayout (mx, my))
7173 SetChangedFlag (true);
7176 /* Now reload footprint */
7177 e = find_element_by_refdes (refdes);
7179 old = ChangeElementText (PCB, PCB->Data, e, NAMEONPCB_INDEX, strdup (refdes));
7180 if (old)
7181 free(old);
7182 old = ChangeElementText (PCB, PCB->Data, e, VALUE_INDEX, strdup (value));
7183 if (old)
7184 free(old);
7186 SET_FLAG (FOUNDFLAG, e);
7188 #ifdef DEBUG
7189 printf(" ... Leaving ActionElementList.\n");
7190 #endif
7192 return 0;
7195 /* ---------------------------------------------------------------- */
7196 static const char elementsetattr_syntax[] = "ElementSetAttr(refdes,name[,value])";
7198 static const char elementsetattr_help[] = "Sets or clears an element-specific attribute";
7200 /* %start-doc actions elementsetattr
7202 If a value is specified, the named attribute is added (if not already
7203 present) or changed (if it is) to the given value. If the value is
7204 not specified, the given attribute is removed if present.
7206 %end-doc */
7208 static int
7209 ActionElementSetAttr (int argc, char **argv, int x, int y)
7211 ElementType *e = NULL;
7212 char *refdes, *name, *value;
7213 AttributeType *attr;
7215 if (argc < 2)
7217 AFAIL (changepinname);
7220 refdes = argv[0];
7221 name = argv[1];
7222 value = ARG(2);
7224 ELEMENT_LOOP (PCB->Data);
7226 if (NSTRCMP (refdes, NAMEONPCB_NAME (element)) == 0)
7228 e = element;
7229 break;
7232 END_LOOP;
7234 if (!e)
7236 Message(_("Cannot change attribute of %s - element not found\n"), refdes);
7237 return 1;
7240 attr = lookup_attr (&e->Attributes, name);
7242 if (attr && value)
7244 free (attr->value);
7245 attr->value = strdup (value);
7247 if (attr && ! value)
7249 delete_attr (& e->Attributes, attr);
7251 if (!attr && value)
7253 CreateNewAttribute (& e->Attributes, name, value);
7256 return 0;
7259 /* ---------------------------------------------------------------- */
7260 static const char execcommand_syntax[] = "ExecCommand(command)";
7262 static const char execcommand_help[] = "Runs a command";
7264 /* %start-doc actions execcommand
7266 Runs the given command, which is a system executable.
7268 %end-doc */
7270 static int
7271 ActionExecCommand (int argc, char **argv, int x, int y)
7273 char *command;
7275 if (argc < 1)
7277 AFAIL (execcommand);
7280 command = ARG(0);
7282 if (system (command))
7283 return 1;
7284 return 0;
7287 /* ---------------------------------------------------------------- */
7289 static int
7290 pcb_spawnvp (char **argv)
7292 #ifdef HAVE__SPAWNVP
7293 int result = _spawnvp (_P_WAIT, argv[0], argv);
7294 if (result == -1)
7295 return 1;
7296 else
7297 return 0;
7298 #else
7299 int pid;
7300 pid = fork ();
7301 if (pid < 0)
7303 /* error */
7304 Message(_("Cannot fork!"));
7305 return 1;
7307 else if (pid == 0)
7309 /* Child */
7310 execvp (argv[0], argv);
7311 exit(1);
7313 else
7315 int rv;
7316 /* Parent */
7317 wait (&rv);
7319 return 0;
7320 #endif
7323 /* ---------------------------------------------------------------- */
7325 * Creates a new temporary file name. Hopefully the operating system
7326 * provides a mkdtemp() function to securily create a temporary
7327 * directory with mode 0700. If so then that directory is created and
7328 * the returned string is made up of the directory plus the name
7329 * variable. For example:
7331 * tempfile_name_new ("myfile") might return
7332 * "/var/tmp/pcb.123456/myfile".
7334 * If mkdtemp() is not available then 'name' is ignored and the
7335 * insecure tmpnam() function is used.
7337 * Files/names created with tempfile_name_new() should be unlinked
7338 * with tempfile_unlink to make sure the temporary directory is also
7339 * removed when mkdtemp() is used.
7341 static char *
7342 tempfile_name_new (char * name)
7344 char *tmpfile = NULL;
7345 #ifdef HAVE_MKDTEMP
7346 char *tmpdir, *mytmpdir;
7347 size_t len;
7348 #endif
7350 assert ( name != NULL );
7352 #ifdef HAVE_MKDTEMP
7353 #define TEMPLATE "pcb.XXXXXXXX"
7356 tmpdir = getenv ("TMPDIR");
7358 /* FIXME -- what about win32? */
7359 if (tmpdir == NULL) {
7360 tmpdir = "/tmp";
7363 mytmpdir = (char *) malloc (sizeof(char) *
7364 (strlen (tmpdir) +
7366 strlen (TEMPLATE) +
7367 1));
7368 if (mytmpdir == NULL) {
7369 fprintf (stderr, "%s(): malloc failed()\n", __FUNCTION__);
7370 exit (1);
7373 *mytmpdir = '\0';
7374 (void)strcat (mytmpdir, tmpdir);
7375 (void)strcat (mytmpdir, PCB_DIR_SEPARATOR_S);
7376 (void)strcat (mytmpdir, TEMPLATE);
7377 if (mkdtemp (mytmpdir) == NULL) {
7378 fprintf (stderr, "%s(): mkdtemp (\"%s\") failed\n", __FUNCTION__, mytmpdir);
7379 free (mytmpdir);
7380 return NULL;
7384 len = strlen (mytmpdir) + /* the temp directory name */
7385 1 + /* the directory sep. */
7386 strlen (name) + /* the file name */
7387 1 /* the \0 termination */
7390 tmpfile = (char *) malloc (sizeof (char) * len);
7392 *tmpfile = '\0';
7393 (void)strcat (tmpfile, mytmpdir);
7394 (void)strcat (tmpfile, PCB_DIR_SEPARATOR_S);
7395 (void)strcat (tmpfile, name);
7397 free (mytmpdir);
7398 #undef TEMPLATE
7399 #else
7401 * tmpnam() uses a static buffer so strdup() the result right away
7402 * in case someone decides to create multiple temp names.
7404 tmpfile = strdup (tmpnam (NULL));
7405 #endif
7407 return tmpfile;
7410 /* ---------------------------------------------------------------- */
7412 * Unlink a temporary file. If we have mkdtemp() then our temp file
7413 * lives in a temporary directory and we need to remove that directory
7414 * too.
7416 static int
7417 tempfile_unlink (char * name)
7419 int rc;
7421 #ifdef DEBUG
7422 /* SDB says: Want to keep old temp files for examiniation when debugging */
7423 return 0;
7424 #endif
7426 #ifdef HAVE_MKDTEMP
7427 int e, rc2 = 0;
7428 char *dname;
7430 rc = unlink (name);
7431 /* it is possible that the file was never created so it is OK if the
7432 unlink fails */
7434 /* now figure out the directory name to remove */
7435 e = strlen (name) - 1;
7436 while (e > 0 && name[e] != PCB_DIR_SEPARATOR_C) {e--;}
7438 dname = strdup (name);
7439 dname[e] = '\0';
7442 * at this point, e *should* point to the end of the directory part
7443 * but lets make sure.
7445 if (e > 0) {
7446 rc2 = rmdir (dname);
7447 if (rc2 != 0) {
7448 perror (dname);
7451 } else {
7452 fprintf (stderr, _("%s(): Unable to determine temp directory name from the temp file\n"),
7453 __FUNCTION__);
7454 fprintf (stderr, "%s(): \"%s\"\n",
7455 __FUNCTION__, name);
7456 rc2 = -1;
7459 /* name was allocated with malloc */
7460 free (dname);
7461 free (name);
7464 * FIXME - should also return -1 if the temp file exists and was not
7465 * removed.
7467 if (rc2 != 0) {
7468 return -1;
7471 #else
7472 rc = unlink (name);
7474 if (rc != 0) {
7475 fprintf (stderr, _("Failed to unlink \"%s\"\n"), name);
7476 free (name);
7477 return rc;
7479 free (name);
7481 #endif
7483 return 0;
7486 /* ---------------------------------------------------------------- */
7487 static const char import_syntax[] =
7488 "Import()\n"
7489 "Import([gnetlist|make[,source,source,...]])\n"
7490 "Import(setnewpoint[,(mark|center|X,Y)])\n"
7491 "Import(setdisperse,D,units)\n";
7493 static const char import_help[] = "Import schematics";
7495 /* %start-doc actions Import
7497 Imports element and netlist data from the schematics (or some other
7498 source). The first parameter, which is optional, is the mode. If not
7499 specified, the @code{import::mode} attribute in the PCB is used.
7500 @code{gnetlist} means gnetlist is used to obtain the information from
7501 the schematics. @code{make} invokes @code{make}, assuming the user
7502 has a @code{Makefile} in the current directory. The @code{Makefile}
7503 will be invoked with the following variables set:
7505 @table @code
7507 @item PCB
7508 The name of the .pcb file
7510 @item SRCLIST
7511 A space-separated list of source files
7513 @item OUT
7514 The name of the file in which to put the command script, which may
7515 contain any @pcb{} actions. By default, this is a temporary file
7516 selected by @pcb{}, but if you specify an @code{import::outfile}
7517 attribute, that file name is used instead (and not automatically
7518 deleted afterwards).
7520 @end table
7522 The target specified to be built is the first of these that apply:
7524 @itemize @bullet
7526 @item
7527 The target specified by an @code{import::target} attribute.
7529 @item
7530 The output file specified by an @code{import::outfile} attribute.
7532 @item
7533 If nothing else is specified, the target is @code{pcb_import}.
7535 @end itemize
7537 If you specify an @code{import::makefile} attribute, then "-f <that
7538 file>" will be added to the command line.
7540 If you specify the mode, you may also specify the source files
7541 (schematics). If you do not specify any, the list of schematics is
7542 obtained by reading the @code{import::src@var{N}} attributes (like
7543 @code{import::src0}, @code{import::src1}, etc).
7545 For compatibility with future extensions to the import file format,
7546 the generated file @emph{must not} start with the two characters
7547 @code{#%}.
7549 If a temporary file is needed the @code{TMPDIR} environment variable
7550 is used to select its location.
7552 Note that the programs @code{gnetlist} and @code{make} may be
7553 overridden by the user via the @code{make-program} and @code{gnetlist}
7554 @code{pcb} settings (i.e. in @code{~/.pcb/settings} or on the command
7555 line).
7557 If @pcb{} cannot determine which schematic(s) to import from, the GUI
7558 is called to let user choose (see @code{ImportGUI()}).
7560 Note that Import() doesn't delete anything - after an Import, elements
7561 which shouldn't be on the board are selected and may be removed once
7562 it's determined that the deletion is appropriate.
7564 In @code{Import()} is called with @code{setnewpoint} then the location
7565 of new components can be specified. This is where parts show up when
7566 they're added to the board. The default is the center of the board.
7568 @table @code
7570 @item Import(setnewpoint)
7572 Prompts the user to click on the board somewhere, uses that point. If
7573 called by a hotkey, uses the current location of the crosshair.
7575 @item Import(setnewpoint,mark)
7577 Uses the location of the mark. If no mark is present, the point is
7578 not changed.
7580 @item Import(setnewpoint,center)
7582 Resets the point to the center of the board.
7584 @item Import(setnewpoint,X,Y,units)
7586 Sets the point to the specific coordinates given. Example:
7587 @code{Import(setnewpoint,50,25,mm)}
7589 @end table
7591 Note that the X and Y locations are stored in attributes named
7592 @code{import::newX} and @code{import::newY} so you could change them
7593 manually if you wished.
7595 Calling @code{Import(setdisperse,D,units)} sets how much the newly
7596 placed elements are dispersed relative to the set point. For example,
7597 @code{Import(setdisperse,10,mm)} will offset each part randomly up to
7598 10mm away from the point. The default dispersion is 1/10th of the
7599 smallest board dimension. Dispersion is saved in the
7600 @code{import::disperse} attribute.
7602 %end-doc */
7604 static int
7605 ActionImport (int argc, char **argv, int x, int y)
7607 char *mode;
7608 char **sources = NULL;
7609 int nsources = 0;
7611 #ifdef DEBUG
7612 printf("ActionImport: =========== Entering ActionImport ============\n");
7613 #endif
7615 mode = ARG (0);
7617 if (mode && strcasecmp (mode, "setdisperse") == 0)
7619 char *ds, *units;
7620 char buf[50];
7622 ds = ARG (1);
7623 units = ARG (2);
7624 if (!ds)
7626 const char *as = AttributeGet (PCB, "import::disperse");
7627 ds = gui->prompt_for(_("Enter dispersion:"), as ? as : "0");
7629 if (units)
7631 sprintf(buf, "%s%s", ds, units);
7632 AttributePut (PCB, "import::disperse", buf);
7634 else
7635 AttributePut (PCB, "import::disperse", ds);
7636 if (ARG (1) == NULL)
7637 free (ds);
7638 return 0;
7641 if (mode && strcasecmp (mode, "setnewpoint") == 0)
7643 const char *xs, *ys, *units;
7644 int x, y;
7645 char buf[50];
7647 xs = ARG (1);
7648 ys = ARG (2);
7649 units = ARG (3);
7651 if (!xs)
7653 gui->get_coords (_("Click on a location"), &x, &y);
7655 else if (strcasecmp (xs, "center") == 0)
7657 AttributeRemove (PCB, "import::newX");
7658 AttributeRemove (PCB, "import::newY");
7659 return 0;
7661 else if (strcasecmp (xs, "mark") == 0)
7663 if (Marked.status)
7665 x = Marked.X;
7666 y = Marked.Y;
7669 else if (ys)
7671 bool absolute;
7672 x = GetValue (xs, units, &absolute);
7673 y = GetValue (ys, units, &absolute);
7675 else
7677 Message (_("Bad syntax for Import(setnewpoint)"));
7678 return 1;
7681 sprintf (buf, "%d", x);
7682 AttributePut (PCB, "import::newX", buf);
7683 sprintf (buf, "%d", y);
7684 AttributePut (PCB, "import::newY", buf);
7685 return 0;
7688 if (! mode)
7689 mode = AttributeGet (PCB, "import::mode");
7690 if (! mode)
7691 mode = "gnetlist";
7693 if (argc > 1)
7695 sources = argv + 1;
7696 nsources = argc - 1;
7699 if (! sources)
7701 char sname[40];
7702 char *src;
7704 nsources = -1;
7705 do {
7706 nsources ++;
7707 sprintf(sname, "import::src%d", nsources);
7708 src = AttributeGet (PCB, sname);
7709 } while (src);
7711 if (nsources > 0)
7713 sources = (char **) malloc ((nsources + 1) * sizeof (char *));
7714 nsources = -1;
7715 do {
7716 nsources ++;
7717 sprintf(sname, "import::src%d", nsources);
7718 src = AttributeGet (PCB, sname);
7719 sources[nsources] = src;
7720 } while (src);
7724 if (! sources)
7726 /* Replace .pcb with .sch and hope for the best. */
7727 char *pcbname = PCB->Filename;
7728 char *schname;
7729 char *dot, *slash, *bslash;
7731 if (!pcbname)
7732 return hid_action("ImportGUI");
7734 schname = (char *) malloc (strlen(pcbname) + 5);
7735 strcpy (schname, pcbname);
7736 dot = strchr (schname, '.');
7737 slash = strchr (schname, '/');
7738 bslash = strchr (schname, '\\');
7739 if (dot && slash && dot < slash)
7740 dot = NULL;
7741 if (dot && bslash && dot < bslash)
7742 dot = NULL;
7743 if (dot)
7744 *dot = 0;
7745 strcat (schname, ".sch");
7747 if (access (schname, F_OK))
7748 return hid_action("ImportGUI");
7750 sources = (char **) malloc (2 * sizeof (char *));
7751 sources[0] = schname;
7752 sources[1] = NULL;
7753 nsources = 1;
7756 if (strcasecmp (mode, "gnetlist") == 0)
7758 char *tmpfile = tempfile_name_new ("gnetlist_output");
7759 char **cmd;
7760 int i;
7762 if (tmpfile == NULL) {
7763 Message (_("Could not create temp file"));
7764 return 1;
7767 cmd = (char **) malloc ((6 + nsources) * sizeof (char *));
7768 cmd[0] = Settings.GnetlistProgram;
7769 cmd[1] = "-g";
7770 cmd[2] = "pcbfwd";
7771 cmd[3] = "-o";
7772 cmd[4] = tmpfile;
7773 for (i=0; i<nsources; i++)
7774 cmd[5+i] = sources[i];
7775 cmd[5+nsources] = NULL;
7777 #ifdef DEBUG
7778 printf("ActionImport: =========== About to run gnetlist ============\n");
7779 printf("%s %s %s %s %s %s ...\n",
7780 cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5]);
7781 #endif
7783 if (pcb_spawnvp (cmd))
7785 unlink (tmpfile);
7786 return 1;
7789 #ifdef DEBUG
7790 printf("ActionImport: =========== About to run ActionExecuteFile, file = %s ============\n", tmpfile);
7791 #endif
7793 cmd[0] = tmpfile;
7794 cmd[1] = NULL;
7795 ActionExecuteFile (1, cmd, 0, 0);
7797 free (cmd);
7798 tempfile_unlink (tmpfile);
7800 else if (strcasecmp (mode, "make") == 0)
7802 int must_free_tmpfile = 0;
7803 char *tmpfile;
7804 char *cmd[10];
7805 int i;
7806 char *srclist;
7807 int srclen;
7808 char *user_outfile = NULL;
7809 char *user_makefile = NULL;
7810 char *user_target = NULL;
7813 user_outfile = AttributeGet (PCB, "import::outfile");
7814 user_makefile = AttributeGet (PCB, "import::makefile");
7815 user_target = AttributeGet (PCB, "import::target");
7816 if (user_outfile && !user_target)
7817 user_target = user_outfile;
7819 if (user_outfile)
7820 tmpfile = user_outfile;
7821 else
7823 tmpfile = tempfile_name_new ("gnetlist_output");
7824 if (tmpfile == NULL) {
7825 Message (_("Could not create temp file"));
7826 return 1;
7828 must_free_tmpfile = 1;
7831 srclen = sizeof("SRCLIST=") + 2;
7832 for (i=0; i<nsources; i++)
7833 srclen += strlen (sources[i]) + 2;
7834 srclist = (char *) malloc (srclen);
7835 strcpy (srclist, "SRCLIST=");
7836 for (i=0; i<nsources; i++)
7838 if (i)
7839 strcat (srclist, " ");
7840 strcat (srclist, sources[i]);
7843 cmd[0] = Settings.MakeProgram;
7844 cmd[1] = "-s";
7845 cmd[2] = Concat ("PCB=", PCB->Filename, NULL);
7846 cmd[3] = srclist;
7847 cmd[4] = Concat ("OUT=", tmpfile, NULL);
7848 i = 5;
7849 if (user_makefile)
7851 cmd[i++] = "-f";
7852 cmd[i++] = user_makefile;
7854 cmd[i++] = user_target ? user_target : "pcb_import";
7855 cmd[i++] = NULL;
7857 if (pcb_spawnvp (cmd))
7859 if (must_free_tmpfile)
7860 unlink (tmpfile);
7861 free (cmd[2]);
7862 free (cmd[3]);
7863 free (cmd[4]);
7864 return 1;
7867 cmd[0] = tmpfile;
7868 cmd[1] = NULL;
7869 ActionExecuteFile (1, cmd, 0, 0);
7871 free (cmd[2]);
7872 free (cmd[3]);
7873 free (cmd[4]);
7874 if (must_free_tmpfile)
7875 tempfile_unlink (tmpfile);
7877 else
7879 Message (_("Unknown import mode: %s\n"), mode);
7880 return 1;
7883 DeleteRats (false);
7884 AddAllRats (false, NULL);
7886 #ifdef DEBUG
7887 printf("ActionImport: =========== Leaving ActionImport ============\n");
7888 #endif
7890 return 0;
7893 /* ------------------------------------------------------------ */
7895 static const char attributes_syntax[] =
7896 "Attributes(Layout|Layer|Element)\n"
7897 "Attributes(Layer,layername)";
7899 static const char attributes_help[] =
7900 "Let the user edit the attributes of the layout, current or given\n"
7901 "layer, or selected element.";
7903 /* %start-doc actions Attributes
7905 This just pops up a dialog letting the user edit the attributes of the
7906 pcb, an element, or a layer.
7908 %end-doc */
7911 static int
7912 ActionAttributes (int argc, char **argv, int x, int y)
7914 char *function = ARG (0);
7915 char *layername = ARG (1);
7916 char *buf;
7918 if (!function)
7919 AFAIL (attributes);
7921 if (!gui->edit_attributes)
7923 Message (_("This GUI doesn't support Attribute Editing\n"));
7924 return 1;
7927 switch (GetFunctionID (function))
7929 case F_Layout:
7931 gui->edit_attributes("Layout Attributes", &(PCB->Attributes));
7932 return 0;
7935 case F_Layer:
7937 LayerType *layer = CURRENT;
7938 if (layername)
7940 int i;
7941 layer = NULL;
7942 for (i=0; i<max_copper_layer; i++)
7943 if (strcmp (PCB->Data->Layer[i].Name, layername) == 0)
7945 layer = & (PCB->Data->Layer[i]);
7946 break;
7948 if (layer == NULL)
7950 Message (_("No layer named %s\n"), layername);
7951 return 1;
7954 buf = (char *) malloc (strlen (layer->Name) + strlen ("Layer X Attributes"));
7955 sprintf (buf, "Layer %s Attributes", layer->Name);
7956 gui->edit_attributes(buf, &(layer->Attributes));
7957 free (buf);
7958 return 0;
7961 case F_Element:
7963 int n_found = 0;
7964 ElementType *e = NULL;
7965 ELEMENT_LOOP (PCB->Data);
7967 if (TEST_FLAG (SELECTEDFLAG, element))
7969 e = element;
7970 n_found ++;
7973 END_LOOP;
7974 if (n_found > 1)
7976 Message (_("Too many elements selected\n"));
7977 return 1;
7979 if (n_found == 0)
7981 void *ptrtmp;
7982 gui->get_coords (_("Click on an element"), &x, &y);
7983 if ((SearchScreen
7984 (x, y, ELEMENT_TYPE, &ptrtmp,
7985 &ptrtmp, &ptrtmp)) != NO_TYPE)
7986 e = (ElementTypePtr) ptrtmp;
7987 else
7989 Message (_("No element found there\n"));
7990 return 1;
7994 if (NAMEONPCB_NAME(e))
7996 buf = (char *) malloc (strlen (NAMEONPCB_NAME(e)) + strlen ("Element X Attributes"));
7997 sprintf(buf, "Element %s Attributes", NAMEONPCB_NAME(e));
7999 else
8001 buf = strdup ("Unnamed Element Attributes");
8003 gui->edit_attributes(buf, &(e->Attributes));
8004 free (buf);
8005 break;
8008 default:
8009 AFAIL (attributes);
8012 return 0;
8015 /* --------------------------------------------------------------------------- */
8017 HID_Action action_action_list[] = {
8018 {"AddRats", 0, ActionAddRats,
8019 addrats_help, addrats_syntax}
8021 {"Attributes", 0, ActionAttributes,
8022 attributes_help, attributes_syntax}
8024 {"Atomic", 0, ActionAtomic,
8025 atomic_help, atomic_syntax}
8027 {"AutoPlaceSelected", 0, ActionAutoPlaceSelected,
8028 autoplace_help, autoplace_syntax}
8030 {"AutoRoute", 0, ActionAutoRoute,
8031 autoroute_help, autoroute_syntax}
8033 {"ChangeClearSize", 0, ActionChangeClearSize,
8034 changeclearsize_help, changeclearsize_syntax}
8036 {"ChangeDrillSize", 0, ActionChange2ndSize,
8037 changedrillsize_help, changedrillsize_syntax}
8039 {"ChangeHole", 0, ActionChangeHole,
8040 changehold_help, changehold_syntax}
8042 {"ChangeJoin", 0, ActionChangeJoin,
8043 changejoin_help, changejoin_syntax}
8045 {"ChangeName", 0, ActionChangeName,
8046 changename_help, changename_syntax}
8048 {"ChangePaste", 0, ActionChangePaste,
8049 changepaste_help, changepaste_syntax}
8051 {"ChangePinName", 0, ActionChangePinName,
8052 changepinname_help, changepinname_syntax}
8054 {"ChangeSize", 0, ActionChangeSize,
8055 changesize_help, changesize_syntax}
8057 {"ChangeSquare", 0, ActionChangeSquare,
8058 changesquare_help, changesquare_syntax}
8060 {"ChangeOctagon", 0, ActionChangeOctagon,
8061 changeoctagon_help, changeoctagon_syntax}
8063 {"ClearSquare", 0, ActionClearSquare,
8064 clearsquare_help, clearsquare_syntax}
8066 {"ClearOctagon", 0, ActionClearOctagon,
8067 clearoctagon_help, clearoctagon_syntax}
8069 {"Connection", 0, ActionConnection,
8070 connection_help, connection_syntax}
8072 {"Delete", 0, ActionDelete,
8073 delete_help, delete_syntax}
8075 {"DeleteRats", 0, ActionDeleteRats,
8076 deleterats_help, deleterats_syntax}
8078 {"DisperseElements", 0, ActionDisperseElements,
8079 disperseelements_help, disperseelements_syntax}
8081 {"Display", 0, ActionDisplay,
8082 display_help, display_syntax}
8084 {"DRC", 0, ActionDRCheck,
8085 drc_help, drc_syntax}
8087 {"DumpLibrary", 0, ActionDumpLibrary,
8088 dumplibrary_help, dumplibrary_syntax}
8090 {"ExecuteFile", 0, ActionExecuteFile,
8091 executefile_help, executefile_syntax}
8093 {"Flip", N_("Click on Object or Flip Point"), ActionFlip,
8094 flip_help, flip_syntax}
8096 {"LoadFrom", 0, ActionLoadFrom,
8097 loadfrom_help, loadfrom_syntax}
8099 {"MarkCrosshair", 0, ActionMarkCrosshair,
8100 markcrosshair_help, markcrosshair_syntax}
8102 {"Message", 0, ActionMessage,
8103 message_help, message_syntax}
8105 {"MinMaskGap", 0, ActionMinMaskGap,
8106 minmaskgap_help, minmaskgap_syntax}
8108 {"MinClearGap", 0, ActionMinClearGap,
8109 mincleargap_help, mincleargap_syntax}
8111 {"Mode", 0, ActionMode,
8112 mode_help, mode_syntax}
8114 {"MorphPolygon", 0, ActionMorphPolygon,
8115 morphpolygon_help, morphpolygon_syntax}
8117 {"PasteBuffer", 0, ActionPasteBuffer,
8118 pastebuffer_help, pastebuffer_syntax}
8120 {"Quit", 0, ActionQuit,
8121 quit_help, quit_syntax}
8123 {"RemoveSelected", 0, ActionRemoveSelected,
8124 removeselected_help, removeselected_syntax}
8126 {"Renumber", 0, ActionRenumber,
8127 renumber_help, renumber_syntax}
8129 {"RipUp", 0, ActionRipUp,
8130 ripup_help, ripup_syntax}
8132 {"Select", 0, ActionSelect,
8133 select_help, select_syntax}
8135 {"Unselect", 0, ActionUnselect,
8136 unselect_help, unselect_syntax}
8138 {"SaveSettings", 0, ActionSaveSettings,
8139 savesettings_help, savesettings_syntax}
8141 {"SaveTo", 0, ActionSaveTo,
8142 saveto_help, saveto_syntax}
8144 {"SetSquare", 0, ActionSetSquare,
8145 setsquare_help, setsquare_syntax}
8147 {"SetOctagon", 0, ActionSetOctagon,
8148 setoctagon_help, setoctagon_syntax}
8150 {"SetThermal", 0, ActionSetThermal,
8151 setthermal_help, setthermal_syntax}
8153 {"SetValue", 0, ActionSetValue,
8154 setvalue_help, setvalue_syntax}
8156 {"ToggleHideName", 0, ActionToggleHideName,
8157 togglehidename_help, togglehidename_syntax}
8159 {"Undo", 0, ActionUndo,
8160 undo_help, undo_syntax}
8162 {"Redo", 0, ActionRedo,
8163 redo_help, redo_syntax}
8165 {"SetSame", N_("Select item to use attributes from"), ActionSetSame,
8166 setsame_help, setsame_syntax}
8168 {"SetFlag", 0, ActionSetFlag,
8169 setflag_help, setflag_syntax}
8171 {"ClrFlag", 0, ActionClrFlag,
8172 clrflag_help, clrflag_syntax}
8174 {"ChangeFlag", 0, ActionChangeFlag,
8175 changeflag_help, changeflag_syntax}
8177 {"Polygon", 0, ActionPolygon,
8178 polygon_help, polygon_syntax}
8180 {"RouteStyle", 0, ActionRouteStyle,
8181 routestyle_help, routestyle_syntax}
8183 {"MoveObject", N_("Select an Object"), ActionMoveObject,
8184 moveobject_help, moveobject_syntax}
8186 {"MoveToCurrentLayer", 0, ActionMoveToCurrentLayer,
8187 movetocurrentlayer_help, movetocurrentlayer_syntax}
8189 {"New", 0, ActionNew,
8190 new_help, new_syntax}
8192 {"pscalib", 0, ActionPSCalib}
8194 {"ElementList", 0, ActionElementList,
8195 elementlist_help, elementlist_syntax}
8197 {"ElementSetAttr", 0, ActionElementSetAttr,
8198 elementsetattr_help, elementsetattr_syntax}
8200 {"ExecCommand", 0, ActionExecCommand,
8201 execcommand_help, execcommand_syntax}
8203 {"Import", 0, ActionImport,
8204 import_help, import_syntax}
8208 REGISTER_ACTIONS (action_action_list)