Use gui->get_coords to get the crosshair coordinates.
[geda-pcb/pcjc2.git] / src / action.c
blob72c5793e3b595aa4f57e6b2189d06180ffb2a24f
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;
2872 Coord x, y;
2874 gui->get_coords(_("Click on an element"), &x, &y);
2876 switch (SearchScreen (x, y,
2877 ELEMENT_TYPE | PIN_TYPE | PAD_TYPE |
2878 VIA_TYPE, (void **) &ptr1, (void **) &ptr2,
2879 (void **) &ptr3))
2881 case ELEMENT_TYPE:
2882 PIN_LOOP ((ElementType *) ptr1);
2884 if (TEST_FLAG (DISPLAYNAMEFLAG, pin))
2885 ErasePinName (pin);
2886 else
2887 DrawPinName (pin);
2888 AddObjectToFlagUndoList (PIN_TYPE, ptr1, pin, pin);
2889 TOGGLE_FLAG (DISPLAYNAMEFLAG, pin);
2891 END_LOOP;
2892 PAD_LOOP ((ElementType *) ptr1);
2894 if (TEST_FLAG (DISPLAYNAMEFLAG, pad))
2895 ErasePadName (pad);
2896 else
2897 DrawPadName (pad);
2898 AddObjectToFlagUndoList (PAD_TYPE, ptr1, pad, pad);
2899 TOGGLE_FLAG (DISPLAYNAMEFLAG, pad);
2901 END_LOOP;
2902 SetChangedFlag (true);
2903 IncrementUndoSerialNumber ();
2904 Draw ();
2905 break;
2907 case PIN_TYPE:
2908 if (TEST_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2))
2909 ErasePinName ((PinType *) ptr2);
2910 else
2911 DrawPinName ((PinType *) ptr2);
2912 AddObjectToFlagUndoList (PIN_TYPE, ptr1, ptr2, ptr3);
2913 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2);
2914 SetChangedFlag (true);
2915 IncrementUndoSerialNumber ();
2916 Draw ();
2917 break;
2919 case PAD_TYPE:
2920 if (TEST_FLAG (DISPLAYNAMEFLAG, (PadType *) ptr2))
2921 ErasePadName ((PadType *) ptr2);
2922 else
2923 DrawPadName ((PadType *) ptr2);
2924 AddObjectToFlagUndoList (PAD_TYPE, ptr1, ptr2, ptr3);
2925 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PadType *) ptr2);
2926 SetChangedFlag (true);
2927 IncrementUndoSerialNumber ();
2928 Draw ();
2929 break;
2930 case VIA_TYPE:
2931 if (TEST_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2))
2932 EraseViaName ((PinType *) ptr2);
2933 else
2934 DrawViaName ((PinType *) ptr2);
2935 AddObjectToFlagUndoList (VIA_TYPE, ptr1, ptr2, ptr3);
2936 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2);
2937 SetChangedFlag (true);
2938 IncrementUndoSerialNumber ();
2939 Draw ();
2940 break;
2942 break;
2944 default:
2945 err = 1;
2948 else if (function && str_dir)
2950 switch (GetFunctionID (function))
2952 case F_ToggleGrid:
2953 if (argc > 2)
2955 PCB->GridOffsetX = GetValue (argv[1], NULL, NULL);
2956 PCB->GridOffsetY = GetValue (argv[2], NULL, NULL);
2957 if (Settings.DrawGrid)
2958 Redraw ();
2960 break;
2962 default:
2963 err = 1;
2964 break;
2967 else
2968 err = 1;
2970 if (err)
2971 AFAIL (display);
2973 return 0;
2976 /* --------------------------------------------------------------------------- */
2978 static const char mode_syntax[] =
2979 N_("Mode(Arc|Arrow|Copy|InsertPoint|Line|Lock|Move|None|PasteBuffer)\n"
2980 "Mode(Polygon|Rectangle|Remove|Rotate|Text|Thermal|Via)\n"
2981 "Mode(Notify|Release|Cancel|Stroke)\n"
2982 "Mode(Save|Restore)");
2984 static const char mode_help[] = N_("Change or use the tool mode.");
2986 /* %start-doc actions Mode
2988 @table @code
2990 @item Arc
2991 @itemx Arrow
2992 @itemx Copy
2993 @itemx InsertPoint
2994 @itemx Line
2995 @itemx Lock
2996 @itemx Move
2997 @itemx None
2998 @itemx PasteBuffer
2999 @itemx Polygon
3000 @itemx Rectangle
3001 @itemx Remove
3002 @itemx Rotate
3003 @itemx Text
3004 @itemx Thermal
3005 @itemx Via
3006 Select the indicated tool.
3008 @item Notify
3009 Called when you press the mouse button, or move the mouse.
3011 @item Release
3012 Called when you release the mouse button.
3014 @item Cancel
3015 Cancels any pending tool activity, allowing you to restart elsewhere.
3016 For example, this allows you to start a new line rather than attach a
3017 line to the previous line.
3019 @item Escape
3020 Similar to Cancel but calling this action a second time will return
3021 to the Arrow tool.
3023 @item Stroke
3024 If your @code{pcb} was built with libstroke, this invokes the stroke
3025 input method. If not, this will restart a drawing mode if you were
3026 drawing, else it will select objects.
3028 @item Save
3029 Remembers the current tool.
3031 @item Restore
3032 Restores the tool to the last saved tool.
3034 @end table
3036 %end-doc */
3038 static int
3039 ActionMode (int argc, char **argv, Coord x, Coord y)
3041 char *function = ARG (0);
3043 if (function)
3045 Note.X = Crosshair.X;
3046 Note.Y = Crosshair.Y;
3047 notify_crosshair_change (false);
3048 switch (GetFunctionID (function))
3050 case F_Arc:
3051 SetMode (ARC_MODE);
3052 break;
3053 case F_Arrow:
3054 SetMode (ARROW_MODE);
3055 break;
3056 case F_Copy:
3057 SetMode (COPY_MODE);
3058 break;
3059 case F_InsertPoint:
3060 SetMode (INSERTPOINT_MODE);
3061 break;
3062 case F_Line:
3063 SetMode (LINE_MODE);
3064 break;
3065 case F_Lock:
3066 SetMode (LOCK_MODE);
3067 break;
3068 case F_Move:
3069 SetMode (MOVE_MODE);
3070 break;
3071 case F_None:
3072 SetMode (NO_MODE);
3073 break;
3074 case F_Cancel:
3076 int saved_mode = Settings.Mode;
3077 SetMode (NO_MODE);
3078 SetMode (saved_mode);
3080 break;
3081 case F_Escape:
3083 switch (Settings.Mode)
3085 case VIA_MODE:
3086 case PASTEBUFFER_MODE:
3087 case TEXT_MODE:
3088 case ROTATE_MODE:
3089 case REMOVE_MODE:
3090 case MOVE_MODE:
3091 case COPY_MODE:
3092 case INSERTPOINT_MODE:
3093 case RUBBERBANDMOVE_MODE:
3094 case THERMAL_MODE:
3095 case LOCK_MODE:
3096 SetMode (NO_MODE);
3097 SetMode (ARROW_MODE);
3098 break;
3100 case LINE_MODE:
3101 if (Crosshair.AttachedLine.State == STATE_FIRST)
3102 SetMode (ARROW_MODE);
3103 else
3105 SetMode (NO_MODE);
3106 SetMode (LINE_MODE);
3108 break;
3110 case RECTANGLE_MODE:
3111 if (Crosshair.AttachedBox.State == STATE_FIRST)
3112 SetMode (ARROW_MODE);
3113 else
3115 SetMode (NO_MODE);
3116 SetMode (RECTANGLE_MODE);
3118 break;
3120 case POLYGON_MODE:
3121 if (Crosshair.AttachedLine.State == STATE_FIRST)
3122 SetMode (ARROW_MODE);
3123 else
3125 SetMode (NO_MODE);
3126 SetMode (POLYGON_MODE);
3128 break;
3130 case POLYGONHOLE_MODE:
3131 if (Crosshair.AttachedLine.State == STATE_FIRST)
3132 SetMode (ARROW_MODE);
3133 else
3135 SetMode (NO_MODE);
3136 SetMode (POLYGONHOLE_MODE);
3138 break;
3140 case ARC_MODE:
3141 if (Crosshair.AttachedBox.State == STATE_FIRST)
3142 SetMode (ARROW_MODE);
3143 else
3145 SetMode (NO_MODE);
3146 SetMode (ARC_MODE);
3148 break;
3150 case ARROW_MODE:
3151 break;
3153 default:
3154 break;
3157 break;
3159 case F_Notify:
3160 NotifyMode ();
3161 break;
3162 case F_PasteBuffer:
3163 SetMode (PASTEBUFFER_MODE);
3164 break;
3165 case F_Polygon:
3166 SetMode (POLYGON_MODE);
3167 break;
3168 case F_PolygonHole:
3169 SetMode (POLYGONHOLE_MODE);
3170 break;
3171 #ifndef HAVE_LIBSTROKE
3172 case F_Release:
3173 ReleaseMode ();
3174 break;
3175 #else
3176 case F_Release:
3177 if (mid_stroke)
3178 FinishStroke ();
3179 else
3180 ReleaseMode ();
3181 break;
3182 #endif
3183 case F_Remove:
3184 SetMode (REMOVE_MODE);
3185 break;
3186 case F_Rectangle:
3187 SetMode (RECTANGLE_MODE);
3188 break;
3189 case F_Rotate:
3190 SetMode (ROTATE_MODE);
3191 break;
3192 case F_Stroke:
3193 #ifdef HAVE_LIBSTROKE
3194 mid_stroke = true;
3195 StrokeBox.X1 = Crosshair.X;
3196 StrokeBox.Y1 = Crosshair.Y;
3197 break;
3198 #else
3199 /* Handle middle mouse button restarts of drawing mode. If not in
3200 | a drawing mode, middle mouse button will select objects.
3202 if (Settings.Mode == LINE_MODE
3203 && Crosshair.AttachedLine.State != STATE_FIRST)
3205 SetMode (LINE_MODE);
3207 else if (Settings.Mode == ARC_MODE
3208 && Crosshair.AttachedBox.State != STATE_FIRST)
3209 SetMode (ARC_MODE);
3210 else if (Settings.Mode == RECTANGLE_MODE
3211 && Crosshair.AttachedBox.State != STATE_FIRST)
3212 SetMode (RECTANGLE_MODE);
3213 else if (Settings.Mode == POLYGON_MODE
3214 && Crosshair.AttachedLine.State != STATE_FIRST)
3215 SetMode (POLYGON_MODE);
3216 else
3218 SaveMode ();
3219 saved_mode = true;
3220 SetMode (ARROW_MODE);
3221 NotifyMode ();
3223 break;
3224 #endif
3225 case F_Text:
3226 SetMode (TEXT_MODE);
3227 break;
3228 case F_Thermal:
3229 SetMode (THERMAL_MODE);
3230 break;
3231 case F_Via:
3232 SetMode (VIA_MODE);
3233 break;
3235 case F_Restore: /* restore the last saved mode */
3236 RestoreMode ();
3237 break;
3239 case F_Save: /* save currently selected mode */
3240 SaveMode ();
3241 break;
3243 notify_crosshair_change (true);
3244 return 0;
3247 AFAIL (mode);
3250 /* --------------------------------------------------------------------------- */
3252 static const char removeselected_syntax[] = N_("RemoveSelected()");
3254 static const char removeselected_help[] = N_("Removes any selected objects.");
3256 /* %start-doc actions RemoveSelected
3258 %end-doc */
3260 static int
3261 ActionRemoveSelected (int argc, char **argv, Coord x, Coord y)
3263 if (RemoveSelected ())
3264 SetChangedFlag (true);
3265 return 0;
3268 /* --------------------------------------------------------------------------- */
3270 static const char renumber_syntax[] = N_("Renumber()\n"
3271 "Renumber(filename)");
3273 static const char renumber_help[] =
3274 N_("Renumber all elements. The changes will be recorded to filename\n"
3275 "for use in backannotating these changes to the schematic.");
3277 /* %start-doc actions Renumber
3279 %end-doc */
3281 static int
3282 ActionRenumber (int argc, char **argv, Coord x, Coord y)
3284 bool changed = false;
3285 ElementType **element_list;
3286 ElementType **locked_element_list;
3287 unsigned int i, j, k, cnt, lock_cnt;
3288 unsigned int tmpi;
3289 size_t sz;
3290 char *tmps;
3291 char *name;
3292 FILE *out;
3293 static char * default_file = NULL;
3294 size_t cnt_list_sz = 100;
3295 struct _cnt_list
3297 char *name;
3298 unsigned int cnt;
3299 } *cnt_list;
3300 char **was, **is, *pin;
3301 unsigned int c_cnt = 0;
3302 int unique, ok;
3303 int free_name = 0;
3305 if (argc < 1)
3308 * We deal with the case where name already exists in this
3309 * function so the GUI doesn't need to deal with it
3311 name = gui->fileselect (_("Save Renumber Annotation File As ..."),
3312 _("Choose a file to record the renumbering to.\n"
3313 "This file may be used to back annotate the\n"
3314 "change to the schematics.\n"),
3315 default_file, ".eco", "eco",
3318 free_name = 1;
3320 else
3321 name = argv[0];
3323 if (default_file)
3325 free (default_file);
3326 default_file = NULL;
3329 if (name && *name)
3331 default_file = strdup (name);
3334 if ((out = fopen (name, "r")))
3336 fclose (out);
3337 if (!gui->confirm_dialog (_("File exists! Ok to overwrite?"), 0))
3339 if (free_name && name)
3340 free (name);
3341 return 0;
3345 if ((out = fopen (name, "w")) == NULL)
3347 Message (_("Could not open %s\n"), name);
3348 if (free_name && name)
3349 free (name);
3350 return 1;
3353 if (free_name && name)
3354 free (name);
3356 fprintf (out, "*COMMENT* PCB Annotation File\n");
3357 fprintf (out, "*FILEVERSION* 20061031\n");
3360 * Make a first pass through all of the elements and sort them out
3361 * by location on the board. While here we also collect a list of
3362 * locked elements.
3364 * We'll actually renumber things in the 2nd pass.
3366 element_list = (ElementType **)calloc (PCB->Data->ElementN, sizeof (ElementType *));
3367 locked_element_list = (ElementType **)calloc (PCB->Data->ElementN, sizeof (ElementType *));
3368 was = (char **)calloc (PCB->Data->ElementN, sizeof (char *));
3369 is = (char **)calloc (PCB->Data->ElementN, sizeof (char *));
3370 if (element_list == NULL || locked_element_list == NULL || was == NULL
3371 || is == NULL)
3373 fprintf (stderr, "calloc() failed in %s\n", __FUNCTION__);
3374 exit (1);
3378 cnt = 0;
3379 lock_cnt = 0;
3380 ELEMENT_LOOP (PCB->Data);
3382 if (TEST_FLAG (LOCKFLAG, element->Name) || TEST_FLAG (LOCKFLAG, element))
3385 * add to the list of locked elements which we won't try to
3386 * renumber and whose reference designators are now reserved.
3388 pcb_fprintf (out,
3389 "*WARN* Element \"%s\" at %$md is locked and will not be renumbered.\n",
3390 UNKNOWN (NAMEONPCB_NAME (element)), element->MarkX, element->MarkY);
3391 locked_element_list[lock_cnt] = element;
3392 lock_cnt++;
3395 else
3397 /* count of devices which will be renumbered */
3398 cnt++;
3400 /* search for correct position in the list */
3401 i = 0;
3402 while (element_list[i] && element->MarkY > element_list[i]->MarkY)
3403 i++;
3406 * We have found the position where we have the first element that
3407 * has the same Y value or a lower Y value. Now move forward if
3408 * needed through the X values
3410 while (element_list[i]
3411 && element->MarkY == element_list[i]->MarkY
3412 && element->MarkX > element_list[i]->MarkX)
3413 i++;
3415 for (j = cnt - 1; j > i; j--)
3417 element_list[j] = element_list[j - 1];
3419 element_list[i] = element;
3422 END_LOOP;
3426 * Now that the elements are sorted by board position, we go through
3427 * and renumber them.
3431 * turn off the flag which requires unique names so it doesn't get
3432 * in our way. When we're done with the renumber we will have unique
3433 * names.
3435 unique = TEST_FLAG (UNIQUENAMEFLAG, PCB);
3436 CLEAR_FLAG (UNIQUENAMEFLAG, PCB);
3438 cnt_list = (struct _cnt_list *)calloc (cnt_list_sz, sizeof (struct _cnt_list));
3439 for (i = 0; i < cnt; i++)
3441 /* If there is no refdes, maybe just spit out a warning */
3442 if (NAMEONPCB_NAME (element_list[i]))
3444 /* figure out the prefix */
3445 tmps = strdup (NAMEONPCB_NAME (element_list[i]));
3446 j = 0;
3447 while (tmps[j] && (tmps[j] < '0' || tmps[j] > '9')
3448 && tmps[j] != '?')
3449 j++;
3450 tmps[j] = '\0';
3452 /* check the counter for this prefix */
3453 for (j = 0;
3454 cnt_list[j].name && (strcmp (cnt_list[j].name, tmps) != 0)
3455 && j < cnt_list_sz; j++);
3457 /* grow the list if needed */
3458 if (j == cnt_list_sz)
3460 cnt_list_sz += 100;
3461 cnt_list = (struct _cnt_list *)realloc (cnt_list, cnt_list_sz);
3462 if (cnt_list == NULL)
3464 fprintf (stderr, _("realloc failed() in %s\n"), __FUNCTION__);
3465 exit (1);
3467 /* zero out the memory that we added */
3468 for (tmpi = j; tmpi < cnt_list_sz; tmpi++)
3470 cnt_list[tmpi].name = NULL;
3471 cnt_list[tmpi].cnt = 0;
3476 * start a new counter if we don't have a counter for this
3477 * prefix
3479 if (!cnt_list[j].name)
3481 cnt_list[j].name = strdup (tmps);
3482 cnt_list[j].cnt = 0;
3486 * check to see if the new refdes is already used by a
3487 * locked element
3491 ok = 1;
3492 cnt_list[j].cnt++;
3493 free (tmps);
3495 /* space for the prefix plus 1 digit plus the '\0' */
3496 sz = strlen (cnt_list[j].name) + 2;
3498 /* and 1 more per extra digit needed to hold the number */
3499 tmpi = cnt_list[j].cnt;
3500 while (tmpi > 10)
3502 sz++;
3503 tmpi = tmpi / 10;
3505 tmps = (char *)malloc (sz * sizeof (char));
3506 sprintf (tmps, "%s%d", cnt_list[j].name, (int) cnt_list[j].cnt);
3509 * now compare to the list of reserved (by locked
3510 * elements) names
3512 for (k = 0; k < lock_cnt; k++)
3514 if (strcmp
3515 (UNKNOWN (NAMEONPCB_NAME (locked_element_list[k])),
3516 tmps) == 0)
3518 ok = 0;
3519 break;
3524 while (!ok);
3526 if (strcmp (tmps, NAMEONPCB_NAME (element_list[i])) != 0)
3528 fprintf (out, "*RENAME* \"%s\" \"%s\"\n",
3529 NAMEONPCB_NAME (element_list[i]), tmps);
3531 /* add this rename to our table of renames so we can update the netlist */
3532 was[c_cnt] = strdup (NAMEONPCB_NAME (element_list[i]));
3533 is[c_cnt] = strdup (tmps);
3534 c_cnt++;
3536 AddObjectToChangeNameUndoList (ELEMENT_TYPE, NULL, NULL,
3537 element_list[i],
3538 NAMEONPCB_NAME (element_list
3539 [i]));
3541 ChangeObjectName (ELEMENT_TYPE, element_list[i], NULL, NULL,
3542 tmps);
3543 changed = true;
3545 /* we don't free tmps in this case because it is used */
3547 else
3548 free (tmps);
3550 else
3552 pcb_fprintf (out, "*WARN* Element at %$md has no name.\n",
3553 element_list[i]->MarkX, element_list[i]->MarkY);
3558 fclose (out);
3560 /* restore the unique flag setting */
3561 if (unique)
3562 SET_FLAG (UNIQUENAMEFLAG, PCB);
3564 if (changed)
3567 /* update the netlist */
3568 AddNetlistLibToUndoList (&(PCB->NetlistLib));
3570 /* iterate over each net */
3571 for (i = 0; i < PCB->NetlistLib.MenuN; i++)
3574 /* iterate over each pin on the net */
3575 for (j = 0; j < PCB->NetlistLib.Menu[i].EntryN; j++)
3578 /* figure out the pin number part from strings like U3-21 */
3579 tmps = strdup (PCB->NetlistLib.Menu[i].Entry[j].ListEntry);
3580 for (k = 0; tmps[k] && tmps[k] != '-'; k++);
3581 tmps[k] = '\0';
3582 pin = tmps + k + 1;
3584 /* iterate over the list of changed reference designators */
3585 for (k = 0; k < c_cnt; k++)
3588 * if the pin needs to change, change it and quit
3589 * searching in the list.
3591 if (strcmp (tmps, was[k]) == 0)
3593 free (PCB->NetlistLib.Menu[i].Entry[j].ListEntry);
3594 PCB->NetlistLib.Menu[i].Entry[j].ListEntry =
3595 (char *)malloc ((strlen (is[k]) + strlen (pin) +
3596 2) * sizeof (char));
3597 sprintf (PCB->NetlistLib.Menu[i].Entry[j].ListEntry,
3598 "%s-%s", is[k], pin);
3599 k = c_cnt;
3603 free (tmps);
3606 for (k = 0; k < c_cnt; k++)
3608 free (was[k]);
3609 free (is[k]);
3612 NetlistChanged (0);
3613 IncrementUndoSerialNumber ();
3614 SetChangedFlag (true);
3617 free (locked_element_list);
3618 free (element_list);
3619 free (cnt_list);
3620 free (is);
3621 free (was);
3622 return 0;
3626 /* --------------------------------------------------------------------------- */
3628 static const char ripup_syntax[] = N_("RipUp(All|Selected|Element)");
3630 static const char ripup_help[] =
3631 N_("Ripup auto-routed tracks, or convert an element to parts.");
3633 /* %start-doc actions RipUp
3635 @table @code
3637 @item All
3638 Removes all lines and vias which were created by the autorouter.
3640 @item Selected
3641 Removes all selected lines and vias which were created by the
3642 autorouter.
3644 @item Element
3645 Converts the element under the cursor to parts (vias and lines). Note
3646 that this uses the highest numbered paste buffer.
3648 @end table
3650 %end-doc */
3652 static int
3653 ActionRipUp (int argc, char **argv, Coord x, Coord y)
3655 char *function = ARG (0);
3656 bool changed = false;
3658 if (function)
3660 switch (GetFunctionID (function))
3662 case F_All:
3663 ALLLINE_LOOP (PCB->Data);
3665 if (TEST_FLAG (AUTOFLAG, line) && !TEST_FLAG (LOCKFLAG, line))
3667 RemoveObject (LINE_TYPE, layer, line, line);
3668 changed = true;
3671 ENDALL_LOOP;
3672 ALLARC_LOOP (PCB->Data);
3674 if (TEST_FLAG (AUTOFLAG, arc) && !TEST_FLAG (LOCKFLAG, arc))
3676 RemoveObject (ARC_TYPE, layer, arc, arc);
3677 changed = true;
3680 ENDALL_LOOP;
3681 VIA_LOOP (PCB->Data);
3683 if (TEST_FLAG (AUTOFLAG, via) && !TEST_FLAG (LOCKFLAG, via))
3685 RemoveObject (VIA_TYPE, via, via, via);
3686 changed = true;
3689 END_LOOP;
3691 if (changed)
3693 IncrementUndoSerialNumber ();
3694 SetChangedFlag (true);
3696 break;
3697 case F_Selected:
3698 VISIBLELINE_LOOP (PCB->Data);
3700 if (TEST_FLAGS (AUTOFLAG | SELECTEDFLAG, line)
3701 && !TEST_FLAG (LOCKFLAG, line))
3703 RemoveObject (LINE_TYPE, layer, line, line);
3704 changed = true;
3707 ENDALL_LOOP;
3708 if (PCB->ViaOn)
3709 VIA_LOOP (PCB->Data);
3711 if (TEST_FLAGS (AUTOFLAG | SELECTEDFLAG, via)
3712 && !TEST_FLAG (LOCKFLAG, via))
3714 RemoveObject (VIA_TYPE, via, via, via);
3715 changed = true;
3718 END_LOOP;
3719 if (changed)
3721 IncrementUndoSerialNumber ();
3722 SetChangedFlag (true);
3724 break;
3725 case F_Element:
3727 void *ptr1, *ptr2, *ptr3;
3729 if (SearchScreen (Crosshair.X, Crosshair.Y, ELEMENT_TYPE,
3730 &ptr1, &ptr2, &ptr3) != NO_TYPE)
3732 Note.Buffer = Settings.BufferNumber;
3733 SetBufferNumber (MAX_BUFFER - 1);
3734 ClearBuffer (PASTEBUFFER);
3735 CopyObjectToBuffer (PASTEBUFFER->Data, PCB->Data,
3736 ELEMENT_TYPE, ptr1, ptr2, ptr3);
3737 SmashBufferElement (PASTEBUFFER);
3738 PASTEBUFFER->X = 0;
3739 PASTEBUFFER->Y = 0;
3740 SaveUndoSerialNumber ();
3741 EraseObject (ELEMENT_TYPE, ptr1, ptr1);
3742 MoveObjectToRemoveUndoList (ELEMENT_TYPE, ptr1, ptr2, ptr3);
3743 RestoreUndoSerialNumber ();
3744 CopyPastebufferToLayout (0, 0);
3745 SetBufferNumber (Note.Buffer);
3746 SetChangedFlag (true);
3749 break;
3752 return 0;
3755 /* --------------------------------------------------------------------------- */
3757 static const char addrats_syntax[] = N_("AddRats(AllRats|SelectedRats|Close)");
3759 static const char addrats_help[] =
3760 N_("Add one or more rat lines to the board.");
3762 /* %start-doc actions AddRats
3764 @table @code
3766 @item AllRats
3767 Create rat lines for all loaded nets that aren't already connected on
3768 with copper.
3770 @item SelectedRats
3771 Similarly, but only add rat lines for nets connected to selected pins
3772 and pads.
3774 @item Close
3775 Selects the shortest unselected rat on the board.
3777 @end table
3779 %end-doc */
3781 static int
3782 ActionAddRats (int argc, char **argv, Coord x, Coord y)
3784 char *function = ARG (0);
3785 RatType *shorty;
3786 float len, small;
3788 if (function)
3790 if (Settings.RatWarn)
3791 ClearWarnings ();
3792 switch (GetFunctionID (function))
3794 case F_AllRats:
3795 if (AddAllRats (false, NULL))
3796 SetChangedFlag (true);
3797 break;
3798 case F_SelectedRats:
3799 case F_Selected:
3800 if (AddAllRats (true, NULL))
3801 SetChangedFlag (true);
3802 break;
3803 case F_Close:
3804 small = SQUARE (MAX_COORD);
3805 shorty = NULL;
3806 RAT_LOOP (PCB->Data);
3808 if (TEST_FLAG (SELECTEDFLAG, line))
3809 continue;
3810 len = SQUARE (line->Point1.X - line->Point2.X) +
3811 SQUARE (line->Point1.Y - line->Point2.Y);
3812 if (len < small)
3814 small = len;
3815 shorty = line;
3818 END_LOOP;
3819 if (shorty)
3821 AddObjectToFlagUndoList (RATLINE_TYPE, shorty, shorty, shorty);
3822 SET_FLAG (SELECTEDFLAG, shorty);
3823 DrawRat (shorty);
3824 Draw ();
3825 CenterDisplay ((shorty->Point2.X + shorty->Point1.X) / 2,
3826 (shorty->Point2.Y + shorty->Point1.Y) / 2,
3827 false);
3829 break;
3832 return 0;
3835 /* --------------------------------------------------------------------------- */
3837 static const char delete_syntax[] =
3838 N_("Delete(Object|Selected)\n"
3839 "Delete(AllRats|SelectedRats)");
3841 static const char delete_help[] = N_("Delete stuff.");
3843 /* %start-doc actions Delete
3845 %end-doc */
3847 static int
3848 ActionDelete (int argc, char **argv, Coord x, Coord y)
3850 char *function = ARG (0);
3851 int id = GetFunctionID (function);
3853 Note.X = Crosshair.X;
3854 Note.Y = Crosshair.Y;
3856 if (id == -1) /* no arg */
3858 if (RemoveSelected() == false)
3859 id = F_Object;
3862 switch (id)
3864 case F_Object:
3865 SaveMode();
3866 SetMode(REMOVE_MODE);
3867 NotifyMode();
3868 RestoreMode();
3869 break;
3870 case F_Selected:
3871 RemoveSelected();
3872 break;
3873 case F_AllRats:
3874 if (DeleteRats (false))
3875 SetChangedFlag (true);
3876 break;
3877 case F_SelectedRats:
3878 if (DeleteRats (true))
3879 SetChangedFlag (true);
3880 break;
3883 return 0;
3886 /* --------------------------------------------------------------------------- */
3888 static const char deleterats_syntax[] =
3889 N_("DeleteRats(AllRats|Selected|SelectedRats)");
3891 static const char deleterats_help[] = N_("Delete rat lines.");
3893 /* %start-doc actions DeleteRats
3895 %end-doc */
3897 static int
3898 ActionDeleteRats (int argc, char **argv, Coord x, Coord y)
3900 char *function = ARG (0);
3901 if (function)
3903 if (Settings.RatWarn)
3904 ClearWarnings ();
3905 switch (GetFunctionID (function))
3907 case F_AllRats:
3908 if (DeleteRats (false))
3909 SetChangedFlag (true);
3910 break;
3911 case F_SelectedRats:
3912 case F_Selected:
3913 if (DeleteRats (true))
3914 SetChangedFlag (true);
3915 break;
3918 return 0;
3921 /* --------------------------------------------------------------------------- */
3923 static const char autoplace_syntax[] = N_("AutoPlaceSelected()");
3925 static const char autoplace_help[] = N_("Auto-place selected components.");
3927 /* %start-doc actions AutoPlaceSelected
3929 Attempts to re-arrange the selected components such that the nets
3930 connecting them are minimized. Note that you cannot undo this.
3932 %end-doc */
3934 static int
3935 ActionAutoPlaceSelected (int argc, char **argv, Coord x, Coord y)
3937 hid_action("Busy");
3938 if (gui->confirm_dialog (_("Auto-placement can NOT be undone.\n"
3939 "Do you want to continue anyway?\n"), 0))
3941 if (AutoPlaceSelected ())
3942 SetChangedFlag (true);
3944 return 0;
3947 /* --------------------------------------------------------------------------- */
3949 static const char autoroute_syntax[] = N_("AutoRoute(AllRats|SelectedRats)");
3951 static const char autoroute_help[] = N_("Auto-route some or all rat lines.");
3953 /* %start-doc actions AutoRoute
3955 @table @code
3957 @item AllRats
3958 Attempt to autoroute all rats.
3960 @item SelectedRats
3961 Attempt to autoroute the selected rats.
3963 @end table
3965 Before autorouting, it's important to set up a few things. First,
3966 make sure any layers you aren't using are disabled, else the
3967 autorouter may use them. Next, make sure the current line and via
3968 styles are set accordingly. Last, make sure "new lines clear
3969 polygons" is set, in case you eventually want to add a copper pour.
3971 Autorouting takes a while. During this time, the program may not be
3972 responsive.
3974 %end-doc */
3976 static int
3977 ActionAutoRoute (int argc, char **argv, Coord x, Coord y)
3979 char *function = ARG (0);
3980 hid_action("Busy");
3981 if (function) /* one parameter */
3983 switch (GetFunctionID (function))
3985 case F_AllRats:
3986 if (AutoRoute (false))
3987 SetChangedFlag (true);
3988 break;
3989 case F_SelectedRats:
3990 case F_Selected:
3991 if (AutoRoute (true))
3992 SetChangedFlag (true);
3993 break;
3996 return 0;
3999 /* --------------------------------------------------------------------------- */
4001 static const char markcrosshair_syntax[] =
4002 N_("MarkCrosshair()\n"
4003 "MarkCrosshair(Center)");
4005 static const char markcrosshair_help[] = N_("Set/Reset the Crosshair mark.");
4007 /* %start-doc actions MarkCrosshair
4009 The ``mark'' is a small X-shaped target on the display which is
4010 treated like a second origin (the normal origin is the upper let
4011 corner of the board). The GUI will display a second set of
4012 coordinates for this mark, which tells you how far you are from it.
4014 If no argument is given, the mark is toggled - disabled if it was
4015 enabled, or enabled at the current cursor position of disabled. If
4016 the @code{Center} argument is given, the mark is moved to the current
4017 cursor location.
4019 %end-doc */
4021 static int
4022 ActionMarkCrosshair (int argc, char **argv, Coord x, Coord y)
4024 char *function = ARG (0);
4025 if (!function || !*function)
4027 if (Marked.status)
4029 notify_mark_change (false);
4030 Marked.status = false;
4031 notify_mark_change (true);
4033 else
4035 notify_mark_change (false);
4036 Marked.status = false;
4037 Marked.status = true;
4038 Marked.X = Crosshair.X;
4039 Marked.Y = Crosshair.Y;
4040 notify_mark_change (true);
4043 else if (GetFunctionID (function) == F_Center)
4045 notify_mark_change (false);
4046 Marked.status = true;
4047 Marked.X = Crosshair.X;
4048 Marked.Y = Crosshair.Y;
4049 notify_mark_change (true);
4051 return 0;
4054 /* --------------------------------------------------------------------------- */
4056 static const char changesize_syntax[] =
4057 N_("ChangeSize(Object, delta)\n"
4058 "ChangeSize(SelectedObjects|Selected, delta)\n"
4059 "ChangeSize(SelectedLines|SelectedPins|SelectedVias, delta)\n"
4060 "ChangeSize(SelectedPads|SelectedTexts|SelectedNames, delta)\n"
4061 "ChangeSize(SelectedElements, delta)");
4063 static const char changesize_help[] = N_("Changes the size of objects.");
4065 /* %start-doc actions ChangeSize
4067 For lines and arcs, this changes the width. For pins and vias, this
4068 changes the overall diameter of the copper annulus. For pads, this
4069 changes the width and, indirectly, the length. For texts and names,
4070 this changes the scaling factor. For elements, this changes the width
4071 of the silk layer lines and arcs for this element.
4073 %end-doc */
4075 static int
4076 ActionChangeSize (int argc, char **argv, Coord x, Coord y)
4078 char *function = ARG (0);
4079 char *delta = ARG (1);
4080 char *units = ARG (2);
4081 bool absolute; /* indicates if absolute size is given */
4082 Coord value;
4084 if (function && delta)
4086 value = GetValue (delta, units, &absolute);
4087 if (value == 0)
4088 value = delta[0] == '-' ? -Settings.increments->size
4089 : Settings.increments->size;
4090 switch (GetFunctionID (function))
4092 case F_Object:
4094 int type;
4095 void *ptr1, *ptr2, *ptr3;
4097 if ((type =
4098 SearchScreen (Crosshair.X, Crosshair.Y, CHANGESIZE_TYPES,
4099 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4100 if (TEST_FLAG (LOCKFLAG, (PinType *) ptr2))
4101 Message (_("Sorry, the object is locked\n"));
4102 if (ChangeObjectSize (type, ptr1, ptr2, ptr3, value, absolute))
4103 SetChangedFlag (true);
4104 break;
4107 case F_SelectedVias:
4108 if (ChangeSelectedSize (VIA_TYPE, value, absolute))
4109 SetChangedFlag (true);
4110 break;
4112 case F_SelectedPins:
4113 if (ChangeSelectedSize (PIN_TYPE, value, absolute))
4114 SetChangedFlag (true);
4115 break;
4117 case F_SelectedPads:
4118 if (ChangeSelectedSize (PAD_TYPE, value, absolute))
4119 SetChangedFlag (true);
4120 break;
4122 case F_SelectedArcs:
4123 if (ChangeSelectedSize (ARC_TYPE, value, absolute))
4124 SetChangedFlag (true);
4125 break;
4127 case F_SelectedLines:
4128 if (ChangeSelectedSize (LINE_TYPE, value, absolute))
4129 SetChangedFlag (true);
4130 break;
4132 case F_SelectedTexts:
4133 if (ChangeSelectedSize (TEXT_TYPE, value, absolute))
4134 SetChangedFlag (true);
4135 break;
4137 case F_SelectedNames:
4138 if (ChangeSelectedSize (ELEMENTNAME_TYPE, value, absolute))
4139 SetChangedFlag (true);
4140 break;
4142 case F_SelectedElements:
4143 if (ChangeSelectedSize (ELEMENT_TYPE, value, absolute))
4144 SetChangedFlag (true);
4145 break;
4147 case F_Selected:
4148 case F_SelectedObjects:
4149 if (ChangeSelectedSize (CHANGESIZE_TYPES, value, absolute))
4150 SetChangedFlag (true);
4151 break;
4154 return 0;
4157 /* --------------------------------------------------------------------------- */
4159 static const char changedrillsize_syntax[] =
4160 N_("ChangeDrillSize(Object, delta)\n"
4161 "ChangeDrillSize(SelectedPins|SelectedVias|Selected|SelectedObjects, delta)");
4163 static const char changedrillsize_help[] =
4164 N_("Changes the drilling hole size of objects.");
4166 /* %start-doc actions ChangeDrillSize
4168 %end-doc */
4170 static int
4171 ActionChange2ndSize (int argc, char **argv, Coord x, Coord y)
4173 char *function = ARG (0);
4174 char *delta = ARG (1);
4175 char *units = ARG (2);
4176 bool absolute;
4177 Coord value;
4179 if (function && delta)
4181 value = GetValue (delta, units, &absolute);
4182 switch (GetFunctionID (function))
4184 case F_Object:
4186 int type;
4187 void *ptr1, *ptr2, *ptr3;
4189 gui->get_coords (_("Select an Object"), &x, &y);
4190 if ((type =
4191 SearchScreen (x, y, CHANGE2NDSIZE_TYPES,
4192 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4193 if (ChangeObject2ndSize
4194 (type, ptr1, ptr2, ptr3, value, absolute, true))
4195 SetChangedFlag (true);
4196 break;
4199 case F_SelectedVias:
4200 if (ChangeSelected2ndSize (VIA_TYPE, value, absolute))
4201 SetChangedFlag (true);
4202 break;
4204 case F_SelectedPins:
4205 if (ChangeSelected2ndSize (PIN_TYPE, value, absolute))
4206 SetChangedFlag (true);
4207 break;
4208 case F_Selected:
4209 case F_SelectedObjects:
4210 if (ChangeSelected2ndSize (PIN_TYPES, value, absolute))
4211 SetChangedFlag (true);
4212 break;
4215 return 0;
4218 /* --------------------------------------------------------------------------- */
4220 static const char changeclearsize_syntax[] =
4221 N_("ChangeClearSize(Object, delta)\n"
4222 "ChangeClearSize(SelectedPins|SelectedPads|SelectedVias, delta)\n"
4223 "ChangeClearSize(SelectedLines|SelectedArcs, delta\n"
4224 "ChangeClearSize(Selected|SelectedObjects, delta)");
4226 static const char changeclearsize_help[] =
4227 N_("Changes the clearance size of objects.");
4229 /* %start-doc actions ChangeClearSize
4231 If the solder mask is currently showing, this action changes the
4232 solder mask clearance. If the mask is not showing, this action
4233 changes the polygon clearance.
4235 %end-doc */
4237 static int
4238 ActionChangeClearSize (int argc, char **argv, Coord x, Coord y)
4240 char *function = ARG (0);
4241 char *delta = ARG (1);
4242 char *units = ARG (2);
4243 bool absolute;
4244 Coord value;
4246 if (function && delta)
4248 value = 2 * GetValue (delta, units, &absolute);
4249 if (value == 0)
4250 value = delta[0] == '-' ? -Settings.increments->clear
4251 : Settings.increments->clear;
4252 switch (GetFunctionID (function))
4254 case F_Object:
4256 int type;
4257 void *ptr1, *ptr2, *ptr3;
4259 gui->get_coords (_("Select an Object"), &x, &y);
4260 if ((type =
4261 SearchScreen (x, y,
4262 CHANGECLEARSIZE_TYPES, &ptr1, &ptr2,
4263 &ptr3)) != NO_TYPE)
4264 if (ChangeObjectClearSize (type, ptr1, ptr2, ptr3, value, absolute))
4265 SetChangedFlag (true);
4266 break;
4268 case F_SelectedVias:
4269 if (ChangeSelectedClearSize (VIA_TYPE, value, absolute))
4270 SetChangedFlag (true);
4271 break;
4272 case F_SelectedPads:
4273 if (ChangeSelectedClearSize (PAD_TYPE, value, absolute))
4274 SetChangedFlag (true);
4275 break;
4276 case F_SelectedPins:
4277 if (ChangeSelectedClearSize (PIN_TYPE, value, absolute))
4278 SetChangedFlag (true);
4279 break;
4280 case F_SelectedLines:
4281 if (ChangeSelectedClearSize (LINE_TYPE, value, absolute))
4282 SetChangedFlag (true);
4283 break;
4284 case F_SelectedArcs:
4285 if (ChangeSelectedClearSize (ARC_TYPE, value, absolute))
4286 SetChangedFlag (true);
4287 break;
4288 case F_Selected:
4289 case F_SelectedObjects:
4290 if (ChangeSelectedClearSize (CHANGECLEARSIZE_TYPES, value, absolute))
4291 SetChangedFlag (true);
4292 break;
4295 return 0;
4298 /* --------------------------------------------------------------------------- */
4300 static const char minmaskgap_syntax[] =
4301 N_("MinMaskGap(delta)\n"
4302 "MinMaskGap(Selected, delta)");
4304 static const char minmaskgap_help[] =
4305 N_("Ensures the mask is a minimum distance from pins and pads.");
4307 /* %start-doc actions MinMaskGap
4309 Checks all specified pins and/or pads, and increases the mask if
4310 needed to ensure a minimum distance between the pin or pad edge and
4311 the mask edge.
4313 %end-doc */
4315 static int
4316 ActionMinMaskGap (int argc, char **argv, Coord x, Coord y)
4318 char *function = ARG (0);
4319 char *delta = ARG (1);
4320 char *units = ARG (2);
4321 bool absolute;
4322 Coord value;
4323 Coord thickness;
4324 int flags;
4326 if (!function)
4327 return 1;
4328 if (strcasecmp (function, "Selected") == 0)
4329 flags = SELECTEDFLAG;
4330 else
4332 units = delta;
4333 delta = function;
4334 flags = 0;
4336 value = 2 * GetValue (delta, units, &absolute);
4338 SaveUndoSerialNumber ();
4339 ELEMENT_LOOP (PCB->Data);
4341 PIN_LOOP (element);
4343 if (!TEST_FLAGS (flags, pin) || ! pin->Mask)
4344 continue;
4346 thickness = pin->DrillingHole;
4347 if (pin->Thickness > thickness)
4348 thickness = pin->Thickness;
4349 thickness += value;
4351 if (pin->Mask < thickness)
4353 ChangeObjectMaskSize (PIN_TYPE, element, pin, 0, thickness, 1);
4354 RestoreUndoSerialNumber ();
4357 END_LOOP;
4358 PAD_LOOP (element);
4360 if (!TEST_FLAGS (flags, pad) || ! pad->Mask)
4361 continue;
4362 if (pad->Mask < pad->Thickness + value)
4364 ChangeObjectMaskSize (PAD_TYPE, element, pad, 0,
4365 pad->Thickness + value, 1);
4366 RestoreUndoSerialNumber ();
4369 END_LOOP;
4371 END_LOOP;
4372 VIA_LOOP (PCB->Data);
4374 if (!TEST_FLAGS (flags, via) || ! via->Mask)
4375 continue;
4377 thickness = via->DrillingHole;
4378 if (via->Thickness > thickness)
4379 thickness = via->Thickness;
4380 thickness += value;
4382 if (via->Mask < thickness)
4384 ChangeObjectMaskSize (VIA_TYPE, via, 0, 0, thickness, 1);
4385 RestoreUndoSerialNumber ();
4388 END_LOOP;
4389 RestoreUndoSerialNumber ();
4390 IncrementUndoSerialNumber ();
4391 return 0;
4394 /* --------------------------------------------------------------------------- */
4396 static const char mincleargap_syntax[] =
4397 N_("MinClearGap(delta)\n"
4398 "MinClearGap(Selected, delta)");
4400 static const char mincleargap_help[] =
4401 N_("Ensures that polygons are a minimum distance from objects.");
4403 /* %start-doc actions MinClearGap
4405 Checks all specified objects, and increases the polygon clearance if
4406 needed to ensure a minimum distance between their edges and the
4407 polygon edges.
4409 %end-doc */
4411 static int
4412 ActionMinClearGap (int argc, char **argv, Coord x, Coord y)
4414 char *function = ARG (0);
4415 char *delta = ARG (1);
4416 char *units = ARG (2);
4417 bool absolute;
4418 Coord value;
4419 int flags;
4421 if (!function)
4422 return 1;
4423 if (strcasecmp (function, "Selected") == 0)
4424 flags = SELECTEDFLAG;
4425 else
4427 units = delta;
4428 delta = function;
4429 flags = 0;
4431 value = 2 * GetValue (delta, units, &absolute);
4433 SaveUndoSerialNumber ();
4434 ELEMENT_LOOP (PCB->Data);
4436 PIN_LOOP (element);
4438 if (!TEST_FLAGS (flags, pin))
4439 continue;
4440 if (pin->Clearance < value)
4442 ChangeObjectClearSize (PIN_TYPE, element, pin, 0,
4443 value, 1);
4444 RestoreUndoSerialNumber ();
4447 END_LOOP;
4448 PAD_LOOP (element);
4450 if (!TEST_FLAGS (flags, pad))
4451 continue;
4452 if (pad->Clearance < value)
4454 ChangeObjectClearSize (PAD_TYPE, element, pad, 0,
4455 value, 1);
4456 RestoreUndoSerialNumber ();
4459 END_LOOP;
4461 END_LOOP;
4462 VIA_LOOP (PCB->Data);
4464 if (!TEST_FLAGS (flags, via))
4465 continue;
4466 if (via->Clearance < value)
4468 ChangeObjectClearSize (VIA_TYPE, via, 0, 0, value, 1);
4469 RestoreUndoSerialNumber ();
4472 END_LOOP;
4473 ALLLINE_LOOP (PCB->Data);
4475 if (!TEST_FLAGS (flags, line))
4476 continue;
4477 if (line->Clearance < value)
4479 ChangeObjectClearSize (LINE_TYPE, layer, line, 0, value, 1);
4480 RestoreUndoSerialNumber ();
4483 ENDALL_LOOP;
4484 ALLARC_LOOP (PCB->Data);
4486 if (!TEST_FLAGS (flags, arc))
4487 continue;
4488 if (arc->Clearance < value)
4490 ChangeObjectClearSize (ARC_TYPE, layer, arc, 0, value, 1);
4491 RestoreUndoSerialNumber ();
4494 ENDALL_LOOP;
4495 RestoreUndoSerialNumber ();
4496 IncrementUndoSerialNumber ();
4497 return 0;
4500 /* --------------------------------------------------------------------------- */
4502 static const char changepinname_syntax[] =
4503 N_("ChangePinName(ElementName,PinNumber,PinName)");
4505 static const char changepinname_help[] =
4506 N_("Sets the name of a specific pin on a specific element.");
4508 /* %start-doc actions ChangePinName
4510 This can be especially useful for annotating pin names from a
4511 schematic to the layout without requiring knowledge of the pcb file
4512 format.
4514 @example
4515 ChangePinName(U3, 7, VCC)
4516 @end example
4518 %end-doc */
4520 static int
4521 ActionChangePinName (int argc, char **argv, Coord x, Coord y)
4523 int changed = 0;
4524 char *refdes, *pinnum, *pinname;
4526 if (argc != 3)
4528 AFAIL (changepinname);
4531 refdes = argv[0];
4532 pinnum = argv[1];
4533 pinname = argv[2];
4535 ELEMENT_LOOP (PCB->Data);
4537 if (NSTRCMP (refdes, NAMEONPCB_NAME (element)) == 0)
4539 PIN_LOOP (element);
4541 if (NSTRCMP (pinnum, pin->Number) == 0)
4543 AddObjectToChangeNameUndoList (PIN_TYPE, NULL, NULL,
4544 pin, pin->Name);
4546 * Note: we can't free() pin->Name first because
4547 * it is used in the undo list
4549 pin->Name = strdup (pinname);
4550 SetChangedFlag (true);
4551 changed = 1;
4554 END_LOOP;
4556 PAD_LOOP (element);
4558 if (NSTRCMP (pinnum, pad->Number) == 0)
4560 AddObjectToChangeNameUndoList (PAD_TYPE, NULL, NULL,
4561 pad, pad->Name);
4563 * Note: we can't free() pad->Name first because
4564 * it is used in the undo list
4566 pad->Name = strdup (pinname);
4567 SetChangedFlag (true);
4568 changed = 1;
4571 END_LOOP;
4574 END_LOOP;
4576 * done with our action so increment the undo # if we actually
4577 * changed anything
4579 if (changed)
4581 if (defer_updates)
4582 defer_needs_update = 1;
4583 else
4585 IncrementUndoSerialNumber ();
4586 gui->invalidate_all ();
4590 return 0;
4593 /* --------------------------------------------------------------------------- */
4595 static const char changename_syntax[] =
4596 N_("ChangeName(Object)\n"
4597 "ChangeName(Layout|Layer)");
4599 static const char changename_help[] = N_("Sets the name of objects.");
4601 /* %start-doc actions ChangeName
4603 @table @code
4605 @item Object
4606 Changes the name of the element under the cursor.
4608 @item Layout
4609 Changes the name of the layout. This is printed on the fab drawings.
4611 @item Layer
4612 Changes the name of the currently active layer.
4614 @end table
4616 %end-doc */
4619 ActionChangeName (int argc, char **argv, Coord x, Coord y)
4621 char *function = ARG (0);
4622 char *name;
4624 if (function)
4626 switch (GetFunctionID (function))
4628 /* change the name of an object */
4629 case F_Object:
4631 int type;
4632 void *ptr1, *ptr2, *ptr3;
4634 gui->get_coords (_("Select an Object"), &x, &y);
4635 if ((type =
4636 SearchScreen (x, y, CHANGENAME_TYPES,
4637 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4639 SaveUndoSerialNumber ();
4640 if (QueryInputAndChangeObjectName (type, ptr1, ptr2, ptr3))
4642 SetChangedFlag (true);
4643 if (type == ELEMENT_TYPE)
4645 RubberbandType *ptr;
4646 int i;
4648 RestoreUndoSerialNumber ();
4649 Crosshair.AttachedObject.RubberbandN = 0;
4650 LookupRatLines (type, ptr1, ptr2, ptr3);
4651 ptr = Crosshair.AttachedObject.Rubberband;
4652 for (i = 0; i < Crosshair.AttachedObject.RubberbandN;
4653 i++, ptr++)
4655 if (PCB->RatOn)
4656 EraseRat ((RatType *) ptr->Line);
4657 MoveObjectToRemoveUndoList (RATLINE_TYPE,
4658 ptr->Line, ptr->Line,
4659 ptr->Line);
4661 IncrementUndoSerialNumber ();
4662 Draw ();
4666 break;
4669 /* change the layout's name */
4670 case F_Layout:
4671 name =
4672 gui->prompt_for (_("Enter the layout name:"), EMPTY (PCB->Name));
4673 /* NB: ChangeLayoutName takes ownership of the passed memory */
4674 if (name && ChangeLayoutName (name))
4675 SetChangedFlag (true);
4676 break;
4678 /* change the name of the active layer */
4679 case F_Layer:
4680 name = gui->prompt_for (_("Enter the layer name:"),
4681 EMPTY (CURRENT->Name));
4682 /* NB: ChangeLayerName takes ownership of the passed memory */
4683 if (name && ChangeLayerName (CURRENT, name))
4684 SetChangedFlag (true);
4685 break;
4688 return 0;
4692 /* --------------------------------------------------------------------------- */
4694 static const char morphpolygon_syntax[] = N_("MorphPolygon(Object|Selected)");
4696 static const char morphpolygon_help[] =
4697 N_("Converts dead polygon islands into separate polygons.");
4699 /* %start-doc actions MorphPolygon
4701 If a polygon is divided into unconnected "islands", you can use
4702 this command to convert the otherwise disappeared islands into
4703 separate polygons. Be sure the cursor is over a portion of the
4704 polygon that remains visible. Very small islands that may flake
4705 off are automatically deleted.
4707 %end-doc */
4709 static int
4710 ActionMorphPolygon (int argc, char **argv, Coord x, Coord y)
4712 char *function = ARG (0);
4713 if (function)
4715 switch (GetFunctionID (function))
4717 case F_Object:
4719 int type;
4720 void *ptr1, *ptr2, *ptr3;
4722 gui->get_coords (_("Select an Object"), &x, &y);
4723 if ((type = SearchScreen (x, y, POLYGON_TYPE,
4724 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4726 MorphPolygon ((LayerType *) ptr1, (PolygonType *) ptr3);
4727 Draw ();
4728 IncrementUndoSerialNumber ();
4730 break;
4732 case F_Selected:
4733 case F_SelectedObjects:
4734 ALLPOLYGON_LOOP (PCB->Data);
4736 if (TEST_FLAG (SELECTEDFLAG, polygon))
4737 MorphPolygon (layer, polygon);
4739 ENDALL_LOOP;
4740 Draw ();
4741 IncrementUndoSerialNumber ();
4742 break;
4745 return 0;
4748 /* --------------------------------------------------------------------------- */
4750 static const char togglehidename_syntax[] =
4751 N_("ToggleHideName(Object|SelectedElements)");
4753 static const char togglehidename_help[] =
4754 N_("Toggles the visibility of element names.");
4756 /* %start-doc actions ToggleHideName
4758 If names are hidden you won't see them on the screen and they will not
4759 appear on the silk layer when you print the layout.
4761 %end-doc */
4763 static int
4764 ActionToggleHideName (int argc, char **argv, Coord x, Coord y)
4766 char *function = ARG (0);
4767 if (function && PCB->ElementOn)
4769 switch (GetFunctionID (function))
4771 case F_Object:
4773 int type;
4774 void *ptr1, *ptr2, *ptr3;
4776 gui->get_coords (_("Select an Object"), &x, &y);
4777 if ((type = SearchScreen (x, y, ELEMENT_TYPE,
4778 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4780 AddObjectToFlagUndoList (type, ptr1, ptr2, ptr3);
4781 EraseElementName ((ElementType *) ptr2);
4782 TOGGLE_FLAG (HIDENAMEFLAG, (ElementType *) ptr2);
4783 DrawElementName ((ElementType *) ptr2);
4784 Draw ();
4785 IncrementUndoSerialNumber ();
4787 break;
4789 case F_SelectedElements:
4790 case F_Selected:
4792 bool changed = false;
4793 ELEMENT_LOOP (PCB->Data);
4795 if ((TEST_FLAG (SELECTEDFLAG, element) ||
4796 TEST_FLAG (SELECTEDFLAG,
4797 &NAMEONPCB_TEXT (element)))
4798 && (FRONT (element) || PCB->InvisibleObjectsOn))
4800 AddObjectToFlagUndoList (ELEMENT_TYPE, element,
4801 element, element);
4802 EraseElementName (element);
4803 TOGGLE_FLAG (HIDENAMEFLAG, element);
4804 DrawElementName (element);
4805 changed = true;
4808 END_LOOP;
4809 if (changed)
4811 Draw ();
4812 IncrementUndoSerialNumber ();
4817 return 0;
4820 /* --------------------------------------------------------------------------- */
4822 static const char changejoin_syntax[] =
4823 N_("ChangeJoin(ToggleObject|SelectedLines|SelectedArcs|Selected)");
4825 static const char changejoin_help[] =
4826 N_("Changes the join (clearance through polygons) of objects.");
4828 /* %start-doc actions ChangeJoin
4830 The join flag determines whether a line or arc, drawn to intersect a
4831 polygon, electrically connects to the polygon or not. When joined,
4832 the line/arc is simply drawn over the polygon, making an electrical
4833 connection. When not joined, a gap is drawn between the line and the
4834 polygon, insulating them from each other.
4836 %end-doc */
4838 static int
4839 ActionChangeJoin (int argc, char **argv, Coord x, Coord y)
4841 char *function = ARG (0);
4842 if (function)
4844 switch (GetFunctionID (function))
4846 case F_ToggleObject:
4847 case F_Object:
4849 int type;
4850 void *ptr1, *ptr2, *ptr3;
4852 gui->get_coords (_("Select an Object"), &x, &y);
4853 if ((type =
4854 SearchScreen (x, y, CHANGEJOIN_TYPES,
4855 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4856 if (ChangeObjectJoin (type, ptr1, ptr2, ptr3))
4857 SetChangedFlag (true);
4858 break;
4861 case F_SelectedLines:
4862 if (ChangeSelectedJoin (LINE_TYPE))
4863 SetChangedFlag (true);
4864 break;
4866 case F_SelectedArcs:
4867 if (ChangeSelectedJoin (ARC_TYPE))
4868 SetChangedFlag (true);
4869 break;
4871 case F_Selected:
4872 case F_SelectedObjects:
4873 if (ChangeSelectedJoin (CHANGEJOIN_TYPES))
4874 SetChangedFlag (true);
4875 break;
4878 return 0;
4881 /* --------------------------------------------------------------------------- */
4883 static const char changesquare_syntax[] =
4884 N_("ChangeSquare(ToggleObject)\n"
4885 "ChangeSquare(SelectedElements|SelectedPins)\n"
4886 "ChangeSquare(Selected|SelectedObjects)");
4888 static const char changesquare_help[] =
4889 N_("Changes the square flag of pins and pads.");
4891 /* %start-doc actions ChangeSquare
4893 Note that @code{Pins} means both pins and pads.
4895 @pinshapes
4897 %end-doc */
4899 static int
4900 ActionChangeSquare (int argc, char **argv, Coord x, Coord y)
4902 char *function = ARG (0);
4903 if (function)
4905 switch (GetFunctionID (function))
4907 case F_ToggleObject:
4908 case F_Object:
4910 int type;
4911 void *ptr1, *ptr2, *ptr3;
4913 gui->get_coords (_("Select an Object"), &x, &y);
4914 if ((type =
4915 SearchScreen (x, y, CHANGESQUARE_TYPES,
4916 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4917 if (ChangeObjectSquare (type, ptr1, ptr2, ptr3))
4918 SetChangedFlag (true);
4919 break;
4922 case F_SelectedElements:
4923 if (ChangeSelectedSquare (ELEMENT_TYPE))
4924 SetChangedFlag (true);
4925 break;
4927 case F_SelectedPins:
4928 if (ChangeSelectedSquare (PIN_TYPE | PAD_TYPE))
4929 SetChangedFlag (true);
4930 break;
4932 case F_Selected:
4933 case F_SelectedObjects:
4934 if (ChangeSelectedSquare (PIN_TYPE | PAD_TYPE))
4935 SetChangedFlag (true);
4936 break;
4939 return 0;
4942 /* --------------------------------------------------------------------------- */
4944 static const char setsquare_syntax[] =
4945 N_("SetSquare(ToggleObject|SelectedElements|SelectedPins)");
4947 static const char setsquare_help[] = N_("sets the square-flag of objects.");
4949 /* %start-doc actions SetSquare
4951 Note that @code{Pins} means pins and pads.
4953 @pinshapes
4955 %end-doc */
4957 static int
4958 ActionSetSquare (int argc, char **argv, Coord x, Coord y)
4960 char *function = ARG (0);
4961 if (function && *function)
4963 switch (GetFunctionID (function))
4965 case F_ToggleObject:
4966 case F_Object:
4968 int type;
4969 void *ptr1, *ptr2, *ptr3;
4971 gui->get_coords (_("Select an Object"), &x, &y);
4972 if ((type =
4973 SearchScreen (x, y, CHANGESQUARE_TYPES,
4974 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4975 if (SetObjectSquare (type, ptr1, ptr2, ptr3))
4976 SetChangedFlag (true);
4977 break;
4980 case F_SelectedElements:
4981 if (SetSelectedSquare (ELEMENT_TYPE))
4982 SetChangedFlag (true);
4983 break;
4985 case F_SelectedPins:
4986 if (SetSelectedSquare (PIN_TYPE | PAD_TYPE))
4987 SetChangedFlag (true);
4988 break;
4990 case F_Selected:
4991 case F_SelectedObjects:
4992 if (SetSelectedSquare (PIN_TYPE | PAD_TYPE))
4993 SetChangedFlag (true);
4994 break;
4997 return 0;
5000 /* --------------------------------------------------------------------------- */
5002 static const char clearsquare_syntax[] =
5003 N_("ClearSquare(ToggleObject|SelectedElements|SelectedPins)");
5005 static const char clearsquare_help[] =
5006 N_("Clears the square-flag of pins and pads.");
5008 /* %start-doc actions ClearSquare
5010 Note that @code{Pins} means pins and pads.
5012 @pinshapes
5014 %end-doc */
5016 static int
5017 ActionClearSquare (int argc, char **argv, Coord x, Coord y)
5019 char *function = ARG (0);
5020 if (function && *function)
5022 switch (GetFunctionID (function))
5024 case F_ToggleObject:
5025 case F_Object:
5027 int type;
5028 void *ptr1, *ptr2, *ptr3;
5030 gui->get_coords (_("Select an Object"), &x, &y);
5031 if ((type =
5032 SearchScreen (x, y, CHANGESQUARE_TYPES,
5033 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5034 if (ClrObjectSquare (type, ptr1, ptr2, ptr3))
5035 SetChangedFlag (true);
5036 break;
5039 case F_SelectedElements:
5040 if (ClrSelectedSquare (ELEMENT_TYPE))
5041 SetChangedFlag (true);
5042 break;
5044 case F_SelectedPins:
5045 if (ClrSelectedSquare (PIN_TYPE | PAD_TYPE))
5046 SetChangedFlag (true);
5047 break;
5049 case F_Selected:
5050 case F_SelectedObjects:
5051 if (ClrSelectedSquare (PIN_TYPE | PAD_TYPE))
5052 SetChangedFlag (true);
5053 break;
5056 return 0;
5059 /* --------------------------------------------------------------------------- */
5061 static const char changeoctagon_syntax[] =
5062 N_("ChangeOctagon(Object|ToggleObject|SelectedObjects|Selected)\n"
5063 "ChangeOctagon(SelectedElements|SelectedPins|SelectedVias)");
5065 static const char changeoctagon_help[] =
5066 N_("Changes the octagon-flag of pins and vias.");
5068 /* %start-doc actions ChangeOctagon
5070 @pinshapes
5072 %end-doc */
5074 static int
5075 ActionChangeOctagon (int argc, char **argv, Coord x, Coord y)
5077 char *function = ARG (0);
5078 if (function)
5080 switch (GetFunctionID (function))
5082 case F_ToggleObject:
5083 case F_Object:
5085 int type;
5086 void *ptr1, *ptr2, *ptr3;
5088 gui->get_coords (_("Select an Object"), &x, &y);
5089 if ((type =
5090 SearchScreen (x, y, CHANGEOCTAGON_TYPES,
5091 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5092 if (ChangeObjectOctagon (type, ptr1, ptr2, ptr3))
5093 SetChangedFlag (true);
5094 break;
5097 case F_SelectedElements:
5098 if (ChangeSelectedOctagon (ELEMENT_TYPE))
5099 SetChangedFlag (true);
5100 break;
5102 case F_SelectedPins:
5103 if (ChangeSelectedOctagon (PIN_TYPE))
5104 SetChangedFlag (true);
5105 break;
5107 case F_SelectedVias:
5108 if (ChangeSelectedOctagon (VIA_TYPE))
5109 SetChangedFlag (true);
5110 break;
5112 case F_Selected:
5113 case F_SelectedObjects:
5114 if (ChangeSelectedOctagon (PIN_TYPES))
5115 SetChangedFlag (true);
5116 break;
5119 return 0;
5122 /* --------------------------------------------------------------------------- */
5124 static const char setoctagon_syntax[] =
5125 N_("SetOctagon(Object|ToggleObject|SelectedElements|Selected)");
5127 static const char setoctagon_help[] = N_("Sets the octagon-flag of objects.");
5129 /* %start-doc actions SetOctagon
5131 @pinshapes
5133 %end-doc */
5135 static int
5136 ActionSetOctagon (int argc, char **argv, Coord x, Coord y)
5138 char *function = ARG (0);
5139 if (function)
5141 switch (GetFunctionID (function))
5143 case F_ToggleObject:
5144 case F_Object:
5146 int type;
5147 void *ptr1, *ptr2, *ptr3;
5149 gui->get_coords (_("Select an Object"), &x, &y);
5150 if ((type =
5151 SearchScreen (x, y, CHANGEOCTAGON_TYPES,
5152 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5153 if (SetObjectOctagon (type, ptr1, ptr2, ptr3))
5154 SetChangedFlag (true);
5155 break;
5158 case F_SelectedElements:
5159 if (SetSelectedOctagon (ELEMENT_TYPE))
5160 SetChangedFlag (true);
5161 break;
5163 case F_SelectedPins:
5164 if (SetSelectedOctagon (PIN_TYPE))
5165 SetChangedFlag (true);
5166 break;
5168 case F_SelectedVias:
5169 if (SetSelectedOctagon (VIA_TYPE))
5170 SetChangedFlag (true);
5171 break;
5173 case F_Selected:
5174 case F_SelectedObjects:
5175 if (SetSelectedOctagon (PIN_TYPES))
5176 SetChangedFlag (true);
5177 break;
5180 return 0;
5183 /* --------------------------------------------------------------------------- */
5185 static const char clearoctagon_syntax[] =
5186 N_("ClearOctagon(ToggleObject|Object|SelectedObjects|Selected)\n"
5187 "ClearOctagon(SelectedElements|SelectedPins|SelectedVias)");
5189 static const char clearoctagon_help[] =
5190 N_("Clears the octagon-flag of pins and vias.");
5192 /* %start-doc actions ClearOctagon
5194 @pinshapes
5196 %end-doc */
5198 static int
5199 ActionClearOctagon (int argc, char **argv, Coord x, Coord y)
5201 char *function = ARG (0);
5202 if (function)
5204 switch (GetFunctionID (function))
5206 case F_ToggleObject:
5207 case F_Object:
5209 int type;
5210 void *ptr1, *ptr2, *ptr3;
5212 gui->get_coords (_("Select an Object"), &x, &y);
5213 if ((type =
5214 SearchScreen (Crosshair.X, Crosshair.Y, CHANGEOCTAGON_TYPES,
5215 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5216 if (ClrObjectOctagon (type, ptr1, ptr2, ptr3))
5217 SetChangedFlag (true);
5218 break;
5221 case F_SelectedElements:
5222 if (ClrSelectedOctagon (ELEMENT_TYPE))
5223 SetChangedFlag (true);
5224 break;
5226 case F_SelectedPins:
5227 if (ClrSelectedOctagon (PIN_TYPE))
5228 SetChangedFlag (true);
5229 break;
5231 case F_SelectedVias:
5232 if (ClrSelectedOctagon (VIA_TYPE))
5233 SetChangedFlag (true);
5234 break;
5236 case F_Selected:
5237 case F_SelectedObjects:
5238 if (ClrSelectedOctagon (PIN_TYPES))
5239 SetChangedFlag (true);
5240 break;
5243 return 0;
5246 /* --------------------------------------------------------------------------- */
5248 static const char changehold_syntax[] =
5249 N_("ChangeHole(ToggleObject|Object|SelectedVias|Selected)");
5251 static const char changehold_help[] = N_("Changes the hole flag of objects.");
5253 /* %start-doc actions ChangeHole
5255 The "hole flag" of a via determines whether the via is a
5256 plated-through hole (not set), or an unplated hole (set).
5258 %end-doc */
5260 static int
5261 ActionChangeHole (int argc, char **argv, Coord x, Coord y)
5263 char *function = ARG (0);
5264 if (function)
5266 switch (GetFunctionID (function))
5268 case F_ToggleObject:
5269 case F_Object:
5271 int type;
5272 void *ptr1, *ptr2, *ptr3;
5274 gui->get_coords (_("Select an Object"), &x, &y);
5275 if ((type = SearchScreen (x, y, VIA_TYPE,
5276 &ptr1, &ptr2, &ptr3)) != NO_TYPE
5277 && ChangeHole ((PinType *) ptr3))
5278 IncrementUndoSerialNumber ();
5279 break;
5282 case F_SelectedVias:
5283 case F_Selected:
5284 if (ChangeSelectedHole ())
5285 SetChangedFlag (true);
5286 break;
5289 return 0;
5292 /* --------------------------------------------------------------------------- */
5294 static const char changepaste_syntax[] =
5295 N_("ChangePaste(ToggleObject|Object|SelectedPads|Selected)");
5297 static const char changepaste_help[] =
5298 N_("Changes the no paste flag of objects.");
5300 /* %start-doc actions ChangePaste
5302 The "no paste flag" of a pad determines whether the solderpaste
5303 stencil will have an opening for the pad (no set) or if there wil be
5304 no solderpaste on the pad (set). This is used for things such as
5305 fiducial pads.
5307 %end-doc */
5309 static int
5310 ActionChangePaste (int argc, char **argv, Coord x, Coord y)
5312 char *function = ARG (0);
5313 if (function)
5315 switch (GetFunctionID (function))
5317 case F_ToggleObject:
5318 case F_Object:
5320 int type;
5321 void *ptr1, *ptr2, *ptr3;
5323 gui->get_coords (_("Select an Object"), &x, &y);
5324 if ((type = SearchScreen (x, y, PAD_TYPE,
5325 &ptr1, &ptr2, &ptr3)) != NO_TYPE
5326 && ChangePaste ((PadType *) ptr3))
5327 IncrementUndoSerialNumber ();
5328 break;
5331 case F_SelectedPads:
5332 case F_Selected:
5333 if (ChangeSelectedPaste ())
5334 SetChangedFlag (true);
5335 break;
5338 return 0;
5341 /* --------------------------------------------------------------------------- */
5343 static const char select_syntax[] =
5344 N_("Select(Object|ToggleObject)\n"
5345 "Select(All|Block|Connection)\n"
5346 "Select(ElementByName|ObjectByName|PadByName|PinByName)\n"
5347 "Select(ElementByName|ObjectByName|PadByName|PinByName, Name)\n"
5348 "Select(TextByName|ViaByName|NetByName)\n"
5349 "Select(TextByName|ViaByName|NetByName, Name)\n"
5350 "Select(Convert)");
5352 static const char select_help[] = N_("Toggles or sets the selection.");
5354 /* %start-doc actions Select
5356 @table @code
5358 @item ElementByName
5359 @item ObjectByName
5360 @item PadByName
5361 @item PinByName
5362 @item TextByName
5363 @item ViaByName
5364 @item NetByName
5366 These all rely on having a regular expression parser built into
5367 @code{pcb}. If the name is not specified then the user is prompted
5368 for a pattern, and all objects that match the pattern and are of the
5369 type specified are selected.
5371 @item Object
5372 @item ToggleObject
5373 Selects the object under the cursor.
5375 @item Block
5376 Selects all objects in a rectangle indicated by the cursor.
5378 @item All
5379 Selects all objects on the board.
5381 @item Found
5382 Selects all connections with the ``found'' flag set.
5384 @item Connection
5385 Selects all connections with the ``connected'' flag set.
5387 @item Convert
5388 Converts the selected objects to an element. This uses the highest
5389 numbered paste buffer.
5391 @end table
5393 %end-doc */
5395 static int
5396 ActionSelect (int argc, char **argv, Coord x, Coord y)
5398 char *function = ARG (0);
5399 if (function)
5401 switch (GetFunctionID (function))
5403 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5404 int type;
5405 /* select objects by their names */
5406 case F_ElementByName:
5407 type = ELEMENT_TYPE;
5408 goto commonByName;
5409 case F_ObjectByName:
5410 type = ALL_TYPES;
5411 goto commonByName;
5412 case F_PadByName:
5413 type = PAD_TYPE;
5414 goto commonByName;
5415 case F_PinByName:
5416 type = PIN_TYPE;
5417 goto commonByName;
5418 case F_TextByName:
5419 type = TEXT_TYPE;
5420 goto commonByName;
5421 case F_ViaByName:
5422 type = VIA_TYPE;
5423 goto commonByName;
5424 case F_NetByName:
5425 type = NET_TYPE;
5426 goto commonByName;
5428 commonByName:
5430 char *pattern = ARG (1);
5432 if (pattern
5433 || (pattern =
5434 gui->prompt_for (_("Enter pattern:"), "")) != NULL)
5436 if (SelectObjectByName (type, pattern, true))
5437 SetChangedFlag (true);
5438 if (ARG (1) == NULL)
5439 free (pattern);
5441 break;
5443 #endif /* defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP) */
5445 /* select a single object */
5446 case F_ToggleObject:
5447 case F_Object:
5448 if (SelectObject ())
5449 SetChangedFlag (true);
5450 break;
5452 /* all objects in block */
5453 case F_Block:
5455 BoxType box;
5457 box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
5458 Crosshair.AttachedBox.Point2.X);
5459 box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
5460 Crosshair.AttachedBox.Point2.Y);
5461 box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
5462 Crosshair.AttachedBox.Point2.X);
5463 box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
5464 Crosshair.AttachedBox.Point2.Y);
5465 notify_crosshair_change (false);
5466 NotifyBlock ();
5467 if (Crosshair.AttachedBox.State == STATE_THIRD &&
5468 SelectBlock (&box, true))
5470 SetChangedFlag (true);
5471 Crosshair.AttachedBox.State = STATE_FIRST;
5473 notify_crosshair_change (true);
5474 break;
5477 /* select all visible objects */
5478 case F_All:
5480 BoxType box;
5482 box.X1 = -MAX_COORD;
5483 box.Y1 = -MAX_COORD;
5484 box.X2 = MAX_COORD;
5485 box.Y2 = MAX_COORD;
5486 if (SelectBlock (&box, true))
5487 SetChangedFlag (true);
5488 break;
5491 /* all logical connections */
5492 case F_Found:
5493 if (SelectByFlag (FOUNDFLAG, true))
5495 Draw ();
5496 IncrementUndoSerialNumber ();
5497 SetChangedFlag (true);
5499 break;
5501 /* all physical connections */
5502 case F_Connection:
5503 if (SelectByFlag (CONNECTEDFLAG, true))
5505 Draw ();
5506 IncrementUndoSerialNumber ();
5507 SetChangedFlag (true);
5509 break;
5511 case F_Convert:
5513 Coord x, y;
5514 Note.Buffer = Settings.BufferNumber;
5515 SetBufferNumber (MAX_BUFFER - 1);
5516 ClearBuffer (PASTEBUFFER);
5517 gui->get_coords (_("Select the Element's Mark Location"), &x, &y);
5518 x = GridFit (x, PCB->Grid, PCB->GridOffsetX);
5519 y = GridFit (y, PCB->Grid, PCB->GridOffsetY);
5520 AddSelectedToBuffer (PASTEBUFFER, x, y, true);
5521 SaveUndoSerialNumber ();
5522 RemoveSelected ();
5523 ConvertBufferToElement (PASTEBUFFER);
5524 RestoreUndoSerialNumber ();
5525 CopyPastebufferToLayout (x, y);
5526 SetBufferNumber (Note.Buffer);
5528 break;
5530 default:
5531 AFAIL (select);
5532 break;
5535 return 0;
5538 /* FLAG(have_regex,FlagHaveRegex,0) */
5540 FlagHaveRegex (int parm)
5542 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5543 return 1;
5544 #else
5545 return 0;
5546 #endif
5549 /* --------------------------------------------------------------------------- */
5551 static const char unselect_syntax[] =
5552 N_("Unselect(All|Block|Connection)\n"
5553 "Unselect(ElementByName|ObjectByName|PadByName|PinByName)\n"
5554 "Unselect(ElementByName|ObjectByName|PadByName|PinByName, Name)\n"
5555 "Unselect(TextByName|ViaByName)\n"
5556 "Unselect(TextByName|ViaByName, Name)\n");
5558 static const char unselect_help[] =
5559 N_("Unselects the object at the pointer location or the specified objects.");
5561 /* %start-doc actions Unselect
5563 @table @code
5565 @item All
5566 Unselect all objects.
5568 @item Block
5569 Unselect all objects in a rectangle given by the cursor.
5571 @item Connection
5572 Unselect all connections with the ``found'' flag set.
5574 @item ElementByName
5575 @item ObjectByName
5576 @item PadByName
5577 @item PinByName
5578 @item TextByName
5579 @item ViaByName
5581 These all rely on having a regular expression parser built into
5582 @code{pcb}. If the name is not specified then the user is prompted
5583 for a pattern, and all objects that match the pattern and are of the
5584 type specified are unselected.
5587 @end table
5589 %end-doc */
5591 static int
5592 ActionUnselect (int argc, char **argv, Coord x, Coord y)
5594 char *function = ARG (0);
5595 if (function)
5597 switch (GetFunctionID (function))
5599 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5600 int type;
5601 /* select objects by their names */
5602 case F_ElementByName:
5603 type = ELEMENT_TYPE;
5604 goto commonByName;
5605 case F_ObjectByName:
5606 type = ALL_TYPES;
5607 goto commonByName;
5608 case F_PadByName:
5609 type = PAD_TYPE;
5610 goto commonByName;
5611 case F_PinByName:
5612 type = PIN_TYPE;
5613 goto commonByName;
5614 case F_TextByName:
5615 type = TEXT_TYPE;
5616 goto commonByName;
5617 case F_ViaByName:
5618 type = VIA_TYPE;
5619 goto commonByName;
5620 case F_NetByName:
5621 type = NET_TYPE;
5622 goto commonByName;
5624 commonByName:
5626 char *pattern = ARG (1);
5628 if (pattern
5629 || (pattern =
5630 gui->prompt_for (_("Enter pattern:"), "")) != NULL)
5632 if (SelectObjectByName (type, pattern, false))
5633 SetChangedFlag (true);
5634 if (ARG (1) == NULL)
5635 free (pattern);
5637 break;
5639 #endif /* defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP) */
5641 /* all objects in block */
5642 case F_Block:
5644 BoxType box;
5646 box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
5647 Crosshair.AttachedBox.Point2.X);
5648 box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
5649 Crosshair.AttachedBox.Point2.Y);
5650 box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
5651 Crosshair.AttachedBox.Point2.X);
5652 box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
5653 Crosshair.AttachedBox.Point2.Y);
5654 notify_crosshair_change (false);
5655 NotifyBlock ();
5656 if (Crosshair.AttachedBox.State == STATE_THIRD &&
5657 SelectBlock (&box, false))
5659 SetChangedFlag (true);
5660 Crosshair.AttachedBox.State = STATE_FIRST;
5662 notify_crosshair_change (true);
5663 break;
5666 /* unselect all visible objects */
5667 case F_All:
5669 BoxType box;
5671 box.X1 = -MAX_COORD;
5672 box.Y1 = -MAX_COORD;
5673 box.X2 = MAX_COORD;
5674 box.Y2 = MAX_COORD;
5675 if (SelectBlock (&box, false))
5676 SetChangedFlag (true);
5677 break;
5680 /* all logical connections */
5681 case F_Found:
5682 if (SelectByFlag (FOUNDFLAG, false))
5684 Draw ();
5685 IncrementUndoSerialNumber ();
5686 SetChangedFlag (true);
5688 break;
5690 /* all physical connections */
5691 case F_Connection:
5692 if (SelectByFlag (CONNECTEDFLAG, false))
5694 Draw ();
5695 IncrementUndoSerialNumber ();
5696 SetChangedFlag (true);
5698 break;
5700 default:
5701 AFAIL (unselect);
5702 break;
5706 return 0;
5709 /* --------------------------------------------------------------------------- */
5711 static const char saveto_syntax[] =
5712 N_("SaveTo(Layout|LayoutAs,filename)\n"
5713 "SaveTo(AllConnections|AllUnusedPins|ElementConnections,filename)\n"
5714 "SaveTo(PasteBuffer,filename)");
5716 static const char saveto_help[] = N_("Saves data to a file.");
5718 /* %start-doc actions SaveTo
5720 @table @code
5722 @item Layout
5723 Saves the current layout.
5725 @item LayoutAs
5726 Saves the current layout, and remembers the filename used.
5728 @item AllConnections
5729 Save all connections to a file.
5731 @item AllUnusedPins
5732 List all unused pins to a file.
5734 @item ElementConnections
5735 Save connections to the element at the cursor to a file.
5737 @item PasteBuffer
5738 Save the content of the active Buffer to a file. This is the graphical way to create a footprint.
5740 @end table
5742 %end-doc */
5744 static int
5745 ActionSaveTo (int argc, char **argv, Coord x, Coord y)
5747 char *function;
5748 char *name;
5750 function = ARG (0);
5752 if ( ! function || strcasecmp (function, "Layout") == 0)
5754 if (SavePCB (PCB->Filename) == 0)
5755 SetChangedFlag (false);
5756 return 0;
5759 if (argc != 2)
5760 AFAIL (saveto);
5762 name = argv[1];
5764 if (strcasecmp (function, "LayoutAs") == 0)
5766 if (SavePCB (name) == 0)
5768 SetChangedFlag (false);
5769 free (PCB->Filename);
5770 PCB->Filename = strdup (name);
5771 if (gui->notify_filename_changed != NULL)
5772 gui->notify_filename_changed ();
5774 return 0;
5777 if (strcasecmp (function, "AllConnections") == 0)
5779 FILE *fp;
5780 bool result;
5781 if ((fp = CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
5783 LookupConnectionsToAllElements (fp);
5784 fclose (fp);
5785 SetChangedFlag (true);
5787 return 0;
5790 if (strcasecmp (function, "AllUnusedPins") == 0)
5792 FILE *fp;
5793 bool result;
5794 if ((fp = CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
5796 LookupUnusedPins (fp);
5797 fclose (fp);
5798 SetChangedFlag (true);
5800 return 0;
5803 if (strcasecmp (function, "ElementConnections") == 0)
5805 ElementType *element;
5806 void *ptrtmp;
5807 FILE *fp;
5808 bool result;
5810 if ((SearchScreen (Crosshair.X, Crosshair.Y, ELEMENT_TYPE,
5811 &ptrtmp, &ptrtmp, &ptrtmp)) != NO_TYPE)
5813 element = (ElementType *) ptrtmp;
5814 if ((fp =
5815 CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
5817 LookupElementConnections (element, fp);
5818 fclose (fp);
5819 SetChangedFlag (true);
5822 return 0;
5825 if (strcasecmp (function, "PasteBuffer") == 0)
5827 return SaveBufferElements (name);
5830 AFAIL (saveto);
5833 /* --------------------------------------------------------------------------- */
5835 static const char savesettings_syntax[] =
5836 N_("SaveSettings()\n"
5837 "SaveSettings(local)");
5839 static const char savesettings_help[] = N_("Saves settings.");
5841 /* %start-doc actions SaveSettings
5843 If you pass no arguments, the settings are stored in
5844 @code{$HOME/.pcb/settings}. If you pass the word @code{local} they're
5845 saved in @code{./pcb.settings}.
5847 %end-doc */
5849 static int
5850 ActionSaveSettings (int argc, char **argv, Coord x, Coord y)
5852 int locally = argc > 0 ? (strncasecmp (argv[0], "local", 5) == 0) : 0;
5853 hid_save_settings (locally);
5854 return 0;
5857 /* --------------------------------------------------------------------------- */
5859 static const char loadfrom_syntax[] =
5860 N_("LoadFrom(Layout|LayoutToBuffer|ElementToBuffer|Netlist|Revert,filename)");
5862 static const char loadfrom_help[] = N_("Load layout data from a file.");
5864 /* %start-doc actions LoadFrom
5866 This action assumes you know what the filename is. The various GUIs
5867 should have a similar @code{Load} action where the filename is
5868 optional, and will provide their own file selection mechanism to let
5869 you choose the file name.
5871 @table @code
5873 @item Layout
5874 Loads an entire PCB layout, replacing the current one.
5876 @item LayoutToBuffer
5877 Loads an entire PCB layout to the paste buffer.
5879 @item ElementToBuffer
5880 Loads the given element file into the paste buffer. Element files
5881 contain only a single @code{Element} definition, such as the
5882 ``newlib'' library uses.
5884 @item Netlist
5885 Loads a new netlist, replacing any current netlist.
5887 @item Revert
5888 Re-loads the current layout from its disk file, reverting any changes
5889 you may have made.
5891 @end table
5893 %end-doc */
5895 static int
5896 ActionLoadFrom (int argc, char **argv, Coord x, Coord y)
5898 char *function;
5899 char *name;
5901 if (argc < 2)
5902 AFAIL (loadfrom);
5904 function = argv[0];
5905 name = argv[1];
5907 if (strcasecmp (function, "ElementToBuffer") == 0)
5909 notify_crosshair_change (false);
5910 if (LoadElementToBuffer (PASTEBUFFER, name, true))
5911 SetMode (PASTEBUFFER_MODE);
5912 notify_crosshair_change (true);
5915 else if (strcasecmp (function, "LayoutToBuffer") == 0)
5917 notify_crosshair_change (false);
5918 if (LoadLayoutToBuffer (PASTEBUFFER, name))
5919 SetMode (PASTEBUFFER_MODE);
5920 notify_crosshair_change (true);
5923 else if (strcasecmp (function, "Layout") == 0)
5925 if (!PCB->Changed ||
5926 gui->confirm_dialog (_("OK to override layout data?"), 0))
5927 LoadPCB (name);
5930 else if (strcasecmp (function, "Netlist") == 0)
5932 if (PCB->Netlistname)
5933 free (PCB->Netlistname);
5934 PCB->Netlistname = StripWhiteSpaceAndDup (name);
5935 FreeLibraryMemory (&PCB->NetlistLib);
5936 ImportNetlist (PCB->Netlistname);
5937 NetlistChanged (1);
5939 else if (strcasecmp (function, "Revert") == 0 && PCB->Filename
5940 && (!PCB->Changed
5941 || gui->confirm_dialog (_("OK to override changes?"), 0)))
5943 RevertPCB ();
5946 return 0;
5949 /* --------------------------------------------------------------------------- */
5951 static const char new_syntax[] = N_("New([name])");
5953 static const char new_help[] = N_("Starts a new layout.");
5955 /* %start-doc actions New
5957 If a name is not given, one is prompted for.
5959 %end-doc */
5961 static int
5962 ActionNew (int argc, char **argv, Coord x, Coord y)
5964 char *name = ARG (0);
5966 if (!PCB->Changed || gui->confirm_dialog (_("OK to clear layout data?"), 0))
5968 if (name)
5969 name = strdup (name);
5970 else
5971 name = gui->prompt_for (_("Enter the layout name:"), "");
5973 if (!name)
5974 return 1;
5976 notify_crosshair_change (false);
5977 /* do emergency saving
5978 * clear the old struct and allocate memory for the new one
5980 if (PCB->Changed && Settings.SaveInTMP)
5981 SaveInTMP ();
5982 RemovePCB (PCB);
5983 PCB = NULL;
5984 PCB = CreateNewPCB ();
5985 CreateNewPCBPost (PCB, 1);
5987 /* setup the new name and reset some values to default */
5988 free (PCB->Name);
5989 PCB->Name = name;
5991 ResetStackAndVisibility ();
5992 SetCrosshairRange (0, 0, PCB->MaxWidth, PCB->MaxHeight);
5993 CenterDisplay (PCB->MaxWidth / 2, PCB->MaxHeight / 2, false);
5994 Redraw ();
5996 hid_action ("PCBChanged");
5997 notify_crosshair_change (true);
5998 return 0;
6000 return 1;
6004 * \brief No operation, just for testing purposes.
6005 * syntax: Bell(volume)
6007 void
6008 ActionBell (char *volume)
6010 gui->beep ();
6013 /* --------------------------------------------------------------------------- */
6015 static const char pastebuffer_syntax[] =
6016 N_("PasteBuffer(AddSelected|Clear|1..MAX_BUFFER)\n"
6017 "PasteBuffer(Rotate, 1..3)\n"
6018 "PasteBuffer(Convert|Save|Restore|Mirror)\n"
6019 "PasteBuffer(ToLayout, X, Y, units)");
6021 static const char pastebuffer_help[] =
6022 N_("Various operations on the paste buffer.");
6024 /* %start-doc actions PasteBuffer
6026 There are a number of paste buffers; the actual limit is a
6027 compile-time constant @code{MAX_BUFFER} in @file{globalconst.h}. It
6028 is currently @code{5}. One of these is the ``current'' paste buffer,
6029 often referred to as ``the'' paste buffer.
6031 @table @code
6033 @item AddSelected
6034 Copies the selected objects to the current paste buffer.
6036 @item Clear
6037 Remove all objects from the current paste buffer.
6039 @item Convert
6040 Convert the current paste buffer to an element. Vias are converted to
6041 pins, lines are converted to pads.
6043 @item Restore
6044 Convert any elements in the paste buffer back to vias and lines.
6046 @item Mirror
6047 Flip all objects in the paste buffer vertically (up/down flip). To mirror
6048 horizontally, combine this with rotations.
6050 @item Rotate
6051 Rotates the current buffer. The number to pass is 1..3, where 1 means
6052 90 degrees counter clockwise, 2 means 180 degrees, and 3 means 90
6053 degrees clockwise (270 CCW).
6055 @item Save
6056 Saves any elements in the current buffer to the indicated file.
6058 @item ToLayout
6059 Pastes any elements in the current buffer to the indicated X, Y
6060 coordinates in the layout. The @code{X} and @code{Y} are treated like
6061 @code{delta} is for many other objects. For each, if it's prefixed by
6062 @code{+} or @code{-}, then that amount is relative to the last
6063 location. Otherwise, it's absolute. Units can be
6064 @code{mil} or @code{mm}; if unspecified, units are PCB's internal
6065 units, currently 1/100 mil.
6068 @item 1..MAX_BUFFER
6069 Selects the given buffer to be the current paste buffer.
6071 @end table
6073 %end-doc */
6075 static int
6076 ActionPasteBuffer (int argc, char **argv, Coord x, Coord y)
6078 char *function = argc ? argv[0] : (char *)"";
6079 char *sbufnum = argc > 1 ? argv[1] : (char *)"";
6080 char *name;
6081 static char *default_file = NULL;
6082 int free_name = 0;
6084 notify_crosshair_change (false);
6085 if (function)
6087 switch (GetFunctionID (function))
6089 /* clear contents of paste buffer */
6090 case F_Clear:
6091 ClearBuffer (PASTEBUFFER);
6092 break;
6094 /* copies objects to paste buffer */
6095 case F_AddSelected:
6096 AddSelectedToBuffer (PASTEBUFFER, 0, 0, false);
6097 break;
6099 /* converts buffer contents into an element */
6100 case F_Convert:
6101 ConvertBufferToElement (PASTEBUFFER);
6102 break;
6104 /* break up element for editing */
6105 case F_Restore:
6106 SmashBufferElement (PASTEBUFFER);
6107 break;
6109 /* Mirror buffer */
6110 case F_Mirror:
6111 MirrorBuffer (PASTEBUFFER);
6112 break;
6114 case F_Rotate:
6115 if (sbufnum)
6117 RotateBuffer (PASTEBUFFER, (BYTE) atoi (sbufnum));
6118 SetCrosshairRangeToBuffer ();
6120 break;
6122 case F_Save:
6123 if (PASTEBUFFER->Data->ElementN == 0)
6125 Message (_("Buffer has no elements!\n"));
6126 break;
6128 free_name = 0;
6129 if (argc <= 1)
6131 name = gui->fileselect (_("Save Paste Buffer As ..."),
6132 _("Choose a file to save the contents of the\n"
6133 "paste buffer to.\n"),
6134 default_file, ".fp", "footprint",
6137 if (default_file)
6139 free (default_file);
6140 default_file = NULL;
6142 if ( name && *name)
6144 default_file = strdup (name);
6146 free_name = 1;
6149 else
6150 name = argv[1];
6153 FILE *exist;
6155 if ((exist = fopen (name, "r")))
6157 fclose (exist);
6158 if (gui->
6159 confirm_dialog (_("File exists! Ok to overwrite?"), 0))
6160 SaveBufferElements (name);
6162 else
6163 SaveBufferElements (name);
6165 if (free_name && name)
6166 free (name);
6168 break;
6170 case F_ToLayout:
6172 static Coord oldx = 0, oldy = 0;
6173 Coord x, y;
6174 bool absolute;
6176 if (argc == 1)
6178 x = y = 0;
6180 else if (argc == 3 || argc == 4)
6182 x = GetValue (ARG (1), ARG (3), &absolute);
6183 if (!absolute)
6184 x += oldx;
6185 y = GetValue (ARG (2), ARG (3), &absolute);
6186 if (!absolute)
6187 y += oldy;
6189 else
6191 notify_crosshair_change (true);
6192 AFAIL (pastebuffer);
6195 oldx = x;
6196 oldy = y;
6197 if (CopyPastebufferToLayout (x, y))
6198 SetChangedFlag (true);
6200 break;
6202 /* set number */
6203 default:
6205 int number = atoi (function);
6207 /* correct number */
6208 if (number)
6209 SetBufferNumber (number - 1);
6214 notify_crosshair_change (true);
6215 return 0;
6218 /* --------------------------------------------------------------------------- */
6220 static const char undo_syntax[] = N_("Undo()\n"
6221 "Undo(ClearList)");
6223 static const char undo_help[] = N_("Undo recent changes.");
6225 /* %start-doc actions Undo
6227 The unlimited undo feature of @code{Pcb} allows you to recover from
6228 most operations that materially affect you work. Calling
6229 @code{Undo()} without any parameter recovers from the last (non-undo)
6230 operation. @code{ClearList} is used to release the allocated
6231 memory. @code{ClearList} is called whenever a new layout is started or
6232 loaded. See also @code{Redo} and @code{Atomic}.
6234 Note that undo groups operations by serial number; changes with the
6235 same serial number will be undone (or redone) as a group. See
6236 @code{Atomic}.
6238 %end-doc */
6240 static int
6241 ActionUndo (int argc, char **argv, Coord x, Coord y)
6243 char *function = ARG (0);
6244 if (!function || !*function)
6246 /* don't allow undo in the middle of an operation */
6247 if (Settings.Mode != POLYGONHOLE_MODE &&
6248 Crosshair.AttachedObject.State != STATE_FIRST)
6249 return 1;
6250 if (Crosshair.AttachedBox.State != STATE_FIRST
6251 && Settings.Mode != ARC_MODE)
6252 return 1;
6253 /* undo the last operation */
6255 notify_crosshair_change (false);
6256 if ((Settings.Mode == POLYGON_MODE ||
6257 Settings.Mode == POLYGONHOLE_MODE) &&
6258 Crosshair.AttachedPolygon.PointN)
6260 GoToPreviousPoint ();
6261 notify_crosshair_change (true);
6262 return 0;
6264 /* move anchor point if undoing during line creation */
6265 if (Settings.Mode == LINE_MODE)
6267 if (Crosshair.AttachedLine.State == STATE_SECOND)
6269 if (TEST_FLAG (AUTODRCFLAG, PCB))
6270 Undo (true); /* undo the connection find */
6271 Crosshair.AttachedLine.State = STATE_FIRST;
6272 SetLocalRef (0, 0, false);
6273 notify_crosshair_change (true);
6274 return 0;
6276 if (Crosshair.AttachedLine.State == STATE_THIRD)
6278 int type;
6279 void *ptr1, *ptr3, *ptrtmp;
6280 LineType *ptr2;
6281 /* this search is guaranteed to succeed */
6282 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6283 &ptrtmp, &ptr3,
6284 Crosshair.AttachedLine.Point1.X,
6285 Crosshair.AttachedLine.Point1.Y, 0);
6286 ptr2 = (LineType *) ptrtmp;
6288 /* save both ends of line */
6289 Crosshair.AttachedLine.Point2.X = ptr2->Point1.X;
6290 Crosshair.AttachedLine.Point2.Y = ptr2->Point1.Y;
6291 if ((type = Undo (true)))
6292 SetChangedFlag (true);
6293 /* check that the undo was of the right type */
6294 if ((type & UNDO_CREATE) == 0)
6296 /* wrong undo type, restore anchor points */
6297 Crosshair.AttachedLine.Point2.X =
6298 Crosshair.AttachedLine.Point1.X;
6299 Crosshair.AttachedLine.Point2.Y =
6300 Crosshair.AttachedLine.Point1.Y;
6301 notify_crosshair_change (true);
6302 return 0;
6304 /* move to new anchor */
6305 Crosshair.AttachedLine.Point1.X =
6306 Crosshair.AttachedLine.Point2.X;
6307 Crosshair.AttachedLine.Point1.Y =
6308 Crosshair.AttachedLine.Point2.Y;
6309 /* check if an intermediate point was removed */
6310 if (type & UNDO_REMOVE)
6312 /* this search should find the restored line */
6313 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6314 &ptrtmp,
6315 &ptr3,
6316 Crosshair.AttachedLine.Point2.X,
6317 Crosshair.AttachedLine.Point2.Y, 0);
6318 ptr2 = (LineType *) ptrtmp;
6319 if (TEST_FLAG (AUTODRCFLAG, PCB))
6321 /* undo loses CONNECTEDFLAG and FOUNDFLAG */
6322 SET_FLAG(CONNECTEDFLAG, ptr2);
6323 SET_FLAG(FOUNDFLAG, ptr2);
6324 DrawLine (CURRENT, ptr2);
6326 Crosshair.AttachedLine.Point1.X =
6327 Crosshair.AttachedLine.Point2.X = ptr2->Point2.X;
6328 Crosshair.AttachedLine.Point1.Y =
6329 Crosshair.AttachedLine.Point2.Y = ptr2->Point2.Y;
6331 FitCrosshairIntoGrid (Crosshair.X, Crosshair.Y);
6332 AdjustAttachedObjects ();
6333 if (--addedLines == 0)
6335 Crosshair.AttachedLine.State = STATE_SECOND;
6336 lastLayer = CURRENT;
6338 else
6340 /* this search is guaranteed to succeed too */
6341 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6342 &ptrtmp,
6343 &ptr3,
6344 Crosshair.AttachedLine.Point1.X,
6345 Crosshair.AttachedLine.Point1.Y, 0);
6346 ptr2 = (LineType *) ptrtmp;
6347 lastLayer = (LayerType *) ptr1;
6349 notify_crosshair_change (true);
6350 return 0;
6353 if (Settings.Mode == ARC_MODE)
6355 if (Crosshair.AttachedBox.State == STATE_SECOND)
6357 Crosshair.AttachedBox.State = STATE_FIRST;
6358 notify_crosshair_change (true);
6359 return 0;
6361 if (Crosshair.AttachedBox.State == STATE_THIRD)
6363 void *ptr1, *ptr2, *ptr3;
6364 BoxType *bx;
6365 /* guaranteed to succeed */
6366 SearchObjectByLocation (ARC_TYPE, &ptr1, &ptr2, &ptr3,
6367 Crosshair.AttachedBox.Point1.X,
6368 Crosshair.AttachedBox.Point1.Y, 0);
6369 bx = GetArcEnds ((ArcType *) ptr2);
6370 Crosshair.AttachedBox.Point1.X =
6371 Crosshair.AttachedBox.Point2.X = bx->X1;
6372 Crosshair.AttachedBox.Point1.Y =
6373 Crosshair.AttachedBox.Point2.Y = bx->Y1;
6374 AdjustAttachedObjects ();
6375 if (--addedLines == 0)
6376 Crosshair.AttachedBox.State = STATE_SECOND;
6379 /* undo the last destructive operation */
6380 if (Undo (true))
6381 SetChangedFlag (true);
6383 else if (function)
6385 switch (GetFunctionID (function))
6387 /* clear 'undo objects' list */
6388 case F_ClearList:
6389 ClearUndoList (false);
6390 break;
6393 notify_crosshair_change (true);
6394 return 0;
6397 /* --------------------------------------------------------------------------- */
6399 static const char redo_syntax[] = N_("Redo()");
6401 static const char redo_help[] = N_("Redo recent \"undo\" operations.");
6403 /* %start-doc actions Redo
6405 This routine allows you to recover from the last undo command. You
6406 might want to do this if you thought that undo was going to revert
6407 something other than what it actually did (in case you are confused
6408 about which operations are un-doable), or if you have been backing up
6409 through a long undo list and over-shoot your stopping point. Any
6410 change that is made since the undo in question will trim the redo
6411 list. For example if you add ten lines, then undo three of them you
6412 could use redo to put them back, but if you move a line on the board
6413 before performing the redo, you will lose the ability to "redo" the
6414 three "undone" lines.
6416 %end-doc */
6418 static int
6419 ActionRedo (int argc, char **argv, Coord x, Coord y)
6421 if (((Settings.Mode == POLYGON_MODE ||
6422 Settings.Mode == POLYGONHOLE_MODE) &&
6423 Crosshair.AttachedPolygon.PointN) ||
6424 Crosshair.AttachedLine.State == STATE_SECOND)
6425 return 1;
6426 notify_crosshair_change (false);
6427 if (Redo (true))
6429 SetChangedFlag (true);
6430 if (Settings.Mode == LINE_MODE &&
6431 Crosshair.AttachedLine.State != STATE_FIRST)
6433 LineType *line = g_list_last (CURRENT->Line)->data;
6434 Crosshair.AttachedLine.Point1.X =
6435 Crosshair.AttachedLine.Point2.X = line->Point2.X;
6436 Crosshair.AttachedLine.Point1.Y =
6437 Crosshair.AttachedLine.Point2.Y = line->Point2.Y;
6438 addedLines++;
6441 notify_crosshair_change (true);
6442 return 0;
6445 /* --------------------------------------------------------------------------- */
6447 static const char polygon_syntax[] = N_("Polygon(Close|PreviousPoint)");
6449 static const char polygon_help[] = N_("Some polygon related stuff.");
6451 /* %start-doc actions Polygon
6453 Polygons need a special action routine to make life easier.
6455 @table @code
6457 @item Close
6458 Creates the final segment of the polygon. This may fail if clipping
6459 to 45 degree lines is switched on, in which case a warning is issued.
6461 @item PreviousPoint
6462 Resets the newly entered corner to the previous one. The Undo action
6463 will call Polygon(PreviousPoint) when appropriate to do so.
6465 @end table
6467 %end-doc */
6469 static int
6470 ActionPolygon (int argc, char **argv, Coord x, Coord y)
6472 char *function = ARG (0);
6473 if (function && Settings.Mode == POLYGON_MODE)
6475 notify_crosshair_change (false);
6476 switch (GetFunctionID (function))
6478 /* close open polygon if possible */
6479 case F_Close:
6480 ClosePolygon ();
6481 break;
6483 /* go back to the previous point */
6484 case F_PreviousPoint:
6485 GoToPreviousPoint ();
6486 break;
6488 notify_crosshair_change (true);
6490 return 0;
6493 /* --------------------------------------------------------------------------- */
6495 static const char routestyle_syntax[] = N_("RouteStyle(1|2|3|4)");
6497 static const char routestyle_help[] =
6498 N_("Copies the indicated routing style into the current sizes.");
6500 /* %start-doc actions RouteStyle
6502 %end-doc */
6504 static int
6505 ActionRouteStyle (int argc, char **argv, Coord x, Coord y)
6507 char *str = ARG (0);
6508 RouteStyleType *rts;
6509 int number;
6511 if (str)
6513 number = atoi (str);
6514 if (number > 0 && number <= NUM_STYLES)
6516 rts = &PCB->RouteStyle[number - 1];
6517 SetLineSize (rts->Thick);
6518 SetViaSize (rts->Diameter, true);
6519 SetViaDrillingHole (rts->Hole, true);
6520 SetKeepawayWidth (rts->Keepaway);
6521 hid_action("RouteStylesChanged");
6524 return 0;
6528 /* --------------------------------------------------------------------------- */
6530 static const char moveobject_syntax[] = N_("MoveObject(X,Y,dim)");
6532 static const char moveobject_help[] =
6533 N_("Moves the object under the crosshair.");
6535 /* %start-doc actions MoveObject
6537 The @code{X} and @code{Y} are treated like @code{delta} is for many
6538 other objects. For each, if it's prefixed by @code{+} or @code{-},
6539 then that amount is relative. Otherwise, it's absolute. Units can be
6540 @code{mil} or @code{mm}; if unspecified, units are PCB's internal
6541 units, currently 1/100 mil.
6543 %end-doc */
6545 static int
6546 ActionMoveObject (int argc, char **argv, Coord x, Coord y)
6548 char *x_str = ARG (0);
6549 char *y_str = ARG (1);
6550 char *units = ARG (2);
6551 Coord nx, ny;
6552 bool absolute1, absolute2;
6553 void *ptr1, *ptr2, *ptr3;
6554 int type;
6556 ny = GetValue (y_str, units, &absolute1);
6557 nx = GetValue (x_str, units, &absolute2);
6559 type = SearchScreen (x, y, MOVE_TYPES, &ptr1, &ptr2, &ptr3);
6560 if (type == NO_TYPE)
6562 Message (_("Nothing found under crosshair\n"));
6563 return 1;
6565 if (absolute1)
6566 nx -= x;
6567 if (absolute2)
6568 ny -= y;
6569 Crosshair.AttachedObject.RubberbandN = 0;
6570 if (TEST_FLAG (RUBBERBANDFLAG, PCB))
6571 LookupRubberbandLines (type, ptr1, ptr2, ptr3);
6572 if (type == ELEMENT_TYPE)
6573 LookupRatLines (type, ptr1, ptr2, ptr3);
6574 MoveObjectAndRubberband (type, ptr1, ptr2, ptr3, nx, ny);
6575 SetChangedFlag (true);
6576 return 0;
6579 /* --------------------------------------------------------------------------- */
6581 static const char movetocurrentlayer_syntax[] =
6582 N_("MoveToCurrentLayer(Object|SelectedObjects)");
6584 static const char movetocurrentlayer_help[] =
6585 N_("Moves objects to the current layer.");
6587 /* %start-doc actions MoveToCurrentLayer
6589 Note that moving an element from a component layer to a solder layer,
6590 or from solder to component, won't automatically flip it. Use the
6591 @code{Flip()} action to do that.
6593 %end-doc */
6595 static int
6596 ActionMoveToCurrentLayer (int argc, char **argv, Coord x, Coord y)
6598 char *function = ARG (0);
6599 if (function)
6601 switch (GetFunctionID (function))
6603 case F_Object:
6605 int type;
6606 void *ptr1, *ptr2, *ptr3;
6608 gui->get_coords (_("Select an Object"), &x, &y);
6609 if ((type =
6610 SearchScreen (x, y, MOVETOLAYER_TYPES,
6611 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
6612 if (MoveObjectToLayer (type, ptr1, ptr2, ptr3, CURRENT, false))
6613 SetChangedFlag (true);
6614 break;
6617 case F_SelectedObjects:
6618 case F_Selected:
6619 if (MoveSelectedObjectsToLayer (CURRENT))
6620 SetChangedFlag (true);
6621 break;
6624 return 0;
6628 static const char setsame_syntax[] = N_("SetSame()");
6630 static const char setsame_help[] =
6631 N_("Sets current layer and sizes to match indicated item.");
6633 /* %start-doc actions SetSame
6635 When invoked over any line, arc, polygon, or via, this changes the
6636 current layer to be the layer that item is on, and changes the current
6637 sizes (thickness, keepaway, drill, etc) according to that item.
6639 %end-doc */
6641 static int
6642 ActionSetSame (int argc, char **argv, Coord x, Coord y)
6644 void *ptr1, *ptr2, *ptr3;
6645 int type;
6646 LayerType *layer = CURRENT;
6648 type = SearchScreen (x, y, CLONE_TYPES, &ptr1, &ptr2, &ptr3);
6649 /* set layer current and size from line or arc */
6650 switch (type)
6652 case LINE_TYPE:
6653 notify_crosshair_change (false);
6654 Settings.LineThickness = ((LineType *) ptr2)->Thickness;
6655 Settings.Keepaway = ((LineType *) ptr2)->Clearance / 2;
6656 layer = (LayerType *) ptr1;
6657 if (Settings.Mode != LINE_MODE)
6658 SetMode (LINE_MODE);
6659 notify_crosshair_change (true);
6660 hid_action ("RouteStylesChanged");
6661 break;
6663 case ARC_TYPE:
6664 notify_crosshair_change (false);
6665 Settings.LineThickness = ((ArcType *) ptr2)->Thickness;
6666 Settings.Keepaway = ((ArcType *) ptr2)->Clearance / 2;
6667 layer = (LayerType *) ptr1;
6668 if (Settings.Mode != ARC_MODE)
6669 SetMode (ARC_MODE);
6670 notify_crosshair_change (true);
6671 hid_action ("RouteStylesChanged");
6672 break;
6674 case POLYGON_TYPE:
6675 layer = (LayerType *) ptr1;
6676 break;
6678 case VIA_TYPE:
6679 notify_crosshair_change (false);
6680 Settings.ViaThickness = ((PinType *) ptr2)->Thickness;
6681 Settings.ViaDrillingHole = ((PinType *) ptr2)->DrillingHole;
6682 Settings.Keepaway = ((PinType *) ptr2)->Clearance / 2;
6683 if (Settings.Mode != VIA_MODE)
6684 SetMode (VIA_MODE);
6685 notify_crosshair_change (true);
6686 hid_action ("RouteStylesChanged");
6687 break;
6689 default:
6690 return 1;
6692 if (layer != CURRENT)
6694 ChangeGroupVisibility (GetLayerNumber (PCB->Data, layer), true, true);
6695 Redraw ();
6697 return 0;
6701 /* --------------------------------------------------------------------------- */
6703 static const char setflag_syntax[] =
6704 N_("SetFlag(Object|Selected|SelectedObjects, flag)\n"
6705 "SetFlag(SelectedLines|SelectedPins|SelectedVias, flag)\n"
6706 "SetFlag(SelectedPads|SelectedTexts|SelectedNames, flag)\n"
6707 "SetFlag(SelectedElements, flag)\n"
6708 "flag = square | octagon | thermal | join");
6710 static const char setflag_help[] = N_("Sets flags on objects.");
6712 /* %start-doc actions SetFlag
6714 Turns the given flag on, regardless of its previous setting. See
6715 @code{ChangeFlag}.
6717 @example
6718 SetFlag(SelectedPins,thermal)
6719 @end example
6721 %end-doc */
6723 static int
6724 ActionSetFlag (int argc, char **argv, Coord x, Coord y)
6726 char *function = ARG (0);
6727 char *flag = ARG (1);
6728 ChangeFlag (function, flag, 1, "SetFlag");
6729 return 0;
6732 /* --------------------------------------------------------------------------- */
6734 static const char clrflag_syntax[] =
6735 N_("ClrFlag(Object|Selected|SelectedObjects, flag)\n"
6736 "ClrFlag(SelectedLines|SelectedPins|SelectedVias, flag)\n"
6737 "ClrFlag(SelectedPads|SelectedTexts|SelectedNames, flag)\n"
6738 "ClrFlag(SelectedElements, flag)\n"
6739 "flag = square | octagon | thermal | join");
6741 static const char clrflag_help[] = N_("Clears flags on objects.");
6743 /* %start-doc actions ClrFlag
6745 Turns the given flag off, regardless of its previous setting. See
6746 @code{ChangeFlag}.
6748 @example
6749 ClrFlag(SelectedLines,join)
6750 @end example
6752 %end-doc */
6754 static int
6755 ActionClrFlag (int argc, char **argv, Coord x, Coord y)
6757 char *function = ARG (0);
6758 char *flag = ARG (1);
6759 ChangeFlag (function, flag, 0, "ClrFlag");
6760 return 0;
6763 /* --------------------------------------------------------------------------- */
6765 static const char changeflag_syntax[] =
6766 N_("ChangeFlag(Object|Selected|SelectedObjects, flag, value)\n"
6767 "ChangeFlag(SelectedLines|SelectedPins|SelectedVias, flag, value)\n"
6768 "ChangeFlag(SelectedPads|SelectedTexts|SelectedNames, flag, value)\n"
6769 "ChangeFlag(SelectedElements, flag, value)\n"
6770 "flag = square | octagon | thermal | join\n"
6771 "value = 0 | 1");
6773 static const char changeflag_help[] = N_("Sets or clears flags on objects.");
6775 /* %start-doc actions ChangeFlag
6777 Toggles the given flag on the indicated object(s). The flag may be
6778 one of the flags listed above (square, octagon, thermal, join). The
6779 value may be the number 0 or 1. If the value is 0, the flag is
6780 cleared. If the value is 1, the flag is set.
6782 %end-doc */
6784 static int
6785 ActionChangeFlag (int argc, char **argv, Coord x, Coord y)
6787 char *function = ARG (0);
6788 char *flag = ARG (1);
6789 int value = argc > 2 ? atoi (argv[2]) : -1;
6790 if (value != 0 && value != 1)
6791 AFAIL (changeflag);
6793 ChangeFlag (function, flag, value, "ChangeFlag");
6794 return 0;
6798 static void
6799 ChangeFlag (char *what, char *flag_name, int value, char *cmd_name)
6801 bool (*set_object) (int, void *, void *, void *);
6802 bool (*set_selected) (int);
6804 if (NSTRCMP (flag_name, "square") == 0)
6806 set_object = value ? SetObjectSquare : ClrObjectSquare;
6807 set_selected = value ? SetSelectedSquare : ClrSelectedSquare;
6809 else if (NSTRCMP (flag_name, "octagon") == 0)
6811 set_object = value ? SetObjectOctagon : ClrObjectOctagon;
6812 set_selected = value ? SetSelectedOctagon : ClrSelectedOctagon;
6814 else if (NSTRCMP (flag_name, "join") == 0)
6816 /* Note: these are backwards, because the flag is "clear" but
6817 the command is "join". */
6818 set_object = value ? ClrObjectJoin : SetObjectJoin;
6819 set_selected = value ? ClrSelectedJoin : SetSelectedJoin;
6821 else
6823 Message (_("%s(): Flag \"%s\" is not valid\n"), cmd_name, flag_name);
6824 return;
6827 switch (GetFunctionID (what))
6829 case F_Object:
6831 int type;
6832 void *ptr1, *ptr2, *ptr3;
6834 if ((type =
6835 SearchScreen (Crosshair.X, Crosshair.Y, CHANGESIZE_TYPES,
6836 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
6837 if (TEST_FLAG (LOCKFLAG, (PinType *) ptr2))
6838 Message (_("Sorry, the object is locked\n"));
6839 if (set_object (type, ptr1, ptr2, ptr3))
6840 SetChangedFlag (true);
6841 break;
6844 case F_SelectedVias:
6845 if (set_selected (VIA_TYPE))
6846 SetChangedFlag (true);
6847 break;
6849 case F_SelectedPins:
6850 if (set_selected (PIN_TYPE))
6851 SetChangedFlag (true);
6852 break;
6854 case F_SelectedPads:
6855 if (set_selected (PAD_TYPE))
6856 SetChangedFlag (true);
6857 break;
6859 case F_SelectedLines:
6860 if (set_selected (LINE_TYPE))
6861 SetChangedFlag (true);
6862 break;
6864 case F_SelectedTexts:
6865 if (set_selected (TEXT_TYPE))
6866 SetChangedFlag (true);
6867 break;
6869 case F_SelectedNames:
6870 if (set_selected (ELEMENTNAME_TYPE))
6871 SetChangedFlag (true);
6872 break;
6874 case F_SelectedElements:
6875 if (set_selected (ELEMENT_TYPE))
6876 SetChangedFlag (true);
6877 break;
6879 case F_Selected:
6880 case F_SelectedObjects:
6881 if (set_selected (CHANGESIZE_TYPES))
6882 SetChangedFlag (true);
6883 break;
6887 /* --------------------------------------------------------------------------- */
6889 static const char executefile_syntax[] = N_("ExecuteFile(filename)");
6891 static const char executefile_help[] = N_("Run actions from the given file.");
6893 /* %start-doc actions ExecuteFile
6895 Lines starting with @code{#} are ignored.
6897 %end-doc */
6899 static int
6900 ActionExecuteFile (int argc, char **argv, Coord x, Coord y)
6902 FILE *fp;
6903 char *fname;
6904 char line[256];
6905 int n = 0;
6906 char *sp;
6908 if (argc != 1)
6909 AFAIL (executefile);
6911 fname = argv[0];
6913 if ((fp = fopen (fname, "r")) == NULL)
6915 fprintf (stderr, _("Could not open actions file \"%s\".\n"), fname);
6916 return 1;
6919 defer_updates = 1;
6920 defer_needs_update = 0;
6921 while (fgets (line, sizeof (line), fp) != NULL)
6923 n++;
6924 sp = line;
6926 /* eat the trailing newline */
6927 while (*sp && *sp != '\r' && *sp != '\n')
6928 sp++;
6929 *sp = '\0';
6931 /* eat leading spaces and tabs */
6932 sp = line;
6933 while (*sp && (*sp == ' ' || *sp == '\t'))
6934 sp++;
6937 * if we have anything left and its not a comment line
6938 * then execute it
6941 if (*sp && *sp != '#')
6943 /*Message ("%s : line %-3d : \"%s\"\n", fname, n, sp);*/
6944 hid_parse_actions (sp);
6948 defer_updates = 0;
6949 if (defer_needs_update)
6951 IncrementUndoSerialNumber ();
6952 gui->invalidate_all ();
6954 fclose (fp);
6955 return 0;
6958 /* --------------------------------------------------------------------------- */
6960 static int
6961 ActionPSCalib (int argc, char **argv, Coord x, Coord y)
6963 HID *ps = hid_find_exporter ("ps");
6964 ps->calibrate (0.0,0.0);
6965 return 0;
6968 /* --------------------------------------------------------------------------- */
6970 static ElementType *element_cache = NULL;
6972 static ElementType *
6973 find_element_by_refdes (char *refdes)
6975 if (element_cache
6976 && NAMEONPCB_NAME(element_cache)
6977 && strcmp (NAMEONPCB_NAME(element_cache), refdes) == 0)
6978 return element_cache;
6980 ELEMENT_LOOP (PCB->Data);
6982 if (NAMEONPCB_NAME(element)
6983 && strcmp (NAMEONPCB_NAME(element), refdes) == 0)
6985 element_cache = element;
6986 return element_cache;
6989 END_LOOP;
6990 return NULL;
6993 static AttributeType *
6994 lookup_attr (AttributeListType *list, const char *name)
6996 int i;
6997 for (i=0; i<list->Number; i++)
6998 if (strcmp (list->List[i].name, name) == 0)
6999 return & list->List[i];
7000 return NULL;
7003 static void
7004 delete_attr (AttributeListType *list, AttributeType *attr)
7006 int idx = attr - list->List;
7007 if (idx < 0 || idx >= list->Number)
7008 return;
7009 if (list->Number - idx > 1)
7010 memmove (attr, attr+1, (list->Number - idx - 1) * sizeof(AttributeType));
7011 list->Number --;
7014 /* ---------------------------------------------------------------- */
7015 static const char elementlist_syntax[] =
7016 N_("ElementList(Start|Done|Need,<refdes>,<footprint>,<value>)");
7018 static const char elementlist_help[] =
7019 N_("Adds the given element if it doesn't already exist.");
7021 /* %start-doc actions elementlist
7023 @table @code
7025 @item Start
7026 Indicates the start of an element list; call this before any Need
7027 actions.
7029 @item Need
7030 Searches the board for an element with a matching refdes.
7032 If found, the value and footprint are updated.
7034 If not found, a new element is created with the given footprint and value.
7036 @item Done
7037 Compares the list of elements needed since the most recent
7038 @code{start} with the list of elements actually on the board. Any
7039 elements that weren't listed are selected, so that the user may delete
7040 them.
7042 @end table
7044 %end-doc */
7046 static int number_of_footprints_not_found;
7048 static int
7049 parse_layout_attribute_units (char *name, int def)
7051 const char *as = AttributeGet (PCB, name);
7052 if (!as)
7053 return def;
7054 return GetValue (as, NULL, NULL);
7057 static int
7058 ActionElementList (int argc, char **argv, Coord x, Coord y)
7060 ElementType *e = NULL;
7061 char *refdes, *value, *footprint, *old;
7062 char *args[3];
7063 char *function;
7065 if (argc < 1)
7066 AFAIL (elementlist);
7068 function = argv[0];
7070 #ifdef DEBUG
7071 printf("Entered ActionElementList, executing function %s\n", function);
7072 #endif
7074 if (strcasecmp (function, "start") == 0)
7076 ELEMENT_LOOP (PCB->Data);
7078 CLEAR_FLAG (FOUNDFLAG, element);
7080 END_LOOP;
7081 element_cache = NULL;
7082 number_of_footprints_not_found = 0;
7083 return 0;
7086 if (strcasecmp (function, "done") == 0)
7088 ELEMENT_LOOP (PCB->Data);
7090 if (TEST_FLAG (FOUNDFLAG, element))
7092 CLEAR_FLAG (FOUNDFLAG, element);
7094 else if (! EMPTY_STRING_P (NAMEONPCB_NAME (element)))
7096 /* Unnamed elements should remain untouched */
7097 SET_FLAG (SELECTEDFLAG, element);
7100 END_LOOP;
7101 if (number_of_footprints_not_found > 0)
7102 gui->confirm_dialog (_("Not all requested footprints were found.\n"
7103 "See the message log for details"),
7104 "Ok", NULL);
7105 return 0;
7108 if (strcasecmp (function, "need") != 0)
7109 AFAIL (elementlist);
7111 if (argc != 4)
7112 AFAIL (elementlist);
7114 argc --;
7115 argv ++;
7117 refdes = ARG(0);
7118 footprint = ARG(1);
7119 value = ARG(2);
7121 args[0] = footprint;
7122 args[1] = refdes;
7123 args[2] = value;
7125 #ifdef DEBUG
7126 printf(" ... footprint = %s\n", footprint);
7127 printf(" ... refdes = %s\n", refdes);
7128 printf(" ... value = %s\n", value);
7129 #endif
7131 e = find_element_by_refdes (refdes);
7133 if (!e)
7135 Coord nx, ny, d;
7137 #ifdef DEBUG
7138 printf(" ... Footprint not on board, need to add it.\n");
7139 #endif
7140 /* Not on board, need to add it. */
7141 if (LoadFootprint(argc, args, x, y))
7143 number_of_footprints_not_found ++;
7144 return 1;
7147 nx = PCB->MaxWidth / 2;
7148 ny = PCB->MaxHeight / 2;
7149 d = MIN (PCB->MaxWidth, PCB->MaxHeight) / 10;
7151 nx = parse_layout_attribute_units ("import::newX", nx);
7152 ny = parse_layout_attribute_units ("import::newY", ny);
7153 d = parse_layout_attribute_units ("import::disperse", d);
7155 if (d > 0)
7157 nx += rand () % (d*2) - d;
7158 ny += rand () % (d*2) - d;
7161 if (nx < 0)
7162 nx = 0;
7163 if (nx >= PCB->MaxWidth)
7164 nx = PCB->MaxWidth - 1;
7165 if (ny < 0)
7166 ny = 0;
7167 if (ny >= PCB->MaxHeight)
7168 ny = PCB->MaxHeight - 1;
7170 /* Place components onto center of board. */
7171 if (CopyPastebufferToLayout (nx, ny))
7172 SetChangedFlag (true);
7175 else if (e && DESCRIPTION_NAME(e) && strcmp (DESCRIPTION_NAME(e), footprint) != 0)
7177 #ifdef DEBUG
7178 printf(" ... Footprint on board, but different from footprint loaded.\n");
7179 #endif
7180 int er, pr, i;
7181 Coord mx, my;
7182 ElementType *pe;
7184 /* Different footprint, we need to swap them out. */
7185 if (LoadFootprint(argc, args, x, y))
7187 number_of_footprints_not_found ++;
7188 return 1;
7191 er = ElementOrientation (e);
7192 pe = PASTEBUFFER->Data->Element->data;
7193 if (!FRONT (e))
7194 MirrorElementCoordinates (PASTEBUFFER->Data, pe, pe->MarkY*2 - PCB->MaxHeight);
7195 pr = ElementOrientation (pe);
7197 mx = e->MarkX;
7198 my = e->MarkY;
7200 if (er != pr)
7201 RotateElementLowLevel (PASTEBUFFER->Data, pe, pe->MarkX, pe->MarkY, (er-pr+4)%4);
7203 for (i=0; i<MAX_ELEMENTNAMES; i++)
7205 pe->Name[i].X = e->Name[i].X - mx + pe->MarkX ;
7206 pe->Name[i].Y = e->Name[i].Y - my + pe->MarkY ;
7207 pe->Name[i].Direction = e->Name[i].Direction;
7208 pe->Name[i].Scale = e->Name[i].Scale;
7211 RemoveElement (e);
7213 if (CopyPastebufferToLayout (mx, my))
7214 SetChangedFlag (true);
7217 /* Now reload footprint */
7218 element_cache = NULL;
7219 e = find_element_by_refdes (refdes);
7221 old = ChangeElementText (PCB, PCB->Data, e, NAMEONPCB_INDEX, strdup (refdes));
7222 if (old)
7223 free(old);
7224 old = ChangeElementText (PCB, PCB->Data, e, VALUE_INDEX, strdup (value));
7225 if (old)
7226 free(old);
7228 SET_FLAG (FOUNDFLAG, e);
7230 #ifdef DEBUG
7231 printf(" ... Leaving ActionElementList.\n");
7232 #endif
7234 return 0;
7237 /* ---------------------------------------------------------------- */
7238 static const char elementsetattr_syntax[] =
7239 N_("ElementSetAttr(refdes,name[,value])");
7241 static const char elementsetattr_help[] =
7242 N_("Sets or clears an element-specific attribute.");
7244 /* %start-doc actions elementsetattr
7246 If a value is specified, the named attribute is added (if not already
7247 present) or changed (if it is) to the given value. If the value is
7248 not specified, the given attribute is removed if present.
7250 %end-doc */
7252 static int
7253 ActionElementSetAttr (int argc, char **argv, Coord x, Coord y)
7255 ElementType *e = NULL;
7256 char *refdes, *name, *value;
7257 AttributeType *attr;
7259 if (argc < 2)
7261 AFAIL (elementsetattr);
7264 refdes = argv[0];
7265 name = argv[1];
7266 value = ARG(2);
7268 ELEMENT_LOOP (PCB->Data);
7270 if (NSTRCMP (refdes, NAMEONPCB_NAME (element)) == 0)
7272 e = element;
7273 break;
7276 END_LOOP;
7278 if (!e)
7280 Message(_("Cannot change attribute of %s - element not found\n"), refdes);
7281 return 1;
7284 attr = lookup_attr (&e->Attributes, name);
7286 if (attr && value)
7288 free (attr->value);
7289 attr->value = strdup (value);
7291 if (attr && ! value)
7293 delete_attr (& e->Attributes, attr);
7295 if (!attr && value)
7297 CreateNewAttribute (& e->Attributes, name, value);
7300 return 0;
7303 /* ---------------------------------------------------------------- */
7304 static const char execcommand_syntax[] = N_("ExecCommand(command)");
7306 static const char execcommand_help[] = N_("Runs a command.");
7308 /* %start-doc actions execcommand
7310 Runs the given command, which is a system executable.
7312 %end-doc */
7314 static int
7315 ActionExecCommand (int argc, char **argv, Coord x, Coord y)
7317 char *command;
7319 if (argc < 1)
7321 AFAIL (execcommand);
7324 command = ARG(0);
7326 if (system (command))
7327 return 1;
7328 return 0;
7331 /* ---------------------------------------------------------------- */
7333 static int
7334 pcb_spawnvp (char **argv)
7336 #ifdef HAVE__SPAWNVP
7337 int result = _spawnvp (_P_WAIT, argv[0], (const char * const *) argv);
7338 if (result == -1)
7339 return 1;
7340 else
7341 return 0;
7342 #else
7343 int pid;
7344 pid = fork ();
7345 if (pid < 0)
7347 /* error */
7348 Message(_("Cannot fork!"));
7349 return 1;
7351 else if (pid == 0)
7353 /* Child */
7354 execvp (argv[0], argv);
7355 exit(1);
7357 else
7359 int rv;
7360 /* Parent */
7361 wait (&rv);
7363 return 0;
7364 #endif
7367 /* ---------------------------------------------------------------- */
7369 /*!
7370 * \brief Creates a new temporary file name.
7372 * Hopefully the operating system provides a mkdtemp() function to
7373 * securily create a temporary directory with mode 0700.\n
7374 * If so then that directory is created and the returned string is made
7375 * up of the directory plus the name variable.\n
7376 * For example:\n
7378 * tempfile_name_new ("myfile") might return
7379 * "/var/tmp/pcb.123456/myfile".
7381 * If mkdtemp() is not available then 'name' is ignored and the
7382 * insecure tmpnam() function is used.
7384 * Files/names created with tempfile_name_new() should be unlinked
7385 * with tempfile_unlink to make sure the temporary directory is also
7386 * removed when mkdtemp() is used.
7388 static char *
7389 tempfile_name_new (char * name)
7391 char *tmpfile = NULL;
7392 #ifdef HAVE_MKDTEMP
7393 char *tmpdir, *mytmpdir;
7394 size_t len;
7395 #endif
7397 assert ( name != NULL );
7399 #ifdef HAVE_MKDTEMP
7400 #define TEMPLATE "pcb.XXXXXXXX"
7403 tmpdir = getenv ("TMPDIR");
7405 /* FIXME -- what about win32? */
7406 if (tmpdir == NULL) {
7407 tmpdir = "/tmp";
7410 mytmpdir = (char *) malloc (sizeof(char) *
7411 (strlen (tmpdir) +
7413 strlen (TEMPLATE) +
7414 1));
7415 if (mytmpdir == NULL) {
7416 fprintf (stderr, "%s(): malloc failed()\n", __FUNCTION__);
7417 exit (1);
7420 *mytmpdir = '\0';
7421 (void)strcat (mytmpdir, tmpdir);
7422 (void)strcat (mytmpdir, PCB_DIR_SEPARATOR_S);
7423 (void)strcat (mytmpdir, TEMPLATE);
7424 if (mkdtemp (mytmpdir) == NULL) {
7425 fprintf (stderr, "%s(): mkdtemp (\"%s\") failed\n", __FUNCTION__, mytmpdir);
7426 free (mytmpdir);
7427 return NULL;
7431 len = strlen (mytmpdir) + /* the temp directory name */
7432 1 + /* the directory sep. */
7433 strlen (name) + /* the file name */
7434 1 /* the \0 termination */
7437 tmpfile = (char *) malloc (sizeof (char) * len);
7439 *tmpfile = '\0';
7440 (void)strcat (tmpfile, mytmpdir);
7441 (void)strcat (tmpfile, PCB_DIR_SEPARATOR_S);
7442 (void)strcat (tmpfile, name);
7444 free (mytmpdir);
7445 #undef TEMPLATE
7446 #else
7448 * tmpnam() uses a static buffer so strdup() the result right away
7449 * in case someone decides to create multiple temp names.
7451 tmpfile = strdup (tmpnam (NULL));
7452 #ifdef __WIN32__
7454 /* Guile doesn't like \ separators */
7455 char *c;
7456 for (c = tmpfile; *c; c++)
7457 if (*c == '\\')
7458 *c = '/';
7460 #endif
7461 #endif
7463 return tmpfile;
7466 /* ---------------------------------------------------------------- */
7469 * \brief Unlink a temporary file.
7471 * If we have mkdtemp() then our temp file lives in a temporary
7472 * directory and we need to remove that directory too.
7474 static int
7475 tempfile_unlink (char * name)
7477 #ifdef DEBUG
7478 /* SDB says: Want to keep old temp files for examiniation when debugging */
7479 return 0;
7480 #endif
7482 #ifdef HAVE_MKDTEMP
7483 int e, rc2 = 0;
7484 char *dname;
7486 unlink (name);
7487 /* it is possible that the file was never created so it is OK if the
7488 unlink fails */
7490 /* now figure out the directory name to remove */
7491 e = strlen (name) - 1;
7492 while (e > 0 && name[e] != PCB_DIR_SEPARATOR_C) {e--;}
7494 dname = strdup (name);
7495 dname[e] = '\0';
7498 * at this point, e *should* point to the end of the directory part
7499 * but lets make sure.
7501 if (e > 0) {
7502 rc2 = rmdir (dname);
7503 if (rc2 != 0) {
7504 perror (dname);
7507 } else {
7508 fprintf (stderr, _("%s(): Unable to determine temp directory name from the temp file\n"),
7509 __FUNCTION__);
7510 fprintf (stderr, "%s(): \"%s\"\n",
7511 __FUNCTION__, name);
7512 rc2 = -1;
7515 /* name was allocated with malloc */
7516 free (dname);
7517 free (name);
7520 * FIXME - should also return -1 if the temp file exists and was not
7521 * removed.
7523 if (rc2 != 0) {
7524 return -1;
7527 #else
7528 int rc = unlink (name);
7530 if (rc != 0) {
7531 fprintf (stderr, _("Failed to unlink \"%s\"\n"), name);
7532 free (name);
7533 return rc;
7535 free (name);
7537 #endif
7539 return 0;
7542 /* ---------------------------------------------------------------- */
7543 static const char import_syntax[] =
7544 N_("Import()\n"
7545 "Import([gnetlist|make[,source,source,...]])\n"
7546 "Import(setnewpoint[,(mark|center|X,Y)])\n"
7547 "Import(setdisperse,D,units)\n");
7549 static const char import_help[] = N_("Import schematics.");
7551 /* %start-doc actions Import
7553 Imports element and netlist data from the schematics (or some other
7554 source). The first parameter, which is optional, is the mode. If not
7555 specified, the @code{import::mode} attribute in the PCB is used.
7556 @code{gnetlist} means gnetlist is used to obtain the information from
7557 the schematics. @code{make} invokes @code{make}, assuming the user
7558 has a @code{Makefile} in the current directory. The @code{Makefile}
7559 will be invoked with the following variables set:
7561 @table @code
7563 @item PCB
7564 The name of the .pcb file
7566 @item SRCLIST
7567 A space-separated list of source files
7569 @item OUT
7570 The name of the file in which to put the command script, which may
7571 contain any @pcb{} actions. By default, this is a temporary file
7572 selected by @pcb{}, but if you specify an @code{import::outfile}
7573 attribute, that file name is used instead (and not automatically
7574 deleted afterwards).
7576 @end table
7578 The target specified to be built is the first of these that apply:
7580 @itemize @bullet
7582 @item
7583 The target specified by an @code{import::target} attribute.
7585 @item
7586 The output file specified by an @code{import::outfile} attribute.
7588 @item
7589 If nothing else is specified, the target is @code{pcb_import}.
7591 @end itemize
7593 If you specify an @code{import::makefile} attribute, then "-f <that
7594 file>" will be added to the command line.
7596 If you specify the mode, you may also specify the source files
7597 (schematics). If you do not specify any, the list of schematics is
7598 obtained by reading the @code{import::src@var{N}} attributes (like
7599 @code{import::src0}, @code{import::src1}, etc).
7601 For compatibility with future extensions to the import file format,
7602 the generated file @emph{must not} start with the two characters
7603 @code{#%}.
7605 If a temporary file is needed the @code{TMPDIR} environment variable
7606 is used to select its location.
7608 Note that the programs @code{gnetlist} and @code{make} may be
7609 overridden by the user via the @code{make-program} and @code{gnetlist}
7610 @code{pcb} settings (i.e. in @code{~/.pcb/settings} or on the command
7611 line).
7613 If @pcb{} cannot determine which schematic(s) to import from, the GUI
7614 is called to let user choose (see @code{ImportGUI()}).
7616 Note that Import() doesn't delete anything - after an Import, elements
7617 which shouldn't be on the board are selected and may be removed once
7618 it's determined that the deletion is appropriate.
7620 If @code{Import()} is called with @code{setnewpoint}, then the location
7621 of new components can be specified. This is where parts show up when
7622 they're added to the board. The default is the center of the board.
7624 @table @code
7626 @item Import(setnewpoint)
7628 Prompts the user to click on the board somewhere, uses that point. If
7629 called by a hotkey, uses the current location of the crosshair.
7631 @item Import(setnewpoint,mark)
7633 Uses the location of the mark. If no mark is present, the point is
7634 not changed.
7636 @item Import(setnewpoint,center)
7638 Resets the point to the center of the board.
7640 @item Import(setnewpoint,X,Y,units)
7642 Sets the point to the specific coordinates given. Example:
7643 @code{Import(setnewpoint,50,25,mm)}
7645 @end table
7647 Note that the X and Y locations are stored in attributes named
7648 @code{import::newX} and @code{import::newY} so you could change them
7649 manually if you wished.
7651 Calling @code{Import(setdisperse,D,units)} sets how much the newly
7652 placed elements are dispersed relative to the set point. For example,
7653 @code{Import(setdisperse,10,mm)} will offset each part randomly up to
7654 10mm away from the point. The default dispersion is 1/10th of the
7655 smallest board dimension. Dispersion is saved in the
7656 @code{import::disperse} attribute.
7658 %end-doc */
7660 static int
7661 ActionImport (int argc, char **argv, Coord x, Coord y)
7663 char *mode;
7664 char **sources = NULL;
7665 int nsources = 0;
7667 #ifdef DEBUG
7668 printf("ActionImport: =========== Entering ActionImport ============\n");
7669 #endif
7671 mode = ARG (0);
7673 if (mode && strcasecmp (mode, "setdisperse") == 0)
7675 char *ds, *units;
7676 char buf[50];
7678 ds = ARG (1);
7679 units = ARG (2);
7680 if (!ds)
7682 const char *as = AttributeGet (PCB, "import::disperse");
7683 ds = gui->prompt_for(_("Enter dispersion:"), as ? as : "0");
7685 if (units)
7687 sprintf(buf, "%s%s", ds, units);
7688 AttributePut (PCB, "import::disperse", buf);
7690 else
7691 AttributePut (PCB, "import::disperse", ds);
7692 if (ARG (1) == NULL)
7693 free (ds);
7694 return 0;
7697 if (mode && strcasecmp (mode, "setnewpoint") == 0)
7699 const char *xs, *ys, *units;
7700 Coord x, y;
7701 char buf[50];
7703 xs = ARG (1);
7704 ys = ARG (2);
7705 units = ARG (3);
7707 if (!xs)
7709 gui->get_coords (_("Click on a location"), &x, &y);
7711 else if (strcasecmp (xs, "center") == 0)
7713 AttributeRemove (PCB, "import::newX");
7714 AttributeRemove (PCB, "import::newY");
7715 return 0;
7717 else if (strcasecmp (xs, "mark") == 0)
7719 if (!Marked.status)
7720 return 0;
7722 x = Marked.X;
7723 y = Marked.Y;
7725 else if (ys)
7727 x = GetValue (xs, units, NULL);
7728 y = GetValue (ys, units, NULL);
7730 else
7732 Message (_("Bad syntax for Import(setnewpoint)"));
7733 return 1;
7736 pcb_snprintf (buf, sizeof (buf), "%$ms", x);
7737 AttributePut (PCB, "import::newX", buf);
7738 pcb_snprintf (buf, sizeof (buf), "%$ms", y);
7739 AttributePut (PCB, "import::newY", buf);
7740 return 0;
7743 if (! mode)
7744 mode = AttributeGet (PCB, "import::mode");
7745 if (! mode)
7746 mode = "gnetlist";
7748 if (argc > 1)
7750 sources = argv + 1;
7751 nsources = argc - 1;
7754 if (! sources)
7756 char sname[40];
7757 char *src;
7759 nsources = -1;
7760 do {
7761 nsources ++;
7762 sprintf(sname, "import::src%d", nsources);
7763 src = AttributeGet (PCB, sname);
7764 } while (src);
7766 if (nsources > 0)
7768 sources = (char **) malloc ((nsources + 1) * sizeof (char *));
7769 nsources = -1;
7770 do {
7771 nsources ++;
7772 sprintf(sname, "import::src%d", nsources);
7773 src = AttributeGet (PCB, sname);
7774 sources[nsources] = src;
7775 } while (src);
7779 if (! sources)
7781 /* Replace .pcb with .sch and hope for the best. */
7782 char *pcbname = PCB->Filename;
7783 char *schname;
7784 char *dot, *slash, *bslash;
7786 if (!pcbname)
7787 return hid_action("ImportGUI");
7789 schname = (char *) malloc (strlen(pcbname) + 5);
7790 strcpy (schname, pcbname);
7791 dot = strchr (schname, '.');
7792 slash = strchr (schname, '/');
7793 bslash = strchr (schname, '\\');
7794 if (dot && slash && dot < slash)
7795 dot = NULL;
7796 if (dot && bslash && dot < bslash)
7797 dot = NULL;
7798 if (dot)
7799 *dot = 0;
7800 strcat (schname, ".sch");
7802 if (access (schname, F_OK))
7804 free (schname);
7805 return hid_action("ImportGUI");
7808 sources = (char **) malloc (2 * sizeof (char *));
7809 sources[0] = schname;
7810 sources[1] = NULL;
7811 nsources = 1;
7814 if (strcasecmp (mode, "gnetlist") == 0)
7816 char *tmpfile = tempfile_name_new ("gnetlist_output");
7817 char **cmd;
7818 int i;
7820 if (tmpfile == NULL) {
7821 Message (_("Could not create temp file"));
7822 return 1;
7825 cmd = (char **) malloc ((7 + nsources) * sizeof (char *));
7826 cmd[0] = Settings.GnetlistProgram;
7827 cmd[1] = "-g";
7828 cmd[2] = "pcbfwd";
7829 cmd[3] = "-o";
7830 cmd[4] = tmpfile;
7831 cmd[5] = "--";
7832 for (i=0; i<nsources; i++)
7833 cmd[6+i] = sources[i];
7834 cmd[6+nsources] = NULL;
7836 #ifdef DEBUG
7837 printf("ActionImport: =========== About to run gnetlist ============\n");
7838 printf("%s %s %s %s %s %s %s ...\n",
7839 cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6]);
7840 #endif
7842 if (pcb_spawnvp (cmd))
7844 unlink (tmpfile);
7845 return 1;
7848 #ifdef DEBUG
7849 printf("ActionImport: =========== About to run ActionExecuteFile, file = %s ============\n", tmpfile);
7850 #endif
7852 cmd[0] = tmpfile;
7853 cmd[1] = NULL;
7854 ActionExecuteFile (1, cmd, 0, 0);
7856 free (cmd);
7857 tempfile_unlink (tmpfile);
7859 else if (strcasecmp (mode, "make") == 0)
7861 int must_free_tmpfile = 0;
7862 char *tmpfile;
7863 char *cmd[10];
7864 int i;
7865 char *srclist;
7866 int srclen;
7867 char *user_outfile = NULL;
7868 char *user_makefile = NULL;
7869 char *user_target = NULL;
7872 user_outfile = AttributeGet (PCB, "import::outfile");
7873 user_makefile = AttributeGet (PCB, "import::makefile");
7874 user_target = AttributeGet (PCB, "import::target");
7875 if (user_outfile && !user_target)
7876 user_target = user_outfile;
7878 if (user_outfile)
7879 tmpfile = user_outfile;
7880 else
7882 tmpfile = tempfile_name_new ("gnetlist_output");
7883 if (tmpfile == NULL) {
7884 Message (_("Could not create temp file"));
7885 free (sources);
7886 return 1;
7888 must_free_tmpfile = 1;
7891 srclen = sizeof("SRCLIST=") + 2;
7892 for (i=0; i<nsources; i++)
7893 srclen += strlen (sources[i]) + 2;
7894 srclist = (char *) malloc (srclen);
7895 strcpy (srclist, "SRCLIST=");
7896 for (i=0; i<nsources; i++)
7898 if (i)
7899 strcat (srclist, " ");
7900 strcat (srclist, sources[i]);
7903 cmd[0] = Settings.MakeProgram;
7904 cmd[1] = "-s";
7905 cmd[2] = Concat ("PCB=", PCB->Filename, NULL);
7906 cmd[3] = srclist;
7907 cmd[4] = Concat ("OUT=", tmpfile, NULL);
7908 i = 5;
7909 if (user_makefile)
7911 cmd[i++] = "-f";
7912 cmd[i++] = user_makefile;
7914 cmd[i++] = user_target ? user_target : (char *)"pcb_import";
7915 cmd[i++] = NULL;
7917 if (pcb_spawnvp (cmd))
7919 if (must_free_tmpfile)
7920 unlink (tmpfile);
7921 free (cmd[2]);
7922 free (cmd[3]);
7923 free (cmd[4]);
7924 return 1;
7927 cmd[0] = tmpfile;
7928 cmd[1] = NULL;
7929 ActionExecuteFile (1, cmd, 0, 0);
7931 free (cmd[2]);
7932 free (cmd[3]);
7933 free (cmd[4]);
7934 if (must_free_tmpfile)
7935 tempfile_unlink (tmpfile);
7937 else
7939 Message (_("Unknown import mode: %s\n"), mode);
7940 return 1;
7943 DeleteRats (false);
7944 AddAllRats (false, NULL);
7946 #ifdef DEBUG
7947 printf("ActionImport: =========== Leaving ActionImport ============\n");
7948 #endif
7950 return 0;
7953 /* ------------------------------------------------------------ */
7955 static const char attributes_syntax[] =
7956 N_("Attributes(Layout|Layer|Element)\n"
7957 "Attributes(Layer,layername)");
7959 static const char attributes_help[] =
7960 N_("Let the user edit the attributes of the layout, current or given\n"
7961 "layer, or selected element.");
7963 /* %start-doc actions Attributes
7965 This just pops up a dialog letting the user edit the attributes of the
7966 pcb, an element, or a layer.
7968 %end-doc */
7971 static int
7972 ActionAttributes (int argc, char **argv, Coord x, Coord y)
7974 char *function = ARG (0);
7975 char *layername = ARG (1);
7976 char *buf;
7978 if (!function)
7979 AFAIL (attributes);
7981 if (!gui->edit_attributes)
7983 Message (_("This GUI doesn't support Attribute Editing\n"));
7984 return 1;
7987 switch (GetFunctionID (function))
7989 case F_Layout:
7991 gui->edit_attributes(_("Layout Attributes"), &(PCB->Attributes));
7992 return 0;
7995 case F_Layer:
7997 LayerType *layer = CURRENT;
7998 if (layername)
8000 int i;
8001 layer = NULL;
8002 for (i=0; i<max_copper_layer; i++)
8003 if (strcmp (PCB->Data->Layer[i].Name, layername) == 0)
8005 layer = & (PCB->Data->Layer[i]);
8006 break;
8008 if (layer == NULL)
8010 Message (_("No layer named %s\n"), layername);
8011 return 1;
8014 buf = (char *) malloc (strlen (layer->Name) +
8015 strlen (_("Layer %s Attributes")));
8016 sprintf (buf, _("Layer %s Attributes"), layer->Name);
8017 gui->edit_attributes(buf, &(layer->Attributes));
8018 free (buf);
8019 return 0;
8022 case F_Element:
8024 int n_found = 0;
8025 ElementType *e = NULL;
8026 ELEMENT_LOOP (PCB->Data);
8028 if (TEST_FLAG (SELECTEDFLAG, element))
8030 e = element;
8031 n_found ++;
8034 END_LOOP;
8035 if (n_found > 1)
8037 Message (_("Too many elements selected\n"));
8038 return 1;
8040 if (n_found == 0)
8042 void *ptrtmp;
8043 gui->get_coords (_("Click on an element"), &x, &y);
8044 if ((SearchScreen
8045 (x, y, ELEMENT_TYPE, &ptrtmp,
8046 &ptrtmp, &ptrtmp)) != NO_TYPE)
8047 e = (ElementType *) ptrtmp;
8048 else
8050 Message (_("No element found there\n"));
8051 return 1;
8055 if (NAMEONPCB_NAME(e))
8057 buf = (char *) malloc (strlen (NAMEONPCB_NAME(e)) +
8058 strlen (_("Element %s Attributes")));
8059 sprintf(buf, _("Element %s Attributes"), NAMEONPCB_NAME(e));
8061 else
8063 buf = strdup (_("Unnamed Element Attributes"));
8065 gui->edit_attributes(buf, &(e->Attributes));
8066 free (buf);
8067 break;
8070 default:
8071 AFAIL (attributes);
8074 return 0;
8077 /* --------------------------------------------------------------------------- */
8079 HID_Action action_action_list[] = {
8080 {"AddRats", 0, ActionAddRats,
8081 addrats_help, addrats_syntax}
8083 {"Attributes", 0, ActionAttributes,
8084 attributes_help, attributes_syntax}
8086 {"Atomic", 0, ActionAtomic,
8087 atomic_help, atomic_syntax}
8089 {"AutoPlaceSelected", 0, ActionAutoPlaceSelected,
8090 autoplace_help, autoplace_syntax}
8092 {"AutoRoute", 0, ActionAutoRoute,
8093 autoroute_help, autoroute_syntax}
8095 {"ChangeClearSize", 0, ActionChangeClearSize,
8096 changeclearsize_help, changeclearsize_syntax}
8098 {"ChangeDrillSize", 0, ActionChange2ndSize,
8099 changedrillsize_help, changedrillsize_syntax}
8101 {"ChangeHole", 0, ActionChangeHole,
8102 changehold_help, changehold_syntax}
8104 {"ChangeJoin", 0, ActionChangeJoin,
8105 changejoin_help, changejoin_syntax}
8107 {"ChangeName", 0, ActionChangeName,
8108 changename_help, changename_syntax}
8110 {"ChangePaste", 0, ActionChangePaste,
8111 changepaste_help, changepaste_syntax}
8113 {"ChangePinName", 0, ActionChangePinName,
8114 changepinname_help, changepinname_syntax}
8116 {"ChangeSize", 0, ActionChangeSize,
8117 changesize_help, changesize_syntax}
8119 {"ChangeSquare", 0, ActionChangeSquare,
8120 changesquare_help, changesquare_syntax}
8122 {"ChangeOctagon", 0, ActionChangeOctagon,
8123 changeoctagon_help, changeoctagon_syntax}
8125 {"ClearSquare", 0, ActionClearSquare,
8126 clearsquare_help, clearsquare_syntax}
8128 {"ClearOctagon", 0, ActionClearOctagon,
8129 clearoctagon_help, clearoctagon_syntax}
8131 {"Connection", 0, ActionConnection,
8132 connection_help, connection_syntax}
8134 {"Delete", 0, ActionDelete,
8135 delete_help, delete_syntax}
8137 {"DeleteRats", 0, ActionDeleteRats,
8138 deleterats_help, deleterats_syntax}
8140 {"DisperseElements", 0, ActionDisperseElements,
8141 disperseelements_help, disperseelements_syntax}
8143 {"Display", 0, ActionDisplay,
8144 display_help, display_syntax}
8146 {"DRC", 0, ActionDRCheck,
8147 drc_help, drc_syntax}
8149 {"DumpLibrary", 0, ActionDumpLibrary,
8150 dumplibrary_help, dumplibrary_syntax}
8152 {"ExecuteFile", 0, ActionExecuteFile,
8153 executefile_help, executefile_syntax}
8155 {"Flip", N_("Click on Object or Flip Point"), ActionFlip,
8156 flip_help, flip_syntax}
8158 {"LoadFrom", 0, ActionLoadFrom,
8159 loadfrom_help, loadfrom_syntax}
8161 {"MarkCrosshair", 0, ActionMarkCrosshair,
8162 markcrosshair_help, markcrosshair_syntax}
8164 {"Message", 0, ActionMessage,
8165 message_help, message_syntax}
8167 {"MinMaskGap", 0, ActionMinMaskGap,
8168 minmaskgap_help, minmaskgap_syntax}
8170 {"MinClearGap", 0, ActionMinClearGap,
8171 mincleargap_help, mincleargap_syntax}
8173 {"Mode", 0, ActionMode,
8174 mode_help, mode_syntax}
8176 {"MorphPolygon", 0, ActionMorphPolygon,
8177 morphpolygon_help, morphpolygon_syntax}
8179 {"PasteBuffer", 0, ActionPasteBuffer,
8180 pastebuffer_help, pastebuffer_syntax}
8182 {"Quit", 0, ActionQuit,
8183 quit_help, quit_syntax}
8185 {"RemoveSelected", 0, ActionRemoveSelected,
8186 removeselected_help, removeselected_syntax}
8188 {"Renumber", 0, ActionRenumber,
8189 renumber_help, renumber_syntax}
8191 {"RipUp", 0, ActionRipUp,
8192 ripup_help, ripup_syntax}
8194 {"Select", 0, ActionSelect,
8195 select_help, select_syntax}
8197 {"Unselect", 0, ActionUnselect,
8198 unselect_help, unselect_syntax}
8200 {"SaveSettings", 0, ActionSaveSettings,
8201 savesettings_help, savesettings_syntax}
8203 {"SaveTo", 0, ActionSaveTo,
8204 saveto_help, saveto_syntax}
8206 {"SetSquare", 0, ActionSetSquare,
8207 setsquare_help, setsquare_syntax}
8209 {"SetOctagon", 0, ActionSetOctagon,
8210 setoctagon_help, setoctagon_syntax}
8212 {"SetThermal", 0, ActionSetThermal,
8213 setthermal_help, setthermal_syntax}
8215 {"SetValue", 0, ActionSetValue,
8216 setvalue_help, setvalue_syntax}
8218 {"ToggleHideName", 0, ActionToggleHideName,
8219 togglehidename_help, togglehidename_syntax}
8221 {"Undo", 0, ActionUndo,
8222 undo_help, undo_syntax}
8224 {"Redo", 0, ActionRedo,
8225 redo_help, redo_syntax}
8227 {"SetSame", N_("Select item to use attributes from"), ActionSetSame,
8228 setsame_help, setsame_syntax}
8230 {"SetFlag", 0, ActionSetFlag,
8231 setflag_help, setflag_syntax}
8233 {"ClrFlag", 0, ActionClrFlag,
8234 clrflag_help, clrflag_syntax}
8236 {"ChangeFlag", 0, ActionChangeFlag,
8237 changeflag_help, changeflag_syntax}
8239 {"Polygon", 0, ActionPolygon,
8240 polygon_help, polygon_syntax}
8242 {"RouteStyle", 0, ActionRouteStyle,
8243 routestyle_help, routestyle_syntax}
8245 {"MoveObject", N_("Select an Object"), ActionMoveObject,
8246 moveobject_help, moveobject_syntax}
8248 {"MoveToCurrentLayer", 0, ActionMoveToCurrentLayer,
8249 movetocurrentlayer_help, movetocurrentlayer_syntax}
8251 {"New", 0, ActionNew,
8252 new_help, new_syntax}
8254 {"pscalib", 0, ActionPSCalib}
8256 {"ElementList", 0, ActionElementList,
8257 elementlist_help, elementlist_syntax}
8259 {"ElementSetAttr", 0, ActionElementSetAttr,
8260 elementsetattr_help, elementsetattr_syntax}
8262 {"ExecCommand", 0, ActionExecCommand,
8263 execcommand_help, execcommand_syntax}
8265 {"Import", 0, ActionImport,
8266 import_help, import_syntax}
8270 REGISTER_ACTIONS (action_action_list)