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

gEDA-user: Reverting in PCB



Hi all,


I've implemented a File->Revert function in PCB. I use it if some script or me changes the PCB file I'm working on. This is good, when you delete some element in gschem, you run gsch2pcb, and then select File->Revert in PCB, and your element is deleted from the board too. You don't have to restart pcb....

Here I copy the modified files. Sorry for the large mail. If you find it usefull, please commit to CVS.

I'm planning to add a "PCB" button to gschem to run gsch2pcb from there.

Levente

-- 

E-Mail: lekovacs@xxxxxxxxxxxx
AIM: ha5ogl
ICQ: 48710903
MSN: ha5ogl@xxxxxxxxxxx
Yahoo!: kieg_tk16
Home Page: http://web.interware.hu/lekovacs
Public key: http://web.interware.hu/lekovacs/cuccok/public_key

________________________________________

/"\
\ /    ASCII Ribbon Campaign
 X   against HTML email & vCards
/ \     http://arc.pasp.de/

Have Fun, & Linux! 73 for all by HA5OGL.
This message was generated by Sylpheed.

/* $Id: action.c,v 1.61 2006/03/03 21:33:44 danmc Exp $ */

/*
 *                            COPYRIGHT
 *
 *  PCB, interactive printed circuit board design
 *  Copyright (C) 1994,1995,1996 Thomas Nau
 *  Copyright (C) 1997, 1998, 1999, 2000, 2001 Harry Eaton
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *  Contact addresses for paper mail and Email:
 *  Harry Eaton, 6697 Buttonhole Ct, Columbia, MD 21044, USA
 *  haceaton@xxxxxxxxxxxxxxxxxx
 *
 */

/* action routines for output window
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "global.h"

#include "action.h"
#include "autoplace.h"
#include "autoroute.h"
#include "buffer.h"
#include "change.h"
#include "command.h"
#include "copy.h"
#include "create.h"
#include "crosshair.h"
#include "data.h"
#include "draw.h"
#include "error.h"
#include "file.h"
#include "find.h"
#include "insert.h"
#include "line.h"
#include "mymem.h"
#include "misc.h"
#include "move.h"
#include "output.h"
#include "polygon.h"
#include "print.h"
#include "rats.h"
#include "remove.h"
#include "report.h"
#include "rotate.h"
#include "rubberband.h"
#include "search.h"
#include "select.h"
#include "set.h"
#include "undo.h"

#include "gui.h"


RCSID("$Id: action.c,v 1.61 2006/03/03 21:33:44 danmc Exp $");

/* ---------------------------------------------------------------------------
 * some local types
 */
typedef enum
{
  F_AddSelected,
  F_All,
  F_AllConnections,
  F_AllRats,
  F_AllUnusedPins,
  F_Arc,
  F_Arrow,
  F_Block,
  F_Description,
  F_Center,
  F_Clear,
  F_ClearAndRedraw,
  F_ClearList,
  F_Close,
  F_Connection,
  F_Convert,
  F_Copy,
  F_CycleClip,
  F_DeleteRats,
  F_Drag,
  F_DrillReport,
  F_Element,
  F_ElementByName,
  F_ElementConnections,
  F_ElementToBuffer,
  F_Find,
  F_FlipElement,
  F_FoundPins,
  F_Grid,
  F_InsertPoint,
  F_Layer,
  F_Layout,
  F_LayoutAs,
  F_LayoutToBuffer,
  F_Line,
  F_LineSize,
  F_Mirror,
  F_Move,
  F_NameOnPCB,
  F_Netlist,
  F_None,
  F_Notify,
  F_Object,
  F_ObjectByName,
  F_PasteBuffer,
  F_PadByName,
  F_PinByName,
  F_PinOrPadName,
  F_Pinout,
  F_Polygon,
  F_PreviousPoint,
  F_RatsNest,
  F_Rectangle,
  F_Redraw,
  F_Release,
  F_Remove,
  F_RemoveSelected,
  F_Report,
  F_Reset,
  F_ResetLinesAndPolygons,
  F_ResetPinsViasAndPads,
  F_Restore,
  F_Revert,
  F_Rotate,
  F_Save,
  F_Scroll,
  F_Selected,
  F_SelectedArcs,
  F_SelectedElements,
  F_SelectedLines,
  F_SelectedNames,
  F_SelectedObjects,
  F_SelectedPads,
  F_SelectedPins,
  F_SelectedTexts,
  F_SelectedVias,
  F_SelectedRats,
  F_Stroke,
  F_Text,
  F_TextByName,
  F_TextScale,
  F_Thermal,
  F_ToggleAllDirections,
  F_ToggleAutoDRC,
  F_ToggleClearLine,
  F_ToggleGrid,
  F_ToggleMask,
  F_ToggleName,
  F_ToggleObject,
  F_ToggleShowDRC,
  F_ToggleLiveRoute,
  F_ToggleRubberBandMode,
  F_ToggleStartDirection,
  F_ToggleSnapPin,
  F_ToggleThindraw,
  F_ToggleOrthoMove,
  F_ToggleLocalRef,
  F_ToggleCheckPlanes,
  F_ToggleUniqueNames,
  F_Via,
  F_ViaByName,
  F_Value,
  F_ViaDrillingHole,
  F_ViaSize,
  F_Zoom
}
FunctionID;

typedef struct			/* used to identify subfunctions */
{
  char *Identifier;
  FunctionID ID;
}
FunctionType, *FunctionTypePtr;

/* ---------------------------------------------------------------------------
 * some local identifiers
 */
static PointType InsertedPoint;
static LayerTypePtr lastLayer;
static struct
{
  PolygonTypePtr poly;
  LineType line;
}
fake;

static struct
{
  int X;
  int Y;
  Cardinal Buffer;
  Boolean Click;
  Boolean Moving;		/* selected type clicked on */
  int Hit;			/* move type clicked on */
  void *ptr1;
  void *ptr2;
  void *ptr3;
}
Note;

static Cardinal polyIndex = 0;
static Boolean IgnoreMotionEvents = False;
static Boolean saved_mode = False;
#ifdef HAVE_LIBSTROKE
static Boolean mid_stroke = False;
static BoxType StrokeBox;
#endif
static FunctionType Functions[] = {
  {"AddSelected", F_AddSelected},
  {"All", F_All},
  {"AllConnections", F_AllConnections},
  {"AllRats", F_AllRats},
  {"AllUnusedPins", F_AllUnusedPins},
  {"Arc", F_Arc},
  {"Arrow", F_Arrow},
  {"Block", F_Block},
  {"Description", F_Description},
  {"Center", F_Center},
  {"Clear", F_Clear},
  {"ClearAndRedraw", F_ClearAndRedraw},
  {"ClearList", F_ClearList},
  {"Close", F_Close},
  {"Connection", F_Connection},
  {"Convert", F_Convert},
  {"Copy", F_Copy},
  {"CycleClip", F_CycleClip},
  {"DeleteRats", F_DeleteRats},
  {"Drag", F_Drag},
  {"DrillReport", F_DrillReport},
  {"Element", F_Element},
  {"ElementByName", F_ElementByName},
  {"ElementConnections", F_ElementConnections},
  {"ElementToBuffer", F_ElementToBuffer},
  {"Find", F_Find},
  {"FlipElement", F_FlipElement},
  {"FoundPins", F_FoundPins},
  {"Grid", F_Grid},
  {"InsertPoint", F_InsertPoint},
  {"Layer", F_Layer},
  {"Layout", F_Layout},
  {"LayoutAs", F_LayoutAs},
  {"LayoutToBuffer", F_LayoutToBuffer},
  {"Line", F_Line},
  {"LineSize", F_LineSize},
  {"Mirror", F_Mirror},
  {"Move", F_Move},
  {"NameOnPCB", F_NameOnPCB},
  {"Netlist", F_Netlist},
  {"None", F_None},
  {"Notify", F_Notify},
  {"Object", F_Object},
  {"ObjectByName", F_ObjectByName},
  {"PasteBuffer", F_PasteBuffer},
  {"PadByName", F_PadByName},
  {"PinByName", F_PinByName},
  {"PinOrPadName", F_PinOrPadName},
  {"Pinout", F_Pinout},
  {"Polygon", F_Polygon},
  {"PreviousPoint", F_PreviousPoint},
  {"RatsNest", F_RatsNest},
  {"Rectangle", F_Rectangle},
  {"Redraw", F_Redraw},
  {"Release", F_Release},
  {"Remove", F_Remove},
  {"RemoveSelected", F_RemoveSelected},
  {"Report", F_Report},
  {"Reset", F_Reset},
  {"ResetLinesAndPolygons", F_ResetLinesAndPolygons},
  {"ResetPinsViasAndPads", F_ResetPinsViasAndPads},
  {"Restore", F_Restore},
  {"Revert", F_Revert},
  {"Rotate", F_Rotate},
  {"Save", F_Save},
  {"Scroll", F_Scroll},
  {"Selected", F_Selected},
  {"SelectedArcs", F_SelectedArcs},
  {"SelectedElements", F_SelectedElements},
  {"SelectedLines", F_SelectedLines},
  {"SelectedNames", F_SelectedNames},
  {"SelectedObjects", F_SelectedObjects},
  {"SelectedPins", F_SelectedPins},
  {"SelectedPads", F_SelectedPads},
  {"SelectedRats", F_SelectedRats},
  {"SelectedTexts", F_SelectedTexts},
  {"SelectedVias", F_SelectedVias},
  {"Stroke", F_Stroke},
  {"Text", F_Text},
  {"TextByName", F_TextByName},
  {"TextScale", F_TextScale},
  {"Thermal", F_Thermal},
  {"Toggle45Degree", F_ToggleAllDirections},
  {"ToggleClearLine", F_ToggleClearLine},
  {"ToggleGrid", F_ToggleGrid},
  {"ToggleMask", F_ToggleMask},
  {"ToggleName", F_ToggleName},
  {"ToggleObject", F_ToggleObject},
  {"ToggleRubberBandMode", F_ToggleRubberBandMode},
  {"ToggleStartDirection", F_ToggleStartDirection},
  {"ToggleSnapPin", F_ToggleSnapPin},
  {"ToggleThindraw", F_ToggleThindraw},
  {"ToggleCheckPlanes", F_ToggleCheckPlanes},
  {"ToggleLocalRef", F_ToggleLocalRef},
  {"ToggleOrthoMove", F_ToggleOrthoMove},
  {"ToggleShowDRC", F_ToggleShowDRC},
  {"ToggleLiveRoute", F_ToggleLiveRoute},
  {"ToggleAutoDRC", F_ToggleAutoDRC},
  {"ToggleUniqueNames", F_ToggleUniqueNames},
  {"Value", F_Value},
  {"Via", F_Via},
  {"ViaByName", F_ViaByName},
  {"ViaSize", F_ViaSize},
  {"ViaDrillingHole", F_ViaDrillingHole},
  {"Zoom", F_Zoom}
};

/* ---------------------------------------------------------------------------
 * some local routines
 */
static void WarpPointer (Boolean);
static int GetFunctionID (String);
static void AdjustAttachedBox (void);
static void NotifyLine (void);
static void NotifyBlock (void);
static void NotifyMode (void);
static void ClearWarnings (void);
#ifdef HAVE_LIBSTROKE
static void FinishStroke (void);
extern void stroke_init (void);
extern void stroke_record (int x, int y);
extern int stroke_trans (char *s);
#endif
static void ChangeFlag (char *, char *, int, char *);

#ifdef HAVE_LIBSTROKE

/* ---------------------------------------------------------------------------
 * FinishStroke - try to recognize the stroke sent
 */
void
FinishStroke (void)
{
  char msg[255];
  int type;
  unsigned long num;
  void *ptr1, *ptr2, *ptr3;

  mid_stroke = False;
  if (stroke_trans (msg))
    {
      num = atoi (msg);
      switch (num)
	{
	case 456:
	  if (Settings.Mode == LINE_MODE)
	    {
	      SetMode (LINE_MODE);
	    }
	  break;
	case 9874123:
	case 74123:
	case 987412:
	case 8741236:
	case 874123:
	  RotateScreenObject (StrokeBox.X1, StrokeBox.Y1, SWAP_IDENT ? 1 : 3);
	  break;
	case 7896321:
	case 786321:
	case 789632:
	case 896321:
	  RotateScreenObject (StrokeBox.X1, StrokeBox.Y1, SWAP_IDENT ? 3 : 1);
	  break;
	case 258:
	  SetMode (LINE_MODE);
	  break;
	case 852:
	  SetMode (ARROW_MODE);
	  break;
	case 1478963:
	  ActionUndo ("");
	  break;
	case 147423:
	case 147523:
	case 1474123:
	  Redo (True);
	  break;
	case 148963:
	case 147863:
	case 147853:
	case 145863:
	  SetMode (VIA_MODE);
	  break;
	case 951:
	case 9651:
	case 9521:
	case 9621:
	case 9851:
	case 9541:
	case 96521:
	case 96541:
	case 98541:
	  SetZoom (1000);	/* special zoom extents */
	  break;
	case 159:
	case 1269:
	case 1259:
	case 1459:
	case 1569:
	case 1589:
	case 12569:
	case 12589:
	case 14589:
	  {
	    LocationType x = (StrokeBox.X1 + StrokeBox.X2) / 2;
	    LocationType y = (StrokeBox.Y1 + StrokeBox.Y2) / 2;
	    int z;
	    z =
	      1 +
	      log (fabs (StrokeBox.X2 - StrokeBox.X1) / Output.Width) /
	      log (2.0);
	    z =
	      MAX (z,
		   1 +
		   log (fabs (StrokeBox.Y2 - StrokeBox.Y1) / Output.Height) /
		   log (2.0));
	    SetZoom (z);

	    CenterDisplay (x, y, False);
	    break;
	  }

	default:
	  Message ("Unknown stroke %s\n", msg);
	  break;
	}
    }
  else
    gui_beep(Settings.Volume);
}
#endif

/* ---------------------------------------------------------------------------
 * Clear warning color from pins/pads
 */
static void
ClearWarnings ()
{
  Settings.RatWarn = False;
  ALLPIN_LOOP (PCB->Data);
  {
    if (TEST_FLAG (WARNFLAG, pin))
      {
	CLEAR_FLAG (WARNFLAG, pin);
	DrawPin (pin, 0);
      }
  }
  ENDALL_LOOP;
  ALLPAD_LOOP (PCB->Data);
  {
    if (TEST_FLAG (WARNFLAG, pad))
      {
	CLEAR_FLAG (WARNFLAG, pad);
	DrawPad (pad, 0);
      }
  }
  ENDALL_LOOP;
  UpdatePIPFlags (NULL, NULL, NULL, False);
  Draw ();
}

static gint
click_cb(void)
{
  if (Note.Click)
    {
      Note.Click = False;
      if (Note.Moving && !gui_shift_is_pressed())
	{
	  HideCrosshair (True);
	  Note.Buffer = Settings.BufferNumber;
	  SetBufferNumber (MAX_BUFFER - 1);
	  ClearBuffer (PASTEBUFFER);
	  AddSelectedToBuffer (PASTEBUFFER, Note.X, Note.Y, True);
	  SaveUndoSerialNumber ();
	  RemoveSelected ();
	  SaveMode ();
	  saved_mode = True;
	  SetMode (PASTEBUFFER_MODE);
	  RestoreCrosshair (True);
	}
      else if (Note.Hit && !gui_shift_is_pressed())
	{
	  HideCrosshair (True);
	  SaveMode ();
	  saved_mode = True;
	  SetMode (gui_control_is_pressed() ? COPY_MODE : MOVE_MODE);
	  Crosshair.AttachedObject.Ptr1 = Note.ptr1;
	  Crosshair.AttachedObject.Ptr2 = Note.ptr2;
	  Crosshair.AttachedObject.Ptr3 = Note.ptr3;
	  Crosshair.AttachedObject.Type = Note.Hit;
	  AttachForCopy (Note.X, Note.Y);
	  RestoreCrosshair (True);
	}
      else
	{
	  BoxType box;

	  Note.Hit = 0;
	  Note.Moving = False;
	  HideCrosshair (True);
	  SaveUndoSerialNumber ();
	  box.X1 = -MAX_COORD;
	  box.Y1 = -MAX_COORD;
	  box.X2 = MAX_COORD;
	  box.Y2 = MAX_COORD;
	  /* unselect first if shift key not down */
	  if (!gui_shift_is_pressed() && SelectBlock (&box, False))
	    SetChangedFlag (True);
	  NotifyBlock ();
	  Crosshair.AttachedBox.Point1.X = Note.X;
	  Crosshair.AttachedBox.Point1.Y = Note.Y;
	  RestoreCrosshair (True);
	}
    }
	return False;		/* stop timer */
}

static void
ReleaseMode (void)
{
  BoxType box;

  if (Note.Click)
    {
      BoxType box;

      box.X1 = -MAX_COORD;
      box.Y1 = -MAX_COORD;
      box.X2 = MAX_COORD;
      box.Y2 = MAX_COORD;

      Note.Click = False;	/* inhibit timer action */
      SaveUndoSerialNumber ();
      /* unselect first if shift key not down */
      if (!gui_shift_is_pressed())
	{
	  if (SelectBlock (&box, False))
	    SetChangedFlag (True);
	  if (Note.Moving)
	    {
	      Note.Moving = 0;
	      Note.Hit = 0;
	      return;
	    }
	}
      RestoreUndoSerialNumber ();
      if (SelectObject ())
	SetChangedFlag (True);
      Note.Hit = 0;
      Note.Moving = 0;
    }
  else if (Note.Moving)
    {
      RestoreUndoSerialNumber ();
      NotifyMode ();
      ClearBuffer (PASTEBUFFER);
      SetBufferNumber (Note.Buffer);
      Note.Moving = False;
      Note.Hit = 0;
    }
  else if (Note.Hit)
    {
      NotifyMode ();
      Note.Hit = 0;
    }
  else if (Settings.Mode == ARROW_MODE)
    {
      box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
		    Crosshair.AttachedBox.Point2.X);
      box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
		    Crosshair.AttachedBox.Point2.Y);
      box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
		    Crosshair.AttachedBox.Point2.X);
      box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
		    Crosshair.AttachedBox.Point2.Y);
      RestoreUndoSerialNumber ();
      if (SelectBlock (&box, True))
	SetChangedFlag (True);
      else if (Bumped)
	IncrementUndoSerialNumber ();
      Crosshair.AttachedBox.State = STATE_FIRST;
    }
  if (saved_mode)
    RestoreMode ();
  saved_mode = False;
}

/* ---------------------------------------------------------------------------
 * get function ID of passed string
 */
static int
GetFunctionID (String Ident)
{
  int i;

  i = ENTRIES (Functions);
  while (i)
    if (!strcasecmp (Ident, Functions[--i].Identifier))
      return ((int) Functions[i].ID);
  return (-1);
}

/* ---------------------------------------------------------------------------
 * set new coordinates if in 'RECTANGLE' mode
 * the cursor shape is also adjusted
 */
static void
AdjustAttachedBox (void)
{
  if (Settings.Mode == ARC_MODE)
    {
      Crosshair.AttachedBox.otherway = gui_shift_is_pressed();
      return;
    }
  switch (Crosshair.AttachedBox.State)
    {
    case STATE_SECOND:		/* one corner is selected */
      {
	/* update coordinates */
	Crosshair.AttachedBox.Point2.X = Crosshair.X;
	Crosshair.AttachedBox.Point2.Y = Crosshair.Y;

	/* set pointer shape depending on location relative
	 * to first corner
	 */
	gui_corner_cursor ();
	break;
      }

    default:
      gui_mode_cursor (RECTANGLE_MODE);
      break;
    }
}

/* ---------------------------------------------------------------------------
 * adjusts the objects which are to be created like attached lines...
 */
void
AdjustAttachedObjects (void)
{
  PointTypePtr pnt;
  switch (Settings.Mode)
    {
      /* update at least an attached block (selection) */
    case NO_MODE:
    case ARROW_MODE:
      if (Crosshair.AttachedBox.State)
	{
	  Crosshair.AttachedBox.Point2.X = Crosshair.X;
	  Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
	}
      break;

      /* rectangle creation mode */
    case RECTANGLE_MODE:
    case ARC_MODE:
      AdjustAttachedBox ();
      break;

      /* polygon creation mode */
    case POLYGON_MODE:
      AdjustAttachedLine ();
      break;
      /* line creation mode */
    case LINE_MODE:
      if (PCB->RatDraw || PCB->Clipping == 0)
	AdjustAttachedLine ();
      else
	AdjustTwoLine (PCB->Clipping - 1);
      break;
      /* point insertion mode */
    case INSERTPOINT_MODE:
      pnt = AdjustInsertPoint ();
      if (pnt)
	InsertedPoint = *pnt;
      break;
    case ROTATE_MODE:
      gui_mode_cursor (ROTATE_MODE);
      break;
    }
}

/* ---------------------------------------------------------------------------
 * creates points of a line
 */
static void
NotifyLine (void)
{
  int type = NO_TYPE;
  void *ptr1, *ptr2, *ptr3;

  if (!Marked.status || TEST_FLAG (LOCALREFFLAG, PCB))
    SetLocalRef (Crosshair.X, Crosshair.Y, True);
  switch (Crosshair.AttachedLine.State)
    {
    case STATE_FIRST:		/* first point */
      if (PCB->RatDraw && SearchScreen (Crosshair.X, Crosshair.Y,
					PAD_TYPE | PIN_TYPE, &ptr1, &ptr1,
					&ptr1) == NO_TYPE)
	{
	  gui_beep(Settings.Volume);
	  break;
	}
      if (TEST_FLAG (AUTODRCFLAG, PCB) && Settings.Mode == LINE_MODE)
	{
	  type = SearchScreen (Crosshair.X, Crosshair.Y,
			       PIN_TYPE | PAD_TYPE | VIA_TYPE, &ptr1, &ptr2,
			       &ptr3);
	  LookupConnection (Crosshair.X, Crosshair.Y, True, TO_PCB (1));
	}
      if (type == PIN_TYPE || type == VIA_TYPE)
	{
	  Crosshair.AttachedLine.Point1.X =
	    Crosshair.AttachedLine.Point2.X = ((PinTypePtr) ptr2)->X;
	  Crosshair.AttachedLine.Point1.Y =
	    Crosshair.AttachedLine.Point2.Y = ((PinTypePtr) ptr2)->Y;
	}
      else if (type == PAD_TYPE)
	{
	  PadTypePtr pad = (PadTypePtr) ptr2;
	  float d1, d2;
	  d1 = SQUARE (Crosshair.X - pad->Point1.X) +
	    SQUARE (Crosshair.Y - pad->Point1.Y);
	  d2 = SQUARE (Crosshair.X - pad->Point2.X) +
	    SQUARE (Crosshair.Y - pad->Point2.Y);
	  if (d2 < d1)
	    {
	      Crosshair.AttachedLine.Point1 =
		Crosshair.AttachedLine.Point2 = pad->Point2;
	    }
	  else
	    {
	      Crosshair.AttachedLine.Point1 =
		Crosshair.AttachedLine.Point2 = pad->Point1;
	    }
	}
      else
	{
	  Crosshair.AttachedLine.Point1.X =
	    Crosshair.AttachedLine.Point2.X = Crosshair.X;
	  Crosshair.AttachedLine.Point1.Y =
	    Crosshair.AttachedLine.Point2.Y = Crosshair.Y;
	}
      Crosshair.AttachedLine.State = STATE_SECOND;
      break;

    case STATE_SECOND:
      /* fall through to third state too */
      lastLayer = CURRENT;
    default:			/* all following points */
      Crosshair.AttachedLine.State = STATE_THIRD;
      break;
    }
}

/* ---------------------------------------------------------------------------
 * create first or second corner of a marked block
 */
static void
NotifyBlock (void)
{
  HideCrosshair (True);
  switch (Crosshair.AttachedBox.State)
    {
    case STATE_FIRST:		/* setup first point */
      Crosshair.AttachedBox.Point1.X =
	Crosshair.AttachedBox.Point2.X = Crosshair.X;
      Crosshair.AttachedBox.Point1.Y =
	Crosshair.AttachedBox.Point2.Y = Crosshair.Y;
      Crosshair.AttachedBox.State = STATE_SECOND;
      break;

    case STATE_SECOND:		/* setup second point */
      Crosshair.AttachedBox.State = STATE_THIRD;
      break;
    }
  RestoreCrosshair (True);
}


/* ---------------------------------------------------------------------------
 *
 * does what's appropriate for the current mode setting. This normaly
 * means creation of an object at the current crosshair location.
 *
 * new created objects are added to the create undo list of course
 */
static void
NotifyMode (void)
{
  void *ptr1, *ptr2, *ptr3;
  int type;

  if (Settings.RatWarn)
    ClearWarnings ();
  switch (Settings.Mode)
    {
    case ARROW_MODE:
      {
	int test;

	Note.Click = True;
	/* do something after click time */
	gtk_timeout_add(CLICK_TIME,
			(GtkFunction) click_cb, NULL);

	/* see if we clicked on something already selected
	 * (Note.Moving) or clicked on a MOVE_TYPE
	 * (Note.Hit)
	 */
	for (test = (SELECT_TYPES | MOVE_TYPES) & ~RATLINE_TYPE;
	     test; test &= ~type)
	  {
	    type = SearchScreen (Note.X, Note.Y, test, &ptr1, &ptr2, &ptr3);
	    if (!Note.Hit && (type & MOVE_TYPES) &&
		!TEST_FLAG (LOCKFLAG, (PinTypePtr) ptr2))
	      {
		Note.Hit = type;
		Note.ptr1 = ptr1;
		Note.ptr2 = ptr2;
		Note.ptr3 = ptr3;
	      }
	    if (!Note.Moving && (type & SELECT_TYPES) &&
		TEST_FLAG (SELECTEDFLAG, (PinTypePtr) ptr2))
	      Note.Moving = True;
	    if ((Note.Hit && Note.Moving) || type == NO_TYPE)
	      break;
	  }
	break;
      }

    case VIA_MODE:
      {
	PinTypePtr via;

	if (!PCB->ViaOn)
	  {
	    Message (_("You must turn via visibility on before\n"
		     "you can place vias\n"));
	    break;
	  }
	if ((via = CreateNewVia (PCB->Data, Note.X, Note.Y,
				 Settings.ViaThickness, 2 * Settings.Keepaway,
				 0, Settings.ViaDrillingHole, NULL,
				 NoFlags())) != NULL)
	  {
	    UpdatePIPFlags (via, (ElementTypePtr) via, NULL, False);
	    AddObjectToCreateUndoList (VIA_TYPE, via, via, via);
	    IncrementUndoSerialNumber ();
	    DrawVia (via, 0);
	    Draw ();
	  }
	break;
      }

    case ARC_MODE:
      {
	switch (Crosshair.AttachedBox.State)
	  {
	  case STATE_FIRST:
	    Crosshair.AttachedBox.Point1.X =
	      Crosshair.AttachedBox.Point2.X = Note.X;
	    Crosshair.AttachedBox.Point1.Y =
	      Crosshair.AttachedBox.Point2.Y = Note.Y;
	    Crosshair.AttachedBox.State = STATE_SECOND;
	    break;

	  case STATE_SECOND:
	  case STATE_THIRD:
	    {
	      ArcTypePtr arc;
	      LocationType wx, wy;
	      int sa, dir;

	      wx = Note.X - Crosshair.AttachedBox.Point1.X;
	      wy = Note.Y - Crosshair.AttachedBox.Point1.Y;
	      if (XOR (Crosshair.AttachedBox.otherway, abs (wy) > abs (wx)))
		{
		  Crosshair.AttachedBox.Point2.X =
		    Crosshair.AttachedBox.Point1.X + abs (wy) * SGNZ (wx);
		  sa = (wx >= 0) ? 0 : 180;
#ifdef ARC45
		  if (abs (wy) / 2 >= abs (wx))
		    dir = (SGNZ (wx) == SGNZ (wy)) ? 45 : -45;
		  else
#endif
		    dir = (SGNZ (wx) == SGNZ (wy)) ? 90 : -90;
		}
	      else
		{
		  Crosshair.AttachedBox.Point2.Y =
		    Crosshair.AttachedBox.Point1.Y + abs (wx) * SGNZ (wy);
		  sa = (wy >= 0) ? -90 : 90;
#ifdef ARC45
		  if (abs (wx) / 2 >= abs (wy))
		    dir = (SGNZ (wx) == SGNZ (wy)) ? -45 : 45;
		  else
#endif
		    dir = (SGNZ (wx) == SGNZ (wy)) ? -90 : 90;
		  wy = wx;
		}
	      if (abs (wy) > 0 && (arc = CreateNewArcOnLayer (CURRENT,
							      Crosshair.
							      AttachedBox.
							      Point2.X,
							      Crosshair.
							      AttachedBox.
							      Point2.Y,
							      abs (wy), sa,
							      dir,
							      Settings.
							      LineThickness,
							      2 * Settings.
							      Keepaway,
							      MakeFlags (
							      TEST_FLAG
							      (CLEARNEWFLAG,
							       PCB) ?
							      CLEARLINEFLAG :
							      0))))
		{
		  BoxTypePtr bx;

		  bx = GetArcEnds (arc);
		  Crosshair.AttachedBox.Point1.X =
		    Crosshair.AttachedBox.Point2.X = bx->X2;
		  Crosshair.AttachedBox.Point1.Y =
		    Crosshair.AttachedBox.Point2.Y = bx->Y2;
		  AddObjectToCreateUndoList (ARC_TYPE, CURRENT, arc, arc);
		  IncrementUndoSerialNumber ();
		  addedLines++;
		  DrawArc (CURRENT, arc, 0);
		  Draw ();
		  Crosshair.AttachedBox.State = STATE_THIRD;
		}
	      break;
	    }
	  }
	break;
      }
    case LOCK_MODE:
      {
	type = SearchScreen (Note.X, Note.Y, LOCK_TYPES, &ptr1, &ptr2, &ptr3);
	if (type == ELEMENT_TYPE)
	  {
	    ElementTypePtr element = (ElementTypePtr) ptr2;

	    TOGGLE_FLAG (LOCKFLAG, element);
	    PIN_LOOP (element);
	    {
	      TOGGLE_FLAG (LOCKFLAG, pin);
	      CLEAR_FLAG (SELECTEDFLAG, pin);
	    }
	    END_LOOP;
	    PAD_LOOP (element);
	    {
	      TOGGLE_FLAG (LOCKFLAG, pad);
	      CLEAR_FLAG (SELECTEDFLAG, pad);
	    }
	    END_LOOP;
	    CLEAR_FLAG (SELECTEDFLAG, element);
	    /* always re-draw it since I'm too lazy
	     * to tell if a selected flag changed
	     */
	    DrawElement (element, 0);
	    Draw ();
	    ReportDialog ();
	  }
	else if (type != NO_TYPE)
	  {
	    TextTypePtr thing = (TextTypePtr) ptr3;
	    TOGGLE_FLAG (LOCKFLAG, thing);
	    if (TEST_FLAG (LOCKFLAG, thing)
		&& TEST_FLAG (SELECTEDFLAG, thing))
	      {
		/* this is not un-doable since LOCK isn't */
		CLEAR_FLAG (SELECTEDFLAG, thing);
		DrawObject (type, ptr1, ptr2, 0);
		Draw ();
	      }
	    ReportDialog ();
	  }
	break;
      }
    case THERMAL_MODE:
      {
	if (((type
	      =
	      SearchScreen (Note.X, Note.Y, PIN_TYPES, &ptr1, &ptr2,
			    &ptr3)) != NO_TYPE)
	    && TEST_PIP (INDEXOFCURRENT, (PinTypePtr) ptr3)
	    && !TEST_FLAG (HOLEFLAG, (PinTypePtr) ptr3))
	  {
	    AddObjectToFlagUndoList (type, ptr1, ptr2, ptr3);
	    TOGGLE_THERM (INDEXOFCURRENT, (PinTypePtr) ptr3);
	    IncrementUndoSerialNumber ();
	    ClearPin ((PinTypePtr) ptr3, type, 0);
	    Draw ();
	  }
	break;
      }

    case LINE_MODE:
      /* do update of position */
      NotifyLine ();
      if (Crosshair.AttachedLine.State != STATE_THIRD)
	break;

      /* Remove anchor if clicking on start point;
       * this means we can't paint 0 length lines
       * which could be used for square SMD pads.
       * Instead use a very small delta, or change
       * the file after saving.
       */
      if (Crosshair.X == Crosshair.AttachedLine.Point1.X
	  && Crosshair.Y == Crosshair.AttachedLine.Point1.Y)
	{
	  SetMode (LINE_MODE);
	  break;
	}

      if (PCB->RatDraw)
	{
	  RatTypePtr line;
	  if ((line = AddNet ()))
	    {
	      AddObjectToCreateUndoList (RATLINE_TYPE, line, line, line);
	      IncrementUndoSerialNumber ();
	      DrawRat (line, 0);
	      Crosshair.AttachedLine.Point1.X =
		Crosshair.AttachedLine.Point2.X;
	      Crosshair.AttachedLine.Point1.Y =
		Crosshair.AttachedLine.Point2.Y;
	      Draw ();
	    }
	  break;
	}
      else
	/* create line if both ends are determined && length != 0 */
	{
	  LineTypePtr line;

	  if (PCB->Clipping
	      && Crosshair.AttachedLine.Point1.X ==
	      Crosshair.AttachedLine.Point2.X
	      && Crosshair.AttachedLine.Point1.Y ==
	      Crosshair.AttachedLine.Point2.Y
	      && (Crosshair.AttachedLine.Point2.X != Note.X
		  || Crosshair.AttachedLine.Point2.Y != Note.Y))
	    {
	      /* We will paint only the second line segment.
	         Since we only check for vias on the first segment,
	         swap them so we only paint the first segment. */
	      Crosshair.AttachedLine.Point2.X = Note.X;
	      Crosshair.AttachedLine.Point2.Y = Note.Y;
	    }

	  if ((Crosshair.AttachedLine.Point1.X !=
	       Crosshair.AttachedLine.Point2.X
	       || Crosshair.AttachedLine.Point1.Y !=
	       Crosshair.AttachedLine.Point2.Y)
	      && (line =
		  CreateDrawnLineOnLayer (CURRENT,
					  Crosshair.AttachedLine.Point1.X,
					  Crosshair.AttachedLine.Point1.Y,
					  Crosshair.AttachedLine.Point2.X,
					  Crosshair.AttachedLine.Point2.Y,
					  Settings.LineThickness,
					  2 * Settings.Keepaway,
					  MakeFlags((TEST_FLAG (AUTODRCFLAG, PCB) ?
					   FOUNDFLAG : 0) |
					  (TEST_FLAG (CLEARNEWFLAG, PCB) ?
					   CLEARLINEFLAG : 0)))) != NULL)
	    {
	      PinTypePtr via;

	      addedLines++;
	      AddObjectToCreateUndoList (LINE_TYPE, CURRENT, line, line);
	      DrawLine (CURRENT, line, 0);
	      /* place a via if vias are visible, the layer is
	         in a new group since the last line and there
	         isn't a pin already here */
	      if (PCB->ViaOn && GetLayerGroupNumberByPointer (CURRENT) !=
		  GetLayerGroupNumberByPointer (lastLayer) &&
		  SearchObjectByLocation (PIN_TYPES, &ptr1, &ptr2, &ptr3,
					  Crosshair.AttachedLine.Point1.X,
					  Crosshair.AttachedLine.Point1.Y,
					  Settings.ViaThickness / 2) ==
		  NO_TYPE
		  && (via =
		      CreateNewVia (PCB->Data,
				    Crosshair.AttachedLine.Point1.X,
				    Crosshair.AttachedLine.Point1.Y,
				    Settings.ViaThickness,
				    2 * Settings.Keepaway, 0,
				    Settings.ViaDrillingHole, NULL,
				    NoFlags())) != NULL)
		{
		  UpdatePIPFlags (via, (ElementTypePtr) via, NULL, False);
		  AddObjectToCreateUndoList (VIA_TYPE, via, via, via);
		  DrawVia (via, 0);
		}
	      /* copy the coordinates */
	      Crosshair.AttachedLine.Point1.X =
		Crosshair.AttachedLine.Point2.X;
	      Crosshair.AttachedLine.Point1.Y =
		Crosshair.AttachedLine.Point2.Y;
	      IncrementUndoSerialNumber ();
	      lastLayer = CURRENT;
	    }
	  if (PCB->Clipping && (Note.X != Crosshair.AttachedLine.Point2.X
				|| Note.Y !=
				Crosshair.AttachedLine.Point2.Y)
	      && (line =
		  CreateDrawnLineOnLayer (CURRENT,
					  Crosshair.AttachedLine.Point2.X,
					  Crosshair.AttachedLine.Point2.Y,
					  Note.X, Note.Y,
					  Settings.LineThickness,
					  2 * Settings.Keepaway,
					  MakeFlags((TEST_FLAG (AUTODRCFLAG, PCB) ?
					   FOUNDFLAG : 0) |
					  (TEST_FLAG (CLEARNEWFLAG, PCB) ?
					   CLEARLINEFLAG : 0)))) != NULL)
	    {
	      addedLines++;
	      AddObjectToCreateUndoList (LINE_TYPE, CURRENT, line, line);
	      IncrementUndoSerialNumber ();
	      DrawLine (CURRENT, line, 0);
	      /* move to new start point */
	      Crosshair.AttachedLine.Point1.X = Note.X;
	      Crosshair.AttachedLine.Point1.Y = Note.Y;
	      Crosshair.AttachedLine.Point2.X = Note.X;
	      Crosshair.AttachedLine.Point2.Y = Note.Y;
	      if (TEST_FLAG (SWAPSTARTDIRFLAG, PCB))
		{
		  PCB->Clipping ^= 3;
		  set_status_line_label ();
		}
	    }
	  Draw ();
	}
      break;

    case RECTANGLE_MODE:
      /* do update of position */
      NotifyBlock ();

      /* create rectangle if both corners are determined 
       * and width, height are != 0
       */
      if (Crosshair.AttachedBox.State == STATE_THIRD &&
	  Crosshair.AttachedBox.Point1.X != Crosshair.AttachedBox.Point2.X &&
	  Crosshair.AttachedBox.Point1.Y != Crosshair.AttachedBox.Point2.Y)
	{
	  PolygonTypePtr polygon;

	  if ((polygon = CreateNewPolygonFromRectangle (CURRENT,
							Crosshair.
							AttachedBox.Point1.X,
							Crosshair.
							AttachedBox.Point1.Y,
							Crosshair.
							AttachedBox.Point2.X,
							Crosshair.
							AttachedBox.Point2.Y,
							MakeFlags(CLEARPOLYFLAG))) !=
	      NULL)
	    {
	      AddObjectToCreateUndoList (POLYGON_TYPE, CURRENT,
					 polygon, polygon);
	      UpdatePIPFlags (NULL, NULL, CURRENT, True);
	      IncrementUndoSerialNumber ();
	      DrawPolygon (CURRENT, polygon, 0);
	      Draw ();
	    }

	  /* reset state to 'first corner' */
	  Crosshair.AttachedBox.State = STATE_FIRST;
	}
      break;

    case TEXT_MODE:
      {
	char *string;

	if ((string = gui_dialog_input(_("Enter text:"), "")) != NULL)
	  {
	    TextTypePtr text;
	    int flag = NOFLAG;

	    if (GetLayerGroupNumberByNumber (INDEXOFCURRENT) ==
		GetLayerGroupNumberByNumber (MAX_LAYER + SOLDER_LAYER))
	      flag = ONSOLDERFLAG;
	    if ((text = CreateNewText (CURRENT, &PCB->Font, Note.X,
				       Note.Y, 0, Settings.TextScale,
				       string, MakeFlags(flag))) != NULL)
	      {
		AddObjectToCreateUndoList (TEXT_TYPE, CURRENT, text, text);
		IncrementUndoSerialNumber ();
		DrawText (CURRENT, text, 0);
		Draw ();
	      }

	    /* free memory allocated by gui_dialog_input() */
	    g_free(string);
	  }
	break;
      }

    case POLYGON_MODE:
      {
	PointTypePtr points = Crosshair.AttachedPolygon.Points;
	Cardinal n = Crosshair.AttachedPolygon.PointN;

	/* 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)
	  {
	    CopyAttachedPolygonToLayer ();
	    Draw ();
	    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;
      }

    case PASTEBUFFER_MODE:
      {
	gui_watch_cursor ();
	if (CopyPastebufferToLayout (Note.X, Note.Y))
	  SetChangedFlag (True);
	gui_restore_cursor ();
	break;
      }

    case REMOVE_MODE:
      if ((type =
	   SearchScreen (Note.X, Note.Y, REMOVE_TYPES, &ptr1, &ptr2,
			 &ptr3)) != NO_TYPE)
	{
	  if (TEST_FLAG (LOCKFLAG, (LineTypePtr) ptr2))
	    {
	      Message (_("Sorry, the object is locked\n"));
	      break;
	    }
	  if (type == ELEMENT_TYPE)
	    {
	      RubberbandTypePtr ptr;
	      int i;

	      Crosshair.AttachedObject.RubberbandN = 0;
	      LookupRatLines (type, ptr1, ptr2, ptr3);
	      ptr = Crosshair.AttachedObject.Rubberband;
	      for (i = 0; i < Crosshair.AttachedObject.RubberbandN; i++)
		{
		  if (PCB->RatOn)
		    EraseRat ((RatTypePtr) ptr->Line);
		  MoveObjectToRemoveUndoList (RATLINE_TYPE,
					      ptr->Line, ptr->Line,
					      ptr->Line);
		  ptr++;
		}
	    }
	  RemoveObject (type, ptr1, ptr2, ptr3);
	  IncrementUndoSerialNumber ();
	  SetChangedFlag (True);
	}
      break;

    case ROTATE_MODE:
      RotateScreenObject (Note.X, Note.Y,
					gui_shift_is_pressed() ? (SWAP_IDENT ?
							    1 : 3)
			  : (SWAP_IDENT ? 3 : 1));
      break;

      /* both are almost the same */
    case COPY_MODE:
    case MOVE_MODE:
      switch (Crosshair.AttachedObject.State)
	{
	  /* first notify, lookup object */
	case STATE_FIRST:
	  {
	    int types = (Settings.Mode == COPY_MODE) ?
	      COPY_TYPES : MOVE_TYPES;

	    Crosshair.AttachedObject.Type =
	      SearchScreen (Note.X, Note.Y, types,
			    &Crosshair.AttachedObject.Ptr1,
			    &Crosshair.AttachedObject.Ptr2,
			    &Crosshair.AttachedObject.Ptr3);
	    if (Crosshair.AttachedObject.Type != NO_TYPE)
	      {
		if (Settings.Mode == MOVE_MODE &&
		    TEST_FLAG (LOCKFLAG, (PinTypePtr)
			       Crosshair.AttachedObject.Ptr2))
		  {
		    Message (_("Sorry, the object is locked\n"));
		    Crosshair.AttachedObject.Type = NO_TYPE;
		  }
		else
		  AttachForCopy (Note.X, Note.Y);
	      }
	    break;
	  }

	  /* second notify, move or copy object */
	case STATE_SECOND:
	  if (Settings.Mode == COPY_MODE)
	    CopyObject (Crosshair.AttachedObject.Type,
			Crosshair.AttachedObject.Ptr1,
			Crosshair.AttachedObject.Ptr2,
			Crosshair.AttachedObject.Ptr3,
			Note.X - Crosshair.AttachedObject.X,
			Note.Y - Crosshair.AttachedObject.Y);
	  else
	    {
	      MoveObjectAndRubberband (Crosshair.AttachedObject.Type,
				       Crosshair.AttachedObject.Ptr1,
				       Crosshair.AttachedObject.Ptr2,
				       Crosshair.AttachedObject.Ptr3,
				       Note.X - Crosshair.AttachedObject.X,
				       Note.Y - Crosshair.AttachedObject.Y);
	      SetLocalRef (0, 0, False);
	    }
	  SetChangedFlag (True);

	  /* reset identifiers */
	  Crosshair.AttachedObject.Type = NO_TYPE;
	  Crosshair.AttachedObject.State = STATE_FIRST;
	  break;
	}
      break;

      /* insert a point into a polygon/line/... */
    case INSERTPOINT_MODE:
      switch (Crosshair.AttachedObject.State)
	{
	  /* first notify, lookup object */
	case STATE_FIRST:
	  Crosshair.AttachedObject.Type =
	    SearchScreen (Note.X, Note.Y, INSERT_TYPES,
			  &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
		{
		  /* get starting point of nearest segment */
		  if (Crosshair.AttachedObject.Type == POLYGON_TYPE)
		    {
		      fake.poly =
			(PolygonTypePtr) Crosshair.AttachedObject.Ptr2;
		      polyIndex =
			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];
		      Crosshair.AttachedObject.Ptr2 = &fake.line;

		    }
		  Crosshair.AttachedObject.State = STATE_SECOND;
		  InsertedPoint = *AdjustInsertPoint ();
		}
	    }
	  break;

	  /* second notify, insert new point into object */
	case STATE_SECOND:
	  if (Crosshair.AttachedObject.Type == POLYGON_TYPE)
	    InsertPointIntoObject (POLYGON_TYPE,
				   Crosshair.AttachedObject.Ptr1, fake.poly,
				   &polyIndex,
				   InsertedPoint.X, InsertedPoint.Y, False);
	  else
	    InsertPointIntoObject (Crosshair.AttachedObject.Type,
				   Crosshair.AttachedObject.Ptr1,
				   Crosshair.AttachedObject.Ptr2,
				   &polyIndex,
				   InsertedPoint.X, InsertedPoint.Y, False);
	  SetChangedFlag (True);

	  /* reset identifiers */
	  Crosshair.AttachedObject.Type = NO_TYPE;
	  Crosshair.AttachedObject.State = STATE_FIRST;
	  break;
	}
      break;
    }
}


#include <gdk/gdkx.h>

/* ---------------------------------------------------------------------------
 * warp pointer to new cursor location
 */
static void
WarpPointer (Boolean ignore)
	{
	Window				w_src,
						w_dst;

	w_src = GDK_WINDOW_XID(Output.drawing_area->window);
	w_dst = w_src;

	/* don't warp with the auto drc - that creates auto-scroll chaos */
	if (   TEST_FLAG (AUTODRCFLAG, PCB) && Settings.Mode == LINE_MODE
	    && Crosshair.AttachedLine.State != STATE_FIRST
	   )
		return;

	XWarpPointer(GDK_DRAWABLE_XDISPLAY(Output.drawing_area->window),
			w_src, w_dst,
			0, 0, 0, 0,
			(int) (TO_SCREEN_X (Crosshair.X)),
			(int) (TO_SCREEN_Y (Crosshair.Y)));

	/* XWarpPointer creates Motion events normally bound to
	*  EventMoveCrosshair.
	*  We don't do any updates when EventMoveCrosshair
	*  is called the next time to prevent from rounding errors
	*/
	IgnoreMotionEvents = ignore;
	}


/* ---------------------------------------------------------------------------
 * action routine to save and restore the undo serial number
 * this allows making multiple-action bindings into an atomic operation
 * that will be undone by a single Undo command
 *
 * syntax: Atomic(Save|Restore|Close|Block)
 * Save saves the undo serial number
 * Restore returns it to the last saved number
 * Close sets it to 1 greater than the last save
 * Block increments it only if was actually incremented
 * 	since the last save
 */
void
ActionAtomic(char *function)
{
	switch (GetFunctionID (function))
	{
	case F_Save:
	  SaveUndoSerialNumber ();
	  break;
	case F_Restore:
	  RestoreUndoSerialNumber ();
	  break;
	case F_Close:
	  RestoreUndoSerialNumber ();
	  IncrementUndoSerialNumber ();
	  break;
	case F_Block:
	  RestoreUndoSerialNumber ();
	  if (Bumped)
	    IncrementUndoSerialNumber ();
	  break;
	}
}

/* --------------------------------------------------------------------------
 * action routine to invoke the DRC check
 * needs more work
 * syntax: DRC();
 */
void
ActionDRCheck (void)
{
  Cardinal count;

      Message (_("Rules are minspace %d.%02d, minoverlap %d.%d "
	       "minwidth %d.%02d, minsilk %d.%02d\n"
	       "min drill %d.%02d, min annular ring %d.%02d\n"),
	       (PCB->Bloat + 1) / 100, (PCB->Bloat + 1) % 100,
	       PCB->Shrink / 100, PCB->Shrink % 100,
	       PCB->minWid / 100, PCB->minWid % 100,
	       PCB->minSlk / 100, PCB->minSlk % 100,
	       PCB->minDrill / 100, PCB->minDrill % 100,
	       PCB->minRing / 100, PCB->minRing % 100);
      HideCrosshair (True);
      gui_watch_cursor ();
      count = DRCAll ();
      if (count == 0)
	Message (_("No DRC problems found.\n"));
      else
	Message (_("Found %d design rule errors\n"), count);
      gui_restore_cursor ();
      RestoreCrosshair (True);
      return;
}

/* --------------------------------------------------------------------------
 * action routine to flip an element to the opposite side of the board 
 * syntax: Flip(SelectedElements|Object);
 */
void
ActionFlip (char *function)
{
  ElementTypePtr element;
  void *ptrtmp;
  int err = 0;

  if (function)
    {
      HideCrosshair (True);
      switch (GetFunctionID (function))
	{
	case F_Object:
	  if ((SearchScreen (Crosshair.X, Crosshair.Y, ELEMENT_TYPE,
			     &ptrtmp,
			     &ptrtmp,
			     &ptrtmp)) != NO_TYPE)
	    {
	      element = (ElementTypePtr) ptrtmp;
	      ChangeElementSide (element, 2 * Crosshair.Y - PCB->MaxHeight);
	      UpdatePIPFlags (NULL, element, NULL, True);
	      IncrementUndoSerialNumber ();
	      Draw ();
	    }
	  break;
	case F_Selected:
	case F_SelectedElements:
	  ChangeSelectedElementSide ();
	  break;
	default:
	  err = 1;
	  break;
	}
      RestoreCrosshair (True);
      if (!err)
	return;
    }

  Message ("Usage:  \n" "Flip(Object|Selected|SelectedElements)\n");

}


/* --------------------------------------------------------------------------
 * action routine to toggle a thermal (on the current layer) to pins or vias
 * syntax: ToggleThermal(Object|SelectePins|SelectedVias|Selected);
 */
void
ActionToggleThermal (char *function)
{
  void *ptr1, *ptr2, *ptr3;
  int type;
  int err = 0;

  if (function && *function)
    {
      HideCrosshair (True);
      switch (GetFunctionID (function))
	{
	case F_Object:
	  if ((type =
	       SearchScreen (Crosshair.X, Crosshair.Y, CHANGETHERMAL_TYPES,
			     &ptr1, &ptr2, &ptr3)) != NO_TYPE)
	    {
	      ChangeObjectThermal (type, ptr1, ptr2, ptr3);
	      IncrementUndoSerialNumber ();
	      Draw ();
	    }
	  break;
	case F_SelectedPins:
	  ChangeSelectedThermals (PIN_TYPE);
	  break;
	case F_SelectedVias:
	  ChangeSelectedThermals (VIA_TYPE);
	  break;
	case F_Selected:
	case F_SelectedElements:
	  ChangeSelectedThermals (CHANGETHERMAL_TYPES);
	  break;
	default:
	  err = 1;
	  break;
	}
      RestoreCrosshair (True);
      if (!err)
	return;
    }
  Message ("Usage:  \n"
	   "ToggleThermal(Object|Selected|SelectedElements|"
	   "SelectedPins|SelectedVias)\n");
}

/* --------------------------------------------------------------------------
 * action routine to set a thermal (on the current layer) to pins or vias
 * syntax: SetThermal(Object|SelectePins|SelectedVias|Selected);
 */
void
ActionSetThermal (char *function)
{
  void *ptr1, *ptr2, *ptr3;
  int type;
  int err = 0;

  if (function && *function)
    {
      HideCrosshair (True);
      switch (GetFunctionID (function))
	{
	case F_Object:
	  if ((type =
	       SearchScreen (Crosshair.X, Crosshair.Y, CHANGETHERMAL_TYPES,
			     &ptr1, &ptr2, &ptr3)) != NO_TYPE)
	    {
	      SetObjectThermal (type, ptr1, ptr2, ptr3);
	      IncrementUndoSerialNumber ();
	      Draw ();
	    }
	  break;
	case F_SelectedPins:
	  SetSelectedThermals (PIN_TYPE);
	  break;
	case F_SelectedVias:
	  SetSelectedThermals (VIA_TYPE);
	  break;
	case F_Selected:
	case F_SelectedElements:
	  SetSelectedThermals (CHANGETHERMAL_TYPES);
	  break;
	default:
	  err = 1;
	  break;
	}
      RestoreCrosshair (True);
      if (!err)
	return;
    }
  Message ("Usage:  \n"
	   "SetThermal(Object|Selected|SelectedElements|"
	   "SelectedPins|SelectedVias)\n");
}

/* --------------------------------------------------------------------------
 * action routine to clear a thermal (on the current layer) to pins or vias
 * syntax: ClearThermal(Object|SelectePins|SelectedVias|Selected);
 */
void
ActionClearThermal (char *function)
{
  void *ptr1, *ptr2, *ptr3;
  int type;
  int err = 0;

  if (function && *function)
    {
      HideCrosshair (True);
      switch (GetFunctionID (function))
	{
	case F_Object:
	  if ((type =
	       SearchScreen (Crosshair.X, Crosshair.Y, CHANGETHERMAL_TYPES,
			     &ptr1, &ptr2, &ptr3)) != NO_TYPE)
	    {
	      ClrObjectThermal (type, ptr1, ptr2, ptr3);
	      IncrementUndoSerialNumber ();
	      Draw ();
	    }
	  break;
	case F_SelectedPins:
	  ClrSelectedThermals (PIN_TYPE);
	  break;
	case F_SelectedVias:
	  ClrSelectedThermals (VIA_TYPE);
	  break;
	case F_Selected:
	case F_SelectedElements:
	  ClrSelectedThermals (CHANGETHERMAL_TYPES);
	  break;
	default:
	  err = 1;
	  break;
	}
      RestoreCrosshair (True);
      if (!err)
	return;
    }
  Message ("Usage:  \n"
	   "ClearThermal(Object|Selected|SelectedElements"
	   "|SelectedPins|SelectedVias)\n");
}


/* ---------------------------------------------------------------------------
 * action routine to move the X pointer relative to the current position
 * syntax: MovePointer(deltax,deltay)
 */
void
ActionMovePointer (gchar *deltax, gchar *deltay)
{
  LocationType x, y, dx, dy;

      /* save old crosshair position */
      x = Crosshair.X;
      y = Crosshair.Y;
      dx = (LocationType) (atoi(deltax) * PCB->Grid);
      dy = (LocationType) (atoi(deltay) * PCB->Grid);
      MoveCrosshairRelative (TO_SCREEN_SIGN_X (dx), TO_SCREEN_SIGN_Y (dy));
      FitCrosshairIntoGrid (Crosshair.X, Crosshair.Y);
      /* adjust pointer before erasing anything */
      /* in case there is no hardware cursor */
      WarpPointer (True);
      /* restore crosshair for erasure */
      Crosshair.X = x;
      Crosshair.Y = y;
      HideCrosshair (False);
      MoveCrosshairRelative (TO_SCREEN_SIGN_X (dx), TO_SCREEN_SIGN_Y (dy));
      /* update object position and cursor location */
      AdjustAttachedObjects ();
      set_cursor_position_labels();
      RestoreCrosshair (False);
}

/* ---------------------------------------------------------------------------
 * !!! no action routine !!!
 *
 * event handler to set the cursor according to the X pointer position
 * called from inside main.c
 */
void
EventMoveCrosshair (int ev_x, int ev_y)
{
#ifdef HAVE_LIBSTROKE
  if (mid_stroke)
    {
      StrokeBox.X2 = TO_PCB_X (ev_x);
      StrokeBox.Y2 = TO_PCB_Y (ev_y);
      stroke_record (Event->x, ev_y);
      return;
    }
#endif /* HAVE_LIBSTROKE */
  /* ignore events that are caused by ActionMovePointer */
  if (!IgnoreMotionEvents)
    {
		GdkModifierType mask;
      int childX, childY;

      /* only handle the event if the pointer is still at
       * the same position to prevent slow systems from
       * slow redrawing
       */
	gdk_window_get_pointer (Output.drawing_area->window,
			&childX, &childY, &mask);

      if (ev_x == childX && ev_y == childY)
	{
	  if (Settings.Mode == NO_MODE && (mask & GDK_BUTTON1_MASK))
	    {
	      LocationType x, y;
	      HideCrosshair (False);
	      x = TO_SCREEN_X (Crosshair.X) - ev_x;
	      y = TO_SCREEN_Y (Crosshair.Y) - ev_y;
	      CenterDisplay (x, y, True);
	      RestoreCrosshair (False);
	    }
	  else if (MoveCrosshairAbsolute (TO_PCB_X (ev_x),
					  TO_PCB_Y (ev_y)))
	    {

	      /* update object position and cursor location */
	      AdjustAttachedObjects ();
	      set_cursor_position_labels ();
	      RestoreCrosshair (False);
	    }
	}
    }
  else
    IgnoreMotionEvents = False;
}

/* ---------------------------------------------------------------------------
 * action routine to change the grid, zoom and sizes
 * the first the type of object and the second is passed to
 * the specific routine
 * the value of the second determines also if it is absolute (without a sign)
 * or relative to the current one (with sign + or -)
 * syntax: SetValue(Grid|Zoom|LineSize|TextScale|ViaDrillingHole|ViaSize, value)
 */
void
ActionSetValue (char *function, char *val, char *units)
{
  Boolean r;			/* flag for 'relative' value */
  float value;
  int err = 0;

  if (function && val)
    {
      HideCrosshair (True);
      value = GetValue (val, units, &r);
      switch (GetFunctionID (function))
	{
	case F_ViaDrillingHole:
	  SetViaDrillingHole (r ? value : value + Settings.ViaDrillingHole,
			      False);
	  gui_route_style_buttons_update();
	  break;

	case F_Grid:
	  if (!r)
	    {
	      if ((value == (int) value && PCB->Grid == (int) PCB->Grid)
		  || (value != (int) value && PCB->Grid != (int) PCB->Grid))
		SetGrid (value + PCB->Grid, False);
	      else
		Message (_("Don't combine metric/English grids like that!\n"));
	    }
	  else
	    SetGrid (value, False);
	  break;

	case F_Zoom:
	  SetZoom (r ? value : value + PCB->Zoom);
	  break;

	case F_LineSize:
	case F_Line:
	  SetLineSize (r ? value : value + Settings.LineThickness);
	  gui_route_style_buttons_update();
	  break;

	case F_Via:
	case F_ViaSize:
	  SetViaSize (r ? value : value + Settings.ViaThickness, False);
	  gui_route_style_buttons_update();
	  break;

	case F_Text:
	case F_TextScale:
	  value /= 45;
	  SetTextScale (r ? value : value + Settings.TextScale);
	  break;
	default:
	  err = 1;
	  break;
	}
      RestoreCrosshair (True);
      if (!err)
	return;
    }
  Message ("Usage:  \n"
	   "SetValue(Grid|Zoom|LineSize|TextScale|"
	   "ViaDrillingHole|ViaSize, value)\n"
	   "SetValue(Grid|Zoom|LineSize|TextScale|"
	   "ViaDrillingHole|ViaSize, value, mil|mm)\n");

}


/* ---------------------------------------------------------------------------
 * reports on an object 
 * syntax: Report(Object|DrillReport|FoundPins)
 */
void
ActionReport (char *function)
{
  if (function)
    switch (GetFunctionID (function))
      {
      case F_Object:
	{
	  ReportDialog ();
	  break;
	}
      case F_DrillReport:
	{
	  ReportDrills ();
	  break;
	}
      case F_FoundPins:
	{
	  ReportFoundPins ();
	  break;
	}
      }
  else
    Message ("Usage:  \n" "Report(Object|DrillReport|FoundPins)\n");

}

/* ---------------------------------------------------------------------------
 * quits application
 * syntax: Quit()
 */
void
ActionQuit (void)
{
	if (!PCB->Changed || gui_dialog_confirm(_("OK to lose data ?")))
		QuitApplication ();

}

/* ---------------------------------------------------------------------------
 * searches connections of the object at the cursor position
 * syntax: Connection(Find|ResetLinesAndPolygons|ResetPinsAndVias|Reset)
 */
void
ActionConnection (char *function)
{
  if (function)
    {
      HideCrosshair (True);
      switch (GetFunctionID (function))
	{
	case F_Find:
	  {
	    gui_watch_cursor ();
	    LookupConnection (Crosshair.X, Crosshair.Y, True, TO_PCB (1));
	    gui_restore_cursor ();
	    break;
	  }

	case F_ResetLinesAndPolygons:
	  ResetFoundLinesAndPolygons (True);
	  break;

	case F_ResetPinsViasAndPads:
	  ResetFoundPinsViasAndPads (True);
	  break;

	case F_Reset:
	  SaveUndoSerialNumber ();
	  ResetFoundPinsViasAndPads (True);
	  RestoreUndoSerialNumber ();
	  ResetFoundLinesAndPolygons (True);
	  break;
	}
      RestoreCrosshair (True);
      return;
    }
  Message ("Usage:  \n"
	   "Connection(Find|ResetLinesAndPolygons|ResetPinsAndVias|Reset)\n");
}

/* ---------------------------------------------------------------------------
 * starts input of user commands
 * syntax: Command()
 */
void
ActionCommand (gchar *str)
	{
	char		*command;
	static char	*previous = NULL;

	if (Settings.use_command_window)
		gui_command_window_show();
	else
		{
		HideCrosshair (True);
		if (!str || !*str)
			{
			if (Settings.SaveLastCommand)
				command = gui_command_entry_get("Enter command:",
							previous ? previous :"");
			else
				command = gui_command_entry_get("Enter command:", "");
			if (command != NULL)
				{
				/* copy new comand line to save buffer */
				g_free(previous);
				previous = g_strdup(command);
				ExecuteUserCommand (command);
				g_free(command);
				}
			}
		else if (previous)
			{
			command = g_strdup(previous);
			ExecuteUserCommand (command);
			g_free(command);
			}
		RestoreCrosshair (True);
		}
	}

void
warpNoWhere (void)
{
	ActionMovePointer("0", "0");
}

/* ---------------------------------------------------------------------------
 * disperses all elements
 * syntax: DisperseElements(All|Selected)
 */
#define GAP 10000

void
ActionDisperseElements (gchar *function)
{
  long minx, miny, maxx, maxy, dx, dy;
  int all = 0, bad = 0;

  minx = GAP;
  miny = GAP;
  maxx = GAP;
  maxy = GAP;

  if (!function || !*function)
    {
      bad = 1;
    }
  else 
    {
      switch (GetFunctionID (function))
	{
	case F_All:
	  all = 1;
	  break;
	  
	case F_Selected:
	  all = 0;
	  break;
	  
	default:
	  bad = 1;
	}
    }

  if (bad) 
    {
      Message ("Usage:  \n"
	       "DisperseElements(Selected|All)\n");
      return ;
    }


  ELEMENT_LOOP (PCB->Data);
  {
    /* 
     * If we want to disperse selected elements, maybe we need smarter
     * code here to avoid putting components on top of others which
     * are not selected.  For now, I'm assuming that this is typically
     * going to be used either with a brand new design or a scratch
     * design holding some new components
     */
    if (all || TEST_FLAG (SELECTEDFLAG, element))
      {

	/* figure out how much to move the element */
	dx = minx - element->BoundingBox.X1 ;
	
	/* snap to the grid */
	dx -= ( element->MarkX + dx ) % (long) (PCB->Grid);
	
	/* 
	 * and add one grid size so we make sure we always space by GAP or
	 * more
	 */
	dx += (long) (PCB->Grid);
	
	/* Figure out if this row has room.  If not, start a new row */
	if ( GAP + element->BoundingBox.X2 + dx > PCB->MaxWidth )
	  {
	    miny = maxy + GAP;
	    minx = GAP;
	  }
	
	/* figure out how much to move the element */
	dx = minx - element->BoundingBox.X1 ;
	dy = miny - element->BoundingBox.Y1 ;
	
	/* snap to the grid */
	dx -= ( element->MarkX + dx ) % (long) (PCB->Grid);
	dx += (long) (PCB->Grid);
	dy -= ( element->MarkY + dy ) % (long) (PCB->Grid);
	dy += (long) (PCB->Grid);

	/* move the element */
	MoveElementLowLevel (PCB->Data, element, dx, dy);

	/* and add to the undo list so we can undo this operation */
	AddObjectToMoveUndoList (ELEMENT_TYPE, NULL, NULL, element, dx, dy);

	/* keep track of how tall this row is */
	minx += element->BoundingBox.X2 - element->BoundingBox.X1 + GAP;
	if (maxy < element->BoundingBox.Y2 )
	  {
	    maxy = element->BoundingBox.Y2;
	  }
      }
    
  }
  END_LOOP;

  /* done with our action so increment the undo # */
  IncrementUndoSerialNumber ();

  ClearAndRedrawOutput ();
  SetChangedFlag (True);

  return ;
}

#undef GAP

/* ---------------------------------------------------------------------------
 * several display related actions
 * syntax: Display(NameOnPCB|Description|Value)
 *         Display(Grid|Center|ClearAndRedraw|Redraw)
 *         Display(CycleClip|Toggle45Degree|ToggleStartDirection)
 *         Display(ToggleGrid|ToggleRubberBandMode|ToggleUniqueNames)
 *         Display(ToggleMask|ToggleName|ToggleClearLine|ToggleSnapPin)
 *         Display(ToggleThindraw|ToggleOrthoMove|ToggleLocalRef)
 *         Display(ToggleCheckPlanes|ToggleShowDRC|ToggleAutoDRC)
 *         Display(ToggleLiveRoute)
 *         Display(Pinout|PinOrPadName)
 *	   Display(Save|Restore)
 *	   Display(Scroll, Direction)
 */
void
ActionDisplay (char *function, char *str_dir)
{
  int id;
  static int saved = 0;
  int err = 0;

  if (function && (!str_dir || !*str_dir))
    {
      HideCrosshair (True);
      switch (id = GetFunctionID (function))
	{

	case F_Save:
	  saved = PCB->Zoom;
	  break;
	case F_Restore:
	  SetZoom (saved);
	  break;
	  /* redraw layout with clearing the background */
	case F_ClearAndRedraw:
	  ClearAndRedrawOutput ();
	  UpdateAll ();
	  break;

	  /* redraw layout without clearing the background */
	case F_Redraw:
	  {
	    BoxType area;
	    area.X1 = 0;
	    area.Y1 = 0;
	    area.X2 = Output.Width;
	    area.Y2 = Output.Height;
	    RedrawOutput (&area);
	    break;
	  }
	  /* center cursor and move X pointer too */
	case F_Center:
	  CenterDisplay (Crosshair.X, Crosshair.Y, False);
	  warpNoWhere ();
	  break;

	  /* change the displayed name of elements */
	case F_Value:
	case F_NameOnPCB:
	case F_Description:
	  ELEMENT_LOOP (PCB->Data);
	  {
	    EraseElementName (element);
	  }
	  END_LOOP;
	  CLEAR_FLAG (DESCRIPTIONFLAG | NAMEONPCBFLAG, PCB);
	  switch (id)
	    {
	    case F_Value:
	      break;
	    case F_NameOnPCB:
	      SET_FLAG (NAMEONPCBFLAG, PCB);
	      break;
	    case F_Description:
	      SET_FLAG (DESCRIPTIONFLAG, PCB);
	      break;
	    }
	  ELEMENT_LOOP (PCB->Data);
	  {
	    DrawElementName (element, 0);
	  }
	  END_LOOP;
	  Draw ();
	  break;

	  /* toggle line-adjust flag */
	case F_ToggleAllDirections:
	  TOGGLE_FLAG (ALLDIRECTIONFLAG, PCB);
	  AdjustAttachedObjects ();
	  set_status_line_label ();
	  break;

	case F_CycleClip:
	  if TEST_FLAG
	    (ALLDIRECTIONFLAG, PCB)
	    {
	      TOGGLE_FLAG (ALLDIRECTIONFLAG, PCB);
	      PCB->Clipping = 0;
	    }
	  else
	    PCB->Clipping = (PCB->Clipping + 1) % 3;
	  AdjustAttachedObjects ();
	  set_status_line_label ();
	  break;

	case F_ToggleRubberBandMode:
	  TOGGLE_FLAG (RUBBERBANDFLAG, PCB);
	  set_status_line_label ();
	  break;

	case F_ToggleStartDirection:
	  TOGGLE_FLAG (SWAPSTARTDIRFLAG, PCB);
	  set_status_line_label ();
	  break;

	case F_ToggleUniqueNames:
	  TOGGLE_FLAG (UNIQUENAMEFLAG, PCB);
	  set_status_line_label ();
	  break;

	case F_ToggleSnapPin:
	  TOGGLE_FLAG (SNAPPINFLAG, PCB);
	  set_status_line_label ();
	  break;

	case F_ToggleLocalRef:
	  TOGGLE_FLAG (LOCALREFFLAG, PCB);
	  break;

	case F_ToggleThindraw:
	  TOGGLE_FLAG (THINDRAWFLAG, PCB);
	  ClearAndRedrawOutput ();
	  break;

	case F_ToggleShowDRC:
	  TOGGLE_FLAG (SHOWDRCFLAG, PCB);
	  break;

	case F_ToggleLiveRoute:
	  TOGGLE_FLAG (LIVEROUTEFLAG, PCB);
	  break;

	case F_ToggleAutoDRC:
	  TOGGLE_FLAG (AUTODRCFLAG, PCB);
	  if (TEST_FLAG (AUTODRCFLAG, PCB) && Settings.Mode == LINE_MODE)
	    {
	      SaveUndoSerialNumber ();
	      ResetFoundPinsViasAndPads (True);
	      RestoreUndoSerialNumber ();
	      ResetFoundLinesAndPolygons (True);
	      if (Crosshair.AttachedLine.State != STATE_FIRST)
		LookupConnection (Crosshair.AttachedLine.Point1.X,
				  Crosshair.AttachedLine.Point1.Y, True, 1);
	    }
	  break;

	case F_ToggleCheckPlanes:
	  TOGGLE_FLAG (CHECKPLANESFLAG, PCB);
	  ClearAndRedrawOutput ();
	  break;

	case F_ToggleOrthoMove:
	  TOGGLE_FLAG (ORTHOMOVEFLAG, PCB);
	  break;

	case F_ToggleName:
	  TOGGLE_FLAG (SHOWNUMBERFLAG, PCB);
	  UpdateAll ();
	  break;

	case F_ToggleMask:
	  TOGGLE_FLAG (SHOWMASKFLAG, PCB);
	  UpdateAll ();
	  break;

	case F_ToggleClearLine:
	  TOGGLE_FLAG (CLEARNEWFLAG, PCB);
	  set_status_line_label ();
	  break;

	  /* shift grid alignment */
	case F_ToggleGrid:
	  {
	    int childX, childY;
	    float oldGrid;

	    gui_get_pointer(&childX, &childY);
	    oldGrid = PCB->Grid;
	    PCB->Grid = 1.0;
	    if (MoveCrosshairAbsolute (TO_PCB_X (childX), TO_PCB_Y (childY)))
	      RestoreCrosshair (False);	/* was hidden by MoveCrosshairAbs */
	    SetGrid (oldGrid, True);
	    break;
	  }

	  /* toggle displaying of the grid */
	case F_Grid:
	  Settings.DrawGrid = !Settings.DrawGrid;
	  UpdateAll ();
	  break;

	  /* display the pinout of an element */
	case F_Pinout:
	  {
	    ElementTypePtr element;
	    void *ptrtmp;

	    if ((SearchScreen
		 (Crosshair.X, Crosshair.Y, ELEMENT_TYPE, &ptrtmp,
		  &ptrtmp, &ptrtmp)) != NO_TYPE)
	      {
	      element = (ElementTypePtr) ptrtmp;
	      gui_pinout_window_show(&Output, element);
	      }
	    break;
	  }

	  /* toggle displaying of pin/pad/via names */
	case F_PinOrPadName:
	  {
	    void *ptr1, *ptr2, *ptr3;

	    switch (SearchScreen (Crosshair.X, Crosshair.Y,
				  ELEMENT_TYPE | PIN_TYPE | PAD_TYPE |
				  VIA_TYPE, (void **) &ptr1, (void **) &ptr2,
				  (void **) &ptr3))
	      {
	      case ELEMENT_TYPE:
		PIN_LOOP ((ElementTypePtr) ptr1);
		{
		  if (TEST_FLAG (DISPLAYNAMEFLAG, pin))
		    ErasePinName (pin);
		  else
		    DrawPinName (pin, 0);
		  AddObjectToFlagUndoList (PIN_TYPE, ptr1, pin, pin);
		  TOGGLE_FLAG (DISPLAYNAMEFLAG, pin);
		}
		END_LOOP;
		PAD_LOOP ((ElementTypePtr) ptr1);
		{
		  if (TEST_FLAG (DISPLAYNAMEFLAG, pad))
		    ErasePadName (pad);
		  else
		    DrawPadName (pad, 0);
		  AddObjectToFlagUndoList (PAD_TYPE, ptr1, pad, pad);
		  TOGGLE_FLAG (DISPLAYNAMEFLAG, pad);
		}
		END_LOOP;
		SetChangedFlag (True);
		IncrementUndoSerialNumber ();
		Draw ();
		break;

	      case PIN_TYPE:
		if (TEST_FLAG (DISPLAYNAMEFLAG, (PinTypePtr) ptr2))
		  ErasePinName ((PinTypePtr) ptr2);
		else
		  DrawPinName ((PinTypePtr) ptr2, 0);
		AddObjectToFlagUndoList (PIN_TYPE, ptr1, ptr2, ptr3);
		TOGGLE_FLAG (DISPLAYNAMEFLAG, (PinTypePtr) ptr2);
		SetChangedFlag (True);
		IncrementUndoSerialNumber ();
		Draw ();
		break;

	      case PAD_TYPE:
		if (TEST_FLAG (DISPLAYNAMEFLAG, (PadTypePtr) ptr2))
		  ErasePadName ((PadTypePtr) ptr2);
		else
		  DrawPadName ((PadTypePtr) ptr2, 0);
		AddObjectToFlagUndoList (PAD_TYPE, ptr1, ptr2, ptr3);
		TOGGLE_FLAG (DISPLAYNAMEFLAG, (PadTypePtr) ptr2);
		SetChangedFlag (True);
		IncrementUndoSerialNumber ();
		Draw ();
		break;
	      case VIA_TYPE:
		if (TEST_FLAG (DISPLAYNAMEFLAG, (PinTypePtr) ptr2))
		  EraseViaName ((PinTypePtr) ptr2);
		else
		  DrawViaName ((PinTypePtr) ptr2, 0);
		AddObjectToFlagUndoList (VIA_TYPE, ptr1, ptr2, ptr3);
		TOGGLE_FLAG (DISPLAYNAMEFLAG, (PinTypePtr) ptr2);
		SetChangedFlag (True);
		IncrementUndoSerialNumber ();
		Draw ();
		break;
	      }
	    break;
	  }
	default:
	  err = 1;
	}
      RestoreCrosshair (True);
    }
  else if (function && str_dir)
    {
      if (GetFunctionID (function) == F_Scroll)
	{
	  /* direction is like keypad, e.g. 4 = left 8 = up */
	  int direction = atoi (str_dir);

	  switch (direction)
	    {
	    case 0:		/* special case: reposition crosshair */
	      {
		int x, y;
		gui_get_pointer(&x, &y);
		if (MoveCrosshairAbsolute (TO_PCB_X (x), TO_PCB_Y (y)))
		  {
		    AdjustAttachedObjects ();
		    set_cursor_position_labels ();
		    RestoreCrosshair (False);
		  }
	      }
	      break;
	    case 1:		/* down, left */
	      CenterDisplay (-Output.Width / 2, Output.Height / 2, True);
	      break;
	    case 2:		/* down */
	      CenterDisplay (0, Output.Height / 2, True);
	      break;
	    case 3:		/* down, right */
	      CenterDisplay (Output.Width / 2, Output.Height / 2, True);
	      break;
	    case 4:		/* left */
	      CenterDisplay (-Output.Width / 2, 0, True);
	      break;
	    case 6:		/* right */
	      CenterDisplay (Output.Width / 2, 0, True);
	      break;
	    case 7:		/* up, left */
	      CenterDisplay (-Output.Width / 2, -Output.Height / 2, True);
	      break;
	    case 8:		/* up */
	      CenterDisplay (0, -Output.Height / 2, True);
	      break;
	    case 9:		/* up, right */
	      CenterDisplay (Output.Width / 2, -Output.Height / 2, True);
	      break;
	    default:
	      Message ("Bad argument (%d) to Display(Scroll)\n", direction);
	      err = 1;
	    }
	}
      else
	err = 1;
    }

  if (!err)
    return;

  Message ("Usage\n"
	   "Display(NameOnPCB|Description|Value)\n"
	   "Display(Grid|Center|ClearAndRedraw|Redraw)\n"
	   "Display(CycleClip|Toggle45Degree|ToggleStartDirection)\n"
	   "Display(ToggleGrid|ToggleRubberBandMode|ToggleUniqueNames)\n"
	   "Display(ToggleMask|ToggleName|ToggleClearLine|ToggleSnapPin)\n"
	   "Display(ToggleThindraw|ToggleOrthoMove|ToggleLocalRef)\n"
	   "Display(ToggleCheckPlanes|ToggleShowDRC|ToggleAutoDRC)\n"
	   "Display(ToggleLiveRoute)\n"
	   "Display(Pinout|PinOrPadName)\n"
	   "Display(Save|Restore)\n" "Display(Scroll, Direction)\n");

}

/* ---------------------------------------------------------------------------
 * action routine to
 *   set a new mode
 *   save the current one or restore the last saved mode
 *   call an appropriate action for the current mode
 * syntax: Mode(Copy|InsertPoint|Line|Move|None|PasteBuffer|Polygon)
 *         Mode(Remove|Rectangle|Text|Via|Arrow|Thermal)
 *         Mode(Notify|Release)
 *         Mode(Save|Restore)
 */
void
ActionMode (char *function)
{
  if (function)
    {
      Note.X = Crosshair.X;
      Note.Y = Crosshair.Y;
      HideCrosshair (True);
      switch (GetFunctionID (function))
	{
	case F_Arc:
	  SetMode (ARC_MODE);
	  break;
	case F_Arrow:
	  SetMode (ARROW_MODE);
	  break;
	case F_Copy:
	  SetMode (COPY_MODE);
	  break;
	case F_InsertPoint:
	  SetMode (INSERTPOINT_MODE);
	  break;
	case F_Line:
	  SetMode (LINE_MODE);
	  break;
	case F_Move:
	  SetMode (MOVE_MODE);
	  break;
	case F_None:
	  SetMode (NO_MODE);
	  break;
	case F_Notify:
	  NotifyMode ();
	  break;
	case F_PasteBuffer:
	  SetMode (PASTEBUFFER_MODE);
	  break;
	case F_Polygon:
	  SetMode (POLYGON_MODE);
	  break;
#ifndef HAVE_LIBSTROKE
	case F_Release:
	  ReleaseMode ();
	  break;
#else
	case F_Release:
	  if (mid_stroke)
	    FinishStroke ();
	  else
	    ReleaseMode ();
	  break;
#endif
	case F_Remove:
	  SetMode (REMOVE_MODE);
	  break;
	case F_Rectangle:
	  SetMode (RECTANGLE_MODE);
	  break;
	case F_Rotate:
	  SetMode (ROTATE_MODE);
	  break;
	case F_Stroke:
#ifdef HAVE_LIBSTROKE
	  mid_stroke = True;
	  StrokeBox.X1 = Crosshair.X;
	  StrokeBox.Y1 = Crosshair.Y;
	  break;
#else
	  /* Handle middle mouse button restarts of drawing mode.  If not in
	  |  a drawing mode, middle mouse button will select objects.
	  */
	  if (Settings.Mode == LINE_MODE
	      && Crosshair.AttachedLine.State != STATE_FIRST)
	    {
	      SetMode (LINE_MODE);
	    }
	  else if (   Settings.Mode == ARC_MODE
	           && Crosshair.AttachedBox.State != STATE_FIRST
	          )
	      SetMode(ARC_MODE);
	  else if (   Settings.Mode == RECTANGLE_MODE
	           && Crosshair.AttachedBox.State != STATE_FIRST
	          )
	      SetMode(RECTANGLE_MODE);
	  else if (   Settings.Mode == POLYGON_MODE
	           && Crosshair.AttachedLine.State != STATE_FIRST
	          )
	      SetMode(POLYGON_MODE);
	  else
	    {
	      SaveMode ();
	      saved_mode = True;
	      SetMode (ARROW_MODE);
	      NotifyMode ();
	    }
	  break;
#endif
	case F_Text:
	  SetMode (TEXT_MODE);
	  break;
	case F_Thermal:
	  SetMode (THERMAL_MODE);
	  break;
	case F_Via:
	  SetMode (VIA_MODE);
	  break;

	case F_Restore:	/* restore the last saved mode */
	  RestoreMode ();
	  break;

	case F_Save:		/* save currently selected mode */
	  SaveMode ();
	  break;
	}
      RestoreCrosshair (True);
      return;
    }

  Message ("Usage\n"
	   "Mode(Copy|InsertPoint|Line|Move|None|PasteBuffer|Polygon)\n"
	   "Mode(Remove|Rectangle|Text|Via|Arrow|Thermal)\n"
	   "Mode(Notify|Release)\n" "Mode(Save|Restore)\n");

}

/* ---------------------------------------------------------------------------
 * action routine to remove objects
 * syntax: RemoveSelected()
 */
void
ActionRemoveSelected (void)
{
	HideCrosshair (True);
	if (RemoveSelected ())
		SetChangedFlag (True);
	RestoreCrosshair (True);
}

/* ---------------------------------------------------------------------------
 * action routine to ripup auto-routed tracks (All|Selected)
 * or smash an element to pieces on the layout
 * syntax: RipUp(All|Selected|Element)
 */
void
ActionRipUp (char *function)
{
  Boolean changed = False;

  if (function)
    {
      HideCrosshair (True);
      switch (GetFunctionID (function))
	{
	case F_All:
	  ALLLINE_LOOP (PCB->Data);
	  {
	    if (TEST_FLAG (AUTOFLAG, line) && !TEST_FLAG (LOCKFLAG, line))
	      {
		RemoveObject (LINE_TYPE, layer, line, line);
		changed = True;
	      }
	  }
	  ENDALL_LOOP;
	  VIA_LOOP (PCB->Data);
	  {
	    if (TEST_FLAG (AUTOFLAG, via) && !TEST_FLAG (LOCKFLAG, via))
	      {
		RemoveObject (VIA_TYPE, via, via, via);
		changed = True;
	      }
	  }
	  END_LOOP;

	  if (changed)
	    {
	      IncrementUndoSerialNumber ();
	      SetChangedFlag (True);
	    }
	  break;
	case F_Selected:
	  VISIBLELINE_LOOP (PCB->Data);
	  {
	    if (TEST_FLAGS (AUTOFLAG | SELECTEDFLAG, line)
		&& !TEST_FLAG (LOCKFLAG, line))
	      {
		RemoveObject (LINE_TYPE, layer, line, line);
		changed = True;
	      }
	  }
	  ENDALL_LOOP;
	  if (PCB->ViaOn)
	    VIA_LOOP (PCB->Data);
	  {
	    if (TEST_FLAGS (AUTOFLAG | SELECTEDFLAG, via)
		&& !TEST_FLAG (LOCKFLAG, via))
	      {
		RemoveObject (VIA_TYPE, via, via, via);
		changed = True;
	      }
	  }
	  END_LOOP;
	  if (changed)
	    {
	      IncrementUndoSerialNumber ();
	      SetChangedFlag (True);
	    }
	  break;
	case F_Element:
	  {
	    void *ptr1, *ptr2, *ptr3;

	    if (SearchScreen (Crosshair.X, Crosshair.Y, ELEMENT_TYPE,
			      &ptr1, &ptr2, &ptr3) != NO_TYPE)
	      {
		Note.Buffer = Settings.BufferNumber;
		SetBufferNumber (MAX_BUFFER - 1);
		ClearBuffer (PASTEBUFFER);
		CopyObjectToBuffer (PASTEBUFFER->Data, PCB->Data,
				    ELEMENT_TYPE, ptr1, ptr2, ptr3);
		SmashBufferElement (PASTEBUFFER);
		PASTEBUFFER->X = 0;
		PASTEBUFFER->Y = 0;
		SaveUndoSerialNumber ();
		EraseObject (ELEMENT_TYPE, ptr1);
		MoveObjectToRemoveUndoList (ELEMENT_TYPE, ptr1, ptr2, ptr3);
		RestoreUndoSerialNumber ();
		CopyPastebufferToLayout (0, 0);
		SetBufferNumber (Note.Buffer);
		SetChangedFlag (True);
	      }
	  }
	  break;
	}
      RestoreCrosshair (True);
    }
}

/* ---------------------------------------------------------------------------
 * action routine to add rat lines
 * syntax: AddRats(AllRats|SelectedRats|Close)
 * The Close argument selects the shortest unselect rat on the board
 */
void
ActionAddRats (char *function)
{
  RatTypePtr shorty;
  float len, small;

  if (function)
    {
      HideCrosshair (True);
      gui_watch_cursor ();
      switch (GetFunctionID (function))
	{
	case F_AllRats:
	  if (AddAllRats (False, NULL))
	    SetChangedFlag (True);
	  break;
	case F_SelectedRats:
	case F_Selected:
	  if (AddAllRats (True, NULL))
	    SetChangedFlag (True);
	  break;
	case F_Close:
	  small = SQUARE (MAX_COORD);
	  shorty = NULL;
	  RAT_LOOP (PCB->Data);
	  {
	    if (TEST_FLAG (SELECTEDFLAG, line))
	      continue;
	    len = SQUARE (line->Point1.X - line->Point2.X) +
	      SQUARE (line->Point1.Y - line->Point2.Y);
	    if (len < small)
	      {
		small = len;
		shorty = line;
	      }
	  }
	  END_LOOP;
	  if (shorty)
	    {
	      AddObjectToFlagUndoList (RATLINE_TYPE, shorty, shorty, shorty);
	      SET_FLAG (SELECTEDFLAG, shorty);
	      DrawRat (shorty, 0);
	      Draw ();
	      CenterDisplay ((shorty->Point2.X + shorty->Point1.X) / 2,
			     (shorty->Point2.Y + shorty->Point1.Y) / 2,
			     False);
	    }
	  break;
	}
      gui_restore_cursor ();
      RestoreCrosshair (True);
    }
}

/* ---------------------------------------------------------------------------
 * action routine to delete rat lines
 * syntax: DeleteRats(AllRats|SelectedRats)
 */
void
ActionDeleteRats (char *function)
{
  if (function)
    {
      HideCrosshair (True);
      switch (GetFunctionID (function))
	{
	case F_AllRats:
	  if (DeleteRats (False))
	    SetChangedFlag (True);
	  break;
	case F_SelectedRats:
	case F_Selected:
	  if (DeleteRats (True))
	    SetChangedFlag (True);
	  break;
	}
      RestoreCrosshair (True);
    }
}

/* ---------------------------------------------------------------------------
 * action routine to auto-place selected components.
 * syntax: AutoPlaceSelected()
 */
void
ActionAutoPlaceSelected (void)
{
  if (gui_dialog_confirm(_("Auto-placement can NOT be undone.\n"
		     "Do you want to continue anyway?\n")))
    {
      HideCrosshair (True);
      gui_watch_cursor ();
      if (AutoPlaceSelected ())
	SetChangedFlag (True);
      gui_restore_cursor ();
      RestoreCrosshair (True);
    }
}

/* ---------------------------------------------------------------------------
 * action routine to auto-route rat lines.
 * syntax: AutoRoute(AllRats|SelectedRats)
 */
void
ActionAutoRoute (char *function)
{
  if (function)		/* one parameter */
    {
      HideCrosshair (True);
      gui_watch_cursor ();
      switch (GetFunctionID (function))
	{
	case F_AllRats:
	  if (AutoRoute (False))
	    SetChangedFlag (True);
	  break;
	case F_SelectedRats:
	case F_Selected:
	  if (AutoRoute (True))
	    SetChangedFlag (True);
	  break;
	}
      gui_restore_cursor ();
      RestoreCrosshair (True);
    }
}

/* ---------------------------------------------------------------------------
 * Set/Reset the Crosshair mark
 * syntax: MarkCrosshair(|Center)
 */
void
ActionMarkCrosshair (char *function)
{
  if (!function || !*function)
    {
      if (Marked.status)
	{
	  DrawMark (True);
	  Marked.status = False;
	}
      else
	{
	  Marked.status = True;
	  Marked.X = Crosshair.X;
	  Marked.Y = Crosshair.Y;
	  DrawMark (False);
	}
      set_cursor_position_labels ();
    }
  else if (GetFunctionID (function) == F_Center)
    {
      DrawMark (True);
      Marked.status = True;
      Marked.X = Crosshair.X;
      Marked.Y = Crosshair.Y;
      DrawMark (False);
      set_cursor_position_labels ();
    }
}

/* ---------------------------------------------------------------------------
 * changes the size of objects
 * syntax: ChangeSize(Object, delta)
 *         ChangeSize(SelectedLines|SelectedPins|SelectedVias, delta)
 *         ChangeSize(SelectedPads|SelectedTexts|SelectedNames, delta)
 *	   ChangeSize(SelectedElements, delta)
 */
void
ActionChangeSize (char *function, char *delta, char *units)
{
  Boolean r;			/* indicates if absolute size is given */
  float value;

  if (function && delta)
    {
      value = GetValue (delta, units, &r);
      HideCrosshair (True);
      switch (GetFunctionID (function))
	{
	case F_Object:
	  {
	    int type;
	    void *ptr1, *ptr2, *ptr3;

	    if ((type =
		 SearchScreen (Crosshair.X, Crosshair.Y, CHANGESIZE_TYPES,
			       &ptr1, &ptr2, &ptr3)) != NO_TYPE)
	      if (TEST_FLAG (LOCKFLAG, (PinTypePtr) ptr2))
		Message (_("Sorry, the object is locked\n"));
	    if (ChangeObjectSize (type, ptr1, ptr2, ptr3, value, r))
	      SetChangedFlag (True);
	    break;
	  }

	case F_SelectedVias:
	  if (ChangeSelectedSize (VIA_TYPE, value, r))
	    SetChangedFlag (True);
	  break;

	case F_SelectedPins:
	  if (ChangeSelectedSize (PIN_TYPE, value, r))
	    SetChangedFlag (True);
	  break;

	case F_SelectedPads:
	  if (ChangeSelectedSize (PAD_TYPE, value, r))
	    SetChangedFlag (True);
	  break;

	case F_SelectedLines:
	  if (ChangeSelectedSize (LINE_TYPE, value, r))
	    SetChangedFlag (True);
	  break;

	case F_SelectedTexts:
	  if (ChangeSelectedSize (TEXT_TYPE, value, r))
	    SetChangedFlag (True);
	  break;

	case F_SelectedNames:
	  if (ChangeSelectedSize (ELEMENTNAME_TYPE, value, r))
	    SetChangedFlag (True);
	  break;

	case F_SelectedElements:
	  if (ChangeSelectedSize (ELEMENT_TYPE, value, r))
	    SetChangedFlag (True);
	  break;

	case F_Selected:
	case F_SelectedObjects:
	  if (ChangeSelectedSize (CHANGESIZE_TYPES, value, r))
	    SetChangedFlag (True);
	  break;
	}
      RestoreCrosshair (True);
    }
}

/* ---------------------------------------------------------------------------
 * changes the drilling hole size of objects
 * syntax: ChangeDrillSize(Object, delta)
 *         ChangeDrillSize(SelectedPins|SelectedVias|Selected|SelectedObjects, delta)
 */
void
ActionChange2ndSize (char *function, char *delta, char *units)
{
  Boolean r;
  float value;

  if (function && delta)
    {
      value = GetValue (delta, units, &r);
      HideCrosshair (True);
      switch (GetFunctionID (function))
	{
	case F_Object:
	  {
	    int type;
	    void *ptr1, *ptr2, *ptr3;

	    if ((type =
		 SearchScreen (Crosshair.X, Crosshair.Y, CHANGE2NDSIZE_TYPES,
			       &ptr1, &ptr2, &ptr3)) != NO_TYPE)
	      if (ChangeObject2ndSize (type, ptr1, ptr2, ptr3, value, r, True))
		SetChangedFlag (True);
	    break;
	  }

	case F_SelectedVias:
	  if (ChangeSelected2ndSize (VIA_TYPE, value, r))
	    SetChangedFlag (True);
	  break;

	case F_SelectedPins:
	  if (ChangeSelected2ndSize (PIN_TYPE, value, r))
	    SetChangedFlag (True);
	  break;
	case F_Selected:
	case F_SelectedObjects:
	  if (ChangeSelected2ndSize (PIN_TYPES, value, r))
	    SetChangedFlag (True);
	  break;
	}
      RestoreCrosshair (True);
    }
}

/* ---------------------------------------------------------------------------
 * changes the clearance size of objects
 * syntax: ChangeClearSize(Object, delta)
 *         ChangeClearSize(SelectedPins|SelectedPads|SelectedVias, delta)
 *	   ChangeClearSize(SelectedLines|SelectedArcs, delta
 *	   ChangeClearSize(Selected|SelectedObjects, delta)
 */
void
ActionChangeClearSize (char *function, char *delta, char *units)
{
  Boolean r;
  float value;

  if (function && delta)
    {
      value = 2 * GetValue (delta, units, &r);
      HideCrosshair (True);
      switch (GetFunctionID (function))
	{
	case F_Object:
	  {
	    int type;
	    void *ptr1, *ptr2, *ptr3;

	    if ((type =
		 SearchScreen (Crosshair.X, Crosshair.Y,
			       CHANGECLEARSIZE_TYPES, &ptr1, &ptr2,
			       &ptr3)) != NO_TYPE)
	      if (ChangeObjectClearSize (type, ptr1, ptr2, ptr3, value, r))
		SetChangedFlag (True);
	    break;
	  }
	case F_SelectedVias:
	  if (ChangeSelectedClearSize (VIA_TYPE, value, r))
	    SetChangedFlag (True);
	  break;
	case F_SelectedPads:
	  if (ChangeSelectedClearSize (PAD_TYPE, value, r))
	    SetChangedFlag (True);
	  break;
	case F_SelectedPins:
	  if (ChangeSelectedClearSize (PIN_TYPE, value, r))
	    SetChangedFlag (True);
	  break;
	case F_SelectedLines:
	  if (ChangeSelectedClearSize (LINE_TYPE, value, r))
	    SetChangedFlag (True);
	  break;
	case F_SelectedArcs:
	  if (ChangeSelectedClearSize (ARC_TYPE, value, r))
	    SetChangedFlag (True);
	  break;
	case F_Selected:
	case F_SelectedObjects:
	  if (ChangeSelectedClearSize (CHANGECLEARSIZE_TYPES, value, r))
	    SetChangedFlag (True);
	  break;
	}
      RestoreCrosshair (True);
    }
}

/* ---------------------------------------------------------------------------
 * sets the name of a specific pin on a specific instance
 * syntax: ChangePinName(ElementName, PinNumber, PinName)
 * example: ChangePinName(U3, 7, VCC)
 *
 * This can be especially useful for annotating pin names from
 * a schematic to the layout without requiring knowledge of
 * the pcb file format.
 */
void
ActionChangePinName (char *refdes, char *pinnum, char *pinname)
{
  int changed = 0;
  
  /* Strip leading white space */
  while (refdes  && (*refdes  != '\0') && (*refdes  == ' '))
    refdes++;
  while (pinnum  && (*pinnum  != '\0') && (*pinnum  == ' '))
    pinnum++;
  while (pinname && (*pinname != '\0') && (*pinname == ' '))
    pinname++;

  if ((refdes != NULL) && (pinnum != NULL) && (pinname != NULL))
    {
      ELEMENT_LOOP (PCB->Data);
      {
	if( NSTRCMP (refdes, NAMEONPCB_NAME (element)) == 0)
	  {
	    PIN_LOOP (element);
	    {
	      if (NSTRCMP (pinnum, pin->Number) == 0)
		{
		  AddObjectToChangeNameUndoList (PIN_TYPE, NULL, NULL, 
						 pin, pin->Name);
		  /*
		   * Note:  we can't free() pin->Name first because 
		   * it is used in the undo list
		   */
		  pin->Name = strdup (pinname);
		  SetChangedFlag (True);
		  changed = 1;
		}
	    }
	    END_LOOP;
	    
	    PAD_LOOP (element);
	    {
	      if (NSTRCMP (pinnum, pad->Number) == 0)
		{
		  AddObjectToChangeNameUndoList (PAD_TYPE, NULL, NULL, 
						 pad, pad->Name);
		  /* 
		   * Note:  we can't free() pad->Name first because 
		   * it is used in the undo list
		   */
		  pad->Name = strdup (pinname);
		  SetChangedFlag (True);
		  changed = 1;
		}
	    }
	    END_LOOP;
	  }
      }
      END_LOOP;
      /*
       * done with our action so increment the undo # if we 
       * actually changed anything
       */
      if (changed)
	IncrementUndoSerialNumber ();
    }
  else 
    {
      Message ("Usage:  ChangePinName(RefDes, PinNumber, PinName)\n");
    }
  
}

/* ---------------------------------------------------------------------------
 * sets the name of objects
 * syntax: ChangeName(Object)
 *         ChangeName(Layout|Layer)
 */
void
ActionChangeName (char *function)
{
  char *name;

  if (function)
    {
      HideCrosshair (True);
      switch (GetFunctionID (function))
	{
	  /* change the name of an object */
	case F_Object:
	  {
	    int type;
	    void *ptr1, *ptr2, *ptr3;

	    if ((type =
		 SearchScreen (Crosshair.X, Crosshair.Y, CHANGENAME_TYPES,
			       &ptr1, &ptr2, &ptr3)) != NO_TYPE)
	      {
		SaveUndoSerialNumber ();
		if (QueryInputAndChangeObjectName (type, ptr1, ptr2, ptr3))
		  {
		    SetChangedFlag (True);
		    if (type == ELEMENT_TYPE)
		      {
			RubberbandTypePtr ptr;
			int i;

			RestoreUndoSerialNumber ();
			Crosshair.AttachedObject.RubberbandN = 0;
			LookupRatLines (type, ptr1, ptr2, ptr3);
			ptr = Crosshair.AttachedObject.Rubberband;
			for (i = 0; i < Crosshair.AttachedObject.RubberbandN;
			     i++, ptr++)
			  {
			    if (PCB->RatOn)
			      EraseRat ((RatTypePtr) ptr->Line);
			    MoveObjectToRemoveUndoList (RATLINE_TYPE,
							ptr->Line, ptr->Line,
							ptr->Line);
			  }
			IncrementUndoSerialNumber ();
			Draw ();
		      }
		  }
	      }
	    break;
	  }

	  /* change the layouts name */
	case F_Layout:
	  name = gui_dialog_input(_("Enter the layout name:"), EMPTY (PCB->Name));
	  if (name && ChangeLayoutName (name))	/* XXX memory leak */
	    SetChangedFlag (True);
	  break;

	  /* change the name of the activ layer */
	case F_Layer:
	  name = gui_dialog_input(_("Enter the layer name:"),
			       EMPTY (CURRENT->Name));
	  if (name && ChangeLayerName (CURRENT, name))	/* XXX memory leak */
	    SetChangedFlag (True);
	  break;
	}
      RestoreCrosshair (True);
    }
}


/* ---------------------------------------------------------------------------
 * toggles the visibility of element names 
 * syntax: ToggleHideName(Object|SelectedElements)
 */
void
ActionToggleHideName (char *function)
{
  if (function && PCB->ElementOn)
    {
      HideCrosshair (True);
      switch (GetFunctionID (function))
	{
	case F_Object:
	  {
	    int type;
	    void *ptr1, *ptr2, *ptr3;

	    if ((type = SearchScreen (Crosshair.X, Crosshair.Y, ELEMENT_TYPE,
				      &ptr1, &ptr2, &ptr3)) != NO_TYPE)
	      {
		AddObjectToFlagUndoList (type, ptr1, ptr2, ptr3);
		EraseElementName ((ElementTypePtr) ptr2);
		TOGGLE_FLAG (HIDENAMEFLAG, (ElementTypePtr) ptr2);
		DrawElementName ((ElementTypePtr) ptr2, 0);
		Draw ();
		IncrementUndoSerialNumber ();
	      }
	    break;
	  }
	case F_SelectedElements:
	case F_Selected:
	  {
	    Boolean changed = False;
	    ELEMENT_LOOP (PCB->Data);
	    {
	      if ((TEST_FLAG (SELECTEDFLAG, element) ||
		   TEST_FLAG (SELECTEDFLAG,
			      &NAMEONPCB_TEXT (element)))
		  && (FRONT (element) || PCB->InvisibleObjectsOn))
		{
		  AddObjectToFlagUndoList (ELEMENT_TYPE, element,
					   element, element);
		  EraseElementName (element);
		  TOGGLE_FLAG (HIDENAMEFLAG, element);
		  DrawElementName (element, 0);
		  changed = True;
		}
	    }
	    END_LOOP;
	    if (changed)
	      {
		Draw ();
		IncrementUndoSerialNumber ();
	      }
	  }
	}
      RestoreCrosshair (True);
    }
}

/* ---------------------------------------------------------------------------
 * changes the join (clearance through polygons) of objects
 * syntax: ChangeJoin(ToggleObject|SelectedLines|SelectedArcs|Selected)
 */
void
ActionChangeJoin (char *function)
{
  if (function)
    {
      HideCrosshair (True);
      switch (GetFunctionID (function))
	{
	case F_ToggleObject:
	case F_Object:
	  {
	    int type;
	    void *ptr1, *ptr2, *ptr3;

	    if ((type =
		 SearchScreen (Crosshair.X, Crosshair.Y, CHANGEJOIN_TYPES,
			       &ptr1, &ptr2, &ptr3)) != NO_TYPE)
	      if (ChangeObjectJoin (type, ptr1, ptr2, ptr3))
		SetChangedFlag (True);
	    break;
	  }

	case F_SelectedLines:
	  if (ChangeSelectedJoin (LINE_TYPE))
	    SetChangedFlag (True);
	  break;

	case F_SelectedArcs:
	  if (ChangeSelectedJoin (ARC_TYPE))
	    SetChangedFlag (True);
	  break;

	case F_Selected:
	case F_SelectedObjects:
	  if (ChangeSelectedJoin (CHANGEJOIN_TYPES))
	    SetChangedFlag (True);
	  break;
	}
      RestoreCrosshair (True);
    }
}

/* ---------------------------------------------------------------------------
 * changes the square-flag of objects
 * syntax: ChangeSquare(ToggleObject|SelectedElements|SelectedPins)
 */
void
ActionChangeSquare (char *function)
{
  if (function)
    {
      /* HideCrosshair (True); */
      switch (GetFunctionID (function))
	{
	case F_ToggleObject:
	case F_Object:
	  {
	    int type;
	    void *ptr1, *ptr2, *ptr3;

	    if ((type =
		 SearchScreen (Crosshair.X, Crosshair.Y, CHANGESQUARE_TYPES,
			       &ptr1, &ptr2, &ptr3)) != NO_TYPE)
	      if (ChangeObjectSquare (type, ptr1, ptr2, ptr3))
		SetChangedFlag (True);
	    break;
	  }

	case F_SelectedElements:
	  if (ChangeSelectedSquare (ELEMENT_TYPE))
	    SetChangedFlag (True);
	  break;

	case F_SelectedPins:
	  if (ChangeSelectedSquare (PIN_TYPE | PAD_TYPE))
	    SetChangedFlag (True);
	  break;

	case F_Selected:
	case F_SelectedObjects:
	  if (ChangeSelectedSquare (PIN_TYPE | PAD_TYPE))
	    SetChangedFlag (True);
	  break;
	}
      /* RestoreCrosshair (True); */
    }
}

/* ---------------------------------------------------------------------------
 * sets the square-flag of objects
 * syntax: SetSquare(ToggleObject|SelectedElements|SelectedPins)
 */
void
ActionSetSquare (char *function)
{
  if (function && *function)
    {
      /* HideCrosshair (True); */
      switch (GetFunctionID (function))
	{
	case F_ToggleObject:
	case F_Object:
	  {
	    int type;
	    void *ptr1, *ptr2, *ptr3;

	    if ((type =
		 SearchScreen (Crosshair.X, Crosshair.Y, CHANGESQUARE_TYPES,
			       &ptr1, &ptr2, &ptr3)) != NO_TYPE)
	      if (SetObjectSquare (type, ptr1, ptr2, ptr3))
		SetChangedFlag (True);
	    break;
	  }

	case F_SelectedElements:
	  if (SetSelectedSquare (ELEMENT_TYPE))
	    SetChangedFlag (True);
	  break;

	case F_SelectedPins:
	  if (SetSelectedSquare (PIN_TYPE | PAD_TYPE))
	    SetChangedFlag (True);
	  break;

	case F_Selected:
	case F_SelectedObjects:
	  if (SetSelectedSquare (PIN_TYPE | PAD_TYPE))
	    SetChangedFlag (True);
	  break;
	}
      /* RestoreCrosshair (True); */
    }
}

/* ---------------------------------------------------------------------------
 * clears the square-flag of objects
 * syntax: ClearSquare(ToggleObject|SelectedElements|SelectedPins)
 */
void
ActionClearSquare (char *function)
{
  if (function && *function)
    {
      /* HideCrosshair (True); */
      switch (GetFunctionID (function))
	{
	case F_ToggleObject:
	case F_Object:
	  {
	    int type;
	    void *ptr1, *ptr2, *ptr3;

	    if ((type =
		 SearchScreen (Crosshair.X, Crosshair.Y, CHANGESQUARE_TYPES,
			       &ptr1, &ptr2, &ptr3)) != NO_TYPE)
	      if (ClrObjectSquare (type, ptr1, ptr2, ptr3))
		SetChangedFlag (True);
	    break;
	  }

	case F_SelectedElements:
	  if (ClrSelectedSquare (ELEMENT_TYPE))
	    SetChangedFlag (True);
	  break;

	case F_SelectedPins:
	  if (ClrSelectedSquare (PIN_TYPE | PAD_TYPE))
	    SetChangedFlag (True);
	  break;

	case F_Selected:
	case F_SelectedObjects:
	  if (ClrSelectedSquare (PIN_TYPE | PAD_TYPE))
	    SetChangedFlag (True);
	  break;
	}
      /* RestoreCrosshair (True); */
    }
}

/* ---------------------------------------------------------------------------
 * changes the octagon-flag of objects
 * syntax: ChangeOctagon(ToggleObject|SelectedElements|Selected)
 */
void
ActionChangeOctagon (char *function)
{
  if (function)
    {
      /* HideCrosshair (True); */
      switch (GetFunctionID (function))
	{
	case F_ToggleObject:
	case F_Object:
	  {
	    int type;
	    void *ptr1, *ptr2, *ptr3;

	    if ((type =
		 SearchScreen (Crosshair.X, Crosshair.Y, CHANGEOCTAGON_TYPES,
			       &ptr1, &ptr2, &ptr3)) != NO_TYPE)
	      if (ChangeObjectOctagon (type, ptr1, ptr2, ptr3))
		SetChangedFlag (True);
	    break;
	  }

	case F_SelectedElements:
	  if (ChangeSelectedOctagon (ELEMENT_TYPE))
	    SetChangedFlag (True);
	  break;

	case F_SelectedPins:
	  if (ChangeSelectedOctagon (PIN_TYPE))
	    SetChangedFlag (True);
	  break;

	case F_SelectedVias:
	  if (ChangeSelectedOctagon (VIA_TYPE))
	    SetChangedFlag (True);
	  break;

	case F_Selected:
	case F_SelectedObjects:
	  if (ChangeSelectedOctagon (PIN_TYPES))
	    SetChangedFlag (True);
	  break;
	}
      /* RestoreCrosshair (True); */
    }
}

/* ---------------------------------------------------------------------------
 * sets the octagon-flag of objects
 * syntax: ChangeOctagon(ToggleObject|SelectedElements|Selected)
 */
void
ActionSetOctagon (char *function)
{
  if (function)
    {
      /* HideCrosshair (True); */
      switch (GetFunctionID (function))
	{
	case F_ToggleObject:
	case F_Object:
	  {
	    int type;
	    void *ptr1, *ptr2, *ptr3;

	    if ((type =
		 SearchScreen (Crosshair.X, Crosshair.Y, CHANGEOCTAGON_TYPES,
			       &ptr1, &ptr2, &ptr3)) != NO_TYPE)
	      if (SetObjectOctagon (type, ptr1, ptr2, ptr3))
		SetChangedFlag (True);
	    break;
	  }

	case F_SelectedElements:
	  if (SetSelectedOctagon (ELEMENT_TYPE))
	    SetChangedFlag (True);
	  break;

	case F_SelectedPins:
	  if (SetSelectedOctagon (PIN_TYPE))
	    SetChangedFlag (True);
	  break;

	case F_SelectedVias:
	  if (SetSelectedOctagon (VIA_TYPE))
	    SetChangedFlag (True);
	  break;

	case F_Selected:
	case F_SelectedObjects:
	  if (SetSelectedOctagon (PIN_TYPES))
	    SetChangedFlag (True);
	  break;
	}
      /* RestoreCrosshair (True); */
    }
}

/* ---------------------------------------------------------------------------
 * clears the octagon-flag of objects
 * syntax: ClearOctagon(ToggleObject|SelectedElements|Selected)
 */
void
ActionClearOctagon (char *function)
{
  if (function)
    {
      /* HideCrosshair (True); */
      switch (GetFunctionID (function))
	{
	case F_ToggleObject:
	case F_Object:
	  {
	    int type;
	    void *ptr1, *ptr2, *ptr3;

	    if ((type =
		 SearchScreen (Crosshair.X, Crosshair.Y, CHANGEOCTAGON_TYPES,
			       &ptr1, &ptr2, &ptr3)) != NO_TYPE)
	      if (ClrObjectOctagon (type, ptr1, ptr2, ptr3))
		SetChangedFlag (True);
	    break;
	  }

	case F_SelectedElements:
	  if (ClrSelectedOctagon (ELEMENT_TYPE))
	    SetChangedFlag (True);
	  break;

	case F_SelectedPins:
	  if (ClrSelectedOctagon (PIN_TYPE))
	    SetChangedFlag (True);
	  break;

	case F_SelectedVias:
	  if (ClrSelectedOctagon (VIA_TYPE))
	    SetChangedFlag (True);
	  break;

	case F_Selected:
	case F_SelectedObjects:
	  if (ClrSelectedOctagon (PIN_TYPES))
	    SetChangedFlag (True);
	  break;
	}
      /* RestoreCrosshair (True); */
    }
}

/* ---------------------------------------------------------------------------
 * changes the hole-flag of objects
 * syntax: ChangeHole(ToggleObject|SelectedVias|Selected)
 */
void
ActionChangeHole (char *function)
{
  if (function)
    {
      /* HideCrosshair (True); */
      switch (GetFunctionID (function))
	{
	case F_ToggleObject:
	case F_Object:
	  {
	    int type;
	    void *ptr1, *ptr2, *ptr3;

	    if ((type = SearchScreen (Crosshair.X, Crosshair.Y, VIA_TYPE,
				      &ptr1, &ptr2, &ptr3)) != NO_TYPE
		&& ChangeHole ((PinTypePtr) ptr3))
	      IncrementUndoSerialNumber ();
	    break;
	  }

	case F_SelectedVias:
	case F_Selected:
	  if (ChangeSelectedHole ())
	    SetChangedFlag (True);
	  break;
	}
      /* RestoreCrosshair (True); */
    }
}

/* ---------------------------------------------------------------------------
 * toggles the selection of the object at the pointer location
 * or sets it if 'All', 'Block' or 'Connection' is passed
 * syntax: Select(ToggleObject)
 *         Select(All|Block|Connection)
 *         Select(ElementByName|ObjectByName|PadByName|PinByName)
 *         Select(TextByName|ViaByName)
 *         Select(Convert)
 */
void
ActionSelect (char *function)
{
  if (function)
    {

      HideCrosshair (True);
      switch (GetFunctionID (function))
	{
#if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
	  int type;
	  /* select objects by their names */
	case F_ElementByName:
	  type = ELEMENT_TYPE;
	  goto commonByName;
	case F_ObjectByName:
	  type = ALL_TYPES;
	  goto commonByName;
	case F_PadByName:
	  type = PAD_TYPE;
	  goto commonByName;
	case F_PinByName:
	  type = PIN_TYPE;
	  goto commonByName;
	case F_TextByName:
	  type = TEXT_TYPE;
	  goto commonByName;
	case F_ViaByName:
	  type = VIA_TYPE;
	  goto commonByName;

	commonByName:
	  {
	    char *pattern;

	    if ((pattern = gui_dialog_input(_("Enter pattern:"), "")) != NULL)
	      {
	      if (SelectObjectByName (type, pattern))
	         SetChangedFlag (True);
          g_free(pattern);
	      }
	    break;
	  }
#endif /* defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP) */

	  /* select a single object */
	case F_ToggleObject:
	case F_Object:
	  if (SelectObject ())
	    SetChangedFlag (True);
	  break;

	  /* all objects in block */
	case F_Block:
	  {
	    BoxType box;

	    box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
			  Crosshair.AttachedBox.Point2.X);
	    box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
			  Crosshair.AttachedBox.Point2.Y);
	    box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
			  Crosshair.AttachedBox.Point2.X);
	    box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
			  Crosshair.AttachedBox.Point2.Y);
	    NotifyBlock ();
	    if (Crosshair.AttachedBox.State == STATE_THIRD &&
		SelectBlock (&box, True))
	      {
		SetChangedFlag (True);
		Crosshair.AttachedBox.State = STATE_FIRST;
	      }
	    break;
	  }

	  /* select all visible objects */
	case F_All:
	  {
	    BoxType box;

	    box.X1 = -MAX_COORD;
	    box.Y1 = -MAX_COORD;
	    box.X2 = MAX_COORD;
	    box.Y2 = MAX_COORD;
	    if (SelectBlock (&box, True))
	      SetChangedFlag (True);
	    break;
	  }

	  /* all found connections */
	case F_Connection:
	  if (SelectConnection (True))
	    {
	      IncrementUndoSerialNumber ();
	      SetChangedFlag (True);
	    }
	  break;

	case F_Convert:
	  Note.Buffer = Settings.BufferNumber;
	  SetBufferNumber (MAX_BUFFER - 1);
	  ClearBuffer (PASTEBUFFER);
	  AddSelectedToBuffer (PASTEBUFFER, Crosshair.X, Crosshair.Y, True);
	  SaveUndoSerialNumber ();
	  RemoveSelected ();
	  ConvertBufferToElement (PASTEBUFFER);
	  RestoreUndoSerialNumber ();
	  CopyPastebufferToLayout (Crosshair.X, Crosshair.Y);
	  SetBufferNumber (Note.Buffer);
	  break;
	}
      RestoreCrosshair (True);
    }
}

/* FLAG(have_regex,FlagHaveRegex,0) */
int 
FlagHaveRegex (int parm)
{
#if defined(HAVE_REGCOMP) || defined(HAVE_RE_COMP)
  return 1;
#else
  return 0;
#endif
}

/* ---------------------------------------------------------------------------
 * unselects the object at the pointer location
 * syntax: Unselect(All|Block|Connection)
 */
void
ActionUnselect (char *function)
{
  if (function)
    {
      HideCrosshair (True);
      switch (GetFunctionID (function))
	{
	  /* all objects in block */
	case F_Block:
	  {
	    BoxType box;

	    box.X1 = MIN (Crosshair.AttachedBox.Point1.X,
			  Crosshair.AttachedBox.Point2.X);
	    box.Y1 = MIN (Crosshair.AttachedBox.Point1.Y,
			  Crosshair.AttachedBox.Point2.Y);
	    box.X2 = MAX (Crosshair.AttachedBox.Point1.X,
			  Crosshair.AttachedBox.Point2.X);
	    box.Y2 = MAX (Crosshair.AttachedBox.Point1.Y,
			  Crosshair.AttachedBox.Point2.Y);
	    NotifyBlock ();
	    if (Crosshair.AttachedBox.State == STATE_THIRD &&
		SelectBlock (&box, False))
	      {
		SetChangedFlag (True);
		Crosshair.AttachedBox.State = STATE_FIRST;
	      }
	    break;
	  }

	  /* unselect all visible objects */
	case F_All:
	  {
	    BoxType box;

	    box.X1 = -MAX_COORD;
	    box.Y1 = -MAX_COORD;
	    box.X2 = MAX_COORD;
	    box.Y2 = MAX_COORD;
	    if (SelectBlock (&box, False))
	      SetChangedFlag (True);
	    break;
	  }

	  /* all found connections */
	case F_Connection:
	  if (SelectConnection (False))
	    {
	      IncrementUndoSerialNumber ();
	      SetChangedFlag (True);
	    }
	  break;
	}
      RestoreCrosshair (True);
    }
}

/* ---------------------------------------------------------------------------
 * saves data to file
 * syntax: Save(Layout|LayoutAs)
 *         Save(AllConnections|AllUnusedPins|ElementConnections)
 */
void
ActionSave (char *function)
{
  char *name;
  static gchar	*current_save_path = NULL;

  if (function)
    switch (GetFunctionID (function))
      {
	/* save layout; use its original file name
	 * or 'fall thru' to next routine
	 */
      case F_Layout:
	if (PCB->Filename)
	  {
	    SavePCB (PCB->Filename);
	    break;
	  }

	/* save data to any file */
      case F_LayoutAs:
	name = gui_dialog_file_select_save(_("Save layout as:"),
						&current_save_path, PCB->Filename,
						Settings.FilePath);
	if (name)
	  {
	    FILE *exist;

	    if ((exist = fopen (name, "r")))
	      {
		fclose (exist);
		if (gui_dialog_confirm(_("File exists!  Ok to overwrite?")))
		  SavePCB (name);
	      }
	    else
	      SavePCB (name);
	    g_free(name);
	  }
	break;

	/* save all connections to file */
      case F_AllConnections:
	{
	  FILE *fp;

	  if ((fp = OpenConnectionDataFile ()) != NULL)
	    {
	      gui_watch_cursor ();
	      LookupConnectionsToAllElements (fp);
	      fclose (fp);
	      gui_restore_cursor ();
	      SetChangedFlag (True);
	    }
	  break;
	}

	/* save all unused pins to file */
      case F_AllUnusedPins:
	{
	  FILE *fp;

	  if ((fp = OpenConnectionDataFile ()) != NULL)
	    {
	      gui_watch_cursor ();
	      LookupUnusedPins (fp);
	      fclose (fp);
	      gui_restore_cursor ();
	      SetChangedFlag (True);
	    }
	  break;
	}

	/* save all connections to a file */
      case F_ElementConnections:
	{
	  ElementTypePtr element;
	  void *ptrtmp;
	  FILE *fp;

	  if ((SearchScreen (Crosshair.X, Crosshair.Y, ELEMENT_TYPE,
			     &ptrtmp,
			     &ptrtmp,
			     &ptrtmp)) != NO_TYPE)
	    {
	      element = (ElementTypePtr) ptrtmp;
	      if ((fp = OpenConnectionDataFile ()) != NULL)
		{
		  gui_watch_cursor ();
		  LookupElementConnections (element, fp);
		  fclose (fp);
		  gui_restore_cursor ();
		  SetChangedFlag (True);
		}
	    }
	  break;
	}
      }
}

/* ---------------------------------------------------------------------------
 * load data
 * syntax: Load(ElementToBuffer|Layout|LayoutToBuffer|Netlist|Revert)
 */
void
ActionLoad (char *function)
{
  char *name;
  static gchar	*current_element_dir = NULL,
				*current_layout_dir = NULL,
				*current_netlist_dir = NULL;
	char fname[256];

  if (function)
    {
      HideCrosshair (True);
      switch (GetFunctionID (function))
	{
	  /* load element data into buffer */
	case F_ElementToBuffer:
	  name = gui_dialog_file_select_open(_("Load element to buffer"),
						&current_element_dir, Settings.LibraryTree);
	  if (name && LoadElementToBuffer (PASTEBUFFER, name, True))
	    SetMode (PASTEBUFFER_MODE);
	  g_free(name);
	  break;

	  /* load PCB data into buffer */
	case F_LayoutToBuffer:
	  name = gui_dialog_file_select_open(_("Load layout file to buffer"),
						&current_layout_dir, Settings.FilePath);
	  if (name && LoadLayoutToBuffer (PASTEBUFFER, name))
	    SetMode (PASTEBUFFER_MODE);
	  g_free(name);
	  break;

	  /* load new data */
	case F_Layout:
	  name = gui_dialog_file_select_open(_("Load layout file"),
						&current_layout_dir, Settings.FilePath);
	  if (name)
	    if (!PCB->Changed ||
		gui_dialog_confirm(_("OK to override layout data?")))
	      LoadPCB (name);
	  g_free(name);
	  break;
	case F_Netlist:
	  name = gui_dialog_file_select_open(_("Load netlist file"),
						&current_netlist_dir, Settings.FilePath);

	  if (name)
	  {
	    if (PCB->Netlistname)
	      SaveFree (PCB->Netlistname);
	    PCB->Netlistname = StripWhiteSpaceAndDup (name);
	    FreeLibraryMemory (&PCB->NetlistLib);
	    if (!ReadNetlist (PCB->Netlistname))
	      gui_netlist_window_show(&Output);
	  g_free(name);
	  }
	  break;
	 case F_Revert:
		 if (!PCB->Filename || PCB->Changed && !gui_dialog_confirm(_("OK to override changes?")))
		 	break;
				strcpy(fname,PCB->Filename);	/*Calling LoadPCB(PCB->Filename) changes the content of PCB->Filename.*/
			   LoadPCB (fname);
	 break;
	}
      RestoreCrosshair (True);
    }
}

/* ---------------------------------------------------------------------------
 * print data -- brings up Print dialog box
 * syntax: PrintDialog()
 */
void
ActionPrintDialog (void)
{
      /* check if layout is empty */
      if (!IsDataEmpty (PCB->Data))
	gui_dialog_print();
      else
	Message (_("Can't print empty layout"));
}

/* ---------------------------------------------------------------------------
 * sets all print settings and prints.  
 *
 * syntax: Print(prefix, device, scale,");
 *               mirror, rotate, color, invert, outline");
 *               add_alignment, add_drill_helper,");
 *               use_dos_filenames)");
 */

void
ActionPrint (gchar **Params, gint num)
{

  if (num == 11)
    {
      /* check if layout is empty */
      if (!IsDataEmpty (PCB->Data))
        {
          char *prefix_str = Params[0];
          int device_choice_i = atoi(Params[1]);
          float scale = atof(Params[2]);

          int mirror = atoi(Params[3]);
          int rotate = atoi(Params[4]);
          int color = atoi(Params[5]);
          int invert = atoi(Params[6]);
          int outline = atoi(Params[7]);

          int add_alignment = atoi(Params[8]);
          int add_drill_helper = atoi(Params[9]);

          int use_dos_filenames = atoi(Params[10]);

          /*
	   * for(num_devices = 0; PrintingDevice[num_devices].Query; num_devices++);
           * assert(0 <= i && i < num_devices);
	   */

          Print(
		prefix_str,
		scale,

		/* boolean options */
		mirror,
		rotate,
		color,
		invert,
		outline,
		add_alignment,
		add_drill_helper,
		use_dos_filenames,

		PrintingDevice[device_choice_i].Info,
		0, /* media */

		0, /* offsetx */
		0, /* offsety */

		/* boolean options */
		0 /* silk screen text flag */
		);
        }
      else
	Message (_("Can't print empty layout"));
    }
  else
    {
      Message (_("syntax: Print(prefix, device, scale,\n"
	       "              mirror, rotate, color, invert, outline\n"
	       "              add_alignment, add_drill_helper,\n"
	       "              use_dos_filenames)\n"));
    }
}


/* ---------------------------------------------------------------------------
 * starts a new layout
 * syntax: New()
 */
void
ActionNew (void)
{
  char *name;

      HideCrosshair (True);
      if (!PCB->Changed || gui_dialog_confirm(_("OK to clear layout data?")))
	{
	  name = gui_dialog_input(_("Enter the layout name:"), "");
	  if (!name)
	    return;

	  /* do emergency saving
	   * clear the old struct and allocate memory for the new one
	   */
	  if (PCB->Changed && Settings.SaveInTMP)
	    SaveInTMP ();
	  RemovePCB (PCB);
	  PCB = CreateNewPCB (True);
	  gui_netlist_window_show(&Output);

	  /* setup the new name and reset some values to default */
	  PCB->Name = name;		/* XXX memory leak */
	  gui_output_set_name_label(name);

	  ResetStackAndVisibility ();
	  CreateDefaultFont ();
	  SetCrosshairRange (0, 0, PCB->MaxWidth, PCB->MaxHeight);
	  UpdateSettingsOnScreen ();
	  gui_output_positioners_scale();
	  SetZoom (2);
	  CenterDisplay (PCB->MaxWidth / 2, PCB->MaxHeight / 2, False);
	  ClearAndRedrawOutput ();

	  gui_sync_with_new_layout();
	}
      RestoreCrosshair (True);
}

/* ---------------------------------------------------------------------------
 * swap visible sides
 */
void
ActionSwapSides (void)
{
  LocationType x, y;

  x = TO_SCREEN_X (Crosshair.X);
  y = TO_SCREEN_Y (Crosshair.Y);
  SwapBuffers ();
  Settings.ShowSolderSide = !Settings.ShowSolderSide;
  /* set silk colors as needed */
  PCB->Data->SILKLAYER.Color = PCB->ElementColor;
  PCB->Data->BACKSILKLAYER.Color = PCB->InvisibleObjectsColor;
  /* change the display */
  if (CoalignScreen (x, y, Crosshair.X, Crosshair.Y))
    warpNoWhere ();

  /* update object position and cursor location */
  AdjustAttachedObjects ();
  ClearAndRedrawOutput ();
  set_cursor_position_labels ();
  set_status_line_label ();
}

/* ---------------------------------------------------------------------------
 * no operation, just for testing purposes
 * syntax: Bell(volume)
 */
void
ActionBell (char *volume)
{
  gdk_beep ();
}

/* ---------------------------------------------------------------------------
 * paste buffer operations
 * syntax: PasteBuffer(AddSelected|Clear|1..MAX_BUFFER)
 *         PasteBuffer(Rotate, 1..3)
 *         PasteBuffer(Convert|Save|Restore|Mirror)
 */
void
ActionPasteBuffer (char *function, char *sbufnum)
{
  char *name;
  static gchar	*current_library_dir = NULL;

  HideCrosshair (True);
  if (function)
    {
      switch (GetFunctionID (function))
	{
	  /* clear contents of paste buffer */
	case F_Clear:
	  ClearBuffer (PASTEBUFFER);
	  break;

	  /* copies objects to paste buffer */
	case F_AddSelected:
	  AddSelectedToBuffer (PASTEBUFFER, 0, 0, False);
	  break;

	  /* converts buffer contents into an element */
	case F_Convert:
	  ConvertBufferToElement (PASTEBUFFER);
	  break;

	  /* break up element for editing */
	case F_Restore:
	  SmashBufferElement (PASTEBUFFER);
	  break;

	  /* Mirror buffer */
	case F_Mirror:
	  MirrorBuffer (PASTEBUFFER);
	  break;

	case F_Rotate:
		if (sbufnum)
			{
		    RotateBuffer (PASTEBUFFER, (BYTE) atoi(sbufnum));
		    SetCrosshairRangeToBuffer ();
			}
	    break;

	case F_Save:
	  if (PASTEBUFFER->Data->ElementN == 0)
	    {
	      Message (_("Buffer has no elements!\n"));
	      break;
	    }
	  name = gui_dialog_file_select_save(_("Save buffer elements as"),
						&current_library_dir,
						DESCRIPTION_NAME (PASTEBUFFER->Data->Element),
						Settings.LibraryTree);
	  if (name)
	    {
	      FILE *exist;

	      if ((exist = fopen (name, "r")))
		{
		  fclose (exist);
		  if (gui_dialog_confirm(_("File exists!  Ok to overwrite?")))
		    SaveBufferElements (name);
		}
	      else
		SaveBufferElements (name);
	    g_free(name);
	    }
	  break;

	  /* set number */
	default:
	  {
	    int number = atoi (function);

	    /* correct number */
	    if (number)
	      SetBufferNumber (number - 1);
	  }
	}
    }
  RestoreCrosshair (True);
}

/* ---------------------------------------------------------------------------
 * action routine to 'undo' operations
 * The serial number indicates the operation. This makes it possible
 * to group undo requests.
 * syntax: Undo(ClearList)
 *         Undo()
 */
/* XXX */
void
ActionUndo (char *function)
{
  if (!function || !*function)
    {
      /* don't allow undo in the middle of an operation */
      if (Crosshair.AttachedObject.State != STATE_FIRST)
	return;
      if (Crosshair.AttachedBox.State != STATE_FIRST
	  && Settings.Mode != ARC_MODE)
	return;
      /* undo the last operation */

      HideCrosshair (True);
      if (Settings.Mode == POLYGON_MODE && Crosshair.AttachedPolygon.PointN)
	{
	  GoToPreviousPoint ();
	  RestoreCrosshair (True);
	  return;
	}
      /* move anchor point if undoing during line creation */
      if (Settings.Mode == LINE_MODE)
	{
	  if (Crosshair.AttachedLine.State == STATE_SECOND)
	    {
	      if (TEST_FLAG (AUTODRCFLAG, PCB))
		Undo (True);	/* undo the connection find */
	      Crosshair.AttachedLine.State = STATE_FIRST;
	      SetLocalRef (0, 0, False);
	      RestoreCrosshair (True);
	      return;
	    }
	  if (Crosshair.AttachedLine.State == STATE_THIRD)
	    {
	      int type;
	      void *ptr1, *ptr3, *ptrtmp;
	      LineTypePtr ptr2;
	      /* this search is guranteed to succeed */
	      SearchObjectByLocation (LINE_TYPE, &ptr1, &ptrtmp,
				      &ptr3, Crosshair.AttachedLine.Point1.X,
				      Crosshair.AttachedLine.Point1.Y, 0);
	      ptr2 = (LineTypePtr) ptrtmp;

	      /* save both ends of line */
	      Crosshair.AttachedLine.Point2.X = ptr2->Point1.X;
	      Crosshair.AttachedLine.Point2.Y = ptr2->Point1.Y;
	      if ((type = Undo (True)))
		SetChangedFlag (True);
	      /* check that the undo was of the right type */
	      if ((type & UNDO_CREATE) == 0)
		{
		  /* wrong undo type, restore anchor points */
		  Crosshair.AttachedLine.Point2.X =
		    Crosshair.AttachedLine.Point1.X;
		  Crosshair.AttachedLine.Point2.Y =
		    Crosshair.AttachedLine.Point1.Y;
		  RestoreCrosshair (True);
		  return;
		}
	      /* move to new anchor */
	      Crosshair.AttachedLine.Point1.X =
		Crosshair.AttachedLine.Point2.X;
	      Crosshair.AttachedLine.Point1.Y =
		Crosshair.AttachedLine.Point2.Y;
	      /* check if an intermediate point was removed */
	      if (type & UNDO_REMOVE)
		{
		  /* this search should find the restored line */
		  SearchObjectByLocation (LINE_TYPE, &ptr1, &ptrtmp,
					  &ptr3,
					  Crosshair.AttachedLine.Point2.X,
					  Crosshair.AttachedLine.Point2.Y, 0);
	      ptr2 = (LineTypePtr) ptrtmp;
		  Crosshair.AttachedLine.Point1.X =
		    Crosshair.AttachedLine.Point2.X = ptr2->Point2.X;
		  Crosshair.AttachedLine.Point1.Y =
		    Crosshair.AttachedLine.Point2.Y = ptr2->Point2.Y;
		}
	      FitCrosshairIntoGrid (Crosshair.X, Crosshair.Y);
	      AdjustAttachedObjects ();
	      if (--addedLines == 0)
		{
		  Crosshair.AttachedLine.State = STATE_SECOND;
		  lastLayer = CURRENT;
		}
	      else
		{
		  /* this search is guranteed to succeed too */
		  SearchObjectByLocation (LINE_TYPE, &ptr1, &ptrtmp,
					  &ptr3,
					  Crosshair.AttachedLine.Point1.X,
					  Crosshair.AttachedLine.Point1.Y, 0);
	      ptr2 = (LineTypePtr) ptrtmp;
		  lastLayer = (LayerTypePtr) ptr1;
		}
	      RestoreCrosshair (True);
	      return;
	    }
	}
      if (Settings.Mode == ARC_MODE)
	{
	  if (Crosshair.AttachedBox.State == STATE_SECOND)
	    {
	      Crosshair.AttachedBox.State = STATE_FIRST;
	      RestoreCrosshair (True);
	      return;
	    }
	  if (Crosshair.AttachedBox.State == STATE_THIRD)
	    {
	      void *ptr1, *ptr2, *ptr3;
	      BoxTypePtr bx;
	      /* guranteed to succeed */
	      SearchObjectByLocation (ARC_TYPE, &ptr1, &ptr2, &ptr3,
				      Crosshair.AttachedBox.Point1.X,
				      Crosshair.AttachedBox.Point1.Y, 0);
	      bx = GetArcEnds ((ArcTypePtr) ptr2);
	      Crosshair.AttachedBox.Point1.X =
		Crosshair.AttachedBox.Point2.X = bx->X1;
	      Crosshair.AttachedBox.Point1.Y =
		Crosshair.AttachedBox.Point2.Y = bx->Y1;
	      AdjustAttachedObjects ();
	      if (--addedLines == 0)
		Crosshair.AttachedBox.State = STATE_SECOND;
	    }
	}
      /* undo the last destructive operation */
      if (Undo (True))
	SetChangedFlag (True);
    }
  else if (function)
    {
      switch (GetFunctionID (function))
	{
	  /* clear 'undo objects' list */
	case F_ClearList:
	  ClearUndoList (False);
	  break;
	}
    }
  RestoreCrosshair (True);
}

/* ---------------------------------------------------------------------------
 * action routine to 'redo' operations
 * The serial number indicates the operation. This makes it possible
 * to group redo requests.
 * syntax: Redo()
 */
void
ActionRedo (void)
{
  if ((Settings.Mode == POLYGON_MODE &&
       Crosshair.AttachedPolygon.PointN) ||
      Crosshair.AttachedLine.State == STATE_SECOND)
    return;
  HideCrosshair (True);
  if (Redo (True))
    {
      SetChangedFlag (True);
      if (Settings.Mode == LINE_MODE &&
	  Crosshair.AttachedLine.State != STATE_FIRST)
	{
	  LineTypePtr line = &CURRENT->Line[CURRENT->LineN - 1];
	  Crosshair.AttachedLine.Point1.X =
	    Crosshair.AttachedLine.Point2.X = line->Point2.X;
	  Crosshair.AttachedLine.Point1.Y =
	    Crosshair.AttachedLine.Point2.Y = line->Point2.Y;
	  addedLines++;
	}
    }
  RestoreCrosshair (True);
}

/* ---------------------------------------------------------------------------
 * some polygon related stuff
 * syntax: Polygon(Close|PreviousPoint)
 */
void
ActionPolygon (char *function)
{
  if (function && Settings.Mode == POLYGON_MODE)
    {
      HideCrosshair (True);
      switch (GetFunctionID (function))
	{
	  /* close open polygon if possible */
	case F_Close:
	  ClosePolygon ();
	  break;

	  /* go back to the previous point */
	case F_PreviousPoint:
	  GoToPreviousPoint ();
	  break;
	}
      RestoreCrosshair (True);
    }
}

/* ---------------------------------------------------------------------------
 * copies a routing style into the current sizes
 * syntax: RouteStyle()
 */
void
ActionRouteStyle (char *str)
	{
	RouteStyleType	*rts;
	gint			number;

	if (str)
		{
		number = atoi (str);
		if (number > 0 && number <= NUM_STYLES)
			{
			rts = &PCB->RouteStyle[number - 1];
			SetLineSize (rts->Thick);
			SetViaSize (rts->Diameter, True);
			SetViaDrillingHole (rts->Hole, True);
			SetKeepawayWidth (rts->Keepaway);
			}
		}
	}


/* ---------------------------------------------------------------------------
 * Turn on or off the visibility of a layer
 */
void
ActionToggleVisibility (char *str)
{
  int number;

  if (str)
    {
      number = atoi (str) - 1;
      if (number >= 0 && number < MAX_LAYER + 2)
	{
	  if (PCB->Data->Layer[number].On == False)
	    g_message("ChangeGroupVisibility (number, True, False)");
	  else if ((LayerStack[0] != number) &&
		   (GetLayerGroupNumberByNumber (number) !=
		    GetLayerGroupNumberByNumber (LayerStack[0])))
	    ChangeGroupVisibility (number, False, False);
	  gui_layer_buttons_update();
	  ClearAndRedrawOutput ();
	}
    }
}

/* ---------------------------------------------------------------------------
 * changes the current drawing-layer
 * syntax: SwitchDrawingLayer()
 */
void
ActionSwitchDrawingLayer (char *str)
{
  int number;

  if (str)
    {
      number = atoi (str) - 1;
      if (number >= 0 && number < MAX_LAYER + 2)
	{
	  ChangeGroupVisibility (number, True, True);
	  gui_layer_buttons_update();
	  ClearAndRedrawOutput ();
	}
    }
}


/* ---------------------------------------------------------------------------
 * MoveObject
 * syntax: MoveObject(X,Y,dim)
 */
void
ActionMoveObject (char *x_str, char *y_str, char *units)
{
  LocationType x, y;
  Boolean r1, r2;
  void *ptr1, *ptr2, *ptr3;
  int type;

	y = GetValue (y_str, units, &r1);
	x = GetValue (x_str, units, &r2);

  type = SearchScreen (Crosshair.X, Crosshair.Y, MOVE_TYPES,
		       &ptr1, &ptr2, &ptr3);
  if (type == NO_TYPE)
    {
      Message (_("Nothing found under crosshair\n"));
      return;
    }
  if (r1)
    x -= Crosshair.X;
  if (r2)
    y -= Crosshair.Y;
  Crosshair.AttachedObject.RubberbandN = 0;
  if (TEST_FLAG (RUBBERBANDFLAG, PCB))
    LookupRubberbandLines (type, ptr1, ptr2, ptr3);
  if (type == ELEMENT_TYPE)
    LookupRatLines (type, ptr1, ptr2, ptr3);
  MoveObjectAndRubberband (type, ptr1, ptr2, ptr3, x, y);
  SetChangedFlag (True);
}

/* ---------------------------------------------------------------------------
 * moves objects to the current layer
 * syntax: MoveToCurrentLayer(Object|SelectedObjects)
 */
void
ActionMoveToCurrentLayer (char *function)
{
  if (function)
    {
      HideCrosshair (True);
      switch (GetFunctionID (function))
	{
	case F_Object:
	  {
	    int type;
	    void *ptr1, *ptr2, *ptr3;

	    if ((type =
		 SearchScreen (Crosshair.X, Crosshair.Y, MOVETOLAYER_TYPES,
			       &ptr1, &ptr2, &ptr3)) != NO_TYPE)
	      if (MoveObjectToLayer (type, ptr1, ptr2, ptr3, CURRENT, False))
		SetChangedFlag (True);
	    break;
	  }

	case F_SelectedObjects:
	case F_Selected:
	  if (MoveSelectedObjectsToLayer (CURRENT))
	    SetChangedFlag (True);
	  break;
	}
      RestoreCrosshair (True);
    }
}


void
ActionSetSame (void)
{
  void *ptr1, *ptr2, *ptr3;
  int type;
  LayerTypePtr layer = CURRENT;

  type =
    SearchScreen (Crosshair.X, Crosshair.Y, CLONE_TYPES, &ptr1, &ptr2, &ptr3);
/* set layer current and size from line or arc */
  switch (type)
    {
    case LINE_TYPE:
      HideCrosshair (True);
      Settings.LineThickness = ((LineTypePtr) ptr2)->Thickness;
      Settings.Keepaway = ((LineTypePtr) ptr2)->Clearance / 2;
      layer = (LayerTypePtr) ptr1;
      if (Settings.Mode != LINE_MODE)
	SetMode (LINE_MODE);
      RestoreCrosshair (True);
      gui_route_style_buttons_update();
      break;
    case ARC_TYPE:
      HideCrosshair (True);
      Settings.LineThickness = ((ArcTypePtr) ptr2)->Thickness;
      Settings.Keepaway = ((ArcTypePtr) ptr2)->Clearance / 2;
      layer = (LayerTypePtr) ptr1;
      if (Settings.Mode != ARC_MODE)
	SetMode (ARC_MODE);
      RestoreCrosshair (True);
      gui_route_style_buttons_update();
      break;
    case POLYGON_TYPE:
      layer = (LayerTypePtr) ptr1;
      break;
    case VIA_TYPE:
      HideCrosshair (True);
      Settings.ViaThickness = ((PinTypePtr) ptr2)->Thickness;
      Settings.ViaDrillingHole = ((PinTypePtr) ptr2)->DrillingHole;
      Settings.Keepaway = ((PinTypePtr) ptr2)->Clearance / 2;
      if (Settings.Mode != VIA_MODE)
	SetMode (VIA_MODE);
      RestoreCrosshair (True);
      gui_route_style_buttons_update();
      break;
    default:
      return;
    }
  if (layer != CURRENT)
    {
      ChangeGroupVisibility (GetLayerNumber (PCB->Data, layer), True, True);
      gui_layer_buttons_update();
      ClearAndRedrawOutput ();
    }
  set_status_line_label ();
}


/* ---------------------------------------------------------------------------
 * sets flags on object
 * syntax: SetFlag(Object, flag)
 *         SetFlag(SelectedLines|SelectedPins|SelectedVias, flag)
 *         SetFlag(SelectedPads|SelectedTexts|SelectedNames, flag)
 *	   SetFlag(SelectedElements, flag)
 *
 *	   flag = square | octagon | thermal
 *
 * examples:
 * :SetFlag(SelectedVias,thermal) 
 * :SetFlag(SelectedElements,square)
 */

void
ActionSetFlag (char *function, char *flag)
{
  ChangeFlag (function, flag, 1, "SetFlag");
}

/* ---------------------------------------------------------------------------
 * clears flags on object
 * syntax: ClrFlag(Object, flag)
 *         ClrFlag(SelectedLines|SelectedPins|SelectedVias, flag)
 *         ClrFlag(SelectedPads|SelectedTexts|SelectedNames, flag)
 *	   ClrFlag(SelectedElements, flag)
 * examples:
 * :ClrFlag(SelectedVias,thermal) 
 * :ClrFlag(SelectedElements,square)
 */

void
ActionClrFlag (char *function, char *flag)
{
  ChangeFlag (function, flag, 0, "ClrFlag");
}

/* ---------------------------------------------------------------------------
 * sets or clears flags on object
 * syntax: ChangeFlag(Object, flag, value)
 *         ChangeFlag(SelectedLines|SelectedPins|SelectedVias, flag, value)
 *         ChangeFlag(SelectedPads|SelectedTexts|SelectedNames, flag, value)
 *	   ChangeFlag(SelectedElements, flag, value)
 *
 * examples:
 *
 * :ChangeFlag(SelectedVias,thermal,0) 
 * :ChangeFlag(SelectedElements,square,1)
 */
void
ActionChangeFlag (char *function, char *flag, int value)
{
	if (value != 0 && value != 1)
    {
      Message (_("ChangeFlag():  Value %d is not valid\n"), value);
      return;
    }

  ChangeFlag (function, flag, value, "ChangeFlag");
}


static void
ChangeFlag (char *what, char *flag_name, int value, char *cmd_name)
{
  Boolean (*set_object) (int, void *, void *, void *);
  Boolean (*set_selected) (int);

  if (NSTRCMP (flag_name, "square") == 0)
    {
      set_object = value ? SetObjectSquare : ClrObjectSquare;
      set_selected = value ? SetSelectedSquare : ClrSelectedSquare;
    }
  else if (NSTRCMP (flag_name, "octagon") == 0)
    {
      set_object = value ? SetObjectOctagon : ClrObjectOctagon;
      set_selected = value ? SetSelectedOctagon : ClrSelectedOctagon;
    }
  else if (NSTRCMP (flag_name, "thermal") == 0)
    {
      set_object = value ? SetObjectThermal : ClrObjectThermal;
      set_selected = value ? SetSelectedThermals : ClrSelectedThermals;
    }
  else
    {
      Message (_("%s():  Flag \"%s\" is not valid\n"), cmd_name, flag_name);
      return;
    }

  HideCrosshair (True);
  switch (GetFunctionID (what))
    {
    case F_Object:
      {
	int type;
	void *ptr1, *ptr2, *ptr3;

	if ((type =
	     SearchScreen (Crosshair.X, Crosshair.Y, CHANGESIZE_TYPES,
			   &ptr1, &ptr2, &ptr3)) != NO_TYPE)
	  if (TEST_FLAG (LOCKFLAG, (PinTypePtr) ptr2))
	    Message (_("Sorry, the object is locked\n"));
	if (set_object (type, ptr1, ptr2, ptr3))
	  SetChangedFlag (True);
	break;
      }

    case F_SelectedVias:
      if (set_selected (VIA_TYPE))
	SetChangedFlag (True);
      break;

    case F_SelectedPins:
      if (set_selected (PIN_TYPE))
	SetChangedFlag (True);
      break;

    case F_SelectedPads:
      if (set_selected (PAD_TYPE))
	SetChangedFlag (True);
      break;

    case F_SelectedLines:
      if (set_selected (LINE_TYPE))
	SetChangedFlag (True);
      break;

    case F_SelectedTexts:
      if (set_selected (TEXT_TYPE))
	SetChangedFlag (True);
      break;

    case F_SelectedNames:
      if (set_selected (ELEMENTNAME_TYPE))
	SetChangedFlag (True);
      break;

    case F_SelectedElements:
      if (set_selected (ELEMENT_TYPE))
	SetChangedFlag (True);
      break;

    case F_Selected:
    case F_SelectedObjects:
      if (set_selected (CHANGESIZE_TYPES))
	SetChangedFlag (True);
      break;
    }
  RestoreCrosshair (True);

}




static char *
invoke_action (char *rstr)
{
  static char **list = 0;
  static int max = 0;
  int num = 0;
  static char *str = NULL;
  char *sp, *aname;
  int maybe_empty = 0;

  if (str != NULL)
    {
      g_free (str);
      str = NULL;
    }

  sp = str = MyStrdup(rstr, "invoke_action");

  /* eat leading spaces and tabs */
  while (*sp && (*sp == ' ' || *sp == '\t'))
    sp ++;

  aname = sp;

  /* search for the leading ( */
  while (*sp && *sp != '(')
    sp++;

  /*
   * we didn't find a leading ( so invoke the action
   * with no parameters or event.
   */
  if (! *sp)
    {
      CallActionProc(aname, NULL, 0);
      return 0;
    }

  /* 
   * we found a leading ( so see if we have parameters to pass to the
   * action 
   */
  *sp++ = 0;
  while (1) {
    /* 
     * maybe_empty == 0 means that the last char examined was not a
     * "," 
     */
    if (*sp == ')' && !maybe_empty)
      {
	*sp = 0;
	CallActionProc(aname, list, num);
	return sp+1;
      }
    else if (*sp == 0 && !maybe_empty)
      break;
    else
      {
	maybe_empty = 0;
	/* 
	 * if we have more parameters than memory in our array of
	 * pointers, then either allocate some or grow the array
	 */
	if (num >= max)
	  {
	    max += 10;
	    if (list)
	      list = (char **)g_realloc(list, max * sizeof(char *));
	    else
	      list = (char **)g_malloc(max * sizeof(char *));
	  }
	list[num++] = sp;

	/* search for a "," or a ")" */
	while (*sp && *sp != ',' && *sp != ')')
	  sp++;
	if (*sp == ',')
	  {
	    maybe_empty = 1;
	    *sp++ = 0;
	  }
      }
  }

  return 0;
}

static void
invoke_multiple_actions (char *string)
{
  char *sp = string;;
  while (sp)
    {
      while (*sp == ' ' || *sp == '\t')
	*sp ++;
      if (! *sp)
	return;
      sp = invoke_action (sp);
    }
}

/* ************************************************************ */

/* ACTION(ExecuteAction,ActionExecuteAction) */

void
ActionExecuteAction(gchar **params, gint num)
{
  int i;
  for (i=0; i<num; i++)
    invoke_multiple_actions (params[i]);
}

/* ACTION(ExecuteFile,ActionExecuteFile) */

void
ActionExecuteFile(gchar *arg)
{
  FILE *fp;
  char *fname;
  char line[256];
  int n=0;
  char *sp;

  if (!arg || !*arg)
  {
	Message("Usage:  ExecuteFile(filename)");
	return ;
  }

  fname = arg;

  if ( (fp = fopen(fname, "r")) == NULL )
    {
      fprintf(stderr, "Could not open actions file \"%s\".\n", fname);
      return ;
    }

  while ( fgets(line, sizeof(line), fp) != NULL )
    {
      n++;
      sp = line;
      
      /* eat the trailing newline */
      while (*sp && *sp != '\r' && *sp != '\n')
	sp ++;
      *sp = '\0';
  
      /* eat leading spaces and tabs */
      sp = line;
      while (*sp && (*sp == ' ' || *sp == '\t'))
	sp ++;
  
      /* 
       * if we have anything left and its not a comment line
       * then execute it
       */

      if (*sp && *sp != '#') 
	{
	  Message("%s : line %-3d : \"%s\"\n", fname, n, sp);
	  /* printf ("%s : line %-3d : \"%s\"\n", fname, n, sp); */
	  invoke_multiple_actions(sp);
	}
    }
  
  fclose(fp);
}
/*
 *                            COPYRIGHT
 *
 *  PCB, interactive printed circuit board design
 *  Copyright (C) 1994,1995,1996 Thomas Nau
 *
 *  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.
 *
 */

/* This file written by Bill Wilson for the PCB Gtk port
*/


/* gui-top-window.c
|  This handles creation of the top level window and all its widgets.
|  events for the Output.drawing_area widget are handled in a separate
|  file gui-output-events.c
|
|  Some caveats with menu shorcut keys:  Some keys are trapped out by Gtk
|  and can't be used as shortcuts (eg. '|', TAB, etc).  For these cases
|  the Gtk menus can't show the same shortcut as the Xt menus did, but the
|  actions are the same as the keys are handled in gui_output_key_press_cb().
*/



#include "gui.h"

#include "action.h"
#include "autoplace.h"
#include "autoroute.h"
#include "buffer.h"
#include "change.h"
#include "command.h"
#include "copy.h"
#include "create.h"
#include "crosshair.h"
#include "djopt.h"
#include "draw.h"
#include "error.h"
#include "file.h"
#include "find.h"
#include "insert.h"
#include "line.h"
#include "mymem.h"
#include "misc.h"
#include "move.h"
#include "output.h"
#include "polygon.h"
#include "rats.h"
#include "remove.h"
#include "report.h"
#include "rotate.h"
#include "rubberband.h"
#include "search.h"
#include "select.h"
#include "set.h"
#include "undo.h"
#include "vendor.h"

#include "gui-icons-mode-buttons.data"
#include "gui-icons-misc.data"


GuiPCB	*gui;


#define	N_GRID_SETTINGS		8

static gdouble	grid_mil_values[N_GRID_SETTINGS] =
	{
	10.0,
	50.0,
	100.0,
	500.0,
	1000.0,
	2500.0,
	5000.0,
	10000.0
	};

static gdouble	grid_mm_values[N_GRID_SETTINGS] =
	{
	39.370078740,
	78.740157480,
	196.850393701,
	393.700787402,
	787.401574804,
	1968.50393701,
	3937.00787402,
	7874.01574804
	};

  /* When the user toggles grid units mil<->mm, call this to get an
  |  index into the grid values table of the current grid setting.  Then a
  |  grid in the new units may be selected that is closest to what we had.
  |
  |  May want this call to fail if user has altered grid with 'g' key
  |  and so current grid does not match any of the presets.  In that
  |  case we want no item in the grid setting radio group to get set.
  */
static gint
get_grid_value_index(gboolean allow_fail)
	{
	gdouble		*value;
	gint		i;

	value = Settings.grid_units_mm ? &grid_mm_values[0] : &grid_mil_values[0];
	for (i = 0; i < N_GRID_SETTINGS; ++i, ++value)
		if (PCB->Grid < *value + 1.0 && PCB->Grid > *value - 1.0)
			break;
	if (i >= N_GRID_SETTINGS)
		i = allow_fail ? -1 : N_GRID_SETTINGS - 1;

	return i;
	}

static void
h_adjustment_changed_cb(GtkAdjustment *adj, GuiPCB *g)
	{
	gdouble	xval, yval;

	if (g->adjustment_changed_holdoff)
		return;
	xval = gtk_adjustment_get_value(adj);
	yval = gtk_adjustment_get_value(GTK_ADJUSTMENT(gui->v_adjustment));

/*	printf("h_adjustment_changed_cb: %f %f\n", xval, yval); */
	Pan((gint) xval, (gint)yval, TRUE, TRUE);
	}

static void
v_adjustment_changed_cb(GtkAdjustment *adj, GuiPCB *g)
	{
	gdouble	xval, yval;

	if (g->adjustment_changed_holdoff)
		return;
	yval = gtk_adjustment_get_value(adj);
	xval = gtk_adjustment_get_value(GTK_ADJUSTMENT(gui->h_adjustment));

/*	printf("v_adjustment_changed_cb: %f %f\n", xval, yval); */
	Pan((gint) xval, (gint)yval, TRUE, TRUE);
	}

  /* Save size of top window changes so PCB can restart at its size at exit.
  */
static gint
top_window_configure_event_cb(GtkWidget *widget, GdkEventConfigure *ev,
			OutputType *out)
	{
	gboolean	new_w, new_h;

	new_w = (Settings.pcb_width != widget->allocation.width);
	new_h = (Settings.pcb_height != widget->allocation.height);

	Settings.pcb_width = widget->allocation.width;
	Settings.pcb_height = widget->allocation.height;

	if (new_w || new_h)
		Settings.config_modified = TRUE;

	return FALSE;
	}


/* =========================================================================
|  Here are the menu callbacks.  To use the "Save layout as" menu item as
|  an example of Gtk ui_manager menu handling, here's how it would be added:
|    1) In the XML ui_info string, add a menuitem:
|         <menuitem action='SaveLayoutAs'/>
|       which says that a menu item should appear as defined by the
|       'SaveLayoutAs' action.
|    2) So a GtkActionEntry for this must exist in one of the action arrays
|       to be loaded into the ui_manager.  For this there is the entry:
|          { "SaveLayoutAs", NULL, N_("Save layout as"), NULL, NULL,
|                      G_CALLBACK(save_layout_as_cb) },
|       which connects the menu position (via the name SaveLayoutAs) to the
|       text to display and the callback function to call.
|    3) And the callback function must be written.
|
|  Actions can be removed, modified and reloaded.  This is how the menus
|  can be updated, for example, to display grid units appropriate when the
|  grid units are toggle from mil<->mm.  At toggles, the relevant actions are
|  removed, the string to display is updated, and the actions are added
|  back in.  There are several other similar dynamic menu adjustments here.
|
|  Some of the callbacks that need to get a location when invoked from the
|  menu need to work differently and not get a location when invoked via
|  a keyboard shortcut.  That's done by checking shift or control state
|  in the callback to determine which indicates keyboard being used.
*/

/* ============== FileMenu callbacks =============== */
static void
save_layout_cb(GtkAction *action, OutputType *out)
	{
	ActionSave("Layout");
	}

static void
save_layout_as_cb(GtkAction *action, OutputType *out)
	{
	ActionSave("LayoutAs");
	}

static void
revert_cb(GtkAction *action, OutputType *out)
	{
	ActionLoad("Revert");
	}

static void
load_layout_cb(GtkAction *action, OutputType *out)
	{
	ActionLoad("Layout");
	}

static void
load_element_data_cb(GtkAction *action, OutputType *out)
	{
	ActionPasteBuffer("Clear", "");
	ActionLoad("ElementTobuffer");
	}

static void
load_layout_data_cb(GtkAction *action, OutputType *out)
	{
	ActionPasteBuffer("Clear", "");
	ActionLoad("LayoutTobuffer");
	}

static void
load_netlist_file_cb(GtkAction *action, OutputType *out)
	{
	ActionLoad("Netlist");
	}

static void
load_vendor_file_cb(GtkAction *action, OutputType *out)
	{
	ActionLoadVendor("");
	}

static void
print_layout_cb(GtkAction *action, OutputType *out)
	{
	ActionPrintDialog();
	}

static void
connections_single_element_cb(GtkAction *action, OutputType *out)
	{
	if (ActionGetLocation(_("Press a button at the element's location")))
		ActionSave("ElementConnections");
	}

static void
connections_all_elements_cb(GtkAction *action, OutputType *out)
	{
	ActionSave("AllConnections");
	}

static void
connections_unused_pins_cb(GtkAction *action, OutputType *out)
	{
	ActionSave("AllUnusedPins");
	}

static void
new_layout_cb(GtkAction *action, OutputType *out)
	{
	ActionNew();
	}

static void
quit_cb(GtkAction *action, OutputType *out)
	{
	ActionQuit();
	}



/* ============== EditMenu callbacks =============== */
static void
undo_cb(GtkAction *action, OutputType *out)
	{
	ActionUndo("");
	}

static void
redo_cb(GtkAction *action, OutputType *out)
	{
	ActionRedo();
	}

static void
clear_undo_cb(GtkAction *action, OutputType *out)
	{
	ActionUndo("ClearList");
	}


static void
edit_text_on_layout_cb(GtkAction *action, OutputType *out)
	{
	ActionChangeName("Object");
	}

static void
edit_name_of_layout_cb(GtkAction *action, OutputType *out)
	{
	ActionChangeName("Layout");
	}

static void
edit_name_of_active_layer_cb(GtkAction *action, OutputType *out)
	{
	ActionChangeName("Layer");
	}



/* ============== ScreenMenu callbacks =============== */
static void
redraw_layout_cb(GtkAction *action, OutputType *out)
	{
	ActionDisplay("ClearAndRedraw", "");
	}

static void
center_layout_cb(GtkAction *action, OutputType *out)
	{
	ActionDisplay("Center", "");
	}

static void
toggle_draw_grid_cb(GtkToggleAction *action, OutputType *out)
	{
	if (gui->toggle_holdoff)	/* If setting initial condition */
		return;

	/* Set to ! because ActionDisplay toggles it */
	Settings.DrawGrid = !gtk_toggle_action_get_active(action);
	Settings.config_modified = TRUE;
	ActionDisplay("Grid", "");
	}

static void
realign_grid_cb(GtkAction *action, OutputType *out)
	{
	if (ActionGetLocation(_("Press a button at a grid point")))
		ActionDisplay("ToggleGrid", "");
	}

static void
zoom_cb(GtkAction *action, GtkRadioAction *current)
	{
	const gchar	*saction = gtk_action_get_name(action);
	gchar		*arg;

	arg = !strcmp(saction, "ZoomIn") ? "-1" : "+1";
	ActionSetValue("Zoom", arg, "");
	}


static void
toggle_view_solder_side_cb(GtkAction *action, OutputType *out)
	{
	/* If get here from the menu, ask for a locaton.
	*/
	if (   !gui_shift_is_pressed()
	    && !ActionGetLocation(_("Press a button at the desired point"))
	   )
		return;
	ActionSwapSides();
	}

static void
toggle_show_solder_mask_cb(GtkAction *action, OutputType *out)
	{
	/* Transient setting, not saved in Settings & not used for new PCB flag. */
	ActionDisplay("ToggleMask", "");
	}

static void
toggle_pinout_shows_number_cb(GtkToggleAction *action, OutputType *out)
	{
	/* Toggle existing PCB flag and use setting to initialize new PCB flag */
	Settings.ShowNumber = gtk_toggle_action_get_active(action);
	Settings.config_modified = TRUE;
	if (!gui->toggle_holdoff)
		ActionDisplay("ToggleName", "");
	}

static void
pinout_menu_cb(GtkAction *action, OutputType *out)
	{
	if (   !gui_shift_is_pressed()
	    && !ActionGetLocation("Select an element")
	   )
		return;
	ActionDisplay("Pinout", "");
	}


static void
toggle_grid_units_cb(GtkToggleAction *action, OutputType *out)
	{
	gchar		*grid;
	gint		i;
	gboolean	active;

	if (gui->toggle_holdoff)	/* If setting initial condition */
		return;
	active = gtk_toggle_action_get_active(action);

	i = get_grid_value_index(FALSE);
	Settings.grid_units_mm = active;
	PCB->Grid = active ? grid_mm_values[i] : grid_mil_values[i];

	gui_grid_setting_update_menu_actions();

	grid = g_strdup_printf("%f", PCB->Grid);
	ActionSetValue("Grid", grid, "");
	g_free(grid);

	gui_config_handle_units_changed();
	gui_change_selected_update_menu_actions();
	}

static void
radio_grid_mil_setting_cb(GtkAction *action, GtkRadioAction *current)
	{
	gdouble	value;
	gchar	*grid;
	gint	index;

	if (gui->toggle_holdoff)
		return;
	index = gtk_radio_action_get_current_value(current);
	value = grid_mil_values[index];
	grid = g_strdup_printf("%f", value);
	ActionSetValue("Grid", grid, "");
	g_free(grid);
	}

static void
radio_grid_mm_setting_cb(GtkAction *action, GtkRadioAction *current)
	{
	gdouble	value;
	gchar	*grid;
	gint	index;

	if (gui->toggle_holdoff)
		return;
	index = gtk_radio_action_get_current_value(current);
	value = grid_mm_values[index];
	grid = g_strdup_printf("%f", value);
	ActionSetValue("Grid", grid, "");
	g_free(grid);
	}

static void
radio_displayed_element_name_cb(GtkAction *action, GtkRadioAction *current)
	{
	gint			value;
	static gchar	*doit[] = { "Description", "NameOnPCB", "Value" };
	
	value = gtk_radio_action_get_current_value(current);
	if (value >= 0 && value < 4)
		ActionDisplay(doit[value], "");
	}



/* ============== SettingsMenu callbacks =============== */
static void
toggle_45_degree_cb(GtkToggleAction *action, OutputType *out)
	{
	/* Toggle existing PCB flag and use setting to initialize new PCB flag */
	Settings.AllDirectionLines = gtk_toggle_action_get_active(action);
	Settings.config_modified = TRUE;
	if (!gui->toggle_holdoff)
		ActionDisplay("Toggle45Degree", "");
	}

static void
toggle_start_direction_cb(GtkToggleAction *action, OutputType *out)
	{
	/* Toggle existing PCB flag and use setting to initialize new PCB flag */
	Settings.SwapStartDirection = gtk_toggle_action_get_active(action);
	Settings.config_modified = TRUE;
	if (!gui->toggle_holdoff)
		ActionDisplay("ToggleStartDirection", "");
	}

static void
toggle_orthogonal_moves_cb(GtkToggleAction *action, OutputType *out)
	{
	/* Toggle existing PCB flag and use setting to initialize new PCB flag */
	Settings.OrthogonalMoves = gtk_toggle_action_get_active(action);
	Settings.config_modified = TRUE;
	if (!gui->toggle_holdoff)
		ActionDisplay("ToggleOrthoMove", "");
	}

static void
toggle_snap_pin_cb(GtkToggleAction *action, OutputType *out)
	{
	/* Toggle existing PCB flag and use setting to initialize new PCB flag */
	Settings.SnapPin = gtk_toggle_action_get_active(action);
	Settings.config_modified = TRUE;
	if (!gui->toggle_holdoff)
		ActionDisplay("ToggleSnapPin", "");
	}

static void
toggle_show_DRC_cb(GtkToggleAction *action, OutputType *out)
	{
	/* Toggle existing PCB flag and use setting to initialize new PCB flag */
	Settings.ShowDRC = gtk_toggle_action_get_active(action);
	Settings.config_modified = TRUE;
	if (!gui->toggle_holdoff)
		ActionDisplay("ToggleShowDRC", "");
	}

static void
toggle_auto_DRC_cb(GtkToggleAction *action, OutputType *out)
	{
	/* Toggle existing PCB flag and use setting to initialize new PCB flag */
	Settings.AutoDRC = gtk_toggle_action_get_active(action);
	Settings.config_modified = TRUE;
	if (!gui->toggle_holdoff)
		ActionDisplay("ToggleAutoDRC", "");
	}

static void
toggle_rubber_band_cb(GtkToggleAction *action, OutputType *out)
	{
	/* Toggle existing PCB flag and use setting to initialize new PCB flag */
	Settings.RubberBandMode = gtk_toggle_action_get_active(action);
	Settings.config_modified = TRUE;
	if (!gui->toggle_holdoff)
		ActionDisplay("ToggleRubberBandMode", "");
	}

static void
toggle_unique_names_cb(GtkToggleAction *action, OutputType *out)
	{
	/* Toggle existing PCB flag and use setting to initialize new PCB flag */
	Settings.UniqueNames = gtk_toggle_action_get_active(action);
	Settings.config_modified = TRUE;
	if (!gui->toggle_holdoff)
		ActionDisplay("ToggleUniqueNames", "");
	}

static void
toggle_local_ref_cb(GtkAction *action, OutputType *out)
	{
	/* Transient setting, not saved in Settings & not used for new PCB flag. */
	ActionDisplay("ToggleLocalRef", "");
	}

static void
toggle_clear_line_cb(GtkToggleAction *action, OutputType *out)
	{
	/* Toggle existing PCB flag and use setting to initialize new PCB flag */
	Settings.ClearLine = gtk_toggle_action_get_active(action);
	Settings.config_modified = TRUE;
	if (!gui->toggle_holdoff)
		ActionDisplay("ToggleClearLine", "");
	}

static void
toggle_live_route_cb(GtkToggleAction *action, OutputType *out)
	{
	/* Toggle existing PCB flag and use setting to initialize new PCB flag */
	Settings.liveRouting = gtk_toggle_action_get_active(action);
	Settings.config_modified = TRUE;
	if (!gui->toggle_holdoff)
		ActionDisplay("ToggleLiveRoute", "");
	}

static void
toggle_thin_draw_cb(GtkAction *action, OutputType *out)
	{
	/* Transient setting, not saved in Settings & not used for new PCB flag. */
	ActionDisplay("ToggleThindraw", "");
	}

static void
toggle_check_planes_cb(GtkAction *action, OutputType *out)
	{
	/* Transient setting, not saved in Settings & not used for new PCB flag. */
	ActionDisplay("ToggleCheckPlanes", "");
	}

static void
toggle_vendor_drill_mapping_cb(GtkAction *action, OutputType *out)
	{
	/* Transient setting, not saved in Settings & not used for new PCB flag. */
	ActionToggleVendor();
	}

/* ============== SelectMenu callbacks =============== */
static void
select_all_cb(GtkAction *action, OutputType *out)
	{
	ActionSelect("All");
	}

static void
select_all_connected_cb(GtkAction *action, OutputType *out)
	{
	ActionSelect("Connection");
	}

static void
unselect_all_cb(GtkAction *action, OutputType *out)
	{
	ActionUnselect("All");
	}

static void
unselect_all_connected_cb(GtkAction *action, OutputType *out)
	{
	ActionUnselect("Connection");
	}

static void
select_objects_by_name_cb(GtkAction *action, OutputType *out)
	{
	ActionSelect("ObjectByName");
	}

static void
select_elements_by_name_cb(GtkAction *action, OutputType *out)
	{
	ActionSelect("ElementByName");
	}

static void
select_pads_by_name_cb(GtkAction *action, OutputType *out)
	{
	ActionSelect("PadByName");
	}

static void
select_pins_by_name_cb(GtkAction *action, OutputType *out)
	{
	ActionSelect("PinByName");
	}

static void
select_text_by_name_cb(GtkAction *action, OutputType *out)
	{
	ActionSelect("TextByName");
	}

static void
select_vias_by_name_cb(GtkAction *action, OutputType *out)
	{
	ActionSelect("ViaByName");
	}

static void
auto_place_selected_cb(GtkAction *action, OutputType *out)
	{
	ActionAutoPlaceSelected();
	}

static void
disperse_all_elements_cb(GtkAction *action, OutputType *out)
	{
	ActionDisperseElements("All");
	}

static void
disperse_selected_elements_cb(GtkAction *action, OutputType *out)
	{
	ActionDisperseElements("Selected");
	}

static void
move_selected_other_side_cb(GtkAction *action, OutputType *out)
	{
	ActionFlip("SelectedElements");
	}

static void
remove_selected_cb(GtkAction *action, OutputType *out)
	{
	ActionRemoveSelected();
	}

static void
convert_selected_to_element_cb(GtkAction *action, OutputType *out)
	{
	ActionSelect("Convert");
	}

static void
optimize_selected_rats_cb(GtkAction *action, OutputType *out)
	{
	ActionDeleteRats("SelectedRats");
	ActionAddRats("SelectedRats");
	}


static void
rip_up_selected_tracks_cb(GtkAction *action, OutputType *out)
	{
	ActionRipUp("Selected");
	}


static void
selected_lines_size_change_cb(GtkAction *action, OutputType *out)
	{
	gchar		*value, *units;

	gui_size_increment_get_value(gtk_action_get_name(action), &value, &units);
	ActionChangeSize("SelectedLines", value, units);
	}

static void
selected_pads_size_change_cb(GtkAction *action, OutputType *out)
	{
	gchar		*value, *units;

	gui_size_increment_get_value(gtk_action_get_name(action), &value, &units);
	ActionChangeSize("SelectedPads", value, units);
	}

static void
selected_pins_size_change_cb(GtkAction *action, OutputType *out)
	{
	gchar		*value, *units;

	gui_size_increment_get_value(gtk_action_get_name(action), &value, &units);
	ActionChangeSize("SelectedPins", value, units);
	}

static void
selected_text_size_change_cb(GtkAction *action, OutputType *out)
	{
	gchar		*value, *units;

	gui_size_increment_get_value(gtk_action_get_name(action), &value, &units);
	ActionChangeSize("SelectedText", value, units);
	}

static void
selected_vias_size_change_cb(GtkAction *action, OutputType *out)
	{
	gchar		*value, *units;

	gui_size_increment_get_value(gtk_action_get_name(action), &value, &units);
	ActionChangeSize("SelectedVias", value, units);
	}

static void
selected_vias_drill_change_cb(GtkAction *action, OutputType *out)
	{
	gchar		*value, *units;

	gui_size_increment_get_value(gtk_action_get_name(action), &value, &units);
	ActionChange2ndSize("SelectedVias", value, units);
	}

static void
selected_pins_drill_change_cb(GtkAction *action, OutputType *out)
	{
	gchar		*value, *units;

	gui_size_increment_get_value(gtk_action_get_name(action), &value, &units);
	ActionChange2ndSize("SelectedPins", value, units);
	}

static void
selected_change_square_elements_cb(GtkAction *action, OutputType *out)
	{
	ActionChangeSquare("SelectedElements");
	}

static void
selected_change_square_pins_cb(GtkAction *action, OutputType *out)
	{
	ActionChangeSquare("SelectedPins");
	}


/* ============== BufferMenu callbacks =============== */
#define	PRESS_BUTTON_ELEMENT_PROMPT	\
		_("Press a button on a reference point for your selection")

static void
copy_selection_to_buffer_cb(GtkAction *action, OutputType *out)
	{
	if (   !gui_control_is_pressed()
	    && !ActionGetLocation(PRESS_BUTTON_ELEMENT_PROMPT)
	   )
		return;
	ActionPasteBuffer("Clear", "");
	ActionPasteBuffer("AddSelected", "");
	ActionMode("PasteBuffer");
	}


static void
cut_selection_to_buffer_cb(GtkAction *action, OutputType *out)
	{
	if (   !gui_control_is_pressed()
	    && !ActionGetLocation(PRESS_BUTTON_ELEMENT_PROMPT)
	   )
		return;
	ActionPasteBuffer("Clear", "");
	ActionPasteBuffer("AddSelected", "");
	ActionRemoveSelected();
	ActionMode("PasteBuffer");
	}

static void
paste_buffer_to_layout_cb(GtkAction *action, OutputType *out)
	{
	ActionMode("PasteBuffer");
	}

static void
rotate_buffer_CCW_cb(GtkAction *action, OutputType *out)
	{
	ActionMode("PasteBuffer");
	ActionPasteBuffer("Rotate", "1");
	}

static void
rotate_buffer_CW_cb(GtkAction *action, OutputType *out)
	{
	ActionMode("PasteBuffer");
	ActionPasteBuffer("Rotate", "3");
	}

static void
mirror_buffer_up_down_cb(GtkAction *action, OutputType *out)
	{
	ActionMode("PasteBuffer");
	ActionPasteBuffer("Mirror", "");
	}

static void
mirror_buffer_left_right_cb(GtkAction *action, OutputType *out)
	{
	ActionMode("PasteBuffer");
	ActionPasteBuffer("Rotate", "1");
	ActionPasteBuffer("Mirror", "");	
	ActionPasteBuffer("Rotate", "3");
	}

/* ============== ConnectsMenu callbacks =============== */
static void
clear_buffer_cb(GtkAction *action, OutputType *out)
	{
	ActionPasteBuffer("Clear", "");
	}

static void
convert_buffer_to_element_cb(GtkAction *action, OutputType *out)
	{
	ActionPasteBuffer("Convert", "");
	}

static void
break_buffer_elements_cb(GtkAction *action, OutputType *out)
	{
	ActionPasteBuffer("Restore", "");
	}

static void
save_buffer_elements_cb(GtkAction *action, OutputType *out)
	{
	ActionPasteBuffer("Save", "");
	}

static void
radio_select_current_buffer_cb(GtkAction *action, GtkRadioAction *current)
	{
	gchar	buf[16];
	gint	value;
	
	value = gtk_radio_action_get_current_value(current);
	if (value >= 1 && value <= 5)
		{
		sprintf(buf, "%d", value);
		ActionPasteBuffer(buf, "");
		}
	}


/* ============== ConnectsMenu callbacks =============== */
static void
lookup_connection_cb(GtkAction *action, OutputType *out)
	{
	if (   !gui_control_is_pressed()
	    && !ActionGetLocation(_("Select the object"))
	   )
		return;
	ActionConnection("Find");
	}

static void
reset_scanned_pads_cb(GtkAction *action, OutputType *out)
	{
	ActionConnection("ResetPinsViasAndPads");
	ActionDisplay("Redraw", "");
	}

static void
reset_scanned_lines_cb(GtkAction *action, OutputType *out)
	{
	ActionConnection("ResetLinesAndPolygons");
	ActionDisplay("Redraw", "");
	}

static void
reset_all_connections_cb(GtkAction *action, OutputType *out)
	{
	ActionConnection("Reset");
	ActionDisplay("Redraw", "");
	}

static void
optimize_rats_nest_cb(GtkAction *action, OutputType *out)
	{
	ActionAtomic("Save");
	ActionDeleteRats("AllRats");
	ActionAtomic("Restore");
	ActionAddRats("AllRats");
	ActionAtomic("Block");
	}

static void
erase_rats_nest_cb(GtkAction *action, OutputType *out)
	{
	ActionDeleteRats("AllRats");
	}

static void
auto_route_selected_rats_cb(GtkAction *action, OutputType *out)
	{
	ActionAutoRoute("Selected");
	}

static void
auto_route_all_rats_cb(GtkAction *action, OutputType *out)
	{
	ActionAutoRoute("AllRats");
	}

static void
rip_up_auto_routed_cb(GtkAction *action, OutputType *out)
	{
	ActionRipUp("All");
	}

static void
optimize_routes_cb(GtkAction *action, OutputType *out)
	{
	const gchar	*saction;

	saction = gtk_action_get_name(action);
	if (!strcmp(saction, "AutoOptimize"))
		ActionDJopt("auto");
	else if (!strcmp(saction, "Debumpify"))
		ActionDJopt("debumpify");
	else if (!strcmp(saction, "Unjaggy"))
		ActionDJopt("unjaggy");
	else if (!strcmp(saction, "ViaNudge"))
		ActionDJopt("vianudge");
	else if (!strcmp(saction, "ViaTrim"))
		ActionDJopt("viatrim");
	else if (!strcmp(saction, "OrthoPull"))
		ActionDJopt("orthopull");
	else if (!strcmp(saction, "SimpleOpts"))
		ActionDJopt("simple");
	else if (!strcmp(saction, "Miter"))
		ActionDJopt("miter");

	ActionDisplay("ClearAndRedraw", "");
	}

static void
toggle_only_auto_routed_cb(GtkAction *action, OutputType *out)
	{
	/* Transient setting, not saved in Settings. Not a PCB flag */
	djopt_set_auto_only();
	}

static void
design_rule_check_cb(GtkAction *action, OutputType *out)
	{
	ActionDRCheck();
	}

static void
apply_vendor_mapping_cb(GtkAction *action, OutputType *out)
	{
	ActionApplyVendor();
	}



/* ============== InfoMenu callbacks =============== */
static void
object_report_cb(GtkAction *action, OutputType *out)
	{
	if (   !gui_control_is_pressed()
	    && !ActionGetLocation(_("Select the object"))
	   )
		return;
	ActionReport("Object");
	}

static void
drill_summary_cb(GtkAction *action, OutputType *out)
	{
	ActionReport("DrillReport");
	}

static void
found_pins_pads_cb(GtkAction *action, OutputType *out)
	{
	ActionReport("FoundPins");
	}


/* ============== WindowMenu callbacks =============== */
static void
about_dialog_cb(GtkAction *action, OutputType *out)
	{
	gui_dialog_about();
	}

static void
keyref_window_cb(GtkAction *action, OutputType *out)
	{
	gui_keyref_window_show();
	}

static void
library_window_cb(GtkAction *action, OutputType *out)
	{
	gui_library_window_show(&Output);
	}

static void
message_window_cb(GtkAction *action, OutputType *out)
	{
	gui_log_window_show();
	}

static void
netlist_window_cb(GtkAction *action, OutputType *out)
	{
	gui_netlist_window_show(&Output);
	}

static void
command_entry_cb(GtkAction *action, OutputType *out)
	{
	ActionCommand("");
	}



/* ============== PopupMenu callbacks =============== */

static void
radio_select_tool_cb(GtkAction *action, GtkRadioAction *current)
	{
	const gchar	*saction;

	saction = gtk_action_get_name(GTK_ACTION(current));
	g_message("Grid setting action: \"%s\"", saction);
	}



/* ======================================================================
|  Here are the action entries that connect menuitems to the
|  above callbacks.
*/

static GtkActionEntry entries[] =
	{
	/* name, stock_id, label, accelerator, tooltip callback */
	{ "FileMenu", NULL, N_("File") },
		{ "SaveConnectionMenu", NULL, N_("Save connection data of") },

/* FileMenu */
	{ "SaveLayout", NULL, N_("Save layout"), NULL, NULL,
			G_CALLBACK(save_layout_cb) },
	{ "SaveLayoutAs", NULL, N_("Save layout as"), NULL, NULL,
			G_CALLBACK(save_layout_as_cb) },
	{ "Revert", NULL, N_("Revert"), NULL, NULL,
			G_CALLBACK(revert_cb) },
	{ "LoadLayout", NULL, N_("Load layout"), NULL, NULL,
			G_CALLBACK(load_layout_cb) },
	{ "LoadElementData", NULL, N_("Load element data to paste-buffer"),
			NULL, NULL,
			G_CALLBACK(load_element_data_cb) },
	{ "LoadLayoutData", NULL, N_("Load layout data to paste-buffer"),
			NULL, NULL,
			G_CALLBACK(load_layout_data_cb) },
	{ "LoadNetlistFile", NULL, N_("Load netlist file"), NULL, NULL,
			G_CALLBACK(load_netlist_file_cb) },
	{ "LoadVendorFile", NULL, N_("Load vendor resource file"), NULL, NULL,
			G_CALLBACK(load_vendor_file_cb) },
	{ "PrintLayout", NULL, N_("Print layout"), NULL, NULL,
			G_CALLBACK(print_layout_cb) },
	{ "SingleElement", NULL, N_("a single element"), NULL, NULL,
			G_CALLBACK(connections_single_element_cb) },
	{ "AllElements", NULL, N_("all elements"), NULL, NULL,
			G_CALLBACK(connections_all_elements_cb) },
	{ "UnusedPins", NULL, N_("unused pins"), NULL, NULL,
			G_CALLBACK(connections_unused_pins_cb) },
	{ "NewLayout", NULL, N_("Start new layout"), NULL, NULL,
			G_CALLBACK(new_layout_cb) },
	{ "Preferences", NULL, N_("Preferences"), NULL, NULL,
			G_CALLBACK(gui_config_window_show) },
	{ "Quit", NULL, N_("Quit Program"), NULL, NULL,
			G_CALLBACK(quit_cb) },

/* EditMenu */
	{ "EditMenu", NULL, N_("Edit") },
	{ "Undo", NULL, N_("Undo last operation"),
			"u", NULL,
			G_CALLBACK(undo_cb) },
	{ "Redo", NULL, N_("Redo last undone operation"),
			"<shift>r", NULL,
			G_CALLBACK(redo_cb) },
	{ "ClearUndo", NULL, N_("Clear undo-buffer"),
			"<shift><control>u", NULL,
			G_CALLBACK(clear_undo_cb) },
	/* CutSelectionToBuffer CopySelectionToBuffer PasteBufferToLayout
	|  are in BufferMenu
	*/
	/* UnselectAll SelectAll are in SelectMenu
	*/
	{ "EditNamesMenu", NULL, N_("Edit name of") },
	{ "EditTextOnLayout", NULL, "text on layout", "n", NULL,
			G_CALLBACK(edit_text_on_layout_cb) },
	{ "EditNameOfLayout", NULL, N_("layout"), NULL, NULL,
			G_CALLBACK(edit_name_of_layout_cb) },
	{ "EditNameOfActiveLayer", NULL, N_("active layer"), NULL, NULL,
			G_CALLBACK(edit_name_of_active_layer_cb) },

/* ScreenMenu */
	{ "ScreenMenu", NULL, N_("Screen") },
	{ "RedrawLayout", NULL, N_("Redraw layout"),
			"r", NULL,
			G_CALLBACK(redraw_layout_cb) },
	{ "CenterLayout", NULL, N_("Center layout"),
			"c", NULL,
			G_CALLBACK(center_layout_cb) },
	{ "RealignGrid", NULL, N_("Realign grid"), NULL, NULL,
			G_CALLBACK(realign_grid_cb) },
	{ "GridSettingMenu", NULL, N_("Grid setting") },
		/* Some radio actions */
	{ "AdjustGridMenu", NULL, N_("Adjust grid") },
	{ "ZoomIn", NULL, "Zoom in",
			"z", NULL,
			G_CALLBACK(zoom_cb) },
	{ "ZoomOut", NULL, N_("Zoom out"),
			"<shift>z", NULL,
			G_CALLBACK(zoom_cb) },
	{ "DisplayedElementNameMenu", NULL, N_("Displayed element name") },
		/* Radio actions */
	{ "PinoutMenu", NULL, N_("Open pinout menu"),
			"<shift>d", NULL,
			G_CALLBACK(pinout_menu_cb) },

/* SizesMenu */
	{ "SizesMenu", NULL, N_("Sizes") },

/* SettingsMenu */
	{ "SettingsMenu", NULL, N_("Settings") },

/* SelectMenu */
	{ "SelectMenu", NULL, N_("Select") },
	{ "SelectAll", NULL, N_("Select all objects"),
			"<alt>a", NULL,
			G_CALLBACK(select_all_cb) },
	{ "SelectAllConnected", NULL, N_("Select all connected objects"),
			NULL, NULL,
			G_CALLBACK(select_all_connected_cb) },
	{ "UnselectAll", NULL, N_("Unselect all objects"),
			"<shift><alt>a", NULL,
			G_CALLBACK(unselect_all_cb) },
	{ "UnselectAllConnected", NULL, N_("Unselect all connected objects"),
			NULL, NULL,
			G_CALLBACK(unselect_all_connected_cb) },
	{ "SelectByNameMenu", NULL, N_("Select by name") },
	{ "SelectObjectsByName", NULL, N_("All objects"), NULL, NULL,
			G_CALLBACK(select_objects_by_name_cb) },
	{ "SelectElementsByName", NULL, N_("Elements"), NULL, NULL,
			G_CALLBACK(select_elements_by_name_cb) },
	{ "SelectPadsByName", NULL, N_("Pads"), NULL, NULL,
			G_CALLBACK(select_pads_by_name_cb) },
	{ "SelectPinsByName", NULL, N_("Pins"), NULL, NULL,
			G_CALLBACK(select_pins_by_name_cb) },
	{ "SelectTextByName", NULL, N_("Text"), NULL, NULL,
			G_CALLBACK(select_text_by_name_cb) },
	{ "SelectViasByName", NULL, N_("Vias"), NULL, NULL,
			G_CALLBACK(select_vias_by_name_cb) },
	{ "AutoPlaceSelected", NULL, N_("Auto place selected elements"),
			"<control>p", NULL,
			G_CALLBACK(auto_place_selected_cb) },
	{ "DisperseAllElements", NULL, N_("Disperse all elements"), NULL, NULL,
			G_CALLBACK(disperse_all_elements_cb) },
	{ "DisperseSelectedElements", NULL, N_("Disperse selected elements"),
			NULL, NULL,
			G_CALLBACK(disperse_selected_elements_cb) },
	{ "MoveSelectedOtherSide", NULL, N_("Move selected elements to other side"),
			"<shift>b", NULL,
			G_CALLBACK(move_selected_other_side_cb) },
	{ "RemoveSelected", NULL, N_("Remove selected objects"), NULL, NULL,
			G_CALLBACK(remove_selected_cb) },
	{ "ConvertSelectionToElement", NULL, N_("Convert selection to element"),
			NULL, NULL,
			G_CALLBACK(convert_selected_to_element_cb) },
	{ "OptimizeSelectedRats", NULL, N_("Optimize selected rats"), NULL, NULL,
			G_CALLBACK(optimize_selected_rats_cb) },
	{ "AutoRouteSelectedRats", NULL, N_("Auto route selected rats"),
			"<alt>r", NULL,
			G_CALLBACK(auto_route_selected_rats_cb) },
	{ "RipUpSelectedTracks", NULL, N_("Rip up selected auto routed tracks"),
			NULL, NULL,
			G_CALLBACK(rip_up_selected_tracks_cb) },
	{ "ChangeSelectedSizeMenu", NULL, N_("Change size of selected objects") },
		/* Actions in change_selected_entries[] to handle dynamic labels */
	{ "ChangeSelectedDrillMenu", NULL,
					N_("Change drill hole of selected objects") },
		/* Actions in change_selected_entries[] to handle dynamic labels */
	{ "ChangeSelectedSquareMenu", NULL,
					N_("Change square flag of selected objects") },
	{ "ChangeSquareElements", NULL, N_("Elements"), NULL, NULL,
			G_CALLBACK(selected_change_square_elements_cb) },
	{ "ChangeSquarePins", NULL, N_("Pins"), NULL, NULL,
			G_CALLBACK(selected_change_square_pins_cb) },

/* BufferMenu */
	{ "BufferMenu", NULL, "Buffer" },
	{ "CopySelectionToBuffer", NULL, N_("Copy selection to buffer"),
			"<control>x", NULL,
			G_CALLBACK(copy_selection_to_buffer_cb) },
	{ "CutSelectionToBuffer", NULL, N_("Cut selection to buffer"),
			"<shift><control>x", NULL,
			G_CALLBACK(cut_selection_to_buffer_cb) },
	{ "PasteBufferToLayout", NULL, N_("Paste buffer to layout"),
			NULL, NULL,
			G_CALLBACK(paste_buffer_to_layout_cb) },
	{ "RotateBufferCCW", NULL, N_("Rotate buffer 90 deg CCW"), NULL, NULL,
			G_CALLBACK(rotate_buffer_CCW_cb) },
	{ "RotateBufferCW", NULL, N_("Rotate buffer 90 deg CW"), NULL, NULL,
			G_CALLBACK(rotate_buffer_CW_cb) },
	{ "MirrorBufferUpDown", NULL, N_("Mirror buffer (up/down)"), NULL, NULL,
			G_CALLBACK(mirror_buffer_up_down_cb) },
	{ "MirrorBufferLeftRight", NULL, N_("Mirror buffer (left/right)"),
			NULL, NULL,
			G_CALLBACK(mirror_buffer_left_right_cb) },
	{ "ClearBuffer", NULL, N_("Clear buffer"), NULL, NULL,
			G_CALLBACK(clear_buffer_cb) },
	{ "ConvertBufferToElement", NULL, N_("Convert buffer to element"),
			NULL, NULL,
			G_CALLBACK(convert_buffer_to_element_cb) },
	{ "BreakBufferElements", NULL, N_("Break buffer elements to pieces"),
			NULL, NULL,
			G_CALLBACK(break_buffer_elements_cb) },
	{ "SaveBufferElements", NULL, N_("Save buffer elements to file"),
			NULL, NULL,
			G_CALLBACK(save_buffer_elements_cb) },
	{ "SelectCurrentBufferMenu", NULL, N_("Select current buffer") },
		/* Radio action menu */

/* ConnectsMenu */
	{ "ConnectsMenu", NULL, "Connects" },
	{ "LookupConnections", NULL, N_("Lookup connection to object"),
			"<control>f", NULL,
			G_CALLBACK(lookup_connection_cb) },
	{ "ResetScannedPads", NULL, N_("Reset scanned pads/pins/vias"), NULL, NULL,
			G_CALLBACK(reset_scanned_pads_cb) },
	{ "ResetScannedLines", NULL, N_("Reset scanned lines/polygons"), NULL, NULL,
			G_CALLBACK(reset_scanned_lines_cb) },
	{ "ResetAllConnections", NULL, N_("Reset all connections"),
			"<shift>f", NULL,
			G_CALLBACK(reset_all_connections_cb) },
	{ "OptimizeRatsNest", NULL, N_("Optimize rats nest"),
			"o", NULL,
			G_CALLBACK(optimize_rats_nest_cb) },
	{ "EraseRatsNest", NULL, N_("Erase rats nest"),
			"e", NULL,
			G_CALLBACK(erase_rats_nest_cb) },
	{ "AutoRouteSelectedRats", NULL, N_("Auto route selected rats"), NULL, NULL,
			G_CALLBACK(auto_route_selected_rats_cb) },
	{ "AutoRouteAllRats", NULL, N_("Auto route all rats"), NULL, NULL,
			G_CALLBACK(auto_route_all_rats_cb) },
	{ "RipUpAutoRouted", NULL, N_("Rip up all auto routed tracks"), NULL, NULL,
			G_CALLBACK(rip_up_auto_routed_cb) },
	{ "OptimizeTracksMenu", NULL, N_("Optimize routed tracks") },
	{ "AutoOptimize", NULL, N_("Auto optimize"),
			/* "<shift>="*/ NULL, NULL,
			G_CALLBACK(optimize_routes_cb) },
	{ "Debumpify", NULL, N_("Debumpify"), NULL, NULL,
			G_CALLBACK(optimize_routes_cb) },
	{ "Unjaggy", NULL, N_("Unjaggy"), NULL, NULL,
			G_CALLBACK(optimize_routes_cb) },
	{ "ViaNudge", NULL, N_("Via nudge"), NULL, NULL,
			G_CALLBACK(optimize_routes_cb) },
	{ "ViaTrim", NULL, N_("Via trim"), NULL, NULL,
			G_CALLBACK(optimize_routes_cb) },
	{ "OrthoPull", NULL, N_("Ortho pull"), NULL, NULL,
			G_CALLBACK(optimize_routes_cb) },
	{ "SimpleOpts", NULL, N_("Simple optimizations"), NULL, NULL,
			G_CALLBACK(optimize_routes_cb) },
	{ "Miter", NULL, N_("Miter"), NULL, NULL,
			G_CALLBACK(optimize_routes_cb) },
	{ "DesignRuleCheck", NULL, N_("Design Rule Checker"), NULL, NULL,
			G_CALLBACK(design_rule_check_cb) },
	{ "ApplyVendorMapping", NULL, N_("Apply vendor drill mapping"), NULL, NULL,
			G_CALLBACK(apply_vendor_mapping_cb) },

/* InfoMenu */
	{ "InfoMenu", NULL, N_("Info") },
	{ "ObjectReport", NULL, N_("Generate object report"),
			"<control>r", NULL,
			G_CALLBACK(object_report_cb) },
	{ "DrillSummary", NULL, N_("Generate drill summary"), NULL, NULL,
			G_CALLBACK(drill_summary_cb) },
	{ "FoundPinsPads", NULL, N_("Report found pins/pads"), NULL, NULL,
			G_CALLBACK(found_pins_pads_cb) },

/* WindowMenu */
	{ "WindowMenu", NULL, N_("Window") },
	{ "LibraryWindow", NULL, N_("Library"), NULL, NULL,
			G_CALLBACK(library_window_cb) },
	{ "MessageLogWindow", NULL, N_("Message Log"), NULL, NULL,
			G_CALLBACK(message_window_cb) },
	{ "NetlistWindow", NULL, N_("Netlist"), NULL, NULL,
			G_CALLBACK(netlist_window_cb) },
	{ "CommandWindow", NULL, N_("Command Entry"), NULL, NULL,
			G_CALLBACK(command_entry_cb) },
	{ "KeyrefWindow", NULL, N_("Key Reference"), NULL, NULL,
			G_CALLBACK(keyref_window_cb) },
	{ "AboutDialog", NULL, N_("About"), NULL, NULL,
			G_CALLBACK(about_dialog_cb) },

/* PopupMenu */
	{ "SelectionOperationMenu", NULL, N_("Operations on selections") },
	{ "LocationOperationMenu", NULL, N_("Operations on this location") },
	{ "SelectToolMenu", NULL, N_("Select tool") },

	};

static gint n_entries = G_N_ELEMENTS(entries);



  /* These get NULL labels because labels are dynamically set in
  |  gui_change_selected_update_menu_actions()
  */
static GtkActionEntry change_selected_entries[] =
	{
	/* name, stock_id, label, accelerator, tooltip callback */
	{ "-LinesChange", NULL, NULL, NULL, NULL,
			G_CALLBACK(selected_lines_size_change_cb) },
	{ "+LinesChange", NULL, NULL, NULL, NULL,
			G_CALLBACK(selected_lines_size_change_cb) },
	{ "-PadsChange", NULL, NULL, NULL, NULL,
			G_CALLBACK(selected_pads_size_change_cb) },
	{ "+PadsChange", NULL, NULL, NULL, NULL,
			G_CALLBACK(selected_pads_size_change_cb) },
	{ "-PinsChange", NULL, NULL, NULL, NULL,
			G_CALLBACK(selected_pins_size_change_cb) },
	{ "+PinsChange", NULL, NULL, NULL, NULL,
			G_CALLBACK(selected_pins_size_change_cb) },
	{ "-TextChange", NULL, NULL, NULL, NULL,
			G_CALLBACK(selected_text_size_change_cb) },
	{ "+TextChange", NULL, NULL, NULL, NULL,
			G_CALLBACK(selected_text_size_change_cb) },
	{ "-ViasChange", NULL, NULL, NULL, NULL,
			G_CALLBACK(selected_vias_size_change_cb) },
	{ "+ViasChange", NULL, NULL, NULL, NULL,
			G_CALLBACK(selected_vias_size_change_cb) },

	{ "-ViasDrillChange", NULL, NULL, NULL, NULL,
			G_CALLBACK(selected_vias_drill_change_cb) },
	{ "+ViasDrillChange", NULL, NULL, NULL, NULL,
			G_CALLBACK(selected_vias_drill_change_cb) },
	{ "-PinsDrillChange", NULL, NULL, NULL, NULL,
			G_CALLBACK(selected_pins_drill_change_cb) },
	{ "+PinsDrillChange", NULL, NULL, NULL, NULL,
			G_CALLBACK(selected_pins_drill_change_cb) },
	};

static gint n_change_selected_entries = G_N_ELEMENTS(change_selected_entries);


static GtkRadioActionEntry radio_grid_mil_setting_entries[] =
	{
	/* name, stock_id, label, accelerator, tooltip, value */
	{ "grid0",	NULL, "0.1 mil", NULL, NULL, 0 },
	{ "grid1",	NULL, "0.5 mil", NULL, NULL, 1 },
	{ "grid2",	NULL, "1 mil",   NULL, NULL, 2 },
	{ "grid3",	NULL, "5 mil",   NULL, NULL, 3 },
	{ "grid4",	NULL, "10 mil",  NULL, NULL, 4 },
	{ "grid5",	NULL, "25 mil",  NULL, NULL, 5 },
	{ "grid6",	NULL, "50 mil",  NULL, NULL, 6 },
	{ "grid7",	NULL, "100 mil", NULL, NULL, 7 }
	};

static gint n_radio_grid_mil_setting_entries
			= G_N_ELEMENTS(radio_grid_mil_setting_entries);


static GtkRadioActionEntry radio_grid_mm_setting_entries[] =
	{
	/* name, stock_id, label, accelerator, tooltip, value */
	{ "grid0",	NULL, "0.01 mm",  NULL, NULL, 0 },
	{ "grid1",	NULL, "0.02 mm",  NULL, NULL, 1 },
	{ "grid2",	NULL, "0.05 mm",  NULL, NULL, 2 },
	{ "grid3",	NULL, "0.1 mm",  NULL, NULL,  3 },
	{ "grid4",	NULL, "0.2 mm",  NULL, NULL,  4 },
	{ "grid5",	NULL, "0.5 mm",  NULL, NULL,  5 },
	{ "grid6",	NULL, "1 mm",    NULL, NULL,  6 },
	{ "grid7",	NULL, "2 mm",    NULL, NULL,  7 },
	};

static gint n_radio_grid_mm_setting_entries
			= G_N_ELEMENTS(radio_grid_mm_setting_entries);


static GtkRadioActionEntry radio_displayed_element_name_entries[] =
	{
	/* name, stock_id, label, accelerator, tooltip, value */
	{ "Description",  NULL, N_("Description"),  NULL, NULL, 0 },
	{ "ReferenceDesignator",  NULL, N_("Reference designator"),
				NULL, NULL, 1 },
	{ "Value",  NULL, N_("Value"),  NULL, NULL, 2 }
	};

static gint n_radio_displayed_element_name_entries
			= G_N_ELEMENTS(radio_displayed_element_name_entries);


static GtkRadioActionEntry radio_select_current_buffer_entries[] =
	{
	/* name, stock_id, label, accelerator, tooltip, value */
	{ "SelectBuffer1", NULL, "#1", "<control>1", NULL, 1 },
	{ "SelectBuffer2", NULL, "#2", "<control>2", NULL, 2 },
	{ "SelectBuffer3", NULL, "#3", "<control>3", NULL, 3 },
	{ "SelectBuffer4", NULL, "#4", "<control>4", NULL, 4 },
	{ "SelectBuffer5", NULL, "#5", "<control>5", NULL, 5 }
	};

static gint n_radio_select_current_buffer_entries
			= G_N_ELEMENTS(radio_select_current_buffer_entries);

static GtkRadioActionEntry radio_select_tool_entries[] =
	{
	/* name, stock_id, label, accelerator, tooltip, value */
	{ "SelectLineTool", NULL, N_("Line"), NULL, NULL, 0 },
	{ "SelectViaTool", NULL, N_("Via"), NULL, NULL, 0 },
	{ "SelectRectangleTool", NULL, N_("Rectangle"), NULL, NULL, 0 },
	{ "SelectSelectionTool", NULL, N_("Selection"), NULL, NULL, 0 },
	{ "SelectTextTool", NULL, N_("Text"), NULL, NULL, 0 },
	{ "SelectPannerTool", NULL, N_("Panner"), NULL, NULL, 0 },
	};

static gint n_radio_select_tool_entries
			= G_N_ELEMENTS(radio_select_tool_entries);


static GtkToggleActionEntry toggle_entries[] =
	{
	/* name, stock_id, label, accelerator, tooltip, callback, is_active */

	/* ScreenMenu */
	{ "ToggleDrawGrid", NULL, N_("Enable visible grid"), NULL, NULL,
			G_CALLBACK(toggle_draw_grid_cb), FALSE },
	{ "ToggleGridUnitsMm", NULL, N_("Enable millimeter grid units"), NULL, NULL,
			G_CALLBACK(toggle_grid_units_cb), FALSE },
	{ "ToggleViewSolderSide", NULL, N_("Enable view solder side"),
			"<shift>b", NULL,
			G_CALLBACK(toggle_view_solder_side_cb) },
	{ "ToggleShowSolderMask", NULL, N_("Enable view soldermask"), NULL, NULL,
			G_CALLBACK(toggle_show_solder_mask_cb) },
	{ "TogglePinoutShowsNumber", NULL, N_("Enable pinout shows number"),
			NULL, NULL,
			G_CALLBACK(toggle_pinout_shows_number_cb) },

/* SettingsMenu */
	{ "Toggle45degree", NULL, N_("Enable all direction lines"), NULL, NULL,
			G_CALLBACK(toggle_45_degree_cb) },
	{ "ToggleStartDirection", NULL, N_("Enable auto swap line start angle"),
			NULL, NULL,
			G_CALLBACK(toggle_start_direction_cb) },
	{ "ToggleOrthogonalMoves", NULL, N_("Enable orthogonal moves"), NULL, NULL,
			G_CALLBACK(toggle_orthogonal_moves_cb) },
	{ "ToggleSnapPin", NULL, N_("Enable crosshair snaps to pins and pads"),
			NULL, NULL,
			G_CALLBACK(toggle_snap_pin_cb) },
	{ "ToggleShowDRC", NULL, N_("Enable crosshair shows DRC clearance"),
			NULL, NULL,
			G_CALLBACK(toggle_show_DRC_cb) },
	{ "ToggleAutoDrC", NULL, N_("Enable auto enforce DRC clearance"),
			NULL, NULL,
			G_CALLBACK(toggle_auto_DRC_cb) },
	{ "ToggleRubberBand", NULL, N_("Enable rubber band mode"), NULL, NULL,
			G_CALLBACK(toggle_rubber_band_cb) },
	{ "ToggleUniqueNames", NULL, N_("Enable require unique element names"),
			NULL, NULL,
			G_CALLBACK(toggle_unique_names_cb) },
	{ "ToggleLocalRef", NULL, N_("Enable auto zero delta measurements"),
			NULL, NULL,
			G_CALLBACK(toggle_local_ref_cb) },
	{ "ToggleClearLine", NULL, N_("Enable new lines, arcs clear polygons"),
			NULL, NULL,
			G_CALLBACK(toggle_clear_line_cb) },
	{ "ToggleLiveRoute", NULL, N_("Enable show autorouter trials"), NULL, NULL,
			G_CALLBACK(toggle_live_route_cb) },
	{ "ToggleThinDraw", NULL, N_("Enable thin line draw"),
			NULL /* Gtk can't take '\' or '|' */, NULL,
			G_CALLBACK(toggle_thin_draw_cb) },
	{ "ToggleCheckPlanes", NULL, N_("Enable check polygons"), NULL, NULL,
			G_CALLBACK(toggle_check_planes_cb) },
	{ "ToggleVendorDrillMapping", NULL, N_("Enable vendor drill mapping"),
			NULL, NULL,
			G_CALLBACK(toggle_vendor_drill_mapping_cb) },

/* ConnectsMenu */
	{ "ToggleOnlyAutoRoutedNets", NULL, N_("Enable only autorouted nets"),
			NULL, NULL,
			G_CALLBACK(toggle_only_auto_routed_cb) },
	};

static gint n_toggle_entries = G_N_ELEMENTS(toggle_entries);



/* ======================================================================
|  Here is the ui_manager string that defines the order of items displayed
|  in the menus.
*/

static const gchar *ui_info =
"<ui>"
"  <menubar name='MenuBar'>"
"		<menu action='FileMenu'>"
"			<menuitem action='SaveLayout'/>"
"			<menuitem action='SaveLayoutAs'/>"
"			<separator/>"
"			<menuitem action='Revert'/>"
"			<separator/>"
"			<menuitem action='LoadLayout'/>"
"			<menuitem action='LoadElementData'/>"
"			<menuitem action='LoadLayoutData'/>"
"			<menuitem action='LoadNetlistFile'/>"
"			<menuitem action='LoadVendorFile'/>"
"			<separator/>"
"			<menu action='SaveConnectionMenu'>"
"				<menuitem action='SingleElement'/>"
"				<menuitem action='AllElements'/>"
"				<menuitem action='UnusedPins'/>"
"			</menu>"
"			<separator/>"
"			<menuitem action='PrintLayout'/>"
"			<separator/>"
"			<menuitem action='NewLayout'/>"
"			<separator/>"
"			<menuitem action='Preferences'/>"
"			<separator/>"
"			<menuitem action='Quit'/>"
"		</menu>"

"		<menu action='EditMenu'>"
"			<menuitem action='Undo'/>"
"			<menuitem action='Redo'/>"
"			<menuitem action='ClearUndo'/>"
"			<separator/>"
"			<menuitem action='CutSelectionToBuffer'/>"
"			<menuitem action='CopySelectionToBuffer'/>"
"			<menuitem action='PasteBufferToLayout'/>"
"			<separator/>"
"			<menuitem action='UnselectAll'/>"
"			<menuitem action='SelectAll'/>"
"			<separator/>"
"			<menu action='EditNamesMenu'>"
"				<menuitem action='EditTextOnLayout'/>"
"				<menuitem action='EditNameOfLayout'/>"
"				<menuitem action='EditNameOfActiveLayer'/>"
"			</menu>"
"		</menu>"

"		<menu action='ScreenMenu'>"
"			<menuitem action='RedrawLayout'/>"
"			<menuitem action='CenterLayout'/>"
"			<menuitem action='RealignGrid'/>"
"			<separator/>"
"			<menuitem action='ToggleViewSolderSide'/>"
"			<menuitem action='ToggleShowSolderMask'/>"
"			<separator/>"
"			<menuitem action='ToggleDrawGrid'/>"
"			<menuitem action='ToggleGridUnitsMm'/>"
"			<menu action='GridSettingMenu'>"
"				<menuitem action='grid0'/>"
"				<menuitem action='grid1'/>"
"				<menuitem action='grid2'/>"
"				<menuitem action='grid3'/>"
"				<menuitem action='grid4'/>"
"				<menuitem action='grid5'/>"
"				<menuitem action='grid6'/>"
"				<menuitem action='grid7'/>"
"			</menu>"
"			<separator/>"
"			<menuitem action='ZoomIn'/>"
"			<menuitem action='ZoomOut'/>"
"			<separator/>"
"			<menu action='DisplayedElementNameMenu'>"
"				<menuitem action='Description'/>"
"				<menuitem action='ReferenceDesignator'/>"
"				<menuitem action='Value'/>"
"			</menu>"
"			<separator/>"
"			<menuitem action='TogglePinoutShowsNumber'/>"
"			<menuitem action='PinoutMenu'/>"
"		</menu>"

"		<menu action='SizesMenu'>"
"		</menu>"

"		<menu action='SettingsMenu'>"
"			<menuitem action='Toggle45degree'/>"
"			<menuitem action='ToggleStartDirection'/>"
"			<menuitem action='ToggleOrthogonalMoves'/>"
"			<menuitem action='ToggleSnapPin'/>"
"			<menuitem action='ToggleShowDRC'/>"
"			<menuitem action='ToggleAutoDrC'/>"
"			<separator/>"
"			<menuitem action='ToggleRubberBand'/>"
"			<menuitem action='ToggleUniqueNames'/>"
"			<menuitem action='ToggleLocalRef'/>"
"			<menuitem action='ToggleClearLine'/>"
"			<menuitem action='ToggleLiveRoute'/>"
"			<menuitem action='ToggleThinDraw'/>"
"			<menuitem action='ToggleCheckPlanes'/>"
"			<separator/>"
"			<menuitem action='ToggleVendorDrillMapping'/>"
"		</menu>"

"		<menu action='SelectMenu'>"
"			<menuitem action='SelectAll'/>"
"			<menuitem action='SelectAllConnected'/>"
"			<separator/>"
"			<menuitem action='UnselectAll'/>"
"			<menuitem action='UnselectAllConnected'/>"
"			<separator/>"
"			<menu action='SelectByNameMenu'>"
"				<menuitem action='SelectObjectsByName'/>"
"				<menuitem action='SelectElementsByName'/>"
"				<menuitem action='SelectPadsByName'/>"
"				<menuitem action='SelectPinsByName'/>"
"				<menuitem action='SelectTextByName'/>"
"				<menuitem action='SelectViasByName'/>"
"			</menu>"
"			<separator/>"
"			<menuitem action='AutoPlaceSelected'/>"
"			<menuitem action='DisperseAllElements'/>"
"			<menuitem action='DisperseSelectedElements'/>"
"			<separator/>"
"			<menuitem action='MoveSelectedOtherSide'/>"
"			<menuitem action='RemoveSelected'/>"
"			<separator/>"
"			<menuitem action='ConvertSelectionToElement'/>"
"			<separator/>"
"			<menuitem action='OptimizeSelectedRats'/>"
"			<menuitem action='AutoRouteSelectedRats'/>"
"			<menuitem action='RipUpSelectedTracks'/>"
"			<separator/>"
"			<menu action='ChangeSelectedSizeMenu'>"
"				<menuitem action='-LinesChange'/>"
"				<menuitem action='+LinesChange'/>"
"				<menuitem action='-PadsChange'/>"
"				<menuitem action='+PadsChange'/>"
"				<menuitem action='-PinsChange'/>"
"				<menuitem action='+PinsChange'/>"
"				<menuitem action='-TextChange'/>"
"				<menuitem action='+TextChange'/>"
"				<menuitem action='-ViasChange'/>"
"				<menuitem action='+ViasChange'/>"
"			</menu>"
"			<menu action='ChangeSelectedDrillMenu'>"
"				<menuitem action='-ViasDrillChange'/>"
"				<menuitem action='+ViasDrillChange'/>"
"				<menuitem action='-PinsDrillChange'/>"
"				<menuitem action='+PinsDrillChange'/>"
"			</menu>"
"			<menu action='ChangeSelectedSquareMenu'>"
"				<menuitem action='ChangeSquareElements'/>"
"				<menuitem action='ChangeSquarePins'/>"
"			</menu>"
"		</menu>"

"		<menu action='BufferMenu'>"
"			<menuitem action='CopySelectionToBuffer'/>"
"			<menuitem action='CutSelectionToBuffer'/>"
"			<menuitem action='PasteBufferToLayout'/>"
"			<separator/>"
"			<menuitem action='RotateBufferCCW'/>"
"			<menuitem action='RotateBufferCW'/>"
"			<menuitem action='MirrorBufferUpDown'/>"
"			<menuitem action='MirrorBufferLeftRight'/>"
"			<separator/>"
"			<menuitem action='ClearBuffer'/>"
"			<menuitem action='ConvertBufferToElement'/>"
"			<menuitem action='BreakBufferElements'/>"
"			<menuitem action='SaveBufferElements'/>"
"			<separator/>"
"			<menu action='SelectCurrentBufferMenu'>"
"				<menuitem action='SelectBuffer1'/>"
"				<menuitem action='SelectBuffer2'/>"
"				<menuitem action='SelectBuffer3'/>"
"				<menuitem action='SelectBuffer4'/>"
"				<menuitem action='SelectBuffer5'/>"
"			</menu>"
"		</menu>"

"		<menu action='ConnectsMenu'>"
"			<menuitem action='LookupConnections'/>"
"			<menuitem action='ResetScannedPads'/>"
"			<menuitem action='ResetScannedLines'/>"
"			<menuitem action='ResetAllConnections'/>"
"			<separator/>"
"			<menuitem action='OptimizeRatsNest'/>"
"			<menuitem action='EraseRatsNest'/>"
"			<separator/>"
"			<menuitem action='AutoRouteSelectedRats'/>"
"			<menuitem action='AutoRouteAllRats'/>"
"			<menuitem action='RipUpAutoRouted'/>"
"			<separator/>"
"			<menu action='OptimizeTracksMenu'>"
"				<menuitem action='AutoOptimize'/>"
"				<menuitem action='Debumpify'/>"
"				<menuitem action='Unjaggy'/>"
"				<menuitem action='ViaNudge'/>"
"				<menuitem action='ViaTrim'/>"
"				<menuitem action='OrthoPull'/>"
"				<menuitem action='SimpleOpts'/>"
"				<menuitem action='Miter'/>"
"				<separator/>"
"				<menuitem action='ToggleOnlyAutoRoutedNets'/>"
"				</menu>"
"			<separator/>"
"			<menuitem action='DesignRuleCheck'/>"
"			<separator/>"
"			<menuitem action='ApplyVendorMapping'/>"
"		</menu>"

"		<menu action='InfoMenu'>"
"			<menuitem action='ObjectReport'/>"
"			<menuitem action='DrillSummary'/>"
"			<menuitem action='FoundPinsPads'/>"
"		</menu>"

"		<menu action='WindowMenu'>"
"			<menuitem action='LibraryWindow'/>"
"			<menuitem action='MessageLogWindow'/>"
"			<menuitem action='NetlistWindow'/>"
"			<menuitem action='CommandWindow'/>"
"			<menuitem action='KeyrefWindow'/>"
"			<separator/>"
"			<menuitem action='AboutDialog'/>"
"		</menu>"
"	</menubar>"

"	<popup name='PopupMenu'>"
"		<menu action='SelectionOperationMenu'>"
"			<menuitem action='UnselectAll' />"
"			<menuitem action='RemoveSelected' />"
"			<menuitem action='CopySelectionToBuffer' />"
"			<menuitem action='CutSelectionToBuffer' />"
"			<menuitem action='ConvertSelectionToElement' />"
/*"			<menuitem action='BreakElement' />"*/
"			<menuitem action='AutoPlaceSelected' />"
"			<menuitem action='AutoRouteSelectedRats' />"
"			<menuitem action='RipUpSelectedTracks' />"
"		</menu>"
"		<menu action='LocationOperationMenu'>"
/*"			<menuitem action='ToggleNameVisibility' />"*/
/*"			<menuitem action='EditName' />"*/
"			<menuitem action='ObjectReport' />"
/*"			<menuitem action='RotateObjectCCW' />"*/
/*"			<menuitem action='RotateObjectCW' />"*/
/*"			<menuitem action='SendToOtherSide' />"*/
/*"			<menuitem action='ToggleThermal' />"*/
/*"			<menuitem action='LookupConnections' />"*/
"		</menu>"
"		<separator/>"
"		<menuitem action='Undo' />"
"		<menuitem action='Redo' />"
"		<separator/>"
"		<menu action='SelectToolMenu'>"
"			<menuitem action='SelectLineTool' />"
"			<menuitem action='SelectViaTool' />"
"			<menuitem action='SelectRectangleTool' />"
"			<menuitem action='SelectSelectionTool' />"
"			<menuitem action='SelectTextTool' />"
"			<menuitem action='SelectPannerTool' />"
"		</menu>"
#if 0
"		<menuitem name='New' action='SaveLayoutAs' position='top' />"
"		<menu name='HelpMenu' action='OptimizeTracksMenu'>"
"			<menuitem name='About' action='FoundPinsPads' />"
"		</menu>"
#endif
"	</popup>"

"</ui>";


  /* When user toggles grid units mil<->mm or when a new layout is loaded
  |  that might use different units, the "Change size of selected" menu
  |  displayed text muxt be changed.
  */
void
gui_change_selected_update_menu_actions(void)
	{
	gchar		size_buf[32], buf[128];

	if (gui->change_selected_actions)
		{
		/* Remove the existing change selected actions from the menu.
		*/
		gtk_ui_manager_remove_action_group(gui->ui_manager,
					gui->change_selected_actions);
		g_object_unref(gui->change_selected_actions);
		}

	/* And create a new action group for the changed selection actions.
	*/
	gui->change_selected_actions = gtk_action_group_new("ChangeSelActions");
	gtk_action_group_set_translation_domain(gui->change_selected_actions,NULL);
	gtk_ui_manager_insert_action_group(gui->ui_manager,
			gui->change_selected_actions, 0);

	/* Update the labels to match current units and increment values settings.
	*/
	if (Settings.grid_units_mm)
		snprintf(size_buf, sizeof(size_buf), "%.2f mm",
				 Settings.size_increment_mm);
	else
		snprintf(size_buf, sizeof(size_buf), "%.0f mil",
				Settings.size_increment_mil);

	snprintf(buf, sizeof(buf), _("Decrement lines by %s"), size_buf);
	dup_string((gchar **) &change_selected_entries[0].label, buf);

	snprintf(buf, sizeof(buf), _("Increment lines by %s"), size_buf);
	dup_string((gchar **) &change_selected_entries[1].label, buf);

	snprintf(buf, sizeof(buf), _("Decrement pads by %s"), size_buf);
	dup_string((gchar **) &change_selected_entries[2].label, buf);

	snprintf(buf, sizeof(buf), _("Increment pads by %s"), size_buf);
	dup_string((gchar **) &change_selected_entries[3].label, buf);

	snprintf(buf, sizeof(buf), _("Decrement pins by %s"), size_buf);
	dup_string((gchar **) &change_selected_entries[4].label, buf);

	snprintf(buf, sizeof(buf), _("Increment pins by %s"), size_buf);
	dup_string((gchar **) &change_selected_entries[5].label, buf);

	snprintf(buf, sizeof(buf), _("Decrement text by %s"), size_buf);
	dup_string((gchar **) &change_selected_entries[6].label, buf);

	snprintf(buf, sizeof(buf), _("Increment text by %s"), size_buf);
	dup_string((gchar **) &change_selected_entries[7].label, buf);

	snprintf(buf, sizeof(buf), _("Decrement vias by %s"), size_buf);
	dup_string((gchar **) &change_selected_entries[8].label, buf);

	snprintf(buf, sizeof(buf), _("Increment vias by %s"), size_buf);
	dup_string((gchar **) &change_selected_entries[9].label, buf);

	/* -- Drill size changes */
	snprintf(buf, sizeof(buf), _("Decrement vias by %s"), size_buf);
	dup_string((gchar **) &change_selected_entries[10].label, buf);

	snprintf(buf, sizeof(buf), _("Increment vias by %s"), size_buf);
	dup_string((gchar **) &change_selected_entries[11].label, buf);

	snprintf(buf, sizeof(buf), _("Decrement pins by %s"), size_buf);
	dup_string((gchar **) &change_selected_entries[12].label, buf);

	snprintf(buf, sizeof(buf), _("Increment pins by %s"), size_buf);
	dup_string((gchar **) &change_selected_entries[13].label, buf);

	/* And add the actions with new labels back in.
	*/
	gtk_action_group_add_actions(gui->change_selected_actions,
				change_selected_entries, n_change_selected_entries, &Output);
	}

  /* Grid setting labels must also match user and new layout unit changes.
  */
void
gui_grid_setting_update_menu_actions(void)
	{
	gint		i;

	if (gui->grid_actions)
		{
		/* Remove the existing radio grid actions from the menu.
		*/
		gtk_ui_manager_remove_action_group(gui->ui_manager, gui->grid_actions);
		g_object_unref(gui->grid_actions);
		}

	/* And add back actions appropriate for mil or mm grid settings.
	*/
	gui->grid_actions = gtk_action_group_new("GridActions");
	gtk_action_group_set_translation_domain(gui->grid_actions, NULL);
	gtk_ui_manager_insert_action_group(gui->ui_manager, gui->grid_actions, 0);

	/* Get the index of the radio button to set depending on current
	|  PCB Grid value.  But if user hits 'g' key and no grid index matches,
	|  'i' will be -1 and no button will be set active.  At least Gtk docs
	|  say so, but I see different.
	*/
	i = get_grid_value_index(TRUE);

	if (Settings.grid_units_mm)
		gtk_action_group_add_radio_actions(gui->grid_actions,
				radio_grid_mm_setting_entries,
				n_radio_grid_mm_setting_entries,
				i,
				G_CALLBACK(radio_grid_mm_setting_cb), NULL);
	else
		gtk_action_group_add_radio_actions(gui->grid_actions,
				radio_grid_mil_setting_entries,
				n_radio_grid_mil_setting_entries,
				i,
				G_CALLBACK(radio_grid_mil_setting_cb), NULL);
	}


  /* When a new layout is loaded, must set the radio state to the current
  |  "Displayed element name".  Now I unload and reload the actions so
  |  an initial value can be set, but there must be a better way?
  */
static void
update_displayed_name_actions(void)
	{
	gint		i;

	if (gui->displayed_name_actions)
		{
		/* Remove the existing radio actions from the menu.
		*/
		gtk_ui_manager_remove_action_group(gui->ui_manager,
								gui->displayed_name_actions);
		g_object_unref(gui->displayed_name_actions);
		}

	/* And add back actions just to get the initial one set.
	*/
	gui->displayed_name_actions = gtk_action_group_new("DispNameActions");
	gtk_action_group_set_translation_domain(gui->displayed_name_actions, NULL);
	gtk_ui_manager_insert_action_group(gui->ui_manager,
				gui->displayed_name_actions, 0);

	/* Get the index of the radio button to set
	*/
	if (TEST_FLAG (DESCRIPTIONFLAG, PCB))
		i = 0;
	else if (TEST_FLAG (NAMEONPCBFLAG, PCB))
		i = 1;
	else
		i = 2;

	gtk_action_group_add_radio_actions(gui->displayed_name_actions,
			radio_displayed_element_name_entries,
			n_radio_displayed_element_name_entries,
			i,
			G_CALLBACK(radio_displayed_element_name_cb), NULL);
	}


  /* Sync toggle states that were saved with the layout and notify the
  |  config code to update Settings values it manages.
  */
void
gui_sync_with_new_layout(void)
	{
	GtkAction	*action;
	gboolean	old_holdoff;

	/* Just want to update the state of the menus without calling the
	|  action functions at this time because causing a toggle action can
	|  undo the initial condition set we want here.
	*/
	old_holdoff = gui->toggle_holdoff;
	gui->toggle_holdoff = TRUE;

	action = gtk_action_group_get_action(gui->main_actions, "ToggleDrawGrid");
 	gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), Settings.DrawGrid);
/*	g_object_set(action, "sensitive", Settings.XXX, NULL); */

	action = gtk_action_group_get_action(gui->main_actions,
				"ToggleGridUnitsMm");
 	gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action),
				Settings.grid_units_mm);

	/* Toggle actions in the menu which set a PCB flag must be set to
	|  the new layout PCB flag states.  Transient toggle buttons which
	|  do not set a PCB flag don't need setting here.
	*/
	action = gtk_action_group_get_action(gui->main_actions,
				"TogglePinoutShowsNumber");
 	gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action),
				TEST_FLAG(SHOWNUMBERFLAG, PCB));

	action = gtk_action_group_get_action(gui->main_actions,
				"Toggle45degree");
 	gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action),
				TEST_FLAG(ALLDIRECTIONFLAG, PCB));

	action = gtk_action_group_get_action(gui->main_actions,
				"ToggleRubberBand");
 	gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action),
				TEST_FLAG(RUBBERBANDFLAG, PCB));

	action = gtk_action_group_get_action(gui->main_actions,
				"ToggleStartDirection");
 	gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action),
				TEST_FLAG(SWAPSTARTDIRFLAG, PCB));

	action = gtk_action_group_get_action(gui->main_actions,
				"ToggleUniqueNames");
 	gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action),
				TEST_FLAG(UNIQUENAMEFLAG, PCB));

	action = gtk_action_group_get_action(gui->main_actions,
				"ToggleSnapPin");
 	gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action),
				TEST_FLAG(SNAPPINFLAG, PCB));

	action = gtk_action_group_get_action(gui->main_actions,
				"ToggleClearLine");
 	gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action),
				TEST_FLAG(CLEARNEWFLAG, PCB));

	action = gtk_action_group_get_action(gui->main_actions,
				"ToggleOrthogonalMoves");
 	gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action),
				TEST_FLAG(ORTHOMOVEFLAG, PCB));

	action = gtk_action_group_get_action(gui->main_actions,
				"ToggleLiveRoute");
 	gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action),
				TEST_FLAG(LIVEROUTEFLAG, PCB));

	action = gtk_action_group_get_action(gui->main_actions,
				"ToggleShowDRC");
 	gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action),
				TEST_FLAG(SHOWDRCFLAG, PCB));

	action = gtk_action_group_get_action(gui->main_actions,
				"ToggleAutoDrC");
 	gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action),
				TEST_FLAG(AUTODRCFLAG, PCB));

	/* Not sure if I can set a radio button without loading actions, so
	|  load the actions.
	*/
	gui_grid_setting_update_menu_actions();
	update_displayed_name_actions();

	gui->toggle_holdoff = old_holdoff;

	pcb_use_route_style(&PCB->RouteStyle[0]);
	gui_route_style_button_set_active(0);
	gui_config_handle_units_changed();
	gui_change_selected_update_menu_actions();

	gui_zoom_display_update();
	}

  /* Sync toggle states in the menus at startup to Settings values loaded
  |  in the config.
  */
void
gui_init_toggle_states(void)
	{
	GtkAction	*action;
	gboolean	old_holdoff;

	/* Just want to update the state of the menus without calling the
	|  action functions at this time because causing a toggle action can
	|  undo the initial condition set we want here.
	*/
	old_holdoff = gui->toggle_holdoff;
	gui->toggle_holdoff = TRUE;

	action = gtk_action_group_get_action(gui->main_actions, "ToggleDrawGrid");
 	gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), Settings.DrawGrid);

	action = gtk_action_group_get_action(gui->main_actions,
				"ToggleGridUnitsMm");
 	gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action),
				Settings.grid_units_mm);

	action = gtk_action_group_get_action(gui->main_actions,
				"TogglePinoutShowsNumber");
 	gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action),
				Settings.ShowNumber);

	action = gtk_action_group_get_action(gui->main_actions,
				"Toggle45degree");
 	gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action),
				Settings.AllDirectionLines);

	action = gtk_action_group_get_action(gui->main_actions,
				"ToggleRubberBand");
 	gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action),
				Settings.RubberBandMode);

	action = gtk_action_group_get_action(gui->main_actions,
				"ToggleStartDirection");
 	gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action),
				Settings.SwapStartDirection);

	action = gtk_action_group_get_action(gui->main_actions,
				"ToggleUniqueNames");
 	gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action),
				Settings.UniqueNames);

	action = gtk_action_group_get_action(gui->main_actions,
				"ToggleSnapPin");
 	gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action),
				Settings.SnapPin);

	action = gtk_action_group_get_action(gui->main_actions,
				"ToggleClearLine");
 	gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action),
				Settings.ClearLine);

	action = gtk_action_group_get_action(gui->main_actions,
				"ToggleOrthogonalMoves");
 	gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action),
				Settings.OrthogonalMoves);

	action = gtk_action_group_get_action(gui->main_actions,
				"ToggleLiveRoute");
 	gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action),
				Settings.liveRouting);

	action = gtk_action_group_get_action(gui->main_actions,
				"ToggleShowDRC");
 	gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action),
				Settings.ShowDRC);

	action = gtk_action_group_get_action(gui->main_actions,
				"ToggleAutoDrC");
 	gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action),
				Settings.AutoDRC);

	gui->toggle_holdoff = old_holdoff;
	}

  /* The intial loading of all actions at startup.
  */
static void
make_menu_actions(GtkActionGroup *actions, OutputType *out)
	{
	gtk_action_group_add_actions(actions, entries, n_entries, out);

	/* Handle menu actions with dynamic content.
	*/
	gui_change_selected_update_menu_actions();
	gui_grid_setting_update_menu_actions();
	update_displayed_name_actions();

	gtk_action_group_add_radio_actions(actions,
			radio_select_current_buffer_entries,
			n_radio_select_current_buffer_entries,
			0,
			G_CALLBACK(radio_select_current_buffer_cb), NULL);

	gtk_action_group_add_radio_actions(actions,
			radio_select_tool_entries,
			n_radio_select_tool_entries,
			0,
			G_CALLBACK(radio_select_tool_cb), NULL);

	gtk_action_group_add_toggle_actions(actions,
			toggle_entries, n_toggle_entries,
			out);
	}

  /* Make a frame for the top menubar, load in actions for the menus and
  |  load the ui_manager string.
  */
static void
make_top_menubar(GtkWidget *hbox, OutputType *out)
	{
	GtkUIManager	*ui;
	GtkWidget		*frame;
	GtkActionGroup	*actions;
	GError			*error = NULL;


	frame = gtk_frame_new(NULL);
	gtk_box_pack_start(GTK_BOX(hbox), frame, FALSE, TRUE, 0);
	gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_OUT);

	ui = gtk_ui_manager_new();
	gui->ui_manager = ui;

	actions = gtk_action_group_new("Actions");
	gtk_action_group_set_translation_domain(actions, NULL);
	gui->main_actions = actions;
	make_menu_actions(actions, out);

	gtk_ui_manager_insert_action_group(ui, actions, 0);
	gtk_window_add_accel_group(GTK_WINDOW(out->top_window),
			gtk_ui_manager_get_accel_group(ui));

	/* For user customization, we could add
	|  gtk_menu_item_set_accel_path(), gtk_accel_map_save (), etc
	|  But probably can't do this because of command combo box interaction.
	*/

	if (!gtk_ui_manager_add_ui_from_string (ui, ui_info, -1, &error))
		{
		g_message("building menus failed: %s", error->message);
		g_error_free(error);
		}

	gtk_container_add(GTK_CONTAINER(frame),
				gtk_ui_manager_get_widget(ui, "/MenuBar"));
	}


  /* Set the PCB name on a label or on the window title bar.
  */
void
gui_output_set_name_label(gchar *name)
	{
	gchar	*str;

	dup_string(&gui->name_label_string, name);
	if (!gui->name_label_string || !*gui->name_label_string)
		gui->name_label_string = g_strdup(_("Unnamed"));

	if (Settings.gui_title_window)
		{
		gtk_widget_hide(gui->name_label);
		str = g_strdup_printf("PCB:  %s", gui->name_label_string);
		gtk_window_set_title(GTK_WINDOW(Output.top_window), str);
		}
	else
		{
		gtk_widget_show(gui->name_label);
		str = g_strdup_printf(" <b><big>%s</big></b> ",
				gui->name_label_string);
		gtk_label_set_markup(GTK_LABEL(gui->name_label), str);
		gtk_window_set_title(GTK_WINDOW(Output.top_window), "PCB");
		}
	g_free(str);
	}

static void
make_cursor_position_labels(GtkWidget *hbox, OutputType *out)
	{
	GtkWidget	*frame, *label;

	/* The cursor position units label
	*/
	label = gtk_label_new("");
	gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
	gtk_label_set_markup(GTK_LABEL(label),
				Settings.grid_units_mm ?
				"<b>mm</b> " : "<b>mil</b> ");
	gtk_box_pack_end(GTK_BOX(hbox), label, FALSE, TRUE, 0);
	gui->cursor_units_label = label;


	/* The absolute cursor position label
	*/
	frame = gtk_frame_new(NULL);
	gtk_box_pack_end(GTK_BOX(hbox), frame, FALSE, TRUE, 0);
	gtk_container_border_width(GTK_CONTAINER(frame), 2);
	gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_OUT);

	label = gtk_label_new("");
	gtk_container_add(GTK_CONTAINER(frame), label);
	gui->cursor_position_absolute_label = label;

	/* The relative cursor position label
	*/
	frame = gtk_frame_new(NULL);
	gtk_box_pack_end(GTK_BOX(hbox), frame, FALSE, TRUE, 0);
	gtk_container_border_width(GTK_CONTAINER(frame), 2);
	gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_OUT);
	label = gtk_label_new(" __.__  __.__ ");
	gtk_container_add(GTK_CONTAINER(frame), label);
	gui->cursor_position_relative_label = label;
	}


/* ------------------------------------------------------------------
|  Handle the layer buttons.
*/
typedef struct
	{
	GtkWidget	*radio_select_button,
				*layer_enable_button,
				*layer_enable_ebox,
				*label;
	gchar		*text;
	gint		index;
	}
	LayerButtonSet;

static LayerButtonSet	layer_buttons[MAX_LAYER + 5];

static gint				layer_select_button_index;

static gboolean			layer_enable_button_cb_hold_off,
						layer_select_button_cb_hold_off;

static void
layer_select_button_cb(GtkWidget *widget, LayerButtonSet *lb)
	{
	gboolean active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));

	if (!active || layer_select_button_cb_hold_off)
		return;

	ChangeGroupVisibility(lb->index, True, True);

/*	if (GTK_TOGGLE_BUTTON(widget)->active)
		printf("layer selected %d\n", lb->index); */

	layer_select_button_index = lb->index;

	UpdateAll();
	}

static void
layer_enable_button_cb(GtkWidget *widget, gpointer data)
	{
	LayerButtonSet	*lb;
	gint			i, group, layer = GPOINTER_TO_INT(data);
	gboolean		active, redraw = FALSE;

	active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));

	if (layer_enable_button_cb_hold_off)
		return;

	lb = &layer_buttons[layer];
	switch (layer)
		{
		case MAX_LAYER + 2: /* settings for pins */
			PCB->PinOn = active;
			redraw |= (PCB->Data->ElementN != 0);
			break;

		case MAX_LAYER + 3: /* settings for vias */
			PCB->ViaOn = active;
			redraw |= (PCB->Data->ViaN != 0);
			break;

		case MAX_LAYER + 4: /* settings for invisible objects */
			PCB->InvisibleObjectsOn = active;
			PCB->Data->BACKSILKLAYER.On = (active && PCB->ElementOn);
			redraw = True;
			break;

		default:
			/* check if active layer is in the group;
			|  if YES, make a different one active if possible.  Logic from
			|  Xt PCB code.
			*/
			if ((group = GetGroupOfLayer (layer)) ==
						GetGroupOfLayer (MIN (MAX_LAYER, INDEXOFCURRENT)))
				{
				for (i = (layer + 1) % (MAX_LAYER + 1); i != layer;
						i = (i + 1) % (MAX_LAYER + 1))
					if (PCB->Data->Layer[i].On == True &&
							GetGroupOfLayer (i) != group)
						break;
				if (i != layer)
					{
					ChangeGroupVisibility ((int) i, True, True);
					}
				else
					{
					/* everything else off, we can't turn this off too */
					gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),
								TRUE);
					return;
					}
				}
			/* switch layer group on/off */
			ChangeGroupVisibility(layer, active, False);
			redraw = TRUE;
			break;
		}
	if (redraw)
		UpdateAll();
	}

static void
layer_button_set_color(LayerButtonSet *lb, GdkColor *color)
	{
	if (!lb->layer_enable_ebox)
		return;
	gtk_widget_modify_bg(lb->layer_enable_ebox, GTK_STATE_ACTIVE, color);
	gtk_widget_modify_bg(lb->layer_enable_ebox, GTK_STATE_PRELIGHT, color);

	gtk_widget_modify_fg(lb->label, GTK_STATE_ACTIVE,
				&Settings.WhiteColor);
	}

void
layer_enable_button_set_label(GtkWidget *label, gchar *text)
	{
	gchar	*s;

	if (Settings.small_layer_enable_label_markup)
		s = g_strdup_printf("<small>%s</small>", text);
	else
		s = g_strdup(text);
	gtk_label_set_markup(GTK_LABEL(label), s);
	g_free(s);
	}

  /* After layers comes some special cases.  Since silk and netlist (rats)
  |  are selectable as separate drawing areas, they are more consistently
  |  placed after the layers in the gui so the select radio buttons will
  |  be grouped.  This is different from Xt PCB which had a different looking
  |  select interface.
  */
static void
make_layer_buttons(GtkWidget *vbox, OutputType *out)
	{
	LayerButtonSet	*lb;
	GtkWidget		*table, *ebox, *label, *button;
	GdkColor		*color;
	GSList			*group = NULL;
	gchar			*text;
	gint			i;

	label = gtk_label_new(_("Layers"));
	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 4);

	table = gtk_table_new(MAX_LAYER + 5, 2, FALSE);
	gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);

	for (i = 0; i < MAX_LAYER + 5; ++i)
		{
		lb = &layer_buttons[i];
		lb->index = i;

		if (i < GUI_N_SELECTABLE_LAYERS)
			{
			button = gtk_radio_button_new(group);
			group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(button));
			gtk_table_attach_defaults(GTK_TABLE(table), button,
						0, 1, i, i + 1);

			lb->radio_select_button = button;
			g_signal_connect(G_OBJECT(button), "toggled",
						G_CALLBACK(layer_select_button_cb), lb);
			}

		switch (i)
			{
			case GUI_SILK_LAYER:	/* MAX_LAYER	*/
				color = PCB->ElementColor;
				text = _("silk");
				break;

			case GUI_RATS_LAYER:	/* MAX_LAYER + 1 */
				color = PCB->RatColor;
				text = _("rat lines");
				break;

			case MAX_LAYER + 2:
				color = PCB->PinColor;
				text = _("pins/pads");
				break;

			case MAX_LAYER + 3:
				color = PCB->ViaColor;
				text = _("vias");
				break;

			case MAX_LAYER + 4:
				color = PCB->InvisibleObjectsColor;
				text = _("far side");
				break;

			default:
				color = PCB->Data->Layer[i].Color;
				text = UNKNOWN (PCB->Data->Layer[i].Name);
				text = _(text);
				break;
			}
		button = gtk_check_button_new();
		label = gtk_label_new("");
		gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
		layer_enable_button_set_label(label, text);

		ebox = gtk_event_box_new();
		gtk_container_add(GTK_CONTAINER(ebox), label);
		gtk_container_add(GTK_CONTAINER(button), ebox);
		gtk_table_attach_defaults(GTK_TABLE(table), button,
					1, 2, i, i + 1);
/*		gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0); */
		gtk_toggle_button_set_mode(GTK_TOGGLE_BUTTON(button), FALSE);

		lb->layer_enable_button = button;
		lb->layer_enable_ebox = ebox;
		lb->text = g_strdup(text);
		lb->label = label;

		layer_button_set_color(lb, color);
		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);

		g_signal_connect(G_OBJECT(button), "toggled",
					G_CALLBACK(layer_enable_button_cb), GINT_TO_POINTER(i));
		}
	}

  /* If new color scheme is loaded from the config or user changes a color
  |  in the preferences, make sure our layer button colors get updated.
  */
void
gui_layer_buttons_color_update(void)
	{
	GdkColor		*color;
	LayerButtonSet	*lb;
	gint			i;

	if (!Output.drawing_area)
		return;
	for (i = 0; i < MAX_LAYER + 5; ++i)
		{
		lb = &layer_buttons[i];

		if (i == GUI_SILK_LAYER)
			color = PCB->ElementColor;
		else if (i == GUI_RATS_LAYER)
			color = PCB->RatColor;
		else if (i == MAX_LAYER + 2)
			color = PCB->PinColor;
		else if (i == MAX_LAYER + 3)
			color = PCB->ViaColor;
		else if (i == MAX_LAYER + 4)
			color = PCB->InvisibleObjectsColor;
		else
			color = PCB->Data->Layer[i].Color;

		layer_button_set_color(lb, color);
		}
	}


  /* Update layer button labels and enabled state to match current PCB.
  */
void
gui_layer_enable_buttons_update(void)
	{
	LayerButtonSet	*lb;
	gchar			*s;
	gint			i;

	
	/* Update layer button labels and active state to state inside of PCB
	*/
	layer_enable_button_cb_hold_off = TRUE;
	for (i = 0; i < MAX_LAYER; ++i)
		{
		lb = &layer_buttons[i];
		s = UNKNOWN (PCB->Data->Layer[i].Name);
		if (dup_string(&lb->text, s))
			{
			layer_enable_button_set_label(lb->label, _(s));
			gui_config_layer_name_update(_(s), i);
			}
		if (Settings.verbose)
			{
			gboolean	active, new;

			active = gtk_toggle_button_get_active(
						GTK_TOGGLE_BUTTON(lb->layer_enable_button));
			new = PCB->Data->Layer[i].On;
			if (active != new)
				printf("gui_layer_enable_buttons_update: active=%d new=%d\n",
						active, new);
			}
		gtk_toggle_button_set_active(
				GTK_TOGGLE_BUTTON(lb->layer_enable_button),
				PCB->Data->Layer[i].On);
		}
	/* Buttons for elements (silk), rats, pins, vias, and far side don't
	|  change labels.
	*/
	lb = &layer_buttons[i++];		/* GUI_SILK_LAYER */
	gtk_toggle_button_set_active(
				GTK_TOGGLE_BUTTON(lb->layer_enable_button), PCB->ElementOn);

	lb = &layer_buttons[i++];		/* GUI_RATS_LAYER */
	gtk_toggle_button_set_active(
				GTK_TOGGLE_BUTTON(lb->layer_enable_button), PCB->RatOn);

	lb = &layer_buttons[i++];		/* pins/pads	*/
	gtk_toggle_button_set_active(
				GTK_TOGGLE_BUTTON(lb->layer_enable_button), PCB->PinOn);

	lb = &layer_buttons[i++];		/* vias		*/
	gtk_toggle_button_set_active(
				GTK_TOGGLE_BUTTON(lb->layer_enable_button), PCB->ViaOn);

	lb = &layer_buttons[i++];		/* far side	*/
	gtk_toggle_button_set_active(
				GTK_TOGGLE_BUTTON(lb->layer_enable_button),
				PCB->InvisibleObjectsOn);
	layer_enable_button_cb_hold_off = FALSE;
	}


  /* Main layer button synchronization with current PCB state.  Called when
  |  user toggles layer visibility or changes drawing layer or when internal
  |  PCB code changes layer visibility.
  */
void
gui_layer_buttons_update(void)
	{
	gint		layer;
	gboolean	active = FALSE;

	gui_layer_enable_buttons_update();

	/* Turning off a layer that was selected will cause PCB to switch to
	|  another layer.
	*/
	if (PCB->RatDraw)
		layer = GUI_RATS_LAYER;
	else
		layer = PCB->SilkActive ? GUI_SILK_LAYER : LayerStack[0];

	if (layer < MAX_LAYER)
		active = PCB->Data->Layer[layer].On;
	else if (layer == GUI_SILK_LAYER)
		active = PCB->ElementOn;
	else if (layer == GUI_RATS_LAYER)
		active = PCB->RatOn;

	if (Settings.verbose)
		{
		printf("gui_layer_buttons_update cur_index=%d update_index=%d\n",
				layer_select_button_index, layer);
		if (active && layer != layer_select_button_index)
			printf("\tActivating button %d\n", layer);
		}
	if (active && layer != layer_select_button_index)
		{
		layer_select_button_cb_hold_off = TRUE;
		gtk_toggle_button_set_active(
				GTK_TOGGLE_BUTTON(layer_buttons[layer].radio_select_button),
				TRUE);
		layer_select_button_index = layer;
		layer_select_button_cb_hold_off = FALSE;
		}
	}

  /* ------------------------------------------------------------------
  |  Route style buttons
  */
typedef struct
	{
	GtkWidget		*button;
	RouteStyleType	route_style;
	gboolean		shown;			/* For temp buttons */
	}
	RouteStyleButton;

  /* Make 3 extra route style radio buttons.  2 for the extra Temp route
  |  styles, and the 3rd is an always invisible button selected when the
  |  route style settings in use don't match any defined route style (the
  |  user can hit 'l', 'v', etc keys to change the settings without selecting
  |  a new defined style.
  */
static RouteStyleButton	route_style_button[NUM_STYLES + 3];
static gint				route_style_index;

static GtkWidget		*route_style_edit_button;


static void
route_style_edit_cb(GtkWidget *widget, OutputType *out)
	{
	RouteStyleType	*rst = NULL;

	if (route_style_index >= NUM_STYLES)
		rst = &route_style_button[route_style_index].route_style;
	gui_route_style_dialog(route_style_index, rst);
	}

static void
route_style_select_button_cb(GtkToggleButton *button, gpointer data)
	{
	RouteStyleType	*rst;
	gchar			buf[16];
	gint			index	= GPOINTER_TO_INT(data);

	if (gui->toggle_holdoff || index == NUM_STYLES + 2)
		return;

	if (route_style_index == index)
		return;
	route_style_index = index;

	if (index < NUM_STYLES)
		{
		snprintf(buf, sizeof(buf), "%d", index + 1);
		if (gtk_toggle_button_get_active(button))
			ActionRouteStyle(buf);
		}
	else if (index < NUM_STYLES + 2)
		{
		rst = &route_style_button[index].route_style;
		SetLineSize(rst->Thick);
		SetViaSize(rst->Diameter, TRUE);
		SetViaDrillingHole(rst->Hole, TRUE);
		SetKeepawayWidth(rst->Keepaway);
		}
	gtk_widget_set_sensitive(route_style_edit_button, TRUE);
	}

static void
gui_route_style_temp_buttons_hide(void)
	{
	gtk_widget_hide(route_style_button[NUM_STYLES].button);
	gtk_widget_hide(route_style_button[NUM_STYLES + 1].button);

	/* This one never becomes visibile.
	*/
	gtk_widget_hide(route_style_button[NUM_STYLES + 2].button);
	}


static void
make_route_style_buttons(GtkWidget *vbox, OutputType *out)
	{
	GtkWidget			*button;
	GSList				*group = NULL;
	RouteStyleButton	*rbut;
	gint				i;

	button = gtk_button_new_with_label(_("Route Style"));
	gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 3);
	g_signal_connect(button, "clicked",
				G_CALLBACK(route_style_edit_cb), out);
	route_style_edit_button = button;

	for (i = 0; i < NUM_STYLES + 3; ++i)
		{
		RouteStyleType	*rst;
		gchar			buf[32];

		rbut = &route_style_button[i];
		if (i < NUM_STYLES)
			{
			rst = &PCB->RouteStyle[i];
			button = gtk_radio_button_new_with_label(group, _(rst->Name));
			}
		else
			{
			snprintf(buf, sizeof(buf), _("Temp%d"), i - NUM_STYLES + 1);
			button = gtk_radio_button_new_with_label(group, buf);
			if (!route_style_button[i].route_style.Name)
				route_style_button[i].route_style.Name = g_strdup(buf);
			}
		group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(button));
		gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
		rbut->button = button;
		if (i < NUM_STYLES + 2)
			g_signal_connect(G_OBJECT(button), "toggled",
						G_CALLBACK(route_style_select_button_cb),
						GINT_TO_POINTER(i));
		}
	}

void
gui_route_style_button_set_active(gint n)
	{
	if (n < 0 || n >= NUM_STYLES + 3)
		return;

	gtk_toggle_button_set_active(
			GTK_TOGGLE_BUTTON(route_style_button[n].button), TRUE);
	}

  /* Upate the route style button selected to match current route settings.
  |  If user has changed an in use route setting so they don't match any
  |  defined route style, select the invisible dummy route style button.
  */
void
gui_route_style_buttons_update(void)
	{
	RouteStyleType		*rst;
	gint				i;

	for (i = 0; i < NUM_STYLES + 2; ++i)
		{
		if (i < NUM_STYLES)
			rst = &PCB->RouteStyle[i];
		else
			{
			if (!route_style_button[i].shown)	/* Temp button shown? */
				continue;
			rst = &route_style_button[i].route_style;
			}
		if (   Settings.LineThickness == rst->Thick
		    && Settings.ViaThickness == rst->Diameter
		    && Settings.ViaDrillingHole == rst->Hole
		    && Settings.Keepaway == rst->Keepaway
		   )
			break;
		}
	/* If i == NUM_STYLES + 2 at this point, we activate the invisible button.
	*/
	gui->toggle_holdoff = TRUE;
	gui_route_style_button_set_active(i);
	route_style_index = i;
	gui->toggle_holdoff = FALSE;

	gtk_widget_set_sensitive(route_style_edit_button,
				(i == NUM_STYLES + 2) ? FALSE : TRUE);
	}

void
gui_route_style_set_button_label(gchar *name, gint index)
	{
	if (index < 0 || index >= NUM_STYLES || !route_style_button[index].button)
		return;
	gtk_button_set_label(GTK_BUTTON(route_style_button[index].button),
				_(name));
	}

void
gui_route_style_set_temp_style(RouteStyleType *rst, gint which)
	{
	RouteStyleButton	*rsb;
	gchar				*tmp;
	gint				index = which + NUM_STYLES;

	if (which < 0 || which > 1)
		return;
	rsb = &route_style_button[index];
	gtk_widget_show(rsb->button);
	rsb->shown = TRUE;
	tmp = rsb->route_style.Name;
	rsb->route_style = *rst;
	rsb->route_style.Name = tmp;
	if (route_style_index != index)
		{
		route_style_index = index;	/* Sets already done */
		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(rsb->button), TRUE);
		}
	}


/* ---------------------------------------------------------------
|  Zoom spin button
*/
static GtkWidget	*zoom_spin_button;
static gboolean		zoom_holdoff;

static void
zoom_spin_button_cb(GtkWidget *spin_button, gpointer data)
	{
	gint	z;

	if (zoom_holdoff)
		return;
	z = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_button));
	SetZoom(z);
	}

void
gui_zoom_display_update(void)
	{
	zoom_holdoff = TRUE;
	gtk_spin_button_set_value(GTK_SPIN_BUTTON(zoom_spin_button), PCB->Zoom);
	zoom_holdoff = FALSE;
	}


/* ---------------------------------------------------------------
|  Mode buttons
*/
typedef struct
	{
	GtkWidget	*button;
	gchar		*name;
	gint		mode;
	gchar		**xpm;
	}
ModeButton;


static ModeButton	mode_buttons[] =
	{
	{NULL, 	"via",			VIA_MODE,			via},
	{NULL,	"line",			LINE_MODE,			line},
	{NULL,	"arc",			ARC_MODE,			arc},
	{NULL,	"text",			TEXT_MODE,			text},
	{NULL,	"rectangle",	RECTANGLE_MODE,		rect},
	{NULL,	"polygon",		POLYGON_MODE,		poly},
	{NULL,	"buffer",		PASTEBUFFER_MODE,	buf},
	{NULL,	"remove",		REMOVE_MODE,		del},
	{NULL,	"rotate",		ROTATE_MODE,		rot},
	{NULL,	"insertPoint",	INSERTPOINT_MODE,	ins},
	{NULL,	"thermal",		THERMAL_MODE,		thrm},
	{NULL,	"select",		ARROW_MODE,			sel},
	{NULL,	"pan",			PAN_MODE,			pan},
	{NULL,	"lock",			LOCK_MODE,			lock}
	};

static gint	n_mode_buttons = G_N_ELEMENTS(mode_buttons);


static void
mode_button_toggled_cb(GtkWidget *widget, ModeButton *mb)
	{
	gboolean  active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));

	if (active)
		SetMode(mb->mode);
	}

void
gui_mode_buttons_update(void)
	{
	ModeButton	*mb;
	gint		i;

	for (i = 0; i < n_mode_buttons; ++i)
		{
		mb = &mode_buttons[i];
		if (Settings.Mode == mb->mode)
			{
			gtk_toggle_button_set_active(
					GTK_TOGGLE_BUTTON(mb->button), TRUE);
			break;
			}
		}
	}


static void
make_mode_buttons(GtkWidget *vbox, OutputType *out)
	{
	ModeButton		*mb;
	GtkWidget		*hbox = NULL, *button;
	GtkWidget		*image;
	GdkPixbuf		*pixbuf;
	GSList			*group = NULL;
	gint			i;

	for (i = 0; i < n_mode_buttons; ++i)
		{
		if ((i % Settings.n_mode_button_columns) == 0)
			{
			hbox = gtk_hbox_new(FALSE, 0);
			gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
			}
		mb = &mode_buttons[i];
		button = gtk_radio_button_new(group);
		mb->button = button;
		group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(button));
		gtk_toggle_button_set_mode(GTK_TOGGLE_BUTTON(button), FALSE);
		gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);

		pixbuf = gdk_pixbuf_new_from_xpm_data((const char **) mb->xpm);
		image = gtk_image_new_from_pixbuf(pixbuf);
		g_object_unref(G_OBJECT(pixbuf));

		gtk_container_add(GTK_CONTAINER(button), image);
		if (!strcmp(mb->name, "pan"))
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
		g_signal_connect (button, "toggled",
				G_CALLBACK(mode_button_toggled_cb), mb);
		}
	}



/* ---------------------------------------------------------------
|  Top window
*/

static GtkWidget	*gui_left_sensitive_box;

static gint
delete_chart_cb(GtkWidget *widget, GdkEvent *event, OutputType *out)
	{
	/* Return FALSE to emit the "destroy" signal */
	return FALSE;
    }

static void
destroy_chart_cb(GtkWidget *widget, OutputType *out)
	{
	gtk_main_quit();
	}


  /* Create the top_window contents.  The config settings should be loaded
  |  before this is called.
  */
static void
gui_build_pcb_top_window(void)
	{
	GtkWidget	*window;
	GtkWidget	*vbox_main, *vbox_left, *hbox_middle, *hbox = NULL;
	GtkWidget	*viewport, *ebox, *vbox;
	GtkWidget	*label;
	GtkWidget	*separator;
	OutputType	*out	= &Output;

	window = out->top_window;
	out->creating = TRUE;		/* signal holdoff */

	gtk_window_set_default_size(GTK_WINDOW(window),
				Settings.pcb_width, Settings.pcb_height);

	vbox_main = gtk_vbox_new(FALSE, 0);
	gtk_container_add(GTK_CONTAINER(window), vbox_main);

  /* -- Top control bar */
	hbox = gtk_hbox_new(FALSE, 8);
	gtk_box_pack_start(GTK_BOX(vbox_main), hbox, FALSE, FALSE, 0);
	gui->top_hbox = hbox;

	/* menu_hbox will be made insensitive when the gui needs
	|  a modal button GetLocation button press.
	*/
	gui->menu_hbox = gtk_hbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(gui->top_hbox), gui->menu_hbox,
				FALSE, FALSE, 0);

	make_top_menubar(gui->menu_hbox, out);

	gui->compact_vbox = gtk_vbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(gui->top_hbox), gui->compact_vbox,
				FALSE, FALSE, 0);

	gui->compact_hbox = gtk_hbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(gui->top_hbox), gui->compact_hbox,
				FALSE, FALSE, 0);

	/* The zoom spin and board name are in compact_vbox and the position
	|  labels will be packed below them or to the side of them.
	*/
	hbox = gtk_hbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(gui->compact_vbox), hbox, TRUE, FALSE, 3);
	gui_spin_button(hbox, &zoom_spin_button, PCB->Zoom,
				-10.0, 12.0, 1.0, 4.0, 0, 0,
				zoom_spin_button_cb, NULL, FALSE, _("Zoom"));
	label = gtk_label_new("");
	gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
	gtk_label_set_markup(GTK_LABEL(label), "<b><big>Unnamed</big></b>");
	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 4);
	gui->name_label = label;

	/* The position_box pack location depends on user setting of
	|  compact horizontal mode.
	*/
	gui->position_hbox = gtk_hbox_new(FALSE, 0);
	g_object_ref(G_OBJECT(gui->position_hbox));		/* so can remove it */
	if (Settings.gui_compact_horizontal)
		gtk_box_pack_end(GTK_BOX(gui->compact_vbox), gui->position_hbox,
					FALSE, FALSE, 0);
	else
		gtk_box_pack_end(GTK_BOX(gui->top_hbox), gui->position_hbox,
					FALSE, FALSE, 0);

	make_cursor_position_labels(gui->position_hbox, out);

	hbox_middle = gtk_hbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(vbox_main), hbox_middle, TRUE, TRUE, 3);


  /* -- Left control bar */
	ebox = gtk_event_box_new();
	gtk_widget_set_events (ebox, GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK);
	gtk_box_pack_start(GTK_BOX(hbox_middle), ebox, FALSE, FALSE, 3);
	g_signal_connect (ebox, "motion_notify_event",
				G_CALLBACK(gui_output_stop_scroll_cb), out);

	/* This box will also be made insensitive when the gui needs
	|  a modal button GetLocation button press.
	*/
	gui_left_sensitive_box = ebox;

	vbox_left = gtk_vbox_new(FALSE, 0);
	gtk_container_add(GTK_CONTAINER(ebox), vbox_left);

	make_layer_buttons(vbox_left, out);

	separator = gtk_hseparator_new();
	gtk_box_pack_start(GTK_BOX(vbox_left), separator, FALSE, FALSE, 8);

	make_mode_buttons(vbox_left, out);

	separator = gtk_hseparator_new();
	gtk_box_pack_start(GTK_BOX(vbox_left), separator, FALSE, FALSE, 8);

	make_route_style_buttons(vbox_left, out);



	vbox = gtk_vbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(hbox_middle), vbox, TRUE, TRUE, 0);

	hbox = gtk_hbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);

  /* -- The PCB layout output drawing area */
	viewport = gtk_viewport_new(NULL, NULL);
	gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_IN);
	gtk_box_pack_start(GTK_BOX(hbox), viewport, TRUE, TRUE, 0);

	out->drawing_area = gtk_drawing_area_new();

	gtk_widget_add_events(out->drawing_area, GDK_EXPOSURE_MASK
				| GDK_LEAVE_NOTIFY_MASK | GDK_ENTER_NOTIFY_MASK
				| GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK
				| GDK_KEY_RELEASE_MASK | GDK_KEY_PRESS_MASK
				| GDK_FOCUS_CHANGE_MASK | GDK_POINTER_MOTION_MASK);

	/* This is required to get the drawing_area key-press-event.  Also the
	|  enter and button press callbacks grab focus to be sure we have it
	|  when in the drawing_area.
	*/
	GTK_WIDGET_SET_FLAGS(out->drawing_area, GTK_CAN_FOCUS);

	gtk_container_add(GTK_CONTAINER(viewport), out->drawing_area);

	gui->v_adjustment = gtk_adjustment_new(0.0, 0.0, 100.0, 10.0, 10.0, 10.0);
	gui->v_scrollbar = gtk_vscrollbar_new(GTK_ADJUSTMENT(gui->v_adjustment));
	gtk_range_set_update_policy(GTK_RANGE(gui->v_scrollbar),
				GTK_UPDATE_CONTINUOUS);
	gtk_box_pack_start(GTK_BOX(hbox), gui->v_scrollbar, FALSE, FALSE, 0);
	g_signal_connect(G_OBJECT(gui->v_adjustment), "value_changed",
				G_CALLBACK(v_adjustment_changed_cb), gui);

	gui->h_adjustment = gtk_adjustment_new(0.0, 0.0, 100.0, 10.0, 10.0, 10.0);
	gui->h_scrollbar = gtk_hscrollbar_new(GTK_ADJUSTMENT(gui->h_adjustment));
	gtk_range_set_update_policy(GTK_RANGE(gui->h_scrollbar),
				GTK_UPDATE_CONTINUOUS);
	gtk_box_pack_start(GTK_BOX(vbox), gui->h_scrollbar, FALSE, FALSE, 0);
	g_signal_connect(G_OBJECT(gui->h_adjustment), "value_changed",
				G_CALLBACK(h_adjustment_changed_cb), gui);


  /* -- The bottom status line label */
	gui->status_line_hbox = gtk_hbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(vbox), gui->status_line_hbox, FALSE, FALSE, 2);

	label = gtk_label_new("");
	gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
	gtk_box_pack_start(GTK_BOX(gui->status_line_hbox), label, FALSE, FALSE, 0);
	gui->status_line_label = label;

	/* Depending on user setting, the command_combo_box may get packed into
	|  the status_line_hbox, but it will happen on demand the first time
	|  the user does a command entry.
	*/

	g_signal_connect(G_OBJECT(out->drawing_area), "expose_event",
				G_CALLBACK(gui_output_drawing_area_expose_event_cb), out);
	g_signal_connect(G_OBJECT(out->top_window), "configure_event",
				G_CALLBACK(top_window_configure_event_cb), out);
	g_signal_connect(G_OBJECT(out->drawing_area), "configure_event",
				G_CALLBACK(gui_output_drawing_area_configure_event_cb), out);

	/* Mouse and key events will need to be intercepted when PCB needs a
	|  location from the user.
	*/
	gui_interface_input_signals_connect();

	g_signal_connect(G_OBJECT(out->drawing_area), "scroll_event",
				G_CALLBACK(gui_output_window_mouse_scroll_cb), out);
	g_signal_connect(G_OBJECT(out->drawing_area), "enter_notify_event",
				G_CALLBACK(gui_output_window_enter_cb), out);
	g_signal_connect(G_OBJECT(out->drawing_area), "leave_notify_event",
				G_CALLBACK(gui_output_window_leave_cb), out);
	g_signal_connect(G_OBJECT(out->drawing_area), "motion_notify_event",
				G_CALLBACK(gui_output_window_motion_cb), out);


	g_signal_connect(G_OBJECT(window), "delete_event",
			G_CALLBACK(delete_chart_cb), out);
	g_signal_connect(G_OBJECT(window), "destroy",
			G_CALLBACK(destroy_chart_cb), out);

	out->creating = FALSE;

	gtk_widget_show_all(out->top_window);
	gtk_widget_realize(vbox_main);
	gtk_widget_realize(hbox_middle);
	gtk_widget_realize(viewport);
	gtk_widget_realize(out->drawing_area);
	gdk_window_set_back_pixmap(out->drawing_area->window, NULL, FALSE);

	gui_route_style_temp_buttons_hide();
	}

  /* Connect and disconnect just the signals a g_main_loop() will need.
  |  Cursor and motion events still need to be handled by the top level
  |  loop, so don't connect/reconnect these.
  |  A g_main_loop will be running when PCB wants the user to select a
  |  location or if command entry is needed in the status line hbox.
  |  During these times normal button/key presses are intercepted, either
  |  by new signal handlers or the command_combo_box entry.
  */
static gulong	button_press_handler, button_release_handler,
				key_press_handler, key_release_handler;

void
gui_interface_input_signals_connect(void)
	{
	OutputType	*out	= &Output;
	
	button_press_handler = 
		g_signal_connect(G_OBJECT(out->drawing_area), "button_press_event",
					G_CALLBACK(gui_output_button_press_cb), gui->ui_manager);

	button_release_handler =
		g_signal_connect(G_OBJECT(out->drawing_area), "button_release_event",
					G_CALLBACK(gui_output_button_release_cb), gui->ui_manager);

	key_press_handler =
		g_signal_connect(G_OBJECT(out->drawing_area), "key_press_event",
					G_CALLBACK(gui_output_key_press_cb), gui->ui_manager);

	key_release_handler =
		g_signal_connect(G_OBJECT(out->drawing_area), "key_release_event",
					G_CALLBACK(gui_output_key_release_cb), gui->ui_manager);
	}

void
gui_interface_input_signals_disconnect(void)
	{
	OutputType	*out	= &Output;
	
	if (button_press_handler)
		g_signal_handler_disconnect(out->drawing_area, button_press_handler);

	if (button_release_handler)
		g_signal_handler_disconnect(out->drawing_area, button_release_handler);

	if (key_press_handler)
		g_signal_handler_disconnect(out->drawing_area, key_press_handler);

	if (key_release_handler)
		g_signal_handler_disconnect(out->drawing_area, key_release_handler);

	button_press_handler = button_release_handler = 0;
	key_press_handler = key_release_handler = 0;
	}

  /* We'll set the interface insensitive when a g_main_loop is running so the
  |  Gtk menus and buttons don't respond and interfere with the special entry
  |  the user needs to be doing.
  */
void
gui_interface_set_sensitive(gboolean sensitive)
	{
	gtk_widget_set_sensitive(gui_left_sensitive_box, sensitive);
	gtk_widget_set_sensitive(gui->menu_hbox, sensitive);
	}

/* ---------------------------------------------------------------------------
 *  Stipple patterns with 50 % pixels
 */
#define	GRAY_SIZE	8

typedef unsigned char pattern[8];

static pattern gray_bits[] = {
  {0xa5, 0x5a, 0xa5, 0x5a, 0xa5, 0x5a, 0xa5, 0x5a},
  {0x33, 0xcc, 0x33, 0xcc, 0x33, 0xcc, 0x33, 0xcc},
  {0xcc, 0x33, 0xcc, 0x33, 0xcc, 0x33, 0xcc, 0x33},
  {0x88, 0x77, 0x88, 0x77, 0x88, 0x77, 0x88, 0x77},
  {0x77, 0x88, 0x77, 0x88, 0x77, 0x88, 0x77, 0x88},
  {0xee, 0x11, 0xee, 0x11, 0xee, 0x11, 0xee, 0x11},
  {0x11, 0xee, 0x11, 0xee, 0x11, 0xee, 0x11, 0xee},
  {0x96, 0x69, 0x96, 0x69, 0x96, 0x69, 0x96, 0x69},
  {0x69, 0x96, 0x69, 0x96, 0x69, 0x96, 0x69, 0x96}
};

static void
gui_init_gc(OutputType *out)
	{
	GdkBitmap	*dummy_bitmap;
	GdkColor	bit_color;
	gint		i;

	out->bgGC = gdk_gc_new(out->top_window->window);
	gdk_gc_copy(out->bgGC, out->top_window->style->white_gc );

	out->fgGC = gdk_gc_new(out->top_window->window);
	gdk_gc_copy(out->fgGC, out->top_window->style->white_gc );

	out->pmGC = gdk_gc_new(out->top_window->window);
	gdk_gc_copy(out->pmGC, out->top_window->style->white_gc );

	out->GridGC = gdk_gc_new(out->top_window->window);
	gdk_gc_copy(out->GridGC, out->top_window->style->white_gc );

	gdk_gc_set_foreground(out->bgGC, &Settings.BackgroundColor);
	gdk_gc_set_fill(out->fgGC, GDK_SOLID);

	/* Set up the depth 1 pmGC.
	*/
	dummy_bitmap = gdk_pixmap_new(out->top_window->window, 16, 16, 1);
	out->pmGC = gdk_gc_new(dummy_bitmap);
	bit_color.pixel = 1;
	gdk_gc_set_foreground(out->pmGC, &bit_color);
	g_object_unref(G_OBJECT(dummy_bitmap));

	Stipples = g_new0(GdkPixmap *, 9);
	for (i = 0; i < 9; ++i)
		Stipples[i] = gdk_bitmap_create_from_data(out->top_window->window,
						(const gchar *) gray_bits[i], GRAY_SIZE, GRAY_SIZE);

	gdk_gc_set_foreground(out->GridGC, &Settings.GridColor);
	gdk_gc_set_background(out->GridGC, &Settings.BackgroundColor);
	gdk_gc_set_function(out->GridGC, GDK_INVERT);
	gdk_gc_set_line_attributes(out->GridGC, 2,
			GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
	}

/* ----------------------------------------------------------------------
 * initializes icon pixmap and also cursor bit maps
 */
static void
gui_init_icons(OutputType *out)
	{
	GdkPixbuf	*icon;

	icon = gdk_pixbuf_new_from_xpm_data((const gchar **) icon_bits);
	gtk_window_set_default_icon(icon);

	XC_clock_source = gdk_bitmap_create_from_data(out->top_window->window,
						rotateIcon_bits,
						rotateIcon_width, rotateIcon_height);
	XC_clock_mask = gdk_bitmap_create_from_data(out->top_window->window,
						rotateMask_bits,
						rotateMask_width, rotateMask_height);

	XC_hand_source = gdk_bitmap_create_from_data(out->top_window->window,
						handIcon_bits,
						handIcon_width, handIcon_height);
	XC_hand_mask =  gdk_bitmap_create_from_data(out->top_window->window,
						handMask_bits,
						handMask_width, handMask_height);

	XC_lock_source =  gdk_bitmap_create_from_data(out->top_window->window,
						lockIcon_bits,
						lockIcon_width, lockIcon_height);
	XC_lock_mask =  gdk_bitmap_create_from_data(out->top_window->window,
						lockMask_bits,
						lockMask_width, lockMask_height);
	}

void
gui_create_pcb_widgets(void)
	{
	OutputType	*out	= &Output;

	gui_build_pcb_top_window();
	gui_init_toggle_states();
	gui_init_gc(out);
	gui_init_icons(out);
	}

  /* Create top level window for routines that will need top_window
  |  before gui_create_pcb_widgets() is called.
  */
void
gui_init(gint *argc, gchar ***argv)
	{
	GtkWidget	*window;
	OutputType	*out	= &Output;
	gint i;

	/* Threads aren't used in PCB, but this call would go here.
	*/
/*	g_thread_init(NULL); */

#if defined (ENABLE_NLS)
	/* Do our own setlocale() stufff since we want to override LC_NUMERIC
	*/
	gtk_set_locale();
	setlocale(LC_NUMERIC, "POSIX");  /* use decimal point instead of comma */
#endif

	/* Prevent gtk_init() and gtk_init_check() from automatically
	| calling setlocale (LC_ALL, "") which would undo LC_NUMERIC if ENABLE_NLS
	| We also don't want locale set if no ENABLE_NLS to keep POSIX LC_NUMERIC.
	*/
	gtk_disable_setlocale();

	gtk_init(argc, argv);
	gtk_widget_push_colormap(gdk_rgb_get_colormap());  /* need this ?? */

	Settings.AutoPlace = 0;
	for(i=0; i<*argc; i++)
	{
		if( strcmp((*argv)[i], "-auto-place") == 0)
			Settings.AutoPlace = 1;
	}
#ifdef ENABLE_NLS
#ifdef LOCALEDIR
	bindtextdomain(PACKAGE, LOCALEDIR);
#endif
	textdomain(PACKAGE);
	bind_textdomain_codeset(PACKAGE, "UTF-8");
#endif  /* ENABLE_NLS */

	gui = g_new0(GuiPCB, 1);

	window = out->top_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	gtk_window_set_title(GTK_WINDOW(window), "PCB");

	if( Settings.AutoPlace )
	  gtk_widget_set_uposition( GTK_WIDGET(window), 10, 10);

	gtk_widget_realize(out->top_window);
	}

Attachment: pgpbJ4zmo9Z2f.pgp
Description: PGP signature