1 /*!
2 * \file src/action.c
3 *
4 * \brief Action routines for output window.
5 *
6 * <hr>
7 *
8 * <h1><b>Copyright.</b></h1>\n
9 *
10 * PCB, interactive printed circuit board design
11 *
12 * Copyright (C) 1994,1995,1996 Thomas Nau
13 *
14 * Copyright (C) 1997, 1998, 1999, 2000, 2001 Harry Eaton
15 *
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.
20 *
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.
25 *
26 * You should have received a copy of the GNU General Public License along
27 * with this program; if not, write to the Free Software Foundation, Inc.,
28 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
29 *
30 * Contact addresses for paper mail and Email:
31 * Harry Eaton, 6697 Buttonhole Ct, Columbia, MD 21044, USA
32 * haceaton@aplcomm.jhuapl.edu
33 */
34
35 #ifdef HAVE_CONFIG_H
36 #include "config.h"
37 #endif
38
39 #include "global.h"
40
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 "flags.h"
55 #include "hid.h"
56 #include "insert.h"
57 #include "line.h"
58 #include "mymem.h"
59 #include "misc.h"
60 #include "mirror.h"
61 #include "move.h"
62 #include "polygon.h"
63 /*#include "print.h"*/
64 #include "rats.h"
65 #include "remove.h"
66 #include "report.h"
67 #include "rotate.h"
68 #include "rubberband.h"
69 #include "search.h"
70 #include "select.h"
71 #include "set.h"
72 #include "thermal.h"
73 #include "undo.h"
74 #include "rtree.h"
75 #include "macro.h"
76 #include "pcb-printf.h"
77
78 #include <assert.h>
79 #include <stdlib.h> /* rand() */
80
81 #ifdef HAVE_LIBDMALLOC
82 #include <dmalloc.h>
83 #endif
84
85 /* for fork() and friends */
86 #ifdef HAVE_UNISTD_H
87 #include <unistd.h>
88 #endif
89
90 #ifdef HAVE_SYS_WAIT_H
91 #include <sys/wait.h>
92 #endif
93
94 /* ---------------------------------------------------------------------------
95 * some local types
96 */
97 typedef enum
98 {
99 F_AddSelected,
100 F_All,
101 F_AllConnections,
102 F_AllRats,
103 F_AllUnusedPins,
104 F_Arc,
105 F_Arrow,
106 F_Block,
107 F_Description,
108 F_Cancel,
109 F_Center,
110 F_Clear,
111 F_ClearAndRedraw,
112 F_ClearList,
113 F_Close,
114 F_Found,
115 F_Connection,
116 F_Convert,
117 F_Copy,
118 F_CycleClip,
119 F_CycleCrosshair,
120 F_DeleteRats,
121 F_Drag,
122 F_DrillReport,
123 F_Element,
124 F_ElementByName,
125 F_ElementConnections,
126 F_ElementToBuffer,
127 F_Escape,
128 F_Find,
129 F_FlipElement,
130 F_FoundPins,
131 F_Grid,
132 F_InsertPoint,
133 F_Layer,
134 F_Layout,
135 F_LayoutAs,
136 F_LayoutToBuffer,
137 F_Line,
138 F_LineSize,
139 F_Lock,
140 F_Mirror,
141 F_Move,
142 F_NameOnPCB,
143 F_Netlist,
144 F_NetByName,
145 F_None,
146 F_Notify,
147 F_Object,
148 F_ObjectByName,
149 F_PasteBuffer,
150 F_PadByName,
151 F_PinByName,
152 F_PinOrPadName,
153 F_Pinout,
154 F_Polygon,
155 F_PolygonHole,
156 F_PreviousPoint,
157 F_RatsNest,
158 F_Rectangle,
159 F_Redraw,
160 F_Release,
161 F_Revert,
162 F_Remove,
163 F_RemoveSelected,
164 F_Report,
165 F_Reset,
166 F_ResetLinesAndPolygons,
167 F_ResetPinsViasAndPads,
168 F_Restore,
169 F_Rotate,
170 F_Save,
171 F_Selected,
172 F_SelectedArcs,
173 F_SelectedElements,
174 F_SelectedLines,
175 F_SelectedNames,
176 F_SelectedObjects,
177 F_SelectedPads,
178 F_SelectedPins,
179 F_SelectedTexts,
180 F_SelectedVias,
181 F_SelectedRats,
182 F_Stroke,
183 F_Text,
184 F_TextByName,
185 F_TextScale,
186 F_Thermal,
187 F_ToLayout,
188 F_ToggleAllDirections,
189 F_ToggleAutoDRC,
190 F_ToggleClearLine,
191 F_ToggleFullPoly,
192 F_ToggleGrid,
193 F_ToggleHideNames,
194 F_ToggleMask,
195 F_ToggleName,
196 F_ToggleObject,
197 F_ToggleShowDRC,
198 F_ToggleLiveRoute,
199 F_ToggleRubberBandMode,
200 F_ToggleStartDirection,
201 F_ToggleSnapPin,
202 F_ToggleThindraw,
203 F_ToggleLockNames,
204 F_ToggleOnlyNames,
205 F_ToggleThindrawPoly,
206 F_ToggleOrthoMove,
207 F_ToggleLocalRef,
208 F_ToggleCheckPlanes,
209 F_ToggleUniqueNames,
210 F_Via,
211 F_ViaByName,
212 F_Value,
213 F_ViaDrillingHole,
214 F_ViaSize,
215 F_Zoom,
216 F_ThroughHole,
217 F_BuriedVias,
218 F_ToggleAutoBuriedVias
219 }
220 FunctionID;
221
222 typedef struct /* used to identify subfunctions */
223 {
224 char *Identifier;
225 FunctionID ID;
226 }
227 FunctionType;
228
229 /* --------------------------------------------------------------------------- */
230
231 /* %start-doc actions 00delta
232
233 Many actions take a @code{delta} parameter as the last parameter,
234 which is an amount to change something. That @code{delta} may include
235 units, as an additional parameter, such as @code{Action(Object,5,mm)}.
236 If no units are specified, the default is PCB's native units
237 (currently 1/100 mil). Also, if the delta is prefixed by @code{+} or
238 @code{-}, the size is increased or decreased by that amount.
239 Otherwise, the size size is set to the given amount.
240
241 @example
242 Action(Object,5,mil)
243 Action(Object,+0.5,mm)
244 Action(Object,-1)
245 @end example
246
247 Actions which take a @code{delta} parameter which do not accept all
248 these options will specify what they do take.
249
250 %end-doc */
251
252 /* %start-doc actions 00objects
253
254 Many actions act on indicated objects on the board. They will have
255 parameters like @code{ToggleObject} or @code{SelectedVias} to indicate
256 what group of objects they act on. Unless otherwise specified, these
257 parameters are defined as follows:
258
259 @table @code
260
261 @item Object
262 @itemx ToggleObject
263 Affects the object under the mouse pointer. If this action is invoked
264 from a menu or script, the user will be prompted to click on an
265 object, which is then the object affected.
266
267 @item Selected
268 @itemx SelectedObjects
269
270 Affects all objects which are currently selected. At least, all
271 selected objects for which the given action makes sense.
272
273 @item SelectedPins
274 @itemx SelectedVias
275 @itemx Selected@var{Type}
276 @itemx @i{etc}
277 Affects all objects which are both selected and of the @var{Type} specified.
278
279 @end table
280
281 %end-doc */
282
283 /* %start-doc actions 00macros
284
285 @macro pinshapes
286
287 Pins, pads, and vias can have various shapes. All may be round. Pins
288 and pads may be square (obviously "square" pads are usually
289 rectangular). Pins and vias may be octagonal. When you change a
290 shape flag of an element, you actually change all of its pins and
291 pads.
292
293 Note that the square flag takes precedence over the octagon flag,
294 thus, if both the square and octagon flags are set, the object is
295 square. When the square flag is cleared, the pins and pads will be
296 either round or, if the octagon flag is set, octagonal.
297
298 @end macro
299
300 %end-doc */
301
302 /* ---------------------------------------------------------------------------
303 * some local identifiers
304 */
305 static PointType InsertedPoint;
306 static LayerType *lastLayer;
307 static struct
308 {
309 PolygonType *poly;
310 LineType line;
311 }
312 fake;
313
314 static struct
315 {
316 Coord X, Y;
317 Cardinal Buffer;
318 bool Click;
319 bool Moving; /* selected type clicked on */
320 int Hit; /* move type clicked on */
321 void *ptr1;
322 void *ptr2;
323 void *ptr3;
324 }
325 Note;
326
327 static int defer_updates = 0;
328 static int defer_needs_update = 0;
329
330 static Cardinal polyIndex = 0;
331 static bool saved_mode = false;
332 #ifdef HAVE_LIBSTROKE
333 static bool mid_stroke = false;
334 static BoxType StrokeBox;
335 #endif
336 static FunctionType Functions[] = {
337 {"AddSelected", F_AddSelected},
338 {"All", F_All},
339 {"AllConnections", F_AllConnections},
340 {"AllRats", F_AllRats},
341 {"AllUnusedPins", F_AllUnusedPins},
342 {"Arc", F_Arc},
343 {"Arrow", F_Arrow},
344 {"Block", F_Block},
345 {"Description", F_Description},
346 {"Cancel", F_Cancel},
347 {"Center", F_Center},
348 {"Clear", F_Clear},
349 {"ClearAndRedraw", F_ClearAndRedraw},
350 {"ClearList", F_ClearList},
351 {"Close", F_Close},
352 {"Found", F_Found},
353 {"Connection", F_Connection},
354 {"Convert", F_Convert},
355 {"Copy", F_Copy},
356 {"CycleClip", F_CycleClip},
357 {"CycleCrosshair", F_CycleCrosshair},
358 {"DeleteRats", F_DeleteRats},
359 {"Drag", F_Drag},
360 {"DrillReport", F_DrillReport},
361 {"Element", F_Element},
362 {"ElementByName", F_ElementByName},
363 {"ElementConnections", F_ElementConnections},
364 {"ElementToBuffer", F_ElementToBuffer},
365 {"Escape", F_Escape},
366 {"Find", F_Find},
367 {"FlipElement", F_FlipElement},
368 {"FoundPins", F_FoundPins},
369 {"Grid", F_Grid},
370 {"InsertPoint", F_InsertPoint},
371 {"Layer", F_Layer},
372 {"Layout", F_Layout},
373 {"LayoutAs", F_LayoutAs},
374 {"LayoutToBuffer", F_LayoutToBuffer},
375 {"Line", F_Line},
376 {"LineSize", F_LineSize},
377 {"Lock", F_Lock},
378 {"Mirror", F_Mirror},
379 {"Move", F_Move},
380 {"NameOnPCB", F_NameOnPCB},
381 {"Netlist", F_Netlist},
382 {"NetByName", F_NetByName},
383 {"None", F_None},
384 {"Notify", F_Notify},
385 {"Object", F_Object},
386 {"ObjectByName", F_ObjectByName},
387 {"PasteBuffer", F_PasteBuffer},
388 {"PadByName", F_PadByName},
389 {"PinByName", F_PinByName},
390 {"PinOrPadName", F_PinOrPadName},
391 {"Pinout", F_Pinout},
392 {"Polygon", F_Polygon},
393 {"PolygonHole", F_PolygonHole},
394 {"PreviousPoint", F_PreviousPoint},
395 {"RatsNest", F_RatsNest},
396 {"Rectangle", F_Rectangle},
397 {"Redraw", F_Redraw},
398 {"Release", F_Release},
399 {"Remove", F_Remove},
400 {"RemoveSelected", F_RemoveSelected},
401 {"Report", F_Report},
402 {"Reset", F_Reset},
403 {"ResetLinesAndPolygons", F_ResetLinesAndPolygons},
404 {"ResetPinsViasAndPads", F_ResetPinsViasAndPads},
405 {"Restore", F_Restore},
406 {"Revert", F_Revert},
407 {"Rotate", F_Rotate},
408 {"Save", F_Save},
409 {"Selected", F_Selected},
410 {"SelectedArcs", F_SelectedArcs},
411 {"SelectedElements", F_SelectedElements},
412 {"SelectedLines", F_SelectedLines},
413 {"SelectedNames", F_SelectedNames},
414 {"SelectedObjects", F_SelectedObjects},
415 {"SelectedPins", F_SelectedPins},
416 {"SelectedPads", F_SelectedPads},
417 {"SelectedRats", F_SelectedRats},
418 {"SelectedTexts", F_SelectedTexts},
419 {"SelectedVias", F_SelectedVias},
420 {"Stroke", F_Stroke},
421 {"Text", F_Text},
422 {"TextByName", F_TextByName},
423 {"TextScale", F_TextScale},
424 {"Thermal", F_Thermal},
425 {"ToLayout", F_ToLayout},
426 {"Toggle45Degree", F_ToggleAllDirections},
427 {"ToggleClearLine", F_ToggleClearLine},
428 {"ToggleFullPoly", F_ToggleFullPoly},
429 {"ToggleGrid", F_ToggleGrid},
430 {"ToggleMask", F_ToggleMask},
431 {"ToggleName", F_ToggleName},
432 {"ToggleObject", F_ToggleObject},
433 {"ToggleRubberBandMode", F_ToggleRubberBandMode},
434 {"ToggleStartDirection", F_ToggleStartDirection},
435 {"ToggleSnapPin", F_ToggleSnapPin},
436 {"ToggleThindraw", F_ToggleThindraw},
437 {"ToggleThindrawPoly", F_ToggleThindrawPoly},
438 {"ToggleLockNames", F_ToggleLockNames},
439 {"ToggleOnlyNames", F_ToggleOnlyNames},
440 {"ToggleHideNames", F_ToggleHideNames},
441 {"ToggleCheckPlanes", F_ToggleCheckPlanes},
442 {"ToggleLocalRef", F_ToggleLocalRef},
443 {"ToggleOrthoMove", F_ToggleOrthoMove},
444 {"ToggleShowDRC", F_ToggleShowDRC},
445 {"ToggleLiveRoute", F_ToggleLiveRoute},
446 {"ToggleAutoDRC", F_ToggleAutoDRC},
447 {"ToggleUniqueNames", F_ToggleUniqueNames},
448 {"Value", F_Value},
449 {"Via", F_Via},
450 {"ViaByName", F_ViaByName},
451 {"ViaSize", F_ViaSize},
452 {"ViaDrillingHole", F_ViaDrillingHole},
453 {"Zoom", F_Zoom},
454 {"ThroughHole", F_ThroughHole},
455 {"TH", F_ThroughHole},
456 {"BuriedVias", F_BuriedVias},
457 {"ToggleAutoBuriedVias", F_ToggleAutoBuriedVias}
458 };
459
460 /* ---------------------------------------------------------------------------
461 * some local routines
462 */
463 static int GetFunctionID (String);
464 static void AdjustAttachedBox (void);
465 static void NotifyLine (void);
466 static void NotifyBlock (void);
467 static void NotifyMode (void);
468 static void ClearWarnings (void);
469 #ifdef HAVE_LIBSTROKE
470 static void FinishStroke (void);
471 extern void stroke_init (void);
472 extern void stroke_record (int x, int y);
473 extern int stroke_trans (char *s);
474 #endif
475 static void ChangeFlag (char *, char *, int, char *);
476
477 #ifdef HAVE_LIBSTROKE
478
479 /*!
480 * \brief Try to recognize the stroke sent.
481 */
482 void
FinishStroke(void)483 FinishStroke (void)
484 {
485 char msg[255];
486 int type;
487 unsigned long num;
488 void *ptr1, *ptr2, *ptr3;
489
490 mid_stroke = false;
491 if (stroke_trans (msg))
492 {
493 num = atoi (msg);
494 switch (num)
495 {
496 case 456:
497 if (Settings.Mode == LINE_MODE)
498 {
499 SetMode (LINE_MODE);
500 }
501 break;
502 case 9874123:
503 case 74123:
504 case 987412:
505 case 8741236:
506 case 874123:
507 RotateScreenObject (StrokeBox.X1, StrokeBox.Y1, SWAP_IDENT ? 1 : 3);
508 break;
509 case 7896321:
510 case 786321:
511 case 789632:
512 case 896321:
513 RotateScreenObject (StrokeBox.X1, StrokeBox.Y1, SWAP_IDENT ? 3 : 1);
514 break;
515 case 258:
516 SetMode (LINE_MODE);
517 break;
518 case 852:
519 SetMode (ARROW_MODE);
520 break;
521 case 1478963:
522 ActionUndo ("");
523 break;
524 case 147423:
525 case 147523:
526 case 1474123:
527 Redo (true);
528 break;
529 case 148963:
530 case 147863:
531 case 147853:
532 case 145863:
533 SetMode (VIA_MODE);
534 break;
535 case 951:
536 case 9651:
537 case 9521:
538 case 9621:
539 case 9851:
540 case 9541:
541 case 96521:
542 case 96541:
543 case 98541:
544 /* XXX: FIXME: Call a zoom-extents action */
545 break;
546 case 159:
547 case 1269:
548 case 1259:
549 case 1459:
550 case 1569:
551 case 1589:
552 case 12569:
553 case 12589:
554 case 14589:
555 /* XXX: FIXME: Zoom to fit the box StrokeBox.[X1,Y1] - StrokeBox.[X2,Y2] */
556 break;
557
558 default:
559 Message (_("Unknown stroke %s\n"), msg);
560 break;
561 }
562 }
563 else
564 gui->beep ();
565 }
566 #endif
567
568 /*!
569 * \brief Clear warning color from pins/pads.
570 */
571 static void
ClearWarnings()572 ClearWarnings ()
573 {
574 Settings.RatWarn = false;
575 ALLPIN_LOOP (PCB->Data);
576 {
577 if (TEST_FLAG (WARNFLAG, pin))
578 {
579 CLEAR_FLAG (WARNFLAG, pin);
580 DrawPin (pin);
581 }
582 }
583 ENDALL_LOOP;
584 ALLPAD_LOOP (PCB->Data);
585 {
586 if (TEST_FLAG (WARNFLAG, pad))
587 {
588 CLEAR_FLAG (WARNFLAG, pad);
589 DrawPad (pad);
590 }
591 }
592 ENDALL_LOOP;
593 Draw ();
594 }
595
596 /*!
597 * \brief Click callback.
598 *
599 * This is called a clicktime after a mouse down, to we can distinguish
600 * between short clicks (typically: select or create something) and long
601 * clicks. Long clicks typically drag something.
602 */
603 static void
click_cb(hidval hv)604 click_cb (hidval hv)
605 {
606 if (Note.Click)
607 {
608 notify_crosshair_change (false);
609 Note.Click = false;
610 if (Note.Moving && !gui->shift_is_pressed ())
611 {
612 Note.Buffer = Settings.BufferNumber;
613 SetBufferNumber (MAX_BUFFER - 1);
614 ClearBuffer (PASTEBUFFER);
615 AddSelectedToBuffer (PASTEBUFFER, Note.X, Note.Y, true);
616 SaveUndoSerialNumber ();
617 RemoveSelected ();
618 SaveMode ();
619 saved_mode = true;
620 SetMode (PASTEBUFFER_MODE);
621 }
622 else if (Note.Hit && !gui->shift_is_pressed ())
623 {
624 SaveMode ();
625 saved_mode = true;
626 SetMode (gui->control_is_pressed ()? COPY_MODE : MOVE_MODE);
627 Crosshair.AttachedObject.Ptr1 = Note.ptr1;
628 Crosshair.AttachedObject.Ptr2 = Note.ptr2;
629 Crosshair.AttachedObject.Ptr3 = Note.ptr3;
630 Crosshair.AttachedObject.Type = Note.Hit;
631 AttachForCopy (Note.X, Note.Y);
632 }
633 else
634 {
635 BoxType box;
636
637 Note.Hit = 0;
638 Note.Moving = false;
639 SaveUndoSerialNumber ();
640 box.X1 = -MAX_COORD;
641 box.Y1 = -MAX_COORD;
642 box.X2 = MAX_COORD;
643 box.Y2 = MAX_COORD;
644 /* unselect first if shift key not down */
645 if (!gui->shift_is_pressed () && SelectBlock (&box, false))
646 SetChangedFlag (true);
647 NotifyBlock ();
648 Crosshair.AttachedBox.Point1.X = Note.X;
649 Crosshair.AttachedBox.Point1.Y = Note.Y;
650 }
651 notify_crosshair_change (true);
652 }
653 }
654
655 /*!
656 * \brief This is typically called when the mouse has moved or the mouse
657 * button was released.
658 */
659 static void
ReleaseMode(void)660 ReleaseMode (void)
661 {
662 BoxType box;
663
664 if (Note.Click)
665 {
666 BoxType box;
667
668 box.X1 = -MAX_COORD;
669 box.Y1 = -MAX_COORD;
670 box.X2 = MAX_COORD;
671 box.Y2 = MAX_COORD;
672
673 Note.Click = false; /* inhibit timer action */
674 SaveUndoSerialNumber ();
675 /* unselect first if shift key not down */
676 if (!gui->shift_is_pressed ())
677 {
678 if (SelectBlock (&box, false))
679 SetChangedFlag (true);
680 if (Note.Moving)
681 {
682 Note.Moving = 0;
683 Note.Hit = 0;
684 return;
685 }
686 }
687 /* Restore the SN so that if we select something the deselect/select combo
688 gets the same SN. */
689 RestoreUndoSerialNumber();
690 if (SelectObject ())
691 SetChangedFlag (true);
692 else
693 /* We didn't select anything new, so, the deselection should get its
694 own SN. */
695 IncrementUndoSerialNumber();
696 Note.Hit = 0;
697 Note.Moving = 0;
698 }
699 else if (Note.Moving)
700 {
701 RestoreUndoSerialNumber ();
702 NotifyMode ();
703 ClearBuffer (PASTEBUFFER);
704 SetBufferNumber (Note.Buffer);
705 Note.Moving = false;
706 Note.Hit = 0;
707 }
708 else if (Note.Hit)
709 {
710 NotifyMode ();
711 Note.Hit = 0;
712 }
713 else if (Settings.Mode == ARROW_MODE)
714 {
715 box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
716 Crosshair.AttachedBox.Point2.X);
717 box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
718 Crosshair.AttachedBox.Point2.Y);
719 box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
720 Crosshair.AttachedBox.Point2.X);
721 box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
722 Crosshair.AttachedBox.Point2.Y);
723 RestoreUndoSerialNumber ();
724 if (SelectBlock (&box, true))
725 SetChangedFlag (true);
726 else if (Bumped)
727 IncrementUndoSerialNumber ();
728 Crosshair.AttachedBox.State = STATE_FIRST;
729 }
730 if (saved_mode)
731 RestoreMode ();
732 saved_mode = false;
733 }
734
735 #define HSIZE 257
736 static char function_hash[HSIZE];
737 static int hash_initted = 0;
738
739 static int
hashfunc(String s)740 hashfunc(String s)
741 {
742 int i = 0;
743 while (*s)
744 {
745 i ^= i >> 16;
746 i = (i * 13) ^ (unsigned char)tolower((int) *s);
747 s ++;
748 }
749 i = (unsigned int)i % HSIZE;
750 return i;
751 }
752
753 /*!
754 * \brief Get function ID of passed string.
755 */
756 static int
GetFunctionID(String Ident)757 GetFunctionID (String Ident)
758 {
759 int i, h;
760
761 if (Ident == 0)
762 return -1;
763
764 if (!hash_initted)
765 {
766 hash_initted = 1;
767 if (HSIZE < ENTRIES (Functions) * 2)
768 {
769 fprintf(stderr, _("Error: function hash size too small (%d vs %lu at %s:%d)\n"),
770 HSIZE, (unsigned long) ENTRIES (Functions)*2, __FILE__, __LINE__);
771 exit(1);
772 }
773 if (ENTRIES (Functions) > 254)
774 {
775 /* Change 'char' to 'int' and remove this when we get to 256
776 strings to hash. */
777 fprintf(stderr, _("Error: function hash type too small (%d vs %lu at %s:%d)\n"),
778 256, (unsigned long) ENTRIES (Functions), __FILE__, __LINE__);
779 exit(1);
780
781 }
782 for (i=ENTRIES (Functions)-1; i>=0; i--)
783 {
784 h = hashfunc (Functions[i].Identifier);
785 while (function_hash[h])
786 h = (h + 1) % HSIZE;
787 function_hash[h] = i + 1;
788 }
789 }
790
791 i = hashfunc (Ident);
792 while (1)
793 {
794 /* We enforce the "hash table bigger than function table" rule,
795 so we know there will be at least one zero entry to find. */
796 if (!function_hash[i])
797 return (-1);
798 if (!strcasecmp (Ident, Functions[function_hash[i]-1].Identifier))
799 return ((int) Functions[function_hash[i]-1].ID);
800 i = (i + 1) % HSIZE;
801 }
802 }
803
804 /*!
805 * \brief Set new coordinates if in 'RECTANGLE' mode.
806 *
807 * The cursor shape is also adjusted.
808 */
809 static void
AdjustAttachedBox(void)810 AdjustAttachedBox (void)
811 {
812 if (Settings.Mode == ARC_MODE)
813 {
814 Crosshair.AttachedBox.otherway = gui->shift_is_pressed ();
815 return;
816 }
817 switch (Crosshair.AttachedBox.State)
818 {
819 case STATE_SECOND: /* one corner is selected */
820 {
821 /* update coordinates */
822 Crosshair.AttachedBox.Point2.X = Crosshair.X;
823 Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
824 break;
825 }
826 }
827 }
828
829 /*!
830 * \brief Adjusts the objects which are to be created like attached
831 * lines.
832 */
833 void
AdjustAttachedObjects(void)834 AdjustAttachedObjects (void)
835 {
836 PointType *pnt;
837 switch (Settings.Mode)
838 {
839 /* update at least an attached block (selection) */
840 case NO_MODE:
841 case ARROW_MODE:
842 if (Crosshair.AttachedBox.State)
843 {
844 Crosshair.AttachedBox.Point2.X = Crosshair.X;
845 Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
846 }
847 break;
848
849 /* rectangle creation mode */
850 case RECTANGLE_MODE:
851 case ARC_MODE:
852 AdjustAttachedBox ();
853 break;
854
855 /* polygon creation mode */
856 case POLYGON_MODE:
857 case POLYGONHOLE_MODE:
858 AdjustAttachedLine ();
859 break;
860 /* line creation mode */
861 case LINE_MODE:
862 if (PCB->RatDraw || PCB->Clipping == 0)
863 AdjustAttachedLine ();
864 else
865 AdjustTwoLine (PCB->Clipping - 1);
866 break;
867 /* point insertion mode */
868 case INSERTPOINT_MODE:
869 pnt = AdjustInsertPoint ();
870 if (pnt)
871 InsertedPoint = *pnt;
872 break;
873 case ROTATE_MODE:
874 break;
875 }
876 }
877
878 /*!
879 * \brief Creates points of a line.
880 */
881 static void
NotifyLine(void)882 NotifyLine (void)
883 {
884 int type = NO_TYPE;
885 void *ptr1, *ptr2, *ptr3;
886
887 if (!Marked.status || TEST_FLAG (LOCALREFFLAG, PCB))
888 SetLocalRef (Crosshair.X, Crosshair.Y, true);
889 switch (Crosshair.AttachedLine.State)
890 {
891 case STATE_FIRST: /* first point */
892 if (PCB->RatDraw && SearchScreen (Crosshair.X, Crosshair.Y,
893 PAD_TYPE | PIN_TYPE, &ptr1, &ptr1,
894 &ptr1) == NO_TYPE)
895 {
896 gui->beep ();
897 break;
898 }
899 if (TEST_FLAG (AUTODRCFLAG, PCB) && Settings.Mode == LINE_MODE)
900 {
901 type = SearchScreen (Crosshair.X, Crosshair.Y,
902 PIN_TYPE | PAD_TYPE | VIA_TYPE, &ptr1, &ptr2,
903 &ptr3);
904 LookupConnection (Crosshair.X, Crosshair.Y, true, 1, CONNECTEDFLAG, false);
905 LookupConnection (Crosshair.X, Crosshair.Y, true, 1, FOUNDFLAG, true);
906 }
907 if (type == PIN_TYPE || type == VIA_TYPE)
908 {
909 Crosshair.AttachedLine.Point1.X =
910 Crosshair.AttachedLine.Point2.X = ((PinType *) ptr2)->X;
911 Crosshair.AttachedLine.Point1.Y =
912 Crosshair.AttachedLine.Point2.Y = ((PinType *) ptr2)->Y;
913 }
914 else if (type == PAD_TYPE)
915 {
916 PadType *pad = (PadType *) ptr2;
917 double d1 = Distance (Crosshair.X, Crosshair.Y, pad->Point1.X, pad->Point1.Y);
918 double d2 = Distance (Crosshair.X, Crosshair.Y, pad->Point2.X, pad->Point2.Y);
919 if (d2 < d1)
920 {
921 Crosshair.AttachedLine.Point1 =
922 Crosshair.AttachedLine.Point2 = pad->Point2;
923 }
924 else
925 {
926 Crosshair.AttachedLine.Point1 =
927 Crosshair.AttachedLine.Point2 = pad->Point1;
928 }
929 }
930 else
931 {
932 Crosshair.AttachedLine.Point1.X =
933 Crosshair.AttachedLine.Point2.X = Crosshair.X;
934 Crosshair.AttachedLine.Point1.Y =
935 Crosshair.AttachedLine.Point2.Y = Crosshair.Y;
936 }
937 Crosshair.AttachedLine.State = STATE_SECOND;
938 break;
939
940 case STATE_SECOND:
941 /* fall through to third state too */
942 lastLayer = CURRENT;
943 default: /* all following points */
944 Crosshair.AttachedLine.State = STATE_THIRD;
945 break;
946 }
947 }
948
949 /*!
950 * \brief Create first or second corner of a marked block.
951 */
952 static void
NotifyBlock(void)953 NotifyBlock (void)
954 {
955 notify_crosshair_change (false);
956 switch (Crosshair.AttachedBox.State)
957 {
958 case STATE_FIRST: /* setup first point */
959 Crosshair.AttachedBox.Point1.X =
960 Crosshair.AttachedBox.Point2.X = Crosshair.X;
961 Crosshair.AttachedBox.Point1.Y =
962 Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
963 Crosshair.AttachedBox.State = STATE_SECOND;
964 break;
965
966 case STATE_SECOND: /* setup second point */
967 Crosshair.AttachedBox.State = STATE_THIRD;
968 break;
969 }
970 notify_crosshair_change (true);
971 }
972
973
974 /*!
975 * \brief This is called after every mode change, like mouse button pressed,
976 * mouse button released, dragging something started or a different tool
977 * selected.
978 *
979 * It does what's appropriate for the current mode setting.
980 * This can also mean creation of an object at the current crosshair location.
981 *
982 * New created objects are added to the create undo list of course.
983 */
984 static void
NotifyMode(void)985 NotifyMode (void)
986 {
987 void *ptr1, *ptr2, *ptr3;
988 int type;
989
990 if (Settings.RatWarn)
991 ClearWarnings ();
992 switch (Settings.Mode)
993 {
994 case ARROW_MODE:
995 {
996 int test;
997 hidval hv;
998
999 Note.Click = true;
1000 /* do something after click time */
1001 gui->add_timer (click_cb, CLICK_TIME, hv);
1002
1003 /* see if we clicked on something already selected
1004 * (Note.Moving) or clicked on a MOVE_TYPE
1005 * (Note.Hit)
1006 */
1007 for (test = (SELECT_TYPES | MOVE_TYPES) & ~RATLINE_TYPE;
1008 test; test &= ~type)
1009 {
1010 type = SearchScreen (Note.X, Note.Y, test, &ptr1, &ptr2, &ptr3);
1011 if (!Note.Hit && (type & MOVE_TYPES) &&
1012 !TEST_FLAG (LOCKFLAG, (PinType *) ptr2))
1013 {
1014 Note.Hit = type;
1015 Note.ptr1 = ptr1;
1016 Note.ptr2 = ptr2;
1017 Note.ptr3 = ptr3;
1018 }
1019 if (!Note.Moving && (type & SELECT_TYPES) &&
1020 TEST_FLAG (SELECTEDFLAG, (PinType *) ptr2))
1021 Note.Moving = true;
1022 if ((Note.Hit && Note.Moving) || type == NO_TYPE)
1023 break;
1024 }
1025 break;
1026 }
1027
1028 case VIA_MODE:
1029 {
1030 PinType *via;
1031
1032 if (!PCB->ViaOn)
1033 {
1034 Message (_("You must turn via visibility on before\n"
1035 "you can place vias\n"));
1036 break;
1037 }
1038 if ((via = CreateNewVia (PCB->Data, Note.X, Note.Y,
1039 Settings.ViaThickness, 2 * Settings.Keepaway,
1040 Settings.ViaMaskAperture, Settings.ViaDrillingHole, NULL,
1041 NoFlags ())) != NULL)
1042 {
1043 AddObjectToCreateUndoList (VIA_TYPE, via, via, via);
1044 if (gui->shift_is_pressed ())
1045 ChangeObjectThermal (VIA_TYPE, via, via, via, PCB->ThermStyle);
1046 IncrementUndoSerialNumber ();
1047 DrawVia (via);
1048 Draw ();
1049 }
1050 break;
1051 }
1052
1053 case ARC_MODE:
1054 {
1055 switch (Crosshair.AttachedBox.State)
1056 {
1057 case STATE_FIRST:
1058 Crosshair.AttachedBox.Point1.X =
1059 Crosshair.AttachedBox.Point2.X = Note.X;
1060 Crosshair.AttachedBox.Point1.Y =
1061 Crosshair.AttachedBox.Point2.Y = Note.Y;
1062 Crosshair.AttachedBox.State = STATE_SECOND;
1063 break;
1064
1065 case STATE_SECOND:
1066 case STATE_THIRD:
1067 {
1068 ArcType *arc;
1069 Coord wx, wy;
1070 Angle sa, dir;
1071
1072 wx = Note.X - Crosshair.AttachedBox.Point1.X;
1073 wy = Note.Y - Crosshair.AttachedBox.Point1.Y;
1074 if (XOR (Crosshair.AttachedBox.otherway, abs (wy) > abs (wx)))
1075 {
1076 Crosshair.AttachedBox.Point2.X =
1077 Crosshair.AttachedBox.Point1.X + abs (wy) * SGNZ (wx);
1078 sa = (wx >= 0) ? 0 : 180;
1079 #ifdef ARC45
1080 if (abs (wy) / 2 >= abs (wx))
1081 dir = (SGNZ (wx) == SGNZ (wy)) ? 45 : -45;
1082 else
1083 #endif
1084 dir = (SGNZ (wx) == SGNZ (wy)) ? 90 : -90;
1085 }
1086 else
1087 {
1088 Crosshair.AttachedBox.Point2.Y =
1089 Crosshair.AttachedBox.Point1.Y + abs (wx) * SGNZ (wy);
1090 sa = (wy >= 0) ? -90 : 90;
1091 #ifdef ARC45
1092 if (abs (wx) / 2 >= abs (wy))
1093 dir = (SGNZ (wx) == SGNZ (wy)) ? -45 : 45;
1094 else
1095 #endif
1096 dir = (SGNZ (wx) == SGNZ (wy)) ? -90 : 90;
1097 wy = wx;
1098 }
1099 if (abs (wy) > 0 && (arc = CreateNewArcOnLayer (CURRENT,
1100 Crosshair.
1101 AttachedBox.
1102 Point2.X,
1103 Crosshair.
1104 AttachedBox.
1105 Point2.Y,
1106 abs (wy),
1107 abs (wy),
1108 sa,
1109 dir,
1110 Settings.
1111 LineThickness,
1112 2 * Settings.
1113 Keepaway,
1114 MakeFlags
1115 (TEST_FLAG
1116 (CLEARNEWFLAG,
1117 PCB) ?
1118 CLEARLINEFLAG :
1119 0))))
1120 {
1121 BoxType *bx;
1122
1123 bx = GetArcEnds (arc);
1124 Crosshair.AttachedBox.Point1.X =
1125 Crosshair.AttachedBox.Point2.X = bx->X2;
1126 Crosshair.AttachedBox.Point1.Y =
1127 Crosshair.AttachedBox.Point2.Y = bx->Y2;
1128 AddObjectToCreateUndoList (ARC_TYPE, CURRENT, arc, arc);
1129 IncrementUndoSerialNumber ();
1130 addedLines++;
1131 DrawArc (CURRENT, arc);
1132 Draw ();
1133 Crosshair.AttachedBox.State = STATE_THIRD;
1134 }
1135 break;
1136 }
1137 }
1138 break;
1139 }
1140 case LOCK_MODE:
1141 {
1142 type = SearchScreen (Note.X, Note.Y, LOCK_TYPES, &ptr1, &ptr2, &ptr3);
1143 if (type == ELEMENT_TYPE)
1144 {
1145 ElementType *element = (ElementType *) ptr2;
1146
1147 TOGGLE_FLAG (LOCKFLAG, element);
1148 PIN_LOOP (element);
1149 {
1150 TOGGLE_FLAG (LOCKFLAG, pin);
1151 CLEAR_FLAG (SELECTEDFLAG, pin);
1152 }
1153 END_LOOP;
1154 PAD_LOOP (element);
1155 {
1156 TOGGLE_FLAG (LOCKFLAG, pad);
1157 CLEAR_FLAG (SELECTEDFLAG, pad);
1158 }
1159 END_LOOP;
1160 CLEAR_FLAG (SELECTEDFLAG, element);
1161 /* always re-draw it since I'm too lazy
1162 * to tell if a selected flag changed
1163 */
1164 DrawElement (element);
1165 Draw ();
1166 SetChangedFlag (true);
1167 hid_actionl ("Report", "Object", NULL);
1168 }
1169 else if (type != NO_TYPE)
1170 {
1171 TextType *thing = (TextType *) ptr3;
1172 TOGGLE_FLAG (LOCKFLAG, thing);
1173 if (TEST_FLAG (LOCKFLAG, thing)
1174 && TEST_FLAG (SELECTEDFLAG, thing))
1175 {
1176 /* this is not un-doable since LOCK isn't */
1177 CLEAR_FLAG (SELECTEDFLAG, thing);
1178 DrawObject (type, ptr1, ptr2);
1179 Draw ();
1180 }
1181 SetChangedFlag (true);
1182 hid_actionl ("Report", "Object", NULL);
1183 }
1184 break;
1185 }
1186 case THERMAL_MODE:
1187 {
1188 if (((type
1189 =
1190 SearchScreen (Note.X, Note.Y, PIN_TYPES, &ptr1, &ptr2,
1191 &ptr3)) != NO_TYPE)
1192 && !TEST_FLAG (HOLEFLAG, (PinType *) ptr3))
1193 {
1194 if (gui->shift_is_pressed ())
1195 {
1196 int tstyle = GET_THERM (INDEXOFCURRENT, (PinType *) ptr3);
1197 tstyle++;
1198 if (tstyle > 5)
1199 tstyle = 1;
1200 ChangeObjectThermal (type, ptr1, ptr2, ptr3, tstyle);
1201 }
1202 else if (GET_THERM (INDEXOFCURRENT, (PinType *) ptr3))
1203 ChangeObjectThermal (type, ptr1, ptr2, ptr3, 0);
1204 else
1205 ChangeObjectThermal (type, ptr1, ptr2, ptr3, PCB->ThermStyle);
1206 }
1207 break;
1208 }
1209
1210 case LINE_MODE:
1211 /* do update of position */
1212 NotifyLine ();
1213 if (Crosshair.AttachedLine.State != STATE_THIRD)
1214 break;
1215
1216 /* Remove anchor if clicking on start point;
1217 * this means we can't paint 0 length lines
1218 * which could be used for square SMD pads.
1219 * Instead use a very small delta, or change
1220 * the file after saving.
1221 */
1222 if (Crosshair.X == Crosshair.AttachedLine.Point1.X
1223 && Crosshair.Y == Crosshair.AttachedLine.Point1.Y)
1224 {
1225 SetMode (LINE_MODE);
1226 break;
1227 }
1228
1229 if (PCB->RatDraw)
1230 {
1231 RatType *line;
1232 if ((line = AddNet ()))
1233 {
1234 addedLines++;
1235 AddObjectToCreateUndoList (RATLINE_TYPE, line, line, line);
1236 IncrementUndoSerialNumber ();
1237 DrawRat (line);
1238 Crosshair.AttachedLine.Point1.X =
1239 Crosshair.AttachedLine.Point2.X;
1240 Crosshair.AttachedLine.Point1.Y =
1241 Crosshair.AttachedLine.Point2.Y;
1242 Draw ();
1243 }
1244 break;
1245 }
1246 else
1247 /* create line if both ends are determined && length != 0 */
1248 {
1249 LineType *line;
1250 int line_flags = 0;
1251
1252 if (TEST_FLAG (AUTODRCFLAG, PCB) && !TEST_SILK_LAYER (CURRENT))
1253 line_flags |= CONNECTEDFLAG | FOUNDFLAG;
1254
1255 if (TEST_FLAG (CLEARNEWFLAG, PCB))
1256 line_flags |= CLEARLINEFLAG;
1257
1258 if (PCB->Clipping
1259 && Crosshair.AttachedLine.Point1.X ==
1260 Crosshair.AttachedLine.Point2.X
1261 && Crosshair.AttachedLine.Point1.Y ==
1262 Crosshair.AttachedLine.Point2.Y
1263 && (Crosshair.AttachedLine.Point2.X != Note.X
1264 || Crosshair.AttachedLine.Point2.Y != Note.Y))
1265 {
1266 /* We will only need to paint the second line segment.
1267 Since we only check for vias on the first segment,
1268 swap them so the non-empty segment is the first segment. */
1269 Crosshair.AttachedLine.Point2.X = Note.X;
1270 Crosshair.AttachedLine.Point2.Y = Note.Y;
1271 }
1272
1273 if ((Crosshair.AttachedLine.Point1.X !=
1274 Crosshair.AttachedLine.Point2.X
1275 || Crosshair.AttachedLine.Point1.Y !=
1276 Crosshair.AttachedLine.Point2.Y))
1277 {
1278 PinType *via;
1279 Cardinal layer_from, layer_to;
1280
1281 if ((line =
1282 CreateDrawnLineOnLayer (CURRENT,
1283 Crosshair.AttachedLine.Point1.X,
1284 Crosshair.AttachedLine.Point1.Y,
1285 Crosshair.AttachedLine.Point2.X,
1286 Crosshair.AttachedLine.Point2.Y,
1287 Settings.LineThickness,
1288 2 * Settings.Keepaway,
1289 MakeFlags (line_flags))) != NULL)
1290 {
1291 addedLines++;
1292 AddObjectToCreateUndoList (LINE_TYPE, CURRENT, line, line);
1293 DrawLine (CURRENT, line);
1294 }
1295 /* place a via if vias are visible, the layer is
1296 in a new group since the last line and there
1297 isn't a pin already here */
1298 if (TEST_FLAG (AUTOBURIEDVIASFLAG, PCB))
1299 {
1300 layer_from = GetLayerNumber (PCB->Data, lastLayer);
1301 layer_to = GetLayerNumber (PCB->Data, CURRENT);
1302 }
1303 else
1304 {
1305 layer_from = 0;
1306 layer_to = 0;
1307 }
1308
1309 if (PCB->ViaOn && GetLayerGroupNumberByPointer (CURRENT) !=
1310 GetLayerGroupNumberByPointer (lastLayer) &&
1311 SearchObjectByLocation (PIN_TYPES, &ptr1, &ptr2, &ptr3,
1312 Crosshair.AttachedLine.Point1.X,
1313 Crosshair.AttachedLine.Point1.Y,
1314 Settings.ViaThickness / 2) ==
1315 NO_TYPE
1316 && (via =
1317 CreateNewViaEx (PCB->Data,
1318 Crosshair.AttachedLine.Point1.X,
1319 Crosshair.AttachedLine.Point1.Y,
1320 Settings.ViaThickness,
1321 2 * Settings.Keepaway, Settings.ViaMaskAperture,
1322 Settings.ViaDrillingHole, NULL,
1323 NoFlags (), layer_from, layer_to)) != NULL)
1324 {
1325 AddObjectToCreateUndoList (VIA_TYPE, via, via, via);
1326 DrawVia (via);
1327 }
1328 /* copy the coordinates */
1329 Crosshair.AttachedLine.Point1.X =
1330 Crosshair.AttachedLine.Point2.X;
1331 Crosshair.AttachedLine.Point1.Y =
1332 Crosshair.AttachedLine.Point2.Y;
1333 IncrementUndoSerialNumber ();
1334 lastLayer = CURRENT;
1335 }
1336 if (PCB->Clipping && (Note.X != Crosshair.AttachedLine.Point2.X
1337 || Note.Y !=
1338 Crosshair.AttachedLine.Point2.Y))
1339 {
1340 if ((line =
1341 CreateDrawnLineOnLayer (CURRENT,
1342 Crosshair.AttachedLine.Point2.X,
1343 Crosshair.AttachedLine.Point2.Y,
1344 Note.X, Note.Y,
1345 Settings.LineThickness,
1346 2 * Settings.Keepaway,
1347 MakeFlags (line_flags))) != NULL)
1348 {
1349 addedLines++;
1350 AddObjectToCreateUndoList (LINE_TYPE, CURRENT, line, line);
1351 IncrementUndoSerialNumber ();
1352 DrawLine (CURRENT, line);
1353 }
1354 /* move to new start point */
1355 Crosshair.AttachedLine.Point1.X = Note.X;
1356 Crosshair.AttachedLine.Point1.Y = Note.Y;
1357 Crosshair.AttachedLine.Point2.X = Note.X;
1358 Crosshair.AttachedLine.Point2.Y = Note.Y;
1359 if (TEST_FLAG (SWAPSTARTDIRFLAG, PCB))
1360 {
1361 PCB->Clipping ^= 3;
1362 }
1363 }
1364 if (TEST_FLAG (AUTODRCFLAG, PCB) && !TEST_SILK_LAYER (CURRENT))
1365 LookupConnection (Note.X, Note.Y, true, 1, CONNECTEDFLAG, false);
1366 Draw ();
1367 }
1368 break;
1369
1370 case RECTANGLE_MODE:
1371 /* do update of position */
1372 NotifyBlock ();
1373
1374 /* create rectangle if both corners are determined
1375 * and width, height are != 0
1376 */
1377 if (Crosshair.AttachedBox.State == STATE_THIRD &&
1378 Crosshair.AttachedBox.Point1.X != Crosshair.AttachedBox.Point2.X &&
1379 Crosshair.AttachedBox.Point1.Y != Crosshair.AttachedBox.Point2.Y)
1380 {
1381 PolygonType *polygon;
1382
1383 int flags = CLEARPOLYFLAG;
1384 if (TEST_FLAG (NEWFULLPOLYFLAG, PCB))
1385 flags |= FULLPOLYFLAG;
1386 if ((polygon = CreateNewPolygonFromRectangle (CURRENT,
1387 Crosshair.
1388 AttachedBox.Point1.X,
1389 Crosshair.
1390 AttachedBox.Point1.Y,
1391 Crosshair.
1392 AttachedBox.Point2.X,
1393 Crosshair.
1394 AttachedBox.Point2.Y,
1395 MakeFlags
1396 (flags))) !=
1397 NULL)
1398 {
1399 AddObjectToCreateUndoList (POLYGON_TYPE, CURRENT,
1400 polygon, polygon);
1401 IncrementUndoSerialNumber ();
1402 DrawPolygon (CURRENT, polygon);
1403 Draw ();
1404 }
1405
1406 /* reset state to 'first corner' */
1407 Crosshair.AttachedBox.State = STATE_FIRST;
1408 }
1409 break;
1410
1411 case TEXT_MODE:
1412 {
1413 char *string;
1414
1415 if ((string = gui->prompt_for (_("Enter text:"), "")) != NULL)
1416 {
1417 if (strlen(string) > 0)
1418 {
1419 TextType *text;
1420 int flag = CLEARLINEFLAG;
1421
1422 if (GetLayerGroupNumberByNumber (INDEXOFCURRENT) ==
1423 GetLayerGroupNumberBySide (BOTTOM_SIDE))
1424 flag |= ONSOLDERFLAG;
1425 if ((text = CreateNewText (CURRENT, &PCB->Font, Note.X,
1426 Note.Y, 0, Settings.TextScale,
1427 string, MakeFlags (flag))) != NULL)
1428 {
1429 AddObjectToCreateUndoList (TEXT_TYPE, CURRENT, text, text);
1430 IncrementUndoSerialNumber ();
1431 DrawText (CURRENT, text);
1432 Draw ();
1433 }
1434 }
1435 free (string);
1436 }
1437 break;
1438 }
1439
1440 case POLYGON_MODE:
1441 {
1442 PointType *points = Crosshair.AttachedPolygon.Points;
1443 Cardinal n = Crosshair.AttachedPolygon.PointN;
1444
1445 /* do update of position; use the 'LINE_MODE' mechanism */
1446 NotifyLine ();
1447
1448 /* check if this is the last point of a polygon */
1449 if (n >= 3 &&
1450 points->X == Crosshair.AttachedLine.Point2.X &&
1451 points->Y == Crosshair.AttachedLine.Point2.Y)
1452 {
1453 CopyAttachedPolygonToLayer ();
1454 Draw ();
1455 break;
1456 }
1457
1458 /* create new point if it's the first one or if it's
1459 * different to the last one
1460 */
1461 if (!n ||
1462 points[n - 1].X != Crosshair.AttachedLine.Point2.X ||
1463 points[n - 1].Y != Crosshair.AttachedLine.Point2.Y)
1464 {
1465 CreateNewPointInPolygon (&Crosshair.AttachedPolygon,
1466 Crosshair.AttachedLine.Point2.X,
1467 Crosshair.AttachedLine.Point2.Y);
1468
1469 /* copy the coordinates */
1470 Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X;
1471 Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y;
1472 }
1473 break;
1474 }
1475
1476 case POLYGONHOLE_MODE:
1477 {
1478 switch (Crosshair.AttachedObject.State)
1479 {
1480 /* first notify, lookup object */
1481 case STATE_FIRST:
1482 Crosshair.AttachedObject.Type =
1483 SearchScreen (Note.X, Note.Y, POLYGON_TYPE,
1484 &Crosshair.AttachedObject.Ptr1,
1485 &Crosshair.AttachedObject.Ptr2,
1486 &Crosshair.AttachedObject.Ptr3);
1487
1488 if (Crosshair.AttachedObject.Type == NO_TYPE)
1489 {
1490 Message (_("The first point of a polygon hole must be on a polygon.\n"));
1491 break; /* don't start doing anything if clicked outside of polys */
1492 }
1493
1494 if (TEST_FLAG(LOCKFLAG, (PolygonType *) Crosshair.AttachedObject.Ptr2))
1495 {
1496 Message (_("Sorry, the object is locked\n"));
1497 Crosshair.AttachedObject.Type = NO_TYPE;
1498 break;
1499 }
1500 else
1501 Crosshair.AttachedObject.State = STATE_SECOND;
1502 /* Fall thru: first click is also the first point of the
1503 * poly hole. */
1504
1505 /* second notify, insert new point into object */
1506 case STATE_SECOND:
1507 {
1508 PointType *points = Crosshair.AttachedPolygon.Points;
1509 Cardinal n = Crosshair.AttachedPolygon.PointN;
1510 POLYAREA *original, *new_hole, *result;
1511 FlagType Flags;
1512
1513 /* do update of position; use the 'LINE_MODE' mechanism */
1514 NotifyLine ();
1515
1516 /* check if this is the last point of a polygon */
1517 if (n >= 3 &&
1518 points->X == Crosshair.AttachedLine.Point2.X &&
1519 points->Y == Crosshair.AttachedLine.Point2.Y)
1520 {
1521 /* Create POLYAREAs from the original polygon
1522 * and the new hole polygon */
1523 original = PolygonToPoly ((PolygonType *)Crosshair.AttachedObject.Ptr2);
1524 new_hole = PolygonToPoly (&Crosshair.AttachedPolygon);
1525
1526 /* Subtract the hole from the original polygon shape */
1527 poly_Boolean_free (original, new_hole, &result, PBO_SUB);
1528
1529 /* Convert the resulting polygon(s) into a new set of nodes
1530 * and place them on the page. Delete the original polygon.
1531 */
1532 SaveUndoSerialNumber ();
1533 Flags = ((PolygonType *)Crosshair.AttachedObject.Ptr2)->Flags;
1534 PolyToPolygonsOnLayer (PCB->Data, (LayerType *)Crosshair.AttachedObject.Ptr1,
1535 result, Flags);
1536 RemoveObject (POLYGON_TYPE,
1537 Crosshair.AttachedObject.Ptr1,
1538 Crosshair.AttachedObject.Ptr2,
1539 Crosshair.AttachedObject.Ptr3);
1540 RestoreUndoSerialNumber ();
1541 IncrementUndoSerialNumber ();
1542 Draw ();
1543
1544 /* reset state of attached line */
1545 memset (&Crosshair.AttachedPolygon, 0, sizeof (PolygonType));
1546 Crosshair.AttachedObject.State = STATE_FIRST;
1547 addedLines = 0;
1548
1549 break;
1550 }
1551
1552 /* create new point if it's the first one or if it's
1553 * different to the last one
1554 */
1555 if (!n ||
1556 points[n - 1].X != Crosshair.AttachedLine.Point2.X ||
1557 points[n - 1].Y != Crosshair.AttachedLine.Point2.Y)
1558 {
1559 CreateNewPointInPolygon (&Crosshair.AttachedPolygon,
1560 Crosshair.AttachedLine.Point2.X,
1561 Crosshair.AttachedLine.Point2.Y);
1562
1563 /* copy the coordinates */
1564 Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X;
1565 Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y;
1566 }
1567 break;
1568 }
1569 }
1570
1571 break;
1572 }
1573
1574 case PASTEBUFFER_MODE:
1575 {
1576 TextType estr[MAX_ELEMENTNAMES];
1577 ElementType *e = 0;
1578
1579 if (gui->shift_is_pressed ())
1580 {
1581 int type =
1582 SearchScreen (Note.X, Note.Y, ELEMENT_TYPE, &ptr1, &ptr2,
1583 &ptr3);
1584 if (type == ELEMENT_TYPE)
1585 {
1586 e = (ElementType *) ptr1;
1587 if (e)
1588 {
1589 int i;
1590
1591 memcpy (estr, e->Name,
1592 MAX_ELEMENTNAMES * sizeof (TextType));
1593 for (i = 0; i < MAX_ELEMENTNAMES; ++i)
1594 estr[i].TextString = estr[i].TextString ? strdup(estr[i].TextString) : NULL;
1595 RemoveElement (e);
1596 }
1597 }
1598 }
1599 if (CopyPastebufferToLayout (Note.X, Note.Y))
1600 SetChangedFlag (true);
1601 if (e)
1602 {
1603 int type =
1604 SearchScreen (Note.X, Note.Y, ELEMENT_TYPE, &ptr1, &ptr2,
1605 &ptr3);
1606 if (type == ELEMENT_TYPE && ptr1)
1607 {
1608 int i, save_n;
1609 e = (ElementType *) ptr1;
1610
1611 save_n = NAME_INDEX (PCB);
1612
1613 for (i = 0; i < MAX_ELEMENTNAMES; i++)
1614 {
1615 if (i == save_n)
1616 EraseElementName (e);
1617 r_delete_entry (PCB->Data->name_tree[i],
1618 (BoxType *) & (e->Name[i]));
1619 memcpy (&(e->Name[i]), &(estr[i]), sizeof (TextType));
1620 e->Name[i].Element = e;
1621 SetTextBoundingBox (&PCB->Font, &(e->Name[i]));
1622 r_insert_entry (PCB->Data->name_tree[i],
1623 (BoxType *) & (e->Name[i]), 0);
1624 if (i == save_n)
1625 DrawElementName (e);
1626 }
1627 }
1628 }
1629 break;
1630 }
1631
1632 case REMOVE_MODE:
1633 if ((type =
1634 SearchScreen (Note.X, Note.Y, REMOVE_TYPES, &ptr1, &ptr2,
1635 &ptr3)) != NO_TYPE)
1636 {
1637 if (TEST_FLAG (LOCKFLAG, (LineType *) ptr2))
1638 {
1639 Message (_("Sorry, the object is locked\n"));
1640 break;
1641 }
1642 if (type == ELEMENT_TYPE)
1643 {
1644 RubberbandType *ptr;
1645 int i;
1646
1647 Crosshair.AttachedObject.RubberbandN = 0;
1648 LookupRatLines (type, ptr1, ptr2, ptr3);
1649 ptr = Crosshair.AttachedObject.Rubberband;
1650 for (i = 0; i < Crosshair.AttachedObject.RubberbandN; i++)
1651 {
1652 if (PCB->RatOn)
1653 EraseRat ((RatType *) ptr->Line);
1654 if (TEST_FLAG (RUBBERENDFLAG, ptr->Line))
1655 MoveObjectToRemoveUndoList (RATLINE_TYPE,
1656 ptr->Line, ptr->Line,
1657 ptr->Line);
1658 else
1659 TOGGLE_FLAG (RUBBERENDFLAG, ptr->Line); /* only remove line once */
1660 ptr++;
1661 }
1662 }
1663 RemoveObject (type, ptr1, ptr2, ptr3);
1664 IncrementUndoSerialNumber ();
1665 SetChangedFlag (true);
1666 }
1667 break;
1668
1669 case ROTATE_MODE:
1670 RotateScreenObject (Note.X, Note.Y,
1671 gui->shift_is_pressed ()? (SWAP_IDENT ?
1672 1 : 3)
1673 : (SWAP_IDENT ? 3 : 1));
1674 break;
1675
1676 /* both are almost the same */
1677 case COPY_MODE:
1678 case MOVE_MODE:
1679 switch (Crosshair.AttachedObject.State)
1680 {
1681 /* first notify, lookup object */
1682 case STATE_FIRST:
1683 {
1684 int types = (Settings.Mode == COPY_MODE) ?
1685 COPY_TYPES : MOVE_TYPES;
1686
1687 Crosshair.AttachedObject.Type =
1688 SearchScreen (Note.X, Note.Y, types,
1689 &Crosshair.AttachedObject.Ptr1,
1690 &Crosshair.AttachedObject.Ptr2,
1691 &Crosshair.AttachedObject.Ptr3);
1692 if (Crosshair.AttachedObject.Type != NO_TYPE)
1693 {
1694 if (Settings.Mode == MOVE_MODE &&
1695 TEST_FLAG (LOCKFLAG, (PinType *)
1696 Crosshair.AttachedObject.Ptr2))
1697 {
1698 Message (_("Sorry, the object is locked\n"));
1699 Crosshair.AttachedObject.Type = NO_TYPE;
1700 }
1701 else
1702 AttachForCopy (Note.X, Note.Y);
1703 }
1704 break;
1705 }
1706
1707 /* second notify, move or copy object */
1708 case STATE_SECOND:
1709 if (Settings.Mode == COPY_MODE)
1710 CopyObject (Crosshair.AttachedObject.Type,
1711 Crosshair.AttachedObject.Ptr1,
1712 Crosshair.AttachedObject.Ptr2,
1713 Crosshair.AttachedObject.Ptr3,
1714 Note.X - Crosshair.AttachedObject.X,
1715 Note.Y - Crosshair.AttachedObject.Y);
1716 else
1717 {
1718 MoveObjectAndRubberband (Crosshair.AttachedObject.Type,
1719 Crosshair.AttachedObject.Ptr1,
1720 Crosshair.AttachedObject.Ptr2,
1721 Crosshair.AttachedObject.Ptr3,
1722 Note.X - Crosshair.AttachedObject.X,
1723 Note.Y - Crosshair.AttachedObject.Y);
1724 SetLocalRef (0, 0, false);
1725 }
1726 SetChangedFlag (true);
1727
1728 /* reset identifiers */
1729 Crosshair.AttachedObject.Type = NO_TYPE;
1730 Crosshair.AttachedObject.State = STATE_FIRST;
1731 break;
1732 }
1733 break;
1734
1735 /* insert a point into a polygon/line/... */
1736 case INSERTPOINT_MODE:
1737 switch (Crosshair.AttachedObject.State)
1738 {
1739 /* first notify, lookup object */
1740 case STATE_FIRST:
1741 Crosshair.AttachedObject.Type =
1742 SearchScreen (Note.X, Note.Y, INSERT_TYPES,
1743 &Crosshair.AttachedObject.Ptr1,
1744 &Crosshair.AttachedObject.Ptr2,
1745 &Crosshair.AttachedObject.Ptr3);
1746
1747 if (Crosshair.AttachedObject.Type != NO_TYPE)
1748 {
1749 if (TEST_FLAG (LOCKFLAG, (PolygonType *)
1750 Crosshair.AttachedObject.Ptr2))
1751 {
1752 Message (_("Sorry, the object is locked\n"));
1753 Crosshair.AttachedObject.Type = NO_TYPE;
1754 break;
1755 }
1756 else
1757 {
1758 /* get starting point of nearest segment */
1759 if (Crosshair.AttachedObject.Type == POLYGON_TYPE)
1760 {
1761 fake.poly =
1762 (PolygonType *) Crosshair.AttachedObject.Ptr2;
1763 polyIndex =
1764 GetLowestDistancePolygonPoint (fake.poly, Note.X,
1765 Note.Y);
1766 fake.line.Point1 = fake.poly->Points[polyIndex];
1767 fake.line.Point2 = fake.poly->Points[
1768 prev_contour_point (fake.poly, polyIndex)];
1769 Crosshair.AttachedObject.Ptr2 = &fake.line;
1770
1771 }
1772 Crosshair.AttachedObject.State = STATE_SECOND;
1773 InsertedPoint = *AdjustInsertPoint ();
1774 }
1775 }
1776 break;
1777
1778 /* second notify, insert new point into object */
1779 case STATE_SECOND:
1780 if (Crosshair.AttachedObject.Type == POLYGON_TYPE)
1781 InsertPointIntoObject (POLYGON_TYPE,
1782 Crosshair.AttachedObject.Ptr1, fake.poly,
1783 &polyIndex,
1784 InsertedPoint.X, InsertedPoint.Y, false, false);
1785 else
1786 InsertPointIntoObject (Crosshair.AttachedObject.Type,
1787 Crosshair.AttachedObject.Ptr1,
1788 Crosshair.AttachedObject.Ptr2,
1789 &polyIndex,
1790 InsertedPoint.X, InsertedPoint.Y, false, false);
1791 SetChangedFlag (true);
1792
1793 /* reset identifiers */
1794 Crosshair.AttachedObject.Type = NO_TYPE;
1795 Crosshair.AttachedObject.State = STATE_FIRST;
1796 break;
1797 }
1798 break;
1799 }
1800 }
1801
1802
1803 /* --------------------------------------------------------------------------- */
1804
1805 static const char atomic_syntax[] = N_("Atomic(Save|Restore|Close|Block)");
1806
1807 static const char atomic_help[] = N_("Save or restore the undo serial number.");
1808
1809 /* %start-doc actions Atomic
1810
1811 This action allows making multiple-action bindings into an atomic
1812 operation that will be undone by a single Undo command. For example,
1813 to optimize rat lines, you'd delete the rats and re-add them. To
1814 group these into a single undo, you'd want the deletions and the
1815 additions to have the same undo serial number. So, you @code{Save},
1816 delete the rats, @code{Restore}, add the rats - using the same serial
1817 number as the deletes, then @code{Block}, which checks to see if the
1818 deletions or additions actually did anything. If not, the serial
1819 number is set to the saved number, as there's nothing to undo. If
1820 something did happen, the serial number is incremented so that these
1821 actions are counted as a single undo step.
1822
1823 @table @code
1824
1825 @item Save
1826 Saves the undo serial number.
1827
1828 @item Restore
1829 Returns it to the last saved number.
1830
1831 @item Close
1832 Sets it to 1 greater than the last save.
1833
1834 @item Block
1835 Does a Restore if there was nothing to undo, else does a Close.
1836
1837 @end table
1838
1839 %end-doc */
1840
1841 static int
ActionAtomic(int argc,char ** argv,Coord x,Coord y)1842 ActionAtomic (int argc, char **argv, Coord x, Coord y)
1843 {
1844 if (argc != 1)
1845 AFAIL (atomic);
1846
1847 switch (GetFunctionID (argv[0]))
1848 {
1849 case F_Save:
1850 SaveUndoSerialNumber ();
1851 break;
1852 case F_Restore:
1853 RestoreUndoSerialNumber ();
1854 break;
1855 case F_Close:
1856 RestoreUndoSerialNumber ();
1857 IncrementUndoSerialNumber ();
1858 break;
1859 case F_Block:
1860 RestoreUndoSerialNumber ();
1861 if (Bumped)
1862 IncrementUndoSerialNumber ();
1863 break;
1864 }
1865 return 0;
1866 }
1867
1868
1869 /* -------------------------------------------------------------------------- */
1870
1871 static const char dumplibrary_syntax[] = N_("DumpLibrary()");
1872
1873 static const char dumplibrary_help[] =
1874 N_("Display the entire contents of the libraries.");
1875
1876 /* %start-doc actions DumpLibrary
1877
1878
1879 %end-doc */
1880
1881 static int
ActionDumpLibrary(int argc,char ** argv,Coord x,Coord y)1882 ActionDumpLibrary (int argc, char **argv, Coord x, Coord y)
1883 {
1884 int i, j;
1885
1886 printf ("**** Do not count on this format. It will change ****\n\n");
1887 printf ("MenuN = %d\n", (int) Library.MenuN);
1888 printf ("MenuMax = %d\n", (int) Library.MenuMax);
1889 for (i = 0; i < Library.MenuN; i++)
1890 {
1891 printf ("Library #%d:\n", i);
1892 printf (" EntryN = %d\n", (int) Library.Menu[i].EntryN);
1893 printf (" EntryMax = %d\n", (int) Library.Menu[i].EntryMax);
1894 printf (" Name = \"%s\"\n", UNKNOWN (Library.Menu[i].Name));
1895 printf (" directory = \"%s\"\n",
1896 UNKNOWN (Library.Menu[i].directory));
1897 printf (" Style = \"%s\"\n", UNKNOWN (Library.Menu[i].Style));
1898 printf (" flag = %d\n", Library.Menu[i].flag);
1899
1900 for (j = 0; j < Library.Menu[i].EntryN; j++)
1901 {
1902 printf (" #%4d: ", j);
1903 if (Library.Menu[i].Entry[j].Template == (char *) -1)
1904 {
1905 printf ("newlib: \"%s\"\n",
1906 UNKNOWN (Library.Menu[i].Entry[j].ListEntry));
1907 }
1908 else
1909 {
1910 printf ("\"%s\", \"%s\", \"%s\", \"%s\", \"%s\"\n",
1911 UNKNOWN (Library.Menu[i].Entry[j].ListEntry),
1912 UNKNOWN (Library.Menu[i].Entry[j].Template),
1913 UNKNOWN (Library.Menu[i].Entry[j].Package),
1914 UNKNOWN (Library.Menu[i].Entry[j].Value),
1915 UNKNOWN (Library.Menu[i].Entry[j].Description));
1916 }
1917 }
1918 }
1919
1920 return 0;
1921 }
1922
1923 /* -------------------------------------------------------------------------- */
1924
1925 static const char flip_syntax[] = N_("Flip(Object|Selected|SelectedElements)");
1926
1927 static const char flip_help[] =
1928 N_("Flip an element to the opposite side of the board.");
1929
1930 /* %start-doc actions Flip
1931
1932 Note that the location of the element will be symmetric about the
1933 cursor location; i.e. if the part you are pointing at will still be at
1934 the same spot once the element is on the other side. When flipping
1935 multiple elements, this retains their positions relative to each
1936 other, not their absolute positions on the board.
1937
1938 %end-doc */
1939
1940 static int
ActionFlip(int argc,char ** argv,Coord x,Coord y)1941 ActionFlip (int argc, char **argv, Coord x, Coord y)
1942 {
1943 char *function = ARG (0);
1944 ElementType *element;
1945 void *ptrtmp;
1946 int err = 0;
1947
1948 if (function)
1949 {
1950 switch (GetFunctionID (function))
1951 {
1952 case F_Object:
1953 if ((SearchScreen (x, y, ELEMENT_TYPE,
1954 &ptrtmp, &ptrtmp, &ptrtmp)) != NO_TYPE)
1955 {
1956 element = (ElementType *) ptrtmp;
1957 ChangeElementSide (element, 2 * Crosshair.Y - PCB->MaxHeight);
1958 IncrementUndoSerialNumber ();
1959 Draw ();
1960 }
1961 break;
1962 case F_Selected:
1963 case F_SelectedElements:
1964 ChangeSelectedElementSide ();
1965 break;
1966 default:
1967 err = 1;
1968 break;
1969 }
1970 if (!err)
1971 return 0;
1972 }
1973
1974 AFAIL (flip);
1975 }
1976
1977 /* -------------------------------------------------------------------------- */
1978
1979 static const char message_syntax[] = N_("Message(message)");
1980
1981 static const char message_help[] = N_("Writes a message to the log window.");
1982
1983 /* %start-doc actions Message
1984
1985 This action displays a message to the log window. This action is primarily
1986 provided for use by other programs which may interface with PCB. If
1987 multiple arguments are given, each one is sent to the log window
1988 followed by a newline.
1989
1990 %end-doc */
1991
1992 static int
ActionMessage(int argc,char ** argv,Coord x,Coord y)1993 ActionMessage (int argc, char **argv, Coord x, Coord y)
1994 {
1995 int i;
1996
1997 if (argc < 1)
1998 AFAIL (message);
1999
2000 for (i = 0; i < argc; i++)
2001 {
2002 Message (argv[i]);
2003 Message ("\n");
2004 }
2005
2006 return 0;
2007 }
2008
2009
2010 /* -------------------------------------------------------------------------- */
2011
2012 static const char setthermal_syntax[] =
2013 "SetThermal(Object|SelectedPins|SelectedVias|Selected, Style)";
2014
2015 static const char setthermal_help[] =
2016 N_("Set the thermal (on the current layer) of pins or vias to the given style.\n"
2017 "Style = 0 means no thermal.\n"
2018 "Style = 1 has diagonal fingers with sharp edges.\n"
2019 "Style = 2 has horizontal and vertical fingers with sharp edges.\n"
2020 "Style = 3 is a solid connection to the plane.\n"
2021 "Style = 4 has diagonal fingers with rounded edges.\n"
2022 "Style = 5 has horizontal and vertical fingers with rounded edges.\n");
2023
2024 /* %start-doc actions SetThermal
2025
2026 This changes how/whether pins or vias connect to any rectangle or polygon
2027 on the current layer. The first argument can specify one object, or all
2028 selected pins, or all selected vias, or all selected pins and vias.
2029 The second argument specifies the style of connection.
2030 There are 5 possibilities:
2031 0 - no connection,
2032 1 - 45 degree fingers with sharp edges,
2033 2 - horizontal & vertical fingers with sharp edges,
2034 3 - solid connection,
2035 4 - 45 degree fingers with rounded corners,
2036 5 - horizontal & vertical fingers with rounded corners.
2037
2038 Pins and Vias may have thermals whether or not there is a polygon available
2039 to connect with. However, they will have no effect without the polygon.
2040 %end-doc */
2041
2042 static int
ActionSetThermal(int argc,char ** argv,Coord x,Coord y)2043 ActionSetThermal (int argc, char **argv, Coord x, Coord y)
2044 {
2045 char *function = ARG (0);
2046 char *style = ARG (1);
2047 void *ptr1, *ptr2, *ptr3;
2048 int type, kind;
2049 int err = 0;
2050
2051 if (function && *function)
2052 {
2053 bool absolute;
2054
2055 if ( ! style || ! *style)
2056 {
2057 kind = PCB->ThermStyle;
2058 absolute = true;
2059 }
2060 else
2061 kind = GetUnitlessValue (style, &absolute);
2062
2063 /* To allow relative values we could search for the first selected
2064 item and make 'kind' relative to that, but that's not too useful
2065 and requires quite some code. For example there's no
2066 GetFirstSelectedPin() function available. Let's postpone this
2067 functionality, there are more urgent things to do. */
2068
2069 if (absolute)
2070 switch (GetFunctionID (function))
2071 {
2072 case F_Object:
2073 if ((type =
2074 SearchScreen (Crosshair.X, Crosshair.Y, CHANGETHERMAL_TYPES,
2075 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
2076 {
2077 ChangeObjectThermal (type, ptr1, ptr2, ptr3, kind);
2078 IncrementUndoSerialNumber ();
2079 Draw ();
2080 }
2081 break;
2082 case F_SelectedPins:
2083 ChangeSelectedThermals (PIN_TYPE, kind);
2084 break;
2085 case F_SelectedVias:
2086 ChangeSelectedThermals (VIA_TYPE, kind);
2087 break;
2088 case F_Selected:
2089 case F_SelectedElements:
2090 ChangeSelectedThermals (CHANGETHERMAL_TYPES, kind);
2091 break;
2092 default:
2093 err = 1;
2094 break;
2095 }
2096 else
2097 err = 1;
2098 }
2099 else
2100 err = 1;
2101
2102 if (err)
2103 AFAIL (setthermal);
2104
2105 return 0;
2106 }
2107
2108 /*!
2109 * \brief Event handler to set the cursor according to the X pointer
2110 * position called from inside main.c.
2111 *
2112 * \warning !!! no action routine !!!
2113 */
2114 void
EventMoveCrosshair(int ev_x,int ev_y)2115 EventMoveCrosshair (int ev_x, int ev_y)
2116 {
2117 #ifdef HAVE_LIBSTROKE
2118 if (mid_stroke)
2119 {
2120 StrokeBox.X2 = ev_x;
2121 StrokeBox.Y2 = ev_y;
2122 stroke_record (ev_x, ev_y);
2123 return;
2124 }
2125 #endif /* HAVE_LIBSTROKE */
2126 if (MoveCrosshairAbsolute (ev_x, ev_y))
2127 {
2128 /* update object position and cursor location */
2129 AdjustAttachedObjects ();
2130 notify_crosshair_change (true);
2131 }
2132 }
2133
2134 /* --------------------------------------------------------------------------- */
2135
2136 static const char setvalue_syntax[] =
2137 N_("SetValue(Grid|Line|LineSize|Text|TextScale|ViaDrillingHole|Via|ViaSize, "
2138 "delta)");
2139
2140 static const char setvalue_help[] =
2141 N_("Change various board-wide values and sizes.");
2142
2143 /* %start-doc actions SetValue
2144
2145 @table @code
2146
2147 @item ViaDrillingHole
2148 Changes the diameter of the drill for new vias.
2149
2150 @item Grid
2151 Sets the grid spacing.
2152
2153 @item Line
2154 @item LineSize
2155 Changes the thickness of new lines.
2156
2157 @item Via
2158 @item ViaSize
2159 Changes the diameter of new vias.
2160
2161 @item Text
2162 @item TextScale
2163 Changes the size of new text.
2164
2165 @end table
2166
2167 %end-doc */
2168
2169 static int
ActionSetValue(int argc,char ** argv,Coord x,Coord y)2170 ActionSetValue (int argc, char **argv, Coord x, Coord y)
2171 {
2172 char *function = ARG (0);
2173 char *val = ARG (1);
2174 char *units = ARG (2);
2175 bool absolute; /* flag for 'absolute' value */
2176 double value;
2177 int text_scale;
2178 int err = 0;
2179
2180 if (function && val)
2181 {
2182 value = GetValue (val, units, &absolute);
2183 switch (GetFunctionID (function))
2184 {
2185 case F_ViaDrillingHole:
2186 SetViaDrillingHole (absolute ? value :
2187 value + Settings.ViaDrillingHole,
2188 false);
2189 hid_action ("RouteStylesChanged");
2190 break;
2191
2192 case F_Grid:
2193 if (absolute)
2194 SetGrid (value, false);
2195 else
2196 {
2197 if (value == 0)
2198 value = val[0] == '-' ? -Settings.increments->grid
2199 : Settings.increments->grid;
2200 /* On the way down, short against the minimum
2201 * PCB drawing unit */
2202 if ((value + PCB->Grid) < 1)
2203 SetGrid (1, false);
2204 else if (PCB->Grid == 1)
2205 SetGrid (value, false);
2206 else
2207 SetGrid (value + PCB->Grid, false);
2208 }
2209 break;
2210
2211 case F_LineSize:
2212 case F_Line:
2213 if (!absolute && value == 0)
2214 value = val[0] == '-' ? -Settings.increments->line
2215 : Settings.increments->line;
2216 SetLineSize (absolute ? value : value + Settings.LineThickness);
2217 hid_action ("RouteStylesChanged");
2218 break;
2219
2220 case F_Via:
2221 case F_ViaSize:
2222 SetViaSize (absolute ? value : value + Settings.ViaThickness, false);
2223 hid_action ("RouteStylesChanged");
2224 break;
2225
2226 case F_Text:
2227 case F_TextScale:
2228 text_scale = value / (double)FONT_CAPHEIGHT * 100.;
2229 if (!absolute)
2230 text_scale += Settings.TextScale;
2231 SetTextScale (text_scale);
2232 break;
2233 default:
2234 err = 1;
2235 break;
2236 }
2237 if (!err)
2238 return 0;
2239 }
2240
2241 AFAIL (setvalue);
2242 }
2243
2244
2245 /* --------------------------------------------------------------------------- */
2246
2247 static const char quit_syntax[] = N_("Quit()");
2248
2249 static const char quit_help[] = N_("Quits the application after confirming.");
2250
2251 /* %start-doc actions Quit
2252
2253 If you have unsaved changes, you will be prompted to confirm (or
2254 save) before quitting.
2255
2256 %end-doc */
2257
2258 static int
ActionQuit(int argc,char ** argv,Coord x,Coord y)2259 ActionQuit (int argc, char **argv, Coord x, Coord y)
2260 {
2261 char *force = ARG (0);
2262 if (force && strcasecmp (force, "force") == 0)
2263 {
2264 PCB->Changed = 0;
2265 exit (0);
2266 }
2267 if (!PCB->Changed || gui->close_confirm_dialog () == HID_CLOSE_CONFIRM_OK)
2268 QuitApplication ();
2269 return 1;
2270 }
2271
2272 /* --------------------------------------------------------------------------- */
2273
2274 static const char connection_syntax[] =
2275 N_("Connection(Find|ResetLinesAndPolygons|ResetPinsAndVias|Reset)");
2276
2277 static const char connection_help[] =
2278 N_("Searches connections of the object at the cursor position.");
2279
2280 /* %start-doc actions Connection
2281
2282 Connections found with this action will be highlighted in the
2283 ``connected-color'' color and will have the ``found'' flag set.
2284
2285 @table @code
2286
2287 @item Find
2288 The net under the cursor is ``found''.
2289
2290 @item ResetLinesAndPolygons
2291 Any ``found'' lines and polygons are marked ``not found''.
2292
2293 @item ResetPinsAndVias
2294 Any ``found'' pins and vias are marked ``not found''.
2295
2296 @item Reset
2297 All ``found'' objects are marked ``not found''.
2298
2299 @end table
2300
2301 %end-doc */
2302
2303 static int
ActionConnection(int argc,char ** argv,Coord x,Coord y)2304 ActionConnection (int argc, char **argv, Coord x, Coord y)
2305 {
2306 char *function = ARG (0);
2307 if (function)
2308 {
2309 switch (GetFunctionID (function))
2310 {
2311 case F_Find:
2312 {
2313 gui->get_coords (_("Click on a connection"), &x, &y);
2314 LookupConnection (x, y, true, 1, CONNECTEDFLAG, false);
2315 LookupConnection (x, y, true, 1, FOUNDFLAG, true);
2316 break;
2317 }
2318
2319 case F_ResetLinesAndPolygons:
2320 if (ClearFlagOnLinesAndPolygons (CONNECTEDFLAG | FOUNDFLAG, true))
2321 {
2322 IncrementUndoSerialNumber ();
2323 Draw ();
2324 }
2325 break;
2326
2327 case F_ResetPinsViasAndPads:
2328 if (ClearFlagOnPinsViasAndPads (CONNECTEDFLAG | FOUNDFLAG, true))
2329 {
2330 IncrementUndoSerialNumber ();
2331 Draw ();
2332 }
2333 break;
2334
2335 case F_Reset:
2336 if (ClearFlagOnAllObjects (CONNECTEDFLAG | FOUNDFLAG, true))
2337 {
2338 IncrementUndoSerialNumber ();
2339 Draw ();
2340 }
2341 break;
2342 }
2343 return 0;
2344 }
2345
2346 AFAIL (connection);
2347 }
2348
2349 /* --------------------------------------------------------------------------- */
2350
2351 static const char disperseelements_syntax[] =
2352 N_("DisperseElements(All|Selected)");
2353
2354 static const char disperseelements_help[] = N_("Disperses elements.");
2355
2356 /* %start-doc actions DisperseElements
2357
2358 Normally this is used when starting a board, by selecting all elements
2359 and then dispersing them. This scatters the elements around the board
2360 so that you can pick individual ones, rather than have all the
2361 elements at the same 0,0 coordinate and thus impossible to choose
2362 from.
2363
2364 %end-doc */
2365
2366 #define GAP MIL_TO_COORD(100)
2367
2368 static int
ActionDisperseElements(int argc,char ** argv,Coord x,Coord y)2369 ActionDisperseElements (int argc, char **argv, Coord x, Coord y)
2370 {
2371 char *function = ARG (0);
2372 Coord minx = GAP,
2373 miny = GAP,
2374 maxy = GAP,
2375 dx, dy;
2376 int all = 0, bad = 0;
2377
2378 if (!function || !*function)
2379 {
2380 bad = 1;
2381 }
2382 else
2383 {
2384 switch (GetFunctionID (function))
2385 {
2386 case F_All:
2387 all = 1;
2388 break;
2389
2390 case F_Selected:
2391 all = 0;
2392 break;
2393
2394 default:
2395 bad = 1;
2396 }
2397 }
2398
2399 if (bad)
2400 {
2401 AFAIL (disperseelements);
2402 }
2403
2404
2405 ELEMENT_LOOP (PCB->Data);
2406 {
2407 /*
2408 * If we want to disperse selected elements, maybe we need smarter
2409 * code here to avoid putting components on top of others which
2410 * are not selected. For now, I'm assuming that this is typically
2411 * going to be used either with a brand new design or a scratch
2412 * design holding some new components
2413 */
2414 if (!TEST_FLAG (LOCKFLAG, element) && (all || TEST_FLAG (SELECTEDFLAG, element)))
2415 {
2416
2417 /* figure out how much to move the element */
2418 dx = minx - element->BoundingBox.X1;
2419
2420 /* snap to the grid */
2421 dx -= (element->MarkX + dx) % PCB->Grid;
2422
2423 /*
2424 * and add one grid size so we make sure we always space by GAP or
2425 * more
2426 */
2427 dx += PCB->Grid;
2428
2429 /* Figure out if this row has room. If not, start a new row */
2430 if (GAP + element->BoundingBox.X2 + dx > PCB->MaxWidth)
2431 {
2432 miny = maxy + GAP;
2433 minx = GAP;
2434 }
2435
2436 /* figure out how much to move the element */
2437 dx = minx - element->BoundingBox.X1;
2438 dy = miny - element->BoundingBox.Y1;
2439
2440 /* snap to the grid */
2441 dx -= (element->MarkX + dx) % PCB->Grid;
2442 dx += PCB->Grid;
2443 dy -= (element->MarkY + dy) % PCB->Grid;
2444 dy += PCB->Grid;
2445
2446 /* move the element */
2447 MoveElementLowLevel (PCB->Data, element, dx, dy);
2448
2449 /* and add to the undo list so we can undo this operation */
2450 AddObjectToMoveUndoList (ELEMENT_TYPE, NULL, NULL, element, dx, dy);
2451
2452 /* keep track of how tall this row is */
2453 minx += element->BoundingBox.X2 - element->BoundingBox.X1 + GAP;
2454 if (maxy < element->BoundingBox.Y2)
2455 {
2456 maxy = element->BoundingBox.Y2;
2457 }
2458 }
2459
2460 }
2461 END_LOOP;
2462
2463 /* done with our action so increment the undo # */
2464 IncrementUndoSerialNumber ();
2465
2466 Redraw ();
2467 SetChangedFlag (true);
2468
2469 return 0;
2470 }
2471
2472 #undef GAP
2473
2474 /* --------------------------------------------------------------------------- */
2475
2476 static const char display_syntax[] =
2477 N_("Display(NameOnPCB|Description|Value)\n"
2478 "Display(Grid|Redraw)\n"
2479 "Display(CycleClip|CycleCrosshair|Toggle45Degree|ToggleStartDirection)\n"
2480 "Display(ToggleGrid|ToggleRubberBandMode|ToggleUniqueNames)\n"
2481 "Display(ToggleMask|ToggleName|ToggleClearLine|ToggleFullPoly|ToggleSnapPin)\n"
2482 "Display(ToggleThindraw|ToggleThindrawPoly|ToggleOrthoMove|ToggleLocalRef)\n"
2483 "Display(ToggleCheckPlanes|ToggleShowDRC|ToggleAutoDRC)\n"
2484 "Display(ToggleLiveRoute|LockNames|OnlyNames)\n"
2485 "Display(Pinout|PinOrPadName)");
2486
2487 static const char display_help[] = N_("Several display-related actions.");
2488
2489 /* %start-doc actions Display
2490
2491 @table @code
2492
2493 @item NameOnPCB
2494 @item Description
2495 @item Value
2496 Specify whether all elements show their name, description, or value.
2497
2498 @item Redraw
2499 Redraw the whole board.
2500
2501 @item Toggle45Degree
2502 When clear, lines can be drawn at any angle. When set, lines are
2503 restricted to multiples of 45 degrees and requested lines may be
2504 broken up according to the clip setting.
2505
2506 @item CycleClip
2507 Changes the way lines are restricted to 45 degree increments. The
2508 various settings are: straight only, orthogonal then angled, and angled
2509 then orthogonal. If AllDirections is set, this action disables it.
2510
2511 @item CycleCrosshair
2512 Changes crosshair drawing. Crosshair may accept form of 4-ray,
2513 8-ray and 12-ray cross.
2514
2515 @item ToggleRubberBandMode
2516 If set, moving an object moves all the lines attached to it too.
2517
2518 @item ToggleStartDirection
2519 If set, each time you set a point in a line, the Clip toggles between
2520 orth-angle and angle-ortho.
2521
2522 @item ToggleUniqueNames
2523 If set, you will not be permitted to change the name of an element to
2524 match that of another element.
2525
2526 @item ToggleSnapPin
2527 If set, pin centers and pad end points are treated as additional grid
2528 points that the cursor can snap to.
2529
2530 @item ToggleLocalRef
2531 If set, the mark is automatically set to the beginning of any move, so
2532 you can see the relative distance you've moved.
2533
2534 @item ToggleThindraw
2535 If set, objects on the screen are drawn as outlines (lines are drawn
2536 as center-lines). This lets you see line endpoints hidden under pins,
2537 for example.
2538
2539 @item ToggleThindrawPoly
2540 If set, polygons on the screen are drawn as outlines.
2541
2542 @item ToggleShowDRC
2543 If set, pending objects (i.e. lines you're in the process of drawing)
2544 will be drawn with an outline showing how far away from other copper
2545 you need to be.
2546
2547 @item ToggleLiveRoute
2548 If set, the progress of the autorouter will be visible on the screen.
2549
2550 @item ToggleAutoDRC
2551 If set, you will not be permitted to make connections which violate
2552 the current DRC and netlist settings.
2553
2554 @item ToggleCheckPlanes
2555 If set, lines and arcs aren't drawn, which usually leaves just the
2556 polygons. If you also disable all but the layer you're interested in,
2557 this allows you to check for isolated regions.
2558
2559 @item ToggleOrthoMove
2560 If set, the crosshair is only allowed to move orthogonally from its
2561 previous position. I.e. you can move an element or line up, down,
2562 left, or right, but not up+left or down+right.
2563
2564 @item ToggleName
2565 Selects whether the pinouts show the pin names or the pin numbers.
2566
2567 @item ToggleLockNames
2568 If set, text will ignore left mouse clicks and actions that work on
2569 objects under the mouse. You can still select text with a lasso (left
2570 mouse drag) and perform actions on the selection.
2571
2572 @item ToggleOnlyNames
2573 If set, only text will be sensitive for mouse clicks and actions that
2574 work on objects under the mouse. You can still select other objects
2575 with a lasso (left mouse drag) and perform actions on the selection.
2576
2577 @item ToggleMask
2578 Turns the solder mask on or off.
2579
2580 @item ToggleClearLine
2581 When set, the clear-line flag causes new lines and arcs to have their
2582 ``clear polygons'' flag set, so they won't be electrically connected
2583 to any polygons they overlap.
2584
2585 @item ToggleFullPoly
2586 When set, the full-poly flag causes new polygons to have their
2587 ``full polygon'' flag set, so all parts of them will be displayed
2588 instead of only the biggest one.
2589
2590 @item ToggleGrid
2591 Resets the origin of the current grid to be wherever the mouse pointer
2592 is (not where the crosshair currently is). If you provide two numbers
2593 after this, the origin is set to that coordinate.
2594
2595 @item Grid
2596 Toggles whether the grid is displayed or not.
2597
2598 @item Pinout
2599 Causes the pinout of the element indicated by the cursor to be
2600 displayed, usually in a separate window.
2601
2602 @item PinOrPadName
2603 Toggles whether the names of pins, pads, or (yes) vias will be
2604 displayed. If the cursor is over an element, all of its pins and pads
2605 are affected.
2606
2607 @item ToggleAutoBuriedVias
2608 If set, automatically created vias are buried vias.
2609
2610 @end table
2611
2612 %end-doc */
2613
2614 static enum crosshair_shape
CrosshairShapeIncrement(enum crosshair_shape shape)2615 CrosshairShapeIncrement (enum crosshair_shape shape)
2616 {
2617 switch(shape)
2618 {
2619 case Basic_Crosshair_Shape:
2620 shape = Union_Jack_Crosshair_Shape;
2621 break;
2622 case Union_Jack_Crosshair_Shape:
2623 shape = Dozen_Crosshair_Shape;
2624 break;
2625 case Dozen_Crosshair_Shape:
2626 shape = Crosshair_Shapes_Number;
2627 break;
2628 case Crosshair_Shapes_Number:
2629 shape = Basic_Crosshair_Shape;
2630 break;
2631 }
2632 return shape;
2633 }
2634
2635 static int
ActionDisplay(int argc,char ** argv,Coord childX,Coord childY)2636 ActionDisplay (int argc, char **argv, Coord childX, Coord childY)
2637 {
2638 char *function, *str_dir;
2639 int id;
2640 int err = 0;
2641
2642 function = ARG (0);
2643 str_dir = ARG (1);
2644
2645 if (function && (!str_dir || !*str_dir))
2646 {
2647 switch (id = GetFunctionID (function))
2648 {
2649
2650 /* redraw layout */
2651 case F_ClearAndRedraw:
2652 case F_Redraw:
2653 Redraw ();
2654 break;
2655
2656 /* change the displayed name of elements */
2657 case F_Value:
2658 case F_NameOnPCB:
2659 case F_Description:
2660 ELEMENT_LOOP (PCB->Data);
2661 {
2662 EraseElementName (element);
2663 }
2664 END_LOOP;
2665 CLEAR_FLAG (DESCRIPTIONFLAG | NAMEONPCBFLAG, PCB);
2666 switch (id)
2667 {
2668 case F_Value:
2669 break;
2670 case F_NameOnPCB:
2671 SET_FLAG (NAMEONPCBFLAG, PCB);
2672 break;
2673 case F_Description:
2674 SET_FLAG (DESCRIPTIONFLAG, PCB);
2675 break;
2676 }
2677 ELEMENT_LOOP (PCB->Data);
2678 {
2679 DrawElementName (element);
2680 }
2681 END_LOOP;
2682 Draw ();
2683 break;
2684
2685 /* toggle line-adjust flag */
2686 case F_ToggleAllDirections:
2687 TOGGLE_FLAG (ALLDIRECTIONFLAG, PCB);
2688 AdjustAttachedObjects ();
2689 break;
2690
2691 case F_CycleClip:
2692 notify_crosshair_change (false);
2693 if (TEST_FLAG (ALLDIRECTIONFLAG, PCB))
2694 {
2695 TOGGLE_FLAG (ALLDIRECTIONFLAG, PCB);
2696 PCB->Clipping = 0;
2697 }
2698 else
2699 PCB->Clipping = (PCB->Clipping + 1) % 3;
2700 AdjustAttachedObjects ();
2701 notify_crosshair_change (true);
2702 break;
2703
2704 case F_CycleCrosshair:
2705 notify_crosshair_change (false);
2706 Crosshair.shape = CrosshairShapeIncrement(Crosshair.shape);
2707 if (Crosshair_Shapes_Number == Crosshair.shape)
2708 Crosshair.shape = Basic_Crosshair_Shape;
2709 notify_crosshair_change (true);
2710 break;
2711
2712 case F_ToggleRubberBandMode:
2713 notify_crosshair_change (false);
2714 TOGGLE_FLAG (RUBBERBANDFLAG, PCB);
2715 notify_crosshair_change (true);
2716 break;
2717
2718 case F_ToggleAutoBuriedVias:
2719 notify_crosshair_change (false);
2720 TOGGLE_FLAG (AUTOBURIEDVIASFLAG, PCB);
2721 notify_crosshair_change (true);
2722 break;
2723
2724 case F_ToggleStartDirection:
2725 notify_crosshair_change (false);
2726 TOGGLE_FLAG (SWAPSTARTDIRFLAG, PCB);
2727 notify_crosshair_change (true);
2728 break;
2729
2730 case F_ToggleUniqueNames:
2731 TOGGLE_FLAG (UNIQUENAMEFLAG, PCB);
2732 break;
2733
2734 case F_ToggleSnapPin:
2735 notify_crosshair_change (false);
2736 TOGGLE_FLAG (SNAPPINFLAG, PCB);
2737 notify_crosshair_change (true);
2738 break;
2739
2740 case F_ToggleLocalRef:
2741 TOGGLE_FLAG (LOCALREFFLAG, PCB);
2742 break;
2743
2744 case F_ToggleThindraw:
2745 TOGGLE_FLAG (THINDRAWFLAG, PCB);
2746 Redraw ();
2747 break;
2748
2749 case F_ToggleThindrawPoly:
2750 TOGGLE_FLAG (THINDRAWPOLYFLAG, PCB);
2751 Redraw ();
2752 break;
2753
2754 case F_ToggleLockNames:
2755 TOGGLE_FLAG (LOCKNAMESFLAG, PCB);
2756 CLEAR_FLAG (ONLYNAMESFLAG, PCB);
2757 break;
2758
2759 case F_ToggleOnlyNames:
2760 TOGGLE_FLAG (ONLYNAMESFLAG, PCB);
2761 CLEAR_FLAG (LOCKNAMESFLAG, PCB);
2762 break;
2763
2764 case F_ToggleHideNames:
2765 TOGGLE_FLAG (HIDENAMESFLAG, PCB);
2766 Redraw ();
2767 break;
2768
2769 case F_ToggleShowDRC:
2770 TOGGLE_FLAG (SHOWDRCFLAG, PCB);
2771 break;
2772
2773 case F_ToggleLiveRoute:
2774 TOGGLE_FLAG (LIVEROUTEFLAG, PCB);
2775 break;
2776
2777 case F_ToggleAutoDRC:
2778 notify_crosshair_change (false);
2779 TOGGLE_FLAG (AUTODRCFLAG, PCB);
2780 if (TEST_FLAG (AUTODRCFLAG, PCB) && Settings.Mode == LINE_MODE)
2781 {
2782 if (ClearFlagOnAllObjects (CONNECTEDFLAG | FOUNDFLAG, true))
2783 {
2784 IncrementUndoSerialNumber ();
2785 Draw ();
2786 }
2787 if (Crosshair.AttachedLine.State != STATE_FIRST)
2788 {
2789 LookupConnection (Crosshair.AttachedLine.Point1.X,
2790 Crosshair.AttachedLine.Point1.Y,
2791 true, 1, CONNECTEDFLAG, false);
2792 LookupConnection (Crosshair.AttachedLine.Point1.X,
2793 Crosshair.AttachedLine.Point1.Y,
2794 true, 1, FOUNDFLAG, true);
2795 }
2796 }
2797 notify_crosshair_change (true);
2798 break;
2799
2800 case F_ToggleCheckPlanes:
2801 TOGGLE_FLAG (CHECKPLANESFLAG, PCB);
2802 Redraw ();
2803 break;
2804
2805 case F_ToggleOrthoMove:
2806 TOGGLE_FLAG (ORTHOMOVEFLAG, PCB);
2807 break;
2808
2809 case F_ToggleName:
2810 TOGGLE_FLAG (SHOWNUMBERFLAG, PCB);
2811 Redraw ();
2812 break;
2813
2814 case F_ToggleMask:
2815 TOGGLE_FLAG (SHOWMASKFLAG, PCB);
2816 Redraw ();
2817 break;
2818
2819 case F_ToggleClearLine:
2820 TOGGLE_FLAG (CLEARNEWFLAG, PCB);
2821 break;
2822
2823 case F_ToggleFullPoly:
2824 TOGGLE_FLAG (NEWFULLPOLYFLAG, PCB);
2825 break;
2826
2827 /* shift grid alignment */
2828 case F_ToggleGrid:
2829 {
2830 Coord oldGrid = PCB->Grid;
2831
2832 PCB->Grid = 1;
2833 if (MoveCrosshairAbsolute (Crosshair.X, Crosshair.Y))
2834 notify_crosshair_change (true); /* first notify was in MoveCrosshairAbs */
2835 SetGrid (oldGrid, true);
2836 }
2837 break;
2838
2839 /* toggle displaying of the grid */
2840 case F_Grid:
2841 Settings.DrawGrid = !Settings.DrawGrid;
2842 Redraw ();
2843 break;
2844
2845 /* display the pinout of an element */
2846 case F_Pinout:
2847 {
2848 ElementType *element;
2849 void *ptrtmp;
2850 Coord x, y;
2851
2852 gui->get_coords (_("Click on an element"), &x, &y);
2853 if ((SearchScreen
2854 (x, y, ELEMENT_TYPE, &ptrtmp,
2855 &ptrtmp, &ptrtmp)) != NO_TYPE)
2856 {
2857 element = (ElementType *) ptrtmp;
2858 gui->show_item (element);
2859 }
2860 break;
2861 }
2862
2863 /* toggle displaying of pin/pad/via names */
2864 case F_PinOrPadName:
2865 {
2866 void *ptr1, *ptr2, *ptr3;
2867 Coord x, y;
2868
2869 gui->get_coords(_("Click on an element"), &x, &y);
2870
2871 switch (SearchScreen (x, y,
2872 ELEMENT_TYPE | PIN_TYPE | PAD_TYPE |
2873 VIA_TYPE, (void **) &ptr1, (void **) &ptr2,
2874 (void **) &ptr3))
2875 {
2876 case ELEMENT_TYPE:
2877 PIN_LOOP ((ElementType *) ptr1);
2878 {
2879 if (TEST_FLAG (DISPLAYNAMEFLAG, pin))
2880 ErasePinName (pin);
2881 else
2882 DrawPinName (pin);
2883 AddObjectToFlagUndoList (PIN_TYPE, ptr1, pin, pin);
2884 TOGGLE_FLAG (DISPLAYNAMEFLAG, pin);
2885 }
2886 END_LOOP;
2887 PAD_LOOP ((ElementType *) ptr1);
2888 {
2889 if (TEST_FLAG (DISPLAYNAMEFLAG, pad))
2890 ErasePadName (pad);
2891 else
2892 DrawPadName (pad);
2893 AddObjectToFlagUndoList (PAD_TYPE, ptr1, pad, pad);
2894 TOGGLE_FLAG (DISPLAYNAMEFLAG, pad);
2895 }
2896 END_LOOP;
2897 SetChangedFlag (true);
2898 IncrementUndoSerialNumber ();
2899 Draw ();
2900 break;
2901
2902 case PIN_TYPE:
2903 if (TEST_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2))
2904 ErasePinName ((PinType *) ptr2);
2905 else
2906 DrawPinName ((PinType *) ptr2);
2907 AddObjectToFlagUndoList (PIN_TYPE, ptr1, ptr2, ptr3);
2908 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2);
2909 SetChangedFlag (true);
2910 IncrementUndoSerialNumber ();
2911 Draw ();
2912 break;
2913
2914 case PAD_TYPE:
2915 if (TEST_FLAG (DISPLAYNAMEFLAG, (PadType *) ptr2))
2916 ErasePadName ((PadType *) ptr2);
2917 else
2918 DrawPadName ((PadType *) ptr2);
2919 AddObjectToFlagUndoList (PAD_TYPE, ptr1, ptr2, ptr3);
2920 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PadType *) ptr2);
2921 SetChangedFlag (true);
2922 IncrementUndoSerialNumber ();
2923 Draw ();
2924 break;
2925 case VIA_TYPE:
2926 if (TEST_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2))
2927 EraseViaName ((PinType *) ptr2);
2928 else
2929 DrawViaName ((PinType *) ptr2);
2930 AddObjectToFlagUndoList (VIA_TYPE, ptr1, ptr2, ptr3);
2931 TOGGLE_FLAG (DISPLAYNAMEFLAG, (PinType *) ptr2);
2932 SetChangedFlag (true);
2933 IncrementUndoSerialNumber ();
2934 Draw ();
2935 break;
2936 }
2937 break;
2938 }
2939 default:
2940 err = 1;
2941 }
2942 }
2943 else if (function && str_dir)
2944 {
2945 switch (GetFunctionID (function))
2946 {
2947 case F_ToggleGrid:
2948 if (argc > 2)
2949 {
2950 PCB->GridOffsetX = GetValue (argv[1], NULL, NULL);
2951 PCB->GridOffsetY = GetValue (argv[2], NULL, NULL);
2952 if (Settings.DrawGrid)
2953 Redraw ();
2954 }
2955 break;
2956
2957 default:
2958 err = 1;
2959 break;
2960 }
2961 }
2962 else
2963 err = 1;
2964
2965 if (err)
2966 AFAIL (display);
2967
2968 return 0;
2969 }
2970
2971 /* --------------------------------------------------------------------------- */
2972
2973 static const char mode_syntax[] =
2974 N_("Mode(Arc|Arrow|Copy|InsertPoint|Line|Lock|Move|None|PasteBuffer)\n"
2975 "Mode(Polygon|Rectangle|Remove|Rotate|Text|Thermal|Via)\n"
2976 "Mode(Notify|Release|Cancel|Stroke)\n"
2977 "Mode(Save|Restore)");
2978
2979 static const char mode_help[] = N_("Change or use the tool mode.");
2980
2981 /* %start-doc actions Mode
2982
2983 @table @code
2984
2985 @item Arc
2986 @itemx Arrow
2987 @itemx Copy
2988 @itemx InsertPoint
2989 @itemx Line
2990 @itemx Lock
2991 @itemx Move
2992 @itemx None
2993 @itemx PasteBuffer
2994 @itemx Polygon
2995 @itemx Rectangle
2996 @itemx Remove
2997 @itemx Rotate
2998 @itemx Text
2999 @itemx Thermal
3000 @itemx Via
3001 Select the indicated tool.
3002
3003 @item Notify
3004 Called when you press the mouse button, or move the mouse.
3005
3006 @item Release
3007 Called when you release the mouse button.
3008
3009 @item Cancel
3010 Cancels any pending tool activity, allowing you to restart elsewhere.
3011 For example, this allows you to start a new line rather than attach a
3012 line to the previous line.
3013
3014 @item Escape
3015 Similar to Cancel but calling this action a second time will return
3016 to the Arrow tool.
3017
3018 @item Stroke
3019 If your @code{pcb} was built with libstroke, this invokes the stroke
3020 input method. If not, this will restart a drawing mode if you were
3021 drawing, else it will select objects.
3022
3023 @item Save
3024 Remembers the current tool.
3025
3026 @item Restore
3027 Restores the tool to the last saved tool.
3028
3029 @end table
3030
3031 %end-doc */
3032
3033 static int
ActionMode(int argc,char ** argv,Coord x,Coord y)3034 ActionMode (int argc, char **argv, Coord x, Coord y)
3035 {
3036 char *function = ARG (0);
3037
3038 if (function)
3039 {
3040 Note.X = Crosshair.X;
3041 Note.Y = Crosshair.Y;
3042 notify_crosshair_change (false);
3043 switch (GetFunctionID (function))
3044 {
3045 case F_Arc:
3046 SetMode (ARC_MODE);
3047 break;
3048 case F_Arrow:
3049 SetMode (ARROW_MODE);
3050 break;
3051 case F_Copy:
3052 SetMode (COPY_MODE);
3053 break;
3054 case F_InsertPoint:
3055 SetMode (INSERTPOINT_MODE);
3056 break;
3057 case F_Line:
3058 SetMode (LINE_MODE);
3059 break;
3060 case F_Lock:
3061 SetMode (LOCK_MODE);
3062 break;
3063 case F_Move:
3064 SetMode (MOVE_MODE);
3065 break;
3066 case F_None:
3067 SetMode (NO_MODE);
3068 break;
3069 case F_Cancel:
3070 {
3071 int saved_mode = Settings.Mode;
3072 SetMode (NO_MODE);
3073 SetMode (saved_mode);
3074 }
3075 break;
3076 case F_Escape:
3077 {
3078 switch (Settings.Mode)
3079 {
3080 case VIA_MODE:
3081 case PASTEBUFFER_MODE:
3082 case TEXT_MODE:
3083 case ROTATE_MODE:
3084 case REMOVE_MODE:
3085 case MOVE_MODE:
3086 case COPY_MODE:
3087 case INSERTPOINT_MODE:
3088 case RUBBERBANDMOVE_MODE:
3089 case THERMAL_MODE:
3090 case LOCK_MODE:
3091 SetMode (NO_MODE);
3092 SetMode (ARROW_MODE);
3093 break;
3094
3095 case LINE_MODE:
3096 if (Crosshair.AttachedLine.State == STATE_FIRST)
3097 SetMode (ARROW_MODE);
3098 else
3099 {
3100 SetMode (NO_MODE);
3101 SetMode (LINE_MODE);
3102 }
3103 break;
3104
3105 case RECTANGLE_MODE:
3106 if (Crosshair.AttachedBox.State == STATE_FIRST)
3107 SetMode (ARROW_MODE);
3108 else
3109 {
3110 SetMode (NO_MODE);
3111 SetMode (RECTANGLE_MODE);
3112 }
3113 break;
3114
3115 case POLYGON_MODE:
3116 if (Crosshair.AttachedLine.State == STATE_FIRST)
3117 SetMode (ARROW_MODE);
3118 else
3119 {
3120 SetMode (NO_MODE);
3121 SetMode (POLYGON_MODE);
3122 }
3123 break;
3124
3125 case POLYGONHOLE_MODE:
3126 if (Crosshair.AttachedLine.State == STATE_FIRST)
3127 SetMode (ARROW_MODE);
3128 else
3129 {
3130 SetMode (NO_MODE);
3131 SetMode (POLYGONHOLE_MODE);
3132 }
3133 break;
3134
3135 case ARC_MODE:
3136 if (Crosshair.AttachedBox.State == STATE_FIRST)
3137 SetMode (ARROW_MODE);
3138 else
3139 {
3140 SetMode (NO_MODE);
3141 SetMode (ARC_MODE);
3142 }
3143 break;
3144
3145 case ARROW_MODE:
3146 break;
3147
3148 default:
3149 break;
3150 }
3151 }
3152 break;
3153
3154 case F_Notify:
3155 NotifyMode ();
3156 break;
3157 case F_PasteBuffer:
3158 SetMode (PASTEBUFFER_MODE);
3159 break;
3160 case F_Polygon:
3161 SetMode (POLYGON_MODE);
3162 break;
3163 case F_PolygonHole:
3164 SetMode (POLYGONHOLE_MODE);
3165 break;
3166 #ifndef HAVE_LIBSTROKE
3167 case F_Release:
3168 ReleaseMode ();
3169 break;
3170 #else
3171 case F_Release:
3172 if (mid_stroke)
3173 FinishStroke ();
3174 else
3175 ReleaseMode ();
3176 break;
3177 #endif
3178 case F_Remove:
3179 SetMode (REMOVE_MODE);
3180 break;
3181 case F_Rectangle:
3182 SetMode (RECTANGLE_MODE);
3183 break;
3184 case F_Rotate:
3185 SetMode (ROTATE_MODE);
3186 break;
3187 case F_Stroke:
3188 #ifdef HAVE_LIBSTROKE
3189 mid_stroke = true;
3190 StrokeBox.X1 = Crosshair.X;
3191 StrokeBox.Y1 = Crosshair.Y;
3192 break;
3193 #else
3194 /* Handle middle mouse button restarts of drawing mode. If not in
3195 | a drawing mode, middle mouse button will select objects.
3196 */
3197 if (Settings.Mode == LINE_MODE
3198 && Crosshair.AttachedLine.State != STATE_FIRST)
3199 {
3200 SetMode (LINE_MODE);
3201 }
3202 else if (Settings.Mode == ARC_MODE
3203 && Crosshair.AttachedBox.State != STATE_FIRST)
3204 SetMode (ARC_MODE);
3205 else if (Settings.Mode == RECTANGLE_MODE
3206 && Crosshair.AttachedBox.State != STATE_FIRST)
3207 SetMode (RECTANGLE_MODE);
3208 else if (Settings.Mode == POLYGON_MODE
3209 && Crosshair.AttachedLine.State != STATE_FIRST)
3210 SetMode (POLYGON_MODE);
3211 else
3212 {
3213 SaveMode ();
3214 saved_mode = true;
3215 SetMode (ARROW_MODE);
3216 NotifyMode ();
3217 }
3218 break;
3219 #endif
3220 case F_Text:
3221 SetMode (TEXT_MODE);
3222 break;
3223 case F_Thermal:
3224 SetMode (THERMAL_MODE);
3225 break;
3226 case F_Via:
3227 SetMode (VIA_MODE);
3228 break;
3229
3230 case F_Restore: /* restore the last saved mode */
3231 RestoreMode ();
3232 break;
3233
3234 case F_Save: /* save currently selected mode */
3235 SaveMode ();
3236 break;
3237 }
3238 notify_crosshair_change (true);
3239 return 0;
3240 }
3241
3242 AFAIL (mode);
3243 }
3244
3245 /* --------------------------------------------------------------------------- */
3246
3247 static const char removeselected_syntax[] = N_("RemoveSelected()");
3248
3249 static const char removeselected_help[] = N_("Removes any selected objects.");
3250
3251 /* %start-doc actions RemoveSelected
3252
3253 %end-doc */
3254
3255 static int
ActionRemoveSelected(int argc,char ** argv,Coord x,Coord y)3256 ActionRemoveSelected (int argc, char **argv, Coord x, Coord y)
3257 {
3258 if (RemoveSelected ())
3259 SetChangedFlag (true);
3260 return 0;
3261 }
3262
3263 /* --------------------------------------------------------------------------- */
3264
3265 static const char renumber_syntax[] = N_("Renumber()\n"
3266 "Renumber(filename)");
3267
3268 static const char renumber_help[] =
3269 N_("Renumber all elements. The changes will be recorded to filename\n"
3270 "for use in backannotating these changes to the schematic.");
3271
3272 /* %start-doc actions Renumber
3273
3274 %end-doc */
3275
3276 static int
ActionRenumber(int argc,char ** argv,Coord x,Coord y)3277 ActionRenumber (int argc, char **argv, Coord x, Coord y)
3278 {
3279 bool changed = false;
3280 ElementType **element_list;
3281 ElementType **locked_element_list;
3282 unsigned int i, j, k, cnt, lock_cnt;
3283 unsigned int tmpi;
3284 size_t sz;
3285 char *tmps;
3286 char *name;
3287 FILE *out;
3288 static char * default_file = NULL;
3289 size_t cnt_list_sz = 100;
3290 struct _cnt_list
3291 {
3292 char *name;
3293 unsigned int cnt;
3294 } *cnt_list;
3295 char **was, **is, *pin;
3296 unsigned int c_cnt = 0;
3297 int unique, ok;
3298 int free_name = 0;
3299
3300 if (argc < 1)
3301 {
3302 /*
3303 * We deal with the case where name already exists in this
3304 * function so the GUI doesn't need to deal with it
3305 */
3306 name = gui->fileselect (_("Save Renumber Annotation File As ..."),
3307 _("Choose a file to record the renumbering to.\n"
3308 "This file may be used to back annotate the\n"
3309 "change to the schematics.\n"),
3310 default_file, ".eco", "eco",
3311 0);
3312
3313 free_name = 1;
3314 }
3315 else
3316 name = argv[0];
3317
3318 if (default_file)
3319 {
3320 free (default_file);
3321 default_file = NULL;
3322 }
3323
3324 if (name && *name)
3325 {
3326 default_file = strdup (name);
3327 }
3328
3329 if ((out = fopen (name, "r")))
3330 {
3331 fclose (out);
3332 if (!gui->confirm_dialog (_("File exists! Ok to overwrite?"), 0))
3333 {
3334 if (free_name && name)
3335 free (name);
3336 return 0;
3337 }
3338 }
3339
3340 if ((out = fopen (name, "w")) == NULL)
3341 {
3342 Message (_("Could not open %s\n"), name);
3343 if (free_name && name)
3344 free (name);
3345 return 1;
3346 }
3347
3348 if (free_name && name)
3349 free (name);
3350
3351 fprintf (out, "*COMMENT* PCB Annotation File\n");
3352 fprintf (out, "*FILEVERSION* 20061031\n");
3353
3354 /*
3355 * Make a first pass through all of the elements and sort them out
3356 * by location on the board. While here we also collect a list of
3357 * locked elements.
3358 *
3359 * We'll actually renumber things in the 2nd pass.
3360 */
3361 element_list = (ElementType **)calloc (PCB->Data->ElementN, sizeof (ElementType *));
3362 locked_element_list = (ElementType **)calloc (PCB->Data->ElementN, sizeof (ElementType *));
3363 was = (char **)calloc (PCB->Data->ElementN, sizeof (char *));
3364 is = (char **)calloc (PCB->Data->ElementN, sizeof (char *));
3365 if (element_list == NULL || locked_element_list == NULL || was == NULL
3366 || is == NULL)
3367 {
3368 fprintf (stderr, "calloc() failed in %s\n", __FUNCTION__);
3369 exit (1);
3370 }
3371
3372
3373 cnt = 0;
3374 lock_cnt = 0;
3375 ELEMENT_LOOP (PCB->Data);
3376 {
3377 if (TEST_FLAG (LOCKFLAG, element->Name) || TEST_FLAG (LOCKFLAG, element))
3378 {
3379 /*
3380 * add to the list of locked elements which we won't try to
3381 * renumber and whose reference designators are now reserved.
3382 */
3383 pcb_fprintf (out,
3384 "*WARN* Element \"%s\" at %$md is locked and will not be renumbered.\n",
3385 UNKNOWN (NAMEONPCB_NAME (element)), element->MarkX, element->MarkY);
3386 locked_element_list[lock_cnt] = element;
3387 lock_cnt++;
3388 }
3389
3390 else
3391 {
3392 /* count of devices which will be renumbered */
3393 cnt++;
3394
3395 /* search for correct position in the list */
3396 i = 0;
3397 while (element_list[i] && element->MarkY > element_list[i]->MarkY)
3398 i++;
3399
3400 /*
3401 * We have found the position where we have the first element that
3402 * has the same Y value or a lower Y value. Now move forward if
3403 * needed through the X values
3404 */
3405 while (element_list[i]
3406 && element->MarkY == element_list[i]->MarkY
3407 && element->MarkX > element_list[i]->MarkX)
3408 i++;
3409
3410 for (j = cnt - 1; j > i; j--)
3411 {
3412 element_list[j] = element_list[j - 1];
3413 }
3414 element_list[i] = element;
3415 }
3416 }
3417 END_LOOP;
3418
3419
3420 /*
3421 * Now that the elements are sorted by board position, we go through
3422 * and renumber them.
3423 */
3424
3425 /*
3426 * turn off the flag which requires unique names so it doesn't get
3427 * in our way. When we're done with the renumber we will have unique
3428 * names.
3429 */
3430 unique = TEST_FLAG (UNIQUENAMEFLAG, PCB);
3431 CLEAR_FLAG (UNIQUENAMEFLAG, PCB);
3432
3433 cnt_list = (struct _cnt_list *)calloc (cnt_list_sz, sizeof (struct _cnt_list));
3434 for (i = 0; i < cnt; i++)
3435 {
3436 /* If there is no refdes, maybe just spit out a warning */
3437 if (NAMEONPCB_NAME (element_list[i]))
3438 {
3439 /* figure out the prefix */
3440 tmps = strdup (NAMEONPCB_NAME (element_list[i]));
3441 j = 0;
3442 while (tmps[j] && (tmps[j] < '0' || tmps[j] > '9')
3443 && tmps[j] != '?')
3444 j++;
3445 tmps[j] = '\0';
3446
3447 /* check the counter for this prefix */
3448 for (j = 0;
3449 cnt_list[j].name && (strcmp (cnt_list[j].name, tmps) != 0)
3450 && j < cnt_list_sz; j++);
3451
3452 /* grow the list if needed */
3453 if (j == cnt_list_sz)
3454 {
3455 cnt_list_sz += 100;
3456 cnt_list = (struct _cnt_list *)realloc (cnt_list, cnt_list_sz);
3457 if (cnt_list == NULL)
3458 {
3459 fprintf (stderr, _("realloc() failed in %s()\n"), __FUNCTION__);
3460 exit (1);
3461 }
3462 /* zero out the memory that we added */
3463 for (tmpi = j; tmpi < cnt_list_sz; tmpi++)
3464 {
3465 cnt_list[tmpi].name = NULL;
3466 cnt_list[tmpi].cnt = 0;
3467 }
3468 }
3469
3470 /*
3471 * start a new counter if we don't have a counter for this
3472 * prefix
3473 */
3474 if (!cnt_list[j].name)
3475 {
3476 cnt_list[j].name = strdup (tmps);
3477 cnt_list[j].cnt = 0;
3478 }
3479
3480 /*
3481 * check to see if the new refdes is already used by a
3482 * locked element
3483 */
3484 do
3485 {
3486 ok = 1;
3487 cnt_list[j].cnt++;
3488 free (tmps);
3489
3490 /* space for the prefix plus 1 digit plus the '\0' */
3491 sz = strlen (cnt_list[j].name) + 2;
3492
3493 /* and 1 more per extra digit needed to hold the number */
3494 tmpi = cnt_list[j].cnt;
3495 while (tmpi > 10)
3496 {
3497 sz++;
3498 tmpi = tmpi / 10;
3499 }
3500 tmps = (char *)malloc (sz * sizeof (char));
3501 sprintf (tmps, "%s%d", cnt_list[j].name, (int) cnt_list[j].cnt);
3502
3503 /*
3504 * now compare to the list of reserved (by locked
3505 * elements) names
3506 */
3507 for (k = 0; k < lock_cnt; k++)
3508 {
3509 if (strcmp
3510 (UNKNOWN (NAMEONPCB_NAME (locked_element_list[k])),
3511 tmps) == 0)
3512 {
3513 ok = 0;
3514 break;
3515 }
3516 }
3517
3518 }
3519 while (!ok);
3520
3521 if (strcmp (tmps, NAMEONPCB_NAME (element_list[i])) != 0)
3522 {
3523 fprintf (out, "*RENAME* \"%s\" \"%s\"\n",
3524 NAMEONPCB_NAME (element_list[i]), tmps);
3525
3526 /* add this rename to our table of renames so we can update the netlist */
3527 was[c_cnt] = strdup (NAMEONPCB_NAME (element_list[i]));
3528 is[c_cnt] = strdup (tmps);
3529 c_cnt++;
3530
3531 AddObjectToChangeNameUndoList (ELEMENT_TYPE, NULL, NULL,
3532 element_list[i],
3533 NAMEONPCB_NAME (element_list
3534 [i]));
3535
3536 ChangeObjectName (ELEMENT_TYPE, element_list[i], NULL, NULL,
3537 tmps);
3538 changed = true;
3539
3540 /* we don't free tmps in this case because it is used */
3541 }
3542 else
3543 free (tmps);
3544 }
3545 else
3546 {
3547 pcb_fprintf (out, "*WARN* Element at %$md has no name.\n",
3548 element_list[i]->MarkX, element_list[i]->MarkY);
3549 }
3550
3551 }
3552
3553 fclose (out);
3554
3555 /* restore the unique flag setting */
3556 if (unique)
3557 SET_FLAG (UNIQUENAMEFLAG, PCB);
3558
3559 if (changed)
3560 {
3561
3562 /* update the netlist */
3563 AddNetlistLibToUndoList (&(PCB->NetlistLib));
3564
3565 /* iterate over each net */
3566 for (i = 0; i < PCB->NetlistLib.MenuN; i++)
3567 {
3568
3569 /* iterate over each pin on the net */
3570 for (j = 0; j < PCB->NetlistLib.Menu[i].EntryN; j++)
3571 {
3572
3573 /* figure out the pin number part from strings like U3-21 */
3574 tmps = strdup (PCB->NetlistLib.Menu[i].Entry[j].ListEntry);
3575 for (k = 0; tmps[k] && tmps[k] != '-'; k++);
3576 tmps[k] = '\0';
3577 pin = tmps + k + 1;
3578
3579 /* iterate over the list of changed reference designators */
3580 for (k = 0; k < c_cnt; k++)
3581 {
3582 /*
3583 * if the pin needs to change, change it and quit
3584 * searching in the list.
3585 */
3586 if (strcmp (tmps, was[k]) == 0)
3587 {
3588 free (PCB->NetlistLib.Menu[i].Entry[j].ListEntry);
3589 PCB->NetlistLib.Menu[i].Entry[j].ListEntry =
3590 (char *)malloc ((strlen (is[k]) + strlen (pin) +
3591 2) * sizeof (char));
3592 sprintf (PCB->NetlistLib.Menu[i].Entry[j].ListEntry,
3593 "%s-%s", is[k], pin);
3594 k = c_cnt;
3595 }
3596
3597 }
3598 free (tmps);
3599 }
3600 }
3601 for (k = 0; k < c_cnt; k++)
3602 {
3603 free (was[k]);
3604 free (is[k]);
3605 }
3606
3607 NetlistChanged (0);
3608 IncrementUndoSerialNumber ();
3609 SetChangedFlag (true);
3610 }
3611
3612 free (locked_element_list);
3613 free (element_list);
3614 free (cnt_list);
3615 free (is);
3616 free (was);
3617 return 0;
3618 }
3619
3620
3621 /* --------------------------------------------------------------------------- */
3622
3623 static const char ripup_syntax[] = N_("RipUp(All|Selected|Element)");
3624
3625 static const char ripup_help[] =
3626 N_("Ripup auto-routed tracks, or convert an element to parts.");
3627
3628 /* %start-doc actions RipUp
3629
3630 @table @code
3631
3632 @item All
3633 Removes all lines and vias which were created by the autorouter.
3634
3635 @item Selected
3636 Removes all selected lines and vias which were created by the
3637 autorouter.
3638
3639 @item Element
3640 Converts the element under the cursor to parts (vias and lines). Note
3641 that this uses the highest numbered paste buffer.
3642
3643 @end table
3644
3645 %end-doc */
3646
3647 static int
ActionRipUp(int argc,char ** argv,Coord x,Coord y)3648 ActionRipUp (int argc, char **argv, Coord x, Coord y)
3649 {
3650 char *function = ARG (0);
3651 bool changed = false;
3652
3653 if (function)
3654 {
3655 switch (GetFunctionID (function))
3656 {
3657 case F_All:
3658 ALLLINE_LOOP (PCB->Data);
3659 {
3660 if (TEST_FLAG (AUTOFLAG, line) && !TEST_FLAG (LOCKFLAG, line))
3661 {
3662 RemoveObject (LINE_TYPE, layer, line, line);
3663 changed = true;
3664 }
3665 }
3666 ENDALL_LOOP;
3667 ALLARC_LOOP (PCB->Data);
3668 {
3669 if (TEST_FLAG (AUTOFLAG, arc) && !TEST_FLAG (LOCKFLAG, arc))
3670 {
3671 RemoveObject (ARC_TYPE, layer, arc, arc);
3672 changed = true;
3673 }
3674 }
3675 ENDALL_LOOP;
3676 VIA_LOOP (PCB->Data);
3677 {
3678 if (TEST_FLAG (AUTOFLAG, via) && !TEST_FLAG (LOCKFLAG, via))
3679 {
3680 RemoveObject (VIA_TYPE, via, via, via);
3681 changed = true;
3682 }
3683 }
3684 END_LOOP;
3685
3686 if (changed)
3687 {
3688 IncrementUndoSerialNumber ();
3689 SetChangedFlag (true);
3690 }
3691 break;
3692 case F_Selected:
3693 VISIBLELINE_LOOP (PCB->Data);
3694 {
3695 if (TEST_FLAGS (AUTOFLAG | SELECTEDFLAG, line)
3696 && !TEST_FLAG (LOCKFLAG, line))
3697 {
3698 RemoveObject (LINE_TYPE, layer, line, line);
3699 changed = true;
3700 }
3701 }
3702 ENDALL_LOOP;
3703 if (PCB->ViaOn)
3704 VIA_LOOP (PCB->Data);
3705 {
3706 if (TEST_FLAGS (AUTOFLAG | SELECTEDFLAG, via)
3707 && !TEST_FLAG (LOCKFLAG, via))
3708 {
3709 RemoveObject (VIA_TYPE, via, via, via);
3710 changed = true;
3711 }
3712 }
3713 END_LOOP;
3714 if (changed)
3715 {
3716 IncrementUndoSerialNumber ();
3717 SetChangedFlag (true);
3718 }
3719 break;
3720 case F_Element:
3721 {
3722 void *ptr1, *ptr2, *ptr3;
3723
3724 if (SearchScreen (Crosshair.X, Crosshair.Y, ELEMENT_TYPE,
3725 &ptr1, &ptr2, &ptr3) != NO_TYPE)
3726 {
3727 Note.Buffer = Settings.BufferNumber;
3728 SetBufferNumber (MAX_BUFFER - 1);
3729 ClearBuffer (PASTEBUFFER);
3730 CopyObjectToBuffer (PASTEBUFFER->Data, PCB->Data,
3731 ELEMENT_TYPE, ptr1, ptr2, ptr3);
3732 SmashBufferElement (PASTEBUFFER);
3733 PASTEBUFFER->X = 0;
3734 PASTEBUFFER->Y = 0;
3735 SaveUndoSerialNumber ();
3736 EraseObject (ELEMENT_TYPE, ptr1, ptr1);
3737 MoveObjectToRemoveUndoList (ELEMENT_TYPE, ptr1, ptr2, ptr3);
3738 RestoreUndoSerialNumber ();
3739 CopyPastebufferToLayout (0, 0);
3740 SetBufferNumber (Note.Buffer);
3741 SetChangedFlag (true);
3742 }
3743 }
3744 break;
3745 }
3746 }
3747 return 0;
3748 }
3749
3750 /* --------------------------------------------------------------------------- */
3751
3752 static const char addrats_syntax[] = N_("AddRats(AllRats|SelectedRats|Close)");
3753
3754 static const char addrats_help[] =
3755 N_("Add one or more rat lines to the board.");
3756
3757 /* %start-doc actions AddRats
3758
3759 @table @code
3760
3761 @item AllRats
3762 Create rat lines for all loaded nets that aren't already connected on
3763 with copper.
3764
3765 @item SelectedRats
3766 Similarly, but only add rat lines for nets connected to selected pins
3767 and pads.
3768
3769 @item Close
3770 Selects the shortest unselected rat on the board.
3771
3772 @end table
3773
3774 %end-doc */
3775
3776 static int
ActionAddRats(int argc,char ** argv,Coord x,Coord y)3777 ActionAddRats (int argc, char **argv, Coord x, Coord y)
3778 {
3779 char *function = ARG (0);
3780 RatType *shorty;
3781 float len, small;
3782
3783 if (function)
3784 {
3785 if (Settings.RatWarn)
3786 ClearWarnings ();
3787 switch (GetFunctionID (function))
3788 {
3789 case F_AllRats:
3790 if (AddAllRats (false, NULL))
3791 SetChangedFlag (true);
3792 break;
3793 case F_SelectedRats:
3794 case F_Selected:
3795 if (AddAllRats (true, NULL))
3796 SetChangedFlag (true);
3797 break;
3798 case F_Close:
3799 small = SQUARE (MAX_COORD);
3800 shorty = NULL;
3801 RAT_LOOP (PCB->Data);
3802 {
3803 if (TEST_FLAG (SELECTEDFLAG, line))
3804 continue;
3805 len = SQUARE (line->Point1.X - line->Point2.X) +
3806 SQUARE (line->Point1.Y - line->Point2.Y);
3807 if (len < small)
3808 {
3809 small = len;
3810 shorty = line;
3811 }
3812 }
3813 END_LOOP;
3814 if (shorty)
3815 {
3816 AddObjectToFlagUndoList (RATLINE_TYPE, shorty, shorty, shorty);
3817 SET_FLAG (SELECTEDFLAG, shorty);
3818 DrawRat (shorty);
3819 Draw ();
3820 CenterDisplay ((shorty->Point2.X + shorty->Point1.X) / 2,
3821 (shorty->Point2.Y + shorty->Point1.Y) / 2,
3822 false);
3823 }
3824 break;
3825 }
3826 }
3827 return 0;
3828 }
3829
3830 /* --------------------------------------------------------------------------- */
3831
3832 static const char delete_syntax[] =
3833 N_("Delete(Object|Selected)\n"
3834 "Delete(AllRats|SelectedRats)");
3835
3836 static const char delete_help[] = N_("Delete stuff.");
3837
3838 /* %start-doc actions Delete
3839
3840 %end-doc */
3841
3842 static int
ActionDelete(int argc,char ** argv,Coord x,Coord y)3843 ActionDelete (int argc, char **argv, Coord x, Coord y)
3844 {
3845 char *function = ARG (0);
3846 int id = GetFunctionID (function);
3847
3848 Note.X = Crosshair.X;
3849 Note.Y = Crosshair.Y;
3850
3851 if (id == -1) /* no arg */
3852 {
3853 if (RemoveSelected() == false)
3854 id = F_Object;
3855 }
3856
3857 switch (id)
3858 {
3859 case F_Object:
3860 SaveMode();
3861 SetMode(REMOVE_MODE);
3862 NotifyMode();
3863 RestoreMode();
3864 break;
3865 case F_Selected:
3866 RemoveSelected();
3867 break;
3868 case F_AllRats:
3869 if (DeleteRats (false))
3870 SetChangedFlag (true);
3871 break;
3872 case F_SelectedRats:
3873 if (DeleteRats (true))
3874 SetChangedFlag (true);
3875 break;
3876 }
3877
3878 return 0;
3879 }
3880
3881 /* --------------------------------------------------------------------------- */
3882
3883 static const char deleterats_syntax[] =
3884 N_("DeleteRats(AllRats|Selected|SelectedRats)");
3885
3886 static const char deleterats_help[] = N_("Delete rat lines.");
3887
3888 /* %start-doc actions DeleteRats
3889
3890 %end-doc */
3891
3892 static int
ActionDeleteRats(int argc,char ** argv,Coord x,Coord y)3893 ActionDeleteRats (int argc, char **argv, Coord x, Coord y)
3894 {
3895 char *function = ARG (0);
3896 if (function)
3897 {
3898 if (Settings.RatWarn)
3899 ClearWarnings ();
3900 switch (GetFunctionID (function))
3901 {
3902 case F_AllRats:
3903 if (DeleteRats (false))
3904 SetChangedFlag (true);
3905 break;
3906 case F_SelectedRats:
3907 case F_Selected:
3908 if (DeleteRats (true))
3909 SetChangedFlag (true);
3910 break;
3911 }
3912 }
3913 return 0;
3914 }
3915
3916 /* --------------------------------------------------------------------------- */
3917
3918 static const char autoplace_syntax[] = N_("AutoPlaceSelected()");
3919
3920 static const char autoplace_help[] = N_("Auto-place selected components.");
3921
3922 /* %start-doc actions AutoPlaceSelected
3923
3924 Attempts to re-arrange the selected components such that the nets
3925 connecting them are minimized. Note that you cannot undo this.
3926
3927 %end-doc */
3928
3929 static int
ActionAutoPlaceSelected(int argc,char ** argv,Coord x,Coord y)3930 ActionAutoPlaceSelected (int argc, char **argv, Coord x, Coord y)
3931 {
3932 hid_action("Busy");
3933 if (gui->confirm_dialog (_("Auto-placement can NOT be undone.\n"
3934 "Do you want to continue anyway?\n"), 0))
3935 {
3936 if (AutoPlaceSelected ())
3937 SetChangedFlag (true);
3938 }
3939 return 0;
3940 }
3941
3942 /* --------------------------------------------------------------------------- */
3943
3944 static const char autoroute_syntax[] = N_("AutoRoute(AllRats|SelectedRats)");
3945
3946 static const char autoroute_help[] = N_("Auto-route some or all rat lines.");
3947
3948 /* %start-doc actions AutoRoute
3949
3950 @table @code
3951
3952 @item AllRats
3953 Attempt to autoroute all rats.
3954
3955 @item SelectedRats
3956 Attempt to autoroute the selected rats.
3957
3958 @end table
3959
3960 Before autorouting, it's important to set up a few things. First,
3961 make sure any layers you aren't using are disabled, else the
3962 autorouter may use them. Next, make sure the current line and via
3963 styles are set accordingly. Last, make sure "new lines clear
3964 polygons" is set, in case you eventually want to add a copper pour.
3965
3966 Autorouting takes a while. During this time, the program may not be
3967 responsive.
3968
3969 %end-doc */
3970
3971 static int
ActionAutoRoute(int argc,char ** argv,Coord x,Coord y)3972 ActionAutoRoute (int argc, char **argv, Coord x, Coord y)
3973 {
3974 char *function = ARG (0);
3975 hid_action("Busy");
3976 if (function) /* one parameter */
3977 {
3978 switch (GetFunctionID (function))
3979 {
3980 case F_AllRats:
3981 if (AutoRoute (false))
3982 SetChangedFlag (true);
3983 break;
3984 case F_SelectedRats:
3985 case F_Selected:
3986 if (AutoRoute (true))
3987 SetChangedFlag (true);
3988 break;
3989 }
3990 }
3991 return 0;
3992 }
3993
3994 /* --------------------------------------------------------------------------- */
3995
3996 static const char markcrosshair_syntax[] =
3997 N_("MarkCrosshair()\n"
3998 "MarkCrosshair(Center)");
3999
4000 static const char markcrosshair_help[] = N_("Set/Reset the Crosshair mark.");
4001
4002 /* %start-doc actions MarkCrosshair
4003
4004 The ``mark'' is a small X-shaped target on the display which is
4005 treated like a second origin (the normal origin is the upper let
4006 corner of the board). The GUI will display a second set of
4007 coordinates for this mark, which tells you how far you are from it.
4008
4009 If no argument is given, the mark is toggled - disabled if it was
4010 enabled, or enabled at the current cursor position of disabled. If
4011 the @code{Center} argument is given, the mark is moved to the current
4012 cursor location.
4013
4014 %end-doc */
4015
4016 static int
ActionMarkCrosshair(int argc,char ** argv,Coord x,Coord y)4017 ActionMarkCrosshair (int argc, char **argv, Coord x, Coord y)
4018 {
4019 char *function = ARG (0);
4020 if (!function || !*function)
4021 {
4022 if (Marked.status)
4023 {
4024 notify_mark_change (false);
4025 Marked.status = false;
4026 notify_mark_change (true);
4027 }
4028 else
4029 {
4030 notify_mark_change (false);
4031 Marked.status = false;
4032 Marked.status = true;
4033 Marked.X = Crosshair.X;
4034 Marked.Y = Crosshair.Y;
4035 notify_mark_change (true);
4036 }
4037 }
4038 else if (GetFunctionID (function) == F_Center)
4039 {
4040 notify_mark_change (false);
4041 Marked.status = true;
4042 Marked.X = Crosshair.X;
4043 Marked.Y = Crosshair.Y;
4044 notify_mark_change (true);
4045 }
4046 return 0;
4047 }
4048
4049 /* --------------------------------------------------------------------------- */
4050
4051 static const char changesize_syntax[] =
4052 N_("ChangeSize(Object, delta)\n"
4053 "ChangeSize(SelectedObjects|Selected, delta)\n"
4054 "ChangeSize(SelectedLines|SelectedPins|SelectedVias, delta)\n"
4055 "ChangeSize(SelectedPads|SelectedTexts|SelectedNames, delta)\n"
4056 "ChangeSize(SelectedElements, delta)");
4057
4058 static const char changesize_help[] = N_("Changes the size of objects.");
4059
4060 /* %start-doc actions ChangeSize
4061
4062 For lines and arcs, this changes the width. For pins and vias, this
4063 changes the overall diameter of the copper annulus. For pads, this
4064 changes the width and, indirectly, the length. For texts and names,
4065 this changes the scaling factor. For elements, this changes the width
4066 of the silk layer lines and arcs for this element.
4067
4068 %end-doc */
4069
4070 static int
ActionChangeSize(int argc,char ** argv,Coord x,Coord y)4071 ActionChangeSize (int argc, char **argv, Coord x, Coord y)
4072 {
4073 char *function = ARG (0);
4074 char *delta = ARG (1);
4075 char *units = ARG (2);
4076 bool absolute; /* indicates if absolute size is given */
4077 Coord value;
4078
4079 if (function && delta)
4080 {
4081 value = GetValue (delta, units, &absolute);
4082 if (value == 0)
4083 value = delta[0] == '-' ? -Settings.increments->size
4084 : Settings.increments->size;
4085 switch (GetFunctionID (function))
4086 {
4087 case F_Object:
4088 {
4089 int type;
4090 void *ptr1, *ptr2, *ptr3;
4091
4092 if ((type =
4093 SearchScreen (Crosshair.X, Crosshair.Y, CHANGESIZE_TYPES,
4094 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4095 if (TEST_FLAG (LOCKFLAG, (PinType *) ptr2))
4096 Message (_("Sorry, the object is locked\n"));
4097 if (ChangeObjectSize (type, ptr1, ptr2, ptr3, value, absolute))
4098 SetChangedFlag (true);
4099 break;
4100 }
4101
4102 case F_SelectedVias:
4103 if (ChangeSelectedSize (VIA_TYPE, value, absolute))
4104 SetChangedFlag (true);
4105 break;
4106
4107 case F_SelectedPins:
4108 if (ChangeSelectedSize (PIN_TYPE, value, absolute))
4109 SetChangedFlag (true);
4110 break;
4111
4112 case F_SelectedPads:
4113 if (ChangeSelectedSize (PAD_TYPE, value, absolute))
4114 SetChangedFlag (true);
4115 break;
4116
4117 case F_SelectedArcs:
4118 if (ChangeSelectedSize (ARC_TYPE, value, absolute))
4119 SetChangedFlag (true);
4120 break;
4121
4122 case F_SelectedLines:
4123 if (ChangeSelectedSize (LINE_TYPE, value, absolute))
4124 SetChangedFlag (true);
4125 break;
4126
4127 case F_SelectedTexts:
4128 if (ChangeSelectedSize (TEXT_TYPE, value, absolute))
4129 SetChangedFlag (true);
4130 break;
4131
4132 case F_SelectedNames:
4133 if (ChangeSelectedSize (ELEMENTNAME_TYPE, value, absolute))
4134 SetChangedFlag (true);
4135 break;
4136
4137 case F_SelectedElements:
4138 if (ChangeSelectedSize (ELEMENT_TYPE, value, absolute))
4139 SetChangedFlag (true);
4140 break;
4141
4142 case F_Selected:
4143 case F_SelectedObjects:
4144 if (ChangeSelectedSize (CHANGESIZE_TYPES, value, absolute))
4145 SetChangedFlag (true);
4146 break;
4147 }
4148 }
4149 return 0;
4150 }
4151
4152 /* --------------------------------------------------------------------------- */
4153
4154 static const char changedrillsize_syntax[] =
4155 N_("ChangeDrillSize(Object, delta)\n"
4156 "ChangeDrillSize(SelectedPins|SelectedVias|Selected|SelectedObjects, delta)");
4157
4158 static const char changedrillsize_help[] =
4159 N_("Changes the drilling hole size of objects.");
4160
4161 /* %start-doc actions ChangeDrillSize
4162
4163 %end-doc */
4164
4165 static int
ActionChange2ndSize(int argc,char ** argv,Coord x,Coord y)4166 ActionChange2ndSize (int argc, char **argv, Coord x, Coord y)
4167 {
4168 char *function = ARG (0);
4169 char *delta = ARG (1);
4170 char *units = ARG (2);
4171 bool absolute;
4172 Coord value;
4173
4174 if (function && delta)
4175 {
4176 value = GetValue (delta, units, &absolute);
4177 switch (GetFunctionID (function))
4178 {
4179 case F_Object:
4180 {
4181 int type;
4182 void *ptr1, *ptr2, *ptr3;
4183
4184 gui->get_coords (_("Select an Object"), &x, &y);
4185 if ((type =
4186 SearchScreen (x, y, CHANGE2NDSIZE_TYPES,
4187 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4188 if (ChangeObject2ndSize
4189 (type, ptr1, ptr2, ptr3, value, absolute, true))
4190 SetChangedFlag (true);
4191 break;
4192 }
4193
4194 case F_SelectedVias:
4195 if (ChangeSelected2ndSize (VIA_TYPE, value, absolute))
4196 SetChangedFlag (true);
4197 break;
4198
4199 case F_SelectedPins:
4200 if (ChangeSelected2ndSize (PIN_TYPE, value, absolute))
4201 SetChangedFlag (true);
4202 break;
4203 case F_Selected:
4204 case F_SelectedObjects:
4205 if (ChangeSelected2ndSize (PIN_TYPES, value, absolute))
4206 SetChangedFlag (true);
4207 break;
4208 }
4209 }
4210 return 0;
4211 }
4212
4213 /* --------------------------------------------------------------------------- */
4214
4215 static const char changeclearsize_syntax[] =
4216 N_("ChangeClearSize(Object, delta)\n"
4217 "ChangeClearSize(SelectedPins|SelectedPads|SelectedVias, delta)\n"
4218 "ChangeClearSize(SelectedLines|SelectedArcs, delta\n"
4219 "ChangeClearSize(Selected|SelectedObjects, delta)");
4220
4221 static const char changeclearsize_help[] =
4222 N_("Changes the clearance size of objects.");
4223
4224 /* %start-doc actions ChangeClearSize
4225
4226 If the solder mask is currently showing, this action changes the
4227 solder mask clearance. If the mask is not showing, this action
4228 changes the polygon clearance.
4229
4230 %end-doc */
4231
4232 static int
ActionChangeClearSize(int argc,char ** argv,Coord x,Coord y)4233 ActionChangeClearSize (int argc, char **argv, Coord x, Coord y)
4234 {
4235 char *function = ARG (0);
4236 char *delta = ARG (1);
4237 char *units = ARG (2);
4238 bool absolute;
4239 Coord value;
4240
4241 if (function && delta)
4242 {
4243 value = 2 * GetValue (delta, units, &absolute);
4244 if ((value == 0) && !absolute)
4245 value = delta[0] == '-' ? -Settings.increments->clear
4246 : Settings.increments->clear;
4247 switch (GetFunctionID (function))
4248 {
4249 case F_Object:
4250 {
4251 int type;
4252 void *ptr1, *ptr2, *ptr3;
4253
4254 gui->get_coords (_("Select an Object"), &x, &y);
4255 if ((type =
4256 SearchScreen (x, y,
4257 CHANGECLEARSIZE_TYPES, &ptr1, &ptr2,
4258 &ptr3)) != NO_TYPE)
4259 if (ChangeObjectClearSize (type, ptr1, ptr2, ptr3, value, absolute))
4260 SetChangedFlag (true);
4261 break;
4262 }
4263 case F_SelectedVias:
4264 if (ChangeSelectedClearSize (VIA_TYPE, value, absolute))
4265 SetChangedFlag (true);
4266 break;
4267 case F_SelectedPads:
4268 if (ChangeSelectedClearSize (PAD_TYPE, value, absolute))
4269 SetChangedFlag (true);
4270 break;
4271 case F_SelectedPins:
4272 if (ChangeSelectedClearSize (PIN_TYPE, value, absolute))
4273 SetChangedFlag (true);
4274 break;
4275 case F_SelectedLines:
4276 if (ChangeSelectedClearSize (LINE_TYPE, value, absolute))
4277 SetChangedFlag (true);
4278 break;
4279 case F_SelectedArcs:
4280 if (ChangeSelectedClearSize (ARC_TYPE, value, absolute))
4281 SetChangedFlag (true);
4282 break;
4283 case F_Selected:
4284 case F_SelectedObjects:
4285 if (ChangeSelectedClearSize (CHANGECLEARSIZE_TYPES, value, absolute))
4286 SetChangedFlag (true);
4287 break;
4288 }
4289 }
4290 return 0;
4291 }
4292
4293 /* --------------------------------------------------------------------------- */
4294
4295 static const char minmaskgap_syntax[] =
4296 N_("MinMaskGap(delta)\n"
4297 "MinMaskGap(Selected, delta)");
4298
4299 static const char minmaskgap_help[] =
4300 N_("Ensures the mask is a minimum distance from pins and pads.");
4301
4302 /* %start-doc actions MinMaskGap
4303
4304 Checks all specified pins and/or pads, and increases the mask if
4305 needed to ensure a minimum distance between the pin or pad edge and
4306 the mask edge.
4307
4308 %end-doc */
4309
4310 static int
ActionMinMaskGap(int argc,char ** argv,Coord x,Coord y)4311 ActionMinMaskGap (int argc, char **argv, Coord x, Coord y)
4312 {
4313 char *function = ARG (0);
4314 char *delta = ARG (1);
4315 char *units = ARG (2);
4316 bool absolute;
4317 Coord value;
4318 Coord thickness;
4319 int flags;
4320
4321 if (!function)
4322 return 1;
4323 if (strcasecmp (function, "Selected") == 0)
4324 flags = SELECTEDFLAG;
4325 else
4326 {
4327 units = delta;
4328 delta = function;
4329 flags = 0;
4330 }
4331 value = 2 * GetValue (delta, units, &absolute);
4332
4333 SaveUndoSerialNumber ();
4334 ELEMENT_LOOP (PCB->Data);
4335 {
4336 PIN_LOOP (element);
4337 {
4338 if (!TEST_FLAGS (flags, pin) || ! pin->Mask) continue;
4339
4340 thickness = pin->DrillingHole;
4341
4342 if (pin->Thickness > thickness) thickness = pin->Thickness;
4343
4344 thickness += value;
4345
4346 if (pin->Mask < thickness)
4347 {
4348 ChangeObjectMaskSize (PIN_TYPE, element, pin, 0, thickness, 1);
4349 RestoreUndoSerialNumber ();
4350 }
4351 }
4352 END_LOOP;
4353 PAD_LOOP (element);
4354 {
4355 if (!TEST_FLAGS (flags, pad) || ! pad->Mask)
4356 continue;
4357 if (pad->Mask < pad->Thickness + value)
4358 {
4359 ChangeObjectMaskSize (PAD_TYPE, element, pad, 0,
4360 pad->Thickness + value, 1);
4361 RestoreUndoSerialNumber ();
4362 }
4363 }
4364 END_LOOP;
4365 }
4366 END_LOOP;
4367 VIA_LOOP (PCB->Data);
4368 {
4369 if (!TEST_FLAGS (flags, via) || ! via->Mask)
4370 continue;
4371
4372 thickness = via->DrillingHole;
4373 if (via->Thickness > thickness)
4374 thickness = via->Thickness;
4375 thickness += value;
4376
4377 if (via->Mask < thickness)
4378 {
4379 ChangeObjectMaskSize (VIA_TYPE, via, 0, 0, thickness, 1);
4380 RestoreUndoSerialNumber ();
4381 }
4382 }
4383 END_LOOP;
4384 RestoreUndoSerialNumber ();
4385 IncrementUndoSerialNumber ();
4386 return 0;
4387 }
4388
4389 /* --------------------------------------------------------------------------- */
4390
4391 static const char mincleargap_syntax[] =
4392 N_("MinClearGap(delta)\n"
4393 "MinClearGap(Selected, delta)");
4394
4395 static const char mincleargap_help[] =
4396 N_("Ensures that polygons are a minimum distance from objects.");
4397
4398 /* %start-doc actions MinClearGap
4399
4400 Checks all specified objects, and increases the polygon clearance if
4401 needed to ensure a minimum distance between their edges and the
4402 polygon edges.
4403
4404 %end-doc */
4405
4406 static int
ActionMinClearGap(int argc,char ** argv,Coord x,Coord y)4407 ActionMinClearGap (int argc, char **argv, Coord x, Coord y)
4408 {
4409 char *function = ARG (0);
4410 char *delta = ARG (1);
4411 char *units = ARG (2);
4412 bool absolute;
4413 Coord value;
4414 int flags;
4415
4416 if (!function)
4417 return 1;
4418 if (strcasecmp (function, "Selected") == 0)
4419 flags = SELECTEDFLAG;
4420 else
4421 {
4422 units = delta;
4423 delta = function;
4424 flags = 0;
4425 }
4426 value = 2 * GetValue (delta, units, &absolute);
4427
4428 SaveUndoSerialNumber ();
4429 ELEMENT_LOOP (PCB->Data);
4430 {
4431 PIN_LOOP (element);
4432 {
4433 if (!TEST_FLAGS (flags, pin))
4434 continue;
4435 if (pin->Clearance < value)
4436 {
4437 ChangeObjectClearSize (PIN_TYPE, element, pin, 0,
4438 value, 1);
4439 RestoreUndoSerialNumber ();
4440 }
4441 }
4442 END_LOOP;
4443 PAD_LOOP (element);
4444 {
4445 if (!TEST_FLAGS (flags, pad))
4446 continue;
4447 if (pad->Clearance < value)
4448 {
4449 ChangeObjectClearSize (PAD_TYPE, element, pad, 0,
4450 value, 1);
4451 RestoreUndoSerialNumber ();
4452 }
4453 }
4454 END_LOOP;
4455 }
4456 END_LOOP;
4457 VIA_LOOP (PCB->Data);
4458 {
4459 if (!TEST_FLAGS (flags, via))
4460 continue;
4461 if (via->Clearance < value)
4462 {
4463 ChangeObjectClearSize (VIA_TYPE, via, 0, 0, value, 1);
4464 RestoreUndoSerialNumber ();
4465 }
4466 }
4467 END_LOOP;
4468 ALLLINE_LOOP (PCB->Data);
4469 {
4470 if (!TEST_FLAGS (flags, line))
4471 continue;
4472 if (line->Clearance < value)
4473 {
4474 ChangeObjectClearSize (LINE_TYPE, layer, line, 0, value, 1);
4475 RestoreUndoSerialNumber ();
4476 }
4477 }
4478 ENDALL_LOOP;
4479 ALLARC_LOOP (PCB->Data);
4480 {
4481 if (!TEST_FLAGS (flags, arc))
4482 continue;
4483 if (arc->Clearance < value)
4484 {
4485 ChangeObjectClearSize (ARC_TYPE, layer, arc, 0, value, 1);
4486 RestoreUndoSerialNumber ();
4487 }
4488 }
4489 ENDALL_LOOP;
4490 RestoreUndoSerialNumber ();
4491 IncrementUndoSerialNumber ();
4492 return 0;
4493 }
4494
4495 /* --------------------------------------------------------------------------- */
4496
4497 static const char changepinname_syntax[] =
4498 N_("ChangePinName(ElementName,PinNumber,PinName)");
4499
4500 static const char changepinname_help[] =
4501 N_("Sets the name of a specific pin on a specific element.");
4502
4503 /* %start-doc actions ChangePinName
4504
4505 This can be especially useful for annotating pin names from a
4506 schematic to the layout without requiring knowledge of the pcb file
4507 format.
4508
4509 @example
4510 ChangePinName(U3, 7, VCC)
4511 @end example
4512
4513 %end-doc */
4514
4515 static int
ActionChangePinName(int argc,char ** argv,Coord x,Coord y)4516 ActionChangePinName (int argc, char **argv, Coord x, Coord y)
4517 {
4518 int changed = 0;
4519 char *refdes, *pinnum, *pinname;
4520
4521 if (argc != 3)
4522 {
4523 AFAIL (changepinname);
4524 }
4525
4526 refdes = argv[0];
4527 pinnum = argv[1];
4528 pinname = argv[2];
4529
4530 ELEMENT_LOOP (PCB->Data);
4531 {
4532 if (NSTRCMP (refdes, NAMEONPCB_NAME (element)) == 0)
4533 {
4534 PIN_LOOP (element);
4535 {
4536 if (NSTRCMP (pinnum, pin->Number) == 0)
4537 {
4538 AddObjectToChangeNameUndoList (PIN_TYPE, NULL, NULL,
4539 pin, pin->Name);
4540 /*
4541 * Note: we can't free() pin->Name first because
4542 * it is used in the undo list
4543 */
4544 pin->Name = strdup (pinname);
4545 SetChangedFlag (true);
4546 changed = 1;
4547 }
4548 }
4549 END_LOOP;
4550
4551 PAD_LOOP (element);
4552 {
4553 if (NSTRCMP (pinnum, pad->Number) == 0)
4554 {
4555 AddObjectToChangeNameUndoList (PAD_TYPE, NULL, NULL,
4556 pad, pad->Name);
4557 /*
4558 * Note: we can't free() pad->Name first because
4559 * it is used in the undo list
4560 */
4561 pad->Name = strdup (pinname);
4562 SetChangedFlag (true);
4563 changed = 1;
4564 }
4565 }
4566 END_LOOP;
4567 }
4568 }
4569 END_LOOP;
4570 /*
4571 * done with our action so increment the undo # if we actually
4572 * changed anything
4573 */
4574 if (changed)
4575 {
4576 if (defer_updates)
4577 defer_needs_update = 1;
4578 else
4579 {
4580 IncrementUndoSerialNumber ();
4581 gui->invalidate_all ();
4582 }
4583 }
4584
4585 return 0;
4586 }
4587
4588 /* --------------------------------------------------------------------------- */
4589
4590 static const char changename_syntax[] =
4591 N_("ChangeName(Object)\n"
4592 "ChangeName(Layout|Layer)");
4593
4594 static const char changename_help[] = N_("Sets the name of objects.");
4595
4596 /* %start-doc actions ChangeName
4597
4598 @table @code
4599
4600 @item Object
4601 Changes the name of the element under the cursor.
4602
4603 @item Layout
4604 Changes the name of the layout. This is printed on the fab drawings.
4605
4606 @item Layer
4607 Changes the name of the currently active layer.
4608
4609 @end table
4610
4611 %end-doc */
4612
4613 int
ActionChangeName(int argc,char ** argv,Coord x,Coord y)4614 ActionChangeName (int argc, char **argv, Coord x, Coord y)
4615 {
4616 char *function = ARG (0);
4617 char *name;
4618
4619 if (function)
4620 {
4621 switch (GetFunctionID (function))
4622 {
4623 /* change the name of an object */
4624 case F_Object:
4625 {
4626 int type;
4627 void *ptr1, *ptr2, *ptr3;
4628
4629 gui->get_coords (_("Select an Object"), &x, &y);
4630 if ((type =
4631 SearchScreen (x, y, CHANGENAME_TYPES,
4632 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4633 {
4634 SaveUndoSerialNumber ();
4635 if (QueryInputAndChangeObjectName (type, ptr1, ptr2, ptr3))
4636 {
4637 SetChangedFlag (true);
4638 if (type == ELEMENT_TYPE)
4639 {
4640 RubberbandType *ptr;
4641 int i;
4642
4643 RestoreUndoSerialNumber ();
4644 Crosshair.AttachedObject.RubberbandN = 0;
4645 LookupRatLines (type, ptr1, ptr2, ptr3);
4646 ptr = Crosshair.AttachedObject.Rubberband;
4647 for (i = 0; i < Crosshair.AttachedObject.RubberbandN;
4648 i++, ptr++)
4649 {
4650 if (PCB->RatOn)
4651 EraseRat ((RatType *) ptr->Line);
4652 MoveObjectToRemoveUndoList (RATLINE_TYPE,
4653 ptr->Line, ptr->Line,
4654 ptr->Line);
4655 }
4656 IncrementUndoSerialNumber ();
4657 Draw ();
4658 }
4659 }
4660 }
4661 break;
4662 }
4663
4664 /* change the layout's name */
4665 case F_Layout:
4666 name =
4667 gui->prompt_for (_("Enter the layout name:"), EMPTY (PCB->Name));
4668 /* NB: ChangeLayoutName takes ownership of the passed memory */
4669 if (name && ChangeLayoutName (name))
4670 SetChangedFlag (true);
4671 break;
4672
4673 /* change the name of the active layer */
4674 case F_Layer:
4675 name = gui->prompt_for (_("Enter the layer name:"),
4676 EMPTY (CURRENT->Name));
4677 /* NB: ChangeLayerName takes ownership of the passed memory */
4678 if (name && ChangeLayerName (CURRENT, name))
4679 SetChangedFlag (true);
4680 break;
4681 }
4682 }
4683 return 0;
4684 }
4685
4686
4687 /* --------------------------------------------------------------------------- */
4688
4689 static const char morphpolygon_syntax[] = N_("MorphPolygon(Object|Selected)");
4690
4691 static const char morphpolygon_help[] =
4692 N_("Converts dead polygon islands into separate polygons.");
4693
4694 /* %start-doc actions MorphPolygon
4695
4696 If a polygon is divided into unconnected "islands", you can use
4697 this command to convert the otherwise disappeared islands into
4698 separate polygons. Be sure the cursor is over a portion of the
4699 polygon that remains visible. Very small islands that may flake
4700 off are automatically deleted.
4701
4702 %end-doc */
4703
4704 static int
ActionMorphPolygon(int argc,char ** argv,Coord x,Coord y)4705 ActionMorphPolygon (int argc, char **argv, Coord x, Coord y)
4706 {
4707 char *function = ARG (0);
4708 if (function)
4709 {
4710 switch (GetFunctionID (function))
4711 {
4712 case F_Object:
4713 {
4714 int type;
4715 void *ptr1, *ptr2, *ptr3;
4716
4717 gui->get_coords (_("Select an Object"), &x, &y);
4718 if ((type = SearchScreen (x, y, POLYGON_TYPE,
4719 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4720 {
4721 MorphPolygon ((LayerType *) ptr1, (PolygonType *) ptr3);
4722 Draw ();
4723 IncrementUndoSerialNumber ();
4724 }
4725 break;
4726 }
4727 case F_Selected:
4728 case F_SelectedObjects:
4729 ALLPOLYGON_LOOP (PCB->Data);
4730 {
4731 if (TEST_FLAG (SELECTEDFLAG, polygon))
4732 MorphPolygon (layer, polygon);
4733 }
4734 ENDALL_LOOP;
4735 Draw ();
4736 IncrementUndoSerialNumber ();
4737 break;
4738 }
4739 }
4740 return 0;
4741 }
4742
4743 /* --------------------------------------------------------------------------- */
4744
4745 static const char togglehidename_syntax[] =
4746 N_("ToggleHideName(Object|SelectedElements)");
4747
4748 static const char togglehidename_help[] =
4749 N_("Toggles the visibility of element names.");
4750
4751 /* %start-doc actions ToggleHideName
4752
4753 If names are hidden you won't see them on the screen and they will not
4754 appear on the silk layer when you print the layout.
4755
4756 %end-doc */
4757
4758 static int
ActionToggleHideName(int argc,char ** argv,Coord x,Coord y)4759 ActionToggleHideName (int argc, char **argv, Coord x, Coord y)
4760 {
4761 char *function = ARG (0);
4762 if (function && PCB->ElementOn)
4763 {
4764 switch (GetFunctionID (function))
4765 {
4766 case F_Object:
4767 {
4768 int type;
4769 void *ptr1, *ptr2, *ptr3;
4770
4771 gui->get_coords (_("Select an Object"), &x, &y);
4772 if ((type = SearchScreen (x, y, ELEMENT_TYPE,
4773 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4774 {
4775 AddObjectToFlagUndoList (type, ptr1, ptr2, ptr3);
4776 EraseElementName ((ElementType *) ptr2);
4777 TOGGLE_FLAG (HIDENAMEFLAG, (ElementType *) ptr2);
4778 DrawElementName ((ElementType *) ptr2);
4779 Draw ();
4780 IncrementUndoSerialNumber ();
4781 }
4782 break;
4783 }
4784 case F_SelectedElements:
4785 case F_Selected:
4786 {
4787 bool changed = false;
4788 ELEMENT_LOOP (PCB->Data);
4789 {
4790 if ((TEST_FLAG (SELECTEDFLAG, element) ||
4791 TEST_FLAG (SELECTEDFLAG,
4792 &NAMEONPCB_TEXT (element)))
4793 && (FRONT (element) || PCB->InvisibleObjectsOn))
4794 {
4795 AddObjectToFlagUndoList (ELEMENT_TYPE, element,
4796 element, element);
4797 EraseElementName (element);
4798 TOGGLE_FLAG (HIDENAMEFLAG, element);
4799 DrawElementName (element);
4800 changed = true;
4801 }
4802 }
4803 END_LOOP;
4804 if (changed)
4805 {
4806 Draw ();
4807 IncrementUndoSerialNumber ();
4808 }
4809 }
4810 }
4811 }
4812 return 0;
4813 }
4814
4815 /* --------------------------------------------------------------------------- */
4816
4817 static const char changejoin_syntax[] =
4818 N_("ChangeJoin(ToggleObject|SelectedLines|SelectedArcs|Selected)");
4819
4820 static const char changejoin_help[] =
4821 N_("Changes the join (clearance through polygons) of objects.");
4822
4823 /* %start-doc actions ChangeJoin
4824
4825 The join flag determines whether a line or arc, drawn to intersect a
4826 polygon, electrically connects to the polygon or not. When joined,
4827 the line/arc is simply drawn over the polygon, making an electrical
4828 connection. When not joined, a gap is drawn between the line and the
4829 polygon, insulating them from each other.
4830
4831 %end-doc */
4832
4833 static int
ActionChangeJoin(int argc,char ** argv,Coord x,Coord y)4834 ActionChangeJoin (int argc, char **argv, Coord x, Coord y)
4835 {
4836 char *function = ARG (0);
4837 if (function)
4838 {
4839 switch (GetFunctionID (function))
4840 {
4841 case F_ToggleObject:
4842 case F_Object:
4843 {
4844 int type;
4845 void *ptr1, *ptr2, *ptr3;
4846
4847 gui->get_coords (_("Select an Object"), &x, &y);
4848 if ((type =
4849 SearchScreen (x, y, CHANGEJOIN_TYPES,
4850 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4851 if (ChangeObjectJoin (type, ptr1, ptr2, ptr3))
4852 SetChangedFlag (true);
4853 break;
4854 }
4855
4856 case F_SelectedLines:
4857 if (ChangeSelectedJoin (LINE_TYPE))
4858 SetChangedFlag (true);
4859 break;
4860
4861 case F_SelectedArcs:
4862 if (ChangeSelectedJoin (ARC_TYPE))
4863 SetChangedFlag (true);
4864 break;
4865
4866 case F_Selected:
4867 case F_SelectedObjects:
4868 if (ChangeSelectedJoin (CHANGEJOIN_TYPES))
4869 SetChangedFlag (true);
4870 break;
4871 }
4872 }
4873 return 0;
4874 }
4875
4876 /* --------------------------------------------------------------------------- */
4877
4878 static const char changesquare_syntax[] =
4879 N_("ChangeSquare(ToggleObject)\n"
4880 "ChangeSquare(SelectedElements|SelectedPins)\n"
4881 "ChangeSquare(Selected|SelectedObjects)");
4882
4883 static const char changesquare_help[] =
4884 N_("Changes the square flag of pins and pads.");
4885
4886 /* %start-doc actions ChangeSquare
4887
4888 Note that @code{Pins} means both pins and pads.
4889
4890 @pinshapes
4891
4892 %end-doc */
4893
4894 static int
ActionChangeSquare(int argc,char ** argv,Coord x,Coord y)4895 ActionChangeSquare (int argc, char **argv, Coord x, Coord y)
4896 {
4897 char *function = ARG (0);
4898 if (function)
4899 {
4900 switch (GetFunctionID (function))
4901 {
4902 case F_ToggleObject:
4903 case F_Object:
4904 {
4905 int type;
4906 void *ptr1, *ptr2, *ptr3;
4907
4908 gui->get_coords (_("Select an Object"), &x, &y);
4909 if ((type =
4910 SearchScreen (x, y, CHANGESQUARE_TYPES,
4911 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4912 if (ChangeObjectSquare (type, ptr1, ptr2, ptr3))
4913 SetChangedFlag (true);
4914 break;
4915 }
4916
4917 case F_SelectedElements:
4918 if (ChangeSelectedSquare (ELEMENT_TYPE))
4919 SetChangedFlag (true);
4920 break;
4921
4922 case F_SelectedPins:
4923 if (ChangeSelectedSquare (PIN_TYPE | PAD_TYPE))
4924 SetChangedFlag (true);
4925 break;
4926
4927 case F_Selected:
4928 case F_SelectedObjects:
4929 if (ChangeSelectedSquare (PIN_TYPE | PAD_TYPE))
4930 SetChangedFlag (true);
4931 break;
4932 }
4933 }
4934 return 0;
4935 }
4936
4937 /* --------------------------------------------------------------------------- */
4938
4939 static const char setsquare_syntax[] =
4940 N_("SetSquare(ToggleObject|SelectedElements|SelectedPins)");
4941
4942 static const char setsquare_help[] = N_("sets the square-flag of objects.");
4943
4944 /* %start-doc actions SetSquare
4945
4946 Note that @code{Pins} means pins and pads.
4947
4948 @pinshapes
4949
4950 %end-doc */
4951
4952 static int
ActionSetSquare(int argc,char ** argv,Coord x,Coord y)4953 ActionSetSquare (int argc, char **argv, Coord x, Coord y)
4954 {
4955 char *function = ARG (0);
4956 if (function && *function)
4957 {
4958 switch (GetFunctionID (function))
4959 {
4960 case F_ToggleObject:
4961 case F_Object:
4962 {
4963 int type;
4964 void *ptr1, *ptr2, *ptr3;
4965
4966 gui->get_coords (_("Select an Object"), &x, &y);
4967 if ((type =
4968 SearchScreen (x, y, CHANGESQUARE_TYPES,
4969 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
4970 if (SetObjectSquare (type, ptr1, ptr2, ptr3))
4971 SetChangedFlag (true);
4972 break;
4973 }
4974
4975 case F_SelectedElements:
4976 if (SetSelectedSquare (ELEMENT_TYPE))
4977 SetChangedFlag (true);
4978 break;
4979
4980 case F_SelectedPins:
4981 if (SetSelectedSquare (PIN_TYPE | PAD_TYPE))
4982 SetChangedFlag (true);
4983 break;
4984
4985 case F_Selected:
4986 case F_SelectedObjects:
4987 if (SetSelectedSquare (PIN_TYPE | PAD_TYPE))
4988 SetChangedFlag (true);
4989 break;
4990 }
4991 }
4992 return 0;
4993 }
4994
4995 /* --------------------------------------------------------------------------- */
4996
4997 static const char clearsquare_syntax[] =
4998 N_("ClearSquare(ToggleObject|SelectedElements|SelectedPins)");
4999
5000 static const char clearsquare_help[] =
5001 N_("Clears the square-flag of pins and pads.");
5002
5003 /* %start-doc actions ClearSquare
5004
5005 Note that @code{Pins} means pins and pads.
5006
5007 @pinshapes
5008
5009 %end-doc */
5010
5011 static int
ActionClearSquare(int argc,char ** argv,Coord x,Coord y)5012 ActionClearSquare (int argc, char **argv, Coord x, Coord y)
5013 {
5014 char *function = ARG (0);
5015 if (function && *function)
5016 {
5017 switch (GetFunctionID (function))
5018 {
5019 case F_ToggleObject:
5020 case F_Object:
5021 {
5022 int type;
5023 void *ptr1, *ptr2, *ptr3;
5024
5025 gui->get_coords (_("Select an Object"), &x, &y);
5026 if ((type =
5027 SearchScreen (x, y, CHANGESQUARE_TYPES,
5028 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5029 if (ClrObjectSquare (type, ptr1, ptr2, ptr3))
5030 SetChangedFlag (true);
5031 break;
5032 }
5033
5034 case F_SelectedElements:
5035 if (ClrSelectedSquare (ELEMENT_TYPE))
5036 SetChangedFlag (true);
5037 break;
5038
5039 case F_SelectedPins:
5040 if (ClrSelectedSquare (PIN_TYPE | PAD_TYPE))
5041 SetChangedFlag (true);
5042 break;
5043
5044 case F_Selected:
5045 case F_SelectedObjects:
5046 if (ClrSelectedSquare (PIN_TYPE | PAD_TYPE))
5047 SetChangedFlag (true);
5048 break;
5049 }
5050 }
5051 return 0;
5052 }
5053
5054 /* --------------------------------------------------------------------------- */
5055
5056 static const char changeoctagon_syntax[] =
5057 N_("ChangeOctagon(Object|ToggleObject|SelectedObjects|Selected)\n"
5058 "ChangeOctagon(SelectedElements|SelectedPins|SelectedVias)");
5059
5060 static const char changeoctagon_help[] =
5061 N_("Changes the octagon-flag of pins and vias.");
5062
5063 /* %start-doc actions ChangeOctagon
5064
5065 @pinshapes
5066
5067 %end-doc */
5068
5069 static int
ActionChangeOctagon(int argc,char ** argv,Coord x,Coord y)5070 ActionChangeOctagon (int argc, char **argv, Coord x, Coord y)
5071 {
5072 char *function = ARG (0);
5073 if (function)
5074 {
5075 switch (GetFunctionID (function))
5076 {
5077 case F_ToggleObject:
5078 case F_Object:
5079 {
5080 int type;
5081 void *ptr1, *ptr2, *ptr3;
5082
5083 gui->get_coords (_("Select an Object"), &x, &y);
5084 if ((type =
5085 SearchScreen (x, y, CHANGEOCTAGON_TYPES,
5086 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5087 if (ChangeObjectOctagon (type, ptr1, ptr2, ptr3))
5088 SetChangedFlag (true);
5089 break;
5090 }
5091
5092 case F_SelectedElements:
5093 if (ChangeSelectedOctagon (ELEMENT_TYPE))
5094 SetChangedFlag (true);
5095 break;
5096
5097 case F_SelectedPins:
5098 if (ChangeSelectedOctagon (PIN_TYPE))
5099 SetChangedFlag (true);
5100 break;
5101
5102 case F_SelectedVias:
5103 if (ChangeSelectedOctagon (VIA_TYPE))
5104 SetChangedFlag (true);
5105 break;
5106
5107 case F_Selected:
5108 case F_SelectedObjects:
5109 if (ChangeSelectedOctagon (PIN_TYPES))
5110 SetChangedFlag (true);
5111 break;
5112 }
5113 }
5114 return 0;
5115 }
5116
5117 /* --------------------------------------------------------------------------- */
5118
5119 static const char setoctagon_syntax[] =
5120 N_("SetOctagon(Object|ToggleObject|SelectedElements|Selected)");
5121
5122 static const char setoctagon_help[] = N_("Sets the octagon-flag of objects.");
5123
5124 /* %start-doc actions SetOctagon
5125
5126 @pinshapes
5127
5128 %end-doc */
5129
5130 static int
ActionSetOctagon(int argc,char ** argv,Coord x,Coord y)5131 ActionSetOctagon (int argc, char **argv, Coord x, Coord y)
5132 {
5133 char *function = ARG (0);
5134 if (function)
5135 {
5136 switch (GetFunctionID (function))
5137 {
5138 case F_ToggleObject:
5139 case F_Object:
5140 {
5141 int type;
5142 void *ptr1, *ptr2, *ptr3;
5143
5144 gui->get_coords (_("Select an Object"), &x, &y);
5145 if ((type =
5146 SearchScreen (x, y, CHANGEOCTAGON_TYPES,
5147 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5148 if (SetObjectOctagon (type, ptr1, ptr2, ptr3))
5149 SetChangedFlag (true);
5150 break;
5151 }
5152
5153 case F_SelectedElements:
5154 if (SetSelectedOctagon (ELEMENT_TYPE))
5155 SetChangedFlag (true);
5156 break;
5157
5158 case F_SelectedPins:
5159 if (SetSelectedOctagon (PIN_TYPE))
5160 SetChangedFlag (true);
5161 break;
5162
5163 case F_SelectedVias:
5164 if (SetSelectedOctagon (VIA_TYPE))
5165 SetChangedFlag (true);
5166 break;
5167
5168 case F_Selected:
5169 case F_SelectedObjects:
5170 if (SetSelectedOctagon (PIN_TYPES))
5171 SetChangedFlag (true);
5172 break;
5173 }
5174 }
5175 return 0;
5176 }
5177
5178 /* --------------------------------------------------------------------------- */
5179
5180 static const char clearoctagon_syntax[] =
5181 N_("ClearOctagon(ToggleObject|Object|SelectedObjects|Selected)\n"
5182 "ClearOctagon(SelectedElements|SelectedPins|SelectedVias)");
5183
5184 static const char clearoctagon_help[] =
5185 N_("Clears the octagon-flag of pins and vias.");
5186
5187 /* %start-doc actions ClearOctagon
5188
5189 @pinshapes
5190
5191 %end-doc */
5192
5193 static int
ActionClearOctagon(int argc,char ** argv,Coord x,Coord y)5194 ActionClearOctagon (int argc, char **argv, Coord x, Coord y)
5195 {
5196 char *function = ARG (0);
5197 if (function)
5198 {
5199 switch (GetFunctionID (function))
5200 {
5201 case F_ToggleObject:
5202 case F_Object:
5203 {
5204 int type;
5205 void *ptr1, *ptr2, *ptr3;
5206
5207 gui->get_coords (_("Select an Object"), &x, &y);
5208 if ((type =
5209 SearchScreen (Crosshair.X, Crosshair.Y, CHANGEOCTAGON_TYPES,
5210 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
5211 if (ClrObjectOctagon (type, ptr1, ptr2, ptr3))
5212 SetChangedFlag (true);
5213 break;
5214 }
5215
5216 case F_SelectedElements:
5217 if (ClrSelectedOctagon (ELEMENT_TYPE))
5218 SetChangedFlag (true);
5219 break;
5220
5221 case F_SelectedPins:
5222 if (ClrSelectedOctagon (PIN_TYPE))
5223 SetChangedFlag (true);
5224 break;
5225
5226 case F_SelectedVias:
5227 if (ClrSelectedOctagon (VIA_TYPE))
5228 SetChangedFlag (true);
5229 break;
5230
5231 case F_Selected:
5232 case F_SelectedObjects:
5233 if (ClrSelectedOctagon (PIN_TYPES))
5234 SetChangedFlag (true);
5235 break;
5236 }
5237 }
5238 return 0;
5239 }
5240
5241 /* --------------------------------------------------------------------------- */
5242
5243 static const char changehold_syntax[] =
5244 N_("ChangeHole(ToggleObject|Object|SelectedVias|Selected)");
5245
5246 static const char changehold_help[] = N_("Changes the hole flag of objects.");
5247
5248 /* %start-doc actions ChangeHole
5249
5250 The "hole flag" of a via determines whether the via is a
5251 plated-through hole (not set), or an unplated hole (set).
5252
5253 %end-doc */
5254
5255 static int
ActionChangeHole(int argc,char ** argv,Coord x,Coord y)5256 ActionChangeHole (int argc, char **argv, Coord x, Coord y)
5257 {
5258 char *function = ARG (0);
5259 if (function)
5260 {
5261 switch (GetFunctionID (function))
5262 {
5263 case F_ToggleObject:
5264 case F_Object:
5265 {
5266 int type;
5267 void *ptr1, *ptr2, *ptr3;
5268
5269 gui->get_coords (_("Select an Object"), &x, &y);
5270 if ((type = SearchScreen (x, y, VIA_TYPE,
5271 &ptr1, &ptr2, &ptr3)) != NO_TYPE
5272 && ChangeHole ((PinType *) ptr3))
5273 IncrementUndoSerialNumber ();
5274 break;
5275 }
5276
5277 case F_SelectedVias:
5278 case F_Selected:
5279 if (ChangeSelectedHole ())
5280 SetChangedFlag (true);
5281 break;
5282 }
5283 }
5284 return 0;
5285 }
5286
5287 /* --------------------------------------------------------------------------- */
5288
5289 static const char changepaste_syntax[] =
5290 N_("ChangePaste(ToggleObject|Object|SelectedPads|Selected)");
5291
5292 static const char changepaste_help[] =
5293 N_("Changes the no paste flag of objects.");
5294
5295 /* %start-doc actions ChangePaste
5296
5297 The "no paste flag" of a pad determines whether the solderpaste
5298 stencil will have an opening for the pad (no set) or if there wil be
5299 no solderpaste on the pad (set). This is used for things such as
5300 fiducial pads.
5301
5302 %end-doc */
5303
5304 static int
ActionChangePaste(int argc,char ** argv,Coord x,Coord y)5305 ActionChangePaste (int argc, char **argv, Coord x, Coord y)
5306 {
5307 char *function = ARG (0);
5308 if (function)
5309 {
5310 switch (GetFunctionID (function))
5311 {
5312 case F_ToggleObject:
5313 case F_Object:
5314 {
5315 int type;
5316 void *ptr1, *ptr2, *ptr3;
5317
5318 gui->get_coords (_("Select an Object"), &x, &y);
5319 if ((type = SearchScreen (x, y, PAD_TYPE,
5320 &ptr1, &ptr2, &ptr3)) != NO_TYPE
5321 && ChangePaste ((PadType *) ptr3))
5322 IncrementUndoSerialNumber ();
5323 break;
5324 }
5325
5326 case F_SelectedPads:
5327 case F_Selected:
5328 if (ChangeSelectedPaste ())
5329 SetChangedFlag (true);
5330 break;
5331 }
5332 }
5333 return 0;
5334 }
5335
5336 /* --------------------------------------------------------------------------- */
5337
5338 static const char select_syntax[] =
5339 N_("Select(Object|ToggleObject)\n"
5340 "Select(All|Block|Connection|BuriedVias)\n"
5341 "Select(ElementByName|ObjectByName|PadByName|PinByName)\n"
5342 "Select(ElementByName|ObjectByName|PadByName|PinByName, Name)\n"
5343 "Select(TextByName|ViaByName|NetByName)\n"
5344 "Select(TextByName|ViaByName|NetByName, Name)\n"
5345 "Select(Convert)");
5346
5347 static const char select_help[] = N_("Toggles or sets the selection.");
5348
5349 /* %start-doc actions Select
5350
5351 @table @code
5352
5353 @item ElementByName
5354 @item ObjectByName
5355 @item PadByName
5356 @item PinByName
5357 @item TextByName
5358 @item ViaByName
5359 @item NetByName
5360
5361 These all rely on having a regular expression parser built into
5362 @code{pcb}. If the name is not specified then the user is prompted
5363 for a pattern, and all objects that match the pattern and are of the
5364 type specified are selected.
5365
5366 @item Object
5367 @item ToggleObject
5368 Selects the object under the cursor.
5369
5370 @item Block
5371 Selects all objects in a rectangle indicated by the cursor.
5372
5373 @item All
5374 Selects all objects on the board.
5375
5376 @item Found
5377 Selects all connections with the ``found'' flag set.
5378
5379 @item Connection
5380 Selects all connections with the ``connected'' flag set.
5381
5382 @item Connection
5383 Selects all blind and buried vias.
5384
5385 @item Convert
5386 Converts the selected objects to an element. This uses the highest
5387 numbered paste buffer.
5388
5389 @end table
5390
5391 %end-doc */
5392
5393 static int
ActionSelect(int argc,char ** argv,Coord x,Coord y)5394 ActionSelect (int argc, char **argv, Coord x, Coord y)
5395 {
5396 char *function = ARG (0);
5397 if (function)
5398 {
5399 switch (GetFunctionID (function))
5400 {
5401 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5402 int type;
5403 /* select objects by their names */
5404 case F_ElementByName:
5405 type = ELEMENT_TYPE;
5406 goto commonByName;
5407 case F_ObjectByName:
5408 type = ALL_TYPES;
5409 goto commonByName;
5410 case F_PadByName:
5411 type = PAD_TYPE;
5412 goto commonByName;
5413 case F_PinByName:
5414 type = PIN_TYPE;
5415 goto commonByName;
5416 case F_TextByName:
5417 type = TEXT_TYPE;
5418 goto commonByName;
5419 case F_ViaByName:
5420 type = VIA_TYPE;
5421 goto commonByName;
5422 case F_NetByName:
5423 type = NET_TYPE;
5424 goto commonByName;
5425
5426 commonByName:
5427 {
5428 char *pattern = ARG (1);
5429
5430 if (pattern
5431 || (pattern =
5432 gui->prompt_for (_("Enter pattern:"), "")) != NULL)
5433 {
5434 if (SelectObjectByName (type, pattern, true))
5435 SetChangedFlag (true);
5436 if (ARG (1) == NULL)
5437 free (pattern);
5438 }
5439 break;
5440 }
5441 #endif /* defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP) */
5442
5443 /* select a single object */
5444 case F_ToggleObject:
5445 case F_Object:
5446 if (SelectObject ())
5447 SetChangedFlag (true);
5448 break;
5449
5450 /* all objects in block */
5451 case F_Block:
5452 {
5453 BoxType box;
5454
5455 box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
5456 Crosshair.AttachedBox.Point2.X);
5457 box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
5458 Crosshair.AttachedBox.Point2.Y);
5459 box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
5460 Crosshair.AttachedBox.Point2.X);
5461 box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
5462 Crosshair.AttachedBox.Point2.Y);
5463 notify_crosshair_change (false);
5464 NotifyBlock ();
5465 if (Crosshair.AttachedBox.State == STATE_THIRD &&
5466 SelectBlock (&box, true))
5467 {
5468 SetChangedFlag (true);
5469 Crosshair.AttachedBox.State = STATE_FIRST;
5470 }
5471 notify_crosshair_change (true);
5472 break;
5473 }
5474
5475 /* select all visible objects */
5476 case F_All:
5477 {
5478 BoxType box;
5479
5480 box.X1 = -MAX_COORD;
5481 box.Y1 = -MAX_COORD;
5482 box.X2 = MAX_COORD;
5483 box.Y2 = MAX_COORD;
5484 if (SelectBlock (&box, true))
5485 SetChangedFlag (true);
5486 break;
5487 }
5488
5489 /* all logical connections */
5490 case F_Found:
5491 if (SelectByFlag (FOUNDFLAG, true))
5492 {
5493 Draw ();
5494 IncrementUndoSerialNumber ();
5495 SetChangedFlag (true);
5496 }
5497 break;
5498
5499 /* all physical connections */
5500 case F_Connection:
5501 if (SelectByFlag (CONNECTEDFLAG, true))
5502 {
5503 Draw ();
5504 IncrementUndoSerialNumber ();
5505 SetChangedFlag (true);
5506 }
5507 break;
5508
5509 case F_BuriedVias:
5510 if (SelectBuriedVias (true))
5511 {
5512 Draw ();
5513 IncrementUndoSerialNumber ();
5514 SetChangedFlag (true);
5515 }
5516 break;
5517
5518 case F_Convert:
5519 {
5520 Coord x, y;
5521 Note.Buffer = Settings.BufferNumber;
5522 SetBufferNumber (MAX_BUFFER - 1);
5523 ClearBuffer (PASTEBUFFER);
5524 gui->get_coords (_("Select the Element's Mark Location"), &x, &y);
5525 x = GridFit (x, PCB->Grid, PCB->GridOffsetX);
5526 y = GridFit (y, PCB->Grid, PCB->GridOffsetY);
5527 AddSelectedToBuffer (PASTEBUFFER, x, y, true);
5528 SaveUndoSerialNumber ();
5529 RemoveSelected ();
5530 ConvertBufferToElement (PASTEBUFFER);
5531 RestoreUndoSerialNumber ();
5532 CopyPastebufferToLayout (x, y);
5533 SetBufferNumber (Note.Buffer);
5534 }
5535 break;
5536
5537 default:
5538 AFAIL (select);
5539 break;
5540 }
5541 }
5542 return 0;
5543 }
5544
5545 /* FLAG(have_regex,FlagHaveRegex,0) */
5546 int
FlagHaveRegex(int parm)5547 FlagHaveRegex (int parm)
5548 {
5549 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5550 return 1;
5551 #else
5552 return 0;
5553 #endif
5554 }
5555
5556 /* --------------------------------------------------------------------------- */
5557
5558 static const char unselect_syntax[] =
5559 N_("Unselect(All|Block|Connection)\n"
5560 "Unselect(ElementByName|ObjectByName|PadByName|PinByName)\n"
5561 "Unselect(ElementByName|ObjectByName|PadByName|PinByName, Name)\n"
5562 "Unselect(TextByName|ViaByName)\n"
5563 "Unselect(TextByName|ViaByName, Name)\n");
5564
5565 static const char unselect_help[] =
5566 N_("Unselects the object at the pointer location or the specified objects.");
5567
5568 /* %start-doc actions Unselect
5569
5570 @table @code
5571
5572 @item All
5573 Unselect all objects.
5574
5575 @item Block
5576 Unselect all objects in a rectangle given by the cursor.
5577
5578 @item Connection
5579 Unselect all connections with the ``found'' flag set.
5580
5581 @item ElementByName
5582 @item ObjectByName
5583 @item PadByName
5584 @item PinByName
5585 @item TextByName
5586 @item ViaByName
5587
5588 These all rely on having a regular expression parser built into
5589 @code{pcb}. If the name is not specified then the user is prompted
5590 for a pattern, and all objects that match the pattern and are of the
5591 type specified are unselected.
5592
5593
5594 @end table
5595
5596 %end-doc */
5597
5598 static int
ActionUnselect(int argc,char ** argv,Coord x,Coord y)5599 ActionUnselect (int argc, char **argv, Coord x, Coord y)
5600 {
5601 char *function = ARG (0);
5602 if (function)
5603 {
5604 switch (GetFunctionID (function))
5605 {
5606 #if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
5607 int type;
5608 /* select objects by their names */
5609 case F_ElementByName:
5610 type = ELEMENT_TYPE;
5611 goto commonByName;
5612 case F_ObjectByName:
5613 type = ALL_TYPES;
5614 goto commonByName;
5615 case F_PadByName:
5616 type = PAD_TYPE;
5617 goto commonByName;
5618 case F_PinByName:
5619 type = PIN_TYPE;
5620 goto commonByName;
5621 case F_TextByName:
5622 type = TEXT_TYPE;
5623 goto commonByName;
5624 case F_ViaByName:
5625 type = VIA_TYPE;
5626 goto commonByName;
5627 case F_NetByName:
5628 type = NET_TYPE;
5629 goto commonByName;
5630
5631 commonByName:
5632 {
5633 char *pattern = ARG (1);
5634
5635 if (pattern
5636 || (pattern =
5637 gui->prompt_for (_("Enter pattern:"), "")) != NULL)
5638 {
5639 if (SelectObjectByName (type, pattern, false))
5640 SetChangedFlag (true);
5641 if (ARG (1) == NULL)
5642 free (pattern);
5643 }
5644 break;
5645 }
5646 #endif /* defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP) */
5647
5648 /* all objects in block */
5649 case F_Block:
5650 {
5651 BoxType box;
5652
5653 box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
5654 Crosshair.AttachedBox.Point2.X);
5655 box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
5656 Crosshair.AttachedBox.Point2.Y);
5657 box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
5658 Crosshair.AttachedBox.Point2.X);
5659 box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
5660 Crosshair.AttachedBox.Point2.Y);
5661 notify_crosshair_change (false);
5662 NotifyBlock ();
5663 if (Crosshair.AttachedBox.State == STATE_THIRD &&
5664 SelectBlock (&box, false))
5665 {
5666 SetChangedFlag (true);
5667 Crosshair.AttachedBox.State = STATE_FIRST;
5668 }
5669 notify_crosshair_change (true);
5670 break;
5671 }
5672
5673 /* unselect all visible objects */
5674 case F_All:
5675 {
5676 BoxType box;
5677
5678 box.X1 = -MAX_COORD;
5679 box.Y1 = -MAX_COORD;
5680 box.X2 = MAX_COORD;
5681 box.Y2 = MAX_COORD;
5682 if (SelectBlock (&box, false))
5683 SetChangedFlag (true);
5684 break;
5685 }
5686
5687 /* all logical connections */
5688 case F_Found:
5689 if (SelectByFlag (FOUNDFLAG, false))
5690 {
5691 Draw ();
5692 IncrementUndoSerialNumber ();
5693 SetChangedFlag (true);
5694 }
5695 break;
5696
5697 /* all physical connections */
5698 case F_Connection:
5699 if (SelectByFlag (CONNECTEDFLAG, false))
5700 {
5701 Draw ();
5702 IncrementUndoSerialNumber ();
5703 SetChangedFlag (true);
5704 }
5705 break;
5706
5707 default:
5708 AFAIL (unselect);
5709 break;
5710
5711 }
5712 }
5713 return 0;
5714 }
5715
5716 /* --------------------------------------------------------------------------- */
5717
5718 static const char saveto_syntax[] =
5719 N_("SaveTo(Layout|LayoutAs,filename)\n"
5720 "SaveTo(AllConnections|AllUnusedPins|ElementConnections,filename)\n"
5721 "SaveTo(PasteBuffer,filename)");
5722
5723 static const char saveto_help[] = N_("Saves data to a file.");
5724
5725 /* %start-doc actions SaveTo
5726
5727 @table @code
5728
5729 @item Layout
5730 Saves the current layout.
5731
5732 @item LayoutAs
5733 Saves the current layout, and remembers the filename used.
5734
5735 @item AllConnections
5736 Save all connections to a file.
5737
5738 @item AllUnusedPins
5739 List all unused pins to a file.
5740
5741 @item ElementConnections
5742 Save connections to the element at the cursor to a file.
5743
5744 @item PasteBuffer
5745 Save the content of the active Buffer to a file. This is the graphical way to create a footprint.
5746
5747 @end table
5748
5749 %end-doc */
5750
5751 static int
ActionSaveTo(int argc,char ** argv,Coord x,Coord y)5752 ActionSaveTo (int argc, char **argv, Coord x, Coord y)
5753 {
5754 char *function;
5755 char *name;
5756
5757 function = ARG (0);
5758
5759 if ( ! function || strcasecmp (function, "Layout") == 0)
5760 {
5761 if (SavePCB (PCB->Filename) == 0)
5762 SetChangedFlag (false);
5763 return 0;
5764 }
5765
5766 if (argc != 2)
5767 AFAIL (saveto);
5768
5769 name = argv[1];
5770
5771 if (strcasecmp (function, "LayoutAs") == 0)
5772 {
5773 if (SavePCB (name) == 0)
5774 {
5775 SetChangedFlag (false);
5776 free (PCB->Filename);
5777 PCB->Filename = strdup (name);
5778 if (gui->notify_filename_changed != NULL)
5779 gui->notify_filename_changed ();
5780 }
5781 return 0;
5782 }
5783
5784 if (strcasecmp (function, "AllConnections") == 0)
5785 {
5786 FILE *fp;
5787 bool result;
5788 if ((fp = CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
5789 {
5790 LookupConnectionsToAllElements (fp);
5791 fclose (fp);
5792 SetChangedFlag (true);
5793 }
5794 return 0;
5795 }
5796
5797 if (strcasecmp (function, "AllUnusedPins") == 0)
5798 {
5799 FILE *fp;
5800 bool result;
5801 if ((fp = CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
5802 {
5803 LookupUnusedPins (fp);
5804 fclose (fp);
5805 SetChangedFlag (true);
5806 }
5807 return 0;
5808 }
5809
5810 if (strcasecmp (function, "ElementConnections") == 0)
5811 {
5812 ElementType *element;
5813 void *ptrtmp;
5814 FILE *fp;
5815 bool result;
5816
5817 if ((SearchScreen (Crosshair.X, Crosshair.Y, ELEMENT_TYPE,
5818 &ptrtmp, &ptrtmp, &ptrtmp)) != NO_TYPE)
5819 {
5820 element = (ElementType *) ptrtmp;
5821 if ((fp =
5822 CheckAndOpenFile (name, true, false, &result, NULL)) != NULL)
5823 {
5824 LookupElementConnections (element, fp);
5825 fclose (fp);
5826 SetChangedFlag (true);
5827 }
5828 }
5829 return 0;
5830 }
5831
5832 if (strcasecmp (function, "PasteBuffer") == 0)
5833 {
5834 return SaveBufferElements (name);
5835 }
5836
5837 AFAIL (saveto);
5838 }
5839
5840 /* --------------------------------------------------------------------------- */
5841
5842 static const char savesettings_syntax[] =
5843 N_("SaveSettings()\n"
5844 "SaveSettings(local)");
5845
5846 static const char savesettings_help[] = N_("Saves settings.");
5847
5848 /* %start-doc actions SaveSettings
5849
5850 If you pass no arguments, the settings are stored in
5851 @code{$HOME/.pcb/settings}. If you pass the word @code{local} they're
5852 saved in @code{./pcb.settings}.
5853
5854 %end-doc */
5855
5856 static int
ActionSaveSettings(int argc,char ** argv,Coord x,Coord y)5857 ActionSaveSettings (int argc, char **argv, Coord x, Coord y)
5858 {
5859 int locally = argc > 0 ? (strncasecmp (argv[0], "local", 5) == 0) : 0;
5860 hid_save_settings (locally);
5861 return 0;
5862 }
5863
5864 /* --------------------------------------------------------------------------- */
5865
5866 static const char loadfrom_syntax[] =
5867 N_("LoadFrom(Layout|LayoutToBuffer|ElementToBuffer|Netlist|Revert,filename)");
5868
5869 static const char loadfrom_help[] = N_("Load layout data from a file.");
5870
5871 /* %start-doc actions LoadFrom
5872
5873 This action assumes you know what the filename is. The various GUIs
5874 should have a similar @code{Load} action where the filename is
5875 optional, and will provide their own file selection mechanism to let
5876 you choose the file name.
5877
5878 @table @code
5879
5880 @item Layout
5881 Loads an entire PCB layout, replacing the current one.
5882
5883 @item LayoutToBuffer
5884 Loads an entire PCB layout to the paste buffer.
5885
5886 @item ElementToBuffer
5887 Loads the given element file into the paste buffer. Element files
5888 contain only a single @code{Element} definition, such as the
5889 ``newlib'' library uses.
5890
5891 @item Netlist
5892 Loads a new netlist, replacing any current netlist.
5893
5894 @item Revert
5895 Re-loads the current layout from its disk file, reverting any changes
5896 you may have made.
5897
5898 @end table
5899
5900 %end-doc */
5901
5902 static int
ActionLoadFrom(int argc,char ** argv,Coord x,Coord y)5903 ActionLoadFrom (int argc, char **argv, Coord x, Coord y)
5904 {
5905 char *function;
5906 char *name;
5907
5908 if (argc < 2)
5909 AFAIL (loadfrom);
5910
5911 function = argv[0];
5912 name = argv[1];
5913
5914 if (strcasecmp (function, "ElementToBuffer") == 0)
5915 {
5916 notify_crosshair_change (false);
5917 if (LoadElementToBuffer (PASTEBUFFER, name, true))
5918 SetMode (PASTEBUFFER_MODE);
5919 notify_crosshair_change (true);
5920 }
5921
5922 else if (strcasecmp (function, "LayoutToBuffer") == 0)
5923 {
5924 notify_crosshair_change (false);
5925 if (LoadLayoutToBuffer (PASTEBUFFER, name))
5926 SetMode (PASTEBUFFER_MODE);
5927 notify_crosshair_change (true);
5928 }
5929
5930 else if (strcasecmp (function, "Layout") == 0)
5931 {
5932 if (!PCB->Changed ||
5933 gui->confirm_dialog (_("OK to override layout data?"), 0))
5934 LoadPCB (name);
5935 }
5936
5937 else if (strcasecmp (function, "Netlist") == 0)
5938 {
5939 if (PCB->Netlistname)
5940 free (PCB->Netlistname);
5941 PCB->Netlistname = StripWhiteSpaceAndDup (name);
5942 FreeLibraryMemory (&PCB->NetlistLib);
5943 ImportNetlist (PCB->Netlistname);
5944 NetlistChanged (1);
5945 }
5946 else if (strcasecmp (function, "Revert") == 0 && PCB->Filename
5947 && (!PCB->Changed
5948 || gui->confirm_dialog (_("OK to override changes?"), 0)))
5949 {
5950 RevertPCB ();
5951 }
5952
5953 return 0;
5954 }
5955
5956 /* --------------------------------------------------------------------------- */
5957
5958 static const char new_syntax[] = N_("New([name])");
5959
5960 static const char new_help[] = N_("Starts a new layout.");
5961
5962 /* %start-doc actions New
5963
5964 If a name is not given, one is prompted for.
5965
5966 %end-doc */
5967
5968 static int
ActionNew(int argc,char ** argv,Coord x,Coord y)5969 ActionNew (int argc, char **argv, Coord x, Coord y)
5970 {
5971 char *name = ARG (0);
5972
5973 if (!PCB->Changed || gui->confirm_dialog (_("OK to clear layout data?"), 0))
5974 {
5975 if (name)
5976 name = strdup (name);
5977 else
5978 name = gui->prompt_for (_("Enter the layout name:"), "");
5979
5980 if (!name)
5981 return 1;
5982
5983 notify_crosshair_change (false);
5984 /* do emergency saving
5985 * clear the old struct and allocate memory for the new one
5986 */
5987 if (PCB->Changed && Settings.SaveInTMP)
5988 SaveInTMP ();
5989 RemovePCB (PCB);
5990 PCB = NULL;
5991 PCB = CreateNewPCB ();
5992 CreateNewPCBPost (PCB, 1);
5993
5994 /* setup the new name and reset some values to default */
5995 free (PCB->Name);
5996 PCB->Name = name;
5997
5998 ResetStackAndVisibility ();
5999 CenterDisplay (PCB->MaxWidth / 2, PCB->MaxHeight / 2, false);
6000 Redraw ();
6001
6002 hid_action ("PCBChanged");
6003 notify_crosshair_change (true);
6004 return 0;
6005 }
6006 return 1;
6007 }
6008
6009 /*!
6010 * \brief No operation, just for testing purposes.
6011 * syntax: Bell(volume)
6012 */
6013 void
ActionBell(char * volume)6014 ActionBell (char *volume)
6015 {
6016 gui->beep ();
6017 }
6018
6019 /* --------------------------------------------------------------------------- */
6020
6021 static const char pastebuffer_syntax[] =
6022 N_("PasteBuffer(AddSelected|Clear|1..MAX_BUFFER)\n"
6023 "PasteBuffer(Rotate, 1..3)\n"
6024 "PasteBuffer(Convert|Save|Restore|Mirror)\n"
6025 "PasteBuffer(ToLayout, X, Y, units)");
6026
6027 static const char pastebuffer_help[] =
6028 N_("Various operations on the paste buffer.");
6029
6030 /* %start-doc actions PasteBuffer
6031
6032 There are a number of paste buffers; the actual limit is a
6033 compile-time constant @code{MAX_BUFFER} in @file{globalconst.h}. It
6034 is currently @code{5}. One of these is the ``current'' paste buffer,
6035 often referred to as ``the'' paste buffer.
6036
6037 @table @code
6038
6039 @item AddSelected
6040 Copies the selected objects to the current paste buffer.
6041
6042 @item Clear
6043 Remove all objects from the current paste buffer.
6044
6045 @item Convert
6046 Convert the current paste buffer to an element. Vias are converted to
6047 pins, lines are converted to pads.
6048
6049 @item Restore
6050 Convert any elements in the paste buffer back to vias and lines.
6051
6052 @item Mirror
6053 Flip all objects in the paste buffer vertically (up/down flip). To mirror
6054 horizontally, combine this with rotations.
6055
6056 @item Rotate
6057 Rotates the current buffer. The number to pass is 1..3, where 1 means
6058 90 degrees counter clockwise, 2 means 180 degrees, and 3 means 90
6059 degrees clockwise (270 CCW).
6060
6061 @item Save
6062 Saves any elements in the current buffer to the indicated file.
6063
6064 @item ToLayout
6065 Pastes any elements in the current buffer to the indicated X, Y
6066 coordinates in the layout. The @code{X} and @code{Y} are treated like
6067 @code{delta} is for many other objects. For each, if it's prefixed by
6068 @code{+} or @code{-}, then that amount is relative to the last
6069 location. Otherwise, it's absolute. Units can be
6070 @code{mil} or @code{mm}; if unspecified, units are PCB's internal
6071 units, currently 1/100 mil.
6072
6073
6074 @item 1..MAX_BUFFER
6075 Selects the given buffer to be the current paste buffer.
6076
6077 @end table
6078
6079 %end-doc */
6080
6081 static int
ActionPasteBuffer(int argc,char ** argv,Coord x,Coord y)6082 ActionPasteBuffer (int argc, char **argv, Coord x, Coord y)
6083 {
6084 char *function = argc ? argv[0] : (char *)"";
6085 char *sbufnum = argc > 1 ? argv[1] : (char *)"";
6086 char *name;
6087 static char *default_file = NULL;
6088 int free_name = 0;
6089
6090 notify_crosshair_change (false);
6091 if (function)
6092 {
6093 switch (GetFunctionID (function))
6094 {
6095 /* clear contents of paste buffer */
6096 case F_Clear:
6097 ClearBuffer (PASTEBUFFER);
6098 break;
6099
6100 /* copies objects to paste buffer */
6101 case F_AddSelected:
6102 AddSelectedToBuffer (PASTEBUFFER, 0, 0, false);
6103 break;
6104
6105 /* converts buffer contents into an element */
6106 case F_Convert:
6107 ConvertBufferToElement (PASTEBUFFER);
6108 break;
6109
6110 /* break up element for editing */
6111 case F_Restore:
6112 SmashBufferElement (PASTEBUFFER);
6113 break;
6114
6115 /* Mirror buffer */
6116 case F_Mirror:
6117 MirrorBuffer (PASTEBUFFER);
6118 break;
6119
6120 case F_Rotate:
6121 if (sbufnum)
6122 {
6123 RotateBuffer (PASTEBUFFER, (BYTE) atoi (sbufnum));
6124 crosshair_update_range();
6125 }
6126 break;
6127
6128 case F_Save:
6129 if (PASTEBUFFER->Data->ElementN == 0)
6130 {
6131 Message (_("Buffer has no elements!\n"));
6132 break;
6133 }
6134 free_name = 0;
6135 if (argc <= 1)
6136 {
6137 name = gui->fileselect (_("Save Paste Buffer As ..."),
6138 _("Choose a file to save the contents of the\n"
6139 "paste buffer to.\n"),
6140 default_file, ".fp", "footprint",
6141 0);
6142
6143 if (default_file)
6144 {
6145 free (default_file);
6146 default_file = NULL;
6147 }
6148 if ( name && *name)
6149 {
6150 default_file = strdup (name);
6151 }
6152 free_name = 1;
6153 }
6154
6155 else
6156 name = argv[1];
6157
6158 {
6159 FILE *exist;
6160
6161 if ((exist = fopen (name, "r")))
6162 {
6163 fclose (exist);
6164 if (gui->
6165 confirm_dialog (_("File exists! Ok to overwrite?"), 0))
6166 SaveBufferElements (name);
6167 }
6168 else
6169 SaveBufferElements (name);
6170
6171 if (free_name && name)
6172 free (name);
6173 }
6174 break;
6175
6176 case F_ToLayout:
6177 {
6178 static Coord oldx = 0, oldy = 0;
6179 Coord x, y;
6180 bool absolute;
6181
6182 if (argc == 1)
6183 {
6184 x = y = 0;
6185 }
6186 else if (argc == 3 || argc == 4)
6187 {
6188 x = GetValue (ARG (1), ARG (3), &absolute);
6189 if (!absolute)
6190 x += oldx;
6191 y = GetValue (ARG (2), ARG (3), &absolute);
6192 if (!absolute)
6193 y += oldy;
6194 }
6195 else
6196 {
6197 notify_crosshair_change (true);
6198 AFAIL (pastebuffer);
6199 }
6200
6201 oldx = x;
6202 oldy = y;
6203 if (CopyPastebufferToLayout (x, y))
6204 SetChangedFlag (true);
6205 }
6206 break;
6207
6208 /* set number */
6209 default:
6210 {
6211 int number = atoi (function);
6212
6213 /* correct number */
6214 if (number)
6215 SetBufferNumber (number - 1);
6216 }
6217 }
6218 }
6219
6220 notify_crosshair_change (true);
6221 return 0;
6222 }
6223
6224 /* --------------------------------------------------------------------------- */
6225
6226 static const char undo_syntax[] = N_("Undo()\n"
6227 "Undo(ClearList)");
6228
6229 static const char undo_help[] = N_("Undo recent changes.");
6230
6231 /* %start-doc actions Undo
6232
6233 The unlimited undo feature of @code{Pcb} allows you to recover from
6234 most operations that materially affect you work. Calling
6235 @code{Undo()} without any parameter recovers from the last (non-undo)
6236 operation. @code{ClearList} is used to release the allocated
6237 memory. @code{ClearList} is called whenever a new layout is started or
6238 loaded. See also @code{Redo} and @code{Atomic}.
6239
6240 Note that undo groups operations by serial number; changes with the
6241 same serial number will be undone (or redone) as a group. See
6242 @code{Atomic}.
6243
6244 %end-doc */
6245
6246 static int
ActionUndo(int argc,char ** argv,Coord x,Coord y)6247 ActionUndo (int argc, char **argv, Coord x, Coord y)
6248 {
6249 char *function = ARG (0);
6250 if (!function || !*function)
6251 {
6252 /* don't allow undo in the middle of an operation */
6253 if (Settings.Mode != POLYGONHOLE_MODE &&
6254 Crosshair.AttachedObject.State != STATE_FIRST)
6255 return 1;
6256 if (Crosshair.AttachedBox.State != STATE_FIRST
6257 && Settings.Mode != ARC_MODE)
6258 return 1;
6259 /* undo the last operation */
6260
6261 notify_crosshair_change (false);
6262 if ((Settings.Mode == POLYGON_MODE ||
6263 Settings.Mode == POLYGONHOLE_MODE) &&
6264 Crosshair.AttachedPolygon.PointN)
6265 {
6266 GoToPreviousPoint ();
6267 notify_crosshair_change (true);
6268 return 0;
6269 }
6270 /* move anchor point if undoing during line creation */
6271 if (Settings.Mode == LINE_MODE)
6272 {
6273 if (Crosshair.AttachedLine.State == STATE_SECOND)
6274 {
6275 if (TEST_FLAG (AUTODRCFLAG, PCB))
6276 Undo (true); /* undo the connection find */
6277 Crosshair.AttachedLine.State = STATE_FIRST;
6278 SetLocalRef (0, 0, false);
6279 notify_crosshair_change (true);
6280 return 0;
6281 }
6282 if (Crosshair.AttachedLine.State == STATE_THIRD)
6283 {
6284 int type;
6285 void *ptr1, *ptr3, *ptrtmp;
6286 LineType *ptr2;
6287 /* this search is guaranteed to succeed */
6288 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6289 &ptrtmp, &ptr3,
6290 Crosshair.AttachedLine.Point1.X,
6291 Crosshair.AttachedLine.Point1.Y, 0);
6292 ptr2 = (LineType *) ptrtmp;
6293
6294 /* save both ends of line */
6295 Crosshair.AttachedLine.Point2.X = ptr2->Point1.X;
6296 Crosshair.AttachedLine.Point2.Y = ptr2->Point1.Y;
6297 if ((type = Undo (true)))
6298 SetChangedFlag (true);
6299 /* check that the undo was of the right type */
6300 if ((type & UNDO_CREATE) == 0)
6301 {
6302 /* wrong undo type, restore anchor points */
6303 Crosshair.AttachedLine.Point2.X =
6304 Crosshair.AttachedLine.Point1.X;
6305 Crosshair.AttachedLine.Point2.Y =
6306 Crosshair.AttachedLine.Point1.Y;
6307 notify_crosshair_change (true);
6308 return 0;
6309 }
6310 /* move to new anchor */
6311 Crosshair.AttachedLine.Point1.X =
6312 Crosshair.AttachedLine.Point2.X;
6313 Crosshair.AttachedLine.Point1.Y =
6314 Crosshair.AttachedLine.Point2.Y;
6315 /* check if an intermediate point was removed */
6316 if (type & UNDO_REMOVE)
6317 {
6318 /* this search should find the restored line */
6319 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6320 &ptrtmp,
6321 &ptr3,
6322 Crosshair.AttachedLine.Point2.X,
6323 Crosshair.AttachedLine.Point2.Y, 0);
6324 ptr2 = (LineType *) ptrtmp;
6325 if (TEST_FLAG (AUTODRCFLAG, PCB))
6326 {
6327 /* undo loses CONNECTEDFLAG and FOUNDFLAG */
6328 SET_FLAG(CONNECTEDFLAG, ptr2);
6329 SET_FLAG(FOUNDFLAG, ptr2);
6330 DrawLine (CURRENT, ptr2);
6331 }
6332 Crosshair.AttachedLine.Point1.X =
6333 Crosshair.AttachedLine.Point2.X = ptr2->Point2.X;
6334 Crosshair.AttachedLine.Point1.Y =
6335 Crosshair.AttachedLine.Point2.Y = ptr2->Point2.Y;
6336 }
6337 FitCrosshairIntoGrid (Crosshair.X, Crosshair.Y);
6338 AdjustAttachedObjects ();
6339 if (--addedLines == 0)
6340 {
6341 Crosshair.AttachedLine.State = STATE_SECOND;
6342 lastLayer = CURRENT;
6343 }
6344 else
6345 {
6346 /* this search is guaranteed to succeed too */
6347 SearchObjectByLocation (LINE_TYPE | RATLINE_TYPE, &ptr1,
6348 &ptrtmp,
6349 &ptr3,
6350 Crosshair.AttachedLine.Point1.X,
6351 Crosshair.AttachedLine.Point1.Y, 0);
6352 ptr2 = (LineType *) ptrtmp;
6353 lastLayer = (LayerType *) ptr1;
6354 }
6355 notify_crosshair_change (true);
6356 return 0;
6357 }
6358 }
6359 if (Settings.Mode == ARC_MODE)
6360 {
6361 if (Crosshair.AttachedBox.State == STATE_SECOND)
6362 {
6363 Crosshair.AttachedBox.State = STATE_FIRST;
6364 notify_crosshair_change (true);
6365 return 0;
6366 }
6367 if (Crosshair.AttachedBox.State == STATE_THIRD)
6368 {
6369 void *ptr1, *ptr2, *ptr3;
6370 BoxType *bx;
6371 /* guaranteed to succeed */
6372 SearchObjectByLocation (ARC_TYPE, &ptr1, &ptr2, &ptr3,
6373 Crosshair.AttachedBox.Point1.X,
6374 Crosshair.AttachedBox.Point1.Y, 0);
6375 bx = GetArcEnds ((ArcType *) ptr2);
6376 Crosshair.AttachedBox.Point1.X =
6377 Crosshair.AttachedBox.Point2.X = bx->X1;
6378 Crosshair.AttachedBox.Point1.Y =
6379 Crosshair.AttachedBox.Point2.Y = bx->Y1;
6380 AdjustAttachedObjects ();
6381 if (--addedLines == 0)
6382 Crosshair.AttachedBox.State = STATE_SECOND;
6383 }
6384 }
6385 /* undo the last destructive operation */
6386 if (Undo (true))
6387 SetChangedFlag (true);
6388 }
6389 else if (function)
6390 {
6391 switch (GetFunctionID (function))
6392 {
6393 /* clear 'undo objects' list */
6394 case F_ClearList:
6395 ClearUndoList (false);
6396 break;
6397 }
6398 }
6399 notify_crosshair_change (true);
6400 return 0;
6401 }
6402
6403 /* --------------------------------------------------------------------------- */
6404
6405 static const char redo_syntax[] = N_("Redo()");
6406
6407 static const char redo_help[] = N_("Redo recent \"undo\" operations.");
6408
6409 /* %start-doc actions Redo
6410
6411 This routine allows you to recover from the last undo command. You
6412 might want to do this if you thought that undo was going to revert
6413 something other than what it actually did (in case you are confused
6414 about which operations are un-doable), or if you have been backing up
6415 through a long undo list and over-shoot your stopping point. Any
6416 change that is made since the undo in question will trim the redo
6417 list. For example if you add ten lines, then undo three of them you
6418 could use redo to put them back, but if you move a line on the board
6419 before performing the redo, you will lose the ability to "redo" the
6420 three "undone" lines.
6421
6422 %end-doc */
6423
6424 static int
ActionRedo(int argc,char ** argv,Coord x,Coord y)6425 ActionRedo (int argc, char **argv, Coord x, Coord y)
6426 {
6427 if (((Settings.Mode == POLYGON_MODE ||
6428 Settings.Mode == POLYGONHOLE_MODE) &&
6429 Crosshair.AttachedPolygon.PointN) ||
6430 Crosshair.AttachedLine.State == STATE_SECOND)
6431 return 1;
6432 notify_crosshair_change (false);
6433 if (Redo (true))
6434 {
6435 SetChangedFlag (true);
6436 if (Settings.Mode == LINE_MODE &&
6437 Crosshair.AttachedLine.State != STATE_FIRST)
6438 {
6439 LineType *line = g_list_last (CURRENT->Line)->data;
6440 Crosshair.AttachedLine.Point1.X =
6441 Crosshair.AttachedLine.Point2.X = line->Point2.X;
6442 Crosshair.AttachedLine.Point1.Y =
6443 Crosshair.AttachedLine.Point2.Y = line->Point2.Y;
6444 addedLines++;
6445 }
6446 }
6447 notify_crosshair_change (true);
6448 return 0;
6449 }
6450
6451 /* --------------------------------------------------------------------------- */
6452
6453 static const char polygon_syntax[] = N_("Polygon(Close|PreviousPoint)");
6454
6455 static const char polygon_help[] = N_("Some polygon related stuff.");
6456
6457 /* %start-doc actions Polygon
6458
6459 Polygons need a special action routine to make life easier.
6460
6461 @table @code
6462
6463 @item Close
6464 Creates the final segment of the polygon. This may fail if clipping
6465 to 45 degree lines is switched on, in which case a warning is issued.
6466
6467 @item PreviousPoint
6468 Resets the newly entered corner to the previous one. The Undo action
6469 will call Polygon(PreviousPoint) when appropriate to do so.
6470
6471 @end table
6472
6473 %end-doc */
6474
6475 static int
ActionPolygon(int argc,char ** argv,Coord x,Coord y)6476 ActionPolygon (int argc, char **argv, Coord x, Coord y)
6477 {
6478 char *function = ARG (0);
6479 if (function && Settings.Mode == POLYGON_MODE)
6480 {
6481 notify_crosshair_change (false);
6482 switch (GetFunctionID (function))
6483 {
6484 /* close open polygon if possible */
6485 case F_Close:
6486 ClosePolygon ();
6487 break;
6488
6489 /* go back to the previous point */
6490 case F_PreviousPoint:
6491 GoToPreviousPoint ();
6492 break;
6493 }
6494 notify_crosshair_change (true);
6495 }
6496 return 0;
6497 }
6498
6499 /* --------------------------------------------------------------------------- */
6500
6501 static const char routestyle_syntax[] = N_("RouteStyle(1|2|3|4)");
6502
6503 static const char routestyle_help[] =
6504 N_("Copies the indicated routing style into the current sizes.");
6505
6506 /* %start-doc actions RouteStyle
6507
6508 %end-doc */
6509
6510 static int
ActionRouteStyle(int argc,char ** argv,Coord x,Coord y)6511 ActionRouteStyle (int argc, char **argv, Coord x, Coord y)
6512 {
6513 char *str = ARG (0);
6514 RouteStyleType *rts;
6515 int number;
6516
6517 if (str)
6518 {
6519 number = atoi (str);
6520 if (number > 0 && number <= NUM_STYLES)
6521 {
6522 rts = &PCB->RouteStyle[number - 1];
6523 SetLineSize (rts->Thick);
6524 SetViaSize (rts->Diameter, true);
6525 SetViaDrillingHole (rts->Hole, true);
6526 SetKeepawayWidth (rts->Keepaway);
6527 SetViaMaskAperture(rts->ViaMask);
6528 hid_action("RouteStylesChanged");
6529 }
6530 }
6531 return 0;
6532 }
6533
6534
6535 /* --------------------------------------------------------------------------- */
6536
6537 static const char moveobject_syntax[] = N_("MoveObject(X,Y,dim)");
6538
6539 static const char moveobject_help[] =
6540 N_("Moves the object under the crosshair.");
6541
6542 /* %start-doc actions MoveObject
6543
6544 The @code{X} and @code{Y} are treated like @code{delta} is for many
6545 other objects. For each, if it's prefixed by @code{+} or @code{-},
6546 then that amount is relative. Otherwise, it's absolute. Units can be
6547 @code{mil} or @code{mm}; if unspecified, units are PCB's internal
6548 units, currently 1/100 mil.
6549
6550 %end-doc */
6551
6552 static int
ActionMoveObject(int argc,char ** argv,Coord x,Coord y)6553 ActionMoveObject (int argc, char **argv, Coord x, Coord y)
6554 {
6555 char *x_str = ARG (0);
6556 char *y_str = ARG (1);
6557 char *units = ARG (2);
6558 Coord nx, ny;
6559 bool absolute1, absolute2;
6560 void *ptr1, *ptr2, *ptr3;
6561 int type;
6562
6563 ny = GetValue (y_str, units, &absolute1);
6564 nx = GetValue (x_str, units, &absolute2);
6565
6566 type = SearchScreen (x, y, MOVE_TYPES, &ptr1, &ptr2, &ptr3);
6567 if (type == NO_TYPE)
6568 {
6569 Message (_("Nothing found under crosshair\n"));
6570 return 1;
6571 }
6572 if (absolute1)
6573 nx -= x;
6574 if (absolute2)
6575 ny -= y;
6576 Crosshair.AttachedObject.RubberbandN = 0;
6577 if (TEST_FLAG (RUBBERBANDFLAG, PCB))
6578 LookupRubberbandLines (type, ptr1, ptr2, ptr3);
6579 if (type == ELEMENT_TYPE)
6580 LookupRatLines (type, ptr1, ptr2, ptr3);
6581 MoveObjectAndRubberband (type, ptr1, ptr2, ptr3, nx, ny);
6582 SetChangedFlag (true);
6583 return 0;
6584 }
6585
6586 /* --------------------------------------------------------------------------- */
6587
6588 static const char movetocurrentlayer_syntax[] =
6589 N_("MoveToCurrentLayer(Object|SelectedObjects)");
6590
6591 static const char movetocurrentlayer_help[] =
6592 N_("Moves objects to the current layer.");
6593
6594 /* %start-doc actions MoveToCurrentLayer
6595
6596 Note that moving an element from a component layer to a solder layer,
6597 or from solder to component, won't automatically flip it. Use the
6598 @code{Flip()} action to do that.
6599
6600 %end-doc */
6601
6602 static int
ActionMoveToCurrentLayer(int argc,char ** argv,Coord x,Coord y)6603 ActionMoveToCurrentLayer (int argc, char **argv, Coord x, Coord y)
6604 {
6605 char *function = ARG (0);
6606 if (function)
6607 {
6608 switch (GetFunctionID (function))
6609 {
6610 case F_Object:
6611 {
6612 int type;
6613 void *ptr1, *ptr2, *ptr3;
6614
6615 gui->get_coords (_("Select an Object"), &x, &y);
6616 if ((type =
6617 SearchScreen (x, y, MOVETOLAYER_TYPES,
6618 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
6619 if (MoveObjectToLayer (type, ptr1, ptr2, ptr3, CURRENT, false))
6620 SetChangedFlag (true);
6621 break;
6622 }
6623
6624 case F_SelectedObjects:
6625 case F_Selected:
6626 if (MoveSelectedObjectsToLayer (CURRENT))
6627 SetChangedFlag (true);
6628 break;
6629 }
6630 }
6631 return 0;
6632 }
6633
6634
6635 static const char setsame_syntax[] = N_("SetSame()");
6636
6637 static const char setsame_help[] =
6638 N_("Sets current layer and sizes to match indicated item.");
6639
6640 /* %start-doc actions SetSame
6641
6642 When invoked over any line, arc, polygon, or via, this changes the
6643 current layer to be the layer that item is on, and changes the current
6644 sizes (thickness, keepaway, drill, etc) according to that item.
6645
6646 %end-doc */
6647
6648 static int
ActionSetSame(int argc,char ** argv,Coord x,Coord y)6649 ActionSetSame (int argc, char **argv, Coord x, Coord y)
6650 {
6651 void *ptr1, *ptr2, *ptr3;
6652 int type;
6653 LayerType *layer = CURRENT;
6654
6655 type = SearchScreen (x, y, CLONE_TYPES, &ptr1, &ptr2, &ptr3);
6656 /* set layer current and size from line or arc */
6657 switch (type)
6658 {
6659 case LINE_TYPE:
6660 notify_crosshair_change (false);
6661 Settings.LineThickness = ((LineType *) ptr2)->Thickness;
6662 Settings.Keepaway = ((LineType *) ptr2)->Clearance / 2;
6663 layer = (LayerType *) ptr1;
6664 if (Settings.Mode != LINE_MODE)
6665 SetMode (LINE_MODE);
6666 notify_crosshair_change (true);
6667 hid_action ("RouteStylesChanged");
6668 break;
6669
6670 case ARC_TYPE:
6671 notify_crosshair_change (false);
6672 Settings.LineThickness = ((ArcType *) ptr2)->Thickness;
6673 Settings.Keepaway = ((ArcType *) ptr2)->Clearance / 2;
6674 layer = (LayerType *) ptr1;
6675 if (Settings.Mode != ARC_MODE)
6676 SetMode (ARC_MODE);
6677 notify_crosshair_change (true);
6678 hid_action ("RouteStylesChanged");
6679 break;
6680
6681 case POLYGON_TYPE:
6682 layer = (LayerType *) ptr1;
6683 break;
6684
6685 case VIA_TYPE:
6686 notify_crosshair_change (false);
6687 Settings.ViaThickness = ((PinType *) ptr2)->Thickness;
6688 Settings.ViaDrillingHole = ((PinType *) ptr2)->DrillingHole;
6689 Settings.Keepaway = ((PinType *) ptr2)->Clearance / 2;
6690 if (Settings.Mode != VIA_MODE)
6691 SetMode (VIA_MODE);
6692 notify_crosshair_change (true);
6693 hid_action ("RouteStylesChanged");
6694 break;
6695
6696 default:
6697 return 1;
6698 }
6699 if (layer != CURRENT)
6700 {
6701 ChangeGroupVisibility (GetLayerNumber (PCB->Data, layer), true, true);
6702 Redraw ();
6703 }
6704 return 0;
6705 }
6706
6707
6708 /* --------------------------------------------------------------------------- */
6709
6710 static const char setflag_syntax[] =
6711 N_("SetFlag(Object|Selected|SelectedObjects, flag)\n"
6712 "SetFlag(SelectedLines|SelectedPins|SelectedVias, flag)\n"
6713 "SetFlag(SelectedPads|SelectedTexts|SelectedNames, flag)\n"
6714 "SetFlag(SelectedElements, flag)\n"
6715 "flag = square | octagon | thermal | join");
6716
6717 static const char setflag_help[] = N_("Sets flags on objects.");
6718
6719 /* %start-doc actions SetFlag
6720
6721 Turns the given flag on, regardless of its previous setting. See
6722 @code{ChangeFlag}.
6723
6724 @example
6725 SetFlag(SelectedPins,thermal)
6726 @end example
6727
6728 %end-doc */
6729
6730 static int
ActionSetFlag(int argc,char ** argv,Coord x,Coord y)6731 ActionSetFlag (int argc, char **argv, Coord x, Coord y)
6732 {
6733 char *function = ARG (0);
6734 char *flag = ARG (1);
6735 ChangeFlag (function, flag, 1, "SetFlag");
6736 return 0;
6737 }
6738
6739 /* --------------------------------------------------------------------------- */
6740
6741 static const char clrflag_syntax[] =
6742 N_("ClrFlag(Object|Selected|SelectedObjects, flag)\n"
6743 "ClrFlag(SelectedLines|SelectedPins|SelectedVias, flag)\n"
6744 "ClrFlag(SelectedPads|SelectedTexts|SelectedNames, flag)\n"
6745 "ClrFlag(SelectedElements, flag)\n"
6746 "flag = square | octagon | thermal | join");
6747
6748 static const char clrflag_help[] = N_("Clears flags on objects.");
6749
6750 /* %start-doc actions ClrFlag
6751
6752 Turns the given flag off, regardless of its previous setting. See
6753 @code{ChangeFlag}.
6754
6755 @example
6756 ClrFlag(SelectedLines,join)
6757 @end example
6758
6759 %end-doc */
6760
6761 static int
ActionClrFlag(int argc,char ** argv,Coord x,Coord y)6762 ActionClrFlag (int argc, char **argv, Coord x, Coord y)
6763 {
6764 char *function = ARG (0);
6765 char *flag = ARG (1);
6766 ChangeFlag (function, flag, 0, "ClrFlag");
6767 return 0;
6768 }
6769
6770 /* --------------------------------------------------------------------------- */
6771
6772 static const char changeflag_syntax[] =
6773 N_("ChangeFlag(Object|Selected|SelectedObjects, flag, value)\n"
6774 "ChangeFlag(SelectedLines|SelectedPins|SelectedVias, flag, value)\n"
6775 "ChangeFlag(SelectedPads|SelectedTexts|SelectedNames, flag, value)\n"
6776 "ChangeFlag(SelectedElements, flag, value)\n"
6777 "flag = square | octagon | thermal | join\n"
6778 "value = 0 | 1");
6779
6780 static const char changeflag_help[] = N_("Sets or clears flags on objects.");
6781
6782 /* %start-doc actions ChangeFlag
6783
6784 Toggles the given flag on the indicated object(s). The flag may be
6785 one of the flags listed above (square, octagon, thermal, join). The
6786 value may be the number 0 or 1. If the value is 0, the flag is
6787 cleared. If the value is 1, the flag is set.
6788
6789 %end-doc */
6790
6791 static int
ActionChangeFlag(int argc,char ** argv,Coord x,Coord y)6792 ActionChangeFlag (int argc, char **argv, Coord x, Coord y)
6793 {
6794 char *function = ARG (0);
6795 char *flag = ARG (1);
6796 int value = argc > 2 ? atoi (argv[2]) : -1;
6797 if (value != 0 && value != 1)
6798 AFAIL (changeflag);
6799
6800 ChangeFlag (function, flag, value, "ChangeFlag");
6801 return 0;
6802 }
6803
6804
6805 static void
ChangeFlag(char * what,char * flag_name,int value,char * cmd_name)6806 ChangeFlag (char *what, char *flag_name, int value, char *cmd_name)
6807 {
6808 bool (*set_object) (int, void *, void *, void *);
6809 bool (*set_selected) (int);
6810
6811 if (NSTRCMP (flag_name, "square") == 0)
6812 {
6813 set_object = value ? SetObjectSquare : ClrObjectSquare;
6814 set_selected = value ? SetSelectedSquare : ClrSelectedSquare;
6815 }
6816 else if (NSTRCMP (flag_name, "octagon") == 0)
6817 {
6818 set_object = value ? SetObjectOctagon : ClrObjectOctagon;
6819 set_selected = value ? SetSelectedOctagon : ClrSelectedOctagon;
6820 }
6821 else if (NSTRCMP (flag_name, "join") == 0)
6822 {
6823 /* Note: these are backwards, because the flag is "clear" but
6824 the command is "join". */
6825 set_object = value ? ClrObjectJoin : SetObjectJoin;
6826 set_selected = value ? ClrSelectedJoin : SetSelectedJoin;
6827 }
6828 else
6829 {
6830 Message (_("%s(): Flag \"%s\" is not valid\n"), cmd_name, flag_name);
6831 return;
6832 }
6833
6834 switch (GetFunctionID (what))
6835 {
6836 case F_Object:
6837 {
6838 int type;
6839 void *ptr1, *ptr2, *ptr3;
6840
6841 if ((type =
6842 SearchScreen (Crosshair.X, Crosshair.Y, CHANGESIZE_TYPES,
6843 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
6844 if (TEST_FLAG (LOCKFLAG, (PinType *) ptr2))
6845 Message (_("Sorry, the object is locked\n"));
6846 if (set_object (type, ptr1, ptr2, ptr3))
6847 SetChangedFlag (true);
6848 break;
6849 }
6850
6851 case F_SelectedVias:
6852 if (set_selected (VIA_TYPE))
6853 SetChangedFlag (true);
6854 break;
6855
6856 case F_SelectedPins:
6857 if (set_selected (PIN_TYPE))
6858 SetChangedFlag (true);
6859 break;
6860
6861 case F_SelectedPads:
6862 if (set_selected (PAD_TYPE))
6863 SetChangedFlag (true);
6864 break;
6865
6866 case F_SelectedLines:
6867 if (set_selected (LINE_TYPE))
6868 SetChangedFlag (true);
6869 break;
6870
6871 case F_SelectedTexts:
6872 if (set_selected (TEXT_TYPE))
6873 SetChangedFlag (true);
6874 break;
6875
6876 case F_SelectedNames:
6877 if (set_selected (ELEMENTNAME_TYPE))
6878 SetChangedFlag (true);
6879 break;
6880
6881 case F_SelectedElements:
6882 if (set_selected (ELEMENT_TYPE))
6883 SetChangedFlag (true);
6884 break;
6885
6886 case F_Selected:
6887 case F_SelectedObjects:
6888 if (set_selected (CHANGESIZE_TYPES))
6889 SetChangedFlag (true);
6890 break;
6891 }
6892 }
6893
6894 /* --------------------------------------------------------------------------- */
6895
6896 static const char executefile_syntax[] = N_("ExecuteFile(filename)");
6897
6898 static const char executefile_help[] = N_("Run actions from the given file.");
6899
6900 /* %start-doc actions ExecuteFile
6901
6902 Lines starting with @code{#} are ignored.
6903
6904 %end-doc */
6905
6906 static int
ActionExecuteFile(int argc,char ** argv,Coord x,Coord y)6907 ActionExecuteFile (int argc, char **argv, Coord x, Coord y)
6908 {
6909 FILE *fp;
6910 char *fname;
6911 char line[256];
6912 int n = 0;
6913 char *sp;
6914
6915 if (argc != 1)
6916 AFAIL (executefile);
6917
6918 fname = argv[0];
6919
6920 if ((fp = fopen (fname, "r")) == NULL)
6921 {
6922 fprintf (stderr, _("Could not open actions file \"%s\".\n"), fname);
6923 return 1;
6924 }
6925
6926 defer_updates = 1;
6927 defer_needs_update = 0;
6928 while (fgets (line, sizeof (line), fp) != NULL)
6929 {
6930 n++;
6931 sp = line;
6932
6933 /* eat the trailing newline */
6934 while (*sp && *sp != '\r' && *sp != '\n')
6935 sp++;
6936 *sp = '\0';
6937
6938 /* eat leading spaces and tabs */
6939 sp = line;
6940 while (*sp && (*sp == ' ' || *sp == '\t'))
6941 sp++;
6942
6943 /*
6944 * if we have anything left and its not a comment line
6945 * then execute it
6946 */
6947
6948 if (*sp && *sp != '#')
6949 {
6950 /*Message ("%s : line %-3d : \"%s\"\n", fname, n, sp);*/
6951 hid_parse_actions (sp);
6952 }
6953 }
6954
6955 defer_updates = 0;
6956 if (defer_needs_update)
6957 {
6958 IncrementUndoSerialNumber ();
6959 gui->invalidate_all ();
6960 }
6961 fclose (fp);
6962 return 0;
6963 }
6964
6965 /* --------------------------------------------------------------------------- */
6966
6967 static int
ActionPSCalib(int argc,char ** argv,Coord x,Coord y)6968 ActionPSCalib (int argc, char **argv, Coord x, Coord y)
6969 {
6970 HID *ps = hid_find_exporter ("ps");
6971 ps->calibrate (0.0,0.0);
6972 return 0;
6973 }
6974
6975 /* --------------------------------------------------------------------------- */
6976
6977 static ElementType *element_cache = NULL;
6978
6979 static ElementType *
find_element_by_refdes(char * refdes)6980 find_element_by_refdes (char *refdes)
6981 {
6982 if (element_cache
6983 && NAMEONPCB_NAME(element_cache)
6984 && strcmp (NAMEONPCB_NAME(element_cache), refdes) == 0)
6985 return element_cache;
6986
6987 ELEMENT_LOOP (PCB->Data);
6988 {
6989 if (NAMEONPCB_NAME(element)
6990 && strcmp (NAMEONPCB_NAME(element), refdes) == 0)
6991 {
6992 element_cache = element;
6993 return element_cache;
6994 }
6995 }
6996 END_LOOP;
6997 return NULL;
6998 }
6999
7000 static AttributeType *
lookup_attr(AttributeListType * list,const char * name)7001 lookup_attr (AttributeListType *list, const char *name)
7002 {
7003 int i;
7004 for (i=0; i<list->Number; i++)
7005 if (strcmp (list->List[i].name, name) == 0)
7006 return & list->List[i];
7007 return NULL;
7008 }
7009
7010 static void
delete_attr(AttributeListType * list,AttributeType * attr)7011 delete_attr (AttributeListType *list, AttributeType *attr)
7012 {
7013 int idx = attr - list->List;
7014 if (idx < 0 || idx >= list->Number)
7015 return;
7016 if (list->Number - idx > 1)
7017 memmove (attr, attr+1, (list->Number - idx - 1) * sizeof(AttributeType));
7018 list->Number --;
7019 }
7020
7021 /* ---------------------------------------------------------------- */
7022 static const char elementlist_syntax[] =
7023 N_("ElementList(Start|Done|Need,<refdes>,<footprint>,<value>)");
7024
7025 static const char elementlist_help[] =
7026 N_("Adds the given element if it doesn't already exist.");
7027
7028 /* %start-doc actions elementlist
7029
7030 @table @code
7031
7032 @item Start
7033 Indicates the start of an element list; call this before any Need
7034 actions.
7035
7036 @item Need
7037 Searches the board for an element with a matching refdes.
7038
7039 If found, the value and footprint are updated.
7040
7041 If not found, a new element is created with the given footprint and value.
7042
7043 @item Done
7044 Compares the list of elements needed since the most recent
7045 @code{start} with the list of elements actually on the board. Any
7046 elements that weren't listed are selected, so that the user may delete
7047 them.
7048
7049 @end table
7050
7051 %end-doc */
7052
7053 static int number_of_footprints_not_found;
7054
7055 static int
parse_layout_attribute_units(char * name,int def)7056 parse_layout_attribute_units (char *name, int def)
7057 {
7058 const char *as = AttributeGet (PCB, name);
7059 if (!as)
7060 return def;
7061 return GetValue (as, NULL, NULL);
7062 }
7063
7064 static int
ActionElementList(int argc,char ** argv,Coord x,Coord y)7065 ActionElementList (int argc, char **argv, Coord x, Coord y)
7066 {
7067 ElementType *e = NULL;
7068 char *refdes, *value, *footprint, *old;
7069 char *args[3];
7070 char *function;
7071
7072 if (argc < 1)
7073 AFAIL (elementlist);
7074
7075 function = argv[0];
7076
7077 #ifdef DEBUG
7078 printf("Entered ActionElementList, executing function %s\n", function);
7079 #endif
7080
7081 if (strcasecmp (function, "start") == 0)
7082 {
7083 ELEMENT_LOOP (PCB->Data);
7084 {
7085 CLEAR_FLAG (FOUNDFLAG, element);
7086 }
7087 END_LOOP;
7088 element_cache = NULL;
7089 number_of_footprints_not_found = 0;
7090 return 0;
7091 }
7092
7093 if (strcasecmp (function, "done") == 0)
7094 {
7095 ELEMENT_LOOP (PCB->Data);
7096 {
7097 if (TEST_FLAG (FOUNDFLAG, element))
7098 {
7099 CLEAR_FLAG (FOUNDFLAG, element);
7100 }
7101 else if (! EMPTY_STRING_P (NAMEONPCB_NAME (element)))
7102 {
7103 /* Unnamed elements should remain untouched */
7104 SET_FLAG (SELECTEDFLAG, element);
7105 }
7106 }
7107 END_LOOP;
7108 if (number_of_footprints_not_found > 0)
7109 gui->confirm_dialog (_("Not all requested footprints were found.\n"
7110 "See the message log for details"),
7111 "Ok", NULL);
7112 return 0;
7113 }
7114
7115 if (strcasecmp (function, "need") != 0)
7116 AFAIL (elementlist);
7117
7118 if (argc != 4)
7119 AFAIL (elementlist);
7120
7121 argc --;
7122 argv ++;
7123
7124 refdes = ARG(0);
7125 footprint = ARG(1);
7126 value = ARG(2);
7127
7128 args[0] = footprint;
7129 args[1] = refdes;
7130 args[2] = value;
7131
7132 #ifdef DEBUG
7133 printf(" ... footprint = %s\n", footprint);
7134 printf(" ... refdes = %s\n", refdes);
7135 printf(" ... value = %s\n", value);
7136 #endif
7137
7138 e = find_element_by_refdes (refdes);
7139
7140 if (!e)
7141 {
7142 Coord nx, ny, d;
7143
7144 #ifdef DEBUG
7145 printf(" ... Footprint not on board, need to add it.\n");
7146 #endif
7147 /* Not on board, need to add it. */
7148 if (LoadFootprint(argc, args, x, y))
7149 {
7150 number_of_footprints_not_found ++;
7151 return 1;
7152 }
7153
7154 nx = PCB->MaxWidth / 2;
7155 ny = PCB->MaxHeight / 2;
7156 d = MIN (PCB->MaxWidth, PCB->MaxHeight) / 10;
7157
7158 nx = parse_layout_attribute_units ("import::newX", nx);
7159 ny = parse_layout_attribute_units ("import::newY", ny);
7160 d = parse_layout_attribute_units ("import::disperse", d);
7161
7162 if (d > 0)
7163 {
7164 nx += rand () % (d*2) - d;
7165 ny += rand () % (d*2) - d;
7166 }
7167
7168 if (nx < 0)
7169 nx = 0;
7170 if (nx >= PCB->MaxWidth)
7171 nx = PCB->MaxWidth - 1;
7172 if (ny < 0)
7173 ny = 0;
7174 if (ny >= PCB->MaxHeight)
7175 ny = PCB->MaxHeight - 1;
7176
7177 /* Place components onto center of board. */
7178 if (CopyPastebufferToLayout (nx, ny))
7179 SetChangedFlag (true);
7180 }
7181
7182 else if (e && DESCRIPTION_NAME(e) && strcmp (DESCRIPTION_NAME(e), footprint) != 0)
7183 {
7184 int er, pr, i;
7185 Coord mx, my;
7186 ElementType *pe;
7187
7188 #ifdef DEBUG
7189 printf(" ... Footprint on board, but different from footprint loaded.\n");
7190 #endif
7191 /* Different footprint, we need to swap them out. */
7192 if (LoadFootprint(argc, args, x, y))
7193 {
7194 number_of_footprints_not_found ++;
7195 return 1;
7196 }
7197
7198 er = ElementOrientation (e);
7199 pe = PASTEBUFFER->Data->Element->data;
7200 if (!FRONT (e))
7201 MirrorElementCoordinates (PASTEBUFFER->Data, pe, pe->MarkY*2 - PCB->MaxHeight);
7202 pr = ElementOrientation (pe);
7203
7204 mx = e->MarkX;
7205 my = e->MarkY;
7206
7207 if (er != pr)
7208 RotateElementLowLevel (PASTEBUFFER->Data, pe, pe->MarkX, pe->MarkY, (er-pr+4)%4);
7209
7210 for (i=0; i<MAX_ELEMENTNAMES; i++)
7211 {
7212 pe->Name[i].X = e->Name[i].X - mx + pe->MarkX ;
7213 pe->Name[i].Y = e->Name[i].Y - my + pe->MarkY ;
7214 pe->Name[i].Direction = e->Name[i].Direction;
7215 pe->Name[i].Scale = e->Name[i].Scale;
7216 }
7217
7218 RemoveElement (e);
7219
7220 if (CopyPastebufferToLayout (mx, my))
7221 SetChangedFlag (true);
7222 }
7223
7224 /* Now reload footprint */
7225 element_cache = NULL;
7226 e = find_element_by_refdes (refdes);
7227
7228 old = ChangeElementText (PCB, PCB->Data, e, NAMEONPCB_INDEX, strdup (refdes));
7229 if (old)
7230 free(old);
7231 old = ChangeElementText (PCB, PCB->Data, e, VALUE_INDEX, strdup (value));
7232 if (old)
7233 free(old);
7234
7235 SET_FLAG (FOUNDFLAG, e);
7236
7237 #ifdef DEBUG
7238 printf(" ... Leaving ActionElementList.\n");
7239 #endif
7240
7241 return 0;
7242 }
7243
7244 /* ---------------------------------------------------------------- */
7245 static const char elementsetattr_syntax[] =
7246 N_("ElementSetAttr(refdes,name[,value])");
7247
7248 static const char elementsetattr_help[] =
7249 N_("Sets or clears an element-specific attribute.");
7250
7251 /* %start-doc actions elementsetattr
7252
7253 If a value is specified, the named attribute is added (if not already
7254 present) or changed (if it is) to the given value. If the value is
7255 not specified, the given attribute is removed if present.
7256
7257 %end-doc */
7258
7259 static int
ActionElementSetAttr(int argc,char ** argv,Coord x,Coord y)7260 ActionElementSetAttr (int argc, char **argv, Coord x, Coord y)
7261 {
7262 ElementType *e = NULL;
7263 char *refdes, *name, *value;
7264 AttributeType *attr;
7265
7266 if (argc < 2)
7267 {
7268 AFAIL (elementsetattr);
7269 }
7270
7271 refdes = argv[0];
7272 name = argv[1];
7273 value = ARG(2);
7274
7275 ELEMENT_LOOP (PCB->Data);
7276 {
7277 if (NSTRCMP (refdes, NAMEONPCB_NAME (element)) == 0)
7278 {
7279 e = element;
7280 break;
7281 }
7282 }
7283 END_LOOP;
7284
7285 if (!e)
7286 {
7287 Message(_("Cannot change attribute of %s - element not found\n"), refdes);
7288 return 1;
7289 }
7290
7291 attr = lookup_attr (&e->Attributes, name);
7292
7293 if (attr && value)
7294 {
7295 free (attr->value);
7296 attr->value = strdup (value);
7297 }
7298 if (attr && ! value)
7299 {
7300 delete_attr (& e->Attributes, attr);
7301 }
7302 if (!attr && value)
7303 {
7304 CreateNewAttribute (& e->Attributes, name, value);
7305 }
7306
7307 return 0;
7308 }
7309
7310 /* ---------------------------------------------------------------- */
7311 static const char execcommand_syntax[] = N_("ExecCommand(command)");
7312
7313 static const char execcommand_help[] = N_("Runs a command.");
7314
7315 /* %start-doc actions execcommand
7316
7317 Runs the given command, which is a system executable.
7318
7319 %end-doc */
7320
7321 static int
ActionExecCommand(int argc,char ** argv,Coord x,Coord y)7322 ActionExecCommand (int argc, char **argv, Coord x, Coord y)
7323 {
7324 char *command;
7325
7326 if (argc < 1)
7327 {
7328 AFAIL (execcommand);
7329 }
7330
7331 command = ARG(0);
7332
7333 if (system (command))
7334 return 1;
7335 return 0;
7336 }
7337
7338 /* ---------------------------------------------------------------- */
7339
7340 static int
pcb_spawnvp(char ** argv)7341 pcb_spawnvp (char **argv)
7342 {
7343 #ifdef HAVE__SPAWNVP
7344 int result = _spawnvp (_P_WAIT, argv[0], (const char * const *) argv);
7345 if (result == -1)
7346 return 1;
7347 else
7348 return 0;
7349 #else
7350 int pid;
7351 pid = fork ();
7352 if (pid < 0)
7353 {
7354 /* error */
7355 Message(_("Cannot fork!"));
7356 return 1;
7357 }
7358 else if (pid == 0)
7359 {
7360 /* Child */
7361 execvp (argv[0], argv);
7362 exit(1);
7363 }
7364 else
7365 {
7366 int rv;
7367 /* Parent */
7368 wait (&rv);
7369 }
7370 return 0;
7371 #endif
7372 }
7373
7374 /* ---------------------------------------------------------------- */
7375
7376 /*!
7377 * \brief Creates a new temporary file name.
7378 *
7379 * Hopefully the operating system provides a mkdtemp() function to
7380 * securily create a temporary directory with mode 0700.\n
7381 * If so then that directory is created and the returned string is made
7382 * up of the directory plus the name variable.\n
7383 * For example:\n
7384 *
7385 * tempfile_name_new ("myfile") might return
7386 * "/var/tmp/pcb.123456/myfile".
7387 *
7388 * If mkdtemp() is not available then 'name' is ignored and the
7389 * insecure tmpnam() function is used.
7390 *
7391 * Files/names created with tempfile_name_new() should be unlinked
7392 * with tempfile_unlink to make sure the temporary directory is also
7393 * removed when mkdtemp() is used.
7394 */
7395 static char *
tempfile_name_new(char * name)7396 tempfile_name_new (char * name)
7397 {
7398 char *tmpfile = NULL;
7399 #ifdef HAVE_MKDTEMP
7400 char *tmpdir, *mytmpdir;
7401 size_t len;
7402 #endif
7403
7404 assert ( name != NULL );
7405
7406 #ifdef HAVE_MKDTEMP
7407 #define TEMPLATE "pcb.XXXXXXXX"
7408
7409
7410 tmpdir = getenv ("TMPDIR");
7411
7412 /* FIXME -- what about win32? */
7413 if (tmpdir == NULL) {
7414 tmpdir = "/tmp";
7415 }
7416
7417 mytmpdir = (char *) malloc (sizeof(char) *
7418 (strlen (tmpdir) +
7419 1 +
7420 strlen (TEMPLATE) +
7421 1));
7422 if (mytmpdir == NULL) {
7423 fprintf (stderr, "%s(): malloc failed()\n", __FUNCTION__);
7424 exit (1);
7425 }
7426
7427 *mytmpdir = '\0';
7428 (void)strcat (mytmpdir, tmpdir);
7429 (void)strcat (mytmpdir, PCB_DIR_SEPARATOR_S);
7430 (void)strcat (mytmpdir, TEMPLATE);
7431 if (mkdtemp (mytmpdir) == NULL) {
7432 fprintf (stderr, "%s(): mkdtemp (\"%s\") failed\n", __FUNCTION__, mytmpdir);
7433 free (mytmpdir);
7434 return NULL;
7435 }
7436
7437
7438 len = strlen (mytmpdir) + /* the temp directory name */
7439 1 + /* the directory sep. */
7440 strlen (name) + /* the file name */
7441 1 /* the \0 termination */
7442 ;
7443
7444 tmpfile = (char *) malloc (sizeof (char) * len);
7445
7446 *tmpfile = '\0';
7447 (void)strcat (tmpfile, mytmpdir);
7448 (void)strcat (tmpfile, PCB_DIR_SEPARATOR_S);
7449 (void)strcat (tmpfile, name);
7450
7451 free (mytmpdir);
7452 #undef TEMPLATE
7453 #else
7454 /*
7455 * tmpnam() uses a static buffer so strdup() the result right away
7456 * in case someone decides to create multiple temp names.
7457 */
7458 tmpfile = strdup (tmpnam (NULL));
7459 #ifdef __WIN32__
7460 {
7461 /* Guile doesn't like \ separators */
7462 char *c;
7463 for (c = tmpfile; *c; c++)
7464 if (*c == '\\')
7465 *c = '/';
7466 }
7467 #endif
7468 #endif
7469
7470 return tmpfile;
7471 }
7472
7473 /* ---------------------------------------------------------------- */
7474
7475 /*!
7476 * \brief Unlink a temporary file.
7477 *
7478 * If we have mkdtemp() then our temp file lives in a temporary
7479 * directory and we need to remove that directory too.
7480 */
7481 static int
tempfile_unlink(char * name)7482 tempfile_unlink (char * name)
7483 {
7484 #ifdef DEBUG
7485 /* SDB says: Want to keep old temp files for examiniation when debugging */
7486 return 0;
7487 #else /* DEBUG */
7488
7489 #ifdef HAVE_MKDTEMP
7490 int e, rc2 = 0;
7491 char *dname;
7492
7493 unlink (name);
7494 /* it is possible that the file was never created so it is OK if the
7495 unlink fails */
7496
7497 /* now figure out the directory name to remove */
7498 e = strlen (name) - 1;
7499 while (e > 0 && name[e] != PCB_DIR_SEPARATOR_C) {e--;}
7500
7501 dname = strdup (name);
7502 dname[e] = '\0';
7503
7504 /*
7505 * at this point, e *should* point to the end of the directory part
7506 * but lets make sure.
7507 */
7508 if (e > 0) {
7509 rc2 = rmdir (dname);
7510 if (rc2 != 0) {
7511 perror (dname);
7512 }
7513
7514 } else {
7515 fprintf (stderr, _("%s(): Unable to determine temp directory name from the temp file\n"),
7516 __FUNCTION__);
7517 fprintf (stderr, "%s(): \"%s\"\n",
7518 __FUNCTION__, name);
7519 rc2 = -1;
7520 }
7521
7522 /* name was allocated with malloc */
7523 free (dname);
7524 free (name);
7525
7526 /*
7527 * FIXME - should also return -1 if the temp file exists and was not
7528 * removed.
7529 */
7530 if (rc2 != 0) {
7531 return -1;
7532 }
7533
7534 #else /* HAVE_MKDTEMP */
7535 int rc = unlink (name);
7536
7537 if (rc != 0) {
7538 fprintf (stderr, _("Failed to unlink \"%s\"\n"), name);
7539 free (name);
7540 return rc;
7541 }
7542 free (name);
7543
7544 #endif /* HAVE_MKDTEMP */
7545 #endif /* DEBUG */
7546
7547 return 0;
7548 }
7549
7550 /* ---------------------------------------------------------------- */
7551 static const char import_syntax[] =
7552 N_("Import()\n"
7553 "Import([gnetlist|make[,source,source,...]])\n"
7554 "Import(setnewpoint[,(mark|center|X,Y)])\n"
7555 "Import(setdisperse,D,units)\n");
7556
7557 static const char import_help[] = N_("Import schematics.");
7558
7559 /* %start-doc actions Import
7560
7561 Imports element and netlist data from the schematics (or some other
7562 source). The first parameter, which is optional, is the mode. If not
7563 specified, the @code{import::mode} attribute in the PCB is used.
7564 @code{gnetlist} means gnetlist is used to obtain the information from
7565 the schematics. @code{make} invokes @code{make}, assuming the user
7566 has a @code{Makefile} in the current directory. The @code{Makefile}
7567 will be invoked with the following variables set:
7568
7569 @table @code
7570
7571 @item PCB
7572 The name of the .pcb file
7573
7574 @item SRCLIST
7575 A space-separated list of source files
7576
7577 @item OUT
7578 The name of the file in which to put the command script, which may
7579 contain any @pcb{} actions. By default, this is a temporary file
7580 selected by @pcb{}, but if you specify an @code{import::outfile}
7581 attribute, that file name is used instead (and not automatically
7582 deleted afterwards).
7583
7584 @end table
7585
7586 The target specified to be built is the first of these that apply:
7587
7588 @itemize @bullet
7589
7590 @item
7591 The target specified by an @code{import::target} attribute.
7592
7593 @item
7594 The output file specified by an @code{import::outfile} attribute.
7595
7596 @item
7597 If nothing else is specified, the target is @code{pcb_import}.
7598
7599 @end itemize
7600
7601 If you specify an @code{import::makefile} attribute, then "-f <that
7602 file>" will be added to the command line.
7603
7604 If you specify the mode, you may also specify the source files
7605 (schematics). If you do not specify any, the list of schematics is
7606 obtained by reading the @code{import::src@var{N}} attributes (like
7607 @code{import::src0}, @code{import::src1}, etc).
7608
7609 For compatibility with future extensions to the import file format,
7610 the generated file @emph{must not} start with the two characters
7611 @code{#%}.
7612
7613 If a temporary file is needed the @code{TMPDIR} environment variable
7614 is used to select its location.
7615
7616 Note that the programs @code{gnetlist} and @code{make} may be
7617 overridden by the user via the @code{make-program} and @code{gnetlist}
7618 @code{pcb} settings (i.e. in @code{~/.pcb/settings} or on the command
7619 line).
7620
7621 If @pcb{} cannot determine which schematic(s) to import from, the GUI
7622 is called to let user choose (see @code{ImportGUI()}).
7623
7624 Note that Import() doesn't delete anything - after an Import, elements
7625 which shouldn't be on the board are selected and may be removed once
7626 it's determined that the deletion is appropriate.
7627
7628 If @code{Import()} is called with @code{setnewpoint}, then the location
7629 of new components can be specified. This is where parts show up when
7630 they're added to the board. The default is the center of the board.
7631
7632 @table @code
7633
7634 @item Import(setnewpoint)
7635
7636 Prompts the user to click on the board somewhere, uses that point. If
7637 called by a hotkey, uses the current location of the crosshair.
7638
7639 @item Import(setnewpoint,mark)
7640
7641 Uses the location of the mark. If no mark is present, the point is
7642 not changed.
7643
7644 @item Import(setnewpoint,center)
7645
7646 Resets the point to the center of the board.
7647
7648 @item Import(setnewpoint,X,Y,units)
7649
7650 Sets the point to the specific coordinates given. Example:
7651 @code{Import(setnewpoint,50,25,mm)}
7652
7653 @end table
7654
7655 Note that the X and Y locations are stored in attributes named
7656 @code{import::newX} and @code{import::newY} so you could change them
7657 manually if you wished.
7658
7659 Calling @code{Import(setdisperse,D,units)} sets how much the newly
7660 placed elements are dispersed relative to the set point. For example,
7661 @code{Import(setdisperse,10,mm)} will offset each part randomly up to
7662 10mm away from the point. The default dispersion is 1/10th of the
7663 smallest board dimension. Dispersion is saved in the
7664 @code{import::disperse} attribute.
7665
7666 %end-doc */
7667
7668 static int
ActionImport(int argc,char ** argv,Coord x,Coord y)7669 ActionImport (int argc, char **argv, Coord x, Coord y)
7670 {
7671 char *mode;
7672 char **sources = NULL;
7673 int nsources = 0;
7674
7675 #ifdef DEBUG
7676 printf("ActionImport: =========== Entering ActionImport ============\n");
7677 #endif
7678
7679 mode = ARG (0);
7680
7681 if (mode && strcasecmp (mode, "setdisperse") == 0)
7682 {
7683 char *ds, *units;
7684 char buf[50];
7685
7686 ds = ARG (1);
7687 units = ARG (2);
7688 if (!ds)
7689 {
7690 const char *as = AttributeGet (PCB, "import::disperse");
7691 ds = gui->prompt_for(_("Enter dispersion:"), as ? as : "0");
7692 }
7693 if (units)
7694 {
7695 sprintf(buf, "%s%s", ds, units);
7696 AttributePut (PCB, "import::disperse", buf);
7697 }
7698 else
7699 AttributePut (PCB, "import::disperse", ds);
7700 if (ARG (1) == NULL)
7701 free (ds);
7702 return 0;
7703 }
7704
7705 if (mode && strcasecmp (mode, "setnewpoint") == 0)
7706 {
7707 const char *xs, *ys, *units;
7708 Coord x, y;
7709 char buf[50];
7710
7711 xs = ARG (1);
7712 ys = ARG (2);
7713 units = ARG (3);
7714
7715 if (!xs)
7716 {
7717 gui->get_coords (_("Click on a location"), &x, &y);
7718 }
7719 else if (strcasecmp (xs, "center") == 0)
7720 {
7721 AttributeRemove (PCB, "import::newX");
7722 AttributeRemove (PCB, "import::newY");
7723 return 0;
7724 }
7725 else if (strcasecmp (xs, "mark") == 0)
7726 {
7727 if (!Marked.status)
7728 return 0;
7729
7730 x = Marked.X;
7731 y = Marked.Y;
7732 }
7733 else if (ys)
7734 {
7735 x = GetValue (xs, units, NULL);
7736 y = GetValue (ys, units, NULL);
7737 }
7738 else
7739 {
7740 Message (_("Bad syntax for Import(setnewpoint)"));
7741 return 1;
7742 }
7743
7744 pcb_snprintf (buf, sizeof (buf), "%$ms", x);
7745 AttributePut (PCB, "import::newX", buf);
7746 pcb_snprintf (buf, sizeof (buf), "%$ms", y);
7747 AttributePut (PCB, "import::newY", buf);
7748 return 0;
7749 }
7750
7751 if (! mode)
7752 mode = AttributeGet (PCB, "import::mode");
7753 if (! mode)
7754 mode = "gnetlist";
7755
7756 if (argc > 1)
7757 {
7758 sources = argv + 1;
7759 nsources = argc - 1;
7760 }
7761
7762 if (! sources)
7763 {
7764 char sname[40];
7765 char *src;
7766
7767 nsources = -1;
7768 do {
7769 nsources ++;
7770 sprintf(sname, "import::src%d", nsources);
7771 src = AttributeGet (PCB, sname);
7772 } while (src);
7773
7774 if (nsources > 0)
7775 {
7776 sources = (char **) malloc ((nsources + 1) * sizeof (char *));
7777 nsources = -1;
7778 do {
7779 nsources ++;
7780 sprintf(sname, "import::src%d", nsources);
7781 src = AttributeGet (PCB, sname);
7782 sources[nsources] = src;
7783 } while (src);
7784 }
7785 }
7786
7787 if (! sources)
7788 {
7789 /* Replace .pcb with .sch and hope for the best. */
7790 char *pcbname = PCB->Filename;
7791 char *schname;
7792 char *dot, *slash, *bslash;
7793
7794 if (!pcbname)
7795 return hid_action("ImportGUI");
7796
7797 schname = (char *) malloc (strlen(pcbname) + 5);
7798 strcpy (schname, pcbname);
7799 dot = strchr (schname, '.');
7800 slash = strchr (schname, '/');
7801 bslash = strchr (schname, '\\');
7802 if (dot && slash && dot < slash)
7803 dot = NULL;
7804 if (dot && bslash && dot < bslash)
7805 dot = NULL;
7806 if (dot)
7807 *dot = 0;
7808 strcat (schname, ".sch");
7809
7810 if (access (schname, F_OK))
7811 {
7812 free (schname);
7813 return hid_action("ImportGUI");
7814 }
7815
7816 sources = (char **) malloc (2 * sizeof (char *));
7817 sources[0] = schname;
7818 sources[1] = NULL;
7819 nsources = 1;
7820 }
7821
7822 if (strcasecmp (mode, "gnetlist") == 0)
7823 {
7824 char *tmpfile = tempfile_name_new ("gnetlist_output");
7825 char **cmd;
7826 int i;
7827
7828 if (tmpfile == NULL) {
7829 Message (_("Could not create temp file"));
7830 return 1;
7831 }
7832
7833 cmd = (char **) malloc ((7 + nsources) * sizeof (char *));
7834 cmd[0] = Settings.GnetlistProgram;
7835 cmd[1] = "-g";
7836 cmd[2] = "pcbfwd";
7837 cmd[3] = "-o";
7838 cmd[4] = tmpfile;
7839 cmd[5] = "--";
7840 for (i=0; i<nsources; i++)
7841 cmd[6+i] = sources[i];
7842 cmd[6+nsources] = NULL;
7843
7844 #ifdef DEBUG
7845 printf("ActionImport: =========== About to run gnetlist ============\n");
7846 printf("%s %s %s %s %s %s %s ...\n",
7847 cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6]);
7848 #endif
7849
7850 if (pcb_spawnvp (cmd))
7851 {
7852 unlink (tmpfile);
7853 return 1;
7854 }
7855
7856 #ifdef DEBUG
7857 printf("ActionImport: =========== About to run ActionExecuteFile, file = %s ============\n", tmpfile);
7858 #endif
7859
7860 cmd[0] = tmpfile;
7861 cmd[1] = NULL;
7862 ActionExecuteFile (1, cmd, 0, 0);
7863
7864 free (cmd);
7865 tempfile_unlink (tmpfile);
7866 }
7867 else if (strcasecmp (mode, "make") == 0)
7868 {
7869 int must_free_tmpfile = 0;
7870 char *tmpfile;
7871 char *cmd[10];
7872 int i;
7873 char *srclist;
7874 int srclen;
7875 char *user_outfile = NULL;
7876 char *user_makefile = NULL;
7877 char *user_target = NULL;
7878
7879
7880 user_outfile = AttributeGet (PCB, "import::outfile");
7881 user_makefile = AttributeGet (PCB, "import::makefile");
7882 user_target = AttributeGet (PCB, "import::target");
7883 if (user_outfile && !user_target)
7884 user_target = user_outfile;
7885
7886 if (user_outfile)
7887 tmpfile = user_outfile;
7888 else
7889 {
7890 tmpfile = tempfile_name_new ("gnetlist_output");
7891 if (tmpfile == NULL) {
7892 Message (_("Could not create temp file"));
7893 free (sources);
7894 return 1;
7895 }
7896 must_free_tmpfile = 1;
7897 }
7898
7899 srclen = sizeof("SRCLIST=") + 2;
7900 for (i=0; i<nsources; i++)
7901 srclen += strlen (sources[i]) + 2;
7902 srclist = (char *) malloc (srclen);
7903 strcpy (srclist, "SRCLIST=");
7904 for (i=0; i<nsources; i++)
7905 {
7906 if (i)
7907 strcat (srclist, " ");
7908 strcat (srclist, sources[i]);
7909 }
7910
7911 cmd[0] = Settings.MakeProgram;
7912 cmd[1] = "-s";
7913 cmd[2] = Concat ("PCB=", PCB->Filename, NULL);
7914 cmd[3] = srclist;
7915 cmd[4] = Concat ("OUT=", tmpfile, NULL);
7916 i = 5;
7917 if (user_makefile)
7918 {
7919 cmd[i++] = "-f";
7920 cmd[i++] = user_makefile;
7921 }
7922 cmd[i++] = user_target ? user_target : (char *)"pcb_import";
7923 cmd[i++] = NULL;
7924
7925 if (pcb_spawnvp (cmd))
7926 {
7927 if (must_free_tmpfile)
7928 unlink (tmpfile);
7929 free (cmd[2]);
7930 free (cmd[3]);
7931 free (cmd[4]);
7932 return 1;
7933 }
7934
7935 cmd[0] = tmpfile;
7936 cmd[1] = NULL;
7937 ActionExecuteFile (1, cmd, 0, 0);
7938
7939 free (cmd[2]);
7940 free (cmd[3]);
7941 free (cmd[4]);
7942 if (must_free_tmpfile)
7943 tempfile_unlink (tmpfile);
7944 }
7945 else
7946 {
7947 Message (_("Unknown import mode: %s\n"), mode);
7948 return 1;
7949 }
7950
7951 DeleteRats (false);
7952 AddAllRats (false, NULL);
7953
7954 #ifdef DEBUG
7955 printf("ActionImport: =========== Leaving ActionImport ============\n");
7956 #endif
7957
7958 return 0;
7959 }
7960
7961 /* ------------------------------------------------------------ */
7962
7963 static const char attributes_syntax[] =
7964 N_("Attributes(Layout|Layer|Element)\n"
7965 "Attributes(Layer,layername)");
7966
7967 static const char attributes_help[] =
7968 N_("Let the user edit the attributes of the layout, current or given\n"
7969 "layer, or selected element.");
7970
7971 /* %start-doc actions Attributes
7972
7973 This just pops up a dialog letting the user edit the attributes of the
7974 pcb, an element, or a layer.
7975
7976 %end-doc */
7977
7978
7979 static int
ActionAttributes(int argc,char ** argv,Coord x,Coord y)7980 ActionAttributes (int argc, char **argv, Coord x, Coord y)
7981 {
7982 char *function = ARG (0);
7983 char *layername = ARG (1);
7984 char *buf;
7985
7986 if (!function)
7987 AFAIL (attributes);
7988
7989 if (!gui->edit_attributes)
7990 {
7991 Message (_("This GUI doesn't support Attribute Editing\n"));
7992 return 1;
7993 }
7994
7995 switch (GetFunctionID (function))
7996 {
7997 case F_Layout:
7998 {
7999 gui->edit_attributes(_("Layout Attributes"), &(PCB->Attributes));
8000 return 0;
8001 }
8002
8003 case F_Layer:
8004 {
8005 LayerType *layer = CURRENT;
8006 if (layername)
8007 {
8008 int i;
8009 layer = NULL;
8010 for (i=0; i<max_copper_layer; i++)
8011 if (strcmp (PCB->Data->Layer[i].Name, layername) == 0)
8012 {
8013 layer = & (PCB->Data->Layer[i]);
8014 break;
8015 }
8016 if (layer == NULL)
8017 {
8018 Message (_("No layer named %s\n"), layername);
8019 return 1;
8020 }
8021 }
8022 buf = (char *) malloc (strlen (layer->Name) +
8023 strlen (_("Layer %s Attributes")));
8024 sprintf (buf, _("Layer %s Attributes"), layer->Name);
8025 gui->edit_attributes(buf, &(layer->Attributes));
8026 free (buf);
8027 return 0;
8028 }
8029
8030 case F_Element:
8031 {
8032 int n_found = 0;
8033 ElementType *e = NULL;
8034 ELEMENT_LOOP (PCB->Data);
8035 {
8036 if (TEST_FLAG (SELECTEDFLAG, element))
8037 {
8038 e = element;
8039 n_found ++;
8040 }
8041 }
8042 END_LOOP;
8043 if (n_found > 1)
8044 {
8045 Message (_("Too many elements selected\n"));
8046 return 1;
8047 }
8048 if (n_found == 0)
8049 {
8050 void *ptrtmp;
8051 gui->get_coords (_("Click on an element"), &x, &y);
8052 if ((SearchScreen
8053 (x, y, ELEMENT_TYPE, &ptrtmp,
8054 &ptrtmp, &ptrtmp)) != NO_TYPE)
8055 e = (ElementType *) ptrtmp;
8056 else
8057 {
8058 Message (_("No element found there\n"));
8059 return 1;
8060 }
8061 }
8062
8063 if (NAMEONPCB_NAME(e))
8064 {
8065 buf = (char *) malloc (strlen (NAMEONPCB_NAME(e)) +
8066 strlen (_("Element %s Attributes")));
8067 sprintf(buf, _("Element %s Attributes"), NAMEONPCB_NAME(e));
8068 }
8069 else
8070 {
8071 buf = strdup (_("Unnamed Element Attributes"));
8072 }
8073 gui->edit_attributes(buf, &(e->Attributes));
8074 free (buf);
8075 break;
8076 }
8077
8078 default:
8079 AFAIL (attributes);
8080 }
8081
8082 return 0;
8083 }
8084
8085 /* --------------------------------------------------------------------------- */
8086
8087 static const char setvialayers_syntax[] =
8088 N_("SetViaLayers(Object|SelectedVias|Selected[,ThroughHole|TH])\n"
8089 "SetViaLayers(Object|SelectedVias|Selected,from,to)\n"
8090 "SetViaLayers(Object|SelectedVias|Selected,[c|-|from],[c|-|to])"
8091 );
8092
8093 static const char setvialayers_help[] =
8094 N_("Sets starting and ending layer for burried/blind/standard vias.");
8095
8096 /* %start-doc actions setvialayers
8097
8098 Specifies layers, which are connected by via.
8099
8100 @table @code
8101
8102 @item TH|ThroughHole
8103 The vias will be set as through-hole, connecting all layers
8104
8105 @item from
8106 layer name or layer number of the first layer to be connected by via; "-" stands for unchanged, "c" stands for currently selected layer
8107
8108 @item to
8109 layer name or layer number of the last layer to be connected by via; "-" stands for unchanged, "c" stands for currently selected layer
8110
8111 @end table
8112
8113 If no parameter us used, dialog is displayed (if implemented in the respective GUI HID).
8114
8115
8116 %end-doc */
8117
8118 static bool
identify_layer(char * layer_name,Cardinal * layer_no)8119 identify_layer (char *layer_name, Cardinal *layer_no)
8120 {
8121 int layer;
8122
8123 if (strcmp (layer_name, "-") == 0)
8124 {
8125 *layer_no = -1;
8126 return true;
8127 }
8128
8129 if (strcmp (layer_name, "c") == 0)
8130 {
8131 if ((unsigned int)INDEXOFCURRENT < max_copper_layer)
8132 {
8133 *layer_no = INDEXOFCURRENT;
8134 return true;
8135 }
8136 }
8137
8138 layer = SearchLayerByName (PCB->Data, layer_name);
8139 if (layer == -1)
8140 {
8141 if (sscanf (layer_name, "%d", &layer) != 1)
8142 layer = -1;
8143 }
8144
8145 if (layer != -1)
8146 *layer_no = layer;
8147
8148 return (layer != -1);
8149 }
8150
8151 static int
ActionSetViaLayers(int argc,char ** argv,Coord x,Coord y)8152 ActionSetViaLayers (int argc, char **argv, Coord x, Coord y)
8153 {
8154 char *function = ARG (0);
8155 char *layername_from = ARG (1);
8156 char *layername_to = ARG (2);
8157 Cardinal layer_from ;
8158 Cardinal layer_to = -1;
8159
8160 if (!function)
8161 AFAIL (setvialayers);
8162
8163 if ( /* !gui->edit_attributes &&*/ argc < 2)
8164 {
8165 Message (_("This GUI doesn't support Via Layers editing\n"));
8166 return 1;
8167 }
8168
8169 if (GetFunctionID (layername_from) == F_ThroughHole)
8170 {
8171 layer_from = 0;
8172 layer_to = 0;
8173 }
8174 else
8175 {
8176 if (!identify_layer (layername_from, &layer_from)
8177 || !identify_layer (layername_to, &layer_to))
8178 {
8179 Message (_("Sorry, wrong layers specified.\n"));
8180 return 1;
8181 }
8182 }
8183
8184 /* ensure that layer_from < layer_to */
8185 if (layer_from != -1
8186 && layer_to != -1
8187 && layer_from > layer_to)
8188 {
8189 int tmp;
8190
8191 tmp = layer_from;
8192 layer_from = layer_to;
8193 layer_to = tmp;
8194 }
8195
8196 if (layer_to != -1)
8197 layer_to = min (layer_to, max_copper_layer-1);
8198
8199 switch (GetFunctionID (function))
8200 {
8201 case F_Object:
8202 {
8203 int type;
8204 void *ptr1, *ptr2, *ptr3;
8205
8206 if ((type =
8207 SearchScreen (Crosshair.X, Crosshair.Y, VIA_TYPE,
8208 &ptr1, &ptr2, &ptr3)) != NO_TYPE)
8209 {
8210 if (TEST_FLAG (LOCKFLAG, (PinType *) ptr1))
8211 Message (_("Sorry, the object is locked\n"));
8212 else
8213 {
8214 if (ChangeObjectViaLayers (ptr1, ptr2, ptr3, layer_from, layer_to))
8215 {
8216 SetChangedFlag (true);
8217 }
8218 }
8219 }
8220 break;
8221 case F_SelectedVias:
8222 case F_Selected:
8223 if (ChangeSelectedViaLayers (layer_from, layer_to))
8224 {
8225 SetChangedFlag (true);
8226 }
8227 break;
8228 }
8229 }
8230
8231 return 0;
8232 }
8233 /* --------------------------------------------------------------------------- */
8234
8235 HID_Action action_action_list[] = {
8236 {"AddRats", 0, ActionAddRats,
8237 addrats_help, addrats_syntax}
8238 ,
8239 {"Attributes", 0, ActionAttributes,
8240 attributes_help, attributes_syntax}
8241 ,
8242 {"Atomic", 0, ActionAtomic,
8243 atomic_help, atomic_syntax}
8244 ,
8245 {"AutoPlaceSelected", 0, ActionAutoPlaceSelected,
8246 autoplace_help, autoplace_syntax}
8247 ,
8248 {"AutoRoute", 0, ActionAutoRoute,
8249 autoroute_help, autoroute_syntax}
8250 ,
8251 {"ChangeClearSize", 0, ActionChangeClearSize,
8252 changeclearsize_help, changeclearsize_syntax}
8253 ,
8254 {"ChangeDrillSize", 0, ActionChange2ndSize,
8255 changedrillsize_help, changedrillsize_syntax}
8256 ,
8257 {"ChangeHole", 0, ActionChangeHole,
8258 changehold_help, changehold_syntax}
8259 ,
8260 {"ChangeJoin", 0, ActionChangeJoin,
8261 changejoin_help, changejoin_syntax}
8262 ,
8263 {"ChangeName", 0, ActionChangeName,
8264 changename_help, changename_syntax}
8265 ,
8266 {"ChangePaste", 0, ActionChangePaste,
8267 changepaste_help, changepaste_syntax}
8268 ,
8269 {"ChangePinName", 0, ActionChangePinName,
8270 changepinname_help, changepinname_syntax}
8271 ,
8272 {"ChangeSize", 0, ActionChangeSize,
8273 changesize_help, changesize_syntax}
8274 ,
8275 {"ChangeSquare", 0, ActionChangeSquare,
8276 changesquare_help, changesquare_syntax}
8277 ,
8278 {"ChangeOctagon", 0, ActionChangeOctagon,
8279 changeoctagon_help, changeoctagon_syntax}
8280 ,
8281 {"ClearSquare", 0, ActionClearSquare,
8282 clearsquare_help, clearsquare_syntax}
8283 ,
8284 {"ClearOctagon", 0, ActionClearOctagon,
8285 clearoctagon_help, clearoctagon_syntax}
8286 ,
8287 {"Connection", 0, ActionConnection,
8288 connection_help, connection_syntax}
8289 ,
8290 {"Delete", 0, ActionDelete,
8291 delete_help, delete_syntax}
8292 ,
8293 {"DeleteRats", 0, ActionDeleteRats,
8294 deleterats_help, deleterats_syntax}
8295 ,
8296 {"DisperseElements", 0, ActionDisperseElements,
8297 disperseelements_help, disperseelements_syntax}
8298 ,
8299 {"Display", 0, ActionDisplay,
8300 display_help, display_syntax}
8301 ,
8302 {"DumpLibrary", 0, ActionDumpLibrary,
8303 dumplibrary_help, dumplibrary_syntax}
8304 ,
8305 {"ExecuteFile", 0, ActionExecuteFile,
8306 executefile_help, executefile_syntax}
8307 ,
8308 {"Flip", N_("Click on Object or Flip Point"), ActionFlip,
8309 flip_help, flip_syntax}
8310 ,
8311 {"LoadFrom", 0, ActionLoadFrom,
8312 loadfrom_help, loadfrom_syntax}
8313 ,
8314 {"MarkCrosshair", 0, ActionMarkCrosshair,
8315 markcrosshair_help, markcrosshair_syntax}
8316 ,
8317 {"Message", 0, ActionMessage,
8318 message_help, message_syntax}
8319 ,
8320 {"MinMaskGap", 0, ActionMinMaskGap,
8321 minmaskgap_help, minmaskgap_syntax}
8322 ,
8323 {"MinClearGap", 0, ActionMinClearGap,
8324 mincleargap_help, mincleargap_syntax}
8325 ,
8326 {"Mode", 0, ActionMode,
8327 mode_help, mode_syntax}
8328 ,
8329 {"MorphPolygon", 0, ActionMorphPolygon,
8330 morphpolygon_help, morphpolygon_syntax}
8331 ,
8332 {"PasteBuffer", 0, ActionPasteBuffer,
8333 pastebuffer_help, pastebuffer_syntax}
8334 ,
8335 {"Quit", 0, ActionQuit,
8336 quit_help, quit_syntax}
8337 ,
8338 {"RemoveSelected", 0, ActionRemoveSelected,
8339 removeselected_help, removeselected_syntax}
8340 ,
8341 {"Renumber", 0, ActionRenumber,
8342 renumber_help, renumber_syntax}
8343 ,
8344 {"RipUp", 0, ActionRipUp,
8345 ripup_help, ripup_syntax}
8346 ,
8347 {"Select", 0, ActionSelect,
8348 select_help, select_syntax}
8349 ,
8350 {"Unselect", 0, ActionUnselect,
8351 unselect_help, unselect_syntax}
8352 ,
8353 {"SaveSettings", 0, ActionSaveSettings,
8354 savesettings_help, savesettings_syntax}
8355 ,
8356 {"SaveTo", 0, ActionSaveTo,
8357 saveto_help, saveto_syntax}
8358 ,
8359 {"SetSquare", 0, ActionSetSquare,
8360 setsquare_help, setsquare_syntax}
8361 ,
8362 {"SetOctagon", 0, ActionSetOctagon,
8363 setoctagon_help, setoctagon_syntax}
8364 ,
8365 {"SetThermal", 0, ActionSetThermal,
8366 setthermal_help, setthermal_syntax}
8367 ,
8368 {"SetValue", 0, ActionSetValue,
8369 setvalue_help, setvalue_syntax}
8370 ,
8371 {"ToggleHideName", 0, ActionToggleHideName,
8372 togglehidename_help, togglehidename_syntax}
8373 ,
8374 {"Undo", 0, ActionUndo,
8375 undo_help, undo_syntax}
8376 ,
8377 {"Redo", 0, ActionRedo,
8378 redo_help, redo_syntax}
8379 ,
8380 {"SetSame", N_("Select item to use attributes from"), ActionSetSame,
8381 setsame_help, setsame_syntax}
8382 ,
8383 {"SetFlag", 0, ActionSetFlag,
8384 setflag_help, setflag_syntax}
8385 ,
8386 {"ClrFlag", 0, ActionClrFlag,
8387 clrflag_help, clrflag_syntax}
8388 ,
8389 {"ChangeFlag", 0, ActionChangeFlag,
8390 changeflag_help, changeflag_syntax}
8391 ,
8392 {"Polygon", 0, ActionPolygon,
8393 polygon_help, polygon_syntax}
8394 ,
8395 {"RouteStyle", 0, ActionRouteStyle,
8396 routestyle_help, routestyle_syntax}
8397 ,
8398 {"MoveObject", N_("Select an Object"), ActionMoveObject,
8399 moveobject_help, moveobject_syntax}
8400 ,
8401 {"MoveToCurrentLayer", 0, ActionMoveToCurrentLayer,
8402 movetocurrentlayer_help, movetocurrentlayer_syntax}
8403 ,
8404 {"New", 0, ActionNew,
8405 new_help, new_syntax}
8406 ,
8407 {"pscalib", 0, ActionPSCalib}
8408 ,
8409 {"ElementList", 0, ActionElementList,
8410 elementlist_help, elementlist_syntax}
8411 ,
8412 {"ElementSetAttr", 0, ActionElementSetAttr,
8413 elementsetattr_help, elementsetattr_syntax}
8414 ,
8415 {"ExecCommand", 0, ActionExecCommand,
8416 execcommand_help, execcommand_syntax}
8417 ,
8418 {"Import", 0, ActionImport,
8419 import_help, import_syntax}
8420 ,
8421 {"SetViaLayers", 0, ActionSetViaLayers,
8422 setvialayers_help, setvialayers_syntax}
8423 ,
8424 };
8425
8426 REGISTER_ACTIONS (action_action_list)
8427
8428