action.c: new UpdatePackage(Selected|All)
[geda-pcb/whiteaudio.git] / src / action.c
blobc075d10d5d7e772f13ed9308fefe08c7706dda7c
1 /*
2 * COPYRIGHT
4 * PCB, interactive printed circuit board design
5 * Copyright (C) 1994,1995,1996 Thomas Nau
6 * Copyright (C) 1997, 1998, 1999, 2000, 2001 Harry Eaton
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 * Contact addresses for paper mail and Email:
23 * Harry Eaton, 6697 Buttonhole Ct, Columbia, MD 21044, USA
24 * haceaton@aplcomm.jhuapl.edu
28 /* action routines for output window
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
35 #include "global.h"
37 #include "action.h"
38 #include "autoplace.h"
39 #include "autoroute.h"
40 #include "buffer.h"
41 #include "change.h"
42 #include "command.h"
43 #include "copy.h"
44 #include "create.h"
45 #include "crosshair.h"
46 #include "data.h"
47 #include "draw.h"
48 #include "error.h"
49 #include "file.h"
50 #include "find.h"
51 #include "hid.h"
52 #include "insert.h"
53 #include "line.h"
54 #include "mymem.h"
55 #include "misc.h"
56 #include "mirror.h"
57 #include "move.h"
58 #include "polygon.h"
59 /*#include "print.h"*/
60 #include "rats.h"
61 #include "remove.h"
62 #include "report.h"
63 #include "rotate.h"
64 #include "rubberband.h"
65 #include "search.h"
66 #include "select.h"
67 #include "set.h"
68 #include "thermal.h"
69 #include "undo.h"
70 #include "rtree.h"
71 #include "macro.h"
72 #include "pcb-printf.h"
74 #include <assert.h>
75 #include <stdlib.h> /* rand() */
77 #ifdef HAVE_LIBDMALLOC
78 #include <dmalloc.h>
79 #endif
81 /* for fork() and friends */
82 #ifdef HAVE_UNISTD_H
83 #include <unistd.h>
84 #endif
86 #ifdef HAVE_SYS_WAIT_H
87 #include <sys/wait.h>
88 #endif
90 /* ---------------------------------------------------------------------------
91 * some local types
93 typedef enum
95 F_AddSelected,
96 F_All,
97 F_AllConnections,
98 F_AllRats,
99 F_AllUnusedPins,
100 F_Arc,
101 F_Arrow,
102 F_Block,
103 F_Description,
104 F_Cancel,
105 F_Center,
106 F_Clear,
107 F_ClearAndRedraw,
108 F_ClearList,
109 F_Close,
110 F_Connection,
111 F_Convert,
112 F_Copy,
113 F_CycleClip,
114 F_CycleCrosshair,
115 F_DeleteRats,
116 F_Drag,
117 F_DrillReport,
118 F_Element,
119 F_ElementByName,
120 F_ElementConnections,
121 F_ElementToBuffer,
122 F_Escape,
123 F_Find,
124 F_FlipElement,
125 F_FoundPins,
126 F_Grid,
127 F_InsertPoint,
128 F_Layer,
129 F_Layout,
130 F_LayoutAs,
131 F_LayoutToBuffer,
132 F_Line,
133 F_LineSize,
134 F_Lock,
135 F_Mirror,
136 F_Move,
137 F_NameOnPCB,
138 F_Netlist,
139 F_NetByName,
140 F_None,
141 F_Notify,
142 F_Object,
143 F_ObjectByName,
144 F_PasteBuffer,
145 F_PadByName,
146 F_PinByName,
147 F_PinOrPadName,
148 F_Pinout,
149 F_Polygon,
150 F_PolygonHole,
151 F_PreviousPoint,
152 F_RatsNest,
153 F_Rectangle,
154 F_Redraw,
155 F_Release,
156 F_Revert,
157 F_Remove,
158 F_RemoveSelected,
159 F_Report,
160 F_Reset,
161 F_ResetLinesAndPolygons,
162 F_ResetPinsViasAndPads,
163 F_Restore,
164 F_Rotate,
165 F_Save,
166 F_Selected,
167 F_SelectedArcs,
168 F_SelectedElements,
169 F_SelectedLines,
170 F_SelectedNames,
171 F_SelectedObjects,
172 F_SelectedPads,
173 F_SelectedPins,
174 F_SelectedTexts,
175 F_SelectedVias,
176 F_SelectedRats,
177 F_Stroke,
178 F_Text,
179 F_TextByName,
180 F_TextScale,
181 F_Thermal,
182 F_ToLayout,
183 F_ToggleAllDirections,
184 F_ToggleAutoDRC,
185 F_ToggleClearLine,
186 F_ToggleFullPoly,
187 F_ToggleGrid,
188 F_ToggleHideNames,
189 F_ToggleMask,
190 F_ToggleName,
191 F_ToggleObject,
192 F_ToggleShowDRC,
193 F_ToggleLiveRoute,
194 F_ToggleRubberBandMode,
195 F_ToggleStartDirection,
196 F_ToggleSnapPin,
197 F_ToggleThindraw,
198 F_ToggleLockNames,
199 F_ToggleOnlyNames,
200 F_ToggleThindrawPoly,
201 F_ToggleOrthoMove,
202 F_ToggleLocalRef,
203 F_ToggleCheckPlanes,
204 F_ToggleUniqueNames,
205 F_Via,
206 F_ViaByName,
207 F_Value,
208 F_ViaDrillingHole,
209 F_ViaSize,
210 F_Zoom
212 FunctionID;
214 typedef struct /* used to identify subfunctions */
216 char *Identifier;
217 FunctionID ID;
219 FunctionType;
221 /* --------------------------------------------------------------------------- */
223 /* %start-doc actions 00delta
225 Many actions take a @code{delta} parameter as the last parameter,
226 which is an amount to change something. That @code{delta} may include
227 units, as an additional parameter, such as @code{Action(Object,5,mm)}.
228 If no units are specified, the default is PCB's native units
229 (currently 1/100 mil). Also, if the delta is prefixed by @code{+} or
230 @code{-}, the size is increased or decreased by that amount.
231 Otherwise, the size size is set to the given amount.
233 @example
234 Action(Object,5,mil)
235 Action(Object,+0.5,mm)
236 Action(Object,-1)
237 @end example
239 Actions which take a @code{delta} parameter which do not accept all
240 these options will specify what they do take.
242 %end-doc */
244 /* %start-doc actions 00objects
246 Many actions act on indicated objects on the board. They will have
247 parameters like @code{ToggleObject} or @code{SelectedVias} to indicate
248 what group of objects they act on. Unless otherwise specified, these
249 parameters are defined as follows:
251 @table @code
253 @item Object
254 @itemx ToggleObject
255 Affects the object under the mouse pointer. If this action is invoked
256 from a menu or script, the user will be prompted to click on an
257 object, which is then the object affected.
259 @item Selected
260 @itemx SelectedObjects
262 Affects all objects which are currently selected. At least, all
263 selected objects for which the given action makes sense.
265 @item SelectedPins
266 @itemx SelectedVias
267 @itemx Selected@var{Type}
268 @itemx @i{etc}
269 Affects all objects which are both selected and of the @var{Type} specified.
271 @end table
273 %end-doc */
275 /* %start-doc actions 00macros
277 @macro pinshapes
279 Pins, pads, and vias can have various shapes. All may be round. Pins
280 and pads may be square (obviously "square" pads are usually
281 rectangular). Pins and vias may be octagonal. When you change a
282 shape flag of an element, you actually change all of its pins and
283 pads.
285 Note that the square flag takes precedence over the octagon flag,
286 thus, if both the square and octagon flags are set, the object is
287 square. When the square flag is cleared, the pins and pads will be
288 either round or, if the octagon flag is set, octagonal.
290 @end macro
292 %end-doc */
294 /* ---------------------------------------------------------------------------
295 * some local identifiers
297 static PointType InsertedPoint;
298 static LayerType *lastLayer;
299 static struct
301 PolygonType *poly;
302 LineType line;
304 fake;
306 static struct
308 Coord X, Y;
309 Cardinal Buffer;
310 bool Click;
311 bool Moving; /* selected type clicked on */
312 int Hit; /* move type clicked on */
313 void *ptr1;
314 void *ptr2;
315 void *ptr3;
317 Note;
319 static int defer_updates = 0;
320 static int defer_needs_update = 0;
322 static Cardinal polyIndex = 0;
323 static bool saved_mode = false;
324 #ifdef HAVE_LIBSTROKE
325 static bool mid_stroke = false;
326 static BoxType StrokeBox;
327 #endif
328 static FunctionType Functions[] = {
329 {"AddSelected", F_AddSelected},
330 {"All", F_All},
331 {"AllConnections", F_AllConnections},
332 {"AllRats", F_AllRats},
333 {"AllUnusedPins", F_AllUnusedPins},
334 {"Arc", F_Arc},
335 {"Arrow", F_Arrow},
336 {"Block", F_Block},
337 {"Description", F_Description},
338 {"Cancel", F_Cancel},
339 {"Center", F_Center},
340 {"Clear", F_Clear},
341 {"ClearAndRedraw", F_ClearAndRedraw},
342 {"ClearList", F_ClearList},
343 {"Close", F_Close},
344 {"Connection", F_Connection},
345 {"Convert", F_Convert},
346 {"Copy", F_Copy},
347 {"CycleClip", F_CycleClip},
348 {"CycleCrosshair", F_CycleCrosshair},
349 {"DeleteRats", F_DeleteRats},
350 {"Drag", F_Drag},
351 {"DrillReport", F_DrillReport},
352 {"Element", F_Element},
353 {"ElementByName", F_ElementByName},
354 {"ElementConnections", F_ElementConnections},
355 {"ElementToBuffer", F_ElementToBuffer},
356 {"Escape", F_Escape},
357 {"Find", F_Find},
358 {"FlipElement", F_FlipElement},
359 {"FoundPins", F_FoundPins},
360 {"Grid", F_Grid},
361 {"InsertPoint", F_InsertPoint},
362 {"Layer", F_Layer},
363 {"Layout", F_Layout},
364 {"LayoutAs", F_LayoutAs},
365 {"LayoutToBuffer", F_LayoutToBuffer},
366 {"Line", F_Line},
367 {"LineSize", F_LineSize},
368 {"Lock", F_Lock},
369 {"Mirror", F_Mirror},
370 {"Move", F_Move},
371 {"NameOnPCB", F_NameOnPCB},
372 {"Netlist", F_Netlist},
373 {"NetByName", F_NetByName},
374 {"None", F_None},
375 {"Notify", F_Notify},
376 {"Object", F_Object},
377 {"ObjectByName", F_ObjectByName},
378 {"PasteBuffer", F_PasteBuffer},
379 {"PadByName", F_PadByName},
380 {"PinByName", F_PinByName},
381 {"PinOrPadName", F_PinOrPadName},
382 {"Pinout", F_Pinout},
383 {"Polygon", F_Polygon},
384 {"PolygonHole", F_PolygonHole},
385 {"PreviousPoint", F_PreviousPoint},
386 {"RatsNest", F_RatsNest},
387 {"Rectangle", F_Rectangle},
388 {"Redraw", F_Redraw},
389 {"Release", F_Release},
390 {"Remove", F_Remove},
391 {"RemoveSelected", F_RemoveSelected},
392 {"Report", F_Report},
393 {"Reset", F_Reset},
394 {"ResetLinesAndPolygons", F_ResetLinesAndPolygons},
395 {"ResetPinsViasAndPads", F_ResetPinsViasAndPads},
396 {"Restore", F_Restore},
397 {"Revert", F_Revert},
398 {"Rotate", F_Rotate},
399 {"Save", F_Save},
400 {"Selected", F_Selected},
401 {"SelectedArcs", F_SelectedArcs},
402 {"SelectedElements", F_SelectedElements},
403 {"SelectedLines", F_SelectedLines},
404 {"SelectedNames", F_SelectedNames},
405 {"SelectedObjects", F_SelectedObjects},
406 {"SelectedPins", F_SelectedPins},
407 {"SelectedPads", F_SelectedPads},
408 {"SelectedRats", F_SelectedRats},
409 {"SelectedTexts", F_SelectedTexts},
410 {"SelectedVias", F_SelectedVias},
411 {"Stroke", F_Stroke},
412 {"Text", F_Text},
413 {"TextByName", F_TextByName},
414 {"TextScale", F_TextScale},
415 {"Thermal", F_Thermal},
416 {"ToLayout", F_ToLayout},
417 {"Toggle45Degree", F_ToggleAllDirections},
418 {"ToggleClearLine", F_ToggleClearLine},
419 {"ToggleFullPoly", F_ToggleFullPoly},
420 {"ToggleGrid", F_ToggleGrid},
421 {"ToggleMask", F_ToggleMask},
422 {"ToggleName", F_ToggleName},
423 {"ToggleObject", F_ToggleObject},
424 {"ToggleRubberBandMode", F_ToggleRubberBandMode},
425 {"ToggleStartDirection", F_ToggleStartDirection},
426 {"ToggleSnapPin", F_ToggleSnapPin},
427 {"ToggleThindraw", F_ToggleThindraw},
428 {"ToggleThindrawPoly", F_ToggleThindrawPoly},
429 {"ToggleLockNames", F_ToggleLockNames},
430 {"ToggleOnlyNames", F_ToggleOnlyNames},
431 {"ToggleHideNames", F_ToggleHideNames},
432 {"ToggleCheckPlanes", F_ToggleCheckPlanes},
433 {"ToggleLocalRef", F_ToggleLocalRef},
434 {"ToggleOrthoMove", F_ToggleOrthoMove},
435 {"ToggleShowDRC", F_ToggleShowDRC},
436 {"ToggleLiveRoute", F_ToggleLiveRoute},
437 {"ToggleAutoDRC", F_ToggleAutoDRC},
438 {"ToggleUniqueNames", F_ToggleUniqueNames},
439 {"Value", F_Value},
440 {"Via", F_Via},
441 {"ViaByName", F_ViaByName},
442 {"ViaSize", F_ViaSize},
443 {"ViaDrillingHole", F_ViaDrillingHole},
444 {"Zoom", F_Zoom}
447 /* ---------------------------------------------------------------------------
448 * some local routines
450 static int GetFunctionID (String);
451 static void AdjustAttachedBox (void);
452 static void NotifyLine (void);
453 static void NotifyBlock (void);
454 static void NotifyMode (void);
455 static void ClearWarnings (void);
456 #ifdef HAVE_LIBSTROKE
457 static void FinishStroke (void);
458 extern void stroke_init (void);
459 extern void stroke_record (int x, int y);
460 extern int stroke_trans (char *s);
461 #endif
462 static void ChangeFlag (char *, char *, int, char *);
464 #define ARG(n) (argc > (n) ? argv[n] : NULL)
466 #ifdef HAVE_LIBSTROKE
468 /* ---------------------------------------------------------------------------
469 * FinishStroke - try to recognize the stroke sent
471 void
472 FinishStroke (void)
474 char msg[255];
475 int type;
476 unsigned long num;
477 void *ptr1, *ptr2, *ptr3;
479 mid_stroke = false;
480 if (stroke_trans (msg))
482 num = atoi (msg);
483 switch (num)
485 case 456:
486 if (Settings.Mode == LINE_MODE)
488 SetMode (LINE_MODE);
490 break;
491 case 9874123:
492 case 74123:
493 case 987412:
494 case 8741236:
495 case 874123:
496 RotateScreenObject (StrokeBox.X1, StrokeBox.Y1, SWAP_IDENT ? 1 : 3);
497 break;
498 case 7896321:
499 case 786321:
500 case 789632:
501 case 896321:
502 RotateScreenObject (StrokeBox.X1, StrokeBox.Y1, SWAP_IDENT ? 3 : 1);
503 break;
504 case 258:
505 SetMode (LINE_MODE);
506 break;
507 case 852:
508 SetMode (ARROW_MODE);
509 break;
510 case 1478963:
511 ActionUndo ("");
512 break;
513 case 147423:
514 case 147523:
515 case 1474123:
516 Redo (true);
517 break;
518 case 148963:
519 case 147863:
520 case 147853:
521 case 145863:
522 SetMode (VIA_MODE);
523 break;
524 case 951:
525 case 9651:
526 case 9521:
527 case 9621:
528 case 9851:
529 case 9541:
530 case 96521:
531 case 96541:
532 case 98541:
533 SetZoom (1000); /* special zoom extents */
534 break;
535 case 159:
536 case 1269:
537 case 1259:
538 case 1459:
539 case 1569:
540 case 1589:
541 case 12569:
542 case 12589:
543 case 14589:
545 Coord x = (StrokeBox.X1 + StrokeBox.X2) / 2;
546 Coord y = (StrokeBox.Y1 + StrokeBox.Y2) / 2;
547 double z;
548 /* XXX: PCB->MaxWidth and PCB->MaxHeight may be the wrong
549 * divisors below. The old code WAS broken, but this
550 * replacement has not been tested for correctness.
554 log (fabs (StrokeBox.X2 - StrokeBox.X1) / PCB->MaxWidth) /
555 log (2.0);
557 MAX (z,
559 log (fabs (StrokeBox.Y2 - StrokeBox.Y1) / PCB->MaxHeight) /
560 log (2.0));
561 SetZoom (z);
563 CenterDisplay (x, y);
564 break;
567 default:
568 Message (_("Unknown stroke %s\n"), msg);
569 break;
572 else
573 gui->beep ();
575 #endif
577 /* ---------------------------------------------------------------------------
578 * Clear warning color from pins/pads
580 static void
581 ClearWarnings ()
583 Settings.RatWarn = false;
584 ALLPIN_LOOP (PCB->Data);
586 if (TEST_FLAG (WARNFLAG, pin))
588 CLEAR_FLAG (WARNFLAG, pin);
589 DrawPin (pin);
592 ENDALL_LOOP;
593 ALLPAD_LOOP (PCB->Data);
595 if (TEST_FLAG (WARNFLAG, pad))
597 CLEAR_FLAG (WARNFLAG, pad);
598 DrawPad (pad);
601 ENDALL_LOOP;
602 Draw ();
605 static void
606 click_cb (hidval hv)
608 if (Note.Click)
610 notify_crosshair_change (false);
611 Note.Click = false;
612 if (Note.Moving && !gui->shift_is_pressed ())
614 Note.Buffer = Settings.BufferNumber;
615 SetBufferNumber (MAX_BUFFER - 1);
616 ClearBuffer (PASTEBUFFER);
617 AddSelectedToBuffer (PASTEBUFFER, Note.X, Note.Y, true);
618 SaveUndoSerialNumber ();
619 RemoveSelected ();
620 SaveMode ();
621 saved_mode = true;
622 SetMode (PASTEBUFFER_MODE);
624 else if (Note.Hit && !gui->shift_is_pressed ())
626 SaveMode ();
627 saved_mode = true;
628 SetMode (gui->control_is_pressed ()? COPY_MODE : MOVE_MODE);
629 Crosshair.AttachedObject.Ptr1 = Note.ptr1;
630 Crosshair.AttachedObject.Ptr2 = Note.ptr2;
631 Crosshair.AttachedObject.Ptr3 = Note.ptr3;
632 Crosshair.AttachedObject.Type = Note.Hit;
633 AttachForCopy (Note.X, Note.Y);
635 else
637 BoxType box;
639 Note.Hit = 0;
640 Note.Moving = false;
641 SaveUndoSerialNumber ();
642 box.X1 = -MAX_COORD;
643 box.Y1 = -MAX_COORD;
644 box.X2 = MAX_COORD;
645 box.Y2 = MAX_COORD;
646 /* unselect first if shift key not down */
647 if (!gui->shift_is_pressed () && SelectBlock (&box, false))
648 SetChangedFlag (true);
649 NotifyBlock ();
650 Crosshair.AttachedBox.Point1.X = Note.X;
651 Crosshair.AttachedBox.Point1.Y = Note.Y;
653 notify_crosshair_change (true);
657 static void
658 ReleaseMode (void)
660 BoxType box;
662 if (Note.Click)
664 BoxType box;
666 box.X1 = -MAX_COORD;
667 box.Y1 = -MAX_COORD;
668 box.X2 = MAX_COORD;
669 box.Y2 = MAX_COORD;
671 Note.Click = false; /* inhibit timer action */
672 SaveUndoSerialNumber ();
673 /* unselect first if shift key not down */
674 if (!gui->shift_is_pressed ())
676 if (SelectBlock (&box, false))
677 SetChangedFlag (true);
678 if (Note.Moving)
680 Note.Moving = 0;
681 Note.Hit = 0;
682 return;
685 RestoreUndoSerialNumber ();
686 if (SelectObject ())
687 SetChangedFlag (true);
688 Note.Hit = 0;
689 Note.Moving = 0;
691 else if (Note.Moving)
693 RestoreUndoSerialNumber ();
694 NotifyMode ();
695 ClearBuffer (PASTEBUFFER);
696 SetBufferNumber (Note.Buffer);
697 Note.Moving = false;
698 Note.Hit = 0;
700 else if (Note.Hit)
702 NotifyMode ();
703 Note.Hit = 0;
705 else if (Settings.Mode == ARROW_MODE)
707 box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
708 Crosshair.AttachedBox.Point2.X);
709 box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
710 Crosshair.AttachedBox.Point2.Y);
711 box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
712 Crosshair.AttachedBox.Point2.X);
713 box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
714 Crosshair.AttachedBox.Point2.Y);
715 RestoreUndoSerialNumber ();
716 if (SelectBlock (&box, true))
717 SetChangedFlag (true);
718 else if (Bumped)
719 IncrementUndoSerialNumber ();
720 Crosshair.AttachedBox.State = STATE_FIRST;
722 if (saved_mode)
723 RestoreMode ();
724 saved_mode = false;
727 /* ---------------------------------------------------------------------------
728 * get function ID of passed string
730 #define HSIZE 257
731 static char function_hash[HSIZE];
732 static int hash_initted = 0;
734 static int
735 hashfunc(String s)
737 int i = 0;
738 while (*s)
740 i ^= i >> 16;
741 i = (i * 13) ^ (unsigned char)tolower((int) *s);
742 s ++;
744 i = (unsigned int)i % HSIZE;
745 return i;
748 static int
749 GetFunctionID (String Ident)
751 int i, h;
753 if (Ident == 0)
754 return -1;
756 if (!hash_initted)
758 hash_initted = 1;
759 if (HSIZE < ENTRIES (Functions) * 2)
761 fprintf(stderr, _("Error: function hash size too small (%d vs %lu at %s:%d)\n"),
762 HSIZE, (unsigned long) ENTRIES (Functions)*2, __FILE__, __LINE__);
763 exit(1);
765 if (ENTRIES (Functions) > 254)
767 /* Change 'char' to 'int' and remove this when we get to 256
768 strings to hash. */
769 fprintf(stderr, _("Error: function hash type too small (%d vs %lu at %s:%d)\n"),
770 256, (unsigned long) ENTRIES (Functions), __FILE__, __LINE__);
771 exit(1);
774 for (i=ENTRIES (Functions)-1; i>=0; i--)
776 h = hashfunc (Functions[i].Identifier);
777 while (function_hash[h])
778 h = (h + 1) % HSIZE;
779 function_hash[h] = i + 1;
783 i = hashfunc (Ident);
784 while (1)
786 /* We enforce the "hash table bigger than function table" rule,
787 so we know there will be at least one zero entry to find. */
788 if (!function_hash[i])
789 return (-1);
790 if (!strcasecmp (Ident, Functions[function_hash[i]-1].Identifier))
791 return ((int) Functions[function_hash[i]-1].ID);
792 i = (i + 1) % HSIZE;
796 /* ---------------------------------------------------------------------------
797 * set new coordinates if in 'RECTANGLE' mode
798 * the cursor shape is also adjusted
800 static void
801 AdjustAttachedBox (void)
803 if (Settings.Mode == ARC_MODE)
805 Crosshair.AttachedBox.otherway = gui->shift_is_pressed ();
806 return;
808 switch (Crosshair.AttachedBox.State)
810 case STATE_SECOND: /* one corner is selected */
812 /* update coordinates */
813 Crosshair.AttachedBox.Point2.X = Crosshair.X;
814 Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
815 break;
820 /* ---------------------------------------------------------------------------
821 * adjusts the objects which are to be created like attached lines...
823 void
824 AdjustAttachedObjects (void)
826 PointType *pnt;
827 switch (Settings.Mode)
829 /* update at least an attached block (selection) */
830 case NO_MODE:
831 case ARROW_MODE:
832 if (Crosshair.AttachedBox.State)
834 Crosshair.AttachedBox.Point2.X = Crosshair.X;
835 Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
837 break;
839 /* rectangle creation mode */
840 case RECTANGLE_MODE:
841 case ARC_MODE:
842 AdjustAttachedBox ();
843 break;
845 /* polygon creation mode */
846 case POLYGON_MODE:
847 case POLYGONHOLE_MODE:
848 AdjustAttachedLine ();
849 break;
850 /* line creation mode */
851 case LINE_MODE:
852 if (PCB->RatDraw || PCB->Clipping == 0)
853 AdjustAttachedLine ();
854 else
855 AdjustTwoLine (PCB->Clipping - 1);
856 break;
857 /* point insertion mode */
858 case INSERTPOINT_MODE:
859 pnt = AdjustInsertPoint ();
860 if (pnt)
861 InsertedPoint = *pnt;
862 break;
863 case ROTATE_MODE:
864 break;
868 /* ---------------------------------------------------------------------------
869 * creates points of a line
871 static void
872 NotifyLine (void)
874 int type = NO_TYPE;
875 void *ptr1, *ptr2, *ptr3;
877 if (!Marked.status || TEST_FLAG (LOCALREFFLAG, PCB))
878 SetLocalRef (Crosshair.X, Crosshair.Y, true);
879 switch (Crosshair.AttachedLine.State)
881 case STATE_FIRST: /* first point */
882 if (PCB->RatDraw && SearchScreen (Crosshair.X, Crosshair.Y,
883 PAD_TYPE | PIN_TYPE, &ptr1, &ptr1,
884 &ptr1) == NO_TYPE)
886 gui->beep ();
887 break;
889 if (TEST_FLAG (AUTODRCFLAG, PCB) && Settings.Mode == LINE_MODE)
891 type = SearchScreen (Crosshair.X, Crosshair.Y,
892 PIN_TYPE | PAD_TYPE | VIA_TYPE, &ptr1, &ptr2,
893 &ptr3);
894 LookupConnection (Crosshair.X, Crosshair.Y, true, 1,
895 FOUNDFLAG);
897 if (type == PIN_TYPE || type == VIA_TYPE)
899 Crosshair.AttachedLine.Point1.X =
900 Crosshair.AttachedLine.Point2.X = ((PinType *) ptr2)->X;
901 Crosshair.AttachedLine.Point1.Y =
902 Crosshair.AttachedLine.Point2.Y = ((PinType *) ptr2)->Y;
904 else if (type == PAD_TYPE)
906 PadType *pad = (PadType *) ptr2;
907 double d1 = Distance (Crosshair.X, Crosshair.Y, pad->Point1.X, pad->Point1.Y);
908 double d2 = Distance (Crosshair.X, Crosshair.Y, pad->Point2.X, pad->Point2.Y);
909 if (d2 < d1)
911 Crosshair.AttachedLine.Point1 =
912 Crosshair.AttachedLine.Point2 = pad->Point2;
914 else
916 Crosshair.AttachedLine.Point1 =
917 Crosshair.AttachedLine.Point2 = pad->Point1;
920 else
922 Crosshair.AttachedLine.Point1.X =
923 Crosshair.AttachedLine.Point2.X = Crosshair.X;
924 Crosshair.AttachedLine.Point1.Y =
925 Crosshair.AttachedLine.Point2.Y = Crosshair.Y;
927 Crosshair.AttachedLine.State = STATE_SECOND;
928 break;
930 case STATE_SECOND:
931 /* fall through to third state too */
932 lastLayer = CURRENT;
933 default: /* all following points */
934 Crosshair.AttachedLine.State = STATE_THIRD;
935 break;
939 /* ---------------------------------------------------------------------------
940 * create first or second corner of a marked block
942 static void
943 NotifyBlock (void)
945 notify_crosshair_change (false);
946 switch (Crosshair.AttachedBox.State)
948 case STATE_FIRST: /* setup first point */
949 Crosshair.AttachedBox.Point1.X =
950 Crosshair.AttachedBox.Point2.X = Crosshair.X;
951 Crosshair.AttachedBox.Point1.Y =
952 Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
953 Crosshair.AttachedBox.State = STATE_SECOND;
954 break;
956 case STATE_SECOND: /* setup second point */
957 Crosshair.AttachedBox.State = STATE_THIRD;
958 break;
960 notify_crosshair_change (true);
964 /* ---------------------------------------------------------------------------
966 * does what's appropriate for the current mode setting. This normally
967 * means creation of an object at the current crosshair location.
969 * new created objects are added to the create undo list of course
971 static void
972 NotifyMode (void)
974 void *ptr1, *ptr2, *ptr3;
975 int type;
977 if (Settings.RatWarn)
978 ClearWarnings ();
979 switch (Settings.Mode)
981 case ARROW_MODE:
983 int test;
984 hidval hv;
986 Note.Click = true;
987 /* do something after click time */
988 gui->add_timer (click_cb, CLICK_TIME, hv);
990 /* see if we clicked on something already selected
991 * (Note.Moving) or clicked on a MOVE_TYPE
992 * (Note.Hit)
994 for (test = (SELECT_TYPES | MOVE_TYPES) & ~RATLINE_TYPE;
995 test; test &= ~type)
997 type = SearchScreen (Note.X, Note.Y, test, &ptr1, &ptr2, &ptr3);
998 if (!Note.Hit && (type & MOVE_TYPES) &&
999 !TEST_FLAG (LOCKFLAG, (PinType *) ptr2))
1001 Note.Hit = type;
1002 Note.ptr1 = ptr1;
1003 Note.ptr2 = ptr2;
1004 Note.ptr3 = ptr3;
1006 if (!Note.Moving && (type & SELECT_TYPES) &&
1007 TEST_FLAG (SELECTEDFLAG, (PinType *) ptr2))
1008 Note.Moving = true;
1009 if ((Note.Hit && Note.Moving) || type == NO_TYPE)
1010 break;
1012 break;
1015 case VIA_MODE:
1017 PinType *via;
1019 if (!PCB->ViaOn)
1021 Message (_("You must turn via visibility on before\n"
1022 "you can place vias\n"));
1023 break;
1025 if ((via = CreateNewVia (PCB->Data, Note.X, Note.Y,
1026 Settings.ViaThickness, 2 * Settings.Keepaway,
1027 0, Settings.ViaDrillingHole, NULL,
1028 NoFlags ())) != NULL)
1030 AddObjectToCreateUndoList (VIA_TYPE, via, via, via);
1031 if (gui->shift_is_pressed ())
1032 ChangeObjectThermal (VIA_TYPE, via, via, via, PCB->ThermStyle);
1033 IncrementUndoSerialNumber ();
1034 DrawVia (via);
1035 Draw ();
1037 break;
1040 case ARC_MODE:
1042 switch (Crosshair.AttachedBox.State)
1044 case STATE_FIRST:
1045 Crosshair.AttachedBox.Point1.X =
1046 Crosshair.AttachedBox.Point2.X = Note.X;
1047 Crosshair.AttachedBox.Point1.Y =
1048 Crosshair.AttachedBox.Point2.Y = Note.Y;
1049 Crosshair.AttachedBox.State = STATE_SECOND;
1050 break;
1052 case STATE_SECOND:
1053 case STATE_THIRD:
1055 ArcType *arc;
1056 Coord wx, wy;
1057 Angle sa, dir;
1059 wx = Note.X - Crosshair.AttachedBox.Point1.X;
1060 wy = Note.Y - Crosshair.AttachedBox.Point1.Y;
1061 if (XOR (Crosshair.AttachedBox.otherway, abs (wy) > abs (wx)))
1063 Crosshair.AttachedBox.Point2.X =
1064 Crosshair.AttachedBox.Point1.X + abs (wy) * SGNZ (wx);
1065 sa = (wx >= 0) ? 0 : 180;
1066 #ifdef ARC45
1067 if (abs (wy) / 2 >= abs (wx))
1068 dir = (SGNZ (wx) == SGNZ (wy)) ? 45 : -45;
1069 else
1070 #endif
1071 dir = (SGNZ (wx) == SGNZ (wy)) ? 90 : -90;
1073 else
1075 Crosshair.AttachedBox.Point2.Y =
1076 Crosshair.AttachedBox.Point1.Y + abs (wx) * SGNZ (wy);
1077 sa = (wy >= 0) ? -90 : 90;
1078 #ifdef ARC45
1079 if (abs (wx) / 2 >= abs (wy))
1080 dir = (SGNZ (wx) == SGNZ (wy)) ? -45 : 45;
1081 else
1082 #endif
1083 dir = (SGNZ (wx) == SGNZ (wy)) ? -90 : 90;
1084 wy = wx;
1086 if (abs (wy) > 0 && (arc = CreateNewArcOnLayer (CURRENT,
1087 Crosshair.
1088 AttachedBox.
1089 Point2.X,
1090 Crosshair.
1091 AttachedBox.
1092 Point2.Y,
1093 abs (wy),
1094 abs (wy),
1096 dir,
1097 Settings.
1098 LineThickness,
1099 2 * Settings.
1100 Keepaway,
1101 MakeFlags
1102 (TEST_FLAG
1103 (CLEARNEWFLAG,
1104 PCB) ?
1105 CLEARLINEFLAG :
1106 0))))
1108 BoxType *bx;
1110 bx = GetArcEnds (arc);
1111 Crosshair.AttachedBox.Point1.X =
1112 Crosshair.AttachedBox.Point2.X = bx->X2;
1113 Crosshair.AttachedBox.Point1.Y =
1114 Crosshair.AttachedBox.Point2.Y = bx->Y2;
1115 AddObjectToCreateUndoList (ARC_TYPE, CURRENT, arc, arc);
1116 IncrementUndoSerialNumber ();
1117 addedLines++;
1118 DrawArc (CURRENT, arc);
1119 Draw ();
1120 Crosshair.AttachedBox.State = STATE_THIRD;
1122 break;
1125 break;
1127 case LOCK_MODE:
1129 type = SearchScreen (Note.X, Note.Y, LOCK_TYPES, &ptr1, &ptr2, &ptr3);
1130 if (type == ELEMENT_TYPE)
1132 ElementType *element = (ElementType *) ptr2;
1134 TOGGLE_FLAG (LOCKFLAG, element);
1135 PIN_LOOP (element);
1137 TOGGLE_FLAG (LOCKFLAG, pin);
1138 CLEAR_FLAG (SELECTEDFLAG, pin);
1140 END_LOOP;
1141 PAD_LOOP (element);
1143 TOGGLE_FLAG (LOCKFLAG, pad);
1144 CLEAR_FLAG (SELECTEDFLAG, pad);
1146 END_LOOP;
1147 CLEAR_FLAG (SELECTEDFLAG, element);
1148 /* always re-draw it since I'm too lazy
1149 * to tell if a selected flag changed
1151 DrawElement (element);
1152 Draw ();
1153 SetChangedFlag (true);
1154 hid_actionl ("Report", "Object", NULL);
1156 else if (type != NO_TYPE)
1158 TextType *thing = (TextType *) ptr3;
1159 TOGGLE_FLAG (LOCKFLAG, thing);
1160 if (TEST_FLAG (LOCKFLAG, thing)
1161 && TEST_FLAG (SELECTEDFLAG, thing))
1163 /* this is not un-doable since LOCK isn't */
1164 CLEAR_FLAG (SELECTEDFLAG, thing);
1165 DrawObject (type, ptr1, ptr2);
1166 Draw ();
1168 SetChangedFlag (true);
1169 hid_actionl ("Report", "Object", NULL);
1171 break;
1173 case THERMAL_MODE:
1175 if (((type
1177 SearchScreen (Note.X, Note.Y, PIN_TYPES, &ptr1, &ptr2,
1178 &ptr3)) != NO_TYPE)
1179 && !TEST_FLAG (HOLEFLAG, (PinType *) ptr3))
1181 if (gui->shift_is_pressed ())
1183 int tstyle = GET_THERM (INDEXOFCURRENT, (PinType *) ptr3);
1184 tstyle++;
1185 if (tstyle > 5)
1186 tstyle = 1;
1187 ChangeObjectThermal (type, ptr1, ptr2, ptr3, tstyle);
1189 else if (GET_THERM (INDEXOFCURRENT, (PinType *) ptr3))
1190 ChangeObjectThermal (type, ptr1, ptr2, ptr3, 0);
1191 else
1192 ChangeObjectThermal (type, ptr1, ptr2, ptr3, PCB->ThermStyle);
1194 break;
1197 case LINE_MODE:
1198 /* do update of position */
1199 NotifyLine ();
1200 if (Crosshair.AttachedLine.State != STATE_THIRD)
1201 break;
1203 /* Remove anchor if clicking on start point;
1204 * this means we can't paint 0 length lines
1205 * which could be used for square SMD pads.
1206 * Instead use a very small delta, or change
1207 * the file after saving.
1209 if (Crosshair.X == Crosshair.AttachedLine.Point1.X
1210 && Crosshair.Y == Crosshair.AttachedLine.Point1.Y)
1212 SetMode (LINE_MODE);
1213 break;
1216 if (PCB->RatDraw)
1218 RatType *line;
1219 if ((line = AddNet ()))
1221 addedLines++;
1222 AddObjectToCreateUndoList (RATLINE_TYPE, line, line, line);
1223 IncrementUndoSerialNumber ();
1224 DrawRat (line);
1225 Crosshair.AttachedLine.Point1.X =
1226 Crosshair.AttachedLine.Point2.X;
1227 Crosshair.AttachedLine.Point1.Y =
1228 Crosshair.AttachedLine.Point2.Y;
1229 Draw ();
1231 break;
1233 else
1234 /* create line if both ends are determined && length != 0 */
1236 LineType *line;
1237 int maybe_found_flag;
1239 if (PCB->Clipping
1240 && Crosshair.AttachedLine.Point1.X ==
1241 Crosshair.AttachedLine.Point2.X
1242 && Crosshair.AttachedLine.Point1.Y ==
1243 Crosshair.AttachedLine.Point2.Y
1244 && (Crosshair.AttachedLine.Point2.X != Note.X
1245 || Crosshair.AttachedLine.Point2.Y != Note.Y))
1247 /* We will only need to paint the second line segment.
1248 Since we only check for vias on the first segment,
1249 swap them so the non-empty segment is the first segment. */
1250 Crosshair.AttachedLine.Point2.X = Note.X;
1251 Crosshair.AttachedLine.Point2.Y = Note.Y;
1254 if (TEST_FLAG (AUTODRCFLAG, PCB)
1255 && ! TEST_SILK_LAYER (CURRENT))
1256 maybe_found_flag = FOUNDFLAG;
1257 else
1258 maybe_found_flag = 0;
1260 if ((Crosshair.AttachedLine.Point1.X !=
1261 Crosshair.AttachedLine.Point2.X
1262 || Crosshair.AttachedLine.Point1.Y !=
1263 Crosshair.AttachedLine.Point2.Y)
1264 && (line =
1265 CreateDrawnLineOnLayer (CURRENT,
1266 Crosshair.AttachedLine.Point1.X,
1267 Crosshair.AttachedLine.Point1.Y,
1268 Crosshair.AttachedLine.Point2.X,
1269 Crosshair.AttachedLine.Point2.Y,
1270 Settings.LineThickness,
1271 2 * Settings.Keepaway,
1272 MakeFlags (maybe_found_flag |
1273 (TEST_FLAG
1274 (CLEARNEWFLAG,
1275 PCB) ? CLEARLINEFLAG :
1276 0)))) != NULL)
1278 PinType *via;
1280 addedLines++;
1281 AddObjectToCreateUndoList (LINE_TYPE, CURRENT, line, line);
1282 DrawLine (CURRENT, line);
1283 /* place a via if vias are visible, the layer is
1284 in a new group since the last line and there
1285 isn't a pin already here */
1286 if (PCB->ViaOn && GetLayerGroupNumberByPointer (CURRENT) !=
1287 GetLayerGroupNumberByPointer (lastLayer) &&
1288 SearchObjectByLocation (PIN_TYPES, &ptr1, &ptr2, &ptr3,
1289 Crosshair.AttachedLine.Point1.X,
1290 Crosshair.AttachedLine.Point1.Y,
1291 Settings.ViaThickness / 2) ==
1292 NO_TYPE
1293 && (via =
1294 CreateNewVia (PCB->Data,
1295 Crosshair.AttachedLine.Point1.X,
1296 Crosshair.AttachedLine.Point1.Y,
1297 Settings.ViaThickness,
1298 2 * Settings.Keepaway, 0,
1299 Settings.ViaDrillingHole, NULL,
1300 NoFlags ())) != NULL)
1302 AddObjectToCreateUndoList (VIA_TYPE, via, via, via);
1303 DrawVia (via);
1305 /* copy the coordinates */
1306 Crosshair.AttachedLine.Point1.X =
1307 Crosshair.AttachedLine.Point2.X;
1308 Crosshair.AttachedLine.Point1.Y =
1309 Crosshair.AttachedLine.Point2.Y;
1310 IncrementUndoSerialNumber ();
1311 lastLayer = CURRENT;
1313 if (PCB->Clipping && (Note.X != Crosshair.AttachedLine.Point2.X
1314 || Note.Y !=
1315 Crosshair.AttachedLine.Point2.Y)
1316 && (line =
1317 CreateDrawnLineOnLayer (CURRENT,
1318 Crosshair.AttachedLine.Point2.X,
1319 Crosshair.AttachedLine.Point2.Y,
1320 Note.X, Note.Y,
1321 Settings.LineThickness,
1322 2 * Settings.Keepaway,
1323 MakeFlags ((TEST_FLAG
1324 (AUTODRCFLAG,
1325 PCB) ? FOUNDFLAG : 0) |
1326 (TEST_FLAG
1327 (CLEARNEWFLAG,
1328 PCB) ? CLEARLINEFLAG :
1329 0)))) != NULL)
1331 addedLines++;
1332 AddObjectToCreateUndoList (LINE_TYPE, CURRENT, line, line);
1333 IncrementUndoSerialNumber ();
1334 DrawLine (CURRENT, line);
1335 /* move to new start point */
1336 Crosshair.AttachedLine.Point1.X = Note.X;
1337 Crosshair.AttachedLine.Point1.Y = Note.Y;
1338 Crosshair.AttachedLine.Point2.X = Note.X;
1339 Crosshair.AttachedLine.Point2.Y = Note.Y;
1340 if (TEST_FLAG (SWAPSTARTDIRFLAG, PCB))
1342 PCB->Clipping ^= 3;
1345 Draw ();
1347 break;
1349 case RECTANGLE_MODE:
1350 /* do update of position */
1351 NotifyBlock ();
1353 /* create rectangle if both corners are determined
1354 * and width, height are != 0
1356 if (Crosshair.AttachedBox.State == STATE_THIRD &&
1357 Crosshair.AttachedBox.Point1.X != Crosshair.AttachedBox.Point2.X &&
1358 Crosshair.AttachedBox.Point1.Y != Crosshair.AttachedBox.Point2.Y)
1360 PolygonType *polygon;
1362 int flags = CLEARPOLYFLAG;
1363 if (TEST_FLAG (NEWFULLPOLYFLAG, PCB))
1364 flags |= FULLPOLYFLAG;
1365 if ((polygon = CreateNewPolygonFromRectangle (CURRENT,
1366 Crosshair.
1367 AttachedBox.Point1.X,
1368 Crosshair.
1369 AttachedBox.Point1.Y,
1370 Crosshair.
1371 AttachedBox.Point2.X,
1372 Crosshair.
1373 AttachedBox.Point2.Y,
1374 MakeFlags
1375 (flags))) !=
1376 NULL)
1378 AddObjectToCreateUndoList (POLYGON_TYPE, CURRENT,
1379 polygon, polygon);
1380 IncrementUndoSerialNumber ();
1381 DrawPolygon (CURRENT, polygon);
1382 Draw ();
1385 /* reset state to 'first corner' */
1386 Crosshair.AttachedBox.State = STATE_FIRST;
1388 break;
1390 case TEXT_MODE:
1392 char *string;
1394 if ((string = gui->prompt_for (_("Enter text:"), "")) != NULL)
1396 if (strlen(string) > 0)
1398 TextType *text;
1399 int flag = CLEARLINEFLAG;
1401 if (GetLayerGroupNumberByNumber (INDEXOFCURRENT) ==
1402 GetLayerGroupNumberByNumber (solder_silk_layer))
1403 flag |= ONSOLDERFLAG;
1404 if ((text = CreateNewText (CURRENT, &PCB->Font, Note.X,
1405 Note.Y, 0, Settings.TextScale,
1406 string, MakeFlags (flag))) != NULL)
1408 AddObjectToCreateUndoList (TEXT_TYPE, CURRENT, text, text);
1409 IncrementUndoSerialNumber ();
1410 DrawText (CURRENT, text);
1411 Draw ();
1414 free (string);
1416 break;
1419 case POLYGON_MODE:
1421 PointType *points = Crosshair.AttachedPolygon.Points;
1422 Cardinal n = Crosshair.AttachedPolygon.PointN;
1424 /* do update of position; use the 'LINE_MODE' mechanism */
1425 NotifyLine ();
1427 /* check if this is the last point of a polygon */
1428 if (n >= 3 &&
1429 points->X == Crosshair.AttachedLine.Point2.X &&
1430 points->Y == Crosshair.AttachedLine.Point2.Y)
1432 CopyAttachedPolygonToLayer ();
1433 Draw ();
1434 break;
1437 /* create new point if it's the first one or if it's
1438 * different to the last one
1440 if (!n ||
1441 points[n - 1].X != Crosshair.AttachedLine.Point2.X ||
1442 points[n - 1].Y != Crosshair.AttachedLine.Point2.Y)
1444 CreateNewPointInPolygon (&Crosshair.AttachedPolygon,
1445 Crosshair.AttachedLine.Point2.X,
1446 Crosshair.AttachedLine.Point2.Y);
1448 /* copy the coordinates */
1449 Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X;
1450 Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y;
1452 break;
1455 case POLYGONHOLE_MODE:
1457 switch (Crosshair.AttachedObject.State)
1459 /* first notify, lookup object */
1460 case STATE_FIRST:
1461 Crosshair.AttachedObject.Type =
1462 SearchScreen (Note.X, Note.Y, POLYGON_TYPE,
1463 &Crosshair.AttachedObject.Ptr1,
1464 &Crosshair.AttachedObject.Ptr2,
1465 &Crosshair.AttachedObject.Ptr3);
1467 if (Crosshair.AttachedObject.Type != NO_TYPE)
1469 if (TEST_FLAG (LOCKFLAG, (PolygonType *)
1470 Crosshair.AttachedObject.Ptr2))
1472 Message (_("Sorry, the object is locked\n"));
1473 Crosshair.AttachedObject.Type = NO_TYPE;
1474 break;
1476 else
1477 Crosshair.AttachedObject.State = STATE_SECOND;
1479 break;
1481 /* second notify, insert new point into object */
1482 case STATE_SECOND:
1484 PointType *points = Crosshair.AttachedPolygon.Points;
1485 Cardinal n = Crosshair.AttachedPolygon.PointN;
1486 POLYAREA *original, *new_hole, *result;
1487 FlagType Flags;
1489 /* do update of position; use the 'LINE_MODE' mechanism */
1490 NotifyLine ();
1492 /* check if this is the last point of a polygon */
1493 if (n >= 3 &&
1494 points->X == Crosshair.AttachedLine.Point2.X &&
1495 points->Y == Crosshair.AttachedLine.Point2.Y)
1497 /* Create POLYAREAs from the original polygon
1498 * and the new hole polygon */
1499 original = PolygonToPoly ((PolygonType *)Crosshair.AttachedObject.Ptr2);
1500 new_hole = PolygonToPoly (&Crosshair.AttachedPolygon);
1502 /* Subtract the hole from the original polygon shape */
1503 poly_Boolean_free (original, new_hole, &result, PBO_SUB);
1505 /* Convert the resulting polygon(s) into a new set of nodes
1506 * and place them on the page. Delete the original polygon.
1508 SaveUndoSerialNumber ();
1509 Flags = ((PolygonType *)Crosshair.AttachedObject.Ptr2)->Flags;
1510 PolyToPolygonsOnLayer (PCB->Data, (LayerType *)Crosshair.AttachedObject.Ptr1,
1511 result, Flags);
1512 RemoveObject (POLYGON_TYPE,
1513 Crosshair.AttachedObject.Ptr1,
1514 Crosshair.AttachedObject.Ptr2,
1515 Crosshair.AttachedObject.Ptr3);
1516 RestoreUndoSerialNumber ();
1517 IncrementUndoSerialNumber ();
1518 Draw ();
1520 /* reset state of attached line */
1521 memset (&Crosshair.AttachedPolygon, 0, sizeof (PolygonType));
1522 Crosshair.AttachedLine.State = STATE_FIRST;
1523 addedLines = 0;
1525 break;
1528 /* create new point if it's the first one or if it's
1529 * different to the last one
1531 if (!n ||
1532 points[n - 1].X != Crosshair.AttachedLine.Point2.X ||
1533 points[n - 1].Y != Crosshair.AttachedLine.Point2.Y)
1535 CreateNewPointInPolygon (&Crosshair.AttachedPolygon,
1536 Crosshair.AttachedLine.Point2.X,
1537 Crosshair.AttachedLine.Point2.Y);
1539 /* copy the coordinates */
1540 Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X;
1541 Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y;
1543 break;
1547 break;
1550 case PASTEBUFFER_MODE:
1552 TextType estr[MAX_ELEMENTNAMES];
1553 ElementType *e = 0;
1555 if (gui->shift_is_pressed ())
1557 int type =
1558 SearchScreen (Note.X, Note.Y, ELEMENT_TYPE, &ptr1, &ptr2,
1559 &ptr3);
1560 if (type == ELEMENT_TYPE)
1562 e = (ElementType *) ptr1;
1563 if (e)
1565 int i;
1567 memcpy (estr, e->Name,
1568 MAX_ELEMENTNAMES * sizeof (TextType));
1569 for (i = 0; i < MAX_ELEMENTNAMES; ++i)
1570 estr[i].TextString = estr[i].TextString ? strdup(estr[i].TextString) : NULL;
1571 RemoveElement (e);
1575 if (CopyPastebufferToLayout (Note.X, Note.Y))
1576 SetChangedFlag (true);
1577 if (e)
1579 int type =
1580 SearchScreen (Note.X, Note.Y, ELEMENT_TYPE, &ptr1, &ptr2,
1581 &ptr3);
1582 if (type == ELEMENT_TYPE && ptr1)
1584 int i, save_n;
1585 e = (ElementType *) ptr1;
1587 save_n = NAME_INDEX (PCB);
1589 for (i = 0; i < MAX_ELEMENTNAMES; i++)
1591 if (i == save_n)
1592 EraseElementName (e);
1593 r_delete_entry (PCB->Data->name_tree[i],
1594 (BoxType *) & (e->Name[i]));
1595 memcpy (&(e->Name[i]), &(estr[i]), sizeof (TextType));
1596 e->Name[i].Element = e;
1597 SetTextBoundingBox (&PCB->Font, &(e->Name[i]));
1598 r_insert_entry (PCB->Data->name_tree[i],
1599 (BoxType *) & (e->Name[i]), 0);
1600 if (i == save_n)
1601 DrawElementName (e);
1605 break;
1608 case REMOVE_MODE:
1609 if ((type =
1610 SearchScreen (Note.X, Note.Y, REMOVE_TYPES, &ptr1, &ptr2,
1611 &ptr3)) != NO_TYPE)
1613 if (TEST_FLAG (LOCKFLAG, (LineType *) ptr2))
1615 Message (_("Sorry, the object is locked\n"));
1616 break;
1618 if (type == ELEMENT_TYPE)
1620 RubberbandType *ptr;
1621 int i;
1623 Crosshair.AttachedObject.RubberbandN = 0;
1624 LookupRatLines (type, ptr1, ptr2, ptr3);
1625 ptr = Crosshair.AttachedObject.Rubberband;
1626 for (i = 0; i < Crosshair.AttachedObject.RubberbandN; i++)
1628 if (PCB->RatOn)
1629 EraseRat ((RatType *) ptr->Line);
1630 if (TEST_FLAG (RUBBERENDFLAG, ptr->Line))
1631 MoveObjectToRemoveUndoList (RATLINE_TYPE,
1632 ptr->Line, ptr->Line,
1633 ptr->Line);
1634 else
1635 TOGGLE_FLAG (RUBBERENDFLAG, ptr->Line); /* only remove line once */
1636 ptr++;
1639 RemoveObject (type, ptr1, ptr2, ptr3);
1640 IncrementUndoSerialNumber ();
1641 SetChangedFlag (true);
1643 break;
1645 case ROTATE_MODE:
1646 RotateScreenObject (Note.X, Note.Y,
1647 gui->shift_is_pressed ()? (SWAP_IDENT ?
1648 1 : 3)
1649 : (SWAP_IDENT ? 3 : 1));
1650 break;
1652 /* both are almost the same */
1653 case COPY_MODE:
1654 case MOVE_MODE:
1655 switch (Crosshair.AttachedObject.State)
1657 /* first notify, lookup object */
1658 case STATE_FIRST:
1660 int types = (Settings.Mode == COPY_MODE) ?
1661 COPY_TYPES : MOVE_TYPES;
1663 Crosshair.AttachedObject.Type =
1664 SearchScreen (Note.X, Note.Y, types,
1665 &Crosshair.AttachedObject.Ptr1,
1666 &Crosshair.AttachedObject.Ptr2,
1667 &Crosshair.AttachedObject.Ptr3);
1668 if (Crosshair.AttachedObject.Type != NO_TYPE)
1670 if (Settings.Mode == MOVE_MODE &&
1671 TEST_FLAG (LOCKFLAG, (PinType *)
1672 Crosshair.AttachedObject.Ptr2))
1674 Message (_("Sorry, the object is locked\n"));
1675 Crosshair.AttachedObject.Type = NO_TYPE;
1677 else
1678 AttachForCopy (Note.X, Note.Y);
1680 break;
1683 /* second notify, move or copy object */
1684 case STATE_SECOND:
1685 if (Settings.Mode == COPY_MODE)
1686 CopyObject (Crosshair.AttachedObject.Type,
1687 Crosshair.AttachedObject.Ptr1,
1688 Crosshair.AttachedObject.Ptr2,
1689 Crosshair.AttachedObject.Ptr3,
1690 Note.X - Crosshair.AttachedObject.X,
1691 Note.Y - Crosshair.AttachedObject.Y);
1692 else
1694 MoveObjectAndRubberband (Crosshair.AttachedObject.Type,
1695 Crosshair.AttachedObject.Ptr1,
1696 Crosshair.AttachedObject.Ptr2,
1697 Crosshair.AttachedObject.Ptr3,
1698 Note.X - Crosshair.AttachedObject.X,
1699 Note.Y - Crosshair.AttachedObject.Y);
1700 SetLocalRef (0, 0, false);
1702 SetChangedFlag (true);
1704 /* reset identifiers */
1705 Crosshair.AttachedObject.Type = NO_TYPE;
1706 Crosshair.AttachedObject.State = STATE_FIRST;
1707 break;
1709 break;
1711 /* insert a point into a polygon/line/... */
1712 case INSERTPOINT_MODE:
1713 switch (Crosshair.AttachedObject.State)
1715 /* first notify, lookup object */
1716 case STATE_FIRST:
1717 Crosshair.AttachedObject.Type =
1718 SearchScreen (Note.X, Note.Y, INSERT_TYPES,
1719 &Crosshair.AttachedObject.Ptr1,
1720 &Crosshair.AttachedObject.Ptr2,
1721 &Crosshair.AttachedObject.Ptr3);
1723 if (Crosshair.AttachedObject.Type != NO_TYPE)
1725 if (TEST_FLAG (LOCKFLAG, (PolygonType *)
1726 Crosshair.AttachedObject.Ptr2))
1728 Message (_("Sorry, the object is locked\n"));
1729 Crosshair.AttachedObject.Type = NO_TYPE;
1730 break;
1732 else
1734 /* get starting point of nearest segment */
1735 if (Crosshair.AttachedObject.Type == POLYGON_TYPE)
1737 fake.poly =
1738 (PolygonType *) Crosshair.AttachedObject.Ptr2;
1739 polyIndex =
1740 GetLowestDistancePolygonPoint (fake.poly, Note.X,
1741 Note.Y);
1742 fake.line.Point1 = fake.poly->Points[polyIndex];
1743 fake.line.Point2 = fake.poly->Points[
1744 prev_contour_point (fake.poly, polyIndex)];
1745 Crosshair.AttachedObject.Ptr2 = &fake.line;
1748 Crosshair.AttachedObject.State = STATE_SECOND;
1749 InsertedPoint = *AdjustInsertPoint ();
1752 break;
1754 /* second notify, insert new point into object */
1755 case STATE_SECOND:
1756 if (Crosshair.AttachedObject.Type == POLYGON_TYPE)
1757 InsertPointIntoObject (POLYGON_TYPE,
1758 Crosshair.AttachedObject.Ptr1, fake.poly,
1759 &polyIndex,
1760 InsertedPoint.X, InsertedPoint.Y, false, false);
1761 else
1762 InsertPointIntoObject (Crosshair.AttachedObject.Type,
1763 Crosshair.AttachedObject.Ptr1,
1764 Crosshair.AttachedObject.Ptr2,
1765 &polyIndex,
1766 InsertedPoint.X, InsertedPoint.Y, false, false);
1767 SetChangedFlag (true);
1769 /* reset identifiers */
1770 Crosshair.AttachedObject.Type = NO_TYPE;
1771 Crosshair.AttachedObject.State = STATE_FIRST;
1772 break;
1774 break;
1779 /* --------------------------------------------------------------------------- */
1781 static const char atomic_syntax[] = "Atomic(Save|Restore|Close|Block)";
1783 static const char atomic_help[] = "Save or restore the undo serial number.";
1785 /* %start-doc actions Atomic
1787 This action allows making multiple-action bindings into an atomic
1788 operation that will be undone by a single Undo command. For example,
1789 to optimize rat lines, you'd delete the rats and re-add them. To
1790 group these into a single undo, you'd want the deletions and the
1791 additions to have the same undo serial number. So, you @code{Save},
1792 delete the rats, @code{Restore}, add the rats - using the same serial
1793 number as the deletes, then @code{Block}, which checks to see if the
1794 deletions or additions actually did anything. If not, the serial
1795 number is set to the saved number, as there's nothing to undo. If
1796 something did happen, the serial number is incremented so that these
1797 actions are counted as a single undo step.
1799 @table @code
1801 @item Save
1802 Saves the undo serial number.
1804 @item Restore
1805 Returns it to the last saved number.
1807 @item Close
1808 Sets it to 1 greater than the last save.
1810 @item Block
1811 Does a Restore if there was nothing to undo, else does a Close.
1813 @end table
1815 %end-doc */
1817 static int
1818 ActionAtomic (int argc, char **argv, Coord x, Coord y)
1820 if (argc != 1)
1821 AFAIL (atomic);
1823 switch (GetFunctionID (argv[0]))
1825 case F_Save:
1826 SaveUndoSerialNumber ();
1827 break;
1828 case F_Restore:
1829 RestoreUndoSerialNumber ();
1830 break;
1831 case F_Close:
1832 RestoreUndoSerialNumber ();
1833 IncrementUndoSerialNumber ();
1834 break;
1835 case F_Block:
1836 RestoreUndoSerialNumber ();
1837 if (Bumped)
1838 IncrementUndoSerialNumber ();
1839 break;
1841 return 0;
1844 /* -------------------------------------------------------------------------- */
1846 static const char drc_syntax[] = "DRC()";
1848 static const char drc_help[] = "Invoke the DRC check.";
1850 /* %start-doc actions DRC
1852 Note that the design rule check uses the current board rule settings,
1853 not the current style settings.
1855 %end-doc */
1857 static int
1858 ActionDRCheck (int argc, char **argv, Coord x, Coord y)
1860 int count;
1862 if (gui->drc_gui == NULL || gui->drc_gui->log_drc_overview)
1864 Message (_("%m+Rules are minspace %$mS, minoverlap %$mS "
1865 "minwidth %$mS, minsilk %$mS\n"
1866 "min drill %$mS, min annular ring %$mS\n"),
1867 Settings.grid_unit->allow,
1868 PCB->Bloat, PCB->Shrink,
1869 PCB->minWid, PCB->minSlk,
1870 PCB->minDrill, PCB->minRing);
1872 count = DRCAll ();
1873 if (gui->drc_gui == NULL || gui->drc_gui->log_drc_overview)
1875 if (count == 0)
1876 Message (_("No DRC problems found.\n"));
1877 else if (count > 0)
1878 Message (_("Found %d design rule errors.\n"), count);
1879 else
1880 Message (_("Aborted DRC after %d design rule errors.\n"), -count);
1882 return 0;
1885 /* -------------------------------------------------------------------------- */
1887 static const char dumplibrary_syntax[] = "DumpLibrary()";
1889 static const char dumplibrary_help[] =
1890 "Display the entire contents of the libraries.";
1892 /* %start-doc actions DumpLibrary
1895 %end-doc */
1897 static int
1898 ActionDumpLibrary (int argc, char **argv, Coord x, Coord y)
1900 int i, j;
1902 printf ("**** Do not count on this format. It will change ****\n\n");
1903 printf ("MenuN = %d\n", Library.MenuN);
1904 printf ("MenuMax = %d\n", Library.MenuMax);
1905 for (i = 0; i < Library.MenuN; i++)
1907 printf ("Library #%d:\n", i);
1908 printf (" EntryN = %d\n", Library.Menu[i].EntryN);
1909 printf (" EntryMax = %d\n", Library.Menu[i].EntryMax);
1910 printf (" Name = \"%s\"\n", UNKNOWN (Library.Menu[i].Name));
1911 printf (" directory = \"%s\"\n",
1912 UNKNOWN (Library.Menu[i].directory));
1913 printf (" Style = \"%s\"\n", UNKNOWN (Library.Menu[i].Style));
1914 printf (" flag = %d\n", Library.Menu[i].flag);
1916 for (j = 0; j < Library.Menu[i].EntryN; j++)
1918 printf (" #%4d: ", j);
1919 if (Library.Menu[i].Entry[j].Template == (char *) -1)
1921 printf ("newlib: \"%s\"\n",
1922 UNKNOWN (Library.Menu[i].Entry[j].ListEntry));
1924 else
1926 printf ("\"%s\", \"%s\", \"%s\", \"%s\", \"%s\"\n",
1927 UNKNOWN (Library.Menu[i].Entry[j].ListEntry),
1928 UNKNOWN (Library.Menu[i].Entry[j].Template),
1929 UNKNOWN (Library.Menu[i].Entry[j].Package),
1930 UNKNOWN (Library.Menu[i].Entry[j].Value),
1931 UNKNOWN (Library.Menu[i].Entry[j].Description));
1936 return 0;
1939 /* -------------------------------------------------------------------------- */
1941 static const char flip_syntax[] = "Flip(Object|Selected|SelectedElements)";
1943 static const char flip_help[] =
1944 "Flip an element to the opposite side of the board.";
1946 /* %start-doc actions Flip
1948 Note that the location of the element will be symmetric about the
1949 cursor location; i.e. if the part you are pointing at will still be at
1950 the same spot once the element is on the other side. When flipping
1951 multiple elements, this retains their positions relative to each
1952 other, not their absolute positions on the board.
1954 %end-doc */
1956 static int
1957 ActionFlip (int argc, char **argv, Coord x, Coord y)
1959 char *function = ARG (0);
1960 ElementType *element;
1961 void *ptrtmp;
1962 int err = 0;
1964 if (function)
1966 switch (GetFunctionID (function))
1968 case F_Object:
1969 if ((SearchScreen (x, y, ELEMENT_TYPE,
1970 &ptrtmp, &ptrtmp, &ptrtmp)) != NO_TYPE)
1972 element = (ElementType *) ptrtmp;
1973 ChangeElementSide (element, 2 * Crosshair.Y - PCB->MaxHeight);
1974 IncrementUndoSerialNumber ();
1975 Draw ();
1977 break;
1978 case F_Selected:
1979 case F_SelectedElements:
1980 ChangeSelectedElementSide ();
1981 break;
1982 default:
1983 err = 1;
1984 break;
1986 if (!err)
1987 return 0;
1990 AFAIL (flip);
1993 /* -------------------------------------------------------------------------- */
1995 static const char message_syntax[] = "Message(message)";
1997 static const char message_help[] = "Writes a message to the log window.";
1999 /* %start-doc actions Message
2001 This action displays a message to the log window. This action is primarily
2002 provided for use by other programs which may interface with PCB. If
2003 multiple arguments are given, each one is sent to the log window
2004 followed by a newline.
2006 %end-doc */
2008 static int
2009 ActionMessage (int argc, char **argv, Coord x, Coord y)
2011 int i;
2013 if (argc < 1)
2014 AFAIL (message);
2016 for (i = 0; i < argc; i++)
2018 Message (argv[i]);
2019 Message ("\n");
2022 return 0;
2026 /* -------------------------------------------------------------------------- */
2028 static const char setthermal_syntax[] =
2029 "SetThermal(Object|SelectedPins|SelectedVias|Selected, Style)";
2031 static const char setthermal_help[] =
2032 "Set the thermal (on the current layer) of pins or vias to the given style.\n"
2033 "Style = 0 means no thermal.\n"
2034 "Style = 1 has diagonal fingers with sharp edges.\n"
2035 "Style = 2 has horizontal and vertical fingers with sharp edges.\n"
2036 "Style = 3 is a solid connection to the plane."
2037 "Style = 4 has diagonal fingers with rounded edges.\n"
2038 "Style = 5 has horizontal and vertical fingers with rounded edges.\n";
2040 /* %start-doc actions SetThermal
2042 This changes how/whether pins or vias connect to any rectangle or polygon
2043 on the current layer. The first argument can specify one object, or all
2044 selected pins, or all selected vias, or all selected pins and vias.
2045 The second argument specifies the style of connection.
2046 There are 5 possibilities:
2047 0 - no connection,
2048 1 - 45 degree fingers with sharp edges,
2049 2 - horizontal & vertical fingers with sharp edges,
2050 3 - solid connection,
2051 4 - 45 degree fingers with rounded corners,
2052 5 - horizontal & vertical fingers with rounded corners.
2054 Pins and Vias may have thermals whether or not there is a polygon available
2055 to connect with. However, they will have no effect without the polygon.
2056 %end-doc */
2058 static int
2059 ActionSetThermal (int argc, char **argv, Coord x, Coord y)
2061 char *function = ARG (0);
2062 char *style = ARG (1);
2063 void *ptr1, *ptr2, *ptr3;
2064 int type, kind;
2065 int err = 0;
2067 if (function && *function && style && *style)
2069 bool absolute;
2071 kind = GetValue (style, NULL, &absolute);
2072 if (absolute)
2073 switch (GetFunctionID (function))
2075 case F_Object:
2076 if ((type =
2077 SearchScreen (Crosshair.X, Crosshair.Y, CHANGETHERMAL_TYPES,
2078 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
2080 ChangeObjectThermal (type, ptr1, ptr2, ptr3, kind);
2081 IncrementUndoSerialNumber ();
2082 Draw ();
2084 break;
2085 case F_SelectedPins:
2086 ChangeSelectedThermals (PIN_TYPE, kind);
2087 break;
2088 case F_SelectedVias:
2089 ChangeSelectedThermals (VIA_TYPE, kind);
2090 break;
2091 case F_Selected:
2092 case F_SelectedElements:
2093 ChangeSelectedThermals (CHANGETHERMAL_TYPES, kind);
2094 break;
2095 default:
2096 err = 1;
2097 break;
2099 else
2100 err = 1;
2101 if (!err)
2102 return 0;
2105 AFAIL (setthermal);
2108 /* ---------------------------------------------------------------------------
2109 * !!! no action routine !!!
2111 * event handler to set the cursor according to the X pointer position
2112 * called from inside main.c
2114 void
2115 EventMoveCrosshair (int ev_x, int ev_y)
2117 #ifdef HAVE_LIBSTROKE
2118 if (mid_stroke)
2120 StrokeBox.X2 = ev_x;
2121 StrokeBox.Y2 = ev_y;
2122 stroke_record (ev_x, ev_y);
2123 return;
2125 #endif /* HAVE_LIBSTROKE */
2126 if (MoveCrosshairAbsolute (ev_x, ev_y))
2128 /* update object position and cursor location */
2129 AdjustAttachedObjects ();
2130 notify_crosshair_change (true);
2134 /* --------------------------------------------------------------------------- */
2136 static const char setvalue_syntax[] =
2137 "SetValue(Grid|Line|LineSize|Text|TextScale|ViaDrillingHole|Via|ViaSize, delta)";
2139 static const char setvalue_help[] =
2140 "Change various board-wide values and sizes.";
2142 /* %start-doc actions SetValue
2144 @table @code
2146 @item ViaDrillingHole
2147 Changes the diameter of the drill for new vias.
2149 @item Grid
2150 Sets the grid spacing.
2152 @item Line
2153 @item LineSize
2154 Changes the thickness of new lines.
2156 @item Via
2157 @item ViaSize
2158 Changes the diameter of new vias.
2160 @item Text
2161 @item TextScale
2162 Changes the size of new text.
2164 @end table
2166 %end-doc */
2168 static int
2169 ActionSetValue (int argc, char **argv, Coord x, Coord y)
2171 char *function = ARG (0);
2172 char *val = ARG (1);
2173 char *units = ARG (2);
2174 bool absolute; /* flag for 'absolute' value */
2175 double value;
2176 int text_scale;
2177 int err = 0;
2179 if (function && val)
2181 value = GetValue (val, units, &absolute);
2182 switch (GetFunctionID (function))
2184 case F_ViaDrillingHole:
2185 SetViaDrillingHole (absolute ? value :
2186 value + Settings.ViaDrillingHole,
2187 false);
2188 hid_action ("RouteStylesChanged");
2189 break;
2191 case F_Grid:
2192 if (absolute)
2193 SetGrid (value, false);
2194 else
2196 if (value == 0)
2197 value = val[0] == '-' ? -Settings.increments->grid
2198 : Settings.increments->grid;
2199 /* On the way down, short against the minimum
2200 * PCB drawing unit */
2201 if ((value + PCB->Grid) < 1)
2202 SetGrid (1, false);
2203 else if (PCB->Grid == 1)
2204 SetGrid (value, false);
2205 else
2206 SetGrid (value + PCB->Grid, false);
2208 break;
2210 case F_LineSize:
2211 case F_Line:
2212 if (!absolute && value == 0)
2213 value = val[0] == '-' ? -Settings.increments->line
2214 : Settings.increments->line;
2215 SetLineSize (absolute ? value : value + Settings.LineThickness);
2216 hid_action ("RouteStylesChanged");
2217 break;
2219 case F_Via:
2220 case F_ViaSize:
2221 SetViaSize (absolute ? value : value + Settings.ViaThickness, false);
2222 hid_action ("RouteStylesChanged");
2223 break;
2225 case F_Text:
2226 case F_TextScale:
2227 text_scale = value / (double)FONT_CAPHEIGHT * 100.;
2228 if (!absolute)
2229 text_scale += Settings.TextScale;
2230 SetTextScale (text_scale);
2231 break;
2232 default:
2233 err = 1;
2234 break;
2236 if (!err)
2237 return 0;
2240 AFAIL (setvalue);
2244 /* --------------------------------------------------------------------------- */
2246 static const char quit_syntax[] = "Quit()";
2248 static const char quit_help[] = "Quits the application after confirming.";
2250 /* %start-doc actions Quit
2252 If you have unsaved changes, you will be prompted to confirm (or
2253 save) before quitting.
2255 %end-doc */
2257 static int
2258 ActionQuit (int argc, char **argv, Coord x, Coord y)
2260 char *force = ARG (0);
2261 if (force && strcasecmp (force, "force") == 0)
2263 PCB->Changed = 0;
2264 exit (0);
2266 if (!PCB->Changed || gui->close_confirm_dialog () == HID_CLOSE_CONFIRM_OK)
2267 QuitApplication ();
2268 return 1;
2271 /* --------------------------------------------------------------------------- */
2273 static const char connection_syntax[] =
2274 "Connection(Find|ResetLinesAndPolygons|ResetPinsAndVias|Reset)";
2276 static const char connection_help[] =
2277 "Searches connections of the object at the cursor position.";
2279 /* %start-doc actions Connection
2281 Connections found with this action will be highlighted in the
2282 ``connected-color'' color and will have the ``found'' flag set.
2284 @table @code
2286 @item Find
2287 The net under the cursor is ``found''.
2289 @item ResetLinesAndPolygons
2290 Any ``found'' lines and polygons are marked ``not found''.
2292 @item ResetPinsAndVias
2293 Any ``found'' pins and vias are marked ``not found''.
2295 @item Reset
2296 All ``found'' objects are marked ``not found''.
2298 @end table
2300 %end-doc */
2302 static int
2303 ActionConnection (int argc, char **argv, Coord x, Coord y)
2305 char *function = ARG (0);
2306 if (function)
2308 switch (GetFunctionID (function))
2310 case F_Find:
2312 gui->get_coords (_("Click on a connection"), &x, &y);
2313 LookupConnection (x, y, true, 1, FOUNDFLAG);
2314 break;
2317 case F_ResetLinesAndPolygons:
2318 if (ResetFoundLinesAndPolygons (true))
2320 IncrementUndoSerialNumber ();
2321 Draw ();
2323 break;
2325 case F_ResetPinsViasAndPads:
2326 if (ResetFoundPinsViasAndPads (true))
2328 IncrementUndoSerialNumber ();
2329 Draw ();
2331 break;
2333 case F_Reset:
2334 if (ResetConnections (true))
2336 IncrementUndoSerialNumber ();
2337 Draw ();
2339 break;
2341 return 0;
2344 AFAIL (connection);
2347 /* --------------------------------------------------------------------------- */
2349 static const char disperseelements_syntax[] =
2350 "DisperseElements(All|Selected)";
2352 static const char disperseelements_help[] = "Disperses elements.";
2354 /* %start-doc actions DisperseElements
2356 Normally this is used when starting a board, by selecting all elements
2357 and then dispersing them. This scatters the elements around the board
2358 so that you can pick individual ones, rather than have all the
2359 elements at the same 0,0 coordinate and thus impossible to choose
2360 from.
2362 %end-doc */
2364 #define GAP MIL_TO_COORD(100)
2366 static int
2367 ActionDisperseElements (int argc, char **argv, Coord x, Coord y)
2369 char *function = ARG (0);
2370 Coord minx = GAP,
2371 miny = GAP,
2372 maxy = GAP,
2373 dx, dy;
2374 int all = 0, bad = 0;
2376 if (!function || !*function)
2378 bad = 1;
2380 else
2382 switch (GetFunctionID (function))
2384 case F_All:
2385 all = 1;
2386 break;
2388 case F_Selected:
2389 all = 0;
2390 break;
2392 default:
2393 bad = 1;
2397 if (bad)
2399 AFAIL (disperseelements);
2403 ELEMENT_LOOP (PCB->Data);
2406 * If we want to disperse selected elements, maybe we need smarter
2407 * code here to avoid putting components on top of others which
2408 * are not selected. For now, I'm assuming that this is typically
2409 * going to be used either with a brand new design or a scratch
2410 * design holding some new components
2412 if (!TEST_FLAG (LOCKFLAG, element) && (all || TEST_FLAG (SELECTEDFLAG, element)))
2415 /* figure out how much to move the element */
2416 dx = minx - element->BoundingBox.X1;
2418 /* snap to the grid */
2419 dx -= (element->MarkX + dx) % PCB->Grid;
2422 * and add one grid size so we make sure we always space by GAP or
2423 * more
2425 dx += PCB->Grid;
2427 /* Figure out if this row has room. If not, start a new row */
2428 if (GAP + element->BoundingBox.X2 + dx > PCB->MaxWidth)
2430 miny = maxy + GAP;
2431 minx = GAP;
2434 /* figure out how much to move the element */
2435 dx = minx - element->BoundingBox.X1;
2436 dy = miny - element->BoundingBox.Y1;
2438 /* snap to the grid */
2439 dx -= (element->MarkX + dx) % PCB->Grid;
2440 dx += PCB->Grid;
2441 dy -= (element->MarkY + dy) % PCB->Grid;
2442 dy += PCB->Grid;
2444 /* move the element */
2445 MoveElementLowLevel (PCB->Data, element, dx, dy);
2447 /* and add to the undo list so we can undo this operation */
2448 AddObjectToMoveUndoList (ELEMENT_TYPE, NULL, NULL, element, dx, dy);
2450 /* keep track of how tall this row is */
2451 minx += element->BoundingBox.X2 - element->BoundingBox.X1 + GAP;
2452 if (maxy < element->BoundingBox.Y2)
2454 maxy = element->BoundingBox.Y2;
2459 END_LOOP;
2461 /* done with our action so increment the undo # */
2462 IncrementUndoSerialNumber ();
2464 Redraw ();
2465 SetChangedFlag (true);
2467 return 0;
2470 #undef GAP
2472 /* --------------------------------------------------------------------------- */
2474 static const char display_syntax[] =
2475 "Display(NameOnPCB|Description|Value)\n"
2476 "Display(Grid|Redraw)\n"
2477 "Display(CycleClip|CycleCrosshair|Toggle45Degree|ToggleStartDirection)\n"
2478 "Display(ToggleGrid|ToggleRubberBandMode|ToggleUniqueNames)\n"
2479 "Display(ToggleMask|ToggleName|ToggleClearLine|ToggleFullPoly|ToggleSnapPin)\n"
2480 "Display(ToggleThindraw|ToggleThindrawPoly|ToggleOrthoMove|ToggleLocalRef)\n"
2481 "Display(ToggleCheckPlanes|ToggleShowDRC|ToggleAutoDRC)\n"
2482 "Display(ToggleLiveRoute|LockNames|OnlyNames)\n"
2483 "Display(Pinout|PinOrPadName)";
2485 static const char display_help[] = "Several display-related actions.";
2487 /* %start-doc actions Display
2489 @table @code
2491 @item NameOnPCB
2492 @item Description
2493 @item Value
2494 Specify whether all elements show their name, description, or value.
2496 @item Redraw
2497 Redraw the whole board.
2499 @item Toggle45Degree
2500 When clear, lines can be drawn at any angle. When set, lines are
2501 restricted to multiples of 45 degrees and requested lines may be
2502 broken up according to the clip setting.
2504 @item CycleClip
2505 Changes the way lines are restricted to 45 degree increments. The
2506 various settings are: straight only, orthogonal then angled, and angled
2507 then orthogonal. If AllDirections is set, this action disables it.
2509 @item CycleCrosshair
2510 Changes crosshair drawing. Crosshair may accept form of 4-ray,
2511 8-ray and 12-ray cross.
2513 @item ToggleRubberBandMode
2514 If set, moving an object moves all the lines attached to it too.
2516 @item ToggleStartDirection
2517 If set, each time you set a point in a line, the Clip toggles between
2518 orth-angle and angle-ortho.
2520 @item ToggleUniqueNames
2521 If set, you will not be permitted to change the name of an element to
2522 match that of another element.
2524 @item ToggleSnapPin
2525 If set, pin centers and pad end points are treated as additional grid
2526 points that the cursor can snap to.
2528 @item ToggleLocalRef
2529 If set, the mark is automatically set to the beginning of any move, so
2530 you can see the relative distance you've moved.
2532 @item ToggleThindraw
2533 If set, objects on the screen are drawn as outlines (lines are drawn
2534 as center-lines). This lets you see line endpoints hidden under pins,
2535 for example.
2537 @item ToggleThindrawPoly
2538 If set, polygons on the screen are drawn as outlines.
2540 @item ToggleShowDRC
2541 If set, pending objects (i.e. lines you're in the process of drawing)
2542 will be drawn with an outline showing how far away from other copper
2543 you need to be.
2545 @item ToggleLiveRoute
2546 If set, the progress of the autorouter will be visible on the screen.
2548 @item ToggleAutoDRC
2549 If set, you will not be permitted to make connections which violate
2550 the current DRC and netlist settings.
2552 @item ToggleCheckPlanes
2553 If set, lines and arcs aren't drawn, which usually leaves just the
2554 polygons. If you also disable all but the layer you're interested in,
2555 this allows you to check for isolated regions.
2557 @item ToggleOrthoMove
2558 If set, the crosshair is only allowed to move orthogonally from its
2559 previous position. I.e. you can move an element or line up, down,
2560 left, or right, but not up+left or down+right.
2562 @item ToggleName
2563 Selects whether the pinouts show the pin names or the pin numbers.
2565 @item ToggleLockNames
2566 If set, text will ignore left mouse clicks and actions that work on
2567 objects under the mouse. You can still select text with a lasso (left
2568 mouse drag) and perform actions on the selection.
2570 @item ToggleOnlyNames
2571 If set, only text will be sensitive for mouse clicks and actions that
2572 work on objects under the mouse. You can still select other objects
2573 with a lasso (left mouse drag) and perform actions on the selection.
2575 @item ToggleMask
2576 Turns the solder mask on or off.
2578 @item ToggleClearLine
2579 When set, the clear-line flag causes new lines and arcs to have their
2580 ``clear polygons'' flag set, so they won't be electrically connected
2581 to any polygons they overlap.
2583 @item ToggleFullPoly
2584 When set, the full-poly flag causes new polygons to have their
2585 ``full polygon'' flag set, so all parts of them will be displayed
2586 instead of only the biggest one.
2588 @item ToggleGrid
2589 Resets the origin of the current grid to be wherever the mouse pointer
2590 is (not where the crosshair currently is). If you provide two numbers
2591 after this, the origin is set to that coordinate.
2593 @item Grid
2594 Toggles whether the grid is displayed or not.
2596 @item Pinout
2597 Causes the pinout of the element indicated by the cursor to be
2598 displayed, usually in a separate window.
2600 @item PinOrPadName
2601 Toggles whether the names of pins, pads, or (yes) vias will be
2602 displayed. If the cursor is over an element, all of its pins and pads
2603 are affected.
2605 @end table
2607 %end-doc */
2609 static enum crosshair_shape
2610 CrosshairShapeIncrement (enum crosshair_shape shape)
2612 switch(shape)
2614 case Basic_Crosshair_Shape:
2615 shape = Union_Jack_Crosshair_Shape;
2616 break;
2617 case Union_Jack_Crosshair_Shape:
2618 shape = Dozen_Crosshair_Shape;
2619 break;
2620 case Dozen_Crosshair_Shape:
2621 shape = Crosshair_Shapes_Number;
2622 break;
2623 case Crosshair_Shapes_Number:
2624 shape = Basic_Crosshair_Shape;
2625 break;
2627 return shape;
2630 static int
2631 ActionDisplay (int argc, char **argv, Coord childX, Coord childY)
2633 char *function, *str_dir;
2634 int id;
2635 int err = 0;
2637 function = ARG (0);
2638 str_dir = ARG (1);
2640 if (function && (!str_dir || !*str_dir))
2642 switch (id = GetFunctionID (function))
2645 /* redraw layout */
2646 case F_ClearAndRedraw:
2647 case F_Redraw:
2648 Redraw ();
2649 break;
2651 /* change the displayed name of elements */
2652 case F_Value:
2653 case F_NameOnPCB:
2654 case F_Description:
2655 ELEMENT_LOOP (PCB->Data);
2657 EraseElementName (element);
2659 END_LOOP;
2660 CLEAR_FLAG (DESCRIPTIONFLAG | NAMEONPCBFLAG, PCB);
2661 switch (id)
2663 case F_Value:
2664 break;
2665 case F_NameOnPCB:
2666 SET_FLAG (NAMEONPCBFLAG, PCB);
2667 break;
2668 case F_Description:
2669 SET_FLAG (DESCRIPTIONFLAG, PCB);
2670 break;
2672 ELEMENT_LOOP (PCB->Data);
2674 DrawElementName (element);
2676 END_LOOP;
2677 Draw ();
2678 break;
2680 /* toggle line-adjust flag */
2681 case F_ToggleAllDirections:
2682 TOGGLE_FLAG (ALLDIRECTIONFLAG, PCB);
2683 AdjustAttachedObjects ();
2684 break;
2686 case F_CycleClip:
2687 notify_crosshair_change (false);
2688 if (TEST_FLAG (ALLDIRECTIONFLAG, PCB))
2690 TOGGLE_FLAG (ALLDIRECTIONFLAG, PCB);
2691 PCB->Clipping = 0;
2693 else
2694 PCB->Clipping = (PCB->Clipping + 1) % 3;
2695 AdjustAttachedObjects ();
2696 notify_crosshair_change (true);
2697 break;
2699 case F_CycleCrosshair:
2700 notify_crosshair_change (false);
2701 Crosshair.shape = CrosshairShapeIncrement(Crosshair.shape);
2702 if (Crosshair_Shapes_Number == Crosshair.shape)
2703 Crosshair.shape = Basic_Crosshair_Shape;
2704 notify_crosshair_change (true);
2705 break;
2707 case F_ToggleRubberBandMode:
2708 notify_crosshair_change (false);
2709 TOGGLE_FLAG (RUBBERBANDFLAG, PCB);
2710 notify_crosshair_change (true);
2711 break;
2713 case F_ToggleStartDirection:
2714 notify_crosshair_change (false);
2715 TOGGLE_FLAG (SWAPSTARTDIRFLAG, PCB);
2716 notify_crosshair_change (true);
2717 break;
2719 case F_ToggleUniqueNames:
2720 TOGGLE_FLAG (UNIQUENAMEFLAG, PCB);
2721 break;
2723 case F_ToggleSnapPin:
2724 notify_crosshair_change (false);
2725 TOGGLE_FLAG (SNAPPINFLAG, PCB);
2726 notify_crosshair_change (true);
2727 break;
2729 case F_ToggleLocalRef:
2730 TOGGLE_FLAG (LOCALREFFLAG, PCB);
2731 break;
2733 case F_ToggleThindraw:
2734 TOGGLE_FLAG (THINDRAWFLAG, PCB);
2735 Redraw ();
2736 break;
2738 case F_ToggleThindrawPoly:
2739 TOGGLE_FLAG (THINDRAWPOLYFLAG, PCB);
2740 Redraw ();
2741 break;
2743 case F_ToggleLockNames:
2744 TOGGLE_FLAG (LOCKNAMESFLAG, PCB);
2745 CLEAR_FLAG (ONLYNAMESFLAG, PCB);
2746 break;
2748 case F_ToggleOnlyNames:
2749 TOGGLE_FLAG (ONLYNAMESFLAG, PCB);
2750 CLEAR_FLAG (LOCKNAMESFLAG, PCB);
2751 break;
2753 case F_ToggleHideNames:
2754 TOGGLE_FLAG (HIDENAMESFLAG, PCB);
2755 Redraw ();
2756 break;
2758 case F_ToggleShowDRC:
2759 TOGGLE_FLAG (SHOWDRCFLAG, PCB);
2760 break;
2762 case F_ToggleLiveRoute:
2763 TOGGLE_FLAG (LIVEROUTEFLAG, PCB);
2764 break;
2766 case F_ToggleAutoDRC:
2767 notify_crosshair_change (false);
2768 TOGGLE_FLAG (AUTODRCFLAG, PCB);
2769 if (TEST_FLAG (AUTODRCFLAG, PCB) && Settings.Mode == LINE_MODE)
2771 if (ResetConnections (true))
2773 IncrementUndoSerialNumber ();
2774 Draw ();
2776 if (Crosshair.AttachedLine.State != STATE_FIRST)
2777 LookupConnection (Crosshair.AttachedLine.Point1.X,
2778 Crosshair.AttachedLine.Point1.Y, true, 1,
2779 FOUNDFLAG);
2781 notify_crosshair_change (true);
2782 break;
2784 case F_ToggleCheckPlanes:
2785 TOGGLE_FLAG (CHECKPLANESFLAG, PCB);
2786 Redraw ();
2787 break;
2789 case F_ToggleOrthoMove:
2790 TOGGLE_FLAG (ORTHOMOVEFLAG, PCB);
2791 break;
2793 case F_ToggleName:
2794 TOGGLE_FLAG (SHOWNUMBERFLAG, PCB);
2795 Redraw ();
2796 break;
2798 case F_ToggleMask:
2799 TOGGLE_FLAG (SHOWMASKFLAG, PCB);
2800 Redraw ();
2801 break;
2803 case F_ToggleClearLine:
2804 TOGGLE_FLAG (CLEARNEWFLAG, PCB);
2805 break;
2807 case F_ToggleFullPoly:
2808 TOGGLE_FLAG (NEWFULLPOLYFLAG, PCB);
2809 break;
2811 /* shift grid alignment */
2812 case F_ToggleGrid:
2814 Coord oldGrid = PCB->Grid;
2816 PCB->Grid = 1;
2817 if (MoveCrosshairAbsolute (Crosshair.X, Crosshair.Y))
2818 notify_crosshair_change (true); /* first notify was in MoveCrosshairAbs */
2819 SetGrid (oldGrid, true);
2821 break;
2823 /* toggle displaying of the grid */
2824 case F_Grid:
2825 Settings.DrawGrid = !Settings.DrawGrid;
2826 Redraw ();
2827 break;
2829 /* display the pinout of an element */
2830 case F_Pinout:
2832 ElementType *element;
2833 void *ptrtmp;
2834 Coord x, y;
2836 gui->get_coords (_("Click on an element"), &x, &y);
2837 if ((SearchScreen
2838 (x, y, ELEMENT_TYPE, &ptrtmp,
2839 &ptrtmp, &ptrtmp)) != NO_TYPE)
2841 element = (ElementType *) ptrtmp;
2842 gui->show_item (element);
2844 break;
2847 /* toggle displaying of pin/pad/via names */
2848 case F_PinOrPadName:
2850 void *ptr1, *ptr2, *ptr3;
2852 switch (SearchScreen (Crosshair.X, Crosshair.Y,
2853 ELEMENT_TYPE | PIN_TYPE | PAD_TYPE |
2854 VIA_TYPE, (void **) &ptr1, (void **) &ptr2,
2855 (void **) &ptr3))
2857 case ELEMENT_TYPE:
2858 PIN_LOOP ((ElementType *) ptr1);
2860 if (TEST_FLAG (DISPLAYNAMEFLAG, pin))
2861 ErasePinName (pin);
2862 else
2863 DrawPinName (pin);
2864 AddObjectToFlagUndoList (PIN_TYPE, ptr1, pin, pin);
2865 TOGGLE_FLAG (DISPLAYNAMEFLAG, pin);
2867 END_LOOP;
2868 PAD_LOOP ((ElementType *) ptr1);
2870 if (TEST_FLAG (DISPLAYNAMEFLAG, pad))
2871 ErasePadName (pad);
2872 else
2873 DrawPadName (pad);
2874 AddObjectToFlagUndoList (PAD_TYPE, ptr1, pad, pad);
2875 TOGGLE_FLAG (DISPLAYNAMEFLAG, pad);
2877 END_LOOP;
2878 SetChangedFlag (true);
2879 IncrementUndoSerialNumber ();
2880 Draw ();
2881 break;
2883 case PIN_TYPE:
2884 if (TEST_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2))
2885 ErasePinName ((PinType *) ptr2);
2886 else
2887 DrawPinName ((PinType *) ptr2);
2888 AddObjectToFlagUndoList (PIN_TYPE, ptr1, ptr2, ptr3);
2889 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2);
2890 SetChangedFlag (true);
2891 IncrementUndoSerialNumber ();
2892 Draw ();
2893 break;
2895 case PAD_TYPE:
2896 if (TEST_FLAG (DISPLAYNAMEFLAG, (PadType *) ptr2))
2897 ErasePadName ((PadType *) ptr2);
2898 else
2899 DrawPadName ((PadType *) ptr2);
2900 AddObjectToFlagUndoList (PAD_TYPE, ptr1, ptr2, ptr3);
2901 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PadType *) ptr2);
2902 SetChangedFlag (true);
2903 IncrementUndoSerialNumber ();
2904 Draw ();
2905 break;
2906 case VIA_TYPE:
2907 if (TEST_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2))
2908 EraseViaName ((PinType *) ptr2);
2909 else
2910 DrawViaName ((PinType *) ptr2);
2911 AddObjectToFlagUndoList (VIA_TYPE, ptr1, ptr2, ptr3);
2912 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2);
2913 SetChangedFlag (true);
2914 IncrementUndoSerialNumber ();
2915 Draw ();
2916 break;
2918 break;
2920 default:
2921 err = 1;
2924 else if (function && str_dir)
2926 switch (GetFunctionID (function))
2928 case F_ToggleGrid:
2929 if (argc > 2)
2931 PCB->GridOffsetX = GetValue (argv[1], NULL, NULL);
2932 PCB->GridOffsetY = GetValue (argv[2], NULL, NULL);
2933 if (Settings.DrawGrid)
2934 Redraw ();
2936 break;
2938 default:
2939 err = 1;
2940 break;
2944 if (!err)
2945 return 0;
2947 AFAIL (display);
2950 /* --------------------------------------------------------------------------- */
2952 static const char mode_syntax[] =
2953 "Mode(Arc|Arrow|Copy|InsertPoint|Line|Lock|Move|None|PasteBuffer)\n"
2954 "Mode(Polygon|Rectangle|Remove|Rotate|Text|Thermal|Via)\n"
2955 "Mode(Notify|Release|Cancel|Stroke)\n"
2956 "Mode(Save|Restore)";
2958 static const char mode_help[] = "Change or use the tool mode.";
2960 /* %start-doc actions Mode
2962 @table @code
2964 @item Arc
2965 @itemx Arrow
2966 @itemx Copy
2967 @itemx InsertPoint
2968 @itemx Line
2969 @itemx Lock
2970 @itemx Move
2971 @itemx None
2972 @itemx PasteBuffer
2973 @itemx Polygon
2974 @itemx Rectangle
2975 @itemx Remove
2976 @itemx Rotate
2977 @itemx Text
2978 @itemx Thermal
2979 @itemx Via
2980 Select the indicated tool.
2982 @item Notify
2983 Called when you press the mouse button, or move the mouse.
2985 @item Release
2986 Called when you release the mouse button.
2988 @item Cancel
2989 Cancels any pending tool activity, allowing you to restart elsewhere.
2990 For example, this allows you to start a new line rather than attach a
2991 line to the previous line.
2993 @item Escape
2994 Similar to Cancel but calling this action a second time will return
2995 to the Arrow tool.
2997 @item Stroke
2998 If your @code{pcb} was built with libstroke, this invokes the stroke
2999 input method. If not, this will restart a drawing mode if you were
3000 drawing, else it will select objects.
3002 @item Save
3003 Remembers the current tool.
3005 @item Restore
3006 Restores the tool to the last saved tool.
3008 @end table
3010 %end-doc */
3012 static int
3013 ActionMode (int argc, char **argv, Coord x, Coord y)
3015 char *function = ARG (0);
3017 if (function)
3019 Note.X = Crosshair.X;
3020 Note.Y = Crosshair.Y;
3021 notify_crosshair_change (false);
3022 switch (GetFunctionID (function))
3024 case F_Arc:
3025 SetMode (ARC_MODE);
3026 break;
3027 case F_Arrow:
3028 SetMode (ARROW_MODE);
3029 break;
3030 case F_Copy:
3031 SetMode (COPY_MODE);
3032 break;
3033 case F_InsertPoint:
3034 SetMode (INSERTPOINT_MODE);
3035 break;
3036 case F_Line:
3037 SetMode (LINE_MODE);
3038 break;
3039 case F_Lock:
3040 SetMode (LOCK_MODE);
3041 break;
3042 case F_Move:
3043 SetMode (MOVE_MODE);
3044 break;
3045 case F_None:
3046 SetMode (NO_MODE);
3047 break;
3048 case F_Cancel:
3050 int saved_mode = Settings.Mode;
3051 SetMode (NO_MODE);
3052 SetMode (saved_mode);
3054 break;
3055 case F_Escape:
3057 switch (Settings.Mode)
3059 case VIA_MODE:
3060 case PASTEBUFFER_MODE:
3061 case TEXT_MODE:
3062 case ROTATE_MODE:
3063 case REMOVE_MODE:
3064 case MOVE_MODE:
3065 case COPY_MODE:
3066 case INSERTPOINT_MODE:
3067 case RUBBERBANDMOVE_MODE:
3068 case THERMAL_MODE:
3069 case LOCK_MODE:
3070 SetMode (NO_MODE);
3071 SetMode (ARROW_MODE);
3072 break;
3074 case LINE_MODE:
3075 if (Crosshair.AttachedLine.State == STATE_FIRST)
3076 SetMode (ARROW_MODE);
3077 else
3079 SetMode (NO_MODE);
3080 SetMode (LINE_MODE);
3082 break;
3084 case RECTANGLE_MODE:
3085 if (Crosshair.AttachedBox.State == STATE_FIRST)
3086 SetMode (ARROW_MODE);
3087 else
3089 SetMode (NO_MODE);
3090 SetMode (RECTANGLE_MODE);
3092 break;
3094 case POLYGON_MODE:
3095 if (Crosshair.AttachedLine.State == STATE_FIRST)
3096 SetMode (ARROW_MODE);
3097 else
3099 SetMode (NO_MODE);
3100 SetMode (POLYGON_MODE);
3102 break;
3104 case POLYGONHOLE_MODE:
3105 if (Crosshair.AttachedLine.State == STATE_FIRST)
3106 SetMode (ARROW_MODE);
3107 else
3109 SetMode (NO_MODE);
3110 SetMode (POLYGONHOLE_MODE);
3112 break;
3114 case ARC_MODE:
3115 if (Crosshair.AttachedBox.State == STATE_FIRST)
3116 SetMode (ARROW_MODE);
3117 else
3119 SetMode (NO_MODE);
3120 SetMode (ARC_MODE);
3122 break;
3124 case ARROW_MODE:
3125 break;
3127 default:
3128 break;
3131 break;
3133 case F_Notify:
3134 NotifyMode ();
3135 break;
3136 case F_PasteBuffer:
3137 SetMode (PASTEBUFFER_MODE);
3138 break;
3139 case F_Polygon:
3140 SetMode (POLYGON_MODE);
3141 break;
3142 case F_PolygonHole:
3143 SetMode (POLYGONHOLE_MODE);
3144 break;
3145 #ifndef HAVE_LIBSTROKE
3146 case F_Release:
3147 ReleaseMode ();
3148 break;
3149 #else
3150 case F_Release:
3151 if (mid_stroke)
3152 FinishStroke ();
3153 else
3154 ReleaseMode ();
3155 break;
3156 #endif
3157 case F_Remove:
3158 SetMode (REMOVE_MODE);
3159 break;
3160 case F_Rectangle:
3161 SetMode (RECTANGLE_MODE);
3162 break;
3163 case F_Rotate:
3164 SetMode (ROTATE_MODE);
3165 break;
3166 case F_Stroke:
3167 #ifdef HAVE_LIBSTROKE
3168 mid_stroke = true;
3169 StrokeBox.X1 = Crosshair.X;
3170 StrokeBox.Y1 = Crosshair.Y;
3171 break;
3172 #else
3173 /* Handle middle mouse button restarts of drawing mode. If not in
3174 | a drawing mode, middle mouse button will select objects.
3176 if (Settings.Mode == LINE_MODE
3177 && Crosshair.AttachedLine.State != STATE_FIRST)
3179 SetMode (LINE_MODE);
3181 else if (Settings.Mode == ARC_MODE
3182 && Crosshair.AttachedBox.State != STATE_FIRST)
3183 SetMode (ARC_MODE);
3184 else if (Settings.Mode == RECTANGLE_MODE
3185 && Crosshair.AttachedBox.State != STATE_FIRST)
3186 SetMode (RECTANGLE_MODE);
3187 else if (Settings.Mode == POLYGON_MODE
3188 && Crosshair.AttachedLine.State != STATE_FIRST)
3189 SetMode (POLYGON_MODE);
3190 else
3192 SaveMode ();
3193 saved_mode = true;
3194 SetMode (ARROW_MODE);
3195 NotifyMode ();
3197 break;
3198 #endif
3199 case F_Text:
3200 SetMode (TEXT_MODE);
3201 break;
3202 case F_Thermal:
3203 SetMode (THERMAL_MODE);
3204 break;
3205 case F_Via:
3206 SetMode (VIA_MODE);
3207 break;
3209 case F_Restore: /* restore the last saved mode */
3210 RestoreMode ();
3211 break;
3213 case F_Save: /* save currently selected mode */
3214 SaveMode ();
3215 break;
3217 notify_crosshair_change (true);
3218 return 0;
3221 AFAIL (mode);
3224 /* --------------------------------------------------------------------------- */
3226 static const char removeselected_syntax[] = "RemoveSelected()";
3228 static const char removeselected_help[] = "Removes any selected objects.";
3230 /* %start-doc actions RemoveSelected
3232 %end-doc */
3234 static int
3235 ActionRemoveSelected (int argc, char **argv, Coord x, Coord y)
3237 if (RemoveSelected ())
3238 SetChangedFlag (true);
3239 return 0;
3242 /* --------------------------------------------------------------------------- */
3244 static const char renumber_syntax[] = "Renumber()\n"
3245 "Renumber(filename)";
3247 static const char renumber_help[] =
3248 "Renumber all elements. The changes will be recorded to filename\n"
3249 "for use in backannotating these changes to the schematic.";
3251 /* %start-doc actions Renumber
3253 %end-doc */
3255 static int
3256 ActionRenumber (int argc, char **argv, Coord x, Coord y)
3258 bool changed = false;
3259 ElementType **element_list;
3260 ElementType **locked_element_list;
3261 unsigned int i, j, k, cnt, lock_cnt;
3262 unsigned int tmpi;
3263 size_t sz;
3264 char *tmps;
3265 char *name;
3266 FILE *out;
3267 static char * default_file = NULL;
3268 size_t cnt_list_sz = 100;
3269 struct _cnt_list
3271 char *name;
3272 unsigned int cnt;
3273 } *cnt_list;
3274 char **was, **is, *pin;
3275 unsigned int c_cnt = 0;
3276 int unique, ok;
3277 int free_name = 0;
3279 if (argc < 1)
3282 * We deal with the case where name already exists in this
3283 * function so the GUI doesn't need to deal with it
3285 name = gui->fileselect (_("Save Renumber Annotation File As ..."),
3286 _("Choose a file to record the renumbering to.\n"
3287 "This file may be used to back annotate the\n"
3288 "change to the schematics.\n"),
3289 default_file, ".eco", "eco",
3292 free_name = 1;
3294 else
3295 name = argv[0];
3297 if (default_file)
3299 free (default_file);
3300 default_file = NULL;
3303 if (name && *name)
3305 default_file = strdup (name);
3308 if ((out = fopen (name, "r")))
3310 fclose (out);
3311 if (!gui->confirm_dialog (_("File exists! Ok to overwrite?"), 0))
3313 if (free_name && name)
3314 free (name);
3315 return 0;
3319 if ((out = fopen (name, "w")) == NULL)
3321 Message (_("Could not open %s\n"), name);
3322 if (free_name && name)
3323 free (name);
3324 return 1;
3327 if (free_name && name)
3328 free (name);
3330 fprintf (out, "*COMMENT* PCB Annotation File\n");
3331 fprintf (out, "*FILEVERSION* 20061031\n");
3334 * Make a first pass through all of the elements and sort them out
3335 * by location on the board. While here we also collect a list of
3336 * locked elements.
3338 * We'll actually renumber things in the 2nd pass.
3340 element_list = (ElementType **)calloc (PCB->Data->ElementN, sizeof (ElementType *));
3341 locked_element_list = (ElementType **)calloc (PCB->Data->ElementN, sizeof (ElementType *));
3342 was = (char **)calloc (PCB->Data->ElementN, sizeof (char *));
3343 is = (char **)calloc (PCB->Data->ElementN, sizeof (char *));
3344 if (element_list == NULL || locked_element_list == NULL || was == NULL
3345 || is == NULL)
3347 fprintf (stderr, "calloc() failed in %s\n", __FUNCTION__);
3348 exit (1);
3352 cnt = 0;
3353 lock_cnt = 0;
3354 ELEMENT_LOOP (PCB->Data);
3356 if (TEST_FLAG (LOCKFLAG, element->Name) || TEST_FLAG (LOCKFLAG, element))
3359 * add to the list of locked elements which we won't try to
3360 * renumber and whose reference designators are now reserved.
3362 pcb_fprintf (out,
3363 "*WARN* Element \"%s\" at %$md is locked and will not be renumbered.\n",
3364 UNKNOWN (NAMEONPCB_NAME (element)), element->MarkX, element->MarkY);
3365 locked_element_list[lock_cnt] = element;
3366 lock_cnt++;
3369 else
3371 /* count of devices which will be renumbered */
3372 cnt++;
3374 /* search for correct position in the list */
3375 i = 0;
3376 while (element_list[i] && element->MarkY > element_list[i]->MarkY)
3377 i++;
3380 * We have found the position where we have the first element that
3381 * has the same Y value or a lower Y value. Now move forward if
3382 * needed through the X values
3384 while (element_list[i]
3385 && element->MarkY == element_list[i]->MarkY
3386 && element->MarkX > element_list[i]->MarkX)
3387 i++;
3389 for (j = cnt - 1; j > i; j--)
3391 element_list[j] = element_list[j - 1];
3393 element_list[i] = element;
3396 END_LOOP;
3400 * Now that the elements are sorted by board position, we go through
3401 * and renumber them.
3405 * turn off the flag which requires unique names so it doesn't get
3406 * in our way. When we're done with the renumber we will have unique
3407 * names.
3409 unique = TEST_FLAG (UNIQUENAMEFLAG, PCB);
3410 CLEAR_FLAG (UNIQUENAMEFLAG, PCB);
3412 cnt_list = (struct _cnt_list *)calloc (cnt_list_sz, sizeof (struct _cnt_list));
3413 for (i = 0; i < cnt; i++)
3415 /* If there is no refdes, maybe just spit out a warning */
3416 if (NAMEONPCB_NAME (element_list[i]))
3418 /* figure out the prefix */
3419 tmps = strdup (NAMEONPCB_NAME (element_list[i]));
3420 j = 0;
3421 while (tmps[j] && (tmps[j] < '0' || tmps[j] > '9')
3422 && tmps[j] != '?')
3423 j++;
3424 tmps[j] = '\0';
3426 /* check the counter for this prefix */
3427 for (j = 0;
3428 cnt_list[j].name && (strcmp (cnt_list[j].name, tmps) != 0)
3429 && j < cnt_list_sz; j++);
3431 /* grow the list if needed */
3432 if (j == cnt_list_sz)
3434 cnt_list_sz += 100;
3435 cnt_list = (struct _cnt_list *)realloc (cnt_list, cnt_list_sz);
3436 if (cnt_list == NULL)
3438 fprintf (stderr, "realloc failed() in %s\n", __FUNCTION__);
3439 exit (1);
3441 /* zero out the memory that we added */
3442 for (tmpi = j; tmpi < cnt_list_sz; tmpi++)
3444 cnt_list[tmpi].name = NULL;
3445 cnt_list[tmpi].cnt = 0;
3450 * start a new counter if we don't have a counter for this
3451 * prefix
3453 if (!cnt_list[j].name)
3455 cnt_list[j].name = strdup (tmps);
3456 cnt_list[j].cnt = 0;
3460 * check to see if the new refdes is already used by a
3461 * locked element
3465 ok = 1;
3466 cnt_list[j].cnt++;
3467 free (tmps);
3469 /* space for the prefix plus 1 digit plus the '\0' */
3470 sz = strlen (cnt_list[j].name) + 2;
3472 /* and 1 more per extra digit needed to hold the number */
3473 tmpi = cnt_list[j].cnt;
3474 while (tmpi > 10)
3476 sz++;
3477 tmpi = tmpi / 10;
3479 tmps = (char *)malloc (sz * sizeof (char));
3480 sprintf (tmps, "%s%d", cnt_list[j].name, cnt_list[j].cnt);
3483 * now compare to the list of reserved (by locked
3484 * elements) names
3486 for (k = 0; k < lock_cnt; k++)
3488 if (strcmp
3489 (UNKNOWN (NAMEONPCB_NAME (locked_element_list[k])),
3490 tmps) == 0)
3492 ok = 0;
3493 break;
3498 while (!ok);
3500 if (strcmp (tmps, NAMEONPCB_NAME (element_list[i])) != 0)
3502 fprintf (out, "*RENAME* \"%s\" \"%s\"\n",
3503 NAMEONPCB_NAME (element_list[i]), tmps);
3505 /* add this rename to our table of renames so we can update the netlist */
3506 was[c_cnt] = strdup (NAMEONPCB_NAME (element_list[i]));
3507 is[c_cnt] = strdup (tmps);
3508 c_cnt++;
3510 AddObjectToChangeNameUndoList (ELEMENT_TYPE, NULL, NULL,
3511 element_list[i],
3512 NAMEONPCB_NAME (element_list
3513 [i]));
3515 ChangeObjectName (ELEMENT_TYPE, element_list[i], NULL, NULL,
3516 tmps);
3517 changed = true;
3519 /* we don't free tmps in this case because it is used */
3521 else
3522 free (tmps);
3524 else
3526 pcb_fprintf (out, "*WARN* Element at %$md has no name.\n",
3527 element_list[i]->MarkX, element_list[i]->MarkY);
3532 fclose (out);
3534 /* restore the unique flag setting */
3535 if (unique)
3536 SET_FLAG (UNIQUENAMEFLAG, PCB);
3538 if (changed)
3541 /* update the netlist */
3542 AddNetlistLibToUndoList (&(PCB->NetlistLib));
3544 /* iterate over each net */
3545 for (i = 0; i < PCB->NetlistLib.MenuN; i++)
3548 /* iterate over each pin on the net */
3549 for (j = 0; j < PCB->NetlistLib.Menu[i].EntryN; j++)
3552 /* figure out the pin number part from strings like U3-21 */
3553 tmps = strdup (PCB->NetlistLib.Menu[i].Entry[j].ListEntry);
3554 for (k = 0; tmps[k] && tmps[k] != '-'; k++);
3555 tmps[k] = '\0';
3556 pin = tmps + k + 1;
3558 /* iterate over the list of changed reference designators */
3559 for (k = 0; k < c_cnt; k++)
3562 * if the pin needs to change, change it and quit
3563 * searching in the list.
3565 if (strcmp (tmps, was[k]) == 0)
3567 free (PCB->NetlistLib.Menu[i].Entry[j].ListEntry);
3568 PCB->NetlistLib.Menu[i].Entry[j].ListEntry =
3569 (char *)malloc ((strlen (is[k]) + strlen (pin) +
3570 2) * sizeof (char));
3571 sprintf (PCB->NetlistLib.Menu[i].Entry[j].ListEntry,
3572 "%s-%s", is[k], pin);
3573 k = c_cnt;
3577 free (tmps);
3580 for (k = 0; k < c_cnt; k++)
3582 free (was[k]);
3583 free (is[k]);
3586 NetlistChanged (0);
3587 IncrementUndoSerialNumber ();
3588 SetChangedFlag (true);
3591 free (locked_element_list);
3592 free (element_list);
3593 free (cnt_list);
3594 return 0;
3598 /* --------------------------------------------------------------------------- */
3600 static const char ripup_syntax[] = "RipUp(All|Selected|Element)";
3602 static const char ripup_help[] =
3603 "Ripup auto-routed tracks, or convert an element to parts.";
3605 /* %start-doc actions RipUp
3607 @table @code
3609 @item All
3610 Removes all lines and vias which were created by the autorouter.
3612 @item Selected
3613 Removes all selected lines and vias which were created by the
3614 autorouter.
3616 @item Element
3617 Converts the element under the cursor to parts (vias and lines). Note
3618 that this uses the highest numbered paste buffer.
3620 @end table
3622 %end-doc */
3624 static int
3625 ActionRipUp (int argc, char **argv, Coord x, Coord y)
3627 char *function = ARG (0);
3628 bool changed = false;
3630 if (function)
3632 switch (GetFunctionID (function))
3634 case F_All:
3635 ALLLINE_LOOP (PCB->Data);
3637 if (TEST_FLAG (AUTOFLAG, line) && !TEST_FLAG (LOCKFLAG, line))
3639 RemoveObject (LINE_TYPE, layer, line, line);
3640 changed = true;
3643 ENDALL_LOOP;
3644 ALLARC_LOOP (PCB->Data);
3646 if (TEST_FLAG (AUTOFLAG, arc) && !TEST_FLAG (LOCKFLAG, arc))
3648 RemoveObject (ARC_TYPE, layer, arc, arc);
3649 changed = true;
3652 ENDALL_LOOP;
3653 VIA_LOOP (PCB->Data);
3655 if (TEST_FLAG (AUTOFLAG, via) && !TEST_FLAG (LOCKFLAG, via))
3657 RemoveObject (VIA_TYPE, via, via, via);
3658 changed = true;
3661 END_LOOP;
3663 if (changed)
3665 IncrementUndoSerialNumber ();
3666 SetChangedFlag (true);
3668 break;
3669 case F_Selected:
3670 VISIBLELINE_LOOP (PCB->Data);
3672 if (TEST_FLAGS (AUTOFLAG | SELECTEDFLAG, line)
3673 && !TEST_FLAG (LOCKFLAG, line))
3675 RemoveObject (LINE_TYPE, layer, line, line);
3676 changed = true;
3679 ENDALL_LOOP;
3680 if (PCB->ViaOn)
3681 VIA_LOOP (PCB->Data);
3683 if (TEST_FLAGS (AUTOFLAG | SELECTEDFLAG, via)
3684 && !TEST_FLAG (LOCKFLAG, via))
3686 RemoveObject (VIA_TYPE, via, via, via);
3687 changed = true;
3690 END_LOOP;
3691 if (changed)
3693 IncrementUndoSerialNumber ();
3694 SetChangedFlag (true);
3696 break;
3697 case F_Element:
3699 void *ptr1, *ptr2, *ptr3;
3701 if (SearchScreen (Crosshair.X, Crosshair.Y, ELEMENT_TYPE,
3702 &ptr1, &ptr2, &ptr3) != NO_TYPE)
3704 Note.Buffer = Settings.BufferNumber;
3705 SetBufferNumber (MAX_BUFFER - 1);
3706 ClearBuffer (PASTEBUFFER);
3707 CopyObjectToBuffer (PASTEBUFFER->Data, PCB->Data,
3708 ELEMENT_TYPE, ptr1, ptr2, ptr3);
3709 SmashBufferElement (PASTEBUFFER);
3710 PASTEBUFFER->X = 0;
3711 PASTEBUFFER->Y = 0;
3712 SaveUndoSerialNumber ();
3713 EraseObject (ELEMENT_TYPE, ptr1, ptr1);
3714 MoveObjectToRemoveUndoList (ELEMENT_TYPE, ptr1, ptr2, ptr3);
3715 RestoreUndoSerialNumber ();
3716 CopyPastebufferToLayout (0, 0);
3717 SetBufferNumber (Note.Buffer);
3718 SetChangedFlag (true);
3721 break;
3724 return 0;
3727 /* --------------------------------------------------------------------------- */
3729 static const char addrats_syntax[] = "AddRats(AllRats|SelectedRats|Close)";
3731 static const char addrats_help[] = "Add one or more rat lines to the board.";
3733 /* %start-doc actions AddRats
3735 @table @code
3737 @item AllRats
3738 Create rat lines for all loaded nets that aren't already connected on
3739 with copper.
3741 @item SelectedRats
3742 Similarly, but only add rat lines for nets connected to selected pins
3743 and pads.
3745 @item Close
3746 Selects the shortest unselected rat on the board.
3748 @end table
3750 %end-doc */
3752 static int
3753 ActionAddRats (int argc, char **argv, Coord x, Coord y)
3755 char *function = ARG (0);
3756 RatType *shorty;
3757 float len, small;
3759 if (function)
3761 if (Settings.RatWarn)
3762 ClearWarnings ();
3763 switch (GetFunctionID (function))
3765 case F_AllRats:
3766 if (AddAllRats (false, NULL))
3767 SetChangedFlag (true);
3768 break;
3769 case F_SelectedRats:
3770 case F_Selected:
3771 if (AddAllRats (true, NULL))
3772 SetChangedFlag (true);
3773 break;
3774 case F_Close:
3775 small = SQUARE (MAX_COORD);
3776 shorty = NULL;
3777 RAT_LOOP (PCB->Data);
3779 if (TEST_FLAG (SELECTEDFLAG, line))
3780 continue;
3781 len = SQUARE (line->Point1.X - line->Point2.X) +
3782 SQUARE (line->Point1.Y - line->Point2.Y);
3783 if (len < small)
3785 small = len;
3786 shorty = line;
3789 END_LOOP;
3790 if (shorty)
3792 AddObjectToFlagUndoList (RATLINE_TYPE, shorty, shorty, shorty);
3793 SET_FLAG (SELECTEDFLAG, shorty);
3794 DrawRat (shorty);
3795 Draw ();
3796 CenterDisplay ((shorty->Point2.X + shorty->Point1.X) / 2,
3797 (shorty->Point2.Y + shorty->Point1.Y) / 2);
3799 break;
3802 return 0;
3805 /* --------------------------------------------------------------------------- */
3807 static const char delete_syntax[] =
3808 "Delete(Object|Selected)\n"
3809 "Delete(AllRats|SelectedRats)";
3811 static const char delete_help[] = "Delete stuff.";
3813 /* %start-doc actions Delete
3815 %end-doc */
3817 static int
3818 ActionDelete (int argc, char **argv, Coord x, Coord y)
3820 char *function = ARG (0);
3821 int id = GetFunctionID (function);
3823 Note.X = Crosshair.X;
3824 Note.Y = Crosshair.Y;
3826 if (id == -1) /* no arg */
3828 if (RemoveSelected() == false)
3829 id = F_Object;
3832 switch (id)
3834 case F_Object:
3835 SaveMode();
3836 SetMode(REMOVE_MODE);
3837 NotifyMode();
3838 RestoreMode();
3839 break;
3840 case F_Selected:
3841 RemoveSelected();
3842 break;
3843 case F_AllRats:
3844 if (DeleteRats (false))
3845 SetChangedFlag (true);
3846 break;
3847 case F_SelectedRats:
3848 if (DeleteRats (true))
3849 SetChangedFlag (true);
3850 break;
3853 return 0;
3856 /* --------------------------------------------------------------------------- */
3858 static const char deleterats_syntax[] =
3859 "DeleteRats(AllRats|Selected|SelectedRats)";
3861 static const char deleterats_help[] = "Delete rat lines.";
3863 /* %start-doc actions DeleteRats
3865 %end-doc */
3867 static int
3868 ActionDeleteRats (int argc, char **argv, Coord x, Coord y)
3870 char *function = ARG (0);
3871 if (function)
3873 if (Settings.RatWarn)
3874 ClearWarnings ();
3875 switch (GetFunctionID (function))
3877 case F_AllRats:
3878 if (DeleteRats (false))
3879 SetChangedFlag (true);
3880 break;
3881 case F_SelectedRats:
3882 case F_Selected:
3883 if (DeleteRats (true))
3884 SetChangedFlag (true);
3885 break;
3888 return 0;
3891 /* --------------------------------------------------------------------------- */
3893 static const char autoplace_syntax[] = "AutoPlaceSelected()";
3895 static const char autoplace_help[] = "Auto-place selected components.";
3897 /* %start-doc actions AutoPlaceSelected
3899 Attempts to re-arrange the selected components such that the nets
3900 connecting them are minimized. Note that you cannot undo this.
3902 %end-doc */
3904 static int
3905 ActionAutoPlaceSelected (int argc, char **argv, Coord x, Coord y)
3907 hid_action("Busy");
3908 if (gui->confirm_dialog (_("Auto-placement can NOT be undone.\n"
3909 "Do you want to continue anyway?\n"), 0))
3911 if (AutoPlaceSelected ())
3912 SetChangedFlag (true);
3914 return 0;
3917 /* --------------------------------------------------------------------------- */
3919 static const char autoroute_syntax[] = "AutoRoute(AllRats|SelectedRats)";
3921 static const char autoroute_help[] = "Auto-route some or all rat lines.";
3923 /* %start-doc actions AutoRoute
3925 @table @code
3927 @item AllRats
3928 Attempt to autoroute all rats.
3930 @item SelectedRats
3931 Attempt to autoroute the selected rats.
3933 @end table
3935 Before autorouting, it's important to set up a few things. First,
3936 make sure any layers you aren't using are disabled, else the
3937 autorouter may use them. Next, make sure the current line and via
3938 styles are set accordingly. Last, make sure "new lines clear
3939 polygons" is set, in case you eventually want to add a copper pour.
3941 Autorouting takes a while. During this time, the program may not be
3942 responsive.
3944 %end-doc */
3946 static int
3947 ActionAutoRoute (int argc, char **argv, Coord x, Coord y)
3949 char *function = ARG (0);
3950 hid_action("Busy");
3951 if (function) /* one parameter */
3953 switch (GetFunctionID (function))
3955 case F_AllRats:
3956 if (AutoRoute (false))
3957 SetChangedFlag (true);
3958 break;
3959 case F_SelectedRats:
3960 case F_Selected:
3961 if (AutoRoute (true))
3962 SetChangedFlag (true);
3963 break;
3966 return 0;
3969 /* --------------------------------------------------------------------------- */
3971 static const char markcrosshair_syntax[] =
3972 "MarkCrosshair()\n"
3973 "MarkCrosshair(Center)";
3975 static const char markcrosshair_help[] = "Set/Reset the Crosshair mark.";
3977 /* %start-doc actions MarkCrosshair
3979 The ``mark'' is a small X-shaped target on the display which is
3980 treated like a second origin (the normal origin is the upper let
3981 corner of the board). The GUI will display a second set of
3982 coordinates for this mark, which tells you how far you are from it.
3984 If no argument is given, the mark is toggled - disabled if it was
3985 enabled, or enabled at the current cursor position of disabled. If
3986 the @code{Center} argument is given, the mark is moved to the current
3987 cursor location.
3989 %end-doc */
3991 static int
3992 ActionMarkCrosshair (int argc, char **argv, Coord x, Coord y)
3994 char *function = ARG (0);
3995 if (!function || !*function)
3997 if (Marked.status)
3999 notify_mark_change (false);
4000 Marked.status = false;
4001 notify_mark_change (true);
4003 else
4005 notify_mark_change (false);
4006 Marked.status = false;
4007 Marked.status = true;
4008 Marked.X = Crosshair.X;
4009 Marked.Y = Crosshair.Y;
4010 notify_mark_change (true);
4013 else if (GetFunctionID (function) == F_Center)
4015 notify_mark_change (false);
4016 Marked.status = true;
4017 Marked.X = Crosshair.X;
4018 Marked.Y = Crosshair.Y;
4019 notify_mark_change (true);
4021 return 0;
4024 /* --------------------------------------------------------------------------- */
4026 static const char changesize_syntax[] =
4027 "ChangeSize(Object, delta)\n"
4028 "ChangeSize(SelectedObjects|Selected, delta)\n"
4029 "ChangeSize(SelectedLines|SelectedPins|SelectedVias, delta)\n"
4030 "ChangeSize(SelectedPads|SelectedTexts|SelectedNames, delta)\n"
4031 "ChangeSize(SelectedElements, delta)";
4033 static const char changesize_help[] = "Changes the size of objects.";
4035 /* %start-doc actions ChangeSize
4037 For lines and arcs, this changes the width. For pins and vias, this
4038 changes the overall diameter of the copper annulus. For pads, this
4039 changes the width and, indirectly, the length. For texts and names,
4040 this changes the scaling factor. For elements, this changes the width
4041 of the silk layer lines and arcs for this element.
4043 %end-doc */
4045 static int
4046 ActionChangeSize (int argc, char **argv, Coord x, Coord y)
4048 char *function = ARG (0);
4049 char *delta = ARG (1);
4050 char *units = ARG (2);
4051 bool absolute; /* indicates if absolute size is given */
4052 Coord value;
4054 if (function && delta)
4056 value = GetValue (delta, units, &absolute);
4057 if (value == 0)
4058 value = delta[0] == '-' ? -Settings.increments->size
4059 : Settings.increments->size;
4060 switch (GetFunctionID (function))
4062 case F_Object:
4064 int type;
4065 void *ptr1, *ptr2, *ptr3;
4067 if ((type =
4068 SearchScreen (Crosshair.X, Crosshair.Y, CHANGESIZE_TYPES,
4069 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4070 if (TEST_FLAG (LOCKFLAG, (PinType *) ptr2))
4071 Message (_("Sorry, the object is locked\n"));
4072 if (ChangeObjectSize (type, ptr1, ptr2, ptr3, value, absolute))
4073 SetChangedFlag (true);
4074 break;
4077 case F_SelectedVias:
4078 if (ChangeSelectedSize (VIA_TYPE, value, absolute))
4079 SetChangedFlag (true);
4080 break;
4082 case F_SelectedPins:
4083 if (ChangeSelectedSize (PIN_TYPE, value, absolute))
4084 SetChangedFlag (true);
4085 break;
4087 case F_SelectedPads:
4088 if (ChangeSelectedSize (PAD_TYPE, value, absolute))
4089 SetChangedFlag (true);
4090 break;
4092 case F_SelectedArcs:
4093 if (ChangeSelectedSize (ARC_TYPE, value, absolute))
4094 SetChangedFlag (true);
4095 break;
4097 case F_SelectedLines:
4098 if (ChangeSelectedSize (LINE_TYPE, value, absolute))
4099 SetChangedFlag (true);
4100 break;
4102 case F_SelectedTexts:
4103 if (ChangeSelectedSize (TEXT_TYPE, value, absolute))
4104 SetChangedFlag (true);
4105 break;
4107 case F_SelectedNames:
4108 if (ChangeSelectedSize (ELEMENTNAME_TYPE, value, absolute))
4109 SetChangedFlag (true);
4110 break;
4112 case F_SelectedElements:
4113 if (ChangeSelectedSize (ELEMENT_TYPE, value, absolute))
4114 SetChangedFlag (true);
4115 break;
4117 case F_Selected:
4118 case F_SelectedObjects:
4119 if (ChangeSelectedSize (CHANGESIZE_TYPES, value, absolute))
4120 SetChangedFlag (true);
4121 break;
4124 return 0;
4127 /* --------------------------------------------------------------------------- */
4129 static const char changedrillsize_syntax[] =
4130 "ChangeDrillSize(Object, delta)\n"
4131 "ChangeDrillSize(SelectedPins|SelectedVias|Selected|SelectedObjects, delta)";
4133 static const char changedrillsize_help[] =
4134 "Changes the drilling hole size of objects.";
4136 /* %start-doc actions ChangeDrillSize
4138 %end-doc */
4140 static int
4141 ActionChange2ndSize (int argc, char **argv, Coord x, Coord y)
4143 char *function = ARG (0);
4144 char *delta = ARG (1);
4145 char *units = ARG (2);
4146 bool absolute;
4147 Coord value;
4149 if (function && delta)
4151 value = GetValue (delta, units, &absolute);
4152 switch (GetFunctionID (function))
4154 case F_Object:
4156 int type;
4157 void *ptr1, *ptr2, *ptr3;
4159 gui->get_coords (_("Select an Object"), &x, &y);
4160 if ((type =
4161 SearchScreen (x, y, CHANGE2NDSIZE_TYPES,
4162 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4163 if (ChangeObject2ndSize
4164 (type, ptr1, ptr2, ptr3, value, absolute, true))
4165 SetChangedFlag (true);
4166 break;
4169 case F_SelectedVias:
4170 if (ChangeSelected2ndSize (VIA_TYPE, value, absolute))
4171 SetChangedFlag (true);
4172 break;
4174 case F_SelectedPins:
4175 if (ChangeSelected2ndSize (PIN_TYPE, value, absolute))
4176 SetChangedFlag (true);
4177 break;
4178 case F_Selected:
4179 case F_SelectedObjects:
4180 if (ChangeSelected2ndSize (PIN_TYPES, value, absolute))
4181 SetChangedFlag (true);
4182 break;
4185 return 0;
4188 /* --------------------------------------------------------------------------- */
4190 static const char changeclearsize_syntax[] =
4191 "ChangeClearSize(Object, delta)\n"
4192 "ChangeClearSize(SelectedPins|SelectedPads|SelectedVias, delta)\n"
4193 "ChangeClearSize(SelectedLines|SelectedArcs, delta\n"
4194 "ChangeClearSize(Selected|SelectedObjects, delta)";
4196 static const char changeclearsize_help[] =
4197 "Changes the clearance size of objects.";
4199 /* %start-doc actions ChangeClearSize
4201 If the solder mask is currently showing, this action changes the
4202 solder mask clearance. If the mask is not showing, this action
4203 changes the polygon clearance.
4205 %end-doc */
4207 static int
4208 ActionChangeClearSize (int argc, char **argv, Coord x, Coord y)
4210 char *function = ARG (0);
4211 char *delta = ARG (1);
4212 char *units = ARG (2);
4213 bool absolute;
4214 Coord value;
4216 if (function && delta)
4218 value = 2 * GetValue (delta, units, &absolute);
4219 if (value == 0)
4220 value = delta[0] == '-' ? -Settings.increments->clear
4221 : Settings.increments->clear;
4222 switch (GetFunctionID (function))
4224 case F_Object:
4226 int type;
4227 void *ptr1, *ptr2, *ptr3;
4229 gui->get_coords (_("Select an Object"), &x, &y);
4230 if ((type =
4231 SearchScreen (x, y,
4232 CHANGECLEARSIZE_TYPES, &ptr1, &ptr2,
4233 &ptr3)) != NO_TYPE)
4234 if (ChangeObjectClearSize (type, ptr1, ptr2, ptr3, value, absolute))
4235 SetChangedFlag (true);
4236 break;
4238 case F_SelectedVias:
4239 if (ChangeSelectedClearSize (VIA_TYPE, value, absolute))
4240 SetChangedFlag (true);
4241 break;
4242 case F_SelectedPads:
4243 if (ChangeSelectedClearSize (PAD_TYPE, value, absolute))
4244 SetChangedFlag (true);
4245 break;
4246 case F_SelectedPins:
4247 if (ChangeSelectedClearSize (PIN_TYPE, value, absolute))
4248 SetChangedFlag (true);
4249 break;
4250 case F_SelectedLines:
4251 if (ChangeSelectedClearSize (LINE_TYPE, value, absolute))
4252 SetChangedFlag (true);
4253 break;
4254 case F_SelectedArcs:
4255 if (ChangeSelectedClearSize (ARC_TYPE, value, absolute))
4256 SetChangedFlag (true);
4257 break;
4258 case F_Selected:
4259 case F_SelectedObjects:
4260 if (ChangeSelectedClearSize (CHANGECLEARSIZE_TYPES, value, absolute))
4261 SetChangedFlag (true);
4262 break;
4265 return 0;
4268 /* --------------------------------------------------------------------------- */
4270 static const char minmaskgap_syntax[] =
4271 "MinMaskGap(delta)\n"
4272 "MinMaskGap(Selected, delta)";
4274 static const char minmaskgap_help[] =
4275 "Ensures the mask is a minimum distance from pins and pads.";
4277 /* %start-doc actions MinMaskGap
4279 Checks all specified pins and/or pads, and increases the mask if
4280 needed to ensure a minimum distance between the pin or pad edge and
4281 the mask edge.
4283 %end-doc */
4285 static int
4286 ActionMinMaskGap (int argc, char **argv, Coord x, Coord y)
4288 char *function = ARG (0);
4289 char *delta = ARG (1);
4290 char *units = ARG (2);
4291 bool absolute;
4292 Coord value;
4293 int flags;
4295 if (!function)
4296 return 1;
4297 if (strcasecmp (function, "Selected") == 0)
4298 flags = SELECTEDFLAG;
4299 else
4301 units = delta;
4302 delta = function;
4303 flags = 0;
4305 value = 2 * GetValue (delta, units, &absolute);
4307 SaveUndoSerialNumber ();
4308 ELEMENT_LOOP (PCB->Data);
4310 PIN_LOOP (element);
4312 if (!TEST_FLAGS (flags, pin))
4313 continue;
4314 if (pin->Mask < pin->Thickness + value)
4316 ChangeObjectMaskSize (PIN_TYPE, element, pin, 0,
4317 pin->Thickness + value, 1);
4318 RestoreUndoSerialNumber ();
4321 END_LOOP;
4322 PAD_LOOP (element);
4324 if (!TEST_FLAGS (flags, pad))
4325 continue;
4326 if (pad->Mask < pad->Thickness + value)
4328 ChangeObjectMaskSize (PAD_TYPE, element, pad, 0,
4329 pad->Thickness + value, 1);
4330 RestoreUndoSerialNumber ();
4333 END_LOOP;
4335 END_LOOP;
4336 VIA_LOOP (PCB->Data);
4338 if (!TEST_FLAGS (flags, via))
4339 continue;
4340 if (via->Mask && via->Mask < via->Thickness + value)
4342 ChangeObjectMaskSize (VIA_TYPE, via, 0, 0, via->Thickness + value, 1);
4343 RestoreUndoSerialNumber ();
4346 END_LOOP;
4347 RestoreUndoSerialNumber ();
4348 IncrementUndoSerialNumber ();
4349 return 0;
4352 /* --------------------------------------------------------------------------- */
4354 static const char mincleargap_syntax[] =
4355 "MinClearGap(delta)\n"
4356 "MinClearGap(Selected, delta)";
4358 static const char mincleargap_help[] =
4359 "Ensures that polygons are a minimum distance from objects.";
4361 /* %start-doc actions MinClearGap
4363 Checks all specified objects, and increases the polygon clearance if
4364 needed to ensure a minimum distance between their edges and the
4365 polygon edges.
4367 %end-doc */
4369 static int
4370 ActionMinClearGap (int argc, char **argv, Coord x, Coord y)
4372 char *function = ARG (0);
4373 char *delta = ARG (1);
4374 char *units = ARG (2);
4375 bool absolute;
4376 Coord value;
4377 int flags;
4379 if (!function)
4380 return 1;
4381 if (strcasecmp (function, "Selected") == 0)
4382 flags = SELECTEDFLAG;
4383 else
4385 units = delta;
4386 delta = function;
4387 flags = 0;
4389 value = 2 * GetValue (delta, units, &absolute);
4391 SaveUndoSerialNumber ();
4392 ELEMENT_LOOP (PCB->Data);
4394 PIN_LOOP (element);
4396 if (!TEST_FLAGS (flags, pin))
4397 continue;
4398 if (pin->Clearance < value)
4400 ChangeObjectClearSize (PIN_TYPE, element, pin, 0,
4401 value, 1);
4402 RestoreUndoSerialNumber ();
4405 END_LOOP;
4406 PAD_LOOP (element);
4408 if (!TEST_FLAGS (flags, pad))
4409 continue;
4410 if (pad->Clearance < value)
4412 ChangeObjectClearSize (PAD_TYPE, element, pad, 0,
4413 value, 1);
4414 RestoreUndoSerialNumber ();
4417 END_LOOP;
4419 END_LOOP;
4420 VIA_LOOP (PCB->Data);
4422 if (!TEST_FLAGS (flags, via))
4423 continue;
4424 if (via->Clearance < value)
4426 ChangeObjectClearSize (VIA_TYPE, via, 0, 0, value, 1);
4427 RestoreUndoSerialNumber ();
4430 END_LOOP;
4431 ALLLINE_LOOP (PCB->Data);
4433 if (!TEST_FLAGS (flags, line))
4434 continue;
4435 if (line->Clearance < value)
4437 ChangeObjectClearSize (LINE_TYPE, layer, line, 0, value, 1);
4438 RestoreUndoSerialNumber ();
4441 ENDALL_LOOP;
4442 ALLARC_LOOP (PCB->Data);
4444 if (!TEST_FLAGS (flags, arc))
4445 continue;
4446 if (arc->Clearance < value)
4448 ChangeObjectClearSize (ARC_TYPE, layer, arc, 0, value, 1);
4449 RestoreUndoSerialNumber ();
4452 ENDALL_LOOP;
4453 RestoreUndoSerialNumber ();
4454 IncrementUndoSerialNumber ();
4455 return 0;
4458 /* --------------------------------------------------------------------------- */
4460 static const char changepinname_syntax[] =
4461 "ChangePinName(ElementName,PinNumber,PinName)";
4463 static const char changepinname_help[] =
4464 "Sets the name of a specific pin on a specific element.";
4466 /* %start-doc actions ChangePinName
4468 This can be especially useful for annotating pin names from a
4469 schematic to the layout without requiring knowledge of the pcb file
4470 format.
4472 @example
4473 ChangePinName(U3, 7, VCC)
4474 @end example
4476 %end-doc */
4478 static int
4479 ActionChangePinName (int argc, char **argv, Coord x, Coord y)
4481 int changed = 0;
4482 char *refdes, *pinnum, *pinname;
4484 if (argc != 3)
4486 AFAIL (changepinname);
4489 refdes = argv[0];
4490 pinnum = argv[1];
4491 pinname = argv[2];
4493 ELEMENT_LOOP (PCB->Data);
4495 if (NSTRCMP (refdes, NAMEONPCB_NAME (element)) == 0)
4497 PIN_LOOP (element);
4499 if (NSTRCMP (pinnum, pin->Number) == 0)
4501 AddObjectToChangeNameUndoList (PIN_TYPE, NULL, NULL,
4502 pin, pin->Name);
4504 * Note: we can't free() pin->Name first because
4505 * it is used in the undo list
4507 pin->Name = strdup (pinname);
4508 SetChangedFlag (true);
4509 changed = 1;
4512 END_LOOP;
4514 PAD_LOOP (element);
4516 if (NSTRCMP (pinnum, pad->Number) == 0)
4518 AddObjectToChangeNameUndoList (PAD_TYPE, NULL, NULL,
4519 pad, pad->Name);
4521 * Note: we can't free() pad->Name first because
4522 * it is used in the undo list
4524 pad->Name = strdup (pinname);
4525 SetChangedFlag (true);
4526 changed = 1;
4529 END_LOOP;
4532 END_LOOP;
4534 * done with our action so increment the undo # if we actually
4535 * changed anything
4537 if (changed)
4539 if (defer_updates)
4540 defer_needs_update = 1;
4541 else
4543 IncrementUndoSerialNumber ();
4544 gui->invalidate_all ();
4548 return 0;
4551 /* --------------------------------------------------------------------------- */
4553 static const char changename_syntax[] =
4554 "ChangeName(Object)\n"
4555 "ChangeName(Layout|Layer)";
4557 static const char changename_help[] = "Sets the name of objects.";
4559 /* %start-doc actions ChangeName
4561 @table @code
4563 @item Object
4564 Changes the name of the element under the cursor.
4566 @item Layout
4567 Changes the name of the layout. This is printed on the fab drawings.
4569 @item Layer
4570 Changes the name of the currently active layer.
4572 @end table
4574 %end-doc */
4577 ActionChangeName (int argc, char **argv, Coord x, Coord y)
4579 char *function = ARG (0);
4580 char *name;
4582 if (function)
4584 switch (GetFunctionID (function))
4586 /* change the name of an object */
4587 case F_Object:
4589 int type;
4590 void *ptr1, *ptr2, *ptr3;
4592 gui->get_coords (_("Select an Object"), &x, &y);
4593 if ((type =
4594 SearchScreen (x, y, CHANGENAME_TYPES,
4595 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4597 SaveUndoSerialNumber ();
4598 if (QueryInputAndChangeObjectName (type, ptr1, ptr2, ptr3))
4600 SetChangedFlag (true);
4601 if (type == ELEMENT_TYPE)
4603 RubberbandType *ptr;
4604 int i;
4606 RestoreUndoSerialNumber ();
4607 Crosshair.AttachedObject.RubberbandN = 0;
4608 LookupRatLines (type, ptr1, ptr2, ptr3);
4609 ptr = Crosshair.AttachedObject.Rubberband;
4610 for (i = 0; i < Crosshair.AttachedObject.RubberbandN;
4611 i++, ptr++)
4613 if (PCB->RatOn)
4614 EraseRat ((RatType *) ptr->Line);
4615 MoveObjectToRemoveUndoList (RATLINE_TYPE,
4616 ptr->Line, ptr->Line,
4617 ptr->Line);
4619 IncrementUndoSerialNumber ();
4620 Draw ();
4624 break;
4627 /* change the layout's name */
4628 case F_Layout:
4629 name =
4630 gui->prompt_for (_("Enter the layout name:"), EMPTY (PCB->Name));
4631 /* NB: ChangeLayoutName takes ownership of the passed memory */
4632 if (name && ChangeLayoutName (name))
4633 SetChangedFlag (true);
4634 break;
4636 /* change the name of the active layer */
4637 case F_Layer:
4638 name = gui->prompt_for (_("Enter the layer name:"),
4639 EMPTY (CURRENT->Name));
4640 /* NB: ChangeLayerName takes ownership of the passed memory */
4641 if (name && ChangeLayerName (CURRENT, name))
4642 SetChangedFlag (true);
4643 break;
4646 return 0;
4650 /* --------------------------------------------------------------------------- */
4652 static const char morphpolygon_syntax[] = "MorphPolygon(Object|Selected)";
4654 static const char morphpolygon_help[] =
4655 "Converts dead polygon islands into separate polygons.";
4657 /* %start-doc actions MorphPolygon
4659 If a polygon is divided into unconnected "islands", you can use
4660 this command to convert the otherwise disappeared islands into
4661 separate polygons. Be sure the cursor is over a portion of the
4662 polygon that remains visible. Very small islands that may flake
4663 off are automatically deleted.
4665 %end-doc */
4667 static int
4668 ActionMorphPolygon (int argc, char **argv, Coord x, Coord y)
4670 char *function = ARG (0);
4671 if (function)
4673 switch (GetFunctionID (function))
4675 case F_Object:
4677 int type;
4678 void *ptr1, *ptr2, *ptr3;
4680 gui->get_coords (_("Select an Object"), &x, &y);
4681 if ((type = SearchScreen (x, y, POLYGON_TYPE,
4682 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4684 MorphPolygon ((LayerType *) ptr1, (PolygonType *) ptr3);
4685 Draw ();
4686 IncrementUndoSerialNumber ();
4688 break;
4690 case F_Selected:
4691 case F_SelectedObjects:
4692 ALLPOLYGON_LOOP (PCB->Data);
4694 if (TEST_FLAG (SELECTEDFLAG, polygon))
4695 MorphPolygon (layer, polygon);
4697 ENDALL_LOOP;
4698 Draw ();
4699 IncrementUndoSerialNumber ();
4700 break;
4703 return 0;
4706 /* --------------------------------------------------------------------------- */
4708 static const char togglehidename_syntax[] =
4709 "ToggleHideName(Object|SelectedElements)";
4711 static const char togglehidename_help[] =
4712 "Toggles the visibility of element names.";
4714 /* %start-doc actions ToggleHideName
4716 If names are hidden you won't see them on the screen and they will not
4717 appear on the silk layer when you print the layout.
4719 %end-doc */
4721 static int
4722 ActionToggleHideName (int argc, char **argv, Coord x, Coord y)
4724 char *function = ARG (0);
4725 if (function && PCB->ElementOn)
4727 switch (GetFunctionID (function))
4729 case F_Object:
4731 int type;
4732 void *ptr1, *ptr2, *ptr3;
4734 gui->get_coords (_("Select an Object"), &x, &y);
4735 if ((type = SearchScreen (x, y, ELEMENT_TYPE,
4736 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4738 AddObjectToFlagUndoList (type, ptr1, ptr2, ptr3);
4739 EraseElementName ((ElementType *) ptr2);
4740 TOGGLE_FLAG (HIDENAMEFLAG, (ElementType *) ptr2);
4741 DrawElementName ((ElementType *) ptr2);
4742 Draw ();
4743 IncrementUndoSerialNumber ();
4745 break;
4747 case F_SelectedElements:
4748 case F_Selected:
4750 bool changed = false;
4751 ELEMENT_LOOP (PCB->Data);
4753 if ((TEST_FLAG (SELECTEDFLAG, element) ||
4754 TEST_FLAG (SELECTEDFLAG,
4755 &NAMEONPCB_TEXT (element)))
4756 && (FRONT (element) || PCB->InvisibleObjectsOn))
4758 AddObjectToFlagUndoList (ELEMENT_TYPE, element,
4759 element, element);
4760 EraseElementName (element);
4761 TOGGLE_FLAG (HIDENAMEFLAG, element);
4762 DrawElementName (element);
4763 changed = true;
4766 END_LOOP;
4767 if (changed)
4769 Draw ();
4770 IncrementUndoSerialNumber ();
4775 return 0;
4778 /* --------------------------------------------------------------------------- */
4780 static const char changejoin_syntax[] =
4781 "ChangeJoin(ToggleObject|SelectedLines|SelectedArcs|Selected)";
4783 static const char changejoin_help[] =
4784 "Changes the join (clearance through polygons) of objects.";
4786 /* %start-doc actions ChangeJoin
4788 The join flag determines whether a line or arc, drawn to intersect a
4789 polygon, electrically connects to the polygon or not. When joined,
4790 the line/arc is simply drawn over the polygon, making an electrical
4791 connection. When not joined, a gap is drawn between the line and the
4792 polygon, insulating them from each other.
4794 %end-doc */
4796 static int
4797 ActionChangeJoin (int argc, char **argv, Coord x, Coord y)
4799 char *function = ARG (0);
4800 if (function)
4802 switch (GetFunctionID (function))
4804 case F_ToggleObject:
4805 case F_Object:
4807 int type;
4808 void *ptr1, *ptr2, *ptr3;
4810 gui->get_coords (_("Select an Object"), &x, &y);
4811 if ((type =
4812 SearchScreen (x, y, CHANGEJOIN_TYPES,
4813 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4814 if (ChangeObjectJoin (type, ptr1, ptr2, ptr3))
4815 SetChangedFlag (true);
4816 break;
4819 case F_SelectedLines:
4820 if (ChangeSelectedJoin (LINE_TYPE))
4821 SetChangedFlag (true);
4822 break;
4824 case F_SelectedArcs:
4825 if (ChangeSelectedJoin (ARC_TYPE))
4826 SetChangedFlag (true);
4827 break;
4829 case F_Selected:
4830 case F_SelectedObjects:
4831 if (ChangeSelectedJoin (CHANGEJOIN_TYPES))
4832 SetChangedFlag (true);
4833 break;
4836 return 0;
4839 /* --------------------------------------------------------------------------- */
4841 static const char changesquare_syntax[] =
4842 "ChangeSquare(ToggleObject)\n"
4843 "ChangeSquare(SelectedElements|SelectedPins)\n"
4844 "ChangeSquare(Selected|SelectedObjects)";
4846 static const char changesquare_help[] =
4847 "Changes the square flag of pins and pads.";
4849 /* %start-doc actions ChangeSquare
4851 Note that @code{Pins} means both pins and pads.
4853 @pinshapes
4855 %end-doc */
4857 static int
4858 ActionChangeSquare (int argc, char **argv, Coord x, Coord y)
4860 char *function = ARG (0);
4861 if (function)
4863 switch (GetFunctionID (function))
4865 case F_ToggleObject:
4866 case F_Object:
4868 int type;
4869 void *ptr1, *ptr2, *ptr3;
4871 gui->get_coords (_("Select an Object"), &x, &y);
4872 if ((type =
4873 SearchScreen (x, y, CHANGESQUARE_TYPES,
4874 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4875 if (ChangeObjectSquare (type, ptr1, ptr2, ptr3))
4876 SetChangedFlag (true);
4877 break;
4880 case F_SelectedElements:
4881 if (ChangeSelectedSquare (ELEMENT_TYPE))
4882 SetChangedFlag (true);
4883 break;
4885 case F_SelectedPins:
4886 if (ChangeSelectedSquare (PIN_TYPE | PAD_TYPE))
4887 SetChangedFlag (true);
4888 break;
4890 case F_Selected:
4891 case F_SelectedObjects:
4892 if (ChangeSelectedSquare (PIN_TYPE | PAD_TYPE))
4893 SetChangedFlag (true);
4894 break;
4897 return 0;
4900 /* --------------------------------------------------------------------------- */
4902 static const char setsquare_syntax[] =
4903 "SetSquare(ToggleObject|SelectedElements|SelectedPins)";
4905 static const char setsquare_help[] = "sets the square-flag of objects.";
4907 /* %start-doc actions SetSquare
4909 Note that @code{Pins} means pins and pads.
4911 @pinshapes
4913 %end-doc */
4915 static int
4916 ActionSetSquare (int argc, char **argv, Coord x, Coord y)
4918 char *function = ARG (0);
4919 if (function && *function)
4921 switch (GetFunctionID (function))
4923 case F_ToggleObject:
4924 case F_Object:
4926 int type;
4927 void *ptr1, *ptr2, *ptr3;
4929 gui->get_coords (_("Select an Object"), &x, &y);
4930 if ((type =
4931 SearchScreen (x, y, CHANGESQUARE_TYPES,
4932 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4933 if (SetObjectSquare (type, ptr1, ptr2, ptr3))
4934 SetChangedFlag (true);
4935 break;
4938 case F_SelectedElements:
4939 if (SetSelectedSquare (ELEMENT_TYPE))
4940 SetChangedFlag (true);
4941 break;
4943 case F_SelectedPins:
4944 if (SetSelectedSquare (PIN_TYPE | PAD_TYPE))
4945 SetChangedFlag (true);
4946 break;
4948 case F_Selected:
4949 case F_SelectedObjects:
4950 if (SetSelectedSquare (PIN_TYPE | PAD_TYPE))
4951 SetChangedFlag (true);
4952 break;
4955 return 0;
4958 /* --------------------------------------------------------------------------- */
4960 static const char clearsquare_syntax[] =
4961 "ClearSquare(ToggleObject|SelectedElements|SelectedPins)";
4963 static const char clearsquare_help[] =
4964 "Clears the square-flag of pins and pads.";
4966 /* %start-doc actions ClearSquare
4968 Note that @code{Pins} means pins and pads.
4970 @pinshapes
4972 %end-doc */
4974 static int
4975 ActionClearSquare (int argc, char **argv, Coord x, Coord y)
4977 char *function = ARG (0);
4978 if (function && *function)
4980 switch (GetFunctionID (function))
4982 case F_ToggleObject:
4983 case F_Object:
4985 int type;
4986 void *ptr1, *ptr2, *ptr3;
4988 gui->get_coords (_("Select an Object"), &x, &y);
4989 if ((type =
4990 SearchScreen (x, y, CHANGESQUARE_TYPES,
4991 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4992 if (ClrObjectSquare (type, ptr1, ptr2, ptr3))
4993 SetChangedFlag (true);
4994 break;
4997 case F_SelectedElements:
4998 if (ClrSelectedSquare (ELEMENT_TYPE))
4999 SetChangedFlag (true);
5000 break;
5002 case F_SelectedPins:
5003 if (ClrSelectedSquare (PIN_TYPE | PAD_TYPE))
5004 SetChangedFlag (true);
5005 break;
5007 case F_Selected:
5008 case F_SelectedObjects:
5009 if (ClrSelectedSquare (PIN_TYPE | PAD_TYPE))
5010 SetChangedFlag (true);
5011 break;
5014 return 0;
5017 /* --------------------------------------------------------------------------- */
5019 static const char changeoctagon_syntax[] =
5020 "ChangeOctagon(Object|ToggleObject|SelectedObjects|Selected)\n"
5021 "ChangeOctagon(SelectedElements|SelectedPins|SelectedVias)";
5023 static const char changeoctagon_help[] =
5024 "Changes the octagon-flag of pins and vias.";
5026 /* %start-doc actions ChangeOctagon
5028 @pinshapes
5030 %end-doc */
5032 static int
5033 ActionChangeOctagon (int argc, char **argv, Coord x, Coord y)
5035 char *function = ARG (0);
5036 if (function)
5038 switch (GetFunctionID (function))
5040 case F_ToggleObject:
5041 case F_Object:
5043 int type;
5044 void *ptr1, *ptr2, *ptr3;
5046 gui->get_coords (_("Select an Object"), &x, &y);
5047 if ((type =
5048 SearchScreen (x, y, CHANGEOCTAGON_TYPES,
5049 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5050 if (ChangeObjectOctagon (type, ptr1, ptr2, ptr3))
5051 SetChangedFlag (true);
5052 break;
5055 case F_SelectedElements:
5056 if (ChangeSelectedOctagon (ELEMENT_TYPE))
5057 SetChangedFlag (true);
5058 break;
5060 case F_SelectedPins:
5061 if (ChangeSelectedOctagon (PIN_TYPE))
5062 SetChangedFlag (true);
5063 break;
5065 case F_SelectedVias:
5066 if (ChangeSelectedOctagon (VIA_TYPE))
5067 SetChangedFlag (true);
5068 break;
5070 case F_Selected:
5071 case F_SelectedObjects:
5072 if (ChangeSelectedOctagon (PIN_TYPES))
5073 SetChangedFlag (true);
5074 break;
5077 return 0;
5080 /* --------------------------------------------------------------------------- */
5082 static const char setoctagon_syntax[] =
5083 "SetOctagon(Object|ToggleObject|SelectedElements|Selected)";
5085 static const char setoctagon_help[] = "Sets the octagon-flag of objects.";
5087 /* %start-doc actions SetOctagon
5089 @pinshapes
5091 %end-doc */
5093 static int
5094 ActionSetOctagon (int argc, char **argv, Coord x, Coord y)
5096 char *function = ARG (0);
5097 if (function)
5099 switch (GetFunctionID (function))
5101 case F_ToggleObject:
5102 case F_Object:
5104 int type;
5105 void *ptr1, *ptr2, *ptr3;
5107 gui->get_coords (_("Select an Object"), &x, &y);
5108 if ((type =
5109 SearchScreen (x, y, CHANGEOCTAGON_TYPES,
5110 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5111 if (SetObjectOctagon (type, ptr1, ptr2, ptr3))
5112 SetChangedFlag (true);
5113 break;
5116 case F_SelectedElements:
5117 if (SetSelectedOctagon (ELEMENT_TYPE))
5118 SetChangedFlag (true);
5119 break;
5121 case F_SelectedPins:
5122 if (SetSelectedOctagon (PIN_TYPE))
5123 SetChangedFlag (true);
5124 break;
5126 case F_SelectedVias:
5127 if (SetSelectedOctagon (VIA_TYPE))
5128 SetChangedFlag (true);
5129 break;
5131 case F_Selected:
5132 case F_SelectedObjects:
5133 if (SetSelectedOctagon (PIN_TYPES))
5134 SetChangedFlag (true);
5135 break;
5138 return 0;
5141 /* --------------------------------------------------------------------------- */
5143 static const char clearoctagon_syntax[] =
5144 "ClearOctagon(ToggleObject|Object|SelectedObjects|Selected)\n"
5145 "ClearOctagon(SelectedElements|SelectedPins|SelectedVias)";
5147 static const char clearoctagon_help[] =
5148 "Clears the octagon-flag of pins and vias.";
5150 /* %start-doc actions ClearOctagon
5152 @pinshapes
5154 %end-doc */
5156 static int
5157 ActionClearOctagon (int argc, char **argv, Coord x, Coord y)
5159 char *function = ARG (0);
5160 if (function)
5162 switch (GetFunctionID (function))
5164 case F_ToggleObject:
5165 case F_Object:
5167 int type;
5168 void *ptr1, *ptr2, *ptr3;
5170 gui->get_coords (_("Select an Object"), &x, &y);
5171 if ((type =
5172 SearchScreen (Crosshair.X, Crosshair.Y, CHANGEOCTAGON_TYPES,
5173 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5174 if (ClrObjectOctagon (type, ptr1, ptr2, ptr3))
5175 SetChangedFlag (true);
5176 break;
5179 case F_SelectedElements:
5180 if (ClrSelectedOctagon (ELEMENT_TYPE))
5181 SetChangedFlag (true);
5182 break;
5184 case F_SelectedPins:
5185 if (ClrSelectedOctagon (PIN_TYPE))
5186 SetChangedFlag (true);
5187 break;
5189 case F_SelectedVias:
5190 if (ClrSelectedOctagon (VIA_TYPE))
5191 SetChangedFlag (true);
5192 break;
5194 case F_Selected:
5195 case F_SelectedObjects:
5196 if (ClrSelectedOctagon (PIN_TYPES))
5197 SetChangedFlag (true);
5198 break;
5201 return 0;
5204 /* --------------------------------------------------------------------------- */
5206 static const char changehold_syntax[] =
5207 "ChangeHole(ToggleObject|Object|SelectedVias|Selected)";
5209 static const char changehold_help[] = "Changes the hole flag of objects.";
5211 /* %start-doc actions ChangeHole
5213 The "hole flag" of a via determines whether the via is a
5214 plated-through hole (not set), or an unplated hole (set).
5216 %end-doc */
5218 static int
5219 ActionChangeHole (int argc, char **argv, Coord x, Coord y)
5221 char *function = ARG (0);
5222 if (function)
5224 switch (GetFunctionID (function))
5226 case F_ToggleObject:
5227 case F_Object:
5229 int type;
5230 void *ptr1, *ptr2, *ptr3;
5232 gui->get_coords (_("Select an Object"), &x, &y);
5233 if ((type = SearchScreen (x, y, VIA_TYPE,
5234 &ptr1, &ptr2, &ptr3)) != NO_TYPE
5235 && ChangeHole ((PinType *) ptr3))
5236 IncrementUndoSerialNumber ();
5237 break;
5240 case F_SelectedVias:
5241 case F_Selected:
5242 if (ChangeSelectedHole ())
5243 SetChangedFlag (true);
5244 break;
5247 return 0;
5250 /* --------------------------------------------------------------------------- */
5252 static const char changepaste_syntax[] =
5253 "ChangePaste(ToggleObject|Object|SelectedPads|Selected)";
5255 static const char changepaste_help[] = "Changes the no paste flag of objects.";
5257 /* %start-doc actions ChangePaste
5259 The "no paste flag" of a pad determines whether the solderpaste
5260 stencil will have an opening for the pad (no set) or if there wil be
5261 no solderpaste on the pad (set). This is used for things such as
5262 fiducial pads.
5264 %end-doc */
5266 static int
5267 ActionChangePaste (int argc, char **argv, Coord x, Coord y)
5269 char *function = ARG (0);
5270 if (function)
5272 switch (GetFunctionID (function))
5274 case F_ToggleObject:
5275 case F_Object:
5277 int type;
5278 void *ptr1, *ptr2, *ptr3;
5280 gui->get_coords (_("Select an Object"), &x, &y);
5281 if ((type = SearchScreen (x, y, PAD_TYPE,
5282 &ptr1, &ptr2, &ptr3)) != NO_TYPE
5283 && ChangePaste ((PadType *) ptr3))
5284 IncrementUndoSerialNumber ();
5285 break;
5288 case F_SelectedPads:
5289 case F_Selected:
5290 if (ChangeSelectedPaste ())
5291 SetChangedFlag (true);
5292 break;
5295 return 0;
5298 /* --------------------------------------------------------------------------- */
5300 static const char select_syntax[] =
5301 "Select(Object|ToggleObject)\n"
5302 "Select(All|Block|Connection)\n"
5303 "Select(ElementByName|ObjectByName|PadByName|PinByName)\n"
5304 "Select(ElementByName|ObjectByName|PadByName|PinByName, Name)\n"
5305 "Select(TextByName|ViaByName|NetByName)\n"
5306 "Select(TextByName|ViaByName|NetByName, Name)\n"
5307 "Select(Convert)";
5309 static const char select_help[] = "Toggles or sets the selection.";
5311 /* %start-doc actions Select
5313 @table @code
5315 @item ElementByName
5316 @item ObjectByName
5317 @item PadByName
5318 @item PinByName
5319 @item TextByName
5320 @item ViaByName
5321 @item NetByName
5323 These all rely on having a regular expression parser built into
5324 @code{pcb}. If the name is not specified then the user is prompted
5325 for a pattern, and all objects that match the pattern and are of the
5326 type specified are selected.
5328 @item Object
5329 @item ToggleObject
5330 Selects the object under the cursor.
5332 @item Block
5333 Selects all objects in a rectangle indicated by the cursor.
5335 @item All
5336 Selects all objects on the board.
5338 @item Connection
5339 Selects all connections with the ``found'' flag set.
5341 @item Convert
5342 Converts the selected objects to an element. This uses the highest
5343 numbered paste buffer.
5345 @end table
5347 %end-doc */
5349 static int
5350 ActionSelect (int argc, char **argv, Coord x, Coord y)
5352 char *function = ARG (0);
5353 if (function)
5355 switch (GetFunctionID (function))
5357 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5358 int type;
5359 /* select objects by their names */
5360 case F_ElementByName:
5361 type = ELEMENT_TYPE;
5362 goto commonByName;
5363 case F_ObjectByName:
5364 type = ALL_TYPES;
5365 goto commonByName;
5366 case F_PadByName:
5367 type = PAD_TYPE;
5368 goto commonByName;
5369 case F_PinByName:
5370 type = PIN_TYPE;
5371 goto commonByName;
5372 case F_TextByName:
5373 type = TEXT_TYPE;
5374 goto commonByName;
5375 case F_ViaByName:
5376 type = VIA_TYPE;
5377 goto commonByName;
5378 case F_NetByName:
5379 type = NET_TYPE;
5380 goto commonByName;
5382 commonByName:
5384 char *pattern = ARG (1);
5386 if (pattern
5387 || (pattern =
5388 gui->prompt_for (_("Enter pattern:"), "")) != NULL)
5390 if (SelectObjectByName (type, pattern, true))
5391 SetChangedFlag (true);
5392 if (ARG (1) == NULL)
5393 free (pattern);
5395 break;
5397 #endif /* defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP) */
5399 /* select a single object */
5400 case F_ToggleObject:
5401 case F_Object:
5402 if (SelectObject ())
5403 SetChangedFlag (true);
5404 break;
5406 /* all objects in block */
5407 case F_Block:
5409 BoxType box;
5411 box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
5412 Crosshair.AttachedBox.Point2.X);
5413 box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
5414 Crosshair.AttachedBox.Point2.Y);
5415 box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
5416 Crosshair.AttachedBox.Point2.X);
5417 box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
5418 Crosshair.AttachedBox.Point2.Y);
5419 notify_crosshair_change (false);
5420 NotifyBlock ();
5421 if (Crosshair.AttachedBox.State == STATE_THIRD &&
5422 SelectBlock (&box, true))
5424 SetChangedFlag (true);
5425 Crosshair.AttachedBox.State = STATE_FIRST;
5427 notify_crosshair_change (true);
5428 break;
5431 /* select all visible objects */
5432 case F_All:
5434 BoxType box;
5436 box.X1 = -MAX_COORD;
5437 box.Y1 = -MAX_COORD;
5438 box.X2 = MAX_COORD;
5439 box.Y2 = MAX_COORD;
5440 if (SelectBlock (&box, true))
5441 SetChangedFlag (true);
5442 break;
5445 /* all found connections */
5446 case F_Connection:
5447 if (SelectConnection (true))
5449 Draw ();
5450 IncrementUndoSerialNumber ();
5451 SetChangedFlag (true);
5453 break;
5455 case F_Convert:
5457 Coord x, y;
5458 Note.Buffer = Settings.BufferNumber;
5459 SetBufferNumber (MAX_BUFFER - 1);
5460 ClearBuffer (PASTEBUFFER);
5461 gui->get_coords (_("Select the Element's Mark Location"), &x, &y);
5462 x = GridFit (x, PCB->Grid, PCB->GridOffsetX);
5463 y = GridFit (y, PCB->Grid, PCB->GridOffsetY);
5464 AddSelectedToBuffer (PASTEBUFFER, x, y, true);
5465 SaveUndoSerialNumber ();
5466 RemoveSelected ();
5467 ConvertBufferToElement (PASTEBUFFER);
5468 RestoreUndoSerialNumber ();
5469 CopyPastebufferToLayout (x, y);
5470 SetBufferNumber (Note.Buffer);
5472 break;
5474 default:
5475 AFAIL (select);
5476 break;
5479 return 0;
5482 /* FLAG(have_regex,FlagHaveRegex,0) */
5484 FlagHaveRegex (int parm)
5486 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5487 return 1;
5488 #else
5489 return 0;
5490 #endif
5493 /* --------------------------------------------------------------------------- */
5495 static const char unselect_syntax[] =
5496 "Unselect(All|Block|Connection)\n"
5497 "Unselect(ElementByName|ObjectByName|PadByName|PinByName)\n"
5498 "Unselect(ElementByName|ObjectByName|PadByName|PinByName, Name)\n"
5499 "Unselect(TextByName|ViaByName)\n"
5500 "Unselect(TextByName|ViaByName, Name)\n";
5502 static const char unselect_help[] =
5503 "Unselects the object at the pointer location or the specified objects.";
5505 /* %start-doc actions Unselect
5507 @table @code
5509 @item All
5510 Unselect all objects.
5512 @item Block
5513 Unselect all objects in a rectangle given by the cursor.
5515 @item Connection
5516 Unselect all connections with the ``found'' flag set.
5518 @item ElementByName
5519 @item ObjectByName
5520 @item PadByName
5521 @item PinByName
5522 @item TextByName
5523 @item ViaByName
5525 These all rely on having a regular expression parser built into
5526 @code{pcb}. If the name is not specified then the user is prompted
5527 for a pattern, and all objects that match the pattern and are of the
5528 type specified are unselected.
5531 @end table
5533 %end-doc */
5535 static int
5536 ActionUnselect (int argc, char **argv, Coord x, Coord y)
5538 char *function = ARG (0);
5539 if (function)
5541 switch (GetFunctionID (function))
5543 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5544 int type;
5545 /* select objects by their names */
5546 case F_ElementByName:
5547 type = ELEMENT_TYPE;
5548 goto commonByName;
5549 case F_ObjectByName:
5550 type = ALL_TYPES;
5551 goto commonByName;
5552 case F_PadByName:
5553 type = PAD_TYPE;
5554 goto commonByName;
5555 case F_PinByName:
5556 type = PIN_TYPE;
5557 goto commonByName;
5558 case F_TextByName:
5559 type = TEXT_TYPE;
5560 goto commonByName;
5561 case F_ViaByName:
5562 type = VIA_TYPE;
5563 goto commonByName;
5564 case F_NetByName:
5565 type = NET_TYPE;
5566 goto commonByName;
5568 commonByName:
5570 char *pattern = ARG (1);
5572 if (pattern
5573 || (pattern =
5574 gui->prompt_for (_("Enter pattern:"), "")) != NULL)
5576 if (SelectObjectByName (type, pattern, false))
5577 SetChangedFlag (true);
5578 if (ARG (1) == NULL)
5579 free (pattern);
5581 break;
5583 #endif /* defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP) */
5585 /* all objects in block */
5586 case F_Block:
5588 BoxType box;
5590 box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
5591 Crosshair.AttachedBox.Point2.X);
5592 box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
5593 Crosshair.AttachedBox.Point2.Y);
5594 box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
5595 Crosshair.AttachedBox.Point2.X);
5596 box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
5597 Crosshair.AttachedBox.Point2.Y);
5598 notify_crosshair_change (false);
5599 NotifyBlock ();
5600 if (Crosshair.AttachedBox.State == STATE_THIRD &&
5601 SelectBlock (&box, false))
5603 SetChangedFlag (true);
5604 Crosshair.AttachedBox.State = STATE_FIRST;
5606 notify_crosshair_change (true);
5607 break;
5610 /* unselect all visible objects */
5611 case F_All:
5613 BoxType box;
5615 box.X1 = -MAX_COORD;
5616 box.Y1 = -MAX_COORD;
5617 box.X2 = MAX_COORD;
5618 box.Y2 = MAX_COORD;
5619 if (SelectBlock (&box, false))
5620 SetChangedFlag (true);
5621 break;
5624 /* all found connections */
5625 case F_Connection:
5626 if (SelectConnection (false))
5628 Draw ();
5629 IncrementUndoSerialNumber ();
5630 SetChangedFlag (true);
5632 break;
5634 default:
5635 AFAIL (unselect);
5636 break;
5640 return 0;
5643 /* --------------------------------------------------------------------------- */
5645 static const char saveto_syntax[] =
5646 "SaveTo(Layout|LayoutAs,filename)\n"
5647 "SaveTo(AllConnections|AllUnusedPins|ElementConnections,filename)\n"
5648 "SaveTo(PasteBuffer,filename)";
5650 static const char saveto_help[] = "Saves data to a file.";
5652 /* %start-doc actions SaveTo
5654 @table @code
5656 @item Layout
5657 Saves the current layout.
5659 @item LayoutAs
5660 Saves the current layout, and remembers the filename used.
5662 @item AllConnections
5663 Save all connections to a file.
5665 @item AllUnusedPins
5666 List all unused pins to a file.
5668 @item ElementConnections
5669 Save connections to the element at the cursor to a file.
5671 @item PasteBuffer
5672 Save the content of the active Buffer to a file. This is the graphical way to create a footprint.
5674 @end table
5676 %end-doc */
5678 static int
5679 ActionSaveTo (int argc, char **argv, Coord x, Coord y)
5681 char *function;
5682 char *name;
5684 function = argv[0];
5685 name = argv[1];
5687 if (strcasecmp (function, "Layout") == 0)
5689 if (SavePCB (PCB->Filename) == 0)
5690 SetChangedFlag (false);
5691 return 0;
5694 if (argc != 2)
5695 AFAIL (saveto);
5697 if (strcasecmp (function, "LayoutAs") == 0)
5699 if (SavePCB (name) == 0)
5701 SetChangedFlag (false);
5702 free (PCB->Filename);
5703 PCB->Filename = strdup (name);
5704 if (gui->notify_filename_changed != NULL)
5705 gui->notify_filename_changed ();
5707 return 0;
5710 if (strcasecmp (function, "AllConnections") == 0)
5712 FILE *fp;
5713 bool result;
5714 if ((fp = CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
5716 LookupConnectionsToAllElements (fp);
5717 fclose (fp);
5718 SetChangedFlag (true);
5720 return 0;
5723 if (strcasecmp (function, "AllUnusedPins") == 0)
5725 FILE *fp;
5726 bool result;
5727 if ((fp = CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
5729 LookupUnusedPins (fp);
5730 fclose (fp);
5731 SetChangedFlag (true);
5733 return 0;
5736 if (strcasecmp (function, "ElementConnections") == 0)
5738 ElementType *element;
5739 void *ptrtmp;
5740 FILE *fp;
5741 bool result;
5743 if ((SearchScreen (Crosshair.X, Crosshair.Y, ELEMENT_TYPE,
5744 &ptrtmp, &ptrtmp, &ptrtmp)) != NO_TYPE)
5746 element = (ElementType *) ptrtmp;
5747 if ((fp =
5748 CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
5750 LookupElementConnections (element, fp);
5751 fclose (fp);
5752 SetChangedFlag (true);
5755 return 0;
5758 if (strcasecmp (function, "PasteBuffer") == 0)
5760 return SaveBufferElements (name);
5763 AFAIL (saveto);
5766 /* --------------------------------------------------------------------------- */
5768 static const char savesettings_syntax[] =
5769 "SaveSettings()\n"
5770 "SaveSettings(local)";
5772 static const char savesettings_help[] = "Saves settings.";
5774 /* %start-doc actions SaveSettings
5776 If you pass no arguments, the settings are stored in
5777 @code{$HOME/.pcb/settings}. If you pass the word @code{local} they're
5778 saved in @code{./pcb.settings}.
5780 %end-doc */
5782 static int
5783 ActionSaveSettings (int argc, char **argv, Coord x, Coord y)
5785 int locally = argc > 0 ? (strncasecmp (argv[0], "local", 5) == 0) : 0;
5786 hid_save_settings (locally);
5787 return 0;
5790 /* --------------------------------------------------------------------------- */
5792 static const char loadfrom_syntax[] =
5793 "LoadFrom(Layout|LayoutToBuffer|ElementToBuffer|Netlist|Revert,filename)";
5795 static const char loadfrom_help[] = "Load layout data from a file.";
5797 /* %start-doc actions LoadFrom
5799 This action assumes you know what the filename is. The various GUIs
5800 should have a similar @code{Load} action where the filename is
5801 optional, and will provide their own file selection mechanism to let
5802 you choose the file name.
5804 @table @code
5806 @item Layout
5807 Loads an entire PCB layout, replacing the current one.
5809 @item LayoutToBuffer
5810 Loads an entire PCB layout to the paste buffer.
5812 @item ElementToBuffer
5813 Loads the given element file into the paste buffer. Element files
5814 contain only a single @code{Element} definition, such as the
5815 ``newlib'' library uses.
5817 @item Netlist
5818 Loads a new netlist, replacing any current netlist.
5820 @item Revert
5821 Re-loads the current layout from its disk file, reverting any changes
5822 you may have made.
5824 @end table
5826 %end-doc */
5828 static int
5829 ActionLoadFrom (int argc, char **argv, Coord x, Coord y)
5831 char *function;
5832 char *name;
5834 if (argc < 2)
5835 AFAIL (loadfrom);
5837 function = argv[0];
5838 name = argv[1];
5840 if (strcasecmp (function, "ElementToBuffer") == 0)
5842 notify_crosshair_change (false);
5843 if (LoadElementToBuffer (PASTEBUFFER, name, true))
5844 SetMode (PASTEBUFFER_MODE);
5845 notify_crosshair_change (true);
5848 else if (strcasecmp (function, "LayoutToBuffer") == 0)
5850 notify_crosshair_change (false);
5851 if (LoadLayoutToBuffer (PASTEBUFFER, name))
5852 SetMode (PASTEBUFFER_MODE);
5853 notify_crosshair_change (true);
5856 else if (strcasecmp (function, "Layout") == 0)
5858 if (!PCB->Changed ||
5859 gui->confirm_dialog (_("OK to override layout data?"), 0))
5860 LoadPCB (name);
5863 else if (strcasecmp (function, "Netlist") == 0)
5865 if (PCB->Netlistname)
5866 free (PCB->Netlistname);
5867 PCB->Netlistname = StripWhiteSpaceAndDup (name);
5868 FreeLibraryMemory (&PCB->NetlistLib);
5869 ImportNetlist (PCB->Netlistname);
5870 NetlistChanged (1);
5872 else if (strcasecmp (function, "Revert") == 0 && PCB->Filename
5873 && (!PCB->Changed
5874 || gui->confirm_dialog (_("OK to override changes?"), 0)))
5876 RevertPCB ();
5879 return 0;
5882 /* --------------------------------------------------------------------------- */
5884 static const char new_syntax[] = "New([name])";
5886 static const char new_help[] = "Starts a new layout.";
5888 /* %start-doc actions New
5890 If a name is not given, one is prompted for.
5892 %end-doc */
5894 static int
5895 ActionNew (int argc, char **argv, Coord x, Coord y)
5897 char *name = ARG (0);
5899 if (!PCB->Changed || gui->confirm_dialog (_("OK to clear layout data?"), 0))
5901 if (name)
5902 name = strdup (name);
5903 else
5904 name = gui->prompt_for (_("Enter the layout name:"), "");
5906 if (!name)
5907 return 1;
5909 notify_crosshair_change (false);
5910 /* do emergency saving
5911 * clear the old struct and allocate memory for the new one
5913 if (PCB->Changed && Settings.SaveInTMP)
5914 SaveInTMP ();
5915 RemovePCB (PCB);
5916 PCB = NULL;
5917 PCB = CreateNewPCB (true);
5918 PCB->Data->LayerN = DEF_LAYER;
5919 CreateNewPCBPost (PCB, 1);
5921 /* setup the new name and reset some values to default */
5922 free (PCB->Name);
5923 PCB->Name = name;
5925 ResetStackAndVisibility ();
5926 SetCrosshairRange (0, 0, PCB->MaxWidth, PCB->MaxHeight);
5927 CenterDisplay (PCB->MaxWidth / 2, PCB->MaxHeight / 2);
5928 Redraw ();
5930 hid_action ("PCBChanged");
5931 notify_crosshair_change (true);
5932 return 0;
5934 return 1;
5937 /* ---------------------------------------------------------------------------
5938 * no operation, just for testing purposes
5939 * syntax: Bell(volume)
5941 void
5942 ActionBell (char *volume)
5944 gui->beep ();
5947 /* --------------------------------------------------------------------------- */
5949 static const char pastebuffer_syntax[] =
5950 "PasteBuffer(AddSelected|Clear|1..MAX_BUFFER)\n"
5951 "PasteBuffer(Rotate, 1..3)\n"
5952 "PasteBuffer(Convert|Save|Restore|Mirror)\n"
5953 "PasteBuffer(ToLayout, X, Y, units)";
5955 static const char pastebuffer_help[] =
5956 "Various operations on the paste buffer.";
5958 /* %start-doc actions PasteBuffer
5960 There are a number of paste buffers; the actual limit is a
5961 compile-time constant @code{MAX_BUFFER} in @file{globalconst.h}. It
5962 is currently @code{5}. One of these is the ``current'' paste buffer,
5963 often referred to as ``the'' paste buffer.
5965 @table @code
5967 @item AddSelected
5968 Copies the selected objects to the current paste buffer.
5970 @item Clear
5971 Remove all objects from the current paste buffer.
5973 @item Convert
5974 Convert the current paste buffer to an element. Vias are converted to
5975 pins, lines are converted to pads.
5977 @item Restore
5978 Convert any elements in the paste buffer back to vias and lines.
5980 @item Mirror
5981 Flip all objects in the paste buffer vertically (up/down flip). To mirror
5982 horizontally, combine this with rotations.
5984 @item Rotate
5985 Rotates the current buffer. The number to pass is 1..3, where 1 means
5986 90 degrees counter clockwise, 2 means 180 degrees, and 3 means 90
5987 degrees clockwise (270 CCW).
5989 @item Save
5990 Saves any elements in the current buffer to the indicated file.
5992 @item ToLayout
5993 Pastes any elements in the current buffer to the indicated X, Y
5994 coordinates in the layout. The @code{X} and @code{Y} are treated like
5995 @code{delta} is for many other objects. For each, if it's prefixed by
5996 @code{+} or @code{-}, then that amount is relative to the last
5997 location. Otherwise, it's absolute. Units can be
5998 @code{mil} or @code{mm}; if unspecified, units are PCB's internal
5999 units, currently 1/100 mil.
6002 @item 1..MAX_BUFFER
6003 Selects the given buffer to be the current paste buffer.
6005 @end table
6007 %end-doc */
6009 static int
6010 ActionPasteBuffer (int argc, char **argv, Coord x, Coord y)
6012 char *function = argc ? argv[0] : (char *)"";
6013 char *sbufnum = argc > 1 ? argv[1] : (char *)"";
6014 char *name;
6015 static char *default_file = NULL;
6016 int free_name = 0;
6018 notify_crosshair_change (false);
6019 if (function)
6021 switch (GetFunctionID (function))
6023 /* clear contents of paste buffer */
6024 case F_Clear:
6025 ClearBuffer (PASTEBUFFER);
6026 break;
6028 /* copies objects to paste buffer */
6029 case F_AddSelected:
6030 AddSelectedToBuffer (PASTEBUFFER, 0, 0, false);
6031 break;
6033 /* converts buffer contents into an element */
6034 case F_Convert:
6035 ConvertBufferToElement (PASTEBUFFER);
6036 break;
6038 /* break up element for editing */
6039 case F_Restore:
6040 SmashBufferElement (PASTEBUFFER);
6041 break;
6043 /* Mirror buffer */
6044 case F_Mirror:
6045 MirrorBuffer (PASTEBUFFER);
6046 break;
6048 case F_Rotate:
6049 if (sbufnum)
6051 RotateBuffer (PASTEBUFFER, (BYTE) atoi (sbufnum));
6052 SetCrosshairRangeToBuffer ();
6054 break;
6056 case F_Save:
6057 if (PASTEBUFFER->Data->ElementN == 0)
6059 Message (_("Buffer has no elements!\n"));
6060 break;
6062 free_name = 0;
6063 if (argc <= 1)
6065 name = gui->fileselect (_("Save Paste Buffer As ..."),
6066 _("Choose a file to save the contents of the\n"
6067 "paste buffer to.\n"),
6068 default_file, ".fp", "footprint",
6071 if (default_file)
6073 free (default_file);
6074 default_file = NULL;
6076 if ( name && *name)
6078 default_file = strdup (name);
6080 free_name = 1;
6083 else
6084 name = argv[1];
6087 FILE *exist;
6089 if ((exist = fopen (name, "r")))
6091 fclose (exist);
6092 if (gui->
6093 confirm_dialog (_("File exists! Ok to overwrite?"), 0))
6094 SaveBufferElements (name);
6096 else
6097 SaveBufferElements (name);
6099 if (free_name && name)
6100 free (name);
6102 break;
6104 case F_ToLayout:
6106 static Coord oldx = 0, oldy = 0;
6107 Coord x, y;
6108 bool absolute;
6110 if (argc == 1)
6112 x = y = 0;
6114 else if (argc == 3 || argc == 4)
6116 x = GetValue (ARG (1), ARG (3), &absolute);
6117 if (!absolute)
6118 x += oldx;
6119 y = GetValue (ARG (2), ARG (3), &absolute);
6120 if (!absolute)
6121 y += oldy;
6123 else
6125 notify_crosshair_change (true);
6126 AFAIL (pastebuffer);
6129 oldx = x;
6130 oldy = y;
6131 if (CopyPastebufferToLayout (x, y))
6132 SetChangedFlag (true);
6134 break;
6136 /* set number */
6137 default:
6139 int number = atoi (function);
6141 /* correct number */
6142 if (number)
6143 SetBufferNumber (number - 1);
6148 notify_crosshair_change (true);
6149 return 0;
6152 /* --------------------------------------------------------------------------- */
6154 static const char undo_syntax[] = "Undo()\n"
6155 "Undo(ClearList)";
6157 static const char undo_help[] = "Undo recent changes.";
6159 /* %start-doc actions Undo
6161 The unlimited undo feature of @code{Pcb} allows you to recover from
6162 most operations that materially affect you work. Calling
6163 @code{Undo()} without any parameter recovers from the last (non-undo)
6164 operation. @code{ClearList} is used to release the allocated
6165 memory. @code{ClearList} is called whenever a new layout is started or
6166 loaded. See also @code{Redo} and @code{Atomic}.
6168 Note that undo groups operations by serial number; changes with the
6169 same serial number will be undone (or redone) as a group. See
6170 @code{Atomic}.
6172 %end-doc */
6174 static int
6175 ActionUndo (int argc, char **argv, Coord x, Coord y)
6177 char *function = ARG (0);
6178 if (!function || !*function)
6180 /* don't allow undo in the middle of an operation */
6181 if (Settings.Mode != POLYGONHOLE_MODE &&
6182 Crosshair.AttachedObject.State != STATE_FIRST)
6183 return 1;
6184 if (Crosshair.AttachedBox.State != STATE_FIRST
6185 && Settings.Mode != ARC_MODE)
6186 return 1;
6187 /* undo the last operation */
6189 notify_crosshair_change (false);
6190 if ((Settings.Mode == POLYGON_MODE ||
6191 Settings.Mode == POLYGONHOLE_MODE) &&
6192 Crosshair.AttachedPolygon.PointN)
6194 GoToPreviousPoint ();
6195 notify_crosshair_change (true);
6196 return 0;
6198 /* move anchor point if undoing during line creation */
6199 if (Settings.Mode == LINE_MODE)
6201 if (Crosshair.AttachedLine.State == STATE_SECOND)
6203 if (TEST_FLAG (AUTODRCFLAG, PCB))
6204 Undo (true); /* undo the connection find */
6205 Crosshair.AttachedLine.State = STATE_FIRST;
6206 SetLocalRef (0, 0, false);
6207 notify_crosshair_change (true);
6208 return 0;
6210 if (Crosshair.AttachedLine.State == STATE_THIRD)
6212 int type;
6213 void *ptr1, *ptr3, *ptrtmp;
6214 LineType *ptr2;
6215 /* this search is guaranteed to succeed */
6216 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6217 &ptrtmp, &ptr3,
6218 Crosshair.AttachedLine.Point1.X,
6219 Crosshair.AttachedLine.Point1.Y, 0);
6220 ptr2 = (LineType *) ptrtmp;
6222 /* save both ends of line */
6223 Crosshair.AttachedLine.Point2.X = ptr2->Point1.X;
6224 Crosshair.AttachedLine.Point2.Y = ptr2->Point1.Y;
6225 if ((type = Undo (true)))
6226 SetChangedFlag (true);
6227 /* check that the undo was of the right type */
6228 if ((type & UNDO_CREATE) == 0)
6230 /* wrong undo type, restore anchor points */
6231 Crosshair.AttachedLine.Point2.X =
6232 Crosshair.AttachedLine.Point1.X;
6233 Crosshair.AttachedLine.Point2.Y =
6234 Crosshair.AttachedLine.Point1.Y;
6235 notify_crosshair_change (true);
6236 return 0;
6238 /* move to new anchor */
6239 Crosshair.AttachedLine.Point1.X =
6240 Crosshair.AttachedLine.Point2.X;
6241 Crosshair.AttachedLine.Point1.Y =
6242 Crosshair.AttachedLine.Point2.Y;
6243 /* check if an intermediate point was removed */
6244 if (type & UNDO_REMOVE)
6246 /* this search should find the restored line */
6247 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6248 &ptrtmp,
6249 &ptr3,
6250 Crosshair.AttachedLine.Point2.X,
6251 Crosshair.AttachedLine.Point2.Y, 0);
6252 ptr2 = (LineType *) ptrtmp;
6253 if (TEST_FLAG (AUTODRCFLAG, PCB))
6255 /* undo loses FOUNDFLAG */
6256 SET_FLAG(FOUNDFLAG, ptr2);
6257 DrawLine (CURRENT, ptr2);
6259 Crosshair.AttachedLine.Point1.X =
6260 Crosshair.AttachedLine.Point2.X = ptr2->Point2.X;
6261 Crosshair.AttachedLine.Point1.Y =
6262 Crosshair.AttachedLine.Point2.Y = ptr2->Point2.Y;
6264 FitCrosshairIntoGrid (Crosshair.X, Crosshair.Y);
6265 AdjustAttachedObjects ();
6266 if (--addedLines == 0)
6268 Crosshair.AttachedLine.State = STATE_SECOND;
6269 lastLayer = CURRENT;
6271 else
6273 /* this search is guaranteed to succeed too */
6274 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6275 &ptrtmp,
6276 &ptr3,
6277 Crosshair.AttachedLine.Point1.X,
6278 Crosshair.AttachedLine.Point1.Y, 0);
6279 ptr2 = (LineType *) ptrtmp;
6280 lastLayer = (LayerType *) ptr1;
6282 notify_crosshair_change (true);
6283 return 0;
6286 if (Settings.Mode == ARC_MODE)
6288 if (Crosshair.AttachedBox.State == STATE_SECOND)
6290 Crosshair.AttachedBox.State = STATE_FIRST;
6291 notify_crosshair_change (true);
6292 return 0;
6294 if (Crosshair.AttachedBox.State == STATE_THIRD)
6296 void *ptr1, *ptr2, *ptr3;
6297 BoxType *bx;
6298 /* guaranteed to succeed */
6299 SearchObjectByLocation (ARC_TYPE, &ptr1, &ptr2, &ptr3,
6300 Crosshair.AttachedBox.Point1.X,
6301 Crosshair.AttachedBox.Point1.Y, 0);
6302 bx = GetArcEnds ((ArcType *) ptr2);
6303 Crosshair.AttachedBox.Point1.X =
6304 Crosshair.AttachedBox.Point2.X = bx->X1;
6305 Crosshair.AttachedBox.Point1.Y =
6306 Crosshair.AttachedBox.Point2.Y = bx->Y1;
6307 AdjustAttachedObjects ();
6308 if (--addedLines == 0)
6309 Crosshair.AttachedBox.State = STATE_SECOND;
6312 /* undo the last destructive operation */
6313 if (Undo (true))
6314 SetChangedFlag (true);
6316 else if (function)
6318 switch (GetFunctionID (function))
6320 /* clear 'undo objects' list */
6321 case F_ClearList:
6322 ClearUndoList (false);
6323 break;
6326 notify_crosshair_change (true);
6327 return 0;
6330 /* --------------------------------------------------------------------------- */
6332 static const char redo_syntax[] = "Redo()";
6334 static const char redo_help[] = "Redo recent \"undo\" operations.";
6336 /* %start-doc actions Redo
6338 This routine allows you to recover from the last undo command. You
6339 might want to do this if you thought that undo was going to revert
6340 something other than what it actually did (in case you are confused
6341 about which operations are un-doable), or if you have been backing up
6342 through a long undo list and over-shoot your stopping point. Any
6343 change that is made since the undo in question will trim the redo
6344 list. For example if you add ten lines, then undo three of them you
6345 could use redo to put them back, but if you move a line on the board
6346 before performing the redo, you will lose the ability to "redo" the
6347 three "undone" lines.
6349 %end-doc */
6351 static int
6352 ActionRedo (int argc, char **argv, Coord x, Coord y)
6354 if (((Settings.Mode == POLYGON_MODE ||
6355 Settings.Mode == POLYGONHOLE_MODE) &&
6356 Crosshair.AttachedPolygon.PointN) ||
6357 Crosshair.AttachedLine.State == STATE_SECOND)
6358 return 1;
6359 notify_crosshair_change (false);
6360 if (Redo (true))
6362 SetChangedFlag (true);
6363 if (Settings.Mode == LINE_MODE &&
6364 Crosshair.AttachedLine.State != STATE_FIRST)
6366 LineType *line = g_list_last (CURRENT->Line)->data;
6367 Crosshair.AttachedLine.Point1.X =
6368 Crosshair.AttachedLine.Point2.X = line->Point2.X;
6369 Crosshair.AttachedLine.Point1.Y =
6370 Crosshair.AttachedLine.Point2.Y = line->Point2.Y;
6371 addedLines++;
6374 notify_crosshair_change (true);
6375 return 0;
6378 /* --------------------------------------------------------------------------- */
6380 static const char polygon_syntax[] = "Polygon(Close|PreviousPoint)";
6382 static const char polygon_help[] = "Some polygon related stuff.";
6384 /* %start-doc actions Polygon
6386 Polygons need a special action routine to make life easier.
6388 @table @code
6390 @item Close
6391 Creates the final segment of the polygon. This may fail if clipping
6392 to 45 degree lines is switched on, in which case a warning is issued.
6394 @item PreviousPoint
6395 Resets the newly entered corner to the previous one. The Undo action
6396 will call Polygon(PreviousPoint) when appropriate to do so.
6398 @end table
6400 %end-doc */
6402 static int
6403 ActionPolygon (int argc, char **argv, Coord x, Coord y)
6405 char *function = ARG (0);
6406 if (function && Settings.Mode == POLYGON_MODE)
6408 notify_crosshair_change (false);
6409 switch (GetFunctionID (function))
6411 /* close open polygon if possible */
6412 case F_Close:
6413 ClosePolygon ();
6414 break;
6416 /* go back to the previous point */
6417 case F_PreviousPoint:
6418 GoToPreviousPoint ();
6419 break;
6421 notify_crosshair_change (true);
6423 return 0;
6426 /* --------------------------------------------------------------------------- */
6428 static const char routestyle_syntax[] = "RouteStyle(1|2|3|4)";
6430 static const char routestyle_help[] =
6431 "Copies the indicated routing style into the current sizes.";
6433 /* %start-doc actions RouteStyle
6435 %end-doc */
6437 static int
6438 ActionRouteStyle (int argc, char **argv, Coord x, Coord y)
6440 char *str = ARG (0);
6441 RouteStyleType *rts;
6442 int number;
6444 if (str)
6446 number = atoi (str);
6447 if (number > 0 && number <= NUM_STYLES)
6449 rts = &PCB->RouteStyle[number - 1];
6450 SetLineSize (rts->Thick);
6451 SetViaSize (rts->Diameter, true);
6452 SetViaDrillingHole (rts->Hole, true);
6453 SetKeepawayWidth (rts->Keepaway);
6454 hid_action("RouteStylesChanged");
6457 return 0;
6461 /* --------------------------------------------------------------------------- */
6463 static const char moveobject_syntax[] = "MoveObject(X,Y,dim)";
6465 static const char moveobject_help[] = "Moves the object under the crosshair.";
6467 /* %start-doc actions MoveObject
6469 The @code{X} and @code{Y} are treated like @code{delta} is for many
6470 other objects. For each, if it's prefixed by @code{+} or @code{-},
6471 then that amount is relative. Otherwise, it's absolute. Units can be
6472 @code{mil} or @code{mm}; if unspecified, units are PCB's internal
6473 units, currently 1/100 mil.
6475 %end-doc */
6477 static int
6478 ActionMoveObject (int argc, char **argv, Coord x, Coord y)
6480 char *x_str = ARG (0);
6481 char *y_str = ARG (1);
6482 char *units = ARG (2);
6483 Coord nx, ny;
6484 bool absolute1, absolute2;
6485 void *ptr1, *ptr2, *ptr3;
6486 int type;
6488 ny = GetValue (y_str, units, &absolute1);
6489 nx = GetValue (x_str, units, &absolute2);
6491 type = SearchScreen (x, y, MOVE_TYPES, &ptr1, &ptr2, &ptr3);
6492 if (type == NO_TYPE)
6494 Message (_("Nothing found under crosshair\n"));
6495 return 1;
6497 if (absolute1)
6498 nx -= x;
6499 if (absolute2)
6500 ny -= y;
6501 Crosshair.AttachedObject.RubberbandN = 0;
6502 if (TEST_FLAG (RUBBERBANDFLAG, PCB))
6503 LookupRubberbandLines (type, ptr1, ptr2, ptr3);
6504 if (type == ELEMENT_TYPE)
6505 LookupRatLines (type, ptr1, ptr2, ptr3);
6506 MoveObjectAndRubberband (type, ptr1, ptr2, ptr3, nx, ny);
6507 SetChangedFlag (true);
6508 return 0;
6511 /* --------------------------------------------------------------------------- */
6513 static const char movetocurrentlayer_syntax[] =
6514 "MoveToCurrentLayer(Object|SelectedObjects)";
6516 static const char movetocurrentlayer_help[] =
6517 "Moves objects to the current layer.";
6519 /* %start-doc actions MoveToCurrentLayer
6521 Note that moving an element from a component layer to a solder layer,
6522 or from solder to component, won't automatically flip it. Use the
6523 @code{Flip()} action to do that.
6525 %end-doc */
6527 static int
6528 ActionMoveToCurrentLayer (int argc, char **argv, Coord x, Coord y)
6530 char *function = ARG (0);
6531 if (function)
6533 switch (GetFunctionID (function))
6535 case F_Object:
6537 int type;
6538 void *ptr1, *ptr2, *ptr3;
6540 gui->get_coords (_("Select an Object"), &x, &y);
6541 if ((type =
6542 SearchScreen (x, y, MOVETOLAYER_TYPES,
6543 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
6544 if (MoveObjectToLayer (type, ptr1, ptr2, ptr3, CURRENT, false))
6545 SetChangedFlag (true);
6546 break;
6549 case F_SelectedObjects:
6550 case F_Selected:
6551 if (MoveSelectedObjectsToLayer (CURRENT))
6552 SetChangedFlag (true);
6553 break;
6556 return 0;
6560 static const char setsame_syntax[] = "SetSame()";
6562 static const char setsame_help[] =
6563 "Sets current layer and sizes to match indicated item.";
6565 /* %start-doc actions SetSame
6567 When invoked over any line, arc, polygon, or via, this changes the
6568 current layer to be the layer that item is on, and changes the current
6569 sizes (thickness, keepaway, drill, etc) according to that item.
6571 %end-doc */
6573 static int
6574 ActionSetSame (int argc, char **argv, Coord x, Coord y)
6576 void *ptr1, *ptr2, *ptr3;
6577 int type;
6578 LayerType *layer = CURRENT;
6580 type = SearchScreen (x, y, CLONE_TYPES, &ptr1, &ptr2, &ptr3);
6581 /* set layer current and size from line or arc */
6582 switch (type)
6584 case LINE_TYPE:
6585 notify_crosshair_change (false);
6586 Settings.LineThickness = ((LineType *) ptr2)->Thickness;
6587 Settings.Keepaway = ((LineType *) ptr2)->Clearance / 2;
6588 layer = (LayerType *) ptr1;
6589 if (Settings.Mode != LINE_MODE)
6590 SetMode (LINE_MODE);
6591 notify_crosshair_change (true);
6592 hid_action ("RouteStylesChanged");
6593 break;
6595 case ARC_TYPE:
6596 notify_crosshair_change (false);
6597 Settings.LineThickness = ((ArcType *) ptr2)->Thickness;
6598 Settings.Keepaway = ((ArcType *) ptr2)->Clearance / 2;
6599 layer = (LayerType *) ptr1;
6600 if (Settings.Mode != ARC_MODE)
6601 SetMode (ARC_MODE);
6602 notify_crosshair_change (true);
6603 hid_action ("RouteStylesChanged");
6604 break;
6606 case POLYGON_TYPE:
6607 layer = (LayerType *) ptr1;
6608 break;
6610 case VIA_TYPE:
6611 notify_crosshair_change (false);
6612 Settings.ViaThickness = ((PinType *) ptr2)->Thickness;
6613 Settings.ViaDrillingHole = ((PinType *) ptr2)->DrillingHole;
6614 Settings.Keepaway = ((PinType *) ptr2)->Clearance / 2;
6615 if (Settings.Mode != VIA_MODE)
6616 SetMode (VIA_MODE);
6617 notify_crosshair_change (true);
6618 hid_action ("RouteStylesChanged");
6619 break;
6621 default:
6622 return 1;
6624 if (layer != CURRENT)
6626 ChangeGroupVisibility (GetLayerNumber (PCB->Data, layer), true, true);
6627 Redraw ();
6629 return 0;
6633 /* --------------------------------------------------------------------------- */
6635 static const char setflag_syntax[] =
6636 "SetFlag(Object|Selected|SelectedObjects, flag)\n"
6637 "SetFlag(SelectedLines|SelectedPins|SelectedVias, flag)\n"
6638 "SetFlag(SelectedPads|SelectedTexts|SelectedNames, flag)\n"
6639 "SetFlag(SelectedElements, flag)\n"
6640 "flag = square | octagon | thermal | join";
6642 static const char setflag_help[] = "Sets flags on objects.";
6644 /* %start-doc actions SetFlag
6646 Turns the given flag on, regardless of its previous setting. See
6647 @code{ChangeFlag}.
6649 @example
6650 SetFlag(SelectedPins,thermal)
6651 @end example
6653 %end-doc */
6655 static int
6656 ActionSetFlag (int argc, char **argv, Coord x, Coord y)
6658 char *function = ARG (0);
6659 char *flag = ARG (1);
6660 ChangeFlag (function, flag, 1, "SetFlag");
6661 return 0;
6664 /* --------------------------------------------------------------------------- */
6666 static const char clrflag_syntax[] =
6667 "ClrFlag(Object|Selected|SelectedObjects, flag)\n"
6668 "ClrFlag(SelectedLines|SelectedPins|SelectedVias, flag)\n"
6669 "ClrFlag(SelectedPads|SelectedTexts|SelectedNames, flag)\n"
6670 "ClrFlag(SelectedElements, flag)\n"
6671 "flag = square | octagon | thermal | join";
6673 static const char clrflag_help[] = "Clears flags on objects.";
6675 /* %start-doc actions ClrFlag
6677 Turns the given flag off, regardless of its previous setting. See
6678 @code{ChangeFlag}.
6680 @example
6681 ClrFlag(SelectedLines,join)
6682 @end example
6684 %end-doc */
6686 static int
6687 ActionClrFlag (int argc, char **argv, Coord x, Coord y)
6689 char *function = ARG (0);
6690 char *flag = ARG (1);
6691 ChangeFlag (function, flag, 0, "ClrFlag");
6692 return 0;
6695 /* --------------------------------------------------------------------------- */
6697 static const char changeflag_syntax[] =
6698 "ChangeFlag(Object|Selected|SelectedObjects, flag, value)\n"
6699 "ChangeFlag(SelectedLines|SelectedPins|SelectedVias, flag, value)\n"
6700 "ChangeFlag(SelectedPads|SelectedTexts|SelectedNames, flag, value)\n"
6701 "ChangeFlag(SelectedElements, flag, value)\n"
6702 "flag = square | octagon | thermal | join\n"
6703 "value = 0 | 1";
6705 static const char changeflag_help[] = "Sets or clears flags on objects.";
6707 /* %start-doc actions ChangeFlag
6709 Toggles the given flag on the indicated object(s). The flag may be
6710 one of the flags listed above (square, octagon, thermal, join). The
6711 value may be the number 0 or 1. If the value is 0, the flag is
6712 cleared. If the value is 1, the flag is set.
6714 %end-doc */
6716 static int
6717 ActionChangeFlag (int argc, char **argv, Coord x, Coord y)
6719 char *function = ARG (0);
6720 char *flag = ARG (1);
6721 int value = argc > 2 ? atoi (argv[2]) : -1;
6722 if (value != 0 && value != 1)
6723 AFAIL (changeflag);
6725 ChangeFlag (function, flag, value, "ChangeFlag");
6726 return 0;
6730 static void
6731 ChangeFlag (char *what, char *flag_name, int value, char *cmd_name)
6733 bool (*set_object) (int, void *, void *, void *);
6734 bool (*set_selected) (int);
6736 if (NSTRCMP (flag_name, "square") == 0)
6738 set_object = value ? SetObjectSquare : ClrObjectSquare;
6739 set_selected = value ? SetSelectedSquare : ClrSelectedSquare;
6741 else if (NSTRCMP (flag_name, "octagon") == 0)
6743 set_object = value ? SetObjectOctagon : ClrObjectOctagon;
6744 set_selected = value ? SetSelectedOctagon : ClrSelectedOctagon;
6746 else if (NSTRCMP (flag_name, "join") == 0)
6748 /* Note: these are backwards, because the flag is "clear" but
6749 the command is "join". */
6750 set_object = value ? ClrObjectJoin : SetObjectJoin;
6751 set_selected = value ? ClrSelectedJoin : SetSelectedJoin;
6753 else
6755 Message (_("%s(): Flag \"%s\" is not valid\n"), cmd_name, flag_name);
6756 return;
6759 switch (GetFunctionID (what))
6761 case F_Object:
6763 int type;
6764 void *ptr1, *ptr2, *ptr3;
6766 if ((type =
6767 SearchScreen (Crosshair.X, Crosshair.Y, CHANGESIZE_TYPES,
6768 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
6769 if (TEST_FLAG (LOCKFLAG, (PinType *) ptr2))
6770 Message (_("Sorry, the object is locked\n"));
6771 if (set_object (type, ptr1, ptr2, ptr3))
6772 SetChangedFlag (true);
6773 break;
6776 case F_SelectedVias:
6777 if (set_selected (VIA_TYPE))
6778 SetChangedFlag (true);
6779 break;
6781 case F_SelectedPins:
6782 if (set_selected (PIN_TYPE))
6783 SetChangedFlag (true);
6784 break;
6786 case F_SelectedPads:
6787 if (set_selected (PAD_TYPE))
6788 SetChangedFlag (true);
6789 break;
6791 case F_SelectedLines:
6792 if (set_selected (LINE_TYPE))
6793 SetChangedFlag (true);
6794 break;
6796 case F_SelectedTexts:
6797 if (set_selected (TEXT_TYPE))
6798 SetChangedFlag (true);
6799 break;
6801 case F_SelectedNames:
6802 if (set_selected (ELEMENTNAME_TYPE))
6803 SetChangedFlag (true);
6804 break;
6806 case F_SelectedElements:
6807 if (set_selected (ELEMENT_TYPE))
6808 SetChangedFlag (true);
6809 break;
6811 case F_Selected:
6812 case F_SelectedObjects:
6813 if (set_selected (CHANGESIZE_TYPES))
6814 SetChangedFlag (true);
6815 break;
6819 /* --------------------------------------------------------------------------- */
6821 static const char executefile_syntax[] = "ExecuteFile(filename)";
6823 static const char executefile_help[] = "Run actions from the given file.";
6825 /* %start-doc actions ExecuteFile
6827 Lines starting with @code{#} are ignored.
6829 %end-doc */
6831 static int
6832 ActionExecuteFile (int argc, char **argv, Coord x, Coord y)
6834 FILE *fp;
6835 char *fname;
6836 char line[256];
6837 int n = 0;
6838 char *sp;
6840 if (argc != 1)
6841 AFAIL (executefile);
6843 fname = argv[0];
6845 if ((fp = fopen (fname, "r")) == NULL)
6847 fprintf (stderr, _("Could not open actions file \"%s\".\n"), fname);
6848 return 1;
6851 defer_updates = 1;
6852 defer_needs_update = 0;
6853 while (fgets (line, sizeof (line), fp) != NULL)
6855 n++;
6856 sp = line;
6858 /* eat the trailing newline */
6859 while (*sp && *sp != '\r' && *sp != '\n')
6860 sp++;
6861 *sp = '\0';
6863 /* eat leading spaces and tabs */
6864 sp = line;
6865 while (*sp && (*sp == ' ' || *sp == '\t'))
6866 sp++;
6869 * if we have anything left and its not a comment line
6870 * then execute it
6873 if (*sp && *sp != '#')
6875 /*Message ("%s : line %-3d : \"%s\"\n", fname, n, sp);*/
6876 hid_parse_actions (sp);
6880 defer_updates = 0;
6881 if (defer_needs_update)
6883 IncrementUndoSerialNumber ();
6884 gui->invalidate_all ();
6886 fclose (fp);
6887 return 0;
6890 /* --------------------------------------------------------------------------- */
6892 static int
6893 ActionPSCalib (int argc, char **argv, Coord x, Coord y)
6895 HID *ps = hid_find_exporter ("ps");
6896 ps->calibrate (0.0,0.0);
6897 return 0;
6900 /* --------------------------------------------------------------------------- */
6902 static ElementType *element_cache = NULL;
6904 static ElementType *
6905 find_element_by_refdes (char *refdes)
6907 if (element_cache
6908 && NAMEONPCB_NAME(element_cache)
6909 && strcmp (NAMEONPCB_NAME(element_cache), refdes) == 0)
6910 return element_cache;
6912 ELEMENT_LOOP (PCB->Data);
6914 if (NAMEONPCB_NAME(element)
6915 && strcmp (NAMEONPCB_NAME(element), refdes) == 0)
6917 element_cache = element;
6918 return element_cache;
6921 END_LOOP;
6922 return NULL;
6925 static AttributeType *
6926 lookup_attr (AttributeListType *list, const char *name)
6928 int i;
6929 for (i=0; i<list->Number; i++)
6930 if (strcmp (list->List[i].name, name) == 0)
6931 return & list->List[i];
6932 return NULL;
6935 static void
6936 delete_attr (AttributeListType *list, AttributeType *attr)
6938 int idx = attr - list->List;
6939 if (idx < 0 || idx >= list->Number)
6940 return;
6941 if (list->Number - idx > 1)
6942 memmove (attr, attr+1, (list->Number - idx - 1) * sizeof(AttributeType));
6943 list->Number --;
6946 /* ---------------------------------------------------------------- */
6947 static const char elementlist_syntax[] = "ElementList(Start|Done)\n"
6948 "ElementList(Need,<refdes>,<footprint>,<value>[,ForceUpdate])";
6950 static const char elementlist_help[] = "Adds the given element if it doesn't already exist.";
6952 /* %start-doc actions ElementList
6954 @table @code
6956 @item Start
6957 Indicates the start of an element list; call this before any Need
6958 actions.
6960 @item Need
6961 Searches the board for an element with a matching refdes.
6963 If found, the value and footprint are updated.
6965 If not found, a new element is created with the given footprint and value.
6967 @item ForceUpdate
6968 If set, updates the value and footprint even if neither have changed. This
6969 can be useful to pull in minor footprint updates.
6971 @item Done
6972 Compares the list of elements needed since the most recent
6973 @code{start} with the list of elements actually on the board. Any
6974 elements that weren't listed are selected, so that the user may delete
6975 them.
6977 @end table
6979 %end-doc */
6981 static int number_of_footprints_not_found;
6983 static int
6984 parse_layout_attribute_units (char *name, int def)
6986 const char *as = AttributeGet (PCB, name);
6987 if (!as)
6988 return def;
6989 return GetValue (as, NULL, NULL);
6992 static int
6993 ActionElementList (int argc, char **argv, Coord x, Coord y)
6995 ElementType *e = NULL;
6996 char *refdes, *value, *footprint, *forceupdate=NULL, *old;
6997 char *args[3];
6998 char *function = argv[0];
7000 #ifdef DEBUG
7001 printf("Entered ActionElementList, executing function %s\n", function);
7002 #endif
7004 if (strcasecmp (function, "start") == 0)
7006 ELEMENT_LOOP (PCB->Data);
7008 CLEAR_FLAG (FOUNDFLAG, element);
7010 END_LOOP;
7011 element_cache = NULL;
7012 number_of_footprints_not_found = 0;
7013 return 0;
7016 if (strcasecmp (function, "done") == 0)
7018 ELEMENT_LOOP (PCB->Data);
7020 if (TEST_FLAG (FOUNDFLAG, element))
7022 CLEAR_FLAG (FOUNDFLAG, element);
7024 else if (! EMPTY_STRING_P (NAMEONPCB_NAME (element)))
7026 /* Unnamed elements should remain untouched */
7027 SET_FLAG (SELECTEDFLAG, element);
7030 END_LOOP;
7031 if (number_of_footprints_not_found > 0)
7032 gui->confirm_dialog (_("Not all requested footprints were found.\n"
7033 "See the message log for details"), 0);
7034 return 0;
7037 if (strcasecmp (function, "need") != 0)
7038 AFAIL (elementlist);
7040 if (argc < 4 || argc > 5)
7041 AFAIL (elementlist);
7043 argc --;
7044 argv ++;
7046 refdes = ARG(0);
7047 footprint = ARG(1);
7048 value = ARG(2);
7049 forceupdate = ARG(3);
7051 args[0] = footprint;
7052 args[1] = refdes;
7053 args[2] = value;
7055 #ifdef DEBUG
7056 printf(" ... footprint = %s\n", footprint);
7057 printf(" ... refdes = %s\n", refdes);
7058 printf(" ... value = %s\n", value);
7059 printf(" ... forceupdate = %s\n", forceupdate);
7060 #endif
7062 e = find_element_by_refdes (refdes);
7064 /* reload footprints from disk in case they changed */
7065 clear_footprint_hash ();
7067 if (!e)
7069 Coord nx, ny, d;
7071 #ifdef DEBUG
7072 printf(" ... Footprint not on board, need to add it.\n");
7073 #endif
7074 /* Not on board, need to add it. */
7075 if (LoadFootprint(argc, args, x, y))
7077 number_of_footprints_not_found ++;
7078 return 1;
7081 nx = PCB->MaxWidth / 2;
7082 ny = PCB->MaxHeight / 2;
7083 d = MIN (PCB->MaxWidth, PCB->MaxHeight) / 10;
7085 nx = parse_layout_attribute_units ("import::newX", nx);
7086 ny = parse_layout_attribute_units ("import::newY", ny);
7087 d = parse_layout_attribute_units ("import::disperse", d);
7089 if (d > 0)
7091 nx += rand () % (d*2) - d;
7092 ny += rand () % (d*2) - d;
7095 if (nx < 0)
7096 nx = 0;
7097 if (nx >= PCB->MaxWidth)
7098 nx = PCB->MaxWidth - 1;
7099 if (ny < 0)
7100 ny = 0;
7101 if (ny >= PCB->MaxHeight)
7102 ny = PCB->MaxHeight - 1;
7104 /* Place components onto center of board. */
7105 if (CopyPastebufferToLayout (nx, ny))
7106 SetChangedFlag (true);
7109 else if (e && ((strcmp (DESCRIPTION_NAME(e), footprint) != 0)
7110 || (forceupdate && (strcasecmp (forceupdate, "forceupdate") == 0))))
7112 #ifdef DEBUG
7113 printf(" ... Footprint on board, but different from footprint loaded.\n");
7114 #endif
7115 if (forceupdate && (strcasecmp (forceupdate, "forceupdate") == 0)) {
7116 Message ("Force-updated %s %s %s\n", refdes, value, footprint);
7118 int er, pr, i;
7119 Coord mx, my;
7120 ElementType *pe;
7122 /* Different footprint, we need to swap them out. */
7123 if (LoadFootprint(argc, args, x, y))
7125 number_of_footprints_not_found ++;
7126 return 1;
7129 er = ElementOrientation (e);
7130 pe = PASTEBUFFER->Data->Element->data;
7131 if (!FRONT (e))
7132 MirrorElementCoordinates (PASTEBUFFER->Data, pe, pe->MarkY*2 - PCB->MaxHeight);
7133 pr = ElementOrientation (pe);
7135 mx = e->MarkX;
7136 my = e->MarkY;
7138 if (er != pr)
7139 RotateElementLowLevel (PASTEBUFFER->Data, pe, pe->MarkX, pe->MarkY, (er-pr+4)%4);
7141 for (i=0; i<MAX_ELEMENTNAMES; i++)
7143 pe->Name[i].X = e->Name[i].X - mx + pe->MarkX ;
7144 pe->Name[i].Y = e->Name[i].Y - my + pe->MarkY ;
7145 pe->Name[i].Direction = e->Name[i].Direction;
7146 pe->Name[i].Scale = e->Name[i].Scale;
7149 RemoveElement (e);
7151 if (CopyPastebufferToLayout (mx, my))
7152 SetChangedFlag (true);
7155 /* Now reload footprint */
7156 element_cache = NULL;
7157 e = find_element_by_refdes (refdes);
7159 old = ChangeElementText (PCB, PCB->Data, e, NAMEONPCB_INDEX, strdup (refdes));
7160 if (old)
7161 free(old);
7162 old = ChangeElementText (PCB, PCB->Data, e, VALUE_INDEX, strdup (value));
7163 if (old)
7164 free(old);
7166 SET_FLAG (FOUNDFLAG, e);
7168 #ifdef DEBUG
7169 printf(" ... Leaving ActionElementList.\n");
7170 #endif
7172 return 0;
7177 /* ---------------------------------------------------------------- */
7178 static const char updatepackage_syntax[] = "UpdatePackage(Selected|All)";
7180 static const char updatepackage_help[] = "Reloads the indicated element packages.";
7182 /* %start-doc actions UpdatePackage
7184 @table @code
7186 @item Selected
7187 Only updates the footprint of the selected elements from the currently-loaded
7188 library.
7190 @item All
7191 Reloads from the currently-loaded library the packages of all elements in the
7192 layout.
7194 @end table
7196 Extracts the footprint name to load from the element's @code{Desc} field.
7197 Preserves the old element's Desc, Name, and Value fields.
7199 %end-doc */
7201 static int update_footprints_not_found;
7203 static int
7204 ActionUpdatePackage (int argc, char **argv, Coord x, Coord y)
7206 char *function = NULL;
7207 int fnid;
7208 int er, pr;
7209 ElementType *pe = NULL;
7210 Coord mx, my;
7211 int i;
7212 char *old;
7214 function = ARG(0);
7215 update_footprints_not_found = 0;
7217 #ifdef DEBUG
7218 printf("Entered ActionUpdatePackage, executing function %s\n", function);
7219 #endif
7221 if (function)
7223 fnid = GetFunctionID (function);
7224 if ( !(fnid == F_All || fnid == F_Selected))
7225 AFAIL (updatepackage);
7227 else
7229 AFAIL (updatepackage);
7232 /* Select all elements for All mode */
7233 if (fnid == F_All)
7234 SelectObjectByName (ELEMENT_TYPE, ".*", true);
7236 /* Flag all original elements, we are adding more but don't want to process
7237 * the new ones*/
7238 ELEMENT_LOOP (PCB->Data);
7240 SET_FLAG(VISITFLAG, element);
7242 END_LOOP;
7245 ELEMENT_LOOP (PCB->Data);
7247 /* All elements (from here on ??) were added in previous iterations of this
7248 * loop; we are done. */
7249 if (!TEST_FLAG(VISITFLAG, element))
7250 break;
7252 /* Only touch selected elements. */
7253 if (!TEST_FLAG (SELECTEDFLAG, element))
7254 continue;
7256 /* no element name, it's likely not something we wish to change skip */
7257 if (EMPTY_STRING_P (NAMEONPCB_NAME (element)))
7258 continue;
7260 if (EMPTY_STRING_P (DESCRIPTION_NAME (element)))
7262 Message (_("Skipping refdes %s, no footprint name\n"), NAMEONPCB_NAME (element));
7263 continue;
7266 if (EMPTY_STRING_P (VALUE_NAME (element)))
7268 Message (_("For refdes %s, no value\n"), NAMEONPCB_NAME (element));
7271 /* to get here, this is a footprint element (??) */
7273 /* do not touch element if not in current library */
7274 if (LoadFootprint(1, &DESCRIPTION_NAME(element), x, y))
7276 update_footprints_not_found ++;
7277 Message (_("Footprint not found: %s\n"), DESCRIPTION_NAME(element));
7278 continue;
7281 /* all good, now update the package */
7282 Message (_("Updating footprint for (%s, %s, %s).\n"),
7283 NAMEONPCB_NAME(element),
7284 VALUE_NAME(element),
7285 DESCRIPTION_NAME(element)
7288 /* in the method of ElementList(Need,...) */
7289 pe = PASTEBUFFER->Data->Element->data;
7290 if (!FRONT (element))
7291 MirrorElementCoordinates (PASTEBUFFER->Data, pe, pe->MarkY*2 - PCB->MaxHeight);
7293 er = ElementOrientation (element);
7294 pr = ElementOrientation (pe);
7295 if (er != pr)
7296 RotateElementLowLevel (PASTEBUFFER->Data, pe, pe->MarkX, pe->MarkY, (er-pr+4)%4);
7298 /* move element parts to their previous locations */
7299 mx = element->MarkX;
7300 my = element->MarkY;
7301 for (i=0; i<MAX_ELEMENTNAMES; i++)
7303 pe->Name[i].X = element->Name[i].X - mx + pe->MarkX ;
7304 pe->Name[i].Y = element->Name[i].Y - my + pe->MarkY ;
7305 pe->Name[i].Direction = element->Name[i].Direction;
7306 pe->Name[i].Scale = element->Name[i].Scale;
7309 /* preserve attributes */
7310 for (i=0; i<element->Attributes.Number; i++)
7312 CreateNewAttribute (& pe->Attributes,
7313 element->Attributes.List[i].name,
7314 element->Attributes.List[i].value);
7317 /* preserve name, refdes, and value */
7318 old = ChangeElementText (PCB, PASTEBUFFER->Data, pe, DESCRIPTION_INDEX,
7319 strdup (DESCRIPTION_NAME(element)));
7320 if (old) free(old);
7321 old = ChangeElementText (PCB, PASTEBUFFER->Data, pe, NAMEONPCB_INDEX,
7322 strdup (NAMEONPCB_NAME(element)));
7323 if (old) free(old);
7324 old = ChangeElementText (PCB, PASTEBUFFER->Data, pe, VALUE_INDEX,
7325 strdup (VALUE_NAME(element)));
7326 if (old) free(old);
7328 /* who needs old data? */
7329 RemoveElement (element);
7331 /* place in layout */
7332 if (CopyPastebufferToLayout (mx, my))
7333 SetChangedFlag (true);
7336 END_LOOP;
7338 if (update_footprints_not_found > 0)
7340 gui->confirm_dialog (_("Not all requested footprints were found.\n"
7341 "See the message log for details"), 0);
7344 #ifdef DEBUG
7345 printf(" ... Leaving ActionUpdatePackage.\n");
7346 #endif
7347 return 0;
7351 /* ---------------------------------------------------------------- */
7352 static const char elementsetattr_syntax[] = "ElementSetAttr(refdes,name[,value])";
7354 static const char elementsetattr_help[] = "Sets or clears an element-specific attribute.";
7356 /* %start-doc actions ElementSetAttr
7358 If a value is specified, the named attribute is added (if not already
7359 present) or changed (if it is) to the given value. If the value is
7360 not specified, the given attribute is removed if present.
7362 %end-doc */
7364 static int
7365 ActionElementSetAttr (int argc, char **argv, Coord x, Coord y)
7367 ElementType *e = NULL;
7368 char *refdes, *name, *value;
7369 AttributeType *attr;
7371 if (argc < 2)
7373 AFAIL (changepinname);
7376 refdes = argv[0];
7377 name = argv[1];
7378 value = ARG(2);
7380 ELEMENT_LOOP (PCB->Data);
7382 if (NSTRCMP (refdes, NAMEONPCB_NAME (element)) == 0)
7384 e = element;
7385 break;
7388 END_LOOP;
7390 if (!e)
7392 Message(_("Cannot change attribute of %s - element not found\n"), refdes);
7393 return 1;
7396 attr = lookup_attr (&e->Attributes, name);
7398 if (attr && value)
7400 free (attr->value);
7401 attr->value = strdup (value);
7403 if (attr && ! value)
7405 delete_attr (& e->Attributes, attr);
7407 if (!attr && value)
7409 CreateNewAttribute (& e->Attributes, name, value);
7412 return 0;
7415 /* ---------------------------------------------------------------- */
7416 static const char execcommand_syntax[] = "ExecCommand(command)";
7418 static const char execcommand_help[] = "Runs a command.";
7420 /* %start-doc actions ExecCommand
7422 Runs the given command, which is a system executable.
7424 %end-doc */
7426 static int
7427 ActionExecCommand (int argc, char **argv, Coord x, Coord y)
7429 char *command;
7431 if (argc < 1)
7433 AFAIL (execcommand);
7436 command = ARG(0);
7438 if (system (command))
7439 return 1;
7440 return 0;
7443 /* ---------------------------------------------------------------- */
7445 static int
7446 pcb_spawnvp (char **argv)
7448 #ifdef HAVE__SPAWNVP
7449 int result = _spawnvp (_P_WAIT, argv[0], (const char * const *) argv);
7450 if (result == -1)
7451 return 1;
7452 else
7453 return 0;
7454 #else
7455 int pid;
7456 pid = fork ();
7457 if (pid < 0)
7459 /* error */
7460 Message(_("Cannot fork!"));
7461 return 1;
7463 else if (pid == 0)
7465 /* Child */
7466 execvp (argv[0], argv);
7467 exit(1);
7469 else
7471 int rv;
7472 /* Parent */
7473 wait (&rv);
7475 return 0;
7476 #endif
7479 /* ---------------------------------------------------------------- */
7481 * Creates a new temporary file name. Hopefully the operating system
7482 * provides a mkdtemp() function to securily create a temporary
7483 * directory with mode 0700. If so then that directory is created and
7484 * the returned string is made up of the directory plus the name
7485 * variable. For example:
7487 * tempfile_name_new ("myfile") might return
7488 * "/var/tmp/pcb.123456/myfile".
7490 * If mkdtemp() is not available then 'name' is ignored and the
7491 * insecure tmpnam() function is used.
7493 * Files/names created with tempfile_name_new() should be unlinked
7494 * with tempfile_unlink to make sure the temporary directory is also
7495 * removed when mkdtemp() is used.
7497 static char *
7498 tempfile_name_new (char * name)
7500 char *tmpfile = NULL;
7501 #ifdef HAVE_MKDTEMP
7502 char *tmpdir, *mytmpdir;
7503 size_t len;
7504 #endif
7506 assert ( name != NULL );
7508 #ifdef HAVE_MKDTEMP
7509 #define TEMPLATE "pcb.XXXXXXXX"
7512 tmpdir = getenv ("TMPDIR");
7514 /* FIXME -- what about win32? */
7515 if (tmpdir == NULL) {
7516 tmpdir = "/tmp";
7519 mytmpdir = (char *) malloc (sizeof(char) *
7520 (strlen (tmpdir) +
7522 strlen (TEMPLATE) +
7523 1));
7524 if (mytmpdir == NULL) {
7525 fprintf (stderr, "%s(): malloc failed()\n", __FUNCTION__);
7526 exit (1);
7529 *mytmpdir = '\0';
7530 (void)strcat (mytmpdir, tmpdir);
7531 (void)strcat (mytmpdir, PCB_DIR_SEPARATOR_S);
7532 (void)strcat (mytmpdir, TEMPLATE);
7533 if (mkdtemp (mytmpdir) == NULL) {
7534 fprintf (stderr, "%s(): mkdtemp (\"%s\") failed\n", __FUNCTION__, mytmpdir);
7535 free (mytmpdir);
7536 return NULL;
7540 len = strlen (mytmpdir) + /* the temp directory name */
7541 1 + /* the directory sep. */
7542 strlen (name) + /* the file name */
7543 1 /* the \0 termination */
7546 tmpfile = (char *) malloc (sizeof (char) * len);
7548 *tmpfile = '\0';
7549 (void)strcat (tmpfile, mytmpdir);
7550 (void)strcat (tmpfile, PCB_DIR_SEPARATOR_S);
7551 (void)strcat (tmpfile, name);
7553 free (mytmpdir);
7554 #undef TEMPLATE
7555 #else
7557 * tmpnam() uses a static buffer so strdup() the result right away
7558 * in case someone decides to create multiple temp names.
7560 tmpfile = strdup (tmpnam (NULL));
7561 #ifdef __WIN32__
7563 /* Guile doesn't like \ separators */
7564 char *c;
7565 for (c = tmpfile; *c; c++)
7566 if (*c == '\\')
7567 *c = '/';
7569 #endif
7570 #endif
7572 return tmpfile;
7575 /* ---------------------------------------------------------------- */
7577 * Unlink a temporary file. If we have mkdtemp() then our temp file
7578 * lives in a temporary directory and we need to remove that directory
7579 * too.
7581 static int
7582 tempfile_unlink (char * name)
7584 #ifdef DEBUG
7585 /* SDB says: Want to keep old temp files for examiniation when debugging */
7586 return 0;
7587 #endif
7589 #ifdef HAVE_MKDTEMP
7590 int e, rc2 = 0;
7591 char *dname;
7593 unlink (name);
7594 /* it is possible that the file was never created so it is OK if the
7595 unlink fails */
7597 /* now figure out the directory name to remove */
7598 e = strlen (name) - 1;
7599 while (e > 0 && name[e] != PCB_DIR_SEPARATOR_C) {e--;}
7601 dname = strdup (name);
7602 dname[e] = '\0';
7605 * at this point, e *should* point to the end of the directory part
7606 * but lets make sure.
7608 if (e > 0) {
7609 rc2 = rmdir (dname);
7610 if (rc2 != 0) {
7611 perror (dname);
7614 } else {
7615 fprintf (stderr, _("%s(): Unable to determine temp directory name from the temp file\n"),
7616 __FUNCTION__);
7617 fprintf (stderr, "%s(): \"%s\"\n",
7618 __FUNCTION__, name);
7619 rc2 = -1;
7622 /* name was allocated with malloc */
7623 free (dname);
7624 free (name);
7627 * FIXME - should also return -1 if the temp file exists and was not
7628 * removed.
7630 if (rc2 != 0) {
7631 return -1;
7634 #else
7635 int rc = unlink (name);
7637 if (rc != 0) {
7638 fprintf (stderr, _("Failed to unlink \"%s\"\n"), name);
7639 free (name);
7640 return rc;
7642 free (name);
7644 #endif
7646 return 0;
7649 /* ---------------------------------------------------------------- */
7650 static const char import_syntax[] =
7651 "Import()\n"
7652 "Import([gnetlist|make[,source,source,...]])\n"
7653 "Import(setnewpoint[,(mark|center|X,Y)])\n"
7654 "Import(setdisperse,D,units)\n";
7656 static const char import_help[] = "Import schematics.";
7658 /* %start-doc actions Import
7660 Imports element and netlist data from the schematics (or some other
7661 source). The first parameter, which is optional, is the mode. If not
7662 specified, the @code{import::mode} attribute in the PCB is used.
7663 @code{gnetlist} means gnetlist is used to obtain the information from
7664 the schematics. @code{make} invokes @code{make}, assuming the user
7665 has a @code{Makefile} in the current directory. The @code{Makefile}
7666 will be invoked with the following variables set:
7668 @table @code
7670 @item PCB
7671 The name of the .pcb file
7673 @item SRCLIST
7674 A space-separated list of source files
7676 @item OUT
7677 The name of the file in which to put the command script, which may
7678 contain any @pcb{} actions. By default, this is a temporary file
7679 selected by @pcb{}, but if you specify an @code{import::outfile}
7680 attribute, that file name is used instead (and not automatically
7681 deleted afterwards).
7683 @end table
7685 The target specified to be built is the first of these that apply:
7687 @itemize @bullet
7689 @item
7690 The target specified by an @code{import::target} attribute.
7692 @item
7693 The output file specified by an @code{import::outfile} attribute.
7695 @item
7696 If nothing else is specified, the target is @code{pcb_import}.
7698 @end itemize
7700 If you specify an @code{import::makefile} attribute, then "-f <that
7701 file>" will be added to the command line.
7703 If you specify the mode, you may also specify the source files
7704 (schematics). If you do not specify any, the list of schematics is
7705 obtained by reading the @code{import::src@var{N}} attributes (like
7706 @code{import::src0}, @code{import::src1}, etc).
7708 For compatibility with future extensions to the import file format,
7709 the generated file @emph{must not} start with the two characters
7710 @code{#%}.
7712 If a temporary file is needed the @code{TMPDIR} environment variable
7713 is used to select its location.
7715 Note that the programs @code{gnetlist} and @code{make} may be
7716 overridden by the user via the @code{make-program} and @code{gnetlist}
7717 @code{pcb} settings (i.e. in @code{~/.pcb/settings} or on the command
7718 line).
7720 If @pcb{} cannot determine which schematic(s) to import from, the GUI
7721 is called to let user choose (see @code{ImportGUI()}).
7723 Note that Import() doesn't delete anything - after an Import, elements
7724 which shouldn't be on the board are selected and may be removed once
7725 it's determined that the deletion is appropriate.
7727 If @code{Import()} is called with @code{setnewpoint}, then the location
7728 of new components can be specified. This is where parts show up when
7729 they're added to the board. The default is the center of the board.
7731 @table @code
7733 @item Import(setnewpoint)
7735 Prompts the user to click on the board somewhere, uses that point. If
7736 called by a hotkey, uses the current location of the crosshair.
7738 @item Import(setnewpoint,mark)
7740 Uses the location of the mark. If no mark is present, the point is
7741 not changed.
7743 @item Import(setnewpoint,center)
7745 Resets the point to the center of the board.
7747 @item Import(setnewpoint,X,Y,units)
7749 Sets the point to the specific coordinates given. Example:
7750 @code{Import(setnewpoint,50,25,mm)}
7752 @end table
7754 Note that the X and Y locations are stored in attributes named
7755 @code{import::newX} and @code{import::newY} so you could change them
7756 manually if you wished.
7758 Calling @code{Import(setdisperse,D,units)} sets how much the newly
7759 placed elements are dispersed relative to the set point. For example,
7760 @code{Import(setdisperse,10,mm)} will offset each part randomly up to
7761 10mm away from the point. The default dispersion is 1/10th of the
7762 smallest board dimension. Dispersion is saved in the
7763 @code{import::disperse} attribute.
7765 %end-doc */
7767 static int
7768 ActionImport (int argc, char **argv, Coord x, Coord y)
7770 char *mode;
7771 char **sources = NULL;
7772 int nsources = 0;
7774 #ifdef DEBUG
7775 printf("ActionImport: =========== Entering ActionImport ============\n");
7776 #endif
7778 mode = ARG (0);
7780 if (mode && strcasecmp (mode, "setdisperse") == 0)
7782 char *ds, *units;
7783 char buf[50];
7785 ds = ARG (1);
7786 units = ARG (2);
7787 if (!ds)
7789 const char *as = AttributeGet (PCB, "import::disperse");
7790 ds = gui->prompt_for(_("Enter dispersion:"), as ? as : "0");
7792 if (units)
7794 sprintf(buf, "%s%s", ds, units);
7795 AttributePut (PCB, "import::disperse", buf);
7797 else
7798 AttributePut (PCB, "import::disperse", ds);
7799 if (ARG (1) == NULL)
7800 free (ds);
7801 return 0;
7804 if (mode && strcasecmp (mode, "setnewpoint") == 0)
7806 const char *xs, *ys, *units;
7807 Coord x, y;
7808 char buf[50];
7810 xs = ARG (1);
7811 ys = ARG (2);
7812 units = ARG (3);
7814 if (!xs)
7816 gui->get_coords (_("Click on a location"), &x, &y);
7818 else if (strcasecmp (xs, "center") == 0)
7820 AttributeRemove (PCB, "import::newX");
7821 AttributeRemove (PCB, "import::newY");
7822 return 0;
7824 else if (strcasecmp (xs, "mark") == 0)
7826 if (!Marked.status)
7827 return 0;
7829 x = Marked.X;
7830 y = Marked.Y;
7832 else if (ys)
7834 x = GetValue (xs, units, NULL);
7835 y = GetValue (ys, units, NULL);
7837 else
7839 Message (_("Bad syntax for Import(setnewpoint)"));
7840 return 1;
7843 pcb_sprintf (buf, "%$ms", x);
7844 AttributePut (PCB, "import::newX", buf);
7845 pcb_sprintf (buf, "%$ms", y);
7846 AttributePut (PCB, "import::newY", buf);
7847 return 0;
7850 if (! mode)
7851 mode = AttributeGet (PCB, "import::mode");
7852 if (! mode)
7853 mode = "gnetlist";
7855 if (argc > 1)
7857 sources = argv + 1;
7858 nsources = argc - 1;
7861 if (! sources)
7863 char sname[40];
7864 char *src;
7866 nsources = -1;
7867 do {
7868 nsources ++;
7869 sprintf(sname, "import::src%d", nsources);
7870 src = AttributeGet (PCB, sname);
7871 } while (src);
7873 if (nsources > 0)
7875 sources = (char **) malloc ((nsources + 1) * sizeof (char *));
7876 nsources = -1;
7877 do {
7878 nsources ++;
7879 sprintf(sname, "import::src%d", nsources);
7880 src = AttributeGet (PCB, sname);
7881 sources[nsources] = src;
7882 } while (src);
7886 if (! sources)
7888 /* Replace .pcb with .sch and hope for the best. */
7889 char *pcbname = PCB->Filename;
7890 char *schname;
7891 char *dot, *slash, *bslash;
7893 if (!pcbname)
7894 return hid_action("ImportGUI");
7896 schname = (char *) malloc (strlen(pcbname) + 5);
7897 strcpy (schname, pcbname);
7898 dot = strchr (schname, '.');
7899 slash = strchr (schname, '/');
7900 bslash = strchr (schname, '\\');
7901 if (dot && slash && dot < slash)
7902 dot = NULL;
7903 if (dot && bslash && dot < bslash)
7904 dot = NULL;
7905 if (dot)
7906 *dot = 0;
7907 strcat (schname, ".sch");
7909 if (access (schname, F_OK))
7911 free (schname);
7912 return hid_action("ImportGUI");
7915 sources = (char **) malloc (2 * sizeof (char *));
7916 sources[0] = schname;
7917 sources[1] = NULL;
7918 nsources = 1;
7921 if (strcasecmp (mode, "gnetlist") == 0)
7923 char *tmpfile = tempfile_name_new ("gnetlist_output");
7924 char **cmd;
7925 int i;
7927 if (tmpfile == NULL) {
7928 Message (_("Could not create temp file"));
7929 return 1;
7932 cmd = (char **) malloc ((7 + nsources) * sizeof (char *));
7933 cmd[0] = Settings.GnetlistProgram;
7934 cmd[1] = "-g";
7935 cmd[2] = "pcbfwd";
7936 cmd[3] = "-o";
7937 cmd[4] = tmpfile;
7938 cmd[5] = "--";
7939 for (i=0; i<nsources; i++)
7940 cmd[6+i] = sources[i];
7941 cmd[6+nsources] = NULL;
7943 #ifdef DEBUG
7944 printf("ActionImport: =========== About to run gnetlist ============\n");
7945 printf("%s %s %s %s %s %s %s ...\n",
7946 cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6]);
7947 #endif
7949 if (pcb_spawnvp (cmd))
7951 unlink (tmpfile);
7952 return 1;
7955 #ifdef DEBUG
7956 printf("ActionImport: =========== About to run ActionExecuteFile, file = %s ============\n", tmpfile);
7957 #endif
7959 cmd[0] = tmpfile;
7960 cmd[1] = NULL;
7961 ActionExecuteFile (1, cmd, 0, 0);
7963 free (cmd);
7964 tempfile_unlink (tmpfile);
7966 else if (strcasecmp (mode, "make") == 0)
7968 int must_free_tmpfile = 0;
7969 char *tmpfile;
7970 char *cmd[10];
7971 int i;
7972 char *srclist;
7973 int srclen;
7974 char *user_outfile = NULL;
7975 char *user_makefile = NULL;
7976 char *user_target = NULL;
7979 user_outfile = AttributeGet (PCB, "import::outfile");
7980 user_makefile = AttributeGet (PCB, "import::makefile");
7981 user_target = AttributeGet (PCB, "import::target");
7982 if (user_outfile && !user_target)
7983 user_target = user_outfile;
7985 if (user_outfile)
7986 tmpfile = user_outfile;
7987 else
7989 tmpfile = tempfile_name_new ("gnetlist_output");
7990 if (tmpfile == NULL) {
7991 Message (_("Could not create temp file"));
7992 return 1;
7994 must_free_tmpfile = 1;
7997 srclen = sizeof("SRCLIST=") + 2;
7998 for (i=0; i<nsources; i++)
7999 srclen += strlen (sources[i]) + 2;
8000 srclist = (char *) malloc (srclen);
8001 strcpy (srclist, "SRCLIST=");
8002 for (i=0; i<nsources; i++)
8004 if (i)
8005 strcat (srclist, " ");
8006 strcat (srclist, sources[i]);
8009 cmd[0] = Settings.MakeProgram;
8010 cmd[1] = "-s";
8011 cmd[2] = Concat ("PCB=", PCB->Filename, NULL);
8012 cmd[3] = srclist;
8013 cmd[4] = Concat ("OUT=", tmpfile, NULL);
8014 i = 5;
8015 if (user_makefile)
8017 cmd[i++] = "-f";
8018 cmd[i++] = user_makefile;
8020 cmd[i++] = user_target ? user_target : (char *)"pcb_import";
8021 cmd[i++] = NULL;
8023 if (pcb_spawnvp (cmd))
8025 if (must_free_tmpfile)
8026 unlink (tmpfile);
8027 free (cmd[2]);
8028 free (cmd[3]);
8029 free (cmd[4]);
8030 return 1;
8033 cmd[0] = tmpfile;
8034 cmd[1] = NULL;
8035 ActionExecuteFile (1, cmd, 0, 0);
8037 free (cmd[2]);
8038 free (cmd[3]);
8039 free (cmd[4]);
8040 if (must_free_tmpfile)
8041 tempfile_unlink (tmpfile);
8043 else
8045 Message (_("Unknown import mode: %s\n"), mode);
8046 return 1;
8049 DeleteRats (false);
8050 AddAllRats (false, NULL);
8052 #ifdef DEBUG
8053 printf("ActionImport: =========== Leaving ActionImport ============\n");
8054 #endif
8056 return 0;
8059 /* ------------------------------------------------------------ */
8061 static const char attributes_syntax[] =
8062 "Attributes(Layout|Layer|Element)\n"
8063 "Attributes(Layer,layername)";
8065 static const char attributes_help[] =
8066 "Let the user edit the attributes of the layout, current or given\n"
8067 "layer, or selected element.";
8069 /* %start-doc actions Attributes
8071 This just pops up a dialog letting the user edit the attributes of the
8072 pcb, an element, or a layer.
8074 %end-doc */
8077 static int
8078 ActionAttributes (int argc, char **argv, Coord x, Coord y)
8080 char *function = ARG (0);
8081 char *layername = ARG (1);
8082 char *buf;
8084 if (!function)
8085 AFAIL (attributes);
8087 if (!gui->edit_attributes)
8089 Message (_("This GUI doesn't support Attribute Editing\n"));
8090 return 1;
8093 switch (GetFunctionID (function))
8095 case F_Layout:
8097 gui->edit_attributes("Layout Attributes", &(PCB->Attributes));
8098 return 0;
8101 case F_Layer:
8103 LayerType *layer = CURRENT;
8104 if (layername)
8106 int i;
8107 layer = NULL;
8108 for (i=0; i<max_copper_layer; i++)
8109 if (strcmp (PCB->Data->Layer[i].Name, layername) == 0)
8111 layer = & (PCB->Data->Layer[i]);
8112 break;
8114 if (layer == NULL)
8116 Message (_("No layer named %s\n"), layername);
8117 return 1;
8120 buf = (char *) malloc (strlen (layer->Name) + strlen ("Layer X Attributes"));
8121 sprintf (buf, "Layer %s Attributes", layer->Name);
8122 gui->edit_attributes(buf, &(layer->Attributes));
8123 free (buf);
8124 return 0;
8127 case F_Element:
8129 int n_found = 0;
8130 ElementType *e = NULL;
8131 ELEMENT_LOOP (PCB->Data);
8133 if (TEST_FLAG (SELECTEDFLAG, element))
8135 e = element;
8136 n_found ++;
8139 END_LOOP;
8140 if (n_found > 1)
8142 Message (_("Too many elements selected\n"));
8143 return 1;
8145 if (n_found == 0)
8147 void *ptrtmp;
8148 gui->get_coords (_("Click on an element"), &x, &y);
8149 if ((SearchScreen
8150 (x, y, ELEMENT_TYPE, &ptrtmp,
8151 &ptrtmp, &ptrtmp)) != NO_TYPE)
8152 e = (ElementType *) ptrtmp;
8153 else
8155 Message (_("No element found there\n"));
8156 return 1;
8160 if (NAMEONPCB_NAME(e))
8162 buf = (char *) malloc (strlen (NAMEONPCB_NAME(e)) + strlen ("Element X Attributes"));
8163 sprintf(buf, "Element %s Attributes", NAMEONPCB_NAME(e));
8165 else
8167 buf = strdup ("Unnamed Element Attributes");
8169 gui->edit_attributes(buf, &(e->Attributes));
8170 free (buf);
8171 break;
8174 default:
8175 AFAIL (attributes);
8178 return 0;
8181 /* --------------------------------------------------------------------------- */
8183 HID_Action action_action_list[] = {
8184 {"AddRats", 0, ActionAddRats,
8185 addrats_help, addrats_syntax}
8187 {"Attributes", 0, ActionAttributes,
8188 attributes_help, attributes_syntax}
8190 {"Atomic", 0, ActionAtomic,
8191 atomic_help, atomic_syntax}
8193 {"AutoPlaceSelected", 0, ActionAutoPlaceSelected,
8194 autoplace_help, autoplace_syntax}
8196 {"AutoRoute", 0, ActionAutoRoute,
8197 autoroute_help, autoroute_syntax}
8199 {"ChangeClearSize", 0, ActionChangeClearSize,
8200 changeclearsize_help, changeclearsize_syntax}
8202 {"ChangeDrillSize", 0, ActionChange2ndSize,
8203 changedrillsize_help, changedrillsize_syntax}
8205 {"ChangeHole", 0, ActionChangeHole,
8206 changehold_help, changehold_syntax}
8208 {"ChangeJoin", 0, ActionChangeJoin,
8209 changejoin_help, changejoin_syntax}
8211 {"ChangeName", 0, ActionChangeName,
8212 changename_help, changename_syntax}
8214 {"ChangePaste", 0, ActionChangePaste,
8215 changepaste_help, changepaste_syntax}
8217 {"ChangePinName", 0, ActionChangePinName,
8218 changepinname_help, changepinname_syntax}
8220 {"ChangeSize", 0, ActionChangeSize,
8221 changesize_help, changesize_syntax}
8223 {"ChangeSquare", 0, ActionChangeSquare,
8224 changesquare_help, changesquare_syntax}
8226 {"ChangeOctagon", 0, ActionChangeOctagon,
8227 changeoctagon_help, changeoctagon_syntax}
8229 {"ClearSquare", 0, ActionClearSquare,
8230 clearsquare_help, clearsquare_syntax}
8232 {"ClearOctagon", 0, ActionClearOctagon,
8233 clearoctagon_help, clearoctagon_syntax}
8235 {"Connection", 0, ActionConnection,
8236 connection_help, connection_syntax}
8238 {"Delete", 0, ActionDelete,
8239 delete_help, delete_syntax}
8241 {"DeleteRats", 0, ActionDeleteRats,
8242 deleterats_help, deleterats_syntax}
8244 {"DisperseElements", 0, ActionDisperseElements,
8245 disperseelements_help, disperseelements_syntax}
8247 {"Display", 0, ActionDisplay,
8248 display_help, display_syntax}
8250 {"DRC", 0, ActionDRCheck,
8251 drc_help, drc_syntax}
8253 {"DumpLibrary", 0, ActionDumpLibrary,
8254 dumplibrary_help, dumplibrary_syntax}
8256 {"ExecuteFile", 0, ActionExecuteFile,
8257 executefile_help, executefile_syntax}
8259 {"Flip", N_("Click on Object or Flip Point"), ActionFlip,
8260 flip_help, flip_syntax}
8262 {"LoadFrom", 0, ActionLoadFrom,
8263 loadfrom_help, loadfrom_syntax}
8265 {"MarkCrosshair", 0, ActionMarkCrosshair,
8266 markcrosshair_help, markcrosshair_syntax}
8268 {"Message", 0, ActionMessage,
8269 message_help, message_syntax}
8271 {"MinMaskGap", 0, ActionMinMaskGap,
8272 minmaskgap_help, minmaskgap_syntax}
8274 {"MinClearGap", 0, ActionMinClearGap,
8275 mincleargap_help, mincleargap_syntax}
8277 {"Mode", 0, ActionMode,
8278 mode_help, mode_syntax}
8280 {"MorphPolygon", 0, ActionMorphPolygon,
8281 morphpolygon_help, morphpolygon_syntax}
8283 {"PasteBuffer", 0, ActionPasteBuffer,
8284 pastebuffer_help, pastebuffer_syntax}
8286 {"Quit", 0, ActionQuit,
8287 quit_help, quit_syntax}
8289 {"RemoveSelected", 0, ActionRemoveSelected,
8290 removeselected_help, removeselected_syntax}
8292 {"Renumber", 0, ActionRenumber,
8293 renumber_help, renumber_syntax}
8295 {"RipUp", 0, ActionRipUp,
8296 ripup_help, ripup_syntax}
8298 {"Select", 0, ActionSelect,
8299 select_help, select_syntax}
8301 {"Unselect", 0, ActionUnselect,
8302 unselect_help, unselect_syntax}
8304 {"SaveSettings", 0, ActionSaveSettings,
8305 savesettings_help, savesettings_syntax}
8307 {"SaveTo", 0, ActionSaveTo,
8308 saveto_help, saveto_syntax}
8310 {"SetSquare", 0, ActionSetSquare,
8311 setsquare_help, setsquare_syntax}
8313 {"SetOctagon", 0, ActionSetOctagon,
8314 setoctagon_help, setoctagon_syntax}
8316 {"SetThermal", 0, ActionSetThermal,
8317 setthermal_help, setthermal_syntax}
8319 {"SetValue", 0, ActionSetValue,
8320 setvalue_help, setvalue_syntax}
8322 {"ToggleHideName", 0, ActionToggleHideName,
8323 togglehidename_help, togglehidename_syntax}
8325 {"Undo", 0, ActionUndo,
8326 undo_help, undo_syntax}
8328 {"Redo", 0, ActionRedo,
8329 redo_help, redo_syntax}
8331 {"SetSame", N_("Select item to use attributes from"), ActionSetSame,
8332 setsame_help, setsame_syntax}
8334 {"SetFlag", 0, ActionSetFlag,
8335 setflag_help, setflag_syntax}
8337 {"ClrFlag", 0, ActionClrFlag,
8338 clrflag_help, clrflag_syntax}
8340 {"ChangeFlag", 0, ActionChangeFlag,
8341 changeflag_help, changeflag_syntax}
8343 {"Polygon", 0, ActionPolygon,
8344 polygon_help, polygon_syntax}
8346 {"RouteStyle", 0, ActionRouteStyle,
8347 routestyle_help, routestyle_syntax}
8349 {"MoveObject", N_("Select an Object"), ActionMoveObject,
8350 moveobject_help, moveobject_syntax}
8352 {"MoveToCurrentLayer", 0, ActionMoveToCurrentLayer,
8353 movetocurrentlayer_help, movetocurrentlayer_syntax}
8355 {"New", 0, ActionNew,
8356 new_help, new_syntax}
8358 {"pscalib", 0, ActionPSCalib}
8360 {"ElementList", 0, ActionElementList,
8361 elementlist_help, elementlist_syntax}
8363 {"ElementSetAttr", 0, ActionElementSetAttr,
8364 elementsetattr_help, elementsetattr_syntax}
8366 {"UpdatePackage", 0, ActionUpdatePackage,
8367 updatepackage_help, updatepackage_syntax}
8369 {"ExecCommand", 0, ActionExecCommand,
8370 execcommand_help, execcommand_syntax}
8372 {"Import", 0, ActionImport,
8373 import_help, import_syntax}
8377 REGISTER_ACTIONS (action_action_list)