/*  Part of XPCE --- The SWI-Prolog GUI toolkit

    Author:        Jan Wielemaker and Anjo Anjewierden
    E-mail:        jan@swi.psy.uva.nl
    WWW:           http://www.swi.psy.uva.nl/projects/xpce/
    Copyright (c)  1985-2002, University of Amsterdam
    All rights reserved.

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions
    are met:

    1. Redistributions of source code must retain the above copyright
       notice, this list of conditions and the following disclaimer.

    2. Redistributions in binary form must reproduce the above copyright
       notice, this list of conditions and the following disclaimer in
       the documentation and/or other materials provided with the
       distribution.

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    POSSIBILITY OF SUCH DAMAGE.
*/

#include <h/kernel.h>
#include <h/graphics.h>

static void	swap_parents(Node, Node, Chain);
static int	isSonNode2(Node, Node);
static int	isParentNode2(Node, Node);
static status	unrelate_node(Node, Node);
static status	unlinkParentsNode(Node n);
static status	unlinkSonsNode(Node n);
static status	relateNode(Node n, Node n2, Node before);
static status	unrelateImageNode(Node n, Node n2);
static status	unrelateImagesNode(Node n);
static status	relateImagesNode(Node n);
static status	isSonNode(Node n, Node n2);
static status	isParentNode(Node n, Node n2);
static void	changedLink(Node n, Node n2);

static status
initialiseNode(Node n, Graphical gr)
{ assign(n, tree,      NIL);
  assign(n, image,     gr);
  assign(n, parents,   newObject(ClassChain,EAV));
  assign(n, sons,      newObject(ClassChain,EAV));
  assign(n, sons_size, ZERO);
  assign(n, my_size,   ZERO);
  assign(n, level,     ZERO);
  assign(n, computed,  NIL);
  assign(n, collapsed, OFF);
  assign(n, displayed, ON);

  succeed;
}


static status
unlinkNode(Node n)
{ Tree tree = n->tree;
  Cell cell1, cell2;

  if ( notNil(tree) )
  { if ( tree->displayRoot == n )
    { if ( tree->root == n )
	assign(tree, displayRoot, NIL);
      else
	assign(tree, displayRoot, tree->root);
    }

    if ( onFlag(tree, F_FREED|F_FREEING) ) /* destroying the tree */
    { Node son;

      for_chain(n->sons, son, freeObject(son));
      assign(n, image, NIL);

      succeed;
    } else				/* just remove this node */
    { for_cell(cell1, n->parents)
	for_cell(cell2, n->sons)
	  relateNode(cell1->value, cell2->value, NIL);

      if ( n == tree->root )
      { if ( emptyChain(n->sons) )
	  assign(tree, root, NIL);
        else
	  assign(tree, root, n->sons->head->value);

	if ( tree->displayRoot == n )
	  assign(tree, displayRoot, tree->root);
      } else if ( n == tree->displayRoot )
	tree->displayRoot = n->parents->head->value;

      unlinkParentsNode(n);
      unlinkSonsNode(n);
    }

    if ( notNil(n->image) && !isFreedObj(n->image) )
      send(n->image, NAME_device, NIL, EAV);

    assign(n, tree, NIL);
    assign(n, image, NIL);
  }

  succeed;
}


static status
unlinkParentsNode(Node n)
{ Node parent;

  for_chain(n->parents, parent, unrelate_node(parent, n));
  succeed;
}


static status
unlinkSonsNode(Node n)
{ Node son;

  for_chain(n->sons, son, unrelate_node(n, son));
  succeed;
}


static Node
getConvertNode(Class class, Graphical gr)
{ if ( instanceOfObject(gr->device, ClassTree) )
    answer(getNodeGraphical(gr));

  answer(newObject(ClassNode, gr, EAV));
}


static status
computeLevelNode(Node n, Int l, BoolObj force)
{ Cell cell;
  Int nextLevel;			/* level + 1 */

  if ( n->computed != NAME_level || force == ON )
  { assign(n, computed, NAME_level);
    assign(n, level, l);

    if ( n->collapsed == ON )
      succeed;

    nextLevel = inc(l);
    for_cell(cell, n->sons)
      computeLevelNode(cell->value, nextLevel, force);
  } else
  { if ( valInt(l) > valInt(n->level) )
      computeLevelNode(n, l, ON);
  }

  succeed;
}


static Int
getComputeSizeNode(Node n, Int l)
{ Cell cell;
  int hor = n->tree->direction != NAME_vertical;
  Int nextLevel;	/* level + 1 */
  int sons_size;	/* total size of sons */

  if ( n->computed == NAME_size ||	/* already done */
       n->level != l )			/* to be done in another brance */
    answer(ZERO);

  assign(n, computed, NAME_size);
  assign(n, my_size, getv(n->image, hor ? NAME_height : NAME_width, 0, NULL));

  if ( n->collapsed == ON )
  { assign(n, sons_size, ZERO);
    answer(n->my_size);
  }

  nextLevel = inc(l);

  sons_size = 0;
  for_cell(cell, n->sons)
  { if ( cell != n->sons->head )				/* not first */
      sons_size += valInt(n->tree->neighbourGap);
    sons_size += valInt(getComputeSizeNode(cell->value, nextLevel));
  }

  assign(n, sons_size, toInt(sons_size));

  if ( n->tree->direction == NAME_list )
  { if ( notNil(n->sons->head) )
      answer(add(add(n->sons_size, n->my_size), n->tree->neighbourGap));
    else
      answer(n->my_size);
  } else
    answer(maxInt(n->sons_size, n->my_size));
}


static status
computeLayoutNode(Node n, Int l, Int x, Int y)
{ Int y2, x2;
  Tree t = n->tree;
  Graphical gr = n->image;
  int hor = t->direction != NAME_vertical;
  int list = t->direction == NAME_list;
  Int size = maxInt(n->my_size,n->sons_size);
  Int nextLevel;
  Cell cell;
  Node son;

  if ( n->displayed == OFF )
    succeed;

  if ( n->computed == NAME_layout ||
       n->level != l )
    succeed;
  assign(n, computed, NAME_layout);

  x2 = ( hor || list ? x : add(x, div(sub(size,n->my_size), TWO)));
  y2 = (!hor || list ? y : add(y, div(sub(size,n->my_size), TWO)));

  if ( (Tree) gr->device != t ||
       gr->displayed == OFF )
    send(t, NAME_display, gr, EAV);
  if ( gr->area->x != x2 || gr->area->y != y2 )
  { Int av[4];

    av[0] = x2; av[1] = y2; av[2] = DEFAULT; av[3] = DEFAULT;
    qadSendv(gr, NAME_geometry, 4, av);

    if ( list )
    { Cell cell;

      for_cell(cell, n->parents)
      { changedLink(cell->value, n);
      }
    }
  }

  if ( n->collapsed == ON )
    succeed;

  if ( list )
  { x2 = add(x2, t->levelGap);
    y2 = add(y2, add(get(gr, NAME_height, EAV), t->neighbourGap));
  } else if ( hor )
  { x2 = add(x, add(get(gr, NAME_width, EAV), t->levelGap));
    if ( valInt(n->sons_size) > valInt(size) )
      y2 = y;
    else
      y2 = add(y, div(sub(size, n->sons_size), TWO));
  } else
  { y2 = add(y, add(get(gr, NAME_height, EAV), t->levelGap));
    if ( valInt(n->sons_size) > valInt(size) )
      x2 = x;
    else
      x2 = add(x, div(sub(size, n->sons_size), TWO));
  }

  nextLevel = inc(l);

  for_cell(cell, n->sons)
  { son = cell->value;

    if ( son->level == nextLevel && son->computed != NAME_layout )
    { computeLayoutNode(son, nextLevel, x2, y2);
      if ( list )
      { if ( notNil(son->sons->head) )
	  y2 = toInt(valInt(y2) +
		     valInt(son->sons_size) + valInt(son->my_size) +
		     valInt(t->neighbourGap));
	else
	  y2 = add(y2, son->my_size);

	y2 = add(y2, t->neighbourGap);
      } else
      { size = maxInt(son->my_size, son->sons_size);
	if ( hor )
	  y2 = add(y2, add(size, t->neighbourGap));
	else
	  x2 = add(x2, add(size, t->neighbourGap));
      }
    }
  }

  succeed;
}


static void
initUpdateDisplayedNode(Node n)
{ Cell cell;

  assign(n, displayed, DEFAULT);
  for_cell(cell, n->sons)
    initUpdateDisplayedNode(cell->value);
}


static void
markDisplayedNode(Node n)
{ assign(n, displayed, ON);
  if ( n->collapsed != ON )
  { Cell cell;

    for_cell(cell, n->sons)
      markDisplayedNode(cell->value);
  }
}


static status
updateDisplayedNode(Node n)
{ Cell cell;

  if ( isDefault(n->displayed) )
    assign(n, displayed, OFF);

  if ( notNil(n->image) && n->image->displayed != n->displayed )
    DisplayedGraphical(n->image, n->displayed);

  for_cell(cell, n->sons)
    updateDisplayedNode(cell->value);

  succeed;
}


status
updateDisplayedTree(Tree t)
{ if ( notNil(t->root) )
  { initUpdateDisplayedNode(t->root);
    if ( notNil(t->displayRoot) )
      markDisplayedNode(t->displayRoot);
    updateDisplayedNode(t->root);
  }

  succeed;
}


static status
moveAfterNode(Node n, Node n2)
{ Cell cell;

  if ( isDefault(n2) || isNil(n2) )
  { Node parent = getHeadChain(n->parents);

    if ( isObject(parent) )
    { status rval;

      if ( isNil(n2) )			/* @nil: move to the start */
	rval = moveAfterChain(parent->sons, n, DEFAULT);
      else				/* @default: move to the end */
      { Any tail = getTailChain(parent->sons);

	if ( tail && tail != n )
	  rval = moveAfterChain(parent->sons, n, tail);
	else if ( tail && tail == n )
	  succeed;			/* is already the last */
	else
	  rval = FAIL;
      }

      if ( rval )
	requestComputeTree(n->tree);

      return rval;
    }

    fail;
  }


  for_cell(cell, n->parents)
  { Node parent = cell->value;

    if ( memberChain(n2->parents, parent) )
    { status rval;

      if ( (rval = moveAfterChain(parent->sons, n, n2)) )
	requestComputeTree(n->tree);

      return rval;
    }
  }

  fail;
}


static status
moveBeforeNode(Node n, Node n2)
{ Cell cell;

  for_cell(cell, n->parents)
  { Node parent = cell->value;

    if ( memberChain(n2->parents, parent) )
    { status rval;

      if ( (rval = moveBeforeChain(parent->sons, n, n2)) )
	requestComputeTree(n->tree);

      return rval;
    }
  }

  fail;
}


static status
moveNode(Node n, Node n2)		/* n2 becomes a son of n */
{ if ( n->tree != n2->tree ||
       isNil(n->tree) ||
       n == n2 ||
       isSonNode(n2, n) == SUCCEED )
    fail;

  if ( memberChain(n->sons, n2 ) == SUCCEED)
    succeed;

  unlinkParentsNode(n2);
  relateNode(n, n2, NIL);
  requestComputeTree(n->tree);

  succeed;
}


static status
sonNode(Node n, Node n2, Node before)		/* make n2 a son of n */
{ if ( notNil(n2->tree) && n2->tree != n->tree )
    return errorPce(n, NAME_alreadyShown, n2, n2->tree);

  if ( memberChain(n->sons, n2) )
    succeed;

  if ( isParentNode(n, n2) || n2 == n )
    return errorPce(n, NAME_wouldBeCyclic);

  relateNode(n, n2, before);

  if ( notNil(n->tree) )
  { if ( isNil(n2->tree) )
      displayTree(n->tree, n2);

    requestComputeTree(n->tree);
  }

  succeed;
}


static status
imageNode(Node n, Graphical gr)		/* change image of node */
{ if ( isNil(n->tree) )
  { assign(n, image, gr);
  } else
  { if ( notNil(n->image) )
    { Device dev = n->image->device;
      BoolObj disp = n->image->displayed;

      unrelateImagesNode(n);
      send(n->image, NAME_destroy, EAV);
      assign(n, image, gr);
      if ( notNil(dev) && disp == ON )
	send(dev, NAME_display, gr, EAV);
      else
	send(gr, NAME_device, dev, EAV);
    }

    send(gr, NAME_handle, n->tree->sonHandle, EAV);
    send(gr, NAME_handle, n->tree->parentHandle, EAV);
    relateImagesNode(n);

    requestComputeTree(n->tree);
  }

  succeed;
}


/*  Delete an entire subtree, but leave all nodes that still have a
    link to the root node of the tree in the tree. Used in
    unrelateNode().

 ** Thu May 18 15:43:00 1989  jan@swivax.UUCP (Jan Wielemaker)  */

static status
delete_tree_node(Node n)
{ Cell cell;
  Tree tree = n->tree;

  if ( isParentNode(n, tree->root) == SUCCEED )
    succeed;				/* has some other path */

  for_cell(cell, n->sons)
  { Node son = cell->value;

    unrelate_node(n, son);
    delete_tree_node(son);
  }
  send(n, NAME_destroy, EAV);

  succeed;
}


static status
deleteTreeNode(Node n)
{ Cell cell;

  for_cell(cell, n->parents)
    unrelate_node(cell->value, n);

  return delete_tree_node(n);
}


/*  Delete a node from a tree.  All sons of this node  are  connected to
    all  parents of this node.  If the node is the root node of the tree
    it can only be deleted if it has exactly ONE son, which then becomes
    the parent.

 ** Thu Oct 20 22:24:57 1988  jan@swivax.UUCP (Jan Wielemaker)  */

static status
deleteNode(Node n)
{ return freeObject(n);
}


/* swap the positions of two entire subtrees */

static status
swapTreeNode(Node n, Node n2)
{ Cell cell;
  Chain intersection, tmp;
  Node parent;

  if ( n->tree != n2->tree ||
       isNil(n->tree) ||
       isSonNode(n, n2) ||
       isSonNode(n2, n) )
    fail;

  TRY( intersection = getIntersectionChain(n->parents, n2->parents) );

  for_cell(cell, intersection)
  { parent = cell->value;
    swapChain(parent->sons, n, n2);
  }

  swap_parents(n, n2, intersection);
  swap_parents(n2, n, intersection);

  tmp = n2->parents;			/* swap the parent chains. Do not */
  n2->parents = n->parents;		/* use assign as `tmp' will drop out */
  n->parents = tmp;			/* ref. counts is not affected */

  freeObject(intersection);
  requestComputeTree(n->tree);

  succeed;
}

/*  swap the son links of the parents of n to point to n2. Parents in
    `intersection are already swapped.

 ** Fri Oct 21 10:14:51 1988  jan@swivax.UUCP (Jan Wielemaker)  */

static void
swap_parents(Node n, Node n2, Chain intersection)
{ Node parent;
  Cell cell, cell2;

  for_cell(cell, n->parents)
  { parent = cell->value;
    if ( memberChain(intersection, parent) )
      continue;
    for_cell(cell2, parent->sons)
    { if ( (Node) cell2->value == n )
      { unrelateImageNode(parent, n);
	relateImageNode(parent, n2);
	cell2->value = n2;		/* do not use assign here as the son */
	break;				/* might drop. Will be corrected */
      }					/* in second pass anyway */
    }
  }
}


static status
sortSonsNode(Node parent, Code cmp)
{ if ( notNil(parent->sons) )
  { sortChain(parent->sons, cmp, OFF);

    return requestComputeTree(parent->tree);
  }

  succeed;
}


static status
swapNode(Node n, Node n2)			/* swap images of two nodes */
{ Graphical gr;

  if ( n->tree != n2->tree || isNil(n->tree) )
    fail;

  unrelateImagesNode(n);
  unrelateImagesNode(n2);

  gr = n->image;
  assign(n, image, n2->image);
  assign(n2, image, gr);

  relateImagesNode(n);
  relateImagesNode(n2);
  requestComputeTree(n->tree);

  succeed;
}


static status
relateNode(Node n, Node n2, Node before)
{ if ( isNil(before) || isDefault(before) )
    appendChain(n->sons, n2);
  else
    insertBeforeChain(n->sons, n2, before);
  appendChain(n2->parents, n);
  if ( notNil(n->tree) )
    relateImageNode(n, n2);

  succeed;
}


status
relateImageNode(Node n, Node n2)
{ if ( !connectedGraphical(n->image, n2->image, DEFAULT, DEFAULT, DEFAULT) )
    connectGraphical(n->image, n2->image, n->tree->link, DEFAULT, DEFAULT);

  succeed;
}


static status
unrelate_node(Node n, Node n2)
{ status rval;

  addCodeReference(n);
  addCodeReference(n2);

  if ( deleteChain(n->sons, n2) && deleteChain(n2->parents, n) )
  { unrelateImageNode(n, n2);
    rval = SUCCEED;
  } else
    rval = FAIL;

  delCodeReference(n);
  delCodeReference(n2);

  return rval;
}


static status
unrelateNode(Node n, Node n2)
{ status rval = SUCCEED;

  if ( isNil(n->tree) )
    succeed;			/* cannot be related yet */

  if ( memberChain(n->sons, n2) == SUCCEED )
  { unrelate_node(n, n2);
    delete_tree_node(n2);	/* throw away unconnected subtree */
  } else if ( memberChain(n2->sons, n) == SUCCEED )
  { unrelate_node(n2, n);
    delete_tree_node(n);
  } else
    rval = FAIL;

  requestComputeTree(n->tree);

  return rval;
}


static status
unrelateImageNode(Node n, Node n2)	/* parent, son */
{ disconnectGraphical(n->image, n2->image, n->tree->link, DEFAULT, DEFAULT);
  changedLink(n, n2);

  succeed;
}


static status
unrelateImagesNode(Node n)
{ Cell cell;

  for_cell(cell, n->sons)
    unrelateImageNode(n, cell->value);
  for_cell(cell, n->parents)
    unrelateImageNode(cell->value, n);

  succeed;
}


static void
changedLink(Node n, Node n2)		/* parent, son */
{ Tree t = n->tree;
  int list = t->direction == NAME_list;

  if ( list )
  { int lg = valInt(t->levelGap);
    int lx = valInt(n->image->area->x) + lg/2;
    int fy = valInt(getBottomSideGraphical(n->image));
    int ty = valInt(n2->image->area->y) + valInt(n2->image->area->h)/2;

    changedImageGraphical(t,
			  toInt(lx-5), toInt(fy),
			  toInt(7+lg/2), toInt(3+ty-fy));
  }
}


static status
isSonNode(Node n, Node n2)
{ Cell cell;

  for_cell(cell, n->sons)
    DONE(isSonNode2(cell->value, n2));

  fail;
}


static int
isSonNode2(Node n, Node n2)
{ Cell cell;

  if ( n == n2 )
    succeed;
  for_cell(cell, n->sons)
    DONE(isSonNode2(cell->value, n2));

  fail;
}


static status
isParentNode(Node n, Node n2)		/* is n2 a parent of n? */
{ Cell cell;

  for_cell(cell, n->parents)
    DONE(isParentNode2(cell->value, n2));

  fail;
}

static status
isParentNode2(Node n, Node n2)
{ Cell cell;

  if ( n == n2 )
    succeed;
  for_cell(cell, n->parents)
    DONE(isParentNode2(cell->value, n2));

  fail;
}

static status
zoomNode(Node n)
{ return zoomTree(n->tree, n);
}

static status
unzoomNode(Node n)
{ return unzoomTree(n->tree);
}

static status
collapsedNode(Node n, BoolObj val)
{ if ( n->collapsed != val )
  { if ( isNil(n->tree) )
    { assign(n, collapsed, val);
    } else
    { int update = (n->collapsed == ON || val == ON);

      if ( !update && n->tree->direction == NAME_list &&
	   n == n->tree->displayRoot && isNil(n->collapsed) )
	update = TRUE;

      assign(n, collapsed, val);

      if ( update )
      { updateDisplayedTree(n->tree);
	requestComputeTree(n->tree);
      }

      if ( n->tree->direction == NAME_list ) /* TBD: compute the area */
	changedEntireImageGraphical(n->tree);
    }
  }

  succeed;
}


static status
relateImagesNode(Node n)
{ Cell cell;

  for_cell(cell, n->parents)
    relateImageNode(cell->value, n);
  for_cell(cell, n->sons)
    relateImageNode(n, cell->value);

  succeed;
}


status
forAllNode(Node n, Code msg)
{ Cell cell;

  for_cell(cell, n->sons)
    TRY( forAllNode(cell->value, msg) );
  TRY( forwardCode(msg, n, EAV) );

  succeed;
}


status
forSomeNode(Node n, Code msg)
{ Cell cell;

  for_cell(cell, n->sons)
    forSomeNode(cell->value, msg);
  forwardCode(msg, n, EAV);

  succeed;
}


Node
getFindNodeNode(Node n, Graphical gr)
{ Cell cell;
  Node n2;

  if ( n->image == gr )
    answer( n );

  for_cell(cell, n->sons)
    if ( (n2 = getFindNodeNode(cell->value, gr)) )
      answer(n2);

  fail;
}


static Node
getFindNode(Node n, Code msg)
{ Cell cell;
  Node n2;

  if ( forwardCode(msg, n, EAV) != FAIL )
    answer(n);

  for_cell(cell, n->sons)
    if ( (n2 = getFindNode(cell->value, msg)) )
      answer(n2);

  fail;
}


		 /*******************************
		 *	      EVENT		*
		 *******************************/

static status
eventNode(Node n, EventObj ev)
{ Cell cell;
  Tree t = n->tree;

  if ( isNil(t) )
    fail;

  if ( n->collapsed == ON )
  { for_cell(cell, t->collapsedHandlers)
    { if ( postEvent(ev, n->image, cell->value) )
	succeed;
    }
  }
  if ( emptyChain(n->sons) )
  { for_cell(cell, t->leafHandlers)
    { if ( postEvent(ev, n->image, cell->value) )
	succeed;
    }
  }
  if ( n->tree->displayRoot == n )
  { for_cell(cell, t->rootHandlers)
    { if ( postEvent(ev, n->image, cell->value) )
	succeed;
    }
  }
  for_cell(cell, t->nodeHandlers)
  { if ( postEvent(ev, n->image, cell->value) )
      succeed;
  }

  fail;
}


		/********************************
		*             VISUAL		*
		********************************/

static Chain
getContainsNode(Node n)
{ answer(answerObject(ClassChain, n->image, EAV));
}


static Any
getContainedInNode(Node n)
{ if ( notNil(n->tree) )
    answer(n->tree);

  fail;
}

		 /*******************************
		 *	 CLASS DECLARATION	*
		 *******************************/

/* Type declarations */

static char *T_computeLevel[] =
        { "int", "[bool]" };
static char *T_computeLayout[] =
        { "int", "int", "int" };
static char *T_son[] =
        { "son=node", "before=[node]*" };

/* Instance Variables */

static vardecl var_node[] =
{ SV(NAME_image, "graphical", IV_GET|IV_STORE, imageNode,
     NAME_appearance, "Graphical image of the node"),
  IV(NAME_tree, "tree", IV_GET,
     NAME_organisation, "Tree the node is in"),
  IV(NAME_level, "int", IV_GET,
     NAME_hierarchy, "Distance from the root"),
  IV(NAME_sons, "chain", IV_GET,
     NAME_hierarchy, "Sub nodes of this node"),
  IV(NAME_parents, "chain", IV_GET,
     NAME_hierarchy, "Parent nodes of this node"),
  SV(NAME_collapsed, "bool*", IV_GET|IV_STORE, collapsedNode,
     NAME_scroll, "Sub nodes are invisible"),
  IV(NAME_displayed, "[bool]", IV_GET,
     NAME_scroll, "Node is visible (@default used by update)"),
  IV(NAME_sonsSize, "int", IV_NONE,
     NAME_update, "Height of the combined sons"),
  IV(NAME_mySize, "int", IV_NONE,
     NAME_update, "Height of myself"),
  IV(NAME_computed, "{level,size,layout}*", IV_NONE,
     NAME_update, "Stage of the layout process")
};

/* Send Methods */

static senddecl send_node[] =
{ SM(NAME_initialise, 1, "image=graphical", initialiseNode,
     DEFAULT, "Create from graphical"),
  SM(NAME_unlink, 0, NULL, unlinkNode,
     DEFAULT, "Unlink from tree"),
  SM(NAME_delete, 0, NULL, deleteNode,
     NAME_edit, "Delete, reconnect sons to parents"),
  SM(NAME_deleteTree, 0, NULL, deleteTreeNode,
     NAME_edit, "Delete node and all subnodes"),
  SM(NAME_move, 1, "node", moveNode,
     NAME_edit, "Move argument to become a son of me"),
  SM(NAME_moveAfter, 1, "[node]*", moveAfterNode,
     NAME_edit, "Move node to be just after (below) arg"),
  SM(NAME_moveBefore, 1, "node", moveBeforeNode,
     NAME_edit, "Move node to be just before (above) arg"),
  SM(NAME_son, 2, T_son, sonNode,
     NAME_edit, "Add a new child-node"),
  SM(NAME_swap, 1, "node", swapNode,
     NAME_edit, "Swap images of two nodes"),
  SM(NAME_swapTree, 1, "node", swapTreeNode,
     NAME_edit, "Swap positions of two entire trees"),
  SM(NAME_sortSons, 1, "code", sortSonsNode,
     NAME_order, "Sort sons using code and recompute"),
  SM(NAME_unrelate, 1, "node", unrelateNode,
     NAME_edit, "Delete (direct) relation to argument"),
  SM(NAME_isParent, 1, "node", isParentNode,
     NAME_hierarchy, "Test if node is a parent (recursively)"),
  SM(NAME_isSon, 1, "node", isSonNode,
     NAME_hierarchy, "Test if node is a son (recursively)"),
  SM(NAME_computeLevel, 2, T_computeLevel, computeLevelNode,
     NAME_internal, "Recursively assign each node a <-level"),
  SM(NAME_forAll, 1, "code", forAllNode,
     NAME_iterate, "Run code on all sons; demand acceptance"),
  SM(NAME_forSome, 1, "code", forSomeNode,
     NAME_iterate, "Run code on all sons"),
  SM(NAME_unzoom, 0, NULL, unzoomNode,
     NAME_scroll, "Unzoom tree back to its root"),
  SM(NAME_zoom, 0, NULL, zoomNode,
     NAME_scroll, "Zoom the tree to this node"),
  SM(NAME_computeLayout, 3, T_computeLayout, computeLayoutNode,
     NAME_update, "Recursively place each node (level, x, y)"),
  SM(NAME_event, 1, "event", eventNode,
     NAME_event, "Process central tree recognisers")
};

/* Get Methods */

static getdecl get_node[] =
{ GM(NAME_containedIn, 0, "tree", NULL, getContainedInNode,
     DEFAULT, "Visual I'm contained in (tree)"),
  GM(NAME_contains, 0, "chain", NULL, getContainsNode,
     DEFAULT, "New chain with visuals I manage (holding <-image)"),
  GM(NAME_convert, 1, "node", "graphical", getConvertNode,
     DEFAULT, "Convert graphical object"),
  GM(NAME_find, 1, "node", "code", getFindNode,
     NAME_iterate, "Find node that accepts code"),
  GM(NAME_findNode, 1, "node", "graphical", getFindNodeNode,
     NAME_iterate, "Find node that has ->image graphical"),
  GM(NAME_computeSize, 1, "int", "int", getComputeSizeNode,
     NAME_update, "Recursively compute the sub-tree-size (level)")
};

/* Resources */

#define rc_node NULL
/*
static classvardecl rc_node[] =
{
};
*/

/* Class Declaration */

static Name node_termnames[] = { NAME_image };

ClassDecl(node_decls,
          var_node, send_node, get_node, rc_node,
          1, node_termnames);

status
makeClassNode(Class class)
{ declareClass(class, &node_decls);
  delegateClass(class, NAME_image);

  succeed;
}
