[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

gEDA-cvs: pcb.git: branch: master updated (aa28e8686b2d95f42dfcf476ad20e30ad59f78a1)



The branch, master has been updated
       via  aa28e8686b2d95f42dfcf476ad20e30ad59f78a1 (commit)
       via  041164fc72def5c498e5c2c4ca3615a9d318184d (commit)
       via  54637f522dae03e68661cce77594c9436680b63c (commit)
       via  78f9c04b25459be009357eeef278da58899aabbf (commit)
       via  4099d55781fa04d5b18b1b4fb6dd22907bcb1002 (commit)
       via  dcc6281f399787765d17e0ce9969e9abf9d0b35c (commit)
       via  12c0d8055373b2a43eb5d2d324bfbb0c513d24e8 (commit)
      from  c090b46eeadf2f4aa77e9fe4df1f53b1d5459708 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.


=========
 Summary
=========

 src/action.c                |  660 ++++++++++++++++++++++++++++++++++++++++++-
 src/buffer.c                |  287 +++++++++++++++++++-
 src/buffer.h                |    3 +
 src/change.c                |   35 ++-
 src/change.h                |    6 +
 src/create.c                |    2 +-
 src/data.h                  |    2 +
 src/file.c                  |    2 +-
 src/global.h                |    6 +-
 src/gpcb-menu.res           |    6 +
 src/hid.h                   |    4 +-
 src/hid/batch/batch.c       |    3 +-
 src/hid/bom/bom.c           |    1 +
 src/hid/common/hidnogui.c   |    4 +-
 src/hid/gtk/gtkhid-main.c   |  220 ++++++++++++++-
 src/hid/lesstif/dialogs.c   |  346 ++++++++++++++++++++++
 src/hid/lesstif/lesstif.h   |    1 +
 src/hid/lesstif/main.c      |    5 +-
 src/hid/lesstif/xincludes.h |    1 +
 src/hid/lpr/lpr.c           |    3 +-
 src/hid/nelma/nelma.c       |    3 +-
 src/hid/png/png.c           |    3 +-
 src/hid/ps/eps.c            |    3 +-
 src/hid/ps/ps.c             |    3 +-
 src/misc.c                  |  108 +++++++
 src/misc.h                  |   20 ++
 src/netlist.c               |  187 ++++++++++++-
 src/pcb-menu.res            |    5 +
 src/rats.c                  |    2 +-
 src/undo.c                  |    2 +-
 tools/Makefile.am           |    7 +-
 tools/gnet-pcbfwd.scm       |  115 ++++++++
 32 files changed, 2017 insertions(+), 38 deletions(-)
 create mode 100644 tools/gnet-pcbfwd.scm


=================
 Commit Messages
=================

commit aa28e8686b2d95f42dfcf476ad20e30ad59f78a1
Author: DJ Delorie <dj@xxxxxxxxxxx>
Commit: DJ Delorie <dj@xxxxxxxxxxx>

    Changed element/netlist syntax as follows:
    
    ElementList(start)
    ElementList(need,...)
    ElementList(done)
    
    Netlist(freeze)
    Netlist(add,...)
    Netlist(thaw)
    
    Hooked all NetlistChanged() actions into a wrapper that checks for frozen.

:100644 100644 4b660df... cbf3223... M	src/action.c
:100644 100644 5f0d945... 53618c4... M	src/data.h
:100644 100644 1e3fb86... 3c23338... M	src/hid.h
:100644 100644 f814784... 7f06804... M	src/misc.h
:100644 100644 78c8c3d... 80e94eb... M	src/netlist.c
:100644 100644 35047b5... c174cc9... M	src/rats.c
:100644 100644 998fc2c... cb8d180... M	src/undo.c
:100644 100644 77a2d1a... 002fecc... M	tools/Makefile.am
:000000 100644 0000000... a15cd8a... A	tools/gnet-pcbfwd.scm
:100644 000000 637dca8... 0000000... D	tools/gnet-pcblf.scm

commit 041164fc72def5c498e5c2c4ca3615a9d318184d
Author: DJ Delorie <dj@xxxxxxxxxxx>
Commit: DJ Delorie <dj@xxxxxxxxxxx>

    Add import to gtk menu, change lesstif menu to match.

:100644 100644 2d21a19... 11251b2... M	src/gpcb-menu.res
:100644 100644 6874538... b326a04... M	src/pcb-menu.res

commit 54637f522dae03e68661cce77594c9436680b63c
Author: DJ Delorie <dj@xxxxxxxxxxx>
Commit: DJ Delorie <dj@xxxxxxxxxxx>

    Add the new netlister.
    
    Adds the new script-based netlister and the install rules.  Note
    that this installs PCB's netlister in gEDA's data directory.
    Unusual, but it's the best way to keep pcb in sync with its own netlister.

:100644 100644 d0438d1... 77a2d1a... M	tools/Makefile.am
:000000 100644 0000000... 637dca8... A	tools/gnet-pcblf.scm

commit 78f9c04b25459be009357eeef278da58899aabbf
Author: DJ Delorie <dj@xxxxxxxxxxx>
Commit: DJ Delorie <dj@xxxxxxxxxxx>

    Add attribute editor GUI to gtk and lesstif.
    
    Attributes(Layout|Layer|Element)
    Attributes(Layer,layername)
    
    Let the user edit the attributes of the layout, current or given
    layer, or selected element.

:100644 100644 0ed25e8... 4b660df... M	src/action.c
:100644 100644 fa9f55e... 1462e50... M	src/global.h
:100644 100644 b844f56... 2d21a19... M	src/gpcb-menu.res
:100644 100644 7966346... 1e3fb86... M	src/hid.h
:100644 100644 407bc3a... c10b280... M	src/hid/batch/batch.c
:100644 100644 08d4328... e593bdd... M	src/hid/bom/bom.c
:100644 100644 4d6baa0... 51c95cb... M	src/hid/common/hidnogui.c
:100644 100644 64d8622... fd399ce... M	src/hid/gtk/gtkhid-main.c
:100644 100644 61b8097... 213c5f2... M	src/hid/lesstif/dialogs.c
:100644 100644 fe31371... 54e4ced... M	src/hid/lesstif/lesstif.h
:100644 100644 8ece6ef... 6dfc513... M	src/hid/lesstif/main.c
:100644 100644 44c25e9... 7245a77... M	src/hid/lesstif/xincludes.h
:100644 100644 cf35d8b... e47c40f... M	src/hid/lpr/lpr.c
:100644 100644 ee3a896... dc1cee5... M	src/hid/nelma/nelma.c
:100644 100644 2b4968b... b819533... M	src/hid/png/png.c
:100644 100644 2115b47... b0e7a7d... M	src/hid/ps/eps.c
:100644 100644 e648fa1... 3a92dc3... M	src/hid/ps/ps.c
:100644 100644 80590ac... 6874538... M	src/pcb-menu.res

commit 4099d55781fa04d5b18b1b4fb6dd22907bcb1002
Author: DJ Delorie <dj@xxxxxxxxxxx>
Commit: DJ Delorie <dj@xxxxxxxxxxx>

    Add Import() action
    
    Add an action to import schematics into the pcb.  It allows for a
    list of schematics to be specified in the action or in the pcb,
    as well as supporting using a Makfile instead of gnetlist.

:100644 100644 7b1456a... 0ed25e8... M	src/action.c
:100644 100644 8bb8cf9... 1938dea... M	src/misc.c
:100644 100644 b528ebe... f814784... M	src/misc.h
:100644 100644 5186183... 80590ac... M	src/pcb-menu.res

commit dcc6281f399787765d17e0ce9969e9abf9d0b35c
Author: DJ Delorie <dj@xxxxxxxxxxx>
Commit: DJ Delorie <dj@xxxxxxxxxxx>

    Undo an accidental change.

:100644 100644 73881f3... b843da6... M	src/draw.c

commit 12c0d8055373b2a43eb5d2d324bfbb0c513d24e8
Author: DJ Delorie <dj@xxxxxxxxxxx>
Commit: DJ Delorie <dj@xxxxxxxxxxx>

    Work-in-progress for LF: netlist importing

:100644 100644 9cf163b... 7b1456a... M	src/action.c
:100644 100644 be51a0f... cdb34dd... M	src/buffer.c
:100644 100644 74c7399... 07c21c1... M	src/buffer.h
:100644 100644 0599ee4... f67e682... M	src/change.c
:100644 100644 7f43fba... bd7749a... M	src/change.h
:100644 100644 500d0a0... 518a35b... M	src/create.c
:100644 100644 b843da6... 73881f3... M	src/draw.c
:100644 100644 4cef71f... fd18340... M	src/file.c
:100644 100644 6ff7e74... 8ece6ef... M	src/hid/lesstif/main.c
:100644 100644 2ba104c... 8bb8cf9... M	src/misc.c
:100644 100644 58e75dc... b528ebe... M	src/misc.h
:100644 100644 d9592b0... 78c8c3d... M	src/netlist.c

=========
 Changes
=========

commit aa28e8686b2d95f42dfcf476ad20e30ad59f78a1
Author: DJ Delorie <dj@xxxxxxxxxxx>
Commit: DJ Delorie <dj@xxxxxxxxxxx>

    Changed element/netlist syntax as follows:
    
    ElementList(start)
    ElementList(need,...)
    ElementList(done)
    
    Netlist(freeze)
    Netlist(add,...)
    Netlist(thaw)
    
    Hooked all NetlistChanged() actions into a wrapper that checks for frozen.

diff --git a/src/action.c b/src/action.c
index 4b660df..cbf3223 100644
--- a/src/action.c
+++ b/src/action.c
@@ -3486,7 +3486,7 @@ ActionRenumber (int argc, char **argv, int x, int y)
 	  free (is[k]);
 	}
 
-      hid_action ("NetlistChanged");
+      NetlistChanged (0);
       IncrementUndoSerialNumber ();
       SetChangedFlag (True);
     }
@@ -5776,7 +5776,7 @@ ActionLoadFrom (int argc, char **argv, int x, int y)
       PCB->Netlistname = StripWhiteSpaceAndDup (name);
       FreeLibraryMemory (&PCB->NetlistLib);
       if (!ImportNetlist (PCB->Netlistname))
-	hid_action ("NetlistChanged");
+	NetlistChanged (1);
     }
   else if (strcasecmp (function, "Revert") == 0 && PCB->Filename
 	   && (!PCB->Changed
@@ -6855,53 +6855,80 @@ delete_attr (AttributeListTypePtr list, AttributeType *attr)
   list->Number --;
 }
 
-static const char elementaddstart_syntax[] = "ElementAddStart()";
+static const char elementlist_syntax[] = "ElementList(Start|Done|Need,<refdes>,<footprint>,<value>)";
 
-static const char elementaddstart_help[] = "Notes the start of an element set update.";
+static const char elementlist_help[] = "Adds the given element if it doesn't already exist.";
 
-/* %start-doc actions elementaddstart
+/* %start-doc actions elementlist
 
-Used before conditionally adding elements, it clears the list of
-"remembered" elements, so that unlisted elements can be discovered
-later.
-
-%end-doc */
-
-static int
-ActionElementAddStart (int argc, char **argv, int x, int y)
-{
-  ELEMENT_LOOP (PCB->Data);
-  {
-    CLEAR_FLAG (FOUNDFLAG, element);
-  }
-  END_LOOP;
-  element_cache = NULL;
-  return 0;
-}
-
-static const char elementaddif_syntax[] = "ElementAddIf(<refdes>,<footprint>,<value>)";
-
-static const char elementaddif_help[] = "Adds the given element if it doesn't already exist.";
+@table @code
 
-/* %start-doc actions elementaddif
+@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
-ActionElementAddIf (int argc, char **argv, int x, int y)
+ActionElementList (int argc, char **argv, int x, int y)
 {
   ElementType *e = NULL;
   char *refdes, *value, *footprint, *old;
   char *args[3];
+  char *function = argv[0];
 
-  if (argc != 3)
-    AFAIL (elementaddif);
+  if (strcasecmp (function, "start") == 0)
+    {
+      ELEMENT_LOOP (PCB->Data);
+      {
+	CLEAR_FLAG (FOUNDFLAG, element);
+      }
+      END_LOOP;
+      element_cache = NULL;
+      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;
+      return 0;
+    }
+
+  if (strcasecmp (function, "need") != 0)
+    AFAIL (elementlist);
+
+  if (argc != 4)
+    AFAIL (elementlist);
+
+  argc --;
+  argv ++;
 
   refdes = ARG(0);
   footprint = ARG(1);
@@ -6970,37 +6997,6 @@ ActionElementAddIf (int argc, char **argv, int x, int y)
   return 0;
 }
 
-static const char elementadddone_syntax[] = "ElementAddDone()";
-
-static const char elementadddone_help[] = "Notes the end of an element set update";
-
-/* %start-doc actions elementadddone
-
-Used after conditionally adding elements, it finds any unmentioned
-elements which were previously added (non-empty refdes) and deletes
-them.
-
-%end-doc */
-
-static int
-ActionElementAddDone (int argc, char **argv, int x, int y)
-{
-  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;
-  return 0;
-}
-
 static const char elementsetattr_syntax[] = "ElementSetAttr(refdes,name[,value])";
 
 static const char elementsetattr_help[] = "Sets or clears an element-specific attribute";
@@ -7610,14 +7606,8 @@ HID_Action action_action_list[] = {
   ,
   {"pscalib", 0, ActionPSCalib}
   ,
-  {"ElementAddStart", 0, ActionElementAddStart,
-   elementaddstart_help, elementaddstart_syntax}
-  ,
-  {"ElementAddIf", 0, ActionElementAddIf,
-   elementaddif_help, elementaddif_syntax}
-  ,
-  {"ElementAddDone", 0, ActionElementAddDone,
-   elementadddone_help, elementadddone_syntax}
+  {"ElementList", 0, ActionElementList,
+   elementlist_help, elementlist_syntax}
   ,
   {"ElementSetAttr", 0, ActionElementSetAttr,
    elementsetattr_help, elementsetattr_syntax}
diff --git a/src/data.h b/src/data.h
index 5f0d945..53618c4 100644
--- a/src/data.h
+++ b/src/data.h
@@ -74,4 +74,6 @@ extern Boolean Bumped;
 
 extern FlagType no_flags;
 
+extern int netlist_frozen;
+
 #endif
diff --git a/src/hid.h b/src/hid.h
index 1e3fb86..3c23338 100644
--- a/src/hid.h
+++ b/src/hid.h
@@ -112,7 +112,7 @@ extern "C"
 
      PCBChanged();
      RouteStylesChanged()
-     NetlistChanged()
+     NetlistChanged()  (but core should call "void NetlistChanged(int);" in netlist.c)
      LayersChanged()
      LibraryChanged()
      Busy()
diff --git a/src/misc.h b/src/misc.h
index f814784..7f06804 100644
--- a/src/misc.h
+++ b/src/misc.h
@@ -119,4 +119,8 @@ char * GetInfoString (void);
    comparing two similar footprints.  */
 int ElementOrientation (ElementType *e);
 
+/* These are in netlist.c */
+
+void NetlistChanged (int force_unfreeze);
+
 #endif
diff --git a/src/netlist.c b/src/netlist.c
index 78c8c3d..80e94eb 100644
--- a/src/netlist.c
+++ b/src/netlist.c
@@ -86,6 +86,23 @@ RCSID ("$Id$");
 
 typedef void (*NFunc) (LibraryMenuType *, LibraryEntryType *);
 
+int netlist_frozen = 0;
+static int netlist_needs_update = 0;
+
+void
+NetlistChanged (int force_unfreeze)
+{
+  if (force_unfreeze)
+    netlist_frozen = 0;
+  if (netlist_frozen)
+    netlist_needs_update = 1;
+  else
+    {
+      netlist_needs_update = 0;
+      hid_action ("NetlistChanged");
+    }
+}
+
 LibraryMenuTypePtr
 netnode_to_netname (char *nodename)
 {
@@ -168,7 +185,7 @@ netlist_rats (LibraryMenuType * net, LibraryEntryType * pin)
 {
   net->Name[0] = ' ';
   net->flag = 1;
-  hid_action ("NetlistChanged");
+  NetlistChanged (0);
 }
 
 static void
@@ -176,7 +193,7 @@ netlist_norats (LibraryMenuType * net, LibraryEntryType * pin)
 {
   net->Name[0] = '*';
   net->flag = 0;
-  hid_action ("NetlistChanged");
+  NetlistChanged (0);
 }
 
 /* The primary purpose of this action is to remove the netlist
@@ -219,7 +236,7 @@ netlist_clear (LibraryMenuType * net, LibraryEntryType * pin)
 	  net->EntryN --;
 	}
     }
-  hid_action ("NetlistChanged");
+  NetlistChanged (0);
 }
 
 static void
@@ -234,7 +251,7 @@ netlist_style (LibraryMenuType *net, const char *style)
 /* The primary purpose of this action is to rebuild a netlist from a
    script, in conjunction with the clear action above.  */
 static int
-netlist_add (const char *netname, const char *pinname, const char *defer_update)
+netlist_add (const char *netname, const char *pinname)
 {
   int ni, pi;
   LibraryType *netlist = &PCB->NetlistLib;
@@ -263,14 +280,14 @@ netlist_add (const char *netname, const char *pinname, const char *defer_update)
       pin = CreateNewConnection (net, (char *)pinname);
     }
 
-  if (!defer_update)
-    hid_action ("NetlistChanged");
+  NetlistChanged (0);
   return 0;
 }
 
 static const char netlist_syntax[] =
   "Net(find|select|rats|norats|clear[,net[,pin]])\n"
-  "Net(add,net,pin[,defer])";
+  "Net(freeze|thaw|forcethaw)\n"
+  "Net(add,net,pin)";
 
 static const char netlist_help[] = "Perform various actions on netlists.";
 
@@ -306,13 +323,22 @@ Nets which apply are marked as not available for the rats nest.
 Clears the netlist.
 
 @item add
-Add the given pin to the given netlist, creating either if needed.  If
-defer is specified, the GUI is not informed of this change - after a
-list of such changes, call NetlistChanged() to update the GUI.
+Add the given pin to the given netlist, creating either if needed.
 
 @item sort
 Called after a list of add's, this sorts the netlist.
 
+@item freeze
+@itemx thaw
+@itemx forcethaw
+Temporarily prevents changes to the netlist from being reflected in
+the GUI.  For example, if you need to make multiple changes, you
+freeze the netlist, make the changes, then thaw it.  Note that
+freeze/thaw requests may nest, with the netlist being fully thawed
+only when all pending freezes are thawed.  You can bypass the nesting
+by using forcethaw, which resets the freeze count and immediately
+updates the GUI.
+
 @end table
 
 %end-doc */
@@ -360,7 +386,6 @@ Netlist (int argc, char **argv, int x, int y)
       if (argc == 1)
 	{
 	  netlist_clear (NULL, NULL);
-	  hid_action ("NetlistChanged");
 	  return 0;
 	}
     }
@@ -369,12 +394,34 @@ Netlist (int argc, char **argv, int x, int y)
   else if (strcasecmp (argv[0], "add") == 0)
     {
       /* Add is different, because the net/pin won't already exist.  */
-      return netlist_add (ARG(1), ARG(2), ARG(3));
+      return netlist_add (ARG(1), ARG(2));
     }
   else if (strcasecmp (argv[0], "sort") == 0)
     {
       return sort_netlist ();
     }
+  else if (strcasecmp (argv[0], "freeze") == 0)
+    {
+      netlist_frozen ++;
+      return;
+    }
+  else if (strcasecmp (argv[0], "thaw") == 0)
+    {
+      if (netlist_frozen > 0)
+	{
+	  netlist_frozen --;
+	  if (netlist_needs_update)
+	    NetlistChanged (0);
+	}
+      return;
+    }
+  else if (strcasecmp (argv[0], "forcethaw") == 0)
+    {
+      netlist_frozen = 0;
+      if (netlist_needs_update)
+	NetlistChanged (0);
+      return;
+    }
   else
     {
       Message (netlist_syntax);
diff --git a/src/rats.c b/src/rats.c
index 35047b5..c174cc9 100644
--- a/src/rats.c
+++ b/src/rats.c
@@ -947,7 +947,7 @@ AddNet (void)
   menu->flag = 1;
 
 ratIt:
-  hid_action ("NetlistChanged");
+  NetlistChanged (0);
   return (CreateNewRat (PCB->Data, Crosshair.AttachedLine.Point1.X,
 			Crosshair.AttachedLine.Point1.Y,
 			Crosshair.AttachedLine.Point2.X,
diff --git a/src/undo.c b/src/undo.c
index 998fc2c..cb8d180 100644
--- a/src/undo.c
+++ b/src/undo.c
@@ -873,7 +873,7 @@ UndoNetlistChange (UndoListTypePtr Entry)
 
   *lib = *saved;
 
-  hid_action ("NetlistChanged");
+  NetlistChanged (0);
   return True;
 }
 
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 77a2d1a..002fecc 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -26,4 +26,4 @@ TOOLS= \
 	PCB2HPGL 
 
 DIST_SCM= \
-	gnet-pcblf.scm
+	gnet-pcbfwd.scm
diff --git a/tools/gnet-pcbfwd.scm b/tools/gnet-pcbfwd.scm
new file mode 100644
index 0000000..a15cd8a
--- /dev/null
+++ b/tools/gnet-pcbfwd.scm
@@ -0,0 +1,115 @@
+;;; gEDA - GPL Electronic Design Automation
+;;; gnetlist - gEDA Netlist
+;;; Copyright (C) 1998-2008 Ales Hvezda
+;;; Copyright (C) 1998-2008 gEDA Contributors (see ChangeLog for details)
+;;;
+;;; 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+;; PCB forward annotation script
+
+(use-modules (ice-9 format))
+
+;; This is a list of attributes which are propogated to the pcb
+;; elements.  Note that refdes, value, and footprint need not be
+;; listed here.
+(define pcblf:element-attrs
+  '("device"
+    "manufacturer"
+    "manufacturer_part_number"
+    "vendor"
+    "vendor_part_number"
+    ))
+
+(define (pcblf:pinfmt pin)
+  (format #f "~a-~a" (car pin) (car (cdr pin)))
+  )
+
+(define (pcblf:each-pin net pins port)
+  (if (not (null? pins))
+      (let ((pin (car pins)))
+	(format port "Netlist(Add,~a,~a)~%" net (pcblf:pinfmt pin))
+	(pcblf:each-pin net (cdr pins) port))))
+
+(define (pcblf:each-net netnames port)
+  (if (not (null? netnames))
+      (let ((netname (car netnames)))
+	(pcblf:each-pin netname (gnetlist:get-all-connections netname) port)
+	(pcblf:each-net (cdr netnames) port))))
+
+(define (pcblf:each-attr refdes attrs port)
+  (if (not (null? attrs))
+      (let ((attr (car attrs)))
+	(format port "ElementSetAttr(~a,~a,~a)~%"
+		refdes
+		attr
+		(gnetlist:get-package-attribute refdes attr))
+	(pcblf:each-attr refdes (cdr attrs) port))))
+
+;; write out the pins for a particular component
+(define pcblf:component_pins
+  (lambda (port package pins)
+    (if (and (not (null? package)) (not (null? pins)))
+	(begin
+	  (let (
+		(pin (car pins))
+		(label #f)
+		(pinnum #f)
+		)
+	    (display "ChangePinName(" port)
+	    (display package port)
+	    (display ", " port)
+
+	    (set! pinnum (gnetlist:get-attribute-by-pinnumber package pin "pinnumber"))
+
+	    (display pinnum port)
+	    (display ", " port)
+
+	    (set! label (gnetlist:get-attribute-by-pinnumber package pin "pinlabel"))
+	    (if (string=? label "unknown") 
+		(set! label pinnum)
+		)
+	    (display label port)
+	    (display ")\n" port)
+	    )
+	  (pcblf:component_pins port package (cdr pins))
+	  )
+	)
+    )
+  )
+
+(define (pcblf:each-element elements port)
+  (if (not (null? elements))
+      (let* ((refdes (car elements))
+	     (value (gnetlist:get-package-attribute refdes "value"))
+	     (footprint (gnetlist:get-package-attribute refdes "footprint"))
+	     )
+
+	(format port "ElementList(Need,~a,~a,~a)~%" refdes footprint value)
+	(pcblf:each-attr refdes pcblf:element-attrs port)
+	(pcblf:component_pins port refdes (gnetlist:get-pins refdes))
+
+	(pcblf:each-element (cdr elements) port))))
+
+(define (pcblf output-filename)
+  (let ((port (open-output-file output-filename)))
+    (format port "Netlist(Freeze)\n")
+    (format port "Netlist(Clear)\n")
+    (pcblf:each-net (gnetlist:get-all-unique-nets "dummy") port)
+    (format port "Netlist(Sort)\n")
+    (format port "Netlist(Thaw)\n")
+    (format port "ElementList(Start)\n")
+    (pcblf:each-element packages port)
+    (format port "ElementList(Done)\n")
+    (close-output-port port)))
diff --git a/tools/gnet-pcblf.scm b/tools/gnet-pcblf.scm
deleted file mode 100644
index 637dca8..0000000
--- a/tools/gnet-pcblf.scm
+++ /dev/null
@@ -1,114 +0,0 @@
-;;; gEDA - GPL Electronic Design Automation
-;;; gnetlist - gEDA Netlist
-;;; Copyright (C) 1998-2008 Ales Hvezda
-;;; Copyright (C) 1998-2008 gEDA Contributors (see ChangeLog for details)
-;;;
-;;; 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., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-;; PCB forward annotation script
-
-(use-modules (ice-9 format))
-
-;; This is a list of attributes which are propogated to the pcb
-;; elements.  Note that refdes, value, and footprint need not be
-;; listed here.
-(define pcblf:element-attrs
-  '("device"
-    "manufacturer"
-    "manufacturer_part_number"
-    "vendor"
-    "vendor_part_number"
-    ))
-
-(define (pcblf:pinfmt pin)
-  (format #f "~a-~a" (car pin) (car (cdr pin)))
-  )
-
-(define (pcblf:each-pin net pins port)
-  (if (not (null? pins))
-      (let ((pin (car pins)))
-	(format port "Netlist(Add,~a,~a,1)~%" net (pcblf:pinfmt pin))
-	(pcblf:each-pin net (cdr pins) port))))
-
-(define (pcblf:each-net netnames port)
-  (if (not (null? netnames))
-      (let ((netname (car netnames)))
-	(pcblf:each-pin netname (gnetlist:get-all-connections netname) port)
-	(pcblf:each-net (cdr netnames) port))))
-
-(define (pcblf:each-attr refdes attrs port)
-  (if (not (null? attrs))
-      (let ((attr (car attrs)))
-	(format port "ElementSetAttr(~a,~a,~a)~%"
-		refdes
-		attr
-		(gnetlist:get-package-attribute refdes attr))
-	(pcblf:each-attr refdes (cdr attrs) port))))
-
-;; write out the pins for a particular component
-(define pcblf:component_pins
-  (lambda (port package pins)
-    (if (and (not (null? package)) (not (null? pins)))
-	(begin
-	  (let (
-		(pin (car pins))
-		(label #f)
-		(pinnum #f)
-		)
-	    (display "ChangePinName(" port)
-	    (display package port)
-	    (display ", " port)
-
-	    (set! pinnum (gnetlist:get-attribute-by-pinnumber package pin "pinnumber"))
-
-	    (display pinnum port)
-	    (display ", " port)
-
-	    (set! label (gnetlist:get-attribute-by-pinnumber package pin "pinlabel"))
-	    (if (string=? label "unknown") 
-		(set! label pinnum)
-		)
-	    (display label port)
-	    (display ")\n" port)
-	    )
-	  (pcblf:component_pins port package (cdr pins))
-	  )
-	)
-    )
-  )
-
-(define (pcblf:each-element elements port)
-  (if (not (null? elements))
-      (let* ((refdes (car elements))
-	     (value (gnetlist:get-package-attribute refdes "value"))
-	     (footprint (gnetlist:get-package-attribute refdes "footprint"))
-	     )
-
-	(format port "ElementAddIf(~a,~a,~a)~%" refdes footprint value)
-	(pcblf:each-attr refdes pcblf:element-attrs port)
-	(pcblf:component_pins port refdes (gnetlist:get-pins refdes))
-
-	(pcblf:each-element (cdr elements) port))))
-
-(define (pcblf output-filename)
-  (let ((port (open-output-file output-filename)))
-    (format port "Netlist(Clear)\n")
-    (pcblf:each-net (gnetlist:get-all-unique-nets "dummy") port)
-    (format port "Netlist(Sort)\n")
-    (format port "NetlistChanged()\n")
-    (format port "ElementAddStart()\n")
-    (pcblf:each-element packages port)
-    (format port "ElementAddDone()\n")
-    (close-output-port port)))

commit 041164fc72def5c498e5c2c4ca3615a9d318184d
Author: DJ Delorie <dj@xxxxxxxxxxx>
Commit: DJ Delorie <dj@xxxxxxxxxxx>

    Add import to gtk menu, change lesstif menu to match.

diff --git a/src/gpcb-menu.res b/src/gpcb-menu.res
index 2d21a19..11251b2 100644
--- a/src/gpcb-menu.res
+++ b/src/gpcb-menu.res
@@ -58,6 +58,7 @@ MainMenu =
    -
    {"Revert" Load(Revert,none) tip="Revert to the layout stored on disk"}
    -
+   {"Import Schematics" Import() }
    {"Load layout" Load(Layout) tip="Load a layout from a file"}
    {"Load element data to paste-buffer" PasteBuffer(Clear) Load(ElementTobuffer)}
    {"Load layout data to paste-buffer" PasteBuffer(Clear) Load(LayoutTobuffer)}
diff --git a/src/pcb-menu.res b/src/pcb-menu.res
index 6874538..b326a04 100644
--- a/src/pcb-menu.res
+++ b/src/pcb-menu.res
@@ -29,10 +29,10 @@ MainMenu =
 {
   {File
    {"About..." About()}
-   {"Import Schematics" Import() }
    {"Save layout" Save(Layout)}
    {"Save layout as..." Save(LayoutAs)}
 	{"Revert" Load(Revert,none)}
+   {"Import Schematics" Import() }
    {"Load layout" Load(Layout)}
    {"Load element data to paste-buffer" PasteBuffer(Clear) Load(ElementTobuffer)}
    {"Load layout data to paste-buffer" PasteBuffer(Clear) Load(LayoutTobuffer)}

commit 54637f522dae03e68661cce77594c9436680b63c
Author: DJ Delorie <dj@xxxxxxxxxxx>
Commit: DJ Delorie <dj@xxxxxxxxxxx>

    Add the new netlister.
    
    Adds the new script-based netlister and the install rules.  Note
    that this installs PCB's netlister in gEDA's data directory.
    Unusual, but it's the best way to keep pcb in sync with its own netlister.

diff --git a/tools/Makefile.am b/tools/Makefile.am
index d0438d1..77a2d1a 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -7,12 +7,15 @@
 toolsdir=	$(pkgdatadir)/tools
 tools_DATA=	${TOOLS}
 
+scmdatadir = $(datadir)/gEDA/scheme
+scmdata_DATA = $(DIST_SCM)
+
 EXTRA_DIST= ${TOOLS}
 
 bin_SCRIPTS= \
 	MergePCBPS \
 	Merge_dimPCBPS 
-	
+
 TOOLS= \
 	apctools.zip \
 	gerbertotk.c \
@@ -22,3 +25,5 @@ TOOLS= \
 	Merge_dimPCBPS \
 	PCB2HPGL 
 
+DIST_SCM= \
+	gnet-pcblf.scm
diff --git a/tools/gnet-pcblf.scm b/tools/gnet-pcblf.scm
new file mode 100644
index 0000000..637dca8
--- /dev/null
+++ b/tools/gnet-pcblf.scm
@@ -0,0 +1,114 @@
+;;; gEDA - GPL Electronic Design Automation
+;;; gnetlist - gEDA Netlist
+;;; Copyright (C) 1998-2008 Ales Hvezda
+;;; Copyright (C) 1998-2008 gEDA Contributors (see ChangeLog for details)
+;;;
+;;; 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+;; PCB forward annotation script
+
+(use-modules (ice-9 format))
+
+;; This is a list of attributes which are propogated to the pcb
+;; elements.  Note that refdes, value, and footprint need not be
+;; listed here.
+(define pcblf:element-attrs
+  '("device"
+    "manufacturer"
+    "manufacturer_part_number"
+    "vendor"
+    "vendor_part_number"
+    ))
+
+(define (pcblf:pinfmt pin)
+  (format #f "~a-~a" (car pin) (car (cdr pin)))
+  )
+
+(define (pcblf:each-pin net pins port)
+  (if (not (null? pins))
+      (let ((pin (car pins)))
+	(format port "Netlist(Add,~a,~a,1)~%" net (pcblf:pinfmt pin))
+	(pcblf:each-pin net (cdr pins) port))))
+
+(define (pcblf:each-net netnames port)
+  (if (not (null? netnames))
+      (let ((netname (car netnames)))
+	(pcblf:each-pin netname (gnetlist:get-all-connections netname) port)
+	(pcblf:each-net (cdr netnames) port))))
+
+(define (pcblf:each-attr refdes attrs port)
+  (if (not (null? attrs))
+      (let ((attr (car attrs)))
+	(format port "ElementSetAttr(~a,~a,~a)~%"
+		refdes
+		attr
+		(gnetlist:get-package-attribute refdes attr))
+	(pcblf:each-attr refdes (cdr attrs) port))))
+
+;; write out the pins for a particular component
+(define pcblf:component_pins
+  (lambda (port package pins)
+    (if (and (not (null? package)) (not (null? pins)))
+	(begin
+	  (let (
+		(pin (car pins))
+		(label #f)
+		(pinnum #f)
+		)
+	    (display "ChangePinName(" port)
+	    (display package port)
+	    (display ", " port)
+
+	    (set! pinnum (gnetlist:get-attribute-by-pinnumber package pin "pinnumber"))
+
+	    (display pinnum port)
+	    (display ", " port)
+
+	    (set! label (gnetlist:get-attribute-by-pinnumber package pin "pinlabel"))
+	    (if (string=? label "unknown") 
+		(set! label pinnum)
+		)
+	    (display label port)
+	    (display ")\n" port)
+	    )
+	  (pcblf:component_pins port package (cdr pins))
+	  )
+	)
+    )
+  )
+
+(define (pcblf:each-element elements port)
+  (if (not (null? elements))
+      (let* ((refdes (car elements))
+	     (value (gnetlist:get-package-attribute refdes "value"))
+	     (footprint (gnetlist:get-package-attribute refdes "footprint"))
+	     )
+
+	(format port "ElementAddIf(~a,~a,~a)~%" refdes footprint value)
+	(pcblf:each-attr refdes pcblf:element-attrs port)
+	(pcblf:component_pins port refdes (gnetlist:get-pins refdes))
+
+	(pcblf:each-element (cdr elements) port))))
+
+(define (pcblf output-filename)
+  (let ((port (open-output-file output-filename)))
+    (format port "Netlist(Clear)\n")
+    (pcblf:each-net (gnetlist:get-all-unique-nets "dummy") port)
+    (format port "Netlist(Sort)\n")
+    (format port "NetlistChanged()\n")
+    (format port "ElementAddStart()\n")
+    (pcblf:each-element packages port)
+    (format port "ElementAddDone()\n")
+    (close-output-port port)))

commit 78f9c04b25459be009357eeef278da58899aabbf
Author: DJ Delorie <dj@xxxxxxxxxxx>
Commit: DJ Delorie <dj@xxxxxxxxxxx>

    Add attribute editor GUI to gtk and lesstif.
    
    Attributes(Layout|Layer|Element)
    Attributes(Layer,layername)
    
    Let the user edit the attributes of the layout, current or given
    layer, or selected element.

diff --git a/src/action.c b/src/action.c
index 0ed25e8..4b660df 100644
--- a/src/action.c
+++ b/src/action.c
@@ -7316,12 +7316,130 @@ ActionImport (int argc, char **argv, int x, int y)
   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, int x, int 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_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;
+	      }
+	  }
+
+	buf = (char *) malloc (strlen (NAMEONPCB_NAME(e)) + strlen ("Element X Attributes"));
+	sprintf(buf, "Element %s Attributes", NAMEONPCB_NAME(e));
+	gui->edit_attributes(buf, &(e->Attributes));
+	free (buf);
+	break;
+      }
+
+    default:
+      AFAIL (attributes);
+    }
+
+  return 0;
+}
+
 /* --------------------------------------------------------------------------- */
 
 HID_Action action_action_list[] = {
   {"AddRats", 0, ActionAddRats,
    addrats_help, addrats_syntax}
   ,
+  {"Attributes", 0, ActionAttributes,
+   attributes_help, attributes_syntax}
+  ,
   {"Atomic", 0, ActionAtomic,
    atomic_help, atomic_syntax}
   ,
diff --git a/src/global.h b/src/global.h
index fa9f55e..1462e50 100644
--- a/src/global.h
+++ b/src/global.h
@@ -50,10 +50,12 @@
 #include <ctype.h>
 #include <sys/types.h>
 
+/* Forward declarations for structures the HIDs need.  */
 typedef struct BoxType BoxType, *BoxTypePtr;
 typedef struct polygon_st PolygonType, *PolygonTypePtr;
 typedef struct drc_violation_st DrcViolationType, *DrcViolationTypePtr;
 typedef struct rtree rtree_t;
+typedef struct AttributeListType AttributeListType, *AttributeListTypePtr;
 
 #include "hid.h"
 #include "polyarea.h"
@@ -192,11 +194,11 @@ typedef struct
   char *value;
 } AttributeType, *AttributeTypePtr;
 
-typedef struct
+struct AttributeListType
 {
   int Number, Max;
   AttributeType *List;
-} AttributeListType, *AttributeListTypePtr;
+};
 
 /* ---------------------------------------------------------------------------
  * the basic object types supported by PCB
diff --git a/src/gpcb-menu.res b/src/gpcb-menu.res
index b844f56..2d21a19 100644
--- a/src/gpcb-menu.res
+++ b/src/gpcb-menu.res
@@ -106,6 +106,11 @@ MainMenu =
      {"layout" ChangeName(Layout)}
      {"active layer" ChangeName(Layer)}
    }
+   {"Edit attributes of"
+    {"Layout" Attributes(Layout)}
+    {"CurrentLayer" Attributes(Layer)}
+    {"Element" Attributes(Element)}
+   }
    -
    {"Move to current layer" MoveToCurrentLayer(Object) a={"M" "<Key>m"}}
    {"Move selected to current layer" MoveToCurrentLayer(Selected) a={"Shift-M" "Shift<Key>m"}}
diff --git a/src/hid.h b/src/hid.h
index 7966346..1e3fb86 100644
--- a/src/hid.h
+++ b/src/hid.h
@@ -554,6 +554,8 @@ typedef enum
 
     HID_DRC_GUI *drc_gui;
 
+    void (*edit_attributes) (char *owner, AttributeListType *attrlist_);
+
   } HID;
 
 /* Call this as soon as possible from main().  No other HID calls are
diff --git a/src/hid/batch/batch.c b/src/hid/batch/batch.c
index 407bc3a..c10b280 100644
--- a/src/hid/batch/batch.c
+++ b/src/hid/batch/batch.c
@@ -515,7 +515,8 @@ HID batch_gui = {
   batch_show_item,
   batch_beep,
   batch_progress,
-  0 /* batch_drc_gui */
+  0 /* batch_drc_gui */ ,
+  0 /* batch_edit_attributes */
 };
 
 #include "dolists.h"
diff --git a/src/hid/bom/bom.c b/src/hid/bom/bom.c
index 08d4328..e593bdd 100644
--- a/src/hid/bom/bom.c
+++ b/src/hid/bom/bom.c
@@ -589,6 +589,7 @@ HID bom_hid = {
   0,				/* bom_beep */
   0,				/* bom_progress */
   0,				/* bom_drc_gui */
+  0,				/* bom_edit_attributes */
 };
 
 void
diff --git a/src/hid/common/hidnogui.c b/src/hid/common/hidnogui.c
index 4d6baa0..51c95cb 100644
--- a/src/hid/common/hidnogui.c
+++ b/src/hid/common/hidnogui.c
@@ -403,7 +403,8 @@ HID hid_nogui = {
   nogui_show_item,
   nogui_beep,
   nogui_progress,
-  0 /* nogui_drc_gui */
+  0 /* nogui_drc_gui */ ,
+  0 /* edit_attributes */
 };
 
 #define AD(x) if (!d->x) d->x = s->x
@@ -458,4 +459,5 @@ apply_default_hid (HID * d, HID * s)
   AD (beep);
   AD (progress);
   AD (drc_gui);
+  AD (edit_attributes);
 }
diff --git a/src/hid/gtk/gtkhid-main.c b/src/hid/gtk/gtkhid-main.c
index 64d8622..fd399ce 100644
--- a/src/hid/gtk/gtkhid-main.c
+++ b/src/hid/gtk/gtkhid-main.c
@@ -890,6 +890,223 @@ ghid_progress (int so_far, int total, const char *message)
 }
 
 /* ---------------------------------------------------------------------- */
+
+
+typedef struct {
+  GtkWidget *del;
+  GtkWidget *w_name;
+  GtkWidget *w_value;
+} AttrRow;
+
+static AttrRow *attr_row = 0;
+static int attr_num_rows = 0;
+static int attr_max_rows = 0;
+static AttributeListType *attributes_list;
+static GtkWidget *attributes_dialog, *attr_table;
+
+static void attributes_delete_callback (GtkWidget *w, void *v);
+
+#define GA_RESPONSE_REVERT	1
+#define GA_RESPONSE_NEW		2
+
+static void
+ghid_attr_set_table_size ()
+{
+  gtk_table_resize (GTK_TABLE (attr_table), attr_num_rows > 0 ? attr_num_rows : 1, 3);
+}
+
+static void
+ghid_attributes_need_rows (int new_max)
+{
+  if (attr_max_rows < new_max)
+    {
+      if (attr_row)
+	attr_row = (AttrRow *) realloc (attr_row, new_max * sizeof(AttrRow));
+      else
+	attr_row = (AttrRow *) malloc (new_max * sizeof(AttrRow));
+    }
+  while (attr_max_rows < new_max)
+    {
+      /* add [attr_max_rows] */
+      attr_row[attr_max_rows].del = gtk_button_new_with_label ("del");
+      gtk_table_attach (GTK_TABLE (attr_table), attr_row[attr_max_rows].del,
+			0, 1,
+			attr_max_rows, attr_max_rows+1,
+			GTK_FILL | GTK_EXPAND,
+			GTK_FILL,
+			0, 0);
+      g_signal_connect (G_OBJECT (attr_row[attr_max_rows].del), "clicked",
+			G_CALLBACK (attributes_delete_callback), (void *)attr_max_rows);
+
+      attr_row[attr_max_rows].w_name = gtk_entry_new ();
+      gtk_table_attach (GTK_TABLE (attr_table), attr_row[attr_max_rows].w_name,
+			1, 2,
+			attr_max_rows, attr_max_rows+1,
+			GTK_FILL | GTK_EXPAND,
+			GTK_FILL,
+			0, 0);
+
+      attr_row[attr_max_rows].w_value = gtk_entry_new ();
+      gtk_table_attach (GTK_TABLE (attr_table), attr_row[attr_max_rows].w_value,
+			2, 3,
+			attr_max_rows, attr_max_rows+1,
+			GTK_FILL | GTK_EXPAND,
+			GTK_FILL,
+			0, 0);
+
+      attr_max_rows ++;
+    }
+
+  /* Manage any previously unused rows we now need to show.  */
+  while (attr_num_rows < new_max)
+    {
+      /* manage attr_num_rows */
+      gtk_widget_show (attr_row[attr_num_rows].del);
+      gtk_widget_show (attr_row[attr_num_rows].w_name);
+      gtk_widget_show (attr_row[attr_num_rows].w_value);
+      attr_num_rows ++;
+    }
+}
+
+static void
+ghid_attributes_revert ()
+{
+  int i;
+
+  ghid_attributes_need_rows (attributes_list->Number);
+
+  /* Unmanage any previously used rows we don't need.  */
+  while (attr_num_rows > attributes_list->Number)
+    {
+      attr_num_rows --;
+      gtk_widget_hide (attr_row[attr_num_rows].del);
+      gtk_widget_hide (attr_row[attr_num_rows].w_name);
+      gtk_widget_hide (attr_row[attr_num_rows].w_value);
+    }
+
+  /* Fill in values */
+  for (i=0; i<attributes_list->Number; i++)
+    {
+      /* create row [i] */
+      gtk_entry_set_text (GTK_ENTRY (attr_row[i].w_name), attributes_list->List[i].name);
+      gtk_entry_set_text (GTK_ENTRY (attr_row[i].w_value), attributes_list->List[i].value);
+#if 0
+#endif
+    }
+  ghid_attr_set_table_size ();
+}
+
+static void
+attributes_delete_callback (GtkWidget *w, void *v)
+{
+  int i, n;
+  GtkWidget *wn, *wv;
+
+  n = (int) v;
+
+  for (i=n; i<attr_num_rows-1; i++)
+    {
+      gtk_entry_set_text (GTK_ENTRY (attr_row[i].w_name),
+			  gtk_entry_get_text (GTK_ENTRY (attr_row[i+1].w_name)));
+      gtk_entry_set_text (GTK_ENTRY (attr_row[i].w_value),
+			  gtk_entry_get_text (GTK_ENTRY (attr_row[i+1].w_value)));
+    }
+  attr_num_rows --;
+
+  gtk_widget_hide (attr_row[attr_num_rows].del);
+  gtk_widget_hide (attr_row[attr_num_rows].w_name);
+  gtk_widget_hide (attr_row[attr_num_rows].w_value);
+
+  ghid_attr_set_table_size ();
+}
+
+static void
+ghid_attributes (char *owner, AttributeListType *attrs)
+{
+  int response;
+
+  attributes_list = attrs;
+
+  attr_max_rows = 0;
+  attr_num_rows = 0;
+
+  attributes_dialog = gtk_dialog_new_with_buttons (owner,
+						   GTK_WINDOW (ghid_port.top_window),
+						   GTK_DIALOG_MODAL,
+						   GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+						   "Revert", GA_RESPONSE_REVERT,
+						   "New", GA_RESPONSE_NEW,
+						   GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
+
+  attr_table = gtk_table_new (attrs->Number, 3, 0);
+
+  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (attributes_dialog)->vbox), attr_table, FALSE, FALSE, 0);
+    
+  gtk_widget_show (attr_table);
+
+  ghid_attributes_revert ();
+
+  while (1)
+    {
+      response = gtk_dialog_run (GTK_DIALOG (attributes_dialog));
+
+      if (response == GTK_RESPONSE_CANCEL)
+	break;
+
+      if (response == GTK_RESPONSE_OK)
+	{
+	  int i;
+	  /* Copy the values back */
+	  for (i=0; i<attributes_list->Number; i++)
+	    {
+	      if (attributes_list->List[i].name)
+		free (attributes_list->List[i].name);
+	      if (attributes_list->List[i].value)
+		free (attributes_list->List[i].value);
+	    }
+	  if (attributes_list->Max < attr_num_rows)
+	    {
+	      int sz = attr_num_rows * sizeof (AttributeType);
+	      if (attributes_list->List == NULL)
+		attributes_list->List = (AttributeType *) malloc (sz);
+	      else
+		attributes_list->List = (AttributeType *) realloc (attributes_list->List, sz);
+	      attributes_list->Max = attr_num_rows;
+	    }
+	  for (i=0; i<attr_num_rows; i++)
+	    {
+	      attributes_list->List[i].name = strdup (gtk_entry_get_text (GTK_ENTRY (attr_row[i].w_name)));
+	      attributes_list->List[i].value = strdup (gtk_entry_get_text (GTK_ENTRY (attr_row[i].w_value)));
+	      attributes_list->Number = attr_num_rows;
+	    }
+
+	  break;
+	}
+
+      if (response == GA_RESPONSE_REVERT)
+	{
+	  /* Revert */
+	  ghid_attributes_revert ();
+	}
+
+      if (response == GA_RESPONSE_NEW)
+	{
+	  ghid_attributes_need_rows (attr_num_rows + 1); /* also bumps attr_num_rows */
+
+	  gtk_entry_set_text (attr_row[attr_num_rows-1].w_name, "");
+	  gtk_entry_set_text (attr_row[attr_num_rows-1].w_value, "");
+
+	  ghid_attr_set_table_size ();
+	}
+    }
+
+  gtk_widget_destroy (attributes_dialog);
+  free (attr_row);
+  attr_row = NULL;
+}
+
+/* ---------------------------------------------------------------------- */
+
 HID_DRC_GUI ghid_drc_gui = {
   1,				/* log_drc_overview */
   0,				/* log_drc_details */
@@ -959,7 +1176,8 @@ HID ghid_hid = {
   ghid_show_item,
   ghid_beep,
   ghid_progress,
-  &ghid_drc_gui
+  &ghid_drc_gui,
+  ghid_attributes
 };
 
 /* ------------------------------------------------------------ 
diff --git a/src/hid/lesstif/dialogs.c b/src/hid/lesstif/dialogs.c
index 61b8097..213c5f2 100644
--- a/src/hid/lesstif/dialogs.c
+++ b/src/hid/lesstif/dialogs.c
@@ -1664,6 +1664,352 @@ EditLayerGroups (int argc, char **argv, int x, int y)
 
 /* ------------------------------------------------------------ */
 
+typedef struct {
+  Widget del;
+  Widget w_name;
+  Widget w_value;
+} AttrRow;
+
+static AttrRow *attr_row = 0;
+static int attr_num_rows = 0;
+static int attr_max_rows = 0;
+static Widget attr_dialog = NULL, f_top;
+static AttributeListType *attributes_list;
+
+static void attributes_delete_callback (Widget w, void *v, void *cbs);
+
+static void
+fiddle_with_bb_layout ()
+{
+  int i;
+  int max_height = 0;
+  int max_del_width = 0;
+  int max_name_width = 0;
+  int max_value_width = 0;
+  short ncolumns = 20;
+  short vcolumns = 20;
+
+  for (i=0; i<attr_num_rows; i++)
+    {
+      String v;
+
+      n = 0;
+      stdarg (XmNvalue, &v);
+      XtGetValues (attr_row[i].w_name, args, n);
+      if (ncolumns < strlen (v))
+	ncolumns = strlen (v);
+
+      n = 0;
+      stdarg (XmNvalue, &v);
+      XtGetValues (attr_row[i].w_value, args, n);
+      if (vcolumns < strlen (v))
+	vcolumns = strlen (v);
+    }
+
+  for (i=0; i<attr_num_rows; i++)
+    {
+      n = 0;
+      stdarg (XmNcolumns, ncolumns);
+      XtSetValues (attr_row[i].w_name, args, n);
+
+      n = 0;
+      stdarg (XmNcolumns, vcolumns);
+      XtSetValues (attr_row[i].w_value, args, n);
+    }
+
+  for (i=0; i<attr_num_rows; i++)
+    {
+      Dimension w, h;
+      n = 0;
+      stdarg (XmNwidth, &w);
+      stdarg (XmNheight, &h);
+
+      XtGetValues (attr_row[i].del, args, n);
+      if (max_height < h)
+	max_height = h;
+      if (max_del_width < w)
+	max_del_width = w;
+
+      XtGetValues (attr_row[i].w_name, args, n);
+      if (max_height < h)
+	max_height = h;
+      if (max_name_width < w)
+	max_name_width = w;
+
+      XtGetValues (attr_row[i].w_value, args, n);
+      if (max_height < h)
+	max_height = h;
+      if (max_value_width < w)
+	max_value_width = w;
+    }
+
+  for (i=0; i<attr_num_rows; i++)
+    {
+      n = 0;
+      stdarg (XmNx, 0);
+      stdarg (XmNy, i * max_height);
+      stdarg (XmNwidth, max_del_width);
+      stdarg (XmNheight, max_height);
+      XtSetValues (attr_row[i].del, args, n);
+
+      n = 0;
+      stdarg (XmNx, max_del_width);
+      stdarg (XmNy, i * max_height);
+      stdarg (XmNwidth, max_name_width);
+      stdarg (XmNheight, max_height);
+      XtSetValues (attr_row[i].w_name, args, n);
+
+      n = 0;
+      stdarg (XmNx, max_del_width + max_name_width);
+      stdarg (XmNy, i * max_height);
+      stdarg (XmNwidth, max_value_width);
+      stdarg (XmNheight, max_height);
+      XtSetValues (attr_row[i].w_value, args, n);
+    }
+
+  n = 0;
+  stdarg (XmNwidth, max_del_width + max_name_width + max_value_width + 1);
+  stdarg (XmNheight, max_height * attr_num_rows + 1);
+  XtSetValues (f_top, args, n);
+}
+
+static void
+lesstif_attributes_need_rows (int new_max)
+{
+  if (attr_max_rows < new_max)
+    {
+      if (attr_row)
+	attr_row = (AttrRow *) realloc (attr_row, new_max * sizeof(AttrRow));
+      else
+	attr_row = (AttrRow *) malloc (new_max * sizeof(AttrRow));
+    }
+
+  while (attr_max_rows < new_max)
+    {
+      n = 0;
+      attr_row[attr_max_rows].del = XmCreatePushButton (f_top, "del", args, n);
+      XtManageChild (attr_row[attr_max_rows].del);
+      XtAddCallback (attr_row[attr_max_rows].del, XmNactivateCallback,
+		     (XtCallbackProc) attributes_delete_callback,
+		     (XtPointer) attr_max_rows);
+
+      n = 0;
+      stdarg (XmNresizeWidth, True);
+      attr_row[attr_max_rows].w_name = XmCreateTextField (f_top, "name", args, n);
+      XtManageChild (attr_row[attr_max_rows].w_name);
+      XtAddCallback (attr_row[attr_max_rows].w_name, XmNvalueChangedCallback,
+		     (XtCallbackProc) fiddle_with_bb_layout, NULL);
+
+      n = 0;
+      stdarg (XmNresizeWidth, True);
+      attr_row[attr_max_rows].w_value = XmCreateTextField (f_top, "value", args, n);
+      XtManageChild (attr_row[attr_max_rows].w_value);
+      XtAddCallback (attr_row[attr_max_rows].w_value, XmNvalueChangedCallback,
+		     (XtCallbackProc) fiddle_with_bb_layout, NULL);
+
+      attr_max_rows ++;
+    }
+
+  /* Manage any previously unused rows we now need to show.  */
+  while (attr_num_rows < new_max)
+    {
+      XtManageChild (attr_row[attr_num_rows].del);
+      XtManageChild (attr_row[attr_num_rows].w_name);
+      XtManageChild (attr_row[attr_num_rows].w_value);
+      attr_num_rows ++;
+    }
+}
+
+static void
+lesstif_attributes_revert ()
+{
+  int i;
+
+  lesstif_attributes_need_rows (attributes_list->Number);
+
+  /* Unmanage any previously used rows we don't need.  */
+  while (attr_num_rows > attributes_list->Number)
+    {
+      attr_num_rows --;
+      XtUnmanageChild (attr_row[attr_num_rows].del);
+      XtUnmanageChild (attr_row[attr_num_rows].w_name);
+      XtUnmanageChild (attr_row[attr_num_rows].w_value);
+    }
+
+  /* Fill in values */
+  for (i=0; i<attributes_list->Number; i++)
+    {
+      XmTextFieldSetString (attr_row[i].w_name, attributes_list->List[i].name);
+      XmTextFieldSetString (attr_row[i].w_value, attributes_list->List[i].value);
+    }
+
+  fiddle_with_bb_layout ();
+}
+
+static void
+attributes_new_callback (Widget w, void *v, void *cbs)
+{
+  lesstif_attributes_need_rows (attr_num_rows + 1); /* also bumps attr_num_rows */
+  XmTextFieldSetString (attr_row[attr_num_rows-1].w_name, "");
+  XmTextFieldSetString (attr_row[attr_num_rows-1].w_value, "");
+
+  fiddle_with_bb_layout ();
+}
+
+static void
+attributes_delete_callback (Widget w, void *v, void *cbs)
+{
+  int i, n;
+  Widget wn, wv;
+
+  n = (int) v;
+
+  wn = attr_row[n].w_name;
+  wv = attr_row[n].w_value;
+
+  for (i=n; i<attr_num_rows-1; i++)
+    {
+      attr_row[i].w_name = attr_row[i+1].w_name;
+      attr_row[i].w_value = attr_row[i+1].w_value;
+    }
+  attr_row[attr_num_rows-1].w_name = wn;
+  attr_row[attr_num_rows-1].w_value = wv;
+  attr_num_rows --;
+
+  XtUnmanageChild (wn);
+  XtUnmanageChild (wv);
+
+  fiddle_with_bb_layout ();
+}
+
+static void
+attributes_revert_callback (Widget w, void *v, void *cbs)
+{
+  lesstif_attributes_revert ();
+}
+
+void
+lesstif_attributes_dialog (char *owner, AttributeListType *attrs_list)
+{
+  Widget bform, sw, b_ok, b_cancel, b_revert, b_new;
+  Widget sep;
+
+  if (attr_dialog == NULL)
+    {
+      n = 0;
+      stdarg (XmNautoUnmanage, False);
+      stdarg (XmNtitle, owner);
+      stdarg (XmNwidth, 400);
+      stdarg (XmNheight, 300);
+      attr_dialog = XmCreateFormDialog (mainwind, "attributes", args, n);
+
+      n = 0;
+      stdarg (XmNrightAttachment, XmATTACH_FORM);
+      stdarg (XmNbottomAttachment, XmATTACH_FORM);
+      stdarg (XmNorientation, XmHORIZONTAL);
+      stdarg (XmNentryAlignment, XmALIGNMENT_CENTER);
+      stdarg (XmNpacking, XmPACK_COLUMN);
+      bform = XmCreateRowColumn (attr_dialog, "attributes", args, n);
+      XtManageChild (bform);
+
+      n = 0;
+      b_ok = XmCreatePushButton (bform, "OK", args, n);
+      XtManageChild (b_ok);
+      XtAddCallback (b_ok, XmNactivateCallback,
+		     (XtCallbackProc) dialog_callback,
+		     (XtPointer) 0);
+
+      n = 0;
+      b_new = XmCreatePushButton (bform, "New", args, n);
+      XtManageChild (b_new);
+      XtAddCallback (b_new, XmNactivateCallback,
+		     (XtCallbackProc) attributes_new_callback,
+		     NULL);
+
+      n = 0;
+      b_revert = XmCreatePushButton (bform, "Revert", args, n);
+      XtManageChild (b_revert);
+      XtAddCallback (b_revert, XmNactivateCallback,
+		     (XtCallbackProc) attributes_revert_callback,
+		     NULL);
+
+      n = 0;
+      b_cancel = XmCreatePushButton (bform, "Cancel", args, n);
+      XtManageChild (b_cancel);
+      XtAddCallback (b_cancel, XmNactivateCallback,
+		     (XtCallbackProc) dialog_callback,
+		     (XtPointer) 1);
+
+      n = 0;
+      stdarg (XmNleftAttachment, XmATTACH_FORM);
+      stdarg (XmNrightAttachment, XmATTACH_FORM);
+      stdarg (XmNbottomAttachment, XmATTACH_WIDGET);
+      stdarg (XmNbottomWidget, bform);
+      sep = XmCreateSeparator (attr_dialog, "attributes", args, n);
+      XtManageChild (sep);
+
+      n = 0;
+      stdarg (XmNtopAttachment, XmATTACH_FORM);
+      stdarg (XmNleftAttachment, XmATTACH_FORM);
+      stdarg (XmNrightAttachment, XmATTACH_FORM);
+      stdarg (XmNbottomAttachment, XmATTACH_WIDGET);
+      stdarg (XmNbottomWidget, sep);
+      stdarg (XmNscrollingPolicy, XmAUTOMATIC);
+      sw = XmCreateScrolledWindow (attr_dialog, "attributes", args, n);
+      XtManageChild (sw);
+
+      n = 0;
+      stdarg (XmNmarginHeight, 0);
+      stdarg (XmNmarginWidth, 0);
+      f_top = XmCreateBulletinBoard (sw, "f_top", args, n);
+      XtManageChild (f_top);
+    }
+  else
+    {
+      n = 0;
+      stdarg (XmNtitle, owner);
+      XtSetValues (XtParent (attr_dialog), args, n);
+    }
+
+  attributes_list = attrs_list;
+  lesstif_attributes_revert ();
+
+  fiddle_with_bb_layout ();
+
+  if (wait_for_dialog (attr_dialog) == 0)
+    {
+      int i;
+      /* Copy the values back */
+      for (i=0; i<attributes_list->Number; i++)
+	{
+	  if (attributes_list->List[i].name)
+	    free (attributes_list->List[i].name);
+	  if (attributes_list->List[i].value)
+	    free (attributes_list->List[i].value);
+	}
+      if (attributes_list->Max < attr_num_rows)
+	{
+	  int sz = attr_num_rows * sizeof (AttributeType);
+	  if (attributes_list->List == NULL)
+	    attributes_list->List = (AttributeType *) malloc (sz);
+	  else
+	    attributes_list->List = (AttributeType *) realloc (attributes_list->List, sz);
+	  attributes_list->Max = attr_num_rows;
+	}
+      for (i=0; i<attr_num_rows; i++)
+	{
+	  attributes_list->List[i].name = strdup (XmTextFieldGetString (attr_row[i].w_name));
+	  attributes_list->List[i].value = strdup (XmTextFieldGetString (attr_row[i].w_value));
+	  attributes_list->Number = attr_num_rows;
+	}
+    }
+
+  return;
+}
+
+
+/* ------------------------------------------------------------ */
+
 HID_Action lesstif_dialog_action_list[] = {
   {"Load", 0, Load,
    load_help, load_syntax},
diff --git a/src/hid/lesstif/lesstif.h b/src/hid/lesstif/lesstif.h
index fe31371..54e4ced 100644
--- a/src/hid/lesstif/lesstif.h
+++ b/src/hid/lesstif/lesstif.h
@@ -55,6 +55,7 @@ extern char *lesstif_fileselect (const char *, const char *,
 				 char *, char *,
 				 const char *, int);
 extern void lesstif_log (const char *fmt, ...);
+extern void lesstif_attributes_dialog (char *, AttributeListType *);
 
 #define need_idle_proc lesstif_need_idle_proc
 #define show_crosshair lesstif_show_crosshair
diff --git a/src/hid/lesstif/main.c b/src/hid/lesstif/main.c
index 8ece6ef..6dfc513 100644
--- a/src/hid/lesstif/main.c
+++ b/src/hid/lesstif/main.c
@@ -3821,7 +3821,8 @@ HID lesstif_gui = {
   lesstif_show_item,
   lesstif_beep,
   lesstif_progress,
-  0 /* lesstif_drc_gui */
+  0, /* lesstif_drc_gui */
+  lesstif_attributes_dialog
 };
 
 #include "dolists.h"
diff --git a/src/hid/lesstif/xincludes.h b/src/hid/lesstif/xincludes.h
index 44c25e9..7245a77 100644
--- a/src/hid/lesstif/xincludes.h
+++ b/src/hid/lesstif/xincludes.h
@@ -30,6 +30,7 @@
 #include <Xm/RowColumn.h>
 #include <Xm/Scale.h>
 #include <Xm/ScrollBar.h>
+#include <Xm/ScrolledW.h>
 #include <Xm/Separator.h>
 #include <Xm/Text.h>
 #include <Xm/TextF.h>
diff --git a/src/hid/lpr/lpr.c b/src/hid/lpr/lpr.c
index cf35d8b..e47c40f 100644
--- a/src/hid/lpr/lpr.c
+++ b/src/hid/lpr/lpr.c
@@ -165,7 +165,8 @@ HID lpr_hid = {
   0 /* lpr_show_item */ ,
   0 /* lpr_beep */ ,
   0 /* lpr_progress */ ,
-  0 /* lpr_drc_gui */
+  0 /* lpr_drc_gui */ ,
+  0 /* lpr_edit_attributes */
 };
 
 void
diff --git a/src/hid/nelma/nelma.c b/src/hid/nelma/nelma.c
index ee3a896..dc1cee5 100644
--- a/src/hid/nelma/nelma.c
+++ b/src/hid/nelma/nelma.c
@@ -1100,7 +1100,8 @@ HID             nelma_hid = {
 	0 /* nelma_show_item */ ,
 	0 /* nelma_beep */ ,
 	0 /* nelma_progress */ ,
-	0 /* nelma_drc_gui */
+	0 /* nelma_drc_gui */ ,
+	0 /* nelma_edit_attributes */
 };
 
 #include "dolists.h"
diff --git a/src/hid/png/png.c b/src/hid/png/png.c
index 2b4968b..b819533 100644
--- a/src/hid/png/png.c
+++ b/src/hid/png/png.c
@@ -1512,7 +1512,8 @@ HID png_hid = {
   0 /* png_show_item */ ,
   0 /* png_beep */ ,
   0 /* png_progress */ ,
-  0 /* png_drc_gui */
+  0 /* png_drc_gui */ ,
+  0 /* png_edit_attributes */
 };
 
 #include "dolists.h"
diff --git a/src/hid/ps/eps.c b/src/hid/ps/eps.c
index 2115b47..b0e7a7d 100644
--- a/src/hid/ps/eps.c
+++ b/src/hid/ps/eps.c
@@ -662,7 +662,8 @@ static HID eps_hid = {
   0 /* eps_show_item */ ,
   0 /* eps_beep */ ,
   0 /* eps_progress */ ,
-  0 /* eps_drc_gui */
+  0 /* eps_drc_gui */ ,
+  0 /* eps_edit_attributes */
 };
 
 void
diff --git a/src/hid/ps/ps.c b/src/hid/ps/ps.c
index e648fa1..3a92dc3 100644
--- a/src/hid/ps/ps.c
+++ b/src/hid/ps/ps.c
@@ -1230,7 +1230,8 @@ HID ps_hid = {
   0 /* ps_show_item */ ,
   0 /* ps_beep */ ,
   0 /* ps_progress */ ,
-  0 /* ps_drc_gui */
+  0 /* ps_drc_gui */ ,
+  0 /* ps_edit_attributes */
 };
 
 #include "dolists.h"
diff --git a/src/pcb-menu.res b/src/pcb-menu.res
index 80590ac..6874538 100644
--- a/src/pcb-menu.res
+++ b/src/pcb-menu.res
@@ -154,6 +154,10 @@ MainMenu =
    {" Change text on layout" ChangeName(Object) a={"N" "<Key>n"}}
    {" Edit name of layout" ChangeName(Layout)}
    {" Edit name of active layer" ChangeName(Layer)}
+   {"Edit Attributes..." foreground=grey50 sensitive=false}
+   {" Layout" Attributes(Layout)}
+   {" CurrentLayer" Attributes(Layer)}
+   {" Element" Attributes(Element)}
    -
    {"Board Sizes" AdjustSizes()}
    {"Route Styles"

commit 4099d55781fa04d5b18b1b4fb6dd22907bcb1002
Author: DJ Delorie <dj@xxxxxxxxxxx>
Commit: DJ Delorie <dj@xxxxxxxxxxx>

    Add Import() action
    
    Add an action to import schematics into the pcb.  It allows for a
    list of schematics to be specified in the action or in the pcb,
    as well as supporting using a Makfile instead of gnetlist.

diff --git a/src/action.c b/src/action.c
index 7b1456a..0ed25e8 100644
--- a/src/action.c
+++ b/src/action.c
@@ -6936,8 +6936,8 @@ ActionElementAddIf (int argc, char **argv, int x, int y)
       pe = & PASTEBUFFER->Data->Element[0];
       pr = ElementOrientation (pe);
 
-      mx = e->MarkX - pe->MarkX;
-      my = e->MarkY - pe->MarkY;
+      mx = e->MarkX;
+      my = e->MarkY;
 
       if (er != pr)
 	RotateElementLowLevel (PASTEBUFFER->Data, pe, pe->MarkX, pe->MarkY, (er-pr+4)%4);
@@ -7091,6 +7091,231 @@ ActionExecCommand (int argc, char **argv, int x, int y)
   return 0;
 }
 
+static const char import_syntax[] =
+  "Import()\n"
+  "Import([gnetlist|make[,source,source,...]])\n";
+
+static const char import_help[] = "Import schematics";
+
+/* %start-doc actions import
+
+Imports element and netlist data from the schematics (or some other
+source).  The first parameter, which is optional, is the mode.  If not
+specified, the @code{import::mode} attribute in the PCB is used.
+@code{gnetlist} means gnetlist is used to obtain the information from
+the schematics.  @code{make} invokes @code{make}, assuming the user
+has a @code{Makefile} in the current directory.  The @code{Makefile}
+will be invoked with the following variables set:
+
+@table @code
+
+@item PCB
+The name of the .pcb file
+
+@item SRCLIST
+A space-separated list of source files
+
+@item OUT
+The name of the file in which to put the command script.
+
+@end table
+
+The target requested will be @code{pcb_import}.
+
+If you specify the mode, you may also specify the source files
+(schematics).  If you do not specify any, the list of schematics is
+obtained by reading the @code{import::src@var{N}} attributes (like
+@code{import::src0}, @code{import::src1}, etc).
+
+%end-doc */
+
+static int
+pcb_spawnvp (char **argv)
+{
+  int pid;
+  pid = fork ();
+  if (pid < 0)
+    {
+      /* error */
+      Message("Cannot fork!");
+      return 1;
+    }
+  else if (pid == 0)
+    {
+      /* Child */
+      execvp (argv[0], argv);
+      exit(1);
+    }
+  else
+    {
+      int rv;
+      /* Parent */
+      wait (&rv);
+    }
+  return 0;
+}
+
+static int
+ActionImport (int argc, char **argv, int x, int y)
+{
+  char *mode;
+  char **sources = NULL;
+  int nsources = 0;
+
+  mode = ARG (0);
+  if (! mode)
+    mode = AttributeGet (PCB, "import::mode");
+  if (! mode)
+    mode = "gnetlist";
+
+  if (argc > 1)
+    {
+      sources = argv + 1;
+      nsources = argc - 1;
+    }
+
+  if (! sources)
+    {
+      char sname[40];
+      char *src;
+
+      nsources = -1;
+      do {
+	nsources ++;
+	sprintf(sname, "import::src%d", nsources);
+	src = AttributeGet (PCB, sname);
+      } while (src);
+
+      if (nsources > 0)
+	{
+	  sources = (char **) malloc ((nsources + 1) * sizeof (char *));
+	  nsources = -1;
+	  do {
+	    nsources ++;
+	    sprintf(sname, "import::src%d", nsources);
+	    src = AttributeGet (PCB, sname);
+	    sources[nsources] = src;
+	  } while (src);
+	}
+    }
+
+  if (! sources)
+    {
+      /* Replace .pcb with .sch and hope for the best.  */
+      char *pcbname = PCB->Filename;
+      char *schname;
+      char *dot, *slash, *bslash;
+
+      schname = (char *) malloc (strlen(pcbname) + 5);
+      strcpy (schname, pcbname);
+      dot = strchr (schname, '.');
+      slash = strchr (schname, '/');
+      bslash = strchr (schname, '\\');
+      if (dot && slash && dot < slash)
+	dot = NULL;
+      if (dot && bslash && dot < bslash)
+	dot = NULL;
+      if (dot)
+	*dot = 0;
+      strcat (schname, ".sch");
+
+      sources = (char **) malloc (2 * sizeof (char *));
+      sources[0] = schname;
+      sources[1] = NULL;
+      nsources = 1;
+    }
+
+  if (strcasecmp (mode, "gnetlist") == 0)
+    {
+      char *tmpfile = tmpnam (NULL);
+      char **cmd;
+      int i, pid;
+      cmd = (char **) malloc ((6 + nsources) * sizeof (char *));
+      cmd[0] = "gnetlist";
+      cmd[1] = "-g";
+      cmd[2] = "pcblf";
+      cmd[3] = "-o";
+      cmd[4] = tmpfile;
+      for (i=0; i<nsources; i++)
+	cmd[5+i] = sources[i];
+      cmd[5+nsources] = NULL;
+
+      if (pcb_spawnvp (cmd))
+	{
+	  unlink (tmpfile);
+	  return 1;
+	}
+
+      cmd[0] = tmpfile;
+      cmd[1] = NULL;
+      ActionExecuteFile (1, cmd, 0, 0);
+
+      free (cmd);
+      unlink (tmpfile);
+    }
+  else if (strcasecmp (mode, "make") == 0)
+    {
+      char *tmpfile = tmpnam (NULL);
+      char **cmd;
+      int i, pid;
+      char *srclist;
+      int srclen;
+
+      srclen = sizeof("SRCLIB=") + 2;
+      for (i=0; i<nsources; i++)
+	srclen += strlen (sources[i]) + 2;
+      srclist = (char *) malloc (srclen);
+      strcpy (srclist, "SRCLIST=");
+      for (i=0; i<nsources; i++)
+	{
+	  if (i)
+	    strcat (srclist, " ");
+	  strcat (srclist, sources[i]);
+	}
+
+      printf("Makefile!\n");
+      
+      cmd = (char **) malloc (7 * sizeof (char *));
+      cmd[0] = "make";
+      cmd[1] = "-s";
+      cmd[2] = Concat ("PCB=", PCB->Filename, NULL);
+      cmd[3] = srclist;
+      cmd[4] = Concat ("OUT=", tmpfile, NULL);
+      cmd[5] = "pcb_import";
+      cmd[6] = NULL;
+
+      if (pcb_spawnvp (cmd))
+	{
+	  unlink (tmpfile);
+	  free (cmd[2]);
+	  free (cmd[3]);
+	  free (cmd[4]);
+	  free (cmd);
+	  return 1;
+	}
+
+      cmd[0] = tmpfile;
+      cmd[1] = NULL;
+      ActionExecuteFile (1, cmd, 0, 0);
+
+      free (cmd[2]);
+      free (cmd[3]);
+      free (cmd[4]);
+      free (cmd);
+      unlink (tmpfile);
+    }
+  else
+    {
+      Message ("Unknown import mode: %s\n", mode);
+      return 1;
+    }
+
+  DeleteRats (False);
+  AddAllRats (False, NULL);
+
+  return 0;
+}
+
 /* --------------------------------------------------------------------------- */
 
 HID_Action action_action_list[] = {
@@ -7282,6 +7507,9 @@ HID_Action action_action_list[] = {
   {"ExecCommand", 0, ActionExecCommand,
    execcommand_help, execcommand_syntax}
   ,
+  {"Import", 0, ActionImport,
+   import_help, import_syntax}
+  ,
 };
 
 REGISTER_ACTIONS (action_action_list)
diff --git a/src/misc.c b/src/misc.c
index 8bb8cf9..1938dea 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -1882,6 +1882,25 @@ pcb_author (void)
 }
 
 
+char *
+AttributeGetFromList (AttributeListType *list, char *name)
+{
+  int i;
+  for (i=0; i<list->Number; i++)
+    if (strcmp (name, list->List[i].name) == 0)
+      return list->List[i].value;
+  return NULL;
+}
+
+int
+AttributePutToList (AttributeListType *list, char *name, char *value, int replace)
+{
+  /* Not implemented yet.  */
+  abort ();
+}
+
+
+
 const char *
 c_dtostr (double d)
 {
diff --git a/src/misc.h b/src/misc.h
index b528ebe..f814784 100644
--- a/src/misc.h
+++ b/src/misc.h
@@ -80,6 +80,18 @@ char *Concat (const char *, ...);	/* end with NULL */
 
 char *pcb_author ();
 
+/* Returns NULL if the name isn't found, else the value for that named
+   attribute.  */
+char *AttributeGetFromList (AttributeListType *list, char *name);
+/* Adds an attribute to the list.  If the attribute already exists,
+   whether it's replaced or a second copy added depends on
+   REPLACE.  */
+int AttributePutToList (AttributeListType *list, char *name, char *value, int replace);
+/* Simplistic version: Takes a pointer to an object, looks up attributes in it.  */
+#define AttributeGet(OBJ,name) AttributeGetFromList (&(OBJ->Attributes), name)
+/* Simplistic version: Takes a pointer to an object, sets attributes in it.  */
+#define AttributePut(OBJ,name,value) AttributePutToList (&(OBJ->Attributes), name, value, 1)
+
 /* For passing modified flags to other functions. */
 FlagType MakeFlags (unsigned int);
 FlagType OldFlags (unsigned int);
diff --git a/src/pcb-menu.res b/src/pcb-menu.res
index 5186183..80590ac 100644
--- a/src/pcb-menu.res
+++ b/src/pcb-menu.res
@@ -29,6 +29,7 @@ MainMenu =
 {
   {File
    {"About..." About()}
+   {"Import Schematics" Import() }
    {"Save layout" Save(Layout)}
    {"Save layout as..." Save(LayoutAs)}
 	{"Revert" Load(Revert,none)}

commit dcc6281f399787765d17e0ce9969e9abf9d0b35c
Author: DJ Delorie <dj@xxxxxxxxxxx>
Commit: DJ Delorie <dj@xxxxxxxxxxx>

    Undo an accidental change.

diff --git a/src/draw.c b/src/draw.c
index 73881f3..b843da6 100644
--- a/src/draw.c
+++ b/src/draw.c
@@ -1624,30 +1624,8 @@ DrawPolygonLowLevel (PolygonTypePtr Polygon)
       AddPart (Polygon);
       return;
     }
-  pl = Polygon->Clipped->contours;
-  n = pl->Count;
-  x = (int *) malloc (n * sizeof (int));
-  y = (int *) malloc (n * sizeof (int));
-  for (v = &pl->head; i < n; v = v->next)
-    {
-      x[i] = v->point[0];
-      y[i++] = v->point[1];
-    }
-  if (TEST_FLAG (THINDRAWFLAG, PCB)
-      || TEST_FLAG (THINDRAWPOLYFLAG, PCB))
-    {
-      gui->set_line_width (Output.fgGC, 0);
-      for (i = 0; i < n - 1; i++)
-	{
-	  gui->draw_line (Output.fgGC, x[i], y[i], x[i + 1], y[i + 1]);
-	  //  gui->fill_circle (Output.fgGC, x[i], y[i], 30);
-	}
-      gui->draw_line (Output.fgGC, x[n - 1], y[n - 1], x[0], y[0]);
-    }
-  else
-    gui->fill_polygon (Output.fgGC, n, x, y);
-  free (x);
-  free (y);
+
+  printf ("DrawPolygonLowLevel: Called without Gathering set!\n");
 }
 
 /* ---------------------------------------------------------------------------

commit 12c0d8055373b2a43eb5d2d324bfbb0c513d24e8
Author: DJ Delorie <dj@xxxxxxxxxxx>
Commit: DJ Delorie <dj@xxxxxxxxxxx>

    Work-in-progress for LF: netlist importing

diff --git a/src/action.c b/src/action.c
index 9cf163b..7b1456a 100644
--- a/src/action.c
+++ b/src/action.c
@@ -70,6 +70,7 @@
 #include "thermal.h"
 #include "undo.h"
 #include "rtree.h"
+#include "macro.h"
 
 #ifdef HAVE_LIBDMALLOC
 #include <dmalloc.h>
@@ -306,6 +307,9 @@ static struct
 }
 Note;
 
+static int defer_updates = 0;
+static int defer_needs_update = 0;
+
 static Cardinal polyIndex = 0;
 static Boolean IgnoreMotionEvents = False;
 static Boolean saved_mode = False;
@@ -4436,8 +4440,13 @@ ActionChangePinName (int argc, char **argv, int x, int y)
    */
   if (changed)
     {
-      IncrementUndoSerialNumber ();
-      gui->invalidate_all ();
+      if (defer_updates)
+	defer_needs_update = 1;
+      else
+	{
+	  IncrementUndoSerialNumber ();
+	  gui->invalidate_all ();
+	}
     }
 
   return 0;
@@ -6752,6 +6761,8 @@ ActionExecuteFile (int argc, char **argv, int x, int y)
       return 1;
     }
 
+  defer_updates = 1;
+  defer_needs_update = 0;
   while (fgets (line, sizeof (line), fp) != NULL)
     {
       n++;
@@ -6774,11 +6785,17 @@ ActionExecuteFile (int argc, char **argv, int x, int y)
 
       if (*sp && *sp != '#')
 	{
-	  Message ("%s : line %-3d : \"%s\"\n", fname, n, sp);
+	  /*Message ("%s : line %-3d : \"%s\"\n", fname, n, sp);*/
 	  hid_parse_actions (sp, 0);
 	}
     }
 
+  defer_updates = 0;
+  if (defer_needs_update)
+    {
+      IncrementUndoSerialNumber ();
+      gui->invalidate_all ();
+    }
   fclose (fp);
   return 0;
 }
@@ -6795,6 +6812,287 @@ ActionPSCalib (int argc, char **argv, int x, int y)
 
 /* --------------------------------------------------------------------------- */
 
+static ElementType *element_cache = NULL;
+
+static ElementType *
+find_element_by_refdes (char *refdes)
+{
+  if (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 elementaddstart_syntax[] = "ElementAddStart()";
+
+static const char elementaddstart_help[] = "Notes the start of an element set update.";
+
+/* %start-doc actions elementaddstart
+
+Used before conditionally adding elements, it clears the list of
+"remembered" elements, so that unlisted elements can be discovered
+later.
+
+%end-doc */
+
+static int
+ActionElementAddStart (int argc, char **argv, int x, int y)
+{
+  ELEMENT_LOOP (PCB->Data);
+  {
+    CLEAR_FLAG (FOUNDFLAG, element);
+  }
+  END_LOOP;
+  element_cache = NULL;
+  return 0;
+}
+
+static const char elementaddif_syntax[] = "ElementAddIf(<refdes>,<footprint>,<value>)";
+
+static const char elementaddif_help[] = "Adds the given element if it doesn't already exist.";
+
+/* %start-doc actions elementaddif
+
+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.
+
+%end-doc */
+
+static int
+ActionElementAddIf (int argc, char **argv, int x, int y)
+{
+  ElementType *e = NULL;
+  char *refdes, *value, *footprint, *old;
+  char *args[3];
+
+  if (argc != 3)
+    AFAIL (elementaddif);
+
+  refdes = ARG(0);
+  footprint = ARG(1);
+  value = ARG(2);
+
+  args[0] = footprint;
+  args[1] = refdes;
+  args[2] = value;
+
+  e = find_element_by_refdes (refdes);
+
+  if (!e)
+    {
+      /* Not on board, need to add it. */
+      if (LoadFootprint(argc, args, x, y))
+	return 1;
+      if (CopyPastebufferToLayout (0, 0))
+	SetChangedFlag (True);
+    }
+
+  else if (e && strcmp (DESCRIPTION_NAME(e), footprint) != 0)
+    {
+      int er, pr, i;
+      LocationType mx, my;
+      ElementType *pe;
+
+      /* Different footprint, we need to swap them out.  */
+      if (LoadFootprint(argc, args, x, y))
+	return 1;
+
+      er = ElementOrientation (e);
+      pe = & PASTEBUFFER->Data->Element[0];
+      pr = ElementOrientation (pe);
+
+      mx = e->MarkX - pe->MarkX;
+      my = e->MarkY - pe->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->Name[i].Y = e->Name[i].Y - my;
+	  pe->Name[i].Direction = e->Name[i].Direction;
+	  pe->Name[i].Scale = e->Name[i].Scale;
+	}
+
+      RemoveElement (e);
+
+      if (CopyPastebufferToLayout (mx, my))
+	SetChangedFlag (True);
+    }
+
+  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);
+
+  return 0;
+}
+
+static const char elementadddone_syntax[] = "ElementAddDone()";
+
+static const char elementadddone_help[] = "Notes the end of an element set update";
+
+/* %start-doc actions elementadddone
+
+Used after conditionally adding elements, it finds any unmentioned
+elements which were previously added (non-empty refdes) and deletes
+them.
+
+%end-doc */
+
+static int
+ActionElementAddDone (int argc, char **argv, int x, int y)
+{
+  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;
+  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, int x, int y)
+{
+  ElementType *e = NULL;
+  char *refdes, *name, *value;
+  AttributeType *attr;
+
+  if (argc < 2)
+    {
+      AFAIL (changepinname);
+    }
+
+  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", refdes);
+      return 1;
+    }
+
+  attr = lookup_attr (&e->Attributes, name);
+
+  if (attr && value)
+    {
+      MYFREE (attr->value);
+      attr->value = MyStrdup (value, "ElementSetAttr");
+    }
+  if (attr && ! value)
+    {
+      delete_attr (& e->Attributes, attr);
+    }
+  if (!attr && value)
+    {
+      CreateNewAttribute (& e->Attributes, name, value);
+    }
+
+  return 0;
+}
+
+static const char execcommand_syntax[] = "ExecCommand(command)";
+
+static const char execcommand_help[] = "Runs a command";
+
+/* %start-doc actions execcommand
+
+Runs the given command, which is a system executable.
+
+%end-doc */
+
+static int
+ActionExecCommand (int argc, char **argv, int x, int y)
+{
+  char *command;
+
+  if (argc < 1)
+    {
+      AFAIL (execcommand);
+    }
+
+  command = ARG(0);
+
+  if (system (command))
+    return 1;
+  return 0;
+}
+
+/* --------------------------------------------------------------------------- */
+
 HID_Action action_action_list[] = {
   {"AddRats", 0, ActionAddRats,
    addrats_help, addrats_syntax}
@@ -6969,6 +7267,22 @@ HID_Action action_action_list[] = {
   ,
   {"pscalib", 0, ActionPSCalib}
   ,
+  {"ElementAddStart", 0, ActionElementAddStart,
+   elementaddstart_help, elementaddstart_syntax}
+  ,
+  {"ElementAddIf", 0, ActionElementAddIf,
+   elementaddif_help, elementaddif_syntax}
+  ,
+  {"ElementAddDone", 0, ActionElementAddDone,
+   elementadddone_help, elementadddone_syntax}
+  ,
+  {"ElementSetAttr", 0, ActionElementSetAttr,
+   elementsetattr_help, elementsetattr_syntax}
+  ,
+  {"ExecCommand", 0, ActionExecCommand,
+   execcommand_help, execcommand_syntax}
+  ,
 };
 
 REGISTER_ACTIONS (action_action_list)
+
diff --git a/src/buffer.c b/src/buffer.c
index be51a0f..cdb34dd 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -83,6 +83,8 @@ static void *MovePolygonToBuffer (LayerTypePtr, PolygonTypePtr);
 static void *MoveElementToBuffer (ElementTypePtr);
 static void SwapBuffer (BufferTypePtr);
 
+#define ARG(n) (argc > (n) ? argv[n] : 0)
+
 /* ---------------------------------------------------------------------------
  * some local identifiers
  */
@@ -603,6 +605,287 @@ LoadElementToBuffer (BufferTypePtr Buffer, char *Name, Boolean FromFile)
 
 
 /*---------------------------------------------------------------------------
+ * Searches for the given element by "footprint" name, and loads it
+ * into the buffer.
+ */
+
+/* Figuring out which library entry is the one we want is a little
+   tricky.  For file-based footprints, it's just a matter of finding
+   the first match in the search list.  For m4-based footprints you
+   need to know what magic to pass to the m4 functions.  Fortunately,
+   the footprint needed is determined when we build the m4 libraries
+   and stored as a comment in the description, so we can search for
+   that to find the magic we need.  We use a hash to store the
+   corresponding footprints and pointers to the library tree so we can
+   quickly find the various bits we need to load a given
+   footprint.  */
+
+typedef struct {
+  char *footprint;
+  int footprint_allocated;
+  int menu_idx;
+  int entry_idx;
+} FootprintHashEntry;
+
+static FootprintHashEntry *footprint_hash = 0;
+int footprint_hash_size = 0;
+
+void
+clear_footprint_hash ()
+{
+  int i;
+  if (!footprint_hash)
+    return;
+  for (i=0; i<footprint_hash_size; i++)
+    if (footprint_hash[i].footprint_allocated)
+      free (footprint_hash[i].footprint);
+  free (footprint_hash);
+  footprint_hash = NULL;
+  footprint_hash_size = 0;
+}
+
+/* Used to sort footprint pointer entries.  Note we include the index
+   numbers so that same-named footprints are sorted by the library
+   search order.  */
+static int
+footprint_hash_cmp (const void *va, const void *vb)
+{
+  int i;
+  FootprintHashEntry *a = (FootprintHashEntry *)va;
+  FootprintHashEntry *b = (FootprintHashEntry *)vb;
+
+  i = strcmp (a->footprint, b->footprint);
+  if (i == 0)
+    i = a->menu_idx - b->menu_idx;
+  if (i == 0)
+    i = a->entry_idx - b->entry_idx;
+  return i;
+}
+
+void
+make_footprint_hash ()
+{
+  int i, j;
+  char *fp;
+  int num_entries = 0;
+
+  clear_footprint_hash ();
+
+  for (i=0; i<Library.MenuN; i++)
+    for (j=0; j<Library.Menu[i].EntryN; j++)
+      num_entries ++;
+  footprint_hash = (FootprintHashEntry *)malloc (num_entries * sizeof(FootprintHashEntry));
+  num_entries = 0;
+
+  /* There are two types of library entries.  The file-based types
+     have a Template of (char *)-1 and the AllocatedMemory is the full
+     path to the footprint file.  The m4 ones have the footprint name
+     in brackets in the description.  */
+  for (i=0; i<Library.MenuN; i++)
+    {
+      for (j=0; j<Library.Menu[i].EntryN; j++)
+	{
+	  footprint_hash[num_entries].menu_idx = i;
+	  footprint_hash[num_entries].entry_idx = j;
+	  if (Library.Menu[i].Entry[j].Template == (char *) -1)
+	    {
+	      fp = strrchr (Library.Menu[i].Entry[j].AllocatedMemory, '/');
+	      if (!fp)
+		fp = strrchr (Library.Menu[i].Entry[j].AllocatedMemory, '\\');
+	      if (fp)
+		fp ++;
+	      else
+		fp = Library.Menu[i].Entry[j].AllocatedMemory;
+	      footprint_hash[num_entries].footprint = fp;
+	      footprint_hash[num_entries].footprint_allocated = 0;
+	    }
+	  else
+	    {
+	      fp = strrchr (Library.Menu[i].Entry[j].Description, '[');
+	      if (fp)
+		{
+		  footprint_hash[num_entries].footprint = strdup (fp+1);
+		  footprint_hash[num_entries].footprint_allocated = 1;
+		  fp = strchr (footprint_hash[num_entries].footprint, ']');
+		  if (fp)
+		    *fp = 0;
+		}
+	      else
+		{
+		  fp = Library.Menu[i].Entry[j].Description;
+		  footprint_hash[num_entries].footprint = fp;
+		  footprint_hash[num_entries].footprint_allocated = 0;
+		}
+	    }
+	  num_entries ++;
+	}
+    }
+
+  footprint_hash_size = num_entries;
+  qsort (footprint_hash, num_entries, sizeof(footprint_hash[0]), footprint_hash_cmp);
+#if 0
+  for (i=0; i<num_entries; i++)
+    printf("[%s]\n", footprint_hash[i].footprint);
+#endif
+}
+
+FootprintHashEntry *
+search_footprint_hash (const char *footprint)
+{
+  int i, min, max, c;
+
+  /* Standard binary search */
+
+  min = -1;
+  max = footprint_hash_size;
+
+  while (max - min > 1)
+    {
+      i = (min+max)/2;
+      c = strcmp (footprint, footprint_hash[i].footprint);
+      if (c < 0)
+	max = i;
+      else if (c > 0)
+	min = i;
+      else
+	{
+	  /* We want to return the first match, not just any match.  */
+	  while (i > 0
+		 && strcmp (footprint, footprint_hash[i-1].footprint) == 0)
+	    i--;
+	  return & footprint_hash[i];
+	}
+    }
+  return NULL;
+}
+
+/* Returns zero on success, non-zero on error.  */
+int
+LoadFootprintByName (BufferTypePtr Buffer, char *Footprint)
+{
+  int i;
+  FootprintHashEntry *fpe;
+  LibraryMenuType *menu;
+  LibraryEntryType *entry;
+
+  if (!footprint_hash)
+    make_footprint_hash ();
+
+  fpe = search_footprint_hash (Footprint);
+  if (!fpe)
+    {
+      Message("Unable to load footprint %s\n", Footprint);
+      return 1;
+    }
+
+  menu = & Library.Menu[fpe->menu_idx];
+  entry = & menu->Entry[fpe->entry_idx];
+
+  if (entry->Template == (char *) -1)
+    {
+      i = LoadElementToBuffer (Buffer, entry->AllocatedMemory, True);
+      return i ? 0 : 1;
+    }
+  else
+    {
+      char *args;
+
+      args = Concat("'", EMPTY (entry->Template), "' '",
+		    EMPTY (entry->Value), "' '", EMPTY (entry->Package), "'", NULL);
+      i = LoadElementToBuffer (Buffer, args, False);
+
+      free (args);
+      return i ? 0 : 1;
+    }
+
+#if 1
+  {
+    int j;
+    printf("Library path: %s\n", Settings.LibraryPath);
+    printf("Library tree: %s\n", Settings.LibraryTree);
+
+    printf("Library:\n");
+    for (i=0; i<Library.MenuN; i++)
+      {
+	printf("  [%02d] Name: %s\n", i, Library.Menu[i].Name);
+	printf("       Dir:  %s\n", Library.Menu[i].directory);
+	printf("       Sty:  %s\n", Library.Menu[i].Style);
+	for (j=0; j<Library.Menu[i].EntryN; j++)
+	  {
+	    printf("       [%02d] E: %s\n", j, Library.Menu[i].Entry[j].ListEntry);
+	    if (Library.Menu[i].Entry[j].Template == (char *) -1)
+	      printf("            A: %s\n", Library.Menu[i].Entry[j].AllocatedMemory);
+	    else
+	      {
+		printf("            T: %s\n", Library.Menu[i].Entry[j].Template);
+		printf("            P: %s\n", Library.Menu[i].Entry[j].Package);
+		printf("            V: %s\n", Library.Menu[i].Entry[j].Value);
+		printf("            D: %s\n", Library.Menu[i].Entry[j].Description);
+	      }
+	    if (j == 10)
+	      break;
+	  }
+      }
+  }
+#endif
+}
+
+
+static const char loadfootprint_syntax[] = "LoadFootprint(filename[,refdes,value])";
+
+static const char loadfootprint_help[] = "Loads a single footprint by name";
+
+/* %start-doc actions LoadFootprint
+
+Loads a single footprint by name, rather than by reference or through
+the library.  If a refdes and value are specified, those are inserted
+into the footprint as well.  The footprint remains in the paste buffer.
+
+%end-doc */
+
+int
+LoadFootprint (int argc, char **argv, int x, int y)
+{
+  char *name = ARG(0);
+  char *refdes = ARG(1);
+  char *value = ARG(2);
+  ElementTypePtr e;
+
+  if (!name)
+    AFAIL (loadfootprint);
+
+  if (LoadFootprintByName (PASTEBUFFER, name))
+    return 1;
+
+  if (PASTEBUFFER->Data->ElementN == 0)
+    {
+      Message("Footprint %s contains no elements", name);
+      return 1;
+    }
+  if (PASTEBUFFER->Data->ElementN > 1)
+    {
+      Message("Footprint %s contains multiple elements", name);
+      return 1;
+    }
+
+  e = & PASTEBUFFER->Data->Element[0];
+
+  if (e->Name[0].TextString)
+    free (e->Name[0].TextString);
+  e->Name[0].TextString = strdup (name);
+
+  if (e->Name[1].TextString)
+    free (e->Name[1].TextString);
+  e->Name[1].TextString = refdes ? strdup (refdes) : 0;
+
+  if (e->Name[2].TextString)
+    free (e->Name[2].TextString);
+  e->Name[2].TextString = value ? strdup (value) : 0;
+
+  return 0;
+}
+
+/*---------------------------------------------------------------------------
  *
  * break buffer element into pieces
  */
@@ -1369,7 +1652,9 @@ CopyObjectToBuffer (DataTypePtr Destination, DataTypePtr Src,
 
 HID_Action rotate_action_list[] = {
   {"FreeRotateBuffer", 0, ActionFreeRotateBuffer,
-   freerotatebuffer_syntax, freerotatebuffer_help}
+   freerotatebuffer_syntax, freerotatebuffer_help},
+  {"LoadFootprint", 0, LoadFootprint,
+   0,0}
 };
 
 REGISTER_ACTIONS (rotate_action_list)
diff --git a/src/buffer.h b/src/buffer.h
index 74c7399..07c21c1 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -52,4 +52,7 @@ void *MoveObjectToBuffer (DataTypePtr, DataTypePtr, int, void *, void *, void *)
 void *CopyObjectToBuffer (DataTypePtr, DataTypePtr, int,
 			  void *, void *, void *);
 
+/* This action is called from ActionElementAddIf() */
+int LoadFootprint (int argc, char **argv, int x, int y);
+
 #endif
diff --git a/src/change.c b/src/change.c
index 0599ee4..f67e682 100644
--- a/src/change.c
+++ b/src/change.c
@@ -982,6 +982,30 @@ ChangeLineName (LayerTypePtr Layer, LineTypePtr Line)
 /* ---------------------------------------------------------------------------
  * changes the layout-name of an element
  */
+
+char *
+ChangeElementText (PCBType *pcb, DataType *data, ElementTypePtr Element, int which, char *new_name)
+{
+  char *old = Element->Name[which].TextString;
+
+  if (pcb && which == NAME_INDEX (pcb))
+    EraseElementName (Element);
+
+  r_delete_entry (data->name_tree[which],
+		  (BoxType *) & Element->Name[which].TextString);
+
+  Element->Name[which].TextString = new_name;
+  SetTextBoundingBox (&PCB->Font, &Element->Name[which]);
+
+  r_insert_entry (data->name_tree[which],
+		  (BoxType *) & Element->Name[which].TextString, 0);
+
+  if (pcb && which == NAME_INDEX (pcb))
+    DrawElementName (Element, 0);
+
+  return old;
+}
+
 static void *
 ChangeElementName (ElementTypePtr Element)
 {
@@ -998,15 +1022,8 @@ ChangeElementName (ElementTypePtr Element)
 	  return ((char *) -1);
 	}
     }
-  EraseElementName (Element);
-  r_delete_entry (PCB->Data->name_tree[NAME_INDEX (PCB)],
-		  (BoxType *) & ELEMENT_TEXT (PCB, Element));
-  ELEMENT_NAME (PCB, Element) = NewName;
-  SetTextBoundingBox (&PCB->Font, &ELEMENT_TEXT (PCB, Element));
-  r_insert_entry (PCB->Data->name_tree[NAME_INDEX (PCB)],
-		  (BoxType *) & ELEMENT_TEXT (PCB, Element), 0);
-  DrawElementName (Element, 0);
-  return (old);
+
+  return ChangeElementText (PCB, PCB->Data, Element, NAME_INDEX (PCB), NewName);
 }
 
 /* ---------------------------------------------------------------------------
diff --git a/src/change.h b/src/change.h
index 7f43fba..bd7749a 100644
--- a/src/change.h
+++ b/src/change.h
@@ -107,4 +107,10 @@ void *ChangeObjectName (int, void *, void *, void *, char *);
 void *QueryInputAndChangeObjectName (int, void *, void *, void *);
 void ChangePCBSize (BDimension, BDimension);
 
+/* Change the specified text on an element, either on the board (give
+   PCB, PCB->Data) or in a buffer (give NULL, Buffer->Data).  The old
+   string is returned, and must be properly freed by the caller.  */
+char *ChangeElementText (PCBType *pcb, DataType *data, ElementTypePtr Element,
+			 int which, char *new_name);
+
 #endif
diff --git a/src/create.c b/src/create.c
index 500d0a0..518a35b 100644
--- a/src/create.c
+++ b/src/create.c
@@ -955,7 +955,7 @@ CreateNewNet (LibraryTypePtr lib, char *name, char *style)
   menu = GetLibraryMenuMemory (lib);
   menu->Name = MyStrdup (temp, "CreateNewNet()");
   menu->flag = 1;		/* net is enabled by default */
-  if (NSTRCMP ("(unknown)", style) == 0)
+  if (style == NULL || NSTRCMP ("(unknown)", style) == 0)
     menu->Style = NULL;
   else
     menu->Style = MyStrdup (style, "CreateNewNet()");
diff --git a/src/draw.c b/src/draw.c
index b843da6..73881f3 100644
--- a/src/draw.c
+++ b/src/draw.c
@@ -1624,8 +1624,30 @@ DrawPolygonLowLevel (PolygonTypePtr Polygon)
       AddPart (Polygon);
       return;
     }
-
-  printf ("DrawPolygonLowLevel: Called without Gathering set!\n");
+  pl = Polygon->Clipped->contours;
+  n = pl->Count;
+  x = (int *) malloc (n * sizeof (int));
+  y = (int *) malloc (n * sizeof (int));
+  for (v = &pl->head; i < n; v = v->next)
+    {
+      x[i] = v->point[0];
+      y[i++] = v->point[1];
+    }
+  if (TEST_FLAG (THINDRAWFLAG, PCB)
+      || TEST_FLAG (THINDRAWPOLYFLAG, PCB))
+    {
+      gui->set_line_width (Output.fgGC, 0);
+      for (i = 0; i < n - 1; i++)
+	{
+	  gui->draw_line (Output.fgGC, x[i], y[i], x[i + 1], y[i + 1]);
+	  //  gui->fill_circle (Output.fgGC, x[i], y[i], 30);
+	}
+      gui->draw_line (Output.fgGC, x[n - 1], y[n - 1], x[0], y[0]);
+    }
+  else
+    gui->fill_polygon (Output.fgGC, n, x, y);
+  free (x);
+  free (y);
 }
 
 /* ---------------------------------------------------------------------------
diff --git a/src/file.c b/src/file.c
index 4cef71f..fd18340 100644
--- a/src/file.c
+++ b/src/file.c
@@ -197,7 +197,7 @@ sort_library (LibraryTypePtr lib)
 	   lib->Menu[i].EntryN, sizeof (lib->Menu[i].Entry[0]), netnode_sort);
 }
 
-static void
+void
 sort_netlist ()
 {
   netlist_sort_offset = 2;
diff --git a/src/hid/lesstif/main.c b/src/hid/lesstif/main.c
index 6ff7e74..8ece6ef 100644
--- a/src/hid/lesstif/main.c
+++ b/src/hid/lesstif/main.c
@@ -3116,6 +3116,8 @@ lesstif_set_line_cap (hidGC gc, EndCapStyle style)
 static void
 lesstif_set_line_width (hidGC gc, int width)
 {
+  if (width == 1)
+    width = 0;
   gc->width = width;
 }
 
diff --git a/src/misc.c b/src/misc.c
index 2ba104c..8bb8cf9 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -2080,3 +2080,92 @@ GetInfoString (void)
 
   return info.Data;
 }
+
+/* ---------------------------------------------------------------------------
+ * Returns a best guess about the orientation of an element.  The
+ * value corresponds to the rotation; a difference is the right value
+ * to pass to RotateElementLowLevel.  However, the actual value is no
+ * indication of absolute rotation; only relative rotation is
+ * meaningful.
+ */
+
+int 
+ElementOrientation (ElementType *e)
+{
+  int pin1x, pin1y, pin2x, pin2y, dx, dy;
+  int found_pin1 = 0;
+  int found_pin2 = 0;
+
+  PIN_LOOP (e);
+  {
+    if (NSTRCMP (pin->Number, "1") == 0)
+      {
+	pin1x = pin->X;
+	pin1y = pin->Y;
+	found_pin1 = 1;
+      }
+    else if (NSTRCMP (pin->Number, "2") == 0)
+      {
+	pin2x = pin->X;
+	pin2y = pin->Y;
+	found_pin2 = 1;
+      }
+  }
+  END_LOOP;
+
+  PAD_LOOP (e);
+  {
+    if (NSTRCMP (pad->Number, "1") == 0)
+      {
+	pin1x = (pad->Point1.X + pad->Point2.X) / 2;
+	pin1y = (pad->Point1.Y + pad->Point2.Y) / 2;
+	found_pin1 = 1;
+      }
+    else if (NSTRCMP (pad->Number, "2") == 0)
+      {
+	pin2x = (pad->Point1.X + pad->Point2.X) / 2;
+	pin2y = (pad->Point1.Y + pad->Point2.Y) / 2;
+	found_pin2 = 1;
+      }
+  }
+  END_LOOP;
+
+  if (found_pin1 && found_pin2)
+    {
+      dx = pin2x - pin1x;
+      dy = pin2y - pin1y;
+    }
+  else if (found_pin1 && (pin1x || pin1y))
+    {
+      dx = pin1x;
+      dy = pin1y;
+    }
+  else if (found_pin2 && (pin2x || pin2y))
+    {
+      dx = pin2x;
+      dy = pin2y;
+    }
+  else
+    return 0;
+
+  if (abs(dx) > abs(dy))
+    return dx > 0 ? 0 : 2;
+  return dy > 0 ? 3 : 1;
+}
+
+int
+ActionListRotations(int argc, char **argv, int x, int y)
+{
+  ELEMENT_LOOP (PCB->Data);
+  {
+    printf("%d %s\n", ElementOrientation(element), NAMEONPCB_NAME(element));
+  }
+  END_LOOP;
+}
+
+HID_Action misc_action_list[] = {
+  {"ListRotations", 0, ActionListRotations,
+   0,0},
+};
+
+REGISTER_ACTIONS (misc_action_list)
diff --git a/src/misc.h b/src/misc.h
index 58e75dc..b528ebe 100644
--- a/src/misc.h
+++ b/src/misc.h
@@ -103,4 +103,8 @@ extern const char *c_dtostr(double d);
 /* Returns a string with info about this copy of pcb. */
 char * GetInfoString (void);
 
+/* Return a relative rotation for an element, useful only for
+   comparing two similar footprints.  */
+int ElementOrientation (ElementType *e);
+
 #endif
diff --git a/src/netlist.c b/src/netlist.c
index d9592b0..78c8c3d 100644
--- a/src/netlist.c
+++ b/src/netlist.c
@@ -57,6 +57,7 @@
 #include "rats.h"
 #include "set.h"
 #include "vendor.h"
+#include "create.h"
 
 #ifdef HAVE_REGCOMP
 #undef HAVE_RE_COMP
@@ -178,9 +179,98 @@ netlist_norats (LibraryMenuType * net, LibraryEntryType * pin)
   hid_action ("NetlistChanged");
 }
 
+/* The primary purpose of this action is to remove the netlist
+   completely so that a new one can be loaded, usually via a gsch2pcb
+   style script.  */
+static void
+netlist_clear (LibraryMenuType * net, LibraryEntryType * pin)
+{
+  LibraryType *netlist = &PCB->NetlistLib;
+  int ni, pi;
+
+  if (net == 0)
+    {
+      /* Clear the entire netlist. */
+      FreeLibraryMemory (&PCB->NetlistLib);
+    }
+  else if (pin == 0)
+    {
+      /* Remove a net from the netlist. */
+      ni = net - netlist->Menu;
+      if (ni >= 0 && ni < netlist->MenuN)
+	{
+	  /* if there is exactly one item, MenuN is 1 and ni is 0 */
+	  if (netlist->MenuN - ni > 1)
+	    memmove (net, net+1, (netlist->MenuN - ni - 1) * sizeof (*net));
+	  netlist->MenuN --;
+	}
+    }
+  else
+    {
+      /* Remove a pin from the given net.  Note that this may leave an
+	 empty net, which is different than removing the net
+	 (above).  */
+      pi = pin - net->Entry;
+      if (pi >= 0 && pi < net->EntryN)
+	{
+	  /* if there is exactly one item, MenuN is 1 and ni is 0 */
+	  if (net->EntryN - pi > 1)
+	    memmove (pin, pin+1, (net->EntryN - pi - 1) * sizeof (*pin));
+	  net->EntryN --;
+	}
+    }
+  hid_action ("NetlistChanged");
+}
+
+static void
+netlist_style (LibraryMenuType *net, const char *style)
+{
+  if (net->Style)
+    MYFREE (net->Style);
+  if (style)
+    net->Style = MyStrdup ((char *)style, "Netlist(Style)");
+}
+
+/* The primary purpose of this action is to rebuild a netlist from a
+   script, in conjunction with the clear action above.  */
+static int
+netlist_add (const char *netname, const char *pinname, const char *defer_update)
+{
+  int ni, pi;
+  LibraryType *netlist = &PCB->NetlistLib;
+  LibraryMenuType *net = NULL;
+  LibraryEntryType *pin = NULL;
+
+  for (ni=0; ni<netlist->MenuN; ni++)
+    if (strcmp (netlist->Menu[ni].Name+2, netname) == 0)
+      {
+	net = & (netlist->Menu[ni]);
+	break;
+      }
+  if (net == NULL)
+    {
+      net = CreateNewNet (netlist, (char *)netname, NULL);
+    }
+
+  for (pi=0; pi<net->EntryN; pi++)
+    if (strcmp (net->Entry[pi].ListEntry, pinname) == 0)
+      {
+	pin = & (net->Entry[pi]);
+	break;
+      }
+  if (pin == NULL)
+    {
+      pin = CreateNewConnection (net, (char *)pinname);
+    }
+
+  if (!defer_update)
+    hid_action ("NetlistChanged");
+  return 0;
+}
 
 static const char netlist_syntax[] =
-  "Net(find|select|rats|norats[,net[,pin]])";
+  "Net(find|select|rats|norats|clear[,net[,pin]])\n"
+  "Net(add,net,pin[,defer])";
 
 static const char netlist_help[] = "Perform various actions on netlists.";
 
@@ -212,10 +302,23 @@ Nets which apply are marked as available for the rats nest.
 @item norats
 Nets which apply are marked as not available for the rats nest.
 
+@item clear
+Clears the netlist.
+
+@item add
+Add the given pin to the given netlist, creating either if needed.  If
+defer is specified, the GUI is not informed of this change - after a
+list of such changes, call NetlistChanged() to update the GUI.
+
+@item sort
+Called after a list of add's, this sorts the netlist.
+
 @end table
 
 %end-doc */
 
+#define ARG(n) (argc > (n) ? argv[n] : 0)
+
 static int
 Netlist (int argc, char **argv, int x, int y)
 {
@@ -251,6 +354,27 @@ Netlist (int argc, char **argv, int x, int y)
     func = netlist_rats;
   else if (strcasecmp (argv[0], "norats") == 0)
     func = netlist_norats;
+  else if (strcasecmp (argv[0], "clear") == 0)
+    {
+      func = netlist_clear;
+      if (argc == 1)
+	{
+	  netlist_clear (NULL, NULL);
+	  hid_action ("NetlistChanged");
+	  return 0;
+	}
+    }
+  else if (strcasecmp (argv[0], "style") == 0)
+    func = (void *)netlist_style;
+  else if (strcasecmp (argv[0], "add") == 0)
+    {
+      /* Add is different, because the net/pin won't already exist.  */
+      return netlist_add (ARG(1), ARG(2), ARG(3));
+    }
+  else if (strcasecmp (argv[0], "sort") == 0)
+    {
+      return sort_netlist ();
+    }
   else
     {
       Message (netlist_syntax);
@@ -295,7 +419,7 @@ Netlist (int argc, char **argv, int x, int y)
     }
 #endif
 
-  for (i = 0; i < PCB->NetlistLib.MenuN; i++)
+  for (i = PCB->NetlistLib.MenuN-1; i >= 0; i--)
     {
       net = PCB->NetlistLib.Menu + i;
 
@@ -321,10 +445,14 @@ Netlist (int argc, char **argv, int x, int y)
       net_found = 1;
 
       pin = 0;
-      if (argc > 2)
+      if (func == (void *)netlist_style)
+	{
+	  netlist_style (net, ARG(2));
+	}
+      else if (argc > 2)
 	{
 	  int l = strlen (argv[2]);
-	  for (j = 0; j < net->EntryN; j++)
+	  for (j = net->EntryN-1; j >= 0 ; j--)
 	    if (strcasecmp (net->Entry[j].ListEntry, argv[2]) == 0
 		|| (strncasecmp (net->Entry[j].ListEntry, argv[2], l) == 0
 		    && net->Entry[j].ListEntry[l] == '-'))




_______________________________________________
geda-cvs mailing list
geda-cvs@xxxxxxxxxxxxxx
http://www.seul.org/cgi-bin/mailman/listinfo/geda-cvs