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

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



The branch, master has been updated
       via  ea13d896dbead7151e9d86819c51f4d34d52c8d6 (commit)
       via  0400962a289e36dacdcc99fb36b938bb69c0a1e5 (commit)
       via  183d8a3535bfaefbeb3ac5045956c7b9c9e60982 (commit)
      from  76bc5883fdf1e1f6c5b9c16bdef7208911d713d6 (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                            |  130 ++++++++++++++++-
 src/autoroute.c                         |    1 +
 src/buffer.c                            |    4 +-
 src/const.h                             |    1 +
 src/copy.c                              |   18 ++-
 src/create.c                            |   11 ++
 src/create.h                            |    1 +
 src/crosshair.c                         |   55 +++----
 src/file.c                              |   20 +++-
 src/file.h                              |    2 +-
 src/flags.c                             |    1 +
 src/global.h                            |   32 +++--
 src/gpcb-menu.res                       |    1 +
 src/hid/gtk/gui-icons-mode-buttons.data |   28 ++++
 src/hid/gtk/gui-misc.c                  |    1 +
 src/hid/gtk/gui-output-events.c         |    1 +
 src/hid/gtk/gui-top-window.c            |    1 +
 src/hid/lesstif/main.c                  |    4 +
 src/insert.c                            |   18 ++-
 src/insert.h                            |    2 +-
 src/mymem.c                             |   23 +++
 src/mymem.h                             |    2 +
 src/parse_l.l                           |    1 +
 src/parse_y.y                           |   91 ++++++++----
 src/pcb-menu.res                        |    1 +
 src/polygon.c                           |  239 +++++++++++++++++++++++++------
 src/polygon.h                           |    6 +
 src/remove.c                            |  130 +++++++++++++----
 src/report.c                            |    3 +-
 src/set.c                               |    1 +
 src/undo.c                              |  158 +++++++++++++++++++--
 src/undo.h                              |    2 +
 32 files changed, 810 insertions(+), 179 deletions(-)


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

commit ea13d896dbead7151e9d86819c51f4d34d52c8d6
Author: Peter Clifton <pcjc2@xxxxxxxxx>
Commit: Peter Clifton <pcjc2@xxxxxxxxx>

    Introduce POLYGONHOLE_MODE for creating holes in polygons
    
    Having selected polygon hole mode, the first click selects which
    polygon to cut a hole in. A second click defines the start point of
    the hole contour. The tool then behaves in a similar way to the
    polygon drawing tool, with the hole ending when the start point is
    re-clicked.
    
    To avoid creating illegal polygons, the hole drawn is subtracted from
    a representation of the original polygon with the poly_Boolean_free().
    This consolidates any contours it intersects with and prevents the user
    defining contours which intersect each other. (Although we don't
    currently prevent the the user drawing self-intersecting contours).
    
    The resulting POLYAREA is re-processed into PolygonType objects,
    potentially more than one - if the hole drawn bisects the original
    polygon. To keep undo operations simple, these are added as completely
    new objects and the original polygon is deleted - along with its ID.

:100644 100644 ab2d6c5... 5f1c7a1... M	src/action.c
:100644 100644 2f97d4e... 676a6f0... M	src/const.h
:100644 100644 fa08695... b9a3dd2... M	src/crosshair.c
:100644 100644 41e0b55... 60688dd... M	src/flags.c
:100644 100644 df307be... 0db5aad... M	src/gpcb-menu.res
:100644 100644 1938ee2... be040f1... M	src/hid/gtk/gui-icons-mode-buttons.data
:100644 100644 32ca1c9... a607a69... M	src/hid/gtk/gui-misc.c
:100644 100644 c82e067... 7230aa3... M	src/hid/gtk/gui-output-events.c
:100644 100644 4d4ba7c... 4c00c13... M	src/hid/gtk/gui-top-window.c
:100644 100644 3280032... e16397e... M	src/hid/lesstif/main.c
:100644 100644 38e86e4... 0df4b7f... M	src/pcb-menu.res
:100644 100644 48d704d... 7a41f73... M	src/set.c

commit 0400962a289e36dacdcc99fb36b938bb69c0a1e5
Author: Peter Clifton <pcjc2@xxxxxxxxx>
Commit: Peter Clifton <pcjc2@xxxxxxxxx>

    Expose APIs for creating POLYAREA from PolygonType objects and back
    
    The PolygonPoly() API wraps polygon.c's original_poly() function,
    whilst PolyToPolygonsOnLayer() converts the passed POLYAREA and all
    those linked to it into discrete PolygonType objects on the board.

:100644 100644 e916741... f2745ed... M	src/polygon.c
:100644 100644 a93d965... e29f67f... M	src/polygon.h

commit 183d8a3535bfaefbeb3ac5045956c7b9c9e60982
Author: Peter Clifton <pcjc2@xxxxxxxxx>
Commit: Peter Clifton <pcjc2@xxxxxxxxx>

    Support holes in input polygons (incl. file-format addition)
    
    PCB file-format date is now 20100606, and files saved with this
    or greater PCB version will not load with older versions of PCB.
    If a particular board doesn't make use of the polygon hole feature,
    the PCB revision date in the file can be manually reset to 20070407.
    
    The file-format addition is as follows. Previously, a polygon would
    be specified as a series of coordinates, such as:
    
    Layer(1 "component")
    (
      Polygon("clearpoly")
      (
        [6000 6000] [81000 6000] [81000 59000] [6000 59000]
      )
    )
    
    This commit introduces the ability to specify negative contours
    which form holes in the polygon shape, e.g.:
    
    Layer(1 "component")
    (
      Polygon("")
      (
        [6000 6000] [81000 6000] [81000 59000] [6000 59000]
        Hole (
          [76000 55000] [76000 38000] [58000 38000] [58000 55000]
        )
        Hole (
          [10000 10000] [10000 28000] [27000 28000] [27000 10000]
        )
      )
    )
    
    The winding order of the contours specified in the file does not
    matter, since PCB will automatically invert the order of the points
    as necessary (as it always did with the outer contour).
    
    Hole contours should not intersect or self-intersect (although this
    isn't checked for at load time). Hole contours must not intersect
    the polygon's outer contour.
    
    Technical details:
    
    The PolygonType structure has a number of new fields, the critical
    ones being an array of indices defining the start of hole contours.
    
    (PolygonType *)->HoleIndex[n]
    
    The number of hole contours is stored in (PolygonType *)->HoleIndexN,
    and the maximum allocated memory for indices in (...)->HoleIndexMax.
    The first hole contour starts at the point given by
    (...)->Points[(...)->HoleIndex[0]], and continues until the start of
    the next contour, or the last point defined.
    
    By storing all polygon points (including holes) in the existing array
    (...)->Points[], existing code which operates on the polygon as a
    whole, e.g. translation and rotation, can operate without change.
    
    For other operations, determining wrap-around to operate within the
    same contour requires more computation. Some helper functions have
    been introduced in polygon.c to aid this, next_contour_point() and
    prev_contour_point(). Where applicable, these have been used to
    simplify existing code which used ad-hoc wrap-around code.
    
    polygon_point_idx() computes the array index of a point in a polygon
    from its PointTypePtr address. This is used to replace a search idiom
    used in a number of places. polygon_point_contour() returns the
    number of the contour a given point index belongs in, 0 for the outer
    contour, 1 for the first hole etc..
    
    Undo:
    
    Undo with holes has become a little more complex. The undo for a point
    removal must now record which contour the point came from. This is
    determined by the index of the removed point, and a new boolean flag
    "last_in_contour", indicating if the point was at the end of its contour.
    This flag is passed to InsertPointIntoObject(), which uses it to
    disambiguate inserting a point at an index on the boundary of two
    contours.
    
    Undo operations for removing hole contours "cheat" by saving a copy of
    the whole polygon into the undo buffer rather than attempting to
    describe the operation as a delta change to an existing polygon. When
    undoing, the object IDs are swapped to keep them consistent.

:100644 100644 6546be1... ab2d6c5... M	src/action.c
:100644 100644 b6bf91d... 7fb7443... M	src/autoroute.c
:100644 100644 e04ae2a... 7b8bce8... M	src/buffer.c
:100644 100644 ffa0ed3... 58ce9df... M	src/copy.c
:100644 100644 0c5abe1... b3d5bee... M	src/create.c
:100644 100644 b9f418c... 8086b1e... M	src/create.h
:100644 100644 13ddc71... fa08695... M	src/crosshair.c
:100644 100644 3129601... a61d53f... M	src/file.c
:100644 100644 5791dda... 4b9ecf2... M	src/file.h
:100644 100644 62be07f... bb78abc... M	src/global.h
:100644 100644 b4bcfe7... 5e62463... M	src/insert.c
:100644 100644 87b6cbb... 350a0de... M	src/insert.h
:100644 100644 46714bb... bed0f9b... M	src/mymem.c
:100644 100644 b9d4de1... 81dff7a... M	src/mymem.h
:100644 100644 0f3b72e... 8aa754c... M	src/parse_l.l
:100644 100644 1d64fe8... a5b2d23... M	src/parse_y.y
:100644 100644 97164ab... e916741... M	src/polygon.c
:100644 100644 1eb5757... a93d965... M	src/polygon.h
:100644 100644 a537bc0... 98b9ade... M	src/remove.c
:100644 100644 6a1c47b... dc01153... M	src/report.c
:100644 100644 50c6de5... 68ea8ff... M	src/undo.c
:100644 100644 c7a0288... 0a4601f... M	src/undo.h

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

commit ea13d896dbead7151e9d86819c51f4d34d52c8d6
Author: Peter Clifton <pcjc2@xxxxxxxxx>
Commit: Peter Clifton <pcjc2@xxxxxxxxx>

    Introduce POLYGONHOLE_MODE for creating holes in polygons
    
    Having selected polygon hole mode, the first click selects which
    polygon to cut a hole in. A second click defines the start point of
    the hole contour. The tool then behaves in a similar way to the
    polygon drawing tool, with the hole ending when the start point is
    re-clicked.
    
    To avoid creating illegal polygons, the hole drawn is subtracted from
    a representation of the original polygon with the poly_Boolean_free().
    This consolidates any contours it intersects with and prevents the user
    defining contours which intersect each other. (Although we don't
    currently prevent the the user drawing self-intersecting contours).
    
    The resulting POLYAREA is re-processed into PolygonType objects,
    potentially more than one - if the hole drawn bisects the original
    polygon. To keep undo operations simple, these are added as completely
    new objects and the original polygon is deleted - along with its ID.

diff --git a/src/action.c b/src/action.c
index ab2d6c5..5f1c7a1 100644
--- a/src/action.c
+++ b/src/action.c
@@ -145,6 +145,7 @@ typedef enum
   F_PinOrPadName,
   F_Pinout,
   F_Polygon,
+  F_PolygonHole,
   F_PreviousPoint,
   F_RatsNest,
   F_Rectangle,
@@ -380,6 +381,7 @@ static FunctionType Functions[] = {
   {"PinOrPadName", F_PinOrPadName},
   {"Pinout", F_Pinout},
   {"Polygon", F_Polygon},
+  {"PolygonHole", F_PolygonHole},
   {"PreviousPoint", F_PreviousPoint},
   {"RatsNest", F_RatsNest},
   {"Rectangle", F_Rectangle},
@@ -843,6 +845,7 @@ AdjustAttachedObjects (void)
 
       /* polygon creation mode */
     case POLYGON_MODE:
+    case POLYGONHOLE_MODE:
       AdjustAttachedLine ();
       break;
       /* line creation mode */
@@ -1448,6 +1451,101 @@ NotifyMode (void)
 	break;
       }
 
+    case POLYGONHOLE_MODE:
+      {
+	switch (Crosshair.AttachedObject.State)
+	  {
+	    /* first notify, lookup object */
+	  case STATE_FIRST:
+	    Crosshair.AttachedObject.Type =
+	      SearchScreen (Note.X, Note.Y, POLYGON_TYPE,
+			    &Crosshair.AttachedObject.Ptr1,
+			    &Crosshair.AttachedObject.Ptr2,
+			    &Crosshair.AttachedObject.Ptr3);
+
+	    if (Crosshair.AttachedObject.Type != NO_TYPE)
+	      {
+		if (TEST_FLAG (LOCKFLAG, (PolygonTypePtr)
+			       Crosshair.AttachedObject.Ptr2))
+		  {
+		    Message (_("Sorry, the object is locked\n"));
+		    Crosshair.AttachedObject.Type = NO_TYPE;
+		    break;
+		  }
+		else
+		  Crosshair.AttachedObject.State = STATE_SECOND;
+	      }
+	    break;
+
+            /* second notify, insert new point into object */
+          case STATE_SECOND:
+            {
+	      PointTypePtr points = Crosshair.AttachedPolygon.Points;
+	      Cardinal n = Crosshair.AttachedPolygon.PointN;
+	      POLYAREA *original, *new_hole, *result;
+	      FlagType Flags;
+
+	      /* do update of position; use the 'LINE_MODE' mechanism */
+	      NotifyLine ();
+
+	      /* check if this is the last point of a polygon */
+	      if (n >= 3 &&
+		  points->X == Crosshair.AttachedLine.Point2.X &&
+		  points->Y == Crosshair.AttachedLine.Point2.Y)
+		{
+		  /* Create POLYAREAs from the original polygon
+		   * and the new hole polygon */
+		  original = PolygonToPoly (Crosshair.AttachedObject.Ptr2);
+		  new_hole = PolygonToPoly (&Crosshair.AttachedPolygon);
+
+		  /* Subtract the hole from the original polygon shape */
+		  poly_Boolean_free (original, new_hole, &result, PBO_SUB);
+
+		  /* Convert the resulting polygon(s) into a new set of nodes
+		   * and place them on the page. Delete the original polygon.
+		   */
+		  SaveUndoSerialNumber ();
+		  Flags = ((PolygonType *)Crosshair.AttachedObject.Ptr2)->Flags;
+		  PolyToPolygonsOnLayer (PCB->Data, Crosshair.AttachedObject.Ptr1,
+					 result, Flags);
+		  RemoveObject (POLYGON_TYPE,
+				Crosshair.AttachedObject.Ptr1,
+				Crosshair.AttachedObject.Ptr2,
+				Crosshair.AttachedObject.Ptr3);
+		  RestoreUndoSerialNumber ();
+		  IncrementUndoSerialNumber ();
+		  Draw ();
+
+		/* reset state of attached line */
+		memset (&Crosshair.AttachedPolygon, 0, sizeof (PolygonType));
+		Crosshair.AttachedLine.State = STATE_FIRST;
+		addedLines = 0;
+
+		  break;
+		}
+
+	      /* create new point if it's the first one or if it's
+	       * different to the last one
+	       */
+	      if (!n ||
+		  points[n - 1].X != Crosshair.AttachedLine.Point2.X ||
+		  points[n - 1].Y != Crosshair.AttachedLine.Point2.Y)
+		{
+		  CreateNewPointInPolygon (&Crosshair.AttachedPolygon,
+					   Crosshair.AttachedLine.Point2.X,
+					   Crosshair.AttachedLine.Point2.Y);
+
+		  /* copy the coordinates */
+		  Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X;
+		  Crosshair.AttachedLine.Point1.Y = Crosshair.AttachedLine.Point2.Y;
+		}
+	      break;
+	    }
+	  }
+
+	break;
+      }
+
     case PASTEBUFFER_MODE:
       {
 	TextType estr[MAX_ELEMENTNAMES];
@@ -3022,6 +3120,16 @@ ActionMode (int argc, char **argv, int x, int y)
 		  }
 		break;
 
+	      case POLYGONHOLE_MODE:
+		if (Crosshair.AttachedLine.State == STATE_FIRST)
+		  SetMode (ARROW_MODE);
+		else
+		  {
+		    SetMode (NO_MODE);
+		    SetMode (POLYGONHOLE_MODE);
+		  }
+		break;
+
 	      case ARC_MODE:
 		if (Crosshair.AttachedBox.State == STATE_FIRST)
 		  SetMode (ARROW_MODE);
@@ -3050,6 +3158,9 @@ ActionMode (int argc, char **argv, int x, int y)
 	case F_Polygon:
 	  SetMode (POLYGON_MODE);
 	  break;
+	case F_PolygonHole:
+	  SetMode (POLYGONHOLE_MODE);
+	  break;
 #ifndef HAVE_LIBSTROKE
 	case F_Release:
 	  ReleaseMode ();
@@ -6098,7 +6209,8 @@ ActionUndo (int argc, char **argv, int x, int y)
   if (!function || !*function)
     {
       /* don't allow undo in the middle of an operation */
-      if (Crosshair.AttachedObject.State != STATE_FIRST)
+      if (Settings.Mode != POLYGONHOLE_MODE &&
+	  Crosshair.AttachedObject.State != STATE_FIRST)
 	return 1;
       if (Crosshair.AttachedBox.State != STATE_FIRST
 	  && Settings.Mode != ARC_MODE)
@@ -6106,7 +6218,9 @@ ActionUndo (int argc, char **argv, int x, int y)
       /* undo the last operation */
 
       HideCrosshair (true);
-      if (Settings.Mode == POLYGON_MODE && Crosshair.AttachedPolygon.PointN)
+      if ((Settings.Mode == POLYGON_MODE ||
+           Settings.Mode == POLYGONHOLE_MODE) &&
+          Crosshair.AttachedPolygon.PointN)
 	{
 	  GoToPreviousPoint ();
 	  RestoreCrosshair (true);
@@ -6268,7 +6382,8 @@ three "undone" lines.
 static int
 ActionRedo (int argc, char **argv, int x, int y)
 {
-  if ((Settings.Mode == POLYGON_MODE &&
+  if (((Settings.Mode == POLYGON_MODE ||
+        Settings.Mode == POLYGONHOLE_MODE) &&
        Crosshair.AttachedPolygon.PointN) ||
       Crosshair.AttachedLine.State == STATE_SECOND)
     return 1;
diff --git a/src/const.h b/src/const.h
index 2f97d4e..676a6f0 100644
--- a/src/const.h
+++ b/src/const.h
@@ -92,6 +92,7 @@
 #define ARROW_MODE		110	/* selection with arrow mode */
 #define PAN_MODE                0	/* same as no mode */
 #define LOCK_MODE               111	/* lock/unlock objects */
+#define	POLYGONHOLE_MODE	112	/* cut holes in filled polygons */
 
 /* ---------------------------------------------------------------------------
  * object flags
diff --git a/src/crosshair.c b/src/crosshair.c
index fa08695..b9a3dd2 100644
--- a/src/crosshair.c
+++ b/src/crosshair.c
@@ -588,8 +588,9 @@ DrawAttached (bool BlockToo)
 	}
       break;
 
-      /* the attached line is used by both LINEMODE and POLYGON_MODE */
+      /* the attached line is used by both LINEMODE, POLYGON_MODE and POLYGONHOLE_MODE*/
     case POLYGON_MODE:
+    case POLYGONHOLE_MODE:
       /* draw only if starting point is set */
       if (Crosshair.AttachedLine.State != STATE_FIRST)
 	gui->draw_line (Crosshair.GC,
@@ -598,7 +599,7 @@ DrawAttached (bool BlockToo)
 			Crosshair.AttachedLine.Point2.X,
 			Crosshair.AttachedLine.Point2.Y);
 
-      /* draw attached polygon only if in POLYGON_MODE */
+      /* draw attached polygon only if in POLYGON_MODE or POLYGONHOLE_MODE */
       if (Crosshair.AttachedPolygon.PointN > 1)
 	{
 	  XORPolygon (&Crosshair.AttachedPolygon, 0, 0);
diff --git a/src/flags.c b/src/flags.c
index 41e0b55..60688dd 100644
--- a/src/flags.c
+++ b/src/flags.c
@@ -192,6 +192,7 @@ HID_Flag flags_flag_list[] = {
   {"movemode", FlagMode, MOVE_MODE},
   {"pastebuffermode", FlagMode, PASTEBUFFER_MODE},
   {"polygonmode", FlagMode, POLYGON_MODE},
+  {"polygonholemode", FlagMode, POLYGONHOLE_MODE},
   {"rectanglemode", FlagMode, RECTANGLE_MODE},
   {"removemode", FlagMode, REMOVE_MODE},
   {"rotatemode", FlagMode, ROTATE_MODE},
diff --git a/src/gpcb-menu.res b/src/gpcb-menu.res
index df307be..0db5aad 100644
--- a/src/gpcb-menu.res
+++ b/src/gpcb-menu.res
@@ -508,6 +508,7 @@ PopupMenus =
        {"Text" checked=textmode,1 Mode(Text) a={"F4" "<Key>F4"}}
        {"Rectangle" checked=rectanglemode,1 Mode(Rectangle) a={"F5" "<Key>F5"}}
        {"Polygon" checked=polygonmode,1 Mode(Polygon) a={"F6" "<Key>F6"}}
+       {"Polygon Hole" checked=polygonholemode,1 Mode(PolygonHole)}
        {"Buffer" checked=pastebuffermode,1 Mode(PasteBuffer) a={"F7" "<Key>F7"}}
        {"Remove" checked=removemode,1 Mode(Remove) a={"F8" "<Key>F8"}}
        {"Rotate" checked=rotatemode,1 Mode(Rotate) a={"F9" "<Key>F9"}}
diff --git a/src/hid/gtk/gui-icons-mode-buttons.data b/src/hid/gtk/gui-icons-mode-buttons.data
index 1938ee2..be040f1 100644
--- a/src/hid/gtk/gui-icons-mode-buttons.data
+++ b/src/hid/gtk/gui-icons-mode-buttons.data
@@ -283,6 +283,34 @@ static char *poly[] = {
 "ooooooooooooooooooooo"
 };
 
+/* XPM */
+static char * polyhole[] = {
+"21 21 3 1",
+" 	c None",
+".	c #6EA5D7",
+"+	c #000000",
+"        ..           ",
+"       ...           ",
+"      .....          ",
+"     .......         ",
+"    .........        ",
+"  ....+++++...       ",
+"  ....+   +....      ",
+"  ...+    +.....     ",
+"  ...++++++......    ",
+"  ................   ",
+"  .................  ",
+"                     ",
+"  +  +  ++  +   +++  ",
+"  +  + +  + +   +    ",
+"  +  + +  + +   +    ",
+"  ++++ +  + +   +++  ",
+"  +  + +  + +   +    ",
+"  +  + +  + +   +    ",
+"  +  + +  + +   +    ",
+"  +  +  ++  +++ +++  ",
+"                     "
+};
 
 /* XPM */
 static char *rect[] = {
diff --git a/src/hid/gtk/gui-misc.c b/src/hid/gtk/gui-misc.c
index 32ca1c9..a607a69 100644
--- a/src/hid/gtk/gui-misc.c
+++ b/src/hid/gtk/gui-misc.c
@@ -220,6 +220,7 @@ ghid_mode_cursor (int Mode)
       break;
 
     case POLYGON_MODE:
+    case POLYGONHOLE_MODE:
       gport_set_cursor (GDK_SB_UP_ARROW);
       break;
 
diff --git a/src/hid/gtk/gui-output-events.c b/src/hid/gtk/gui-output-events.c
index c82e067..7230aa3 100644
--- a/src/hid/gtk/gui-output-events.c
+++ b/src/hid/gtk/gui-output-events.c
@@ -277,6 +277,7 @@ have_crosshair_attachments (void)
       result = TRUE;
       break;
     case POLYGON_MODE:
+    case POLYGONHOLE_MODE:
       if (Crosshair.AttachedLine.State != STATE_FIRST)
 	result = TRUE;
       break;
diff --git a/src/hid/gtk/gui-top-window.c b/src/hid/gtk/gui-top-window.c
index 4d4ba7c..4c00c13 100644
--- a/src/hid/gtk/gui-top-window.c
+++ b/src/hid/gtk/gui-top-window.c
@@ -1983,6 +1983,7 @@ static ModeButton mode_buttons[] = {
   {NULL, NULL, NULL, "text", TEXT_MODE, text},
   {NULL, NULL, NULL, "rectangle", RECTANGLE_MODE, rect},
   {NULL, NULL, NULL, "polygon", POLYGON_MODE, poly},
+  {NULL, NULL, NULL, "polygonhole", POLYGONHOLE_MODE, polyhole},
   {NULL, NULL, NULL, "buffer", PASTEBUFFER_MODE, buf},
   {NULL, NULL, NULL, "remove", REMOVE_MODE, del},
   {NULL, NULL, NULL, "rotate", ROTATE_MODE, rot},
diff --git a/src/hid/lesstif/main.c b/src/hid/lesstif/main.c
index 3280032..e16397e 100644
--- a/src/hid/lesstif/main.c
+++ b/src/hid/lesstif/main.c
@@ -2631,6 +2631,10 @@ idle_proc (XtPointer dummy)
 	    s = "Polygon";
 	    cursor = XC_sb_up_arrow;
 	    break;
+	  case POLYGONHOLE_MODE:
+	    s = "Polygon Hole";
+	    cursor = XC_sb_up_arrow;
+	    break;
 	  case PASTEBUFFER_MODE:
 	    s = "Paste";
 	    cursor = XC_hand1;
diff --git a/src/pcb-menu.res b/src/pcb-menu.res
index 38e86e4..0df4b7f 100644
--- a/src/pcb-menu.res
+++ b/src/pcb-menu.res
@@ -174,6 +174,7 @@ MainMenu =
    {"Text" checked=textmode,1 Mode(Text) a={"F4" "<Key>F4"}}
    {"Rectangle" checked=rectanglemode,1 Mode(Rectangle) a={"F5" "<Key>F5"}}
    {"Polygon" checked=polygonmode,1 Mode(Polygon) a={"F6" "<Key>F6"}}
+   {"Polygon Hole" checked=polygonholemode,1 Mode(PolygonHole)}
    {"Buffer" checked=pastebuffermode,1 Mode(PasteBuffer) a={"F7" "<Key>F7"}}
    {"Remove" checked=removemode,1 Mode(Remove) a={"F8" "<Key>F8"}}
    {"Rotate" checked=rotatemode,1 Mode(Rotate) a={"F9" "<Key>F9"}}
diff --git a/src/set.c b/src/set.c
index 48d704d..7a41f73 100644
--- a/src/set.c
+++ b/src/set.c
@@ -253,6 +253,7 @@ SetMode (int Mode)
     {
       if (Mode == ARC_MODE || Mode == RECTANGLE_MODE ||
 	  Mode == VIA_MODE || Mode == POLYGON_MODE ||
+	  Mode == POLYGONHOLE_MODE ||
 	  Mode == TEXT_MODE || Mode == INSERTPOINT_MODE ||
 	  Mode == THERMAL_MODE)
 	{

commit 0400962a289e36dacdcc99fb36b938bb69c0a1e5
Author: Peter Clifton <pcjc2@xxxxxxxxx>
Commit: Peter Clifton <pcjc2@xxxxxxxxx>

    Expose APIs for creating POLYAREA from PolygonType objects and back
    
    The PolygonPoly() API wraps polygon.c's original_poly() function,
    whilst PolyToPolygonsOnLayer() converts the passed POLYAREA and all
    those linked to it into discrete PolygonType objects on the board.

diff --git a/src/polygon.c b/src/polygon.c
index e916741..f2745ed 100644
--- a/src/polygon.c
+++ b/src/polygon.c
@@ -319,6 +319,12 @@ original_poly (PolygonType * p)
 }
 
 POLYAREA *
+PolygonToPoly (PolygonType *p)
+{
+  return original_poly (p);
+}
+
+POLYAREA *
 RectPoly (LocationType x1, LocationType x2, LocationType y1, LocationType y2)
 {
   PLINE *contour = NULL;
@@ -1830,3 +1836,59 @@ debug_polygon (PolygonType *p)
 	break;
     }
 }
+
+/* Convert a POLYAREA (and all linked POLYAREA) to
+ * raw PCB polygons on the given layer.
+ */
+void
+PolyToPolygonsOnLayer (DataType *Destination, LayerType *Layer,
+                       POLYAREA *Input, FlagType Flags)
+{
+  PolygonType *Polygon;
+  POLYAREA *pa;
+  PLINE *pline;
+  VNODE *node;
+  bool outer;
+
+  if (Input == NULL)
+    return;
+
+  pa = Input;
+  do
+    {
+      Polygon = CreateNewPolygon (Layer, Flags);
+
+      pline = pa->contours;
+      outer = true;
+
+      do
+        {
+          if (!outer)
+            CreateNewHoleInPolygon (Polygon);
+          outer = false;
+
+          node = &pline->head;
+          do
+            {
+              CreateNewPointInPolygon (Polygon, node->point[0],
+                                                node->point[1]);
+            }
+          while ((node = node->next) != &pline->head);
+
+        }
+      while ((pline = pline->next) != NULL);
+
+      InitClip (Destination, Layer, Polygon);
+      SetPolygonBoundingBox (Polygon);
+      if (!Layer->polygon_tree)
+        Layer->polygon_tree = r_create_tree (NULL, 0, 0);
+      r_insert_entry (Layer->polygon_tree, (BoxType *) Polygon, 0);
+
+      DrawPolygon (Layer, Polygon, 0);
+      /* add to undo list */
+      AddObjectToCreateUndoList (POLYGON_TYPE, Layer, Polygon, Polygon);
+    }
+  while ((pa = pa->f) != Input);
+
+  SetChangedFlag (true);
+}
diff --git a/src/polygon.h b/src/polygon.h
index a93d965..e29f67f 100644
--- a/src/polygon.h
+++ b/src/polygon.h
@@ -50,6 +50,7 @@ int PlowsPolygon (DataType *, int, void *, void *,
 		  int (*callback) (DataTypePtr, LayerTypePtr, PolygonTypePtr, int, void *, void *));
 void ComputeNoHoles (PolygonType *poly);
 POLYAREA * ContourToPoly (PLINE *);
+POLYAREA * PolygonToPoly (PolygonType *);
 POLYAREA * RectPoly (LocationType x1, LocationType x2, LocationType y1, LocationType y2);
 POLYAREA * CirclePoly(LocationType x, LocationType y, BDimension radius);
 POLYAREA * OctagonPoly(LocationType x, LocationType y, BDimension radius);
@@ -70,4 +71,5 @@ bool isects (POLYAREA *, PolygonTypePtr, bool);
 bool MorphPolygon (LayerTypePtr, PolygonTypePtr);
 void NoHolesPolygonDicer (PolygonType *p, const BoxType *clip,
                           void (*emit) (PLINE *, void *), void *user_data);
+void PolyToPolygonsOnLayer (DataType *, LayerType *, POLYAREA *, FlagType);
 #endif

commit 183d8a3535bfaefbeb3ac5045956c7b9c9e60982
Author: Peter Clifton <pcjc2@xxxxxxxxx>
Commit: Peter Clifton <pcjc2@xxxxxxxxx>

    Support holes in input polygons (incl. file-format addition)
    
    PCB file-format date is now 20100606, and files saved with this
    or greater PCB version will not load with older versions of PCB.
    If a particular board doesn't make use of the polygon hole feature,
    the PCB revision date in the file can be manually reset to 20070407.
    
    The file-format addition is as follows. Previously, a polygon would
    be specified as a series of coordinates, such as:
    
    Layer(1 "component")
    (
      Polygon("clearpoly")
      (
        [6000 6000] [81000 6000] [81000 59000] [6000 59000]
      )
    )
    
    This commit introduces the ability to specify negative contours
    which form holes in the polygon shape, e.g.:
    
    Layer(1 "component")
    (
      Polygon("")
      (
        [6000 6000] [81000 6000] [81000 59000] [6000 59000]
        Hole (
          [76000 55000] [76000 38000] [58000 38000] [58000 55000]
        )
        Hole (
          [10000 10000] [10000 28000] [27000 28000] [27000 10000]
        )
      )
    )
    
    The winding order of the contours specified in the file does not
    matter, since PCB will automatically invert the order of the points
    as necessary (as it always did with the outer contour).
    
    Hole contours should not intersect or self-intersect (although this
    isn't checked for at load time). Hole contours must not intersect
    the polygon's outer contour.
    
    Technical details:
    
    The PolygonType structure has a number of new fields, the critical
    ones being an array of indices defining the start of hole contours.
    
    (PolygonType *)->HoleIndex[n]
    
    The number of hole contours is stored in (PolygonType *)->HoleIndexN,
    and the maximum allocated memory for indices in (...)->HoleIndexMax.
    The first hole contour starts at the point given by
    (...)->Points[(...)->HoleIndex[0]], and continues until the start of
    the next contour, or the last point defined.
    
    By storing all polygon points (including holes) in the existing array
    (...)->Points[], existing code which operates on the polygon as a
    whole, e.g. translation and rotation, can operate without change.
    
    For other operations, determining wrap-around to operate within the
    same contour requires more computation. Some helper functions have
    been introduced in polygon.c to aid this, next_contour_point() and
    prev_contour_point(). Where applicable, these have been used to
    simplify existing code which used ad-hoc wrap-around code.
    
    polygon_point_idx() computes the array index of a point in a polygon
    from its PointTypePtr address. This is used to replace a search idiom
    used in a number of places. polygon_point_contour() returns the
    number of the contour a given point index belongs in, 0 for the outer
    contour, 1 for the first hole etc..
    
    Undo:
    
    Undo with holes has become a little more complex. The undo for a point
    removal must now record which contour the point came from. This is
    determined by the index of the removed point, and a new boolean flag
    "last_in_contour", indicating if the point was at the end of its contour.
    This flag is passed to InsertPointIntoObject(), which uses it to
    disambiguate inserting a point at an index on the boundary of two
    contours.
    
    Undo operations for removing hole contours "cheat" by saving a copy of
    the whole polygon into the undo buffer rather than attempting to
    describe the operation as a delta change to an existing polygon. When
    undoing, the object IDs are swapped to keep them consistent.

diff --git a/src/action.c b/src/action.c
index 6546be1..ab2d6c5 100644
--- a/src/action.c
+++ b/src/action.c
@@ -1641,9 +1641,8 @@ NotifyMode (void)
 			GetLowestDistancePolygonPoint (fake.poly, Note.X,
 						       Note.Y);
 		      fake.line.Point1 = fake.poly->Points[polyIndex];
-		      fake.line.Point2 = (polyIndex) ?
-			fake.poly->Points[polyIndex - 1]
-			: fake.poly->Points[fake.poly->PointN - 1];
+		      fake.line.Point2 = fake.poly->Points[
+			  prev_contour_point (fake.poly, polyIndex)];
 		      Crosshair.AttachedObject.Ptr2 = &fake.line;
 
 		    }
@@ -1659,13 +1658,13 @@ NotifyMode (void)
 	    InsertPointIntoObject (POLYGON_TYPE,
 				   Crosshair.AttachedObject.Ptr1, fake.poly,
 				   &polyIndex,
-				   InsertedPoint.X, InsertedPoint.Y, false);
+				   InsertedPoint.X, InsertedPoint.Y, false, false);
 	  else
 	    InsertPointIntoObject (Crosshair.AttachedObject.Type,
 				   Crosshair.AttachedObject.Ptr1,
 				   Crosshair.AttachedObject.Ptr2,
 				   &polyIndex,
-				   InsertedPoint.X, InsertedPoint.Y, false);
+				   InsertedPoint.X, InsertedPoint.Y, false, false);
 	  SetChangedFlag (true);
 
 	  /* reset identifiers */
diff --git a/src/autoroute.c b/src/autoroute.c
index b6bf91d..7fb7443 100644
--- a/src/autoroute.c
+++ b/src/autoroute.c
@@ -768,6 +768,7 @@ AddPolygon (PointerListType layergroupboxes[], Cardinal layer,
 			     polygon->BoundingBox.Y2,
 			     layergroup, polygon, style);
   if (polygon->PointN == 4 &&
+      polygon->HoleIndexN == 0 &&
       (polygon->Points[0].X == polygon->Points[1].X ||
        polygon->Points[0].Y == polygon->Points[1].Y) &&
       (polygon->Points[1].X == polygon->Points[2].X ||
diff --git a/src/buffer.c b/src/buffer.c
index e04ae2a..7b8bce8 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -194,7 +194,7 @@ AddPolygonToBuffer (LayerTypePtr Layer, PolygonTypePtr Polygon)
   LayerTypePtr layer = &Dest->Layer[GetLayerNumber (Source, Layer)];
   PolygonTypePtr polygon;
 
-  polygon = GetPolygonMemory (layer);
+  polygon = CreateNewPolygon (layer, Polygon->Flags);
   CopyPolygonLowLevel (polygon, Polygon);
   CLEAR_FLAG (FOUNDFLAG | ExtraFlag, polygon);
   return (polygon);
@@ -1001,7 +1001,7 @@ polygon_is_rectangle (PolygonTypePtr poly)
 {
   int i, best;
   PointType temp[4];
-  if (poly->PointN != 4)
+  if (poly->PointN != 4 || poly->HoleIndexN != 0)
     return 0;
   best = 0;
   for (i=1; i<4; i++)
diff --git a/src/copy.c b/src/copy.c
index ffa0ed3..58ce9df 100644
--- a/src/copy.c
+++ b/src/copy.c
@@ -95,12 +95,18 @@ static ObjectFunctionType CopyFunctions = {
 PolygonTypePtr
 CopyPolygonLowLevel (PolygonTypePtr Dest, PolygonTypePtr Src)
 {
-  /* copy all data */
-  POLYGONPOINT_LOOP (Src);
-  {
-    CreateNewPointInPolygon (Dest, point->X, point->Y);
-  }
-  END_LOOP;
+  Cardinal hole = 0;
+  Cardinal n;
+
+  for (n = 0; n < Src->PointN; n++)
+    {
+      if (hole < Src->HoleIndexN && n == Src->HoleIndex[hole])
+        {
+          CreateNewHoleInPolygon (Dest);
+          hole++;
+        }
+      CreateNewPointInPolygon (Dest, Src->Points[n].X, Src->Points[n].Y);
+    }
   SetPolygonBoundingBox (Dest);
   Dest->Flags = Src->Flags;
   CLEAR_FLAG (FOUNDFLAG, Dest);
diff --git a/src/create.c b/src/create.c
index 0c5abe1..b3d5bee 100644
--- a/src/create.c
+++ b/src/create.c
@@ -635,6 +635,17 @@ CreateNewPointInPolygon (PolygonTypePtr Polygon, LocationType X,
 }
 
 /* ---------------------------------------------------------------------------
+ * creates a new hole in a polygon
+ */
+PolygonType *
+CreateNewHoleInPolygon (PolygonType *Polygon)
+{
+  Cardinal *holeindex = GetHoleIndexMemoryInPolygon (Polygon);
+  *holeindex = Polygon->PointN;
+  return Polygon;
+}
+
+/* ---------------------------------------------------------------------------
  * creates an new element
  * memory is allocated if needed
  */
diff --git a/src/create.h b/src/create.h
index b9f418c..8086b1e 100644
--- a/src/create.h
+++ b/src/create.h
@@ -62,6 +62,7 @@ TextTypePtr CreateNewText (LayerTypePtr, FontTypePtr, LocationType,
 PolygonTypePtr CreateNewPolygon (LayerTypePtr, FlagType);
 PointTypePtr CreateNewPointInPolygon (PolygonTypePtr,
 				      LocationType, LocationType);
+PolygonType *CreateNewHoleInPolygon (PolygonType *polygon);
 ElementTypePtr CreateNewElement (DataTypePtr, ElementTypePtr,
 				 FontTypePtr, FlagType, char *, char *,
 				 char *, LocationType, LocationType, BYTE,
diff --git a/src/crosshair.c b/src/crosshair.c
index 13ddc71..fa08695 100644
--- a/src/crosshair.c
+++ b/src/crosshair.c
@@ -48,6 +48,7 @@
 #include "misc.h"
 #include "mymem.h"
 #include "search.h"
+#include "polygon.h"
 
 #ifdef HAVE_LIBDMALLOC
 #include <dmalloc.h>
@@ -92,16 +93,16 @@ static void DrawAttached (bool);
 static void
 XORPolygon (PolygonTypePtr polygon, LocationType dx, LocationType dy)
 {
-  int i;
-  for (i = 0; i < polygon->PointN - 1; i++)
-    gui->draw_line (Crosshair.GC,
-		    polygon->Points[i].X + dx, polygon->Points[i].Y + dy,
-		    polygon->Points[i + 1].X + dx,
-		    polygon->Points[i + 1].Y + dy);
-  if (i > 1)
-    gui->draw_line (Crosshair.GC,
-		    polygon->Points[i].X + dx, polygon->Points[i].Y + dy,
-		    polygon->Points[0].X + dx, polygon->Points[0].Y + dy);
+  Cardinal i;
+  for (i = 0; i < polygon->PointN; i++)
+    {
+      Cardinal next = next_contour_point (polygon, i);
+      gui->draw_line (Crosshair.GC,
+                      polygon->Points[i].X + dx,
+                      polygon->Points[i].Y + dy,
+                      polygon->Points[next].X + dx,
+                      polygon->Points[next].Y + dy);
+    }
 }
 
 /*-----------------------------------------------------------
@@ -473,35 +474,24 @@ XORDrawMoveOrCopyObject (void)
     case POLYGONPOINT_TYPE:
       {
 	PolygonTypePtr polygon;
-	PointTypePtr point, previous, following;
+	PointTypePtr point;
+	Cardinal point_idx, prev, next;
 
 	polygon = (PolygonTypePtr) Crosshair.AttachedObject.Ptr2;
 	point = (PointTypePtr) Crosshair.AttachedObject.Ptr3;
+	point_idx = polygon_point_idx (polygon, point);
 
 	/* get previous and following point */
-	if (point == polygon->Points)
-	  {
-	    previous = &polygon->Points[polygon->PointN - 1];
-	    following = point + 1;
-	  }
-	else if (point == &polygon->Points[polygon->PointN - 1])
-	  {
-	    previous = point - 1;
-	    following = &polygon->Points[0];
-	  }
-	else
-	  {
-	    previous = point - 1;
-	    following = point + 1;
-	  }
+	prev = prev_contour_point (polygon, point_idx);
+	next = next_contour_point (polygon, point_idx);
 
 	/* draw the two segments */
 	gui->draw_line (Crosshair.GC,
-			previous->X,
-			previous->Y, point->X + dx, point->Y + dy);
+			polygon->Points[prev].X, polygon->Points[prev].Y,
+			point->X + dx, point->Y + dy);
 	gui->draw_line (Crosshair.GC,
-			point->X + dx,
-			point->Y + dy, following->X, following->Y);
+			point->X + dx, point->Y + dy,
+			polygon->Points[next].X, polygon->Points[next].Y);
 	break;
       }
 
diff --git a/src/file.c b/src/file.c
index 3129601..a61d53f 100644
--- a/src/file.c
+++ b/src/file.c
@@ -825,14 +825,32 @@ WriteLayerData (FILE * FP, Cardinal Number, LayerTypePtr layer)
 	{
 	  PolygonTypePtr polygon = &layer->Polygon[n];
 	  int p, i = 0;
+	  Cardinal hole = 0;
 	  fprintf (FP, "\tPolygon(%s)\n\t(", F2S (polygon, POLYGON_TYPE));
 	  for (p = 0; p < polygon->PointN; p++)
 	    {
 	      PointTypePtr point = &polygon->Points[p];
+
+	      if (hole < polygon->HoleIndexN &&
+		  p == polygon->HoleIndex[hole])
+		{
+		  if (hole > 0)
+		    fputs ("\n\t\t)", FP);
+		  fputs ("\n\t\tHole (", FP);
+		  hole++;
+		  i = 0;
+		}
+
 	      if (i++ % 5 == 0)
-		fputs ("\n\t\t", FP);
+		{
+		  fputs ("\n\t\t", FP);
+		  if (hole)
+		    fputs ("\t", FP);
+		}
 	      fprintf (FP, "[%i %i] ", (int) point->X, (int) point->Y);
 	    }
+	  if (hole > 0)
+	    fputs ("\n\t\t)", FP);
 	  fputs ("\n\t)\n", FP);
 	}
       fputs (")\n", FP);
diff --git a/src/file.h b/src/file.h
index 5791dda..4b9ecf2 100644
--- a/src/file.h
+++ b/src/file.h
@@ -56,7 +56,7 @@ void sort_netlist (void);
  * guidance to the user as to what the minimum version of pcb required
  * is.
  */
-#define PCB_FILE_VERSION 20070407
+#define PCB_FILE_VERSION 20100606
 
 
 #ifndef HAS_ATEXIT
diff --git a/src/global.h b/src/global.h
index 62be07f..bb78abc 100644
--- a/src/global.h
+++ b/src/global.h
@@ -260,6 +260,10 @@ struct polygon_st			/* holds information about a polygon */
   PLINE *NoHoles;		/* the polygon broken into hole-less regions */
   int NoHolesValid;		/* Is the NoHoles polygon up to date? */
   PointTypePtr Points;		/* data */
+  Cardinal *HoleIndex;		/* Index of hole data within the Points array */
+  Cardinal HoleIndexN;		/* number of holes in polygon */
+  Cardinal HoleIndexMax;	/* max number from malloc() */
+
 };
 
 typedef struct			/* holds information about arcs */
@@ -779,19 +783,21 @@ struct drc_violation_st
 #define	UNDO_REMOVE			0x0004	/* removing objects */
 #define	UNDO_REMOVE_POINT		0x0008	/* removing polygon/... points */
 #define	UNDO_INSERT_POINT		0x0010	/* inserting polygon/... points */
-#define	UNDO_ROTATE			0x0020	/* rotations */
-#define	UNDO_CREATE			0x0040	/* creation of objects */
-#define	UNDO_MOVETOLAYER		0x0080	/* moving objects to */
-#define UNDO_FLAG			0x0100	/* toggling SELECTED flag */
-#define UNDO_CHANGESIZE			0x0200	/* change size of object */
-#define UNDO_CHANGE2NDSIZE		0x0400	/* change 2ndSize of object */
-#define UNDO_MIRROR			0x0800	/* change side of board */
-#define UNDO_CHANGECLEARSIZE		0x1000	/* change clearance size */
-#define UNDO_CHANGEMASKSIZE		0x2000	/* change mask size */
-#define UNDO_CHANGEANGLES		0x4000	/* change arc angles */
-#define UNDO_LAYERCHANGE		0x8000	/* layer new/delete/move */
-#define UNDO_CLEAR		       0x10000  /* clear/restore to polygons */
-#define UNDO_NETLISTCHANGE     	       0x20000	/* netlist change */
+#define	UNDO_REMOVE_CONTOUR		0x0020	/* removing a contour from a polygon */
+#define	UNDO_INSERT_CONTOUR		0x0040	/* inserting a contour from a polygon */
+#define	UNDO_ROTATE			0x0080	/* rotations */
+#define	UNDO_CREATE			0x0100	/* creation of objects */
+#define	UNDO_MOVETOLAYER		0x0200	/* moving objects to */
+#define	UNDO_FLAG			0x0400	/* toggling SELECTED flag */
+#define	UNDO_CHANGESIZE			0x0800	/* change size of object */
+#define	UNDO_CHANGE2NDSIZE		0x1000	/* change 2ndSize of object */
+#define	UNDO_MIRROR			0x2000	/* change side of board */
+#define	UNDO_CHANGECLEARSIZE		0x4000	/* change clearance size */
+#define	UNDO_CHANGEMASKSIZE		0x8000	/* change mask size */
+#define	UNDO_CHANGEANGLES	       0x10000	/* change arc angles */
+#define	UNDO_LAYERCHANGE	       0x20000	/* layer new/delete/move */
+#define	UNDO_CLEAR		       0x40000	/* clear/restore to polygons */
+#define	UNDO_NETLISTCHANGE	       0x80000	/* netlist change */
 
 
 /* ---------------------------------------------------------------------------
diff --git a/src/insert.c b/src/insert.c
index b4bcfe7..5e62463 100644
--- a/src/insert.c
+++ b/src/insert.c
@@ -74,6 +74,7 @@ static void *InsertPointIntoRat (RatTypePtr);
 static LocationType InsertX,	/* used by local routines as offset */
   InsertY;
 static Cardinal InsertAt;
+static bool InsertLast;
 static bool Forcible;
 static ObjectFunctionType InsertFunctions = {
   InsertPointIntoLine,
@@ -174,10 +175,7 @@ InsertPointIntoPolygon (LayerTypePtr Layer, PolygonTypePtr Polygon)
        * first make sure adding the point is sensible
        */
       line.Thickness = 0;
-      if (InsertAt == 0)
-	line.Point1 = Polygon->Points[Polygon->PointN - 1];
-      else
-	line.Point1 = Polygon->Points[InsertAt - 1];
+      line.Point1 = Polygon->Points[prev_contour_point (Polygon, InsertAt)];
       line.Point2 = Polygon->Points[InsertAt];
       if (IsPointOnLine ((float) InsertX, (float) InsertY, 0.0, &line))
 	return (NULL);
@@ -190,10 +188,18 @@ InsertPointIntoPolygon (LayerTypePtr Layer, PolygonTypePtr Polygon)
   save = *CreateNewPointInPolygon (Polygon, InsertX, InsertY);
   for (n = Polygon->PointN - 1; n > InsertAt; n--)
     Polygon->Points[n] = Polygon->Points[n - 1];
+
+  /* Shift up indices of any holes */
+  for (n = 0; n < Polygon->HoleIndexN; n++)
+    if (Polygon->HoleIndex[n] > InsertAt ||
+	(InsertLast && Polygon->HoleIndex[n] == InsertAt))
+      Polygon->HoleIndex[n]++;
+
   Polygon->Points[InsertAt] = save;
   SetChangedFlag (true);
   AddObjectToInsertPointUndoList (POLYGONPOINT_TYPE, Layer, Polygon,
 				  &Polygon->Points[InsertAt]);
+
   SetPolygonBoundingBox (Polygon);
   r_insert_entry (Layer->polygon_tree, (BoxType *) Polygon, 0);
   InitClip (PCB->Data, Layer, Polygon);
@@ -210,7 +216,8 @@ InsertPointIntoPolygon (LayerTypePtr Layer, PolygonTypePtr Polygon)
  */
 void *
 InsertPointIntoObject (int Type, void *Ptr1, void *Ptr2, Cardinal * Ptr3,
-		       LocationType DX, LocationType DY, bool Force)
+		       LocationType DX, LocationType DY, bool Force,
+		       bool insert_last)
 {
   void *ptr;
 
@@ -218,6 +225,7 @@ InsertPointIntoObject (int Type, void *Ptr1, void *Ptr2, Cardinal * Ptr3,
   InsertX = DX;
   InsertY = DY;
   InsertAt = *Ptr3;
+  InsertLast = insert_last;
   Forcible = Force;
 
   /* the operation insert the points to the undo-list */
diff --git a/src/insert.h b/src/insert.h
index 87b6cbb..350a0de 100644
--- a/src/insert.h
+++ b/src/insert.h
@@ -39,7 +39,7 @@
  * prototypes
  */
 void *InsertPointIntoObject (int, void *, void *, Cardinal *, LocationType,
-			     LocationType, bool);
+			     LocationType, bool, bool);
 PointTypePtr AdjustInsertPoint (void);
 
 #endif
diff --git a/src/mymem.c b/src/mymem.c
index 46714bb..bed0f9b 100644
--- a/src/mymem.c
+++ b/src/mymem.c
@@ -459,6 +459,28 @@ GetPointMemoryInPolygon (PolygonTypePtr Polygon)
 }
 
 /* ---------------------------------------------------------------------------
+ * gets the next slot for a point in a polygon struct, allocates memory
+ * if necessary
+ */
+Cardinal *
+GetHoleIndexMemoryInPolygon (PolygonTypePtr Polygon)
+{
+  Cardinal *holeindex = Polygon->HoleIndex;
+
+  /* realloc new memory if necessary and clear it */
+  if (Polygon->HoleIndexN >= Polygon->HoleIndexMax)
+    {
+      Polygon->HoleIndexMax += STEP_POLYGONHOLEINDEX;
+      holeindex = MyRealloc (holeindex, Polygon->HoleIndexMax * sizeof (int),
+			     "GetHoleIndexMemoryInPolygon()");
+      Polygon->HoleIndex = holeindex;
+      memset (holeindex + Polygon->HoleIndexN, 0,
+	      STEP_POLYGONHOLEINDEX * sizeof (int));
+    }
+  return (holeindex + Polygon->HoleIndexN++);
+}
+
+/* ---------------------------------------------------------------------------
  * get next slot for an element, allocates memory if necessary
  */
 ElementTypePtr
@@ -734,6 +756,7 @@ FreePolygonMemory (PolygonTypePtr Polygon)
   if (Polygon)
     {
       MYFREE (Polygon->Points);
+      MYFREE (Polygon->HoleIndex);
       if (Polygon->Clipped)
 	poly_Free (&Polygon->Clipped);
       poly_FreeContours (&Polygon->NoHoles);
diff --git a/src/mymem.h b/src/mymem.h
index b9d4de1..81dff7a 100644
--- a/src/mymem.h
+++ b/src/mymem.h
@@ -58,6 +58,7 @@
 #define	STEP_UNDOLIST		500
 #define	STEP_POLYGON		10
 #define	STEP_POLYGONPOINT	10
+#define	STEP_POLYGONHOLEINDEX	10
 #define	STEP_LIBRARYMENU	10
 #define	STEP_LIBRARYENTRY	20
 #define	STEP_RUBBERBAND		100
@@ -82,6 +83,7 @@ RatTypePtr GetRatMemory (DataTypePtr);
 TextTypePtr GetTextMemory (LayerTypePtr);
 PolygonTypePtr GetPolygonMemory (LayerTypePtr);
 PointTypePtr GetPointMemoryInPolygon (PolygonTypePtr);
+Cardinal *GetHoleIndexMemoryInPolygon (PolygonTypePtr);
 ElementTypePtr GetElementMemory (DataTypePtr);
 BoxTypePtr GetBoxMemory (BoxListTypePtr);
 ConnectionTypePtr GetConnectionMemory (NetTypePtr);
diff --git a/src/parse_l.l b/src/parse_l.l
index 0f3b72e..8aa754c 100644
--- a/src/parse_l.l
+++ b/src/parse_l.l
@@ -129,6 +129,7 @@ Mark		{ return(T_MARK); }
 Groups		{ return(T_GROUPS); }
 Styles		{ return(T_STYLES); }
 Polygon		{ return(T_POLYGON); }
+Hole		{ return(T_POLYGON_HOLE); }
 Arc		{ return(T_ARC); }
 NetList		{ return(T_NETLIST); }
 Net		{ return(T_NET); }
diff --git a/src/parse_y.y b/src/parse_y.y
index 1d64fe8..a5b2d23 100644
--- a/src/parse_y.y
+++ b/src/parse_y.y
@@ -102,9 +102,8 @@ static int check_file_version (int);
 
 %token	T_FILEVERSION T_PCB T_LAYER T_VIA T_RAT T_LINE T_ARC T_RECTANGLE T_TEXT T_ELEMENTLINE
 %token	T_ELEMENT T_PIN T_PAD T_GRID T_FLAGS T_SYMBOL T_SYMBOLLINE T_CURSOR
-%token	T_ELEMENTARC T_MARK T_GROUPS T_STYLES T_POLYGON T_NETLIST T_NET T_CONN
+%token	T_ELEMENTARC T_MARK T_GROUPS T_STYLES T_POLYGON T_POLYGON_HOLE T_NETLIST T_NET T_CONN
 %token	T_AREA T_THERMAL T_DRC T_ATTRIBUTE
-
 %type	<number>	symbolid
 %type	<string>	opt_string
 %type	<flagtype>	flags
@@ -895,31 +894,7 @@ layerdefinition
 		| text_newformat
 		| text_oldformat
 		| { attr_list = & Layer->Attributes; } attributes
-			/* flags are passed in */
-		| T_POLYGON '(' flags ')' '('
-			{
-				Polygon = CreateNewPolygon(Layer, $3);
-			}
-		  polygonpoints ')'
-		  	{
-					/* ignore junk */
-				if (Polygon->PointN >= 3)
-				  {
-				    SetPolygonBoundingBox (Polygon);
-				    if (!Layer->polygon_tree)
-				      Layer->polygon_tree = r_create_tree (NULL, 0, 0);
-				    r_insert_entry (Layer->polygon_tree, (BoxType *) Polygon, 0);
-				  }
-				else
-				{
-					Message("WARNING parsing file '%s'\n"
-						"    line:        %i\n"
-						"    description: 'ignored polygon (< 3 points)'\n",
-						yyfilename, yylineno);
-					DestroyObject(yyData, POLYGON_TYPE, Layer, Polygon, Polygon);
-				}
-			}
-		;
+		| polygon_format
 
 /* %start-doc pcbfile Line
 
@@ -1124,6 +1099,11 @@ text_hi_format
 Polygon (SFlags) (
 @ @ @ @dots{} (X Y) @dots{}
 @ @ @ @dots{} [X Y] @dots{}
+@ @ @ Hole (
+@ @ @ @ @ @ @dots{} (X Y) @dots{}
+@ @ @ @ @ @ @dots{} [X Y] @dots{}
+@ @ @ )
+@ @ @ @dots{}
 )
 @end syntax
 
@@ -1132,10 +1112,67 @@ Polygon (SFlags) (
 Symbolic or numeric flags.
 @item X Y
 Coordinates of each vertex.  You must list at least three coordinates.
+@item Hole (...)
+Defines a hole within the polygon's outer contour. There may be zero or more such sections.
 @end table
 
 %end-doc */
 
+polygon_format
+		: /* flags are passed in */
+		T_POLYGON '(' flags ')' '('
+			{
+				Polygon = CreateNewPolygon(Layer, $3);
+			}
+		  polygonpoints
+		  polygonholes ')'
+			{
+				Cardinal contour, contour_start, contour_end;
+				bool bad_contour_found = false;
+				/* ignore junk */
+				for (contour = 0; contour <= Polygon->HoleIndexN; contour++)
+				  {
+				    contour_start = (contour == 0) ?
+						      0 : Polygon->HoleIndex[contour - 1];
+				    contour_end = (contour == Polygon->HoleIndexN) ?
+						 Polygon->PointN :
+						 Polygon->HoleIndex[contour];
+				    if (contour_end - contour_start < 3)
+				      bad_contour_found = true;
+				  }
+
+				if (bad_contour_found)
+				  {
+				    Message("WARNING parsing file '%s'\n"
+					    "    line:        %i\n"
+					    "    description: 'ignored polygon (< 3 points in a contour)'\n",
+					    yyfilename, yylineno);
+				    DestroyObject(yyData, POLYGON_TYPE, Layer, Polygon, Polygon);
+				  }
+				else
+				  {
+				    SetPolygonBoundingBox (Polygon);
+				    if (!Layer->polygon_tree)
+				      Layer->polygon_tree = r_create_tree (NULL, 0, 0);
+				    r_insert_entry (Layer->polygon_tree, (BoxType *) Polygon, 0);
+				  }
+			}
+		;
+
+polygonholes
+		: /* empty */
+		| polygonhole
+		| polygonholes polygonhole
+		;
+
+polygonhole
+		: T_POLYGON_HOLE '('
+			{
+				CreateNewHoleInPolygon (Polygon);
+			}
+		  polygonpoints ')'
+		;
+
 polygonpoints
 		: polygonpoint
 		| polygonpoints polygonpoint
diff --git a/src/polygon.c b/src/polygon.c
index 97164ab..e916741 100644
--- a/src/polygon.c
+++ b/src/polygon.c
@@ -120,6 +120,73 @@ static double circleVerticies[] = {
   0.98768834059513777, 0.15643446504023087,
 };
 
+Cardinal
+polygon_point_idx (PolygonTypePtr polygon, PointTypePtr point)
+{
+  assert (point >= polygon->Points);
+  assert (point <= polygon->Points + polygon->PointN);
+  return ((char *)point - (char *)polygon->Points) / sizeof (PointType);
+}
+
+/* Find contour number: 0 for outer, 1 for first hole etc.. */
+Cardinal
+polygon_point_contour (PolygonTypePtr polygon, Cardinal point)
+{
+  Cardinal i;
+  Cardinal contour = 0;
+
+  for (i = 0; i < polygon->HoleIndexN; i++)
+    if (point >= polygon->HoleIndex[i])
+      contour = i + 1;
+  return contour;
+}
+
+Cardinal
+next_contour_point (PolygonTypePtr polygon, Cardinal point)
+{
+  Cardinal contour;
+  Cardinal this_contour_start;
+  Cardinal next_contour_start;
+
+  contour = polygon_point_contour (polygon, point);
+
+  this_contour_start = (contour == 0) ? 0 :
+                                        polygon->HoleIndex[contour - 1];
+  next_contour_start =
+    (contour == polygon->HoleIndexN) ? polygon->PointN :
+                                       polygon->HoleIndex[contour];
+
+  /* Wrap back to the start of the contour we're in if we pass the end */
+  if (++point == next_contour_start)
+    point = this_contour_start;
+
+  return point;
+}
+
+Cardinal
+prev_contour_point (PolygonTypePtr polygon, Cardinal point)
+{
+  Cardinal contour;
+  Cardinal prev_contour_end;
+  Cardinal this_contour_end;
+
+  contour = polygon_point_contour (polygon, point);
+
+  prev_contour_end = (contour == 0) ? 0 :
+                                      polygon->HoleIndex[contour - 1];
+  this_contour_end =
+    (contour == polygon->HoleIndexN) ? polygon->PointN - 1:
+                                       polygon->HoleIndex[contour] - 1;
+
+  /* Wrap back to the start of the contour we're in if we pass the end */
+  if (point == prev_contour_end)
+    point = this_contour_end;
+  else
+    point--;
+
+  return point;
+}
+
 static void
 add_noholes_polyarea (PLINE *pline, void *user_data)
 {
@@ -205,33 +272,49 @@ original_poly (PolygonType * p)
 {
   PLINE *contour = NULL;
   POLYAREA *np = NULL;
+  Cardinal n;
   Vector v;
+  int hole = 0;
 
-  /* first make initial polygon contour */
-  POLYGONPOINT_LOOP (p);
-  {
-    v[0] = point->X;
-    v[1] = point->Y;
-    if (contour == NULL)
-      {
-        if ((contour = poly_NewContour (v)) == NULL)
-          return NULL;
-      }
-    else
-      {
-        poly_InclVertex (contour->head.prev, poly_CreateNode (v));
-      }
-  }
-  END_LOOP;
-  poly_PreContour (contour, TRUE);
-  /* make sure it is a positive contour */
-  if ((contour->Flags.orient) != PLF_DIR)
-    poly_InvContour (contour);
-  assert ((contour->Flags.orient) == PLF_DIR);
   if ((np = poly_Create ()) == NULL)
     return NULL;
-  poly_InclContour (np, contour);
-  assert (poly_Valid (np));
+
+  /* first make initial polygon contour */
+  for (n = 0; n < p->PointN; n++)
+    {
+      /* No current contour? Make a new one starting at point */
+      /*   (or) Add point to existing contour */
+
+      v[0] = p->Points[n].X;
+      v[1] = p->Points[n].Y;
+      if (contour == NULL)
+        {
+          if ((contour = poly_NewContour (v)) == NULL)
+            return NULL;
+        }
+      else
+        {
+          poly_InclVertex (contour->head.prev, poly_CreateNode (v));
+        }
+
+      /* Is current point last in contour? If so process it. */
+      if (n == p->PointN - 1 ||
+          (hole < p->HoleIndexN && n == p->HoleIndex[hole] - 1))
+        {
+          poly_PreContour (contour, TRUE);
+
+          /* make sure it is a positive contour (outer) or negative (hole) */
+          if (contour->Flags.orient != (hole ? PLF_INV : PLF_DIR))
+            poly_InvContour (contour);
+          assert (contour->Flags.orient == (hole ? PLF_INV : PLF_DIR));
+
+          poly_InclContour (np, contour);
+          contour = NULL;
+          assert (poly_Valid (np));
+
+          hole++;
+        }
+  }
   return biggest (np);
 }
 
@@ -1078,31 +1161,26 @@ InitClip (DataTypePtr Data, LayerTypePtr layer, PolygonType * p)
 bool
 RemoveExcessPolygonPoints (LayerTypePtr Layer, PolygonTypePtr Polygon)
 {
-  PointTypePtr pt1, pt2, pt3;
-  Cardinal n;
+  PointTypePtr p;
+  Cardinal n, prev, next;
   LineType line;
   bool changed = false;
 
   if (Undoing ())
     return (false);
-  /* there are always at least three points in a polygon */
-  pt1 = &Polygon->Points[Polygon->PointN - 1];
-  pt2 = &Polygon->Points[0];
-  pt3 = &Polygon->Points[1];
-  for (n = 0; n < Polygon->PointN; n++, pt1++, pt2++, pt3++)
+
+  for (n = 0; n < Polygon->PointN; n++)
     {
-      /* wrap around polygon */
-      if (n == 1)
-        pt1 = &Polygon->Points[0];
-      if (n == Polygon->PointN - 1)
-        pt3 = &Polygon->Points[0];
-      line.Point1 = *pt1;
-      line.Point2 = *pt3;
+      prev = prev_contour_point (Polygon, n);
+      next = next_contour_point (Polygon, n);
+      p = &Polygon->Points[n];
+
+      line.Point1 = Polygon->Points[prev];
+      line.Point2 = Polygon->Points[next];
       line.Thickness = 0;
-      if (IsPointOnLine ((float) pt2->X, (float) pt2->Y, 0.0, &line))
+      if (IsPointOnLine ((float) p->X, (float) p->Y, 0.0, &line))
         {
-          RemoveObject (POLYGONPOINT_TYPE, (void *) Layer, (void *) Polygon,
-                        (void *) pt2);
+          RemoveObject (POLYGONPOINT_TYPE, Layer, Polygon, p);
           changed = true;
         }
     }
@@ -1119,8 +1197,7 @@ GetLowestDistancePolygonPoint (PolygonTypePtr Polygon, LocationType X,
                                LocationType Y)
 {
   double mindistance = (double) MAX_COORD * MAX_COORD;
-  PointTypePtr ptr1 = &Polygon->Points[Polygon->PointN - 1],
-    ptr2 = &Polygon->Points[0];
+  PointTypePtr ptr1, ptr2;
   Cardinal n, result = 0;
 
   /* we calculate the distance to each segment and choose the
@@ -1130,9 +1207,12 @@ GetLowestDistancePolygonPoint (PolygonTypePtr Polygon, LocationType X,
    * to the segment end point.
    */
 
-  for (n = 0; n < Polygon->PointN; n++, ptr2++)
+  for (n = 0; n < Polygon->PointN; n++)
     {
       register double u, dx, dy;
+      ptr1 = &Polygon->Points[prev_contour_point (Polygon, n)];
+      ptr2 = &Polygon->Points[n];
+
       dx = ptr2->X - ptr1->X;
       dy = ptr2->Y - ptr1->Y;
       if (dx != 0.0 || dy != 0.0)
@@ -1159,7 +1239,6 @@ GetLowestDistancePolygonPoint (PolygonTypePtr Polygon, LocationType X,
               result = n;
             }
         }
-      ptr1 = ptr2;
     }
   return (result);
 }
@@ -1729,11 +1808,19 @@ debug_polyarea (POLYAREA *p)
 void
 debug_polygon (PolygonType *p)
 {
-  int i;
+  Cardinal i;
   POLYAREA *pa;
   fprintf (stderr, "POLYGON %p  %d pts\n", p, p->PointN);
   for (i=0; i<p->PointN; i++)
     fprintf(stderr, "\t%d: %d, %d\n", i, p->Points[i].X, p->Points[i].Y);
+  if (p->HoleIndexN)
+    {
+      fprintf (stderr, "%d holes, starting at indices\n", p->HoleIndexN);
+      for (i=0; i<p->HoleIndexN; i++)
+        fprintf(stderr, "\t%d: %d\n", i, p->HoleIndex[i]);
+    }
+  else
+    fprintf (stderr, "it has no holes\n");
   pa = p->Clipped;
   while (pa)
     {
diff --git a/src/polygon.h b/src/polygon.h
index 1eb5757..a93d965 100644
--- a/src/polygon.h
+++ b/src/polygon.h
@@ -33,6 +33,10 @@
 
 #include "global.h"
 
+Cardinal polygon_point_idx (PolygonTypePtr polygon, PointTypePtr point);
+Cardinal polygon_point_contour (PolygonTypePtr polygon, Cardinal point);
+Cardinal prev_contour_point (PolygonTypePtr polygon, Cardinal point);
+Cardinal next_contour_point (PolygonTypePtr polygon, Cardinal point);
 Cardinal GetLowestDistancePolygonPoint (PolygonTypePtr,
 					LocationType, LocationType);
 bool RemoveExcessPolygonPoints (LayerTypePtr, PolygonTypePtr);
diff --git a/src/remove.c b/src/remove.c
index a537bc0..98b9ade 100644
--- a/src/remove.c
+++ b/src/remove.c
@@ -74,6 +74,7 @@ static void *DestroyElement (ElementTypePtr);
 static void *RemoveVia (PinTypePtr);
 static void *RemoveRat (RatTypePtr);
 static void *DestroyPolygonPoint (LayerTypePtr, PolygonTypePtr, PointTypePtr);
+static void *RemovePolygonContour (LayerTypePtr, PolygonTypePtr, Cardinal);
 static void *RemovePolygonPoint (LayerTypePtr, PolygonTypePtr, PointTypePtr);
 static void *RemoveLinePoint (LayerTypePtr, LineTypePtr, PointTypePtr);
 
@@ -203,17 +204,33 @@ static void *
 DestroyPolygonPoint (LayerTypePtr Layer,
 		     PolygonTypePtr Polygon, PointTypePtr Point)
 {
-  PointTypePtr ptr;
+  Cardinal point_idx;
+  Cardinal i;
+  Cardinal contour;
+  Cardinal contour_start, contour_end, contour_points;
+
+  point_idx = polygon_point_idx (Polygon, Point);
+  contour = polygon_point_contour (Polygon, point_idx);
+  contour_start = (contour == 0) ? 0 : Polygon->HoleIndex[contour - 1];
+  contour_end = (contour == Polygon->HoleIndexN) ? Polygon->PointN :
+                                                   Polygon->HoleIndex[contour];
+  contour_points = contour_end - contour_start;
+
+  if (contour_points <= 3)
+    return RemovePolygonContour (Layer, Polygon, contour);
 
-  if (Polygon->PointN <= 3)
-    return RemovePolygon (Layer, Polygon);
   r_delete_entry (Layer->polygon_tree, (BoxType *) Polygon);
-  for (ptr = Point + 1; ptr != &Polygon->Points[Polygon->PointN]; ptr++)
-    {
-      *Point = *ptr;
-      Point = ptr;
-    }
+
+  /* remove point from list, keep point order */
+  for (i = point_idx; i < Polygon->PointN - 1; i++)
+    Polygon->Points[i] = Polygon->Points[i + 1];
   Polygon->PointN--;
+
+  /* Shift down indices of any holes */
+  for (i = 0; i < Polygon->HoleIndexN; i++)
+    if (Polygon->HoleIndex[i] > point_idx)
+      Polygon->HoleIndex[i]--;
+
   SetPolygonBoundingBox (Polygon);
   r_insert_entry (Layer->polygon_tree, (BoxType *) Polygon, 0);
   InitClip (PCB->Data, Layer, Polygon);
@@ -481,41 +498,100 @@ RemovePolygon (LayerTypePtr Layer, PolygonTypePtr Polygon)
 }
 
 /* ---------------------------------------------------------------------------
+ * removes a contour from a polygon.
+ * If removing the outer contour, it removes the whole polygon.
+ */
+static void *
+RemovePolygonContour (LayerTypePtr Layer,
+                      PolygonTypePtr Polygon,
+                      Cardinal contour)
+{
+  Cardinal contour_start, contour_end, contour_points;
+  Cardinal i;
+
+  if (contour == 0)
+    return RemovePolygon (Layer, Polygon);
+
+  if (Layer->On)
+    {
+      ErasePolygon (Polygon);
+      if (!Bulk)
+        Draw ();
+    }
+
+  /* Copy the polygon to the undo list */
+  AddObjectToRemoveContourUndoList (POLYGON_TYPE, Layer, Polygon);
+
+  contour_start = (contour == 0) ? 0 : Polygon->HoleIndex[contour - 1];
+  contour_end = (contour == Polygon->HoleIndexN) ? Polygon->PointN :
+                                                   Polygon->HoleIndex[contour];
+  contour_points = contour_end - contour_start;
+
+  /* remove points from list, keep point order */
+  for (i = contour_start; i < Polygon->PointN - contour_points; i++)
+    Polygon->Points[i] = Polygon->Points[i + contour_points];
+  Polygon->PointN -= contour_points;
+
+  /* remove hole from list and shift down remaining indices */
+  for (i = contour; i < Polygon->HoleIndexN; i++)
+    Polygon->HoleIndex[i - 1] = Polygon->HoleIndex[i] - contour_points;
+  Polygon->HoleIndexN--;
+
+  InitClip (PCB->Data, Layer, Polygon);
+  /* redraw polygon if necessary */
+  if (Layer->On)
+    {
+      DrawPolygon (Layer, Polygon, 0);
+      if (!Bulk)
+        Draw ();
+    }
+  return NULL;
+}
+
+/* ---------------------------------------------------------------------------
  * removes a polygon-point from a polygon
  */
 static void *
 RemovePolygonPoint (LayerTypePtr Layer,
 		    PolygonTypePtr Polygon, PointTypePtr Point)
 {
-  PointTypePtr ptr;
-  Cardinal index = 0;
-  if (Polygon->PointN <= 3)
-    return RemovePolygon (Layer, Polygon);
+  Cardinal point_idx;
+  Cardinal i;
+  Cardinal contour;
+  Cardinal contour_start, contour_end, contour_points;
+
+  point_idx = polygon_point_idx (Polygon, Point);
+  contour = polygon_point_contour (Polygon, point_idx);
+  contour_start = (contour == 0) ? 0 : Polygon->HoleIndex[contour - 1];
+  contour_end = (contour == Polygon->HoleIndexN) ? Polygon->PointN :
+                                                   Polygon->HoleIndex[contour];
+  contour_points = contour_end - contour_start;
+
+  if (contour_points <= 3)
+    return RemovePolygonContour (Layer, Polygon, contour);
+
   if (Layer->On)
     ErasePolygon (Polygon);
+
   /* insert the polygon-point into the undo list */
-  POLYGONPOINT_LOOP (Polygon);
-  {
-    if (point == Point)
-      {
-	index = n;
-	break;
-      }
-  }
-  END_LOOP;
-  AddObjectToRemovePointUndoList (POLYGONPOINT_TYPE, Layer, Polygon, index);
+  AddObjectToRemovePointUndoList (POLYGONPOINT_TYPE, Layer, Polygon, point_idx);
   r_delete_entry (Layer->polygon_tree, (BoxType *) Polygon);
+
   /* remove point from list, keep point order */
-  for (ptr = Point + 1; ptr != &Polygon->Points[Polygon->PointN]; ptr++)
-    {
-      *Point = *ptr;
-      Point = ptr;
-    }
+  for (i = point_idx; i < Polygon->PointN - 1; i++)
+    Polygon->Points[i] = Polygon->Points[i + 1];
   Polygon->PointN--;
+
+  /* Shift down indices of any holes */
+  for (i = 0; i < Polygon->HoleIndexN; i++)
+    if (Polygon->HoleIndex[i] > point_idx)
+      Polygon->HoleIndex[i]--;
+
   SetPolygonBoundingBox (Polygon);
   r_insert_entry (Layer->polygon_tree, (BoxType *) Polygon, 0);
   RemoveExcessPolygonPoints (Layer, Polygon);
   InitClip (PCB->Data, Layer, Polygon);
+
   /* redraw polygon if necessary */
   if (Layer->On)
     {
diff --git a/src/report.c b/src/report.c
index 6a1c47b..dc01153 100644
--- a/src/report.c
+++ b/src/report.c
@@ -325,12 +325,13 @@ ReportDialog (int argc, char **argv, int x, int y)
 		 "Its bounding box is (%d,%d) (%d,%d)\n"
 		 "It has %d points and could store %d more\n"
 		 "without using more memory.\n"
-		 "It resides on layer %d\n"
+		 "It has %d holes and resides on layer %d\n"
 		 "%s", Polygon->ID,
 		 flags_to_string (Polygon->Flags, POLYGON_TYPE),
 		 Polygon->BoundingBox.X1, Polygon->BoundingBox.Y1,
 		 Polygon->BoundingBox.X2, Polygon->BoundingBox.Y2,
 		 Polygon->PointN, Polygon->PointMax - Polygon->PointN,
+		 Polygon->HoleIndexN,
 		 GetLayerNumber (PCB->Data, (LayerTypePtr) ptr1),
 		 TEST_FLAG (LOCKFLAG, Polygon) ? "It is LOCKED\n" : "");
 	break;
diff --git a/src/undo.c b/src/undo.c
index 50c6de5..68ea8ff 100644
--- a/src/undo.c
+++ b/src/undo.c
@@ -97,6 +97,7 @@ typedef struct			/* information about removed polygon points */
   LocationType X, Y;		/* data */
   int ID;
   Cardinal Index;		/* index in a polygons array of points */
+  bool last_in_contour;		/* Whether the point was the last in its contour */
 }
 RemovedPointType, *RemovedPointTypePtr;
 
@@ -153,6 +154,7 @@ typedef struct			/* holds information about an operation */
     LayerChangeType LayerChange;
     ClearPolyType ClearPoly;
     NetlistChangeType NetlistChange;
+    long int CopyID;
   }
   Data;
 }
@@ -184,6 +186,8 @@ static bool UndoMove (UndoListTypePtr);
 static bool UndoRemove (UndoListTypePtr);
 static bool UndoRemovePoint (UndoListTypePtr);
 static bool UndoInsertPoint (UndoListTypePtr);
+static bool UndoRemoveContour (UndoListTypePtr);
+static bool UndoInsertContour (UndoListTypePtr);
 static bool UndoMoveToLayer (UndoListTypePtr);
 static bool UndoFlag (UndoListTypePtr);
 static bool UndoMirror (UndoListTypePtr);
@@ -731,7 +735,9 @@ UndoRemovePoint (UndoListTypePtr Entry)
 	InsertPointIntoObject (POLYGON_TYPE, layer, polygon,
 			       &Entry->Data.RemovedPoint.Index,
 			       Entry->Data.RemovedPoint.X,
-			       Entry->Data.RemovedPoint.Y, true);
+			       Entry->Data.RemovedPoint.Y, true,
+			       Entry->Data.RemovedPoint.last_in_contour);
+
 	polygon->Points[Entry->Data.RemovedPoint.Index].ID =
 	  Entry->Data.RemovedPoint.ID;
 	if (andDraw && layer->On)
@@ -758,6 +764,9 @@ UndoInsertPoint (UndoListTypePtr Entry)
   PolygonTypePtr polygon;
   PointTypePtr pnt;
   int type;
+  Cardinal point_idx;
+  Cardinal hole;
+  bool last_in_contour = false;
 
   assert (Entry->Kind == POLYGONPOINT_TYPE);
   /* lookup entry by it's ID */
@@ -772,21 +781,26 @@ UndoInsertPoint (UndoListTypePtr Entry)
 	  return (false);
 	if (andDraw && layer->On)
 	  ErasePolygon (polygon);
+
+	/* Check whether this point was at the end of its contour.
+	 * If so, we need to flag as such when re-adding the point
+	 * so it goes back in the correct place
+	 */
+	point_idx = polygon_point_idx (polygon, pnt);
+	for (hole = 0; hole < polygon->HoleIndexN; hole++)
+	  if (point_idx == polygon->HoleIndex[hole] - 1)
+	    last_in_contour = true;
+	if (point_idx == polygon->PointN - 1)
+	  last_in_contour = true;
+	Entry->Data.RemovedPoint.last_in_contour = last_in_contour;
+
 	Entry->Data.RemovedPoint.X = pnt->X;
 	Entry->Data.RemovedPoint.Y = pnt->Y;
 	Entry->Data.RemovedPoint.ID = pnt->ID;
 	Entry->ID = polygon->ID;
 	Entry->Kind = POLYGON_TYPE;
 	Entry->Type = UNDO_REMOVE_POINT;
-	POLYGONPOINT_LOOP (polygon);
-	{
-	  if (pnt == point)
-	    {
-	      Entry->Data.RemovedPoint.Index = n;
-	      break;
-	    }
-	}
-	END_LOOP;
+	Entry->Data.RemovedPoint.Index = point_idx;
 	DestroyObject (PCB->Data, POLYGONPOINT_TYPE, layer, polygon, pnt);
 	if (andDraw && layer->On)
 	  DrawPolygon (layer, polygon, 0);
@@ -798,6 +812,68 @@ UndoInsertPoint (UndoListTypePtr Entry)
     }
 }
 
+static bool
+UndoSwapCopiedObject (UndoListTypePtr Entry)
+{
+  void *ptr1, *ptr2, *ptr3;
+  void *ptr1b, *ptr2b, *ptr3b;
+  AnyObjectType *obj, *obj2;
+  int type;
+  long int swap_id;
+
+  /* lookup entry by it's ID */
+  type =
+    SearchObjectByID (RemoveList, &ptr1, &ptr2, &ptr3, Entry->Data.CopyID,
+		      Entry->Kind);
+  if (type == NO_TYPE)
+    return false;
+
+  type =
+    SearchObjectByID (PCB->Data, &ptr1b, &ptr2b, &ptr3b, Entry->ID,
+		      Entry->Kind);
+  if (type == NO_TYPE)
+    return FALSE;
+
+  obj = ptr2;
+  obj2 = ptr2b;
+
+  swap_id = obj->ID;
+  obj->ID = obj2->ID;
+  obj2->ID = swap_id;
+
+  MoveObjectToBuffer (RemoveList, PCB->Data, type, ptr1b, ptr2b, ptr3b);
+
+  if (andDraw)
+    DrawRecoveredObject (Entry->Kind, ptr1, ptr2, ptr3);
+
+  obj = MoveObjectToBuffer (PCB->Data, RemoveList, type, ptr1, ptr2, ptr3);
+  if (Entry->Kind == POLYGON_TYPE)
+    InitClip (PCB->Data, ptr1b, (PolygonType *)obj);
+  return (true);
+}
+
+/* ---------------------------------------------------------------------------
+ * recovers an removed polygon point
+ * returns true on success
+ */
+static bool
+UndoRemoveContour (UndoListTypePtr Entry)
+{
+  assert (Entry->Kind == POLYGON_TYPE);
+  return UndoSwapCopiedObject (Entry);
+}
+
+/* ---------------------------------------------------------------------------
+ * recovers an inserted polygon point
+ * returns true on success
+ */
+static bool
+UndoInsertContour (UndoListTypePtr Entry)
+{
+  assert (Entry->Kind == POLYGON_TYPE);
+  return UndoSwapCopiedObject (Entry);
+}
+
 /* ---------------------------------------------------------------------------
  * undo a layer change
  * returns true on success
@@ -969,6 +1045,16 @@ PerformUndo (UndoListTypePtr ptr)
 	return (UNDO_INSERT_POINT);
       break;
 
+    case UNDO_REMOVE_CONTOUR:
+      if (UndoRemoveContour (ptr))
+	return (UNDO_REMOVE_CONTOUR);
+      break;
+
+    case UNDO_INSERT_CONTOUR:
+      if (UndoInsertContour (ptr))
+	return (UNDO_INSERT_CONTOUR);
+      break;
+
     case UNDO_ROTATE:
       if (UndoRotate (ptr))
 	return (UNDO_ROTATE);
@@ -1223,6 +1309,8 @@ AddObjectToRemovePointUndoList (int Type,
 {
   UndoListTypePtr undo;
   PolygonTypePtr polygon = (PolygonTypePtr) Ptr2;
+  Cardinal hole;
+  bool last_in_contour = false;
 
   if (!Locked)
     {
@@ -1240,6 +1328,17 @@ AddObjectToRemovePointUndoList (int Type,
 	    undo->Data.RemovedPoint.Y = polygon->Points[index].Y;
 	    undo->Data.RemovedPoint.ID = polygon->Points[index].ID;
 	    undo->Data.RemovedPoint.Index = index;
+
+	    /* Check whether this point was at the end of its contour.
+	     * If so, we need to flag as such when re-adding the point
+	     * so it goes back in the correct place
+	     */
+	    for (hole = 0; hole < polygon->HoleIndexN; hole++)
+	      if (index == polygon->HoleIndex[hole] - 1)
+		last_in_contour = true;
+	    if (index == polygon->PointN - 1)
+	      last_in_contour = true;
+	    undo->Data.RemovedPoint.last_in_contour = last_in_contour;
 	  }
 	  break;
 	}
@@ -1258,6 +1357,45 @@ AddObjectToInsertPointUndoList (int Type, void *Ptr1, void *Ptr2, void *Ptr3)
     undo = GetUndoSlot (UNDO_INSERT_POINT, OBJECT_ID (Ptr3), Type);
 }
 
+static void
+CopyObjectToUndoList (int undo_type, int Type, void *Ptr1, void *Ptr2, void *Ptr3)
+{
+  UndoListTypePtr undo;
+  AnyObjectType *copy;
+
+  if (Locked)
+    return;
+
+  if (!RemoveList)
+    RemoveList = CreateNewBuffer ();
+
+  undo = GetUndoSlot (undo_type, OBJECT_ID (Ptr2), Type);
+  copy = CopyObjectToBuffer (RemoveList, PCB->Data, Type, Ptr1, Ptr2, Ptr3);
+  undo->Data.CopyID = copy->ID;
+}
+
+/* ---------------------------------------------------------------------------
+ * adds an object to the list of removed contours
+ * (Actually just takes a copy of the whole polygon to restore)
+ */
+void
+AddObjectToRemoveContourUndoList (int Type,
+				  LayerType *Layer, PolygonType *Polygon)
+{
+  CopyObjectToUndoList (UNDO_REMOVE_CONTOUR, Type, Layer, Polygon, NULL);
+}
+
+/* ---------------------------------------------------------------------------
+ * adds an object to the list of insert contours
+ * (Actually just takes a copy of the whole polygon to restore)
+ */
+void
+AddObjectToInsertContourUndoList (int Type,
+				  LayerType *Layer, PolygonType *Polygon)
+{
+  CopyObjectToUndoList (UNDO_INSERT_CONTOUR, Type, Layer, Polygon, NULL);
+}
+
 /* ---------------------------------------------------------------------------
  * adds an object to the list of moved objects
  */
diff --git a/src/undo.h b/src/undo.h
index c7a0288..0a4601f 100644
--- a/src/undo.h
+++ b/src/undo.h
@@ -47,6 +47,8 @@ void ClearUndoList (bool);
 void MoveObjectToRemoveUndoList (int, void *, void *, void *);
 void AddObjectToRemovePointUndoList (int, void *, void *, Cardinal);
 void AddObjectToInsertPointUndoList (int, void *, void *, void *);
+void AddObjectToRemoveContourUndoList (int, LayerType *, PolygonType *);
+void AddObjectToInsertContourUndoList (int, LayerType *, PolygonType *);
 void AddObjectToMoveUndoList (int, void *, void *, void *,
 			      LocationType, LocationType);
 void AddObjectToChangeNameUndoList (int, void *, void *, void *, char *);




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