Rename ResetFound{LinesAndPolygons,PinsViasAndPads} and ResetConnections
[geda-pcb/pcjc2.git] / src / action.c
blob834a27329e2c0514bf097f2638a72b0f61ed1429
1 /*
2 * COPYRIGHT
4 * PCB, interactive printed circuit board design
5 * Copyright (C) 1994,1995,1996 Thomas Nau
6 * Copyright (C) 1997, 1998, 1999, 2000, 2001 Harry Eaton
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 * Contact addresses for paper mail and Email:
23 * Harry Eaton, 6697 Buttonhole Ct, Columbia, MD 21044, USA
24 * haceaton@aplcomm.jhuapl.edu
28 /* action routines for output window
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
35 #include "global.h"
37 #include "action.h"
38 #include "autoplace.h"
39 #include "autoroute.h"
40 #include "buffer.h"
41 #include "change.h"
42 #include "command.h"
43 #include "copy.h"
44 #include "create.h"
45 #include "crosshair.h"
46 #include "data.h"
47 #include "draw.h"
48 #include "error.h"
49 #include "file.h"
50 #include "find.h"
51 #include "hid.h"
52 #include "insert.h"
53 #include "line.h"
54 #include "mymem.h"
55 #include "misc.h"
56 #include "mirror.h"
57 #include "move.h"
58 #include "polygon.h"
59 /*#include "print.h"*/
60 #include "rats.h"
61 #include "remove.h"
62 #include "report.h"
63 #include "rotate.h"
64 #include "rubberband.h"
65 #include "search.h"
66 #include "select.h"
67 #include "set.h"
68 #include "thermal.h"
69 #include "undo.h"
70 #include "rtree.h"
71 #include "macro.h"
72 #include "pcb-printf.h"
74 #include <assert.h>
75 #include <stdlib.h> /* rand() */
77 #ifdef HAVE_LIBDMALLOC
78 #include <dmalloc.h>
79 #endif
81 /* for fork() and friends */
82 #ifdef HAVE_UNISTD_H
83 #include <unistd.h>
84 #endif
86 #ifdef HAVE_SYS_WAIT_H
87 #include <sys/wait.h>
88 #endif
90 /* ---------------------------------------------------------------------------
91 * some local types
93 typedef enum
95 F_AddSelected,
96 F_All,
97 F_AllConnections,
98 F_AllRats,
99 F_AllUnusedPins,
100 F_Arc,
101 F_Arrow,
102 F_Block,
103 F_Description,
104 F_Cancel,
105 F_Center,
106 F_Clear,
107 F_ClearAndRedraw,
108 F_ClearList,
109 F_Close,
110 F_Connection,
111 F_Convert,
112 F_Copy,
113 F_CycleClip,
114 F_CycleCrosshair,
115 F_DeleteRats,
116 F_Drag,
117 F_DrillReport,
118 F_Element,
119 F_ElementByName,
120 F_ElementConnections,
121 F_ElementToBuffer,
122 F_Escape,
123 F_Find,
124 F_FlipElement,
125 F_FoundPins,
126 F_Grid,
127 F_InsertPoint,
128 F_Layer,
129 F_Layout,
130 F_LayoutAs,
131 F_LayoutToBuffer,
132 F_Line,
133 F_LineSize,
134 F_Lock,
135 F_Mirror,
136 F_Move,
137 F_NameOnPCB,
138 F_Netlist,
139 F_NetByName,
140 F_None,
141 F_Notify,
142 F_Object,
143 F_ObjectByName,
144 F_PasteBuffer,
145 F_PadByName,
146 F_PinByName,
147 F_PinOrPadName,
148 F_Pinout,
149 F_Polygon,
150 F_PolygonHole,
151 F_PreviousPoint,
152 F_RatsNest,
153 F_Rectangle,
154 F_Redraw,
155 F_Release,
156 F_Revert,
157 F_Remove,
158 F_RemoveSelected,
159 F_Report,
160 F_Reset,
161 F_ResetLinesAndPolygons,
162 F_ResetPinsViasAndPads,
163 F_Restore,
164 F_Rotate,
165 F_Save,
166 F_Selected,
167 F_SelectedArcs,
168 F_SelectedElements,
169 F_SelectedLines,
170 F_SelectedNames,
171 F_SelectedObjects,
172 F_SelectedPads,
173 F_SelectedPins,
174 F_SelectedTexts,
175 F_SelectedVias,
176 F_SelectedRats,
177 F_Stroke,
178 F_Text,
179 F_TextByName,
180 F_TextScale,
181 F_Thermal,
182 F_ToLayout,
183 F_ToggleAllDirections,
184 F_ToggleAutoDRC,
185 F_ToggleClearLine,
186 F_ToggleFullPoly,
187 F_ToggleGrid,
188 F_ToggleHideNames,
189 F_ToggleMask,
190 F_ToggleName,
191 F_ToggleObject,
192 F_ToggleShowDRC,
193 F_ToggleLiveRoute,
194 F_ToggleRubberBandMode,
195 F_ToggleStartDirection,
196 F_ToggleSnapPin,
197 F_ToggleThindraw,
198 F_ToggleLockNames,
199 F_ToggleOnlyNames,
200 F_ToggleThindrawPoly,
201 F_ToggleOrthoMove,
202 F_ToggleLocalRef,
203 F_ToggleCheckPlanes,
204 F_ToggleUniqueNames,
205 F_Via,
206 F_ViaByName,
207 F_Value,
208 F_ViaDrillingHole,
209 F_ViaSize,
210 F_Zoom
212 FunctionID;
214 typedef struct /* used to identify subfunctions */
216 char *Identifier;
217 FunctionID ID;
219 FunctionType;
221 /* --------------------------------------------------------------------------- */
223 /* %start-doc actions 00delta
225 Many actions take a @code{delta} parameter as the last parameter,
226 which is an amount to change something. That @code{delta} may include
227 units, as an additional parameter, such as @code{Action(Object,5,mm)}.
228 If no units are specified, the default is PCB's native units
229 (currently 1/100 mil). Also, if the delta is prefixed by @code{+} or
230 @code{-}, the size is increased or decreased by that amount.
231 Otherwise, the size size is set to the given amount.
233 @example
234 Action(Object,5,mil)
235 Action(Object,+0.5,mm)
236 Action(Object,-1)
237 @end example
239 Actions which take a @code{delta} parameter which do not accept all
240 these options will specify what they do take.
242 %end-doc */
244 /* %start-doc actions 00objects
246 Many actions act on indicated objects on the board. They will have
247 parameters like @code{ToggleObject} or @code{SelectedVias} to indicate
248 what group of objects they act on. Unless otherwise specified, these
249 parameters are defined as follows:
251 @table @code
253 @item Object
254 @itemx ToggleObject
255 Affects the object under the mouse pointer. If this action is invoked
256 from a menu or script, the user will be prompted to click on an
257 object, which is then the object affected.
259 @item Selected
260 @itemx SelectedObjects
262 Affects all objects which are currently selected. At least, all
263 selected objects for which the given action makes sense.
265 @item SelectedPins
266 @itemx SelectedVias
267 @itemx Selected@var{Type}
268 @itemx @i{etc}
269 Affects all objects which are both selected and of the @var{Type} specified.
271 @end table
273 %end-doc */
275 /* %start-doc actions 00macros
277 @macro pinshapes
279 Pins, pads, and vias can have various shapes. All may be round. Pins
280 and pads may be square (obviously "square" pads are usually
281 rectangular). Pins and vias may be octagonal. When you change a
282 shape flag of an element, you actually change all of its pins and
283 pads.
285 Note that the square flag takes precedence over the octagon flag,
286 thus, if both the square and octagon flags are set, the object is
287 square. When the square flag is cleared, the pins and pads will be
288 either round or, if the octagon flag is set, octagonal.
290 @end macro
292 %end-doc */
294 /* ---------------------------------------------------------------------------
295 * some local identifiers
297 static PointType InsertedPoint;
298 static LayerType *lastLayer;
299 static struct
301 PolygonType *poly;
302 LineType line;
304 fake;
306 static struct
308 Coord X, Y;
309 Cardinal Buffer;
310 bool Click;
311 bool Moving; /* selected type clicked on */
312 int Hit; /* move type clicked on */
313 void *ptr1;
314 void *ptr2;
315 void *ptr3;
317 Note;
319 static int defer_updates = 0;
320 static int defer_needs_update = 0;
322 static Cardinal polyIndex = 0;
323 static bool saved_mode = false;
324 #ifdef HAVE_LIBSTROKE
325 static bool 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 {"NetByName", F_NetByName},
374 {"None", F_None},
375 {"Notify", F_Notify},
376 {"Object", F_Object},
377 {"ObjectByName", F_ObjectByName},
378 {"PasteBuffer", F_PasteBuffer},
379 {"PadByName", F_PadByName},
380 {"PinByName", F_PinByName},
381 {"PinOrPadName", F_PinOrPadName},
382 {"Pinout", F_Pinout},
383 {"Polygon", F_Polygon},
384 {"PolygonHole", F_PolygonHole},
385 {"PreviousPoint", F_PreviousPoint},
386 {"RatsNest", F_RatsNest},
387 {"Rectangle", F_Rectangle},
388 {"Redraw", F_Redraw},
389 {"Release", F_Release},
390 {"Remove", F_Remove},
391 {"RemoveSelected", F_RemoveSelected},
392 {"Report", F_Report},
393 {"Reset", F_Reset},
394 {"ResetLinesAndPolygons", F_ResetLinesAndPolygons},
395 {"ResetPinsViasAndPads", F_ResetPinsViasAndPads},
396 {"Restore", F_Restore},
397 {"Revert", F_Revert},
398 {"Rotate", F_Rotate},
399 {"Save", F_Save},
400 {"Selected", F_Selected},
401 {"SelectedArcs", F_SelectedArcs},
402 {"SelectedElements", F_SelectedElements},
403 {"SelectedLines", F_SelectedLines},
404 {"SelectedNames", F_SelectedNames},
405 {"SelectedObjects", F_SelectedObjects},
406 {"SelectedPins", F_SelectedPins},
407 {"SelectedPads", F_SelectedPads},
408 {"SelectedRats", F_SelectedRats},
409 {"SelectedTexts", F_SelectedTexts},
410 {"SelectedVias", F_SelectedVias},
411 {"Stroke", F_Stroke},
412 {"Text", F_Text},
413 {"TextByName", F_TextByName},
414 {"TextScale", F_TextScale},
415 {"Thermal", F_Thermal},
416 {"ToLayout", F_ToLayout},
417 {"Toggle45Degree", F_ToggleAllDirections},
418 {"ToggleClearLine", F_ToggleClearLine},
419 {"ToggleFullPoly", F_ToggleFullPoly},
420 {"ToggleGrid", F_ToggleGrid},
421 {"ToggleMask", F_ToggleMask},
422 {"ToggleName", F_ToggleName},
423 {"ToggleObject", F_ToggleObject},
424 {"ToggleRubberBandMode", F_ToggleRubberBandMode},
425 {"ToggleStartDirection", F_ToggleStartDirection},
426 {"ToggleSnapPin", F_ToggleSnapPin},
427 {"ToggleThindraw", F_ToggleThindraw},
428 {"ToggleThindrawPoly", F_ToggleThindrawPoly},
429 {"ToggleLockNames", F_ToggleLockNames},
430 {"ToggleOnlyNames", F_ToggleOnlyNames},
431 {"ToggleHideNames", F_ToggleHideNames},
432 {"ToggleCheckPlanes", F_ToggleCheckPlanes},
433 {"ToggleLocalRef", F_ToggleLocalRef},
434 {"ToggleOrthoMove", F_ToggleOrthoMove},
435 {"ToggleShowDRC", F_ToggleShowDRC},
436 {"ToggleLiveRoute", F_ToggleLiveRoute},
437 {"ToggleAutoDRC", F_ToggleAutoDRC},
438 {"ToggleUniqueNames", F_ToggleUniqueNames},
439 {"Value", F_Value},
440 {"Via", F_Via},
441 {"ViaByName", F_ViaByName},
442 {"ViaSize", F_ViaSize},
443 {"ViaDrillingHole", F_ViaDrillingHole},
444 {"Zoom", F_Zoom}
447 /* ---------------------------------------------------------------------------
448 * some local routines
450 static int GetFunctionID (String);
451 static void AdjustAttachedBox (void);
452 static void NotifyLine (void);
453 static void NotifyBlock (void);
454 static void NotifyMode (void);
455 static void ClearWarnings (void);
456 #ifdef HAVE_LIBSTROKE
457 static void FinishStroke (void);
458 extern void stroke_init (void);
459 extern void stroke_record (int x, int y);
460 extern int stroke_trans (char *s);
461 #endif
462 static void ChangeFlag (char *, char *, int, char *);
464 #define ARG(n) (argc > (n) ? argv[n] : NULL)
466 #ifdef HAVE_LIBSTROKE
468 /* ---------------------------------------------------------------------------
469 * FinishStroke - try to recognize the stroke sent
471 void
472 FinishStroke (void)
474 char msg[255];
475 int type;
476 unsigned long num;
477 void *ptr1, *ptr2, *ptr3;
479 mid_stroke = false;
480 if (stroke_trans (msg))
482 num = atoi (msg);
483 switch (num)
485 case 456:
486 if (Settings.Mode == LINE_MODE)
488 SetMode (LINE_MODE);
490 break;
491 case 9874123:
492 case 74123:
493 case 987412:
494 case 8741236:
495 case 874123:
496 RotateScreenObject (StrokeBox.X1, StrokeBox.Y1, SWAP_IDENT ? 1 : 3);
497 break;
498 case 7896321:
499 case 786321:
500 case 789632:
501 case 896321:
502 RotateScreenObject (StrokeBox.X1, StrokeBox.Y1, SWAP_IDENT ? 3 : 1);
503 break;
504 case 258:
505 SetMode (LINE_MODE);
506 break;
507 case 852:
508 SetMode (ARROW_MODE);
509 break;
510 case 1478963:
511 ActionUndo ("");
512 break;
513 case 147423:
514 case 147523:
515 case 1474123:
516 Redo (true);
517 break;
518 case 148963:
519 case 147863:
520 case 147853:
521 case 145863:
522 SetMode (VIA_MODE);
523 break;
524 case 951:
525 case 9651:
526 case 9521:
527 case 9621:
528 case 9851:
529 case 9541:
530 case 96521:
531 case 96541:
532 case 98541:
533 /* XXX: FIXME: Call a zoom-extents action */
534 break;
535 case 159:
536 case 1269:
537 case 1259:
538 case 1459:
539 case 1569:
540 case 1589:
541 case 12569:
542 case 12589:
543 case 14589:
544 /* XXX: FIXME: Zoom to fit the box StrokeBox.[X1,Y1] - StrokeBox.[X2,Y2] */
545 break;
547 default:
548 Message (_("Unknown stroke %s\n"), msg);
549 break;
552 else
553 gui->beep ();
555 #endif
557 /* ---------------------------------------------------------------------------
558 * Clear warning color from pins/pads
560 static void
561 ClearWarnings ()
563 Settings.RatWarn = false;
564 ALLPIN_LOOP (PCB->Data);
566 if (TEST_FLAG (WARNFLAG, pin))
568 CLEAR_FLAG (WARNFLAG, pin);
569 DrawPin (pin);
572 ENDALL_LOOP;
573 ALLPAD_LOOP (PCB->Data);
575 if (TEST_FLAG (WARNFLAG, pad))
577 CLEAR_FLAG (WARNFLAG, pad);
578 DrawPad (pad);
581 ENDALL_LOOP;
582 Draw ();
585 static void
586 click_cb (hidval hv)
588 if (Note.Click)
590 notify_crosshair_change (false);
591 Note.Click = false;
592 if (Note.Moving && !gui->shift_is_pressed ())
594 Note.Buffer = Settings.BufferNumber;
595 SetBufferNumber (MAX_BUFFER - 1);
596 ClearBuffer (PASTEBUFFER);
597 AddSelectedToBuffer (PASTEBUFFER, Note.X, Note.Y, true);
598 SaveUndoSerialNumber ();
599 RemoveSelected ();
600 SaveMode ();
601 saved_mode = true;
602 SetMode (PASTEBUFFER_MODE);
604 else if (Note.Hit && !gui->shift_is_pressed ())
606 SaveMode ();
607 saved_mode = true;
608 SetMode (gui->control_is_pressed ()? COPY_MODE : MOVE_MODE);
609 Crosshair.AttachedObject.Ptr1 = Note.ptr1;
610 Crosshair.AttachedObject.Ptr2 = Note.ptr2;
611 Crosshair.AttachedObject.Ptr3 = Note.ptr3;
612 Crosshair.AttachedObject.Type = Note.Hit;
613 AttachForCopy (Note.X, Note.Y);
615 else
617 BoxType box;
619 Note.Hit = 0;
620 Note.Moving = false;
621 SaveUndoSerialNumber ();
622 box.X1 = -MAX_COORD;
623 box.Y1 = -MAX_COORD;
624 box.X2 = MAX_COORD;
625 box.Y2 = MAX_COORD;
626 /* unselect first if shift key not down */
627 if (!gui->shift_is_pressed () && SelectBlock (&box, false))
628 SetChangedFlag (true);
629 NotifyBlock ();
630 Crosshair.AttachedBox.Point1.X = Note.X;
631 Crosshair.AttachedBox.Point1.Y = Note.Y;
633 notify_crosshair_change (true);
637 static void
638 ReleaseMode (void)
640 BoxType box;
642 if (Note.Click)
644 BoxType box;
646 box.X1 = -MAX_COORD;
647 box.Y1 = -MAX_COORD;
648 box.X2 = MAX_COORD;
649 box.Y2 = MAX_COORD;
651 Note.Click = false; /* inhibit timer action */
652 SaveUndoSerialNumber ();
653 /* unselect first if shift key not down */
654 if (!gui->shift_is_pressed ())
656 if (SelectBlock (&box, false))
657 SetChangedFlag (true);
658 if (Note.Moving)
660 Note.Moving = 0;
661 Note.Hit = 0;
662 return;
665 RestoreUndoSerialNumber ();
666 if (SelectObject ())
667 SetChangedFlag (true);
668 Note.Hit = 0;
669 Note.Moving = 0;
671 else if (Note.Moving)
673 RestoreUndoSerialNumber ();
674 NotifyMode ();
675 ClearBuffer (PASTEBUFFER);
676 SetBufferNumber (Note.Buffer);
677 Note.Moving = false;
678 Note.Hit = 0;
680 else if (Note.Hit)
682 NotifyMode ();
683 Note.Hit = 0;
685 else if (Settings.Mode == ARROW_MODE)
687 box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
688 Crosshair.AttachedBox.Point2.X);
689 box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
690 Crosshair.AttachedBox.Point2.Y);
691 box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
692 Crosshair.AttachedBox.Point2.X);
693 box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
694 Crosshair.AttachedBox.Point2.Y);
695 RestoreUndoSerialNumber ();
696 if (SelectBlock (&box, true))
697 SetChangedFlag (true);
698 else if (Bumped)
699 IncrementUndoSerialNumber ();
700 Crosshair.AttachedBox.State = STATE_FIRST;
702 if (saved_mode)
703 RestoreMode ();
704 saved_mode = false;
707 /* ---------------------------------------------------------------------------
708 * get function ID of passed string
710 #define HSIZE 257
711 static char function_hash[HSIZE];
712 static int hash_initted = 0;
714 static int
715 hashfunc(String s)
717 int i = 0;
718 while (*s)
720 i ^= i >> 16;
721 i = (i * 13) ^ (unsigned char)tolower((int) *s);
722 s ++;
724 i = (unsigned int)i % HSIZE;
725 return i;
728 static int
729 GetFunctionID (String Ident)
731 int i, h;
733 if (Ident == 0)
734 return -1;
736 if (!hash_initted)
738 hash_initted = 1;
739 if (HSIZE < ENTRIES (Functions) * 2)
741 fprintf(stderr, _("Error: function hash size too small (%d vs %lu at %s:%d)\n"),
742 HSIZE, (unsigned long) ENTRIES (Functions)*2, __FILE__, __LINE__);
743 exit(1);
745 if (ENTRIES (Functions) > 254)
747 /* Change 'char' to 'int' and remove this when we get to 256
748 strings to hash. */
749 fprintf(stderr, _("Error: function hash type too small (%d vs %lu at %s:%d)\n"),
750 256, (unsigned long) ENTRIES (Functions), __FILE__, __LINE__);
751 exit(1);
754 for (i=ENTRIES (Functions)-1; i>=0; i--)
756 h = hashfunc (Functions[i].Identifier);
757 while (function_hash[h])
758 h = (h + 1) % HSIZE;
759 function_hash[h] = i + 1;
763 i = hashfunc (Ident);
764 while (1)
766 /* We enforce the "hash table bigger than function table" rule,
767 so we know there will be at least one zero entry to find. */
768 if (!function_hash[i])
769 return (-1);
770 if (!strcasecmp (Ident, Functions[function_hash[i]-1].Identifier))
771 return ((int) Functions[function_hash[i]-1].ID);
772 i = (i + 1) % HSIZE;
776 /* ---------------------------------------------------------------------------
777 * set new coordinates if in 'RECTANGLE' mode
778 * the cursor shape is also adjusted
780 static void
781 AdjustAttachedBox (void)
783 if (Settings.Mode == ARC_MODE)
785 Crosshair.AttachedBox.otherway = gui->shift_is_pressed ();
786 return;
788 switch (Crosshair.AttachedBox.State)
790 case STATE_SECOND: /* one corner is selected */
792 /* update coordinates */
793 Crosshair.AttachedBox.Point2.X = Crosshair.X;
794 Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
795 break;
800 /* ---------------------------------------------------------------------------
801 * adjusts the objects which are to be created like attached lines...
803 void
804 AdjustAttachedObjects (void)
806 PointType *pnt;
807 switch (Settings.Mode)
809 /* update at least an attached block (selection) */
810 case NO_MODE:
811 case ARROW_MODE:
812 if (Crosshair.AttachedBox.State)
814 Crosshair.AttachedBox.Point2.X = Crosshair.X;
815 Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
817 break;
819 /* rectangle creation mode */
820 case RECTANGLE_MODE:
821 case ARC_MODE:
822 AdjustAttachedBox ();
823 break;
825 /* polygon creation mode */
826 case POLYGON_MODE:
827 case POLYGONHOLE_MODE:
828 AdjustAttachedLine ();
829 break;
830 /* line creation mode */
831 case LINE_MODE:
832 if (PCB->RatDraw || PCB->Clipping == 0)
833 AdjustAttachedLine ();
834 else
835 AdjustTwoLine (PCB->Clipping - 1);
836 break;
837 /* point insertion mode */
838 case INSERTPOINT_MODE:
839 pnt = AdjustInsertPoint ();
840 if (pnt)
841 InsertedPoint = *pnt;
842 break;
843 case ROTATE_MODE:
844 break;
848 /* ---------------------------------------------------------------------------
849 * creates points of a line
851 static void
852 NotifyLine (void)
854 int type = NO_TYPE;
855 void *ptr1, *ptr2, *ptr3;
857 if (!Marked.status || TEST_FLAG (LOCALREFFLAG, PCB))
858 SetLocalRef (Crosshair.X, Crosshair.Y, true);
859 switch (Crosshair.AttachedLine.State)
861 case STATE_FIRST: /* first point */
862 if (PCB->RatDraw && SearchScreen (Crosshair.X, Crosshair.Y,
863 PAD_TYPE | PIN_TYPE, &ptr1, &ptr1,
864 &ptr1) == NO_TYPE)
866 gui->beep ();
867 break;
869 if (TEST_FLAG (AUTODRCFLAG, PCB) && Settings.Mode == LINE_MODE)
871 type = SearchScreen (Crosshair.X, Crosshair.Y,
872 PIN_TYPE | PAD_TYPE | VIA_TYPE, &ptr1, &ptr2,
873 &ptr3);
874 LookupConnection (Crosshair.X, Crosshair.Y, true, 1,
875 FOUNDFLAG, true);
877 if (type == PIN_TYPE || type == VIA_TYPE)
879 Crosshair.AttachedLine.Point1.X =
880 Crosshair.AttachedLine.Point2.X = ((PinType *) ptr2)->X;
881 Crosshair.AttachedLine.Point1.Y =
882 Crosshair.AttachedLine.Point2.Y = ((PinType *) ptr2)->Y;
884 else if (type == PAD_TYPE)
886 PadType *pad = (PadType *) ptr2;
887 double d1 = Distance (Crosshair.X, Crosshair.Y, pad->Point1.X, pad->Point1.Y);
888 double d2 = Distance (Crosshair.X, Crosshair.Y, pad->Point2.X, pad->Point2.Y);
889 if (d2 < d1)
891 Crosshair.AttachedLine.Point1 =
892 Crosshair.AttachedLine.Point2 = pad->Point2;
894 else
896 Crosshair.AttachedLine.Point1 =
897 Crosshair.AttachedLine.Point2 = pad->Point1;
900 else
902 Crosshair.AttachedLine.Point1.X =
903 Crosshair.AttachedLine.Point2.X = Crosshair.X;
904 Crosshair.AttachedLine.Point1.Y =
905 Crosshair.AttachedLine.Point2.Y = Crosshair.Y;
907 Crosshair.AttachedLine.State = STATE_SECOND;
908 break;
910 case STATE_SECOND:
911 /* fall through to third state too */
912 lastLayer = CURRENT;
913 default: /* all following points */
914 Crosshair.AttachedLine.State = STATE_THIRD;
915 break;
919 /* ---------------------------------------------------------------------------
920 * create first or second corner of a marked block
922 static void
923 NotifyBlock (void)
925 notify_crosshair_change (false);
926 switch (Crosshair.AttachedBox.State)
928 case STATE_FIRST: /* setup first point */
929 Crosshair.AttachedBox.Point1.X =
930 Crosshair.AttachedBox.Point2.X = Crosshair.X;
931 Crosshair.AttachedBox.Point1.Y =
932 Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
933 Crosshair.AttachedBox.State = STATE_SECOND;
934 break;
936 case STATE_SECOND: /* setup second point */
937 Crosshair.AttachedBox.State = STATE_THIRD;
938 break;
940 notify_crosshair_change (true);
944 /* ---------------------------------------------------------------------------
946 * does what's appropriate for the current mode setting. This normally
947 * means creation of an object at the current crosshair location.
949 * new created objects are added to the create undo list of course
951 static void
952 NotifyMode (void)
954 void *ptr1, *ptr2, *ptr3;
955 int type;
957 if (Settings.RatWarn)
958 ClearWarnings ();
959 switch (Settings.Mode)
961 case ARROW_MODE:
963 int test;
964 hidval hv;
966 Note.Click = true;
967 /* do something after click time */
968 gui->add_timer (click_cb, CLICK_TIME, hv);
970 /* see if we clicked on something already selected
971 * (Note.Moving) or clicked on a MOVE_TYPE
972 * (Note.Hit)
974 for (test = (SELECT_TYPES | MOVE_TYPES) & ~RATLINE_TYPE;
975 test; test &= ~type)
977 type = SearchScreen (Note.X, Note.Y, test, &ptr1, &ptr2, &ptr3);
978 if (!Note.Hit && (type & MOVE_TYPES) &&
979 !TEST_FLAG (LOCKFLAG, (PinType *) ptr2))
981 Note.Hit = type;
982 Note.ptr1 = ptr1;
983 Note.ptr2 = ptr2;
984 Note.ptr3 = ptr3;
986 if (!Note.Moving && (type & SELECT_TYPES) &&
987 TEST_FLAG (SELECTEDFLAG, (PinType *) ptr2))
988 Note.Moving = true;
989 if ((Note.Hit && Note.Moving) || type == NO_TYPE)
990 break;
992 break;
995 case VIA_MODE:
997 PinType *via;
999 if (!PCB->ViaOn)
1001 Message (_("You must turn via visibility on before\n"
1002 "you can place vias\n"));
1003 break;
1005 if ((via = CreateNewVia (PCB->Data, Note.X, Note.Y,
1006 Settings.ViaThickness, 2 * Settings.Keepaway,
1007 0, Settings.ViaDrillingHole, NULL,
1008 NoFlags ())) != NULL)
1010 AddObjectToCreateUndoList (VIA_TYPE, via, via, via);
1011 if (gui->shift_is_pressed ())
1012 ChangeObjectThermal (VIA_TYPE, via, via, via, PCB->ThermStyle);
1013 IncrementUndoSerialNumber ();
1014 DrawVia (via);
1015 Draw ();
1017 break;
1020 case ARC_MODE:
1022 switch (Crosshair.AttachedBox.State)
1024 case STATE_FIRST:
1025 Crosshair.AttachedBox.Point1.X =
1026 Crosshair.AttachedBox.Point2.X = Note.X;
1027 Crosshair.AttachedBox.Point1.Y =
1028 Crosshair.AttachedBox.Point2.Y = Note.Y;
1029 Crosshair.AttachedBox.State = STATE_SECOND;
1030 break;
1032 case STATE_SECOND:
1033 case STATE_THIRD:
1035 ArcType *arc;
1036 Coord wx, wy;
1037 Angle sa, dir;
1039 wx = Note.X - Crosshair.AttachedBox.Point1.X;
1040 wy = Note.Y - Crosshair.AttachedBox.Point1.Y;
1041 if (XOR (Crosshair.AttachedBox.otherway, abs (wy) > abs (wx)))
1043 Crosshair.AttachedBox.Point2.X =
1044 Crosshair.AttachedBox.Point1.X + abs (wy) * SGNZ (wx);
1045 sa = (wx >= 0) ? 0 : 180;
1046 #ifdef ARC45
1047 if (abs (wy) / 2 >= abs (wx))
1048 dir = (SGNZ (wx) == SGNZ (wy)) ? 45 : -45;
1049 else
1050 #endif
1051 dir = (SGNZ (wx) == SGNZ (wy)) ? 90 : -90;
1053 else
1055 Crosshair.AttachedBox.Point2.Y =
1056 Crosshair.AttachedBox.Point1.Y + abs (wx) * SGNZ (wy);
1057 sa = (wy >= 0) ? -90 : 90;
1058 #ifdef ARC45
1059 if (abs (wx) / 2 >= abs (wy))
1060 dir = (SGNZ (wx) == SGNZ (wy)) ? -45 : 45;
1061 else
1062 #endif
1063 dir = (SGNZ (wx) == SGNZ (wy)) ? -90 : 90;
1064 wy = wx;
1066 if (abs (wy) > 0 && (arc = CreateNewArcOnLayer (CURRENT,
1067 Crosshair.
1068 AttachedBox.
1069 Point2.X,
1070 Crosshair.
1071 AttachedBox.
1072 Point2.Y,
1073 abs (wy),
1074 abs (wy),
1076 dir,
1077 Settings.
1078 LineThickness,
1079 2 * Settings.
1080 Keepaway,
1081 MakeFlags
1082 (TEST_FLAG
1083 (CLEARNEWFLAG,
1084 PCB) ?
1085 CLEARLINEFLAG :
1086 0))))
1088 BoxType *bx;
1090 bx = GetArcEnds (arc);
1091 Crosshair.AttachedBox.Point1.X =
1092 Crosshair.AttachedBox.Point2.X = bx->X2;
1093 Crosshair.AttachedBox.Point1.Y =
1094 Crosshair.AttachedBox.Point2.Y = bx->Y2;
1095 AddObjectToCreateUndoList (ARC_TYPE, CURRENT, arc, arc);
1096 IncrementUndoSerialNumber ();
1097 addedLines++;
1098 DrawArc (CURRENT, arc);
1099 Draw ();
1100 Crosshair.AttachedBox.State = STATE_THIRD;
1102 break;
1105 break;
1107 case LOCK_MODE:
1109 type = SearchScreen (Note.X, Note.Y, LOCK_TYPES, &ptr1, &ptr2, &ptr3);
1110 if (type == ELEMENT_TYPE)
1112 ElementType *element = (ElementType *) ptr2;
1114 TOGGLE_FLAG (LOCKFLAG, element);
1115 PIN_LOOP (element);
1117 TOGGLE_FLAG (LOCKFLAG, pin);
1118 CLEAR_FLAG (SELECTEDFLAG, pin);
1120 END_LOOP;
1121 PAD_LOOP (element);
1123 TOGGLE_FLAG (LOCKFLAG, pad);
1124 CLEAR_FLAG (SELECTEDFLAG, pad);
1126 END_LOOP;
1127 CLEAR_FLAG (SELECTEDFLAG, element);
1128 /* always re-draw it since I'm too lazy
1129 * to tell if a selected flag changed
1131 DrawElement (element);
1132 Draw ();
1133 SetChangedFlag (true);
1134 hid_actionl ("Report", "Object", NULL);
1136 else if (type != NO_TYPE)
1138 TextType *thing = (TextType *) ptr3;
1139 TOGGLE_FLAG (LOCKFLAG, thing);
1140 if (TEST_FLAG (LOCKFLAG, thing)
1141 && TEST_FLAG (SELECTEDFLAG, thing))
1143 /* this is not un-doable since LOCK isn't */
1144 CLEAR_FLAG (SELECTEDFLAG, thing);
1145 DrawObject (type, ptr1, ptr2);
1146 Draw ();
1148 SetChangedFlag (true);
1149 hid_actionl ("Report", "Object", NULL);
1151 break;
1153 case THERMAL_MODE:
1155 if (((type
1157 SearchScreen (Note.X, Note.Y, PIN_TYPES, &ptr1, &ptr2,
1158 &ptr3)) != NO_TYPE)
1159 && !TEST_FLAG (HOLEFLAG, (PinType *) ptr3))
1161 if (gui->shift_is_pressed ())
1163 int tstyle = GET_THERM (INDEXOFCURRENT, (PinType *) ptr3);
1164 tstyle++;
1165 if (tstyle > 5)
1166 tstyle = 1;
1167 ChangeObjectThermal (type, ptr1, ptr2, ptr3, tstyle);
1169 else if (GET_THERM (INDEXOFCURRENT, (PinType *) ptr3))
1170 ChangeObjectThermal (type, ptr1, ptr2, ptr3, 0);
1171 else
1172 ChangeObjectThermal (type, ptr1, ptr2, ptr3, PCB->ThermStyle);
1174 break;
1177 case LINE_MODE:
1178 /* do update of position */
1179 NotifyLine ();
1180 if (Crosshair.AttachedLine.State != STATE_THIRD)
1181 break;
1183 /* Remove anchor if clicking on start point;
1184 * this means we can't paint 0 length lines
1185 * which could be used for square SMD pads.
1186 * Instead use a very small delta, or change
1187 * the file after saving.
1189 if (Crosshair.X == Crosshair.AttachedLine.Point1.X
1190 && Crosshair.Y == Crosshair.AttachedLine.Point1.Y)
1192 SetMode (LINE_MODE);
1193 break;
1196 if (PCB->RatDraw)
1198 RatType *line;
1199 if ((line = AddNet ()))
1201 addedLines++;
1202 AddObjectToCreateUndoList (RATLINE_TYPE, line, line, line);
1203 IncrementUndoSerialNumber ();
1204 DrawRat (line);
1205 Crosshair.AttachedLine.Point1.X =
1206 Crosshair.AttachedLine.Point2.X;
1207 Crosshair.AttachedLine.Point1.Y =
1208 Crosshair.AttachedLine.Point2.Y;
1209 Draw ();
1211 break;
1213 else
1214 /* create line if both ends are determined && length != 0 */
1216 LineType *line;
1217 int maybe_found_flag;
1219 if (PCB->Clipping
1220 && Crosshair.AttachedLine.Point1.X ==
1221 Crosshair.AttachedLine.Point2.X
1222 && Crosshair.AttachedLine.Point1.Y ==
1223 Crosshair.AttachedLine.Point2.Y
1224 && (Crosshair.AttachedLine.Point2.X != Note.X
1225 || Crosshair.AttachedLine.Point2.Y != Note.Y))
1227 /* We will only need to paint the second line segment.
1228 Since we only check for vias on the first segment,
1229 swap them so the non-empty segment is the first segment. */
1230 Crosshair.AttachedLine.Point2.X = Note.X;
1231 Crosshair.AttachedLine.Point2.Y = Note.Y;
1234 if (TEST_FLAG (AUTODRCFLAG, PCB)
1235 && ! TEST_SILK_LAYER (CURRENT))
1236 maybe_found_flag = FOUNDFLAG;
1237 else
1238 maybe_found_flag = 0;
1240 if ((Crosshair.AttachedLine.Point1.X !=
1241 Crosshair.AttachedLine.Point2.X
1242 || Crosshair.AttachedLine.Point1.Y !=
1243 Crosshair.AttachedLine.Point2.Y)
1244 && (line =
1245 CreateDrawnLineOnLayer (CURRENT,
1246 Crosshair.AttachedLine.Point1.X,
1247 Crosshair.AttachedLine.Point1.Y,
1248 Crosshair.AttachedLine.Point2.X,
1249 Crosshair.AttachedLine.Point2.Y,
1250 Settings.LineThickness,
1251 2 * Settings.Keepaway,
1252 MakeFlags (maybe_found_flag |
1253 (TEST_FLAG
1254 (CLEARNEWFLAG,
1255 PCB) ? CLEARLINEFLAG :
1256 0)))) != NULL)
1258 PinType *via;
1260 addedLines++;
1261 AddObjectToCreateUndoList (LINE_TYPE, CURRENT, line, line);
1262 DrawLine (CURRENT, line);
1263 /* place a via if vias are visible, the layer is
1264 in a new group since the last line and there
1265 isn't a pin already here */
1266 if (PCB->ViaOn && GetLayerGroupNumberByPointer (CURRENT) !=
1267 GetLayerGroupNumberByPointer (lastLayer) &&
1268 SearchObjectByLocation (PIN_TYPES, &ptr1, &ptr2, &ptr3,
1269 Crosshair.AttachedLine.Point1.X,
1270 Crosshair.AttachedLine.Point1.Y,
1271 Settings.ViaThickness / 2) ==
1272 NO_TYPE
1273 && (via =
1274 CreateNewVia (PCB->Data,
1275 Crosshair.AttachedLine.Point1.X,
1276 Crosshair.AttachedLine.Point1.Y,
1277 Settings.ViaThickness,
1278 2 * Settings.Keepaway, 0,
1279 Settings.ViaDrillingHole, NULL,
1280 NoFlags ())) != NULL)
1282 AddObjectToCreateUndoList (VIA_TYPE, via, via, via);
1283 DrawVia (via);
1285 /* copy the coordinates */
1286 Crosshair.AttachedLine.Point1.X =
1287 Crosshair.AttachedLine.Point2.X;
1288 Crosshair.AttachedLine.Point1.Y =
1289 Crosshair.AttachedLine.Point2.Y;
1290 IncrementUndoSerialNumber ();
1291 lastLayer = CURRENT;
1293 if (PCB->Clipping && (Note.X != Crosshair.AttachedLine.Point2.X
1294 || Note.Y !=
1295 Crosshair.AttachedLine.Point2.Y)
1296 && (line =
1297 CreateDrawnLineOnLayer (CURRENT,
1298 Crosshair.AttachedLine.Point2.X,
1299 Crosshair.AttachedLine.Point2.Y,
1300 Note.X, Note.Y,
1301 Settings.LineThickness,
1302 2 * Settings.Keepaway,
1303 MakeFlags ((TEST_FLAG
1304 (AUTODRCFLAG,
1305 PCB) ? FOUNDFLAG : 0) |
1306 (TEST_FLAG
1307 (CLEARNEWFLAG,
1308 PCB) ? CLEARLINEFLAG :
1309 0)))) != NULL)
1311 addedLines++;
1312 AddObjectToCreateUndoList (LINE_TYPE, CURRENT, line, line);
1313 IncrementUndoSerialNumber ();
1314 DrawLine (CURRENT, line);
1315 /* move to new start point */
1316 Crosshair.AttachedLine.Point1.X = Note.X;
1317 Crosshair.AttachedLine.Point1.Y = Note.Y;
1318 Crosshair.AttachedLine.Point2.X = Note.X;
1319 Crosshair.AttachedLine.Point2.Y = Note.Y;
1320 if (TEST_FLAG (SWAPSTARTDIRFLAG, PCB))
1322 PCB->Clipping ^= 3;
1325 Draw ();
1327 break;
1329 case RECTANGLE_MODE:
1330 /* do update of position */
1331 NotifyBlock ();
1333 /* create rectangle if both corners are determined
1334 * and width, height are != 0
1336 if (Crosshair.AttachedBox.State == STATE_THIRD &&
1337 Crosshair.AttachedBox.Point1.X != Crosshair.AttachedBox.Point2.X &&
1338 Crosshair.AttachedBox.Point1.Y != Crosshair.AttachedBox.Point2.Y)
1340 PolygonType *polygon;
1342 int flags = CLEARPOLYFLAG;
1343 if (TEST_FLAG (NEWFULLPOLYFLAG, PCB))
1344 flags |= FULLPOLYFLAG;
1345 if ((polygon = CreateNewPolygonFromRectangle (CURRENT,
1346 Crosshair.
1347 AttachedBox.Point1.X,
1348 Crosshair.
1349 AttachedBox.Point1.Y,
1350 Crosshair.
1351 AttachedBox.Point2.X,
1352 Crosshair.
1353 AttachedBox.Point2.Y,
1354 MakeFlags
1355 (flags))) !=
1356 NULL)
1358 AddObjectToCreateUndoList (POLYGON_TYPE, CURRENT,
1359 polygon, polygon);
1360 IncrementUndoSerialNumber ();
1361 DrawPolygon (CURRENT, polygon);
1362 Draw ();
1365 /* reset state to 'first corner' */
1366 Crosshair.AttachedBox.State = STATE_FIRST;
1368 break;
1370 case TEXT_MODE:
1372 char *string;
1374 if ((string = gui->prompt_for (_("Enter text:"), "")) != NULL)
1376 if (strlen(string) > 0)
1378 TextType *text;
1379 int flag = CLEARLINEFLAG;
1381 if (GetLayerGroupNumberByNumber (INDEXOFCURRENT) ==
1382 GetLayerGroupNumberByNumber (solder_silk_layer))
1383 flag |= ONSOLDERFLAG;
1384 if ((text = CreateNewText (CURRENT, &PCB->Font, Note.X,
1385 Note.Y, 0, Settings.TextScale,
1386 string, MakeFlags (flag))) != NULL)
1388 AddObjectToCreateUndoList (TEXT_TYPE, CURRENT, text, text);
1389 IncrementUndoSerialNumber ();
1390 DrawText (CURRENT, text);
1391 Draw ();
1394 free (string);
1396 break;
1399 case POLYGON_MODE:
1401 PointType *points = Crosshair.AttachedPolygon.Points;
1402 Cardinal n = Crosshair.AttachedPolygon.PointN;
1404 /* do update of position; use the 'LINE_MODE' mechanism */
1405 NotifyLine ();
1407 /* check if this is the last point of a polygon */
1408 if (n >= 3 &&
1409 points->X == Crosshair.AttachedLine.Point2.X &&
1410 points->Y == Crosshair.AttachedLine.Point2.Y)
1412 CopyAttachedPolygonToLayer ();
1413 Draw ();
1414 break;
1417 /* create new point if it's the first one or if it's
1418 * different to the last one
1420 if (!n ||
1421 points[n - 1].X != Crosshair.AttachedLine.Point2.X ||
1422 points[n - 1].Y != Crosshair.AttachedLine.Point2.Y)
1424 CreateNewPointInPolygon (&Crosshair.AttachedPolygon,
1425 Crosshair.AttachedLine.Point2.X,
1426 Crosshair.AttachedLine.Point2.Y);
1428 /* copy the coordinates */
1429 Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X;
1430 Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y;
1432 break;
1435 case POLYGONHOLE_MODE:
1437 switch (Crosshair.AttachedObject.State)
1439 /* first notify, lookup object */
1440 case STATE_FIRST:
1441 Crosshair.AttachedObject.Type =
1442 SearchScreen (Note.X, Note.Y, POLYGON_TYPE,
1443 &Crosshair.AttachedObject.Ptr1,
1444 &Crosshair.AttachedObject.Ptr2,
1445 &Crosshair.AttachedObject.Ptr3);
1447 if (Crosshair.AttachedObject.Type != NO_TYPE)
1449 if (TEST_FLAG (LOCKFLAG, (PolygonType *)
1450 Crosshair.AttachedObject.Ptr2))
1452 Message (_("Sorry, the object is locked\n"));
1453 Crosshair.AttachedObject.Type = NO_TYPE;
1454 break;
1456 else
1457 Crosshair.AttachedObject.State = STATE_SECOND;
1459 break;
1461 /* second notify, insert new point into object */
1462 case STATE_SECOND:
1464 PointType *points = Crosshair.AttachedPolygon.Points;
1465 Cardinal n = Crosshair.AttachedPolygon.PointN;
1466 POLYAREA *original, *new_hole, *result;
1467 FlagType Flags;
1469 /* do update of position; use the 'LINE_MODE' mechanism */
1470 NotifyLine ();
1472 /* check if this is the last point of a polygon */
1473 if (n >= 3 &&
1474 points->X == Crosshair.AttachedLine.Point2.X &&
1475 points->Y == Crosshair.AttachedLine.Point2.Y)
1477 /* Create POLYAREAs from the original polygon
1478 * and the new hole polygon */
1479 original = PolygonToPoly ((PolygonType *)Crosshair.AttachedObject.Ptr2);
1480 new_hole = PolygonToPoly (&Crosshair.AttachedPolygon);
1482 /* Subtract the hole from the original polygon shape */
1483 poly_Boolean_free (original, new_hole, &result, PBO_SUB);
1485 /* Convert the resulting polygon(s) into a new set of nodes
1486 * and place them on the page. Delete the original polygon.
1488 SaveUndoSerialNumber ();
1489 Flags = ((PolygonType *)Crosshair.AttachedObject.Ptr2)->Flags;
1490 PolyToPolygonsOnLayer (PCB->Data, (LayerType *)Crosshair.AttachedObject.Ptr1,
1491 result, Flags);
1492 RemoveObject (POLYGON_TYPE,
1493 Crosshair.AttachedObject.Ptr1,
1494 Crosshair.AttachedObject.Ptr2,
1495 Crosshair.AttachedObject.Ptr3);
1496 RestoreUndoSerialNumber ();
1497 IncrementUndoSerialNumber ();
1498 Draw ();
1500 /* reset state of attached line */
1501 memset (&Crosshair.AttachedPolygon, 0, sizeof (PolygonType));
1502 Crosshair.AttachedLine.State = STATE_FIRST;
1503 addedLines = 0;
1505 break;
1508 /* create new point if it's the first one or if it's
1509 * different to the last one
1511 if (!n ||
1512 points[n - 1].X != Crosshair.AttachedLine.Point2.X ||
1513 points[n - 1].Y != Crosshair.AttachedLine.Point2.Y)
1515 CreateNewPointInPolygon (&Crosshair.AttachedPolygon,
1516 Crosshair.AttachedLine.Point2.X,
1517 Crosshair.AttachedLine.Point2.Y);
1519 /* copy the coordinates */
1520 Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X;
1521 Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y;
1523 break;
1527 break;
1530 case PASTEBUFFER_MODE:
1532 TextType estr[MAX_ELEMENTNAMES];
1533 ElementType *e = 0;
1535 if (gui->shift_is_pressed ())
1537 int type =
1538 SearchScreen (Note.X, Note.Y, ELEMENT_TYPE, &ptr1, &ptr2,
1539 &ptr3);
1540 if (type == ELEMENT_TYPE)
1542 e = (ElementType *) ptr1;
1543 if (e)
1545 int i;
1547 memcpy (estr, e->Name,
1548 MAX_ELEMENTNAMES * sizeof (TextType));
1549 for (i = 0; i < MAX_ELEMENTNAMES; ++i)
1550 estr[i].TextString = estr[i].TextString ? strdup(estr[i].TextString) : NULL;
1551 RemoveElement (e);
1555 if (CopyPastebufferToLayout (Note.X, Note.Y))
1556 SetChangedFlag (true);
1557 if (e)
1559 int type =
1560 SearchScreen (Note.X, Note.Y, ELEMENT_TYPE, &ptr1, &ptr2,
1561 &ptr3);
1562 if (type == ELEMENT_TYPE && ptr1)
1564 int i, save_n;
1565 e = (ElementType *) ptr1;
1567 save_n = NAME_INDEX (PCB);
1569 for (i = 0; i < MAX_ELEMENTNAMES; i++)
1571 if (i == save_n)
1572 EraseElementName (e);
1573 r_delete_entry (PCB->Data->name_tree[i],
1574 (BoxType *) & (e->Name[i]));
1575 memcpy (&(e->Name[i]), &(estr[i]), sizeof (TextType));
1576 e->Name[i].Element = e;
1577 SetTextBoundingBox (&PCB->Font, &(e->Name[i]));
1578 r_insert_entry (PCB->Data->name_tree[i],
1579 (BoxType *) & (e->Name[i]), 0);
1580 if (i == save_n)
1581 DrawElementName (e);
1585 break;
1588 case REMOVE_MODE:
1589 if ((type =
1590 SearchScreen (Note.X, Note.Y, REMOVE_TYPES, &ptr1, &ptr2,
1591 &ptr3)) != NO_TYPE)
1593 if (TEST_FLAG (LOCKFLAG, (LineType *) ptr2))
1595 Message (_("Sorry, the object is locked\n"));
1596 break;
1598 if (type == ELEMENT_TYPE)
1600 RubberbandType *ptr;
1601 int i;
1603 Crosshair.AttachedObject.RubberbandN = 0;
1604 LookupRatLines (type, ptr1, ptr2, ptr3);
1605 ptr = Crosshair.AttachedObject.Rubberband;
1606 for (i = 0; i < Crosshair.AttachedObject.RubberbandN; i++)
1608 if (PCB->RatOn)
1609 EraseRat ((RatType *) ptr->Line);
1610 if (TEST_FLAG (RUBBERENDFLAG, ptr->Line))
1611 MoveObjectToRemoveUndoList (RATLINE_TYPE,
1612 ptr->Line, ptr->Line,
1613 ptr->Line);
1614 else
1615 TOGGLE_FLAG (RUBBERENDFLAG, ptr->Line); /* only remove line once */
1616 ptr++;
1619 RemoveObject (type, ptr1, ptr2, ptr3);
1620 IncrementUndoSerialNumber ();
1621 SetChangedFlag (true);
1623 break;
1625 case ROTATE_MODE:
1626 RotateScreenObject (Note.X, Note.Y,
1627 gui->shift_is_pressed ()? (SWAP_IDENT ?
1628 1 : 3)
1629 : (SWAP_IDENT ? 3 : 1));
1630 break;
1632 /* both are almost the same */
1633 case COPY_MODE:
1634 case MOVE_MODE:
1635 switch (Crosshair.AttachedObject.State)
1637 /* first notify, lookup object */
1638 case STATE_FIRST:
1640 int types = (Settings.Mode == COPY_MODE) ?
1641 COPY_TYPES : MOVE_TYPES;
1643 Crosshair.AttachedObject.Type =
1644 SearchScreen (Note.X, Note.Y, types,
1645 &Crosshair.AttachedObject.Ptr1,
1646 &Crosshair.AttachedObject.Ptr2,
1647 &Crosshair.AttachedObject.Ptr3);
1648 if (Crosshair.AttachedObject.Type != NO_TYPE)
1650 if (Settings.Mode == MOVE_MODE &&
1651 TEST_FLAG (LOCKFLAG, (PinType *)
1652 Crosshair.AttachedObject.Ptr2))
1654 Message (_("Sorry, the object is locked\n"));
1655 Crosshair.AttachedObject.Type = NO_TYPE;
1657 else
1658 AttachForCopy (Note.X, Note.Y);
1660 break;
1663 /* second notify, move or copy object */
1664 case STATE_SECOND:
1665 if (Settings.Mode == COPY_MODE)
1666 CopyObject (Crosshair.AttachedObject.Type,
1667 Crosshair.AttachedObject.Ptr1,
1668 Crosshair.AttachedObject.Ptr2,
1669 Crosshair.AttachedObject.Ptr3,
1670 Note.X - Crosshair.AttachedObject.X,
1671 Note.Y - Crosshair.AttachedObject.Y);
1672 else
1674 MoveObjectAndRubberband (Crosshair.AttachedObject.Type,
1675 Crosshair.AttachedObject.Ptr1,
1676 Crosshair.AttachedObject.Ptr2,
1677 Crosshair.AttachedObject.Ptr3,
1678 Note.X - Crosshair.AttachedObject.X,
1679 Note.Y - Crosshair.AttachedObject.Y);
1680 SetLocalRef (0, 0, false);
1682 SetChangedFlag (true);
1684 /* reset identifiers */
1685 Crosshair.AttachedObject.Type = NO_TYPE;
1686 Crosshair.AttachedObject.State = STATE_FIRST;
1687 break;
1689 break;
1691 /* insert a point into a polygon/line/... */
1692 case INSERTPOINT_MODE:
1693 switch (Crosshair.AttachedObject.State)
1695 /* first notify, lookup object */
1696 case STATE_FIRST:
1697 Crosshair.AttachedObject.Type =
1698 SearchScreen (Note.X, Note.Y, INSERT_TYPES,
1699 &Crosshair.AttachedObject.Ptr1,
1700 &Crosshair.AttachedObject.Ptr2,
1701 &Crosshair.AttachedObject.Ptr3);
1703 if (Crosshair.AttachedObject.Type != NO_TYPE)
1705 if (TEST_FLAG (LOCKFLAG, (PolygonType *)
1706 Crosshair.AttachedObject.Ptr2))
1708 Message (_("Sorry, the object is locked\n"));
1709 Crosshair.AttachedObject.Type = NO_TYPE;
1710 break;
1712 else
1714 /* get starting point of nearest segment */
1715 if (Crosshair.AttachedObject.Type == POLYGON_TYPE)
1717 fake.poly =
1718 (PolygonType *) Crosshair.AttachedObject.Ptr2;
1719 polyIndex =
1720 GetLowestDistancePolygonPoint (fake.poly, Note.X,
1721 Note.Y);
1722 fake.line.Point1 = fake.poly->Points[polyIndex];
1723 fake.line.Point2 = fake.poly->Points[
1724 prev_contour_point (fake.poly, polyIndex)];
1725 Crosshair.AttachedObject.Ptr2 = &fake.line;
1728 Crosshair.AttachedObject.State = STATE_SECOND;
1729 InsertedPoint = *AdjustInsertPoint ();
1732 break;
1734 /* second notify, insert new point into object */
1735 case STATE_SECOND:
1736 if (Crosshair.AttachedObject.Type == POLYGON_TYPE)
1737 InsertPointIntoObject (POLYGON_TYPE,
1738 Crosshair.AttachedObject.Ptr1, fake.poly,
1739 &polyIndex,
1740 InsertedPoint.X, InsertedPoint.Y, false, false);
1741 else
1742 InsertPointIntoObject (Crosshair.AttachedObject.Type,
1743 Crosshair.AttachedObject.Ptr1,
1744 Crosshair.AttachedObject.Ptr2,
1745 &polyIndex,
1746 InsertedPoint.X, InsertedPoint.Y, false, false);
1747 SetChangedFlag (true);
1749 /* reset identifiers */
1750 Crosshair.AttachedObject.Type = NO_TYPE;
1751 Crosshair.AttachedObject.State = STATE_FIRST;
1752 break;
1754 break;
1759 /* --------------------------------------------------------------------------- */
1761 static const char atomic_syntax[] = "Atomic(Save|Restore|Close|Block)";
1763 static const char atomic_help[] = "Save or restore the undo serial number.";
1765 /* %start-doc actions Atomic
1767 This action allows making multiple-action bindings into an atomic
1768 operation that will be undone by a single Undo command. For example,
1769 to optimize rat lines, you'd delete the rats and re-add them. To
1770 group these into a single undo, you'd want the deletions and the
1771 additions to have the same undo serial number. So, you @code{Save},
1772 delete the rats, @code{Restore}, add the rats - using the same serial
1773 number as the deletes, then @code{Block}, which checks to see if the
1774 deletions or additions actually did anything. If not, the serial
1775 number is set to the saved number, as there's nothing to undo. If
1776 something did happen, the serial number is incremented so that these
1777 actions are counted as a single undo step.
1779 @table @code
1781 @item Save
1782 Saves the undo serial number.
1784 @item Restore
1785 Returns it to the last saved number.
1787 @item Close
1788 Sets it to 1 greater than the last save.
1790 @item Block
1791 Does a Restore if there was nothing to undo, else does a Close.
1793 @end table
1795 %end-doc */
1797 static int
1798 ActionAtomic (int argc, char **argv, Coord x, Coord y)
1800 if (argc != 1)
1801 AFAIL (atomic);
1803 switch (GetFunctionID (argv[0]))
1805 case F_Save:
1806 SaveUndoSerialNumber ();
1807 break;
1808 case F_Restore:
1809 RestoreUndoSerialNumber ();
1810 break;
1811 case F_Close:
1812 RestoreUndoSerialNumber ();
1813 IncrementUndoSerialNumber ();
1814 break;
1815 case F_Block:
1816 RestoreUndoSerialNumber ();
1817 if (Bumped)
1818 IncrementUndoSerialNumber ();
1819 break;
1821 return 0;
1824 /* -------------------------------------------------------------------------- */
1826 static const char drc_syntax[] = "DRC()";
1828 static const char drc_help[] = "Invoke the DRC check.";
1830 /* %start-doc actions DRC
1832 Note that the design rule check uses the current board rule settings,
1833 not the current style settings.
1835 %end-doc */
1837 static int
1838 ActionDRCheck (int argc, char **argv, Coord x, Coord y)
1840 int count;
1842 if (gui->drc_gui == NULL || gui->drc_gui->log_drc_overview)
1844 Message (_("%m+Rules are minspace %$mS, minoverlap %$mS "
1845 "minwidth %$mS, minsilk %$mS\n"
1846 "min drill %$mS, min annular ring %$mS\n"),
1847 Settings.grid_unit->allow,
1848 PCB->Bloat, PCB->Shrink,
1849 PCB->minWid, PCB->minSlk,
1850 PCB->minDrill, PCB->minRing);
1852 count = DRCAll ();
1853 if (gui->drc_gui == NULL || gui->drc_gui->log_drc_overview)
1855 if (count == 0)
1856 Message (_("No DRC problems found.\n"));
1857 else if (count > 0)
1858 Message (_("Found %d design rule errors.\n"), count);
1859 else
1860 Message (_("Aborted DRC after %d design rule errors.\n"), -count);
1862 return 0;
1865 /* -------------------------------------------------------------------------- */
1867 static const char dumplibrary_syntax[] = "DumpLibrary()";
1869 static const char dumplibrary_help[] =
1870 "Display the entire contents of the libraries.";
1872 /* %start-doc actions DumpLibrary
1875 %end-doc */
1877 static int
1878 ActionDumpLibrary (int argc, char **argv, Coord x, Coord y)
1880 int i, j;
1882 printf ("**** Do not count on this format. It will change ****\n\n");
1883 printf ("MenuN = %d\n", Library.MenuN);
1884 printf ("MenuMax = %d\n", Library.MenuMax);
1885 for (i = 0; i < Library.MenuN; i++)
1887 printf ("Library #%d:\n", i);
1888 printf (" EntryN = %d\n", Library.Menu[i].EntryN);
1889 printf (" EntryMax = %d\n", Library.Menu[i].EntryMax);
1890 printf (" Name = \"%s\"\n", UNKNOWN (Library.Menu[i].Name));
1891 printf (" directory = \"%s\"\n",
1892 UNKNOWN (Library.Menu[i].directory));
1893 printf (" Style = \"%s\"\n", UNKNOWN (Library.Menu[i].Style));
1894 printf (" flag = %d\n", Library.Menu[i].flag);
1896 for (j = 0; j < Library.Menu[i].EntryN; j++)
1898 printf (" #%4d: ", j);
1899 if (Library.Menu[i].Entry[j].Template == (char *) -1)
1901 printf ("newlib: \"%s\"\n",
1902 UNKNOWN (Library.Menu[i].Entry[j].ListEntry));
1904 else
1906 printf ("\"%s\", \"%s\", \"%s\", \"%s\", \"%s\"\n",
1907 UNKNOWN (Library.Menu[i].Entry[j].ListEntry),
1908 UNKNOWN (Library.Menu[i].Entry[j].Template),
1909 UNKNOWN (Library.Menu[i].Entry[j].Package),
1910 UNKNOWN (Library.Menu[i].Entry[j].Value),
1911 UNKNOWN (Library.Menu[i].Entry[j].Description));
1916 return 0;
1919 /* -------------------------------------------------------------------------- */
1921 static const char flip_syntax[] = "Flip(Object|Selected|SelectedElements)";
1923 static const char flip_help[] =
1924 "Flip an element to the opposite side of the board.";
1926 /* %start-doc actions Flip
1928 Note that the location of the element will be symmetric about the
1929 cursor location; i.e. if the part you are pointing at will still be at
1930 the same spot once the element is on the other side. When flipping
1931 multiple elements, this retains their positions relative to each
1932 other, not their absolute positions on the board.
1934 %end-doc */
1936 static int
1937 ActionFlip (int argc, char **argv, Coord x, Coord y)
1939 char *function = ARG (0);
1940 ElementType *element;
1941 void *ptrtmp;
1942 int err = 0;
1944 if (function)
1946 switch (GetFunctionID (function))
1948 case F_Object:
1949 if ((SearchScreen (x, y, ELEMENT_TYPE,
1950 &ptrtmp, &ptrtmp, &ptrtmp)) != NO_TYPE)
1952 element = (ElementType *) ptrtmp;
1953 ChangeElementSide (element, 2 * Crosshair.Y - PCB->MaxHeight);
1954 IncrementUndoSerialNumber ();
1955 Draw ();
1957 break;
1958 case F_Selected:
1959 case F_SelectedElements:
1960 ChangeSelectedElementSide ();
1961 break;
1962 default:
1963 err = 1;
1964 break;
1966 if (!err)
1967 return 0;
1970 AFAIL (flip);
1973 /* -------------------------------------------------------------------------- */
1975 static const char message_syntax[] = "Message(message)";
1977 static const char message_help[] = "Writes a message to the log window.";
1979 /* %start-doc actions Message
1981 This action displays a message to the log window. This action is primarily
1982 provided for use by other programs which may interface with PCB. If
1983 multiple arguments are given, each one is sent to the log window
1984 followed by a newline.
1986 %end-doc */
1988 static int
1989 ActionMessage (int argc, char **argv, Coord x, Coord y)
1991 int i;
1993 if (argc < 1)
1994 AFAIL (message);
1996 for (i = 0; i < argc; i++)
1998 Message (argv[i]);
1999 Message ("\n");
2002 return 0;
2006 /* -------------------------------------------------------------------------- */
2008 static const char setthermal_syntax[] =
2009 "SetThermal(Object|SelectedPins|SelectedVias|Selected, Style)";
2011 static const char setthermal_help[] =
2012 "Set the thermal (on the current layer) of pins or vias to the given style.\n"
2013 "Style = 0 means no thermal.\n"
2014 "Style = 1 has diagonal fingers with sharp edges.\n"
2015 "Style = 2 has horizontal and vertical fingers with sharp edges.\n"
2016 "Style = 3 is a solid connection to the plane."
2017 "Style = 4 has diagonal fingers with rounded edges.\n"
2018 "Style = 5 has horizontal and vertical fingers with rounded edges.\n";
2020 /* %start-doc actions SetThermal
2022 This changes how/whether pins or vias connect to any rectangle or polygon
2023 on the current layer. The first argument can specify one object, or all
2024 selected pins, or all selected vias, or all selected pins and vias.
2025 The second argument specifies the style of connection.
2026 There are 5 possibilities:
2027 0 - no connection,
2028 1 - 45 degree fingers with sharp edges,
2029 2 - horizontal & vertical fingers with sharp edges,
2030 3 - solid connection,
2031 4 - 45 degree fingers with rounded corners,
2032 5 - horizontal & vertical fingers with rounded corners.
2034 Pins and Vias may have thermals whether or not there is a polygon available
2035 to connect with. However, they will have no effect without the polygon.
2036 %end-doc */
2038 static int
2039 ActionSetThermal (int argc, char **argv, Coord x, Coord y)
2041 char *function = ARG (0);
2042 char *style = ARG (1);
2043 void *ptr1, *ptr2, *ptr3;
2044 int type, kind;
2045 int err = 0;
2047 if (function && *function && style && *style)
2049 bool absolute;
2051 kind = GetValue (style, NULL, &absolute);
2052 if (absolute)
2053 switch (GetFunctionID (function))
2055 case F_Object:
2056 if ((type =
2057 SearchScreen (Crosshair.X, Crosshair.Y, CHANGETHERMAL_TYPES,
2058 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
2060 ChangeObjectThermal (type, ptr1, ptr2, ptr3, kind);
2061 IncrementUndoSerialNumber ();
2062 Draw ();
2064 break;
2065 case F_SelectedPins:
2066 ChangeSelectedThermals (PIN_TYPE, kind);
2067 break;
2068 case F_SelectedVias:
2069 ChangeSelectedThermals (VIA_TYPE, kind);
2070 break;
2071 case F_Selected:
2072 case F_SelectedElements:
2073 ChangeSelectedThermals (CHANGETHERMAL_TYPES, kind);
2074 break;
2075 default:
2076 err = 1;
2077 break;
2079 else
2080 err = 1;
2081 if (!err)
2082 return 0;
2085 AFAIL (setthermal);
2088 /* ---------------------------------------------------------------------------
2089 * !!! no action routine !!!
2091 * event handler to set the cursor according to the X pointer position
2092 * called from inside main.c
2094 void
2095 EventMoveCrosshair (int ev_x, int ev_y)
2097 #ifdef HAVE_LIBSTROKE
2098 if (mid_stroke)
2100 StrokeBox.X2 = ev_x;
2101 StrokeBox.Y2 = ev_y;
2102 stroke_record (ev_x, ev_y);
2103 return;
2105 #endif /* HAVE_LIBSTROKE */
2106 if (MoveCrosshairAbsolute (ev_x, ev_y))
2108 /* update object position and cursor location */
2109 AdjustAttachedObjects ();
2110 notify_crosshair_change (true);
2114 /* --------------------------------------------------------------------------- */
2116 static const char setvalue_syntax[] =
2117 "SetValue(Grid|Line|LineSize|Text|TextScale|ViaDrillingHole|Via|ViaSize, delta)";
2119 static const char setvalue_help[] =
2120 "Change various board-wide values and sizes.";
2122 /* %start-doc actions SetValue
2124 @table @code
2126 @item ViaDrillingHole
2127 Changes the diameter of the drill for new vias.
2129 @item Grid
2130 Sets the grid spacing.
2132 @item Line
2133 @item LineSize
2134 Changes the thickness of new lines.
2136 @item Via
2137 @item ViaSize
2138 Changes the diameter of new vias.
2140 @item Text
2141 @item TextScale
2142 Changes the size of new text.
2144 @end table
2146 %end-doc */
2148 static int
2149 ActionSetValue (int argc, char **argv, Coord x, Coord y)
2151 char *function = ARG (0);
2152 char *val = ARG (1);
2153 char *units = ARG (2);
2154 bool absolute; /* flag for 'absolute' value */
2155 double value;
2156 int text_scale;
2157 int err = 0;
2159 if (function && val)
2161 value = GetValue (val, units, &absolute);
2162 switch (GetFunctionID (function))
2164 case F_ViaDrillingHole:
2165 SetViaDrillingHole (absolute ? value :
2166 value + Settings.ViaDrillingHole,
2167 false);
2168 hid_action ("RouteStylesChanged");
2169 break;
2171 case F_Grid:
2172 if (absolute)
2173 SetGrid (value, false);
2174 else
2176 if (value == 0)
2177 value = val[0] == '-' ? -Settings.increments->grid
2178 : Settings.increments->grid;
2179 /* On the way down, short against the minimum
2180 * PCB drawing unit */
2181 if ((value + PCB->Grid) < 1)
2182 SetGrid (1, false);
2183 else if (PCB->Grid == 1)
2184 SetGrid (value, false);
2185 else
2186 SetGrid (value + PCB->Grid, false);
2188 break;
2190 case F_LineSize:
2191 case F_Line:
2192 if (!absolute && value == 0)
2193 value = val[0] == '-' ? -Settings.increments->line
2194 : Settings.increments->line;
2195 SetLineSize (absolute ? value : value + Settings.LineThickness);
2196 hid_action ("RouteStylesChanged");
2197 break;
2199 case F_Via:
2200 case F_ViaSize:
2201 SetViaSize (absolute ? value : value + Settings.ViaThickness, false);
2202 hid_action ("RouteStylesChanged");
2203 break;
2205 case F_Text:
2206 case F_TextScale:
2207 text_scale = value / (double)FONT_CAPHEIGHT * 100.;
2208 if (!absolute)
2209 text_scale += Settings.TextScale;
2210 SetTextScale (text_scale);
2211 break;
2212 default:
2213 err = 1;
2214 break;
2216 if (!err)
2217 return 0;
2220 AFAIL (setvalue);
2224 /* --------------------------------------------------------------------------- */
2226 static const char quit_syntax[] = "Quit()";
2228 static const char quit_help[] = "Quits the application after confirming.";
2230 /* %start-doc actions Quit
2232 If you have unsaved changes, you will be prompted to confirm (or
2233 save) before quitting.
2235 %end-doc */
2237 static int
2238 ActionQuit (int argc, char **argv, Coord x, Coord y)
2240 char *force = ARG (0);
2241 if (force && strcasecmp (force, "force") == 0)
2243 PCB->Changed = 0;
2244 exit (0);
2246 if (!PCB->Changed || gui->close_confirm_dialog () == HID_CLOSE_CONFIRM_OK)
2247 QuitApplication ();
2248 return 1;
2251 /* --------------------------------------------------------------------------- */
2253 static const char connection_syntax[] =
2254 "Connection(Find|ResetLinesAndPolygons|ResetPinsAndVias|Reset)";
2256 static const char connection_help[] =
2257 "Searches connections of the object at the cursor position.";
2259 /* %start-doc actions Connection
2261 Connections found with this action will be highlighted in the
2262 ``connected-color'' color and will have the ``found'' flag set.
2264 @table @code
2266 @item Find
2267 The net under the cursor is ``found''.
2269 @item ResetLinesAndPolygons
2270 Any ``found'' lines and polygons are marked ``not found''.
2272 @item ResetPinsAndVias
2273 Any ``found'' pins and vias are marked ``not found''.
2275 @item Reset
2276 All ``found'' objects are marked ``not found''.
2278 @end table
2280 %end-doc */
2282 static int
2283 ActionConnection (int argc, char **argv, Coord x, Coord y)
2285 char *function = ARG (0);
2286 if (function)
2288 switch (GetFunctionID (function))
2290 case F_Find:
2292 gui->get_coords (_("Click on a connection"), &x, &y);
2293 LookupConnection (x, y, true, 1, FOUNDFLAG, false);
2294 break;
2297 case F_ResetLinesAndPolygons:
2298 if (ClearFlagOnLinesAndPolygons (true, FOUNDFLAG))
2300 IncrementUndoSerialNumber ();
2301 Draw ();
2303 break;
2305 case F_ResetPinsViasAndPads:
2306 if (ClearFlagOnPinsViasAndPads (true, FOUNDFLAG))
2308 IncrementUndoSerialNumber ();
2309 Draw ();
2311 break;
2313 case F_Reset:
2314 if (ClearFlagOnAllObjects (true, FOUNDFLAG))
2316 IncrementUndoSerialNumber ();
2317 Draw ();
2319 break;
2321 return 0;
2324 AFAIL (connection);
2327 /* --------------------------------------------------------------------------- */
2329 static const char disperseelements_syntax[] =
2330 "DisperseElements(All|Selected)";
2332 static const char disperseelements_help[] = "Disperses elements.";
2334 /* %start-doc actions DisperseElements
2336 Normally this is used when starting a board, by selecting all elements
2337 and then dispersing them. This scatters the elements around the board
2338 so that you can pick individual ones, rather than have all the
2339 elements at the same 0,0 coordinate and thus impossible to choose
2340 from.
2342 %end-doc */
2344 #define GAP MIL_TO_COORD(100)
2346 static int
2347 ActionDisperseElements (int argc, char **argv, Coord x, Coord y)
2349 char *function = ARG (0);
2350 Coord minx = GAP,
2351 miny = GAP,
2352 maxy = GAP,
2353 dx, dy;
2354 int all = 0, bad = 0;
2356 if (!function || !*function)
2358 bad = 1;
2360 else
2362 switch (GetFunctionID (function))
2364 case F_All:
2365 all = 1;
2366 break;
2368 case F_Selected:
2369 all = 0;
2370 break;
2372 default:
2373 bad = 1;
2377 if (bad)
2379 AFAIL (disperseelements);
2383 ELEMENT_LOOP (PCB->Data);
2386 * If we want to disperse selected elements, maybe we need smarter
2387 * code here to avoid putting components on top of others which
2388 * are not selected. For now, I'm assuming that this is typically
2389 * going to be used either with a brand new design or a scratch
2390 * design holding some new components
2392 if (!TEST_FLAG (LOCKFLAG, element) && (all || TEST_FLAG (SELECTEDFLAG, element)))
2395 /* figure out how much to move the element */
2396 dx = minx - element->BoundingBox.X1;
2398 /* snap to the grid */
2399 dx -= (element->MarkX + dx) % PCB->Grid;
2402 * and add one grid size so we make sure we always space by GAP or
2403 * more
2405 dx += PCB->Grid;
2407 /* Figure out if this row has room. If not, start a new row */
2408 if (GAP + element->BoundingBox.X2 + dx > PCB->MaxWidth)
2410 miny = maxy + GAP;
2411 minx = GAP;
2414 /* figure out how much to move the element */
2415 dx = minx - element->BoundingBox.X1;
2416 dy = miny - element->BoundingBox.Y1;
2418 /* snap to the grid */
2419 dx -= (element->MarkX + dx) % PCB->Grid;
2420 dx += PCB->Grid;
2421 dy -= (element->MarkY + dy) % PCB->Grid;
2422 dy += PCB->Grid;
2424 /* move the element */
2425 MoveElementLowLevel (PCB->Data, element, dx, dy);
2427 /* and add to the undo list so we can undo this operation */
2428 AddObjectToMoveUndoList (ELEMENT_TYPE, NULL, NULL, element, dx, dy);
2430 /* keep track of how tall this row is */
2431 minx += element->BoundingBox.X2 - element->BoundingBox.X1 + GAP;
2432 if (maxy < element->BoundingBox.Y2)
2434 maxy = element->BoundingBox.Y2;
2439 END_LOOP;
2441 /* done with our action so increment the undo # */
2442 IncrementUndoSerialNumber ();
2444 Redraw ();
2445 SetChangedFlag (true);
2447 return 0;
2450 #undef GAP
2452 /* --------------------------------------------------------------------------- */
2454 static const char display_syntax[] =
2455 "Display(NameOnPCB|Description|Value)\n"
2456 "Display(Grid|Redraw)\n"
2457 "Display(CycleClip|CycleCrosshair|Toggle45Degree|ToggleStartDirection)\n"
2458 "Display(ToggleGrid|ToggleRubberBandMode|ToggleUniqueNames)\n"
2459 "Display(ToggleMask|ToggleName|ToggleClearLine|ToggleFullPoly|ToggleSnapPin)\n"
2460 "Display(ToggleThindraw|ToggleThindrawPoly|ToggleOrthoMove|ToggleLocalRef)\n"
2461 "Display(ToggleCheckPlanes|ToggleShowDRC|ToggleAutoDRC)\n"
2462 "Display(ToggleLiveRoute|LockNames|OnlyNames)\n"
2463 "Display(Pinout|PinOrPadName)";
2465 static const char display_help[] = "Several display-related actions.";
2467 /* %start-doc actions Display
2469 @table @code
2471 @item NameOnPCB
2472 @item Description
2473 @item Value
2474 Specify whether all elements show their name, description, or value.
2476 @item Redraw
2477 Redraw the whole board.
2479 @item Toggle45Degree
2480 When clear, lines can be drawn at any angle. When set, lines are
2481 restricted to multiples of 45 degrees and requested lines may be
2482 broken up according to the clip setting.
2484 @item CycleClip
2485 Changes the way lines are restricted to 45 degree increments. The
2486 various settings are: straight only, orthogonal then angled, and angled
2487 then orthogonal. If AllDirections is set, this action disables it.
2489 @item CycleCrosshair
2490 Changes crosshair drawing. Crosshair may accept form of 4-ray,
2491 8-ray and 12-ray cross.
2493 @item ToggleRubberBandMode
2494 If set, moving an object moves all the lines attached to it too.
2496 @item ToggleStartDirection
2497 If set, each time you set a point in a line, the Clip toggles between
2498 orth-angle and angle-ortho.
2500 @item ToggleUniqueNames
2501 If set, you will not be permitted to change the name of an element to
2502 match that of another element.
2504 @item ToggleSnapPin
2505 If set, pin centers and pad end points are treated as additional grid
2506 points that the cursor can snap to.
2508 @item ToggleLocalRef
2509 If set, the mark is automatically set to the beginning of any move, so
2510 you can see the relative distance you've moved.
2512 @item ToggleThindraw
2513 If set, objects on the screen are drawn as outlines (lines are drawn
2514 as center-lines). This lets you see line endpoints hidden under pins,
2515 for example.
2517 @item ToggleThindrawPoly
2518 If set, polygons on the screen are drawn as outlines.
2520 @item ToggleShowDRC
2521 If set, pending objects (i.e. lines you're in the process of drawing)
2522 will be drawn with an outline showing how far away from other copper
2523 you need to be.
2525 @item ToggleLiveRoute
2526 If set, the progress of the autorouter will be visible on the screen.
2528 @item ToggleAutoDRC
2529 If set, you will not be permitted to make connections which violate
2530 the current DRC and netlist settings.
2532 @item ToggleCheckPlanes
2533 If set, lines and arcs aren't drawn, which usually leaves just the
2534 polygons. If you also disable all but the layer you're interested in,
2535 this allows you to check for isolated regions.
2537 @item ToggleOrthoMove
2538 If set, the crosshair is only allowed to move orthogonally from its
2539 previous position. I.e. you can move an element or line up, down,
2540 left, or right, but not up+left or down+right.
2542 @item ToggleName
2543 Selects whether the pinouts show the pin names or the pin numbers.
2545 @item ToggleLockNames
2546 If set, text will ignore left mouse clicks and actions that work on
2547 objects under the mouse. You can still select text with a lasso (left
2548 mouse drag) and perform actions on the selection.
2550 @item ToggleOnlyNames
2551 If set, only text will be sensitive for mouse clicks and actions that
2552 work on objects under the mouse. You can still select other objects
2553 with a lasso (left mouse drag) and perform actions on the selection.
2555 @item ToggleMask
2556 Turns the solder mask on or off.
2558 @item ToggleClearLine
2559 When set, the clear-line flag causes new lines and arcs to have their
2560 ``clear polygons'' flag set, so they won't be electrically connected
2561 to any polygons they overlap.
2563 @item ToggleFullPoly
2564 When set, the full-poly flag causes new polygons to have their
2565 ``full polygon'' flag set, so all parts of them will be displayed
2566 instead of only the biggest one.
2568 @item ToggleGrid
2569 Resets the origin of the current grid to be wherever the mouse pointer
2570 is (not where the crosshair currently is). If you provide two numbers
2571 after this, the origin is set to that coordinate.
2573 @item Grid
2574 Toggles whether the grid is displayed or not.
2576 @item Pinout
2577 Causes the pinout of the element indicated by the cursor to be
2578 displayed, usually in a separate window.
2580 @item PinOrPadName
2581 Toggles whether the names of pins, pads, or (yes) vias will be
2582 displayed. If the cursor is over an element, all of its pins and pads
2583 are affected.
2585 @end table
2587 %end-doc */
2589 static enum crosshair_shape
2590 CrosshairShapeIncrement (enum crosshair_shape shape)
2592 switch(shape)
2594 case Basic_Crosshair_Shape:
2595 shape = Union_Jack_Crosshair_Shape;
2596 break;
2597 case Union_Jack_Crosshair_Shape:
2598 shape = Dozen_Crosshair_Shape;
2599 break;
2600 case Dozen_Crosshair_Shape:
2601 shape = Crosshair_Shapes_Number;
2602 break;
2603 case Crosshair_Shapes_Number:
2604 shape = Basic_Crosshair_Shape;
2605 break;
2607 return shape;
2610 static int
2611 ActionDisplay (int argc, char **argv, Coord childX, Coord childY)
2613 char *function, *str_dir;
2614 int id;
2615 int err = 0;
2617 function = ARG (0);
2618 str_dir = ARG (1);
2620 if (function && (!str_dir || !*str_dir))
2622 switch (id = GetFunctionID (function))
2625 /* redraw layout */
2626 case F_ClearAndRedraw:
2627 case F_Redraw:
2628 Redraw ();
2629 break;
2631 /* change the displayed name of elements */
2632 case F_Value:
2633 case F_NameOnPCB:
2634 case F_Description:
2635 ELEMENT_LOOP (PCB->Data);
2637 EraseElementName (element);
2639 END_LOOP;
2640 CLEAR_FLAG (DESCRIPTIONFLAG | NAMEONPCBFLAG, PCB);
2641 switch (id)
2643 case F_Value:
2644 break;
2645 case F_NameOnPCB:
2646 SET_FLAG (NAMEONPCBFLAG, PCB);
2647 break;
2648 case F_Description:
2649 SET_FLAG (DESCRIPTIONFLAG, PCB);
2650 break;
2652 ELEMENT_LOOP (PCB->Data);
2654 DrawElementName (element);
2656 END_LOOP;
2657 Draw ();
2658 break;
2660 /* toggle line-adjust flag */
2661 case F_ToggleAllDirections:
2662 TOGGLE_FLAG (ALLDIRECTIONFLAG, PCB);
2663 AdjustAttachedObjects ();
2664 break;
2666 case F_CycleClip:
2667 notify_crosshair_change (false);
2668 if (TEST_FLAG (ALLDIRECTIONFLAG, PCB))
2670 TOGGLE_FLAG (ALLDIRECTIONFLAG, PCB);
2671 PCB->Clipping = 0;
2673 else
2674 PCB->Clipping = (PCB->Clipping + 1) % 3;
2675 AdjustAttachedObjects ();
2676 notify_crosshair_change (true);
2677 break;
2679 case F_CycleCrosshair:
2680 notify_crosshair_change (false);
2681 Crosshair.shape = CrosshairShapeIncrement(Crosshair.shape);
2682 if (Crosshair_Shapes_Number == Crosshair.shape)
2683 Crosshair.shape = Basic_Crosshair_Shape;
2684 notify_crosshair_change (true);
2685 break;
2687 case F_ToggleRubberBandMode:
2688 notify_crosshair_change (false);
2689 TOGGLE_FLAG (RUBBERBANDFLAG, PCB);
2690 notify_crosshair_change (true);
2691 break;
2693 case F_ToggleStartDirection:
2694 notify_crosshair_change (false);
2695 TOGGLE_FLAG (SWAPSTARTDIRFLAG, PCB);
2696 notify_crosshair_change (true);
2697 break;
2699 case F_ToggleUniqueNames:
2700 TOGGLE_FLAG (UNIQUENAMEFLAG, PCB);
2701 break;
2703 case F_ToggleSnapPin:
2704 notify_crosshair_change (false);
2705 TOGGLE_FLAG (SNAPPINFLAG, PCB);
2706 notify_crosshair_change (true);
2707 break;
2709 case F_ToggleLocalRef:
2710 TOGGLE_FLAG (LOCALREFFLAG, PCB);
2711 break;
2713 case F_ToggleThindraw:
2714 TOGGLE_FLAG (THINDRAWFLAG, PCB);
2715 Redraw ();
2716 break;
2718 case F_ToggleThindrawPoly:
2719 TOGGLE_FLAG (THINDRAWPOLYFLAG, PCB);
2720 Redraw ();
2721 break;
2723 case F_ToggleLockNames:
2724 TOGGLE_FLAG (LOCKNAMESFLAG, PCB);
2725 CLEAR_FLAG (ONLYNAMESFLAG, PCB);
2726 break;
2728 case F_ToggleOnlyNames:
2729 TOGGLE_FLAG (ONLYNAMESFLAG, PCB);
2730 CLEAR_FLAG (LOCKNAMESFLAG, PCB);
2731 break;
2733 case F_ToggleHideNames:
2734 TOGGLE_FLAG (HIDENAMESFLAG, PCB);
2735 Redraw ();
2736 break;
2738 case F_ToggleShowDRC:
2739 TOGGLE_FLAG (SHOWDRCFLAG, PCB);
2740 break;
2742 case F_ToggleLiveRoute:
2743 TOGGLE_FLAG (LIVEROUTEFLAG, PCB);
2744 break;
2746 case F_ToggleAutoDRC:
2747 notify_crosshair_change (false);
2748 TOGGLE_FLAG (AUTODRCFLAG, PCB);
2749 if (TEST_FLAG (AUTODRCFLAG, PCB) && Settings.Mode == LINE_MODE)
2751 if (ClearFlagOnAllObjects (true, FOUNDFLAG))
2753 IncrementUndoSerialNumber ();
2754 Draw ();
2756 if (Crosshair.AttachedLine.State != STATE_FIRST)
2757 LookupConnection (Crosshair.AttachedLine.Point1.X,
2758 Crosshair.AttachedLine.Point1.Y, true, 1,
2759 FOUNDFLAG, true);
2761 notify_crosshair_change (true);
2762 break;
2764 case F_ToggleCheckPlanes:
2765 TOGGLE_FLAG (CHECKPLANESFLAG, PCB);
2766 Redraw ();
2767 break;
2769 case F_ToggleOrthoMove:
2770 TOGGLE_FLAG (ORTHOMOVEFLAG, PCB);
2771 break;
2773 case F_ToggleName:
2774 TOGGLE_FLAG (SHOWNUMBERFLAG, PCB);
2775 Redraw ();
2776 break;
2778 case F_ToggleMask:
2779 TOGGLE_FLAG (SHOWMASKFLAG, PCB);
2780 Redraw ();
2781 break;
2783 case F_ToggleClearLine:
2784 TOGGLE_FLAG (CLEARNEWFLAG, PCB);
2785 break;
2787 case F_ToggleFullPoly:
2788 TOGGLE_FLAG (NEWFULLPOLYFLAG, PCB);
2789 break;
2791 /* shift grid alignment */
2792 case F_ToggleGrid:
2794 Coord oldGrid = PCB->Grid;
2796 PCB->Grid = 1;
2797 if (MoveCrosshairAbsolute (Crosshair.X, Crosshair.Y))
2798 notify_crosshair_change (true); /* first notify was in MoveCrosshairAbs */
2799 SetGrid (oldGrid, true);
2801 break;
2803 /* toggle displaying of the grid */
2804 case F_Grid:
2805 Settings.DrawGrid = !Settings.DrawGrid;
2806 Redraw ();
2807 break;
2809 /* display the pinout of an element */
2810 case F_Pinout:
2812 ElementType *element;
2813 void *ptrtmp;
2814 Coord x, y;
2816 gui->get_coords (_("Click on an element"), &x, &y);
2817 if ((SearchScreen
2818 (x, y, ELEMENT_TYPE, &ptrtmp,
2819 &ptrtmp, &ptrtmp)) != NO_TYPE)
2821 element = (ElementType *) ptrtmp;
2822 gui->show_item (element);
2824 break;
2827 /* toggle displaying of pin/pad/via names */
2828 case F_PinOrPadName:
2830 void *ptr1, *ptr2, *ptr3;
2832 switch (SearchScreen (Crosshair.X, Crosshair.Y,
2833 ELEMENT_TYPE | PIN_TYPE | PAD_TYPE |
2834 VIA_TYPE, (void **) &ptr1, (void **) &ptr2,
2835 (void **) &ptr3))
2837 case ELEMENT_TYPE:
2838 PIN_LOOP ((ElementType *) ptr1);
2840 if (TEST_FLAG (DISPLAYNAMEFLAG, pin))
2841 ErasePinName (pin);
2842 else
2843 DrawPinName (pin);
2844 AddObjectToFlagUndoList (PIN_TYPE, ptr1, pin, pin);
2845 TOGGLE_FLAG (DISPLAYNAMEFLAG, pin);
2847 END_LOOP;
2848 PAD_LOOP ((ElementType *) ptr1);
2850 if (TEST_FLAG (DISPLAYNAMEFLAG, pad))
2851 ErasePadName (pad);
2852 else
2853 DrawPadName (pad);
2854 AddObjectToFlagUndoList (PAD_TYPE, ptr1, pad, pad);
2855 TOGGLE_FLAG (DISPLAYNAMEFLAG, pad);
2857 END_LOOP;
2858 SetChangedFlag (true);
2859 IncrementUndoSerialNumber ();
2860 Draw ();
2861 break;
2863 case PIN_TYPE:
2864 if (TEST_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2))
2865 ErasePinName ((PinType *) ptr2);
2866 else
2867 DrawPinName ((PinType *) ptr2);
2868 AddObjectToFlagUndoList (PIN_TYPE, ptr1, ptr2, ptr3);
2869 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2);
2870 SetChangedFlag (true);
2871 IncrementUndoSerialNumber ();
2872 Draw ();
2873 break;
2875 case PAD_TYPE:
2876 if (TEST_FLAG (DISPLAYNAMEFLAG, (PadType *) ptr2))
2877 ErasePadName ((PadType *) ptr2);
2878 else
2879 DrawPadName ((PadType *) ptr2);
2880 AddObjectToFlagUndoList (PAD_TYPE, ptr1, ptr2, ptr3);
2881 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PadType *) ptr2);
2882 SetChangedFlag (true);
2883 IncrementUndoSerialNumber ();
2884 Draw ();
2885 break;
2886 case VIA_TYPE:
2887 if (TEST_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2))
2888 EraseViaName ((PinType *) ptr2);
2889 else
2890 DrawViaName ((PinType *) ptr2);
2891 AddObjectToFlagUndoList (VIA_TYPE, ptr1, ptr2, ptr3);
2892 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2);
2893 SetChangedFlag (true);
2894 IncrementUndoSerialNumber ();
2895 Draw ();
2896 break;
2898 break;
2900 default:
2901 err = 1;
2904 else if (function && str_dir)
2906 switch (GetFunctionID (function))
2908 case F_ToggleGrid:
2909 if (argc > 2)
2911 PCB->GridOffsetX = GetValue (argv[1], NULL, NULL);
2912 PCB->GridOffsetY = GetValue (argv[2], NULL, NULL);
2913 if (Settings.DrawGrid)
2914 Redraw ();
2916 break;
2918 default:
2919 err = 1;
2920 break;
2924 if (!err)
2925 return 0;
2927 AFAIL (display);
2930 /* --------------------------------------------------------------------------- */
2932 static const char mode_syntax[] =
2933 "Mode(Arc|Arrow|Copy|InsertPoint|Line|Lock|Move|None|PasteBuffer)\n"
2934 "Mode(Polygon|Rectangle|Remove|Rotate|Text|Thermal|Via)\n"
2935 "Mode(Notify|Release|Cancel|Stroke)\n"
2936 "Mode(Save|Restore)";
2938 static const char mode_help[] = "Change or use the tool mode.";
2940 /* %start-doc actions Mode
2942 @table @code
2944 @item Arc
2945 @itemx Arrow
2946 @itemx Copy
2947 @itemx InsertPoint
2948 @itemx Line
2949 @itemx Lock
2950 @itemx Move
2951 @itemx None
2952 @itemx PasteBuffer
2953 @itemx Polygon
2954 @itemx Rectangle
2955 @itemx Remove
2956 @itemx Rotate
2957 @itemx Text
2958 @itemx Thermal
2959 @itemx Via
2960 Select the indicated tool.
2962 @item Notify
2963 Called when you press the mouse button, or move the mouse.
2965 @item Release
2966 Called when you release the mouse button.
2968 @item Cancel
2969 Cancels any pending tool activity, allowing you to restart elsewhere.
2970 For example, this allows you to start a new line rather than attach a
2971 line to the previous line.
2973 @item Escape
2974 Similar to Cancel but calling this action a second time will return
2975 to the Arrow tool.
2977 @item Stroke
2978 If your @code{pcb} was built with libstroke, this invokes the stroke
2979 input method. If not, this will restart a drawing mode if you were
2980 drawing, else it will select objects.
2982 @item Save
2983 Remembers the current tool.
2985 @item Restore
2986 Restores the tool to the last saved tool.
2988 @end table
2990 %end-doc */
2992 static int
2993 ActionMode (int argc, char **argv, Coord x, Coord y)
2995 char *function = ARG (0);
2997 if (function)
2999 Note.X = Crosshair.X;
3000 Note.Y = Crosshair.Y;
3001 notify_crosshair_change (false);
3002 switch (GetFunctionID (function))
3004 case F_Arc:
3005 SetMode (ARC_MODE);
3006 break;
3007 case F_Arrow:
3008 SetMode (ARROW_MODE);
3009 break;
3010 case F_Copy:
3011 SetMode (COPY_MODE);
3012 break;
3013 case F_InsertPoint:
3014 SetMode (INSERTPOINT_MODE);
3015 break;
3016 case F_Line:
3017 SetMode (LINE_MODE);
3018 break;
3019 case F_Lock:
3020 SetMode (LOCK_MODE);
3021 break;
3022 case F_Move:
3023 SetMode (MOVE_MODE);
3024 break;
3025 case F_None:
3026 SetMode (NO_MODE);
3027 break;
3028 case F_Cancel:
3030 int saved_mode = Settings.Mode;
3031 SetMode (NO_MODE);
3032 SetMode (saved_mode);
3034 break;
3035 case F_Escape:
3037 switch (Settings.Mode)
3039 case VIA_MODE:
3040 case PASTEBUFFER_MODE:
3041 case TEXT_MODE:
3042 case ROTATE_MODE:
3043 case REMOVE_MODE:
3044 case MOVE_MODE:
3045 case COPY_MODE:
3046 case INSERTPOINT_MODE:
3047 case RUBBERBANDMOVE_MODE:
3048 case THERMAL_MODE:
3049 case LOCK_MODE:
3050 SetMode (NO_MODE);
3051 SetMode (ARROW_MODE);
3052 break;
3054 case LINE_MODE:
3055 if (Crosshair.AttachedLine.State == STATE_FIRST)
3056 SetMode (ARROW_MODE);
3057 else
3059 SetMode (NO_MODE);
3060 SetMode (LINE_MODE);
3062 break;
3064 case RECTANGLE_MODE:
3065 if (Crosshair.AttachedBox.State == STATE_FIRST)
3066 SetMode (ARROW_MODE);
3067 else
3069 SetMode (NO_MODE);
3070 SetMode (RECTANGLE_MODE);
3072 break;
3074 case POLYGON_MODE:
3075 if (Crosshair.AttachedLine.State == STATE_FIRST)
3076 SetMode (ARROW_MODE);
3077 else
3079 SetMode (NO_MODE);
3080 SetMode (POLYGON_MODE);
3082 break;
3084 case POLYGONHOLE_MODE:
3085 if (Crosshair.AttachedLine.State == STATE_FIRST)
3086 SetMode (ARROW_MODE);
3087 else
3089 SetMode (NO_MODE);
3090 SetMode (POLYGONHOLE_MODE);
3092 break;
3094 case ARC_MODE:
3095 if (Crosshair.AttachedBox.State == STATE_FIRST)
3096 SetMode (ARROW_MODE);
3097 else
3099 SetMode (NO_MODE);
3100 SetMode (ARC_MODE);
3102 break;
3104 case ARROW_MODE:
3105 break;
3107 default:
3108 break;
3111 break;
3113 case F_Notify:
3114 NotifyMode ();
3115 break;
3116 case F_PasteBuffer:
3117 SetMode (PASTEBUFFER_MODE);
3118 break;
3119 case F_Polygon:
3120 SetMode (POLYGON_MODE);
3121 break;
3122 case F_PolygonHole:
3123 SetMode (POLYGONHOLE_MODE);
3124 break;
3125 #ifndef HAVE_LIBSTROKE
3126 case F_Release:
3127 ReleaseMode ();
3128 break;
3129 #else
3130 case F_Release:
3131 if (mid_stroke)
3132 FinishStroke ();
3133 else
3134 ReleaseMode ();
3135 break;
3136 #endif
3137 case F_Remove:
3138 SetMode (REMOVE_MODE);
3139 break;
3140 case F_Rectangle:
3141 SetMode (RECTANGLE_MODE);
3142 break;
3143 case F_Rotate:
3144 SetMode (ROTATE_MODE);
3145 break;
3146 case F_Stroke:
3147 #ifdef HAVE_LIBSTROKE
3148 mid_stroke = true;
3149 StrokeBox.X1 = Crosshair.X;
3150 StrokeBox.Y1 = Crosshair.Y;
3151 break;
3152 #else
3153 /* Handle middle mouse button restarts of drawing mode. If not in
3154 | a drawing mode, middle mouse button will select objects.
3156 if (Settings.Mode == LINE_MODE
3157 && Crosshair.AttachedLine.State != STATE_FIRST)
3159 SetMode (LINE_MODE);
3161 else if (Settings.Mode == ARC_MODE
3162 && Crosshair.AttachedBox.State != STATE_FIRST)
3163 SetMode (ARC_MODE);
3164 else if (Settings.Mode == RECTANGLE_MODE
3165 && Crosshair.AttachedBox.State != STATE_FIRST)
3166 SetMode (RECTANGLE_MODE);
3167 else if (Settings.Mode == POLYGON_MODE
3168 && Crosshair.AttachedLine.State != STATE_FIRST)
3169 SetMode (POLYGON_MODE);
3170 else
3172 SaveMode ();
3173 saved_mode = true;
3174 SetMode (ARROW_MODE);
3175 NotifyMode ();
3177 break;
3178 #endif
3179 case F_Text:
3180 SetMode (TEXT_MODE);
3181 break;
3182 case F_Thermal:
3183 SetMode (THERMAL_MODE);
3184 break;
3185 case F_Via:
3186 SetMode (VIA_MODE);
3187 break;
3189 case F_Restore: /* restore the last saved mode */
3190 RestoreMode ();
3191 break;
3193 case F_Save: /* save currently selected mode */
3194 SaveMode ();
3195 break;
3197 notify_crosshair_change (true);
3198 return 0;
3201 AFAIL (mode);
3204 /* --------------------------------------------------------------------------- */
3206 static const char removeselected_syntax[] = "RemoveSelected()";
3208 static const char removeselected_help[] = "Removes any selected objects.";
3210 /* %start-doc actions RemoveSelected
3212 %end-doc */
3214 static int
3215 ActionRemoveSelected (int argc, char **argv, Coord x, Coord y)
3217 if (RemoveSelected ())
3218 SetChangedFlag (true);
3219 return 0;
3222 /* --------------------------------------------------------------------------- */
3224 static const char renumber_syntax[] = "Renumber()\n"
3225 "Renumber(filename)";
3227 static const char renumber_help[] =
3228 "Renumber all elements. The changes will be recorded to filename\n"
3229 "for use in backannotating these changes to the schematic.";
3231 /* %start-doc actions Renumber
3233 %end-doc */
3235 static int
3236 ActionRenumber (int argc, char **argv, Coord x, Coord y)
3238 bool changed = false;
3239 ElementType **element_list;
3240 ElementType **locked_element_list;
3241 unsigned int i, j, k, cnt, lock_cnt;
3242 unsigned int tmpi;
3243 size_t sz;
3244 char *tmps;
3245 char *name;
3246 FILE *out;
3247 static char * default_file = NULL;
3248 size_t cnt_list_sz = 100;
3249 struct _cnt_list
3251 char *name;
3252 unsigned int cnt;
3253 } *cnt_list;
3254 char **was, **is, *pin;
3255 unsigned int c_cnt = 0;
3256 int unique, ok;
3257 int free_name = 0;
3259 if (argc < 1)
3262 * We deal with the case where name already exists in this
3263 * function so the GUI doesn't need to deal with it
3265 name = gui->fileselect (_("Save Renumber Annotation File As ..."),
3266 _("Choose a file to record the renumbering to.\n"
3267 "This file may be used to back annotate the\n"
3268 "change to the schematics.\n"),
3269 default_file, ".eco", "eco",
3272 free_name = 1;
3274 else
3275 name = argv[0];
3277 if (default_file)
3279 free (default_file);
3280 default_file = NULL;
3283 if (name && *name)
3285 default_file = strdup (name);
3288 if ((out = fopen (name, "r")))
3290 fclose (out);
3291 if (!gui->confirm_dialog (_("File exists! Ok to overwrite?"), 0))
3293 if (free_name && name)
3294 free (name);
3295 return 0;
3299 if ((out = fopen (name, "w")) == NULL)
3301 Message (_("Could not open %s\n"), name);
3302 if (free_name && name)
3303 free (name);
3304 return 1;
3307 if (free_name && name)
3308 free (name);
3310 fprintf (out, "*COMMENT* PCB Annotation File\n");
3311 fprintf (out, "*FILEVERSION* 20061031\n");
3314 * Make a first pass through all of the elements and sort them out
3315 * by location on the board. While here we also collect a list of
3316 * locked elements.
3318 * We'll actually renumber things in the 2nd pass.
3320 element_list = (ElementType **)calloc (PCB->Data->ElementN, sizeof (ElementType *));
3321 locked_element_list = (ElementType **)calloc (PCB->Data->ElementN, sizeof (ElementType *));
3322 was = (char **)calloc (PCB->Data->ElementN, sizeof (char *));
3323 is = (char **)calloc (PCB->Data->ElementN, sizeof (char *));
3324 if (element_list == NULL || locked_element_list == NULL || was == NULL
3325 || is == NULL)
3327 fprintf (stderr, "calloc() failed in %s\n", __FUNCTION__);
3328 exit (1);
3332 cnt = 0;
3333 lock_cnt = 0;
3334 ELEMENT_LOOP (PCB->Data);
3336 if (TEST_FLAG (LOCKFLAG, element->Name) || TEST_FLAG (LOCKFLAG, element))
3339 * add to the list of locked elements which we won't try to
3340 * renumber and whose reference designators are now reserved.
3342 pcb_fprintf (out,
3343 "*WARN* Element \"%s\" at %$md is locked and will not be renumbered.\n",
3344 UNKNOWN (NAMEONPCB_NAME (element)), element->MarkX, element->MarkY);
3345 locked_element_list[lock_cnt] = element;
3346 lock_cnt++;
3349 else
3351 /* count of devices which will be renumbered */
3352 cnt++;
3354 /* search for correct position in the list */
3355 i = 0;
3356 while (element_list[i] && element->MarkY > element_list[i]->MarkY)
3357 i++;
3360 * We have found the position where we have the first element that
3361 * has the same Y value or a lower Y value. Now move forward if
3362 * needed through the X values
3364 while (element_list[i]
3365 && element->MarkY == element_list[i]->MarkY
3366 && element->MarkX > element_list[i]->MarkX)
3367 i++;
3369 for (j = cnt - 1; j > i; j--)
3371 element_list[j] = element_list[j - 1];
3373 element_list[i] = element;
3376 END_LOOP;
3380 * Now that the elements are sorted by board position, we go through
3381 * and renumber them.
3385 * turn off the flag which requires unique names so it doesn't get
3386 * in our way. When we're done with the renumber we will have unique
3387 * names.
3389 unique = TEST_FLAG (UNIQUENAMEFLAG, PCB);
3390 CLEAR_FLAG (UNIQUENAMEFLAG, PCB);
3392 cnt_list = (struct _cnt_list *)calloc (cnt_list_sz, sizeof (struct _cnt_list));
3393 for (i = 0; i < cnt; i++)
3395 /* If there is no refdes, maybe just spit out a warning */
3396 if (NAMEONPCB_NAME (element_list[i]))
3398 /* figure out the prefix */
3399 tmps = strdup (NAMEONPCB_NAME (element_list[i]));
3400 j = 0;
3401 while (tmps[j] && (tmps[j] < '0' || tmps[j] > '9')
3402 && tmps[j] != '?')
3403 j++;
3404 tmps[j] = '\0';
3406 /* check the counter for this prefix */
3407 for (j = 0;
3408 cnt_list[j].name && (strcmp (cnt_list[j].name, tmps) != 0)
3409 && j < cnt_list_sz; j++);
3411 /* grow the list if needed */
3412 if (j == cnt_list_sz)
3414 cnt_list_sz += 100;
3415 cnt_list = (struct _cnt_list *)realloc (cnt_list, cnt_list_sz);
3416 if (cnt_list == NULL)
3418 fprintf (stderr, "realloc failed() in %s\n", __FUNCTION__);
3419 exit (1);
3421 /* zero out the memory that we added */
3422 for (tmpi = j; tmpi < cnt_list_sz; tmpi++)
3424 cnt_list[tmpi].name = NULL;
3425 cnt_list[tmpi].cnt = 0;
3430 * start a new counter if we don't have a counter for this
3431 * prefix
3433 if (!cnt_list[j].name)
3435 cnt_list[j].name = strdup (tmps);
3436 cnt_list[j].cnt = 0;
3440 * check to see if the new refdes is already used by a
3441 * locked element
3445 ok = 1;
3446 cnt_list[j].cnt++;
3447 free (tmps);
3449 /* space for the prefix plus 1 digit plus the '\0' */
3450 sz = strlen (cnt_list[j].name) + 2;
3452 /* and 1 more per extra digit needed to hold the number */
3453 tmpi = cnt_list[j].cnt;
3454 while (tmpi > 10)
3456 sz++;
3457 tmpi = tmpi / 10;
3459 tmps = (char *)malloc (sz * sizeof (char));
3460 sprintf (tmps, "%s%d", cnt_list[j].name, cnt_list[j].cnt);
3463 * now compare to the list of reserved (by locked
3464 * elements) names
3466 for (k = 0; k < lock_cnt; k++)
3468 if (strcmp
3469 (UNKNOWN (NAMEONPCB_NAME (locked_element_list[k])),
3470 tmps) == 0)
3472 ok = 0;
3473 break;
3478 while (!ok);
3480 if (strcmp (tmps, NAMEONPCB_NAME (element_list[i])) != 0)
3482 fprintf (out, "*RENAME* \"%s\" \"%s\"\n",
3483 NAMEONPCB_NAME (element_list[i]), tmps);
3485 /* add this rename to our table of renames so we can update the netlist */
3486 was[c_cnt] = strdup (NAMEONPCB_NAME (element_list[i]));
3487 is[c_cnt] = strdup (tmps);
3488 c_cnt++;
3490 AddObjectToChangeNameUndoList (ELEMENT_TYPE, NULL, NULL,
3491 element_list[i],
3492 NAMEONPCB_NAME (element_list
3493 [i]));
3495 ChangeObjectName (ELEMENT_TYPE, element_list[i], NULL, NULL,
3496 tmps);
3497 changed = true;
3499 /* we don't free tmps in this case because it is used */
3501 else
3502 free (tmps);
3504 else
3506 pcb_fprintf (out, "*WARN* Element at %$md has no name.\n",
3507 element_list[i]->MarkX, element_list[i]->MarkY);
3512 fclose (out);
3514 /* restore the unique flag setting */
3515 if (unique)
3516 SET_FLAG (UNIQUENAMEFLAG, PCB);
3518 if (changed)
3521 /* update the netlist */
3522 AddNetlistLibToUndoList (&(PCB->NetlistLib));
3524 /* iterate over each net */
3525 for (i = 0; i < PCB->NetlistLib.MenuN; i++)
3528 /* iterate over each pin on the net */
3529 for (j = 0; j < PCB->NetlistLib.Menu[i].EntryN; j++)
3532 /* figure out the pin number part from strings like U3-21 */
3533 tmps = strdup (PCB->NetlistLib.Menu[i].Entry[j].ListEntry);
3534 for (k = 0; tmps[k] && tmps[k] != '-'; k++);
3535 tmps[k] = '\0';
3536 pin = tmps + k + 1;
3538 /* iterate over the list of changed reference designators */
3539 for (k = 0; k < c_cnt; k++)
3542 * if the pin needs to change, change it and quit
3543 * searching in the list.
3545 if (strcmp (tmps, was[k]) == 0)
3547 free (PCB->NetlistLib.Menu[i].Entry[j].ListEntry);
3548 PCB->NetlistLib.Menu[i].Entry[j].ListEntry =
3549 (char *)malloc ((strlen (is[k]) + strlen (pin) +
3550 2) * sizeof (char));
3551 sprintf (PCB->NetlistLib.Menu[i].Entry[j].ListEntry,
3552 "%s-%s", is[k], pin);
3553 k = c_cnt;
3557 free (tmps);
3560 for (k = 0; k < c_cnt; k++)
3562 free (was[k]);
3563 free (is[k]);
3566 NetlistChanged (0);
3567 IncrementUndoSerialNumber ();
3568 SetChangedFlag (true);
3571 free (locked_element_list);
3572 free (element_list);
3573 free (cnt_list);
3574 return 0;
3578 /* --------------------------------------------------------------------------- */
3580 static const char ripup_syntax[] = "RipUp(All|Selected|Element)";
3582 static const char ripup_help[] =
3583 "Ripup auto-routed tracks, or convert an element to parts.";
3585 /* %start-doc actions RipUp
3587 @table @code
3589 @item All
3590 Removes all lines and vias which were created by the autorouter.
3592 @item Selected
3593 Removes all selected lines and vias which were created by the
3594 autorouter.
3596 @item Element
3597 Converts the element under the cursor to parts (vias and lines). Note
3598 that this uses the highest numbered paste buffer.
3600 @end table
3602 %end-doc */
3604 static int
3605 ActionRipUp (int argc, char **argv, Coord x, Coord y)
3607 char *function = ARG (0);
3608 bool changed = false;
3610 if (function)
3612 switch (GetFunctionID (function))
3614 case F_All:
3615 ALLLINE_LOOP (PCB->Data);
3617 if (TEST_FLAG (AUTOFLAG, line) && !TEST_FLAG (LOCKFLAG, line))
3619 RemoveObject (LINE_TYPE, layer, line, line);
3620 changed = true;
3623 ENDALL_LOOP;
3624 ALLARC_LOOP (PCB->Data);
3626 if (TEST_FLAG (AUTOFLAG, arc) && !TEST_FLAG (LOCKFLAG, arc))
3628 RemoveObject (ARC_TYPE, layer, arc, arc);
3629 changed = true;
3632 ENDALL_LOOP;
3633 VIA_LOOP (PCB->Data);
3635 if (TEST_FLAG (AUTOFLAG, via) && !TEST_FLAG (LOCKFLAG, via))
3637 RemoveObject (VIA_TYPE, via, via, via);
3638 changed = true;
3641 END_LOOP;
3643 if (changed)
3645 IncrementUndoSerialNumber ();
3646 SetChangedFlag (true);
3648 break;
3649 case F_Selected:
3650 VISIBLELINE_LOOP (PCB->Data);
3652 if (TEST_FLAGS (AUTOFLAG | SELECTEDFLAG, line)
3653 && !TEST_FLAG (LOCKFLAG, line))
3655 RemoveObject (LINE_TYPE, layer, line, line);
3656 changed = true;
3659 ENDALL_LOOP;
3660 if (PCB->ViaOn)
3661 VIA_LOOP (PCB->Data);
3663 if (TEST_FLAGS (AUTOFLAG | SELECTEDFLAG, via)
3664 && !TEST_FLAG (LOCKFLAG, via))
3666 RemoveObject (VIA_TYPE, via, via, via);
3667 changed = true;
3670 END_LOOP;
3671 if (changed)
3673 IncrementUndoSerialNumber ();
3674 SetChangedFlag (true);
3676 break;
3677 case F_Element:
3679 void *ptr1, *ptr2, *ptr3;
3681 if (SearchScreen (Crosshair.X, Crosshair.Y, ELEMENT_TYPE,
3682 &ptr1, &ptr2, &ptr3) != NO_TYPE)
3684 Note.Buffer = Settings.BufferNumber;
3685 SetBufferNumber (MAX_BUFFER - 1);
3686 ClearBuffer (PASTEBUFFER);
3687 CopyObjectToBuffer (PASTEBUFFER->Data, PCB->Data,
3688 ELEMENT_TYPE, ptr1, ptr2, ptr3);
3689 SmashBufferElement (PASTEBUFFER);
3690 PASTEBUFFER->X = 0;
3691 PASTEBUFFER->Y = 0;
3692 SaveUndoSerialNumber ();
3693 EraseObject (ELEMENT_TYPE, ptr1, ptr1);
3694 MoveObjectToRemoveUndoList (ELEMENT_TYPE, ptr1, ptr2, ptr3);
3695 RestoreUndoSerialNumber ();
3696 CopyPastebufferToLayout (0, 0);
3697 SetBufferNumber (Note.Buffer);
3698 SetChangedFlag (true);
3701 break;
3704 return 0;
3707 /* --------------------------------------------------------------------------- */
3709 static const char addrats_syntax[] = "AddRats(AllRats|SelectedRats|Close)";
3711 static const char addrats_help[] = "Add one or more rat lines to the board.";
3713 /* %start-doc actions AddRats
3715 @table @code
3717 @item AllRats
3718 Create rat lines for all loaded nets that aren't already connected on
3719 with copper.
3721 @item SelectedRats
3722 Similarly, but only add rat lines for nets connected to selected pins
3723 and pads.
3725 @item Close
3726 Selects the shortest unselected rat on the board.
3728 @end table
3730 %end-doc */
3732 static int
3733 ActionAddRats (int argc, char **argv, Coord x, Coord y)
3735 char *function = ARG (0);
3736 RatType *shorty;
3737 float len, small;
3739 if (function)
3741 if (Settings.RatWarn)
3742 ClearWarnings ();
3743 switch (GetFunctionID (function))
3745 case F_AllRats:
3746 if (AddAllRats (false, NULL))
3747 SetChangedFlag (true);
3748 break;
3749 case F_SelectedRats:
3750 case F_Selected:
3751 if (AddAllRats (true, NULL))
3752 SetChangedFlag (true);
3753 break;
3754 case F_Close:
3755 small = SQUARE (MAX_COORD);
3756 shorty = NULL;
3757 RAT_LOOP (PCB->Data);
3759 if (TEST_FLAG (SELECTEDFLAG, line))
3760 continue;
3761 len = SQUARE (line->Point1.X - line->Point2.X) +
3762 SQUARE (line->Point1.Y - line->Point2.Y);
3763 if (len < small)
3765 small = len;
3766 shorty = line;
3769 END_LOOP;
3770 if (shorty)
3772 AddObjectToFlagUndoList (RATLINE_TYPE, shorty, shorty, shorty);
3773 SET_FLAG (SELECTEDFLAG, shorty);
3774 DrawRat (shorty);
3775 Draw ();
3776 CenterDisplay ((shorty->Point2.X + shorty->Point1.X) / 2,
3777 (shorty->Point2.Y + shorty->Point1.Y) / 2);
3779 break;
3782 return 0;
3785 /* --------------------------------------------------------------------------- */
3787 static const char delete_syntax[] =
3788 "Delete(Object|Selected)\n"
3789 "Delete(AllRats|SelectedRats)";
3791 static const char delete_help[] = "Delete stuff.";
3793 /* %start-doc actions Delete
3795 %end-doc */
3797 static int
3798 ActionDelete (int argc, char **argv, Coord x, Coord y)
3800 char *function = ARG (0);
3801 int id = GetFunctionID (function);
3803 Note.X = Crosshair.X;
3804 Note.Y = Crosshair.Y;
3806 if (id == -1) /* no arg */
3808 if (RemoveSelected() == false)
3809 id = F_Object;
3812 switch (id)
3814 case F_Object:
3815 SaveMode();
3816 SetMode(REMOVE_MODE);
3817 NotifyMode();
3818 RestoreMode();
3819 break;
3820 case F_Selected:
3821 RemoveSelected();
3822 break;
3823 case F_AllRats:
3824 if (DeleteRats (false))
3825 SetChangedFlag (true);
3826 break;
3827 case F_SelectedRats:
3828 if (DeleteRats (true))
3829 SetChangedFlag (true);
3830 break;
3833 return 0;
3836 /* --------------------------------------------------------------------------- */
3838 static const char deleterats_syntax[] =
3839 "DeleteRats(AllRats|Selected|SelectedRats)";
3841 static const char deleterats_help[] = "Delete rat lines.";
3843 /* %start-doc actions DeleteRats
3845 %end-doc */
3847 static int
3848 ActionDeleteRats (int argc, char **argv, Coord x, Coord y)
3850 char *function = ARG (0);
3851 if (function)
3853 if (Settings.RatWarn)
3854 ClearWarnings ();
3855 switch (GetFunctionID (function))
3857 case F_AllRats:
3858 if (DeleteRats (false))
3859 SetChangedFlag (true);
3860 break;
3861 case F_SelectedRats:
3862 case F_Selected:
3863 if (DeleteRats (true))
3864 SetChangedFlag (true);
3865 break;
3868 return 0;
3871 /* --------------------------------------------------------------------------- */
3873 static const char autoplace_syntax[] = "AutoPlaceSelected()";
3875 static const char autoplace_help[] = "Auto-place selected components.";
3877 /* %start-doc actions AutoPlaceSelected
3879 Attempts to re-arrange the selected components such that the nets
3880 connecting them are minimized. Note that you cannot undo this.
3882 %end-doc */
3884 static int
3885 ActionAutoPlaceSelected (int argc, char **argv, Coord x, Coord y)
3887 hid_action("Busy");
3888 if (gui->confirm_dialog (_("Auto-placement can NOT be undone.\n"
3889 "Do you want to continue anyway?\n"), 0))
3891 if (AutoPlaceSelected ())
3892 SetChangedFlag (true);
3894 return 0;
3897 /* --------------------------------------------------------------------------- */
3899 static const char autoroute_syntax[] = "AutoRoute(AllRats|SelectedRats)";
3901 static const char autoroute_help[] = "Auto-route some or all rat lines.";
3903 /* %start-doc actions AutoRoute
3905 @table @code
3907 @item AllRats
3908 Attempt to autoroute all rats.
3910 @item SelectedRats
3911 Attempt to autoroute the selected rats.
3913 @end table
3915 Before autorouting, it's important to set up a few things. First,
3916 make sure any layers you aren't using are disabled, else the
3917 autorouter may use them. Next, make sure the current line and via
3918 styles are set accordingly. Last, make sure "new lines clear
3919 polygons" is set, in case you eventually want to add a copper pour.
3921 Autorouting takes a while. During this time, the program may not be
3922 responsive.
3924 %end-doc */
3926 static int
3927 ActionAutoRoute (int argc, char **argv, Coord x, Coord y)
3929 char *function = ARG (0);
3930 hid_action("Busy");
3931 if (function) /* one parameter */
3933 switch (GetFunctionID (function))
3935 case F_AllRats:
3936 if (AutoRoute (false))
3937 SetChangedFlag (true);
3938 break;
3939 case F_SelectedRats:
3940 case F_Selected:
3941 if (AutoRoute (true))
3942 SetChangedFlag (true);
3943 break;
3946 return 0;
3949 /* --------------------------------------------------------------------------- */
3951 static const char markcrosshair_syntax[] =
3952 "MarkCrosshair()\n"
3953 "MarkCrosshair(Center)";
3955 static const char markcrosshair_help[] = "Set/Reset the Crosshair mark.";
3957 /* %start-doc actions MarkCrosshair
3959 The ``mark'' is a small X-shaped target on the display which is
3960 treated like a second origin (the normal origin is the upper let
3961 corner of the board). The GUI will display a second set of
3962 coordinates for this mark, which tells you how far you are from it.
3964 If no argument is given, the mark is toggled - disabled if it was
3965 enabled, or enabled at the current cursor position of disabled. If
3966 the @code{Center} argument is given, the mark is moved to the current
3967 cursor location.
3969 %end-doc */
3971 static int
3972 ActionMarkCrosshair (int argc, char **argv, Coord x, Coord y)
3974 char *function = ARG (0);
3975 if (!function || !*function)
3977 if (Marked.status)
3979 notify_mark_change (false);
3980 Marked.status = false;
3981 notify_mark_change (true);
3983 else
3985 notify_mark_change (false);
3986 Marked.status = false;
3987 Marked.status = true;
3988 Marked.X = Crosshair.X;
3989 Marked.Y = Crosshair.Y;
3990 notify_mark_change (true);
3993 else if (GetFunctionID (function) == F_Center)
3995 notify_mark_change (false);
3996 Marked.status = true;
3997 Marked.X = Crosshair.X;
3998 Marked.Y = Crosshair.Y;
3999 notify_mark_change (true);
4001 return 0;
4004 /* --------------------------------------------------------------------------- */
4006 static const char changesize_syntax[] =
4007 "ChangeSize(Object, delta)\n"
4008 "ChangeSize(SelectedObjects|Selected, delta)\n"
4009 "ChangeSize(SelectedLines|SelectedPins|SelectedVias, delta)\n"
4010 "ChangeSize(SelectedPads|SelectedTexts|SelectedNames, delta)\n"
4011 "ChangeSize(SelectedElements, delta)";
4013 static const char changesize_help[] = "Changes the size of objects.";
4015 /* %start-doc actions ChangeSize
4017 For lines and arcs, this changes the width. For pins and vias, this
4018 changes the overall diameter of the copper annulus. For pads, this
4019 changes the width and, indirectly, the length. For texts and names,
4020 this changes the scaling factor. For elements, this changes the width
4021 of the silk layer lines and arcs for this element.
4023 %end-doc */
4025 static int
4026 ActionChangeSize (int argc, char **argv, Coord x, Coord y)
4028 char *function = ARG (0);
4029 char *delta = ARG (1);
4030 char *units = ARG (2);
4031 bool absolute; /* indicates if absolute size is given */
4032 Coord value;
4034 if (function && delta)
4036 value = GetValue (delta, units, &absolute);
4037 if (value == 0)
4038 value = delta[0] == '-' ? -Settings.increments->size
4039 : Settings.increments->size;
4040 switch (GetFunctionID (function))
4042 case F_Object:
4044 int type;
4045 void *ptr1, *ptr2, *ptr3;
4047 if ((type =
4048 SearchScreen (Crosshair.X, Crosshair.Y, CHANGESIZE_TYPES,
4049 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4050 if (TEST_FLAG (LOCKFLAG, (PinType *) ptr2))
4051 Message (_("Sorry, the object is locked\n"));
4052 if (ChangeObjectSize (type, ptr1, ptr2, ptr3, value, absolute))
4053 SetChangedFlag (true);
4054 break;
4057 case F_SelectedVias:
4058 if (ChangeSelectedSize (VIA_TYPE, value, absolute))
4059 SetChangedFlag (true);
4060 break;
4062 case F_SelectedPins:
4063 if (ChangeSelectedSize (PIN_TYPE, value, absolute))
4064 SetChangedFlag (true);
4065 break;
4067 case F_SelectedPads:
4068 if (ChangeSelectedSize (PAD_TYPE, value, absolute))
4069 SetChangedFlag (true);
4070 break;
4072 case F_SelectedArcs:
4073 if (ChangeSelectedSize (ARC_TYPE, value, absolute))
4074 SetChangedFlag (true);
4075 break;
4077 case F_SelectedLines:
4078 if (ChangeSelectedSize (LINE_TYPE, value, absolute))
4079 SetChangedFlag (true);
4080 break;
4082 case F_SelectedTexts:
4083 if (ChangeSelectedSize (TEXT_TYPE, value, absolute))
4084 SetChangedFlag (true);
4085 break;
4087 case F_SelectedNames:
4088 if (ChangeSelectedSize (ELEMENTNAME_TYPE, value, absolute))
4089 SetChangedFlag (true);
4090 break;
4092 case F_SelectedElements:
4093 if (ChangeSelectedSize (ELEMENT_TYPE, value, absolute))
4094 SetChangedFlag (true);
4095 break;
4097 case F_Selected:
4098 case F_SelectedObjects:
4099 if (ChangeSelectedSize (CHANGESIZE_TYPES, value, absolute))
4100 SetChangedFlag (true);
4101 break;
4104 return 0;
4107 /* --------------------------------------------------------------------------- */
4109 static const char changedrillsize_syntax[] =
4110 "ChangeDrillSize(Object, delta)\n"
4111 "ChangeDrillSize(SelectedPins|SelectedVias|Selected|SelectedObjects, delta)";
4113 static const char changedrillsize_help[] =
4114 "Changes the drilling hole size of objects.";
4116 /* %start-doc actions ChangeDrillSize
4118 %end-doc */
4120 static int
4121 ActionChange2ndSize (int argc, char **argv, Coord x, Coord y)
4123 char *function = ARG (0);
4124 char *delta = ARG (1);
4125 char *units = ARG (2);
4126 bool absolute;
4127 Coord value;
4129 if (function && delta)
4131 value = GetValue (delta, units, &absolute);
4132 switch (GetFunctionID (function))
4134 case F_Object:
4136 int type;
4137 void *ptr1, *ptr2, *ptr3;
4139 gui->get_coords (_("Select an Object"), &x, &y);
4140 if ((type =
4141 SearchScreen (x, y, CHANGE2NDSIZE_TYPES,
4142 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4143 if (ChangeObject2ndSize
4144 (type, ptr1, ptr2, ptr3, value, absolute, true))
4145 SetChangedFlag (true);
4146 break;
4149 case F_SelectedVias:
4150 if (ChangeSelected2ndSize (VIA_TYPE, value, absolute))
4151 SetChangedFlag (true);
4152 break;
4154 case F_SelectedPins:
4155 if (ChangeSelected2ndSize (PIN_TYPE, value, absolute))
4156 SetChangedFlag (true);
4157 break;
4158 case F_Selected:
4159 case F_SelectedObjects:
4160 if (ChangeSelected2ndSize (PIN_TYPES, value, absolute))
4161 SetChangedFlag (true);
4162 break;
4165 return 0;
4168 /* --------------------------------------------------------------------------- */
4170 static const char changeclearsize_syntax[] =
4171 "ChangeClearSize(Object, delta)\n"
4172 "ChangeClearSize(SelectedPins|SelectedPads|SelectedVias, delta)\n"
4173 "ChangeClearSize(SelectedLines|SelectedArcs, delta\n"
4174 "ChangeClearSize(Selected|SelectedObjects, delta)";
4176 static const char changeclearsize_help[] =
4177 "Changes the clearance size of objects.";
4179 /* %start-doc actions ChangeClearSize
4181 If the solder mask is currently showing, this action changes the
4182 solder mask clearance. If the mask is not showing, this action
4183 changes the polygon clearance.
4185 %end-doc */
4187 static int
4188 ActionChangeClearSize (int argc, char **argv, Coord x, Coord y)
4190 char *function = ARG (0);
4191 char *delta = ARG (1);
4192 char *units = ARG (2);
4193 bool absolute;
4194 Coord value;
4196 if (function && delta)
4198 value = 2 * GetValue (delta, units, &absolute);
4199 if (value == 0)
4200 value = delta[0] == '-' ? -Settings.increments->clear
4201 : Settings.increments->clear;
4202 switch (GetFunctionID (function))
4204 case F_Object:
4206 int type;
4207 void *ptr1, *ptr2, *ptr3;
4209 gui->get_coords (_("Select an Object"), &x, &y);
4210 if ((type =
4211 SearchScreen (x, y,
4212 CHANGECLEARSIZE_TYPES, &ptr1, &ptr2,
4213 &ptr3)) != NO_TYPE)
4214 if (ChangeObjectClearSize (type, ptr1, ptr2, ptr3, value, absolute))
4215 SetChangedFlag (true);
4216 break;
4218 case F_SelectedVias:
4219 if (ChangeSelectedClearSize (VIA_TYPE, value, absolute))
4220 SetChangedFlag (true);
4221 break;
4222 case F_SelectedPads:
4223 if (ChangeSelectedClearSize (PAD_TYPE, value, absolute))
4224 SetChangedFlag (true);
4225 break;
4226 case F_SelectedPins:
4227 if (ChangeSelectedClearSize (PIN_TYPE, value, absolute))
4228 SetChangedFlag (true);
4229 break;
4230 case F_SelectedLines:
4231 if (ChangeSelectedClearSize (LINE_TYPE, value, absolute))
4232 SetChangedFlag (true);
4233 break;
4234 case F_SelectedArcs:
4235 if (ChangeSelectedClearSize (ARC_TYPE, value, absolute))
4236 SetChangedFlag (true);
4237 break;
4238 case F_Selected:
4239 case F_SelectedObjects:
4240 if (ChangeSelectedClearSize (CHANGECLEARSIZE_TYPES, value, absolute))
4241 SetChangedFlag (true);
4242 break;
4245 return 0;
4248 /* --------------------------------------------------------------------------- */
4250 static const char minmaskgap_syntax[] =
4251 "MinMaskGap(delta)\n"
4252 "MinMaskGap(Selected, delta)";
4254 static const char minmaskgap_help[] =
4255 "Ensures the mask is a minimum distance from pins and pads.";
4257 /* %start-doc actions MinMaskGap
4259 Checks all specified pins and/or pads, and increases the mask if
4260 needed to ensure a minimum distance between the pin or pad edge and
4261 the mask edge.
4263 %end-doc */
4265 static int
4266 ActionMinMaskGap (int argc, char **argv, Coord x, Coord y)
4268 char *function = ARG (0);
4269 char *delta = ARG (1);
4270 char *units = ARG (2);
4271 bool absolute;
4272 Coord value;
4273 int flags;
4275 if (!function)
4276 return 1;
4277 if (strcasecmp (function, "Selected") == 0)
4278 flags = SELECTEDFLAG;
4279 else
4281 units = delta;
4282 delta = function;
4283 flags = 0;
4285 value = 2 * GetValue (delta, units, &absolute);
4287 SaveUndoSerialNumber ();
4288 ELEMENT_LOOP (PCB->Data);
4290 PIN_LOOP (element);
4292 if (!TEST_FLAGS (flags, pin))
4293 continue;
4294 if (pin->Mask < pin->Thickness + value)
4296 ChangeObjectMaskSize (PIN_TYPE, element, pin, 0,
4297 pin->Thickness + value, 1);
4298 RestoreUndoSerialNumber ();
4301 END_LOOP;
4302 PAD_LOOP (element);
4304 if (!TEST_FLAGS (flags, pad))
4305 continue;
4306 if (pad->Mask < pad->Thickness + value)
4308 ChangeObjectMaskSize (PAD_TYPE, element, pad, 0,
4309 pad->Thickness + value, 1);
4310 RestoreUndoSerialNumber ();
4313 END_LOOP;
4315 END_LOOP;
4316 VIA_LOOP (PCB->Data);
4318 if (!TEST_FLAGS (flags, via))
4319 continue;
4320 if (via->Mask && via->Mask < via->Thickness + value)
4322 ChangeObjectMaskSize (VIA_TYPE, via, 0, 0, via->Thickness + value, 1);
4323 RestoreUndoSerialNumber ();
4326 END_LOOP;
4327 RestoreUndoSerialNumber ();
4328 IncrementUndoSerialNumber ();
4329 return 0;
4332 /* --------------------------------------------------------------------------- */
4334 static const char mincleargap_syntax[] =
4335 "MinClearGap(delta)\n"
4336 "MinClearGap(Selected, delta)";
4338 static const char mincleargap_help[] =
4339 "Ensures that polygons are a minimum distance from objects.";
4341 /* %start-doc actions MinClearGap
4343 Checks all specified objects, and increases the polygon clearance if
4344 needed to ensure a minimum distance between their edges and the
4345 polygon edges.
4347 %end-doc */
4349 static int
4350 ActionMinClearGap (int argc, char **argv, Coord x, Coord y)
4352 char *function = ARG (0);
4353 char *delta = ARG (1);
4354 char *units = ARG (2);
4355 bool absolute;
4356 Coord value;
4357 int flags;
4359 if (!function)
4360 return 1;
4361 if (strcasecmp (function, "Selected") == 0)
4362 flags = SELECTEDFLAG;
4363 else
4365 units = delta;
4366 delta = function;
4367 flags = 0;
4369 value = 2 * GetValue (delta, units, &absolute);
4371 SaveUndoSerialNumber ();
4372 ELEMENT_LOOP (PCB->Data);
4374 PIN_LOOP (element);
4376 if (!TEST_FLAGS (flags, pin))
4377 continue;
4378 if (pin->Clearance < value)
4380 ChangeObjectClearSize (PIN_TYPE, element, pin, 0,
4381 value, 1);
4382 RestoreUndoSerialNumber ();
4385 END_LOOP;
4386 PAD_LOOP (element);
4388 if (!TEST_FLAGS (flags, pad))
4389 continue;
4390 if (pad->Clearance < value)
4392 ChangeObjectClearSize (PAD_TYPE, element, pad, 0,
4393 value, 1);
4394 RestoreUndoSerialNumber ();
4397 END_LOOP;
4399 END_LOOP;
4400 VIA_LOOP (PCB->Data);
4402 if (!TEST_FLAGS (flags, via))
4403 continue;
4404 if (via->Clearance < value)
4406 ChangeObjectClearSize (VIA_TYPE, via, 0, 0, value, 1);
4407 RestoreUndoSerialNumber ();
4410 END_LOOP;
4411 ALLLINE_LOOP (PCB->Data);
4413 if (!TEST_FLAGS (flags, line))
4414 continue;
4415 if (line->Clearance < value)
4417 ChangeObjectClearSize (LINE_TYPE, layer, line, 0, value, 1);
4418 RestoreUndoSerialNumber ();
4421 ENDALL_LOOP;
4422 ALLARC_LOOP (PCB->Data);
4424 if (!TEST_FLAGS (flags, arc))
4425 continue;
4426 if (arc->Clearance < value)
4428 ChangeObjectClearSize (ARC_TYPE, layer, arc, 0, value, 1);
4429 RestoreUndoSerialNumber ();
4432 ENDALL_LOOP;
4433 RestoreUndoSerialNumber ();
4434 IncrementUndoSerialNumber ();
4435 return 0;
4438 /* --------------------------------------------------------------------------- */
4440 static const char changepinname_syntax[] =
4441 "ChangePinName(ElementName,PinNumber,PinName)";
4443 static const char changepinname_help[] =
4444 "Sets the name of a specific pin on a specific element.";
4446 /* %start-doc actions ChangePinName
4448 This can be especially useful for annotating pin names from a
4449 schematic to the layout without requiring knowledge of the pcb file
4450 format.
4452 @example
4453 ChangePinName(U3, 7, VCC)
4454 @end example
4456 %end-doc */
4458 static int
4459 ActionChangePinName (int argc, char **argv, Coord x, Coord y)
4461 int changed = 0;
4462 char *refdes, *pinnum, *pinname;
4464 if (argc != 3)
4466 AFAIL (changepinname);
4469 refdes = argv[0];
4470 pinnum = argv[1];
4471 pinname = argv[2];
4473 ELEMENT_LOOP (PCB->Data);
4475 if (NSTRCMP (refdes, NAMEONPCB_NAME (element)) == 0)
4477 PIN_LOOP (element);
4479 if (NSTRCMP (pinnum, pin->Number) == 0)
4481 AddObjectToChangeNameUndoList (PIN_TYPE, NULL, NULL,
4482 pin, pin->Name);
4484 * Note: we can't free() pin->Name first because
4485 * it is used in the undo list
4487 pin->Name = strdup (pinname);
4488 SetChangedFlag (true);
4489 changed = 1;
4492 END_LOOP;
4494 PAD_LOOP (element);
4496 if (NSTRCMP (pinnum, pad->Number) == 0)
4498 AddObjectToChangeNameUndoList (PAD_TYPE, NULL, NULL,
4499 pad, pad->Name);
4501 * Note: we can't free() pad->Name first because
4502 * it is used in the undo list
4504 pad->Name = strdup (pinname);
4505 SetChangedFlag (true);
4506 changed = 1;
4509 END_LOOP;
4512 END_LOOP;
4514 * done with our action so increment the undo # if we actually
4515 * changed anything
4517 if (changed)
4519 if (defer_updates)
4520 defer_needs_update = 1;
4521 else
4523 IncrementUndoSerialNumber ();
4524 gui->invalidate_all ();
4528 return 0;
4531 /* --------------------------------------------------------------------------- */
4533 static const char changename_syntax[] =
4534 "ChangeName(Object)\n"
4535 "ChangeName(Layout|Layer)";
4537 static const char changename_help[] = "Sets the name of objects.";
4539 /* %start-doc actions ChangeName
4541 @table @code
4543 @item Object
4544 Changes the name of the element under the cursor.
4546 @item Layout
4547 Changes the name of the layout. This is printed on the fab drawings.
4549 @item Layer
4550 Changes the name of the currently active layer.
4552 @end table
4554 %end-doc */
4557 ActionChangeName (int argc, char **argv, Coord x, Coord y)
4559 char *function = ARG (0);
4560 char *name;
4562 if (function)
4564 switch (GetFunctionID (function))
4566 /* change the name of an object */
4567 case F_Object:
4569 int type;
4570 void *ptr1, *ptr2, *ptr3;
4572 gui->get_coords (_("Select an Object"), &x, &y);
4573 if ((type =
4574 SearchScreen (x, y, CHANGENAME_TYPES,
4575 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4577 SaveUndoSerialNumber ();
4578 if (QueryInputAndChangeObjectName (type, ptr1, ptr2, ptr3))
4580 SetChangedFlag (true);
4581 if (type == ELEMENT_TYPE)
4583 RubberbandType *ptr;
4584 int i;
4586 RestoreUndoSerialNumber ();
4587 Crosshair.AttachedObject.RubberbandN = 0;
4588 LookupRatLines (type, ptr1, ptr2, ptr3);
4589 ptr = Crosshair.AttachedObject.Rubberband;
4590 for (i = 0; i < Crosshair.AttachedObject.RubberbandN;
4591 i++, ptr++)
4593 if (PCB->RatOn)
4594 EraseRat ((RatType *) ptr->Line);
4595 MoveObjectToRemoveUndoList (RATLINE_TYPE,
4596 ptr->Line, ptr->Line,
4597 ptr->Line);
4599 IncrementUndoSerialNumber ();
4600 Draw ();
4604 break;
4607 /* change the layout's name */
4608 case F_Layout:
4609 name =
4610 gui->prompt_for (_("Enter the layout name:"), EMPTY (PCB->Name));
4611 /* NB: ChangeLayoutName takes ownership of the passed memory */
4612 if (name && ChangeLayoutName (name))
4613 SetChangedFlag (true);
4614 break;
4616 /* change the name of the active layer */
4617 case F_Layer:
4618 name = gui->prompt_for (_("Enter the layer name:"),
4619 EMPTY (CURRENT->Name));
4620 /* NB: ChangeLayerName takes ownership of the passed memory */
4621 if (name && ChangeLayerName (CURRENT, name))
4622 SetChangedFlag (true);
4623 break;
4626 return 0;
4630 /* --------------------------------------------------------------------------- */
4632 static const char morphpolygon_syntax[] = "MorphPolygon(Object|Selected)";
4634 static const char morphpolygon_help[] =
4635 "Converts dead polygon islands into separate polygons.";
4637 /* %start-doc actions MorphPolygon
4639 If a polygon is divided into unconnected "islands", you can use
4640 this command to convert the otherwise disappeared islands into
4641 separate polygons. Be sure the cursor is over a portion of the
4642 polygon that remains visible. Very small islands that may flake
4643 off are automatically deleted.
4645 %end-doc */
4647 static int
4648 ActionMorphPolygon (int argc, char **argv, Coord x, Coord y)
4650 char *function = ARG (0);
4651 if (function)
4653 switch (GetFunctionID (function))
4655 case F_Object:
4657 int type;
4658 void *ptr1, *ptr2, *ptr3;
4660 gui->get_coords (_("Select an Object"), &x, &y);
4661 if ((type = SearchScreen (x, y, POLYGON_TYPE,
4662 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4664 MorphPolygon ((LayerType *) ptr1, (PolygonType *) ptr3);
4665 Draw ();
4666 IncrementUndoSerialNumber ();
4668 break;
4670 case F_Selected:
4671 case F_SelectedObjects:
4672 ALLPOLYGON_LOOP (PCB->Data);
4674 if (TEST_FLAG (SELECTEDFLAG, polygon))
4675 MorphPolygon (layer, polygon);
4677 ENDALL_LOOP;
4678 Draw ();
4679 IncrementUndoSerialNumber ();
4680 break;
4683 return 0;
4686 /* --------------------------------------------------------------------------- */
4688 static const char togglehidename_syntax[] =
4689 "ToggleHideName(Object|SelectedElements)";
4691 static const char togglehidename_help[] =
4692 "Toggles the visibility of element names.";
4694 /* %start-doc actions ToggleHideName
4696 If names are hidden you won't see them on the screen and they will not
4697 appear on the silk layer when you print the layout.
4699 %end-doc */
4701 static int
4702 ActionToggleHideName (int argc, char **argv, Coord x, Coord y)
4704 char *function = ARG (0);
4705 if (function && PCB->ElementOn)
4707 switch (GetFunctionID (function))
4709 case F_Object:
4711 int type;
4712 void *ptr1, *ptr2, *ptr3;
4714 gui->get_coords (_("Select an Object"), &x, &y);
4715 if ((type = SearchScreen (x, y, ELEMENT_TYPE,
4716 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4718 AddObjectToFlagUndoList (type, ptr1, ptr2, ptr3);
4719 EraseElementName ((ElementType *) ptr2);
4720 TOGGLE_FLAG (HIDENAMEFLAG, (ElementType *) ptr2);
4721 DrawElementName ((ElementType *) ptr2);
4722 Draw ();
4723 IncrementUndoSerialNumber ();
4725 break;
4727 case F_SelectedElements:
4728 case F_Selected:
4730 bool changed = false;
4731 ELEMENT_LOOP (PCB->Data);
4733 if ((TEST_FLAG (SELECTEDFLAG, element) ||
4734 TEST_FLAG (SELECTEDFLAG,
4735 &NAMEONPCB_TEXT (element)))
4736 && (FRONT (element) || PCB->InvisibleObjectsOn))
4738 AddObjectToFlagUndoList (ELEMENT_TYPE, element,
4739 element, element);
4740 EraseElementName (element);
4741 TOGGLE_FLAG (HIDENAMEFLAG, element);
4742 DrawElementName (element);
4743 changed = true;
4746 END_LOOP;
4747 if (changed)
4749 Draw ();
4750 IncrementUndoSerialNumber ();
4755 return 0;
4758 /* --------------------------------------------------------------------------- */
4760 static const char changejoin_syntax[] =
4761 "ChangeJoin(ToggleObject|SelectedLines|SelectedArcs|Selected)";
4763 static const char changejoin_help[] =
4764 "Changes the join (clearance through polygons) of objects.";
4766 /* %start-doc actions ChangeJoin
4768 The join flag determines whether a line or arc, drawn to intersect a
4769 polygon, electrically connects to the polygon or not. When joined,
4770 the line/arc is simply drawn over the polygon, making an electrical
4771 connection. When not joined, a gap is drawn between the line and the
4772 polygon, insulating them from each other.
4774 %end-doc */
4776 static int
4777 ActionChangeJoin (int argc, char **argv, Coord x, Coord y)
4779 char *function = ARG (0);
4780 if (function)
4782 switch (GetFunctionID (function))
4784 case F_ToggleObject:
4785 case F_Object:
4787 int type;
4788 void *ptr1, *ptr2, *ptr3;
4790 gui->get_coords (_("Select an Object"), &x, &y);
4791 if ((type =
4792 SearchScreen (x, y, CHANGEJOIN_TYPES,
4793 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4794 if (ChangeObjectJoin (type, ptr1, ptr2, ptr3))
4795 SetChangedFlag (true);
4796 break;
4799 case F_SelectedLines:
4800 if (ChangeSelectedJoin (LINE_TYPE))
4801 SetChangedFlag (true);
4802 break;
4804 case F_SelectedArcs:
4805 if (ChangeSelectedJoin (ARC_TYPE))
4806 SetChangedFlag (true);
4807 break;
4809 case F_Selected:
4810 case F_SelectedObjects:
4811 if (ChangeSelectedJoin (CHANGEJOIN_TYPES))
4812 SetChangedFlag (true);
4813 break;
4816 return 0;
4819 /* --------------------------------------------------------------------------- */
4821 static const char changesquare_syntax[] =
4822 "ChangeSquare(ToggleObject)\n"
4823 "ChangeSquare(SelectedElements|SelectedPins)\n"
4824 "ChangeSquare(Selected|SelectedObjects)";
4826 static const char changesquare_help[] =
4827 "Changes the square flag of pins and pads.";
4829 /* %start-doc actions ChangeSquare
4831 Note that @code{Pins} means both pins and pads.
4833 @pinshapes
4835 %end-doc */
4837 static int
4838 ActionChangeSquare (int argc, char **argv, Coord x, Coord y)
4840 char *function = ARG (0);
4841 if (function)
4843 switch (GetFunctionID (function))
4845 case F_ToggleObject:
4846 case F_Object:
4848 int type;
4849 void *ptr1, *ptr2, *ptr3;
4851 gui->get_coords (_("Select an Object"), &x, &y);
4852 if ((type =
4853 SearchScreen (x, y, CHANGESQUARE_TYPES,
4854 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4855 if (ChangeObjectSquare (type, ptr1, ptr2, ptr3))
4856 SetChangedFlag (true);
4857 break;
4860 case F_SelectedElements:
4861 if (ChangeSelectedSquare (ELEMENT_TYPE))
4862 SetChangedFlag (true);
4863 break;
4865 case F_SelectedPins:
4866 if (ChangeSelectedSquare (PIN_TYPE | PAD_TYPE))
4867 SetChangedFlag (true);
4868 break;
4870 case F_Selected:
4871 case F_SelectedObjects:
4872 if (ChangeSelectedSquare (PIN_TYPE | PAD_TYPE))
4873 SetChangedFlag (true);
4874 break;
4877 return 0;
4880 /* --------------------------------------------------------------------------- */
4882 static const char setsquare_syntax[] =
4883 "SetSquare(ToggleObject|SelectedElements|SelectedPins)";
4885 static const char setsquare_help[] = "sets the square-flag of objects.";
4887 /* %start-doc actions SetSquare
4889 Note that @code{Pins} means pins and pads.
4891 @pinshapes
4893 %end-doc */
4895 static int
4896 ActionSetSquare (int argc, char **argv, Coord x, Coord y)
4898 char *function = ARG (0);
4899 if (function && *function)
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 (SetObjectSquare (type, ptr1, ptr2, ptr3))
4914 SetChangedFlag (true);
4915 break;
4918 case F_SelectedElements:
4919 if (SetSelectedSquare (ELEMENT_TYPE))
4920 SetChangedFlag (true);
4921 break;
4923 case F_SelectedPins:
4924 if (SetSelectedSquare (PIN_TYPE | PAD_TYPE))
4925 SetChangedFlag (true);
4926 break;
4928 case F_Selected:
4929 case F_SelectedObjects:
4930 if (SetSelectedSquare (PIN_TYPE | PAD_TYPE))
4931 SetChangedFlag (true);
4932 break;
4935 return 0;
4938 /* --------------------------------------------------------------------------- */
4940 static const char clearsquare_syntax[] =
4941 "ClearSquare(ToggleObject|SelectedElements|SelectedPins)";
4943 static const char clearsquare_help[] =
4944 "Clears the square-flag of pins and pads.";
4946 /* %start-doc actions ClearSquare
4948 Note that @code{Pins} means pins and pads.
4950 @pinshapes
4952 %end-doc */
4954 static int
4955 ActionClearSquare (int argc, char **argv, Coord x, Coord y)
4957 char *function = ARG (0);
4958 if (function && *function)
4960 switch (GetFunctionID (function))
4962 case F_ToggleObject:
4963 case F_Object:
4965 int type;
4966 void *ptr1, *ptr2, *ptr3;
4968 gui->get_coords (_("Select an Object"), &x, &y);
4969 if ((type =
4970 SearchScreen (x, y, CHANGESQUARE_TYPES,
4971 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4972 if (ClrObjectSquare (type, ptr1, ptr2, ptr3))
4973 SetChangedFlag (true);
4974 break;
4977 case F_SelectedElements:
4978 if (ClrSelectedSquare (ELEMENT_TYPE))
4979 SetChangedFlag (true);
4980 break;
4982 case F_SelectedPins:
4983 if (ClrSelectedSquare (PIN_TYPE | PAD_TYPE))
4984 SetChangedFlag (true);
4985 break;
4987 case F_Selected:
4988 case F_SelectedObjects:
4989 if (ClrSelectedSquare (PIN_TYPE | PAD_TYPE))
4990 SetChangedFlag (true);
4991 break;
4994 return 0;
4997 /* --------------------------------------------------------------------------- */
4999 static const char changeoctagon_syntax[] =
5000 "ChangeOctagon(Object|ToggleObject|SelectedObjects|Selected)\n"
5001 "ChangeOctagon(SelectedElements|SelectedPins|SelectedVias)";
5003 static const char changeoctagon_help[] =
5004 "Changes the octagon-flag of pins and vias.";
5006 /* %start-doc actions ChangeOctagon
5008 @pinshapes
5010 %end-doc */
5012 static int
5013 ActionChangeOctagon (int argc, char **argv, Coord x, Coord y)
5015 char *function = ARG (0);
5016 if (function)
5018 switch (GetFunctionID (function))
5020 case F_ToggleObject:
5021 case F_Object:
5023 int type;
5024 void *ptr1, *ptr2, *ptr3;
5026 gui->get_coords (_("Select an Object"), &x, &y);
5027 if ((type =
5028 SearchScreen (x, y, CHANGEOCTAGON_TYPES,
5029 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5030 if (ChangeObjectOctagon (type, ptr1, ptr2, ptr3))
5031 SetChangedFlag (true);
5032 break;
5035 case F_SelectedElements:
5036 if (ChangeSelectedOctagon (ELEMENT_TYPE))
5037 SetChangedFlag (true);
5038 break;
5040 case F_SelectedPins:
5041 if (ChangeSelectedOctagon (PIN_TYPE))
5042 SetChangedFlag (true);
5043 break;
5045 case F_SelectedVias:
5046 if (ChangeSelectedOctagon (VIA_TYPE))
5047 SetChangedFlag (true);
5048 break;
5050 case F_Selected:
5051 case F_SelectedObjects:
5052 if (ChangeSelectedOctagon (PIN_TYPES))
5053 SetChangedFlag (true);
5054 break;
5057 return 0;
5060 /* --------------------------------------------------------------------------- */
5062 static const char setoctagon_syntax[] =
5063 "SetOctagon(Object|ToggleObject|SelectedElements|Selected)";
5065 static const char setoctagon_help[] = "Sets the octagon-flag of objects.";
5067 /* %start-doc actions SetOctagon
5069 @pinshapes
5071 %end-doc */
5073 static int
5074 ActionSetOctagon (int argc, char **argv, Coord x, Coord y)
5076 char *function = ARG (0);
5077 if (function)
5079 switch (GetFunctionID (function))
5081 case F_ToggleObject:
5082 case F_Object:
5084 int type;
5085 void *ptr1, *ptr2, *ptr3;
5087 gui->get_coords (_("Select an Object"), &x, &y);
5088 if ((type =
5089 SearchScreen (x, y, CHANGEOCTAGON_TYPES,
5090 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5091 if (SetObjectOctagon (type, ptr1, ptr2, ptr3))
5092 SetChangedFlag (true);
5093 break;
5096 case F_SelectedElements:
5097 if (SetSelectedOctagon (ELEMENT_TYPE))
5098 SetChangedFlag (true);
5099 break;
5101 case F_SelectedPins:
5102 if (SetSelectedOctagon (PIN_TYPE))
5103 SetChangedFlag (true);
5104 break;
5106 case F_SelectedVias:
5107 if (SetSelectedOctagon (VIA_TYPE))
5108 SetChangedFlag (true);
5109 break;
5111 case F_Selected:
5112 case F_SelectedObjects:
5113 if (SetSelectedOctagon (PIN_TYPES))
5114 SetChangedFlag (true);
5115 break;
5118 return 0;
5121 /* --------------------------------------------------------------------------- */
5123 static const char clearoctagon_syntax[] =
5124 "ClearOctagon(ToggleObject|Object|SelectedObjects|Selected)\n"
5125 "ClearOctagon(SelectedElements|SelectedPins|SelectedVias)";
5127 static const char clearoctagon_help[] =
5128 "Clears the octagon-flag of pins and vias.";
5130 /* %start-doc actions ClearOctagon
5132 @pinshapes
5134 %end-doc */
5136 static int
5137 ActionClearOctagon (int argc, char **argv, Coord x, Coord y)
5139 char *function = ARG (0);
5140 if (function)
5142 switch (GetFunctionID (function))
5144 case F_ToggleObject:
5145 case F_Object:
5147 int type;
5148 void *ptr1, *ptr2, *ptr3;
5150 gui->get_coords (_("Select an Object"), &x, &y);
5151 if ((type =
5152 SearchScreen (Crosshair.X, Crosshair.Y, CHANGEOCTAGON_TYPES,
5153 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5154 if (ClrObjectOctagon (type, ptr1, ptr2, ptr3))
5155 SetChangedFlag (true);
5156 break;
5159 case F_SelectedElements:
5160 if (ClrSelectedOctagon (ELEMENT_TYPE))
5161 SetChangedFlag (true);
5162 break;
5164 case F_SelectedPins:
5165 if (ClrSelectedOctagon (PIN_TYPE))
5166 SetChangedFlag (true);
5167 break;
5169 case F_SelectedVias:
5170 if (ClrSelectedOctagon (VIA_TYPE))
5171 SetChangedFlag (true);
5172 break;
5174 case F_Selected:
5175 case F_SelectedObjects:
5176 if (ClrSelectedOctagon (PIN_TYPES))
5177 SetChangedFlag (true);
5178 break;
5181 return 0;
5184 /* --------------------------------------------------------------------------- */
5186 static const char changehold_syntax[] =
5187 "ChangeHole(ToggleObject|Object|SelectedVias|Selected)";
5189 static const char changehold_help[] = "Changes the hole flag of objects.";
5191 /* %start-doc actions ChangeHole
5193 The "hole flag" of a via determines whether the via is a
5194 plated-through hole (not set), or an unplated hole (set).
5196 %end-doc */
5198 static int
5199 ActionChangeHole (int argc, char **argv, Coord x, Coord y)
5201 char *function = ARG (0);
5202 if (function)
5204 switch (GetFunctionID (function))
5206 case F_ToggleObject:
5207 case F_Object:
5209 int type;
5210 void *ptr1, *ptr2, *ptr3;
5212 gui->get_coords (_("Select an Object"), &x, &y);
5213 if ((type = SearchScreen (x, y, VIA_TYPE,
5214 &ptr1, &ptr2, &ptr3)) != NO_TYPE
5215 && ChangeHole ((PinType *) ptr3))
5216 IncrementUndoSerialNumber ();
5217 break;
5220 case F_SelectedVias:
5221 case F_Selected:
5222 if (ChangeSelectedHole ())
5223 SetChangedFlag (true);
5224 break;
5227 return 0;
5230 /* --------------------------------------------------------------------------- */
5232 static const char changepaste_syntax[] =
5233 "ChangePaste(ToggleObject|Object|SelectedPads|Selected)";
5235 static const char changepaste_help[] = "Changes the no paste flag of objects.";
5237 /* %start-doc actions ChangePaste
5239 The "no paste flag" of a pad determines whether the solderpaste
5240 stencil will have an opening for the pad (no set) or if there wil be
5241 no solderpaste on the pad (set). This is used for things such as
5242 fiducial pads.
5244 %end-doc */
5246 static int
5247 ActionChangePaste (int argc, char **argv, Coord x, Coord y)
5249 char *function = ARG (0);
5250 if (function)
5252 switch (GetFunctionID (function))
5254 case F_ToggleObject:
5255 case F_Object:
5257 int type;
5258 void *ptr1, *ptr2, *ptr3;
5260 gui->get_coords (_("Select an Object"), &x, &y);
5261 if ((type = SearchScreen (x, y, PAD_TYPE,
5262 &ptr1, &ptr2, &ptr3)) != NO_TYPE
5263 && ChangePaste ((PadType *) ptr3))
5264 IncrementUndoSerialNumber ();
5265 break;
5268 case F_SelectedPads:
5269 case F_Selected:
5270 if (ChangeSelectedPaste ())
5271 SetChangedFlag (true);
5272 break;
5275 return 0;
5278 /* --------------------------------------------------------------------------- */
5280 static const char select_syntax[] =
5281 "Select(Object|ToggleObject)\n"
5282 "Select(All|Block|Connection)\n"
5283 "Select(ElementByName|ObjectByName|PadByName|PinByName)\n"
5284 "Select(ElementByName|ObjectByName|PadByName|PinByName, Name)\n"
5285 "Select(TextByName|ViaByName|NetByName)\n"
5286 "Select(TextByName|ViaByName|NetByName, Name)\n"
5287 "Select(Convert)";
5289 static const char select_help[] = "Toggles or sets the selection.";
5291 /* %start-doc actions Select
5293 @table @code
5295 @item ElementByName
5296 @item ObjectByName
5297 @item PadByName
5298 @item PinByName
5299 @item TextByName
5300 @item ViaByName
5301 @item NetByName
5303 These all rely on having a regular expression parser built into
5304 @code{pcb}. If the name is not specified then the user is prompted
5305 for a pattern, and all objects that match the pattern and are of the
5306 type specified are selected.
5308 @item Object
5309 @item ToggleObject
5310 Selects the object under the cursor.
5312 @item Block
5313 Selects all objects in a rectangle indicated by the cursor.
5315 @item All
5316 Selects all objects on the board.
5318 @item Connection
5319 Selects all connections with the ``found'' flag set.
5321 @item Convert
5322 Converts the selected objects to an element. This uses the highest
5323 numbered paste buffer.
5325 @end table
5327 %end-doc */
5329 static int
5330 ActionSelect (int argc, char **argv, Coord x, Coord y)
5332 char *function = ARG (0);
5333 if (function)
5335 switch (GetFunctionID (function))
5337 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5338 int type;
5339 /* select objects by their names */
5340 case F_ElementByName:
5341 type = ELEMENT_TYPE;
5342 goto commonByName;
5343 case F_ObjectByName:
5344 type = ALL_TYPES;
5345 goto commonByName;
5346 case F_PadByName:
5347 type = PAD_TYPE;
5348 goto commonByName;
5349 case F_PinByName:
5350 type = PIN_TYPE;
5351 goto commonByName;
5352 case F_TextByName:
5353 type = TEXT_TYPE;
5354 goto commonByName;
5355 case F_ViaByName:
5356 type = VIA_TYPE;
5357 goto commonByName;
5358 case F_NetByName:
5359 type = NET_TYPE;
5360 goto commonByName;
5362 commonByName:
5364 char *pattern = ARG (1);
5366 if (pattern
5367 || (pattern =
5368 gui->prompt_for (_("Enter pattern:"), "")) != NULL)
5370 if (SelectObjectByName (type, pattern, true))
5371 SetChangedFlag (true);
5372 if (ARG (1) == NULL)
5373 free (pattern);
5375 break;
5377 #endif /* defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP) */
5379 /* select a single object */
5380 case F_ToggleObject:
5381 case F_Object:
5382 if (SelectObject ())
5383 SetChangedFlag (true);
5384 break;
5386 /* all objects in block */
5387 case F_Block:
5389 BoxType box;
5391 box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
5392 Crosshair.AttachedBox.Point2.X);
5393 box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
5394 Crosshair.AttachedBox.Point2.Y);
5395 box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
5396 Crosshair.AttachedBox.Point2.X);
5397 box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
5398 Crosshair.AttachedBox.Point2.Y);
5399 notify_crosshair_change (false);
5400 NotifyBlock ();
5401 if (Crosshair.AttachedBox.State == STATE_THIRD &&
5402 SelectBlock (&box, true))
5404 SetChangedFlag (true);
5405 Crosshair.AttachedBox.State = STATE_FIRST;
5407 notify_crosshair_change (true);
5408 break;
5411 /* select all visible objects */
5412 case F_All:
5414 BoxType box;
5416 box.X1 = -MAX_COORD;
5417 box.Y1 = -MAX_COORD;
5418 box.X2 = MAX_COORD;
5419 box.Y2 = MAX_COORD;
5420 if (SelectBlock (&box, true))
5421 SetChangedFlag (true);
5422 break;
5425 /* all found connections */
5426 case F_Connection:
5427 if (SelectByFlag (FOUNDFLAG, true))
5429 Draw ();
5430 IncrementUndoSerialNumber ();
5431 SetChangedFlag (true);
5433 break;
5435 case F_Convert:
5437 Coord x, y;
5438 Note.Buffer = Settings.BufferNumber;
5439 SetBufferNumber (MAX_BUFFER - 1);
5440 ClearBuffer (PASTEBUFFER);
5441 gui->get_coords (_("Select the Element's Mark Location"), &x, &y);
5442 x = GridFit (x, PCB->Grid, PCB->GridOffsetX);
5443 y = GridFit (y, PCB->Grid, PCB->GridOffsetY);
5444 AddSelectedToBuffer (PASTEBUFFER, x, y, true);
5445 SaveUndoSerialNumber ();
5446 RemoveSelected ();
5447 ConvertBufferToElement (PASTEBUFFER);
5448 RestoreUndoSerialNumber ();
5449 CopyPastebufferToLayout (x, y);
5450 SetBufferNumber (Note.Buffer);
5452 break;
5454 default:
5455 AFAIL (select);
5456 break;
5459 return 0;
5462 /* FLAG(have_regex,FlagHaveRegex,0) */
5464 FlagHaveRegex (int parm)
5466 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5467 return 1;
5468 #else
5469 return 0;
5470 #endif
5473 /* --------------------------------------------------------------------------- */
5475 static const char unselect_syntax[] =
5476 "Unselect(All|Block|Connection)\n"
5477 "Unselect(ElementByName|ObjectByName|PadByName|PinByName)\n"
5478 "Unselect(ElementByName|ObjectByName|PadByName|PinByName, Name)\n"
5479 "Unselect(TextByName|ViaByName)\n"
5480 "Unselect(TextByName|ViaByName, Name)\n";
5482 static const char unselect_help[] =
5483 "Unselects the object at the pointer location or the specified objects.";
5485 /* %start-doc actions Unselect
5487 @table @code
5489 @item All
5490 Unselect all objects.
5492 @item Block
5493 Unselect all objects in a rectangle given by the cursor.
5495 @item Connection
5496 Unselect all connections with the ``found'' flag set.
5498 @item ElementByName
5499 @item ObjectByName
5500 @item PadByName
5501 @item PinByName
5502 @item TextByName
5503 @item ViaByName
5505 These all rely on having a regular expression parser built into
5506 @code{pcb}. If the name is not specified then the user is prompted
5507 for a pattern, and all objects that match the pattern and are of the
5508 type specified are unselected.
5511 @end table
5513 %end-doc */
5515 static int
5516 ActionUnselect (int argc, char **argv, Coord x, Coord y)
5518 char *function = ARG (0);
5519 if (function)
5521 switch (GetFunctionID (function))
5523 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5524 int type;
5525 /* select objects by their names */
5526 case F_ElementByName:
5527 type = ELEMENT_TYPE;
5528 goto commonByName;
5529 case F_ObjectByName:
5530 type = ALL_TYPES;
5531 goto commonByName;
5532 case F_PadByName:
5533 type = PAD_TYPE;
5534 goto commonByName;
5535 case F_PinByName:
5536 type = PIN_TYPE;
5537 goto commonByName;
5538 case F_TextByName:
5539 type = TEXT_TYPE;
5540 goto commonByName;
5541 case F_ViaByName:
5542 type = VIA_TYPE;
5543 goto commonByName;
5544 case F_NetByName:
5545 type = NET_TYPE;
5546 goto commonByName;
5548 commonByName:
5550 char *pattern = ARG (1);
5552 if (pattern
5553 || (pattern =
5554 gui->prompt_for (_("Enter pattern:"), "")) != NULL)
5556 if (SelectObjectByName (type, pattern, false))
5557 SetChangedFlag (true);
5558 if (ARG (1) == NULL)
5559 free (pattern);
5561 break;
5563 #endif /* defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP) */
5565 /* all objects in block */
5566 case F_Block:
5568 BoxType box;
5570 box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
5571 Crosshair.AttachedBox.Point2.X);
5572 box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
5573 Crosshair.AttachedBox.Point2.Y);
5574 box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
5575 Crosshair.AttachedBox.Point2.X);
5576 box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
5577 Crosshair.AttachedBox.Point2.Y);
5578 notify_crosshair_change (false);
5579 NotifyBlock ();
5580 if (Crosshair.AttachedBox.State == STATE_THIRD &&
5581 SelectBlock (&box, false))
5583 SetChangedFlag (true);
5584 Crosshair.AttachedBox.State = STATE_FIRST;
5586 notify_crosshair_change (true);
5587 break;
5590 /* unselect all visible objects */
5591 case F_All:
5593 BoxType box;
5595 box.X1 = -MAX_COORD;
5596 box.Y1 = -MAX_COORD;
5597 box.X2 = MAX_COORD;
5598 box.Y2 = MAX_COORD;
5599 if (SelectBlock (&box, false))
5600 SetChangedFlag (true);
5601 break;
5604 /* all found connections */
5605 case F_Connection:
5606 if (SelectByFlag (FOUNDFLAG, false))
5608 Draw ();
5609 IncrementUndoSerialNumber ();
5610 SetChangedFlag (true);
5612 break;
5614 default:
5615 AFAIL (unselect);
5616 break;
5620 return 0;
5623 /* --------------------------------------------------------------------------- */
5625 static const char saveto_syntax[] =
5626 "SaveTo(Layout|LayoutAs,filename)\n"
5627 "SaveTo(AllConnections|AllUnusedPins|ElementConnections,filename)\n"
5628 "SaveTo(PasteBuffer,filename)";
5630 static const char saveto_help[] = "Saves data to a file.";
5632 /* %start-doc actions SaveTo
5634 @table @code
5636 @item Layout
5637 Saves the current layout.
5639 @item LayoutAs
5640 Saves the current layout, and remembers the filename used.
5642 @item AllConnections
5643 Save all connections to a file.
5645 @item AllUnusedPins
5646 List all unused pins to a file.
5648 @item ElementConnections
5649 Save connections to the element at the cursor to a file.
5651 @item PasteBuffer
5652 Save the content of the active Buffer to a file. This is the graphical way to create a footprint.
5654 @end table
5656 %end-doc */
5658 static int
5659 ActionSaveTo (int argc, char **argv, Coord x, Coord y)
5661 char *function;
5662 char *name;
5664 function = argv[0];
5665 name = argv[1];
5667 if (strcasecmp (function, "Layout") == 0)
5669 if (SavePCB (PCB->Filename) == 0)
5670 SetChangedFlag (false);
5671 return 0;
5674 if (argc != 2)
5675 AFAIL (saveto);
5677 if (strcasecmp (function, "LayoutAs") == 0)
5679 if (SavePCB (name) == 0)
5681 SetChangedFlag (false);
5682 free (PCB->Filename);
5683 PCB->Filename = strdup (name);
5684 if (gui->notify_filename_changed != NULL)
5685 gui->notify_filename_changed ();
5687 return 0;
5690 if (strcasecmp (function, "AllConnections") == 0)
5692 FILE *fp;
5693 bool result;
5694 if ((fp = CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
5696 LookupConnectionsToAllElements (fp);
5697 fclose (fp);
5698 SetChangedFlag (true);
5700 return 0;
5703 if (strcasecmp (function, "AllUnusedPins") == 0)
5705 FILE *fp;
5706 bool result;
5707 if ((fp = CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
5709 LookupUnusedPins (fp);
5710 fclose (fp);
5711 SetChangedFlag (true);
5713 return 0;
5716 if (strcasecmp (function, "ElementConnections") == 0)
5718 ElementType *element;
5719 void *ptrtmp;
5720 FILE *fp;
5721 bool result;
5723 if ((SearchScreen (Crosshair.X, Crosshair.Y, ELEMENT_TYPE,
5724 &ptrtmp, &ptrtmp, &ptrtmp)) != NO_TYPE)
5726 element = (ElementType *) ptrtmp;
5727 if ((fp =
5728 CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
5730 LookupElementConnections (element, fp);
5731 fclose (fp);
5732 SetChangedFlag (true);
5735 return 0;
5738 if (strcasecmp (function, "PasteBuffer") == 0)
5740 return SaveBufferElements (name);
5743 AFAIL (saveto);
5746 /* --------------------------------------------------------------------------- */
5748 static const char savesettings_syntax[] =
5749 "SaveSettings()\n"
5750 "SaveSettings(local)";
5752 static const char savesettings_help[] = "Saves settings.";
5754 /* %start-doc actions SaveSettings
5756 If you pass no arguments, the settings are stored in
5757 @code{$HOME/.pcb/settings}. If you pass the word @code{local} they're
5758 saved in @code{./pcb.settings}.
5760 %end-doc */
5762 static int
5763 ActionSaveSettings (int argc, char **argv, Coord x, Coord y)
5765 int locally = argc > 0 ? (strncasecmp (argv[0], "local", 5) == 0) : 0;
5766 hid_save_settings (locally);
5767 return 0;
5770 /* --------------------------------------------------------------------------- */
5772 static const char loadfrom_syntax[] =
5773 "LoadFrom(Layout|LayoutToBuffer|ElementToBuffer|Netlist|Revert,filename)";
5775 static const char loadfrom_help[] = "Load layout data from a file.";
5777 /* %start-doc actions LoadFrom
5779 This action assumes you know what the filename is. The various GUIs
5780 should have a similar @code{Load} action where the filename is
5781 optional, and will provide their own file selection mechanism to let
5782 you choose the file name.
5784 @table @code
5786 @item Layout
5787 Loads an entire PCB layout, replacing the current one.
5789 @item LayoutToBuffer
5790 Loads an entire PCB layout to the paste buffer.
5792 @item ElementToBuffer
5793 Loads the given element file into the paste buffer. Element files
5794 contain only a single @code{Element} definition, such as the
5795 ``newlib'' library uses.
5797 @item Netlist
5798 Loads a new netlist, replacing any current netlist.
5800 @item Revert
5801 Re-loads the current layout from its disk file, reverting any changes
5802 you may have made.
5804 @end table
5806 %end-doc */
5808 static int
5809 ActionLoadFrom (int argc, char **argv, Coord x, Coord y)
5811 char *function;
5812 char *name;
5814 if (argc < 2)
5815 AFAIL (loadfrom);
5817 function = argv[0];
5818 name = argv[1];
5820 if (strcasecmp (function, "ElementToBuffer") == 0)
5822 notify_crosshair_change (false);
5823 if (LoadElementToBuffer (PASTEBUFFER, name, true))
5824 SetMode (PASTEBUFFER_MODE);
5825 notify_crosshair_change (true);
5828 else if (strcasecmp (function, "LayoutToBuffer") == 0)
5830 notify_crosshair_change (false);
5831 if (LoadLayoutToBuffer (PASTEBUFFER, name))
5832 SetMode (PASTEBUFFER_MODE);
5833 notify_crosshair_change (true);
5836 else if (strcasecmp (function, "Layout") == 0)
5838 if (!PCB->Changed ||
5839 gui->confirm_dialog (_("OK to override layout data?"), 0))
5840 LoadPCB (name);
5843 else if (strcasecmp (function, "Netlist") == 0)
5845 if (PCB->Netlistname)
5846 free (PCB->Netlistname);
5847 PCB->Netlistname = StripWhiteSpaceAndDup (name);
5848 FreeLibraryMemory (&PCB->NetlistLib);
5849 ImportNetlist (PCB->Netlistname);
5850 NetlistChanged (1);
5852 else if (strcasecmp (function, "Revert") == 0 && PCB->Filename
5853 && (!PCB->Changed
5854 || gui->confirm_dialog (_("OK to override changes?"), 0)))
5856 RevertPCB ();
5859 return 0;
5862 /* --------------------------------------------------------------------------- */
5864 static const char new_syntax[] = "New([name])";
5866 static const char new_help[] = "Starts a new layout.";
5868 /* %start-doc actions New
5870 If a name is not given, one is prompted for.
5872 %end-doc */
5874 static int
5875 ActionNew (int argc, char **argv, Coord x, Coord y)
5877 char *name = ARG (0);
5879 if (!PCB->Changed || gui->confirm_dialog (_("OK to clear layout data?"), 0))
5881 if (name)
5882 name = strdup (name);
5883 else
5884 name = gui->prompt_for (_("Enter the layout name:"), "");
5886 if (!name)
5887 return 1;
5889 notify_crosshair_change (false);
5890 /* do emergency saving
5891 * clear the old struct and allocate memory for the new one
5893 if (PCB->Changed && Settings.SaveInTMP)
5894 SaveInTMP ();
5895 RemovePCB (PCB);
5896 PCB = NULL;
5897 PCB = CreateNewPCB (true);
5898 PCB->Data->LayerN = DEF_LAYER;
5899 CreateNewPCBPost (PCB, 1);
5901 /* setup the new name and reset some values to default */
5902 free (PCB->Name);
5903 PCB->Name = name;
5905 ResetStackAndVisibility ();
5906 SetCrosshairRange (0, 0, PCB->MaxWidth, PCB->MaxHeight);
5907 CenterDisplay (PCB->MaxWidth / 2, PCB->MaxHeight / 2);
5908 Redraw ();
5910 hid_action ("PCBChanged");
5911 notify_crosshair_change (true);
5912 return 0;
5914 return 1;
5917 /* ---------------------------------------------------------------------------
5918 * no operation, just for testing purposes
5919 * syntax: Bell(volume)
5921 void
5922 ActionBell (char *volume)
5924 gui->beep ();
5927 /* --------------------------------------------------------------------------- */
5929 static const char pastebuffer_syntax[] =
5930 "PasteBuffer(AddSelected|Clear|1..MAX_BUFFER)\n"
5931 "PasteBuffer(Rotate, 1..3)\n"
5932 "PasteBuffer(Convert|Save|Restore|Mirror)\n"
5933 "PasteBuffer(ToLayout, X, Y, units)";
5935 static const char pastebuffer_help[] =
5936 "Various operations on the paste buffer.";
5938 /* %start-doc actions PasteBuffer
5940 There are a number of paste buffers; the actual limit is a
5941 compile-time constant @code{MAX_BUFFER} in @file{globalconst.h}. It
5942 is currently @code{5}. One of these is the ``current'' paste buffer,
5943 often referred to as ``the'' paste buffer.
5945 @table @code
5947 @item AddSelected
5948 Copies the selected objects to the current paste buffer.
5950 @item Clear
5951 Remove all objects from the current paste buffer.
5953 @item Convert
5954 Convert the current paste buffer to an element. Vias are converted to
5955 pins, lines are converted to pads.
5957 @item Restore
5958 Convert any elements in the paste buffer back to vias and lines.
5960 @item Mirror
5961 Flip all objects in the paste buffer vertically (up/down flip). To mirror
5962 horizontally, combine this with rotations.
5964 @item Rotate
5965 Rotates the current buffer. The number to pass is 1..3, where 1 means
5966 90 degrees counter clockwise, 2 means 180 degrees, and 3 means 90
5967 degrees clockwise (270 CCW).
5969 @item Save
5970 Saves any elements in the current buffer to the indicated file.
5972 @item ToLayout
5973 Pastes any elements in the current buffer to the indicated X, Y
5974 coordinates in the layout. The @code{X} and @code{Y} are treated like
5975 @code{delta} is for many other objects. For each, if it's prefixed by
5976 @code{+} or @code{-}, then that amount is relative to the last
5977 location. Otherwise, it's absolute. Units can be
5978 @code{mil} or @code{mm}; if unspecified, units are PCB's internal
5979 units, currently 1/100 mil.
5982 @item 1..MAX_BUFFER
5983 Selects the given buffer to be the current paste buffer.
5985 @end table
5987 %end-doc */
5989 static int
5990 ActionPasteBuffer (int argc, char **argv, Coord x, Coord y)
5992 char *function = argc ? argv[0] : (char *)"";
5993 char *sbufnum = argc > 1 ? argv[1] : (char *)"";
5994 char *name;
5995 static char *default_file = NULL;
5996 int free_name = 0;
5998 notify_crosshair_change (false);
5999 if (function)
6001 switch (GetFunctionID (function))
6003 /* clear contents of paste buffer */
6004 case F_Clear:
6005 ClearBuffer (PASTEBUFFER);
6006 break;
6008 /* copies objects to paste buffer */
6009 case F_AddSelected:
6010 AddSelectedToBuffer (PASTEBUFFER, 0, 0, false);
6011 break;
6013 /* converts buffer contents into an element */
6014 case F_Convert:
6015 ConvertBufferToElement (PASTEBUFFER);
6016 break;
6018 /* break up element for editing */
6019 case F_Restore:
6020 SmashBufferElement (PASTEBUFFER);
6021 break;
6023 /* Mirror buffer */
6024 case F_Mirror:
6025 MirrorBuffer (PASTEBUFFER);
6026 break;
6028 case F_Rotate:
6029 if (sbufnum)
6031 RotateBuffer (PASTEBUFFER, (BYTE) atoi (sbufnum));
6032 SetCrosshairRangeToBuffer ();
6034 break;
6036 case F_Save:
6037 if (PASTEBUFFER->Data->ElementN == 0)
6039 Message (_("Buffer has no elements!\n"));
6040 break;
6042 free_name = 0;
6043 if (argc <= 1)
6045 name = gui->fileselect (_("Save Paste Buffer As ..."),
6046 _("Choose a file to save the contents of the\n"
6047 "paste buffer to.\n"),
6048 default_file, ".fp", "footprint",
6051 if (default_file)
6053 free (default_file);
6054 default_file = NULL;
6056 if ( name && *name)
6058 default_file = strdup (name);
6060 free_name = 1;
6063 else
6064 name = argv[1];
6067 FILE *exist;
6069 if ((exist = fopen (name, "r")))
6071 fclose (exist);
6072 if (gui->
6073 confirm_dialog (_("File exists! Ok to overwrite?"), 0))
6074 SaveBufferElements (name);
6076 else
6077 SaveBufferElements (name);
6079 if (free_name && name)
6080 free (name);
6082 break;
6084 case F_ToLayout:
6086 static Coord oldx = 0, oldy = 0;
6087 Coord x, y;
6088 bool absolute;
6090 if (argc == 1)
6092 x = y = 0;
6094 else if (argc == 3 || argc == 4)
6096 x = GetValue (ARG (1), ARG (3), &absolute);
6097 if (!absolute)
6098 x += oldx;
6099 y = GetValue (ARG (2), ARG (3), &absolute);
6100 if (!absolute)
6101 y += oldy;
6103 else
6105 notify_crosshair_change (true);
6106 AFAIL (pastebuffer);
6109 oldx = x;
6110 oldy = y;
6111 if (CopyPastebufferToLayout (x, y))
6112 SetChangedFlag (true);
6114 break;
6116 /* set number */
6117 default:
6119 int number = atoi (function);
6121 /* correct number */
6122 if (number)
6123 SetBufferNumber (number - 1);
6128 notify_crosshair_change (true);
6129 return 0;
6132 /* --------------------------------------------------------------------------- */
6134 static const char undo_syntax[] = "Undo()\n"
6135 "Undo(ClearList)";
6137 static const char undo_help[] = "Undo recent changes.";
6139 /* %start-doc actions Undo
6141 The unlimited undo feature of @code{Pcb} allows you to recover from
6142 most operations that materially affect you work. Calling
6143 @code{Undo()} without any parameter recovers from the last (non-undo)
6144 operation. @code{ClearList} is used to release the allocated
6145 memory. @code{ClearList} is called whenever a new layout is started or
6146 loaded. See also @code{Redo} and @code{Atomic}.
6148 Note that undo groups operations by serial number; changes with the
6149 same serial number will be undone (or redone) as a group. See
6150 @code{Atomic}.
6152 %end-doc */
6154 static int
6155 ActionUndo (int argc, char **argv, Coord x, Coord y)
6157 char *function = ARG (0);
6158 if (!function || !*function)
6160 /* don't allow undo in the middle of an operation */
6161 if (Settings.Mode != POLYGONHOLE_MODE &&
6162 Crosshair.AttachedObject.State != STATE_FIRST)
6163 return 1;
6164 if (Crosshair.AttachedBox.State != STATE_FIRST
6165 && Settings.Mode != ARC_MODE)
6166 return 1;
6167 /* undo the last operation */
6169 notify_crosshair_change (false);
6170 if ((Settings.Mode == POLYGON_MODE ||
6171 Settings.Mode == POLYGONHOLE_MODE) &&
6172 Crosshair.AttachedPolygon.PointN)
6174 GoToPreviousPoint ();
6175 notify_crosshair_change (true);
6176 return 0;
6178 /* move anchor point if undoing during line creation */
6179 if (Settings.Mode == LINE_MODE)
6181 if (Crosshair.AttachedLine.State == STATE_SECOND)
6183 if (TEST_FLAG (AUTODRCFLAG, PCB))
6184 Undo (true); /* undo the connection find */
6185 Crosshair.AttachedLine.State = STATE_FIRST;
6186 SetLocalRef (0, 0, false);
6187 notify_crosshair_change (true);
6188 return 0;
6190 if (Crosshair.AttachedLine.State == STATE_THIRD)
6192 int type;
6193 void *ptr1, *ptr3, *ptrtmp;
6194 LineType *ptr2;
6195 /* this search is guaranteed to succeed */
6196 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6197 &ptrtmp, &ptr3,
6198 Crosshair.AttachedLine.Point1.X,
6199 Crosshair.AttachedLine.Point1.Y, 0);
6200 ptr2 = (LineType *) ptrtmp;
6202 /* save both ends of line */
6203 Crosshair.AttachedLine.Point2.X = ptr2->Point1.X;
6204 Crosshair.AttachedLine.Point2.Y = ptr2->Point1.Y;
6205 if ((type = Undo (true)))
6206 SetChangedFlag (true);
6207 /* check that the undo was of the right type */
6208 if ((type & UNDO_CREATE) == 0)
6210 /* wrong undo type, restore anchor points */
6211 Crosshair.AttachedLine.Point2.X =
6212 Crosshair.AttachedLine.Point1.X;
6213 Crosshair.AttachedLine.Point2.Y =
6214 Crosshair.AttachedLine.Point1.Y;
6215 notify_crosshair_change (true);
6216 return 0;
6218 /* move to new anchor */
6219 Crosshair.AttachedLine.Point1.X =
6220 Crosshair.AttachedLine.Point2.X;
6221 Crosshair.AttachedLine.Point1.Y =
6222 Crosshair.AttachedLine.Point2.Y;
6223 /* check if an intermediate point was removed */
6224 if (type & UNDO_REMOVE)
6226 /* this search should find the restored line */
6227 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6228 &ptrtmp,
6229 &ptr3,
6230 Crosshair.AttachedLine.Point2.X,
6231 Crosshair.AttachedLine.Point2.Y, 0);
6232 ptr2 = (LineType *) ptrtmp;
6233 if (TEST_FLAG (AUTODRCFLAG, PCB))
6235 /* undo loses FOUNDFLAG */
6236 SET_FLAG(FOUNDFLAG, ptr2);
6237 DrawLine (CURRENT, ptr2);
6239 Crosshair.AttachedLine.Point1.X =
6240 Crosshair.AttachedLine.Point2.X = ptr2->Point2.X;
6241 Crosshair.AttachedLine.Point1.Y =
6242 Crosshair.AttachedLine.Point2.Y = ptr2->Point2.Y;
6244 FitCrosshairIntoGrid (Crosshair.X, Crosshair.Y);
6245 AdjustAttachedObjects ();
6246 if (--addedLines == 0)
6248 Crosshair.AttachedLine.State = STATE_SECOND;
6249 lastLayer = CURRENT;
6251 else
6253 /* this search is guaranteed to succeed too */
6254 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6255 &ptrtmp,
6256 &ptr3,
6257 Crosshair.AttachedLine.Point1.X,
6258 Crosshair.AttachedLine.Point1.Y, 0);
6259 ptr2 = (LineType *) ptrtmp;
6260 lastLayer = (LayerType *) ptr1;
6262 notify_crosshair_change (true);
6263 return 0;
6266 if (Settings.Mode == ARC_MODE)
6268 if (Crosshair.AttachedBox.State == STATE_SECOND)
6270 Crosshair.AttachedBox.State = STATE_FIRST;
6271 notify_crosshair_change (true);
6272 return 0;
6274 if (Crosshair.AttachedBox.State == STATE_THIRD)
6276 void *ptr1, *ptr2, *ptr3;
6277 BoxType *bx;
6278 /* guaranteed to succeed */
6279 SearchObjectByLocation (ARC_TYPE, &ptr1, &ptr2, &ptr3,
6280 Crosshair.AttachedBox.Point1.X,
6281 Crosshair.AttachedBox.Point1.Y, 0);
6282 bx = GetArcEnds ((ArcType *) ptr2);
6283 Crosshair.AttachedBox.Point1.X =
6284 Crosshair.AttachedBox.Point2.X = bx->X1;
6285 Crosshair.AttachedBox.Point1.Y =
6286 Crosshair.AttachedBox.Point2.Y = bx->Y1;
6287 AdjustAttachedObjects ();
6288 if (--addedLines == 0)
6289 Crosshair.AttachedBox.State = STATE_SECOND;
6292 /* undo the last destructive operation */
6293 if (Undo (true))
6294 SetChangedFlag (true);
6296 else if (function)
6298 switch (GetFunctionID (function))
6300 /* clear 'undo objects' list */
6301 case F_ClearList:
6302 ClearUndoList (false);
6303 break;
6306 notify_crosshair_change (true);
6307 return 0;
6310 /* --------------------------------------------------------------------------- */
6312 static const char redo_syntax[] = "Redo()";
6314 static const char redo_help[] = "Redo recent \"undo\" operations.";
6316 /* %start-doc actions Redo
6318 This routine allows you to recover from the last undo command. You
6319 might want to do this if you thought that undo was going to revert
6320 something other than what it actually did (in case you are confused
6321 about which operations are un-doable), or if you have been backing up
6322 through a long undo list and over-shoot your stopping point. Any
6323 change that is made since the undo in question will trim the redo
6324 list. For example if you add ten lines, then undo three of them you
6325 could use redo to put them back, but if you move a line on the board
6326 before performing the redo, you will lose the ability to "redo" the
6327 three "undone" lines.
6329 %end-doc */
6331 static int
6332 ActionRedo (int argc, char **argv, Coord x, Coord y)
6334 if (((Settings.Mode == POLYGON_MODE ||
6335 Settings.Mode == POLYGONHOLE_MODE) &&
6336 Crosshair.AttachedPolygon.PointN) ||
6337 Crosshair.AttachedLine.State == STATE_SECOND)
6338 return 1;
6339 notify_crosshair_change (false);
6340 if (Redo (true))
6342 SetChangedFlag (true);
6343 if (Settings.Mode == LINE_MODE &&
6344 Crosshair.AttachedLine.State != STATE_FIRST)
6346 LineType *line = g_list_last (CURRENT->Line)->data;
6347 Crosshair.AttachedLine.Point1.X =
6348 Crosshair.AttachedLine.Point2.X = line->Point2.X;
6349 Crosshair.AttachedLine.Point1.Y =
6350 Crosshair.AttachedLine.Point2.Y = line->Point2.Y;
6351 addedLines++;
6354 notify_crosshair_change (true);
6355 return 0;
6358 /* --------------------------------------------------------------------------- */
6360 static const char polygon_syntax[] = "Polygon(Close|PreviousPoint)";
6362 static const char polygon_help[] = "Some polygon related stuff.";
6364 /* %start-doc actions Polygon
6366 Polygons need a special action routine to make life easier.
6368 @table @code
6370 @item Close
6371 Creates the final segment of the polygon. This may fail if clipping
6372 to 45 degree lines is switched on, in which case a warning is issued.
6374 @item PreviousPoint
6375 Resets the newly entered corner to the previous one. The Undo action
6376 will call Polygon(PreviousPoint) when appropriate to do so.
6378 @end table
6380 %end-doc */
6382 static int
6383 ActionPolygon (int argc, char **argv, Coord x, Coord y)
6385 char *function = ARG (0);
6386 if (function && Settings.Mode == POLYGON_MODE)
6388 notify_crosshair_change (false);
6389 switch (GetFunctionID (function))
6391 /* close open polygon if possible */
6392 case F_Close:
6393 ClosePolygon ();
6394 break;
6396 /* go back to the previous point */
6397 case F_PreviousPoint:
6398 GoToPreviousPoint ();
6399 break;
6401 notify_crosshair_change (true);
6403 return 0;
6406 /* --------------------------------------------------------------------------- */
6408 static const char routestyle_syntax[] = "RouteStyle(1|2|3|4)";
6410 static const char routestyle_help[] =
6411 "Copies the indicated routing style into the current sizes.";
6413 /* %start-doc actions RouteStyle
6415 %end-doc */
6417 static int
6418 ActionRouteStyle (int argc, char **argv, Coord x, Coord y)
6420 char *str = ARG (0);
6421 RouteStyleType *rts;
6422 int number;
6424 if (str)
6426 number = atoi (str);
6427 if (number > 0 && number <= NUM_STYLES)
6429 rts = &PCB->RouteStyle[number - 1];
6430 SetLineSize (rts->Thick);
6431 SetViaSize (rts->Diameter, true);
6432 SetViaDrillingHole (rts->Hole, true);
6433 SetKeepawayWidth (rts->Keepaway);
6434 hid_action("RouteStylesChanged");
6437 return 0;
6441 /* --------------------------------------------------------------------------- */
6443 static const char moveobject_syntax[] = "MoveObject(X,Y,dim)";
6445 static const char moveobject_help[] = "Moves the object under the crosshair.";
6447 /* %start-doc actions MoveObject
6449 The @code{X} and @code{Y} are treated like @code{delta} is for many
6450 other objects. For each, if it's prefixed by @code{+} or @code{-},
6451 then that amount is relative. Otherwise, it's absolute. Units can be
6452 @code{mil} or @code{mm}; if unspecified, units are PCB's internal
6453 units, currently 1/100 mil.
6455 %end-doc */
6457 static int
6458 ActionMoveObject (int argc, char **argv, Coord x, Coord y)
6460 char *x_str = ARG (0);
6461 char *y_str = ARG (1);
6462 char *units = ARG (2);
6463 Coord nx, ny;
6464 bool absolute1, absolute2;
6465 void *ptr1, *ptr2, *ptr3;
6466 int type;
6468 ny = GetValue (y_str, units, &absolute1);
6469 nx = GetValue (x_str, units, &absolute2);
6471 type = SearchScreen (x, y, MOVE_TYPES, &ptr1, &ptr2, &ptr3);
6472 if (type == NO_TYPE)
6474 Message (_("Nothing found under crosshair\n"));
6475 return 1;
6477 if (absolute1)
6478 nx -= x;
6479 if (absolute2)
6480 ny -= y;
6481 Crosshair.AttachedObject.RubberbandN = 0;
6482 if (TEST_FLAG (RUBBERBANDFLAG, PCB))
6483 LookupRubberbandLines (type, ptr1, ptr2, ptr3);
6484 if (type == ELEMENT_TYPE)
6485 LookupRatLines (type, ptr1, ptr2, ptr3);
6486 MoveObjectAndRubberband (type, ptr1, ptr2, ptr3, nx, ny);
6487 SetChangedFlag (true);
6488 return 0;
6491 /* --------------------------------------------------------------------------- */
6493 static const char movetocurrentlayer_syntax[] =
6494 "MoveToCurrentLayer(Object|SelectedObjects)";
6496 static const char movetocurrentlayer_help[] =
6497 "Moves objects to the current layer.";
6499 /* %start-doc actions MoveToCurrentLayer
6501 Note that moving an element from a component layer to a solder layer,
6502 or from solder to component, won't automatically flip it. Use the
6503 @code{Flip()} action to do that.
6505 %end-doc */
6507 static int
6508 ActionMoveToCurrentLayer (int argc, char **argv, Coord x, Coord y)
6510 char *function = ARG (0);
6511 if (function)
6513 switch (GetFunctionID (function))
6515 case F_Object:
6517 int type;
6518 void *ptr1, *ptr2, *ptr3;
6520 gui->get_coords (_("Select an Object"), &x, &y);
6521 if ((type =
6522 SearchScreen (x, y, MOVETOLAYER_TYPES,
6523 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
6524 if (MoveObjectToLayer (type, ptr1, ptr2, ptr3, CURRENT, false))
6525 SetChangedFlag (true);
6526 break;
6529 case F_SelectedObjects:
6530 case F_Selected:
6531 if (MoveSelectedObjectsToLayer (CURRENT))
6532 SetChangedFlag (true);
6533 break;
6536 return 0;
6540 static const char setsame_syntax[] = "SetSame()";
6542 static const char setsame_help[] =
6543 "Sets current layer and sizes to match indicated item.";
6545 /* %start-doc actions SetSame
6547 When invoked over any line, arc, polygon, or via, this changes the
6548 current layer to be the layer that item is on, and changes the current
6549 sizes (thickness, keepaway, drill, etc) according to that item.
6551 %end-doc */
6553 static int
6554 ActionSetSame (int argc, char **argv, Coord x, Coord y)
6556 void *ptr1, *ptr2, *ptr3;
6557 int type;
6558 LayerType *layer = CURRENT;
6560 type = SearchScreen (x, y, CLONE_TYPES, &ptr1, &ptr2, &ptr3);
6561 /* set layer current and size from line or arc */
6562 switch (type)
6564 case LINE_TYPE:
6565 notify_crosshair_change (false);
6566 Settings.LineThickness = ((LineType *) ptr2)->Thickness;
6567 Settings.Keepaway = ((LineType *) ptr2)->Clearance / 2;
6568 layer = (LayerType *) ptr1;
6569 if (Settings.Mode != LINE_MODE)
6570 SetMode (LINE_MODE);
6571 notify_crosshair_change (true);
6572 hid_action ("RouteStylesChanged");
6573 break;
6575 case ARC_TYPE:
6576 notify_crosshair_change (false);
6577 Settings.LineThickness = ((ArcType *) ptr2)->Thickness;
6578 Settings.Keepaway = ((ArcType *) ptr2)->Clearance / 2;
6579 layer = (LayerType *) ptr1;
6580 if (Settings.Mode != ARC_MODE)
6581 SetMode (ARC_MODE);
6582 notify_crosshair_change (true);
6583 hid_action ("RouteStylesChanged");
6584 break;
6586 case POLYGON_TYPE:
6587 layer = (LayerType *) ptr1;
6588 break;
6590 case VIA_TYPE:
6591 notify_crosshair_change (false);
6592 Settings.ViaThickness = ((PinType *) ptr2)->Thickness;
6593 Settings.ViaDrillingHole = ((PinType *) ptr2)->DrillingHole;
6594 Settings.Keepaway = ((PinType *) ptr2)->Clearance / 2;
6595 if (Settings.Mode != VIA_MODE)
6596 SetMode (VIA_MODE);
6597 notify_crosshair_change (true);
6598 hid_action ("RouteStylesChanged");
6599 break;
6601 default:
6602 return 1;
6604 if (layer != CURRENT)
6606 ChangeGroupVisibility (GetLayerNumber (PCB->Data, layer), true, true);
6607 Redraw ();
6609 return 0;
6613 /* --------------------------------------------------------------------------- */
6615 static const char setflag_syntax[] =
6616 "SetFlag(Object|Selected|SelectedObjects, flag)\n"
6617 "SetFlag(SelectedLines|SelectedPins|SelectedVias, flag)\n"
6618 "SetFlag(SelectedPads|SelectedTexts|SelectedNames, flag)\n"
6619 "SetFlag(SelectedElements, flag)\n"
6620 "flag = square | octagon | thermal | join";
6622 static const char setflag_help[] = "Sets flags on objects.";
6624 /* %start-doc actions SetFlag
6626 Turns the given flag on, regardless of its previous setting. See
6627 @code{ChangeFlag}.
6629 @example
6630 SetFlag(SelectedPins,thermal)
6631 @end example
6633 %end-doc */
6635 static int
6636 ActionSetFlag (int argc, char **argv, Coord x, Coord y)
6638 char *function = ARG (0);
6639 char *flag = ARG (1);
6640 ChangeFlag (function, flag, 1, "SetFlag");
6641 return 0;
6644 /* --------------------------------------------------------------------------- */
6646 static const char clrflag_syntax[] =
6647 "ClrFlag(Object|Selected|SelectedObjects, flag)\n"
6648 "ClrFlag(SelectedLines|SelectedPins|SelectedVias, flag)\n"
6649 "ClrFlag(SelectedPads|SelectedTexts|SelectedNames, flag)\n"
6650 "ClrFlag(SelectedElements, flag)\n"
6651 "flag = square | octagon | thermal | join";
6653 static const char clrflag_help[] = "Clears flags on objects.";
6655 /* %start-doc actions ClrFlag
6657 Turns the given flag off, regardless of its previous setting. See
6658 @code{ChangeFlag}.
6660 @example
6661 ClrFlag(SelectedLines,join)
6662 @end example
6664 %end-doc */
6666 static int
6667 ActionClrFlag (int argc, char **argv, Coord x, Coord y)
6669 char *function = ARG (0);
6670 char *flag = ARG (1);
6671 ChangeFlag (function, flag, 0, "ClrFlag");
6672 return 0;
6675 /* --------------------------------------------------------------------------- */
6677 static const char changeflag_syntax[] =
6678 "ChangeFlag(Object|Selected|SelectedObjects, flag, value)\n"
6679 "ChangeFlag(SelectedLines|SelectedPins|SelectedVias, flag, value)\n"
6680 "ChangeFlag(SelectedPads|SelectedTexts|SelectedNames, flag, value)\n"
6681 "ChangeFlag(SelectedElements, flag, value)\n"
6682 "flag = square | octagon | thermal | join\n"
6683 "value = 0 | 1";
6685 static const char changeflag_help[] = "Sets or clears flags on objects.";
6687 /* %start-doc actions ChangeFlag
6689 Toggles the given flag on the indicated object(s). The flag may be
6690 one of the flags listed above (square, octagon, thermal, join). The
6691 value may be the number 0 or 1. If the value is 0, the flag is
6692 cleared. If the value is 1, the flag is set.
6694 %end-doc */
6696 static int
6697 ActionChangeFlag (int argc, char **argv, Coord x, Coord y)
6699 char *function = ARG (0);
6700 char *flag = ARG (1);
6701 int value = argc > 2 ? atoi (argv[2]) : -1;
6702 if (value != 0 && value != 1)
6703 AFAIL (changeflag);
6705 ChangeFlag (function, flag, value, "ChangeFlag");
6706 return 0;
6710 static void
6711 ChangeFlag (char *what, char *flag_name, int value, char *cmd_name)
6713 bool (*set_object) (int, void *, void *, void *);
6714 bool (*set_selected) (int);
6716 if (NSTRCMP (flag_name, "square") == 0)
6718 set_object = value ? SetObjectSquare : ClrObjectSquare;
6719 set_selected = value ? SetSelectedSquare : ClrSelectedSquare;
6721 else if (NSTRCMP (flag_name, "octagon") == 0)
6723 set_object = value ? SetObjectOctagon : ClrObjectOctagon;
6724 set_selected = value ? SetSelectedOctagon : ClrSelectedOctagon;
6726 else if (NSTRCMP (flag_name, "join") == 0)
6728 /* Note: these are backwards, because the flag is "clear" but
6729 the command is "join". */
6730 set_object = value ? ClrObjectJoin : SetObjectJoin;
6731 set_selected = value ? ClrSelectedJoin : SetSelectedJoin;
6733 else
6735 Message (_("%s(): Flag \"%s\" is not valid\n"), cmd_name, flag_name);
6736 return;
6739 switch (GetFunctionID (what))
6741 case F_Object:
6743 int type;
6744 void *ptr1, *ptr2, *ptr3;
6746 if ((type =
6747 SearchScreen (Crosshair.X, Crosshair.Y, CHANGESIZE_TYPES,
6748 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
6749 if (TEST_FLAG (LOCKFLAG, (PinType *) ptr2))
6750 Message (_("Sorry, the object is locked\n"));
6751 if (set_object (type, ptr1, ptr2, ptr3))
6752 SetChangedFlag (true);
6753 break;
6756 case F_SelectedVias:
6757 if (set_selected (VIA_TYPE))
6758 SetChangedFlag (true);
6759 break;
6761 case F_SelectedPins:
6762 if (set_selected (PIN_TYPE))
6763 SetChangedFlag (true);
6764 break;
6766 case F_SelectedPads:
6767 if (set_selected (PAD_TYPE))
6768 SetChangedFlag (true);
6769 break;
6771 case F_SelectedLines:
6772 if (set_selected (LINE_TYPE))
6773 SetChangedFlag (true);
6774 break;
6776 case F_SelectedTexts:
6777 if (set_selected (TEXT_TYPE))
6778 SetChangedFlag (true);
6779 break;
6781 case F_SelectedNames:
6782 if (set_selected (ELEMENTNAME_TYPE))
6783 SetChangedFlag (true);
6784 break;
6786 case F_SelectedElements:
6787 if (set_selected (ELEMENT_TYPE))
6788 SetChangedFlag (true);
6789 break;
6791 case F_Selected:
6792 case F_SelectedObjects:
6793 if (set_selected (CHANGESIZE_TYPES))
6794 SetChangedFlag (true);
6795 break;
6799 /* --------------------------------------------------------------------------- */
6801 static const char executefile_syntax[] = "ExecuteFile(filename)";
6803 static const char executefile_help[] = "Run actions from the given file.";
6805 /* %start-doc actions ExecuteFile
6807 Lines starting with @code{#} are ignored.
6809 %end-doc */
6811 static int
6812 ActionExecuteFile (int argc, char **argv, Coord x, Coord y)
6814 FILE *fp;
6815 char *fname;
6816 char line[256];
6817 int n = 0;
6818 char *sp;
6820 if (argc != 1)
6821 AFAIL (executefile);
6823 fname = argv[0];
6825 if ((fp = fopen (fname, "r")) == NULL)
6827 fprintf (stderr, _("Could not open actions file \"%s\".\n"), fname);
6828 return 1;
6831 defer_updates = 1;
6832 defer_needs_update = 0;
6833 while (fgets (line, sizeof (line), fp) != NULL)
6835 n++;
6836 sp = line;
6838 /* eat the trailing newline */
6839 while (*sp && *sp != '\r' && *sp != '\n')
6840 sp++;
6841 *sp = '\0';
6843 /* eat leading spaces and tabs */
6844 sp = line;
6845 while (*sp && (*sp == ' ' || *sp == '\t'))
6846 sp++;
6849 * if we have anything left and its not a comment line
6850 * then execute it
6853 if (*sp && *sp != '#')
6855 /*Message ("%s : line %-3d : \"%s\"\n", fname, n, sp);*/
6856 hid_parse_actions (sp);
6860 defer_updates = 0;
6861 if (defer_needs_update)
6863 IncrementUndoSerialNumber ();
6864 gui->invalidate_all ();
6866 fclose (fp);
6867 return 0;
6870 /* --------------------------------------------------------------------------- */
6872 static int
6873 ActionPSCalib (int argc, char **argv, Coord x, Coord y)
6875 HID *ps = hid_find_exporter ("ps");
6876 ps->calibrate (0.0,0.0);
6877 return 0;
6880 /* --------------------------------------------------------------------------- */
6882 static ElementType *element_cache = NULL;
6884 static ElementType *
6885 find_element_by_refdes (char *refdes)
6887 if (element_cache
6888 && NAMEONPCB_NAME(element_cache)
6889 && strcmp (NAMEONPCB_NAME(element_cache), refdes) == 0)
6890 return element_cache;
6892 ELEMENT_LOOP (PCB->Data);
6894 if (NAMEONPCB_NAME(element)
6895 && strcmp (NAMEONPCB_NAME(element), refdes) == 0)
6897 element_cache = element;
6898 return element_cache;
6901 END_LOOP;
6902 return NULL;
6905 static AttributeType *
6906 lookup_attr (AttributeListType *list, const char *name)
6908 int i;
6909 for (i=0; i<list->Number; i++)
6910 if (strcmp (list->List[i].name, name) == 0)
6911 return & list->List[i];
6912 return NULL;
6915 static void
6916 delete_attr (AttributeListType *list, AttributeType *attr)
6918 int idx = attr - list->List;
6919 if (idx < 0 || idx >= list->Number)
6920 return;
6921 if (list->Number - idx > 1)
6922 memmove (attr, attr+1, (list->Number - idx - 1) * sizeof(AttributeType));
6923 list->Number --;
6926 /* ---------------------------------------------------------------- */
6927 static const char elementlist_syntax[] = "ElementList(Start|Done|Need,<refdes>,<footprint>,<value>)";
6929 static const char elementlist_help[] = "Adds the given element if it doesn't already exist.";
6931 /* %start-doc actions elementlist
6933 @table @code
6935 @item Start
6936 Indicates the start of an element list; call this before any Need
6937 actions.
6939 @item Need
6940 Searches the board for an element with a matching refdes.
6942 If found, the value and footprint are updated.
6944 If not found, a new element is created with the given footprint and value.
6946 @item Done
6947 Compares the list of elements needed since the most recent
6948 @code{start} with the list of elements actually on the board. Any
6949 elements that weren't listed are selected, so that the user may delete
6950 them.
6952 @end table
6954 %end-doc */
6956 static int number_of_footprints_not_found;
6958 static int
6959 parse_layout_attribute_units (char *name, int def)
6961 const char *as = AttributeGet (PCB, name);
6962 if (!as)
6963 return def;
6964 return GetValue (as, NULL, NULL);
6967 static int
6968 ActionElementList (int argc, char **argv, Coord x, Coord y)
6970 ElementType *e = NULL;
6971 char *refdes, *value, *footprint, *old;
6972 char *args[3];
6973 char *function = argv[0];
6975 #ifdef DEBUG
6976 printf("Entered ActionElementList, executing function %s\n", function);
6977 #endif
6979 if (strcasecmp (function, "start") == 0)
6981 ELEMENT_LOOP (PCB->Data);
6983 CLEAR_FLAG (FOUNDFLAG, element);
6985 END_LOOP;
6986 element_cache = NULL;
6987 number_of_footprints_not_found = 0;
6988 return 0;
6991 if (strcasecmp (function, "done") == 0)
6993 ELEMENT_LOOP (PCB->Data);
6995 if (TEST_FLAG (FOUNDFLAG, element))
6997 CLEAR_FLAG (FOUNDFLAG, element);
6999 else if (! EMPTY_STRING_P (NAMEONPCB_NAME (element)))
7001 /* Unnamed elements should remain untouched */
7002 SET_FLAG (SELECTEDFLAG, element);
7005 END_LOOP;
7006 if (number_of_footprints_not_found > 0)
7007 gui->confirm_dialog ("Not all requested footprints were found.\n"
7008 "See the message log for details",
7009 "Ok", NULL);
7010 return 0;
7013 if (strcasecmp (function, "need") != 0)
7014 AFAIL (elementlist);
7016 if (argc != 4)
7017 AFAIL (elementlist);
7019 argc --;
7020 argv ++;
7022 refdes = ARG(0);
7023 footprint = ARG(1);
7024 value = ARG(2);
7026 args[0] = footprint;
7027 args[1] = refdes;
7028 args[2] = value;
7030 #ifdef DEBUG
7031 printf(" ... footprint = %s\n", footprint);
7032 printf(" ... refdes = %s\n", refdes);
7033 printf(" ... value = %s\n", value);
7034 #endif
7036 e = find_element_by_refdes (refdes);
7038 if (!e)
7040 Coord nx, ny, d;
7042 #ifdef DEBUG
7043 printf(" ... Footprint not on board, need to add it.\n");
7044 #endif
7045 /* Not on board, need to add it. */
7046 if (LoadFootprint(argc, args, x, y))
7048 number_of_footprints_not_found ++;
7049 return 1;
7052 nx = PCB->MaxWidth / 2;
7053 ny = PCB->MaxHeight / 2;
7054 d = MIN (PCB->MaxWidth, PCB->MaxHeight) / 10;
7056 nx = parse_layout_attribute_units ("import::newX", nx);
7057 ny = parse_layout_attribute_units ("import::newY", ny);
7058 d = parse_layout_attribute_units ("import::disperse", d);
7060 if (d > 0)
7062 nx += rand () % (d*2) - d;
7063 ny += rand () % (d*2) - d;
7066 if (nx < 0)
7067 nx = 0;
7068 if (nx >= PCB->MaxWidth)
7069 nx = PCB->MaxWidth - 1;
7070 if (ny < 0)
7071 ny = 0;
7072 if (ny >= PCB->MaxHeight)
7073 ny = PCB->MaxHeight - 1;
7075 /* Place components onto center of board. */
7076 if (CopyPastebufferToLayout (nx, ny))
7077 SetChangedFlag (true);
7080 else if (e && strcmp (DESCRIPTION_NAME(e), footprint) != 0)
7082 #ifdef DEBUG
7083 printf(" ... Footprint on board, but different from footprint loaded.\n");
7084 #endif
7085 int er, pr, i;
7086 Coord mx, my;
7087 ElementType *pe;
7089 /* Different footprint, we need to swap them out. */
7090 if (LoadFootprint(argc, args, x, y))
7092 number_of_footprints_not_found ++;
7093 return 1;
7096 er = ElementOrientation (e);
7097 pe = PASTEBUFFER->Data->Element->data;
7098 if (!FRONT (e))
7099 MirrorElementCoordinates (PASTEBUFFER->Data, pe, pe->MarkY*2 - PCB->MaxHeight);
7100 pr = ElementOrientation (pe);
7102 mx = e->MarkX;
7103 my = e->MarkY;
7105 if (er != pr)
7106 RotateElementLowLevel (PASTEBUFFER->Data, pe, pe->MarkX, pe->MarkY, (er-pr+4)%4);
7108 for (i=0; i<MAX_ELEMENTNAMES; i++)
7110 pe->Name[i].X = e->Name[i].X - mx + pe->MarkX ;
7111 pe->Name[i].Y = e->Name[i].Y - my + pe->MarkY ;
7112 pe->Name[i].Direction = e->Name[i].Direction;
7113 pe->Name[i].Scale = e->Name[i].Scale;
7116 RemoveElement (e);
7118 if (CopyPastebufferToLayout (mx, my))
7119 SetChangedFlag (true);
7122 /* Now reload footprint */
7123 element_cache = NULL;
7124 e = find_element_by_refdes (refdes);
7126 old = ChangeElementText (PCB, PCB->Data, e, NAMEONPCB_INDEX, strdup (refdes));
7127 if (old)
7128 free(old);
7129 old = ChangeElementText (PCB, PCB->Data, e, VALUE_INDEX, strdup (value));
7130 if (old)
7131 free(old);
7133 SET_FLAG (FOUNDFLAG, e);
7135 #ifdef DEBUG
7136 printf(" ... Leaving ActionElementList.\n");
7137 #endif
7139 return 0;
7142 /* ---------------------------------------------------------------- */
7143 static const char elementsetattr_syntax[] = "ElementSetAttr(refdes,name[,value])";
7145 static const char elementsetattr_help[] = "Sets or clears an element-specific attribute.";
7147 /* %start-doc actions elementsetattr
7149 If a value is specified, the named attribute is added (if not already
7150 present) or changed (if it is) to the given value. If the value is
7151 not specified, the given attribute is removed if present.
7153 %end-doc */
7155 static int
7156 ActionElementSetAttr (int argc, char **argv, Coord x, Coord y)
7158 ElementType *e = NULL;
7159 char *refdes, *name, *value;
7160 AttributeType *attr;
7162 if (argc < 2)
7164 AFAIL (changepinname);
7167 refdes = argv[0];
7168 name = argv[1];
7169 value = ARG(2);
7171 ELEMENT_LOOP (PCB->Data);
7173 if (NSTRCMP (refdes, NAMEONPCB_NAME (element)) == 0)
7175 e = element;
7176 break;
7179 END_LOOP;
7181 if (!e)
7183 Message(_("Cannot change attribute of %s - element not found\n"), refdes);
7184 return 1;
7187 attr = lookup_attr (&e->Attributes, name);
7189 if (attr && value)
7191 free (attr->value);
7192 attr->value = strdup (value);
7194 if (attr && ! value)
7196 delete_attr (& e->Attributes, attr);
7198 if (!attr && value)
7200 CreateNewAttribute (& e->Attributes, name, value);
7203 return 0;
7206 /* ---------------------------------------------------------------- */
7207 static const char execcommand_syntax[] = "ExecCommand(command)";
7209 static const char execcommand_help[] = "Runs a command.";
7211 /* %start-doc actions execcommand
7213 Runs the given command, which is a system executable.
7215 %end-doc */
7217 static int
7218 ActionExecCommand (int argc, char **argv, Coord x, Coord y)
7220 char *command;
7222 if (argc < 1)
7224 AFAIL (execcommand);
7227 command = ARG(0);
7229 if (system (command))
7230 return 1;
7231 return 0;
7234 /* ---------------------------------------------------------------- */
7236 static int
7237 pcb_spawnvp (char **argv)
7239 #ifdef HAVE__SPAWNVP
7240 int result = _spawnvp (_P_WAIT, argv[0], (const char * const *) argv);
7241 if (result == -1)
7242 return 1;
7243 else
7244 return 0;
7245 #else
7246 int pid;
7247 pid = fork ();
7248 if (pid < 0)
7250 /* error */
7251 Message(_("Cannot fork!"));
7252 return 1;
7254 else if (pid == 0)
7256 /* Child */
7257 execvp (argv[0], argv);
7258 exit(1);
7260 else
7262 int rv;
7263 /* Parent */
7264 wait (&rv);
7266 return 0;
7267 #endif
7270 /* ---------------------------------------------------------------- */
7272 * Creates a new temporary file name. Hopefully the operating system
7273 * provides a mkdtemp() function to securily create a temporary
7274 * directory with mode 0700. If so then that directory is created and
7275 * the returned string is made up of the directory plus the name
7276 * variable. For example:
7278 * tempfile_name_new ("myfile") might return
7279 * "/var/tmp/pcb.123456/myfile".
7281 * If mkdtemp() is not available then 'name' is ignored and the
7282 * insecure tmpnam() function is used.
7284 * Files/names created with tempfile_name_new() should be unlinked
7285 * with tempfile_unlink to make sure the temporary directory is also
7286 * removed when mkdtemp() is used.
7288 static char *
7289 tempfile_name_new (char * name)
7291 char *tmpfile = NULL;
7292 #ifdef HAVE_MKDTEMP
7293 char *tmpdir, *mytmpdir;
7294 size_t len;
7295 #endif
7297 assert ( name != NULL );
7299 #ifdef HAVE_MKDTEMP
7300 #define TEMPLATE "pcb.XXXXXXXX"
7303 tmpdir = getenv ("TMPDIR");
7305 /* FIXME -- what about win32? */
7306 if (tmpdir == NULL) {
7307 tmpdir = "/tmp";
7310 mytmpdir = (char *) malloc (sizeof(char) *
7311 (strlen (tmpdir) +
7313 strlen (TEMPLATE) +
7314 1));
7315 if (mytmpdir == NULL) {
7316 fprintf (stderr, "%s(): malloc failed()\n", __FUNCTION__);
7317 exit (1);
7320 *mytmpdir = '\0';
7321 (void)strcat (mytmpdir, tmpdir);
7322 (void)strcat (mytmpdir, PCB_DIR_SEPARATOR_S);
7323 (void)strcat (mytmpdir, TEMPLATE);
7324 if (mkdtemp (mytmpdir) == NULL) {
7325 fprintf (stderr, "%s(): mkdtemp (\"%s\") failed\n", __FUNCTION__, mytmpdir);
7326 free (mytmpdir);
7327 return NULL;
7331 len = strlen (mytmpdir) + /* the temp directory name */
7332 1 + /* the directory sep. */
7333 strlen (name) + /* the file name */
7334 1 /* the \0 termination */
7337 tmpfile = (char *) malloc (sizeof (char) * len);
7339 *tmpfile = '\0';
7340 (void)strcat (tmpfile, mytmpdir);
7341 (void)strcat (tmpfile, PCB_DIR_SEPARATOR_S);
7342 (void)strcat (tmpfile, name);
7344 free (mytmpdir);
7345 #undef TEMPLATE
7346 #else
7348 * tmpnam() uses a static buffer so strdup() the result right away
7349 * in case someone decides to create multiple temp names.
7351 tmpfile = strdup (tmpnam (NULL));
7352 #ifdef __WIN32__
7354 /* Guile doesn't like \ separators */
7355 char *c;
7356 for (c = tmpfile; *c; c++)
7357 if (*c == '\\')
7358 *c = '/';
7360 #endif
7361 #endif
7363 return tmpfile;
7366 /* ---------------------------------------------------------------- */
7368 * Unlink a temporary file. If we have mkdtemp() then our temp file
7369 * lives in a temporary directory and we need to remove that directory
7370 * too.
7372 static int
7373 tempfile_unlink (char * name)
7375 #ifdef DEBUG
7376 /* SDB says: Want to keep old temp files for examiniation when debugging */
7377 return 0;
7378 #endif
7380 #ifdef HAVE_MKDTEMP
7381 int e, rc2 = 0;
7382 char *dname;
7384 unlink (name);
7385 /* it is possible that the file was never created so it is OK if the
7386 unlink fails */
7388 /* now figure out the directory name to remove */
7389 e = strlen (name) - 1;
7390 while (e > 0 && name[e] != PCB_DIR_SEPARATOR_C) {e--;}
7392 dname = strdup (name);
7393 dname[e] = '\0';
7396 * at this point, e *should* point to the end of the directory part
7397 * but lets make sure.
7399 if (e > 0) {
7400 rc2 = rmdir (dname);
7401 if (rc2 != 0) {
7402 perror (dname);
7405 } else {
7406 fprintf (stderr, _("%s(): Unable to determine temp directory name from the temp file\n"),
7407 __FUNCTION__);
7408 fprintf (stderr, "%s(): \"%s\"\n",
7409 __FUNCTION__, name);
7410 rc2 = -1;
7413 /* name was allocated with malloc */
7414 free (dname);
7415 free (name);
7418 * FIXME - should also return -1 if the temp file exists and was not
7419 * removed.
7421 if (rc2 != 0) {
7422 return -1;
7425 #else
7426 int rc = unlink (name);
7428 if (rc != 0) {
7429 fprintf (stderr, _("Failed to unlink \"%s\"\n"), name);
7430 free (name);
7431 return rc;
7433 free (name);
7435 #endif
7437 return 0;
7440 /* ---------------------------------------------------------------- */
7441 static const char import_syntax[] =
7442 "Import()\n"
7443 "Import([gnetlist|make[,source,source,...]])\n"
7444 "Import(setnewpoint[,(mark|center|X,Y)])\n"
7445 "Import(setdisperse,D,units)\n";
7447 static const char import_help[] = "Import schematics.";
7449 /* %start-doc actions Import
7451 Imports element and netlist data from the schematics (or some other
7452 source). The first parameter, which is optional, is the mode. If not
7453 specified, the @code{import::mode} attribute in the PCB is used.
7454 @code{gnetlist} means gnetlist is used to obtain the information from
7455 the schematics. @code{make} invokes @code{make}, assuming the user
7456 has a @code{Makefile} in the current directory. The @code{Makefile}
7457 will be invoked with the following variables set:
7459 @table @code
7461 @item PCB
7462 The name of the .pcb file
7464 @item SRCLIST
7465 A space-separated list of source files
7467 @item OUT
7468 The name of the file in which to put the command script, which may
7469 contain any @pcb{} actions. By default, this is a temporary file
7470 selected by @pcb{}, but if you specify an @code{import::outfile}
7471 attribute, that file name is used instead (and not automatically
7472 deleted afterwards).
7474 @end table
7476 The target specified to be built is the first of these that apply:
7478 @itemize @bullet
7480 @item
7481 The target specified by an @code{import::target} attribute.
7483 @item
7484 The output file specified by an @code{import::outfile} attribute.
7486 @item
7487 If nothing else is specified, the target is @code{pcb_import}.
7489 @end itemize
7491 If you specify an @code{import::makefile} attribute, then "-f <that
7492 file>" will be added to the command line.
7494 If you specify the mode, you may also specify the source files
7495 (schematics). If you do not specify any, the list of schematics is
7496 obtained by reading the @code{import::src@var{N}} attributes (like
7497 @code{import::src0}, @code{import::src1}, etc).
7499 For compatibility with future extensions to the import file format,
7500 the generated file @emph{must not} start with the two characters
7501 @code{#%}.
7503 If a temporary file is needed the @code{TMPDIR} environment variable
7504 is used to select its location.
7506 Note that the programs @code{gnetlist} and @code{make} may be
7507 overridden by the user via the @code{make-program} and @code{gnetlist}
7508 @code{pcb} settings (i.e. in @code{~/.pcb/settings} or on the command
7509 line).
7511 If @pcb{} cannot determine which schematic(s) to import from, the GUI
7512 is called to let user choose (see @code{ImportGUI()}).
7514 Note that Import() doesn't delete anything - after an Import, elements
7515 which shouldn't be on the board are selected and may be removed once
7516 it's determined that the deletion is appropriate.
7518 If @code{Import()} is called with @code{setnewpoint}, then the location
7519 of new components can be specified. This is where parts show up when
7520 they're added to the board. The default is the center of the board.
7522 @table @code
7524 @item Import(setnewpoint)
7526 Prompts the user to click on the board somewhere, uses that point. If
7527 called by a hotkey, uses the current location of the crosshair.
7529 @item Import(setnewpoint,mark)
7531 Uses the location of the mark. If no mark is present, the point is
7532 not changed.
7534 @item Import(setnewpoint,center)
7536 Resets the point to the center of the board.
7538 @item Import(setnewpoint,X,Y,units)
7540 Sets the point to the specific coordinates given. Example:
7541 @code{Import(setnewpoint,50,25,mm)}
7543 @end table
7545 Note that the X and Y locations are stored in attributes named
7546 @code{import::newX} and @code{import::newY} so you could change them
7547 manually if you wished.
7549 Calling @code{Import(setdisperse,D,units)} sets how much the newly
7550 placed elements are dispersed relative to the set point. For example,
7551 @code{Import(setdisperse,10,mm)} will offset each part randomly up to
7552 10mm away from the point. The default dispersion is 1/10th of the
7553 smallest board dimension. Dispersion is saved in the
7554 @code{import::disperse} attribute.
7556 %end-doc */
7558 static int
7559 ActionImport (int argc, char **argv, Coord x, Coord y)
7561 char *mode;
7562 char **sources = NULL;
7563 int nsources = 0;
7565 #ifdef DEBUG
7566 printf("ActionImport: =========== Entering ActionImport ============\n");
7567 #endif
7569 mode = ARG (0);
7571 if (mode && strcasecmp (mode, "setdisperse") == 0)
7573 char *ds, *units;
7574 char buf[50];
7576 ds = ARG (1);
7577 units = ARG (2);
7578 if (!ds)
7580 const char *as = AttributeGet (PCB, "import::disperse");
7581 ds = gui->prompt_for(_("Enter dispersion:"), as ? as : "0");
7583 if (units)
7585 sprintf(buf, "%s%s", ds, units);
7586 AttributePut (PCB, "import::disperse", buf);
7588 else
7589 AttributePut (PCB, "import::disperse", ds);
7590 if (ARG (1) == NULL)
7591 free (ds);
7592 return 0;
7595 if (mode && strcasecmp (mode, "setnewpoint") == 0)
7597 const char *xs, *ys, *units;
7598 Coord x, y;
7599 char buf[50];
7601 xs = ARG (1);
7602 ys = ARG (2);
7603 units = ARG (3);
7605 if (!xs)
7607 gui->get_coords (_("Click on a location"), &x, &y);
7609 else if (strcasecmp (xs, "center") == 0)
7611 AttributeRemove (PCB, "import::newX");
7612 AttributeRemove (PCB, "import::newY");
7613 return 0;
7615 else if (strcasecmp (xs, "mark") == 0)
7617 if (!Marked.status)
7618 return 0;
7620 x = Marked.X;
7621 y = Marked.Y;
7623 else if (ys)
7625 x = GetValue (xs, units, NULL);
7626 y = GetValue (ys, units, NULL);
7628 else
7630 Message (_("Bad syntax for Import(setnewpoint)"));
7631 return 1;
7634 pcb_sprintf (buf, "%$ms", x);
7635 AttributePut (PCB, "import::newX", buf);
7636 pcb_sprintf (buf, "%$ms", y);
7637 AttributePut (PCB, "import::newY", buf);
7638 return 0;
7641 if (! mode)
7642 mode = AttributeGet (PCB, "import::mode");
7643 if (! mode)
7644 mode = "gnetlist";
7646 if (argc > 1)
7648 sources = argv + 1;
7649 nsources = argc - 1;
7652 if (! sources)
7654 char sname[40];
7655 char *src;
7657 nsources = -1;
7658 do {
7659 nsources ++;
7660 sprintf(sname, "import::src%d", nsources);
7661 src = AttributeGet (PCB, sname);
7662 } while (src);
7664 if (nsources > 0)
7666 sources = (char **) malloc ((nsources + 1) * sizeof (char *));
7667 nsources = -1;
7668 do {
7669 nsources ++;
7670 sprintf(sname, "import::src%d", nsources);
7671 src = AttributeGet (PCB, sname);
7672 sources[nsources] = src;
7673 } while (src);
7677 if (! sources)
7679 /* Replace .pcb with .sch and hope for the best. */
7680 char *pcbname = PCB->Filename;
7681 char *schname;
7682 char *dot, *slash, *bslash;
7684 if (!pcbname)
7685 return hid_action("ImportGUI");
7687 schname = (char *) malloc (strlen(pcbname) + 5);
7688 strcpy (schname, pcbname);
7689 dot = strchr (schname, '.');
7690 slash = strchr (schname, '/');
7691 bslash = strchr (schname, '\\');
7692 if (dot && slash && dot < slash)
7693 dot = NULL;
7694 if (dot && bslash && dot < bslash)
7695 dot = NULL;
7696 if (dot)
7697 *dot = 0;
7698 strcat (schname, ".sch");
7700 if (access (schname, F_OK))
7702 free (schname);
7703 return hid_action("ImportGUI");
7706 sources = (char **) malloc (2 * sizeof (char *));
7707 sources[0] = schname;
7708 sources[1] = NULL;
7709 nsources = 1;
7712 if (strcasecmp (mode, "gnetlist") == 0)
7714 char *tmpfile = tempfile_name_new ("gnetlist_output");
7715 char **cmd;
7716 int i;
7718 if (tmpfile == NULL) {
7719 Message (_("Could not create temp file"));
7720 return 1;
7723 cmd = (char **) malloc ((7 + nsources) * sizeof (char *));
7724 cmd[0] = Settings.GnetlistProgram;
7725 cmd[1] = "-g";
7726 cmd[2] = "pcbfwd";
7727 cmd[3] = "-o";
7728 cmd[4] = tmpfile;
7729 cmd[5] = "--";
7730 for (i=0; i<nsources; i++)
7731 cmd[6+i] = sources[i];
7732 cmd[6+nsources] = NULL;
7734 #ifdef DEBUG
7735 printf("ActionImport: =========== About to run gnetlist ============\n");
7736 printf("%s %s %s %s %s %s %s ...\n",
7737 cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6]);
7738 #endif
7740 if (pcb_spawnvp (cmd))
7742 unlink (tmpfile);
7743 return 1;
7746 #ifdef DEBUG
7747 printf("ActionImport: =========== About to run ActionExecuteFile, file = %s ============\n", tmpfile);
7748 #endif
7750 cmd[0] = tmpfile;
7751 cmd[1] = NULL;
7752 ActionExecuteFile (1, cmd, 0, 0);
7754 free (cmd);
7755 tempfile_unlink (tmpfile);
7757 else if (strcasecmp (mode, "make") == 0)
7759 int must_free_tmpfile = 0;
7760 char *tmpfile;
7761 char *cmd[10];
7762 int i;
7763 char *srclist;
7764 int srclen;
7765 char *user_outfile = NULL;
7766 char *user_makefile = NULL;
7767 char *user_target = NULL;
7770 user_outfile = AttributeGet (PCB, "import::outfile");
7771 user_makefile = AttributeGet (PCB, "import::makefile");
7772 user_target = AttributeGet (PCB, "import::target");
7773 if (user_outfile && !user_target)
7774 user_target = user_outfile;
7776 if (user_outfile)
7777 tmpfile = user_outfile;
7778 else
7780 tmpfile = tempfile_name_new ("gnetlist_output");
7781 if (tmpfile == NULL) {
7782 Message (_("Could not create temp file"));
7783 return 1;
7785 must_free_tmpfile = 1;
7788 srclen = sizeof("SRCLIST=") + 2;
7789 for (i=0; i<nsources; i++)
7790 srclen += strlen (sources[i]) + 2;
7791 srclist = (char *) malloc (srclen);
7792 strcpy (srclist, "SRCLIST=");
7793 for (i=0; i<nsources; i++)
7795 if (i)
7796 strcat (srclist, " ");
7797 strcat (srclist, sources[i]);
7800 cmd[0] = Settings.MakeProgram;
7801 cmd[1] = "-s";
7802 cmd[2] = Concat ("PCB=", PCB->Filename, NULL);
7803 cmd[3] = srclist;
7804 cmd[4] = Concat ("OUT=", tmpfile, NULL);
7805 i = 5;
7806 if (user_makefile)
7808 cmd[i++] = "-f";
7809 cmd[i++] = user_makefile;
7811 cmd[i++] = user_target ? user_target : (char *)"pcb_import";
7812 cmd[i++] = NULL;
7814 if (pcb_spawnvp (cmd))
7816 if (must_free_tmpfile)
7817 unlink (tmpfile);
7818 free (cmd[2]);
7819 free (cmd[3]);
7820 free (cmd[4]);
7821 return 1;
7824 cmd[0] = tmpfile;
7825 cmd[1] = NULL;
7826 ActionExecuteFile (1, cmd, 0, 0);
7828 free (cmd[2]);
7829 free (cmd[3]);
7830 free (cmd[4]);
7831 if (must_free_tmpfile)
7832 tempfile_unlink (tmpfile);
7834 else
7836 Message (_("Unknown import mode: %s\n"), mode);
7837 return 1;
7840 DeleteRats (false);
7841 AddAllRats (false, NULL);
7843 #ifdef DEBUG
7844 printf("ActionImport: =========== Leaving ActionImport ============\n");
7845 #endif
7847 return 0;
7850 /* ------------------------------------------------------------ */
7852 static const char attributes_syntax[] =
7853 "Attributes(Layout|Layer|Element)\n"
7854 "Attributes(Layer,layername)";
7856 static const char attributes_help[] =
7857 "Let the user edit the attributes of the layout, current or given\n"
7858 "layer, or selected element.";
7860 /* %start-doc actions Attributes
7862 This just pops up a dialog letting the user edit the attributes of the
7863 pcb, an element, or a layer.
7865 %end-doc */
7868 static int
7869 ActionAttributes (int argc, char **argv, Coord x, Coord y)
7871 char *function = ARG (0);
7872 char *layername = ARG (1);
7873 char *buf;
7875 if (!function)
7876 AFAIL (attributes);
7878 if (!gui->edit_attributes)
7880 Message (_("This GUI doesn't support Attribute Editing\n"));
7881 return 1;
7884 switch (GetFunctionID (function))
7886 case F_Layout:
7888 gui->edit_attributes("Layout Attributes", &(PCB->Attributes));
7889 return 0;
7892 case F_Layer:
7894 LayerType *layer = CURRENT;
7895 if (layername)
7897 int i;
7898 layer = NULL;
7899 for (i=0; i<max_copper_layer; i++)
7900 if (strcmp (PCB->Data->Layer[i].Name, layername) == 0)
7902 layer = & (PCB->Data->Layer[i]);
7903 break;
7905 if (layer == NULL)
7907 Message (_("No layer named %s\n"), layername);
7908 return 1;
7911 buf = (char *) malloc (strlen (layer->Name) + strlen ("Layer X Attributes"));
7912 sprintf (buf, "Layer %s Attributes", layer->Name);
7913 gui->edit_attributes(buf, &(layer->Attributes));
7914 free (buf);
7915 return 0;
7918 case F_Element:
7920 int n_found = 0;
7921 ElementType *e = NULL;
7922 ELEMENT_LOOP (PCB->Data);
7924 if (TEST_FLAG (SELECTEDFLAG, element))
7926 e = element;
7927 n_found ++;
7930 END_LOOP;
7931 if (n_found > 1)
7933 Message (_("Too many elements selected\n"));
7934 return 1;
7936 if (n_found == 0)
7938 void *ptrtmp;
7939 gui->get_coords (_("Click on an element"), &x, &y);
7940 if ((SearchScreen
7941 (x, y, ELEMENT_TYPE, &ptrtmp,
7942 &ptrtmp, &ptrtmp)) != NO_TYPE)
7943 e = (ElementType *) ptrtmp;
7944 else
7946 Message (_("No element found there\n"));
7947 return 1;
7951 if (NAMEONPCB_NAME(e))
7953 buf = (char *) malloc (strlen (NAMEONPCB_NAME(e)) + strlen ("Element X Attributes"));
7954 sprintf(buf, "Element %s Attributes", NAMEONPCB_NAME(e));
7956 else
7958 buf = strdup ("Unnamed Element Attributes");
7960 gui->edit_attributes(buf, &(e->Attributes));
7961 free (buf);
7962 break;
7965 default:
7966 AFAIL (attributes);
7969 return 0;
7972 /* --------------------------------------------------------------------------- */
7974 HID_Action action_action_list[] = {
7975 {"AddRats", 0, ActionAddRats,
7976 addrats_help, addrats_syntax}
7978 {"Attributes", 0, ActionAttributes,
7979 attributes_help, attributes_syntax}
7981 {"Atomic", 0, ActionAtomic,
7982 atomic_help, atomic_syntax}
7984 {"AutoPlaceSelected", 0, ActionAutoPlaceSelected,
7985 autoplace_help, autoplace_syntax}
7987 {"AutoRoute", 0, ActionAutoRoute,
7988 autoroute_help, autoroute_syntax}
7990 {"ChangeClearSize", 0, ActionChangeClearSize,
7991 changeclearsize_help, changeclearsize_syntax}
7993 {"ChangeDrillSize", 0, ActionChange2ndSize,
7994 changedrillsize_help, changedrillsize_syntax}
7996 {"ChangeHole", 0, ActionChangeHole,
7997 changehold_help, changehold_syntax}
7999 {"ChangeJoin", 0, ActionChangeJoin,
8000 changejoin_help, changejoin_syntax}
8002 {"ChangeName", 0, ActionChangeName,
8003 changename_help, changename_syntax}
8005 {"ChangePaste", 0, ActionChangePaste,
8006 changepaste_help, changepaste_syntax}
8008 {"ChangePinName", 0, ActionChangePinName,
8009 changepinname_help, changepinname_syntax}
8011 {"ChangeSize", 0, ActionChangeSize,
8012 changesize_help, changesize_syntax}
8014 {"ChangeSquare", 0, ActionChangeSquare,
8015 changesquare_help, changesquare_syntax}
8017 {"ChangeOctagon", 0, ActionChangeOctagon,
8018 changeoctagon_help, changeoctagon_syntax}
8020 {"ClearSquare", 0, ActionClearSquare,
8021 clearsquare_help, clearsquare_syntax}
8023 {"ClearOctagon", 0, ActionClearOctagon,
8024 clearoctagon_help, clearoctagon_syntax}
8026 {"Connection", 0, ActionConnection,
8027 connection_help, connection_syntax}
8029 {"Delete", 0, ActionDelete,
8030 delete_help, delete_syntax}
8032 {"DeleteRats", 0, ActionDeleteRats,
8033 deleterats_help, deleterats_syntax}
8035 {"DisperseElements", 0, ActionDisperseElements,
8036 disperseelements_help, disperseelements_syntax}
8038 {"Display", 0, ActionDisplay,
8039 display_help, display_syntax}
8041 {"DRC", 0, ActionDRCheck,
8042 drc_help, drc_syntax}
8044 {"DumpLibrary", 0, ActionDumpLibrary,
8045 dumplibrary_help, dumplibrary_syntax}
8047 {"ExecuteFile", 0, ActionExecuteFile,
8048 executefile_help, executefile_syntax}
8050 {"Flip", N_("Click on Object or Flip Point"), ActionFlip,
8051 flip_help, flip_syntax}
8053 {"LoadFrom", 0, ActionLoadFrom,
8054 loadfrom_help, loadfrom_syntax}
8056 {"MarkCrosshair", 0, ActionMarkCrosshair,
8057 markcrosshair_help, markcrosshair_syntax}
8059 {"Message", 0, ActionMessage,
8060 message_help, message_syntax}
8062 {"MinMaskGap", 0, ActionMinMaskGap,
8063 minmaskgap_help, minmaskgap_syntax}
8065 {"MinClearGap", 0, ActionMinClearGap,
8066 mincleargap_help, mincleargap_syntax}
8068 {"Mode", 0, ActionMode,
8069 mode_help, mode_syntax}
8071 {"MorphPolygon", 0, ActionMorphPolygon,
8072 morphpolygon_help, morphpolygon_syntax}
8074 {"PasteBuffer", 0, ActionPasteBuffer,
8075 pastebuffer_help, pastebuffer_syntax}
8077 {"Quit", 0, ActionQuit,
8078 quit_help, quit_syntax}
8080 {"RemoveSelected", 0, ActionRemoveSelected,
8081 removeselected_help, removeselected_syntax}
8083 {"Renumber", 0, ActionRenumber,
8084 renumber_help, renumber_syntax}
8086 {"RipUp", 0, ActionRipUp,
8087 ripup_help, ripup_syntax}
8089 {"Select", 0, ActionSelect,
8090 select_help, select_syntax}
8092 {"Unselect", 0, ActionUnselect,
8093 unselect_help, unselect_syntax}
8095 {"SaveSettings", 0, ActionSaveSettings,
8096 savesettings_help, savesettings_syntax}
8098 {"SaveTo", 0, ActionSaveTo,
8099 saveto_help, saveto_syntax}
8101 {"SetSquare", 0, ActionSetSquare,
8102 setsquare_help, setsquare_syntax}
8104 {"SetOctagon", 0, ActionSetOctagon,
8105 setoctagon_help, setoctagon_syntax}
8107 {"SetThermal", 0, ActionSetThermal,
8108 setthermal_help, setthermal_syntax}
8110 {"SetValue", 0, ActionSetValue,
8111 setvalue_help, setvalue_syntax}
8113 {"ToggleHideName", 0, ActionToggleHideName,
8114 togglehidename_help, togglehidename_syntax}
8116 {"Undo", 0, ActionUndo,
8117 undo_help, undo_syntax}
8119 {"Redo", 0, ActionRedo,
8120 redo_help, redo_syntax}
8122 {"SetSame", N_("Select item to use attributes from"), ActionSetSame,
8123 setsame_help, setsame_syntax}
8125 {"SetFlag", 0, ActionSetFlag,
8126 setflag_help, setflag_syntax}
8128 {"ClrFlag", 0, ActionClrFlag,
8129 clrflag_help, clrflag_syntax}
8131 {"ChangeFlag", 0, ActionChangeFlag,
8132 changeflag_help, changeflag_syntax}
8134 {"Polygon", 0, ActionPolygon,
8135 polygon_help, polygon_syntax}
8137 {"RouteStyle", 0, ActionRouteStyle,
8138 routestyle_help, routestyle_syntax}
8140 {"MoveObject", N_("Select an Object"), ActionMoveObject,
8141 moveobject_help, moveobject_syntax}
8143 {"MoveToCurrentLayer", 0, ActionMoveToCurrentLayer,
8144 movetocurrentlayer_help, movetocurrentlayer_syntax}
8146 {"New", 0, ActionNew,
8147 new_help, new_syntax}
8149 {"pscalib", 0, ActionPSCalib}
8151 {"ElementList", 0, ActionElementList,
8152 elementlist_help, elementlist_syntax}
8154 {"ElementSetAttr", 0, ActionElementSetAttr,
8155 elementsetattr_help, elementsetattr_syntax}
8157 {"ExecCommand", 0, ActionExecCommand,
8158 execcommand_help, execcommand_syntax}
8160 {"Import", 0, ActionImport,
8161 import_help, import_syntax}
8165 REGISTER_ACTIONS (action_action_list)