Fixed the "poly twin hole" bug.
[geda-pcb/pcjc2.git] / src / action.c
blob87a0d31bf3cd9f832f9dad7b05217ce51cc14433
1 /*!
2 * \file src/action.c
4 * \brief Action routines for output window.
6 * <hr>
8 * <h1><b>Copyright.</b></h1>\n
10 * PCB, interactive printed circuit board design
12 * Copyright (C) 1994,1995,1996 Thomas Nau
14 * Copyright (C) 1997, 1998, 1999, 2000, 2001 Harry Eaton
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
30 * Contact addresses for paper mail and Email:
31 * Harry Eaton, 6697 Buttonhole Ct, Columbia, MD 21044, USA
32 * haceaton@aplcomm.jhuapl.edu
35 #ifdef HAVE_CONFIG_H
36 #include "config.h"
37 #endif
39 #include "global.h"
41 #include "action.h"
42 #include "autoplace.h"
43 #include "autoroute.h"
44 #include "buffer.h"
45 #include "change.h"
46 #include "copy.h"
47 #include "create.h"
48 #include "crosshair.h"
49 #include "data.h"
50 #include "draw.h"
51 #include "error.h"
52 #include "file.h"
53 #include "find.h"
54 #include "hid.h"
55 #include "insert.h"
56 #include "line.h"
57 #include "mymem.h"
58 #include "misc.h"
59 #include "mirror.h"
60 #include "move.h"
61 #include "polygon.h"
62 /*#include "print.h"*/
63 #include "rats.h"
64 #include "remove.h"
65 #include "report.h"
66 #include "rotate.h"
67 #include "rubberband.h"
68 #include "search.h"
69 #include "select.h"
70 #include "set.h"
71 #include "thermal.h"
72 #include "undo.h"
73 #include "rtree.h"
74 #include "macro.h"
75 #include "pcb-printf.h"
77 #include <assert.h>
78 #include <stdlib.h> /* rand() */
80 #ifdef HAVE_LIBDMALLOC
81 #include <dmalloc.h>
82 #endif
84 /* for fork() and friends */
85 #ifdef HAVE_UNISTD_H
86 #include <unistd.h>
87 #endif
89 #ifdef HAVE_SYS_WAIT_H
90 #include <sys/wait.h>
91 #endif
93 /* ---------------------------------------------------------------------------
94 * some local types
96 typedef enum
98 F_AddSelected,
99 F_All,
100 F_AllConnections,
101 F_AllRats,
102 F_AllUnusedPins,
103 F_Arc,
104 F_Arrow,
105 F_Block,
106 F_Description,
107 F_Cancel,
108 F_Center,
109 F_Clear,
110 F_ClearAndRedraw,
111 F_ClearList,
112 F_Close,
113 F_Found,
114 F_Connection,
115 F_Convert,
116 F_Copy,
117 F_CycleClip,
118 F_CycleCrosshair,
119 F_DeleteRats,
120 F_Drag,
121 F_DrillReport,
122 F_Element,
123 F_ElementByName,
124 F_ElementConnections,
125 F_ElementToBuffer,
126 F_Escape,
127 F_Find,
128 F_FlipElement,
129 F_FoundPins,
130 F_Grid,
131 F_InsertPoint,
132 F_Layer,
133 F_Layout,
134 F_LayoutAs,
135 F_LayoutToBuffer,
136 F_Line,
137 F_LineSize,
138 F_Lock,
139 F_Mirror,
140 F_Move,
141 F_NameOnPCB,
142 F_Netlist,
143 F_NetByName,
144 F_None,
145 F_Notify,
146 F_Object,
147 F_ObjectByName,
148 F_PasteBuffer,
149 F_PadByName,
150 F_PinByName,
151 F_PinOrPadName,
152 F_Pinout,
153 F_Polygon,
154 F_PolygonHole,
155 F_PreviousPoint,
156 F_RatsNest,
157 F_Rectangle,
158 F_Redraw,
159 F_Release,
160 F_Revert,
161 F_Remove,
162 F_RemoveSelected,
163 F_Report,
164 F_Reset,
165 F_ResetLinesAndPolygons,
166 F_ResetPinsViasAndPads,
167 F_Restore,
168 F_Rotate,
169 F_Save,
170 F_Selected,
171 F_SelectedArcs,
172 F_SelectedElements,
173 F_SelectedLines,
174 F_SelectedNames,
175 F_SelectedObjects,
176 F_SelectedPads,
177 F_SelectedPins,
178 F_SelectedTexts,
179 F_SelectedVias,
180 F_SelectedRats,
181 F_Stroke,
182 F_Text,
183 F_TextByName,
184 F_TextScale,
185 F_Thermal,
186 F_ToLayout,
187 F_ToggleAllDirections,
188 F_ToggleAutoDRC,
189 F_ToggleClearLine,
190 F_ToggleFullPoly,
191 F_ToggleGrid,
192 F_ToggleHideNames,
193 F_ToggleMask,
194 F_ToggleName,
195 F_ToggleObject,
196 F_ToggleShowDRC,
197 F_ToggleLiveRoute,
198 F_ToggleRubberBandMode,
199 F_ToggleStartDirection,
200 F_ToggleSnapPin,
201 F_ToggleThindraw,
202 F_ToggleLockNames,
203 F_ToggleOnlyNames,
204 F_ToggleThindrawPoly,
205 F_ToggleOrthoMove,
206 F_ToggleLocalRef,
207 F_ToggleCheckPlanes,
208 F_ToggleUniqueNames,
209 F_Via,
210 F_ViaByName,
211 F_Value,
212 F_ViaDrillingHole,
213 F_ViaSize,
214 F_Zoom
216 FunctionID;
218 typedef struct /* used to identify subfunctions */
220 char *Identifier;
221 FunctionID ID;
223 FunctionType;
225 /* --------------------------------------------------------------------------- */
227 /* %start-doc actions 00delta
229 Many actions take a @code{delta} parameter as the last parameter,
230 which is an amount to change something. That @code{delta} may include
231 units, as an additional parameter, such as @code{Action(Object,5,mm)}.
232 If no units are specified, the default is PCB's native units
233 (currently 1/100 mil). Also, if the delta is prefixed by @code{+} or
234 @code{-}, the size is increased or decreased by that amount.
235 Otherwise, the size size is set to the given amount.
237 @example
238 Action(Object,5,mil)
239 Action(Object,+0.5,mm)
240 Action(Object,-1)
241 @end example
243 Actions which take a @code{delta} parameter which do not accept all
244 these options will specify what they do take.
246 %end-doc */
248 /* %start-doc actions 00objects
250 Many actions act on indicated objects on the board. They will have
251 parameters like @code{ToggleObject} or @code{SelectedVias} to indicate
252 what group of objects they act on. Unless otherwise specified, these
253 parameters are defined as follows:
255 @table @code
257 @item Object
258 @itemx ToggleObject
259 Affects the object under the mouse pointer. If this action is invoked
260 from a menu or script, the user will be prompted to click on an
261 object, which is then the object affected.
263 @item Selected
264 @itemx SelectedObjects
266 Affects all objects which are currently selected. At least, all
267 selected objects for which the given action makes sense.
269 @item SelectedPins
270 @itemx SelectedVias
271 @itemx Selected@var{Type}
272 @itemx @i{etc}
273 Affects all objects which are both selected and of the @var{Type} specified.
275 @end table
277 %end-doc */
279 /* %start-doc actions 00macros
281 @macro pinshapes
283 Pins, pads, and vias can have various shapes. All may be round. Pins
284 and pads may be square (obviously "square" pads are usually
285 rectangular). Pins and vias may be octagonal. When you change a
286 shape flag of an element, you actually change all of its pins and
287 pads.
289 Note that the square flag takes precedence over the octagon flag,
290 thus, if both the square and octagon flags are set, the object is
291 square. When the square flag is cleared, the pins and pads will be
292 either round or, if the octagon flag is set, octagonal.
294 @end macro
296 %end-doc */
298 /* ---------------------------------------------------------------------------
299 * some local identifiers
301 static PointType InsertedPoint;
302 static LayerType *lastLayer;
303 static struct
305 PolygonType *poly;
306 LineType line;
308 fake;
310 static struct
312 Coord X, Y;
313 Cardinal Buffer;
314 bool Click;
315 bool Moving; /* selected type clicked on */
316 int Hit; /* move type clicked on */
317 void *ptr1;
318 void *ptr2;
319 void *ptr3;
321 Note;
323 static int defer_updates = 0;
324 static int defer_needs_update = 0;
326 static Cardinal polyIndex = 0;
327 static bool saved_mode = false;
328 #ifdef HAVE_LIBSTROKE
329 static bool mid_stroke = false;
330 static BoxType StrokeBox;
331 #endif
332 static FunctionType Functions[] = {
333 {"AddSelected", F_AddSelected},
334 {"All", F_All},
335 {"AllConnections", F_AllConnections},
336 {"AllRats", F_AllRats},
337 {"AllUnusedPins", F_AllUnusedPins},
338 {"Arc", F_Arc},
339 {"Arrow", F_Arrow},
340 {"Block", F_Block},
341 {"Description", F_Description},
342 {"Cancel", F_Cancel},
343 {"Center", F_Center},
344 {"Clear", F_Clear},
345 {"ClearAndRedraw", F_ClearAndRedraw},
346 {"ClearList", F_ClearList},
347 {"Close", F_Close},
348 {"Found", F_Found},
349 {"Connection", F_Connection},
350 {"Convert", F_Convert},
351 {"Copy", F_Copy},
352 {"CycleClip", F_CycleClip},
353 {"CycleCrosshair", F_CycleCrosshair},
354 {"DeleteRats", F_DeleteRats},
355 {"Drag", F_Drag},
356 {"DrillReport", F_DrillReport},
357 {"Element", F_Element},
358 {"ElementByName", F_ElementByName},
359 {"ElementConnections", F_ElementConnections},
360 {"ElementToBuffer", F_ElementToBuffer},
361 {"Escape", F_Escape},
362 {"Find", F_Find},
363 {"FlipElement", F_FlipElement},
364 {"FoundPins", F_FoundPins},
365 {"Grid", F_Grid},
366 {"InsertPoint", F_InsertPoint},
367 {"Layer", F_Layer},
368 {"Layout", F_Layout},
369 {"LayoutAs", F_LayoutAs},
370 {"LayoutToBuffer", F_LayoutToBuffer},
371 {"Line", F_Line},
372 {"LineSize", F_LineSize},
373 {"Lock", F_Lock},
374 {"Mirror", F_Mirror},
375 {"Move", F_Move},
376 {"NameOnPCB", F_NameOnPCB},
377 {"Netlist", F_Netlist},
378 {"NetByName", F_NetByName},
379 {"None", F_None},
380 {"Notify", F_Notify},
381 {"Object", F_Object},
382 {"ObjectByName", F_ObjectByName},
383 {"PasteBuffer", F_PasteBuffer},
384 {"PadByName", F_PadByName},
385 {"PinByName", F_PinByName},
386 {"PinOrPadName", F_PinOrPadName},
387 {"Pinout", F_Pinout},
388 {"Polygon", F_Polygon},
389 {"PolygonHole", F_PolygonHole},
390 {"PreviousPoint", F_PreviousPoint},
391 {"RatsNest", F_RatsNest},
392 {"Rectangle", F_Rectangle},
393 {"Redraw", F_Redraw},
394 {"Release", F_Release},
395 {"Remove", F_Remove},
396 {"RemoveSelected", F_RemoveSelected},
397 {"Report", F_Report},
398 {"Reset", F_Reset},
399 {"ResetLinesAndPolygons", F_ResetLinesAndPolygons},
400 {"ResetPinsViasAndPads", F_ResetPinsViasAndPads},
401 {"Restore", F_Restore},
402 {"Revert", F_Revert},
403 {"Rotate", F_Rotate},
404 {"Save", F_Save},
405 {"Selected", F_Selected},
406 {"SelectedArcs", F_SelectedArcs},
407 {"SelectedElements", F_SelectedElements},
408 {"SelectedLines", F_SelectedLines},
409 {"SelectedNames", F_SelectedNames},
410 {"SelectedObjects", F_SelectedObjects},
411 {"SelectedPins", F_SelectedPins},
412 {"SelectedPads", F_SelectedPads},
413 {"SelectedRats", F_SelectedRats},
414 {"SelectedTexts", F_SelectedTexts},
415 {"SelectedVias", F_SelectedVias},
416 {"Stroke", F_Stroke},
417 {"Text", F_Text},
418 {"TextByName", F_TextByName},
419 {"TextScale", F_TextScale},
420 {"Thermal", F_Thermal},
421 {"ToLayout", F_ToLayout},
422 {"Toggle45Degree", F_ToggleAllDirections},
423 {"ToggleClearLine", F_ToggleClearLine},
424 {"ToggleFullPoly", F_ToggleFullPoly},
425 {"ToggleGrid", F_ToggleGrid},
426 {"ToggleMask", F_ToggleMask},
427 {"ToggleName", F_ToggleName},
428 {"ToggleObject", F_ToggleObject},
429 {"ToggleRubberBandMode", F_ToggleRubberBandMode},
430 {"ToggleStartDirection", F_ToggleStartDirection},
431 {"ToggleSnapPin", F_ToggleSnapPin},
432 {"ToggleThindraw", F_ToggleThindraw},
433 {"ToggleThindrawPoly", F_ToggleThindrawPoly},
434 {"ToggleLockNames", F_ToggleLockNames},
435 {"ToggleOnlyNames", F_ToggleOnlyNames},
436 {"ToggleHideNames", F_ToggleHideNames},
437 {"ToggleCheckPlanes", F_ToggleCheckPlanes},
438 {"ToggleLocalRef", F_ToggleLocalRef},
439 {"ToggleOrthoMove", F_ToggleOrthoMove},
440 {"ToggleShowDRC", F_ToggleShowDRC},
441 {"ToggleLiveRoute", F_ToggleLiveRoute},
442 {"ToggleAutoDRC", F_ToggleAutoDRC},
443 {"ToggleUniqueNames", F_ToggleUniqueNames},
444 {"Value", F_Value},
445 {"Via", F_Via},
446 {"ViaByName", F_ViaByName},
447 {"ViaSize", F_ViaSize},
448 {"ViaDrillingHole", F_ViaDrillingHole},
449 {"Zoom", F_Zoom}
452 /* ---------------------------------------------------------------------------
453 * some local routines
455 static int GetFunctionID (String);
456 static void AdjustAttachedBox (void);
457 static void NotifyLine (void);
458 static void NotifyBlock (void);
459 static void NotifyMode (void);
460 static void ClearWarnings (void);
461 #ifdef HAVE_LIBSTROKE
462 static void FinishStroke (void);
463 extern void stroke_init (void);
464 extern void stroke_record (int x, int y);
465 extern int stroke_trans (char *s);
466 #endif
467 static void ChangeFlag (char *, char *, int, char *);
469 #define ARG(n) (argc > (n) ? argv[n] : NULL)
471 #ifdef HAVE_LIBSTROKE
474 * \brief Try to recognize the stroke sent.
476 void
477 FinishStroke (void)
479 char msg[255];
480 int type;
481 unsigned long num;
482 void *ptr1, *ptr2, *ptr3;
484 mid_stroke = false;
485 if (stroke_trans (msg))
487 num = atoi (msg);
488 switch (num)
490 case 456:
491 if (Settings.Mode == LINE_MODE)
493 SetMode (LINE_MODE);
495 break;
496 case 9874123:
497 case 74123:
498 case 987412:
499 case 8741236:
500 case 874123:
501 RotateScreenObject (StrokeBox.X1, StrokeBox.Y1, SWAP_IDENT ? 1 : 3);
502 break;
503 case 7896321:
504 case 786321:
505 case 789632:
506 case 896321:
507 RotateScreenObject (StrokeBox.X1, StrokeBox.Y1, SWAP_IDENT ? 3 : 1);
508 break;
509 case 258:
510 SetMode (LINE_MODE);
511 break;
512 case 852:
513 SetMode (ARROW_MODE);
514 break;
515 case 1478963:
516 ActionUndo ("");
517 break;
518 case 147423:
519 case 147523:
520 case 1474123:
521 Redo (true);
522 break;
523 case 148963:
524 case 147863:
525 case 147853:
526 case 145863:
527 SetMode (VIA_MODE);
528 break;
529 case 951:
530 case 9651:
531 case 9521:
532 case 9621:
533 case 9851:
534 case 9541:
535 case 96521:
536 case 96541:
537 case 98541:
538 /* XXX: FIXME: Call a zoom-extents action */
539 break;
540 case 159:
541 case 1269:
542 case 1259:
543 case 1459:
544 case 1569:
545 case 1589:
546 case 12569:
547 case 12589:
548 case 14589:
549 /* XXX: FIXME: Zoom to fit the box StrokeBox.[X1,Y1] - StrokeBox.[X2,Y2] */
550 break;
552 default:
553 Message (_("Unknown stroke %s\n"), msg);
554 break;
557 else
558 gui->beep ();
560 #endif
563 * \brief Clear warning color from pins/pads.
565 static void
566 ClearWarnings ()
568 Settings.RatWarn = false;
569 ALLPIN_LOOP (PCB->Data);
571 if (TEST_FLAG (WARNFLAG, pin))
573 CLEAR_FLAG (WARNFLAG, pin);
574 DrawPin (pin);
577 ENDALL_LOOP;
578 ALLPAD_LOOP (PCB->Data);
580 if (TEST_FLAG (WARNFLAG, pad))
582 CLEAR_FLAG (WARNFLAG, pad);
583 DrawPad (pad);
586 ENDALL_LOOP;
587 Draw ();
591 * \brief Click callback.
593 * This is called a clicktime after a mouse down, to we can distinguish
594 * between short clicks (typically: select or create something) and long
595 * clicks. Long clicks typically drag something.
597 static void
598 click_cb (hidval hv)
600 if (Note.Click)
602 notify_crosshair_change (false);
603 Note.Click = false;
604 if (Note.Moving && !gui->shift_is_pressed ())
606 Note.Buffer = Settings.BufferNumber;
607 SetBufferNumber (MAX_BUFFER - 1);
608 ClearBuffer (PASTEBUFFER);
609 AddSelectedToBuffer (PASTEBUFFER, Note.X, Note.Y, true);
610 SaveUndoSerialNumber ();
611 RemoveSelected ();
612 SaveMode ();
613 saved_mode = true;
614 SetMode (PASTEBUFFER_MODE);
616 else if (Note.Hit && !gui->shift_is_pressed ())
618 SaveMode ();
619 saved_mode = true;
620 SetMode (gui->control_is_pressed ()? COPY_MODE : MOVE_MODE);
621 Crosshair.AttachedObject.Ptr1 = Note.ptr1;
622 Crosshair.AttachedObject.Ptr2 = Note.ptr2;
623 Crosshair.AttachedObject.Ptr3 = Note.ptr3;
624 Crosshair.AttachedObject.Type = Note.Hit;
625 AttachForCopy (Note.X, Note.Y);
627 else
629 BoxType box;
631 Note.Hit = 0;
632 Note.Moving = false;
633 SaveUndoSerialNumber ();
634 box.X1 = -MAX_COORD;
635 box.Y1 = -MAX_COORD;
636 box.X2 = MAX_COORD;
637 box.Y2 = MAX_COORD;
638 /* unselect first if shift key not down */
639 if (!gui->shift_is_pressed () && SelectBlock (&box, false))
640 SetChangedFlag (true);
641 NotifyBlock ();
642 Crosshair.AttachedBox.Point1.X = Note.X;
643 Crosshair.AttachedBox.Point1.Y = Note.Y;
645 notify_crosshair_change (true);
650 * \brief This is typically called when the mouse has moved or the mouse
651 * button was released.
653 static void
654 ReleaseMode (void)
656 BoxType box;
658 if (Note.Click)
660 BoxType box;
662 box.X1 = -MAX_COORD;
663 box.Y1 = -MAX_COORD;
664 box.X2 = MAX_COORD;
665 box.Y2 = MAX_COORD;
667 Note.Click = false; /* inhibit timer action */
668 SaveUndoSerialNumber ();
669 /* unselect first if shift key not down */
670 if (!gui->shift_is_pressed ())
672 if (SelectBlock (&box, false))
673 SetChangedFlag (true);
674 if (Note.Moving)
676 Note.Moving = 0;
677 Note.Hit = 0;
678 return;
681 /* Restore the SN so that if we select something the deselect/select combo
682 gets the same SN. */
683 RestoreUndoSerialNumber();
684 if (SelectObject ())
685 SetChangedFlag (true);
686 else
687 /* We didn't select anything new, so, the deselection should get its
688 own SN. */
689 IncrementUndoSerialNumber();
690 Note.Hit = 0;
691 Note.Moving = 0;
693 else if (Note.Moving)
695 RestoreUndoSerialNumber ();
696 NotifyMode ();
697 ClearBuffer (PASTEBUFFER);
698 SetBufferNumber (Note.Buffer);
699 Note.Moving = false;
700 Note.Hit = 0;
702 else if (Note.Hit)
704 NotifyMode ();
705 Note.Hit = 0;
707 else if (Settings.Mode == ARROW_MODE)
709 box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
710 Crosshair.AttachedBox.Point2.X);
711 box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
712 Crosshair.AttachedBox.Point2.Y);
713 box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
714 Crosshair.AttachedBox.Point2.X);
715 box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
716 Crosshair.AttachedBox.Point2.Y);
717 RestoreUndoSerialNumber ();
718 if (SelectBlock (&box, true))
719 SetChangedFlag (true);
720 else if (Bumped)
721 IncrementUndoSerialNumber ();
722 Crosshair.AttachedBox.State = STATE_FIRST;
724 if (saved_mode)
725 RestoreMode ();
726 saved_mode = false;
729 #define HSIZE 257
730 static char function_hash[HSIZE];
731 static int hash_initted = 0;
733 static int
734 hashfunc(String s)
736 int i = 0;
737 while (*s)
739 i ^= i >> 16;
740 i = (i * 13) ^ (unsigned char)tolower((int) *s);
741 s ++;
743 i = (unsigned int)i % HSIZE;
744 return i;
748 * \brief Get function ID of passed string.
750 static int
751 GetFunctionID (String Ident)
753 int i, h;
755 if (Ident == 0)
756 return -1;
758 if (!hash_initted)
760 hash_initted = 1;
761 if (HSIZE < ENTRIES (Functions) * 2)
763 fprintf(stderr, _("Error: function hash size too small (%d vs %lu at %s:%d)\n"),
764 HSIZE, (unsigned long) ENTRIES (Functions)*2, __FILE__, __LINE__);
765 exit(1);
767 if (ENTRIES (Functions) > 254)
769 /* Change 'char' to 'int' and remove this when we get to 256
770 strings to hash. */
771 fprintf(stderr, _("Error: function hash type too small (%d vs %lu at %s:%d)\n"),
772 256, (unsigned long) ENTRIES (Functions), __FILE__, __LINE__);
773 exit(1);
776 for (i=ENTRIES (Functions)-1; i>=0; i--)
778 h = hashfunc (Functions[i].Identifier);
779 while (function_hash[h])
780 h = (h + 1) % HSIZE;
781 function_hash[h] = i + 1;
785 i = hashfunc (Ident);
786 while (1)
788 /* We enforce the "hash table bigger than function table" rule,
789 so we know there will be at least one zero entry to find. */
790 if (!function_hash[i])
791 return (-1);
792 if (!strcasecmp (Ident, Functions[function_hash[i]-1].Identifier))
793 return ((int) Functions[function_hash[i]-1].ID);
794 i = (i + 1) % HSIZE;
799 * \brief Set new coordinates if in 'RECTANGLE' mode.
801 * The cursor shape is also adjusted.
803 static void
804 AdjustAttachedBox (void)
806 if (Settings.Mode == ARC_MODE)
808 Crosshair.AttachedBox.otherway = gui->shift_is_pressed ();
809 return;
811 switch (Crosshair.AttachedBox.State)
813 case STATE_SECOND: /* one corner is selected */
815 /* update coordinates */
816 Crosshair.AttachedBox.Point2.X = Crosshair.X;
817 Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
818 break;
824 * \brief Adjusts the objects which are to be created like attached
825 * lines.
827 void
828 AdjustAttachedObjects (void)
830 PointType *pnt;
831 switch (Settings.Mode)
833 /* update at least an attached block (selection) */
834 case NO_MODE:
835 case ARROW_MODE:
836 if (Crosshair.AttachedBox.State)
838 Crosshair.AttachedBox.Point2.X = Crosshair.X;
839 Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
841 break;
843 /* rectangle creation mode */
844 case RECTANGLE_MODE:
845 case ARC_MODE:
846 AdjustAttachedBox ();
847 break;
849 /* polygon creation mode */
850 case POLYGON_MODE:
851 case POLYGONHOLE_MODE:
852 AdjustAttachedLine ();
853 break;
854 /* line creation mode */
855 case LINE_MODE:
856 if (PCB->RatDraw || PCB->Clipping == 0)
857 AdjustAttachedLine ();
858 else
859 AdjustTwoLine (PCB->Clipping - 1);
860 break;
861 /* point insertion mode */
862 case INSERTPOINT_MODE:
863 pnt = AdjustInsertPoint ();
864 if (pnt)
865 InsertedPoint = *pnt;
866 break;
867 case ROTATE_MODE:
868 break;
873 * \brief Creates points of a line.
875 static void
876 NotifyLine (void)
878 int type = NO_TYPE;
879 void *ptr1, *ptr2, *ptr3;
881 if (!Marked.status || TEST_FLAG (LOCALREFFLAG, PCB))
882 SetLocalRef (Crosshair.X, Crosshair.Y, true);
883 switch (Crosshair.AttachedLine.State)
885 case STATE_FIRST: /* first point */
886 if (PCB->RatDraw && SearchScreen (Crosshair.X, Crosshair.Y,
887 PAD_TYPE | PIN_TYPE, &ptr1, &ptr1,
888 &ptr1) == NO_TYPE)
890 gui->beep ();
891 break;
893 if (TEST_FLAG (AUTODRCFLAG, PCB) && Settings.Mode == LINE_MODE)
895 type = SearchScreen (Crosshair.X, Crosshair.Y,
896 PIN_TYPE | PAD_TYPE | VIA_TYPE, &ptr1, &ptr2,
897 &ptr3);
898 LookupConnection (Crosshair.X, Crosshair.Y, true, 1, CONNECTEDFLAG, false);
899 LookupConnection (Crosshair.X, Crosshair.Y, true, 1, FOUNDFLAG, true);
901 if (type == PIN_TYPE || type == VIA_TYPE)
903 Crosshair.AttachedLine.Point1.X =
904 Crosshair.AttachedLine.Point2.X = ((PinType *) ptr2)->X;
905 Crosshair.AttachedLine.Point1.Y =
906 Crosshair.AttachedLine.Point2.Y = ((PinType *) ptr2)->Y;
908 else if (type == PAD_TYPE)
910 PadType *pad = (PadType *) ptr2;
911 double d1 = Distance (Crosshair.X, Crosshair.Y, pad->Point1.X, pad->Point1.Y);
912 double d2 = Distance (Crosshair.X, Crosshair.Y, pad->Point2.X, pad->Point2.Y);
913 if (d2 < d1)
915 Crosshair.AttachedLine.Point1 =
916 Crosshair.AttachedLine.Point2 = pad->Point2;
918 else
920 Crosshair.AttachedLine.Point1 =
921 Crosshair.AttachedLine.Point2 = pad->Point1;
924 else
926 Crosshair.AttachedLine.Point1.X =
927 Crosshair.AttachedLine.Point2.X = Crosshair.X;
928 Crosshair.AttachedLine.Point1.Y =
929 Crosshair.AttachedLine.Point2.Y = Crosshair.Y;
931 Crosshair.AttachedLine.State = STATE_SECOND;
932 break;
934 case STATE_SECOND:
935 /* fall through to third state too */
936 lastLayer = CURRENT;
937 default: /* all following points */
938 Crosshair.AttachedLine.State = STATE_THIRD;
939 break;
944 * \brief Create first or second corner of a marked block.
946 static void
947 NotifyBlock (void)
949 notify_crosshair_change (false);
950 switch (Crosshair.AttachedBox.State)
952 case STATE_FIRST: /* setup first point */
953 Crosshair.AttachedBox.Point1.X =
954 Crosshair.AttachedBox.Point2.X = Crosshair.X;
955 Crosshair.AttachedBox.Point1.Y =
956 Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
957 Crosshair.AttachedBox.State = STATE_SECOND;
958 break;
960 case STATE_SECOND: /* setup second point */
961 Crosshair.AttachedBox.State = STATE_THIRD;
962 break;
964 notify_crosshair_change (true);
969 * \brief This is called after every mode change, like mouse button pressed,
970 * mouse button released, dragging something started or a different tool
971 * selected.
973 * It does what's appropriate for the current mode setting.
974 * This can also mean creation of an object at the current crosshair location.
976 * New created objects are added to the create undo list of course.
978 static void
979 NotifyMode (void)
981 void *ptr1, *ptr2, *ptr3;
982 int type;
984 if (Settings.RatWarn)
985 ClearWarnings ();
986 switch (Settings.Mode)
988 case ARROW_MODE:
990 int test;
991 hidval hv;
993 Note.Click = true;
994 /* do something after click time */
995 gui->add_timer (click_cb, CLICK_TIME, hv);
997 /* see if we clicked on something already selected
998 * (Note.Moving) or clicked on a MOVE_TYPE
999 * (Note.Hit)
1001 for (test = (SELECT_TYPES | MOVE_TYPES) & ~RATLINE_TYPE;
1002 test; test &= ~type)
1004 type = SearchScreen (Note.X, Note.Y, test, &ptr1, &ptr2, &ptr3);
1005 if (!Note.Hit && (type & MOVE_TYPES) &&
1006 !TEST_FLAG (LOCKFLAG, (PinType *) ptr2))
1008 Note.Hit = type;
1009 Note.ptr1 = ptr1;
1010 Note.ptr2 = ptr2;
1011 Note.ptr3 = ptr3;
1013 if (!Note.Moving && (type & SELECT_TYPES) &&
1014 TEST_FLAG (SELECTEDFLAG, (PinType *) ptr2))
1015 Note.Moving = true;
1016 if ((Note.Hit && Note.Moving) || type == NO_TYPE)
1017 break;
1019 break;
1022 case VIA_MODE:
1024 PinType *via;
1026 if (!PCB->ViaOn)
1028 Message (_("You must turn via visibility on before\n"
1029 "you can place vias\n"));
1030 break;
1032 if ((via = CreateNewVia (PCB->Data, Note.X, Note.Y,
1033 Settings.ViaThickness, 2 * Settings.Keepaway,
1034 0, Settings.ViaDrillingHole, NULL,
1035 NoFlags ())) != NULL)
1037 AddObjectToCreateUndoList (VIA_TYPE, via, via, via);
1038 if (gui->shift_is_pressed ())
1039 ChangeObjectThermal (VIA_TYPE, via, via, via, PCB->ThermStyle);
1040 IncrementUndoSerialNumber ();
1041 DrawVia (via);
1042 Draw ();
1044 break;
1047 case ARC_MODE:
1049 switch (Crosshair.AttachedBox.State)
1051 case STATE_FIRST:
1052 Crosshair.AttachedBox.Point1.X =
1053 Crosshair.AttachedBox.Point2.X = Note.X;
1054 Crosshair.AttachedBox.Point1.Y =
1055 Crosshair.AttachedBox.Point2.Y = Note.Y;
1056 Crosshair.AttachedBox.State = STATE_SECOND;
1057 break;
1059 case STATE_SECOND:
1060 case STATE_THIRD:
1062 ArcType *arc;
1063 Coord wx, wy;
1064 Angle sa, dir;
1066 wx = Note.X - Crosshair.AttachedBox.Point1.X;
1067 wy = Note.Y - Crosshair.AttachedBox.Point1.Y;
1068 if (XOR (Crosshair.AttachedBox.otherway, abs (wy) > abs (wx)))
1070 Crosshair.AttachedBox.Point2.X =
1071 Crosshair.AttachedBox.Point1.X + abs (wy) * SGNZ (wx);
1072 sa = (wx >= 0) ? 0 : 180;
1073 #ifdef ARC45
1074 if (abs (wy) / 2 >= abs (wx))
1075 dir = (SGNZ (wx) == SGNZ (wy)) ? 45 : -45;
1076 else
1077 #endif
1078 dir = (SGNZ (wx) == SGNZ (wy)) ? 90 : -90;
1080 else
1082 Crosshair.AttachedBox.Point2.Y =
1083 Crosshair.AttachedBox.Point1.Y + abs (wx) * SGNZ (wy);
1084 sa = (wy >= 0) ? -90 : 90;
1085 #ifdef ARC45
1086 if (abs (wx) / 2 >= abs (wy))
1087 dir = (SGNZ (wx) == SGNZ (wy)) ? -45 : 45;
1088 else
1089 #endif
1090 dir = (SGNZ (wx) == SGNZ (wy)) ? -90 : 90;
1091 wy = wx;
1093 if (abs (wy) > 0 && (arc = CreateNewArcOnLayer (CURRENT,
1094 Crosshair.
1095 AttachedBox.
1096 Point2.X,
1097 Crosshair.
1098 AttachedBox.
1099 Point2.Y,
1100 abs (wy),
1101 abs (wy),
1103 dir,
1104 Settings.
1105 LineThickness,
1106 2 * Settings.
1107 Keepaway,
1108 MakeFlags
1109 (TEST_FLAG
1110 (CLEARNEWFLAG,
1111 PCB) ?
1112 CLEARLINEFLAG :
1113 0))))
1115 BoxType *bx;
1117 bx = GetArcEnds (arc);
1118 Crosshair.AttachedBox.Point1.X =
1119 Crosshair.AttachedBox.Point2.X = bx->X2;
1120 Crosshair.AttachedBox.Point1.Y =
1121 Crosshair.AttachedBox.Point2.Y = bx->Y2;
1122 AddObjectToCreateUndoList (ARC_TYPE, CURRENT, arc, arc);
1123 IncrementUndoSerialNumber ();
1124 addedLines++;
1125 DrawArc (CURRENT, arc);
1126 Draw ();
1127 Crosshair.AttachedBox.State = STATE_THIRD;
1129 break;
1132 break;
1134 case LOCK_MODE:
1136 type = SearchScreen (Note.X, Note.Y, LOCK_TYPES, &ptr1, &ptr2, &ptr3);
1137 if (type == ELEMENT_TYPE)
1139 ElementType *element = (ElementType *) ptr2;
1141 TOGGLE_FLAG (LOCKFLAG, element);
1142 PIN_LOOP (element);
1144 TOGGLE_FLAG (LOCKFLAG, pin);
1145 CLEAR_FLAG (SELECTEDFLAG, pin);
1147 END_LOOP;
1148 PAD_LOOP (element);
1150 TOGGLE_FLAG (LOCKFLAG, pad);
1151 CLEAR_FLAG (SELECTEDFLAG, pad);
1153 END_LOOP;
1154 CLEAR_FLAG (SELECTEDFLAG, element);
1155 /* always re-draw it since I'm too lazy
1156 * to tell if a selected flag changed
1158 DrawElement (element);
1159 Draw ();
1160 SetChangedFlag (true);
1161 hid_actionl ("Report", "Object", NULL);
1163 else if (type != NO_TYPE)
1165 TextType *thing = (TextType *) ptr3;
1166 TOGGLE_FLAG (LOCKFLAG, thing);
1167 if (TEST_FLAG (LOCKFLAG, thing)
1168 && TEST_FLAG (SELECTEDFLAG, thing))
1170 /* this is not un-doable since LOCK isn't */
1171 CLEAR_FLAG (SELECTEDFLAG, thing);
1172 DrawObject (type, ptr1, ptr2);
1173 Draw ();
1175 SetChangedFlag (true);
1176 hid_actionl ("Report", "Object", NULL);
1178 break;
1180 case THERMAL_MODE:
1182 if (((type
1184 SearchScreen (Note.X, Note.Y, PIN_TYPES, &ptr1, &ptr2,
1185 &ptr3)) != NO_TYPE)
1186 && !TEST_FLAG (HOLEFLAG, (PinType *) ptr3))
1188 if (gui->shift_is_pressed ())
1190 int tstyle = GET_THERM (INDEXOFCURRENT, (PinType *) ptr3);
1191 tstyle++;
1192 if (tstyle > 5)
1193 tstyle = 1;
1194 ChangeObjectThermal (type, ptr1, ptr2, ptr3, tstyle);
1196 else if (GET_THERM (INDEXOFCURRENT, (PinType *) ptr3))
1197 ChangeObjectThermal (type, ptr1, ptr2, ptr3, 0);
1198 else
1199 ChangeObjectThermal (type, ptr1, ptr2, ptr3, PCB->ThermStyle);
1201 break;
1204 case LINE_MODE:
1205 /* do update of position */
1206 NotifyLine ();
1207 if (Crosshair.AttachedLine.State != STATE_THIRD)
1208 break;
1210 /* Remove anchor if clicking on start point;
1211 * this means we can't paint 0 length lines
1212 * which could be used for square SMD pads.
1213 * Instead use a very small delta, or change
1214 * the file after saving.
1216 if (Crosshair.X == Crosshair.AttachedLine.Point1.X
1217 && Crosshair.Y == Crosshair.AttachedLine.Point1.Y)
1219 SetMode (LINE_MODE);
1220 break;
1223 if (PCB->RatDraw)
1225 RatType *line;
1226 if ((line = AddNet ()))
1228 addedLines++;
1229 AddObjectToCreateUndoList (RATLINE_TYPE, line, line, line);
1230 IncrementUndoSerialNumber ();
1231 DrawRat (line);
1232 Crosshair.AttachedLine.Point1.X =
1233 Crosshair.AttachedLine.Point2.X;
1234 Crosshair.AttachedLine.Point1.Y =
1235 Crosshair.AttachedLine.Point2.Y;
1236 Draw ();
1238 break;
1240 else
1241 /* create line if both ends are determined && length != 0 */
1243 LineType *line;
1244 int line_flags = 0;
1246 if (TEST_FLAG (AUTODRCFLAG, PCB) && !TEST_SILK_LAYER (CURRENT))
1247 line_flags |= CONNECTEDFLAG | FOUNDFLAG;
1249 if (TEST_FLAG (CLEARNEWFLAG, PCB))
1250 line_flags |= CLEARLINEFLAG;
1252 if (PCB->Clipping
1253 && Crosshair.AttachedLine.Point1.X ==
1254 Crosshair.AttachedLine.Point2.X
1255 && Crosshair.AttachedLine.Point1.Y ==
1256 Crosshair.AttachedLine.Point2.Y
1257 && (Crosshair.AttachedLine.Point2.X != Note.X
1258 || Crosshair.AttachedLine.Point2.Y != Note.Y))
1260 /* We will only need to paint the second line segment.
1261 Since we only check for vias on the first segment,
1262 swap them so the non-empty segment is the first segment. */
1263 Crosshair.AttachedLine.Point2.X = Note.X;
1264 Crosshair.AttachedLine.Point2.Y = Note.Y;
1267 if ((Crosshair.AttachedLine.Point1.X !=
1268 Crosshair.AttachedLine.Point2.X
1269 || Crosshair.AttachedLine.Point1.Y !=
1270 Crosshair.AttachedLine.Point2.Y))
1272 PinType *via;
1274 if ((line =
1275 CreateDrawnLineOnLayer (CURRENT,
1276 Crosshair.AttachedLine.Point1.X,
1277 Crosshair.AttachedLine.Point1.Y,
1278 Crosshair.AttachedLine.Point2.X,
1279 Crosshair.AttachedLine.Point2.Y,
1280 Settings.LineThickness,
1281 2 * Settings.Keepaway,
1282 MakeFlags (line_flags))) != NULL)
1285 addedLines++;
1286 AddObjectToCreateUndoList (LINE_TYPE, CURRENT, line, line);
1287 DrawLine (CURRENT, line);
1289 /* place a via if vias are visible, the layer is
1290 in a new group since the last line and there
1291 isn't a pin already here */
1292 if (PCB->ViaOn && GetLayerGroupNumberByPointer (CURRENT) !=
1293 GetLayerGroupNumberByPointer (lastLayer) &&
1294 SearchObjectByLocation (PIN_TYPES, &ptr1, &ptr2, &ptr3,
1295 Crosshair.AttachedLine.Point1.X,
1296 Crosshair.AttachedLine.Point1.Y,
1297 Settings.ViaThickness / 2) ==
1298 NO_TYPE
1299 && (via =
1300 CreateNewVia (PCB->Data,
1301 Crosshair.AttachedLine.Point1.X,
1302 Crosshair.AttachedLine.Point1.Y,
1303 Settings.ViaThickness,
1304 2 * Settings.Keepaway, 0,
1305 Settings.ViaDrillingHole, NULL,
1306 NoFlags ())) != NULL)
1308 AddObjectToCreateUndoList (VIA_TYPE, via, via, via);
1309 DrawVia (via);
1311 /* copy the coordinates */
1312 Crosshair.AttachedLine.Point1.X =
1313 Crosshair.AttachedLine.Point2.X;
1314 Crosshair.AttachedLine.Point1.Y =
1315 Crosshair.AttachedLine.Point2.Y;
1316 IncrementUndoSerialNumber ();
1317 lastLayer = CURRENT;
1319 if (PCB->Clipping && (Note.X != Crosshair.AttachedLine.Point2.X
1320 || Note.Y !=
1321 Crosshair.AttachedLine.Point2.Y))
1323 if ((line =
1324 CreateDrawnLineOnLayer (CURRENT,
1325 Crosshair.AttachedLine.Point2.X,
1326 Crosshair.AttachedLine.Point2.Y,
1327 Note.X, Note.Y,
1328 Settings.LineThickness,
1329 2 * Settings.Keepaway,
1330 MakeFlags (line_flags))) != NULL)
1332 addedLines++;
1333 AddObjectToCreateUndoList (LINE_TYPE, CURRENT, line, line);
1334 IncrementUndoSerialNumber ();
1335 DrawLine (CURRENT, line);
1337 /* move to new start point */
1338 Crosshair.AttachedLine.Point1.X = Note.X;
1339 Crosshair.AttachedLine.Point1.Y = Note.Y;
1340 Crosshair.AttachedLine.Point2.X = Note.X;
1341 Crosshair.AttachedLine.Point2.Y = Note.Y;
1342 if (TEST_FLAG (SWAPSTARTDIRFLAG, PCB))
1344 PCB->Clipping ^= 3;
1347 if (TEST_FLAG (AUTODRCFLAG, PCB) && !TEST_SILK_LAYER (CURRENT))
1348 LookupConnection (Note.X, Note.Y, true, 1, CONNECTEDFLAG, false);
1349 Draw ();
1351 break;
1353 case RECTANGLE_MODE:
1354 /* do update of position */
1355 NotifyBlock ();
1357 /* create rectangle if both corners are determined
1358 * and width, height are != 0
1360 if (Crosshair.AttachedBox.State == STATE_THIRD &&
1361 Crosshair.AttachedBox.Point1.X != Crosshair.AttachedBox.Point2.X &&
1362 Crosshair.AttachedBox.Point1.Y != Crosshair.AttachedBox.Point2.Y)
1364 PolygonType *polygon;
1366 int flags = CLEARPOLYFLAG;
1367 if (TEST_FLAG (NEWFULLPOLYFLAG, PCB))
1368 flags |= FULLPOLYFLAG;
1369 if ((polygon = CreateNewPolygonFromRectangle (CURRENT,
1370 Crosshair.
1371 AttachedBox.Point1.X,
1372 Crosshair.
1373 AttachedBox.Point1.Y,
1374 Crosshair.
1375 AttachedBox.Point2.X,
1376 Crosshair.
1377 AttachedBox.Point2.Y,
1378 MakeFlags
1379 (flags))) !=
1380 NULL)
1382 AddObjectToCreateUndoList (POLYGON_TYPE, CURRENT,
1383 polygon, polygon);
1384 IncrementUndoSerialNumber ();
1385 DrawPolygon (CURRENT, polygon);
1386 Draw ();
1389 /* reset state to 'first corner' */
1390 Crosshair.AttachedBox.State = STATE_FIRST;
1392 break;
1394 case TEXT_MODE:
1396 char *string;
1398 if ((string = gui->prompt_for (_("Enter text:"), "")) != NULL)
1400 if (strlen(string) > 0)
1402 TextType *text;
1403 int flag = CLEARLINEFLAG;
1405 if (GetLayerGroupNumberByNumber (INDEXOFCURRENT) ==
1406 GetLayerGroupNumberBySide (BOTTOM_SIDE))
1407 flag |= ONSOLDERFLAG;
1408 if ((text = CreateNewText (CURRENT, &PCB->Font, Note.X,
1409 Note.Y, 0, Settings.TextScale,
1410 string, MakeFlags (flag))) != NULL)
1412 AddObjectToCreateUndoList (TEXT_TYPE, CURRENT, text, text);
1413 IncrementUndoSerialNumber ();
1414 DrawText (CURRENT, text);
1415 Draw ();
1418 free (string);
1420 break;
1423 case POLYGON_MODE:
1425 PointType *points = Crosshair.AttachedPolygon.Points;
1426 Cardinal n = Crosshair.AttachedPolygon.PointN;
1428 /* do update of position; use the 'LINE_MODE' mechanism */
1429 NotifyLine ();
1431 /* check if this is the last point of a polygon */
1432 if (n >= 3 &&
1433 points->X == Crosshair.AttachedLine.Point2.X &&
1434 points->Y == Crosshair.AttachedLine.Point2.Y)
1436 CopyAttachedPolygonToLayer ();
1437 Draw ();
1438 break;
1441 /* create new point if it's the first one or if it's
1442 * different to the last one
1444 if (!n ||
1445 points[n - 1].X != Crosshair.AttachedLine.Point2.X ||
1446 points[n - 1].Y != Crosshair.AttachedLine.Point2.Y)
1448 CreateNewPointInPolygon (&Crosshair.AttachedPolygon,
1449 Crosshair.AttachedLine.Point2.X,
1450 Crosshair.AttachedLine.Point2.Y);
1452 /* copy the coordinates */
1453 Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X;
1454 Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y;
1456 break;
1459 case POLYGONHOLE_MODE:
1461 switch (Crosshair.AttachedObject.State)
1463 /* first notify, lookup object */
1464 case STATE_FIRST:
1465 Crosshair.AttachedObject.Type =
1466 SearchScreen (Note.X, Note.Y, POLYGON_TYPE,
1467 &Crosshair.AttachedObject.Ptr1,
1468 &Crosshair.AttachedObject.Ptr2,
1469 &Crosshair.AttachedObject.Ptr3);
1471 if (Crosshair.AttachedObject.Type == NO_TYPE)
1473 Message (_("The first point of a polygon hole must be on a polygon.\n"));
1474 break; /* don't start doing anything if clicked outside of polys */
1477 if (TEST_FLAG(LOCKFLAG, (PolygonType *) Crosshair.AttachedObject.Ptr2))
1479 Message (_("Sorry, the object is locked\n"));
1480 Crosshair.AttachedObject.Type = NO_TYPE;
1481 break;
1483 else
1484 Crosshair.AttachedObject.State = STATE_SECOND;
1485 /* Fall thru: first click is also the first point of the
1486 * poly hole. */
1488 /* second notify, insert new point into object */
1489 case STATE_SECOND:
1491 PointType *points = Crosshair.AttachedPolygon.Points;
1492 Cardinal n = Crosshair.AttachedPolygon.PointN;
1493 POLYAREA *original, *new_hole, *result;
1494 FlagType Flags;
1496 /* do update of position; use the 'LINE_MODE' mechanism */
1497 NotifyLine ();
1499 /* check if this is the last point of a polygon */
1500 if (n >= 3 &&
1501 points->X == Crosshair.AttachedLine.Point2.X &&
1502 points->Y == Crosshair.AttachedLine.Point2.Y)
1504 /* Create POLYAREAs from the original polygon
1505 * and the new hole polygon */
1506 original = PolygonToPoly ((PolygonType *)Crosshair.AttachedObject.Ptr2);
1507 new_hole = PolygonToPoly (&Crosshair.AttachedPolygon);
1509 /* Subtract the hole from the original polygon shape */
1510 poly_Boolean_free (original, new_hole, &result, PBO_SUB);
1512 /* Convert the resulting polygon(s) into a new set of nodes
1513 * and place them on the page. Delete the original polygon.
1515 SaveUndoSerialNumber ();
1516 Flags = ((PolygonType *)Crosshair.AttachedObject.Ptr2)->Flags;
1517 PolyToPolygonsOnLayer (PCB->Data, (LayerType *)Crosshair.AttachedObject.Ptr1,
1518 result, Flags);
1519 RemoveObject (POLYGON_TYPE,
1520 Crosshair.AttachedObject.Ptr1,
1521 Crosshair.AttachedObject.Ptr2,
1522 Crosshair.AttachedObject.Ptr3);
1523 RestoreUndoSerialNumber ();
1524 IncrementUndoSerialNumber ();
1525 Draw ();
1527 /* reset state of attached line */
1528 memset (&Crosshair.AttachedPolygon, 0, sizeof (PolygonType));
1529 Crosshair.AttachedObject.State = STATE_FIRST;
1530 addedLines = 0;
1532 break;
1535 /* create new point if it's the first one or if it's
1536 * different to the last one
1538 if (!n ||
1539 points[n - 1].X != Crosshair.AttachedLine.Point2.X ||
1540 points[n - 1].Y != Crosshair.AttachedLine.Point2.Y)
1542 CreateNewPointInPolygon (&Crosshair.AttachedPolygon,
1543 Crosshair.AttachedLine.Point2.X,
1544 Crosshair.AttachedLine.Point2.Y);
1546 /* copy the coordinates */
1547 Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X;
1548 Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y;
1550 break;
1554 break;
1557 case PASTEBUFFER_MODE:
1559 TextType estr[MAX_ELEMENTNAMES];
1560 ElementType *e = 0;
1562 if (gui->shift_is_pressed ())
1564 int type =
1565 SearchScreen (Note.X, Note.Y, ELEMENT_TYPE, &ptr1, &ptr2,
1566 &ptr3);
1567 if (type == ELEMENT_TYPE)
1569 e = (ElementType *) ptr1;
1570 if (e)
1572 int i;
1574 memcpy (estr, e->Name,
1575 MAX_ELEMENTNAMES * sizeof (TextType));
1576 for (i = 0; i < MAX_ELEMENTNAMES; ++i)
1577 estr[i].TextString = estr[i].TextString ? strdup(estr[i].TextString) : NULL;
1578 RemoveElement (e);
1582 if (CopyPastebufferToLayout (Note.X, Note.Y))
1583 SetChangedFlag (true);
1584 if (e)
1586 int type =
1587 SearchScreen (Note.X, Note.Y, ELEMENT_TYPE, &ptr1, &ptr2,
1588 &ptr3);
1589 if (type == ELEMENT_TYPE && ptr1)
1591 int i, save_n;
1592 e = (ElementType *) ptr1;
1594 save_n = NAME_INDEX (PCB);
1596 for (i = 0; i < MAX_ELEMENTNAMES; i++)
1598 if (i == save_n)
1599 EraseElementName (e);
1600 r_delete_entry (PCB->Data->name_tree[i],
1601 (BoxType *) & (e->Name[i]));
1602 memcpy (&(e->Name[i]), &(estr[i]), sizeof (TextType));
1603 e->Name[i].Element = e;
1604 SetTextBoundingBox (&PCB->Font, &(e->Name[i]));
1605 r_insert_entry (PCB->Data->name_tree[i],
1606 (BoxType *) & (e->Name[i]), 0);
1607 if (i == save_n)
1608 DrawElementName (e);
1612 break;
1615 case REMOVE_MODE:
1616 if ((type =
1617 SearchScreen (Note.X, Note.Y, REMOVE_TYPES, &ptr1, &ptr2,
1618 &ptr3)) != NO_TYPE)
1620 if (TEST_FLAG (LOCKFLAG, (LineType *) ptr2))
1622 Message (_("Sorry, the object is locked\n"));
1623 break;
1625 if (type == ELEMENT_TYPE)
1627 RubberbandType *ptr;
1628 int i;
1630 Crosshair.AttachedObject.RubberbandN = 0;
1631 LookupRatLines (type, ptr1, ptr2, ptr3);
1632 ptr = Crosshair.AttachedObject.Rubberband;
1633 for (i = 0; i < Crosshair.AttachedObject.RubberbandN; i++)
1635 if (PCB->RatOn)
1636 EraseRat ((RatType *) ptr->Line);
1637 if (TEST_FLAG (RUBBERENDFLAG, ptr->Line))
1638 MoveObjectToRemoveUndoList (RATLINE_TYPE,
1639 ptr->Line, ptr->Line,
1640 ptr->Line);
1641 else
1642 TOGGLE_FLAG (RUBBERENDFLAG, ptr->Line); /* only remove line once */
1643 ptr++;
1646 RemoveObject (type, ptr1, ptr2, ptr3);
1647 IncrementUndoSerialNumber ();
1648 SetChangedFlag (true);
1650 break;
1652 case ROTATE_MODE:
1653 RotateScreenObject (Note.X, Note.Y,
1654 gui->shift_is_pressed ()? (SWAP_IDENT ?
1655 1 : 3)
1656 : (SWAP_IDENT ? 3 : 1));
1657 break;
1659 /* both are almost the same */
1660 case COPY_MODE:
1661 case MOVE_MODE:
1662 switch (Crosshair.AttachedObject.State)
1664 /* first notify, lookup object */
1665 case STATE_FIRST:
1667 int types = (Settings.Mode == COPY_MODE) ?
1668 COPY_TYPES : MOVE_TYPES;
1670 Crosshair.AttachedObject.Type =
1671 SearchScreen (Note.X, Note.Y, types,
1672 &Crosshair.AttachedObject.Ptr1,
1673 &Crosshair.AttachedObject.Ptr2,
1674 &Crosshair.AttachedObject.Ptr3);
1675 if (Crosshair.AttachedObject.Type != NO_TYPE)
1677 if (Settings.Mode == MOVE_MODE &&
1678 TEST_FLAG (LOCKFLAG, (PinType *)
1679 Crosshair.AttachedObject.Ptr2))
1681 Message (_("Sorry, the object is locked\n"));
1682 Crosshair.AttachedObject.Type = NO_TYPE;
1684 else
1685 AttachForCopy (Note.X, Note.Y);
1687 break;
1690 /* second notify, move or copy object */
1691 case STATE_SECOND:
1692 if (Settings.Mode == COPY_MODE)
1693 CopyObject (Crosshair.AttachedObject.Type,
1694 Crosshair.AttachedObject.Ptr1,
1695 Crosshair.AttachedObject.Ptr2,
1696 Crosshair.AttachedObject.Ptr3,
1697 Note.X - Crosshair.AttachedObject.X,
1698 Note.Y - Crosshair.AttachedObject.Y);
1699 else
1701 MoveObjectAndRubberband (Crosshair.AttachedObject.Type,
1702 Crosshair.AttachedObject.Ptr1,
1703 Crosshair.AttachedObject.Ptr2,
1704 Crosshair.AttachedObject.Ptr3,
1705 Note.X - Crosshair.AttachedObject.X,
1706 Note.Y - Crosshair.AttachedObject.Y);
1707 SetLocalRef (0, 0, false);
1709 SetChangedFlag (true);
1711 /* reset identifiers */
1712 Crosshair.AttachedObject.Type = NO_TYPE;
1713 Crosshair.AttachedObject.State = STATE_FIRST;
1714 break;
1716 break;
1718 /* insert a point into a polygon/line/... */
1719 case INSERTPOINT_MODE:
1720 switch (Crosshair.AttachedObject.State)
1722 /* first notify, lookup object */
1723 case STATE_FIRST:
1724 Crosshair.AttachedObject.Type =
1725 SearchScreen (Note.X, Note.Y, INSERT_TYPES,
1726 &Crosshair.AttachedObject.Ptr1,
1727 &Crosshair.AttachedObject.Ptr2,
1728 &Crosshair.AttachedObject.Ptr3);
1730 if (Crosshair.AttachedObject.Type != NO_TYPE)
1732 if (TEST_FLAG (LOCKFLAG, (PolygonType *)
1733 Crosshair.AttachedObject.Ptr2))
1735 Message (_("Sorry, the object is locked\n"));
1736 Crosshair.AttachedObject.Type = NO_TYPE;
1737 break;
1739 else
1741 /* get starting point of nearest segment */
1742 if (Crosshair.AttachedObject.Type == POLYGON_TYPE)
1744 fake.poly =
1745 (PolygonType *) Crosshair.AttachedObject.Ptr2;
1746 polyIndex =
1747 GetLowestDistancePolygonPoint (fake.poly, Note.X,
1748 Note.Y);
1749 fake.line.Point1 = fake.poly->Points[polyIndex];
1750 fake.line.Point2 = fake.poly->Points[
1751 prev_contour_point (fake.poly, polyIndex)];
1752 Crosshair.AttachedObject.Ptr2 = &fake.line;
1755 Crosshair.AttachedObject.State = STATE_SECOND;
1756 InsertedPoint = *AdjustInsertPoint ();
1759 break;
1761 /* second notify, insert new point into object */
1762 case STATE_SECOND:
1763 if (Crosshair.AttachedObject.Type == POLYGON_TYPE)
1764 InsertPointIntoObject (POLYGON_TYPE,
1765 Crosshair.AttachedObject.Ptr1, fake.poly,
1766 &polyIndex,
1767 InsertedPoint.X, InsertedPoint.Y, false, false);
1768 else
1769 InsertPointIntoObject (Crosshair.AttachedObject.Type,
1770 Crosshair.AttachedObject.Ptr1,
1771 Crosshair.AttachedObject.Ptr2,
1772 &polyIndex,
1773 InsertedPoint.X, InsertedPoint.Y, false, false);
1774 SetChangedFlag (true);
1776 /* reset identifiers */
1777 Crosshair.AttachedObject.Type = NO_TYPE;
1778 Crosshair.AttachedObject.State = STATE_FIRST;
1779 break;
1781 break;
1786 /* --------------------------------------------------------------------------- */
1788 static const char atomic_syntax[] = N_("Atomic(Save|Restore|Close|Block)");
1790 static const char atomic_help[] = N_("Save or restore the undo serial number.");
1792 /* %start-doc actions Atomic
1794 This action allows making multiple-action bindings into an atomic
1795 operation that will be undone by a single Undo command. For example,
1796 to optimize rat lines, you'd delete the rats and re-add them. To
1797 group these into a single undo, you'd want the deletions and the
1798 additions to have the same undo serial number. So, you @code{Save},
1799 delete the rats, @code{Restore}, add the rats - using the same serial
1800 number as the deletes, then @code{Block}, which checks to see if the
1801 deletions or additions actually did anything. If not, the serial
1802 number is set to the saved number, as there's nothing to undo. If
1803 something did happen, the serial number is incremented so that these
1804 actions are counted as a single undo step.
1806 @table @code
1808 @item Save
1809 Saves the undo serial number.
1811 @item Restore
1812 Returns it to the last saved number.
1814 @item Close
1815 Sets it to 1 greater than the last save.
1817 @item Block
1818 Does a Restore if there was nothing to undo, else does a Close.
1820 @end table
1822 %end-doc */
1824 static int
1825 ActionAtomic (int argc, char **argv, Coord x, Coord y)
1827 if (argc != 1)
1828 AFAIL (atomic);
1830 switch (GetFunctionID (argv[0]))
1832 case F_Save:
1833 SaveUndoSerialNumber ();
1834 break;
1835 case F_Restore:
1836 RestoreUndoSerialNumber ();
1837 break;
1838 case F_Close:
1839 RestoreUndoSerialNumber ();
1840 IncrementUndoSerialNumber ();
1841 break;
1842 case F_Block:
1843 RestoreUndoSerialNumber ();
1844 if (Bumped)
1845 IncrementUndoSerialNumber ();
1846 break;
1848 return 0;
1851 /* -------------------------------------------------------------------------- */
1853 static const char drc_syntax[] = N_("DRC()");
1855 static const char drc_help[] = N_("Invoke the DRC check.");
1857 /* %start-doc actions DRC
1859 Note that the design rule check uses the current board rule settings,
1860 not the current style settings.
1862 %end-doc */
1864 static int
1865 ActionDRCheck (int argc, char **argv, Coord x, Coord y)
1867 int count;
1869 if (gui->drc_gui == NULL || gui->drc_gui->log_drc_overview)
1871 Message (_("%m+Rules are minspace %$mS, minoverlap %$mS "
1872 "minwidth %$mS, minsilk %$mS\n"
1873 "min drill %$mS, min annular ring %$mS\n"),
1874 Settings.grid_unit->allow,
1875 PCB->Bloat, PCB->Shrink,
1876 PCB->minWid, PCB->minSlk,
1877 PCB->minDrill, PCB->minRing);
1879 count = DRCAll ();
1880 if (gui->drc_gui == NULL || gui->drc_gui->log_drc_overview)
1882 if (count == 0)
1883 Message (_("No DRC problems found.\n"));
1884 else if (count > 0)
1885 Message (_("Found %d design rule errors.\n"), count);
1886 else
1887 Message (_("Aborted DRC after %d design rule errors.\n"), -count);
1889 return 0;
1892 /* -------------------------------------------------------------------------- */
1894 static const char dumplibrary_syntax[] = N_("DumpLibrary()");
1896 static const char dumplibrary_help[] =
1897 N_("Display the entire contents of the libraries.");
1899 /* %start-doc actions DumpLibrary
1902 %end-doc */
1904 static int
1905 ActionDumpLibrary (int argc, char **argv, Coord x, Coord y)
1907 int i, j;
1909 printf ("**** Do not count on this format. It will change ****\n\n");
1910 printf ("MenuN = %d\n", (int) Library.MenuN);
1911 printf ("MenuMax = %d\n", (int) Library.MenuMax);
1912 for (i = 0; i < Library.MenuN; i++)
1914 printf ("Library #%d:\n", i);
1915 printf (" EntryN = %d\n", (int) Library.Menu[i].EntryN);
1916 printf (" EntryMax = %d\n", (int) Library.Menu[i].EntryMax);
1917 printf (" Name = \"%s\"\n", UNKNOWN (Library.Menu[i].Name));
1918 printf (" directory = \"%s\"\n",
1919 UNKNOWN (Library.Menu[i].directory));
1920 printf (" Style = \"%s\"\n", UNKNOWN (Library.Menu[i].Style));
1921 printf (" flag = %d\n", Library.Menu[i].flag);
1923 for (j = 0; j < Library.Menu[i].EntryN; j++)
1925 printf (" #%4d: ", j);
1926 if (Library.Menu[i].Entry[j].Template == (char *) -1)
1928 printf ("newlib: \"%s\"\n",
1929 UNKNOWN (Library.Menu[i].Entry[j].ListEntry));
1931 else
1933 printf ("\"%s\", \"%s\", \"%s\", \"%s\", \"%s\"\n",
1934 UNKNOWN (Library.Menu[i].Entry[j].ListEntry),
1935 UNKNOWN (Library.Menu[i].Entry[j].Template),
1936 UNKNOWN (Library.Menu[i].Entry[j].Package),
1937 UNKNOWN (Library.Menu[i].Entry[j].Value),
1938 UNKNOWN (Library.Menu[i].Entry[j].Description));
1943 return 0;
1946 /* -------------------------------------------------------------------------- */
1948 static const char flip_syntax[] = N_("Flip(Object|Selected|SelectedElements)");
1950 static const char flip_help[] =
1951 N_("Flip an element to the opposite side of the board.");
1953 /* %start-doc actions Flip
1955 Note that the location of the element will be symmetric about the
1956 cursor location; i.e. if the part you are pointing at will still be at
1957 the same spot once the element is on the other side. When flipping
1958 multiple elements, this retains their positions relative to each
1959 other, not their absolute positions on the board.
1961 %end-doc */
1963 static int
1964 ActionFlip (int argc, char **argv, Coord x, Coord y)
1966 char *function = ARG (0);
1967 ElementType *element;
1968 void *ptrtmp;
1969 int err = 0;
1971 if (function)
1973 switch (GetFunctionID (function))
1975 case F_Object:
1976 if ((SearchScreen (x, y, ELEMENT_TYPE,
1977 &ptrtmp, &ptrtmp, &ptrtmp)) != NO_TYPE)
1979 element = (ElementType *) ptrtmp;
1980 ChangeElementSide (element, 2 * Crosshair.Y - PCB->MaxHeight);
1981 IncrementUndoSerialNumber ();
1982 Draw ();
1984 break;
1985 case F_Selected:
1986 case F_SelectedElements:
1987 ChangeSelectedElementSide ();
1988 break;
1989 default:
1990 err = 1;
1991 break;
1993 if (!err)
1994 return 0;
1997 AFAIL (flip);
2000 /* -------------------------------------------------------------------------- */
2002 static const char message_syntax[] = N_("Message(message)");
2004 static const char message_help[] = N_("Writes a message to the log window.");
2006 /* %start-doc actions Message
2008 This action displays a message to the log window. This action is primarily
2009 provided for use by other programs which may interface with PCB. If
2010 multiple arguments are given, each one is sent to the log window
2011 followed by a newline.
2013 %end-doc */
2015 static int
2016 ActionMessage (int argc, char **argv, Coord x, Coord y)
2018 int i;
2020 if (argc < 1)
2021 AFAIL (message);
2023 for (i = 0; i < argc; i++)
2025 Message (argv[i]);
2026 Message ("\n");
2029 return 0;
2033 /* -------------------------------------------------------------------------- */
2035 static const char setthermal_syntax[] =
2036 "SetThermal(Object|SelectedPins|SelectedVias|Selected, Style)";
2038 static const char setthermal_help[] =
2039 N_("Set the thermal (on the current layer) of pins or vias to the given style.\n"
2040 "Style = 0 means no thermal.\n"
2041 "Style = 1 has diagonal fingers with sharp edges.\n"
2042 "Style = 2 has horizontal and vertical fingers with sharp edges.\n"
2043 "Style = 3 is a solid connection to the plane.\n"
2044 "Style = 4 has diagonal fingers with rounded edges.\n"
2045 "Style = 5 has horizontal and vertical fingers with rounded edges.\n");
2047 /* %start-doc actions SetThermal
2049 This changes how/whether pins or vias connect to any rectangle or polygon
2050 on the current layer. The first argument can specify one object, or all
2051 selected pins, or all selected vias, or all selected pins and vias.
2052 The second argument specifies the style of connection.
2053 There are 5 possibilities:
2054 0 - no connection,
2055 1 - 45 degree fingers with sharp edges,
2056 2 - horizontal & vertical fingers with sharp edges,
2057 3 - solid connection,
2058 4 - 45 degree fingers with rounded corners,
2059 5 - horizontal & vertical fingers with rounded corners.
2061 Pins and Vias may have thermals whether or not there is a polygon available
2062 to connect with. However, they will have no effect without the polygon.
2063 %end-doc */
2065 static int
2066 ActionSetThermal (int argc, char **argv, Coord x, Coord y)
2068 char *function = ARG (0);
2069 char *style = ARG (1);
2070 void *ptr1, *ptr2, *ptr3;
2071 int type, kind;
2072 int err = 0;
2074 if (function && *function)
2076 bool absolute;
2078 if ( ! style || ! *style)
2080 kind = PCB->ThermStyle;
2081 absolute = true;
2083 else
2084 kind = GetUnitlessValue (style, &absolute);
2086 /* To allow relative values we could search for the first selected
2087 item and make 'kind' relative to that, but that's not too useful
2088 and requires quite some code. For example there's no
2089 GetFirstSelectedPin() function available. Let's postpone this
2090 functionality, there are more urgent things to do. */
2092 if (absolute)
2093 switch (GetFunctionID (function))
2095 case F_Object:
2096 if ((type =
2097 SearchScreen (Crosshair.X, Crosshair.Y, CHANGETHERMAL_TYPES,
2098 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
2100 ChangeObjectThermal (type, ptr1, ptr2, ptr3, kind);
2101 IncrementUndoSerialNumber ();
2102 Draw ();
2104 break;
2105 case F_SelectedPins:
2106 ChangeSelectedThermals (PIN_TYPE, kind);
2107 break;
2108 case F_SelectedVias:
2109 ChangeSelectedThermals (VIA_TYPE, kind);
2110 break;
2111 case F_Selected:
2112 case F_SelectedElements:
2113 ChangeSelectedThermals (CHANGETHERMAL_TYPES, kind);
2114 break;
2115 default:
2116 err = 1;
2117 break;
2119 else
2120 err = 1;
2122 else
2123 err = 1;
2125 if (err)
2126 AFAIL (setthermal);
2128 return 0;
2132 * \brief Event handler to set the cursor according to the X pointer
2133 * position called from inside main.c.
2135 * \warning !!! no action routine !!!
2137 void
2138 EventMoveCrosshair (int ev_x, int ev_y)
2140 #ifdef HAVE_LIBSTROKE
2141 if (mid_stroke)
2143 StrokeBox.X2 = ev_x;
2144 StrokeBox.Y2 = ev_y;
2145 stroke_record (ev_x, ev_y);
2146 return;
2148 #endif /* HAVE_LIBSTROKE */
2149 if (MoveCrosshairAbsolute (ev_x, ev_y))
2151 /* update object position and cursor location */
2152 AdjustAttachedObjects ();
2153 notify_crosshair_change (true);
2157 /* --------------------------------------------------------------------------- */
2159 static const char setvalue_syntax[] =
2160 N_("SetValue(Grid|Line|LineSize|Text|TextScale|ViaDrillingHole|Via|ViaSize, "
2161 "delta)");
2163 static const char setvalue_help[] =
2164 N_("Change various board-wide values and sizes.");
2166 /* %start-doc actions SetValue
2168 @table @code
2170 @item ViaDrillingHole
2171 Changes the diameter of the drill for new vias.
2173 @item Grid
2174 Sets the grid spacing.
2176 @item Line
2177 @item LineSize
2178 Changes the thickness of new lines.
2180 @item Via
2181 @item ViaSize
2182 Changes the diameter of new vias.
2184 @item Text
2185 @item TextScale
2186 Changes the size of new text.
2188 @end table
2190 %end-doc */
2192 static int
2193 ActionSetValue (int argc, char **argv, Coord x, Coord y)
2195 char *function = ARG (0);
2196 char *val = ARG (1);
2197 char *units = ARG (2);
2198 bool absolute; /* flag for 'absolute' value */
2199 double value;
2200 int text_scale;
2201 int err = 0;
2203 if (function && val)
2205 value = GetValue (val, units, &absolute);
2206 switch (GetFunctionID (function))
2208 case F_ViaDrillingHole:
2209 SetViaDrillingHole (absolute ? value :
2210 value + Settings.ViaDrillingHole,
2211 false);
2212 hid_action ("RouteStylesChanged");
2213 break;
2215 case F_Grid:
2216 if (absolute)
2217 SetGrid (value, false);
2218 else
2220 if (value == 0)
2221 value = val[0] == '-' ? -Settings.increments->grid
2222 : Settings.increments->grid;
2223 /* On the way down, short against the minimum
2224 * PCB drawing unit */
2225 if ((value + PCB->Grid) < 1)
2226 SetGrid (1, false);
2227 else if (PCB->Grid == 1)
2228 SetGrid (value, false);
2229 else
2230 SetGrid (value + PCB->Grid, false);
2232 break;
2234 case F_LineSize:
2235 case F_Line:
2236 if (!absolute && value == 0)
2237 value = val[0] == '-' ? -Settings.increments->line
2238 : Settings.increments->line;
2239 SetLineSize (absolute ? value : value + Settings.LineThickness);
2240 hid_action ("RouteStylesChanged");
2241 break;
2243 case F_Via:
2244 case F_ViaSize:
2245 SetViaSize (absolute ? value : value + Settings.ViaThickness, false);
2246 hid_action ("RouteStylesChanged");
2247 break;
2249 case F_Text:
2250 case F_TextScale:
2251 text_scale = value / (double)FONT_CAPHEIGHT * 100.;
2252 if (!absolute)
2253 text_scale += Settings.TextScale;
2254 SetTextScale (text_scale);
2255 break;
2256 default:
2257 err = 1;
2258 break;
2260 if (!err)
2261 return 0;
2264 AFAIL (setvalue);
2268 /* --------------------------------------------------------------------------- */
2270 static const char quit_syntax[] = N_("Quit()");
2272 static const char quit_help[] = N_("Quits the application after confirming.");
2274 /* %start-doc actions Quit
2276 If you have unsaved changes, you will be prompted to confirm (or
2277 save) before quitting.
2279 %end-doc */
2281 static int
2282 ActionQuit (int argc, char **argv, Coord x, Coord y)
2284 char *force = ARG (0);
2285 if (force && strcasecmp (force, "force") == 0)
2287 PCB->Changed = 0;
2288 exit (0);
2290 if (!PCB->Changed || gui->close_confirm_dialog () == HID_CLOSE_CONFIRM_OK)
2291 QuitApplication ();
2292 return 1;
2295 /* --------------------------------------------------------------------------- */
2297 static const char connection_syntax[] =
2298 N_("Connection(Find|ResetLinesAndPolygons|ResetPinsAndVias|Reset)");
2300 static const char connection_help[] =
2301 N_("Searches connections of the object at the cursor position.");
2303 /* %start-doc actions Connection
2305 Connections found with this action will be highlighted in the
2306 ``connected-color'' color and will have the ``found'' flag set.
2308 @table @code
2310 @item Find
2311 The net under the cursor is ``found''.
2313 @item ResetLinesAndPolygons
2314 Any ``found'' lines and polygons are marked ``not found''.
2316 @item ResetPinsAndVias
2317 Any ``found'' pins and vias are marked ``not found''.
2319 @item Reset
2320 All ``found'' objects are marked ``not found''.
2322 @end table
2324 %end-doc */
2326 static int
2327 ActionConnection (int argc, char **argv, Coord x, Coord y)
2329 char *function = ARG (0);
2330 if (function)
2332 switch (GetFunctionID (function))
2334 case F_Find:
2336 gui->get_coords (_("Click on a connection"), &x, &y);
2337 LookupConnection (x, y, true, 1, CONNECTEDFLAG, false);
2338 LookupConnection (x, y, true, 1, FOUNDFLAG, true);
2339 break;
2342 case F_ResetLinesAndPolygons:
2343 if (ClearFlagOnLinesAndPolygons (true, CONNECTEDFLAG | FOUNDFLAG))
2345 IncrementUndoSerialNumber ();
2346 Draw ();
2348 break;
2350 case F_ResetPinsViasAndPads:
2351 if (ClearFlagOnPinsViasAndPads (true, CONNECTEDFLAG | FOUNDFLAG))
2353 IncrementUndoSerialNumber ();
2354 Draw ();
2356 break;
2358 case F_Reset:
2359 if (ClearFlagOnAllObjects (true, CONNECTEDFLAG | FOUNDFLAG))
2361 IncrementUndoSerialNumber ();
2362 Draw ();
2364 break;
2366 return 0;
2369 AFAIL (connection);
2372 /* --------------------------------------------------------------------------- */
2374 static const char disperseelements_syntax[] =
2375 N_("DisperseElements(All|Selected)");
2377 static const char disperseelements_help[] = N_("Disperses elements.");
2379 /* %start-doc actions DisperseElements
2381 Normally this is used when starting a board, by selecting all elements
2382 and then dispersing them. This scatters the elements around the board
2383 so that you can pick individual ones, rather than have all the
2384 elements at the same 0,0 coordinate and thus impossible to choose
2385 from.
2387 %end-doc */
2389 #define GAP MIL_TO_COORD(100)
2391 static int
2392 ActionDisperseElements (int argc, char **argv, Coord x, Coord y)
2394 char *function = ARG (0);
2395 Coord minx = GAP,
2396 miny = GAP,
2397 maxy = GAP,
2398 dx, dy;
2399 int all = 0, bad = 0;
2401 if (!function || !*function)
2403 bad = 1;
2405 else
2407 switch (GetFunctionID (function))
2409 case F_All:
2410 all = 1;
2411 break;
2413 case F_Selected:
2414 all = 0;
2415 break;
2417 default:
2418 bad = 1;
2422 if (bad)
2424 AFAIL (disperseelements);
2428 ELEMENT_LOOP (PCB->Data);
2431 * If we want to disperse selected elements, maybe we need smarter
2432 * code here to avoid putting components on top of others which
2433 * are not selected. For now, I'm assuming that this is typically
2434 * going to be used either with a brand new design or a scratch
2435 * design holding some new components
2437 if (!TEST_FLAG (LOCKFLAG, element) && (all || TEST_FLAG (SELECTEDFLAG, element)))
2440 /* figure out how much to move the element */
2441 dx = minx - element->BoundingBox.X1;
2443 /* snap to the grid */
2444 dx -= (element->MarkX + dx) % PCB->Grid;
2447 * and add one grid size so we make sure we always space by GAP or
2448 * more
2450 dx += PCB->Grid;
2452 /* Figure out if this row has room. If not, start a new row */
2453 if (GAP + element->BoundingBox.X2 + dx > PCB->MaxWidth)
2455 miny = maxy + GAP;
2456 minx = GAP;
2459 /* figure out how much to move the element */
2460 dx = minx - element->BoundingBox.X1;
2461 dy = miny - element->BoundingBox.Y1;
2463 /* snap to the grid */
2464 dx -= (element->MarkX + dx) % PCB->Grid;
2465 dx += PCB->Grid;
2466 dy -= (element->MarkY + dy) % PCB->Grid;
2467 dy += PCB->Grid;
2469 /* move the element */
2470 MoveElementLowLevel (PCB->Data, element, dx, dy);
2472 /* and add to the undo list so we can undo this operation */
2473 AddObjectToMoveUndoList (ELEMENT_TYPE, NULL, NULL, element, dx, dy);
2475 /* keep track of how tall this row is */
2476 minx += element->BoundingBox.X2 - element->BoundingBox.X1 + GAP;
2477 if (maxy < element->BoundingBox.Y2)
2479 maxy = element->BoundingBox.Y2;
2484 END_LOOP;
2486 /* done with our action so increment the undo # */
2487 IncrementUndoSerialNumber ();
2489 Redraw ();
2490 SetChangedFlag (true);
2492 return 0;
2495 #undef GAP
2497 /* --------------------------------------------------------------------------- */
2499 static const char display_syntax[] =
2500 N_("Display(NameOnPCB|Description|Value)\n"
2501 "Display(Grid|Redraw)\n"
2502 "Display(CycleClip|CycleCrosshair|Toggle45Degree|ToggleStartDirection)\n"
2503 "Display(ToggleGrid|ToggleRubberBandMode|ToggleUniqueNames)\n"
2504 "Display(ToggleMask|ToggleName|ToggleClearLine|ToggleFullPoly|ToggleSnapPin)\n"
2505 "Display(ToggleThindraw|ToggleThindrawPoly|ToggleOrthoMove|ToggleLocalRef)\n"
2506 "Display(ToggleCheckPlanes|ToggleShowDRC|ToggleAutoDRC)\n"
2507 "Display(ToggleLiveRoute|LockNames|OnlyNames)\n"
2508 "Display(Pinout|PinOrPadName)");
2510 static const char display_help[] = N_("Several display-related actions.");
2512 /* %start-doc actions Display
2514 @table @code
2516 @item NameOnPCB
2517 @item Description
2518 @item Value
2519 Specify whether all elements show their name, description, or value.
2521 @item Redraw
2522 Redraw the whole board.
2524 @item Toggle45Degree
2525 When clear, lines can be drawn at any angle. When set, lines are
2526 restricted to multiples of 45 degrees and requested lines may be
2527 broken up according to the clip setting.
2529 @item CycleClip
2530 Changes the way lines are restricted to 45 degree increments. The
2531 various settings are: straight only, orthogonal then angled, and angled
2532 then orthogonal. If AllDirections is set, this action disables it.
2534 @item CycleCrosshair
2535 Changes crosshair drawing. Crosshair may accept form of 4-ray,
2536 8-ray and 12-ray cross.
2538 @item ToggleRubberBandMode
2539 If set, moving an object moves all the lines attached to it too.
2541 @item ToggleStartDirection
2542 If set, each time you set a point in a line, the Clip toggles between
2543 orth-angle and angle-ortho.
2545 @item ToggleUniqueNames
2546 If set, you will not be permitted to change the name of an element to
2547 match that of another element.
2549 @item ToggleSnapPin
2550 If set, pin centers and pad end points are treated as additional grid
2551 points that the cursor can snap to.
2553 @item ToggleLocalRef
2554 If set, the mark is automatically set to the beginning of any move, so
2555 you can see the relative distance you've moved.
2557 @item ToggleThindraw
2558 If set, objects on the screen are drawn as outlines (lines are drawn
2559 as center-lines). This lets you see line endpoints hidden under pins,
2560 for example.
2562 @item ToggleThindrawPoly
2563 If set, polygons on the screen are drawn as outlines.
2565 @item ToggleShowDRC
2566 If set, pending objects (i.e. lines you're in the process of drawing)
2567 will be drawn with an outline showing how far away from other copper
2568 you need to be.
2570 @item ToggleLiveRoute
2571 If set, the progress of the autorouter will be visible on the screen.
2573 @item ToggleAutoDRC
2574 If set, you will not be permitted to make connections which violate
2575 the current DRC and netlist settings.
2577 @item ToggleCheckPlanes
2578 If set, lines and arcs aren't drawn, which usually leaves just the
2579 polygons. If you also disable all but the layer you're interested in,
2580 this allows you to check for isolated regions.
2582 @item ToggleOrthoMove
2583 If set, the crosshair is only allowed to move orthogonally from its
2584 previous position. I.e. you can move an element or line up, down,
2585 left, or right, but not up+left or down+right.
2587 @item ToggleName
2588 Selects whether the pinouts show the pin names or the pin numbers.
2590 @item ToggleLockNames
2591 If set, text will ignore left mouse clicks and actions that work on
2592 objects under the mouse. You can still select text with a lasso (left
2593 mouse drag) and perform actions on the selection.
2595 @item ToggleOnlyNames
2596 If set, only text will be sensitive for mouse clicks and actions that
2597 work on objects under the mouse. You can still select other objects
2598 with a lasso (left mouse drag) and perform actions on the selection.
2600 @item ToggleMask
2601 Turns the solder mask on or off.
2603 @item ToggleClearLine
2604 When set, the clear-line flag causes new lines and arcs to have their
2605 ``clear polygons'' flag set, so they won't be electrically connected
2606 to any polygons they overlap.
2608 @item ToggleFullPoly
2609 When set, the full-poly flag causes new polygons to have their
2610 ``full polygon'' flag set, so all parts of them will be displayed
2611 instead of only the biggest one.
2613 @item ToggleGrid
2614 Resets the origin of the current grid to be wherever the mouse pointer
2615 is (not where the crosshair currently is). If you provide two numbers
2616 after this, the origin is set to that coordinate.
2618 @item Grid
2619 Toggles whether the grid is displayed or not.
2621 @item Pinout
2622 Causes the pinout of the element indicated by the cursor to be
2623 displayed, usually in a separate window.
2625 @item PinOrPadName
2626 Toggles whether the names of pins, pads, or (yes) vias will be
2627 displayed. If the cursor is over an element, all of its pins and pads
2628 are affected.
2630 @end table
2632 %end-doc */
2634 static enum crosshair_shape
2635 CrosshairShapeIncrement (enum crosshair_shape shape)
2637 switch(shape)
2639 case Basic_Crosshair_Shape:
2640 shape = Union_Jack_Crosshair_Shape;
2641 break;
2642 case Union_Jack_Crosshair_Shape:
2643 shape = Dozen_Crosshair_Shape;
2644 break;
2645 case Dozen_Crosshair_Shape:
2646 shape = Crosshair_Shapes_Number;
2647 break;
2648 case Crosshair_Shapes_Number:
2649 shape = Basic_Crosshair_Shape;
2650 break;
2652 return shape;
2655 static int
2656 ActionDisplay (int argc, char **argv, Coord childX, Coord childY)
2658 char *function, *str_dir;
2659 int id;
2660 int err = 0;
2662 function = ARG (0);
2663 str_dir = ARG (1);
2665 if (function && (!str_dir || !*str_dir))
2667 switch (id = GetFunctionID (function))
2670 /* redraw layout */
2671 case F_ClearAndRedraw:
2672 case F_Redraw:
2673 Redraw ();
2674 break;
2676 /* change the displayed name of elements */
2677 case F_Value:
2678 case F_NameOnPCB:
2679 case F_Description:
2680 ELEMENT_LOOP (PCB->Data);
2682 EraseElementName (element);
2684 END_LOOP;
2685 CLEAR_FLAG (DESCRIPTIONFLAG | NAMEONPCBFLAG, PCB);
2686 switch (id)
2688 case F_Value:
2689 break;
2690 case F_NameOnPCB:
2691 SET_FLAG (NAMEONPCBFLAG, PCB);
2692 break;
2693 case F_Description:
2694 SET_FLAG (DESCRIPTIONFLAG, PCB);
2695 break;
2697 ELEMENT_LOOP (PCB->Data);
2699 DrawElementName (element);
2701 END_LOOP;
2702 Draw ();
2703 break;
2705 /* toggle line-adjust flag */
2706 case F_ToggleAllDirections:
2707 TOGGLE_FLAG (ALLDIRECTIONFLAG, PCB);
2708 AdjustAttachedObjects ();
2709 break;
2711 case F_CycleClip:
2712 notify_crosshair_change (false);
2713 if (TEST_FLAG (ALLDIRECTIONFLAG, PCB))
2715 TOGGLE_FLAG (ALLDIRECTIONFLAG, PCB);
2716 PCB->Clipping = 0;
2718 else
2719 PCB->Clipping = (PCB->Clipping + 1) % 3;
2720 AdjustAttachedObjects ();
2721 notify_crosshair_change (true);
2722 break;
2724 case F_CycleCrosshair:
2725 notify_crosshair_change (false);
2726 Crosshair.shape = CrosshairShapeIncrement(Crosshair.shape);
2727 if (Crosshair_Shapes_Number == Crosshair.shape)
2728 Crosshair.shape = Basic_Crosshair_Shape;
2729 notify_crosshair_change (true);
2730 break;
2732 case F_ToggleRubberBandMode:
2733 notify_crosshair_change (false);
2734 TOGGLE_FLAG (RUBBERBANDFLAG, PCB);
2735 notify_crosshair_change (true);
2736 break;
2738 case F_ToggleStartDirection:
2739 notify_crosshair_change (false);
2740 TOGGLE_FLAG (SWAPSTARTDIRFLAG, PCB);
2741 notify_crosshair_change (true);
2742 break;
2744 case F_ToggleUniqueNames:
2745 TOGGLE_FLAG (UNIQUENAMEFLAG, PCB);
2746 break;
2748 case F_ToggleSnapPin:
2749 notify_crosshair_change (false);
2750 TOGGLE_FLAG (SNAPPINFLAG, PCB);
2751 notify_crosshair_change (true);
2752 break;
2754 case F_ToggleLocalRef:
2755 TOGGLE_FLAG (LOCALREFFLAG, PCB);
2756 break;
2758 case F_ToggleThindraw:
2759 TOGGLE_FLAG (THINDRAWFLAG, PCB);
2760 Redraw ();
2761 break;
2763 case F_ToggleThindrawPoly:
2764 TOGGLE_FLAG (THINDRAWPOLYFLAG, PCB);
2765 Redraw ();
2766 break;
2768 case F_ToggleLockNames:
2769 TOGGLE_FLAG (LOCKNAMESFLAG, PCB);
2770 CLEAR_FLAG (ONLYNAMESFLAG, PCB);
2771 break;
2773 case F_ToggleOnlyNames:
2774 TOGGLE_FLAG (ONLYNAMESFLAG, PCB);
2775 CLEAR_FLAG (LOCKNAMESFLAG, PCB);
2776 break;
2778 case F_ToggleHideNames:
2779 TOGGLE_FLAG (HIDENAMESFLAG, PCB);
2780 Redraw ();
2781 break;
2783 case F_ToggleShowDRC:
2784 TOGGLE_FLAG (SHOWDRCFLAG, PCB);
2785 break;
2787 case F_ToggleLiveRoute:
2788 TOGGLE_FLAG (LIVEROUTEFLAG, PCB);
2789 break;
2791 case F_ToggleAutoDRC:
2792 notify_crosshair_change (false);
2793 TOGGLE_FLAG (AUTODRCFLAG, PCB);
2794 if (TEST_FLAG (AUTODRCFLAG, PCB) && Settings.Mode == LINE_MODE)
2796 if (ClearFlagOnAllObjects (true, CONNECTEDFLAG | FOUNDFLAG))
2798 IncrementUndoSerialNumber ();
2799 Draw ();
2801 if (Crosshair.AttachedLine.State != STATE_FIRST)
2803 LookupConnection (Crosshair.AttachedLine.Point1.X,
2804 Crosshair.AttachedLine.Point1.Y,
2805 true, 1, CONNECTEDFLAG, false);
2806 LookupConnection (Crosshair.AttachedLine.Point1.X,
2807 Crosshair.AttachedLine.Point1.Y,
2808 true, 1, FOUNDFLAG, true);
2811 notify_crosshair_change (true);
2812 break;
2814 case F_ToggleCheckPlanes:
2815 TOGGLE_FLAG (CHECKPLANESFLAG, PCB);
2816 Redraw ();
2817 break;
2819 case F_ToggleOrthoMove:
2820 TOGGLE_FLAG (ORTHOMOVEFLAG, PCB);
2821 break;
2823 case F_ToggleName:
2824 TOGGLE_FLAG (SHOWNUMBERFLAG, PCB);
2825 Redraw ();
2826 break;
2828 case F_ToggleMask:
2829 TOGGLE_FLAG (SHOWMASKFLAG, PCB);
2830 Redraw ();
2831 break;
2833 case F_ToggleClearLine:
2834 TOGGLE_FLAG (CLEARNEWFLAG, PCB);
2835 break;
2837 case F_ToggleFullPoly:
2838 TOGGLE_FLAG (NEWFULLPOLYFLAG, PCB);
2839 break;
2841 /* shift grid alignment */
2842 case F_ToggleGrid:
2844 Coord oldGrid = PCB->Grid;
2846 PCB->Grid = 1;
2847 if (MoveCrosshairAbsolute (Crosshair.X, Crosshair.Y))
2848 notify_crosshair_change (true); /* first notify was in MoveCrosshairAbs */
2849 SetGrid (oldGrid, true);
2851 break;
2853 /* toggle displaying of the grid */
2854 case F_Grid:
2855 Settings.DrawGrid = !Settings.DrawGrid;
2856 Redraw ();
2857 break;
2859 /* display the pinout of an element */
2860 case F_Pinout:
2862 ElementType *element;
2863 void *ptrtmp;
2864 Coord x, y;
2866 gui->get_coords (_("Click on an element"), &x, &y);
2867 if ((SearchScreen
2868 (x, y, ELEMENT_TYPE, &ptrtmp,
2869 &ptrtmp, &ptrtmp)) != NO_TYPE)
2871 element = (ElementType *) ptrtmp;
2872 gui->show_item (element);
2874 break;
2877 /* toggle displaying of pin/pad/via names */
2878 case F_PinOrPadName:
2880 void *ptr1, *ptr2, *ptr3;
2881 Coord x, y;
2883 gui->get_coords(_("Click on an element"), &x, &y);
2885 switch (SearchScreen (x, y,
2886 ELEMENT_TYPE | PIN_TYPE | PAD_TYPE |
2887 VIA_TYPE, (void **) &ptr1, (void **) &ptr2,
2888 (void **) &ptr3))
2890 case ELEMENT_TYPE:
2891 PIN_LOOP ((ElementType *) ptr1);
2893 if (TEST_FLAG (DISPLAYNAMEFLAG, pin))
2894 ErasePinName (pin);
2895 else
2896 DrawPinName (pin);
2897 AddObjectToFlagUndoList (PIN_TYPE, ptr1, pin, pin);
2898 TOGGLE_FLAG (DISPLAYNAMEFLAG, pin);
2900 END_LOOP;
2901 PAD_LOOP ((ElementType *) ptr1);
2903 if (TEST_FLAG (DISPLAYNAMEFLAG, pad))
2904 ErasePadName (pad);
2905 else
2906 DrawPadName (pad);
2907 AddObjectToFlagUndoList (PAD_TYPE, ptr1, pad, pad);
2908 TOGGLE_FLAG (DISPLAYNAMEFLAG, pad);
2910 END_LOOP;
2911 SetChangedFlag (true);
2912 IncrementUndoSerialNumber ();
2913 Draw ();
2914 break;
2916 case PIN_TYPE:
2917 if (TEST_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2))
2918 ErasePinName ((PinType *) ptr2);
2919 else
2920 DrawPinName ((PinType *) ptr2);
2921 AddObjectToFlagUndoList (PIN_TYPE, ptr1, ptr2, ptr3);
2922 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2);
2923 SetChangedFlag (true);
2924 IncrementUndoSerialNumber ();
2925 Draw ();
2926 break;
2928 case PAD_TYPE:
2929 if (TEST_FLAG (DISPLAYNAMEFLAG, (PadType *) ptr2))
2930 ErasePadName ((PadType *) ptr2);
2931 else
2932 DrawPadName ((PadType *) ptr2);
2933 AddObjectToFlagUndoList (PAD_TYPE, ptr1, ptr2, ptr3);
2934 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PadType *) ptr2);
2935 SetChangedFlag (true);
2936 IncrementUndoSerialNumber ();
2937 Draw ();
2938 break;
2939 case VIA_TYPE:
2940 if (TEST_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2))
2941 EraseViaName ((PinType *) ptr2);
2942 else
2943 DrawViaName ((PinType *) ptr2);
2944 AddObjectToFlagUndoList (VIA_TYPE, ptr1, ptr2, ptr3);
2945 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2);
2946 SetChangedFlag (true);
2947 IncrementUndoSerialNumber ();
2948 Draw ();
2949 break;
2951 break;
2953 default:
2954 err = 1;
2957 else if (function && str_dir)
2959 switch (GetFunctionID (function))
2961 case F_ToggleGrid:
2962 if (argc > 2)
2964 PCB->GridOffsetX = GetValue (argv[1], NULL, NULL);
2965 PCB->GridOffsetY = GetValue (argv[2], NULL, NULL);
2966 if (Settings.DrawGrid)
2967 Redraw ();
2969 break;
2971 default:
2972 err = 1;
2973 break;
2976 else
2977 err = 1;
2979 if (err)
2980 AFAIL (display);
2982 return 0;
2985 /* --------------------------------------------------------------------------- */
2987 static const char mode_syntax[] =
2988 N_("Mode(Arc|Arrow|Copy|InsertPoint|Line|Lock|Move|None|PasteBuffer)\n"
2989 "Mode(Polygon|Rectangle|Remove|Rotate|Text|Thermal|Via)\n"
2990 "Mode(Notify|Release|Cancel|Stroke)\n"
2991 "Mode(Save|Restore)");
2993 static const char mode_help[] = N_("Change or use the tool mode.");
2995 /* %start-doc actions Mode
2997 @table @code
2999 @item Arc
3000 @itemx Arrow
3001 @itemx Copy
3002 @itemx InsertPoint
3003 @itemx Line
3004 @itemx Lock
3005 @itemx Move
3006 @itemx None
3007 @itemx PasteBuffer
3008 @itemx Polygon
3009 @itemx Rectangle
3010 @itemx Remove
3011 @itemx Rotate
3012 @itemx Text
3013 @itemx Thermal
3014 @itemx Via
3015 Select the indicated tool.
3017 @item Notify
3018 Called when you press the mouse button, or move the mouse.
3020 @item Release
3021 Called when you release the mouse button.
3023 @item Cancel
3024 Cancels any pending tool activity, allowing you to restart elsewhere.
3025 For example, this allows you to start a new line rather than attach a
3026 line to the previous line.
3028 @item Escape
3029 Similar to Cancel but calling this action a second time will return
3030 to the Arrow tool.
3032 @item Stroke
3033 If your @code{pcb} was built with libstroke, this invokes the stroke
3034 input method. If not, this will restart a drawing mode if you were
3035 drawing, else it will select objects.
3037 @item Save
3038 Remembers the current tool.
3040 @item Restore
3041 Restores the tool to the last saved tool.
3043 @end table
3045 %end-doc */
3047 static int
3048 ActionMode (int argc, char **argv, Coord x, Coord y)
3050 char *function = ARG (0);
3052 if (function)
3054 Note.X = Crosshair.X;
3055 Note.Y = Crosshair.Y;
3056 notify_crosshair_change (false);
3057 switch (GetFunctionID (function))
3059 case F_Arc:
3060 SetMode (ARC_MODE);
3061 break;
3062 case F_Arrow:
3063 SetMode (ARROW_MODE);
3064 break;
3065 case F_Copy:
3066 SetMode (COPY_MODE);
3067 break;
3068 case F_InsertPoint:
3069 SetMode (INSERTPOINT_MODE);
3070 break;
3071 case F_Line:
3072 SetMode (LINE_MODE);
3073 break;
3074 case F_Lock:
3075 SetMode (LOCK_MODE);
3076 break;
3077 case F_Move:
3078 SetMode (MOVE_MODE);
3079 break;
3080 case F_None:
3081 SetMode (NO_MODE);
3082 break;
3083 case F_Cancel:
3085 int saved_mode = Settings.Mode;
3086 SetMode (NO_MODE);
3087 SetMode (saved_mode);
3089 break;
3090 case F_Escape:
3092 switch (Settings.Mode)
3094 case VIA_MODE:
3095 case PASTEBUFFER_MODE:
3096 case TEXT_MODE:
3097 case ROTATE_MODE:
3098 case REMOVE_MODE:
3099 case MOVE_MODE:
3100 case COPY_MODE:
3101 case INSERTPOINT_MODE:
3102 case RUBBERBANDMOVE_MODE:
3103 case THERMAL_MODE:
3104 case LOCK_MODE:
3105 SetMode (NO_MODE);
3106 SetMode (ARROW_MODE);
3107 break;
3109 case LINE_MODE:
3110 if (Crosshair.AttachedLine.State == STATE_FIRST)
3111 SetMode (ARROW_MODE);
3112 else
3114 SetMode (NO_MODE);
3115 SetMode (LINE_MODE);
3117 break;
3119 case RECTANGLE_MODE:
3120 if (Crosshair.AttachedBox.State == STATE_FIRST)
3121 SetMode (ARROW_MODE);
3122 else
3124 SetMode (NO_MODE);
3125 SetMode (RECTANGLE_MODE);
3127 break;
3129 case POLYGON_MODE:
3130 if (Crosshair.AttachedLine.State == STATE_FIRST)
3131 SetMode (ARROW_MODE);
3132 else
3134 SetMode (NO_MODE);
3135 SetMode (POLYGON_MODE);
3137 break;
3139 case POLYGONHOLE_MODE:
3140 if (Crosshair.AttachedLine.State == STATE_FIRST)
3141 SetMode (ARROW_MODE);
3142 else
3144 SetMode (NO_MODE);
3145 SetMode (POLYGONHOLE_MODE);
3147 break;
3149 case ARC_MODE:
3150 if (Crosshair.AttachedBox.State == STATE_FIRST)
3151 SetMode (ARROW_MODE);
3152 else
3154 SetMode (NO_MODE);
3155 SetMode (ARC_MODE);
3157 break;
3159 case ARROW_MODE:
3160 break;
3162 default:
3163 break;
3166 break;
3168 case F_Notify:
3169 NotifyMode ();
3170 break;
3171 case F_PasteBuffer:
3172 SetMode (PASTEBUFFER_MODE);
3173 break;
3174 case F_Polygon:
3175 SetMode (POLYGON_MODE);
3176 break;
3177 case F_PolygonHole:
3178 SetMode (POLYGONHOLE_MODE);
3179 break;
3180 #ifndef HAVE_LIBSTROKE
3181 case F_Release:
3182 ReleaseMode ();
3183 break;
3184 #else
3185 case F_Release:
3186 if (mid_stroke)
3187 FinishStroke ();
3188 else
3189 ReleaseMode ();
3190 break;
3191 #endif
3192 case F_Remove:
3193 SetMode (REMOVE_MODE);
3194 break;
3195 case F_Rectangle:
3196 SetMode (RECTANGLE_MODE);
3197 break;
3198 case F_Rotate:
3199 SetMode (ROTATE_MODE);
3200 break;
3201 case F_Stroke:
3202 #ifdef HAVE_LIBSTROKE
3203 mid_stroke = true;
3204 StrokeBox.X1 = Crosshair.X;
3205 StrokeBox.Y1 = Crosshair.Y;
3206 break;
3207 #else
3208 /* Handle middle mouse button restarts of drawing mode. If not in
3209 | a drawing mode, middle mouse button will select objects.
3211 if (Settings.Mode == LINE_MODE
3212 && Crosshair.AttachedLine.State != STATE_FIRST)
3214 SetMode (LINE_MODE);
3216 else if (Settings.Mode == ARC_MODE
3217 && Crosshair.AttachedBox.State != STATE_FIRST)
3218 SetMode (ARC_MODE);
3219 else if (Settings.Mode == RECTANGLE_MODE
3220 && Crosshair.AttachedBox.State != STATE_FIRST)
3221 SetMode (RECTANGLE_MODE);
3222 else if (Settings.Mode == POLYGON_MODE
3223 && Crosshair.AttachedLine.State != STATE_FIRST)
3224 SetMode (POLYGON_MODE);
3225 else
3227 SaveMode ();
3228 saved_mode = true;
3229 SetMode (ARROW_MODE);
3230 NotifyMode ();
3232 break;
3233 #endif
3234 case F_Text:
3235 SetMode (TEXT_MODE);
3236 break;
3237 case F_Thermal:
3238 SetMode (THERMAL_MODE);
3239 break;
3240 case F_Via:
3241 SetMode (VIA_MODE);
3242 break;
3244 case F_Restore: /* restore the last saved mode */
3245 RestoreMode ();
3246 break;
3248 case F_Save: /* save currently selected mode */
3249 SaveMode ();
3250 break;
3252 notify_crosshair_change (true);
3253 return 0;
3256 AFAIL (mode);
3259 /* --------------------------------------------------------------------------- */
3261 static const char removeselected_syntax[] = N_("RemoveSelected()");
3263 static const char removeselected_help[] = N_("Removes any selected objects.");
3265 /* %start-doc actions RemoveSelected
3267 %end-doc */
3269 static int
3270 ActionRemoveSelected (int argc, char **argv, Coord x, Coord y)
3272 if (RemoveSelected ())
3273 SetChangedFlag (true);
3274 return 0;
3277 /* --------------------------------------------------------------------------- */
3279 static const char renumber_syntax[] = N_("Renumber()\n"
3280 "Renumber(filename)");
3282 static const char renumber_help[] =
3283 N_("Renumber all elements. The changes will be recorded to filename\n"
3284 "for use in backannotating these changes to the schematic.");
3286 /* %start-doc actions Renumber
3288 %end-doc */
3290 static int
3291 ActionRenumber (int argc, char **argv, Coord x, Coord y)
3293 bool changed = false;
3294 ElementType **element_list;
3295 ElementType **locked_element_list;
3296 unsigned int i, j, k, cnt, lock_cnt;
3297 unsigned int tmpi;
3298 size_t sz;
3299 char *tmps;
3300 char *name;
3301 FILE *out;
3302 static char * default_file = NULL;
3303 size_t cnt_list_sz = 100;
3304 struct _cnt_list
3306 char *name;
3307 unsigned int cnt;
3308 } *cnt_list;
3309 char **was, **is, *pin;
3310 unsigned int c_cnt = 0;
3311 int unique, ok;
3312 int free_name = 0;
3314 if (argc < 1)
3317 * We deal with the case where name already exists in this
3318 * function so the GUI doesn't need to deal with it
3320 name = gui->fileselect (_("Save Renumber Annotation File As ..."),
3321 _("Choose a file to record the renumbering to.\n"
3322 "This file may be used to back annotate the\n"
3323 "change to the schematics.\n"),
3324 default_file, ".eco", "eco",
3327 free_name = 1;
3329 else
3330 name = argv[0];
3332 if (default_file)
3334 free (default_file);
3335 default_file = NULL;
3338 if (name && *name)
3340 default_file = strdup (name);
3343 if ((out = fopen (name, "r")))
3345 fclose (out);
3346 if (!gui->confirm_dialog (_("File exists! Ok to overwrite?"), 0))
3348 if (free_name && name)
3349 free (name);
3350 return 0;
3354 if ((out = fopen (name, "w")) == NULL)
3356 Message (_("Could not open %s\n"), name);
3357 if (free_name && name)
3358 free (name);
3359 return 1;
3362 if (free_name && name)
3363 free (name);
3365 fprintf (out, "*COMMENT* PCB Annotation File\n");
3366 fprintf (out, "*FILEVERSION* 20061031\n");
3369 * Make a first pass through all of the elements and sort them out
3370 * by location on the board. While here we also collect a list of
3371 * locked elements.
3373 * We'll actually renumber things in the 2nd pass.
3375 element_list = (ElementType **)calloc (PCB->Data->ElementN, sizeof (ElementType *));
3376 locked_element_list = (ElementType **)calloc (PCB->Data->ElementN, sizeof (ElementType *));
3377 was = (char **)calloc (PCB->Data->ElementN, sizeof (char *));
3378 is = (char **)calloc (PCB->Data->ElementN, sizeof (char *));
3379 if (element_list == NULL || locked_element_list == NULL || was == NULL
3380 || is == NULL)
3382 fprintf (stderr, "calloc() failed in %s\n", __FUNCTION__);
3383 exit (1);
3387 cnt = 0;
3388 lock_cnt = 0;
3389 ELEMENT_LOOP (PCB->Data);
3391 if (TEST_FLAG (LOCKFLAG, element->Name) || TEST_FLAG (LOCKFLAG, element))
3394 * add to the list of locked elements which we won't try to
3395 * renumber and whose reference designators are now reserved.
3397 pcb_fprintf (out,
3398 "*WARN* Element \"%s\" at %$md is locked and will not be renumbered.\n",
3399 UNKNOWN (NAMEONPCB_NAME (element)), element->MarkX, element->MarkY);
3400 locked_element_list[lock_cnt] = element;
3401 lock_cnt++;
3404 else
3406 /* count of devices which will be renumbered */
3407 cnt++;
3409 /* search for correct position in the list */
3410 i = 0;
3411 while (element_list[i] && element->MarkY > element_list[i]->MarkY)
3412 i++;
3415 * We have found the position where we have the first element that
3416 * has the same Y value or a lower Y value. Now move forward if
3417 * needed through the X values
3419 while (element_list[i]
3420 && element->MarkY == element_list[i]->MarkY
3421 && element->MarkX > element_list[i]->MarkX)
3422 i++;
3424 for (j = cnt - 1; j > i; j--)
3426 element_list[j] = element_list[j - 1];
3428 element_list[i] = element;
3431 END_LOOP;
3435 * Now that the elements are sorted by board position, we go through
3436 * and renumber them.
3440 * turn off the flag which requires unique names so it doesn't get
3441 * in our way. When we're done with the renumber we will have unique
3442 * names.
3444 unique = TEST_FLAG (UNIQUENAMEFLAG, PCB);
3445 CLEAR_FLAG (UNIQUENAMEFLAG, PCB);
3447 cnt_list = (struct _cnt_list *)calloc (cnt_list_sz, sizeof (struct _cnt_list));
3448 for (i = 0; i < cnt; i++)
3450 /* If there is no refdes, maybe just spit out a warning */
3451 if (NAMEONPCB_NAME (element_list[i]))
3453 /* figure out the prefix */
3454 tmps = strdup (NAMEONPCB_NAME (element_list[i]));
3455 j = 0;
3456 while (tmps[j] && (tmps[j] < '0' || tmps[j] > '9')
3457 && tmps[j] != '?')
3458 j++;
3459 tmps[j] = '\0';
3461 /* check the counter for this prefix */
3462 for (j = 0;
3463 cnt_list[j].name && (strcmp (cnt_list[j].name, tmps) != 0)
3464 && j < cnt_list_sz; j++);
3466 /* grow the list if needed */
3467 if (j == cnt_list_sz)
3469 cnt_list_sz += 100;
3470 cnt_list = (struct _cnt_list *)realloc (cnt_list, cnt_list_sz);
3471 if (cnt_list == NULL)
3473 fprintf (stderr, _("realloc failed() in %s\n"), __FUNCTION__);
3474 exit (1);
3476 /* zero out the memory that we added */
3477 for (tmpi = j; tmpi < cnt_list_sz; tmpi++)
3479 cnt_list[tmpi].name = NULL;
3480 cnt_list[tmpi].cnt = 0;
3485 * start a new counter if we don't have a counter for this
3486 * prefix
3488 if (!cnt_list[j].name)
3490 cnt_list[j].name = strdup (tmps);
3491 cnt_list[j].cnt = 0;
3495 * check to see if the new refdes is already used by a
3496 * locked element
3500 ok = 1;
3501 cnt_list[j].cnt++;
3502 free (tmps);
3504 /* space for the prefix plus 1 digit plus the '\0' */
3505 sz = strlen (cnt_list[j].name) + 2;
3507 /* and 1 more per extra digit needed to hold the number */
3508 tmpi = cnt_list[j].cnt;
3509 while (tmpi > 10)
3511 sz++;
3512 tmpi = tmpi / 10;
3514 tmps = (char *)malloc (sz * sizeof (char));
3515 sprintf (tmps, "%s%d", cnt_list[j].name, (int) cnt_list[j].cnt);
3518 * now compare to the list of reserved (by locked
3519 * elements) names
3521 for (k = 0; k < lock_cnt; k++)
3523 if (strcmp
3524 (UNKNOWN (NAMEONPCB_NAME (locked_element_list[k])),
3525 tmps) == 0)
3527 ok = 0;
3528 break;
3533 while (!ok);
3535 if (strcmp (tmps, NAMEONPCB_NAME (element_list[i])) != 0)
3537 fprintf (out, "*RENAME* \"%s\" \"%s\"\n",
3538 NAMEONPCB_NAME (element_list[i]), tmps);
3540 /* add this rename to our table of renames so we can update the netlist */
3541 was[c_cnt] = strdup (NAMEONPCB_NAME (element_list[i]));
3542 is[c_cnt] = strdup (tmps);
3543 c_cnt++;
3545 AddObjectToChangeNameUndoList (ELEMENT_TYPE, NULL, NULL,
3546 element_list[i],
3547 NAMEONPCB_NAME (element_list
3548 [i]));
3550 ChangeObjectName (ELEMENT_TYPE, element_list[i], NULL, NULL,
3551 tmps);
3552 changed = true;
3554 /* we don't free tmps in this case because it is used */
3556 else
3557 free (tmps);
3559 else
3561 pcb_fprintf (out, "*WARN* Element at %$md has no name.\n",
3562 element_list[i]->MarkX, element_list[i]->MarkY);
3567 fclose (out);
3569 /* restore the unique flag setting */
3570 if (unique)
3571 SET_FLAG (UNIQUENAMEFLAG, PCB);
3573 if (changed)
3576 /* update the netlist */
3577 AddNetlistLibToUndoList (&(PCB->NetlistLib));
3579 /* iterate over each net */
3580 for (i = 0; i < PCB->NetlistLib.MenuN; i++)
3583 /* iterate over each pin on the net */
3584 for (j = 0; j < PCB->NetlistLib.Menu[i].EntryN; j++)
3587 /* figure out the pin number part from strings like U3-21 */
3588 tmps = strdup (PCB->NetlistLib.Menu[i].Entry[j].ListEntry);
3589 for (k = 0; tmps[k] && tmps[k] != '-'; k++);
3590 tmps[k] = '\0';
3591 pin = tmps + k + 1;
3593 /* iterate over the list of changed reference designators */
3594 for (k = 0; k < c_cnt; k++)
3597 * if the pin needs to change, change it and quit
3598 * searching in the list.
3600 if (strcmp (tmps, was[k]) == 0)
3602 free (PCB->NetlistLib.Menu[i].Entry[j].ListEntry);
3603 PCB->NetlistLib.Menu[i].Entry[j].ListEntry =
3604 (char *)malloc ((strlen (is[k]) + strlen (pin) +
3605 2) * sizeof (char));
3606 sprintf (PCB->NetlistLib.Menu[i].Entry[j].ListEntry,
3607 "%s-%s", is[k], pin);
3608 k = c_cnt;
3612 free (tmps);
3615 for (k = 0; k < c_cnt; k++)
3617 free (was[k]);
3618 free (is[k]);
3621 NetlistChanged (0);
3622 IncrementUndoSerialNumber ();
3623 SetChangedFlag (true);
3626 free (locked_element_list);
3627 free (element_list);
3628 free (cnt_list);
3629 free (is);
3630 free (was);
3631 return 0;
3635 /* --------------------------------------------------------------------------- */
3637 static const char ripup_syntax[] = N_("RipUp(All|Selected|Element)");
3639 static const char ripup_help[] =
3640 N_("Ripup auto-routed tracks, or convert an element to parts.");
3642 /* %start-doc actions RipUp
3644 @table @code
3646 @item All
3647 Removes all lines and vias which were created by the autorouter.
3649 @item Selected
3650 Removes all selected lines and vias which were created by the
3651 autorouter.
3653 @item Element
3654 Converts the element under the cursor to parts (vias and lines). Note
3655 that this uses the highest numbered paste buffer.
3657 @end table
3659 %end-doc */
3661 static int
3662 ActionRipUp (int argc, char **argv, Coord x, Coord y)
3664 char *function = ARG (0);
3665 bool changed = false;
3667 if (function)
3669 switch (GetFunctionID (function))
3671 case F_All:
3672 ALLLINE_LOOP (PCB->Data);
3674 if (TEST_FLAG (AUTOFLAG, line) && !TEST_FLAG (LOCKFLAG, line))
3676 RemoveObject (LINE_TYPE, layer, line, line);
3677 changed = true;
3680 ENDALL_LOOP;
3681 ALLARC_LOOP (PCB->Data);
3683 if (TEST_FLAG (AUTOFLAG, arc) && !TEST_FLAG (LOCKFLAG, arc))
3685 RemoveObject (ARC_TYPE, layer, arc, arc);
3686 changed = true;
3689 ENDALL_LOOP;
3690 VIA_LOOP (PCB->Data);
3692 if (TEST_FLAG (AUTOFLAG, via) && !TEST_FLAG (LOCKFLAG, via))
3694 RemoveObject (VIA_TYPE, via, via, via);
3695 changed = true;
3698 END_LOOP;
3700 if (changed)
3702 IncrementUndoSerialNumber ();
3703 SetChangedFlag (true);
3705 break;
3706 case F_Selected:
3707 VISIBLELINE_LOOP (PCB->Data);
3709 if (TEST_FLAGS (AUTOFLAG | SELECTEDFLAG, line)
3710 && !TEST_FLAG (LOCKFLAG, line))
3712 RemoveObject (LINE_TYPE, layer, line, line);
3713 changed = true;
3716 ENDALL_LOOP;
3717 if (PCB->ViaOn)
3718 VIA_LOOP (PCB->Data);
3720 if (TEST_FLAGS (AUTOFLAG | SELECTEDFLAG, via)
3721 && !TEST_FLAG (LOCKFLAG, via))
3723 RemoveObject (VIA_TYPE, via, via, via);
3724 changed = true;
3727 END_LOOP;
3728 if (changed)
3730 IncrementUndoSerialNumber ();
3731 SetChangedFlag (true);
3733 break;
3734 case F_Element:
3736 void *ptr1, *ptr2, *ptr3;
3738 if (SearchScreen (Crosshair.X, Crosshair.Y, ELEMENT_TYPE,
3739 &ptr1, &ptr2, &ptr3) != NO_TYPE)
3741 Note.Buffer = Settings.BufferNumber;
3742 SetBufferNumber (MAX_BUFFER - 1);
3743 ClearBuffer (PASTEBUFFER);
3744 CopyObjectToBuffer (PASTEBUFFER->Data, PCB->Data,
3745 ELEMENT_TYPE, ptr1, ptr2, ptr3);
3746 SmashBufferElement (PASTEBUFFER);
3747 PASTEBUFFER->X = 0;
3748 PASTEBUFFER->Y = 0;
3749 SaveUndoSerialNumber ();
3750 EraseObject (ELEMENT_TYPE, ptr1, ptr1);
3751 MoveObjectToRemoveUndoList (ELEMENT_TYPE, ptr1, ptr2, ptr3);
3752 RestoreUndoSerialNumber ();
3753 CopyPastebufferToLayout (0, 0);
3754 SetBufferNumber (Note.Buffer);
3755 SetChangedFlag (true);
3758 break;
3761 return 0;
3764 /* --------------------------------------------------------------------------- */
3766 static const char addrats_syntax[] = N_("AddRats(AllRats|SelectedRats|Close)");
3768 static const char addrats_help[] =
3769 N_("Add one or more rat lines to the board.");
3771 /* %start-doc actions AddRats
3773 @table @code
3775 @item AllRats
3776 Create rat lines for all loaded nets that aren't already connected on
3777 with copper.
3779 @item SelectedRats
3780 Similarly, but only add rat lines for nets connected to selected pins
3781 and pads.
3783 @item Close
3784 Selects the shortest unselected rat on the board.
3786 @end table
3788 %end-doc */
3790 static int
3791 ActionAddRats (int argc, char **argv, Coord x, Coord y)
3793 char *function = ARG (0);
3794 RatType *shorty;
3795 float len, small;
3797 if (function)
3799 if (Settings.RatWarn)
3800 ClearWarnings ();
3801 switch (GetFunctionID (function))
3803 case F_AllRats:
3804 if (AddAllRats (false, NULL))
3805 SetChangedFlag (true);
3806 break;
3807 case F_SelectedRats:
3808 case F_Selected:
3809 if (AddAllRats (true, NULL))
3810 SetChangedFlag (true);
3811 break;
3812 case F_Close:
3813 small = SQUARE (MAX_COORD);
3814 shorty = NULL;
3815 RAT_LOOP (PCB->Data);
3817 if (TEST_FLAG (SELECTEDFLAG, line))
3818 continue;
3819 len = SQUARE (line->Point1.X - line->Point2.X) +
3820 SQUARE (line->Point1.Y - line->Point2.Y);
3821 if (len < small)
3823 small = len;
3824 shorty = line;
3827 END_LOOP;
3828 if (shorty)
3830 AddObjectToFlagUndoList (RATLINE_TYPE, shorty, shorty, shorty);
3831 SET_FLAG (SELECTEDFLAG, shorty);
3832 DrawRat (shorty);
3833 Draw ();
3834 CenterDisplay ((shorty->Point2.X + shorty->Point1.X) / 2,
3835 (shorty->Point2.Y + shorty->Point1.Y) / 2,
3836 false);
3838 break;
3841 return 0;
3844 /* --------------------------------------------------------------------------- */
3846 static const char delete_syntax[] =
3847 N_("Delete(Object|Selected)\n"
3848 "Delete(AllRats|SelectedRats)");
3850 static const char delete_help[] = N_("Delete stuff.");
3852 /* %start-doc actions Delete
3854 %end-doc */
3856 static int
3857 ActionDelete (int argc, char **argv, Coord x, Coord y)
3859 char *function = ARG (0);
3860 int id = GetFunctionID (function);
3862 Note.X = Crosshair.X;
3863 Note.Y = Crosshair.Y;
3865 if (id == -1) /* no arg */
3867 if (RemoveSelected() == false)
3868 id = F_Object;
3871 switch (id)
3873 case F_Object:
3874 SaveMode();
3875 SetMode(REMOVE_MODE);
3876 NotifyMode();
3877 RestoreMode();
3878 break;
3879 case F_Selected:
3880 RemoveSelected();
3881 break;
3882 case F_AllRats:
3883 if (DeleteRats (false))
3884 SetChangedFlag (true);
3885 break;
3886 case F_SelectedRats:
3887 if (DeleteRats (true))
3888 SetChangedFlag (true);
3889 break;
3892 return 0;
3895 /* --------------------------------------------------------------------------- */
3897 static const char deleterats_syntax[] =
3898 N_("DeleteRats(AllRats|Selected|SelectedRats)");
3900 static const char deleterats_help[] = N_("Delete rat lines.");
3902 /* %start-doc actions DeleteRats
3904 %end-doc */
3906 static int
3907 ActionDeleteRats (int argc, char **argv, Coord x, Coord y)
3909 char *function = ARG (0);
3910 if (function)
3912 if (Settings.RatWarn)
3913 ClearWarnings ();
3914 switch (GetFunctionID (function))
3916 case F_AllRats:
3917 if (DeleteRats (false))
3918 SetChangedFlag (true);
3919 break;
3920 case F_SelectedRats:
3921 case F_Selected:
3922 if (DeleteRats (true))
3923 SetChangedFlag (true);
3924 break;
3927 return 0;
3930 /* --------------------------------------------------------------------------- */
3932 static const char autoplace_syntax[] = N_("AutoPlaceSelected()");
3934 static const char autoplace_help[] = N_("Auto-place selected components.");
3936 /* %start-doc actions AutoPlaceSelected
3938 Attempts to re-arrange the selected components such that the nets
3939 connecting them are minimized. Note that you cannot undo this.
3941 %end-doc */
3943 static int
3944 ActionAutoPlaceSelected (int argc, char **argv, Coord x, Coord y)
3946 hid_action("Busy");
3947 if (gui->confirm_dialog (_("Auto-placement can NOT be undone.\n"
3948 "Do you want to continue anyway?\n"), 0))
3950 if (AutoPlaceSelected ())
3951 SetChangedFlag (true);
3953 return 0;
3956 /* --------------------------------------------------------------------------- */
3958 static const char autoroute_syntax[] = N_("AutoRoute(AllRats|SelectedRats)");
3960 static const char autoroute_help[] = N_("Auto-route some or all rat lines.");
3962 /* %start-doc actions AutoRoute
3964 @table @code
3966 @item AllRats
3967 Attempt to autoroute all rats.
3969 @item SelectedRats
3970 Attempt to autoroute the selected rats.
3972 @end table
3974 Before autorouting, it's important to set up a few things. First,
3975 make sure any layers you aren't using are disabled, else the
3976 autorouter may use them. Next, make sure the current line and via
3977 styles are set accordingly. Last, make sure "new lines clear
3978 polygons" is set, in case you eventually want to add a copper pour.
3980 Autorouting takes a while. During this time, the program may not be
3981 responsive.
3983 %end-doc */
3985 static int
3986 ActionAutoRoute (int argc, char **argv, Coord x, Coord y)
3988 char *function = ARG (0);
3989 hid_action("Busy");
3990 if (function) /* one parameter */
3992 switch (GetFunctionID (function))
3994 case F_AllRats:
3995 if (AutoRoute (false))
3996 SetChangedFlag (true);
3997 break;
3998 case F_SelectedRats:
3999 case F_Selected:
4000 if (AutoRoute (true))
4001 SetChangedFlag (true);
4002 break;
4005 return 0;
4008 /* --------------------------------------------------------------------------- */
4010 static const char markcrosshair_syntax[] =
4011 N_("MarkCrosshair()\n"
4012 "MarkCrosshair(Center)");
4014 static const char markcrosshair_help[] = N_("Set/Reset the Crosshair mark.");
4016 /* %start-doc actions MarkCrosshair
4018 The ``mark'' is a small X-shaped target on the display which is
4019 treated like a second origin (the normal origin is the upper let
4020 corner of the board). The GUI will display a second set of
4021 coordinates for this mark, which tells you how far you are from it.
4023 If no argument is given, the mark is toggled - disabled if it was
4024 enabled, or enabled at the current cursor position of disabled. If
4025 the @code{Center} argument is given, the mark is moved to the current
4026 cursor location.
4028 %end-doc */
4030 static int
4031 ActionMarkCrosshair (int argc, char **argv, Coord x, Coord y)
4033 char *function = ARG (0);
4034 if (!function || !*function)
4036 if (Marked.status)
4038 notify_mark_change (false);
4039 Marked.status = false;
4040 notify_mark_change (true);
4042 else
4044 notify_mark_change (false);
4045 Marked.status = false;
4046 Marked.status = true;
4047 Marked.X = Crosshair.X;
4048 Marked.Y = Crosshair.Y;
4049 notify_mark_change (true);
4052 else if (GetFunctionID (function) == F_Center)
4054 notify_mark_change (false);
4055 Marked.status = true;
4056 Marked.X = Crosshair.X;
4057 Marked.Y = Crosshair.Y;
4058 notify_mark_change (true);
4060 return 0;
4063 /* --------------------------------------------------------------------------- */
4065 static const char changesize_syntax[] =
4066 N_("ChangeSize(Object, delta)\n"
4067 "ChangeSize(SelectedObjects|Selected, delta)\n"
4068 "ChangeSize(SelectedLines|SelectedPins|SelectedVias, delta)\n"
4069 "ChangeSize(SelectedPads|SelectedTexts|SelectedNames, delta)\n"
4070 "ChangeSize(SelectedElements, delta)");
4072 static const char changesize_help[] = N_("Changes the size of objects.");
4074 /* %start-doc actions ChangeSize
4076 For lines and arcs, this changes the width. For pins and vias, this
4077 changes the overall diameter of the copper annulus. For pads, this
4078 changes the width and, indirectly, the length. For texts and names,
4079 this changes the scaling factor. For elements, this changes the width
4080 of the silk layer lines and arcs for this element.
4082 %end-doc */
4084 static int
4085 ActionChangeSize (int argc, char **argv, Coord x, Coord y)
4087 char *function = ARG (0);
4088 char *delta = ARG (1);
4089 char *units = ARG (2);
4090 bool absolute; /* indicates if absolute size is given */
4091 Coord value;
4093 if (function && delta)
4095 value = GetValue (delta, units, &absolute);
4096 if (value == 0)
4097 value = delta[0] == '-' ? -Settings.increments->size
4098 : Settings.increments->size;
4099 switch (GetFunctionID (function))
4101 case F_Object:
4103 int type;
4104 void *ptr1, *ptr2, *ptr3;
4106 if ((type =
4107 SearchScreen (Crosshair.X, Crosshair.Y, CHANGESIZE_TYPES,
4108 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4109 if (TEST_FLAG (LOCKFLAG, (PinType *) ptr2))
4110 Message (_("Sorry, the object is locked\n"));
4111 if (ChangeObjectSize (type, ptr1, ptr2, ptr3, value, absolute))
4112 SetChangedFlag (true);
4113 break;
4116 case F_SelectedVias:
4117 if (ChangeSelectedSize (VIA_TYPE, value, absolute))
4118 SetChangedFlag (true);
4119 break;
4121 case F_SelectedPins:
4122 if (ChangeSelectedSize (PIN_TYPE, value, absolute))
4123 SetChangedFlag (true);
4124 break;
4126 case F_SelectedPads:
4127 if (ChangeSelectedSize (PAD_TYPE, value, absolute))
4128 SetChangedFlag (true);
4129 break;
4131 case F_SelectedArcs:
4132 if (ChangeSelectedSize (ARC_TYPE, value, absolute))
4133 SetChangedFlag (true);
4134 break;
4136 case F_SelectedLines:
4137 if (ChangeSelectedSize (LINE_TYPE, value, absolute))
4138 SetChangedFlag (true);
4139 break;
4141 case F_SelectedTexts:
4142 if (ChangeSelectedSize (TEXT_TYPE, value, absolute))
4143 SetChangedFlag (true);
4144 break;
4146 case F_SelectedNames:
4147 if (ChangeSelectedSize (ELEMENTNAME_TYPE, value, absolute))
4148 SetChangedFlag (true);
4149 break;
4151 case F_SelectedElements:
4152 if (ChangeSelectedSize (ELEMENT_TYPE, value, absolute))
4153 SetChangedFlag (true);
4154 break;
4156 case F_Selected:
4157 case F_SelectedObjects:
4158 if (ChangeSelectedSize (CHANGESIZE_TYPES, value, absolute))
4159 SetChangedFlag (true);
4160 break;
4163 return 0;
4166 /* --------------------------------------------------------------------------- */
4168 static const char changedrillsize_syntax[] =
4169 N_("ChangeDrillSize(Object, delta)\n"
4170 "ChangeDrillSize(SelectedPins|SelectedVias|Selected|SelectedObjects, delta)");
4172 static const char changedrillsize_help[] =
4173 N_("Changes the drilling hole size of objects.");
4175 /* %start-doc actions ChangeDrillSize
4177 %end-doc */
4179 static int
4180 ActionChange2ndSize (int argc, char **argv, Coord x, Coord y)
4182 char *function = ARG (0);
4183 char *delta = ARG (1);
4184 char *units = ARG (2);
4185 bool absolute;
4186 Coord value;
4188 if (function && delta)
4190 value = GetValue (delta, units, &absolute);
4191 switch (GetFunctionID (function))
4193 case F_Object:
4195 int type;
4196 void *ptr1, *ptr2, *ptr3;
4198 gui->get_coords (_("Select an Object"), &x, &y);
4199 if ((type =
4200 SearchScreen (x, y, CHANGE2NDSIZE_TYPES,
4201 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4202 if (ChangeObject2ndSize
4203 (type, ptr1, ptr2, ptr3, value, absolute, true))
4204 SetChangedFlag (true);
4205 break;
4208 case F_SelectedVias:
4209 if (ChangeSelected2ndSize (VIA_TYPE, value, absolute))
4210 SetChangedFlag (true);
4211 break;
4213 case F_SelectedPins:
4214 if (ChangeSelected2ndSize (PIN_TYPE, value, absolute))
4215 SetChangedFlag (true);
4216 break;
4217 case F_Selected:
4218 case F_SelectedObjects:
4219 if (ChangeSelected2ndSize (PIN_TYPES, value, absolute))
4220 SetChangedFlag (true);
4221 break;
4224 return 0;
4227 /* --------------------------------------------------------------------------- */
4229 static const char changeclearsize_syntax[] =
4230 N_("ChangeClearSize(Object, delta)\n"
4231 "ChangeClearSize(SelectedPins|SelectedPads|SelectedVias, delta)\n"
4232 "ChangeClearSize(SelectedLines|SelectedArcs, delta\n"
4233 "ChangeClearSize(Selected|SelectedObjects, delta)");
4235 static const char changeclearsize_help[] =
4236 N_("Changes the clearance size of objects.");
4238 /* %start-doc actions ChangeClearSize
4240 If the solder mask is currently showing, this action changes the
4241 solder mask clearance. If the mask is not showing, this action
4242 changes the polygon clearance.
4244 %end-doc */
4246 static int
4247 ActionChangeClearSize (int argc, char **argv, Coord x, Coord y)
4249 char *function = ARG (0);
4250 char *delta = ARG (1);
4251 char *units = ARG (2);
4252 bool absolute;
4253 Coord value;
4255 if (function && delta)
4257 value = 2 * GetValue (delta, units, &absolute);
4258 if (value == 0)
4259 value = delta[0] == '-' ? -Settings.increments->clear
4260 : Settings.increments->clear;
4261 switch (GetFunctionID (function))
4263 case F_Object:
4265 int type;
4266 void *ptr1, *ptr2, *ptr3;
4268 gui->get_coords (_("Select an Object"), &x, &y);
4269 if ((type =
4270 SearchScreen (x, y,
4271 CHANGECLEARSIZE_TYPES, &ptr1, &ptr2,
4272 &ptr3)) != NO_TYPE)
4273 if (ChangeObjectClearSize (type, ptr1, ptr2, ptr3, value, absolute))
4274 SetChangedFlag (true);
4275 break;
4277 case F_SelectedVias:
4278 if (ChangeSelectedClearSize (VIA_TYPE, value, absolute))
4279 SetChangedFlag (true);
4280 break;
4281 case F_SelectedPads:
4282 if (ChangeSelectedClearSize (PAD_TYPE, value, absolute))
4283 SetChangedFlag (true);
4284 break;
4285 case F_SelectedPins:
4286 if (ChangeSelectedClearSize (PIN_TYPE, value, absolute))
4287 SetChangedFlag (true);
4288 break;
4289 case F_SelectedLines:
4290 if (ChangeSelectedClearSize (LINE_TYPE, value, absolute))
4291 SetChangedFlag (true);
4292 break;
4293 case F_SelectedArcs:
4294 if (ChangeSelectedClearSize (ARC_TYPE, value, absolute))
4295 SetChangedFlag (true);
4296 break;
4297 case F_Selected:
4298 case F_SelectedObjects:
4299 if (ChangeSelectedClearSize (CHANGECLEARSIZE_TYPES, value, absolute))
4300 SetChangedFlag (true);
4301 break;
4304 return 0;
4307 /* --------------------------------------------------------------------------- */
4309 static const char minmaskgap_syntax[] =
4310 N_("MinMaskGap(delta)\n"
4311 "MinMaskGap(Selected, delta)");
4313 static const char minmaskgap_help[] =
4314 N_("Ensures the mask is a minimum distance from pins and pads.");
4316 /* %start-doc actions MinMaskGap
4318 Checks all specified pins and/or pads, and increases the mask if
4319 needed to ensure a minimum distance between the pin or pad edge and
4320 the mask edge.
4322 %end-doc */
4324 static int
4325 ActionMinMaskGap (int argc, char **argv, Coord x, Coord y)
4327 char *function = ARG (0);
4328 char *delta = ARG (1);
4329 char *units = ARG (2);
4330 bool absolute;
4331 Coord value;
4332 Coord thickness;
4333 int flags;
4335 if (!function)
4336 return 1;
4337 if (strcasecmp (function, "Selected") == 0)
4338 flags = SELECTEDFLAG;
4339 else
4341 units = delta;
4342 delta = function;
4343 flags = 0;
4345 value = 2 * GetValue (delta, units, &absolute);
4347 SaveUndoSerialNumber ();
4348 ELEMENT_LOOP (PCB->Data);
4350 PIN_LOOP (element);
4352 if (!TEST_FLAGS (flags, pin) || ! pin->Mask)
4353 continue;
4355 thickness = pin->DrillingHole;
4356 if (pin->Thickness > thickness)
4357 thickness = pin->Thickness;
4358 thickness += value;
4360 if (pin->Mask < thickness)
4362 ChangeObjectMaskSize (PIN_TYPE, element, pin, 0, thickness, 1);
4363 RestoreUndoSerialNumber ();
4366 END_LOOP;
4367 PAD_LOOP (element);
4369 if (!TEST_FLAGS (flags, pad) || ! pad->Mask)
4370 continue;
4371 if (pad->Mask < pad->Thickness + value)
4373 ChangeObjectMaskSize (PAD_TYPE, element, pad, 0,
4374 pad->Thickness + value, 1);
4375 RestoreUndoSerialNumber ();
4378 END_LOOP;
4380 END_LOOP;
4381 VIA_LOOP (PCB->Data);
4383 if (!TEST_FLAGS (flags, via) || ! via->Mask)
4384 continue;
4386 thickness = via->DrillingHole;
4387 if (via->Thickness > thickness)
4388 thickness = via->Thickness;
4389 thickness += value;
4391 if (via->Mask < thickness)
4393 ChangeObjectMaskSize (VIA_TYPE, via, 0, 0, thickness, 1);
4394 RestoreUndoSerialNumber ();
4397 END_LOOP;
4398 RestoreUndoSerialNumber ();
4399 IncrementUndoSerialNumber ();
4400 return 0;
4403 /* --------------------------------------------------------------------------- */
4405 static const char mincleargap_syntax[] =
4406 N_("MinClearGap(delta)\n"
4407 "MinClearGap(Selected, delta)");
4409 static const char mincleargap_help[] =
4410 N_("Ensures that polygons are a minimum distance from objects.");
4412 /* %start-doc actions MinClearGap
4414 Checks all specified objects, and increases the polygon clearance if
4415 needed to ensure a minimum distance between their edges and the
4416 polygon edges.
4418 %end-doc */
4420 static int
4421 ActionMinClearGap (int argc, char **argv, Coord x, Coord y)
4423 char *function = ARG (0);
4424 char *delta = ARG (1);
4425 char *units = ARG (2);
4426 bool absolute;
4427 Coord value;
4428 int flags;
4430 if (!function)
4431 return 1;
4432 if (strcasecmp (function, "Selected") == 0)
4433 flags = SELECTEDFLAG;
4434 else
4436 units = delta;
4437 delta = function;
4438 flags = 0;
4440 value = 2 * GetValue (delta, units, &absolute);
4442 SaveUndoSerialNumber ();
4443 ELEMENT_LOOP (PCB->Data);
4445 PIN_LOOP (element);
4447 if (!TEST_FLAGS (flags, pin))
4448 continue;
4449 if (pin->Clearance < value)
4451 ChangeObjectClearSize (PIN_TYPE, element, pin, 0,
4452 value, 1);
4453 RestoreUndoSerialNumber ();
4456 END_LOOP;
4457 PAD_LOOP (element);
4459 if (!TEST_FLAGS (flags, pad))
4460 continue;
4461 if (pad->Clearance < value)
4463 ChangeObjectClearSize (PAD_TYPE, element, pad, 0,
4464 value, 1);
4465 RestoreUndoSerialNumber ();
4468 END_LOOP;
4470 END_LOOP;
4471 VIA_LOOP (PCB->Data);
4473 if (!TEST_FLAGS (flags, via))
4474 continue;
4475 if (via->Clearance < value)
4477 ChangeObjectClearSize (VIA_TYPE, via, 0, 0, value, 1);
4478 RestoreUndoSerialNumber ();
4481 END_LOOP;
4482 ALLLINE_LOOP (PCB->Data);
4484 if (!TEST_FLAGS (flags, line))
4485 continue;
4486 if (line->Clearance < value)
4488 ChangeObjectClearSize (LINE_TYPE, layer, line, 0, value, 1);
4489 RestoreUndoSerialNumber ();
4492 ENDALL_LOOP;
4493 ALLARC_LOOP (PCB->Data);
4495 if (!TEST_FLAGS (flags, arc))
4496 continue;
4497 if (arc->Clearance < value)
4499 ChangeObjectClearSize (ARC_TYPE, layer, arc, 0, value, 1);
4500 RestoreUndoSerialNumber ();
4503 ENDALL_LOOP;
4504 RestoreUndoSerialNumber ();
4505 IncrementUndoSerialNumber ();
4506 return 0;
4509 /* --------------------------------------------------------------------------- */
4511 static const char changepinname_syntax[] =
4512 N_("ChangePinName(ElementName,PinNumber,PinName)");
4514 static const char changepinname_help[] =
4515 N_("Sets the name of a specific pin on a specific element.");
4517 /* %start-doc actions ChangePinName
4519 This can be especially useful for annotating pin names from a
4520 schematic to the layout without requiring knowledge of the pcb file
4521 format.
4523 @example
4524 ChangePinName(U3, 7, VCC)
4525 @end example
4527 %end-doc */
4529 static int
4530 ActionChangePinName (int argc, char **argv, Coord x, Coord y)
4532 int changed = 0;
4533 char *refdes, *pinnum, *pinname;
4535 if (argc != 3)
4537 AFAIL (changepinname);
4540 refdes = argv[0];
4541 pinnum = argv[1];
4542 pinname = argv[2];
4544 ELEMENT_LOOP (PCB->Data);
4546 if (NSTRCMP (refdes, NAMEONPCB_NAME (element)) == 0)
4548 PIN_LOOP (element);
4550 if (NSTRCMP (pinnum, pin->Number) == 0)
4552 AddObjectToChangeNameUndoList (PIN_TYPE, NULL, NULL,
4553 pin, pin->Name);
4555 * Note: we can't free() pin->Name first because
4556 * it is used in the undo list
4558 pin->Name = strdup (pinname);
4559 SetChangedFlag (true);
4560 changed = 1;
4563 END_LOOP;
4565 PAD_LOOP (element);
4567 if (NSTRCMP (pinnum, pad->Number) == 0)
4569 AddObjectToChangeNameUndoList (PAD_TYPE, NULL, NULL,
4570 pad, pad->Name);
4572 * Note: we can't free() pad->Name first because
4573 * it is used in the undo list
4575 pad->Name = strdup (pinname);
4576 SetChangedFlag (true);
4577 changed = 1;
4580 END_LOOP;
4583 END_LOOP;
4585 * done with our action so increment the undo # if we actually
4586 * changed anything
4588 if (changed)
4590 if (defer_updates)
4591 defer_needs_update = 1;
4592 else
4594 IncrementUndoSerialNumber ();
4595 gui->invalidate_all ();
4599 return 0;
4602 /* --------------------------------------------------------------------------- */
4604 static const char changename_syntax[] =
4605 N_("ChangeName(Object)\n"
4606 "ChangeName(Layout|Layer)");
4608 static const char changename_help[] = N_("Sets the name of objects.");
4610 /* %start-doc actions ChangeName
4612 @table @code
4614 @item Object
4615 Changes the name of the element under the cursor.
4617 @item Layout
4618 Changes the name of the layout. This is printed on the fab drawings.
4620 @item Layer
4621 Changes the name of the currently active layer.
4623 @end table
4625 %end-doc */
4628 ActionChangeName (int argc, char **argv, Coord x, Coord y)
4630 char *function = ARG (0);
4631 char *name;
4633 if (function)
4635 switch (GetFunctionID (function))
4637 /* change the name of an object */
4638 case F_Object:
4640 int type;
4641 void *ptr1, *ptr2, *ptr3;
4643 gui->get_coords (_("Select an Object"), &x, &y);
4644 if ((type =
4645 SearchScreen (x, y, CHANGENAME_TYPES,
4646 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4648 SaveUndoSerialNumber ();
4649 if (QueryInputAndChangeObjectName (type, ptr1, ptr2, ptr3))
4651 SetChangedFlag (true);
4652 if (type == ELEMENT_TYPE)
4654 RubberbandType *ptr;
4655 int i;
4657 RestoreUndoSerialNumber ();
4658 Crosshair.AttachedObject.RubberbandN = 0;
4659 LookupRatLines (type, ptr1, ptr2, ptr3);
4660 ptr = Crosshair.AttachedObject.Rubberband;
4661 for (i = 0; i < Crosshair.AttachedObject.RubberbandN;
4662 i++, ptr++)
4664 if (PCB->RatOn)
4665 EraseRat ((RatType *) ptr->Line);
4666 MoveObjectToRemoveUndoList (RATLINE_TYPE,
4667 ptr->Line, ptr->Line,
4668 ptr->Line);
4670 IncrementUndoSerialNumber ();
4671 Draw ();
4675 break;
4678 /* change the layout's name */
4679 case F_Layout:
4680 name =
4681 gui->prompt_for (_("Enter the layout name:"), EMPTY (PCB->Name));
4682 /* NB: ChangeLayoutName takes ownership of the passed memory */
4683 if (name && ChangeLayoutName (name))
4684 SetChangedFlag (true);
4685 break;
4687 /* change the name of the active layer */
4688 case F_Layer:
4689 name = gui->prompt_for (_("Enter the layer name:"),
4690 EMPTY (CURRENT->Name));
4691 /* NB: ChangeLayerName takes ownership of the passed memory */
4692 if (name && ChangeLayerName (CURRENT, name))
4693 SetChangedFlag (true);
4694 break;
4697 return 0;
4701 /* --------------------------------------------------------------------------- */
4703 static const char morphpolygon_syntax[] = N_("MorphPolygon(Object|Selected)");
4705 static const char morphpolygon_help[] =
4706 N_("Converts dead polygon islands into separate polygons.");
4708 /* %start-doc actions MorphPolygon
4710 If a polygon is divided into unconnected "islands", you can use
4711 this command to convert the otherwise disappeared islands into
4712 separate polygons. Be sure the cursor is over a portion of the
4713 polygon that remains visible. Very small islands that may flake
4714 off are automatically deleted.
4716 %end-doc */
4718 static int
4719 ActionMorphPolygon (int argc, char **argv, Coord x, Coord y)
4721 char *function = ARG (0);
4722 if (function)
4724 switch (GetFunctionID (function))
4726 case F_Object:
4728 int type;
4729 void *ptr1, *ptr2, *ptr3;
4731 gui->get_coords (_("Select an Object"), &x, &y);
4732 if ((type = SearchScreen (x, y, POLYGON_TYPE,
4733 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4735 MorphPolygon ((LayerType *) ptr1, (PolygonType *) ptr3);
4736 Draw ();
4737 IncrementUndoSerialNumber ();
4739 break;
4741 case F_Selected:
4742 case F_SelectedObjects:
4743 ALLPOLYGON_LOOP (PCB->Data);
4745 if (TEST_FLAG (SELECTEDFLAG, polygon))
4746 MorphPolygon (layer, polygon);
4748 ENDALL_LOOP;
4749 Draw ();
4750 IncrementUndoSerialNumber ();
4751 break;
4754 return 0;
4757 /* --------------------------------------------------------------------------- */
4759 static const char togglehidename_syntax[] =
4760 N_("ToggleHideName(Object|SelectedElements)");
4762 static const char togglehidename_help[] =
4763 N_("Toggles the visibility of element names.");
4765 /* %start-doc actions ToggleHideName
4767 If names are hidden you won't see them on the screen and they will not
4768 appear on the silk layer when you print the layout.
4770 %end-doc */
4772 static int
4773 ActionToggleHideName (int argc, char **argv, Coord x, Coord y)
4775 char *function = ARG (0);
4776 if (function && PCB->ElementOn)
4778 switch (GetFunctionID (function))
4780 case F_Object:
4782 int type;
4783 void *ptr1, *ptr2, *ptr3;
4785 gui->get_coords (_("Select an Object"), &x, &y);
4786 if ((type = SearchScreen (x, y, ELEMENT_TYPE,
4787 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4789 AddObjectToFlagUndoList (type, ptr1, ptr2, ptr3);
4790 EraseElementName ((ElementType *) ptr2);
4791 TOGGLE_FLAG (HIDENAMEFLAG, (ElementType *) ptr2);
4792 DrawElementName ((ElementType *) ptr2);
4793 Draw ();
4794 IncrementUndoSerialNumber ();
4796 break;
4798 case F_SelectedElements:
4799 case F_Selected:
4801 bool changed = false;
4802 ELEMENT_LOOP (PCB->Data);
4804 if ((TEST_FLAG (SELECTEDFLAG, element) ||
4805 TEST_FLAG (SELECTEDFLAG,
4806 &NAMEONPCB_TEXT (element)))
4807 && (FRONT (element) || PCB->InvisibleObjectsOn))
4809 AddObjectToFlagUndoList (ELEMENT_TYPE, element,
4810 element, element);
4811 EraseElementName (element);
4812 TOGGLE_FLAG (HIDENAMEFLAG, element);
4813 DrawElementName (element);
4814 changed = true;
4817 END_LOOP;
4818 if (changed)
4820 Draw ();
4821 IncrementUndoSerialNumber ();
4826 return 0;
4829 /* --------------------------------------------------------------------------- */
4831 static const char changejoin_syntax[] =
4832 N_("ChangeJoin(ToggleObject|SelectedLines|SelectedArcs|Selected)");
4834 static const char changejoin_help[] =
4835 N_("Changes the join (clearance through polygons) of objects.");
4837 /* %start-doc actions ChangeJoin
4839 The join flag determines whether a line or arc, drawn to intersect a
4840 polygon, electrically connects to the polygon or not. When joined,
4841 the line/arc is simply drawn over the polygon, making an electrical
4842 connection. When not joined, a gap is drawn between the line and the
4843 polygon, insulating them from each other.
4845 %end-doc */
4847 static int
4848 ActionChangeJoin (int argc, char **argv, Coord x, Coord y)
4850 char *function = ARG (0);
4851 if (function)
4853 switch (GetFunctionID (function))
4855 case F_ToggleObject:
4856 case F_Object:
4858 int type;
4859 void *ptr1, *ptr2, *ptr3;
4861 gui->get_coords (_("Select an Object"), &x, &y);
4862 if ((type =
4863 SearchScreen (x, y, CHANGEJOIN_TYPES,
4864 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4865 if (ChangeObjectJoin (type, ptr1, ptr2, ptr3))
4866 SetChangedFlag (true);
4867 break;
4870 case F_SelectedLines:
4871 if (ChangeSelectedJoin (LINE_TYPE))
4872 SetChangedFlag (true);
4873 break;
4875 case F_SelectedArcs:
4876 if (ChangeSelectedJoin (ARC_TYPE))
4877 SetChangedFlag (true);
4878 break;
4880 case F_Selected:
4881 case F_SelectedObjects:
4882 if (ChangeSelectedJoin (CHANGEJOIN_TYPES))
4883 SetChangedFlag (true);
4884 break;
4887 return 0;
4890 /* --------------------------------------------------------------------------- */
4892 static const char changesquare_syntax[] =
4893 N_("ChangeSquare(ToggleObject)\n"
4894 "ChangeSquare(SelectedElements|SelectedPins)\n"
4895 "ChangeSquare(Selected|SelectedObjects)");
4897 static const char changesquare_help[] =
4898 N_("Changes the square flag of pins and pads.");
4900 /* %start-doc actions ChangeSquare
4902 Note that @code{Pins} means both pins and pads.
4904 @pinshapes
4906 %end-doc */
4908 static int
4909 ActionChangeSquare (int argc, char **argv, Coord x, Coord y)
4911 char *function = ARG (0);
4912 if (function)
4914 switch (GetFunctionID (function))
4916 case F_ToggleObject:
4917 case F_Object:
4919 int type;
4920 void *ptr1, *ptr2, *ptr3;
4922 gui->get_coords (_("Select an Object"), &x, &y);
4923 if ((type =
4924 SearchScreen (x, y, CHANGESQUARE_TYPES,
4925 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4926 if (ChangeObjectSquare (type, ptr1, ptr2, ptr3))
4927 SetChangedFlag (true);
4928 break;
4931 case F_SelectedElements:
4932 if (ChangeSelectedSquare (ELEMENT_TYPE))
4933 SetChangedFlag (true);
4934 break;
4936 case F_SelectedPins:
4937 if (ChangeSelectedSquare (PIN_TYPE | PAD_TYPE))
4938 SetChangedFlag (true);
4939 break;
4941 case F_Selected:
4942 case F_SelectedObjects:
4943 if (ChangeSelectedSquare (PIN_TYPE | PAD_TYPE))
4944 SetChangedFlag (true);
4945 break;
4948 return 0;
4951 /* --------------------------------------------------------------------------- */
4953 static const char setsquare_syntax[] =
4954 N_("SetSquare(ToggleObject|SelectedElements|SelectedPins)");
4956 static const char setsquare_help[] = N_("sets the square-flag of objects.");
4958 /* %start-doc actions SetSquare
4960 Note that @code{Pins} means pins and pads.
4962 @pinshapes
4964 %end-doc */
4966 static int
4967 ActionSetSquare (int argc, char **argv, Coord x, Coord y)
4969 char *function = ARG (0);
4970 if (function && *function)
4972 switch (GetFunctionID (function))
4974 case F_ToggleObject:
4975 case F_Object:
4977 int type;
4978 void *ptr1, *ptr2, *ptr3;
4980 gui->get_coords (_("Select an Object"), &x, &y);
4981 if ((type =
4982 SearchScreen (x, y, CHANGESQUARE_TYPES,
4983 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4984 if (SetObjectSquare (type, ptr1, ptr2, ptr3))
4985 SetChangedFlag (true);
4986 break;
4989 case F_SelectedElements:
4990 if (SetSelectedSquare (ELEMENT_TYPE))
4991 SetChangedFlag (true);
4992 break;
4994 case F_SelectedPins:
4995 if (SetSelectedSquare (PIN_TYPE | PAD_TYPE))
4996 SetChangedFlag (true);
4997 break;
4999 case F_Selected:
5000 case F_SelectedObjects:
5001 if (SetSelectedSquare (PIN_TYPE | PAD_TYPE))
5002 SetChangedFlag (true);
5003 break;
5006 return 0;
5009 /* --------------------------------------------------------------------------- */
5011 static const char clearsquare_syntax[] =
5012 N_("ClearSquare(ToggleObject|SelectedElements|SelectedPins)");
5014 static const char clearsquare_help[] =
5015 N_("Clears the square-flag of pins and pads.");
5017 /* %start-doc actions ClearSquare
5019 Note that @code{Pins} means pins and pads.
5021 @pinshapes
5023 %end-doc */
5025 static int
5026 ActionClearSquare (int argc, char **argv, Coord x, Coord y)
5028 char *function = ARG (0);
5029 if (function && *function)
5031 switch (GetFunctionID (function))
5033 case F_ToggleObject:
5034 case F_Object:
5036 int type;
5037 void *ptr1, *ptr2, *ptr3;
5039 gui->get_coords (_("Select an Object"), &x, &y);
5040 if ((type =
5041 SearchScreen (x, y, CHANGESQUARE_TYPES,
5042 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5043 if (ClrObjectSquare (type, ptr1, ptr2, ptr3))
5044 SetChangedFlag (true);
5045 break;
5048 case F_SelectedElements:
5049 if (ClrSelectedSquare (ELEMENT_TYPE))
5050 SetChangedFlag (true);
5051 break;
5053 case F_SelectedPins:
5054 if (ClrSelectedSquare (PIN_TYPE | PAD_TYPE))
5055 SetChangedFlag (true);
5056 break;
5058 case F_Selected:
5059 case F_SelectedObjects:
5060 if (ClrSelectedSquare (PIN_TYPE | PAD_TYPE))
5061 SetChangedFlag (true);
5062 break;
5065 return 0;
5068 /* --------------------------------------------------------------------------- */
5070 static const char changeoctagon_syntax[] =
5071 N_("ChangeOctagon(Object|ToggleObject|SelectedObjects|Selected)\n"
5072 "ChangeOctagon(SelectedElements|SelectedPins|SelectedVias)");
5074 static const char changeoctagon_help[] =
5075 N_("Changes the octagon-flag of pins and vias.");
5077 /* %start-doc actions ChangeOctagon
5079 @pinshapes
5081 %end-doc */
5083 static int
5084 ActionChangeOctagon (int argc, char **argv, Coord x, Coord y)
5086 char *function = ARG (0);
5087 if (function)
5089 switch (GetFunctionID (function))
5091 case F_ToggleObject:
5092 case F_Object:
5094 int type;
5095 void *ptr1, *ptr2, *ptr3;
5097 gui->get_coords (_("Select an Object"), &x, &y);
5098 if ((type =
5099 SearchScreen (x, y, CHANGEOCTAGON_TYPES,
5100 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5101 if (ChangeObjectOctagon (type, ptr1, ptr2, ptr3))
5102 SetChangedFlag (true);
5103 break;
5106 case F_SelectedElements:
5107 if (ChangeSelectedOctagon (ELEMENT_TYPE))
5108 SetChangedFlag (true);
5109 break;
5111 case F_SelectedPins:
5112 if (ChangeSelectedOctagon (PIN_TYPE))
5113 SetChangedFlag (true);
5114 break;
5116 case F_SelectedVias:
5117 if (ChangeSelectedOctagon (VIA_TYPE))
5118 SetChangedFlag (true);
5119 break;
5121 case F_Selected:
5122 case F_SelectedObjects:
5123 if (ChangeSelectedOctagon (PIN_TYPES))
5124 SetChangedFlag (true);
5125 break;
5128 return 0;
5131 /* --------------------------------------------------------------------------- */
5133 static const char setoctagon_syntax[] =
5134 N_("SetOctagon(Object|ToggleObject|SelectedElements|Selected)");
5136 static const char setoctagon_help[] = N_("Sets the octagon-flag of objects.");
5138 /* %start-doc actions SetOctagon
5140 @pinshapes
5142 %end-doc */
5144 static int
5145 ActionSetOctagon (int argc, char **argv, Coord x, Coord y)
5147 char *function = ARG (0);
5148 if (function)
5150 switch (GetFunctionID (function))
5152 case F_ToggleObject:
5153 case F_Object:
5155 int type;
5156 void *ptr1, *ptr2, *ptr3;
5158 gui->get_coords (_("Select an Object"), &x, &y);
5159 if ((type =
5160 SearchScreen (x, y, CHANGEOCTAGON_TYPES,
5161 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5162 if (SetObjectOctagon (type, ptr1, ptr2, ptr3))
5163 SetChangedFlag (true);
5164 break;
5167 case F_SelectedElements:
5168 if (SetSelectedOctagon (ELEMENT_TYPE))
5169 SetChangedFlag (true);
5170 break;
5172 case F_SelectedPins:
5173 if (SetSelectedOctagon (PIN_TYPE))
5174 SetChangedFlag (true);
5175 break;
5177 case F_SelectedVias:
5178 if (SetSelectedOctagon (VIA_TYPE))
5179 SetChangedFlag (true);
5180 break;
5182 case F_Selected:
5183 case F_SelectedObjects:
5184 if (SetSelectedOctagon (PIN_TYPES))
5185 SetChangedFlag (true);
5186 break;
5189 return 0;
5192 /* --------------------------------------------------------------------------- */
5194 static const char clearoctagon_syntax[] =
5195 N_("ClearOctagon(ToggleObject|Object|SelectedObjects|Selected)\n"
5196 "ClearOctagon(SelectedElements|SelectedPins|SelectedVias)");
5198 static const char clearoctagon_help[] =
5199 N_("Clears the octagon-flag of pins and vias.");
5201 /* %start-doc actions ClearOctagon
5203 @pinshapes
5205 %end-doc */
5207 static int
5208 ActionClearOctagon (int argc, char **argv, Coord x, Coord y)
5210 char *function = ARG (0);
5211 if (function)
5213 switch (GetFunctionID (function))
5215 case F_ToggleObject:
5216 case F_Object:
5218 int type;
5219 void *ptr1, *ptr2, *ptr3;
5221 gui->get_coords (_("Select an Object"), &x, &y);
5222 if ((type =
5223 SearchScreen (Crosshair.X, Crosshair.Y, CHANGEOCTAGON_TYPES,
5224 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5225 if (ClrObjectOctagon (type, ptr1, ptr2, ptr3))
5226 SetChangedFlag (true);
5227 break;
5230 case F_SelectedElements:
5231 if (ClrSelectedOctagon (ELEMENT_TYPE))
5232 SetChangedFlag (true);
5233 break;
5235 case F_SelectedPins:
5236 if (ClrSelectedOctagon (PIN_TYPE))
5237 SetChangedFlag (true);
5238 break;
5240 case F_SelectedVias:
5241 if (ClrSelectedOctagon (VIA_TYPE))
5242 SetChangedFlag (true);
5243 break;
5245 case F_Selected:
5246 case F_SelectedObjects:
5247 if (ClrSelectedOctagon (PIN_TYPES))
5248 SetChangedFlag (true);
5249 break;
5252 return 0;
5255 /* --------------------------------------------------------------------------- */
5257 static const char changehold_syntax[] =
5258 N_("ChangeHole(ToggleObject|Object|SelectedVias|Selected)");
5260 static const char changehold_help[] = N_("Changes the hole flag of objects.");
5262 /* %start-doc actions ChangeHole
5264 The "hole flag" of a via determines whether the via is a
5265 plated-through hole (not set), or an unplated hole (set).
5267 %end-doc */
5269 static int
5270 ActionChangeHole (int argc, char **argv, Coord x, Coord y)
5272 char *function = ARG (0);
5273 if (function)
5275 switch (GetFunctionID (function))
5277 case F_ToggleObject:
5278 case F_Object:
5280 int type;
5281 void *ptr1, *ptr2, *ptr3;
5283 gui->get_coords (_("Select an Object"), &x, &y);
5284 if ((type = SearchScreen (x, y, VIA_TYPE,
5285 &ptr1, &ptr2, &ptr3)) != NO_TYPE
5286 && ChangeHole ((PinType *) ptr3))
5287 IncrementUndoSerialNumber ();
5288 break;
5291 case F_SelectedVias:
5292 case F_Selected:
5293 if (ChangeSelectedHole ())
5294 SetChangedFlag (true);
5295 break;
5298 return 0;
5301 /* --------------------------------------------------------------------------- */
5303 static const char changepaste_syntax[] =
5304 N_("ChangePaste(ToggleObject|Object|SelectedPads|Selected)");
5306 static const char changepaste_help[] =
5307 N_("Changes the no paste flag of objects.");
5309 /* %start-doc actions ChangePaste
5311 The "no paste flag" of a pad determines whether the solderpaste
5312 stencil will have an opening for the pad (no set) or if there wil be
5313 no solderpaste on the pad (set). This is used for things such as
5314 fiducial pads.
5316 %end-doc */
5318 static int
5319 ActionChangePaste (int argc, char **argv, Coord x, Coord y)
5321 char *function = ARG (0);
5322 if (function)
5324 switch (GetFunctionID (function))
5326 case F_ToggleObject:
5327 case F_Object:
5329 int type;
5330 void *ptr1, *ptr2, *ptr3;
5332 gui->get_coords (_("Select an Object"), &x, &y);
5333 if ((type = SearchScreen (x, y, PAD_TYPE,
5334 &ptr1, &ptr2, &ptr3)) != NO_TYPE
5335 && ChangePaste ((PadType *) ptr3))
5336 IncrementUndoSerialNumber ();
5337 break;
5340 case F_SelectedPads:
5341 case F_Selected:
5342 if (ChangeSelectedPaste ())
5343 SetChangedFlag (true);
5344 break;
5347 return 0;
5350 /* --------------------------------------------------------------------------- */
5352 static const char select_syntax[] =
5353 N_("Select(Object|ToggleObject)\n"
5354 "Select(All|Block|Connection)\n"
5355 "Select(ElementByName|ObjectByName|PadByName|PinByName)\n"
5356 "Select(ElementByName|ObjectByName|PadByName|PinByName, Name)\n"
5357 "Select(TextByName|ViaByName|NetByName)\n"
5358 "Select(TextByName|ViaByName|NetByName, Name)\n"
5359 "Select(Convert)");
5361 static const char select_help[] = N_("Toggles or sets the selection.");
5363 /* %start-doc actions Select
5365 @table @code
5367 @item ElementByName
5368 @item ObjectByName
5369 @item PadByName
5370 @item PinByName
5371 @item TextByName
5372 @item ViaByName
5373 @item NetByName
5375 These all rely on having a regular expression parser built into
5376 @code{pcb}. If the name is not specified then the user is prompted
5377 for a pattern, and all objects that match the pattern and are of the
5378 type specified are selected.
5380 @item Object
5381 @item ToggleObject
5382 Selects the object under the cursor.
5384 @item Block
5385 Selects all objects in a rectangle indicated by the cursor.
5387 @item All
5388 Selects all objects on the board.
5390 @item Found
5391 Selects all connections with the ``found'' flag set.
5393 @item Connection
5394 Selects all connections with the ``connected'' flag set.
5396 @item Convert
5397 Converts the selected objects to an element. This uses the highest
5398 numbered paste buffer.
5400 @end table
5402 %end-doc */
5404 static int
5405 ActionSelect (int argc, char **argv, Coord x, Coord y)
5407 char *function = ARG (0);
5408 if (function)
5410 switch (GetFunctionID (function))
5412 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5413 int type;
5414 /* select objects by their names */
5415 case F_ElementByName:
5416 type = ELEMENT_TYPE;
5417 goto commonByName;
5418 case F_ObjectByName:
5419 type = ALL_TYPES;
5420 goto commonByName;
5421 case F_PadByName:
5422 type = PAD_TYPE;
5423 goto commonByName;
5424 case F_PinByName:
5425 type = PIN_TYPE;
5426 goto commonByName;
5427 case F_TextByName:
5428 type = TEXT_TYPE;
5429 goto commonByName;
5430 case F_ViaByName:
5431 type = VIA_TYPE;
5432 goto commonByName;
5433 case F_NetByName:
5434 type = NET_TYPE;
5435 goto commonByName;
5437 commonByName:
5439 char *pattern = ARG (1);
5441 if (pattern
5442 || (pattern =
5443 gui->prompt_for (_("Enter pattern:"), "")) != NULL)
5445 if (SelectObjectByName (type, pattern, true))
5446 SetChangedFlag (true);
5447 if (ARG (1) == NULL)
5448 free (pattern);
5450 break;
5452 #endif /* defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP) */
5454 /* select a single object */
5455 case F_ToggleObject:
5456 case F_Object:
5457 if (SelectObject ())
5458 SetChangedFlag (true);
5459 break;
5461 /* all objects in block */
5462 case F_Block:
5464 BoxType box;
5466 box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
5467 Crosshair.AttachedBox.Point2.X);
5468 box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
5469 Crosshair.AttachedBox.Point2.Y);
5470 box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
5471 Crosshair.AttachedBox.Point2.X);
5472 box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
5473 Crosshair.AttachedBox.Point2.Y);
5474 notify_crosshair_change (false);
5475 NotifyBlock ();
5476 if (Crosshair.AttachedBox.State == STATE_THIRD &&
5477 SelectBlock (&box, true))
5479 SetChangedFlag (true);
5480 Crosshair.AttachedBox.State = STATE_FIRST;
5482 notify_crosshair_change (true);
5483 break;
5486 /* select all visible objects */
5487 case F_All:
5489 BoxType box;
5491 box.X1 = -MAX_COORD;
5492 box.Y1 = -MAX_COORD;
5493 box.X2 = MAX_COORD;
5494 box.Y2 = MAX_COORD;
5495 if (SelectBlock (&box, true))
5496 SetChangedFlag (true);
5497 break;
5500 /* all logical connections */
5501 case F_Found:
5502 if (SelectByFlag (FOUNDFLAG, true))
5504 Draw ();
5505 IncrementUndoSerialNumber ();
5506 SetChangedFlag (true);
5508 break;
5510 /* all physical connections */
5511 case F_Connection:
5512 if (SelectByFlag (CONNECTEDFLAG, true))
5514 Draw ();
5515 IncrementUndoSerialNumber ();
5516 SetChangedFlag (true);
5518 break;
5520 case F_Convert:
5522 Coord x, y;
5523 Note.Buffer = Settings.BufferNumber;
5524 SetBufferNumber (MAX_BUFFER - 1);
5525 ClearBuffer (PASTEBUFFER);
5526 gui->get_coords (_("Select the Element's Mark Location"), &x, &y);
5527 x = GridFit (x, PCB->Grid, PCB->GridOffsetX);
5528 y = GridFit (y, PCB->Grid, PCB->GridOffsetY);
5529 AddSelectedToBuffer (PASTEBUFFER, x, y, true);
5530 SaveUndoSerialNumber ();
5531 RemoveSelected ();
5532 ConvertBufferToElement (PASTEBUFFER);
5533 RestoreUndoSerialNumber ();
5534 CopyPastebufferToLayout (x, y);
5535 SetBufferNumber (Note.Buffer);
5537 break;
5539 default:
5540 AFAIL (select);
5541 break;
5544 return 0;
5547 /* FLAG(have_regex,FlagHaveRegex,0) */
5549 FlagHaveRegex (int parm)
5551 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5552 return 1;
5553 #else
5554 return 0;
5555 #endif
5558 /* --------------------------------------------------------------------------- */
5560 static const char unselect_syntax[] =
5561 N_("Unselect(All|Block|Connection)\n"
5562 "Unselect(ElementByName|ObjectByName|PadByName|PinByName)\n"
5563 "Unselect(ElementByName|ObjectByName|PadByName|PinByName, Name)\n"
5564 "Unselect(TextByName|ViaByName)\n"
5565 "Unselect(TextByName|ViaByName, Name)\n");
5567 static const char unselect_help[] =
5568 N_("Unselects the object at the pointer location or the specified objects.");
5570 /* %start-doc actions Unselect
5572 @table @code
5574 @item All
5575 Unselect all objects.
5577 @item Block
5578 Unselect all objects in a rectangle given by the cursor.
5580 @item Connection
5581 Unselect all connections with the ``found'' flag set.
5583 @item ElementByName
5584 @item ObjectByName
5585 @item PadByName
5586 @item PinByName
5587 @item TextByName
5588 @item ViaByName
5590 These all rely on having a regular expression parser built into
5591 @code{pcb}. If the name is not specified then the user is prompted
5592 for a pattern, and all objects that match the pattern and are of the
5593 type specified are unselected.
5596 @end table
5598 %end-doc */
5600 static int
5601 ActionUnselect (int argc, char **argv, Coord x, Coord y)
5603 char *function = ARG (0);
5604 if (function)
5606 switch (GetFunctionID (function))
5608 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5609 int type;
5610 /* select objects by their names */
5611 case F_ElementByName:
5612 type = ELEMENT_TYPE;
5613 goto commonByName;
5614 case F_ObjectByName:
5615 type = ALL_TYPES;
5616 goto commonByName;
5617 case F_PadByName:
5618 type = PAD_TYPE;
5619 goto commonByName;
5620 case F_PinByName:
5621 type = PIN_TYPE;
5622 goto commonByName;
5623 case F_TextByName:
5624 type = TEXT_TYPE;
5625 goto commonByName;
5626 case F_ViaByName:
5627 type = VIA_TYPE;
5628 goto commonByName;
5629 case F_NetByName:
5630 type = NET_TYPE;
5631 goto commonByName;
5633 commonByName:
5635 char *pattern = ARG (1);
5637 if (pattern
5638 || (pattern =
5639 gui->prompt_for (_("Enter pattern:"), "")) != NULL)
5641 if (SelectObjectByName (type, pattern, false))
5642 SetChangedFlag (true);
5643 if (ARG (1) == NULL)
5644 free (pattern);
5646 break;
5648 #endif /* defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP) */
5650 /* all objects in block */
5651 case F_Block:
5653 BoxType box;
5655 box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
5656 Crosshair.AttachedBox.Point2.X);
5657 box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
5658 Crosshair.AttachedBox.Point2.Y);
5659 box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
5660 Crosshair.AttachedBox.Point2.X);
5661 box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
5662 Crosshair.AttachedBox.Point2.Y);
5663 notify_crosshair_change (false);
5664 NotifyBlock ();
5665 if (Crosshair.AttachedBox.State == STATE_THIRD &&
5666 SelectBlock (&box, false))
5668 SetChangedFlag (true);
5669 Crosshair.AttachedBox.State = STATE_FIRST;
5671 notify_crosshair_change (true);
5672 break;
5675 /* unselect all visible objects */
5676 case F_All:
5678 BoxType box;
5680 box.X1 = -MAX_COORD;
5681 box.Y1 = -MAX_COORD;
5682 box.X2 = MAX_COORD;
5683 box.Y2 = MAX_COORD;
5684 if (SelectBlock (&box, false))
5685 SetChangedFlag (true);
5686 break;
5689 /* all logical connections */
5690 case F_Found:
5691 if (SelectByFlag (FOUNDFLAG, false))
5693 Draw ();
5694 IncrementUndoSerialNumber ();
5695 SetChangedFlag (true);
5697 break;
5699 /* all physical connections */
5700 case F_Connection:
5701 if (SelectByFlag (CONNECTEDFLAG, false))
5703 Draw ();
5704 IncrementUndoSerialNumber ();
5705 SetChangedFlag (true);
5707 break;
5709 default:
5710 AFAIL (unselect);
5711 break;
5715 return 0;
5718 /* --------------------------------------------------------------------------- */
5720 static const char saveto_syntax[] =
5721 N_("SaveTo(Layout|LayoutAs,filename)\n"
5722 "SaveTo(AllConnections|AllUnusedPins|ElementConnections,filename)\n"
5723 "SaveTo(PasteBuffer,filename)");
5725 static const char saveto_help[] = N_("Saves data to a file.");
5727 /* %start-doc actions SaveTo
5729 @table @code
5731 @item Layout
5732 Saves the current layout.
5734 @item LayoutAs
5735 Saves the current layout, and remembers the filename used.
5737 @item AllConnections
5738 Save all connections to a file.
5740 @item AllUnusedPins
5741 List all unused pins to a file.
5743 @item ElementConnections
5744 Save connections to the element at the cursor to a file.
5746 @item PasteBuffer
5747 Save the content of the active Buffer to a file. This is the graphical way to create a footprint.
5749 @end table
5751 %end-doc */
5753 static int
5754 ActionSaveTo (int argc, char **argv, Coord x, Coord y)
5756 char *function;
5757 char *name;
5759 function = ARG (0);
5761 if ( ! function || strcasecmp (function, "Layout") == 0)
5763 if (SavePCB (PCB->Filename) == 0)
5764 SetChangedFlag (false);
5765 return 0;
5768 if (argc != 2)
5769 AFAIL (saveto);
5771 name = argv[1];
5773 if (strcasecmp (function, "LayoutAs") == 0)
5775 if (SavePCB (name) == 0)
5777 SetChangedFlag (false);
5778 free (PCB->Filename);
5779 PCB->Filename = strdup (name);
5780 if (gui->notify_filename_changed != NULL)
5781 gui->notify_filename_changed ();
5783 return 0;
5786 if (strcasecmp (function, "AllConnections") == 0)
5788 FILE *fp;
5789 bool result;
5790 if ((fp = CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
5792 LookupConnectionsToAllElements (fp);
5793 fclose (fp);
5794 SetChangedFlag (true);
5796 return 0;
5799 if (strcasecmp (function, "AllUnusedPins") == 0)
5801 FILE *fp;
5802 bool result;
5803 if ((fp = CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
5805 LookupUnusedPins (fp);
5806 fclose (fp);
5807 SetChangedFlag (true);
5809 return 0;
5812 if (strcasecmp (function, "ElementConnections") == 0)
5814 ElementType *element;
5815 void *ptrtmp;
5816 FILE *fp;
5817 bool result;
5819 if ((SearchScreen (Crosshair.X, Crosshair.Y, ELEMENT_TYPE,
5820 &ptrtmp, &ptrtmp, &ptrtmp)) != NO_TYPE)
5822 element = (ElementType *) ptrtmp;
5823 if ((fp =
5824 CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
5826 LookupElementConnections (element, fp);
5827 fclose (fp);
5828 SetChangedFlag (true);
5831 return 0;
5834 if (strcasecmp (function, "PasteBuffer") == 0)
5836 return SaveBufferElements (name);
5839 AFAIL (saveto);
5842 /* --------------------------------------------------------------------------- */
5844 static const char savesettings_syntax[] =
5845 N_("SaveSettings()\n"
5846 "SaveSettings(local)");
5848 static const char savesettings_help[] = N_("Saves settings.");
5850 /* %start-doc actions SaveSettings
5852 If you pass no arguments, the settings are stored in
5853 @code{$HOME/.pcb/settings}. If you pass the word @code{local} they're
5854 saved in @code{./pcb.settings}.
5856 %end-doc */
5858 static int
5859 ActionSaveSettings (int argc, char **argv, Coord x, Coord y)
5861 int locally = argc > 0 ? (strncasecmp (argv[0], "local", 5) == 0) : 0;
5862 hid_save_settings (locally);
5863 return 0;
5866 /* --------------------------------------------------------------------------- */
5868 static const char loadfrom_syntax[] =
5869 N_("LoadFrom(Layout|LayoutToBuffer|ElementToBuffer|Netlist|Revert,filename)");
5871 static const char loadfrom_help[] = N_("Load layout data from a file.");
5873 /* %start-doc actions LoadFrom
5875 This action assumes you know what the filename is. The various GUIs
5876 should have a similar @code{Load} action where the filename is
5877 optional, and will provide their own file selection mechanism to let
5878 you choose the file name.
5880 @table @code
5882 @item Layout
5883 Loads an entire PCB layout, replacing the current one.
5885 @item LayoutToBuffer
5886 Loads an entire PCB layout to the paste buffer.
5888 @item ElementToBuffer
5889 Loads the given element file into the paste buffer. Element files
5890 contain only a single @code{Element} definition, such as the
5891 ``newlib'' library uses.
5893 @item Netlist
5894 Loads a new netlist, replacing any current netlist.
5896 @item Revert
5897 Re-loads the current layout from its disk file, reverting any changes
5898 you may have made.
5900 @end table
5902 %end-doc */
5904 static int
5905 ActionLoadFrom (int argc, char **argv, Coord x, Coord y)
5907 char *function;
5908 char *name;
5910 if (argc < 2)
5911 AFAIL (loadfrom);
5913 function = argv[0];
5914 name = argv[1];
5916 if (strcasecmp (function, "ElementToBuffer") == 0)
5918 notify_crosshair_change (false);
5919 if (LoadElementToBuffer (PASTEBUFFER, name, true))
5920 SetMode (PASTEBUFFER_MODE);
5921 notify_crosshair_change (true);
5924 else if (strcasecmp (function, "LayoutToBuffer") == 0)
5926 notify_crosshair_change (false);
5927 if (LoadLayoutToBuffer (PASTEBUFFER, name))
5928 SetMode (PASTEBUFFER_MODE);
5929 notify_crosshair_change (true);
5932 else if (strcasecmp (function, "Layout") == 0)
5934 if (!PCB->Changed ||
5935 gui->confirm_dialog (_("OK to override layout data?"), 0))
5936 LoadPCB (name);
5939 else if (strcasecmp (function, "Netlist") == 0)
5941 if (PCB->Netlistname)
5942 free (PCB->Netlistname);
5943 PCB->Netlistname = StripWhiteSpaceAndDup (name);
5944 FreeLibraryMemory (&PCB->NetlistLib);
5945 ImportNetlist (PCB->Netlistname);
5946 NetlistChanged (1);
5948 else if (strcasecmp (function, "Revert") == 0 && PCB->Filename
5949 && (!PCB->Changed
5950 || gui->confirm_dialog (_("OK to override changes?"), 0)))
5952 RevertPCB ();
5955 return 0;
5958 /* --------------------------------------------------------------------------- */
5960 static const char new_syntax[] = N_("New([name])");
5962 static const char new_help[] = N_("Starts a new layout.");
5964 /* %start-doc actions New
5966 If a name is not given, one is prompted for.
5968 %end-doc */
5970 static int
5971 ActionNew (int argc, char **argv, Coord x, Coord y)
5973 char *name = ARG (0);
5975 if (!PCB->Changed || gui->confirm_dialog (_("OK to clear layout data?"), 0))
5977 if (name)
5978 name = strdup (name);
5979 else
5980 name = gui->prompt_for (_("Enter the layout name:"), "");
5982 if (!name)
5983 return 1;
5985 notify_crosshair_change (false);
5986 /* do emergency saving
5987 * clear the old struct and allocate memory for the new one
5989 if (PCB->Changed && Settings.SaveInTMP)
5990 SaveInTMP ();
5991 RemovePCB (PCB);
5992 PCB = NULL;
5993 PCB = CreateNewPCB ();
5994 CreateNewPCBPost (PCB, 1);
5996 /* setup the new name and reset some values to default */
5997 free (PCB->Name);
5998 PCB->Name = name;
6000 ResetStackAndVisibility ();
6001 SetCrosshairRange (0, 0, PCB->MaxWidth, PCB->MaxHeight);
6002 CenterDisplay (PCB->MaxWidth / 2, PCB->MaxHeight / 2, false);
6003 Redraw ();
6005 hid_action ("PCBChanged");
6006 notify_crosshair_change (true);
6007 return 0;
6009 return 1;
6013 * \brief No operation, just for testing purposes.
6014 * syntax: Bell(volume)
6016 void
6017 ActionBell (char *volume)
6019 gui->beep ();
6022 /* --------------------------------------------------------------------------- */
6024 static const char pastebuffer_syntax[] =
6025 N_("PasteBuffer(AddSelected|Clear|1..MAX_BUFFER)\n"
6026 "PasteBuffer(Rotate, 1..3)\n"
6027 "PasteBuffer(Convert|Save|Restore|Mirror)\n"
6028 "PasteBuffer(ToLayout, X, Y, units)");
6030 static const char pastebuffer_help[] =
6031 N_("Various operations on the paste buffer.");
6033 /* %start-doc actions PasteBuffer
6035 There are a number of paste buffers; the actual limit is a
6036 compile-time constant @code{MAX_BUFFER} in @file{globalconst.h}. It
6037 is currently @code{5}. One of these is the ``current'' paste buffer,
6038 often referred to as ``the'' paste buffer.
6040 @table @code
6042 @item AddSelected
6043 Copies the selected objects to the current paste buffer.
6045 @item Clear
6046 Remove all objects from the current paste buffer.
6048 @item Convert
6049 Convert the current paste buffer to an element. Vias are converted to
6050 pins, lines are converted to pads.
6052 @item Restore
6053 Convert any elements in the paste buffer back to vias and lines.
6055 @item Mirror
6056 Flip all objects in the paste buffer vertically (up/down flip). To mirror
6057 horizontally, combine this with rotations.
6059 @item Rotate
6060 Rotates the current buffer. The number to pass is 1..3, where 1 means
6061 90 degrees counter clockwise, 2 means 180 degrees, and 3 means 90
6062 degrees clockwise (270 CCW).
6064 @item Save
6065 Saves any elements in the current buffer to the indicated file.
6067 @item ToLayout
6068 Pastes any elements in the current buffer to the indicated X, Y
6069 coordinates in the layout. The @code{X} and @code{Y} are treated like
6070 @code{delta} is for many other objects. For each, if it's prefixed by
6071 @code{+} or @code{-}, then that amount is relative to the last
6072 location. Otherwise, it's absolute. Units can be
6073 @code{mil} or @code{mm}; if unspecified, units are PCB's internal
6074 units, currently 1/100 mil.
6077 @item 1..MAX_BUFFER
6078 Selects the given buffer to be the current paste buffer.
6080 @end table
6082 %end-doc */
6084 static int
6085 ActionPasteBuffer (int argc, char **argv, Coord x, Coord y)
6087 char *function = argc ? argv[0] : (char *)"";
6088 char *sbufnum = argc > 1 ? argv[1] : (char *)"";
6089 char *name;
6090 static char *default_file = NULL;
6091 int free_name = 0;
6093 notify_crosshair_change (false);
6094 if (function)
6096 switch (GetFunctionID (function))
6098 /* clear contents of paste buffer */
6099 case F_Clear:
6100 ClearBuffer (PASTEBUFFER);
6101 break;
6103 /* copies objects to paste buffer */
6104 case F_AddSelected:
6105 AddSelectedToBuffer (PASTEBUFFER, 0, 0, false);
6106 break;
6108 /* converts buffer contents into an element */
6109 case F_Convert:
6110 ConvertBufferToElement (PASTEBUFFER);
6111 break;
6113 /* break up element for editing */
6114 case F_Restore:
6115 SmashBufferElement (PASTEBUFFER);
6116 break;
6118 /* Mirror buffer */
6119 case F_Mirror:
6120 MirrorBuffer (PASTEBUFFER);
6121 break;
6123 case F_Rotate:
6124 if (sbufnum)
6126 RotateBuffer (PASTEBUFFER, (BYTE) atoi (sbufnum));
6127 SetCrosshairRangeToBuffer ();
6129 break;
6131 case F_Save:
6132 if (PASTEBUFFER->Data->ElementN == 0)
6134 Message (_("Buffer has no elements!\n"));
6135 break;
6137 free_name = 0;
6138 if (argc <= 1)
6140 name = gui->fileselect (_("Save Paste Buffer As ..."),
6141 _("Choose a file to save the contents of the\n"
6142 "paste buffer to.\n"),
6143 default_file, ".fp", "footprint",
6146 if (default_file)
6148 free (default_file);
6149 default_file = NULL;
6151 if ( name && *name)
6153 default_file = strdup (name);
6155 free_name = 1;
6158 else
6159 name = argv[1];
6162 FILE *exist;
6164 if ((exist = fopen (name, "r")))
6166 fclose (exist);
6167 if (gui->
6168 confirm_dialog (_("File exists! Ok to overwrite?"), 0))
6169 SaveBufferElements (name);
6171 else
6172 SaveBufferElements (name);
6174 if (free_name && name)
6175 free (name);
6177 break;
6179 case F_ToLayout:
6181 static Coord oldx = 0, oldy = 0;
6182 Coord x, y;
6183 bool absolute;
6185 if (argc == 1)
6187 x = y = 0;
6189 else if (argc == 3 || argc == 4)
6191 x = GetValue (ARG (1), ARG (3), &absolute);
6192 if (!absolute)
6193 x += oldx;
6194 y = GetValue (ARG (2), ARG (3), &absolute);
6195 if (!absolute)
6196 y += oldy;
6198 else
6200 notify_crosshair_change (true);
6201 AFAIL (pastebuffer);
6204 oldx = x;
6205 oldy = y;
6206 if (CopyPastebufferToLayout (x, y))
6207 SetChangedFlag (true);
6209 break;
6211 /* set number */
6212 default:
6214 int number = atoi (function);
6216 /* correct number */
6217 if (number)
6218 SetBufferNumber (number - 1);
6223 notify_crosshair_change (true);
6224 return 0;
6227 /* --------------------------------------------------------------------------- */
6229 static const char undo_syntax[] = N_("Undo()\n"
6230 "Undo(ClearList)");
6232 static const char undo_help[] = N_("Undo recent changes.");
6234 /* %start-doc actions Undo
6236 The unlimited undo feature of @code{Pcb} allows you to recover from
6237 most operations that materially affect you work. Calling
6238 @code{Undo()} without any parameter recovers from the last (non-undo)
6239 operation. @code{ClearList} is used to release the allocated
6240 memory. @code{ClearList} is called whenever a new layout is started or
6241 loaded. See also @code{Redo} and @code{Atomic}.
6243 Note that undo groups operations by serial number; changes with the
6244 same serial number will be undone (or redone) as a group. See
6245 @code{Atomic}.
6247 %end-doc */
6249 static int
6250 ActionUndo (int argc, char **argv, Coord x, Coord y)
6252 char *function = ARG (0);
6253 if (!function || !*function)
6255 /* don't allow undo in the middle of an operation */
6256 if (Settings.Mode != POLYGONHOLE_MODE &&
6257 Crosshair.AttachedObject.State != STATE_FIRST)
6258 return 1;
6259 if (Crosshair.AttachedBox.State != STATE_FIRST
6260 && Settings.Mode != ARC_MODE)
6261 return 1;
6262 /* undo the last operation */
6264 notify_crosshair_change (false);
6265 if ((Settings.Mode == POLYGON_MODE ||
6266 Settings.Mode == POLYGONHOLE_MODE) &&
6267 Crosshair.AttachedPolygon.PointN)
6269 GoToPreviousPoint ();
6270 notify_crosshair_change (true);
6271 return 0;
6273 /* move anchor point if undoing during line creation */
6274 if (Settings.Mode == LINE_MODE)
6276 if (Crosshair.AttachedLine.State == STATE_SECOND)
6278 if (TEST_FLAG (AUTODRCFLAG, PCB))
6279 Undo (true); /* undo the connection find */
6280 Crosshair.AttachedLine.State = STATE_FIRST;
6281 SetLocalRef (0, 0, false);
6282 notify_crosshair_change (true);
6283 return 0;
6285 if (Crosshair.AttachedLine.State == STATE_THIRD)
6287 int type;
6288 void *ptr1, *ptr3, *ptrtmp;
6289 LineType *ptr2;
6290 /* this search is guaranteed to succeed */
6291 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6292 &ptrtmp, &ptr3,
6293 Crosshair.AttachedLine.Point1.X,
6294 Crosshair.AttachedLine.Point1.Y, 0);
6295 ptr2 = (LineType *) ptrtmp;
6297 /* save both ends of line */
6298 Crosshair.AttachedLine.Point2.X = ptr2->Point1.X;
6299 Crosshair.AttachedLine.Point2.Y = ptr2->Point1.Y;
6300 if ((type = Undo (true)))
6301 SetChangedFlag (true);
6302 /* check that the undo was of the right type */
6303 if ((type & UNDO_CREATE) == 0)
6305 /* wrong undo type, restore anchor points */
6306 Crosshair.AttachedLine.Point2.X =
6307 Crosshair.AttachedLine.Point1.X;
6308 Crosshair.AttachedLine.Point2.Y =
6309 Crosshair.AttachedLine.Point1.Y;
6310 notify_crosshair_change (true);
6311 return 0;
6313 /* move to new anchor */
6314 Crosshair.AttachedLine.Point1.X =
6315 Crosshair.AttachedLine.Point2.X;
6316 Crosshair.AttachedLine.Point1.Y =
6317 Crosshair.AttachedLine.Point2.Y;
6318 /* check if an intermediate point was removed */
6319 if (type & UNDO_REMOVE)
6321 /* this search should find the restored line */
6322 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6323 &ptrtmp,
6324 &ptr3,
6325 Crosshair.AttachedLine.Point2.X,
6326 Crosshair.AttachedLine.Point2.Y, 0);
6327 ptr2 = (LineType *) ptrtmp;
6328 if (TEST_FLAG (AUTODRCFLAG, PCB))
6330 /* undo loses CONNECTEDFLAG and FOUNDFLAG */
6331 SET_FLAG(CONNECTEDFLAG, ptr2);
6332 SET_FLAG(FOUNDFLAG, ptr2);
6333 DrawLine (CURRENT, ptr2);
6335 Crosshair.AttachedLine.Point1.X =
6336 Crosshair.AttachedLine.Point2.X = ptr2->Point2.X;
6337 Crosshair.AttachedLine.Point1.Y =
6338 Crosshair.AttachedLine.Point2.Y = ptr2->Point2.Y;
6340 FitCrosshairIntoGrid (Crosshair.X, Crosshair.Y);
6341 AdjustAttachedObjects ();
6342 if (--addedLines == 0)
6344 Crosshair.AttachedLine.State = STATE_SECOND;
6345 lastLayer = CURRENT;
6347 else
6349 /* this search is guaranteed to succeed too */
6350 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6351 &ptrtmp,
6352 &ptr3,
6353 Crosshair.AttachedLine.Point1.X,
6354 Crosshair.AttachedLine.Point1.Y, 0);
6355 ptr2 = (LineType *) ptrtmp;
6356 lastLayer = (LayerType *) ptr1;
6358 notify_crosshair_change (true);
6359 return 0;
6362 if (Settings.Mode == ARC_MODE)
6364 if (Crosshair.AttachedBox.State == STATE_SECOND)
6366 Crosshair.AttachedBox.State = STATE_FIRST;
6367 notify_crosshair_change (true);
6368 return 0;
6370 if (Crosshair.AttachedBox.State == STATE_THIRD)
6372 void *ptr1, *ptr2, *ptr3;
6373 BoxType *bx;
6374 /* guaranteed to succeed */
6375 SearchObjectByLocation (ARC_TYPE, &ptr1, &ptr2, &ptr3,
6376 Crosshair.AttachedBox.Point1.X,
6377 Crosshair.AttachedBox.Point1.Y, 0);
6378 bx = GetArcEnds ((ArcType *) ptr2);
6379 Crosshair.AttachedBox.Point1.X =
6380 Crosshair.AttachedBox.Point2.X = bx->X1;
6381 Crosshair.AttachedBox.Point1.Y =
6382 Crosshair.AttachedBox.Point2.Y = bx->Y1;
6383 AdjustAttachedObjects ();
6384 if (--addedLines == 0)
6385 Crosshair.AttachedBox.State = STATE_SECOND;
6388 /* undo the last destructive operation */
6389 if (Undo (true))
6390 SetChangedFlag (true);
6392 else if (function)
6394 switch (GetFunctionID (function))
6396 /* clear 'undo objects' list */
6397 case F_ClearList:
6398 ClearUndoList (false);
6399 break;
6402 notify_crosshair_change (true);
6403 return 0;
6406 /* --------------------------------------------------------------------------- */
6408 static const char redo_syntax[] = N_("Redo()");
6410 static const char redo_help[] = N_("Redo recent \"undo\" operations.");
6412 /* %start-doc actions Redo
6414 This routine allows you to recover from the last undo command. You
6415 might want to do this if you thought that undo was going to revert
6416 something other than what it actually did (in case you are confused
6417 about which operations are un-doable), or if you have been backing up
6418 through a long undo list and over-shoot your stopping point. Any
6419 change that is made since the undo in question will trim the redo
6420 list. For example if you add ten lines, then undo three of them you
6421 could use redo to put them back, but if you move a line on the board
6422 before performing the redo, you will lose the ability to "redo" the
6423 three "undone" lines.
6425 %end-doc */
6427 static int
6428 ActionRedo (int argc, char **argv, Coord x, Coord y)
6430 if (((Settings.Mode == POLYGON_MODE ||
6431 Settings.Mode == POLYGONHOLE_MODE) &&
6432 Crosshair.AttachedPolygon.PointN) ||
6433 Crosshair.AttachedLine.State == STATE_SECOND)
6434 return 1;
6435 notify_crosshair_change (false);
6436 if (Redo (true))
6438 SetChangedFlag (true);
6439 if (Settings.Mode == LINE_MODE &&
6440 Crosshair.AttachedLine.State != STATE_FIRST)
6442 LineType *line = g_list_last (CURRENT->Line)->data;
6443 Crosshair.AttachedLine.Point1.X =
6444 Crosshair.AttachedLine.Point2.X = line->Point2.X;
6445 Crosshair.AttachedLine.Point1.Y =
6446 Crosshair.AttachedLine.Point2.Y = line->Point2.Y;
6447 addedLines++;
6450 notify_crosshair_change (true);
6451 return 0;
6454 /* --------------------------------------------------------------------------- */
6456 static const char polygon_syntax[] = N_("Polygon(Close|PreviousPoint)");
6458 static const char polygon_help[] = N_("Some polygon related stuff.");
6460 /* %start-doc actions Polygon
6462 Polygons need a special action routine to make life easier.
6464 @table @code
6466 @item Close
6467 Creates the final segment of the polygon. This may fail if clipping
6468 to 45 degree lines is switched on, in which case a warning is issued.
6470 @item PreviousPoint
6471 Resets the newly entered corner to the previous one. The Undo action
6472 will call Polygon(PreviousPoint) when appropriate to do so.
6474 @end table
6476 %end-doc */
6478 static int
6479 ActionPolygon (int argc, char **argv, Coord x, Coord y)
6481 char *function = ARG (0);
6482 if (function && Settings.Mode == POLYGON_MODE)
6484 notify_crosshair_change (false);
6485 switch (GetFunctionID (function))
6487 /* close open polygon if possible */
6488 case F_Close:
6489 ClosePolygon ();
6490 break;
6492 /* go back to the previous point */
6493 case F_PreviousPoint:
6494 GoToPreviousPoint ();
6495 break;
6497 notify_crosshair_change (true);
6499 return 0;
6502 /* --------------------------------------------------------------------------- */
6504 static const char routestyle_syntax[] = N_("RouteStyle(1|2|3|4)");
6506 static const char routestyle_help[] =
6507 N_("Copies the indicated routing style into the current sizes.");
6509 /* %start-doc actions RouteStyle
6511 %end-doc */
6513 static int
6514 ActionRouteStyle (int argc, char **argv, Coord x, Coord y)
6516 char *str = ARG (0);
6517 RouteStyleType *rts;
6518 int number;
6520 if (str)
6522 number = atoi (str);
6523 if (number > 0 && number <= NUM_STYLES)
6525 rts = &PCB->RouteStyle[number - 1];
6526 SetLineSize (rts->Thick);
6527 SetViaSize (rts->Diameter, true);
6528 SetViaDrillingHole (rts->Hole, true);
6529 SetKeepawayWidth (rts->Keepaway);
6530 hid_action("RouteStylesChanged");
6533 return 0;
6537 /* --------------------------------------------------------------------------- */
6539 static const char moveobject_syntax[] = N_("MoveObject(X,Y,dim)");
6541 static const char moveobject_help[] =
6542 N_("Moves the object under the crosshair.");
6544 /* %start-doc actions MoveObject
6546 The @code{X} and @code{Y} are treated like @code{delta} is for many
6547 other objects. For each, if it's prefixed by @code{+} or @code{-},
6548 then that amount is relative. Otherwise, it's absolute. Units can be
6549 @code{mil} or @code{mm}; if unspecified, units are PCB's internal
6550 units, currently 1/100 mil.
6552 %end-doc */
6554 static int
6555 ActionMoveObject (int argc, char **argv, Coord x, Coord y)
6557 char *x_str = ARG (0);
6558 char *y_str = ARG (1);
6559 char *units = ARG (2);
6560 Coord nx, ny;
6561 bool absolute1, absolute2;
6562 void *ptr1, *ptr2, *ptr3;
6563 int type;
6565 ny = GetValue (y_str, units, &absolute1);
6566 nx = GetValue (x_str, units, &absolute2);
6568 type = SearchScreen (x, y, MOVE_TYPES, &ptr1, &ptr2, &ptr3);
6569 if (type == NO_TYPE)
6571 Message (_("Nothing found under crosshair\n"));
6572 return 1;
6574 if (absolute1)
6575 nx -= x;
6576 if (absolute2)
6577 ny -= y;
6578 Crosshair.AttachedObject.RubberbandN = 0;
6579 if (TEST_FLAG (RUBBERBANDFLAG, PCB))
6580 LookupRubberbandLines (type, ptr1, ptr2, ptr3);
6581 if (type == ELEMENT_TYPE)
6582 LookupRatLines (type, ptr1, ptr2, ptr3);
6583 MoveObjectAndRubberband (type, ptr1, ptr2, ptr3, nx, ny);
6584 SetChangedFlag (true);
6585 return 0;
6588 /* --------------------------------------------------------------------------- */
6590 static const char movetocurrentlayer_syntax[] =
6591 N_("MoveToCurrentLayer(Object|SelectedObjects)");
6593 static const char movetocurrentlayer_help[] =
6594 N_("Moves objects to the current layer.");
6596 /* %start-doc actions MoveToCurrentLayer
6598 Note that moving an element from a component layer to a solder layer,
6599 or from solder to component, won't automatically flip it. Use the
6600 @code{Flip()} action to do that.
6602 %end-doc */
6604 static int
6605 ActionMoveToCurrentLayer (int argc, char **argv, Coord x, Coord y)
6607 char *function = ARG (0);
6608 if (function)
6610 switch (GetFunctionID (function))
6612 case F_Object:
6614 int type;
6615 void *ptr1, *ptr2, *ptr3;
6617 gui->get_coords (_("Select an Object"), &x, &y);
6618 if ((type =
6619 SearchScreen (x, y, MOVETOLAYER_TYPES,
6620 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
6621 if (MoveObjectToLayer (type, ptr1, ptr2, ptr3, CURRENT, false))
6622 SetChangedFlag (true);
6623 break;
6626 case F_SelectedObjects:
6627 case F_Selected:
6628 if (MoveSelectedObjectsToLayer (CURRENT))
6629 SetChangedFlag (true);
6630 break;
6633 return 0;
6637 static const char setsame_syntax[] = N_("SetSame()");
6639 static const char setsame_help[] =
6640 N_("Sets current layer and sizes to match indicated item.");
6642 /* %start-doc actions SetSame
6644 When invoked over any line, arc, polygon, or via, this changes the
6645 current layer to be the layer that item is on, and changes the current
6646 sizes (thickness, keepaway, drill, etc) according to that item.
6648 %end-doc */
6650 static int
6651 ActionSetSame (int argc, char **argv, Coord x, Coord y)
6653 void *ptr1, *ptr2, *ptr3;
6654 int type;
6655 LayerType *layer = CURRENT;
6657 type = SearchScreen (x, y, CLONE_TYPES, &ptr1, &ptr2, &ptr3);
6658 /* set layer current and size from line or arc */
6659 switch (type)
6661 case LINE_TYPE:
6662 notify_crosshair_change (false);
6663 Settings.LineThickness = ((LineType *) ptr2)->Thickness;
6664 Settings.Keepaway = ((LineType *) ptr2)->Clearance / 2;
6665 layer = (LayerType *) ptr1;
6666 if (Settings.Mode != LINE_MODE)
6667 SetMode (LINE_MODE);
6668 notify_crosshair_change (true);
6669 hid_action ("RouteStylesChanged");
6670 break;
6672 case ARC_TYPE:
6673 notify_crosshair_change (false);
6674 Settings.LineThickness = ((ArcType *) ptr2)->Thickness;
6675 Settings.Keepaway = ((ArcType *) ptr2)->Clearance / 2;
6676 layer = (LayerType *) ptr1;
6677 if (Settings.Mode != ARC_MODE)
6678 SetMode (ARC_MODE);
6679 notify_crosshair_change (true);
6680 hid_action ("RouteStylesChanged");
6681 break;
6683 case POLYGON_TYPE:
6684 layer = (LayerType *) ptr1;
6685 break;
6687 case VIA_TYPE:
6688 notify_crosshair_change (false);
6689 Settings.ViaThickness = ((PinType *) ptr2)->Thickness;
6690 Settings.ViaDrillingHole = ((PinType *) ptr2)->DrillingHole;
6691 Settings.Keepaway = ((PinType *) ptr2)->Clearance / 2;
6692 if (Settings.Mode != VIA_MODE)
6693 SetMode (VIA_MODE);
6694 notify_crosshair_change (true);
6695 hid_action ("RouteStylesChanged");
6696 break;
6698 default:
6699 return 1;
6701 if (layer != CURRENT)
6703 ChangeGroupVisibility (GetLayerNumber (PCB->Data, layer), true, true);
6704 Redraw ();
6706 return 0;
6710 /* --------------------------------------------------------------------------- */
6712 static const char setflag_syntax[] =
6713 N_("SetFlag(Object|Selected|SelectedObjects, flag)\n"
6714 "SetFlag(SelectedLines|SelectedPins|SelectedVias, flag)\n"
6715 "SetFlag(SelectedPads|SelectedTexts|SelectedNames, flag)\n"
6716 "SetFlag(SelectedElements, flag)\n"
6717 "flag = square | octagon | thermal | join");
6719 static const char setflag_help[] = N_("Sets flags on objects.");
6721 /* %start-doc actions SetFlag
6723 Turns the given flag on, regardless of its previous setting. See
6724 @code{ChangeFlag}.
6726 @example
6727 SetFlag(SelectedPins,thermal)
6728 @end example
6730 %end-doc */
6732 static int
6733 ActionSetFlag (int argc, char **argv, Coord x, Coord y)
6735 char *function = ARG (0);
6736 char *flag = ARG (1);
6737 ChangeFlag (function, flag, 1, "SetFlag");
6738 return 0;
6741 /* --------------------------------------------------------------------------- */
6743 static const char clrflag_syntax[] =
6744 N_("ClrFlag(Object|Selected|SelectedObjects, flag)\n"
6745 "ClrFlag(SelectedLines|SelectedPins|SelectedVias, flag)\n"
6746 "ClrFlag(SelectedPads|SelectedTexts|SelectedNames, flag)\n"
6747 "ClrFlag(SelectedElements, flag)\n"
6748 "flag = square | octagon | thermal | join");
6750 static const char clrflag_help[] = N_("Clears flags on objects.");
6752 /* %start-doc actions ClrFlag
6754 Turns the given flag off, regardless of its previous setting. See
6755 @code{ChangeFlag}.
6757 @example
6758 ClrFlag(SelectedLines,join)
6759 @end example
6761 %end-doc */
6763 static int
6764 ActionClrFlag (int argc, char **argv, Coord x, Coord y)
6766 char *function = ARG (0);
6767 char *flag = ARG (1);
6768 ChangeFlag (function, flag, 0, "ClrFlag");
6769 return 0;
6772 /* --------------------------------------------------------------------------- */
6774 static const char changeflag_syntax[] =
6775 N_("ChangeFlag(Object|Selected|SelectedObjects, flag, value)\n"
6776 "ChangeFlag(SelectedLines|SelectedPins|SelectedVias, flag, value)\n"
6777 "ChangeFlag(SelectedPads|SelectedTexts|SelectedNames, flag, value)\n"
6778 "ChangeFlag(SelectedElements, flag, value)\n"
6779 "flag = square | octagon | thermal | join\n"
6780 "value = 0 | 1");
6782 static const char changeflag_help[] = N_("Sets or clears flags on objects.");
6784 /* %start-doc actions ChangeFlag
6786 Toggles the given flag on the indicated object(s). The flag may be
6787 one of the flags listed above (square, octagon, thermal, join). The
6788 value may be the number 0 or 1. If the value is 0, the flag is
6789 cleared. If the value is 1, the flag is set.
6791 %end-doc */
6793 static int
6794 ActionChangeFlag (int argc, char **argv, Coord x, Coord y)
6796 char *function = ARG (0);
6797 char *flag = ARG (1);
6798 int value = argc > 2 ? atoi (argv[2]) : -1;
6799 if (value != 0 && value != 1)
6800 AFAIL (changeflag);
6802 ChangeFlag (function, flag, value, "ChangeFlag");
6803 return 0;
6807 static void
6808 ChangeFlag (char *what, char *flag_name, int value, char *cmd_name)
6810 bool (*set_object) (int, void *, void *, void *);
6811 bool (*set_selected) (int);
6813 if (NSTRCMP (flag_name, "square") == 0)
6815 set_object = value ? SetObjectSquare : ClrObjectSquare;
6816 set_selected = value ? SetSelectedSquare : ClrSelectedSquare;
6818 else if (NSTRCMP (flag_name, "octagon") == 0)
6820 set_object = value ? SetObjectOctagon : ClrObjectOctagon;
6821 set_selected = value ? SetSelectedOctagon : ClrSelectedOctagon;
6823 else if (NSTRCMP (flag_name, "join") == 0)
6825 /* Note: these are backwards, because the flag is "clear" but
6826 the command is "join". */
6827 set_object = value ? ClrObjectJoin : SetObjectJoin;
6828 set_selected = value ? ClrSelectedJoin : SetSelectedJoin;
6830 else
6832 Message (_("%s(): Flag \"%s\" is not valid\n"), cmd_name, flag_name);
6833 return;
6836 switch (GetFunctionID (what))
6838 case F_Object:
6840 int type;
6841 void *ptr1, *ptr2, *ptr3;
6843 if ((type =
6844 SearchScreen (Crosshair.X, Crosshair.Y, CHANGESIZE_TYPES,
6845 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
6846 if (TEST_FLAG (LOCKFLAG, (PinType *) ptr2))
6847 Message (_("Sorry, the object is locked\n"));
6848 if (set_object (type, ptr1, ptr2, ptr3))
6849 SetChangedFlag (true);
6850 break;
6853 case F_SelectedVias:
6854 if (set_selected (VIA_TYPE))
6855 SetChangedFlag (true);
6856 break;
6858 case F_SelectedPins:
6859 if (set_selected (PIN_TYPE))
6860 SetChangedFlag (true);
6861 break;
6863 case F_SelectedPads:
6864 if (set_selected (PAD_TYPE))
6865 SetChangedFlag (true);
6866 break;
6868 case F_SelectedLines:
6869 if (set_selected (LINE_TYPE))
6870 SetChangedFlag (true);
6871 break;
6873 case F_SelectedTexts:
6874 if (set_selected (TEXT_TYPE))
6875 SetChangedFlag (true);
6876 break;
6878 case F_SelectedNames:
6879 if (set_selected (ELEMENTNAME_TYPE))
6880 SetChangedFlag (true);
6881 break;
6883 case F_SelectedElements:
6884 if (set_selected (ELEMENT_TYPE))
6885 SetChangedFlag (true);
6886 break;
6888 case F_Selected:
6889 case F_SelectedObjects:
6890 if (set_selected (CHANGESIZE_TYPES))
6891 SetChangedFlag (true);
6892 break;
6896 /* --------------------------------------------------------------------------- */
6898 static const char executefile_syntax[] = N_("ExecuteFile(filename)");
6900 static const char executefile_help[] = N_("Run actions from the given file.");
6902 /* %start-doc actions ExecuteFile
6904 Lines starting with @code{#} are ignored.
6906 %end-doc */
6908 static int
6909 ActionExecuteFile (int argc, char **argv, Coord x, Coord y)
6911 FILE *fp;
6912 char *fname;
6913 char line[256];
6914 int n = 0;
6915 char *sp;
6917 if (argc != 1)
6918 AFAIL (executefile);
6920 fname = argv[0];
6922 if ((fp = fopen (fname, "r")) == NULL)
6924 fprintf (stderr, _("Could not open actions file \"%s\".\n"), fname);
6925 return 1;
6928 defer_updates = 1;
6929 defer_needs_update = 0;
6930 while (fgets (line, sizeof (line), fp) != NULL)
6932 n++;
6933 sp = line;
6935 /* eat the trailing newline */
6936 while (*sp && *sp != '\r' && *sp != '\n')
6937 sp++;
6938 *sp = '\0';
6940 /* eat leading spaces and tabs */
6941 sp = line;
6942 while (*sp && (*sp == ' ' || *sp == '\t'))
6943 sp++;
6946 * if we have anything left and its not a comment line
6947 * then execute it
6950 if (*sp && *sp != '#')
6952 /*Message ("%s : line %-3d : \"%s\"\n", fname, n, sp);*/
6953 hid_parse_actions (sp);
6957 defer_updates = 0;
6958 if (defer_needs_update)
6960 IncrementUndoSerialNumber ();
6961 gui->invalidate_all ();
6963 fclose (fp);
6964 return 0;
6967 /* --------------------------------------------------------------------------- */
6969 static int
6970 ActionPSCalib (int argc, char **argv, Coord x, Coord y)
6972 HID *ps = hid_find_exporter ("ps");
6973 ps->calibrate (0.0,0.0);
6974 return 0;
6977 /* --------------------------------------------------------------------------- */
6979 static ElementType *element_cache = NULL;
6981 static ElementType *
6982 find_element_by_refdes (char *refdes)
6984 if (element_cache
6985 && NAMEONPCB_NAME(element_cache)
6986 && strcmp (NAMEONPCB_NAME(element_cache), refdes) == 0)
6987 return element_cache;
6989 ELEMENT_LOOP (PCB->Data);
6991 if (NAMEONPCB_NAME(element)
6992 && strcmp (NAMEONPCB_NAME(element), refdes) == 0)
6994 element_cache = element;
6995 return element_cache;
6998 END_LOOP;
6999 return NULL;
7002 static AttributeType *
7003 lookup_attr (AttributeListType *list, const char *name)
7005 int i;
7006 for (i=0; i<list->Number; i++)
7007 if (strcmp (list->List[i].name, name) == 0)
7008 return & list->List[i];
7009 return NULL;
7012 static void
7013 delete_attr (AttributeListType *list, AttributeType *attr)
7015 int idx = attr - list->List;
7016 if (idx < 0 || idx >= list->Number)
7017 return;
7018 if (list->Number - idx > 1)
7019 memmove (attr, attr+1, (list->Number - idx - 1) * sizeof(AttributeType));
7020 list->Number --;
7023 /* ---------------------------------------------------------------- */
7024 static const char elementlist_syntax[] =
7025 N_("ElementList(Start|Done|Need,<refdes>,<footprint>,<value>)");
7027 static const char elementlist_help[] =
7028 N_("Adds the given element if it doesn't already exist.");
7030 /* %start-doc actions elementlist
7032 @table @code
7034 @item Start
7035 Indicates the start of an element list; call this before any Need
7036 actions.
7038 @item Need
7039 Searches the board for an element with a matching refdes.
7041 If found, the value and footprint are updated.
7043 If not found, a new element is created with the given footprint and value.
7045 @item Done
7046 Compares the list of elements needed since the most recent
7047 @code{start} with the list of elements actually on the board. Any
7048 elements that weren't listed are selected, so that the user may delete
7049 them.
7051 @end table
7053 %end-doc */
7055 static int number_of_footprints_not_found;
7057 static int
7058 parse_layout_attribute_units (char *name, int def)
7060 const char *as = AttributeGet (PCB, name);
7061 if (!as)
7062 return def;
7063 return GetValue (as, NULL, NULL);
7066 static int
7067 ActionElementList (int argc, char **argv, Coord x, Coord y)
7069 ElementType *e = NULL;
7070 char *refdes, *value, *footprint, *old;
7071 char *args[3];
7072 char *function;
7074 if (argc < 1)
7075 AFAIL (elementlist);
7077 function = argv[0];
7079 #ifdef DEBUG
7080 printf("Entered ActionElementList, executing function %s\n", function);
7081 #endif
7083 if (strcasecmp (function, "start") == 0)
7085 ELEMENT_LOOP (PCB->Data);
7087 CLEAR_FLAG (FOUNDFLAG, element);
7089 END_LOOP;
7090 element_cache = NULL;
7091 number_of_footprints_not_found = 0;
7092 return 0;
7095 if (strcasecmp (function, "done") == 0)
7097 ELEMENT_LOOP (PCB->Data);
7099 if (TEST_FLAG (FOUNDFLAG, element))
7101 CLEAR_FLAG (FOUNDFLAG, element);
7103 else if (! EMPTY_STRING_P (NAMEONPCB_NAME (element)))
7105 /* Unnamed elements should remain untouched */
7106 SET_FLAG (SELECTEDFLAG, element);
7109 END_LOOP;
7110 if (number_of_footprints_not_found > 0)
7111 gui->confirm_dialog (_("Not all requested footprints were found.\n"
7112 "See the message log for details"),
7113 "Ok", NULL);
7114 return 0;
7117 if (strcasecmp (function, "need") != 0)
7118 AFAIL (elementlist);
7120 if (argc != 4)
7121 AFAIL (elementlist);
7123 argc --;
7124 argv ++;
7126 refdes = ARG(0);
7127 footprint = ARG(1);
7128 value = ARG(2);
7130 args[0] = footprint;
7131 args[1] = refdes;
7132 args[2] = value;
7134 #ifdef DEBUG
7135 printf(" ... footprint = %s\n", footprint);
7136 printf(" ... refdes = %s\n", refdes);
7137 printf(" ... value = %s\n", value);
7138 #endif
7140 e = find_element_by_refdes (refdes);
7142 if (!e)
7144 Coord nx, ny, d;
7146 #ifdef DEBUG
7147 printf(" ... Footprint not on board, need to add it.\n");
7148 #endif
7149 /* Not on board, need to add it. */
7150 if (LoadFootprint(argc, args, x, y))
7152 number_of_footprints_not_found ++;
7153 return 1;
7156 nx = PCB->MaxWidth / 2;
7157 ny = PCB->MaxHeight / 2;
7158 d = MIN (PCB->MaxWidth, PCB->MaxHeight) / 10;
7160 nx = parse_layout_attribute_units ("import::newX", nx);
7161 ny = parse_layout_attribute_units ("import::newY", ny);
7162 d = parse_layout_attribute_units ("import::disperse", d);
7164 if (d > 0)
7166 nx += rand () % (d*2) - d;
7167 ny += rand () % (d*2) - d;
7170 if (nx < 0)
7171 nx = 0;
7172 if (nx >= PCB->MaxWidth)
7173 nx = PCB->MaxWidth - 1;
7174 if (ny < 0)
7175 ny = 0;
7176 if (ny >= PCB->MaxHeight)
7177 ny = PCB->MaxHeight - 1;
7179 /* Place components onto center of board. */
7180 if (CopyPastebufferToLayout (nx, ny))
7181 SetChangedFlag (true);
7184 else if (e && DESCRIPTION_NAME(e) && strcmp (DESCRIPTION_NAME(e), footprint) != 0)
7186 #ifdef DEBUG
7187 printf(" ... Footprint on board, but different from footprint loaded.\n");
7188 #endif
7189 int er, pr, i;
7190 Coord mx, my;
7191 ElementType *pe;
7193 /* Different footprint, we need to swap them out. */
7194 if (LoadFootprint(argc, args, x, y))
7196 number_of_footprints_not_found ++;
7197 return 1;
7200 er = ElementOrientation (e);
7201 pe = PASTEBUFFER->Data->Element->data;
7202 if (!FRONT (e))
7203 MirrorElementCoordinates (PASTEBUFFER->Data, pe, pe->MarkY*2 - PCB->MaxHeight);
7204 pr = ElementOrientation (pe);
7206 mx = e->MarkX;
7207 my = e->MarkY;
7209 if (er != pr)
7210 RotateElementLowLevel (PASTEBUFFER->Data, pe, pe->MarkX, pe->MarkY, (er-pr+4)%4);
7212 for (i=0; i<MAX_ELEMENTNAMES; i++)
7214 pe->Name[i].X = e->Name[i].X - mx + pe->MarkX ;
7215 pe->Name[i].Y = e->Name[i].Y - my + pe->MarkY ;
7216 pe->Name[i].Direction = e->Name[i].Direction;
7217 pe->Name[i].Scale = e->Name[i].Scale;
7220 RemoveElement (e);
7222 if (CopyPastebufferToLayout (mx, my))
7223 SetChangedFlag (true);
7226 /* Now reload footprint */
7227 element_cache = NULL;
7228 e = find_element_by_refdes (refdes);
7230 old = ChangeElementText (PCB, PCB->Data, e, NAMEONPCB_INDEX, strdup (refdes));
7231 if (old)
7232 free(old);
7233 old = ChangeElementText (PCB, PCB->Data, e, VALUE_INDEX, strdup (value));
7234 if (old)
7235 free(old);
7237 SET_FLAG (FOUNDFLAG, e);
7239 #ifdef DEBUG
7240 printf(" ... Leaving ActionElementList.\n");
7241 #endif
7243 return 0;
7246 /* ---------------------------------------------------------------- */
7247 static const char elementsetattr_syntax[] =
7248 N_("ElementSetAttr(refdes,name[,value])");
7250 static const char elementsetattr_help[] =
7251 N_("Sets or clears an element-specific attribute.");
7253 /* %start-doc actions elementsetattr
7255 If a value is specified, the named attribute is added (if not already
7256 present) or changed (if it is) to the given value. If the value is
7257 not specified, the given attribute is removed if present.
7259 %end-doc */
7261 static int
7262 ActionElementSetAttr (int argc, char **argv, Coord x, Coord y)
7264 ElementType *e = NULL;
7265 char *refdes, *name, *value;
7266 AttributeType *attr;
7268 if (argc < 2)
7270 AFAIL (elementsetattr);
7273 refdes = argv[0];
7274 name = argv[1];
7275 value = ARG(2);
7277 ELEMENT_LOOP (PCB->Data);
7279 if (NSTRCMP (refdes, NAMEONPCB_NAME (element)) == 0)
7281 e = element;
7282 break;
7285 END_LOOP;
7287 if (!e)
7289 Message(_("Cannot change attribute of %s - element not found\n"), refdes);
7290 return 1;
7293 attr = lookup_attr (&e->Attributes, name);
7295 if (attr && value)
7297 free (attr->value);
7298 attr->value = strdup (value);
7300 if (attr && ! value)
7302 delete_attr (& e->Attributes, attr);
7304 if (!attr && value)
7306 CreateNewAttribute (& e->Attributes, name, value);
7309 return 0;
7312 /* ---------------------------------------------------------------- */
7313 static const char execcommand_syntax[] = N_("ExecCommand(command)");
7315 static const char execcommand_help[] = N_("Runs a command.");
7317 /* %start-doc actions execcommand
7319 Runs the given command, which is a system executable.
7321 %end-doc */
7323 static int
7324 ActionExecCommand (int argc, char **argv, Coord x, Coord y)
7326 char *command;
7328 if (argc < 1)
7330 AFAIL (execcommand);
7333 command = ARG(0);
7335 if (system (command))
7336 return 1;
7337 return 0;
7340 /* ---------------------------------------------------------------- */
7342 static int
7343 pcb_spawnvp (char **argv)
7345 #ifdef HAVE__SPAWNVP
7346 int result = _spawnvp (_P_WAIT, argv[0], (const char * const *) argv);
7347 if (result == -1)
7348 return 1;
7349 else
7350 return 0;
7351 #else
7352 int pid;
7353 pid = fork ();
7354 if (pid < 0)
7356 /* error */
7357 Message(_("Cannot fork!"));
7358 return 1;
7360 else if (pid == 0)
7362 /* Child */
7363 execvp (argv[0], argv);
7364 exit(1);
7366 else
7368 int rv;
7369 /* Parent */
7370 wait (&rv);
7372 return 0;
7373 #endif
7376 /* ---------------------------------------------------------------- */
7378 /*!
7379 * \brief Creates a new temporary file name.
7381 * Hopefully the operating system provides a mkdtemp() function to
7382 * securily create a temporary directory with mode 0700.\n
7383 * If so then that directory is created and the returned string is made
7384 * up of the directory plus the name variable.\n
7385 * For example:\n
7387 * tempfile_name_new ("myfile") might return
7388 * "/var/tmp/pcb.123456/myfile".
7390 * If mkdtemp() is not available then 'name' is ignored and the
7391 * insecure tmpnam() function is used.
7393 * Files/names created with tempfile_name_new() should be unlinked
7394 * with tempfile_unlink to make sure the temporary directory is also
7395 * removed when mkdtemp() is used.
7397 static char *
7398 tempfile_name_new (char * name)
7400 char *tmpfile = NULL;
7401 #ifdef HAVE_MKDTEMP
7402 char *tmpdir, *mytmpdir;
7403 size_t len;
7404 #endif
7406 assert ( name != NULL );
7408 #ifdef HAVE_MKDTEMP
7409 #define TEMPLATE "pcb.XXXXXXXX"
7412 tmpdir = getenv ("TMPDIR");
7414 /* FIXME -- what about win32? */
7415 if (tmpdir == NULL) {
7416 tmpdir = "/tmp";
7419 mytmpdir = (char *) malloc (sizeof(char) *
7420 (strlen (tmpdir) +
7422 strlen (TEMPLATE) +
7423 1));
7424 if (mytmpdir == NULL) {
7425 fprintf (stderr, "%s(): malloc failed()\n", __FUNCTION__);
7426 exit (1);
7429 *mytmpdir = '\0';
7430 (void)strcat (mytmpdir, tmpdir);
7431 (void)strcat (mytmpdir, PCB_DIR_SEPARATOR_S);
7432 (void)strcat (mytmpdir, TEMPLATE);
7433 if (mkdtemp (mytmpdir) == NULL) {
7434 fprintf (stderr, "%s(): mkdtemp (\"%s\") failed\n", __FUNCTION__, mytmpdir);
7435 free (mytmpdir);
7436 return NULL;
7440 len = strlen (mytmpdir) + /* the temp directory name */
7441 1 + /* the directory sep. */
7442 strlen (name) + /* the file name */
7443 1 /* the \0 termination */
7446 tmpfile = (char *) malloc (sizeof (char) * len);
7448 *tmpfile = '\0';
7449 (void)strcat (tmpfile, mytmpdir);
7450 (void)strcat (tmpfile, PCB_DIR_SEPARATOR_S);
7451 (void)strcat (tmpfile, name);
7453 free (mytmpdir);
7454 #undef TEMPLATE
7455 #else
7457 * tmpnam() uses a static buffer so strdup() the result right away
7458 * in case someone decides to create multiple temp names.
7460 tmpfile = strdup (tmpnam (NULL));
7461 #ifdef __WIN32__
7463 /* Guile doesn't like \ separators */
7464 char *c;
7465 for (c = tmpfile; *c; c++)
7466 if (*c == '\\')
7467 *c = '/';
7469 #endif
7470 #endif
7472 return tmpfile;
7475 /* ---------------------------------------------------------------- */
7478 * \brief Unlink a temporary file.
7480 * If we have mkdtemp() then our temp file lives in a temporary
7481 * directory and we need to remove that directory too.
7483 static int
7484 tempfile_unlink (char * name)
7486 #ifdef DEBUG
7487 /* SDB says: Want to keep old temp files for examiniation when debugging */
7488 return 0;
7489 #endif
7491 #ifdef HAVE_MKDTEMP
7492 int e, rc2 = 0;
7493 char *dname;
7495 unlink (name);
7496 /* it is possible that the file was never created so it is OK if the
7497 unlink fails */
7499 /* now figure out the directory name to remove */
7500 e = strlen (name) - 1;
7501 while (e > 0 && name[e] != PCB_DIR_SEPARATOR_C) {e--;}
7503 dname = strdup (name);
7504 dname[e] = '\0';
7507 * at this point, e *should* point to the end of the directory part
7508 * but lets make sure.
7510 if (e > 0) {
7511 rc2 = rmdir (dname);
7512 if (rc2 != 0) {
7513 perror (dname);
7516 } else {
7517 fprintf (stderr, _("%s(): Unable to determine temp directory name from the temp file\n"),
7518 __FUNCTION__);
7519 fprintf (stderr, "%s(): \"%s\"\n",
7520 __FUNCTION__, name);
7521 rc2 = -1;
7524 /* name was allocated with malloc */
7525 free (dname);
7526 free (name);
7529 * FIXME - should also return -1 if the temp file exists and was not
7530 * removed.
7532 if (rc2 != 0) {
7533 return -1;
7536 #else
7537 int rc = unlink (name);
7539 if (rc != 0) {
7540 fprintf (stderr, _("Failed to unlink \"%s\"\n"), name);
7541 free (name);
7542 return rc;
7544 free (name);
7546 #endif
7548 return 0;
7551 /* ---------------------------------------------------------------- */
7552 static const char import_syntax[] =
7553 N_("Import()\n"
7554 "Import([gnetlist|make[,source,source,...]])\n"
7555 "Import(setnewpoint[,(mark|center|X,Y)])\n"
7556 "Import(setdisperse,D,units)\n");
7558 static const char import_help[] = N_("Import schematics.");
7560 /* %start-doc actions Import
7562 Imports element and netlist data from the schematics (or some other
7563 source). The first parameter, which is optional, is the mode. If not
7564 specified, the @code{import::mode} attribute in the PCB is used.
7565 @code{gnetlist} means gnetlist is used to obtain the information from
7566 the schematics. @code{make} invokes @code{make}, assuming the user
7567 has a @code{Makefile} in the current directory. The @code{Makefile}
7568 will be invoked with the following variables set:
7570 @table @code
7572 @item PCB
7573 The name of the .pcb file
7575 @item SRCLIST
7576 A space-separated list of source files
7578 @item OUT
7579 The name of the file in which to put the command script, which may
7580 contain any @pcb{} actions. By default, this is a temporary file
7581 selected by @pcb{}, but if you specify an @code{import::outfile}
7582 attribute, that file name is used instead (and not automatically
7583 deleted afterwards).
7585 @end table
7587 The target specified to be built is the first of these that apply:
7589 @itemize @bullet
7591 @item
7592 The target specified by an @code{import::target} attribute.
7594 @item
7595 The output file specified by an @code{import::outfile} attribute.
7597 @item
7598 If nothing else is specified, the target is @code{pcb_import}.
7600 @end itemize
7602 If you specify an @code{import::makefile} attribute, then "-f <that
7603 file>" will be added to the command line.
7605 If you specify the mode, you may also specify the source files
7606 (schematics). If you do not specify any, the list of schematics is
7607 obtained by reading the @code{import::src@var{N}} attributes (like
7608 @code{import::src0}, @code{import::src1}, etc).
7610 For compatibility with future extensions to the import file format,
7611 the generated file @emph{must not} start with the two characters
7612 @code{#%}.
7614 If a temporary file is needed the @code{TMPDIR} environment variable
7615 is used to select its location.
7617 Note that the programs @code{gnetlist} and @code{make} may be
7618 overridden by the user via the @code{make-program} and @code{gnetlist}
7619 @code{pcb} settings (i.e. in @code{~/.pcb/settings} or on the command
7620 line).
7622 If @pcb{} cannot determine which schematic(s) to import from, the GUI
7623 is called to let user choose (see @code{ImportGUI()}).
7625 Note that Import() doesn't delete anything - after an Import, elements
7626 which shouldn't be on the board are selected and may be removed once
7627 it's determined that the deletion is appropriate.
7629 If @code{Import()} is called with @code{setnewpoint}, then the location
7630 of new components can be specified. This is where parts show up when
7631 they're added to the board. The default is the center of the board.
7633 @table @code
7635 @item Import(setnewpoint)
7637 Prompts the user to click on the board somewhere, uses that point. If
7638 called by a hotkey, uses the current location of the crosshair.
7640 @item Import(setnewpoint,mark)
7642 Uses the location of the mark. If no mark is present, the point is
7643 not changed.
7645 @item Import(setnewpoint,center)
7647 Resets the point to the center of the board.
7649 @item Import(setnewpoint,X,Y,units)
7651 Sets the point to the specific coordinates given. Example:
7652 @code{Import(setnewpoint,50,25,mm)}
7654 @end table
7656 Note that the X and Y locations are stored in attributes named
7657 @code{import::newX} and @code{import::newY} so you could change them
7658 manually if you wished.
7660 Calling @code{Import(setdisperse,D,units)} sets how much the newly
7661 placed elements are dispersed relative to the set point. For example,
7662 @code{Import(setdisperse,10,mm)} will offset each part randomly up to
7663 10mm away from the point. The default dispersion is 1/10th of the
7664 smallest board dimension. Dispersion is saved in the
7665 @code{import::disperse} attribute.
7667 %end-doc */
7669 static int
7670 ActionImport (int argc, char **argv, Coord x, Coord y)
7672 char *mode;
7673 char **sources = NULL;
7674 int nsources = 0;
7676 #ifdef DEBUG
7677 printf("ActionImport: =========== Entering ActionImport ============\n");
7678 #endif
7680 mode = ARG (0);
7682 if (mode && strcasecmp (mode, "setdisperse") == 0)
7684 char *ds, *units;
7685 char buf[50];
7687 ds = ARG (1);
7688 units = ARG (2);
7689 if (!ds)
7691 const char *as = AttributeGet (PCB, "import::disperse");
7692 ds = gui->prompt_for(_("Enter dispersion:"), as ? as : "0");
7694 if (units)
7696 sprintf(buf, "%s%s", ds, units);
7697 AttributePut (PCB, "import::disperse", buf);
7699 else
7700 AttributePut (PCB, "import::disperse", ds);
7701 if (ARG (1) == NULL)
7702 free (ds);
7703 return 0;
7706 if (mode && strcasecmp (mode, "setnewpoint") == 0)
7708 const char *xs, *ys, *units;
7709 Coord x, y;
7710 char buf[50];
7712 xs = ARG (1);
7713 ys = ARG (2);
7714 units = ARG (3);
7716 if (!xs)
7718 gui->get_coords (_("Click on a location"), &x, &y);
7720 else if (strcasecmp (xs, "center") == 0)
7722 AttributeRemove (PCB, "import::newX");
7723 AttributeRemove (PCB, "import::newY");
7724 return 0;
7726 else if (strcasecmp (xs, "mark") == 0)
7728 if (!Marked.status)
7729 return 0;
7731 x = Marked.X;
7732 y = Marked.Y;
7734 else if (ys)
7736 x = GetValue (xs, units, NULL);
7737 y = GetValue (ys, units, NULL);
7739 else
7741 Message (_("Bad syntax for Import(setnewpoint)"));
7742 return 1;
7745 pcb_snprintf (buf, sizeof (buf), "%$ms", x);
7746 AttributePut (PCB, "import::newX", buf);
7747 pcb_snprintf (buf, sizeof (buf), "%$ms", y);
7748 AttributePut (PCB, "import::newY", buf);
7749 return 0;
7752 if (! mode)
7753 mode = AttributeGet (PCB, "import::mode");
7754 if (! mode)
7755 mode = "gnetlist";
7757 if (argc > 1)
7759 sources = argv + 1;
7760 nsources = argc - 1;
7763 if (! sources)
7765 char sname[40];
7766 char *src;
7768 nsources = -1;
7769 do {
7770 nsources ++;
7771 sprintf(sname, "import::src%d", nsources);
7772 src = AttributeGet (PCB, sname);
7773 } while (src);
7775 if (nsources > 0)
7777 sources = (char **) malloc ((nsources + 1) * sizeof (char *));
7778 nsources = -1;
7779 do {
7780 nsources ++;
7781 sprintf(sname, "import::src%d", nsources);
7782 src = AttributeGet (PCB, sname);
7783 sources[nsources] = src;
7784 } while (src);
7788 if (! sources)
7790 /* Replace .pcb with .sch and hope for the best. */
7791 char *pcbname = PCB->Filename;
7792 char *schname;
7793 char *dot, *slash, *bslash;
7795 if (!pcbname)
7796 return hid_action("ImportGUI");
7798 schname = (char *) malloc (strlen(pcbname) + 5);
7799 strcpy (schname, pcbname);
7800 dot = strchr (schname, '.');
7801 slash = strchr (schname, '/');
7802 bslash = strchr (schname, '\\');
7803 if (dot && slash && dot < slash)
7804 dot = NULL;
7805 if (dot && bslash && dot < bslash)
7806 dot = NULL;
7807 if (dot)
7808 *dot = 0;
7809 strcat (schname, ".sch");
7811 if (access (schname, F_OK))
7813 free (schname);
7814 return hid_action("ImportGUI");
7817 sources = (char **) malloc (2 * sizeof (char *));
7818 sources[0] = schname;
7819 sources[1] = NULL;
7820 nsources = 1;
7823 if (strcasecmp (mode, "gnetlist") == 0)
7825 char *tmpfile = tempfile_name_new ("gnetlist_output");
7826 char **cmd;
7827 int i;
7829 if (tmpfile == NULL) {
7830 Message (_("Could not create temp file"));
7831 return 1;
7834 cmd = (char **) malloc ((7 + nsources) * sizeof (char *));
7835 cmd[0] = Settings.GnetlistProgram;
7836 cmd[1] = "-g";
7837 cmd[2] = "pcbfwd";
7838 cmd[3] = "-o";
7839 cmd[4] = tmpfile;
7840 cmd[5] = "--";
7841 for (i=0; i<nsources; i++)
7842 cmd[6+i] = sources[i];
7843 cmd[6+nsources] = NULL;
7845 #ifdef DEBUG
7846 printf("ActionImport: =========== About to run gnetlist ============\n");
7847 printf("%s %s %s %s %s %s %s ...\n",
7848 cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6]);
7849 #endif
7851 if (pcb_spawnvp (cmd))
7853 unlink (tmpfile);
7854 return 1;
7857 #ifdef DEBUG
7858 printf("ActionImport: =========== About to run ActionExecuteFile, file = %s ============\n", tmpfile);
7859 #endif
7861 cmd[0] = tmpfile;
7862 cmd[1] = NULL;
7863 ActionExecuteFile (1, cmd, 0, 0);
7865 free (cmd);
7866 tempfile_unlink (tmpfile);
7868 else if (strcasecmp (mode, "make") == 0)
7870 int must_free_tmpfile = 0;
7871 char *tmpfile;
7872 char *cmd[10];
7873 int i;
7874 char *srclist;
7875 int srclen;
7876 char *user_outfile = NULL;
7877 char *user_makefile = NULL;
7878 char *user_target = NULL;
7881 user_outfile = AttributeGet (PCB, "import::outfile");
7882 user_makefile = AttributeGet (PCB, "import::makefile");
7883 user_target = AttributeGet (PCB, "import::target");
7884 if (user_outfile && !user_target)
7885 user_target = user_outfile;
7887 if (user_outfile)
7888 tmpfile = user_outfile;
7889 else
7891 tmpfile = tempfile_name_new ("gnetlist_output");
7892 if (tmpfile == NULL) {
7893 Message (_("Could not create temp file"));
7894 free (sources);
7895 return 1;
7897 must_free_tmpfile = 1;
7900 srclen = sizeof("SRCLIST=") + 2;
7901 for (i=0; i<nsources; i++)
7902 srclen += strlen (sources[i]) + 2;
7903 srclist = (char *) malloc (srclen);
7904 strcpy (srclist, "SRCLIST=");
7905 for (i=0; i<nsources; i++)
7907 if (i)
7908 strcat (srclist, " ");
7909 strcat (srclist, sources[i]);
7912 cmd[0] = Settings.MakeProgram;
7913 cmd[1] = "-s";
7914 cmd[2] = Concat ("PCB=", PCB->Filename, NULL);
7915 cmd[3] = srclist;
7916 cmd[4] = Concat ("OUT=", tmpfile, NULL);
7917 i = 5;
7918 if (user_makefile)
7920 cmd[i++] = "-f";
7921 cmd[i++] = user_makefile;
7923 cmd[i++] = user_target ? user_target : (char *)"pcb_import";
7924 cmd[i++] = NULL;
7926 if (pcb_spawnvp (cmd))
7928 if (must_free_tmpfile)
7929 unlink (tmpfile);
7930 free (cmd[2]);
7931 free (cmd[3]);
7932 free (cmd[4]);
7933 return 1;
7936 cmd[0] = tmpfile;
7937 cmd[1] = NULL;
7938 ActionExecuteFile (1, cmd, 0, 0);
7940 free (cmd[2]);
7941 free (cmd[3]);
7942 free (cmd[4]);
7943 if (must_free_tmpfile)
7944 tempfile_unlink (tmpfile);
7946 else
7948 Message (_("Unknown import mode: %s\n"), mode);
7949 return 1;
7952 DeleteRats (false);
7953 AddAllRats (false, NULL);
7955 #ifdef DEBUG
7956 printf("ActionImport: =========== Leaving ActionImport ============\n");
7957 #endif
7959 return 0;
7962 /* ------------------------------------------------------------ */
7964 static const char attributes_syntax[] =
7965 N_("Attributes(Layout|Layer|Element)\n"
7966 "Attributes(Layer,layername)");
7968 static const char attributes_help[] =
7969 N_("Let the user edit the attributes of the layout, current or given\n"
7970 "layer, or selected element.");
7972 /* %start-doc actions Attributes
7974 This just pops up a dialog letting the user edit the attributes of the
7975 pcb, an element, or a layer.
7977 %end-doc */
7980 static int
7981 ActionAttributes (int argc, char **argv, Coord x, Coord y)
7983 char *function = ARG (0);
7984 char *layername = ARG (1);
7985 char *buf;
7987 if (!function)
7988 AFAIL (attributes);
7990 if (!gui->edit_attributes)
7992 Message (_("This GUI doesn't support Attribute Editing\n"));
7993 return 1;
7996 switch (GetFunctionID (function))
7998 case F_Layout:
8000 gui->edit_attributes(_("Layout Attributes"), &(PCB->Attributes));
8001 return 0;
8004 case F_Layer:
8006 LayerType *layer = CURRENT;
8007 if (layername)
8009 int i;
8010 layer = NULL;
8011 for (i=0; i<max_copper_layer; i++)
8012 if (strcmp (PCB->Data->Layer[i].Name, layername) == 0)
8014 layer = & (PCB->Data->Layer[i]);
8015 break;
8017 if (layer == NULL)
8019 Message (_("No layer named %s\n"), layername);
8020 return 1;
8023 buf = (char *) malloc (strlen (layer->Name) +
8024 strlen (_("Layer %s Attributes")));
8025 sprintf (buf, _("Layer %s Attributes"), layer->Name);
8026 gui->edit_attributes(buf, &(layer->Attributes));
8027 free (buf);
8028 return 0;
8031 case F_Element:
8033 int n_found = 0;
8034 ElementType *e = NULL;
8035 ELEMENT_LOOP (PCB->Data);
8037 if (TEST_FLAG (SELECTEDFLAG, element))
8039 e = element;
8040 n_found ++;
8043 END_LOOP;
8044 if (n_found > 1)
8046 Message (_("Too many elements selected\n"));
8047 return 1;
8049 if (n_found == 0)
8051 void *ptrtmp;
8052 gui->get_coords (_("Click on an element"), &x, &y);
8053 if ((SearchScreen
8054 (x, y, ELEMENT_TYPE, &ptrtmp,
8055 &ptrtmp, &ptrtmp)) != NO_TYPE)
8056 e = (ElementType *) ptrtmp;
8057 else
8059 Message (_("No element found there\n"));
8060 return 1;
8064 if (NAMEONPCB_NAME(e))
8066 buf = (char *) malloc (strlen (NAMEONPCB_NAME(e)) +
8067 strlen (_("Element %s Attributes")));
8068 sprintf(buf, _("Element %s Attributes"), NAMEONPCB_NAME(e));
8070 else
8072 buf = strdup (_("Unnamed Element Attributes"));
8074 gui->edit_attributes(buf, &(e->Attributes));
8075 free (buf);
8076 break;
8079 default:
8080 AFAIL (attributes);
8083 return 0;
8086 /* --------------------------------------------------------------------------- */
8088 HID_Action action_action_list[] = {
8089 {"AddRats", 0, ActionAddRats,
8090 addrats_help, addrats_syntax}
8092 {"Attributes", 0, ActionAttributes,
8093 attributes_help, attributes_syntax}
8095 {"Atomic", 0, ActionAtomic,
8096 atomic_help, atomic_syntax}
8098 {"AutoPlaceSelected", 0, ActionAutoPlaceSelected,
8099 autoplace_help, autoplace_syntax}
8101 {"AutoRoute", 0, ActionAutoRoute,
8102 autoroute_help, autoroute_syntax}
8104 {"ChangeClearSize", 0, ActionChangeClearSize,
8105 changeclearsize_help, changeclearsize_syntax}
8107 {"ChangeDrillSize", 0, ActionChange2ndSize,
8108 changedrillsize_help, changedrillsize_syntax}
8110 {"ChangeHole", 0, ActionChangeHole,
8111 changehold_help, changehold_syntax}
8113 {"ChangeJoin", 0, ActionChangeJoin,
8114 changejoin_help, changejoin_syntax}
8116 {"ChangeName", 0, ActionChangeName,
8117 changename_help, changename_syntax}
8119 {"ChangePaste", 0, ActionChangePaste,
8120 changepaste_help, changepaste_syntax}
8122 {"ChangePinName", 0, ActionChangePinName,
8123 changepinname_help, changepinname_syntax}
8125 {"ChangeSize", 0, ActionChangeSize,
8126 changesize_help, changesize_syntax}
8128 {"ChangeSquare", 0, ActionChangeSquare,
8129 changesquare_help, changesquare_syntax}
8131 {"ChangeOctagon", 0, ActionChangeOctagon,
8132 changeoctagon_help, changeoctagon_syntax}
8134 {"ClearSquare", 0, ActionClearSquare,
8135 clearsquare_help, clearsquare_syntax}
8137 {"ClearOctagon", 0, ActionClearOctagon,
8138 clearoctagon_help, clearoctagon_syntax}
8140 {"Connection", 0, ActionConnection,
8141 connection_help, connection_syntax}
8143 {"Delete", 0, ActionDelete,
8144 delete_help, delete_syntax}
8146 {"DeleteRats", 0, ActionDeleteRats,
8147 deleterats_help, deleterats_syntax}
8149 {"DisperseElements", 0, ActionDisperseElements,
8150 disperseelements_help, disperseelements_syntax}
8152 {"Display", 0, ActionDisplay,
8153 display_help, display_syntax}
8155 {"DRC", 0, ActionDRCheck,
8156 drc_help, drc_syntax}
8158 {"DumpLibrary", 0, ActionDumpLibrary,
8159 dumplibrary_help, dumplibrary_syntax}
8161 {"ExecuteFile", 0, ActionExecuteFile,
8162 executefile_help, executefile_syntax}
8164 {"Flip", N_("Click on Object or Flip Point"), ActionFlip,
8165 flip_help, flip_syntax}
8167 {"LoadFrom", 0, ActionLoadFrom,
8168 loadfrom_help, loadfrom_syntax}
8170 {"MarkCrosshair", 0, ActionMarkCrosshair,
8171 markcrosshair_help, markcrosshair_syntax}
8173 {"Message", 0, ActionMessage,
8174 message_help, message_syntax}
8176 {"MinMaskGap", 0, ActionMinMaskGap,
8177 minmaskgap_help, minmaskgap_syntax}
8179 {"MinClearGap", 0, ActionMinClearGap,
8180 mincleargap_help, mincleargap_syntax}
8182 {"Mode", 0, ActionMode,
8183 mode_help, mode_syntax}
8185 {"MorphPolygon", 0, ActionMorphPolygon,
8186 morphpolygon_help, morphpolygon_syntax}
8188 {"PasteBuffer", 0, ActionPasteBuffer,
8189 pastebuffer_help, pastebuffer_syntax}
8191 {"Quit", 0, ActionQuit,
8192 quit_help, quit_syntax}
8194 {"RemoveSelected", 0, ActionRemoveSelected,
8195 removeselected_help, removeselected_syntax}
8197 {"Renumber", 0, ActionRenumber,
8198 renumber_help, renumber_syntax}
8200 {"RipUp", 0, ActionRipUp,
8201 ripup_help, ripup_syntax}
8203 {"Select", 0, ActionSelect,
8204 select_help, select_syntax}
8206 {"Unselect", 0, ActionUnselect,
8207 unselect_help, unselect_syntax}
8209 {"SaveSettings", 0, ActionSaveSettings,
8210 savesettings_help, savesettings_syntax}
8212 {"SaveTo", 0, ActionSaveTo,
8213 saveto_help, saveto_syntax}
8215 {"SetSquare", 0, ActionSetSquare,
8216 setsquare_help, setsquare_syntax}
8218 {"SetOctagon", 0, ActionSetOctagon,
8219 setoctagon_help, setoctagon_syntax}
8221 {"SetThermal", 0, ActionSetThermal,
8222 setthermal_help, setthermal_syntax}
8224 {"SetValue", 0, ActionSetValue,
8225 setvalue_help, setvalue_syntax}
8227 {"ToggleHideName", 0, ActionToggleHideName,
8228 togglehidename_help, togglehidename_syntax}
8230 {"Undo", 0, ActionUndo,
8231 undo_help, undo_syntax}
8233 {"Redo", 0, ActionRedo,
8234 redo_help, redo_syntax}
8236 {"SetSame", N_("Select item to use attributes from"), ActionSetSame,
8237 setsame_help, setsame_syntax}
8239 {"SetFlag", 0, ActionSetFlag,
8240 setflag_help, setflag_syntax}
8242 {"ClrFlag", 0, ActionClrFlag,
8243 clrflag_help, clrflag_syntax}
8245 {"ChangeFlag", 0, ActionChangeFlag,
8246 changeflag_help, changeflag_syntax}
8248 {"Polygon", 0, ActionPolygon,
8249 polygon_help, polygon_syntax}
8251 {"RouteStyle", 0, ActionRouteStyle,
8252 routestyle_help, routestyle_syntax}
8254 {"MoveObject", N_("Select an Object"), ActionMoveObject,
8255 moveobject_help, moveobject_syntax}
8257 {"MoveToCurrentLayer", 0, ActionMoveToCurrentLayer,
8258 movetocurrentlayer_help, movetocurrentlayer_syntax}
8260 {"New", 0, ActionNew,
8261 new_help, new_syntax}
8263 {"pscalib", 0, ActionPSCalib}
8265 {"ElementList", 0, ActionElementList,
8266 elementlist_help, elementlist_syntax}
8268 {"ElementSetAttr", 0, ActionElementSetAttr,
8269 elementsetattr_help, elementsetattr_syntax}
8271 {"ExecCommand", 0, ActionExecCommand,
8272 execcommand_help, execcommand_syntax}
8274 {"Import", 0, ActionImport,
8275 import_help, import_syntax}
8279 REGISTER_ACTIONS (action_action_list)