Fix distribution of gnet-pcbfwd.scm in the dist tarball
[geda-pcb/gde.git] / src / action.c
blobd7c754bda8415ca7618acf25cf57aaf143651a4c
1 /* 15 Oct 2008 Ineiev: add CycleCrosshair action */
3 /*
4 * COPYRIGHT
6 * PCB, interactive printed circuit board design
7 * Copyright (C) 1994,1995,1996 Thomas Nau
8 * Copyright (C) 1997, 1998, 1999, 2000, 2001 Harry Eaton
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24 * Contact addresses for paper mail and Email:
25 * Harry Eaton, 6697 Buttonhole Ct, Columbia, MD 21044, USA
26 * haceaton@aplcomm.jhuapl.edu
30 /* action routines for output window
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
37 #include "global.h"
39 #include "action.h"
40 #include "autoplace.h"
41 #include "autoroute.h"
42 #include "buffer.h"
43 #include "change.h"
44 #include "command.h"
45 #include "copy.h"
46 #include "create.h"
47 #include "crosshair.h"
48 #include "data.h"
49 #include "draw.h"
50 #include "error.h"
51 #include "file.h"
52 #include "find.h"
53 #include "hid.h"
54 #include "insert.h"
55 #include "line.h"
56 #include "mymem.h"
57 #include "misc.h"
58 #include "move.h"
59 #include "polygon.h"
60 /*#include "print.h"*/
61 #include "rats.h"
62 #include "remove.h"
63 #include "report.h"
64 #include "rotate.h"
65 #include "rubberband.h"
66 #include "search.h"
67 #include "select.h"
68 #include "set.h"
69 #include "thermal.h"
70 #include "undo.h"
71 #include "rtree.h"
72 #include "macro.h"
74 #include <assert.h>
76 #ifdef HAVE_LIBDMALLOC
77 #include <dmalloc.h>
78 #endif
80 /* for fork() and friends */
81 #ifdef HAVE_UNISTD_H
82 #include <unistd.h>
83 #endif
85 #ifdef HAVE_SYS_WAIT_H
86 #include <sys/wait.h>
87 #endif
89 /* ---------------------------------------------------------------------------
90 * some local types
92 typedef enum
94 F_AddSelected,
95 F_All,
96 F_AllConnections,
97 F_AllRats,
98 F_AllUnusedPins,
99 F_Arc,
100 F_Arrow,
101 F_Block,
102 F_Description,
103 F_Cancel,
104 F_Center,
105 F_Clear,
106 F_ClearAndRedraw,
107 F_ClearList,
108 F_Close,
109 F_Connection,
110 F_Convert,
111 F_Copy,
112 F_CycleClip,
113 F_CycleCrosshair,
114 F_DeleteRats,
115 F_Drag,
116 F_DrillReport,
117 F_Element,
118 F_ElementByName,
119 F_ElementConnections,
120 F_ElementToBuffer,
121 F_Escape,
122 F_Find,
123 F_FlipElement,
124 F_FoundPins,
125 F_Grid,
126 F_InsertPoint,
127 F_Layer,
128 F_Layout,
129 F_LayoutAs,
130 F_LayoutToBuffer,
131 F_Line,
132 F_LineSize,
133 F_Lock,
134 F_Mirror,
135 F_Move,
136 F_NameOnPCB,
137 F_Netlist,
138 F_None,
139 F_Notify,
140 F_Object,
141 F_ObjectByName,
142 F_PasteBuffer,
143 F_PadByName,
144 F_PinByName,
145 F_PinOrPadName,
146 F_Pinout,
147 F_Polygon,
148 F_PreviousPoint,
149 F_RatsNest,
150 F_Rectangle,
151 F_Redraw,
152 F_Release,
153 F_Revert,
154 F_Remove,
155 F_RemoveSelected,
156 F_Report,
157 F_Reset,
158 F_ResetLinesAndPolygons,
159 F_ResetPinsViasAndPads,
160 F_Restore,
161 F_Rotate,
162 F_Save,
163 F_Scroll,
164 F_Selected,
165 F_SelectedArcs,
166 F_SelectedElements,
167 F_SelectedLines,
168 F_SelectedNames,
169 F_SelectedObjects,
170 F_SelectedPads,
171 F_SelectedPins,
172 F_SelectedTexts,
173 F_SelectedVias,
174 F_SelectedRats,
175 F_Stroke,
176 F_Text,
177 F_TextByName,
178 F_TextScale,
179 F_Thermal,
180 F_ToLayout,
181 F_ToggleAllDirections,
182 F_ToggleAutoDRC,
183 F_ToggleClearLine,
184 F_ToggleFullPoly,
185 F_ToggleGrid,
186 F_ToggleHideNames,
187 F_ToggleMask,
188 F_ToggleName,
189 F_ToggleObject,
190 F_ToggleShowDRC,
191 F_ToggleLiveRoute,
192 F_ToggleRubberBandMode,
193 F_ToggleStartDirection,
194 F_ToggleSnapPin,
195 F_ToggleThindraw,
196 F_ToggleLockNames,
197 F_ToggleOnlyNames,
198 F_ToggleThindrawPoly,
199 F_ToggleOrthoMove,
200 F_ToggleLocalRef,
201 F_ToggleCheckPlanes,
202 F_ToggleUniqueNames,
203 F_Via,
204 F_ViaByName,
205 F_Value,
206 F_ViaDrillingHole,
207 F_ViaSize,
208 F_Zoom
210 FunctionID;
212 typedef struct /* used to identify subfunctions */
214 char *Identifier;
215 FunctionID ID;
217 FunctionType, *FunctionTypePtr;
219 /* --------------------------------------------------------------------------- */
221 /* %start-doc actions 00delta
223 Many actions take a @code{delta} parameter as the last parameter,
224 which is an amount to change something. That @code{delta} may include
225 units, as an additional parameter, such as @code{Action(Object,5,mm)}.
226 If no units are specified, the default is PCB's native units
227 (currently 1/100 mil). Also, if the delta is prefixed by @code{+} or
228 @code{-}, the size is increased or decreased by that amount.
229 Otherwise, the size size is set to the given amount.
231 @example
232 Action(Object,5,mil)
233 Action(Object,+0.5,mm)
234 Action(Object,-1)
235 @end example
237 Actions which take a @code{delta} parameter which do not accept all
238 these options will specify what they do take.
240 %end-doc */
242 /* %start-doc actions 00objects
244 Many actions act on indicated objects on the board. They will have
245 parameters like @code{ToggleObject} or @code{SelectedVias} to indicate
246 what group of objects they act on. Unless otherwise specified, these
247 parameters are defined as follows:
249 @table @code
251 @item Object
252 @itemx ToggleObject
253 Affects the object under the mouse pointer. If this action is invoked
254 from a menu or script, the user will be prompted to click on an
255 object, which is then the object affected.
257 @item Selected
258 @itemx SelectedObjects
260 Affects all objects which are currently selected. At least, all
261 selected objects for which the given action makes sense.
263 @item SelectedPins
264 @itemx SelectedVias
265 @itemx Selected@var{Type}
266 @itemx @i{etc}
267 Affects all objects which are both selected and of the @var{Type} specified.
269 @end table
271 %end-doc */
273 /* %start-doc actions 00macros
275 @macro pinshapes
277 Pins, pads, and vias can have various shapes. All may be round. Pins
278 and pads may be square (obviously "square" pads are usually
279 rectangular). Pins and vias may be octagonal. When you change a
280 shape flag of an element, you actually change all of its pins and
281 pads.
283 Note that the square flag takes precedence over the octagon flag,
284 thus, if both the square and octagon flags are set, the object is
285 square. When the square flag is cleared, the pins and pads will be
286 either round or, if the octagon flag is set, octagonal.
288 @end macro
290 %end-doc */
292 /* ---------------------------------------------------------------------------
293 * some local identifiers
295 static PointType InsertedPoint;
296 static LayerTypePtr lastLayer;
297 static struct
299 PolygonTypePtr poly;
300 LineType line;
302 fake;
304 static struct
306 int X;
307 int Y;
308 Cardinal Buffer;
309 Boolean Click;
310 Boolean Moving; /* selected type clicked on */
311 int Hit; /* move type clicked on */
312 void *ptr1;
313 void *ptr2;
314 void *ptr3;
316 Note;
318 static int defer_updates = 0;
319 static int defer_needs_update = 0;
321 static Cardinal polyIndex = 0;
322 static Boolean IgnoreMotionEvents = False;
323 static Boolean saved_mode = False;
324 #ifdef HAVE_LIBSTROKE
325 static Boolean mid_stroke = False;
326 static BoxType StrokeBox;
327 #endif
328 static FunctionType Functions[] = {
329 {"AddSelected", F_AddSelected},
330 {"All", F_All},
331 {"AllConnections", F_AllConnections},
332 {"AllRats", F_AllRats},
333 {"AllUnusedPins", F_AllUnusedPins},
334 {"Arc", F_Arc},
335 {"Arrow", F_Arrow},
336 {"Block", F_Block},
337 {"Description", F_Description},
338 {"Cancel", F_Cancel},
339 {"Center", F_Center},
340 {"Clear", F_Clear},
341 {"ClearAndRedraw", F_ClearAndRedraw},
342 {"ClearList", F_ClearList},
343 {"Close", F_Close},
344 {"Connection", F_Connection},
345 {"Convert", F_Convert},
346 {"Copy", F_Copy},
347 {"CycleClip", F_CycleClip},
348 {"CycleCrosshair", F_CycleCrosshair},
349 {"DeleteRats", F_DeleteRats},
350 {"Drag", F_Drag},
351 {"DrillReport", F_DrillReport},
352 {"Element", F_Element},
353 {"ElementByName", F_ElementByName},
354 {"ElementConnections", F_ElementConnections},
355 {"ElementToBuffer", F_ElementToBuffer},
356 {"Escape", F_Escape},
357 {"Find", F_Find},
358 {"FlipElement", F_FlipElement},
359 {"FoundPins", F_FoundPins},
360 {"Grid", F_Grid},
361 {"InsertPoint", F_InsertPoint},
362 {"Layer", F_Layer},
363 {"Layout", F_Layout},
364 {"LayoutAs", F_LayoutAs},
365 {"LayoutToBuffer", F_LayoutToBuffer},
366 {"Line", F_Line},
367 {"LineSize", F_LineSize},
368 {"Lock", F_Lock},
369 {"Mirror", F_Mirror},
370 {"Move", F_Move},
371 {"NameOnPCB", F_NameOnPCB},
372 {"Netlist", F_Netlist},
373 {"None", F_None},
374 {"Notify", F_Notify},
375 {"Object", F_Object},
376 {"ObjectByName", F_ObjectByName},
377 {"PasteBuffer", F_PasteBuffer},
378 {"PadByName", F_PadByName},
379 {"PinByName", F_PinByName},
380 {"PinOrPadName", F_PinOrPadName},
381 {"Pinout", F_Pinout},
382 {"Polygon", F_Polygon},
383 {"PreviousPoint", F_PreviousPoint},
384 {"RatsNest", F_RatsNest},
385 {"Rectangle", F_Rectangle},
386 {"Redraw", F_Redraw},
387 {"Release", F_Release},
388 {"Remove", F_Remove},
389 {"RemoveSelected", F_RemoveSelected},
390 {"Report", F_Report},
391 {"Reset", F_Reset},
392 {"ResetLinesAndPolygons", F_ResetLinesAndPolygons},
393 {"ResetPinsViasAndPads", F_ResetPinsViasAndPads},
394 {"Restore", F_Restore},
395 {"Revert", F_Revert},
396 {"Rotate", F_Rotate},
397 {"Save", F_Save},
398 {"Scroll", F_Scroll},
399 {"Selected", F_Selected},
400 {"SelectedArcs", F_SelectedArcs},
401 {"SelectedElements", F_SelectedElements},
402 {"SelectedLines", F_SelectedLines},
403 {"SelectedNames", F_SelectedNames},
404 {"SelectedObjects", F_SelectedObjects},
405 {"SelectedPins", F_SelectedPins},
406 {"SelectedPads", F_SelectedPads},
407 {"SelectedRats", F_SelectedRats},
408 {"SelectedTexts", F_SelectedTexts},
409 {"SelectedVias", F_SelectedVias},
410 {"Stroke", F_Stroke},
411 {"Text", F_Text},
412 {"TextByName", F_TextByName},
413 {"TextScale", F_TextScale},
414 {"Thermal", F_Thermal},
415 {"ToLayout", F_ToLayout},
416 {"Toggle45Degree", F_ToggleAllDirections},
417 {"ToggleClearLine", F_ToggleClearLine},
418 {"ToggleFullPoly", F_ToggleFullPoly},
419 {"ToggleGrid", F_ToggleGrid},
420 {"ToggleMask", F_ToggleMask},
421 {"ToggleName", F_ToggleName},
422 {"ToggleObject", F_ToggleObject},
423 {"ToggleRubberBandMode", F_ToggleRubberBandMode},
424 {"ToggleStartDirection", F_ToggleStartDirection},
425 {"ToggleSnapPin", F_ToggleSnapPin},
426 {"ToggleThindraw", F_ToggleThindraw},
427 {"ToggleThindrawPoly", F_ToggleThindrawPoly},
428 {"ToggleLockNames", F_ToggleLockNames},
429 {"ToggleOnlyNames", F_ToggleOnlyNames},
430 {"ToggleHideNames", F_ToggleHideNames},
431 {"ToggleCheckPlanes", F_ToggleCheckPlanes},
432 {"ToggleLocalRef", F_ToggleLocalRef},
433 {"ToggleOrthoMove", F_ToggleOrthoMove},
434 {"ToggleShowDRC", F_ToggleShowDRC},
435 {"ToggleLiveRoute", F_ToggleLiveRoute},
436 {"ToggleAutoDRC", F_ToggleAutoDRC},
437 {"ToggleUniqueNames", F_ToggleUniqueNames},
438 {"Value", F_Value},
439 {"Via", F_Via},
440 {"ViaByName", F_ViaByName},
441 {"ViaSize", F_ViaSize},
442 {"ViaDrillingHole", F_ViaDrillingHole},
443 {"Zoom", F_Zoom}
446 /* ---------------------------------------------------------------------------
447 * some local routines
449 static int GetFunctionID (String);
450 static void AdjustAttachedBox (void);
451 static void NotifyLine (void);
452 static void NotifyBlock (void);
453 static void NotifyMode (void);
454 static void ClearWarnings (void);
455 #ifdef HAVE_LIBSTROKE
456 static void FinishStroke (void);
457 extern void stroke_init (void);
458 extern void stroke_record (int x, int y);
459 extern int stroke_trans (char *s);
460 #endif
461 static void ChangeFlag (char *, char *, int, char *);
463 #define ARG(n) (argc > (n) ? argv[n] : 0)
465 #ifdef HAVE_LIBSTROKE
467 /* ---------------------------------------------------------------------------
468 * FinishStroke - try to recognize the stroke sent
470 void
471 FinishStroke (void)
473 char msg[255];
474 int type;
475 unsigned long num;
476 void *ptr1, *ptr2, *ptr3;
478 mid_stroke = False;
479 if (stroke_trans (msg))
481 num = atoi (msg);
482 switch (num)
484 case 456:
485 if (Settings.Mode == LINE_MODE)
487 SetMode (LINE_MODE);
489 break;
490 case 9874123:
491 case 74123:
492 case 987412:
493 case 8741236:
494 case 874123:
495 RotateScreenObject (StrokeBox.X1, StrokeBox.Y1, SWAP_IDENT ? 1 : 3);
496 break;
497 case 7896321:
498 case 786321:
499 case 789632:
500 case 896321:
501 RotateScreenObject (StrokeBox.X1, StrokeBox.Y1, SWAP_IDENT ? 3 : 1);
502 break;
503 case 258:
504 SetMode (LINE_MODE);
505 break;
506 case 852:
507 SetMode (ARROW_MODE);
508 break;
509 case 1478963:
510 ActionUndo ("");
511 break;
512 case 147423:
513 case 147523:
514 case 1474123:
515 Redo (True);
516 break;
517 case 148963:
518 case 147863:
519 case 147853:
520 case 145863:
521 SetMode (VIA_MODE);
522 break;
523 case 951:
524 case 9651:
525 case 9521:
526 case 9621:
527 case 9851:
528 case 9541:
529 case 96521:
530 case 96541:
531 case 98541:
532 SetZoom (1000); /* special zoom extents */
533 break;
534 case 159:
535 case 1269:
536 case 1259:
537 case 1459:
538 case 1569:
539 case 1589:
540 case 12569:
541 case 12589:
542 case 14589:
544 LocationType x = (StrokeBox.X1 + StrokeBox.X2) / 2;
545 LocationType y = (StrokeBox.Y1 + StrokeBox.Y2) / 2;
546 int z;
549 log (fabs (StrokeBox.X2 - StrokeBox.X1) / Output.Width) /
550 log (2.0);
552 MAX (z,
554 log (fabs (StrokeBox.Y2 - StrokeBox.Y1) / Output.Height) /
555 log (2.0));
556 SetZoom (z);
558 CenterDisplay (x, y, False);
559 break;
562 default:
563 Message ("Unknown stroke %s\n", msg);
564 break;
567 else
568 gui->beep ();
570 #endif
572 /* ---------------------------------------------------------------------------
573 * Clear warning color from pins/pads
575 static void
576 ClearWarnings ()
578 Settings.RatWarn = False;
579 ALLPIN_LOOP (PCB->Data);
581 if (TEST_FLAG (WARNFLAG, pin))
583 CLEAR_FLAG (WARNFLAG, pin);
584 DrawPin (pin, 0);
587 ENDALL_LOOP;
588 ALLPAD_LOOP (PCB->Data);
590 if (TEST_FLAG (WARNFLAG, pad))
592 CLEAR_FLAG (WARNFLAG, pad);
593 DrawPad (pad, 0);
596 ENDALL_LOOP;
597 Draw ();
600 static void
601 click_cb (hidval hv)
603 if (Note.Click)
605 Note.Click = False;
606 if (Note.Moving && !gui->shift_is_pressed ())
608 HideCrosshair (True);
609 Note.Buffer = Settings.BufferNumber;
610 SetBufferNumber (MAX_BUFFER - 1);
611 ClearBuffer (PASTEBUFFER);
612 AddSelectedToBuffer (PASTEBUFFER, Note.X, Note.Y, True);
613 SaveUndoSerialNumber ();
614 RemoveSelected ();
615 SaveMode ();
616 saved_mode = True;
617 SetMode (PASTEBUFFER_MODE);
618 RestoreCrosshair (True);
620 else if (Note.Hit && !gui->shift_is_pressed ())
622 HideCrosshair (True);
623 SaveMode ();
624 saved_mode = True;
625 SetMode (gui->control_is_pressed ()? COPY_MODE : MOVE_MODE);
626 Crosshair.AttachedObject.Ptr1 = Note.ptr1;
627 Crosshair.AttachedObject.Ptr2 = Note.ptr2;
628 Crosshair.AttachedObject.Ptr3 = Note.ptr3;
629 Crosshair.AttachedObject.Type = Note.Hit;
630 AttachForCopy (Note.X, Note.Y);
631 RestoreCrosshair (True);
633 else
635 BoxType box;
637 Note.Hit = 0;
638 Note.Moving = False;
639 HideCrosshair (True);
640 SaveUndoSerialNumber ();
641 box.X1 = -MAX_COORD;
642 box.Y1 = -MAX_COORD;
643 box.X2 = MAX_COORD;
644 box.Y2 = MAX_COORD;
645 /* unselect first if shift key not down */
646 if (!gui->shift_is_pressed () && SelectBlock (&box, False))
647 SetChangedFlag (True);
648 NotifyBlock ();
649 Crosshair.AttachedBox.Point1.X = Note.X;
650 Crosshair.AttachedBox.Point1.Y = Note.Y;
651 RestoreCrosshair (True);
656 static void
657 ReleaseMode (void)
659 BoxType box;
661 if (Note.Click)
663 BoxType box;
665 box.X1 = -MAX_COORD;
666 box.Y1 = -MAX_COORD;
667 box.X2 = MAX_COORD;
668 box.Y2 = MAX_COORD;
670 Note.Click = False; /* inhibit timer action */
671 SaveUndoSerialNumber ();
672 /* unselect first if shift key not down */
673 if (!gui->shift_is_pressed ())
675 if (SelectBlock (&box, False))
676 SetChangedFlag (True);
677 if (Note.Moving)
679 Note.Moving = 0;
680 Note.Hit = 0;
681 return;
684 RestoreUndoSerialNumber ();
685 if (SelectObject ())
686 SetChangedFlag (True);
687 Note.Hit = 0;
688 Note.Moving = 0;
690 else if (Note.Moving)
692 RestoreUndoSerialNumber ();
693 NotifyMode ();
694 ClearBuffer (PASTEBUFFER);
695 SetBufferNumber (Note.Buffer);
696 Note.Moving = False;
697 Note.Hit = 0;
699 else if (Note.Hit)
701 NotifyMode ();
702 Note.Hit = 0;
704 else if (Settings.Mode == ARROW_MODE)
706 box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
707 Crosshair.AttachedBox.Point2.X);
708 box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
709 Crosshair.AttachedBox.Point2.Y);
710 box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
711 Crosshair.AttachedBox.Point2.X);
712 box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
713 Crosshair.AttachedBox.Point2.Y);
714 RestoreUndoSerialNumber ();
715 if (SelectBlock (&box, True))
716 SetChangedFlag (True);
717 else if (Bumped)
718 IncrementUndoSerialNumber ();
719 Crosshair.AttachedBox.State = STATE_FIRST;
721 if (saved_mode)
722 RestoreMode ();
723 saved_mode = False;
726 /* ---------------------------------------------------------------------------
727 * get function ID of passed string
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;
747 static int
748 GetFunctionID (String Ident)
750 int i, h;
752 if (Ident == 0)
753 return -1;
755 if (!hash_initted)
757 hash_initted = 1;
758 if (HSIZE < ENTRIES (Functions) * 2)
760 fprintf(stderr, "Error: function hash size too small (%d vs %lu at %s:%d)\n",
761 HSIZE, (unsigned long) ENTRIES (Functions)*2, __FILE__, __LINE__);
762 exit(1);
764 if (ENTRIES (Functions) > 254)
766 /* Change 'char' to 'int' and remove this when we get to 256
767 strings to hash. */
768 fprintf(stderr, "Error: function hash type too small (%d vs %lu at %s:%d)\n",
769 256, (unsigned long) ENTRIES (Functions), __FILE__, __LINE__);
770 exit(1);
773 for (i=ENTRIES (Functions)-1; i>=0; i--)
775 h = hashfunc (Functions[i].Identifier);
776 while (function_hash[h])
777 h = (h + 1) % HSIZE;
778 function_hash[h] = i + 1;
782 i = hashfunc (Ident);
783 while (1)
785 /* We enforce the "hash table bigger than function table" rule,
786 so we know there will be at least one zero entry to find. */
787 if (!function_hash[i])
788 return (-1);
789 if (!strcasecmp (Ident, Functions[function_hash[i]-1].Identifier))
790 return ((int) Functions[function_hash[i]-1].ID);
791 i = (i + 1) % HSIZE;
795 /* ---------------------------------------------------------------------------
796 * set new coordinates if in 'RECTANGLE' mode
797 * the cursor shape is also adjusted
799 static void
800 AdjustAttachedBox (void)
802 if (Settings.Mode == ARC_MODE)
804 Crosshair.AttachedBox.otherway = gui->shift_is_pressed ();
805 return;
807 switch (Crosshair.AttachedBox.State)
809 case STATE_SECOND: /* one corner is selected */
811 /* update coordinates */
812 Crosshair.AttachedBox.Point2.X = Crosshair.X;
813 Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
814 break;
819 /* ---------------------------------------------------------------------------
820 * adjusts the objects which are to be created like attached lines...
822 void
823 AdjustAttachedObjects (void)
825 PointTypePtr pnt;
826 switch (Settings.Mode)
828 /* update at least an attached block (selection) */
829 case NO_MODE:
830 case ARROW_MODE:
831 if (Crosshair.AttachedBox.State)
833 Crosshair.AttachedBox.Point2.X = Crosshair.X;
834 Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
836 break;
838 /* rectangle creation mode */
839 case RECTANGLE_MODE:
840 case ARC_MODE:
841 AdjustAttachedBox ();
842 break;
844 /* polygon creation mode */
845 case POLYGON_MODE:
846 AdjustAttachedLine ();
847 break;
848 /* line creation mode */
849 case LINE_MODE:
850 if (PCB->RatDraw || PCB->Clipping == 0)
851 AdjustAttachedLine ();
852 else
853 AdjustTwoLine (PCB->Clipping - 1);
854 break;
855 /* point insertion mode */
856 case INSERTPOINT_MODE:
857 pnt = AdjustInsertPoint ();
858 if (pnt)
859 InsertedPoint = *pnt;
860 break;
861 case ROTATE_MODE:
862 break;
866 /* ---------------------------------------------------------------------------
867 * creates points of a line
869 static void
870 NotifyLine (void)
872 int type = NO_TYPE;
873 void *ptr1, *ptr2, *ptr3;
875 if (!Marked.status || TEST_FLAG (LOCALREFFLAG, PCB))
876 SetLocalRef (Crosshair.X, Crosshair.Y, True);
877 switch (Crosshair.AttachedLine.State)
879 case STATE_FIRST: /* first point */
880 if (PCB->RatDraw && SearchScreen (Crosshair.X, Crosshair.Y,
881 PAD_TYPE | PIN_TYPE, &ptr1, &ptr1,
882 &ptr1) == NO_TYPE)
884 gui->beep ();
885 break;
887 if (TEST_FLAG (AUTODRCFLAG, PCB) && Settings.Mode == LINE_MODE)
889 type = SearchScreen (Crosshair.X, Crosshair.Y,
890 PIN_TYPE | PAD_TYPE | VIA_TYPE, &ptr1, &ptr2,
891 &ptr3);
892 LookupConnection (Crosshair.X, Crosshair.Y, True, TO_PCB (1),
893 FOUNDFLAG);
895 if (type == PIN_TYPE || type == VIA_TYPE)
897 Crosshair.AttachedLine.Point1.X =
898 Crosshair.AttachedLine.Point2.X = ((PinTypePtr) ptr2)->X;
899 Crosshair.AttachedLine.Point1.Y =
900 Crosshair.AttachedLine.Point2.Y = ((PinTypePtr) ptr2)->Y;
902 else if (type == PAD_TYPE)
904 PadTypePtr pad = (PadTypePtr) ptr2;
905 float d1, d2;
906 d1 = SQUARE (Crosshair.X - pad->Point1.X) +
907 SQUARE (Crosshair.Y - pad->Point1.Y);
908 d2 = SQUARE (Crosshair.X - pad->Point2.X) +
909 SQUARE (Crosshair.Y - pad->Point2.Y);
910 if (d2 < d1)
912 Crosshair.AttachedLine.Point1 =
913 Crosshair.AttachedLine.Point2 = pad->Point2;
915 else
917 Crosshair.AttachedLine.Point1 =
918 Crosshair.AttachedLine.Point2 = pad->Point1;
921 else
923 Crosshair.AttachedLine.Point1.X =
924 Crosshair.AttachedLine.Point2.X = Crosshair.X;
925 Crosshair.AttachedLine.Point1.Y =
926 Crosshair.AttachedLine.Point2.Y = Crosshair.Y;
928 Crosshair.AttachedLine.State = STATE_SECOND;
929 break;
931 case STATE_SECOND:
932 /* fall through to third state too */
933 lastLayer = CURRENT;
934 default: /* all following points */
935 Crosshair.AttachedLine.State = STATE_THIRD;
936 break;
940 /* ---------------------------------------------------------------------------
941 * create first or second corner of a marked block
943 static void
944 NotifyBlock (void)
946 HideCrosshair (True);
947 switch (Crosshair.AttachedBox.State)
949 case STATE_FIRST: /* setup first point */
950 Crosshair.AttachedBox.Point1.X =
951 Crosshair.AttachedBox.Point2.X = Crosshair.X;
952 Crosshair.AttachedBox.Point1.Y =
953 Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
954 Crosshair.AttachedBox.State = STATE_SECOND;
955 break;
957 case STATE_SECOND: /* setup second point */
958 Crosshair.AttachedBox.State = STATE_THIRD;
959 break;
961 RestoreCrosshair (True);
965 /* ---------------------------------------------------------------------------
967 * does what's appropriate for the current mode setting. This normally
968 * means creation of an object at the current crosshair location.
970 * new created objects are added to the create undo list of course
972 static void
973 NotifyMode (void)
975 void *ptr1, *ptr2, *ptr3;
976 int type;
978 if (Settings.RatWarn)
979 ClearWarnings ();
980 switch (Settings.Mode)
982 case ARROW_MODE:
984 int test;
985 hidval hv;
987 Note.Click = True;
988 /* do something after click time */
989 gui->add_timer (click_cb, CLICK_TIME, hv);
991 /* see if we clicked on something already selected
992 * (Note.Moving) or clicked on a MOVE_TYPE
993 * (Note.Hit)
995 for (test = (SELECT_TYPES | MOVE_TYPES) & ~RATLINE_TYPE;
996 test; test &= ~type)
998 type = SearchScreen (Note.X, Note.Y, test, &ptr1, &ptr2, &ptr3);
999 if (!Note.Hit && (type & MOVE_TYPES) &&
1000 !TEST_FLAG (LOCKFLAG, (PinTypePtr) ptr2))
1002 Note.Hit = type;
1003 Note.ptr1 = ptr1;
1004 Note.ptr2 = ptr2;
1005 Note.ptr3 = ptr3;
1007 if (!Note.Moving && (type & SELECT_TYPES) &&
1008 TEST_FLAG (SELECTEDFLAG, (PinTypePtr) ptr2))
1009 Note.Moving = True;
1010 if ((Note.Hit && Note.Moving) || type == NO_TYPE)
1011 break;
1013 break;
1016 case VIA_MODE:
1018 PinTypePtr via;
1020 if (!PCB->ViaOn)
1022 Message (_("You must turn via visibility on before\n"
1023 "you can place vias\n"));
1024 break;
1026 if ((via = CreateNewVia (PCB->Data, Note.X, Note.Y,
1027 Settings.ViaThickness, 2 * Settings.Keepaway,
1028 0, Settings.ViaDrillingHole, NULL,
1029 NoFlags ())) != NULL)
1031 AddObjectToCreateUndoList (VIA_TYPE, via, via, via);
1032 if (gui->shift_is_pressed ())
1033 ChangeObjectThermal (VIA_TYPE, via, via, via, PCB->ThermStyle);
1034 IncrementUndoSerialNumber ();
1035 DrawVia (via, 0);
1036 Draw ();
1038 break;
1041 case ARC_MODE:
1043 switch (Crosshair.AttachedBox.State)
1045 case STATE_FIRST:
1046 Crosshair.AttachedBox.Point1.X =
1047 Crosshair.AttachedBox.Point2.X = Note.X;
1048 Crosshair.AttachedBox.Point1.Y =
1049 Crosshair.AttachedBox.Point2.Y = Note.Y;
1050 Crosshair.AttachedBox.State = STATE_SECOND;
1051 break;
1053 case STATE_SECOND:
1054 case STATE_THIRD:
1056 ArcTypePtr arc;
1057 LocationType wx, wy;
1058 int sa, dir;
1060 wx = Note.X - Crosshair.AttachedBox.Point1.X;
1061 wy = Note.Y - Crosshair.AttachedBox.Point1.Y;
1062 if (XOR (Crosshair.AttachedBox.otherway, abs (wy) > abs (wx)))
1064 Crosshair.AttachedBox.Point2.X =
1065 Crosshair.AttachedBox.Point1.X + abs (wy) * SGNZ (wx);
1066 sa = (wx >= 0) ? 0 : 180;
1067 #ifdef ARC45
1068 if (abs (wy) / 2 >= abs (wx))
1069 dir = (SGNZ (wx) == SGNZ (wy)) ? 45 : -45;
1070 else
1071 #endif
1072 dir = (SGNZ (wx) == SGNZ (wy)) ? 90 : -90;
1074 else
1076 Crosshair.AttachedBox.Point2.Y =
1077 Crosshair.AttachedBox.Point1.Y + abs (wx) * SGNZ (wy);
1078 sa = (wy >= 0) ? -90 : 90;
1079 #ifdef ARC45
1080 if (abs (wx) / 2 >= abs (wy))
1081 dir = (SGNZ (wx) == SGNZ (wy)) ? -45 : 45;
1082 else
1083 #endif
1084 dir = (SGNZ (wx) == SGNZ (wy)) ? -90 : 90;
1085 wy = wx;
1087 if (abs (wy) > 0 && (arc = CreateNewArcOnLayer (CURRENT,
1088 Crosshair.
1089 AttachedBox.
1090 Point2.X,
1091 Crosshair.
1092 AttachedBox.
1093 Point2.Y,
1094 abs (wy),
1095 abs (wy),
1097 dir,
1098 Settings.
1099 LineThickness,
1100 2 * Settings.
1101 Keepaway,
1102 MakeFlags
1103 (TEST_FLAG
1104 (CLEARNEWFLAG,
1105 PCB) ?
1106 CLEARLINEFLAG :
1107 0))))
1109 BoxTypePtr bx;
1111 bx = GetArcEnds (arc);
1112 Crosshair.AttachedBox.Point1.X =
1113 Crosshair.AttachedBox.Point2.X = bx->X2;
1114 Crosshair.AttachedBox.Point1.Y =
1115 Crosshair.AttachedBox.Point2.Y = bx->Y2;
1116 AddObjectToCreateUndoList (ARC_TYPE, CURRENT, arc, arc);
1117 IncrementUndoSerialNumber ();
1118 addedLines++;
1119 DrawArc (CURRENT, arc, 0);
1120 Draw ();
1121 Crosshair.AttachedBox.State = STATE_THIRD;
1123 break;
1126 break;
1128 case LOCK_MODE:
1130 type = SearchScreen (Note.X, Note.Y, LOCK_TYPES, &ptr1, &ptr2, &ptr3);
1131 if (type == ELEMENT_TYPE)
1133 ElementTypePtr element = (ElementTypePtr) ptr2;
1135 TOGGLE_FLAG (LOCKFLAG, element);
1136 PIN_LOOP (element);
1138 TOGGLE_FLAG (LOCKFLAG, pin);
1139 CLEAR_FLAG (SELECTEDFLAG, pin);
1141 END_LOOP;
1142 PAD_LOOP (element);
1144 TOGGLE_FLAG (LOCKFLAG, pad);
1145 CLEAR_FLAG (SELECTEDFLAG, pad);
1147 END_LOOP;
1148 CLEAR_FLAG (SELECTEDFLAG, element);
1149 /* always re-draw it since I'm too lazy
1150 * to tell if a selected flag changed
1152 DrawElement (element, 0);
1153 Draw ();
1154 hid_actionl ("Report", "Object", NULL);
1156 else if (type != NO_TYPE)
1158 TextTypePtr thing = (TextTypePtr) ptr3;
1159 TOGGLE_FLAG (LOCKFLAG, thing);
1160 if (TEST_FLAG (LOCKFLAG, thing)
1161 && TEST_FLAG (SELECTEDFLAG, thing))
1163 /* this is not un-doable since LOCK isn't */
1164 CLEAR_FLAG (SELECTEDFLAG, thing);
1165 DrawObject (type, ptr1, ptr2, 0);
1166 Draw ();
1168 hid_actionl ("Report", "Object", NULL);
1170 break;
1172 case THERMAL_MODE:
1174 if (((type
1176 SearchScreen (Note.X, Note.Y, PIN_TYPES, &ptr1, &ptr2,
1177 &ptr3)) != NO_TYPE)
1178 && !TEST_FLAG (HOLEFLAG, (PinTypePtr) ptr3))
1180 if (gui->shift_is_pressed ())
1182 int tstyle = GET_THERM (INDEXOFCURRENT, (PinTypePtr) ptr3);
1183 tstyle++;
1184 if (tstyle > 5)
1185 tstyle = 1;
1186 ChangeObjectThermal (type, ptr1, ptr2, ptr3, tstyle);
1188 else if (GET_THERM (INDEXOFCURRENT, (PinTypePtr) ptr3))
1189 ChangeObjectThermal (type, ptr1, ptr2, ptr3, 0);
1190 else
1191 ChangeObjectThermal (type, ptr1, ptr2, ptr3, PCB->ThermStyle);
1193 break;
1196 case LINE_MODE:
1197 /* do update of position */
1198 NotifyLine ();
1199 if (Crosshair.AttachedLine.State != STATE_THIRD)
1200 break;
1202 /* Remove anchor if clicking on start point;
1203 * this means we can't paint 0 length lines
1204 * which could be used for square SMD pads.
1205 * Instead use a very small delta, or change
1206 * the file after saving.
1208 if (Crosshair.X == Crosshair.AttachedLine.Point1.X
1209 && Crosshair.Y == Crosshair.AttachedLine.Point1.Y)
1211 SetMode (LINE_MODE);
1212 break;
1215 if (PCB->RatDraw)
1217 RatTypePtr line;
1218 if ((line = AddNet ()))
1220 addedLines++;
1221 AddObjectToCreateUndoList (RATLINE_TYPE, line, line, line);
1222 IncrementUndoSerialNumber ();
1223 DrawRat (line, 0);
1224 Crosshair.AttachedLine.Point1.X =
1225 Crosshair.AttachedLine.Point2.X;
1226 Crosshair.AttachedLine.Point1.Y =
1227 Crosshair.AttachedLine.Point2.Y;
1228 Draw ();
1230 break;
1232 else
1233 /* create line if both ends are determined && length != 0 */
1235 LineTypePtr line;
1237 if (PCB->Clipping
1238 && Crosshair.AttachedLine.Point1.X ==
1239 Crosshair.AttachedLine.Point2.X
1240 && Crosshair.AttachedLine.Point1.Y ==
1241 Crosshair.AttachedLine.Point2.Y
1242 && (Crosshair.AttachedLine.Point2.X != Note.X
1243 || Crosshair.AttachedLine.Point2.Y != Note.Y))
1245 /* We will only need to paint the second line segment.
1246 Since we only check for vias on the first segment,
1247 swap them so the non-empty segment is the first segment. */
1248 Crosshair.AttachedLine.Point2.X = Note.X;
1249 Crosshair.AttachedLine.Point2.Y = Note.Y;
1252 if ((Crosshair.AttachedLine.Point1.X !=
1253 Crosshair.AttachedLine.Point2.X
1254 || Crosshair.AttachedLine.Point1.Y !=
1255 Crosshair.AttachedLine.Point2.Y)
1256 && (line =
1257 CreateDrawnLineOnLayer (CURRENT,
1258 Crosshair.AttachedLine.Point1.X,
1259 Crosshair.AttachedLine.Point1.Y,
1260 Crosshair.AttachedLine.Point2.X,
1261 Crosshair.AttachedLine.Point2.Y,
1262 Settings.LineThickness,
1263 2 * Settings.Keepaway,
1264 MakeFlags ((TEST_FLAG
1265 (AUTODRCFLAG,
1266 PCB) ? FOUNDFLAG : 0) |
1267 (TEST_FLAG
1268 (CLEARNEWFLAG,
1269 PCB) ? CLEARLINEFLAG :
1270 0)))) != NULL)
1272 PinTypePtr via;
1274 addedLines++;
1275 AddObjectToCreateUndoList (LINE_TYPE, CURRENT, line, line);
1276 DrawLine (CURRENT, line, 0);
1277 /* place a via if vias are visible, the layer is
1278 in a new group since the last line and there
1279 isn't a pin already here */
1280 if (PCB->ViaOn && GetLayerGroupNumberByPointer (CURRENT) !=
1281 GetLayerGroupNumberByPointer (lastLayer) &&
1282 SearchObjectByLocation (PIN_TYPES, &ptr1, &ptr2, &ptr3,
1283 Crosshair.AttachedLine.Point1.X,
1284 Crosshair.AttachedLine.Point1.Y,
1285 Settings.ViaThickness / 2) ==
1286 NO_TYPE
1287 && (via =
1288 CreateNewVia (PCB->Data,
1289 Crosshair.AttachedLine.Point1.X,
1290 Crosshair.AttachedLine.Point1.Y,
1291 Settings.ViaThickness,
1292 2 * Settings.Keepaway, 0,
1293 Settings.ViaDrillingHole, NULL,
1294 NoFlags ())) != NULL)
1296 AddObjectToCreateUndoList (VIA_TYPE, via, via, via);
1297 DrawVia (via, 0);
1299 /* copy the coordinates */
1300 Crosshair.AttachedLine.Point1.X =
1301 Crosshair.AttachedLine.Point2.X;
1302 Crosshair.AttachedLine.Point1.Y =
1303 Crosshair.AttachedLine.Point2.Y;
1304 IncrementUndoSerialNumber ();
1305 lastLayer = CURRENT;
1307 if (PCB->Clipping && (Note.X != Crosshair.AttachedLine.Point2.X
1308 || Note.Y !=
1309 Crosshair.AttachedLine.Point2.Y)
1310 && (line =
1311 CreateDrawnLineOnLayer (CURRENT,
1312 Crosshair.AttachedLine.Point2.X,
1313 Crosshair.AttachedLine.Point2.Y,
1314 Note.X, Note.Y,
1315 Settings.LineThickness,
1316 2 * Settings.Keepaway,
1317 MakeFlags ((TEST_FLAG
1318 (AUTODRCFLAG,
1319 PCB) ? FOUNDFLAG : 0) |
1320 (TEST_FLAG
1321 (CLEARNEWFLAG,
1322 PCB) ? CLEARLINEFLAG :
1323 0)))) != NULL)
1325 addedLines++;
1326 AddObjectToCreateUndoList (LINE_TYPE, CURRENT, line, line);
1327 IncrementUndoSerialNumber ();
1328 DrawLine (CURRENT, line, 0);
1329 /* move to new start point */
1330 Crosshair.AttachedLine.Point1.X = Note.X;
1331 Crosshair.AttachedLine.Point1.Y = Note.Y;
1332 Crosshair.AttachedLine.Point2.X = Note.X;
1333 Crosshair.AttachedLine.Point2.Y = Note.Y;
1334 if (TEST_FLAG (SWAPSTARTDIRFLAG, PCB))
1336 PCB->Clipping ^= 3;
1339 Draw ();
1341 break;
1343 case RECTANGLE_MODE:
1344 /* do update of position */
1345 NotifyBlock ();
1347 /* create rectangle if both corners are determined
1348 * and width, height are != 0
1350 if (Crosshair.AttachedBox.State == STATE_THIRD &&
1351 Crosshair.AttachedBox.Point1.X != Crosshair.AttachedBox.Point2.X &&
1352 Crosshair.AttachedBox.Point1.Y != Crosshair.AttachedBox.Point2.Y)
1354 PolygonTypePtr polygon;
1356 int flags = CLEARPOLYFLAG;
1357 if (TEST_FLAG (NEWFULLPOLYFLAG, PCB))
1358 flags |= FULLPOLYFLAG;
1359 if ((polygon = CreateNewPolygonFromRectangle (CURRENT,
1360 Crosshair.
1361 AttachedBox.Point1.X,
1362 Crosshair.
1363 AttachedBox.Point1.Y,
1364 Crosshair.
1365 AttachedBox.Point2.X,
1366 Crosshair.
1367 AttachedBox.Point2.Y,
1368 MakeFlags
1369 (flags))) !=
1370 NULL)
1372 AddObjectToCreateUndoList (POLYGON_TYPE, CURRENT,
1373 polygon, polygon);
1374 IncrementUndoSerialNumber ();
1375 DrawPolygon (CURRENT, polygon, 0);
1376 Draw ();
1379 /* reset state to 'first corner' */
1380 Crosshair.AttachedBox.State = STATE_FIRST;
1382 break;
1384 case TEXT_MODE:
1386 char *string;
1388 if ((string = gui->prompt_for (_("Enter text:"), "")) != NULL)
1390 if (strlen(string) > 0)
1392 TextTypePtr text;
1393 int flag = NOFLAG;
1395 if (GetLayerGroupNumberByNumber (INDEXOFCURRENT) ==
1396 GetLayerGroupNumberByNumber (max_layer + SOLDER_LAYER))
1397 flag = ONSOLDERFLAG;
1398 if ((text = CreateNewText (CURRENT, &PCB->Font, Note.X,
1399 Note.Y, 0, Settings.TextScale,
1400 string, MakeFlags (flag))) != NULL)
1402 AddObjectToCreateUndoList (TEXT_TYPE, CURRENT, text, text);
1403 IncrementUndoSerialNumber ();
1404 DrawText (CURRENT, text, 0);
1405 Draw ();
1409 /* free memory allocated by gui->prompt_for() */
1410 free (string);
1412 break;
1415 case POLYGON_MODE:
1417 PointTypePtr points = Crosshair.AttachedPolygon.Points;
1418 Cardinal n = Crosshair.AttachedPolygon.PointN;
1420 /* do update of position; use the 'LINE_MODE' mechanism */
1421 NotifyLine ();
1423 /* check if this is the last point of a polygon */
1424 if (n >= 3 &&
1425 points->X == Crosshair.AttachedLine.Point2.X &&
1426 points->Y == Crosshair.AttachedLine.Point2.Y)
1428 CopyAttachedPolygonToLayer ();
1429 Draw ();
1430 break;
1433 /* create new point if it's the first one or if it's
1434 * different to the last one
1436 if (!n ||
1437 points[n - 1].X != Crosshair.AttachedLine.Point2.X ||
1438 points[n - 1].Y != Crosshair.AttachedLine.Point2.Y)
1440 CreateNewPointInPolygon (&Crosshair.AttachedPolygon,
1441 Crosshair.AttachedLine.Point2.X,
1442 Crosshair.AttachedLine.Point2.Y);
1444 /* copy the coordinates */
1445 Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X;
1446 Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y;
1448 break;
1451 case PASTEBUFFER_MODE:
1453 TextType estr[MAX_ELEMENTNAMES];
1454 ElementTypePtr e = 0;
1456 if (gui->shift_is_pressed ())
1458 int type =
1459 SearchScreen (Note.X, Note.Y, ELEMENT_TYPE, &ptr1, &ptr2,
1460 &ptr3);
1461 if (type == ELEMENT_TYPE)
1463 e = (ElementTypePtr) ptr1;
1464 if (e)
1466 int i;
1468 memcpy (estr, e->Name,
1469 MAX_ELEMENTNAMES * sizeof (TextType));
1470 for (i = 0; i < MAX_ELEMENTNAMES; ++i)
1471 estr[i].TextString = estr[i].TextString ? strdup(estr[i].TextString) : NULL;
1472 RemoveElement (e);
1476 if (CopyPastebufferToLayout (Note.X, Note.Y))
1477 SetChangedFlag (True);
1478 if (e)
1480 int type =
1481 SearchScreen (Note.X, Note.Y, ELEMENT_TYPE, &ptr1, &ptr2,
1482 &ptr3);
1483 if (type == ELEMENT_TYPE && ptr1)
1485 int i, save_n;
1486 e = (ElementTypePtr) ptr1;
1488 save_n = NAME_INDEX (PCB);
1490 for (i = 0; i < MAX_ELEMENTNAMES; i++)
1492 if (i == save_n)
1493 EraseElementName (e);
1494 r_delete_entry (PCB->Data->name_tree[i],
1495 (BoxType *) & (e->Name[i]));
1496 memcpy (&(e->Name[i]), &(estr[i]), sizeof (TextType));
1497 e->Name[i].Element = e;
1498 SetTextBoundingBox (&PCB->Font, &(e->Name[i]));
1499 r_insert_entry (PCB->Data->name_tree[i],
1500 (BoxType *) & (e->Name[i]), 0);
1501 if (i == save_n)
1502 DrawElementName (e, 0);
1506 break;
1509 case REMOVE_MODE:
1510 if ((type =
1511 SearchScreen (Note.X, Note.Y, REMOVE_TYPES, &ptr1, &ptr2,
1512 &ptr3)) != NO_TYPE)
1514 if (TEST_FLAG (LOCKFLAG, (LineTypePtr) ptr2))
1516 Message (_("Sorry, the object is locked\n"));
1517 break;
1519 if (type == ELEMENT_TYPE)
1521 RubberbandTypePtr ptr;
1522 int i;
1524 Crosshair.AttachedObject.RubberbandN = 0;
1525 LookupRatLines (type, ptr1, ptr2, ptr3);
1526 ptr = Crosshair.AttachedObject.Rubberband;
1527 for (i = 0; i < Crosshair.AttachedObject.RubberbandN; i++)
1529 if (PCB->RatOn)
1530 EraseRat ((RatTypePtr) ptr->Line);
1531 if (TEST_FLAG (RUBBERENDFLAG, ptr->Line))
1532 MoveObjectToRemoveUndoList (RATLINE_TYPE,
1533 ptr->Line, ptr->Line,
1534 ptr->Line);
1535 else
1536 TOGGLE_FLAG (RUBBERENDFLAG, ptr->Line); /* only remove line once */
1537 ptr++;
1540 RemoveObject (type, ptr1, ptr2, ptr3);
1541 IncrementUndoSerialNumber ();
1542 SetChangedFlag (True);
1544 break;
1546 case ROTATE_MODE:
1547 RotateScreenObject (Note.X, Note.Y,
1548 gui->shift_is_pressed ()? (SWAP_IDENT ?
1549 1 : 3)
1550 : (SWAP_IDENT ? 3 : 1));
1551 break;
1553 /* both are almost the same */
1554 case COPY_MODE:
1555 case MOVE_MODE:
1556 switch (Crosshair.AttachedObject.State)
1558 /* first notify, lookup object */
1559 case STATE_FIRST:
1561 int types = (Settings.Mode == COPY_MODE) ?
1562 COPY_TYPES : MOVE_TYPES;
1564 Crosshair.AttachedObject.Type =
1565 SearchScreen (Note.X, Note.Y, types,
1566 &Crosshair.AttachedObject.Ptr1,
1567 &Crosshair.AttachedObject.Ptr2,
1568 &Crosshair.AttachedObject.Ptr3);
1569 if (Crosshair.AttachedObject.Type != NO_TYPE)
1571 if (Settings.Mode == MOVE_MODE &&
1572 TEST_FLAG (LOCKFLAG, (PinTypePtr)
1573 Crosshair.AttachedObject.Ptr2))
1575 Message (_("Sorry, the object is locked\n"));
1576 Crosshair.AttachedObject.Type = NO_TYPE;
1578 else
1579 AttachForCopy (Note.X, Note.Y);
1581 break;
1584 /* second notify, move or copy object */
1585 case STATE_SECOND:
1586 if (Settings.Mode == COPY_MODE)
1587 CopyObject (Crosshair.AttachedObject.Type,
1588 Crosshair.AttachedObject.Ptr1,
1589 Crosshair.AttachedObject.Ptr2,
1590 Crosshair.AttachedObject.Ptr3,
1591 Note.X - Crosshair.AttachedObject.X,
1592 Note.Y - Crosshair.AttachedObject.Y);
1593 else
1595 MoveObjectAndRubberband (Crosshair.AttachedObject.Type,
1596 Crosshair.AttachedObject.Ptr1,
1597 Crosshair.AttachedObject.Ptr2,
1598 Crosshair.AttachedObject.Ptr3,
1599 Note.X - Crosshair.AttachedObject.X,
1600 Note.Y - Crosshair.AttachedObject.Y);
1601 SetLocalRef (0, 0, False);
1603 SetChangedFlag (True);
1605 /* reset identifiers */
1606 Crosshair.AttachedObject.Type = NO_TYPE;
1607 Crosshair.AttachedObject.State = STATE_FIRST;
1608 break;
1610 break;
1612 /* insert a point into a polygon/line/... */
1613 case INSERTPOINT_MODE:
1614 switch (Crosshair.AttachedObject.State)
1616 /* first notify, lookup object */
1617 case STATE_FIRST:
1618 Crosshair.AttachedObject.Type =
1619 SearchScreen (Note.X, Note.Y, INSERT_TYPES,
1620 &Crosshair.AttachedObject.Ptr1,
1621 &Crosshair.AttachedObject.Ptr2,
1622 &Crosshair.AttachedObject.Ptr3);
1624 if (Crosshair.AttachedObject.Type != NO_TYPE)
1626 if (TEST_FLAG (LOCKFLAG, (PolygonTypePtr)
1627 Crosshair.AttachedObject.Ptr2))
1629 Message (_("Sorry, the object is locked\n"));
1630 Crosshair.AttachedObject.Type = NO_TYPE;
1631 break;
1633 else
1635 /* get starting point of nearest segment */
1636 if (Crosshair.AttachedObject.Type == POLYGON_TYPE)
1638 fake.poly =
1639 (PolygonTypePtr) Crosshair.AttachedObject.Ptr2;
1640 polyIndex =
1641 GetLowestDistancePolygonPoint (fake.poly, Note.X,
1642 Note.Y);
1643 fake.line.Point1 = fake.poly->Points[polyIndex];
1644 fake.line.Point2 = (polyIndex) ?
1645 fake.poly->Points[polyIndex - 1]
1646 : fake.poly->Points[fake.poly->PointN - 1];
1647 Crosshair.AttachedObject.Ptr2 = &fake.line;
1650 Crosshair.AttachedObject.State = STATE_SECOND;
1651 InsertedPoint = *AdjustInsertPoint ();
1654 break;
1656 /* second notify, insert new point into object */
1657 case STATE_SECOND:
1658 if (Crosshair.AttachedObject.Type == POLYGON_TYPE)
1659 InsertPointIntoObject (POLYGON_TYPE,
1660 Crosshair.AttachedObject.Ptr1, fake.poly,
1661 &polyIndex,
1662 InsertedPoint.X, InsertedPoint.Y, False);
1663 else
1664 InsertPointIntoObject (Crosshair.AttachedObject.Type,
1665 Crosshair.AttachedObject.Ptr1,
1666 Crosshair.AttachedObject.Ptr2,
1667 &polyIndex,
1668 InsertedPoint.X, InsertedPoint.Y, False);
1669 SetChangedFlag (True);
1671 /* reset identifiers */
1672 Crosshair.AttachedObject.Type = NO_TYPE;
1673 Crosshair.AttachedObject.State = STATE_FIRST;
1674 break;
1676 break;
1681 /* --------------------------------------------------------------------------- */
1683 static const char atomic_syntax[] = "Atomic(Save|Restore|Close|Block)";
1685 static const char atomic_help[] = "Save or restore the undo serial number.";
1687 /* %start-doc actions Atomic
1689 This action allows making multiple-action bindings into an atomic
1690 operation that will be undone by a single Undo command. For example,
1691 to optimize rat lines, you'd delete the rats and re-add them. To
1692 group these into a single undo, you'd want the deletions and the
1693 additions to have the same undo serial number. So, you @code{Save},
1694 delete the rats, @code{Restore}, add the rats - using the same serial
1695 number as the deletes, then @code{Block}, which checks to see if the
1696 deletions or additions actually did anything. If not, the serial
1697 number is set to the saved number, as there's nothing to undo. If
1698 something did happen, the serial number is incremented so that these
1699 actions are counted as a single undo step.
1701 @table @code
1703 @item Save
1704 Saves the undo serial number.
1706 @item Restore
1707 Returns it to the last saved number.
1709 @item Close
1710 Sets it to 1 greater than the last save.
1712 @item Block
1713 Does a Restore if there was nothing to undo, else does a Close.
1715 @end table
1717 %end-doc */
1719 static int
1720 ActionAtomic (int argc, char **argv, int x, int y)
1722 if (argc != 1)
1723 AFAIL (atomic);
1725 switch (GetFunctionID (argv[0]))
1727 case F_Save:
1728 SaveUndoSerialNumber ();
1729 break;
1730 case F_Restore:
1731 RestoreUndoSerialNumber ();
1732 break;
1733 case F_Close:
1734 RestoreUndoSerialNumber ();
1735 IncrementUndoSerialNumber ();
1736 break;
1737 case F_Block:
1738 RestoreUndoSerialNumber ();
1739 if (Bumped)
1740 IncrementUndoSerialNumber ();
1741 break;
1743 return 0;
1746 /* -------------------------------------------------------------------------- */
1748 static const char drc_syntax[] = "DRC()";
1750 static const char drc_help[] = "Invoke the DRC check.";
1752 /* %start-doc actions DRC
1754 Note that the design rule check uses the current board rule settings,
1755 not the current style settings.
1757 %end-doc */
1759 static int
1760 ActionDRCheck (int argc, char **argv, int x, int y)
1762 int count;
1764 if (gui->drc_gui == NULL || gui->drc_gui->log_drc_overview)
1766 Message (_("Rules are minspace %d.%02d, minoverlap %d.%d "
1767 "minwidth %d.%02d, minsilk %d.%02d\n"
1768 "min drill %d.%02d, min annular ring %d.%02d\n"),
1769 (PCB->Bloat + 1) / 100, (PCB->Bloat + 1) % 100,
1770 PCB->Shrink / 100, PCB->Shrink % 100,
1771 PCB->minWid / 100, PCB->minWid % 100,
1772 PCB->minSlk / 100, PCB->minSlk % 100,
1773 PCB->minDrill / 100, PCB->minDrill % 100,
1774 PCB->minRing / 100, PCB->minRing % 100);
1776 HideCrosshair (True);
1777 count = DRCAll ();
1778 if (gui->drc_gui == NULL || gui->drc_gui->log_drc_overview)
1780 if (count == 0)
1781 Message (_("No DRC problems found.\n"));
1782 else if (count > 0)
1783 Message (_("Found %d design rule errors.\n"), count);
1784 else
1785 Message (_("Aborted DRC after %d design rule errors.\n"), -count);
1787 RestoreCrosshair (True);
1788 return 0;
1791 /* -------------------------------------------------------------------------- */
1793 static const char dumplibrary_syntax[] = "DumpLibrary()";
1795 static const char dumplibrary_help[] =
1796 "Display the entire contents of the libraries.";
1798 /* %start-doc actions DumpLibrary
1801 %end-doc */
1803 static int
1804 ActionDumpLibrary (int argc, char **argv, int x, int y)
1806 int i, j;
1808 printf ("**** Do not count on this format. It will change ****\n\n");
1809 printf ("MenuN = %d\n", Library.MenuN);
1810 printf ("MenuMax = %d\n", Library.MenuMax);
1811 for (i = 0; i < Library.MenuN; i++)
1813 printf ("Library #%d:\n", i);
1814 printf (" EntryN = %d\n", Library.Menu[i].EntryN);
1815 printf (" EntryMax = %d\n", Library.Menu[i].EntryMax);
1816 printf (" Name = \"%s\"\n", UNKNOWN (Library.Menu[i].Name));
1817 printf (" directory = \"%s\"\n",
1818 UNKNOWN (Library.Menu[i].directory));
1819 printf (" Style = \"%s\"\n", UNKNOWN (Library.Menu[i].Style));
1820 printf (" flag = %d\n", Library.Menu[i].flag);
1822 for (j = 0; j < Library.Menu[i].EntryN; j++)
1824 printf (" #%4d: ", j);
1825 if (Library.Menu[i].Entry[j].Template == (char *) -1)
1827 printf ("newlib: \"%s\"\n",
1828 UNKNOWN (Library.Menu[i].Entry[j].ListEntry));
1830 else
1832 printf ("\"%s\", \"%s\", \"%s\", \"%s\", \"%s\"\n",
1833 UNKNOWN (Library.Menu[i].Entry[j].ListEntry),
1834 UNKNOWN (Library.Menu[i].Entry[j].Template),
1835 UNKNOWN (Library.Menu[i].Entry[j].Package),
1836 UNKNOWN (Library.Menu[i].Entry[j].Value),
1837 UNKNOWN (Library.Menu[i].Entry[j].Description));
1842 return 0;
1845 /* -------------------------------------------------------------------------- */
1847 static const char flip_syntax[] = "Flip(Object|Selected|SelectedElements)";
1849 static const char flip_help[] =
1850 "Flip an element to the opposite side of the board.";
1852 /* %start-doc actions Flip
1854 Note that the location of the element will be symmetric about the
1855 cursor location; i.e. if the part you are pointing at will still be at
1856 the same spot once the element is on the other side. When flipping
1857 multiple elements, this retains their positions relative to each
1858 other, not their absolute positions on the board.
1860 %end-doc */
1862 static int
1863 ActionFlip (int argc, char **argv, int x, int y)
1865 char *function = ARG (0);
1866 ElementTypePtr element;
1867 void *ptrtmp;
1868 int err = 0;
1870 if (function)
1872 HideCrosshair (True);
1873 switch (GetFunctionID (function))
1875 case F_Object:
1876 if ((SearchScreen (x, y, ELEMENT_TYPE,
1877 &ptrtmp, &ptrtmp, &ptrtmp)) != NO_TYPE)
1879 element = (ElementTypePtr) ptrtmp;
1880 ChangeElementSide (element, 2 * Crosshair.Y - PCB->MaxHeight);
1881 IncrementUndoSerialNumber ();
1882 Draw ();
1884 break;
1885 case F_Selected:
1886 case F_SelectedElements:
1887 ChangeSelectedElementSide ();
1888 break;
1889 default:
1890 err = 1;
1891 break;
1893 RestoreCrosshair (True);
1894 if (!err)
1895 return 0;
1898 AFAIL (flip);
1901 /* -------------------------------------------------------------------------- */
1903 static const char message_syntax[] = "Message(message)";
1905 static const char message_help[] = "Writes a message to the log window.";
1907 /* %start-doc actions Message
1909 This action displays a message to the log window. This action is primarily
1910 provided for use by other programs which may interface with PCB. If
1911 multiple arguments are given, each one is sent to the log window
1912 followed by a newline.
1914 %end-doc */
1916 static int
1917 ActionMessage (int argc, char **argv, int x, int y)
1919 int i;
1921 if (argc < 1)
1922 AFAIL (message);
1924 for (i = 0; i < argc; i++)
1926 Message (argv[i]);
1927 Message ("\n");
1930 return 0;
1934 /* -------------------------------------------------------------------------- */
1936 static const char setthermal_syntax[] =
1937 "SetThermal(Object|SelectedPins|SelectedVias|Selected, Style)";
1939 static const char setthermal_help[] =
1940 "Set the thermal (on the current layer) of pins or vias to the given style.\n"
1941 "Style = 0 means no thermal.\n"
1942 "Style = 1 has diagonal fingers with sharp edges.\n"
1943 "Style = 2 has horizontal and vertical fingers with sharp edges.\n"
1944 "Style = 3 is a solid connection to the plane."
1945 "Style = 4 has diagonal fingers with rounded edges.\n"
1946 "Style = 5 has horizontal and vertical fingers with rounded edges.\n";
1948 /* %start-doc actions SetThermal
1950 This changes how/whether pins or vias connect to any rectangle or polygon
1951 on the current layer. The first argument can specify one object, or all
1952 selected pins, or all selected vias, or all selected pins and vias.
1953 The second argument specifies the style of connection.
1954 There are 5 possibilities:
1955 0 - no connection,
1956 1 - 45 degree fingers with sharp edges,
1957 2 - horizontal & vertical fingers with sharp edges,
1958 3 - solid connection,
1959 4 - 45 degree fingers with rounded corners,
1960 5 - horizontal & vertical fingers with rounded corners.
1962 Pins and Vias may have thermals whether or not there is a polygon available
1963 to connect with. However, they will have no effect without the polygon.
1964 %end-doc */
1966 static int
1967 ActionSetThermal (int argc, char **argv, int x, int y)
1969 char *function = ARG (0);
1970 char *style = ARG (1);
1971 void *ptr1, *ptr2, *ptr3;
1972 int type, kind;
1973 int err = 0;
1975 if (function && *function && style && *style)
1977 Boolean absolute;
1979 kind = GetValue (style, NULL, &absolute);
1980 HideCrosshair (True);
1981 if (absolute)
1982 switch (GetFunctionID (function))
1984 case F_Object:
1985 if ((type =
1986 SearchScreen (Crosshair.X, Crosshair.Y, CHANGETHERMAL_TYPES,
1987 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
1989 ChangeObjectThermal (type, ptr1, ptr2, ptr3, kind);
1990 IncrementUndoSerialNumber ();
1991 Draw ();
1993 break;
1994 case F_SelectedPins:
1995 ChangeSelectedThermals (PIN_TYPE, kind);
1996 break;
1997 case F_SelectedVias:
1998 ChangeSelectedThermals (VIA_TYPE, kind);
1999 break;
2000 case F_Selected:
2001 case F_SelectedElements:
2002 ChangeSelectedThermals (CHANGETHERMAL_TYPES, kind);
2003 break;
2004 default:
2005 err = 1;
2006 break;
2008 else
2009 err = 1;
2010 RestoreCrosshair (True);
2011 if (!err)
2012 return 0;
2015 AFAIL (setthermal);
2018 /* ---------------------------------------------------------------------------
2019 * action routine to move the X pointer relative to the current position
2020 * syntax: MovePointer(deltax,deltay)
2022 void
2023 ActionMovePointer (char *deltax, char *deltay)
2025 LocationType x, y, dx, dy;
2027 /* save old crosshair position */
2028 x = Crosshair.X;
2029 y = Crosshair.Y;
2030 dx = (LocationType) (atoi (deltax) * PCB->Grid);
2031 dy = (LocationType) (atoi (deltay) * PCB->Grid);
2032 MoveCrosshairRelative (TO_SCREEN_SIGN_X (dx), TO_SCREEN_SIGN_Y (dy));
2033 FitCrosshairIntoGrid (Crosshair.X, Crosshair.Y);
2034 /* restore crosshair for erasure */
2035 Crosshair.X = x;
2036 Crosshair.Y = y;
2037 HideCrosshair (False);
2038 MoveCrosshairRelative (TO_SCREEN_SIGN_X (dx), TO_SCREEN_SIGN_Y (dy));
2039 /* update object position and cursor location */
2040 AdjustAttachedObjects ();
2041 RestoreCrosshair (False);
2044 /* ---------------------------------------------------------------------------
2045 * !!! no action routine !!!
2047 * event handler to set the cursor according to the X pointer position
2048 * called from inside main.c
2050 void
2051 EventMoveCrosshair (int ev_x, int ev_y)
2053 #ifdef HAVE_LIBSTROKE
2054 if (mid_stroke)
2056 StrokeBox.X2 = TO_PCB_X (ev_x);
2057 StrokeBox.Y2 = TO_PCB_Y (ev_y);
2058 stroke_record (Event->x, ev_y);
2059 return;
2061 #endif /* HAVE_LIBSTROKE */
2062 /* ignore events that are caused by ActionMovePointer */
2063 if (!IgnoreMotionEvents)
2065 if (MoveCrosshairAbsolute (ev_x, ev_y))
2068 /* update object position and cursor location */
2069 AdjustAttachedObjects ();
2070 RestoreCrosshair (False);
2073 else
2074 IgnoreMotionEvents = False;
2077 /* --------------------------------------------------------------------------- */
2079 static const char setvalue_syntax[] =
2080 "SetValue(Grid|Line|LineSize|Text|TextScale|ViaDrillingHole|Via|ViaSize, delta)";
2082 static const char setvalue_help[] =
2083 "Change various board-wide values and sizes.";
2085 /* %start-doc actions SetValue
2087 @table @code
2089 @item ViaDrillingHole
2090 Changes the diameter of the drill for new vias.
2092 @item Grid
2093 Sets the grid spacing.
2095 @item Line
2096 @item LineSize
2097 Changes the thickness of new lines.
2099 @item Via
2100 @item ViaSize
2101 Changes the diameter of new vias.
2103 @item Text
2104 @item TextScale
2105 Changes the size of new text.
2107 @end table
2109 %end-doc */
2111 static int
2112 ActionSetValue (int argc, char **argv, int x, int y)
2114 char *function = ARG (0);
2115 char *val = ARG (1);
2116 char *units = ARG (2);
2117 Boolean r; /* flag for 'relative' value */
2118 float value;
2119 int err = 0;
2121 if (function && val)
2123 HideCrosshair (True);
2124 value = GetValue (val, units, &r);
2125 switch (GetFunctionID (function))
2127 case F_ViaDrillingHole:
2128 SetViaDrillingHole (r ? value : value + Settings.ViaDrillingHole,
2129 False);
2130 hid_action ("RouteStylesChanged");
2131 break;
2133 case F_Grid:
2134 if (!r)
2136 if ((value == (int) value && PCB->Grid == (int) PCB->Grid)
2137 || (value != (int) value && PCB->Grid != (int) PCB->Grid)
2138 || PCB->Grid ==1)
2141 * On the way down short against the minimum
2142 * PCB drawing unit
2144 if ((value + PCB->Grid) < 1)
2145 SetGrid (1, False);
2146 else if (PCB->Grid == 1)
2147 SetGrid ( value, False);
2148 else
2149 SetGrid (value + PCB->Grid, False);
2152 else
2153 Message (_
2154 ("Don't combine metric/English grids like that!\n"));
2156 else
2157 SetGrid (value, False);
2158 break;
2160 case F_LineSize:
2161 case F_Line:
2162 SetLineSize (r ? value : value + Settings.LineThickness);
2163 hid_action ("RouteStylesChanged");
2164 break;
2166 case F_Via:
2167 case F_ViaSize:
2168 SetViaSize (r ? value : value + Settings.ViaThickness, False);
2169 hid_action ("RouteStylesChanged");
2170 break;
2172 case F_Text:
2173 case F_TextScale:
2174 value /= 45;
2175 SetTextScale (r ? value : value + Settings.TextScale);
2176 break;
2177 default:
2178 err = 1;
2179 break;
2181 RestoreCrosshair (True);
2182 if (!err)
2183 return 0;
2186 AFAIL (setvalue);
2190 /* --------------------------------------------------------------------------- */
2192 static const char quit_syntax[] = "Quit()";
2194 static const char quit_help[] = "Quits the application after confirming.";
2196 /* %start-doc actions Quit
2198 If you have unsaved changes, you will be prompted to confirm (or
2199 save) before quitting.
2201 %end-doc */
2203 static int
2204 ActionQuit (int argc, char **argv, int x, int y)
2206 char *force = ARG (0);
2207 if (force && strcasecmp (force, "force") == 0)
2209 PCB->Changed = 0;
2210 exit (0);
2212 if (!PCB->Changed || gui->close_confirm_dialog () == HID_CLOSE_CONFIRM_OK)
2213 QuitApplication ();
2214 return 1;
2217 /* --------------------------------------------------------------------------- */
2219 static const char connection_syntax[] =
2220 "Connection(Find|ResetLinesAndPolygons|ResetPinsAndVias|Reset)";
2222 static const char connection_help[] =
2223 "Searches connections of the object at the cursor position.";
2225 /* %start-doc actions Connection
2227 Connections found with this action will be highlighted in the
2228 ``connected-color'' color and will have the ``found'' flag set.
2230 @table @code
2232 @item Find
2233 The net under the cursor is ``found''.
2235 @item ResetLinesAndPolygons
2236 Any ``found'' lines and polygons are marked ``not found''.
2238 @item ResetPinsAndVias
2239 Any ``found'' pins and vias are marked ``not found''.
2241 @item Reset
2242 All ``found'' objects are marked ``not found''.
2244 @item Measure
2245 The net under the cursor is found and measured (the lengths of all
2246 line segments are added together)
2248 @end table
2250 %end-doc */
2252 static int
2253 ActionConnection (int argc, char **argv, int x, int y)
2255 char *function = ARG (0);
2256 if (function)
2258 HideCrosshair (True);
2259 switch (GetFunctionID (function))
2261 case F_Find:
2263 gui->get_coords ("Click on a connection", &x, &y);
2264 LookupConnection (x, y, True, 1, FOUNDFLAG);
2265 break;
2268 case F_ResetLinesAndPolygons:
2269 ResetFoundLinesAndPolygons (True);
2270 break;
2272 case F_ResetPinsViasAndPads:
2273 ResetFoundPinsViasAndPads (True);
2274 break;
2276 case F_Reset:
2277 SaveUndoSerialNumber ();
2278 ResetFoundPinsViasAndPads (True);
2279 RestoreUndoSerialNumber ();
2280 ResetFoundLinesAndPolygons (True);
2281 break;
2283 RestoreCrosshair (True);
2284 return 0;
2287 AFAIL (connection);
2290 /* --------------------------------------------------------------------------- */
2292 static const char disperseelements_syntax[] =
2293 "DisperseElements(All|Selected)";
2295 static const char disperseelements_help[] = "Disperses elements.";
2297 /* %start-doc actions DisperseElements
2299 Normally this is used when starting a board, by selecting all elements
2300 and then dispersing them. This scatters the elements around the board
2301 so that you can pick individual ones, rather than have all the
2302 elements at the same 0,0 coordinate and thus impossible to choose
2303 from.
2305 %end-doc */
2307 #define GAP 10000
2309 static int
2310 ActionDisperseElements (int argc, char **argv, int x, int y)
2312 char *function = ARG (0);
2313 long minx, miny, maxx, maxy, dx, dy;
2314 int all = 0, bad = 0;
2316 minx = GAP;
2317 miny = GAP;
2318 maxx = GAP;
2319 maxy = GAP;
2321 if (!function || !*function)
2323 bad = 1;
2325 else
2327 switch (GetFunctionID (function))
2329 case F_All:
2330 all = 1;
2331 break;
2333 case F_Selected:
2334 all = 0;
2335 break;
2337 default:
2338 bad = 1;
2342 if (bad)
2344 AFAIL (disperseelements);
2348 ELEMENT_LOOP (PCB->Data);
2351 * If we want to disperse selected elements, maybe we need smarter
2352 * code here to avoid putting components on top of others which
2353 * are not selected. For now, I'm assuming that this is typically
2354 * going to be used either with a brand new design or a scratch
2355 * design holding some new components
2357 if (!TEST_FLAG (LOCKFLAG, element) && (all || TEST_FLAG (SELECTEDFLAG, element)))
2360 /* figure out how much to move the element */
2361 dx = minx - element->BoundingBox.X1;
2363 /* snap to the grid */
2364 dx -= (element->MarkX + dx) % (long) (PCB->Grid);
2367 * and add one grid size so we make sure we always space by GAP or
2368 * more
2370 dx += (long) (PCB->Grid);
2372 /* Figure out if this row has room. If not, start a new row */
2373 if (GAP + element->BoundingBox.X2 + dx > PCB->MaxWidth)
2375 miny = maxy + GAP;
2376 minx = GAP;
2379 /* figure out how much to move the element */
2380 dx = minx - element->BoundingBox.X1;
2381 dy = miny - element->BoundingBox.Y1;
2383 /* snap to the grid */
2384 dx -= (element->MarkX + dx) % (long) (PCB->Grid);
2385 dx += (long) (PCB->Grid);
2386 dy -= (element->MarkY + dy) % (long) (PCB->Grid);
2387 dy += (long) (PCB->Grid);
2389 /* move the element */
2390 MoveElementLowLevel (PCB->Data, element, dx, dy);
2392 /* and add to the undo list so we can undo this operation */
2393 AddObjectToMoveUndoList (ELEMENT_TYPE, NULL, NULL, element, dx, dy);
2395 /* keep track of how tall this row is */
2396 minx += element->BoundingBox.X2 - element->BoundingBox.X1 + GAP;
2397 if (maxy < element->BoundingBox.Y2)
2399 maxy = element->BoundingBox.Y2;
2404 END_LOOP;
2406 /* done with our action so increment the undo # */
2407 IncrementUndoSerialNumber ();
2409 ClearAndRedrawOutput ();
2410 SetChangedFlag (True);
2412 return 0;
2415 #undef GAP
2417 /* --------------------------------------------------------------------------- */
2419 static const char display_syntax[] =
2420 "Display(NameOnPCB|Description|Value)\n"
2421 "Display(Grid|Redraw)\n"
2422 "Display(CycleClip|CycleCrosshair|Toggle45Degree|ToggleStartDirection)\n"
2423 "Display(ToggleGrid|ToggleRubberBandMode|ToggleUniqueNames)\n"
2424 "Display(ToggleMask|ToggleName|ToggleClearLine|ToggleFullPoly|ToggleSnapPin)\n"
2425 "Display(ToggleThindraw|ToggleThindrawPoly|ToggleOrthoMove|ToggleLocalRef)\n"
2426 "Display(ToggleCheckPlanes|ToggleShowDRC|ToggleAutoDRC)\n"
2427 "Display(ToggleLiveRoute|LockNames|OnlyNames)\n"
2428 "Display(Pinout|PinOrPadName)\n" "Display(Scroll, Direction)";
2430 static const char display_help[] = "Several display-related actions.";
2432 /* %start-doc actions Display
2434 @table @code
2436 @item NameOnPCB
2437 @item Description
2438 @item Value
2439 Specify whether all elements show their name, description, or value.
2441 @item Redraw
2442 Redraw the whole board.
2444 @item Toggle45Degree
2445 When clear, lines can be drawn at any angle. When set, lines are
2446 restricted to multiples of 45 degrees and requested lines may be
2447 broken up according to the clip setting.
2449 @item CycleClip
2450 Changes the way lines are restricted to 45 degree increments. The
2451 various settings are: straight only, orthogonal then angled, and angled
2452 then orthogonal. If AllDirections is set, this action disables it.
2454 @item CycleCrosshair
2455 Changes crosshair drawing. Crosshair may accept form of 4-ray,
2456 8-ray and 12-ray cross.
2458 @item ToggleRubberBandMode
2459 If set, moving an object moves all the lines attached to it too.
2461 @item ToggleStartDirection
2462 If set, each time you set a point in a line, the Clip toggles between
2463 orth-angle and angle-ortho.
2465 @item ToggleUniqueNames
2466 If set, you will not be permitted to change the name of an element to
2467 match that of another element.
2469 @item ToggleSnapPin
2470 If set, pin centers and pad end points are treated as additional grid
2471 points that the cursor can snap to.
2473 @item ToggleLocalRef
2474 If set, the mark is automatically set to the beginning of any move, so
2475 you can see the relative distance you've moved.
2477 @item ToggleThindraw
2478 If set, objects on the screen are drawn as outlines (lines are drawn
2479 as center-lines). This lets you see line endpoints hidden under pins,
2480 for example.
2482 @item ToggleThindrawPoly
2483 If set, polygons on the screen are drawn as outlines.
2485 @item ToggleShowDRC
2486 If set, pending objects (i.e. lines you're in the process of drawing)
2487 will be drawn with an outline showing how far away from other copper
2488 you need to be.
2490 @item ToggleLiveRoute
2491 If set, the progress of the autorouter will be visible on the screen.
2493 @item ToggleAutoDRC
2494 If set, you will not be permitted to make connections which violate
2495 the current DRC and netlist settings.
2497 @item ToggleCheckPlanes
2498 If set, lines and arcs aren't drawn, which usually leaves just the
2499 polygons. If you also disable all but the layer you're interested in,
2500 this allows you to check for isolated regions.
2502 @item ToggleOrthoMove
2503 If set, the crosshair is only allowed to move orthogonally from its
2504 previous position. I.e. you can move an element or line up, down,
2505 left, or right, but not up+left or down+right.
2507 @item ToggleName
2508 Selects whether the pinouts show the pin names or the pin numbers.
2510 @item ToggleMask
2511 Turns the solder mask on or off.
2513 @item ToggleClearLine
2514 When set, the clear-line flag causes new lines and arcs to have their
2515 ``clear polygons'' flag set, so they won't be electrically connected
2516 to any polygons they overlap.
2518 @item ToggleFullPoly
2519 When set, the full-poly flag causes new polygons to have their
2520 ``full polygon'' flag set, so all parts of them will be displayed
2521 instead of only the biggest one.
2523 @item ToggleGrid
2524 Resets the origin of the current grid to be wherever the mouse pointer
2525 is (not where the crosshair currently is). If you provide two numbers
2526 after this, the origin is set to that coordinate. The numbers are in
2527 PCB internal units, currently 1/100 mil.
2529 @item Grid
2530 Toggles whether the grid is displayed or not.
2532 @item Pinout
2533 Causes the pinout of the element indicated by the cursor to be
2534 displayed, usually in a separate window.
2536 @item PinOrPadName
2537 Toggles whether the names of pins, pads, or (yes) vias will be
2538 displayed. If the cursor is over an element, all of its pins and pads
2539 are affected.
2541 @item Step <direction> <amount> <units>
2542 Steps the crosshair in the given direction, with 1=down/left, 2=down,
2543 etc, according to the numeric keypad layout. If amount is not given,
2544 the crosshair steps along the grid.
2546 @end table
2548 %end-doc */
2550 static int
2551 ActionDisplay (int argc, char **argv, int childX, int childY)
2553 char *function, *str_dir;
2554 int id;
2555 int err = 0;
2557 function = ARG (0);
2558 str_dir = ARG (1);
2560 if (function && (!str_dir || !*str_dir))
2562 HideCrosshair (True);
2563 switch (id = GetFunctionID (function))
2566 /* redraw layout with clearing the background */
2567 case F_ClearAndRedraw:
2568 gui->invalidate_all();
2569 break;
2571 /* redraw layout without clearing the background */
2572 case F_Redraw:
2574 BoxType area;
2575 area.X1 = 0;
2576 area.Y1 = 0;
2577 area.X2 = Output.Width;
2578 area.Y2 = Output.Height;
2579 RedrawOutput (&area);
2580 break;
2583 /* change the displayed name of elements */
2584 case F_Value:
2585 case F_NameOnPCB:
2586 case F_Description:
2587 ELEMENT_LOOP (PCB->Data);
2589 EraseElementName (element);
2591 END_LOOP;
2592 CLEAR_FLAG (DESCRIPTIONFLAG | NAMEONPCBFLAG, PCB);
2593 switch (id)
2595 case F_Value:
2596 break;
2597 case F_NameOnPCB:
2598 SET_FLAG (NAMEONPCBFLAG, PCB);
2599 break;
2600 case F_Description:
2601 SET_FLAG (DESCRIPTIONFLAG, PCB);
2602 break;
2604 ELEMENT_LOOP (PCB->Data);
2606 DrawElementName (element, 0);
2608 END_LOOP;
2609 Draw ();
2610 break;
2612 /* toggle line-adjust flag */
2613 case F_ToggleAllDirections:
2614 TOGGLE_FLAG (ALLDIRECTIONFLAG, PCB);
2615 AdjustAttachedObjects ();
2616 break;
2618 case F_CycleClip:
2619 if TEST_FLAG
2620 (ALLDIRECTIONFLAG, PCB)
2622 TOGGLE_FLAG (ALLDIRECTIONFLAG, PCB);
2623 PCB->Clipping = 0;
2625 else
2626 PCB->Clipping = (PCB->Clipping + 1) % 3;
2627 AdjustAttachedObjects ();
2628 break;
2630 case F_CycleCrosshair:
2631 Crosshair.shape++;
2632 if (Crosshair_Shapes_Number == Crosshair.shape)
2633 Crosshair.shape = Basic_Crosshair_Shape;
2634 break;
2636 case F_ToggleRubberBandMode:
2637 TOGGLE_FLAG (RUBBERBANDFLAG, PCB);
2638 break;
2640 case F_ToggleStartDirection:
2641 TOGGLE_FLAG (SWAPSTARTDIRFLAG, PCB);
2642 break;
2644 case F_ToggleUniqueNames:
2645 TOGGLE_FLAG (UNIQUENAMEFLAG, PCB);
2646 break;
2648 case F_ToggleSnapPin:
2649 TOGGLE_FLAG (SNAPPINFLAG, PCB);
2650 break;
2652 case F_ToggleLocalRef:
2653 TOGGLE_FLAG (LOCALREFFLAG, PCB);
2654 break;
2656 case F_ToggleThindraw:
2657 TOGGLE_FLAG (THINDRAWFLAG, PCB);
2658 ClearAndRedrawOutput ();
2659 break;
2661 case F_ToggleThindrawPoly:
2662 TOGGLE_FLAG (THINDRAWPOLYFLAG, PCB);
2663 ClearAndRedrawOutput ();
2664 break;
2666 case F_ToggleLockNames:
2667 TOGGLE_FLAG (LOCKNAMESFLAG, PCB);
2668 CLEAR_FLAG (ONLYNAMESFLAG, PCB);
2669 break;
2671 case F_ToggleOnlyNames:
2672 TOGGLE_FLAG (ONLYNAMESFLAG, PCB);
2673 CLEAR_FLAG (LOCKNAMESFLAG, PCB);
2674 break;
2676 case F_ToggleHideNames:
2677 TOGGLE_FLAG (HIDENAMESFLAG, PCB);
2678 ClearAndRedrawOutput ();
2679 break;
2681 case F_ToggleShowDRC:
2682 TOGGLE_FLAG (SHOWDRCFLAG, PCB);
2683 break;
2685 case F_ToggleLiveRoute:
2686 TOGGLE_FLAG (LIVEROUTEFLAG, PCB);
2687 break;
2689 case F_ToggleAutoDRC:
2690 TOGGLE_FLAG (AUTODRCFLAG, PCB);
2691 if (TEST_FLAG (AUTODRCFLAG, PCB) && Settings.Mode == LINE_MODE)
2693 SaveUndoSerialNumber ();
2694 ResetFoundPinsViasAndPads (True);
2695 RestoreUndoSerialNumber ();
2696 ResetFoundLinesAndPolygons (True);
2697 if (Crosshair.AttachedLine.State != STATE_FIRST)
2698 LookupConnection (Crosshair.AttachedLine.Point1.X,
2699 Crosshair.AttachedLine.Point1.Y, True, 1,
2700 FOUNDFLAG);
2702 break;
2704 case F_ToggleCheckPlanes:
2705 TOGGLE_FLAG (CHECKPLANESFLAG, PCB);
2706 ClearAndRedrawOutput ();
2707 break;
2709 case F_ToggleOrthoMove:
2710 TOGGLE_FLAG (ORTHOMOVEFLAG, PCB);
2711 break;
2713 case F_ToggleName:
2714 TOGGLE_FLAG (SHOWNUMBERFLAG, PCB);
2715 UpdateAll ();
2716 break;
2718 case F_ToggleMask:
2719 TOGGLE_FLAG (SHOWMASKFLAG, PCB);
2720 UpdateAll ();
2721 break;
2723 case F_ToggleClearLine:
2724 TOGGLE_FLAG (CLEARNEWFLAG, PCB);
2725 break;
2727 case F_ToggleFullPoly:
2728 TOGGLE_FLAG (NEWFULLPOLYFLAG, PCB);
2729 break;
2731 /* shift grid alignment */
2732 case F_ToggleGrid:
2734 float oldGrid;
2736 oldGrid = PCB->Grid;
2737 PCB->Grid = 1.0;
2738 if (MoveCrosshairAbsolute (childX, childY))
2739 RestoreCrosshair (False); /* was hidden by MoveCrosshairAbs */
2740 SetGrid (oldGrid, True);
2742 break;
2744 /* toggle displaying of the grid */
2745 case F_Grid:
2746 Settings.DrawGrid = !Settings.DrawGrid;
2747 UpdateAll ();
2748 break;
2750 /* display the pinout of an element */
2751 case F_Pinout:
2753 ElementTypePtr element;
2754 void *ptrtmp;
2755 int x, y;
2757 gui->get_coords ("Click on an element", &x, &y);
2758 if ((SearchScreen
2759 (x, y, ELEMENT_TYPE, &ptrtmp,
2760 &ptrtmp, &ptrtmp)) != NO_TYPE)
2762 element = (ElementTypePtr) ptrtmp;
2763 gui->show_item (element);
2765 break;
2768 /* toggle displaying of pin/pad/via names */
2769 case F_PinOrPadName:
2771 void *ptr1, *ptr2, *ptr3;
2773 switch (SearchScreen (Crosshair.X, Crosshair.Y,
2774 ELEMENT_TYPE | PIN_TYPE | PAD_TYPE |
2775 VIA_TYPE, (void **) &ptr1, (void **) &ptr2,
2776 (void **) &ptr3))
2778 case ELEMENT_TYPE:
2779 PIN_LOOP ((ElementTypePtr) ptr1);
2781 if (TEST_FLAG (DISPLAYNAMEFLAG, pin))
2782 ErasePinName (pin);
2783 else
2784 DrawPinName (pin, 0);
2785 AddObjectToFlagUndoList (PIN_TYPE, ptr1, pin, pin);
2786 TOGGLE_FLAG (DISPLAYNAMEFLAG, pin);
2788 END_LOOP;
2789 PAD_LOOP ((ElementTypePtr) ptr1);
2791 if (TEST_FLAG (DISPLAYNAMEFLAG, pad))
2792 ErasePadName (pad);
2793 else
2794 DrawPadName (pad, 0);
2795 AddObjectToFlagUndoList (PAD_TYPE, ptr1, pad, pad);
2796 TOGGLE_FLAG (DISPLAYNAMEFLAG, pad);
2798 END_LOOP;
2799 SetChangedFlag (True);
2800 IncrementUndoSerialNumber ();
2801 Draw ();
2802 break;
2804 case PIN_TYPE:
2805 if (TEST_FLAG (DISPLAYNAMEFLAG, (PinTypePtr) ptr2))
2806 ErasePinName ((PinTypePtr) ptr2);
2807 else
2808 DrawPinName ((PinTypePtr) ptr2, 0);
2809 AddObjectToFlagUndoList (PIN_TYPE, ptr1, ptr2, ptr3);
2810 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PinTypePtr) ptr2);
2811 SetChangedFlag (True);
2812 IncrementUndoSerialNumber ();
2813 Draw ();
2814 break;
2816 case PAD_TYPE:
2817 if (TEST_FLAG (DISPLAYNAMEFLAG, (PadTypePtr) ptr2))
2818 ErasePadName ((PadTypePtr) ptr2);
2819 else
2820 DrawPadName ((PadTypePtr) ptr2, 0);
2821 AddObjectToFlagUndoList (PAD_TYPE, ptr1, ptr2, ptr3);
2822 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PadTypePtr) ptr2);
2823 SetChangedFlag (True);
2824 IncrementUndoSerialNumber ();
2825 Draw ();
2826 break;
2827 case VIA_TYPE:
2828 if (TEST_FLAG (DISPLAYNAMEFLAG, (PinTypePtr) ptr2))
2829 EraseViaName ((PinTypePtr) ptr2);
2830 else
2831 DrawViaName ((PinTypePtr) ptr2, 0);
2832 AddObjectToFlagUndoList (VIA_TYPE, ptr1, ptr2, ptr3);
2833 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PinTypePtr) ptr2);
2834 SetChangedFlag (True);
2835 IncrementUndoSerialNumber ();
2836 Draw ();
2837 break;
2839 break;
2841 default:
2842 err = 1;
2844 RestoreCrosshair (True);
2846 else if (function && str_dir)
2848 switch (GetFunctionID (function))
2850 case F_ToggleGrid:
2851 if (argc > 2)
2853 /* FIXME: units */
2854 PCB->GridOffsetX = atoi (argv[1]);
2855 PCB->GridOffsetY = atoi (argv[2]);
2856 if (Settings.DrawGrid)
2857 UpdateAll ();
2859 break;
2861 default:
2862 err = 1;
2863 break;
2867 if (!err)
2868 return 0;
2870 AFAIL (display);
2873 /* --------------------------------------------------------------------------- */
2875 static const char mode_syntax[] =
2876 "Mode(Arc|Arrow|Copy|InsertPoint|Line|Lock|Move|None|PasteBuffer)\n"
2877 "Mode(Polygon|Rectangle|Remove|Rotate|Text|Thermal|Via)\n"
2878 "Mode(Notify|Release|Cancel|Stroke)\n" "Mode(Save|Restore)";
2880 static const char mode_help[] = "Change or use the tool mode.";
2882 /* %start-doc actions Mode
2884 @table @code
2886 @item Arc
2887 @itemx Arrow
2888 @itemx Copy
2889 @itemx InsertPoint
2890 @itemx Line
2891 @itemx Lock
2892 @itemx Move
2893 @itemx None
2894 @itemx PasteBuffer
2895 @itemx Polygon
2896 @itemx Rectangle
2897 @itemx Remove
2898 @itemx Rotate
2899 @itemx Text
2900 @itemx Thermal
2901 @itemx Via
2902 Select the indicated tool.
2904 @item Notify
2905 Called when you press the mouse button, or move the mouse.
2907 @item Release
2908 Called when you release the mouse button.
2910 @item Cancel
2911 Cancels any pending tool activity, allowing you to restart elsewhere.
2912 For example, this allows you to start a new line rather than attach a
2913 line to the previous line.
2915 @item Escape
2916 Similar to Cancel but calling this action a second time will return
2917 to the Arrow tool.
2919 @item Stroke
2920 If your @code{pcb} was built with libstroke, this invokes the stroke
2921 input method. If not, this will restart a drawing mode if you were
2922 drawing, else it will select objects.
2924 @item Save
2925 Remembers the current tool.
2927 @item Restore
2928 Restores the tool to the last saved tool.
2930 @end table
2932 %end-doc */
2934 static int
2935 ActionMode (int argc, char **argv, int x, int y)
2937 char *function = ARG (0);
2939 if (function)
2941 Note.X = Crosshair.X;
2942 Note.Y = Crosshair.Y;
2943 HideCrosshair (True);
2944 switch (GetFunctionID (function))
2946 case F_Arc:
2947 SetMode (ARC_MODE);
2948 break;
2949 case F_Arrow:
2950 SetMode (ARROW_MODE);
2951 break;
2952 case F_Copy:
2953 SetMode (COPY_MODE);
2954 break;
2955 case F_InsertPoint:
2956 SetMode (INSERTPOINT_MODE);
2957 break;
2958 case F_Line:
2959 SetMode (LINE_MODE);
2960 break;
2961 case F_Lock:
2962 SetMode (LOCK_MODE);
2963 break;
2964 case F_Move:
2965 SetMode (MOVE_MODE);
2966 break;
2967 case F_None:
2968 SetMode (NO_MODE);
2969 break;
2970 case F_Cancel:
2972 int saved_mode = Settings.Mode;
2973 SetMode (NO_MODE);
2974 SetMode (saved_mode);
2976 break;
2977 case F_Escape:
2979 switch (Settings.Mode)
2981 case VIA_MODE:
2982 case PASTEBUFFER_MODE:
2983 case TEXT_MODE:
2984 case ROTATE_MODE:
2985 case REMOVE_MODE:
2986 case MOVE_MODE:
2987 case COPY_MODE:
2988 case INSERTPOINT_MODE:
2989 case RUBBERBANDMOVE_MODE:
2990 case THERMAL_MODE:
2991 case LOCK_MODE:
2992 SetMode (NO_MODE);
2993 SetMode (ARROW_MODE);
2994 break;
2996 case LINE_MODE:
2997 if (Crosshair.AttachedLine.State == STATE_FIRST)
2998 SetMode (ARROW_MODE);
2999 else
3001 SetMode (NO_MODE);
3002 SetMode (LINE_MODE);
3004 break;
3006 case RECTANGLE_MODE:
3007 if (Crosshair.AttachedBox.State == STATE_FIRST)
3008 SetMode (ARROW_MODE);
3009 else
3011 SetMode (NO_MODE);
3012 SetMode (RECTANGLE_MODE);
3014 break;
3016 case POLYGON_MODE:
3017 if (Crosshair.AttachedLine.State == STATE_FIRST)
3018 SetMode (ARROW_MODE);
3019 else
3021 SetMode (NO_MODE);
3022 SetMode (POLYGON_MODE);
3024 break;
3026 case ARC_MODE:
3027 if (Crosshair.AttachedBox.State == STATE_FIRST)
3028 SetMode (ARROW_MODE);
3029 else
3031 SetMode (NO_MODE);
3032 SetMode (ARC_MODE);
3034 break;
3036 case ARROW_MODE:
3037 break;
3039 default:
3040 break;
3043 break;
3045 case F_Notify:
3046 NotifyMode ();
3047 break;
3048 case F_PasteBuffer:
3049 SetMode (PASTEBUFFER_MODE);
3050 break;
3051 case F_Polygon:
3052 SetMode (POLYGON_MODE);
3053 break;
3054 #ifndef HAVE_LIBSTROKE
3055 case F_Release:
3056 ReleaseMode ();
3057 break;
3058 #else
3059 case F_Release:
3060 if (mid_stroke)
3061 FinishStroke ();
3062 else
3063 ReleaseMode ();
3064 break;
3065 #endif
3066 case F_Remove:
3067 SetMode (REMOVE_MODE);
3068 break;
3069 case F_Rectangle:
3070 SetMode (RECTANGLE_MODE);
3071 break;
3072 case F_Rotate:
3073 SetMode (ROTATE_MODE);
3074 break;
3075 case F_Stroke:
3076 #ifdef HAVE_LIBSTROKE
3077 mid_stroke = True;
3078 StrokeBox.X1 = Crosshair.X;
3079 StrokeBox.Y1 = Crosshair.Y;
3080 break;
3081 #else
3082 /* Handle middle mouse button restarts of drawing mode. If not in
3083 | a drawing mode, middle mouse button will select objects.
3085 if (Settings.Mode == LINE_MODE
3086 && Crosshair.AttachedLine.State != STATE_FIRST)
3088 SetMode (LINE_MODE);
3090 else if (Settings.Mode == ARC_MODE
3091 && Crosshair.AttachedBox.State != STATE_FIRST)
3092 SetMode (ARC_MODE);
3093 else if (Settings.Mode == RECTANGLE_MODE
3094 && Crosshair.AttachedBox.State != STATE_FIRST)
3095 SetMode (RECTANGLE_MODE);
3096 else if (Settings.Mode == POLYGON_MODE
3097 && Crosshair.AttachedLine.State != STATE_FIRST)
3098 SetMode (POLYGON_MODE);
3099 else
3101 SaveMode ();
3102 saved_mode = True;
3103 SetMode (ARROW_MODE);
3104 NotifyMode ();
3106 break;
3107 #endif
3108 case F_Text:
3109 SetMode (TEXT_MODE);
3110 break;
3111 case F_Thermal:
3112 SetMode (THERMAL_MODE);
3113 break;
3114 case F_Via:
3115 SetMode (VIA_MODE);
3116 break;
3118 case F_Restore: /* restore the last saved mode */
3119 RestoreMode ();
3120 break;
3122 case F_Save: /* save currently selected mode */
3123 SaveMode ();
3124 break;
3126 RestoreCrosshair (True);
3127 return 0;
3130 AFAIL (mode);
3133 /* --------------------------------------------------------------------------- */
3135 static const char removeselected_syntax[] = "RemoveSelected()";
3137 static const char removeselected_help[] = "Removes any selected objects.";
3139 /* %start-doc actions RemoveSelected
3141 %end-doc */
3143 static int
3144 ActionRemoveSelected (int argc, char **argv, int x, int y)
3146 HideCrosshair (True);
3147 if (RemoveSelected ())
3148 SetChangedFlag (True);
3149 RestoreCrosshair (True);
3150 return 0;
3153 /* --------------------------------------------------------------------------- */
3155 static const char renumber_syntax[] = "Renumber()\n" "Renumber(filename)";
3157 static const char renumber_help[] =
3158 "Renumber all elements. The changes will be recorded to filename\n"
3159 "for use in backannotating these changes to the schematic.";
3161 /* %start-doc actions Renumber
3163 %end-doc */
3165 static int
3166 ActionRenumber (int argc, char **argv, int x, int y)
3168 Boolean changed = False;
3169 ElementTypePtr *element_list;
3170 ElementTypePtr *locked_element_list;
3171 unsigned int i, j, k, cnt, lock_cnt;
3172 unsigned int tmpi;
3173 size_t sz;
3174 char *tmps;
3175 char *name;
3176 FILE *out;
3177 static char * default_file = NULL;
3178 size_t cnt_list_sz = 100;
3179 struct _cnt_list
3181 char *name;
3182 unsigned int cnt;
3183 } *cnt_list;
3184 char **was, **is, *pin;
3185 unsigned int c_cnt = 0;
3186 int unique, ok;
3187 int free_name = 0;
3189 if (argc < 1)
3192 * We deal with the case where name already exists in this
3193 * function so the GUI doesn't need to deal with it
3195 name = gui->fileselect (_("Save Renumber Annotation File As ..."),
3196 _("Choose a file to record the renumbering to.\n"
3197 "This file may be used to back annotate the\n"
3198 "change to the schematics.\n"),
3199 default_file, ".eco", "eco",
3202 free_name = 1;
3204 else
3205 name = argv[0];
3207 if (default_file)
3209 free (default_file);
3210 default_file = NULL;
3213 if (name && *name)
3215 default_file = strdup (name);
3218 if ((out = fopen (name, "r")))
3220 fclose (out);
3221 if (!gui->confirm_dialog (_("File exists! Ok to overwrite?"), 0))
3223 if (free_name && name)
3224 free (name);
3225 return 0;
3229 if ((out = fopen (name, "w")) == NULL)
3231 Message ("Could not open %s\n", name);
3232 if (free_name && name)
3233 free (name);
3234 return 1;
3237 if (free_name && name)
3238 free (name);
3240 fprintf (out, "*COMMENT* PCB Annotation File\n");
3241 fprintf (out, "*FILEVERSION* 20061031\n");
3244 * Make a first pass through all of the elements and sort them out
3245 * by location on the board. While here we also collect a list of
3246 * locked elements.
3248 * We'll actually renumber things in the 2nd pass.
3250 element_list = calloc (PCB->Data->ElementN, sizeof (ElementTypePtr));
3251 locked_element_list = calloc (PCB->Data->ElementN, sizeof (ElementTypePtr));
3252 was = calloc (PCB->Data->ElementN, sizeof (char *));
3253 is = calloc (PCB->Data->ElementN, sizeof (char *));
3254 if (element_list == NULL || locked_element_list == NULL || was == NULL
3255 || is == NULL)
3257 fprintf (stderr, "calloc() failed in %s\n", __FUNCTION__);
3258 exit (1);
3262 cnt = 0;
3263 lock_cnt = 0;
3264 ELEMENT_LOOP (PCB->Data);
3266 if (TEST_FLAG (LOCKFLAG, element->Name) || TEST_FLAG (LOCKFLAG, element))
3269 * add to the list of locked elements which we won't try to
3270 * renumber and whose reference designators are now reserved.
3272 fprintf (out,
3273 "*WARN* Element \"%s\" at (%d,%d) is locked and will not be renumbered.\n",
3274 UNKNOWN (NAMEONPCB_NAME (element)), element->MarkX,
3275 element->MarkY);
3276 locked_element_list[lock_cnt] = element;
3277 lock_cnt++;
3280 else
3282 /* count of devices which will be renumbered */
3283 cnt++;
3285 /* search for correct position in the list */
3286 i = 0;
3287 while (element_list[i] && element->MarkY > element_list[i]->MarkY)
3288 i++;
3291 * We have found the position where we have the first element that
3292 * has the same Y value or a lower Y value. Now move forward if
3293 * needed through the X values
3295 while (element_list[i]
3296 && element->MarkY == element_list[i]->MarkY
3297 && element->MarkX > element_list[i]->MarkX)
3298 i++;
3300 for (j = cnt - 1; j > i; j--)
3302 element_list[j] = element_list[j - 1];
3304 element_list[i] = element;
3307 END_LOOP;
3311 * Now that the elements are sorted by board position, we go through
3312 * and renumber them.
3316 * turn off the flag which requires unique names so it doesn't get
3317 * in our way. When we're done with the renumber we will have unique
3318 * names.
3320 unique = TEST_FLAG (UNIQUENAMEFLAG, PCB);
3321 CLEAR_FLAG (UNIQUENAMEFLAG, PCB);
3323 cnt_list = calloc (cnt_list_sz, sizeof (struct _cnt_list));
3324 for (i = 0; i < cnt; i++)
3326 /* If there is no refdes, maybe just spit out a warning */
3327 if (NAMEONPCB_NAME (element_list[i]))
3329 /* figure out the prefix */
3330 tmps = strdup (NAMEONPCB_NAME (element_list[i]));
3331 j = 0;
3332 while (tmps[j] && (tmps[j] < '0' || tmps[j] > '9')
3333 && tmps[j] != '?')
3334 j++;
3335 tmps[j] = '\0';
3337 /* check the counter for this prefix */
3338 for (j = 0;
3339 cnt_list[j].name && (strcmp (cnt_list[j].name, tmps) != 0)
3340 && j < cnt_list_sz; j++);
3342 /* grow the list if needed */
3343 if (j == cnt_list_sz)
3345 cnt_list_sz += 100;
3346 cnt_list = realloc (cnt_list, cnt_list_sz);
3347 if (cnt_list == NULL)
3349 fprintf (stderr, "realloc failed() in %s\n", __FUNCTION__);
3350 exit (1);
3352 /* zero out the memory that we added */
3353 for (tmpi = j; tmpi < cnt_list_sz; tmpi++)
3355 cnt_list[tmpi].name = NULL;
3356 cnt_list[tmpi].cnt = 0;
3361 * start a new counter if we don't have a counter for this
3362 * prefix
3364 if (!cnt_list[j].name)
3366 cnt_list[j].name = strdup (tmps);
3367 cnt_list[j].cnt = 0;
3371 * check to see if the new refdes is already used by a
3372 * locked element
3376 ok = 1;
3377 cnt_list[j].cnt++;
3378 free (tmps);
3380 /* space for the prefix plus 1 digit plus the '\0' */
3381 sz = strlen (cnt_list[j].name) + 2;
3383 /* and 1 more per extra digit needed to hold the number */
3384 tmpi = cnt_list[j].cnt;
3385 while (tmpi > 10)
3387 sz++;
3388 tmpi = tmpi / 10;
3390 tmps = malloc (sz * sizeof (char));
3391 sprintf (tmps, "%s%d", cnt_list[j].name, cnt_list[j].cnt);
3394 * now compare to the list of reserved (by locked
3395 * elements) names
3397 for (k = 0; k < lock_cnt; k++)
3399 if (strcmp
3400 (UNKNOWN (NAMEONPCB_NAME (locked_element_list[k])),
3401 tmps) == 0)
3403 ok = 0;
3404 break;
3409 while (!ok);
3411 if (strcmp (tmps, NAMEONPCB_NAME (element_list[i])) != 0)
3413 fprintf (out, "*RENAME* \"%s\" \"%s\"\n",
3414 NAMEONPCB_NAME (element_list[i]), tmps);
3416 /* add this rename to our table of renames so we can update the netlist */
3417 was[c_cnt] = strdup (NAMEONPCB_NAME (element_list[i]));
3418 is[c_cnt] = strdup (tmps);
3419 c_cnt++;
3421 AddObjectToChangeNameUndoList (ELEMENT_TYPE, NULL, NULL,
3422 element_list[i],
3423 NAMEONPCB_NAME (element_list
3424 [i]));
3426 ChangeObjectName (ELEMENT_TYPE, element_list[i], NULL, NULL,
3427 tmps);
3428 changed = True;
3430 /* we don't free tmps in this case because it is used */
3432 else
3433 free (tmps);
3435 else
3437 fprintf (out, "*WARN* Element at (%d,%d) has no name.\n",
3438 element_list[i]->MarkX, element_list[i]->MarkY);
3443 fclose (out);
3445 /* restore the unique flag setting */
3446 if (unique)
3447 SET_FLAG (UNIQUENAMEFLAG, PCB);
3449 if (changed)
3452 /* update the netlist */
3453 AddNetlistLibToUndoList (&(PCB->NetlistLib));
3455 /* iterate over each net */
3456 for (i = 0; i < PCB->NetlistLib.MenuN; i++)
3459 /* iterate over each pin on the net */
3460 for (j = 0; j < PCB->NetlistLib.Menu[i].EntryN; j++)
3463 /* figure out the pin number part from strings like U3-21 */
3464 tmps = strdup (PCB->NetlistLib.Menu[i].Entry[j].ListEntry);
3465 for (k = 0; tmps[k] && tmps[k] != '-'; k++);
3466 tmps[k] = '\0';
3467 pin = tmps + k + 1;
3469 /* iterate over the list of changed reference designators */
3470 for (k = 0; k < c_cnt; k++)
3473 * if the pin needs to change, change it and quit
3474 * searching in the list.
3476 if (strcmp (tmps, was[k]) == 0)
3478 free (PCB->NetlistLib.Menu[i].Entry[j].ListEntry);
3479 PCB->NetlistLib.Menu[i].Entry[j].ListEntry =
3480 malloc ((strlen (is[k]) + strlen (pin) +
3481 2) * sizeof (char));
3482 sprintf (PCB->NetlistLib.Menu[i].Entry[j].ListEntry,
3483 "%s-%s", is[k], pin);
3484 k = c_cnt;
3488 free (tmps);
3491 for (k = 0; k < c_cnt; k++)
3493 free (was[k]);
3494 free (is[k]);
3497 NetlistChanged (0);
3498 IncrementUndoSerialNumber ();
3499 SetChangedFlag (True);
3502 free (locked_element_list);
3503 free (element_list);
3504 free (cnt_list);
3505 return 0;
3509 /* --------------------------------------------------------------------------- */
3511 static const char ripup_syntax[] = "RipUp(All|Selected|Element)";
3513 static const char ripup_help[] =
3514 "Ripup auto-routed tracks, or convert an element to parts.";
3516 /* %start-doc actions RipUp
3518 @table @code
3520 @item All
3521 Removes all lines and vias which were created by the autorouter.
3523 @item Selected
3524 Removes all selected lines and vias which were created by the
3525 autorouter.
3527 @item Element
3528 Converts the element under the cursor to parts (vias and lines). Note
3529 that this uses the highest numbered paste buffer.
3531 @end table
3533 %end-doc */
3535 static int
3536 ActionRipUp (int argc, char **argv, int x, int y)
3538 char *function = ARG (0);
3539 Boolean changed = False;
3541 if (function)
3543 HideCrosshair (True);
3544 switch (GetFunctionID (function))
3546 case F_All:
3547 ALLLINE_LOOP (PCB->Data);
3549 if (TEST_FLAG (AUTOFLAG, line) && !TEST_FLAG (LOCKFLAG, line))
3551 RemoveObject (LINE_TYPE, layer, line, line);
3552 changed = True;
3555 ENDALL_LOOP;
3556 VIA_LOOP (PCB->Data);
3558 if (TEST_FLAG (AUTOFLAG, via) && !TEST_FLAG (LOCKFLAG, via))
3560 RemoveObject (VIA_TYPE, via, via, via);
3561 changed = True;
3564 END_LOOP;
3566 if (changed)
3568 IncrementUndoSerialNumber ();
3569 SetChangedFlag (True);
3571 break;
3572 case F_Selected:
3573 VISIBLELINE_LOOP (PCB->Data);
3575 if (TEST_FLAGS (AUTOFLAG | SELECTEDFLAG, line)
3576 && !TEST_FLAG (LOCKFLAG, line))
3578 RemoveObject (LINE_TYPE, layer, line, line);
3579 changed = True;
3582 ENDALL_LOOP;
3583 if (PCB->ViaOn)
3584 VIA_LOOP (PCB->Data);
3586 if (TEST_FLAGS (AUTOFLAG | SELECTEDFLAG, via)
3587 && !TEST_FLAG (LOCKFLAG, via))
3589 RemoveObject (VIA_TYPE, via, via, via);
3590 changed = True;
3593 END_LOOP;
3594 if (changed)
3596 IncrementUndoSerialNumber ();
3597 SetChangedFlag (True);
3599 break;
3600 case F_Element:
3602 void *ptr1, *ptr2, *ptr3;
3604 if (SearchScreen (Crosshair.X, Crosshair.Y, ELEMENT_TYPE,
3605 &ptr1, &ptr2, &ptr3) != NO_TYPE)
3607 Note.Buffer = Settings.BufferNumber;
3608 SetBufferNumber (MAX_BUFFER - 1);
3609 ClearBuffer (PASTEBUFFER);
3610 CopyObjectToBuffer (PASTEBUFFER->Data, PCB->Data,
3611 ELEMENT_TYPE, ptr1, ptr2, ptr3);
3612 SmashBufferElement (PASTEBUFFER);
3613 PASTEBUFFER->X = 0;
3614 PASTEBUFFER->Y = 0;
3615 SaveUndoSerialNumber ();
3616 EraseObject (ELEMENT_TYPE, ptr1, ptr1);
3617 MoveObjectToRemoveUndoList (ELEMENT_TYPE, ptr1, ptr2, ptr3);
3618 RestoreUndoSerialNumber ();
3619 CopyPastebufferToLayout (0, 0);
3620 SetBufferNumber (Note.Buffer);
3621 SetChangedFlag (True);
3624 break;
3626 RestoreCrosshair (True);
3628 return 0;
3631 /* --------------------------------------------------------------------------- */
3633 static const char addrats_syntax[] = "AddRats(AllRats|SelectedRats|Close)";
3635 static const char addrats_help[] = "Add one or more rat lines to the board.";
3637 /* %start-doc actions AddRats
3639 @table @code
3641 @item AllRats
3642 Create rat lines for all loaded nets that aren't already connected on
3643 with copper.
3645 @item SelectedRats
3646 Similarly, but only add rat lines for nets connected to selected pins
3647 and pads.
3649 @item Close
3650 Selects the shortest unselected rat on the board.
3652 @end table
3654 %end-doc */
3656 static int
3657 ActionAddRats (int argc, char **argv, int x, int y)
3659 char *function = ARG (0);
3660 RatTypePtr shorty;
3661 float len, small;
3663 if (function)
3665 if (Settings.RatWarn)
3666 ClearWarnings ();
3667 HideCrosshair (True);
3668 switch (GetFunctionID (function))
3670 case F_AllRats:
3671 if (AddAllRats (False, NULL))
3672 SetChangedFlag (True);
3673 break;
3674 case F_SelectedRats:
3675 case F_Selected:
3676 if (AddAllRats (True, NULL))
3677 SetChangedFlag (True);
3678 break;
3679 case F_Close:
3680 small = SQUARE (MAX_COORD);
3681 shorty = NULL;
3682 RAT_LOOP (PCB->Data);
3684 if (TEST_FLAG (SELECTEDFLAG, line))
3685 continue;
3686 len = SQUARE (line->Point1.X - line->Point2.X) +
3687 SQUARE (line->Point1.Y - line->Point2.Y);
3688 if (len < small)
3690 small = len;
3691 shorty = line;
3694 END_LOOP;
3695 if (shorty)
3697 AddObjectToFlagUndoList (RATLINE_TYPE, shorty, shorty, shorty);
3698 SET_FLAG (SELECTEDFLAG, shorty);
3699 DrawRat (shorty, 0);
3700 Draw ();
3701 CenterDisplay ((shorty->Point2.X + shorty->Point1.X) / 2,
3702 (shorty->Point2.Y + shorty->Point1.Y) / 2,
3703 False);
3705 break;
3707 RestoreCrosshair (True);
3709 return 0;
3712 /* --------------------------------------------------------------------------- */
3714 static const char delete_syntax[] =
3715 "Delete(Object|Selected)\n"
3716 "Delete(AllRats|SelectedRats)"
3719 static const char delete_help[] = "Delete stuff.";
3721 /* %start-doc actions Delete
3723 %end-doc */
3725 static int
3726 ActionDelete (int argc, char **argv, int x, int y)
3728 char *function = ARG (0);
3729 int id = GetFunctionID (function);
3731 Note.X = Crosshair.X;
3732 Note.Y = Crosshair.Y;
3734 if (id == -1) /* no arg */
3736 if (RemoveSelected() == False)
3737 id = F_Object;
3740 HideCrosshair (True);
3741 switch (id)
3743 case F_Object:
3744 SaveMode();
3745 SetMode(REMOVE_MODE);
3746 NotifyMode();
3747 RestoreMode();
3748 break;
3749 case F_Selected:
3750 RemoveSelected();
3751 break;
3752 case F_AllRats:
3753 if (DeleteRats (False))
3754 SetChangedFlag (True);
3755 break;
3756 case F_SelectedRats:
3757 if (DeleteRats (True))
3758 SetChangedFlag (True);
3759 break;
3762 RestoreCrosshair (True);
3763 return 0;
3766 /* --------------------------------------------------------------------------- */
3768 static const char deleterats_syntax[] =
3769 "DeleteRats(AllRats|Selected|SelectedRats)";
3771 static const char deleterats_help[] = "Delete rat lines.";
3773 /* %start-doc actions DeleteRats
3775 %end-doc */
3777 static int
3778 ActionDeleteRats (int argc, char **argv, int x, int y)
3780 char *function = ARG (0);
3781 if (function)
3783 if (Settings.RatWarn)
3784 ClearWarnings ();
3785 HideCrosshair (True);
3786 switch (GetFunctionID (function))
3788 case F_AllRats:
3789 if (DeleteRats (False))
3790 SetChangedFlag (True);
3791 break;
3792 case F_SelectedRats:
3793 case F_Selected:
3794 if (DeleteRats (True))
3795 SetChangedFlag (True);
3796 break;
3798 RestoreCrosshair (True);
3800 return 0;
3803 /* --------------------------------------------------------------------------- */
3805 static const char autoplace_syntax[] = "AutoPlaceSelected()";
3807 static const char autoplace_help[] = "Auto-place selected components.";
3809 /* %start-doc actions AutoPlaceSelected
3811 Attempts to re-arrange the selected components such that the nets
3812 connecting them are minimized. Note that you cannot undo this.
3814 %end-doc */
3816 static int
3817 ActionAutoPlaceSelected (int argc, char **argv, int x, int y)
3819 hid_action("Busy");
3820 if (gui->confirm_dialog (_("Auto-placement can NOT be undone.\n"
3821 "Do you want to continue anyway?\n"), 0))
3823 HideCrosshair (True);
3824 if (AutoPlaceSelected ())
3825 SetChangedFlag (True);
3826 RestoreCrosshair (True);
3828 return 0;
3831 /* --------------------------------------------------------------------------- */
3833 static const char autoroute_syntax[] = "AutoRoute(AllRats|SelectedRats)";
3835 static const char autoroute_help[] = "Auto-route some or all rat lines.";
3837 /* %start-doc actions AutoRoute
3839 @table @code
3841 @item AllRats
3842 Attempt to autoroute all rats.
3844 @item SelectedRats
3845 Attempt to autoroute the selected rats.
3847 @end table
3849 Before autorouting, it's important to set up a few things. First,
3850 make sure any layers you aren't using are disabled, else the
3851 autorouter may use them. Next, make sure the current line and via
3852 styles are set accordingly. Last, make sure "new lines clear
3853 polygons" is set, in case you eventually want to add a copper pour.
3855 Autorouting takes a while. During this time, the program may not be
3856 responsive.
3858 %end-doc */
3860 static int
3861 ActionAutoRoute (int argc, char **argv, int x, int y)
3863 char *function = ARG (0);
3864 hid_action("Busy");
3865 if (function) /* one parameter */
3867 HideCrosshair (True);
3868 switch (GetFunctionID (function))
3870 case F_AllRats:
3871 if (AutoRoute (False))
3872 SetChangedFlag (True);
3873 break;
3874 case F_SelectedRats:
3875 case F_Selected:
3876 if (AutoRoute (True))
3877 SetChangedFlag (True);
3878 break;
3880 RestoreCrosshair (True);
3882 return 0;
3885 /* --------------------------------------------------------------------------- */
3887 static const char markcrosshair_syntax[] =
3888 "MarkCrosshair()\n" "MarkCrosshair(Center)";
3890 static const char markcrosshair_help[] = "Set/Reset the Crosshair mark";
3892 /* %start-doc actions MarkCrosshair
3894 The ``mark'' is a small X-shaped target on the display which is
3895 treated like a second origin (the normal origin is the upper let
3896 corner of the board). The GUI will display a second set of
3897 coordinates for this mark, which tells you how far you are from it.
3899 If no argument is given, the mark is toggled - disabled if it was
3900 enabled, or enabled at the current cursor position of disabled. If
3901 the @code{Center} argument is given, the mark is moved to the current
3902 cursor location.
3904 %end-doc */
3906 static int
3907 ActionMarkCrosshair (int argc, char **argv, int x, int y)
3909 char *function = ARG (0);
3910 if (!function || !*function)
3912 if (Marked.status)
3914 DrawMark (True);
3915 Marked.status = False;
3917 else
3919 Marked.status = True;
3920 Marked.X = Crosshair.X;
3921 Marked.Y = Crosshair.Y;
3922 DrawMark (False);
3925 else if (GetFunctionID (function) == F_Center)
3927 DrawMark (True);
3928 Marked.status = True;
3929 Marked.X = Crosshair.X;
3930 Marked.Y = Crosshair.Y;
3931 DrawMark (False);
3933 return 0;
3936 /* --------------------------------------------------------------------------- */
3938 static const char changesize_syntax[] =
3939 "ChangeSize(Object, delta)\n"
3940 "ChangeSize(SelectedObjects|Selected, delta)\n"
3941 "ChangeSize(SelectedLines|SelectedPins|SelectedVias, delta)\n"
3942 "ChangeSize(SelectedPads|SelectedTexts|SelectedNames, delta)\n"
3943 "ChangeSize(SelectedElements, delta)";
3945 static const char changesize_help[] = "Changes the size of objects.";
3947 /* %start-doc actions ChangeSize
3949 For lines and arcs, this changes the width. For pins and vias, this
3950 changes the overall diameter of the copper annulus. For pads, this
3951 changes the width and, indirectly, the length. For texts and names,
3952 this changes the scaling factor. For elements, this changes the width
3953 of the silk layer lines and arcs for this element.
3955 %end-doc */
3957 static int
3958 ActionChangeSize (int argc, char **argv, int x, int y)
3960 char *function = ARG (0);
3961 char *delta = ARG (1);
3962 char *units = ARG (2);
3963 Boolean r; /* indicates if absolute size is given */
3964 float value;
3966 if (function && delta)
3968 value = GetValue (delta, units, &r);
3969 HideCrosshair (True);
3970 switch (GetFunctionID (function))
3972 case F_Object:
3974 int type;
3975 void *ptr1, *ptr2, *ptr3;
3977 if ((type =
3978 SearchScreen (Crosshair.X, Crosshair.Y, CHANGESIZE_TYPES,
3979 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
3980 if (TEST_FLAG (LOCKFLAG, (PinTypePtr) ptr2))
3981 Message (_("Sorry, the object is locked\n"));
3982 if (ChangeObjectSize (type, ptr1, ptr2, ptr3, value, r))
3983 SetChangedFlag (True);
3984 break;
3987 case F_SelectedVias:
3988 if (ChangeSelectedSize (VIA_TYPE, value, r))
3989 SetChangedFlag (True);
3990 break;
3992 case F_SelectedPins:
3993 if (ChangeSelectedSize (PIN_TYPE, value, r))
3994 SetChangedFlag (True);
3995 break;
3997 case F_SelectedPads:
3998 if (ChangeSelectedSize (PAD_TYPE, value, r))
3999 SetChangedFlag (True);
4000 break;
4002 case F_SelectedArcs:
4003 if (ChangeSelectedSize (ARC_TYPE, value, r))
4004 SetChangedFlag (True);
4005 break;
4007 case F_SelectedLines:
4008 if (ChangeSelectedSize (LINE_TYPE, value, r))
4009 SetChangedFlag (True);
4010 break;
4012 case F_SelectedTexts:
4013 if (ChangeSelectedSize (TEXT_TYPE, value, r))
4014 SetChangedFlag (True);
4015 break;
4017 case F_SelectedNames:
4018 if (ChangeSelectedSize (ELEMENTNAME_TYPE, value, r))
4019 SetChangedFlag (True);
4020 break;
4022 case F_SelectedElements:
4023 if (ChangeSelectedSize (ELEMENT_TYPE, value, r))
4024 SetChangedFlag (True);
4025 break;
4027 case F_Selected:
4028 case F_SelectedObjects:
4029 if (ChangeSelectedSize (CHANGESIZE_TYPES, value, r))
4030 SetChangedFlag (True);
4031 break;
4033 RestoreCrosshair (True);
4035 return 0;
4038 /* --------------------------------------------------------------------------- */
4040 static const char changedrillsize_syntax[] =
4041 "ChangeDrillSize(Object, delta)\n"
4042 "ChangeDrillSize(SelectedPins|SelectedVias|Selected|SelectedObjects, delta)";
4044 static const char changedrillsize_help[] =
4045 "Changes the drilling hole size of objects.";
4047 /* %start-doc actions ChangeDrillSize
4049 %end-doc */
4051 static int
4052 ActionChange2ndSize (int argc, char **argv, int x, int y)
4054 char *function = ARG (0);
4055 char *delta = ARG (1);
4056 char *units = ARG (2);
4057 Boolean r;
4058 float value;
4060 if (function && delta)
4062 value = GetValue (delta, units, &r);
4063 HideCrosshair (True);
4064 switch (GetFunctionID (function))
4066 case F_Object:
4068 int type;
4069 void *ptr1, *ptr2, *ptr3;
4071 gui->get_coords ("Select an Object", &x, &y);
4072 if ((type =
4073 SearchScreen (x, y, CHANGE2NDSIZE_TYPES,
4074 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4075 if (ChangeObject2ndSize
4076 (type, ptr1, ptr2, ptr3, value, r, True))
4077 SetChangedFlag (True);
4078 break;
4081 case F_SelectedVias:
4082 if (ChangeSelected2ndSize (VIA_TYPE, value, r))
4083 SetChangedFlag (True);
4084 break;
4086 case F_SelectedPins:
4087 if (ChangeSelected2ndSize (PIN_TYPE, value, r))
4088 SetChangedFlag (True);
4089 break;
4090 case F_Selected:
4091 case F_SelectedObjects:
4092 if (ChangeSelected2ndSize (PIN_TYPES, value, r))
4093 SetChangedFlag (True);
4094 break;
4096 RestoreCrosshair (True);
4098 return 0;
4101 /* --------------------------------------------------------------------------- */
4103 static const char changeclearsize_syntax[] =
4104 "ChangeClearSize(Object, delta)\n"
4105 "ChangeClearSize(SelectedPins|SelectedPads|SelectedVias, delta)\n"
4106 "ChangeClearSize(SelectedLines|SelectedArcs, delta\n"
4107 "ChangeClearSize(Selected|SelectedObjects, delta)";
4109 static const char changeclearsize_help[] =
4110 "Changes the clearance size of objects.";
4112 /* %start-doc actions ChangeClearSize
4114 If the solder mask is currently showing, this action changes the
4115 solder mask clearance. If the mask is not showing, this action
4116 changes the polygon clearance.
4118 %end-doc */
4120 static int
4121 ActionChangeClearSize (int argc, char **argv, int x, int y)
4123 char *function = ARG (0);
4124 char *delta = ARG (1);
4125 char *units = ARG (2);
4126 Boolean r;
4127 float value;
4129 if (function && delta)
4131 value = 2 * GetValue (delta, units, &r);
4132 HideCrosshair (True);
4133 switch (GetFunctionID (function))
4135 case F_Object:
4137 int type;
4138 void *ptr1, *ptr2, *ptr3;
4140 gui->get_coords ("Select an Object", &x, &y);
4141 if ((type =
4142 SearchScreen (x, y,
4143 CHANGECLEARSIZE_TYPES, &ptr1, &ptr2,
4144 &ptr3)) != NO_TYPE)
4145 if (ChangeObjectClearSize (type, ptr1, ptr2, ptr3, value, r))
4146 SetChangedFlag (True);
4147 break;
4149 case F_SelectedVias:
4150 if (ChangeSelectedClearSize (VIA_TYPE, value, r))
4151 SetChangedFlag (True);
4152 break;
4153 case F_SelectedPads:
4154 if (ChangeSelectedClearSize (PAD_TYPE, value, r))
4155 SetChangedFlag (True);
4156 break;
4157 case F_SelectedPins:
4158 if (ChangeSelectedClearSize (PIN_TYPE, value, r))
4159 SetChangedFlag (True);
4160 break;
4161 case F_SelectedLines:
4162 if (ChangeSelectedClearSize (LINE_TYPE, value, r))
4163 SetChangedFlag (True);
4164 break;
4165 case F_SelectedArcs:
4166 if (ChangeSelectedClearSize (ARC_TYPE, value, r))
4167 SetChangedFlag (True);
4168 break;
4169 case F_Selected:
4170 case F_SelectedObjects:
4171 if (ChangeSelectedClearSize (CHANGECLEARSIZE_TYPES, value, r))
4172 SetChangedFlag (True);
4173 break;
4175 RestoreCrosshair (True);
4177 return 0;
4180 /* --------------------------------------------------------------------------- */
4182 static const char minmaskgap_syntax[] =
4183 "MinMaskGap(delta)\n" "MinMaskGap(Selected, delta)";
4185 static const char minmaskgap_help[] =
4186 "Ensures the mask is a minimum distance from pins and pads.";
4188 /* %start-doc actions MinMaskGap
4190 Checks all specified pins and/or pads, and increases the mask if
4191 needed to ensure a minimum distance between the pin or pad edge and
4192 the mask edge.
4194 %end-doc */
4196 static int
4197 ActionMinMaskGap (int argc, char **argv, int x, int y)
4199 char *function = ARG (0);
4200 char *delta = ARG (1);
4201 char *units = ARG (2);
4202 Boolean r;
4203 int value;
4204 int flags;
4206 if (!function)
4207 return 1;
4208 if (strcasecmp (function, "Selected") == 0)
4209 flags = SELECTEDFLAG;
4210 else
4212 units = delta;
4213 delta = function;
4214 flags = 0;
4216 value = 2 * GetValue (delta, units, &r);
4218 HideCrosshair (True);
4219 SaveUndoSerialNumber ();
4220 ELEMENT_LOOP (PCB->Data);
4222 PIN_LOOP (element);
4224 if (!TEST_FLAGS (flags, pin))
4225 continue;
4226 if (pin->Mask < pin->Thickness + value)
4228 ChangeObjectMaskSize (PIN_TYPE, element, pin, 0,
4229 pin->Thickness + value, 1);
4230 RestoreUndoSerialNumber ();
4233 END_LOOP;
4234 PAD_LOOP (element);
4236 if (!TEST_FLAGS (flags, pad))
4237 continue;
4238 if (pad->Mask < pad->Thickness + value)
4240 ChangeObjectMaskSize (PAD_TYPE, element, pad, 0,
4241 pad->Thickness + value, 1);
4242 RestoreUndoSerialNumber ();
4245 END_LOOP;
4247 END_LOOP;
4248 VIA_LOOP (PCB->Data);
4250 if (!TEST_FLAGS (flags, via))
4251 continue;
4252 if (via->Mask && via->Mask < via->Thickness + value)
4254 ChangeObjectMaskSize (VIA_TYPE, via, 0, 0, via->Thickness + value, 1);
4255 RestoreUndoSerialNumber ();
4258 END_LOOP;
4259 RestoreUndoSerialNumber ();
4260 IncrementUndoSerialNumber ();
4261 return 0;
4264 /* --------------------------------------------------------------------------- */
4266 static const char mincleargap_syntax[] =
4267 "MinClearGap(delta)\n" "MinClearGap(Selected, delta)";
4269 static const char mincleargap_help[] =
4270 "Ensures that polygons are a minimum distance from objects.";
4272 /* %start-doc actions MinClearGap
4274 Checks all specified objects, and increases the polygon clearance if
4275 needed to ensure a minimum distance between their edges and the
4276 polygon edges.
4278 %end-doc */
4280 static int
4281 ActionMinClearGap (int argc, char **argv, int x, int y)
4283 char *function = ARG (0);
4284 char *delta = ARG (1);
4285 char *units = ARG (2);
4286 Boolean r;
4287 int value;
4288 int flags;
4290 if (!function)
4291 return 1;
4292 if (strcasecmp (function, "Selected") == 0)
4293 flags = SELECTEDFLAG;
4294 else
4296 units = delta;
4297 delta = function;
4298 flags = 0;
4300 value = 2 * GetValue (delta, units, &r);
4302 HideCrosshair (True);
4303 SaveUndoSerialNumber ();
4304 ELEMENT_LOOP (PCB->Data);
4306 PIN_LOOP (element);
4308 if (!TEST_FLAGS (flags, pin))
4309 continue;
4310 if (pin->Clearance < value)
4312 ChangeObjectClearSize (PIN_TYPE, element, pin, 0,
4313 value, 1);
4314 RestoreUndoSerialNumber ();
4317 END_LOOP;
4318 PAD_LOOP (element);
4320 if (!TEST_FLAGS (flags, pad))
4321 continue;
4322 if (pad->Clearance < value)
4324 ChangeObjectClearSize (PAD_TYPE, element, pad, 0,
4325 value, 1);
4326 RestoreUndoSerialNumber ();
4329 END_LOOP;
4331 END_LOOP;
4332 VIA_LOOP (PCB->Data);
4334 if (!TEST_FLAGS (flags, via))
4335 continue;
4336 if (via->Clearance < value)
4338 ChangeObjectClearSize (VIA_TYPE, via, 0, 0, value, 1);
4339 RestoreUndoSerialNumber ();
4342 END_LOOP;
4343 ALLLINE_LOOP (PCB->Data);
4345 if (!TEST_FLAGS (flags, line))
4346 continue;
4347 if (line->Clearance < value)
4349 ChangeObjectClearSize (LINE_TYPE, layer, line, 0, value, 1);
4350 RestoreUndoSerialNumber ();
4353 ENDALL_LOOP;
4354 ALLARC_LOOP (PCB->Data);
4356 if (!TEST_FLAGS (flags, arc))
4357 continue;
4358 if (arc->Clearance < value)
4360 ChangeObjectClearSize (ARC_TYPE, layer, arc, 0, value, 1);
4361 RestoreUndoSerialNumber ();
4364 ENDALL_LOOP;
4365 RestoreUndoSerialNumber ();
4366 IncrementUndoSerialNumber ();
4367 return 0;
4370 /* --------------------------------------------------------------------------- */
4372 static const char changepinname_syntax[] =
4373 "ChangePinName(ElementName,PinNumber,PinName)";
4375 static const char changepinname_help[] =
4376 "Sets the name of a specific pin on a specific element.";
4378 /* %start-doc actions ChangePinName
4380 This can be especially useful for annotating pin names from a
4381 schematic to the layout without requiring knowledge of the pcb file
4382 format.
4384 @example
4385 ChangePinName(U3, 7, VCC)
4386 @end example
4388 %end-doc */
4390 static int
4391 ActionChangePinName (int argc, char **argv, int x, int y)
4393 int changed = 0;
4394 char *refdes, *pinnum, *pinname;
4396 if (argc != 3)
4398 AFAIL (changepinname);
4401 refdes = argv[0];
4402 pinnum = argv[1];
4403 pinname = argv[2];
4405 ELEMENT_LOOP (PCB->Data);
4407 if (NSTRCMP (refdes, NAMEONPCB_NAME (element)) == 0)
4409 PIN_LOOP (element);
4411 if (NSTRCMP (pinnum, pin->Number) == 0)
4413 AddObjectToChangeNameUndoList (PIN_TYPE, NULL, NULL,
4414 pin, pin->Name);
4416 * Note: we can't free() pin->Name first because
4417 * it is used in the undo list
4419 pin->Name = strdup (pinname);
4420 SetChangedFlag (True);
4421 changed = 1;
4424 END_LOOP;
4426 PAD_LOOP (element);
4428 if (NSTRCMP (pinnum, pad->Number) == 0)
4430 AddObjectToChangeNameUndoList (PAD_TYPE, NULL, NULL,
4431 pad, pad->Name);
4433 * Note: we can't free() pad->Name first because
4434 * it is used in the undo list
4436 pad->Name = strdup (pinname);
4437 SetChangedFlag (True);
4438 changed = 1;
4441 END_LOOP;
4444 END_LOOP;
4446 * done with our action so increment the undo # if we actually
4447 * changed anything
4449 if (changed)
4451 if (defer_updates)
4452 defer_needs_update = 1;
4453 else
4455 IncrementUndoSerialNumber ();
4456 gui->invalidate_all ();
4460 return 0;
4463 /* --------------------------------------------------------------------------- */
4465 static const char changename_syntax[] =
4466 "ChangeName(Object)\n" "ChangeName(Layout|Layer)";
4468 static const char changename_help[] = "Sets the name of objects.";
4470 /* %start-doc actions ChangeName
4472 @table @code
4474 @item Object
4475 Changes the name of the element under the cursor.
4477 @item Layout
4478 Changes the name of the layout. This is printed on the fab drawings.
4480 @item Layer
4481 Changes the name of the currently active layer.
4483 @end table
4485 %end-doc */
4488 ActionChangeName (int argc, char **argv, int x, int y)
4490 char *function = ARG (0);
4491 char *name;
4493 if (function)
4495 HideCrosshair (True);
4496 switch (GetFunctionID (function))
4498 /* change the name of an object */
4499 case F_Object:
4501 int type;
4502 void *ptr1, *ptr2, *ptr3;
4504 gui->get_coords ("Select an Object", &x, &y);
4505 if ((type =
4506 SearchScreen (x, y, CHANGENAME_TYPES,
4507 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4509 SaveUndoSerialNumber ();
4510 if (QueryInputAndChangeObjectName (type, ptr1, ptr2, ptr3))
4512 SetChangedFlag (True);
4513 if (type == ELEMENT_TYPE)
4515 RubberbandTypePtr ptr;
4516 int i;
4518 RestoreUndoSerialNumber ();
4519 Crosshair.AttachedObject.RubberbandN = 0;
4520 LookupRatLines (type, ptr1, ptr2, ptr3);
4521 ptr = Crosshair.AttachedObject.Rubberband;
4522 for (i = 0; i < Crosshair.AttachedObject.RubberbandN;
4523 i++, ptr++)
4525 if (PCB->RatOn)
4526 EraseRat ((RatTypePtr) ptr->Line);
4527 MoveObjectToRemoveUndoList (RATLINE_TYPE,
4528 ptr->Line, ptr->Line,
4529 ptr->Line);
4531 IncrementUndoSerialNumber ();
4532 Draw ();
4536 break;
4539 /* change the layout's name */
4540 case F_Layout:
4541 name =
4542 gui->prompt_for (_("Enter the layout name:"), EMPTY (PCB->Name));
4543 if (name && ChangeLayoutName (name)) /* XXX memory leak */
4544 SetChangedFlag (True);
4545 break;
4547 /* change the name of the active layer */
4548 case F_Layer:
4549 name = gui->prompt_for (_("Enter the layer name:"),
4550 EMPTY (CURRENT->Name));
4551 if (name && ChangeLayerName (CURRENT, name)) /* XXX memory leak */
4552 SetChangedFlag (True);
4553 break;
4555 RestoreCrosshair (True);
4557 return 0;
4561 /* --------------------------------------------------------------------------- */
4563 static const char morphpolygon_syntax[] = "MorphPolygon(Object|Selected)";
4565 static const char morphpolygon_help[] =
4566 "Converts dead polygon islands into separate polygons.";
4568 /* %start-doc actions MorphPolygon
4570 If a polygon is divided into unconnected "islands", you can use
4571 this command to convert the otherwise disappeared islands into
4572 separate polygons. Be sure the cursor is over a portion of the
4573 polygon that remains visible. Very small islands that may flake
4574 off are automatically deleted.
4576 %end-doc */
4578 static int
4579 ActionMorphPolygon (int argc, char **argv, int x, int y)
4581 char *function = ARG (0);
4582 if (function)
4584 HideCrosshair (True);
4585 switch (GetFunctionID (function))
4587 case F_Object:
4589 int type;
4590 void *ptr1, *ptr2, *ptr3;
4592 gui->get_coords ("Select an Object", &x, &y);
4593 if ((type = SearchScreen (x, y, POLYGON_TYPE,
4594 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4596 MorphPolygon ((LayerType *) ptr1, (PolygonType *) ptr3);
4597 Draw ();
4598 IncrementUndoSerialNumber ();
4600 break;
4602 case F_Selected:
4603 case F_SelectedObjects:
4604 ALLPOLYGON_LOOP (PCB->Data);
4606 if (TEST_FLAG (SELECTEDFLAG, polygon))
4607 MorphPolygon (layer, polygon);
4609 ENDALL_LOOP;
4610 Draw ();
4611 IncrementUndoSerialNumber ();
4612 break;
4615 return 0;
4618 /* --------------------------------------------------------------------------- */
4620 static const char togglehidename_syntax[] =
4621 "ToggleHideName(Object|SelectedElements)";
4623 static const char togglehidename_help[] =
4624 "Toggles the visibility of element names.";
4626 /* %start-doc actions ToggleHideName
4628 If names are hidden you won't see them on the screen and they will not
4629 appear on the silk layer when you print the layout.
4631 %end-doc */
4633 static int
4634 ActionToggleHideName (int argc, char **argv, int x, int y)
4636 char *function = ARG (0);
4637 if (function && PCB->ElementOn)
4639 HideCrosshair (True);
4640 switch (GetFunctionID (function))
4642 case F_Object:
4644 int type;
4645 void *ptr1, *ptr2, *ptr3;
4647 gui->get_coords ("Select an Object", &x, &y);
4648 if ((type = SearchScreen (x, y, ELEMENT_TYPE,
4649 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4651 AddObjectToFlagUndoList (type, ptr1, ptr2, ptr3);
4652 EraseElementName ((ElementTypePtr) ptr2);
4653 TOGGLE_FLAG (HIDENAMEFLAG, (ElementTypePtr) ptr2);
4654 DrawElementName ((ElementTypePtr) ptr2, 0);
4655 Draw ();
4656 IncrementUndoSerialNumber ();
4658 break;
4660 case F_SelectedElements:
4661 case F_Selected:
4663 Boolean changed = False;
4664 ELEMENT_LOOP (PCB->Data);
4666 if ((TEST_FLAG (SELECTEDFLAG, element) ||
4667 TEST_FLAG (SELECTEDFLAG,
4668 &NAMEONPCB_TEXT (element)))
4669 && (FRONT (element) || PCB->InvisibleObjectsOn))
4671 AddObjectToFlagUndoList (ELEMENT_TYPE, element,
4672 element, element);
4673 EraseElementName (element);
4674 TOGGLE_FLAG (HIDENAMEFLAG, element);
4675 DrawElementName (element, 0);
4676 changed = True;
4679 END_LOOP;
4680 if (changed)
4682 Draw ();
4683 IncrementUndoSerialNumber ();
4687 RestoreCrosshair (True);
4689 return 0;
4692 /* --------------------------------------------------------------------------- */
4694 static const char changejoin_syntax[] =
4695 "ChangeJoin(ToggleObject|SelectedLines|SelectedArcs|Selected)";
4697 static const char changejoin_help[] =
4698 "Changes the join (clearance through polygons) of objects.";
4700 /* %start-doc actions ChangeJoin
4702 The join flag determines whether a line or arc, drawn to intersect a
4703 polygon, electrically connects to the polygon or not. When joined,
4704 the line/arc is simply drawn over the polygon, making an electrical
4705 connection. When not joined, a gap is drawn between the line and the
4706 polygon, insulating them from each other.
4708 %end-doc */
4710 static int
4711 ActionChangeJoin (int argc, char **argv, int x, int y)
4713 char *function = ARG (0);
4714 if (function)
4716 HideCrosshair (True);
4717 switch (GetFunctionID (function))
4719 case F_ToggleObject:
4720 case F_Object:
4722 int type;
4723 void *ptr1, *ptr2, *ptr3;
4725 gui->get_coords ("Select an Object", &x, &y);
4726 if ((type =
4727 SearchScreen (x, y, CHANGEJOIN_TYPES,
4728 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4729 if (ChangeObjectJoin (type, ptr1, ptr2, ptr3))
4730 SetChangedFlag (True);
4731 break;
4734 case F_SelectedLines:
4735 if (ChangeSelectedJoin (LINE_TYPE))
4736 SetChangedFlag (True);
4737 break;
4739 case F_SelectedArcs:
4740 if (ChangeSelectedJoin (ARC_TYPE))
4741 SetChangedFlag (True);
4742 break;
4744 case F_Selected:
4745 case F_SelectedObjects:
4746 if (ChangeSelectedJoin (CHANGEJOIN_TYPES))
4747 SetChangedFlag (True);
4748 break;
4750 RestoreCrosshair (True);
4752 return 0;
4755 /* --------------------------------------------------------------------------- */
4757 static const char changesquare_syntax[] =
4758 "ChangeSquare(ToggleObject)\n"
4759 "ChangeSquare(SelectedElements|SelectedPins)\n"
4760 "ChangeSquare(Selected|SelectedObjects)";
4762 static const char changesquare_help[] =
4763 "Changes the square flag of pins and pads.";
4765 /* %start-doc actions ChangeSquare
4767 Note that @code{Pins} means both pins and pads.
4769 @pinshapes
4771 %end-doc */
4773 static int
4774 ActionChangeSquare (int argc, char **argv, int x, int y)
4776 char *function = ARG (0);
4777 if (function)
4779 HideCrosshair (True);
4780 switch (GetFunctionID (function))
4782 case F_ToggleObject:
4783 case F_Object:
4785 int type;
4786 void *ptr1, *ptr2, *ptr3;
4788 gui->get_coords ("Select an Object", &x, &y);
4789 if ((type =
4790 SearchScreen (x, y, CHANGESQUARE_TYPES,
4791 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4792 if (ChangeObjectSquare (type, ptr1, ptr2, ptr3))
4793 SetChangedFlag (True);
4794 break;
4797 case F_SelectedElements:
4798 if (ChangeSelectedSquare (ELEMENT_TYPE))
4799 SetChangedFlag (True);
4800 break;
4802 case F_SelectedPins:
4803 if (ChangeSelectedSquare (PIN_TYPE | PAD_TYPE))
4804 SetChangedFlag (True);
4805 break;
4807 case F_Selected:
4808 case F_SelectedObjects:
4809 if (ChangeSelectedSquare (PIN_TYPE | PAD_TYPE))
4810 SetChangedFlag (True);
4811 break;
4813 RestoreCrosshair (True);
4815 return 0;
4818 /* --------------------------------------------------------------------------- */
4820 static const char setsquare_syntax[] =
4821 "SetSquare(ToggleObject|SelectedElements|SelectedPins)";
4823 static const char setsquare_help[] = "sets the square-flag of objects.";
4825 /* %start-doc actions SetSquare
4827 Note that @code{Pins} means pins and pads.
4829 @pinshapes
4831 %end-doc */
4833 static int
4834 ActionSetSquare (int argc, char **argv, int x, int y)
4836 char *function = ARG (0);
4837 if (function && *function)
4839 /* HideCrosshair (True); */
4840 switch (GetFunctionID (function))
4842 case F_ToggleObject:
4843 case F_Object:
4845 int type;
4846 void *ptr1, *ptr2, *ptr3;
4848 gui->get_coords ("Select an object", &x, &y);
4849 if ((type =
4850 SearchScreen (x, y, CHANGESQUARE_TYPES,
4851 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4852 if (SetObjectSquare (type, ptr1, ptr2, ptr3))
4853 SetChangedFlag (True);
4854 break;
4857 case F_SelectedElements:
4858 if (SetSelectedSquare (ELEMENT_TYPE))
4859 SetChangedFlag (True);
4860 break;
4862 case F_SelectedPins:
4863 if (SetSelectedSquare (PIN_TYPE | PAD_TYPE))
4864 SetChangedFlag (True);
4865 break;
4867 case F_Selected:
4868 case F_SelectedObjects:
4869 if (SetSelectedSquare (PIN_TYPE | PAD_TYPE))
4870 SetChangedFlag (True);
4871 break;
4873 /* RestoreCrosshair (True); */
4875 return 0;
4878 /* --------------------------------------------------------------------------- */
4880 static const char clearsquare_syntax[] =
4881 "ClearSquare(ToggleObject|SelectedElements|SelectedPins)";
4883 static const char clearsquare_help[] =
4884 "Clears the square-flag of pins and pads.";
4886 /* %start-doc actions ClearSquare
4888 Note that @code{Pins} means pins and pads.
4890 @pinshapes
4892 %end-doc */
4894 static int
4895 ActionClearSquare (int argc, char **argv, int x, int y)
4897 char *function = ARG (0);
4898 if (function && *function)
4900 /* HideCrosshair (True); */
4901 switch (GetFunctionID (function))
4903 case F_ToggleObject:
4904 case F_Object:
4906 int type;
4907 void *ptr1, *ptr2, *ptr3;
4909 gui->get_coords ("Select an Object", &x, &y);
4910 if ((type =
4911 SearchScreen (x, y, CHANGESQUARE_TYPES,
4912 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4913 if (ClrObjectSquare (type, ptr1, ptr2, ptr3))
4914 SetChangedFlag (True);
4915 break;
4918 case F_SelectedElements:
4919 if (ClrSelectedSquare (ELEMENT_TYPE))
4920 SetChangedFlag (True);
4921 break;
4923 case F_SelectedPins:
4924 if (ClrSelectedSquare (PIN_TYPE | PAD_TYPE))
4925 SetChangedFlag (True);
4926 break;
4928 case F_Selected:
4929 case F_SelectedObjects:
4930 if (ClrSelectedSquare (PIN_TYPE | PAD_TYPE))
4931 SetChangedFlag (True);
4932 break;
4934 /* RestoreCrosshair (True); */
4936 return 0;
4939 /* --------------------------------------------------------------------------- */
4941 static const char changeoctagon_syntax[] =
4942 "ChangeOctagon(Object|ToggleObject|SelectedObjects|Selected)\n"
4943 "ChangeOctagon(SelectedElements|SelectedPins|SelectedVias)";
4945 static const char changeoctagon_help[] =
4946 "Changes the octagon-flag of pins and vias.";
4948 /* %start-doc actions ChangeOctagon
4950 @pinshapes
4952 %end-doc */
4954 static int
4955 ActionChangeOctagon (int argc, char **argv, int x, int y)
4957 char *function = ARG (0);
4958 if (function)
4960 /* HideCrosshair (True); */
4961 switch (GetFunctionID (function))
4963 case F_ToggleObject:
4964 case F_Object:
4966 int type;
4967 void *ptr1, *ptr2, *ptr3;
4969 gui->get_coords ("Select an Object", &x, &y);
4970 if ((type =
4971 SearchScreen (x, y, CHANGEOCTAGON_TYPES,
4972 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4973 if (ChangeObjectOctagon (type, ptr1, ptr2, ptr3))
4974 SetChangedFlag (True);
4975 break;
4978 case F_SelectedElements:
4979 if (ChangeSelectedOctagon (ELEMENT_TYPE))
4980 SetChangedFlag (True);
4981 break;
4983 case F_SelectedPins:
4984 if (ChangeSelectedOctagon (PIN_TYPE))
4985 SetChangedFlag (True);
4986 break;
4988 case F_SelectedVias:
4989 if (ChangeSelectedOctagon (VIA_TYPE))
4990 SetChangedFlag (True);
4991 break;
4993 case F_Selected:
4994 case F_SelectedObjects:
4995 if (ChangeSelectedOctagon (PIN_TYPES))
4996 SetChangedFlag (True);
4997 break;
4999 /* RestoreCrosshair (True); */
5001 return 0;
5004 /* --------------------------------------------------------------------------- */
5006 static const char setoctagon_syntax[] =
5007 "SetOctagon(Object|ToggleObject|SelectedElements|Selected)";
5009 static const char setoctagon_help[] = "Sets the octagon-flag of objects.";
5011 /* %start-doc actions SetOctagon
5013 @pinshapes
5015 %end-doc */
5017 static int
5018 ActionSetOctagon (int argc, char **argv, int x, int y)
5020 char *function = ARG (0);
5021 if (function)
5023 /* HideCrosshair (True); */
5024 switch (GetFunctionID (function))
5026 case F_ToggleObject:
5027 case F_Object:
5029 int type;
5030 void *ptr1, *ptr2, *ptr3;
5032 gui->get_coords ("Select an object", &x, &y);
5033 if ((type =
5034 SearchScreen (x, y, CHANGEOCTAGON_TYPES,
5035 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5036 if (SetObjectOctagon (type, ptr1, ptr2, ptr3))
5037 SetChangedFlag (True);
5038 break;
5041 case F_SelectedElements:
5042 if (SetSelectedOctagon (ELEMENT_TYPE))
5043 SetChangedFlag (True);
5044 break;
5046 case F_SelectedPins:
5047 if (SetSelectedOctagon (PIN_TYPE))
5048 SetChangedFlag (True);
5049 break;
5051 case F_SelectedVias:
5052 if (SetSelectedOctagon (VIA_TYPE))
5053 SetChangedFlag (True);
5054 break;
5056 case F_Selected:
5057 case F_SelectedObjects:
5058 if (SetSelectedOctagon (PIN_TYPES))
5059 SetChangedFlag (True);
5060 break;
5062 /* RestoreCrosshair (True); */
5064 return 0;
5067 /* --------------------------------------------------------------------------- */
5069 static const char clearoctagon_syntax[] =
5070 "ClearOctagon(ToggleObject|Object|SelectedObjects|Selected)\n"
5071 "ClearOctagon(SelectedElements|SelectedPins|SelectedVias)";
5073 static const char clearoctagon_help[] =
5074 "Clears the octagon-flag of pins and vias.";
5076 /* %start-doc actions ClearOctagon
5078 @pinshapes
5080 %end-doc */
5082 static int
5083 ActionClearOctagon (int argc, char **argv, int x, int y)
5085 char *function = ARG (0);
5086 if (function)
5088 /* HideCrosshair (True); */
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 (Crosshair.X, Crosshair.Y, CHANGEOCTAGON_TYPES,
5100 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5101 if (ClrObjectOctagon (type, ptr1, ptr2, ptr3))
5102 SetChangedFlag (True);
5103 break;
5106 case F_SelectedElements:
5107 if (ClrSelectedOctagon (ELEMENT_TYPE))
5108 SetChangedFlag (True);
5109 break;
5111 case F_SelectedPins:
5112 if (ClrSelectedOctagon (PIN_TYPE))
5113 SetChangedFlag (True);
5114 break;
5116 case F_SelectedVias:
5117 if (ClrSelectedOctagon (VIA_TYPE))
5118 SetChangedFlag (True);
5119 break;
5121 case F_Selected:
5122 case F_SelectedObjects:
5123 if (ClrSelectedOctagon (PIN_TYPES))
5124 SetChangedFlag (True);
5125 break;
5127 /* RestoreCrosshair (True); */
5129 return 0;
5132 /* --------------------------------------------------------------------------- */
5134 static const char changehold_syntax[] =
5135 "ChangeHole(ToggleObject|Object|SelectedVias|Selected)";
5137 static const char changehold_help[] = "Changes the hole flag of objects.";
5139 /* %start-doc actions ChangeHole
5141 The "hole flag" of a via determines whether the via is a
5142 plated-through hole (not set), or an unplated hole (set).
5144 %end-doc */
5146 static int
5147 ActionChangeHole (int argc, char **argv, int x, int y)
5149 char *function = ARG (0);
5150 if (function)
5152 /* HideCrosshair (True); */
5153 switch (GetFunctionID (function))
5155 case F_ToggleObject:
5156 case F_Object:
5158 int type;
5159 void *ptr1, *ptr2, *ptr3;
5161 gui->get_coords ("Select an Object", &x, &y);
5162 if ((type = SearchScreen (x, y, VIA_TYPE,
5163 &ptr1, &ptr2, &ptr3)) != NO_TYPE
5164 && ChangeHole ((PinTypePtr) ptr3))
5165 IncrementUndoSerialNumber ();
5166 break;
5169 case F_SelectedVias:
5170 case F_Selected:
5171 if (ChangeSelectedHole ())
5172 SetChangedFlag (True);
5173 break;
5175 /* RestoreCrosshair (True); */
5177 return 0;
5180 /* --------------------------------------------------------------------------- */
5182 static const char changepaste_syntax[] =
5183 "ChangePaste(ToggleObject|Object|SelectedPads|Selected)";
5185 static const char changepaste_help[] = "Changes the no paste flag of objects.";
5187 /* %start-doc actions ChangePaste
5189 The "no paste flag" of a pad determines whether the solderpaste
5190 stencil will have an opening for the pad (no set) or if there wil be
5191 no solderpaste on the pad (set). This is used for things such as
5192 fiducial pads.
5194 %end-doc */
5196 static int
5197 ActionChangePaste (int argc, char **argv, int x, int y)
5199 char *function = ARG (0);
5200 if (function)
5202 /* HideCrosshair (True); */
5203 switch (GetFunctionID (function))
5205 case F_ToggleObject:
5206 case F_Object:
5208 int type;
5209 void *ptr1, *ptr2, *ptr3;
5211 gui->get_coords ("Select an Object", &x, &y);
5212 if ((type = SearchScreen (x, y, PAD_TYPE,
5213 &ptr1, &ptr2, &ptr3)) != NO_TYPE
5214 && ChangePaste ((PadTypePtr) ptr3))
5215 IncrementUndoSerialNumber ();
5216 break;
5219 case F_SelectedPads:
5220 case F_Selected:
5221 if (ChangeSelectedPaste ())
5222 SetChangedFlag (True);
5223 break;
5225 /* RestoreCrosshair (True); */
5227 return 0;
5230 /* --------------------------------------------------------------------------- */
5232 static const char select_syntax[] =
5233 "Select(ToggleObject)\n"
5234 "Select(All|Block|Connection)\n"
5235 "Select(ElementByName|ObjectByName|PadByName|PinByName)\n"
5236 "Select(ElementByName|ObjectByName|PadByName|PinByName, Name)\n"
5237 "Select(TextByName|ViaByName)\n"
5238 "Select(TextByName|ViaByName, Name)\n" "Select(Convert)";
5240 static const char select_help[] = "Toggles or sets the selection";
5242 /* %start-doc actions Select
5244 @table @code
5246 @item ElementByName
5247 @item ObjectByName
5248 @item PadByName
5249 @item PinByName
5250 @item TextByName
5251 @item ViaByName
5253 These all rely on having a regular expression parser built into
5254 @code{pcb}. If the name is not specified then the user is prompted
5255 for a pattern, and all objects that match the pattern and are of the
5256 type specified are selected.
5258 @item Object
5259 @item ToggleObject
5260 Selects the object under the cursor.
5262 @item Block
5263 Selects all objects in a rectangle indicated by the cursor.
5265 @item All
5266 Selects all objects on the board.
5268 @item Connection
5269 Selects all connections with the ``found'' flag set.
5271 @item Convert
5272 Converts the selected objects to an element. This uses the highest
5273 numbered paste buffer.
5275 @end table
5277 %end-doc */
5279 static int
5280 ActionSelect (int argc, char **argv, int x, int y)
5282 char *function = ARG (0);
5283 if (function)
5286 HideCrosshair (True);
5287 switch (GetFunctionID (function))
5289 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5290 int type;
5291 /* select objects by their names */
5292 case F_ElementByName:
5293 type = ELEMENT_TYPE;
5294 goto commonByName;
5295 case F_ObjectByName:
5296 type = ALL_TYPES;
5297 goto commonByName;
5298 case F_PadByName:
5299 type = PAD_TYPE;
5300 goto commonByName;
5301 case F_PinByName:
5302 type = PIN_TYPE;
5303 goto commonByName;
5304 case F_TextByName:
5305 type = TEXT_TYPE;
5306 goto commonByName;
5307 case F_ViaByName:
5308 type = VIA_TYPE;
5309 goto commonByName;
5311 commonByName:
5313 char *pattern = ARG (1);
5315 if (pattern
5316 || (pattern =
5317 gui->prompt_for (_("Enter pattern:"), "")) != NULL)
5319 if (SelectObjectByName (type, pattern, True))
5320 SetChangedFlag (True);
5321 if (ARG (1) == 0)
5322 free (pattern);
5324 break;
5326 #endif /* defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP) */
5328 /* select a single object */
5329 case F_ToggleObject:
5330 case F_Object:
5331 if (SelectObject ())
5332 SetChangedFlag (True);
5333 break;
5335 /* all objects in block */
5336 case F_Block:
5338 BoxType box;
5340 box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
5341 Crosshair.AttachedBox.Point2.X);
5342 box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
5343 Crosshair.AttachedBox.Point2.Y);
5344 box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
5345 Crosshair.AttachedBox.Point2.X);
5346 box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
5347 Crosshair.AttachedBox.Point2.Y);
5348 NotifyBlock ();
5349 if (Crosshair.AttachedBox.State == STATE_THIRD &&
5350 SelectBlock (&box, True))
5352 SetChangedFlag (True);
5353 Crosshair.AttachedBox.State = STATE_FIRST;
5355 break;
5358 /* select all visible objects */
5359 case F_All:
5361 BoxType box;
5363 box.X1 = -MAX_COORD;
5364 box.Y1 = -MAX_COORD;
5365 box.X2 = MAX_COORD;
5366 box.Y2 = MAX_COORD;
5367 if (SelectBlock (&box, True))
5368 SetChangedFlag (True);
5369 break;
5372 /* all found connections */
5373 case F_Connection:
5374 if (SelectConnection (True))
5376 IncrementUndoSerialNumber ();
5377 SetChangedFlag (True);
5379 break;
5381 case F_Convert:
5383 int x, y;
5384 Note.Buffer = Settings.BufferNumber;
5385 SetBufferNumber (MAX_BUFFER - 1);
5386 ClearBuffer (PASTEBUFFER);
5387 gui->get_coords ("Select the Element's Mark Location", &x, &y);
5388 x = GRIDFIT_X (x, PCB->Grid);
5389 y = GRIDFIT_Y (y, PCB->Grid);
5390 AddSelectedToBuffer (PASTEBUFFER, x, y, True);
5391 SaveUndoSerialNumber ();
5392 RemoveSelected ();
5393 ConvertBufferToElement (PASTEBUFFER);
5394 RestoreUndoSerialNumber ();
5395 CopyPastebufferToLayout (x, y);
5396 SetBufferNumber (Note.Buffer);
5398 break;
5400 default:
5401 RestoreCrosshair (True);
5402 AFAIL (select);
5403 break;
5405 RestoreCrosshair (True);
5407 return 0;
5410 /* FLAG(have_regex,FlagHaveRegex,0) */
5412 FlagHaveRegex (int parm)
5414 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5415 return 1;
5416 #else
5417 return 0;
5418 #endif
5421 /* --------------------------------------------------------------------------- */
5423 static const char unselect_syntax[] =
5424 "Unselect(All|Block|Connection)\n"
5425 "Unselect(ElementByName|ObjectByName|PadByName|PinByName)\n"
5426 "Unselect(ElementByName|ObjectByName|PadByName|PinByName, Name)\n"
5427 "Unselect(TextByName|ViaByName)\n" "Unselect(TextByName|ViaByName, Name)\n";
5429 static const char unselect_help[] =
5430 "unselects the object at the pointer location or the specified objects";
5432 /* %start-doc actions Unselect
5434 @table @code
5436 @item All
5437 Unselect all objects.
5439 @item Block
5440 Unselect all objects in a rectangle given by the cursor.
5442 @item Connection
5443 Unselect all connections with the ``found'' flag set.
5445 @item ElementByName
5446 @item ObjectByName
5447 @item PadByName
5448 @item PinByName
5449 @item TextByName
5450 @item ViaByName
5452 These all rely on having a regular expression parser built into
5453 @code{pcb}. If the name is not specified then the user is prompted
5454 for a pattern, and all objects that match the pattern and are of the
5455 type specified are unselected.
5458 @end table
5460 %end-doc */
5462 static int
5463 ActionUnselect (int argc, char **argv, int x, int y)
5465 char *function = ARG (0);
5467 if (function)
5469 HideCrosshair (True);
5470 switch (GetFunctionID (function))
5472 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5473 int type;
5474 /* select objects by their names */
5475 case F_ElementByName:
5476 type = ELEMENT_TYPE;
5477 goto commonByName;
5478 case F_ObjectByName:
5479 type = ALL_TYPES;
5480 goto commonByName;
5481 case F_PadByName:
5482 type = PAD_TYPE;
5483 goto commonByName;
5484 case F_PinByName:
5485 type = PIN_TYPE;
5486 goto commonByName;
5487 case F_TextByName:
5488 type = TEXT_TYPE;
5489 goto commonByName;
5490 case F_ViaByName:
5491 type = VIA_TYPE;
5492 goto commonByName;
5494 commonByName:
5496 char *pattern = ARG (1);
5498 if (pattern
5499 || (pattern =
5500 gui->prompt_for (_("Enter pattern:"), "")) != NULL)
5502 if (SelectObjectByName (type, pattern, False))
5503 SetChangedFlag (True);
5504 if (ARG (1) == 0)
5505 free (pattern);
5507 break;
5509 #endif /* defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP) */
5511 /* all objects in block */
5512 case F_Block:
5514 BoxType box;
5516 box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
5517 Crosshair.AttachedBox.Point2.X);
5518 box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
5519 Crosshair.AttachedBox.Point2.Y);
5520 box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
5521 Crosshair.AttachedBox.Point2.X);
5522 box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
5523 Crosshair.AttachedBox.Point2.Y);
5524 NotifyBlock ();
5525 if (Crosshair.AttachedBox.State == STATE_THIRD &&
5526 SelectBlock (&box, False))
5528 SetChangedFlag (True);
5529 Crosshair.AttachedBox.State = STATE_FIRST;
5531 break;
5534 /* unselect all visible objects */
5535 case F_All:
5537 BoxType box;
5539 box.X1 = -MAX_COORD;
5540 box.Y1 = -MAX_COORD;
5541 box.X2 = MAX_COORD;
5542 box.Y2 = MAX_COORD;
5543 if (SelectBlock (&box, False))
5544 SetChangedFlag (True);
5545 break;
5548 /* all found connections */
5549 case F_Connection:
5550 if (SelectConnection (False))
5552 IncrementUndoSerialNumber ();
5553 SetChangedFlag (True);
5555 break;
5557 default:
5558 RestoreCrosshair (True);
5559 AFAIL (unselect);
5560 break;
5563 RestoreCrosshair (True);
5565 return 0;
5568 /* --------------------------------------------------------------------------- */
5570 static const char saveto_syntax[] =
5571 "SaveTo(Layout|LayoutAs,filename)\n"
5572 "SaveTo(AllConnections|AllUnusedPins|ElementConnections,filename)\n"
5573 "SaveTo(PasteBuffer,filename)";
5575 static const char saveto_help[] = "Saves data to a file.";
5577 /* %start-doc actions SaveTo
5579 @table @code
5581 @item Layout
5582 Saves the current layout.
5584 @item LayoutAs
5585 Saves the current layout, and remembers the filename used.
5587 @item AllConnections
5588 Save all connections to a file.
5590 @item AllUnusedPins
5591 List all unused pins to a file.
5593 @item ElementConnections
5594 Save connections to the element at the cursor to a file.
5596 @item PasteBuffer
5597 Save the content of the active Buffer to a file. This is the graphical way to create a footprint.
5599 @end table
5601 %end-doc */
5603 static int
5604 ActionSaveTo (int argc, char **argv, int x, int y)
5606 char *function;
5607 char *name;
5609 function = argv[0];
5610 name = argv[1];
5612 if (strcasecmp (function, "Layout") == 0)
5614 SavePCB (PCB->Filename);
5615 return 0;
5618 if (argc != 2)
5619 AFAIL (saveto);
5621 if (strcasecmp (function, "LayoutAs") == 0)
5623 MYFREE (PCB->Filename);
5624 PCB->Filename = MyStrdup (name, __FUNCTION__);
5625 SavePCB (PCB->Filename);
5626 return 0;
5629 if (strcasecmp (function, "AllConnections") == 0)
5631 FILE *fp;
5632 Boolean result;
5633 if ((fp = CheckAndOpenFile (name, True, False, &result, NULL)) != NULL)
5635 LookupConnectionsToAllElements (fp);
5636 fclose (fp);
5637 SetChangedFlag (True);
5639 return 0;
5642 if (strcasecmp (function, "AllUnusedPins") == 0)
5644 FILE *fp;
5645 Boolean result;
5646 if ((fp = CheckAndOpenFile (name, True, False, &result, NULL)) != NULL)
5648 LookupUnusedPins (fp);
5649 fclose (fp);
5650 SetChangedFlag (True);
5652 return 0;
5655 if (strcasecmp (function, "ElementConnections") == 0)
5657 ElementTypePtr element;
5658 void *ptrtmp;
5659 FILE *fp;
5660 Boolean result;
5662 if ((SearchScreen (Crosshair.X, Crosshair.Y, ELEMENT_TYPE,
5663 &ptrtmp, &ptrtmp, &ptrtmp)) != NO_TYPE)
5665 element = (ElementTypePtr) ptrtmp;
5666 if ((fp =
5667 CheckAndOpenFile (name, True, False, &result, NULL)) != NULL)
5669 LookupElementConnections (element, fp);
5670 fclose (fp);
5671 SetChangedFlag (True);
5674 return 0;
5677 if (strcasecmp (function, "PasteBuffer") == 0)
5679 return SaveBufferElements (name);
5682 AFAIL (saveto);
5685 /* --------------------------------------------------------------------------- */
5687 static const char savesettings_syntax[] =
5688 "SaveSettings()\n" "SaveSettings(local)";
5690 static const char savesettings_help[] = "Saves settings.";
5692 /* %start-doc actions SaveSettings
5694 If you pass no arguments, the settings are stored in
5695 @code{$HOME/.pcb/settings}. If you pass the word @code{local} they're
5696 saved in @code{./pcb.settings}.
5698 %end-doc */
5700 static int
5701 ActionSaveSettings (int argc, char **argv, int x, int y)
5703 int locally = argc > 0 ? (strncasecmp (argv[0], "local", 5) == 0) : 0;
5704 hid_save_settings (locally);
5705 return 0;
5708 /* --------------------------------------------------------------------------- */
5710 static const char loadfrom_syntax[] =
5711 "LoadFrom(Layout|LayoutToBuffer|ElementToBuffer|Netlist|Revert,filename)";
5713 static const char loadfrom_help[] = "Load layout data from a file.";
5715 /* %start-doc actions LoadFrom
5717 This action assumes you know what the filename is. The various GUIs
5718 should have a similar @code{Load} action where the filename is
5719 optional, and will provide their own file selection mechanism to let
5720 you choose the file name.
5722 @table @code
5724 @item Layout
5725 Loads an entire PCB layout, replacing the current one.
5727 @item LayoutToBuffer
5728 Loads an entire PCB layout to the paste buffer.
5730 @item ElementToBuffer
5731 Loads the given element file into the paste buffer. Element files
5732 contain only a single @code{Element} definition, such as the
5733 ``newlib'' library uses.
5735 @item Netlist
5736 Loads a new netlist, replacing any current netlist.
5738 @item Revert
5739 Re-loads the current layout from its disk file, reverting any changes
5740 you may have made.
5742 @end table
5744 %end-doc */
5746 static int
5747 ActionLoadFrom (int argc, char **argv, int x, int y)
5749 char *function;
5750 char *name;
5751 char fname[256];
5753 if (argc < 2)
5754 AFAIL (loadfrom);
5756 function = argv[0];
5757 name = argv[1];
5759 HideCrosshair (True);
5761 if (strcasecmp (function, "ElementToBuffer") == 0)
5763 if (LoadElementToBuffer (PASTEBUFFER, name, True))
5764 SetMode (PASTEBUFFER_MODE);
5767 else if (strcasecmp (function, "LayoutToBuffer") == 0)
5769 if (LoadLayoutToBuffer (PASTEBUFFER, name))
5770 SetMode (PASTEBUFFER_MODE);
5773 else if (strcasecmp (function, "Layout") == 0)
5775 if (!PCB->Changed ||
5776 gui->confirm_dialog (_("OK to override layout data?"), 0))
5777 LoadPCB (name);
5780 else if (strcasecmp (function, "Netlist") == 0)
5782 if (PCB->Netlistname)
5783 SaveFree (PCB->Netlistname);
5784 PCB->Netlistname = StripWhiteSpaceAndDup (name);
5785 FreeLibraryMemory (&PCB->NetlistLib);
5786 if (!ImportNetlist (PCB->Netlistname))
5787 NetlistChanged (1);
5789 else if (strcasecmp (function, "Revert") == 0 && PCB->Filename
5790 && (!PCB->Changed
5791 || gui->confirm_dialog (_("OK to override changes?"), 0)))
5793 strcpy (fname, PCB->Filename); /*Calling LoadPCB(PCB->Filename) changes the content of PCB->Filename. */
5794 LoadPCB (fname);
5797 RestoreCrosshair (True);
5798 return 0;
5801 /* --------------------------------------------------------------------------- */
5803 static const char new_syntax[] = "New([name])";
5805 static const char new_help[] = "Starts a new layout.";
5807 /* %start-doc actions New
5809 If a name is not given, one is prompted for.
5811 %end-doc */
5813 static int
5814 ActionNew (int argc, char **argv, int x, int y)
5816 char *name = ARG (0);
5818 HideCrosshair (True);
5819 if (!PCB->Changed || gui->confirm_dialog (_("OK to clear layout data?"), 0))
5821 if (name)
5822 name = MyStrdup (name, "ActionNew");
5823 else
5824 name = gui->prompt_for (_("Enter the layout name:"), "");
5826 if (!name)
5828 RestoreCrosshair(True);
5829 return 1;
5832 /* do emergency saving
5833 * clear the old struct and allocate memory for the new one
5835 if (PCB->Changed && Settings.SaveInTMP)
5836 SaveInTMP ();
5837 RemovePCB (PCB);
5838 PCB = CreateNewPCB (True);
5839 PCB->Data->LayerN = DEF_LAYER;
5840 CreateNewPCBPost (PCB, 1);
5842 /* setup the new name and reset some values to default */
5843 PCB->Name = name; /* XXX memory leak */
5845 ResetStackAndVisibility ();
5846 CreateDefaultFont ();
5847 SetCrosshairRange (0, 0, PCB->MaxWidth, PCB->MaxHeight);
5848 CenterDisplay (PCB->MaxWidth / 2, PCB->MaxHeight / 2, False);
5849 ClearAndRedrawOutput ();
5851 hid_action ("PCBChanged");
5852 RestoreCrosshair(True);
5853 return 0;
5855 RestoreCrosshair (True);
5856 return 1;
5859 /* ---------------------------------------------------------------------------
5860 * no operation, just for testing purposes
5861 * syntax: Bell(volume)
5863 void
5864 ActionBell (char *volume)
5866 gui->beep ();
5869 /* --------------------------------------------------------------------------- */
5871 static const char pastebuffer_syntax[] =
5872 "PasteBuffer(AddSelected|Clear|1..MAX_BUFFER)\n"
5873 "PasteBuffer(Rotate, 1..3)\n"
5874 "PasteBuffer(Convert|Save|Restore|Mirror)\n"
5875 "PasteBuffer(ToLayout, X, Y, units)";
5877 static const char pastebuffer_help[] =
5878 "Various operations on the paste buffer.";
5880 /* %start-doc actions PasteBuffer
5882 There are a number of paste buffers; the actual limit is a
5883 compile-time constant @code{MAX_BUFFER} in @file{globalconst.h}. It
5884 is currently @code{5}. One of these is the ``current'' paste buffer,
5885 often referred to as ``the'' paste buffer.
5887 @table @code
5889 @item AddSelected
5890 Copies the selected objects to the current paste buffer.
5892 @item Clear
5893 Remove all objects from the current paste buffer.
5895 @item Convert
5896 Convert the current paste buffer to an element. Vias are converted to
5897 pins, lines are converted to pads.
5899 @item Restore
5900 Convert any elements in the paste buffer back to vias and lines.
5902 @item Mirror
5903 Flip all objects in the paste buffer vertically (up/down flip). To mirror
5904 horizontally, combine this with rotations.
5906 @item Rotate
5907 Rotates the current buffer. The number to pass is 1..3, where 1 means
5908 90 degrees counter clockwise, 2 means 180 degrees, and 3 means 90
5909 degrees clockwise (270 CCW).
5911 @item Save
5912 Saves any elements in the current buffer to the indicated file.
5914 @item ToLayout
5915 Pastes any elements in the current buffer to the indicated X, Y
5916 coordinates in the layout. The @code{X} and @code{Y} are treated like
5917 @code{delta} is for many other objects. For each, if it's prefixed by
5918 @code{+} or @code{-}, then that amount is relative to the last
5919 location. Otherwise, it's absolute. Units can be
5920 @code{mil} or @code{mm}; if unspecified, units are PCB's internal
5921 units, currently 1/100 mil.
5924 @item 1..MAX_BUFFER
5925 Selects the given buffer to be the current paste buffer.
5927 @end table
5929 %end-doc */
5931 static int
5932 ActionPasteBuffer (int argc, char **argv, int x, int y)
5934 char *function = argc ? argv[0] : "";
5935 char *sbufnum = argc > 1 ? argv[1] : "";
5936 char *name;
5937 static char *default_file = NULL;
5938 int free_name = 0;
5940 HideCrosshair (True);
5941 if (function)
5943 switch (GetFunctionID (function))
5945 /* clear contents of paste buffer */
5946 case F_Clear:
5947 ClearBuffer (PASTEBUFFER);
5948 break;
5950 /* copies objects to paste buffer */
5951 case F_AddSelected:
5952 AddSelectedToBuffer (PASTEBUFFER, 0, 0, False);
5953 break;
5955 /* converts buffer contents into an element */
5956 case F_Convert:
5957 ConvertBufferToElement (PASTEBUFFER);
5958 break;
5960 /* break up element for editing */
5961 case F_Restore:
5962 SmashBufferElement (PASTEBUFFER);
5963 break;
5965 /* Mirror buffer */
5966 case F_Mirror:
5967 MirrorBuffer (PASTEBUFFER);
5968 break;
5970 case F_Rotate:
5971 if (sbufnum)
5973 RotateBuffer (PASTEBUFFER, (BYTE) atoi (sbufnum));
5974 SetCrosshairRangeToBuffer ();
5976 break;
5978 case F_Save:
5979 if (PASTEBUFFER->Data->ElementN == 0)
5981 Message (_("Buffer has no elements!\n"));
5982 break;
5984 free_name = 0;
5985 if (argc <= 1)
5987 name = gui->fileselect (_("Save Paste Buffer As ..."),
5988 _("Choose a file to save the contents of the\n"
5989 "paste buffer to.\n"),
5990 default_file, ".fp", "footprint",
5993 if (default_file)
5995 free (default_file);
5996 default_file = NULL;
5998 if ( name && *name)
6000 default_file = strdup (name);
6002 free_name = 1;
6005 else
6006 name = argv[1];
6009 FILE *exist;
6011 if ((exist = fopen (name, "r")))
6013 fclose (exist);
6014 if (gui->
6015 confirm_dialog (_("File exists! Ok to overwrite?"), 0))
6016 SaveBufferElements (name);
6018 else
6019 SaveBufferElements (name);
6021 if (free_name && name)
6022 free (name);
6024 break;
6026 case F_ToLayout:
6028 static int oldx = 0, oldy = 0;
6029 int x, y;
6030 Boolean r;
6032 if (argc == 1)
6034 x = y = 0;
6036 else if (argc == 3 || argc == 4)
6038 x = GetValue (ARG (1), ARG (3), &r);
6039 if (!r)
6040 x += oldx;
6041 y = GetValue (ARG (2), ARG (3), &r);
6042 if (!r)
6043 y += oldy;
6045 else
6047 RestoreCrosshair (True);
6048 AFAIL (pastebuffer);
6051 oldx = x;
6052 oldy = y;
6053 if (CopyPastebufferToLayout (x, y))
6054 SetChangedFlag (True);
6056 break;
6058 /* set number */
6059 default:
6061 int number = atoi (function);
6063 /* correct number */
6064 if (number)
6065 SetBufferNumber (number - 1);
6070 RestoreCrosshair (True);
6071 return 0;
6074 /* --------------------------------------------------------------------------- */
6076 static const char undo_syntax[] = "Undo()\n" "Undo(ClearList)";
6078 static const char undo_help[] = "Undo recent changes.";
6080 /* %start-doc actions Undo
6082 The unlimited undo feature of @code{Pcb} allows you to recover from
6083 most operations that materially affect you work. Calling
6084 @code{Undo()} without any parameter recovers from the last (non-undo)
6085 operation. @code{ClearList} is used to release the allocated
6086 memory. @code{ClearList} is called whenever a new layout is started or
6087 loaded. See also @code{Redo} and @code{Atomic}.
6089 Note that undo groups operations by serial number; changes with the
6090 same serial number will be undone (or redone) as a group. See
6091 @code{Atomic}.
6093 %end-doc */
6095 static int
6096 ActionUndo (int argc, char **argv, int x, int y)
6098 char *function = ARG (0);
6099 if (!function || !*function)
6101 /* don't allow undo in the middle of an operation */
6102 if (Crosshair.AttachedObject.State != STATE_FIRST)
6103 return 1;
6104 if (Crosshair.AttachedBox.State != STATE_FIRST
6105 && Settings.Mode != ARC_MODE)
6106 return 1;
6107 /* undo the last operation */
6109 HideCrosshair (True);
6110 if (Settings.Mode == POLYGON_MODE && Crosshair.AttachedPolygon.PointN)
6112 GoToPreviousPoint ();
6113 RestoreCrosshair (True);
6114 return 0;
6116 /* move anchor point if undoing during line creation */
6117 if (Settings.Mode == LINE_MODE)
6119 if (Crosshair.AttachedLine.State == STATE_SECOND)
6121 if (TEST_FLAG (AUTODRCFLAG, PCB))
6122 Undo (True); /* undo the connection find */
6123 Crosshair.AttachedLine.State = STATE_FIRST;
6124 SetLocalRef (0, 0, False);
6125 RestoreCrosshair (True);
6126 return 0;
6128 if (Crosshair.AttachedLine.State == STATE_THIRD)
6130 int type;
6131 void *ptr1, *ptr3, *ptrtmp;
6132 LineTypePtr ptr2;
6133 /* this search is guaranteed to succeed */
6134 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6135 &ptrtmp, &ptr3,
6136 Crosshair.AttachedLine.Point1.X,
6137 Crosshair.AttachedLine.Point1.Y, 0);
6138 ptr2 = (LineTypePtr) ptrtmp;
6140 /* save both ends of line */
6141 Crosshair.AttachedLine.Point2.X = ptr2->Point1.X;
6142 Crosshair.AttachedLine.Point2.Y = ptr2->Point1.Y;
6143 if ((type = Undo (True)))
6144 SetChangedFlag (True);
6145 /* check that the undo was of the right type */
6146 if ((type & UNDO_CREATE) == 0)
6148 /* wrong undo type, restore anchor points */
6149 Crosshair.AttachedLine.Point2.X =
6150 Crosshair.AttachedLine.Point1.X;
6151 Crosshair.AttachedLine.Point2.Y =
6152 Crosshair.AttachedLine.Point1.Y;
6153 RestoreCrosshair (True);
6154 return 0;
6156 /* move to new anchor */
6157 Crosshair.AttachedLine.Point1.X =
6158 Crosshair.AttachedLine.Point2.X;
6159 Crosshair.AttachedLine.Point1.Y =
6160 Crosshair.AttachedLine.Point2.Y;
6161 /* check if an intermediate point was removed */
6162 if (type & UNDO_REMOVE)
6164 /* this search should find the restored line */
6165 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6166 &ptrtmp,
6167 &ptr3,
6168 Crosshair.AttachedLine.Point2.X,
6169 Crosshair.AttachedLine.Point2.Y, 0);
6170 ptr2 = (LineTypePtr) ptrtmp;
6171 if (TEST_FLAG (AUTODRCFLAG, PCB))
6173 /* undo loses FOUNDFLAG */
6174 SET_FLAG(FOUNDFLAG, ptr2);
6175 DrawLine (CURRENT, ptr2, 0);
6177 Crosshair.AttachedLine.Point1.X =
6178 Crosshair.AttachedLine.Point2.X = ptr2->Point2.X;
6179 Crosshair.AttachedLine.Point1.Y =
6180 Crosshair.AttachedLine.Point2.Y = ptr2->Point2.Y;
6182 FitCrosshairIntoGrid (Crosshair.X, Crosshair.Y);
6183 AdjustAttachedObjects ();
6184 if (--addedLines == 0)
6186 Crosshair.AttachedLine.State = STATE_SECOND;
6187 lastLayer = CURRENT;
6189 else
6191 /* this search is guaranteed to succeed too */
6192 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6193 &ptrtmp,
6194 &ptr3,
6195 Crosshair.AttachedLine.Point1.X,
6196 Crosshair.AttachedLine.Point1.Y, 0);
6197 ptr2 = (LineTypePtr) ptrtmp;
6198 lastLayer = (LayerTypePtr) ptr1;
6200 RestoreCrosshair (True);
6201 return 0;
6204 if (Settings.Mode == ARC_MODE)
6206 if (Crosshair.AttachedBox.State == STATE_SECOND)
6208 Crosshair.AttachedBox.State = STATE_FIRST;
6209 RestoreCrosshair (True);
6210 return 0;
6212 if (Crosshair.AttachedBox.State == STATE_THIRD)
6214 void *ptr1, *ptr2, *ptr3;
6215 BoxTypePtr bx;
6216 /* guaranteed to succeed */
6217 SearchObjectByLocation (ARC_TYPE, &ptr1, &ptr2, &ptr3,
6218 Crosshair.AttachedBox.Point1.X,
6219 Crosshair.AttachedBox.Point1.Y, 0);
6220 bx = GetArcEnds ((ArcTypePtr) ptr2);
6221 Crosshair.AttachedBox.Point1.X =
6222 Crosshair.AttachedBox.Point2.X = bx->X1;
6223 Crosshair.AttachedBox.Point1.Y =
6224 Crosshair.AttachedBox.Point2.Y = bx->Y1;
6225 AdjustAttachedObjects ();
6226 if (--addedLines == 0)
6227 Crosshair.AttachedBox.State = STATE_SECOND;
6230 /* undo the last destructive operation */
6231 if (Undo (True))
6232 SetChangedFlag (True);
6234 else if (function)
6236 switch (GetFunctionID (function))
6238 /* clear 'undo objects' list */
6239 case F_ClearList:
6240 ClearUndoList (False);
6241 break;
6244 RestoreCrosshair (True);
6245 return 0;
6248 /* --------------------------------------------------------------------------- */
6250 static const char redo_syntax[] = "Redo()";
6252 static const char redo_help[] = "Redo recent \"undo\" operations.";
6254 /* %start-doc actions Redo
6256 This routine allows you to recover from the last undo command. You
6257 might want to do this if you thought that undo was going to revert
6258 something other than what it actually did (in case you are confused
6259 about which operations are un-doable), or if you have been backing up
6260 through a long undo list and over-shoot your stopping point. Any
6261 change that is made since the undo in question will trim the redo
6262 list. For example if you add ten lines, then undo three of them you
6263 could use redo to put them back, but if you move a line on the board
6264 before performing the redo, you will lose the ability to "redo" the
6265 three "undone" lines.
6267 %end-doc */
6269 static int
6270 ActionRedo (int argc, char **argv, int x, int y)
6272 if ((Settings.Mode == POLYGON_MODE &&
6273 Crosshair.AttachedPolygon.PointN) ||
6274 Crosshair.AttachedLine.State == STATE_SECOND)
6275 return 1;
6276 HideCrosshair (True);
6277 if (Redo (True))
6279 SetChangedFlag (True);
6280 if (Settings.Mode == LINE_MODE &&
6281 Crosshair.AttachedLine.State != STATE_FIRST)
6283 LineTypePtr line = &CURRENT->Line[CURRENT->LineN - 1];
6284 Crosshair.AttachedLine.Point1.X =
6285 Crosshair.AttachedLine.Point2.X = line->Point2.X;
6286 Crosshair.AttachedLine.Point1.Y =
6287 Crosshair.AttachedLine.Point2.Y = line->Point2.Y;
6288 addedLines++;
6291 RestoreCrosshair (True);
6292 return 0;
6295 /* --------------------------------------------------------------------------- */
6297 static const char polygon_syntax[] = "Polygon(Close|PreviousPoint)";
6299 static const char polygon_help[] = "Some polygon related stuff.";
6301 /* %start-doc actions Polygon
6303 Polygons need a special action routine to make life easier.
6305 @table @code
6307 @item Close
6308 Creates the final segment of the polygon. This may fail if clipping
6309 to 45 degree lines is switched on, in which case a warning is issued.
6311 @item PreviousPoint
6312 Resets the newly entered corner to the previous one. The Undo action
6313 will call Polygon(PreviousPoint) when appropriate to do so.
6315 @end table
6317 %end-doc */
6319 static int
6320 ActionPolygon (int argc, char **argv, int x, int y)
6322 char *function = ARG (0);
6323 if (function && Settings.Mode == POLYGON_MODE)
6325 HideCrosshair (True);
6326 switch (GetFunctionID (function))
6328 /* close open polygon if possible */
6329 case F_Close:
6330 ClosePolygon ();
6331 break;
6333 /* go back to the previous point */
6334 case F_PreviousPoint:
6335 GoToPreviousPoint ();
6336 break;
6338 RestoreCrosshair (True);
6340 return 0;
6343 /* --------------------------------------------------------------------------- */
6345 static const char routestyle_syntax[] = "RouteStyle(1|2|3|4)";
6347 static const char routestyle_help[] =
6348 "Copies the indicated routing style into the current sizes.";
6350 /* %start-doc actions RouteStyle
6352 %end-doc */
6354 static int
6355 ActionRouteStyle (int argc, char **argv, int x, int y)
6357 char *str = ARG (0);
6358 RouteStyleType *rts;
6359 int number;
6361 if (str)
6363 number = atoi (str);
6364 if (number > 0 && number <= NUM_STYLES)
6366 rts = &PCB->RouteStyle[number - 1];
6367 SetLineSize (rts->Thick);
6368 SetViaSize (rts->Diameter, True);
6369 SetViaDrillingHole (rts->Hole, True);
6370 SetKeepawayWidth (rts->Keepaway);
6371 hid_action("RouteStylesChanged");
6374 return 0;
6378 /* --------------------------------------------------------------------------- */
6380 static const char moveobject_syntax[] = "MoveObject(X,Y,dim)";
6382 static const char moveobject_help[] = "Moves the object under the crosshair.";
6384 /* %start-doc actions MoveObject
6386 The @code{X} and @code{Y} are treated like @code{delta} is for many
6387 other objects. For each, if it's prefixed by @code{+} or @code{-},
6388 then that amount is relative. Otherwise, it's absolute. Units can be
6389 @code{mil} or @code{mm}; if unspecified, units are PCB's internal
6390 units, currently 1/100 mil.
6392 %end-doc */
6394 static int
6395 ActionMoveObject (int argc, char **argv, int x, int y)
6397 char *x_str = ARG (0);
6398 char *y_str = ARG (1);
6399 char *units = ARG (2);
6400 LocationType nx, ny;
6401 Boolean r1, r2;
6402 void *ptr1, *ptr2, *ptr3;
6403 int type;
6405 ny = GetValue (y_str, units, &r1);
6406 nx = GetValue (x_str, units, &r2);
6408 type = SearchScreen (x, y, MOVE_TYPES, &ptr1, &ptr2, &ptr3);
6409 if (type == NO_TYPE)
6411 Message (_("Nothing found under crosshair\n"));
6412 return 1;
6414 if (r1)
6415 nx -= x;
6416 if (r2)
6417 ny -= y;
6418 Crosshair.AttachedObject.RubberbandN = 0;
6419 if (TEST_FLAG (RUBBERBANDFLAG, PCB))
6420 LookupRubberbandLines (type, ptr1, ptr2, ptr3);
6421 if (type == ELEMENT_TYPE)
6422 LookupRatLines (type, ptr1, ptr2, ptr3);
6423 MoveObjectAndRubberband (type, ptr1, ptr2, ptr3, nx, ny);
6424 SetChangedFlag (True);
6425 return 0;
6428 /* --------------------------------------------------------------------------- */
6430 static const char movetocurrentlayer_syntax[] =
6431 "MoveToCurrentLayer(Object|SelectedObjects)";
6433 static const char movetocurrentlayer_help[] =
6434 "Moves objects to the current layer.";
6436 /* %start-doc actions MoveToCurrentLayer
6438 Note that moving an element from a component layer to a solder layer,
6439 or from solder to component, won't automatically flip it. Use the
6440 @code{Flip()} action to do that.
6442 %end-doc */
6444 static int
6445 ActionMoveToCurrentLayer (int argc, char **argv, int x, int y)
6447 char *function = ARG (0);
6448 if (function)
6450 HideCrosshair (True);
6451 switch (GetFunctionID (function))
6453 case F_Object:
6455 int type;
6456 void *ptr1, *ptr2, *ptr3;
6458 gui->get_coords ("Select an Object", &x, &y);
6459 if ((type =
6460 SearchScreen (x, y, MOVETOLAYER_TYPES,
6461 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
6462 if (MoveObjectToLayer (type, ptr1, ptr2, ptr3, CURRENT, False))
6463 SetChangedFlag (True);
6464 break;
6467 case F_SelectedObjects:
6468 case F_Selected:
6469 if (MoveSelectedObjectsToLayer (CURRENT))
6470 SetChangedFlag (True);
6471 break;
6473 RestoreCrosshair (True);
6475 return 0;
6479 static const char setsame_syntax[] = "SetSame()";
6481 static const char setsame_help[] =
6482 "Sets current layer and sizes to match indicated item.";
6484 /* %start-doc actions SetSame
6486 When invoked over any line, arc, polygon, or via, this changes the
6487 current layer to be the layer that item is on, and changes the current
6488 sizes (thickness, keepaway, drill, etc) according to that item.
6490 %end-doc */
6492 static int
6493 ActionSetSame (int argc, char **argv, int x, int y)
6495 void *ptr1, *ptr2, *ptr3;
6496 int type;
6497 LayerTypePtr layer = CURRENT;
6499 type = SearchScreen (x, y, CLONE_TYPES, &ptr1, &ptr2, &ptr3);
6500 /* set layer current and size from line or arc */
6501 switch (type)
6503 case LINE_TYPE:
6504 HideCrosshair (True);
6505 Settings.LineThickness = ((LineTypePtr) ptr2)->Thickness;
6506 Settings.Keepaway = ((LineTypePtr) ptr2)->Clearance / 2;
6507 layer = (LayerTypePtr) ptr1;
6508 if (Settings.Mode != LINE_MODE)
6509 SetMode (LINE_MODE);
6510 RestoreCrosshair (True);
6511 hid_action ("RouteStylesChanged");
6512 break;
6514 case ARC_TYPE:
6515 HideCrosshair (True);
6516 Settings.LineThickness = ((ArcTypePtr) ptr2)->Thickness;
6517 Settings.Keepaway = ((ArcTypePtr) ptr2)->Clearance / 2;
6518 layer = (LayerTypePtr) ptr1;
6519 if (Settings.Mode != ARC_MODE)
6520 SetMode (ARC_MODE);
6521 RestoreCrosshair (True);
6522 hid_action ("RouteStylesChanged");
6523 break;
6525 case POLYGON_TYPE:
6526 layer = (LayerTypePtr) ptr1;
6527 break;
6529 case VIA_TYPE:
6530 HideCrosshair (True);
6531 Settings.ViaThickness = ((PinTypePtr) ptr2)->Thickness;
6532 Settings.ViaDrillingHole = ((PinTypePtr) ptr2)->DrillingHole;
6533 Settings.Keepaway = ((PinTypePtr) ptr2)->Clearance / 2;
6534 if (Settings.Mode != VIA_MODE)
6535 SetMode (VIA_MODE);
6536 RestoreCrosshair (True);
6537 hid_action ("RouteStylesChanged");
6538 break;
6540 default:
6541 return 1;
6543 if (layer != CURRENT)
6545 ChangeGroupVisibility (GetLayerNumber (PCB->Data, layer), True, True);
6546 ClearAndRedrawOutput ();
6548 return 0;
6552 /* --------------------------------------------------------------------------- */
6554 static const char setflag_syntax[] =
6555 "SetFlag(Object|Selected|SelectedObjects, flag)\n"
6556 "SetFlag(SelectedLines|SelectedPins|SelectedVias, flag)\n"
6557 "SetFlag(SelectedPads|SelectedTexts|SelectedNames, flag)\n"
6558 "SetFlag(SelectedElements, flag)\n"
6559 "flag = square | octagon | thermal | join";
6561 static const char setflag_help[] = "Sets flags on objects.";
6563 /* %start-doc actions SetFlag
6565 Turns the given flag on, regardless of its previous setting. See
6566 @code{ChangeFlag}.
6568 @example
6569 SetFlag(SelectedPins,thermal)
6570 @end example
6572 %end-doc */
6574 static int
6575 ActionSetFlag (int argc, char **argv, int x, int y)
6577 char *function = ARG (0);
6578 char *flag = ARG (1);
6579 ChangeFlag (function, flag, 1, "SetFlag");
6580 return 0;
6583 /* --------------------------------------------------------------------------- */
6585 static const char clrflag_syntax[] =
6586 "ClrFlag(Object|Selected|SelectedObjects, flag)\n"
6587 "ClrFlag(SelectedLines|SelectedPins|SelectedVias, flag)\n"
6588 "ClrFlag(SelectedPads|SelectedTexts|SelectedNames, flag)\n"
6589 "ClrFlag(SelectedElements, flag)\n"
6590 "flag = square | octagon | thermal | join";
6592 static const char clrflag_help[] = "Clears flags on objects.";
6594 /* %start-doc actions ClrFlag
6596 Turns the given flag off, regardless of its previous setting. See
6597 @code{ChangeFlag}.
6599 @example
6600 ClrFlag(SelectedLines,join)
6601 @end example
6603 %end-doc */
6605 static int
6606 ActionClrFlag (int argc, char **argv, int x, int y)
6608 char *function = ARG (0);
6609 char *flag = ARG (1);
6610 ChangeFlag (function, flag, 0, "ClrFlag");
6611 return 0;
6614 /* --------------------------------------------------------------------------- */
6616 static const char changeflag_syntax[] =
6617 "ChangeFlag(Object|Selected|SelectedObjects, flag, value)\n"
6618 "ChangeFlag(SelectedLines|SelectedPins|SelectedVias, flag, value)\n"
6619 "ChangeFlag(SelectedPads|SelectedTexts|SelectedNames, flag, value)\n"
6620 "ChangeFlag(SelectedElements, flag, value)\n"
6621 "flag = square | octagon | thermal | join\n" "value = 0 | 1";
6623 static const char changeflag_help[] = "Sets or clears flags on objects.";
6625 /* %start-doc actions ChangeFlag
6627 Toggles the given flag on the indicated object(s). The flag may be
6628 one of the flags listed above (square, octagon, thermal, join). The
6629 value may be the number 0 or 1. If the value is 0, the flag is
6630 cleared. If the value is 1, the flag is set.
6632 %end-doc */
6634 static int
6635 ActionChangeFlag (int argc, char **argv, int x, int y)
6637 char *function = ARG (0);
6638 char *flag = ARG (1);
6639 int value = argc > 2 ? atoi (argv[2]) : -1;
6640 if (value != 0 && value != 1)
6641 AFAIL (changeflag);
6643 ChangeFlag (function, flag, value, "ChangeFlag");
6644 return 0;
6648 static void
6649 ChangeFlag (char *what, char *flag_name, int value, char *cmd_name)
6651 Boolean (*set_object) (int, void *, void *, void *);
6652 Boolean (*set_selected) (int);
6654 if (NSTRCMP (flag_name, "square") == 0)
6656 set_object = value ? SetObjectSquare : ClrObjectSquare;
6657 set_selected = value ? SetSelectedSquare : ClrSelectedSquare;
6659 else if (NSTRCMP (flag_name, "octagon") == 0)
6661 set_object = value ? SetObjectOctagon : ClrObjectOctagon;
6662 set_selected = value ? SetSelectedOctagon : ClrSelectedOctagon;
6664 else if (NSTRCMP (flag_name, "join") == 0)
6666 /* Note: these are backwards, because the flag is "clear" but
6667 the command is "join". */
6668 set_object = value ? ClrObjectJoin : SetObjectJoin;
6669 set_selected = value ? ClrSelectedJoin : SetSelectedJoin;
6671 else
6673 Message (_("%s(): Flag \"%s\" is not valid\n"), cmd_name, flag_name);
6674 return;
6677 HideCrosshair (True);
6678 switch (GetFunctionID (what))
6680 case F_Object:
6682 int type;
6683 void *ptr1, *ptr2, *ptr3;
6685 if ((type =
6686 SearchScreen (Crosshair.X, Crosshair.Y, CHANGESIZE_TYPES,
6687 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
6688 if (TEST_FLAG (LOCKFLAG, (PinTypePtr) ptr2))
6689 Message (_("Sorry, the object is locked\n"));
6690 if (set_object (type, ptr1, ptr2, ptr3))
6691 SetChangedFlag (True);
6692 break;
6695 case F_SelectedVias:
6696 if (set_selected (VIA_TYPE))
6697 SetChangedFlag (True);
6698 break;
6700 case F_SelectedPins:
6701 if (set_selected (PIN_TYPE))
6702 SetChangedFlag (True);
6703 break;
6705 case F_SelectedPads:
6706 if (set_selected (PAD_TYPE))
6707 SetChangedFlag (True);
6708 break;
6710 case F_SelectedLines:
6711 if (set_selected (LINE_TYPE))
6712 SetChangedFlag (True);
6713 break;
6715 case F_SelectedTexts:
6716 if (set_selected (TEXT_TYPE))
6717 SetChangedFlag (True);
6718 break;
6720 case F_SelectedNames:
6721 if (set_selected (ELEMENTNAME_TYPE))
6722 SetChangedFlag (True);
6723 break;
6725 case F_SelectedElements:
6726 if (set_selected (ELEMENT_TYPE))
6727 SetChangedFlag (True);
6728 break;
6730 case F_Selected:
6731 case F_SelectedObjects:
6732 if (set_selected (CHANGESIZE_TYPES))
6733 SetChangedFlag (True);
6734 break;
6736 RestoreCrosshair (True);
6740 /* --------------------------------------------------------------------------- */
6742 static const char executefile_syntax[] = "ExecuteFile(filename)";
6744 static const char executefile_help[] = "Run actions from the given file.";
6746 /* %start-doc actions ExecuteFile
6748 Lines starting with @code{#} are ignored.
6750 %end-doc */
6752 static int
6753 ActionExecuteFile (int argc, char **argv, int x, int y)
6755 FILE *fp;
6756 char *fname;
6757 char line[256];
6758 int n = 0;
6759 char *sp;
6761 if (argc != 1)
6762 AFAIL (executefile);
6764 fname = argv[0];
6766 if ((fp = fopen (fname, "r")) == NULL)
6768 fprintf (stderr, "Could not open actions file \"%s\".\n", fname);
6769 return 1;
6772 defer_updates = 1;
6773 defer_needs_update = 0;
6774 while (fgets (line, sizeof (line), fp) != NULL)
6776 n++;
6777 sp = line;
6779 /* eat the trailing newline */
6780 while (*sp && *sp != '\r' && *sp != '\n')
6781 sp++;
6782 *sp = '\0';
6784 /* eat leading spaces and tabs */
6785 sp = line;
6786 while (*sp && (*sp == ' ' || *sp == '\t'))
6787 sp++;
6790 * if we have anything left and its not a comment line
6791 * then execute it
6794 if (*sp && *sp != '#')
6796 /*Message ("%s : line %-3d : \"%s\"\n", fname, n, sp);*/
6797 hid_parse_actions (sp, 0);
6801 defer_updates = 0;
6802 if (defer_needs_update)
6804 IncrementUndoSerialNumber ();
6805 gui->invalidate_all ();
6807 fclose (fp);
6808 return 0;
6811 /* --------------------------------------------------------------------------- */
6813 static int
6814 ActionPSCalib (int argc, char **argv, int x, int y)
6816 HID *ps = hid_find_exporter ("ps");
6817 ps->calibrate (0.0,0.0);
6818 return 0;
6821 /* --------------------------------------------------------------------------- */
6823 static ElementType *element_cache = NULL;
6825 static ElementType *
6826 find_element_by_refdes (char *refdes)
6828 if (element_cache
6829 && NAMEONPCB_NAME(element_cache)
6830 && strcmp (NAMEONPCB_NAME(element_cache), refdes) == 0)
6831 return element_cache;
6833 ELEMENT_LOOP (PCB->Data);
6835 if (NAMEONPCB_NAME(element)
6836 && strcmp (NAMEONPCB_NAME(element), refdes) == 0)
6838 element_cache = element;
6839 return element_cache;
6842 END_LOOP;
6843 return NULL;
6846 static AttributeType *
6847 lookup_attr (AttributeListTypePtr list, const char *name)
6849 int i;
6850 for (i=0; i<list->Number; i++)
6851 if (strcmp (list->List[i].name, name) == 0)
6852 return & list->List[i];
6853 return NULL;
6856 static void
6857 delete_attr (AttributeListTypePtr list, AttributeType *attr)
6859 int idx = attr - list->List;
6860 if (idx < 0 || idx >= list->Number)
6861 return;
6862 if (list->Number - idx > 1)
6863 memmove (attr, attr+1, (list->Number - idx - 1) * sizeof(AttributeType));
6864 list->Number --;
6867 static const char elementlist_syntax[] = "ElementList(Start|Done|Need,<refdes>,<footprint>,<value>)";
6869 static const char elementlist_help[] = "Adds the given element if it doesn't already exist.";
6871 /* %start-doc actions elementlist
6873 @table @code
6875 @item Start
6876 Indicates the start of an element list; call this before any Need
6877 actions.
6879 @item Need
6880 Searches the board for an element with a matching refdes.
6882 If found, the value and footprint are updated.
6884 If not found, a new element is created with the given footprint and value.
6886 @item Done
6887 Compares the list of elements needed since the most recent
6888 @code{start} with the list of elements actually on the board. Any
6889 elements that weren't listed are selected, so that the user may delete
6890 them.
6892 @end table
6894 %end-doc */
6896 static int
6897 ActionElementList (int argc, char **argv, int x, int y)
6899 ElementType *e = NULL;
6900 char *refdes, *value, *footprint, *old;
6901 char *args[3];
6902 char *function = argv[0];
6904 if (strcasecmp (function, "start") == 0)
6906 ELEMENT_LOOP (PCB->Data);
6908 CLEAR_FLAG (FOUNDFLAG, element);
6910 END_LOOP;
6911 element_cache = NULL;
6912 return 0;
6915 if (strcasecmp (function, "done") == 0)
6917 ELEMENT_LOOP (PCB->Data);
6919 if (TEST_FLAG (FOUNDFLAG, element))
6921 CLEAR_FLAG (FOUNDFLAG, element);
6923 else if (! EMPTY_STRING_P (NAMEONPCB_NAME (element)))
6925 /* Unnamed elements should remain untouched */
6926 SET_FLAG (SELECTEDFLAG, element);
6929 END_LOOP;
6930 return 0;
6933 if (strcasecmp (function, "need") != 0)
6934 AFAIL (elementlist);
6936 if (argc != 4)
6937 AFAIL (elementlist);
6939 argc --;
6940 argv ++;
6942 refdes = ARG(0);
6943 footprint = ARG(1);
6944 value = ARG(2);
6946 args[0] = footprint;
6947 args[1] = refdes;
6948 args[2] = value;
6950 e = find_element_by_refdes (refdes);
6952 if (!e)
6954 /* Not on board, need to add it. */
6955 if (LoadFootprint(argc, args, x, y))
6956 return 1;
6957 if (CopyPastebufferToLayout (0, 0))
6958 SetChangedFlag (True);
6961 else if (e && strcmp (DESCRIPTION_NAME(e), footprint) != 0)
6963 int er, pr, i;
6964 LocationType mx, my;
6965 ElementType *pe;
6967 /* Different footprint, we need to swap them out. */
6968 if (LoadFootprint(argc, args, x, y))
6969 return 1;
6971 er = ElementOrientation (e);
6972 pe = & PASTEBUFFER->Data->Element[0];
6973 pr = ElementOrientation (pe);
6975 mx = e->MarkX;
6976 my = e->MarkY;
6978 if (er != pr)
6979 RotateElementLowLevel (PASTEBUFFER->Data, pe, pe->MarkX, pe->MarkY, (er-pr+4)%4);
6981 for (i=0; i<MAX_ELEMENTNAMES; i++)
6983 pe->Name[i].X = e->Name[i].X - mx;
6984 pe->Name[i].Y = e->Name[i].Y - my;
6985 pe->Name[i].Direction = e->Name[i].Direction;
6986 pe->Name[i].Scale = e->Name[i].Scale;
6989 RemoveElement (e);
6991 if (CopyPastebufferToLayout (mx, my))
6992 SetChangedFlag (True);
6995 e = find_element_by_refdes (refdes);
6997 old = ChangeElementText (PCB, PCB->Data, e, NAMEONPCB_INDEX, strdup (refdes));
6998 if (old)
6999 free(old);
7000 old = ChangeElementText (PCB, PCB->Data, e, VALUE_INDEX, strdup (value));
7001 if (old)
7002 free(old);
7004 SET_FLAG (FOUNDFLAG, e);
7006 return 0;
7009 static const char elementsetattr_syntax[] = "ElementSetAttr(refdes,name[,value])";
7011 static const char elementsetattr_help[] = "Sets or clears an element-specific attribute";
7013 /* %start-doc actions elementsetattr
7015 If a value is specified, the named attribute is added (if not already
7016 present) or changed (if it is) to the given value. If the value is
7017 not specified, the given attribute is removed if present.
7019 %end-doc */
7021 static int
7022 ActionElementSetAttr (int argc, char **argv, int x, int y)
7024 ElementType *e = NULL;
7025 char *refdes, *name, *value;
7026 AttributeType *attr;
7028 if (argc < 2)
7030 AFAIL (changepinname);
7033 refdes = argv[0];
7034 name = argv[1];
7035 value = ARG(2);
7037 ELEMENT_LOOP (PCB->Data);
7039 if (NSTRCMP (refdes, NAMEONPCB_NAME (element)) == 0)
7041 e = element;
7042 break;
7045 END_LOOP;
7047 if (!e)
7049 Message("Cannot change attribute of %s - element not found", refdes);
7050 return 1;
7053 attr = lookup_attr (&e->Attributes, name);
7055 if (attr && value)
7057 MYFREE (attr->value);
7058 attr->value = MyStrdup (value, "ElementSetAttr");
7060 if (attr && ! value)
7062 delete_attr (& e->Attributes, attr);
7064 if (!attr && value)
7066 CreateNewAttribute (& e->Attributes, name, value);
7069 return 0;
7072 static const char execcommand_syntax[] = "ExecCommand(command)";
7074 static const char execcommand_help[] = "Runs a command";
7076 /* %start-doc actions execcommand
7078 Runs the given command, which is a system executable.
7080 %end-doc */
7082 static int
7083 ActionExecCommand (int argc, char **argv, int x, int y)
7085 char *command;
7087 if (argc < 1)
7089 AFAIL (execcommand);
7092 command = ARG(0);
7094 if (system (command))
7095 return 1;
7096 return 0;
7099 static const char import_syntax[] =
7100 "Import()\n"
7101 "Import([gnetlist|make[,source,source,...]])\n";
7103 static const char import_help[] = "Import schematics";
7105 /* %start-doc actions import
7107 Imports element and netlist data from the schematics (or some other
7108 source). The first parameter, which is optional, is the mode. If not
7109 specified, the @code{import::mode} attribute in the PCB is used.
7110 @code{gnetlist} means gnetlist is used to obtain the information from
7111 the schematics. @code{make} invokes @code{make}, assuming the user
7112 has a @code{Makefile} in the current directory. The @code{Makefile}
7113 will be invoked with the following variables set:
7115 @table @code
7117 @item PCB
7118 The name of the .pcb file
7120 @item SRCLIST
7121 A space-separated list of source files
7123 @item OUT
7124 The name of the file in which to put the command script, which may
7125 contain any @pcb{} actions. By default, this is a temporary file
7126 selected by @pcb{}, but if you specify an @code{import::outfile}
7127 attribute, that file name is used instead (and not automatically
7128 deleted afterwards).
7130 @end table
7132 The target specified to be built is the first of these that apply:
7134 @itemize @bullet
7136 @item
7137 The target specified by an @code{import::target} attribute.
7139 @item
7140 The output file specified by an @code{import::outfile} attribute.
7142 @item
7143 If nothing else is specified, the target is @code{pcb_import}.
7145 @end itemize
7147 If you specify an @code{import::makefile} attribute, then "-f <that
7148 file>" will be added to the command line.
7150 If you specify the mode, you may also specify the source files
7151 (schematics). If you do not specify any, the list of schematics is
7152 obtained by reading the @code{import::src@var{N}} attributes (like
7153 @code{import::src0}, @code{import::src1}, etc).
7155 For compatibility with future extensions to the import file format,
7156 the generated file @emph{must not} start with the two characters
7157 @code{#%}.
7159 If a temporary file is needed the @code{TMPDIR} environment variable
7160 is used to select its location.
7162 Note that the programs @code{gnetlist} and @code{make} may be
7163 overridden by the user via the @code{make-program} and @code{gnetlist}
7164 @code{pcb} settings (i.e. in @code{~/.pcb/settings} or on the command
7165 line).
7167 %end-doc */
7169 static int
7170 pcb_spawnvp (char **argv)
7172 int pid;
7173 pid = fork ();
7174 if (pid < 0)
7176 /* error */
7177 Message("Cannot fork!");
7178 return 1;
7180 else if (pid == 0)
7182 /* Child */
7183 execvp (argv[0], argv);
7184 exit(1);
7186 else
7188 int rv;
7189 /* Parent */
7190 wait (&rv);
7192 return 0;
7196 * Creates a new temporary file name. Hopefully the operating system
7197 * provides a mkdtemp() function to securily create a temporary
7198 * directory with mode 0700. If so then that directory is created and
7199 * the returned string is made up of the directory plus the name
7200 * variable. For example:
7202 * tempfile_name_new ("myfile") might return
7203 * "/var/tmp/pcb.123456/myfile".
7205 * If mkdtemp() is not available then 'name' is ignored and the
7206 * insecure tmpnam() function is used.
7208 * Files/names created with tempfile_name_new() should be unlinked
7209 * with tempfile_unlink to make sure the temporary directory is also
7210 * removed when mkdtemp() is used.
7212 static char *
7213 tempfile_name_new (char * name)
7215 char *tmpfile = NULL;
7216 #ifdef HAVE_MKDTEMP
7217 char *tmpdir, *mytmpdir;
7218 size_t len;
7219 #endif
7221 assert ( name != NULL );
7223 #ifdef HAVE_MKDTEMP
7224 #define TEMPLATE "pcb.XXXXXXXX"
7227 tmpdir = getenv ("TMPDIR");
7229 /* FIXME -- what about win32? */
7230 if (tmpdir == NULL) {
7231 tmpdir = "/tmp";
7234 mytmpdir = (char *) malloc (sizeof(char) *
7235 (strlen (tmpdir) +
7237 strlen (TEMPLATE) +
7238 1));
7239 if (mytmpdir == NULL) {
7240 fprintf (stderr, "%s(): malloc failed()\n", __FUNCTION__);
7241 exit (1);
7244 *mytmpdir = '\0';
7245 (void)strcat (mytmpdir, tmpdir);
7246 (void)strcat (mytmpdir, PCB_DIR_SEPARATOR_S);
7247 (void)strcat (mytmpdir, TEMPLATE);
7248 if (mkdtemp (mytmpdir) == NULL) {
7249 fprintf (stderr, "%s(): mkdtemp (\"%s\") failed\n", __FUNCTION__, mytmpdir);
7250 free (mytmpdir);
7251 return NULL;
7255 len = strlen (mytmpdir) + /* the temp directory name */
7256 1 + /* the directory sep. */
7257 strlen (name) + /* the file name */
7258 1 /* the \0 termination */
7261 tmpfile = (char *) malloc (sizeof (char) * len);
7263 *tmpfile = '\0';
7264 (void)strcat (tmpfile, mytmpdir);
7265 (void)strcat (tmpfile, PCB_DIR_SEPARATOR_S);
7266 (void)strcat (tmpfile, name);
7268 free (mytmpdir);
7269 #undef TEMPLATE
7270 #else
7272 * tmpnam() uses a static buffer so strdup() the result right away
7273 * in case someone decides to create multiple temp names.
7275 tmpfile = strdup (tmpnam (NULL));
7276 #endif
7278 return tmpfile;
7282 * Unlink a temporary file. If we have mkdtemp() then our temp file
7283 * lives in a temporary directory and we need to remove that directory
7284 * too.
7286 static int
7287 tempfile_unlink (char * name)
7289 int rc;
7291 #ifdef HAVE_MKDTEMP
7292 int e, rc2 = 0;
7293 char *dname;
7295 rc = unlink (name);
7296 /* it is possible that the file was never created so it is OK if the
7297 unlink fails */
7299 /* now figure out the directory name to remove */
7300 e = strlen (name) - 1;
7301 while (e > 0 && name[e] != PCB_DIR_SEPARATOR_C) {e--;}
7303 dname = strdup (name);
7304 dname[e] = '\0';
7307 * at this point, e *should* point to the end of the directory part
7308 * but lets make sure.
7310 if (e > 0) {
7311 rc2 = rmdir (dname);
7312 if (rc2 != 0) {
7313 perror (dname);
7316 } else {
7317 fprintf (stderr, "%s(): Unable to determine temp directory name from the temp file\n",
7318 __FUNCTION__);
7319 fprintf (stderr, "%s(): \"%s\"\n",
7320 __FUNCTION__, name);
7321 rc2 = -1;
7324 /* name was allocated with malloc */
7325 free (dname);
7326 free (name);
7329 * FIXME - should also return -1 if the temp file exists and was not
7330 * removed.
7332 if (rc2 != 0) {
7333 return -1;
7336 #else
7337 rc = unlink (name);
7339 if (rc != 0) {
7340 fprintf (stderr, "Failed to unlink \"%s\"\n", name);
7341 free (name);
7342 return rc;
7344 free (name);
7346 #endif
7348 return 0;
7351 static int
7352 ActionImport (int argc, char **argv, int x, int y)
7354 char *mode;
7355 char **sources = NULL;
7356 int nsources = 0;
7358 mode = ARG (0);
7359 if (! mode)
7360 mode = AttributeGet (PCB, "import::mode");
7361 if (! mode)
7362 mode = "gnetlist";
7364 if (argc > 1)
7366 sources = argv + 1;
7367 nsources = argc - 1;
7370 if (! sources)
7372 char sname[40];
7373 char *src;
7375 nsources = -1;
7376 do {
7377 nsources ++;
7378 sprintf(sname, "import::src%d", nsources);
7379 src = AttributeGet (PCB, sname);
7380 } while (src);
7382 if (nsources > 0)
7384 sources = (char **) malloc ((nsources + 1) * sizeof (char *));
7385 nsources = -1;
7386 do {
7387 nsources ++;
7388 sprintf(sname, "import::src%d", nsources);
7389 src = AttributeGet (PCB, sname);
7390 sources[nsources] = src;
7391 } while (src);
7395 if (! sources)
7397 /* Replace .pcb with .sch and hope for the best. */
7398 char *pcbname = PCB->Filename;
7399 char *schname;
7400 char *dot, *slash, *bslash;
7402 if (!pcbname)
7404 Message ("Please save your PCB first, so that it has a\n"
7405 "file name, or manually add an import::src0 attribute with\n"
7406 "the name of the schematic to import from.");
7407 return 1;
7410 schname = (char *) malloc (strlen(pcbname) + 5);
7411 strcpy (schname, pcbname);
7412 dot = strchr (schname, '.');
7413 slash = strchr (schname, '/');
7414 bslash = strchr (schname, '\\');
7415 if (dot && slash && dot < slash)
7416 dot = NULL;
7417 if (dot && bslash && dot < bslash)
7418 dot = NULL;
7419 if (dot)
7420 *dot = 0;
7421 strcat (schname, ".sch");
7423 sources = (char **) malloc (2 * sizeof (char *));
7424 sources[0] = schname;
7425 sources[1] = NULL;
7426 nsources = 1;
7429 if (strcasecmp (mode, "gnetlist") == 0)
7431 char *tmpfile = tempfile_name_new ("gnetlist_output");
7432 char **cmd;
7433 int i;
7435 if (tmpfile == NULL) {
7436 Message ("Could not create temp file");
7437 return 1;
7440 cmd = (char **) malloc ((6 + nsources) * sizeof (char *));
7441 cmd[0] = Settings.GnetlistProgram;
7442 cmd[1] = "-g";
7443 cmd[2] = "pcbfwd";
7444 cmd[3] = "-o";
7445 cmd[4] = tmpfile;
7446 for (i=0; i<nsources; i++)
7447 cmd[5+i] = sources[i];
7448 cmd[5+nsources] = NULL;
7450 if (pcb_spawnvp (cmd))
7452 unlink (tmpfile);
7453 return 1;
7456 cmd[0] = tmpfile;
7457 cmd[1] = NULL;
7458 ActionExecuteFile (1, cmd, 0, 0);
7460 free (cmd);
7461 tempfile_unlink (tmpfile);
7463 else if (strcasecmp (mode, "make") == 0)
7465 int must_free_tmpfile = 0;
7466 char *tmpfile;
7467 char *cmd[10];
7468 int i;
7469 char *srclist;
7470 int srclen;
7471 char *user_outfile = NULL;
7472 char *user_makefile = NULL;
7473 char *user_target = NULL;
7476 user_outfile = AttributeGet (PCB, "import::outfile");
7477 user_makefile = AttributeGet (PCB, "import::makefile");
7478 user_target = AttributeGet (PCB, "import::target");
7479 if (user_outfile && !user_target)
7480 user_target = user_outfile;
7482 if (user_outfile)
7483 tmpfile = user_outfile;
7484 else
7486 tmpfile = tempfile_name_new ("gnetlist_output");
7487 if (tmpfile == NULL) {
7488 Message ("Could not create temp file");
7489 return 1;
7491 must_free_tmpfile = 1;
7494 srclen = sizeof("SRCLIST=") + 2;
7495 for (i=0; i<nsources; i++)
7496 srclen += strlen (sources[i]) + 2;
7497 srclist = (char *) malloc (srclen);
7498 strcpy (srclist, "SRCLIST=");
7499 for (i=0; i<nsources; i++)
7501 if (i)
7502 strcat (srclist, " ");
7503 strcat (srclist, sources[i]);
7506 cmd[0] = Settings.MakeProgram;
7507 cmd[1] = "-s";
7508 cmd[2] = Concat ("PCB=", PCB->Filename, NULL);
7509 cmd[3] = srclist;
7510 cmd[4] = Concat ("OUT=", tmpfile, NULL);
7511 i = 5;
7512 if (user_makefile)
7514 cmd[i++] = "-f";
7515 cmd[i++] = user_makefile;
7517 cmd[i++] = user_target ? user_target : "pcb_import";
7518 cmd[i++] = NULL;
7520 if (pcb_spawnvp (cmd))
7522 if (must_free_tmpfile)
7523 unlink (tmpfile);
7524 free (cmd[2]);
7525 free (cmd[3]);
7526 free (cmd[4]);
7527 return 1;
7530 cmd[0] = tmpfile;
7531 cmd[1] = NULL;
7532 ActionExecuteFile (1, cmd, 0, 0);
7534 free (cmd[2]);
7535 free (cmd[3]);
7536 free (cmd[4]);
7537 if (must_free_tmpfile)
7538 tempfile_unlink (tmpfile);
7540 else
7542 Message ("Unknown import mode: %s\n", mode);
7543 return 1;
7546 DeleteRats (False);
7547 AddAllRats (False, NULL);
7549 return 0;
7552 /* ------------------------------------------------------------ */
7554 static const char attributes_syntax[] =
7555 "Attributes(Layout|Layer|Element)\n"
7556 "Attributes(Layer,layername)";
7558 static const char attributes_help[] =
7559 "Let the user edit the attributes of the layout, current or given\n"
7560 "layer, or selected element.";
7562 /* %start-doc actions Attributes
7564 This just pops up a dialog letting the user edit the attributes of the
7565 pcb, an element, or a layer.
7567 %end-doc */
7570 static int
7571 ActionAttributes (int argc, char **argv, int x, int y)
7573 char *function = ARG (0);
7574 char *layername = ARG (1);
7575 char *buf;
7577 if (!function)
7578 AFAIL (attributes);
7580 if (!gui->edit_attributes)
7582 Message ("This GUI doesn't support Attribute Editing\n");
7583 return 1;
7586 switch (GetFunctionID (function))
7588 case F_Layout:
7590 gui->edit_attributes("Layout Attributes", &(PCB->Attributes));
7591 return 0;
7594 case F_Layer:
7596 LayerType *layer = CURRENT;
7597 if (layername)
7599 int i;
7600 layer = NULL;
7601 for (i=0; i<max_layer; i++)
7602 if (strcmp (PCB->Data->Layer[i].Name, layername) == 0)
7604 layer = & (PCB->Data->Layer[i]);
7605 break;
7607 if (layer == NULL)
7609 Message ("No layer named %s\n", layername);
7610 return 1;
7613 buf = (char *) malloc (strlen (layer->Name) + strlen ("Layer X Attributes"));
7614 sprintf (buf, "Layer %s Attributes", layer->Name);
7615 gui->edit_attributes(buf, &(layer->Attributes));
7616 free (buf);
7617 return 0;
7620 case F_Element:
7622 int n_found = 0;
7623 ElementType *e = NULL;
7624 ELEMENT_LOOP (PCB->Data);
7626 if (TEST_FLAG (SELECTEDFLAG, element))
7628 e = element;
7629 n_found ++;
7632 END_LOOP;
7633 if (n_found > 1)
7635 Message ("Too many elements selected\n");
7636 return 1;
7638 if (n_found == 0)
7640 void *ptrtmp;
7641 gui->get_coords ("Click on an element", &x, &y);
7642 if ((SearchScreen
7643 (x, y, ELEMENT_TYPE, &ptrtmp,
7644 &ptrtmp, &ptrtmp)) != NO_TYPE)
7645 e = (ElementTypePtr) ptrtmp;
7646 else
7648 Message ("No element found there\n");
7649 return 1;
7653 buf = (char *) malloc (strlen (NAMEONPCB_NAME(e)) + strlen ("Element X Attributes"));
7654 sprintf(buf, "Element %s Attributes", NAMEONPCB_NAME(e));
7655 gui->edit_attributes(buf, &(e->Attributes));
7656 free (buf);
7657 break;
7660 default:
7661 AFAIL (attributes);
7664 return 0;
7667 /* --------------------------------------------------------------------------- */
7669 HID_Action action_action_list[] = {
7670 {"AddRats", 0, ActionAddRats,
7671 addrats_help, addrats_syntax}
7673 {"Attributes", 0, ActionAttributes,
7674 attributes_help, attributes_syntax}
7676 {"Atomic", 0, ActionAtomic,
7677 atomic_help, atomic_syntax}
7679 {"AutoPlaceSelected", 0, ActionAutoPlaceSelected,
7680 autoplace_help, autoplace_syntax}
7682 {"AutoRoute", 0, ActionAutoRoute,
7683 autoroute_help, autoroute_syntax}
7685 {"ChangeClearSize", 0, ActionChangeClearSize,
7686 changeclearsize_help, changeclearsize_syntax}
7688 {"ChangeDrillSize", 0, ActionChange2ndSize,
7689 changedrillsize_help, changedrillsize_syntax}
7691 {"ChangeHole", 0, ActionChangeHole,
7692 changehold_help, changehold_syntax}
7694 {"ChangeJoin", 0, ActionChangeJoin,
7695 changejoin_help, changejoin_syntax}
7697 {"ChangeName", 0, ActionChangeName,
7698 changename_help, changename_syntax}
7700 {"ChangePaste", 0, ActionChangePaste,
7701 changepaste_help, changepaste_syntax}
7703 {"ChangePinName", 0, ActionChangePinName,
7704 changepinname_help, changepinname_syntax}
7706 {"ChangeSize", 0, ActionChangeSize,
7707 changesize_help, changesize_syntax}
7709 {"ChangeSquare", 0, ActionChangeSquare,
7710 changesquare_help, changesquare_syntax}
7712 {"ChangeOctagon", 0, ActionChangeOctagon,
7713 changeoctagon_help, changeoctagon_syntax}
7715 {"ClearSquare", 0, ActionClearSquare,
7716 clearsquare_help, clearsquare_syntax}
7718 {"ClearOctagon", 0, ActionClearOctagon,
7719 clearoctagon_help, clearoctagon_syntax}
7721 {"Connection", 0, ActionConnection,
7722 connection_help, connection_syntax}
7724 {"Delete", 0, ActionDelete,
7725 delete_help, delete_syntax}
7727 {"DeleteRats", 0, ActionDeleteRats,
7728 deleterats_help, deleterats_syntax}
7730 {"DisperseElements", 0, ActionDisperseElements,
7731 disperseelements_help, disperseelements_syntax}
7733 {"Display", 0, ActionDisplay,
7734 display_help, display_syntax}
7736 {"DRC", 0, ActionDRCheck,
7737 drc_help, drc_syntax}
7739 {"DumpLibrary", 0, ActionDumpLibrary,
7740 dumplibrary_help, dumplibrary_syntax}
7742 {"ExecuteFile", 0, ActionExecuteFile,
7743 executefile_help, executefile_syntax}
7745 {"Flip", "Click on Object or Flip Point", ActionFlip,
7746 flip_help, flip_syntax}
7748 {"LoadFrom", 0, ActionLoadFrom,
7749 loadfrom_help, loadfrom_syntax}
7751 {"MarkCrosshair", 0, ActionMarkCrosshair,
7752 markcrosshair_help, markcrosshair_syntax}
7754 {"Message", 0, ActionMessage,
7755 message_help, message_syntax}
7757 {"MinMaskGap", 0, ActionMinMaskGap,
7758 minmaskgap_help, minmaskgap_syntax}
7760 {"MinClearGap", 0, ActionMinClearGap,
7761 mincleargap_help, mincleargap_syntax}
7763 {"Mode", 0, ActionMode,
7764 mode_help, mode_syntax}
7766 {"MorphPolygon", 0, ActionMorphPolygon,
7767 morphpolygon_help, morphpolygon_syntax}
7769 {"PasteBuffer", 0, ActionPasteBuffer,
7770 pastebuffer_help, pastebuffer_syntax}
7772 {"Quit", 0, ActionQuit,
7773 quit_help, quit_syntax}
7775 {"RemoveSelected", 0, ActionRemoveSelected,
7776 removeselected_help, removeselected_syntax}
7778 {"Renumber", 0, ActionRenumber,
7779 renumber_help, renumber_syntax}
7781 {"RipUp", 0, ActionRipUp,
7782 ripup_help, ripup_syntax}
7784 {"Select", 0, ActionSelect,
7785 select_help, select_syntax}
7787 {"Unselect", 0, ActionUnselect,
7788 unselect_help, unselect_syntax}
7790 {"SaveSettings", 0, ActionSaveSettings,
7791 savesettings_help, savesettings_syntax}
7793 {"SaveTo", 0, ActionSaveTo,
7794 saveto_help, saveto_syntax}
7796 {"SetSquare", 0, ActionSetSquare,
7797 setsquare_help, setsquare_syntax}
7799 {"SetOctagon", 0, ActionSetOctagon,
7800 setoctagon_help, setoctagon_syntax}
7802 {"SetThermal", 0, ActionSetThermal,
7803 setthermal_help, setthermal_syntax}
7805 {"SetValue", 0, ActionSetValue,
7806 setvalue_help, setvalue_syntax}
7808 {"ToggleHideName", 0, ActionToggleHideName,
7809 togglehidename_help, togglehidename_syntax}
7811 {"Undo", 0, ActionUndo,
7812 undo_help, undo_syntax}
7814 {"Redo", 0, ActionRedo,
7815 redo_help, redo_syntax}
7817 {"SetSame", "Select item to use attributes from", ActionSetSame,
7818 setsame_help, setsame_syntax}
7820 {"SetFlag", 0, ActionSetFlag,
7821 setflag_help, setflag_syntax}
7823 {"ClrFlag", 0, ActionClrFlag,
7824 clrflag_help, clrflag_syntax}
7826 {"ChangeFlag", 0, ActionChangeFlag,
7827 changeflag_help, changeflag_syntax}
7829 {"Polygon", 0, ActionPolygon,
7830 polygon_help, polygon_syntax}
7832 {"RouteStyle", 0, ActionRouteStyle,
7833 routestyle_help, routestyle_syntax}
7835 {"MoveObject", "Select an Object", ActionMoveObject,
7836 moveobject_help, moveobject_syntax}
7838 {"MoveToCurrentLayer", 0, ActionMoveToCurrentLayer,
7839 movetocurrentlayer_help, movetocurrentlayer_syntax}
7841 {"New", 0, ActionNew,
7842 new_help, new_syntax}
7844 {"pscalib", 0, ActionPSCalib}
7846 {"ElementList", 0, ActionElementList,
7847 elementlist_help, elementlist_syntax}
7849 {"ElementSetAttr", 0, ActionElementSetAttr,
7850 elementsetattr_help, elementsetattr_syntax}
7852 {"ExecCommand", 0, ActionExecCommand,
7853 execcommand_help, execcommand_syntax}
7855 {"Import", 0, ActionImport,
7856 import_help, import_syntax}
7860 REGISTER_ACTIONS (action_action_list)