src/hid/lesstif/menu.c: Added doxygen comments for GetXY().
[geda-pcb/pcjc2.git] / src / action.c
blob7a31a59b1c7df75f0491bdc47e27f804f0caaeb7
1 /*!
2 * \file src/action.c
4 * \brief Action routines for output window.
6 * <hr>
8 * <h1><b>Copyright.</b></h1>\n
10 * PCB, interactive printed circuit board design
12 * Copyright (C) 1994,1995,1996 Thomas Nau
14 * Copyright (C) 1997, 1998, 1999, 2000, 2001 Harry Eaton
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
30 * Contact addresses for paper mail and Email:
31 * Harry Eaton, 6697 Buttonhole Ct, Columbia, MD 21044, USA
32 * haceaton@aplcomm.jhuapl.edu
35 #ifdef HAVE_CONFIG_H
36 #include "config.h"
37 #endif
39 #include "global.h"
41 #include "action.h"
42 #include "autoplace.h"
43 #include "autoroute.h"
44 #include "buffer.h"
45 #include "change.h"
46 #include "copy.h"
47 #include "create.h"
48 #include "crosshair.h"
49 #include "data.h"
50 #include "draw.h"
51 #include "error.h"
52 #include "file.h"
53 #include "find.h"
54 #include "hid.h"
55 #include "insert.h"
56 #include "line.h"
57 #include "mymem.h"
58 #include "misc.h"
59 #include "mirror.h"
60 #include "move.h"
61 #include "polygon.h"
62 /*#include "print.h"*/
63 #include "rats.h"
64 #include "remove.h"
65 #include "report.h"
66 #include "rotate.h"
67 #include "rubberband.h"
68 #include "search.h"
69 #include "select.h"
70 #include "set.h"
71 #include "thermal.h"
72 #include "undo.h"
73 #include "rtree.h"
74 #include "macro.h"
75 #include "pcb-printf.h"
77 #include <assert.h>
78 #include <stdlib.h> /* rand() */
80 #ifdef HAVE_LIBDMALLOC
81 #include <dmalloc.h>
82 #endif
84 /* for fork() and friends */
85 #ifdef HAVE_UNISTD_H
86 #include <unistd.h>
87 #endif
89 #ifdef HAVE_SYS_WAIT_H
90 #include <sys/wait.h>
91 #endif
93 /* ---------------------------------------------------------------------------
94 * some local types
96 typedef enum
98 F_AddSelected,
99 F_All,
100 F_AllConnections,
101 F_AllRats,
102 F_AllUnusedPins,
103 F_Arc,
104 F_Arrow,
105 F_Block,
106 F_Description,
107 F_Cancel,
108 F_Center,
109 F_Clear,
110 F_ClearAndRedraw,
111 F_ClearList,
112 F_Close,
113 F_Found,
114 F_Connection,
115 F_Convert,
116 F_Copy,
117 F_CycleClip,
118 F_CycleCrosshair,
119 F_DeleteRats,
120 F_Drag,
121 F_DrillReport,
122 F_Element,
123 F_ElementByName,
124 F_ElementConnections,
125 F_ElementToBuffer,
126 F_Escape,
127 F_Find,
128 F_FlipElement,
129 F_FoundPins,
130 F_Grid,
131 F_InsertPoint,
132 F_Layer,
133 F_Layout,
134 F_LayoutAs,
135 F_LayoutToBuffer,
136 F_Line,
137 F_LineSize,
138 F_Lock,
139 F_Mirror,
140 F_Move,
141 F_NameOnPCB,
142 F_Netlist,
143 F_NetByName,
144 F_None,
145 F_Notify,
146 F_Object,
147 F_ObjectByName,
148 F_PasteBuffer,
149 F_PadByName,
150 F_PinByName,
151 F_PinOrPadName,
152 F_Pinout,
153 F_Polygon,
154 F_PolygonHole,
155 F_PreviousPoint,
156 F_RatsNest,
157 F_Rectangle,
158 F_Redraw,
159 F_Release,
160 F_Revert,
161 F_Remove,
162 F_RemoveSelected,
163 F_Report,
164 F_Reset,
165 F_ResetLinesAndPolygons,
166 F_ResetPinsViasAndPads,
167 F_Restore,
168 F_Rotate,
169 F_Save,
170 F_Selected,
171 F_SelectedArcs,
172 F_SelectedElements,
173 F_SelectedLines,
174 F_SelectedNames,
175 F_SelectedObjects,
176 F_SelectedPads,
177 F_SelectedPins,
178 F_SelectedTexts,
179 F_SelectedVias,
180 F_SelectedRats,
181 F_Stroke,
182 F_Text,
183 F_TextByName,
184 F_TextScale,
185 F_Thermal,
186 F_ToLayout,
187 F_ToggleAllDirections,
188 F_ToggleAutoDRC,
189 F_ToggleClearLine,
190 F_ToggleFullPoly,
191 F_ToggleGrid,
192 F_ToggleHideNames,
193 F_ToggleMask,
194 F_ToggleName,
195 F_ToggleObject,
196 F_ToggleShowDRC,
197 F_ToggleLiveRoute,
198 F_ToggleRubberBandMode,
199 F_ToggleStartDirection,
200 F_ToggleSnapPin,
201 F_ToggleThindraw,
202 F_ToggleLockNames,
203 F_ToggleOnlyNames,
204 F_ToggleThindrawPoly,
205 F_ToggleOrthoMove,
206 F_ToggleLocalRef,
207 F_ToggleCheckPlanes,
208 F_ToggleUniqueNames,
209 F_Via,
210 F_ViaByName,
211 F_Value,
212 F_ViaDrillingHole,
213 F_ViaSize,
214 F_Zoom
216 FunctionID;
218 typedef struct /* used to identify subfunctions */
220 char *Identifier;
221 FunctionID ID;
223 FunctionType;
225 /* --------------------------------------------------------------------------- */
227 /* %start-doc actions 00delta
229 Many actions take a @code{delta} parameter as the last parameter,
230 which is an amount to change something. That @code{delta} may include
231 units, as an additional parameter, such as @code{Action(Object,5,mm)}.
232 If no units are specified, the default is PCB's native units
233 (currently 1/100 mil). Also, if the delta is prefixed by @code{+} or
234 @code{-}, the size is increased or decreased by that amount.
235 Otherwise, the size size is set to the given amount.
237 @example
238 Action(Object,5,mil)
239 Action(Object,+0.5,mm)
240 Action(Object,-1)
241 @end example
243 Actions which take a @code{delta} parameter which do not accept all
244 these options will specify what they do take.
246 %end-doc */
248 /* %start-doc actions 00objects
250 Many actions act on indicated objects on the board. They will have
251 parameters like @code{ToggleObject} or @code{SelectedVias} to indicate
252 what group of objects they act on. Unless otherwise specified, these
253 parameters are defined as follows:
255 @table @code
257 @item Object
258 @itemx ToggleObject
259 Affects the object under the mouse pointer. If this action is invoked
260 from a menu or script, the user will be prompted to click on an
261 object, which is then the object affected.
263 @item Selected
264 @itemx SelectedObjects
266 Affects all objects which are currently selected. At least, all
267 selected objects for which the given action makes sense.
269 @item SelectedPins
270 @itemx SelectedVias
271 @itemx Selected@var{Type}
272 @itemx @i{etc}
273 Affects all objects which are both selected and of the @var{Type} specified.
275 @end table
277 %end-doc */
279 /* %start-doc actions 00macros
281 @macro pinshapes
283 Pins, pads, and vias can have various shapes. All may be round. Pins
284 and pads may be square (obviously "square" pads are usually
285 rectangular). Pins and vias may be octagonal. When you change a
286 shape flag of an element, you actually change all of its pins and
287 pads.
289 Note that the square flag takes precedence over the octagon flag,
290 thus, if both the square and octagon flags are set, the object is
291 square. When the square flag is cleared, the pins and pads will be
292 either round or, if the octagon flag is set, octagonal.
294 @end macro
296 %end-doc */
298 /* ---------------------------------------------------------------------------
299 * some local identifiers
301 static PointType InsertedPoint;
302 static LayerType *lastLayer;
303 static struct
305 PolygonType *poly;
306 LineType line;
308 fake;
310 static struct
312 Coord X, Y;
313 Cardinal Buffer;
314 bool Click;
315 bool Moving; /* selected type clicked on */
316 int Hit; /* move type clicked on */
317 void *ptr1;
318 void *ptr2;
319 void *ptr3;
321 Note;
323 static int defer_updates = 0;
324 static int defer_needs_update = 0;
326 static Cardinal polyIndex = 0;
327 static bool saved_mode = false;
328 #ifdef HAVE_LIBSTROKE
329 static bool mid_stroke = false;
330 static BoxType StrokeBox;
331 #endif
332 static FunctionType Functions[] = {
333 {"AddSelected", F_AddSelected},
334 {"All", F_All},
335 {"AllConnections", F_AllConnections},
336 {"AllRats", F_AllRats},
337 {"AllUnusedPins", F_AllUnusedPins},
338 {"Arc", F_Arc},
339 {"Arrow", F_Arrow},
340 {"Block", F_Block},
341 {"Description", F_Description},
342 {"Cancel", F_Cancel},
343 {"Center", F_Center},
344 {"Clear", F_Clear},
345 {"ClearAndRedraw", F_ClearAndRedraw},
346 {"ClearList", F_ClearList},
347 {"Close", F_Close},
348 {"Found", F_Found},
349 {"Connection", F_Connection},
350 {"Convert", F_Convert},
351 {"Copy", F_Copy},
352 {"CycleClip", F_CycleClip},
353 {"CycleCrosshair", F_CycleCrosshair},
354 {"DeleteRats", F_DeleteRats},
355 {"Drag", F_Drag},
356 {"DrillReport", F_DrillReport},
357 {"Element", F_Element},
358 {"ElementByName", F_ElementByName},
359 {"ElementConnections", F_ElementConnections},
360 {"ElementToBuffer", F_ElementToBuffer},
361 {"Escape", F_Escape},
362 {"Find", F_Find},
363 {"FlipElement", F_FlipElement},
364 {"FoundPins", F_FoundPins},
365 {"Grid", F_Grid},
366 {"InsertPoint", F_InsertPoint},
367 {"Layer", F_Layer},
368 {"Layout", F_Layout},
369 {"LayoutAs", F_LayoutAs},
370 {"LayoutToBuffer", F_LayoutToBuffer},
371 {"Line", F_Line},
372 {"LineSize", F_LineSize},
373 {"Lock", F_Lock},
374 {"Mirror", F_Mirror},
375 {"Move", F_Move},
376 {"NameOnPCB", F_NameOnPCB},
377 {"Netlist", F_Netlist},
378 {"NetByName", F_NetByName},
379 {"None", F_None},
380 {"Notify", F_Notify},
381 {"Object", F_Object},
382 {"ObjectByName", F_ObjectByName},
383 {"PasteBuffer", F_PasteBuffer},
384 {"PadByName", F_PadByName},
385 {"PinByName", F_PinByName},
386 {"PinOrPadName", F_PinOrPadName},
387 {"Pinout", F_Pinout},
388 {"Polygon", F_Polygon},
389 {"PolygonHole", F_PolygonHole},
390 {"PreviousPoint", F_PreviousPoint},
391 {"RatsNest", F_RatsNest},
392 {"Rectangle", F_Rectangle},
393 {"Redraw", F_Redraw},
394 {"Release", F_Release},
395 {"Remove", F_Remove},
396 {"RemoveSelected", F_RemoveSelected},
397 {"Report", F_Report},
398 {"Reset", F_Reset},
399 {"ResetLinesAndPolygons", F_ResetLinesAndPolygons},
400 {"ResetPinsViasAndPads", F_ResetPinsViasAndPads},
401 {"Restore", F_Restore},
402 {"Revert", F_Revert},
403 {"Rotate", F_Rotate},
404 {"Save", F_Save},
405 {"Selected", F_Selected},
406 {"SelectedArcs", F_SelectedArcs},
407 {"SelectedElements", F_SelectedElements},
408 {"SelectedLines", F_SelectedLines},
409 {"SelectedNames", F_SelectedNames},
410 {"SelectedObjects", F_SelectedObjects},
411 {"SelectedPins", F_SelectedPins},
412 {"SelectedPads", F_SelectedPads},
413 {"SelectedRats", F_SelectedRats},
414 {"SelectedTexts", F_SelectedTexts},
415 {"SelectedVias", F_SelectedVias},
416 {"Stroke", F_Stroke},
417 {"Text", F_Text},
418 {"TextByName", F_TextByName},
419 {"TextScale", F_TextScale},
420 {"Thermal", F_Thermal},
421 {"ToLayout", F_ToLayout},
422 {"Toggle45Degree", F_ToggleAllDirections},
423 {"ToggleClearLine", F_ToggleClearLine},
424 {"ToggleFullPoly", F_ToggleFullPoly},
425 {"ToggleGrid", F_ToggleGrid},
426 {"ToggleMask", F_ToggleMask},
427 {"ToggleName", F_ToggleName},
428 {"ToggleObject", F_ToggleObject},
429 {"ToggleRubberBandMode", F_ToggleRubberBandMode},
430 {"ToggleStartDirection", F_ToggleStartDirection},
431 {"ToggleSnapPin", F_ToggleSnapPin},
432 {"ToggleThindraw", F_ToggleThindraw},
433 {"ToggleThindrawPoly", F_ToggleThindrawPoly},
434 {"ToggleLockNames", F_ToggleLockNames},
435 {"ToggleOnlyNames", F_ToggleOnlyNames},
436 {"ToggleHideNames", F_ToggleHideNames},
437 {"ToggleCheckPlanes", F_ToggleCheckPlanes},
438 {"ToggleLocalRef", F_ToggleLocalRef},
439 {"ToggleOrthoMove", F_ToggleOrthoMove},
440 {"ToggleShowDRC", F_ToggleShowDRC},
441 {"ToggleLiveRoute", F_ToggleLiveRoute},
442 {"ToggleAutoDRC", F_ToggleAutoDRC},
443 {"ToggleUniqueNames", F_ToggleUniqueNames},
444 {"Value", F_Value},
445 {"Via", F_Via},
446 {"ViaByName", F_ViaByName},
447 {"ViaSize", F_ViaSize},
448 {"ViaDrillingHole", F_ViaDrillingHole},
449 {"Zoom", F_Zoom}
452 /* ---------------------------------------------------------------------------
453 * some local routines
455 static int GetFunctionID (String);
456 static void AdjustAttachedBox (void);
457 static void NotifyLine (void);
458 static void NotifyBlock (void);
459 static void NotifyMode (void);
460 static void ClearWarnings (void);
461 #ifdef HAVE_LIBSTROKE
462 static void FinishStroke (void);
463 extern void stroke_init (void);
464 extern void stroke_record (int x, int y);
465 extern int stroke_trans (char *s);
466 #endif
467 static void ChangeFlag (char *, char *, int, char *);
469 #define ARG(n) (argc > (n) ? argv[n] : NULL)
471 #ifdef HAVE_LIBSTROKE
474 * \brief Try to recognize the stroke sent.
476 void
477 FinishStroke (void)
479 char msg[255];
480 int type;
481 unsigned long num;
482 void *ptr1, *ptr2, *ptr3;
484 mid_stroke = false;
485 if (stroke_trans (msg))
487 num = atoi (msg);
488 switch (num)
490 case 456:
491 if (Settings.Mode == LINE_MODE)
493 SetMode (LINE_MODE);
495 break;
496 case 9874123:
497 case 74123:
498 case 987412:
499 case 8741236:
500 case 874123:
501 RotateScreenObject (StrokeBox.X1, StrokeBox.Y1, SWAP_IDENT ? 1 : 3);
502 break;
503 case 7896321:
504 case 786321:
505 case 789632:
506 case 896321:
507 RotateScreenObject (StrokeBox.X1, StrokeBox.Y1, SWAP_IDENT ? 3 : 1);
508 break;
509 case 258:
510 SetMode (LINE_MODE);
511 break;
512 case 852:
513 SetMode (ARROW_MODE);
514 break;
515 case 1478963:
516 ActionUndo ("");
517 break;
518 case 147423:
519 case 147523:
520 case 1474123:
521 Redo (true);
522 break;
523 case 148963:
524 case 147863:
525 case 147853:
526 case 145863:
527 SetMode (VIA_MODE);
528 break;
529 case 951:
530 case 9651:
531 case 9521:
532 case 9621:
533 case 9851:
534 case 9541:
535 case 96521:
536 case 96541:
537 case 98541:
538 /* XXX: FIXME: Call a zoom-extents action */
539 break;
540 case 159:
541 case 1269:
542 case 1259:
543 case 1459:
544 case 1569:
545 case 1589:
546 case 12569:
547 case 12589:
548 case 14589:
549 /* XXX: FIXME: Zoom to fit the box StrokeBox.[X1,Y1] - StrokeBox.[X2,Y2] */
550 break;
552 default:
553 Message (_("Unknown stroke %s\n"), msg);
554 break;
557 else
558 gui->beep ();
560 #endif
563 * \brief Clear warning color from pins/pads.
565 static void
566 ClearWarnings ()
568 Settings.RatWarn = false;
569 ALLPIN_LOOP (PCB->Data);
571 if (TEST_FLAG (WARNFLAG, pin))
573 CLEAR_FLAG (WARNFLAG, pin);
574 DrawPin (pin);
577 ENDALL_LOOP;
578 ALLPAD_LOOP (PCB->Data);
580 if (TEST_FLAG (WARNFLAG, pad))
582 CLEAR_FLAG (WARNFLAG, pad);
583 DrawPad (pad);
586 ENDALL_LOOP;
587 Draw ();
591 * \brief Click callback.
593 * This is called a clicktime after a mouse down, to we can distinguish
594 * between short clicks (typically: select or create something) and long
595 * clicks. Long clicks typically drag something.
597 static void
598 click_cb (hidval hv)
600 if (Note.Click)
602 notify_crosshair_change (false);
603 Note.Click = false;
604 if (Note.Moving && !gui->shift_is_pressed ())
606 Note.Buffer = Settings.BufferNumber;
607 SetBufferNumber (MAX_BUFFER - 1);
608 ClearBuffer (PASTEBUFFER);
609 AddSelectedToBuffer (PASTEBUFFER, Note.X, Note.Y, true);
610 SaveUndoSerialNumber ();
611 RemoveSelected ();
612 SaveMode ();
613 saved_mode = true;
614 SetMode (PASTEBUFFER_MODE);
616 else if (Note.Hit && !gui->shift_is_pressed ())
618 SaveMode ();
619 saved_mode = true;
620 SetMode (gui->control_is_pressed ()? COPY_MODE : MOVE_MODE);
621 Crosshair.AttachedObject.Ptr1 = Note.ptr1;
622 Crosshair.AttachedObject.Ptr2 = Note.ptr2;
623 Crosshair.AttachedObject.Ptr3 = Note.ptr3;
624 Crosshair.AttachedObject.Type = Note.Hit;
625 AttachForCopy (Note.X, Note.Y);
627 else
629 BoxType box;
631 Note.Hit = 0;
632 Note.Moving = false;
633 SaveUndoSerialNumber ();
634 box.X1 = -MAX_COORD;
635 box.Y1 = -MAX_COORD;
636 box.X2 = MAX_COORD;
637 box.Y2 = MAX_COORD;
638 /* unselect first if shift key not down */
639 if (!gui->shift_is_pressed () && SelectBlock (&box, false))
640 SetChangedFlag (true);
641 NotifyBlock ();
642 Crosshair.AttachedBox.Point1.X = Note.X;
643 Crosshair.AttachedBox.Point1.Y = Note.Y;
645 notify_crosshair_change (true);
650 * \brief This is typically called when the mouse has moved or the mouse
651 * button was released.
653 static void
654 ReleaseMode (void)
656 BoxType box;
658 if (Note.Click)
660 BoxType box;
662 box.X1 = -MAX_COORD;
663 box.Y1 = -MAX_COORD;
664 box.X2 = MAX_COORD;
665 box.Y2 = MAX_COORD;
667 Note.Click = false; /* inhibit timer action */
668 SaveUndoSerialNumber ();
669 /* unselect first if shift key not down */
670 if (!gui->shift_is_pressed ())
672 if (SelectBlock (&box, false))
673 SetChangedFlag (true);
674 if (Note.Moving)
676 Note.Moving = 0;
677 Note.Hit = 0;
678 return;
681 RestoreUndoSerialNumber ();
682 if (SelectObject ())
683 SetChangedFlag (true);
684 Note.Hit = 0;
685 Note.Moving = 0;
687 else if (Note.Moving)
689 RestoreUndoSerialNumber ();
690 NotifyMode ();
691 ClearBuffer (PASTEBUFFER);
692 SetBufferNumber (Note.Buffer);
693 Note.Moving = false;
694 Note.Hit = 0;
696 else if (Note.Hit)
698 NotifyMode ();
699 Note.Hit = 0;
701 else if (Settings.Mode == ARROW_MODE)
703 box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
704 Crosshair.AttachedBox.Point2.X);
705 box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
706 Crosshair.AttachedBox.Point2.Y);
707 box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
708 Crosshair.AttachedBox.Point2.X);
709 box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
710 Crosshair.AttachedBox.Point2.Y);
711 RestoreUndoSerialNumber ();
712 if (SelectBlock (&box, true))
713 SetChangedFlag (true);
714 else if (Bumped)
715 IncrementUndoSerialNumber ();
716 Crosshair.AttachedBox.State = STATE_FIRST;
718 if (saved_mode)
719 RestoreMode ();
720 saved_mode = false;
723 #define HSIZE 257
724 static char function_hash[HSIZE];
725 static int hash_initted = 0;
727 static int
728 hashfunc(String s)
730 int i = 0;
731 while (*s)
733 i ^= i >> 16;
734 i = (i * 13) ^ (unsigned char)tolower((int) *s);
735 s ++;
737 i = (unsigned int)i % HSIZE;
738 return i;
742 * \brief Get function ID of passed string.
744 static int
745 GetFunctionID (String Ident)
747 int i, h;
749 if (Ident == 0)
750 return -1;
752 if (!hash_initted)
754 hash_initted = 1;
755 if (HSIZE < ENTRIES (Functions) * 2)
757 fprintf(stderr, _("Error: function hash size too small (%d vs %lu at %s:%d)\n"),
758 HSIZE, (unsigned long) ENTRIES (Functions)*2, __FILE__, __LINE__);
759 exit(1);
761 if (ENTRIES (Functions) > 254)
763 /* Change 'char' to 'int' and remove this when we get to 256
764 strings to hash. */
765 fprintf(stderr, _("Error: function hash type too small (%d vs %lu at %s:%d)\n"),
766 256, (unsigned long) ENTRIES (Functions), __FILE__, __LINE__);
767 exit(1);
770 for (i=ENTRIES (Functions)-1; i>=0; i--)
772 h = hashfunc (Functions[i].Identifier);
773 while (function_hash[h])
774 h = (h + 1) % HSIZE;
775 function_hash[h] = i + 1;
779 i = hashfunc (Ident);
780 while (1)
782 /* We enforce the "hash table bigger than function table" rule,
783 so we know there will be at least one zero entry to find. */
784 if (!function_hash[i])
785 return (-1);
786 if (!strcasecmp (Ident, Functions[function_hash[i]-1].Identifier))
787 return ((int) Functions[function_hash[i]-1].ID);
788 i = (i + 1) % HSIZE;
793 * \brief Set new coordinates if in 'RECTANGLE' mode.
795 * The cursor shape is also adjusted.
797 static void
798 AdjustAttachedBox (void)
800 if (Settings.Mode == ARC_MODE)
802 Crosshair.AttachedBox.otherway = gui->shift_is_pressed ();
803 return;
805 switch (Crosshair.AttachedBox.State)
807 case STATE_SECOND: /* one corner is selected */
809 /* update coordinates */
810 Crosshair.AttachedBox.Point2.X = Crosshair.X;
811 Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
812 break;
818 * \brief Adjusts the objects which are to be created like attached
819 * lines.
821 void
822 AdjustAttachedObjects (void)
824 PointType *pnt;
825 switch (Settings.Mode)
827 /* update at least an attached block (selection) */
828 case NO_MODE:
829 case ARROW_MODE:
830 if (Crosshair.AttachedBox.State)
832 Crosshair.AttachedBox.Point2.X = Crosshair.X;
833 Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
835 break;
837 /* rectangle creation mode */
838 case RECTANGLE_MODE:
839 case ARC_MODE:
840 AdjustAttachedBox ();
841 break;
843 /* polygon creation mode */
844 case POLYGON_MODE:
845 case POLYGONHOLE_MODE:
846 AdjustAttachedLine ();
847 break;
848 /* line creation mode */
849 case LINE_MODE:
850 if (PCB->RatDraw || PCB->Clipping == 0)
851 AdjustAttachedLine ();
852 else
853 AdjustTwoLine (PCB->Clipping - 1);
854 break;
855 /* point insertion mode */
856 case INSERTPOINT_MODE:
857 pnt = AdjustInsertPoint ();
858 if (pnt)
859 InsertedPoint = *pnt;
860 break;
861 case ROTATE_MODE:
862 break;
867 * \brief Creates points of a line.
869 static void
870 NotifyLine (void)
872 int type = NO_TYPE;
873 void *ptr1, *ptr2, *ptr3;
875 if (!Marked.status || TEST_FLAG (LOCALREFFLAG, PCB))
876 SetLocalRef (Crosshair.X, Crosshair.Y, true);
877 switch (Crosshair.AttachedLine.State)
879 case STATE_FIRST: /* first point */
880 if (PCB->RatDraw && SearchScreen (Crosshair.X, Crosshair.Y,
881 PAD_TYPE | PIN_TYPE, &ptr1, &ptr1,
882 &ptr1) == NO_TYPE)
884 gui->beep ();
885 break;
887 if (TEST_FLAG (AUTODRCFLAG, PCB) && Settings.Mode == LINE_MODE)
889 type = SearchScreen (Crosshair.X, Crosshair.Y,
890 PIN_TYPE | PAD_TYPE | VIA_TYPE, &ptr1, &ptr2,
891 &ptr3);
892 LookupConnection (Crosshair.X, Crosshair.Y, true, 1, CONNECTEDFLAG, false);
893 LookupConnection (Crosshair.X, Crosshair.Y, true, 1, FOUNDFLAG, true);
895 if (type == PIN_TYPE || type == VIA_TYPE)
897 Crosshair.AttachedLine.Point1.X =
898 Crosshair.AttachedLine.Point2.X = ((PinType *) ptr2)->X;
899 Crosshair.AttachedLine.Point1.Y =
900 Crosshair.AttachedLine.Point2.Y = ((PinType *) ptr2)->Y;
902 else if (type == PAD_TYPE)
904 PadType *pad = (PadType *) ptr2;
905 double d1 = Distance (Crosshair.X, Crosshair.Y, pad->Point1.X, pad->Point1.Y);
906 double d2 = Distance (Crosshair.X, Crosshair.Y, pad->Point2.X, pad->Point2.Y);
907 if (d2 < d1)
909 Crosshair.AttachedLine.Point1 =
910 Crosshair.AttachedLine.Point2 = pad->Point2;
912 else
914 Crosshair.AttachedLine.Point1 =
915 Crosshair.AttachedLine.Point2 = pad->Point1;
918 else
920 Crosshair.AttachedLine.Point1.X =
921 Crosshair.AttachedLine.Point2.X = Crosshair.X;
922 Crosshair.AttachedLine.Point1.Y =
923 Crosshair.AttachedLine.Point2.Y = Crosshair.Y;
925 Crosshair.AttachedLine.State = STATE_SECOND;
926 break;
928 case STATE_SECOND:
929 /* fall through to third state too */
930 lastLayer = CURRENT;
931 default: /* all following points */
932 Crosshair.AttachedLine.State = STATE_THIRD;
933 break;
938 * \brief Create first or second corner of a marked block.
940 static void
941 NotifyBlock (void)
943 notify_crosshair_change (false);
944 switch (Crosshair.AttachedBox.State)
946 case STATE_FIRST: /* setup first point */
947 Crosshair.AttachedBox.Point1.X =
948 Crosshair.AttachedBox.Point2.X = Crosshair.X;
949 Crosshair.AttachedBox.Point1.Y =
950 Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
951 Crosshair.AttachedBox.State = STATE_SECOND;
952 break;
954 case STATE_SECOND: /* setup second point */
955 Crosshair.AttachedBox.State = STATE_THIRD;
956 break;
958 notify_crosshair_change (true);
963 * \brief This is called after every mode change, like mouse button pressed,
964 * mouse button released, dragging something started or a different tool
965 * selected.
967 * It does what's appropriate for the current mode setting.
968 * This can also mean creation of an object at the current crosshair location.
970 * New created objects are added to the create undo list of course.
972 static void
973 NotifyMode (void)
975 void *ptr1, *ptr2, *ptr3;
976 int type;
978 if (Settings.RatWarn)
979 ClearWarnings ();
980 switch (Settings.Mode)
982 case ARROW_MODE:
984 int test;
985 hidval hv;
987 Note.Click = true;
988 /* do something after click time */
989 gui->add_timer (click_cb, CLICK_TIME, hv);
991 /* see if we clicked on something already selected
992 * (Note.Moving) or clicked on a MOVE_TYPE
993 * (Note.Hit)
995 for (test = (SELECT_TYPES | MOVE_TYPES) & ~RATLINE_TYPE;
996 test; test &= ~type)
998 type = SearchScreen (Note.X, Note.Y, test, &ptr1, &ptr2, &ptr3);
999 if (!Note.Hit && (type & MOVE_TYPES) &&
1000 !TEST_FLAG (LOCKFLAG, (PinType *) ptr2))
1002 Note.Hit = type;
1003 Note.ptr1 = ptr1;
1004 Note.ptr2 = ptr2;
1005 Note.ptr3 = ptr3;
1007 if (!Note.Moving && (type & SELECT_TYPES) &&
1008 TEST_FLAG (SELECTEDFLAG, (PinType *) ptr2))
1009 Note.Moving = true;
1010 if ((Note.Hit && Note.Moving) || type == NO_TYPE)
1011 break;
1013 break;
1016 case VIA_MODE:
1018 PinType *via;
1020 if (!PCB->ViaOn)
1022 Message (_("You must turn via visibility on before\n"
1023 "you can place vias\n"));
1024 break;
1026 if ((via = CreateNewVia (PCB->Data, Note.X, Note.Y,
1027 Settings.ViaThickness, 2 * Settings.Keepaway,
1028 0, Settings.ViaDrillingHole, NULL,
1029 NoFlags ())) != NULL)
1031 AddObjectToCreateUndoList (VIA_TYPE, via, via, via);
1032 if (gui->shift_is_pressed ())
1033 ChangeObjectThermal (VIA_TYPE, via, via, via, PCB->ThermStyle);
1034 IncrementUndoSerialNumber ();
1035 DrawVia (via);
1036 Draw ();
1038 break;
1041 case ARC_MODE:
1043 switch (Crosshair.AttachedBox.State)
1045 case STATE_FIRST:
1046 Crosshair.AttachedBox.Point1.X =
1047 Crosshair.AttachedBox.Point2.X = Note.X;
1048 Crosshair.AttachedBox.Point1.Y =
1049 Crosshair.AttachedBox.Point2.Y = Note.Y;
1050 Crosshair.AttachedBox.State = STATE_SECOND;
1051 break;
1053 case STATE_SECOND:
1054 case STATE_THIRD:
1056 ArcType *arc;
1057 Coord wx, wy;
1058 Angle sa, dir;
1060 wx = Note.X - Crosshair.AttachedBox.Point1.X;
1061 wy = Note.Y - Crosshair.AttachedBox.Point1.Y;
1062 if (XOR (Crosshair.AttachedBox.otherway, abs (wy) > abs (wx)))
1064 Crosshair.AttachedBox.Point2.X =
1065 Crosshair.AttachedBox.Point1.X + abs (wy) * SGNZ (wx);
1066 sa = (wx >= 0) ? 0 : 180;
1067 #ifdef ARC45
1068 if (abs (wy) / 2 >= abs (wx))
1069 dir = (SGNZ (wx) == SGNZ (wy)) ? 45 : -45;
1070 else
1071 #endif
1072 dir = (SGNZ (wx) == SGNZ (wy)) ? 90 : -90;
1074 else
1076 Crosshair.AttachedBox.Point2.Y =
1077 Crosshair.AttachedBox.Point1.Y + abs (wx) * SGNZ (wy);
1078 sa = (wy >= 0) ? -90 : 90;
1079 #ifdef ARC45
1080 if (abs (wx) / 2 >= abs (wy))
1081 dir = (SGNZ (wx) == SGNZ (wy)) ? -45 : 45;
1082 else
1083 #endif
1084 dir = (SGNZ (wx) == SGNZ (wy)) ? -90 : 90;
1085 wy = wx;
1087 if (abs (wy) > 0 && (arc = CreateNewArcOnLayer (CURRENT,
1088 Crosshair.
1089 AttachedBox.
1090 Point2.X,
1091 Crosshair.
1092 AttachedBox.
1093 Point2.Y,
1094 abs (wy),
1095 abs (wy),
1097 dir,
1098 Settings.
1099 LineThickness,
1100 2 * Settings.
1101 Keepaway,
1102 MakeFlags
1103 (TEST_FLAG
1104 (CLEARNEWFLAG,
1105 PCB) ?
1106 CLEARLINEFLAG :
1107 0))))
1109 BoxType *bx;
1111 bx = GetArcEnds (arc);
1112 Crosshair.AttachedBox.Point1.X =
1113 Crosshair.AttachedBox.Point2.X = bx->X2;
1114 Crosshair.AttachedBox.Point1.Y =
1115 Crosshair.AttachedBox.Point2.Y = bx->Y2;
1116 AddObjectToCreateUndoList (ARC_TYPE, CURRENT, arc, arc);
1117 IncrementUndoSerialNumber ();
1118 addedLines++;
1119 DrawArc (CURRENT, arc);
1120 Draw ();
1121 Crosshair.AttachedBox.State = STATE_THIRD;
1123 break;
1126 break;
1128 case LOCK_MODE:
1130 type = SearchScreen (Note.X, Note.Y, LOCK_TYPES, &ptr1, &ptr2, &ptr3);
1131 if (type == ELEMENT_TYPE)
1133 ElementType *element = (ElementType *) ptr2;
1135 TOGGLE_FLAG (LOCKFLAG, element);
1136 PIN_LOOP (element);
1138 TOGGLE_FLAG (LOCKFLAG, pin);
1139 CLEAR_FLAG (SELECTEDFLAG, pin);
1141 END_LOOP;
1142 PAD_LOOP (element);
1144 TOGGLE_FLAG (LOCKFLAG, pad);
1145 CLEAR_FLAG (SELECTEDFLAG, pad);
1147 END_LOOP;
1148 CLEAR_FLAG (SELECTEDFLAG, element);
1149 /* always re-draw it since I'm too lazy
1150 * to tell if a selected flag changed
1152 DrawElement (element);
1153 Draw ();
1154 SetChangedFlag (true);
1155 hid_actionl ("Report", "Object", NULL);
1157 else if (type != NO_TYPE)
1159 TextType *thing = (TextType *) ptr3;
1160 TOGGLE_FLAG (LOCKFLAG, thing);
1161 if (TEST_FLAG (LOCKFLAG, thing)
1162 && TEST_FLAG (SELECTEDFLAG, thing))
1164 /* this is not un-doable since LOCK isn't */
1165 CLEAR_FLAG (SELECTEDFLAG, thing);
1166 DrawObject (type, ptr1, ptr2);
1167 Draw ();
1169 SetChangedFlag (true);
1170 hid_actionl ("Report", "Object", NULL);
1172 break;
1174 case THERMAL_MODE:
1176 if (((type
1178 SearchScreen (Note.X, Note.Y, PIN_TYPES, &ptr1, &ptr2,
1179 &ptr3)) != NO_TYPE)
1180 && !TEST_FLAG (HOLEFLAG, (PinType *) ptr3))
1182 if (gui->shift_is_pressed ())
1184 int tstyle = GET_THERM (INDEXOFCURRENT, (PinType *) ptr3);
1185 tstyle++;
1186 if (tstyle > 5)
1187 tstyle = 1;
1188 ChangeObjectThermal (type, ptr1, ptr2, ptr3, tstyle);
1190 else if (GET_THERM (INDEXOFCURRENT, (PinType *) ptr3))
1191 ChangeObjectThermal (type, ptr1, ptr2, ptr3, 0);
1192 else
1193 ChangeObjectThermal (type, ptr1, ptr2, ptr3, PCB->ThermStyle);
1195 break;
1198 case LINE_MODE:
1199 /* do update of position */
1200 NotifyLine ();
1201 if (Crosshair.AttachedLine.State != STATE_THIRD)
1202 break;
1204 /* Remove anchor if clicking on start point;
1205 * this means we can't paint 0 length lines
1206 * which could be used for square SMD pads.
1207 * Instead use a very small delta, or change
1208 * the file after saving.
1210 if (Crosshair.X == Crosshair.AttachedLine.Point1.X
1211 && Crosshair.Y == Crosshair.AttachedLine.Point1.Y)
1213 SetMode (LINE_MODE);
1214 break;
1217 if (PCB->RatDraw)
1219 RatType *line;
1220 if ((line = AddNet ()))
1222 addedLines++;
1223 AddObjectToCreateUndoList (RATLINE_TYPE, line, line, line);
1224 IncrementUndoSerialNumber ();
1225 DrawRat (line);
1226 Crosshair.AttachedLine.Point1.X =
1227 Crosshair.AttachedLine.Point2.X;
1228 Crosshair.AttachedLine.Point1.Y =
1229 Crosshair.AttachedLine.Point2.Y;
1230 Draw ();
1232 break;
1234 else
1235 /* create line if both ends are determined && length != 0 */
1237 LineType *line;
1238 int line_flags = 0;
1240 if (TEST_FLAG (AUTODRCFLAG, PCB) && !TEST_SILK_LAYER (CURRENT))
1241 line_flags |= CONNECTEDFLAG | FOUNDFLAG;
1243 if (TEST_FLAG (CLEARNEWFLAG, PCB))
1244 line_flags |= CLEARLINEFLAG;
1246 if (PCB->Clipping
1247 && Crosshair.AttachedLine.Point1.X ==
1248 Crosshair.AttachedLine.Point2.X
1249 && Crosshair.AttachedLine.Point1.Y ==
1250 Crosshair.AttachedLine.Point2.Y
1251 && (Crosshair.AttachedLine.Point2.X != Note.X
1252 || Crosshair.AttachedLine.Point2.Y != Note.Y))
1254 /* We will only need to paint the second line segment.
1255 Since we only check for vias on the first segment,
1256 swap them so the non-empty segment is the first segment. */
1257 Crosshair.AttachedLine.Point2.X = Note.X;
1258 Crosshair.AttachedLine.Point2.Y = Note.Y;
1261 if ((Crosshair.AttachedLine.Point1.X !=
1262 Crosshair.AttachedLine.Point2.X
1263 || Crosshair.AttachedLine.Point1.Y !=
1264 Crosshair.AttachedLine.Point2.Y))
1266 PinType *via;
1268 if ((line =
1269 CreateDrawnLineOnLayer (CURRENT,
1270 Crosshair.AttachedLine.Point1.X,
1271 Crosshair.AttachedLine.Point1.Y,
1272 Crosshair.AttachedLine.Point2.X,
1273 Crosshair.AttachedLine.Point2.Y,
1274 Settings.LineThickness,
1275 2 * Settings.Keepaway,
1276 MakeFlags (line_flags))) != NULL)
1279 addedLines++;
1280 AddObjectToCreateUndoList (LINE_TYPE, CURRENT, line, line);
1281 DrawLine (CURRENT, line);
1283 /* place a via if vias are visible, the layer is
1284 in a new group since the last line and there
1285 isn't a pin already here */
1286 if (PCB->ViaOn && GetLayerGroupNumberByPointer (CURRENT) !=
1287 GetLayerGroupNumberByPointer (lastLayer) &&
1288 SearchObjectByLocation (PIN_TYPES, &ptr1, &ptr2, &ptr3,
1289 Crosshair.AttachedLine.Point1.X,
1290 Crosshair.AttachedLine.Point1.Y,
1291 Settings.ViaThickness / 2) ==
1292 NO_TYPE
1293 && (via =
1294 CreateNewVia (PCB->Data,
1295 Crosshair.AttachedLine.Point1.X,
1296 Crosshair.AttachedLine.Point1.Y,
1297 Settings.ViaThickness,
1298 2 * Settings.Keepaway, 0,
1299 Settings.ViaDrillingHole, NULL,
1300 NoFlags ())) != NULL)
1302 AddObjectToCreateUndoList (VIA_TYPE, via, via, via);
1303 DrawVia (via);
1305 /* copy the coordinates */
1306 Crosshair.AttachedLine.Point1.X =
1307 Crosshair.AttachedLine.Point2.X;
1308 Crosshair.AttachedLine.Point1.Y =
1309 Crosshair.AttachedLine.Point2.Y;
1310 IncrementUndoSerialNumber ();
1311 lastLayer = CURRENT;
1313 if (PCB->Clipping && (Note.X != Crosshair.AttachedLine.Point2.X
1314 || Note.Y !=
1315 Crosshair.AttachedLine.Point2.Y))
1317 if ((line =
1318 CreateDrawnLineOnLayer (CURRENT,
1319 Crosshair.AttachedLine.Point2.X,
1320 Crosshair.AttachedLine.Point2.Y,
1321 Note.X, Note.Y,
1322 Settings.LineThickness,
1323 2 * Settings.Keepaway,
1324 MakeFlags (line_flags))) != NULL)
1326 addedLines++;
1327 AddObjectToCreateUndoList (LINE_TYPE, CURRENT, line, line);
1328 IncrementUndoSerialNumber ();
1329 DrawLine (CURRENT, line);
1331 /* move to new start point */
1332 Crosshair.AttachedLine.Point1.X = Note.X;
1333 Crosshair.AttachedLine.Point1.Y = Note.Y;
1334 Crosshair.AttachedLine.Point2.X = Note.X;
1335 Crosshair.AttachedLine.Point2.Y = Note.Y;
1336 if (TEST_FLAG (SWAPSTARTDIRFLAG, PCB))
1338 PCB->Clipping ^= 3;
1341 if (TEST_FLAG (AUTODRCFLAG, PCB) && !TEST_SILK_LAYER (CURRENT))
1342 LookupConnection (Note.X, Note.Y, true, 1, CONNECTEDFLAG, false);
1343 Draw ();
1345 break;
1347 case RECTANGLE_MODE:
1348 /* do update of position */
1349 NotifyBlock ();
1351 /* create rectangle if both corners are determined
1352 * and width, height are != 0
1354 if (Crosshair.AttachedBox.State == STATE_THIRD &&
1355 Crosshair.AttachedBox.Point1.X != Crosshair.AttachedBox.Point2.X &&
1356 Crosshair.AttachedBox.Point1.Y != Crosshair.AttachedBox.Point2.Y)
1358 PolygonType *polygon;
1360 int flags = CLEARPOLYFLAG;
1361 if (TEST_FLAG (NEWFULLPOLYFLAG, PCB))
1362 flags |= FULLPOLYFLAG;
1363 if ((polygon = CreateNewPolygonFromRectangle (CURRENT,
1364 Crosshair.
1365 AttachedBox.Point1.X,
1366 Crosshair.
1367 AttachedBox.Point1.Y,
1368 Crosshair.
1369 AttachedBox.Point2.X,
1370 Crosshair.
1371 AttachedBox.Point2.Y,
1372 MakeFlags
1373 (flags))) !=
1374 NULL)
1376 AddObjectToCreateUndoList (POLYGON_TYPE, CURRENT,
1377 polygon, polygon);
1378 IncrementUndoSerialNumber ();
1379 DrawPolygon (CURRENT, polygon);
1380 Draw ();
1383 /* reset state to 'first corner' */
1384 Crosshair.AttachedBox.State = STATE_FIRST;
1386 break;
1388 case TEXT_MODE:
1390 char *string;
1392 if ((string = gui->prompt_for (_("Enter text:"), "")) != NULL)
1394 if (strlen(string) > 0)
1396 TextType *text;
1397 int flag = CLEARLINEFLAG;
1399 if (GetLayerGroupNumberByNumber (INDEXOFCURRENT) ==
1400 GetLayerGroupNumberBySide (BOTTOM_SIDE))
1401 flag |= ONSOLDERFLAG;
1402 if ((text = CreateNewText (CURRENT, &PCB->Font, Note.X,
1403 Note.Y, 0, Settings.TextScale,
1404 string, MakeFlags (flag))) != NULL)
1406 AddObjectToCreateUndoList (TEXT_TYPE, CURRENT, text, text);
1407 IncrementUndoSerialNumber ();
1408 DrawText (CURRENT, text);
1409 Draw ();
1412 free (string);
1414 break;
1417 case POLYGON_MODE:
1419 PointType *points = Crosshair.AttachedPolygon.Points;
1420 Cardinal n = Crosshair.AttachedPolygon.PointN;
1422 /* do update of position; use the 'LINE_MODE' mechanism */
1423 NotifyLine ();
1425 /* check if this is the last point of a polygon */
1426 if (n >= 3 &&
1427 points->X == Crosshair.AttachedLine.Point2.X &&
1428 points->Y == Crosshair.AttachedLine.Point2.Y)
1430 CopyAttachedPolygonToLayer ();
1431 Draw ();
1432 break;
1435 /* create new point if it's the first one or if it's
1436 * different to the last one
1438 if (!n ||
1439 points[n - 1].X != Crosshair.AttachedLine.Point2.X ||
1440 points[n - 1].Y != Crosshair.AttachedLine.Point2.Y)
1442 CreateNewPointInPolygon (&Crosshair.AttachedPolygon,
1443 Crosshair.AttachedLine.Point2.X,
1444 Crosshair.AttachedLine.Point2.Y);
1446 /* copy the coordinates */
1447 Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X;
1448 Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y;
1450 break;
1453 case POLYGONHOLE_MODE:
1455 switch (Crosshair.AttachedObject.State)
1457 /* first notify, lookup object */
1458 case STATE_FIRST:
1459 Crosshair.AttachedObject.Type =
1460 SearchScreen (Note.X, Note.Y, POLYGON_TYPE,
1461 &Crosshair.AttachedObject.Ptr1,
1462 &Crosshair.AttachedObject.Ptr2,
1463 &Crosshair.AttachedObject.Ptr3);
1465 if (Crosshair.AttachedObject.Type != NO_TYPE)
1467 if (TEST_FLAG (LOCKFLAG, (PolygonType *)
1468 Crosshair.AttachedObject.Ptr2))
1470 Message (_("Sorry, the object is locked\n"));
1471 Crosshair.AttachedObject.Type = NO_TYPE;
1472 break;
1474 else
1475 Crosshair.AttachedObject.State = STATE_SECOND;
1477 break;
1479 /* second notify, insert new point into object */
1480 case STATE_SECOND:
1482 PointType *points = Crosshair.AttachedPolygon.Points;
1483 Cardinal n = Crosshair.AttachedPolygon.PointN;
1484 POLYAREA *original, *new_hole, *result;
1485 FlagType Flags;
1487 /* do update of position; use the 'LINE_MODE' mechanism */
1488 NotifyLine ();
1490 /* check if this is the last point of a polygon */
1491 if (n >= 3 &&
1492 points->X == Crosshair.AttachedLine.Point2.X &&
1493 points->Y == Crosshair.AttachedLine.Point2.Y)
1495 /* Create POLYAREAs from the original polygon
1496 * and the new hole polygon */
1497 original = PolygonToPoly ((PolygonType *)Crosshair.AttachedObject.Ptr2);
1498 new_hole = PolygonToPoly (&Crosshair.AttachedPolygon);
1500 /* Subtract the hole from the original polygon shape */
1501 poly_Boolean_free (original, new_hole, &result, PBO_SUB);
1503 /* Convert the resulting polygon(s) into a new set of nodes
1504 * and place them on the page. Delete the original polygon.
1506 SaveUndoSerialNumber ();
1507 Flags = ((PolygonType *)Crosshair.AttachedObject.Ptr2)->Flags;
1508 PolyToPolygonsOnLayer (PCB->Data, (LayerType *)Crosshair.AttachedObject.Ptr1,
1509 result, Flags);
1510 RemoveObject (POLYGON_TYPE,
1511 Crosshair.AttachedObject.Ptr1,
1512 Crosshair.AttachedObject.Ptr2,
1513 Crosshair.AttachedObject.Ptr3);
1514 RestoreUndoSerialNumber ();
1515 IncrementUndoSerialNumber ();
1516 Draw ();
1518 /* reset state of attached line */
1519 memset (&Crosshair.AttachedPolygon, 0, sizeof (PolygonType));
1520 Crosshair.AttachedLine.State = STATE_FIRST;
1521 addedLines = 0;
1523 break;
1526 /* create new point if it's the first one or if it's
1527 * different to the last one
1529 if (!n ||
1530 points[n - 1].X != Crosshair.AttachedLine.Point2.X ||
1531 points[n - 1].Y != Crosshair.AttachedLine.Point2.Y)
1533 CreateNewPointInPolygon (&Crosshair.AttachedPolygon,
1534 Crosshair.AttachedLine.Point2.X,
1535 Crosshair.AttachedLine.Point2.Y);
1537 /* copy the coordinates */
1538 Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X;
1539 Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y;
1541 break;
1545 break;
1548 case PASTEBUFFER_MODE:
1550 TextType estr[MAX_ELEMENTNAMES];
1551 ElementType *e = 0;
1553 if (gui->shift_is_pressed ())
1555 int type =
1556 SearchScreen (Note.X, Note.Y, ELEMENT_TYPE, &ptr1, &ptr2,
1557 &ptr3);
1558 if (type == ELEMENT_TYPE)
1560 e = (ElementType *) ptr1;
1561 if (e)
1563 int i;
1565 memcpy (estr, e->Name,
1566 MAX_ELEMENTNAMES * sizeof (TextType));
1567 for (i = 0; i < MAX_ELEMENTNAMES; ++i)
1568 estr[i].TextString = estr[i].TextString ? strdup(estr[i].TextString) : NULL;
1569 RemoveElement (e);
1573 if (CopyPastebufferToLayout (Note.X, Note.Y))
1574 SetChangedFlag (true);
1575 if (e)
1577 int type =
1578 SearchScreen (Note.X, Note.Y, ELEMENT_TYPE, &ptr1, &ptr2,
1579 &ptr3);
1580 if (type == ELEMENT_TYPE && ptr1)
1582 int i, save_n;
1583 e = (ElementType *) ptr1;
1585 save_n = NAME_INDEX (PCB);
1587 for (i = 0; i < MAX_ELEMENTNAMES; i++)
1589 if (i == save_n)
1590 EraseElementName (e);
1591 r_delete_entry (PCB->Data->name_tree[i],
1592 (BoxType *) & (e->Name[i]));
1593 memcpy (&(e->Name[i]), &(estr[i]), sizeof (TextType));
1594 e->Name[i].Element = e;
1595 SetTextBoundingBox (&PCB->Font, &(e->Name[i]));
1596 r_insert_entry (PCB->Data->name_tree[i],
1597 (BoxType *) & (e->Name[i]), 0);
1598 if (i == save_n)
1599 DrawElementName (e);
1603 break;
1606 case REMOVE_MODE:
1607 if ((type =
1608 SearchScreen (Note.X, Note.Y, REMOVE_TYPES, &ptr1, &ptr2,
1609 &ptr3)) != NO_TYPE)
1611 if (TEST_FLAG (LOCKFLAG, (LineType *) ptr2))
1613 Message (_("Sorry, the object is locked\n"));
1614 break;
1616 if (type == ELEMENT_TYPE)
1618 RubberbandType *ptr;
1619 int i;
1621 Crosshair.AttachedObject.RubberbandN = 0;
1622 LookupRatLines (type, ptr1, ptr2, ptr3);
1623 ptr = Crosshair.AttachedObject.Rubberband;
1624 for (i = 0; i < Crosshair.AttachedObject.RubberbandN; i++)
1626 if (PCB->RatOn)
1627 EraseRat ((RatType *) ptr->Line);
1628 if (TEST_FLAG (RUBBERENDFLAG, ptr->Line))
1629 MoveObjectToRemoveUndoList (RATLINE_TYPE,
1630 ptr->Line, ptr->Line,
1631 ptr->Line);
1632 else
1633 TOGGLE_FLAG (RUBBERENDFLAG, ptr->Line); /* only remove line once */
1634 ptr++;
1637 RemoveObject (type, ptr1, ptr2, ptr3);
1638 IncrementUndoSerialNumber ();
1639 SetChangedFlag (true);
1641 break;
1643 case ROTATE_MODE:
1644 RotateScreenObject (Note.X, Note.Y,
1645 gui->shift_is_pressed ()? (SWAP_IDENT ?
1646 1 : 3)
1647 : (SWAP_IDENT ? 3 : 1));
1648 break;
1650 /* both are almost the same */
1651 case COPY_MODE:
1652 case MOVE_MODE:
1653 switch (Crosshair.AttachedObject.State)
1655 /* first notify, lookup object */
1656 case STATE_FIRST:
1658 int types = (Settings.Mode == COPY_MODE) ?
1659 COPY_TYPES : MOVE_TYPES;
1661 Crosshair.AttachedObject.Type =
1662 SearchScreen (Note.X, Note.Y, types,
1663 &Crosshair.AttachedObject.Ptr1,
1664 &Crosshair.AttachedObject.Ptr2,
1665 &Crosshair.AttachedObject.Ptr3);
1666 if (Crosshair.AttachedObject.Type != NO_TYPE)
1668 if (Settings.Mode == MOVE_MODE &&
1669 TEST_FLAG (LOCKFLAG, (PinType *)
1670 Crosshair.AttachedObject.Ptr2))
1672 Message (_("Sorry, the object is locked\n"));
1673 Crosshair.AttachedObject.Type = NO_TYPE;
1675 else
1676 AttachForCopy (Note.X, Note.Y);
1678 break;
1681 /* second notify, move or copy object */
1682 case STATE_SECOND:
1683 if (Settings.Mode == COPY_MODE)
1684 CopyObject (Crosshair.AttachedObject.Type,
1685 Crosshair.AttachedObject.Ptr1,
1686 Crosshair.AttachedObject.Ptr2,
1687 Crosshair.AttachedObject.Ptr3,
1688 Note.X - Crosshair.AttachedObject.X,
1689 Note.Y - Crosshair.AttachedObject.Y);
1690 else
1692 MoveObjectAndRubberband (Crosshair.AttachedObject.Type,
1693 Crosshair.AttachedObject.Ptr1,
1694 Crosshair.AttachedObject.Ptr2,
1695 Crosshair.AttachedObject.Ptr3,
1696 Note.X - Crosshair.AttachedObject.X,
1697 Note.Y - Crosshair.AttachedObject.Y);
1698 SetLocalRef (0, 0, false);
1700 SetChangedFlag (true);
1702 /* reset identifiers */
1703 Crosshair.AttachedObject.Type = NO_TYPE;
1704 Crosshair.AttachedObject.State = STATE_FIRST;
1705 break;
1707 break;
1709 /* insert a point into a polygon/line/... */
1710 case INSERTPOINT_MODE:
1711 switch (Crosshair.AttachedObject.State)
1713 /* first notify, lookup object */
1714 case STATE_FIRST:
1715 Crosshair.AttachedObject.Type =
1716 SearchScreen (Note.X, Note.Y, INSERT_TYPES,
1717 &Crosshair.AttachedObject.Ptr1,
1718 &Crosshair.AttachedObject.Ptr2,
1719 &Crosshair.AttachedObject.Ptr3);
1721 if (Crosshair.AttachedObject.Type != NO_TYPE)
1723 if (TEST_FLAG (LOCKFLAG, (PolygonType *)
1724 Crosshair.AttachedObject.Ptr2))
1726 Message (_("Sorry, the object is locked\n"));
1727 Crosshair.AttachedObject.Type = NO_TYPE;
1728 break;
1730 else
1732 /* get starting point of nearest segment */
1733 if (Crosshair.AttachedObject.Type == POLYGON_TYPE)
1735 fake.poly =
1736 (PolygonType *) Crosshair.AttachedObject.Ptr2;
1737 polyIndex =
1738 GetLowestDistancePolygonPoint (fake.poly, Note.X,
1739 Note.Y);
1740 fake.line.Point1 = fake.poly->Points[polyIndex];
1741 fake.line.Point2 = fake.poly->Points[
1742 prev_contour_point (fake.poly, polyIndex)];
1743 Crosshair.AttachedObject.Ptr2 = &fake.line;
1746 Crosshair.AttachedObject.State = STATE_SECOND;
1747 InsertedPoint = *AdjustInsertPoint ();
1750 break;
1752 /* second notify, insert new point into object */
1753 case STATE_SECOND:
1754 if (Crosshair.AttachedObject.Type == POLYGON_TYPE)
1755 InsertPointIntoObject (POLYGON_TYPE,
1756 Crosshair.AttachedObject.Ptr1, fake.poly,
1757 &polyIndex,
1758 InsertedPoint.X, InsertedPoint.Y, false, false);
1759 else
1760 InsertPointIntoObject (Crosshair.AttachedObject.Type,
1761 Crosshair.AttachedObject.Ptr1,
1762 Crosshair.AttachedObject.Ptr2,
1763 &polyIndex,
1764 InsertedPoint.X, InsertedPoint.Y, false, false);
1765 SetChangedFlag (true);
1767 /* reset identifiers */
1768 Crosshair.AttachedObject.Type = NO_TYPE;
1769 Crosshair.AttachedObject.State = STATE_FIRST;
1770 break;
1772 break;
1777 /* --------------------------------------------------------------------------- */
1779 static const char atomic_syntax[] = N_("Atomic(Save|Restore|Close|Block)");
1781 static const char atomic_help[] = N_("Save or restore the undo serial number.");
1783 /* %start-doc actions Atomic
1785 This action allows making multiple-action bindings into an atomic
1786 operation that will be undone by a single Undo command. For example,
1787 to optimize rat lines, you'd delete the rats and re-add them. To
1788 group these into a single undo, you'd want the deletions and the
1789 additions to have the same undo serial number. So, you @code{Save},
1790 delete the rats, @code{Restore}, add the rats - using the same serial
1791 number as the deletes, then @code{Block}, which checks to see if the
1792 deletions or additions actually did anything. If not, the serial
1793 number is set to the saved number, as there's nothing to undo. If
1794 something did happen, the serial number is incremented so that these
1795 actions are counted as a single undo step.
1797 @table @code
1799 @item Save
1800 Saves the undo serial number.
1802 @item Restore
1803 Returns it to the last saved number.
1805 @item Close
1806 Sets it to 1 greater than the last save.
1808 @item Block
1809 Does a Restore if there was nothing to undo, else does a Close.
1811 @end table
1813 %end-doc */
1815 static int
1816 ActionAtomic (int argc, char **argv, Coord x, Coord y)
1818 if (argc != 1)
1819 AFAIL (atomic);
1821 switch (GetFunctionID (argv[0]))
1823 case F_Save:
1824 SaveUndoSerialNumber ();
1825 break;
1826 case F_Restore:
1827 RestoreUndoSerialNumber ();
1828 break;
1829 case F_Close:
1830 RestoreUndoSerialNumber ();
1831 IncrementUndoSerialNumber ();
1832 break;
1833 case F_Block:
1834 RestoreUndoSerialNumber ();
1835 if (Bumped)
1836 IncrementUndoSerialNumber ();
1837 break;
1839 return 0;
1842 /* -------------------------------------------------------------------------- */
1844 static const char drc_syntax[] = N_("DRC()");
1846 static const char drc_help[] = N_("Invoke the DRC check.");
1848 /* %start-doc actions DRC
1850 Note that the design rule check uses the current board rule settings,
1851 not the current style settings.
1853 %end-doc */
1855 static int
1856 ActionDRCheck (int argc, char **argv, Coord x, Coord y)
1858 int count;
1860 if (gui->drc_gui == NULL || gui->drc_gui->log_drc_overview)
1862 Message (_("%m+Rules are minspace %$mS, minoverlap %$mS "
1863 "minwidth %$mS, minsilk %$mS\n"
1864 "min drill %$mS, min annular ring %$mS\n"),
1865 Settings.grid_unit->allow,
1866 PCB->Bloat, PCB->Shrink,
1867 PCB->minWid, PCB->minSlk,
1868 PCB->minDrill, PCB->minRing);
1870 count = DRCAll ();
1871 if (gui->drc_gui == NULL || gui->drc_gui->log_drc_overview)
1873 if (count == 0)
1874 Message (_("No DRC problems found.\n"));
1875 else if (count > 0)
1876 Message (_("Found %d design rule errors.\n"), count);
1877 else
1878 Message (_("Aborted DRC after %d design rule errors.\n"), -count);
1880 return 0;
1883 /* -------------------------------------------------------------------------- */
1885 static const char dumplibrary_syntax[] = N_("DumpLibrary()");
1887 static const char dumplibrary_help[] =
1888 N_("Display the entire contents of the libraries.");
1890 /* %start-doc actions DumpLibrary
1893 %end-doc */
1895 static int
1896 ActionDumpLibrary (int argc, char **argv, Coord x, Coord y)
1898 int i, j;
1900 printf ("**** Do not count on this format. It will change ****\n\n");
1901 printf ("MenuN = %d\n", (int) Library.MenuN);
1902 printf ("MenuMax = %d\n", (int) Library.MenuMax);
1903 for (i = 0; i < Library.MenuN; i++)
1905 printf ("Library #%d:\n", i);
1906 printf (" EntryN = %d\n", (int) Library.Menu[i].EntryN);
1907 printf (" EntryMax = %d\n", (int) Library.Menu[i].EntryMax);
1908 printf (" Name = \"%s\"\n", UNKNOWN (Library.Menu[i].Name));
1909 printf (" directory = \"%s\"\n",
1910 UNKNOWN (Library.Menu[i].directory));
1911 printf (" Style = \"%s\"\n", UNKNOWN (Library.Menu[i].Style));
1912 printf (" flag = %d\n", Library.Menu[i].flag);
1914 for (j = 0; j < Library.Menu[i].EntryN; j++)
1916 printf (" #%4d: ", j);
1917 if (Library.Menu[i].Entry[j].Template == (char *) -1)
1919 printf ("newlib: \"%s\"\n",
1920 UNKNOWN (Library.Menu[i].Entry[j].ListEntry));
1922 else
1924 printf ("\"%s\", \"%s\", \"%s\", \"%s\", \"%s\"\n",
1925 UNKNOWN (Library.Menu[i].Entry[j].ListEntry),
1926 UNKNOWN (Library.Menu[i].Entry[j].Template),
1927 UNKNOWN (Library.Menu[i].Entry[j].Package),
1928 UNKNOWN (Library.Menu[i].Entry[j].Value),
1929 UNKNOWN (Library.Menu[i].Entry[j].Description));
1934 return 0;
1937 /* -------------------------------------------------------------------------- */
1939 static const char flip_syntax[] = N_("Flip(Object|Selected|SelectedElements)");
1941 static const char flip_help[] =
1942 N_("Flip an element to the opposite side of the board.");
1944 /* %start-doc actions Flip
1946 Note that the location of the element will be symmetric about the
1947 cursor location; i.e. if the part you are pointing at will still be at
1948 the same spot once the element is on the other side. When flipping
1949 multiple elements, this retains their positions relative to each
1950 other, not their absolute positions on the board.
1952 %end-doc */
1954 static int
1955 ActionFlip (int argc, char **argv, Coord x, Coord y)
1957 char *function = ARG (0);
1958 ElementType *element;
1959 void *ptrtmp;
1960 int err = 0;
1962 if (function)
1964 switch (GetFunctionID (function))
1966 case F_Object:
1967 if ((SearchScreen (x, y, ELEMENT_TYPE,
1968 &ptrtmp, &ptrtmp, &ptrtmp)) != NO_TYPE)
1970 element = (ElementType *) ptrtmp;
1971 ChangeElementSide (element, 2 * Crosshair.Y - PCB->MaxHeight);
1972 IncrementUndoSerialNumber ();
1973 Draw ();
1975 break;
1976 case F_Selected:
1977 case F_SelectedElements:
1978 ChangeSelectedElementSide ();
1979 break;
1980 default:
1981 err = 1;
1982 break;
1984 if (!err)
1985 return 0;
1988 AFAIL (flip);
1991 /* -------------------------------------------------------------------------- */
1993 static const char message_syntax[] = N_("Message(message)");
1995 static const char message_help[] = N_("Writes a message to the log window.");
1997 /* %start-doc actions Message
1999 This action displays a message to the log window. This action is primarily
2000 provided for use by other programs which may interface with PCB. If
2001 multiple arguments are given, each one is sent to the log window
2002 followed by a newline.
2004 %end-doc */
2006 static int
2007 ActionMessage (int argc, char **argv, Coord x, Coord y)
2009 int i;
2011 if (argc < 1)
2012 AFAIL (message);
2014 for (i = 0; i < argc; i++)
2016 Message (argv[i]);
2017 Message ("\n");
2020 return 0;
2024 /* -------------------------------------------------------------------------- */
2026 static const char setthermal_syntax[] =
2027 "SetThermal(Object|SelectedPins|SelectedVias|Selected, Style)";
2029 static const char setthermal_help[] =
2030 N_("Set the thermal (on the current layer) of pins or vias to the given style.\n"
2031 "Style = 0 means no thermal.\n"
2032 "Style = 1 has diagonal fingers with sharp edges.\n"
2033 "Style = 2 has horizontal and vertical fingers with sharp edges.\n"
2034 "Style = 3 is a solid connection to the plane.\n"
2035 "Style = 4 has diagonal fingers with rounded edges.\n"
2036 "Style = 5 has horizontal and vertical fingers with rounded edges.\n");
2038 /* %start-doc actions SetThermal
2040 This changes how/whether pins or vias connect to any rectangle or polygon
2041 on the current layer. The first argument can specify one object, or all
2042 selected pins, or all selected vias, or all selected pins and vias.
2043 The second argument specifies the style of connection.
2044 There are 5 possibilities:
2045 0 - no connection,
2046 1 - 45 degree fingers with sharp edges,
2047 2 - horizontal & vertical fingers with sharp edges,
2048 3 - solid connection,
2049 4 - 45 degree fingers with rounded corners,
2050 5 - horizontal & vertical fingers with rounded corners.
2052 Pins and Vias may have thermals whether or not there is a polygon available
2053 to connect with. However, they will have no effect without the polygon.
2054 %end-doc */
2056 static int
2057 ActionSetThermal (int argc, char **argv, Coord x, Coord y)
2059 char *function = ARG (0);
2060 char *style = ARG (1);
2061 void *ptr1, *ptr2, *ptr3;
2062 int type, kind;
2063 int err = 0;
2065 if (function && *function)
2067 bool absolute;
2069 if ( ! style || ! *style)
2071 kind = PCB->ThermStyle;
2072 absolute = true;
2074 else
2075 kind = GetUnitlessValue (style, &absolute);
2077 /* To allow relative values we could search for the first selected
2078 item and make 'kind' relative to that, but that's not too useful
2079 and requires quite some code. For example there's no
2080 GetFirstSelectedPin() function available. Let's postpone this
2081 functionality, there are more urgent things to do. */
2083 if (absolute)
2084 switch (GetFunctionID (function))
2086 case F_Object:
2087 if ((type =
2088 SearchScreen (Crosshair.X, Crosshair.Y, CHANGETHERMAL_TYPES,
2089 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
2091 ChangeObjectThermal (type, ptr1, ptr2, ptr3, kind);
2092 IncrementUndoSerialNumber ();
2093 Draw ();
2095 break;
2096 case F_SelectedPins:
2097 ChangeSelectedThermals (PIN_TYPE, kind);
2098 break;
2099 case F_SelectedVias:
2100 ChangeSelectedThermals (VIA_TYPE, kind);
2101 break;
2102 case F_Selected:
2103 case F_SelectedElements:
2104 ChangeSelectedThermals (CHANGETHERMAL_TYPES, kind);
2105 break;
2106 default:
2107 err = 1;
2108 break;
2110 else
2111 err = 1;
2113 else
2114 err = 1;
2116 if (err)
2117 AFAIL (setthermal);
2119 return 0;
2123 * \brief Event handler to set the cursor according to the X pointer
2124 * position called from inside main.c.
2126 * \warning !!! no action routine !!!
2128 void
2129 EventMoveCrosshair (int ev_x, int ev_y)
2131 #ifdef HAVE_LIBSTROKE
2132 if (mid_stroke)
2134 StrokeBox.X2 = ev_x;
2135 StrokeBox.Y2 = ev_y;
2136 stroke_record (ev_x, ev_y);
2137 return;
2139 #endif /* HAVE_LIBSTROKE */
2140 if (MoveCrosshairAbsolute (ev_x, ev_y))
2142 /* update object position and cursor location */
2143 AdjustAttachedObjects ();
2144 notify_crosshair_change (true);
2148 /* --------------------------------------------------------------------------- */
2150 static const char setvalue_syntax[] =
2151 N_("SetValue(Grid|Line|LineSize|Text|TextScale|ViaDrillingHole|Via|ViaSize, "
2152 "delta)");
2154 static const char setvalue_help[] =
2155 N_("Change various board-wide values and sizes.");
2157 /* %start-doc actions SetValue
2159 @table @code
2161 @item ViaDrillingHole
2162 Changes the diameter of the drill for new vias.
2164 @item Grid
2165 Sets the grid spacing.
2167 @item Line
2168 @item LineSize
2169 Changes the thickness of new lines.
2171 @item Via
2172 @item ViaSize
2173 Changes the diameter of new vias.
2175 @item Text
2176 @item TextScale
2177 Changes the size of new text.
2179 @end table
2181 %end-doc */
2183 static int
2184 ActionSetValue (int argc, char **argv, Coord x, Coord y)
2186 char *function = ARG (0);
2187 char *val = ARG (1);
2188 char *units = ARG (2);
2189 bool absolute; /* flag for 'absolute' value */
2190 double value;
2191 int text_scale;
2192 int err = 0;
2194 if (function && val)
2196 value = GetValue (val, units, &absolute);
2197 switch (GetFunctionID (function))
2199 case F_ViaDrillingHole:
2200 SetViaDrillingHole (absolute ? value :
2201 value + Settings.ViaDrillingHole,
2202 false);
2203 hid_action ("RouteStylesChanged");
2204 break;
2206 case F_Grid:
2207 if (absolute)
2208 SetGrid (value, false);
2209 else
2211 if (value == 0)
2212 value = val[0] == '-' ? -Settings.increments->grid
2213 : Settings.increments->grid;
2214 /* On the way down, short against the minimum
2215 * PCB drawing unit */
2216 if ((value + PCB->Grid) < 1)
2217 SetGrid (1, false);
2218 else if (PCB->Grid == 1)
2219 SetGrid (value, false);
2220 else
2221 SetGrid (value + PCB->Grid, false);
2223 break;
2225 case F_LineSize:
2226 case F_Line:
2227 if (!absolute && value == 0)
2228 value = val[0] == '-' ? -Settings.increments->line
2229 : Settings.increments->line;
2230 SetLineSize (absolute ? value : value + Settings.LineThickness);
2231 hid_action ("RouteStylesChanged");
2232 break;
2234 case F_Via:
2235 case F_ViaSize:
2236 SetViaSize (absolute ? value : value + Settings.ViaThickness, false);
2237 hid_action ("RouteStylesChanged");
2238 break;
2240 case F_Text:
2241 case F_TextScale:
2242 text_scale = value / (double)FONT_CAPHEIGHT * 100.;
2243 if (!absolute)
2244 text_scale += Settings.TextScale;
2245 SetTextScale (text_scale);
2246 break;
2247 default:
2248 err = 1;
2249 break;
2251 if (!err)
2252 return 0;
2255 AFAIL (setvalue);
2259 /* --------------------------------------------------------------------------- */
2261 static const char quit_syntax[] = N_("Quit()");
2263 static const char quit_help[] = N_("Quits the application after confirming.");
2265 /* %start-doc actions Quit
2267 If you have unsaved changes, you will be prompted to confirm (or
2268 save) before quitting.
2270 %end-doc */
2272 static int
2273 ActionQuit (int argc, char **argv, Coord x, Coord y)
2275 char *force = ARG (0);
2276 if (force && strcasecmp (force, "force") == 0)
2278 PCB->Changed = 0;
2279 exit (0);
2281 if (!PCB->Changed || gui->close_confirm_dialog () == HID_CLOSE_CONFIRM_OK)
2282 QuitApplication ();
2283 return 1;
2286 /* --------------------------------------------------------------------------- */
2288 static const char connection_syntax[] =
2289 N_("Connection(Find|ResetLinesAndPolygons|ResetPinsAndVias|Reset)");
2291 static const char connection_help[] =
2292 N_("Searches connections of the object at the cursor position.");
2294 /* %start-doc actions Connection
2296 Connections found with this action will be highlighted in the
2297 ``connected-color'' color and will have the ``found'' flag set.
2299 @table @code
2301 @item Find
2302 The net under the cursor is ``found''.
2304 @item ResetLinesAndPolygons
2305 Any ``found'' lines and polygons are marked ``not found''.
2307 @item ResetPinsAndVias
2308 Any ``found'' pins and vias are marked ``not found''.
2310 @item Reset
2311 All ``found'' objects are marked ``not found''.
2313 @end table
2315 %end-doc */
2317 static int
2318 ActionConnection (int argc, char **argv, Coord x, Coord y)
2320 char *function = ARG (0);
2321 if (function)
2323 switch (GetFunctionID (function))
2325 case F_Find:
2327 gui->get_coords (_("Click on a connection"), &x, &y);
2328 LookupConnection (x, y, true, 1, CONNECTEDFLAG, false);
2329 LookupConnection (x, y, true, 1, FOUNDFLAG, true);
2330 break;
2333 case F_ResetLinesAndPolygons:
2334 if (ClearFlagOnLinesAndPolygons (true, CONNECTEDFLAG | FOUNDFLAG))
2336 IncrementUndoSerialNumber ();
2337 Draw ();
2339 break;
2341 case F_ResetPinsViasAndPads:
2342 if (ClearFlagOnPinsViasAndPads (true, CONNECTEDFLAG | FOUNDFLAG))
2344 IncrementUndoSerialNumber ();
2345 Draw ();
2347 break;
2349 case F_Reset:
2350 if (ClearFlagOnAllObjects (true, CONNECTEDFLAG | FOUNDFLAG))
2352 IncrementUndoSerialNumber ();
2353 Draw ();
2355 break;
2357 return 0;
2360 AFAIL (connection);
2363 /* --------------------------------------------------------------------------- */
2365 static const char disperseelements_syntax[] =
2366 N_("DisperseElements(All|Selected)");
2368 static const char disperseelements_help[] = N_("Disperses elements.");
2370 /* %start-doc actions DisperseElements
2372 Normally this is used when starting a board, by selecting all elements
2373 and then dispersing them. This scatters the elements around the board
2374 so that you can pick individual ones, rather than have all the
2375 elements at the same 0,0 coordinate and thus impossible to choose
2376 from.
2378 %end-doc */
2380 #define GAP MIL_TO_COORD(100)
2382 static int
2383 ActionDisperseElements (int argc, char **argv, Coord x, Coord y)
2385 char *function = ARG (0);
2386 Coord minx = GAP,
2387 miny = GAP,
2388 maxy = GAP,
2389 dx, dy;
2390 int all = 0, bad = 0;
2392 if (!function || !*function)
2394 bad = 1;
2396 else
2398 switch (GetFunctionID (function))
2400 case F_All:
2401 all = 1;
2402 break;
2404 case F_Selected:
2405 all = 0;
2406 break;
2408 default:
2409 bad = 1;
2413 if (bad)
2415 AFAIL (disperseelements);
2419 ELEMENT_LOOP (PCB->Data);
2422 * If we want to disperse selected elements, maybe we need smarter
2423 * code here to avoid putting components on top of others which
2424 * are not selected. For now, I'm assuming that this is typically
2425 * going to be used either with a brand new design or a scratch
2426 * design holding some new components
2428 if (!TEST_FLAG (LOCKFLAG, element) && (all || TEST_FLAG (SELECTEDFLAG, element)))
2431 /* figure out how much to move the element */
2432 dx = minx - element->BoundingBox.X1;
2434 /* snap to the grid */
2435 dx -= (element->MarkX + dx) % PCB->Grid;
2438 * and add one grid size so we make sure we always space by GAP or
2439 * more
2441 dx += PCB->Grid;
2443 /* Figure out if this row has room. If not, start a new row */
2444 if (GAP + element->BoundingBox.X2 + dx > PCB->MaxWidth)
2446 miny = maxy + GAP;
2447 minx = GAP;
2450 /* figure out how much to move the element */
2451 dx = minx - element->BoundingBox.X1;
2452 dy = miny - element->BoundingBox.Y1;
2454 /* snap to the grid */
2455 dx -= (element->MarkX + dx) % PCB->Grid;
2456 dx += PCB->Grid;
2457 dy -= (element->MarkY + dy) % PCB->Grid;
2458 dy += PCB->Grid;
2460 /* move the element */
2461 MoveElementLowLevel (PCB->Data, element, dx, dy);
2463 /* and add to the undo list so we can undo this operation */
2464 AddObjectToMoveUndoList (ELEMENT_TYPE, NULL, NULL, element, dx, dy);
2466 /* keep track of how tall this row is */
2467 minx += element->BoundingBox.X2 - element->BoundingBox.X1 + GAP;
2468 if (maxy < element->BoundingBox.Y2)
2470 maxy = element->BoundingBox.Y2;
2475 END_LOOP;
2477 /* done with our action so increment the undo # */
2478 IncrementUndoSerialNumber ();
2480 Redraw ();
2481 SetChangedFlag (true);
2483 return 0;
2486 #undef GAP
2488 /* --------------------------------------------------------------------------- */
2490 static const char display_syntax[] =
2491 N_("Display(NameOnPCB|Description|Value)\n"
2492 "Display(Grid|Redraw)\n"
2493 "Display(CycleClip|CycleCrosshair|Toggle45Degree|ToggleStartDirection)\n"
2494 "Display(ToggleGrid|ToggleRubberBandMode|ToggleUniqueNames)\n"
2495 "Display(ToggleMask|ToggleName|ToggleClearLine|ToggleFullPoly|ToggleSnapPin)\n"
2496 "Display(ToggleThindraw|ToggleThindrawPoly|ToggleOrthoMove|ToggleLocalRef)\n"
2497 "Display(ToggleCheckPlanes|ToggleShowDRC|ToggleAutoDRC)\n"
2498 "Display(ToggleLiveRoute|LockNames|OnlyNames)\n"
2499 "Display(Pinout|PinOrPadName)");
2501 static const char display_help[] = N_("Several display-related actions.");
2503 /* %start-doc actions Display
2505 @table @code
2507 @item NameOnPCB
2508 @item Description
2509 @item Value
2510 Specify whether all elements show their name, description, or value.
2512 @item Redraw
2513 Redraw the whole board.
2515 @item Toggle45Degree
2516 When clear, lines can be drawn at any angle. When set, lines are
2517 restricted to multiples of 45 degrees and requested lines may be
2518 broken up according to the clip setting.
2520 @item CycleClip
2521 Changes the way lines are restricted to 45 degree increments. The
2522 various settings are: straight only, orthogonal then angled, and angled
2523 then orthogonal. If AllDirections is set, this action disables it.
2525 @item CycleCrosshair
2526 Changes crosshair drawing. Crosshair may accept form of 4-ray,
2527 8-ray and 12-ray cross.
2529 @item ToggleRubberBandMode
2530 If set, moving an object moves all the lines attached to it too.
2532 @item ToggleStartDirection
2533 If set, each time you set a point in a line, the Clip toggles between
2534 orth-angle and angle-ortho.
2536 @item ToggleUniqueNames
2537 If set, you will not be permitted to change the name of an element to
2538 match that of another element.
2540 @item ToggleSnapPin
2541 If set, pin centers and pad end points are treated as additional grid
2542 points that the cursor can snap to.
2544 @item ToggleLocalRef
2545 If set, the mark is automatically set to the beginning of any move, so
2546 you can see the relative distance you've moved.
2548 @item ToggleThindraw
2549 If set, objects on the screen are drawn as outlines (lines are drawn
2550 as center-lines). This lets you see line endpoints hidden under pins,
2551 for example.
2553 @item ToggleThindrawPoly
2554 If set, polygons on the screen are drawn as outlines.
2556 @item ToggleShowDRC
2557 If set, pending objects (i.e. lines you're in the process of drawing)
2558 will be drawn with an outline showing how far away from other copper
2559 you need to be.
2561 @item ToggleLiveRoute
2562 If set, the progress of the autorouter will be visible on the screen.
2564 @item ToggleAutoDRC
2565 If set, you will not be permitted to make connections which violate
2566 the current DRC and netlist settings.
2568 @item ToggleCheckPlanes
2569 If set, lines and arcs aren't drawn, which usually leaves just the
2570 polygons. If you also disable all but the layer you're interested in,
2571 this allows you to check for isolated regions.
2573 @item ToggleOrthoMove
2574 If set, the crosshair is only allowed to move orthogonally from its
2575 previous position. I.e. you can move an element or line up, down,
2576 left, or right, but not up+left or down+right.
2578 @item ToggleName
2579 Selects whether the pinouts show the pin names or the pin numbers.
2581 @item ToggleLockNames
2582 If set, text will ignore left mouse clicks and actions that work on
2583 objects under the mouse. You can still select text with a lasso (left
2584 mouse drag) and perform actions on the selection.
2586 @item ToggleOnlyNames
2587 If set, only text will be sensitive for mouse clicks and actions that
2588 work on objects under the mouse. You can still select other objects
2589 with a lasso (left mouse drag) and perform actions on the selection.
2591 @item ToggleMask
2592 Turns the solder mask on or off.
2594 @item ToggleClearLine
2595 When set, the clear-line flag causes new lines and arcs to have their
2596 ``clear polygons'' flag set, so they won't be electrically connected
2597 to any polygons they overlap.
2599 @item ToggleFullPoly
2600 When set, the full-poly flag causes new polygons to have their
2601 ``full polygon'' flag set, so all parts of them will be displayed
2602 instead of only the biggest one.
2604 @item ToggleGrid
2605 Resets the origin of the current grid to be wherever the mouse pointer
2606 is (not where the crosshair currently is). If you provide two numbers
2607 after this, the origin is set to that coordinate.
2609 @item Grid
2610 Toggles whether the grid is displayed or not.
2612 @item Pinout
2613 Causes the pinout of the element indicated by the cursor to be
2614 displayed, usually in a separate window.
2616 @item PinOrPadName
2617 Toggles whether the names of pins, pads, or (yes) vias will be
2618 displayed. If the cursor is over an element, all of its pins and pads
2619 are affected.
2621 @end table
2623 %end-doc */
2625 static enum crosshair_shape
2626 CrosshairShapeIncrement (enum crosshair_shape shape)
2628 switch(shape)
2630 case Basic_Crosshair_Shape:
2631 shape = Union_Jack_Crosshair_Shape;
2632 break;
2633 case Union_Jack_Crosshair_Shape:
2634 shape = Dozen_Crosshair_Shape;
2635 break;
2636 case Dozen_Crosshair_Shape:
2637 shape = Crosshair_Shapes_Number;
2638 break;
2639 case Crosshair_Shapes_Number:
2640 shape = Basic_Crosshair_Shape;
2641 break;
2643 return shape;
2646 static int
2647 ActionDisplay (int argc, char **argv, Coord childX, Coord 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 switch (id = GetFunctionID (function))
2661 /* redraw layout */
2662 case F_ClearAndRedraw:
2663 case F_Redraw:
2664 Redraw ();
2665 break;
2667 /* change the displayed name of elements */
2668 case F_Value:
2669 case F_NameOnPCB:
2670 case F_Description:
2671 ELEMENT_LOOP (PCB->Data);
2673 EraseElementName (element);
2675 END_LOOP;
2676 CLEAR_FLAG (DESCRIPTIONFLAG | NAMEONPCBFLAG, PCB);
2677 switch (id)
2679 case F_Value:
2680 break;
2681 case F_NameOnPCB:
2682 SET_FLAG (NAMEONPCBFLAG, PCB);
2683 break;
2684 case F_Description:
2685 SET_FLAG (DESCRIPTIONFLAG, PCB);
2686 break;
2688 ELEMENT_LOOP (PCB->Data);
2690 DrawElementName (element);
2692 END_LOOP;
2693 Draw ();
2694 break;
2696 /* toggle line-adjust flag */
2697 case F_ToggleAllDirections:
2698 TOGGLE_FLAG (ALLDIRECTIONFLAG, PCB);
2699 AdjustAttachedObjects ();
2700 break;
2702 case F_CycleClip:
2703 notify_crosshair_change (false);
2704 if (TEST_FLAG (ALLDIRECTIONFLAG, PCB))
2706 TOGGLE_FLAG (ALLDIRECTIONFLAG, PCB);
2707 PCB->Clipping = 0;
2709 else
2710 PCB->Clipping = (PCB->Clipping + 1) % 3;
2711 AdjustAttachedObjects ();
2712 notify_crosshair_change (true);
2713 break;
2715 case F_CycleCrosshair:
2716 notify_crosshair_change (false);
2717 Crosshair.shape = CrosshairShapeIncrement(Crosshair.shape);
2718 if (Crosshair_Shapes_Number == Crosshair.shape)
2719 Crosshair.shape = Basic_Crosshair_Shape;
2720 notify_crosshair_change (true);
2721 break;
2723 case F_ToggleRubberBandMode:
2724 notify_crosshair_change (false);
2725 TOGGLE_FLAG (RUBBERBANDFLAG, PCB);
2726 notify_crosshair_change (true);
2727 break;
2729 case F_ToggleStartDirection:
2730 notify_crosshair_change (false);
2731 TOGGLE_FLAG (SWAPSTARTDIRFLAG, PCB);
2732 notify_crosshair_change (true);
2733 break;
2735 case F_ToggleUniqueNames:
2736 TOGGLE_FLAG (UNIQUENAMEFLAG, PCB);
2737 break;
2739 case F_ToggleSnapPin:
2740 notify_crosshair_change (false);
2741 TOGGLE_FLAG (SNAPPINFLAG, PCB);
2742 notify_crosshair_change (true);
2743 break;
2745 case F_ToggleLocalRef:
2746 TOGGLE_FLAG (LOCALREFFLAG, PCB);
2747 break;
2749 case F_ToggleThindraw:
2750 TOGGLE_FLAG (THINDRAWFLAG, PCB);
2751 Redraw ();
2752 break;
2754 case F_ToggleThindrawPoly:
2755 TOGGLE_FLAG (THINDRAWPOLYFLAG, PCB);
2756 Redraw ();
2757 break;
2759 case F_ToggleLockNames:
2760 TOGGLE_FLAG (LOCKNAMESFLAG, PCB);
2761 CLEAR_FLAG (ONLYNAMESFLAG, PCB);
2762 break;
2764 case F_ToggleOnlyNames:
2765 TOGGLE_FLAG (ONLYNAMESFLAG, PCB);
2766 CLEAR_FLAG (LOCKNAMESFLAG, PCB);
2767 break;
2769 case F_ToggleHideNames:
2770 TOGGLE_FLAG (HIDENAMESFLAG, PCB);
2771 Redraw ();
2772 break;
2774 case F_ToggleShowDRC:
2775 TOGGLE_FLAG (SHOWDRCFLAG, PCB);
2776 break;
2778 case F_ToggleLiveRoute:
2779 TOGGLE_FLAG (LIVEROUTEFLAG, PCB);
2780 break;
2782 case F_ToggleAutoDRC:
2783 notify_crosshair_change (false);
2784 TOGGLE_FLAG (AUTODRCFLAG, PCB);
2785 if (TEST_FLAG (AUTODRCFLAG, PCB) && Settings.Mode == LINE_MODE)
2787 if (ClearFlagOnAllObjects (true, CONNECTEDFLAG | FOUNDFLAG))
2789 IncrementUndoSerialNumber ();
2790 Draw ();
2792 if (Crosshair.AttachedLine.State != STATE_FIRST)
2794 LookupConnection (Crosshair.AttachedLine.Point1.X,
2795 Crosshair.AttachedLine.Point1.Y,
2796 true, 1, CONNECTEDFLAG, false);
2797 LookupConnection (Crosshair.AttachedLine.Point1.X,
2798 Crosshair.AttachedLine.Point1.Y,
2799 true, 1, FOUNDFLAG, true);
2802 notify_crosshair_change (true);
2803 break;
2805 case F_ToggleCheckPlanes:
2806 TOGGLE_FLAG (CHECKPLANESFLAG, PCB);
2807 Redraw ();
2808 break;
2810 case F_ToggleOrthoMove:
2811 TOGGLE_FLAG (ORTHOMOVEFLAG, PCB);
2812 break;
2814 case F_ToggleName:
2815 TOGGLE_FLAG (SHOWNUMBERFLAG, PCB);
2816 Redraw ();
2817 break;
2819 case F_ToggleMask:
2820 TOGGLE_FLAG (SHOWMASKFLAG, PCB);
2821 Redraw ();
2822 break;
2824 case F_ToggleClearLine:
2825 TOGGLE_FLAG (CLEARNEWFLAG, PCB);
2826 break;
2828 case F_ToggleFullPoly:
2829 TOGGLE_FLAG (NEWFULLPOLYFLAG, PCB);
2830 break;
2832 /* shift grid alignment */
2833 case F_ToggleGrid:
2835 Coord oldGrid = PCB->Grid;
2837 PCB->Grid = 1;
2838 if (MoveCrosshairAbsolute (Crosshair.X, Crosshair.Y))
2839 notify_crosshair_change (true); /* first notify was in MoveCrosshairAbs */
2840 SetGrid (oldGrid, true);
2842 break;
2844 /* toggle displaying of the grid */
2845 case F_Grid:
2846 Settings.DrawGrid = !Settings.DrawGrid;
2847 Redraw ();
2848 break;
2850 /* display the pinout of an element */
2851 case F_Pinout:
2853 ElementType *element;
2854 void *ptrtmp;
2855 Coord x, y;
2857 gui->get_coords (_("Click on an element"), &x, &y);
2858 if ((SearchScreen
2859 (x, y, ELEMENT_TYPE, &ptrtmp,
2860 &ptrtmp, &ptrtmp)) != NO_TYPE)
2862 element = (ElementType *) ptrtmp;
2863 gui->show_item (element);
2865 break;
2868 /* toggle displaying of pin/pad/via names */
2869 case F_PinOrPadName:
2871 void *ptr1, *ptr2, *ptr3;
2873 switch (SearchScreen (Crosshair.X, Crosshair.Y,
2874 ELEMENT_TYPE | PIN_TYPE | PAD_TYPE |
2875 VIA_TYPE, (void **) &ptr1, (void **) &ptr2,
2876 (void **) &ptr3))
2878 case ELEMENT_TYPE:
2879 PIN_LOOP ((ElementType *) ptr1);
2881 if (TEST_FLAG (DISPLAYNAMEFLAG, pin))
2882 ErasePinName (pin);
2883 else
2884 DrawPinName (pin);
2885 AddObjectToFlagUndoList (PIN_TYPE, ptr1, pin, pin);
2886 TOGGLE_FLAG (DISPLAYNAMEFLAG, pin);
2888 END_LOOP;
2889 PAD_LOOP ((ElementType *) ptr1);
2891 if (TEST_FLAG (DISPLAYNAMEFLAG, pad))
2892 ErasePadName (pad);
2893 else
2894 DrawPadName (pad);
2895 AddObjectToFlagUndoList (PAD_TYPE, ptr1, pad, pad);
2896 TOGGLE_FLAG (DISPLAYNAMEFLAG, pad);
2898 END_LOOP;
2899 SetChangedFlag (true);
2900 IncrementUndoSerialNumber ();
2901 Draw ();
2902 break;
2904 case PIN_TYPE:
2905 if (TEST_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2))
2906 ErasePinName ((PinType *) ptr2);
2907 else
2908 DrawPinName ((PinType *) ptr2);
2909 AddObjectToFlagUndoList (PIN_TYPE, ptr1, ptr2, ptr3);
2910 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2);
2911 SetChangedFlag (true);
2912 IncrementUndoSerialNumber ();
2913 Draw ();
2914 break;
2916 case PAD_TYPE:
2917 if (TEST_FLAG (DISPLAYNAMEFLAG, (PadType *) ptr2))
2918 ErasePadName ((PadType *) ptr2);
2919 else
2920 DrawPadName ((PadType *) ptr2);
2921 AddObjectToFlagUndoList (PAD_TYPE, ptr1, ptr2, ptr3);
2922 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PadType *) ptr2);
2923 SetChangedFlag (true);
2924 IncrementUndoSerialNumber ();
2925 Draw ();
2926 break;
2927 case VIA_TYPE:
2928 if (TEST_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2))
2929 EraseViaName ((PinType *) ptr2);
2930 else
2931 DrawViaName ((PinType *) ptr2);
2932 AddObjectToFlagUndoList (VIA_TYPE, ptr1, ptr2, ptr3);
2933 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2);
2934 SetChangedFlag (true);
2935 IncrementUndoSerialNumber ();
2936 Draw ();
2937 break;
2939 break;
2941 default:
2942 err = 1;
2945 else if (function && str_dir)
2947 switch (GetFunctionID (function))
2949 case F_ToggleGrid:
2950 if (argc > 2)
2952 PCB->GridOffsetX = GetValue (argv[1], NULL, NULL);
2953 PCB->GridOffsetY = GetValue (argv[2], NULL, NULL);
2954 if (Settings.DrawGrid)
2955 Redraw ();
2957 break;
2959 default:
2960 err = 1;
2961 break;
2964 else
2965 err = 1;
2967 if (err)
2968 AFAIL (display);
2970 return 0;
2973 /* --------------------------------------------------------------------------- */
2975 static const char mode_syntax[] =
2976 N_("Mode(Arc|Arrow|Copy|InsertPoint|Line|Lock|Move|None|PasteBuffer)\n"
2977 "Mode(Polygon|Rectangle|Remove|Rotate|Text|Thermal|Via)\n"
2978 "Mode(Notify|Release|Cancel|Stroke)\n"
2979 "Mode(Save|Restore)");
2981 static const char mode_help[] = N_("Change or use the tool mode.");
2983 /* %start-doc actions Mode
2985 @table @code
2987 @item Arc
2988 @itemx Arrow
2989 @itemx Copy
2990 @itemx InsertPoint
2991 @itemx Line
2992 @itemx Lock
2993 @itemx Move
2994 @itemx None
2995 @itemx PasteBuffer
2996 @itemx Polygon
2997 @itemx Rectangle
2998 @itemx Remove
2999 @itemx Rotate
3000 @itemx Text
3001 @itemx Thermal
3002 @itemx Via
3003 Select the indicated tool.
3005 @item Notify
3006 Called when you press the mouse button, or move the mouse.
3008 @item Release
3009 Called when you release the mouse button.
3011 @item Cancel
3012 Cancels any pending tool activity, allowing you to restart elsewhere.
3013 For example, this allows you to start a new line rather than attach a
3014 line to the previous line.
3016 @item Escape
3017 Similar to Cancel but calling this action a second time will return
3018 to the Arrow tool.
3020 @item Stroke
3021 If your @code{pcb} was built with libstroke, this invokes the stroke
3022 input method. If not, this will restart a drawing mode if you were
3023 drawing, else it will select objects.
3025 @item Save
3026 Remembers the current tool.
3028 @item Restore
3029 Restores the tool to the last saved tool.
3031 @end table
3033 %end-doc */
3035 static int
3036 ActionMode (int argc, char **argv, Coord x, Coord y)
3038 char *function = ARG (0);
3040 if (function)
3042 Note.X = Crosshair.X;
3043 Note.Y = Crosshair.Y;
3044 notify_crosshair_change (false);
3045 switch (GetFunctionID (function))
3047 case F_Arc:
3048 SetMode (ARC_MODE);
3049 break;
3050 case F_Arrow:
3051 SetMode (ARROW_MODE);
3052 break;
3053 case F_Copy:
3054 SetMode (COPY_MODE);
3055 break;
3056 case F_InsertPoint:
3057 SetMode (INSERTPOINT_MODE);
3058 break;
3059 case F_Line:
3060 SetMode (LINE_MODE);
3061 break;
3062 case F_Lock:
3063 SetMode (LOCK_MODE);
3064 break;
3065 case F_Move:
3066 SetMode (MOVE_MODE);
3067 break;
3068 case F_None:
3069 SetMode (NO_MODE);
3070 break;
3071 case F_Cancel:
3073 int saved_mode = Settings.Mode;
3074 SetMode (NO_MODE);
3075 SetMode (saved_mode);
3077 break;
3078 case F_Escape:
3080 switch (Settings.Mode)
3082 case VIA_MODE:
3083 case PASTEBUFFER_MODE:
3084 case TEXT_MODE:
3085 case ROTATE_MODE:
3086 case REMOVE_MODE:
3087 case MOVE_MODE:
3088 case COPY_MODE:
3089 case INSERTPOINT_MODE:
3090 case RUBBERBANDMOVE_MODE:
3091 case THERMAL_MODE:
3092 case LOCK_MODE:
3093 SetMode (NO_MODE);
3094 SetMode (ARROW_MODE);
3095 break;
3097 case LINE_MODE:
3098 if (Crosshair.AttachedLine.State == STATE_FIRST)
3099 SetMode (ARROW_MODE);
3100 else
3102 SetMode (NO_MODE);
3103 SetMode (LINE_MODE);
3105 break;
3107 case RECTANGLE_MODE:
3108 if (Crosshair.AttachedBox.State == STATE_FIRST)
3109 SetMode (ARROW_MODE);
3110 else
3112 SetMode (NO_MODE);
3113 SetMode (RECTANGLE_MODE);
3115 break;
3117 case POLYGON_MODE:
3118 if (Crosshair.AttachedLine.State == STATE_FIRST)
3119 SetMode (ARROW_MODE);
3120 else
3122 SetMode (NO_MODE);
3123 SetMode (POLYGON_MODE);
3125 break;
3127 case POLYGONHOLE_MODE:
3128 if (Crosshair.AttachedLine.State == STATE_FIRST)
3129 SetMode (ARROW_MODE);
3130 else
3132 SetMode (NO_MODE);
3133 SetMode (POLYGONHOLE_MODE);
3135 break;
3137 case ARC_MODE:
3138 if (Crosshair.AttachedBox.State == STATE_FIRST)
3139 SetMode (ARROW_MODE);
3140 else
3142 SetMode (NO_MODE);
3143 SetMode (ARC_MODE);
3145 break;
3147 case ARROW_MODE:
3148 break;
3150 default:
3151 break;
3154 break;
3156 case F_Notify:
3157 NotifyMode ();
3158 break;
3159 case F_PasteBuffer:
3160 SetMode (PASTEBUFFER_MODE);
3161 break;
3162 case F_Polygon:
3163 SetMode (POLYGON_MODE);
3164 break;
3165 case F_PolygonHole:
3166 SetMode (POLYGONHOLE_MODE);
3167 break;
3168 #ifndef HAVE_LIBSTROKE
3169 case F_Release:
3170 ReleaseMode ();
3171 break;
3172 #else
3173 case F_Release:
3174 if (mid_stroke)
3175 FinishStroke ();
3176 else
3177 ReleaseMode ();
3178 break;
3179 #endif
3180 case F_Remove:
3181 SetMode (REMOVE_MODE);
3182 break;
3183 case F_Rectangle:
3184 SetMode (RECTANGLE_MODE);
3185 break;
3186 case F_Rotate:
3187 SetMode (ROTATE_MODE);
3188 break;
3189 case F_Stroke:
3190 #ifdef HAVE_LIBSTROKE
3191 mid_stroke = true;
3192 StrokeBox.X1 = Crosshair.X;
3193 StrokeBox.Y1 = Crosshair.Y;
3194 break;
3195 #else
3196 /* Handle middle mouse button restarts of drawing mode. If not in
3197 | a drawing mode, middle mouse button will select objects.
3199 if (Settings.Mode == LINE_MODE
3200 && Crosshair.AttachedLine.State != STATE_FIRST)
3202 SetMode (LINE_MODE);
3204 else if (Settings.Mode == ARC_MODE
3205 && Crosshair.AttachedBox.State != STATE_FIRST)
3206 SetMode (ARC_MODE);
3207 else if (Settings.Mode == RECTANGLE_MODE
3208 && Crosshair.AttachedBox.State != STATE_FIRST)
3209 SetMode (RECTANGLE_MODE);
3210 else if (Settings.Mode == POLYGON_MODE
3211 && Crosshair.AttachedLine.State != STATE_FIRST)
3212 SetMode (POLYGON_MODE);
3213 else
3215 SaveMode ();
3216 saved_mode = true;
3217 SetMode (ARROW_MODE);
3218 NotifyMode ();
3220 break;
3221 #endif
3222 case F_Text:
3223 SetMode (TEXT_MODE);
3224 break;
3225 case F_Thermal:
3226 SetMode (THERMAL_MODE);
3227 break;
3228 case F_Via:
3229 SetMode (VIA_MODE);
3230 break;
3232 case F_Restore: /* restore the last saved mode */
3233 RestoreMode ();
3234 break;
3236 case F_Save: /* save currently selected mode */
3237 SaveMode ();
3238 break;
3240 notify_crosshair_change (true);
3241 return 0;
3244 AFAIL (mode);
3247 /* --------------------------------------------------------------------------- */
3249 static const char removeselected_syntax[] = N_("RemoveSelected()");
3251 static const char removeselected_help[] = N_("Removes any selected objects.");
3253 /* %start-doc actions RemoveSelected
3255 %end-doc */
3257 static int
3258 ActionRemoveSelected (int argc, char **argv, Coord x, Coord y)
3260 if (RemoveSelected ())
3261 SetChangedFlag (true);
3262 return 0;
3265 /* --------------------------------------------------------------------------- */
3267 static const char renumber_syntax[] = N_("Renumber()\n"
3268 "Renumber(filename)");
3270 static const char renumber_help[] =
3271 N_("Renumber all elements. The changes will be recorded to filename\n"
3272 "for use in backannotating these changes to the schematic.");
3274 /* %start-doc actions Renumber
3276 %end-doc */
3278 static int
3279 ActionRenumber (int argc, char **argv, Coord x, Coord y)
3281 bool changed = false;
3282 ElementType **element_list;
3283 ElementType **locked_element_list;
3284 unsigned int i, j, k, cnt, lock_cnt;
3285 unsigned int tmpi;
3286 size_t sz;
3287 char *tmps;
3288 char *name;
3289 FILE *out;
3290 static char * default_file = NULL;
3291 size_t cnt_list_sz = 100;
3292 struct _cnt_list
3294 char *name;
3295 unsigned int cnt;
3296 } *cnt_list;
3297 char **was, **is, *pin;
3298 unsigned int c_cnt = 0;
3299 int unique, ok;
3300 int free_name = 0;
3302 if (argc < 1)
3305 * We deal with the case where name already exists in this
3306 * function so the GUI doesn't need to deal with it
3308 name = gui->fileselect (_("Save Renumber Annotation File As ..."),
3309 _("Choose a file to record the renumbering to.\n"
3310 "This file may be used to back annotate the\n"
3311 "change to the schematics.\n"),
3312 default_file, ".eco", "eco",
3315 free_name = 1;
3317 else
3318 name = argv[0];
3320 if (default_file)
3322 free (default_file);
3323 default_file = NULL;
3326 if (name && *name)
3328 default_file = strdup (name);
3331 if ((out = fopen (name, "r")))
3333 fclose (out);
3334 if (!gui->confirm_dialog (_("File exists! Ok to overwrite?"), 0))
3336 if (free_name && name)
3337 free (name);
3338 return 0;
3342 if ((out = fopen (name, "w")) == NULL)
3344 Message (_("Could not open %s\n"), name);
3345 if (free_name && name)
3346 free (name);
3347 return 1;
3350 if (free_name && name)
3351 free (name);
3353 fprintf (out, "*COMMENT* PCB Annotation File\n");
3354 fprintf (out, "*FILEVERSION* 20061031\n");
3357 * Make a first pass through all of the elements and sort them out
3358 * by location on the board. While here we also collect a list of
3359 * locked elements.
3361 * We'll actually renumber things in the 2nd pass.
3363 element_list = (ElementType **)calloc (PCB->Data->ElementN, sizeof (ElementType *));
3364 locked_element_list = (ElementType **)calloc (PCB->Data->ElementN, sizeof (ElementType *));
3365 was = (char **)calloc (PCB->Data->ElementN, sizeof (char *));
3366 is = (char **)calloc (PCB->Data->ElementN, sizeof (char *));
3367 if (element_list == NULL || locked_element_list == NULL || was == NULL
3368 || is == NULL)
3370 fprintf (stderr, "calloc() failed in %s\n", __FUNCTION__);
3371 exit (1);
3375 cnt = 0;
3376 lock_cnt = 0;
3377 ELEMENT_LOOP (PCB->Data);
3379 if (TEST_FLAG (LOCKFLAG, element->Name) || TEST_FLAG (LOCKFLAG, element))
3382 * add to the list of locked elements which we won't try to
3383 * renumber and whose reference designators are now reserved.
3385 pcb_fprintf (out,
3386 "*WARN* Element \"%s\" at %$md is locked and will not be renumbered.\n",
3387 UNKNOWN (NAMEONPCB_NAME (element)), element->MarkX, element->MarkY);
3388 locked_element_list[lock_cnt] = element;
3389 lock_cnt++;
3392 else
3394 /* count of devices which will be renumbered */
3395 cnt++;
3397 /* search for correct position in the list */
3398 i = 0;
3399 while (element_list[i] && element->MarkY > element_list[i]->MarkY)
3400 i++;
3403 * We have found the position where we have the first element that
3404 * has the same Y value or a lower Y value. Now move forward if
3405 * needed through the X values
3407 while (element_list[i]
3408 && element->MarkY == element_list[i]->MarkY
3409 && element->MarkX > element_list[i]->MarkX)
3410 i++;
3412 for (j = cnt - 1; j > i; j--)
3414 element_list[j] = element_list[j - 1];
3416 element_list[i] = element;
3419 END_LOOP;
3423 * Now that the elements are sorted by board position, we go through
3424 * and renumber them.
3428 * turn off the flag which requires unique names so it doesn't get
3429 * in our way. When we're done with the renumber we will have unique
3430 * names.
3432 unique = TEST_FLAG (UNIQUENAMEFLAG, PCB);
3433 CLEAR_FLAG (UNIQUENAMEFLAG, PCB);
3435 cnt_list = (struct _cnt_list *)calloc (cnt_list_sz, sizeof (struct _cnt_list));
3436 for (i = 0; i < cnt; i++)
3438 /* If there is no refdes, maybe just spit out a warning */
3439 if (NAMEONPCB_NAME (element_list[i]))
3441 /* figure out the prefix */
3442 tmps = strdup (NAMEONPCB_NAME (element_list[i]));
3443 j = 0;
3444 while (tmps[j] && (tmps[j] < '0' || tmps[j] > '9')
3445 && tmps[j] != '?')
3446 j++;
3447 tmps[j] = '\0';
3449 /* check the counter for this prefix */
3450 for (j = 0;
3451 cnt_list[j].name && (strcmp (cnt_list[j].name, tmps) != 0)
3452 && j < cnt_list_sz; j++);
3454 /* grow the list if needed */
3455 if (j == cnt_list_sz)
3457 cnt_list_sz += 100;
3458 cnt_list = (struct _cnt_list *)realloc (cnt_list, cnt_list_sz);
3459 if (cnt_list == NULL)
3461 fprintf (stderr, _("realloc failed() in %s\n"), __FUNCTION__);
3462 exit (1);
3464 /* zero out the memory that we added */
3465 for (tmpi = j; tmpi < cnt_list_sz; tmpi++)
3467 cnt_list[tmpi].name = NULL;
3468 cnt_list[tmpi].cnt = 0;
3473 * start a new counter if we don't have a counter for this
3474 * prefix
3476 if (!cnt_list[j].name)
3478 cnt_list[j].name = strdup (tmps);
3479 cnt_list[j].cnt = 0;
3483 * check to see if the new refdes is already used by a
3484 * locked element
3488 ok = 1;
3489 cnt_list[j].cnt++;
3490 free (tmps);
3492 /* space for the prefix plus 1 digit plus the '\0' */
3493 sz = strlen (cnt_list[j].name) + 2;
3495 /* and 1 more per extra digit needed to hold the number */
3496 tmpi = cnt_list[j].cnt;
3497 while (tmpi > 10)
3499 sz++;
3500 tmpi = tmpi / 10;
3502 tmps = (char *)malloc (sz * sizeof (char));
3503 sprintf (tmps, "%s%d", cnt_list[j].name, (int) cnt_list[j].cnt);
3506 * now compare to the list of reserved (by locked
3507 * elements) names
3509 for (k = 0; k < lock_cnt; k++)
3511 if (strcmp
3512 (UNKNOWN (NAMEONPCB_NAME (locked_element_list[k])),
3513 tmps) == 0)
3515 ok = 0;
3516 break;
3521 while (!ok);
3523 if (strcmp (tmps, NAMEONPCB_NAME (element_list[i])) != 0)
3525 fprintf (out, "*RENAME* \"%s\" \"%s\"\n",
3526 NAMEONPCB_NAME (element_list[i]), tmps);
3528 /* add this rename to our table of renames so we can update the netlist */
3529 was[c_cnt] = strdup (NAMEONPCB_NAME (element_list[i]));
3530 is[c_cnt] = strdup (tmps);
3531 c_cnt++;
3533 AddObjectToChangeNameUndoList (ELEMENT_TYPE, NULL, NULL,
3534 element_list[i],
3535 NAMEONPCB_NAME (element_list
3536 [i]));
3538 ChangeObjectName (ELEMENT_TYPE, element_list[i], NULL, NULL,
3539 tmps);
3540 changed = true;
3542 /* we don't free tmps in this case because it is used */
3544 else
3545 free (tmps);
3547 else
3549 pcb_fprintf (out, "*WARN* Element at %$md has no name.\n",
3550 element_list[i]->MarkX, element_list[i]->MarkY);
3555 fclose (out);
3557 /* restore the unique flag setting */
3558 if (unique)
3559 SET_FLAG (UNIQUENAMEFLAG, PCB);
3561 if (changed)
3564 /* update the netlist */
3565 AddNetlistLibToUndoList (&(PCB->NetlistLib));
3567 /* iterate over each net */
3568 for (i = 0; i < PCB->NetlistLib.MenuN; i++)
3571 /* iterate over each pin on the net */
3572 for (j = 0; j < PCB->NetlistLib.Menu[i].EntryN; j++)
3575 /* figure out the pin number part from strings like U3-21 */
3576 tmps = strdup (PCB->NetlistLib.Menu[i].Entry[j].ListEntry);
3577 for (k = 0; tmps[k] && tmps[k] != '-'; k++);
3578 tmps[k] = '\0';
3579 pin = tmps + k + 1;
3581 /* iterate over the list of changed reference designators */
3582 for (k = 0; k < c_cnt; k++)
3585 * if the pin needs to change, change it and quit
3586 * searching in the list.
3588 if (strcmp (tmps, was[k]) == 0)
3590 free (PCB->NetlistLib.Menu[i].Entry[j].ListEntry);
3591 PCB->NetlistLib.Menu[i].Entry[j].ListEntry =
3592 (char *)malloc ((strlen (is[k]) + strlen (pin) +
3593 2) * sizeof (char));
3594 sprintf (PCB->NetlistLib.Menu[i].Entry[j].ListEntry,
3595 "%s-%s", is[k], pin);
3596 k = c_cnt;
3600 free (tmps);
3603 for (k = 0; k < c_cnt; k++)
3605 free (was[k]);
3606 free (is[k]);
3609 NetlistChanged (0);
3610 IncrementUndoSerialNumber ();
3611 SetChangedFlag (true);
3614 free (locked_element_list);
3615 free (element_list);
3616 free (cnt_list);
3617 free (is);
3618 free (was);
3619 return 0;
3623 /* --------------------------------------------------------------------------- */
3625 static const char ripup_syntax[] = N_("RipUp(All|Selected|Element)");
3627 static const char ripup_help[] =
3628 N_("Ripup auto-routed tracks, or convert an element to parts.");
3630 /* %start-doc actions RipUp
3632 @table @code
3634 @item All
3635 Removes all lines and vias which were created by the autorouter.
3637 @item Selected
3638 Removes all selected lines and vias which were created by the
3639 autorouter.
3641 @item Element
3642 Converts the element under the cursor to parts (vias and lines). Note
3643 that this uses the highest numbered paste buffer.
3645 @end table
3647 %end-doc */
3649 static int
3650 ActionRipUp (int argc, char **argv, Coord x, Coord y)
3652 char *function = ARG (0);
3653 bool changed = false;
3655 if (function)
3657 switch (GetFunctionID (function))
3659 case F_All:
3660 ALLLINE_LOOP (PCB->Data);
3662 if (TEST_FLAG (AUTOFLAG, line) && !TEST_FLAG (LOCKFLAG, line))
3664 RemoveObject (LINE_TYPE, layer, line, line);
3665 changed = true;
3668 ENDALL_LOOP;
3669 ALLARC_LOOP (PCB->Data);
3671 if (TEST_FLAG (AUTOFLAG, arc) && !TEST_FLAG (LOCKFLAG, arc))
3673 RemoveObject (ARC_TYPE, layer, arc, arc);
3674 changed = true;
3677 ENDALL_LOOP;
3678 VIA_LOOP (PCB->Data);
3680 if (TEST_FLAG (AUTOFLAG, via) && !TEST_FLAG (LOCKFLAG, via))
3682 RemoveObject (VIA_TYPE, via, via, via);
3683 changed = true;
3686 END_LOOP;
3688 if (changed)
3690 IncrementUndoSerialNumber ();
3691 SetChangedFlag (true);
3693 break;
3694 case F_Selected:
3695 VISIBLELINE_LOOP (PCB->Data);
3697 if (TEST_FLAGS (AUTOFLAG | SELECTEDFLAG, line)
3698 && !TEST_FLAG (LOCKFLAG, line))
3700 RemoveObject (LINE_TYPE, layer, line, line);
3701 changed = true;
3704 ENDALL_LOOP;
3705 if (PCB->ViaOn)
3706 VIA_LOOP (PCB->Data);
3708 if (TEST_FLAGS (AUTOFLAG | SELECTEDFLAG, via)
3709 && !TEST_FLAG (LOCKFLAG, via))
3711 RemoveObject (VIA_TYPE, via, via, via);
3712 changed = true;
3715 END_LOOP;
3716 if (changed)
3718 IncrementUndoSerialNumber ();
3719 SetChangedFlag (true);
3721 break;
3722 case F_Element:
3724 void *ptr1, *ptr2, *ptr3;
3726 if (SearchScreen (Crosshair.X, Crosshair.Y, ELEMENT_TYPE,
3727 &ptr1, &ptr2, &ptr3) != NO_TYPE)
3729 Note.Buffer = Settings.BufferNumber;
3730 SetBufferNumber (MAX_BUFFER - 1);
3731 ClearBuffer (PASTEBUFFER);
3732 CopyObjectToBuffer (PASTEBUFFER->Data, PCB->Data,
3733 ELEMENT_TYPE, ptr1, ptr2, ptr3);
3734 SmashBufferElement (PASTEBUFFER);
3735 PASTEBUFFER->X = 0;
3736 PASTEBUFFER->Y = 0;
3737 SaveUndoSerialNumber ();
3738 EraseObject (ELEMENT_TYPE, ptr1, ptr1);
3739 MoveObjectToRemoveUndoList (ELEMENT_TYPE, ptr1, ptr2, ptr3);
3740 RestoreUndoSerialNumber ();
3741 CopyPastebufferToLayout (0, 0);
3742 SetBufferNumber (Note.Buffer);
3743 SetChangedFlag (true);
3746 break;
3749 return 0;
3752 /* --------------------------------------------------------------------------- */
3754 static const char addrats_syntax[] = N_("AddRats(AllRats|SelectedRats|Close)");
3756 static const char addrats_help[] =
3757 N_("Add one or more rat lines to the board.");
3759 /* %start-doc actions AddRats
3761 @table @code
3763 @item AllRats
3764 Create rat lines for all loaded nets that aren't already connected on
3765 with copper.
3767 @item SelectedRats
3768 Similarly, but only add rat lines for nets connected to selected pins
3769 and pads.
3771 @item Close
3772 Selects the shortest unselected rat on the board.
3774 @end table
3776 %end-doc */
3778 static int
3779 ActionAddRats (int argc, char **argv, Coord x, Coord y)
3781 char *function = ARG (0);
3782 RatType *shorty;
3783 float len, small;
3785 if (function)
3787 if (Settings.RatWarn)
3788 ClearWarnings ();
3789 switch (GetFunctionID (function))
3791 case F_AllRats:
3792 if (AddAllRats (false, NULL))
3793 SetChangedFlag (true);
3794 break;
3795 case F_SelectedRats:
3796 case F_Selected:
3797 if (AddAllRats (true, NULL))
3798 SetChangedFlag (true);
3799 break;
3800 case F_Close:
3801 small = SQUARE (MAX_COORD);
3802 shorty = NULL;
3803 RAT_LOOP (PCB->Data);
3805 if (TEST_FLAG (SELECTEDFLAG, line))
3806 continue;
3807 len = SQUARE (line->Point1.X - line->Point2.X) +
3808 SQUARE (line->Point1.Y - line->Point2.Y);
3809 if (len < small)
3811 small = len;
3812 shorty = line;
3815 END_LOOP;
3816 if (shorty)
3818 AddObjectToFlagUndoList (RATLINE_TYPE, shorty, shorty, shorty);
3819 SET_FLAG (SELECTEDFLAG, shorty);
3820 DrawRat (shorty);
3821 Draw ();
3822 CenterDisplay ((shorty->Point2.X + shorty->Point1.X) / 2,
3823 (shorty->Point2.Y + shorty->Point1.Y) / 2,
3824 false);
3826 break;
3829 return 0;
3832 /* --------------------------------------------------------------------------- */
3834 static const char delete_syntax[] =
3835 N_("Delete(Object|Selected)\n"
3836 "Delete(AllRats|SelectedRats)");
3838 static const char delete_help[] = N_("Delete stuff.");
3840 /* %start-doc actions Delete
3842 %end-doc */
3844 static int
3845 ActionDelete (int argc, char **argv, Coord x, Coord y)
3847 char *function = ARG (0);
3848 int id = GetFunctionID (function);
3850 Note.X = Crosshair.X;
3851 Note.Y = Crosshair.Y;
3853 if (id == -1) /* no arg */
3855 if (RemoveSelected() == false)
3856 id = F_Object;
3859 switch (id)
3861 case F_Object:
3862 SaveMode();
3863 SetMode(REMOVE_MODE);
3864 NotifyMode();
3865 RestoreMode();
3866 break;
3867 case F_Selected:
3868 RemoveSelected();
3869 break;
3870 case F_AllRats:
3871 if (DeleteRats (false))
3872 SetChangedFlag (true);
3873 break;
3874 case F_SelectedRats:
3875 if (DeleteRats (true))
3876 SetChangedFlag (true);
3877 break;
3880 return 0;
3883 /* --------------------------------------------------------------------------- */
3885 static const char deleterats_syntax[] =
3886 N_("DeleteRats(AllRats|Selected|SelectedRats)");
3888 static const char deleterats_help[] = N_("Delete rat lines.");
3890 /* %start-doc actions DeleteRats
3892 %end-doc */
3894 static int
3895 ActionDeleteRats (int argc, char **argv, Coord x, Coord y)
3897 char *function = ARG (0);
3898 if (function)
3900 if (Settings.RatWarn)
3901 ClearWarnings ();
3902 switch (GetFunctionID (function))
3904 case F_AllRats:
3905 if (DeleteRats (false))
3906 SetChangedFlag (true);
3907 break;
3908 case F_SelectedRats:
3909 case F_Selected:
3910 if (DeleteRats (true))
3911 SetChangedFlag (true);
3912 break;
3915 return 0;
3918 /* --------------------------------------------------------------------------- */
3920 static const char autoplace_syntax[] = N_("AutoPlaceSelected()");
3922 static const char autoplace_help[] = N_("Auto-place selected components.");
3924 /* %start-doc actions AutoPlaceSelected
3926 Attempts to re-arrange the selected components such that the nets
3927 connecting them are minimized. Note that you cannot undo this.
3929 %end-doc */
3931 static int
3932 ActionAutoPlaceSelected (int argc, char **argv, Coord x, Coord y)
3934 hid_action("Busy");
3935 if (gui->confirm_dialog (_("Auto-placement can NOT be undone.\n"
3936 "Do you want to continue anyway?\n"), 0))
3938 if (AutoPlaceSelected ())
3939 SetChangedFlag (true);
3941 return 0;
3944 /* --------------------------------------------------------------------------- */
3946 static const char autoroute_syntax[] = N_("AutoRoute(AllRats|SelectedRats)");
3948 static const char autoroute_help[] = N_("Auto-route some or all rat lines.");
3950 /* %start-doc actions AutoRoute
3952 @table @code
3954 @item AllRats
3955 Attempt to autoroute all rats.
3957 @item SelectedRats
3958 Attempt to autoroute the selected rats.
3960 @end table
3962 Before autorouting, it's important to set up a few things. First,
3963 make sure any layers you aren't using are disabled, else the
3964 autorouter may use them. Next, make sure the current line and via
3965 styles are set accordingly. Last, make sure "new lines clear
3966 polygons" is set, in case you eventually want to add a copper pour.
3968 Autorouting takes a while. During this time, the program may not be
3969 responsive.
3971 %end-doc */
3973 static int
3974 ActionAutoRoute (int argc, char **argv, Coord x, Coord y)
3976 char *function = ARG (0);
3977 hid_action("Busy");
3978 if (function) /* one parameter */
3980 switch (GetFunctionID (function))
3982 case F_AllRats:
3983 if (AutoRoute (false))
3984 SetChangedFlag (true);
3985 break;
3986 case F_SelectedRats:
3987 case F_Selected:
3988 if (AutoRoute (true))
3989 SetChangedFlag (true);
3990 break;
3993 return 0;
3996 /* --------------------------------------------------------------------------- */
3998 static const char markcrosshair_syntax[] =
3999 N_("MarkCrosshair()\n"
4000 "MarkCrosshair(Center)");
4002 static const char markcrosshair_help[] = N_("Set/Reset the Crosshair mark.");
4004 /* %start-doc actions MarkCrosshair
4006 The ``mark'' is a small X-shaped target on the display which is
4007 treated like a second origin (the normal origin is the upper let
4008 corner of the board). The GUI will display a second set of
4009 coordinates for this mark, which tells you how far you are from it.
4011 If no argument is given, the mark is toggled - disabled if it was
4012 enabled, or enabled at the current cursor position of disabled. If
4013 the @code{Center} argument is given, the mark is moved to the current
4014 cursor location.
4016 %end-doc */
4018 static int
4019 ActionMarkCrosshair (int argc, char **argv, Coord x, Coord y)
4021 char *function = ARG (0);
4022 if (!function || !*function)
4024 if (Marked.status)
4026 notify_mark_change (false);
4027 Marked.status = false;
4028 notify_mark_change (true);
4030 else
4032 notify_mark_change (false);
4033 Marked.status = false;
4034 Marked.status = true;
4035 Marked.X = Crosshair.X;
4036 Marked.Y = Crosshair.Y;
4037 notify_mark_change (true);
4040 else if (GetFunctionID (function) == F_Center)
4042 notify_mark_change (false);
4043 Marked.status = true;
4044 Marked.X = Crosshair.X;
4045 Marked.Y = Crosshair.Y;
4046 notify_mark_change (true);
4048 return 0;
4051 /* --------------------------------------------------------------------------- */
4053 static const char changesize_syntax[] =
4054 N_("ChangeSize(Object, delta)\n"
4055 "ChangeSize(SelectedObjects|Selected, delta)\n"
4056 "ChangeSize(SelectedLines|SelectedPins|SelectedVias, delta)\n"
4057 "ChangeSize(SelectedPads|SelectedTexts|SelectedNames, delta)\n"
4058 "ChangeSize(SelectedElements, delta)");
4060 static const char changesize_help[] = N_("Changes the size of objects.");
4062 /* %start-doc actions ChangeSize
4064 For lines and arcs, this changes the width. For pins and vias, this
4065 changes the overall diameter of the copper annulus. For pads, this
4066 changes the width and, indirectly, the length. For texts and names,
4067 this changes the scaling factor. For elements, this changes the width
4068 of the silk layer lines and arcs for this element.
4070 %end-doc */
4072 static int
4073 ActionChangeSize (int argc, char **argv, Coord x, Coord y)
4075 char *function = ARG (0);
4076 char *delta = ARG (1);
4077 char *units = ARG (2);
4078 bool absolute; /* indicates if absolute size is given */
4079 Coord value;
4081 if (function && delta)
4083 value = GetValue (delta, units, &absolute);
4084 if (value == 0)
4085 value = delta[0] == '-' ? -Settings.increments->size
4086 : Settings.increments->size;
4087 switch (GetFunctionID (function))
4089 case F_Object:
4091 int type;
4092 void *ptr1, *ptr2, *ptr3;
4094 if ((type =
4095 SearchScreen (Crosshair.X, Crosshair.Y, CHANGESIZE_TYPES,
4096 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4097 if (TEST_FLAG (LOCKFLAG, (PinType *) ptr2))
4098 Message (_("Sorry, the object is locked\n"));
4099 if (ChangeObjectSize (type, ptr1, ptr2, ptr3, value, absolute))
4100 SetChangedFlag (true);
4101 break;
4104 case F_SelectedVias:
4105 if (ChangeSelectedSize (VIA_TYPE, value, absolute))
4106 SetChangedFlag (true);
4107 break;
4109 case F_SelectedPins:
4110 if (ChangeSelectedSize (PIN_TYPE, value, absolute))
4111 SetChangedFlag (true);
4112 break;
4114 case F_SelectedPads:
4115 if (ChangeSelectedSize (PAD_TYPE, value, absolute))
4116 SetChangedFlag (true);
4117 break;
4119 case F_SelectedArcs:
4120 if (ChangeSelectedSize (ARC_TYPE, value, absolute))
4121 SetChangedFlag (true);
4122 break;
4124 case F_SelectedLines:
4125 if (ChangeSelectedSize (LINE_TYPE, value, absolute))
4126 SetChangedFlag (true);
4127 break;
4129 case F_SelectedTexts:
4130 if (ChangeSelectedSize (TEXT_TYPE, value, absolute))
4131 SetChangedFlag (true);
4132 break;
4134 case F_SelectedNames:
4135 if (ChangeSelectedSize (ELEMENTNAME_TYPE, value, absolute))
4136 SetChangedFlag (true);
4137 break;
4139 case F_SelectedElements:
4140 if (ChangeSelectedSize (ELEMENT_TYPE, value, absolute))
4141 SetChangedFlag (true);
4142 break;
4144 case F_Selected:
4145 case F_SelectedObjects:
4146 if (ChangeSelectedSize (CHANGESIZE_TYPES, value, absolute))
4147 SetChangedFlag (true);
4148 break;
4151 return 0;
4154 /* --------------------------------------------------------------------------- */
4156 static const char changedrillsize_syntax[] =
4157 N_("ChangeDrillSize(Object, delta)\n"
4158 "ChangeDrillSize(SelectedPins|SelectedVias|Selected|SelectedObjects, delta)");
4160 static const char changedrillsize_help[] =
4161 N_("Changes the drilling hole size of objects.");
4163 /* %start-doc actions ChangeDrillSize
4165 %end-doc */
4167 static int
4168 ActionChange2ndSize (int argc, char **argv, Coord x, Coord y)
4170 char *function = ARG (0);
4171 char *delta = ARG (1);
4172 char *units = ARG (2);
4173 bool absolute;
4174 Coord value;
4176 if (function && delta)
4178 value = GetValue (delta, units, &absolute);
4179 switch (GetFunctionID (function))
4181 case F_Object:
4183 int type;
4184 void *ptr1, *ptr2, *ptr3;
4186 gui->get_coords (_("Select an Object"), &x, &y);
4187 if ((type =
4188 SearchScreen (x, y, CHANGE2NDSIZE_TYPES,
4189 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4190 if (ChangeObject2ndSize
4191 (type, ptr1, ptr2, ptr3, value, absolute, true))
4192 SetChangedFlag (true);
4193 break;
4196 case F_SelectedVias:
4197 if (ChangeSelected2ndSize (VIA_TYPE, value, absolute))
4198 SetChangedFlag (true);
4199 break;
4201 case F_SelectedPins:
4202 if (ChangeSelected2ndSize (PIN_TYPE, value, absolute))
4203 SetChangedFlag (true);
4204 break;
4205 case F_Selected:
4206 case F_SelectedObjects:
4207 if (ChangeSelected2ndSize (PIN_TYPES, value, absolute))
4208 SetChangedFlag (true);
4209 break;
4212 return 0;
4215 /* --------------------------------------------------------------------------- */
4217 static const char changeclearsize_syntax[] =
4218 N_("ChangeClearSize(Object, delta)\n"
4219 "ChangeClearSize(SelectedPins|SelectedPads|SelectedVias, delta)\n"
4220 "ChangeClearSize(SelectedLines|SelectedArcs, delta\n"
4221 "ChangeClearSize(Selected|SelectedObjects, delta)");
4223 static const char changeclearsize_help[] =
4224 N_("Changes the clearance size of objects.");
4226 /* %start-doc actions ChangeClearSize
4228 If the solder mask is currently showing, this action changes the
4229 solder mask clearance. If the mask is not showing, this action
4230 changes the polygon clearance.
4232 %end-doc */
4234 static int
4235 ActionChangeClearSize (int argc, char **argv, Coord x, Coord y)
4237 char *function = ARG (0);
4238 char *delta = ARG (1);
4239 char *units = ARG (2);
4240 bool absolute;
4241 Coord value;
4243 if (function && delta)
4245 value = 2 * GetValue (delta, units, &absolute);
4246 if (value == 0)
4247 value = delta[0] == '-' ? -Settings.increments->clear
4248 : Settings.increments->clear;
4249 switch (GetFunctionID (function))
4251 case F_Object:
4253 int type;
4254 void *ptr1, *ptr2, *ptr3;
4256 gui->get_coords (_("Select an Object"), &x, &y);
4257 if ((type =
4258 SearchScreen (x, y,
4259 CHANGECLEARSIZE_TYPES, &ptr1, &ptr2,
4260 &ptr3)) != NO_TYPE)
4261 if (ChangeObjectClearSize (type, ptr1, ptr2, ptr3, value, absolute))
4262 SetChangedFlag (true);
4263 break;
4265 case F_SelectedVias:
4266 if (ChangeSelectedClearSize (VIA_TYPE, value, absolute))
4267 SetChangedFlag (true);
4268 break;
4269 case F_SelectedPads:
4270 if (ChangeSelectedClearSize (PAD_TYPE, value, absolute))
4271 SetChangedFlag (true);
4272 break;
4273 case F_SelectedPins:
4274 if (ChangeSelectedClearSize (PIN_TYPE, value, absolute))
4275 SetChangedFlag (true);
4276 break;
4277 case F_SelectedLines:
4278 if (ChangeSelectedClearSize (LINE_TYPE, value, absolute))
4279 SetChangedFlag (true);
4280 break;
4281 case F_SelectedArcs:
4282 if (ChangeSelectedClearSize (ARC_TYPE, value, absolute))
4283 SetChangedFlag (true);
4284 break;
4285 case F_Selected:
4286 case F_SelectedObjects:
4287 if (ChangeSelectedClearSize (CHANGECLEARSIZE_TYPES, value, absolute))
4288 SetChangedFlag (true);
4289 break;
4292 return 0;
4295 /* --------------------------------------------------------------------------- */
4297 static const char minmaskgap_syntax[] =
4298 N_("MinMaskGap(delta)\n"
4299 "MinMaskGap(Selected, delta)");
4301 static const char minmaskgap_help[] =
4302 N_("Ensures the mask is a minimum distance from pins and pads.");
4304 /* %start-doc actions MinMaskGap
4306 Checks all specified pins and/or pads, and increases the mask if
4307 needed to ensure a minimum distance between the pin or pad edge and
4308 the mask edge.
4310 %end-doc */
4312 static int
4313 ActionMinMaskGap (int argc, char **argv, Coord x, Coord y)
4315 char *function = ARG (0);
4316 char *delta = ARG (1);
4317 char *units = ARG (2);
4318 bool absolute;
4319 Coord value;
4320 Coord thickness;
4321 int flags;
4323 if (!function)
4324 return 1;
4325 if (strcasecmp (function, "Selected") == 0)
4326 flags = SELECTEDFLAG;
4327 else
4329 units = delta;
4330 delta = function;
4331 flags = 0;
4333 value = 2 * GetValue (delta, units, &absolute);
4335 SaveUndoSerialNumber ();
4336 ELEMENT_LOOP (PCB->Data);
4338 PIN_LOOP (element);
4340 if (!TEST_FLAGS (flags, pin) || ! pin->Mask)
4341 continue;
4343 thickness = pin->DrillingHole;
4344 if (pin->Thickness > thickness)
4345 thickness = pin->Thickness;
4346 thickness += value;
4348 if (pin->Mask < thickness)
4350 ChangeObjectMaskSize (PIN_TYPE, element, pin, 0, thickness, 1);
4351 RestoreUndoSerialNumber ();
4354 END_LOOP;
4355 PAD_LOOP (element);
4357 if (!TEST_FLAGS (flags, pad) || ! pad->Mask)
4358 continue;
4359 if (pad->Mask < pad->Thickness + value)
4361 ChangeObjectMaskSize (PAD_TYPE, element, pad, 0,
4362 pad->Thickness + value, 1);
4363 RestoreUndoSerialNumber ();
4366 END_LOOP;
4368 END_LOOP;
4369 VIA_LOOP (PCB->Data);
4371 if (!TEST_FLAGS (flags, via) || ! via->Mask)
4372 continue;
4374 thickness = via->DrillingHole;
4375 if (via->Thickness > thickness)
4376 thickness = via->Thickness;
4377 thickness += value;
4379 if (via->Mask < thickness)
4381 ChangeObjectMaskSize (VIA_TYPE, via, 0, 0, thickness, 1);
4382 RestoreUndoSerialNumber ();
4385 END_LOOP;
4386 RestoreUndoSerialNumber ();
4387 IncrementUndoSerialNumber ();
4388 return 0;
4391 /* --------------------------------------------------------------------------- */
4393 static const char mincleargap_syntax[] =
4394 N_("MinClearGap(delta)\n"
4395 "MinClearGap(Selected, delta)");
4397 static const char mincleargap_help[] =
4398 N_("Ensures that polygons are a minimum distance from objects.");
4400 /* %start-doc actions MinClearGap
4402 Checks all specified objects, and increases the polygon clearance if
4403 needed to ensure a minimum distance between their edges and the
4404 polygon edges.
4406 %end-doc */
4408 static int
4409 ActionMinClearGap (int argc, char **argv, Coord x, Coord y)
4411 char *function = ARG (0);
4412 char *delta = ARG (1);
4413 char *units = ARG (2);
4414 bool absolute;
4415 Coord value;
4416 int flags;
4418 if (!function)
4419 return 1;
4420 if (strcasecmp (function, "Selected") == 0)
4421 flags = SELECTEDFLAG;
4422 else
4424 units = delta;
4425 delta = function;
4426 flags = 0;
4428 value = 2 * GetValue (delta, units, &absolute);
4430 SaveUndoSerialNumber ();
4431 ELEMENT_LOOP (PCB->Data);
4433 PIN_LOOP (element);
4435 if (!TEST_FLAGS (flags, pin))
4436 continue;
4437 if (pin->Clearance < value)
4439 ChangeObjectClearSize (PIN_TYPE, element, pin, 0,
4440 value, 1);
4441 RestoreUndoSerialNumber ();
4444 END_LOOP;
4445 PAD_LOOP (element);
4447 if (!TEST_FLAGS (flags, pad))
4448 continue;
4449 if (pad->Clearance < value)
4451 ChangeObjectClearSize (PAD_TYPE, element, pad, 0,
4452 value, 1);
4453 RestoreUndoSerialNumber ();
4456 END_LOOP;
4458 END_LOOP;
4459 VIA_LOOP (PCB->Data);
4461 if (!TEST_FLAGS (flags, via))
4462 continue;
4463 if (via->Clearance < value)
4465 ChangeObjectClearSize (VIA_TYPE, via, 0, 0, value, 1);
4466 RestoreUndoSerialNumber ();
4469 END_LOOP;
4470 ALLLINE_LOOP (PCB->Data);
4472 if (!TEST_FLAGS (flags, line))
4473 continue;
4474 if (line->Clearance < value)
4476 ChangeObjectClearSize (LINE_TYPE, layer, line, 0, value, 1);
4477 RestoreUndoSerialNumber ();
4480 ENDALL_LOOP;
4481 ALLARC_LOOP (PCB->Data);
4483 if (!TEST_FLAGS (flags, arc))
4484 continue;
4485 if (arc->Clearance < value)
4487 ChangeObjectClearSize (ARC_TYPE, layer, arc, 0, value, 1);
4488 RestoreUndoSerialNumber ();
4491 ENDALL_LOOP;
4492 RestoreUndoSerialNumber ();
4493 IncrementUndoSerialNumber ();
4494 return 0;
4497 /* --------------------------------------------------------------------------- */
4499 static const char changepinname_syntax[] =
4500 N_("ChangePinName(ElementName,PinNumber,PinName)");
4502 static const char changepinname_help[] =
4503 N_("Sets the name of a specific pin on a specific element.");
4505 /* %start-doc actions ChangePinName
4507 This can be especially useful for annotating pin names from a
4508 schematic to the layout without requiring knowledge of the pcb file
4509 format.
4511 @example
4512 ChangePinName(U3, 7, VCC)
4513 @end example
4515 %end-doc */
4517 static int
4518 ActionChangePinName (int argc, char **argv, Coord x, Coord y)
4520 int changed = 0;
4521 char *refdes, *pinnum, *pinname;
4523 if (argc != 3)
4525 AFAIL (changepinname);
4528 refdes = argv[0];
4529 pinnum = argv[1];
4530 pinname = argv[2];
4532 ELEMENT_LOOP (PCB->Data);
4534 if (NSTRCMP (refdes, NAMEONPCB_NAME (element)) == 0)
4536 PIN_LOOP (element);
4538 if (NSTRCMP (pinnum, pin->Number) == 0)
4540 AddObjectToChangeNameUndoList (PIN_TYPE, NULL, NULL,
4541 pin, pin->Name);
4543 * Note: we can't free() pin->Name first because
4544 * it is used in the undo list
4546 pin->Name = strdup (pinname);
4547 SetChangedFlag (true);
4548 changed = 1;
4551 END_LOOP;
4553 PAD_LOOP (element);
4555 if (NSTRCMP (pinnum, pad->Number) == 0)
4557 AddObjectToChangeNameUndoList (PAD_TYPE, NULL, NULL,
4558 pad, pad->Name);
4560 * Note: we can't free() pad->Name first because
4561 * it is used in the undo list
4563 pad->Name = strdup (pinname);
4564 SetChangedFlag (true);
4565 changed = 1;
4568 END_LOOP;
4571 END_LOOP;
4573 * done with our action so increment the undo # if we actually
4574 * changed anything
4576 if (changed)
4578 if (defer_updates)
4579 defer_needs_update = 1;
4580 else
4582 IncrementUndoSerialNumber ();
4583 gui->invalidate_all ();
4587 return 0;
4590 /* --------------------------------------------------------------------------- */
4592 static const char changename_syntax[] =
4593 N_("ChangeName(Object)\n"
4594 "ChangeName(Layout|Layer)");
4596 static const char changename_help[] = N_("Sets the name of objects.");
4598 /* %start-doc actions ChangeName
4600 @table @code
4602 @item Object
4603 Changes the name of the element under the cursor.
4605 @item Layout
4606 Changes the name of the layout. This is printed on the fab drawings.
4608 @item Layer
4609 Changes the name of the currently active layer.
4611 @end table
4613 %end-doc */
4616 ActionChangeName (int argc, char **argv, Coord x, Coord y)
4618 char *function = ARG (0);
4619 char *name;
4621 if (function)
4623 switch (GetFunctionID (function))
4625 /* change the name of an object */
4626 case F_Object:
4628 int type;
4629 void *ptr1, *ptr2, *ptr3;
4631 gui->get_coords (_("Select an Object"), &x, &y);
4632 if ((type =
4633 SearchScreen (x, y, CHANGENAME_TYPES,
4634 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4636 SaveUndoSerialNumber ();
4637 if (QueryInputAndChangeObjectName (type, ptr1, ptr2, ptr3))
4639 SetChangedFlag (true);
4640 if (type == ELEMENT_TYPE)
4642 RubberbandType *ptr;
4643 int i;
4645 RestoreUndoSerialNumber ();
4646 Crosshair.AttachedObject.RubberbandN = 0;
4647 LookupRatLines (type, ptr1, ptr2, ptr3);
4648 ptr = Crosshair.AttachedObject.Rubberband;
4649 for (i = 0; i < Crosshair.AttachedObject.RubberbandN;
4650 i++, ptr++)
4652 if (PCB->RatOn)
4653 EraseRat ((RatType *) ptr->Line);
4654 MoveObjectToRemoveUndoList (RATLINE_TYPE,
4655 ptr->Line, ptr->Line,
4656 ptr->Line);
4658 IncrementUndoSerialNumber ();
4659 Draw ();
4663 break;
4666 /* change the layout's name */
4667 case F_Layout:
4668 name =
4669 gui->prompt_for (_("Enter the layout name:"), EMPTY (PCB->Name));
4670 /* NB: ChangeLayoutName takes ownership of the passed memory */
4671 if (name && ChangeLayoutName (name))
4672 SetChangedFlag (true);
4673 break;
4675 /* change the name of the active layer */
4676 case F_Layer:
4677 name = gui->prompt_for (_("Enter the layer name:"),
4678 EMPTY (CURRENT->Name));
4679 /* NB: ChangeLayerName takes ownership of the passed memory */
4680 if (name && ChangeLayerName (CURRENT, name))
4681 SetChangedFlag (true);
4682 break;
4685 return 0;
4689 /* --------------------------------------------------------------------------- */
4691 static const char morphpolygon_syntax[] = N_("MorphPolygon(Object|Selected)");
4693 static const char morphpolygon_help[] =
4694 N_("Converts dead polygon islands into separate polygons.");
4696 /* %start-doc actions MorphPolygon
4698 If a polygon is divided into unconnected "islands", you can use
4699 this command to convert the otherwise disappeared islands into
4700 separate polygons. Be sure the cursor is over a portion of the
4701 polygon that remains visible. Very small islands that may flake
4702 off are automatically deleted.
4704 %end-doc */
4706 static int
4707 ActionMorphPolygon (int argc, char **argv, Coord x, Coord y)
4709 char *function = ARG (0);
4710 if (function)
4712 switch (GetFunctionID (function))
4714 case F_Object:
4716 int type;
4717 void *ptr1, *ptr2, *ptr3;
4719 gui->get_coords (_("Select an Object"), &x, &y);
4720 if ((type = SearchScreen (x, y, POLYGON_TYPE,
4721 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4723 MorphPolygon ((LayerType *) ptr1, (PolygonType *) ptr3);
4724 Draw ();
4725 IncrementUndoSerialNumber ();
4727 break;
4729 case F_Selected:
4730 case F_SelectedObjects:
4731 ALLPOLYGON_LOOP (PCB->Data);
4733 if (TEST_FLAG (SELECTEDFLAG, polygon))
4734 MorphPolygon (layer, polygon);
4736 ENDALL_LOOP;
4737 Draw ();
4738 IncrementUndoSerialNumber ();
4739 break;
4742 return 0;
4745 /* --------------------------------------------------------------------------- */
4747 static const char togglehidename_syntax[] =
4748 N_("ToggleHideName(Object|SelectedElements)");
4750 static const char togglehidename_help[] =
4751 N_("Toggles the visibility of element names.");
4753 /* %start-doc actions ToggleHideName
4755 If names are hidden you won't see them on the screen and they will not
4756 appear on the silk layer when you print the layout.
4758 %end-doc */
4760 static int
4761 ActionToggleHideName (int argc, char **argv, Coord x, Coord y)
4763 char *function = ARG (0);
4764 if (function && PCB->ElementOn)
4766 switch (GetFunctionID (function))
4768 case F_Object:
4770 int type;
4771 void *ptr1, *ptr2, *ptr3;
4773 gui->get_coords (_("Select an Object"), &x, &y);
4774 if ((type = SearchScreen (x, y, ELEMENT_TYPE,
4775 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4777 AddObjectToFlagUndoList (type, ptr1, ptr2, ptr3);
4778 EraseElementName ((ElementType *) ptr2);
4779 TOGGLE_FLAG (HIDENAMEFLAG, (ElementType *) ptr2);
4780 DrawElementName ((ElementType *) ptr2);
4781 Draw ();
4782 IncrementUndoSerialNumber ();
4784 break;
4786 case F_SelectedElements:
4787 case F_Selected:
4789 bool changed = false;
4790 ELEMENT_LOOP (PCB->Data);
4792 if ((TEST_FLAG (SELECTEDFLAG, element) ||
4793 TEST_FLAG (SELECTEDFLAG,
4794 &NAMEONPCB_TEXT (element)))
4795 && (FRONT (element) || PCB->InvisibleObjectsOn))
4797 AddObjectToFlagUndoList (ELEMENT_TYPE, element,
4798 element, element);
4799 EraseElementName (element);
4800 TOGGLE_FLAG (HIDENAMEFLAG, element);
4801 DrawElementName (element);
4802 changed = true;
4805 END_LOOP;
4806 if (changed)
4808 Draw ();
4809 IncrementUndoSerialNumber ();
4814 return 0;
4817 /* --------------------------------------------------------------------------- */
4819 static const char changejoin_syntax[] =
4820 N_("ChangeJoin(ToggleObject|SelectedLines|SelectedArcs|Selected)");
4822 static const char changejoin_help[] =
4823 N_("Changes the join (clearance through polygons) of objects.");
4825 /* %start-doc actions ChangeJoin
4827 The join flag determines whether a line or arc, drawn to intersect a
4828 polygon, electrically connects to the polygon or not. When joined,
4829 the line/arc is simply drawn over the polygon, making an electrical
4830 connection. When not joined, a gap is drawn between the line and the
4831 polygon, insulating them from each other.
4833 %end-doc */
4835 static int
4836 ActionChangeJoin (int argc, char **argv, Coord x, Coord y)
4838 char *function = ARG (0);
4839 if (function)
4841 switch (GetFunctionID (function))
4843 case F_ToggleObject:
4844 case F_Object:
4846 int type;
4847 void *ptr1, *ptr2, *ptr3;
4849 gui->get_coords (_("Select an Object"), &x, &y);
4850 if ((type =
4851 SearchScreen (x, y, CHANGEJOIN_TYPES,
4852 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4853 if (ChangeObjectJoin (type, ptr1, ptr2, ptr3))
4854 SetChangedFlag (true);
4855 break;
4858 case F_SelectedLines:
4859 if (ChangeSelectedJoin (LINE_TYPE))
4860 SetChangedFlag (true);
4861 break;
4863 case F_SelectedArcs:
4864 if (ChangeSelectedJoin (ARC_TYPE))
4865 SetChangedFlag (true);
4866 break;
4868 case F_Selected:
4869 case F_SelectedObjects:
4870 if (ChangeSelectedJoin (CHANGEJOIN_TYPES))
4871 SetChangedFlag (true);
4872 break;
4875 return 0;
4878 /* --------------------------------------------------------------------------- */
4880 static const char changesquare_syntax[] =
4881 N_("ChangeSquare(ToggleObject)\n"
4882 "ChangeSquare(SelectedElements|SelectedPins)\n"
4883 "ChangeSquare(Selected|SelectedObjects)");
4885 static const char changesquare_help[] =
4886 N_("Changes the square flag of pins and pads.");
4888 /* %start-doc actions ChangeSquare
4890 Note that @code{Pins} means both pins and pads.
4892 @pinshapes
4894 %end-doc */
4896 static int
4897 ActionChangeSquare (int argc, char **argv, Coord x, Coord y)
4899 char *function = ARG (0);
4900 if (function)
4902 switch (GetFunctionID (function))
4904 case F_ToggleObject:
4905 case F_Object:
4907 int type;
4908 void *ptr1, *ptr2, *ptr3;
4910 gui->get_coords (_("Select an Object"), &x, &y);
4911 if ((type =
4912 SearchScreen (x, y, CHANGESQUARE_TYPES,
4913 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4914 if (ChangeObjectSquare (type, ptr1, ptr2, ptr3))
4915 SetChangedFlag (true);
4916 break;
4919 case F_SelectedElements:
4920 if (ChangeSelectedSquare (ELEMENT_TYPE))
4921 SetChangedFlag (true);
4922 break;
4924 case F_SelectedPins:
4925 if (ChangeSelectedSquare (PIN_TYPE | PAD_TYPE))
4926 SetChangedFlag (true);
4927 break;
4929 case F_Selected:
4930 case F_SelectedObjects:
4931 if (ChangeSelectedSquare (PIN_TYPE | PAD_TYPE))
4932 SetChangedFlag (true);
4933 break;
4936 return 0;
4939 /* --------------------------------------------------------------------------- */
4941 static const char setsquare_syntax[] =
4942 N_("SetSquare(ToggleObject|SelectedElements|SelectedPins)");
4944 static const char setsquare_help[] = N_("sets the square-flag of objects.");
4946 /* %start-doc actions SetSquare
4948 Note that @code{Pins} means pins and pads.
4950 @pinshapes
4952 %end-doc */
4954 static int
4955 ActionSetSquare (int argc, char **argv, Coord x, Coord y)
4957 char *function = ARG (0);
4958 if (function && *function)
4960 switch (GetFunctionID (function))
4962 case F_ToggleObject:
4963 case F_Object:
4965 int type;
4966 void *ptr1, *ptr2, *ptr3;
4968 gui->get_coords (_("Select an Object"), &x, &y);
4969 if ((type =
4970 SearchScreen (x, y, CHANGESQUARE_TYPES,
4971 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4972 if (SetObjectSquare (type, ptr1, ptr2, ptr3))
4973 SetChangedFlag (true);
4974 break;
4977 case F_SelectedElements:
4978 if (SetSelectedSquare (ELEMENT_TYPE))
4979 SetChangedFlag (true);
4980 break;
4982 case F_SelectedPins:
4983 if (SetSelectedSquare (PIN_TYPE | PAD_TYPE))
4984 SetChangedFlag (true);
4985 break;
4987 case F_Selected:
4988 case F_SelectedObjects:
4989 if (SetSelectedSquare (PIN_TYPE | PAD_TYPE))
4990 SetChangedFlag (true);
4991 break;
4994 return 0;
4997 /* --------------------------------------------------------------------------- */
4999 static const char clearsquare_syntax[] =
5000 N_("ClearSquare(ToggleObject|SelectedElements|SelectedPins)");
5002 static const char clearsquare_help[] =
5003 N_("Clears the square-flag of pins and pads.");
5005 /* %start-doc actions ClearSquare
5007 Note that @code{Pins} means pins and pads.
5009 @pinshapes
5011 %end-doc */
5013 static int
5014 ActionClearSquare (int argc, char **argv, Coord x, Coord y)
5016 char *function = ARG (0);
5017 if (function && *function)
5019 switch (GetFunctionID (function))
5021 case F_ToggleObject:
5022 case F_Object:
5024 int type;
5025 void *ptr1, *ptr2, *ptr3;
5027 gui->get_coords (_("Select an Object"), &x, &y);
5028 if ((type =
5029 SearchScreen (x, y, CHANGESQUARE_TYPES,
5030 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5031 if (ClrObjectSquare (type, ptr1, ptr2, ptr3))
5032 SetChangedFlag (true);
5033 break;
5036 case F_SelectedElements:
5037 if (ClrSelectedSquare (ELEMENT_TYPE))
5038 SetChangedFlag (true);
5039 break;
5041 case F_SelectedPins:
5042 if (ClrSelectedSquare (PIN_TYPE | PAD_TYPE))
5043 SetChangedFlag (true);
5044 break;
5046 case F_Selected:
5047 case F_SelectedObjects:
5048 if (ClrSelectedSquare (PIN_TYPE | PAD_TYPE))
5049 SetChangedFlag (true);
5050 break;
5053 return 0;
5056 /* --------------------------------------------------------------------------- */
5058 static const char changeoctagon_syntax[] =
5059 N_("ChangeOctagon(Object|ToggleObject|SelectedObjects|Selected)\n"
5060 "ChangeOctagon(SelectedElements|SelectedPins|SelectedVias)");
5062 static const char changeoctagon_help[] =
5063 N_("Changes the octagon-flag of pins and vias.");
5065 /* %start-doc actions ChangeOctagon
5067 @pinshapes
5069 %end-doc */
5071 static int
5072 ActionChangeOctagon (int argc, char **argv, Coord x, Coord y)
5074 char *function = ARG (0);
5075 if (function)
5077 switch (GetFunctionID (function))
5079 case F_ToggleObject:
5080 case F_Object:
5082 int type;
5083 void *ptr1, *ptr2, *ptr3;
5085 gui->get_coords (_("Select an Object"), &x, &y);
5086 if ((type =
5087 SearchScreen (x, y, CHANGEOCTAGON_TYPES,
5088 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5089 if (ChangeObjectOctagon (type, ptr1, ptr2, ptr3))
5090 SetChangedFlag (true);
5091 break;
5094 case F_SelectedElements:
5095 if (ChangeSelectedOctagon (ELEMENT_TYPE))
5096 SetChangedFlag (true);
5097 break;
5099 case F_SelectedPins:
5100 if (ChangeSelectedOctagon (PIN_TYPE))
5101 SetChangedFlag (true);
5102 break;
5104 case F_SelectedVias:
5105 if (ChangeSelectedOctagon (VIA_TYPE))
5106 SetChangedFlag (true);
5107 break;
5109 case F_Selected:
5110 case F_SelectedObjects:
5111 if (ChangeSelectedOctagon (PIN_TYPES))
5112 SetChangedFlag (true);
5113 break;
5116 return 0;
5119 /* --------------------------------------------------------------------------- */
5121 static const char setoctagon_syntax[] =
5122 N_("SetOctagon(Object|ToggleObject|SelectedElements|Selected)");
5124 static const char setoctagon_help[] = N_("Sets the octagon-flag of objects.");
5126 /* %start-doc actions SetOctagon
5128 @pinshapes
5130 %end-doc */
5132 static int
5133 ActionSetOctagon (int argc, char **argv, Coord x, Coord y)
5135 char *function = ARG (0);
5136 if (function)
5138 switch (GetFunctionID (function))
5140 case F_ToggleObject:
5141 case F_Object:
5143 int type;
5144 void *ptr1, *ptr2, *ptr3;
5146 gui->get_coords (_("Select an Object"), &x, &y);
5147 if ((type =
5148 SearchScreen (x, y, CHANGEOCTAGON_TYPES,
5149 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5150 if (SetObjectOctagon (type, ptr1, ptr2, ptr3))
5151 SetChangedFlag (true);
5152 break;
5155 case F_SelectedElements:
5156 if (SetSelectedOctagon (ELEMENT_TYPE))
5157 SetChangedFlag (true);
5158 break;
5160 case F_SelectedPins:
5161 if (SetSelectedOctagon (PIN_TYPE))
5162 SetChangedFlag (true);
5163 break;
5165 case F_SelectedVias:
5166 if (SetSelectedOctagon (VIA_TYPE))
5167 SetChangedFlag (true);
5168 break;
5170 case F_Selected:
5171 case F_SelectedObjects:
5172 if (SetSelectedOctagon (PIN_TYPES))
5173 SetChangedFlag (true);
5174 break;
5177 return 0;
5180 /* --------------------------------------------------------------------------- */
5182 static const char clearoctagon_syntax[] =
5183 N_("ClearOctagon(ToggleObject|Object|SelectedObjects|Selected)\n"
5184 "ClearOctagon(SelectedElements|SelectedPins|SelectedVias)");
5186 static const char clearoctagon_help[] =
5187 N_("Clears the octagon-flag of pins and vias.");
5189 /* %start-doc actions ClearOctagon
5191 @pinshapes
5193 %end-doc */
5195 static int
5196 ActionClearOctagon (int argc, char **argv, Coord x, Coord y)
5198 char *function = ARG (0);
5199 if (function)
5201 switch (GetFunctionID (function))
5203 case F_ToggleObject:
5204 case F_Object:
5206 int type;
5207 void *ptr1, *ptr2, *ptr3;
5209 gui->get_coords (_("Select an Object"), &x, &y);
5210 if ((type =
5211 SearchScreen (Crosshair.X, Crosshair.Y, CHANGEOCTAGON_TYPES,
5212 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5213 if (ClrObjectOctagon (type, ptr1, ptr2, ptr3))
5214 SetChangedFlag (true);
5215 break;
5218 case F_SelectedElements:
5219 if (ClrSelectedOctagon (ELEMENT_TYPE))
5220 SetChangedFlag (true);
5221 break;
5223 case F_SelectedPins:
5224 if (ClrSelectedOctagon (PIN_TYPE))
5225 SetChangedFlag (true);
5226 break;
5228 case F_SelectedVias:
5229 if (ClrSelectedOctagon (VIA_TYPE))
5230 SetChangedFlag (true);
5231 break;
5233 case F_Selected:
5234 case F_SelectedObjects:
5235 if (ClrSelectedOctagon (PIN_TYPES))
5236 SetChangedFlag (true);
5237 break;
5240 return 0;
5243 /* --------------------------------------------------------------------------- */
5245 static const char changehold_syntax[] =
5246 N_("ChangeHole(ToggleObject|Object|SelectedVias|Selected)");
5248 static const char changehold_help[] = N_("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, Coord x, Coord y)
5260 char *function = ARG (0);
5261 if (function)
5263 switch (GetFunctionID (function))
5265 case F_ToggleObject:
5266 case F_Object:
5268 int type;
5269 void *ptr1, *ptr2, *ptr3;
5271 gui->get_coords (_("Select an Object"), &x, &y);
5272 if ((type = SearchScreen (x, y, VIA_TYPE,
5273 &ptr1, &ptr2, &ptr3)) != NO_TYPE
5274 && ChangeHole ((PinType *) ptr3))
5275 IncrementUndoSerialNumber ();
5276 break;
5279 case F_SelectedVias:
5280 case F_Selected:
5281 if (ChangeSelectedHole ())
5282 SetChangedFlag (true);
5283 break;
5286 return 0;
5289 /* --------------------------------------------------------------------------- */
5291 static const char changepaste_syntax[] =
5292 N_("ChangePaste(ToggleObject|Object|SelectedPads|Selected)");
5294 static const char changepaste_help[] =
5295 N_("Changes the no paste flag of objects.");
5297 /* %start-doc actions ChangePaste
5299 The "no paste flag" of a pad determines whether the solderpaste
5300 stencil will have an opening for the pad (no set) or if there wil be
5301 no solderpaste on the pad (set). This is used for things such as
5302 fiducial pads.
5304 %end-doc */
5306 static int
5307 ActionChangePaste (int argc, char **argv, Coord x, Coord y)
5309 char *function = ARG (0);
5310 if (function)
5312 switch (GetFunctionID (function))
5314 case F_ToggleObject:
5315 case F_Object:
5317 int type;
5318 void *ptr1, *ptr2, *ptr3;
5320 gui->get_coords (_("Select an Object"), &x, &y);
5321 if ((type = SearchScreen (x, y, PAD_TYPE,
5322 &ptr1, &ptr2, &ptr3)) != NO_TYPE
5323 && ChangePaste ((PadType *) ptr3))
5324 IncrementUndoSerialNumber ();
5325 break;
5328 case F_SelectedPads:
5329 case F_Selected:
5330 if (ChangeSelectedPaste ())
5331 SetChangedFlag (true);
5332 break;
5335 return 0;
5338 /* --------------------------------------------------------------------------- */
5340 static const char select_syntax[] =
5341 N_("Select(Object|ToggleObject)\n"
5342 "Select(All|Block|Connection)\n"
5343 "Select(ElementByName|ObjectByName|PadByName|PinByName)\n"
5344 "Select(ElementByName|ObjectByName|PadByName|PinByName, Name)\n"
5345 "Select(TextByName|ViaByName|NetByName)\n"
5346 "Select(TextByName|ViaByName|NetByName, Name)\n"
5347 "Select(Convert)");
5349 static const char select_help[] = N_("Toggles or sets the selection.");
5351 /* %start-doc actions Select
5353 @table @code
5355 @item ElementByName
5356 @item ObjectByName
5357 @item PadByName
5358 @item PinByName
5359 @item TextByName
5360 @item ViaByName
5361 @item NetByName
5363 These all rely on having a regular expression parser built into
5364 @code{pcb}. If the name is not specified then the user is prompted
5365 for a pattern, and all objects that match the pattern and are of the
5366 type specified are selected.
5368 @item Object
5369 @item ToggleObject
5370 Selects the object under the cursor.
5372 @item Block
5373 Selects all objects in a rectangle indicated by the cursor.
5375 @item All
5376 Selects all objects on the board.
5378 @item Found
5379 Selects all connections with the ``found'' flag set.
5381 @item Connection
5382 Selects all connections with the ``connected'' flag set.
5384 @item Convert
5385 Converts the selected objects to an element. This uses the highest
5386 numbered paste buffer.
5388 @end table
5390 %end-doc */
5392 static int
5393 ActionSelect (int argc, char **argv, Coord x, Coord y)
5395 char *function = ARG (0);
5396 if (function)
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;
5421 case F_NetByName:
5422 type = NET_TYPE;
5423 goto commonByName;
5425 commonByName:
5427 char *pattern = ARG (1);
5429 if (pattern
5430 || (pattern =
5431 gui->prompt_for (_("Enter pattern:"), "")) != NULL)
5433 if (SelectObjectByName (type, pattern, true))
5434 SetChangedFlag (true);
5435 if (ARG (1) == NULL)
5436 free (pattern);
5438 break;
5440 #endif /* defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP) */
5442 /* select a single object */
5443 case F_ToggleObject:
5444 case F_Object:
5445 if (SelectObject ())
5446 SetChangedFlag (true);
5447 break;
5449 /* all objects in block */
5450 case F_Block:
5452 BoxType box;
5454 box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
5455 Crosshair.AttachedBox.Point2.X);
5456 box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
5457 Crosshair.AttachedBox.Point2.Y);
5458 box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
5459 Crosshair.AttachedBox.Point2.X);
5460 box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
5461 Crosshair.AttachedBox.Point2.Y);
5462 notify_crosshair_change (false);
5463 NotifyBlock ();
5464 if (Crosshair.AttachedBox.State == STATE_THIRD &&
5465 SelectBlock (&box, true))
5467 SetChangedFlag (true);
5468 Crosshair.AttachedBox.State = STATE_FIRST;
5470 notify_crosshair_change (true);
5471 break;
5474 /* select all visible objects */
5475 case F_All:
5477 BoxType box;
5479 box.X1 = -MAX_COORD;
5480 box.Y1 = -MAX_COORD;
5481 box.X2 = MAX_COORD;
5482 box.Y2 = MAX_COORD;
5483 if (SelectBlock (&box, true))
5484 SetChangedFlag (true);
5485 break;
5488 /* all logical connections */
5489 case F_Found:
5490 if (SelectByFlag (FOUNDFLAG, true))
5492 Draw ();
5493 IncrementUndoSerialNumber ();
5494 SetChangedFlag (true);
5496 break;
5498 /* all physical connections */
5499 case F_Connection:
5500 if (SelectByFlag (CONNECTEDFLAG, true))
5502 Draw ();
5503 IncrementUndoSerialNumber ();
5504 SetChangedFlag (true);
5506 break;
5508 case F_Convert:
5510 Coord x, y;
5511 Note.Buffer = Settings.BufferNumber;
5512 SetBufferNumber (MAX_BUFFER - 1);
5513 ClearBuffer (PASTEBUFFER);
5514 gui->get_coords (_("Select the Element's Mark Location"), &x, &y);
5515 x = GridFit (x, PCB->Grid, PCB->GridOffsetX);
5516 y = GridFit (y, PCB->Grid, PCB->GridOffsetY);
5517 AddSelectedToBuffer (PASTEBUFFER, x, y, true);
5518 SaveUndoSerialNumber ();
5519 RemoveSelected ();
5520 ConvertBufferToElement (PASTEBUFFER);
5521 RestoreUndoSerialNumber ();
5522 CopyPastebufferToLayout (x, y);
5523 SetBufferNumber (Note.Buffer);
5525 break;
5527 default:
5528 AFAIL (select);
5529 break;
5532 return 0;
5535 /* FLAG(have_regex,FlagHaveRegex,0) */
5537 FlagHaveRegex (int parm)
5539 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5540 return 1;
5541 #else
5542 return 0;
5543 #endif
5546 /* --------------------------------------------------------------------------- */
5548 static const char unselect_syntax[] =
5549 N_("Unselect(All|Block|Connection)\n"
5550 "Unselect(ElementByName|ObjectByName|PadByName|PinByName)\n"
5551 "Unselect(ElementByName|ObjectByName|PadByName|PinByName, Name)\n"
5552 "Unselect(TextByName|ViaByName)\n"
5553 "Unselect(TextByName|ViaByName, Name)\n");
5555 static const char unselect_help[] =
5556 N_("Unselects the object at the pointer location or the specified objects.");
5558 /* %start-doc actions Unselect
5560 @table @code
5562 @item All
5563 Unselect all objects.
5565 @item Block
5566 Unselect all objects in a rectangle given by the cursor.
5568 @item Connection
5569 Unselect all connections with the ``found'' flag set.
5571 @item ElementByName
5572 @item ObjectByName
5573 @item PadByName
5574 @item PinByName
5575 @item TextByName
5576 @item ViaByName
5578 These all rely on having a regular expression parser built into
5579 @code{pcb}. If the name is not specified then the user is prompted
5580 for a pattern, and all objects that match the pattern and are of the
5581 type specified are unselected.
5584 @end table
5586 %end-doc */
5588 static int
5589 ActionUnselect (int argc, char **argv, Coord x, Coord y)
5591 char *function = ARG (0);
5592 if (function)
5594 switch (GetFunctionID (function))
5596 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5597 int type;
5598 /* select objects by their names */
5599 case F_ElementByName:
5600 type = ELEMENT_TYPE;
5601 goto commonByName;
5602 case F_ObjectByName:
5603 type = ALL_TYPES;
5604 goto commonByName;
5605 case F_PadByName:
5606 type = PAD_TYPE;
5607 goto commonByName;
5608 case F_PinByName:
5609 type = PIN_TYPE;
5610 goto commonByName;
5611 case F_TextByName:
5612 type = TEXT_TYPE;
5613 goto commonByName;
5614 case F_ViaByName:
5615 type = VIA_TYPE;
5616 goto commonByName;
5617 case F_NetByName:
5618 type = NET_TYPE;
5619 goto commonByName;
5621 commonByName:
5623 char *pattern = ARG (1);
5625 if (pattern
5626 || (pattern =
5627 gui->prompt_for (_("Enter pattern:"), "")) != NULL)
5629 if (SelectObjectByName (type, pattern, false))
5630 SetChangedFlag (true);
5631 if (ARG (1) == NULL)
5632 free (pattern);
5634 break;
5636 #endif /* defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP) */
5638 /* all objects in block */
5639 case F_Block:
5641 BoxType box;
5643 box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
5644 Crosshair.AttachedBox.Point2.X);
5645 box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
5646 Crosshair.AttachedBox.Point2.Y);
5647 box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
5648 Crosshair.AttachedBox.Point2.X);
5649 box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
5650 Crosshair.AttachedBox.Point2.Y);
5651 notify_crosshair_change (false);
5652 NotifyBlock ();
5653 if (Crosshair.AttachedBox.State == STATE_THIRD &&
5654 SelectBlock (&box, false))
5656 SetChangedFlag (true);
5657 Crosshair.AttachedBox.State = STATE_FIRST;
5659 notify_crosshair_change (true);
5660 break;
5663 /* unselect all visible objects */
5664 case F_All:
5666 BoxType box;
5668 box.X1 = -MAX_COORD;
5669 box.Y1 = -MAX_COORD;
5670 box.X2 = MAX_COORD;
5671 box.Y2 = MAX_COORD;
5672 if (SelectBlock (&box, false))
5673 SetChangedFlag (true);
5674 break;
5677 /* all logical connections */
5678 case F_Found:
5679 if (SelectByFlag (FOUNDFLAG, false))
5681 Draw ();
5682 IncrementUndoSerialNumber ();
5683 SetChangedFlag (true);
5685 break;
5687 /* all physical connections */
5688 case F_Connection:
5689 if (SelectByFlag (CONNECTEDFLAG, false))
5691 Draw ();
5692 IncrementUndoSerialNumber ();
5693 SetChangedFlag (true);
5695 break;
5697 default:
5698 AFAIL (unselect);
5699 break;
5703 return 0;
5706 /* --------------------------------------------------------------------------- */
5708 static const char saveto_syntax[] =
5709 N_("SaveTo(Layout|LayoutAs,filename)\n"
5710 "SaveTo(AllConnections|AllUnusedPins|ElementConnections,filename)\n"
5711 "SaveTo(PasteBuffer,filename)");
5713 static const char saveto_help[] = N_("Saves data to a file.");
5715 /* %start-doc actions SaveTo
5717 @table @code
5719 @item Layout
5720 Saves the current layout.
5722 @item LayoutAs
5723 Saves the current layout, and remembers the filename used.
5725 @item AllConnections
5726 Save all connections to a file.
5728 @item AllUnusedPins
5729 List all unused pins to a file.
5731 @item ElementConnections
5732 Save connections to the element at the cursor to a file.
5734 @item PasteBuffer
5735 Save the content of the active Buffer to a file. This is the graphical way to create a footprint.
5737 @end table
5739 %end-doc */
5741 static int
5742 ActionSaveTo (int argc, char **argv, Coord x, Coord y)
5744 char *function;
5745 char *name;
5747 function = ARG (0);
5749 if ( ! function || strcasecmp (function, "Layout") == 0)
5751 if (SavePCB (PCB->Filename) == 0)
5752 SetChangedFlag (false);
5753 return 0;
5756 if (argc != 2)
5757 AFAIL (saveto);
5759 name = argv[1];
5761 if (strcasecmp (function, "LayoutAs") == 0)
5763 if (SavePCB (name) == 0)
5765 SetChangedFlag (false);
5766 free (PCB->Filename);
5767 PCB->Filename = strdup (name);
5768 if (gui->notify_filename_changed != NULL)
5769 gui->notify_filename_changed ();
5771 return 0;
5774 if (strcasecmp (function, "AllConnections") == 0)
5776 FILE *fp;
5777 bool result;
5778 if ((fp = CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
5780 LookupConnectionsToAllElements (fp);
5781 fclose (fp);
5782 SetChangedFlag (true);
5784 return 0;
5787 if (strcasecmp (function, "AllUnusedPins") == 0)
5789 FILE *fp;
5790 bool result;
5791 if ((fp = CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
5793 LookupUnusedPins (fp);
5794 fclose (fp);
5795 SetChangedFlag (true);
5797 return 0;
5800 if (strcasecmp (function, "ElementConnections") == 0)
5802 ElementType *element;
5803 void *ptrtmp;
5804 FILE *fp;
5805 bool result;
5807 if ((SearchScreen (Crosshair.X, Crosshair.Y, ELEMENT_TYPE,
5808 &ptrtmp, &ptrtmp, &ptrtmp)) != NO_TYPE)
5810 element = (ElementType *) ptrtmp;
5811 if ((fp =
5812 CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
5814 LookupElementConnections (element, fp);
5815 fclose (fp);
5816 SetChangedFlag (true);
5819 return 0;
5822 if (strcasecmp (function, "PasteBuffer") == 0)
5824 return SaveBufferElements (name);
5827 AFAIL (saveto);
5830 /* --------------------------------------------------------------------------- */
5832 static const char savesettings_syntax[] =
5833 N_("SaveSettings()\n"
5834 "SaveSettings(local)");
5836 static const char savesettings_help[] = N_("Saves settings.");
5838 /* %start-doc actions SaveSettings
5840 If you pass no arguments, the settings are stored in
5841 @code{$HOME/.pcb/settings}. If you pass the word @code{local} they're
5842 saved in @code{./pcb.settings}.
5844 %end-doc */
5846 static int
5847 ActionSaveSettings (int argc, char **argv, Coord x, Coord y)
5849 int locally = argc > 0 ? (strncasecmp (argv[0], "local", 5) == 0) : 0;
5850 hid_save_settings (locally);
5851 return 0;
5854 /* --------------------------------------------------------------------------- */
5856 static const char loadfrom_syntax[] =
5857 N_("LoadFrom(Layout|LayoutToBuffer|ElementToBuffer|Netlist|Revert,filename)");
5859 static const char loadfrom_help[] = N_("Load layout data from a file.");
5861 /* %start-doc actions LoadFrom
5863 This action assumes you know what the filename is. The various GUIs
5864 should have a similar @code{Load} action where the filename is
5865 optional, and will provide their own file selection mechanism to let
5866 you choose the file name.
5868 @table @code
5870 @item Layout
5871 Loads an entire PCB layout, replacing the current one.
5873 @item LayoutToBuffer
5874 Loads an entire PCB layout to the paste buffer.
5876 @item ElementToBuffer
5877 Loads the given element file into the paste buffer. Element files
5878 contain only a single @code{Element} definition, such as the
5879 ``newlib'' library uses.
5881 @item Netlist
5882 Loads a new netlist, replacing any current netlist.
5884 @item Revert
5885 Re-loads the current layout from its disk file, reverting any changes
5886 you may have made.
5888 @end table
5890 %end-doc */
5892 static int
5893 ActionLoadFrom (int argc, char **argv, Coord x, Coord y)
5895 char *function;
5896 char *name;
5898 if (argc < 2)
5899 AFAIL (loadfrom);
5901 function = argv[0];
5902 name = argv[1];
5904 if (strcasecmp (function, "ElementToBuffer") == 0)
5906 notify_crosshair_change (false);
5907 if (LoadElementToBuffer (PASTEBUFFER, name, true))
5908 SetMode (PASTEBUFFER_MODE);
5909 notify_crosshair_change (true);
5912 else if (strcasecmp (function, "LayoutToBuffer") == 0)
5914 notify_crosshair_change (false);
5915 if (LoadLayoutToBuffer (PASTEBUFFER, name))
5916 SetMode (PASTEBUFFER_MODE);
5917 notify_crosshair_change (true);
5920 else if (strcasecmp (function, "Layout") == 0)
5922 if (!PCB->Changed ||
5923 gui->confirm_dialog (_("OK to override layout data?"), 0))
5924 LoadPCB (name);
5927 else if (strcasecmp (function, "Netlist") == 0)
5929 if (PCB->Netlistname)
5930 free (PCB->Netlistname);
5931 PCB->Netlistname = StripWhiteSpaceAndDup (name);
5932 FreeLibraryMemory (&PCB->NetlistLib);
5933 ImportNetlist (PCB->Netlistname);
5934 NetlistChanged (1);
5936 else if (strcasecmp (function, "Revert") == 0 && PCB->Filename
5937 && (!PCB->Changed
5938 || gui->confirm_dialog (_("OK to override changes?"), 0)))
5940 RevertPCB ();
5943 return 0;
5946 /* --------------------------------------------------------------------------- */
5948 static const char new_syntax[] = N_("New([name])");
5950 static const char new_help[] = N_("Starts a new layout.");
5952 /* %start-doc actions New
5954 If a name is not given, one is prompted for.
5956 %end-doc */
5958 static int
5959 ActionNew (int argc, char **argv, Coord x, Coord y)
5961 char *name = ARG (0);
5963 if (!PCB->Changed || gui->confirm_dialog (_("OK to clear layout data?"), 0))
5965 if (name)
5966 name = strdup (name);
5967 else
5968 name = gui->prompt_for (_("Enter the layout name:"), "");
5970 if (!name)
5971 return 1;
5973 notify_crosshair_change (false);
5974 /* do emergency saving
5975 * clear the old struct and allocate memory for the new one
5977 if (PCB->Changed && Settings.SaveInTMP)
5978 SaveInTMP ();
5979 RemovePCB (PCB);
5980 PCB = NULL;
5981 PCB = CreateNewPCB ();
5982 CreateNewPCBPost (PCB, 1);
5984 /* setup the new name and reset some values to default */
5985 free (PCB->Name);
5986 PCB->Name = name;
5988 ResetStackAndVisibility ();
5989 SetCrosshairRange (0, 0, PCB->MaxWidth, PCB->MaxHeight);
5990 CenterDisplay (PCB->MaxWidth / 2, PCB->MaxHeight / 2, false);
5991 Redraw ();
5993 hid_action ("PCBChanged");
5994 notify_crosshair_change (true);
5995 return 0;
5997 return 1;
6001 * \brief No operation, just for testing purposes.
6002 * syntax: Bell(volume)
6004 void
6005 ActionBell (char *volume)
6007 gui->beep ();
6010 /* --------------------------------------------------------------------------- */
6012 static const char pastebuffer_syntax[] =
6013 N_("PasteBuffer(AddSelected|Clear|1..MAX_BUFFER)\n"
6014 "PasteBuffer(Rotate, 1..3)\n"
6015 "PasteBuffer(Convert|Save|Restore|Mirror)\n"
6016 "PasteBuffer(ToLayout, X, Y, units)");
6018 static const char pastebuffer_help[] =
6019 N_("Various operations on the paste buffer.");
6021 /* %start-doc actions PasteBuffer
6023 There are a number of paste buffers; the actual limit is a
6024 compile-time constant @code{MAX_BUFFER} in @file{globalconst.h}. It
6025 is currently @code{5}. One of these is the ``current'' paste buffer,
6026 often referred to as ``the'' paste buffer.
6028 @table @code
6030 @item AddSelected
6031 Copies the selected objects to the current paste buffer.
6033 @item Clear
6034 Remove all objects from the current paste buffer.
6036 @item Convert
6037 Convert the current paste buffer to an element. Vias are converted to
6038 pins, lines are converted to pads.
6040 @item Restore
6041 Convert any elements in the paste buffer back to vias and lines.
6043 @item Mirror
6044 Flip all objects in the paste buffer vertically (up/down flip). To mirror
6045 horizontally, combine this with rotations.
6047 @item Rotate
6048 Rotates the current buffer. The number to pass is 1..3, where 1 means
6049 90 degrees counter clockwise, 2 means 180 degrees, and 3 means 90
6050 degrees clockwise (270 CCW).
6052 @item Save
6053 Saves any elements in the current buffer to the indicated file.
6055 @item ToLayout
6056 Pastes any elements in the current buffer to the indicated X, Y
6057 coordinates in the layout. The @code{X} and @code{Y} are treated like
6058 @code{delta} is for many other objects. For each, if it's prefixed by
6059 @code{+} or @code{-}, then that amount is relative to the last
6060 location. Otherwise, it's absolute. Units can be
6061 @code{mil} or @code{mm}; if unspecified, units are PCB's internal
6062 units, currently 1/100 mil.
6065 @item 1..MAX_BUFFER
6066 Selects the given buffer to be the current paste buffer.
6068 @end table
6070 %end-doc */
6072 static int
6073 ActionPasteBuffer (int argc, char **argv, Coord x, Coord y)
6075 char *function = argc ? argv[0] : (char *)"";
6076 char *sbufnum = argc > 1 ? argv[1] : (char *)"";
6077 char *name;
6078 static char *default_file = NULL;
6079 int free_name = 0;
6081 notify_crosshair_change (false);
6082 if (function)
6084 switch (GetFunctionID (function))
6086 /* clear contents of paste buffer */
6087 case F_Clear:
6088 ClearBuffer (PASTEBUFFER);
6089 break;
6091 /* copies objects to paste buffer */
6092 case F_AddSelected:
6093 AddSelectedToBuffer (PASTEBUFFER, 0, 0, false);
6094 break;
6096 /* converts buffer contents into an element */
6097 case F_Convert:
6098 ConvertBufferToElement (PASTEBUFFER);
6099 break;
6101 /* break up element for editing */
6102 case F_Restore:
6103 SmashBufferElement (PASTEBUFFER);
6104 break;
6106 /* Mirror buffer */
6107 case F_Mirror:
6108 MirrorBuffer (PASTEBUFFER);
6109 break;
6111 case F_Rotate:
6112 if (sbufnum)
6114 RotateBuffer (PASTEBUFFER, (BYTE) atoi (sbufnum));
6115 SetCrosshairRangeToBuffer ();
6117 break;
6119 case F_Save:
6120 if (PASTEBUFFER->Data->ElementN == 0)
6122 Message (_("Buffer has no elements!\n"));
6123 break;
6125 free_name = 0;
6126 if (argc <= 1)
6128 name = gui->fileselect (_("Save Paste Buffer As ..."),
6129 _("Choose a file to save the contents of the\n"
6130 "paste buffer to.\n"),
6131 default_file, ".fp", "footprint",
6134 if (default_file)
6136 free (default_file);
6137 default_file = NULL;
6139 if ( name && *name)
6141 default_file = strdup (name);
6143 free_name = 1;
6146 else
6147 name = argv[1];
6150 FILE *exist;
6152 if ((exist = fopen (name, "r")))
6154 fclose (exist);
6155 if (gui->
6156 confirm_dialog (_("File exists! Ok to overwrite?"), 0))
6157 SaveBufferElements (name);
6159 else
6160 SaveBufferElements (name);
6162 if (free_name && name)
6163 free (name);
6165 break;
6167 case F_ToLayout:
6169 static Coord oldx = 0, oldy = 0;
6170 Coord x, y;
6171 bool absolute;
6173 if (argc == 1)
6175 x = y = 0;
6177 else if (argc == 3 || argc == 4)
6179 x = GetValue (ARG (1), ARG (3), &absolute);
6180 if (!absolute)
6181 x += oldx;
6182 y = GetValue (ARG (2), ARG (3), &absolute);
6183 if (!absolute)
6184 y += oldy;
6186 else
6188 notify_crosshair_change (true);
6189 AFAIL (pastebuffer);
6192 oldx = x;
6193 oldy = y;
6194 if (CopyPastebufferToLayout (x, y))
6195 SetChangedFlag (true);
6197 break;
6199 /* set number */
6200 default:
6202 int number = atoi (function);
6204 /* correct number */
6205 if (number)
6206 SetBufferNumber (number - 1);
6211 notify_crosshair_change (true);
6212 return 0;
6215 /* --------------------------------------------------------------------------- */
6217 static const char undo_syntax[] = N_("Undo()\n"
6218 "Undo(ClearList)");
6220 static const char undo_help[] = N_("Undo recent changes.");
6222 /* %start-doc actions Undo
6224 The unlimited undo feature of @code{Pcb} allows you to recover from
6225 most operations that materially affect you work. Calling
6226 @code{Undo()} without any parameter recovers from the last (non-undo)
6227 operation. @code{ClearList} is used to release the allocated
6228 memory. @code{ClearList} is called whenever a new layout is started or
6229 loaded. See also @code{Redo} and @code{Atomic}.
6231 Note that undo groups operations by serial number; changes with the
6232 same serial number will be undone (or redone) as a group. See
6233 @code{Atomic}.
6235 %end-doc */
6237 static int
6238 ActionUndo (int argc, char **argv, Coord x, Coord y)
6240 char *function = ARG (0);
6241 if (!function || !*function)
6243 /* don't allow undo in the middle of an operation */
6244 if (Settings.Mode != POLYGONHOLE_MODE &&
6245 Crosshair.AttachedObject.State != STATE_FIRST)
6246 return 1;
6247 if (Crosshair.AttachedBox.State != STATE_FIRST
6248 && Settings.Mode != ARC_MODE)
6249 return 1;
6250 /* undo the last operation */
6252 notify_crosshair_change (false);
6253 if ((Settings.Mode == POLYGON_MODE ||
6254 Settings.Mode == POLYGONHOLE_MODE) &&
6255 Crosshair.AttachedPolygon.PointN)
6257 GoToPreviousPoint ();
6258 notify_crosshair_change (true);
6259 return 0;
6261 /* move anchor point if undoing during line creation */
6262 if (Settings.Mode == LINE_MODE)
6264 if (Crosshair.AttachedLine.State == STATE_SECOND)
6266 if (TEST_FLAG (AUTODRCFLAG, PCB))
6267 Undo (true); /* undo the connection find */
6268 Crosshair.AttachedLine.State = STATE_FIRST;
6269 SetLocalRef (0, 0, false);
6270 notify_crosshair_change (true);
6271 return 0;
6273 if (Crosshair.AttachedLine.State == STATE_THIRD)
6275 int type;
6276 void *ptr1, *ptr3, *ptrtmp;
6277 LineType *ptr2;
6278 /* this search is guaranteed to succeed */
6279 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6280 &ptrtmp, &ptr3,
6281 Crosshair.AttachedLine.Point1.X,
6282 Crosshair.AttachedLine.Point1.Y, 0);
6283 ptr2 = (LineType *) ptrtmp;
6285 /* save both ends of line */
6286 Crosshair.AttachedLine.Point2.X = ptr2->Point1.X;
6287 Crosshair.AttachedLine.Point2.Y = ptr2->Point1.Y;
6288 if ((type = Undo (true)))
6289 SetChangedFlag (true);
6290 /* check that the undo was of the right type */
6291 if ((type & UNDO_CREATE) == 0)
6293 /* wrong undo type, restore anchor points */
6294 Crosshair.AttachedLine.Point2.X =
6295 Crosshair.AttachedLine.Point1.X;
6296 Crosshair.AttachedLine.Point2.Y =
6297 Crosshair.AttachedLine.Point1.Y;
6298 notify_crosshair_change (true);
6299 return 0;
6301 /* move to new anchor */
6302 Crosshair.AttachedLine.Point1.X =
6303 Crosshair.AttachedLine.Point2.X;
6304 Crosshair.AttachedLine.Point1.Y =
6305 Crosshair.AttachedLine.Point2.Y;
6306 /* check if an intermediate point was removed */
6307 if (type & UNDO_REMOVE)
6309 /* this search should find the restored line */
6310 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6311 &ptrtmp,
6312 &ptr3,
6313 Crosshair.AttachedLine.Point2.X,
6314 Crosshair.AttachedLine.Point2.Y, 0);
6315 ptr2 = (LineType *) ptrtmp;
6316 if (TEST_FLAG (AUTODRCFLAG, PCB))
6318 /* undo loses CONNECTEDFLAG and FOUNDFLAG */
6319 SET_FLAG(CONNECTEDFLAG, ptr2);
6320 SET_FLAG(FOUNDFLAG, ptr2);
6321 DrawLine (CURRENT, ptr2);
6323 Crosshair.AttachedLine.Point1.X =
6324 Crosshair.AttachedLine.Point2.X = ptr2->Point2.X;
6325 Crosshair.AttachedLine.Point1.Y =
6326 Crosshair.AttachedLine.Point2.Y = ptr2->Point2.Y;
6328 FitCrosshairIntoGrid (Crosshair.X, Crosshair.Y);
6329 AdjustAttachedObjects ();
6330 if (--addedLines == 0)
6332 Crosshair.AttachedLine.State = STATE_SECOND;
6333 lastLayer = CURRENT;
6335 else
6337 /* this search is guaranteed to succeed too */
6338 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6339 &ptrtmp,
6340 &ptr3,
6341 Crosshair.AttachedLine.Point1.X,
6342 Crosshair.AttachedLine.Point1.Y, 0);
6343 ptr2 = (LineType *) ptrtmp;
6344 lastLayer = (LayerType *) ptr1;
6346 notify_crosshair_change (true);
6347 return 0;
6350 if (Settings.Mode == ARC_MODE)
6352 if (Crosshair.AttachedBox.State == STATE_SECOND)
6354 Crosshair.AttachedBox.State = STATE_FIRST;
6355 notify_crosshair_change (true);
6356 return 0;
6358 if (Crosshair.AttachedBox.State == STATE_THIRD)
6360 void *ptr1, *ptr2, *ptr3;
6361 BoxType *bx;
6362 /* guaranteed to succeed */
6363 SearchObjectByLocation (ARC_TYPE, &ptr1, &ptr2, &ptr3,
6364 Crosshair.AttachedBox.Point1.X,
6365 Crosshair.AttachedBox.Point1.Y, 0);
6366 bx = GetArcEnds ((ArcType *) ptr2);
6367 Crosshair.AttachedBox.Point1.X =
6368 Crosshair.AttachedBox.Point2.X = bx->X1;
6369 Crosshair.AttachedBox.Point1.Y =
6370 Crosshair.AttachedBox.Point2.Y = bx->Y1;
6371 AdjustAttachedObjects ();
6372 if (--addedLines == 0)
6373 Crosshair.AttachedBox.State = STATE_SECOND;
6376 /* undo the last destructive operation */
6377 if (Undo (true))
6378 SetChangedFlag (true);
6380 else if (function)
6382 switch (GetFunctionID (function))
6384 /* clear 'undo objects' list */
6385 case F_ClearList:
6386 ClearUndoList (false);
6387 break;
6390 notify_crosshair_change (true);
6391 return 0;
6394 /* --------------------------------------------------------------------------- */
6396 static const char redo_syntax[] = N_("Redo()");
6398 static const char redo_help[] = N_("Redo recent \"undo\" operations.");
6400 /* %start-doc actions Redo
6402 This routine allows you to recover from the last undo command. You
6403 might want to do this if you thought that undo was going to revert
6404 something other than what it actually did (in case you are confused
6405 about which operations are un-doable), or if you have been backing up
6406 through a long undo list and over-shoot your stopping point. Any
6407 change that is made since the undo in question will trim the redo
6408 list. For example if you add ten lines, then undo three of them you
6409 could use redo to put them back, but if you move a line on the board
6410 before performing the redo, you will lose the ability to "redo" the
6411 three "undone" lines.
6413 %end-doc */
6415 static int
6416 ActionRedo (int argc, char **argv, Coord x, Coord y)
6418 if (((Settings.Mode == POLYGON_MODE ||
6419 Settings.Mode == POLYGONHOLE_MODE) &&
6420 Crosshair.AttachedPolygon.PointN) ||
6421 Crosshair.AttachedLine.State == STATE_SECOND)
6422 return 1;
6423 notify_crosshair_change (false);
6424 if (Redo (true))
6426 SetChangedFlag (true);
6427 if (Settings.Mode == LINE_MODE &&
6428 Crosshair.AttachedLine.State != STATE_FIRST)
6430 LineType *line = g_list_last (CURRENT->Line)->data;
6431 Crosshair.AttachedLine.Point1.X =
6432 Crosshair.AttachedLine.Point2.X = line->Point2.X;
6433 Crosshair.AttachedLine.Point1.Y =
6434 Crosshair.AttachedLine.Point2.Y = line->Point2.Y;
6435 addedLines++;
6438 notify_crosshair_change (true);
6439 return 0;
6442 /* --------------------------------------------------------------------------- */
6444 static const char polygon_syntax[] = N_("Polygon(Close|PreviousPoint)");
6446 static const char polygon_help[] = N_("Some polygon related stuff.");
6448 /* %start-doc actions Polygon
6450 Polygons need a special action routine to make life easier.
6452 @table @code
6454 @item Close
6455 Creates the final segment of the polygon. This may fail if clipping
6456 to 45 degree lines is switched on, in which case a warning is issued.
6458 @item PreviousPoint
6459 Resets the newly entered corner to the previous one. The Undo action
6460 will call Polygon(PreviousPoint) when appropriate to do so.
6462 @end table
6464 %end-doc */
6466 static int
6467 ActionPolygon (int argc, char **argv, Coord x, Coord y)
6469 char *function = ARG (0);
6470 if (function && Settings.Mode == POLYGON_MODE)
6472 notify_crosshair_change (false);
6473 switch (GetFunctionID (function))
6475 /* close open polygon if possible */
6476 case F_Close:
6477 ClosePolygon ();
6478 break;
6480 /* go back to the previous point */
6481 case F_PreviousPoint:
6482 GoToPreviousPoint ();
6483 break;
6485 notify_crosshair_change (true);
6487 return 0;
6490 /* --------------------------------------------------------------------------- */
6492 static const char routestyle_syntax[] = N_("RouteStyle(1|2|3|4)");
6494 static const char routestyle_help[] =
6495 N_("Copies the indicated routing style into the current sizes.");
6497 /* %start-doc actions RouteStyle
6499 %end-doc */
6501 static int
6502 ActionRouteStyle (int argc, char **argv, Coord x, Coord y)
6504 char *str = ARG (0);
6505 RouteStyleType *rts;
6506 int number;
6508 if (str)
6510 number = atoi (str);
6511 if (number > 0 && number <= NUM_STYLES)
6513 rts = &PCB->RouteStyle[number - 1];
6514 SetLineSize (rts->Thick);
6515 SetViaSize (rts->Diameter, true);
6516 SetViaDrillingHole (rts->Hole, true);
6517 SetKeepawayWidth (rts->Keepaway);
6518 hid_action("RouteStylesChanged");
6521 return 0;
6525 /* --------------------------------------------------------------------------- */
6527 static const char moveobject_syntax[] = N_("MoveObject(X,Y,dim)");
6529 static const char moveobject_help[] =
6530 N_("Moves the object under the crosshair.");
6532 /* %start-doc actions MoveObject
6534 The @code{X} and @code{Y} are treated like @code{delta} is for many
6535 other objects. For each, if it's prefixed by @code{+} or @code{-},
6536 then that amount is relative. Otherwise, it's absolute. Units can be
6537 @code{mil} or @code{mm}; if unspecified, units are PCB's internal
6538 units, currently 1/100 mil.
6540 %end-doc */
6542 static int
6543 ActionMoveObject (int argc, char **argv, Coord x, Coord y)
6545 char *x_str = ARG (0);
6546 char *y_str = ARG (1);
6547 char *units = ARG (2);
6548 Coord nx, ny;
6549 bool absolute1, absolute2;
6550 void *ptr1, *ptr2, *ptr3;
6551 int type;
6553 ny = GetValue (y_str, units, &absolute1);
6554 nx = GetValue (x_str, units, &absolute2);
6556 type = SearchScreen (x, y, MOVE_TYPES, &ptr1, &ptr2, &ptr3);
6557 if (type == NO_TYPE)
6559 Message (_("Nothing found under crosshair\n"));
6560 return 1;
6562 if (absolute1)
6563 nx -= x;
6564 if (absolute2)
6565 ny -= y;
6566 Crosshair.AttachedObject.RubberbandN = 0;
6567 if (TEST_FLAG (RUBBERBANDFLAG, PCB))
6568 LookupRubberbandLines (type, ptr1, ptr2, ptr3);
6569 if (type == ELEMENT_TYPE)
6570 LookupRatLines (type, ptr1, ptr2, ptr3);
6571 MoveObjectAndRubberband (type, ptr1, ptr2, ptr3, nx, ny);
6572 SetChangedFlag (true);
6573 return 0;
6576 /* --------------------------------------------------------------------------- */
6578 static const char movetocurrentlayer_syntax[] =
6579 N_("MoveToCurrentLayer(Object|SelectedObjects)");
6581 static const char movetocurrentlayer_help[] =
6582 N_("Moves objects to the current layer.");
6584 /* %start-doc actions MoveToCurrentLayer
6586 Note that moving an element from a component layer to a solder layer,
6587 or from solder to component, won't automatically flip it. Use the
6588 @code{Flip()} action to do that.
6590 %end-doc */
6592 static int
6593 ActionMoveToCurrentLayer (int argc, char **argv, Coord x, Coord y)
6595 char *function = ARG (0);
6596 if (function)
6598 switch (GetFunctionID (function))
6600 case F_Object:
6602 int type;
6603 void *ptr1, *ptr2, *ptr3;
6605 gui->get_coords (_("Select an Object"), &x, &y);
6606 if ((type =
6607 SearchScreen (x, y, MOVETOLAYER_TYPES,
6608 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
6609 if (MoveObjectToLayer (type, ptr1, ptr2, ptr3, CURRENT, false))
6610 SetChangedFlag (true);
6611 break;
6614 case F_SelectedObjects:
6615 case F_Selected:
6616 if (MoveSelectedObjectsToLayer (CURRENT))
6617 SetChangedFlag (true);
6618 break;
6621 return 0;
6625 static const char setsame_syntax[] = N_("SetSame()");
6627 static const char setsame_help[] =
6628 N_("Sets current layer and sizes to match indicated item.");
6630 /* %start-doc actions SetSame
6632 When invoked over any line, arc, polygon, or via, this changes the
6633 current layer to be the layer that item is on, and changes the current
6634 sizes (thickness, keepaway, drill, etc) according to that item.
6636 %end-doc */
6638 static int
6639 ActionSetSame (int argc, char **argv, Coord x, Coord y)
6641 void *ptr1, *ptr2, *ptr3;
6642 int type;
6643 LayerType *layer = CURRENT;
6645 type = SearchScreen (x, y, CLONE_TYPES, &ptr1, &ptr2, &ptr3);
6646 /* set layer current and size from line or arc */
6647 switch (type)
6649 case LINE_TYPE:
6650 notify_crosshair_change (false);
6651 Settings.LineThickness = ((LineType *) ptr2)->Thickness;
6652 Settings.Keepaway = ((LineType *) ptr2)->Clearance / 2;
6653 layer = (LayerType *) ptr1;
6654 if (Settings.Mode != LINE_MODE)
6655 SetMode (LINE_MODE);
6656 notify_crosshair_change (true);
6657 hid_action ("RouteStylesChanged");
6658 break;
6660 case ARC_TYPE:
6661 notify_crosshair_change (false);
6662 Settings.LineThickness = ((ArcType *) ptr2)->Thickness;
6663 Settings.Keepaway = ((ArcType *) ptr2)->Clearance / 2;
6664 layer = (LayerType *) ptr1;
6665 if (Settings.Mode != ARC_MODE)
6666 SetMode (ARC_MODE);
6667 notify_crosshair_change (true);
6668 hid_action ("RouteStylesChanged");
6669 break;
6671 case POLYGON_TYPE:
6672 layer = (LayerType *) ptr1;
6673 break;
6675 case VIA_TYPE:
6676 notify_crosshair_change (false);
6677 Settings.ViaThickness = ((PinType *) ptr2)->Thickness;
6678 Settings.ViaDrillingHole = ((PinType *) ptr2)->DrillingHole;
6679 Settings.Keepaway = ((PinType *) ptr2)->Clearance / 2;
6680 if (Settings.Mode != VIA_MODE)
6681 SetMode (VIA_MODE);
6682 notify_crosshair_change (true);
6683 hid_action ("RouteStylesChanged");
6684 break;
6686 default:
6687 return 1;
6689 if (layer != CURRENT)
6691 ChangeGroupVisibility (GetLayerNumber (PCB->Data, layer), true, true);
6692 Redraw ();
6694 return 0;
6698 /* --------------------------------------------------------------------------- */
6700 static const char setflag_syntax[] =
6701 N_("SetFlag(Object|Selected|SelectedObjects, flag)\n"
6702 "SetFlag(SelectedLines|SelectedPins|SelectedVias, flag)\n"
6703 "SetFlag(SelectedPads|SelectedTexts|SelectedNames, flag)\n"
6704 "SetFlag(SelectedElements, flag)\n"
6705 "flag = square | octagon | thermal | join");
6707 static const char setflag_help[] = N_("Sets flags on objects.");
6709 /* %start-doc actions SetFlag
6711 Turns the given flag on, regardless of its previous setting. See
6712 @code{ChangeFlag}.
6714 @example
6715 SetFlag(SelectedPins,thermal)
6716 @end example
6718 %end-doc */
6720 static int
6721 ActionSetFlag (int argc, char **argv, Coord x, Coord y)
6723 char *function = ARG (0);
6724 char *flag = ARG (1);
6725 ChangeFlag (function, flag, 1, "SetFlag");
6726 return 0;
6729 /* --------------------------------------------------------------------------- */
6731 static const char clrflag_syntax[] =
6732 N_("ClrFlag(Object|Selected|SelectedObjects, flag)\n"
6733 "ClrFlag(SelectedLines|SelectedPins|SelectedVias, flag)\n"
6734 "ClrFlag(SelectedPads|SelectedTexts|SelectedNames, flag)\n"
6735 "ClrFlag(SelectedElements, flag)\n"
6736 "flag = square | octagon | thermal | join");
6738 static const char clrflag_help[] = N_("Clears flags on objects.");
6740 /* %start-doc actions ClrFlag
6742 Turns the given flag off, regardless of its previous setting. See
6743 @code{ChangeFlag}.
6745 @example
6746 ClrFlag(SelectedLines,join)
6747 @end example
6749 %end-doc */
6751 static int
6752 ActionClrFlag (int argc, char **argv, Coord x, Coord y)
6754 char *function = ARG (0);
6755 char *flag = ARG (1);
6756 ChangeFlag (function, flag, 0, "ClrFlag");
6757 return 0;
6760 /* --------------------------------------------------------------------------- */
6762 static const char changeflag_syntax[] =
6763 N_("ChangeFlag(Object|Selected|SelectedObjects, flag, value)\n"
6764 "ChangeFlag(SelectedLines|SelectedPins|SelectedVias, flag, value)\n"
6765 "ChangeFlag(SelectedPads|SelectedTexts|SelectedNames, flag, value)\n"
6766 "ChangeFlag(SelectedElements, flag, value)\n"
6767 "flag = square | octagon | thermal | join\n"
6768 "value = 0 | 1");
6770 static const char changeflag_help[] = N_("Sets or clears flags on objects.");
6772 /* %start-doc actions ChangeFlag
6774 Toggles the given flag on the indicated object(s). The flag may be
6775 one of the flags listed above (square, octagon, thermal, join). The
6776 value may be the number 0 or 1. If the value is 0, the flag is
6777 cleared. If the value is 1, the flag is set.
6779 %end-doc */
6781 static int
6782 ActionChangeFlag (int argc, char **argv, Coord x, Coord y)
6784 char *function = ARG (0);
6785 char *flag = ARG (1);
6786 int value = argc > 2 ? atoi (argv[2]) : -1;
6787 if (value != 0 && value != 1)
6788 AFAIL (changeflag);
6790 ChangeFlag (function, flag, value, "ChangeFlag");
6791 return 0;
6795 static void
6796 ChangeFlag (char *what, char *flag_name, int value, char *cmd_name)
6798 bool (*set_object) (int, void *, void *, void *);
6799 bool (*set_selected) (int);
6801 if (NSTRCMP (flag_name, "square") == 0)
6803 set_object = value ? SetObjectSquare : ClrObjectSquare;
6804 set_selected = value ? SetSelectedSquare : ClrSelectedSquare;
6806 else if (NSTRCMP (flag_name, "octagon") == 0)
6808 set_object = value ? SetObjectOctagon : ClrObjectOctagon;
6809 set_selected = value ? SetSelectedOctagon : ClrSelectedOctagon;
6811 else if (NSTRCMP (flag_name, "join") == 0)
6813 /* Note: these are backwards, because the flag is "clear" but
6814 the command is "join". */
6815 set_object = value ? ClrObjectJoin : SetObjectJoin;
6816 set_selected = value ? ClrSelectedJoin : SetSelectedJoin;
6818 else
6820 Message (_("%s(): Flag \"%s\" is not valid\n"), cmd_name, flag_name);
6821 return;
6824 switch (GetFunctionID (what))
6826 case F_Object:
6828 int type;
6829 void *ptr1, *ptr2, *ptr3;
6831 if ((type =
6832 SearchScreen (Crosshair.X, Crosshair.Y, CHANGESIZE_TYPES,
6833 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
6834 if (TEST_FLAG (LOCKFLAG, (PinType *) ptr2))
6835 Message (_("Sorry, the object is locked\n"));
6836 if (set_object (type, ptr1, ptr2, ptr3))
6837 SetChangedFlag (true);
6838 break;
6841 case F_SelectedVias:
6842 if (set_selected (VIA_TYPE))
6843 SetChangedFlag (true);
6844 break;
6846 case F_SelectedPins:
6847 if (set_selected (PIN_TYPE))
6848 SetChangedFlag (true);
6849 break;
6851 case F_SelectedPads:
6852 if (set_selected (PAD_TYPE))
6853 SetChangedFlag (true);
6854 break;
6856 case F_SelectedLines:
6857 if (set_selected (LINE_TYPE))
6858 SetChangedFlag (true);
6859 break;
6861 case F_SelectedTexts:
6862 if (set_selected (TEXT_TYPE))
6863 SetChangedFlag (true);
6864 break;
6866 case F_SelectedNames:
6867 if (set_selected (ELEMENTNAME_TYPE))
6868 SetChangedFlag (true);
6869 break;
6871 case F_SelectedElements:
6872 if (set_selected (ELEMENT_TYPE))
6873 SetChangedFlag (true);
6874 break;
6876 case F_Selected:
6877 case F_SelectedObjects:
6878 if (set_selected (CHANGESIZE_TYPES))
6879 SetChangedFlag (true);
6880 break;
6884 /* --------------------------------------------------------------------------- */
6886 static const char executefile_syntax[] = N_("ExecuteFile(filename)");
6888 static const char executefile_help[] = N_("Run actions from the given file.");
6890 /* %start-doc actions ExecuteFile
6892 Lines starting with @code{#} are ignored.
6894 %end-doc */
6896 static int
6897 ActionExecuteFile (int argc, char **argv, Coord x, Coord y)
6899 FILE *fp;
6900 char *fname;
6901 char line[256];
6902 int n = 0;
6903 char *sp;
6905 if (argc != 1)
6906 AFAIL (executefile);
6908 fname = argv[0];
6910 if ((fp = fopen (fname, "r")) == NULL)
6912 fprintf (stderr, _("Could not open actions file \"%s\".\n"), fname);
6913 return 1;
6916 defer_updates = 1;
6917 defer_needs_update = 0;
6918 while (fgets (line, sizeof (line), fp) != NULL)
6920 n++;
6921 sp = line;
6923 /* eat the trailing newline */
6924 while (*sp && *sp != '\r' && *sp != '\n')
6925 sp++;
6926 *sp = '\0';
6928 /* eat leading spaces and tabs */
6929 sp = line;
6930 while (*sp && (*sp == ' ' || *sp == '\t'))
6931 sp++;
6934 * if we have anything left and its not a comment line
6935 * then execute it
6938 if (*sp && *sp != '#')
6940 /*Message ("%s : line %-3d : \"%s\"\n", fname, n, sp);*/
6941 hid_parse_actions (sp);
6945 defer_updates = 0;
6946 if (defer_needs_update)
6948 IncrementUndoSerialNumber ();
6949 gui->invalidate_all ();
6951 fclose (fp);
6952 return 0;
6955 /* --------------------------------------------------------------------------- */
6957 static int
6958 ActionPSCalib (int argc, char **argv, Coord x, Coord y)
6960 HID *ps = hid_find_exporter ("ps");
6961 ps->calibrate (0.0,0.0);
6962 return 0;
6965 /* --------------------------------------------------------------------------- */
6967 static ElementType *element_cache = NULL;
6969 static ElementType *
6970 find_element_by_refdes (char *refdes)
6972 if (element_cache
6973 && NAMEONPCB_NAME(element_cache)
6974 && strcmp (NAMEONPCB_NAME(element_cache), refdes) == 0)
6975 return element_cache;
6977 ELEMENT_LOOP (PCB->Data);
6979 if (NAMEONPCB_NAME(element)
6980 && strcmp (NAMEONPCB_NAME(element), refdes) == 0)
6982 element_cache = element;
6983 return element_cache;
6986 END_LOOP;
6987 return NULL;
6990 static AttributeType *
6991 lookup_attr (AttributeListType *list, const char *name)
6993 int i;
6994 for (i=0; i<list->Number; i++)
6995 if (strcmp (list->List[i].name, name) == 0)
6996 return & list->List[i];
6997 return NULL;
7000 static void
7001 delete_attr (AttributeListType *list, AttributeType *attr)
7003 int idx = attr - list->List;
7004 if (idx < 0 || idx >= list->Number)
7005 return;
7006 if (list->Number - idx > 1)
7007 memmove (attr, attr+1, (list->Number - idx - 1) * sizeof(AttributeType));
7008 list->Number --;
7011 /* ---------------------------------------------------------------- */
7012 static const char elementlist_syntax[] =
7013 N_("ElementList(Start|Done|Need,<refdes>,<footprint>,<value>)");
7015 static const char elementlist_help[] =
7016 N_("Adds the given element if it doesn't already exist.");
7018 /* %start-doc actions elementlist
7020 @table @code
7022 @item Start
7023 Indicates the start of an element list; call this before any Need
7024 actions.
7026 @item Need
7027 Searches the board for an element with a matching refdes.
7029 If found, the value and footprint are updated.
7031 If not found, a new element is created with the given footprint and value.
7033 @item Done
7034 Compares the list of elements needed since the most recent
7035 @code{start} with the list of elements actually on the board. Any
7036 elements that weren't listed are selected, so that the user may delete
7037 them.
7039 @end table
7041 %end-doc */
7043 static int number_of_footprints_not_found;
7045 static int
7046 parse_layout_attribute_units (char *name, int def)
7048 const char *as = AttributeGet (PCB, name);
7049 if (!as)
7050 return def;
7051 return GetValue (as, NULL, NULL);
7054 static int
7055 ActionElementList (int argc, char **argv, Coord x, Coord y)
7057 ElementType *e = NULL;
7058 char *refdes, *value, *footprint, *old;
7059 char *args[3];
7060 char *function;
7062 if (argc < 1)
7063 AFAIL (elementlist);
7065 function = argv[0];
7067 #ifdef DEBUG
7068 printf("Entered ActionElementList, executing function %s\n", function);
7069 #endif
7071 if (strcasecmp (function, "start") == 0)
7073 ELEMENT_LOOP (PCB->Data);
7075 CLEAR_FLAG (FOUNDFLAG, element);
7077 END_LOOP;
7078 element_cache = NULL;
7079 number_of_footprints_not_found = 0;
7080 return 0;
7083 if (strcasecmp (function, "done") == 0)
7085 ELEMENT_LOOP (PCB->Data);
7087 if (TEST_FLAG (FOUNDFLAG, element))
7089 CLEAR_FLAG (FOUNDFLAG, element);
7091 else if (! EMPTY_STRING_P (NAMEONPCB_NAME (element)))
7093 /* Unnamed elements should remain untouched */
7094 SET_FLAG (SELECTEDFLAG, element);
7097 END_LOOP;
7098 if (number_of_footprints_not_found > 0)
7099 gui->confirm_dialog (_("Not all requested footprints were found.\n"
7100 "See the message log for details"),
7101 "Ok", NULL);
7102 return 0;
7105 if (strcasecmp (function, "need") != 0)
7106 AFAIL (elementlist);
7108 if (argc != 4)
7109 AFAIL (elementlist);
7111 argc --;
7112 argv ++;
7114 refdes = ARG(0);
7115 footprint = ARG(1);
7116 value = ARG(2);
7118 args[0] = footprint;
7119 args[1] = refdes;
7120 args[2] = value;
7122 #ifdef DEBUG
7123 printf(" ... footprint = %s\n", footprint);
7124 printf(" ... refdes = %s\n", refdes);
7125 printf(" ... value = %s\n", value);
7126 #endif
7128 e = find_element_by_refdes (refdes);
7130 if (!e)
7132 Coord nx, ny, d;
7134 #ifdef DEBUG
7135 printf(" ... Footprint not on board, need to add it.\n");
7136 #endif
7137 /* Not on board, need to add it. */
7138 if (LoadFootprint(argc, args, x, y))
7140 number_of_footprints_not_found ++;
7141 return 1;
7144 nx = PCB->MaxWidth / 2;
7145 ny = PCB->MaxHeight / 2;
7146 d = MIN (PCB->MaxWidth, PCB->MaxHeight) / 10;
7148 nx = parse_layout_attribute_units ("import::newX", nx);
7149 ny = parse_layout_attribute_units ("import::newY", ny);
7150 d = parse_layout_attribute_units ("import::disperse", d);
7152 if (d > 0)
7154 nx += rand () % (d*2) - d;
7155 ny += rand () % (d*2) - d;
7158 if (nx < 0)
7159 nx = 0;
7160 if (nx >= PCB->MaxWidth)
7161 nx = PCB->MaxWidth - 1;
7162 if (ny < 0)
7163 ny = 0;
7164 if (ny >= PCB->MaxHeight)
7165 ny = PCB->MaxHeight - 1;
7167 /* Place components onto center of board. */
7168 if (CopyPastebufferToLayout (nx, ny))
7169 SetChangedFlag (true);
7172 else if (e && DESCRIPTION_NAME(e) && strcmp (DESCRIPTION_NAME(e), footprint) != 0)
7174 #ifdef DEBUG
7175 printf(" ... Footprint on board, but different from footprint loaded.\n");
7176 #endif
7177 int er, pr, i;
7178 Coord mx, my;
7179 ElementType *pe;
7181 /* Different footprint, we need to swap them out. */
7182 if (LoadFootprint(argc, args, x, y))
7184 number_of_footprints_not_found ++;
7185 return 1;
7188 er = ElementOrientation (e);
7189 pe = PASTEBUFFER->Data->Element->data;
7190 if (!FRONT (e))
7191 MirrorElementCoordinates (PASTEBUFFER->Data, pe, pe->MarkY*2 - PCB->MaxHeight);
7192 pr = ElementOrientation (pe);
7194 mx = e->MarkX;
7195 my = e->MarkY;
7197 if (er != pr)
7198 RotateElementLowLevel (PASTEBUFFER->Data, pe, pe->MarkX, pe->MarkY, (er-pr+4)%4);
7200 for (i=0; i<MAX_ELEMENTNAMES; i++)
7202 pe->Name[i].X = e->Name[i].X - mx + pe->MarkX ;
7203 pe->Name[i].Y = e->Name[i].Y - my + pe->MarkY ;
7204 pe->Name[i].Direction = e->Name[i].Direction;
7205 pe->Name[i].Scale = e->Name[i].Scale;
7208 RemoveElement (e);
7210 if (CopyPastebufferToLayout (mx, my))
7211 SetChangedFlag (true);
7214 /* Now reload footprint */
7215 element_cache = NULL;
7216 e = find_element_by_refdes (refdes);
7218 old = ChangeElementText (PCB, PCB->Data, e, NAMEONPCB_INDEX, strdup (refdes));
7219 if (old)
7220 free(old);
7221 old = ChangeElementText (PCB, PCB->Data, e, VALUE_INDEX, strdup (value));
7222 if (old)
7223 free(old);
7225 SET_FLAG (FOUNDFLAG, e);
7227 #ifdef DEBUG
7228 printf(" ... Leaving ActionElementList.\n");
7229 #endif
7231 return 0;
7234 /* ---------------------------------------------------------------- */
7235 static const char elementsetattr_syntax[] =
7236 N_("ElementSetAttr(refdes,name[,value])");
7238 static const char elementsetattr_help[] =
7239 N_("Sets or clears an element-specific attribute.");
7241 /* %start-doc actions elementsetattr
7243 If a value is specified, the named attribute is added (if not already
7244 present) or changed (if it is) to the given value. If the value is
7245 not specified, the given attribute is removed if present.
7247 %end-doc */
7249 static int
7250 ActionElementSetAttr (int argc, char **argv, Coord x, Coord y)
7252 ElementType *e = NULL;
7253 char *refdes, *name, *value;
7254 AttributeType *attr;
7256 if (argc < 2)
7258 AFAIL (elementsetattr);
7261 refdes = argv[0];
7262 name = argv[1];
7263 value = ARG(2);
7265 ELEMENT_LOOP (PCB->Data);
7267 if (NSTRCMP (refdes, NAMEONPCB_NAME (element)) == 0)
7269 e = element;
7270 break;
7273 END_LOOP;
7275 if (!e)
7277 Message(_("Cannot change attribute of %s - element not found\n"), refdes);
7278 return 1;
7281 attr = lookup_attr (&e->Attributes, name);
7283 if (attr && value)
7285 free (attr->value);
7286 attr->value = strdup (value);
7288 if (attr && ! value)
7290 delete_attr (& e->Attributes, attr);
7292 if (!attr && value)
7294 CreateNewAttribute (& e->Attributes, name, value);
7297 return 0;
7300 /* ---------------------------------------------------------------- */
7301 static const char execcommand_syntax[] = N_("ExecCommand(command)");
7303 static const char execcommand_help[] = N_("Runs a command.");
7305 /* %start-doc actions execcommand
7307 Runs the given command, which is a system executable.
7309 %end-doc */
7311 static int
7312 ActionExecCommand (int argc, char **argv, Coord x, Coord y)
7314 char *command;
7316 if (argc < 1)
7318 AFAIL (execcommand);
7321 command = ARG(0);
7323 if (system (command))
7324 return 1;
7325 return 0;
7328 /* ---------------------------------------------------------------- */
7330 static int
7331 pcb_spawnvp (char **argv)
7333 #ifdef HAVE__SPAWNVP
7334 int result = _spawnvp (_P_WAIT, argv[0], (const char * const *) argv);
7335 if (result == -1)
7336 return 1;
7337 else
7338 return 0;
7339 #else
7340 int pid;
7341 pid = fork ();
7342 if (pid < 0)
7344 /* error */
7345 Message(_("Cannot fork!"));
7346 return 1;
7348 else if (pid == 0)
7350 /* Child */
7351 execvp (argv[0], argv);
7352 exit(1);
7354 else
7356 int rv;
7357 /* Parent */
7358 wait (&rv);
7360 return 0;
7361 #endif
7364 /* ---------------------------------------------------------------- */
7366 /*!
7367 * \brief Creates a new temporary file name.
7369 * Hopefully the operating system provides a mkdtemp() function to
7370 * securily create a temporary directory with mode 0700.\n
7371 * If so then that directory is created and the returned string is made
7372 * up of the directory plus the name variable.\n
7373 * For example:\n
7375 * tempfile_name_new ("myfile") might return
7376 * "/var/tmp/pcb.123456/myfile".
7378 * If mkdtemp() is not available then 'name' is ignored and the
7379 * insecure tmpnam() function is used.
7381 * Files/names created with tempfile_name_new() should be unlinked
7382 * with tempfile_unlink to make sure the temporary directory is also
7383 * removed when mkdtemp() is used.
7385 static char *
7386 tempfile_name_new (char * name)
7388 char *tmpfile = NULL;
7389 #ifdef HAVE_MKDTEMP
7390 char *tmpdir, *mytmpdir;
7391 size_t len;
7392 #endif
7394 assert ( name != NULL );
7396 #ifdef HAVE_MKDTEMP
7397 #define TEMPLATE "pcb.XXXXXXXX"
7400 tmpdir = getenv ("TMPDIR");
7402 /* FIXME -- what about win32? */
7403 if (tmpdir == NULL) {
7404 tmpdir = "/tmp";
7407 mytmpdir = (char *) malloc (sizeof(char) *
7408 (strlen (tmpdir) +
7410 strlen (TEMPLATE) +
7411 1));
7412 if (mytmpdir == NULL) {
7413 fprintf (stderr, "%s(): malloc failed()\n", __FUNCTION__);
7414 exit (1);
7417 *mytmpdir = '\0';
7418 (void)strcat (mytmpdir, tmpdir);
7419 (void)strcat (mytmpdir, PCB_DIR_SEPARATOR_S);
7420 (void)strcat (mytmpdir, TEMPLATE);
7421 if (mkdtemp (mytmpdir) == NULL) {
7422 fprintf (stderr, "%s(): mkdtemp (\"%s\") failed\n", __FUNCTION__, mytmpdir);
7423 free (mytmpdir);
7424 return NULL;
7428 len = strlen (mytmpdir) + /* the temp directory name */
7429 1 + /* the directory sep. */
7430 strlen (name) + /* the file name */
7431 1 /* the \0 termination */
7434 tmpfile = (char *) malloc (sizeof (char) * len);
7436 *tmpfile = '\0';
7437 (void)strcat (tmpfile, mytmpdir);
7438 (void)strcat (tmpfile, PCB_DIR_SEPARATOR_S);
7439 (void)strcat (tmpfile, name);
7441 free (mytmpdir);
7442 #undef TEMPLATE
7443 #else
7445 * tmpnam() uses a static buffer so strdup() the result right away
7446 * in case someone decides to create multiple temp names.
7448 tmpfile = strdup (tmpnam (NULL));
7449 #ifdef __WIN32__
7451 /* Guile doesn't like \ separators */
7452 char *c;
7453 for (c = tmpfile; *c; c++)
7454 if (*c == '\\')
7455 *c = '/';
7457 #endif
7458 #endif
7460 return tmpfile;
7463 /* ---------------------------------------------------------------- */
7466 * \brief Unlink a temporary file.
7468 * If we have mkdtemp() then our temp file lives in a temporary
7469 * directory and we need to remove that directory too.
7471 static int
7472 tempfile_unlink (char * name)
7474 #ifdef DEBUG
7475 /* SDB says: Want to keep old temp files for examiniation when debugging */
7476 return 0;
7477 #endif
7479 #ifdef HAVE_MKDTEMP
7480 int e, rc2 = 0;
7481 char *dname;
7483 unlink (name);
7484 /* it is possible that the file was never created so it is OK if the
7485 unlink fails */
7487 /* now figure out the directory name to remove */
7488 e = strlen (name) - 1;
7489 while (e > 0 && name[e] != PCB_DIR_SEPARATOR_C) {e--;}
7491 dname = strdup (name);
7492 dname[e] = '\0';
7495 * at this point, e *should* point to the end of the directory part
7496 * but lets make sure.
7498 if (e > 0) {
7499 rc2 = rmdir (dname);
7500 if (rc2 != 0) {
7501 perror (dname);
7504 } else {
7505 fprintf (stderr, _("%s(): Unable to determine temp directory name from the temp file\n"),
7506 __FUNCTION__);
7507 fprintf (stderr, "%s(): \"%s\"\n",
7508 __FUNCTION__, name);
7509 rc2 = -1;
7512 /* name was allocated with malloc */
7513 free (dname);
7514 free (name);
7517 * FIXME - should also return -1 if the temp file exists and was not
7518 * removed.
7520 if (rc2 != 0) {
7521 return -1;
7524 #else
7525 int rc = unlink (name);
7527 if (rc != 0) {
7528 fprintf (stderr, _("Failed to unlink \"%s\"\n"), name);
7529 free (name);
7530 return rc;
7532 free (name);
7534 #endif
7536 return 0;
7539 /* ---------------------------------------------------------------- */
7540 static const char import_syntax[] =
7541 N_("Import()\n"
7542 "Import([gnetlist|make[,source,source,...]])\n"
7543 "Import(setnewpoint[,(mark|center|X,Y)])\n"
7544 "Import(setdisperse,D,units)\n");
7546 static const char import_help[] = N_("Import schematics.");
7548 /* %start-doc actions Import
7550 Imports element and netlist data from the schematics (or some other
7551 source). The first parameter, which is optional, is the mode. If not
7552 specified, the @code{import::mode} attribute in the PCB is used.
7553 @code{gnetlist} means gnetlist is used to obtain the information from
7554 the schematics. @code{make} invokes @code{make}, assuming the user
7555 has a @code{Makefile} in the current directory. The @code{Makefile}
7556 will be invoked with the following variables set:
7558 @table @code
7560 @item PCB
7561 The name of the .pcb file
7563 @item SRCLIST
7564 A space-separated list of source files
7566 @item OUT
7567 The name of the file in which to put the command script, which may
7568 contain any @pcb{} actions. By default, this is a temporary file
7569 selected by @pcb{}, but if you specify an @code{import::outfile}
7570 attribute, that file name is used instead (and not automatically
7571 deleted afterwards).
7573 @end table
7575 The target specified to be built is the first of these that apply:
7577 @itemize @bullet
7579 @item
7580 The target specified by an @code{import::target} attribute.
7582 @item
7583 The output file specified by an @code{import::outfile} attribute.
7585 @item
7586 If nothing else is specified, the target is @code{pcb_import}.
7588 @end itemize
7590 If you specify an @code{import::makefile} attribute, then "-f <that
7591 file>" will be added to the command line.
7593 If you specify the mode, you may also specify the source files
7594 (schematics). If you do not specify any, the list of schematics is
7595 obtained by reading the @code{import::src@var{N}} attributes (like
7596 @code{import::src0}, @code{import::src1}, etc).
7598 For compatibility with future extensions to the import file format,
7599 the generated file @emph{must not} start with the two characters
7600 @code{#%}.
7602 If a temporary file is needed the @code{TMPDIR} environment variable
7603 is used to select its location.
7605 Note that the programs @code{gnetlist} and @code{make} may be
7606 overridden by the user via the @code{make-program} and @code{gnetlist}
7607 @code{pcb} settings (i.e. in @code{~/.pcb/settings} or on the command
7608 line).
7610 If @pcb{} cannot determine which schematic(s) to import from, the GUI
7611 is called to let user choose (see @code{ImportGUI()}).
7613 Note that Import() doesn't delete anything - after an Import, elements
7614 which shouldn't be on the board are selected and may be removed once
7615 it's determined that the deletion is appropriate.
7617 If @code{Import()} is called with @code{setnewpoint}, then the location
7618 of new components can be specified. This is where parts show up when
7619 they're added to the board. The default is the center of the board.
7621 @table @code
7623 @item Import(setnewpoint)
7625 Prompts the user to click on the board somewhere, uses that point. If
7626 called by a hotkey, uses the current location of the crosshair.
7628 @item Import(setnewpoint,mark)
7630 Uses the location of the mark. If no mark is present, the point is
7631 not changed.
7633 @item Import(setnewpoint,center)
7635 Resets the point to the center of the board.
7637 @item Import(setnewpoint,X,Y,units)
7639 Sets the point to the specific coordinates given. Example:
7640 @code{Import(setnewpoint,50,25,mm)}
7642 @end table
7644 Note that the X and Y locations are stored in attributes named
7645 @code{import::newX} and @code{import::newY} so you could change them
7646 manually if you wished.
7648 Calling @code{Import(setdisperse,D,units)} sets how much the newly
7649 placed elements are dispersed relative to the set point. For example,
7650 @code{Import(setdisperse,10,mm)} will offset each part randomly up to
7651 10mm away from the point. The default dispersion is 1/10th of the
7652 smallest board dimension. Dispersion is saved in the
7653 @code{import::disperse} attribute.
7655 %end-doc */
7657 static int
7658 ActionImport (int argc, char **argv, Coord x, Coord y)
7660 char *mode;
7661 char **sources = NULL;
7662 int nsources = 0;
7664 #ifdef DEBUG
7665 printf("ActionImport: =========== Entering ActionImport ============\n");
7666 #endif
7668 mode = ARG (0);
7670 if (mode && strcasecmp (mode, "setdisperse") == 0)
7672 char *ds, *units;
7673 char buf[50];
7675 ds = ARG (1);
7676 units = ARG (2);
7677 if (!ds)
7679 const char *as = AttributeGet (PCB, "import::disperse");
7680 ds = gui->prompt_for(_("Enter dispersion:"), as ? as : "0");
7682 if (units)
7684 sprintf(buf, "%s%s", ds, units);
7685 AttributePut (PCB, "import::disperse", buf);
7687 else
7688 AttributePut (PCB, "import::disperse", ds);
7689 if (ARG (1) == NULL)
7690 free (ds);
7691 return 0;
7694 if (mode && strcasecmp (mode, "setnewpoint") == 0)
7696 const char *xs, *ys, *units;
7697 Coord x, y;
7698 char buf[50];
7700 xs = ARG (1);
7701 ys = ARG (2);
7702 units = ARG (3);
7704 if (!xs)
7706 gui->get_coords (_("Click on a location"), &x, &y);
7708 else if (strcasecmp (xs, "center") == 0)
7710 AttributeRemove (PCB, "import::newX");
7711 AttributeRemove (PCB, "import::newY");
7712 return 0;
7714 else if (strcasecmp (xs, "mark") == 0)
7716 if (!Marked.status)
7717 return 0;
7719 x = Marked.X;
7720 y = Marked.Y;
7722 else if (ys)
7724 x = GetValue (xs, units, NULL);
7725 y = GetValue (ys, units, NULL);
7727 else
7729 Message (_("Bad syntax for Import(setnewpoint)"));
7730 return 1;
7733 pcb_snprintf (buf, sizeof (buf), "%$ms", x);
7734 AttributePut (PCB, "import::newX", buf);
7735 pcb_snprintf (buf, sizeof (buf), "%$ms", y);
7736 AttributePut (PCB, "import::newY", buf);
7737 return 0;
7740 if (! mode)
7741 mode = AttributeGet (PCB, "import::mode");
7742 if (! mode)
7743 mode = "gnetlist";
7745 if (argc > 1)
7747 sources = argv + 1;
7748 nsources = argc - 1;
7751 if (! sources)
7753 char sname[40];
7754 char *src;
7756 nsources = -1;
7757 do {
7758 nsources ++;
7759 sprintf(sname, "import::src%d", nsources);
7760 src = AttributeGet (PCB, sname);
7761 } while (src);
7763 if (nsources > 0)
7765 sources = (char **) malloc ((nsources + 1) * sizeof (char *));
7766 nsources = -1;
7767 do {
7768 nsources ++;
7769 sprintf(sname, "import::src%d", nsources);
7770 src = AttributeGet (PCB, sname);
7771 sources[nsources] = src;
7772 } while (src);
7776 if (! sources)
7778 /* Replace .pcb with .sch and hope for the best. */
7779 char *pcbname = PCB->Filename;
7780 char *schname;
7781 char *dot, *slash, *bslash;
7783 if (!pcbname)
7784 return hid_action("ImportGUI");
7786 schname = (char *) malloc (strlen(pcbname) + 5);
7787 strcpy (schname, pcbname);
7788 dot = strchr (schname, '.');
7789 slash = strchr (schname, '/');
7790 bslash = strchr (schname, '\\');
7791 if (dot && slash && dot < slash)
7792 dot = NULL;
7793 if (dot && bslash && dot < bslash)
7794 dot = NULL;
7795 if (dot)
7796 *dot = 0;
7797 strcat (schname, ".sch");
7799 if (access (schname, F_OK))
7801 free (schname);
7802 return hid_action("ImportGUI");
7805 sources = (char **) malloc (2 * sizeof (char *));
7806 sources[0] = schname;
7807 sources[1] = NULL;
7808 nsources = 1;
7811 if (strcasecmp (mode, "gnetlist") == 0)
7813 char *tmpfile = tempfile_name_new ("gnetlist_output");
7814 char **cmd;
7815 int i;
7817 if (tmpfile == NULL) {
7818 Message (_("Could not create temp file"));
7819 return 1;
7822 cmd = (char **) malloc ((7 + nsources) * sizeof (char *));
7823 cmd[0] = Settings.GnetlistProgram;
7824 cmd[1] = "-g";
7825 cmd[2] = "pcbfwd";
7826 cmd[3] = "-o";
7827 cmd[4] = tmpfile;
7828 cmd[5] = "--";
7829 for (i=0; i<nsources; i++)
7830 cmd[6+i] = sources[i];
7831 cmd[6+nsources] = NULL;
7833 #ifdef DEBUG
7834 printf("ActionImport: =========== About to run gnetlist ============\n");
7835 printf("%s %s %s %s %s %s %s ...\n",
7836 cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6]);
7837 #endif
7839 if (pcb_spawnvp (cmd))
7841 unlink (tmpfile);
7842 return 1;
7845 #ifdef DEBUG
7846 printf("ActionImport: =========== About to run ActionExecuteFile, file = %s ============\n", tmpfile);
7847 #endif
7849 cmd[0] = tmpfile;
7850 cmd[1] = NULL;
7851 ActionExecuteFile (1, cmd, 0, 0);
7853 free (cmd);
7854 tempfile_unlink (tmpfile);
7856 else if (strcasecmp (mode, "make") == 0)
7858 int must_free_tmpfile = 0;
7859 char *tmpfile;
7860 char *cmd[10];
7861 int i;
7862 char *srclist;
7863 int srclen;
7864 char *user_outfile = NULL;
7865 char *user_makefile = NULL;
7866 char *user_target = NULL;
7869 user_outfile = AttributeGet (PCB, "import::outfile");
7870 user_makefile = AttributeGet (PCB, "import::makefile");
7871 user_target = AttributeGet (PCB, "import::target");
7872 if (user_outfile && !user_target)
7873 user_target = user_outfile;
7875 if (user_outfile)
7876 tmpfile = user_outfile;
7877 else
7879 tmpfile = tempfile_name_new ("gnetlist_output");
7880 if (tmpfile == NULL) {
7881 Message (_("Could not create temp file"));
7882 free (sources);
7883 return 1;
7885 must_free_tmpfile = 1;
7888 srclen = sizeof("SRCLIST=") + 2;
7889 for (i=0; i<nsources; i++)
7890 srclen += strlen (sources[i]) + 2;
7891 srclist = (char *) malloc (srclen);
7892 strcpy (srclist, "SRCLIST=");
7893 for (i=0; i<nsources; i++)
7895 if (i)
7896 strcat (srclist, " ");
7897 strcat (srclist, sources[i]);
7900 cmd[0] = Settings.MakeProgram;
7901 cmd[1] = "-s";
7902 cmd[2] = Concat ("PCB=", PCB->Filename, NULL);
7903 cmd[3] = srclist;
7904 cmd[4] = Concat ("OUT=", tmpfile, NULL);
7905 i = 5;
7906 if (user_makefile)
7908 cmd[i++] = "-f";
7909 cmd[i++] = user_makefile;
7911 cmd[i++] = user_target ? user_target : (char *)"pcb_import";
7912 cmd[i++] = NULL;
7914 if (pcb_spawnvp (cmd))
7916 if (must_free_tmpfile)
7917 unlink (tmpfile);
7918 free (cmd[2]);
7919 free (cmd[3]);
7920 free (cmd[4]);
7921 return 1;
7924 cmd[0] = tmpfile;
7925 cmd[1] = NULL;
7926 ActionExecuteFile (1, cmd, 0, 0);
7928 free (cmd[2]);
7929 free (cmd[3]);
7930 free (cmd[4]);
7931 if (must_free_tmpfile)
7932 tempfile_unlink (tmpfile);
7934 else
7936 Message (_("Unknown import mode: %s\n"), mode);
7937 return 1;
7940 DeleteRats (false);
7941 AddAllRats (false, NULL);
7943 #ifdef DEBUG
7944 printf("ActionImport: =========== Leaving ActionImport ============\n");
7945 #endif
7947 return 0;
7950 /* ------------------------------------------------------------ */
7952 static const char attributes_syntax[] =
7953 N_("Attributes(Layout|Layer|Element)\n"
7954 "Attributes(Layer,layername)");
7956 static const char attributes_help[] =
7957 N_("Let the user edit the attributes of the layout, current or given\n"
7958 "layer, or selected element.");
7960 /* %start-doc actions Attributes
7962 This just pops up a dialog letting the user edit the attributes of the
7963 pcb, an element, or a layer.
7965 %end-doc */
7968 static int
7969 ActionAttributes (int argc, char **argv, Coord x, Coord y)
7971 char *function = ARG (0);
7972 char *layername = ARG (1);
7973 char *buf;
7975 if (!function)
7976 AFAIL (attributes);
7978 if (!gui->edit_attributes)
7980 Message (_("This GUI doesn't support Attribute Editing\n"));
7981 return 1;
7984 switch (GetFunctionID (function))
7986 case F_Layout:
7988 gui->edit_attributes(_("Layout Attributes"), &(PCB->Attributes));
7989 return 0;
7992 case F_Layer:
7994 LayerType *layer = CURRENT;
7995 if (layername)
7997 int i;
7998 layer = NULL;
7999 for (i=0; i<max_copper_layer; i++)
8000 if (strcmp (PCB->Data->Layer[i].Name, layername) == 0)
8002 layer = & (PCB->Data->Layer[i]);
8003 break;
8005 if (layer == NULL)
8007 Message (_("No layer named %s\n"), layername);
8008 return 1;
8011 buf = (char *) malloc (strlen (layer->Name) +
8012 strlen (_("Layer %s Attributes")));
8013 sprintf (buf, _("Layer %s Attributes"), layer->Name);
8014 gui->edit_attributes(buf, &(layer->Attributes));
8015 free (buf);
8016 return 0;
8019 case F_Element:
8021 int n_found = 0;
8022 ElementType *e = NULL;
8023 ELEMENT_LOOP (PCB->Data);
8025 if (TEST_FLAG (SELECTEDFLAG, element))
8027 e = element;
8028 n_found ++;
8031 END_LOOP;
8032 if (n_found > 1)
8034 Message (_("Too many elements selected\n"));
8035 return 1;
8037 if (n_found == 0)
8039 void *ptrtmp;
8040 gui->get_coords (_("Click on an element"), &x, &y);
8041 if ((SearchScreen
8042 (x, y, ELEMENT_TYPE, &ptrtmp,
8043 &ptrtmp, &ptrtmp)) != NO_TYPE)
8044 e = (ElementType *) ptrtmp;
8045 else
8047 Message (_("No element found there\n"));
8048 return 1;
8052 if (NAMEONPCB_NAME(e))
8054 buf = (char *) malloc (strlen (NAMEONPCB_NAME(e)) +
8055 strlen (_("Element %s Attributes")));
8056 sprintf(buf, _("Element %s Attributes"), NAMEONPCB_NAME(e));
8058 else
8060 buf = strdup (_("Unnamed Element Attributes"));
8062 gui->edit_attributes(buf, &(e->Attributes));
8063 free (buf);
8064 break;
8067 default:
8068 AFAIL (attributes);
8071 return 0;
8074 /* --------------------------------------------------------------------------- */
8076 HID_Action action_action_list[] = {
8077 {"AddRats", 0, ActionAddRats,
8078 addrats_help, addrats_syntax}
8080 {"Attributes", 0, ActionAttributes,
8081 attributes_help, attributes_syntax}
8083 {"Atomic", 0, ActionAtomic,
8084 atomic_help, atomic_syntax}
8086 {"AutoPlaceSelected", 0, ActionAutoPlaceSelected,
8087 autoplace_help, autoplace_syntax}
8089 {"AutoRoute", 0, ActionAutoRoute,
8090 autoroute_help, autoroute_syntax}
8092 {"ChangeClearSize", 0, ActionChangeClearSize,
8093 changeclearsize_help, changeclearsize_syntax}
8095 {"ChangeDrillSize", 0, ActionChange2ndSize,
8096 changedrillsize_help, changedrillsize_syntax}
8098 {"ChangeHole", 0, ActionChangeHole,
8099 changehold_help, changehold_syntax}
8101 {"ChangeJoin", 0, ActionChangeJoin,
8102 changejoin_help, changejoin_syntax}
8104 {"ChangeName", 0, ActionChangeName,
8105 changename_help, changename_syntax}
8107 {"ChangePaste", 0, ActionChangePaste,
8108 changepaste_help, changepaste_syntax}
8110 {"ChangePinName", 0, ActionChangePinName,
8111 changepinname_help, changepinname_syntax}
8113 {"ChangeSize", 0, ActionChangeSize,
8114 changesize_help, changesize_syntax}
8116 {"ChangeSquare", 0, ActionChangeSquare,
8117 changesquare_help, changesquare_syntax}
8119 {"ChangeOctagon", 0, ActionChangeOctagon,
8120 changeoctagon_help, changeoctagon_syntax}
8122 {"ClearSquare", 0, ActionClearSquare,
8123 clearsquare_help, clearsquare_syntax}
8125 {"ClearOctagon", 0, ActionClearOctagon,
8126 clearoctagon_help, clearoctagon_syntax}
8128 {"Connection", 0, ActionConnection,
8129 connection_help, connection_syntax}
8131 {"Delete", 0, ActionDelete,
8132 delete_help, delete_syntax}
8134 {"DeleteRats", 0, ActionDeleteRats,
8135 deleterats_help, deleterats_syntax}
8137 {"DisperseElements", 0, ActionDisperseElements,
8138 disperseelements_help, disperseelements_syntax}
8140 {"Display", 0, ActionDisplay,
8141 display_help, display_syntax}
8143 {"DRC", 0, ActionDRCheck,
8144 drc_help, drc_syntax}
8146 {"DumpLibrary", 0, ActionDumpLibrary,
8147 dumplibrary_help, dumplibrary_syntax}
8149 {"ExecuteFile", 0, ActionExecuteFile,
8150 executefile_help, executefile_syntax}
8152 {"Flip", N_("Click on Object or Flip Point"), ActionFlip,
8153 flip_help, flip_syntax}
8155 {"LoadFrom", 0, ActionLoadFrom,
8156 loadfrom_help, loadfrom_syntax}
8158 {"MarkCrosshair", 0, ActionMarkCrosshair,
8159 markcrosshair_help, markcrosshair_syntax}
8161 {"Message", 0, ActionMessage,
8162 message_help, message_syntax}
8164 {"MinMaskGap", 0, ActionMinMaskGap,
8165 minmaskgap_help, minmaskgap_syntax}
8167 {"MinClearGap", 0, ActionMinClearGap,
8168 mincleargap_help, mincleargap_syntax}
8170 {"Mode", 0, ActionMode,
8171 mode_help, mode_syntax}
8173 {"MorphPolygon", 0, ActionMorphPolygon,
8174 morphpolygon_help, morphpolygon_syntax}
8176 {"PasteBuffer", 0, ActionPasteBuffer,
8177 pastebuffer_help, pastebuffer_syntax}
8179 {"Quit", 0, ActionQuit,
8180 quit_help, quit_syntax}
8182 {"RemoveSelected", 0, ActionRemoveSelected,
8183 removeselected_help, removeselected_syntax}
8185 {"Renumber", 0, ActionRenumber,
8186 renumber_help, renumber_syntax}
8188 {"RipUp", 0, ActionRipUp,
8189 ripup_help, ripup_syntax}
8191 {"Select", 0, ActionSelect,
8192 select_help, select_syntax}
8194 {"Unselect", 0, ActionUnselect,
8195 unselect_help, unselect_syntax}
8197 {"SaveSettings", 0, ActionSaveSettings,
8198 savesettings_help, savesettings_syntax}
8200 {"SaveTo", 0, ActionSaveTo,
8201 saveto_help, saveto_syntax}
8203 {"SetSquare", 0, ActionSetSquare,
8204 setsquare_help, setsquare_syntax}
8206 {"SetOctagon", 0, ActionSetOctagon,
8207 setoctagon_help, setoctagon_syntax}
8209 {"SetThermal", 0, ActionSetThermal,
8210 setthermal_help, setthermal_syntax}
8212 {"SetValue", 0, ActionSetValue,
8213 setvalue_help, setvalue_syntax}
8215 {"ToggleHideName", 0, ActionToggleHideName,
8216 togglehidename_help, togglehidename_syntax}
8218 {"Undo", 0, ActionUndo,
8219 undo_help, undo_syntax}
8221 {"Redo", 0, ActionRedo,
8222 redo_help, redo_syntax}
8224 {"SetSame", N_("Select item to use attributes from"), ActionSetSame,
8225 setsame_help, setsame_syntax}
8227 {"SetFlag", 0, ActionSetFlag,
8228 setflag_help, setflag_syntax}
8230 {"ClrFlag", 0, ActionClrFlag,
8231 clrflag_help, clrflag_syntax}
8233 {"ChangeFlag", 0, ActionChangeFlag,
8234 changeflag_help, changeflag_syntax}
8236 {"Polygon", 0, ActionPolygon,
8237 polygon_help, polygon_syntax}
8239 {"RouteStyle", 0, ActionRouteStyle,
8240 routestyle_help, routestyle_syntax}
8242 {"MoveObject", N_("Select an Object"), ActionMoveObject,
8243 moveobject_help, moveobject_syntax}
8245 {"MoveToCurrentLayer", 0, ActionMoveToCurrentLayer,
8246 movetocurrentlayer_help, movetocurrentlayer_syntax}
8248 {"New", 0, ActionNew,
8249 new_help, new_syntax}
8251 {"pscalib", 0, ActionPSCalib}
8253 {"ElementList", 0, ActionElementList,
8254 elementlist_help, elementlist_syntax}
8256 {"ElementSetAttr", 0, ActionElementSetAttr,
8257 elementsetattr_help, elementsetattr_syntax}
8259 {"ExecCommand", 0, ActionExecCommand,
8260 execcommand_help, execcommand_syntax}
8262 {"Import", 0, ActionImport,
8263 import_help, import_syntax}
8267 REGISTER_ACTIONS (action_action_list)