/****************************************************************************************************************
 *
 *  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/Intrinsic.h>
#include <X11/Shell.h>
#include <X11/StringDefs.h>

#include <X11/Xaw/SimpleMenu.h>
#include <X11/Xaw/SmeBSB.h>
#include <X11/cursorfont.h>

#include "xgoban.h"

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

/* ARGSUSED */

void NextComment (w, event, params, num_params)
Widget    w;
XEvent   *event;
String   *params;
Cardinal *num_params;
{
  Arg     arg;
  Boolean no_beep = False;

  if (current_node != NULL)
    switch (current_mode) {
    case EditMode :
    case MailMode :
      
      if (current_node->down != NULL) {
	XtSetArg (arg, XtNautoRedisplay, (XtArgVal) True ); XtSetValues (w_goban, &arg, 1);
	
	do {
	  HideTmpProperty (current_node);
	  
	  current_node = current_node->down;
	  
	  Play            (current_node);
	  ShowProperty    (current_node);
	  
	  if (SG_GetProperty (current_node, SG_Comment) != NULL) {
	    no_beep = True;
	    break;
	  }
	  
	} while (current_node->down != NULL && current_node->right == NULL);
	
	XtSetArg (arg, XtNautoRedisplay, (XtArgVal) False); XtSetValues (w_goban, &arg, 1);
      }
      
      if (beep >= -100 && no_beep == False)
	XBell (XtDisplay (w), beep);
      break;
      
    default : break;
    }
}

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

/* ARGSUSED */

void PreviousComment (w, event, params, num_params)
Widget    w;
XEvent   *event;
String   *params;
Cardinal *num_params;
{
  SG_NodePtr tmp;
  Arg        arg;
  Boolean    no_beep = False;

  if (current_node != NULL)
    switch (current_mode) {
    case EditMode :
    case MailMode :
      
      XtSetArg (arg, XtNautoRedisplay, (XtArgVal) True ); XtSetValues (w_goban, &arg, 1);
      
      do {
	tmp = current_node;
	while (tmp->left != NULL) tmp = tmp->left;
	
	if (tmp->up != NULL) {
	  Unplay          (current_node);
	  HideProperty    (current_node);
	  
	  current_node = tmp->up;
	  
	  ShowTmpProperty (current_node);
	}
	
	if (SG_GetProperty (current_node, SG_Comment) != NULL) {
	  no_beep = True;
	  break;
	}
	
      } while (tmp->up != NULL && current_node->left == NULL && current_node->right == NULL); 
      
      XtSetArg (arg, XtNautoRedisplay, (XtArgVal) False); XtSetValues (w_goban, &arg, 1);
      
      if (beep >= -100 && no_beep == False)
	XBell (XtDisplay (w), beep);
    }
}

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

/* ARGSUSED */

void NextVariationBranch (w, event, params, num_params)
Widget    w;
XEvent   *event;
String   *params;
Cardinal *num_params;
{
  Arg arg;

  if (current_node != NULL)
    switch (current_mode) {
    case EditMode :
    case MailMode :
      
      if (current_node->down != NULL) {
	XtSetArg (arg, XtNautoRedisplay, (XtArgVal) True ); XtSetValues (w_goban, &arg, 1);
	
	HideTmpProperty (current_node);
	
	current_node = current_node->down;
	
	Play            (current_node);
	ShowProperty    (current_node);
	
	while (current_node->down != NULL && current_node->right == NULL) {
	  HideTmpProperty  (current_node);
	  
	  current_node = current_node->down;
	  
	  Play             (current_node);
	  ShowProperty     (current_node);
	}
	
	XtSetArg (arg, XtNautoRedisplay, (XtArgVal) False); XtSetValues (w_goban, &arg, 1);
      }
      
      else if (beep >= -100)
	XBell (XtDisplay (w), beep);
    }
}    

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

/* ARGSUSED */

void PreviousVariationBranch (w, event, params, num_params)
Widget    w;
XEvent   *event;
String   *params;
Cardinal *num_params;
{
  SG_NodePtr tmp;
  Boolean    no_beep = False;
  Arg        arg;

  if (current_node != NULL)
    switch (current_mode) {
    case EditMode :
    case MailMode :
      
      XtSetArg (arg, XtNautoRedisplay, (XtArgVal) True ); XtSetValues (w_goban, &arg, 1);
      
      do {
	tmp = current_node;
	while (tmp->left != NULL) tmp = tmp->left;
	
	if (tmp->up != NULL) {
	  Unplay          (current_node);
	  HideProperty    (current_node);
	  
	  current_node = tmp->up;
	  no_beep      = True;
	  
	  ShowTmpProperty (current_node);
	}
      } while (tmp->up != NULL && current_node->left == NULL && current_node->right == NULL); 
      
      XtSetArg (arg, XtNautoRedisplay, (XtArgVal) False); XtSetValues (w_goban, &arg, 1);
      
      if (beep >= -100 && no_beep == False)
	XBell (XtDisplay (w), beep);
    }  
}

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

/* ARGSUSED */

void PreviousMove (w, event, params, num_params)
Widget    w;
XEvent   *event;
String   *params;
Cardinal *num_params;
{
  SG_NodePtr tmp = current_node;

  if (current_node != NULL)
    switch (current_mode) {
    case EditMode :
    case MailMode :
      
      while (tmp->left != NULL)
	tmp = tmp->left;
      
      if (tmp->up != NULL) {
	Unplay           (current_node);
	HideProperty     (current_node);
	
	current_node = tmp->up;
	
	ShowTmpProperty  (current_node);
	RedisplayBoard   ();

	return;
      }

      if (beep >= -100)
	XBell (XtDisplay (w), beep);
    }
}

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

/* ARGSUSED */

void Exit (w, event, params, num_params)
Widget  w;
XEvent *event;
String   *params;
Cardinal *num_params;
{
  HideTmpProperty (current_node);
  ShowTmpProperty (current_node);

  switch (current_mode) {
  case EditMode :
    PopupDialog (w, event, modified == True ? SaveBeforeQuitDialog : ReallyQuitDialog);
    break;
  case PlayMode :
    PopupDialog (w, event, StopGameBeforeQuitDialog);
    break;
  case MailMode :
    PopupDialog (w, event, mail_node->down == NULL ? MailNoMoveDialog : MailConfirmMoveDialog);
    break;
  }
}

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


/* ARGSUSED */

void destroyMenu (w, client_data, call_data)
Widget    w;
XtPointer client_data;
XtPointer call_data;
{
  if (w_menu != NULL) {
    XtDestroyWidget (w_menu);
    w_menu = NULL;
  }
}

/* ARGSUSED */

void menuAction (w, client_data, call_data)
Widget    w;
XtPointer client_data;
XtPointer call_data;
{
  SG_NodePtr tmp  = current_node;
  char       mark = (int) client_data - 'A';

  while (tmp->left != NULL)
    tmp = tmp->left;

  while (mark-- != 0)
    tmp = tmp->right;

	      
  Unplay           (current_node);
  HideProperty     (current_node);
	      
  current_node = tmp;
	      
  Play           (current_node);
  ShowProperty   (current_node);
  RedisplayBoard ();
}

/* ARGSUSED */

void SelectDiagram (w, event, params, num_params)
Widget    w;
XEvent   *event;
String   *params;
Cardinal *num_params;
{
  Widget         entry;
  Arg            args[4];
  int            xtargc;
  SG_NodePtr     tmp   = current_node;
  SG_PropertyPtr property;
  String         nparams[1];
  char           buffer[256];
  char           mark  = 'A';
    
  if (event->type != ButtonPress) {
    XtAppWarning (XtWidgetToApplicationContext (w), 
		  "XGoban - selectDiagram : This action routine requires a button press event.");
    return;
  }

  if (*num_params != 0)
    XtAppWarning (XtWidgetToApplicationContext (w), 
		  "XGoban - selectDiagram : This action routine requires no argument.");

  if (current_node != NULL)
    switch (current_mode) {
    case EditMode :
    case MailMode :

      while (tmp->left != NULL)
	tmp = tmp->left;

      if (tmp->right == NULL) {
	if (beep >= -100)
	  XBell (XtDisplay (w), beep);
	return;
      }
 
      if (w_menu == NULL) {
 
	xtargc = 0;
	XtSetArg (args[xtargc], XtNlabel        , " Variations "); xtargc++;
	
	w_menu = XtCreatePopupShell ("variations", simpleMenuWidgetClass, w_goban, args, xtargc);
	
	entry = XtNameToWidget (w_menu, "menuLabel");
	
	xtargc = 0;
	XtSetArg (args[xtargc], XtNheight        , 32           ); xtargc++;
	XtSetValues (entry, args, xtargc);
	
	while (tmp != NULL) {
	  property = SG_GetProperty (tmp, SG_nodeName);

	  if (property != NULL && property->data.pvalue != NULL)
	    sprintf (buffer, "%c : %s", mark, property->data.pvalue);

	  else
	    switch (tmp->type) {
	    case SG_EventType :
	      sprintf (buffer, "%c : (Event)", mark);
	      break;

	    case SG_DiagramType :
	      sprintf (buffer, "%c : (Diagram)", 
		       mark);
	      break;
	      
	    case SG_MoveType :
	      if (tmp->x != 0 && tmp->y != 0)
		sprintf (buffer, "%c : %s %c%d", 
			 mark,
			 tmp->color == SG_BlackStone ? "Black" : "White",
			 tmp->x + 'A' + (tmp->x < 9 ? -1 : 0),
			 tmp->y);
	      else
		sprintf (buffer, "%c : %s passes", 
			 mark,
			 tmp->color == SG_BlackStone ? "Black" : "White");
	      break;
	    }
	  
	  xtargc = 0;
	  XtSetArg (args[xtargc], XtNleftMargin, 8); xtargc++;
	  
	  entry = XtCreateManagedWidget (buffer, smeBSBObjectClass, w_menu, args, xtargc);
	  
	  XtAddCallback (entry, XtNcallback, menuAction, (XtPointer) mark);
	  
	  if (tmp == current_node) {
	    XtSetArg (args[0], XtNpopupOnEntry, entry);
	    XtSetValues (w_menu, args, 1);
	  }

	  mark++;
	  tmp = tmp->right;
	}
      }

      XtAddCallback (w_menu, XtNpopdownCallback, destroyMenu, (XtPointer) NULL);

      nparams[0] = "variations";

      XtCallActionProc (w_goban, "XawPositionSimpleMenu", event, nparams, 1); 
      XtCallActionProc (w_goban, "MenuPopup"            , event, nparams, 1);

      return;
    }
}

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


/* ARGSUSED */

void SelectVariation (w, event, params, num_params)
Widget    w;
XEvent   *event;
String   *params;
Cardinal *num_params;
{
  char       ch;
  SG_NodePtr tmp    = current_node;

  if (*num_params != 1) {
    XtAppWarning (XtWidgetToApplicationContext (w), 
		  "XGoban - selectVariation : This action routine requires one argument.");
    return;
  }

  ch = params[0][0];

  if (ch < 'a' && ch > 'z' && ch < 'A' && ch > 'Z') {
    XtAppWarning (XtWidgetToApplicationContext (w), 
		  "XGoban - selectVariation : argument should range between 'A' and 'Z'.");
    return;
  }

  if (current_node != NULL) 
    switch (current_mode) {
    case EditMode :
    case MailMode :

      ch  = tolower (ch) - 'a';
      
      while (tmp->left != NULL) 
	tmp = tmp->left;
      
      if (tmp->right != NULL) {
	while (tmp != NULL && ch != 0) {
	  ch--;
	  tmp = tmp->right;
	}

	if (ch == 0 && tmp != NULL) {
	  if (tmp != current_node) {
	    Unplay       (current_node);
	    HideProperty (current_node);
	    
	    current_node = tmp; 
	    
	    Play           (current_node);
	    ShowProperty   (current_node);
	    RedisplayBoard ();
	  }

	  return;
	}
      }
      
      if (beep >= -100)
	XBell (XtDisplay (w), beep);
    }
}

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

/* ARGSUSED */

void Dialog (w, event, params, num_params)
Widget    w;
XEvent   *event;
String   *params;
Cardinal *num_params;
{
  if (*num_params != 1) {
    XtAppWarning (XtWidgetToApplicationContext (w), 
		  "XGoban - dialog : This action routine requires one argument.");
    return;
  }

  switch (current_mode) {
  case EditMode :
    switch (params[0][0]) {
    case 'E' : case 'e' : PopupDialog (w, event, EditDialog    ); break;
    case 'L' : case 'l' : PopupDialog (w, event, modified == True ? SaveBeforeLoadDialog : LoadDialog); break;
    case 'N' : case 'n' : PopupDialog (w, event, NodeNameDialog); break;
    case 'S' : case 's' : PopupDialog (w, event, SaveAsDialog  ); break;
    case 'H' : case 'h' : PopupDialog (w, event, HelpDialog    ); break;
    default :
      XtAppWarning (XtWidgetToApplicationContext (w), 
		    "XGoban - dialog : Argument should be Save, Load, Edit, Help or NodeName.");
    }
    break;

  case PlayMode :
    switch (params[0][0]) {
    case 'E' : case 'e' : PopupDialog (w, event, StopGameDialog); break;
    case 'S' : case 's' : PopupDialog (w, event, SaveAsDialog  ); break;
    case 'H' : case 'h' : PopupDialog (w, event, HelpDialog    ); break;
    case 'N' : case 'n' : 
    case 'L' : case 'l' :
      if (beep >= -100)
	XBell (XtDisplay (w), beep); break;
    default :
      XtAppWarning (XtWidgetToApplicationContext (w), 
		    "XGoban - dialog : Argument should be Save, Load, Edit, Help or NodeName.");
    }
      
  case MailMode :
    switch (params[0][0]) {
    case 'N' : case 'n' : PopupDialog (w, event, NodeNameDialog); break;
    case 'S' : case 's' : PopupDialog (w, event, SaveAsDialog  ); break;
    case 'H' : case 'h' : PopupDialog (w, event, HelpDialog    ); break;
    case 'E' : case 'e' :
    case 'L' : case 'l' :
      if (beep >= -100)
	XBell (XtDisplay (w), beep); break;

    default :
      XtAppWarning (XtWidgetToApplicationContext (w), 
		    "XGoban - dialog : Argument should be Save, Load, Edit, Help or NodeName.");
    }
    break;
  }
}

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

/* ARGSUSED */

void NextMove (w, event, params, num_params)
Widget    w;
XEvent   *event;
String   *params;
Cardinal *num_params;
{
  Position   x;
  Position   y;
  SG_NodePtr tmp    = current_node;

  if (current_node != NULL)
    switch (current_mode) {
    case EditMode :
    case MailMode :
      
      if (event->type == ButtonPress) {
	
	if (current_node->type == SG_MoveType) {
	  while (tmp->left != NULL)
	    tmp = tmp->left;
	  
	  if (tmp->right != NULL) {
	    
	    x = (Position) ((XButtonPressedEvent *) event)->x;
	    y = (Position) ((XButtonPressedEvent *) event)->y;
	    
	    if (GbGetStoneFromPosition (w_goban, &x, &y) == TRUE) {

	      while (tmp != NULL) {
		if (tmp->type == SG_MoveType && tmp->color == current_node->color &&
		    (Position) tmp->x == x && (Position) tmp->y == y) {
		    
		  Unplay           (current_node);
		  HideProperty     (current_node);
		    
		  current_node = tmp; 
		    
		  Play           (current_node);
		  ShowProperty   (current_node);
		  RedisplayBoard ();
		  
		  return;
		}

		tmp = tmp->right;
	      }
	    }
	  }
	}
      }
      
      if (current_node->down != NULL) {
	HideTmpProperty (current_node);
	
	current_node = current_node->down;
	
	Play           (current_node);
	ShowProperty   (current_node);
	RedisplayBoard ();
      }
      
      else if (beep >= -100)
	XBell (XtDisplay (w), beep);
    }
}

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

/* ARGSUSED */

void MakeVariationDiagram (w, event, params, num_params)
Widget    w;
XEvent   *event;
String   *params;
Cardinal *num_params;
{
  SG_NodePtr  node;

  if (*num_params != 0)
    XtAppWarning (XtWidgetToApplicationContext (w), 
		  "XGoban - makeVariationDiagram : This action routine requires no argument.");

  if (current_node != NULL)
    switch (current_mode) {
    case EditMode :
    case MailMode :
      if (edit == False) { PopupDialog (w, event, EditDialog); return; }

      node = SG_MakeNode (current_node, current_node->type == SG_EventType ? SG_Event : SG_VariationDiagram);

      if (node == NULL) {
	PopupDialog (w, event, MemoryErrorDialog);
	return;
      }
     
      node->player = current_node->player;
      modified     = True;
      
      Unplay           (current_node);
      HideProperty     (current_node);
      
      current_node = node;
      current_mode = DiagramMode;

      StartDiagram ();

      Play           (current_node);
      ShowProperty   (current_node);
      RedisplayBoard ();
    }
}

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

/* ARGSUSED */

void Add (w, event, params, num_params)
Widget    w;
XEvent   *event;
String   *params;
Cardinal *num_params;
{
  Position      x;
  Position      y;

  if (event->type != ButtonPress) {
    XtAppWarning (XtWidgetToApplicationContext (w), 
		  "XGoban - add : This action routine requires a ButtonPress event.");
    return;
  }

  if (*num_params != 1) {
    XtAppWarning (XtWidgetToApplicationContext (w), 
		  "XGoban - add : This action routine requires one argument.");
    return;
  }

  if (current_node != NULL)
    switch (current_mode) {
    case DiagramMode :
      
      x = (Position) ((XButtonPressedEvent *) event)->x;
      y = (Position) ((XButtonPressedEvent *) event)->y;
      
      if (GbGetStoneFromPosition (w_goban, &x, &y) == TRUE ) {
	
	switch (params[0][0]) {
	case 'B' : case 'b' : AddStone (x, y, SG_BlackStone); GbSetPoint (w_goban, x, y, GbBlackStone); break;
	case 'W' : case 'w' : AddStone (x, y, SG_WhiteStone); GbSetPoint (w_goban, x, y, GbWhiteStone); break;
	case 'E' : case 'e' : AddStone (x, y, SG_EmptyPoint); GbSetPoint (w_goban, x, y, GbEmptyPoint); break;
	default :
	  XtAppWarning (XtWidgetToApplicationContext (w), 
			"XGoban - add : Argument should be Black, White or Empty.");
	  return;
	}
	
	modified = True;
	RedisplayBoard ();
      }
      
      else if (beep >= -100)
	XBell (XtDisplay (w), beep);
    }
}

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

/* ARGSUSED */

void SetPlayer (w, event, params, num_params)
Widget    w;
XEvent   *event;
String   *params;
Cardinal *num_params;
{
  int player;

  if (*num_params != 1) {
    XtAppWarning (XtWidgetToApplicationContext (w), 
		  "XGoban - player : This action routine requires one argument.");
    return;
  }

  if (current_node != NULL)
    switch (current_mode) {
    case EditMode :
    case MailMode :
      if (edit == False) { PopupDialog (w, event, EditDialog); return; }

      switch (params[0][0]) {
      case 'B' : case 'b' : player = SG_BlackStone; break;
      case 'W' : case 'w' : player = SG_WhiteStone; break;
      default  :
	XtAppWarning (XtWidgetToApplicationContext (w), 
		      "XGoban - player : Argument should be Black or White.");
	return;
      }
      
      if (strict == True) {
	if (current_node->type == SG_MoveType) {
	  PopupDialog (w, event, PlayerNodeErrorWarning);
	  return;
	}
	
	else if (current_node->down != NULL) {
	  PopupDialog (w, event, PlayerMoveErrorWarning);
	  return;
	}
      }
      
      HideProperty     (current_node);
      
      current_node->player = player;
      modified             = True;
      
      ShowProperty   (current_node);
      RedisplayBoard ();
    }
}

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

/* ARGSUSED */

void Letter (w, event, params, num_params)
Widget    w;
XEvent   *event;
String   *params;
Cardinal *num_params;
{
  unsigned char  letter[3];
  Position       x;
  Position       y;
  
  if (event->type != ButtonPress) {
    XtAppWarning (XtWidgetToApplicationContext (w), 
		  "XGoban - letter : This action routine requires a ButtonPress event.");
    return;
  }

  if (current_node != NULL)
    switch (current_mode) {
    case EditMode :
    case MailMode :
      if (edit == False) { PopupDialog (w, event, EditDialog); return; }

      x = (Position) ((XButtonPressedEvent *) event)->x;
      y = (Position) ((XButtonPressedEvent *) event)->y;

      if (GbGetStoneFromPosition (w_goban, &x, &y) == TRUE) {
	
	letter[0] = x;
	letter[1] = y;
	letter[2] = SG_EOS;
	
	HideTmpProperty  (current_node);
	SG_MakeProperty  (current_node, SG_Letters, letter);
	ShowTmpProperty  (current_node);
	RedisplayBoard   ();
	
	modified = True;
      }
      
      else if (beep >= -100)
	XBell (XtDisplay (w), beep);
    }
}

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

/* ARGSUSED */

void Mark (w, event, params, num_params)
Widget    w;
XEvent   *event;
String   *params;
Cardinal *num_params;
{
  unsigned char  mark[3];
  Position       x;
  Position       y;
  
  if (event->type != ButtonPress) {
    XtAppWarning (XtWidgetToApplicationContext (w), 
		  "XGoban - mark : This action routine requires a ButtonPress event.");
    return;
  }

  if (current_node != NULL)
    switch (current_mode) {
    case EditMode :
    case MailMode :
      if (edit == False) { PopupDialog (w, event, EditDialog); return; }
      
      x = (Position) ((XButtonPressedEvent *) event)->x;
      y = (Position) ((XButtonPressedEvent *) event)->y;
      
      if (GbGetStoneFromPosition (w_goban, &x, &y) == TRUE) {
	
	mark[0] = x;
	mark[1] = y;
	mark[2] = SG_EOS;
	
	HideTmpProperty  (current_node);
	SG_MakeProperty  (current_node, SG_Marked, mark);
	ShowTmpProperty  (current_node);
	RedisplayBoard   ();
	
	modified = True;
      }
      
      else if (beep >= -100)
	XBell (XtDisplay (w), beep);
    }
}
 
/*******************************************************************************************************
 */

/* ARGSUSED */

void DeleteMark (w, event, params, num_params)
Widget    w;
XEvent   *event;
String   *params;
Cardinal *num_params;
{
  SG_PropertyPtr  property;
  unsigned char  *ptr;
  Position        x;
  Position        y;
  
  if (event->type != ButtonPress) {
    XtAppWarning (XtWidgetToApplicationContext (w), 
		  "XGoban - deleteMark : This action routine requires a ButtonPress event.");
    return;
  }

  if (current_node != NULL)
    switch (current_mode) {
    case EditMode :
    case MailMode :
      if (edit == False) { PopupDialog (w, event, EditDialog); return; }
      
      x = (Position) ((XButtonPressedEvent *) event)->x;
      y = (Position) ((XButtonPressedEvent *) event)->y;
      
      if (GbGetStoneFromPosition (w_goban, &x, &y) == TRUE) {
	
	HideTmpProperty (current_node);
	
	property = SG_GetProperty (current_node, SG_Letters);
	
	if (property != NULL && (ptr = property->data.pvalue) != NULL) {
	  
	  while (*ptr != SG_EOS) {
	    if ((*ptr & 0x3f) == x && (*(ptr + 1) & 0x3f) == y) {
	      *ptr     = *(ptr + 1) = 0;
	      modified = True;
	    }
	    ptr += 2;
	  }
	}
	
	property = SG_GetProperty (current_node, SG_Marked);
	
	if (property != NULL && (ptr = property->data.pvalue) != NULL) {
	  
	  while (*ptr != SG_EOS) {
	    if ((*ptr & 0x3f) == x && (*(ptr + 1) & 0x3f) == y) {
	      *ptr     = *(ptr + 1) = 0;
	      modified = True;
	    }
	    ptr += 2;
	  }
	}
	
	ShowTmpProperty  (current_node);
	RedisplayBoard   ();
      }
      
      else if (beep >= -100)
	XBell (XtDisplay (w), beep);
    }
}

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

Boolean delete_confirm = False;

/* ARGSUSED */

void DeleteMove (w, event, params, num_params)
Widget    w;
XEvent   *event;
String   *params;
Cardinal *num_params;
{
  SG_NodePtr node   = current_node;
  Boolean    replay = False;

  if (current_node != NULL)
    switch (current_mode) {
    case EditMode :
    case MailMode :
      if (edit           == False) { PopupDialog (w, event, EditDialog        ); return; }
      if (delete_confirm == False) { PopupDialog (w, event, ReallyDeleteDialog); return; }
      
      delete_confirm = False;
      
      Unplay        (node);
      HideProperty  (node);
      
      if (node->left != NULL)
	{ current_node = node->left ; replay = True; }
      else if (node->right != NULL)
	{ current_node = node->right; replay = True; }
      else if (node->up != NULL) 
	current_node = node->up;
      else
	tree = current_node = NULL;
      
      SG_RemoveNode (node);
      SG_FreeNode   (node);
      
      if (tree == NULL)
	{ current_node = tree = SG_MakeNode ((SG_NodePtr) NULL, SG_Event); replay = True; }
      
      if (replay == True) {
	Play             (current_node);
	ShowProperty     (current_node);
      }
      
      else 
	ShowTmpProperty (current_node);
      
      RedisplayBoard ();
      
      modified = True;

      if (current_mode == MailMode) {
	Edit (False);
	edit = True;
      }
    }
}

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

/* ARGSUSED */

void Pass (w, event, params, num_params)
Widget    w;
XEvent   *event;
String   *params;
Cardinal *num_params;
{
  SG_NodePtr node;

  if (current_node != NULL) {
    switch (current_mode) {
    case EditMode :
    case MailMode :
      if (edit == False) { PopupDialog (w, event, EditDialog); return; }

      if (current_node->down == NULL) 
	goto play_move;
      else
	PopupDialog (w, event, ReplaceNodeDialog);
      break;

    case PlayMode :
      if ((current_node->player == SG_BlackStone && black_player == Human) ||
	  (current_node->player == SG_WhiteStone && white_player == Human))
    	goto play_move;
      break;
    }
    return;

   play_move :
    HideTmpProperty (current_node);
    
    node         = SG_MakeNode (current_node, SG_Move);
    node->x      = 0;
    node->y      = 0;
    node->color  = current_node->player;
    
    Play (node);
    
    modified     = True;
    current_node = node;

    ShowProperty   (current_node);
    RedisplayBoard ();

    switch (current_mode) {
    case PlayMode : signalMove (node); break;
    case MailMode : Edit (True); break;
    }
  }
}

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

/* ARGSUSED */

void MakeMove (w, event, params, num_params)
Widget    w;
XEvent   *event;
String   *params;
Cardinal *num_params;
{
  SG_NodePtr    node;
  int           player;
  int           play;
  Position      x;
  Position      y;

  if (event->type != ButtonPress) {
    XtAppWarning (XtWidgetToApplicationContext (w), 
		  "XGoban - makeMove : This action routine requires a ButtonPress event.");
    return;
  }

  if (current_node != NULL)
    switch (current_mode) {
    case EditMode :
    case MailMode :
      if (edit               == False) { PopupDialog (w, event, EditDialog       ); return; }
      if (current_node->down != NULL ) { PopupDialog (w, event, ReplaceNodeDialog); return; }

      goto play_move;

    case PlayMode :
      if (current_node->player == SG_BlackStone && black_player != Human) break;
      if (current_node->player == SG_WhiteStone && white_player != Human) break;
      if (w_second_toplevel != NULL) {
	if (w == w_second_goban && current_node->player != second_player) break;
	if (w == w_goban        && current_node->player == second_player) break;
      }

      goto play_move;
    }

  if (beep >= -100)
    XBell (XtDisplay (w_goban), beep);

  return;

 play_move :
  x = (Position) ((XButtonPressedEvent *) event)->x;
  y = (Position) ((XButtonPressedEvent *) event)->y;
  
  if (GbGetStoneFromPosition (w_goban, &x, &y) == TRUE) {
    
    HideTmpProperty (current_node);
    
    player = current_node->player;
    node   =  SG_MakeNode (current_node, SG_Move);
    
    node->x     = x;
    node->y     = y;
    node->color = player;
    
    play = Play (node);
    
    switch (play) {
    case SG_PlayIllegal : PopupDialog (w, event, IllegalMoveDialog); break;
    case SG_PlayKo      : PopupDialog (w, event, KoMoveDialog     ); break;
    case SG_PlaySuicide : PopupDialog (w, event, SuicideMoveDialog); break;
    }
    
    if (play != SG_OK) {
      SG_RemoveNode   (node);
      SG_FreeNode     (node);
      ShowTmpProperty (current_node);
    }
    
    else {
      modified     = True;
      current_node = node;

      ShowProperty   (current_node);
      RedisplayBoard ();

      switch (current_mode) {
      case PlayMode : signalMove (node); break;
      case MailMode : Edit (True); break;
      }
    }
  }

  else if (beep >= -100)
    XBell (XtDisplay (w_goban), beep);
}

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

/* ARGSUSED */

void MakeVariation (w, event, params, num_params)
Widget    w;
XEvent   *event;
String   *params;
Cardinal *num_params;
{
  SG_NodePtr   tmp = current_node;
  SG_NodePtr   node;
  int          player;
  int          play;
  Position     x;
  Position     y;

  if (event->type != ButtonPress) {
    XtAppWarning (XtWidgetToApplicationContext (w), 
		  "XGoban - makeVariation : This action routine requires a ButtonPress event.");
    return;
  }

  if (current_node != NULL)
    switch (current_mode) {
    case EditMode :
    case MailMode :

      if (current_node->type != SG_EventType) {
	
	x = (Position) ((XButtonPressedEvent *) event)->x;
	y = (Position) ((XButtonPressedEvent *) event)->y;
	
	if (GbGetStoneFromPosition (w_goban, &x, &y) == TRUE) {
	  
	  tmp = current_node;
	  while (tmp->left != NULL) tmp = tmp->left;
	  
	  player = tmp->up->player;
	  
	  tmp = current_node;
	  while (tmp->left != NULL) tmp = tmp->left;
	  
	  while (tmp != NULL) {
	    if (tmp->type == SG_MoveType && tmp->color == player &&
		(Position) tmp->x == x && (Position) tmp->y == y)
	      return;
	    
	    tmp = tmp->right;
	  }
	  
	  HideProperty (current_node);
	  Unplay       (current_node);
	  
	  node =  SG_MakeNode (current_node, SG_VariationMove);
	  
	  node->x     = x;
	  node->y     = y;
	  node->color = player;
	  
	  play = Play (node);
	  
	  switch (play) {
	  case SG_PlayIllegal : PopupDialog (w, event, IllegalMoveDialog); break;
	  case SG_PlayKo      : PopupDialog (w, event, KoMoveDialog     ); break;
	  case SG_PlaySuicide : PopupDialog (w, event, SuicideMoveDialog); break;
	    
	  }
	  
	  if (play != SG_OK) {
	    SG_RemoveNode (node);
	    SG_FreeNode   (node);
	    Play          (current_node);
	    ShowProperty  (current_node);
	  }
	  
	  else {
	    modified     = True;
	    current_node = node;
	    ShowProperty   (current_node);
	    RedisplayBoard ();
	  }
	}
      }
    }
}

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

/* ARGSUSED */

void MakeVariationPass (w, event, params, num_params)
Widget    w;
XEvent   *event;
String   *params;
Cardinal *num_params;
{
  SG_NodePtr   tmp = current_node;
  SG_NodePtr   node;
  int          player;
  Position     x;
  Position     y;

  if (current_node != NULL)
    switch (current_mode) {
    case EditMode :
    case MailMode :

      if (current_node->type != SG_EventType) {
	
	x = 0;
	y = 0;
	  
	tmp = current_node;
	while (tmp->left != NULL) tmp = tmp->left;
	  
	player = tmp->up->player;
	  
	tmp = current_node;
	while (tmp->left != NULL) tmp = tmp->left;
	  
	while (tmp != NULL) {
	  if (tmp->type == SG_MoveType && tmp->color == player &&
	      (Position) tmp->x == x && (Position) tmp->y == y)
	    return;
	    
	  tmp = tmp->right;
	}
	  
	HideProperty (current_node);
	Unplay       (current_node);
	
	node =  SG_MakeNode (current_node, SG_VariationMove);
	
	node->x     = x;
	node->y     = y;
	node->color = player;
	
	(void) Play (node);

	current_node = node;
	modified     = True;

	ShowProperty   (current_node);
	RedisplayBoard ();
      }
    }
}

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

static Dimension size;
static Position  top;
static Position  left;
static Position  bottom;
static Position  right;

static Position  u_top;
static Position  u_left;
static Position  u_bottom;
static Position  u_right;

/* ARGSUSED */

void SetMode (w, event, params, num_params)
Widget    w;
XEvent   *event;
String   *params;
Cardinal *num_params;
{
  Mode           md;
  Arg            args[8];
  unsigned char *ptr = NULL;

  if (current_node != NULL) {
    if (*num_params != 1) {
      XtAppWarning (XtWidgetToApplicationContext (w), 
		    "XGoban - mode : This action routine requires one argument.");
      return;
    }

    switch (params[0][0]) {
    case 'E' : case 'e' : md = EditMode   ; break;
    case 'D' : case 'd' : md = DiagramMode; break;
    case 'S' : case 's' : md = SizeMode   ; break;
    case 'V' : case 'v' : md = ViewMode   ; break;
    default  :
      XtAppWarning (XtWidgetToApplicationContext (w), 
		    "XGoban - mode : Argument should be Edit, Read, View, Diagram or Size.");
      return;
    }

    if (md != EditMode && edit         == False   ) { PopupDialog (w, event, EditDialog    ); return; }
    
    switch (md) {
    case EditMode :

      switch (current_mode) {

      case SizeMode :
	XtSetArg (args[0], XtNgameSize  , (XtArgVal) size);
	XtSetValues (w_goban, args, 1);

      case ViewMode :

	XtSetArg (args[0], XtNviewTop   , (XtArgVal) top   );
	XtSetArg (args[1], XtNviewLeft  , (XtArgVal) left  );
	XtSetArg (args[2], XtNviewBottom, (XtArgVal) bottom);
	XtSetArg (args[3], XtNviewRight , (XtArgVal) right );
	XtSetValues (w_goban, args, 4);
	
	RedisplayBoard ();
	break;

      case DiagramMode :
	ptr = EndDiagram ();

	if (ptr != NULL) {

	  current_node->setup = ptr;

	  Play (current_node);
	  ShowProperty (current_node);
	}
	break;

      default :
	if (beep >= -100) 
	  XBell (XtDisplay (w_goban), beep); 
	return;
      }
      
      md = (mail_node == NULL) ? EditMode : MailMode;

      break;

    case DiagramMode   : 
      if (current_mode != EditMode && current_mode != MailMode) 
	{ if (beep >= -100) XBell (XtDisplay (w_goban), beep); return; }
      if (current_node->type == SG_MoveType) 
	{ PopupDialog (w, event, DiagramBadNodeWarning); return; }
      
      Unplay (current_node);
      StartDiagram ();
      Play (current_node);
    
      break;
    
    case SizeMode      : 
      if (current_mode != EditMode) { if (beep >= -100) XBell (XtDisplay (w_goban), beep); return; }
      if (current_node->type  != SG_EventType) { PopupDialog (w, event, SizeBadNodeWarning); return; }
      if (current_node->down  != NULL        ) { PopupDialog (w, event, RemoveStonesDialog); return; }

      HideProperty (current_node);
      Unplay       (current_node);

      free (current_node->setup);
      current_node->setup = NULL;

      RedisplayBoard ();

      break;
    
    case ViewMode      : 
      if (current_mode != EditMode && current_mode != MailMode)
	{ if (beep >= -100) XBell (XtDisplay (w_goban), beep); return; }

      XtSetArg (args[0], XtNgameSize  , (XtArgVal) &size);
      XtGetValues (w_goban, args, 1);
 
      XtSetArg (args[0], XtNviewTop   , (XtArgVal) &top   );
      XtSetArg (args[1], XtNviewLeft  , (XtArgVal) &left  );
      XtSetArg (args[2], XtNviewBottom, (XtArgVal) &bottom);
      XtSetArg (args[3], XtNviewRight , (XtArgVal) &right );
      XtGetValues (w_goban, args, 4);
   
      HideProperty (current_node);

      XtSetArg (args[0], XtNviewTop   , (XtArgVal) &u_top   );
      XtSetArg (args[1], XtNviewLeft  , (XtArgVal) &u_left  );
      XtSetArg (args[2], XtNviewBottom, (XtArgVal) &u_bottom);
      XtSetArg (args[3], XtNviewRight , (XtArgVal) &u_right );
      XtGetValues (w_goban, args, 4);
    
      ShowProperty (current_node);

      XtSetArg (args[0], XtNviewTop   , (XtArgVal) size);
      XtSetArg (args[1], XtNviewLeft  , (XtArgVal) 1   );
      XtSetArg (args[2], XtNviewBottom, (XtArgVal) 1   );
      XtSetArg (args[3], XtNviewRight , (XtArgVal) size);
      XtSetValues (w_goban, args, 4);
      
      RedisplayBoard ();
      break;
    
      default :
	return;
    }

    current_mode = md;

    ShowCursor (current_node);
  }
}

/*******************************************************************************************************
 *
 *    Drag a rectangle on screen to set property SiZe or VieW.
 *    Must be called by user as 'drag(Start)' 'drag()' and 'drag(Stop)'.
 */

static Position x0;
static Position y0;
static Position x1;
static Position y1;

static int      drag_in_progress = 0;
static GC       drag_gc;

void Drag (w, event, params, num_params)
Widget    w;
XEvent   *event;
String   *params;
Cardinal *num_params;
{
  static Boolean  gc_created = False;

  int             size;
  Arg             args[4];
  Position        x;
  Position        y;
  Position        xmax;
  Position        ymax;
  Dimension       point_size;
  long            value;

  if (current_mode == SizeMode || current_mode == ViewMode) {
    
    if (event->type != MotionNotify && event->type != ButtonPress && event->type != ButtonRelease) {
      XtAppWarning (XtWidgetToApplicationContext (w), 
		    "XGoban - drag : This action routine requires a button event.");
      return;
    }

    if (gc_created == False) {
      XGCValues      values;

      values.function = GXinvert;
      drag_gc         = XCreateGC (XtDisplay (w_goban), XtWindow (w_goban), GCFunction, &values);
      gc_created      = True;
    }

    switch (*num_params) {

    case 0 :
	
	/*
	 *   drag() : erase previous rectangle, update coordinates and redraw rectangle on screen.
	 */

      if (event->type != MotionNotify) {
	XtAppWarning (XtWidgetToApplicationContext (w), 
		      "XGoban - drag() : This action routine requires a MotionNotify event.");
	return;
      }

      if (drag_in_progress == 1) {

	x = (Position) ((XButtonEvent *) event)->x;
	y = (Position) ((XButtonEvent *) event)->y;
	
	if (GbGetStoneFromPosition (w_goban, &x, &y) != TRUE) return;
      
	xmax = 19;
	ymax = 1;

	GbGetPositionFromStone (w_goban, &x   , &y   );
	GbGetPositionFromStone (w_goban, &xmax, &ymax);
	
	if (x > x0 && y > y0 && (x != x1 || y != y1)) {
	  
	  XtSetArg (args[0], XtNpointSize, &point_size);
	  XtGetValues (w_goban, args, 1);
	  
	  if (x0 != x1 || y0 != y1)
	    XDrawRectangle (XtDisplay (w), XtWindow (w), drag_gc, 
			    x0 - point_size / 2, y0 - point_size / 2, 
			    x1 - x0 + point_size, y1 - y0 + point_size);
	        
	  if (current_mode == SizeMode) {
	    x = x0 + Min (xmax - x0, Min (ymax - y0, Max (x - x0, y - y0)));
	    y = y0 + Min (xmax - x0, Min (ymax - y0, Max (x - x0, y - y0)));
	  }
	  
	  x1 = x;
	  y1 = y;
	
	  XDrawRectangle (XtDisplay (w), XtWindow (w), drag_gc, 
			  x0 - point_size / 2, y0 - point_size / 2, 
			  x1 - x0 + point_size, y1 - y0 + point_size);

	  XFlush (XtDisplay (w));
	}
      }
      break;

    case 1 :

      switch (params[0][2]) {

      case 'A' : 
      case 'a' :
	
	/*
	 *   drag(Start) : initialize rectangle (x0 y0 x1 y1).
	 */

	if (event->type != ButtonPress) {
	  XtAppWarning (XtWidgetToApplicationContext (w), 
			"XGoban - drag(Start) : This action routine requires a ButtonPress event.");
	  return;
	}

	x = (Position) ((XButtonEvent *) event)->x;
	y = (Position) ((XButtonEvent *) event)->y;
    
	if (GbGetStoneFromPosition (w_goban, &x, &y) == TRUE) {
	
	  GbGetPositionFromStone (w_goban, &x, &y);
	
	  x1 = x0 = x;
	  y1 = y0 = y;
	  
	  XtSetArg (args[0], XtNcursor, XC_bottom_right_corner);
	  XtSetValues (w_goban, args, 1);
	  
	  drag_in_progress = 1;
	}
	break;
  
      case 'O' : 
      case 'o' :
	
	/*
	 *   drag(Stop) : erase rectangle on screen and create desired property.
	 */

	if (event->type != ButtonRelease) {
	  XtAppWarning (XtWidgetToApplicationContext (w), 
			"XGoban - drag(Stop) : This action routine requires a ButtonRelease event.");
	  return;
	}
	  
	if (drag_in_progress == 1) {
	  XtSetArg (args[0], XtNpointSize, &point_size);
	  XtGetValues (w_goban, args, 1);
	
	  XDrawRectangle (XtDisplay (w), XtWindow (w), drag_gc, 
			  x0 - point_size / 2, y0 - point_size / 2, 
			  x1 - x0 + point_size, y1 - y0 + point_size);
	
	  GbGetStoneFromPosition (w_goban, &x0, &y0);
	  GbGetStoneFromPosition (w_goban, &x1, &y1);

	  size = Max (x1 - x0 + 1, y0 - y1 + 1);
	
	  switch (current_mode) {

	    /*	    
	     *   Create SiZe property.
	     */

	  case SizeMode :
	  
	    if (size > 1 && size < 20) {
	      XtSetArg (args[0], XtNgameSize , (XtArgVal) size);
	      XtSetValues (w_goban, args, 1);

	      value = ((long) size << 24) + (1 << 16) + (1 << 8) + (long) size;

	      SG_MakeProperty (current_node, SG_SiZe, size );
	      SG_MakeProperty (current_node, SG_VieW, value);

	      modified = True;
	    }

	    break;

	    /*	    
	     *   Create VieW property.
	     */
	  
	  case ViewMode :
	    
	    if (y0 > y1 && x1 > x0) {
   
	      XtSetArg (args[0], XtNviewTop   , (XtArgVal) y0);
	      XtSetArg (args[1], XtNviewLeft  , (XtArgVal) x0);
	      XtSetArg (args[2], XtNviewBottom, (XtArgVal) y1);
	      XtSetArg (args[3], XtNviewRight , (XtArgVal) x1);
	      XtSetValues (w_goban, args, 4);

	      value = ((long) u_top << 24) + ((long) u_left << 16) + ((long) u_bottom << 8) + (long) u_right;
	      SG_MakeProperty (current_node, SG_unVieW, value);

	      value = ((long) y0    << 24) + ((long) x0     << 16) + ((long) y1       << 8) + (long) x1     ;
	      SG_MakeProperty (current_node, SG_VieW  , value);

	      modified = True;
	    }
	    break;
	  }
	}

	/*	    
	 *   Redisplay current node.
	 */

	current_mode     = (mail_node == NULL) ? EditMode : MailMode;
	drag_in_progress = 0;

	ShowCursor     (current_node);
	RedisplayBoard ();
	break;

      default  :
	XtAppWarning (XtWidgetToApplicationContext (w), 
		      "XGoban - drag : Argument should be Start or Stop.");
	return;
      }
      break;

    default :
      XtAppWarning (XtWidgetToApplicationContext (w), 
		    "XGoban - drag : This action routine requires one or no argument.");
      return;
    }
  }
}
