/****************************************************************************************************************
 *
 *  Copyright (c) 1992 by Antoine Dumesnil de Maricourt. All rights reserved.
 *
 *  This program is distributed in the hope that it will be useful.
 *  Use and copying of this software and preparation of derivative works
 *  based upon this software are permitted, so long as the following
 *  conditions are met:
 *       o credit to the authors is acknowledged following current
 *         academic behaviour
 *       o no fees or compensation are charged for use, copies, or
 *         access to this software
 *       o this copyright notice is included intact.
 *  This software is made available AS IS, and no warranty is made about 
 *  the software or its performance. 
 * 
 *  Bug descriptions, use reports, comments or suggestions are welcome.
 *  Send them to    dumesnil@etca.fr   or to:
 *       
 *       Antoine de Maricourt
 *       ETCA CREA-SP
 *       16 bis, avenue Prieur de la Cote d'Or
 *       94114 Arcueil Cedex
 *       France
 */

#include <X11/IntrinsicP.h>
#include <X11/StringDefs.h>
#include <X11/Xlib.h>
#include <X11/cursorfont.h>

#include "GobanP.h"

#include "whitestone.bm"
#include "blackstone.bm"
#include "graystone.bm"
#include "stonemask.bm"

#define offset(field) XtOffset (GobanWidget, goban.field)

static XtResource resources[] = {
  { XtNautoRedisplay     , XtCAutoRedisplay     , XtRBoolean   , sizeof (Boolean)      ,
      offset (auto_redisplay)     , XtRImmediate     , (caddr_t) TRUE         },
  { XtNviewBottom        , XtCPosition          , XtRPosition  , sizeof (Position)     ,
      offset (bottom)             , XtRImmediate     , (caddr_t) 1            },
  { XtNcursor            , XtCCursor            , XtRInt       , sizeof (int)          ,
      offset (cursor)             , XtRImmediate     , (caddr_t) GbCGrayStone },
  { XtNfont              , XtCFont              , XtRFontStruct, sizeof (XFontStruct *),
      offset (font)               , XtRString        , "fixed"                },
  { XtNforeground        , XtCForeground        , XtRPixel     , sizeof (Pixel)        ,
      offset (foreground)         , XtRString   , XtDefaultForeground    },
  { XtNgameSize          , XtCSize              , XtRDimension , sizeof (Dimension)    ,
      offset (game_size)          , XtRImmediate, (caddr_t) 19           },
  { XtNviewLeft          , XtCPosition          , XtRPosition  , sizeof (Position)     ,
      offset (left)               , XtRImmediate, (caddr_t) 1            },
  { XtNdisplayCoordinates, XtCDisplayCoordinates, XtRBoolean   , sizeof (Boolean)      ,
      offset (display_coordinates), XtRImmediate, (caddr_t) TRUE         },
  { XtNpointSize         , XtCSize              , XtRDimension , sizeof (Dimension)    ,
      offset (point_size)         , XtRImmediate     , (caddr_t) 26           },
  { XtNviewRight         , XtCPosition          , XtRPosition  , sizeof (Position)     ,
      offset (right)              , XtRImmediate     , (caddr_t) 19           },
  { XtNviewTop           , XtCPosition          , XtRPosition  , sizeof (Position)     , 
      offset (top)                , XtRImmediate     , (caddr_t) 19           },
  { XtNwhiteStoneForeground, XtCForeground        , XtRPixel     , sizeof (Pixel)        ,
      offset (white_fg)           , XtRString   , "black"    },
  { XtNwhiteStoneBackground, XtCBackground        , XtRPixel     , sizeof (Pixel)        ,
      offset (white_bg)           , XtRString   , "white"    },
  { XtNwhiteStoneBorder    , XtCBorderColor       , XtRPixel     , sizeof (Pixel)        ,
      offset (white_bd)           , XtRString   , "black"    },
  { XtNblackStoneForeground, XtCForeground        , XtRPixel     , sizeof (Pixel)        ,
      offset (black_fg)           , XtRString   , "white"    },
  { XtNblackStoneBackground, XtCBackground        , XtRPixel     , sizeof (Pixel)        ,
      offset (black_bg)           , XtRString   , "black"    },
  { XtNblackStoneBorder    , XtCBorderColor       , XtRPixel     , sizeof (Pixel)        ,
      offset (black_bd)           , XtRString   , "black"    },
};

static void    ClassInitialize ();
static void    Initialize      ();
static void    Redisplay       ();
static void    Realize         ();
static void    Destroy         ();
static void    Resize          ();
static void    DrawPoint       ();
static Boolean SetValues       ();

GobanClassRec gobanClassRec = {
  { /* core fields */
    /* superclass		*/	(WidgetClass) &widgetClassRec,
    /* class_name		*/	"Goban",
    /* widget_size		*/	sizeof (GobanRec),
    /* class_initialize		*/	ClassInitialize,
    /* class_part_initialize	*/	NULL,
    /* class_inited		*/	FALSE,
    /* initialize		*/	Initialize,
    /* initialize_hook		*/	NULL,
    /* realize			*/	Realize,
    /* actions			*/	NULL,
    /* num_actions		*/	0,
    /* resources		*/	resources,
    /* num_resources		*/	XtNumber (resources),
    /* xrm_class		*/	NULLQUARK,
    /* compress_motion		*/	TRUE,
    /* compress_exposure	*/	TRUE,
    /* compress_enterleave	*/	TRUE,
    /* visible_interest		*/	FALSE,
    /* destroy			*/	Destroy,
    /* resize			*/	Resize,
    /* expose			*/	Redisplay,
    /* set_values		*/	SetValues,
    /* set_values_hook		*/	NULL,
    /* set_values_almost	*/	XtInheritSetValuesAlmost,
    /* get_values_hook		*/	NULL,
    /* accept_focus		*/	NULL,
    /* version			*/	XtVersion,
    /* callback_private		*/	NULL,
    /* tm_table			*/	NULL,
    /* query_geometry		*/	XtInheritQueryGeometry,
    /* display_accelerator	*/	XtInheritDisplayAccelerator,
    /* extension		*/	NULL
  }
};

WidgetClass gobanWidgetClass = (WidgetClass) &gobanClassRec;
 
/****************************************************************************************************************
 */

void GbRedisplayBoard (w)
Widget w;
{
  GobanWidget gw      = (GobanWidget) w;
  Dimension   width   = w->core.width;
  Dimension   height  = w->core.height;

  if (XtIsRealized (w))
    XCopyArea (XtDisplay (w), gw->goban.picture, XtWindow (w), gw->goban.copy_gc, 0, 0, width, height, 0, 0);
}

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

void GbClearBoard (w)
Widget w;
{
  GobanWidget gw        = (GobanWidget) w;
  Boolean     redisplay = gw->goban.auto_redisplay;
  Position    x;
  Position    y;

  gw->goban.auto_redisplay = FALSE;

  for (x = 1; x <= gw->goban.game_size; x++)
    for (y = 1; y <= gw->goban.game_size; y++)
      GbSetPoint (w, x, y, GbEmptyPoint);

  gw->goban.auto_redisplay = redisplay;

  if (redisplay == TRUE)
    GbRedisplayBoard (w);
}

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

void GbClearMarks (w)
Widget w;
{
  GobanWidget gw        = (GobanWidget) w;
  Boolean     redisplay = gw->goban.auto_redisplay;
  Position    x;
  Position    y;

  gw->goban.auto_redisplay = FALSE;

  for (x = 1; x <= gw->goban.game_size; x++)
    for (y = 1; y <= gw->goban.game_size; y++) {

      gw->goban.points[x][y].mark1 = 0;
      gw->goban.points[x][y].mark2 = 0;
      gw->goban.points[x][y].num   = 0;
    
      if (x >= gw->goban.left && y >= gw->goban.bottom && x <= gw->goban.right && y <= gw->goban.top)
	if (gw->goban.points[x][y].free == 1)
	  DrawPoint (w, x, y, GbEmptyPoint);
	else
	  if (gw->goban.points[x][y].black == 1)
	    DrawPoint (w, x, y, GbBlackStone);
	  else
	    DrawPoint (w, x, y, GbWhiteStone);
    }

  gw->goban.auto_redisplay = redisplay;

  if (redisplay == TRUE)
    GbRedisplayBoard (w);
}

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

/* ARGSUSED */

void GbSetPoint (w, x, y, color)
Widget       w;
Position     x;
Position     y;
GbPointState color;
{
  GobanWidget gw = (GobanWidget) w;

  if (x >= 1 && y >= 1 && x <= gw->goban.game_size && y <= gw->goban.game_size) {
    switch (color) {

    case GbBlackStone :
      gw->goban.points[x][y].free  = 0;
      gw->goban.points[x][y].black = 1;
      break;

    case GbWhiteStone :
      gw->goban.points[x][y].free  = 0;
      gw->goban.points[x][y].black = 0;
      break;

    case GbEmptyPoint :
      gw->goban.points[x][y].free  = 1;
      break;
    }
    
    gw->goban.points[x][y].num   = 0;
    gw->goban.points[x][y].mark1 = 0;
    gw->goban.points[x][y].mark2 = 0;

    if (x >= gw->goban.left && y >= gw->goban.bottom && x <= gw->goban.right && y <= gw->goban.top)
      DrawPoint (w, x, y, color);
  }
}

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

/* ARGSUSED */

Boolean GbGetPositionFromStone (w, x, y)
GobanWidget   w;
Position     *x;
Position     *y;
{
  if (*x >= w->goban.left && *y >= w->goban.bottom && *x <= w->goban.right && *y <= w->goban.top) {
	
    *x = ((w->goban.x_offset + w->goban.point_size * *x) * 2 + w->goban.point_size) / 2;
    *y = ((w->goban.y_offset - w->goban.point_size * *y) * 2 + w->goban.point_size) / 2;
    
    return TRUE;
  }
  
  else 
    return FALSE;
}

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

/* ARGSUSED */

Boolean GbGetStoneFromPosition (w, x, y)
Widget     w;
Position  *x;
Position  *y;
{
  GobanWidget gw         = (GobanWidget) w;
  Dimension   point_size = gw->goban.point_size;
  Position    xmin       = gw->goban.x_offset + point_size * gw->goban.left + 1;
  Position    ymin       = gw->goban.y_offset - point_size * gw->goban.top  + 1;
  Position    xmax       = gw->goban.x_offset + point_size * (gw->goban.right  + 1) - 1;
  Position    ymax       = gw->goban.y_offset - point_size * (gw->goban.bottom - 1) - 1;

  if (*x > xmin && *x < xmax && *y > ymin && *y < ymax) {

    *x = (*x - gw->goban.x_offset - 1) / point_size;
    *y = (gw->goban.y_offset - *y + point_size + 1) / point_size;

    return TRUE;
  }

  return FALSE;
}

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

void GbSetNum (w, x, y, num)
Widget      w;
Position    x;
Position    y;
int         num;
{
  GobanWidget  gw      = (GobanWidget) w;
  Display     *display = XtDisplay (w);
  GC           draw_gc;
  GC           undraw_gc;
  XCharStruct  extent;
  XExposeEvent event;
  int          ascent;
  int          descent;
  int          direction;
  int          X;
  int          Y;
  char         s[3];
  int          len = 0;

  if (num >= 0 && num <= 999 && x >= 1 && y >= 1  && x <= gw->goban.game_size && y <= gw->goban.game_size) {
	
    gw->goban.points[x][y].num   = num;
    gw->goban.points[x][y].mark1 = 0;
    gw->goban.points[x][y].mark2 = 0;
    
    if (x >= gw->goban.left && y >= gw->goban.bottom && x <= gw->goban.right && y <= gw->goban.top) {
      
      if (gw->goban.points[x][y].free == 0) {
	
	if (num == 0) {
	  if (gw->goban.points[x][y].black == 1)
	    DrawPoint (w, x, y, GbBlackStone);
	  else
	    DrawPoint (w, x, y, GbWhiteStone);
	}
	
	else {
	  if (gw->goban.points[x][y].black == 1) {
	    draw_gc   = gw->goban.black_fg_gc;
	    undraw_gc = gw->goban.black_bg_gc;
	  } else {
	    draw_gc   = gw->goban.white_fg_gc;
	    undraw_gc = gw->goban.white_bg_gc;
	  }
	  
	  X = (gw->goban.x_offset + gw->goban.point_size * x) * 2 + gw->goban.point_size;
	  Y = (gw->goban.y_offset - gw->goban.point_size * y) * 2 + gw->goban.point_size;
	  
	  if (num > 99)
	    s[len++] = '0' + (num % 1000) / 100;
	  if (num > 9)
	    s[len++] = '0' + (num % 100) / 10;
	  s[len++] = '0' + num % 10;
	  
	  XQueryTextExtents (display, XGContextFromGC (draw_gc),
			     s, len, &direction, &ascent, &descent, &extent);
	  
	  if (extent.width + extent.descent + extent.ascent < gw->goban.point_size - 1) {
	    XFillArc (display, gw->goban.picture, undraw_gc, 
		      (int) (X - gw->goban.point_size) / 2 + 2, (int) (Y - gw->goban.point_size) / 2 + 2, 
		      (unsigned int) gw->goban.point_size - 4, (unsigned int) gw->goban.point_size - 4, 
		      0, 360 * 64);
	    XDrawString (display, gw->goban.picture, draw_gc,
			 (X - extent.width) / 2 - extent.lbearing + 1, 
			 (Y + extent.ascent - extent.descent) / 2 + 1, s, len);
	  }
	
	  if (gw->goban.auto_redisplay == TRUE && XtIsRealized (w) == TRUE) {
	    event.x      = (X - gw->goban.point_size) / 2;
	    event.y      = (Y - gw->goban.point_size) / 2;
	    event.width  = gw->goban.point_size;
	    event.height = gw->goban.point_size;
	    
	    Redisplay (w, &event, (Region) NULL);
	  }
	}
      }
    }
  }
}

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

void GbSetMark (w, x, y, c1, c2)
Widget        w;
Position      x;
Position      y;
unsigned char c1;
unsigned char c2;
{
  GobanWidget  gw      = (GobanWidget) w;
  Display     *display = XtDisplay (w);
  int          len     = 0;
  XCharStruct  extent;
  XExposeEvent event;
  int          ascent;
  int          descent;
  int          direction;
  int          X;
  int          Y;
  int          radius;
  char         s[2];
  XSegment     segment[4];
  GC           mark_gc;
  GC           erase_gc;
  
  if (x >= 1 && y >= 1 && x <= gw->goban.game_size && y <= gw->goban.game_size) {
   
    if (c1 != 0 || c2 != 0)
      GbSetMark (w, x, y, 0, 0);
     
    gw->goban.points[x][y].mark1 = c1;
    gw->goban.points[x][y].mark2 = c2;
    gw->goban.points[x][y].num   = 0;

    if (x >= gw->goban.left && y >= gw->goban.bottom && x <= gw->goban.right && y <= gw->goban.top) {
      
      if (c1 == 0 && c2 == 0) {
	if (gw->goban.points[x][y].free == 0)
	  if (gw->goban.points[x][y].black == 1)
	    DrawPoint (w, x, y, GbBlackStone);
	  else
	    DrawPoint (w, x, y, GbWhiteStone);
	else
	  DrawPoint (w, x, y, GbEmptyPoint);
      }
      
      else {
	X = (gw->goban.x_offset + gw->goban.point_size * x) * 2 + gw->goban.point_size;
	Y = (gw->goban.y_offset - gw->goban.point_size * y) * 2 + gw->goban.point_size;
	  	    
	if (gw->goban.points[x][y].free == 0)
	  if (gw->goban.points[x][y].black == 1) {
	    mark_gc  = gw->goban.black_fg_gc;
	    erase_gc = gw->goban.black_bg_gc;
	  }
	  else {
	    mark_gc  = gw->goban.white_fg_gc;
	    erase_gc = gw->goban.white_bg_gc;
	  }

	else {
	  mark_gc  = gw->goban.foreground_gc;
	  erase_gc = gw->goban.background_gc;
	}
	
	if (c1 == 0) {
	  radius = 10 * gw->goban.point_size / 15;
	  
	  if (gw->goban.points[x][y].free == 0)
	      XFillArc (display, gw->goban.picture, erase_gc, 
			(int) (X - gw->goban.point_size) / 2 + 2, (int) (Y - gw->goban.point_size) / 2 + 2, 
			(unsigned int) gw->goban.point_size - 4, (unsigned int) gw->goban.point_size - 4, 
			0, 360 * 64);
	  else
	    XFillArc (display, gw->goban.picture, erase_gc,
		      (int) (X - radius) / 2, (int) (Y - radius) / 2, 
		      (unsigned int) radius, (unsigned int) radius, 
		      0, 360 * 64);

	  switch (c2) {
	    
	  case GbMTriangleMark :
	    radius -= 4;

	    segment[0].x1 = (short) X / 2;
	    segment[0].x2 = (short) (X / 2 + (86 * radius + 100) / 200);
	    segment[0].y1 = (short) (Y / 2 - (radius - 1) / 2);
	    segment[0].y2 = (short) (Y / 2 + (radius + 4) / 4);
	    
	    segment[1].x1 = (short) segment[0].x2;
	    segment[1].x2 = (short) (X / 2 - (86 * radius + 100) / 200);
	    segment[1].y1 = (short) segment[0].y2;
	    segment[1].y2 = (short) (Y / 2 + (radius + 4) / 4);
	    
	    segment[2].x1 = (short) segment[1].x2;
	    segment[2].x2 = (short) segment[0].x1;
	    segment[2].y1 = (short) segment[1].y2;
	    segment[2].y2 = (short) segment[0].y1;

	    XDrawSegments (display, gw->goban.picture, mark_gc, segment, 3);
	    break;

	  case GbMSquareMark  :
	    radius = 10 * radius / 14 - 2;
	    
	    XDrawRectangle (display, gw->goban.picture, mark_gc, 
			    (X - radius) / 2, (Y - radius) / 2, 
			    (unsigned int) radius, (unsigned int) radius);
	    break;
    
	  case GbMDCrossMark  :
	    radius = 10 * radius / 14 - 2;

	    segment[0].x1 = (short) (X - radius) / 2;
	    segment[0].x2 = (short) segment[0].x1    + radius;
	    segment[0].y1 = (short) (Y - radius) / 2;
	    segment[0].y2 = (short) segment[0].y1    + radius;

	    segment[1].x1 = (short) (X - radius) / 2 + radius;
	    segment[1].x2 = (short) segment[1].x1    - radius;
	    segment[1].y1 = (short) (Y - radius) / 2;
	    segment[1].y2 = (short) segment[1].y1    + radius;
	    
	    XDrawSegments (display, gw->goban.picture, mark_gc, segment, 2);
	    break;
	    
	  case GbMVCrossMark  :
	    radius -= 4;

	    segment[0].x1 = (short) X / 2;
	    segment[0].x2 = (short) segment[0].x1;
	    segment[0].y1 = (short) (Y - radius) / 2;
	    segment[0].y2 = (short) segment[0].y1 + radius;

	    segment[1].x1 = (short) (X - radius) / 2;
	    segment[1].x2 = (short) segment[1].x1 + radius;
	    segment[1].y1 = (short) Y / 2;
	    segment[1].y2 = (short) segment[1].y1;
	    
	    XDrawSegments (display, gw->goban.picture, mark_gc, segment, 2);
	    break;

	  case GbMDiamondMark :
	    radius -= 4;

	    segment[0].x1 = (short) X / 2;
	    segment[0].x2 = (short) segment[0].x1 + radius / 2;
	    segment[0].y1 = (short) (Y - radius) / 2;
	    segment[0].y2 = (short) segment[0].y1 + radius / 2;

	    segment[1].x1 = (short) segment[0].x2;
	    segment[1].x2 = (short) segment[0].x1;
	    segment[1].y1 = (short) segment[0].y2;
	    segment[1].y2 = (short) segment[1].y1 + radius / 2;
	    
	    segment[2].x1 = (short) segment[1].x2;
	    segment[2].x2 = (short) segment[2].x1 - radius / 2;
	    segment[2].y1 = (short) segment[1].y2;
	    segment[2].y2 = (short) segment[1].y1;
	    
	    segment[3].x1 = (short) segment[2].x2;
	    segment[3].x2 = (short) segment[0].x1;
	    segment[3].y1 = (short) segment[2].y2;
	    segment[3].y2 = (short) segment[0].y1;
	    
	    XDrawSegments (display, gw->goban.picture, mark_gc, segment, 4);
	    break;
	  }
	}
	
	else {
	  s[len++] = c1;
	  if (c2 != 0)
	    s[len++] = c2;
	  
	  XQueryTextExtents (display, XGContextFromGC (mark_gc), s, len, &direction, &ascent, &descent, &extent);
	  
	  radius = extent.width + extent.descent + extent.ascent;
	  
	  if (radius < gw->goban.point_size - 1) {
	    if (gw->goban.points[x][y].free == 0)
	      XFillArc (display, gw->goban.picture, erase_gc, 
			(int) (X - gw->goban.point_size) / 2 + 2, (int) (Y - gw->goban.point_size) / 2 + 2, 
			(unsigned int) gw->goban.point_size - 4, (unsigned int) gw->goban.point_size - 4, 
			0, 360 * 64);
	    else
	      XFillArc (display, gw->goban.picture, erase_gc,
			(int) (X - radius) / 2, (int) (Y - radius) / 2, 
			(unsigned int) radius, (unsigned int) radius, 
			0, 360 * 64);
	    
	    XDrawString (display, gw->goban.picture, mark_gc,
			 (X - extent.width) / 2 - extent.lbearing + 1, 
			 (Y + extent.ascent - extent.descent) / 2 + 1, s, len);
	  }
	}
      
	if (gw->goban.auto_redisplay == TRUE && XtIsRealized (w) == TRUE) {
	  event.x      = (X - gw->goban.point_size) / 2;
	  event.y      = (Y - gw->goban.point_size) / 2;
	  event.width  = gw->goban.point_size;
	  event.height = gw->goban.point_size;
	  
	  Redisplay (w, &event, (Region) NULL);
	}
      }
    }
  }
}

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

/* ARGSUSED */

static void Redisplay (w, event, region)
Widget        w;
XExposeEvent *event;
Region        region;
{
  GobanWidget gw     = (GobanWidget) w;
  Position    x      = event->x;
  Position    y      = event->y;
  Dimension   width  = event->width;
  Dimension   height = event->height;

  XCopyArea (XtDisplay (w), gw->goban.picture, XtWindow (w), gw->goban.copy_gc, x, y, width, height, x, y);
}

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

/* ARGSUSED */

static void CvtStringToCursor (args, num_args, fromVal, toVal)
XrmValuePtr   args;		/* unused */
Cardinal     *num_args;	/* unused */
XrmValuePtr   fromVal;
XrmValuePtr   toVal;
{
  static int       cursor;
  static XrmQuark  QWhiteStone;
  static XrmQuark  QBlackStone;
  static XrmQuark  QGrayStone;
  XrmQuark         quark;
  char             lowerName[256];
  static Boolean   inited = FALSE;
    
  if (inited == FALSE) {
    QWhiteStone  = XrmStringToQuark (XtEgbWhiteStone);
    QBlackStone  = XrmStringToQuark (XtEgbBlackStone);
    QGrayStone   = XrmStringToQuark (XtEgbGrayStone);
    inited       = TRUE;
  }

  XmuCopyISOLatin1Lowered  (lowerName, (String) fromVal->addr);
  quark = XrmStringToQuark (lowerName);

  if (quark == QWhiteStone)
    cursor = GbCWhiteStone;
  else if (quark == QBlackStone)       
    cursor = GbCBlackStone;
  else if (quark == QGrayStone)         
    cursor = GbCGrayStone;
  else {
    toVal->size = 0;
    toVal->addr = NULL;
    return;
  }

  toVal->size = sizeof (cursor);
  toVal->addr = (caddr_t) &cursor;
}

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

static void ClassInitialize()
{
  XtAddConverter (XtRString, XtRInt, (XtConverter) CvtStringToCursor, NULL, 0);
}

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

static void InitializePixmap (w)
Widget w;
{
  GobanWidget  gw      = (GobanWidget) w;
  Display     *display = XtDisplay (w);
  Drawable     window  = RootWindowOfScreen (XtScreen (w));
  Dimension    width   = w->core.width;
  Dimension    height  = w->core.height;

  gw->goban.board   = XCreatePixmap (display, window, width, height, (unsigned int) DefaultDepthOfScreen (XtScreen (w)));
  gw->goban.picture = XCreatePixmap (display, window, width, height, (unsigned int) DefaultDepthOfScreen (XtScreen (w)));
}

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

static void InitializeGC (w)
Widget w;
{
  GobanWidget  gw      = (GobanWidget) w;
  Display     *display = XtDisplay (w);
  XtGCMask     mask    = GCForeground | GCBackground | GCFont;
  XGCValues    values;

  values.font             = gw->goban.font->fid;
  values.background       = w->core.background_pixel;

  values.foreground       = gw->goban.white_fg;
  gw->goban.white_fg_gc   = XCreateGC  (display, gw->goban.picture, mask, &values);
  values.foreground       = gw->goban.white_bg;
  gw->goban.white_bg_gc   = XCreateGC  (display, gw->goban.picture, mask, &values);
  values.foreground       = gw->goban.white_bd;
  gw->goban.white_bd_gc   = XCreateGC  (display, gw->goban.picture, mask, &values);

  values.foreground       = gw->goban.black_fg;
  gw->goban.black_fg_gc   = XCreateGC  (display, gw->goban.picture, mask, &values);
  values.foreground       = gw->goban.black_bg;
  gw->goban.black_bg_gc   = XCreateGC  (display, gw->goban.picture, mask, &values);
  values.foreground       = gw->goban.black_bd;
  gw->goban.black_bd_gc   = XCreateGC  (display, gw->goban.picture, mask, &values);

  values.foreground       = gw->goban.foreground;
  gw->goban.foreground_gc = XCreateGC  (display, gw->goban.picture, mask, &values);

  if (gw->core.background_pixmap != XtUnspecifiedPixmap) {
    values.tile           = gw->core.background_pixmap;
    values.fill_style     = FillTiled;
    mask                 |= GCTile | GCFillStyle;
  }

  values.foreground       = w->core.background_pixel;
  values.background       = gw->goban.foreground;
  gw->goban.background_gc = XCreateGC  (display, gw->goban.picture, mask, &values);

  gw->goban.copy_gc       = XCreateGC  (display, gw->goban.picture, (XtGCMask) 0, (XGCValues *) NULL);
}

/****************************************************************************************************************
 */ 
static void InitializeCursor (w)
Widget w;
{
  GobanWidget  gw      = (GobanWidget) w;
  Display     *display = XtDisplay (w);
  Drawable     window  = RootWindowOfScreen (XtScreen (w));
  Pixel        black   = BlackPixel (display, DefaultScreen (display));
  Pixel        white   = WhitePixel (display, DefaultScreen (display));

  static XColor black_color = { 0,    0,     0,     0  };  /* black */
  static XColor white_color = { 0, 65535, 65535, 65535 };  /* white */

  /* Some teminals invert the mask with BlackPixel and WhitePixel     */
  /* should use black and white instead of 1 and 0 but depth is 1 ... */
  /* will probably have trouble with some special colormaps           */

  gw->goban.white_stone = XCreatePixmapFromBitmapData
    (display, window, whitestone_bits, whitestone_width, whitestone_height, 1, 0, 1);
  gw->goban.black_stone = XCreatePixmapFromBitmapData
    (display, window, blackstone_bits, blackstone_width, blackstone_height, 1, 0, 1);
  gw->goban.gray_stone  = XCreatePixmapFromBitmapData
    (display, window, graystone_bits , graystone_width , graystone_height , 1, 0, 1);
  gw->goban.mouse_mask  = XCreatePixmapFromBitmapData
    (display, window, stonemask_bits , stonemask_width , stonemask_height , 1, 0, 1);

  gw->goban.white_cursor = 
    XCreatePixmapCursor (display, gw->goban.white_stone, gw->goban.mouse_mask, 
			 &black_color, &white_color, whitestone_x_hot, whitestone_y_hot);
  gw->goban.black_cursor = 
    XCreatePixmapCursor (display, gw->goban.black_stone, gw->goban.mouse_mask, 
			 &black_color, &white_color, blackstone_x_hot, blackstone_y_hot);
  gw->goban.gray_cursor  = 
    XCreatePixmapCursor (display, gw->goban.gray_stone, gw->goban.mouse_mask,  
			 &black_color, &white_color, graystone_x_hot , graystone_y_hot );

  gw->goban.font_cursor  = XCreateFontCursor (display, XC_question_arrow);
}
  
/****************************************************************************************************************
 */

static void DestroyPixmap (w)
Widget w;
{
  GobanWidget  gw      = (GobanWidget) w;
  Display     *display = XtDisplay (w);

  XFreePixmap (display, gw->goban.picture);
  XFreePixmap (display, gw->goban.board  );
}

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

static void DestroyGC (w)
Widget w;
{
  GobanWidget  gw      = (GobanWidget) w;
  Display     *display = XtDisplay (w);

  XFreeGC (display, gw->goban.black_fg_gc  );
  XFreeGC (display, gw->goban.black_bg_gc  );
  XFreeGC (display, gw->goban.black_bd_gc  );
  XFreeGC (display, gw->goban.white_fg_gc  );
  XFreeGC (display, gw->goban.white_bg_gc  );
  XFreeGC (display, gw->goban.white_bd_gc  );
  XFreeGC (display, gw->goban.foreground_gc);
  XFreeGC (display, gw->goban.background_gc);
  XFreeGC (display, gw->goban.copy_gc      );
}

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

static void Destroy (w)
Widget w;
{
  GobanWidget  gw      = (GobanWidget) w;
  Display     *display = XtDisplay (w);

  DestroyPixmap (w);
  DestroyGC     (w);

  XFreePixmap (display, gw->goban.white_stone  );
  XFreePixmap (display, gw->goban.black_stone  );
  XFreePixmap (display, gw->goban.gray_stone   );
  XFreePixmap (display, gw->goban.mouse_mask   );

  XFreeCursor (display, gw->goban.white_cursor );
  XFreeCursor (display, gw->goban.black_cursor );
  XFreeCursor (display, gw->goban.gray_cursor  );
  XFreeCursor (display, gw->goban.font_cursor  );
}

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

static void Realize (w, valuemaskp, attr)
Widget                w;
XtValueMask          *valuemaskp;
XSetWindowAttributes *attr;
{
  GobanWidget  gw      = (GobanWidget) w;
  
  *valuemaskp |= CWCursor;

  switch (gw->goban.cursor) {
  case GbCGrayStone  : attr->cursor = gw->goban.gray_cursor ; break;
  case GbCBlackStone : attr->cursor = gw->goban.black_cursor; break;
  case GbCWhiteStone : attr->cursor = gw->goban.white_cursor; break;
  default            : attr->cursor = gw->goban.font_cursor ; break;
  }
  
  XtCreateWindow (w, InputOutput, (Visual *) CopyFromParent, *valuemaskp, attr);
}

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

static char *stars[18] = {
  "",				/*  2  */  
  "",				/*  3  */  
  "",				/*  4  */  
  "",				/*  5  */  
  "",				/*  6  */  
  "",				/*  7  */  
  "cccffcff",			/*  8  */    
  "cccgeegcgg",			/*  9  */  
  "ccchhchh",			/* 10  */  
  "cccfcifcfffiicifii",		/* 11  */  
  "dddiidii",			/* 12  */  
  "dddjggjdjj",			/* 13  */  
  "dddkkdkk",			/* 14  */  
  "dddhdlhdhhhlldlhll",		/* 15  */  
  "dddmmdmm",			/* 16  */  
  "dddidnidiiinndninn",		/* 17  */  
  "dddoodoo",			/* 18  */  
  "dddjdpjdjjjppdpjpp",		/* 19  */  
};

static void DrawBoard (w)
Widget w;
{
  GobanWidget  gw         = (GobanWidget) w;
  Display     *display    = XtDisplay (w);
  XSegment     segment[44];
  int          segc;
  Position     game_size  = (Position) gw->goban.game_size;
  Position     point_size = (Position) gw->goban.point_size;
  Position     star_size  = 4;
  Position     xbase      = gw->goban.x_offset + point_size / 2;
  Position     ybase      = gw->goban.y_offset - point_size / 2;
  Position     xmin       = gw->goban.x_offset + point_size * gw->goban.left + 1;
  Position     ymin       = gw->goban.y_offset - point_size * gw->goban.top  + 1;
  Position     xmax       = gw->goban.x_offset + point_size * (gw->goban.right  + 1) - 1;
  Position     ymax       = gw->goban.y_offset - point_size * (gw->goban.bottom - 1) - 1;
  char        *star;
  int          x;
  int          y;

  XFillRectangle (display, gw->goban.board, gw->goban.background_gc, 0, 0, w->core.width, w->core.height);

  if (point_size > 0) {
    segc = 0;
    for (y = gw->goban.bottom - 1; y < gw->goban.top; y++, segc++) {
      segment[segc].x1 = (short) Max ((int) xbase + point_size , xmin);
      segment[segc].x2 = (short) Min ((int) xbase + point_size * game_size, xmax);
      segment[segc].y1 = (short) (ybase - point_size * y);
      segment[segc].y2 = (short) (ybase - point_size * y);
    }
    
    for (x = gw->goban.left; x <= gw->goban.right; x++, segc++) {
      segment[segc].x1 = (short) (xbase + point_size * x);
      segment[segc].x2 = (short) (xbase + point_size * x);
      segment[segc].y1 = (short) Max (ybase - point_size * (game_size - 1), ymin);
      segment[segc].y2 = (short) Min (ybase, ymax);
    }
    
    if (gw->goban.top == gw->goban.game_size) {
      segment[segc].x1 = (short) Max (xbase + point_size - 1, xmin);
      segment[segc].x2 = (short) Min (xbase + point_size * game_size + 1, xmax);
      segment[segc].y1 = (short) (ybase - point_size * (game_size - 1) - 1);
      segment[segc].y2 = (short) (ybase - point_size * (game_size - 1) - 1);
      segc++;
    }
    
    if (gw->goban.bottom == 1) {
      segment[segc].x1 = (short) Max (xbase + point_size - 1, xmin);
      segment[segc].x2 = (short) Min (xbase + point_size * game_size + 1, xmax);
      segment[segc].y1 = (short) (ybase + 1);
      segment[segc].y2 = (short) (ybase + 1);
      segc++;
    }
    
    if (gw->goban.left == 1) {
      segment[segc].x1 = (short) (xbase + point_size - 1);
      segment[segc].x2 = (short) (xbase + point_size - 1);
      segment[segc].y1 = (short) Max (ybase - point_size * (game_size - 1) - 1, ymin); 
      segment[segc].y2 = (short) Min (ybase + 1, ymax);
      segc++;
    }
    
    if (gw->goban.right == gw->goban.game_size) {
      segment[segc].x1 = (short) (xbase + point_size * game_size + 1);
      segment[segc].x2 = (short) (xbase + point_size * game_size + 1);
      segment[segc].y1 = (short) Max (ybase - point_size * (game_size - 1) - 1, ymin);
      segment[segc].y2 = (short) Min (ybase + 1, ymax);
      segc++;
    }
    
    XDrawSegments (display, gw->goban.board, gw->goban.foreground_gc, segment, segc);
    
    if (point_size > 8) {
      star_size = (point_size > 10) ? 4 : 2;
      star      = stars[game_size - 2];
      
      while (*star != '\0') {
	x = *star++ - 'a' + 1;
	y = *star++ - 'a' + 1;
	
	if (x >= gw->goban.left && x <= gw->goban.right && y >= gw->goban.bottom && y <= gw->goban.top) {
	  x = gw->goban.x_offset + (point_size * (2 * x + 1)) / 2;
	  y = gw->goban.y_offset - (point_size * (2 * y - 1)) / 2;
	  
	  XDrawArc (display, gw->goban.board, gw->goban.foreground_gc, 
		    (int) x - star_size / 2, (int) y - star_size / 2, 
		    (unsigned int) star_size, (unsigned int) star_size, 
		    0, 64 * 360);
	  XFillArc (display, gw->goban.board, gw->goban.foreground_gc, 
		    (int) x - star_size / 2, (int) y - star_size / 2, 
		    (unsigned int) star_size, (unsigned int) star_size, 
		    0, 64 * 360);
	}
      }
    }
  }

  XCopyArea (display, gw->goban.board, gw->goban.picture, gw->goban.copy_gc, 0, 0, 
	     w->core.width, w->core.height, 0, 0);
}

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

static void DrawCoordinates (w)
Widget w;
{
  GobanWidget  gw         = (GobanWidget) w;
  Display     *display    = XtDisplay (w);
  XCharStruct  extent;
  int          point_size = gw->goban.point_size;
  char         s[2];
  int          len;
  int          ascent;
  int          descent;
  int          direction;
  int          xbase;
  int          ybase;
  int          x;
  int          y;
  unsigned int width;
  unsigned int height;

  s[0] = s[1] = '8';
  XQueryTextExtents (display, XGContextFromGC (gw->goban.foreground_gc),
		     s, 2, &direction, &ascent, &descent, &extent);

  if ((point_size > extent.width + 2) && (point_size > extent.ascent + extent.descent + 2)) {
    xbase = 2 * gw->goban.x_offset + point_size;
    ybase = 2 * gw->goban.y_offset - point_size;

    if (gw->goban.display_coordinates == TRUE) {
    
      for (x = gw->goban.left; x <= gw->goban.right; x++) {
	s[0] = 'A' - 1 + ((x > 8) ? (x + 1) : x);
	XQueryTextExtents (display, XGContextFromGC (gw->goban.foreground_gc),
			   s, 1, &direction, &ascent, &descent, &extent);

	XDrawString (display, gw->goban.board, gw->goban.foreground_gc, 
		     (xbase - extent.width) / 2 + point_size * x - extent.lbearing + 1, 
		     (ybase + extent.ascent - extent.descent) / 2 - point_size * gw->goban.top + 1, s, 1);
	XDrawString (display, gw->goban.board, gw->goban.foreground_gc, 
		     (xbase - extent.width) / 2 + point_size * x - extent.lbearing + 1, 
		     (ybase + extent.ascent - extent.descent) / 2 - point_size * (gw->goban.bottom - 2) + 1, s, 1);
      }
    }

    x      = gw->goban.x_offset + point_size * gw->goban.left;
    y      = gw->goban.y_offset - point_size * (gw->goban.top + 1);
    width  = point_size * (gw->goban.right - gw->goban.left + 1);
    height = point_size;
    
    if (gw->goban.display_coordinates == FALSE)
      XFillRectangle (display, gw->goban.board, gw->goban.background_gc, x, y, width, height);
    XCopyArea (display, gw->goban.board, gw->goban.picture, gw->goban.copy_gc, x, y, width, height, x, y);

    y      = gw->goban.y_offset - point_size * (gw->goban.bottom - 1);

    if (gw->goban.display_coordinates == FALSE)
      XFillRectangle (display, gw->goban.board, gw->goban.background_gc, x, y, width, height);
    XCopyArea (display, gw->goban.board, gw->goban.picture, gw->goban.copy_gc, x, y, width, height, x, y);
    
    ybase += 2 * point_size;

    for (y = gw->goban.bottom; y <= gw->goban.top; y++) {
      if (gw->goban.display_coordinates == TRUE) {
	len = 0;
	if (y > 9)
	  s[len++] = '1';
	s[len++] = '0' + y % 10;
	XQueryTextExtents (display, XGContextFromGC (gw->goban.foreground_gc),
			   s, len, &direction, &ascent, &descent, &extent);

	XDrawString (display, gw->goban.board, gw->goban.foreground_gc, 
		     (xbase - extent.width) / 2 + point_size * (gw->goban.left  - 1) - extent.lbearing + 1, 
		     (ybase + extent.ascent - extent.descent) / 2 - point_size * y + 1, s, len);
	XDrawString (display, gw->goban.board, gw->goban.foreground_gc, 
		     (xbase - extent.width) / 2 + point_size * (gw->goban.right + 1) - extent.lbearing + 1, 
		     (ybase + extent.ascent - extent.descent) / 2 - point_size * y + 1, s, len);
      }
    }

    x      = gw->goban.x_offset + point_size * (gw->goban.left  - 1);
    y      = gw->goban.y_offset - point_size * gw->goban.top;
    width  = point_size;
    height = point_size * (gw->goban.top - gw->goban.bottom + 1);;
    
    if (gw->goban.display_coordinates == FALSE)
      XFillRectangle (display, gw->goban.board, gw->goban.background_gc, x, y, width, height);
    XCopyArea (display, gw->goban.board, gw->goban.picture, gw->goban.copy_gc, x, y, width, height, x, y);

    x      = gw->goban.x_offset + point_size * (gw->goban.right + 1);
    
    if (gw->goban.display_coordinates == FALSE)
      XFillRectangle (display, gw->goban.board, gw->goban.background_gc, x, y, width, height);
    XCopyArea (display, gw->goban.board, gw->goban.picture, gw->goban.copy_gc, x, y, width, height, x, y);
  }

}

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

static void InitializeSize (w)
Widget w;
{  
  GobanWidget  gw        = (GobanWidget) w;
  Position     bottom    = Min (gw->goban.bottom, gw->goban.top  );
  Position     left      = Min (gw->goban.left  , gw->goban.right);
  Position     top       = Max (gw->goban.bottom, gw->goban.top  );
  Position     right     = Max (gw->goban.left  , gw->goban.right);
  Dimension    game_size = gw->goban.game_size;
  Dimension    width;
  Dimension    height;

  game_size = (game_size > 1      && game_size <  20       ) ? game_size : 19;
  bottom    = (bottom    > 0      && bottom    <  game_size) ? bottom    : 1;
  left      = (left      > 0      && left      <  game_size) ? left      : 1;
  top       = (top       > bottom && top       <= game_size) ? top       : game_size;
  right     = (right     > left   && right     <= game_size) ? right     : game_size;

  width     = right - left   + 3;
  height    = top   - bottom + 3;

  if ((w->core.height == 0) && (w->core.width == 0)) {
    w->core.width  = gw->goban.point_size * width ;
    w->core.height = gw->goban.point_size * height;
  } 
 
  else 
    gw->goban.point_size = Min (w->core.width / width, w->core.height / height);

  gw->goban.game_size = game_size;
  gw->goban.bottom    = bottom;
  gw->goban.left      = left;
  gw->goban.top       = top;
  gw->goban.right     = right;
  gw->goban.x_offset  = (int) (w->core.width  - gw->goban.point_size * (width  + 2 * left - 2)) / 2;
  gw->goban.y_offset  = (int) (w->core.height - gw->goban.point_size * (height - 2 * top  - 2)) / 2;
}

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

/* ARGSUSED */

static void Initialize (request, new)
Widget request;
Widget new;
{
  InitializeSize   (new);
  InitializePixmap (new);
  InitializeGC     (new);
  InitializeCursor (new);
  DrawBoard        (new);
  DrawCoordinates  (new);
  GbClearBoard     (new);
}

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

static void DrawPoint (w, x, y, color)
Widget      w;
int         x;
int         y;
GbPointState color;
{
  GobanWidget   gw      = (GobanWidget) w;
  Display      *display = XtDisplay (w);
  unsigned int  size    = (unsigned int) gw->goban.point_size - 2;
  XExposeEvent  event;
  int           X;
  int           Y;

  if (size > 1) {
    X = gw->goban.x_offset + gw->goban.point_size * x + 1;
    Y = gw->goban.y_offset - gw->goban.point_size * y + 1;
    
    switch (color)
      {
      case GbBlackStone :
	XFillArc (display, gw->goban.picture, gw->goban.black_bg_gc, X, Y, size, size, 0, 360 * 64);
	XDrawArc (display, gw->goban.picture, gw->goban.black_bd_gc, X, Y, size, size, 0, 360 * 64);
	XDrawArc (display, gw->goban.picture, gw->goban.black_fg_gc, X + (int) size / 6, Y + (int) size / 6, 
		  (size * 2) / 3, (size * 2) / 3, -15 * 64, -60 * 64);
	break;
      case GbWhiteStone :
	XFillArc (display, gw->goban.picture, gw->goban.white_bg_gc, X, Y, size, size, 0, 360 * 64);
	XDrawArc (display, gw->goban.picture, gw->goban.white_bd_gc, X, Y, size, size, 0, 360 * 64);
	XDrawArc (display, gw->goban.picture, gw->goban.white_fg_gc, X + (int) size / 6, Y + (int) size / 6, 
		  (size * 2) / 3, (size * 2) / 3, -15 * 64, -60 * 64);
	break;
      case GbEmptyPoint :
	XCopyArea (XtDisplay (w), gw->goban.board, gw->goban.picture, gw->goban.copy_gc,
		   X, Y, gw->goban.point_size, gw->goban.point_size, X, Y); 
	break;
      }
    
    if (gw->goban.auto_redisplay == TRUE && XtIsRealized (w) == TRUE) {
      event.x      = X - 1;
      event.y      = Y - 1;
      event.width  = gw->goban.point_size;
      event.height = gw->goban.point_size;

      Redisplay (w, &event, (Region) NULL);
    }
  }
}

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

static void RedrawStones (w)
Widget w;
{
  GobanWidget  gw         = (GobanWidget) w;
  Boolean      redisplay  = gw->goban.auto_redisplay;
  Position     x;
  Position     y;

  gw->goban.auto_redisplay = FALSE;

  for (x = gw->goban.left; x <= gw->goban.right; x++)
    for (y = gw->goban.bottom; y <= gw->goban.top; y++) {
      if (gw->goban.points[x][y].free == 0) {
	if (gw->goban.points[x][y].black == 1)
	  DrawPoint (w, x, y, GbBlackStone);
	else
	  DrawPoint (w, x, y, GbWhiteStone);
	if (gw->goban.points[x][y].num > 0)
	  GbSetNum (w, x, y, (int) gw->goban.points[x][y].num);
      }

      if (gw->goban.points[x][y].mark1 != 0 || gw->goban.points[x][y].mark2 != 0)
	GbSetMark (w, x, y, gw->goban.points[x][y].mark1, gw->goban.points[x][y].mark2);
    }

  gw->goban.auto_redisplay = redisplay;

  if (redisplay == TRUE)
    GbRedisplayBoard (w);
}

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

static void Resize (w)
Widget w;
{
  DestroyPixmap    (w);
  DestroyGC        (w);
  InitializeSize   (w);
  InitializePixmap (w);
  InitializeGC     (w);
  DrawBoard        (w);
  DrawCoordinates  (w);
  RedrawStones     (w);
}

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

/* ARGSUSED */

static Boolean SetValues (current, request, new)
Widget current;
Widget request;
Widget new;
{
  GobanWidget gnew      = (GobanWidget) new;
  GobanWidget gcurrent  = (GobanWidget) current;
  Boolean     redisplay = FALSE;

  if (gnew->goban.point_size != gcurrent->goban.point_size)
    gnew->goban.point_size = gcurrent->goban.point_size;

  if (XtIsRealized (new)) {
    if (gnew->goban.cursor != gcurrent->goban.cursor) {

      switch (gnew->goban.cursor) {

      case GbCGrayStone  : 
	XDefineCursor (XtDisplay (new), XtWindow (new), gnew->goban.gray_cursor);
	break;
      case GbCBlackStone : 
	XDefineCursor (XtDisplay (new), XtWindow (new), gnew->goban.black_cursor);
	break;
      case GbCWhiteStone : 
	XDefineCursor (XtDisplay (new), XtWindow (new), gnew->goban.white_cursor);
	break;
      default :
	XFreeCursor (XtDisplay (new), gnew->goban.font_cursor );
	gnew->goban.font_cursor = XCreateFontCursor (XtDisplay (new), (unsigned int) gnew->goban.cursor);
	XDefineCursor (XtDisplay (new), XtWindow (new), gnew->goban.font_cursor);
      }
    }
  }

  if (gnew->goban.game_size != gcurrent->goban.game_size) 
    if (gnew->goban.game_size > 1 && gnew->goban.game_size < 20) {

      gnew->goban.bottom = 1;
      gnew->goban.left   = 1;
      gnew->goban.top    = gnew->goban.game_size;
      gnew->goban.right  = gnew->goban.game_size;
      
      InitializeSize   (new);
      DrawBoard        (new);
      DrawCoordinates  (new);
      GbClearBoard     (new);
      
      redisplay = TRUE;
    }
    else
      gnew->goban.game_size = gcurrent->goban.game_size;

  else if (gnew->goban.top    != gcurrent->goban.top   ||
	   gnew->goban.left   != gcurrent->goban.left  ||
	   gnew->goban.right  != gcurrent->goban.right ||
	   gnew->goban.bottom != gcurrent->goban.bottom) {

    InitializeSize  (new);
    DrawBoard       (new);
    DrawCoordinates (new);
    RedrawStones    (new);

    redisplay = TRUE;
  }

  else if (new->core.background_pixel != current->core.background_pixel || 
           gnew->goban.foreground     != gcurrent->goban.foreground     ||
           gnew->goban.white_fg       != gcurrent->goban.white_fg       ||
           gnew->goban.white_bg       != gcurrent->goban.white_bg       ||
           gnew->goban.white_bd       != gcurrent->goban.white_bd       ||
           gnew->goban.black_fg       != gcurrent->goban.black_fg       ||
           gnew->goban.black_bg       != gcurrent->goban.black_bg       ||
           gnew->goban.black_bd       != gcurrent->goban.black_bd       ) {

    DestroyGC        (new);
    InitializeGC     (new);
    DrawBoard        (new);
    DrawCoordinates  (new);
    RedrawStones     (new);

    redisplay = TRUE;
  }

  if (gnew->goban.display_coordinates != gcurrent->goban.display_coordinates) {
    DrawCoordinates (new);

    redisplay = TRUE;
  }

  return (gnew->goban.auto_redisplay == True) ? redisplay : False;
}

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