(no commit message)
[geda-pcb/pcjc2.git] / src / action.c
blob8cd389ce72ab9a3ddd5e79944a058619d3d1da20
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 GetLayerGroupNumberBySide (BOTTOM_SIDE))
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[] = N_("Atomic(Save|Restore|Close|Block)");
1770 static const char atomic_help[] = N_("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[] = N_("DRC()");
1835 static const char drc_help[] = N_("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[] = N_("DumpLibrary()");
1876 static const char dumplibrary_help[] =
1877 N_("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[] = N_("Flip(Object|Selected|SelectedElements)");
1930 static const char flip_help[] =
1931 N_("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[] = N_("Message(message)");
1984 static const char message_help[] = N_("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 N_("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 N_("SetValue(Grid|Line|LineSize|Text|TextScale|ViaDrillingHole|Via|ViaSize, "
2125 "delta)");
2127 static const char setvalue_help[] =
2128 N_("Change various board-wide values and sizes.");
2130 /* %start-doc actions SetValue
2132 @table @code
2134 @item ViaDrillingHole
2135 Changes the diameter of the drill for new vias.
2137 @item Grid
2138 Sets the grid spacing.
2140 @item Line
2141 @item LineSize
2142 Changes the thickness of new lines.
2144 @item Via
2145 @item ViaSize
2146 Changes the diameter of new vias.
2148 @item Text
2149 @item TextScale
2150 Changes the size of new text.
2152 @end table
2154 %end-doc */
2156 static int
2157 ActionSetValue (int argc, char **argv, Coord x, Coord y)
2159 char *function = ARG (0);
2160 char *val = ARG (1);
2161 char *units = ARG (2);
2162 bool absolute; /* flag for 'absolute' value */
2163 double value;
2164 int text_scale;
2165 int err = 0;
2167 if (function && val)
2169 value = GetValue (val, units, &absolute);
2170 switch (GetFunctionID (function))
2172 case F_ViaDrillingHole:
2173 SetViaDrillingHole (absolute ? value :
2174 value + Settings.ViaDrillingHole,
2175 false);
2176 hid_action ("RouteStylesChanged");
2177 break;
2179 case F_Grid:
2180 if (absolute)
2181 SetGrid (value, false);
2182 else
2184 if (value == 0)
2185 value = val[0] == '-' ? -Settings.increments->grid
2186 : Settings.increments->grid;
2187 /* On the way down, short against the minimum
2188 * PCB drawing unit */
2189 if ((value + PCB->Grid) < 1)
2190 SetGrid (1, false);
2191 else if (PCB->Grid == 1)
2192 SetGrid (value, false);
2193 else
2194 SetGrid (value + PCB->Grid, false);
2196 break;
2198 case F_LineSize:
2199 case F_Line:
2200 if (!absolute && value == 0)
2201 value = val[0] == '-' ? -Settings.increments->line
2202 : Settings.increments->line;
2203 SetLineSize (absolute ? value : value + Settings.LineThickness);
2204 hid_action ("RouteStylesChanged");
2205 break;
2207 case F_Via:
2208 case F_ViaSize:
2209 SetViaSize (absolute ? value : value + Settings.ViaThickness, false);
2210 hid_action ("RouteStylesChanged");
2211 break;
2213 case F_Text:
2214 case F_TextScale:
2215 text_scale = value / (double)FONT_CAPHEIGHT * 100.;
2216 if (!absolute)
2217 text_scale += Settings.TextScale;
2218 SetTextScale (text_scale);
2219 break;
2220 default:
2221 err = 1;
2222 break;
2224 if (!err)
2225 return 0;
2228 AFAIL (setvalue);
2232 /* --------------------------------------------------------------------------- */
2234 static const char quit_syntax[] = N_("Quit()");
2236 static const char quit_help[] = N_("Quits the application after confirming.");
2238 /* %start-doc actions Quit
2240 If you have unsaved changes, you will be prompted to confirm (or
2241 save) before quitting.
2243 %end-doc */
2245 static int
2246 ActionQuit (int argc, char **argv, Coord x, Coord y)
2248 char *force = ARG (0);
2249 if (force && strcasecmp (force, "force") == 0)
2251 PCB->Changed = 0;
2252 exit (0);
2254 if (!PCB->Changed || gui->close_confirm_dialog () == HID_CLOSE_CONFIRM_OK)
2255 QuitApplication ();
2256 return 1;
2259 /* --------------------------------------------------------------------------- */
2261 static const char connection_syntax[] =
2262 N_("Connection(Find|ResetLinesAndPolygons|ResetPinsAndVias|Reset)");
2264 static const char connection_help[] =
2265 N_("Searches connections of the object at the cursor position.");
2267 /* %start-doc actions Connection
2269 Connections found with this action will be highlighted in the
2270 ``connected-color'' color and will have the ``found'' flag set.
2272 @table @code
2274 @item Find
2275 The net under the cursor is ``found''.
2277 @item ResetLinesAndPolygons
2278 Any ``found'' lines and polygons are marked ``not found''.
2280 @item ResetPinsAndVias
2281 Any ``found'' pins and vias are marked ``not found''.
2283 @item Reset
2284 All ``found'' objects are marked ``not found''.
2286 @end table
2288 %end-doc */
2290 static int
2291 ActionConnection (int argc, char **argv, Coord x, Coord y)
2293 char *function = ARG (0);
2294 if (function)
2296 switch (GetFunctionID (function))
2298 case F_Find:
2300 gui->get_coords (_("Click on a connection"), &x, &y);
2301 LookupConnection (x, y, true, 1, CONNECTEDFLAG, false);
2302 LookupConnection (x, y, true, 1, FOUNDFLAG, true);
2303 break;
2306 case F_ResetLinesAndPolygons:
2307 if (ClearFlagOnLinesAndPolygons (true, CONNECTEDFLAG | FOUNDFLAG))
2309 IncrementUndoSerialNumber ();
2310 Draw ();
2312 break;
2314 case F_ResetPinsViasAndPads:
2315 if (ClearFlagOnPinsViasAndPads (true, CONNECTEDFLAG | FOUNDFLAG))
2317 IncrementUndoSerialNumber ();
2318 Draw ();
2320 break;
2322 case F_Reset:
2323 if (ClearFlagOnAllObjects (true, CONNECTEDFLAG | FOUNDFLAG))
2325 IncrementUndoSerialNumber ();
2326 Draw ();
2328 break;
2330 return 0;
2333 AFAIL (connection);
2336 /* --------------------------------------------------------------------------- */
2338 static const char disperseelements_syntax[] =
2339 N_("DisperseElements(All|Selected)");
2341 static const char disperseelements_help[] = N_("Disperses elements.");
2343 /* %start-doc actions DisperseElements
2345 Normally this is used when starting a board, by selecting all elements
2346 and then dispersing them. This scatters the elements around the board
2347 so that you can pick individual ones, rather than have all the
2348 elements at the same 0,0 coordinate and thus impossible to choose
2349 from.
2351 %end-doc */
2353 #define GAP MIL_TO_COORD(100)
2355 static int
2356 ActionDisperseElements (int argc, char **argv, Coord x, Coord y)
2358 char *function = ARG (0);
2359 Coord minx = GAP,
2360 miny = GAP,
2361 maxy = GAP,
2362 dx, dy;
2363 int all = 0, bad = 0;
2365 if (!function || !*function)
2367 bad = 1;
2369 else
2371 switch (GetFunctionID (function))
2373 case F_All:
2374 all = 1;
2375 break;
2377 case F_Selected:
2378 all = 0;
2379 break;
2381 default:
2382 bad = 1;
2386 if (bad)
2388 AFAIL (disperseelements);
2392 ELEMENT_LOOP (PCB->Data);
2395 * If we want to disperse selected elements, maybe we need smarter
2396 * code here to avoid putting components on top of others which
2397 * are not selected. For now, I'm assuming that this is typically
2398 * going to be used either with a brand new design or a scratch
2399 * design holding some new components
2401 if (!TEST_FLAG (LOCKFLAG, element) && (all || TEST_FLAG (SELECTEDFLAG, element)))
2404 /* figure out how much to move the element */
2405 dx = minx - element->BoundingBox.X1;
2407 /* snap to the grid */
2408 dx -= (element->MarkX + dx) % PCB->Grid;
2411 * and add one grid size so we make sure we always space by GAP or
2412 * more
2414 dx += PCB->Grid;
2416 /* Figure out if this row has room. If not, start a new row */
2417 if (GAP + element->BoundingBox.X2 + dx > PCB->MaxWidth)
2419 miny = maxy + GAP;
2420 minx = GAP;
2423 /* figure out how much to move the element */
2424 dx = minx - element->BoundingBox.X1;
2425 dy = miny - element->BoundingBox.Y1;
2427 /* snap to the grid */
2428 dx -= (element->MarkX + dx) % PCB->Grid;
2429 dx += PCB->Grid;
2430 dy -= (element->MarkY + dy) % PCB->Grid;
2431 dy += PCB->Grid;
2433 /* move the element */
2434 MoveElementLowLevel (PCB->Data, element, dx, dy);
2436 /* and add to the undo list so we can undo this operation */
2437 AddObjectToMoveUndoList (ELEMENT_TYPE, NULL, NULL, element, dx, dy);
2439 /* keep track of how tall this row is */
2440 minx += element->BoundingBox.X2 - element->BoundingBox.X1 + GAP;
2441 if (maxy < element->BoundingBox.Y2)
2443 maxy = element->BoundingBox.Y2;
2448 END_LOOP;
2450 /* done with our action so increment the undo # */
2451 IncrementUndoSerialNumber ();
2453 Redraw ();
2454 SetChangedFlag (true);
2456 return 0;
2459 #undef GAP
2461 /* --------------------------------------------------------------------------- */
2463 static const char display_syntax[] =
2464 N_("Display(NameOnPCB|Description|Value)\n"
2465 "Display(Grid|Redraw)\n"
2466 "Display(CycleClip|CycleCrosshair|Toggle45Degree|ToggleStartDirection)\n"
2467 "Display(ToggleGrid|ToggleRubberBandMode|ToggleUniqueNames)\n"
2468 "Display(ToggleMask|ToggleName|ToggleClearLine|ToggleFullPoly|ToggleSnapPin)\n"
2469 "Display(ToggleThindraw|ToggleThindrawPoly|ToggleOrthoMove|ToggleLocalRef)\n"
2470 "Display(ToggleCheckPlanes|ToggleShowDRC|ToggleAutoDRC)\n"
2471 "Display(ToggleLiveRoute|LockNames|OnlyNames)\n"
2472 "Display(Pinout|PinOrPadName)");
2474 static const char display_help[] = N_("Several display-related actions.");
2476 /* %start-doc actions Display
2478 @table @code
2480 @item NameOnPCB
2481 @item Description
2482 @item Value
2483 Specify whether all elements show their name, description, or value.
2485 @item Redraw
2486 Redraw the whole board.
2488 @item Toggle45Degree
2489 When clear, lines can be drawn at any angle. When set, lines are
2490 restricted to multiples of 45 degrees and requested lines may be
2491 broken up according to the clip setting.
2493 @item CycleClip
2494 Changes the way lines are restricted to 45 degree increments. The
2495 various settings are: straight only, orthogonal then angled, and angled
2496 then orthogonal. If AllDirections is set, this action disables it.
2498 @item CycleCrosshair
2499 Changes crosshair drawing. Crosshair may accept form of 4-ray,
2500 8-ray and 12-ray cross.
2502 @item ToggleRubberBandMode
2503 If set, moving an object moves all the lines attached to it too.
2505 @item ToggleStartDirection
2506 If set, each time you set a point in a line, the Clip toggles between
2507 orth-angle and angle-ortho.
2509 @item ToggleUniqueNames
2510 If set, you will not be permitted to change the name of an element to
2511 match that of another element.
2513 @item ToggleSnapPin
2514 If set, pin centers and pad end points are treated as additional grid
2515 points that the cursor can snap to.
2517 @item ToggleLocalRef
2518 If set, the mark is automatically set to the beginning of any move, so
2519 you can see the relative distance you've moved.
2521 @item ToggleThindraw
2522 If set, objects on the screen are drawn as outlines (lines are drawn
2523 as center-lines). This lets you see line endpoints hidden under pins,
2524 for example.
2526 @item ToggleThindrawPoly
2527 If set, polygons on the screen are drawn as outlines.
2529 @item ToggleShowDRC
2530 If set, pending objects (i.e. lines you're in the process of drawing)
2531 will be drawn with an outline showing how far away from other copper
2532 you need to be.
2534 @item ToggleLiveRoute
2535 If set, the progress of the autorouter will be visible on the screen.
2537 @item ToggleAutoDRC
2538 If set, you will not be permitted to make connections which violate
2539 the current DRC and netlist settings.
2541 @item ToggleCheckPlanes
2542 If set, lines and arcs aren't drawn, which usually leaves just the
2543 polygons. If you also disable all but the layer you're interested in,
2544 this allows you to check for isolated regions.
2546 @item ToggleOrthoMove
2547 If set, the crosshair is only allowed to move orthogonally from its
2548 previous position. I.e. you can move an element or line up, down,
2549 left, or right, but not up+left or down+right.
2551 @item ToggleName
2552 Selects whether the pinouts show the pin names or the pin numbers.
2554 @item ToggleLockNames
2555 If set, text will ignore left mouse clicks and actions that work on
2556 objects under the mouse. You can still select text with a lasso (left
2557 mouse drag) and perform actions on the selection.
2559 @item ToggleOnlyNames
2560 If set, only text will be sensitive for mouse clicks and actions that
2561 work on objects under the mouse. You can still select other objects
2562 with a lasso (left mouse drag) and perform actions on the selection.
2564 @item ToggleMask
2565 Turns the solder mask on or off.
2567 @item ToggleClearLine
2568 When set, the clear-line flag causes new lines and arcs to have their
2569 ``clear polygons'' flag set, so they won't be electrically connected
2570 to any polygons they overlap.
2572 @item ToggleFullPoly
2573 When set, the full-poly flag causes new polygons to have their
2574 ``full polygon'' flag set, so all parts of them will be displayed
2575 instead of only the biggest one.
2577 @item ToggleGrid
2578 Resets the origin of the current grid to be wherever the mouse pointer
2579 is (not where the crosshair currently is). If you provide two numbers
2580 after this, the origin is set to that coordinate.
2582 @item Grid
2583 Toggles whether the grid is displayed or not.
2585 @item Pinout
2586 Causes the pinout of the element indicated by the cursor to be
2587 displayed, usually in a separate window.
2589 @item PinOrPadName
2590 Toggles whether the names of pins, pads, or (yes) vias will be
2591 displayed. If the cursor is over an element, all of its pins and pads
2592 are affected.
2594 @end table
2596 %end-doc */
2598 static enum crosshair_shape
2599 CrosshairShapeIncrement (enum crosshair_shape shape)
2601 switch(shape)
2603 case Basic_Crosshair_Shape:
2604 shape = Union_Jack_Crosshair_Shape;
2605 break;
2606 case Union_Jack_Crosshair_Shape:
2607 shape = Dozen_Crosshair_Shape;
2608 break;
2609 case Dozen_Crosshair_Shape:
2610 shape = Crosshair_Shapes_Number;
2611 break;
2612 case Crosshair_Shapes_Number:
2613 shape = Basic_Crosshair_Shape;
2614 break;
2616 return shape;
2619 static int
2620 ActionDisplay (int argc, char **argv, Coord childX, Coord childY)
2622 char *function, *str_dir;
2623 int id;
2624 int err = 0;
2626 function = ARG (0);
2627 str_dir = ARG (1);
2629 if (function && (!str_dir || !*str_dir))
2631 switch (id = GetFunctionID (function))
2634 /* redraw layout */
2635 case F_ClearAndRedraw:
2636 case F_Redraw:
2637 Redraw ();
2638 break;
2640 /* change the displayed name of elements */
2641 case F_Value:
2642 case F_NameOnPCB:
2643 case F_Description:
2644 ELEMENT_LOOP (PCB->Data);
2646 EraseElementName (element);
2648 END_LOOP;
2649 CLEAR_FLAG (DESCRIPTIONFLAG | NAMEONPCBFLAG, PCB);
2650 switch (id)
2652 case F_Value:
2653 break;
2654 case F_NameOnPCB:
2655 SET_FLAG (NAMEONPCBFLAG, PCB);
2656 break;
2657 case F_Description:
2658 SET_FLAG (DESCRIPTIONFLAG, PCB);
2659 break;
2661 ELEMENT_LOOP (PCB->Data);
2663 DrawElementName (element);
2665 END_LOOP;
2666 Draw ();
2667 break;
2669 /* toggle line-adjust flag */
2670 case F_ToggleAllDirections:
2671 TOGGLE_FLAG (ALLDIRECTIONFLAG, PCB);
2672 AdjustAttachedObjects ();
2673 break;
2675 case F_CycleClip:
2676 notify_crosshair_change (false);
2677 if (TEST_FLAG (ALLDIRECTIONFLAG, PCB))
2679 TOGGLE_FLAG (ALLDIRECTIONFLAG, PCB);
2680 PCB->Clipping = 0;
2682 else
2683 PCB->Clipping = (PCB->Clipping + 1) % 3;
2684 AdjustAttachedObjects ();
2685 notify_crosshair_change (true);
2686 break;
2688 case F_CycleCrosshair:
2689 notify_crosshair_change (false);
2690 Crosshair.shape = CrosshairShapeIncrement(Crosshair.shape);
2691 if (Crosshair_Shapes_Number == Crosshair.shape)
2692 Crosshair.shape = Basic_Crosshair_Shape;
2693 notify_crosshair_change (true);
2694 break;
2696 case F_ToggleRubberBandMode:
2697 notify_crosshair_change (false);
2698 TOGGLE_FLAG (RUBBERBANDFLAG, PCB);
2699 notify_crosshair_change (true);
2700 break;
2702 case F_ToggleStartDirection:
2703 notify_crosshair_change (false);
2704 TOGGLE_FLAG (SWAPSTARTDIRFLAG, PCB);
2705 notify_crosshair_change (true);
2706 break;
2708 case F_ToggleUniqueNames:
2709 TOGGLE_FLAG (UNIQUENAMEFLAG, PCB);
2710 break;
2712 case F_ToggleSnapPin:
2713 notify_crosshair_change (false);
2714 TOGGLE_FLAG (SNAPPINFLAG, PCB);
2715 notify_crosshair_change (true);
2716 break;
2718 case F_ToggleLocalRef:
2719 TOGGLE_FLAG (LOCALREFFLAG, PCB);
2720 break;
2722 case F_ToggleThindraw:
2723 TOGGLE_FLAG (THINDRAWFLAG, PCB);
2724 Redraw ();
2725 break;
2727 case F_ToggleThindrawPoly:
2728 TOGGLE_FLAG (THINDRAWPOLYFLAG, PCB);
2729 Redraw ();
2730 break;
2732 case F_ToggleLockNames:
2733 TOGGLE_FLAG (LOCKNAMESFLAG, PCB);
2734 CLEAR_FLAG (ONLYNAMESFLAG, PCB);
2735 break;
2737 case F_ToggleOnlyNames:
2738 TOGGLE_FLAG (ONLYNAMESFLAG, PCB);
2739 CLEAR_FLAG (LOCKNAMESFLAG, PCB);
2740 break;
2742 case F_ToggleHideNames:
2743 TOGGLE_FLAG (HIDENAMESFLAG, PCB);
2744 Redraw ();
2745 break;
2747 case F_ToggleShowDRC:
2748 TOGGLE_FLAG (SHOWDRCFLAG, PCB);
2749 break;
2751 case F_ToggleLiveRoute:
2752 TOGGLE_FLAG (LIVEROUTEFLAG, PCB);
2753 break;
2755 case F_ToggleAutoDRC:
2756 notify_crosshair_change (false);
2757 TOGGLE_FLAG (AUTODRCFLAG, PCB);
2758 if (TEST_FLAG (AUTODRCFLAG, PCB) && Settings.Mode == LINE_MODE)
2760 if (ClearFlagOnAllObjects (true, CONNECTEDFLAG | FOUNDFLAG))
2762 IncrementUndoSerialNumber ();
2763 Draw ();
2765 if (Crosshair.AttachedLine.State != STATE_FIRST)
2767 LookupConnection (Crosshair.AttachedLine.Point1.X,
2768 Crosshair.AttachedLine.Point1.Y,
2769 true, 1, CONNECTEDFLAG, false);
2770 LookupConnection (Crosshair.AttachedLine.Point1.X,
2771 Crosshair.AttachedLine.Point1.Y,
2772 true, 1, FOUNDFLAG, true);
2775 notify_crosshair_change (true);
2776 break;
2778 case F_ToggleCheckPlanes:
2779 TOGGLE_FLAG (CHECKPLANESFLAG, PCB);
2780 Redraw ();
2781 break;
2783 case F_ToggleOrthoMove:
2784 TOGGLE_FLAG (ORTHOMOVEFLAG, PCB);
2785 break;
2787 case F_ToggleName:
2788 TOGGLE_FLAG (SHOWNUMBERFLAG, PCB);
2789 Redraw ();
2790 break;
2792 case F_ToggleMask:
2793 TOGGLE_FLAG (SHOWMASKFLAG, PCB);
2794 Redraw ();
2795 break;
2797 case F_ToggleClearLine:
2798 TOGGLE_FLAG (CLEARNEWFLAG, PCB);
2799 break;
2801 case F_ToggleFullPoly:
2802 TOGGLE_FLAG (NEWFULLPOLYFLAG, PCB);
2803 break;
2805 /* shift grid alignment */
2806 case F_ToggleGrid:
2808 Coord oldGrid = PCB->Grid;
2810 PCB->Grid = 1;
2811 if (MoveCrosshairAbsolute (Crosshair.X, Crosshair.Y))
2812 notify_crosshair_change (true); /* first notify was in MoveCrosshairAbs */
2813 SetGrid (oldGrid, true);
2815 break;
2817 /* toggle displaying of the grid */
2818 case F_Grid:
2819 Settings.DrawGrid = !Settings.DrawGrid;
2820 Redraw ();
2821 break;
2823 /* display the pinout of an element */
2824 case F_Pinout:
2826 ElementType *element;
2827 void *ptrtmp;
2828 Coord x, y;
2830 gui->get_coords (_("Click on an element"), &x, &y);
2831 if ((SearchScreen
2832 (x, y, ELEMENT_TYPE, &ptrtmp,
2833 &ptrtmp, &ptrtmp)) != NO_TYPE)
2835 element = (ElementType *) ptrtmp;
2836 gui->show_item (element);
2838 break;
2841 /* toggle displaying of pin/pad/via names */
2842 case F_PinOrPadName:
2844 void *ptr1, *ptr2, *ptr3;
2846 switch (SearchScreen (Crosshair.X, Crosshair.Y,
2847 ELEMENT_TYPE | PIN_TYPE | PAD_TYPE |
2848 VIA_TYPE, (void **) &ptr1, (void **) &ptr2,
2849 (void **) &ptr3))
2851 case ELEMENT_TYPE:
2852 PIN_LOOP ((ElementType *) ptr1);
2854 if (TEST_FLAG (DISPLAYNAMEFLAG, pin))
2855 ErasePinName (pin);
2856 else
2857 DrawPinName (pin);
2858 AddObjectToFlagUndoList (PIN_TYPE, ptr1, pin, pin);
2859 TOGGLE_FLAG (DISPLAYNAMEFLAG, pin);
2861 END_LOOP;
2862 PAD_LOOP ((ElementType *) ptr1);
2864 if (TEST_FLAG (DISPLAYNAMEFLAG, pad))
2865 ErasePadName (pad);
2866 else
2867 DrawPadName (pad);
2868 AddObjectToFlagUndoList (PAD_TYPE, ptr1, pad, pad);
2869 TOGGLE_FLAG (DISPLAYNAMEFLAG, pad);
2871 END_LOOP;
2872 SetChangedFlag (true);
2873 IncrementUndoSerialNumber ();
2874 Draw ();
2875 break;
2877 case PIN_TYPE:
2878 if (TEST_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2))
2879 ErasePinName ((PinType *) ptr2);
2880 else
2881 DrawPinName ((PinType *) ptr2);
2882 AddObjectToFlagUndoList (PIN_TYPE, ptr1, ptr2, ptr3);
2883 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2);
2884 SetChangedFlag (true);
2885 IncrementUndoSerialNumber ();
2886 Draw ();
2887 break;
2889 case PAD_TYPE:
2890 if (TEST_FLAG (DISPLAYNAMEFLAG, (PadType *) ptr2))
2891 ErasePadName ((PadType *) ptr2);
2892 else
2893 DrawPadName ((PadType *) ptr2);
2894 AddObjectToFlagUndoList (PAD_TYPE, ptr1, ptr2, ptr3);
2895 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PadType *) ptr2);
2896 SetChangedFlag (true);
2897 IncrementUndoSerialNumber ();
2898 Draw ();
2899 break;
2900 case VIA_TYPE:
2901 if (TEST_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2))
2902 EraseViaName ((PinType *) ptr2);
2903 else
2904 DrawViaName ((PinType *) ptr2);
2905 AddObjectToFlagUndoList (VIA_TYPE, ptr1, ptr2, ptr3);
2906 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2);
2907 SetChangedFlag (true);
2908 IncrementUndoSerialNumber ();
2909 Draw ();
2910 break;
2912 break;
2914 default:
2915 err = 1;
2918 else if (function && str_dir)
2920 switch (GetFunctionID (function))
2922 case F_ToggleGrid:
2923 if (argc > 2)
2925 PCB->GridOffsetX = GetValue (argv[1], NULL, NULL);
2926 PCB->GridOffsetY = GetValue (argv[2], NULL, NULL);
2927 if (Settings.DrawGrid)
2928 Redraw ();
2930 break;
2932 default:
2933 err = 1;
2934 break;
2938 if (!err)
2939 return 0;
2941 AFAIL (display);
2944 /* --------------------------------------------------------------------------- */
2946 static const char mode_syntax[] =
2947 N_("Mode(Arc|Arrow|Copy|InsertPoint|Line|Lock|Move|None|PasteBuffer)\n"
2948 "Mode(Polygon|Rectangle|Remove|Rotate|Text|Thermal|Via)\n"
2949 "Mode(Notify|Release|Cancel|Stroke)\n"
2950 "Mode(Save|Restore)");
2952 static const char mode_help[] = N_("Change or use the tool mode.");
2954 /* %start-doc actions Mode
2956 @table @code
2958 @item Arc
2959 @itemx Arrow
2960 @itemx Copy
2961 @itemx InsertPoint
2962 @itemx Line
2963 @itemx Lock
2964 @itemx Move
2965 @itemx None
2966 @itemx PasteBuffer
2967 @itemx Polygon
2968 @itemx Rectangle
2969 @itemx Remove
2970 @itemx Rotate
2971 @itemx Text
2972 @itemx Thermal
2973 @itemx Via
2974 Select the indicated tool.
2976 @item Notify
2977 Called when you press the mouse button, or move the mouse.
2979 @item Release
2980 Called when you release the mouse button.
2982 @item Cancel
2983 Cancels any pending tool activity, allowing you to restart elsewhere.
2984 For example, this allows you to start a new line rather than attach a
2985 line to the previous line.
2987 @item Escape
2988 Similar to Cancel but calling this action a second time will return
2989 to the Arrow tool.
2991 @item Stroke
2992 If your @code{pcb} was built with libstroke, this invokes the stroke
2993 input method. If not, this will restart a drawing mode if you were
2994 drawing, else it will select objects.
2996 @item Save
2997 Remembers the current tool.
2999 @item Restore
3000 Restores the tool to the last saved tool.
3002 @end table
3004 %end-doc */
3006 static int
3007 ActionMode (int argc, char **argv, Coord x, Coord y)
3009 char *function = ARG (0);
3011 if (function)
3013 Note.X = Crosshair.X;
3014 Note.Y = Crosshair.Y;
3015 notify_crosshair_change (false);
3016 switch (GetFunctionID (function))
3018 case F_Arc:
3019 SetMode (ARC_MODE);
3020 break;
3021 case F_Arrow:
3022 SetMode (ARROW_MODE);
3023 break;
3024 case F_Copy:
3025 SetMode (COPY_MODE);
3026 break;
3027 case F_InsertPoint:
3028 SetMode (INSERTPOINT_MODE);
3029 break;
3030 case F_Line:
3031 SetMode (LINE_MODE);
3032 break;
3033 case F_Lock:
3034 SetMode (LOCK_MODE);
3035 break;
3036 case F_Move:
3037 SetMode (MOVE_MODE);
3038 break;
3039 case F_None:
3040 SetMode (NO_MODE);
3041 break;
3042 case F_Cancel:
3044 int saved_mode = Settings.Mode;
3045 SetMode (NO_MODE);
3046 SetMode (saved_mode);
3048 break;
3049 case F_Escape:
3051 switch (Settings.Mode)
3053 case VIA_MODE:
3054 case PASTEBUFFER_MODE:
3055 case TEXT_MODE:
3056 case ROTATE_MODE:
3057 case REMOVE_MODE:
3058 case MOVE_MODE:
3059 case COPY_MODE:
3060 case INSERTPOINT_MODE:
3061 case RUBBERBANDMOVE_MODE:
3062 case THERMAL_MODE:
3063 case LOCK_MODE:
3064 SetMode (NO_MODE);
3065 SetMode (ARROW_MODE);
3066 break;
3068 case LINE_MODE:
3069 if (Crosshair.AttachedLine.State == STATE_FIRST)
3070 SetMode (ARROW_MODE);
3071 else
3073 SetMode (NO_MODE);
3074 SetMode (LINE_MODE);
3076 break;
3078 case RECTANGLE_MODE:
3079 if (Crosshair.AttachedBox.State == STATE_FIRST)
3080 SetMode (ARROW_MODE);
3081 else
3083 SetMode (NO_MODE);
3084 SetMode (RECTANGLE_MODE);
3086 break;
3088 case POLYGON_MODE:
3089 if (Crosshair.AttachedLine.State == STATE_FIRST)
3090 SetMode (ARROW_MODE);
3091 else
3093 SetMode (NO_MODE);
3094 SetMode (POLYGON_MODE);
3096 break;
3098 case POLYGONHOLE_MODE:
3099 if (Crosshair.AttachedLine.State == STATE_FIRST)
3100 SetMode (ARROW_MODE);
3101 else
3103 SetMode (NO_MODE);
3104 SetMode (POLYGONHOLE_MODE);
3106 break;
3108 case ARC_MODE:
3109 if (Crosshair.AttachedBox.State == STATE_FIRST)
3110 SetMode (ARROW_MODE);
3111 else
3113 SetMode (NO_MODE);
3114 SetMode (ARC_MODE);
3116 break;
3118 case ARROW_MODE:
3119 break;
3121 default:
3122 break;
3125 break;
3127 case F_Notify:
3128 NotifyMode ();
3129 break;
3130 case F_PasteBuffer:
3131 SetMode (PASTEBUFFER_MODE);
3132 break;
3133 case F_Polygon:
3134 SetMode (POLYGON_MODE);
3135 break;
3136 case F_PolygonHole:
3137 SetMode (POLYGONHOLE_MODE);
3138 break;
3139 #ifndef HAVE_LIBSTROKE
3140 case F_Release:
3141 ReleaseMode ();
3142 break;
3143 #else
3144 case F_Release:
3145 if (mid_stroke)
3146 FinishStroke ();
3147 else
3148 ReleaseMode ();
3149 break;
3150 #endif
3151 case F_Remove:
3152 SetMode (REMOVE_MODE);
3153 break;
3154 case F_Rectangle:
3155 SetMode (RECTANGLE_MODE);
3156 break;
3157 case F_Rotate:
3158 SetMode (ROTATE_MODE);
3159 break;
3160 case F_Stroke:
3161 #ifdef HAVE_LIBSTROKE
3162 mid_stroke = true;
3163 StrokeBox.X1 = Crosshair.X;
3164 StrokeBox.Y1 = Crosshair.Y;
3165 break;
3166 #else
3167 /* Handle middle mouse button restarts of drawing mode. If not in
3168 | a drawing mode, middle mouse button will select objects.
3170 if (Settings.Mode == LINE_MODE
3171 && Crosshair.AttachedLine.State != STATE_FIRST)
3173 SetMode (LINE_MODE);
3175 else if (Settings.Mode == ARC_MODE
3176 && Crosshair.AttachedBox.State != STATE_FIRST)
3177 SetMode (ARC_MODE);
3178 else if (Settings.Mode == RECTANGLE_MODE
3179 && Crosshair.AttachedBox.State != STATE_FIRST)
3180 SetMode (RECTANGLE_MODE);
3181 else if (Settings.Mode == POLYGON_MODE
3182 && Crosshair.AttachedLine.State != STATE_FIRST)
3183 SetMode (POLYGON_MODE);
3184 else
3186 SaveMode ();
3187 saved_mode = true;
3188 SetMode (ARROW_MODE);
3189 NotifyMode ();
3191 break;
3192 #endif
3193 case F_Text:
3194 SetMode (TEXT_MODE);
3195 break;
3196 case F_Thermal:
3197 SetMode (THERMAL_MODE);
3198 break;
3199 case F_Via:
3200 SetMode (VIA_MODE);
3201 break;
3203 case F_Restore: /* restore the last saved mode */
3204 RestoreMode ();
3205 break;
3207 case F_Save: /* save currently selected mode */
3208 SaveMode ();
3209 break;
3211 notify_crosshair_change (true);
3212 return 0;
3215 AFAIL (mode);
3218 /* --------------------------------------------------------------------------- */
3220 static const char removeselected_syntax[] = N_("RemoveSelected()");
3222 static const char removeselected_help[] = N_("Removes any selected objects.");
3224 /* %start-doc actions RemoveSelected
3226 %end-doc */
3228 static int
3229 ActionRemoveSelected (int argc, char **argv, Coord x, Coord y)
3231 if (RemoveSelected ())
3232 SetChangedFlag (true);
3233 return 0;
3236 /* --------------------------------------------------------------------------- */
3238 static const char renumber_syntax[] = N_("Renumber()\n"
3239 "Renumber(filename)");
3241 static const char renumber_help[] =
3242 N_("Renumber all elements. The changes will be recorded to filename\n"
3243 "for use in backannotating these changes to the schematic.");
3245 /* %start-doc actions Renumber
3247 %end-doc */
3249 static int
3250 ActionRenumber (int argc, char **argv, Coord x, Coord y)
3252 bool changed = false;
3253 ElementType **element_list;
3254 ElementType **locked_element_list;
3255 unsigned int i, j, k, cnt, lock_cnt;
3256 unsigned int tmpi;
3257 size_t sz;
3258 char *tmps;
3259 char *name;
3260 FILE *out;
3261 static char * default_file = NULL;
3262 size_t cnt_list_sz = 100;
3263 struct _cnt_list
3265 char *name;
3266 unsigned int cnt;
3267 } *cnt_list;
3268 char **was, **is, *pin;
3269 unsigned int c_cnt = 0;
3270 int unique, ok;
3271 int free_name = 0;
3273 if (argc < 1)
3276 * We deal with the case where name already exists in this
3277 * function so the GUI doesn't need to deal with it
3279 name = gui->fileselect (_("Save Renumber Annotation File As ..."),
3280 _("Choose a file to record the renumbering to.\n"
3281 "This file may be used to back annotate the\n"
3282 "change to the schematics.\n"),
3283 default_file, ".eco", "eco",
3286 free_name = 1;
3288 else
3289 name = argv[0];
3291 if (default_file)
3293 free (default_file);
3294 default_file = NULL;
3297 if (name && *name)
3299 default_file = strdup (name);
3302 if ((out = fopen (name, "r")))
3304 fclose (out);
3305 if (!gui->confirm_dialog (_("File exists! Ok to overwrite?"), 0))
3307 if (free_name && name)
3308 free (name);
3309 return 0;
3313 if ((out = fopen (name, "w")) == NULL)
3315 Message (_("Could not open %s\n"), name);
3316 if (free_name && name)
3317 free (name);
3318 return 1;
3321 if (free_name && name)
3322 free (name);
3324 fprintf (out, "*COMMENT* PCB Annotation File\n");
3325 fprintf (out, "*FILEVERSION* 20061031\n");
3328 * Make a first pass through all of the elements and sort them out
3329 * by location on the board. While here we also collect a list of
3330 * locked elements.
3332 * We'll actually renumber things in the 2nd pass.
3334 element_list = (ElementType **)calloc (PCB->Data->ElementN, sizeof (ElementType *));
3335 locked_element_list = (ElementType **)calloc (PCB->Data->ElementN, sizeof (ElementType *));
3336 was = (char **)calloc (PCB->Data->ElementN, sizeof (char *));
3337 is = (char **)calloc (PCB->Data->ElementN, sizeof (char *));
3338 if (element_list == NULL || locked_element_list == NULL || was == NULL
3339 || is == NULL)
3341 fprintf (stderr, "calloc() failed in %s\n", __FUNCTION__);
3342 exit (1);
3346 cnt = 0;
3347 lock_cnt = 0;
3348 ELEMENT_LOOP (PCB->Data);
3350 if (TEST_FLAG (LOCKFLAG, element->Name) || TEST_FLAG (LOCKFLAG, element))
3353 * add to the list of locked elements which we won't try to
3354 * renumber and whose reference designators are now reserved.
3356 pcb_fprintf (out,
3357 "*WARN* Element \"%s\" at %$md is locked and will not be renumbered.\n",
3358 UNKNOWN (NAMEONPCB_NAME (element)), element->MarkX, element->MarkY);
3359 locked_element_list[lock_cnt] = element;
3360 lock_cnt++;
3363 else
3365 /* count of devices which will be renumbered */
3366 cnt++;
3368 /* search for correct position in the list */
3369 i = 0;
3370 while (element_list[i] && element->MarkY > element_list[i]->MarkY)
3371 i++;
3374 * We have found the position where we have the first element that
3375 * has the same Y value or a lower Y value. Now move forward if
3376 * needed through the X values
3378 while (element_list[i]
3379 && element->MarkY == element_list[i]->MarkY
3380 && element->MarkX > element_list[i]->MarkX)
3381 i++;
3383 for (j = cnt - 1; j > i; j--)
3385 element_list[j] = element_list[j - 1];
3387 element_list[i] = element;
3390 END_LOOP;
3394 * Now that the elements are sorted by board position, we go through
3395 * and renumber them.
3399 * turn off the flag which requires unique names so it doesn't get
3400 * in our way. When we're done with the renumber we will have unique
3401 * names.
3403 unique = TEST_FLAG (UNIQUENAMEFLAG, PCB);
3404 CLEAR_FLAG (UNIQUENAMEFLAG, PCB);
3406 cnt_list = (struct _cnt_list *)calloc (cnt_list_sz, sizeof (struct _cnt_list));
3407 for (i = 0; i < cnt; i++)
3409 /* If there is no refdes, maybe just spit out a warning */
3410 if (NAMEONPCB_NAME (element_list[i]))
3412 /* figure out the prefix */
3413 tmps = strdup (NAMEONPCB_NAME (element_list[i]));
3414 j = 0;
3415 while (tmps[j] && (tmps[j] < '0' || tmps[j] > '9')
3416 && tmps[j] != '?')
3417 j++;
3418 tmps[j] = '\0';
3420 /* check the counter for this prefix */
3421 for (j = 0;
3422 cnt_list[j].name && (strcmp (cnt_list[j].name, tmps) != 0)
3423 && j < cnt_list_sz; j++);
3425 /* grow the list if needed */
3426 if (j == cnt_list_sz)
3428 cnt_list_sz += 100;
3429 cnt_list = (struct _cnt_list *)realloc (cnt_list, cnt_list_sz);
3430 if (cnt_list == NULL)
3432 fprintf (stderr, _("realloc failed() in %s\n"), __FUNCTION__);
3433 exit (1);
3435 /* zero out the memory that we added */
3436 for (tmpi = j; tmpi < cnt_list_sz; tmpi++)
3438 cnt_list[tmpi].name = NULL;
3439 cnt_list[tmpi].cnt = 0;
3444 * start a new counter if we don't have a counter for this
3445 * prefix
3447 if (!cnt_list[j].name)
3449 cnt_list[j].name = strdup (tmps);
3450 cnt_list[j].cnt = 0;
3454 * check to see if the new refdes is already used by a
3455 * locked element
3459 ok = 1;
3460 cnt_list[j].cnt++;
3461 free (tmps);
3463 /* space for the prefix plus 1 digit plus the '\0' */
3464 sz = strlen (cnt_list[j].name) + 2;
3466 /* and 1 more per extra digit needed to hold the number */
3467 tmpi = cnt_list[j].cnt;
3468 while (tmpi > 10)
3470 sz++;
3471 tmpi = tmpi / 10;
3473 tmps = (char *)malloc (sz * sizeof (char));
3474 sprintf (tmps, "%s%d", cnt_list[j].name, cnt_list[j].cnt);
3477 * now compare to the list of reserved (by locked
3478 * elements) names
3480 for (k = 0; k < lock_cnt; k++)
3482 if (strcmp
3483 (UNKNOWN (NAMEONPCB_NAME (locked_element_list[k])),
3484 tmps) == 0)
3486 ok = 0;
3487 break;
3492 while (!ok);
3494 if (strcmp (tmps, NAMEONPCB_NAME (element_list[i])) != 0)
3496 fprintf (out, "*RENAME* \"%s\" \"%s\"\n",
3497 NAMEONPCB_NAME (element_list[i]), tmps);
3499 /* add this rename to our table of renames so we can update the netlist */
3500 was[c_cnt] = strdup (NAMEONPCB_NAME (element_list[i]));
3501 is[c_cnt] = strdup (tmps);
3502 c_cnt++;
3504 AddObjectToChangeNameUndoList (ELEMENT_TYPE, NULL, NULL,
3505 element_list[i],
3506 NAMEONPCB_NAME (element_list
3507 [i]));
3509 ChangeObjectName (ELEMENT_TYPE, element_list[i], NULL, NULL,
3510 tmps);
3511 changed = true;
3513 /* we don't free tmps in this case because it is used */
3515 else
3516 free (tmps);
3518 else
3520 pcb_fprintf (out, "*WARN* Element at %$md has no name.\n",
3521 element_list[i]->MarkX, element_list[i]->MarkY);
3526 fclose (out);
3528 /* restore the unique flag setting */
3529 if (unique)
3530 SET_FLAG (UNIQUENAMEFLAG, PCB);
3532 if (changed)
3535 /* update the netlist */
3536 AddNetlistLibToUndoList (&(PCB->NetlistLib));
3538 /* iterate over each net */
3539 for (i = 0; i < PCB->NetlistLib.MenuN; i++)
3542 /* iterate over each pin on the net */
3543 for (j = 0; j < PCB->NetlistLib.Menu[i].EntryN; j++)
3546 /* figure out the pin number part from strings like U3-21 */
3547 tmps = strdup (PCB->NetlistLib.Menu[i].Entry[j].ListEntry);
3548 for (k = 0; tmps[k] && tmps[k] != '-'; k++);
3549 tmps[k] = '\0';
3550 pin = tmps + k + 1;
3552 /* iterate over the list of changed reference designators */
3553 for (k = 0; k < c_cnt; k++)
3556 * if the pin needs to change, change it and quit
3557 * searching in the list.
3559 if (strcmp (tmps, was[k]) == 0)
3561 free (PCB->NetlistLib.Menu[i].Entry[j].ListEntry);
3562 PCB->NetlistLib.Menu[i].Entry[j].ListEntry =
3563 (char *)malloc ((strlen (is[k]) + strlen (pin) +
3564 2) * sizeof (char));
3565 sprintf (PCB->NetlistLib.Menu[i].Entry[j].ListEntry,
3566 "%s-%s", is[k], pin);
3567 k = c_cnt;
3571 free (tmps);
3574 for (k = 0; k < c_cnt; k++)
3576 free (was[k]);
3577 free (is[k]);
3580 NetlistChanged (0);
3581 IncrementUndoSerialNumber ();
3582 SetChangedFlag (true);
3585 free (locked_element_list);
3586 free (element_list);
3587 free (cnt_list);
3588 return 0;
3592 /* --------------------------------------------------------------------------- */
3594 static const char ripup_syntax[] = N_("RipUp(All|Selected|Element)");
3596 static const char ripup_help[] =
3597 N_("Ripup auto-routed tracks, or convert an element to parts.");
3599 /* %start-doc actions RipUp
3601 @table @code
3603 @item All
3604 Removes all lines and vias which were created by the autorouter.
3606 @item Selected
3607 Removes all selected lines and vias which were created by the
3608 autorouter.
3610 @item Element
3611 Converts the element under the cursor to parts (vias and lines). Note
3612 that this uses the highest numbered paste buffer.
3614 @end table
3616 %end-doc */
3618 static int
3619 ActionRipUp (int argc, char **argv, Coord x, Coord y)
3621 char *function = ARG (0);
3622 bool changed = false;
3624 if (function)
3626 switch (GetFunctionID (function))
3628 case F_All:
3629 ALLLINE_LOOP (PCB->Data);
3631 if (TEST_FLAG (AUTOFLAG, line) && !TEST_FLAG (LOCKFLAG, line))
3633 RemoveObject (LINE_TYPE, layer, line, line);
3634 changed = true;
3637 ENDALL_LOOP;
3638 ALLARC_LOOP (PCB->Data);
3640 if (TEST_FLAG (AUTOFLAG, arc) && !TEST_FLAG (LOCKFLAG, arc))
3642 RemoveObject (ARC_TYPE, layer, arc, arc);
3643 changed = true;
3646 ENDALL_LOOP;
3647 VIA_LOOP (PCB->Data);
3649 if (TEST_FLAG (AUTOFLAG, via) && !TEST_FLAG (LOCKFLAG, via))
3651 RemoveObject (VIA_TYPE, via, via, via);
3652 changed = true;
3655 END_LOOP;
3657 if (changed)
3659 IncrementUndoSerialNumber ();
3660 SetChangedFlag (true);
3662 break;
3663 case F_Selected:
3664 VISIBLELINE_LOOP (PCB->Data);
3666 if (TEST_FLAGS (AUTOFLAG | SELECTEDFLAG, line)
3667 && !TEST_FLAG (LOCKFLAG, line))
3669 RemoveObject (LINE_TYPE, layer, line, line);
3670 changed = true;
3673 ENDALL_LOOP;
3674 if (PCB->ViaOn)
3675 VIA_LOOP (PCB->Data);
3677 if (TEST_FLAGS (AUTOFLAG | SELECTEDFLAG, via)
3678 && !TEST_FLAG (LOCKFLAG, via))
3680 RemoveObject (VIA_TYPE, via, via, via);
3681 changed = true;
3684 END_LOOP;
3685 if (changed)
3687 IncrementUndoSerialNumber ();
3688 SetChangedFlag (true);
3690 break;
3691 case F_Element:
3693 void *ptr1, *ptr2, *ptr3;
3695 if (SearchScreen (Crosshair.X, Crosshair.Y, ELEMENT_TYPE,
3696 &ptr1, &ptr2, &ptr3) != NO_TYPE)
3698 Note.Buffer = Settings.BufferNumber;
3699 SetBufferNumber (MAX_BUFFER - 1);
3700 ClearBuffer (PASTEBUFFER);
3701 CopyObjectToBuffer (PASTEBUFFER->Data, PCB->Data,
3702 ELEMENT_TYPE, ptr1, ptr2, ptr3);
3703 SmashBufferElement (PASTEBUFFER);
3704 PASTEBUFFER->X = 0;
3705 PASTEBUFFER->Y = 0;
3706 SaveUndoSerialNumber ();
3707 EraseObject (ELEMENT_TYPE, ptr1, ptr1);
3708 MoveObjectToRemoveUndoList (ELEMENT_TYPE, ptr1, ptr2, ptr3);
3709 RestoreUndoSerialNumber ();
3710 CopyPastebufferToLayout (0, 0);
3711 SetBufferNumber (Note.Buffer);
3712 SetChangedFlag (true);
3715 break;
3718 return 0;
3721 /* --------------------------------------------------------------------------- */
3723 static const char addrats_syntax[] = N_("AddRats(AllRats|SelectedRats|Close)");
3725 static const char addrats_help[] =
3726 N_("Add one or more rat lines to the board.");
3728 /* %start-doc actions AddRats
3730 @table @code
3732 @item AllRats
3733 Create rat lines for all loaded nets that aren't already connected on
3734 with copper.
3736 @item SelectedRats
3737 Similarly, but only add rat lines for nets connected to selected pins
3738 and pads.
3740 @item Close
3741 Selects the shortest unselected rat on the board.
3743 @end table
3745 %end-doc */
3747 static int
3748 ActionAddRats (int argc, char **argv, Coord x, Coord y)
3750 char *function = ARG (0);
3751 RatType *shorty;
3752 float len, small;
3754 if (function)
3756 if (Settings.RatWarn)
3757 ClearWarnings ();
3758 switch (GetFunctionID (function))
3760 case F_AllRats:
3761 if (AddAllRats (false, NULL))
3762 SetChangedFlag (true);
3763 break;
3764 case F_SelectedRats:
3765 case F_Selected:
3766 if (AddAllRats (true, NULL))
3767 SetChangedFlag (true);
3768 break;
3769 case F_Close:
3770 small = SQUARE (MAX_COORD);
3771 shorty = NULL;
3772 RAT_LOOP (PCB->Data);
3774 if (TEST_FLAG (SELECTEDFLAG, line))
3775 continue;
3776 len = SQUARE (line->Point1.X - line->Point2.X) +
3777 SQUARE (line->Point1.Y - line->Point2.Y);
3778 if (len < small)
3780 small = len;
3781 shorty = line;
3784 END_LOOP;
3785 if (shorty)
3787 AddObjectToFlagUndoList (RATLINE_TYPE, shorty, shorty, shorty);
3788 SET_FLAG (SELECTEDFLAG, shorty);
3789 DrawRat (shorty);
3790 Draw ();
3791 CenterDisplay ((shorty->Point2.X + shorty->Point1.X) / 2,
3792 (shorty->Point2.Y + shorty->Point1.Y) / 2);
3794 break;
3797 return 0;
3800 /* --------------------------------------------------------------------------- */
3802 static const char delete_syntax[] =
3803 N_("Delete(Object|Selected)\n"
3804 "Delete(AllRats|SelectedRats)");
3806 static const char delete_help[] = N_("Delete stuff.");
3808 /* %start-doc actions Delete
3810 %end-doc */
3812 static int
3813 ActionDelete (int argc, char **argv, Coord x, Coord y)
3815 char *function = ARG (0);
3816 int id = GetFunctionID (function);
3818 Note.X = Crosshair.X;
3819 Note.Y = Crosshair.Y;
3821 if (id == -1) /* no arg */
3823 if (RemoveSelected() == false)
3824 id = F_Object;
3827 switch (id)
3829 case F_Object:
3830 SaveMode();
3831 SetMode(REMOVE_MODE);
3832 NotifyMode();
3833 RestoreMode();
3834 break;
3835 case F_Selected:
3836 RemoveSelected();
3837 break;
3838 case F_AllRats:
3839 if (DeleteRats (false))
3840 SetChangedFlag (true);
3841 break;
3842 case F_SelectedRats:
3843 if (DeleteRats (true))
3844 SetChangedFlag (true);
3845 break;
3848 return 0;
3851 /* --------------------------------------------------------------------------- */
3853 static const char deleterats_syntax[] =
3854 N_("DeleteRats(AllRats|Selected|SelectedRats)");
3856 static const char deleterats_help[] = N_("Delete rat lines.");
3858 /* %start-doc actions DeleteRats
3860 %end-doc */
3862 static int
3863 ActionDeleteRats (int argc, char **argv, Coord x, Coord y)
3865 char *function = ARG (0);
3866 if (function)
3868 if (Settings.RatWarn)
3869 ClearWarnings ();
3870 switch (GetFunctionID (function))
3872 case F_AllRats:
3873 if (DeleteRats (false))
3874 SetChangedFlag (true);
3875 break;
3876 case F_SelectedRats:
3877 case F_Selected:
3878 if (DeleteRats (true))
3879 SetChangedFlag (true);
3880 break;
3883 return 0;
3886 /* --------------------------------------------------------------------------- */
3888 static const char autoplace_syntax[] = N_("AutoPlaceSelected()");
3890 static const char autoplace_help[] = N_("Auto-place selected components.");
3892 /* %start-doc actions AutoPlaceSelected
3894 Attempts to re-arrange the selected components such that the nets
3895 connecting them are minimized. Note that you cannot undo this.
3897 %end-doc */
3899 static int
3900 ActionAutoPlaceSelected (int argc, char **argv, Coord x, Coord y)
3902 hid_action("Busy");
3903 if (gui->confirm_dialog (_("Auto-placement can NOT be undone.\n"
3904 "Do you want to continue anyway?\n"), 0))
3906 if (AutoPlaceSelected ())
3907 SetChangedFlag (true);
3909 return 0;
3912 /* --------------------------------------------------------------------------- */
3914 static const char autoroute_syntax[] = N_("AutoRoute(AllRats|SelectedRats)");
3916 static const char autoroute_help[] = N_("Auto-route some or all rat lines.");
3918 /* %start-doc actions AutoRoute
3920 @table @code
3922 @item AllRats
3923 Attempt to autoroute all rats.
3925 @item SelectedRats
3926 Attempt to autoroute the selected rats.
3928 @end table
3930 Before autorouting, it's important to set up a few things. First,
3931 make sure any layers you aren't using are disabled, else the
3932 autorouter may use them. Next, make sure the current line and via
3933 styles are set accordingly. Last, make sure "new lines clear
3934 polygons" is set, in case you eventually want to add a copper pour.
3936 Autorouting takes a while. During this time, the program may not be
3937 responsive.
3939 %end-doc */
3941 static int
3942 ActionAutoRoute (int argc, char **argv, Coord x, Coord y)
3944 char *function = ARG (0);
3945 hid_action("Busy");
3946 if (function) /* one parameter */
3948 switch (GetFunctionID (function))
3950 case F_AllRats:
3951 if (AutoRoute (false))
3952 SetChangedFlag (true);
3953 break;
3954 case F_SelectedRats:
3955 case F_Selected:
3956 if (AutoRoute (true))
3957 SetChangedFlag (true);
3958 break;
3961 return 0;
3964 /* --------------------------------------------------------------------------- */
3966 static const char markcrosshair_syntax[] =
3967 N_("MarkCrosshair()\n"
3968 "MarkCrosshair(Center)");
3970 static const char markcrosshair_help[] = N_("Set/Reset the Crosshair mark.");
3972 /* %start-doc actions MarkCrosshair
3974 The ``mark'' is a small X-shaped target on the display which is
3975 treated like a second origin (the normal origin is the upper let
3976 corner of the board). The GUI will display a second set of
3977 coordinates for this mark, which tells you how far you are from it.
3979 If no argument is given, the mark is toggled - disabled if it was
3980 enabled, or enabled at the current cursor position of disabled. If
3981 the @code{Center} argument is given, the mark is moved to the current
3982 cursor location.
3984 %end-doc */
3986 static int
3987 ActionMarkCrosshair (int argc, char **argv, Coord x, Coord y)
3989 char *function = ARG (0);
3990 if (!function || !*function)
3992 if (Marked.status)
3994 notify_mark_change (false);
3995 Marked.status = false;
3996 notify_mark_change (true);
3998 else
4000 notify_mark_change (false);
4001 Marked.status = false;
4002 Marked.status = true;
4003 Marked.X = Crosshair.X;
4004 Marked.Y = Crosshair.Y;
4005 notify_mark_change (true);
4008 else if (GetFunctionID (function) == F_Center)
4010 notify_mark_change (false);
4011 Marked.status = true;
4012 Marked.X = Crosshair.X;
4013 Marked.Y = Crosshair.Y;
4014 notify_mark_change (true);
4016 return 0;
4019 /* --------------------------------------------------------------------------- */
4021 static const char changesize_syntax[] =
4022 N_("ChangeSize(Object, delta)\n"
4023 "ChangeSize(SelectedObjects|Selected, delta)\n"
4024 "ChangeSize(SelectedLines|SelectedPins|SelectedVias, delta)\n"
4025 "ChangeSize(SelectedPads|SelectedTexts|SelectedNames, delta)\n"
4026 "ChangeSize(SelectedElements, delta)");
4028 static const char changesize_help[] = N_("Changes the size of objects.");
4030 /* %start-doc actions ChangeSize
4032 For lines and arcs, this changes the width. For pins and vias, this
4033 changes the overall diameter of the copper annulus. For pads, this
4034 changes the width and, indirectly, the length. For texts and names,
4035 this changes the scaling factor. For elements, this changes the width
4036 of the silk layer lines and arcs for this element.
4038 %end-doc */
4040 static int
4041 ActionChangeSize (int argc, char **argv, Coord x, Coord y)
4043 char *function = ARG (0);
4044 char *delta = ARG (1);
4045 char *units = ARG (2);
4046 bool absolute; /* indicates if absolute size is given */
4047 Coord value;
4049 if (function && delta)
4051 value = GetValue (delta, units, &absolute);
4052 if (value == 0)
4053 value = delta[0] == '-' ? -Settings.increments->size
4054 : Settings.increments->size;
4055 switch (GetFunctionID (function))
4057 case F_Object:
4059 int type;
4060 void *ptr1, *ptr2, *ptr3;
4062 if ((type =
4063 SearchScreen (Crosshair.X, Crosshair.Y, CHANGESIZE_TYPES,
4064 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4065 if (TEST_FLAG (LOCKFLAG, (PinType *) ptr2))
4066 Message (_("Sorry, the object is locked\n"));
4067 if (ChangeObjectSize (type, ptr1, ptr2, ptr3, value, absolute))
4068 SetChangedFlag (true);
4069 break;
4072 case F_SelectedVias:
4073 if (ChangeSelectedSize (VIA_TYPE, value, absolute))
4074 SetChangedFlag (true);
4075 break;
4077 case F_SelectedPins:
4078 if (ChangeSelectedSize (PIN_TYPE, value, absolute))
4079 SetChangedFlag (true);
4080 break;
4082 case F_SelectedPads:
4083 if (ChangeSelectedSize (PAD_TYPE, value, absolute))
4084 SetChangedFlag (true);
4085 break;
4087 case F_SelectedArcs:
4088 if (ChangeSelectedSize (ARC_TYPE, value, absolute))
4089 SetChangedFlag (true);
4090 break;
4092 case F_SelectedLines:
4093 if (ChangeSelectedSize (LINE_TYPE, value, absolute))
4094 SetChangedFlag (true);
4095 break;
4097 case F_SelectedTexts:
4098 if (ChangeSelectedSize (TEXT_TYPE, value, absolute))
4099 SetChangedFlag (true);
4100 break;
4102 case F_SelectedNames:
4103 if (ChangeSelectedSize (ELEMENTNAME_TYPE, value, absolute))
4104 SetChangedFlag (true);
4105 break;
4107 case F_SelectedElements:
4108 if (ChangeSelectedSize (ELEMENT_TYPE, value, absolute))
4109 SetChangedFlag (true);
4110 break;
4112 case F_Selected:
4113 case F_SelectedObjects:
4114 if (ChangeSelectedSize (CHANGESIZE_TYPES, value, absolute))
4115 SetChangedFlag (true);
4116 break;
4119 return 0;
4122 /* --------------------------------------------------------------------------- */
4124 static const char changedrillsize_syntax[] =
4125 N_("ChangeDrillSize(Object, delta)\n"
4126 "ChangeDrillSize(SelectedPins|SelectedVias|Selected|SelectedObjects, delta)");
4128 static const char changedrillsize_help[] =
4129 N_("Changes the drilling hole size of objects.");
4131 /* %start-doc actions ChangeDrillSize
4133 %end-doc */
4135 static int
4136 ActionChange2ndSize (int argc, char **argv, Coord x, Coord y)
4138 char *function = ARG (0);
4139 char *delta = ARG (1);
4140 char *units = ARG (2);
4141 bool absolute;
4142 Coord value;
4144 if (function && delta)
4146 value = GetValue (delta, units, &absolute);
4147 switch (GetFunctionID (function))
4149 case F_Object:
4151 int type;
4152 void *ptr1, *ptr2, *ptr3;
4154 gui->get_coords (_("Select an Object"), &x, &y);
4155 if ((type =
4156 SearchScreen (x, y, CHANGE2NDSIZE_TYPES,
4157 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4158 if (ChangeObject2ndSize
4159 (type, ptr1, ptr2, ptr3, value, absolute, true))
4160 SetChangedFlag (true);
4161 break;
4164 case F_SelectedVias:
4165 if (ChangeSelected2ndSize (VIA_TYPE, value, absolute))
4166 SetChangedFlag (true);
4167 break;
4169 case F_SelectedPins:
4170 if (ChangeSelected2ndSize (PIN_TYPE, value, absolute))
4171 SetChangedFlag (true);
4172 break;
4173 case F_Selected:
4174 case F_SelectedObjects:
4175 if (ChangeSelected2ndSize (PIN_TYPES, value, absolute))
4176 SetChangedFlag (true);
4177 break;
4180 return 0;
4183 /* --------------------------------------------------------------------------- */
4185 static const char changeclearsize_syntax[] =
4186 N_("ChangeClearSize(Object, delta)\n"
4187 "ChangeClearSize(SelectedPins|SelectedPads|SelectedVias, delta)\n"
4188 "ChangeClearSize(SelectedLines|SelectedArcs, delta\n"
4189 "ChangeClearSize(Selected|SelectedObjects, delta)");
4191 static const char changeclearsize_help[] =
4192 N_("Changes the clearance size of objects.");
4194 /* %start-doc actions ChangeClearSize
4196 If the solder mask is currently showing, this action changes the
4197 solder mask clearance. If the mask is not showing, this action
4198 changes the polygon clearance.
4200 %end-doc */
4202 static int
4203 ActionChangeClearSize (int argc, char **argv, Coord x, Coord y)
4205 char *function = ARG (0);
4206 char *delta = ARG (1);
4207 char *units = ARG (2);
4208 bool absolute;
4209 Coord value;
4211 if (function && delta)
4213 value = 2 * GetValue (delta, units, &absolute);
4214 if (value == 0)
4215 value = delta[0] == '-' ? -Settings.increments->clear
4216 : Settings.increments->clear;
4217 switch (GetFunctionID (function))
4219 case F_Object:
4221 int type;
4222 void *ptr1, *ptr2, *ptr3;
4224 gui->get_coords (_("Select an Object"), &x, &y);
4225 if ((type =
4226 SearchScreen (x, y,
4227 CHANGECLEARSIZE_TYPES, &ptr1, &ptr2,
4228 &ptr3)) != NO_TYPE)
4229 if (ChangeObjectClearSize (type, ptr1, ptr2, ptr3, value, absolute))
4230 SetChangedFlag (true);
4231 break;
4233 case F_SelectedVias:
4234 if (ChangeSelectedClearSize (VIA_TYPE, value, absolute))
4235 SetChangedFlag (true);
4236 break;
4237 case F_SelectedPads:
4238 if (ChangeSelectedClearSize (PAD_TYPE, value, absolute))
4239 SetChangedFlag (true);
4240 break;
4241 case F_SelectedPins:
4242 if (ChangeSelectedClearSize (PIN_TYPE, value, absolute))
4243 SetChangedFlag (true);
4244 break;
4245 case F_SelectedLines:
4246 if (ChangeSelectedClearSize (LINE_TYPE, value, absolute))
4247 SetChangedFlag (true);
4248 break;
4249 case F_SelectedArcs:
4250 if (ChangeSelectedClearSize (ARC_TYPE, value, absolute))
4251 SetChangedFlag (true);
4252 break;
4253 case F_Selected:
4254 case F_SelectedObjects:
4255 if (ChangeSelectedClearSize (CHANGECLEARSIZE_TYPES, value, absolute))
4256 SetChangedFlag (true);
4257 break;
4260 return 0;
4263 /* --------------------------------------------------------------------------- */
4265 static const char minmaskgap_syntax[] =
4266 N_("MinMaskGap(delta)\n"
4267 "MinMaskGap(Selected, delta)");
4269 static const char minmaskgap_help[] =
4270 N_("Ensures the mask is a minimum distance from pins and pads.");
4272 /* %start-doc actions MinMaskGap
4274 Checks all specified pins and/or pads, and increases the mask if
4275 needed to ensure a minimum distance between the pin or pad edge and
4276 the mask edge.
4278 %end-doc */
4280 static int
4281 ActionMinMaskGap (int argc, char **argv, Coord x, Coord y)
4283 char *function = ARG (0);
4284 char *delta = ARG (1);
4285 char *units = ARG (2);
4286 bool absolute;
4287 Coord value;
4288 int flags;
4290 if (!function)
4291 return 1;
4292 if (strcasecmp (function, "Selected") == 0)
4293 flags = SELECTEDFLAG;
4294 else
4296 units = delta;
4297 delta = function;
4298 flags = 0;
4300 value = 2 * GetValue (delta, units, &absolute);
4302 SaveUndoSerialNumber ();
4303 ELEMENT_LOOP (PCB->Data);
4305 PIN_LOOP (element);
4307 if (!TEST_FLAGS (flags, pin))
4308 continue;
4309 if (pin->Mask < pin->Thickness + value)
4311 ChangeObjectMaskSize (PIN_TYPE, element, pin, 0,
4312 pin->Thickness + value, 1);
4313 RestoreUndoSerialNumber ();
4316 END_LOOP;
4317 PAD_LOOP (element);
4319 if (!TEST_FLAGS (flags, pad))
4320 continue;
4321 if (pad->Mask < pad->Thickness + value)
4323 ChangeObjectMaskSize (PAD_TYPE, element, pad, 0,
4324 pad->Thickness + value, 1);
4325 RestoreUndoSerialNumber ();
4328 END_LOOP;
4330 END_LOOP;
4331 VIA_LOOP (PCB->Data);
4333 if (!TEST_FLAGS (flags, via))
4334 continue;
4335 if (via->Mask && via->Mask < via->Thickness + value)
4337 ChangeObjectMaskSize (VIA_TYPE, via, 0, 0, via->Thickness + value, 1);
4338 RestoreUndoSerialNumber ();
4341 END_LOOP;
4342 RestoreUndoSerialNumber ();
4343 IncrementUndoSerialNumber ();
4344 return 0;
4347 /* --------------------------------------------------------------------------- */
4349 static const char mincleargap_syntax[] =
4350 N_("MinClearGap(delta)\n"
4351 "MinClearGap(Selected, delta)");
4353 static const char mincleargap_help[] =
4354 N_("Ensures that polygons are a minimum distance from objects.");
4356 /* %start-doc actions MinClearGap
4358 Checks all specified objects, and increases the polygon clearance if
4359 needed to ensure a minimum distance between their edges and the
4360 polygon edges.
4362 %end-doc */
4364 static int
4365 ActionMinClearGap (int argc, char **argv, Coord x, Coord y)
4367 char *function = ARG (0);
4368 char *delta = ARG (1);
4369 char *units = ARG (2);
4370 bool absolute;
4371 Coord value;
4372 int flags;
4374 if (!function)
4375 return 1;
4376 if (strcasecmp (function, "Selected") == 0)
4377 flags = SELECTEDFLAG;
4378 else
4380 units = delta;
4381 delta = function;
4382 flags = 0;
4384 value = 2 * GetValue (delta, units, &absolute);
4386 SaveUndoSerialNumber ();
4387 ELEMENT_LOOP (PCB->Data);
4389 PIN_LOOP (element);
4391 if (!TEST_FLAGS (flags, pin))
4392 continue;
4393 if (pin->Clearance < value)
4395 ChangeObjectClearSize (PIN_TYPE, element, pin, 0,
4396 value, 1);
4397 RestoreUndoSerialNumber ();
4400 END_LOOP;
4401 PAD_LOOP (element);
4403 if (!TEST_FLAGS (flags, pad))
4404 continue;
4405 if (pad->Clearance < value)
4407 ChangeObjectClearSize (PAD_TYPE, element, pad, 0,
4408 value, 1);
4409 RestoreUndoSerialNumber ();
4412 END_LOOP;
4414 END_LOOP;
4415 VIA_LOOP (PCB->Data);
4417 if (!TEST_FLAGS (flags, via))
4418 continue;
4419 if (via->Clearance < value)
4421 ChangeObjectClearSize (VIA_TYPE, via, 0, 0, value, 1);
4422 RestoreUndoSerialNumber ();
4425 END_LOOP;
4426 ALLLINE_LOOP (PCB->Data);
4428 if (!TEST_FLAGS (flags, line))
4429 continue;
4430 if (line->Clearance < value)
4432 ChangeObjectClearSize (LINE_TYPE, layer, line, 0, value, 1);
4433 RestoreUndoSerialNumber ();
4436 ENDALL_LOOP;
4437 ALLARC_LOOP (PCB->Data);
4439 if (!TEST_FLAGS (flags, arc))
4440 continue;
4441 if (arc->Clearance < value)
4443 ChangeObjectClearSize (ARC_TYPE, layer, arc, 0, value, 1);
4444 RestoreUndoSerialNumber ();
4447 ENDALL_LOOP;
4448 RestoreUndoSerialNumber ();
4449 IncrementUndoSerialNumber ();
4450 return 0;
4453 /* --------------------------------------------------------------------------- */
4455 static const char changepinname_syntax[] =
4456 N_("ChangePinName(ElementName,PinNumber,PinName)");
4458 static const char changepinname_help[] =
4459 N_("Sets the name of a specific pin on a specific element.");
4461 /* %start-doc actions ChangePinName
4463 This can be especially useful for annotating pin names from a
4464 schematic to the layout without requiring knowledge of the pcb file
4465 format.
4467 @example
4468 ChangePinName(U3, 7, VCC)
4469 @end example
4471 %end-doc */
4473 static int
4474 ActionChangePinName (int argc, char **argv, Coord x, Coord y)
4476 int changed = 0;
4477 char *refdes, *pinnum, *pinname;
4479 if (argc != 3)
4481 AFAIL (changepinname);
4484 refdes = argv[0];
4485 pinnum = argv[1];
4486 pinname = argv[2];
4488 ELEMENT_LOOP (PCB->Data);
4490 if (NSTRCMP (refdes, NAMEONPCB_NAME (element)) == 0)
4492 PIN_LOOP (element);
4494 if (NSTRCMP (pinnum, pin->Number) == 0)
4496 AddObjectToChangeNameUndoList (PIN_TYPE, NULL, NULL,
4497 pin, pin->Name);
4499 * Note: we can't free() pin->Name first because
4500 * it is used in the undo list
4502 pin->Name = strdup (pinname);
4503 SetChangedFlag (true);
4504 changed = 1;
4507 END_LOOP;
4509 PAD_LOOP (element);
4511 if (NSTRCMP (pinnum, pad->Number) == 0)
4513 AddObjectToChangeNameUndoList (PAD_TYPE, NULL, NULL,
4514 pad, pad->Name);
4516 * Note: we can't free() pad->Name first because
4517 * it is used in the undo list
4519 pad->Name = strdup (pinname);
4520 SetChangedFlag (true);
4521 changed = 1;
4524 END_LOOP;
4527 END_LOOP;
4529 * done with our action so increment the undo # if we actually
4530 * changed anything
4532 if (changed)
4534 if (defer_updates)
4535 defer_needs_update = 1;
4536 else
4538 IncrementUndoSerialNumber ();
4539 gui->invalidate_all ();
4543 return 0;
4546 /* --------------------------------------------------------------------------- */
4548 static const char changename_syntax[] =
4549 N_("ChangeName(Object)\n"
4550 "ChangeName(Layout|Layer)");
4552 static const char changename_help[] = N_("Sets the name of objects.");
4554 /* %start-doc actions ChangeName
4556 @table @code
4558 @item Object
4559 Changes the name of the element under the cursor.
4561 @item Layout
4562 Changes the name of the layout. This is printed on the fab drawings.
4564 @item Layer
4565 Changes the name of the currently active layer.
4567 @end table
4569 %end-doc */
4572 ActionChangeName (int argc, char **argv, Coord x, Coord y)
4574 char *function = ARG (0);
4575 char *name;
4577 if (function)
4579 switch (GetFunctionID (function))
4581 /* change the name of an object */
4582 case F_Object:
4584 int type;
4585 void *ptr1, *ptr2, *ptr3;
4587 gui->get_coords (_("Select an Object"), &x, &y);
4588 if ((type =
4589 SearchScreen (x, y, CHANGENAME_TYPES,
4590 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4592 SaveUndoSerialNumber ();
4593 if (QueryInputAndChangeObjectName (type, ptr1, ptr2, ptr3))
4595 SetChangedFlag (true);
4596 if (type == ELEMENT_TYPE)
4598 RubberbandType *ptr;
4599 int i;
4601 RestoreUndoSerialNumber ();
4602 Crosshair.AttachedObject.RubberbandN = 0;
4603 LookupRatLines (type, ptr1, ptr2, ptr3);
4604 ptr = Crosshair.AttachedObject.Rubberband;
4605 for (i = 0; i < Crosshair.AttachedObject.RubberbandN;
4606 i++, ptr++)
4608 if (PCB->RatOn)
4609 EraseRat ((RatType *) ptr->Line);
4610 MoveObjectToRemoveUndoList (RATLINE_TYPE,
4611 ptr->Line, ptr->Line,
4612 ptr->Line);
4614 IncrementUndoSerialNumber ();
4615 Draw ();
4619 break;
4622 /* change the layout's name */
4623 case F_Layout:
4624 name =
4625 gui->prompt_for (_("Enter the layout name:"), EMPTY (PCB->Name));
4626 /* NB: ChangeLayoutName takes ownership of the passed memory */
4627 if (name && ChangeLayoutName (name))
4628 SetChangedFlag (true);
4629 break;
4631 /* change the name of the active layer */
4632 case F_Layer:
4633 name = gui->prompt_for (_("Enter the layer name:"),
4634 EMPTY (CURRENT->Name));
4635 /* NB: ChangeLayerName takes ownership of the passed memory */
4636 if (name && ChangeLayerName (CURRENT, name))
4637 SetChangedFlag (true);
4638 break;
4641 return 0;
4645 /* --------------------------------------------------------------------------- */
4647 static const char morphpolygon_syntax[] = N_("MorphPolygon(Object|Selected)");
4649 static const char morphpolygon_help[] =
4650 N_("Converts dead polygon islands into separate polygons.");
4652 /* %start-doc actions MorphPolygon
4654 If a polygon is divided into unconnected "islands", you can use
4655 this command to convert the otherwise disappeared islands into
4656 separate polygons. Be sure the cursor is over a portion of the
4657 polygon that remains visible. Very small islands that may flake
4658 off are automatically deleted.
4660 %end-doc */
4662 static int
4663 ActionMorphPolygon (int argc, char **argv, Coord x, Coord y)
4665 char *function = ARG (0);
4666 if (function)
4668 switch (GetFunctionID (function))
4670 case F_Object:
4672 int type;
4673 void *ptr1, *ptr2, *ptr3;
4675 gui->get_coords (_("Select an Object"), &x, &y);
4676 if ((type = SearchScreen (x, y, POLYGON_TYPE,
4677 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4679 MorphPolygon ((LayerType *) ptr1, (PolygonType *) ptr3);
4680 Draw ();
4681 IncrementUndoSerialNumber ();
4683 break;
4685 case F_Selected:
4686 case F_SelectedObjects:
4687 ALLPOLYGON_LOOP (PCB->Data);
4689 if (TEST_FLAG (SELECTEDFLAG, polygon))
4690 MorphPolygon (layer, polygon);
4692 ENDALL_LOOP;
4693 Draw ();
4694 IncrementUndoSerialNumber ();
4695 break;
4698 return 0;
4701 /* --------------------------------------------------------------------------- */
4703 static const char togglehidename_syntax[] =
4704 N_("ToggleHideName(Object|SelectedElements)");
4706 static const char togglehidename_help[] =
4707 N_("Toggles the visibility of element names.");
4709 /* %start-doc actions ToggleHideName
4711 If names are hidden you won't see them on the screen and they will not
4712 appear on the silk layer when you print the layout.
4714 %end-doc */
4716 static int
4717 ActionToggleHideName (int argc, char **argv, Coord x, Coord y)
4719 char *function = ARG (0);
4720 if (function && PCB->ElementOn)
4722 switch (GetFunctionID (function))
4724 case F_Object:
4726 int type;
4727 void *ptr1, *ptr2, *ptr3;
4729 gui->get_coords (_("Select an Object"), &x, &y);
4730 if ((type = SearchScreen (x, y, ELEMENT_TYPE,
4731 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4733 AddObjectToFlagUndoList (type, ptr1, ptr2, ptr3);
4734 EraseElementName ((ElementType *) ptr2);
4735 TOGGLE_FLAG (HIDENAMEFLAG, (ElementType *) ptr2);
4736 DrawElementName ((ElementType *) ptr2);
4737 Draw ();
4738 IncrementUndoSerialNumber ();
4740 break;
4742 case F_SelectedElements:
4743 case F_Selected:
4745 bool changed = false;
4746 ELEMENT_LOOP (PCB->Data);
4748 if ((TEST_FLAG (SELECTEDFLAG, element) ||
4749 TEST_FLAG (SELECTEDFLAG,
4750 &NAMEONPCB_TEXT (element)))
4751 && (FRONT (element) || PCB->InvisibleObjectsOn))
4753 AddObjectToFlagUndoList (ELEMENT_TYPE, element,
4754 element, element);
4755 EraseElementName (element);
4756 TOGGLE_FLAG (HIDENAMEFLAG, element);
4757 DrawElementName (element);
4758 changed = true;
4761 END_LOOP;
4762 if (changed)
4764 Draw ();
4765 IncrementUndoSerialNumber ();
4770 return 0;
4773 /* --------------------------------------------------------------------------- */
4775 static const char changejoin_syntax[] =
4776 N_("ChangeJoin(ToggleObject|SelectedLines|SelectedArcs|Selected)");
4778 static const char changejoin_help[] =
4779 N_("Changes the join (clearance through polygons) of objects.");
4781 /* %start-doc actions ChangeJoin
4783 The join flag determines whether a line or arc, drawn to intersect a
4784 polygon, electrically connects to the polygon or not. When joined,
4785 the line/arc is simply drawn over the polygon, making an electrical
4786 connection. When not joined, a gap is drawn between the line and the
4787 polygon, insulating them from each other.
4789 %end-doc */
4791 static int
4792 ActionChangeJoin (int argc, char **argv, Coord x, Coord y)
4794 char *function = ARG (0);
4795 if (function)
4797 switch (GetFunctionID (function))
4799 case F_ToggleObject:
4800 case F_Object:
4802 int type;
4803 void *ptr1, *ptr2, *ptr3;
4805 gui->get_coords (_("Select an Object"), &x, &y);
4806 if ((type =
4807 SearchScreen (x, y, CHANGEJOIN_TYPES,
4808 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4809 if (ChangeObjectJoin (type, ptr1, ptr2, ptr3))
4810 SetChangedFlag (true);
4811 break;
4814 case F_SelectedLines:
4815 if (ChangeSelectedJoin (LINE_TYPE))
4816 SetChangedFlag (true);
4817 break;
4819 case F_SelectedArcs:
4820 if (ChangeSelectedJoin (ARC_TYPE))
4821 SetChangedFlag (true);
4822 break;
4824 case F_Selected:
4825 case F_SelectedObjects:
4826 if (ChangeSelectedJoin (CHANGEJOIN_TYPES))
4827 SetChangedFlag (true);
4828 break;
4831 return 0;
4834 /* --------------------------------------------------------------------------- */
4836 static const char changesquare_syntax[] =
4837 N_("ChangeSquare(ToggleObject)\n"
4838 "ChangeSquare(SelectedElements|SelectedPins)\n"
4839 "ChangeSquare(Selected|SelectedObjects)");
4841 static const char changesquare_help[] =
4842 N_("Changes the square flag of pins and pads.");
4844 /* %start-doc actions ChangeSquare
4846 Note that @code{Pins} means both pins and pads.
4848 @pinshapes
4850 %end-doc */
4852 static int
4853 ActionChangeSquare (int argc, char **argv, Coord x, Coord y)
4855 char *function = ARG (0);
4856 if (function)
4858 switch (GetFunctionID (function))
4860 case F_ToggleObject:
4861 case F_Object:
4863 int type;
4864 void *ptr1, *ptr2, *ptr3;
4866 gui->get_coords (_("Select an Object"), &x, &y);
4867 if ((type =
4868 SearchScreen (x, y, CHANGESQUARE_TYPES,
4869 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4870 if (ChangeObjectSquare (type, ptr1, ptr2, ptr3))
4871 SetChangedFlag (true);
4872 break;
4875 case F_SelectedElements:
4876 if (ChangeSelectedSquare (ELEMENT_TYPE))
4877 SetChangedFlag (true);
4878 break;
4880 case F_SelectedPins:
4881 if (ChangeSelectedSquare (PIN_TYPE | PAD_TYPE))
4882 SetChangedFlag (true);
4883 break;
4885 case F_Selected:
4886 case F_SelectedObjects:
4887 if (ChangeSelectedSquare (PIN_TYPE | PAD_TYPE))
4888 SetChangedFlag (true);
4889 break;
4892 return 0;
4895 /* --------------------------------------------------------------------------- */
4897 static const char setsquare_syntax[] =
4898 N_("SetSquare(ToggleObject|SelectedElements|SelectedPins)");
4900 static const char setsquare_help[] = N_("sets the square-flag of objects.");
4902 /* %start-doc actions SetSquare
4904 Note that @code{Pins} means pins and pads.
4906 @pinshapes
4908 %end-doc */
4910 static int
4911 ActionSetSquare (int argc, char **argv, Coord x, Coord y)
4913 char *function = ARG (0);
4914 if (function && *function)
4916 switch (GetFunctionID (function))
4918 case F_ToggleObject:
4919 case F_Object:
4921 int type;
4922 void *ptr1, *ptr2, *ptr3;
4924 gui->get_coords (_("Select an Object"), &x, &y);
4925 if ((type =
4926 SearchScreen (x, y, CHANGESQUARE_TYPES,
4927 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4928 if (SetObjectSquare (type, ptr1, ptr2, ptr3))
4929 SetChangedFlag (true);
4930 break;
4933 case F_SelectedElements:
4934 if (SetSelectedSquare (ELEMENT_TYPE))
4935 SetChangedFlag (true);
4936 break;
4938 case F_SelectedPins:
4939 if (SetSelectedSquare (PIN_TYPE | PAD_TYPE))
4940 SetChangedFlag (true);
4941 break;
4943 case F_Selected:
4944 case F_SelectedObjects:
4945 if (SetSelectedSquare (PIN_TYPE | PAD_TYPE))
4946 SetChangedFlag (true);
4947 break;
4950 return 0;
4953 /* --------------------------------------------------------------------------- */
4955 static const char clearsquare_syntax[] =
4956 N_("ClearSquare(ToggleObject|SelectedElements|SelectedPins)");
4958 static const char clearsquare_help[] =
4959 N_("Clears the square-flag of pins and pads.");
4961 /* %start-doc actions ClearSquare
4963 Note that @code{Pins} means pins and pads.
4965 @pinshapes
4967 %end-doc */
4969 static int
4970 ActionClearSquare (int argc, char **argv, Coord x, Coord y)
4972 char *function = ARG (0);
4973 if (function && *function)
4975 switch (GetFunctionID (function))
4977 case F_ToggleObject:
4978 case F_Object:
4980 int type;
4981 void *ptr1, *ptr2, *ptr3;
4983 gui->get_coords (_("Select an Object"), &x, &y);
4984 if ((type =
4985 SearchScreen (x, y, CHANGESQUARE_TYPES,
4986 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4987 if (ClrObjectSquare (type, ptr1, ptr2, ptr3))
4988 SetChangedFlag (true);
4989 break;
4992 case F_SelectedElements:
4993 if (ClrSelectedSquare (ELEMENT_TYPE))
4994 SetChangedFlag (true);
4995 break;
4997 case F_SelectedPins:
4998 if (ClrSelectedSquare (PIN_TYPE | PAD_TYPE))
4999 SetChangedFlag (true);
5000 break;
5002 case F_Selected:
5003 case F_SelectedObjects:
5004 if (ClrSelectedSquare (PIN_TYPE | PAD_TYPE))
5005 SetChangedFlag (true);
5006 break;
5009 return 0;
5012 /* --------------------------------------------------------------------------- */
5014 static const char changeoctagon_syntax[] =
5015 N_("ChangeOctagon(Object|ToggleObject|SelectedObjects|Selected)\n"
5016 "ChangeOctagon(SelectedElements|SelectedPins|SelectedVias)");
5018 static const char changeoctagon_help[] =
5019 N_("Changes the octagon-flag of pins and vias.");
5021 /* %start-doc actions ChangeOctagon
5023 @pinshapes
5025 %end-doc */
5027 static int
5028 ActionChangeOctagon (int argc, char **argv, Coord x, Coord y)
5030 char *function = ARG (0);
5031 if (function)
5033 switch (GetFunctionID (function))
5035 case F_ToggleObject:
5036 case F_Object:
5038 int type;
5039 void *ptr1, *ptr2, *ptr3;
5041 gui->get_coords (_("Select an Object"), &x, &y);
5042 if ((type =
5043 SearchScreen (x, y, CHANGEOCTAGON_TYPES,
5044 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5045 if (ChangeObjectOctagon (type, ptr1, ptr2, ptr3))
5046 SetChangedFlag (true);
5047 break;
5050 case F_SelectedElements:
5051 if (ChangeSelectedOctagon (ELEMENT_TYPE))
5052 SetChangedFlag (true);
5053 break;
5055 case F_SelectedPins:
5056 if (ChangeSelectedOctagon (PIN_TYPE))
5057 SetChangedFlag (true);
5058 break;
5060 case F_SelectedVias:
5061 if (ChangeSelectedOctagon (VIA_TYPE))
5062 SetChangedFlag (true);
5063 break;
5065 case F_Selected:
5066 case F_SelectedObjects:
5067 if (ChangeSelectedOctagon (PIN_TYPES))
5068 SetChangedFlag (true);
5069 break;
5072 return 0;
5075 /* --------------------------------------------------------------------------- */
5077 static const char setoctagon_syntax[] =
5078 N_("SetOctagon(Object|ToggleObject|SelectedElements|Selected)");
5080 static const char setoctagon_help[] = N_("Sets the octagon-flag of objects.");
5082 /* %start-doc actions SetOctagon
5084 @pinshapes
5086 %end-doc */
5088 static int
5089 ActionSetOctagon (int argc, char **argv, Coord x, Coord y)
5091 char *function = ARG (0);
5092 if (function)
5094 switch (GetFunctionID (function))
5096 case F_ToggleObject:
5097 case F_Object:
5099 int type;
5100 void *ptr1, *ptr2, *ptr3;
5102 gui->get_coords (_("Select an Object"), &x, &y);
5103 if ((type =
5104 SearchScreen (x, y, CHANGEOCTAGON_TYPES,
5105 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5106 if (SetObjectOctagon (type, ptr1, ptr2, ptr3))
5107 SetChangedFlag (true);
5108 break;
5111 case F_SelectedElements:
5112 if (SetSelectedOctagon (ELEMENT_TYPE))
5113 SetChangedFlag (true);
5114 break;
5116 case F_SelectedPins:
5117 if (SetSelectedOctagon (PIN_TYPE))
5118 SetChangedFlag (true);
5119 break;
5121 case F_SelectedVias:
5122 if (SetSelectedOctagon (VIA_TYPE))
5123 SetChangedFlag (true);
5124 break;
5126 case F_Selected:
5127 case F_SelectedObjects:
5128 if (SetSelectedOctagon (PIN_TYPES))
5129 SetChangedFlag (true);
5130 break;
5133 return 0;
5136 /* --------------------------------------------------------------------------- */
5138 static const char clearoctagon_syntax[] =
5139 N_("ClearOctagon(ToggleObject|Object|SelectedObjects|Selected)\n"
5140 "ClearOctagon(SelectedElements|SelectedPins|SelectedVias)");
5142 static const char clearoctagon_help[] =
5143 N_("Clears the octagon-flag of pins and vias.");
5145 /* %start-doc actions ClearOctagon
5147 @pinshapes
5149 %end-doc */
5151 static int
5152 ActionClearOctagon (int argc, char **argv, Coord x, Coord y)
5154 char *function = ARG (0);
5155 if (function)
5157 switch (GetFunctionID (function))
5159 case F_ToggleObject:
5160 case F_Object:
5162 int type;
5163 void *ptr1, *ptr2, *ptr3;
5165 gui->get_coords (_("Select an Object"), &x, &y);
5166 if ((type =
5167 SearchScreen (Crosshair.X, Crosshair.Y, CHANGEOCTAGON_TYPES,
5168 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5169 if (ClrObjectOctagon (type, ptr1, ptr2, ptr3))
5170 SetChangedFlag (true);
5171 break;
5174 case F_SelectedElements:
5175 if (ClrSelectedOctagon (ELEMENT_TYPE))
5176 SetChangedFlag (true);
5177 break;
5179 case F_SelectedPins:
5180 if (ClrSelectedOctagon (PIN_TYPE))
5181 SetChangedFlag (true);
5182 break;
5184 case F_SelectedVias:
5185 if (ClrSelectedOctagon (VIA_TYPE))
5186 SetChangedFlag (true);
5187 break;
5189 case F_Selected:
5190 case F_SelectedObjects:
5191 if (ClrSelectedOctagon (PIN_TYPES))
5192 SetChangedFlag (true);
5193 break;
5196 return 0;
5199 /* --------------------------------------------------------------------------- */
5201 static const char changehold_syntax[] =
5202 N_("ChangeHole(ToggleObject|Object|SelectedVias|Selected)");
5204 static const char changehold_help[] = N_("Changes the hole flag of objects.");
5206 /* %start-doc actions ChangeHole
5208 The "hole flag" of a via determines whether the via is a
5209 plated-through hole (not set), or an unplated hole (set).
5211 %end-doc */
5213 static int
5214 ActionChangeHole (int argc, char **argv, Coord x, Coord y)
5216 char *function = ARG (0);
5217 if (function)
5219 switch (GetFunctionID (function))
5221 case F_ToggleObject:
5222 case F_Object:
5224 int type;
5225 void *ptr1, *ptr2, *ptr3;
5227 gui->get_coords (_("Select an Object"), &x, &y);
5228 if ((type = SearchScreen (x, y, VIA_TYPE,
5229 &ptr1, &ptr2, &ptr3)) != NO_TYPE
5230 && ChangeHole ((PinType *) ptr3))
5231 IncrementUndoSerialNumber ();
5232 break;
5235 case F_SelectedVias:
5236 case F_Selected:
5237 if (ChangeSelectedHole ())
5238 SetChangedFlag (true);
5239 break;
5242 return 0;
5245 /* --------------------------------------------------------------------------- */
5247 static const char changepaste_syntax[] =
5248 N_("ChangePaste(ToggleObject|Object|SelectedPads|Selected)");
5250 static const char changepaste_help[] =
5251 N_("Changes the no paste flag of objects.");
5253 /* %start-doc actions ChangePaste
5255 The "no paste flag" of a pad determines whether the solderpaste
5256 stencil will have an opening for the pad (no set) or if there wil be
5257 no solderpaste on the pad (set). This is used for things such as
5258 fiducial pads.
5260 %end-doc */
5262 static int
5263 ActionChangePaste (int argc, char **argv, Coord x, Coord y)
5265 char *function = ARG (0);
5266 if (function)
5268 switch (GetFunctionID (function))
5270 case F_ToggleObject:
5271 case F_Object:
5273 int type;
5274 void *ptr1, *ptr2, *ptr3;
5276 gui->get_coords (_("Select an Object"), &x, &y);
5277 if ((type = SearchScreen (x, y, PAD_TYPE,
5278 &ptr1, &ptr2, &ptr3)) != NO_TYPE
5279 && ChangePaste ((PadType *) ptr3))
5280 IncrementUndoSerialNumber ();
5281 break;
5284 case F_SelectedPads:
5285 case F_Selected:
5286 if (ChangeSelectedPaste ())
5287 SetChangedFlag (true);
5288 break;
5291 return 0;
5294 /* --------------------------------------------------------------------------- */
5296 static const char select_syntax[] =
5297 N_("Select(Object|ToggleObject)\n"
5298 "Select(All|Block|Connection)\n"
5299 "Select(ElementByName|ObjectByName|PadByName|PinByName)\n"
5300 "Select(ElementByName|ObjectByName|PadByName|PinByName, Name)\n"
5301 "Select(TextByName|ViaByName|NetByName)\n"
5302 "Select(TextByName|ViaByName|NetByName, Name)\n"
5303 "Select(Convert)");
5305 static const char select_help[] = N_("Toggles or sets the selection.");
5307 /* %start-doc actions Select
5309 @table @code
5311 @item ElementByName
5312 @item ObjectByName
5313 @item PadByName
5314 @item PinByName
5315 @item TextByName
5316 @item ViaByName
5317 @item NetByName
5319 These all rely on having a regular expression parser built into
5320 @code{pcb}. If the name is not specified then the user is prompted
5321 for a pattern, and all objects that match the pattern and are of the
5322 type specified are selected.
5324 @item Object
5325 @item ToggleObject
5326 Selects the object under the cursor.
5328 @item Block
5329 Selects all objects in a rectangle indicated by the cursor.
5331 @item All
5332 Selects all objects on the board.
5334 @item Found
5335 Selects all connections with the ``found'' flag set.
5337 @item Connection
5338 Selects all connections with the ``connected'' flag set.
5340 @item Convert
5341 Converts the selected objects to an element. This uses the highest
5342 numbered paste buffer.
5344 @end table
5346 %end-doc */
5348 static int
5349 ActionSelect (int argc, char **argv, Coord x, Coord y)
5351 char *function = ARG (0);
5352 if (function)
5354 switch (GetFunctionID (function))
5356 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5357 int type;
5358 /* select objects by their names */
5359 case F_ElementByName:
5360 type = ELEMENT_TYPE;
5361 goto commonByName;
5362 case F_ObjectByName:
5363 type = ALL_TYPES;
5364 goto commonByName;
5365 case F_PadByName:
5366 type = PAD_TYPE;
5367 goto commonByName;
5368 case F_PinByName:
5369 type = PIN_TYPE;
5370 goto commonByName;
5371 case F_TextByName:
5372 type = TEXT_TYPE;
5373 goto commonByName;
5374 case F_ViaByName:
5375 type = VIA_TYPE;
5376 goto commonByName;
5377 case F_NetByName:
5378 type = NET_TYPE;
5379 goto commonByName;
5381 commonByName:
5383 char *pattern = ARG (1);
5385 if (pattern
5386 || (pattern =
5387 gui->prompt_for (_("Enter pattern:"), "")) != NULL)
5389 if (SelectObjectByName (type, pattern, true))
5390 SetChangedFlag (true);
5391 if (ARG (1) == NULL)
5392 free (pattern);
5394 break;
5396 #endif /* defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP) */
5398 /* select a single object */
5399 case F_ToggleObject:
5400 case F_Object:
5401 if (SelectObject ())
5402 SetChangedFlag (true);
5403 break;
5405 /* all objects in block */
5406 case F_Block:
5408 BoxType box;
5410 box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
5411 Crosshair.AttachedBox.Point2.X);
5412 box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
5413 Crosshair.AttachedBox.Point2.Y);
5414 box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
5415 Crosshair.AttachedBox.Point2.X);
5416 box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
5417 Crosshair.AttachedBox.Point2.Y);
5418 notify_crosshair_change (false);
5419 NotifyBlock ();
5420 if (Crosshair.AttachedBox.State == STATE_THIRD &&
5421 SelectBlock (&box, true))
5423 SetChangedFlag (true);
5424 Crosshair.AttachedBox.State = STATE_FIRST;
5426 notify_crosshair_change (true);
5427 break;
5430 /* select all visible objects */
5431 case F_All:
5433 BoxType box;
5435 box.X1 = -MAX_COORD;
5436 box.Y1 = -MAX_COORD;
5437 box.X2 = MAX_COORD;
5438 box.Y2 = MAX_COORD;
5439 if (SelectBlock (&box, true))
5440 SetChangedFlag (true);
5441 break;
5444 /* all logical connections */
5445 case F_Found:
5446 if (SelectByFlag (FOUNDFLAG, true))
5448 Draw ();
5449 IncrementUndoSerialNumber ();
5450 SetChangedFlag (true);
5452 break;
5454 /* all physical connections */
5455 case F_Connection:
5456 if (SelectByFlag (CONNECTEDFLAG, true))
5458 Draw ();
5459 IncrementUndoSerialNumber ();
5460 SetChangedFlag (true);
5462 break;
5464 case F_Convert:
5466 Coord x, y;
5467 Note.Buffer = Settings.BufferNumber;
5468 SetBufferNumber (MAX_BUFFER - 1);
5469 ClearBuffer (PASTEBUFFER);
5470 gui->get_coords (_("Select the Element's Mark Location"), &x, &y);
5471 x = GridFit (x, PCB->Grid, PCB->GridOffsetX);
5472 y = GridFit (y, PCB->Grid, PCB->GridOffsetY);
5473 AddSelectedToBuffer (PASTEBUFFER, x, y, true);
5474 SaveUndoSerialNumber ();
5475 RemoveSelected ();
5476 ConvertBufferToElement (PASTEBUFFER);
5477 RestoreUndoSerialNumber ();
5478 CopyPastebufferToLayout (x, y);
5479 SetBufferNumber (Note.Buffer);
5481 break;
5483 default:
5484 AFAIL (select);
5485 break;
5488 return 0;
5491 /* FLAG(have_regex,FlagHaveRegex,0) */
5493 FlagHaveRegex (int parm)
5495 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5496 return 1;
5497 #else
5498 return 0;
5499 #endif
5502 /* --------------------------------------------------------------------------- */
5504 static const char unselect_syntax[] =
5505 N_("Unselect(All|Block|Connection)\n"
5506 "Unselect(ElementByName|ObjectByName|PadByName|PinByName)\n"
5507 "Unselect(ElementByName|ObjectByName|PadByName|PinByName, Name)\n"
5508 "Unselect(TextByName|ViaByName)\n"
5509 "Unselect(TextByName|ViaByName, Name)\n");
5511 static const char unselect_help[] =
5512 N_("Unselects the object at the pointer location or the specified objects.");
5514 /* %start-doc actions Unselect
5516 @table @code
5518 @item All
5519 Unselect all objects.
5521 @item Block
5522 Unselect all objects in a rectangle given by the cursor.
5524 @item Connection
5525 Unselect all connections with the ``found'' flag set.
5527 @item ElementByName
5528 @item ObjectByName
5529 @item PadByName
5530 @item PinByName
5531 @item TextByName
5532 @item ViaByName
5534 These all rely on having a regular expression parser built into
5535 @code{pcb}. If the name is not specified then the user is prompted
5536 for a pattern, and all objects that match the pattern and are of the
5537 type specified are unselected.
5540 @end table
5542 %end-doc */
5544 static int
5545 ActionUnselect (int argc, char **argv, Coord x, Coord y)
5547 char *function = ARG (0);
5548 if (function)
5550 switch (GetFunctionID (function))
5552 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5553 int type;
5554 /* select objects by their names */
5555 case F_ElementByName:
5556 type = ELEMENT_TYPE;
5557 goto commonByName;
5558 case F_ObjectByName:
5559 type = ALL_TYPES;
5560 goto commonByName;
5561 case F_PadByName:
5562 type = PAD_TYPE;
5563 goto commonByName;
5564 case F_PinByName:
5565 type = PIN_TYPE;
5566 goto commonByName;
5567 case F_TextByName:
5568 type = TEXT_TYPE;
5569 goto commonByName;
5570 case F_ViaByName:
5571 type = VIA_TYPE;
5572 goto commonByName;
5573 case F_NetByName:
5574 type = NET_TYPE;
5575 goto commonByName;
5577 commonByName:
5579 char *pattern = ARG (1);
5581 if (pattern
5582 || (pattern =
5583 gui->prompt_for (_("Enter pattern:"), "")) != NULL)
5585 if (SelectObjectByName (type, pattern, false))
5586 SetChangedFlag (true);
5587 if (ARG (1) == NULL)
5588 free (pattern);
5590 break;
5592 #endif /* defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP) */
5594 /* all objects in block */
5595 case F_Block:
5597 BoxType box;
5599 box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
5600 Crosshair.AttachedBox.Point2.X);
5601 box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
5602 Crosshair.AttachedBox.Point2.Y);
5603 box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
5604 Crosshair.AttachedBox.Point2.X);
5605 box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
5606 Crosshair.AttachedBox.Point2.Y);
5607 notify_crosshair_change (false);
5608 NotifyBlock ();
5609 if (Crosshair.AttachedBox.State == STATE_THIRD &&
5610 SelectBlock (&box, false))
5612 SetChangedFlag (true);
5613 Crosshair.AttachedBox.State = STATE_FIRST;
5615 notify_crosshair_change (true);
5616 break;
5619 /* unselect all visible objects */
5620 case F_All:
5622 BoxType box;
5624 box.X1 = -MAX_COORD;
5625 box.Y1 = -MAX_COORD;
5626 box.X2 = MAX_COORD;
5627 box.Y2 = MAX_COORD;
5628 if (SelectBlock (&box, false))
5629 SetChangedFlag (true);
5630 break;
5633 /* all logical connections */
5634 case F_Found:
5635 if (SelectByFlag (FOUNDFLAG, false))
5637 Draw ();
5638 IncrementUndoSerialNumber ();
5639 SetChangedFlag (true);
5641 break;
5643 /* all physical connections */
5644 case F_Connection:
5645 if (SelectByFlag (CONNECTEDFLAG, false))
5647 Draw ();
5648 IncrementUndoSerialNumber ();
5649 SetChangedFlag (true);
5651 break;
5653 default:
5654 AFAIL (unselect);
5655 break;
5659 return 0;
5662 /* --------------------------------------------------------------------------- */
5664 static const char saveto_syntax[] =
5665 N_("SaveTo(Layout|LayoutAs,filename)\n"
5666 "SaveTo(AllConnections|AllUnusedPins|ElementConnections,filename)\n"
5667 "SaveTo(PasteBuffer,filename)");
5669 static const char saveto_help[] = N_("Saves data to a file.");
5671 /* %start-doc actions SaveTo
5673 @table @code
5675 @item Layout
5676 Saves the current layout.
5678 @item LayoutAs
5679 Saves the current layout, and remembers the filename used.
5681 @item AllConnections
5682 Save all connections to a file.
5684 @item AllUnusedPins
5685 List all unused pins to a file.
5687 @item ElementConnections
5688 Save connections to the element at the cursor to a file.
5690 @item PasteBuffer
5691 Save the content of the active Buffer to a file. This is the graphical way to create a footprint.
5693 @end table
5695 %end-doc */
5697 static int
5698 ActionSaveTo (int argc, char **argv, Coord x, Coord y)
5700 char *function;
5701 char *name;
5703 function = argv[0];
5704 name = argv[1];
5706 if (strcasecmp (function, "Layout") == 0)
5708 if (SavePCB (PCB->Filename) == 0)
5709 SetChangedFlag (false);
5710 return 0;
5713 if (argc != 2)
5714 AFAIL (saveto);
5716 if (strcasecmp (function, "LayoutAs") == 0)
5718 if (SavePCB (name) == 0)
5720 SetChangedFlag (false);
5721 free (PCB->Filename);
5722 PCB->Filename = strdup (name);
5723 if (gui->notify_filename_changed != NULL)
5724 gui->notify_filename_changed ();
5726 return 0;
5729 if (strcasecmp (function, "AllConnections") == 0)
5731 FILE *fp;
5732 bool result;
5733 if ((fp = CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
5735 LookupConnectionsToAllElements (fp);
5736 fclose (fp);
5737 SetChangedFlag (true);
5739 return 0;
5742 if (strcasecmp (function, "AllUnusedPins") == 0)
5744 FILE *fp;
5745 bool result;
5746 if ((fp = CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
5748 LookupUnusedPins (fp);
5749 fclose (fp);
5750 SetChangedFlag (true);
5752 return 0;
5755 if (strcasecmp (function, "ElementConnections") == 0)
5757 ElementType *element;
5758 void *ptrtmp;
5759 FILE *fp;
5760 bool result;
5762 if ((SearchScreen (Crosshair.X, Crosshair.Y, ELEMENT_TYPE,
5763 &ptrtmp, &ptrtmp, &ptrtmp)) != NO_TYPE)
5765 element = (ElementType *) ptrtmp;
5766 if ((fp =
5767 CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
5769 LookupElementConnections (element, fp);
5770 fclose (fp);
5771 SetChangedFlag (true);
5774 return 0;
5777 if (strcasecmp (function, "PasteBuffer") == 0)
5779 return SaveBufferElements (name);
5782 AFAIL (saveto);
5785 /* --------------------------------------------------------------------------- */
5787 static const char savesettings_syntax[] =
5788 N_("SaveSettings()\n"
5789 "SaveSettings(local)");
5791 static const char savesettings_help[] = N_("Saves settings.");
5793 /* %start-doc actions SaveSettings
5795 If you pass no arguments, the settings are stored in
5796 @code{$HOME/.pcb/settings}. If you pass the word @code{local} they're
5797 saved in @code{./pcb.settings}.
5799 %end-doc */
5801 static int
5802 ActionSaveSettings (int argc, char **argv, Coord x, Coord y)
5804 int locally = argc > 0 ? (strncasecmp (argv[0], "local", 5) == 0) : 0;
5805 hid_save_settings (locally);
5806 return 0;
5809 /* --------------------------------------------------------------------------- */
5811 static const char loadfrom_syntax[] =
5812 N_("LoadFrom(Layout|LayoutToBuffer|ElementToBuffer|Netlist|Revert,filename)");
5814 static const char loadfrom_help[] = N_("Load layout data from a file.");
5816 /* %start-doc actions LoadFrom
5818 This action assumes you know what the filename is. The various GUIs
5819 should have a similar @code{Load} action where the filename is
5820 optional, and will provide their own file selection mechanism to let
5821 you choose the file name.
5823 @table @code
5825 @item Layout
5826 Loads an entire PCB layout, replacing the current one.
5828 @item LayoutToBuffer
5829 Loads an entire PCB layout to the paste buffer.
5831 @item ElementToBuffer
5832 Loads the given element file into the paste buffer. Element files
5833 contain only a single @code{Element} definition, such as the
5834 ``newlib'' library uses.
5836 @item Netlist
5837 Loads a new netlist, replacing any current netlist.
5839 @item Revert
5840 Re-loads the current layout from its disk file, reverting any changes
5841 you may have made.
5843 @end table
5845 %end-doc */
5847 static int
5848 ActionLoadFrom (int argc, char **argv, Coord x, Coord y)
5850 char *function;
5851 char *name;
5853 if (argc < 2)
5854 AFAIL (loadfrom);
5856 function = argv[0];
5857 name = argv[1];
5859 if (strcasecmp (function, "ElementToBuffer") == 0)
5861 notify_crosshair_change (false);
5862 if (LoadElementToBuffer (PASTEBUFFER, name, true))
5863 SetMode (PASTEBUFFER_MODE);
5864 notify_crosshair_change (true);
5867 else if (strcasecmp (function, "LayoutToBuffer") == 0)
5869 notify_crosshair_change (false);
5870 if (LoadLayoutToBuffer (PASTEBUFFER, name))
5871 SetMode (PASTEBUFFER_MODE);
5872 notify_crosshair_change (true);
5875 else if (strcasecmp (function, "Layout") == 0)
5877 if (!PCB->Changed ||
5878 gui->confirm_dialog (_("OK to override layout data?"), 0))
5879 LoadPCB (name);
5882 else if (strcasecmp (function, "Netlist") == 0)
5884 if (PCB->Netlistname)
5885 free (PCB->Netlistname);
5886 PCB->Netlistname = StripWhiteSpaceAndDup (name);
5887 FreeLibraryMemory (&PCB->NetlistLib);
5888 ImportNetlist (PCB->Netlistname);
5889 NetlistChanged (1);
5891 else if (strcasecmp (function, "Revert") == 0 && PCB->Filename
5892 && (!PCB->Changed
5893 || gui->confirm_dialog (_("OK to override changes?"), 0)))
5895 RevertPCB ();
5898 return 0;
5901 /* --------------------------------------------------------------------------- */
5903 static const char new_syntax[] = N_("New([name])");
5905 static const char new_help[] = N_("Starts a new layout.");
5907 /* %start-doc actions New
5909 If a name is not given, one is prompted for.
5911 %end-doc */
5913 static int
5914 ActionNew (int argc, char **argv, Coord x, Coord y)
5916 char *name = ARG (0);
5918 if (!PCB->Changed || gui->confirm_dialog (_("OK to clear layout data?"), 0))
5920 if (name)
5921 name = strdup (name);
5922 else
5923 name = gui->prompt_for (_("Enter the layout name:"), "");
5925 if (!name)
5926 return 1;
5928 notify_crosshair_change (false);
5929 /* do emergency saving
5930 * clear the old struct and allocate memory for the new one
5932 if (PCB->Changed && Settings.SaveInTMP)
5933 SaveInTMP ();
5934 RemovePCB (PCB);
5935 PCB = NULL;
5936 PCB = CreateNewPCB (true);
5937 CreateNewPCBPost (PCB, 1);
5939 /* setup the new name and reset some values to default */
5940 free (PCB->Name);
5941 PCB->Name = name;
5943 ResetStackAndVisibility ();
5944 SetCrosshairRange (0, 0, PCB->MaxWidth, PCB->MaxHeight);
5945 CenterDisplay (PCB->MaxWidth / 2, PCB->MaxHeight / 2);
5946 Redraw ();
5948 hid_action ("PCBChanged");
5949 notify_crosshair_change (true);
5950 return 0;
5952 return 1;
5955 /* ---------------------------------------------------------------------------
5956 * no operation, just for testing purposes
5957 * syntax: Bell(volume)
5959 void
5960 ActionBell (char *volume)
5962 gui->beep ();
5965 /* --------------------------------------------------------------------------- */
5967 static const char pastebuffer_syntax[] =
5968 N_("PasteBuffer(AddSelected|Clear|1..MAX_BUFFER)\n"
5969 "PasteBuffer(Rotate, 1..3)\n"
5970 "PasteBuffer(Convert|Save|Restore|Mirror)\n"
5971 "PasteBuffer(ToLayout, X, Y, units)");
5973 static const char pastebuffer_help[] =
5974 N_("Various operations on the paste buffer.");
5976 /* %start-doc actions PasteBuffer
5978 There are a number of paste buffers; the actual limit is a
5979 compile-time constant @code{MAX_BUFFER} in @file{globalconst.h}. It
5980 is currently @code{5}. One of these is the ``current'' paste buffer,
5981 often referred to as ``the'' paste buffer.
5983 @table @code
5985 @item AddSelected
5986 Copies the selected objects to the current paste buffer.
5988 @item Clear
5989 Remove all objects from the current paste buffer.
5991 @item Convert
5992 Convert the current paste buffer to an element. Vias are converted to
5993 pins, lines are converted to pads.
5995 @item Restore
5996 Convert any elements in the paste buffer back to vias and lines.
5998 @item Mirror
5999 Flip all objects in the paste buffer vertically (up/down flip). To mirror
6000 horizontally, combine this with rotations.
6002 @item Rotate
6003 Rotates the current buffer. The number to pass is 1..3, where 1 means
6004 90 degrees counter clockwise, 2 means 180 degrees, and 3 means 90
6005 degrees clockwise (270 CCW).
6007 @item Save
6008 Saves any elements in the current buffer to the indicated file.
6010 @item ToLayout
6011 Pastes any elements in the current buffer to the indicated X, Y
6012 coordinates in the layout. The @code{X} and @code{Y} are treated like
6013 @code{delta} is for many other objects. For each, if it's prefixed by
6014 @code{+} or @code{-}, then that amount is relative to the last
6015 location. Otherwise, it's absolute. Units can be
6016 @code{mil} or @code{mm}; if unspecified, units are PCB's internal
6017 units, currently 1/100 mil.
6020 @item 1..MAX_BUFFER
6021 Selects the given buffer to be the current paste buffer.
6023 @end table
6025 %end-doc */
6027 static int
6028 ActionPasteBuffer (int argc, char **argv, Coord x, Coord y)
6030 char *function = argc ? argv[0] : (char *)"";
6031 char *sbufnum = argc > 1 ? argv[1] : (char *)"";
6032 char *name;
6033 static char *default_file = NULL;
6034 int free_name = 0;
6036 notify_crosshair_change (false);
6037 if (function)
6039 switch (GetFunctionID (function))
6041 /* clear contents of paste buffer */
6042 case F_Clear:
6043 ClearBuffer (PASTEBUFFER);
6044 break;
6046 /* copies objects to paste buffer */
6047 case F_AddSelected:
6048 AddSelectedToBuffer (PASTEBUFFER, 0, 0, false);
6049 break;
6051 /* converts buffer contents into an element */
6052 case F_Convert:
6053 ConvertBufferToElement (PASTEBUFFER);
6054 break;
6056 /* break up element for editing */
6057 case F_Restore:
6058 SmashBufferElement (PASTEBUFFER);
6059 break;
6061 /* Mirror buffer */
6062 case F_Mirror:
6063 MirrorBuffer (PASTEBUFFER);
6064 break;
6066 case F_Rotate:
6067 if (sbufnum)
6069 RotateBuffer (PASTEBUFFER, (BYTE) atoi (sbufnum));
6070 SetCrosshairRangeToBuffer ();
6072 break;
6074 case F_Save:
6075 if (PASTEBUFFER->Data->ElementN == 0)
6077 Message (_("Buffer has no elements!\n"));
6078 break;
6080 free_name = 0;
6081 if (argc <= 1)
6083 name = gui->fileselect (_("Save Paste Buffer As ..."),
6084 _("Choose a file to save the contents of the\n"
6085 "paste buffer to.\n"),
6086 default_file, ".fp", "footprint",
6089 if (default_file)
6091 free (default_file);
6092 default_file = NULL;
6094 if ( name && *name)
6096 default_file = strdup (name);
6098 free_name = 1;
6101 else
6102 name = argv[1];
6105 FILE *exist;
6107 if ((exist = fopen (name, "r")))
6109 fclose (exist);
6110 if (gui->
6111 confirm_dialog (_("File exists! Ok to overwrite?"), 0))
6112 SaveBufferElements (name);
6114 else
6115 SaveBufferElements (name);
6117 if (free_name && name)
6118 free (name);
6120 break;
6122 case F_ToLayout:
6124 static Coord oldx = 0, oldy = 0;
6125 Coord x, y;
6126 bool absolute;
6128 if (argc == 1)
6130 x = y = 0;
6132 else if (argc == 3 || argc == 4)
6134 x = GetValue (ARG (1), ARG (3), &absolute);
6135 if (!absolute)
6136 x += oldx;
6137 y = GetValue (ARG (2), ARG (3), &absolute);
6138 if (!absolute)
6139 y += oldy;
6141 else
6143 notify_crosshair_change (true);
6144 AFAIL (pastebuffer);
6147 oldx = x;
6148 oldy = y;
6149 if (CopyPastebufferToLayout (x, y))
6150 SetChangedFlag (true);
6152 break;
6154 /* set number */
6155 default:
6157 int number = atoi (function);
6159 /* correct number */
6160 if (number)
6161 SetBufferNumber (number - 1);
6166 notify_crosshair_change (true);
6167 return 0;
6170 /* --------------------------------------------------------------------------- */
6172 static const char undo_syntax[] = N_("Undo()\n"
6173 "Undo(ClearList)");
6175 static const char undo_help[] = N_("Undo recent changes.");
6177 /* %start-doc actions Undo
6179 The unlimited undo feature of @code{Pcb} allows you to recover from
6180 most operations that materially affect you work. Calling
6181 @code{Undo()} without any parameter recovers from the last (non-undo)
6182 operation. @code{ClearList} is used to release the allocated
6183 memory. @code{ClearList} is called whenever a new layout is started or
6184 loaded. See also @code{Redo} and @code{Atomic}.
6186 Note that undo groups operations by serial number; changes with the
6187 same serial number will be undone (or redone) as a group. See
6188 @code{Atomic}.
6190 %end-doc */
6192 static int
6193 ActionUndo (int argc, char **argv, Coord x, Coord y)
6195 char *function = ARG (0);
6196 if (!function || !*function)
6198 /* don't allow undo in the middle of an operation */
6199 if (Settings.Mode != POLYGONHOLE_MODE &&
6200 Crosshair.AttachedObject.State != STATE_FIRST)
6201 return 1;
6202 if (Crosshair.AttachedBox.State != STATE_FIRST
6203 && Settings.Mode != ARC_MODE)
6204 return 1;
6205 /* undo the last operation */
6207 notify_crosshair_change (false);
6208 if ((Settings.Mode == POLYGON_MODE ||
6209 Settings.Mode == POLYGONHOLE_MODE) &&
6210 Crosshair.AttachedPolygon.PointN)
6212 GoToPreviousPoint ();
6213 notify_crosshair_change (true);
6214 return 0;
6216 /* move anchor point if undoing during line creation */
6217 if (Settings.Mode == LINE_MODE)
6219 if (Crosshair.AttachedLine.State == STATE_SECOND)
6221 if (TEST_FLAG (AUTODRCFLAG, PCB))
6222 Undo (true); /* undo the connection find */
6223 Crosshair.AttachedLine.State = STATE_FIRST;
6224 SetLocalRef (0, 0, false);
6225 notify_crosshair_change (true);
6226 return 0;
6228 if (Crosshair.AttachedLine.State == STATE_THIRD)
6230 int type;
6231 void *ptr1, *ptr3, *ptrtmp;
6232 LineType *ptr2;
6233 /* this search is guaranteed to succeed */
6234 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6235 &ptrtmp, &ptr3,
6236 Crosshair.AttachedLine.Point1.X,
6237 Crosshair.AttachedLine.Point1.Y, 0);
6238 ptr2 = (LineType *) ptrtmp;
6240 /* save both ends of line */
6241 Crosshair.AttachedLine.Point2.X = ptr2->Point1.X;
6242 Crosshair.AttachedLine.Point2.Y = ptr2->Point1.Y;
6243 if ((type = Undo (true)))
6244 SetChangedFlag (true);
6245 /* check that the undo was of the right type */
6246 if ((type & UNDO_CREATE) == 0)
6248 /* wrong undo type, restore anchor points */
6249 Crosshair.AttachedLine.Point2.X =
6250 Crosshair.AttachedLine.Point1.X;
6251 Crosshair.AttachedLine.Point2.Y =
6252 Crosshair.AttachedLine.Point1.Y;
6253 notify_crosshair_change (true);
6254 return 0;
6256 /* move to new anchor */
6257 Crosshair.AttachedLine.Point1.X =
6258 Crosshair.AttachedLine.Point2.X;
6259 Crosshair.AttachedLine.Point1.Y =
6260 Crosshair.AttachedLine.Point2.Y;
6261 /* check if an intermediate point was removed */
6262 if (type & UNDO_REMOVE)
6264 /* this search should find the restored line */
6265 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6266 &ptrtmp,
6267 &ptr3,
6268 Crosshair.AttachedLine.Point2.X,
6269 Crosshair.AttachedLine.Point2.Y, 0);
6270 ptr2 = (LineType *) ptrtmp;
6271 if (TEST_FLAG (AUTODRCFLAG, PCB))
6273 /* undo loses CONNECTEDFLAG and FOUNDFLAG */
6274 SET_FLAG(CONNECTEDFLAG, ptr2);
6275 SET_FLAG(FOUNDFLAG, ptr2);
6276 DrawLine (CURRENT, ptr2);
6278 Crosshair.AttachedLine.Point1.X =
6279 Crosshair.AttachedLine.Point2.X = ptr2->Point2.X;
6280 Crosshair.AttachedLine.Point1.Y =
6281 Crosshair.AttachedLine.Point2.Y = ptr2->Point2.Y;
6283 FitCrosshairIntoGrid (Crosshair.X, Crosshair.Y);
6284 AdjustAttachedObjects ();
6285 if (--addedLines == 0)
6287 Crosshair.AttachedLine.State = STATE_SECOND;
6288 lastLayer = CURRENT;
6290 else
6292 /* this search is guaranteed to succeed too */
6293 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6294 &ptrtmp,
6295 &ptr3,
6296 Crosshair.AttachedLine.Point1.X,
6297 Crosshair.AttachedLine.Point1.Y, 0);
6298 ptr2 = (LineType *) ptrtmp;
6299 lastLayer = (LayerType *) ptr1;
6301 notify_crosshair_change (true);
6302 return 0;
6305 if (Settings.Mode == ARC_MODE)
6307 if (Crosshair.AttachedBox.State == STATE_SECOND)
6309 Crosshair.AttachedBox.State = STATE_FIRST;
6310 notify_crosshair_change (true);
6311 return 0;
6313 if (Crosshair.AttachedBox.State == STATE_THIRD)
6315 void *ptr1, *ptr2, *ptr3;
6316 BoxType *bx;
6317 /* guaranteed to succeed */
6318 SearchObjectByLocation (ARC_TYPE, &ptr1, &ptr2, &ptr3,
6319 Crosshair.AttachedBox.Point1.X,
6320 Crosshair.AttachedBox.Point1.Y, 0);
6321 bx = GetArcEnds ((ArcType *) ptr2);
6322 Crosshair.AttachedBox.Point1.X =
6323 Crosshair.AttachedBox.Point2.X = bx->X1;
6324 Crosshair.AttachedBox.Point1.Y =
6325 Crosshair.AttachedBox.Point2.Y = bx->Y1;
6326 AdjustAttachedObjects ();
6327 if (--addedLines == 0)
6328 Crosshair.AttachedBox.State = STATE_SECOND;
6331 /* undo the last destructive operation */
6332 if (Undo (true))
6333 SetChangedFlag (true);
6335 else if (function)
6337 switch (GetFunctionID (function))
6339 /* clear 'undo objects' list */
6340 case F_ClearList:
6341 ClearUndoList (false);
6342 break;
6345 notify_crosshair_change (true);
6346 return 0;
6349 /* --------------------------------------------------------------------------- */
6351 static const char redo_syntax[] = N_("Redo()");
6353 static const char redo_help[] = N_("Redo recent \"undo\" operations.");
6355 /* %start-doc actions Redo
6357 This routine allows you to recover from the last undo command. You
6358 might want to do this if you thought that undo was going to revert
6359 something other than what it actually did (in case you are confused
6360 about which operations are un-doable), or if you have been backing up
6361 through a long undo list and over-shoot your stopping point. Any
6362 change that is made since the undo in question will trim the redo
6363 list. For example if you add ten lines, then undo three of them you
6364 could use redo to put them back, but if you move a line on the board
6365 before performing the redo, you will lose the ability to "redo" the
6366 three "undone" lines.
6368 %end-doc */
6370 static int
6371 ActionRedo (int argc, char **argv, Coord x, Coord y)
6373 if (((Settings.Mode == POLYGON_MODE ||
6374 Settings.Mode == POLYGONHOLE_MODE) &&
6375 Crosshair.AttachedPolygon.PointN) ||
6376 Crosshair.AttachedLine.State == STATE_SECOND)
6377 return 1;
6378 notify_crosshair_change (false);
6379 if (Redo (true))
6381 SetChangedFlag (true);
6382 if (Settings.Mode == LINE_MODE &&
6383 Crosshair.AttachedLine.State != STATE_FIRST)
6385 LineType *line = g_list_last (CURRENT->Line)->data;
6386 Crosshair.AttachedLine.Point1.X =
6387 Crosshair.AttachedLine.Point2.X = line->Point2.X;
6388 Crosshair.AttachedLine.Point1.Y =
6389 Crosshair.AttachedLine.Point2.Y = line->Point2.Y;
6390 addedLines++;
6393 notify_crosshair_change (true);
6394 return 0;
6397 /* --------------------------------------------------------------------------- */
6399 static const char polygon_syntax[] = N_("Polygon(Close|PreviousPoint)");
6401 static const char polygon_help[] = N_("Some polygon related stuff.");
6403 /* %start-doc actions Polygon
6405 Polygons need a special action routine to make life easier.
6407 @table @code
6409 @item Close
6410 Creates the final segment of the polygon. This may fail if clipping
6411 to 45 degree lines is switched on, in which case a warning is issued.
6413 @item PreviousPoint
6414 Resets the newly entered corner to the previous one. The Undo action
6415 will call Polygon(PreviousPoint) when appropriate to do so.
6417 @end table
6419 %end-doc */
6421 static int
6422 ActionPolygon (int argc, char **argv, Coord x, Coord y)
6424 char *function = ARG (0);
6425 if (function && Settings.Mode == POLYGON_MODE)
6427 notify_crosshair_change (false);
6428 switch (GetFunctionID (function))
6430 /* close open polygon if possible */
6431 case F_Close:
6432 ClosePolygon ();
6433 break;
6435 /* go back to the previous point */
6436 case F_PreviousPoint:
6437 GoToPreviousPoint ();
6438 break;
6440 notify_crosshair_change (true);
6442 return 0;
6445 /* --------------------------------------------------------------------------- */
6447 static const char routestyle_syntax[] = N_("RouteStyle(1|2|3|4)");
6449 static const char routestyle_help[] =
6450 N_("Copies the indicated routing style into the current sizes.");
6452 /* %start-doc actions RouteStyle
6454 %end-doc */
6456 static int
6457 ActionRouteStyle (int argc, char **argv, Coord x, Coord y)
6459 char *str = ARG (0);
6460 RouteStyleType *rts;
6461 int number;
6463 if (str)
6465 number = atoi (str);
6466 if (number > 0 && number <= NUM_STYLES)
6468 rts = &PCB->RouteStyle[number - 1];
6469 SetLineSize (rts->Thick);
6470 SetViaSize (rts->Diameter, true);
6471 SetViaDrillingHole (rts->Hole, true);
6472 SetKeepawayWidth (rts->Keepaway);
6473 hid_action("RouteStylesChanged");
6476 return 0;
6480 /* --------------------------------------------------------------------------- */
6482 static const char moveobject_syntax[] = N_("MoveObject(X,Y,dim)");
6484 static const char moveobject_help[] =
6485 N_("Moves the object under the crosshair.");
6487 /* %start-doc actions MoveObject
6489 The @code{X} and @code{Y} are treated like @code{delta} is for many
6490 other objects. For each, if it's prefixed by @code{+} or @code{-},
6491 then that amount is relative. Otherwise, it's absolute. Units can be
6492 @code{mil} or @code{mm}; if unspecified, units are PCB's internal
6493 units, currently 1/100 mil.
6495 %end-doc */
6497 static int
6498 ActionMoveObject (int argc, char **argv, Coord x, Coord y)
6500 char *x_str = ARG (0);
6501 char *y_str = ARG (1);
6502 char *units = ARG (2);
6503 Coord nx, ny;
6504 bool absolute1, absolute2;
6505 void *ptr1, *ptr2, *ptr3;
6506 int type;
6508 ny = GetValue (y_str, units, &absolute1);
6509 nx = GetValue (x_str, units, &absolute2);
6511 type = SearchScreen (x, y, MOVE_TYPES, &ptr1, &ptr2, &ptr3);
6512 if (type == NO_TYPE)
6514 Message (_("Nothing found under crosshair\n"));
6515 return 1;
6517 if (absolute1)
6518 nx -= x;
6519 if (absolute2)
6520 ny -= y;
6521 Crosshair.AttachedObject.RubberbandN = 0;
6522 if (TEST_FLAG (RUBBERBANDFLAG, PCB))
6523 LookupRubberbandLines (type, ptr1, ptr2, ptr3);
6524 if (type == ELEMENT_TYPE)
6525 LookupRatLines (type, ptr1, ptr2, ptr3);
6526 MoveObjectAndRubberband (type, ptr1, ptr2, ptr3, nx, ny);
6527 SetChangedFlag (true);
6528 return 0;
6531 /* --------------------------------------------------------------------------- */
6533 static const char movetocurrentlayer_syntax[] =
6534 N_("MoveToCurrentLayer(Object|SelectedObjects)");
6536 static const char movetocurrentlayer_help[] =
6537 N_("Moves objects to the current layer.");
6539 /* %start-doc actions MoveToCurrentLayer
6541 Note that moving an element from a component layer to a solder layer,
6542 or from solder to component, won't automatically flip it. Use the
6543 @code{Flip()} action to do that.
6545 %end-doc */
6547 static int
6548 ActionMoveToCurrentLayer (int argc, char **argv, Coord x, Coord y)
6550 char *function = ARG (0);
6551 if (function)
6553 switch (GetFunctionID (function))
6555 case F_Object:
6557 int type;
6558 void *ptr1, *ptr2, *ptr3;
6560 gui->get_coords (_("Select an Object"), &x, &y);
6561 if ((type =
6562 SearchScreen (x, y, MOVETOLAYER_TYPES,
6563 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
6564 if (MoveObjectToLayer (type, ptr1, ptr2, ptr3, CURRENT, false))
6565 SetChangedFlag (true);
6566 break;
6569 case F_SelectedObjects:
6570 case F_Selected:
6571 if (MoveSelectedObjectsToLayer (CURRENT))
6572 SetChangedFlag (true);
6573 break;
6576 return 0;
6580 static const char setsame_syntax[] = N_("SetSame()");
6582 static const char setsame_help[] =
6583 N_("Sets current layer and sizes to match indicated item.");
6585 /* %start-doc actions SetSame
6587 When invoked over any line, arc, polygon, or via, this changes the
6588 current layer to be the layer that item is on, and changes the current
6589 sizes (thickness, keepaway, drill, etc) according to that item.
6591 %end-doc */
6593 static int
6594 ActionSetSame (int argc, char **argv, Coord x, Coord y)
6596 void *ptr1, *ptr2, *ptr3;
6597 int type;
6598 LayerType *layer = CURRENT;
6600 type = SearchScreen (x, y, CLONE_TYPES, &ptr1, &ptr2, &ptr3);
6601 /* set layer current and size from line or arc */
6602 switch (type)
6604 case LINE_TYPE:
6605 notify_crosshair_change (false);
6606 Settings.LineThickness = ((LineType *) ptr2)->Thickness;
6607 Settings.Keepaway = ((LineType *) ptr2)->Clearance / 2;
6608 layer = (LayerType *) ptr1;
6609 if (Settings.Mode != LINE_MODE)
6610 SetMode (LINE_MODE);
6611 notify_crosshair_change (true);
6612 hid_action ("RouteStylesChanged");
6613 break;
6615 case ARC_TYPE:
6616 notify_crosshair_change (false);
6617 Settings.LineThickness = ((ArcType *) ptr2)->Thickness;
6618 Settings.Keepaway = ((ArcType *) ptr2)->Clearance / 2;
6619 layer = (LayerType *) ptr1;
6620 if (Settings.Mode != ARC_MODE)
6621 SetMode (ARC_MODE);
6622 notify_crosshair_change (true);
6623 hid_action ("RouteStylesChanged");
6624 break;
6626 case POLYGON_TYPE:
6627 layer = (LayerType *) ptr1;
6628 break;
6630 case VIA_TYPE:
6631 notify_crosshair_change (false);
6632 Settings.ViaThickness = ((PinType *) ptr2)->Thickness;
6633 Settings.ViaDrillingHole = ((PinType *) ptr2)->DrillingHole;
6634 Settings.Keepaway = ((PinType *) ptr2)->Clearance / 2;
6635 if (Settings.Mode != VIA_MODE)
6636 SetMode (VIA_MODE);
6637 notify_crosshair_change (true);
6638 hid_action ("RouteStylesChanged");
6639 break;
6641 default:
6642 return 1;
6644 if (layer != CURRENT)
6646 ChangeGroupVisibility (GetLayerNumber (PCB->Data, layer), true, true);
6647 Redraw ();
6649 return 0;
6653 /* --------------------------------------------------------------------------- */
6655 static const char setflag_syntax[] =
6656 N_("SetFlag(Object|Selected|SelectedObjects, flag)\n"
6657 "SetFlag(SelectedLines|SelectedPins|SelectedVias, flag)\n"
6658 "SetFlag(SelectedPads|SelectedTexts|SelectedNames, flag)\n"
6659 "SetFlag(SelectedElements, flag)\n"
6660 "flag = square | octagon | thermal | join");
6662 static const char setflag_help[] = N_("Sets flags on objects.");
6664 /* %start-doc actions SetFlag
6666 Turns the given flag on, regardless of its previous setting. See
6667 @code{ChangeFlag}.
6669 @example
6670 SetFlag(SelectedPins,thermal)
6671 @end example
6673 %end-doc */
6675 static int
6676 ActionSetFlag (int argc, char **argv, Coord x, Coord y)
6678 char *function = ARG (0);
6679 char *flag = ARG (1);
6680 ChangeFlag (function, flag, 1, "SetFlag");
6681 return 0;
6684 /* --------------------------------------------------------------------------- */
6686 static const char clrflag_syntax[] =
6687 N_("ClrFlag(Object|Selected|SelectedObjects, flag)\n"
6688 "ClrFlag(SelectedLines|SelectedPins|SelectedVias, flag)\n"
6689 "ClrFlag(SelectedPads|SelectedTexts|SelectedNames, flag)\n"
6690 "ClrFlag(SelectedElements, flag)\n"
6691 "flag = square | octagon | thermal | join");
6693 static const char clrflag_help[] = N_("Clears flags on objects.");
6695 /* %start-doc actions ClrFlag
6697 Turns the given flag off, regardless of its previous setting. See
6698 @code{ChangeFlag}.
6700 @example
6701 ClrFlag(SelectedLines,join)
6702 @end example
6704 %end-doc */
6706 static int
6707 ActionClrFlag (int argc, char **argv, Coord x, Coord y)
6709 char *function = ARG (0);
6710 char *flag = ARG (1);
6711 ChangeFlag (function, flag, 0, "ClrFlag");
6712 return 0;
6715 /* --------------------------------------------------------------------------- */
6717 static const char changeflag_syntax[] =
6718 N_("ChangeFlag(Object|Selected|SelectedObjects, flag, value)\n"
6719 "ChangeFlag(SelectedLines|SelectedPins|SelectedVias, flag, value)\n"
6720 "ChangeFlag(SelectedPads|SelectedTexts|SelectedNames, flag, value)\n"
6721 "ChangeFlag(SelectedElements, flag, value)\n"
6722 "flag = square | octagon | thermal | join\n"
6723 "value = 0 | 1");
6725 static const char changeflag_help[] = N_("Sets or clears flags on objects.");
6727 /* %start-doc actions ChangeFlag
6729 Toggles the given flag on the indicated object(s). The flag may be
6730 one of the flags listed above (square, octagon, thermal, join). The
6731 value may be the number 0 or 1. If the value is 0, the flag is
6732 cleared. If the value is 1, the flag is set.
6734 %end-doc */
6736 static int
6737 ActionChangeFlag (int argc, char **argv, Coord x, Coord y)
6739 char *function = ARG (0);
6740 char *flag = ARG (1);
6741 int value = argc > 2 ? atoi (argv[2]) : -1;
6742 if (value != 0 && value != 1)
6743 AFAIL (changeflag);
6745 ChangeFlag (function, flag, value, "ChangeFlag");
6746 return 0;
6750 static void
6751 ChangeFlag (char *what, char *flag_name, int value, char *cmd_name)
6753 bool (*set_object) (int, void *, void *, void *);
6754 bool (*set_selected) (int);
6756 if (NSTRCMP (flag_name, "square") == 0)
6758 set_object = value ? SetObjectSquare : ClrObjectSquare;
6759 set_selected = value ? SetSelectedSquare : ClrSelectedSquare;
6761 else if (NSTRCMP (flag_name, "octagon") == 0)
6763 set_object = value ? SetObjectOctagon : ClrObjectOctagon;
6764 set_selected = value ? SetSelectedOctagon : ClrSelectedOctagon;
6766 else if (NSTRCMP (flag_name, "join") == 0)
6768 /* Note: these are backwards, because the flag is "clear" but
6769 the command is "join". */
6770 set_object = value ? ClrObjectJoin : SetObjectJoin;
6771 set_selected = value ? ClrSelectedJoin : SetSelectedJoin;
6773 else
6775 Message (_("%s(): Flag \"%s\" is not valid\n"), cmd_name, flag_name);
6776 return;
6779 switch (GetFunctionID (what))
6781 case F_Object:
6783 int type;
6784 void *ptr1, *ptr2, *ptr3;
6786 if ((type =
6787 SearchScreen (Crosshair.X, Crosshair.Y, CHANGESIZE_TYPES,
6788 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
6789 if (TEST_FLAG (LOCKFLAG, (PinType *) ptr2))
6790 Message (_("Sorry, the object is locked\n"));
6791 if (set_object (type, ptr1, ptr2, ptr3))
6792 SetChangedFlag (true);
6793 break;
6796 case F_SelectedVias:
6797 if (set_selected (VIA_TYPE))
6798 SetChangedFlag (true);
6799 break;
6801 case F_SelectedPins:
6802 if (set_selected (PIN_TYPE))
6803 SetChangedFlag (true);
6804 break;
6806 case F_SelectedPads:
6807 if (set_selected (PAD_TYPE))
6808 SetChangedFlag (true);
6809 break;
6811 case F_SelectedLines:
6812 if (set_selected (LINE_TYPE))
6813 SetChangedFlag (true);
6814 break;
6816 case F_SelectedTexts:
6817 if (set_selected (TEXT_TYPE))
6818 SetChangedFlag (true);
6819 break;
6821 case F_SelectedNames:
6822 if (set_selected (ELEMENTNAME_TYPE))
6823 SetChangedFlag (true);
6824 break;
6826 case F_SelectedElements:
6827 if (set_selected (ELEMENT_TYPE))
6828 SetChangedFlag (true);
6829 break;
6831 case F_Selected:
6832 case F_SelectedObjects:
6833 if (set_selected (CHANGESIZE_TYPES))
6834 SetChangedFlag (true);
6835 break;
6839 /* --------------------------------------------------------------------------- */
6841 static const char executefile_syntax[] = N_("ExecuteFile(filename)");
6843 static const char executefile_help[] = N_("Run actions from the given file.");
6845 /* %start-doc actions ExecuteFile
6847 Lines starting with @code{#} are ignored.
6849 %end-doc */
6851 static int
6852 ActionExecuteFile (int argc, char **argv, Coord x, Coord y)
6854 FILE *fp;
6855 char *fname;
6856 char line[256];
6857 int n = 0;
6858 char *sp;
6860 if (argc != 1)
6861 AFAIL (executefile);
6863 fname = argv[0];
6865 if ((fp = fopen (fname, "r")) == NULL)
6867 fprintf (stderr, _("Could not open actions file \"%s\".\n"), fname);
6868 return 1;
6871 defer_updates = 1;
6872 defer_needs_update = 0;
6873 while (fgets (line, sizeof (line), fp) != NULL)
6875 n++;
6876 sp = line;
6878 /* eat the trailing newline */
6879 while (*sp && *sp != '\r' && *sp != '\n')
6880 sp++;
6881 *sp = '\0';
6883 /* eat leading spaces and tabs */
6884 sp = line;
6885 while (*sp && (*sp == ' ' || *sp == '\t'))
6886 sp++;
6889 * if we have anything left and its not a comment line
6890 * then execute it
6893 if (*sp && *sp != '#')
6895 /*Message ("%s : line %-3d : \"%s\"\n", fname, n, sp);*/
6896 hid_parse_actions (sp);
6900 defer_updates = 0;
6901 if (defer_needs_update)
6903 IncrementUndoSerialNumber ();
6904 gui->invalidate_all ();
6906 fclose (fp);
6907 return 0;
6910 /* --------------------------------------------------------------------------- */
6912 static int
6913 ActionPSCalib (int argc, char **argv, Coord x, Coord y)
6915 HID *ps = hid_find_exporter ("ps");
6916 ps->calibrate (0.0,0.0);
6917 return 0;
6920 /* --------------------------------------------------------------------------- */
6922 static ElementType *element_cache = NULL;
6924 static ElementType *
6925 find_element_by_refdes (char *refdes)
6927 if (element_cache
6928 && NAMEONPCB_NAME(element_cache)
6929 && strcmp (NAMEONPCB_NAME(element_cache), refdes) == 0)
6930 return element_cache;
6932 ELEMENT_LOOP (PCB->Data);
6934 if (NAMEONPCB_NAME(element)
6935 && strcmp (NAMEONPCB_NAME(element), refdes) == 0)
6937 element_cache = element;
6938 return element_cache;
6941 END_LOOP;
6942 return NULL;
6945 static AttributeType *
6946 lookup_attr (AttributeListType *list, const char *name)
6948 int i;
6949 for (i=0; i<list->Number; i++)
6950 if (strcmp (list->List[i].name, name) == 0)
6951 return & list->List[i];
6952 return NULL;
6955 static void
6956 delete_attr (AttributeListType *list, AttributeType *attr)
6958 int idx = attr - list->List;
6959 if (idx < 0 || idx >= list->Number)
6960 return;
6961 if (list->Number - idx > 1)
6962 memmove (attr, attr+1, (list->Number - idx - 1) * sizeof(AttributeType));
6963 list->Number --;
6966 /* ---------------------------------------------------------------- */
6967 static const char elementlist_syntax[] =
6968 N_("ElementList(Start|Done|Need,<refdes>,<footprint>,<value>)");
6970 static const char elementlist_help[] =
6971 N_("Adds the given element if it doesn't already exist.");
6973 /* %start-doc actions elementlist
6975 @table @code
6977 @item Start
6978 Indicates the start of an element list; call this before any Need
6979 actions.
6981 @item Need
6982 Searches the board for an element with a matching refdes.
6984 If found, the value and footprint are updated.
6986 If not found, a new element is created with the given footprint and value.
6988 @item Done
6989 Compares the list of elements needed since the most recent
6990 @code{start} with the list of elements actually on the board. Any
6991 elements that weren't listed are selected, so that the user may delete
6992 them.
6994 @end table
6996 %end-doc */
6998 static int number_of_footprints_not_found;
7000 static int
7001 parse_layout_attribute_units (char *name, int def)
7003 const char *as = AttributeGet (PCB, name);
7004 if (!as)
7005 return def;
7006 return GetValue (as, NULL, NULL);
7009 static int
7010 ActionElementList (int argc, char **argv, Coord x, Coord y)
7012 ElementType *e = NULL;
7013 char *refdes, *value, *footprint, *old;
7014 char *args[3];
7015 char *function = argv[0];
7017 #ifdef DEBUG
7018 printf("Entered ActionElementList, executing function %s\n", function);
7019 #endif
7021 if (strcasecmp (function, "start") == 0)
7023 ELEMENT_LOOP (PCB->Data);
7025 CLEAR_FLAG (FOUNDFLAG, element);
7027 END_LOOP;
7028 element_cache = NULL;
7029 number_of_footprints_not_found = 0;
7030 return 0;
7033 if (strcasecmp (function, "done") == 0)
7035 ELEMENT_LOOP (PCB->Data);
7037 if (TEST_FLAG (FOUNDFLAG, element))
7039 CLEAR_FLAG (FOUNDFLAG, element);
7041 else if (! EMPTY_STRING_P (NAMEONPCB_NAME (element)))
7043 /* Unnamed elements should remain untouched */
7044 SET_FLAG (SELECTEDFLAG, element);
7047 END_LOOP;
7048 if (number_of_footprints_not_found > 0)
7049 gui->confirm_dialog (_("Not all requested footprints were found.\n"
7050 "See the message log for details"),
7051 "Ok", NULL);
7052 return 0;
7055 if (strcasecmp (function, "need") != 0)
7056 AFAIL (elementlist);
7058 if (argc != 4)
7059 AFAIL (elementlist);
7061 argc --;
7062 argv ++;
7064 refdes = ARG(0);
7065 footprint = ARG(1);
7066 value = ARG(2);
7068 args[0] = footprint;
7069 args[1] = refdes;
7070 args[2] = value;
7072 #ifdef DEBUG
7073 printf(" ... footprint = %s\n", footprint);
7074 printf(" ... refdes = %s\n", refdes);
7075 printf(" ... value = %s\n", value);
7076 #endif
7078 e = find_element_by_refdes (refdes);
7080 if (!e)
7082 Coord nx, ny, d;
7084 #ifdef DEBUG
7085 printf(" ... Footprint not on board, need to add it.\n");
7086 #endif
7087 /* Not on board, need to add it. */
7088 if (LoadFootprint(argc, args, x, y))
7090 number_of_footprints_not_found ++;
7091 return 1;
7094 nx = PCB->MaxWidth / 2;
7095 ny = PCB->MaxHeight / 2;
7096 d = MIN (PCB->MaxWidth, PCB->MaxHeight) / 10;
7098 nx = parse_layout_attribute_units ("import::newX", nx);
7099 ny = parse_layout_attribute_units ("import::newY", ny);
7100 d = parse_layout_attribute_units ("import::disperse", d);
7102 if (d > 0)
7104 nx += rand () % (d*2) - d;
7105 ny += rand () % (d*2) - d;
7108 if (nx < 0)
7109 nx = 0;
7110 if (nx >= PCB->MaxWidth)
7111 nx = PCB->MaxWidth - 1;
7112 if (ny < 0)
7113 ny = 0;
7114 if (ny >= PCB->MaxHeight)
7115 ny = PCB->MaxHeight - 1;
7117 /* Place components onto center of board. */
7118 if (CopyPastebufferToLayout (nx, ny))
7119 SetChangedFlag (true);
7122 else if (e && DESCRIPTION_NAME(e) && strcmp (DESCRIPTION_NAME(e), footprint) != 0)
7124 #ifdef DEBUG
7125 printf(" ... Footprint on board, but different from footprint loaded.\n");
7126 #endif
7127 int er, pr, i;
7128 Coord mx, my;
7129 ElementType *pe;
7131 /* Different footprint, we need to swap them out. */
7132 if (LoadFootprint(argc, args, x, y))
7134 number_of_footprints_not_found ++;
7135 return 1;
7138 er = ElementOrientation (e);
7139 pe = PASTEBUFFER->Data->Element->data;
7140 if (!FRONT (e))
7141 MirrorElementCoordinates (PASTEBUFFER->Data, pe, pe->MarkY*2 - PCB->MaxHeight);
7142 pr = ElementOrientation (pe);
7144 mx = e->MarkX;
7145 my = e->MarkY;
7147 if (er != pr)
7148 RotateElementLowLevel (PASTEBUFFER->Data, pe, pe->MarkX, pe->MarkY, (er-pr+4)%4);
7150 for (i=0; i<MAX_ELEMENTNAMES; i++)
7152 pe->Name[i].X = e->Name[i].X - mx + pe->MarkX ;
7153 pe->Name[i].Y = e->Name[i].Y - my + pe->MarkY ;
7154 pe->Name[i].Direction = e->Name[i].Direction;
7155 pe->Name[i].Scale = e->Name[i].Scale;
7158 RemoveElement (e);
7160 if (CopyPastebufferToLayout (mx, my))
7161 SetChangedFlag (true);
7164 /* Now reload footprint */
7165 element_cache = NULL;
7166 e = find_element_by_refdes (refdes);
7168 old = ChangeElementText (PCB, PCB->Data, e, NAMEONPCB_INDEX, strdup (refdes));
7169 if (old)
7170 free(old);
7171 old = ChangeElementText (PCB, PCB->Data, e, VALUE_INDEX, strdup (value));
7172 if (old)
7173 free(old);
7175 SET_FLAG (FOUNDFLAG, e);
7177 #ifdef DEBUG
7178 printf(" ... Leaving ActionElementList.\n");
7179 #endif
7181 return 0;
7184 /* ---------------------------------------------------------------- */
7185 static const char elementsetattr_syntax[] =
7186 N_("ElementSetAttr(refdes,name[,value])");
7188 static const char elementsetattr_help[] =
7189 N_("Sets or clears an element-specific attribute.");
7191 /* %start-doc actions elementsetattr
7193 If a value is specified, the named attribute is added (if not already
7194 present) or changed (if it is) to the given value. If the value is
7195 not specified, the given attribute is removed if present.
7197 %end-doc */
7199 static int
7200 ActionElementSetAttr (int argc, char **argv, Coord x, Coord y)
7202 ElementType *e = NULL;
7203 char *refdes, *name, *value;
7204 AttributeType *attr;
7206 if (argc < 2)
7208 AFAIL (changepinname);
7211 refdes = argv[0];
7212 name = argv[1];
7213 value = ARG(2);
7215 ELEMENT_LOOP (PCB->Data);
7217 if (NSTRCMP (refdes, NAMEONPCB_NAME (element)) == 0)
7219 e = element;
7220 break;
7223 END_LOOP;
7225 if (!e)
7227 Message(_("Cannot change attribute of %s - element not found\n"), refdes);
7228 return 1;
7231 attr = lookup_attr (&e->Attributes, name);
7233 if (attr && value)
7235 free (attr->value);
7236 attr->value = strdup (value);
7238 if (attr && ! value)
7240 delete_attr (& e->Attributes, attr);
7242 if (!attr && value)
7244 CreateNewAttribute (& e->Attributes, name, value);
7247 return 0;
7250 /* ---------------------------------------------------------------- */
7251 static const char execcommand_syntax[] = N_("ExecCommand(command)");
7253 static const char execcommand_help[] = N_("Runs a command.");
7255 /* %start-doc actions execcommand
7257 Runs the given command, which is a system executable.
7259 %end-doc */
7261 static int
7262 ActionExecCommand (int argc, char **argv, Coord x, Coord y)
7264 char *command;
7266 if (argc < 1)
7268 AFAIL (execcommand);
7271 command = ARG(0);
7273 if (system (command))
7274 return 1;
7275 return 0;
7278 /* ---------------------------------------------------------------- */
7280 static int
7281 pcb_spawnvp (char **argv)
7283 #ifdef HAVE__SPAWNVP
7284 int result = _spawnvp (_P_WAIT, argv[0], (const char * const *) argv);
7285 if (result == -1)
7286 return 1;
7287 else
7288 return 0;
7289 #else
7290 int pid;
7291 pid = fork ();
7292 if (pid < 0)
7294 /* error */
7295 Message(_("Cannot fork!"));
7296 return 1;
7298 else if (pid == 0)
7300 /* Child */
7301 execvp (argv[0], argv);
7302 exit(1);
7304 else
7306 int rv;
7307 /* Parent */
7308 wait (&rv);
7310 return 0;
7311 #endif
7314 /* ---------------------------------------------------------------- */
7316 * Creates a new temporary file name. Hopefully the operating system
7317 * provides a mkdtemp() function to securily create a temporary
7318 * directory with mode 0700. If so then that directory is created and
7319 * the returned string is made up of the directory plus the name
7320 * variable. For example:
7322 * tempfile_name_new ("myfile") might return
7323 * "/var/tmp/pcb.123456/myfile".
7325 * If mkdtemp() is not available then 'name' is ignored and the
7326 * insecure tmpnam() function is used.
7328 * Files/names created with tempfile_name_new() should be unlinked
7329 * with tempfile_unlink to make sure the temporary directory is also
7330 * removed when mkdtemp() is used.
7332 static char *
7333 tempfile_name_new (char * name)
7335 char *tmpfile = NULL;
7336 #ifdef HAVE_MKDTEMP
7337 char *tmpdir, *mytmpdir;
7338 size_t len;
7339 #endif
7341 assert ( name != NULL );
7343 #ifdef HAVE_MKDTEMP
7344 #define TEMPLATE "pcb.XXXXXXXX"
7347 tmpdir = getenv ("TMPDIR");
7349 /* FIXME -- what about win32? */
7350 if (tmpdir == NULL) {
7351 tmpdir = "/tmp";
7354 mytmpdir = (char *) malloc (sizeof(char) *
7355 (strlen (tmpdir) +
7357 strlen (TEMPLATE) +
7358 1));
7359 if (mytmpdir == NULL) {
7360 fprintf (stderr, "%s(): malloc failed()\n", __FUNCTION__);
7361 exit (1);
7364 *mytmpdir = '\0';
7365 (void)strcat (mytmpdir, tmpdir);
7366 (void)strcat (mytmpdir, PCB_DIR_SEPARATOR_S);
7367 (void)strcat (mytmpdir, TEMPLATE);
7368 if (mkdtemp (mytmpdir) == NULL) {
7369 fprintf (stderr, "%s(): mkdtemp (\"%s\") failed\n", __FUNCTION__, mytmpdir);
7370 free (mytmpdir);
7371 return NULL;
7375 len = strlen (mytmpdir) + /* the temp directory name */
7376 1 + /* the directory sep. */
7377 strlen (name) + /* the file name */
7378 1 /* the \0 termination */
7381 tmpfile = (char *) malloc (sizeof (char) * len);
7383 *tmpfile = '\0';
7384 (void)strcat (tmpfile, mytmpdir);
7385 (void)strcat (tmpfile, PCB_DIR_SEPARATOR_S);
7386 (void)strcat (tmpfile, name);
7388 free (mytmpdir);
7389 #undef TEMPLATE
7390 #else
7392 * tmpnam() uses a static buffer so strdup() the result right away
7393 * in case someone decides to create multiple temp names.
7395 tmpfile = strdup (tmpnam (NULL));
7396 #ifdef __WIN32__
7398 /* Guile doesn't like \ separators */
7399 char *c;
7400 for (c = tmpfile; *c; c++)
7401 if (*c == '\\')
7402 *c = '/';
7404 #endif
7405 #endif
7407 return tmpfile;
7410 /* ---------------------------------------------------------------- */
7412 * Unlink a temporary file. If we have mkdtemp() then our temp file
7413 * lives in a temporary directory and we need to remove that directory
7414 * too.
7416 static int
7417 tempfile_unlink (char * name)
7419 #ifdef DEBUG
7420 /* SDB says: Want to keep old temp files for examiniation when debugging */
7421 return 0;
7422 #endif
7424 #ifdef HAVE_MKDTEMP
7425 int e, rc2 = 0;
7426 char *dname;
7428 unlink (name);
7429 /* it is possible that the file was never created so it is OK if the
7430 unlink fails */
7432 /* now figure out the directory name to remove */
7433 e = strlen (name) - 1;
7434 while (e > 0 && name[e] != PCB_DIR_SEPARATOR_C) {e--;}
7436 dname = strdup (name);
7437 dname[e] = '\0';
7440 * at this point, e *should* point to the end of the directory part
7441 * but lets make sure.
7443 if (e > 0) {
7444 rc2 = rmdir (dname);
7445 if (rc2 != 0) {
7446 perror (dname);
7449 } else {
7450 fprintf (stderr, _("%s(): Unable to determine temp directory name from the temp file\n"),
7451 __FUNCTION__);
7452 fprintf (stderr, "%s(): \"%s\"\n",
7453 __FUNCTION__, name);
7454 rc2 = -1;
7457 /* name was allocated with malloc */
7458 free (dname);
7459 free (name);
7462 * FIXME - should also return -1 if the temp file exists and was not
7463 * removed.
7465 if (rc2 != 0) {
7466 return -1;
7469 #else
7470 int rc = unlink (name);
7472 if (rc != 0) {
7473 fprintf (stderr, _("Failed to unlink \"%s\"\n"), name);
7474 free (name);
7475 return rc;
7477 free (name);
7479 #endif
7481 return 0;
7484 /* ---------------------------------------------------------------- */
7485 static const char import_syntax[] =
7486 N_("Import()\n"
7487 "Import([gnetlist|make[,source,source,...]])\n"
7488 "Import(setnewpoint[,(mark|center|X,Y)])\n"
7489 "Import(setdisperse,D,units)\n");
7491 static const char import_help[] = N_("Import schematics.");
7493 /* %start-doc actions Import
7495 Imports element and netlist data from the schematics (or some other
7496 source). The first parameter, which is optional, is the mode. If not
7497 specified, the @code{import::mode} attribute in the PCB is used.
7498 @code{gnetlist} means gnetlist is used to obtain the information from
7499 the schematics. @code{make} invokes @code{make}, assuming the user
7500 has a @code{Makefile} in the current directory. The @code{Makefile}
7501 will be invoked with the following variables set:
7503 @table @code
7505 @item PCB
7506 The name of the .pcb file
7508 @item SRCLIST
7509 A space-separated list of source files
7511 @item OUT
7512 The name of the file in which to put the command script, which may
7513 contain any @pcb{} actions. By default, this is a temporary file
7514 selected by @pcb{}, but if you specify an @code{import::outfile}
7515 attribute, that file name is used instead (and not automatically
7516 deleted afterwards).
7518 @end table
7520 The target specified to be built is the first of these that apply:
7522 @itemize @bullet
7524 @item
7525 The target specified by an @code{import::target} attribute.
7527 @item
7528 The output file specified by an @code{import::outfile} attribute.
7530 @item
7531 If nothing else is specified, the target is @code{pcb_import}.
7533 @end itemize
7535 If you specify an @code{import::makefile} attribute, then "-f <that
7536 file>" will be added to the command line.
7538 If you specify the mode, you may also specify the source files
7539 (schematics). If you do not specify any, the list of schematics is
7540 obtained by reading the @code{import::src@var{N}} attributes (like
7541 @code{import::src0}, @code{import::src1}, etc).
7543 For compatibility with future extensions to the import file format,
7544 the generated file @emph{must not} start with the two characters
7545 @code{#%}.
7547 If a temporary file is needed the @code{TMPDIR} environment variable
7548 is used to select its location.
7550 Note that the programs @code{gnetlist} and @code{make} may be
7551 overridden by the user via the @code{make-program} and @code{gnetlist}
7552 @code{pcb} settings (i.e. in @code{~/.pcb/settings} or on the command
7553 line).
7555 If @pcb{} cannot determine which schematic(s) to import from, the GUI
7556 is called to let user choose (see @code{ImportGUI()}).
7558 Note that Import() doesn't delete anything - after an Import, elements
7559 which shouldn't be on the board are selected and may be removed once
7560 it's determined that the deletion is appropriate.
7562 If @code{Import()} is called with @code{setnewpoint}, then the location
7563 of new components can be specified. This is where parts show up when
7564 they're added to the board. The default is the center of the board.
7566 @table @code
7568 @item Import(setnewpoint)
7570 Prompts the user to click on the board somewhere, uses that point. If
7571 called by a hotkey, uses the current location of the crosshair.
7573 @item Import(setnewpoint,mark)
7575 Uses the location of the mark. If no mark is present, the point is
7576 not changed.
7578 @item Import(setnewpoint,center)
7580 Resets the point to the center of the board.
7582 @item Import(setnewpoint,X,Y,units)
7584 Sets the point to the specific coordinates given. Example:
7585 @code{Import(setnewpoint,50,25,mm)}
7587 @end table
7589 Note that the X and Y locations are stored in attributes named
7590 @code{import::newX} and @code{import::newY} so you could change them
7591 manually if you wished.
7593 Calling @code{Import(setdisperse,D,units)} sets how much the newly
7594 placed elements are dispersed relative to the set point. For example,
7595 @code{Import(setdisperse,10,mm)} will offset each part randomly up to
7596 10mm away from the point. The default dispersion is 1/10th of the
7597 smallest board dimension. Dispersion is saved in the
7598 @code{import::disperse} attribute.
7600 %end-doc */
7602 static int
7603 ActionImport (int argc, char **argv, Coord x, Coord y)
7605 char *mode;
7606 char **sources = NULL;
7607 int nsources = 0;
7609 #ifdef DEBUG
7610 printf("ActionImport: =========== Entering ActionImport ============\n");
7611 #endif
7613 mode = ARG (0);
7615 if (mode && strcasecmp (mode, "setdisperse") == 0)
7617 char *ds, *units;
7618 char buf[50];
7620 ds = ARG (1);
7621 units = ARG (2);
7622 if (!ds)
7624 const char *as = AttributeGet (PCB, "import::disperse");
7625 ds = gui->prompt_for(_("Enter dispersion:"), as ? as : "0");
7627 if (units)
7629 sprintf(buf, "%s%s", ds, units);
7630 AttributePut (PCB, "import::disperse", buf);
7632 else
7633 AttributePut (PCB, "import::disperse", ds);
7634 if (ARG (1) == NULL)
7635 free (ds);
7636 return 0;
7639 if (mode && strcasecmp (mode, "setnewpoint") == 0)
7641 const char *xs, *ys, *units;
7642 Coord x, y;
7643 char buf[50];
7645 xs = ARG (1);
7646 ys = ARG (2);
7647 units = ARG (3);
7649 if (!xs)
7651 gui->get_coords (_("Click on a location"), &x, &y);
7653 else if (strcasecmp (xs, "center") == 0)
7655 AttributeRemove (PCB, "import::newX");
7656 AttributeRemove (PCB, "import::newY");
7657 return 0;
7659 else if (strcasecmp (xs, "mark") == 0)
7661 if (!Marked.status)
7662 return 0;
7664 x = Marked.X;
7665 y = Marked.Y;
7667 else if (ys)
7669 x = GetValue (xs, units, NULL);
7670 y = GetValue (ys, units, NULL);
7672 else
7674 Message (_("Bad syntax for Import(setnewpoint)"));
7675 return 1;
7678 pcb_sprintf (buf, "%$ms", x);
7679 AttributePut (PCB, "import::newX", buf);
7680 pcb_sprintf (buf, "%$ms", y);
7681 AttributePut (PCB, "import::newY", buf);
7682 return 0;
7685 if (! mode)
7686 mode = AttributeGet (PCB, "import::mode");
7687 if (! mode)
7688 mode = "gnetlist";
7690 if (argc > 1)
7692 sources = argv + 1;
7693 nsources = argc - 1;
7696 if (! sources)
7698 char sname[40];
7699 char *src;
7701 nsources = -1;
7702 do {
7703 nsources ++;
7704 sprintf(sname, "import::src%d", nsources);
7705 src = AttributeGet (PCB, sname);
7706 } while (src);
7708 if (nsources > 0)
7710 sources = (char **) malloc ((nsources + 1) * sizeof (char *));
7711 nsources = -1;
7712 do {
7713 nsources ++;
7714 sprintf(sname, "import::src%d", nsources);
7715 src = AttributeGet (PCB, sname);
7716 sources[nsources] = src;
7717 } while (src);
7721 if (! sources)
7723 /* Replace .pcb with .sch and hope for the best. */
7724 char *pcbname = PCB->Filename;
7725 char *schname;
7726 char *dot, *slash, *bslash;
7728 if (!pcbname)
7729 return hid_action("ImportGUI");
7731 schname = (char *) malloc (strlen(pcbname) + 5);
7732 strcpy (schname, pcbname);
7733 dot = strchr (schname, '.');
7734 slash = strchr (schname, '/');
7735 bslash = strchr (schname, '\\');
7736 if (dot && slash && dot < slash)
7737 dot = NULL;
7738 if (dot && bslash && dot < bslash)
7739 dot = NULL;
7740 if (dot)
7741 *dot = 0;
7742 strcat (schname, ".sch");
7744 if (access (schname, F_OK))
7746 free (schname);
7747 return hid_action("ImportGUI");
7750 sources = (char **) malloc (2 * sizeof (char *));
7751 sources[0] = schname;
7752 sources[1] = NULL;
7753 nsources = 1;
7756 if (strcasecmp (mode, "gnetlist") == 0)
7758 char *tmpfile = tempfile_name_new ("gnetlist_output");
7759 char **cmd;
7760 int i;
7762 if (tmpfile == NULL) {
7763 Message (_("Could not create temp file"));
7764 return 1;
7767 cmd = (char **) malloc ((7 + nsources) * sizeof (char *));
7768 cmd[0] = Settings.GnetlistProgram;
7769 cmd[1] = "-g";
7770 cmd[2] = "pcbfwd";
7771 cmd[3] = "-o";
7772 cmd[4] = tmpfile;
7773 cmd[5] = "--";
7774 for (i=0; i<nsources; i++)
7775 cmd[6+i] = sources[i];
7776 cmd[6+nsources] = NULL;
7778 #ifdef DEBUG
7779 printf("ActionImport: =========== About to run gnetlist ============\n");
7780 printf("%s %s %s %s %s %s %s ...\n",
7781 cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6]);
7782 #endif
7784 if (pcb_spawnvp (cmd))
7786 unlink (tmpfile);
7787 return 1;
7790 #ifdef DEBUG
7791 printf("ActionImport: =========== About to run ActionExecuteFile, file = %s ============\n", tmpfile);
7792 #endif
7794 cmd[0] = tmpfile;
7795 cmd[1] = NULL;
7796 ActionExecuteFile (1, cmd, 0, 0);
7798 free (cmd);
7799 tempfile_unlink (tmpfile);
7801 else if (strcasecmp (mode, "make") == 0)
7803 int must_free_tmpfile = 0;
7804 char *tmpfile;
7805 char *cmd[10];
7806 int i;
7807 char *srclist;
7808 int srclen;
7809 char *user_outfile = NULL;
7810 char *user_makefile = NULL;
7811 char *user_target = NULL;
7814 user_outfile = AttributeGet (PCB, "import::outfile");
7815 user_makefile = AttributeGet (PCB, "import::makefile");
7816 user_target = AttributeGet (PCB, "import::target");
7817 if (user_outfile && !user_target)
7818 user_target = user_outfile;
7820 if (user_outfile)
7821 tmpfile = user_outfile;
7822 else
7824 tmpfile = tempfile_name_new ("gnetlist_output");
7825 if (tmpfile == NULL) {
7826 Message (_("Could not create temp file"));
7827 return 1;
7829 must_free_tmpfile = 1;
7832 srclen = sizeof("SRCLIST=") + 2;
7833 for (i=0; i<nsources; i++)
7834 srclen += strlen (sources[i]) + 2;
7835 srclist = (char *) malloc (srclen);
7836 strcpy (srclist, "SRCLIST=");
7837 for (i=0; i<nsources; i++)
7839 if (i)
7840 strcat (srclist, " ");
7841 strcat (srclist, sources[i]);
7844 cmd[0] = Settings.MakeProgram;
7845 cmd[1] = "-s";
7846 cmd[2] = Concat ("PCB=", PCB->Filename, NULL);
7847 cmd[3] = srclist;
7848 cmd[4] = Concat ("OUT=", tmpfile, NULL);
7849 i = 5;
7850 if (user_makefile)
7852 cmd[i++] = "-f";
7853 cmd[i++] = user_makefile;
7855 cmd[i++] = user_target ? user_target : (char *)"pcb_import";
7856 cmd[i++] = NULL;
7858 if (pcb_spawnvp (cmd))
7860 if (must_free_tmpfile)
7861 unlink (tmpfile);
7862 free (cmd[2]);
7863 free (cmd[3]);
7864 free (cmd[4]);
7865 return 1;
7868 cmd[0] = tmpfile;
7869 cmd[1] = NULL;
7870 ActionExecuteFile (1, cmd, 0, 0);
7872 free (cmd[2]);
7873 free (cmd[3]);
7874 free (cmd[4]);
7875 if (must_free_tmpfile)
7876 tempfile_unlink (tmpfile);
7878 else
7880 Message (_("Unknown import mode: %s\n"), mode);
7881 return 1;
7884 DeleteRats (false);
7885 AddAllRats (false, NULL);
7887 #ifdef DEBUG
7888 printf("ActionImport: =========== Leaving ActionImport ============\n");
7889 #endif
7891 return 0;
7894 /* ------------------------------------------------------------ */
7896 static const char attributes_syntax[] =
7897 N_("Attributes(Layout|Layer|Element)\n"
7898 "Attributes(Layer,layername)");
7900 static const char attributes_help[] =
7901 N_("Let the user edit the attributes of the layout, current or given\n"
7902 "layer, or selected element.");
7904 /* %start-doc actions Attributes
7906 This just pops up a dialog letting the user edit the attributes of the
7907 pcb, an element, or a layer.
7909 %end-doc */
7912 static int
7913 ActionAttributes (int argc, char **argv, Coord x, Coord y)
7915 char *function = ARG (0);
7916 char *layername = ARG (1);
7917 char *buf;
7919 if (!function)
7920 AFAIL (attributes);
7922 if (!gui->edit_attributes)
7924 Message (_("This GUI doesn't support Attribute Editing\n"));
7925 return 1;
7928 switch (GetFunctionID (function))
7930 case F_Layout:
7932 gui->edit_attributes(_("Layout Attributes"), &(PCB->Attributes));
7933 return 0;
7936 case F_Layer:
7938 LayerType *layer = CURRENT;
7939 if (layername)
7941 int i;
7942 layer = NULL;
7943 for (i=0; i<max_copper_layer; i++)
7944 if (strcmp (PCB->Data->Layer[i].Name, layername) == 0)
7946 layer = & (PCB->Data->Layer[i]);
7947 break;
7949 if (layer == NULL)
7951 Message (_("No layer named %s\n"), layername);
7952 return 1;
7955 buf = (char *) malloc (strlen (layer->Name) +
7956 strlen (_("Layer %s Attributes")));
7957 sprintf (buf, _("Layer %s Attributes"), layer->Name);
7958 gui->edit_attributes(buf, &(layer->Attributes));
7959 free (buf);
7960 return 0;
7963 case F_Element:
7965 int n_found = 0;
7966 ElementType *e = NULL;
7967 ELEMENT_LOOP (PCB->Data);
7969 if (TEST_FLAG (SELECTEDFLAG, element))
7971 e = element;
7972 n_found ++;
7975 END_LOOP;
7976 if (n_found > 1)
7978 Message (_("Too many elements selected\n"));
7979 return 1;
7981 if (n_found == 0)
7983 void *ptrtmp;
7984 gui->get_coords (_("Click on an element"), &x, &y);
7985 if ((SearchScreen
7986 (x, y, ELEMENT_TYPE, &ptrtmp,
7987 &ptrtmp, &ptrtmp)) != NO_TYPE)
7988 e = (ElementType *) ptrtmp;
7989 else
7991 Message (_("No element found there\n"));
7992 return 1;
7996 if (NAMEONPCB_NAME(e))
7998 buf = (char *) malloc (strlen (NAMEONPCB_NAME(e)) +
7999 strlen (_("Element %s Attributes")));
8000 sprintf(buf, _("Element %s Attributes"), NAMEONPCB_NAME(e));
8002 else
8004 buf = strdup (_("Unnamed Element Attributes"));
8006 gui->edit_attributes(buf, &(e->Attributes));
8007 free (buf);
8008 break;
8011 default:
8012 AFAIL (attributes);
8015 return 0;
8018 /* --------------------------------------------------------------------------- */
8020 HID_Action action_action_list[] = {
8021 {"AddRats", 0, ActionAddRats,
8022 addrats_help, addrats_syntax}
8024 {"Attributes", 0, ActionAttributes,
8025 attributes_help, attributes_syntax}
8027 {"Atomic", 0, ActionAtomic,
8028 atomic_help, atomic_syntax}
8030 {"AutoPlaceSelected", 0, ActionAutoPlaceSelected,
8031 autoplace_help, autoplace_syntax}
8033 {"AutoRoute", 0, ActionAutoRoute,
8034 autoroute_help, autoroute_syntax}
8036 {"ChangeClearSize", 0, ActionChangeClearSize,
8037 changeclearsize_help, changeclearsize_syntax}
8039 {"ChangeDrillSize", 0, ActionChange2ndSize,
8040 changedrillsize_help, changedrillsize_syntax}
8042 {"ChangeHole", 0, ActionChangeHole,
8043 changehold_help, changehold_syntax}
8045 {"ChangeJoin", 0, ActionChangeJoin,
8046 changejoin_help, changejoin_syntax}
8048 {"ChangeName", 0, ActionChangeName,
8049 changename_help, changename_syntax}
8051 {"ChangePaste", 0, ActionChangePaste,
8052 changepaste_help, changepaste_syntax}
8054 {"ChangePinName", 0, ActionChangePinName,
8055 changepinname_help, changepinname_syntax}
8057 {"ChangeSize", 0, ActionChangeSize,
8058 changesize_help, changesize_syntax}
8060 {"ChangeSquare", 0, ActionChangeSquare,
8061 changesquare_help, changesquare_syntax}
8063 {"ChangeOctagon", 0, ActionChangeOctagon,
8064 changeoctagon_help, changeoctagon_syntax}
8066 {"ClearSquare", 0, ActionClearSquare,
8067 clearsquare_help, clearsquare_syntax}
8069 {"ClearOctagon", 0, ActionClearOctagon,
8070 clearoctagon_help, clearoctagon_syntax}
8072 {"Connection", 0, ActionConnection,
8073 connection_help, connection_syntax}
8075 {"Delete", 0, ActionDelete,
8076 delete_help, delete_syntax}
8078 {"DeleteRats", 0, ActionDeleteRats,
8079 deleterats_help, deleterats_syntax}
8081 {"DisperseElements", 0, ActionDisperseElements,
8082 disperseelements_help, disperseelements_syntax}
8084 {"Display", 0, ActionDisplay,
8085 display_help, display_syntax}
8087 {"DRC", 0, ActionDRCheck,
8088 drc_help, drc_syntax}
8090 {"DumpLibrary", 0, ActionDumpLibrary,
8091 dumplibrary_help, dumplibrary_syntax}
8093 {"ExecuteFile", 0, ActionExecuteFile,
8094 executefile_help, executefile_syntax}
8096 {"Flip", N_("Click on Object or Flip Point"), ActionFlip,
8097 flip_help, flip_syntax}
8099 {"LoadFrom", 0, ActionLoadFrom,
8100 loadfrom_help, loadfrom_syntax}
8102 {"MarkCrosshair", 0, ActionMarkCrosshair,
8103 markcrosshair_help, markcrosshair_syntax}
8105 {"Message", 0, ActionMessage,
8106 message_help, message_syntax}
8108 {"MinMaskGap", 0, ActionMinMaskGap,
8109 minmaskgap_help, minmaskgap_syntax}
8111 {"MinClearGap", 0, ActionMinClearGap,
8112 mincleargap_help, mincleargap_syntax}
8114 {"Mode", 0, ActionMode,
8115 mode_help, mode_syntax}
8117 {"MorphPolygon", 0, ActionMorphPolygon,
8118 morphpolygon_help, morphpolygon_syntax}
8120 {"PasteBuffer", 0, ActionPasteBuffer,
8121 pastebuffer_help, pastebuffer_syntax}
8123 {"Quit", 0, ActionQuit,
8124 quit_help, quit_syntax}
8126 {"RemoveSelected", 0, ActionRemoveSelected,
8127 removeselected_help, removeselected_syntax}
8129 {"Renumber", 0, ActionRenumber,
8130 renumber_help, renumber_syntax}
8132 {"RipUp", 0, ActionRipUp,
8133 ripup_help, ripup_syntax}
8135 {"Select", 0, ActionSelect,
8136 select_help, select_syntax}
8138 {"Unselect", 0, ActionUnselect,
8139 unselect_help, unselect_syntax}
8141 {"SaveSettings", 0, ActionSaveSettings,
8142 savesettings_help, savesettings_syntax}
8144 {"SaveTo", 0, ActionSaveTo,
8145 saveto_help, saveto_syntax}
8147 {"SetSquare", 0, ActionSetSquare,
8148 setsquare_help, setsquare_syntax}
8150 {"SetOctagon", 0, ActionSetOctagon,
8151 setoctagon_help, setoctagon_syntax}
8153 {"SetThermal", 0, ActionSetThermal,
8154 setthermal_help, setthermal_syntax}
8156 {"SetValue", 0, ActionSetValue,
8157 setvalue_help, setvalue_syntax}
8159 {"ToggleHideName", 0, ActionToggleHideName,
8160 togglehidename_help, togglehidename_syntax}
8162 {"Undo", 0, ActionUndo,
8163 undo_help, undo_syntax}
8165 {"Redo", 0, ActionRedo,
8166 redo_help, redo_syntax}
8168 {"SetSame", N_("Select item to use attributes from"), ActionSetSame,
8169 setsame_help, setsame_syntax}
8171 {"SetFlag", 0, ActionSetFlag,
8172 setflag_help, setflag_syntax}
8174 {"ClrFlag", 0, ActionClrFlag,
8175 clrflag_help, clrflag_syntax}
8177 {"ChangeFlag", 0, ActionChangeFlag,
8178 changeflag_help, changeflag_syntax}
8180 {"Polygon", 0, ActionPolygon,
8181 polygon_help, polygon_syntax}
8183 {"RouteStyle", 0, ActionRouteStyle,
8184 routestyle_help, routestyle_syntax}
8186 {"MoveObject", N_("Select an Object"), ActionMoveObject,
8187 moveobject_help, moveobject_syntax}
8189 {"MoveToCurrentLayer", 0, ActionMoveToCurrentLayer,
8190 movetocurrentlayer_help, movetocurrentlayer_syntax}
8192 {"New", 0, ActionNew,
8193 new_help, new_syntax}
8195 {"pscalib", 0, ActionPSCalib}
8197 {"ElementList", 0, ActionElementList,
8198 elementlist_help, elementlist_syntax}
8200 {"ElementSetAttr", 0, ActionElementSetAttr,
8201 elementsetattr_help, elementsetattr_syntax}
8203 {"ExecCommand", 0, ActionExecCommand,
8204 execcommand_help, execcommand_syntax}
8206 {"Import", 0, ActionImport,
8207 import_help, import_syntax}
8211 REGISTER_ACTIONS (action_action_list)