More stackup changes
[geda-pcb/pcjc2/v2.git] / src / action.c
blob69f38a1f1e889dda29ee1c3a55f9cd7f237f94e4
1 /*!
2 * \file src/action.c
4 * \brief Action routines for output window.
6 * <hr>
8 * <h1><b>Copyright.</b></h1>\n
10 * PCB, interactive printed circuit board design
12 * Copyright (C) 1994,1995,1996 Thomas Nau
14 * Copyright (C) 1997, 1998, 1999, 2000, 2001 Harry Eaton
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
30 * Contact addresses for paper mail and Email:
31 * Harry Eaton, 6697 Buttonhole Ct, Columbia, MD 21044, USA
32 * haceaton@aplcomm.jhuapl.edu
35 #ifdef HAVE_CONFIG_H
36 #include "config.h"
37 #endif
39 #include "global.h"
41 #include "action.h"
42 #include "autoplace.h"
43 #include "autoroute.h"
44 #include "buffer.h"
45 #include "change.h"
46 #include "copy.h"
47 #include "create.h"
48 #include "crosshair.h"
49 #include "data.h"
50 #include "draw.h"
51 #include "error.h"
52 #include "file.h"
53 #include "find.h"
54 #include "hid.h"
55 #include "insert.h"
56 #include "line.h"
57 #include "mymem.h"
58 #include "misc.h"
59 #include "mirror.h"
60 #include "move.h"
61 #include "polygon.h"
62 /*#include "print.h"*/
63 #include "rats.h"
64 #include "remove.h"
65 #include "report.h"
66 #include "rotate.h"
67 #include "rubberband.h"
68 #include "search.h"
69 #include "select.h"
70 #include "set.h"
71 #include "thermal.h"
72 #include "undo.h"
73 #include "rtree.h"
74 #include "macro.h"
75 #include "pcb-printf.h"
76 #include "netclass.h"
78 #include <assert.h>
79 #include <stdlib.h> /* rand() */
81 #ifdef HAVE_LIBDMALLOC
82 #include <dmalloc.h>
83 #endif
85 /* for fork() and friends */
86 #ifdef HAVE_UNISTD_H
87 #include <unistd.h>
88 #endif
90 #ifdef HAVE_SYS_WAIT_H
91 #include <sys/wait.h>
92 #endif
94 #define DEBUG
96 /* ---------------------------------------------------------------------------
97 * some local types
99 typedef enum
101 F_AddSelected,
102 F_All,
103 F_AllConnections,
104 F_AllRats,
105 F_AllUnusedPins,
106 F_Arc,
107 F_Arrow,
108 F_Block,
109 F_Description,
110 F_Cancel,
111 F_Center,
112 F_Clear,
113 F_ClearAndRedraw,
114 F_ClearList,
115 F_Close,
116 F_CreatePins,
117 F_CreateVias,
118 F_CreateHoles,
119 F_Found,
120 F_Connection,
121 F_Convert,
122 F_Copy,
123 F_CycleClip,
124 F_CycleCrosshair,
125 F_DeleteRats,
126 F_Drag,
127 F_DrillReport,
128 F_Element,
129 F_ElementByName,
130 F_ElementConnections,
131 F_ElementToBuffer,
132 F_Escape,
133 F_Find,
134 F_FlipElement,
135 F_FoundPins,
136 F_Grid,
137 F_InsertPoint,
138 F_Layer,
139 F_Layout,
140 F_LayoutAs,
141 F_LayoutToBuffer,
142 F_Line,
143 F_LineSize,
144 F_Lock,
145 F_Mirror,
146 F_Move,
147 F_NameOnPCB,
148 F_Netlist,
149 F_NetByName,
150 F_None,
151 F_Notify,
152 F_Object,
153 F_ObjectByName,
154 F_PasteBuffer,
155 F_PadByName,
156 F_PinByName,
157 F_PinOrPadName,
158 F_Pinout,
159 F_Polygon,
160 F_PolygonHole,
161 F_PreviousPoint,
162 F_RatsNest,
163 F_Rectangle,
164 F_Redraw,
165 F_Release,
166 F_Revert,
167 F_Remove,
168 F_RemoveSelected,
169 F_Report,
170 F_Reset,
171 F_ResetLinesAndPolygons,
172 F_ResetPinsViasAndPads,
173 F_Restore,
174 F_Rotate,
175 F_Save,
176 F_Selected,
177 F_SelectedArcs,
178 F_SelectedElements,
179 F_SelectedLines,
180 F_SelectedNames,
181 F_SelectedObjects,
182 F_SelectedPads,
183 F_SelectedPins,
184 F_SelectedTexts,
185 F_SelectedVias,
186 F_SelectedRats,
187 F_Stroke,
188 F_Text,
189 F_TextByName,
190 F_TextScale,
191 F_Thermal,
192 F_ToLayout,
193 F_ToggleAllDirections,
194 F_ToggleAutoDRC,
195 F_ToggleClearLine,
196 F_ToggleFullPoly,
197 F_ToggleGrid,
198 F_ToggleHideNames,
199 F_ToggleMask,
200 F_ToggleName,
201 F_ToggleObject,
202 F_ToggleShowDRC,
203 F_ToggleLiveRoute,
204 F_ToggleRubberBandMode,
205 F_ToggleStartDirection,
206 F_ToggleSnapPin,
207 F_ToggleThindraw,
208 F_ToggleLockNames,
209 F_ToggleOnlyNames,
210 F_ToggleThindrawPoly,
211 F_ToggleOrthoMove,
212 F_ToggleLocalRef,
213 F_ToggleCheckPlanes,
214 F_ToggleUniqueNames,
215 F_Via,
216 F_ViaByName,
217 F_Value,
218 F_ViaDrillingHole,
219 F_ViaSize,
220 F_Zoom,
221 F_ThroughHole,
222 F_BuriedVias,
223 F_ToggleAutoBuriedVias
225 FunctionID;
227 typedef struct /* used to identify subfunctions */
229 char *Identifier;
230 FunctionID ID;
232 FunctionType;
234 /* --------------------------------------------------------------------------- */
236 /* %start-doc actions 00delta
238 Many actions take a @code{delta} parameter as the last parameter,
239 which is an amount to change something. That @code{delta} may include
240 units, as an additional parameter, such as @code{Action(Object,5,mm)}.
241 If no units are specified, the default is PCB's native units
242 (currently 1/100 mil). Also, if the delta is prefixed by @code{+} or
243 @code{-}, the size is increased or decreased by that amount.
244 Otherwise, the size size is set to the given amount.
246 @example
247 Action(Object,5,mil)
248 Action(Object,+0.5,mm)
249 Action(Object,-1)
250 @end example
252 Actions which take a @code{delta} parameter which do not accept all
253 these options will specify what they do take.
255 %end-doc */
257 /* %start-doc actions 00objects
259 Many actions act on indicated objects on the board. They will have
260 parameters like @code{ToggleObject} or @code{SelectedVias} to indicate
261 what group of objects they act on. Unless otherwise specified, these
262 parameters are defined as follows:
264 @table @code
266 @item Object
267 @itemx ToggleObject
268 Affects the object under the mouse pointer. If this action is invoked
269 from a menu or script, the user will be prompted to click on an
270 object, which is then the object affected.
272 @item Selected
273 @itemx SelectedObjects
275 Affects all objects which are currently selected. At least, all
276 selected objects for which the given action makes sense.
278 @item SelectedPins
279 @itemx SelectedVias
280 @itemx Selected@var{Type}
281 @itemx @i{etc}
282 Affects all objects which are both selected and of the @var{Type} specified.
284 @end table
286 %end-doc */
288 /* %start-doc actions 00macros
290 @macro pinshapes
292 Pins, pads, and vias can have various shapes. All may be round. Pins
293 and pads may be square (obviously "square" pads are usually
294 rectangular). Pins and vias may be octagonal. When you change a
295 shape flag of an element, you actually change all of its pins and
296 pads.
298 Note that the square flag takes precedence over the octagon flag,
299 thus, if both the square and octagon flags are set, the object is
300 square. When the square flag is cleared, the pins and pads will be
301 either round or, if the octagon flag is set, octagonal.
303 @end macro
305 %end-doc */
307 /* ---------------------------------------------------------------------------
308 * some local identifiers
310 static PointType InsertedPoint;
311 static LayerType *lastLayer;
312 static struct
314 PolygonType *poly;
315 LineType line;
317 fake;
319 static struct
321 Coord X, Y;
322 Cardinal Buffer;
323 bool Click;
324 bool Moving; /* selected type clicked on */
325 int Hit; /* move type clicked on */
326 void *ptr1;
327 void *ptr2;
328 void *ptr3;
330 Note;
332 static int defer_updates = 0;
333 static int defer_needs_update = 0;
335 static Cardinal polyIndex = 0;
336 static bool saved_mode = false;
337 #ifdef HAVE_LIBSTROKE
338 static bool mid_stroke = false;
339 static BoxType StrokeBox;
340 #endif
341 static FunctionType Functions[] = {
342 {"AddSelected", F_AddSelected},
343 {"All", F_All},
344 {"AllConnections", F_AllConnections},
345 {"AllRats", F_AllRats},
346 {"AllUnusedPins", F_AllUnusedPins},
347 {"Arc", F_Arc},
348 {"Arrow", F_Arrow},
349 {"Block", F_Block},
350 {"Description", F_Description},
351 {"Cancel", F_Cancel},
352 {"Center", F_Center},
353 {"Clear", F_Clear},
354 {"ClearAndRedraw", F_ClearAndRedraw},
355 {"ClearList", F_ClearList},
356 {"Close", F_Close},
357 {"CreatePins", F_CreatePins},
358 {"CreateVias", F_CreateVias},
359 {"CreateHoles", F_CreateHoles},
360 {"Found", F_Found},
361 {"Connection", F_Connection},
362 {"Convert", F_Convert},
363 {"Copy", F_Copy},
364 {"CycleClip", F_CycleClip},
365 {"CycleCrosshair", F_CycleCrosshair},
366 {"DeleteRats", F_DeleteRats},
367 {"Drag", F_Drag},
368 {"DrillReport", F_DrillReport},
369 {"Element", F_Element},
370 {"ElementByName", F_ElementByName},
371 {"ElementConnections", F_ElementConnections},
372 {"ElementToBuffer", F_ElementToBuffer},
373 {"Escape", F_Escape},
374 {"Find", F_Find},
375 {"FlipElement", F_FlipElement},
376 {"FoundPins", F_FoundPins},
377 {"Grid", F_Grid},
378 {"InsertPoint", F_InsertPoint},
379 {"Layer", F_Layer},
380 {"Layout", F_Layout},
381 {"LayoutAs", F_LayoutAs},
382 {"LayoutToBuffer", F_LayoutToBuffer},
383 {"Line", F_Line},
384 {"LineSize", F_LineSize},
385 {"Lock", F_Lock},
386 {"Mirror", F_Mirror},
387 {"Move", F_Move},
388 {"NameOnPCB", F_NameOnPCB},
389 {"Netlist", F_Netlist},
390 {"NetByName", F_NetByName},
391 {"None", F_None},
392 {"Notify", F_Notify},
393 {"Object", F_Object},
394 {"ObjectByName", F_ObjectByName},
395 {"PasteBuffer", F_PasteBuffer},
396 {"PadByName", F_PadByName},
397 {"PinByName", F_PinByName},
398 {"PinOrPadName", F_PinOrPadName},
399 {"Pinout", F_Pinout},
400 {"Polygon", F_Polygon},
401 {"PolygonHole", F_PolygonHole},
402 {"PreviousPoint", F_PreviousPoint},
403 {"RatsNest", F_RatsNest},
404 {"Rectangle", F_Rectangle},
405 {"Redraw", F_Redraw},
406 {"Release", F_Release},
407 {"Remove", F_Remove},
408 {"RemoveSelected", F_RemoveSelected},
409 {"Report", F_Report},
410 {"Reset", F_Reset},
411 {"ResetLinesAndPolygons", F_ResetLinesAndPolygons},
412 {"ResetPinsViasAndPads", F_ResetPinsViasAndPads},
413 {"Restore", F_Restore},
414 {"Revert", F_Revert},
415 {"Rotate", F_Rotate},
416 {"Save", F_Save},
417 {"Selected", F_Selected},
418 {"SelectedArcs", F_SelectedArcs},
419 {"SelectedElements", F_SelectedElements},
420 {"SelectedLines", F_SelectedLines},
421 {"SelectedNames", F_SelectedNames},
422 {"SelectedObjects", F_SelectedObjects},
423 {"SelectedPins", F_SelectedPins},
424 {"SelectedPads", F_SelectedPads},
425 {"SelectedRats", F_SelectedRats},
426 {"SelectedTexts", F_SelectedTexts},
427 {"SelectedVias", F_SelectedVias},
428 {"Stroke", F_Stroke},
429 {"Text", F_Text},
430 {"TextByName", F_TextByName},
431 {"TextScale", F_TextScale},
432 {"Thermal", F_Thermal},
433 {"ToLayout", F_ToLayout},
434 {"Toggle45Degree", F_ToggleAllDirections},
435 {"ToggleClearLine", F_ToggleClearLine},
436 {"ToggleFullPoly", F_ToggleFullPoly},
437 {"ToggleGrid", F_ToggleGrid},
438 {"ToggleMask", F_ToggleMask},
439 {"ToggleName", F_ToggleName},
440 {"ToggleObject", F_ToggleObject},
441 {"ToggleRubberBandMode", F_ToggleRubberBandMode},
442 {"ToggleStartDirection", F_ToggleStartDirection},
443 {"ToggleSnapPin", F_ToggleSnapPin},
444 {"ToggleThindraw", F_ToggleThindraw},
445 {"ToggleThindrawPoly", F_ToggleThindrawPoly},
446 {"ToggleLockNames", F_ToggleLockNames},
447 {"ToggleOnlyNames", F_ToggleOnlyNames},
448 {"ToggleHideNames", F_ToggleHideNames},
449 {"ToggleCheckPlanes", F_ToggleCheckPlanes},
450 {"ToggleLocalRef", F_ToggleLocalRef},
451 {"ToggleOrthoMove", F_ToggleOrthoMove},
452 {"ToggleShowDRC", F_ToggleShowDRC},
453 {"ToggleLiveRoute", F_ToggleLiveRoute},
454 {"ToggleAutoDRC", F_ToggleAutoDRC},
455 {"ToggleUniqueNames", F_ToggleUniqueNames},
456 {"Value", F_Value},
457 {"Via", F_Via},
458 {"ViaByName", F_ViaByName},
459 {"ViaSize", F_ViaSize},
460 {"ViaDrillingHole", F_ViaDrillingHole},
461 {"Zoom", F_Zoom},
462 {"ThroughHole", F_ThroughHole},
463 {"TH", F_ThroughHole},
464 {"BuriedVias", F_BuriedVias},
465 {"ToggleAutoBuriedVias", F_ToggleAutoBuriedVias}
468 /* ---------------------------------------------------------------------------
469 * some local routines
471 static int GetFunctionID (String);
472 static void AdjustAttachedBox (void);
473 static void NotifyLine (void);
474 static void NotifyBlock (void);
475 static void NotifyMode (void);
476 static void ClearWarnings (void);
477 #ifdef HAVE_LIBSTROKE
478 static void FinishStroke (void);
479 extern void stroke_init (void);
480 extern void stroke_record (int x, int y);
481 extern int stroke_trans (char *s);
482 #endif
483 static void ChangeFlag (char *, char *, int, char *);
485 #define ARG(n) (argc > (n) ? argv[n] : NULL)
487 #ifdef HAVE_LIBSTROKE
490 * \brief Try to recognize the stroke sent.
492 void
493 FinishStroke (void)
495 char msg[255];
496 int type;
497 unsigned long num;
498 void *ptr1, *ptr2, *ptr3;
500 mid_stroke = false;
501 if (stroke_trans (msg))
503 num = atoi (msg);
504 switch (num)
506 case 456:
507 if (Settings.Mode == LINE_MODE)
509 SetMode (LINE_MODE);
511 break;
512 case 9874123:
513 case 74123:
514 case 987412:
515 case 8741236:
516 case 874123:
517 RotateScreenObject (StrokeBox.X1, StrokeBox.Y1, SWAP_IDENT ? 1 : 3);
518 break;
519 case 7896321:
520 case 786321:
521 case 789632:
522 case 896321:
523 RotateScreenObject (StrokeBox.X1, StrokeBox.Y1, SWAP_IDENT ? 3 : 1);
524 break;
525 case 258:
526 SetMode (LINE_MODE);
527 break;
528 case 852:
529 SetMode (ARROW_MODE);
530 break;
531 case 1478963:
532 ActionUndo ("");
533 break;
534 case 147423:
535 case 147523:
536 case 1474123:
537 Redo (true);
538 break;
539 case 148963:
540 case 147863:
541 case 147853:
542 case 145863:
543 SetMode (VIA_MODE);
544 break;
545 case 951:
546 case 9651:
547 case 9521:
548 case 9621:
549 case 9851:
550 case 9541:
551 case 96521:
552 case 96541:
553 case 98541:
554 /* XXX: FIXME: Call a zoom-extents action */
555 break;
556 case 159:
557 case 1269:
558 case 1259:
559 case 1459:
560 case 1569:
561 case 1589:
562 case 12569:
563 case 12589:
564 case 14589:
565 /* XXX: FIXME: Zoom to fit the box StrokeBox.[X1,Y1] - StrokeBox.[X2,Y2] */
566 break;
568 default:
569 Message (_("Unknown stroke %s\n"), msg);
570 break;
573 else
574 gui->beep ();
576 #endif
579 * \brief Clear warning color from pins/pads.
581 static void
582 ClearWarnings ()
584 Settings.RatWarn = false;
585 ALLPIN_LOOP (PCB->Data);
587 if (TEST_FLAG (WARNFLAG, pin))
589 CLEAR_FLAG (WARNFLAG, pin);
590 DrawPin (pin);
593 ENDALL_LOOP;
594 ALLPAD_LOOP (PCB->Data);
596 if (TEST_FLAG (WARNFLAG, pad))
598 CLEAR_FLAG (WARNFLAG, pad);
599 DrawPad (pad);
602 ENDALL_LOOP;
603 Draw ();
607 * \brief Click callback.
609 * This is called a clicktime after a mouse down, to we can distinguish
610 * between short clicks (typically: select or create something) and long
611 * clicks. Long clicks typically drag something.
613 static void
614 click_cb (hidval hv)
616 if (Note.Click)
618 notify_crosshair_change (false);
619 Note.Click = false;
620 if (Note.Moving && !gui->shift_is_pressed ())
622 Note.Buffer = Settings.BufferNumber;
623 SetBufferNumber (MAX_BUFFER - 1);
624 ClearBuffer (PASTEBUFFER);
625 AddSelectedToBuffer (PASTEBUFFER, Note.X, Note.Y, true);
626 SaveUndoSerialNumber ();
627 RemoveSelected ();
628 SaveMode ();
629 saved_mode = true;
630 SetMode (PASTEBUFFER_MODE);
632 else if (Note.Hit && !gui->shift_is_pressed ())
634 SaveMode ();
635 saved_mode = true;
636 SetMode (gui->control_is_pressed ()? COPY_MODE : MOVE_MODE);
637 Crosshair.AttachedObject.Ptr1 = Note.ptr1;
638 Crosshair.AttachedObject.Ptr2 = Note.ptr2;
639 Crosshair.AttachedObject.Ptr3 = Note.ptr3;
640 Crosshair.AttachedObject.Type = Note.Hit;
641 AttachForCopy (Note.X, Note.Y);
643 else
645 BoxType box;
647 Note.Hit = 0;
648 Note.Moving = false;
649 SaveUndoSerialNumber ();
650 box.X1 = -MAX_COORD;
651 box.Y1 = -MAX_COORD;
652 box.X2 = MAX_COORD;
653 box.Y2 = MAX_COORD;
654 /* unselect first if shift key not down */
655 if (!gui->shift_is_pressed () && SelectBlock (&box, false))
656 SetChangedFlag (true);
657 NotifyBlock ();
658 Crosshair.AttachedBox.Point1.X = Note.X;
659 Crosshair.AttachedBox.Point1.Y = Note.Y;
661 notify_crosshair_change (true);
666 * \brief This is typically called when the mouse has moved or the mouse
667 * button was released.
669 static void
670 ReleaseMode (void)
672 BoxType box;
674 if (Note.Click)
676 BoxType box;
678 box.X1 = -MAX_COORD;
679 box.Y1 = -MAX_COORD;
680 box.X2 = MAX_COORD;
681 box.Y2 = MAX_COORD;
683 Note.Click = false; /* inhibit timer action */
684 SaveUndoSerialNumber ();
685 /* unselect first if shift key not down */
686 if (!gui->shift_is_pressed ())
688 if (SelectBlock (&box, false))
689 SetChangedFlag (true);
690 if (Note.Moving)
692 Note.Moving = 0;
693 Note.Hit = 0;
694 return;
697 /* Restore the SN so that if we select something the deselect/select combo
698 gets the same SN. */
699 RestoreUndoSerialNumber();
700 if (SelectObject ())
701 SetChangedFlag (true);
702 else
703 /* We didn't select anything new, so, the deselection should get its
704 own SN. */
705 IncrementUndoSerialNumber();
706 Note.Hit = 0;
707 Note.Moving = 0;
709 else if (Note.Moving)
711 RestoreUndoSerialNumber ();
712 NotifyMode ();
713 ClearBuffer (PASTEBUFFER);
714 SetBufferNumber (Note.Buffer);
715 Note.Moving = false;
716 Note.Hit = 0;
718 else if (Note.Hit)
720 NotifyMode ();
721 Note.Hit = 0;
723 else if (Settings.Mode == ARROW_MODE)
725 box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
726 Crosshair.AttachedBox.Point2.X);
727 box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
728 Crosshair.AttachedBox.Point2.Y);
729 box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
730 Crosshair.AttachedBox.Point2.X);
731 box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
732 Crosshair.AttachedBox.Point2.Y);
733 RestoreUndoSerialNumber ();
734 if (SelectBlock (&box, true))
735 SetChangedFlag (true);
736 else if (Bumped)
737 IncrementUndoSerialNumber ();
738 Crosshair.AttachedBox.State = STATE_FIRST;
740 if (saved_mode)
741 RestoreMode ();
742 saved_mode = false;
745 #define HSIZE 257
746 static char function_hash[HSIZE];
747 static int hash_initted = 0;
749 static int
750 hashfunc(String s)
752 int i = 0;
753 while (*s)
755 i ^= i >> 16;
756 i = (i * 13) ^ (unsigned char)tolower((int) *s);
757 s ++;
759 i = (unsigned int)i % HSIZE;
760 return i;
764 * \brief Get function ID of passed string.
766 static int
767 GetFunctionID (String Ident)
769 int i, h;
771 if (Ident == 0)
772 return -1;
774 if (!hash_initted)
776 hash_initted = 1;
777 if (HSIZE < ENTRIES (Functions) * 2)
779 fprintf(stderr, _("Error: function hash size too small (%d vs %lu at %s:%d)\n"),
780 HSIZE, (unsigned long) ENTRIES (Functions)*2, __FILE__, __LINE__);
781 exit(1);
783 if (ENTRIES (Functions) > 254)
785 /* Change 'char' to 'int' and remove this when we get to 256
786 strings to hash. */
787 fprintf(stderr, _("Error: function hash type too small (%d vs %lu at %s:%d)\n"),
788 256, (unsigned long) ENTRIES (Functions), __FILE__, __LINE__);
789 exit(1);
792 for (i=ENTRIES (Functions)-1; i>=0; i--)
794 h = hashfunc (Functions[i].Identifier);
795 while (function_hash[h])
796 h = (h + 1) % HSIZE;
797 function_hash[h] = i + 1;
801 i = hashfunc (Ident);
802 while (1)
804 /* We enforce the "hash table bigger than function table" rule,
805 so we know there will be at least one zero entry to find. */
806 if (!function_hash[i])
807 return (-1);
808 if (!strcasecmp (Ident, Functions[function_hash[i]-1].Identifier))
809 return ((int) Functions[function_hash[i]-1].ID);
810 i = (i + 1) % HSIZE;
815 * \brief Set new coordinates if in 'RECTANGLE' mode.
817 * The cursor shape is also adjusted.
819 static void
820 AdjustAttachedBox (void)
822 if (Settings.Mode == ARC_MODE)
824 Crosshair.AttachedBox.otherway = gui->shift_is_pressed ();
825 return;
827 switch (Crosshair.AttachedBox.State)
829 case STATE_SECOND: /* one corner is selected */
831 /* update coordinates */
832 Crosshair.AttachedBox.Point2.X = Crosshair.X;
833 Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
834 break;
840 * \brief Adjusts the objects which are to be created like attached
841 * lines.
843 void
844 AdjustAttachedObjects (void)
846 PointType *pnt;
847 switch (Settings.Mode)
849 /* update at least an attached block (selection) */
850 case NO_MODE:
851 case ARROW_MODE:
852 if (Crosshair.AttachedBox.State)
854 Crosshair.AttachedBox.Point2.X = Crosshair.X;
855 Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
857 break;
859 /* rectangle creation mode */
860 case RECTANGLE_MODE:
861 case ARC_MODE:
862 AdjustAttachedBox ();
863 break;
865 /* polygon creation mode */
866 case POLYGON_MODE:
867 case POLYGONHOLE_MODE:
868 AdjustAttachedLine ();
869 break;
870 /* line creation mode */
871 case LINE_MODE:
872 if (PCB->RatDraw || PCB->Clipping == 0)
873 AdjustAttachedLine ();
874 else
875 AdjustTwoLine (PCB->Clipping - 1);
876 break;
877 /* point insertion mode */
878 case INSERTPOINT_MODE:
879 pnt = AdjustInsertPoint ();
880 if (pnt)
881 InsertedPoint = *pnt;
882 break;
883 case ROTATE_MODE:
884 break;
889 * \brief Creates points of a line.
891 static void
892 NotifyLine (void)
894 int type = NO_TYPE;
895 void *ptr1, *ptr2, *ptr3;
897 if (!Marked.status || TEST_FLAG (LOCALREFFLAG, PCB))
898 SetLocalRef (Crosshair.X, Crosshair.Y, true);
899 switch (Crosshair.AttachedLine.State)
901 case STATE_FIRST: /* first point */
902 if (PCB->RatDraw && SearchScreen (Crosshair.X, Crosshair.Y,
903 PAD_TYPE | PIN_TYPE, &ptr1, &ptr1,
904 &ptr1) == NO_TYPE)
906 gui->beep ();
907 break;
909 if (Settings.Mode == LINE_MODE)
911 if (TEST_FLAG (AUTODRCFLAG, PCB))
913 type = SearchScreen (Crosshair.X, Crosshair.Y,
914 PIN_TYPE | PAD_TYPE | VIA_TYPE, &ptr1, &ptr2,
915 &ptr3);
916 LookupConnection (Crosshair.X, Crosshair.Y, true, 1, CONNECTEDFLAG, false, true);
917 LookupConnection (Crosshair.X, Crosshair.Y, true, 1, FOUNDFLAG, true, true);
918 /* XXX: What about undo serial number? */
920 /* XXX: NEED TO FIGURE OUT WHAT NET CLASS THIS IS, AND/OR STORE FOR USE WITH DRC */
921 // Crosshair.Netclass = get_netclass_at_xy (LAYER_ON_STACK(0), Crosshair.X, Crosshair.Y); /* XXX: Not sure about the layer! */
922 // PCB->Bloat = get_min_clearance_for_netclass (Crosshair.Netclass);
924 if (type == PIN_TYPE || type == VIA_TYPE)
926 Crosshair.AttachedLine.Point1.X =
927 Crosshair.AttachedLine.Point2.X = ((PinType *) ptr2)->X;
928 Crosshair.AttachedLine.Point1.Y =
929 Crosshair.AttachedLine.Point2.Y = ((PinType *) ptr2)->Y;
931 else if (type == PAD_TYPE)
933 PadType *pad = (PadType *) ptr2;
934 double d1 = Distance (Crosshair.X, Crosshair.Y, pad->Point1.X, pad->Point1.Y);
935 double d2 = Distance (Crosshair.X, Crosshair.Y, pad->Point2.X, pad->Point2.Y);
936 if (d2 < d1)
938 Crosshair.AttachedLine.Point1 =
939 Crosshair.AttachedLine.Point2 = pad->Point2;
941 else
943 Crosshair.AttachedLine.Point1 =
944 Crosshair.AttachedLine.Point2 = pad->Point1;
947 else
949 Crosshair.AttachedLine.Point1.X =
950 Crosshair.AttachedLine.Point2.X = Crosshair.X;
951 Crosshair.AttachedLine.Point1.Y =
952 Crosshair.AttachedLine.Point2.Y = Crosshair.Y;
954 Crosshair.AttachedLine.State = STATE_SECOND;
955 break;
957 case STATE_SECOND:
958 /* fall through to third state too */
959 lastLayer = CURRENT;
960 default: /* all following points */
961 Crosshair.AttachedLine.State = STATE_THIRD;
962 break;
967 * \brief Create first or second corner of a marked block.
969 static void
970 NotifyBlock (void)
972 notify_crosshair_change (false);
973 switch (Crosshair.AttachedBox.State)
975 case STATE_FIRST: /* setup first point */
976 Crosshair.AttachedBox.Point1.X =
977 Crosshair.AttachedBox.Point2.X = Crosshair.X;
978 Crosshair.AttachedBox.Point1.Y =
979 Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
980 Crosshair.AttachedBox.State = STATE_SECOND;
981 break;
983 case STATE_SECOND: /* setup second point */
984 Crosshair.AttachedBox.State = STATE_THIRD;
985 break;
987 notify_crosshair_change (true);
992 * \brief This is called after every mode change, like mouse button pressed,
993 * mouse button released, dragging something started or a different tool
994 * selected.
996 * It does what's appropriate for the current mode setting.
997 * This can also mean creation of an object at the current crosshair location.
999 * New created objects are added to the create undo list of course.
1001 static void
1002 NotifyMode (void)
1004 void *ptr1, *ptr2, *ptr3;
1005 int type;
1007 if (Settings.RatWarn)
1008 ClearWarnings ();
1009 switch (Settings.Mode)
1011 case ARROW_MODE:
1013 int test;
1014 hidval hv;
1016 Note.Click = true;
1017 /* do something after click time */
1018 gui->add_timer (click_cb, CLICK_TIME, hv);
1020 /* see if we clicked on something already selected
1021 * (Note.Moving) or clicked on a MOVE_TYPE
1022 * (Note.Hit)
1024 for (test = (SELECT_TYPES | MOVE_TYPES) & ~RATLINE_TYPE;
1025 test; test &= ~type)
1027 type = SearchScreen (Note.X, Note.Y, test, &ptr1, &ptr2, &ptr3);
1028 if (!Note.Hit && (type & MOVE_TYPES) &&
1029 !TEST_FLAG (LOCKFLAG, (PinType *) ptr2))
1031 Note.Hit = type;
1032 Note.ptr1 = ptr1;
1033 Note.ptr2 = ptr2;
1034 Note.ptr3 = ptr3;
1036 if (!Note.Moving && (type & SELECT_TYPES) &&
1037 TEST_FLAG (SELECTEDFLAG, (PinType *) ptr2))
1038 Note.Moving = true;
1039 if ((Note.Hit && Note.Moving) || type == NO_TYPE)
1040 break;
1042 break;
1045 case VIA_MODE:
1047 PinType *via;
1049 if (!PCB->ViaOn)
1051 Message (_("You must turn via visibility on before\n"
1052 "you can place vias\n"));
1053 break;
1055 if ((via = CreateNewVia (PCB->Data, Note.X, Note.Y,
1056 Settings.ViaThickness, 2 * Settings.Keepaway,
1057 0, Settings.ViaDrillingHole, NULL,
1058 NoFlags ())) != NULL)
1060 AddObjectToCreateUndoList (VIA_TYPE, via, via, via);
1061 if (gui->shift_is_pressed ())
1062 ChangeObjectThermal (VIA_TYPE, via, via, via, PCB->ThermStyle);
1063 IncrementUndoSerialNumber ();
1064 DrawVia (via);
1065 Draw ();
1067 break;
1070 case ARC_MODE:
1072 switch (Crosshair.AttachedBox.State)
1074 case STATE_FIRST:
1075 Crosshair.AttachedBox.Point1.X =
1076 Crosshair.AttachedBox.Point2.X = Note.X;
1077 Crosshair.AttachedBox.Point1.Y =
1078 Crosshair.AttachedBox.Point2.Y = Note.Y;
1079 Crosshair.AttachedBox.State = STATE_SECOND;
1080 break;
1082 case STATE_SECOND:
1083 case STATE_THIRD:
1085 ArcType *arc;
1086 Coord wx, wy;
1087 Angle sa, dir;
1089 wx = Note.X - Crosshair.AttachedBox.Point1.X;
1090 wy = Note.Y - Crosshair.AttachedBox.Point1.Y;
1091 if (XOR (Crosshair.AttachedBox.otherway, abs (wy) > abs (wx)))
1093 Crosshair.AttachedBox.Point2.X =
1094 Crosshair.AttachedBox.Point1.X + abs (wy) * SGNZ (wx);
1095 sa = (wx >= 0) ? 0 : 180;
1096 #ifdef ARC45
1097 if (abs (wy) / 2 >= abs (wx))
1098 dir = (SGNZ (wx) == SGNZ (wy)) ? 45 : -45;
1099 else
1100 #endif
1101 dir = (SGNZ (wx) == SGNZ (wy)) ? 90 : -90;
1103 else
1105 Crosshair.AttachedBox.Point2.Y =
1106 Crosshair.AttachedBox.Point1.Y + abs (wx) * SGNZ (wy);
1107 sa = (wy >= 0) ? -90 : 90;
1108 #ifdef ARC45
1109 if (abs (wx) / 2 >= abs (wy))
1110 dir = (SGNZ (wx) == SGNZ (wy)) ? -45 : 45;
1111 else
1112 #endif
1113 dir = (SGNZ (wx) == SGNZ (wy)) ? -90 : 90;
1114 wy = wx;
1116 if (abs (wy) > 0 && (arc = CreateNewArcOnLayer (CURRENT,
1117 Crosshair.
1118 AttachedBox.
1119 Point2.X,
1120 Crosshair.
1121 AttachedBox.
1122 Point2.Y,
1123 abs (wy),
1124 abs (wy),
1126 dir,
1127 Settings.
1128 LineThickness,
1129 2 * Settings.
1130 Keepaway,
1131 MakeFlags
1132 (TEST_FLAG
1133 (CLEARNEWFLAG,
1134 PCB) ?
1135 CLEARLINEFLAG :
1136 0))))
1138 BoxType *bx;
1140 bx = GetArcEnds (arc);
1141 Crosshair.AttachedBox.Point1.X =
1142 Crosshair.AttachedBox.Point2.X = bx->X2;
1143 Crosshair.AttachedBox.Point1.Y =
1144 Crosshair.AttachedBox.Point2.Y = bx->Y2;
1145 AddObjectToCreateUndoList (ARC_TYPE, CURRENT, arc, arc);
1146 IncrementUndoSerialNumber ();
1147 addedLines++;
1148 DrawArc (CURRENT, arc);
1149 Draw ();
1150 Crosshair.AttachedBox.State = STATE_THIRD;
1152 break;
1155 break;
1157 case LOCK_MODE:
1159 type = SearchScreen (Note.X, Note.Y, LOCK_TYPES, &ptr1, &ptr2, &ptr3);
1160 if (type == ELEMENT_TYPE)
1162 ElementType *element = (ElementType *) ptr2;
1164 TOGGLE_FLAG (LOCKFLAG, element);
1165 PIN_LOOP (element);
1167 TOGGLE_FLAG (LOCKFLAG, pin);
1168 CLEAR_FLAG (SELECTEDFLAG, pin);
1170 END_LOOP;
1171 PAD_LOOP (element);
1173 TOGGLE_FLAG (LOCKFLAG, pad);
1174 CLEAR_FLAG (SELECTEDFLAG, pad);
1176 END_LOOP;
1177 CLEAR_FLAG (SELECTEDFLAG, element);
1178 /* always re-draw it since I'm too lazy
1179 * to tell if a selected flag changed
1181 DrawElement (element);
1182 Draw ();
1183 SetChangedFlag (true);
1185 else if (type != NO_TYPE)
1187 TextType *thing = (TextType *) ptr3;
1188 TOGGLE_FLAG (LOCKFLAG, thing);
1189 if (TEST_FLAG (LOCKFLAG, thing)
1190 && TEST_FLAG (SELECTEDFLAG, thing))
1192 /* this is not un-doable since LOCK isn't */
1193 CLEAR_FLAG (SELECTEDFLAG, thing);
1194 DrawObject (type, ptr1, ptr2);
1195 Draw ();
1197 SetChangedFlag (true);
1199 break;
1201 case THERMAL_MODE:
1203 if (((type
1205 SearchScreen (Note.X, Note.Y, PIN_TYPES, &ptr1, &ptr2,
1206 &ptr3)) != NO_TYPE)
1207 && !TEST_FLAG (HOLEFLAG, (PinType *) ptr3))
1209 if (gui->shift_is_pressed ())
1211 int tstyle = GET_THERM (INDEXOFCURRENT, (PinType *) ptr3);
1212 tstyle++;
1213 if (tstyle > 5)
1214 tstyle = 1;
1215 ChangeObjectThermal (type, ptr1, ptr2, ptr3, tstyle);
1217 else if (GET_THERM (INDEXOFCURRENT, (PinType *) ptr3))
1218 ChangeObjectThermal (type, ptr1, ptr2, ptr3, 0);
1219 else
1220 ChangeObjectThermal (type, ptr1, ptr2, ptr3, PCB->ThermStyle);
1222 break;
1225 case LINE_MODE:
1226 /* do update of position */
1227 NotifyLine ();
1228 if (Crosshair.AttachedLine.State != STATE_THIRD)
1229 break;
1231 /* Remove anchor if clicking on start point;
1232 * this means we can't paint 0 length lines
1233 * which could be used for square SMD pads.
1234 * Instead use a very small delta, or change
1235 * the file after saving.
1237 if (Crosshair.X == Crosshair.AttachedLine.Point1.X
1238 && Crosshair.Y == Crosshair.AttachedLine.Point1.Y)
1240 SetMode (LINE_MODE);
1241 break;
1244 if (PCB->RatDraw)
1246 RatType *line;
1247 if ((line = AddNet ()))
1249 addedLines++;
1250 AddObjectToCreateUndoList (RATLINE_TYPE, line, line, line);
1251 IncrementUndoSerialNumber ();
1252 DrawRat (line);
1253 Crosshair.AttachedLine.Point1.X =
1254 Crosshair.AttachedLine.Point2.X;
1255 Crosshair.AttachedLine.Point1.Y =
1256 Crosshair.AttachedLine.Point2.Y;
1257 Draw ();
1259 break;
1261 else
1262 /* create line if both ends are determined && length != 0 */
1264 LineType *line;
1265 int line_flags = 0;
1267 if (TEST_FLAG (AUTODRCFLAG, PCB) && !TEST_SILK_LAYER (CURRENT))
1268 line_flags |= CONNECTEDFLAG | FOUNDFLAG;
1270 if (TEST_FLAG (CLEARNEWFLAG, PCB))
1271 line_flags |= CLEARLINEFLAG;
1273 if (PCB->Clipping
1274 && Crosshair.AttachedLine.Point1.X ==
1275 Crosshair.AttachedLine.Point2.X
1276 && Crosshair.AttachedLine.Point1.Y ==
1277 Crosshair.AttachedLine.Point2.Y
1278 && (Crosshair.AttachedLine.Point2.X != Note.X
1279 || Crosshair.AttachedLine.Point2.Y != Note.Y))
1281 /* We will only need to paint the second line segment.
1282 Since we only check for vias on the first segment,
1283 swap them so the non-empty segment is the first segment. */
1284 Crosshair.AttachedLine.Point2.X = Note.X;
1285 Crosshair.AttachedLine.Point2.Y = Note.Y;
1288 if ((Crosshair.AttachedLine.Point1.X !=
1289 Crosshair.AttachedLine.Point2.X
1290 || Crosshair.AttachedLine.Point1.Y !=
1291 Crosshair.AttachedLine.Point2.Y))
1293 PinType *via;
1294 Cardinal last_layer_num, current_layer_num;
1295 Cardinal layer_from, layer_to;
1297 if ((line =
1298 CreateDrawnLineOnLayer (CURRENT,
1299 Crosshair.AttachedLine.Point1.X,
1300 Crosshair.AttachedLine.Point1.Y,
1301 Crosshair.AttachedLine.Point2.X,
1302 Crosshair.AttachedLine.Point2.Y,
1303 Settings.LineThickness,
1304 2 * Settings.Keepaway,
1305 MakeFlags (line_flags))) != NULL)
1307 addedLines++;
1308 AddObjectToCreateUndoList (LINE_TYPE, CURRENT, line, line);
1309 DrawLine (CURRENT, line);
1311 /* place a via if vias are visible, the layer is
1312 in a new group since the last line and there
1313 isn't a pin already here */
1314 if (TEST_FLAG (AUTOBURIEDVIASFLAG, PCB))
1316 layer_from = GetLayerNumber (PCB->Data, lastLayer);
1317 layer_to = GetLayerNumber (PCB->Data, CURRENT);
1319 layer_from = MIN (last_layer_num, current_layer_num);
1320 layer_to = MAX (last_layer_num, current_layer_num);
1322 else
1324 layer_from = 0;
1325 layer_to = 0;
1328 if (PCB->ViaOn && GetLayerGroupNumberByPointer (CURRENT) !=
1329 GetLayerGroupNumberByPointer (lastLayer) &&
1330 SearchObjectByLocation (PIN_TYPES, &ptr1, &ptr2, &ptr3,
1331 Crosshair.AttachedLine.Point1.X,
1332 Crosshair.AttachedLine.Point1.Y,
1333 Settings.ViaThickness / 2) ==
1334 NO_TYPE
1335 && (via =
1336 CreateNewViaEx (PCB->Data,
1337 Crosshair.AttachedLine.Point1.X,
1338 Crosshair.AttachedLine.Point1.Y,
1339 Settings.ViaThickness,
1340 2 * Settings.Keepaway, 0,
1341 Settings.ViaDrillingHole, NULL,
1342 NoFlags (), layer_from, layer_to)) != NULL)
1344 AddObjectToCreateUndoList (VIA_TYPE, via, via, via);
1345 DrawVia (via);
1347 /* copy the coordinates */
1348 Crosshair.AttachedLine.Point1.X =
1349 Crosshair.AttachedLine.Point2.X;
1350 Crosshair.AttachedLine.Point1.Y =
1351 Crosshair.AttachedLine.Point2.Y;
1352 IncrementUndoSerialNumber ();
1353 lastLayer = CURRENT;
1355 if (PCB->Clipping && (Note.X != Crosshair.AttachedLine.Point2.X
1356 || Note.Y !=
1357 Crosshair.AttachedLine.Point2.Y))
1359 if ((line =
1360 CreateDrawnLineOnLayer (CURRENT,
1361 Crosshair.AttachedLine.Point2.X,
1362 Crosshair.AttachedLine.Point2.Y,
1363 Note.X, Note.Y,
1364 Settings.LineThickness,
1365 2 * Settings.Keepaway,
1366 MakeFlags (line_flags))) != NULL)
1368 addedLines++;
1369 AddObjectToCreateUndoList (LINE_TYPE, CURRENT, line, line);
1370 IncrementUndoSerialNumber ();
1371 DrawLine (CURRENT, line);
1373 /* move to new start point */
1374 Crosshair.AttachedLine.Point1.X = Note.X;
1375 Crosshair.AttachedLine.Point1.Y = Note.Y;
1376 Crosshair.AttachedLine.Point2.X = Note.X;
1377 Crosshair.AttachedLine.Point2.Y = Note.Y;
1378 if (TEST_FLAG (SWAPSTARTDIRFLAG, PCB))
1380 PCB->Clipping ^= 3;
1383 if (TEST_FLAG (AUTODRCFLAG, PCB) && !TEST_SILK_LAYER (CURRENT))
1384 LookupConnection (Note.X, Note.Y, true, 1, CONNECTEDFLAG, false, true);
1385 Draw ();
1387 break;
1389 case RECTANGLE_MODE:
1390 /* do update of position */
1391 NotifyBlock ();
1393 /* create rectangle if both corners are determined
1394 * and width, height are != 0
1396 if (Crosshair.AttachedBox.State == STATE_THIRD &&
1397 Crosshair.AttachedBox.Point1.X != Crosshair.AttachedBox.Point2.X &&
1398 Crosshair.AttachedBox.Point1.Y != Crosshair.AttachedBox.Point2.Y)
1400 PolygonType *polygon;
1402 int flags = CLEARPOLYFLAG;
1403 if (TEST_FLAG (NEWFULLPOLYFLAG, PCB))
1404 flags |= FULLPOLYFLAG;
1405 if ((polygon = CreateNewPolygonFromRectangle (CURRENT,
1406 Crosshair.
1407 AttachedBox.Point1.X,
1408 Crosshair.
1409 AttachedBox.Point1.Y,
1410 Crosshair.
1411 AttachedBox.Point2.X,
1412 Crosshair.
1413 AttachedBox.Point2.Y,
1414 MakeFlags
1415 (flags))) !=
1416 NULL)
1418 AddObjectToCreateUndoList (POLYGON_TYPE, CURRENT,
1419 polygon, polygon);
1420 IncrementUndoSerialNumber ();
1421 DrawPolygon (CURRENT, polygon);
1422 Draw ();
1425 /* reset state to 'first corner' */
1426 Crosshair.AttachedBox.State = STATE_FIRST;
1428 break;
1430 case TEXT_MODE:
1432 char *string;
1434 if ((string = gui->prompt_for (_("Enter text:"), "")) != NULL)
1436 if (strlen(string) > 0)
1438 TextType *text;
1439 int flag = CLEARLINEFLAG;
1441 if (GetLayerGroupNumberByNumber (INDEXOFCURRENT) ==
1442 GetLayerGroupNumberBySide (BOTTOM_SIDE))
1443 flag |= ONSOLDERFLAG;
1444 if ((text = CreateNewText (CURRENT, &PCB->Font, Note.X,
1445 Note.Y, 0, Settings.TextScale,
1446 string, MakeFlags (flag))) != NULL)
1448 AddObjectToCreateUndoList (TEXT_TYPE, CURRENT, text, text);
1449 IncrementUndoSerialNumber ();
1450 DrawText (CURRENT, text);
1451 Draw ();
1454 free (string);
1456 break;
1459 case POLYGON_MODE:
1461 PointType *points = Crosshair.AttachedPolygon.Points;
1462 Cardinal n = Crosshair.AttachedPolygon.PointN;
1464 /* do update of position; use the 'LINE_MODE' mechanism */
1465 NotifyLine ();
1467 /* check if this is the last point of a polygon */
1468 if (n >= 3 &&
1469 points->X == Crosshair.AttachedLine.Point2.X &&
1470 points->Y == Crosshair.AttachedLine.Point2.Y)
1472 CopyAttachedPolygonToLayer ();
1473 Draw ();
1474 break;
1477 /* create new point if it's the first one or if it's
1478 * different to the last one
1480 if (!n ||
1481 points[n - 1].X != Crosshair.AttachedLine.Point2.X ||
1482 points[n - 1].Y != Crosshair.AttachedLine.Point2.Y)
1484 CreateNewPointInPolygon (&Crosshair.AttachedPolygon,
1485 Crosshair.AttachedLine.Point2.X,
1486 Crosshair.AttachedLine.Point2.Y,
1489 /* copy the coordinates */
1490 Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X;
1491 Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y;
1493 break;
1496 case POLYGONHOLE_MODE:
1498 switch (Crosshair.AttachedObject.State)
1500 /* first notify, lookup object */
1501 case STATE_FIRST:
1502 Crosshair.AttachedObject.Type =
1503 SearchScreen (Note.X, Note.Y, POLYGON_TYPE,
1504 &Crosshair.AttachedObject.Ptr1,
1505 &Crosshair.AttachedObject.Ptr2,
1506 &Crosshair.AttachedObject.Ptr3);
1508 if (Crosshair.AttachedObject.Type == NO_TYPE)
1510 Message (_("The first point of a polygon hole must be on a polygon.\n"));
1511 break; /* don't start doing anything if clicked outside of polys */
1514 if (TEST_FLAG(LOCKFLAG, (PolygonType *) Crosshair.AttachedObject.Ptr2))
1516 Message (_("Sorry, the object is locked\n"));
1517 Crosshair.AttachedObject.Type = NO_TYPE;
1518 break;
1520 else
1521 Crosshair.AttachedObject.State = STATE_SECOND;
1522 /* Fall thru: first click is also the first point of the
1523 * poly hole. */
1525 /* second notify, insert new point into object */
1526 case STATE_SECOND:
1528 PointType *points = Crosshair.AttachedPolygon.Points;
1529 Cardinal n = Crosshair.AttachedPolygon.PointN;
1530 POLYAREA *original, *new_hole, *result;
1531 FlagType Flags;
1533 /* do update of position; use the 'LINE_MODE' mechanism */
1534 NotifyLine ();
1536 /* check if this is the last point of a polygon */
1537 if (n >= 3 &&
1538 points->X == Crosshair.AttachedLine.Point2.X &&
1539 points->Y == Crosshair.AttachedLine.Point2.Y)
1541 /* Create POLYAREAs from the original polygon
1542 * and the new hole polygon */
1543 original = PolygonToPoly ((PolygonType *)Crosshair.AttachedObject.Ptr2);
1544 new_hole = PolygonToPoly (&Crosshair.AttachedPolygon);
1546 /* Subtract the hole from the original polygon shape */
1547 poly_Boolean_free (original, new_hole, &result, PBO_SUB);
1549 /* Convert the resulting polygon(s) into a new set of nodes
1550 * and place them on the page. Delete the original polygon.
1552 SaveUndoSerialNumber ();
1553 Flags = ((PolygonType *)Crosshair.AttachedObject.Ptr2)->Flags;
1554 PolyToPolygonsOnLayer (PCB->Data, (LayerType *)Crosshair.AttachedObject.Ptr1,
1555 result, Flags);
1556 RemoveObject (POLYGON_TYPE,
1557 Crosshair.AttachedObject.Ptr1,
1558 Crosshair.AttachedObject.Ptr2,
1559 Crosshair.AttachedObject.Ptr3);
1560 RestoreUndoSerialNumber ();
1561 IncrementUndoSerialNumber ();
1562 Draw ();
1564 /* reset state of attached line */
1565 memset (&Crosshair.AttachedPolygon, 0, sizeof (PolygonType));
1566 Crosshair.AttachedObject.State = STATE_FIRST;
1567 addedLines = 0;
1569 break;
1572 /* create new point if it's the first one or if it's
1573 * different to the last one
1575 if (!n ||
1576 points[n - 1].X != Crosshair.AttachedLine.Point2.X ||
1577 points[n - 1].Y != Crosshair.AttachedLine.Point2.Y)
1579 CreateNewPointInPolygon (&Crosshair.AttachedPolygon,
1580 Crosshair.AttachedLine.Point2.X,
1581 Crosshair.AttachedLine.Point2.Y,
1584 /* copy the coordinates */
1585 Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X;
1586 Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y;
1588 break;
1592 break;
1595 case PASTEBUFFER_MODE:
1597 TextType estr[MAX_ELEMENTNAMES];
1598 ElementType *e = 0;
1600 if (gui->shift_is_pressed ())
1602 int type =
1603 SearchScreen (Note.X, Note.Y, ELEMENT_TYPE, &ptr1, &ptr2,
1604 &ptr3);
1605 if (type == ELEMENT_TYPE)
1607 e = (ElementType *) ptr1;
1608 if (e)
1610 int i;
1612 memcpy (estr, e->Name,
1613 MAX_ELEMENTNAMES * sizeof (TextType));
1614 for (i = 0; i < MAX_ELEMENTNAMES; ++i)
1615 estr[i].TextString = estr[i].TextString ? strdup(estr[i].TextString) : NULL;
1616 RemoveElement (e);
1620 if (CopyPastebufferToLayout (Note.X, Note.Y))
1621 SetChangedFlag (true);
1622 if (e)
1624 int type =
1625 SearchScreen (Note.X, Note.Y, ELEMENT_TYPE, &ptr1, &ptr2,
1626 &ptr3);
1627 if (type == ELEMENT_TYPE && ptr1)
1629 int i, save_n;
1630 e = (ElementType *) ptr1;
1632 save_n = NAME_INDEX (PCB);
1634 for (i = 0; i < MAX_ELEMENTNAMES; i++)
1636 if (i == save_n)
1637 EraseElementName (e);
1638 r_delete_entry (PCB->Data->name_tree[i],
1639 (BoxType *) & (e->Name[i]));
1640 memcpy (&(e->Name[i]), &(estr[i]), sizeof (TextType));
1641 e->Name[i].Element = e;
1642 SetTextBoundingBox (&PCB->Font, &(e->Name[i]));
1643 r_insert_entry (PCB->Data->name_tree[i],
1644 (BoxType *) & (e->Name[i]), 0);
1645 if (i == save_n)
1646 DrawElementName (e);
1650 break;
1653 case REMOVE_MODE:
1654 if ((type =
1655 SearchScreen (Note.X, Note.Y, REMOVE_TYPES, &ptr1, &ptr2,
1656 &ptr3)) != NO_TYPE)
1658 if (TEST_FLAG (LOCKFLAG, (LineType *) ptr2))
1660 Message (_("Sorry, the object is locked\n"));
1661 break;
1663 if (type == ELEMENT_TYPE)
1665 RubberbandType *ptr;
1666 int i;
1668 Crosshair.AttachedObject.RubberbandN = 0;
1669 LookupRatLines (type, ptr1, ptr2, ptr3);
1670 ptr = Crosshair.AttachedObject.Rubberband;
1671 for (i = 0; i < Crosshair.AttachedObject.RubberbandN; i++)
1673 if (PCB->RatOn)
1674 EraseRat ((RatType *) ptr->Line);
1675 if (TEST_FLAG (RUBBERENDFLAG, ptr->Line))
1676 MoveObjectToRemoveUndoList (RATLINE_TYPE,
1677 ptr->Line, ptr->Line,
1678 ptr->Line);
1679 else
1680 TOGGLE_FLAG (RUBBERENDFLAG, ptr->Line); /* only remove line once */
1681 ptr++;
1684 RemoveObject (type, ptr1, ptr2, ptr3);
1685 IncrementUndoSerialNumber ();
1686 SetChangedFlag (true);
1688 break;
1690 case ROTATE_MODE:
1691 RotateScreenObject (Note.X, Note.Y,
1692 gui->shift_is_pressed ()? (SWAP_IDENT ?
1693 1 : 3)
1694 : (SWAP_IDENT ? 3 : 1));
1695 break;
1697 /* both are almost the same */
1698 case COPY_MODE:
1699 case MOVE_MODE:
1700 switch (Crosshair.AttachedObject.State)
1702 /* first notify, lookup object */
1703 case STATE_FIRST:
1705 int types = (Settings.Mode == COPY_MODE) ?
1706 COPY_TYPES : MOVE_TYPES;
1708 Crosshair.AttachedObject.Type =
1709 SearchScreen (Note.X, Note.Y, types,
1710 &Crosshair.AttachedObject.Ptr1,
1711 &Crosshair.AttachedObject.Ptr2,
1712 &Crosshair.AttachedObject.Ptr3);
1713 if (Crosshair.AttachedObject.Type != NO_TYPE)
1715 if (Settings.Mode == MOVE_MODE &&
1716 TEST_FLAG (LOCKFLAG, (PinType *)
1717 Crosshair.AttachedObject.Ptr2))
1719 Message (_("Sorry, the object is locked\n"));
1720 Crosshair.AttachedObject.Type = NO_TYPE;
1722 else
1723 AttachForCopy (Note.X, Note.Y);
1725 break;
1728 /* second notify, move or copy object */
1729 case STATE_SECOND:
1730 if (Settings.Mode == COPY_MODE)
1731 CopyObject (Crosshair.AttachedObject.Type,
1732 Crosshair.AttachedObject.Ptr1,
1733 Crosshair.AttachedObject.Ptr2,
1734 Crosshair.AttachedObject.Ptr3,
1735 Note.X - Crosshair.AttachedObject.X,
1736 Note.Y - Crosshair.AttachedObject.Y);
1737 else
1739 MoveObjectAndRubberband (Crosshair.AttachedObject.Type,
1740 Crosshair.AttachedObject.Ptr1,
1741 Crosshair.AttachedObject.Ptr2,
1742 Crosshair.AttachedObject.Ptr3,
1743 Note.X - Crosshair.AttachedObject.X,
1744 Note.Y - Crosshair.AttachedObject.Y);
1745 SetLocalRef (0, 0, false);
1747 SetChangedFlag (true);
1749 /* reset identifiers */
1750 Crosshair.AttachedObject.Type = NO_TYPE;
1751 Crosshair.AttachedObject.State = STATE_FIRST;
1752 break;
1754 break;
1756 /* insert a point into a polygon/line/... */
1757 case INSERTPOINT_MODE:
1758 switch (Crosshair.AttachedObject.State)
1760 /* first notify, lookup object */
1761 case STATE_FIRST:
1762 Crosshair.AttachedObject.Type =
1763 SearchScreen (Note.X, Note.Y, INSERT_TYPES,
1764 &Crosshair.AttachedObject.Ptr1,
1765 &Crosshair.AttachedObject.Ptr2,
1766 &Crosshair.AttachedObject.Ptr3);
1768 if (Crosshair.AttachedObject.Type != NO_TYPE)
1770 if (TEST_FLAG (LOCKFLAG, (PolygonType *)
1771 Crosshair.AttachedObject.Ptr2))
1773 Message (_("Sorry, the object is locked\n"));
1774 Crosshair.AttachedObject.Type = NO_TYPE;
1775 break;
1777 else
1779 /* get starting point of nearest segment */
1780 if (Crosshair.AttachedObject.Type == POLYGON_TYPE)
1782 fake.poly =
1783 (PolygonType *) Crosshair.AttachedObject.Ptr2;
1784 polyIndex =
1785 GetLowestDistancePolygonPoint (fake.poly, Note.X,
1786 Note.Y);
1787 fake.line.Point1 = fake.poly->Points[polyIndex];
1788 fake.line.Point2 = fake.poly->Points[
1789 prev_contour_point (fake.poly, polyIndex)];
1790 Crosshair.AttachedObject.Ptr2 = &fake.line;
1793 Crosshair.AttachedObject.State = STATE_SECOND;
1794 InsertedPoint = *AdjustInsertPoint ();
1797 break;
1799 /* second notify, insert new point into object */
1800 case STATE_SECOND:
1801 if (Crosshair.AttachedObject.Type == POLYGON_TYPE)
1802 InsertPointIntoObject (POLYGON_TYPE,
1803 Crosshair.AttachedObject.Ptr1, fake.poly,
1804 &polyIndex,
1805 InsertedPoint.X, InsertedPoint.Y, 0, false, false);
1806 else
1807 InsertPointIntoObject (Crosshair.AttachedObject.Type,
1808 Crosshair.AttachedObject.Ptr1,
1809 Crosshair.AttachedObject.Ptr2,
1810 &polyIndex,
1811 InsertedPoint.X, InsertedPoint.Y, 0, false, false);
1812 SetChangedFlag (true);
1814 /* reset identifiers */
1815 Crosshair.AttachedObject.Type = NO_TYPE;
1816 Crosshair.AttachedObject.State = STATE_FIRST;
1817 break;
1819 break;
1824 /* --------------------------------------------------------------------------- */
1826 static const char atomic_syntax[] = N_("Atomic(Save|Restore|Close|Block)");
1828 static const char atomic_help[] = N_("Save or restore the undo serial number.");
1830 /* %start-doc actions Atomic
1832 This action allows making multiple-action bindings into an atomic
1833 operation that will be undone by a single Undo command. For example,
1834 to optimize rat lines, you'd delete the rats and re-add them. To
1835 group these into a single undo, you'd want the deletions and the
1836 additions to have the same undo serial number. So, you @code{Save},
1837 delete the rats, @code{Restore}, add the rats - using the same serial
1838 number as the deletes, then @code{Block}, which checks to see if the
1839 deletions or additions actually did anything. If not, the serial
1840 number is set to the saved number, as there's nothing to undo. If
1841 something did happen, the serial number is incremented so that these
1842 actions are counted as a single undo step.
1844 @table @code
1846 @item Save
1847 Saves the undo serial number.
1849 @item Restore
1850 Returns it to the last saved number.
1852 @item Close
1853 Sets it to 1 greater than the last save.
1855 @item Block
1856 Does a Restore if there was nothing to undo, else does a Close.
1858 @end table
1860 %end-doc */
1862 static int
1863 ActionAtomic (int argc, char **argv, Coord x, Coord y)
1865 if (argc != 1)
1866 AFAIL (atomic);
1868 switch (GetFunctionID (argv[0]))
1870 case F_Save:
1871 SaveUndoSerialNumber ();
1872 break;
1873 case F_Restore:
1874 RestoreUndoSerialNumber ();
1875 break;
1876 case F_Close:
1877 RestoreUndoSerialNumber ();
1878 IncrementUndoSerialNumber ();
1879 break;
1880 case F_Block:
1881 RestoreUndoSerialNumber ();
1882 if (Bumped)
1883 IncrementUndoSerialNumber ();
1884 break;
1886 return 0;
1889 /* -------------------------------------------------------------------------- */
1891 static const char drc_syntax[] = N_("DRC()");
1893 static const char drc_help[] = N_("Invoke the DRC check.");
1895 /* %start-doc actions DRC
1897 Note that the design rule check uses the current board rule settings,
1898 not the current style settings.
1900 %end-doc */
1902 static int
1903 ActionDRCheck (int argc, char **argv, Coord x, Coord y)
1905 int count;
1907 if (gui->drc_gui == NULL || gui->drc_gui->log_drc_overview)
1909 Message (_("%m+Rules are minspace %$mS, minoverlap %$mS "
1910 "minwidth %$mS, minsilk %$mS\n"
1911 "min drill %$mS, min annular ring %$mS\n"),
1912 Settings.grid_unit->allow,
1913 PCB->Bloat, PCB->Shrink,
1914 PCB->minWid, PCB->minSlk,
1915 PCB->minDrill, PCB->minRing);
1917 count = DRCAll ();
1918 if (gui->drc_gui == NULL || gui->drc_gui->log_drc_overview)
1920 if (count == 0)
1921 Message (_("No DRC problems found.\n"));
1922 else if (count > 0)
1923 Message (_("Found %d design rule errors.\n"), count);
1924 else
1925 Message (_("Aborted DRC after %d design rule errors.\n"), -count);
1927 return 0;
1930 /* -------------------------------------------------------------------------- */
1932 static const char dumplibrary_syntax[] = N_("DumpLibrary()");
1934 static const char dumplibrary_help[] =
1935 N_("Display the entire contents of the libraries.");
1937 /* %start-doc actions DumpLibrary
1940 %end-doc */
1942 static int
1943 ActionDumpLibrary (int argc, char **argv, Coord x, Coord y)
1945 int i, j;
1947 printf ("**** Do not count on this format. It will change ****\n\n");
1948 printf ("MenuN = %d\n", (int) Library.MenuN);
1949 printf ("MenuMax = %d\n", (int) Library.MenuMax);
1950 for (i = 0; i < Library.MenuN; i++)
1952 printf ("Library #%d:\n", i);
1953 printf (" EntryN = %d\n", (int) Library.Menu[i].EntryN);
1954 printf (" EntryMax = %d\n", (int) Library.Menu[i].EntryMax);
1955 printf (" Name = \"%s\"\n", UNKNOWN (Library.Menu[i].Name));
1956 printf (" directory = \"%s\"\n",
1957 UNKNOWN (Library.Menu[i].directory));
1958 printf (" Style = \"%s\"\n", UNKNOWN (Library.Menu[i].Style));
1959 printf (" flag = %d\n", Library.Menu[i].flag);
1961 for (j = 0; j < Library.Menu[i].EntryN; j++)
1963 printf (" #%4d: ", j);
1964 if (Library.Menu[i].Entry[j].Template == (char *) -1)
1966 printf ("newlib: \"%s\"\n",
1967 UNKNOWN (Library.Menu[i].Entry[j].ListEntry));
1969 else
1971 printf ("\"%s\", \"%s\", \"%s\", \"%s\", \"%s\"\n",
1972 UNKNOWN (Library.Menu[i].Entry[j].ListEntry),
1973 UNKNOWN (Library.Menu[i].Entry[j].Template),
1974 UNKNOWN (Library.Menu[i].Entry[j].Package),
1975 UNKNOWN (Library.Menu[i].Entry[j].Value),
1976 UNKNOWN (Library.Menu[i].Entry[j].Description));
1981 return 0;
1984 /* -------------------------------------------------------------------------- */
1986 static const char flip_syntax[] = N_("Flip(Object|Selected|SelectedElements)");
1988 static const char flip_help[] =
1989 N_("Flip an element to the opposite side of the board.");
1991 /* %start-doc actions Flip
1993 Note that the location of the element will be symmetric about the
1994 cursor location; i.e. if the part you are pointing at will still be at
1995 the same spot once the element is on the other side. When flipping
1996 multiple elements, this retains their positions relative to each
1997 other, not their absolute positions on the board.
1999 %end-doc */
2001 static int
2002 ActionFlip (int argc, char **argv, Coord x, Coord y)
2004 char *function = ARG (0);
2005 ElementType *element;
2006 void *ptrtmp;
2007 int err = 0;
2009 if (function)
2011 switch (GetFunctionID (function))
2013 case F_Object:
2014 if ((SearchScreen (x, y, ELEMENT_TYPE,
2015 &ptrtmp, &ptrtmp, &ptrtmp)) != NO_TYPE)
2017 element = (ElementType *) ptrtmp;
2018 ChangeElementSide (element, 2 * Crosshair.Y - PCB->MaxHeight);
2019 IncrementUndoSerialNumber ();
2020 Draw ();
2022 break;
2023 case F_Selected:
2024 case F_SelectedElements:
2025 ChangeSelectedElementSide ();
2026 break;
2027 default:
2028 err = 1;
2029 break;
2031 if (!err)
2032 return 0;
2035 AFAIL (flip);
2038 /* -------------------------------------------------------------------------- */
2040 static const char message_syntax[] = N_("Message(message)");
2042 static const char message_help[] = N_("Writes a message to the log window.");
2044 /* %start-doc actions Message
2046 This action displays a message to the log window. This action is primarily
2047 provided for use by other programs which may interface with PCB. If
2048 multiple arguments are given, each one is sent to the log window
2049 followed by a newline.
2051 %end-doc */
2053 static int
2054 ActionMessage (int argc, char **argv, Coord x, Coord y)
2056 int i;
2058 if (argc < 1)
2059 AFAIL (message);
2061 for (i = 0; i < argc; i++)
2063 Message (argv[i]);
2064 Message ("\n");
2067 return 0;
2071 /* -------------------------------------------------------------------------- */
2073 static const char setthermal_syntax[] =
2074 "SetThermal(Object|SelectedPins|SelectedVias|Selected, Style)";
2076 static const char setthermal_help[] =
2077 N_("Set the thermal (on the current layer) of pins or vias to the given style.\n"
2078 "Style = 0 means no thermal.\n"
2079 "Style = 1 has diagonal fingers with sharp edges.\n"
2080 "Style = 2 has horizontal and vertical fingers with sharp edges.\n"
2081 "Style = 3 is a solid connection to the plane.\n"
2082 "Style = 4 has diagonal fingers with rounded edges.\n"
2083 "Style = 5 has horizontal and vertical fingers with rounded edges.\n");
2085 /* %start-doc actions SetThermal
2087 This changes how/whether pins or vias connect to any rectangle or polygon
2088 on the current layer. The first argument can specify one object, or all
2089 selected pins, or all selected vias, or all selected pins and vias.
2090 The second argument specifies the style of connection.
2091 There are 5 possibilities:
2092 0 - no connection,
2093 1 - 45 degree fingers with sharp edges,
2094 2 - horizontal & vertical fingers with sharp edges,
2095 3 - solid connection,
2096 4 - 45 degree fingers with rounded corners,
2097 5 - horizontal & vertical fingers with rounded corners.
2099 Pins and Vias may have thermals whether or not there is a polygon available
2100 to connect with. However, they will have no effect without the polygon.
2101 %end-doc */
2103 static int
2104 ActionSetThermal (int argc, char **argv, Coord x, Coord y)
2106 char *function = ARG (0);
2107 char *style = ARG (1);
2108 void *ptr1, *ptr2, *ptr3;
2109 int type, kind;
2110 int err = 0;
2112 if (function && *function)
2114 bool absolute;
2116 if ( ! style || ! *style)
2118 kind = PCB->ThermStyle;
2119 absolute = true;
2121 else
2122 kind = GetUnitlessValue (style, &absolute);
2124 /* To allow relative values we could search for the first selected
2125 item and make 'kind' relative to that, but that's not too useful
2126 and requires quite some code. For example there's no
2127 GetFirstSelectedPin() function available. Let's postpone this
2128 functionality, there are more urgent things to do. */
2130 if (absolute)
2131 switch (GetFunctionID (function))
2133 case F_Object:
2134 if ((type =
2135 SearchScreen (Crosshair.X, Crosshair.Y, CHANGETHERMAL_TYPES,
2136 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
2138 ChangeObjectThermal (type, ptr1, ptr2, ptr3, kind);
2139 IncrementUndoSerialNumber ();
2140 Draw ();
2142 break;
2143 case F_SelectedPins:
2144 ChangeSelectedThermals (PIN_TYPE, kind);
2145 break;
2146 case F_SelectedVias:
2147 ChangeSelectedThermals (VIA_TYPE, kind);
2148 break;
2149 case F_Selected:
2150 case F_SelectedElements:
2151 ChangeSelectedThermals (CHANGETHERMAL_TYPES, kind);
2152 break;
2153 default:
2154 err = 1;
2155 break;
2157 else
2158 err = 1;
2160 else
2161 err = 1;
2163 if (err)
2164 AFAIL (setthermal);
2166 return 0;
2170 * \brief Event handler to set the cursor according to the X pointer
2171 * position called from inside main.c.
2173 * \warning !!! no action routine !!!
2175 void
2176 EventMoveCrosshair (int ev_x, int ev_y)
2178 #ifdef HAVE_LIBSTROKE
2179 if (mid_stroke)
2181 StrokeBox.X2 = ev_x;
2182 StrokeBox.Y2 = ev_y;
2183 stroke_record (ev_x, ev_y);
2184 return;
2186 #endif /* HAVE_LIBSTROKE */
2187 if (MoveCrosshairAbsolute (ev_x, ev_y))
2189 /* update object position and cursor location */
2190 AdjustAttachedObjects ();
2191 notify_crosshair_change (true);
2195 /* --------------------------------------------------------------------------- */
2197 static const char setvalue_syntax[] =
2198 N_("SetValue(Grid|Line|LineSize|Text|TextScale|ViaDrillingHole|Via|ViaSize, "
2199 "delta)");
2201 static const char setvalue_help[] =
2202 N_("Change various board-wide values and sizes.");
2204 /* %start-doc actions SetValue
2206 @table @code
2208 @item ViaDrillingHole
2209 Changes the diameter of the drill for new vias.
2211 @item Grid
2212 Sets the grid spacing.
2214 @item Line
2215 @item LineSize
2216 Changes the thickness of new lines.
2218 @item Via
2219 @item ViaSize
2220 Changes the diameter of new vias.
2222 @item Text
2223 @item TextScale
2224 Changes the size of new text.
2226 @end table
2228 %end-doc */
2230 static int
2231 ActionSetValue (int argc, char **argv, Coord x, Coord y)
2233 char *function = ARG (0);
2234 char *val = ARG (1);
2235 char *units = ARG (2);
2236 bool absolute; /* flag for 'absolute' value */
2237 double value;
2238 int text_scale;
2239 int err = 0;
2241 if (function && val)
2243 value = GetValue (val, units, &absolute);
2244 switch (GetFunctionID (function))
2246 case F_ViaDrillingHole:
2247 SetViaDrillingHole (absolute ? value :
2248 value + Settings.ViaDrillingHole,
2249 false);
2250 hid_action ("RouteStylesChanged");
2251 break;
2253 case F_Grid:
2254 if (absolute)
2255 SetGrid (value, false);
2256 else
2258 if (value == 0)
2259 value = val[0] == '-' ? -Settings.increments->grid
2260 : Settings.increments->grid;
2261 /* On the way down, short against the minimum
2262 * PCB drawing unit */
2263 if ((value + PCB->Grid) < 1)
2264 SetGrid (1, false);
2265 else if (PCB->Grid == 1)
2266 SetGrid (value, false);
2267 else
2268 SetGrid (value + PCB->Grid, false);
2270 break;
2272 case F_LineSize:
2273 case F_Line:
2274 if (!absolute && value == 0)
2275 value = val[0] == '-' ? -Settings.increments->line
2276 : Settings.increments->line;
2277 SetLineSize (absolute ? value : value + Settings.LineThickness);
2278 hid_action ("RouteStylesChanged");
2279 break;
2281 case F_Via:
2282 case F_ViaSize:
2283 SetViaSize (absolute ? value : value + Settings.ViaThickness, false);
2284 hid_action ("RouteStylesChanged");
2285 break;
2287 case F_Text:
2288 case F_TextScale:
2289 text_scale = value / (double)FONT_CAPHEIGHT * 100.;
2290 if (!absolute)
2291 text_scale += Settings.TextScale;
2292 SetTextScale (text_scale);
2293 break;
2294 default:
2295 err = 1;
2296 break;
2298 if (!err)
2299 return 0;
2302 AFAIL (setvalue);
2306 /* --------------------------------------------------------------------------- */
2308 static const char quit_syntax[] = N_("Quit()");
2310 static const char quit_help[] = N_("Quits the application after confirming.");
2312 /* %start-doc actions Quit
2314 If you have unsaved changes, you will be prompted to confirm (or
2315 save) before quitting.
2317 %end-doc */
2319 static int
2320 ActionQuit (int argc, char **argv, Coord x, Coord y)
2322 char *force = ARG (0);
2323 if (force && strcasecmp (force, "force") == 0)
2325 PCB->Changed = 0;
2326 exit (0);
2328 if (!PCB->Changed || gui->close_confirm_dialog () == HID_CLOSE_CONFIRM_OK)
2329 QuitApplication ();
2330 return 1;
2333 /* --------------------------------------------------------------------------- */
2335 static const char connection_syntax[] =
2336 N_("Connection(Find|ResetLinesAndPolygons|ResetPinsAndVias|Reset)");
2338 static const char connection_help[] =
2339 N_("Searches connections of the object at the cursor position.");
2341 /* %start-doc actions Connection
2343 Connections found with this action will be highlighted in the
2344 ``connected-color'' color and will have the ``found'' flag set.
2346 @table @code
2348 @item Find
2349 The net under the cursor is ``found''.
2351 @item ResetLinesAndPolygons
2352 Any ``found'' lines and polygons are marked ``not found''.
2354 @item ResetPinsAndVias
2355 Any ``found'' pins and vias are marked ``not found''.
2357 @item Reset
2358 All ``found'' objects are marked ``not found''.
2360 @end table
2362 %end-doc */
2364 static int
2365 ActionConnection (int argc, char **argv, Coord x, Coord y)
2367 char *function = ARG (0);
2368 if (function)
2370 switch (GetFunctionID (function))
2372 case F_Find:
2374 gui->get_coords (_("Click on a connection"), &x, &y);
2375 LookupConnection (x, y, true, 1, CONNECTEDFLAG, false, true);
2376 LookupConnection (x, y, true, 1, FOUNDFLAG, true, true);
2377 break;
2380 case F_ResetLinesAndPolygons:
2381 if (ClearFlagOnLinesAndPolygons (true, CONNECTEDFLAG | FOUNDFLAG, true))
2383 IncrementUndoSerialNumber ();
2384 Draw ();
2386 break;
2388 case F_ResetPinsViasAndPads:
2389 if (ClearFlagOnPinsViasAndPads (true, CONNECTEDFLAG | FOUNDFLAG, true))
2391 IncrementUndoSerialNumber ();
2392 Draw ();
2394 break;
2396 case F_Reset:
2397 if (ClearFlagOnAllObjects (true, CONNECTEDFLAG | FOUNDFLAG, true))
2399 IncrementUndoSerialNumber ();
2400 Draw ();
2402 break;
2404 return 0;
2407 AFAIL (connection);
2410 /* --------------------------------------------------------------------------- */
2412 static const char disperseelements_syntax[] =
2413 N_("DisperseElements(All|Selected)");
2415 static const char disperseelements_help[] = N_("Disperses elements.");
2417 /* %start-doc actions DisperseElements
2419 Normally this is used when starting a board, by selecting all elements
2420 and then dispersing them. This scatters the elements around the board
2421 so that you can pick individual ones, rather than have all the
2422 elements at the same 0,0 coordinate and thus impossible to choose
2423 from.
2425 %end-doc */
2427 #define GAP MIL_TO_COORD(100)
2429 static int
2430 ActionDisperseElements (int argc, char **argv, Coord x, Coord y)
2432 char *function = ARG (0);
2433 Coord minx = GAP,
2434 miny = GAP,
2435 maxy = GAP,
2436 dx, dy;
2437 int all = 0, bad = 0;
2439 if (!function || !*function)
2441 bad = 1;
2443 else
2445 switch (GetFunctionID (function))
2447 case F_All:
2448 all = 1;
2449 break;
2451 case F_Selected:
2452 all = 0;
2453 break;
2455 default:
2456 bad = 1;
2460 if (bad)
2462 AFAIL (disperseelements);
2466 ELEMENT_LOOP (PCB->Data);
2469 * If we want to disperse selected elements, maybe we need smarter
2470 * code here to avoid putting components on top of others which
2471 * are not selected. For now, I'm assuming that this is typically
2472 * going to be used either with a brand new design or a scratch
2473 * design holding some new components
2475 if (!TEST_FLAG (LOCKFLAG, element) && (all || TEST_FLAG (SELECTEDFLAG, element)))
2478 /* figure out how much to move the element */
2479 dx = minx - element->BoundingBox.X1;
2481 /* snap to the grid */
2482 dx -= (element->MarkX + dx) % PCB->Grid;
2485 * and add one grid size so we make sure we always space by GAP or
2486 * more
2488 dx += PCB->Grid;
2490 /* Figure out if this row has room. If not, start a new row */
2491 if (GAP + element->BoundingBox.X2 + dx > PCB->MaxWidth)
2493 miny = maxy + GAP;
2494 minx = GAP;
2497 /* figure out how much to move the element */
2498 dx = minx - element->BoundingBox.X1;
2499 dy = miny - element->BoundingBox.Y1;
2501 /* snap to the grid */
2502 dx -= (element->MarkX + dx) % PCB->Grid;
2503 dx += PCB->Grid;
2504 dy -= (element->MarkY + dy) % PCB->Grid;
2505 dy += PCB->Grid;
2507 /* move the element */
2508 MoveElementLowLevel (PCB->Data, element, dx, dy);
2510 /* and add to the undo list so we can undo this operation */
2511 AddObjectToMoveUndoList (ELEMENT_TYPE, NULL, NULL, element, dx, dy);
2513 /* keep track of how tall this row is */
2514 minx += element->BoundingBox.X2 - element->BoundingBox.X1 + GAP;
2515 if (maxy < element->BoundingBox.Y2)
2517 maxy = element->BoundingBox.Y2;
2522 END_LOOP;
2524 /* done with our action so increment the undo # */
2525 IncrementUndoSerialNumber ();
2527 Redraw ();
2528 SetChangedFlag (true);
2530 return 0;
2533 #undef GAP
2535 /* --------------------------------------------------------------------------- */
2537 static const char display_syntax[] =
2538 N_("Display(NameOnPCB|Description|Value)\n"
2539 "Display(Grid|Redraw)\n"
2540 "Display(CycleClip|CycleCrosshair|Toggle45Degree|ToggleStartDirection)\n"
2541 "Display(ToggleGrid|ToggleRubberBandMode|ToggleUniqueNames)\n"
2542 "Display(ToggleMask|ToggleName|ToggleClearLine|ToggleFullPoly|ToggleSnapPin)\n"
2543 "Display(ToggleThindraw|ToggleThindrawPoly|ToggleOrthoMove|ToggleLocalRef)\n"
2544 "Display(ToggleCheckPlanes|ToggleShowDRC|ToggleAutoDRC)\n"
2545 "Display(ToggleLiveRoute|LockNames|OnlyNames)\n"
2546 "Display(Pinout|PinOrPadName)");
2548 static const char display_help[] = N_("Several display-related actions.");
2550 /* %start-doc actions Display
2552 @table @code
2554 @item NameOnPCB
2555 @item Description
2556 @item Value
2557 Specify whether all elements show their name, description, or value.
2559 @item Redraw
2560 Redraw the whole board.
2562 @item Toggle45Degree
2563 When clear, lines can be drawn at any angle. When set, lines are
2564 restricted to multiples of 45 degrees and requested lines may be
2565 broken up according to the clip setting.
2567 @item CycleClip
2568 Changes the way lines are restricted to 45 degree increments. The
2569 various settings are: straight only, orthogonal then angled, and angled
2570 then orthogonal. If AllDirections is set, this action disables it.
2572 @item CycleCrosshair
2573 Changes crosshair drawing. Crosshair may accept form of 4-ray,
2574 8-ray and 12-ray cross.
2576 @item ToggleRubberBandMode
2577 If set, moving an object moves all the lines attached to it too.
2579 @item ToggleStartDirection
2580 If set, each time you set a point in a line, the Clip toggles between
2581 orth-angle and angle-ortho.
2583 @item ToggleUniqueNames
2584 If set, you will not be permitted to change the name of an element to
2585 match that of another element.
2587 @item ToggleSnapPin
2588 If set, pin centers and pad end points are treated as additional grid
2589 points that the cursor can snap to.
2591 @item ToggleLocalRef
2592 If set, the mark is automatically set to the beginning of any move, so
2593 you can see the relative distance you've moved.
2595 @item ToggleThindraw
2596 If set, objects on the screen are drawn as outlines (lines are drawn
2597 as center-lines). This lets you see line endpoints hidden under pins,
2598 for example.
2600 @item ToggleThindrawPoly
2601 If set, polygons on the screen are drawn as outlines.
2603 @item ToggleShowDRC
2604 If set, pending objects (i.e. lines you're in the process of drawing)
2605 will be drawn with an outline showing how far away from other copper
2606 you need to be.
2608 @item ToggleLiveRoute
2609 If set, the progress of the autorouter will be visible on the screen.
2611 @item ToggleAutoDRC
2612 If set, you will not be permitted to make connections which violate
2613 the current DRC and netlist settings.
2615 @item ToggleCheckPlanes
2616 If set, lines and arcs aren't drawn, which usually leaves just the
2617 polygons. If you also disable all but the layer you're interested in,
2618 this allows you to check for isolated regions.
2620 @item ToggleOrthoMove
2621 If set, the crosshair is only allowed to move orthogonally from its
2622 previous position. I.e. you can move an element or line up, down,
2623 left, or right, but not up+left or down+right.
2625 @item ToggleName
2626 Selects whether the pinouts show the pin names or the pin numbers.
2628 @item ToggleLockNames
2629 If set, text will ignore left mouse clicks and actions that work on
2630 objects under the mouse. You can still select text with a lasso (left
2631 mouse drag) and perform actions on the selection.
2633 @item ToggleOnlyNames
2634 If set, only text will be sensitive for mouse clicks and actions that
2635 work on objects under the mouse. You can still select other objects
2636 with a lasso (left mouse drag) and perform actions on the selection.
2638 @item ToggleMask
2639 Turns the solder mask on or off.
2641 @item ToggleClearLine
2642 When set, the clear-line flag causes new lines and arcs to have their
2643 ``clear polygons'' flag set, so they won't be electrically connected
2644 to any polygons they overlap.
2646 @item ToggleFullPoly
2647 When set, the full-poly flag causes new polygons to have their
2648 ``full polygon'' flag set, so all parts of them will be displayed
2649 instead of only the biggest one.
2651 @item ToggleGrid
2652 Resets the origin of the current grid to be wherever the mouse pointer
2653 is (not where the crosshair currently is). If you provide two numbers
2654 after this, the origin is set to that coordinate.
2656 @item Grid
2657 Toggles whether the grid is displayed or not.
2659 @item Pinout
2660 Causes the pinout of the element indicated by the cursor to be
2661 displayed, usually in a separate window.
2663 @item PinOrPadName
2664 Toggles whether the names of pins, pads, or (yes) vias will be
2665 displayed. If the cursor is over an element, all of its pins and pads
2666 are affected.
2668 @item ToggleAutoBuriedVias
2669 If set, automatically created vias are buried vias.
2671 @end table
2673 %end-doc */
2675 static enum crosshair_shape
2676 CrosshairShapeIncrement (enum crosshair_shape shape)
2678 switch(shape)
2680 case Basic_Crosshair_Shape:
2681 shape = Union_Jack_Crosshair_Shape;
2682 break;
2683 case Union_Jack_Crosshair_Shape:
2684 shape = Dozen_Crosshair_Shape;
2685 break;
2686 case Dozen_Crosshair_Shape:
2687 shape = Crosshair_Shapes_Number;
2688 break;
2689 case Crosshair_Shapes_Number:
2690 shape = Basic_Crosshair_Shape;
2691 break;
2693 return shape;
2696 static int
2697 ActionDisplay (int argc, char **argv, Coord childX, Coord childY)
2699 char *function, *str_dir;
2700 int id;
2701 int err = 0;
2703 function = ARG (0);
2704 str_dir = ARG (1);
2706 if (function && (!str_dir || !*str_dir))
2708 switch (id = GetFunctionID (function))
2711 /* redraw layout */
2712 case F_ClearAndRedraw:
2713 case F_Redraw:
2714 Redraw ();
2715 break;
2717 /* change the displayed name of elements */
2718 case F_Value:
2719 case F_NameOnPCB:
2720 case F_Description:
2721 ELEMENT_LOOP (PCB->Data);
2723 EraseElementName (element);
2725 END_LOOP;
2726 CLEAR_FLAG (DESCRIPTIONFLAG | NAMEONPCBFLAG, PCB);
2727 switch (id)
2729 case F_Value:
2730 break;
2731 case F_NameOnPCB:
2732 SET_FLAG (NAMEONPCBFLAG, PCB);
2733 break;
2734 case F_Description:
2735 SET_FLAG (DESCRIPTIONFLAG, PCB);
2736 break;
2738 ELEMENT_LOOP (PCB->Data);
2740 DrawElementName (element);
2742 END_LOOP;
2743 Draw ();
2744 break;
2746 /* toggle line-adjust flag */
2747 case F_ToggleAllDirections:
2748 TOGGLE_FLAG (ALLDIRECTIONFLAG, PCB);
2749 AdjustAttachedObjects ();
2750 break;
2752 case F_CycleClip:
2753 notify_crosshair_change (false);
2754 if (TEST_FLAG (ALLDIRECTIONFLAG, PCB))
2756 TOGGLE_FLAG (ALLDIRECTIONFLAG, PCB);
2757 PCB->Clipping = 0;
2759 else
2760 PCB->Clipping = (PCB->Clipping + 1) % 3;
2761 AdjustAttachedObjects ();
2762 notify_crosshair_change (true);
2763 break;
2765 case F_CycleCrosshair:
2766 notify_crosshair_change (false);
2767 Crosshair.shape = CrosshairShapeIncrement(Crosshair.shape);
2768 if (Crosshair_Shapes_Number == Crosshair.shape)
2769 Crosshair.shape = Basic_Crosshair_Shape;
2770 notify_crosshair_change (true);
2771 break;
2773 case F_ToggleRubberBandMode:
2774 notify_crosshair_change (false);
2775 TOGGLE_FLAG (RUBBERBANDFLAG, PCB);
2776 notify_crosshair_change (true);
2777 break;
2779 case F_ToggleAutoBuriedVias:
2780 notify_crosshair_change (false);
2781 TOGGLE_FLAG (AUTOBURIEDVIASFLAG, PCB);
2782 notify_crosshair_change (true);
2783 break;
2785 case F_ToggleStartDirection:
2786 notify_crosshair_change (false);
2787 TOGGLE_FLAG (SWAPSTARTDIRFLAG, PCB);
2788 notify_crosshair_change (true);
2789 break;
2791 case F_ToggleUniqueNames:
2792 TOGGLE_FLAG (UNIQUENAMEFLAG, PCB);
2793 break;
2795 case F_ToggleSnapPin:
2796 notify_crosshair_change (false);
2797 TOGGLE_FLAG (SNAPPINFLAG, PCB);
2798 notify_crosshair_change (true);
2799 break;
2801 case F_ToggleLocalRef:
2802 TOGGLE_FLAG (LOCALREFFLAG, PCB);
2803 break;
2805 case F_ToggleThindraw:
2806 TOGGLE_FLAG (THINDRAWFLAG, PCB);
2807 Redraw ();
2808 break;
2810 case F_ToggleThindrawPoly:
2811 TOGGLE_FLAG (THINDRAWPOLYFLAG, PCB);
2812 Redraw ();
2813 break;
2815 case F_ToggleLockNames:
2816 TOGGLE_FLAG (LOCKNAMESFLAG, PCB);
2817 CLEAR_FLAG (ONLYNAMESFLAG, PCB);
2818 break;
2820 case F_ToggleOnlyNames:
2821 TOGGLE_FLAG (ONLYNAMESFLAG, PCB);
2822 CLEAR_FLAG (LOCKNAMESFLAG, PCB);
2823 break;
2825 case F_ToggleHideNames:
2826 TOGGLE_FLAG (HIDENAMESFLAG, PCB);
2827 Redraw ();
2828 break;
2830 case F_ToggleShowDRC:
2831 TOGGLE_FLAG (SHOWDRCFLAG, PCB);
2832 break;
2834 case F_ToggleLiveRoute:
2835 TOGGLE_FLAG (LIVEROUTEFLAG, PCB);
2836 break;
2838 case F_ToggleAutoDRC:
2839 notify_crosshair_change (false);
2840 TOGGLE_FLAG (AUTODRCFLAG, PCB);
2841 if (TEST_FLAG (AUTODRCFLAG, PCB) && Settings.Mode == LINE_MODE)
2843 if (ClearFlagOnAllObjects (true, CONNECTEDFLAG | FOUNDFLAG, true))
2845 IncrementUndoSerialNumber ();
2846 Draw ();
2848 if (Crosshair.AttachedLine.State != STATE_FIRST)
2850 LookupConnection (Crosshair.AttachedLine.Point1.X,
2851 Crosshair.AttachedLine.Point1.Y,
2852 true, 1, CONNECTEDFLAG, false, true);
2853 LookupConnection (Crosshair.AttachedLine.Point1.X,
2854 Crosshair.AttachedLine.Point1.Y,
2855 true, 1, FOUNDFLAG, true, true);
2858 notify_crosshair_change (true);
2859 break;
2861 case F_ToggleCheckPlanes:
2862 TOGGLE_FLAG (CHECKPLANESFLAG, PCB);
2863 Redraw ();
2864 break;
2866 case F_ToggleOrthoMove:
2867 TOGGLE_FLAG (ORTHOMOVEFLAG, PCB);
2868 break;
2870 case F_ToggleName:
2871 TOGGLE_FLAG (SHOWNUMBERFLAG, PCB);
2872 Redraw ();
2873 break;
2875 case F_ToggleMask:
2876 TOGGLE_FLAG (SHOWMASKFLAG, PCB);
2877 Redraw ();
2878 break;
2880 case F_ToggleClearLine:
2881 TOGGLE_FLAG (CLEARNEWFLAG, PCB);
2882 break;
2884 case F_ToggleFullPoly:
2885 TOGGLE_FLAG (NEWFULLPOLYFLAG, PCB);
2886 break;
2888 /* shift grid alignment */
2889 case F_ToggleGrid:
2891 Coord oldGrid = PCB->Grid;
2893 PCB->Grid = 1;
2894 if (MoveCrosshairAbsolute (Crosshair.X, Crosshair.Y))
2895 notify_crosshair_change (true); /* first notify was in MoveCrosshairAbs */
2896 SetGrid (oldGrid, true);
2898 break;
2900 /* toggle displaying of the grid */
2901 case F_Grid:
2902 Settings.DrawGrid = !Settings.DrawGrid;
2903 Redraw ();
2904 break;
2906 /* display the pinout of an element */
2907 case F_Pinout:
2909 ElementType *element;
2910 void *ptrtmp;
2911 Coord x, y;
2913 gui->get_coords (_("Click on an element"), &x, &y);
2914 if ((SearchScreen
2915 (x, y, ELEMENT_TYPE, &ptrtmp,
2916 &ptrtmp, &ptrtmp)) != NO_TYPE)
2918 element = (ElementType *) ptrtmp;
2919 gui->show_item (element);
2921 break;
2924 /* toggle displaying of pin/pad/via names */
2925 case F_PinOrPadName:
2927 void *ptr1, *ptr2, *ptr3;
2928 Coord x, y;
2930 gui->get_coords(_("Click on an element"), &x, &y);
2932 switch (SearchScreen (x, y,
2933 ELEMENT_TYPE | PIN_TYPE | PAD_TYPE |
2934 VIA_TYPE, (void **) &ptr1, (void **) &ptr2,
2935 (void **) &ptr3))
2937 case ELEMENT_TYPE:
2938 PIN_LOOP ((ElementType *) ptr1);
2940 if (TEST_FLAG (DISPLAYNAMEFLAG, pin))
2941 ErasePinName (pin);
2942 else
2943 DrawPinName (pin);
2944 AddObjectToFlagUndoList (PIN_TYPE, ptr1, pin, pin);
2945 TOGGLE_FLAG (DISPLAYNAMEFLAG, pin);
2947 END_LOOP;
2948 PAD_LOOP ((ElementType *) ptr1);
2950 if (TEST_FLAG (DISPLAYNAMEFLAG, pad))
2951 ErasePadName (pad);
2952 else
2953 DrawPadName (pad);
2954 AddObjectToFlagUndoList (PAD_TYPE, ptr1, pad, pad);
2955 TOGGLE_FLAG (DISPLAYNAMEFLAG, pad);
2957 END_LOOP;
2958 SetChangedFlag (true);
2959 IncrementUndoSerialNumber ();
2960 Draw ();
2961 break;
2963 case PIN_TYPE:
2964 if (TEST_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2))
2965 ErasePinName ((PinType *) ptr2);
2966 else
2967 DrawPinName ((PinType *) ptr2);
2968 AddObjectToFlagUndoList (PIN_TYPE, ptr1, ptr2, ptr3);
2969 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2);
2970 SetChangedFlag (true);
2971 IncrementUndoSerialNumber ();
2972 Draw ();
2973 break;
2975 case PAD_TYPE:
2976 if (TEST_FLAG (DISPLAYNAMEFLAG, (PadType *) ptr2))
2977 ErasePadName ((PadType *) ptr2);
2978 else
2979 DrawPadName ((PadType *) ptr2);
2980 AddObjectToFlagUndoList (PAD_TYPE, ptr1, ptr2, ptr3);
2981 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PadType *) ptr2);
2982 SetChangedFlag (true);
2983 IncrementUndoSerialNumber ();
2984 Draw ();
2985 break;
2986 case VIA_TYPE:
2987 if (TEST_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2))
2988 EraseViaName ((PinType *) ptr2);
2989 else
2990 DrawViaName ((PinType *) ptr2);
2991 AddObjectToFlagUndoList (VIA_TYPE, ptr1, ptr2, ptr3);
2992 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2);
2993 SetChangedFlag (true);
2994 IncrementUndoSerialNumber ();
2995 Draw ();
2996 break;
2998 break;
3000 default:
3001 err = 1;
3004 else if (function && str_dir)
3006 switch (GetFunctionID (function))
3008 case F_ToggleGrid:
3009 if (argc > 2)
3011 PCB->GridOffsetX = GetValue (argv[1], NULL, NULL);
3012 PCB->GridOffsetY = GetValue (argv[2], NULL, NULL);
3013 if (Settings.DrawGrid)
3014 Redraw ();
3016 break;
3018 default:
3019 err = 1;
3020 break;
3023 else
3024 err = 1;
3026 if (err)
3027 AFAIL (display);
3029 return 0;
3032 /* --------------------------------------------------------------------------- */
3034 static const char mode_syntax[] =
3035 N_("Mode(Arc|Arrow|Copy|InsertPoint|Line|Lock|Move|None|PasteBuffer)\n"
3036 "Mode(Polygon|Rectangle|Remove|Rotate|Text|Thermal|Via)\n"
3037 "Mode(Notify|Release|Cancel|Stroke)\n"
3038 "Mode(Save|Restore)");
3040 static const char mode_help[] = N_("Change or use the tool mode.");
3042 /* %start-doc actions Mode
3044 @table @code
3046 @item Arc
3047 @itemx Arrow
3048 @itemx Copy
3049 @itemx InsertPoint
3050 @itemx Line
3051 @itemx Lock
3052 @itemx Move
3053 @itemx None
3054 @itemx PasteBuffer
3055 @itemx Polygon
3056 @itemx Rectangle
3057 @itemx Remove
3058 @itemx Rotate
3059 @itemx Text
3060 @itemx Thermal
3061 @itemx Via
3062 Select the indicated tool.
3064 @item Notify
3065 Called when you press the mouse button, or move the mouse.
3067 @item Release
3068 Called when you release the mouse button.
3070 @item Cancel
3071 Cancels any pending tool activity, allowing you to restart elsewhere.
3072 For example, this allows you to start a new line rather than attach a
3073 line to the previous line.
3075 @item Escape
3076 Similar to Cancel but calling this action a second time will return
3077 to the Arrow tool.
3079 @item Stroke
3080 If your @code{pcb} was built with libstroke, this invokes the stroke
3081 input method. If not, this will restart a drawing mode if you were
3082 drawing, else it will select objects.
3084 @item Save
3085 Remembers the current tool.
3087 @item Restore
3088 Restores the tool to the last saved tool.
3090 @end table
3092 %end-doc */
3094 static int
3095 ActionMode (int argc, char **argv, Coord x, Coord y)
3097 char *function = ARG (0);
3099 if (function)
3101 Note.X = Crosshair.X;
3102 Note.Y = Crosshair.Y;
3103 notify_crosshair_change (false);
3104 switch (GetFunctionID (function))
3106 case F_Arc:
3107 SetMode (ARC_MODE);
3108 break;
3109 case F_Arrow:
3110 SetMode (ARROW_MODE);
3111 break;
3112 case F_Copy:
3113 SetMode (COPY_MODE);
3114 break;
3115 case F_InsertPoint:
3116 SetMode (INSERTPOINT_MODE);
3117 break;
3118 case F_Line:
3119 SetMode (LINE_MODE);
3120 break;
3121 case F_Lock:
3122 SetMode (LOCK_MODE);
3123 break;
3124 case F_Move:
3125 SetMode (MOVE_MODE);
3126 break;
3127 case F_None:
3128 SetMode (NO_MODE);
3129 break;
3130 case F_Cancel:
3132 int saved_mode = Settings.Mode;
3133 SetMode (NO_MODE);
3134 SetMode (saved_mode);
3136 break;
3137 case F_Escape:
3139 switch (Settings.Mode)
3141 case VIA_MODE:
3142 case PASTEBUFFER_MODE:
3143 case TEXT_MODE:
3144 case ROTATE_MODE:
3145 case REMOVE_MODE:
3146 case MOVE_MODE:
3147 case COPY_MODE:
3148 case INSERTPOINT_MODE:
3149 case RUBBERBANDMOVE_MODE:
3150 case THERMAL_MODE:
3151 case LOCK_MODE:
3152 SetMode (NO_MODE);
3153 SetMode (ARROW_MODE);
3154 break;
3156 case LINE_MODE:
3157 if (Crosshair.AttachedLine.State == STATE_FIRST)
3158 SetMode (ARROW_MODE);
3159 else
3161 SetMode (NO_MODE);
3162 SetMode (LINE_MODE);
3164 break;
3166 case RECTANGLE_MODE:
3167 if (Crosshair.AttachedBox.State == STATE_FIRST)
3168 SetMode (ARROW_MODE);
3169 else
3171 SetMode (NO_MODE);
3172 SetMode (RECTANGLE_MODE);
3174 break;
3176 case POLYGON_MODE:
3177 if (Crosshair.AttachedLine.State == STATE_FIRST)
3178 SetMode (ARROW_MODE);
3179 else
3181 SetMode (NO_MODE);
3182 SetMode (POLYGON_MODE);
3184 break;
3186 case POLYGONHOLE_MODE:
3187 if (Crosshair.AttachedLine.State == STATE_FIRST)
3188 SetMode (ARROW_MODE);
3189 else
3191 SetMode (NO_MODE);
3192 SetMode (POLYGONHOLE_MODE);
3194 break;
3196 case ARC_MODE:
3197 if (Crosshair.AttachedBox.State == STATE_FIRST)
3198 SetMode (ARROW_MODE);
3199 else
3201 SetMode (NO_MODE);
3202 SetMode (ARC_MODE);
3204 break;
3206 case ARROW_MODE:
3207 break;
3209 default:
3210 break;
3213 break;
3215 case F_Notify:
3216 NotifyMode ();
3217 break;
3218 case F_PasteBuffer:
3219 SetMode (PASTEBUFFER_MODE);
3220 break;
3221 case F_Polygon:
3222 SetMode (POLYGON_MODE);
3223 break;
3224 case F_PolygonHole:
3225 SetMode (POLYGONHOLE_MODE);
3226 break;
3227 #ifndef HAVE_LIBSTROKE
3228 case F_Release:
3229 ReleaseMode ();
3230 break;
3231 #else
3232 case F_Release:
3233 if (mid_stroke)
3234 FinishStroke ();
3235 else
3236 ReleaseMode ();
3237 break;
3238 #endif
3239 case F_Remove:
3240 SetMode (REMOVE_MODE);
3241 break;
3242 case F_Rectangle:
3243 SetMode (RECTANGLE_MODE);
3244 break;
3245 case F_Rotate:
3246 SetMode (ROTATE_MODE);
3247 break;
3248 case F_Stroke:
3249 #ifdef HAVE_LIBSTROKE
3250 mid_stroke = true;
3251 StrokeBox.X1 = Crosshair.X;
3252 StrokeBox.Y1 = Crosshair.Y;
3253 break;
3254 #else
3255 /* Handle middle mouse button restarts of drawing mode. If not in
3256 | a drawing mode, middle mouse button will select objects.
3258 if (Settings.Mode == LINE_MODE
3259 && Crosshair.AttachedLine.State != STATE_FIRST)
3261 SetMode (LINE_MODE);
3263 else if (Settings.Mode == ARC_MODE
3264 && Crosshair.AttachedBox.State != STATE_FIRST)
3265 SetMode (ARC_MODE);
3266 else if (Settings.Mode == RECTANGLE_MODE
3267 && Crosshair.AttachedBox.State != STATE_FIRST)
3268 SetMode (RECTANGLE_MODE);
3269 else if (Settings.Mode == POLYGON_MODE
3270 && Crosshair.AttachedLine.State != STATE_FIRST)
3271 SetMode (POLYGON_MODE);
3272 else
3274 SaveMode ();
3275 saved_mode = true;
3276 SetMode (ARROW_MODE);
3277 NotifyMode ();
3279 break;
3280 #endif
3281 case F_Text:
3282 SetMode (TEXT_MODE);
3283 break;
3284 case F_Thermal:
3285 SetMode (THERMAL_MODE);
3286 break;
3287 case F_Via:
3288 SetMode (VIA_MODE);
3289 break;
3291 case F_Restore: /* restore the last saved mode */
3292 RestoreMode ();
3293 break;
3295 case F_Save: /* save currently selected mode */
3296 SaveMode ();
3297 break;
3299 notify_crosshair_change (true);
3300 return 0;
3303 AFAIL (mode);
3306 /* --------------------------------------------------------------------------- */
3308 static const char removeselected_syntax[] = N_("RemoveSelected()");
3310 static const char removeselected_help[] = N_("Removes any selected objects.");
3312 /* %start-doc actions RemoveSelected
3314 %end-doc */
3316 static int
3317 ActionRemoveSelected (int argc, char **argv, Coord x, Coord y)
3319 if (RemoveSelected ())
3320 SetChangedFlag (true);
3321 return 0;
3324 /* --------------------------------------------------------------------------- */
3326 static const char renumber_syntax[] = N_("Renumber()\n"
3327 "Renumber(filename)");
3329 static const char renumber_help[] =
3330 N_("Renumber all elements. The changes will be recorded to filename\n"
3331 "for use in backannotating these changes to the schematic.");
3333 /* %start-doc actions Renumber
3335 %end-doc */
3337 static int
3338 ActionRenumber (int argc, char **argv, Coord x, Coord y)
3340 bool changed = false;
3341 ElementType **element_list;
3342 ElementType **locked_element_list;
3343 unsigned int i, j, k, cnt, lock_cnt;
3344 unsigned int tmpi;
3345 size_t sz;
3346 char *tmps;
3347 char *name;
3348 FILE *out;
3349 static char * default_file = NULL;
3350 size_t cnt_list_sz = 100;
3351 struct _cnt_list
3353 char *name;
3354 unsigned int cnt;
3355 } *cnt_list;
3356 char **was, **is, *pin;
3357 unsigned int c_cnt = 0;
3358 int unique, ok;
3359 int free_name = 0;
3361 if (argc < 1)
3364 * We deal with the case where name already exists in this
3365 * function so the GUI doesn't need to deal with it
3367 name = gui->fileselect (_("Save Renumber Annotation File As ..."),
3368 _("Choose a file to record the renumbering to.\n"
3369 "This file may be used to back annotate the\n"
3370 "change to the schematics.\n"),
3371 default_file, ".eco", "eco",
3374 free_name = 1;
3376 else
3377 name = argv[0];
3379 if (default_file)
3381 free (default_file);
3382 default_file = NULL;
3385 if (name && *name)
3387 default_file = strdup (name);
3390 if ((out = fopen (name, "r")))
3392 fclose (out);
3393 if (!gui->confirm_dialog (_("File exists! Ok to overwrite?"), 0))
3395 if (free_name && name)
3396 free (name);
3397 return 0;
3401 if ((out = fopen (name, "w")) == NULL)
3403 Message (_("Could not open %s\n"), name);
3404 if (free_name && name)
3405 free (name);
3406 return 1;
3409 if (free_name && name)
3410 free (name);
3412 fprintf (out, "*COMMENT* PCB Annotation File\n");
3413 fprintf (out, "*FILEVERSION* 20061031\n");
3416 * Make a first pass through all of the elements and sort them out
3417 * by location on the board. While here we also collect a list of
3418 * locked elements.
3420 * We'll actually renumber things in the 2nd pass.
3422 element_list = (ElementType **)calloc (PCB->Data->ElementN, sizeof (ElementType *));
3423 locked_element_list = (ElementType **)calloc (PCB->Data->ElementN, sizeof (ElementType *));
3424 was = (char **)calloc (PCB->Data->ElementN, sizeof (char *));
3425 is = (char **)calloc (PCB->Data->ElementN, sizeof (char *));
3426 if (element_list == NULL || locked_element_list == NULL || was == NULL
3427 || is == NULL)
3429 fprintf (stderr, "calloc() failed in %s\n", __FUNCTION__);
3430 exit (1);
3434 cnt = 0;
3435 lock_cnt = 0;
3436 ELEMENT_LOOP (PCB->Data);
3438 if (TEST_FLAG (LOCKFLAG, element->Name) || TEST_FLAG (LOCKFLAG, element))
3441 * add to the list of locked elements which we won't try to
3442 * renumber and whose reference designators are now reserved.
3444 pcb_fprintf (out,
3445 "*WARN* Element \"%s\" at %$md is locked and will not be renumbered.\n",
3446 UNKNOWN (NAMEONPCB_NAME (element)), element->MarkX, element->MarkY);
3447 locked_element_list[lock_cnt] = element;
3448 lock_cnt++;
3451 else
3453 /* count of devices which will be renumbered */
3454 cnt++;
3456 /* search for correct position in the list */
3457 i = 0;
3458 while (element_list[i] && element->MarkY > element_list[i]->MarkY)
3459 i++;
3462 * We have found the position where we have the first element that
3463 * has the same Y value or a lower Y value. Now move forward if
3464 * needed through the X values
3466 while (element_list[i]
3467 && element->MarkY == element_list[i]->MarkY
3468 && element->MarkX > element_list[i]->MarkX)
3469 i++;
3471 for (j = cnt - 1; j > i; j--)
3473 element_list[j] = element_list[j - 1];
3475 element_list[i] = element;
3478 END_LOOP;
3482 * Now that the elements are sorted by board position, we go through
3483 * and renumber them.
3487 * turn off the flag which requires unique names so it doesn't get
3488 * in our way. When we're done with the renumber we will have unique
3489 * names.
3491 unique = TEST_FLAG (UNIQUENAMEFLAG, PCB);
3492 CLEAR_FLAG (UNIQUENAMEFLAG, PCB);
3494 cnt_list = (struct _cnt_list *)calloc (cnt_list_sz, sizeof (struct _cnt_list));
3495 for (i = 0; i < cnt; i++)
3497 /* If there is no refdes, maybe just spit out a warning */
3498 if (NAMEONPCB_NAME (element_list[i]))
3500 /* figure out the prefix */
3501 tmps = strdup (NAMEONPCB_NAME (element_list[i]));
3502 j = 0;
3503 while (tmps[j] && (tmps[j] < '0' || tmps[j] > '9')
3504 && tmps[j] != '?')
3505 j++;
3506 tmps[j] = '\0';
3508 /* check the counter for this prefix */
3509 for (j = 0;
3510 cnt_list[j].name && (strcmp (cnt_list[j].name, tmps) != 0)
3511 && j < cnt_list_sz; j++);
3513 /* grow the list if needed */
3514 if (j == cnt_list_sz)
3516 cnt_list_sz += 100;
3517 cnt_list = (struct _cnt_list *)realloc (cnt_list, cnt_list_sz);
3518 if (cnt_list == NULL)
3520 fprintf (stderr, _("realloc() failed in %s()\n"), __FUNCTION__);
3521 exit (1);
3523 /* zero out the memory that we added */
3524 for (tmpi = j; tmpi < cnt_list_sz; tmpi++)
3526 cnt_list[tmpi].name = NULL;
3527 cnt_list[tmpi].cnt = 0;
3532 * start a new counter if we don't have a counter for this
3533 * prefix
3535 if (!cnt_list[j].name)
3537 cnt_list[j].name = strdup (tmps);
3538 cnt_list[j].cnt = 0;
3542 * check to see if the new refdes is already used by a
3543 * locked element
3547 ok = 1;
3548 cnt_list[j].cnt++;
3549 free (tmps);
3551 /* space for the prefix plus 1 digit plus the '\0' */
3552 sz = strlen (cnt_list[j].name) + 2;
3554 /* and 1 more per extra digit needed to hold the number */
3555 tmpi = cnt_list[j].cnt;
3556 while (tmpi > 10)
3558 sz++;
3559 tmpi = tmpi / 10;
3561 tmps = (char *)malloc (sz * sizeof (char));
3562 sprintf (tmps, "%s%d", cnt_list[j].name, (int) cnt_list[j].cnt);
3565 * now compare to the list of reserved (by locked
3566 * elements) names
3568 for (k = 0; k < lock_cnt; k++)
3570 if (strcmp
3571 (UNKNOWN (NAMEONPCB_NAME (locked_element_list[k])),
3572 tmps) == 0)
3574 ok = 0;
3575 break;
3580 while (!ok);
3582 if (strcmp (tmps, NAMEONPCB_NAME (element_list[i])) != 0)
3584 fprintf (out, "*RENAME* \"%s\" \"%s\"\n",
3585 NAMEONPCB_NAME (element_list[i]), tmps);
3587 /* add this rename to our table of renames so we can update the netlist */
3588 was[c_cnt] = strdup (NAMEONPCB_NAME (element_list[i]));
3589 is[c_cnt] = strdup (tmps);
3590 c_cnt++;
3592 AddObjectToChangeNameUndoList (ELEMENT_TYPE, NULL, NULL,
3593 element_list[i],
3594 NAMEONPCB_NAME (element_list
3595 [i]));
3597 ChangeObjectName (ELEMENT_TYPE, element_list[i], NULL, NULL,
3598 tmps);
3599 changed = true;
3601 /* we don't free tmps in this case because it is used */
3603 else
3604 free (tmps);
3606 else
3608 pcb_fprintf (out, "*WARN* Element at %$md has no name.\n",
3609 element_list[i]->MarkX, element_list[i]->MarkY);
3614 fclose (out);
3616 /* restore the unique flag setting */
3617 if (unique)
3618 SET_FLAG (UNIQUENAMEFLAG, PCB);
3620 if (changed)
3623 /* update the netlist */
3624 AddNetlistLibToUndoList (&(PCB->NetlistLib));
3626 /* iterate over each net */
3627 for (i = 0; i < PCB->NetlistLib.MenuN; i++)
3630 /* iterate over each pin on the net */
3631 for (j = 0; j < PCB->NetlistLib.Menu[i].EntryN; j++)
3634 /* figure out the pin number part from strings like U3-21 */
3635 tmps = strdup (PCB->NetlistLib.Menu[i].Entry[j].ListEntry);
3636 for (k = 0; tmps[k] && tmps[k] != '-'; k++);
3637 tmps[k] = '\0';
3638 pin = tmps + k + 1;
3640 /* iterate over the list of changed reference designators */
3641 for (k = 0; k < c_cnt; k++)
3644 * if the pin needs to change, change it and quit
3645 * searching in the list.
3647 if (strcmp (tmps, was[k]) == 0)
3649 free (PCB->NetlistLib.Menu[i].Entry[j].ListEntry);
3650 PCB->NetlistLib.Menu[i].Entry[j].ListEntry =
3651 (char *)malloc ((strlen (is[k]) + strlen (pin) +
3652 2) * sizeof (char));
3653 sprintf (PCB->NetlistLib.Menu[i].Entry[j].ListEntry,
3654 "%s-%s", is[k], pin);
3655 k = c_cnt;
3659 free (tmps);
3662 for (k = 0; k < c_cnt; k++)
3664 free (was[k]);
3665 free (is[k]);
3668 NetlistChanged (0);
3669 IncrementUndoSerialNumber ();
3670 SetChangedFlag (true);
3673 free (locked_element_list);
3674 free (element_list);
3675 free (cnt_list);
3676 free (is);
3677 free (was);
3678 return 0;
3682 /* --------------------------------------------------------------------------- */
3684 static const char ripup_syntax[] = N_("RipUp(All|Selected|Element)");
3686 static const char ripup_help[] =
3687 N_("Ripup auto-routed tracks, or convert an element to parts.");
3689 /* %start-doc actions RipUp
3691 @table @code
3693 @item All
3694 Removes all lines and vias which were created by the autorouter.
3696 @item Selected
3697 Removes all selected lines and vias which were created by the
3698 autorouter.
3700 @item Element
3701 Converts the element under the cursor to parts (vias and lines). Note
3702 that this uses the highest numbered paste buffer.
3704 @end table
3706 %end-doc */
3708 static int
3709 ActionRipUp (int argc, char **argv, Coord x, Coord y)
3711 char *function = ARG (0);
3712 bool changed = false;
3714 if (function)
3716 switch (GetFunctionID (function))
3718 case F_All:
3719 ALLLINE_LOOP (PCB->Data);
3721 if (TEST_FLAG (AUTOFLAG, line) && !TEST_FLAG (LOCKFLAG, line))
3723 RemoveObject (LINE_TYPE, layer, line, line);
3724 changed = true;
3727 ENDALL_LOOP;
3728 ALLARC_LOOP (PCB->Data);
3730 if (TEST_FLAG (AUTOFLAG, arc) && !TEST_FLAG (LOCKFLAG, arc))
3732 RemoveObject (ARC_TYPE, layer, arc, arc);
3733 changed = true;
3736 ENDALL_LOOP;
3737 VIA_LOOP (PCB->Data);
3739 if (TEST_FLAG (AUTOFLAG, via) && !TEST_FLAG (LOCKFLAG, via))
3741 RemoveObject (VIA_TYPE, via, via, via);
3742 changed = true;
3745 END_LOOP;
3747 if (changed)
3749 IncrementUndoSerialNumber ();
3750 SetChangedFlag (true);
3752 break;
3753 case F_Selected:
3754 VISIBLELINE_LOOP (PCB->Data);
3756 if (TEST_FLAGS (AUTOFLAG | SELECTEDFLAG, line)
3757 && !TEST_FLAG (LOCKFLAG, line))
3759 RemoveObject (LINE_TYPE, layer, line, line);
3760 changed = true;
3763 ENDALL_LOOP;
3764 if (PCB->ViaOn)
3765 VIA_LOOP (PCB->Data);
3767 if (TEST_FLAGS (AUTOFLAG | SELECTEDFLAG, via)
3768 && !TEST_FLAG (LOCKFLAG, via))
3770 RemoveObject (VIA_TYPE, via, via, via);
3771 changed = true;
3774 END_LOOP;
3775 if (changed)
3777 IncrementUndoSerialNumber ();
3778 SetChangedFlag (true);
3780 break;
3781 case F_Element:
3783 void *ptr1, *ptr2, *ptr3;
3785 if (SearchScreen (Crosshair.X, Crosshair.Y, ELEMENT_TYPE,
3786 &ptr1, &ptr2, &ptr3) != NO_TYPE)
3788 Note.Buffer = Settings.BufferNumber;
3789 SetBufferNumber (MAX_BUFFER - 1);
3790 ClearBuffer (PASTEBUFFER);
3791 CopyObjectToBuffer (PASTEBUFFER->Data, PCB->Data,
3792 ELEMENT_TYPE, ptr1, ptr2, ptr3);
3793 SmashBufferElement (PASTEBUFFER);
3794 PASTEBUFFER->X = 0;
3795 PASTEBUFFER->Y = 0;
3796 SaveUndoSerialNumber ();
3797 EraseObject (ELEMENT_TYPE, ptr1, ptr1);
3798 MoveObjectToRemoveUndoList (ELEMENT_TYPE, ptr1, ptr2, ptr3);
3799 RestoreUndoSerialNumber ();
3800 CopyPastebufferToLayout (0, 0);
3801 SetBufferNumber (Note.Buffer);
3802 SetChangedFlag (true);
3805 break;
3808 return 0;
3811 /* --------------------------------------------------------------------------- */
3813 static const char addrats_syntax[] = N_("AddRats(AllRats|SelectedRats|Close)");
3815 static const char addrats_help[] =
3816 N_("Add one or more rat lines to the board.");
3818 /* %start-doc actions AddRats
3820 @table @code
3822 @item AllRats
3823 Create rat lines for all loaded nets that aren't already connected on
3824 with copper.
3826 @item SelectedRats
3827 Similarly, but only add rat lines for nets connected to selected pins
3828 and pads.
3830 @item Close
3831 Selects the shortest unselected rat on the board.
3833 @end table
3835 %end-doc */
3837 static int
3838 ActionAddRats (int argc, char **argv, Coord x, Coord y)
3840 char *function = ARG (0);
3841 RatType *shorty;
3842 float len, small;
3844 if (function)
3846 if (Settings.RatWarn)
3847 ClearWarnings ();
3848 switch (GetFunctionID (function))
3850 case F_AllRats:
3851 if (AddAllRats (false, NULL))
3852 SetChangedFlag (true);
3853 UpdateLineNetnames (); /* XXX: KLUDGE */
3854 break;
3855 case F_SelectedRats:
3856 case F_Selected:
3857 if (AddAllRats (true, NULL))
3858 SetChangedFlag (true);
3859 break;
3860 case F_Close:
3861 small = SQUARE (MAX_COORD);
3862 shorty = NULL;
3863 RAT_LOOP (PCB->Data);
3865 if (TEST_FLAG (SELECTEDFLAG, line))
3866 continue;
3867 len = SQUARE (line->Point1.X - line->Point2.X) +
3868 SQUARE (line->Point1.Y - line->Point2.Y);
3869 if (len < small)
3871 small = len;
3872 shorty = line;
3875 END_LOOP;
3876 if (shorty)
3878 AddObjectToFlagUndoList (RATLINE_TYPE, shorty, shorty, shorty);
3879 SET_FLAG (SELECTEDFLAG, shorty);
3880 DrawRat (shorty);
3881 Draw ();
3882 CenterDisplay ((shorty->Point2.X + shorty->Point1.X) / 2,
3883 (shorty->Point2.Y + shorty->Point1.Y) / 2,
3884 false);
3886 break;
3889 return 0;
3892 /* --------------------------------------------------------------------------- */
3894 static const char delete_syntax[] =
3895 N_("Delete(Object|Selected)\n"
3896 "Delete(AllRats|SelectedRats)");
3898 static const char delete_help[] = N_("Delete stuff.");
3900 /* %start-doc actions Delete
3902 %end-doc */
3904 static int
3905 ActionDelete (int argc, char **argv, Coord x, Coord y)
3907 char *function = ARG (0);
3908 int id = GetFunctionID (function);
3910 Note.X = Crosshair.X;
3911 Note.Y = Crosshair.Y;
3913 if (id == -1) /* no arg */
3915 if (RemoveSelected() == false)
3916 id = F_Object;
3919 switch (id)
3921 case F_Object:
3922 SaveMode();
3923 SetMode(REMOVE_MODE);
3924 NotifyMode();
3925 RestoreMode();
3926 break;
3927 case F_Selected:
3928 RemoveSelected();
3929 break;
3930 case F_AllRats:
3931 if (DeleteRats (false))
3932 SetChangedFlag (true);
3933 break;
3934 case F_SelectedRats:
3935 if (DeleteRats (true))
3936 SetChangedFlag (true);
3937 break;
3940 return 0;
3943 /* --------------------------------------------------------------------------- */
3945 static const char deleterats_syntax[] =
3946 N_("DeleteRats(AllRats|Selected|SelectedRats)");
3948 static const char deleterats_help[] = N_("Delete rat lines.");
3950 /* %start-doc actions DeleteRats
3952 %end-doc */
3954 static int
3955 ActionDeleteRats (int argc, char **argv, Coord x, Coord y)
3957 char *function = ARG (0);
3958 if (function)
3960 if (Settings.RatWarn)
3961 ClearWarnings ();
3962 switch (GetFunctionID (function))
3964 case F_AllRats:
3965 if (DeleteRats (false))
3966 SetChangedFlag (true);
3967 break;
3968 case F_SelectedRats:
3969 case F_Selected:
3970 if (DeleteRats (true))
3971 SetChangedFlag (true);
3972 break;
3975 return 0;
3978 /* --------------------------------------------------------------------------- */
3980 static const char autoplace_syntax[] = N_("AutoPlaceSelected()");
3982 static const char autoplace_help[] = N_("Auto-place selected components.");
3984 /* %start-doc actions AutoPlaceSelected
3986 Attempts to re-arrange the selected components such that the nets
3987 connecting them are minimized. Note that you cannot undo this.
3989 %end-doc */
3991 static int
3992 ActionAutoPlaceSelected (int argc, char **argv, Coord x, Coord y)
3994 hid_action("Busy");
3995 if (gui->confirm_dialog (_("Auto-placement can NOT be undone.\n"
3996 "Do you want to continue anyway?\n"), 0))
3998 if (AutoPlaceSelected ())
3999 SetChangedFlag (true);
4001 return 0;
4004 /* --------------------------------------------------------------------------- */
4006 static const char autoroute_syntax[] = N_("AutoRoute(AllRats|SelectedRats)");
4008 static const char autoroute_help[] = N_("Auto-route some or all rat lines.");
4010 /* %start-doc actions AutoRoute
4012 @table @code
4014 @item AllRats
4015 Attempt to autoroute all rats.
4017 @item SelectedRats
4018 Attempt to autoroute the selected rats.
4020 @end table
4022 Before autorouting, it's important to set up a few things. First,
4023 make sure any layers you aren't using are disabled, else the
4024 autorouter may use them. Next, make sure the current line and via
4025 styles are set accordingly. Last, make sure "new lines clear
4026 polygons" is set, in case you eventually want to add a copper pour.
4028 Autorouting takes a while. During this time, the program may not be
4029 responsive.
4031 %end-doc */
4033 static int
4034 ActionAutoRoute (int argc, char **argv, Coord x, Coord y)
4036 char *function = ARG (0);
4037 hid_action("Busy");
4038 if (function) /* one parameter */
4040 switch (GetFunctionID (function))
4042 case F_AllRats:
4043 if (AutoRoute (false))
4044 SetChangedFlag (true);
4045 break;
4046 case F_SelectedRats:
4047 case F_Selected:
4048 if (AutoRoute (true))
4049 SetChangedFlag (true);
4050 break;
4053 return 0;
4056 /* --------------------------------------------------------------------------- */
4058 static const char markcrosshair_syntax[] =
4059 N_("MarkCrosshair()\n"
4060 "MarkCrosshair(Center)");
4062 static const char markcrosshair_help[] = N_("Set/Reset the Crosshair mark.");
4064 /* %start-doc actions MarkCrosshair
4066 The ``mark'' is a small X-shaped target on the display which is
4067 treated like a second origin (the normal origin is the upper let
4068 corner of the board). The GUI will display a second set of
4069 coordinates for this mark, which tells you how far you are from it.
4071 If no argument is given, the mark is toggled - disabled if it was
4072 enabled, or enabled at the current cursor position of disabled. If
4073 the @code{Center} argument is given, the mark is moved to the current
4074 cursor location.
4076 %end-doc */
4078 static int
4079 ActionMarkCrosshair (int argc, char **argv, Coord x, Coord y)
4081 char *function = ARG (0);
4082 if (!function || !*function)
4084 if (Marked.status)
4086 notify_mark_change (false);
4087 Marked.status = false;
4088 notify_mark_change (true);
4090 else
4092 notify_mark_change (false);
4093 Marked.status = false;
4094 Marked.status = true;
4095 Marked.X = Crosshair.X;
4096 Marked.Y = Crosshair.Y;
4097 notify_mark_change (true);
4100 else if (GetFunctionID (function) == F_Center)
4102 notify_mark_change (false);
4103 Marked.status = true;
4104 Marked.X = Crosshair.X;
4105 Marked.Y = Crosshair.Y;
4106 notify_mark_change (true);
4108 return 0;
4111 /* --------------------------------------------------------------------------- */
4113 static const char changesize_syntax[] =
4114 N_("ChangeSize(Object, delta)\n"
4115 "ChangeSize(SelectedObjects|Selected, delta)\n"
4116 "ChangeSize(SelectedLines|SelectedPins|SelectedVias, delta)\n"
4117 "ChangeSize(SelectedPads|SelectedTexts|SelectedNames, delta)\n"
4118 "ChangeSize(SelectedElements, delta)");
4120 static const char changesize_help[] = N_("Changes the size of objects.");
4122 /* %start-doc actions ChangeSize
4124 For lines and arcs, this changes the width. For pins and vias, this
4125 changes the overall diameter of the copper annulus. For pads, this
4126 changes the width and, indirectly, the length. For texts and names,
4127 this changes the scaling factor. For elements, this changes the width
4128 of the silk layer lines and arcs for this element.
4130 %end-doc */
4132 static int
4133 ActionChangeSize (int argc, char **argv, Coord x, Coord y)
4135 char *function = ARG (0);
4136 char *delta = ARG (1);
4137 char *units = ARG (2);
4138 bool absolute; /* indicates if absolute size is given */
4139 Coord value;
4141 if (function && delta)
4143 value = GetValue (delta, units, &absolute);
4144 if (value == 0)
4145 value = delta[0] == '-' ? -Settings.increments->size
4146 : Settings.increments->size;
4147 switch (GetFunctionID (function))
4149 case F_Object:
4151 int type;
4152 void *ptr1, *ptr2, *ptr3;
4154 if ((type =
4155 SearchScreen (Crosshair.X, Crosshair.Y, CHANGESIZE_TYPES,
4156 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4157 if (TEST_FLAG (LOCKFLAG, (PinType *) ptr2))
4158 Message (_("Sorry, the object is locked\n"));
4159 if (ChangeObjectSize (type, ptr1, ptr2, ptr3, value, absolute))
4160 SetChangedFlag (true);
4161 break;
4164 case F_SelectedVias:
4165 if (ChangeSelectedSize (VIA_TYPE, value, absolute))
4166 SetChangedFlag (true);
4167 break;
4169 case F_SelectedPins:
4170 if (ChangeSelectedSize (PIN_TYPE, value, absolute))
4171 SetChangedFlag (true);
4172 break;
4174 case F_SelectedPads:
4175 if (ChangeSelectedSize (PAD_TYPE, value, absolute))
4176 SetChangedFlag (true);
4177 break;
4179 case F_SelectedArcs:
4180 if (ChangeSelectedSize (ARC_TYPE, value, absolute))
4181 SetChangedFlag (true);
4182 break;
4184 case F_SelectedLines:
4185 if (ChangeSelectedSize (LINE_TYPE, value, absolute))
4186 SetChangedFlag (true);
4187 break;
4189 case F_SelectedTexts:
4190 if (ChangeSelectedSize (TEXT_TYPE, value, absolute))
4191 SetChangedFlag (true);
4192 break;
4194 case F_SelectedNames:
4195 if (ChangeSelectedSize (ELEMENTNAME_TYPE, value, absolute))
4196 SetChangedFlag (true);
4197 break;
4199 case F_SelectedElements:
4200 if (ChangeSelectedSize (ELEMENT_TYPE, value, absolute))
4201 SetChangedFlag (true);
4202 break;
4204 case F_Selected:
4205 case F_SelectedObjects:
4206 if (ChangeSelectedSize (CHANGESIZE_TYPES, value, absolute))
4207 SetChangedFlag (true);
4208 break;
4211 return 0;
4214 /* --------------------------------------------------------------------------- */
4216 static const char changedrillsize_syntax[] =
4217 N_("ChangeDrillSize(Object, delta)\n"
4218 "ChangeDrillSize(SelectedPins|SelectedVias|Selected|SelectedObjects, delta)");
4220 static const char changedrillsize_help[] =
4221 N_("Changes the drilling hole size of objects.");
4223 /* %start-doc actions ChangeDrillSize
4225 %end-doc */
4227 static int
4228 ActionChange2ndSize (int argc, char **argv, Coord x, Coord y)
4230 char *function = ARG (0);
4231 char *delta = ARG (1);
4232 char *units = ARG (2);
4233 bool absolute;
4234 Coord value;
4236 if (function && delta)
4238 value = GetValue (delta, units, &absolute);
4239 switch (GetFunctionID (function))
4241 case F_Object:
4243 int type;
4244 void *ptr1, *ptr2, *ptr3;
4246 gui->get_coords (_("Select an Object"), &x, &y);
4247 if ((type =
4248 SearchScreen (x, y, CHANGE2NDSIZE_TYPES,
4249 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4250 if (ChangeObject2ndSize
4251 (type, ptr1, ptr2, ptr3, value, absolute, true))
4252 SetChangedFlag (true);
4253 break;
4256 case F_SelectedVias:
4257 if (ChangeSelected2ndSize (VIA_TYPE, value, absolute))
4258 SetChangedFlag (true);
4259 break;
4261 case F_SelectedPins:
4262 if (ChangeSelected2ndSize (PIN_TYPE, value, absolute))
4263 SetChangedFlag (true);
4264 break;
4265 case F_Selected:
4266 case F_SelectedObjects:
4267 if (ChangeSelected2ndSize (PIN_TYPES, value, absolute))
4268 SetChangedFlag (true);
4269 break;
4272 return 0;
4275 /* --------------------------------------------------------------------------- */
4277 static const char changeclearsize_syntax[] =
4278 N_("ChangeClearSize(Object, delta)\n"
4279 "ChangeClearSize(SelectedPins|SelectedPads|SelectedVias, delta)\n"
4280 "ChangeClearSize(SelectedLines|SelectedArcs, delta\n"
4281 "ChangeClearSize(Selected|SelectedObjects, delta)");
4283 static const char changeclearsize_help[] =
4284 N_("Changes the clearance size of objects.");
4286 /* %start-doc actions ChangeClearSize
4288 If the solder mask is currently showing, this action changes the
4289 solder mask clearance. If the mask is not showing, this action
4290 changes the polygon clearance.
4292 %end-doc */
4294 static int
4295 ActionChangeClearSize (int argc, char **argv, Coord x, Coord y)
4297 char *function = ARG (0);
4298 char *delta = ARG (1);
4299 char *units = ARG (2);
4300 bool absolute;
4301 Coord value;
4303 if (function && delta)
4305 value = 2 * GetValue (delta, units, &absolute);
4306 if (value == 0)
4307 value = delta[0] == '-' ? -Settings.increments->clear
4308 : Settings.increments->clear;
4309 switch (GetFunctionID (function))
4311 case F_Object:
4313 int type;
4314 void *ptr1, *ptr2, *ptr3;
4316 gui->get_coords (_("Select an Object"), &x, &y);
4317 if ((type =
4318 SearchScreen (x, y,
4319 CHANGECLEARSIZE_TYPES, &ptr1, &ptr2,
4320 &ptr3)) != NO_TYPE)
4321 if (ChangeObjectClearSize (type, ptr1, ptr2, ptr3, value, absolute))
4322 SetChangedFlag (true);
4323 break;
4325 case F_SelectedVias:
4326 if (ChangeSelectedClearSize (VIA_TYPE, value, absolute))
4327 SetChangedFlag (true);
4328 break;
4329 case F_SelectedPads:
4330 if (ChangeSelectedClearSize (PAD_TYPE, value, absolute))
4331 SetChangedFlag (true);
4332 break;
4333 case F_SelectedPins:
4334 if (ChangeSelectedClearSize (PIN_TYPE, value, absolute))
4335 SetChangedFlag (true);
4336 break;
4337 case F_SelectedLines:
4338 if (ChangeSelectedClearSize (LINE_TYPE, value, absolute))
4339 SetChangedFlag (true);
4340 break;
4341 case F_SelectedArcs:
4342 if (ChangeSelectedClearSize (ARC_TYPE, value, absolute))
4343 SetChangedFlag (true);
4344 break;
4345 case F_Selected:
4346 case F_SelectedObjects:
4347 if (ChangeSelectedClearSize (CHANGECLEARSIZE_TYPES, value, absolute))
4348 SetChangedFlag (true);
4349 break;
4352 return 0;
4355 /* --------------------------------------------------------------------------- */
4357 static const char minmaskgap_syntax[] =
4358 N_("MinMaskGap(delta)\n"
4359 "MinMaskGap(Selected, delta)");
4361 static const char minmaskgap_help[] =
4362 N_("Ensures the mask is a minimum distance from pins and pads.");
4364 /* %start-doc actions MinMaskGap
4366 Checks all specified pins and/or pads, and increases the mask if
4367 needed to ensure a minimum distance between the pin or pad edge and
4368 the mask edge.
4370 %end-doc */
4372 static int
4373 ActionMinMaskGap (int argc, char **argv, Coord x, Coord y)
4375 char *function = ARG (0);
4376 char *delta = ARG (1);
4377 char *units = ARG (2);
4378 bool absolute;
4379 Coord value;
4380 Coord thickness;
4381 int flags;
4383 if (!function)
4384 return 1;
4385 if (strcasecmp (function, "Selected") == 0)
4386 flags = SELECTEDFLAG;
4387 else
4389 units = delta;
4390 delta = function;
4391 flags = 0;
4393 value = 2 * GetValue (delta, units, &absolute);
4395 SaveUndoSerialNumber ();
4396 ELEMENT_LOOP (PCB->Data);
4398 PIN_LOOP (element);
4400 if (!TEST_FLAGS (flags, pin) || ! pin->Mask)
4401 continue;
4403 thickness = pin->DrillingHole;
4405 if (pin->Thickness > thickness)
4406 thickness = pin->Thickness;
4408 thickness += value;
4410 if (pin->Mask < thickness)
4412 ChangeObjectMaskSize (PIN_TYPE, element, pin, 0, thickness, 1);
4413 RestoreUndoSerialNumber ();
4416 END_LOOP;
4417 PAD_LOOP (element);
4419 if (!TEST_FLAGS (flags, pad) || ! pad->Mask)
4420 continue;
4421 if (pad->Mask < pad->Thickness + value)
4423 ChangeObjectMaskSize (PAD_TYPE, element, pad, 0,
4424 pad->Thickness + value, 1);
4425 RestoreUndoSerialNumber ();
4428 END_LOOP;
4430 END_LOOP;
4431 VIA_LOOP (PCB->Data);
4433 if (!TEST_FLAGS (flags, via) || ! via->Mask)
4434 continue;
4436 thickness = via->DrillingHole;
4437 if (via->Thickness > thickness)
4438 thickness = via->Thickness;
4439 thickness += value;
4441 if (via->Mask < thickness)
4443 ChangeObjectMaskSize (VIA_TYPE, via, 0, 0, thickness, 1);
4444 RestoreUndoSerialNumber ();
4447 END_LOOP;
4448 RestoreUndoSerialNumber ();
4449 IncrementUndoSerialNumber ();
4450 return 0;
4453 /* --------------------------------------------------------------------------- */
4455 static const char mincleargap_syntax[] =
4456 N_("MinClearGap(delta)\n"
4457 "MinClearGap(Selected, delta)");
4459 static const char mincleargap_help[] =
4460 N_("Ensures that polygons are a minimum distance from objects.");
4462 /* %start-doc actions MinClearGap
4464 Checks all specified objects, and increases the polygon clearance if
4465 needed to ensure a minimum distance between their edges and the
4466 polygon edges.
4468 %end-doc */
4470 static int
4471 ActionMinClearGap (int argc, char **argv, Coord x, Coord y)
4473 char *function = ARG (0);
4474 char *delta = ARG (1);
4475 char *units = ARG (2);
4476 bool absolute;
4477 Coord value;
4478 int flags;
4480 if (!function)
4481 return 1;
4482 if (strcasecmp (function, "Selected") == 0)
4483 flags = SELECTEDFLAG;
4484 else
4486 units = delta;
4487 delta = function;
4488 flags = 0;
4490 value = 2 * GetValue (delta, units, &absolute);
4492 SaveUndoSerialNumber ();
4493 ELEMENT_LOOP (PCB->Data);
4495 PIN_LOOP (element);
4497 if (!TEST_FLAGS (flags, pin))
4498 continue;
4499 if (pin->Clearance < value)
4501 ChangeObjectClearSize (PIN_TYPE, element, pin, 0,
4502 value, 1);
4503 RestoreUndoSerialNumber ();
4506 END_LOOP;
4507 PAD_LOOP (element);
4509 if (!TEST_FLAGS (flags, pad))
4510 continue;
4511 if (pad->Clearance < value)
4513 ChangeObjectClearSize (PAD_TYPE, element, pad, 0,
4514 value, 1);
4515 RestoreUndoSerialNumber ();
4518 END_LOOP;
4520 END_LOOP;
4521 VIA_LOOP (PCB->Data);
4523 if (!TEST_FLAGS (flags, via))
4524 continue;
4525 if (via->Clearance < value)
4527 ChangeObjectClearSize (VIA_TYPE, via, 0, 0, value, 1);
4528 RestoreUndoSerialNumber ();
4531 END_LOOP;
4532 ALLLINE_LOOP (PCB->Data);
4534 if (!TEST_FLAGS (flags, line))
4535 continue;
4536 if (line->Clearance < value)
4538 ChangeObjectClearSize (LINE_TYPE, layer, line, 0, value, 1);
4539 RestoreUndoSerialNumber ();
4542 ENDALL_LOOP;
4543 ALLARC_LOOP (PCB->Data);
4545 if (!TEST_FLAGS (flags, arc))
4546 continue;
4547 if (arc->Clearance < value)
4549 ChangeObjectClearSize (ARC_TYPE, layer, arc, 0, value, 1);
4550 RestoreUndoSerialNumber ();
4553 ENDALL_LOOP;
4554 RestoreUndoSerialNumber ();
4555 IncrementUndoSerialNumber ();
4556 return 0;
4559 /* --------------------------------------------------------------------------- */
4561 static const char changepinname_syntax[] =
4562 N_("ChangePinName(ElementName,PinNumber,PinName)");
4564 static const char changepinname_help[] =
4565 N_("Sets the name of a specific pin on a specific element.");
4567 /* %start-doc actions ChangePinName
4569 This can be especially useful for annotating pin names from a
4570 schematic to the layout without requiring knowledge of the pcb file
4571 format.
4573 @example
4574 ChangePinName(U3, 7, VCC)
4575 @end example
4577 %end-doc */
4579 static int
4580 ActionChangePinName (int argc, char **argv, Coord x, Coord y)
4582 int changed = 0;
4583 char *refdes, *pinnum, *pinname;
4585 if (argc != 3)
4587 AFAIL (changepinname);
4590 refdes = argv[0];
4591 pinnum = argv[1];
4592 pinname = argv[2];
4594 ELEMENT_LOOP (PCB->Data);
4596 if (NSTRCMP (refdes, NAMEONPCB_NAME (element)) == 0)
4598 PIN_LOOP (element);
4600 if (NSTRCMP (pinnum, pin->Number) == 0)
4602 AddObjectToChangeNameUndoList (PIN_TYPE, NULL, NULL,
4603 pin, pin->Name);
4605 * Note: we can't free() pin->Name first because
4606 * it is used in the undo list
4608 pin->Name = strdup (pinname);
4609 SetChangedFlag (true);
4610 changed = 1;
4613 END_LOOP;
4615 PAD_LOOP (element);
4617 if (NSTRCMP (pinnum, pad->Number) == 0)
4619 AddObjectToChangeNameUndoList (PAD_TYPE, NULL, NULL,
4620 pad, pad->Name);
4622 * Note: we can't free() pad->Name first because
4623 * it is used in the undo list
4625 pad->Name = strdup (pinname);
4626 SetChangedFlag (true);
4627 changed = 1;
4630 END_LOOP;
4633 END_LOOP;
4635 * done with our action so increment the undo # if we actually
4636 * changed anything
4638 if (changed)
4640 if (defer_updates)
4641 defer_needs_update = 1;
4642 else
4644 IncrementUndoSerialNumber ();
4645 gui->invalidate_all ();
4649 return 0;
4652 /* --------------------------------------------------------------------------- */
4654 static const char changename_syntax[] =
4655 N_("ChangeName(Object)\n"
4656 "ChangeName(Layout|Layer)");
4658 static const char changename_help[] = N_("Sets the name of objects.");
4660 /* %start-doc actions ChangeName
4662 @table @code
4664 @item Object
4665 Changes the name of the element under the cursor.
4667 @item Layout
4668 Changes the name of the layout. This is printed on the fab drawings.
4670 @item Layer
4671 Changes the name of the currently active layer.
4673 @end table
4675 %end-doc */
4678 ActionChangeName (int argc, char **argv, Coord x, Coord y)
4680 char *function = ARG (0);
4681 char *name;
4683 if (function)
4685 switch (GetFunctionID (function))
4687 /* change the name of an object */
4688 case F_Object:
4690 int type;
4691 void *ptr1, *ptr2, *ptr3;
4693 gui->get_coords (_("Select an Object"), &x, &y);
4694 if ((type =
4695 SearchScreen (x, y, CHANGENAME_TYPES,
4696 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4698 SaveUndoSerialNumber ();
4699 if (QueryInputAndChangeObjectName (type, ptr1, ptr2, ptr3))
4701 SetChangedFlag (true);
4702 if (type == ELEMENT_TYPE)
4704 RubberbandType *ptr;
4705 int i;
4707 RestoreUndoSerialNumber ();
4708 Crosshair.AttachedObject.RubberbandN = 0;
4709 LookupRatLines (type, ptr1, ptr2, ptr3);
4710 ptr = Crosshair.AttachedObject.Rubberband;
4711 for (i = 0; i < Crosshair.AttachedObject.RubberbandN;
4712 i++, ptr++)
4714 if (PCB->RatOn)
4715 EraseRat ((RatType *) ptr->Line);
4716 MoveObjectToRemoveUndoList (RATLINE_TYPE,
4717 ptr->Line, ptr->Line,
4718 ptr->Line);
4720 IncrementUndoSerialNumber ();
4721 Draw ();
4725 break;
4728 /* change the layout's name */
4729 case F_Layout:
4730 name =
4731 gui->prompt_for (_("Enter the layout name:"), EMPTY (PCB->Name));
4732 /* NB: ChangeLayoutName takes ownership of the passed memory */
4733 if (name && ChangeLayoutName (name))
4734 SetChangedFlag (true);
4735 break;
4737 /* change the name of the active layer */
4738 case F_Layer:
4739 name = gui->prompt_for (_("Enter the layer name:"),
4740 EMPTY (CURRENT->Name));
4741 /* NB: ChangeLayerName takes ownership of the passed memory */
4742 if (name && ChangeLayerName (CURRENT, name))
4743 SetChangedFlag (true);
4744 break;
4747 return 0;
4751 /* --------------------------------------------------------------------------- */
4753 static const char morphpolygon_syntax[] = N_("MorphPolygon(Object|Selected)");
4755 static const char morphpolygon_help[] =
4756 N_("Converts dead polygon islands into separate polygons.");
4758 /* %start-doc actions MorphPolygon
4760 If a polygon is divided into unconnected "islands", you can use
4761 this command to convert the otherwise disappeared islands into
4762 separate polygons. Be sure the cursor is over a portion of the
4763 polygon that remains visible. Very small islands that may flake
4764 off are automatically deleted.
4766 %end-doc */
4768 static int
4769 ActionMorphPolygon (int argc, char **argv, Coord x, Coord y)
4771 char *function = ARG (0);
4772 if (function)
4774 switch (GetFunctionID (function))
4776 case F_Object:
4778 int type;
4779 void *ptr1, *ptr2, *ptr3;
4781 gui->get_coords (_("Select an Object"), &x, &y);
4782 if ((type = SearchScreen (x, y, POLYGON_TYPE,
4783 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4785 MorphPolygon ((LayerType *) ptr1, (PolygonType *) ptr3);
4786 Draw ();
4787 IncrementUndoSerialNumber ();
4789 break;
4791 case F_Selected:
4792 case F_SelectedObjects:
4793 ALLPOLYGON_LOOP (PCB->Data);
4795 if (TEST_FLAG (SELECTEDFLAG, polygon))
4796 MorphPolygon (layer, polygon);
4798 ENDALL_LOOP;
4799 Draw ();
4800 IncrementUndoSerialNumber ();
4801 break;
4804 return 0;
4807 /* --------------------------------------------------------------------------- */
4809 static const char togglehidename_syntax[] =
4810 N_("ToggleHideName(Object|SelectedElements)");
4812 static const char togglehidename_help[] =
4813 N_("Toggles the visibility of element names.");
4815 /* %start-doc actions ToggleHideName
4817 If names are hidden you won't see them on the screen and they will not
4818 appear on the silk layer when you print the layout.
4820 %end-doc */
4822 static int
4823 ActionToggleHideName (int argc, char **argv, Coord x, Coord y)
4825 char *function = ARG (0);
4826 if (function && PCB->ElementOn)
4828 switch (GetFunctionID (function))
4830 case F_Object:
4832 int type;
4833 void *ptr1, *ptr2, *ptr3;
4835 gui->get_coords (_("Select an Object"), &x, &y);
4836 if ((type = SearchScreen (x, y, ELEMENT_TYPE,
4837 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4839 AddObjectToFlagUndoList (type, ptr1, ptr2, ptr3);
4840 EraseElementName ((ElementType *) ptr2);
4841 TOGGLE_FLAG (HIDENAMEFLAG, (ElementType *) ptr2);
4842 DrawElementName ((ElementType *) ptr2);
4843 Draw ();
4844 IncrementUndoSerialNumber ();
4846 break;
4848 case F_SelectedElements:
4849 case F_Selected:
4851 bool changed = false;
4852 ELEMENT_LOOP (PCB->Data);
4854 if ((TEST_FLAG (SELECTEDFLAG, element) ||
4855 TEST_FLAG (SELECTEDFLAG,
4856 &NAMEONPCB_TEXT (element)))
4857 && (FRONT (element) || PCB->InvisibleObjectsOn))
4859 AddObjectToFlagUndoList (ELEMENT_TYPE, element,
4860 element, element);
4861 EraseElementName (element);
4862 TOGGLE_FLAG (HIDENAMEFLAG, element);
4863 DrawElementName (element);
4864 changed = true;
4867 END_LOOP;
4868 if (changed)
4870 Draw ();
4871 IncrementUndoSerialNumber ();
4876 return 0;
4879 /* --------------------------------------------------------------------------- */
4881 static const char changejoin_syntax[] =
4882 N_("ChangeJoin(ToggleObject|SelectedLines|SelectedArcs|Selected)");
4884 static const char changejoin_help[] =
4885 N_("Changes the join (clearance through polygons) of objects.");
4887 /* %start-doc actions ChangeJoin
4889 The join flag determines whether a line or arc, drawn to intersect a
4890 polygon, electrically connects to the polygon or not. When joined,
4891 the line/arc is simply drawn over the polygon, making an electrical
4892 connection. When not joined, a gap is drawn between the line and the
4893 polygon, insulating them from each other.
4895 %end-doc */
4897 static int
4898 ActionChangeJoin (int argc, char **argv, Coord x, Coord y)
4900 char *function = ARG (0);
4901 if (function)
4903 switch (GetFunctionID (function))
4905 case F_ToggleObject:
4906 case F_Object:
4908 int type;
4909 void *ptr1, *ptr2, *ptr3;
4911 gui->get_coords (_("Select an Object"), &x, &y);
4912 if ((type =
4913 SearchScreen (x, y, CHANGEJOIN_TYPES,
4914 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4915 if (ChangeObjectJoin (type, ptr1, ptr2, ptr3))
4916 SetChangedFlag (true);
4917 break;
4920 case F_SelectedLines:
4921 if (ChangeSelectedJoin (LINE_TYPE))
4922 SetChangedFlag (true);
4923 break;
4925 case F_SelectedArcs:
4926 if (ChangeSelectedJoin (ARC_TYPE))
4927 SetChangedFlag (true);
4928 break;
4930 case F_Selected:
4931 case F_SelectedObjects:
4932 if (ChangeSelectedJoin (CHANGEJOIN_TYPES))
4933 SetChangedFlag (true);
4934 break;
4937 return 0;
4940 /* --------------------------------------------------------------------------- */
4942 static const char changesquare_syntax[] =
4943 N_("ChangeSquare(ToggleObject)\n"
4944 "ChangeSquare(SelectedElements|SelectedPins)\n"
4945 "ChangeSquare(Selected|SelectedObjects)");
4947 static const char changesquare_help[] =
4948 N_("Changes the square flag of pins and pads.");
4950 /* %start-doc actions ChangeSquare
4952 Note that @code{Pins} means both pins and pads.
4954 @pinshapes
4956 %end-doc */
4958 static int
4959 ActionChangeSquare (int argc, char **argv, Coord x, Coord y)
4961 char *function = ARG (0);
4962 if (function)
4964 switch (GetFunctionID (function))
4966 case F_ToggleObject:
4967 case F_Object:
4969 int type;
4970 void *ptr1, *ptr2, *ptr3;
4972 gui->get_coords (_("Select an Object"), &x, &y);
4973 if ((type =
4974 SearchScreen (x, y, CHANGESQUARE_TYPES,
4975 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4976 if (ChangeObjectSquare (type, ptr1, ptr2, ptr3))
4977 SetChangedFlag (true);
4978 break;
4981 case F_SelectedElements:
4982 if (ChangeSelectedSquare (ELEMENT_TYPE))
4983 SetChangedFlag (true);
4984 break;
4986 case F_SelectedPins:
4987 if (ChangeSelectedSquare (PIN_TYPE | PAD_TYPE))
4988 SetChangedFlag (true);
4989 break;
4991 case F_Selected:
4992 case F_SelectedObjects:
4993 if (ChangeSelectedSquare (PIN_TYPE | PAD_TYPE))
4994 SetChangedFlag (true);
4995 break;
4998 return 0;
5001 /* --------------------------------------------------------------------------- */
5003 static const char setsquare_syntax[] =
5004 N_("SetSquare(ToggleObject|SelectedElements|SelectedPins)");
5006 static const char setsquare_help[] = N_("sets the square-flag of objects.");
5008 /* %start-doc actions SetSquare
5010 Note that @code{Pins} means pins and pads.
5012 @pinshapes
5014 %end-doc */
5016 static int
5017 ActionSetSquare (int argc, char **argv, Coord x, Coord y)
5019 char *function = ARG (0);
5020 if (function && *function)
5022 switch (GetFunctionID (function))
5024 case F_ToggleObject:
5025 case F_Object:
5027 int type;
5028 void *ptr1, *ptr2, *ptr3;
5030 gui->get_coords (_("Select an Object"), &x, &y);
5031 if ((type =
5032 SearchScreen (x, y, CHANGESQUARE_TYPES,
5033 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5034 if (SetObjectSquare (type, ptr1, ptr2, ptr3))
5035 SetChangedFlag (true);
5036 break;
5039 case F_SelectedElements:
5040 if (SetSelectedSquare (ELEMENT_TYPE))
5041 SetChangedFlag (true);
5042 break;
5044 case F_SelectedPins:
5045 if (SetSelectedSquare (PIN_TYPE | PAD_TYPE))
5046 SetChangedFlag (true);
5047 break;
5049 case F_Selected:
5050 case F_SelectedObjects:
5051 if (SetSelectedSquare (PIN_TYPE | PAD_TYPE))
5052 SetChangedFlag (true);
5053 break;
5056 return 0;
5059 /* --------------------------------------------------------------------------- */
5061 static const char clearsquare_syntax[] =
5062 N_("ClearSquare(ToggleObject|SelectedElements|SelectedPins)");
5064 static const char clearsquare_help[] =
5065 N_("Clears the square-flag of pins and pads.");
5067 /* %start-doc actions ClearSquare
5069 Note that @code{Pins} means pins and pads.
5071 @pinshapes
5073 %end-doc */
5075 static int
5076 ActionClearSquare (int argc, char **argv, Coord x, Coord y)
5078 char *function = ARG (0);
5079 if (function && *function)
5081 switch (GetFunctionID (function))
5083 case F_ToggleObject:
5084 case F_Object:
5086 int type;
5087 void *ptr1, *ptr2, *ptr3;
5089 gui->get_coords (_("Select an Object"), &x, &y);
5090 if ((type =
5091 SearchScreen (x, y, CHANGESQUARE_TYPES,
5092 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5093 if (ClrObjectSquare (type, ptr1, ptr2, ptr3))
5094 SetChangedFlag (true);
5095 break;
5098 case F_SelectedElements:
5099 if (ClrSelectedSquare (ELEMENT_TYPE))
5100 SetChangedFlag (true);
5101 break;
5103 case F_SelectedPins:
5104 if (ClrSelectedSquare (PIN_TYPE | PAD_TYPE))
5105 SetChangedFlag (true);
5106 break;
5108 case F_Selected:
5109 case F_SelectedObjects:
5110 if (ClrSelectedSquare (PIN_TYPE | PAD_TYPE))
5111 SetChangedFlag (true);
5112 break;
5115 return 0;
5118 /* --------------------------------------------------------------------------- */
5120 static const char changeoctagon_syntax[] =
5121 N_("ChangeOctagon(Object|ToggleObject|SelectedObjects|Selected)\n"
5122 "ChangeOctagon(SelectedElements|SelectedPins|SelectedVias)");
5124 static const char changeoctagon_help[] =
5125 N_("Changes the octagon-flag of pins and vias.");
5127 /* %start-doc actions ChangeOctagon
5129 @pinshapes
5131 %end-doc */
5133 static int
5134 ActionChangeOctagon (int argc, char **argv, Coord x, Coord y)
5136 char *function = ARG (0);
5137 if (function)
5139 switch (GetFunctionID (function))
5141 case F_ToggleObject:
5142 case F_Object:
5144 int type;
5145 void *ptr1, *ptr2, *ptr3;
5147 gui->get_coords (_("Select an Object"), &x, &y);
5148 if ((type =
5149 SearchScreen (x, y, CHANGEOCTAGON_TYPES,
5150 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5151 if (ChangeObjectOctagon (type, ptr1, ptr2, ptr3))
5152 SetChangedFlag (true);
5153 break;
5156 case F_SelectedElements:
5157 if (ChangeSelectedOctagon (ELEMENT_TYPE))
5158 SetChangedFlag (true);
5159 break;
5161 case F_SelectedPins:
5162 if (ChangeSelectedOctagon (PIN_TYPE))
5163 SetChangedFlag (true);
5164 break;
5166 case F_SelectedVias:
5167 if (ChangeSelectedOctagon (VIA_TYPE))
5168 SetChangedFlag (true);
5169 break;
5171 case F_Selected:
5172 case F_SelectedObjects:
5173 if (ChangeSelectedOctagon (PIN_TYPES))
5174 SetChangedFlag (true);
5175 break;
5178 return 0;
5181 /* --------------------------------------------------------------------------- */
5183 static const char setoctagon_syntax[] =
5184 N_("SetOctagon(Object|ToggleObject|SelectedElements|Selected)");
5186 static const char setoctagon_help[] = N_("Sets the octagon-flag of objects.");
5188 /* %start-doc actions SetOctagon
5190 @pinshapes
5192 %end-doc */
5194 static int
5195 ActionSetOctagon (int argc, char **argv, Coord x, Coord y)
5197 char *function = ARG (0);
5198 if (function)
5200 switch (GetFunctionID (function))
5202 case F_ToggleObject:
5203 case F_Object:
5205 int type;
5206 void *ptr1, *ptr2, *ptr3;
5208 gui->get_coords (_("Select an Object"), &x, &y);
5209 if ((type =
5210 SearchScreen (x, y, CHANGEOCTAGON_TYPES,
5211 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5212 if (SetObjectOctagon (type, ptr1, ptr2, ptr3))
5213 SetChangedFlag (true);
5214 break;
5217 case F_SelectedElements:
5218 if (SetSelectedOctagon (ELEMENT_TYPE))
5219 SetChangedFlag (true);
5220 break;
5222 case F_SelectedPins:
5223 if (SetSelectedOctagon (PIN_TYPE))
5224 SetChangedFlag (true);
5225 break;
5227 case F_SelectedVias:
5228 if (SetSelectedOctagon (VIA_TYPE))
5229 SetChangedFlag (true);
5230 break;
5232 case F_Selected:
5233 case F_SelectedObjects:
5234 if (SetSelectedOctagon (PIN_TYPES))
5235 SetChangedFlag (true);
5236 break;
5239 return 0;
5242 /* --------------------------------------------------------------------------- */
5244 static const char clearoctagon_syntax[] =
5245 N_("ClearOctagon(ToggleObject|Object|SelectedObjects|Selected)\n"
5246 "ClearOctagon(SelectedElements|SelectedPins|SelectedVias)");
5248 static const char clearoctagon_help[] =
5249 N_("Clears the octagon-flag of pins and vias.");
5251 /* %start-doc actions ClearOctagon
5253 @pinshapes
5255 %end-doc */
5257 static int
5258 ActionClearOctagon (int argc, char **argv, Coord x, Coord y)
5260 char *function = ARG (0);
5261 if (function)
5263 switch (GetFunctionID (function))
5265 case F_ToggleObject:
5266 case F_Object:
5268 int type;
5269 void *ptr1, *ptr2, *ptr3;
5271 gui->get_coords (_("Select an Object"), &x, &y);
5272 if ((type =
5273 SearchScreen (Crosshair.X, Crosshair.Y, CHANGEOCTAGON_TYPES,
5274 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5275 if (ClrObjectOctagon (type, ptr1, ptr2, ptr3))
5276 SetChangedFlag (true);
5277 break;
5280 case F_SelectedElements:
5281 if (ClrSelectedOctagon (ELEMENT_TYPE))
5282 SetChangedFlag (true);
5283 break;
5285 case F_SelectedPins:
5286 if (ClrSelectedOctagon (PIN_TYPE))
5287 SetChangedFlag (true);
5288 break;
5290 case F_SelectedVias:
5291 if (ClrSelectedOctagon (VIA_TYPE))
5292 SetChangedFlag (true);
5293 break;
5295 case F_Selected:
5296 case F_SelectedObjects:
5297 if (ClrSelectedOctagon (PIN_TYPES))
5298 SetChangedFlag (true);
5299 break;
5302 return 0;
5305 /* --------------------------------------------------------------------------- */
5307 static const char changehold_syntax[] =
5308 N_("ChangeHole(ToggleObject|Object|SelectedVias|Selected)");
5310 static const char changehold_help[] = N_("Changes the hole flag of objects.");
5312 /* %start-doc actions ChangeHole
5314 The "hole flag" of a via determines whether the via is a
5315 plated-through hole (not set), or an unplated hole (set).
5317 %end-doc */
5319 static int
5320 ActionChangeHole (int argc, char **argv, Coord x, Coord y)
5322 char *function = ARG (0);
5323 if (function)
5325 switch (GetFunctionID (function))
5327 case F_ToggleObject:
5328 case F_Object:
5330 int type;
5331 void *ptr1, *ptr2, *ptr3;
5333 gui->get_coords (_("Select an Object"), &x, &y);
5334 if ((type = SearchScreen (x, y, VIA_TYPE,
5335 &ptr1, &ptr2, &ptr3)) != NO_TYPE
5336 && ChangeHole ((PinType *) ptr3))
5337 IncrementUndoSerialNumber ();
5338 break;
5341 case F_SelectedVias:
5342 case F_Selected:
5343 if (ChangeSelectedHole ())
5344 SetChangedFlag (true);
5345 break;
5348 return 0;
5351 /* --------------------------------------------------------------------------- */
5353 static const char changepaste_syntax[] =
5354 N_("ChangePaste(ToggleObject|Object|SelectedPads|Selected)");
5356 static const char changepaste_help[] =
5357 N_("Changes the no paste flag of objects.");
5359 /* %start-doc actions ChangePaste
5361 The "no paste flag" of a pad determines whether the solderpaste
5362 stencil will have an opening for the pad (no set) or if there wil be
5363 no solderpaste on the pad (set). This is used for things such as
5364 fiducial pads.
5366 %end-doc */
5368 static int
5369 ActionChangePaste (int argc, char **argv, Coord x, Coord y)
5371 char *function = ARG (0);
5372 if (function)
5374 switch (GetFunctionID (function))
5376 case F_ToggleObject:
5377 case F_Object:
5379 int type;
5380 void *ptr1, *ptr2, *ptr3;
5382 gui->get_coords (_("Select an Object"), &x, &y);
5383 if ((type = SearchScreen (x, y, PAD_TYPE,
5384 &ptr1, &ptr2, &ptr3)) != NO_TYPE
5385 && ChangePaste ((PadType *) ptr3))
5386 IncrementUndoSerialNumber ();
5387 break;
5390 case F_SelectedPads:
5391 case F_Selected:
5392 if (ChangeSelectedPaste ())
5393 SetChangedFlag (true);
5394 break;
5397 return 0;
5400 /* --------------------------------------------------------------------------- */
5402 static const char select_syntax[] =
5403 N_("Select(Object|ToggleObject)\n"
5404 "Select(All|Block|Connection|BuriedVias)\n"
5405 "Select(ElementByName|ObjectByName|PadByName|PinByName)\n"
5406 "Select(ElementByName|ObjectByName|PadByName|PinByName, Name)\n"
5407 "Select(TextByName|ViaByName|NetByName)\n"
5408 "Select(TextByName|ViaByName|NetByName, Name)\n"
5409 "Select(Convert)");
5411 static const char select_help[] = N_("Toggles or sets the selection.");
5413 /* %start-doc actions Select
5415 @table @code
5417 @item ElementByName
5418 @item ObjectByName
5419 @item PadByName
5420 @item PinByName
5421 @item TextByName
5422 @item ViaByName
5423 @item NetByName
5425 These all rely on having a regular expression parser built into
5426 @code{pcb}. If the name is not specified then the user is prompted
5427 for a pattern, and all objects that match the pattern and are of the
5428 type specified are selected.
5430 @item Object
5431 @item ToggleObject
5432 Selects the object under the cursor.
5434 @item Block
5435 Selects all objects in a rectangle indicated by the cursor.
5437 @item All
5438 Selects all objects on the board.
5440 @item Found
5441 Selects all connections with the ``found'' flag set.
5443 @item Connection
5444 Selects all connections with the ``connected'' flag set.
5446 @item Connection
5447 Selects all blind and buried vias.
5449 @item Convert
5450 Converts the selected objects to an element. This uses the highest
5451 numbered paste buffer.
5453 @end table
5455 %end-doc */
5457 static int
5458 ActionSelect (int argc, char **argv, Coord x, Coord y)
5460 char *function = ARG (0);
5461 if (function)
5463 switch (GetFunctionID (function))
5465 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5466 int type;
5467 /* select objects by their names */
5468 case F_ElementByName:
5469 type = ELEMENT_TYPE;
5470 goto commonByName;
5471 case F_ObjectByName:
5472 type = ALL_TYPES;
5473 goto commonByName;
5474 case F_PadByName:
5475 type = PAD_TYPE;
5476 goto commonByName;
5477 case F_PinByName:
5478 type = PIN_TYPE;
5479 goto commonByName;
5480 case F_TextByName:
5481 type = TEXT_TYPE;
5482 goto commonByName;
5483 case F_ViaByName:
5484 type = VIA_TYPE;
5485 goto commonByName;
5486 case F_NetByName:
5487 type = NET_TYPE;
5488 goto commonByName;
5490 commonByName:
5492 char *pattern = ARG (1);
5494 if (pattern
5495 || (pattern =
5496 gui->prompt_for (_("Enter pattern:"), "")) != NULL)
5498 if (SelectObjectByName (type, pattern, true))
5499 SetChangedFlag (true);
5500 if (ARG (1) == NULL)
5501 free (pattern);
5503 break;
5505 #endif /* defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP) */
5507 /* select a single object */
5508 case F_ToggleObject:
5509 case F_Object:
5510 if (SelectObject ())
5511 SetChangedFlag (true);
5512 break;
5514 /* all objects in block */
5515 case F_Block:
5517 BoxType box;
5519 box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
5520 Crosshair.AttachedBox.Point2.X);
5521 box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
5522 Crosshair.AttachedBox.Point2.Y);
5523 box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
5524 Crosshair.AttachedBox.Point2.X);
5525 box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
5526 Crosshair.AttachedBox.Point2.Y);
5527 notify_crosshair_change (false);
5528 NotifyBlock ();
5529 if (Crosshair.AttachedBox.State == STATE_THIRD &&
5530 SelectBlock (&box, true))
5532 SetChangedFlag (true);
5533 Crosshair.AttachedBox.State = STATE_FIRST;
5535 notify_crosshair_change (true);
5536 break;
5539 /* select all visible objects */
5540 case F_All:
5542 BoxType box;
5544 box.X1 = -MAX_COORD;
5545 box.Y1 = -MAX_COORD;
5546 box.X2 = MAX_COORD;
5547 box.Y2 = MAX_COORD;
5548 if (SelectBlock (&box, true))
5549 SetChangedFlag (true);
5550 break;
5553 /* all logical connections */
5554 case F_Found:
5555 if (SelectByFlag (FOUNDFLAG, true))
5557 Draw ();
5558 IncrementUndoSerialNumber ();
5559 SetChangedFlag (true);
5561 break;
5563 /* all physical connections */
5564 case F_Connection:
5565 if (SelectByFlag (CONNECTEDFLAG, true))
5567 Draw ();
5568 IncrementUndoSerialNumber ();
5569 SetChangedFlag (true);
5571 break;
5573 case F_BuriedVias:
5574 if (SelectBuriedVias (true))
5576 Draw ();
5577 IncrementUndoSerialNumber ();
5578 SetChangedFlag (true);
5580 break;
5582 case F_Convert:
5584 Coord x, y;
5585 Note.Buffer = Settings.BufferNumber;
5586 SetBufferNumber (MAX_BUFFER - 1);
5587 ClearBuffer (PASTEBUFFER);
5588 gui->get_coords (_("Select the Element's Mark Location"), &x, &y);
5589 x = GridFit (x, PCB->Grid, PCB->GridOffsetX);
5590 y = GridFit (y, PCB->Grid, PCB->GridOffsetY);
5591 AddSelectedToBuffer (PASTEBUFFER, x, y, true);
5592 SaveUndoSerialNumber ();
5593 RemoveSelected ();
5594 ConvertBufferToElement (PASTEBUFFER);
5595 RestoreUndoSerialNumber ();
5596 CopyPastebufferToLayout (x, y);
5597 SetBufferNumber (Note.Buffer);
5599 break;
5601 default:
5602 AFAIL (select);
5603 break;
5606 return 0;
5609 /* FLAG(have_regex,FlagHaveRegex,0) */
5611 FlagHaveRegex (int parm)
5613 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5614 return 1;
5615 #else
5616 return 0;
5617 #endif
5620 /* --------------------------------------------------------------------------- */
5622 static const char unselect_syntax[] =
5623 N_("Unselect(All|Block|Connection)\n"
5624 "Unselect(ElementByName|ObjectByName|PadByName|PinByName)\n"
5625 "Unselect(ElementByName|ObjectByName|PadByName|PinByName, Name)\n"
5626 "Unselect(TextByName|ViaByName)\n"
5627 "Unselect(TextByName|ViaByName, Name)\n");
5629 static const char unselect_help[] =
5630 N_("Unselects the object at the pointer location or the specified objects.");
5632 /* %start-doc actions Unselect
5634 @table @code
5636 @item All
5637 Unselect all objects.
5639 @item Block
5640 Unselect all objects in a rectangle given by the cursor.
5642 @item Connection
5643 Unselect all connections with the ``found'' flag set.
5645 @item ElementByName
5646 @item ObjectByName
5647 @item PadByName
5648 @item PinByName
5649 @item TextByName
5650 @item ViaByName
5652 These all rely on having a regular expression parser built into
5653 @code{pcb}. If the name is not specified then the user is prompted
5654 for a pattern, and all objects that match the pattern and are of the
5655 type specified are unselected.
5658 @end table
5660 %end-doc */
5662 static int
5663 ActionUnselect (int argc, char **argv, Coord x, Coord y)
5665 char *function = ARG (0);
5666 if (function)
5668 switch (GetFunctionID (function))
5670 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5671 int type;
5672 /* select objects by their names */
5673 case F_ElementByName:
5674 type = ELEMENT_TYPE;
5675 goto commonByName;
5676 case F_ObjectByName:
5677 type = ALL_TYPES;
5678 goto commonByName;
5679 case F_PadByName:
5680 type = PAD_TYPE;
5681 goto commonByName;
5682 case F_PinByName:
5683 type = PIN_TYPE;
5684 goto commonByName;
5685 case F_TextByName:
5686 type = TEXT_TYPE;
5687 goto commonByName;
5688 case F_ViaByName:
5689 type = VIA_TYPE;
5690 goto commonByName;
5691 case F_NetByName:
5692 type = NET_TYPE;
5693 goto commonByName;
5695 commonByName:
5697 char *pattern = ARG (1);
5699 if (pattern
5700 || (pattern =
5701 gui->prompt_for (_("Enter pattern:"), "")) != NULL)
5703 if (SelectObjectByName (type, pattern, false))
5704 SetChangedFlag (true);
5705 if (ARG (1) == NULL)
5706 free (pattern);
5708 break;
5710 #endif /* defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP) */
5712 /* all objects in block */
5713 case F_Block:
5715 BoxType box;
5717 box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
5718 Crosshair.AttachedBox.Point2.X);
5719 box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
5720 Crosshair.AttachedBox.Point2.Y);
5721 box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
5722 Crosshair.AttachedBox.Point2.X);
5723 box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
5724 Crosshair.AttachedBox.Point2.Y);
5725 notify_crosshair_change (false);
5726 NotifyBlock ();
5727 if (Crosshair.AttachedBox.State == STATE_THIRD &&
5728 SelectBlock (&box, false))
5730 SetChangedFlag (true);
5731 Crosshair.AttachedBox.State = STATE_FIRST;
5733 notify_crosshair_change (true);
5734 break;
5737 /* unselect all visible objects */
5738 case F_All:
5740 BoxType box;
5742 box.X1 = -MAX_COORD;
5743 box.Y1 = -MAX_COORD;
5744 box.X2 = MAX_COORD;
5745 box.Y2 = MAX_COORD;
5746 if (SelectBlock (&box, false))
5747 SetChangedFlag (true);
5748 break;
5751 /* all logical connections */
5752 case F_Found:
5753 if (SelectByFlag (FOUNDFLAG, false))
5755 Draw ();
5756 IncrementUndoSerialNumber ();
5757 SetChangedFlag (true);
5759 break;
5761 /* all physical connections */
5762 case F_Connection:
5763 if (SelectByFlag (CONNECTEDFLAG, false))
5765 Draw ();
5766 IncrementUndoSerialNumber ();
5767 SetChangedFlag (true);
5769 break;
5771 default:
5772 AFAIL (unselect);
5773 break;
5777 return 0;
5780 /* --------------------------------------------------------------------------- */
5782 static const char saveto_syntax[] =
5783 N_("SaveTo(Layout|LayoutAs,filename)\n"
5784 "SaveTo(AllConnections|AllUnusedPins|ElementConnections,filename)\n"
5785 "SaveTo(PasteBuffer,filename)");
5787 static const char saveto_help[] = N_("Saves data to a file.");
5789 /* %start-doc actions SaveTo
5791 @table @code
5793 @item Layout
5794 Saves the current layout.
5796 @item LayoutAs
5797 Saves the current layout, and remembers the filename used.
5799 @item AllConnections
5800 Save all connections to a file.
5802 @item AllUnusedPins
5803 List all unused pins to a file.
5805 @item ElementConnections
5806 Save connections to the element at the cursor to a file.
5808 @item PasteBuffer
5809 Save the content of the active Buffer to a file. This is the graphical way to create a footprint.
5811 @end table
5813 %end-doc */
5815 static int
5816 ActionSaveTo (int argc, char **argv, Coord x, Coord y)
5818 char *function;
5819 char *name;
5821 function = ARG (0);
5823 if ( ! function || strcasecmp (function, "Layout") == 0)
5825 if (SavePCB (PCB->Filename) == 0)
5826 SetChangedFlag (false);
5827 return 0;
5830 if (argc != 2)
5831 AFAIL (saveto);
5833 name = argv[1];
5835 if (strcasecmp (function, "LayoutAs") == 0)
5837 if (SavePCB (name) == 0)
5839 SetChangedFlag (false);
5840 free (PCB->Filename);
5841 PCB->Filename = strdup (name);
5842 if (gui->notify_filename_changed != NULL)
5843 gui->notify_filename_changed ();
5845 return 0;
5848 if (strcasecmp (function, "AllConnections") == 0)
5850 FILE *fp;
5851 bool result;
5852 if ((fp = CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
5854 LookupConnectionsToAllElements (fp);
5855 fclose (fp);
5856 SetChangedFlag (true);
5858 return 0;
5861 if (strcasecmp (function, "AllUnusedPins") == 0)
5863 FILE *fp;
5864 bool result;
5865 if ((fp = CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
5867 LookupUnusedPins (fp);
5868 fclose (fp);
5869 SetChangedFlag (true);
5871 return 0;
5874 if (strcasecmp (function, "ElementConnections") == 0)
5876 ElementType *element;
5877 void *ptrtmp;
5878 FILE *fp;
5879 bool result;
5881 if ((SearchScreen (Crosshair.X, Crosshair.Y, ELEMENT_TYPE,
5882 &ptrtmp, &ptrtmp, &ptrtmp)) != NO_TYPE)
5884 element = (ElementType *) ptrtmp;
5885 if ((fp =
5886 CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
5888 LookupElementConnections (element, fp);
5889 fclose (fp);
5890 SetChangedFlag (true);
5893 return 0;
5896 if (strcasecmp (function, "PasteBuffer") == 0)
5898 return SaveBufferElements (name);
5901 AFAIL (saveto);
5904 /* --------------------------------------------------------------------------- */
5906 static const char savesettings_syntax[] =
5907 N_("SaveSettings()\n"
5908 "SaveSettings(local)");
5910 static const char savesettings_help[] = N_("Saves settings.");
5912 /* %start-doc actions SaveSettings
5914 If you pass no arguments, the settings are stored in
5915 @code{$HOME/.pcb/settings}. If you pass the word @code{local} they're
5916 saved in @code{./pcb.settings}.
5918 %end-doc */
5920 static int
5921 ActionSaveSettings (int argc, char **argv, Coord x, Coord y)
5923 int locally = argc > 0 ? (strncasecmp (argv[0], "local", 5) == 0) : 0;
5924 hid_save_settings (locally);
5925 return 0;
5928 /* --------------------------------------------------------------------------- */
5930 static const char loadfrom_syntax[] =
5931 N_("LoadFrom(Layout|LayoutToBuffer|ElementToBuffer|STEPOutlineToBufferLines|STEPOutlineToBufferPolys|Netlist|Revert,filename)");
5933 static const char loadfrom_help[] = N_("Load layout data from a file.");
5935 /* %start-doc actions LoadFrom
5937 This action assumes you know what the filename is. The various GUIs
5938 should have a similar @code{Load} action where the filename is
5939 optional, and will provide their own file selection mechanism to let
5940 you choose the file name.
5942 @table @code
5944 @item Layout
5945 Loads an entire PCB layout, replacing the current one.
5947 @item LayoutToBuffer
5948 Loads an entire PCB layout to the paste buffer.
5950 @item ElementToBuffer
5951 Loads the given element file into the paste buffer. Element files
5952 contain only a single @code{Element} definition, such as the
5953 ``newlib'' library uses.
5955 @item Netlist
5956 Loads a new netlist, replacing any current netlist.
5958 @item Revert
5959 Re-loads the current layout from its disk file, reverting any changes
5960 you may have made.
5962 @end table
5964 %end-doc */
5966 static int
5967 ActionLoadFrom (int argc, char **argv, Coord x, Coord y)
5969 char *function;
5970 char *name;
5972 if (argc < 2)
5973 AFAIL (loadfrom);
5975 function = argv[0];
5976 name = argv[1];
5978 if (strcasecmp (function, "ElementToBuffer") == 0)
5980 notify_crosshair_change (false);
5981 if (LoadElementToBuffer (PASTEBUFFER, name, true))
5982 SetMode (PASTEBUFFER_MODE);
5983 notify_crosshair_change (true);
5986 else if (strcasecmp (function, "LayoutToBuffer") == 0)
5988 notify_crosshair_change (false);
5989 if (LoadLayoutToBuffer (PASTEBUFFER, name))
5990 SetMode (PASTEBUFFER_MODE);
5991 notify_crosshair_change (true);
5994 else if (strcasecmp (function, "STEPOutlineTobufferLines") == 0)
5996 notify_crosshair_change (false);
5997 if (!hid_actionl ("LoadSTEPOutlineToBuffer", name, NULL))
5998 SetMode (PASTEBUFFER_MODE);
5999 notify_crosshair_change (true);
6002 else if (strcasecmp (function, "STEPOutlineTobufferPolys") == 0)
6004 notify_crosshair_change (false);
6005 if (!hid_actionl ("LoadSTEPOutlineToBuffer", name, "polys", NULL))
6006 SetMode (PASTEBUFFER_MODE);
6007 notify_crosshair_change (true);
6010 else if (strcasecmp (function, "Layout") == 0)
6012 if (!PCB->Changed ||
6013 gui->confirm_dialog (_("OK to override layout data?"), 0))
6014 LoadPCB (name);
6017 else if (strcasecmp (function, "Netlist") == 0)
6019 if (PCB->Netlistname)
6020 free (PCB->Netlistname);
6021 PCB->Netlistname = StripWhiteSpaceAndDup (name);
6022 FreeLibraryMemory (&PCB->NetlistLib);
6023 ImportNetlist (PCB->Netlistname);
6024 NetlistChanged (1);
6026 else if (strcasecmp (function, "Revert") == 0 && PCB->Filename
6027 && (!PCB->Changed
6028 || gui->confirm_dialog (_("OK to override changes?"), 0)))
6030 RevertPCB ();
6033 return 0;
6036 /* --------------------------------------------------------------------------- */
6038 static const char new_syntax[] = N_("New([name])");
6040 static const char new_help[] = N_("Starts a new layout.");
6042 /* %start-doc actions New
6044 If a name is not given, one is prompted for.
6046 %end-doc */
6048 static int
6049 ActionNew (int argc, char **argv, Coord x, Coord y)
6051 char *name = ARG (0);
6053 if (!PCB->Changed || gui->confirm_dialog (_("OK to clear layout data?"), 0))
6055 if (name)
6056 name = strdup (name);
6057 else
6058 name = gui->prompt_for (_("Enter the layout name:"), "");
6060 if (!name)
6061 return 1;
6063 notify_crosshair_change (false);
6064 /* do emergency saving
6065 * clear the old struct and allocate memory for the new one
6067 if (PCB->Changed && Settings.SaveInTMP)
6068 SaveInTMP ();
6069 RemovePCB (PCB);
6070 PCB = NULL;
6071 PCB = CreateNewPCB ();
6072 CreateNewPCBPost (PCB, 1);
6074 /* setup the new name and reset some values to default */
6075 free (PCB->Name);
6076 PCB->Name = name;
6078 ResetStackAndVisibility ();
6079 SetCrosshairRange (0, 0, PCB->MaxWidth, PCB->MaxHeight);
6080 CenterDisplay (PCB->MaxWidth / 2, PCB->MaxHeight / 2, false);
6081 Redraw ();
6083 hid_action ("PCBChanged");
6084 notify_crosshair_change (true);
6085 return 0;
6087 return 1;
6091 * \brief No operation, just for testing purposes.
6092 * syntax: Bell(volume)
6094 void
6095 ActionBell (char *volume)
6097 gui->beep ();
6100 /* --------------------------------------------------------------------------- */
6102 static const char pastebuffer_syntax[] =
6103 N_("PasteBuffer(AddSelected|Clear|1..MAX_BUFFER)\n"
6104 "PasteBuffer(Rotate, 1..3)\n"
6105 "PasteBuffer(Convert|Save|Restore|Mirror)\n"
6106 "PasteBuffer(ToLayout, X, Y, units)");
6108 static const char pastebuffer_help[] =
6109 N_("Various operations on the paste buffer.");
6111 /* %start-doc actions PasteBuffer
6113 There are a number of paste buffers; the actual limit is a
6114 compile-time constant @code{MAX_BUFFER} in @file{globalconst.h}. It
6115 is currently @code{5}. One of these is the ``current'' paste buffer,
6116 often referred to as ``the'' paste buffer.
6118 @table @code
6120 @item AddSelected
6121 Copies the selected objects to the current paste buffer.
6123 @item Clear
6124 Remove all objects from the current paste buffer.
6126 @item Convert
6127 Convert the current paste buffer to an element. Vias are converted to
6128 pins, lines are converted to pads.
6130 @item Restore
6131 Convert any elements in the paste buffer back to vias and lines.
6133 @item Mirror
6134 Flip all objects in the paste buffer vertically (up/down flip). To mirror
6135 horizontally, combine this with rotations.
6137 @item Rotate
6138 Rotates the current buffer. The number to pass is 1..3, where 1 means
6139 90 degrees counter clockwise, 2 means 180 degrees, and 3 means 90
6140 degrees clockwise (270 CCW).
6142 @item Save
6143 Saves any elements in the current buffer to the indicated file.
6145 @item ToLayout
6146 Pastes any elements in the current buffer to the indicated X, Y
6147 coordinates in the layout. The @code{X} and @code{Y} are treated like
6148 @code{delta} is for many other objects. For each, if it's prefixed by
6149 @code{+} or @code{-}, then that amount is relative to the last
6150 location. Otherwise, it's absolute. Units can be
6151 @code{mil} or @code{mm}; if unspecified, units are PCB's internal
6152 units, currently 1/100 mil.
6155 @item 1..MAX_BUFFER
6156 Selects the given buffer to be the current paste buffer.
6158 @end table
6160 %end-doc */
6162 static int
6163 ActionPasteBuffer (int argc, char **argv, Coord x, Coord y)
6165 char *function = argc ? argv[0] : (char *)"";
6166 char *sbufnum = argc > 1 ? argv[1] : (char *)"";
6167 char *name;
6168 static char *default_file = NULL;
6169 int free_name = 0;
6171 notify_crosshair_change (false);
6172 if (function)
6174 switch (GetFunctionID (function))
6176 /* clear contents of paste buffer */
6177 case F_Clear:
6178 ClearBuffer (PASTEBUFFER);
6179 break;
6181 /* copies objects to paste buffer */
6182 case F_AddSelected:
6183 AddSelectedToBuffer (PASTEBUFFER, 0, 0, false);
6184 break;
6186 /* converts buffer contents into an element */
6187 case F_Convert:
6188 ConvertBufferToElement (PASTEBUFFER);
6189 break;
6191 /* break up element for editing */
6192 case F_Restore:
6193 SmashBufferElement (PASTEBUFFER);
6194 break;
6196 /* Mirror buffer */
6197 case F_Mirror:
6198 MirrorBuffer (PASTEBUFFER);
6199 break;
6201 case F_Rotate:
6202 if (sbufnum)
6204 RotateBuffer (PASTEBUFFER, (BYTE) atoi (sbufnum));
6205 SetCrosshairRangeToBuffer ();
6207 break;
6209 case F_Save:
6210 if (PASTEBUFFER->Data->ElementN == 0)
6212 Message (_("Buffer has no elements!\n"));
6213 break;
6215 free_name = 0;
6216 if (argc <= 1)
6218 name = gui->fileselect (_("Save Paste Buffer As ..."),
6219 _("Choose a file to save the contents of the\n"
6220 "paste buffer to.\n"),
6221 default_file, ".fp", "footprint",
6224 if (default_file)
6226 free (default_file);
6227 default_file = NULL;
6229 if ( name && *name)
6231 default_file = strdup (name);
6233 free_name = 1;
6236 else
6237 name = argv[1];
6240 FILE *exist;
6242 if ((exist = fopen (name, "r")))
6244 fclose (exist);
6245 if (gui->
6246 confirm_dialog (_("File exists! Ok to overwrite?"), 0))
6247 SaveBufferElements (name);
6249 else
6250 SaveBufferElements (name);
6252 if (free_name && name)
6253 free (name);
6255 break;
6257 case F_ToLayout:
6259 static Coord oldx = 0, oldy = 0;
6260 Coord x, y;
6261 bool absolute;
6263 if (argc == 1)
6265 x = y = 0;
6267 else if (argc == 3 || argc == 4)
6269 x = GetValue (ARG (1), ARG (3), &absolute);
6270 if (!absolute)
6271 x += oldx;
6272 y = GetValue (ARG (2), ARG (3), &absolute);
6273 if (!absolute)
6274 y += oldy;
6276 else
6278 notify_crosshair_change (true);
6279 AFAIL (pastebuffer);
6282 oldx = x;
6283 oldy = y;
6284 if (CopyPastebufferToLayout (x, y))
6285 SetChangedFlag (true);
6287 break;
6289 /* set number */
6290 default:
6292 int number = atoi (function);
6294 /* correct number */
6295 if (number)
6296 SetBufferNumber (number - 1);
6301 notify_crosshair_change (true);
6302 return 0;
6305 /* --------------------------------------------------------------------------- */
6307 static const char undo_syntax[] = N_("Undo()\n"
6308 "Undo(ClearList)");
6310 static const char undo_help[] = N_("Undo recent changes.");
6312 /* %start-doc actions Undo
6314 The unlimited undo feature of @code{Pcb} allows you to recover from
6315 most operations that materially affect you work. Calling
6316 @code{Undo()} without any parameter recovers from the last (non-undo)
6317 operation. @code{ClearList} is used to release the allocated
6318 memory. @code{ClearList} is called whenever a new layout is started or
6319 loaded. See also @code{Redo} and @code{Atomic}.
6321 Note that undo groups operations by serial number; changes with the
6322 same serial number will be undone (or redone) as a group. See
6323 @code{Atomic}.
6325 %end-doc */
6327 static int
6328 ActionUndo (int argc, char **argv, Coord x, Coord y)
6330 char *function = ARG (0);
6331 if (!function || !*function)
6333 /* don't allow undo in the middle of an operation */
6334 if (Settings.Mode != POLYGONHOLE_MODE &&
6335 Crosshair.AttachedObject.State != STATE_FIRST)
6336 return 1;
6337 if (Crosshair.AttachedBox.State != STATE_FIRST
6338 && Settings.Mode != ARC_MODE)
6339 return 1;
6340 /* undo the last operation */
6342 notify_crosshair_change (false);
6343 if ((Settings.Mode == POLYGON_MODE ||
6344 Settings.Mode == POLYGONHOLE_MODE) &&
6345 Crosshair.AttachedPolygon.PointN)
6347 GoToPreviousPoint ();
6348 notify_crosshair_change (true);
6349 return 0;
6351 /* move anchor point if undoing during line creation */
6352 if (Settings.Mode == LINE_MODE)
6354 if (Crosshair.AttachedLine.State == STATE_SECOND)
6356 if (TEST_FLAG (AUTODRCFLAG, PCB))
6357 Undo (true); /* undo the connection find */
6358 Crosshair.AttachedLine.State = STATE_FIRST;
6359 SetLocalRef (0, 0, false);
6360 notify_crosshair_change (true);
6361 return 0;
6363 if (Crosshair.AttachedLine.State == STATE_THIRD)
6365 int type;
6366 void *ptr1, *ptr3, *ptrtmp;
6367 LineType *ptr2;
6368 /* this search is guaranteed to succeed */
6369 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6370 &ptrtmp, &ptr3,
6371 Crosshair.AttachedLine.Point1.X,
6372 Crosshair.AttachedLine.Point1.Y, 0);
6373 ptr2 = (LineType *) ptrtmp;
6375 /* save both ends of line */
6376 Crosshair.AttachedLine.Point2.X = ptr2->Point1.X;
6377 Crosshair.AttachedLine.Point2.Y = ptr2->Point1.Y;
6378 if ((type = Undo (true)))
6379 SetChangedFlag (true);
6380 /* check that the undo was of the right type */
6381 if ((type & UNDO_CREATE) == 0)
6383 /* wrong undo type, restore anchor points */
6384 Crosshair.AttachedLine.Point2.X =
6385 Crosshair.AttachedLine.Point1.X;
6386 Crosshair.AttachedLine.Point2.Y =
6387 Crosshair.AttachedLine.Point1.Y;
6388 notify_crosshair_change (true);
6389 return 0;
6391 /* move to new anchor */
6392 Crosshair.AttachedLine.Point1.X =
6393 Crosshair.AttachedLine.Point2.X;
6394 Crosshair.AttachedLine.Point1.Y =
6395 Crosshair.AttachedLine.Point2.Y;
6396 /* check if an intermediate point was removed */
6397 if (type & UNDO_REMOVE)
6399 /* this search should find the restored line */
6400 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6401 &ptrtmp,
6402 &ptr3,
6403 Crosshair.AttachedLine.Point2.X,
6404 Crosshair.AttachedLine.Point2.Y, 0);
6405 ptr2 = (LineType *) ptrtmp;
6406 if (TEST_FLAG (AUTODRCFLAG, PCB))
6408 /* undo loses CONNECTEDFLAG and FOUNDFLAG */
6409 SET_FLAG(CONNECTEDFLAG, ptr2);
6410 SET_FLAG(FOUNDFLAG, ptr2);
6411 DrawLine (CURRENT, ptr2);
6413 Crosshair.AttachedLine.Point1.X =
6414 Crosshair.AttachedLine.Point2.X = ptr2->Point2.X;
6415 Crosshair.AttachedLine.Point1.Y =
6416 Crosshair.AttachedLine.Point2.Y = ptr2->Point2.Y;
6418 FitCrosshairIntoGrid (Crosshair.X, Crosshair.Y);
6419 AdjustAttachedObjects ();
6420 if (--addedLines == 0)
6422 Crosshair.AttachedLine.State = STATE_SECOND;
6423 lastLayer = CURRENT;
6425 else
6427 /* this search is guaranteed to succeed too */
6428 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6429 &ptrtmp,
6430 &ptr3,
6431 Crosshair.AttachedLine.Point1.X,
6432 Crosshair.AttachedLine.Point1.Y, 0);
6433 ptr2 = (LineType *) ptrtmp;
6434 lastLayer = (LayerType *) ptr1;
6436 notify_crosshair_change (true);
6437 return 0;
6440 if (Settings.Mode == ARC_MODE)
6442 if (Crosshair.AttachedBox.State == STATE_SECOND)
6444 Crosshair.AttachedBox.State = STATE_FIRST;
6445 notify_crosshair_change (true);
6446 return 0;
6448 if (Crosshair.AttachedBox.State == STATE_THIRD)
6450 void *ptr1, *ptr2, *ptr3;
6451 BoxType *bx;
6452 /* guaranteed to succeed */
6453 SearchObjectByLocation (ARC_TYPE, &ptr1, &ptr2, &ptr3,
6454 Crosshair.AttachedBox.Point1.X,
6455 Crosshair.AttachedBox.Point1.Y, 0);
6456 bx = GetArcEnds ((ArcType *) ptr2);
6457 Crosshair.AttachedBox.Point1.X =
6458 Crosshair.AttachedBox.Point2.X = bx->X1;
6459 Crosshair.AttachedBox.Point1.Y =
6460 Crosshair.AttachedBox.Point2.Y = bx->Y1;
6461 AdjustAttachedObjects ();
6462 if (--addedLines == 0)
6463 Crosshair.AttachedBox.State = STATE_SECOND;
6466 /* undo the last destructive operation */
6467 if (Undo (true))
6468 SetChangedFlag (true);
6470 else if (function)
6472 switch (GetFunctionID (function))
6474 /* clear 'undo objects' list */
6475 case F_ClearList:
6476 ClearUndoList (false);
6477 break;
6480 notify_crosshair_change (true);
6481 return 0;
6484 /* --------------------------------------------------------------------------- */
6486 static const char redo_syntax[] = N_("Redo()");
6488 static const char redo_help[] = N_("Redo recent \"undo\" operations.");
6490 /* %start-doc actions Redo
6492 This routine allows you to recover from the last undo command. You
6493 might want to do this if you thought that undo was going to revert
6494 something other than what it actually did (in case you are confused
6495 about which operations are un-doable), or if you have been backing up
6496 through a long undo list and over-shoot your stopping point. Any
6497 change that is made since the undo in question will trim the redo
6498 list. For example if you add ten lines, then undo three of them you
6499 could use redo to put them back, but if you move a line on the board
6500 before performing the redo, you will lose the ability to "redo" the
6501 three "undone" lines.
6503 %end-doc */
6505 static int
6506 ActionRedo (int argc, char **argv, Coord x, Coord y)
6508 if (((Settings.Mode == POLYGON_MODE ||
6509 Settings.Mode == POLYGONHOLE_MODE) &&
6510 Crosshair.AttachedPolygon.PointN) ||
6511 Crosshair.AttachedLine.State == STATE_SECOND)
6512 return 1;
6513 notify_crosshair_change (false);
6514 if (Redo (true))
6516 SetChangedFlag (true);
6517 if (Settings.Mode == LINE_MODE &&
6518 Crosshair.AttachedLine.State != STATE_FIRST)
6520 LineType *line = g_list_last (CURRENT->Line)->data;
6521 Crosshair.AttachedLine.Point1.X =
6522 Crosshair.AttachedLine.Point2.X = line->Point2.X;
6523 Crosshair.AttachedLine.Point1.Y =
6524 Crosshair.AttachedLine.Point2.Y = line->Point2.Y;
6525 addedLines++;
6528 notify_crosshair_change (true);
6529 return 0;
6532 /* --------------------------------------------------------------------------- */
6534 static const char polygon_syntax[] = N_("Polygon(Close|PreviousPoint)");
6536 static const char polygon_help[] = N_("Some polygon related stuff.");
6538 /* %start-doc actions Polygon
6540 Polygons need a special action routine to make life easier.
6542 @table @code
6544 @item Close
6545 Creates the final segment of the polygon. This may fail if clipping
6546 to 45 degree lines is switched on, in which case a warning is issued.
6548 @item PreviousPoint
6549 Resets the newly entered corner to the previous one. The Undo action
6550 will call Polygon(PreviousPoint) when appropriate to do so.
6552 @end table
6554 %end-doc */
6556 static int
6557 ActionPolygon (int argc, char **argv, Coord x, Coord y)
6559 char *function = ARG (0);
6560 if (function && Settings.Mode == POLYGON_MODE)
6562 notify_crosshair_change (false);
6563 switch (GetFunctionID (function))
6565 /* close open polygon if possible */
6566 case F_Close:
6567 ClosePolygon ();
6568 break;
6570 /* go back to the previous point */
6571 case F_PreviousPoint:
6572 GoToPreviousPoint ();
6573 break;
6575 notify_crosshair_change (true);
6577 return 0;
6580 /* --------------------------------------------------------------------------- */
6582 static const char routestyle_syntax[] = N_("RouteStyle(1|2|3|4)");
6584 static const char routestyle_help[] =
6585 N_("Copies the indicated routing style into the current sizes.");
6587 /* %start-doc actions RouteStyle
6589 %end-doc */
6591 static int
6592 ActionRouteStyle (int argc, char **argv, Coord x, Coord y)
6594 char *str = ARG (0);
6595 RouteStyleType *rts;
6596 int number;
6598 if (str)
6600 number = atoi (str);
6601 if (number > 0 && number <= NUM_STYLES)
6603 rts = &PCB->RouteStyle[number - 1];
6604 SetLineSize (rts->Thick);
6605 SetViaSize (rts->Diameter, true);
6606 SetViaDrillingHole (rts->Hole, true);
6607 SetKeepawayWidth (rts->Keepaway);
6608 hid_action("RouteStylesChanged");
6611 return 0;
6615 /* --------------------------------------------------------------------------- */
6617 static const char moveobject_syntax[] = N_("MoveObject(X,Y,dim)");
6619 static const char moveobject_help[] =
6620 N_("Moves the object under the crosshair.");
6622 /* %start-doc actions MoveObject
6624 The @code{X} and @code{Y} are treated like @code{delta} is for many
6625 other objects. For each, if it's prefixed by @code{+} or @code{-},
6626 then that amount is relative. Otherwise, it's absolute. Units can be
6627 @code{mil} or @code{mm}; if unspecified, units are PCB's internal
6628 units, currently 1/100 mil.
6630 %end-doc */
6632 static int
6633 ActionMoveObject (int argc, char **argv, Coord x, Coord y)
6635 char *x_str = ARG (0);
6636 char *y_str = ARG (1);
6637 char *units = ARG (2);
6638 Coord nx, ny;
6639 bool absolute1, absolute2;
6640 void *ptr1, *ptr2, *ptr3;
6641 int type;
6643 ny = GetValue (y_str, units, &absolute1);
6644 nx = GetValue (x_str, units, &absolute2);
6646 type = SearchScreen (x, y, MOVE_TYPES, &ptr1, &ptr2, &ptr3);
6647 if (type == NO_TYPE)
6649 Message (_("Nothing found under crosshair\n"));
6650 return 1;
6652 if (absolute1)
6653 nx -= x;
6654 if (absolute2)
6655 ny -= y;
6656 Crosshair.AttachedObject.RubberbandN = 0;
6657 if (TEST_FLAG (RUBBERBANDFLAG, PCB))
6658 LookupRubberbandLines (type, ptr1, ptr2, ptr3);
6659 if (type == ELEMENT_TYPE)
6660 LookupRatLines (type, ptr1, ptr2, ptr3);
6661 MoveObjectAndRubberband (type, ptr1, ptr2, ptr3, nx, ny);
6662 SetChangedFlag (true);
6663 return 0;
6666 /* --------------------------------------------------------------------------- */
6668 static const char movetocurrentlayer_syntax[] =
6669 N_("MoveToCurrentLayer(Object|SelectedObjects)");
6671 static const char movetocurrentlayer_help[] =
6672 N_("Moves objects to the current layer.");
6674 /* %start-doc actions MoveToCurrentLayer
6676 Note that moving an element from a component layer to a solder layer,
6677 or from solder to component, won't automatically flip it. Use the
6678 @code{Flip()} action to do that.
6680 %end-doc */
6682 static int
6683 ActionMoveToCurrentLayer (int argc, char **argv, Coord x, Coord y)
6685 char *function = ARG (0);
6686 if (function)
6688 switch (GetFunctionID (function))
6690 case F_Object:
6692 int type;
6693 void *ptr1, *ptr2, *ptr3;
6695 gui->get_coords (_("Select an Object"), &x, &y);
6696 if ((type =
6697 SearchScreen (x, y, MOVETOLAYER_TYPES,
6698 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
6699 if (MoveObjectToLayer (type, ptr1, ptr2, ptr3, CURRENT, false))
6700 SetChangedFlag (true);
6701 break;
6704 case F_SelectedObjects:
6705 case F_Selected:
6706 if (MoveSelectedObjectsToLayer (CURRENT))
6707 SetChangedFlag (true);
6708 break;
6711 return 0;
6715 static const char setsame_syntax[] = N_("SetSame()");
6717 static const char setsame_help[] =
6718 N_("Sets current layer and sizes to match indicated item.");
6720 /* %start-doc actions SetSame
6722 When invoked over any line, arc, polygon, or via, this changes the
6723 current layer to be the layer that item is on, and changes the current
6724 sizes (thickness, keepaway, drill, etc) according to that item.
6726 %end-doc */
6728 static int
6729 ActionSetSame (int argc, char **argv, Coord x, Coord y)
6731 void *ptr1, *ptr2, *ptr3;
6732 int type;
6733 LayerType *layer = CURRENT;
6735 type = SearchScreen (x, y, CLONE_TYPES, &ptr1, &ptr2, &ptr3);
6736 /* set layer current and size from line or arc */
6737 switch (type)
6739 case LINE_TYPE:
6740 notify_crosshair_change (false);
6741 Settings.LineThickness = ((LineType *) ptr2)->Thickness;
6742 Settings.Keepaway = ((LineType *) ptr2)->Clearance / 2;
6743 layer = (LayerType *) ptr1;
6744 if (Settings.Mode != LINE_MODE)
6745 SetMode (LINE_MODE);
6746 notify_crosshair_change (true);
6747 hid_action ("RouteStylesChanged");
6748 break;
6750 case ARC_TYPE:
6751 notify_crosshair_change (false);
6752 Settings.LineThickness = ((ArcType *) ptr2)->Thickness;
6753 Settings.Keepaway = ((ArcType *) ptr2)->Clearance / 2;
6754 layer = (LayerType *) ptr1;
6755 if (Settings.Mode != ARC_MODE)
6756 SetMode (ARC_MODE);
6757 notify_crosshair_change (true);
6758 hid_action ("RouteStylesChanged");
6759 break;
6761 case POLYGON_TYPE:
6762 layer = (LayerType *) ptr1;
6763 break;
6765 case VIA_TYPE:
6766 notify_crosshair_change (false);
6767 Settings.ViaThickness = ((PinType *) ptr2)->Thickness;
6768 Settings.ViaDrillingHole = ((PinType *) ptr2)->DrillingHole;
6769 Settings.Keepaway = ((PinType *) ptr2)->Clearance / 2;
6770 if (Settings.Mode != VIA_MODE)
6771 SetMode (VIA_MODE);
6772 notify_crosshair_change (true);
6773 hid_action ("RouteStylesChanged");
6774 break;
6776 default:
6777 return 1;
6779 if (layer != CURRENT)
6781 ChangeGroupVisibility (GetLayerNumber (PCB->Data, layer), true, true);
6782 Redraw ();
6784 return 0;
6788 /* --------------------------------------------------------------------------- */
6790 static const char setflag_syntax[] =
6791 N_("SetFlag(Object|Selected|SelectedObjects, flag)\n"
6792 "SetFlag(SelectedLines|SelectedPins|SelectedVias, flag)\n"
6793 "SetFlag(SelectedPads|SelectedTexts|SelectedNames, flag)\n"
6794 "SetFlag(SelectedElements, flag)\n"
6795 "flag = square | octagon | thermal | join");
6797 static const char setflag_help[] = N_("Sets flags on objects.");
6799 /* %start-doc actions SetFlag
6801 Turns the given flag on, regardless of its previous setting. See
6802 @code{ChangeFlag}.
6804 @example
6805 SetFlag(SelectedPins,thermal)
6806 @end example
6808 %end-doc */
6810 static int
6811 ActionSetFlag (int argc, char **argv, Coord x, Coord y)
6813 char *function = ARG (0);
6814 char *flag = ARG (1);
6815 ChangeFlag (function, flag, 1, "SetFlag");
6816 return 0;
6819 /* --------------------------------------------------------------------------- */
6821 static const char clrflag_syntax[] =
6822 N_("ClrFlag(Object|Selected|SelectedObjects, flag)\n"
6823 "ClrFlag(SelectedLines|SelectedPins|SelectedVias, flag)\n"
6824 "ClrFlag(SelectedPads|SelectedTexts|SelectedNames, flag)\n"
6825 "ClrFlag(SelectedElements, flag)\n"
6826 "flag = square | octagon | thermal | join");
6828 static const char clrflag_help[] = N_("Clears flags on objects.");
6830 /* %start-doc actions ClrFlag
6832 Turns the given flag off, regardless of its previous setting. See
6833 @code{ChangeFlag}.
6835 @example
6836 ClrFlag(SelectedLines,join)
6837 @end example
6839 %end-doc */
6841 static int
6842 ActionClrFlag (int argc, char **argv, Coord x, Coord y)
6844 char *function = ARG (0);
6845 char *flag = ARG (1);
6846 ChangeFlag (function, flag, 0, "ClrFlag");
6847 return 0;
6850 /* --------------------------------------------------------------------------- */
6852 static const char changeflag_syntax[] =
6853 N_("ChangeFlag(Object|Selected|SelectedObjects, flag, value)\n"
6854 "ChangeFlag(SelectedLines|SelectedPins|SelectedVias, flag, value)\n"
6855 "ChangeFlag(SelectedPads|SelectedTexts|SelectedNames, flag, value)\n"
6856 "ChangeFlag(SelectedElements, flag, value)\n"
6857 "flag = square | octagon | thermal | join\n"
6858 "value = 0 | 1");
6860 static const char changeflag_help[] = N_("Sets or clears flags on objects.");
6862 /* %start-doc actions ChangeFlag
6864 Toggles the given flag on the indicated object(s). The flag may be
6865 one of the flags listed above (square, octagon, thermal, join). The
6866 value may be the number 0 or 1. If the value is 0, the flag is
6867 cleared. If the value is 1, the flag is set.
6869 %end-doc */
6871 static int
6872 ActionChangeFlag (int argc, char **argv, Coord x, Coord y)
6874 char *function = ARG (0);
6875 char *flag = ARG (1);
6876 int value = argc > 2 ? atoi (argv[2]) : -1;
6877 if (value != 0 && value != 1)
6878 AFAIL (changeflag);
6880 ChangeFlag (function, flag, value, "ChangeFlag");
6881 return 0;
6885 static void
6886 ChangeFlag (char *what, char *flag_name, int value, char *cmd_name)
6888 bool (*set_object) (int, void *, void *, void *);
6889 bool (*set_selected) (int);
6891 if (NSTRCMP (flag_name, "square") == 0)
6893 set_object = value ? SetObjectSquare : ClrObjectSquare;
6894 set_selected = value ? SetSelectedSquare : ClrSelectedSquare;
6896 else if (NSTRCMP (flag_name, "octagon") == 0)
6898 set_object = value ? SetObjectOctagon : ClrObjectOctagon;
6899 set_selected = value ? SetSelectedOctagon : ClrSelectedOctagon;
6901 else if (NSTRCMP (flag_name, "join") == 0)
6903 /* Note: these are backwards, because the flag is "clear" but
6904 the command is "join". */
6905 set_object = value ? ClrObjectJoin : SetObjectJoin;
6906 set_selected = value ? ClrSelectedJoin : SetSelectedJoin;
6908 else
6910 Message (_("%s(): Flag \"%s\" is not valid\n"), cmd_name, flag_name);
6911 return;
6914 switch (GetFunctionID (what))
6916 case F_Object:
6918 int type;
6919 void *ptr1, *ptr2, *ptr3;
6921 if ((type =
6922 SearchScreen (Crosshair.X, Crosshair.Y, CHANGESIZE_TYPES,
6923 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
6924 if (TEST_FLAG (LOCKFLAG, (PinType *) ptr2))
6925 Message (_("Sorry, the object is locked\n"));
6926 if (set_object (type, ptr1, ptr2, ptr3))
6927 SetChangedFlag (true);
6928 break;
6931 case F_SelectedVias:
6932 if (set_selected (VIA_TYPE))
6933 SetChangedFlag (true);
6934 break;
6936 case F_SelectedPins:
6937 if (set_selected (PIN_TYPE))
6938 SetChangedFlag (true);
6939 break;
6941 case F_SelectedPads:
6942 if (set_selected (PAD_TYPE))
6943 SetChangedFlag (true);
6944 break;
6946 case F_SelectedLines:
6947 if (set_selected (LINE_TYPE))
6948 SetChangedFlag (true);
6949 break;
6951 case F_SelectedTexts:
6952 if (set_selected (TEXT_TYPE))
6953 SetChangedFlag (true);
6954 break;
6956 case F_SelectedNames:
6957 if (set_selected (ELEMENTNAME_TYPE))
6958 SetChangedFlag (true);
6959 break;
6961 case F_SelectedElements:
6962 if (set_selected (ELEMENT_TYPE))
6963 SetChangedFlag (true);
6964 break;
6966 case F_Selected:
6967 case F_SelectedObjects:
6968 if (set_selected (CHANGESIZE_TYPES))
6969 SetChangedFlag (true);
6970 break;
6974 /* --------------------------------------------------------------------------- */
6976 static const char executefile_syntax[] = N_("ExecuteFile(filename)");
6978 static const char executefile_help[] = N_("Run actions from the given file.");
6980 /* %start-doc actions ExecuteFile
6982 Lines starting with @code{#} are ignored.
6984 %end-doc */
6986 static int
6987 ActionExecuteFile (int argc, char **argv, Coord x, Coord y)
6989 FILE *fp;
6990 char *fname;
6991 char line[256];
6992 int n = 0;
6993 char *sp;
6995 if (argc != 1)
6996 AFAIL (executefile);
6998 fname = argv[0];
7000 if ((fp = fopen (fname, "r")) == NULL)
7002 fprintf (stderr, _("Could not open actions file \"%s\".\n"), fname);
7003 return 1;
7006 defer_updates = 1;
7007 defer_needs_update = 0;
7008 while (fgets (line, sizeof (line), fp) != NULL)
7010 n++;
7011 sp = line;
7013 /* eat the trailing newline */
7014 while (*sp && *sp != '\r' && *sp != '\n')
7015 sp++;
7016 *sp = '\0';
7018 /* eat leading spaces and tabs */
7019 sp = line;
7020 while (*sp && (*sp == ' ' || *sp == '\t'))
7021 sp++;
7024 * if we have anything left and its not a comment line
7025 * then execute it
7028 if (*sp && *sp != '#')
7030 /*Message ("%s : line %-3d : \"%s\"\n", fname, n, sp);*/
7031 hid_parse_actions (sp);
7035 defer_updates = 0;
7036 if (defer_needs_update)
7038 IncrementUndoSerialNumber ();
7039 gui->invalidate_all ();
7041 fclose (fp);
7042 return 0;
7045 /* --------------------------------------------------------------------------- */
7047 static int
7048 ActionPSCalib (int argc, char **argv, Coord x, Coord y)
7050 HID *ps = hid_find_exporter ("ps");
7051 ps->calibrate (0.0,0.0);
7052 return 0;
7055 /* --------------------------------------------------------------------------- */
7057 static ElementType *element_cache = NULL;
7059 static ElementType *
7060 find_element_by_refdes (char *refdes)
7062 if (element_cache
7063 && NAMEONPCB_NAME(element_cache)
7064 && strcmp (NAMEONPCB_NAME(element_cache), refdes) == 0)
7065 return element_cache;
7067 ELEMENT_LOOP (PCB->Data);
7069 if (NAMEONPCB_NAME(element)
7070 && strcmp (NAMEONPCB_NAME(element), refdes) == 0)
7072 element_cache = element;
7073 return element_cache;
7076 END_LOOP;
7077 return NULL;
7080 static AttributeType *
7081 lookup_attr (AttributeListType *list, const char *name)
7083 int i;
7084 for (i=0; i<list->Number; i++)
7085 if (strcmp (list->List[i].name, name) == 0)
7086 return & list->List[i];
7087 return NULL;
7090 static void
7091 delete_attr (AttributeListType *list, AttributeType *attr)
7093 int idx = attr - list->List;
7094 if (idx < 0 || idx >= list->Number)
7095 return;
7096 if (list->Number - idx > 1)
7097 memmove (attr, attr+1, (list->Number - idx - 1) * sizeof(AttributeType));
7098 list->Number --;
7101 /* ---------------------------------------------------------------- */
7102 static const char elementlist_syntax[] =
7103 N_("ElementList(Start|Done|Need,<refdes>,<footprint>,<value>)");
7105 static const char elementlist_help[] =
7106 N_("Adds the given element if it doesn't already exist.");
7108 /* %start-doc actions elementlist
7110 @table @code
7112 @item Start
7113 Indicates the start of an element list; call this before any Need
7114 actions.
7116 @item Need
7117 Searches the board for an element with a matching refdes.
7119 If found, the value and footprint are updated.
7121 If not found, a new element is created with the given footprint and value.
7123 @item Done
7124 Compares the list of elements needed since the most recent
7125 @code{start} with the list of elements actually on the board. Any
7126 elements that weren't listed are selected, so that the user may delete
7127 them.
7129 @end table
7131 %end-doc */
7133 static int number_of_footprints_not_found;
7135 static int
7136 parse_layout_attribute_units (char *name, int def)
7138 const char *as = AttributeGet (PCB, name);
7139 if (!as)
7140 return def;
7141 return GetValue (as, NULL, NULL);
7144 static int
7145 ActionElementList (int argc, char **argv, Coord x, Coord y)
7147 ElementType *e = NULL;
7148 char *refdes, *value, *footprint, *old;
7149 char *args[3];
7150 char *function;
7152 if (argc < 1)
7153 AFAIL (elementlist);
7155 function = argv[0];
7157 #ifdef DEBUG
7158 printf("Entered ActionElementList, executing function %s\n", function);
7159 #endif
7161 if (strcasecmp (function, "start") == 0)
7163 ELEMENT_LOOP (PCB->Data);
7165 CLEAR_FLAG (FOUNDFLAG, element);
7167 END_LOOP;
7168 element_cache = NULL;
7169 number_of_footprints_not_found = 0;
7170 return 0;
7173 if (strcasecmp (function, "done") == 0)
7175 ELEMENT_LOOP (PCB->Data);
7177 if (TEST_FLAG (FOUNDFLAG, element))
7179 CLEAR_FLAG (FOUNDFLAG, element);
7181 else if (! EMPTY_STRING_P (NAMEONPCB_NAME (element)))
7183 /* Unnamed elements should remain untouched */
7184 SET_FLAG (SELECTEDFLAG, element);
7187 END_LOOP;
7188 if (number_of_footprints_not_found > 0)
7189 gui->confirm_dialog (_("Not all requested footprints were found.\n"
7190 "See the message log for details"),
7191 "Ok", NULL);
7192 return 0;
7195 if (strcasecmp (function, "need") != 0)
7196 AFAIL (elementlist);
7198 if (argc != 4)
7199 AFAIL (elementlist);
7201 argc --;
7202 argv ++;
7204 refdes = ARG(0);
7205 footprint = ARG(1);
7206 value = ARG(2);
7208 args[0] = footprint;
7209 args[1] = refdes;
7210 args[2] = value;
7212 #ifdef DEBUG
7213 printf(" ... footprint = %s\n", footprint);
7214 printf(" ... refdes = %s\n", refdes);
7215 printf(" ... value = %s\n", value);
7216 #endif
7218 e = find_element_by_refdes (refdes);
7220 if (!e)
7222 Coord nx, ny, d;
7224 #ifdef DEBUG
7225 printf(" ... Footprint not on board, need to add it.\n");
7226 #endif
7227 /* Not on board, need to add it. */
7228 if (LoadFootprint(argc, args, x, y))
7230 number_of_footprints_not_found ++;
7231 return 1;
7234 nx = PCB->MaxWidth / 2;
7235 ny = PCB->MaxHeight / 2;
7236 d = MIN (PCB->MaxWidth, PCB->MaxHeight) / 10;
7238 nx = parse_layout_attribute_units ("import::newX", nx);
7239 ny = parse_layout_attribute_units ("import::newY", ny);
7240 d = parse_layout_attribute_units ("import::disperse", d);
7242 if (d > 0)
7244 nx += rand () % (d*2) - d;
7245 ny += rand () % (d*2) - d;
7248 if (nx < 0)
7249 nx = 0;
7250 if (nx >= PCB->MaxWidth)
7251 nx = PCB->MaxWidth - 1;
7252 if (ny < 0)
7253 ny = 0;
7254 if (ny >= PCB->MaxHeight)
7255 ny = PCB->MaxHeight - 1;
7257 /* Place components onto center of board. */
7258 if (CopyPastebufferToLayout (nx, ny))
7259 SetChangedFlag (true);
7262 else if (e && DESCRIPTION_NAME(e) && strcmp (DESCRIPTION_NAME(e), footprint) != 0)
7264 int er, pr, i;
7265 Coord mx, my;
7266 ElementType *pe;
7268 #ifdef DEBUG
7269 printf(" ... Footprint on board, but different from footprint loaded.\n");
7270 #endif
7271 /* Different footprint, we need to swap them out. */
7272 if (LoadFootprint(argc, args, x, y))
7274 number_of_footprints_not_found ++;
7275 return 1;
7278 er = ElementOrientation (e);
7279 pe = PASTEBUFFER->Data->Element->data;
7280 if (!FRONT (e))
7281 MirrorElementCoordinates (PASTEBUFFER->Data, pe, pe->MarkY*2 - PCB->MaxHeight);
7282 pr = ElementOrientation (pe);
7284 mx = e->MarkX;
7285 my = e->MarkY;
7287 if (er != pr)
7288 RotateElementLowLevel (PASTEBUFFER->Data, pe, pe->MarkX, pe->MarkY, (er-pr+4)%4);
7290 for (i=0; i<MAX_ELEMENTNAMES; i++)
7292 pe->Name[i].X = e->Name[i].X - mx + pe->MarkX ;
7293 pe->Name[i].Y = e->Name[i].Y - my + pe->MarkY ;
7294 pe->Name[i].Direction = e->Name[i].Direction;
7295 pe->Name[i].Scale = e->Name[i].Scale;
7298 RemoveElement (e);
7300 if (CopyPastebufferToLayout (mx, my))
7301 SetChangedFlag (true);
7304 /* Now reload footprint */
7305 element_cache = NULL;
7306 e = find_element_by_refdes (refdes);
7308 old = ChangeElementText (PCB, PCB->Data, e, NAMEONPCB_INDEX, strdup (refdes));
7309 if (old)
7310 free(old);
7311 old = ChangeElementText (PCB, PCB->Data, e, VALUE_INDEX, strdup (value));
7312 if (old)
7313 free(old);
7315 SET_FLAG (FOUNDFLAG, e);
7317 #ifdef DEBUG
7318 printf(" ... Leaving ActionElementList.\n");
7319 #endif
7321 return 0;
7324 /* ---------------------------------------------------------------- */
7325 static const char elementsetattr_syntax[] =
7326 N_("ElementSetAttr(refdes,name[,value])");
7328 static const char elementsetattr_help[] =
7329 N_("Sets or clears an element-specific attribute.");
7331 /* %start-doc actions elementsetattr
7333 If a value is specified, the named attribute is added (if not already
7334 present) or changed (if it is) to the given value. If the value is
7335 not specified, the given attribute is removed if present.
7337 %end-doc */
7339 static int
7340 ActionElementSetAttr (int argc, char **argv, Coord x, Coord y)
7342 ElementType *e = NULL;
7343 char *refdes, *name, *value;
7344 AttributeType *attr;
7346 if (argc < 2)
7348 AFAIL (elementsetattr);
7351 refdes = argv[0];
7352 name = argv[1];
7353 value = ARG(2);
7355 ELEMENT_LOOP (PCB->Data);
7357 if (NSTRCMP (refdes, NAMEONPCB_NAME (element)) == 0)
7359 e = element;
7360 break;
7363 END_LOOP;
7365 if (!e)
7367 Message(_("Cannot change attribute of %s - element not found\n"), refdes);
7368 return 1;
7371 attr = lookup_attr (&e->Attributes, name);
7373 if (attr && value)
7375 free (attr->value);
7376 attr->value = strdup (value);
7378 if (attr && ! value)
7380 delete_attr (& e->Attributes, attr);
7382 if (!attr && value)
7384 CreateNewAttribute (& e->Attributes, name, value);
7387 return 0;
7390 /* ---------------------------------------------------------------- */
7391 static const char execcommand_syntax[] = N_("ExecCommand(command)");
7393 static const char execcommand_help[] = N_("Runs a command.");
7395 /* %start-doc actions execcommand
7397 Runs the given command, which is a system executable.
7399 %end-doc */
7401 static int
7402 ActionExecCommand (int argc, char **argv, Coord x, Coord y)
7404 char *command;
7406 if (argc < 1)
7408 AFAIL (execcommand);
7411 command = ARG(0);
7413 if (system (command))
7414 return 1;
7415 return 0;
7418 /* ---------------------------------------------------------------- */
7420 static int
7421 pcb_spawnvp (char **argv)
7423 #ifdef HAVE__SPAWNVP
7424 int result = _spawnvp (_P_WAIT, argv[0], (const char * const *) argv);
7425 if (result == -1)
7426 return 1;
7427 else
7428 return 0;
7429 #else
7430 int pid;
7431 pid = fork ();
7432 if (pid < 0)
7434 /* error */
7435 Message(_("Cannot fork!"));
7436 return 1;
7438 else if (pid == 0)
7440 /* Child */
7441 execvp (argv[0], argv);
7442 exit(1);
7444 else
7446 int rv;
7447 /* Parent */
7448 wait (&rv);
7450 return 0;
7451 #endif
7454 /* ---------------------------------------------------------------- */
7456 /*!
7457 * \brief Creates a new temporary file name.
7459 * Hopefully the operating system provides a mkdtemp() function to
7460 * securily create a temporary directory with mode 0700.\n
7461 * If so then that directory is created and the returned string is made
7462 * up of the directory plus the name variable.\n
7463 * For example:\n
7465 * tempfile_name_new ("myfile") might return
7466 * "/var/tmp/pcb.123456/myfile".
7468 * If mkdtemp() is not available then 'name' is ignored and the
7469 * insecure tmpnam() function is used.
7471 * Files/names created with tempfile_name_new() should be unlinked
7472 * with tempfile_unlink to make sure the temporary directory is also
7473 * removed when mkdtemp() is used.
7475 static char *
7476 tempfile_name_new (char * name)
7478 char *tmpfile = NULL;
7479 #ifdef HAVE_MKDTEMP
7480 char *tmpdir, *mytmpdir;
7481 size_t len;
7482 #endif
7484 assert ( name != NULL );
7486 #ifdef HAVE_MKDTEMP
7487 #define TEMPLATE "pcb.XXXXXXXX"
7490 tmpdir = getenv ("TMPDIR");
7492 /* FIXME -- what about win32? */
7493 if (tmpdir == NULL) {
7494 tmpdir = "/tmp";
7497 mytmpdir = (char *) malloc (sizeof(char) *
7498 (strlen (tmpdir) +
7500 strlen (TEMPLATE) +
7501 1));
7502 if (mytmpdir == NULL) {
7503 fprintf (stderr, "%s(): malloc failed()\n", __FUNCTION__);
7504 exit (1);
7507 *mytmpdir = '\0';
7508 (void)strcat (mytmpdir, tmpdir);
7509 (void)strcat (mytmpdir, PCB_DIR_SEPARATOR_S);
7510 (void)strcat (mytmpdir, TEMPLATE);
7511 if (mkdtemp (mytmpdir) == NULL) {
7512 fprintf (stderr, "%s(): mkdtemp (\"%s\") failed\n", __FUNCTION__, mytmpdir);
7513 free (mytmpdir);
7514 return NULL;
7518 len = strlen (mytmpdir) + /* the temp directory name */
7519 1 + /* the directory sep. */
7520 strlen (name) + /* the file name */
7521 1 /* the \0 termination */
7524 tmpfile = (char *) malloc (sizeof (char) * len);
7526 *tmpfile = '\0';
7527 (void)strcat (tmpfile, mytmpdir);
7528 (void)strcat (tmpfile, PCB_DIR_SEPARATOR_S);
7529 (void)strcat (tmpfile, name);
7531 free (mytmpdir);
7532 #undef TEMPLATE
7533 #else
7535 * tmpnam() uses a static buffer so strdup() the result right away
7536 * in case someone decides to create multiple temp names.
7538 tmpfile = strdup (tmpnam (NULL));
7539 #ifdef __WIN32__
7541 /* Guile doesn't like \ separators */
7542 char *c;
7543 for (c = tmpfile; *c; c++)
7544 if (*c == '\\')
7545 *c = '/';
7547 #endif
7548 #endif
7550 return tmpfile;
7553 /* ---------------------------------------------------------------- */
7556 * \brief Unlink a temporary file.
7558 * If we have mkdtemp() then our temp file lives in a temporary
7559 * directory and we need to remove that directory too.
7561 static int
7562 tempfile_unlink (char * name)
7564 #ifdef DEBUG
7565 /* SDB says: Want to keep old temp files for examiniation when debugging */
7566 return 0;
7567 #else /* DEBUG */
7569 #ifdef HAVE_MKDTEMP
7570 int e, rc2 = 0;
7571 char *dname;
7573 unlink (name);
7574 /* it is possible that the file was never created so it is OK if the
7575 unlink fails */
7577 /* now figure out the directory name to remove */
7578 e = strlen (name) - 1;
7579 while (e > 0 && name[e] != PCB_DIR_SEPARATOR_C) {e--;}
7581 dname = strdup (name);
7582 dname[e] = '\0';
7585 * at this point, e *should* point to the end of the directory part
7586 * but lets make sure.
7588 if (e > 0) {
7589 rc2 = rmdir (dname);
7590 if (rc2 != 0) {
7591 perror (dname);
7594 } else {
7595 fprintf (stderr, _("%s(): Unable to determine temp directory name from the temp file\n"),
7596 __FUNCTION__);
7597 fprintf (stderr, "%s(): \"%s\"\n",
7598 __FUNCTION__, name);
7599 rc2 = -1;
7602 /* name was allocated with malloc */
7603 free (dname);
7604 free (name);
7607 * FIXME - should also return -1 if the temp file exists and was not
7608 * removed.
7610 if (rc2 != 0) {
7611 return -1;
7614 #else /* HAVE_MKDTEMP */
7615 int rc = unlink (name);
7617 if (rc != 0) {
7618 fprintf (stderr, _("Failed to unlink \"%s\"\n"), name);
7619 free (name);
7620 return rc;
7622 free (name);
7624 #endif /* HAVE_MKDTEMP */
7625 #endif /* DEBUG */
7627 return 0;
7630 /* ---------------------------------------------------------------- */
7631 static const char import_syntax[] =
7632 N_("Import()\n"
7633 "Import([gnetlist|make[,source,source,...]])\n"
7634 "Import(setnewpoint[,(mark|center|X,Y)])\n"
7635 "Import(setdisperse,D,units)\n");
7637 static const char import_help[] = N_("Import schematics.");
7639 /* %start-doc actions Import
7641 Imports element and netlist data from the schematics (or some other
7642 source). The first parameter, which is optional, is the mode. If not
7643 specified, the @code{import::mode} attribute in the PCB is used.
7644 @code{gnetlist} means gnetlist is used to obtain the information from
7645 the schematics. @code{make} invokes @code{make}, assuming the user
7646 has a @code{Makefile} in the current directory. The @code{Makefile}
7647 will be invoked with the following variables set:
7649 @table @code
7651 @item PCB
7652 The name of the .pcb file
7654 @item SRCLIST
7655 A space-separated list of source files
7657 @item OUT
7658 The name of the file in which to put the command script, which may
7659 contain any @pcb{} actions. By default, this is a temporary file
7660 selected by @pcb{}, but if you specify an @code{import::outfile}
7661 attribute, that file name is used instead (and not automatically
7662 deleted afterwards).
7664 @end table
7666 The target specified to be built is the first of these that apply:
7668 @itemize @bullet
7670 @item
7671 The target specified by an @code{import::target} attribute.
7673 @item
7674 The output file specified by an @code{import::outfile} attribute.
7676 @item
7677 If nothing else is specified, the target is @code{pcb_import}.
7679 @end itemize
7681 If you specify an @code{import::makefile} attribute, then "-f <that
7682 file>" will be added to the command line.
7684 If you specify the mode, you may also specify the source files
7685 (schematics). If you do not specify any, the list of schematics is
7686 obtained by reading the @code{import::src@var{N}} attributes (like
7687 @code{import::src0}, @code{import::src1}, etc).
7689 For compatibility with future extensions to the import file format,
7690 the generated file @emph{must not} start with the two characters
7691 @code{#%}.
7693 If a temporary file is needed the @code{TMPDIR} environment variable
7694 is used to select its location.
7696 Note that the programs @code{gnetlist} and @code{make} may be
7697 overridden by the user via the @code{make-program} and @code{gnetlist}
7698 @code{pcb} settings (i.e. in @code{~/.pcb/settings} or on the command
7699 line).
7701 If @pcb{} cannot determine which schematic(s) to import from, the GUI
7702 is called to let user choose (see @code{ImportGUI()}).
7704 Note that Import() doesn't delete anything - after an Import, elements
7705 which shouldn't be on the board are selected and may be removed once
7706 it's determined that the deletion is appropriate.
7708 If @code{Import()} is called with @code{setnewpoint}, then the location
7709 of new components can be specified. This is where parts show up when
7710 they're added to the board. The default is the center of the board.
7712 @table @code
7714 @item Import(setnewpoint)
7716 Prompts the user to click on the board somewhere, uses that point. If
7717 called by a hotkey, uses the current location of the crosshair.
7719 @item Import(setnewpoint,mark)
7721 Uses the location of the mark. If no mark is present, the point is
7722 not changed.
7724 @item Import(setnewpoint,center)
7726 Resets the point to the center of the board.
7728 @item Import(setnewpoint,X,Y,units)
7730 Sets the point to the specific coordinates given. Example:
7731 @code{Import(setnewpoint,50,25,mm)}
7733 @end table
7735 Note that the X and Y locations are stored in attributes named
7736 @code{import::newX} and @code{import::newY} so you could change them
7737 manually if you wished.
7739 Calling @code{Import(setdisperse,D,units)} sets how much the newly
7740 placed elements are dispersed relative to the set point. For example,
7741 @code{Import(setdisperse,10,mm)} will offset each part randomly up to
7742 10mm away from the point. The default dispersion is 1/10th of the
7743 smallest board dimension. Dispersion is saved in the
7744 @code{import::disperse} attribute.
7746 %end-doc */
7748 static int
7749 ActionImport (int argc, char **argv, Coord x, Coord y)
7751 char *mode;
7752 char **sources = NULL;
7753 int nsources = 0;
7755 #ifdef DEBUG
7756 printf("ActionImport: =========== Entering ActionImport ============\n");
7757 #endif
7759 mode = ARG (0);
7761 if (mode && strcasecmp (mode, "setdisperse") == 0)
7763 char *ds, *units;
7764 char buf[50];
7766 ds = ARG (1);
7767 units = ARG (2);
7768 if (!ds)
7770 const char *as = AttributeGet (PCB, "import::disperse");
7771 ds = gui->prompt_for(_("Enter dispersion:"), as ? as : "0");
7773 if (units)
7775 sprintf(buf, "%s%s", ds, units);
7776 AttributePut (PCB, "import::disperse", buf);
7778 else
7779 AttributePut (PCB, "import::disperse", ds);
7780 if (ARG (1) == NULL)
7781 free (ds);
7782 return 0;
7785 if (mode && strcasecmp (mode, "setnewpoint") == 0)
7787 const char *xs, *ys, *units;
7788 Coord x, y;
7789 char buf[50];
7791 xs = ARG (1);
7792 ys = ARG (2);
7793 units = ARG (3);
7795 if (!xs)
7797 gui->get_coords (_("Click on a location"), &x, &y);
7799 else if (strcasecmp (xs, "center") == 0)
7801 AttributeRemove (PCB, "import::newX");
7802 AttributeRemove (PCB, "import::newY");
7803 return 0;
7805 else if (strcasecmp (xs, "mark") == 0)
7807 if (!Marked.status)
7808 return 0;
7810 x = Marked.X;
7811 y = Marked.Y;
7813 else if (ys)
7815 x = GetValue (xs, units, NULL);
7816 y = GetValue (ys, units, NULL);
7818 else
7820 Message (_("Bad syntax for Import(setnewpoint)"));
7821 return 1;
7824 pcb_snprintf (buf, sizeof (buf), "%$ms", x);
7825 AttributePut (PCB, "import::newX", buf);
7826 pcb_snprintf (buf, sizeof (buf), "%$ms", y);
7827 AttributePut (PCB, "import::newY", buf);
7828 return 0;
7831 if (! mode)
7832 mode = AttributeGet (PCB, "import::mode");
7833 if (! mode)
7834 mode = "gnetlist";
7836 if (argc > 1)
7838 sources = argv + 1;
7839 nsources = argc - 1;
7842 if (! sources)
7844 char sname[40];
7845 char *src;
7847 nsources = -1;
7848 do {
7849 nsources ++;
7850 sprintf(sname, "import::src%d", nsources);
7851 src = AttributeGet (PCB, sname);
7852 } while (src);
7854 if (nsources > 0)
7856 sources = (char **) malloc ((nsources + 1) * sizeof (char *));
7857 nsources = -1;
7858 do {
7859 nsources ++;
7860 sprintf(sname, "import::src%d", nsources);
7861 src = AttributeGet (PCB, sname);
7862 sources[nsources] = src;
7863 } while (src);
7867 if (! sources)
7869 /* Replace .pcb with .sch and hope for the best. */
7870 char *pcbname = PCB->Filename;
7871 char *schname;
7872 char *dot, *slash, *bslash;
7874 if (!pcbname)
7875 return hid_action("ImportGUI");
7877 schname = (char *) malloc (strlen(pcbname) + 5);
7878 strcpy (schname, pcbname);
7879 dot = strchr (schname, '.');
7880 slash = strchr (schname, '/');
7881 bslash = strchr (schname, '\\');
7882 if (dot && slash && dot < slash)
7883 dot = NULL;
7884 if (dot && bslash && dot < bslash)
7885 dot = NULL;
7886 if (dot)
7887 *dot = 0;
7888 strcat (schname, ".sch");
7890 if (access (schname, F_OK))
7892 free (schname);
7893 return hid_action("ImportGUI");
7896 sources = (char **) malloc (2 * sizeof (char *));
7897 sources[0] = schname;
7898 sources[1] = NULL;
7899 nsources = 1;
7902 if (strcasecmp (mode, "gnetlist") == 0)
7904 char *tmpfile = tempfile_name_new ("gnetlist_output");
7905 char **cmd;
7906 int i;
7908 if (tmpfile == NULL) {
7909 Message (_("Could not create temp file"));
7910 return 1;
7913 cmd = (char **) malloc ((7 + nsources) * sizeof (char *));
7914 cmd[0] = Settings.GnetlistProgram;
7915 cmd[1] = "-g";
7916 cmd[2] = "pcbfwd";
7917 cmd[3] = "-o";
7918 cmd[4] = tmpfile;
7919 cmd[5] = "--";
7920 for (i=0; i<nsources; i++)
7921 cmd[6+i] = sources[i];
7922 cmd[6+nsources] = NULL;
7924 #ifdef DEBUG
7925 printf("ActionImport: =========== About to run gnetlist ============\n");
7926 printf("%s %s %s %s %s %s %s ...\n",
7927 cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6]);
7928 #endif
7930 if (pcb_spawnvp (cmd))
7932 unlink (tmpfile);
7933 return 1;
7936 #ifdef DEBUG
7937 printf("ActionImport: =========== About to run ActionExecuteFile, file = %s ============\n", tmpfile);
7938 #endif
7940 cmd[0] = tmpfile;
7941 cmd[1] = NULL;
7942 ActionExecuteFile (1, cmd, 0, 0);
7944 free (cmd);
7945 tempfile_unlink (tmpfile);
7947 else if (strcasecmp (mode, "make") == 0)
7949 int must_free_tmpfile = 0;
7950 char *tmpfile;
7951 char *cmd[10];
7952 int i;
7953 char *srclist;
7954 int srclen;
7955 char *user_outfile = NULL;
7956 char *user_makefile = NULL;
7957 char *user_target = NULL;
7960 user_outfile = AttributeGet (PCB, "import::outfile");
7961 user_makefile = AttributeGet (PCB, "import::makefile");
7962 user_target = AttributeGet (PCB, "import::target");
7963 if (user_outfile && !user_target)
7964 user_target = user_outfile;
7966 if (user_outfile)
7967 tmpfile = user_outfile;
7968 else
7970 tmpfile = tempfile_name_new ("gnetlist_output");
7971 if (tmpfile == NULL) {
7972 Message (_("Could not create temp file"));
7973 free (sources);
7974 return 1;
7976 must_free_tmpfile = 1;
7979 srclen = sizeof("SRCLIST=") + 2;
7980 for (i=0; i<nsources; i++)
7981 srclen += strlen (sources[i]) + 2;
7982 srclist = (char *) malloc (srclen);
7983 strcpy (srclist, "SRCLIST=");
7984 for (i=0; i<nsources; i++)
7986 if (i)
7987 strcat (srclist, " ");
7988 strcat (srclist, sources[i]);
7991 cmd[0] = Settings.MakeProgram;
7992 cmd[1] = "-s";
7993 cmd[2] = Concat ("PCB=", PCB->Filename, NULL);
7994 cmd[3] = srclist;
7995 cmd[4] = Concat ("OUT=", tmpfile, NULL);
7996 i = 5;
7997 if (user_makefile)
7999 cmd[i++] = "-f";
8000 cmd[i++] = user_makefile;
8002 cmd[i++] = user_target ? user_target : (char *)"pcb_import";
8003 cmd[i++] = NULL;
8005 if (pcb_spawnvp (cmd))
8007 if (must_free_tmpfile)
8008 unlink (tmpfile);
8009 free (cmd[2]);
8010 free (cmd[3]);
8011 free (cmd[4]);
8012 return 1;
8015 cmd[0] = tmpfile;
8016 cmd[1] = NULL;
8017 ActionExecuteFile (1, cmd, 0, 0);
8019 free (cmd[2]);
8020 free (cmd[3]);
8021 free (cmd[4]);
8022 if (must_free_tmpfile)
8023 tempfile_unlink (tmpfile);
8025 else
8027 Message (_("Unknown import mode: %s\n"), mode);
8028 return 1;
8031 DeleteRats (false);
8032 AddAllRats (false, NULL);
8034 #ifdef DEBUG
8035 printf("ActionImport: =========== Leaving ActionImport ============\n");
8036 #endif
8038 return 0;
8041 /* ------------------------------------------------------------ */
8043 static const char attributes_syntax[] =
8044 N_("Attributes(Layout|Layer|Element)\n"
8045 "Attributes(Layer,layername)");
8047 static const char attributes_help[] =
8048 N_("Let the user edit the attributes of the layout, current or given\n"
8049 "layer, or selected element.");
8051 /* %start-doc actions Attributes
8053 This just pops up a dialog letting the user edit the attributes of the
8054 pcb, an element, or a layer.
8056 %end-doc */
8059 static int
8060 ActionAttributes (int argc, char **argv, Coord x, Coord y)
8062 char *function = ARG (0);
8063 char *layername = ARG (1);
8064 char *buf;
8066 if (!function)
8067 AFAIL (attributes);
8069 if (!gui->edit_attributes)
8071 Message (_("This GUI doesn't support Attribute Editing\n"));
8072 return 1;
8075 switch (GetFunctionID (function))
8077 case F_Layout:
8079 gui->edit_attributes(_("Layout Attributes"), &(PCB->Attributes));
8080 return 0;
8083 case F_Layer:
8085 LayerType *layer = CURRENT;
8086 if (layername)
8088 int i;
8089 layer = NULL;
8090 for (i=0; i<max_copper_layer; i++)
8091 if (strcmp (PCB->Data->Layer[i].Name, layername) == 0)
8093 layer = & (PCB->Data->Layer[i]);
8094 break;
8096 if (layer == NULL)
8098 Message (_("No layer named %s\n"), layername);
8099 return 1;
8102 buf = (char *) malloc (strlen (layer->Name) +
8103 strlen (_("Layer %s Attributes")));
8104 sprintf (buf, _("Layer %s Attributes"), layer->Name);
8105 gui->edit_attributes(buf, &(layer->Attributes));
8106 free (buf);
8107 return 0;
8110 case F_Element:
8112 int n_found = 0;
8113 ElementType *e = NULL;
8114 ELEMENT_LOOP (PCB->Data);
8116 if (TEST_FLAG (SELECTEDFLAG, element))
8118 e = element;
8119 n_found ++;
8122 END_LOOP;
8123 if (n_found > 1)
8125 Message (_("Too many elements selected\n"));
8126 return 1;
8128 if (n_found == 0)
8130 void *ptrtmp;
8131 gui->get_coords (_("Click on an element"), &x, &y);
8132 if ((SearchScreen
8133 (x, y, ELEMENT_TYPE, &ptrtmp,
8134 &ptrtmp, &ptrtmp)) != NO_TYPE)
8135 e = (ElementType *) ptrtmp;
8136 else
8138 Message (_("No element found there\n"));
8139 return 1;
8143 if (NAMEONPCB_NAME(e))
8145 buf = (char *) malloc (strlen (NAMEONPCB_NAME(e)) +
8146 strlen (_("Element %s Attributes")));
8147 sprintf(buf, _("Element %s Attributes"), NAMEONPCB_NAME(e));
8149 else
8151 buf = strdup (_("Unnamed Element Attributes"));
8153 gui->edit_attributes(buf, &(e->Attributes));
8154 free (buf);
8155 break;
8158 default:
8159 AFAIL (attributes);
8162 return 0;
8165 /* ------------------------------------------------------------ */
8167 static const char smash_syntax[] = N_("Smash(Object|Selected|SelectedElements)");
8168 static const char smash_help[] = N_("Smash an element or elements into pieces.");
8170 /* %start-doc actions Attributes
8172 This smashes the given object into pieces in-place on the PCB.
8174 %end-doc */
8177 static void
8178 SmashElement (ElementType *element)
8180 Cardinal group;
8181 LayerType *top_copper, *bottom_copper;
8182 LayerType *top_silk, *bottom_silk;
8183 LayerType *layer;
8184 LineType *new_line;
8185 ArcType *new_arc;
8186 PinType *new_via;
8188 group = GetLayerGroupNumberBySide (TOP_SIDE);
8189 top_copper = &PCB->Data->Layer[PCB->LayerGroups.Entries[group][0]];
8190 group = GetLayerGroupNumberBySide (BOTTOM_SIDE);
8191 bottom_copper = &PCB->Data->Layer[PCB->LayerGroups.Entries[group][0]];
8192 top_silk = &PCB->Data->Layer[top_silk_layer];
8193 bottom_silk = &PCB->Data->Layer[bottom_silk_layer];
8195 ELEMENTLINE_LOOP (element);
8197 layer = TEST_FLAG (ONSOLDERFLAG, element) ? bottom_silk : top_silk;
8198 new_line = CreateNewLineOnLayer (layer,
8199 line->Point1.X, line->Point1.Y,
8200 line->Point2.X, line->Point2.Y,
8201 line->Thickness, 0, NoFlags ());
8202 if (new_line)
8204 new_line->Number = STRDUP (NAMEONPCB_NAME (element));
8205 AddObjectToCreateUndoList (LINE_TYPE, layer, new_line, new_line);
8208 END_LOOP;
8209 ARC_LOOP (element);
8211 layer = TEST_FLAG (ONSOLDERFLAG, element) ? bottom_silk : top_silk;
8212 new_arc = CreateNewArcOnLayer (layer,
8213 arc->X, arc->Y, arc->Width, arc->Height, arc->StartAngle,
8214 arc->Delta, arc->Thickness, 0, NoFlags ());
8215 if (new_arc)
8216 AddObjectToCreateUndoList (LINE_TYPE, layer, new_arc, new_arc);
8219 END_LOOP;
8220 PIN_LOOP (element);
8222 FlagType f = NoFlags ();
8223 AddFlags (f, VIAFLAG);
8224 if (TEST_FLAG (HOLEFLAG, pin))
8225 AddFlags (f, HOLEFLAG);
8227 new_via = CreateNewVia (PCB->Data, pin->X, pin->Y,
8228 pin->Thickness, pin->Clearance, pin->Mask,
8229 pin->DrillingHole, pin->Number, f);
8230 if (new_via)
8231 AddObjectToCreateUndoList (VIA_TYPE, new_via, new_via, new_via);
8233 END_LOOP;
8234 PAD_LOOP (element);
8236 layer = TEST_FLAG (ONSOLDERFLAG, pad) ? bottom_copper : top_copper;
8237 new_line = CreateNewLineOnLayer (layer,
8238 pad->Point1.X, pad->Point1.Y,
8239 pad->Point2.X, pad->Point2.Y,
8240 pad->Thickness, pad->Clearance, NoFlags ());
8241 if (new_line)
8243 new_line->Number = STRDUP (pad->Number);
8244 AddObjectToCreateUndoList (LINE_TYPE, layer, new_line, new_line);
8247 END_LOOP;
8248 RemoveElement (element);
8251 /*---------------------------------------------------------------------------
8253 * break selected element(s) into pieces
8255 static int
8256 ActionSmash (int argc, char **argv, Coord x, Coord y)
8258 char *function = ARG (0);
8259 ElementType *element;
8260 void *ptr1, *ptr2, *ptr3;
8261 bool change = false;
8263 if (function)
8265 switch (GetFunctionID (function))
8267 case F_Object:
8268 if (SearchScreen (Crosshair.X, Crosshair.Y, ELEMENT_TYPE,
8269 &ptr1, &ptr2, &ptr3) != NO_TYPE)
8271 element = ptr1;
8272 change = true;
8273 SmashElement (element);
8275 break;
8277 case F_Selected:
8278 case F_SelectedElements:
8279 if (PCB->PinOn && PCB->ElementOn)
8281 ELEMENT_LOOP (PCB->Data);
8283 if (TEST_FLAG (SELECTEDFLAG, element))
8285 change = true;
8286 SmashElement (element);
8289 END_LOOP;
8291 break;
8293 default:
8294 AFAIL (smash);
8297 if (change)
8299 SetChangedFlag (true);
8300 Draw ();
8301 IncrementUndoSerialNumber ();
8303 return 0;
8306 AFAIL (smash);
8310 static const char repairdrills_syntax[] = N_("RepairDrills(CreatePins|CreateVias|CreateHoles,<layername>)");
8311 static const char repairdrills_help[] = N_("Repair and reconstruct elements containing pins, or discrete vias, where two matching single-pad elements are found (top and bottom sides, and a 0-length line represeting the drill size on the named layer.");
8313 static LayerType *
8314 GetLayerByName (char *name)
8316 int i;
8317 LayerType *layer;
8319 for (i = 0; i < max_copper_layer; i++)
8321 layer = PCB->Data->Layer + i;
8323 if (strcmp (layer->Name, name) == 0)
8324 return layer;
8327 return NULL;
8330 /*---------------------------------------------------------------------------
8332 * break selected element(s) into pieces
8334 static int
8335 ActionRepairDrills (int argc, char **argv, Coord x, Coord y)
8337 int function;
8338 bool save_show_bottom_side;
8339 bool save_invisible_objects;
8341 LayerType *drill_layer;
8343 if (argc != 2)
8344 AFAIL (repairdrills);
8346 function = GetFunctionID (ARG(0));
8347 drill_layer = GetLayerByName (ARG(1));
8349 if (drill_layer == NULL ||
8350 (function != F_CreatePins &&
8351 function != F_CreateVias &&
8352 function != F_CreateHoles))
8353 AFAIL (repairdrills);
8355 save_show_bottom_side = Settings.ShowBottomSide;
8356 save_invisible_objects = PCB->InvisibleObjectsOn;
8358 PCB->InvisibleObjectsOn = false;
8360 /* ITERATE OVER ALL LINE SEGMENTS ON "drill_layer" */
8361 LINE_LOOP (drill_layer);
8363 ElementType *top_pad_element;
8364 ElementType *bottom_pad_element;
8365 PadType *top_pad;
8366 PadType *bottom_pad;
8367 PadType *dummy;
8369 /* Pass over any lines which are not zero length */
8370 if (line->Point1.X != line->Point2.X ||
8371 line->Point1.Y != line->Point2.Y)
8372 continue;
8374 /* Now we have a target "drill", of width line->Thickness, we need to check
8375 * for corresponding pads on the top and bottom layers for it to mate with.
8377 * XXX: Should we delete those here, and make a list of new vias / pins to add?
8379 pcb_printf ("Found potential drill at (%$ms, %$ms) width is %$ms\t", line->Point1.X, line->Point1.Y, line->Thickness);
8381 /* Find a top side pad which corresponds to the drill location */
8382 Settings.ShowBottomSide = false;
8383 if (SearchObjectByLocation (PAD_TYPE, (void **)&top_pad_element, (void **)&top_pad, (void **)&dummy, line->Point1.X, line->Point1.Y, 0) != PAD_TYPE ||
8384 top_pad->Point1.X != line->Point1.X ||
8385 top_pad->Point1.Y != line->Point1.Y ||
8386 top_pad->Point2.X != line->Point1.X ||
8387 top_pad->Point2.Y != line->Point1.Y)
8389 printf ("Could not find top-side pad\n");
8390 continue;
8393 if (function == F_CreateHoles) {
8394 /* Create a hole at given location */
8396 pcb_printf ("Found a viable pin/via at (%$ms, %$ms), pad width %$ms, drill size %$ms\n",
8397 line->Point1.X, line->Point1.Y, top_pad->Thickness, line->Thickness);
8398 continue;
8401 /* Find a bottom side pad which corresponds to the drill location */
8402 Settings.ShowBottomSide = true;
8403 if (SearchObjectByLocation (PAD_TYPE, (void **)&bottom_pad_element, (void **)&bottom_pad, (void **)&dummy, line->Point1.X, line->Point1.Y, 0) != PAD_TYPE ||
8404 bottom_pad->Point1.X != line->Point1.X ||
8405 bottom_pad->Point1.Y != line->Point1.Y ||
8406 bottom_pad->Point2.X != line->Point1.X ||
8407 bottom_pad->Point2.Y != line->Point1.Y)
8409 printf ("Could not find bottom-side pad\n");
8410 continue;
8413 /* If the top and bottom pads are not the same radius, give up */
8414 if (top_pad->Thickness != bottom_pad->Thickness)
8416 printf ("top-side and bottom-side pad are not equal thickness\n");
8417 continue;
8420 printf ("\n");
8422 pcb_printf ("Found a viable pin/via at (%$ms, %$ms), pad width %$ms, drill size %$ms\n",
8423 line->Point1.X, line->Point1.Y, top_pad->Thickness, line->Thickness);
8426 END_LOOP;
8428 Settings.ShowBottomSide= save_show_bottom_side;
8429 PCB->InvisibleObjectsOn= save_invisible_objects;
8431 return 0;
8434 /* --------------------------------------------------------------------------- */
8436 static const char setvialayers_syntax[] =
8437 N_("SetViaLayers(Object|SelectedVias|Selected[,ThroughHole|TH])\n"
8438 "SetViaLayers(Object|SelectedVias|Selected,from,to)\n"
8439 "SetViaLayers(Object|SelectedVias|Selected,[c|-|from],[c|-|to])"
8442 static const char setvialayers_help[] =
8443 N_("Sets starting and ending layer for burried/blind/standard vias.");
8445 /* %start-doc actions setvialayers
8447 Specifies layers, which are connected by via.
8449 @table @code
8451 @item TH|ThroughHole
8452 The vias will be set as through-hole, connecting all layers
8454 @item from
8455 layer name or layer number of the first layer to be connected by via; "-" stands for unchanged, "c" stands for currently selected layer
8457 @item to
8458 layer name or layer number of the last layer to be connected by via; "-" stands for unchanged, "c" stands for currently selected layer
8460 @end table
8462 If no parameter us used, dialog is displayed (if implemented in the respective GUI HID).
8465 %end-doc */
8467 static bool
8468 identify_layer (char *layer_name, Cardinal *layer_no)
8470 int layer;
8472 if (strcmp (layer_name, "-") == 0)
8474 *layer_no = -1;
8475 return true;
8478 if (strcmp (layer_name, "c") == 0)
8480 if ((unsigned int)INDEXOFCURRENT < max_copper_layer)
8482 *layer_no = INDEXOFCURRENT;
8483 return true;
8487 layer = SearchLayerByName (PCB->Data, layer_name);
8488 if (layer == -1)
8490 if (sscanf (layer_name, "%d", &layer) != 1)
8491 layer = -1;
8494 if (layer != -1)
8495 *layer_no = layer;
8497 return (layer != -1);
8500 static int
8501 ActionSetViaLayers (int argc, char **argv, Coord x, Coord y)
8503 char *function = ARG (0);
8504 char *layername_from = ARG (1);
8505 char *layername_to = ARG (2);
8506 Cardinal layer_from ;
8507 Cardinal layer_to = -1;
8509 if (!function)
8510 AFAIL (setvialayers);
8512 if ( /* !gui->edit_attributes &&*/ argc < 2)
8514 Message (_("This GUI doesn't support Via Layers editing\n"));
8515 return 1;
8518 if (GetFunctionID (layername_from) == F_ThroughHole)
8520 layer_from = 0;
8521 layer_to = 0;
8523 else
8525 if (!identify_layer (layername_from, &layer_from)
8526 || !identify_layer (layername_to, &layer_to))
8528 Message (_("Sorry, wrong layers specified.\n"));
8529 return 1;
8533 /* ensure that layer_from < layer_to */
8534 if (layer_from != -1
8535 && layer_to != -1
8536 && layer_from > layer_to)
8538 int tmp;
8540 tmp = layer_from;
8541 layer_from = layer_to;
8542 layer_to = tmp;
8545 if (layer_to != -1)
8546 layer_to = min (layer_to, max_copper_layer-1);
8548 switch (GetFunctionID (function))
8550 case F_Object:
8552 int type;
8553 void *ptr1, *ptr2, *ptr3;
8555 if ((type =
8556 SearchScreen (Crosshair.X, Crosshair.Y, VIA_TYPE,
8557 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
8559 if (TEST_FLAG (LOCKFLAG, (PinType *) ptr1))
8560 Message (_("Sorry, the object is locked\n"));
8561 else
8563 if (ChangeObjectViaLayers (ptr1, ptr2, ptr3, layer_from, layer_to))
8565 SetChangedFlag (true);
8569 break;
8570 case F_SelectedVias:
8571 case F_Selected:
8572 if (ChangeSelectedViaLayers (layer_from, layer_to))
8574 SetChangedFlag (true);
8576 break;
8580 return 0;
8582 /* --------------------------------------------------------------------------- */
8584 HID_Action action_action_list[] = {
8585 {"AddRats", 0, ActionAddRats,
8586 addrats_help, addrats_syntax}
8588 {"Attributes", 0, ActionAttributes,
8589 attributes_help, attributes_syntax}
8591 {"Atomic", 0, ActionAtomic,
8592 atomic_help, atomic_syntax}
8594 {"AutoPlaceSelected", 0, ActionAutoPlaceSelected,
8595 autoplace_help, autoplace_syntax}
8597 {"AutoRoute", 0, ActionAutoRoute,
8598 autoroute_help, autoroute_syntax}
8600 {"ChangeClearSize", 0, ActionChangeClearSize,
8601 changeclearsize_help, changeclearsize_syntax}
8603 {"ChangeDrillSize", 0, ActionChange2ndSize,
8604 changedrillsize_help, changedrillsize_syntax}
8606 {"ChangeHole", 0, ActionChangeHole,
8607 changehold_help, changehold_syntax}
8609 {"ChangeJoin", 0, ActionChangeJoin,
8610 changejoin_help, changejoin_syntax}
8612 {"ChangeName", 0, ActionChangeName,
8613 changename_help, changename_syntax}
8615 {"ChangePaste", 0, ActionChangePaste,
8616 changepaste_help, changepaste_syntax}
8618 {"ChangePinName", 0, ActionChangePinName,
8619 changepinname_help, changepinname_syntax}
8621 {"ChangeSize", 0, ActionChangeSize,
8622 changesize_help, changesize_syntax}
8624 {"ChangeSquare", 0, ActionChangeSquare,
8625 changesquare_help, changesquare_syntax}
8627 {"ChangeOctagon", 0, ActionChangeOctagon,
8628 changeoctagon_help, changeoctagon_syntax}
8630 {"ClearSquare", 0, ActionClearSquare,
8631 clearsquare_help, clearsquare_syntax}
8633 {"ClearOctagon", 0, ActionClearOctagon,
8634 clearoctagon_help, clearoctagon_syntax}
8636 {"Connection", 0, ActionConnection,
8637 connection_help, connection_syntax}
8639 {"Delete", 0, ActionDelete,
8640 delete_help, delete_syntax}
8642 {"DeleteRats", 0, ActionDeleteRats,
8643 deleterats_help, deleterats_syntax}
8645 {"DisperseElements", 0, ActionDisperseElements,
8646 disperseelements_help, disperseelements_syntax}
8648 {"Display", 0, ActionDisplay,
8649 display_help, display_syntax}
8651 {"DRC", 0, ActionDRCheck,
8652 drc_help, drc_syntax}
8654 {"DumpLibrary", 0, ActionDumpLibrary,
8655 dumplibrary_help, dumplibrary_syntax}
8657 {"ExecuteFile", 0, ActionExecuteFile,
8658 executefile_help, executefile_syntax}
8660 {"Flip", N_("Click on Object or Flip Point"), ActionFlip,
8661 flip_help, flip_syntax}
8663 {"LoadFrom", 0, ActionLoadFrom,
8664 loadfrom_help, loadfrom_syntax}
8666 {"MarkCrosshair", 0, ActionMarkCrosshair,
8667 markcrosshair_help, markcrosshair_syntax}
8669 {"Message", 0, ActionMessage,
8670 message_help, message_syntax}
8672 {"MinMaskGap", 0, ActionMinMaskGap,
8673 minmaskgap_help, minmaskgap_syntax}
8675 {"MinClearGap", 0, ActionMinClearGap,
8676 mincleargap_help, mincleargap_syntax}
8678 {"Mode", 0, ActionMode,
8679 mode_help, mode_syntax}
8681 {"MorphPolygon", 0, ActionMorphPolygon,
8682 morphpolygon_help, morphpolygon_syntax}
8684 {"PasteBuffer", 0, ActionPasteBuffer,
8685 pastebuffer_help, pastebuffer_syntax}
8687 {"Quit", 0, ActionQuit,
8688 quit_help, quit_syntax}
8690 {"RemoveSelected", 0, ActionRemoveSelected,
8691 removeselected_help, removeselected_syntax}
8693 {"Renumber", 0, ActionRenumber,
8694 renumber_help, renumber_syntax}
8696 {"RepairDrills", 0, ActionRepairDrills,
8697 repairdrills_help, repairdrills_syntax}
8699 {"RipUp", 0, ActionRipUp,
8700 ripup_help, ripup_syntax}
8702 {"Select", 0, ActionSelect,
8703 select_help, select_syntax}
8705 {"Unselect", 0, ActionUnselect,
8706 unselect_help, unselect_syntax}
8708 {"SaveSettings", 0, ActionSaveSettings,
8709 savesettings_help, savesettings_syntax}
8711 {"SaveTo", 0, ActionSaveTo,
8712 saveto_help, saveto_syntax}
8714 {"SetSquare", 0, ActionSetSquare,
8715 setsquare_help, setsquare_syntax}
8717 {"SetOctagon", 0, ActionSetOctagon,
8718 setoctagon_help, setoctagon_syntax}
8720 {"SetThermal", 0, ActionSetThermal,
8721 setthermal_help, setthermal_syntax}
8723 {"SetValue", 0, ActionSetValue,
8724 setvalue_help, setvalue_syntax}
8726 {"Smash", 0, ActionSmash,
8727 smash_help, smash_syntax}
8729 {"ToggleHideName", 0, ActionToggleHideName,
8730 togglehidename_help, togglehidename_syntax}
8732 {"Undo", 0, ActionUndo,
8733 undo_help, undo_syntax}
8735 {"Redo", 0, ActionRedo,
8736 redo_help, redo_syntax}
8738 {"SetSame", N_("Select item to use attributes from"), ActionSetSame,
8739 setsame_help, setsame_syntax}
8741 {"SetFlag", 0, ActionSetFlag,
8742 setflag_help, setflag_syntax}
8744 {"ClrFlag", 0, ActionClrFlag,
8745 clrflag_help, clrflag_syntax}
8747 {"ChangeFlag", 0, ActionChangeFlag,
8748 changeflag_help, changeflag_syntax}
8750 {"Polygon", 0, ActionPolygon,
8751 polygon_help, polygon_syntax}
8753 {"RouteStyle", 0, ActionRouteStyle,
8754 routestyle_help, routestyle_syntax}
8756 {"MoveObject", N_("Select an Object"), ActionMoveObject,
8757 moveobject_help, moveobject_syntax}
8759 {"MoveToCurrentLayer", 0, ActionMoveToCurrentLayer,
8760 movetocurrentlayer_help, movetocurrentlayer_syntax}
8762 {"New", 0, ActionNew,
8763 new_help, new_syntax}
8765 {"pscalib", 0, ActionPSCalib}
8767 {"ElementList", 0, ActionElementList,
8768 elementlist_help, elementlist_syntax}
8770 {"ElementSetAttr", 0, ActionElementSetAttr,
8771 elementsetattr_help, elementsetattr_syntax}
8773 {"ExecCommand", 0, ActionExecCommand,
8774 execcommand_help, execcommand_syntax}
8776 {"Import", 0, ActionImport,
8777 import_help, import_syntax}
8779 {"SetViaLayers", 0, ActionSetViaLayers,
8780 setvialayers_help, setvialayers_syntax}
8784 REGISTER_ACTIONS (action_action_list)