Fix lockup when a scroll event is received outside the drawing area
[geda-pcb/pcjc2.git] / src / action.c
bloba4ab2d0ac690e9927afba08805a423e90dec765a
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_Found,
111 F_Connection,
112 F_Convert,
113 F_Copy,
114 F_CycleClip,
115 F_CycleCrosshair,
116 F_DeleteRats,
117 F_Drag,
118 F_DrillReport,
119 F_Element,
120 F_ElementByName,
121 F_ElementConnections,
122 F_ElementToBuffer,
123 F_Escape,
124 F_Find,
125 F_FlipElement,
126 F_FoundPins,
127 F_Grid,
128 F_InsertPoint,
129 F_Layer,
130 F_Layout,
131 F_LayoutAs,
132 F_LayoutToBuffer,
133 F_Line,
134 F_LineSize,
135 F_Lock,
136 F_Mirror,
137 F_Move,
138 F_NameOnPCB,
139 F_Netlist,
140 F_NetByName,
141 F_None,
142 F_Notify,
143 F_Object,
144 F_ObjectByName,
145 F_PasteBuffer,
146 F_PadByName,
147 F_PinByName,
148 F_PinOrPadName,
149 F_Pinout,
150 F_Polygon,
151 F_PolygonHole,
152 F_PreviousPoint,
153 F_RatsNest,
154 F_Rectangle,
155 F_Redraw,
156 F_Release,
157 F_Revert,
158 F_Remove,
159 F_RemoveSelected,
160 F_Report,
161 F_Reset,
162 F_ResetLinesAndPolygons,
163 F_ResetPinsViasAndPads,
164 F_Restore,
165 F_Rotate,
166 F_Save,
167 F_Selected,
168 F_SelectedArcs,
169 F_SelectedElements,
170 F_SelectedLines,
171 F_SelectedNames,
172 F_SelectedObjects,
173 F_SelectedPads,
174 F_SelectedPins,
175 F_SelectedTexts,
176 F_SelectedVias,
177 F_SelectedRats,
178 F_Stroke,
179 F_Text,
180 F_TextByName,
181 F_TextScale,
182 F_Thermal,
183 F_ToLayout,
184 F_ToggleAllDirections,
185 F_ToggleAutoDRC,
186 F_ToggleClearLine,
187 F_ToggleFullPoly,
188 F_ToggleGrid,
189 F_ToggleHideNames,
190 F_ToggleMask,
191 F_ToggleName,
192 F_ToggleObject,
193 F_ToggleShowDRC,
194 F_ToggleLiveRoute,
195 F_ToggleRubberBandMode,
196 F_ToggleStartDirection,
197 F_ToggleSnapPin,
198 F_ToggleThindraw,
199 F_ToggleLockNames,
200 F_ToggleOnlyNames,
201 F_ToggleThindrawPoly,
202 F_ToggleOrthoMove,
203 F_ToggleLocalRef,
204 F_ToggleCheckPlanes,
205 F_ToggleUniqueNames,
206 F_Via,
207 F_ViaByName,
208 F_Value,
209 F_ViaDrillingHole,
210 F_ViaSize,
211 F_Zoom
213 FunctionID;
215 typedef struct /* used to identify subfunctions */
217 char *Identifier;
218 FunctionID ID;
220 FunctionType;
222 /* --------------------------------------------------------------------------- */
224 /* %start-doc actions 00delta
226 Many actions take a @code{delta} parameter as the last parameter,
227 which is an amount to change something. That @code{delta} may include
228 units, as an additional parameter, such as @code{Action(Object,5,mm)}.
229 If no units are specified, the default is PCB's native units
230 (currently 1/100 mil). Also, if the delta is prefixed by @code{+} or
231 @code{-}, the size is increased or decreased by that amount.
232 Otherwise, the size size is set to the given amount.
234 @example
235 Action(Object,5,mil)
236 Action(Object,+0.5,mm)
237 Action(Object,-1)
238 @end example
240 Actions which take a @code{delta} parameter which do not accept all
241 these options will specify what they do take.
243 %end-doc */
245 /* %start-doc actions 00objects
247 Many actions act on indicated objects on the board. They will have
248 parameters like @code{ToggleObject} or @code{SelectedVias} to indicate
249 what group of objects they act on. Unless otherwise specified, these
250 parameters are defined as follows:
252 @table @code
254 @item Object
255 @itemx ToggleObject
256 Affects the object under the mouse pointer. If this action is invoked
257 from a menu or script, the user will be prompted to click on an
258 object, which is then the object affected.
260 @item Selected
261 @itemx SelectedObjects
263 Affects all objects which are currently selected. At least, all
264 selected objects for which the given action makes sense.
266 @item SelectedPins
267 @itemx SelectedVias
268 @itemx Selected@var{Type}
269 @itemx @i{etc}
270 Affects all objects which are both selected and of the @var{Type} specified.
272 @end table
274 %end-doc */
276 /* %start-doc actions 00macros
278 @macro pinshapes
280 Pins, pads, and vias can have various shapes. All may be round. Pins
281 and pads may be square (obviously "square" pads are usually
282 rectangular). Pins and vias may be octagonal. When you change a
283 shape flag of an element, you actually change all of its pins and
284 pads.
286 Note that the square flag takes precedence over the octagon flag,
287 thus, if both the square and octagon flags are set, the object is
288 square. When the square flag is cleared, the pins and pads will be
289 either round or, if the octagon flag is set, octagonal.
291 @end macro
293 %end-doc */
295 /* ---------------------------------------------------------------------------
296 * some local identifiers
298 static PointType InsertedPoint;
299 static LayerType *lastLayer;
300 static struct
302 PolygonType *poly;
303 LineType line;
305 fake;
307 static struct
309 Coord X, Y;
310 Cardinal Buffer;
311 bool Click;
312 bool Moving; /* selected type clicked on */
313 int Hit; /* move type clicked on */
314 void *ptr1;
315 void *ptr2;
316 void *ptr3;
318 Note;
320 static int defer_updates = 0;
321 static int defer_needs_update = 0;
323 static Cardinal polyIndex = 0;
324 static bool saved_mode = false;
325 #ifdef HAVE_LIBSTROKE
326 static bool mid_stroke = false;
327 static BoxType StrokeBox;
328 #endif
329 static FunctionType Functions[] = {
330 {"AddSelected", F_AddSelected},
331 {"All", F_All},
332 {"AllConnections", F_AllConnections},
333 {"AllRats", F_AllRats},
334 {"AllUnusedPins", F_AllUnusedPins},
335 {"Arc", F_Arc},
336 {"Arrow", F_Arrow},
337 {"Block", F_Block},
338 {"Description", F_Description},
339 {"Cancel", F_Cancel},
340 {"Center", F_Center},
341 {"Clear", F_Clear},
342 {"ClearAndRedraw", F_ClearAndRedraw},
343 {"ClearList", F_ClearList},
344 {"Close", F_Close},
345 {"Found", F_Found},
346 {"Connection", F_Connection},
347 {"Convert", F_Convert},
348 {"Copy", F_Copy},
349 {"CycleClip", F_CycleClip},
350 {"CycleCrosshair", F_CycleCrosshair},
351 {"DeleteRats", F_DeleteRats},
352 {"Drag", F_Drag},
353 {"DrillReport", F_DrillReport},
354 {"Element", F_Element},
355 {"ElementByName", F_ElementByName},
356 {"ElementConnections", F_ElementConnections},
357 {"ElementToBuffer", F_ElementToBuffer},
358 {"Escape", F_Escape},
359 {"Find", F_Find},
360 {"FlipElement", F_FlipElement},
361 {"FoundPins", F_FoundPins},
362 {"Grid", F_Grid},
363 {"InsertPoint", F_InsertPoint},
364 {"Layer", F_Layer},
365 {"Layout", F_Layout},
366 {"LayoutAs", F_LayoutAs},
367 {"LayoutToBuffer", F_LayoutToBuffer},
368 {"Line", F_Line},
369 {"LineSize", F_LineSize},
370 {"Lock", F_Lock},
371 {"Mirror", F_Mirror},
372 {"Move", F_Move},
373 {"NameOnPCB", F_NameOnPCB},
374 {"Netlist", F_Netlist},
375 {"NetByName", F_NetByName},
376 {"None", F_None},
377 {"Notify", F_Notify},
378 {"Object", F_Object},
379 {"ObjectByName", F_ObjectByName},
380 {"PasteBuffer", F_PasteBuffer},
381 {"PadByName", F_PadByName},
382 {"PinByName", F_PinByName},
383 {"PinOrPadName", F_PinOrPadName},
384 {"Pinout", F_Pinout},
385 {"Polygon", F_Polygon},
386 {"PolygonHole", F_PolygonHole},
387 {"PreviousPoint", F_PreviousPoint},
388 {"RatsNest", F_RatsNest},
389 {"Rectangle", F_Rectangle},
390 {"Redraw", F_Redraw},
391 {"Release", F_Release},
392 {"Remove", F_Remove},
393 {"RemoveSelected", F_RemoveSelected},
394 {"Report", F_Report},
395 {"Reset", F_Reset},
396 {"ResetLinesAndPolygons", F_ResetLinesAndPolygons},
397 {"ResetPinsViasAndPads", F_ResetPinsViasAndPads},
398 {"Restore", F_Restore},
399 {"Revert", F_Revert},
400 {"Rotate", F_Rotate},
401 {"Save", F_Save},
402 {"Selected", F_Selected},
403 {"SelectedArcs", F_SelectedArcs},
404 {"SelectedElements", F_SelectedElements},
405 {"SelectedLines", F_SelectedLines},
406 {"SelectedNames", F_SelectedNames},
407 {"SelectedObjects", F_SelectedObjects},
408 {"SelectedPins", F_SelectedPins},
409 {"SelectedPads", F_SelectedPads},
410 {"SelectedRats", F_SelectedRats},
411 {"SelectedTexts", F_SelectedTexts},
412 {"SelectedVias", F_SelectedVias},
413 {"Stroke", F_Stroke},
414 {"Text", F_Text},
415 {"TextByName", F_TextByName},
416 {"TextScale", F_TextScale},
417 {"Thermal", F_Thermal},
418 {"ToLayout", F_ToLayout},
419 {"Toggle45Degree", F_ToggleAllDirections},
420 {"ToggleClearLine", F_ToggleClearLine},
421 {"ToggleFullPoly", F_ToggleFullPoly},
422 {"ToggleGrid", F_ToggleGrid},
423 {"ToggleMask", F_ToggleMask},
424 {"ToggleName", F_ToggleName},
425 {"ToggleObject", F_ToggleObject},
426 {"ToggleRubberBandMode", F_ToggleRubberBandMode},
427 {"ToggleStartDirection", F_ToggleStartDirection},
428 {"ToggleSnapPin", F_ToggleSnapPin},
429 {"ToggleThindraw", F_ToggleThindraw},
430 {"ToggleThindrawPoly", F_ToggleThindrawPoly},
431 {"ToggleLockNames", F_ToggleLockNames},
432 {"ToggleOnlyNames", F_ToggleOnlyNames},
433 {"ToggleHideNames", F_ToggleHideNames},
434 {"ToggleCheckPlanes", F_ToggleCheckPlanes},
435 {"ToggleLocalRef", F_ToggleLocalRef},
436 {"ToggleOrthoMove", F_ToggleOrthoMove},
437 {"ToggleShowDRC", F_ToggleShowDRC},
438 {"ToggleLiveRoute", F_ToggleLiveRoute},
439 {"ToggleAutoDRC", F_ToggleAutoDRC},
440 {"ToggleUniqueNames", F_ToggleUniqueNames},
441 {"Value", F_Value},
442 {"Via", F_Via},
443 {"ViaByName", F_ViaByName},
444 {"ViaSize", F_ViaSize},
445 {"ViaDrillingHole", F_ViaDrillingHole},
446 {"Zoom", F_Zoom}
449 /* ---------------------------------------------------------------------------
450 * some local routines
452 static int GetFunctionID (String);
453 static void AdjustAttachedBox (void);
454 static void NotifyLine (void);
455 static void NotifyBlock (void);
456 static void NotifyMode (void);
457 static void ClearWarnings (void);
458 #ifdef HAVE_LIBSTROKE
459 static void FinishStroke (void);
460 extern void stroke_init (void);
461 extern void stroke_record (int x, int y);
462 extern int stroke_trans (char *s);
463 #endif
464 static void ChangeFlag (char *, char *, int, char *);
466 #define ARG(n) (argc > (n) ? argv[n] : NULL)
468 #ifdef HAVE_LIBSTROKE
470 /* ---------------------------------------------------------------------------
471 * FinishStroke - try to recognize the stroke sent
473 void
474 FinishStroke (void)
476 char msg[255];
477 int type;
478 unsigned long num;
479 void *ptr1, *ptr2, *ptr3;
481 mid_stroke = false;
482 if (stroke_trans (msg))
484 num = atoi (msg);
485 switch (num)
487 case 456:
488 if (Settings.Mode == LINE_MODE)
490 SetMode (LINE_MODE);
492 break;
493 case 9874123:
494 case 74123:
495 case 987412:
496 case 8741236:
497 case 874123:
498 RotateScreenObject (StrokeBox.X1, StrokeBox.Y1, SWAP_IDENT ? 1 : 3);
499 break;
500 case 7896321:
501 case 786321:
502 case 789632:
503 case 896321:
504 RotateScreenObject (StrokeBox.X1, StrokeBox.Y1, SWAP_IDENT ? 3 : 1);
505 break;
506 case 258:
507 SetMode (LINE_MODE);
508 break;
509 case 852:
510 SetMode (ARROW_MODE);
511 break;
512 case 1478963:
513 ActionUndo ("");
514 break;
515 case 147423:
516 case 147523:
517 case 1474123:
518 Redo (true);
519 break;
520 case 148963:
521 case 147863:
522 case 147853:
523 case 145863:
524 SetMode (VIA_MODE);
525 break;
526 case 951:
527 case 9651:
528 case 9521:
529 case 9621:
530 case 9851:
531 case 9541:
532 case 96521:
533 case 96541:
534 case 98541:
535 /* XXX: FIXME: Call a zoom-extents action */
536 break;
537 case 159:
538 case 1269:
539 case 1259:
540 case 1459:
541 case 1569:
542 case 1589:
543 case 12569:
544 case 12589:
545 case 14589:
546 /* XXX: FIXME: Zoom to fit the box StrokeBox.[X1,Y1] - StrokeBox.[X2,Y2] */
547 break;
549 default:
550 Message (_("Unknown stroke %s\n"), msg);
551 break;
554 else
555 gui->beep ();
557 #endif
559 /* ---------------------------------------------------------------------------
560 * Clear warning color from pins/pads
562 static void
563 ClearWarnings ()
565 Settings.RatWarn = false;
566 ALLPIN_LOOP (PCB->Data);
568 if (TEST_FLAG (WARNFLAG, pin))
570 CLEAR_FLAG (WARNFLAG, pin);
571 DrawPin (pin);
574 ENDALL_LOOP;
575 ALLPAD_LOOP (PCB->Data);
577 if (TEST_FLAG (WARNFLAG, pad))
579 CLEAR_FLAG (WARNFLAG, pad);
580 DrawPad (pad);
583 ENDALL_LOOP;
584 Draw ();
587 /* ---------------------------------------------------------------------------
589 * This is called a clicktime after a mouse down, to we can distinguish
590 * between short clicks (typically: select or create something) and long
591 * clicks. Long clicks typically drag something.
593 static void
594 click_cb (hidval hv)
596 if (Note.Click)
598 notify_crosshair_change (false);
599 Note.Click = false;
600 if (Note.Moving && !gui->shift_is_pressed ())
602 Note.Buffer = Settings.BufferNumber;
603 SetBufferNumber (MAX_BUFFER - 1);
604 ClearBuffer (PASTEBUFFER);
605 AddSelectedToBuffer (PASTEBUFFER, Note.X, Note.Y, true);
606 SaveUndoSerialNumber ();
607 RemoveSelected ();
608 SaveMode ();
609 saved_mode = true;
610 SetMode (PASTEBUFFER_MODE);
612 else if (Note.Hit && !gui->shift_is_pressed ())
614 SaveMode ();
615 saved_mode = true;
616 SetMode (gui->control_is_pressed ()? COPY_MODE : MOVE_MODE);
617 Crosshair.AttachedObject.Ptr1 = Note.ptr1;
618 Crosshair.AttachedObject.Ptr2 = Note.ptr2;
619 Crosshair.AttachedObject.Ptr3 = Note.ptr3;
620 Crosshair.AttachedObject.Type = Note.Hit;
621 AttachForCopy (Note.X, Note.Y);
623 else
625 BoxType box;
627 Note.Hit = 0;
628 Note.Moving = false;
629 SaveUndoSerialNumber ();
630 box.X1 = -MAX_COORD;
631 box.Y1 = -MAX_COORD;
632 box.X2 = MAX_COORD;
633 box.Y2 = MAX_COORD;
634 /* unselect first if shift key not down */
635 if (!gui->shift_is_pressed () && SelectBlock (&box, false))
636 SetChangedFlag (true);
637 NotifyBlock ();
638 Crosshair.AttachedBox.Point1.X = Note.X;
639 Crosshair.AttachedBox.Point1.Y = Note.Y;
641 notify_crosshair_change (true);
645 /* ---------------------------------------------------------------------------
647 * This is typically called when the mouse has moved or the mouse
648 * button was released.
650 static void
651 ReleaseMode (void)
653 BoxType box;
655 if (Note.Click)
657 BoxType box;
659 box.X1 = -MAX_COORD;
660 box.Y1 = -MAX_COORD;
661 box.X2 = MAX_COORD;
662 box.Y2 = MAX_COORD;
664 Note.Click = false; /* inhibit timer action */
665 SaveUndoSerialNumber ();
666 /* unselect first if shift key not down */
667 if (!gui->shift_is_pressed ())
669 if (SelectBlock (&box, false))
670 SetChangedFlag (true);
671 if (Note.Moving)
673 Note.Moving = 0;
674 Note.Hit = 0;
675 return;
678 RestoreUndoSerialNumber ();
679 if (SelectObject ())
680 SetChangedFlag (true);
681 Note.Hit = 0;
682 Note.Moving = 0;
684 else if (Note.Moving)
686 RestoreUndoSerialNumber ();
687 NotifyMode ();
688 ClearBuffer (PASTEBUFFER);
689 SetBufferNumber (Note.Buffer);
690 Note.Moving = false;
691 Note.Hit = 0;
693 else if (Note.Hit)
695 NotifyMode ();
696 Note.Hit = 0;
698 else if (Settings.Mode == ARROW_MODE)
700 box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
701 Crosshair.AttachedBox.Point2.X);
702 box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
703 Crosshair.AttachedBox.Point2.Y);
704 box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
705 Crosshair.AttachedBox.Point2.X);
706 box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
707 Crosshair.AttachedBox.Point2.Y);
708 RestoreUndoSerialNumber ();
709 if (SelectBlock (&box, true))
710 SetChangedFlag (true);
711 else if (Bumped)
712 IncrementUndoSerialNumber ();
713 Crosshair.AttachedBox.State = STATE_FIRST;
715 if (saved_mode)
716 RestoreMode ();
717 saved_mode = false;
720 /* ---------------------------------------------------------------------------
721 * get function ID of passed string
723 #define HSIZE 257
724 static char function_hash[HSIZE];
725 static int hash_initted = 0;
727 static int
728 hashfunc(String s)
730 int i = 0;
731 while (*s)
733 i ^= i >> 16;
734 i = (i * 13) ^ (unsigned char)tolower((int) *s);
735 s ++;
737 i = (unsigned int)i % HSIZE;
738 return i;
741 static int
742 GetFunctionID (String Ident)
744 int i, h;
746 if (Ident == 0)
747 return -1;
749 if (!hash_initted)
751 hash_initted = 1;
752 if (HSIZE < ENTRIES (Functions) * 2)
754 fprintf(stderr, _("Error: function hash size too small (%d vs %lu at %s:%d)\n"),
755 HSIZE, (unsigned long) ENTRIES (Functions)*2, __FILE__, __LINE__);
756 exit(1);
758 if (ENTRIES (Functions) > 254)
760 /* Change 'char' to 'int' and remove this when we get to 256
761 strings to hash. */
762 fprintf(stderr, _("Error: function hash type too small (%d vs %lu at %s:%d)\n"),
763 256, (unsigned long) ENTRIES (Functions), __FILE__, __LINE__);
764 exit(1);
767 for (i=ENTRIES (Functions)-1; i>=0; i--)
769 h = hashfunc (Functions[i].Identifier);
770 while (function_hash[h])
771 h = (h + 1) % HSIZE;
772 function_hash[h] = i + 1;
776 i = hashfunc (Ident);
777 while (1)
779 /* We enforce the "hash table bigger than function table" rule,
780 so we know there will be at least one zero entry to find. */
781 if (!function_hash[i])
782 return (-1);
783 if (!strcasecmp (Ident, Functions[function_hash[i]-1].Identifier))
784 return ((int) Functions[function_hash[i]-1].ID);
785 i = (i + 1) % HSIZE;
789 /* ---------------------------------------------------------------------------
790 * set new coordinates if in 'RECTANGLE' mode
791 * the cursor shape is also adjusted
793 static void
794 AdjustAttachedBox (void)
796 if (Settings.Mode == ARC_MODE)
798 Crosshair.AttachedBox.otherway = gui->shift_is_pressed ();
799 return;
801 switch (Crosshair.AttachedBox.State)
803 case STATE_SECOND: /* one corner is selected */
805 /* update coordinates */
806 Crosshair.AttachedBox.Point2.X = Crosshair.X;
807 Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
808 break;
813 /* ---------------------------------------------------------------------------
814 * adjusts the objects which are to be created like attached lines...
816 void
817 AdjustAttachedObjects (void)
819 PointType *pnt;
820 switch (Settings.Mode)
822 /* update at least an attached block (selection) */
823 case NO_MODE:
824 case ARROW_MODE:
825 if (Crosshair.AttachedBox.State)
827 Crosshair.AttachedBox.Point2.X = Crosshair.X;
828 Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
830 break;
832 /* rectangle creation mode */
833 case RECTANGLE_MODE:
834 case ARC_MODE:
835 AdjustAttachedBox ();
836 break;
838 /* polygon creation mode */
839 case POLYGON_MODE:
840 case POLYGONHOLE_MODE:
841 AdjustAttachedLine ();
842 break;
843 /* line creation mode */
844 case LINE_MODE:
845 if (PCB->RatDraw || PCB->Clipping == 0)
846 AdjustAttachedLine ();
847 else
848 AdjustTwoLine (PCB->Clipping - 1);
849 break;
850 /* point insertion mode */
851 case INSERTPOINT_MODE:
852 pnt = AdjustInsertPoint ();
853 if (pnt)
854 InsertedPoint = *pnt;
855 break;
856 case ROTATE_MODE:
857 break;
861 /* ---------------------------------------------------------------------------
862 * creates points of a line
864 static void
865 NotifyLine (void)
867 int type = NO_TYPE;
868 void *ptr1, *ptr2, *ptr3;
870 if (!Marked.status || TEST_FLAG (LOCALREFFLAG, PCB))
871 SetLocalRef (Crosshair.X, Crosshair.Y, true);
872 switch (Crosshair.AttachedLine.State)
874 case STATE_FIRST: /* first point */
875 if (PCB->RatDraw && SearchScreen (Crosshair.X, Crosshair.Y,
876 PAD_TYPE | PIN_TYPE, &ptr1, &ptr1,
877 &ptr1) == NO_TYPE)
879 gui->beep ();
880 break;
882 if (TEST_FLAG (AUTODRCFLAG, PCB) && Settings.Mode == LINE_MODE)
884 type = SearchScreen (Crosshair.X, Crosshair.Y,
885 PIN_TYPE | PAD_TYPE | VIA_TYPE, &ptr1, &ptr2,
886 &ptr3);
887 LookupConnection (Crosshair.X, Crosshair.Y, true, 1, CONNECTEDFLAG, false);
888 LookupConnection (Crosshair.X, Crosshair.Y, true, 1, FOUNDFLAG, true);
890 if (type == PIN_TYPE || type == VIA_TYPE)
892 Crosshair.AttachedLine.Point1.X =
893 Crosshair.AttachedLine.Point2.X = ((PinType *) ptr2)->X;
894 Crosshair.AttachedLine.Point1.Y =
895 Crosshair.AttachedLine.Point2.Y = ((PinType *) ptr2)->Y;
897 else if (type == PAD_TYPE)
899 PadType *pad = (PadType *) ptr2;
900 double d1 = Distance (Crosshair.X, Crosshair.Y, pad->Point1.X, pad->Point1.Y);
901 double d2 = Distance (Crosshair.X, Crosshair.Y, pad->Point2.X, pad->Point2.Y);
902 if (d2 < d1)
904 Crosshair.AttachedLine.Point1 =
905 Crosshair.AttachedLine.Point2 = pad->Point2;
907 else
909 Crosshair.AttachedLine.Point1 =
910 Crosshair.AttachedLine.Point2 = pad->Point1;
913 else
915 Crosshair.AttachedLine.Point1.X =
916 Crosshair.AttachedLine.Point2.X = Crosshair.X;
917 Crosshair.AttachedLine.Point1.Y =
918 Crosshair.AttachedLine.Point2.Y = Crosshair.Y;
920 Crosshair.AttachedLine.State = STATE_SECOND;
921 break;
923 case STATE_SECOND:
924 /* fall through to third state too */
925 lastLayer = CURRENT;
926 default: /* all following points */
927 Crosshair.AttachedLine.State = STATE_THIRD;
928 break;
932 /* ---------------------------------------------------------------------------
933 * create first or second corner of a marked block
935 static void
936 NotifyBlock (void)
938 notify_crosshair_change (false);
939 switch (Crosshair.AttachedBox.State)
941 case STATE_FIRST: /* setup first point */
942 Crosshair.AttachedBox.Point1.X =
943 Crosshair.AttachedBox.Point2.X = Crosshair.X;
944 Crosshair.AttachedBox.Point1.Y =
945 Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
946 Crosshair.AttachedBox.State = STATE_SECOND;
947 break;
949 case STATE_SECOND: /* setup second point */
950 Crosshair.AttachedBox.State = STATE_THIRD;
951 break;
953 notify_crosshair_change (true);
957 /* ---------------------------------------------------------------------------
959 * This is called after every mode change, like mouse button pressed,
960 * mouse button released, dragging something started or a different tool
961 * selected. It does what's appropriate for the current mode setting.
962 * This can also mean creation of an object at the current crosshair location.
964 * new created objects are added to the create undo list of course
966 static void
967 NotifyMode (void)
969 void *ptr1, *ptr2, *ptr3;
970 int type;
972 if (Settings.RatWarn)
973 ClearWarnings ();
974 switch (Settings.Mode)
976 case ARROW_MODE:
978 int test;
979 hidval hv;
981 Note.Click = true;
982 /* do something after click time */
983 gui->add_timer (click_cb, CLICK_TIME, hv);
985 /* see if we clicked on something already selected
986 * (Note.Moving) or clicked on a MOVE_TYPE
987 * (Note.Hit)
989 for (test = (SELECT_TYPES | MOVE_TYPES) & ~RATLINE_TYPE;
990 test; test &= ~type)
992 type = SearchScreen (Note.X, Note.Y, test, &ptr1, &ptr2, &ptr3);
993 if (!Note.Hit && (type & MOVE_TYPES) &&
994 !TEST_FLAG (LOCKFLAG, (PinType *) ptr2))
996 Note.Hit = type;
997 Note.ptr1 = ptr1;
998 Note.ptr2 = ptr2;
999 Note.ptr3 = ptr3;
1001 if (!Note.Moving && (type & SELECT_TYPES) &&
1002 TEST_FLAG (SELECTEDFLAG, (PinType *) ptr2))
1003 Note.Moving = true;
1004 if ((Note.Hit && Note.Moving) || type == NO_TYPE)
1005 break;
1007 break;
1010 case VIA_MODE:
1012 PinType *via;
1014 if (!PCB->ViaOn)
1016 Message (_("You must turn via visibility on before\n"
1017 "you can place vias\n"));
1018 break;
1020 if ((via = CreateNewVia (PCB->Data, Note.X, Note.Y,
1021 Settings.ViaThickness, 2 * Settings.Keepaway,
1022 0, Settings.ViaDrillingHole, NULL,
1023 NoFlags ())) != NULL)
1025 AddObjectToCreateUndoList (VIA_TYPE, via, via, via);
1026 if (gui->shift_is_pressed ())
1027 ChangeObjectThermal (VIA_TYPE, via, via, via, PCB->ThermStyle);
1028 IncrementUndoSerialNumber ();
1029 DrawVia (via);
1030 Draw ();
1032 break;
1035 case ARC_MODE:
1037 switch (Crosshair.AttachedBox.State)
1039 case STATE_FIRST:
1040 Crosshair.AttachedBox.Point1.X =
1041 Crosshair.AttachedBox.Point2.X = Note.X;
1042 Crosshair.AttachedBox.Point1.Y =
1043 Crosshair.AttachedBox.Point2.Y = Note.Y;
1044 Crosshair.AttachedBox.State = STATE_SECOND;
1045 break;
1047 case STATE_SECOND:
1048 case STATE_THIRD:
1050 ArcType *arc;
1051 Coord wx, wy;
1052 Angle sa, dir;
1054 wx = Note.X - Crosshair.AttachedBox.Point1.X;
1055 wy = Note.Y - Crosshair.AttachedBox.Point1.Y;
1056 if (XOR (Crosshair.AttachedBox.otherway, abs (wy) > abs (wx)))
1058 Crosshair.AttachedBox.Point2.X =
1059 Crosshair.AttachedBox.Point1.X + abs (wy) * SGNZ (wx);
1060 sa = (wx >= 0) ? 0 : 180;
1061 #ifdef ARC45
1062 if (abs (wy) / 2 >= abs (wx))
1063 dir = (SGNZ (wx) == SGNZ (wy)) ? 45 : -45;
1064 else
1065 #endif
1066 dir = (SGNZ (wx) == SGNZ (wy)) ? 90 : -90;
1068 else
1070 Crosshair.AttachedBox.Point2.Y =
1071 Crosshair.AttachedBox.Point1.Y + abs (wx) * SGNZ (wy);
1072 sa = (wy >= 0) ? -90 : 90;
1073 #ifdef ARC45
1074 if (abs (wx) / 2 >= abs (wy))
1075 dir = (SGNZ (wx) == SGNZ (wy)) ? -45 : 45;
1076 else
1077 #endif
1078 dir = (SGNZ (wx) == SGNZ (wy)) ? -90 : 90;
1079 wy = wx;
1081 if (abs (wy) > 0 && (arc = CreateNewArcOnLayer (CURRENT,
1082 Crosshair.
1083 AttachedBox.
1084 Point2.X,
1085 Crosshair.
1086 AttachedBox.
1087 Point2.Y,
1088 abs (wy),
1089 abs (wy),
1091 dir,
1092 Settings.
1093 LineThickness,
1094 2 * Settings.
1095 Keepaway,
1096 MakeFlags
1097 (TEST_FLAG
1098 (CLEARNEWFLAG,
1099 PCB) ?
1100 CLEARLINEFLAG :
1101 0))))
1103 BoxType *bx;
1105 bx = GetArcEnds (arc);
1106 Crosshair.AttachedBox.Point1.X =
1107 Crosshair.AttachedBox.Point2.X = bx->X2;
1108 Crosshair.AttachedBox.Point1.Y =
1109 Crosshair.AttachedBox.Point2.Y = bx->Y2;
1110 AddObjectToCreateUndoList (ARC_TYPE, CURRENT, arc, arc);
1111 IncrementUndoSerialNumber ();
1112 addedLines++;
1113 DrawArc (CURRENT, arc);
1114 Draw ();
1115 Crosshair.AttachedBox.State = STATE_THIRD;
1117 break;
1120 break;
1122 case LOCK_MODE:
1124 type = SearchScreen (Note.X, Note.Y, LOCK_TYPES, &ptr1, &ptr2, &ptr3);
1125 if (type == ELEMENT_TYPE)
1127 ElementType *element = (ElementType *) ptr2;
1129 TOGGLE_FLAG (LOCKFLAG, element);
1130 PIN_LOOP (element);
1132 TOGGLE_FLAG (LOCKFLAG, pin);
1133 CLEAR_FLAG (SELECTEDFLAG, pin);
1135 END_LOOP;
1136 PAD_LOOP (element);
1138 TOGGLE_FLAG (LOCKFLAG, pad);
1139 CLEAR_FLAG (SELECTEDFLAG, pad);
1141 END_LOOP;
1142 CLEAR_FLAG (SELECTEDFLAG, element);
1143 /* always re-draw it since I'm too lazy
1144 * to tell if a selected flag changed
1146 DrawElement (element);
1147 Draw ();
1148 SetChangedFlag (true);
1149 hid_actionl ("Report", "Object", NULL);
1151 else if (type != NO_TYPE)
1153 TextType *thing = (TextType *) ptr3;
1154 TOGGLE_FLAG (LOCKFLAG, thing);
1155 if (TEST_FLAG (LOCKFLAG, thing)
1156 && TEST_FLAG (SELECTEDFLAG, thing))
1158 /* this is not un-doable since LOCK isn't */
1159 CLEAR_FLAG (SELECTEDFLAG, thing);
1160 DrawObject (type, ptr1, ptr2);
1161 Draw ();
1163 SetChangedFlag (true);
1164 hid_actionl ("Report", "Object", NULL);
1166 break;
1168 case THERMAL_MODE:
1170 if (((type
1172 SearchScreen (Note.X, Note.Y, PIN_TYPES, &ptr1, &ptr2,
1173 &ptr3)) != NO_TYPE)
1174 && !TEST_FLAG (HOLEFLAG, (PinType *) ptr3))
1176 if (gui->shift_is_pressed ())
1178 int tstyle = GET_THERM (INDEXOFCURRENT, (PinType *) ptr3);
1179 tstyle++;
1180 if (tstyle > 5)
1181 tstyle = 1;
1182 ChangeObjectThermal (type, ptr1, ptr2, ptr3, tstyle);
1184 else if (GET_THERM (INDEXOFCURRENT, (PinType *) ptr3))
1185 ChangeObjectThermal (type, ptr1, ptr2, ptr3, 0);
1186 else
1187 ChangeObjectThermal (type, ptr1, ptr2, ptr3, PCB->ThermStyle);
1189 break;
1192 case LINE_MODE:
1193 /* do update of position */
1194 NotifyLine ();
1195 if (Crosshair.AttachedLine.State != STATE_THIRD)
1196 break;
1198 /* Remove anchor if clicking on start point;
1199 * this means we can't paint 0 length lines
1200 * which could be used for square SMD pads.
1201 * Instead use a very small delta, or change
1202 * the file after saving.
1204 if (Crosshair.X == Crosshair.AttachedLine.Point1.X
1205 && Crosshair.Y == Crosshair.AttachedLine.Point1.Y)
1207 SetMode (LINE_MODE);
1208 break;
1211 if (PCB->RatDraw)
1213 RatType *line;
1214 if ((line = AddNet ()))
1216 addedLines++;
1217 AddObjectToCreateUndoList (RATLINE_TYPE, line, line, line);
1218 IncrementUndoSerialNumber ();
1219 DrawRat (line);
1220 Crosshair.AttachedLine.Point1.X =
1221 Crosshair.AttachedLine.Point2.X;
1222 Crosshair.AttachedLine.Point1.Y =
1223 Crosshair.AttachedLine.Point2.Y;
1224 Draw ();
1226 break;
1228 else
1229 /* create line if both ends are determined && length != 0 */
1231 LineType *line;
1232 int line_flags = 0;
1234 if (TEST_FLAG (AUTODRCFLAG, PCB) && !TEST_SILK_LAYER (CURRENT))
1235 line_flags |= CONNECTEDFLAG | FOUNDFLAG;
1237 if (TEST_FLAG (CLEARNEWFLAG, PCB))
1238 line_flags |= CLEARLINEFLAG;
1240 if (PCB->Clipping
1241 && Crosshair.AttachedLine.Point1.X ==
1242 Crosshair.AttachedLine.Point2.X
1243 && Crosshair.AttachedLine.Point1.Y ==
1244 Crosshair.AttachedLine.Point2.Y
1245 && (Crosshair.AttachedLine.Point2.X != Note.X
1246 || Crosshair.AttachedLine.Point2.Y != Note.Y))
1248 /* We will only need to paint the second line segment.
1249 Since we only check for vias on the first segment,
1250 swap them so the non-empty segment is the first segment. */
1251 Crosshair.AttachedLine.Point2.X = Note.X;
1252 Crosshair.AttachedLine.Point2.Y = Note.Y;
1255 if ((Crosshair.AttachedLine.Point1.X !=
1256 Crosshair.AttachedLine.Point2.X
1257 || Crosshair.AttachedLine.Point1.Y !=
1258 Crosshair.AttachedLine.Point2.Y)
1259 && (line =
1260 CreateDrawnLineOnLayer (CURRENT,
1261 Crosshair.AttachedLine.Point1.X,
1262 Crosshair.AttachedLine.Point1.Y,
1263 Crosshair.AttachedLine.Point2.X,
1264 Crosshair.AttachedLine.Point2.Y,
1265 Settings.LineThickness,
1266 2 * Settings.Keepaway,
1267 MakeFlags (line_flags))) != NULL)
1269 PinType *via;
1271 addedLines++;
1272 AddObjectToCreateUndoList (LINE_TYPE, CURRENT, line, line);
1273 DrawLine (CURRENT, line);
1274 /* place a via if vias are visible, the layer is
1275 in a new group since the last line and there
1276 isn't a pin already here */
1277 if (PCB->ViaOn && GetLayerGroupNumberByPointer (CURRENT) !=
1278 GetLayerGroupNumberByPointer (lastLayer) &&
1279 SearchObjectByLocation (PIN_TYPES, &ptr1, &ptr2, &ptr3,
1280 Crosshair.AttachedLine.Point1.X,
1281 Crosshair.AttachedLine.Point1.Y,
1282 Settings.ViaThickness / 2) ==
1283 NO_TYPE
1284 && (via =
1285 CreateNewVia (PCB->Data,
1286 Crosshair.AttachedLine.Point1.X,
1287 Crosshair.AttachedLine.Point1.Y,
1288 Settings.ViaThickness,
1289 2 * Settings.Keepaway, 0,
1290 Settings.ViaDrillingHole, NULL,
1291 NoFlags ())) != NULL)
1293 AddObjectToCreateUndoList (VIA_TYPE, via, via, via);
1294 DrawVia (via);
1296 /* copy the coordinates */
1297 Crosshair.AttachedLine.Point1.X =
1298 Crosshair.AttachedLine.Point2.X;
1299 Crosshair.AttachedLine.Point1.Y =
1300 Crosshair.AttachedLine.Point2.Y;
1301 IncrementUndoSerialNumber ();
1302 lastLayer = CURRENT;
1304 if (PCB->Clipping && (Note.X != Crosshair.AttachedLine.Point2.X
1305 || Note.Y !=
1306 Crosshair.AttachedLine.Point2.Y)
1307 && (line =
1308 CreateDrawnLineOnLayer (CURRENT,
1309 Crosshair.AttachedLine.Point2.X,
1310 Crosshair.AttachedLine.Point2.Y,
1311 Note.X, Note.Y,
1312 Settings.LineThickness,
1313 2 * Settings.Keepaway,
1314 MakeFlags (line_flags))) != NULL)
1316 addedLines++;
1317 AddObjectToCreateUndoList (LINE_TYPE, CURRENT, line, line);
1318 IncrementUndoSerialNumber ();
1319 DrawLine (CURRENT, line);
1320 /* move to new start point */
1321 Crosshair.AttachedLine.Point1.X = Note.X;
1322 Crosshair.AttachedLine.Point1.Y = Note.Y;
1323 Crosshair.AttachedLine.Point2.X = Note.X;
1324 Crosshair.AttachedLine.Point2.Y = Note.Y;
1325 if (TEST_FLAG (SWAPSTARTDIRFLAG, PCB))
1327 PCB->Clipping ^= 3;
1330 if (TEST_FLAG (AUTODRCFLAG, PCB) && !TEST_SILK_LAYER (CURRENT))
1331 LookupConnection (Note.X, Note.Y, true, 1, CONNECTEDFLAG, false);
1332 Draw ();
1334 break;
1336 case RECTANGLE_MODE:
1337 /* do update of position */
1338 NotifyBlock ();
1340 /* create rectangle if both corners are determined
1341 * and width, height are != 0
1343 if (Crosshair.AttachedBox.State == STATE_THIRD &&
1344 Crosshair.AttachedBox.Point1.X != Crosshair.AttachedBox.Point2.X &&
1345 Crosshair.AttachedBox.Point1.Y != Crosshair.AttachedBox.Point2.Y)
1347 PolygonType *polygon;
1349 int flags = CLEARPOLYFLAG;
1350 if (TEST_FLAG (NEWFULLPOLYFLAG, PCB))
1351 flags |= FULLPOLYFLAG;
1352 if ((polygon = CreateNewPolygonFromRectangle (CURRENT,
1353 Crosshair.
1354 AttachedBox.Point1.X,
1355 Crosshair.
1356 AttachedBox.Point1.Y,
1357 Crosshair.
1358 AttachedBox.Point2.X,
1359 Crosshair.
1360 AttachedBox.Point2.Y,
1361 MakeFlags
1362 (flags))) !=
1363 NULL)
1365 AddObjectToCreateUndoList (POLYGON_TYPE, CURRENT,
1366 polygon, polygon);
1367 IncrementUndoSerialNumber ();
1368 DrawPolygon (CURRENT, polygon);
1369 Draw ();
1372 /* reset state to 'first corner' */
1373 Crosshair.AttachedBox.State = STATE_FIRST;
1375 break;
1377 case TEXT_MODE:
1379 char *string;
1381 if ((string = gui->prompt_for (_("Enter text:"), "")) != NULL)
1383 if (strlen(string) > 0)
1385 TextType *text;
1386 int flag = CLEARLINEFLAG;
1388 if (GetLayerGroupNumberByNumber (INDEXOFCURRENT) ==
1389 GetLayerGroupNumberByNumber (solder_silk_layer))
1390 flag |= ONSOLDERFLAG;
1391 if ((text = CreateNewText (CURRENT, &PCB->Font, Note.X,
1392 Note.Y, 0, Settings.TextScale,
1393 string, MakeFlags (flag))) != NULL)
1395 AddObjectToCreateUndoList (TEXT_TYPE, CURRENT, text, text);
1396 IncrementUndoSerialNumber ();
1397 DrawText (CURRENT, text);
1398 Draw ();
1401 free (string);
1403 break;
1406 case POLYGON_MODE:
1408 PointType *points = Crosshair.AttachedPolygon.Points;
1409 Cardinal n = Crosshair.AttachedPolygon.PointN;
1411 /* do update of position; use the 'LINE_MODE' mechanism */
1412 NotifyLine ();
1414 /* check if this is the last point of a polygon */
1415 if (n >= 3 &&
1416 points->X == Crosshair.AttachedLine.Point2.X &&
1417 points->Y == Crosshair.AttachedLine.Point2.Y)
1419 CopyAttachedPolygonToLayer ();
1420 Draw ();
1421 break;
1424 /* create new point if it's the first one or if it's
1425 * different to the last one
1427 if (!n ||
1428 points[n - 1].X != Crosshair.AttachedLine.Point2.X ||
1429 points[n - 1].Y != Crosshair.AttachedLine.Point2.Y)
1431 CreateNewPointInPolygon (&Crosshair.AttachedPolygon,
1432 Crosshair.AttachedLine.Point2.X,
1433 Crosshair.AttachedLine.Point2.Y);
1435 /* copy the coordinates */
1436 Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X;
1437 Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y;
1439 break;
1442 case POLYGONHOLE_MODE:
1444 switch (Crosshair.AttachedObject.State)
1446 /* first notify, lookup object */
1447 case STATE_FIRST:
1448 Crosshair.AttachedObject.Type =
1449 SearchScreen (Note.X, Note.Y, POLYGON_TYPE,
1450 &Crosshair.AttachedObject.Ptr1,
1451 &Crosshair.AttachedObject.Ptr2,
1452 &Crosshair.AttachedObject.Ptr3);
1454 if (Crosshair.AttachedObject.Type != NO_TYPE)
1456 if (TEST_FLAG (LOCKFLAG, (PolygonType *)
1457 Crosshair.AttachedObject.Ptr2))
1459 Message (_("Sorry, the object is locked\n"));
1460 Crosshair.AttachedObject.Type = NO_TYPE;
1461 break;
1463 else
1464 Crosshair.AttachedObject.State = STATE_SECOND;
1466 break;
1468 /* second notify, insert new point into object */
1469 case STATE_SECOND:
1471 PointType *points = Crosshair.AttachedPolygon.Points;
1472 Cardinal n = Crosshair.AttachedPolygon.PointN;
1473 POLYAREA *original, *new_hole, *result;
1474 FlagType Flags;
1476 /* do update of position; use the 'LINE_MODE' mechanism */
1477 NotifyLine ();
1479 /* check if this is the last point of a polygon */
1480 if (n >= 3 &&
1481 points->X == Crosshair.AttachedLine.Point2.X &&
1482 points->Y == Crosshair.AttachedLine.Point2.Y)
1484 /* Create POLYAREAs from the original polygon
1485 * and the new hole polygon */
1486 original = PolygonToPoly ((PolygonType *)Crosshair.AttachedObject.Ptr2);
1487 new_hole = PolygonToPoly (&Crosshair.AttachedPolygon);
1489 /* Subtract the hole from the original polygon shape */
1490 poly_Boolean_free (original, new_hole, &result, PBO_SUB);
1492 /* Convert the resulting polygon(s) into a new set of nodes
1493 * and place them on the page. Delete the original polygon.
1495 SaveUndoSerialNumber ();
1496 Flags = ((PolygonType *)Crosshair.AttachedObject.Ptr2)->Flags;
1497 PolyToPolygonsOnLayer (PCB->Data, (LayerType *)Crosshair.AttachedObject.Ptr1,
1498 result, Flags);
1499 RemoveObject (POLYGON_TYPE,
1500 Crosshair.AttachedObject.Ptr1,
1501 Crosshair.AttachedObject.Ptr2,
1502 Crosshair.AttachedObject.Ptr3);
1503 RestoreUndoSerialNumber ();
1504 IncrementUndoSerialNumber ();
1505 Draw ();
1507 /* reset state of attached line */
1508 memset (&Crosshair.AttachedPolygon, 0, sizeof (PolygonType));
1509 Crosshair.AttachedLine.State = STATE_FIRST;
1510 addedLines = 0;
1512 break;
1515 /* create new point if it's the first one or if it's
1516 * different to the last one
1518 if (!n ||
1519 points[n - 1].X != Crosshair.AttachedLine.Point2.X ||
1520 points[n - 1].Y != Crosshair.AttachedLine.Point2.Y)
1522 CreateNewPointInPolygon (&Crosshair.AttachedPolygon,
1523 Crosshair.AttachedLine.Point2.X,
1524 Crosshair.AttachedLine.Point2.Y);
1526 /* copy the coordinates */
1527 Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X;
1528 Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y;
1530 break;
1534 break;
1537 case PASTEBUFFER_MODE:
1539 TextType estr[MAX_ELEMENTNAMES];
1540 ElementType *e = 0;
1542 if (gui->shift_is_pressed ())
1544 int type =
1545 SearchScreen (Note.X, Note.Y, ELEMENT_TYPE, &ptr1, &ptr2,
1546 &ptr3);
1547 if (type == ELEMENT_TYPE)
1549 e = (ElementType *) ptr1;
1550 if (e)
1552 int i;
1554 memcpy (estr, e->Name,
1555 MAX_ELEMENTNAMES * sizeof (TextType));
1556 for (i = 0; i < MAX_ELEMENTNAMES; ++i)
1557 estr[i].TextString = estr[i].TextString ? strdup(estr[i].TextString) : NULL;
1558 RemoveElement (e);
1562 if (CopyPastebufferToLayout (Note.X, Note.Y))
1563 SetChangedFlag (true);
1564 if (e)
1566 int type =
1567 SearchScreen (Note.X, Note.Y, ELEMENT_TYPE, &ptr1, &ptr2,
1568 &ptr3);
1569 if (type == ELEMENT_TYPE && ptr1)
1571 int i, save_n;
1572 e = (ElementType *) ptr1;
1574 save_n = NAME_INDEX (PCB);
1576 for (i = 0; i < MAX_ELEMENTNAMES; i++)
1578 if (i == save_n)
1579 EraseElementName (e);
1580 r_delete_entry (PCB->Data->name_tree[i],
1581 (BoxType *) & (e->Name[i]));
1582 memcpy (&(e->Name[i]), &(estr[i]), sizeof (TextType));
1583 e->Name[i].Element = e;
1584 SetTextBoundingBox (&PCB->Font, &(e->Name[i]));
1585 r_insert_entry (PCB->Data->name_tree[i],
1586 (BoxType *) & (e->Name[i]), 0);
1587 if (i == save_n)
1588 DrawElementName (e);
1592 break;
1595 case REMOVE_MODE:
1596 if ((type =
1597 SearchScreen (Note.X, Note.Y, REMOVE_TYPES, &ptr1, &ptr2,
1598 &ptr3)) != NO_TYPE)
1600 if (TEST_FLAG (LOCKFLAG, (LineType *) ptr2))
1602 Message (_("Sorry, the object is locked\n"));
1603 break;
1605 if (type == ELEMENT_TYPE)
1607 RubberbandType *ptr;
1608 int i;
1610 Crosshair.AttachedObject.RubberbandN = 0;
1611 LookupRatLines (type, ptr1, ptr2, ptr3);
1612 ptr = Crosshair.AttachedObject.Rubberband;
1613 for (i = 0; i < Crosshair.AttachedObject.RubberbandN; i++)
1615 if (PCB->RatOn)
1616 EraseRat ((RatType *) ptr->Line);
1617 if (TEST_FLAG (RUBBERENDFLAG, ptr->Line))
1618 MoveObjectToRemoveUndoList (RATLINE_TYPE,
1619 ptr->Line, ptr->Line,
1620 ptr->Line);
1621 else
1622 TOGGLE_FLAG (RUBBERENDFLAG, ptr->Line); /* only remove line once */
1623 ptr++;
1626 RemoveObject (type, ptr1, ptr2, ptr3);
1627 IncrementUndoSerialNumber ();
1628 SetChangedFlag (true);
1630 break;
1632 case ROTATE_MODE:
1633 RotateScreenObject (Note.X, Note.Y,
1634 gui->shift_is_pressed ()? (SWAP_IDENT ?
1635 1 : 3)
1636 : (SWAP_IDENT ? 3 : 1));
1637 break;
1639 /* both are almost the same */
1640 case COPY_MODE:
1641 case MOVE_MODE:
1642 switch (Crosshair.AttachedObject.State)
1644 /* first notify, lookup object */
1645 case STATE_FIRST:
1647 int types = (Settings.Mode == COPY_MODE) ?
1648 COPY_TYPES : MOVE_TYPES;
1650 Crosshair.AttachedObject.Type =
1651 SearchScreen (Note.X, Note.Y, types,
1652 &Crosshair.AttachedObject.Ptr1,
1653 &Crosshair.AttachedObject.Ptr2,
1654 &Crosshair.AttachedObject.Ptr3);
1655 if (Crosshair.AttachedObject.Type != NO_TYPE)
1657 if (Settings.Mode == MOVE_MODE &&
1658 TEST_FLAG (LOCKFLAG, (PinType *)
1659 Crosshair.AttachedObject.Ptr2))
1661 Message (_("Sorry, the object is locked\n"));
1662 Crosshair.AttachedObject.Type = NO_TYPE;
1664 else
1665 AttachForCopy (Note.X, Note.Y);
1667 break;
1670 /* second notify, move or copy object */
1671 case STATE_SECOND:
1672 if (Settings.Mode == COPY_MODE)
1673 CopyObject (Crosshair.AttachedObject.Type,
1674 Crosshair.AttachedObject.Ptr1,
1675 Crosshair.AttachedObject.Ptr2,
1676 Crosshair.AttachedObject.Ptr3,
1677 Note.X - Crosshair.AttachedObject.X,
1678 Note.Y - Crosshair.AttachedObject.Y);
1679 else
1681 MoveObjectAndRubberband (Crosshair.AttachedObject.Type,
1682 Crosshair.AttachedObject.Ptr1,
1683 Crosshair.AttachedObject.Ptr2,
1684 Crosshair.AttachedObject.Ptr3,
1685 Note.X - Crosshair.AttachedObject.X,
1686 Note.Y - Crosshair.AttachedObject.Y);
1687 SetLocalRef (0, 0, false);
1689 SetChangedFlag (true);
1691 /* reset identifiers */
1692 Crosshair.AttachedObject.Type = NO_TYPE;
1693 Crosshair.AttachedObject.State = STATE_FIRST;
1694 break;
1696 break;
1698 /* insert a point into a polygon/line/... */
1699 case INSERTPOINT_MODE:
1700 switch (Crosshair.AttachedObject.State)
1702 /* first notify, lookup object */
1703 case STATE_FIRST:
1704 Crosshair.AttachedObject.Type =
1705 SearchScreen (Note.X, Note.Y, INSERT_TYPES,
1706 &Crosshair.AttachedObject.Ptr1,
1707 &Crosshair.AttachedObject.Ptr2,
1708 &Crosshair.AttachedObject.Ptr3);
1710 if (Crosshair.AttachedObject.Type != NO_TYPE)
1712 if (TEST_FLAG (LOCKFLAG, (PolygonType *)
1713 Crosshair.AttachedObject.Ptr2))
1715 Message (_("Sorry, the object is locked\n"));
1716 Crosshair.AttachedObject.Type = NO_TYPE;
1717 break;
1719 else
1721 /* get starting point of nearest segment */
1722 if (Crosshair.AttachedObject.Type == POLYGON_TYPE)
1724 fake.poly =
1725 (PolygonType *) Crosshair.AttachedObject.Ptr2;
1726 polyIndex =
1727 GetLowestDistancePolygonPoint (fake.poly, Note.X,
1728 Note.Y);
1729 fake.line.Point1 = fake.poly->Points[polyIndex];
1730 fake.line.Point2 = fake.poly->Points[
1731 prev_contour_point (fake.poly, polyIndex)];
1732 Crosshair.AttachedObject.Ptr2 = &fake.line;
1735 Crosshair.AttachedObject.State = STATE_SECOND;
1736 InsertedPoint = *AdjustInsertPoint ();
1739 break;
1741 /* second notify, insert new point into object */
1742 case STATE_SECOND:
1743 if (Crosshair.AttachedObject.Type == POLYGON_TYPE)
1744 InsertPointIntoObject (POLYGON_TYPE,
1745 Crosshair.AttachedObject.Ptr1, fake.poly,
1746 &polyIndex,
1747 InsertedPoint.X, InsertedPoint.Y, false, false);
1748 else
1749 InsertPointIntoObject (Crosshair.AttachedObject.Type,
1750 Crosshair.AttachedObject.Ptr1,
1751 Crosshair.AttachedObject.Ptr2,
1752 &polyIndex,
1753 InsertedPoint.X, InsertedPoint.Y, false, false);
1754 SetChangedFlag (true);
1756 /* reset identifiers */
1757 Crosshair.AttachedObject.Type = NO_TYPE;
1758 Crosshair.AttachedObject.State = STATE_FIRST;
1759 break;
1761 break;
1766 /* --------------------------------------------------------------------------- */
1768 static const char atomic_syntax[] = "Atomic(Save|Restore|Close|Block)";
1770 static const char atomic_help[] = "Save or restore the undo serial number.";
1772 /* %start-doc actions Atomic
1774 This action allows making multiple-action bindings into an atomic
1775 operation that will be undone by a single Undo command. For example,
1776 to optimize rat lines, you'd delete the rats and re-add them. To
1777 group these into a single undo, you'd want the deletions and the
1778 additions to have the same undo serial number. So, you @code{Save},
1779 delete the rats, @code{Restore}, add the rats - using the same serial
1780 number as the deletes, then @code{Block}, which checks to see if the
1781 deletions or additions actually did anything. If not, the serial
1782 number is set to the saved number, as there's nothing to undo. If
1783 something did happen, the serial number is incremented so that these
1784 actions are counted as a single undo step.
1786 @table @code
1788 @item Save
1789 Saves the undo serial number.
1791 @item Restore
1792 Returns it to the last saved number.
1794 @item Close
1795 Sets it to 1 greater than the last save.
1797 @item Block
1798 Does a Restore if there was nothing to undo, else does a Close.
1800 @end table
1802 %end-doc */
1804 static int
1805 ActionAtomic (int argc, char **argv, Coord x, Coord y)
1807 if (argc != 1)
1808 AFAIL (atomic);
1810 switch (GetFunctionID (argv[0]))
1812 case F_Save:
1813 SaveUndoSerialNumber ();
1814 break;
1815 case F_Restore:
1816 RestoreUndoSerialNumber ();
1817 break;
1818 case F_Close:
1819 RestoreUndoSerialNumber ();
1820 IncrementUndoSerialNumber ();
1821 break;
1822 case F_Block:
1823 RestoreUndoSerialNumber ();
1824 if (Bumped)
1825 IncrementUndoSerialNumber ();
1826 break;
1828 return 0;
1831 /* -------------------------------------------------------------------------- */
1833 static const char drc_syntax[] = "DRC()";
1835 static const char drc_help[] = "Invoke the DRC check.";
1837 /* %start-doc actions DRC
1839 Note that the design rule check uses the current board rule settings,
1840 not the current style settings.
1842 %end-doc */
1844 static int
1845 ActionDRCheck (int argc, char **argv, Coord x, Coord y)
1847 int count;
1849 if (gui->drc_gui == NULL || gui->drc_gui->log_drc_overview)
1851 Message (_("%m+Rules are minspace %$mS, minoverlap %$mS "
1852 "minwidth %$mS, minsilk %$mS\n"
1853 "min drill %$mS, min annular ring %$mS\n"),
1854 Settings.grid_unit->allow,
1855 PCB->Bloat, PCB->Shrink,
1856 PCB->minWid, PCB->minSlk,
1857 PCB->minDrill, PCB->minRing);
1859 count = DRCAll ();
1860 if (gui->drc_gui == NULL || gui->drc_gui->log_drc_overview)
1862 if (count == 0)
1863 Message (_("No DRC problems found.\n"));
1864 else if (count > 0)
1865 Message (_("Found %d design rule errors.\n"), count);
1866 else
1867 Message (_("Aborted DRC after %d design rule errors.\n"), -count);
1869 return 0;
1872 /* -------------------------------------------------------------------------- */
1874 static const char dumplibrary_syntax[] = "DumpLibrary()";
1876 static const char dumplibrary_help[] =
1877 "Display the entire contents of the libraries.";
1879 /* %start-doc actions DumpLibrary
1882 %end-doc */
1884 static int
1885 ActionDumpLibrary (int argc, char **argv, Coord x, Coord y)
1887 int i, j;
1889 printf ("**** Do not count on this format. It will change ****\n\n");
1890 printf ("MenuN = %d\n", Library.MenuN);
1891 printf ("MenuMax = %d\n", Library.MenuMax);
1892 for (i = 0; i < Library.MenuN; i++)
1894 printf ("Library #%d:\n", i);
1895 printf (" EntryN = %d\n", Library.Menu[i].EntryN);
1896 printf (" EntryMax = %d\n", Library.Menu[i].EntryMax);
1897 printf (" Name = \"%s\"\n", UNKNOWN (Library.Menu[i].Name));
1898 printf (" directory = \"%s\"\n",
1899 UNKNOWN (Library.Menu[i].directory));
1900 printf (" Style = \"%s\"\n", UNKNOWN (Library.Menu[i].Style));
1901 printf (" flag = %d\n", Library.Menu[i].flag);
1903 for (j = 0; j < Library.Menu[i].EntryN; j++)
1905 printf (" #%4d: ", j);
1906 if (Library.Menu[i].Entry[j].Template == (char *) -1)
1908 printf ("newlib: \"%s\"\n",
1909 UNKNOWN (Library.Menu[i].Entry[j].ListEntry));
1911 else
1913 printf ("\"%s\", \"%s\", \"%s\", \"%s\", \"%s\"\n",
1914 UNKNOWN (Library.Menu[i].Entry[j].ListEntry),
1915 UNKNOWN (Library.Menu[i].Entry[j].Template),
1916 UNKNOWN (Library.Menu[i].Entry[j].Package),
1917 UNKNOWN (Library.Menu[i].Entry[j].Value),
1918 UNKNOWN (Library.Menu[i].Entry[j].Description));
1923 return 0;
1926 /* -------------------------------------------------------------------------- */
1928 static const char flip_syntax[] = "Flip(Object|Selected|SelectedElements)";
1930 static const char flip_help[] =
1931 "Flip an element to the opposite side of the board.";
1933 /* %start-doc actions Flip
1935 Note that the location of the element will be symmetric about the
1936 cursor location; i.e. if the part you are pointing at will still be at
1937 the same spot once the element is on the other side. When flipping
1938 multiple elements, this retains their positions relative to each
1939 other, not their absolute positions on the board.
1941 %end-doc */
1943 static int
1944 ActionFlip (int argc, char **argv, Coord x, Coord y)
1946 char *function = ARG (0);
1947 ElementType *element;
1948 void *ptrtmp;
1949 int err = 0;
1951 if (function)
1953 switch (GetFunctionID (function))
1955 case F_Object:
1956 if ((SearchScreen (x, y, ELEMENT_TYPE,
1957 &ptrtmp, &ptrtmp, &ptrtmp)) != NO_TYPE)
1959 element = (ElementType *) ptrtmp;
1960 ChangeElementSide (element, 2 * Crosshair.Y - PCB->MaxHeight);
1961 IncrementUndoSerialNumber ();
1962 Draw ();
1964 break;
1965 case F_Selected:
1966 case F_SelectedElements:
1967 ChangeSelectedElementSide ();
1968 break;
1969 default:
1970 err = 1;
1971 break;
1973 if (!err)
1974 return 0;
1977 AFAIL (flip);
1980 /* -------------------------------------------------------------------------- */
1982 static const char message_syntax[] = "Message(message)";
1984 static const char message_help[] = "Writes a message to the log window.";
1986 /* %start-doc actions Message
1988 This action displays a message to the log window. This action is primarily
1989 provided for use by other programs which may interface with PCB. If
1990 multiple arguments are given, each one is sent to the log window
1991 followed by a newline.
1993 %end-doc */
1995 static int
1996 ActionMessage (int argc, char **argv, Coord x, Coord y)
1998 int i;
2000 if (argc < 1)
2001 AFAIL (message);
2003 for (i = 0; i < argc; i++)
2005 Message (argv[i]);
2006 Message ("\n");
2009 return 0;
2013 /* -------------------------------------------------------------------------- */
2015 static const char setthermal_syntax[] =
2016 "SetThermal(Object|SelectedPins|SelectedVias|Selected, Style)";
2018 static const char setthermal_help[] =
2019 "Set the thermal (on the current layer) of pins or vias to the given style.\n"
2020 "Style = 0 means no thermal.\n"
2021 "Style = 1 has diagonal fingers with sharp edges.\n"
2022 "Style = 2 has horizontal and vertical fingers with sharp edges.\n"
2023 "Style = 3 is a solid connection to the plane."
2024 "Style = 4 has diagonal fingers with rounded edges.\n"
2025 "Style = 5 has horizontal and vertical fingers with rounded edges.\n";
2027 /* %start-doc actions SetThermal
2029 This changes how/whether pins or vias connect to any rectangle or polygon
2030 on the current layer. The first argument can specify one object, or all
2031 selected pins, or all selected vias, or all selected pins and vias.
2032 The second argument specifies the style of connection.
2033 There are 5 possibilities:
2034 0 - no connection,
2035 1 - 45 degree fingers with sharp edges,
2036 2 - horizontal & vertical fingers with sharp edges,
2037 3 - solid connection,
2038 4 - 45 degree fingers with rounded corners,
2039 5 - horizontal & vertical fingers with rounded corners.
2041 Pins and Vias may have thermals whether or not there is a polygon available
2042 to connect with. However, they will have no effect without the polygon.
2043 %end-doc */
2045 static int
2046 ActionSetThermal (int argc, char **argv, Coord x, Coord y)
2048 char *function = ARG (0);
2049 char *style = ARG (1);
2050 void *ptr1, *ptr2, *ptr3;
2051 int type, kind;
2052 int err = 0;
2054 if (function && *function && style && *style)
2056 bool absolute;
2058 kind = GetValue (style, NULL, &absolute);
2059 if (absolute)
2060 switch (GetFunctionID (function))
2062 case F_Object:
2063 if ((type =
2064 SearchScreen (Crosshair.X, Crosshair.Y, CHANGETHERMAL_TYPES,
2065 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
2067 ChangeObjectThermal (type, ptr1, ptr2, ptr3, kind);
2068 IncrementUndoSerialNumber ();
2069 Draw ();
2071 break;
2072 case F_SelectedPins:
2073 ChangeSelectedThermals (PIN_TYPE, kind);
2074 break;
2075 case F_SelectedVias:
2076 ChangeSelectedThermals (VIA_TYPE, kind);
2077 break;
2078 case F_Selected:
2079 case F_SelectedElements:
2080 ChangeSelectedThermals (CHANGETHERMAL_TYPES, kind);
2081 break;
2082 default:
2083 err = 1;
2084 break;
2086 else
2087 err = 1;
2088 if (!err)
2089 return 0;
2092 AFAIL (setthermal);
2095 /* ---------------------------------------------------------------------------
2096 * !!! no action routine !!!
2098 * event handler to set the cursor according to the X pointer position
2099 * called from inside main.c
2101 void
2102 EventMoveCrosshair (int ev_x, int ev_y)
2104 #ifdef HAVE_LIBSTROKE
2105 if (mid_stroke)
2107 StrokeBox.X2 = ev_x;
2108 StrokeBox.Y2 = ev_y;
2109 stroke_record (ev_x, ev_y);
2110 return;
2112 #endif /* HAVE_LIBSTROKE */
2113 if (MoveCrosshairAbsolute (ev_x, ev_y))
2115 /* update object position and cursor location */
2116 AdjustAttachedObjects ();
2117 notify_crosshair_change (true);
2121 /* --------------------------------------------------------------------------- */
2123 static const char setvalue_syntax[] =
2124 "SetValue(Grid|Line|LineSize|Text|TextScale|ViaDrillingHole|Via|ViaSize, delta)";
2126 static const char setvalue_help[] =
2127 "Change various board-wide values and sizes.";
2129 /* %start-doc actions SetValue
2131 @table @code
2133 @item ViaDrillingHole
2134 Changes the diameter of the drill for new vias.
2136 @item Grid
2137 Sets the grid spacing.
2139 @item Line
2140 @item LineSize
2141 Changes the thickness of new lines.
2143 @item Via
2144 @item ViaSize
2145 Changes the diameter of new vias.
2147 @item Text
2148 @item TextScale
2149 Changes the size of new text.
2151 @end table
2153 %end-doc */
2155 static int
2156 ActionSetValue (int argc, char **argv, Coord x, Coord y)
2158 char *function = ARG (0);
2159 char *val = ARG (1);
2160 char *units = ARG (2);
2161 bool absolute; /* flag for 'absolute' value */
2162 double value;
2163 int text_scale;
2164 int err = 0;
2166 if (function && val)
2168 value = GetValue (val, units, &absolute);
2169 switch (GetFunctionID (function))
2171 case F_ViaDrillingHole:
2172 SetViaDrillingHole (absolute ? value :
2173 value + Settings.ViaDrillingHole,
2174 false);
2175 hid_action ("RouteStylesChanged");
2176 break;
2178 case F_Grid:
2179 if (absolute)
2180 SetGrid (value, false);
2181 else
2183 if (value == 0)
2184 value = val[0] == '-' ? -Settings.increments->grid
2185 : Settings.increments->grid;
2186 /* On the way down, short against the minimum
2187 * PCB drawing unit */
2188 if ((value + PCB->Grid) < 1)
2189 SetGrid (1, false);
2190 else if (PCB->Grid == 1)
2191 SetGrid (value, false);
2192 else
2193 SetGrid (value + PCB->Grid, false);
2195 break;
2197 case F_LineSize:
2198 case F_Line:
2199 if (!absolute && value == 0)
2200 value = val[0] == '-' ? -Settings.increments->line
2201 : Settings.increments->line;
2202 SetLineSize (absolute ? value : value + Settings.LineThickness);
2203 hid_action ("RouteStylesChanged");
2204 break;
2206 case F_Via:
2207 case F_ViaSize:
2208 SetViaSize (absolute ? value : value + Settings.ViaThickness, false);
2209 hid_action ("RouteStylesChanged");
2210 break;
2212 case F_Text:
2213 case F_TextScale:
2214 text_scale = value / (double)FONT_CAPHEIGHT * 100.;
2215 if (!absolute)
2216 text_scale += Settings.TextScale;
2217 SetTextScale (text_scale);
2218 break;
2219 default:
2220 err = 1;
2221 break;
2223 if (!err)
2224 return 0;
2227 AFAIL (setvalue);
2231 /* --------------------------------------------------------------------------- */
2233 static const char quit_syntax[] = "Quit()";
2235 static const char quit_help[] = "Quits the application after confirming.";
2237 /* %start-doc actions Quit
2239 If you have unsaved changes, you will be prompted to confirm (or
2240 save) before quitting.
2242 %end-doc */
2244 static int
2245 ActionQuit (int argc, char **argv, Coord x, Coord y)
2247 char *force = ARG (0);
2248 if (force && strcasecmp (force, "force") == 0)
2250 PCB->Changed = 0;
2251 exit (0);
2253 if (!PCB->Changed || gui->close_confirm_dialog () == HID_CLOSE_CONFIRM_OK)
2254 QuitApplication ();
2255 return 1;
2258 /* --------------------------------------------------------------------------- */
2260 static const char connection_syntax[] =
2261 "Connection(Find|ResetLinesAndPolygons|ResetPinsAndVias|Reset)";
2263 static const char connection_help[] =
2264 "Searches connections of the object at the cursor position.";
2266 /* %start-doc actions Connection
2268 Connections found with this action will be highlighted in the
2269 ``connected-color'' color and will have the ``found'' flag set.
2271 @table @code
2273 @item Find
2274 The net under the cursor is ``found''.
2276 @item ResetLinesAndPolygons
2277 Any ``found'' lines and polygons are marked ``not found''.
2279 @item ResetPinsAndVias
2280 Any ``found'' pins and vias are marked ``not found''.
2282 @item Reset
2283 All ``found'' objects are marked ``not found''.
2285 @end table
2287 %end-doc */
2289 static int
2290 ActionConnection (int argc, char **argv, Coord x, Coord y)
2292 char *function = ARG (0);
2293 if (function)
2295 switch (GetFunctionID (function))
2297 case F_Find:
2299 gui->get_coords (_("Click on a connection"), &x, &y);
2300 LookupConnection (x, y, true, 1, CONNECTEDFLAG, false);
2301 LookupConnection (x, y, true, 1, FOUNDFLAG, true);
2302 break;
2305 case F_ResetLinesAndPolygons:
2306 if (ClearFlagOnLinesAndPolygons (true, CONNECTEDFLAG | FOUNDFLAG))
2308 IncrementUndoSerialNumber ();
2309 Draw ();
2311 break;
2313 case F_ResetPinsViasAndPads:
2314 if (ClearFlagOnPinsViasAndPads (true, CONNECTEDFLAG | FOUNDFLAG))
2316 IncrementUndoSerialNumber ();
2317 Draw ();
2319 break;
2321 case F_Reset:
2322 if (ClearFlagOnAllObjects (true, CONNECTEDFLAG | FOUNDFLAG))
2324 IncrementUndoSerialNumber ();
2325 Draw ();
2327 break;
2329 return 0;
2332 AFAIL (connection);
2335 /* --------------------------------------------------------------------------- */
2337 static const char disperseelements_syntax[] =
2338 "DisperseElements(All|Selected)";
2340 static const char disperseelements_help[] = "Disperses elements.";
2342 /* %start-doc actions DisperseElements
2344 Normally this is used when starting a board, by selecting all elements
2345 and then dispersing them. This scatters the elements around the board
2346 so that you can pick individual ones, rather than have all the
2347 elements at the same 0,0 coordinate and thus impossible to choose
2348 from.
2350 %end-doc */
2352 #define GAP MIL_TO_COORD(100)
2354 static int
2355 ActionDisperseElements (int argc, char **argv, Coord x, Coord y)
2357 char *function = ARG (0);
2358 Coord minx = GAP,
2359 miny = GAP,
2360 maxy = GAP,
2361 dx, dy;
2362 int all = 0, bad = 0;
2364 if (!function || !*function)
2366 bad = 1;
2368 else
2370 switch (GetFunctionID (function))
2372 case F_All:
2373 all = 1;
2374 break;
2376 case F_Selected:
2377 all = 0;
2378 break;
2380 default:
2381 bad = 1;
2385 if (bad)
2387 AFAIL (disperseelements);
2391 ELEMENT_LOOP (PCB->Data);
2394 * If we want to disperse selected elements, maybe we need smarter
2395 * code here to avoid putting components on top of others which
2396 * are not selected. For now, I'm assuming that this is typically
2397 * going to be used either with a brand new design or a scratch
2398 * design holding some new components
2400 if (!TEST_FLAG (LOCKFLAG, element) && (all || TEST_FLAG (SELECTEDFLAG, element)))
2403 /* figure out how much to move the element */
2404 dx = minx - element->BoundingBox.X1;
2406 /* snap to the grid */
2407 dx -= (element->MarkX + dx) % PCB->Grid;
2410 * and add one grid size so we make sure we always space by GAP or
2411 * more
2413 dx += PCB->Grid;
2415 /* Figure out if this row has room. If not, start a new row */
2416 if (GAP + element->BoundingBox.X2 + dx > PCB->MaxWidth)
2418 miny = maxy + GAP;
2419 minx = GAP;
2422 /* figure out how much to move the element */
2423 dx = minx - element->BoundingBox.X1;
2424 dy = miny - element->BoundingBox.Y1;
2426 /* snap to the grid */
2427 dx -= (element->MarkX + dx) % PCB->Grid;
2428 dx += PCB->Grid;
2429 dy -= (element->MarkY + dy) % PCB->Grid;
2430 dy += PCB->Grid;
2432 /* move the element */
2433 MoveElementLowLevel (PCB->Data, element, dx, dy);
2435 /* and add to the undo list so we can undo this operation */
2436 AddObjectToMoveUndoList (ELEMENT_TYPE, NULL, NULL, element, dx, dy);
2438 /* keep track of how tall this row is */
2439 minx += element->BoundingBox.X2 - element->BoundingBox.X1 + GAP;
2440 if (maxy < element->BoundingBox.Y2)
2442 maxy = element->BoundingBox.Y2;
2447 END_LOOP;
2449 /* done with our action so increment the undo # */
2450 IncrementUndoSerialNumber ();
2452 Redraw ();
2453 SetChangedFlag (true);
2455 return 0;
2458 #undef GAP
2460 /* --------------------------------------------------------------------------- */
2462 static const char display_syntax[] =
2463 "Display(NameOnPCB|Description|Value)\n"
2464 "Display(Grid|Redraw)\n"
2465 "Display(CycleClip|CycleCrosshair|Toggle45Degree|ToggleStartDirection)\n"
2466 "Display(ToggleGrid|ToggleRubberBandMode|ToggleUniqueNames)\n"
2467 "Display(ToggleMask|ToggleName|ToggleClearLine|ToggleFullPoly|ToggleSnapPin)\n"
2468 "Display(ToggleThindraw|ToggleThindrawPoly|ToggleOrthoMove|ToggleLocalRef)\n"
2469 "Display(ToggleCheckPlanes|ToggleShowDRC|ToggleAutoDRC)\n"
2470 "Display(ToggleLiveRoute|LockNames|OnlyNames)\n"
2471 "Display(Pinout|PinOrPadName)";
2473 static const char display_help[] = "Several display-related actions.";
2475 /* %start-doc actions Display
2477 @table @code
2479 @item NameOnPCB
2480 @item Description
2481 @item Value
2482 Specify whether all elements show their name, description, or value.
2484 @item Redraw
2485 Redraw the whole board.
2487 @item Toggle45Degree
2488 When clear, lines can be drawn at any angle. When set, lines are
2489 restricted to multiples of 45 degrees and requested lines may be
2490 broken up according to the clip setting.
2492 @item CycleClip
2493 Changes the way lines are restricted to 45 degree increments. The
2494 various settings are: straight only, orthogonal then angled, and angled
2495 then orthogonal. If AllDirections is set, this action disables it.
2497 @item CycleCrosshair
2498 Changes crosshair drawing. Crosshair may accept form of 4-ray,
2499 8-ray and 12-ray cross.
2501 @item ToggleRubberBandMode
2502 If set, moving an object moves all the lines attached to it too.
2504 @item ToggleStartDirection
2505 If set, each time you set a point in a line, the Clip toggles between
2506 orth-angle and angle-ortho.
2508 @item ToggleUniqueNames
2509 If set, you will not be permitted to change the name of an element to
2510 match that of another element.
2512 @item ToggleSnapPin
2513 If set, pin centers and pad end points are treated as additional grid
2514 points that the cursor can snap to.
2516 @item ToggleLocalRef
2517 If set, the mark is automatically set to the beginning of any move, so
2518 you can see the relative distance you've moved.
2520 @item ToggleThindraw
2521 If set, objects on the screen are drawn as outlines (lines are drawn
2522 as center-lines). This lets you see line endpoints hidden under pins,
2523 for example.
2525 @item ToggleThindrawPoly
2526 If set, polygons on the screen are drawn as outlines.
2528 @item ToggleShowDRC
2529 If set, pending objects (i.e. lines you're in the process of drawing)
2530 will be drawn with an outline showing how far away from other copper
2531 you need to be.
2533 @item ToggleLiveRoute
2534 If set, the progress of the autorouter will be visible on the screen.
2536 @item ToggleAutoDRC
2537 If set, you will not be permitted to make connections which violate
2538 the current DRC and netlist settings.
2540 @item ToggleCheckPlanes
2541 If set, lines and arcs aren't drawn, which usually leaves just the
2542 polygons. If you also disable all but the layer you're interested in,
2543 this allows you to check for isolated regions.
2545 @item ToggleOrthoMove
2546 If set, the crosshair is only allowed to move orthogonally from its
2547 previous position. I.e. you can move an element or line up, down,
2548 left, or right, but not up+left or down+right.
2550 @item ToggleName
2551 Selects whether the pinouts show the pin names or the pin numbers.
2553 @item ToggleLockNames
2554 If set, text will ignore left mouse clicks and actions that work on
2555 objects under the mouse. You can still select text with a lasso (left
2556 mouse drag) and perform actions on the selection.
2558 @item ToggleOnlyNames
2559 If set, only text will be sensitive for mouse clicks and actions that
2560 work on objects under the mouse. You can still select other objects
2561 with a lasso (left mouse drag) and perform actions on the selection.
2563 @item ToggleMask
2564 Turns the solder mask on or off.
2566 @item ToggleClearLine
2567 When set, the clear-line flag causes new lines and arcs to have their
2568 ``clear polygons'' flag set, so they won't be electrically connected
2569 to any polygons they overlap.
2571 @item ToggleFullPoly
2572 When set, the full-poly flag causes new polygons to have their
2573 ``full polygon'' flag set, so all parts of them will be displayed
2574 instead of only the biggest one.
2576 @item ToggleGrid
2577 Resets the origin of the current grid to be wherever the mouse pointer
2578 is (not where the crosshair currently is). If you provide two numbers
2579 after this, the origin is set to that coordinate.
2581 @item Grid
2582 Toggles whether the grid is displayed or not.
2584 @item Pinout
2585 Causes the pinout of the element indicated by the cursor to be
2586 displayed, usually in a separate window.
2588 @item PinOrPadName
2589 Toggles whether the names of pins, pads, or (yes) vias will be
2590 displayed. If the cursor is over an element, all of its pins and pads
2591 are affected.
2593 @end table
2595 %end-doc */
2597 static enum crosshair_shape
2598 CrosshairShapeIncrement (enum crosshair_shape shape)
2600 switch(shape)
2602 case Basic_Crosshair_Shape:
2603 shape = Union_Jack_Crosshair_Shape;
2604 break;
2605 case Union_Jack_Crosshair_Shape:
2606 shape = Dozen_Crosshair_Shape;
2607 break;
2608 case Dozen_Crosshair_Shape:
2609 shape = Crosshair_Shapes_Number;
2610 break;
2611 case Crosshair_Shapes_Number:
2612 shape = Basic_Crosshair_Shape;
2613 break;
2615 return shape;
2618 static int
2619 ActionDisplay (int argc, char **argv, Coord childX, Coord childY)
2621 char *function, *str_dir;
2622 int id;
2623 int err = 0;
2625 function = ARG (0);
2626 str_dir = ARG (1);
2628 if (function && (!str_dir || !*str_dir))
2630 switch (id = GetFunctionID (function))
2633 /* redraw layout */
2634 case F_ClearAndRedraw:
2635 case F_Redraw:
2636 Redraw ();
2637 break;
2639 /* change the displayed name of elements */
2640 case F_Value:
2641 case F_NameOnPCB:
2642 case F_Description:
2643 ELEMENT_LOOP (PCB->Data);
2645 EraseElementName (element);
2647 END_LOOP;
2648 CLEAR_FLAG (DESCRIPTIONFLAG | NAMEONPCBFLAG, PCB);
2649 switch (id)
2651 case F_Value:
2652 break;
2653 case F_NameOnPCB:
2654 SET_FLAG (NAMEONPCBFLAG, PCB);
2655 break;
2656 case F_Description:
2657 SET_FLAG (DESCRIPTIONFLAG, PCB);
2658 break;
2660 ELEMENT_LOOP (PCB->Data);
2662 DrawElementName (element);
2664 END_LOOP;
2665 Draw ();
2666 break;
2668 /* toggle line-adjust flag */
2669 case F_ToggleAllDirections:
2670 TOGGLE_FLAG (ALLDIRECTIONFLAG, PCB);
2671 AdjustAttachedObjects ();
2672 break;
2674 case F_CycleClip:
2675 notify_crosshair_change (false);
2676 if (TEST_FLAG (ALLDIRECTIONFLAG, PCB))
2678 TOGGLE_FLAG (ALLDIRECTIONFLAG, PCB);
2679 PCB->Clipping = 0;
2681 else
2682 PCB->Clipping = (PCB->Clipping + 1) % 3;
2683 AdjustAttachedObjects ();
2684 notify_crosshair_change (true);
2685 break;
2687 case F_CycleCrosshair:
2688 notify_crosshair_change (false);
2689 Crosshair.shape = CrosshairShapeIncrement(Crosshair.shape);
2690 if (Crosshair_Shapes_Number == Crosshair.shape)
2691 Crosshair.shape = Basic_Crosshair_Shape;
2692 notify_crosshair_change (true);
2693 break;
2695 case F_ToggleRubberBandMode:
2696 notify_crosshair_change (false);
2697 TOGGLE_FLAG (RUBBERBANDFLAG, PCB);
2698 notify_crosshair_change (true);
2699 break;
2701 case F_ToggleStartDirection:
2702 notify_crosshair_change (false);
2703 TOGGLE_FLAG (SWAPSTARTDIRFLAG, PCB);
2704 notify_crosshair_change (true);
2705 break;
2707 case F_ToggleUniqueNames:
2708 TOGGLE_FLAG (UNIQUENAMEFLAG, PCB);
2709 break;
2711 case F_ToggleSnapPin:
2712 notify_crosshair_change (false);
2713 TOGGLE_FLAG (SNAPPINFLAG, PCB);
2714 notify_crosshair_change (true);
2715 break;
2717 case F_ToggleLocalRef:
2718 TOGGLE_FLAG (LOCALREFFLAG, PCB);
2719 break;
2721 case F_ToggleThindraw:
2722 TOGGLE_FLAG (THINDRAWFLAG, PCB);
2723 Redraw ();
2724 break;
2726 case F_ToggleThindrawPoly:
2727 TOGGLE_FLAG (THINDRAWPOLYFLAG, PCB);
2728 Redraw ();
2729 break;
2731 case F_ToggleLockNames:
2732 TOGGLE_FLAG (LOCKNAMESFLAG, PCB);
2733 CLEAR_FLAG (ONLYNAMESFLAG, PCB);
2734 break;
2736 case F_ToggleOnlyNames:
2737 TOGGLE_FLAG (ONLYNAMESFLAG, PCB);
2738 CLEAR_FLAG (LOCKNAMESFLAG, PCB);
2739 break;
2741 case F_ToggleHideNames:
2742 TOGGLE_FLAG (HIDENAMESFLAG, PCB);
2743 Redraw ();
2744 break;
2746 case F_ToggleShowDRC:
2747 TOGGLE_FLAG (SHOWDRCFLAG, PCB);
2748 break;
2750 case F_ToggleLiveRoute:
2751 TOGGLE_FLAG (LIVEROUTEFLAG, PCB);
2752 break;
2754 case F_ToggleAutoDRC:
2755 notify_crosshair_change (false);
2756 TOGGLE_FLAG (AUTODRCFLAG, PCB);
2757 if (TEST_FLAG (AUTODRCFLAG, PCB) && Settings.Mode == LINE_MODE)
2759 if (ClearFlagOnAllObjects (true, CONNECTEDFLAG | FOUNDFLAG))
2761 IncrementUndoSerialNumber ();
2762 Draw ();
2764 if (Crosshair.AttachedLine.State != STATE_FIRST)
2766 LookupConnection (Crosshair.AttachedLine.Point1.X,
2767 Crosshair.AttachedLine.Point1.Y,
2768 true, 1, CONNECTEDFLAG, false);
2769 LookupConnection (Crosshair.AttachedLine.Point1.X,
2770 Crosshair.AttachedLine.Point1.Y,
2771 true, 1, FOUNDFLAG, true);
2774 notify_crosshair_change (true);
2775 break;
2777 case F_ToggleCheckPlanes:
2778 TOGGLE_FLAG (CHECKPLANESFLAG, PCB);
2779 Redraw ();
2780 break;
2782 case F_ToggleOrthoMove:
2783 TOGGLE_FLAG (ORTHOMOVEFLAG, PCB);
2784 break;
2786 case F_ToggleName:
2787 TOGGLE_FLAG (SHOWNUMBERFLAG, PCB);
2788 Redraw ();
2789 break;
2791 case F_ToggleMask:
2792 TOGGLE_FLAG (SHOWMASKFLAG, PCB);
2793 Redraw ();
2794 break;
2796 case F_ToggleClearLine:
2797 TOGGLE_FLAG (CLEARNEWFLAG, PCB);
2798 break;
2800 case F_ToggleFullPoly:
2801 TOGGLE_FLAG (NEWFULLPOLYFLAG, PCB);
2802 break;
2804 /* shift grid alignment */
2805 case F_ToggleGrid:
2807 Coord oldGrid = PCB->Grid;
2809 PCB->Grid = 1;
2810 if (MoveCrosshairAbsolute (Crosshair.X, Crosshair.Y))
2811 notify_crosshair_change (true); /* first notify was in MoveCrosshairAbs */
2812 SetGrid (oldGrid, true);
2814 break;
2816 /* toggle displaying of the grid */
2817 case F_Grid:
2818 Settings.DrawGrid = !Settings.DrawGrid;
2819 Redraw ();
2820 break;
2822 /* display the pinout of an element */
2823 case F_Pinout:
2825 ElementType *element;
2826 void *ptrtmp;
2827 Coord x, y;
2829 gui->get_coords (_("Click on an element"), &x, &y);
2830 if ((SearchScreen
2831 (x, y, ELEMENT_TYPE, &ptrtmp,
2832 &ptrtmp, &ptrtmp)) != NO_TYPE)
2834 element = (ElementType *) ptrtmp;
2835 gui->show_item (element);
2837 break;
2840 /* toggle displaying of pin/pad/via names */
2841 case F_PinOrPadName:
2843 void *ptr1, *ptr2, *ptr3;
2845 switch (SearchScreen (Crosshair.X, Crosshair.Y,
2846 ELEMENT_TYPE | PIN_TYPE | PAD_TYPE |
2847 VIA_TYPE, (void **) &ptr1, (void **) &ptr2,
2848 (void **) &ptr3))
2850 case ELEMENT_TYPE:
2851 PIN_LOOP ((ElementType *) ptr1);
2853 if (TEST_FLAG (DISPLAYNAMEFLAG, pin))
2854 ErasePinName (pin);
2855 else
2856 DrawPinName (pin);
2857 AddObjectToFlagUndoList (PIN_TYPE, ptr1, pin, pin);
2858 TOGGLE_FLAG (DISPLAYNAMEFLAG, pin);
2860 END_LOOP;
2861 PAD_LOOP ((ElementType *) ptr1);
2863 if (TEST_FLAG (DISPLAYNAMEFLAG, pad))
2864 ErasePadName (pad);
2865 else
2866 DrawPadName (pad);
2867 AddObjectToFlagUndoList (PAD_TYPE, ptr1, pad, pad);
2868 TOGGLE_FLAG (DISPLAYNAMEFLAG, pad);
2870 END_LOOP;
2871 SetChangedFlag (true);
2872 IncrementUndoSerialNumber ();
2873 Draw ();
2874 break;
2876 case PIN_TYPE:
2877 if (TEST_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2))
2878 ErasePinName ((PinType *) ptr2);
2879 else
2880 DrawPinName ((PinType *) ptr2);
2881 AddObjectToFlagUndoList (PIN_TYPE, ptr1, ptr2, ptr3);
2882 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2);
2883 SetChangedFlag (true);
2884 IncrementUndoSerialNumber ();
2885 Draw ();
2886 break;
2888 case PAD_TYPE:
2889 if (TEST_FLAG (DISPLAYNAMEFLAG, (PadType *) ptr2))
2890 ErasePadName ((PadType *) ptr2);
2891 else
2892 DrawPadName ((PadType *) ptr2);
2893 AddObjectToFlagUndoList (PAD_TYPE, ptr1, ptr2, ptr3);
2894 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PadType *) ptr2);
2895 SetChangedFlag (true);
2896 IncrementUndoSerialNumber ();
2897 Draw ();
2898 break;
2899 case VIA_TYPE:
2900 if (TEST_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2))
2901 EraseViaName ((PinType *) ptr2);
2902 else
2903 DrawViaName ((PinType *) ptr2);
2904 AddObjectToFlagUndoList (VIA_TYPE, ptr1, ptr2, ptr3);
2905 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2);
2906 SetChangedFlag (true);
2907 IncrementUndoSerialNumber ();
2908 Draw ();
2909 break;
2911 break;
2913 default:
2914 err = 1;
2917 else if (function && str_dir)
2919 switch (GetFunctionID (function))
2921 case F_ToggleGrid:
2922 if (argc > 2)
2924 PCB->GridOffsetX = GetValue (argv[1], NULL, NULL);
2925 PCB->GridOffsetY = GetValue (argv[2], NULL, NULL);
2926 if (Settings.DrawGrid)
2927 Redraw ();
2929 break;
2931 default:
2932 err = 1;
2933 break;
2937 if (!err)
2938 return 0;
2940 AFAIL (display);
2943 /* --------------------------------------------------------------------------- */
2945 static const char mode_syntax[] =
2946 "Mode(Arc|Arrow|Copy|InsertPoint|Line|Lock|Move|None|PasteBuffer)\n"
2947 "Mode(Polygon|Rectangle|Remove|Rotate|Text|Thermal|Via)\n"
2948 "Mode(Notify|Release|Cancel|Stroke)\n"
2949 "Mode(Save|Restore)";
2951 static const char mode_help[] = "Change or use the tool mode.";
2953 /* %start-doc actions Mode
2955 @table @code
2957 @item Arc
2958 @itemx Arrow
2959 @itemx Copy
2960 @itemx InsertPoint
2961 @itemx Line
2962 @itemx Lock
2963 @itemx Move
2964 @itemx None
2965 @itemx PasteBuffer
2966 @itemx Polygon
2967 @itemx Rectangle
2968 @itemx Remove
2969 @itemx Rotate
2970 @itemx Text
2971 @itemx Thermal
2972 @itemx Via
2973 Select the indicated tool.
2975 @item Notify
2976 Called when you press the mouse button, or move the mouse.
2978 @item Release
2979 Called when you release the mouse button.
2981 @item Cancel
2982 Cancels any pending tool activity, allowing you to restart elsewhere.
2983 For example, this allows you to start a new line rather than attach a
2984 line to the previous line.
2986 @item Escape
2987 Similar to Cancel but calling this action a second time will return
2988 to the Arrow tool.
2990 @item Stroke
2991 If your @code{pcb} was built with libstroke, this invokes the stroke
2992 input method. If not, this will restart a drawing mode if you were
2993 drawing, else it will select objects.
2995 @item Save
2996 Remembers the current tool.
2998 @item Restore
2999 Restores the tool to the last saved tool.
3001 @end table
3003 %end-doc */
3005 static int
3006 ActionMode (int argc, char **argv, Coord x, Coord y)
3008 char *function = ARG (0);
3010 if (function)
3012 Note.X = Crosshair.X;
3013 Note.Y = Crosshair.Y;
3014 notify_crosshair_change (false);
3015 switch (GetFunctionID (function))
3017 case F_Arc:
3018 SetMode (ARC_MODE);
3019 break;
3020 case F_Arrow:
3021 SetMode (ARROW_MODE);
3022 break;
3023 case F_Copy:
3024 SetMode (COPY_MODE);
3025 break;
3026 case F_InsertPoint:
3027 SetMode (INSERTPOINT_MODE);
3028 break;
3029 case F_Line:
3030 SetMode (LINE_MODE);
3031 break;
3032 case F_Lock:
3033 SetMode (LOCK_MODE);
3034 break;
3035 case F_Move:
3036 SetMode (MOVE_MODE);
3037 break;
3038 case F_None:
3039 SetMode (NO_MODE);
3040 break;
3041 case F_Cancel:
3043 int saved_mode = Settings.Mode;
3044 SetMode (NO_MODE);
3045 SetMode (saved_mode);
3047 break;
3048 case F_Escape:
3050 switch (Settings.Mode)
3052 case VIA_MODE:
3053 case PASTEBUFFER_MODE:
3054 case TEXT_MODE:
3055 case ROTATE_MODE:
3056 case REMOVE_MODE:
3057 case MOVE_MODE:
3058 case COPY_MODE:
3059 case INSERTPOINT_MODE:
3060 case RUBBERBANDMOVE_MODE:
3061 case THERMAL_MODE:
3062 case LOCK_MODE:
3063 SetMode (NO_MODE);
3064 SetMode (ARROW_MODE);
3065 break;
3067 case LINE_MODE:
3068 if (Crosshair.AttachedLine.State == STATE_FIRST)
3069 SetMode (ARROW_MODE);
3070 else
3072 SetMode (NO_MODE);
3073 SetMode (LINE_MODE);
3075 break;
3077 case RECTANGLE_MODE:
3078 if (Crosshair.AttachedBox.State == STATE_FIRST)
3079 SetMode (ARROW_MODE);
3080 else
3082 SetMode (NO_MODE);
3083 SetMode (RECTANGLE_MODE);
3085 break;
3087 case POLYGON_MODE:
3088 if (Crosshair.AttachedLine.State == STATE_FIRST)
3089 SetMode (ARROW_MODE);
3090 else
3092 SetMode (NO_MODE);
3093 SetMode (POLYGON_MODE);
3095 break;
3097 case POLYGONHOLE_MODE:
3098 if (Crosshair.AttachedLine.State == STATE_FIRST)
3099 SetMode (ARROW_MODE);
3100 else
3102 SetMode (NO_MODE);
3103 SetMode (POLYGONHOLE_MODE);
3105 break;
3107 case ARC_MODE:
3108 if (Crosshair.AttachedBox.State == STATE_FIRST)
3109 SetMode (ARROW_MODE);
3110 else
3112 SetMode (NO_MODE);
3113 SetMode (ARC_MODE);
3115 break;
3117 case ARROW_MODE:
3118 break;
3120 default:
3121 break;
3124 break;
3126 case F_Notify:
3127 NotifyMode ();
3128 break;
3129 case F_PasteBuffer:
3130 SetMode (PASTEBUFFER_MODE);
3131 break;
3132 case F_Polygon:
3133 SetMode (POLYGON_MODE);
3134 break;
3135 case F_PolygonHole:
3136 SetMode (POLYGONHOLE_MODE);
3137 break;
3138 #ifndef HAVE_LIBSTROKE
3139 case F_Release:
3140 ReleaseMode ();
3141 break;
3142 #else
3143 case F_Release:
3144 if (mid_stroke)
3145 FinishStroke ();
3146 else
3147 ReleaseMode ();
3148 break;
3149 #endif
3150 case F_Remove:
3151 SetMode (REMOVE_MODE);
3152 break;
3153 case F_Rectangle:
3154 SetMode (RECTANGLE_MODE);
3155 break;
3156 case F_Rotate:
3157 SetMode (ROTATE_MODE);
3158 break;
3159 case F_Stroke:
3160 #ifdef HAVE_LIBSTROKE
3161 mid_stroke = true;
3162 StrokeBox.X1 = Crosshair.X;
3163 StrokeBox.Y1 = Crosshair.Y;
3164 break;
3165 #else
3166 /* Handle middle mouse button restarts of drawing mode. If not in
3167 | a drawing mode, middle mouse button will select objects.
3169 if (Settings.Mode == LINE_MODE
3170 && Crosshair.AttachedLine.State != STATE_FIRST)
3172 SetMode (LINE_MODE);
3174 else if (Settings.Mode == ARC_MODE
3175 && Crosshair.AttachedBox.State != STATE_FIRST)
3176 SetMode (ARC_MODE);
3177 else if (Settings.Mode == RECTANGLE_MODE
3178 && Crosshair.AttachedBox.State != STATE_FIRST)
3179 SetMode (RECTANGLE_MODE);
3180 else if (Settings.Mode == POLYGON_MODE
3181 && Crosshair.AttachedLine.State != STATE_FIRST)
3182 SetMode (POLYGON_MODE);
3183 else
3185 SaveMode ();
3186 saved_mode = true;
3187 SetMode (ARROW_MODE);
3188 NotifyMode ();
3190 break;
3191 #endif
3192 case F_Text:
3193 SetMode (TEXT_MODE);
3194 break;
3195 case F_Thermal:
3196 SetMode (THERMAL_MODE);
3197 break;
3198 case F_Via:
3199 SetMode (VIA_MODE);
3200 break;
3202 case F_Restore: /* restore the last saved mode */
3203 RestoreMode ();
3204 break;
3206 case F_Save: /* save currently selected mode */
3207 SaveMode ();
3208 break;
3210 notify_crosshair_change (true);
3211 return 0;
3214 AFAIL (mode);
3217 /* --------------------------------------------------------------------------- */
3219 static const char removeselected_syntax[] = "RemoveSelected()";
3221 static const char removeselected_help[] = "Removes any selected objects.";
3223 /* %start-doc actions RemoveSelected
3225 %end-doc */
3227 static int
3228 ActionRemoveSelected (int argc, char **argv, Coord x, Coord y)
3230 if (RemoveSelected ())
3231 SetChangedFlag (true);
3232 return 0;
3235 /* --------------------------------------------------------------------------- */
3237 static const char renumber_syntax[] = "Renumber()\n"
3238 "Renumber(filename)";
3240 static const char renumber_help[] =
3241 "Renumber all elements. The changes will be recorded to filename\n"
3242 "for use in backannotating these changes to the schematic.";
3244 /* %start-doc actions Renumber
3246 %end-doc */
3248 static int
3249 ActionRenumber (int argc, char **argv, Coord x, Coord y)
3251 bool changed = false;
3252 ElementType **element_list;
3253 ElementType **locked_element_list;
3254 unsigned int i, j, k, cnt, lock_cnt;
3255 unsigned int tmpi;
3256 size_t sz;
3257 char *tmps;
3258 char *name;
3259 FILE *out;
3260 static char * default_file = NULL;
3261 size_t cnt_list_sz = 100;
3262 struct _cnt_list
3264 char *name;
3265 unsigned int cnt;
3266 } *cnt_list;
3267 char **was, **is, *pin;
3268 unsigned int c_cnt = 0;
3269 int unique, ok;
3270 int free_name = 0;
3272 if (argc < 1)
3275 * We deal with the case where name already exists in this
3276 * function so the GUI doesn't need to deal with it
3278 name = gui->fileselect (_("Save Renumber Annotation File As ..."),
3279 _("Choose a file to record the renumbering to.\n"
3280 "This file may be used to back annotate the\n"
3281 "change to the schematics.\n"),
3282 default_file, ".eco", "eco",
3285 free_name = 1;
3287 else
3288 name = argv[0];
3290 if (default_file)
3292 free (default_file);
3293 default_file = NULL;
3296 if (name && *name)
3298 default_file = strdup (name);
3301 if ((out = fopen (name, "r")))
3303 fclose (out);
3304 if (!gui->confirm_dialog (_("File exists! Ok to overwrite?"), 0))
3306 if (free_name && name)
3307 free (name);
3308 return 0;
3312 if ((out = fopen (name, "w")) == NULL)
3314 Message (_("Could not open %s\n"), name);
3315 if (free_name && name)
3316 free (name);
3317 return 1;
3320 if (free_name && name)
3321 free (name);
3323 fprintf (out, "*COMMENT* PCB Annotation File\n");
3324 fprintf (out, "*FILEVERSION* 20061031\n");
3327 * Make a first pass through all of the elements and sort them out
3328 * by location on the board. While here we also collect a list of
3329 * locked elements.
3331 * We'll actually renumber things in the 2nd pass.
3333 element_list = (ElementType **)calloc (PCB->Data->ElementN, sizeof (ElementType *));
3334 locked_element_list = (ElementType **)calloc (PCB->Data->ElementN, sizeof (ElementType *));
3335 was = (char **)calloc (PCB->Data->ElementN, sizeof (char *));
3336 is = (char **)calloc (PCB->Data->ElementN, sizeof (char *));
3337 if (element_list == NULL || locked_element_list == NULL || was == NULL
3338 || is == NULL)
3340 fprintf (stderr, "calloc() failed in %s\n", __FUNCTION__);
3341 exit (1);
3345 cnt = 0;
3346 lock_cnt = 0;
3347 ELEMENT_LOOP (PCB->Data);
3349 if (TEST_FLAG (LOCKFLAG, element->Name) || TEST_FLAG (LOCKFLAG, element))
3352 * add to the list of locked elements which we won't try to
3353 * renumber and whose reference designators are now reserved.
3355 pcb_fprintf (out,
3356 "*WARN* Element \"%s\" at %$md is locked and will not be renumbered.\n",
3357 UNKNOWN (NAMEONPCB_NAME (element)), element->MarkX, element->MarkY);
3358 locked_element_list[lock_cnt] = element;
3359 lock_cnt++;
3362 else
3364 /* count of devices which will be renumbered */
3365 cnt++;
3367 /* search for correct position in the list */
3368 i = 0;
3369 while (element_list[i] && element->MarkY > element_list[i]->MarkY)
3370 i++;
3373 * We have found the position where we have the first element that
3374 * has the same Y value or a lower Y value. Now move forward if
3375 * needed through the X values
3377 while (element_list[i]
3378 && element->MarkY == element_list[i]->MarkY
3379 && element->MarkX > element_list[i]->MarkX)
3380 i++;
3382 for (j = cnt - 1; j > i; j--)
3384 element_list[j] = element_list[j - 1];
3386 element_list[i] = element;
3389 END_LOOP;
3393 * Now that the elements are sorted by board position, we go through
3394 * and renumber them.
3398 * turn off the flag which requires unique names so it doesn't get
3399 * in our way. When we're done with the renumber we will have unique
3400 * names.
3402 unique = TEST_FLAG (UNIQUENAMEFLAG, PCB);
3403 CLEAR_FLAG (UNIQUENAMEFLAG, PCB);
3405 cnt_list = (struct _cnt_list *)calloc (cnt_list_sz, sizeof (struct _cnt_list));
3406 for (i = 0; i < cnt; i++)
3408 /* If there is no refdes, maybe just spit out a warning */
3409 if (NAMEONPCB_NAME (element_list[i]))
3411 /* figure out the prefix */
3412 tmps = strdup (NAMEONPCB_NAME (element_list[i]));
3413 j = 0;
3414 while (tmps[j] && (tmps[j] < '0' || tmps[j] > '9')
3415 && tmps[j] != '?')
3416 j++;
3417 tmps[j] = '\0';
3419 /* check the counter for this prefix */
3420 for (j = 0;
3421 cnt_list[j].name && (strcmp (cnt_list[j].name, tmps) != 0)
3422 && j < cnt_list_sz; j++);
3424 /* grow the list if needed */
3425 if (j == cnt_list_sz)
3427 cnt_list_sz += 100;
3428 cnt_list = (struct _cnt_list *)realloc (cnt_list, cnt_list_sz);
3429 if (cnt_list == NULL)
3431 fprintf (stderr, "realloc failed() in %s\n", __FUNCTION__);
3432 exit (1);
3434 /* zero out the memory that we added */
3435 for (tmpi = j; tmpi < cnt_list_sz; tmpi++)
3437 cnt_list[tmpi].name = NULL;
3438 cnt_list[tmpi].cnt = 0;
3443 * start a new counter if we don't have a counter for this
3444 * prefix
3446 if (!cnt_list[j].name)
3448 cnt_list[j].name = strdup (tmps);
3449 cnt_list[j].cnt = 0;
3453 * check to see if the new refdes is already used by a
3454 * locked element
3458 ok = 1;
3459 cnt_list[j].cnt++;
3460 free (tmps);
3462 /* space for the prefix plus 1 digit plus the '\0' */
3463 sz = strlen (cnt_list[j].name) + 2;
3465 /* and 1 more per extra digit needed to hold the number */
3466 tmpi = cnt_list[j].cnt;
3467 while (tmpi > 10)
3469 sz++;
3470 tmpi = tmpi / 10;
3472 tmps = (char *)malloc (sz * sizeof (char));
3473 sprintf (tmps, "%s%d", cnt_list[j].name, cnt_list[j].cnt);
3476 * now compare to the list of reserved (by locked
3477 * elements) names
3479 for (k = 0; k < lock_cnt; k++)
3481 if (strcmp
3482 (UNKNOWN (NAMEONPCB_NAME (locked_element_list[k])),
3483 tmps) == 0)
3485 ok = 0;
3486 break;
3491 while (!ok);
3493 if (strcmp (tmps, NAMEONPCB_NAME (element_list[i])) != 0)
3495 fprintf (out, "*RENAME* \"%s\" \"%s\"\n",
3496 NAMEONPCB_NAME (element_list[i]), tmps);
3498 /* add this rename to our table of renames so we can update the netlist */
3499 was[c_cnt] = strdup (NAMEONPCB_NAME (element_list[i]));
3500 is[c_cnt] = strdup (tmps);
3501 c_cnt++;
3503 AddObjectToChangeNameUndoList (ELEMENT_TYPE, NULL, NULL,
3504 element_list[i],
3505 NAMEONPCB_NAME (element_list
3506 [i]));
3508 ChangeObjectName (ELEMENT_TYPE, element_list[i], NULL, NULL,
3509 tmps);
3510 changed = true;
3512 /* we don't free tmps in this case because it is used */
3514 else
3515 free (tmps);
3517 else
3519 pcb_fprintf (out, "*WARN* Element at %$md has no name.\n",
3520 element_list[i]->MarkX, element_list[i]->MarkY);
3525 fclose (out);
3527 /* restore the unique flag setting */
3528 if (unique)
3529 SET_FLAG (UNIQUENAMEFLAG, PCB);
3531 if (changed)
3534 /* update the netlist */
3535 AddNetlistLibToUndoList (&(PCB->NetlistLib));
3537 /* iterate over each net */
3538 for (i = 0; i < PCB->NetlistLib.MenuN; i++)
3541 /* iterate over each pin on the net */
3542 for (j = 0; j < PCB->NetlistLib.Menu[i].EntryN; j++)
3545 /* figure out the pin number part from strings like U3-21 */
3546 tmps = strdup (PCB->NetlistLib.Menu[i].Entry[j].ListEntry);
3547 for (k = 0; tmps[k] && tmps[k] != '-'; k++);
3548 tmps[k] = '\0';
3549 pin = tmps + k + 1;
3551 /* iterate over the list of changed reference designators */
3552 for (k = 0; k < c_cnt; k++)
3555 * if the pin needs to change, change it and quit
3556 * searching in the list.
3558 if (strcmp (tmps, was[k]) == 0)
3560 free (PCB->NetlistLib.Menu[i].Entry[j].ListEntry);
3561 PCB->NetlistLib.Menu[i].Entry[j].ListEntry =
3562 (char *)malloc ((strlen (is[k]) + strlen (pin) +
3563 2) * sizeof (char));
3564 sprintf (PCB->NetlistLib.Menu[i].Entry[j].ListEntry,
3565 "%s-%s", is[k], pin);
3566 k = c_cnt;
3570 free (tmps);
3573 for (k = 0; k < c_cnt; k++)
3575 free (was[k]);
3576 free (is[k]);
3579 NetlistChanged (0);
3580 IncrementUndoSerialNumber ();
3581 SetChangedFlag (true);
3584 free (locked_element_list);
3585 free (element_list);
3586 free (cnt_list);
3587 return 0;
3591 /* --------------------------------------------------------------------------- */
3593 static const char ripup_syntax[] = "RipUp(All|Selected|Element)";
3595 static const char ripup_help[] =
3596 "Ripup auto-routed tracks, or convert an element to parts.";
3598 /* %start-doc actions RipUp
3600 @table @code
3602 @item All
3603 Removes all lines and vias which were created by the autorouter.
3605 @item Selected
3606 Removes all selected lines and vias which were created by the
3607 autorouter.
3609 @item Element
3610 Converts the element under the cursor to parts (vias and lines). Note
3611 that this uses the highest numbered paste buffer.
3613 @end table
3615 %end-doc */
3617 static int
3618 ActionRipUp (int argc, char **argv, Coord x, Coord y)
3620 char *function = ARG (0);
3621 bool changed = false;
3623 if (function)
3625 switch (GetFunctionID (function))
3627 case F_All:
3628 ALLLINE_LOOP (PCB->Data);
3630 if (TEST_FLAG (AUTOFLAG, line) && !TEST_FLAG (LOCKFLAG, line))
3632 RemoveObject (LINE_TYPE, layer, line, line);
3633 changed = true;
3636 ENDALL_LOOP;
3637 ALLARC_LOOP (PCB->Data);
3639 if (TEST_FLAG (AUTOFLAG, arc) && !TEST_FLAG (LOCKFLAG, arc))
3641 RemoveObject (ARC_TYPE, layer, arc, arc);
3642 changed = true;
3645 ENDALL_LOOP;
3646 VIA_LOOP (PCB->Data);
3648 if (TEST_FLAG (AUTOFLAG, via) && !TEST_FLAG (LOCKFLAG, via))
3650 RemoveObject (VIA_TYPE, via, via, via);
3651 changed = true;
3654 END_LOOP;
3656 if (changed)
3658 IncrementUndoSerialNumber ();
3659 SetChangedFlag (true);
3661 break;
3662 case F_Selected:
3663 VISIBLELINE_LOOP (PCB->Data);
3665 if (TEST_FLAGS (AUTOFLAG | SELECTEDFLAG, line)
3666 && !TEST_FLAG (LOCKFLAG, line))
3668 RemoveObject (LINE_TYPE, layer, line, line);
3669 changed = true;
3672 ENDALL_LOOP;
3673 if (PCB->ViaOn)
3674 VIA_LOOP (PCB->Data);
3676 if (TEST_FLAGS (AUTOFLAG | SELECTEDFLAG, via)
3677 && !TEST_FLAG (LOCKFLAG, via))
3679 RemoveObject (VIA_TYPE, via, via, via);
3680 changed = true;
3683 END_LOOP;
3684 if (changed)
3686 IncrementUndoSerialNumber ();
3687 SetChangedFlag (true);
3689 break;
3690 case F_Element:
3692 void *ptr1, *ptr2, *ptr3;
3694 if (SearchScreen (Crosshair.X, Crosshair.Y, ELEMENT_TYPE,
3695 &ptr1, &ptr2, &ptr3) != NO_TYPE)
3697 Note.Buffer = Settings.BufferNumber;
3698 SetBufferNumber (MAX_BUFFER - 1);
3699 ClearBuffer (PASTEBUFFER);
3700 CopyObjectToBuffer (PASTEBUFFER->Data, PCB->Data,
3701 ELEMENT_TYPE, ptr1, ptr2, ptr3);
3702 SmashBufferElement (PASTEBUFFER);
3703 PASTEBUFFER->X = 0;
3704 PASTEBUFFER->Y = 0;
3705 SaveUndoSerialNumber ();
3706 EraseObject (ELEMENT_TYPE, ptr1, ptr1);
3707 MoveObjectToRemoveUndoList (ELEMENT_TYPE, ptr1, ptr2, ptr3);
3708 RestoreUndoSerialNumber ();
3709 CopyPastebufferToLayout (0, 0);
3710 SetBufferNumber (Note.Buffer);
3711 SetChangedFlag (true);
3714 break;
3717 return 0;
3720 /* --------------------------------------------------------------------------- */
3722 static const char addrats_syntax[] = "AddRats(AllRats|SelectedRats|Close)";
3724 static const char addrats_help[] = "Add one or more rat lines to the board.";
3726 /* %start-doc actions AddRats
3728 @table @code
3730 @item AllRats
3731 Create rat lines for all loaded nets that aren't already connected on
3732 with copper.
3734 @item SelectedRats
3735 Similarly, but only add rat lines for nets connected to selected pins
3736 and pads.
3738 @item Close
3739 Selects the shortest unselected rat on the board.
3741 @end table
3743 %end-doc */
3745 static int
3746 ActionAddRats (int argc, char **argv, Coord x, Coord y)
3748 char *function = ARG (0);
3749 RatType *shorty;
3750 float len, small;
3752 if (function)
3754 if (Settings.RatWarn)
3755 ClearWarnings ();
3756 switch (GetFunctionID (function))
3758 case F_AllRats:
3759 if (AddAllRats (false, NULL))
3760 SetChangedFlag (true);
3761 break;
3762 case F_SelectedRats:
3763 case F_Selected:
3764 if (AddAllRats (true, NULL))
3765 SetChangedFlag (true);
3766 break;
3767 case F_Close:
3768 small = SQUARE (MAX_COORD);
3769 shorty = NULL;
3770 RAT_LOOP (PCB->Data);
3772 if (TEST_FLAG (SELECTEDFLAG, line))
3773 continue;
3774 len = SQUARE (line->Point1.X - line->Point2.X) +
3775 SQUARE (line->Point1.Y - line->Point2.Y);
3776 if (len < small)
3778 small = len;
3779 shorty = line;
3782 END_LOOP;
3783 if (shorty)
3785 AddObjectToFlagUndoList (RATLINE_TYPE, shorty, shorty, shorty);
3786 SET_FLAG (SELECTEDFLAG, shorty);
3787 DrawRat (shorty);
3788 Draw ();
3789 CenterDisplay ((shorty->Point2.X + shorty->Point1.X) / 2,
3790 (shorty->Point2.Y + shorty->Point1.Y) / 2);
3792 break;
3795 return 0;
3798 /* --------------------------------------------------------------------------- */
3800 static const char delete_syntax[] =
3801 "Delete(Object|Selected)\n"
3802 "Delete(AllRats|SelectedRats)";
3804 static const char delete_help[] = "Delete stuff.";
3806 /* %start-doc actions Delete
3808 %end-doc */
3810 static int
3811 ActionDelete (int argc, char **argv, Coord x, Coord y)
3813 char *function = ARG (0);
3814 int id = GetFunctionID (function);
3816 Note.X = Crosshair.X;
3817 Note.Y = Crosshair.Y;
3819 if (id == -1) /* no arg */
3821 if (RemoveSelected() == false)
3822 id = F_Object;
3825 switch (id)
3827 case F_Object:
3828 SaveMode();
3829 SetMode(REMOVE_MODE);
3830 NotifyMode();
3831 RestoreMode();
3832 break;
3833 case F_Selected:
3834 RemoveSelected();
3835 break;
3836 case F_AllRats:
3837 if (DeleteRats (false))
3838 SetChangedFlag (true);
3839 break;
3840 case F_SelectedRats:
3841 if (DeleteRats (true))
3842 SetChangedFlag (true);
3843 break;
3846 return 0;
3849 /* --------------------------------------------------------------------------- */
3851 static const char deleterats_syntax[] =
3852 "DeleteRats(AllRats|Selected|SelectedRats)";
3854 static const char deleterats_help[] = "Delete rat lines.";
3856 /* %start-doc actions DeleteRats
3858 %end-doc */
3860 static int
3861 ActionDeleteRats (int argc, char **argv, Coord x, Coord y)
3863 char *function = ARG (0);
3864 if (function)
3866 if (Settings.RatWarn)
3867 ClearWarnings ();
3868 switch (GetFunctionID (function))
3870 case F_AllRats:
3871 if (DeleteRats (false))
3872 SetChangedFlag (true);
3873 break;
3874 case F_SelectedRats:
3875 case F_Selected:
3876 if (DeleteRats (true))
3877 SetChangedFlag (true);
3878 break;
3881 return 0;
3884 /* --------------------------------------------------------------------------- */
3886 static const char autoplace_syntax[] = "AutoPlaceSelected()";
3888 static const char autoplace_help[] = "Auto-place selected components.";
3890 /* %start-doc actions AutoPlaceSelected
3892 Attempts to re-arrange the selected components such that the nets
3893 connecting them are minimized. Note that you cannot undo this.
3895 %end-doc */
3897 static int
3898 ActionAutoPlaceSelected (int argc, char **argv, Coord x, Coord y)
3900 hid_action("Busy");
3901 if (gui->confirm_dialog (_("Auto-placement can NOT be undone.\n"
3902 "Do you want to continue anyway?\n"), 0))
3904 if (AutoPlaceSelected ())
3905 SetChangedFlag (true);
3907 return 0;
3910 /* --------------------------------------------------------------------------- */
3912 static const char autoroute_syntax[] = "AutoRoute(AllRats|SelectedRats)";
3914 static const char autoroute_help[] = "Auto-route some or all rat lines.";
3916 /* %start-doc actions AutoRoute
3918 @table @code
3920 @item AllRats
3921 Attempt to autoroute all rats.
3923 @item SelectedRats
3924 Attempt to autoroute the selected rats.
3926 @end table
3928 Before autorouting, it's important to set up a few things. First,
3929 make sure any layers you aren't using are disabled, else the
3930 autorouter may use them. Next, make sure the current line and via
3931 styles are set accordingly. Last, make sure "new lines clear
3932 polygons" is set, in case you eventually want to add a copper pour.
3934 Autorouting takes a while. During this time, the program may not be
3935 responsive.
3937 %end-doc */
3939 static int
3940 ActionAutoRoute (int argc, char **argv, Coord x, Coord y)
3942 char *function = ARG (0);
3943 hid_action("Busy");
3944 if (function) /* one parameter */
3946 switch (GetFunctionID (function))
3948 case F_AllRats:
3949 if (AutoRoute (false))
3950 SetChangedFlag (true);
3951 break;
3952 case F_SelectedRats:
3953 case F_Selected:
3954 if (AutoRoute (true))
3955 SetChangedFlag (true);
3956 break;
3959 return 0;
3962 /* --------------------------------------------------------------------------- */
3964 static const char markcrosshair_syntax[] =
3965 "MarkCrosshair()\n"
3966 "MarkCrosshair(Center)";
3968 static const char markcrosshair_help[] = "Set/Reset the Crosshair mark.";
3970 /* %start-doc actions MarkCrosshair
3972 The ``mark'' is a small X-shaped target on the display which is
3973 treated like a second origin (the normal origin is the upper let
3974 corner of the board). The GUI will display a second set of
3975 coordinates for this mark, which tells you how far you are from it.
3977 If no argument is given, the mark is toggled - disabled if it was
3978 enabled, or enabled at the current cursor position of disabled. If
3979 the @code{Center} argument is given, the mark is moved to the current
3980 cursor location.
3982 %end-doc */
3984 static int
3985 ActionMarkCrosshair (int argc, char **argv, Coord x, Coord y)
3987 char *function = ARG (0);
3988 if (!function || !*function)
3990 if (Marked.status)
3992 notify_mark_change (false);
3993 Marked.status = false;
3994 notify_mark_change (true);
3996 else
3998 notify_mark_change (false);
3999 Marked.status = false;
4000 Marked.status = true;
4001 Marked.X = Crosshair.X;
4002 Marked.Y = Crosshair.Y;
4003 notify_mark_change (true);
4006 else if (GetFunctionID (function) == F_Center)
4008 notify_mark_change (false);
4009 Marked.status = true;
4010 Marked.X = Crosshair.X;
4011 Marked.Y = Crosshair.Y;
4012 notify_mark_change (true);
4014 return 0;
4017 /* --------------------------------------------------------------------------- */
4019 static const char changesize_syntax[] =
4020 "ChangeSize(Object, delta)\n"
4021 "ChangeSize(SelectedObjects|Selected, delta)\n"
4022 "ChangeSize(SelectedLines|SelectedPins|SelectedVias, delta)\n"
4023 "ChangeSize(SelectedPads|SelectedTexts|SelectedNames, delta)\n"
4024 "ChangeSize(SelectedElements, delta)";
4026 static const char changesize_help[] = "Changes the size of objects.";
4028 /* %start-doc actions ChangeSize
4030 For lines and arcs, this changes the width. For pins and vias, this
4031 changes the overall diameter of the copper annulus. For pads, this
4032 changes the width and, indirectly, the length. For texts and names,
4033 this changes the scaling factor. For elements, this changes the width
4034 of the silk layer lines and arcs for this element.
4036 %end-doc */
4038 static int
4039 ActionChangeSize (int argc, char **argv, Coord x, Coord y)
4041 char *function = ARG (0);
4042 char *delta = ARG (1);
4043 char *units = ARG (2);
4044 bool absolute; /* indicates if absolute size is given */
4045 Coord value;
4047 if (function && delta)
4049 value = GetValue (delta, units, &absolute);
4050 if (value == 0)
4051 value = delta[0] == '-' ? -Settings.increments->size
4052 : Settings.increments->size;
4053 switch (GetFunctionID (function))
4055 case F_Object:
4057 int type;
4058 void *ptr1, *ptr2, *ptr3;
4060 if ((type =
4061 SearchScreen (Crosshair.X, Crosshair.Y, CHANGESIZE_TYPES,
4062 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4063 if (TEST_FLAG (LOCKFLAG, (PinType *) ptr2))
4064 Message (_("Sorry, the object is locked\n"));
4065 if (ChangeObjectSize (type, ptr1, ptr2, ptr3, value, absolute))
4066 SetChangedFlag (true);
4067 break;
4070 case F_SelectedVias:
4071 if (ChangeSelectedSize (VIA_TYPE, value, absolute))
4072 SetChangedFlag (true);
4073 break;
4075 case F_SelectedPins:
4076 if (ChangeSelectedSize (PIN_TYPE, value, absolute))
4077 SetChangedFlag (true);
4078 break;
4080 case F_SelectedPads:
4081 if (ChangeSelectedSize (PAD_TYPE, value, absolute))
4082 SetChangedFlag (true);
4083 break;
4085 case F_SelectedArcs:
4086 if (ChangeSelectedSize (ARC_TYPE, value, absolute))
4087 SetChangedFlag (true);
4088 break;
4090 case F_SelectedLines:
4091 if (ChangeSelectedSize (LINE_TYPE, value, absolute))
4092 SetChangedFlag (true);
4093 break;
4095 case F_SelectedTexts:
4096 if (ChangeSelectedSize (TEXT_TYPE, value, absolute))
4097 SetChangedFlag (true);
4098 break;
4100 case F_SelectedNames:
4101 if (ChangeSelectedSize (ELEMENTNAME_TYPE, value, absolute))
4102 SetChangedFlag (true);
4103 break;
4105 case F_SelectedElements:
4106 if (ChangeSelectedSize (ELEMENT_TYPE, value, absolute))
4107 SetChangedFlag (true);
4108 break;
4110 case F_Selected:
4111 case F_SelectedObjects:
4112 if (ChangeSelectedSize (CHANGESIZE_TYPES, value, absolute))
4113 SetChangedFlag (true);
4114 break;
4117 return 0;
4120 /* --------------------------------------------------------------------------- */
4122 static const char changedrillsize_syntax[] =
4123 "ChangeDrillSize(Object, delta)\n"
4124 "ChangeDrillSize(SelectedPins|SelectedVias|Selected|SelectedObjects, delta)";
4126 static const char changedrillsize_help[] =
4127 "Changes the drilling hole size of objects.";
4129 /* %start-doc actions ChangeDrillSize
4131 %end-doc */
4133 static int
4134 ActionChange2ndSize (int argc, char **argv, Coord x, Coord y)
4136 char *function = ARG (0);
4137 char *delta = ARG (1);
4138 char *units = ARG (2);
4139 bool absolute;
4140 Coord value;
4142 if (function && delta)
4144 value = GetValue (delta, units, &absolute);
4145 switch (GetFunctionID (function))
4147 case F_Object:
4149 int type;
4150 void *ptr1, *ptr2, *ptr3;
4152 gui->get_coords (_("Select an Object"), &x, &y);
4153 if ((type =
4154 SearchScreen (x, y, CHANGE2NDSIZE_TYPES,
4155 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4156 if (ChangeObject2ndSize
4157 (type, ptr1, ptr2, ptr3, value, absolute, true))
4158 SetChangedFlag (true);
4159 break;
4162 case F_SelectedVias:
4163 if (ChangeSelected2ndSize (VIA_TYPE, value, absolute))
4164 SetChangedFlag (true);
4165 break;
4167 case F_SelectedPins:
4168 if (ChangeSelected2ndSize (PIN_TYPE, value, absolute))
4169 SetChangedFlag (true);
4170 break;
4171 case F_Selected:
4172 case F_SelectedObjects:
4173 if (ChangeSelected2ndSize (PIN_TYPES, value, absolute))
4174 SetChangedFlag (true);
4175 break;
4178 return 0;
4181 /* --------------------------------------------------------------------------- */
4183 static const char changeclearsize_syntax[] =
4184 "ChangeClearSize(Object, delta)\n"
4185 "ChangeClearSize(SelectedPins|SelectedPads|SelectedVias, delta)\n"
4186 "ChangeClearSize(SelectedLines|SelectedArcs, delta\n"
4187 "ChangeClearSize(Selected|SelectedObjects, delta)";
4189 static const char changeclearsize_help[] =
4190 "Changes the clearance size of objects.";
4192 /* %start-doc actions ChangeClearSize
4194 If the solder mask is currently showing, this action changes the
4195 solder mask clearance. If the mask is not showing, this action
4196 changes the polygon clearance.
4198 %end-doc */
4200 static int
4201 ActionChangeClearSize (int argc, char **argv, Coord x, Coord y)
4203 char *function = ARG (0);
4204 char *delta = ARG (1);
4205 char *units = ARG (2);
4206 bool absolute;
4207 Coord value;
4209 if (function && delta)
4211 value = 2 * GetValue (delta, units, &absolute);
4212 if (value == 0)
4213 value = delta[0] == '-' ? -Settings.increments->clear
4214 : Settings.increments->clear;
4215 switch (GetFunctionID (function))
4217 case F_Object:
4219 int type;
4220 void *ptr1, *ptr2, *ptr3;
4222 gui->get_coords (_("Select an Object"), &x, &y);
4223 if ((type =
4224 SearchScreen (x, y,
4225 CHANGECLEARSIZE_TYPES, &ptr1, &ptr2,
4226 &ptr3)) != NO_TYPE)
4227 if (ChangeObjectClearSize (type, ptr1, ptr2, ptr3, value, absolute))
4228 SetChangedFlag (true);
4229 break;
4231 case F_SelectedVias:
4232 if (ChangeSelectedClearSize (VIA_TYPE, value, absolute))
4233 SetChangedFlag (true);
4234 break;
4235 case F_SelectedPads:
4236 if (ChangeSelectedClearSize (PAD_TYPE, value, absolute))
4237 SetChangedFlag (true);
4238 break;
4239 case F_SelectedPins:
4240 if (ChangeSelectedClearSize (PIN_TYPE, value, absolute))
4241 SetChangedFlag (true);
4242 break;
4243 case F_SelectedLines:
4244 if (ChangeSelectedClearSize (LINE_TYPE, value, absolute))
4245 SetChangedFlag (true);
4246 break;
4247 case F_SelectedArcs:
4248 if (ChangeSelectedClearSize (ARC_TYPE, value, absolute))
4249 SetChangedFlag (true);
4250 break;
4251 case F_Selected:
4252 case F_SelectedObjects:
4253 if (ChangeSelectedClearSize (CHANGECLEARSIZE_TYPES, value, absolute))
4254 SetChangedFlag (true);
4255 break;
4258 return 0;
4261 /* --------------------------------------------------------------------------- */
4263 static const char minmaskgap_syntax[] =
4264 "MinMaskGap(delta)\n"
4265 "MinMaskGap(Selected, delta)";
4267 static const char minmaskgap_help[] =
4268 "Ensures the mask is a minimum distance from pins and pads.";
4270 /* %start-doc actions MinMaskGap
4272 Checks all specified pins and/or pads, and increases the mask if
4273 needed to ensure a minimum distance between the pin or pad edge and
4274 the mask edge.
4276 %end-doc */
4278 static int
4279 ActionMinMaskGap (int argc, char **argv, Coord x, Coord y)
4281 char *function = ARG (0);
4282 char *delta = ARG (1);
4283 char *units = ARG (2);
4284 bool absolute;
4285 Coord value;
4286 int flags;
4288 if (!function)
4289 return 1;
4290 if (strcasecmp (function, "Selected") == 0)
4291 flags = SELECTEDFLAG;
4292 else
4294 units = delta;
4295 delta = function;
4296 flags = 0;
4298 value = 2 * GetValue (delta, units, &absolute);
4300 SaveUndoSerialNumber ();
4301 ELEMENT_LOOP (PCB->Data);
4303 PIN_LOOP (element);
4305 if (!TEST_FLAGS (flags, pin))
4306 continue;
4307 if (pin->Mask < pin->Thickness + value)
4309 ChangeObjectMaskSize (PIN_TYPE, element, pin, 0,
4310 pin->Thickness + value, 1);
4311 RestoreUndoSerialNumber ();
4314 END_LOOP;
4315 PAD_LOOP (element);
4317 if (!TEST_FLAGS (flags, pad))
4318 continue;
4319 if (pad->Mask < pad->Thickness + value)
4321 ChangeObjectMaskSize (PAD_TYPE, element, pad, 0,
4322 pad->Thickness + value, 1);
4323 RestoreUndoSerialNumber ();
4326 END_LOOP;
4328 END_LOOP;
4329 VIA_LOOP (PCB->Data);
4331 if (!TEST_FLAGS (flags, via))
4332 continue;
4333 if (via->Mask && via->Mask < via->Thickness + value)
4335 ChangeObjectMaskSize (VIA_TYPE, via, 0, 0, via->Thickness + value, 1);
4336 RestoreUndoSerialNumber ();
4339 END_LOOP;
4340 RestoreUndoSerialNumber ();
4341 IncrementUndoSerialNumber ();
4342 return 0;
4345 /* --------------------------------------------------------------------------- */
4347 static const char mincleargap_syntax[] =
4348 "MinClearGap(delta)\n"
4349 "MinClearGap(Selected, delta)";
4351 static const char mincleargap_help[] =
4352 "Ensures that polygons are a minimum distance from objects.";
4354 /* %start-doc actions MinClearGap
4356 Checks all specified objects, and increases the polygon clearance if
4357 needed to ensure a minimum distance between their edges and the
4358 polygon edges.
4360 %end-doc */
4362 static int
4363 ActionMinClearGap (int argc, char **argv, Coord x, Coord y)
4365 char *function = ARG (0);
4366 char *delta = ARG (1);
4367 char *units = ARG (2);
4368 bool absolute;
4369 Coord value;
4370 int flags;
4372 if (!function)
4373 return 1;
4374 if (strcasecmp (function, "Selected") == 0)
4375 flags = SELECTEDFLAG;
4376 else
4378 units = delta;
4379 delta = function;
4380 flags = 0;
4382 value = 2 * GetValue (delta, units, &absolute);
4384 SaveUndoSerialNumber ();
4385 ELEMENT_LOOP (PCB->Data);
4387 PIN_LOOP (element);
4389 if (!TEST_FLAGS (flags, pin))
4390 continue;
4391 if (pin->Clearance < value)
4393 ChangeObjectClearSize (PIN_TYPE, element, pin, 0,
4394 value, 1);
4395 RestoreUndoSerialNumber ();
4398 END_LOOP;
4399 PAD_LOOP (element);
4401 if (!TEST_FLAGS (flags, pad))
4402 continue;
4403 if (pad->Clearance < value)
4405 ChangeObjectClearSize (PAD_TYPE, element, pad, 0,
4406 value, 1);
4407 RestoreUndoSerialNumber ();
4410 END_LOOP;
4412 END_LOOP;
4413 VIA_LOOP (PCB->Data);
4415 if (!TEST_FLAGS (flags, via))
4416 continue;
4417 if (via->Clearance < value)
4419 ChangeObjectClearSize (VIA_TYPE, via, 0, 0, value, 1);
4420 RestoreUndoSerialNumber ();
4423 END_LOOP;
4424 ALLLINE_LOOP (PCB->Data);
4426 if (!TEST_FLAGS (flags, line))
4427 continue;
4428 if (line->Clearance < value)
4430 ChangeObjectClearSize (LINE_TYPE, layer, line, 0, value, 1);
4431 RestoreUndoSerialNumber ();
4434 ENDALL_LOOP;
4435 ALLARC_LOOP (PCB->Data);
4437 if (!TEST_FLAGS (flags, arc))
4438 continue;
4439 if (arc->Clearance < value)
4441 ChangeObjectClearSize (ARC_TYPE, layer, arc, 0, value, 1);
4442 RestoreUndoSerialNumber ();
4445 ENDALL_LOOP;
4446 RestoreUndoSerialNumber ();
4447 IncrementUndoSerialNumber ();
4448 return 0;
4451 /* --------------------------------------------------------------------------- */
4453 static const char changepinname_syntax[] =
4454 "ChangePinName(ElementName,PinNumber,PinName)";
4456 static const char changepinname_help[] =
4457 "Sets the name of a specific pin on a specific element.";
4459 /* %start-doc actions ChangePinName
4461 This can be especially useful for annotating pin names from a
4462 schematic to the layout without requiring knowledge of the pcb file
4463 format.
4465 @example
4466 ChangePinName(U3, 7, VCC)
4467 @end example
4469 %end-doc */
4471 static int
4472 ActionChangePinName (int argc, char **argv, Coord x, Coord y)
4474 int changed = 0;
4475 char *refdes, *pinnum, *pinname;
4477 if (argc != 3)
4479 AFAIL (changepinname);
4482 refdes = argv[0];
4483 pinnum = argv[1];
4484 pinname = argv[2];
4486 ELEMENT_LOOP (PCB->Data);
4488 if (NSTRCMP (refdes, NAMEONPCB_NAME (element)) == 0)
4490 PIN_LOOP (element);
4492 if (NSTRCMP (pinnum, pin->Number) == 0)
4494 AddObjectToChangeNameUndoList (PIN_TYPE, NULL, NULL,
4495 pin, pin->Name);
4497 * Note: we can't free() pin->Name first because
4498 * it is used in the undo list
4500 pin->Name = strdup (pinname);
4501 SetChangedFlag (true);
4502 changed = 1;
4505 END_LOOP;
4507 PAD_LOOP (element);
4509 if (NSTRCMP (pinnum, pad->Number) == 0)
4511 AddObjectToChangeNameUndoList (PAD_TYPE, NULL, NULL,
4512 pad, pad->Name);
4514 * Note: we can't free() pad->Name first because
4515 * it is used in the undo list
4517 pad->Name = strdup (pinname);
4518 SetChangedFlag (true);
4519 changed = 1;
4522 END_LOOP;
4525 END_LOOP;
4527 * done with our action so increment the undo # if we actually
4528 * changed anything
4530 if (changed)
4532 if (defer_updates)
4533 defer_needs_update = 1;
4534 else
4536 IncrementUndoSerialNumber ();
4537 gui->invalidate_all ();
4541 return 0;
4544 /* --------------------------------------------------------------------------- */
4546 static const char changename_syntax[] =
4547 "ChangeName(Object)\n"
4548 "ChangeName(Layout|Layer)";
4550 static const char changename_help[] = "Sets the name of objects.";
4552 /* %start-doc actions ChangeName
4554 @table @code
4556 @item Object
4557 Changes the name of the element under the cursor.
4559 @item Layout
4560 Changes the name of the layout. This is printed on the fab drawings.
4562 @item Layer
4563 Changes the name of the currently active layer.
4565 @end table
4567 %end-doc */
4570 ActionChangeName (int argc, char **argv, Coord x, Coord y)
4572 char *function = ARG (0);
4573 char *name;
4575 if (function)
4577 switch (GetFunctionID (function))
4579 /* change the name of an object */
4580 case F_Object:
4582 int type;
4583 void *ptr1, *ptr2, *ptr3;
4585 gui->get_coords (_("Select an Object"), &x, &y);
4586 if ((type =
4587 SearchScreen (x, y, CHANGENAME_TYPES,
4588 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4590 SaveUndoSerialNumber ();
4591 if (QueryInputAndChangeObjectName (type, ptr1, ptr2, ptr3))
4593 SetChangedFlag (true);
4594 if (type == ELEMENT_TYPE)
4596 RubberbandType *ptr;
4597 int i;
4599 RestoreUndoSerialNumber ();
4600 Crosshair.AttachedObject.RubberbandN = 0;
4601 LookupRatLines (type, ptr1, ptr2, ptr3);
4602 ptr = Crosshair.AttachedObject.Rubberband;
4603 for (i = 0; i < Crosshair.AttachedObject.RubberbandN;
4604 i++, ptr++)
4606 if (PCB->RatOn)
4607 EraseRat ((RatType *) ptr->Line);
4608 MoveObjectToRemoveUndoList (RATLINE_TYPE,
4609 ptr->Line, ptr->Line,
4610 ptr->Line);
4612 IncrementUndoSerialNumber ();
4613 Draw ();
4617 break;
4620 /* change the layout's name */
4621 case F_Layout:
4622 name =
4623 gui->prompt_for (_("Enter the layout name:"), EMPTY (PCB->Name));
4624 /* NB: ChangeLayoutName takes ownership of the passed memory */
4625 if (name && ChangeLayoutName (name))
4626 SetChangedFlag (true);
4627 break;
4629 /* change the name of the active layer */
4630 case F_Layer:
4631 name = gui->prompt_for (_("Enter the layer name:"),
4632 EMPTY (CURRENT->Name));
4633 /* NB: ChangeLayerName takes ownership of the passed memory */
4634 if (name && ChangeLayerName (CURRENT, name))
4635 SetChangedFlag (true);
4636 break;
4639 return 0;
4643 /* --------------------------------------------------------------------------- */
4645 static const char morphpolygon_syntax[] = "MorphPolygon(Object|Selected)";
4647 static const char morphpolygon_help[] =
4648 "Converts dead polygon islands into separate polygons.";
4650 /* %start-doc actions MorphPolygon
4652 If a polygon is divided into unconnected "islands", you can use
4653 this command to convert the otherwise disappeared islands into
4654 separate polygons. Be sure the cursor is over a portion of the
4655 polygon that remains visible. Very small islands that may flake
4656 off are automatically deleted.
4658 %end-doc */
4660 static int
4661 ActionMorphPolygon (int argc, char **argv, Coord x, Coord y)
4663 char *function = ARG (0);
4664 if (function)
4666 switch (GetFunctionID (function))
4668 case F_Object:
4670 int type;
4671 void *ptr1, *ptr2, *ptr3;
4673 gui->get_coords (_("Select an Object"), &x, &y);
4674 if ((type = SearchScreen (x, y, POLYGON_TYPE,
4675 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4677 MorphPolygon ((LayerType *) ptr1, (PolygonType *) ptr3);
4678 Draw ();
4679 IncrementUndoSerialNumber ();
4681 break;
4683 case F_Selected:
4684 case F_SelectedObjects:
4685 ALLPOLYGON_LOOP (PCB->Data);
4687 if (TEST_FLAG (SELECTEDFLAG, polygon))
4688 MorphPolygon (layer, polygon);
4690 ENDALL_LOOP;
4691 Draw ();
4692 IncrementUndoSerialNumber ();
4693 break;
4696 return 0;
4699 /* --------------------------------------------------------------------------- */
4701 static const char togglehidename_syntax[] =
4702 "ToggleHideName(Object|SelectedElements)";
4704 static const char togglehidename_help[] =
4705 "Toggles the visibility of element names.";
4707 /* %start-doc actions ToggleHideName
4709 If names are hidden you won't see them on the screen and they will not
4710 appear on the silk layer when you print the layout.
4712 %end-doc */
4714 static int
4715 ActionToggleHideName (int argc, char **argv, Coord x, Coord y)
4717 char *function = ARG (0);
4718 if (function && PCB->ElementOn)
4720 switch (GetFunctionID (function))
4722 case F_Object:
4724 int type;
4725 void *ptr1, *ptr2, *ptr3;
4727 gui->get_coords (_("Select an Object"), &x, &y);
4728 if ((type = SearchScreen (x, y, ELEMENT_TYPE,
4729 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4731 AddObjectToFlagUndoList (type, ptr1, ptr2, ptr3);
4732 EraseElementName ((ElementType *) ptr2);
4733 TOGGLE_FLAG (HIDENAMEFLAG, (ElementType *) ptr2);
4734 DrawElementName ((ElementType *) ptr2);
4735 Draw ();
4736 IncrementUndoSerialNumber ();
4738 break;
4740 case F_SelectedElements:
4741 case F_Selected:
4743 bool changed = false;
4744 ELEMENT_LOOP (PCB->Data);
4746 if ((TEST_FLAG (SELECTEDFLAG, element) ||
4747 TEST_FLAG (SELECTEDFLAG,
4748 &NAMEONPCB_TEXT (element)))
4749 && (FRONT (element) || PCB->InvisibleObjectsOn))
4751 AddObjectToFlagUndoList (ELEMENT_TYPE, element,
4752 element, element);
4753 EraseElementName (element);
4754 TOGGLE_FLAG (HIDENAMEFLAG, element);
4755 DrawElementName (element);
4756 changed = true;
4759 END_LOOP;
4760 if (changed)
4762 Draw ();
4763 IncrementUndoSerialNumber ();
4768 return 0;
4771 /* --------------------------------------------------------------------------- */
4773 static const char changejoin_syntax[] =
4774 "ChangeJoin(ToggleObject|SelectedLines|SelectedArcs|Selected)";
4776 static const char changejoin_help[] =
4777 "Changes the join (clearance through polygons) of objects.";
4779 /* %start-doc actions ChangeJoin
4781 The join flag determines whether a line or arc, drawn to intersect a
4782 polygon, electrically connects to the polygon or not. When joined,
4783 the line/arc is simply drawn over the polygon, making an electrical
4784 connection. When not joined, a gap is drawn between the line and the
4785 polygon, insulating them from each other.
4787 %end-doc */
4789 static int
4790 ActionChangeJoin (int argc, char **argv, Coord x, Coord y)
4792 char *function = ARG (0);
4793 if (function)
4795 switch (GetFunctionID (function))
4797 case F_ToggleObject:
4798 case F_Object:
4800 int type;
4801 void *ptr1, *ptr2, *ptr3;
4803 gui->get_coords (_("Select an Object"), &x, &y);
4804 if ((type =
4805 SearchScreen (x, y, CHANGEJOIN_TYPES,
4806 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4807 if (ChangeObjectJoin (type, ptr1, ptr2, ptr3))
4808 SetChangedFlag (true);
4809 break;
4812 case F_SelectedLines:
4813 if (ChangeSelectedJoin (LINE_TYPE))
4814 SetChangedFlag (true);
4815 break;
4817 case F_SelectedArcs:
4818 if (ChangeSelectedJoin (ARC_TYPE))
4819 SetChangedFlag (true);
4820 break;
4822 case F_Selected:
4823 case F_SelectedObjects:
4824 if (ChangeSelectedJoin (CHANGEJOIN_TYPES))
4825 SetChangedFlag (true);
4826 break;
4829 return 0;
4832 /* --------------------------------------------------------------------------- */
4834 static const char changesquare_syntax[] =
4835 "ChangeSquare(ToggleObject)\n"
4836 "ChangeSquare(SelectedElements|SelectedPins)\n"
4837 "ChangeSquare(Selected|SelectedObjects)";
4839 static const char changesquare_help[] =
4840 "Changes the square flag of pins and pads.";
4842 /* %start-doc actions ChangeSquare
4844 Note that @code{Pins} means both pins and pads.
4846 @pinshapes
4848 %end-doc */
4850 static int
4851 ActionChangeSquare (int argc, char **argv, Coord x, Coord y)
4853 char *function = ARG (0);
4854 if (function)
4856 switch (GetFunctionID (function))
4858 case F_ToggleObject:
4859 case F_Object:
4861 int type;
4862 void *ptr1, *ptr2, *ptr3;
4864 gui->get_coords (_("Select an Object"), &x, &y);
4865 if ((type =
4866 SearchScreen (x, y, CHANGESQUARE_TYPES,
4867 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4868 if (ChangeObjectSquare (type, ptr1, ptr2, ptr3))
4869 SetChangedFlag (true);
4870 break;
4873 case F_SelectedElements:
4874 if (ChangeSelectedSquare (ELEMENT_TYPE))
4875 SetChangedFlag (true);
4876 break;
4878 case F_SelectedPins:
4879 if (ChangeSelectedSquare (PIN_TYPE | PAD_TYPE))
4880 SetChangedFlag (true);
4881 break;
4883 case F_Selected:
4884 case F_SelectedObjects:
4885 if (ChangeSelectedSquare (PIN_TYPE | PAD_TYPE))
4886 SetChangedFlag (true);
4887 break;
4890 return 0;
4893 /* --------------------------------------------------------------------------- */
4895 static const char setsquare_syntax[] =
4896 "SetSquare(ToggleObject|SelectedElements|SelectedPins)";
4898 static const char setsquare_help[] = "sets the square-flag of objects.";
4900 /* %start-doc actions SetSquare
4902 Note that @code{Pins} means pins and pads.
4904 @pinshapes
4906 %end-doc */
4908 static int
4909 ActionSetSquare (int argc, char **argv, Coord x, Coord y)
4911 char *function = ARG (0);
4912 if (function && *function)
4914 switch (GetFunctionID (function))
4916 case F_ToggleObject:
4917 case F_Object:
4919 int type;
4920 void *ptr1, *ptr2, *ptr3;
4922 gui->get_coords (_("Select an Object"), &x, &y);
4923 if ((type =
4924 SearchScreen (x, y, CHANGESQUARE_TYPES,
4925 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4926 if (SetObjectSquare (type, ptr1, ptr2, ptr3))
4927 SetChangedFlag (true);
4928 break;
4931 case F_SelectedElements:
4932 if (SetSelectedSquare (ELEMENT_TYPE))
4933 SetChangedFlag (true);
4934 break;
4936 case F_SelectedPins:
4937 if (SetSelectedSquare (PIN_TYPE | PAD_TYPE))
4938 SetChangedFlag (true);
4939 break;
4941 case F_Selected:
4942 case F_SelectedObjects:
4943 if (SetSelectedSquare (PIN_TYPE | PAD_TYPE))
4944 SetChangedFlag (true);
4945 break;
4948 return 0;
4951 /* --------------------------------------------------------------------------- */
4953 static const char clearsquare_syntax[] =
4954 "ClearSquare(ToggleObject|SelectedElements|SelectedPins)";
4956 static const char clearsquare_help[] =
4957 "Clears the square-flag of pins and pads.";
4959 /* %start-doc actions ClearSquare
4961 Note that @code{Pins} means pins and pads.
4963 @pinshapes
4965 %end-doc */
4967 static int
4968 ActionClearSquare (int argc, char **argv, Coord x, Coord y)
4970 char *function = ARG (0);
4971 if (function && *function)
4973 switch (GetFunctionID (function))
4975 case F_ToggleObject:
4976 case F_Object:
4978 int type;
4979 void *ptr1, *ptr2, *ptr3;
4981 gui->get_coords (_("Select an Object"), &x, &y);
4982 if ((type =
4983 SearchScreen (x, y, CHANGESQUARE_TYPES,
4984 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4985 if (ClrObjectSquare (type, ptr1, ptr2, ptr3))
4986 SetChangedFlag (true);
4987 break;
4990 case F_SelectedElements:
4991 if (ClrSelectedSquare (ELEMENT_TYPE))
4992 SetChangedFlag (true);
4993 break;
4995 case F_SelectedPins:
4996 if (ClrSelectedSquare (PIN_TYPE | PAD_TYPE))
4997 SetChangedFlag (true);
4998 break;
5000 case F_Selected:
5001 case F_SelectedObjects:
5002 if (ClrSelectedSquare (PIN_TYPE | PAD_TYPE))
5003 SetChangedFlag (true);
5004 break;
5007 return 0;
5010 /* --------------------------------------------------------------------------- */
5012 static const char changeoctagon_syntax[] =
5013 "ChangeOctagon(Object|ToggleObject|SelectedObjects|Selected)\n"
5014 "ChangeOctagon(SelectedElements|SelectedPins|SelectedVias)";
5016 static const char changeoctagon_help[] =
5017 "Changes the octagon-flag of pins and vias.";
5019 /* %start-doc actions ChangeOctagon
5021 @pinshapes
5023 %end-doc */
5025 static int
5026 ActionChangeOctagon (int argc, char **argv, Coord x, Coord y)
5028 char *function = ARG (0);
5029 if (function)
5031 switch (GetFunctionID (function))
5033 case F_ToggleObject:
5034 case F_Object:
5036 int type;
5037 void *ptr1, *ptr2, *ptr3;
5039 gui->get_coords (_("Select an Object"), &x, &y);
5040 if ((type =
5041 SearchScreen (x, y, CHANGEOCTAGON_TYPES,
5042 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5043 if (ChangeObjectOctagon (type, ptr1, ptr2, ptr3))
5044 SetChangedFlag (true);
5045 break;
5048 case F_SelectedElements:
5049 if (ChangeSelectedOctagon (ELEMENT_TYPE))
5050 SetChangedFlag (true);
5051 break;
5053 case F_SelectedPins:
5054 if (ChangeSelectedOctagon (PIN_TYPE))
5055 SetChangedFlag (true);
5056 break;
5058 case F_SelectedVias:
5059 if (ChangeSelectedOctagon (VIA_TYPE))
5060 SetChangedFlag (true);
5061 break;
5063 case F_Selected:
5064 case F_SelectedObjects:
5065 if (ChangeSelectedOctagon (PIN_TYPES))
5066 SetChangedFlag (true);
5067 break;
5070 return 0;
5073 /* --------------------------------------------------------------------------- */
5075 static const char setoctagon_syntax[] =
5076 "SetOctagon(Object|ToggleObject|SelectedElements|Selected)";
5078 static const char setoctagon_help[] = "Sets the octagon-flag of objects.";
5080 /* %start-doc actions SetOctagon
5082 @pinshapes
5084 %end-doc */
5086 static int
5087 ActionSetOctagon (int argc, char **argv, Coord x, Coord y)
5089 char *function = ARG (0);
5090 if (function)
5092 switch (GetFunctionID (function))
5094 case F_ToggleObject:
5095 case F_Object:
5097 int type;
5098 void *ptr1, *ptr2, *ptr3;
5100 gui->get_coords (_("Select an Object"), &x, &y);
5101 if ((type =
5102 SearchScreen (x, y, CHANGEOCTAGON_TYPES,
5103 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5104 if (SetObjectOctagon (type, ptr1, ptr2, ptr3))
5105 SetChangedFlag (true);
5106 break;
5109 case F_SelectedElements:
5110 if (SetSelectedOctagon (ELEMENT_TYPE))
5111 SetChangedFlag (true);
5112 break;
5114 case F_SelectedPins:
5115 if (SetSelectedOctagon (PIN_TYPE))
5116 SetChangedFlag (true);
5117 break;
5119 case F_SelectedVias:
5120 if (SetSelectedOctagon (VIA_TYPE))
5121 SetChangedFlag (true);
5122 break;
5124 case F_Selected:
5125 case F_SelectedObjects:
5126 if (SetSelectedOctagon (PIN_TYPES))
5127 SetChangedFlag (true);
5128 break;
5131 return 0;
5134 /* --------------------------------------------------------------------------- */
5136 static const char clearoctagon_syntax[] =
5137 "ClearOctagon(ToggleObject|Object|SelectedObjects|Selected)\n"
5138 "ClearOctagon(SelectedElements|SelectedPins|SelectedVias)";
5140 static const char clearoctagon_help[] =
5141 "Clears the octagon-flag of pins and vias.";
5143 /* %start-doc actions ClearOctagon
5145 @pinshapes
5147 %end-doc */
5149 static int
5150 ActionClearOctagon (int argc, char **argv, Coord x, Coord y)
5152 char *function = ARG (0);
5153 if (function)
5155 switch (GetFunctionID (function))
5157 case F_ToggleObject:
5158 case F_Object:
5160 int type;
5161 void *ptr1, *ptr2, *ptr3;
5163 gui->get_coords (_("Select an Object"), &x, &y);
5164 if ((type =
5165 SearchScreen (Crosshair.X, Crosshair.Y, CHANGEOCTAGON_TYPES,
5166 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5167 if (ClrObjectOctagon (type, ptr1, ptr2, ptr3))
5168 SetChangedFlag (true);
5169 break;
5172 case F_SelectedElements:
5173 if (ClrSelectedOctagon (ELEMENT_TYPE))
5174 SetChangedFlag (true);
5175 break;
5177 case F_SelectedPins:
5178 if (ClrSelectedOctagon (PIN_TYPE))
5179 SetChangedFlag (true);
5180 break;
5182 case F_SelectedVias:
5183 if (ClrSelectedOctagon (VIA_TYPE))
5184 SetChangedFlag (true);
5185 break;
5187 case F_Selected:
5188 case F_SelectedObjects:
5189 if (ClrSelectedOctagon (PIN_TYPES))
5190 SetChangedFlag (true);
5191 break;
5194 return 0;
5197 /* --------------------------------------------------------------------------- */
5199 static const char changehold_syntax[] =
5200 "ChangeHole(ToggleObject|Object|SelectedVias|Selected)";
5202 static const char changehold_help[] = "Changes the hole flag of objects.";
5204 /* %start-doc actions ChangeHole
5206 The "hole flag" of a via determines whether the via is a
5207 plated-through hole (not set), or an unplated hole (set).
5209 %end-doc */
5211 static int
5212 ActionChangeHole (int argc, char **argv, Coord x, Coord y)
5214 char *function = ARG (0);
5215 if (function)
5217 switch (GetFunctionID (function))
5219 case F_ToggleObject:
5220 case F_Object:
5222 int type;
5223 void *ptr1, *ptr2, *ptr3;
5225 gui->get_coords (_("Select an Object"), &x, &y);
5226 if ((type = SearchScreen (x, y, VIA_TYPE,
5227 &ptr1, &ptr2, &ptr3)) != NO_TYPE
5228 && ChangeHole ((PinType *) ptr3))
5229 IncrementUndoSerialNumber ();
5230 break;
5233 case F_SelectedVias:
5234 case F_Selected:
5235 if (ChangeSelectedHole ())
5236 SetChangedFlag (true);
5237 break;
5240 return 0;
5243 /* --------------------------------------------------------------------------- */
5245 static const char changepaste_syntax[] =
5246 "ChangePaste(ToggleObject|Object|SelectedPads|Selected)";
5248 static const char changepaste_help[] = "Changes the no paste flag of objects.";
5250 /* %start-doc actions ChangePaste
5252 The "no paste flag" of a pad determines whether the solderpaste
5253 stencil will have an opening for the pad (no set) or if there wil be
5254 no solderpaste on the pad (set). This is used for things such as
5255 fiducial pads.
5257 %end-doc */
5259 static int
5260 ActionChangePaste (int argc, char **argv, Coord x, Coord y)
5262 char *function = ARG (0);
5263 if (function)
5265 switch (GetFunctionID (function))
5267 case F_ToggleObject:
5268 case F_Object:
5270 int type;
5271 void *ptr1, *ptr2, *ptr3;
5273 gui->get_coords (_("Select an Object"), &x, &y);
5274 if ((type = SearchScreen (x, y, PAD_TYPE,
5275 &ptr1, &ptr2, &ptr3)) != NO_TYPE
5276 && ChangePaste ((PadType *) ptr3))
5277 IncrementUndoSerialNumber ();
5278 break;
5281 case F_SelectedPads:
5282 case F_Selected:
5283 if (ChangeSelectedPaste ())
5284 SetChangedFlag (true);
5285 break;
5288 return 0;
5291 /* --------------------------------------------------------------------------- */
5293 static const char select_syntax[] =
5294 "Select(Object|ToggleObject)\n"
5295 "Select(All|Block|Connection)\n"
5296 "Select(ElementByName|ObjectByName|PadByName|PinByName)\n"
5297 "Select(ElementByName|ObjectByName|PadByName|PinByName, Name)\n"
5298 "Select(TextByName|ViaByName|NetByName)\n"
5299 "Select(TextByName|ViaByName|NetByName, Name)\n"
5300 "Select(Convert)";
5302 static const char select_help[] = "Toggles or sets the selection.";
5304 /* %start-doc actions Select
5306 @table @code
5308 @item ElementByName
5309 @item ObjectByName
5310 @item PadByName
5311 @item PinByName
5312 @item TextByName
5313 @item ViaByName
5314 @item NetByName
5316 These all rely on having a regular expression parser built into
5317 @code{pcb}. If the name is not specified then the user is prompted
5318 for a pattern, and all objects that match the pattern and are of the
5319 type specified are selected.
5321 @item Object
5322 @item ToggleObject
5323 Selects the object under the cursor.
5325 @item Block
5326 Selects all objects in a rectangle indicated by the cursor.
5328 @item All
5329 Selects all objects on the board.
5331 @item Found
5332 Selects all connections with the ``found'' flag set.
5334 @item Connection
5335 Selects all connections with the ``connected'' flag set.
5337 @item Convert
5338 Converts the selected objects to an element. This uses the highest
5339 numbered paste buffer.
5341 @end table
5343 %end-doc */
5345 static int
5346 ActionSelect (int argc, char **argv, Coord x, Coord y)
5348 char *function = ARG (0);
5349 if (function)
5351 switch (GetFunctionID (function))
5353 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5354 int type;
5355 /* select objects by their names */
5356 case F_ElementByName:
5357 type = ELEMENT_TYPE;
5358 goto commonByName;
5359 case F_ObjectByName:
5360 type = ALL_TYPES;
5361 goto commonByName;
5362 case F_PadByName:
5363 type = PAD_TYPE;
5364 goto commonByName;
5365 case F_PinByName:
5366 type = PIN_TYPE;
5367 goto commonByName;
5368 case F_TextByName:
5369 type = TEXT_TYPE;
5370 goto commonByName;
5371 case F_ViaByName:
5372 type = VIA_TYPE;
5373 goto commonByName;
5374 case F_NetByName:
5375 type = NET_TYPE;
5376 goto commonByName;
5378 commonByName:
5380 char *pattern = ARG (1);
5382 if (pattern
5383 || (pattern =
5384 gui->prompt_for (_("Enter pattern:"), "")) != NULL)
5386 if (SelectObjectByName (type, pattern, true))
5387 SetChangedFlag (true);
5388 if (ARG (1) == NULL)
5389 free (pattern);
5391 break;
5393 #endif /* defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP) */
5395 /* select a single object */
5396 case F_ToggleObject:
5397 case F_Object:
5398 if (SelectObject ())
5399 SetChangedFlag (true);
5400 break;
5402 /* all objects in block */
5403 case F_Block:
5405 BoxType box;
5407 box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
5408 Crosshair.AttachedBox.Point2.X);
5409 box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
5410 Crosshair.AttachedBox.Point2.Y);
5411 box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
5412 Crosshair.AttachedBox.Point2.X);
5413 box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
5414 Crosshair.AttachedBox.Point2.Y);
5415 notify_crosshair_change (false);
5416 NotifyBlock ();
5417 if (Crosshair.AttachedBox.State == STATE_THIRD &&
5418 SelectBlock (&box, true))
5420 SetChangedFlag (true);
5421 Crosshair.AttachedBox.State = STATE_FIRST;
5423 notify_crosshair_change (true);
5424 break;
5427 /* select all visible objects */
5428 case F_All:
5430 BoxType box;
5432 box.X1 = -MAX_COORD;
5433 box.Y1 = -MAX_COORD;
5434 box.X2 = MAX_COORD;
5435 box.Y2 = MAX_COORD;
5436 if (SelectBlock (&box, true))
5437 SetChangedFlag (true);
5438 break;
5441 /* all logical connections */
5442 case F_Found:
5443 if (SelectByFlag (FOUNDFLAG, true))
5445 Draw ();
5446 IncrementUndoSerialNumber ();
5447 SetChangedFlag (true);
5449 break;
5451 /* all physical connections */
5452 case F_Connection:
5453 if (SelectByFlag (CONNECTEDFLAG, true))
5455 Draw ();
5456 IncrementUndoSerialNumber ();
5457 SetChangedFlag (true);
5459 break;
5461 case F_Convert:
5463 Coord x, y;
5464 Note.Buffer = Settings.BufferNumber;
5465 SetBufferNumber (MAX_BUFFER - 1);
5466 ClearBuffer (PASTEBUFFER);
5467 gui->get_coords (_("Select the Element's Mark Location"), &x, &y);
5468 x = GridFit (x, PCB->Grid, PCB->GridOffsetX);
5469 y = GridFit (y, PCB->Grid, PCB->GridOffsetY);
5470 AddSelectedToBuffer (PASTEBUFFER, x, y, true);
5471 SaveUndoSerialNumber ();
5472 RemoveSelected ();
5473 ConvertBufferToElement (PASTEBUFFER);
5474 RestoreUndoSerialNumber ();
5475 CopyPastebufferToLayout (x, y);
5476 SetBufferNumber (Note.Buffer);
5478 break;
5480 default:
5481 AFAIL (select);
5482 break;
5485 return 0;
5488 /* FLAG(have_regex,FlagHaveRegex,0) */
5490 FlagHaveRegex (int parm)
5492 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5493 return 1;
5494 #else
5495 return 0;
5496 #endif
5499 /* --------------------------------------------------------------------------- */
5501 static const char unselect_syntax[] =
5502 "Unselect(All|Block|Connection)\n"
5503 "Unselect(ElementByName|ObjectByName|PadByName|PinByName)\n"
5504 "Unselect(ElementByName|ObjectByName|PadByName|PinByName, Name)\n"
5505 "Unselect(TextByName|ViaByName)\n"
5506 "Unselect(TextByName|ViaByName, Name)\n";
5508 static const char unselect_help[] =
5509 "Unselects the object at the pointer location or the specified objects.";
5511 /* %start-doc actions Unselect
5513 @table @code
5515 @item All
5516 Unselect all objects.
5518 @item Block
5519 Unselect all objects in a rectangle given by the cursor.
5521 @item Connection
5522 Unselect all connections with the ``found'' flag set.
5524 @item ElementByName
5525 @item ObjectByName
5526 @item PadByName
5527 @item PinByName
5528 @item TextByName
5529 @item ViaByName
5531 These all rely on having a regular expression parser built into
5532 @code{pcb}. If the name is not specified then the user is prompted
5533 for a pattern, and all objects that match the pattern and are of the
5534 type specified are unselected.
5537 @end table
5539 %end-doc */
5541 static int
5542 ActionUnselect (int argc, char **argv, Coord x, Coord y)
5544 char *function = ARG (0);
5545 if (function)
5547 switch (GetFunctionID (function))
5549 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5550 int type;
5551 /* select objects by their names */
5552 case F_ElementByName:
5553 type = ELEMENT_TYPE;
5554 goto commonByName;
5555 case F_ObjectByName:
5556 type = ALL_TYPES;
5557 goto commonByName;
5558 case F_PadByName:
5559 type = PAD_TYPE;
5560 goto commonByName;
5561 case F_PinByName:
5562 type = PIN_TYPE;
5563 goto commonByName;
5564 case F_TextByName:
5565 type = TEXT_TYPE;
5566 goto commonByName;
5567 case F_ViaByName:
5568 type = VIA_TYPE;
5569 goto commonByName;
5570 case F_NetByName:
5571 type = NET_TYPE;
5572 goto commonByName;
5574 commonByName:
5576 char *pattern = ARG (1);
5578 if (pattern
5579 || (pattern =
5580 gui->prompt_for (_("Enter pattern:"), "")) != NULL)
5582 if (SelectObjectByName (type, pattern, false))
5583 SetChangedFlag (true);
5584 if (ARG (1) == NULL)
5585 free (pattern);
5587 break;
5589 #endif /* defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP) */
5591 /* all objects in block */
5592 case F_Block:
5594 BoxType box;
5596 box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
5597 Crosshair.AttachedBox.Point2.X);
5598 box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
5599 Crosshair.AttachedBox.Point2.Y);
5600 box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
5601 Crosshair.AttachedBox.Point2.X);
5602 box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
5603 Crosshair.AttachedBox.Point2.Y);
5604 notify_crosshair_change (false);
5605 NotifyBlock ();
5606 if (Crosshair.AttachedBox.State == STATE_THIRD &&
5607 SelectBlock (&box, false))
5609 SetChangedFlag (true);
5610 Crosshair.AttachedBox.State = STATE_FIRST;
5612 notify_crosshair_change (true);
5613 break;
5616 /* unselect all visible objects */
5617 case F_All:
5619 BoxType box;
5621 box.X1 = -MAX_COORD;
5622 box.Y1 = -MAX_COORD;
5623 box.X2 = MAX_COORD;
5624 box.Y2 = MAX_COORD;
5625 if (SelectBlock (&box, false))
5626 SetChangedFlag (true);
5627 break;
5630 /* all logical connections */
5631 case F_Found:
5632 if (SelectByFlag (FOUNDFLAG, false))
5634 Draw ();
5635 IncrementUndoSerialNumber ();
5636 SetChangedFlag (true);
5638 break;
5640 /* all physical connections */
5641 case F_Connection:
5642 if (SelectByFlag (CONNECTEDFLAG, false))
5644 Draw ();
5645 IncrementUndoSerialNumber ();
5646 SetChangedFlag (true);
5648 break;
5650 default:
5651 AFAIL (unselect);
5652 break;
5656 return 0;
5659 /* --------------------------------------------------------------------------- */
5661 static const char saveto_syntax[] =
5662 "SaveTo(Layout|LayoutAs,filename)\n"
5663 "SaveTo(AllConnections|AllUnusedPins|ElementConnections,filename)\n"
5664 "SaveTo(PasteBuffer,filename)";
5666 static const char saveto_help[] = "Saves data to a file.";
5668 /* %start-doc actions SaveTo
5670 @table @code
5672 @item Layout
5673 Saves the current layout.
5675 @item LayoutAs
5676 Saves the current layout, and remembers the filename used.
5678 @item AllConnections
5679 Save all connections to a file.
5681 @item AllUnusedPins
5682 List all unused pins to a file.
5684 @item ElementConnections
5685 Save connections to the element at the cursor to a file.
5687 @item PasteBuffer
5688 Save the content of the active Buffer to a file. This is the graphical way to create a footprint.
5690 @end table
5692 %end-doc */
5694 static int
5695 ActionSaveTo (int argc, char **argv, Coord x, Coord y)
5697 char *function;
5698 char *name;
5700 function = argv[0];
5701 name = argv[1];
5703 if (strcasecmp (function, "Layout") == 0)
5705 if (SavePCB (PCB->Filename) == 0)
5706 SetChangedFlag (false);
5707 return 0;
5710 if (argc != 2)
5711 AFAIL (saveto);
5713 if (strcasecmp (function, "LayoutAs") == 0)
5715 if (SavePCB (name) == 0)
5717 SetChangedFlag (false);
5718 free (PCB->Filename);
5719 PCB->Filename = strdup (name);
5720 if (gui->notify_filename_changed != NULL)
5721 gui->notify_filename_changed ();
5723 return 0;
5726 if (strcasecmp (function, "AllConnections") == 0)
5728 FILE *fp;
5729 bool result;
5730 if ((fp = CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
5732 LookupConnectionsToAllElements (fp);
5733 fclose (fp);
5734 SetChangedFlag (true);
5736 return 0;
5739 if (strcasecmp (function, "AllUnusedPins") == 0)
5741 FILE *fp;
5742 bool result;
5743 if ((fp = CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
5745 LookupUnusedPins (fp);
5746 fclose (fp);
5747 SetChangedFlag (true);
5749 return 0;
5752 if (strcasecmp (function, "ElementConnections") == 0)
5754 ElementType *element;
5755 void *ptrtmp;
5756 FILE *fp;
5757 bool result;
5759 if ((SearchScreen (Crosshair.X, Crosshair.Y, ELEMENT_TYPE,
5760 &ptrtmp, &ptrtmp, &ptrtmp)) != NO_TYPE)
5762 element = (ElementType *) ptrtmp;
5763 if ((fp =
5764 CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
5766 LookupElementConnections (element, fp);
5767 fclose (fp);
5768 SetChangedFlag (true);
5771 return 0;
5774 if (strcasecmp (function, "PasteBuffer") == 0)
5776 return SaveBufferElements (name);
5779 AFAIL (saveto);
5782 /* --------------------------------------------------------------------------- */
5784 static const char savesettings_syntax[] =
5785 "SaveSettings()\n"
5786 "SaveSettings(local)";
5788 static const char savesettings_help[] = "Saves settings.";
5790 /* %start-doc actions SaveSettings
5792 If you pass no arguments, the settings are stored in
5793 @code{$HOME/.pcb/settings}. If you pass the word @code{local} they're
5794 saved in @code{./pcb.settings}.
5796 %end-doc */
5798 static int
5799 ActionSaveSettings (int argc, char **argv, Coord x, Coord y)
5801 int locally = argc > 0 ? (strncasecmp (argv[0], "local", 5) == 0) : 0;
5802 hid_save_settings (locally);
5803 return 0;
5806 /* --------------------------------------------------------------------------- */
5808 static const char loadfrom_syntax[] =
5809 "LoadFrom(Layout|LayoutToBuffer|ElementToBuffer|Netlist|Revert,filename)";
5811 static const char loadfrom_help[] = "Load layout data from a file.";
5813 /* %start-doc actions LoadFrom
5815 This action assumes you know what the filename is. The various GUIs
5816 should have a similar @code{Load} action where the filename is
5817 optional, and will provide their own file selection mechanism to let
5818 you choose the file name.
5820 @table @code
5822 @item Layout
5823 Loads an entire PCB layout, replacing the current one.
5825 @item LayoutToBuffer
5826 Loads an entire PCB layout to the paste buffer.
5828 @item ElementToBuffer
5829 Loads the given element file into the paste buffer. Element files
5830 contain only a single @code{Element} definition, such as the
5831 ``newlib'' library uses.
5833 @item Netlist
5834 Loads a new netlist, replacing any current netlist.
5836 @item Revert
5837 Re-loads the current layout from its disk file, reverting any changes
5838 you may have made.
5840 @end table
5842 %end-doc */
5844 static int
5845 ActionLoadFrom (int argc, char **argv, Coord x, Coord y)
5847 char *function;
5848 char *name;
5850 if (argc < 2)
5851 AFAIL (loadfrom);
5853 function = argv[0];
5854 name = argv[1];
5856 if (strcasecmp (function, "ElementToBuffer") == 0)
5858 notify_crosshair_change (false);
5859 if (LoadElementToBuffer (PASTEBUFFER, name, true))
5860 SetMode (PASTEBUFFER_MODE);
5861 notify_crosshair_change (true);
5864 else if (strcasecmp (function, "LayoutToBuffer") == 0)
5866 notify_crosshair_change (false);
5867 if (LoadLayoutToBuffer (PASTEBUFFER, name))
5868 SetMode (PASTEBUFFER_MODE);
5869 notify_crosshair_change (true);
5872 else if (strcasecmp (function, "Layout") == 0)
5874 if (!PCB->Changed ||
5875 gui->confirm_dialog (_("OK to override layout data?"), 0))
5876 LoadPCB (name);
5879 else if (strcasecmp (function, "Netlist") == 0)
5881 if (PCB->Netlistname)
5882 free (PCB->Netlistname);
5883 PCB->Netlistname = StripWhiteSpaceAndDup (name);
5884 FreeLibraryMemory (&PCB->NetlistLib);
5885 ImportNetlist (PCB->Netlistname);
5886 NetlistChanged (1);
5888 else if (strcasecmp (function, "Revert") == 0 && PCB->Filename
5889 && (!PCB->Changed
5890 || gui->confirm_dialog (_("OK to override changes?"), 0)))
5892 RevertPCB ();
5895 return 0;
5898 /* --------------------------------------------------------------------------- */
5900 static const char new_syntax[] = "New([name])";
5902 static const char new_help[] = "Starts a new layout.";
5904 /* %start-doc actions New
5906 If a name is not given, one is prompted for.
5908 %end-doc */
5910 static int
5911 ActionNew (int argc, char **argv, Coord x, Coord y)
5913 char *name = ARG (0);
5915 if (!PCB->Changed || gui->confirm_dialog (_("OK to clear layout data?"), 0))
5917 if (name)
5918 name = strdup (name);
5919 else
5920 name = gui->prompt_for (_("Enter the layout name:"), "");
5922 if (!name)
5923 return 1;
5925 notify_crosshair_change (false);
5926 /* do emergency saving
5927 * clear the old struct and allocate memory for the new one
5929 if (PCB->Changed && Settings.SaveInTMP)
5930 SaveInTMP ();
5931 RemovePCB (PCB);
5932 PCB = NULL;
5933 PCB = CreateNewPCB (true);
5934 PCB->Data->LayerN = DEF_LAYER;
5935 CreateNewPCBPost (PCB, 1);
5937 /* setup the new name and reset some values to default */
5938 free (PCB->Name);
5939 PCB->Name = name;
5941 ResetStackAndVisibility ();
5942 SetCrosshairRange (0, 0, PCB->MaxWidth, PCB->MaxHeight);
5943 CenterDisplay (PCB->MaxWidth / 2, PCB->MaxHeight / 2);
5944 Redraw ();
5946 hid_action ("PCBChanged");
5947 notify_crosshair_change (true);
5948 return 0;
5950 return 1;
5953 /* ---------------------------------------------------------------------------
5954 * no operation, just for testing purposes
5955 * syntax: Bell(volume)
5957 void
5958 ActionBell (char *volume)
5960 gui->beep ();
5963 /* --------------------------------------------------------------------------- */
5965 static const char pastebuffer_syntax[] =
5966 "PasteBuffer(AddSelected|Clear|1..MAX_BUFFER)\n"
5967 "PasteBuffer(Rotate, 1..3)\n"
5968 "PasteBuffer(Convert|Save|Restore|Mirror)\n"
5969 "PasteBuffer(ToLayout, X, Y, units)";
5971 static const char pastebuffer_help[] =
5972 "Various operations on the paste buffer.";
5974 /* %start-doc actions PasteBuffer
5976 There are a number of paste buffers; the actual limit is a
5977 compile-time constant @code{MAX_BUFFER} in @file{globalconst.h}. It
5978 is currently @code{5}. One of these is the ``current'' paste buffer,
5979 often referred to as ``the'' paste buffer.
5981 @table @code
5983 @item AddSelected
5984 Copies the selected objects to the current paste buffer.
5986 @item Clear
5987 Remove all objects from the current paste buffer.
5989 @item Convert
5990 Convert the current paste buffer to an element. Vias are converted to
5991 pins, lines are converted to pads.
5993 @item Restore
5994 Convert any elements in the paste buffer back to vias and lines.
5996 @item Mirror
5997 Flip all objects in the paste buffer vertically (up/down flip). To mirror
5998 horizontally, combine this with rotations.
6000 @item Rotate
6001 Rotates the current buffer. The number to pass is 1..3, where 1 means
6002 90 degrees counter clockwise, 2 means 180 degrees, and 3 means 90
6003 degrees clockwise (270 CCW).
6005 @item Save
6006 Saves any elements in the current buffer to the indicated file.
6008 @item ToLayout
6009 Pastes any elements in the current buffer to the indicated X, Y
6010 coordinates in the layout. The @code{X} and @code{Y} are treated like
6011 @code{delta} is for many other objects. For each, if it's prefixed by
6012 @code{+} or @code{-}, then that amount is relative to the last
6013 location. Otherwise, it's absolute. Units can be
6014 @code{mil} or @code{mm}; if unspecified, units are PCB's internal
6015 units, currently 1/100 mil.
6018 @item 1..MAX_BUFFER
6019 Selects the given buffer to be the current paste buffer.
6021 @end table
6023 %end-doc */
6025 static int
6026 ActionPasteBuffer (int argc, char **argv, Coord x, Coord y)
6028 char *function = argc ? argv[0] : (char *)"";
6029 char *sbufnum = argc > 1 ? argv[1] : (char *)"";
6030 char *name;
6031 static char *default_file = NULL;
6032 int free_name = 0;
6034 notify_crosshair_change (false);
6035 if (function)
6037 switch (GetFunctionID (function))
6039 /* clear contents of paste buffer */
6040 case F_Clear:
6041 ClearBuffer (PASTEBUFFER);
6042 break;
6044 /* copies objects to paste buffer */
6045 case F_AddSelected:
6046 AddSelectedToBuffer (PASTEBUFFER, 0, 0, false);
6047 break;
6049 /* converts buffer contents into an element */
6050 case F_Convert:
6051 ConvertBufferToElement (PASTEBUFFER);
6052 break;
6054 /* break up element for editing */
6055 case F_Restore:
6056 SmashBufferElement (PASTEBUFFER);
6057 break;
6059 /* Mirror buffer */
6060 case F_Mirror:
6061 MirrorBuffer (PASTEBUFFER);
6062 break;
6064 case F_Rotate:
6065 if (sbufnum)
6067 RotateBuffer (PASTEBUFFER, (BYTE) atoi (sbufnum));
6068 SetCrosshairRangeToBuffer ();
6070 break;
6072 case F_Save:
6073 if (PASTEBUFFER->Data->ElementN == 0)
6075 Message (_("Buffer has no elements!\n"));
6076 break;
6078 free_name = 0;
6079 if (argc <= 1)
6081 name = gui->fileselect (_("Save Paste Buffer As ..."),
6082 _("Choose a file to save the contents of the\n"
6083 "paste buffer to.\n"),
6084 default_file, ".fp", "footprint",
6087 if (default_file)
6089 free (default_file);
6090 default_file = NULL;
6092 if ( name && *name)
6094 default_file = strdup (name);
6096 free_name = 1;
6099 else
6100 name = argv[1];
6103 FILE *exist;
6105 if ((exist = fopen (name, "r")))
6107 fclose (exist);
6108 if (gui->
6109 confirm_dialog (_("File exists! Ok to overwrite?"), 0))
6110 SaveBufferElements (name);
6112 else
6113 SaveBufferElements (name);
6115 if (free_name && name)
6116 free (name);
6118 break;
6120 case F_ToLayout:
6122 static Coord oldx = 0, oldy = 0;
6123 Coord x, y;
6124 bool absolute;
6126 if (argc == 1)
6128 x = y = 0;
6130 else if (argc == 3 || argc == 4)
6132 x = GetValue (ARG (1), ARG (3), &absolute);
6133 if (!absolute)
6134 x += oldx;
6135 y = GetValue (ARG (2), ARG (3), &absolute);
6136 if (!absolute)
6137 y += oldy;
6139 else
6141 notify_crosshair_change (true);
6142 AFAIL (pastebuffer);
6145 oldx = x;
6146 oldy = y;
6147 if (CopyPastebufferToLayout (x, y))
6148 SetChangedFlag (true);
6150 break;
6152 /* set number */
6153 default:
6155 int number = atoi (function);
6157 /* correct number */
6158 if (number)
6159 SetBufferNumber (number - 1);
6164 notify_crosshair_change (true);
6165 return 0;
6168 /* --------------------------------------------------------------------------- */
6170 static const char undo_syntax[] = "Undo()\n"
6171 "Undo(ClearList)";
6173 static const char undo_help[] = "Undo recent changes.";
6175 /* %start-doc actions Undo
6177 The unlimited undo feature of @code{Pcb} allows you to recover from
6178 most operations that materially affect you work. Calling
6179 @code{Undo()} without any parameter recovers from the last (non-undo)
6180 operation. @code{ClearList} is used to release the allocated
6181 memory. @code{ClearList} is called whenever a new layout is started or
6182 loaded. See also @code{Redo} and @code{Atomic}.
6184 Note that undo groups operations by serial number; changes with the
6185 same serial number will be undone (or redone) as a group. See
6186 @code{Atomic}.
6188 %end-doc */
6190 static int
6191 ActionUndo (int argc, char **argv, Coord x, Coord y)
6193 char *function = ARG (0);
6194 if (!function || !*function)
6196 /* don't allow undo in the middle of an operation */
6197 if (Settings.Mode != POLYGONHOLE_MODE &&
6198 Crosshair.AttachedObject.State != STATE_FIRST)
6199 return 1;
6200 if (Crosshair.AttachedBox.State != STATE_FIRST
6201 && Settings.Mode != ARC_MODE)
6202 return 1;
6203 /* undo the last operation */
6205 notify_crosshair_change (false);
6206 if ((Settings.Mode == POLYGON_MODE ||
6207 Settings.Mode == POLYGONHOLE_MODE) &&
6208 Crosshair.AttachedPolygon.PointN)
6210 GoToPreviousPoint ();
6211 notify_crosshair_change (true);
6212 return 0;
6214 /* move anchor point if undoing during line creation */
6215 if (Settings.Mode == LINE_MODE)
6217 if (Crosshair.AttachedLine.State == STATE_SECOND)
6219 if (TEST_FLAG (AUTODRCFLAG, PCB))
6220 Undo (true); /* undo the connection find */
6221 Crosshair.AttachedLine.State = STATE_FIRST;
6222 SetLocalRef (0, 0, false);
6223 notify_crosshair_change (true);
6224 return 0;
6226 if (Crosshair.AttachedLine.State == STATE_THIRD)
6228 int type;
6229 void *ptr1, *ptr3, *ptrtmp;
6230 LineType *ptr2;
6231 /* this search is guaranteed to succeed */
6232 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6233 &ptrtmp, &ptr3,
6234 Crosshair.AttachedLine.Point1.X,
6235 Crosshair.AttachedLine.Point1.Y, 0);
6236 ptr2 = (LineType *) ptrtmp;
6238 /* save both ends of line */
6239 Crosshair.AttachedLine.Point2.X = ptr2->Point1.X;
6240 Crosshair.AttachedLine.Point2.Y = ptr2->Point1.Y;
6241 if ((type = Undo (true)))
6242 SetChangedFlag (true);
6243 /* check that the undo was of the right type */
6244 if ((type & UNDO_CREATE) == 0)
6246 /* wrong undo type, restore anchor points */
6247 Crosshair.AttachedLine.Point2.X =
6248 Crosshair.AttachedLine.Point1.X;
6249 Crosshair.AttachedLine.Point2.Y =
6250 Crosshair.AttachedLine.Point1.Y;
6251 notify_crosshair_change (true);
6252 return 0;
6254 /* move to new anchor */
6255 Crosshair.AttachedLine.Point1.X =
6256 Crosshair.AttachedLine.Point2.X;
6257 Crosshair.AttachedLine.Point1.Y =
6258 Crosshair.AttachedLine.Point2.Y;
6259 /* check if an intermediate point was removed */
6260 if (type & UNDO_REMOVE)
6262 /* this search should find the restored line */
6263 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6264 &ptrtmp,
6265 &ptr3,
6266 Crosshair.AttachedLine.Point2.X,
6267 Crosshair.AttachedLine.Point2.Y, 0);
6268 ptr2 = (LineType *) ptrtmp;
6269 if (TEST_FLAG (AUTODRCFLAG, PCB))
6271 /* undo loses CONNECTEDFLAG and FOUNDFLAG */
6272 SET_FLAG(CONNECTEDFLAG, ptr2);
6273 SET_FLAG(FOUNDFLAG, ptr2);
6274 DrawLine (CURRENT, ptr2);
6276 Crosshair.AttachedLine.Point1.X =
6277 Crosshair.AttachedLine.Point2.X = ptr2->Point2.X;
6278 Crosshair.AttachedLine.Point1.Y =
6279 Crosshair.AttachedLine.Point2.Y = ptr2->Point2.Y;
6281 FitCrosshairIntoGrid (Crosshair.X, Crosshair.Y);
6282 AdjustAttachedObjects ();
6283 if (--addedLines == 0)
6285 Crosshair.AttachedLine.State = STATE_SECOND;
6286 lastLayer = CURRENT;
6288 else
6290 /* this search is guaranteed to succeed too */
6291 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6292 &ptrtmp,
6293 &ptr3,
6294 Crosshair.AttachedLine.Point1.X,
6295 Crosshair.AttachedLine.Point1.Y, 0);
6296 ptr2 = (LineType *) ptrtmp;
6297 lastLayer = (LayerType *) ptr1;
6299 notify_crosshair_change (true);
6300 return 0;
6303 if (Settings.Mode == ARC_MODE)
6305 if (Crosshair.AttachedBox.State == STATE_SECOND)
6307 Crosshair.AttachedBox.State = STATE_FIRST;
6308 notify_crosshair_change (true);
6309 return 0;
6311 if (Crosshair.AttachedBox.State == STATE_THIRD)
6313 void *ptr1, *ptr2, *ptr3;
6314 BoxType *bx;
6315 /* guaranteed to succeed */
6316 SearchObjectByLocation (ARC_TYPE, &ptr1, &ptr2, &ptr3,
6317 Crosshair.AttachedBox.Point1.X,
6318 Crosshair.AttachedBox.Point1.Y, 0);
6319 bx = GetArcEnds ((ArcType *) ptr2);
6320 Crosshair.AttachedBox.Point1.X =
6321 Crosshair.AttachedBox.Point2.X = bx->X1;
6322 Crosshair.AttachedBox.Point1.Y =
6323 Crosshair.AttachedBox.Point2.Y = bx->Y1;
6324 AdjustAttachedObjects ();
6325 if (--addedLines == 0)
6326 Crosshair.AttachedBox.State = STATE_SECOND;
6329 /* undo the last destructive operation */
6330 if (Undo (true))
6331 SetChangedFlag (true);
6333 else if (function)
6335 switch (GetFunctionID (function))
6337 /* clear 'undo objects' list */
6338 case F_ClearList:
6339 ClearUndoList (false);
6340 break;
6343 notify_crosshair_change (true);
6344 return 0;
6347 /* --------------------------------------------------------------------------- */
6349 static const char redo_syntax[] = "Redo()";
6351 static const char redo_help[] = "Redo recent \"undo\" operations.";
6353 /* %start-doc actions Redo
6355 This routine allows you to recover from the last undo command. You
6356 might want to do this if you thought that undo was going to revert
6357 something other than what it actually did (in case you are confused
6358 about which operations are un-doable), or if you have been backing up
6359 through a long undo list and over-shoot your stopping point. Any
6360 change that is made since the undo in question will trim the redo
6361 list. For example if you add ten lines, then undo three of them you
6362 could use redo to put them back, but if you move a line on the board
6363 before performing the redo, you will lose the ability to "redo" the
6364 three "undone" lines.
6366 %end-doc */
6368 static int
6369 ActionRedo (int argc, char **argv, Coord x, Coord y)
6371 if (((Settings.Mode == POLYGON_MODE ||
6372 Settings.Mode == POLYGONHOLE_MODE) &&
6373 Crosshair.AttachedPolygon.PointN) ||
6374 Crosshair.AttachedLine.State == STATE_SECOND)
6375 return 1;
6376 notify_crosshair_change (false);
6377 if (Redo (true))
6379 SetChangedFlag (true);
6380 if (Settings.Mode == LINE_MODE &&
6381 Crosshair.AttachedLine.State != STATE_FIRST)
6383 LineType *line = g_list_last (CURRENT->Line)->data;
6384 Crosshair.AttachedLine.Point1.X =
6385 Crosshair.AttachedLine.Point2.X = line->Point2.X;
6386 Crosshair.AttachedLine.Point1.Y =
6387 Crosshair.AttachedLine.Point2.Y = line->Point2.Y;
6388 addedLines++;
6391 notify_crosshair_change (true);
6392 return 0;
6395 /* --------------------------------------------------------------------------- */
6397 static const char polygon_syntax[] = "Polygon(Close|PreviousPoint)";
6399 static const char polygon_help[] = "Some polygon related stuff.";
6401 /* %start-doc actions Polygon
6403 Polygons need a special action routine to make life easier.
6405 @table @code
6407 @item Close
6408 Creates the final segment of the polygon. This may fail if clipping
6409 to 45 degree lines is switched on, in which case a warning is issued.
6411 @item PreviousPoint
6412 Resets the newly entered corner to the previous one. The Undo action
6413 will call Polygon(PreviousPoint) when appropriate to do so.
6415 @end table
6417 %end-doc */
6419 static int
6420 ActionPolygon (int argc, char **argv, Coord x, Coord y)
6422 char *function = ARG (0);
6423 if (function && Settings.Mode == POLYGON_MODE)
6425 notify_crosshair_change (false);
6426 switch (GetFunctionID (function))
6428 /* close open polygon if possible */
6429 case F_Close:
6430 ClosePolygon ();
6431 break;
6433 /* go back to the previous point */
6434 case F_PreviousPoint:
6435 GoToPreviousPoint ();
6436 break;
6438 notify_crosshair_change (true);
6440 return 0;
6443 /* --------------------------------------------------------------------------- */
6445 static const char routestyle_syntax[] = "RouteStyle(1|2|3|4)";
6447 static const char routestyle_help[] =
6448 "Copies the indicated routing style into the current sizes.";
6450 /* %start-doc actions RouteStyle
6452 %end-doc */
6454 static int
6455 ActionRouteStyle (int argc, char **argv, Coord x, Coord y)
6457 char *str = ARG (0);
6458 RouteStyleType *rts;
6459 int number;
6461 if (str)
6463 number = atoi (str);
6464 if (number > 0 && number <= NUM_STYLES)
6466 rts = &PCB->RouteStyle[number - 1];
6467 SetLineSize (rts->Thick);
6468 SetViaSize (rts->Diameter, true);
6469 SetViaDrillingHole (rts->Hole, true);
6470 SetKeepawayWidth (rts->Keepaway);
6471 hid_action("RouteStylesChanged");
6474 return 0;
6478 /* --------------------------------------------------------------------------- */
6480 static const char moveobject_syntax[] = "MoveObject(X,Y,dim)";
6482 static const char moveobject_help[] = "Moves the object under the crosshair.";
6484 /* %start-doc actions MoveObject
6486 The @code{X} and @code{Y} are treated like @code{delta} is for many
6487 other objects. For each, if it's prefixed by @code{+} or @code{-},
6488 then that amount is relative. Otherwise, it's absolute. Units can be
6489 @code{mil} or @code{mm}; if unspecified, units are PCB's internal
6490 units, currently 1/100 mil.
6492 %end-doc */
6494 static int
6495 ActionMoveObject (int argc, char **argv, Coord x, Coord y)
6497 char *x_str = ARG (0);
6498 char *y_str = ARG (1);
6499 char *units = ARG (2);
6500 Coord nx, ny;
6501 bool absolute1, absolute2;
6502 void *ptr1, *ptr2, *ptr3;
6503 int type;
6505 ny = GetValue (y_str, units, &absolute1);
6506 nx = GetValue (x_str, units, &absolute2);
6508 type = SearchScreen (x, y, MOVE_TYPES, &ptr1, &ptr2, &ptr3);
6509 if (type == NO_TYPE)
6511 Message (_("Nothing found under crosshair\n"));
6512 return 1;
6514 if (absolute1)
6515 nx -= x;
6516 if (absolute2)
6517 ny -= y;
6518 Crosshair.AttachedObject.RubberbandN = 0;
6519 if (TEST_FLAG (RUBBERBANDFLAG, PCB))
6520 LookupRubberbandLines (type, ptr1, ptr2, ptr3);
6521 if (type == ELEMENT_TYPE)
6522 LookupRatLines (type, ptr1, ptr2, ptr3);
6523 MoveObjectAndRubberband (type, ptr1, ptr2, ptr3, nx, ny);
6524 SetChangedFlag (true);
6525 return 0;
6528 /* --------------------------------------------------------------------------- */
6530 static const char movetocurrentlayer_syntax[] =
6531 "MoveToCurrentLayer(Object|SelectedObjects)";
6533 static const char movetocurrentlayer_help[] =
6534 "Moves objects to the current layer.";
6536 /* %start-doc actions MoveToCurrentLayer
6538 Note that moving an element from a component layer to a solder layer,
6539 or from solder to component, won't automatically flip it. Use the
6540 @code{Flip()} action to do that.
6542 %end-doc */
6544 static int
6545 ActionMoveToCurrentLayer (int argc, char **argv, Coord x, Coord y)
6547 char *function = ARG (0);
6548 if (function)
6550 switch (GetFunctionID (function))
6552 case F_Object:
6554 int type;
6555 void *ptr1, *ptr2, *ptr3;
6557 gui->get_coords (_("Select an Object"), &x, &y);
6558 if ((type =
6559 SearchScreen (x, y, MOVETOLAYER_TYPES,
6560 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
6561 if (MoveObjectToLayer (type, ptr1, ptr2, ptr3, CURRENT, false))
6562 SetChangedFlag (true);
6563 break;
6566 case F_SelectedObjects:
6567 case F_Selected:
6568 if (MoveSelectedObjectsToLayer (CURRENT))
6569 SetChangedFlag (true);
6570 break;
6573 return 0;
6577 static const char setsame_syntax[] = "SetSame()";
6579 static const char setsame_help[] =
6580 "Sets current layer and sizes to match indicated item.";
6582 /* %start-doc actions SetSame
6584 When invoked over any line, arc, polygon, or via, this changes the
6585 current layer to be the layer that item is on, and changes the current
6586 sizes (thickness, keepaway, drill, etc) according to that item.
6588 %end-doc */
6590 static int
6591 ActionSetSame (int argc, char **argv, Coord x, Coord y)
6593 void *ptr1, *ptr2, *ptr3;
6594 int type;
6595 LayerType *layer = CURRENT;
6597 type = SearchScreen (x, y, CLONE_TYPES, &ptr1, &ptr2, &ptr3);
6598 /* set layer current and size from line or arc */
6599 switch (type)
6601 case LINE_TYPE:
6602 notify_crosshair_change (false);
6603 Settings.LineThickness = ((LineType *) ptr2)->Thickness;
6604 Settings.Keepaway = ((LineType *) ptr2)->Clearance / 2;
6605 layer = (LayerType *) ptr1;
6606 if (Settings.Mode != LINE_MODE)
6607 SetMode (LINE_MODE);
6608 notify_crosshair_change (true);
6609 hid_action ("RouteStylesChanged");
6610 break;
6612 case ARC_TYPE:
6613 notify_crosshair_change (false);
6614 Settings.LineThickness = ((ArcType *) ptr2)->Thickness;
6615 Settings.Keepaway = ((ArcType *) ptr2)->Clearance / 2;
6616 layer = (LayerType *) ptr1;
6617 if (Settings.Mode != ARC_MODE)
6618 SetMode (ARC_MODE);
6619 notify_crosshair_change (true);
6620 hid_action ("RouteStylesChanged");
6621 break;
6623 case POLYGON_TYPE:
6624 layer = (LayerType *) ptr1;
6625 break;
6627 case VIA_TYPE:
6628 notify_crosshair_change (false);
6629 Settings.ViaThickness = ((PinType *) ptr2)->Thickness;
6630 Settings.ViaDrillingHole = ((PinType *) ptr2)->DrillingHole;
6631 Settings.Keepaway = ((PinType *) ptr2)->Clearance / 2;
6632 if (Settings.Mode != VIA_MODE)
6633 SetMode (VIA_MODE);
6634 notify_crosshair_change (true);
6635 hid_action ("RouteStylesChanged");
6636 break;
6638 default:
6639 return 1;
6641 if (layer != CURRENT)
6643 ChangeGroupVisibility (GetLayerNumber (PCB->Data, layer), true, true);
6644 Redraw ();
6646 return 0;
6650 /* --------------------------------------------------------------------------- */
6652 static const char setflag_syntax[] =
6653 "SetFlag(Object|Selected|SelectedObjects, flag)\n"
6654 "SetFlag(SelectedLines|SelectedPins|SelectedVias, flag)\n"
6655 "SetFlag(SelectedPads|SelectedTexts|SelectedNames, flag)\n"
6656 "SetFlag(SelectedElements, flag)\n"
6657 "flag = square | octagon | thermal | join";
6659 static const char setflag_help[] = "Sets flags on objects.";
6661 /* %start-doc actions SetFlag
6663 Turns the given flag on, regardless of its previous setting. See
6664 @code{ChangeFlag}.
6666 @example
6667 SetFlag(SelectedPins,thermal)
6668 @end example
6670 %end-doc */
6672 static int
6673 ActionSetFlag (int argc, char **argv, Coord x, Coord y)
6675 char *function = ARG (0);
6676 char *flag = ARG (1);
6677 ChangeFlag (function, flag, 1, "SetFlag");
6678 return 0;
6681 /* --------------------------------------------------------------------------- */
6683 static const char clrflag_syntax[] =
6684 "ClrFlag(Object|Selected|SelectedObjects, flag)\n"
6685 "ClrFlag(SelectedLines|SelectedPins|SelectedVias, flag)\n"
6686 "ClrFlag(SelectedPads|SelectedTexts|SelectedNames, flag)\n"
6687 "ClrFlag(SelectedElements, flag)\n"
6688 "flag = square | octagon | thermal | join";
6690 static const char clrflag_help[] = "Clears flags on objects.";
6692 /* %start-doc actions ClrFlag
6694 Turns the given flag off, regardless of its previous setting. See
6695 @code{ChangeFlag}.
6697 @example
6698 ClrFlag(SelectedLines,join)
6699 @end example
6701 %end-doc */
6703 static int
6704 ActionClrFlag (int argc, char **argv, Coord x, Coord y)
6706 char *function = ARG (0);
6707 char *flag = ARG (1);
6708 ChangeFlag (function, flag, 0, "ClrFlag");
6709 return 0;
6712 /* --------------------------------------------------------------------------- */
6714 static const char changeflag_syntax[] =
6715 "ChangeFlag(Object|Selected|SelectedObjects, flag, value)\n"
6716 "ChangeFlag(SelectedLines|SelectedPins|SelectedVias, flag, value)\n"
6717 "ChangeFlag(SelectedPads|SelectedTexts|SelectedNames, flag, value)\n"
6718 "ChangeFlag(SelectedElements, flag, value)\n"
6719 "flag = square | octagon | thermal | join\n"
6720 "value = 0 | 1";
6722 static const char changeflag_help[] = "Sets or clears flags on objects.";
6724 /* %start-doc actions ChangeFlag
6726 Toggles the given flag on the indicated object(s). The flag may be
6727 one of the flags listed above (square, octagon, thermal, join). The
6728 value may be the number 0 or 1. If the value is 0, the flag is
6729 cleared. If the value is 1, the flag is set.
6731 %end-doc */
6733 static int
6734 ActionChangeFlag (int argc, char **argv, Coord x, Coord y)
6736 char *function = ARG (0);
6737 char *flag = ARG (1);
6738 int value = argc > 2 ? atoi (argv[2]) : -1;
6739 if (value != 0 && value != 1)
6740 AFAIL (changeflag);
6742 ChangeFlag (function, flag, value, "ChangeFlag");
6743 return 0;
6747 static void
6748 ChangeFlag (char *what, char *flag_name, int value, char *cmd_name)
6750 bool (*set_object) (int, void *, void *, void *);
6751 bool (*set_selected) (int);
6753 if (NSTRCMP (flag_name, "square") == 0)
6755 set_object = value ? SetObjectSquare : ClrObjectSquare;
6756 set_selected = value ? SetSelectedSquare : ClrSelectedSquare;
6758 else if (NSTRCMP (flag_name, "octagon") == 0)
6760 set_object = value ? SetObjectOctagon : ClrObjectOctagon;
6761 set_selected = value ? SetSelectedOctagon : ClrSelectedOctagon;
6763 else if (NSTRCMP (flag_name, "join") == 0)
6765 /* Note: these are backwards, because the flag is "clear" but
6766 the command is "join". */
6767 set_object = value ? ClrObjectJoin : SetObjectJoin;
6768 set_selected = value ? ClrSelectedJoin : SetSelectedJoin;
6770 else
6772 Message (_("%s(): Flag \"%s\" is not valid\n"), cmd_name, flag_name);
6773 return;
6776 switch (GetFunctionID (what))
6778 case F_Object:
6780 int type;
6781 void *ptr1, *ptr2, *ptr3;
6783 if ((type =
6784 SearchScreen (Crosshair.X, Crosshair.Y, CHANGESIZE_TYPES,
6785 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
6786 if (TEST_FLAG (LOCKFLAG, (PinType *) ptr2))
6787 Message (_("Sorry, the object is locked\n"));
6788 if (set_object (type, ptr1, ptr2, ptr3))
6789 SetChangedFlag (true);
6790 break;
6793 case F_SelectedVias:
6794 if (set_selected (VIA_TYPE))
6795 SetChangedFlag (true);
6796 break;
6798 case F_SelectedPins:
6799 if (set_selected (PIN_TYPE))
6800 SetChangedFlag (true);
6801 break;
6803 case F_SelectedPads:
6804 if (set_selected (PAD_TYPE))
6805 SetChangedFlag (true);
6806 break;
6808 case F_SelectedLines:
6809 if (set_selected (LINE_TYPE))
6810 SetChangedFlag (true);
6811 break;
6813 case F_SelectedTexts:
6814 if (set_selected (TEXT_TYPE))
6815 SetChangedFlag (true);
6816 break;
6818 case F_SelectedNames:
6819 if (set_selected (ELEMENTNAME_TYPE))
6820 SetChangedFlag (true);
6821 break;
6823 case F_SelectedElements:
6824 if (set_selected (ELEMENT_TYPE))
6825 SetChangedFlag (true);
6826 break;
6828 case F_Selected:
6829 case F_SelectedObjects:
6830 if (set_selected (CHANGESIZE_TYPES))
6831 SetChangedFlag (true);
6832 break;
6836 /* --------------------------------------------------------------------------- */
6838 static const char executefile_syntax[] = "ExecuteFile(filename)";
6840 static const char executefile_help[] = "Run actions from the given file.";
6842 /* %start-doc actions ExecuteFile
6844 Lines starting with @code{#} are ignored.
6846 %end-doc */
6848 static int
6849 ActionExecuteFile (int argc, char **argv, Coord x, Coord y)
6851 FILE *fp;
6852 char *fname;
6853 char line[256];
6854 int n = 0;
6855 char *sp;
6857 if (argc != 1)
6858 AFAIL (executefile);
6860 fname = argv[0];
6862 if ((fp = fopen (fname, "r")) == NULL)
6864 fprintf (stderr, _("Could not open actions file \"%s\".\n"), fname);
6865 return 1;
6868 defer_updates = 1;
6869 defer_needs_update = 0;
6870 while (fgets (line, sizeof (line), fp) != NULL)
6872 n++;
6873 sp = line;
6875 /* eat the trailing newline */
6876 while (*sp && *sp != '\r' && *sp != '\n')
6877 sp++;
6878 *sp = '\0';
6880 /* eat leading spaces and tabs */
6881 sp = line;
6882 while (*sp && (*sp == ' ' || *sp == '\t'))
6883 sp++;
6886 * if we have anything left and its not a comment line
6887 * then execute it
6890 if (*sp && *sp != '#')
6892 /*Message ("%s : line %-3d : \"%s\"\n", fname, n, sp);*/
6893 hid_parse_actions (sp);
6897 defer_updates = 0;
6898 if (defer_needs_update)
6900 IncrementUndoSerialNumber ();
6901 gui->invalidate_all ();
6903 fclose (fp);
6904 return 0;
6907 /* --------------------------------------------------------------------------- */
6909 static int
6910 ActionPSCalib (int argc, char **argv, Coord x, Coord y)
6912 HID *ps = hid_find_exporter ("ps");
6913 ps->calibrate (0.0,0.0);
6914 return 0;
6917 /* --------------------------------------------------------------------------- */
6919 static ElementType *element_cache = NULL;
6921 static ElementType *
6922 find_element_by_refdes (char *refdes)
6924 if (element_cache
6925 && NAMEONPCB_NAME(element_cache)
6926 && strcmp (NAMEONPCB_NAME(element_cache), refdes) == 0)
6927 return element_cache;
6929 ELEMENT_LOOP (PCB->Data);
6931 if (NAMEONPCB_NAME(element)
6932 && strcmp (NAMEONPCB_NAME(element), refdes) == 0)
6934 element_cache = element;
6935 return element_cache;
6938 END_LOOP;
6939 return NULL;
6942 static AttributeType *
6943 lookup_attr (AttributeListType *list, const char *name)
6945 int i;
6946 for (i=0; i<list->Number; i++)
6947 if (strcmp (list->List[i].name, name) == 0)
6948 return & list->List[i];
6949 return NULL;
6952 static void
6953 delete_attr (AttributeListType *list, AttributeType *attr)
6955 int idx = attr - list->List;
6956 if (idx < 0 || idx >= list->Number)
6957 return;
6958 if (list->Number - idx > 1)
6959 memmove (attr, attr+1, (list->Number - idx - 1) * sizeof(AttributeType));
6960 list->Number --;
6963 /* ---------------------------------------------------------------- */
6964 static const char elementlist_syntax[] = "ElementList(Start|Done|Need,<refdes>,<footprint>,<value>)";
6966 static const char elementlist_help[] = "Adds the given element if it doesn't already exist.";
6968 /* %start-doc actions elementlist
6970 @table @code
6972 @item Start
6973 Indicates the start of an element list; call this before any Need
6974 actions.
6976 @item Need
6977 Searches the board for an element with a matching refdes.
6979 If found, the value and footprint are updated.
6981 If not found, a new element is created with the given footprint and value.
6983 @item Done
6984 Compares the list of elements needed since the most recent
6985 @code{start} with the list of elements actually on the board. Any
6986 elements that weren't listed are selected, so that the user may delete
6987 them.
6989 @end table
6991 %end-doc */
6993 static int number_of_footprints_not_found;
6995 static int
6996 parse_layout_attribute_units (char *name, int def)
6998 const char *as = AttributeGet (PCB, name);
6999 if (!as)
7000 return def;
7001 return GetValue (as, NULL, NULL);
7004 static int
7005 ActionElementList (int argc, char **argv, Coord x, Coord y)
7007 ElementType *e = NULL;
7008 char *refdes, *value, *footprint, *old;
7009 char *args[3];
7010 char *function = argv[0];
7012 #ifdef DEBUG
7013 printf("Entered ActionElementList, executing function %s\n", function);
7014 #endif
7016 if (strcasecmp (function, "start") == 0)
7018 ELEMENT_LOOP (PCB->Data);
7020 CLEAR_FLAG (FOUNDFLAG, element);
7022 END_LOOP;
7023 element_cache = NULL;
7024 number_of_footprints_not_found = 0;
7025 return 0;
7028 if (strcasecmp (function, "done") == 0)
7030 ELEMENT_LOOP (PCB->Data);
7032 if (TEST_FLAG (FOUNDFLAG, element))
7034 CLEAR_FLAG (FOUNDFLAG, element);
7036 else if (! EMPTY_STRING_P (NAMEONPCB_NAME (element)))
7038 /* Unnamed elements should remain untouched */
7039 SET_FLAG (SELECTEDFLAG, element);
7042 END_LOOP;
7043 if (number_of_footprints_not_found > 0)
7044 gui->confirm_dialog ("Not all requested footprints were found.\n"
7045 "See the message log for details",
7046 "Ok", NULL);
7047 return 0;
7050 if (strcasecmp (function, "need") != 0)
7051 AFAIL (elementlist);
7053 if (argc != 4)
7054 AFAIL (elementlist);
7056 argc --;
7057 argv ++;
7059 refdes = ARG(0);
7060 footprint = ARG(1);
7061 value = ARG(2);
7063 args[0] = footprint;
7064 args[1] = refdes;
7065 args[2] = value;
7067 #ifdef DEBUG
7068 printf(" ... footprint = %s\n", footprint);
7069 printf(" ... refdes = %s\n", refdes);
7070 printf(" ... value = %s\n", value);
7071 #endif
7073 e = find_element_by_refdes (refdes);
7075 if (!e)
7077 Coord nx, ny, d;
7079 #ifdef DEBUG
7080 printf(" ... Footprint not on board, need to add it.\n");
7081 #endif
7082 /* Not on board, need to add it. */
7083 if (LoadFootprint(argc, args, x, y))
7085 number_of_footprints_not_found ++;
7086 return 1;
7089 nx = PCB->MaxWidth / 2;
7090 ny = PCB->MaxHeight / 2;
7091 d = MIN (PCB->MaxWidth, PCB->MaxHeight) / 10;
7093 nx = parse_layout_attribute_units ("import::newX", nx);
7094 ny = parse_layout_attribute_units ("import::newY", ny);
7095 d = parse_layout_attribute_units ("import::disperse", d);
7097 if (d > 0)
7099 nx += rand () % (d*2) - d;
7100 ny += rand () % (d*2) - d;
7103 if (nx < 0)
7104 nx = 0;
7105 if (nx >= PCB->MaxWidth)
7106 nx = PCB->MaxWidth - 1;
7107 if (ny < 0)
7108 ny = 0;
7109 if (ny >= PCB->MaxHeight)
7110 ny = PCB->MaxHeight - 1;
7112 /* Place components onto center of board. */
7113 if (CopyPastebufferToLayout (nx, ny))
7114 SetChangedFlag (true);
7117 else if (e && DESCRIPTION_NAME(e) && strcmp (DESCRIPTION_NAME(e), footprint) != 0)
7119 #ifdef DEBUG
7120 printf(" ... Footprint on board, but different from footprint loaded.\n");
7121 #endif
7122 int er, pr, i;
7123 Coord mx, my;
7124 ElementType *pe;
7126 /* Different footprint, we need to swap them out. */
7127 if (LoadFootprint(argc, args, x, y))
7129 number_of_footprints_not_found ++;
7130 return 1;
7133 er = ElementOrientation (e);
7134 pe = PASTEBUFFER->Data->Element->data;
7135 if (!FRONT (e))
7136 MirrorElementCoordinates (PASTEBUFFER->Data, pe, pe->MarkY*2 - PCB->MaxHeight);
7137 pr = ElementOrientation (pe);
7139 mx = e->MarkX;
7140 my = e->MarkY;
7142 if (er != pr)
7143 RotateElementLowLevel (PASTEBUFFER->Data, pe, pe->MarkX, pe->MarkY, (er-pr+4)%4);
7145 for (i=0; i<MAX_ELEMENTNAMES; i++)
7147 pe->Name[i].X = e->Name[i].X - mx + pe->MarkX ;
7148 pe->Name[i].Y = e->Name[i].Y - my + pe->MarkY ;
7149 pe->Name[i].Direction = e->Name[i].Direction;
7150 pe->Name[i].Scale = e->Name[i].Scale;
7153 RemoveElement (e);
7155 if (CopyPastebufferToLayout (mx, my))
7156 SetChangedFlag (true);
7159 /* Now reload footprint */
7160 element_cache = NULL;
7161 e = find_element_by_refdes (refdes);
7163 old = ChangeElementText (PCB, PCB->Data, e, NAMEONPCB_INDEX, strdup (refdes));
7164 if (old)
7165 free(old);
7166 old = ChangeElementText (PCB, PCB->Data, e, VALUE_INDEX, strdup (value));
7167 if (old)
7168 free(old);
7170 SET_FLAG (FOUNDFLAG, e);
7172 #ifdef DEBUG
7173 printf(" ... Leaving ActionElementList.\n");
7174 #endif
7176 return 0;
7179 /* ---------------------------------------------------------------- */
7180 static const char elementsetattr_syntax[] = "ElementSetAttr(refdes,name[,value])";
7182 static const char elementsetattr_help[] = "Sets or clears an element-specific attribute.";
7184 /* %start-doc actions elementsetattr
7186 If a value is specified, the named attribute is added (if not already
7187 present) or changed (if it is) to the given value. If the value is
7188 not specified, the given attribute is removed if present.
7190 %end-doc */
7192 static int
7193 ActionElementSetAttr (int argc, char **argv, Coord x, Coord y)
7195 ElementType *e = NULL;
7196 char *refdes, *name, *value;
7197 AttributeType *attr;
7199 if (argc < 2)
7201 AFAIL (changepinname);
7204 refdes = argv[0];
7205 name = argv[1];
7206 value = ARG(2);
7208 ELEMENT_LOOP (PCB->Data);
7210 if (NSTRCMP (refdes, NAMEONPCB_NAME (element)) == 0)
7212 e = element;
7213 break;
7216 END_LOOP;
7218 if (!e)
7220 Message(_("Cannot change attribute of %s - element not found\n"), refdes);
7221 return 1;
7224 attr = lookup_attr (&e->Attributes, name);
7226 if (attr && value)
7228 free (attr->value);
7229 attr->value = strdup (value);
7231 if (attr && ! value)
7233 delete_attr (& e->Attributes, attr);
7235 if (!attr && value)
7237 CreateNewAttribute (& e->Attributes, name, value);
7240 return 0;
7243 /* ---------------------------------------------------------------- */
7244 static const char execcommand_syntax[] = "ExecCommand(command)";
7246 static const char execcommand_help[] = "Runs a command.";
7248 /* %start-doc actions execcommand
7250 Runs the given command, which is a system executable.
7252 %end-doc */
7254 static int
7255 ActionExecCommand (int argc, char **argv, Coord x, Coord y)
7257 char *command;
7259 if (argc < 1)
7261 AFAIL (execcommand);
7264 command = ARG(0);
7266 if (system (command))
7267 return 1;
7268 return 0;
7271 /* ---------------------------------------------------------------- */
7273 static int
7274 pcb_spawnvp (char **argv)
7276 #ifdef HAVE__SPAWNVP
7277 int result = _spawnvp (_P_WAIT, argv[0], (const char * const *) argv);
7278 if (result == -1)
7279 return 1;
7280 else
7281 return 0;
7282 #else
7283 int pid;
7284 pid = fork ();
7285 if (pid < 0)
7287 /* error */
7288 Message(_("Cannot fork!"));
7289 return 1;
7291 else if (pid == 0)
7293 /* Child */
7294 execvp (argv[0], argv);
7295 exit(1);
7297 else
7299 int rv;
7300 /* Parent */
7301 wait (&rv);
7303 return 0;
7304 #endif
7307 /* ---------------------------------------------------------------- */
7309 * Creates a new temporary file name. Hopefully the operating system
7310 * provides a mkdtemp() function to securily create a temporary
7311 * directory with mode 0700. If so then that directory is created and
7312 * the returned string is made up of the directory plus the name
7313 * variable. For example:
7315 * tempfile_name_new ("myfile") might return
7316 * "/var/tmp/pcb.123456/myfile".
7318 * If mkdtemp() is not available then 'name' is ignored and the
7319 * insecure tmpnam() function is used.
7321 * Files/names created with tempfile_name_new() should be unlinked
7322 * with tempfile_unlink to make sure the temporary directory is also
7323 * removed when mkdtemp() is used.
7325 static char *
7326 tempfile_name_new (char * name)
7328 char *tmpfile = NULL;
7329 #ifdef HAVE_MKDTEMP
7330 char *tmpdir, *mytmpdir;
7331 size_t len;
7332 #endif
7334 assert ( name != NULL );
7336 #ifdef HAVE_MKDTEMP
7337 #define TEMPLATE "pcb.XXXXXXXX"
7340 tmpdir = getenv ("TMPDIR");
7342 /* FIXME -- what about win32? */
7343 if (tmpdir == NULL) {
7344 tmpdir = "/tmp";
7347 mytmpdir = (char *) malloc (sizeof(char) *
7348 (strlen (tmpdir) +
7350 strlen (TEMPLATE) +
7351 1));
7352 if (mytmpdir == NULL) {
7353 fprintf (stderr, "%s(): malloc failed()\n", __FUNCTION__);
7354 exit (1);
7357 *mytmpdir = '\0';
7358 (void)strcat (mytmpdir, tmpdir);
7359 (void)strcat (mytmpdir, PCB_DIR_SEPARATOR_S);
7360 (void)strcat (mytmpdir, TEMPLATE);
7361 if (mkdtemp (mytmpdir) == NULL) {
7362 fprintf (stderr, "%s(): mkdtemp (\"%s\") failed\n", __FUNCTION__, mytmpdir);
7363 free (mytmpdir);
7364 return NULL;
7368 len = strlen (mytmpdir) + /* the temp directory name */
7369 1 + /* the directory sep. */
7370 strlen (name) + /* the file name */
7371 1 /* the \0 termination */
7374 tmpfile = (char *) malloc (sizeof (char) * len);
7376 *tmpfile = '\0';
7377 (void)strcat (tmpfile, mytmpdir);
7378 (void)strcat (tmpfile, PCB_DIR_SEPARATOR_S);
7379 (void)strcat (tmpfile, name);
7381 free (mytmpdir);
7382 #undef TEMPLATE
7383 #else
7385 * tmpnam() uses a static buffer so strdup() the result right away
7386 * in case someone decides to create multiple temp names.
7388 tmpfile = strdup (tmpnam (NULL));
7389 #ifdef __WIN32__
7391 /* Guile doesn't like \ separators */
7392 char *c;
7393 for (c = tmpfile; *c; c++)
7394 if (*c == '\\')
7395 *c = '/';
7397 #endif
7398 #endif
7400 return tmpfile;
7403 /* ---------------------------------------------------------------- */
7405 * Unlink a temporary file. If we have mkdtemp() then our temp file
7406 * lives in a temporary directory and we need to remove that directory
7407 * too.
7409 static int
7410 tempfile_unlink (char * name)
7412 #ifdef DEBUG
7413 /* SDB says: Want to keep old temp files for examiniation when debugging */
7414 return 0;
7415 #endif
7417 #ifdef HAVE_MKDTEMP
7418 int e, rc2 = 0;
7419 char *dname;
7421 unlink (name);
7422 /* it is possible that the file was never created so it is OK if the
7423 unlink fails */
7425 /* now figure out the directory name to remove */
7426 e = strlen (name) - 1;
7427 while (e > 0 && name[e] != PCB_DIR_SEPARATOR_C) {e--;}
7429 dname = strdup (name);
7430 dname[e] = '\0';
7433 * at this point, e *should* point to the end of the directory part
7434 * but lets make sure.
7436 if (e > 0) {
7437 rc2 = rmdir (dname);
7438 if (rc2 != 0) {
7439 perror (dname);
7442 } else {
7443 fprintf (stderr, _("%s(): Unable to determine temp directory name from the temp file\n"),
7444 __FUNCTION__);
7445 fprintf (stderr, "%s(): \"%s\"\n",
7446 __FUNCTION__, name);
7447 rc2 = -1;
7450 /* name was allocated with malloc */
7451 free (dname);
7452 free (name);
7455 * FIXME - should also return -1 if the temp file exists and was not
7456 * removed.
7458 if (rc2 != 0) {
7459 return -1;
7462 #else
7463 int rc = unlink (name);
7465 if (rc != 0) {
7466 fprintf (stderr, _("Failed to unlink \"%s\"\n"), name);
7467 free (name);
7468 return rc;
7470 free (name);
7472 #endif
7474 return 0;
7477 /* ---------------------------------------------------------------- */
7478 static const char import_syntax[] =
7479 "Import()\n"
7480 "Import([gnetlist|make[,source,source,...]])\n"
7481 "Import(setnewpoint[,(mark|center|X,Y)])\n"
7482 "Import(setdisperse,D,units)\n";
7484 static const char import_help[] = "Import schematics.";
7486 /* %start-doc actions Import
7488 Imports element and netlist data from the schematics (or some other
7489 source). The first parameter, which is optional, is the mode. If not
7490 specified, the @code{import::mode} attribute in the PCB is used.
7491 @code{gnetlist} means gnetlist is used to obtain the information from
7492 the schematics. @code{make} invokes @code{make}, assuming the user
7493 has a @code{Makefile} in the current directory. The @code{Makefile}
7494 will be invoked with the following variables set:
7496 @table @code
7498 @item PCB
7499 The name of the .pcb file
7501 @item SRCLIST
7502 A space-separated list of source files
7504 @item OUT
7505 The name of the file in which to put the command script, which may
7506 contain any @pcb{} actions. By default, this is a temporary file
7507 selected by @pcb{}, but if you specify an @code{import::outfile}
7508 attribute, that file name is used instead (and not automatically
7509 deleted afterwards).
7511 @end table
7513 The target specified to be built is the first of these that apply:
7515 @itemize @bullet
7517 @item
7518 The target specified by an @code{import::target} attribute.
7520 @item
7521 The output file specified by an @code{import::outfile} attribute.
7523 @item
7524 If nothing else is specified, the target is @code{pcb_import}.
7526 @end itemize
7528 If you specify an @code{import::makefile} attribute, then "-f <that
7529 file>" will be added to the command line.
7531 If you specify the mode, you may also specify the source files
7532 (schematics). If you do not specify any, the list of schematics is
7533 obtained by reading the @code{import::src@var{N}} attributes (like
7534 @code{import::src0}, @code{import::src1}, etc).
7536 For compatibility with future extensions to the import file format,
7537 the generated file @emph{must not} start with the two characters
7538 @code{#%}.
7540 If a temporary file is needed the @code{TMPDIR} environment variable
7541 is used to select its location.
7543 Note that the programs @code{gnetlist} and @code{make} may be
7544 overridden by the user via the @code{make-program} and @code{gnetlist}
7545 @code{pcb} settings (i.e. in @code{~/.pcb/settings} or on the command
7546 line).
7548 If @pcb{} cannot determine which schematic(s) to import from, the GUI
7549 is called to let user choose (see @code{ImportGUI()}).
7551 Note that Import() doesn't delete anything - after an Import, elements
7552 which shouldn't be on the board are selected and may be removed once
7553 it's determined that the deletion is appropriate.
7555 If @code{Import()} is called with @code{setnewpoint}, then the location
7556 of new components can be specified. This is where parts show up when
7557 they're added to the board. The default is the center of the board.
7559 @table @code
7561 @item Import(setnewpoint)
7563 Prompts the user to click on the board somewhere, uses that point. If
7564 called by a hotkey, uses the current location of the crosshair.
7566 @item Import(setnewpoint,mark)
7568 Uses the location of the mark. If no mark is present, the point is
7569 not changed.
7571 @item Import(setnewpoint,center)
7573 Resets the point to the center of the board.
7575 @item Import(setnewpoint,X,Y,units)
7577 Sets the point to the specific coordinates given. Example:
7578 @code{Import(setnewpoint,50,25,mm)}
7580 @end table
7582 Note that the X and Y locations are stored in attributes named
7583 @code{import::newX} and @code{import::newY} so you could change them
7584 manually if you wished.
7586 Calling @code{Import(setdisperse,D,units)} sets how much the newly
7587 placed elements are dispersed relative to the set point. For example,
7588 @code{Import(setdisperse,10,mm)} will offset each part randomly up to
7589 10mm away from the point. The default dispersion is 1/10th of the
7590 smallest board dimension. Dispersion is saved in the
7591 @code{import::disperse} attribute.
7593 %end-doc */
7595 static int
7596 ActionImport (int argc, char **argv, Coord x, Coord y)
7598 char *mode;
7599 char **sources = NULL;
7600 int nsources = 0;
7602 #ifdef DEBUG
7603 printf("ActionImport: =========== Entering ActionImport ============\n");
7604 #endif
7606 mode = ARG (0);
7608 if (mode && strcasecmp (mode, "setdisperse") == 0)
7610 char *ds, *units;
7611 char buf[50];
7613 ds = ARG (1);
7614 units = ARG (2);
7615 if (!ds)
7617 const char *as = AttributeGet (PCB, "import::disperse");
7618 ds = gui->prompt_for(_("Enter dispersion:"), as ? as : "0");
7620 if (units)
7622 sprintf(buf, "%s%s", ds, units);
7623 AttributePut (PCB, "import::disperse", buf);
7625 else
7626 AttributePut (PCB, "import::disperse", ds);
7627 if (ARG (1) == NULL)
7628 free (ds);
7629 return 0;
7632 if (mode && strcasecmp (mode, "setnewpoint") == 0)
7634 const char *xs, *ys, *units;
7635 Coord x, y;
7636 char buf[50];
7638 xs = ARG (1);
7639 ys = ARG (2);
7640 units = ARG (3);
7642 if (!xs)
7644 gui->get_coords (_("Click on a location"), &x, &y);
7646 else if (strcasecmp (xs, "center") == 0)
7648 AttributeRemove (PCB, "import::newX");
7649 AttributeRemove (PCB, "import::newY");
7650 return 0;
7652 else if (strcasecmp (xs, "mark") == 0)
7654 if (!Marked.status)
7655 return 0;
7657 x = Marked.X;
7658 y = Marked.Y;
7660 else if (ys)
7662 x = GetValue (xs, units, NULL);
7663 y = GetValue (ys, units, NULL);
7665 else
7667 Message (_("Bad syntax for Import(setnewpoint)"));
7668 return 1;
7671 pcb_sprintf (buf, "%$ms", x);
7672 AttributePut (PCB, "import::newX", buf);
7673 pcb_sprintf (buf, "%$ms", y);
7674 AttributePut (PCB, "import::newY", buf);
7675 return 0;
7678 if (! mode)
7679 mode = AttributeGet (PCB, "import::mode");
7680 if (! mode)
7681 mode = "gnetlist";
7683 if (argc > 1)
7685 sources = argv + 1;
7686 nsources = argc - 1;
7689 if (! sources)
7691 char sname[40];
7692 char *src;
7694 nsources = -1;
7695 do {
7696 nsources ++;
7697 sprintf(sname, "import::src%d", nsources);
7698 src = AttributeGet (PCB, sname);
7699 } while (src);
7701 if (nsources > 0)
7703 sources = (char **) malloc ((nsources + 1) * sizeof (char *));
7704 nsources = -1;
7705 do {
7706 nsources ++;
7707 sprintf(sname, "import::src%d", nsources);
7708 src = AttributeGet (PCB, sname);
7709 sources[nsources] = src;
7710 } while (src);
7714 if (! sources)
7716 /* Replace .pcb with .sch and hope for the best. */
7717 char *pcbname = PCB->Filename;
7718 char *schname;
7719 char *dot, *slash, *bslash;
7721 if (!pcbname)
7722 return hid_action("ImportGUI");
7724 schname = (char *) malloc (strlen(pcbname) + 5);
7725 strcpy (schname, pcbname);
7726 dot = strchr (schname, '.');
7727 slash = strchr (schname, '/');
7728 bslash = strchr (schname, '\\');
7729 if (dot && slash && dot < slash)
7730 dot = NULL;
7731 if (dot && bslash && dot < bslash)
7732 dot = NULL;
7733 if (dot)
7734 *dot = 0;
7735 strcat (schname, ".sch");
7737 if (access (schname, F_OK))
7739 free (schname);
7740 return hid_action("ImportGUI");
7743 sources = (char **) malloc (2 * sizeof (char *));
7744 sources[0] = schname;
7745 sources[1] = NULL;
7746 nsources = 1;
7749 if (strcasecmp (mode, "gnetlist") == 0)
7751 char *tmpfile = tempfile_name_new ("gnetlist_output");
7752 char **cmd;
7753 int i;
7755 if (tmpfile == NULL) {
7756 Message (_("Could not create temp file"));
7757 return 1;
7760 cmd = (char **) malloc ((7 + nsources) * sizeof (char *));
7761 cmd[0] = Settings.GnetlistProgram;
7762 cmd[1] = "-g";
7763 cmd[2] = "pcbfwd";
7764 cmd[3] = "-o";
7765 cmd[4] = tmpfile;
7766 cmd[5] = "--";
7767 for (i=0; i<nsources; i++)
7768 cmd[6+i] = sources[i];
7769 cmd[6+nsources] = NULL;
7771 #ifdef DEBUG
7772 printf("ActionImport: =========== About to run gnetlist ============\n");
7773 printf("%s %s %s %s %s %s %s ...\n",
7774 cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6]);
7775 #endif
7777 if (pcb_spawnvp (cmd))
7779 unlink (tmpfile);
7780 return 1;
7783 #ifdef DEBUG
7784 printf("ActionImport: =========== About to run ActionExecuteFile, file = %s ============\n", tmpfile);
7785 #endif
7787 cmd[0] = tmpfile;
7788 cmd[1] = NULL;
7789 ActionExecuteFile (1, cmd, 0, 0);
7791 free (cmd);
7792 tempfile_unlink (tmpfile);
7794 else if (strcasecmp (mode, "make") == 0)
7796 int must_free_tmpfile = 0;
7797 char *tmpfile;
7798 char *cmd[10];
7799 int i;
7800 char *srclist;
7801 int srclen;
7802 char *user_outfile = NULL;
7803 char *user_makefile = NULL;
7804 char *user_target = NULL;
7807 user_outfile = AttributeGet (PCB, "import::outfile");
7808 user_makefile = AttributeGet (PCB, "import::makefile");
7809 user_target = AttributeGet (PCB, "import::target");
7810 if (user_outfile && !user_target)
7811 user_target = user_outfile;
7813 if (user_outfile)
7814 tmpfile = user_outfile;
7815 else
7817 tmpfile = tempfile_name_new ("gnetlist_output");
7818 if (tmpfile == NULL) {
7819 Message (_("Could not create temp file"));
7820 return 1;
7822 must_free_tmpfile = 1;
7825 srclen = sizeof("SRCLIST=") + 2;
7826 for (i=0; i<nsources; i++)
7827 srclen += strlen (sources[i]) + 2;
7828 srclist = (char *) malloc (srclen);
7829 strcpy (srclist, "SRCLIST=");
7830 for (i=0; i<nsources; i++)
7832 if (i)
7833 strcat (srclist, " ");
7834 strcat (srclist, sources[i]);
7837 cmd[0] = Settings.MakeProgram;
7838 cmd[1] = "-s";
7839 cmd[2] = Concat ("PCB=", PCB->Filename, NULL);
7840 cmd[3] = srclist;
7841 cmd[4] = Concat ("OUT=", tmpfile, NULL);
7842 i = 5;
7843 if (user_makefile)
7845 cmd[i++] = "-f";
7846 cmd[i++] = user_makefile;
7848 cmd[i++] = user_target ? user_target : (char *)"pcb_import";
7849 cmd[i++] = NULL;
7851 if (pcb_spawnvp (cmd))
7853 if (must_free_tmpfile)
7854 unlink (tmpfile);
7855 free (cmd[2]);
7856 free (cmd[3]);
7857 free (cmd[4]);
7858 return 1;
7861 cmd[0] = tmpfile;
7862 cmd[1] = NULL;
7863 ActionExecuteFile (1, cmd, 0, 0);
7865 free (cmd[2]);
7866 free (cmd[3]);
7867 free (cmd[4]);
7868 if (must_free_tmpfile)
7869 tempfile_unlink (tmpfile);
7871 else
7873 Message (_("Unknown import mode: %s\n"), mode);
7874 return 1;
7877 DeleteRats (false);
7878 AddAllRats (false, NULL);
7880 #ifdef DEBUG
7881 printf("ActionImport: =========== Leaving ActionImport ============\n");
7882 #endif
7884 return 0;
7887 /* ------------------------------------------------------------ */
7889 static const char attributes_syntax[] =
7890 "Attributes(Layout|Layer|Element)\n"
7891 "Attributes(Layer,layername)";
7893 static const char attributes_help[] =
7894 "Let the user edit the attributes of the layout, current or given\n"
7895 "layer, or selected element.";
7897 /* %start-doc actions Attributes
7899 This just pops up a dialog letting the user edit the attributes of the
7900 pcb, an element, or a layer.
7902 %end-doc */
7905 static int
7906 ActionAttributes (int argc, char **argv, Coord x, Coord y)
7908 char *function = ARG (0);
7909 char *layername = ARG (1);
7910 char *buf;
7912 if (!function)
7913 AFAIL (attributes);
7915 if (!gui->edit_attributes)
7917 Message (_("This GUI doesn't support Attribute Editing\n"));
7918 return 1;
7921 switch (GetFunctionID (function))
7923 case F_Layout:
7925 gui->edit_attributes("Layout Attributes", &(PCB->Attributes));
7926 return 0;
7929 case F_Layer:
7931 LayerType *layer = CURRENT;
7932 if (layername)
7934 int i;
7935 layer = NULL;
7936 for (i=0; i<max_copper_layer; i++)
7937 if (strcmp (PCB->Data->Layer[i].Name, layername) == 0)
7939 layer = & (PCB->Data->Layer[i]);
7940 break;
7942 if (layer == NULL)
7944 Message (_("No layer named %s\n"), layername);
7945 return 1;
7948 buf = (char *) malloc (strlen (layer->Name) + strlen ("Layer X Attributes"));
7949 sprintf (buf, "Layer %s Attributes", layer->Name);
7950 gui->edit_attributes(buf, &(layer->Attributes));
7951 free (buf);
7952 return 0;
7955 case F_Element:
7957 int n_found = 0;
7958 ElementType *e = NULL;
7959 ELEMENT_LOOP (PCB->Data);
7961 if (TEST_FLAG (SELECTEDFLAG, element))
7963 e = element;
7964 n_found ++;
7967 END_LOOP;
7968 if (n_found > 1)
7970 Message (_("Too many elements selected\n"));
7971 return 1;
7973 if (n_found == 0)
7975 void *ptrtmp;
7976 gui->get_coords (_("Click on an element"), &x, &y);
7977 if ((SearchScreen
7978 (x, y, ELEMENT_TYPE, &ptrtmp,
7979 &ptrtmp, &ptrtmp)) != NO_TYPE)
7980 e = (ElementType *) ptrtmp;
7981 else
7983 Message (_("No element found there\n"));
7984 return 1;
7988 if (NAMEONPCB_NAME(e))
7990 buf = (char *) malloc (strlen (NAMEONPCB_NAME(e)) + strlen ("Element X Attributes"));
7991 sprintf(buf, "Element %s Attributes", NAMEONPCB_NAME(e));
7993 else
7995 buf = strdup ("Unnamed Element Attributes");
7997 gui->edit_attributes(buf, &(e->Attributes));
7998 free (buf);
7999 break;
8002 default:
8003 AFAIL (attributes);
8006 return 0;
8009 /* --------------------------------------------------------------------------- */
8011 HID_Action action_action_list[] = {
8012 {"AddRats", 0, ActionAddRats,
8013 addrats_help, addrats_syntax}
8015 {"Attributes", 0, ActionAttributes,
8016 attributes_help, attributes_syntax}
8018 {"Atomic", 0, ActionAtomic,
8019 atomic_help, atomic_syntax}
8021 {"AutoPlaceSelected", 0, ActionAutoPlaceSelected,
8022 autoplace_help, autoplace_syntax}
8024 {"AutoRoute", 0, ActionAutoRoute,
8025 autoroute_help, autoroute_syntax}
8027 {"ChangeClearSize", 0, ActionChangeClearSize,
8028 changeclearsize_help, changeclearsize_syntax}
8030 {"ChangeDrillSize", 0, ActionChange2ndSize,
8031 changedrillsize_help, changedrillsize_syntax}
8033 {"ChangeHole", 0, ActionChangeHole,
8034 changehold_help, changehold_syntax}
8036 {"ChangeJoin", 0, ActionChangeJoin,
8037 changejoin_help, changejoin_syntax}
8039 {"ChangeName", 0, ActionChangeName,
8040 changename_help, changename_syntax}
8042 {"ChangePaste", 0, ActionChangePaste,
8043 changepaste_help, changepaste_syntax}
8045 {"ChangePinName", 0, ActionChangePinName,
8046 changepinname_help, changepinname_syntax}
8048 {"ChangeSize", 0, ActionChangeSize,
8049 changesize_help, changesize_syntax}
8051 {"ChangeSquare", 0, ActionChangeSquare,
8052 changesquare_help, changesquare_syntax}
8054 {"ChangeOctagon", 0, ActionChangeOctagon,
8055 changeoctagon_help, changeoctagon_syntax}
8057 {"ClearSquare", 0, ActionClearSquare,
8058 clearsquare_help, clearsquare_syntax}
8060 {"ClearOctagon", 0, ActionClearOctagon,
8061 clearoctagon_help, clearoctagon_syntax}
8063 {"Connection", 0, ActionConnection,
8064 connection_help, connection_syntax}
8066 {"Delete", 0, ActionDelete,
8067 delete_help, delete_syntax}
8069 {"DeleteRats", 0, ActionDeleteRats,
8070 deleterats_help, deleterats_syntax}
8072 {"DisperseElements", 0, ActionDisperseElements,
8073 disperseelements_help, disperseelements_syntax}
8075 {"Display", 0, ActionDisplay,
8076 display_help, display_syntax}
8078 {"DRC", 0, ActionDRCheck,
8079 drc_help, drc_syntax}
8081 {"DumpLibrary", 0, ActionDumpLibrary,
8082 dumplibrary_help, dumplibrary_syntax}
8084 {"ExecuteFile", 0, ActionExecuteFile,
8085 executefile_help, executefile_syntax}
8087 {"Flip", N_("Click on Object or Flip Point"), ActionFlip,
8088 flip_help, flip_syntax}
8090 {"LoadFrom", 0, ActionLoadFrom,
8091 loadfrom_help, loadfrom_syntax}
8093 {"MarkCrosshair", 0, ActionMarkCrosshair,
8094 markcrosshair_help, markcrosshair_syntax}
8096 {"Message", 0, ActionMessage,
8097 message_help, message_syntax}
8099 {"MinMaskGap", 0, ActionMinMaskGap,
8100 minmaskgap_help, minmaskgap_syntax}
8102 {"MinClearGap", 0, ActionMinClearGap,
8103 mincleargap_help, mincleargap_syntax}
8105 {"Mode", 0, ActionMode,
8106 mode_help, mode_syntax}
8108 {"MorphPolygon", 0, ActionMorphPolygon,
8109 morphpolygon_help, morphpolygon_syntax}
8111 {"PasteBuffer", 0, ActionPasteBuffer,
8112 pastebuffer_help, pastebuffer_syntax}
8114 {"Quit", 0, ActionQuit,
8115 quit_help, quit_syntax}
8117 {"RemoveSelected", 0, ActionRemoveSelected,
8118 removeselected_help, removeselected_syntax}
8120 {"Renumber", 0, ActionRenumber,
8121 renumber_help, renumber_syntax}
8123 {"RipUp", 0, ActionRipUp,
8124 ripup_help, ripup_syntax}
8126 {"Select", 0, ActionSelect,
8127 select_help, select_syntax}
8129 {"Unselect", 0, ActionUnselect,
8130 unselect_help, unselect_syntax}
8132 {"SaveSettings", 0, ActionSaveSettings,
8133 savesettings_help, savesettings_syntax}
8135 {"SaveTo", 0, ActionSaveTo,
8136 saveto_help, saveto_syntax}
8138 {"SetSquare", 0, ActionSetSquare,
8139 setsquare_help, setsquare_syntax}
8141 {"SetOctagon", 0, ActionSetOctagon,
8142 setoctagon_help, setoctagon_syntax}
8144 {"SetThermal", 0, ActionSetThermal,
8145 setthermal_help, setthermal_syntax}
8147 {"SetValue", 0, ActionSetValue,
8148 setvalue_help, setvalue_syntax}
8150 {"ToggleHideName", 0, ActionToggleHideName,
8151 togglehidename_help, togglehidename_syntax}
8153 {"Undo", 0, ActionUndo,
8154 undo_help, undo_syntax}
8156 {"Redo", 0, ActionRedo,
8157 redo_help, redo_syntax}
8159 {"SetSame", N_("Select item to use attributes from"), ActionSetSame,
8160 setsame_help, setsame_syntax}
8162 {"SetFlag", 0, ActionSetFlag,
8163 setflag_help, setflag_syntax}
8165 {"ClrFlag", 0, ActionClrFlag,
8166 clrflag_help, clrflag_syntax}
8168 {"ChangeFlag", 0, ActionChangeFlag,
8169 changeflag_help, changeflag_syntax}
8171 {"Polygon", 0, ActionPolygon,
8172 polygon_help, polygon_syntax}
8174 {"RouteStyle", 0, ActionRouteStyle,
8175 routestyle_help, routestyle_syntax}
8177 {"MoveObject", N_("Select an Object"), ActionMoveObject,
8178 moveobject_help, moveobject_syntax}
8180 {"MoveToCurrentLayer", 0, ActionMoveToCurrentLayer,
8181 movetocurrentlayer_help, movetocurrentlayer_syntax}
8183 {"New", 0, ActionNew,
8184 new_help, new_syntax}
8186 {"pscalib", 0, ActionPSCalib}
8188 {"ElementList", 0, ActionElementList,
8189 elementlist_help, elementlist_syntax}
8191 {"ElementSetAttr", 0, ActionElementSetAttr,
8192 elementsetattr_help, elementsetattr_syntax}
8194 {"ExecCommand", 0, ActionExecCommand,
8195 execcommand_help, execcommand_syntax}
8197 {"Import", 0, ActionImport,
8198 import_help, import_syntax}
8202 REGISTER_ACTIONS (action_action_list)