4 * \brief Functions used by paste- and move/copy buffer.
8 * <h1><b>Copyright.</b></h1>\n
10 * PCB, interactive printed circuit board design
12 * Copyright (C) 1994,1995,1996, 2005 Thomas Nau
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 * Contact addresses for paper mail and Email:
29 * Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
30 * Thomas.Nau@rz.uni-ulm.de
47 #include "crosshair.h"
63 #ifdef HAVE_LIBDMALLOC
67 /* ---------------------------------------------------------------------------
68 * some local prototypes
70 static void *AddViaToBuffer (PinType
*);
71 static void *AddLineToBuffer (LayerType
*, LineType
*);
72 static void *AddArcToBuffer (LayerType
*, ArcType
*);
73 static void *AddRatToBuffer (RatType
*);
74 static void *AddTextToBuffer (LayerType
*, TextType
*);
75 static void *AddPolygonToBuffer (LayerType
*, PolygonType
*);
76 static void *AddElementToBuffer (ElementType
*);
77 static void *MoveViaToBuffer (PinType
*);
78 static void *MoveLineToBuffer (LayerType
*, LineType
*);
79 static void *MoveArcToBuffer (LayerType
*, ArcType
*);
80 static void *MoveRatToBuffer (RatType
*);
81 static void *MoveTextToBuffer (LayerType
*, TextType
*);
82 static void *MovePolygonToBuffer (LayerType
*, PolygonType
*);
83 static void *MoveElementToBuffer (ElementType
*);
84 static void SwapBuffer (BufferType
*);
86 #define ARG(n) (argc > (n) ? argv[n] : 0)
88 /* ---------------------------------------------------------------------------
89 * some local identifiers
91 static DataType
*Dest
, *Source
;
93 static ObjectFunctionType AddBufferFunctions
= {
106 }, MoveBufferFunctions
=
114 NULL
, NULL
, NULL
, NULL
, NULL
, MoveArcToBuffer
, MoveRatToBuffer
};
116 static int ExtraFlag
= 0;
119 * \brief Copies a via to paste buffer.
122 AddViaToBuffer (PinType
*Via
)
124 return (CreateNewVia (Dest
, Via
->X
, Via
->Y
, Via
->Thickness
, Via
->Clearance
,
125 Via
->Mask
, Via
->DrillingHole
, Via
->Name
,
126 MaskFlags (Via
->Flags
, NOCOPY_FLAGS
| ExtraFlag
)));
130 * \brief Copies a rat-line to paste buffer.
133 AddRatToBuffer (RatType
*Rat
)
135 return (CreateNewRat (Dest
, Rat
->Point1
.X
, Rat
->Point1
.Y
,
136 Rat
->Point2
.X
, Rat
->Point2
.Y
, Rat
->group1
,
137 Rat
->group2
, Rat
->Thickness
,
138 MaskFlags (Rat
->Flags
, NOCOPY_FLAGS
| ExtraFlag
)));
142 * \brief Copies a line to buffer.
145 AddLineToBuffer (LayerType
*Layer
, LineType
*Line
)
148 LayerType
*layer
= &Dest
->Layer
[GetLayerNumber (Source
, Layer
)];
150 line
= CreateNewLineOnLayer (layer
, Line
->Point1
.X
, Line
->Point1
.Y
,
151 Line
->Point2
.X
, Line
->Point2
.Y
,
152 Line
->Thickness
, Line
->Clearance
,
153 MaskFlags (Line
->Flags
,
154 NOCOPY_FLAGS
| ExtraFlag
));
155 if (line
&& Line
->Number
)
156 line
->Number
= strdup (Line
->Number
);
161 * \brief Copies an arc to buffer.
164 AddArcToBuffer (LayerType
*Layer
, ArcType
*Arc
)
166 LayerType
*layer
= &Dest
->Layer
[GetLayerNumber (Source
, Layer
)];
168 return (CreateNewArcOnLayer (layer
, Arc
->X
, Arc
->Y
,
169 Arc
->Width
, Arc
->Height
, Arc
->StartAngle
, Arc
->Delta
,
170 Arc
->Thickness
, Arc
->Clearance
,
171 MaskFlags (Arc
->Flags
,
172 NOCOPY_FLAGS
| ExtraFlag
)));
176 * \brief Copies a text to buffer.
179 AddTextToBuffer (LayerType
*Layer
, TextType
*Text
)
181 LayerType
*layer
= &Dest
->Layer
[GetLayerNumber (Source
, Layer
)];
183 return (CreateNewText (layer
, &PCB
->Font
, Text
->X
, Text
->Y
,
184 Text
->Direction
, Text
->Scale
, Text
->TextString
,
185 MaskFlags (Text
->Flags
, ExtraFlag
)));
189 * \brief Copies a polygon to buffer.
192 AddPolygonToBuffer (LayerType
*Layer
, PolygonType
*Polygon
)
194 LayerType
*layer
= &Dest
->Layer
[GetLayerNumber (Source
, Layer
)];
195 PolygonType
*polygon
;
197 polygon
= CreateNewPolygon (layer
, Polygon
->Flags
);
198 CopyPolygonLowLevel (polygon
, Polygon
);
200 /* Update the polygon r-tree. Unlike similarly named functions for
201 * other objects, CreateNewPolygon does not do this as it creates a
202 * skeleton polygon object, which won't have correct bounds.
204 if (!layer
->polygon_tree
)
205 layer
->polygon_tree
= r_create_tree (NULL
, 0, 0);
206 r_insert_entry (layer
->polygon_tree
, (BoxType
*)polygon
, 0);
208 CLEAR_FLAG (NOCOPY_FLAGS
| ExtraFlag
, polygon
);
213 * \brief Copies a element to buffer.
216 AddElementToBuffer (ElementType
*Element
)
218 return CopyElementLowLevel (Dest
, Element
, false, 0, 0, NOCOPY_FLAGS
| ExtraFlag
);
222 * \brief Moves a via to paste buffer without allocating memory for the
226 MoveViaToBuffer (PinType
*via
)
228 RestoreToPolygon (Source
, VIA_TYPE
, via
, via
);
230 r_delete_entry (Source
->via_tree
, (BoxType
*) via
);
231 Source
->Via
= g_list_remove (Source
->Via
, via
);
233 Dest
->Via
= g_list_append (Dest
->Via
, via
);
236 CLEAR_FLAG (WARNFLAG
| NOCOPY_FLAGS
, via
);
239 Dest
->via_tree
= r_create_tree (NULL
, 0, 0);
240 r_insert_entry (Dest
->via_tree
, (BoxType
*)via
, 0);
241 ClearFromPolygon (Dest
, VIA_TYPE
, via
, via
);
246 * \brief Moves a rat-line to paste buffer.
249 MoveRatToBuffer (RatType
*rat
)
251 r_delete_entry (Source
->rat_tree
, (BoxType
*)rat
);
253 Source
->Rat
= g_list_remove (Source
->Rat
, rat
);
255 Dest
->Rat
= g_list_append (Dest
->Rat
, rat
);
258 CLEAR_FLAG (NOCOPY_FLAGS
, rat
);
261 Dest
->rat_tree
= r_create_tree (NULL
, 0, 0);
262 r_insert_entry (Dest
->rat_tree
, (BoxType
*)rat
, 0);
267 * \brief Moves a line to buffer.
270 MoveLineToBuffer (LayerType
*layer
, LineType
*line
)
272 LayerType
*lay
= &Dest
->Layer
[GetLayerNumber (Source
, layer
)];
274 RestoreToPolygon (Source
, LINE_TYPE
, layer
, line
);
275 r_delete_entry (layer
->line_tree
, (BoxType
*)line
);
277 layer
->Line
= g_list_remove (layer
->Line
, line
);
279 lay
->Line
= g_list_append (lay
->Line
, line
);
282 CLEAR_FLAG (NOCOPY_FLAGS
, line
);
285 lay
->line_tree
= r_create_tree (NULL
, 0, 0);
286 r_insert_entry (lay
->line_tree
, (BoxType
*)line
, 0);
287 ClearFromPolygon (Dest
, LINE_TYPE
, lay
, line
);
292 * \brief Moves an arc to buffer.
295 MoveArcToBuffer (LayerType
*layer
, ArcType
*arc
)
297 LayerType
*lay
= &Dest
->Layer
[GetLayerNumber (Source
, layer
)];
299 RestoreToPolygon (Source
, ARC_TYPE
, layer
, arc
);
300 r_delete_entry (layer
->arc_tree
, (BoxType
*)arc
);
302 layer
->Arc
= g_list_remove (layer
->Arc
, arc
);
304 lay
->Arc
= g_list_append (lay
->Arc
, arc
);
307 CLEAR_FLAG (NOCOPY_FLAGS
, arc
);
310 lay
->arc_tree
= r_create_tree (NULL
, 0, 0);
311 r_insert_entry (lay
->arc_tree
, (BoxType
*)arc
, 0);
312 ClearFromPolygon (Dest
, ARC_TYPE
, lay
, arc
);
317 * \brief Moves a text to buffer without allocating memory for the name.
320 MoveTextToBuffer (LayerType
*layer
, TextType
*text
)
322 LayerType
*lay
= &Dest
->Layer
[GetLayerNumber (Source
, layer
)];
324 r_delete_entry (layer
->text_tree
, (BoxType
*)text
);
325 RestoreToPolygon (Source
, TEXT_TYPE
, layer
, text
);
327 layer
->Text
= g_list_remove (layer
->Text
, text
);
329 lay
->Text
= g_list_append (lay
->Text
, text
);
333 lay
->text_tree
= r_create_tree (NULL
, 0, 0);
334 r_insert_entry (lay
->text_tree
, (BoxType
*)text
, 0);
335 ClearFromPolygon (Dest
, TEXT_TYPE
, lay
, text
);
340 * \brief Moves a polygon to buffer.
342 * Doesn't allocate memory for the points.
345 MovePolygonToBuffer (LayerType
*layer
, PolygonType
*polygon
)
347 LayerType
*lay
= &Dest
->Layer
[GetLayerNumber (Source
, layer
)];
349 r_delete_entry (layer
->polygon_tree
, (BoxType
*)polygon
);
351 layer
->Polygon
= g_list_remove (layer
->Polygon
, polygon
);
353 lay
->Polygon
= g_list_append (lay
->Polygon
, polygon
);
356 CLEAR_FLAG (NOCOPY_FLAGS
, polygon
);
358 if (!lay
->polygon_tree
)
359 lay
->polygon_tree
= r_create_tree (NULL
, 0, 0);
360 r_insert_entry (lay
->polygon_tree
, (BoxType
*)polygon
, 0);
365 * \brief Moves a element to buffer without allocating memory for
369 MoveElementToBuffer (ElementType
*element
)
372 * Delete the element from the source (remove it from trees,
373 * restore to polygons)
375 r_delete_element (Source
, element
);
377 Source
->Element
= g_list_remove (Source
->Element
, element
);
379 Dest
->Element
= g_list_append (Dest
->Element
, element
);
384 RestoreToPolygon(Source
, PIN_TYPE
, element
, pin
);
385 CLEAR_FLAG (WARNFLAG
| NOCOPY_FLAGS
, pin
);
390 RestoreToPolygon(Source
, PAD_TYPE
, element
, pad
);
391 CLEAR_FLAG (WARNFLAG
| NOCOPY_FLAGS
, pad
);
394 SetElementBoundingBox (Dest
, element
, &PCB
->Font
);
396 * Now clear the from the polygons in the destination
400 ClearFromPolygon (Dest
, PIN_TYPE
, element
, pin
);
405 ClearFromPolygon (Dest
, PAD_TYPE
, element
, pad
);
413 * \brief Calculates the bounding box of the buffer.
416 SetBufferBoundingBox (BufferType
*Buffer
)
418 BoxType
*box
= GetDataBoundingBox (Buffer
->Data
);
421 Buffer
->BoundingBox
= *box
;
425 * \brief Clears the contents of the paste buffer.
428 ClearBuffer (BufferType
*Buffer
)
430 if (Buffer
&& Buffer
->Data
)
432 FreeDataMemory (Buffer
->Data
);
433 Buffer
->Data
->pcb
= PCB
;
438 * \brief Copies all selected and visible objects to the paste buffer.
440 * \return true if any objects have been removed.
443 AddSelectedToBuffer (BufferType
*Buffer
, Coord X
, Coord Y
, bool LeaveSelected
)
445 /* switch crosshair off because adding objects to the pastebuffer
446 * may change the 'valid' area for the cursor
449 ExtraFlag
= SELECTEDFLAG
;
450 notify_crosshair_change (false);
453 SelectedOperation (&AddBufferFunctions
, false, ALL_TYPES
);
455 /* set origin to passed or current position */
463 Buffer
->X
= Crosshair
.X
;
464 Buffer
->Y
= Crosshair
.Y
;
466 notify_crosshair_change (true);
471 * \brief Loads element data from file/library into buffer.
473 * Parse the file with disabled 'PCB mode' (see parser).
475 * \return false on error, if successful, update some other stuff and
476 * reposition the pastebuffer.
479 LoadElementToBuffer (BufferType
*Buffer
, char *Name
, bool FromFile
)
481 ElementType
*element
;
483 ClearBuffer (Buffer
);
486 if (!ParseElementFile (Buffer
->Data
, Name
))
488 if (Settings
.ShowBottomSide
)
490 SetBufferBoundingBox (Buffer
);
491 if (Buffer
->Data
->ElementN
)
493 element
= Buffer
->Data
->Element
->data
;
494 Buffer
->X
= element
->MarkX
;
495 Buffer
->Y
= element
->MarkY
;
507 if (!ParseLibraryEntry (Buffer
->Data
, Name
)
508 && Buffer
->Data
->ElementN
!= 0)
510 element
= Buffer
->Data
->Element
->data
;
512 /* always add elements using top-side coordinates */
513 if (Settings
.ShowBottomSide
)
514 MirrorElementCoordinates (Buffer
->Data
, element
, 0);
515 SetElementBoundingBox (Buffer
->Data
, element
, &PCB
->Font
);
517 /* set buffer offset to 'mark' position */
518 Buffer
->X
= element
->MarkX
;
519 Buffer
->Y
= element
->MarkY
;
520 SetBufferBoundingBox (Buffer
);
524 /* release memory which might have been acquired */
525 ClearBuffer (Buffer
);
532 int footprint_allocated
;
535 } FootprintHashEntry
;
537 static FootprintHashEntry
*footprint_hash
= 0;
538 int footprint_hash_size
= 0;
541 clear_footprint_hash ()
546 for (i
=0; i
<footprint_hash_size
; i
++)
547 if (footprint_hash
[i
].footprint_allocated
)
548 free (footprint_hash
[i
].footprint
);
549 free (footprint_hash
);
550 footprint_hash
= NULL
;
551 footprint_hash_size
= 0;
555 * \brief Used to sort footprint pointer entries.
557 * \note We include the index numbers so that same-named footprints are
558 * sorted by the library search order.
561 footprint_hash_cmp (const void *va
, const void *vb
)
564 FootprintHashEntry
*a
= (FootprintHashEntry
*)va
;
565 FootprintHashEntry
*b
= (FootprintHashEntry
*)vb
;
567 i
= strcmp (a
->footprint
, b
->footprint
);
569 i
= a
->menu_idx
- b
->menu_idx
;
571 i
= a
->entry_idx
- b
->entry_idx
;
576 make_footprint_hash ()
582 clear_footprint_hash ();
584 for (i
=0; i
<Library
.MenuN
; i
++)
585 for (j
=0; j
<Library
.Menu
[i
].EntryN
; j
++)
587 footprint_hash
= (FootprintHashEntry
*)malloc (num_entries
* sizeof(FootprintHashEntry
));
590 /* There are two types of library entries. The file-based types
591 have a Template of (char *)-1 and the AllocatedMemory is the full
592 path to the footprint file. The m4 ones have the footprint name
593 in brackets in the description. */
594 for (i
=0; i
<Library
.MenuN
; i
++)
597 printf("In make_footprint_hash, looking for footprints in %s\n",
598 Library
.Menu
[i
].directory
);
601 for (j
=0; j
<Library
.Menu
[i
].EntryN
; j
++)
603 footprint_hash
[num_entries
].menu_idx
= i
;
604 footprint_hash
[num_entries
].entry_idx
= j
;
605 if (Library
.Menu
[i
].Entry
[j
].Template
== (char *) -1)
609 /* printf(" ... Examining file %s\n", Library.Menu[i].Entry[j].AllocatedMemory); */
611 fp
= strrchr (Library
.Menu
[i
].Entry
[j
].AllocatedMemory
, '/');
614 fp
= strrchr (Library
.Menu
[i
].Entry
[j
].AllocatedMemory
, '\\');
619 fp
= Library
.Menu
[i
].Entry
[j
].AllocatedMemory
;
622 /* printf(" ... found file footprint %s\n", fp); */
625 footprint_hash
[num_entries
].footprint
= fp
;
626 footprint_hash
[num_entries
].footprint_allocated
= 0;
631 fp
= strrchr (Library
.Menu
[i
].Entry
[j
].Description
, '[');
634 footprint_hash
[num_entries
].footprint
= strdup (fp
+1);
635 footprint_hash
[num_entries
].footprint_allocated
= 1;
636 fp
= strchr (footprint_hash
[num_entries
].footprint
, ']');
642 fp
= Library
.Menu
[i
].Entry
[j
].Description
;
643 footprint_hash
[num_entries
].footprint
= fp
;
644 footprint_hash
[num_entries
].footprint_allocated
= 0;
651 footprint_hash_size
= num_entries
;
652 qsort (footprint_hash
, num_entries
, sizeof(footprint_hash
[0]), footprint_hash_cmp
);
655 printf(" found footprints: \n");
656 for (i=0; i<num_entries; i++)
657 printf("[%s]\n", footprint_hash[i].footprint);
663 * \brief Searches for the given element by "footprint" name, and loads
664 * it into the buffer.
666 * Figuring out which library entry is the one we want is a little
667 * tricky. For file-based footprints, it's just a matter of finding
668 * the first match in the search list. For m4-based footprints you
669 * need to know what magic to pass to the m4 functions. Fortunately,
670 * the footprint needed is determined when we build the m4 libraries
671 * and stored as a comment in the description, so we can search for
672 * that to find the magic we need. We use a hash to store the
673 * corresponding footprints and pointers to the library tree so we can
674 * quickly find the various bits we need to load a given
678 search_footprint_hash (const char *footprint
)
682 /* Standard binary search */
685 max
= footprint_hash_size
;
687 while (max
- min
> 1)
690 c
= strcmp (footprint
, footprint_hash
[i
].footprint
);
697 /* We want to return the first match, not just any match. */
699 && strcmp (footprint
, footprint_hash
[i
-1].footprint
) == 0)
701 return & footprint_hash
[i
];
710 * \return zero on success, non-zero on error.
713 LoadFootprintByName (BufferType
*Buffer
, char *Footprint
)
716 FootprintHashEntry
*fpe
;
717 LibraryMenuType
*menu
;
718 LibraryEntryType
*entry
;
719 char *with_fp
= NULL
;
722 make_footprint_hash ();
724 fpe
= search_footprint_hash (Footprint
);
727 with_fp
= Concat (Footprint
, ".fp", NULL
);
728 fpe
= search_footprint_hash (with_fp
);
734 Message(_("Unable to load footprint %s\n"), Footprint
);
738 menu
= & Library
.Menu
[fpe
->menu_idx
];
739 entry
= & menu
->Entry
[fpe
->entry_idx
];
741 if (entry
->Template
== (char *) -1)
743 i
= LoadElementToBuffer (Buffer
, entry
->AllocatedMemory
, true);
752 args
= Concat("'", EMPTY (entry
->Template
), "' '",
753 EMPTY (entry
->Value
), "' '", EMPTY (entry
->Package
), "'", NULL
);
754 i
= LoadElementToBuffer (Buffer
, args
, false);
765 printf("Library path: %s\n", Settings
.LibraryPath
);
766 printf("Library tree: %s\n", Settings
.LibraryTree
);
768 printf("Library:\n");
769 for (i
=0; i
<Library
.MenuN
; i
++)
771 printf(" [%02d] Name: %s\n", i
, Library
.Menu
[i
].Name
);
772 printf(" Dir: %s\n", Library
.Menu
[i
].directory
);
773 printf(" Sty: %s\n", Library
.Menu
[i
].Style
);
774 for (j
=0; j
<Library
.Menu
[i
].EntryN
; j
++)
776 printf(" [%02d] E: %s\n", j
, Library
.Menu
[i
].Entry
[j
].ListEntry
);
777 if (Library
.Menu
[i
].Entry
[j
].Template
== (char *) -1)
778 printf(" A: %s\n", Library
.Menu
[i
].Entry
[j
].AllocatedMemory
);
781 printf(" T: %s\n", Library
.Menu
[i
].Entry
[j
].Template
);
782 printf(" P: %s\n", Library
.Menu
[i
].Entry
[j
].Package
);
783 printf(" V: %s\n", Library
.Menu
[i
].Entry
[j
].Value
);
784 printf(" D: %s\n", Library
.Menu
[i
].Entry
[j
].Description
);
795 static const char loadfootprint_syntax
[] =
796 N_("LoadFootprint(filename[,refdes,value])");
798 static const char loadfootprint_help
[] =
799 N_("Loads a single footprint by name.");
801 /* %start-doc actions LoadFootprint
803 Loads a single footprint by name, rather than by reference or through
804 the library. If a refdes and value are specified, those are inserted
805 into the footprint as well. The footprint remains in the paste buffer.
810 * \brief This action is called from ActionElementAddIf().
813 LoadFootprint (int argc
, char **argv
, Coord x
, Coord y
)
816 char *refdes
= ARG(1);
817 char *value
= ARG(2);
821 AFAIL (loadfootprint
);
823 if (LoadFootprintByName (PASTEBUFFER
, name
))
826 if (PASTEBUFFER
->Data
->ElementN
== 0)
828 Message(_("Footprint %s contains no elements"), name
);
831 if (PASTEBUFFER
->Data
->ElementN
> 1)
833 Message(_("Footprint %s contains multiple elements"), name
);
837 e
= PASTEBUFFER
->Data
->Element
->data
;
839 if (e
->Name
[0].TextString
)
840 free (e
->Name
[0].TextString
);
841 e
->Name
[0].TextString
= strdup (name
);
843 if (e
->Name
[1].TextString
)
844 free (e
->Name
[1].TextString
);
845 e
->Name
[1].TextString
= refdes
? strdup (refdes
) : 0;
847 if (e
->Name
[2].TextString
)
848 free (e
->Name
[2].TextString
);
849 e
->Name
[2].TextString
= value
? strdup (value
) : 0;
855 * \brief Break buffer element into pieces.
858 SmashBufferElement (BufferType
*Buffer
)
860 ElementType
*element
;
862 LayerType
*top_layer
, *bottom_layer
;
864 if (Buffer
->Data
->ElementN
!= 1)
866 Message (_("Error! Buffer doesn't contain a single element\n"));
870 * At this point the buffer should contain just a single element.
871 * Now we detach the single element from the buffer and then clear the
872 * buffer, ready to receive the smashed elements. As a result of detaching
873 * it the single element is orphaned from the buffer and thus will not be
874 * free()'d by FreeDataMemory (called via ClearBuffer). This leaves it
875 * around for us to smash bits off it. It then becomes our responsibility,
876 * however, to free the single element when we're finished with it.
878 element
= Buffer
->Data
->Element
->data
;
879 Buffer
->Data
->Element
= NULL
;
880 Buffer
->Data
->ElementN
= 0;
881 ClearBuffer (Buffer
);
882 ELEMENTLINE_LOOP (element
);
884 CreateNewLineOnLayer (&Buffer
->Data
->SILKLAYER
,
885 line
->Point1
.X
, line
->Point1
.Y
,
886 line
->Point2
.X
, line
->Point2
.Y
,
887 line
->Thickness
, 0, NoFlags ());
889 line
->Number
= STRDUP (NAMEONPCB_NAME (element
));
894 CreateNewArcOnLayer (&Buffer
->Data
->SILKLAYER
,
895 arc
->X
, arc
->Y
, arc
->Width
, arc
->Height
, arc
->StartAngle
,
896 arc
->Delta
, arc
->Thickness
, 0, NoFlags ());
901 FlagType f
= NoFlags ();
902 AddFlags (f
, VIAFLAG
);
903 if (TEST_FLAG (HOLEFLAG
, pin
))
904 AddFlags (f
, HOLEFLAG
);
906 CreateNewVia (Buffer
->Data
, pin
->X
, pin
->Y
,
907 pin
->Thickness
, pin
->Clearance
, pin
->Mask
,
908 pin
->DrillingHole
, pin
->Number
, f
);
911 group
= GetLayerGroupNumberBySide (SWAP_IDENT
? BOTTOM_SIDE
: TOP_SIDE
);
912 top_layer
= &Buffer
->Data
->Layer
[PCB
->LayerGroups
.Entries
[group
][0]];
913 group
= GetLayerGroupNumberBySide (SWAP_IDENT
? TOP_SIDE
: BOTTOM_SIDE
);
914 bottom_layer
= &Buffer
->Data
->Layer
[PCB
->LayerGroups
.Entries
[group
][0]];
918 line
= CreateNewLineOnLayer (TEST_FLAG (ONSOLDERFLAG
, pad
) ? bottom_layer
: top_layer
,
919 pad
->Point1
.X
, pad
->Point1
.Y
,
920 pad
->Point2
.X
, pad
->Point2
.Y
,
921 pad
->Thickness
, pad
->Clearance
, NoFlags ());
923 line
->Number
= STRDUP (pad
->Number
);
926 FreeElementMemory (element
);
927 g_slice_free (ElementType
, element
);
932 * \brief See if a polygon is a rectangle.
934 * If so, canonicalize it.
938 polygon_is_rectangle (PolygonType
*poly
)
942 if (poly
->PointN
!= 4 || poly
->HoleIndexN
!= 0)
946 if (poly
->Points
[i
].X
< poly
->Points
[best
].X
947 || poly
->Points
[i
].Y
< poly
->Points
[best
].Y
)
950 temp
[i
] = poly
->Points
[(i
+best
)%4];
951 if (temp
[0].X
== temp
[1].X
)
952 memcpy (poly
->Points
, temp
, sizeof(temp
));
956 poly
->Points
[0] = temp
[0];
957 poly
->Points
[1] = temp
[3];
958 poly
->Points
[2] = temp
[2];
959 poly
->Points
[3] = temp
[1];
961 if (poly
->Points
[0].X
== poly
->Points
[1].X
962 && poly
->Points
[1].Y
== poly
->Points
[2].Y
963 && poly
->Points
[2].X
== poly
->Points
[3].X
964 && poly
->Points
[3].Y
== poly
->Points
[0].Y
)
970 * \brief Convert buffer contents into an element.
973 ConvertBufferToElement (BufferType
*Buffer
)
975 ElementType
*Element
;
978 bool hasParts
= false, crooked
= false;
982 if (Buffer
->Data
->pcb
== 0)
983 Buffer
->Data
->pcb
= PCB
;
985 Element
= CreateNewElement (PCB
->Data
, &PCB
->Font
, NoFlags (),
986 NULL
, NULL
, NULL
, PASTEBUFFER
->X
,
987 PASTEBUFFER
->Y
, 0, 100,
988 MakeFlags (SWAP_IDENT
? ONSOLDERFLAG
: NOFLAG
),
992 VIA_LOOP (Buffer
->Data
);
995 if (via
->Mask
< via
->Thickness
)
996 via
->Mask
= via
->Thickness
+ 2 * MASKFRAME
;
998 CreateNewPin (Element
, via
->X
, via
->Y
, via
->Thickness
,
999 via
->Clearance
, via
->Mask
, via
->DrillingHole
,
1000 NULL
, via
->Name
, MaskFlags (via
->Flags
,
1001 VIAFLAG
| NOCOPY_FLAGS
|
1002 SELECTEDFLAG
| WARNFLAG
));
1005 sprintf (num
, "%d", pin_n
++);
1006 CreateNewPin (Element
, via
->X
, via
->Y
, via
->Thickness
,
1007 via
->Clearance
, via
->Mask
, via
->DrillingHole
,
1008 NULL
, num
, MaskFlags (via
->Flags
,
1009 VIAFLAG
| NOCOPY_FLAGS
| SELECTEDFLAG
1016 for (onsolder
= 0; onsolder
< 2; onsolder
++)
1021 if ((!onsolder
) == (!SWAP_IDENT
))
1024 onsolderflag
= NOFLAG
;
1029 onsolderflag
= ONSOLDERFLAG
;
1032 #define MAYBE_WARN() \
1033 if (onsolder && !hasParts && !warned) \
1037 (_("Warning: All of the pads are on the opposite\n" \
1038 "side from the component - that's probably not what\n" \
1042 /* get the component-side SM pads */
1043 group
= GetLayerGroupNumberBySide (side
);
1044 GROUP_LOOP (Buffer
->Data
, group
);
1049 sprintf (num
, "%d", pin_n
++);
1050 CreateNewPad (Element
, line
->Point1
.X
,
1051 line
->Point1
.Y
, line
->Point2
.X
,
1052 line
->Point2
.Y
, line
->Thickness
,
1054 line
->Thickness
+ line
->Clearance
, NULL
,
1055 line
->Number
? line
->Number
: num
,
1056 MakeFlags (onsolderflag
));
1061 POLYGON_LOOP (layer
);
1063 Coord x1
, y1
, x2
, y2
, w
, h
, t
;
1065 if (! polygon_is_rectangle (polygon
))
1071 w
= polygon
->Points
[2].X
- polygon
->Points
[0].X
;
1072 h
= polygon
->Points
[1].Y
- polygon
->Points
[0].Y
;
1073 t
= (w
< h
) ? w
: h
;
1074 x1
= polygon
->Points
[0].X
+ t
/2;
1075 y1
= polygon
->Points
[0].Y
+ t
/2;
1079 sprintf (num
, "%d", pin_n
++);
1080 CreateNewPad (Element
,
1082 2 * Settings
.Keepaway
,
1083 t
+ Settings
.Keepaway
,
1085 MakeFlags (SQUAREFLAG
| onsolderflag
));
1094 /* now add the silkscreen. NOTE: elements must have pads or pins too */
1095 LINE_LOOP (&Buffer
->Data
->SILKLAYER
);
1097 if (line
->Number
&& !NAMEONPCB_NAME (Element
))
1098 NAMEONPCB_NAME (Element
) = strdup (line
->Number
);
1099 CreateNewLineInElement (Element
, line
->Point1
.X
,
1100 line
->Point1
.Y
, line
->Point2
.X
,
1101 line
->Point2
.Y
, line
->Thickness
);
1105 ARC_LOOP (&Buffer
->Data
->SILKLAYER
);
1107 CreateNewArcInElement (Element
, arc
->X
, arc
->Y
, arc
->Width
,
1108 arc
->Height
, arc
->StartAngle
, arc
->Delta
,
1115 DestroyObject (PCB
->Data
, ELEMENT_TYPE
, Element
, Element
, Element
);
1116 Message (_("There was nothing to convert!\n"
1117 "Elements must have some silk, pads or pins.\n"));
1121 Message (_("There were polygons that can't be made into pins!\n"
1122 "So they were not included in the element\n"));
1123 Element
->MarkX
= Buffer
->X
;
1124 Element
->MarkY
= Buffer
->Y
;
1126 SET_FLAG (ONSOLDERFLAG
, Element
);
1127 SetElementBoundingBox (PCB
->Data
, Element
, &PCB
->Font
);
1128 ClearBuffer (Buffer
);
1129 MoveObjectToBuffer (Buffer
->Data
, PCB
->Data
, ELEMENT_TYPE
, Element
, Element
,
1131 SetBufferBoundingBox (Buffer
);
1136 * \brief Load PCB into buffer.
1138 * Parse the file with enabled 'PCB mode' (see parser).
1139 * If successful, update some other stuff.
1142 LoadLayoutToBuffer (BufferType
*Buffer
, char *Filename
)
1144 PCBType
*newPCB
= CreateNewPCB ();
1146 /* new data isn't added to the undo list */
1147 if (!ParsePCB (newPCB
, Filename
))
1149 /* clear data area and replace pointer */
1150 ClearBuffer (Buffer
);
1151 free (Buffer
->Data
);
1152 Buffer
->Data
= newPCB
->Data
;
1153 newPCB
->Data
= NULL
;
1154 Buffer
->X
= newPCB
->CursorX
;
1155 Buffer
->Y
= newPCB
->CursorY
;
1157 Buffer
->Data
->pcb
= PCB
;
1161 /* release unused memory */
1163 Buffer
->Data
->pcb
= PCB
;
1168 * \brief Rotates the contents of the pastebuffer.
1171 RotateBuffer (BufferType
*Buffer
, BYTE Number
)
1174 VIA_LOOP (Buffer
->Data
);
1176 r_delete_entry (Buffer
->Data
->via_tree
, (BoxType
*)via
);
1177 ROTATE_VIA_LOWLEVEL (via
, Buffer
->X
, Buffer
->Y
, Number
);
1178 SetPinBoundingBox (via
);
1179 r_insert_entry (Buffer
->Data
->via_tree
, (BoxType
*)via
, 0);
1184 ELEMENT_LOOP (Buffer
->Data
);
1186 RotateElementLowLevel (Buffer
->Data
, element
, Buffer
->X
, Buffer
->Y
,
1191 /* all layer related objects */
1192 ALLLINE_LOOP (Buffer
->Data
);
1194 r_delete_entry (layer
->line_tree
, (BoxType
*)line
);
1195 RotateLineLowLevel (line
, Buffer
->X
, Buffer
->Y
, Number
);
1196 r_insert_entry (layer
->line_tree
, (BoxType
*)line
, 0);
1199 ALLARC_LOOP (Buffer
->Data
);
1201 r_delete_entry (layer
->arc_tree
, (BoxType
*)arc
);
1202 RotateArcLowLevel (arc
, Buffer
->X
, Buffer
->Y
, Number
);
1203 r_insert_entry (layer
->arc_tree
, (BoxType
*)arc
, 0);
1206 ALLTEXT_LOOP (Buffer
->Data
);
1208 r_delete_entry (layer
->text_tree
, (BoxType
*)text
);
1209 RotateTextLowLevel (text
, Buffer
->X
, Buffer
->Y
, Number
);
1210 r_insert_entry (layer
->text_tree
, (BoxType
*)text
, 0);
1213 ALLPOLYGON_LOOP (Buffer
->Data
);
1215 r_delete_entry (layer
->polygon_tree
, (BoxType
*)polygon
);
1216 RotatePolygonLowLevel (polygon
, Buffer
->X
, Buffer
->Y
, Number
);
1217 r_insert_entry (layer
->polygon_tree
, (BoxType
*)polygon
, 0);
1221 /* finally the origin and the bounding box */
1222 ROTATE (Buffer
->X
, Buffer
->Y
, Buffer
->X
, Buffer
->Y
, Number
);
1223 RotateBoxLowLevel (&Buffer
->BoundingBox
, Buffer
->X
, Buffer
->Y
, Number
);
1224 SetCrosshairRangeToBuffer ();
1228 free_rotate (Coord
*x
, Coord
*y
, Coord cx
, Coord cy
, double cosa
, double sina
)
1234 nx
= px
* cosa
+ py
* sina
;
1235 ny
= py
* cosa
- px
* sina
;
1242 FreeRotateElementLowLevel (DataType
*Data
, ElementType
*Element
,
1244 double cosa
, double sina
, Angle angle
)
1246 /* solder side objects need a different orientation */
1248 /* the text subroutine decides by itself if the direction
1249 * is to be corrected
1252 ELEMENTTEXT_LOOP (Element
);
1254 if (Data
&& Data
->name_tree
[n
])
1255 r_delete_entry (Data
->name_tree
[n
], (BoxType
*)text
);
1256 RotateTextLowLevel (text
, X
, Y
, Number
);
1260 ELEMENTLINE_LOOP (Element
);
1262 free_rotate (&line
->Point1
.X
, &line
->Point1
.Y
, X
, Y
, cosa
, sina
);
1263 free_rotate (&line
->Point2
.X
, &line
->Point2
.Y
, X
, Y
, cosa
, sina
);
1264 SetLineBoundingBox (line
);
1269 /* pre-delete the pins from the pin-tree before their coordinates change */
1271 r_delete_entry (Data
->pin_tree
, (BoxType
*)pin
);
1272 RestoreToPolygon (Data
, PIN_TYPE
, Element
, pin
);
1273 free_rotate (&pin
->X
, &pin
->Y
, X
, Y
, cosa
, sina
);
1274 SetPinBoundingBox (pin
);
1279 /* pre-delete the pads before their coordinates change */
1281 r_delete_entry (Data
->pad_tree
, (BoxType
*)pad
);
1282 RestoreToPolygon (Data
, PAD_TYPE
, Element
, pad
);
1283 free_rotate (&pad
->Point1
.X
, &pad
->Point1
.Y
, X
, Y
, cosa
, sina
);
1284 free_rotate (&pad
->Point2
.X
, &pad
->Point2
.Y
, X
, Y
, cosa
, sina
);
1285 SetLineBoundingBox ((LineType
*) pad
);
1290 free_rotate (&arc
->X
, &arc
->Y
, X
, Y
, cosa
, sina
);
1291 arc
->StartAngle
= NormalizeAngle (arc
->StartAngle
+ angle
);
1295 free_rotate (&Element
->MarkX
, &Element
->MarkY
, X
, Y
, cosa
, sina
);
1296 SetElementBoundingBox (Data
, Element
, &PCB
->Font
);
1297 ClearFromPolygon (Data
, ELEMENT_TYPE
, Element
, Element
);
1301 FreeRotateBuffer (BufferType
*Buffer
, Angle angle
)
1305 cosa
= cos(angle
* M_PI
/180.0);
1306 sina
= sin(angle
* M_PI
/180.0);
1309 VIA_LOOP (Buffer
->Data
);
1311 r_delete_entry (Buffer
->Data
->via_tree
, (BoxType
*)via
);
1312 free_rotate (&via
->X
, &via
->Y
, Buffer
->X
, Buffer
->Y
, cosa
, sina
);
1313 SetPinBoundingBox (via
);
1314 r_insert_entry (Buffer
->Data
->via_tree
, (BoxType
*)via
, 0);
1319 ELEMENT_LOOP (Buffer
->Data
);
1321 FreeRotateElementLowLevel (Buffer
->Data
, element
, Buffer
->X
, Buffer
->Y
,
1326 /* all layer related objects */
1327 ALLLINE_LOOP (Buffer
->Data
);
1329 r_delete_entry (layer
->line_tree
, (BoxType
*)line
);
1330 free_rotate (&line
->Point1
.X
, &line
->Point1
.Y
, Buffer
->X
, Buffer
->Y
, cosa
, sina
);
1331 free_rotate (&line
->Point2
.X
, &line
->Point2
.Y
, Buffer
->X
, Buffer
->Y
, cosa
, sina
);
1332 SetLineBoundingBox (line
);
1333 r_insert_entry (layer
->line_tree
, (BoxType
*)line
, 0);
1336 ALLARC_LOOP (Buffer
->Data
);
1338 r_delete_entry (layer
->arc_tree
, (BoxType
*)arc
);
1339 free_rotate (&arc
->X
, &arc
->Y
, Buffer
->X
, Buffer
->Y
, cosa
, sina
);
1340 arc
->StartAngle
= NormalizeAngle (arc
->StartAngle
+ angle
);
1341 r_insert_entry (layer
->arc_tree
, (BoxType
*)arc
, 0);
1344 /* FIXME: rotate text */
1345 ALLPOLYGON_LOOP (Buffer
->Data
);
1347 r_delete_entry (layer
->polygon_tree
, (BoxType
*)polygon
);
1348 POLYGONPOINT_LOOP (polygon
);
1350 free_rotate (&point
->X
, &point
->Y
, Buffer
->X
, Buffer
->Y
, cosa
, sina
);
1353 SetPolygonBoundingBox (polygon
);
1354 r_insert_entry (layer
->polygon_tree
, (BoxType
*)polygon
, 0);
1358 SetBufferBoundingBox (Buffer
);
1359 SetCrosshairRangeToBuffer ();
1363 /* -------------------------------------------------------------------------- */
1365 static const char freerotatebuffer_syntax
[] =
1366 N_("FreeRotateBuffer([Angle])");
1368 static const char freerotatebuffer_help
[] =
1369 N_("Rotates the current paste buffer contents by the specified angle. The\n"
1370 "angle is given in degrees. If no angle is given, the user is prompted\n"
1373 /* %start-doc actions FreeRotateBuffer
1375 Rotates the contents of the pastebuffer by an arbitrary angle. If no
1376 angle is given, the user is prompted for one.
1381 ActionFreeRotateBuffer(int argc
, char **argv
, Coord x
, Coord y
)
1386 angle_s
= gui
->prompt_for (_("Enter Rotation (degrees, CCW):"), "0");
1390 notify_crosshair_change (false);
1391 FreeRotateBuffer(PASTEBUFFER
, strtod(angle_s
, 0));
1392 notify_crosshair_change (true);
1397 * \brief Initializes the buffers by allocating memory.
1404 for (i
= 0; i
< MAX_BUFFER
; i
++)
1405 Buffers
[i
].Data
= CreateNewBuffer ();
1413 for (i
= 0; i
< MAX_BUFFER
; i
++)
1414 SwapBuffer (&Buffers
[i
]);
1415 SetCrosshairRangeToBuffer ();
1419 MirrorBuffer (BufferType
*Buffer
)
1423 if (Buffer
->Data
->ElementN
)
1425 Message (_("You can't mirror a buffer that has elements!\n"));
1428 for (i
= 0; i
< max_copper_layer
+ EXTRA_LAYERS
; i
++)
1430 LayerType
*layer
= Buffer
->Data
->Layer
+ i
;
1433 Message (_("You can't mirror a buffer that has text!\n"));
1437 /* set buffer offset to 'mark' position */
1438 Buffer
->X
= SWAP_X (Buffer
->X
);
1439 Buffer
->Y
= SWAP_Y (Buffer
->Y
);
1440 VIA_LOOP (Buffer
->Data
);
1442 via
->X
= SWAP_X (via
->X
);
1443 via
->Y
= SWAP_Y (via
->Y
);
1446 ALLLINE_LOOP (Buffer
->Data
);
1448 line
->Point1
.X
= SWAP_X (line
->Point1
.X
);
1449 line
->Point1
.Y
= SWAP_Y (line
->Point1
.Y
);
1450 line
->Point2
.X
= SWAP_X (line
->Point2
.X
);
1451 line
->Point2
.Y
= SWAP_Y (line
->Point2
.Y
);
1454 ALLARC_LOOP (Buffer
->Data
);
1456 arc
->X
= SWAP_X (arc
->X
);
1457 arc
->Y
= SWAP_Y (arc
->Y
);
1458 arc
->StartAngle
= SWAP_ANGLE (arc
->StartAngle
);
1459 arc
->Delta
= SWAP_DELTA (arc
->Delta
);
1460 SetArcBoundingBox (arc
);
1463 ALLPOLYGON_LOOP (Buffer
->Data
);
1465 POLYGONPOINT_LOOP (polygon
);
1467 point
->X
= SWAP_X (point
->X
);
1468 point
->Y
= SWAP_Y (point
->Y
);
1471 SetPolygonBoundingBox (polygon
);
1474 SetBufferBoundingBox (Buffer
);
1475 SetCrosshairRangeToBuffer ();
1480 * \brief Flip components/tracks from one side to the other.
1483 SwapBuffer (BufferType
*Buffer
)
1486 Cardinal top_group
, bottom_group
;
1489 ELEMENT_LOOP (Buffer
->Data
);
1491 r_delete_element (Buffer
->Data
, element
);
1492 MirrorElementCoordinates (Buffer
->Data
, element
, 0);
1495 /* set buffer offset to 'mark' position */
1496 Buffer
->X
= SWAP_X (Buffer
->X
);
1497 Buffer
->Y
= SWAP_Y (Buffer
->Y
);
1498 VIA_LOOP (Buffer
->Data
);
1500 r_delete_entry (Buffer
->Data
->via_tree
, (BoxType
*)via
);
1501 via
->X
= SWAP_X (via
->X
);
1502 via
->Y
= SWAP_Y (via
->Y
);
1503 SetPinBoundingBox (via
);
1504 r_insert_entry (Buffer
->Data
->via_tree
, (BoxType
*)via
, 0);
1507 ALLLINE_LOOP (Buffer
->Data
);
1509 r_delete_entry (layer
->line_tree
, (BoxType
*)line
);
1510 line
->Point1
.X
= SWAP_X (line
->Point1
.X
);
1511 line
->Point1
.Y
= SWAP_Y (line
->Point1
.Y
);
1512 line
->Point2
.X
= SWAP_X (line
->Point2
.X
);
1513 line
->Point2
.Y
= SWAP_Y (line
->Point2
.Y
);
1514 SetLineBoundingBox (line
);
1515 r_insert_entry (layer
->line_tree
, (BoxType
*)line
, 0);
1518 ALLARC_LOOP (Buffer
->Data
);
1520 r_delete_entry (layer
->arc_tree
, (BoxType
*)arc
);
1521 arc
->X
= SWAP_X (arc
->X
);
1522 arc
->Y
= SWAP_Y (arc
->Y
);
1523 arc
->StartAngle
= SWAP_ANGLE (arc
->StartAngle
);
1524 arc
->Delta
= SWAP_DELTA (arc
->Delta
);
1525 SetArcBoundingBox (arc
);
1526 r_insert_entry (layer
->arc_tree
, (BoxType
*)arc
, 0);
1529 ALLPOLYGON_LOOP (Buffer
->Data
);
1531 r_delete_entry (layer
->polygon_tree
, (BoxType
*)polygon
);
1532 POLYGONPOINT_LOOP (polygon
);
1534 point
->X
= SWAP_X (point
->X
);
1535 point
->Y
= SWAP_Y (point
->Y
);
1538 SetPolygonBoundingBox (polygon
);
1539 r_insert_entry (layer
->polygon_tree
, (BoxType
*)polygon
, 0);
1540 /* hmmm, how to handle clip */
1543 ALLTEXT_LOOP (Buffer
->Data
);
1545 r_delete_entry (layer
->text_tree
, (BoxType
*)text
);
1546 text
->X
= SWAP_X (text
->X
);
1547 text
->Y
= SWAP_Y (text
->Y
);
1548 TOGGLE_FLAG (ONSOLDERFLAG
, text
);
1549 SetTextBoundingBox (&PCB
->Font
, text
);
1550 r_insert_entry (layer
->text_tree
, (BoxType
*)text
, 0);
1553 /* swap silkscreen layers */
1554 swap
= Buffer
->Data
->Layer
[bottom_silk_layer
];
1555 Buffer
->Data
->Layer
[bottom_silk_layer
] =
1556 Buffer
->Data
->Layer
[top_silk_layer
];
1557 Buffer
->Data
->Layer
[top_silk_layer
] = swap
;
1559 /* swap layer groups when balanced */
1560 top_group
= GetLayerGroupNumberBySide (TOP_SIDE
);
1561 bottom_group
= GetLayerGroupNumberBySide (BOTTOM_SIDE
);
1562 if (PCB
->LayerGroups
.Number
[top_group
] == PCB
->LayerGroups
.Number
[bottom_group
])
1564 for (j
= k
= 0; j
< PCB
->LayerGroups
.Number
[bottom_group
]; j
++)
1567 Cardinal top_number
= PCB
->LayerGroups
.Entries
[top_group
][k
];
1568 Cardinal bottom_number
= PCB
->LayerGroups
.Entries
[bottom_group
][j
];
1570 if (bottom_number
>= max_copper_layer
)
1572 swap
= Buffer
->Data
->Layer
[bottom_number
];
1574 while (top_number
>= max_copper_layer
)
1577 top_number
= PCB
->LayerGroups
.Entries
[top_group
][k
];
1579 Buffer
->Data
->Layer
[bottom_number
] = Buffer
->Data
->Layer
[top_number
];
1580 Buffer
->Data
->Layer
[top_number
] = swap
;
1582 /* move the thermal flags with the layers */
1583 ALLPIN_LOOP (Buffer
->Data
);
1585 t1
= TEST_THERM (bottom_number
, pin
);
1586 t2
= TEST_THERM (top_number
, pin
);
1587 ASSIGN_THERM (bottom_number
, t2
, pin
);
1588 ASSIGN_THERM (top_number
, t1
, pin
);
1591 VIA_LOOP (Buffer
->Data
);
1593 t1
= TEST_THERM (bottom_number
, via
);
1594 t2
= TEST_THERM (top_number
, via
);
1595 ASSIGN_THERM (bottom_number
, t2
, via
);
1596 ASSIGN_THERM (top_number
, t1
, via
);
1601 SetBufferBoundingBox (Buffer
);
1602 SetCrosshairRangeToBuffer ();
1606 * \brief Moves the passed object to the passed buffer and removes it
1607 * from its original place.
1610 MoveObjectToBuffer (DataType
*Destination
, DataType
*Src
,
1611 int Type
, void *Ptr1
, void *Ptr2
, void *Ptr3
)
1613 /* setup local identifiers used by move operations */
1616 return (ObjectOperation (&MoveBufferFunctions
, Type
, Ptr1
, Ptr2
, Ptr3
));
1620 * \brief Adds the passed object to the passed buffer.
1623 CopyObjectToBuffer (DataType
*Destination
, DataType
*Src
,
1624 int Type
, void *Ptr1
, void *Ptr2
, void *Ptr3
)
1626 /* setup local identifiers used by Add operations */
1629 return (ObjectOperation (&AddBufferFunctions
, Type
, Ptr1
, Ptr2
, Ptr3
));
1632 /* ---------------------------------------------------------------------- */
1634 HID_Action rotate_action_list
[] = {
1635 {"FreeRotateBuffer", 0, ActionFreeRotateBuffer
,
1636 freerotatebuffer_syntax
, freerotatebuffer_help
},
1637 {"LoadFootprint", 0, LoadFootprint
,
1641 REGISTER_ACTIONS (rotate_action_list
)