Index: src/Makefile.in =================================================================== --- src/Makefile.in (revision 1088) +++ src/Makefile.in (revision 1089) @@ -46,6 +46,7 @@ move.o mymem.o netlist.o + object_act.o paths.o pcb-printf.o plugins.o @@ -99,7 +100,7 @@ # main: action registrations put /local/pcb/ACTION_REG_SRC { action.c buffer.c change_act.c command.c file_act.c flags.c gui_act.c - import_sch.c main.c misc.c move.c netlist.c plugins.c rats_act.c + import_sch.c main.c misc.c move.c netlist.c object_act.c plugins.c rats_act.c remove_act.c report.c select_act.c undo_act.c } Index: src/action.c =================================================================== --- src/action.c (revision 1088) +++ src/action.c (revision 1089) @@ -1332,55 +1332,6 @@ return 0; } -/* -------------------------------------------------------------------------- */ - -static const char flip_syntax[] = "Flip(Object|Selected|SelectedElements)"; - -static const char flip_help[] = "Flip an element to the opposite side of the board."; - -/* %start-doc actions Flip - -Note that the location of the element will be symmetric about the -cursor location; i.e. if the part you are pointing at will still be at -the same spot once the element is on the other side. When flipping -multiple elements, this retains their positions relative to each -other, not their absolute positions on the board. - -%end-doc */ - -static int ActionFlip(int argc, char **argv, Coord x, Coord y) -{ - char *function = ARG(0); - ElementTypePtr element; - void *ptrtmp; - int err = 0; - - if (function) { - switch (GetFunctionID(function)) { - case F_Object: - if ((SearchScreen(x, y, ELEMENT_TYPE, &ptrtmp, &ptrtmp, &ptrtmp)) != NO_TYPE) { - element = (ElementTypePtr) ptrtmp; - ChangeElementSide(element, 2 * Crosshair.Y - PCB->MaxHeight); - IncrementUndoSerialNumber(); - Draw(); - } - break; - case F_Selected: - case F_SelectedElements: - ChangeSelectedElementSide(); - break; - default: - err = 1; - break; - } - if (!err) - return 0; - } - - AFAIL(flip); -} - - /* --------------------------------------------------------------------------- * !!! no action routine !!! * @@ -1404,119 +1355,6 @@ } } -/* --------------------------------------------------------------------------- */ - -static const char disperseelements_syntax[] = "DisperseElements(All|Selected)"; - -static const char disperseelements_help[] = "Disperses elements."; - -/* %start-doc actions DisperseElements - -Normally this is used when starting a board, by selecting all elements -and then dispersing them. This scatters the elements around the board -so that you can pick individual ones, rather than have all the -elements at the same 0,0 coordinate and thus impossible to choose -from. - -%end-doc */ - -#define GAP MIL_TO_COORD(100) - -static int ActionDisperseElements(int argc, char **argv, Coord x, Coord y) -{ - char *function = ARG(0); - Coord minx = GAP, miny = GAP, maxy = GAP, dx, dy; - int all = 0, bad = 0; - - if (!function || !*function) { - bad = 1; - } - else { - switch (GetFunctionID(function)) { - case F_All: - all = 1; - break; - - case F_Selected: - all = 0; - break; - - default: - bad = 1; - } - } - - if (bad) { - AFAIL(disperseelements); - } - - - ELEMENT_LOOP(PCB->Data); - { - /* - * If we want to disperse selected elements, maybe we need smarter - * code here to avoid putting components on top of others which - * are not selected. For now, I'm assuming that this is typically - * going to be used either with a brand new design or a scratch - * design holding some new components - */ - if (!TEST_FLAG(LOCKFLAG, element) && (all || TEST_FLAG(SELECTEDFLAG, element))) { - - /* figure out how much to move the element */ - dx = minx - element->BoundingBox.X1; - - /* snap to the grid */ - dx -= (element->MarkX + dx) % PCB->Grid; - - /* - * and add one grid size so we make sure we always space by GAP or - * more - */ - dx += PCB->Grid; - - /* Figure out if this row has room. If not, start a new row */ - if (GAP + element->BoundingBox.X2 + dx > PCB->MaxWidth) { - miny = maxy + GAP; - minx = GAP; - } - - /* figure out how much to move the element */ - dx = minx - element->BoundingBox.X1; - dy = miny - element->BoundingBox.Y1; - - /* snap to the grid */ - dx -= (element->MarkX + dx) % PCB->Grid; - dx += PCB->Grid; - dy -= (element->MarkY + dy) % PCB->Grid; - dy += PCB->Grid; - - /* move the element */ - MoveElementLowLevel(PCB->Data, element, dx, dy); - - /* and add to the undo list so we can undo this operation */ - AddObjectToMoveUndoList(ELEMENT_TYPE, NULL, NULL, element, dx, dy); - - /* keep track of how tall this row is */ - minx += element->BoundingBox.X2 - element->BoundingBox.X1 + GAP; - if (maxy < element->BoundingBox.Y2) { - maxy = element->BoundingBox.Y2; - } - } - - } - END_LOOP; - - /* done with our action so increment the undo # */ - IncrementUndoSerialNumber(); - - Redraw(); - SetChangedFlag(true); - - return 0; -} - -#undef GAP - #ifdef BA_TODO /* --------------------------------------------------------------------------- */ @@ -2530,97 +2368,8 @@ return 0; } - /* --------------------------------------------------------------------------- */ -static const char moveobject_syntax[] = "MoveObject(X,Y,dim)"; - -static const char moveobject_help[] = "Moves the object under the crosshair."; - -/* %start-doc actions MoveObject - -The @code{X} and @code{Y} are treated like @code{delta} is for many -other objects. For each, if it's prefixed by @code{+} or @code{-}, -then that amount is relative. Otherwise, it's absolute. Units can be -@code{mil} or @code{mm}; if unspecified, units are PCB's internal -units, currently 1/100 mil. - -%end-doc */ - -static int ActionMoveObject(int argc, char **argv, Coord x, Coord y) -{ - char *x_str = ARG(0); - char *y_str = ARG(1); - char *units = ARG(2); - Coord nx, ny; - bool absolute1, absolute2; - void *ptr1, *ptr2, *ptr3; - int type; - - ny = GetValue(y_str, units, &absolute1); - nx = GetValue(x_str, units, &absolute2); - - type = SearchScreen(x, y, MOVE_TYPES, &ptr1, &ptr2, &ptr3); - if (type == NO_TYPE) { - Message(_("Nothing found under crosshair\n")); - return 1; - } - if (absolute1) - nx -= x; - if (absolute2) - ny -= y; - Crosshair.AttachedObject.RubberbandN = 0; - if (TEST_FLAG(RUBBERBANDFLAG, PCB)) - LookupRubberbandLines(type, ptr1, ptr2, ptr3); - if (type == ELEMENT_TYPE) - LookupRatLines(type, ptr1, ptr2, ptr3); - MoveObjectAndRubberband(type, ptr1, ptr2, ptr3, nx, ny); - SetChangedFlag(true); - return 0; -} - -/* --------------------------------------------------------------------------- */ - -static const char movetocurrentlayer_syntax[] = "MoveToCurrentLayer(Object|SelectedObjects)"; - -static const char movetocurrentlayer_help[] = "Moves objects to the current layer."; - -/* %start-doc actions MoveToCurrentLayer - -Note that moving an element from a component layer to a solder layer, -or from solder to component, won't automatically flip it. Use the -@code{Flip()} action to do that. - -%end-doc */ - -static int ActionMoveToCurrentLayer(int argc, char **argv, Coord x, Coord y) -{ - char *function = ARG(0); - if (function) { - switch (GetFunctionID(function)) { - case F_Object: - { - int type; - void *ptr1, *ptr2, *ptr3; - - gui->get_coords(_("Select an Object"), &x, &y); - if ((type = SearchScreen(x, y, MOVETOLAYER_TYPES, &ptr1, &ptr2, &ptr3)) != NO_TYPE) - if (MoveObjectToLayer(type, ptr1, ptr2, ptr3, CURRENT, false)) - SetChangedFlag(true); - break; - } - - case F_SelectedObjects: - case F_Selected: - if (MoveSelectedObjectsToLayer(CURRENT)) - SetChangedFlag(true); - break; - } - } - return 0; -} - - static const char setsame_syntax[] = "SetSame()"; static const char setsame_help[] = "Sets current layer and sizes to match indicated item."; @@ -2755,412 +2504,7 @@ return 0; } -/* --------------------------------------------------------------------------- */ - -static ElementType *element_cache = NULL; - -static ElementType *find_element_by_refdes(char *refdes) -{ - if (element_cache && NAMEONPCB_NAME(element_cache) - && strcmp(NAMEONPCB_NAME(element_cache), refdes) == 0) - return element_cache; - - ELEMENT_LOOP(PCB->Data); - { - if (NAMEONPCB_NAME(element) - && strcmp(NAMEONPCB_NAME(element), refdes) == 0) { - element_cache = element; - return element_cache; - } - } - END_LOOP; - return NULL; -} - -static AttributeType *lookup_attr(AttributeListTypePtr list, const char *name) -{ - int i; - for (i = 0; i < list->Number; i++) - if (strcmp(list->List[i].name, name) == 0) - return &list->List[i]; - return NULL; -} - -static void delete_attr(AttributeListTypePtr list, AttributeType * attr) -{ - int idx = attr - list->List; - if (idx < 0 || idx >= list->Number) - return; - if (list->Number - idx > 1) - memmove(attr, attr + 1, (list->Number - idx - 1) * sizeof(AttributeType)); - list->Number--; -} - /* ---------------------------------------------------------------- */ -static const char elementlist_syntax[] = "ElementList(Start|Done|Need,,,)"; - -static const char elementlist_help[] = "Adds the given element if it doesn't already exist."; - -/* %start-doc actions elementlist - -@table @code - -@item Start -Indicates the start of an element list; call this before any Need -actions. - -@item Need -Searches the board for an element with a matching refdes. - -If found, the value and footprint are updated. - -If not found, a new element is created with the given footprint and value. - -@item Done -Compares the list of elements needed since the most recent -@code{start} with the list of elements actually on the board. Any -elements that weren't listed are selected, so that the user may delete -them. - -@end table - -%end-doc */ - -static int number_of_footprints_not_found; - -static int parse_layout_attribute_units(char *name, int def) -{ - const char *as = AttributeGet(PCB, name); - if (!as) - return def; - return GetValue(as, NULL, NULL); -} - -static int ActionElementList(int argc, char **argv, Coord x, Coord y) -{ - ElementType *e = NULL; - char *refdes, *value, *footprint, *old; - char *args[3]; - char *function = argv[0]; - -#ifdef DEBUG - printf("Entered ActionElementList, executing function %s\n", function); -#endif - - if (strcasecmp(function, "start") == 0) { - ELEMENT_LOOP(PCB->Data); - { - CLEAR_FLAG(FOUNDFLAG, element); - } - END_LOOP; - element_cache = NULL; - number_of_footprints_not_found = 0; - return 0; - } - - if (strcasecmp(function, "done") == 0) { - ELEMENT_LOOP(PCB->Data); - { - if (TEST_FLAG(FOUNDFLAG, element)) { - CLEAR_FLAG(FOUNDFLAG, element); - } - else if (!EMPTY_STRING_P(NAMEONPCB_NAME(element))) { - /* Unnamed elements should remain untouched */ - SET_FLAG(SELECTEDFLAG, element); - } - } - END_LOOP; - if (number_of_footprints_not_found > 0) - gui->confirm_dialog("Not all requested footprints were found.\n" "See the message log for details", "Ok", NULL); - return 0; - } - - if (strcasecmp(function, "need") != 0) - AFAIL(elementlist); - - if (argc != 4) - AFAIL(elementlist); - - argc--; - argv++; - - refdes = ARG(0); - footprint = ARG(1); - value = ARG(2); - - args[0] = footprint; - args[1] = refdes; - args[2] = value; - -#ifdef DEBUG - printf(" ... footprint = %s\n", footprint); - printf(" ... refdes = %s\n", refdes); - printf(" ... value = %s\n", value); -#endif - - e = find_element_by_refdes(refdes); - - if (!e) { - Coord nx, ny, d; - -#ifdef DEBUG - printf(" ... Footprint not on board, need to add it.\n"); -#endif - /* Not on board, need to add it. */ - if (LoadFootprint(argc, args, x, y)) { - number_of_footprints_not_found++; - return 1; - } - - nx = PCB->MaxWidth / 2; - ny = PCB->MaxHeight / 2; - d = MIN(PCB->MaxWidth, PCB->MaxHeight) / 10; - - nx = parse_layout_attribute_units("import::newX", nx); - ny = parse_layout_attribute_units("import::newY", ny); - d = parse_layout_attribute_units("import::disperse", d); - - if (d > 0) { - nx += rand() % (d * 2) - d; - ny += rand() % (d * 2) - d; - } - - if (nx < 0) - nx = 0; - if (nx >= PCB->MaxWidth) - nx = PCB->MaxWidth - 1; - if (ny < 0) - ny = 0; - if (ny >= PCB->MaxHeight) - ny = PCB->MaxHeight - 1; - - /* Place components onto center of board. */ - if (CopyPastebufferToLayout(nx, ny)) - SetChangedFlag(true); - } - - else if (e && strcmp(DESCRIPTION_NAME(e), footprint) != 0) { -#ifdef DEBUG - printf(" ... Footprint on board, but different from footprint loaded.\n"); -#endif - int er, pr, i; - Coord mx, my; - ElementType *pe; - - /* Different footprint, we need to swap them out. */ - if (LoadFootprint(argc, args, x, y)) { - number_of_footprints_not_found++; - return 1; - } - - er = ElementOrientation(e); - pe = PASTEBUFFER->Data->Element->data; - if (!FRONT(e)) - MirrorElementCoordinates(PASTEBUFFER->Data, pe, pe->MarkY * 2 - PCB->MaxHeight); - pr = ElementOrientation(pe); - - mx = e->MarkX; - my = e->MarkY; - - if (er != pr) - RotateElementLowLevel(PASTEBUFFER->Data, pe, pe->MarkX, pe->MarkY, (er - pr + 4) % 4); - - for (i = 0; i < MAX_ELEMENTNAMES; i++) { - pe->Name[i].X = e->Name[i].X - mx + pe->MarkX; - pe->Name[i].Y = e->Name[i].Y - my + pe->MarkY; - pe->Name[i].Direction = e->Name[i].Direction; - pe->Name[i].Scale = e->Name[i].Scale; - } - - RemoveElement(e); - - if (CopyPastebufferToLayout(mx, my)) - SetChangedFlag(true); - } - - /* Now reload footprint */ - element_cache = NULL; - e = find_element_by_refdes(refdes); - - old = ChangeElementText(PCB, PCB->Data, e, NAMEONPCB_INDEX, strdup(refdes)); - if (old) - free(old); - old = ChangeElementText(PCB, PCB->Data, e, VALUE_INDEX, strdup(value)); - if (old) - free(old); - - SET_FLAG(FOUNDFLAG, e); - -#ifdef DEBUG - printf(" ... Leaving ActionElementList.\n"); -#endif - - return 0; -} - -/* ---------------------------------------------------------------- */ -static const char elementsetattr_syntax[] = "ElementSetAttr(refdes,name[,value])"; - -static const char elementsetattr_help[] = "Sets or clears an element-specific attribute."; - -/* %start-doc actions elementsetattr - -If a value is specified, the named attribute is added (if not already -present) or changed (if it is) to the given value. If the value is -not specified, the given attribute is removed if present. - -%end-doc */ - -static int ActionElementSetAttr(int argc, char **argv, Coord x, Coord y) -{ - ElementType *e = NULL; - char *refdes, *name, *value; - AttributeType *attr; - - if (argc < 2) { - AFAIL(elementsetattr); - } - - refdes = argv[0]; - name = argv[1]; - value = ARG(2); - - ELEMENT_LOOP(PCB->Data); - { - if (NSTRCMP(refdes, NAMEONPCB_NAME(element)) == 0) { - e = element; - break; - } - } - END_LOOP; - - if (!e) { - Message(_("Cannot change attribute of %s - element not found\n"), refdes); - return 1; - } - - attr = lookup_attr(&e->Attributes, name); - - if (attr && value) { - free(attr->value); - attr->value = strdup(value); - } - if (attr && !value) { - delete_attr(&e->Attributes, attr); - } - if (!attr && value) { - CreateNewAttribute(&e->Attributes, name, value); - } - - return 0; -} - -/* ------------------------------------------------------------ */ - -static const char attributes_syntax[] = "Attributes(Layout|Layer|Element)\n" "Attributes(Layer,layername)"; - -static const char attributes_help[] = - "Let the user edit the attributes of the layout, current or given\n" "layer, or selected element."; - -/* %start-doc actions Attributes - -This just pops up a dialog letting the user edit the attributes of the -pcb, an element, or a layer. - -%end-doc */ - - -static int ActionAttributes(int argc, char **argv, Coord x, Coord y) -{ - char *function = ARG(0); - char *layername = ARG(1); - char *buf; - - if (!function) - AFAIL(attributes); - - if (!gui->edit_attributes) { - Message(_("This GUI doesn't support Attribute Editing\n")); - return 1; - } - - switch (GetFunctionID(function)) { - case F_Layout: - { - gui->edit_attributes("Layout Attributes", &(PCB->Attributes)); - return 0; - } - - case F_Layer: - { - LayerType *layer = CURRENT; - if (layername) { - int i; - layer = NULL; - for (i = 0; i < max_copper_layer; i++) - if (strcmp(PCB->Data->Layer[i].Name, layername) == 0) { - layer = &(PCB->Data->Layer[i]); - break; - } - if (layer == NULL) { - Message(_("No layer named %s\n"), layername); - return 1; - } - } - buf = (char *) malloc(strlen(layer->Name) + strlen("Layer X Attributes")); - sprintf(buf, "Layer %s Attributes", layer->Name); - gui->edit_attributes(buf, &(layer->Attributes)); - free(buf); - return 0; - } - - case F_Element: - { - int n_found = 0; - ElementType *e = NULL; - ELEMENT_LOOP(PCB->Data); - { - if (TEST_FLAG(SELECTEDFLAG, element)) { - e = element; - n_found++; - } - } - END_LOOP; - if (n_found > 1) { - Message(_("Too many elements selected\n")); - return 1; - } - if (n_found == 0) { - void *ptrtmp; - gui->get_coords(_("Click on an element"), &x, &y); - if ((SearchScreen(x, y, ELEMENT_TYPE, &ptrtmp, &ptrtmp, &ptrtmp)) != NO_TYPE) - e = (ElementTypePtr) ptrtmp; - else { - Message(_("No element found there\n")); - return 1; - } - } - - if (NAMEONPCB_NAME(e)) { - buf = (char *) malloc(strlen(NAMEONPCB_NAME(e)) + strlen("Element X Attributes")); - sprintf(buf, "Element %s Attributes", NAMEONPCB_NAME(e)); - } - else { - buf = strdup("Unnamed Element Attributes"); - } - gui->edit_attributes(buf, &(e->Attributes)); - free(buf); - break; - } - - default: - AFAIL(attributes); - } - - return 0; -} - -/* ---------------------------------------------------------------- */ static const char replacefootprint_syntax[] = "ReplaceFootprint()\n"; static const char replacefootprint_help[] = "Replace the footprint of the selected components with the footprint specified."; @@ -3229,12 +2573,6 @@ /* --------------------------------------------------------------------------- */ HID_Action action_action_list[] = { - {"Attributes", 0, ActionAttributes, - attributes_help, attributes_syntax} - , - {"DisperseElements", 0, ActionDisperseElements, - disperseelements_help, disperseelements_syntax} - , {"DRC", 0, ActionDRCheck, drc_help, drc_syntax} , @@ -3244,9 +2582,6 @@ {"ExecuteFile", 0, ActionExecuteFile, executefile_help, executefile_syntax} , - {"Flip", N_("Click on Object or Flip Point"), ActionFlip, - flip_help, flip_syntax} - , {"MarkCrosshair", 0, ActionMarkCrosshair, markcrosshair_help, markcrosshair_syntax} , @@ -3279,18 +2614,6 @@ {"RouteStyle", 0, ActionRouteStyle, routestyle_help, routestyle_syntax} , - {"MoveObject", N_("Select an Object"), ActionMoveObject, - moveobject_help, moveobject_syntax} - , - {"MoveToCurrentLayer", 0, ActionMoveToCurrentLayer, - movetocurrentlayer_help, movetocurrentlayer_syntax} - , - {"ElementList", 0, ActionElementList, - elementlist_help, elementlist_syntax} - , - {"ElementSetAttr", 0, ActionElementSetAttr, - elementsetattr_help, elementsetattr_syntax} - , {"ReplaceFootprint", 0, ReplaceFootprint, replacefootprint_help, replacefootprint_syntax} , Index: src/action_list.h =================================================================== --- src/action_list.h (revision 1088) +++ src/action_list.h (revision 1089) @@ -75,6 +75,9 @@ /* hid/gcode (export) */ REGISTER_ATTRIBUTES(gcode_attribute_list) +/* object_act.c () */ +REGISTER_ACTIONS(object_action_list) + /* hid/batch (gui) */ if ((gui != NULL) && (strcmp(gui->name, "batch") == 0)) { REGISTER_ACTIONS(batch_action_list) Index: src/object_act.c =================================================================== --- src/object_act.c (nonexistent) +++ src/object_act.c (revision 1089) @@ -0,0 +1,728 @@ +/* + * COPYRIGHT + * + * PCB, interactive printed circuit board design + * Copyright (C) 1994,1995,1996 Thomas Nau + * Copyright (C) 1997, 1998, 1999, 2000, 2001 Harry Eaton + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Contact addresses for paper mail and Email: + * Harry Eaton, 6697 Buttonhole Ct, Columbia, MD 21044, USA + * haceaton@aplcomm.jhuapl.edu + * + */ +#include "config.h" +#include "global.h" +#include "data.h" +#include "action.h" +#include "change.h" +#include "error.h" +#include "undo.h" + +#include "search.h" +#include "move.h" +#include "draw.h" +#include "mirror.h" +#include "rotate.h" +#include "set.h" +#include "copy.h" +#include "misc.h" +#include "rubberband.h" +#include "buffer.h" +#include "remove.h" +#include "create.h" + +/* --------------------------------------------------------------------------- */ + +static ElementType *element_cache = NULL; + +static ElementType *find_element_by_refdes(char *refdes) +{ + if (element_cache && NAMEONPCB_NAME(element_cache) + && strcmp(NAMEONPCB_NAME(element_cache), refdes) == 0) + return element_cache; + + ELEMENT_LOOP(PCB->Data); + { + if (NAMEONPCB_NAME(element) + && strcmp(NAMEONPCB_NAME(element), refdes) == 0) { + element_cache = element; + return element_cache; + } + } + END_LOOP; + return NULL; +} + +static AttributeType *lookup_attr(AttributeListTypePtr list, const char *name) +{ + int i; + for (i = 0; i < list->Number; i++) + if (strcmp(list->List[i].name, name) == 0) + return &list->List[i]; + return NULL; +} + +static void delete_attr(AttributeListTypePtr list, AttributeType * attr) +{ + int idx = attr - list->List; + if (idx < 0 || idx >= list->Number) + return; + if (list->Number - idx > 1) + memmove(attr, attr + 1, (list->Number - idx - 1) * sizeof(AttributeType)); + list->Number--; +} + +/* ------------------------------------------------------------ */ + +static const char attributes_syntax[] = "Attributes(Layout|Layer|Element)\n" "Attributes(Layer,layername)"; + +static const char attributes_help[] = + "Let the user edit the attributes of the layout, current or given\n" "layer, or selected element."; + +/* %start-doc actions Attributes + +This just pops up a dialog letting the user edit the attributes of the +pcb, an element, or a layer. + +%end-doc */ + + +static int ActionAttributes(int argc, char **argv, Coord x, Coord y) +{ + char *function = ARG(0); + char *layername = ARG(1); + char *buf; + + if (!function) + AFAIL(attributes); + + if (!gui->edit_attributes) { + Message(_("This GUI doesn't support Attribute Editing\n")); + return 1; + } + + switch (GetFunctionID(function)) { + case F_Layout: + { + gui->edit_attributes("Layout Attributes", &(PCB->Attributes)); + return 0; + } + + case F_Layer: + { + LayerType *layer = CURRENT; + if (layername) { + int i; + layer = NULL; + for (i = 0; i < max_copper_layer; i++) + if (strcmp(PCB->Data->Layer[i].Name, layername) == 0) { + layer = &(PCB->Data->Layer[i]); + break; + } + if (layer == NULL) { + Message(_("No layer named %s\n"), layername); + return 1; + } + } + buf = (char *) malloc(strlen(layer->Name) + strlen("Layer X Attributes")); + sprintf(buf, "Layer %s Attributes", layer->Name); + gui->edit_attributes(buf, &(layer->Attributes)); + free(buf); + return 0; + } + + case F_Element: + { + int n_found = 0; + ElementType *e = NULL; + ELEMENT_LOOP(PCB->Data); + { + if (TEST_FLAG(SELECTEDFLAG, element)) { + e = element; + n_found++; + } + } + END_LOOP; + if (n_found > 1) { + Message(_("Too many elements selected\n")); + return 1; + } + if (n_found == 0) { + void *ptrtmp; + gui->get_coords(_("Click on an element"), &x, &y); + if ((SearchScreen(x, y, ELEMENT_TYPE, &ptrtmp, &ptrtmp, &ptrtmp)) != NO_TYPE) + e = (ElementTypePtr) ptrtmp; + else { + Message(_("No element found there\n")); + return 1; + } + } + + if (NAMEONPCB_NAME(e)) { + buf = (char *) malloc(strlen(NAMEONPCB_NAME(e)) + strlen("Element X Attributes")); + sprintf(buf, "Element %s Attributes", NAMEONPCB_NAME(e)); + } + else { + buf = strdup("Unnamed Element Attributes"); + } + gui->edit_attributes(buf, &(e->Attributes)); + free(buf); + break; + } + + default: + AFAIL(attributes); + } + + return 0; +} + +/* --------------------------------------------------------------------------- */ + +static const char disperseelements_syntax[] = "DisperseElements(All|Selected)"; + +static const char disperseelements_help[] = "Disperses elements."; + +/* %start-doc actions DisperseElements + +Normally this is used when starting a board, by selecting all elements +and then dispersing them. This scatters the elements around the board +so that you can pick individual ones, rather than have all the +elements at the same 0,0 coordinate and thus impossible to choose +from. + +%end-doc */ + +#define GAP MIL_TO_COORD(100) + +static int ActionDisperseElements(int argc, char **argv, Coord x, Coord y) +{ + char *function = ARG(0); + Coord minx = GAP, miny = GAP, maxy = GAP, dx, dy; + int all = 0, bad = 0; + + if (!function || !*function) { + bad = 1; + } + else { + switch (GetFunctionID(function)) { + case F_All: + all = 1; + break; + + case F_Selected: + all = 0; + break; + + default: + bad = 1; + } + } + + if (bad) { + AFAIL(disperseelements); + } + + + ELEMENT_LOOP(PCB->Data); + { + /* + * If we want to disperse selected elements, maybe we need smarter + * code here to avoid putting components on top of others which + * are not selected. For now, I'm assuming that this is typically + * going to be used either with a brand new design or a scratch + * design holding some new components + */ + if (!TEST_FLAG(LOCKFLAG, element) && (all || TEST_FLAG(SELECTEDFLAG, element))) { + + /* figure out how much to move the element */ + dx = minx - element->BoundingBox.X1; + + /* snap to the grid */ + dx -= (element->MarkX + dx) % PCB->Grid; + + /* + * and add one grid size so we make sure we always space by GAP or + * more + */ + dx += PCB->Grid; + + /* Figure out if this row has room. If not, start a new row */ + if (GAP + element->BoundingBox.X2 + dx > PCB->MaxWidth) { + miny = maxy + GAP; + minx = GAP; + } + + /* figure out how much to move the element */ + dx = minx - element->BoundingBox.X1; + dy = miny - element->BoundingBox.Y1; + + /* snap to the grid */ + dx -= (element->MarkX + dx) % PCB->Grid; + dx += PCB->Grid; + dy -= (element->MarkY + dy) % PCB->Grid; + dy += PCB->Grid; + + /* move the element */ + MoveElementLowLevel(PCB->Data, element, dx, dy); + + /* and add to the undo list so we can undo this operation */ + AddObjectToMoveUndoList(ELEMENT_TYPE, NULL, NULL, element, dx, dy); + + /* keep track of how tall this row is */ + minx += element->BoundingBox.X2 - element->BoundingBox.X1 + GAP; + if (maxy < element->BoundingBox.Y2) { + maxy = element->BoundingBox.Y2; + } + } + + } + END_LOOP; + + /* done with our action so increment the undo # */ + IncrementUndoSerialNumber(); + + Redraw(); + SetChangedFlag(true); + + return 0; +} + +#undef GAP + +/* -------------------------------------------------------------------------- */ + +static const char flip_syntax[] = "Flip(Object|Selected|SelectedElements)"; + +static const char flip_help[] = "Flip an element to the opposite side of the board."; + +/* %start-doc actions Flip + +Note that the location of the element will be symmetric about the +cursor location; i.e. if the part you are pointing at will still be at +the same spot once the element is on the other side. When flipping +multiple elements, this retains their positions relative to each +other, not their absolute positions on the board. + +%end-doc */ + +static int ActionFlip(int argc, char **argv, Coord x, Coord y) +{ + char *function = ARG(0); + ElementTypePtr element; + void *ptrtmp; + int err = 0; + + if (function) { + switch (GetFunctionID(function)) { + case F_Object: + if ((SearchScreen(x, y, ELEMENT_TYPE, &ptrtmp, &ptrtmp, &ptrtmp)) != NO_TYPE) { + element = (ElementTypePtr) ptrtmp; + ChangeElementSide(element, 2 * Crosshair.Y - PCB->MaxHeight); + IncrementUndoSerialNumber(); + Draw(); + } + break; + case F_Selected: + case F_SelectedElements: + ChangeSelectedElementSide(); + break; + default: + err = 1; + break; + } + if (!err) + return 0; + } + + AFAIL(flip); +} +/* --------------------------------------------------------------------------- */ + +static const char moveobject_syntax[] = "MoveObject(X,Y,dim)"; + +static const char moveobject_help[] = "Moves the object under the crosshair."; + +/* %start-doc actions MoveObject + +The @code{X} and @code{Y} are treated like @code{delta} is for many +other objects. For each, if it's prefixed by @code{+} or @code{-}, +then that amount is relative. Otherwise, it's absolute. Units can be +@code{mil} or @code{mm}; if unspecified, units are PCB's internal +units, currently 1/100 mil. + +%end-doc */ + +static int ActionMoveObject(int argc, char **argv, Coord x, Coord y) +{ + char *x_str = ARG(0); + char *y_str = ARG(1); + char *units = ARG(2); + Coord nx, ny; + bool absolute1, absolute2; + void *ptr1, *ptr2, *ptr3; + int type; + + ny = GetValue(y_str, units, &absolute1); + nx = GetValue(x_str, units, &absolute2); + + type = SearchScreen(x, y, MOVE_TYPES, &ptr1, &ptr2, &ptr3); + if (type == NO_TYPE) { + Message(_("Nothing found under crosshair\n")); + return 1; + } + if (absolute1) + nx -= x; + if (absolute2) + ny -= y; + Crosshair.AttachedObject.RubberbandN = 0; + if (TEST_FLAG(RUBBERBANDFLAG, PCB)) + LookupRubberbandLines(type, ptr1, ptr2, ptr3); + if (type == ELEMENT_TYPE) + LookupRatLines(type, ptr1, ptr2, ptr3); + MoveObjectAndRubberband(type, ptr1, ptr2, ptr3, nx, ny); + SetChangedFlag(true); + return 0; +} + +/* --------------------------------------------------------------------------- */ + +static const char movetocurrentlayer_syntax[] = "MoveToCurrentLayer(Object|SelectedObjects)"; + +static const char movetocurrentlayer_help[] = "Moves objects to the current layer."; + +/* %start-doc actions MoveToCurrentLayer + +Note that moving an element from a component layer to a solder layer, +or from solder to component, won't automatically flip it. Use the +@code{Flip()} action to do that. + +%end-doc */ + +static int ActionMoveToCurrentLayer(int argc, char **argv, Coord x, Coord y) +{ + char *function = ARG(0); + if (function) { + switch (GetFunctionID(function)) { + case F_Object: + { + int type; + void *ptr1, *ptr2, *ptr3; + + gui->get_coords(_("Select an Object"), &x, &y); + if ((type = SearchScreen(x, y, MOVETOLAYER_TYPES, &ptr1, &ptr2, &ptr3)) != NO_TYPE) + if (MoveObjectToLayer(type, ptr1, ptr2, ptr3, CURRENT, false)) + SetChangedFlag(true); + break; + } + + case F_SelectedObjects: + case F_Selected: + if (MoveSelectedObjectsToLayer(CURRENT)) + SetChangedFlag(true); + break; + } + } + return 0; +} + +/* ---------------------------------------------------------------- */ +static const char elementlist_syntax[] = "ElementList(Start|Done|Need,,,)"; + +static const char elementlist_help[] = "Adds the given element if it doesn't already exist."; + +/* %start-doc actions elementlist + +@table @code + +@item Start +Indicates the start of an element list; call this before any Need +actions. + +@item Need +Searches the board for an element with a matching refdes. + +If found, the value and footprint are updated. + +If not found, a new element is created with the given footprint and value. + +@item Done +Compares the list of elements needed since the most recent +@code{start} with the list of elements actually on the board. Any +elements that weren't listed are selected, so that the user may delete +them. + +@end table + +%end-doc */ + +static int number_of_footprints_not_found; + +static int parse_layout_attribute_units(char *name, int def) +{ + const char *as = AttributeGet(PCB, name); + if (!as) + return def; + return GetValue(as, NULL, NULL); +} + +static int ActionElementList(int argc, char **argv, Coord x, Coord y) +{ + ElementType *e = NULL; + char *refdes, *value, *footprint, *old; + char *args[3]; + char *function = argv[0]; + +#ifdef DEBUG + printf("Entered ActionElementList, executing function %s\n", function); +#endif + + if (strcasecmp(function, "start") == 0) { + ELEMENT_LOOP(PCB->Data); + { + CLEAR_FLAG(FOUNDFLAG, element); + } + END_LOOP; + element_cache = NULL; + number_of_footprints_not_found = 0; + return 0; + } + + if (strcasecmp(function, "done") == 0) { + ELEMENT_LOOP(PCB->Data); + { + if (TEST_FLAG(FOUNDFLAG, element)) { + CLEAR_FLAG(FOUNDFLAG, element); + } + else if (!EMPTY_STRING_P(NAMEONPCB_NAME(element))) { + /* Unnamed elements should remain untouched */ + SET_FLAG(SELECTEDFLAG, element); + } + } + END_LOOP; + if (number_of_footprints_not_found > 0) + gui->confirm_dialog("Not all requested footprints were found.\n" "See the message log for details", "Ok", NULL); + return 0; + } + + if (strcasecmp(function, "need") != 0) + AFAIL(elementlist); + + if (argc != 4) + AFAIL(elementlist); + + argc--; + argv++; + + refdes = ARG(0); + footprint = ARG(1); + value = ARG(2); + + args[0] = footprint; + args[1] = refdes; + args[2] = value; + +#ifdef DEBUG + printf(" ... footprint = %s\n", footprint); + printf(" ... refdes = %s\n", refdes); + printf(" ... value = %s\n", value); +#endif + + e = find_element_by_refdes(refdes); + + if (!e) { + Coord nx, ny, d; + +#ifdef DEBUG + printf(" ... Footprint not on board, need to add it.\n"); +#endif + /* Not on board, need to add it. */ + if (LoadFootprint(argc, args, x, y)) { + number_of_footprints_not_found++; + return 1; + } + + nx = PCB->MaxWidth / 2; + ny = PCB->MaxHeight / 2; + d = MIN(PCB->MaxWidth, PCB->MaxHeight) / 10; + + nx = parse_layout_attribute_units("import::newX", nx); + ny = parse_layout_attribute_units("import::newY", ny); + d = parse_layout_attribute_units("import::disperse", d); + + if (d > 0) { + nx += rand() % (d * 2) - d; + ny += rand() % (d * 2) - d; + } + + if (nx < 0) + nx = 0; + if (nx >= PCB->MaxWidth) + nx = PCB->MaxWidth - 1; + if (ny < 0) + ny = 0; + if (ny >= PCB->MaxHeight) + ny = PCB->MaxHeight - 1; + + /* Place components onto center of board. */ + if (CopyPastebufferToLayout(nx, ny)) + SetChangedFlag(true); + } + + else if (e && strcmp(DESCRIPTION_NAME(e), footprint) != 0) { +#ifdef DEBUG + printf(" ... Footprint on board, but different from footprint loaded.\n"); +#endif + int er, pr, i; + Coord mx, my; + ElementType *pe; + + /* Different footprint, we need to swap them out. */ + if (LoadFootprint(argc, args, x, y)) { + number_of_footprints_not_found++; + return 1; + } + + er = ElementOrientation(e); + pe = PASTEBUFFER->Data->Element->data; + if (!FRONT(e)) + MirrorElementCoordinates(PASTEBUFFER->Data, pe, pe->MarkY * 2 - PCB->MaxHeight); + pr = ElementOrientation(pe); + + mx = e->MarkX; + my = e->MarkY; + + if (er != pr) + RotateElementLowLevel(PASTEBUFFER->Data, pe, pe->MarkX, pe->MarkY, (er - pr + 4) % 4); + + for (i = 0; i < MAX_ELEMENTNAMES; i++) { + pe->Name[i].X = e->Name[i].X - mx + pe->MarkX; + pe->Name[i].Y = e->Name[i].Y - my + pe->MarkY; + pe->Name[i].Direction = e->Name[i].Direction; + pe->Name[i].Scale = e->Name[i].Scale; + } + + RemoveElement(e); + + if (CopyPastebufferToLayout(mx, my)) + SetChangedFlag(true); + } + + /* Now reload footprint */ + element_cache = NULL; + e = find_element_by_refdes(refdes); + + old = ChangeElementText(PCB, PCB->Data, e, NAMEONPCB_INDEX, strdup(refdes)); + if (old) + free(old); + old = ChangeElementText(PCB, PCB->Data, e, VALUE_INDEX, strdup(value)); + if (old) + free(old); + + SET_FLAG(FOUNDFLAG, e); + +#ifdef DEBUG + printf(" ... Leaving ActionElementList.\n"); +#endif + + return 0; +} + +/* ---------------------------------------------------------------- */ +static const char elementsetattr_syntax[] = "ElementSetAttr(refdes,name[,value])"; + +static const char elementsetattr_help[] = "Sets or clears an element-specific attribute."; + +/* %start-doc actions elementsetattr + +If a value is specified, the named attribute is added (if not already +present) or changed (if it is) to the given value. If the value is +not specified, the given attribute is removed if present. + +%end-doc */ + +static int ActionElementSetAttr(int argc, char **argv, Coord x, Coord y) +{ + ElementType *e = NULL; + char *refdes, *name, *value; + AttributeType *attr; + + if (argc < 2) { + AFAIL(elementsetattr); + } + + refdes = argv[0]; + name = argv[1]; + value = ARG(2); + + ELEMENT_LOOP(PCB->Data); + { + if (NSTRCMP(refdes, NAMEONPCB_NAME(element)) == 0) { + e = element; + break; + } + } + END_LOOP; + + if (!e) { + Message(_("Cannot change attribute of %s - element not found\n"), refdes); + return 1; + } + + attr = lookup_attr(&e->Attributes, name); + + if (attr && value) { + free(attr->value); + attr->value = strdup(value); + } + if (attr && !value) { + delete_attr(&e->Attributes, attr); + } + if (!attr && value) { + CreateNewAttribute(&e->Attributes, name, value); + } + + return 0; +} + + +HID_Action object_action_list[] = { + {"Attributes", 0, ActionAttributes, + attributes_help, attributes_syntax} + , + {"DisperseElements", 0, ActionDisperseElements, + disperseelements_help, disperseelements_syntax} + , + {"Flip", N_("Click on Object or Flip Point"), ActionFlip, + flip_help, flip_syntax} + , + {"MoveObject", N_("Select an Object"), ActionMoveObject, + moveobject_help, moveobject_syntax} + , + {"MoveToCurrentLayer", 0, ActionMoveToCurrentLayer, + movetocurrentlayer_help, movetocurrentlayer_syntax} + , + {"ElementList", 0, ActionElementList, + elementlist_help, elementlist_syntax} + , + {"ElementSetAttr", 0, ActionElementSetAttr, + elementsetattr_help, elementsetattr_syntax} + , + +}; + +REGISTER_ACTIONS(object_action_list)