hid.h: Update comment to reflect change in API
[geda-pcb/pcjc2.git] / src / action.c
blobb8b3fe76d1cabc887bf84fbfc700432ce5e60d6e
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))
1260 PinType *via;
1262 if ((line =
1263 CreateDrawnLineOnLayer (CURRENT,
1264 Crosshair.AttachedLine.Point1.X,
1265 Crosshair.AttachedLine.Point1.Y,
1266 Crosshair.AttachedLine.Point2.X,
1267 Crosshair.AttachedLine.Point2.Y,
1268 Settings.LineThickness,
1269 2 * Settings.Keepaway,
1270 MakeFlags (line_flags))) != NULL)
1273 addedLines++;
1274 AddObjectToCreateUndoList (LINE_TYPE, CURRENT, line, line);
1275 DrawLine (CURRENT, line);
1277 /* place a via if vias are visible, the layer is
1278 in a new group since the last line and there
1279 isn't a pin already here */
1280 if (PCB->ViaOn && GetLayerGroupNumberByPointer (CURRENT) !=
1281 GetLayerGroupNumberByPointer (lastLayer) &&
1282 SearchObjectByLocation (PIN_TYPES, &ptr1, &ptr2, &ptr3,
1283 Crosshair.AttachedLine.Point1.X,
1284 Crosshair.AttachedLine.Point1.Y,
1285 Settings.ViaThickness / 2) ==
1286 NO_TYPE
1287 && (via =
1288 CreateNewVia (PCB->Data,
1289 Crosshair.AttachedLine.Point1.X,
1290 Crosshair.AttachedLine.Point1.Y,
1291 Settings.ViaThickness,
1292 2 * Settings.Keepaway, 0,
1293 Settings.ViaDrillingHole, NULL,
1294 NoFlags ())) != NULL)
1296 AddObjectToCreateUndoList (VIA_TYPE, via, via, via);
1297 DrawVia (via);
1299 /* copy the coordinates */
1300 Crosshair.AttachedLine.Point1.X =
1301 Crosshair.AttachedLine.Point2.X;
1302 Crosshair.AttachedLine.Point1.Y =
1303 Crosshair.AttachedLine.Point2.Y;
1304 IncrementUndoSerialNumber ();
1305 lastLayer = CURRENT;
1307 if (PCB->Clipping && (Note.X != Crosshair.AttachedLine.Point2.X
1308 || Note.Y !=
1309 Crosshair.AttachedLine.Point2.Y))
1311 if ((line =
1312 CreateDrawnLineOnLayer (CURRENT,
1313 Crosshair.AttachedLine.Point2.X,
1314 Crosshair.AttachedLine.Point2.Y,
1315 Note.X, Note.Y,
1316 Settings.LineThickness,
1317 2 * Settings.Keepaway,
1318 MakeFlags (line_flags))) != NULL)
1320 addedLines++;
1321 AddObjectToCreateUndoList (LINE_TYPE, CURRENT, line, line);
1322 IncrementUndoSerialNumber ();
1323 DrawLine (CURRENT, line);
1325 /* move to new start point */
1326 Crosshair.AttachedLine.Point1.X = Note.X;
1327 Crosshair.AttachedLine.Point1.Y = Note.Y;
1328 Crosshair.AttachedLine.Point2.X = Note.X;
1329 Crosshair.AttachedLine.Point2.Y = Note.Y;
1330 if (TEST_FLAG (SWAPSTARTDIRFLAG, PCB))
1332 PCB->Clipping ^= 3;
1335 if (TEST_FLAG (AUTODRCFLAG, PCB) && !TEST_SILK_LAYER (CURRENT))
1336 LookupConnection (Note.X, Note.Y, true, 1, CONNECTEDFLAG, false);
1337 Draw ();
1339 break;
1341 case RECTANGLE_MODE:
1342 /* do update of position */
1343 NotifyBlock ();
1345 /* create rectangle if both corners are determined
1346 * and width, height are != 0
1348 if (Crosshair.AttachedBox.State == STATE_THIRD &&
1349 Crosshair.AttachedBox.Point1.X != Crosshair.AttachedBox.Point2.X &&
1350 Crosshair.AttachedBox.Point1.Y != Crosshair.AttachedBox.Point2.Y)
1352 PolygonType *polygon;
1354 int flags = CLEARPOLYFLAG;
1355 if (TEST_FLAG (NEWFULLPOLYFLAG, PCB))
1356 flags |= FULLPOLYFLAG;
1357 if ((polygon = CreateNewPolygonFromRectangle (CURRENT,
1358 Crosshair.
1359 AttachedBox.Point1.X,
1360 Crosshair.
1361 AttachedBox.Point1.Y,
1362 Crosshair.
1363 AttachedBox.Point2.X,
1364 Crosshair.
1365 AttachedBox.Point2.Y,
1366 MakeFlags
1367 (flags))) !=
1368 NULL)
1370 AddObjectToCreateUndoList (POLYGON_TYPE, CURRENT,
1371 polygon, polygon);
1372 IncrementUndoSerialNumber ();
1373 DrawPolygon (CURRENT, polygon);
1374 Draw ();
1377 /* reset state to 'first corner' */
1378 Crosshair.AttachedBox.State = STATE_FIRST;
1380 break;
1382 case TEXT_MODE:
1384 char *string;
1386 if ((string = gui->prompt_for (_("Enter text:"), "")) != NULL)
1388 if (strlen(string) > 0)
1390 TextType *text;
1391 int flag = CLEARLINEFLAG;
1393 if (GetLayerGroupNumberByNumber (INDEXOFCURRENT) ==
1394 GetLayerGroupNumberBySide (BOTTOM_SIDE))
1395 flag |= ONSOLDERFLAG;
1396 if ((text = CreateNewText (CURRENT, &PCB->Font, Note.X,
1397 Note.Y, 0, Settings.TextScale,
1398 string, MakeFlags (flag))) != NULL)
1400 AddObjectToCreateUndoList (TEXT_TYPE, CURRENT, text, text);
1401 IncrementUndoSerialNumber ();
1402 DrawText (CURRENT, text);
1403 Draw ();
1406 free (string);
1408 break;
1411 case POLYGON_MODE:
1413 PointType *points = Crosshair.AttachedPolygon.Points;
1414 Cardinal n = Crosshair.AttachedPolygon.PointN;
1416 /* do update of position; use the 'LINE_MODE' mechanism */
1417 NotifyLine ();
1419 /* check if this is the last point of a polygon */
1420 if (n >= 3 &&
1421 points->X == Crosshair.AttachedLine.Point2.X &&
1422 points->Y == Crosshair.AttachedLine.Point2.Y)
1424 CopyAttachedPolygonToLayer ();
1425 Draw ();
1426 break;
1429 /* create new point if it's the first one or if it's
1430 * different to the last one
1432 if (!n ||
1433 points[n - 1].X != Crosshair.AttachedLine.Point2.X ||
1434 points[n - 1].Y != Crosshair.AttachedLine.Point2.Y)
1436 CreateNewPointInPolygon (&Crosshair.AttachedPolygon,
1437 Crosshair.AttachedLine.Point2.X,
1438 Crosshair.AttachedLine.Point2.Y);
1440 /* copy the coordinates */
1441 Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X;
1442 Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y;
1444 break;
1447 case POLYGONHOLE_MODE:
1449 switch (Crosshair.AttachedObject.State)
1451 /* first notify, lookup object */
1452 case STATE_FIRST:
1453 Crosshair.AttachedObject.Type =
1454 SearchScreen (Note.X, Note.Y, POLYGON_TYPE,
1455 &Crosshair.AttachedObject.Ptr1,
1456 &Crosshair.AttachedObject.Ptr2,
1457 &Crosshair.AttachedObject.Ptr3);
1459 if (Crosshair.AttachedObject.Type != NO_TYPE)
1461 if (TEST_FLAG (LOCKFLAG, (PolygonType *)
1462 Crosshair.AttachedObject.Ptr2))
1464 Message (_("Sorry, the object is locked\n"));
1465 Crosshair.AttachedObject.Type = NO_TYPE;
1466 break;
1468 else
1469 Crosshair.AttachedObject.State = STATE_SECOND;
1471 break;
1473 /* second notify, insert new point into object */
1474 case STATE_SECOND:
1476 PointType *points = Crosshair.AttachedPolygon.Points;
1477 Cardinal n = Crosshair.AttachedPolygon.PointN;
1478 POLYAREA *original, *new_hole, *result;
1479 FlagType Flags;
1481 /* do update of position; use the 'LINE_MODE' mechanism */
1482 NotifyLine ();
1484 /* check if this is the last point of a polygon */
1485 if (n >= 3 &&
1486 points->X == Crosshair.AttachedLine.Point2.X &&
1487 points->Y == Crosshair.AttachedLine.Point2.Y)
1489 /* Create POLYAREAs from the original polygon
1490 * and the new hole polygon */
1491 original = PolygonToPoly ((PolygonType *)Crosshair.AttachedObject.Ptr2);
1492 new_hole = PolygonToPoly (&Crosshair.AttachedPolygon);
1494 /* Subtract the hole from the original polygon shape */
1495 poly_Boolean_free (original, new_hole, &result, PBO_SUB);
1497 /* Convert the resulting polygon(s) into a new set of nodes
1498 * and place them on the page. Delete the original polygon.
1500 SaveUndoSerialNumber ();
1501 Flags = ((PolygonType *)Crosshair.AttachedObject.Ptr2)->Flags;
1502 PolyToPolygonsOnLayer (PCB->Data, (LayerType *)Crosshair.AttachedObject.Ptr1,
1503 result, Flags);
1504 RemoveObject (POLYGON_TYPE,
1505 Crosshair.AttachedObject.Ptr1,
1506 Crosshair.AttachedObject.Ptr2,
1507 Crosshair.AttachedObject.Ptr3);
1508 RestoreUndoSerialNumber ();
1509 IncrementUndoSerialNumber ();
1510 Draw ();
1512 /* reset state of attached line */
1513 memset (&Crosshair.AttachedPolygon, 0, sizeof (PolygonType));
1514 Crosshair.AttachedLine.State = STATE_FIRST;
1515 addedLines = 0;
1517 break;
1520 /* create new point if it's the first one or if it's
1521 * different to the last one
1523 if (!n ||
1524 points[n - 1].X != Crosshair.AttachedLine.Point2.X ||
1525 points[n - 1].Y != Crosshair.AttachedLine.Point2.Y)
1527 CreateNewPointInPolygon (&Crosshair.AttachedPolygon,
1528 Crosshair.AttachedLine.Point2.X,
1529 Crosshair.AttachedLine.Point2.Y);
1531 /* copy the coordinates */
1532 Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X;
1533 Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y;
1535 break;
1539 break;
1542 case PASTEBUFFER_MODE:
1544 TextType estr[MAX_ELEMENTNAMES];
1545 ElementType *e = 0;
1547 if (gui->shift_is_pressed ())
1549 int type =
1550 SearchScreen (Note.X, Note.Y, ELEMENT_TYPE, &ptr1, &ptr2,
1551 &ptr3);
1552 if (type == ELEMENT_TYPE)
1554 e = (ElementType *) ptr1;
1555 if (e)
1557 int i;
1559 memcpy (estr, e->Name,
1560 MAX_ELEMENTNAMES * sizeof (TextType));
1561 for (i = 0; i < MAX_ELEMENTNAMES; ++i)
1562 estr[i].TextString = estr[i].TextString ? strdup(estr[i].TextString) : NULL;
1563 RemoveElement (e);
1567 if (CopyPastebufferToLayout (Note.X, Note.Y))
1568 SetChangedFlag (true);
1569 if (e)
1571 int type =
1572 SearchScreen (Note.X, Note.Y, ELEMENT_TYPE, &ptr1, &ptr2,
1573 &ptr3);
1574 if (type == ELEMENT_TYPE && ptr1)
1576 int i, save_n;
1577 e = (ElementType *) ptr1;
1579 save_n = NAME_INDEX (PCB);
1581 for (i = 0; i < MAX_ELEMENTNAMES; i++)
1583 if (i == save_n)
1584 EraseElementName (e);
1585 r_delete_entry (PCB->Data->name_tree[i],
1586 (BoxType *) & (e->Name[i]));
1587 memcpy (&(e->Name[i]), &(estr[i]), sizeof (TextType));
1588 e->Name[i].Element = e;
1589 SetTextBoundingBox (&PCB->Font, &(e->Name[i]));
1590 r_insert_entry (PCB->Data->name_tree[i],
1591 (BoxType *) & (e->Name[i]), 0);
1592 if (i == save_n)
1593 DrawElementName (e);
1597 break;
1600 case REMOVE_MODE:
1601 if ((type =
1602 SearchScreen (Note.X, Note.Y, REMOVE_TYPES, &ptr1, &ptr2,
1603 &ptr3)) != NO_TYPE)
1605 if (TEST_FLAG (LOCKFLAG, (LineType *) ptr2))
1607 Message (_("Sorry, the object is locked\n"));
1608 break;
1610 if (type == ELEMENT_TYPE)
1612 RubberbandType *ptr;
1613 int i;
1615 Crosshair.AttachedObject.RubberbandN = 0;
1616 LookupRatLines (type, ptr1, ptr2, ptr3);
1617 ptr = Crosshair.AttachedObject.Rubberband;
1618 for (i = 0; i < Crosshair.AttachedObject.RubberbandN; i++)
1620 if (PCB->RatOn)
1621 EraseRat ((RatType *) ptr->Line);
1622 if (TEST_FLAG (RUBBERENDFLAG, ptr->Line))
1623 MoveObjectToRemoveUndoList (RATLINE_TYPE,
1624 ptr->Line, ptr->Line,
1625 ptr->Line);
1626 else
1627 TOGGLE_FLAG (RUBBERENDFLAG, ptr->Line); /* only remove line once */
1628 ptr++;
1631 RemoveObject (type, ptr1, ptr2, ptr3);
1632 IncrementUndoSerialNumber ();
1633 SetChangedFlag (true);
1635 break;
1637 case ROTATE_MODE:
1638 RotateScreenObject (Note.X, Note.Y,
1639 gui->shift_is_pressed ()? (SWAP_IDENT ?
1640 1 : 3)
1641 : (SWAP_IDENT ? 3 : 1));
1642 break;
1644 /* both are almost the same */
1645 case COPY_MODE:
1646 case MOVE_MODE:
1647 switch (Crosshair.AttachedObject.State)
1649 /* first notify, lookup object */
1650 case STATE_FIRST:
1652 int types = (Settings.Mode == COPY_MODE) ?
1653 COPY_TYPES : MOVE_TYPES;
1655 Crosshair.AttachedObject.Type =
1656 SearchScreen (Note.X, Note.Y, types,
1657 &Crosshair.AttachedObject.Ptr1,
1658 &Crosshair.AttachedObject.Ptr2,
1659 &Crosshair.AttachedObject.Ptr3);
1660 if (Crosshair.AttachedObject.Type != NO_TYPE)
1662 if (Settings.Mode == MOVE_MODE &&
1663 TEST_FLAG (LOCKFLAG, (PinType *)
1664 Crosshair.AttachedObject.Ptr2))
1666 Message (_("Sorry, the object is locked\n"));
1667 Crosshair.AttachedObject.Type = NO_TYPE;
1669 else
1670 AttachForCopy (Note.X, Note.Y);
1672 break;
1675 /* second notify, move or copy object */
1676 case STATE_SECOND:
1677 if (Settings.Mode == COPY_MODE)
1678 CopyObject (Crosshair.AttachedObject.Type,
1679 Crosshair.AttachedObject.Ptr1,
1680 Crosshair.AttachedObject.Ptr2,
1681 Crosshair.AttachedObject.Ptr3,
1682 Note.X - Crosshair.AttachedObject.X,
1683 Note.Y - Crosshair.AttachedObject.Y);
1684 else
1686 MoveObjectAndRubberband (Crosshair.AttachedObject.Type,
1687 Crosshair.AttachedObject.Ptr1,
1688 Crosshair.AttachedObject.Ptr2,
1689 Crosshair.AttachedObject.Ptr3,
1690 Note.X - Crosshair.AttachedObject.X,
1691 Note.Y - Crosshair.AttachedObject.Y);
1692 SetLocalRef (0, 0, false);
1694 SetChangedFlag (true);
1696 /* reset identifiers */
1697 Crosshair.AttachedObject.Type = NO_TYPE;
1698 Crosshair.AttachedObject.State = STATE_FIRST;
1699 break;
1701 break;
1703 /* insert a point into a polygon/line/... */
1704 case INSERTPOINT_MODE:
1705 switch (Crosshair.AttachedObject.State)
1707 /* first notify, lookup object */
1708 case STATE_FIRST:
1709 Crosshair.AttachedObject.Type =
1710 SearchScreen (Note.X, Note.Y, INSERT_TYPES,
1711 &Crosshair.AttachedObject.Ptr1,
1712 &Crosshair.AttachedObject.Ptr2,
1713 &Crosshair.AttachedObject.Ptr3);
1715 if (Crosshair.AttachedObject.Type != NO_TYPE)
1717 if (TEST_FLAG (LOCKFLAG, (PolygonType *)
1718 Crosshair.AttachedObject.Ptr2))
1720 Message (_("Sorry, the object is locked\n"));
1721 Crosshair.AttachedObject.Type = NO_TYPE;
1722 break;
1724 else
1726 /* get starting point of nearest segment */
1727 if (Crosshair.AttachedObject.Type == POLYGON_TYPE)
1729 fake.poly =
1730 (PolygonType *) Crosshair.AttachedObject.Ptr2;
1731 polyIndex =
1732 GetLowestDistancePolygonPoint (fake.poly, Note.X,
1733 Note.Y);
1734 fake.line.Point1 = fake.poly->Points[polyIndex];
1735 fake.line.Point2 = fake.poly->Points[
1736 prev_contour_point (fake.poly, polyIndex)];
1737 Crosshair.AttachedObject.Ptr2 = &fake.line;
1740 Crosshair.AttachedObject.State = STATE_SECOND;
1741 InsertedPoint = *AdjustInsertPoint ();
1744 break;
1746 /* second notify, insert new point into object */
1747 case STATE_SECOND:
1748 if (Crosshair.AttachedObject.Type == POLYGON_TYPE)
1749 InsertPointIntoObject (POLYGON_TYPE,
1750 Crosshair.AttachedObject.Ptr1, fake.poly,
1751 &polyIndex,
1752 InsertedPoint.X, InsertedPoint.Y, false, false);
1753 else
1754 InsertPointIntoObject (Crosshair.AttachedObject.Type,
1755 Crosshair.AttachedObject.Ptr1,
1756 Crosshair.AttachedObject.Ptr2,
1757 &polyIndex,
1758 InsertedPoint.X, InsertedPoint.Y, false, false);
1759 SetChangedFlag (true);
1761 /* reset identifiers */
1762 Crosshair.AttachedObject.Type = NO_TYPE;
1763 Crosshair.AttachedObject.State = STATE_FIRST;
1764 break;
1766 break;
1771 /* --------------------------------------------------------------------------- */
1773 static const char atomic_syntax[] = N_("Atomic(Save|Restore|Close|Block)");
1775 static const char atomic_help[] = N_("Save or restore the undo serial number.");
1777 /* %start-doc actions Atomic
1779 This action allows making multiple-action bindings into an atomic
1780 operation that will be undone by a single Undo command. For example,
1781 to optimize rat lines, you'd delete the rats and re-add them. To
1782 group these into a single undo, you'd want the deletions and the
1783 additions to have the same undo serial number. So, you @code{Save},
1784 delete the rats, @code{Restore}, add the rats - using the same serial
1785 number as the deletes, then @code{Block}, which checks to see if the
1786 deletions or additions actually did anything. If not, the serial
1787 number is set to the saved number, as there's nothing to undo. If
1788 something did happen, the serial number is incremented so that these
1789 actions are counted as a single undo step.
1791 @table @code
1793 @item Save
1794 Saves the undo serial number.
1796 @item Restore
1797 Returns it to the last saved number.
1799 @item Close
1800 Sets it to 1 greater than the last save.
1802 @item Block
1803 Does a Restore if there was nothing to undo, else does a Close.
1805 @end table
1807 %end-doc */
1809 static int
1810 ActionAtomic (int argc, char **argv, Coord x, Coord y)
1812 if (argc != 1)
1813 AFAIL (atomic);
1815 switch (GetFunctionID (argv[0]))
1817 case F_Save:
1818 SaveUndoSerialNumber ();
1819 break;
1820 case F_Restore:
1821 RestoreUndoSerialNumber ();
1822 break;
1823 case F_Close:
1824 RestoreUndoSerialNumber ();
1825 IncrementUndoSerialNumber ();
1826 break;
1827 case F_Block:
1828 RestoreUndoSerialNumber ();
1829 if (Bumped)
1830 IncrementUndoSerialNumber ();
1831 break;
1833 return 0;
1836 /* -------------------------------------------------------------------------- */
1838 static const char drc_syntax[] = N_("DRC()");
1840 static const char drc_help[] = N_("Invoke the DRC check.");
1842 /* %start-doc actions DRC
1844 Note that the design rule check uses the current board rule settings,
1845 not the current style settings.
1847 %end-doc */
1849 static int
1850 ActionDRCheck (int argc, char **argv, Coord x, Coord y)
1852 int count;
1854 if (gui->drc_gui == NULL || gui->drc_gui->log_drc_overview)
1856 Message (_("%m+Rules are minspace %$mS, minoverlap %$mS "
1857 "minwidth %$mS, minsilk %$mS\n"
1858 "min drill %$mS, min annular ring %$mS\n"),
1859 Settings.grid_unit->allow,
1860 PCB->Bloat, PCB->Shrink,
1861 PCB->minWid, PCB->minSlk,
1862 PCB->minDrill, PCB->minRing);
1864 count = DRCAll ();
1865 if (gui->drc_gui == NULL || gui->drc_gui->log_drc_overview)
1867 if (count == 0)
1868 Message (_("No DRC problems found.\n"));
1869 else if (count > 0)
1870 Message (_("Found %d design rule errors.\n"), count);
1871 else
1872 Message (_("Aborted DRC after %d design rule errors.\n"), -count);
1874 return 0;
1877 /* -------------------------------------------------------------------------- */
1879 static const char dumplibrary_syntax[] = N_("DumpLibrary()");
1881 static const char dumplibrary_help[] =
1882 N_("Display the entire contents of the libraries.");
1884 /* %start-doc actions DumpLibrary
1887 %end-doc */
1889 static int
1890 ActionDumpLibrary (int argc, char **argv, Coord x, Coord y)
1892 int i, j;
1894 printf ("**** Do not count on this format. It will change ****\n\n");
1895 printf ("MenuN = %d\n", Library.MenuN);
1896 printf ("MenuMax = %d\n", Library.MenuMax);
1897 for (i = 0; i < Library.MenuN; i++)
1899 printf ("Library #%d:\n", i);
1900 printf (" EntryN = %d\n", Library.Menu[i].EntryN);
1901 printf (" EntryMax = %d\n", Library.Menu[i].EntryMax);
1902 printf (" Name = \"%s\"\n", UNKNOWN (Library.Menu[i].Name));
1903 printf (" directory = \"%s\"\n",
1904 UNKNOWN (Library.Menu[i].directory));
1905 printf (" Style = \"%s\"\n", UNKNOWN (Library.Menu[i].Style));
1906 printf (" flag = %d\n", Library.Menu[i].flag);
1908 for (j = 0; j < Library.Menu[i].EntryN; j++)
1910 printf (" #%4d: ", j);
1911 if (Library.Menu[i].Entry[j].Template == (char *) -1)
1913 printf ("newlib: \"%s\"\n",
1914 UNKNOWN (Library.Menu[i].Entry[j].ListEntry));
1916 else
1918 printf ("\"%s\", \"%s\", \"%s\", \"%s\", \"%s\"\n",
1919 UNKNOWN (Library.Menu[i].Entry[j].ListEntry),
1920 UNKNOWN (Library.Menu[i].Entry[j].Template),
1921 UNKNOWN (Library.Menu[i].Entry[j].Package),
1922 UNKNOWN (Library.Menu[i].Entry[j].Value),
1923 UNKNOWN (Library.Menu[i].Entry[j].Description));
1928 return 0;
1931 /* -------------------------------------------------------------------------- */
1933 static const char flip_syntax[] = N_("Flip(Object|Selected|SelectedElements)");
1935 static const char flip_help[] =
1936 N_("Flip an element to the opposite side of the board.");
1938 /* %start-doc actions Flip
1940 Note that the location of the element will be symmetric about the
1941 cursor location; i.e. if the part you are pointing at will still be at
1942 the same spot once the element is on the other side. When flipping
1943 multiple elements, this retains their positions relative to each
1944 other, not their absolute positions on the board.
1946 %end-doc */
1948 static int
1949 ActionFlip (int argc, char **argv, Coord x, Coord y)
1951 char *function = ARG (0);
1952 ElementType *element;
1953 void *ptrtmp;
1954 int err = 0;
1956 if (function)
1958 switch (GetFunctionID (function))
1960 case F_Object:
1961 if ((SearchScreen (x, y, ELEMENT_TYPE,
1962 &ptrtmp, &ptrtmp, &ptrtmp)) != NO_TYPE)
1964 element = (ElementType *) ptrtmp;
1965 ChangeElementSide (element, 2 * Crosshair.Y - PCB->MaxHeight);
1966 IncrementUndoSerialNumber ();
1967 Draw ();
1969 break;
1970 case F_Selected:
1971 case F_SelectedElements:
1972 ChangeSelectedElementSide ();
1973 break;
1974 default:
1975 err = 1;
1976 break;
1978 if (!err)
1979 return 0;
1982 AFAIL (flip);
1985 /* -------------------------------------------------------------------------- */
1987 static const char message_syntax[] = N_("Message(message)");
1989 static const char message_help[] = N_("Writes a message to the log window.");
1991 /* %start-doc actions Message
1993 This action displays a message to the log window. This action is primarily
1994 provided for use by other programs which may interface with PCB. If
1995 multiple arguments are given, each one is sent to the log window
1996 followed by a newline.
1998 %end-doc */
2000 static int
2001 ActionMessage (int argc, char **argv, Coord x, Coord y)
2003 int i;
2005 if (argc < 1)
2006 AFAIL (message);
2008 for (i = 0; i < argc; i++)
2010 Message (argv[i]);
2011 Message ("\n");
2014 return 0;
2018 /* -------------------------------------------------------------------------- */
2020 static const char setthermal_syntax[] =
2021 "SetThermal(Object|SelectedPins|SelectedVias|Selected, Style)";
2023 static const char setthermal_help[] =
2024 N_("Set the thermal (on the current layer) of pins or vias to the given style.\n"
2025 "Style = 0 means no thermal.\n"
2026 "Style = 1 has diagonal fingers with sharp edges.\n"
2027 "Style = 2 has horizontal and vertical fingers with sharp edges.\n"
2028 "Style = 3 is a solid connection to the plane."
2029 "Style = 4 has diagonal fingers with rounded edges.\n"
2030 "Style = 5 has horizontal and vertical fingers with rounded edges.\n");
2032 /* %start-doc actions SetThermal
2034 This changes how/whether pins or vias connect to any rectangle or polygon
2035 on the current layer. The first argument can specify one object, or all
2036 selected pins, or all selected vias, or all selected pins and vias.
2037 The second argument specifies the style of connection.
2038 There are 5 possibilities:
2039 0 - no connection,
2040 1 - 45 degree fingers with sharp edges,
2041 2 - horizontal & vertical fingers with sharp edges,
2042 3 - solid connection,
2043 4 - 45 degree fingers with rounded corners,
2044 5 - horizontal & vertical fingers with rounded corners.
2046 Pins and Vias may have thermals whether or not there is a polygon available
2047 to connect with. However, they will have no effect without the polygon.
2048 %end-doc */
2050 static int
2051 ActionSetThermal (int argc, char **argv, Coord x, Coord y)
2053 char *function = ARG (0);
2054 char *style = ARG (1);
2055 void *ptr1, *ptr2, *ptr3;
2056 int type, kind;
2057 int err = 0;
2059 if (function && *function && style && *style)
2061 bool absolute;
2063 kind = GetValue (style, NULL, &absolute);
2064 if (absolute)
2065 switch (GetFunctionID (function))
2067 case F_Object:
2068 if ((type =
2069 SearchScreen (Crosshair.X, Crosshair.Y, CHANGETHERMAL_TYPES,
2070 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
2072 ChangeObjectThermal (type, ptr1, ptr2, ptr3, kind);
2073 IncrementUndoSerialNumber ();
2074 Draw ();
2076 break;
2077 case F_SelectedPins:
2078 ChangeSelectedThermals (PIN_TYPE, kind);
2079 break;
2080 case F_SelectedVias:
2081 ChangeSelectedThermals (VIA_TYPE, kind);
2082 break;
2083 case F_Selected:
2084 case F_SelectedElements:
2085 ChangeSelectedThermals (CHANGETHERMAL_TYPES, kind);
2086 break;
2087 default:
2088 err = 1;
2089 break;
2091 else
2092 err = 1;
2093 if (!err)
2094 return 0;
2097 AFAIL (setthermal);
2100 /* ---------------------------------------------------------------------------
2101 * !!! no action routine !!!
2103 * event handler to set the cursor according to the X pointer position
2104 * called from inside main.c
2106 void
2107 EventMoveCrosshair (int ev_x, int ev_y)
2109 #ifdef HAVE_LIBSTROKE
2110 if (mid_stroke)
2112 StrokeBox.X2 = ev_x;
2113 StrokeBox.Y2 = ev_y;
2114 stroke_record (ev_x, ev_y);
2115 return;
2117 #endif /* HAVE_LIBSTROKE */
2118 if (MoveCrosshairAbsolute (ev_x, ev_y))
2120 /* update object position and cursor location */
2121 AdjustAttachedObjects ();
2122 notify_crosshair_change (true);
2126 /* --------------------------------------------------------------------------- */
2128 static const char setvalue_syntax[] =
2129 N_("SetValue(Grid|Line|LineSize|Text|TextScale|ViaDrillingHole|Via|ViaSize, "
2130 "delta)");
2132 static const char setvalue_help[] =
2133 N_("Change various board-wide values and sizes.");
2135 /* %start-doc actions SetValue
2137 @table @code
2139 @item ViaDrillingHole
2140 Changes the diameter of the drill for new vias.
2142 @item Grid
2143 Sets the grid spacing.
2145 @item Line
2146 @item LineSize
2147 Changes the thickness of new lines.
2149 @item Via
2150 @item ViaSize
2151 Changes the diameter of new vias.
2153 @item Text
2154 @item TextScale
2155 Changes the size of new text.
2157 @end table
2159 %end-doc */
2161 static int
2162 ActionSetValue (int argc, char **argv, Coord x, Coord y)
2164 char *function = ARG (0);
2165 char *val = ARG (1);
2166 char *units = ARG (2);
2167 bool absolute; /* flag for 'absolute' value */
2168 double value;
2169 int text_scale;
2170 int err = 0;
2172 if (function && val)
2174 value = GetValue (val, units, &absolute);
2175 switch (GetFunctionID (function))
2177 case F_ViaDrillingHole:
2178 SetViaDrillingHole (absolute ? value :
2179 value + Settings.ViaDrillingHole,
2180 false);
2181 hid_action ("RouteStylesChanged");
2182 break;
2184 case F_Grid:
2185 if (absolute)
2186 SetGrid (value, false);
2187 else
2189 if (value == 0)
2190 value = val[0] == '-' ? -Settings.increments->grid
2191 : Settings.increments->grid;
2192 /* On the way down, short against the minimum
2193 * PCB drawing unit */
2194 if ((value + PCB->Grid) < 1)
2195 SetGrid (1, false);
2196 else if (PCB->Grid == 1)
2197 SetGrid (value, false);
2198 else
2199 SetGrid (value + PCB->Grid, false);
2201 break;
2203 case F_LineSize:
2204 case F_Line:
2205 if (!absolute && value == 0)
2206 value = val[0] == '-' ? -Settings.increments->line
2207 : Settings.increments->line;
2208 SetLineSize (absolute ? value : value + Settings.LineThickness);
2209 hid_action ("RouteStylesChanged");
2210 break;
2212 case F_Via:
2213 case F_ViaSize:
2214 SetViaSize (absolute ? value : value + Settings.ViaThickness, false);
2215 hid_action ("RouteStylesChanged");
2216 break;
2218 case F_Text:
2219 case F_TextScale:
2220 text_scale = value / (double)FONT_CAPHEIGHT * 100.;
2221 if (!absolute)
2222 text_scale += Settings.TextScale;
2223 SetTextScale (text_scale);
2224 break;
2225 default:
2226 err = 1;
2227 break;
2229 if (!err)
2230 return 0;
2233 AFAIL (setvalue);
2237 /* --------------------------------------------------------------------------- */
2239 static const char quit_syntax[] = N_("Quit()");
2241 static const char quit_help[] = N_("Quits the application after confirming.");
2243 /* %start-doc actions Quit
2245 If you have unsaved changes, you will be prompted to confirm (or
2246 save) before quitting.
2248 %end-doc */
2250 static int
2251 ActionQuit (int argc, char **argv, Coord x, Coord y)
2253 char *force = ARG (0);
2254 if (force && strcasecmp (force, "force") == 0)
2256 PCB->Changed = 0;
2257 exit (0);
2259 if (!PCB->Changed || gui->close_confirm_dialog () == HID_CLOSE_CONFIRM_OK)
2260 QuitApplication ();
2261 return 1;
2264 /* --------------------------------------------------------------------------- */
2266 static const char connection_syntax[] =
2267 N_("Connection(Find|ResetLinesAndPolygons|ResetPinsAndVias|Reset)");
2269 static const char connection_help[] =
2270 N_("Searches connections of the object at the cursor position.");
2272 /* %start-doc actions Connection
2274 Connections found with this action will be highlighted in the
2275 ``connected-color'' color and will have the ``found'' flag set.
2277 @table @code
2279 @item Find
2280 The net under the cursor is ``found''.
2282 @item ResetLinesAndPolygons
2283 Any ``found'' lines and polygons are marked ``not found''.
2285 @item ResetPinsAndVias
2286 Any ``found'' pins and vias are marked ``not found''.
2288 @item Reset
2289 All ``found'' objects are marked ``not found''.
2291 @end table
2293 %end-doc */
2295 static int
2296 ActionConnection (int argc, char **argv, Coord x, Coord y)
2298 char *function = ARG (0);
2299 if (function)
2301 switch (GetFunctionID (function))
2303 case F_Find:
2305 gui->get_coords (_("Click on a connection"), &x, &y);
2306 LookupConnection (x, y, true, 1, CONNECTEDFLAG, false);
2307 LookupConnection (x, y, true, 1, FOUNDFLAG, true);
2308 break;
2311 case F_ResetLinesAndPolygons:
2312 if (ClearFlagOnLinesAndPolygons (true, CONNECTEDFLAG | FOUNDFLAG))
2314 IncrementUndoSerialNumber ();
2315 Draw ();
2317 break;
2319 case F_ResetPinsViasAndPads:
2320 if (ClearFlagOnPinsViasAndPads (true, CONNECTEDFLAG | FOUNDFLAG))
2322 IncrementUndoSerialNumber ();
2323 Draw ();
2325 break;
2327 case F_Reset:
2328 if (ClearFlagOnAllObjects (true, CONNECTEDFLAG | FOUNDFLAG))
2330 IncrementUndoSerialNumber ();
2331 Draw ();
2333 break;
2335 return 0;
2338 AFAIL (connection);
2341 /* --------------------------------------------------------------------------- */
2343 static const char disperseelements_syntax[] =
2344 N_("DisperseElements(All|Selected)");
2346 static const char disperseelements_help[] = N_("Disperses elements.");
2348 /* %start-doc actions DisperseElements
2350 Normally this is used when starting a board, by selecting all elements
2351 and then dispersing them. This scatters the elements around the board
2352 so that you can pick individual ones, rather than have all the
2353 elements at the same 0,0 coordinate and thus impossible to choose
2354 from.
2356 %end-doc */
2358 #define GAP MIL_TO_COORD(100)
2360 static int
2361 ActionDisperseElements (int argc, char **argv, Coord x, Coord y)
2363 char *function = ARG (0);
2364 Coord minx = GAP,
2365 miny = GAP,
2366 maxy = GAP,
2367 dx, dy;
2368 int all = 0, bad = 0;
2370 if (!function || !*function)
2372 bad = 1;
2374 else
2376 switch (GetFunctionID (function))
2378 case F_All:
2379 all = 1;
2380 break;
2382 case F_Selected:
2383 all = 0;
2384 break;
2386 default:
2387 bad = 1;
2391 if (bad)
2393 AFAIL (disperseelements);
2397 ELEMENT_LOOP (PCB->Data);
2400 * If we want to disperse selected elements, maybe we need smarter
2401 * code here to avoid putting components on top of others which
2402 * are not selected. For now, I'm assuming that this is typically
2403 * going to be used either with a brand new design or a scratch
2404 * design holding some new components
2406 if (!TEST_FLAG (LOCKFLAG, element) && (all || TEST_FLAG (SELECTEDFLAG, element)))
2409 /* figure out how much to move the element */
2410 dx = minx - element->BoundingBox.X1;
2412 /* snap to the grid */
2413 dx -= (element->MarkX + dx) % PCB->Grid;
2416 * and add one grid size so we make sure we always space by GAP or
2417 * more
2419 dx += PCB->Grid;
2421 /* Figure out if this row has room. If not, start a new row */
2422 if (GAP + element->BoundingBox.X2 + dx > PCB->MaxWidth)
2424 miny = maxy + GAP;
2425 minx = GAP;
2428 /* figure out how much to move the element */
2429 dx = minx - element->BoundingBox.X1;
2430 dy = miny - element->BoundingBox.Y1;
2432 /* snap to the grid */
2433 dx -= (element->MarkX + dx) % PCB->Grid;
2434 dx += PCB->Grid;
2435 dy -= (element->MarkY + dy) % PCB->Grid;
2436 dy += PCB->Grid;
2438 /* move the element */
2439 MoveElementLowLevel (PCB->Data, element, dx, dy);
2441 /* and add to the undo list so we can undo this operation */
2442 AddObjectToMoveUndoList (ELEMENT_TYPE, NULL, NULL, element, dx, dy);
2444 /* keep track of how tall this row is */
2445 minx += element->BoundingBox.X2 - element->BoundingBox.X1 + GAP;
2446 if (maxy < element->BoundingBox.Y2)
2448 maxy = element->BoundingBox.Y2;
2453 END_LOOP;
2455 /* done with our action so increment the undo # */
2456 IncrementUndoSerialNumber ();
2458 Redraw ();
2459 SetChangedFlag (true);
2461 return 0;
2464 #undef GAP
2466 /* --------------------------------------------------------------------------- */
2468 static const char display_syntax[] =
2469 N_("Display(NameOnPCB|Description|Value)\n"
2470 "Display(Grid|Redraw)\n"
2471 "Display(CycleClip|CycleCrosshair|Toggle45Degree|ToggleStartDirection)\n"
2472 "Display(ToggleGrid|ToggleRubberBandMode|ToggleUniqueNames)\n"
2473 "Display(ToggleMask|ToggleName|ToggleClearLine|ToggleFullPoly|ToggleSnapPin)\n"
2474 "Display(ToggleThindraw|ToggleThindrawPoly|ToggleOrthoMove|ToggleLocalRef)\n"
2475 "Display(ToggleCheckPlanes|ToggleShowDRC|ToggleAutoDRC)\n"
2476 "Display(ToggleLiveRoute|LockNames|OnlyNames)\n"
2477 "Display(Pinout|PinOrPadName)");
2479 static const char display_help[] = N_("Several display-related actions.");
2481 /* %start-doc actions Display
2483 @table @code
2485 @item NameOnPCB
2486 @item Description
2487 @item Value
2488 Specify whether all elements show their name, description, or value.
2490 @item Redraw
2491 Redraw the whole board.
2493 @item Toggle45Degree
2494 When clear, lines can be drawn at any angle. When set, lines are
2495 restricted to multiples of 45 degrees and requested lines may be
2496 broken up according to the clip setting.
2498 @item CycleClip
2499 Changes the way lines are restricted to 45 degree increments. The
2500 various settings are: straight only, orthogonal then angled, and angled
2501 then orthogonal. If AllDirections is set, this action disables it.
2503 @item CycleCrosshair
2504 Changes crosshair drawing. Crosshair may accept form of 4-ray,
2505 8-ray and 12-ray cross.
2507 @item ToggleRubberBandMode
2508 If set, moving an object moves all the lines attached to it too.
2510 @item ToggleStartDirection
2511 If set, each time you set a point in a line, the Clip toggles between
2512 orth-angle and angle-ortho.
2514 @item ToggleUniqueNames
2515 If set, you will not be permitted to change the name of an element to
2516 match that of another element.
2518 @item ToggleSnapPin
2519 If set, pin centers and pad end points are treated as additional grid
2520 points that the cursor can snap to.
2522 @item ToggleLocalRef
2523 If set, the mark is automatically set to the beginning of any move, so
2524 you can see the relative distance you've moved.
2526 @item ToggleThindraw
2527 If set, objects on the screen are drawn as outlines (lines are drawn
2528 as center-lines). This lets you see line endpoints hidden under pins,
2529 for example.
2531 @item ToggleThindrawPoly
2532 If set, polygons on the screen are drawn as outlines.
2534 @item ToggleShowDRC
2535 If set, pending objects (i.e. lines you're in the process of drawing)
2536 will be drawn with an outline showing how far away from other copper
2537 you need to be.
2539 @item ToggleLiveRoute
2540 If set, the progress of the autorouter will be visible on the screen.
2542 @item ToggleAutoDRC
2543 If set, you will not be permitted to make connections which violate
2544 the current DRC and netlist settings.
2546 @item ToggleCheckPlanes
2547 If set, lines and arcs aren't drawn, which usually leaves just the
2548 polygons. If you also disable all but the layer you're interested in,
2549 this allows you to check for isolated regions.
2551 @item ToggleOrthoMove
2552 If set, the crosshair is only allowed to move orthogonally from its
2553 previous position. I.e. you can move an element or line up, down,
2554 left, or right, but not up+left or down+right.
2556 @item ToggleName
2557 Selects whether the pinouts show the pin names or the pin numbers.
2559 @item ToggleLockNames
2560 If set, text will ignore left mouse clicks and actions that work on
2561 objects under the mouse. You can still select text with a lasso (left
2562 mouse drag) and perform actions on the selection.
2564 @item ToggleOnlyNames
2565 If set, only text will be sensitive for mouse clicks and actions that
2566 work on objects under the mouse. You can still select other objects
2567 with a lasso (left mouse drag) and perform actions on the selection.
2569 @item ToggleMask
2570 Turns the solder mask on or off.
2572 @item ToggleClearLine
2573 When set, the clear-line flag causes new lines and arcs to have their
2574 ``clear polygons'' flag set, so they won't be electrically connected
2575 to any polygons they overlap.
2577 @item ToggleFullPoly
2578 When set, the full-poly flag causes new polygons to have their
2579 ``full polygon'' flag set, so all parts of them will be displayed
2580 instead of only the biggest one.
2582 @item ToggleGrid
2583 Resets the origin of the current grid to be wherever the mouse pointer
2584 is (not where the crosshair currently is). If you provide two numbers
2585 after this, the origin is set to that coordinate.
2587 @item Grid
2588 Toggles whether the grid is displayed or not.
2590 @item Pinout
2591 Causes the pinout of the element indicated by the cursor to be
2592 displayed, usually in a separate window.
2594 @item PinOrPadName
2595 Toggles whether the names of pins, pads, or (yes) vias will be
2596 displayed. If the cursor is over an element, all of its pins and pads
2597 are affected.
2599 @end table
2601 %end-doc */
2603 static enum crosshair_shape
2604 CrosshairShapeIncrement (enum crosshair_shape shape)
2606 switch(shape)
2608 case Basic_Crosshair_Shape:
2609 shape = Union_Jack_Crosshair_Shape;
2610 break;
2611 case Union_Jack_Crosshair_Shape:
2612 shape = Dozen_Crosshair_Shape;
2613 break;
2614 case Dozen_Crosshair_Shape:
2615 shape = Crosshair_Shapes_Number;
2616 break;
2617 case Crosshair_Shapes_Number:
2618 shape = Basic_Crosshair_Shape;
2619 break;
2621 return shape;
2624 static int
2625 ActionDisplay (int argc, char **argv, Coord childX, Coord childY)
2627 char *function, *str_dir;
2628 int id;
2629 int err = 0;
2631 function = ARG (0);
2632 str_dir = ARG (1);
2634 if (function && (!str_dir || !*str_dir))
2636 switch (id = GetFunctionID (function))
2639 /* redraw layout */
2640 case F_ClearAndRedraw:
2641 case F_Redraw:
2642 Redraw ();
2643 break;
2645 /* change the displayed name of elements */
2646 case F_Value:
2647 case F_NameOnPCB:
2648 case F_Description:
2649 ELEMENT_LOOP (PCB->Data);
2651 EraseElementName (element);
2653 END_LOOP;
2654 CLEAR_FLAG (DESCRIPTIONFLAG | NAMEONPCBFLAG, PCB);
2655 switch (id)
2657 case F_Value:
2658 break;
2659 case F_NameOnPCB:
2660 SET_FLAG (NAMEONPCBFLAG, PCB);
2661 break;
2662 case F_Description:
2663 SET_FLAG (DESCRIPTIONFLAG, PCB);
2664 break;
2666 ELEMENT_LOOP (PCB->Data);
2668 DrawElementName (element);
2670 END_LOOP;
2671 Draw ();
2672 break;
2674 /* toggle line-adjust flag */
2675 case F_ToggleAllDirections:
2676 TOGGLE_FLAG (ALLDIRECTIONFLAG, PCB);
2677 AdjustAttachedObjects ();
2678 break;
2680 case F_CycleClip:
2681 notify_crosshair_change (false);
2682 if (TEST_FLAG (ALLDIRECTIONFLAG, PCB))
2684 TOGGLE_FLAG (ALLDIRECTIONFLAG, PCB);
2685 PCB->Clipping = 0;
2687 else
2688 PCB->Clipping = (PCB->Clipping + 1) % 3;
2689 AdjustAttachedObjects ();
2690 notify_crosshair_change (true);
2691 break;
2693 case F_CycleCrosshair:
2694 notify_crosshair_change (false);
2695 Crosshair.shape = CrosshairShapeIncrement(Crosshair.shape);
2696 if (Crosshair_Shapes_Number == Crosshair.shape)
2697 Crosshair.shape = Basic_Crosshair_Shape;
2698 notify_crosshair_change (true);
2699 break;
2701 case F_ToggleRubberBandMode:
2702 notify_crosshair_change (false);
2703 TOGGLE_FLAG (RUBBERBANDFLAG, PCB);
2704 notify_crosshair_change (true);
2705 break;
2707 case F_ToggleStartDirection:
2708 notify_crosshair_change (false);
2709 TOGGLE_FLAG (SWAPSTARTDIRFLAG, PCB);
2710 notify_crosshair_change (true);
2711 break;
2713 case F_ToggleUniqueNames:
2714 TOGGLE_FLAG (UNIQUENAMEFLAG, PCB);
2715 break;
2717 case F_ToggleSnapPin:
2718 notify_crosshair_change (false);
2719 TOGGLE_FLAG (SNAPPINFLAG, PCB);
2720 notify_crosshair_change (true);
2721 break;
2723 case F_ToggleLocalRef:
2724 TOGGLE_FLAG (LOCALREFFLAG, PCB);
2725 break;
2727 case F_ToggleThindraw:
2728 TOGGLE_FLAG (THINDRAWFLAG, PCB);
2729 Redraw ();
2730 break;
2732 case F_ToggleThindrawPoly:
2733 TOGGLE_FLAG (THINDRAWPOLYFLAG, PCB);
2734 Redraw ();
2735 break;
2737 case F_ToggleLockNames:
2738 TOGGLE_FLAG (LOCKNAMESFLAG, PCB);
2739 CLEAR_FLAG (ONLYNAMESFLAG, PCB);
2740 break;
2742 case F_ToggleOnlyNames:
2743 TOGGLE_FLAG (ONLYNAMESFLAG, PCB);
2744 CLEAR_FLAG (LOCKNAMESFLAG, PCB);
2745 break;
2747 case F_ToggleHideNames:
2748 TOGGLE_FLAG (HIDENAMESFLAG, PCB);
2749 Redraw ();
2750 break;
2752 case F_ToggleShowDRC:
2753 TOGGLE_FLAG (SHOWDRCFLAG, PCB);
2754 break;
2756 case F_ToggleLiveRoute:
2757 TOGGLE_FLAG (LIVEROUTEFLAG, PCB);
2758 break;
2760 case F_ToggleAutoDRC:
2761 notify_crosshair_change (false);
2762 TOGGLE_FLAG (AUTODRCFLAG, PCB);
2763 if (TEST_FLAG (AUTODRCFLAG, PCB) && Settings.Mode == LINE_MODE)
2765 if (ClearFlagOnAllObjects (true, CONNECTEDFLAG | FOUNDFLAG))
2767 IncrementUndoSerialNumber ();
2768 Draw ();
2770 if (Crosshair.AttachedLine.State != STATE_FIRST)
2772 LookupConnection (Crosshair.AttachedLine.Point1.X,
2773 Crosshair.AttachedLine.Point1.Y,
2774 true, 1, CONNECTEDFLAG, false);
2775 LookupConnection (Crosshair.AttachedLine.Point1.X,
2776 Crosshair.AttachedLine.Point1.Y,
2777 true, 1, FOUNDFLAG, true);
2780 notify_crosshair_change (true);
2781 break;
2783 case F_ToggleCheckPlanes:
2784 TOGGLE_FLAG (CHECKPLANESFLAG, PCB);
2785 Redraw ();
2786 break;
2788 case F_ToggleOrthoMove:
2789 TOGGLE_FLAG (ORTHOMOVEFLAG, PCB);
2790 break;
2792 case F_ToggleName:
2793 TOGGLE_FLAG (SHOWNUMBERFLAG, PCB);
2794 Redraw ();
2795 break;
2797 case F_ToggleMask:
2798 TOGGLE_FLAG (SHOWMASKFLAG, PCB);
2799 Redraw ();
2800 break;
2802 case F_ToggleClearLine:
2803 TOGGLE_FLAG (CLEARNEWFLAG, PCB);
2804 break;
2806 case F_ToggleFullPoly:
2807 TOGGLE_FLAG (NEWFULLPOLYFLAG, PCB);
2808 break;
2810 /* shift grid alignment */
2811 case F_ToggleGrid:
2813 Coord oldGrid = PCB->Grid;
2815 PCB->Grid = 1;
2816 if (MoveCrosshairAbsolute (Crosshair.X, Crosshair.Y))
2817 notify_crosshair_change (true); /* first notify was in MoveCrosshairAbs */
2818 SetGrid (oldGrid, true);
2820 break;
2822 /* toggle displaying of the grid */
2823 case F_Grid:
2824 Settings.DrawGrid = !Settings.DrawGrid;
2825 Redraw ();
2826 break;
2828 /* display the pinout of an element */
2829 case F_Pinout:
2831 ElementType *element;
2832 void *ptrtmp;
2833 Coord x, y;
2835 gui->get_coords (_("Click on an element"), &x, &y);
2836 if ((SearchScreen
2837 (x, y, ELEMENT_TYPE, &ptrtmp,
2838 &ptrtmp, &ptrtmp)) != NO_TYPE)
2840 element = (ElementType *) ptrtmp;
2841 gui->show_item (element);
2843 break;
2846 /* toggle displaying of pin/pad/via names */
2847 case F_PinOrPadName:
2849 void *ptr1, *ptr2, *ptr3;
2851 switch (SearchScreen (Crosshair.X, Crosshair.Y,
2852 ELEMENT_TYPE | PIN_TYPE | PAD_TYPE |
2853 VIA_TYPE, (void **) &ptr1, (void **) &ptr2,
2854 (void **) &ptr3))
2856 case ELEMENT_TYPE:
2857 PIN_LOOP ((ElementType *) ptr1);
2859 if (TEST_FLAG (DISPLAYNAMEFLAG, pin))
2860 ErasePinName (pin);
2861 else
2862 DrawPinName (pin);
2863 AddObjectToFlagUndoList (PIN_TYPE, ptr1, pin, pin);
2864 TOGGLE_FLAG (DISPLAYNAMEFLAG, pin);
2866 END_LOOP;
2867 PAD_LOOP ((ElementType *) ptr1);
2869 if (TEST_FLAG (DISPLAYNAMEFLAG, pad))
2870 ErasePadName (pad);
2871 else
2872 DrawPadName (pad);
2873 AddObjectToFlagUndoList (PAD_TYPE, ptr1, pad, pad);
2874 TOGGLE_FLAG (DISPLAYNAMEFLAG, pad);
2876 END_LOOP;
2877 SetChangedFlag (true);
2878 IncrementUndoSerialNumber ();
2879 Draw ();
2880 break;
2882 case PIN_TYPE:
2883 if (TEST_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2))
2884 ErasePinName ((PinType *) ptr2);
2885 else
2886 DrawPinName ((PinType *) ptr2);
2887 AddObjectToFlagUndoList (PIN_TYPE, ptr1, ptr2, ptr3);
2888 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2);
2889 SetChangedFlag (true);
2890 IncrementUndoSerialNumber ();
2891 Draw ();
2892 break;
2894 case PAD_TYPE:
2895 if (TEST_FLAG (DISPLAYNAMEFLAG, (PadType *) ptr2))
2896 ErasePadName ((PadType *) ptr2);
2897 else
2898 DrawPadName ((PadType *) ptr2);
2899 AddObjectToFlagUndoList (PAD_TYPE, ptr1, ptr2, ptr3);
2900 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PadType *) ptr2);
2901 SetChangedFlag (true);
2902 IncrementUndoSerialNumber ();
2903 Draw ();
2904 break;
2905 case VIA_TYPE:
2906 if (TEST_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2))
2907 EraseViaName ((PinType *) ptr2);
2908 else
2909 DrawViaName ((PinType *) ptr2);
2910 AddObjectToFlagUndoList (VIA_TYPE, ptr1, ptr2, ptr3);
2911 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2);
2912 SetChangedFlag (true);
2913 IncrementUndoSerialNumber ();
2914 Draw ();
2915 break;
2917 break;
2919 default:
2920 err = 1;
2923 else if (function && str_dir)
2925 switch (GetFunctionID (function))
2927 case F_ToggleGrid:
2928 if (argc > 2)
2930 PCB->GridOffsetX = GetValue (argv[1], NULL, NULL);
2931 PCB->GridOffsetY = GetValue (argv[2], NULL, NULL);
2932 if (Settings.DrawGrid)
2933 Redraw ();
2935 break;
2937 default:
2938 err = 1;
2939 break;
2943 if (!err)
2944 return 0;
2946 AFAIL (display);
2949 /* --------------------------------------------------------------------------- */
2951 static const char mode_syntax[] =
2952 N_("Mode(Arc|Arrow|Copy|InsertPoint|Line|Lock|Move|None|PasteBuffer)\n"
2953 "Mode(Polygon|Rectangle|Remove|Rotate|Text|Thermal|Via)\n"
2954 "Mode(Notify|Release|Cancel|Stroke)\n"
2955 "Mode(Save|Restore)");
2957 static const char mode_help[] = N_("Change or use the tool mode.");
2959 /* %start-doc actions Mode
2961 @table @code
2963 @item Arc
2964 @itemx Arrow
2965 @itemx Copy
2966 @itemx InsertPoint
2967 @itemx Line
2968 @itemx Lock
2969 @itemx Move
2970 @itemx None
2971 @itemx PasteBuffer
2972 @itemx Polygon
2973 @itemx Rectangle
2974 @itemx Remove
2975 @itemx Rotate
2976 @itemx Text
2977 @itemx Thermal
2978 @itemx Via
2979 Select the indicated tool.
2981 @item Notify
2982 Called when you press the mouse button, or move the mouse.
2984 @item Release
2985 Called when you release the mouse button.
2987 @item Cancel
2988 Cancels any pending tool activity, allowing you to restart elsewhere.
2989 For example, this allows you to start a new line rather than attach a
2990 line to the previous line.
2992 @item Escape
2993 Similar to Cancel but calling this action a second time will return
2994 to the Arrow tool.
2996 @item Stroke
2997 If your @code{pcb} was built with libstroke, this invokes the stroke
2998 input method. If not, this will restart a drawing mode if you were
2999 drawing, else it will select objects.
3001 @item Save
3002 Remembers the current tool.
3004 @item Restore
3005 Restores the tool to the last saved tool.
3007 @end table
3009 %end-doc */
3011 static int
3012 ActionMode (int argc, char **argv, Coord x, Coord y)
3014 char *function = ARG (0);
3016 if (function)
3018 Note.X = Crosshair.X;
3019 Note.Y = Crosshair.Y;
3020 notify_crosshair_change (false);
3021 switch (GetFunctionID (function))
3023 case F_Arc:
3024 SetMode (ARC_MODE);
3025 break;
3026 case F_Arrow:
3027 SetMode (ARROW_MODE);
3028 break;
3029 case F_Copy:
3030 SetMode (COPY_MODE);
3031 break;
3032 case F_InsertPoint:
3033 SetMode (INSERTPOINT_MODE);
3034 break;
3035 case F_Line:
3036 SetMode (LINE_MODE);
3037 break;
3038 case F_Lock:
3039 SetMode (LOCK_MODE);
3040 break;
3041 case F_Move:
3042 SetMode (MOVE_MODE);
3043 break;
3044 case F_None:
3045 SetMode (NO_MODE);
3046 break;
3047 case F_Cancel:
3049 int saved_mode = Settings.Mode;
3050 SetMode (NO_MODE);
3051 SetMode (saved_mode);
3053 break;
3054 case F_Escape:
3056 switch (Settings.Mode)
3058 case VIA_MODE:
3059 case PASTEBUFFER_MODE:
3060 case TEXT_MODE:
3061 case ROTATE_MODE:
3062 case REMOVE_MODE:
3063 case MOVE_MODE:
3064 case COPY_MODE:
3065 case INSERTPOINT_MODE:
3066 case RUBBERBANDMOVE_MODE:
3067 case THERMAL_MODE:
3068 case LOCK_MODE:
3069 SetMode (NO_MODE);
3070 SetMode (ARROW_MODE);
3071 break;
3073 case LINE_MODE:
3074 if (Crosshair.AttachedLine.State == STATE_FIRST)
3075 SetMode (ARROW_MODE);
3076 else
3078 SetMode (NO_MODE);
3079 SetMode (LINE_MODE);
3081 break;
3083 case RECTANGLE_MODE:
3084 if (Crosshair.AttachedBox.State == STATE_FIRST)
3085 SetMode (ARROW_MODE);
3086 else
3088 SetMode (NO_MODE);
3089 SetMode (RECTANGLE_MODE);
3091 break;
3093 case POLYGON_MODE:
3094 if (Crosshair.AttachedLine.State == STATE_FIRST)
3095 SetMode (ARROW_MODE);
3096 else
3098 SetMode (NO_MODE);
3099 SetMode (POLYGON_MODE);
3101 break;
3103 case POLYGONHOLE_MODE:
3104 if (Crosshair.AttachedLine.State == STATE_FIRST)
3105 SetMode (ARROW_MODE);
3106 else
3108 SetMode (NO_MODE);
3109 SetMode (POLYGONHOLE_MODE);
3111 break;
3113 case ARC_MODE:
3114 if (Crosshair.AttachedBox.State == STATE_FIRST)
3115 SetMode (ARROW_MODE);
3116 else
3118 SetMode (NO_MODE);
3119 SetMode (ARC_MODE);
3121 break;
3123 case ARROW_MODE:
3124 break;
3126 default:
3127 break;
3130 break;
3132 case F_Notify:
3133 NotifyMode ();
3134 break;
3135 case F_PasteBuffer:
3136 SetMode (PASTEBUFFER_MODE);
3137 break;
3138 case F_Polygon:
3139 SetMode (POLYGON_MODE);
3140 break;
3141 case F_PolygonHole:
3142 SetMode (POLYGONHOLE_MODE);
3143 break;
3144 #ifndef HAVE_LIBSTROKE
3145 case F_Release:
3146 ReleaseMode ();
3147 break;
3148 #else
3149 case F_Release:
3150 if (mid_stroke)
3151 FinishStroke ();
3152 else
3153 ReleaseMode ();
3154 break;
3155 #endif
3156 case F_Remove:
3157 SetMode (REMOVE_MODE);
3158 break;
3159 case F_Rectangle:
3160 SetMode (RECTANGLE_MODE);
3161 break;
3162 case F_Rotate:
3163 SetMode (ROTATE_MODE);
3164 break;
3165 case F_Stroke:
3166 #ifdef HAVE_LIBSTROKE
3167 mid_stroke = true;
3168 StrokeBox.X1 = Crosshair.X;
3169 StrokeBox.Y1 = Crosshair.Y;
3170 break;
3171 #else
3172 /* Handle middle mouse button restarts of drawing mode. If not in
3173 | a drawing mode, middle mouse button will select objects.
3175 if (Settings.Mode == LINE_MODE
3176 && Crosshair.AttachedLine.State != STATE_FIRST)
3178 SetMode (LINE_MODE);
3180 else if (Settings.Mode == ARC_MODE
3181 && Crosshair.AttachedBox.State != STATE_FIRST)
3182 SetMode (ARC_MODE);
3183 else if (Settings.Mode == RECTANGLE_MODE
3184 && Crosshair.AttachedBox.State != STATE_FIRST)
3185 SetMode (RECTANGLE_MODE);
3186 else if (Settings.Mode == POLYGON_MODE
3187 && Crosshair.AttachedLine.State != STATE_FIRST)
3188 SetMode (POLYGON_MODE);
3189 else
3191 SaveMode ();
3192 saved_mode = true;
3193 SetMode (ARROW_MODE);
3194 NotifyMode ();
3196 break;
3197 #endif
3198 case F_Text:
3199 SetMode (TEXT_MODE);
3200 break;
3201 case F_Thermal:
3202 SetMode (THERMAL_MODE);
3203 break;
3204 case F_Via:
3205 SetMode (VIA_MODE);
3206 break;
3208 case F_Restore: /* restore the last saved mode */
3209 RestoreMode ();
3210 break;
3212 case F_Save: /* save currently selected mode */
3213 SaveMode ();
3214 break;
3216 notify_crosshair_change (true);
3217 return 0;
3220 AFAIL (mode);
3223 /* --------------------------------------------------------------------------- */
3225 static const char removeselected_syntax[] = N_("RemoveSelected()");
3227 static const char removeselected_help[] = N_("Removes any selected objects.");
3229 /* %start-doc actions RemoveSelected
3231 %end-doc */
3233 static int
3234 ActionRemoveSelected (int argc, char **argv, Coord x, Coord y)
3236 if (RemoveSelected ())
3237 SetChangedFlag (true);
3238 return 0;
3241 /* --------------------------------------------------------------------------- */
3243 static const char renumber_syntax[] = N_("Renumber()\n"
3244 "Renumber(filename)");
3246 static const char renumber_help[] =
3247 N_("Renumber all elements. The changes will be recorded to filename\n"
3248 "for use in backannotating these changes to the schematic.");
3250 /* %start-doc actions Renumber
3252 %end-doc */
3254 static int
3255 ActionRenumber (int argc, char **argv, Coord x, Coord y)
3257 bool changed = false;
3258 ElementType **element_list;
3259 ElementType **locked_element_list;
3260 unsigned int i, j, k, cnt, lock_cnt;
3261 unsigned int tmpi;
3262 size_t sz;
3263 char *tmps;
3264 char *name;
3265 FILE *out;
3266 static char * default_file = NULL;
3267 size_t cnt_list_sz = 100;
3268 struct _cnt_list
3270 char *name;
3271 unsigned int cnt;
3272 } *cnt_list;
3273 char **was, **is, *pin;
3274 unsigned int c_cnt = 0;
3275 int unique, ok;
3276 int free_name = 0;
3278 if (argc < 1)
3281 * We deal with the case where name already exists in this
3282 * function so the GUI doesn't need to deal with it
3284 name = gui->fileselect (_("Save Renumber Annotation File As ..."),
3285 _("Choose a file to record the renumbering to.\n"
3286 "This file may be used to back annotate the\n"
3287 "change to the schematics.\n"),
3288 default_file, ".eco", "eco",
3291 free_name = 1;
3293 else
3294 name = argv[0];
3296 if (default_file)
3298 free (default_file);
3299 default_file = NULL;
3302 if (name && *name)
3304 default_file = strdup (name);
3307 if ((out = fopen (name, "r")))
3309 fclose (out);
3310 if (!gui->confirm_dialog (_("File exists! Ok to overwrite?"), 0))
3312 if (free_name && name)
3313 free (name);
3314 return 0;
3318 if ((out = fopen (name, "w")) == NULL)
3320 Message (_("Could not open %s\n"), name);
3321 if (free_name && name)
3322 free (name);
3323 return 1;
3326 if (free_name && name)
3327 free (name);
3329 fprintf (out, "*COMMENT* PCB Annotation File\n");
3330 fprintf (out, "*FILEVERSION* 20061031\n");
3333 * Make a first pass through all of the elements and sort them out
3334 * by location on the board. While here we also collect a list of
3335 * locked elements.
3337 * We'll actually renumber things in the 2nd pass.
3339 element_list = (ElementType **)calloc (PCB->Data->ElementN, sizeof (ElementType *));
3340 locked_element_list = (ElementType **)calloc (PCB->Data->ElementN, sizeof (ElementType *));
3341 was = (char **)calloc (PCB->Data->ElementN, sizeof (char *));
3342 is = (char **)calloc (PCB->Data->ElementN, sizeof (char *));
3343 if (element_list == NULL || locked_element_list == NULL || was == NULL
3344 || is == NULL)
3346 fprintf (stderr, "calloc() failed in %s\n", __FUNCTION__);
3347 exit (1);
3351 cnt = 0;
3352 lock_cnt = 0;
3353 ELEMENT_LOOP (PCB->Data);
3355 if (TEST_FLAG (LOCKFLAG, element->Name) || TEST_FLAG (LOCKFLAG, element))
3358 * add to the list of locked elements which we won't try to
3359 * renumber and whose reference designators are now reserved.
3361 pcb_fprintf (out,
3362 "*WARN* Element \"%s\" at %$md is locked and will not be renumbered.\n",
3363 UNKNOWN (NAMEONPCB_NAME (element)), element->MarkX, element->MarkY);
3364 locked_element_list[lock_cnt] = element;
3365 lock_cnt++;
3368 else
3370 /* count of devices which will be renumbered */
3371 cnt++;
3373 /* search for correct position in the list */
3374 i = 0;
3375 while (element_list[i] && element->MarkY > element_list[i]->MarkY)
3376 i++;
3379 * We have found the position where we have the first element that
3380 * has the same Y value or a lower Y value. Now move forward if
3381 * needed through the X values
3383 while (element_list[i]
3384 && element->MarkY == element_list[i]->MarkY
3385 && element->MarkX > element_list[i]->MarkX)
3386 i++;
3388 for (j = cnt - 1; j > i; j--)
3390 element_list[j] = element_list[j - 1];
3392 element_list[i] = element;
3395 END_LOOP;
3399 * Now that the elements are sorted by board position, we go through
3400 * and renumber them.
3404 * turn off the flag which requires unique names so it doesn't get
3405 * in our way. When we're done with the renumber we will have unique
3406 * names.
3408 unique = TEST_FLAG (UNIQUENAMEFLAG, PCB);
3409 CLEAR_FLAG (UNIQUENAMEFLAG, PCB);
3411 cnt_list = (struct _cnt_list *)calloc (cnt_list_sz, sizeof (struct _cnt_list));
3412 for (i = 0; i < cnt; i++)
3414 /* If there is no refdes, maybe just spit out a warning */
3415 if (NAMEONPCB_NAME (element_list[i]))
3417 /* figure out the prefix */
3418 tmps = strdup (NAMEONPCB_NAME (element_list[i]));
3419 j = 0;
3420 while (tmps[j] && (tmps[j] < '0' || tmps[j] > '9')
3421 && tmps[j] != '?')
3422 j++;
3423 tmps[j] = '\0';
3425 /* check the counter for this prefix */
3426 for (j = 0;
3427 cnt_list[j].name && (strcmp (cnt_list[j].name, tmps) != 0)
3428 && j < cnt_list_sz; j++);
3430 /* grow the list if needed */
3431 if (j == cnt_list_sz)
3433 cnt_list_sz += 100;
3434 cnt_list = (struct _cnt_list *)realloc (cnt_list, cnt_list_sz);
3435 if (cnt_list == NULL)
3437 fprintf (stderr, _("realloc failed() in %s\n"), __FUNCTION__);
3438 exit (1);
3440 /* zero out the memory that we added */
3441 for (tmpi = j; tmpi < cnt_list_sz; tmpi++)
3443 cnt_list[tmpi].name = NULL;
3444 cnt_list[tmpi].cnt = 0;
3449 * start a new counter if we don't have a counter for this
3450 * prefix
3452 if (!cnt_list[j].name)
3454 cnt_list[j].name = strdup (tmps);
3455 cnt_list[j].cnt = 0;
3459 * check to see if the new refdes is already used by a
3460 * locked element
3464 ok = 1;
3465 cnt_list[j].cnt++;
3466 free (tmps);
3468 /* space for the prefix plus 1 digit plus the '\0' */
3469 sz = strlen (cnt_list[j].name) + 2;
3471 /* and 1 more per extra digit needed to hold the number */
3472 tmpi = cnt_list[j].cnt;
3473 while (tmpi > 10)
3475 sz++;
3476 tmpi = tmpi / 10;
3478 tmps = (char *)malloc (sz * sizeof (char));
3479 sprintf (tmps, "%s%d", cnt_list[j].name, cnt_list[j].cnt);
3482 * now compare to the list of reserved (by locked
3483 * elements) names
3485 for (k = 0; k < lock_cnt; k++)
3487 if (strcmp
3488 (UNKNOWN (NAMEONPCB_NAME (locked_element_list[k])),
3489 tmps) == 0)
3491 ok = 0;
3492 break;
3497 while (!ok);
3499 if (strcmp (tmps, NAMEONPCB_NAME (element_list[i])) != 0)
3501 fprintf (out, "*RENAME* \"%s\" \"%s\"\n",
3502 NAMEONPCB_NAME (element_list[i]), tmps);
3504 /* add this rename to our table of renames so we can update the netlist */
3505 was[c_cnt] = strdup (NAMEONPCB_NAME (element_list[i]));
3506 is[c_cnt] = strdup (tmps);
3507 c_cnt++;
3509 AddObjectToChangeNameUndoList (ELEMENT_TYPE, NULL, NULL,
3510 element_list[i],
3511 NAMEONPCB_NAME (element_list
3512 [i]));
3514 ChangeObjectName (ELEMENT_TYPE, element_list[i], NULL, NULL,
3515 tmps);
3516 changed = true;
3518 /* we don't free tmps in this case because it is used */
3520 else
3521 free (tmps);
3523 else
3525 pcb_fprintf (out, "*WARN* Element at %$md has no name.\n",
3526 element_list[i]->MarkX, element_list[i]->MarkY);
3531 fclose (out);
3533 /* restore the unique flag setting */
3534 if (unique)
3535 SET_FLAG (UNIQUENAMEFLAG, PCB);
3537 if (changed)
3540 /* update the netlist */
3541 AddNetlistLibToUndoList (&(PCB->NetlistLib));
3543 /* iterate over each net */
3544 for (i = 0; i < PCB->NetlistLib.MenuN; i++)
3547 /* iterate over each pin on the net */
3548 for (j = 0; j < PCB->NetlistLib.Menu[i].EntryN; j++)
3551 /* figure out the pin number part from strings like U3-21 */
3552 tmps = strdup (PCB->NetlistLib.Menu[i].Entry[j].ListEntry);
3553 for (k = 0; tmps[k] && tmps[k] != '-'; k++);
3554 tmps[k] = '\0';
3555 pin = tmps + k + 1;
3557 /* iterate over the list of changed reference designators */
3558 for (k = 0; k < c_cnt; k++)
3561 * if the pin needs to change, change it and quit
3562 * searching in the list.
3564 if (strcmp (tmps, was[k]) == 0)
3566 free (PCB->NetlistLib.Menu[i].Entry[j].ListEntry);
3567 PCB->NetlistLib.Menu[i].Entry[j].ListEntry =
3568 (char *)malloc ((strlen (is[k]) + strlen (pin) +
3569 2) * sizeof (char));
3570 sprintf (PCB->NetlistLib.Menu[i].Entry[j].ListEntry,
3571 "%s-%s", is[k], pin);
3572 k = c_cnt;
3576 free (tmps);
3579 for (k = 0; k < c_cnt; k++)
3581 free (was[k]);
3582 free (is[k]);
3585 NetlistChanged (0);
3586 IncrementUndoSerialNumber ();
3587 SetChangedFlag (true);
3590 free (locked_element_list);
3591 free (element_list);
3592 free (cnt_list);
3593 return 0;
3597 /* --------------------------------------------------------------------------- */
3599 static const char ripup_syntax[] = N_("RipUp(All|Selected|Element)");
3601 static const char ripup_help[] =
3602 N_("Ripup auto-routed tracks, or convert an element to parts.");
3604 /* %start-doc actions RipUp
3606 @table @code
3608 @item All
3609 Removes all lines and vias which were created by the autorouter.
3611 @item Selected
3612 Removes all selected lines and vias which were created by the
3613 autorouter.
3615 @item Element
3616 Converts the element under the cursor to parts (vias and lines). Note
3617 that this uses the highest numbered paste buffer.
3619 @end table
3621 %end-doc */
3623 static int
3624 ActionRipUp (int argc, char **argv, Coord x, Coord y)
3626 char *function = ARG (0);
3627 bool changed = false;
3629 if (function)
3631 switch (GetFunctionID (function))
3633 case F_All:
3634 ALLLINE_LOOP (PCB->Data);
3636 if (TEST_FLAG (AUTOFLAG, line) && !TEST_FLAG (LOCKFLAG, line))
3638 RemoveObject (LINE_TYPE, layer, line, line);
3639 changed = true;
3642 ENDALL_LOOP;
3643 ALLARC_LOOP (PCB->Data);
3645 if (TEST_FLAG (AUTOFLAG, arc) && !TEST_FLAG (LOCKFLAG, arc))
3647 RemoveObject (ARC_TYPE, layer, arc, arc);
3648 changed = true;
3651 ENDALL_LOOP;
3652 VIA_LOOP (PCB->Data);
3654 if (TEST_FLAG (AUTOFLAG, via) && !TEST_FLAG (LOCKFLAG, via))
3656 RemoveObject (VIA_TYPE, via, via, via);
3657 changed = true;
3660 END_LOOP;
3662 if (changed)
3664 IncrementUndoSerialNumber ();
3665 SetChangedFlag (true);
3667 break;
3668 case F_Selected:
3669 VISIBLELINE_LOOP (PCB->Data);
3671 if (TEST_FLAGS (AUTOFLAG | SELECTEDFLAG, line)
3672 && !TEST_FLAG (LOCKFLAG, line))
3674 RemoveObject (LINE_TYPE, layer, line, line);
3675 changed = true;
3678 ENDALL_LOOP;
3679 if (PCB->ViaOn)
3680 VIA_LOOP (PCB->Data);
3682 if (TEST_FLAGS (AUTOFLAG | SELECTEDFLAG, via)
3683 && !TEST_FLAG (LOCKFLAG, via))
3685 RemoveObject (VIA_TYPE, via, via, via);
3686 changed = true;
3689 END_LOOP;
3690 if (changed)
3692 IncrementUndoSerialNumber ();
3693 SetChangedFlag (true);
3695 break;
3696 case F_Element:
3698 void *ptr1, *ptr2, *ptr3;
3700 if (SearchScreen (Crosshair.X, Crosshair.Y, ELEMENT_TYPE,
3701 &ptr1, &ptr2, &ptr3) != NO_TYPE)
3703 Note.Buffer = Settings.BufferNumber;
3704 SetBufferNumber (MAX_BUFFER - 1);
3705 ClearBuffer (PASTEBUFFER);
3706 CopyObjectToBuffer (PASTEBUFFER->Data, PCB->Data,
3707 ELEMENT_TYPE, ptr1, ptr2, ptr3);
3708 SmashBufferElement (PASTEBUFFER);
3709 PASTEBUFFER->X = 0;
3710 PASTEBUFFER->Y = 0;
3711 SaveUndoSerialNumber ();
3712 EraseObject (ELEMENT_TYPE, ptr1, ptr1);
3713 MoveObjectToRemoveUndoList (ELEMENT_TYPE, ptr1, ptr2, ptr3);
3714 RestoreUndoSerialNumber ();
3715 CopyPastebufferToLayout (0, 0);
3716 SetBufferNumber (Note.Buffer);
3717 SetChangedFlag (true);
3720 break;
3723 return 0;
3726 /* --------------------------------------------------------------------------- */
3728 static const char addrats_syntax[] = N_("AddRats(AllRats|SelectedRats|Close)");
3730 static const char addrats_help[] =
3731 N_("Add one or more rat lines to the board.");
3733 /* %start-doc actions AddRats
3735 @table @code
3737 @item AllRats
3738 Create rat lines for all loaded nets that aren't already connected on
3739 with copper.
3741 @item SelectedRats
3742 Similarly, but only add rat lines for nets connected to selected pins
3743 and pads.
3745 @item Close
3746 Selects the shortest unselected rat on the board.
3748 @end table
3750 %end-doc */
3752 static int
3753 ActionAddRats (int argc, char **argv, Coord x, Coord y)
3755 char *function = ARG (0);
3756 RatType *shorty;
3757 float len, small;
3759 if (function)
3761 if (Settings.RatWarn)
3762 ClearWarnings ();
3763 switch (GetFunctionID (function))
3765 case F_AllRats:
3766 if (AddAllRats (false, NULL))
3767 SetChangedFlag (true);
3768 break;
3769 case F_SelectedRats:
3770 case F_Selected:
3771 if (AddAllRats (true, NULL))
3772 SetChangedFlag (true);
3773 break;
3774 case F_Close:
3775 small = SQUARE (MAX_COORD);
3776 shorty = NULL;
3777 RAT_LOOP (PCB->Data);
3779 if (TEST_FLAG (SELECTEDFLAG, line))
3780 continue;
3781 len = SQUARE (line->Point1.X - line->Point2.X) +
3782 SQUARE (line->Point1.Y - line->Point2.Y);
3783 if (len < small)
3785 small = len;
3786 shorty = line;
3789 END_LOOP;
3790 if (shorty)
3792 AddObjectToFlagUndoList (RATLINE_TYPE, shorty, shorty, shorty);
3793 SET_FLAG (SELECTEDFLAG, shorty);
3794 DrawRat (shorty);
3795 Draw ();
3796 CenterDisplay ((shorty->Point2.X + shorty->Point1.X) / 2,
3797 (shorty->Point2.Y + shorty->Point1.Y) / 2);
3799 break;
3802 return 0;
3805 /* --------------------------------------------------------------------------- */
3807 static const char delete_syntax[] =
3808 N_("Delete(Object|Selected)\n"
3809 "Delete(AllRats|SelectedRats)");
3811 static const char delete_help[] = N_("Delete stuff.");
3813 /* %start-doc actions Delete
3815 %end-doc */
3817 static int
3818 ActionDelete (int argc, char **argv, Coord x, Coord y)
3820 char *function = ARG (0);
3821 int id = GetFunctionID (function);
3823 Note.X = Crosshair.X;
3824 Note.Y = Crosshair.Y;
3826 if (id == -1) /* no arg */
3828 if (RemoveSelected() == false)
3829 id = F_Object;
3832 switch (id)
3834 case F_Object:
3835 SaveMode();
3836 SetMode(REMOVE_MODE);
3837 NotifyMode();
3838 RestoreMode();
3839 break;
3840 case F_Selected:
3841 RemoveSelected();
3842 break;
3843 case F_AllRats:
3844 if (DeleteRats (false))
3845 SetChangedFlag (true);
3846 break;
3847 case F_SelectedRats:
3848 if (DeleteRats (true))
3849 SetChangedFlag (true);
3850 break;
3853 return 0;
3856 /* --------------------------------------------------------------------------- */
3858 static const char deleterats_syntax[] =
3859 N_("DeleteRats(AllRats|Selected|SelectedRats)");
3861 static const char deleterats_help[] = N_("Delete rat lines.");
3863 /* %start-doc actions DeleteRats
3865 %end-doc */
3867 static int
3868 ActionDeleteRats (int argc, char **argv, Coord x, Coord y)
3870 char *function = ARG (0);
3871 if (function)
3873 if (Settings.RatWarn)
3874 ClearWarnings ();
3875 switch (GetFunctionID (function))
3877 case F_AllRats:
3878 if (DeleteRats (false))
3879 SetChangedFlag (true);
3880 break;
3881 case F_SelectedRats:
3882 case F_Selected:
3883 if (DeleteRats (true))
3884 SetChangedFlag (true);
3885 break;
3888 return 0;
3891 /* --------------------------------------------------------------------------- */
3893 static const char autoplace_syntax[] = N_("AutoPlaceSelected()");
3895 static const char autoplace_help[] = N_("Auto-place selected components.");
3897 /* %start-doc actions AutoPlaceSelected
3899 Attempts to re-arrange the selected components such that the nets
3900 connecting them are minimized. Note that you cannot undo this.
3902 %end-doc */
3904 static int
3905 ActionAutoPlaceSelected (int argc, char **argv, Coord x, Coord y)
3907 hid_action("Busy");
3908 if (gui->confirm_dialog (_("Auto-placement can NOT be undone.\n"
3909 "Do you want to continue anyway?\n"), 0))
3911 if (AutoPlaceSelected ())
3912 SetChangedFlag (true);
3914 return 0;
3917 /* --------------------------------------------------------------------------- */
3919 static const char autoroute_syntax[] = N_("AutoRoute(AllRats|SelectedRats)");
3921 static const char autoroute_help[] = N_("Auto-route some or all rat lines.");
3923 /* %start-doc actions AutoRoute
3925 @table @code
3927 @item AllRats
3928 Attempt to autoroute all rats.
3930 @item SelectedRats
3931 Attempt to autoroute the selected rats.
3933 @end table
3935 Before autorouting, it's important to set up a few things. First,
3936 make sure any layers you aren't using are disabled, else the
3937 autorouter may use them. Next, make sure the current line and via
3938 styles are set accordingly. Last, make sure "new lines clear
3939 polygons" is set, in case you eventually want to add a copper pour.
3941 Autorouting takes a while. During this time, the program may not be
3942 responsive.
3944 %end-doc */
3946 static int
3947 ActionAutoRoute (int argc, char **argv, Coord x, Coord y)
3949 char *function = ARG (0);
3950 hid_action("Busy");
3951 if (function) /* one parameter */
3953 switch (GetFunctionID (function))
3955 case F_AllRats:
3956 if (AutoRoute (false))
3957 SetChangedFlag (true);
3958 break;
3959 case F_SelectedRats:
3960 case F_Selected:
3961 if (AutoRoute (true))
3962 SetChangedFlag (true);
3963 break;
3966 return 0;
3969 /* --------------------------------------------------------------------------- */
3971 static const char markcrosshair_syntax[] =
3972 N_("MarkCrosshair()\n"
3973 "MarkCrosshair(Center)");
3975 static const char markcrosshair_help[] = N_("Set/Reset the Crosshair mark.");
3977 /* %start-doc actions MarkCrosshair
3979 The ``mark'' is a small X-shaped target on the display which is
3980 treated like a second origin (the normal origin is the upper let
3981 corner of the board). The GUI will display a second set of
3982 coordinates for this mark, which tells you how far you are from it.
3984 If no argument is given, the mark is toggled - disabled if it was
3985 enabled, or enabled at the current cursor position of disabled. If
3986 the @code{Center} argument is given, the mark is moved to the current
3987 cursor location.
3989 %end-doc */
3991 static int
3992 ActionMarkCrosshair (int argc, char **argv, Coord x, Coord y)
3994 char *function = ARG (0);
3995 if (!function || !*function)
3997 if (Marked.status)
3999 notify_mark_change (false);
4000 Marked.status = false;
4001 notify_mark_change (true);
4003 else
4005 notify_mark_change (false);
4006 Marked.status = false;
4007 Marked.status = true;
4008 Marked.X = Crosshair.X;
4009 Marked.Y = Crosshair.Y;
4010 notify_mark_change (true);
4013 else if (GetFunctionID (function) == F_Center)
4015 notify_mark_change (false);
4016 Marked.status = true;
4017 Marked.X = Crosshair.X;
4018 Marked.Y = Crosshair.Y;
4019 notify_mark_change (true);
4021 return 0;
4024 /* --------------------------------------------------------------------------- */
4026 static const char changesize_syntax[] =
4027 N_("ChangeSize(Object, delta)\n"
4028 "ChangeSize(SelectedObjects|Selected, delta)\n"
4029 "ChangeSize(SelectedLines|SelectedPins|SelectedVias, delta)\n"
4030 "ChangeSize(SelectedPads|SelectedTexts|SelectedNames, delta)\n"
4031 "ChangeSize(SelectedElements, delta)");
4033 static const char changesize_help[] = N_("Changes the size of objects.");
4035 /* %start-doc actions ChangeSize
4037 For lines and arcs, this changes the width. For pins and vias, this
4038 changes the overall diameter of the copper annulus. For pads, this
4039 changes the width and, indirectly, the length. For texts and names,
4040 this changes the scaling factor. For elements, this changes the width
4041 of the silk layer lines and arcs for this element.
4043 %end-doc */
4045 static int
4046 ActionChangeSize (int argc, char **argv, Coord x, Coord y)
4048 char *function = ARG (0);
4049 char *delta = ARG (1);
4050 char *units = ARG (2);
4051 bool absolute; /* indicates if absolute size is given */
4052 Coord value;
4054 if (function && delta)
4056 value = GetValue (delta, units, &absolute);
4057 if (value == 0)
4058 value = delta[0] == '-' ? -Settings.increments->size
4059 : Settings.increments->size;
4060 switch (GetFunctionID (function))
4062 case F_Object:
4064 int type;
4065 void *ptr1, *ptr2, *ptr3;
4067 if ((type =
4068 SearchScreen (Crosshair.X, Crosshair.Y, CHANGESIZE_TYPES,
4069 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4070 if (TEST_FLAG (LOCKFLAG, (PinType *) ptr2))
4071 Message (_("Sorry, the object is locked\n"));
4072 if (ChangeObjectSize (type, ptr1, ptr2, ptr3, value, absolute))
4073 SetChangedFlag (true);
4074 break;
4077 case F_SelectedVias:
4078 if (ChangeSelectedSize (VIA_TYPE, value, absolute))
4079 SetChangedFlag (true);
4080 break;
4082 case F_SelectedPins:
4083 if (ChangeSelectedSize (PIN_TYPE, value, absolute))
4084 SetChangedFlag (true);
4085 break;
4087 case F_SelectedPads:
4088 if (ChangeSelectedSize (PAD_TYPE, value, absolute))
4089 SetChangedFlag (true);
4090 break;
4092 case F_SelectedArcs:
4093 if (ChangeSelectedSize (ARC_TYPE, value, absolute))
4094 SetChangedFlag (true);
4095 break;
4097 case F_SelectedLines:
4098 if (ChangeSelectedSize (LINE_TYPE, value, absolute))
4099 SetChangedFlag (true);
4100 break;
4102 case F_SelectedTexts:
4103 if (ChangeSelectedSize (TEXT_TYPE, value, absolute))
4104 SetChangedFlag (true);
4105 break;
4107 case F_SelectedNames:
4108 if (ChangeSelectedSize (ELEMENTNAME_TYPE, value, absolute))
4109 SetChangedFlag (true);
4110 break;
4112 case F_SelectedElements:
4113 if (ChangeSelectedSize (ELEMENT_TYPE, value, absolute))
4114 SetChangedFlag (true);
4115 break;
4117 case F_Selected:
4118 case F_SelectedObjects:
4119 if (ChangeSelectedSize (CHANGESIZE_TYPES, value, absolute))
4120 SetChangedFlag (true);
4121 break;
4124 return 0;
4127 /* --------------------------------------------------------------------------- */
4129 static const char changedrillsize_syntax[] =
4130 N_("ChangeDrillSize(Object, delta)\n"
4131 "ChangeDrillSize(SelectedPins|SelectedVias|Selected|SelectedObjects, delta)");
4133 static const char changedrillsize_help[] =
4134 N_("Changes the drilling hole size of objects.");
4136 /* %start-doc actions ChangeDrillSize
4138 %end-doc */
4140 static int
4141 ActionChange2ndSize (int argc, char **argv, Coord x, Coord y)
4143 char *function = ARG (0);
4144 char *delta = ARG (1);
4145 char *units = ARG (2);
4146 bool absolute;
4147 Coord value;
4149 if (function && delta)
4151 value = GetValue (delta, units, &absolute);
4152 switch (GetFunctionID (function))
4154 case F_Object:
4156 int type;
4157 void *ptr1, *ptr2, *ptr3;
4159 gui->get_coords (_("Select an Object"), &x, &y);
4160 if ((type =
4161 SearchScreen (x, y, CHANGE2NDSIZE_TYPES,
4162 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4163 if (ChangeObject2ndSize
4164 (type, ptr1, ptr2, ptr3, value, absolute, true))
4165 SetChangedFlag (true);
4166 break;
4169 case F_SelectedVias:
4170 if (ChangeSelected2ndSize (VIA_TYPE, value, absolute))
4171 SetChangedFlag (true);
4172 break;
4174 case F_SelectedPins:
4175 if (ChangeSelected2ndSize (PIN_TYPE, value, absolute))
4176 SetChangedFlag (true);
4177 break;
4178 case F_Selected:
4179 case F_SelectedObjects:
4180 if (ChangeSelected2ndSize (PIN_TYPES, value, absolute))
4181 SetChangedFlag (true);
4182 break;
4185 return 0;
4188 /* --------------------------------------------------------------------------- */
4190 static const char changeclearsize_syntax[] =
4191 N_("ChangeClearSize(Object, delta)\n"
4192 "ChangeClearSize(SelectedPins|SelectedPads|SelectedVias, delta)\n"
4193 "ChangeClearSize(SelectedLines|SelectedArcs, delta\n"
4194 "ChangeClearSize(Selected|SelectedObjects, delta)");
4196 static const char changeclearsize_help[] =
4197 N_("Changes the clearance size of objects.");
4199 /* %start-doc actions ChangeClearSize
4201 If the solder mask is currently showing, this action changes the
4202 solder mask clearance. If the mask is not showing, this action
4203 changes the polygon clearance.
4205 %end-doc */
4207 static int
4208 ActionChangeClearSize (int argc, char **argv, Coord x, Coord y)
4210 char *function = ARG (0);
4211 char *delta = ARG (1);
4212 char *units = ARG (2);
4213 bool absolute;
4214 Coord value;
4216 if (function && delta)
4218 value = 2 * GetValue (delta, units, &absolute);
4219 if (value == 0)
4220 value = delta[0] == '-' ? -Settings.increments->clear
4221 : Settings.increments->clear;
4222 switch (GetFunctionID (function))
4224 case F_Object:
4226 int type;
4227 void *ptr1, *ptr2, *ptr3;
4229 gui->get_coords (_("Select an Object"), &x, &y);
4230 if ((type =
4231 SearchScreen (x, y,
4232 CHANGECLEARSIZE_TYPES, &ptr1, &ptr2,
4233 &ptr3)) != NO_TYPE)
4234 if (ChangeObjectClearSize (type, ptr1, ptr2, ptr3, value, absolute))
4235 SetChangedFlag (true);
4236 break;
4238 case F_SelectedVias:
4239 if (ChangeSelectedClearSize (VIA_TYPE, value, absolute))
4240 SetChangedFlag (true);
4241 break;
4242 case F_SelectedPads:
4243 if (ChangeSelectedClearSize (PAD_TYPE, value, absolute))
4244 SetChangedFlag (true);
4245 break;
4246 case F_SelectedPins:
4247 if (ChangeSelectedClearSize (PIN_TYPE, value, absolute))
4248 SetChangedFlag (true);
4249 break;
4250 case F_SelectedLines:
4251 if (ChangeSelectedClearSize (LINE_TYPE, value, absolute))
4252 SetChangedFlag (true);
4253 break;
4254 case F_SelectedArcs:
4255 if (ChangeSelectedClearSize (ARC_TYPE, value, absolute))
4256 SetChangedFlag (true);
4257 break;
4258 case F_Selected:
4259 case F_SelectedObjects:
4260 if (ChangeSelectedClearSize (CHANGECLEARSIZE_TYPES, value, absolute))
4261 SetChangedFlag (true);
4262 break;
4265 return 0;
4268 /* --------------------------------------------------------------------------- */
4270 static const char minmaskgap_syntax[] =
4271 N_("MinMaskGap(delta)\n"
4272 "MinMaskGap(Selected, delta)");
4274 static const char minmaskgap_help[] =
4275 N_("Ensures the mask is a minimum distance from pins and pads.");
4277 /* %start-doc actions MinMaskGap
4279 Checks all specified pins and/or pads, and increases the mask if
4280 needed to ensure a minimum distance between the pin or pad edge and
4281 the mask edge.
4283 %end-doc */
4285 static int
4286 ActionMinMaskGap (int argc, char **argv, Coord x, Coord y)
4288 char *function = ARG (0);
4289 char *delta = ARG (1);
4290 char *units = ARG (2);
4291 bool absolute;
4292 Coord value;
4293 int flags;
4295 if (!function)
4296 return 1;
4297 if (strcasecmp (function, "Selected") == 0)
4298 flags = SELECTEDFLAG;
4299 else
4301 units = delta;
4302 delta = function;
4303 flags = 0;
4305 value = 2 * GetValue (delta, units, &absolute);
4307 SaveUndoSerialNumber ();
4308 ELEMENT_LOOP (PCB->Data);
4310 PIN_LOOP (element);
4312 if (!TEST_FLAGS (flags, pin))
4313 continue;
4314 if (pin->Mask < pin->Thickness + value)
4316 ChangeObjectMaskSize (PIN_TYPE, element, pin, 0,
4317 pin->Thickness + value, 1);
4318 RestoreUndoSerialNumber ();
4321 END_LOOP;
4322 PAD_LOOP (element);
4324 if (!TEST_FLAGS (flags, pad))
4325 continue;
4326 if (pad->Mask < pad->Thickness + value)
4328 ChangeObjectMaskSize (PAD_TYPE, element, pad, 0,
4329 pad->Thickness + value, 1);
4330 RestoreUndoSerialNumber ();
4333 END_LOOP;
4335 END_LOOP;
4336 VIA_LOOP (PCB->Data);
4338 if (!TEST_FLAGS (flags, via))
4339 continue;
4340 if (via->Mask && via->Mask < via->Thickness + value)
4342 ChangeObjectMaskSize (VIA_TYPE, via, 0, 0, via->Thickness + value, 1);
4343 RestoreUndoSerialNumber ();
4346 END_LOOP;
4347 RestoreUndoSerialNumber ();
4348 IncrementUndoSerialNumber ();
4349 return 0;
4352 /* --------------------------------------------------------------------------- */
4354 static const char mincleargap_syntax[] =
4355 N_("MinClearGap(delta)\n"
4356 "MinClearGap(Selected, delta)");
4358 static const char mincleargap_help[] =
4359 N_("Ensures that polygons are a minimum distance from objects.");
4361 /* %start-doc actions MinClearGap
4363 Checks all specified objects, and increases the polygon clearance if
4364 needed to ensure a minimum distance between their edges and the
4365 polygon edges.
4367 %end-doc */
4369 static int
4370 ActionMinClearGap (int argc, char **argv, Coord x, Coord y)
4372 char *function = ARG (0);
4373 char *delta = ARG (1);
4374 char *units = ARG (2);
4375 bool absolute;
4376 Coord value;
4377 int flags;
4379 if (!function)
4380 return 1;
4381 if (strcasecmp (function, "Selected") == 0)
4382 flags = SELECTEDFLAG;
4383 else
4385 units = delta;
4386 delta = function;
4387 flags = 0;
4389 value = 2 * GetValue (delta, units, &absolute);
4391 SaveUndoSerialNumber ();
4392 ELEMENT_LOOP (PCB->Data);
4394 PIN_LOOP (element);
4396 if (!TEST_FLAGS (flags, pin))
4397 continue;
4398 if (pin->Clearance < value)
4400 ChangeObjectClearSize (PIN_TYPE, element, pin, 0,
4401 value, 1);
4402 RestoreUndoSerialNumber ();
4405 END_LOOP;
4406 PAD_LOOP (element);
4408 if (!TEST_FLAGS (flags, pad))
4409 continue;
4410 if (pad->Clearance < value)
4412 ChangeObjectClearSize (PAD_TYPE, element, pad, 0,
4413 value, 1);
4414 RestoreUndoSerialNumber ();
4417 END_LOOP;
4419 END_LOOP;
4420 VIA_LOOP (PCB->Data);
4422 if (!TEST_FLAGS (flags, via))
4423 continue;
4424 if (via->Clearance < value)
4426 ChangeObjectClearSize (VIA_TYPE, via, 0, 0, value, 1);
4427 RestoreUndoSerialNumber ();
4430 END_LOOP;
4431 ALLLINE_LOOP (PCB->Data);
4433 if (!TEST_FLAGS (flags, line))
4434 continue;
4435 if (line->Clearance < value)
4437 ChangeObjectClearSize (LINE_TYPE, layer, line, 0, value, 1);
4438 RestoreUndoSerialNumber ();
4441 ENDALL_LOOP;
4442 ALLARC_LOOP (PCB->Data);
4444 if (!TEST_FLAGS (flags, arc))
4445 continue;
4446 if (arc->Clearance < value)
4448 ChangeObjectClearSize (ARC_TYPE, layer, arc, 0, value, 1);
4449 RestoreUndoSerialNumber ();
4452 ENDALL_LOOP;
4453 RestoreUndoSerialNumber ();
4454 IncrementUndoSerialNumber ();
4455 return 0;
4458 /* --------------------------------------------------------------------------- */
4460 static const char changepinname_syntax[] =
4461 N_("ChangePinName(ElementName,PinNumber,PinName)");
4463 static const char changepinname_help[] =
4464 N_("Sets the name of a specific pin on a specific element.");
4466 /* %start-doc actions ChangePinName
4468 This can be especially useful for annotating pin names from a
4469 schematic to the layout without requiring knowledge of the pcb file
4470 format.
4472 @example
4473 ChangePinName(U3, 7, VCC)
4474 @end example
4476 %end-doc */
4478 static int
4479 ActionChangePinName (int argc, char **argv, Coord x, Coord y)
4481 int changed = 0;
4482 char *refdes, *pinnum, *pinname;
4484 if (argc != 3)
4486 AFAIL (changepinname);
4489 refdes = argv[0];
4490 pinnum = argv[1];
4491 pinname = argv[2];
4493 ELEMENT_LOOP (PCB->Data);
4495 if (NSTRCMP (refdes, NAMEONPCB_NAME (element)) == 0)
4497 PIN_LOOP (element);
4499 if (NSTRCMP (pinnum, pin->Number) == 0)
4501 AddObjectToChangeNameUndoList (PIN_TYPE, NULL, NULL,
4502 pin, pin->Name);
4504 * Note: we can't free() pin->Name first because
4505 * it is used in the undo list
4507 pin->Name = strdup (pinname);
4508 SetChangedFlag (true);
4509 changed = 1;
4512 END_LOOP;
4514 PAD_LOOP (element);
4516 if (NSTRCMP (pinnum, pad->Number) == 0)
4518 AddObjectToChangeNameUndoList (PAD_TYPE, NULL, NULL,
4519 pad, pad->Name);
4521 * Note: we can't free() pad->Name first because
4522 * it is used in the undo list
4524 pad->Name = strdup (pinname);
4525 SetChangedFlag (true);
4526 changed = 1;
4529 END_LOOP;
4532 END_LOOP;
4534 * done with our action so increment the undo # if we actually
4535 * changed anything
4537 if (changed)
4539 if (defer_updates)
4540 defer_needs_update = 1;
4541 else
4543 IncrementUndoSerialNumber ();
4544 gui->invalidate_all ();
4548 return 0;
4551 /* --------------------------------------------------------------------------- */
4553 static const char changename_syntax[] =
4554 N_("ChangeName(Object)\n"
4555 "ChangeName(Layout|Layer)");
4557 static const char changename_help[] = N_("Sets the name of objects.");
4559 /* %start-doc actions ChangeName
4561 @table @code
4563 @item Object
4564 Changes the name of the element under the cursor.
4566 @item Layout
4567 Changes the name of the layout. This is printed on the fab drawings.
4569 @item Layer
4570 Changes the name of the currently active layer.
4572 @end table
4574 %end-doc */
4577 ActionChangeName (int argc, char **argv, Coord x, Coord y)
4579 char *function = ARG (0);
4580 char *name;
4582 if (function)
4584 switch (GetFunctionID (function))
4586 /* change the name of an object */
4587 case F_Object:
4589 int type;
4590 void *ptr1, *ptr2, *ptr3;
4592 gui->get_coords (_("Select an Object"), &x, &y);
4593 if ((type =
4594 SearchScreen (x, y, CHANGENAME_TYPES,
4595 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4597 SaveUndoSerialNumber ();
4598 if (QueryInputAndChangeObjectName (type, ptr1, ptr2, ptr3))
4600 SetChangedFlag (true);
4601 if (type == ELEMENT_TYPE)
4603 RubberbandType *ptr;
4604 int i;
4606 RestoreUndoSerialNumber ();
4607 Crosshair.AttachedObject.RubberbandN = 0;
4608 LookupRatLines (type, ptr1, ptr2, ptr3);
4609 ptr = Crosshair.AttachedObject.Rubberband;
4610 for (i = 0; i < Crosshair.AttachedObject.RubberbandN;
4611 i++, ptr++)
4613 if (PCB->RatOn)
4614 EraseRat ((RatType *) ptr->Line);
4615 MoveObjectToRemoveUndoList (RATLINE_TYPE,
4616 ptr->Line, ptr->Line,
4617 ptr->Line);
4619 IncrementUndoSerialNumber ();
4620 Draw ();
4624 break;
4627 /* change the layout's name */
4628 case F_Layout:
4629 name =
4630 gui->prompt_for (_("Enter the layout name:"), EMPTY (PCB->Name));
4631 /* NB: ChangeLayoutName takes ownership of the passed memory */
4632 if (name && ChangeLayoutName (name))
4633 SetChangedFlag (true);
4634 break;
4636 /* change the name of the active layer */
4637 case F_Layer:
4638 name = gui->prompt_for (_("Enter the layer name:"),
4639 EMPTY (CURRENT->Name));
4640 /* NB: ChangeLayerName takes ownership of the passed memory */
4641 if (name && ChangeLayerName (CURRENT, name))
4642 SetChangedFlag (true);
4643 break;
4646 return 0;
4650 /* --------------------------------------------------------------------------- */
4652 static const char morphpolygon_syntax[] = N_("MorphPolygon(Object|Selected)");
4654 static const char morphpolygon_help[] =
4655 N_("Converts dead polygon islands into separate polygons.");
4657 /* %start-doc actions MorphPolygon
4659 If a polygon is divided into unconnected "islands", you can use
4660 this command to convert the otherwise disappeared islands into
4661 separate polygons. Be sure the cursor is over a portion of the
4662 polygon that remains visible. Very small islands that may flake
4663 off are automatically deleted.
4665 %end-doc */
4667 static int
4668 ActionMorphPolygon (int argc, char **argv, Coord x, Coord y)
4670 char *function = ARG (0);
4671 if (function)
4673 switch (GetFunctionID (function))
4675 case F_Object:
4677 int type;
4678 void *ptr1, *ptr2, *ptr3;
4680 gui->get_coords (_("Select an Object"), &x, &y);
4681 if ((type = SearchScreen (x, y, POLYGON_TYPE,
4682 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4684 MorphPolygon ((LayerType *) ptr1, (PolygonType *) ptr3);
4685 Draw ();
4686 IncrementUndoSerialNumber ();
4688 break;
4690 case F_Selected:
4691 case F_SelectedObjects:
4692 ALLPOLYGON_LOOP (PCB->Data);
4694 if (TEST_FLAG (SELECTEDFLAG, polygon))
4695 MorphPolygon (layer, polygon);
4697 ENDALL_LOOP;
4698 Draw ();
4699 IncrementUndoSerialNumber ();
4700 break;
4703 return 0;
4706 /* --------------------------------------------------------------------------- */
4708 static const char togglehidename_syntax[] =
4709 N_("ToggleHideName(Object|SelectedElements)");
4711 static const char togglehidename_help[] =
4712 N_("Toggles the visibility of element names.");
4714 /* %start-doc actions ToggleHideName
4716 If names are hidden you won't see them on the screen and they will not
4717 appear on the silk layer when you print the layout.
4719 %end-doc */
4721 static int
4722 ActionToggleHideName (int argc, char **argv, Coord x, Coord y)
4724 char *function = ARG (0);
4725 if (function && PCB->ElementOn)
4727 switch (GetFunctionID (function))
4729 case F_Object:
4731 int type;
4732 void *ptr1, *ptr2, *ptr3;
4734 gui->get_coords (_("Select an Object"), &x, &y);
4735 if ((type = SearchScreen (x, y, ELEMENT_TYPE,
4736 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4738 AddObjectToFlagUndoList (type, ptr1, ptr2, ptr3);
4739 EraseElementName ((ElementType *) ptr2);
4740 TOGGLE_FLAG (HIDENAMEFLAG, (ElementType *) ptr2);
4741 DrawElementName ((ElementType *) ptr2);
4742 Draw ();
4743 IncrementUndoSerialNumber ();
4745 break;
4747 case F_SelectedElements:
4748 case F_Selected:
4750 bool changed = false;
4751 ELEMENT_LOOP (PCB->Data);
4753 if ((TEST_FLAG (SELECTEDFLAG, element) ||
4754 TEST_FLAG (SELECTEDFLAG,
4755 &NAMEONPCB_TEXT (element)))
4756 && (FRONT (element) || PCB->InvisibleObjectsOn))
4758 AddObjectToFlagUndoList (ELEMENT_TYPE, element,
4759 element, element);
4760 EraseElementName (element);
4761 TOGGLE_FLAG (HIDENAMEFLAG, element);
4762 DrawElementName (element);
4763 changed = true;
4766 END_LOOP;
4767 if (changed)
4769 Draw ();
4770 IncrementUndoSerialNumber ();
4775 return 0;
4778 /* --------------------------------------------------------------------------- */
4780 static const char changejoin_syntax[] =
4781 N_("ChangeJoin(ToggleObject|SelectedLines|SelectedArcs|Selected)");
4783 static const char changejoin_help[] =
4784 N_("Changes the join (clearance through polygons) of objects.");
4786 /* %start-doc actions ChangeJoin
4788 The join flag determines whether a line or arc, drawn to intersect a
4789 polygon, electrically connects to the polygon or not. When joined,
4790 the line/arc is simply drawn over the polygon, making an electrical
4791 connection. When not joined, a gap is drawn between the line and the
4792 polygon, insulating them from each other.
4794 %end-doc */
4796 static int
4797 ActionChangeJoin (int argc, char **argv, Coord x, Coord y)
4799 char *function = ARG (0);
4800 if (function)
4802 switch (GetFunctionID (function))
4804 case F_ToggleObject:
4805 case F_Object:
4807 int type;
4808 void *ptr1, *ptr2, *ptr3;
4810 gui->get_coords (_("Select an Object"), &x, &y);
4811 if ((type =
4812 SearchScreen (x, y, CHANGEJOIN_TYPES,
4813 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4814 if (ChangeObjectJoin (type, ptr1, ptr2, ptr3))
4815 SetChangedFlag (true);
4816 break;
4819 case F_SelectedLines:
4820 if (ChangeSelectedJoin (LINE_TYPE))
4821 SetChangedFlag (true);
4822 break;
4824 case F_SelectedArcs:
4825 if (ChangeSelectedJoin (ARC_TYPE))
4826 SetChangedFlag (true);
4827 break;
4829 case F_Selected:
4830 case F_SelectedObjects:
4831 if (ChangeSelectedJoin (CHANGEJOIN_TYPES))
4832 SetChangedFlag (true);
4833 break;
4836 return 0;
4839 /* --------------------------------------------------------------------------- */
4841 static const char changesquare_syntax[] =
4842 N_("ChangeSquare(ToggleObject)\n"
4843 "ChangeSquare(SelectedElements|SelectedPins)\n"
4844 "ChangeSquare(Selected|SelectedObjects)");
4846 static const char changesquare_help[] =
4847 N_("Changes the square flag of pins and pads.");
4849 /* %start-doc actions ChangeSquare
4851 Note that @code{Pins} means both pins and pads.
4853 @pinshapes
4855 %end-doc */
4857 static int
4858 ActionChangeSquare (int argc, char **argv, Coord x, Coord y)
4860 char *function = ARG (0);
4861 if (function)
4863 switch (GetFunctionID (function))
4865 case F_ToggleObject:
4866 case F_Object:
4868 int type;
4869 void *ptr1, *ptr2, *ptr3;
4871 gui->get_coords (_("Select an Object"), &x, &y);
4872 if ((type =
4873 SearchScreen (x, y, CHANGESQUARE_TYPES,
4874 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4875 if (ChangeObjectSquare (type, ptr1, ptr2, ptr3))
4876 SetChangedFlag (true);
4877 break;
4880 case F_SelectedElements:
4881 if (ChangeSelectedSquare (ELEMENT_TYPE))
4882 SetChangedFlag (true);
4883 break;
4885 case F_SelectedPins:
4886 if (ChangeSelectedSquare (PIN_TYPE | PAD_TYPE))
4887 SetChangedFlag (true);
4888 break;
4890 case F_Selected:
4891 case F_SelectedObjects:
4892 if (ChangeSelectedSquare (PIN_TYPE | PAD_TYPE))
4893 SetChangedFlag (true);
4894 break;
4897 return 0;
4900 /* --------------------------------------------------------------------------- */
4902 static const char setsquare_syntax[] =
4903 N_("SetSquare(ToggleObject|SelectedElements|SelectedPins)");
4905 static const char setsquare_help[] = N_("sets the square-flag of objects.");
4907 /* %start-doc actions SetSquare
4909 Note that @code{Pins} means pins and pads.
4911 @pinshapes
4913 %end-doc */
4915 static int
4916 ActionSetSquare (int argc, char **argv, Coord x, Coord y)
4918 char *function = ARG (0);
4919 if (function && *function)
4921 switch (GetFunctionID (function))
4923 case F_ToggleObject:
4924 case F_Object:
4926 int type;
4927 void *ptr1, *ptr2, *ptr3;
4929 gui->get_coords (_("Select an Object"), &x, &y);
4930 if ((type =
4931 SearchScreen (x, y, CHANGESQUARE_TYPES,
4932 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4933 if (SetObjectSquare (type, ptr1, ptr2, ptr3))
4934 SetChangedFlag (true);
4935 break;
4938 case F_SelectedElements:
4939 if (SetSelectedSquare (ELEMENT_TYPE))
4940 SetChangedFlag (true);
4941 break;
4943 case F_SelectedPins:
4944 if (SetSelectedSquare (PIN_TYPE | PAD_TYPE))
4945 SetChangedFlag (true);
4946 break;
4948 case F_Selected:
4949 case F_SelectedObjects:
4950 if (SetSelectedSquare (PIN_TYPE | PAD_TYPE))
4951 SetChangedFlag (true);
4952 break;
4955 return 0;
4958 /* --------------------------------------------------------------------------- */
4960 static const char clearsquare_syntax[] =
4961 N_("ClearSquare(ToggleObject|SelectedElements|SelectedPins)");
4963 static const char clearsquare_help[] =
4964 N_("Clears the square-flag of pins and pads.");
4966 /* %start-doc actions ClearSquare
4968 Note that @code{Pins} means pins and pads.
4970 @pinshapes
4972 %end-doc */
4974 static int
4975 ActionClearSquare (int argc, char **argv, Coord x, Coord y)
4977 char *function = ARG (0);
4978 if (function && *function)
4980 switch (GetFunctionID (function))
4982 case F_ToggleObject:
4983 case F_Object:
4985 int type;
4986 void *ptr1, *ptr2, *ptr3;
4988 gui->get_coords (_("Select an Object"), &x, &y);
4989 if ((type =
4990 SearchScreen (x, y, CHANGESQUARE_TYPES,
4991 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4992 if (ClrObjectSquare (type, ptr1, ptr2, ptr3))
4993 SetChangedFlag (true);
4994 break;
4997 case F_SelectedElements:
4998 if (ClrSelectedSquare (ELEMENT_TYPE))
4999 SetChangedFlag (true);
5000 break;
5002 case F_SelectedPins:
5003 if (ClrSelectedSquare (PIN_TYPE | PAD_TYPE))
5004 SetChangedFlag (true);
5005 break;
5007 case F_Selected:
5008 case F_SelectedObjects:
5009 if (ClrSelectedSquare (PIN_TYPE | PAD_TYPE))
5010 SetChangedFlag (true);
5011 break;
5014 return 0;
5017 /* --------------------------------------------------------------------------- */
5019 static const char changeoctagon_syntax[] =
5020 N_("ChangeOctagon(Object|ToggleObject|SelectedObjects|Selected)\n"
5021 "ChangeOctagon(SelectedElements|SelectedPins|SelectedVias)");
5023 static const char changeoctagon_help[] =
5024 N_("Changes the octagon-flag of pins and vias.");
5026 /* %start-doc actions ChangeOctagon
5028 @pinshapes
5030 %end-doc */
5032 static int
5033 ActionChangeOctagon (int argc, char **argv, Coord x, Coord y)
5035 char *function = ARG (0);
5036 if (function)
5038 switch (GetFunctionID (function))
5040 case F_ToggleObject:
5041 case F_Object:
5043 int type;
5044 void *ptr1, *ptr2, *ptr3;
5046 gui->get_coords (_("Select an Object"), &x, &y);
5047 if ((type =
5048 SearchScreen (x, y, CHANGEOCTAGON_TYPES,
5049 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5050 if (ChangeObjectOctagon (type, ptr1, ptr2, ptr3))
5051 SetChangedFlag (true);
5052 break;
5055 case F_SelectedElements:
5056 if (ChangeSelectedOctagon (ELEMENT_TYPE))
5057 SetChangedFlag (true);
5058 break;
5060 case F_SelectedPins:
5061 if (ChangeSelectedOctagon (PIN_TYPE))
5062 SetChangedFlag (true);
5063 break;
5065 case F_SelectedVias:
5066 if (ChangeSelectedOctagon (VIA_TYPE))
5067 SetChangedFlag (true);
5068 break;
5070 case F_Selected:
5071 case F_SelectedObjects:
5072 if (ChangeSelectedOctagon (PIN_TYPES))
5073 SetChangedFlag (true);
5074 break;
5077 return 0;
5080 /* --------------------------------------------------------------------------- */
5082 static const char setoctagon_syntax[] =
5083 N_("SetOctagon(Object|ToggleObject|SelectedElements|Selected)");
5085 static const char setoctagon_help[] = N_("Sets the octagon-flag of objects.");
5087 /* %start-doc actions SetOctagon
5089 @pinshapes
5091 %end-doc */
5093 static int
5094 ActionSetOctagon (int argc, char **argv, Coord x, Coord y)
5096 char *function = ARG (0);
5097 if (function)
5099 switch (GetFunctionID (function))
5101 case F_ToggleObject:
5102 case F_Object:
5104 int type;
5105 void *ptr1, *ptr2, *ptr3;
5107 gui->get_coords (_("Select an Object"), &x, &y);
5108 if ((type =
5109 SearchScreen (x, y, CHANGEOCTAGON_TYPES,
5110 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5111 if (SetObjectOctagon (type, ptr1, ptr2, ptr3))
5112 SetChangedFlag (true);
5113 break;
5116 case F_SelectedElements:
5117 if (SetSelectedOctagon (ELEMENT_TYPE))
5118 SetChangedFlag (true);
5119 break;
5121 case F_SelectedPins:
5122 if (SetSelectedOctagon (PIN_TYPE))
5123 SetChangedFlag (true);
5124 break;
5126 case F_SelectedVias:
5127 if (SetSelectedOctagon (VIA_TYPE))
5128 SetChangedFlag (true);
5129 break;
5131 case F_Selected:
5132 case F_SelectedObjects:
5133 if (SetSelectedOctagon (PIN_TYPES))
5134 SetChangedFlag (true);
5135 break;
5138 return 0;
5141 /* --------------------------------------------------------------------------- */
5143 static const char clearoctagon_syntax[] =
5144 N_("ClearOctagon(ToggleObject|Object|SelectedObjects|Selected)\n"
5145 "ClearOctagon(SelectedElements|SelectedPins|SelectedVias)");
5147 static const char clearoctagon_help[] =
5148 N_("Clears the octagon-flag of pins and vias.");
5150 /* %start-doc actions ClearOctagon
5152 @pinshapes
5154 %end-doc */
5156 static int
5157 ActionClearOctagon (int argc, char **argv, Coord x, Coord y)
5159 char *function = ARG (0);
5160 if (function)
5162 switch (GetFunctionID (function))
5164 case F_ToggleObject:
5165 case F_Object:
5167 int type;
5168 void *ptr1, *ptr2, *ptr3;
5170 gui->get_coords (_("Select an Object"), &x, &y);
5171 if ((type =
5172 SearchScreen (Crosshair.X, Crosshair.Y, CHANGEOCTAGON_TYPES,
5173 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5174 if (ClrObjectOctagon (type, ptr1, ptr2, ptr3))
5175 SetChangedFlag (true);
5176 break;
5179 case F_SelectedElements:
5180 if (ClrSelectedOctagon (ELEMENT_TYPE))
5181 SetChangedFlag (true);
5182 break;
5184 case F_SelectedPins:
5185 if (ClrSelectedOctagon (PIN_TYPE))
5186 SetChangedFlag (true);
5187 break;
5189 case F_SelectedVias:
5190 if (ClrSelectedOctagon (VIA_TYPE))
5191 SetChangedFlag (true);
5192 break;
5194 case F_Selected:
5195 case F_SelectedObjects:
5196 if (ClrSelectedOctagon (PIN_TYPES))
5197 SetChangedFlag (true);
5198 break;
5201 return 0;
5204 /* --------------------------------------------------------------------------- */
5206 static const char changehold_syntax[] =
5207 N_("ChangeHole(ToggleObject|Object|SelectedVias|Selected)");
5209 static const char changehold_help[] = N_("Changes the hole flag of objects.");
5211 /* %start-doc actions ChangeHole
5213 The "hole flag" of a via determines whether the via is a
5214 plated-through hole (not set), or an unplated hole (set).
5216 %end-doc */
5218 static int
5219 ActionChangeHole (int argc, char **argv, Coord x, Coord y)
5221 char *function = ARG (0);
5222 if (function)
5224 switch (GetFunctionID (function))
5226 case F_ToggleObject:
5227 case F_Object:
5229 int type;
5230 void *ptr1, *ptr2, *ptr3;
5232 gui->get_coords (_("Select an Object"), &x, &y);
5233 if ((type = SearchScreen (x, y, VIA_TYPE,
5234 &ptr1, &ptr2, &ptr3)) != NO_TYPE
5235 && ChangeHole ((PinType *) ptr3))
5236 IncrementUndoSerialNumber ();
5237 break;
5240 case F_SelectedVias:
5241 case F_Selected:
5242 if (ChangeSelectedHole ())
5243 SetChangedFlag (true);
5244 break;
5247 return 0;
5250 /* --------------------------------------------------------------------------- */
5252 static const char changepaste_syntax[] =
5253 N_("ChangePaste(ToggleObject|Object|SelectedPads|Selected)");
5255 static const char changepaste_help[] =
5256 N_("Changes the no paste flag of objects.");
5258 /* %start-doc actions ChangePaste
5260 The "no paste flag" of a pad determines whether the solderpaste
5261 stencil will have an opening for the pad (no set) or if there wil be
5262 no solderpaste on the pad (set). This is used for things such as
5263 fiducial pads.
5265 %end-doc */
5267 static int
5268 ActionChangePaste (int argc, char **argv, Coord x, Coord y)
5270 char *function = ARG (0);
5271 if (function)
5273 switch (GetFunctionID (function))
5275 case F_ToggleObject:
5276 case F_Object:
5278 int type;
5279 void *ptr1, *ptr2, *ptr3;
5281 gui->get_coords (_("Select an Object"), &x, &y);
5282 if ((type = SearchScreen (x, y, PAD_TYPE,
5283 &ptr1, &ptr2, &ptr3)) != NO_TYPE
5284 && ChangePaste ((PadType *) ptr3))
5285 IncrementUndoSerialNumber ();
5286 break;
5289 case F_SelectedPads:
5290 case F_Selected:
5291 if (ChangeSelectedPaste ())
5292 SetChangedFlag (true);
5293 break;
5296 return 0;
5299 /* --------------------------------------------------------------------------- */
5301 static const char select_syntax[] =
5302 N_("Select(Object|ToggleObject)\n"
5303 "Select(All|Block|Connection)\n"
5304 "Select(ElementByName|ObjectByName|PadByName|PinByName)\n"
5305 "Select(ElementByName|ObjectByName|PadByName|PinByName, Name)\n"
5306 "Select(TextByName|ViaByName|NetByName)\n"
5307 "Select(TextByName|ViaByName|NetByName, Name)\n"
5308 "Select(Convert)");
5310 static const char select_help[] = N_("Toggles or sets the selection.");
5312 /* %start-doc actions Select
5314 @table @code
5316 @item ElementByName
5317 @item ObjectByName
5318 @item PadByName
5319 @item PinByName
5320 @item TextByName
5321 @item ViaByName
5322 @item NetByName
5324 These all rely on having a regular expression parser built into
5325 @code{pcb}. If the name is not specified then the user is prompted
5326 for a pattern, and all objects that match the pattern and are of the
5327 type specified are selected.
5329 @item Object
5330 @item ToggleObject
5331 Selects the object under the cursor.
5333 @item Block
5334 Selects all objects in a rectangle indicated by the cursor.
5336 @item All
5337 Selects all objects on the board.
5339 @item Found
5340 Selects all connections with the ``found'' flag set.
5342 @item Connection
5343 Selects all connections with the ``connected'' flag set.
5345 @item Convert
5346 Converts the selected objects to an element. This uses the highest
5347 numbered paste buffer.
5349 @end table
5351 %end-doc */
5353 static int
5354 ActionSelect (int argc, char **argv, Coord x, Coord y)
5356 char *function = ARG (0);
5357 if (function)
5359 switch (GetFunctionID (function))
5361 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5362 int type;
5363 /* select objects by their names */
5364 case F_ElementByName:
5365 type = ELEMENT_TYPE;
5366 goto commonByName;
5367 case F_ObjectByName:
5368 type = ALL_TYPES;
5369 goto commonByName;
5370 case F_PadByName:
5371 type = PAD_TYPE;
5372 goto commonByName;
5373 case F_PinByName:
5374 type = PIN_TYPE;
5375 goto commonByName;
5376 case F_TextByName:
5377 type = TEXT_TYPE;
5378 goto commonByName;
5379 case F_ViaByName:
5380 type = VIA_TYPE;
5381 goto commonByName;
5382 case F_NetByName:
5383 type = NET_TYPE;
5384 goto commonByName;
5386 commonByName:
5388 char *pattern = ARG (1);
5390 if (pattern
5391 || (pattern =
5392 gui->prompt_for (_("Enter pattern:"), "")) != NULL)
5394 if (SelectObjectByName (type, pattern, true))
5395 SetChangedFlag (true);
5396 if (ARG (1) == NULL)
5397 free (pattern);
5399 break;
5401 #endif /* defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP) */
5403 /* select a single object */
5404 case F_ToggleObject:
5405 case F_Object:
5406 if (SelectObject ())
5407 SetChangedFlag (true);
5408 break;
5410 /* all objects in block */
5411 case F_Block:
5413 BoxType box;
5415 box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
5416 Crosshair.AttachedBox.Point2.X);
5417 box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
5418 Crosshair.AttachedBox.Point2.Y);
5419 box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
5420 Crosshair.AttachedBox.Point2.X);
5421 box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
5422 Crosshair.AttachedBox.Point2.Y);
5423 notify_crosshair_change (false);
5424 NotifyBlock ();
5425 if (Crosshair.AttachedBox.State == STATE_THIRD &&
5426 SelectBlock (&box, true))
5428 SetChangedFlag (true);
5429 Crosshair.AttachedBox.State = STATE_FIRST;
5431 notify_crosshair_change (true);
5432 break;
5435 /* select all visible objects */
5436 case F_All:
5438 BoxType box;
5440 box.X1 = -MAX_COORD;
5441 box.Y1 = -MAX_COORD;
5442 box.X2 = MAX_COORD;
5443 box.Y2 = MAX_COORD;
5444 if (SelectBlock (&box, true))
5445 SetChangedFlag (true);
5446 break;
5449 /* all logical connections */
5450 case F_Found:
5451 if (SelectByFlag (FOUNDFLAG, true))
5453 Draw ();
5454 IncrementUndoSerialNumber ();
5455 SetChangedFlag (true);
5457 break;
5459 /* all physical connections */
5460 case F_Connection:
5461 if (SelectByFlag (CONNECTEDFLAG, true))
5463 Draw ();
5464 IncrementUndoSerialNumber ();
5465 SetChangedFlag (true);
5467 break;
5469 case F_Convert:
5471 Coord x, y;
5472 Note.Buffer = Settings.BufferNumber;
5473 SetBufferNumber (MAX_BUFFER - 1);
5474 ClearBuffer (PASTEBUFFER);
5475 gui->get_coords (_("Select the Element's Mark Location"), &x, &y);
5476 x = GridFit (x, PCB->Grid, PCB->GridOffsetX);
5477 y = GridFit (y, PCB->Grid, PCB->GridOffsetY);
5478 AddSelectedToBuffer (PASTEBUFFER, x, y, true);
5479 SaveUndoSerialNumber ();
5480 RemoveSelected ();
5481 ConvertBufferToElement (PASTEBUFFER);
5482 RestoreUndoSerialNumber ();
5483 CopyPastebufferToLayout (x, y);
5484 SetBufferNumber (Note.Buffer);
5486 break;
5488 default:
5489 AFAIL (select);
5490 break;
5493 return 0;
5496 /* FLAG(have_regex,FlagHaveRegex,0) */
5498 FlagHaveRegex (int parm)
5500 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5501 return 1;
5502 #else
5503 return 0;
5504 #endif
5507 /* --------------------------------------------------------------------------- */
5509 static const char unselect_syntax[] =
5510 N_("Unselect(All|Block|Connection)\n"
5511 "Unselect(ElementByName|ObjectByName|PadByName|PinByName)\n"
5512 "Unselect(ElementByName|ObjectByName|PadByName|PinByName, Name)\n"
5513 "Unselect(TextByName|ViaByName)\n"
5514 "Unselect(TextByName|ViaByName, Name)\n");
5516 static const char unselect_help[] =
5517 N_("Unselects the object at the pointer location or the specified objects.");
5519 /* %start-doc actions Unselect
5521 @table @code
5523 @item All
5524 Unselect all objects.
5526 @item Block
5527 Unselect all objects in a rectangle given by the cursor.
5529 @item Connection
5530 Unselect all connections with the ``found'' flag set.
5532 @item ElementByName
5533 @item ObjectByName
5534 @item PadByName
5535 @item PinByName
5536 @item TextByName
5537 @item ViaByName
5539 These all rely on having a regular expression parser built into
5540 @code{pcb}. If the name is not specified then the user is prompted
5541 for a pattern, and all objects that match the pattern and are of the
5542 type specified are unselected.
5545 @end table
5547 %end-doc */
5549 static int
5550 ActionUnselect (int argc, char **argv, Coord x, Coord y)
5552 char *function = ARG (0);
5553 if (function)
5555 switch (GetFunctionID (function))
5557 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5558 int type;
5559 /* select objects by their names */
5560 case F_ElementByName:
5561 type = ELEMENT_TYPE;
5562 goto commonByName;
5563 case F_ObjectByName:
5564 type = ALL_TYPES;
5565 goto commonByName;
5566 case F_PadByName:
5567 type = PAD_TYPE;
5568 goto commonByName;
5569 case F_PinByName:
5570 type = PIN_TYPE;
5571 goto commonByName;
5572 case F_TextByName:
5573 type = TEXT_TYPE;
5574 goto commonByName;
5575 case F_ViaByName:
5576 type = VIA_TYPE;
5577 goto commonByName;
5578 case F_NetByName:
5579 type = NET_TYPE;
5580 goto commonByName;
5582 commonByName:
5584 char *pattern = ARG (1);
5586 if (pattern
5587 || (pattern =
5588 gui->prompt_for (_("Enter pattern:"), "")) != NULL)
5590 if (SelectObjectByName (type, pattern, false))
5591 SetChangedFlag (true);
5592 if (ARG (1) == NULL)
5593 free (pattern);
5595 break;
5597 #endif /* defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP) */
5599 /* all objects in block */
5600 case F_Block:
5602 BoxType box;
5604 box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
5605 Crosshair.AttachedBox.Point2.X);
5606 box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
5607 Crosshair.AttachedBox.Point2.Y);
5608 box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
5609 Crosshair.AttachedBox.Point2.X);
5610 box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
5611 Crosshair.AttachedBox.Point2.Y);
5612 notify_crosshair_change (false);
5613 NotifyBlock ();
5614 if (Crosshair.AttachedBox.State == STATE_THIRD &&
5615 SelectBlock (&box, false))
5617 SetChangedFlag (true);
5618 Crosshair.AttachedBox.State = STATE_FIRST;
5620 notify_crosshair_change (true);
5621 break;
5624 /* unselect all visible objects */
5625 case F_All:
5627 BoxType box;
5629 box.X1 = -MAX_COORD;
5630 box.Y1 = -MAX_COORD;
5631 box.X2 = MAX_COORD;
5632 box.Y2 = MAX_COORD;
5633 if (SelectBlock (&box, false))
5634 SetChangedFlag (true);
5635 break;
5638 /* all logical connections */
5639 case F_Found:
5640 if (SelectByFlag (FOUNDFLAG, false))
5642 Draw ();
5643 IncrementUndoSerialNumber ();
5644 SetChangedFlag (true);
5646 break;
5648 /* all physical connections */
5649 case F_Connection:
5650 if (SelectByFlag (CONNECTEDFLAG, false))
5652 Draw ();
5653 IncrementUndoSerialNumber ();
5654 SetChangedFlag (true);
5656 break;
5658 default:
5659 AFAIL (unselect);
5660 break;
5664 return 0;
5667 /* --------------------------------------------------------------------------- */
5669 static const char saveto_syntax[] =
5670 N_("SaveTo(Layout|LayoutAs,filename)\n"
5671 "SaveTo(AllConnections|AllUnusedPins|ElementConnections,filename)\n"
5672 "SaveTo(PasteBuffer,filename)");
5674 static const char saveto_help[] = N_("Saves data to a file.");
5676 /* %start-doc actions SaveTo
5678 @table @code
5680 @item Layout
5681 Saves the current layout.
5683 @item LayoutAs
5684 Saves the current layout, and remembers the filename used.
5686 @item AllConnections
5687 Save all connections to a file.
5689 @item AllUnusedPins
5690 List all unused pins to a file.
5692 @item ElementConnections
5693 Save connections to the element at the cursor to a file.
5695 @item PasteBuffer
5696 Save the content of the active Buffer to a file. This is the graphical way to create a footprint.
5698 @end table
5700 %end-doc */
5702 static int
5703 ActionSaveTo (int argc, char **argv, Coord x, Coord y)
5705 char *function;
5706 char *name;
5708 function = argv[0];
5709 name = argv[1];
5711 if (strcasecmp (function, "Layout") == 0)
5713 if (SavePCB (PCB->Filename) == 0)
5714 SetChangedFlag (false);
5715 return 0;
5718 if (argc != 2)
5719 AFAIL (saveto);
5721 if (strcasecmp (function, "LayoutAs") == 0)
5723 if (SavePCB (name) == 0)
5725 SetChangedFlag (false);
5726 free (PCB->Filename);
5727 PCB->Filename = strdup (name);
5728 if (gui->notify_filename_changed != NULL)
5729 gui->notify_filename_changed ();
5731 return 0;
5734 if (strcasecmp (function, "AllConnections") == 0)
5736 FILE *fp;
5737 bool result;
5738 if ((fp = CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
5740 LookupConnectionsToAllElements (fp);
5741 fclose (fp);
5742 SetChangedFlag (true);
5744 return 0;
5747 if (strcasecmp (function, "AllUnusedPins") == 0)
5749 FILE *fp;
5750 bool result;
5751 if ((fp = CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
5753 LookupUnusedPins (fp);
5754 fclose (fp);
5755 SetChangedFlag (true);
5757 return 0;
5760 if (strcasecmp (function, "ElementConnections") == 0)
5762 ElementType *element;
5763 void *ptrtmp;
5764 FILE *fp;
5765 bool result;
5767 if ((SearchScreen (Crosshair.X, Crosshair.Y, ELEMENT_TYPE,
5768 &ptrtmp, &ptrtmp, &ptrtmp)) != NO_TYPE)
5770 element = (ElementType *) ptrtmp;
5771 if ((fp =
5772 CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
5774 LookupElementConnections (element, fp);
5775 fclose (fp);
5776 SetChangedFlag (true);
5779 return 0;
5782 if (strcasecmp (function, "PasteBuffer") == 0)
5784 return SaveBufferElements (name);
5787 AFAIL (saveto);
5790 /* --------------------------------------------------------------------------- */
5792 static const char savesettings_syntax[] =
5793 N_("SaveSettings()\n"
5794 "SaveSettings(local)");
5796 static const char savesettings_help[] = N_("Saves settings.");
5798 /* %start-doc actions SaveSettings
5800 If you pass no arguments, the settings are stored in
5801 @code{$HOME/.pcb/settings}. If you pass the word @code{local} they're
5802 saved in @code{./pcb.settings}.
5804 %end-doc */
5806 static int
5807 ActionSaveSettings (int argc, char **argv, Coord x, Coord y)
5809 int locally = argc > 0 ? (strncasecmp (argv[0], "local", 5) == 0) : 0;
5810 hid_save_settings (locally);
5811 return 0;
5814 /* --------------------------------------------------------------------------- */
5816 static const char loadfrom_syntax[] =
5817 N_("LoadFrom(Layout|LayoutToBuffer|ElementToBuffer|Netlist|Revert,filename)");
5819 static const char loadfrom_help[] = N_("Load layout data from a file.");
5821 /* %start-doc actions LoadFrom
5823 This action assumes you know what the filename is. The various GUIs
5824 should have a similar @code{Load} action where the filename is
5825 optional, and will provide their own file selection mechanism to let
5826 you choose the file name.
5828 @table @code
5830 @item Layout
5831 Loads an entire PCB layout, replacing the current one.
5833 @item LayoutToBuffer
5834 Loads an entire PCB layout to the paste buffer.
5836 @item ElementToBuffer
5837 Loads the given element file into the paste buffer. Element files
5838 contain only a single @code{Element} definition, such as the
5839 ``newlib'' library uses.
5841 @item Netlist
5842 Loads a new netlist, replacing any current netlist.
5844 @item Revert
5845 Re-loads the current layout from its disk file, reverting any changes
5846 you may have made.
5848 @end table
5850 %end-doc */
5852 static int
5853 ActionLoadFrom (int argc, char **argv, Coord x, Coord y)
5855 char *function;
5856 char *name;
5858 if (argc < 2)
5859 AFAIL (loadfrom);
5861 function = argv[0];
5862 name = argv[1];
5864 if (strcasecmp (function, "ElementToBuffer") == 0)
5866 notify_crosshair_change (false);
5867 if (LoadElementToBuffer (PASTEBUFFER, name, true))
5868 SetMode (PASTEBUFFER_MODE);
5869 notify_crosshair_change (true);
5872 else if (strcasecmp (function, "LayoutToBuffer") == 0)
5874 notify_crosshair_change (false);
5875 if (LoadLayoutToBuffer (PASTEBUFFER, name))
5876 SetMode (PASTEBUFFER_MODE);
5877 notify_crosshair_change (true);
5880 else if (strcasecmp (function, "Layout") == 0)
5882 if (!PCB->Changed ||
5883 gui->confirm_dialog (_("OK to override layout data?"), 0))
5884 LoadPCB (name);
5887 else if (strcasecmp (function, "Netlist") == 0)
5889 if (PCB->Netlistname)
5890 free (PCB->Netlistname);
5891 PCB->Netlistname = StripWhiteSpaceAndDup (name);
5892 FreeLibraryMemory (&PCB->NetlistLib);
5893 ImportNetlist (PCB->Netlistname);
5894 NetlistChanged (1);
5896 else if (strcasecmp (function, "Revert") == 0 && PCB->Filename
5897 && (!PCB->Changed
5898 || gui->confirm_dialog (_("OK to override changes?"), 0)))
5900 RevertPCB ();
5903 return 0;
5906 /* --------------------------------------------------------------------------- */
5908 static const char new_syntax[] = N_("New([name])");
5910 static const char new_help[] = N_("Starts a new layout.");
5912 /* %start-doc actions New
5914 If a name is not given, one is prompted for.
5916 %end-doc */
5918 static int
5919 ActionNew (int argc, char **argv, Coord x, Coord y)
5921 char *name = ARG (0);
5923 if (!PCB->Changed || gui->confirm_dialog (_("OK to clear layout data?"), 0))
5925 if (name)
5926 name = strdup (name);
5927 else
5928 name = gui->prompt_for (_("Enter the layout name:"), "");
5930 if (!name)
5931 return 1;
5933 notify_crosshair_change (false);
5934 /* do emergency saving
5935 * clear the old struct and allocate memory for the new one
5937 if (PCB->Changed && Settings.SaveInTMP)
5938 SaveInTMP ();
5939 RemovePCB (PCB);
5940 PCB = NULL;
5941 PCB = CreateNewPCB (true);
5942 CreateNewPCBPost (PCB, 1);
5944 /* setup the new name and reset some values to default */
5945 free (PCB->Name);
5946 PCB->Name = name;
5948 ResetStackAndVisibility ();
5949 SetCrosshairRange (0, 0, PCB->MaxWidth, PCB->MaxHeight);
5950 CenterDisplay (PCB->MaxWidth / 2, PCB->MaxHeight / 2);
5951 Redraw ();
5953 hid_action ("PCBChanged");
5954 notify_crosshair_change (true);
5955 return 0;
5957 return 1;
5960 /* ---------------------------------------------------------------------------
5961 * no operation, just for testing purposes
5962 * syntax: Bell(volume)
5964 void
5965 ActionBell (char *volume)
5967 gui->beep ();
5970 /* --------------------------------------------------------------------------- */
5972 static const char pastebuffer_syntax[] =
5973 N_("PasteBuffer(AddSelected|Clear|1..MAX_BUFFER)\n"
5974 "PasteBuffer(Rotate, 1..3)\n"
5975 "PasteBuffer(Convert|Save|Restore|Mirror)\n"
5976 "PasteBuffer(ToLayout, X, Y, units)");
5978 static const char pastebuffer_help[] =
5979 N_("Various operations on the paste buffer.");
5981 /* %start-doc actions PasteBuffer
5983 There are a number of paste buffers; the actual limit is a
5984 compile-time constant @code{MAX_BUFFER} in @file{globalconst.h}. It
5985 is currently @code{5}. One of these is the ``current'' paste buffer,
5986 often referred to as ``the'' paste buffer.
5988 @table @code
5990 @item AddSelected
5991 Copies the selected objects to the current paste buffer.
5993 @item Clear
5994 Remove all objects from the current paste buffer.
5996 @item Convert
5997 Convert the current paste buffer to an element. Vias are converted to
5998 pins, lines are converted to pads.
6000 @item Restore
6001 Convert any elements in the paste buffer back to vias and lines.
6003 @item Mirror
6004 Flip all objects in the paste buffer vertically (up/down flip). To mirror
6005 horizontally, combine this with rotations.
6007 @item Rotate
6008 Rotates the current buffer. The number to pass is 1..3, where 1 means
6009 90 degrees counter clockwise, 2 means 180 degrees, and 3 means 90
6010 degrees clockwise (270 CCW).
6012 @item Save
6013 Saves any elements in the current buffer to the indicated file.
6015 @item ToLayout
6016 Pastes any elements in the current buffer to the indicated X, Y
6017 coordinates in the layout. The @code{X} and @code{Y} are treated like
6018 @code{delta} is for many other objects. For each, if it's prefixed by
6019 @code{+} or @code{-}, then that amount is relative to the last
6020 location. Otherwise, it's absolute. Units can be
6021 @code{mil} or @code{mm}; if unspecified, units are PCB's internal
6022 units, currently 1/100 mil.
6025 @item 1..MAX_BUFFER
6026 Selects the given buffer to be the current paste buffer.
6028 @end table
6030 %end-doc */
6032 static int
6033 ActionPasteBuffer (int argc, char **argv, Coord x, Coord y)
6035 char *function = argc ? argv[0] : (char *)"";
6036 char *sbufnum = argc > 1 ? argv[1] : (char *)"";
6037 char *name;
6038 static char *default_file = NULL;
6039 int free_name = 0;
6041 notify_crosshair_change (false);
6042 if (function)
6044 switch (GetFunctionID (function))
6046 /* clear contents of paste buffer */
6047 case F_Clear:
6048 ClearBuffer (PASTEBUFFER);
6049 break;
6051 /* copies objects to paste buffer */
6052 case F_AddSelected:
6053 AddSelectedToBuffer (PASTEBUFFER, 0, 0, false);
6054 break;
6056 /* converts buffer contents into an element */
6057 case F_Convert:
6058 ConvertBufferToElement (PASTEBUFFER);
6059 break;
6061 /* break up element for editing */
6062 case F_Restore:
6063 SmashBufferElement (PASTEBUFFER);
6064 break;
6066 /* Mirror buffer */
6067 case F_Mirror:
6068 MirrorBuffer (PASTEBUFFER);
6069 break;
6071 case F_Rotate:
6072 if (sbufnum)
6074 RotateBuffer (PASTEBUFFER, (BYTE) atoi (sbufnum));
6075 SetCrosshairRangeToBuffer ();
6077 break;
6079 case F_Save:
6080 if (PASTEBUFFER->Data->ElementN == 0)
6082 Message (_("Buffer has no elements!\n"));
6083 break;
6085 free_name = 0;
6086 if (argc <= 1)
6088 name = gui->fileselect (_("Save Paste Buffer As ..."),
6089 _("Choose a file to save the contents of the\n"
6090 "paste buffer to.\n"),
6091 default_file, ".fp", "footprint",
6094 if (default_file)
6096 free (default_file);
6097 default_file = NULL;
6099 if ( name && *name)
6101 default_file = strdup (name);
6103 free_name = 1;
6106 else
6107 name = argv[1];
6110 FILE *exist;
6112 if ((exist = fopen (name, "r")))
6114 fclose (exist);
6115 if (gui->
6116 confirm_dialog (_("File exists! Ok to overwrite?"), 0))
6117 SaveBufferElements (name);
6119 else
6120 SaveBufferElements (name);
6122 if (free_name && name)
6123 free (name);
6125 break;
6127 case F_ToLayout:
6129 static Coord oldx = 0, oldy = 0;
6130 Coord x, y;
6131 bool absolute;
6133 if (argc == 1)
6135 x = y = 0;
6137 else if (argc == 3 || argc == 4)
6139 x = GetValue (ARG (1), ARG (3), &absolute);
6140 if (!absolute)
6141 x += oldx;
6142 y = GetValue (ARG (2), ARG (3), &absolute);
6143 if (!absolute)
6144 y += oldy;
6146 else
6148 notify_crosshair_change (true);
6149 AFAIL (pastebuffer);
6152 oldx = x;
6153 oldy = y;
6154 if (CopyPastebufferToLayout (x, y))
6155 SetChangedFlag (true);
6157 break;
6159 /* set number */
6160 default:
6162 int number = atoi (function);
6164 /* correct number */
6165 if (number)
6166 SetBufferNumber (number - 1);
6171 notify_crosshair_change (true);
6172 return 0;
6175 /* --------------------------------------------------------------------------- */
6177 static const char undo_syntax[] = N_("Undo()\n"
6178 "Undo(ClearList)");
6180 static const char undo_help[] = N_("Undo recent changes.");
6182 /* %start-doc actions Undo
6184 The unlimited undo feature of @code{Pcb} allows you to recover from
6185 most operations that materially affect you work. Calling
6186 @code{Undo()} without any parameter recovers from the last (non-undo)
6187 operation. @code{ClearList} is used to release the allocated
6188 memory. @code{ClearList} is called whenever a new layout is started or
6189 loaded. See also @code{Redo} and @code{Atomic}.
6191 Note that undo groups operations by serial number; changes with the
6192 same serial number will be undone (or redone) as a group. See
6193 @code{Atomic}.
6195 %end-doc */
6197 static int
6198 ActionUndo (int argc, char **argv, Coord x, Coord y)
6200 char *function = ARG (0);
6201 if (!function || !*function)
6203 /* don't allow undo in the middle of an operation */
6204 if (Settings.Mode != POLYGONHOLE_MODE &&
6205 Crosshair.AttachedObject.State != STATE_FIRST)
6206 return 1;
6207 if (Crosshair.AttachedBox.State != STATE_FIRST
6208 && Settings.Mode != ARC_MODE)
6209 return 1;
6210 /* undo the last operation */
6212 notify_crosshair_change (false);
6213 if ((Settings.Mode == POLYGON_MODE ||
6214 Settings.Mode == POLYGONHOLE_MODE) &&
6215 Crosshair.AttachedPolygon.PointN)
6217 GoToPreviousPoint ();
6218 notify_crosshair_change (true);
6219 return 0;
6221 /* move anchor point if undoing during line creation */
6222 if (Settings.Mode == LINE_MODE)
6224 if (Crosshair.AttachedLine.State == STATE_SECOND)
6226 if (TEST_FLAG (AUTODRCFLAG, PCB))
6227 Undo (true); /* undo the connection find */
6228 Crosshair.AttachedLine.State = STATE_FIRST;
6229 SetLocalRef (0, 0, false);
6230 notify_crosshair_change (true);
6231 return 0;
6233 if (Crosshair.AttachedLine.State == STATE_THIRD)
6235 int type;
6236 void *ptr1, *ptr3, *ptrtmp;
6237 LineType *ptr2;
6238 /* this search is guaranteed to succeed */
6239 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6240 &ptrtmp, &ptr3,
6241 Crosshair.AttachedLine.Point1.X,
6242 Crosshair.AttachedLine.Point1.Y, 0);
6243 ptr2 = (LineType *) ptrtmp;
6245 /* save both ends of line */
6246 Crosshair.AttachedLine.Point2.X = ptr2->Point1.X;
6247 Crosshair.AttachedLine.Point2.Y = ptr2->Point1.Y;
6248 if ((type = Undo (true)))
6249 SetChangedFlag (true);
6250 /* check that the undo was of the right type */
6251 if ((type & UNDO_CREATE) == 0)
6253 /* wrong undo type, restore anchor points */
6254 Crosshair.AttachedLine.Point2.X =
6255 Crosshair.AttachedLine.Point1.X;
6256 Crosshair.AttachedLine.Point2.Y =
6257 Crosshair.AttachedLine.Point1.Y;
6258 notify_crosshair_change (true);
6259 return 0;
6261 /* move to new anchor */
6262 Crosshair.AttachedLine.Point1.X =
6263 Crosshair.AttachedLine.Point2.X;
6264 Crosshair.AttachedLine.Point1.Y =
6265 Crosshair.AttachedLine.Point2.Y;
6266 /* check if an intermediate point was removed */
6267 if (type & UNDO_REMOVE)
6269 /* this search should find the restored line */
6270 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6271 &ptrtmp,
6272 &ptr3,
6273 Crosshair.AttachedLine.Point2.X,
6274 Crosshair.AttachedLine.Point2.Y, 0);
6275 ptr2 = (LineType *) ptrtmp;
6276 if (TEST_FLAG (AUTODRCFLAG, PCB))
6278 /* undo loses CONNECTEDFLAG and FOUNDFLAG */
6279 SET_FLAG(CONNECTEDFLAG, ptr2);
6280 SET_FLAG(FOUNDFLAG, ptr2);
6281 DrawLine (CURRENT, ptr2);
6283 Crosshair.AttachedLine.Point1.X =
6284 Crosshair.AttachedLine.Point2.X = ptr2->Point2.X;
6285 Crosshair.AttachedLine.Point1.Y =
6286 Crosshair.AttachedLine.Point2.Y = ptr2->Point2.Y;
6288 FitCrosshairIntoGrid (Crosshair.X, Crosshair.Y);
6289 AdjustAttachedObjects ();
6290 if (--addedLines == 0)
6292 Crosshair.AttachedLine.State = STATE_SECOND;
6293 lastLayer = CURRENT;
6295 else
6297 /* this search is guaranteed to succeed too */
6298 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6299 &ptrtmp,
6300 &ptr3,
6301 Crosshair.AttachedLine.Point1.X,
6302 Crosshair.AttachedLine.Point1.Y, 0);
6303 ptr2 = (LineType *) ptrtmp;
6304 lastLayer = (LayerType *) ptr1;
6306 notify_crosshair_change (true);
6307 return 0;
6310 if (Settings.Mode == ARC_MODE)
6312 if (Crosshair.AttachedBox.State == STATE_SECOND)
6314 Crosshair.AttachedBox.State = STATE_FIRST;
6315 notify_crosshair_change (true);
6316 return 0;
6318 if (Crosshair.AttachedBox.State == STATE_THIRD)
6320 void *ptr1, *ptr2, *ptr3;
6321 BoxType *bx;
6322 /* guaranteed to succeed */
6323 SearchObjectByLocation (ARC_TYPE, &ptr1, &ptr2, &ptr3,
6324 Crosshair.AttachedBox.Point1.X,
6325 Crosshair.AttachedBox.Point1.Y, 0);
6326 bx = GetArcEnds ((ArcType *) ptr2);
6327 Crosshair.AttachedBox.Point1.X =
6328 Crosshair.AttachedBox.Point2.X = bx->X1;
6329 Crosshair.AttachedBox.Point1.Y =
6330 Crosshair.AttachedBox.Point2.Y = bx->Y1;
6331 AdjustAttachedObjects ();
6332 if (--addedLines == 0)
6333 Crosshair.AttachedBox.State = STATE_SECOND;
6336 /* undo the last destructive operation */
6337 if (Undo (true))
6338 SetChangedFlag (true);
6340 else if (function)
6342 switch (GetFunctionID (function))
6344 /* clear 'undo objects' list */
6345 case F_ClearList:
6346 ClearUndoList (false);
6347 break;
6350 notify_crosshair_change (true);
6351 return 0;
6354 /* --------------------------------------------------------------------------- */
6356 static const char redo_syntax[] = N_("Redo()");
6358 static const char redo_help[] = N_("Redo recent \"undo\" operations.");
6360 /* %start-doc actions Redo
6362 This routine allows you to recover from the last undo command. You
6363 might want to do this if you thought that undo was going to revert
6364 something other than what it actually did (in case you are confused
6365 about which operations are un-doable), or if you have been backing up
6366 through a long undo list and over-shoot your stopping point. Any
6367 change that is made since the undo in question will trim the redo
6368 list. For example if you add ten lines, then undo three of them you
6369 could use redo to put them back, but if you move a line on the board
6370 before performing the redo, you will lose the ability to "redo" the
6371 three "undone" lines.
6373 %end-doc */
6375 static int
6376 ActionRedo (int argc, char **argv, Coord x, Coord y)
6378 if (((Settings.Mode == POLYGON_MODE ||
6379 Settings.Mode == POLYGONHOLE_MODE) &&
6380 Crosshair.AttachedPolygon.PointN) ||
6381 Crosshair.AttachedLine.State == STATE_SECOND)
6382 return 1;
6383 notify_crosshair_change (false);
6384 if (Redo (true))
6386 SetChangedFlag (true);
6387 if (Settings.Mode == LINE_MODE &&
6388 Crosshair.AttachedLine.State != STATE_FIRST)
6390 LineType *line = g_list_last (CURRENT->Line)->data;
6391 Crosshair.AttachedLine.Point1.X =
6392 Crosshair.AttachedLine.Point2.X = line->Point2.X;
6393 Crosshair.AttachedLine.Point1.Y =
6394 Crosshair.AttachedLine.Point2.Y = line->Point2.Y;
6395 addedLines++;
6398 notify_crosshair_change (true);
6399 return 0;
6402 /* --------------------------------------------------------------------------- */
6404 static const char polygon_syntax[] = N_("Polygon(Close|PreviousPoint)");
6406 static const char polygon_help[] = N_("Some polygon related stuff.");
6408 /* %start-doc actions Polygon
6410 Polygons need a special action routine to make life easier.
6412 @table @code
6414 @item Close
6415 Creates the final segment of the polygon. This may fail if clipping
6416 to 45 degree lines is switched on, in which case a warning is issued.
6418 @item PreviousPoint
6419 Resets the newly entered corner to the previous one. The Undo action
6420 will call Polygon(PreviousPoint) when appropriate to do so.
6422 @end table
6424 %end-doc */
6426 static int
6427 ActionPolygon (int argc, char **argv, Coord x, Coord y)
6429 char *function = ARG (0);
6430 if (function && Settings.Mode == POLYGON_MODE)
6432 notify_crosshair_change (false);
6433 switch (GetFunctionID (function))
6435 /* close open polygon if possible */
6436 case F_Close:
6437 ClosePolygon ();
6438 break;
6440 /* go back to the previous point */
6441 case F_PreviousPoint:
6442 GoToPreviousPoint ();
6443 break;
6445 notify_crosshair_change (true);
6447 return 0;
6450 /* --------------------------------------------------------------------------- */
6452 static const char routestyle_syntax[] = N_("RouteStyle(1|2|3|4)");
6454 static const char routestyle_help[] =
6455 N_("Copies the indicated routing style into the current sizes.");
6457 /* %start-doc actions RouteStyle
6459 %end-doc */
6461 static int
6462 ActionRouteStyle (int argc, char **argv, Coord x, Coord y)
6464 char *str = ARG (0);
6465 RouteStyleType *rts;
6466 int number;
6468 if (str)
6470 number = atoi (str);
6471 if (number > 0 && number <= NUM_STYLES)
6473 rts = &PCB->RouteStyle[number - 1];
6474 SetLineSize (rts->Thick);
6475 SetViaSize (rts->Diameter, true);
6476 SetViaDrillingHole (rts->Hole, true);
6477 SetKeepawayWidth (rts->Keepaway);
6478 hid_action("RouteStylesChanged");
6481 return 0;
6485 /* --------------------------------------------------------------------------- */
6487 static const char moveobject_syntax[] = N_("MoveObject(X,Y,dim)");
6489 static const char moveobject_help[] =
6490 N_("Moves the object under the crosshair.");
6492 /* %start-doc actions MoveObject
6494 The @code{X} and @code{Y} are treated like @code{delta} is for many
6495 other objects. For each, if it's prefixed by @code{+} or @code{-},
6496 then that amount is relative. Otherwise, it's absolute. Units can be
6497 @code{mil} or @code{mm}; if unspecified, units are PCB's internal
6498 units, currently 1/100 mil.
6500 %end-doc */
6502 static int
6503 ActionMoveObject (int argc, char **argv, Coord x, Coord y)
6505 char *x_str = ARG (0);
6506 char *y_str = ARG (1);
6507 char *units = ARG (2);
6508 Coord nx, ny;
6509 bool absolute1, absolute2;
6510 void *ptr1, *ptr2, *ptr3;
6511 int type;
6513 ny = GetValue (y_str, units, &absolute1);
6514 nx = GetValue (x_str, units, &absolute2);
6516 type = SearchScreen (x, y, MOVE_TYPES, &ptr1, &ptr2, &ptr3);
6517 if (type == NO_TYPE)
6519 Message (_("Nothing found under crosshair\n"));
6520 return 1;
6522 if (absolute1)
6523 nx -= x;
6524 if (absolute2)
6525 ny -= y;
6526 Crosshair.AttachedObject.RubberbandN = 0;
6527 if (TEST_FLAG (RUBBERBANDFLAG, PCB))
6528 LookupRubberbandLines (type, ptr1, ptr2, ptr3);
6529 if (type == ELEMENT_TYPE)
6530 LookupRatLines (type, ptr1, ptr2, ptr3);
6531 MoveObjectAndRubberband (type, ptr1, ptr2, ptr3, nx, ny);
6532 SetChangedFlag (true);
6533 return 0;
6536 /* --------------------------------------------------------------------------- */
6538 static const char movetocurrentlayer_syntax[] =
6539 N_("MoveToCurrentLayer(Object|SelectedObjects)");
6541 static const char movetocurrentlayer_help[] =
6542 N_("Moves objects to the current layer.");
6544 /* %start-doc actions MoveToCurrentLayer
6546 Note that moving an element from a component layer to a solder layer,
6547 or from solder to component, won't automatically flip it. Use the
6548 @code{Flip()} action to do that.
6550 %end-doc */
6552 static int
6553 ActionMoveToCurrentLayer (int argc, char **argv, Coord x, Coord y)
6555 char *function = ARG (0);
6556 if (function)
6558 switch (GetFunctionID (function))
6560 case F_Object:
6562 int type;
6563 void *ptr1, *ptr2, *ptr3;
6565 gui->get_coords (_("Select an Object"), &x, &y);
6566 if ((type =
6567 SearchScreen (x, y, MOVETOLAYER_TYPES,
6568 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
6569 if (MoveObjectToLayer (type, ptr1, ptr2, ptr3, CURRENT, false))
6570 SetChangedFlag (true);
6571 break;
6574 case F_SelectedObjects:
6575 case F_Selected:
6576 if (MoveSelectedObjectsToLayer (CURRENT))
6577 SetChangedFlag (true);
6578 break;
6581 return 0;
6585 static const char setsame_syntax[] = N_("SetSame()");
6587 static const char setsame_help[] =
6588 N_("Sets current layer and sizes to match indicated item.");
6590 /* %start-doc actions SetSame
6592 When invoked over any line, arc, polygon, or via, this changes the
6593 current layer to be the layer that item is on, and changes the current
6594 sizes (thickness, keepaway, drill, etc) according to that item.
6596 %end-doc */
6598 static int
6599 ActionSetSame (int argc, char **argv, Coord x, Coord y)
6601 void *ptr1, *ptr2, *ptr3;
6602 int type;
6603 LayerType *layer = CURRENT;
6605 type = SearchScreen (x, y, CLONE_TYPES, &ptr1, &ptr2, &ptr3);
6606 /* set layer current and size from line or arc */
6607 switch (type)
6609 case LINE_TYPE:
6610 notify_crosshair_change (false);
6611 Settings.LineThickness = ((LineType *) ptr2)->Thickness;
6612 Settings.Keepaway = ((LineType *) ptr2)->Clearance / 2;
6613 layer = (LayerType *) ptr1;
6614 if (Settings.Mode != LINE_MODE)
6615 SetMode (LINE_MODE);
6616 notify_crosshair_change (true);
6617 hid_action ("RouteStylesChanged");
6618 break;
6620 case ARC_TYPE:
6621 notify_crosshair_change (false);
6622 Settings.LineThickness = ((ArcType *) ptr2)->Thickness;
6623 Settings.Keepaway = ((ArcType *) ptr2)->Clearance / 2;
6624 layer = (LayerType *) ptr1;
6625 if (Settings.Mode != ARC_MODE)
6626 SetMode (ARC_MODE);
6627 notify_crosshair_change (true);
6628 hid_action ("RouteStylesChanged");
6629 break;
6631 case POLYGON_TYPE:
6632 layer = (LayerType *) ptr1;
6633 break;
6635 case VIA_TYPE:
6636 notify_crosshair_change (false);
6637 Settings.ViaThickness = ((PinType *) ptr2)->Thickness;
6638 Settings.ViaDrillingHole = ((PinType *) ptr2)->DrillingHole;
6639 Settings.Keepaway = ((PinType *) ptr2)->Clearance / 2;
6640 if (Settings.Mode != VIA_MODE)
6641 SetMode (VIA_MODE);
6642 notify_crosshair_change (true);
6643 hid_action ("RouteStylesChanged");
6644 break;
6646 default:
6647 return 1;
6649 if (layer != CURRENT)
6651 ChangeGroupVisibility (GetLayerNumber (PCB->Data, layer), true, true);
6652 Redraw ();
6654 return 0;
6658 /* --------------------------------------------------------------------------- */
6660 static const char setflag_syntax[] =
6661 N_("SetFlag(Object|Selected|SelectedObjects, flag)\n"
6662 "SetFlag(SelectedLines|SelectedPins|SelectedVias, flag)\n"
6663 "SetFlag(SelectedPads|SelectedTexts|SelectedNames, flag)\n"
6664 "SetFlag(SelectedElements, flag)\n"
6665 "flag = square | octagon | thermal | join");
6667 static const char setflag_help[] = N_("Sets flags on objects.");
6669 /* %start-doc actions SetFlag
6671 Turns the given flag on, regardless of its previous setting. See
6672 @code{ChangeFlag}.
6674 @example
6675 SetFlag(SelectedPins,thermal)
6676 @end example
6678 %end-doc */
6680 static int
6681 ActionSetFlag (int argc, char **argv, Coord x, Coord y)
6683 char *function = ARG (0);
6684 char *flag = ARG (1);
6685 ChangeFlag (function, flag, 1, "SetFlag");
6686 return 0;
6689 /* --------------------------------------------------------------------------- */
6691 static const char clrflag_syntax[] =
6692 N_("ClrFlag(Object|Selected|SelectedObjects, flag)\n"
6693 "ClrFlag(SelectedLines|SelectedPins|SelectedVias, flag)\n"
6694 "ClrFlag(SelectedPads|SelectedTexts|SelectedNames, flag)\n"
6695 "ClrFlag(SelectedElements, flag)\n"
6696 "flag = square | octagon | thermal | join");
6698 static const char clrflag_help[] = N_("Clears flags on objects.");
6700 /* %start-doc actions ClrFlag
6702 Turns the given flag off, regardless of its previous setting. See
6703 @code{ChangeFlag}.
6705 @example
6706 ClrFlag(SelectedLines,join)
6707 @end example
6709 %end-doc */
6711 static int
6712 ActionClrFlag (int argc, char **argv, Coord x, Coord y)
6714 char *function = ARG (0);
6715 char *flag = ARG (1);
6716 ChangeFlag (function, flag, 0, "ClrFlag");
6717 return 0;
6720 /* --------------------------------------------------------------------------- */
6722 static const char changeflag_syntax[] =
6723 N_("ChangeFlag(Object|Selected|SelectedObjects, flag, value)\n"
6724 "ChangeFlag(SelectedLines|SelectedPins|SelectedVias, flag, value)\n"
6725 "ChangeFlag(SelectedPads|SelectedTexts|SelectedNames, flag, value)\n"
6726 "ChangeFlag(SelectedElements, flag, value)\n"
6727 "flag = square | octagon | thermal | join\n"
6728 "value = 0 | 1");
6730 static const char changeflag_help[] = N_("Sets or clears flags on objects.");
6732 /* %start-doc actions ChangeFlag
6734 Toggles the given flag on the indicated object(s). The flag may be
6735 one of the flags listed above (square, octagon, thermal, join). The
6736 value may be the number 0 or 1. If the value is 0, the flag is
6737 cleared. If the value is 1, the flag is set.
6739 %end-doc */
6741 static int
6742 ActionChangeFlag (int argc, char **argv, Coord x, Coord y)
6744 char *function = ARG (0);
6745 char *flag = ARG (1);
6746 int value = argc > 2 ? atoi (argv[2]) : -1;
6747 if (value != 0 && value != 1)
6748 AFAIL (changeflag);
6750 ChangeFlag (function, flag, value, "ChangeFlag");
6751 return 0;
6755 static void
6756 ChangeFlag (char *what, char *flag_name, int value, char *cmd_name)
6758 bool (*set_object) (int, void *, void *, void *);
6759 bool (*set_selected) (int);
6761 if (NSTRCMP (flag_name, "square") == 0)
6763 set_object = value ? SetObjectSquare : ClrObjectSquare;
6764 set_selected = value ? SetSelectedSquare : ClrSelectedSquare;
6766 else if (NSTRCMP (flag_name, "octagon") == 0)
6768 set_object = value ? SetObjectOctagon : ClrObjectOctagon;
6769 set_selected = value ? SetSelectedOctagon : ClrSelectedOctagon;
6771 else if (NSTRCMP (flag_name, "join") == 0)
6773 /* Note: these are backwards, because the flag is "clear" but
6774 the command is "join". */
6775 set_object = value ? ClrObjectJoin : SetObjectJoin;
6776 set_selected = value ? ClrSelectedJoin : SetSelectedJoin;
6778 else
6780 Message (_("%s(): Flag \"%s\" is not valid\n"), cmd_name, flag_name);
6781 return;
6784 switch (GetFunctionID (what))
6786 case F_Object:
6788 int type;
6789 void *ptr1, *ptr2, *ptr3;
6791 if ((type =
6792 SearchScreen (Crosshair.X, Crosshair.Y, CHANGESIZE_TYPES,
6793 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
6794 if (TEST_FLAG (LOCKFLAG, (PinType *) ptr2))
6795 Message (_("Sorry, the object is locked\n"));
6796 if (set_object (type, ptr1, ptr2, ptr3))
6797 SetChangedFlag (true);
6798 break;
6801 case F_SelectedVias:
6802 if (set_selected (VIA_TYPE))
6803 SetChangedFlag (true);
6804 break;
6806 case F_SelectedPins:
6807 if (set_selected (PIN_TYPE))
6808 SetChangedFlag (true);
6809 break;
6811 case F_SelectedPads:
6812 if (set_selected (PAD_TYPE))
6813 SetChangedFlag (true);
6814 break;
6816 case F_SelectedLines:
6817 if (set_selected (LINE_TYPE))
6818 SetChangedFlag (true);
6819 break;
6821 case F_SelectedTexts:
6822 if (set_selected (TEXT_TYPE))
6823 SetChangedFlag (true);
6824 break;
6826 case F_SelectedNames:
6827 if (set_selected (ELEMENTNAME_TYPE))
6828 SetChangedFlag (true);
6829 break;
6831 case F_SelectedElements:
6832 if (set_selected (ELEMENT_TYPE))
6833 SetChangedFlag (true);
6834 break;
6836 case F_Selected:
6837 case F_SelectedObjects:
6838 if (set_selected (CHANGESIZE_TYPES))
6839 SetChangedFlag (true);
6840 break;
6844 /* --------------------------------------------------------------------------- */
6846 static const char executefile_syntax[] = N_("ExecuteFile(filename)");
6848 static const char executefile_help[] = N_("Run actions from the given file.");
6850 /* %start-doc actions ExecuteFile
6852 Lines starting with @code{#} are ignored.
6854 %end-doc */
6856 static int
6857 ActionExecuteFile (int argc, char **argv, Coord x, Coord y)
6859 FILE *fp;
6860 char *fname;
6861 char line[256];
6862 int n = 0;
6863 char *sp;
6865 if (argc != 1)
6866 AFAIL (executefile);
6868 fname = argv[0];
6870 if ((fp = fopen (fname, "r")) == NULL)
6872 fprintf (stderr, _("Could not open actions file \"%s\".\n"), fname);
6873 return 1;
6876 defer_updates = 1;
6877 defer_needs_update = 0;
6878 while (fgets (line, sizeof (line), fp) != NULL)
6880 n++;
6881 sp = line;
6883 /* eat the trailing newline */
6884 while (*sp && *sp != '\r' && *sp != '\n')
6885 sp++;
6886 *sp = '\0';
6888 /* eat leading spaces and tabs */
6889 sp = line;
6890 while (*sp && (*sp == ' ' || *sp == '\t'))
6891 sp++;
6894 * if we have anything left and its not a comment line
6895 * then execute it
6898 if (*sp && *sp != '#')
6900 /*Message ("%s : line %-3d : \"%s\"\n", fname, n, sp);*/
6901 hid_parse_actions (sp);
6905 defer_updates = 0;
6906 if (defer_needs_update)
6908 IncrementUndoSerialNumber ();
6909 gui->invalidate_all ();
6911 fclose (fp);
6912 return 0;
6915 /* --------------------------------------------------------------------------- */
6917 static int
6918 ActionPSCalib (int argc, char **argv, Coord x, Coord y)
6920 HID *ps = hid_find_exporter ("ps");
6921 ps->calibrate (0.0,0.0);
6922 return 0;
6925 /* --------------------------------------------------------------------------- */
6927 static ElementType *element_cache = NULL;
6929 static ElementType *
6930 find_element_by_refdes (char *refdes)
6932 if (element_cache
6933 && NAMEONPCB_NAME(element_cache)
6934 && strcmp (NAMEONPCB_NAME(element_cache), refdes) == 0)
6935 return element_cache;
6937 ELEMENT_LOOP (PCB->Data);
6939 if (NAMEONPCB_NAME(element)
6940 && strcmp (NAMEONPCB_NAME(element), refdes) == 0)
6942 element_cache = element;
6943 return element_cache;
6946 END_LOOP;
6947 return NULL;
6950 static AttributeType *
6951 lookup_attr (AttributeListType *list, const char *name)
6953 int i;
6954 for (i=0; i<list->Number; i++)
6955 if (strcmp (list->List[i].name, name) == 0)
6956 return & list->List[i];
6957 return NULL;
6960 static void
6961 delete_attr (AttributeListType *list, AttributeType *attr)
6963 int idx = attr - list->List;
6964 if (idx < 0 || idx >= list->Number)
6965 return;
6966 if (list->Number - idx > 1)
6967 memmove (attr, attr+1, (list->Number - idx - 1) * sizeof(AttributeType));
6968 list->Number --;
6971 /* ---------------------------------------------------------------- */
6972 static const char elementlist_syntax[] =
6973 N_("ElementList(Start|Done|Need,<refdes>,<footprint>,<value>)");
6975 static const char elementlist_help[] =
6976 N_("Adds the given element if it doesn't already exist.");
6978 /* %start-doc actions elementlist
6980 @table @code
6982 @item Start
6983 Indicates the start of an element list; call this before any Need
6984 actions.
6986 @item Need
6987 Searches the board for an element with a matching refdes.
6989 If found, the value and footprint are updated.
6991 If not found, a new element is created with the given footprint and value.
6993 @item Done
6994 Compares the list of elements needed since the most recent
6995 @code{start} with the list of elements actually on the board. Any
6996 elements that weren't listed are selected, so that the user may delete
6997 them.
6999 @end table
7001 %end-doc */
7003 static int number_of_footprints_not_found;
7005 static int
7006 parse_layout_attribute_units (char *name, int def)
7008 const char *as = AttributeGet (PCB, name);
7009 if (!as)
7010 return def;
7011 return GetValue (as, NULL, NULL);
7014 static int
7015 ActionElementList (int argc, char **argv, Coord x, Coord y)
7017 ElementType *e = NULL;
7018 char *refdes, *value, *footprint, *old;
7019 char *args[3];
7020 char *function = argv[0];
7022 #ifdef DEBUG
7023 printf("Entered ActionElementList, executing function %s\n", function);
7024 #endif
7026 if (strcasecmp (function, "start") == 0)
7028 ELEMENT_LOOP (PCB->Data);
7030 CLEAR_FLAG (FOUNDFLAG, element);
7032 END_LOOP;
7033 element_cache = NULL;
7034 number_of_footprints_not_found = 0;
7035 return 0;
7038 if (strcasecmp (function, "done") == 0)
7040 ELEMENT_LOOP (PCB->Data);
7042 if (TEST_FLAG (FOUNDFLAG, element))
7044 CLEAR_FLAG (FOUNDFLAG, element);
7046 else if (! EMPTY_STRING_P (NAMEONPCB_NAME (element)))
7048 /* Unnamed elements should remain untouched */
7049 SET_FLAG (SELECTEDFLAG, element);
7052 END_LOOP;
7053 if (number_of_footprints_not_found > 0)
7054 gui->confirm_dialog (_("Not all requested footprints were found.\n"
7055 "See the message log for details"),
7056 "Ok", NULL);
7057 return 0;
7060 if (strcasecmp (function, "need") != 0)
7061 AFAIL (elementlist);
7063 if (argc != 4)
7064 AFAIL (elementlist);
7066 argc --;
7067 argv ++;
7069 refdes = ARG(0);
7070 footprint = ARG(1);
7071 value = ARG(2);
7073 args[0] = footprint;
7074 args[1] = refdes;
7075 args[2] = value;
7077 #ifdef DEBUG
7078 printf(" ... footprint = %s\n", footprint);
7079 printf(" ... refdes = %s\n", refdes);
7080 printf(" ... value = %s\n", value);
7081 #endif
7083 e = find_element_by_refdes (refdes);
7085 if (!e)
7087 Coord nx, ny, d;
7089 #ifdef DEBUG
7090 printf(" ... Footprint not on board, need to add it.\n");
7091 #endif
7092 /* Not on board, need to add it. */
7093 if (LoadFootprint(argc, args, x, y))
7095 number_of_footprints_not_found ++;
7096 return 1;
7099 nx = PCB->MaxWidth / 2;
7100 ny = PCB->MaxHeight / 2;
7101 d = MIN (PCB->MaxWidth, PCB->MaxHeight) / 10;
7103 nx = parse_layout_attribute_units ("import::newX", nx);
7104 ny = parse_layout_attribute_units ("import::newY", ny);
7105 d = parse_layout_attribute_units ("import::disperse", d);
7107 if (d > 0)
7109 nx += rand () % (d*2) - d;
7110 ny += rand () % (d*2) - d;
7113 if (nx < 0)
7114 nx = 0;
7115 if (nx >= PCB->MaxWidth)
7116 nx = PCB->MaxWidth - 1;
7117 if (ny < 0)
7118 ny = 0;
7119 if (ny >= PCB->MaxHeight)
7120 ny = PCB->MaxHeight - 1;
7122 /* Place components onto center of board. */
7123 if (CopyPastebufferToLayout (nx, ny))
7124 SetChangedFlag (true);
7127 else if (e && DESCRIPTION_NAME(e) && strcmp (DESCRIPTION_NAME(e), footprint) != 0)
7129 #ifdef DEBUG
7130 printf(" ... Footprint on board, but different from footprint loaded.\n");
7131 #endif
7132 int er, pr, i;
7133 Coord mx, my;
7134 ElementType *pe;
7136 /* Different footprint, we need to swap them out. */
7137 if (LoadFootprint(argc, args, x, y))
7139 number_of_footprints_not_found ++;
7140 return 1;
7143 er = ElementOrientation (e);
7144 pe = PASTEBUFFER->Data->Element->data;
7145 if (!FRONT (e))
7146 MirrorElementCoordinates (PASTEBUFFER->Data, pe, pe->MarkY*2 - PCB->MaxHeight);
7147 pr = ElementOrientation (pe);
7149 mx = e->MarkX;
7150 my = e->MarkY;
7152 if (er != pr)
7153 RotateElementLowLevel (PASTEBUFFER->Data, pe, pe->MarkX, pe->MarkY, (er-pr+4)%4);
7155 for (i=0; i<MAX_ELEMENTNAMES; i++)
7157 pe->Name[i].X = e->Name[i].X - mx + pe->MarkX ;
7158 pe->Name[i].Y = e->Name[i].Y - my + pe->MarkY ;
7159 pe->Name[i].Direction = e->Name[i].Direction;
7160 pe->Name[i].Scale = e->Name[i].Scale;
7163 RemoveElement (e);
7165 if (CopyPastebufferToLayout (mx, my))
7166 SetChangedFlag (true);
7169 /* Now reload footprint */
7170 element_cache = NULL;
7171 e = find_element_by_refdes (refdes);
7173 old = ChangeElementText (PCB, PCB->Data, e, NAMEONPCB_INDEX, strdup (refdes));
7174 if (old)
7175 free(old);
7176 old = ChangeElementText (PCB, PCB->Data, e, VALUE_INDEX, strdup (value));
7177 if (old)
7178 free(old);
7180 SET_FLAG (FOUNDFLAG, e);
7182 #ifdef DEBUG
7183 printf(" ... Leaving ActionElementList.\n");
7184 #endif
7186 return 0;
7189 /* ---------------------------------------------------------------- */
7190 static const char elementsetattr_syntax[] =
7191 N_("ElementSetAttr(refdes,name[,value])");
7193 static const char elementsetattr_help[] =
7194 N_("Sets or clears an element-specific attribute.");
7196 /* %start-doc actions elementsetattr
7198 If a value is specified, the named attribute is added (if not already
7199 present) or changed (if it is) to the given value. If the value is
7200 not specified, the given attribute is removed if present.
7202 %end-doc */
7204 static int
7205 ActionElementSetAttr (int argc, char **argv, Coord x, Coord y)
7207 ElementType *e = NULL;
7208 char *refdes, *name, *value;
7209 AttributeType *attr;
7211 if (argc < 2)
7213 AFAIL (changepinname);
7216 refdes = argv[0];
7217 name = argv[1];
7218 value = ARG(2);
7220 ELEMENT_LOOP (PCB->Data);
7222 if (NSTRCMP (refdes, NAMEONPCB_NAME (element)) == 0)
7224 e = element;
7225 break;
7228 END_LOOP;
7230 if (!e)
7232 Message(_("Cannot change attribute of %s - element not found\n"), refdes);
7233 return 1;
7236 attr = lookup_attr (&e->Attributes, name);
7238 if (attr && value)
7240 free (attr->value);
7241 attr->value = strdup (value);
7243 if (attr && ! value)
7245 delete_attr (& e->Attributes, attr);
7247 if (!attr && value)
7249 CreateNewAttribute (& e->Attributes, name, value);
7252 return 0;
7255 /* ---------------------------------------------------------------- */
7256 static const char execcommand_syntax[] = N_("ExecCommand(command)");
7258 static const char execcommand_help[] = N_("Runs a command.");
7260 /* %start-doc actions execcommand
7262 Runs the given command, which is a system executable.
7264 %end-doc */
7266 static int
7267 ActionExecCommand (int argc, char **argv, Coord x, Coord y)
7269 char *command;
7271 if (argc < 1)
7273 AFAIL (execcommand);
7276 command = ARG(0);
7278 if (system (command))
7279 return 1;
7280 return 0;
7283 /* ---------------------------------------------------------------- */
7285 static int
7286 pcb_spawnvp (char **argv)
7288 #ifdef HAVE__SPAWNVP
7289 int result = _spawnvp (_P_WAIT, argv[0], (const char * const *) argv);
7290 if (result == -1)
7291 return 1;
7292 else
7293 return 0;
7294 #else
7295 int pid;
7296 pid = fork ();
7297 if (pid < 0)
7299 /* error */
7300 Message(_("Cannot fork!"));
7301 return 1;
7303 else if (pid == 0)
7305 /* Child */
7306 execvp (argv[0], argv);
7307 exit(1);
7309 else
7311 int rv;
7312 /* Parent */
7313 wait (&rv);
7315 return 0;
7316 #endif
7319 /* ---------------------------------------------------------------- */
7321 * Creates a new temporary file name. Hopefully the operating system
7322 * provides a mkdtemp() function to securily create a temporary
7323 * directory with mode 0700. If so then that directory is created and
7324 * the returned string is made up of the directory plus the name
7325 * variable. For example:
7327 * tempfile_name_new ("myfile") might return
7328 * "/var/tmp/pcb.123456/myfile".
7330 * If mkdtemp() is not available then 'name' is ignored and the
7331 * insecure tmpnam() function is used.
7333 * Files/names created with tempfile_name_new() should be unlinked
7334 * with tempfile_unlink to make sure the temporary directory is also
7335 * removed when mkdtemp() is used.
7337 static char *
7338 tempfile_name_new (char * name)
7340 char *tmpfile = NULL;
7341 #ifdef HAVE_MKDTEMP
7342 char *tmpdir, *mytmpdir;
7343 size_t len;
7344 #endif
7346 assert ( name != NULL );
7348 #ifdef HAVE_MKDTEMP
7349 #define TEMPLATE "pcb.XXXXXXXX"
7352 tmpdir = getenv ("TMPDIR");
7354 /* FIXME -- what about win32? */
7355 if (tmpdir == NULL) {
7356 tmpdir = "/tmp";
7359 mytmpdir = (char *) malloc (sizeof(char) *
7360 (strlen (tmpdir) +
7362 strlen (TEMPLATE) +
7363 1));
7364 if (mytmpdir == NULL) {
7365 fprintf (stderr, "%s(): malloc failed()\n", __FUNCTION__);
7366 exit (1);
7369 *mytmpdir = '\0';
7370 (void)strcat (mytmpdir, tmpdir);
7371 (void)strcat (mytmpdir, PCB_DIR_SEPARATOR_S);
7372 (void)strcat (mytmpdir, TEMPLATE);
7373 if (mkdtemp (mytmpdir) == NULL) {
7374 fprintf (stderr, "%s(): mkdtemp (\"%s\") failed\n", __FUNCTION__, mytmpdir);
7375 free (mytmpdir);
7376 return NULL;
7380 len = strlen (mytmpdir) + /* the temp directory name */
7381 1 + /* the directory sep. */
7382 strlen (name) + /* the file name */
7383 1 /* the \0 termination */
7386 tmpfile = (char *) malloc (sizeof (char) * len);
7388 *tmpfile = '\0';
7389 (void)strcat (tmpfile, mytmpdir);
7390 (void)strcat (tmpfile, PCB_DIR_SEPARATOR_S);
7391 (void)strcat (tmpfile, name);
7393 free (mytmpdir);
7394 #undef TEMPLATE
7395 #else
7397 * tmpnam() uses a static buffer so strdup() the result right away
7398 * in case someone decides to create multiple temp names.
7400 tmpfile = strdup (tmpnam (NULL));
7401 #ifdef __WIN32__
7403 /* Guile doesn't like \ separators */
7404 char *c;
7405 for (c = tmpfile; *c; c++)
7406 if (*c == '\\')
7407 *c = '/';
7409 #endif
7410 #endif
7412 return tmpfile;
7415 /* ---------------------------------------------------------------- */
7417 * Unlink a temporary file. If we have mkdtemp() then our temp file
7418 * lives in a temporary directory and we need to remove that directory
7419 * too.
7421 static int
7422 tempfile_unlink (char * name)
7424 #ifdef DEBUG
7425 /* SDB says: Want to keep old temp files for examiniation when debugging */
7426 return 0;
7427 #endif
7429 #ifdef HAVE_MKDTEMP
7430 int e, rc2 = 0;
7431 char *dname;
7433 unlink (name);
7434 /* it is possible that the file was never created so it is OK if the
7435 unlink fails */
7437 /* now figure out the directory name to remove */
7438 e = strlen (name) - 1;
7439 while (e > 0 && name[e] != PCB_DIR_SEPARATOR_C) {e--;}
7441 dname = strdup (name);
7442 dname[e] = '\0';
7445 * at this point, e *should* point to the end of the directory part
7446 * but lets make sure.
7448 if (e > 0) {
7449 rc2 = rmdir (dname);
7450 if (rc2 != 0) {
7451 perror (dname);
7454 } else {
7455 fprintf (stderr, _("%s(): Unable to determine temp directory name from the temp file\n"),
7456 __FUNCTION__);
7457 fprintf (stderr, "%s(): \"%s\"\n",
7458 __FUNCTION__, name);
7459 rc2 = -1;
7462 /* name was allocated with malloc */
7463 free (dname);
7464 free (name);
7467 * FIXME - should also return -1 if the temp file exists and was not
7468 * removed.
7470 if (rc2 != 0) {
7471 return -1;
7474 #else
7475 int rc = unlink (name);
7477 if (rc != 0) {
7478 fprintf (stderr, _("Failed to unlink \"%s\"\n"), name);
7479 free (name);
7480 return rc;
7482 free (name);
7484 #endif
7486 return 0;
7489 /* ---------------------------------------------------------------- */
7490 static const char import_syntax[] =
7491 N_("Import()\n"
7492 "Import([gnetlist|make[,source,source,...]])\n"
7493 "Import(setnewpoint[,(mark|center|X,Y)])\n"
7494 "Import(setdisperse,D,units)\n");
7496 static const char import_help[] = N_("Import schematics.");
7498 /* %start-doc actions Import
7500 Imports element and netlist data from the schematics (or some other
7501 source). The first parameter, which is optional, is the mode. If not
7502 specified, the @code{import::mode} attribute in the PCB is used.
7503 @code{gnetlist} means gnetlist is used to obtain the information from
7504 the schematics. @code{make} invokes @code{make}, assuming the user
7505 has a @code{Makefile} in the current directory. The @code{Makefile}
7506 will be invoked with the following variables set:
7508 @table @code
7510 @item PCB
7511 The name of the .pcb file
7513 @item SRCLIST
7514 A space-separated list of source files
7516 @item OUT
7517 The name of the file in which to put the command script, which may
7518 contain any @pcb{} actions. By default, this is a temporary file
7519 selected by @pcb{}, but if you specify an @code{import::outfile}
7520 attribute, that file name is used instead (and not automatically
7521 deleted afterwards).
7523 @end table
7525 The target specified to be built is the first of these that apply:
7527 @itemize @bullet
7529 @item
7530 The target specified by an @code{import::target} attribute.
7532 @item
7533 The output file specified by an @code{import::outfile} attribute.
7535 @item
7536 If nothing else is specified, the target is @code{pcb_import}.
7538 @end itemize
7540 If you specify an @code{import::makefile} attribute, then "-f <that
7541 file>" will be added to the command line.
7543 If you specify the mode, you may also specify the source files
7544 (schematics). If you do not specify any, the list of schematics is
7545 obtained by reading the @code{import::src@var{N}} attributes (like
7546 @code{import::src0}, @code{import::src1}, etc).
7548 For compatibility with future extensions to the import file format,
7549 the generated file @emph{must not} start with the two characters
7550 @code{#%}.
7552 If a temporary file is needed the @code{TMPDIR} environment variable
7553 is used to select its location.
7555 Note that the programs @code{gnetlist} and @code{make} may be
7556 overridden by the user via the @code{make-program} and @code{gnetlist}
7557 @code{pcb} settings (i.e. in @code{~/.pcb/settings} or on the command
7558 line).
7560 If @pcb{} cannot determine which schematic(s) to import from, the GUI
7561 is called to let user choose (see @code{ImportGUI()}).
7563 Note that Import() doesn't delete anything - after an Import, elements
7564 which shouldn't be on the board are selected and may be removed once
7565 it's determined that the deletion is appropriate.
7567 If @code{Import()} is called with @code{setnewpoint}, then the location
7568 of new components can be specified. This is where parts show up when
7569 they're added to the board. The default is the center of the board.
7571 @table @code
7573 @item Import(setnewpoint)
7575 Prompts the user to click on the board somewhere, uses that point. If
7576 called by a hotkey, uses the current location of the crosshair.
7578 @item Import(setnewpoint,mark)
7580 Uses the location of the mark. If no mark is present, the point is
7581 not changed.
7583 @item Import(setnewpoint,center)
7585 Resets the point to the center of the board.
7587 @item Import(setnewpoint,X,Y,units)
7589 Sets the point to the specific coordinates given. Example:
7590 @code{Import(setnewpoint,50,25,mm)}
7592 @end table
7594 Note that the X and Y locations are stored in attributes named
7595 @code{import::newX} and @code{import::newY} so you could change them
7596 manually if you wished.
7598 Calling @code{Import(setdisperse,D,units)} sets how much the newly
7599 placed elements are dispersed relative to the set point. For example,
7600 @code{Import(setdisperse,10,mm)} will offset each part randomly up to
7601 10mm away from the point. The default dispersion is 1/10th of the
7602 smallest board dimension. Dispersion is saved in the
7603 @code{import::disperse} attribute.
7605 %end-doc */
7607 static int
7608 ActionImport (int argc, char **argv, Coord x, Coord y)
7610 char *mode;
7611 char **sources = NULL;
7612 int nsources = 0;
7614 #ifdef DEBUG
7615 printf("ActionImport: =========== Entering ActionImport ============\n");
7616 #endif
7618 mode = ARG (0);
7620 if (mode && strcasecmp (mode, "setdisperse") == 0)
7622 char *ds, *units;
7623 char buf[50];
7625 ds = ARG (1);
7626 units = ARG (2);
7627 if (!ds)
7629 const char *as = AttributeGet (PCB, "import::disperse");
7630 ds = gui->prompt_for(_("Enter dispersion:"), as ? as : "0");
7632 if (units)
7634 sprintf(buf, "%s%s", ds, units);
7635 AttributePut (PCB, "import::disperse", buf);
7637 else
7638 AttributePut (PCB, "import::disperse", ds);
7639 if (ARG (1) == NULL)
7640 free (ds);
7641 return 0;
7644 if (mode && strcasecmp (mode, "setnewpoint") == 0)
7646 const char *xs, *ys, *units;
7647 Coord x, y;
7648 char buf[50];
7650 xs = ARG (1);
7651 ys = ARG (2);
7652 units = ARG (3);
7654 if (!xs)
7656 gui->get_coords (_("Click on a location"), &x, &y);
7658 else if (strcasecmp (xs, "center") == 0)
7660 AttributeRemove (PCB, "import::newX");
7661 AttributeRemove (PCB, "import::newY");
7662 return 0;
7664 else if (strcasecmp (xs, "mark") == 0)
7666 if (!Marked.status)
7667 return 0;
7669 x = Marked.X;
7670 y = Marked.Y;
7672 else if (ys)
7674 x = GetValue (xs, units, NULL);
7675 y = GetValue (ys, units, NULL);
7677 else
7679 Message (_("Bad syntax for Import(setnewpoint)"));
7680 return 1;
7683 pcb_sprintf (buf, "%$ms", x);
7684 AttributePut (PCB, "import::newX", buf);
7685 pcb_sprintf (buf, "%$ms", y);
7686 AttributePut (PCB, "import::newY", buf);
7687 return 0;
7690 if (! mode)
7691 mode = AttributeGet (PCB, "import::mode");
7692 if (! mode)
7693 mode = "gnetlist";
7695 if (argc > 1)
7697 sources = argv + 1;
7698 nsources = argc - 1;
7701 if (! sources)
7703 char sname[40];
7704 char *src;
7706 nsources = -1;
7707 do {
7708 nsources ++;
7709 sprintf(sname, "import::src%d", nsources);
7710 src = AttributeGet (PCB, sname);
7711 } while (src);
7713 if (nsources > 0)
7715 sources = (char **) malloc ((nsources + 1) * sizeof (char *));
7716 nsources = -1;
7717 do {
7718 nsources ++;
7719 sprintf(sname, "import::src%d", nsources);
7720 src = AttributeGet (PCB, sname);
7721 sources[nsources] = src;
7722 } while (src);
7726 if (! sources)
7728 /* Replace .pcb with .sch and hope for the best. */
7729 char *pcbname = PCB->Filename;
7730 char *schname;
7731 char *dot, *slash, *bslash;
7733 if (!pcbname)
7734 return hid_action("ImportGUI");
7736 schname = (char *) malloc (strlen(pcbname) + 5);
7737 strcpy (schname, pcbname);
7738 dot = strchr (schname, '.');
7739 slash = strchr (schname, '/');
7740 bslash = strchr (schname, '\\');
7741 if (dot && slash && dot < slash)
7742 dot = NULL;
7743 if (dot && bslash && dot < bslash)
7744 dot = NULL;
7745 if (dot)
7746 *dot = 0;
7747 strcat (schname, ".sch");
7749 if (access (schname, F_OK))
7751 free (schname);
7752 return hid_action("ImportGUI");
7755 sources = (char **) malloc (2 * sizeof (char *));
7756 sources[0] = schname;
7757 sources[1] = NULL;
7758 nsources = 1;
7761 if (strcasecmp (mode, "gnetlist") == 0)
7763 char *tmpfile = tempfile_name_new ("gnetlist_output");
7764 char **cmd;
7765 int i;
7767 if (tmpfile == NULL) {
7768 Message (_("Could not create temp file"));
7769 return 1;
7772 cmd = (char **) malloc ((7 + nsources) * sizeof (char *));
7773 cmd[0] = Settings.GnetlistProgram;
7774 cmd[1] = "-g";
7775 cmd[2] = "pcbfwd";
7776 cmd[3] = "-o";
7777 cmd[4] = tmpfile;
7778 cmd[5] = "--";
7779 for (i=0; i<nsources; i++)
7780 cmd[6+i] = sources[i];
7781 cmd[6+nsources] = NULL;
7783 #ifdef DEBUG
7784 printf("ActionImport: =========== About to run gnetlist ============\n");
7785 printf("%s %s %s %s %s %s %s ...\n",
7786 cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6]);
7787 #endif
7789 if (pcb_spawnvp (cmd))
7791 unlink (tmpfile);
7792 return 1;
7795 #ifdef DEBUG
7796 printf("ActionImport: =========== About to run ActionExecuteFile, file = %s ============\n", tmpfile);
7797 #endif
7799 cmd[0] = tmpfile;
7800 cmd[1] = NULL;
7801 ActionExecuteFile (1, cmd, 0, 0);
7803 free (cmd);
7804 tempfile_unlink (tmpfile);
7806 else if (strcasecmp (mode, "make") == 0)
7808 int must_free_tmpfile = 0;
7809 char *tmpfile;
7810 char *cmd[10];
7811 int i;
7812 char *srclist;
7813 int srclen;
7814 char *user_outfile = NULL;
7815 char *user_makefile = NULL;
7816 char *user_target = NULL;
7819 user_outfile = AttributeGet (PCB, "import::outfile");
7820 user_makefile = AttributeGet (PCB, "import::makefile");
7821 user_target = AttributeGet (PCB, "import::target");
7822 if (user_outfile && !user_target)
7823 user_target = user_outfile;
7825 if (user_outfile)
7826 tmpfile = user_outfile;
7827 else
7829 tmpfile = tempfile_name_new ("gnetlist_output");
7830 if (tmpfile == NULL) {
7831 Message (_("Could not create temp file"));
7832 return 1;
7834 must_free_tmpfile = 1;
7837 srclen = sizeof("SRCLIST=") + 2;
7838 for (i=0; i<nsources; i++)
7839 srclen += strlen (sources[i]) + 2;
7840 srclist = (char *) malloc (srclen);
7841 strcpy (srclist, "SRCLIST=");
7842 for (i=0; i<nsources; i++)
7844 if (i)
7845 strcat (srclist, " ");
7846 strcat (srclist, sources[i]);
7849 cmd[0] = Settings.MakeProgram;
7850 cmd[1] = "-s";
7851 cmd[2] = Concat ("PCB=", PCB->Filename, NULL);
7852 cmd[3] = srclist;
7853 cmd[4] = Concat ("OUT=", tmpfile, NULL);
7854 i = 5;
7855 if (user_makefile)
7857 cmd[i++] = "-f";
7858 cmd[i++] = user_makefile;
7860 cmd[i++] = user_target ? user_target : (char *)"pcb_import";
7861 cmd[i++] = NULL;
7863 if (pcb_spawnvp (cmd))
7865 if (must_free_tmpfile)
7866 unlink (tmpfile);
7867 free (cmd[2]);
7868 free (cmd[3]);
7869 free (cmd[4]);
7870 return 1;
7873 cmd[0] = tmpfile;
7874 cmd[1] = NULL;
7875 ActionExecuteFile (1, cmd, 0, 0);
7877 free (cmd[2]);
7878 free (cmd[3]);
7879 free (cmd[4]);
7880 if (must_free_tmpfile)
7881 tempfile_unlink (tmpfile);
7883 else
7885 Message (_("Unknown import mode: %s\n"), mode);
7886 return 1;
7889 DeleteRats (false);
7890 AddAllRats (false, NULL);
7892 #ifdef DEBUG
7893 printf("ActionImport: =========== Leaving ActionImport ============\n");
7894 #endif
7896 return 0;
7899 /* ------------------------------------------------------------ */
7901 static const char attributes_syntax[] =
7902 N_("Attributes(Layout|Layer|Element)\n"
7903 "Attributes(Layer,layername)");
7905 static const char attributes_help[] =
7906 N_("Let the user edit the attributes of the layout, current or given\n"
7907 "layer, or selected element.");
7909 /* %start-doc actions Attributes
7911 This just pops up a dialog letting the user edit the attributes of the
7912 pcb, an element, or a layer.
7914 %end-doc */
7917 static int
7918 ActionAttributes (int argc, char **argv, Coord x, Coord y)
7920 char *function = ARG (0);
7921 char *layername = ARG (1);
7922 char *buf;
7924 if (!function)
7925 AFAIL (attributes);
7927 if (!gui->edit_attributes)
7929 Message (_("This GUI doesn't support Attribute Editing\n"));
7930 return 1;
7933 switch (GetFunctionID (function))
7935 case F_Layout:
7937 gui->edit_attributes(_("Layout Attributes"), &(PCB->Attributes));
7938 return 0;
7941 case F_Layer:
7943 LayerType *layer = CURRENT;
7944 if (layername)
7946 int i;
7947 layer = NULL;
7948 for (i=0; i<max_copper_layer; i++)
7949 if (strcmp (PCB->Data->Layer[i].Name, layername) == 0)
7951 layer = & (PCB->Data->Layer[i]);
7952 break;
7954 if (layer == NULL)
7956 Message (_("No layer named %s\n"), layername);
7957 return 1;
7960 buf = (char *) malloc (strlen (layer->Name) +
7961 strlen (_("Layer %s Attributes")));
7962 sprintf (buf, _("Layer %s Attributes"), layer->Name);
7963 gui->edit_attributes(buf, &(layer->Attributes));
7964 free (buf);
7965 return 0;
7968 case F_Element:
7970 int n_found = 0;
7971 ElementType *e = NULL;
7972 ELEMENT_LOOP (PCB->Data);
7974 if (TEST_FLAG (SELECTEDFLAG, element))
7976 e = element;
7977 n_found ++;
7980 END_LOOP;
7981 if (n_found > 1)
7983 Message (_("Too many elements selected\n"));
7984 return 1;
7986 if (n_found == 0)
7988 void *ptrtmp;
7989 gui->get_coords (_("Click on an element"), &x, &y);
7990 if ((SearchScreen
7991 (x, y, ELEMENT_TYPE, &ptrtmp,
7992 &ptrtmp, &ptrtmp)) != NO_TYPE)
7993 e = (ElementType *) ptrtmp;
7994 else
7996 Message (_("No element found there\n"));
7997 return 1;
8001 if (NAMEONPCB_NAME(e))
8003 buf = (char *) malloc (strlen (NAMEONPCB_NAME(e)) +
8004 strlen (_("Element %s Attributes")));
8005 sprintf(buf, _("Element %s Attributes"), NAMEONPCB_NAME(e));
8007 else
8009 buf = strdup (_("Unnamed Element Attributes"));
8011 gui->edit_attributes(buf, &(e->Attributes));
8012 free (buf);
8013 break;
8016 default:
8017 AFAIL (attributes);
8020 return 0;
8023 /* --------------------------------------------------------------------------- */
8025 HID_Action action_action_list[] = {
8026 {"AddRats", 0, ActionAddRats,
8027 addrats_help, addrats_syntax}
8029 {"Attributes", 0, ActionAttributes,
8030 attributes_help, attributes_syntax}
8032 {"Atomic", 0, ActionAtomic,
8033 atomic_help, atomic_syntax}
8035 {"AutoPlaceSelected", 0, ActionAutoPlaceSelected,
8036 autoplace_help, autoplace_syntax}
8038 {"AutoRoute", 0, ActionAutoRoute,
8039 autoroute_help, autoroute_syntax}
8041 {"ChangeClearSize", 0, ActionChangeClearSize,
8042 changeclearsize_help, changeclearsize_syntax}
8044 {"ChangeDrillSize", 0, ActionChange2ndSize,
8045 changedrillsize_help, changedrillsize_syntax}
8047 {"ChangeHole", 0, ActionChangeHole,
8048 changehold_help, changehold_syntax}
8050 {"ChangeJoin", 0, ActionChangeJoin,
8051 changejoin_help, changejoin_syntax}
8053 {"ChangeName", 0, ActionChangeName,
8054 changename_help, changename_syntax}
8056 {"ChangePaste", 0, ActionChangePaste,
8057 changepaste_help, changepaste_syntax}
8059 {"ChangePinName", 0, ActionChangePinName,
8060 changepinname_help, changepinname_syntax}
8062 {"ChangeSize", 0, ActionChangeSize,
8063 changesize_help, changesize_syntax}
8065 {"ChangeSquare", 0, ActionChangeSquare,
8066 changesquare_help, changesquare_syntax}
8068 {"ChangeOctagon", 0, ActionChangeOctagon,
8069 changeoctagon_help, changeoctagon_syntax}
8071 {"ClearSquare", 0, ActionClearSquare,
8072 clearsquare_help, clearsquare_syntax}
8074 {"ClearOctagon", 0, ActionClearOctagon,
8075 clearoctagon_help, clearoctagon_syntax}
8077 {"Connection", 0, ActionConnection,
8078 connection_help, connection_syntax}
8080 {"Delete", 0, ActionDelete,
8081 delete_help, delete_syntax}
8083 {"DeleteRats", 0, ActionDeleteRats,
8084 deleterats_help, deleterats_syntax}
8086 {"DisperseElements", 0, ActionDisperseElements,
8087 disperseelements_help, disperseelements_syntax}
8089 {"Display", 0, ActionDisplay,
8090 display_help, display_syntax}
8092 {"DRC", 0, ActionDRCheck,
8093 drc_help, drc_syntax}
8095 {"DumpLibrary", 0, ActionDumpLibrary,
8096 dumplibrary_help, dumplibrary_syntax}
8098 {"ExecuteFile", 0, ActionExecuteFile,
8099 executefile_help, executefile_syntax}
8101 {"Flip", N_("Click on Object or Flip Point"), ActionFlip,
8102 flip_help, flip_syntax}
8104 {"LoadFrom", 0, ActionLoadFrom,
8105 loadfrom_help, loadfrom_syntax}
8107 {"MarkCrosshair", 0, ActionMarkCrosshair,
8108 markcrosshair_help, markcrosshair_syntax}
8110 {"Message", 0, ActionMessage,
8111 message_help, message_syntax}
8113 {"MinMaskGap", 0, ActionMinMaskGap,
8114 minmaskgap_help, minmaskgap_syntax}
8116 {"MinClearGap", 0, ActionMinClearGap,
8117 mincleargap_help, mincleargap_syntax}
8119 {"Mode", 0, ActionMode,
8120 mode_help, mode_syntax}
8122 {"MorphPolygon", 0, ActionMorphPolygon,
8123 morphpolygon_help, morphpolygon_syntax}
8125 {"PasteBuffer", 0, ActionPasteBuffer,
8126 pastebuffer_help, pastebuffer_syntax}
8128 {"Quit", 0, ActionQuit,
8129 quit_help, quit_syntax}
8131 {"RemoveSelected", 0, ActionRemoveSelected,
8132 removeselected_help, removeselected_syntax}
8134 {"Renumber", 0, ActionRenumber,
8135 renumber_help, renumber_syntax}
8137 {"RipUp", 0, ActionRipUp,
8138 ripup_help, ripup_syntax}
8140 {"Select", 0, ActionSelect,
8141 select_help, select_syntax}
8143 {"Unselect", 0, ActionUnselect,
8144 unselect_help, unselect_syntax}
8146 {"SaveSettings", 0, ActionSaveSettings,
8147 savesettings_help, savesettings_syntax}
8149 {"SaveTo", 0, ActionSaveTo,
8150 saveto_help, saveto_syntax}
8152 {"SetSquare", 0, ActionSetSquare,
8153 setsquare_help, setsquare_syntax}
8155 {"SetOctagon", 0, ActionSetOctagon,
8156 setoctagon_help, setoctagon_syntax}
8158 {"SetThermal", 0, ActionSetThermal,
8159 setthermal_help, setthermal_syntax}
8161 {"SetValue", 0, ActionSetValue,
8162 setvalue_help, setvalue_syntax}
8164 {"ToggleHideName", 0, ActionToggleHideName,
8165 togglehidename_help, togglehidename_syntax}
8167 {"Undo", 0, ActionUndo,
8168 undo_help, undo_syntax}
8170 {"Redo", 0, ActionRedo,
8171 redo_help, redo_syntax}
8173 {"SetSame", N_("Select item to use attributes from"), ActionSetSame,
8174 setsame_help, setsame_syntax}
8176 {"SetFlag", 0, ActionSetFlag,
8177 setflag_help, setflag_syntax}
8179 {"ClrFlag", 0, ActionClrFlag,
8180 clrflag_help, clrflag_syntax}
8182 {"ChangeFlag", 0, ActionChangeFlag,
8183 changeflag_help, changeflag_syntax}
8185 {"Polygon", 0, ActionPolygon,
8186 polygon_help, polygon_syntax}
8188 {"RouteStyle", 0, ActionRouteStyle,
8189 routestyle_help, routestyle_syntax}
8191 {"MoveObject", N_("Select an Object"), ActionMoveObject,
8192 moveobject_help, moveobject_syntax}
8194 {"MoveToCurrentLayer", 0, ActionMoveToCurrentLayer,
8195 movetocurrentlayer_help, movetocurrentlayer_syntax}
8197 {"New", 0, ActionNew,
8198 new_help, new_syntax}
8200 {"pscalib", 0, ActionPSCalib}
8202 {"ElementList", 0, ActionElementList,
8203 elementlist_help, elementlist_syntax}
8205 {"ElementSetAttr", 0, ActionElementSetAttr,
8206 elementsetattr_help, elementsetattr_syntax}
8208 {"ExecCommand", 0, ActionExecCommand,
8209 execcommand_help, execcommand_syntax}
8211 {"Import", 0, ActionImport,
8212 import_help, import_syntax}
8216 REGISTER_ACTIONS (action_action_list)