#############################################################################
##
#W  poset.gi                  	XGAP library                  Max Neunhoeffer
##
##
#Y  Copyright 1998,       Max Neunhoeffer,              Aachen,       Germany
##
##  This file contains the implementations for graphs and posets
##


#############################################################################
##
##  Declarations of representations:
##
#############################################################################


#############################################################################
##
#R  IsGraphicGraphRep . . . . . . . . . . . . . . .  representation for graph
##
if not IsBound(IsGraphicGraphRep) then
  DeclareRepresentation( "IsGraphicGraphRep",
    IsComponentObjectRep and IsAttributeStoringRep and IsGraphicSheet and
    IsGraphicSheetRep,
# we inherit those components from the sheet:        
    [ "name", "width", "height", "gapMenu", "callbackName", "callbackFunc",
      "menus", "objects", "free",
# now our own components:
      "vertices","edges","selectedvertices","menutypes",
      "menuenabled","rightclickfunction","color"],
    IsGraphicSheet );
fi;



#############################################################################
##
#R  IsGraphicPosetRep . . . . . . . . . . . . . . .  representation for poset
##
if not IsBound(IsGraphicPosetRep) then
  DeclareRepresentation( "IsGraphicPosetRep",
        IsComponentObjectRep and IsAttributeStoringRep and 
        IsGraphicSheet and IsGraphicSheetRep and IsGraphicGraphRep,
# we inherit those components from the sheet:        
    [ "name", "width", "height", "gapMenu", "callbackName", "callbackFunc",
      "menus", "objects", "free",
# now our own components:
      "levels",           # list of levels, stores current total ordering
      "levelparams",      # list of level parameters
      "selectedvertices", # list of selected vertices
      "menutypes",        # one entry per menu which contains list of types
      "menuenabled",      # one entry per menu which contains list of flags
      "rightclickfunction",    # the current function which is called when
                               # user clicks right button
      "color",            # some color infos for the case of different models
      "levelboxes",       # little graphic boxes for the user to handle levels
      "showlevelparams",  # flag, if level parameters are shown
      "showlevels"],      # flag, if levelboxes are shown
    IsGraphicSheet );
fi;


#############################################################################
##
#R  IsGPLevel . . . . . . . . . . . . . . . . . . .  representation for level
##
if not IsBound(IsGPLevel) then
  DeclareRepresentation( "IsGPLevel",
        IsComponentObjectRep,
        [ "top",          # y coordinate of top of level, relative to sheet
          "height",       # height in pixels
          "classes",      # list of classes, which are lists of vertices
          "classparams",  # list of class parameters
          "poset"         # poset to which level belongs
        ],
        IsGraphicObject );
fi;

#############################################################################
##
#R  IsGGVertex . . . . . . . . . . . . . . . . . .  representation for vertex
##
if not IsBound(IsGGVertex) then
  DeclareRepresentation( "IsGGVertex",
        IsComponentObjectRep and IsGraphicObject,
        [ "data",         # the mathematical data
          "obj",          # real graphic object
          "x","y",        # coordinates of graphic object within sheet
          "serial",       # a serial number for comparison
          "label"         # the label of the vertex or false
        ],
        IsGraphicObject );
fi;


#############################################################################
##
#R  IsGPVertex . . . . . . . . . . . . . . . . . .  representation for vertex
##
if not IsBound(IsGPVertex) then
  DeclareRepresentation( "IsGPVertex",
        IsComponentObjectRep and IsGraphicObject,
        [ "data",         # the mathematical data
          "obj",          # real graphic object
          "levelparam",   # level parameter
          "classparam",   # class parameter
          "maximals",     # list of vertices which are maximal subobjects
          "maximalin",    # list of vertices where this one is maximal in
          "x","y",        # coordinates of graphic object within level
          "serial",       # a serial number for comparison
          "label"         # the label of the vertex or false
        ],
        IsGraphicObject );
fi;


#############################################################################
##
##  Some global things we all need:
##
#############################################################################


##  We count all vertices:
PosetLastUsedSerialNumber := 0;


##  The following function is installed as a LeftPBDown in every graph or
##  poset. It calls the operation PosetLeftClick.

PosetLeftClickCallback := function(poset,x,y)
  PosetLeftClick(poset,x,y);
end;
  
##  The following function is installed as a RightPBDown in every graph or
##  poset. It calls the operation PosetRightClick.

PosetRightClickCallback := function(poset,x,y)
  PosetRightClick(poset,x,y);
end;
  
##  The following function is installed as a CtrlLeftPBDown and 
##  ShiftLeftPBDown in every graph or poset. It calls the operation 
##  PosetCtrlLeftClick.

PosetCtrlLeftClickCallback := function(poset,x,y)
  PosetCtrlLeftClick(poset,x,y);
end;
    

##  The following is a for a menu entry and just calls another method:
PosetDoRedraw := function(poset,menu,entry)
  DoRedraw(poset);
end;


##  Our menu which goes in all poset sheets:
PosetMenuEntries :=
  ["Redraw","Show Levels","Show Level Parameters",,
   "Delete Vertices","Delete Edge","Merge Classes",,
   "Magnify Lattice", "Shrink Lattice", "Resize Lattice", "Resize Sheet",
   "Move Lattice",,
   "Change Labels","Average Y Positions","Average X Positions",
   "Rearrange Classes"];
PosetMenuTypes :=
  ["forany","forany","forany",,
   "forsubset","foredge","forsubset",,
   "forany","forany","forany","forany","forany",,
   "forsubset","forany","forsubset","forsubset"];
PosetMenuFunctions :=
  [ PosetDoRedraw,PosetShowLevels,PosetShowLevelparams,,
    UserDeleteVerticesOp, UserDeleteEdgeOp, UserMergeClassesOp,,
    UserMagnifyLattice,UserShrinkLattice,UserResizeLattice,UserResizeSheet,
    UserMoveLattice,,
    UserChangeLabels,UserAverageY,UserAverageX,UserRearrangeClasses];


#############################################################################
##
##  Constructors:
##
#############################################################################


## we need this to set up the colors in a sheet:

BindGlobal( "GPMakeColors",
        function( sheet )
  
  # set up color information:
  if sheet!.color.model = "color"  then
    if COLORS.red <> false  then
      sheet!.color.unselected := COLORS.black;
      sheet!.color.selected   := COLORS.red;
    else
      sheet!.color.unselected := COLORS.dimGray;
      sheet!.color.selected   := COLORS.black;
    fi;
    if COLORS.green <> false  then
      sheet!.color.result := COLORS.green;
    else
      sheet!.color.result := COLORS.black; # COLORS.lightGray;
    fi;
  else
    sheet!.color.selected   := COLORS.black;
    sheet!.color.unselected := COLORS.black;
    sheet!.color.result     := false;
  fi;
end);

  
#############################################################################
##
#M  GraphicGraph( <name>, <width>, <height> ) . . . . . . a new graphic graph
##
##  creates a new graphic graph which is a graphic sheet representation
##  with knowledge about vertices and edges and infrastructure for user
##  interfaces.
##
InstallMethod( GraphicGraph,
    "for a string, and two integers",
    true,
    [ IsString,
      IsInt,
      IsInt ],
    0,

function( name, width, height )
  #local ...;
  
end);


#############################################################################
##
#M  GraphicPoset( <name>, <width>, <height> ) . . . . . . a new graphic poset
##
##  creates a new graphic poset which is a specialization of a graphic graph
##  mainly because per definition a poset comes in "layers" or "levels". This
##  leads to some algorithms that are more efficient than the general ones
##  for graphs.
##
InstallMethod( GraphicPoset,
    "for a string, and two integers",
    true,
    [ IsString,
      IsInt,
      IsInt ],
    0,

function( name, width, height )
  local   poset,  tmpEntries,  tmpTypes,  tmpFuncs,  m;
  
  poset := GraphicSheet(name,width,height);
  SetFilterObj(poset,IsGraphicGraphRep);
  SetFilterObj(poset,IsGraphicPosetRep);
  poset!.levels := [];
  poset!.levelparams := [];
  poset!.selectedvertices := [];
  # think of the GAP menu:
  poset!.menutypes := [List(poset!.menus[1]!.entries,x->"forany")];
  poset!.menuenabled := [List(poset!.menus[1]!.entries,x->true)];
  poset!.rightclickfunction := Ignore;
  
  # set up color information:
  poset!.color := rec();
  if COLORS.red <> false or COLORS.lightGray <> false  then
    poset!.color.model := "color";
    # note: if you rename this, think of the "use black&white" below!  
  else
    poset!.color.model := "monochrome";
  fi;
  GPMakeColors(poset);
  
  poset!.levelboxes := [];
  poset!.showlevels := false;
  poset!.lptexts := [];
  poset!.showlevelparams := true;
  
  InstallCallback(poset,"LeftPBDown",PosetLeftClickCallback);
  InstallCallback(poset,"ShiftLeftPBDown",PosetCtrlLeftClickCallback);
  InstallCallback(poset,"CtrlLeftPBDown",PosetCtrlLeftClickCallback);
  InstallCallback(poset,"RightPBDown",PosetRightClickCallback);
  
  tmpEntries := ShallowCopy(PosetMenuEntries);
  tmpTypes := ShallowCopy(PosetMenuTypes);
  tmpFuncs := ShallowCopy(PosetMenuFunctions);
  if poset!.color.model = "color" then
    Append(tmpEntries,["-","Use Black&White"]);
    Append(tmpTypes,["-","forany"]);
    Append(tmpFuncs,["-",UserUseBlackWhite]);
  fi;
  m := Menu(poset,"Poset",tmpEntries,tmpTypes,tmpFuncs);
  Check(m,"Show Level Parameters",true);
  
  return poset;
end);


#############################################################################
##
#M  CreateLevel(<poset>, <levelparam>) . . . . . . creates new level in poset
#M  CreateLevel(<poset>, <levelparam>, <lptext>) . creates new level in poset
##
##  A level in a graphic poset can be thought of as a horizontal slice of
##  the poset. It has a y coordinate of the top of the level relatively to
##  the graphic sheet and a height. Every class of vertices in a graphic
##  poset is in a level. The levels are totally ordered by their y
##  coordinate. No two vertices which are included in each other are in the
##  same level. A vertex containing another one is always "higher" on the
##  screen, meaning in a "higher" level.  Every level has a unique
##  levelparam, which can be any {\GAP} object. The user is responsible for
##  all methods where a levelparam occurs as parameter and is not just an
##  integer. There is NO {\GAP} object representing a level which is visible
##  for the user of posets. All communication about levels goes via the
##  levelparam.  Returns fail if there is already a level with a level
##  parameter which is considered "equal" by CompareLevels or levelparam if
##  everything went well.
##  The second method allows to specify which text appears for the level at
##  the right edge of the sheet.
##
InstallMethod( CreateLevel,
    "for a graphic poset, a level parameter, and a string",
    true,
    [ IsGraphicPosetRep, IsObject, IsString ],
    0,

function( poset, levelparam, lpstr )
  local   level,  box,  str,  strlen,  text,  l,  firstpos,  before,  look,  
          compare,  i,  cl,  v;
      
  # does this level parameter exist already?
  if Position(poset!.levelparams,levelparam) <> fail then
    return fail;
  fi;
  
  # create a level object:
  level := rec(classes := [],
               classparams := [],
               poset := poset);
  Objectify(NewType(GraphicObjectFamily,IsGPLevel),level);
  
  # is it the first level:
  if poset!.levelparams = [] then
    poset!.levelparams := [levelparam];
    poset!.levels := [level];
    level!.top := 0;
    level!.height := 2 * VERTEX.diameter;
    
    # make a level box:
    box := Box(poset,0,level!.top+level!.height-8,8,8);
    if COLORS.blue <> false then
      Recolor(box,COLORS.blue);
    fi;
    if not poset!.showlevels then
      Destroy(box);
    fi;
    
    poset!.levelboxes := [ box ];
    
    # make a text for level parameter:
    if lpstr <> "" then
      str := lpstr;
    else
      str := String(levelparam);
    fi;
    strlen := Length(str);
    text := Text(poset,FONTS.normal,
                 poset!.width - 24 - strlen*FontInfo(FONTS.normal)[3],
                 level!.top + QuoInt(level!.height,2),str);
    if COLORS.blue <> false then
      Recolor(text,COLORS.blue);
    fi;
    if not poset!.showlevelparams then
      Destroy(text);
    fi;
    
    poset!.lptexts := [ text ];
    
    return levelparam;
  fi;
  
  # now find the position, we choose the last position where the new level
  # can be according to the partial order defined by CompareLevels we do a
  # binary search, we insert not before "firstpos" and before "before".
  # Attention: We cannot decide at a level which is not comparable to the
  # new level, so we have to search linearly for a comparable level!
  l := Length(poset!.levelparams);
  firstpos := 1;
  before := l + 1;
  while firstpos < before do
    look := QuoInt(firstpos + before,2);
    repeat  # search first backward up to firstpos, then down
      compare := CompareLevels(poset,levelparam,poset!.levelparams[look]);
      if compare = 0 then 
        return fail;
      elif compare = fail then   # not comparable
        look := look-1;
      fi;
    until compare <> fail or look < firstpos;
    if compare = fail then
      # search now forward down to before
      look := QuoInt(firstpos + before,2)+1;
      if look = before then
        firstpos := before;   # we insert right HERE!
        compare := 0;
      fi;
      while compare = fail do
        compare := CompareLevels(poset,levelparam,poset!.levelparams[look]);
        if compare = 0 then 
          return fail;
        elif compare = fail then     # not comparable
          look := look+1;
          if look = before then     # nothing comparable in between!
            firstpos := before;     # we insert right HERE!
            compare := 0;           # this does exactly that!
          fi;
        fi;
      od;
    fi;
    if compare < 0 then
      before := look;
    elif compare > 0 then
      firstpos := look+1;
    fi;
  od;
  
  # we now insert at position firstpos = before:
  poset!.levelparams{[firstpos+1..l+1]} := poset!.levelparams{[firstpos..l]};
  poset!.levelparams[firstpos] := levelparam;
  poset!.levels{[firstpos+1..l+1]} := poset!.levels{[firstpos..l]};
  poset!.levels[firstpos] := level;
  poset!.levelboxes{[firstpos+1..l+1]} := poset!.levelboxes{[firstpos..l]};
  poset!.lptexts{[firstpos+1..l+1]} := poset!.lptexts{[firstpos..l]};
  
  if firstpos = 1 then
    level!.top := 0;
  else
    level!.top := poset!.levels[firstpos-1]!.top +
                  poset!.levels[firstpos-1]!.height;
  fi;
  level!.height := 2 * VERTEX.diameter;
  
  # move all lower levels down:
  FastUpdate(poset,true);
  for i in [firstpos+1..l+1] do
    poset!.levels[i]!.top := poset!.levels[i]!.top + level!.height;
    for cl in poset!.levels[i]!.classes do
      for v in cl do
        MoveDelta(v!.obj,0,level!.height);
      od;
    od;
    if poset!.showlevels then
      MoveDelta(poset!.levelboxes[i],0,level!.height);
    fi;
    if poset!.showlevelparams then
      MoveDelta(poset!.lptexts[i],0,level!.height);
    fi;
  od;
  FastUpdate(poset,false);
  
  # has the graphic sheet become higher?
  l := l + 1;    # this means:   l := Length(poset!.levels);
  i := poset!.levels[l]!.top + poset!.levels[l]!.height;
  if i > poset!.height then
    Resize(poset,poset!.width,i);
  fi;
  
  # create a level box:
  box := Box(poset,0,level!.top+level!.height-8,8,8);
  if COLORS.blue <> false then
    Recolor(box,COLORS.blue);
  fi;
  if not poset!.showlevels then
    Destroy(box);
  fi;
  poset!.levelboxes[firstpos] := box;
  
  # create a level parameter text:
  if lpstr <> "" then
    str := lpstr;
  else
    str := String(levelparam);
  fi;
  strlen := Length(str);
  text := Text(poset,FONTS.normal,
               poset!.width - 24 - strlen*FontInfo(FONTS.normal)[3],
               level!.top + QuoInt(level!.height,2),str);
  if COLORS.blue <> false then
    Recolor(text,COLORS.blue);
  fi;
  if not poset!.showlevelparams then
    Destroy(text);
  fi;
  poset!.lptexts[firstpos] := text;
  
  return levelparam;
end);


InstallOtherMethod( CreateLevel,
    "for a graphic poset, and a level parameter",
    true,
    [ IsGraphicPosetRep, IsObject ],
    0,
function( poset, levelparam )
  return CreateLevel(poset,levelparam,"");
end);


#############################################################################
##
#M  CreateClass(<poset>,<levelparam>,<classparam>) . . . .  creates new class
##
##  A class in a graphic poset is a collection of vertices within a level
##  which belong together in some sense.  Every vertex in a graphic poset
##  is in a class, which in turn belongs to a level. Every class in a level
##  has a unique classparam, which can be any {\GAP} object. The user is
##  responsible for all methods where a classparam occurs as parameter and
##  is not just an integer. There is NO {\GAP} object representing a class
##  which is visible to the user of posets. All communication about classes
##  goes via the classparam.  Returns fail if there is no level with
##  parameter levelparam or there is already a class in this level with
##  parameter classparam. Returns classparam otherwise.
##
InstallMethod( CreateClass,
    "for a graphic poset, a level parameter, and a class parameter",
    true,
    [ IsGraphicPosetRep, IsObject, IsObject ],
    0,

function( poset, levelparam, classparam )
  local nr, level;
  
  nr := Position(poset!.levelparams,levelparam);
  if nr = fail then
    return fail;
  fi;
  level := poset!.levels[nr];
  
  nr := Position(level!.classparams,classparam);
  if nr <> fail then
    return fail;
  fi;
  
  Add(level!.classparams,classparam);
  Add(level!.classes,[]);
  
  return classparam;
end);

  
#############################################################################
##
#M  Vertex(<graph>,<data>[,<inf>]) . . . . . . . . . . . . creates new vertex
##
##  Creates a new vertex. <inf> is a record in which additional info can be
##  supplied for the new vertex. For general graphic graphs only the
##  "label", "color", "shape", "x" and "y" components are applicable, they
##  contain a short label which will be attached to the vertex, the color,
##  the shape ("circle", "diamond", or "rectangle") and the coordinates
##  relative to the graphic sheet respectively. For graphic posets also the 
##  components "levelparam" and "classparam" are evaluated. If the component
##  "hints" is bound it must be a list of x coordinates which will be
##  delivered to ChoosePosition to help placement. Those x coordinates will
##  be the coordinates of other vertices related to the new one. All values of
##  record components which are not specified will be determined by calling 
##  some methods for graphic graphs or posets. Those are:
##    ChooseLabel for the label,
##    ChooseColor for the color,
##    ChooseShape for the shape,
##    ChoosePosition for the position,
##    ChooseLevel for the levelparam, and
##    ChooseClass for the classparam.
##    ChooseWidth for the line width of the vertex
##  Returns fail no vertex was created. This happens only, if one of the
##  choose functions return fail or no possible value, for example a
##  non-existing level or class parameter.
##  Returns vertex object if everything went well. 
##
InstallOtherMethod( Vertex,
    "for a graphic poset, an object, and a record",
    true,
    [ IsGraphicPosetRep, IsObject, IsRecord ],
    0,

function( poset, data, info )
  local   lp,  lnr,  level,  cp,  cnr,  class,  vertex,  label,  shape,  
          color,  position, width;
  
  # first determine levelparam:
  if not IsBound(info.levelparam) then
    lp := ChooseLevel(poset,data);
  else
    lp := info.levelparam;
  fi;
  if lp = fail then
    return fail;
  fi;
  
  # we search for the level:
  lnr := Position(poset!.levelparams,lp);
  if lnr = fail then
    return fail;
  fi;
  level := poset!.levels[lnr];
  
  # now determine class:
  if not IsBound(info.classparam) then
    cp := ChooseClass(poset,data,lp);
  else
    cp := info.classparam;
  fi;
  if cp = fail then
    return fail;
  fi;
  
  # we search for the class:
  cnr := Position(level!.classparams,cp);
  if cnr = fail then
    return fail;
  fi;
  class := level!.classes[cnr];
  
  # create a new vertex object:
  PosetLastUsedSerialNumber := PosetLastUsedSerialNumber + 1;
  vertex := rec(data := data,
                levelparam := lp,
                classparam := cp,
                maximals := [],
                maximalin := [],
                serial := PosetLastUsedSerialNumber);
  Objectify(NewType(GraphicObjectFamily,IsGPVertex),vertex);
  SetFilterObj(vertex,IsGGVertex);
  SetFilterObj(vertex,IsAlive);
  
  # choose label, shape, color and position:
  if not IsBound(info.label) then
    label := ChooseLabel(poset,data);
    if label = fail then
      return fail;
    fi;
  else
    label := info.label;
  fi;
  if not IsBound(info.shape) then
    shape := ChooseShape(poset,data);
    if shape = fail then
      return fail;
    fi;
  else
    shape := info.shape;
  fi;
  if not IsBound(info.color) then
    color := ChooseColor(poset,data);
    if color = fail then
      return fail;
    fi;
  else
    color := info.color;
  fi;
  if not (IsBound(info.x) and IsBound(info.y)) then
    if IsBound(info.hints) then
      position := ChoosePosition(poset,data,level,class,info.hints);
    else
      position := ChoosePosition(poset,data,level,class,[]);
    fi;
    if IsBound(info.x) then   # this takes precedence!
      vertex!.x := info.x;
    else
      vertex!.x := position[1];
    fi;
    if IsBound(info.y) then   # this takes precedence!
      vertex!.y := info.y;
    else
      vertex!.y := position[2];
    fi;
  else
    vertex!.x := info.x;
    vertex!.y := info.y;
  fi;
  if not IsBound(info.width) then
    width := ChooseWidth(poset,data);
    if width = fail then
      return fail;
    fi;
  fi;
  
  vertex!.label := label;
  
  # create the graphic object:
  vertex!.obj := Vertex(poset,vertex!.x,level!.top + vertex!.y,
                        rec(label := label,color := color,width := width));
  if shape = "diamond" then
    Reshape(vertex!.obj,VERTEX.diamond);
  elif shape = "rectangle" then
    Reshape(vertex!.obj,VERTEX.rectangle);
  fi;
  
  # put it into the class:
  Add(class,vertex);
  
  return vertex;
end);


#############################################################################
##
##  The following function is only internal:
##
##  Use it on your own risk and only if you know what you are doing!
##
GPSearchWay := function(poset,v1,v2,l2)
  local v, p;
  for v in v1!.maximals do
    if v = v2 then
      return true;
    fi;
    
    if Position(poset!.levelparams,v!.levelparam) < l2 then
      if GPSearchWay(poset,v,v2,l2) then
        return true;
      fi;
    fi;
  od;
  return false;
end;


#############################################################################
##
#M  Edge(<poset>,<vertex1>,<vertex2>) . . . . . . . . . . . . adds a new edge
#M  Edge(<poset>,<vertex1>,<vertex2>,<def>) . . . . . . . . . adds a new edge
##
##  Adds a new edge from <vertex1> to <vertex2>. For posets this puts one
##  of the vertices into the other as a maximal subvertex. So either
##  <vertex1> must lie in a "higher" level than <vertex2> or the other way
##  round. There must be no vertex "between" <vertex1> and <vertex2>. If
##  the two vertices are in the same level or one is already indirectly
##  included in the other fail is returned, otherwise true. That means,
##  that in the case where one of the two vertices is already a maximal
##  subobject of the other, then the method does nothing and returns true.
##  The variation with a defaults record just hands this over to the lower
##  levels, meaning that the line width and color are modified.
##
InstallOtherMethod( Edge,
    "for a graphic poset, two vertices, and a defaults record",
    true,
    [ IsGraphicPosetRep, IsGPVertex, IsGPVertex, IsRecord ],
    0,

function( poset, v1, v2, def )
  local   l1,  l2,  dummy,  l,  p;
  
  # we permute v1 and v2 such that v1 is in higher level:
  if CompareLevels(poset,v1!.levelparam,v2!.levelparam) = 0 then
    return fail;
  fi;
  l1 := Position(poset!.levelparams,v1!.levelparam);
  l2 := Position(poset!.levelparams,v2!.levelparam);
  if l1 > l2 then
    dummy := v1; 
    v1 := v2;
    v2 := dummy;
    dummy := l1;
    l1 := l2;
    l2 := dummy;
  fi;
   
  # first we have to perform a few checks:
  if Position(v1!.maximals,v2) <> fail then
    return true;
  fi;
  if GPSearchWay(poset,v1,v2,l2) then
    return fail;
  fi;
  
  # let's think about color, label and width:
  if not IsBound(def.color) then
    def.color := ChooseColor(poset,v1!.data,v2!.data);
    if def.color = fail then
      return fail;
    fi;
  fi;
  if not IsBound(def.label) then
    def.label := ChooseLabel(poset,v1!.data,v2!.data);
    if def.label = fail then
      return fail;
    fi;
  fi;
  if not IsBound(def.width) then
    def.width := ChooseWidth(poset,v1!.data,v2!.data);
    if def.width = fail then
      return fail;
    fi;
  fi;
  
  # now we know that there is no direct or indirect inclusion of v2 in v1.
  # we can savely put v2 "into" v1.
  Add(v1!.maximals,v2);
  Add(v2!.maximalin,v1);
  Connection(v1!.obj,v2!.obj,def);                                               

  return true;
  
end);

InstallOtherMethod( Edge,
    "for a graphic poset, and two vertices",
    true,
    [ IsGraphicPosetRep, IsGPVertex, IsGPVertex ],
    0,

function( poset, v1, v2 )
  return Edge(poset,v1,v2,rec());
end);

        
#############################################################################
##
##  Destructors:
##
#############################################################################


#############################################################################
##
##  Set this variable temporarily to false if you delete many things!
##
GGDeleteModifiesMenu := true;


#############################################################################
##
#M  Delete(<graph>,<obj>) . . . . . . . . . . . . . remove something in graph
##
##  This operation already exists in {\XGAP} for the graphic objects!
##  Applicable for edges, vertices, classes.
##
##  The following method applies to an edge, given by two vertices. It returns
##  fail if not one of the vertices is maximal in the other and true
##  otherwise. 
InstallOtherMethod( Delete,
    "for a graphic poset, and two vertices",
    true,
    [ IsGraphicPosetRep, IsGPVertex, IsGPVertex ],
    0,

function( poset, v1, v2 )
  local   p,  dummy,  l;
  
  # determine which is the "bigger one":
  p := Position(v2!.maximals,v1);
  if p = fail then
    p := Position(v1!.maximals,v2);
    if p = fail then
      return fail;
    fi;
    # swap the vertices:
    dummy := v1;
    v1 := v2;
    v2 := dummy;
  fi;
  # v1 is now maximal in v2 at position p in v2!.maximals
  
  Disconnect(v1!.obj,v2!.obj);
  l := Length(v2!.maximals);
  v2!.maximals[p] := v2!.maximals[l];
  Unbind(v2!.maximals[l]);
  p := Position(v1!.maximalin,v2);
  # fail is not an option here! If that happens we bomb out!
  l := Length(v1!.maximalin);
  v1!.maximalin[p] := v1!.maximalin[l];
  Unbind(v1!.maximalin[l]);
  
  # think about the menus:
  if GGDeleteModifiesMenu then
    ModifyEnabled(poset,1,Length(poset!.menus));
  fi;
  
  return true;
end);  

##  The following method applies to a vertex. It returns fail if the vertex
##  is not in the poset. The vertex is deleted and all connections to other
##  vertices are also deleted! Returns true if vertex is successfully deleted.
InstallOtherMethod( Delete,
    "for a graphic poset, and a vertex",
    true,
    [ IsGraphicPosetRep, IsGPVertex ],
    0,

function( poset, v )
  local   lp,  l,  cp,  cl,  p,  savemaximals,  savemaximalin,  noerror,  
          v1,  v2,  store;
  
  lp := Position(poset!.levelparams,v!.levelparam);
  if lp = fail then
    return fail;
  fi;
  l := poset!.levels[lp];
  
  cp := Position(l!.classparams,v!.classparam);
  if cp = fail then
    return fail;
  fi;
  cl := l!.classes[cp];
  
  p := Position(cl,v);
  if p = fail then
    return fail;
  fi;
  
  # Remember all connections:
  savemaximals := ShallowCopy(v!.maximals);
  savemaximalin := ShallowCopy(v!.maximalin);
  
  # Delete all connections:
  noerror := true;
  store := GGDeleteModifiesMenu;
  GGDeleteModifiesMenu := false;
  while v!.maximals <> [] do
    if Delete(poset,v,v!.maximals[1]) = fail then
      noerror := fail;
    fi;
  od;
  while v!.maximalin <> [] do
    if Delete(poset,v,v!.maximalin[1]) = fail then
      noerror := fail;
    fi;
  od;
  GGDeleteModifiesMenu := store;
  
  # was it selected?
  RemoveSet(poset!.selectedvertices,v);
  
  # now delete vertex:
  Delete(v!.obj);
  ResetFilterObj(v,IsAlive);
  
  l := Length(cl);
  cl[p] := cl[l];
  Unbind(cl[l]);
  
  # now we have to add new inclusions from the maximal subobjects to those
  # where our vertex was maximal in. We should not do that however, if there is
  # already a way. This ensures that the diagram will be again a Hasse diagram
  # of the remaining vertices with the inclusions induced by the poset
  # before deletion.
  for v1 in savemaximals do
    for v2 in savemaximalin do
      if not GPSearchWay(poset,v2,v1,
                         Position(poset!.levelparams,v1!.levelparam)) then
        Edge(poset,v2,v1);
      fi;
    od;
  od;
        
  # think about the menus:
  if GGDeleteModifiesMenu then
    ModifyEnabled(poset,1,Length(poset!.menus));
  fi;
  
  return noerror;
end);

##  The following method applies to a class. It returns fail if the class
##  is not in the poset. The class is deleted and all vertices including
##  their connections to other vertices are also deleted! Returns true 
##  if class is successfully deleted.
##  The two parameters are a level parameter and a class parameter.
InstallOtherMethod( Delete,
    "for a graphic poset, and two objects",
    true,
    [ IsGraphicPosetRep, IsObject, IsObject ],
    0,

function( poset, levelparam, classparam )
  local   lp,  l,  cp,  noerror,  v,  store;
  
  lp := Position(poset!.levelparams,levelparam);
  if lp = fail then
    return fail;
  fi;
  l := poset!.levels[lp];
  
  cp := Position(l!.classparams,classparam);
  if cp = fail then
    return fail;
  fi;
  
  # delete all vertices:
  noerror := true;
  store := GGDeleteModifiesMenu;
  GGDeleteModifiesMenu := false;
  for v in l!.classes[cp] do
    if Delete(poset,v) = fail then
      noerror := fail;
    fi;
  od;
  GGDeleteModifiesMenu := store;
  
  lp := Length(l!.classes);
  l!.classes[cp] := l!.classes[lp];
  Unbind(l!.classes[lp]);
  l!.classparams[cp] := l!.classparams[lp];
  Unbind(l!.classparams[lp]);
  
  # think about the menus:
  if GGDeleteModifiesMenu then
    ModifyEnabled(poset,1,Length(poset!.menus));
  fi;
    
  return noerror;
end);


#############################################################################
##
#M  DeleteLevel(<poset>,<levelparam>) . . . . . . . . . remove level in poset
##
##  The following method applies to a level. It returns `fail' if no level
##  with level parameter <levelparam> is in the poset. Otherwise the level
##  is deleted and all classes within it are also deleted! `DeleteLevel'
##  returns `true' if the level is successfully deleted.
##
InstallOtherMethod( DeleteLevel,
    "for a graphic poset, and an object",
    true,
    [ IsGraphicPosetRep, IsObject ],
    0,

function( poset, levelparam )
  local   lp,  noerror,  cl,  v,  l,  lev,  store;
  
  lp := Position(poset!.levelparams,levelparam);
  if lp = fail then
    return fail;
  fi;
  
  # delete all vertices:
  noerror := true;
  store := GGDeleteModifiesMenu;
  GGDeleteModifiesMenu := false;
  for cl in poset!.levels[lp]!.classes do
    while cl <> [] do
      if Delete(poset,cl[1]) = fail then
        noerror := fail;
      fi;
    od;
  od;
  GGDeleteModifiesMenu := store;
    
  l := Length(poset!.levels);
  # now we have to move all lower levels up:
  FastUpdate(poset,true);
  for lev in [lp+1..l] do
    poset!.levels[lev]!.top := poset!.levels[lev]!.top 
                               - poset!.levels[lp]!.height;
    for cl in poset!.levels[lev]!.classes do
      for v in cl do
        Move(poset,v,v!.x,v!.y);
      od;
    od;
    if IsAlive(poset!.levelboxes[lev]) then
      MoveDelta(poset!.levelboxes[lev],0,-poset!.levels[lp]!.height);
    fi;
    if IsAlive(poset!.lptexts[lev]) then
      MoveDelta(poset!.lptexts[lev],0,-poset!.levels[lp]!.height);
    fi;
  od;
  FastUpdate(poset,false);
  poset!.levels{[lp..l-1]} := poset!.levels{[lp+1..l]};
  Unbind(poset!.levels[l]);
  poset!.levelparams{[lp..l-1]} := poset!.levelparams{[lp+1..l]};
  Unbind(poset!.levelparams[l]);
  if IsAlive(poset!.levelboxes[lp]) then
    Delete(poset,poset!.levelboxes[lp]);
  fi;
  poset!.levelboxes{[lp..l-1]} := poset!.levelboxes{[lp+1..l]};
  Unbind(poset!.levelboxes[l]);
  if IsAlive(poset!.lptexts[lp]) then
    Delete(poset,poset!.lptexts[lp]);
  fi;
  poset!.lptexts{[lp..l-1]} := poset!.lptexts{[lp+1..l]};
  Unbind(poset!.lptexts[l]);
  
  # think about the menus:
  if GGDeleteModifiesMenu then
    ModifyEnabled(poset,1,Length(poset!.menus));
  fi;

  return noerror;
end);

  
#############################################################################
##
##  Modification methods:
##
#############################################################################


#############################################################################
##
#M  ResizeLevel(<poset>,<levelparam>,<height>)  . . .  change height of level
##
##  Changes the height of a level. The y coordinate can only be changed by
##  permuting levels, see below.
##  Attention: can increase the size of the sheet!
##  Returns fail if no level with parameter levelparam exists and true
##  otherwise. 
##
InstallOtherMethod( ResizeLevel,
    "for a graphic poset, an object, and an integer",
    true,
    [ IsGraphicPosetRep, IsObject, IsInt ],
    0,

function( poset, levelparam, height )
  local   lp,  l,  cl,  v,  dist,  len;
  
  lp := Position(poset!.levelparams,levelparam);
  if lp = fail then
    return fail;
  fi;
  l := poset!.levels[lp];
  
  if height < VERTEX.diameter then
    height := VERTEX.diameter;
  fi;
  
  if height = l!.height then
    return true;
  elif height < l!.height then
    # move all vertices within level into the new range
    FastUpdate(poset,true);
    for cl in l!.classes do
      for v in cl do
        if v!.y > height-VERTEX.radius then
          v!.y := height-VERTEX.radius;
          Move(v!.obj,v!.x,v!.y + l!.top);
        fi;
      od;
    od;
         
    # now move all lower levels up:
    dist := height - l!.height;
    l!.height := height;
    
    # move level box and text:
    if poset!.showlevels then
      Move(poset!.levelboxes[lp],0,l!.top + l!.height - 8);
    fi;
    if poset!.showlevelparams then
      Move(poset!.lptexts[lp],poset!.lptexts[lp]!.x,
           l!.top + QuoInt(l!.height,2));
    fi;
    FastUpdate(poset,false);
    
  else   # height > l!.height
    dist := height - l!.height;
    l!.height := height;
    
    # do we have to increase height of sheet?
    len := Length(poset!.levels);
    if poset!.levels[len]!.top + poset!.levels[len]!.height + dist 
       > poset!.height then
      Resize(poset,poset!.width,
             poset!.levels[len]!.top + poset!.levels[len]!.height + dist);
    fi;
    
    if poset!.showlevels then
      Move(poset!.levelboxes[lp],0,l!.top + l!.height - 8);
    fi;
    if poset!.showlevelparams then
      Move(poset!.lptexts[lp],poset!.lptexts[lp]!.x,
           l!.top + QuoInt(l!.height,2));
    fi;
    
    # next move down all the levels below the increased level:
  fi;
  
  FastUpdate(poset,true);
  for l in [lp+1..Length(poset!.levels)] do
    poset!.levels[l]!.top := poset!.levels[l]!.top + dist;
    for cl in poset!.levels[l]!.classes do
      for v in cl do
        MoveDelta(v!.obj,0,dist);
      od;
    od;
    # move level box:
    if poset!.showlevels then
      MoveDelta(poset!.levelboxes[l],0,dist);
    fi;
    if poset!.showlevelparams then
      MoveDelta(poset!.lptexts[l],0,dist);
    fi;
  od;
  FastUpdate(poset,false);
end);


#############################################################################
##
#M  MoveLevel(<poset>,<levelparam>,<position>) move level to another position
##
##  Moves a level to another position. <position> is an absolute index in
##  the list of levels. The level with parameter <levelparam> will be at the
##  position <position> after the operation. This is only allowed if the
##  new ordering is compatible with the partial order given by CompareLevels
##  and if there is no connection of a vertex in the moving level with 
##  another level with which it is interchanged.
##  So <levelparam> is compared with all levelparams between the old and
##  the new position. If there is a contradiction nothing happens and the
##  method returns fail. If everything works the operation returns true.
##  This operation already exists in {\XGAP} for graphic objects.
##
InstallOtherMethod( MoveLevel,
    "for a graphic poset, an object, and an integer",
    true,
    [ IsGraphicPosetRep, IsObject, IsInt ],
    0,

function( poset, levelparam, position )
  local   lp,  i,  compare,  cl,  v,  v2,  p,  list;
  # nonsense position?
  if position < 1 or position > Length(poset!.levels) then
    return fail;
  fi;
  
  # does level exist?
  lp := Position(poset!.levelparams,levelparam);
  if lp = fail then
    return fail;
  fi;
  
  # nothing to do?
  if position = lp then
    return true;  # we are done
  fi;
  
  if position < lp then   # move level UP
    # check with partial ordering:
    for i in [position..lp-1] do
      compare := CompareLevels(poset,poset!.levelparams[i],levelparam);
      if compare <> fail and compare < 0 then
        # that would contradict the partial order
        return fail;
      fi;
    od;
    
    # now check vertices:
    for cl in poset!.levels[lp]!.classes do
      for v in cl do
        for v2 in v!.maximalin do
          p := Position(poset!.levelparams,v2!.levelparam);
          if p >= position then  # < lp is a MUST!
            return fail;
          fi;
        od;
      od;
    od;
    
    # OK, we can do it:
    FastUpdate(poset,true);
    list := Concatenation([lp],[position..lp-1]);
    poset!.levels{[position..lp]} := poset!.levels{list};
    poset!.levelparams{[position..lp]} := poset!.levelparams{list};
    poset!.levelboxes{[position..lp]} := poset!.levelboxes{list};
    poset!.lptexts{[position..lp]} := poset!.lptexts{list};
    poset!.levels[position]!.top := poset!.levels[position+1]!.top;
    if poset!.showlevels then
      Move(poset!.levelboxes[position],0,poset!.levels[position]!.top 
                                  + poset!.levels[position]!.height - 8);
    fi;
    if poset!.showlevelparams then
      Move(poset!.lptexts[position],poset!.lptexts[position]!.x,
           poset!.levels[position]!.top + 
           QuoInt(poset!.levels[position]!.height,2));
    fi;
    
    for cl in poset!.levels[position]!.classes do
      for v in cl do
        Move(poset,v,v!.x,v!.y);
      od;
    od;
    for i in [position+1..lp] do
      poset!.levels[i]!.top := poset!.levels[i]!.top 
                               + poset!.levels[position]!.height;
      
      if poset!.showlevels then
        Move(poset!.levelboxes[i],0,poset!.levels[i]!.top
                                    + poset!.levels[i]!.height - 8);
      fi;
      if poset!.showlevelparams then
        Move(poset!.lptexts[i],poset!.lptexts[i]!.x,
             poset!.levels[i]!.top + QuoInt(poset!.levels[i]!.height,2));
      fi;
      for cl in poset!.levels[i]!.classes do
        for v in cl do
          Move(poset,v,v!.x,v!.y);
        od;
      od;
    od;
    # in case another one has overwritten our box:
    if poset!.showlevels then
      Draw(poset!.levelboxes[position]);
    fi;
    if poset!.showlevelparams then
      Draw(poset!.lptexts[position]);
    fi;
    FastUpdate(poset,false);
    
    # we did it.
  else   # position > lp, move level DOWN
    # check with partial ordering:
    for i in [lp+1..position] do
      compare := CompareLevels(poset,poset!.levelparams[i],levelparam);
      if compare <> fail and compare > 0 then
        # that would contradict the partial order
        return fail;
      fi;
    od;
    
    # now check vertices:
    for cl in poset!.levels[lp]!.classes do
      for v in cl do
        for v2 in v!.maximals do
          p := Position(poset!.levelparams,v2!.levelparam);
          if p <= position then  # > lp is a MUST!
            return fail;
          fi;
        od;
      od;
    od;
    
    # OK, we can do it:
    FastUpdate(poset,true);
    list := Concatenation([lp+1..position],[lp]);
    poset!.levels{[lp..position]} := poset!.levels{list};
    poset!.levelparams{[lp..position]} := poset!.levelparams{list};
    poset!.levelboxes{[lp..position]} := poset!.levelboxes{list};
    poset!.lptexts{[lp..position]} := poset!.lptexts{list};
    poset!.levels[position]!.top := poset!.levels[position-1]!.top
                                  - poset!.levels[position]!.height
                                    + poset!.levels[position-1]!.height;
    if poset!.showlevels then
      Move(poset!.levelboxes[position],0,poset!.levels[position]!.top
                                       + poset!.levels[position]!.height - 8);
    fi;
    if poset!.showlevelparams then
      Move(poset!.lptexts[position],poset!.lptexts[position]!.x,
           poset!.levels[position]!.top + 
           QuoInt(poset!.levels[position]!.height,2));
    fi;
    for cl in poset!.levels[position]!.classes do
      for v in cl do
        Move(poset,v,v!.x,v!.y);
      od;
    od;
    for i in [lp..position-1] do
      poset!.levels[i]!.top := poset!.levels[i]!.top 
                             - poset!.levels[position]!.height;
      if poset!.showlevels then
        Move(poset!.levelboxes[i],0,poset!.levels[i]!.top
                                           + poset!.levels[i]!.height - 8);
      fi;
      if poset!.showlevelparams then
        Move(poset!.lptexts[i],poset!.lptexts[i]!.x,
             poset!.levels[i]!.top + QuoInt(poset!.levels[i]!.height,2));
      fi;
      for cl in poset!.levels[i]!.classes do
        for v in cl do
          Move(poset,v,v!.x,v!.y);
        od;
      od;
    od;
    # in case another one has overwritten our box:
    if poset!.showlevels then
      Draw(poset!.levelboxes[position]);
    fi;
    if poset!.showlevelparams then
      Draw(poset!.lptexts[position]);
    fi;
    FastUpdate(poset,false);
    
    # we did it.
  fi;
  
  return true;
end);


#############################################################################
##
#M  Relabel(<graph>,<vertex>,<label>)  . . . . . . . . change label of vertex
#M  Relabel(<graph>,<vertex>)  . . . . . . . . . . . . change label of vertex
#M  Relabel(<poset>,<vertex1>,<vertex2>,<label>) . . . . change label of edge
#M  Relabel(<poset>,<vertex1>,<vertex2>) . . . . . . . . change label of edge
##
##  Changes the label of the vertex <vertex> or the edge between <vertex1>
##  and <vertex2>. This must be a short string. In the method where no
##  label is specified the new label is chosen functionally: the operation
##  `ChooseLabel' is called. `Relabel' returns `fail' if an error occurs
##  and `true' otherwise.  This operations already exists in {\XGAP} for
##  graphic objects.
##
InstallOtherMethod( Relabel,
    "for a graphic graph, a vertex, and a string",
    true,
    [ IsGraphicGraphRep, IsGGVertex, IsString ],
    0,

function( graph, vertex, label )
  if label = "" then
    label := false;
  fi;
  # we just call the low level routines:
  vertex!.label := label;
  Relabel(vertex!.obj,label);
end);

InstallOtherMethod( Relabel,
    "for a graphic graph, and a vertex",
    true,
    [ IsGraphicGraphRep, IsGGVertex ],    
    0,
        
function( graph, vertex)
  local label;
  
  label := ChooseLabel( graph, vertex!.data );
  if label = "" then
    label := false;
  fi;
  # we just call the low level routines:
  vertex!.label := label;
  Relabel(vertex!.obj,label);
end);
  
InstallOtherMethod( Relabel,
    "for a graphic poset, two vertices, and a string",
    true,
    [ IsGraphicPosetRep, IsGPVertex, IsGPVertex, IsString ],
    0,

function( poset, v1, v2, label )
  local   p;
  p := Position(v1!.maximals,v2);
  if p = fail then
    p := Position(v2!.maximals,v1);
    if p = fail then
      return fail;
    fi;
  fi;
  # we know now that there is a connection!
  p := Position(v1!.obj!.connections,v2!.obj);
  
  if label = "" then
    label := false;
  fi;
  
  # now we just call the low level routines:
  Relabel(v1!.obj!.connectingLines[p],label);
end);

InstallOtherMethod( Relabel,
    "for a graphic poset, and two vertices",
    true,
    [ IsGraphicPosetRep, IsGPVertex, IsGPVertex ],    
    0,
        
function( poset, v1, v2)
  local   label;
  
  label := ChooseLabel( poset, v1!.data, v2!.data );
  if label = "" then
    label := false;
  fi;
  # we just call the low level routines:
  Relabel(poset,v1,v2,label);
end);
  
  
#############################################################################
##
#M  Move(<graph>,<vertex>,<x>,<y>) . . . . . . . . . . . . . . .  move vertex
#M  Move(<graph>,<vertex>) . . . . . . . . . . . . . . . . . . .  move vertex
##
##  Moves vertex <vertex>. For posets coordinates are relative to the level
##  of the vertex. <vertex> must be a vertex object in <graph>. If no
##  coordinates are specified the operation `ChoosePosition' is
##  called. Returns `fail' if an error occurs and `true' otherwise.  This
##  operations already exists in {\XGAP} for graphic objects.
##
InstallOtherMethod( Move,
    "for a graphic poset, a vertex, and two integers",
    true,
    [ IsGraphicPosetRep, IsGPVertex, IsInt, IsInt ],
    0,

function( poset, vertex, x, y )
  local l;
  
  if x < VERTEX.radius then 
    x := VERTEX.radius;
  elif x > poset!.width-VERTEX.radius then
    x := poset!.width-VERTEX.radius;
  fi;
  l := Position(poset!.levelparams,vertex!.levelparam);
  l := poset!.levels[l];
  if y < VERTEX.radius then
    y := VERTEX.radius;
  elif y > l!.height-VERTEX.radius then
    y := l!.height-VERTEX.radius;
  fi;
  
  vertex!.x := x;
  vertex!.y := y;
  Move(vertex!.obj,x,y+l!.top);
  
  return true;
end);

InstallOtherMethod( Move,
    "for a graphic poset, and a vertex",
    true,
    [ IsGraphicPosetRep, IsGPVertex ],
    0,

function( poset, vertex )
  local position;
  
  position := ChoosePosition(poset, vertex!.data, vertex!.levelparam,
                             vertex!.classparam); 
  Move(poset,vertex,position[1],position[2]);
end);


#############################################################################
##
#M  Reshape(<graph>,<vertex>,<shape>)  . . . . . . . . change shape of vertex
#M  Reshape(<graph>,<vertex>)  . . . . . . . . . . . . change shape of vertex
##
##  Changes the shape of the vertex <vertex>. <vertex> must be a vertex
##  object in the graph or poset <graph>. For the method where no shape is
##  specified the new shape is chosen functionally: `ChooseShape` is called
##  for the corresponding data.  `Reshape' returns `fail' if an error
##  occurs and `true' otherwise.  This operations already exists in {\XGAP}
##  for graphic objects.
##
InstallOtherMethod( Reshape,
    "for a graphic graph, a vertex, and a string",
    true,
    [ IsGraphicGraphRep, IsGGVertex, IsString ],
    0,

function( graph, vertex, shape )
  if shape = "circle" then
    Reshape(vertex!.obj,VERTEX.circle);
  elif shape = "diamond" then
    Reshape(vertex!.obj,VERTEX.diamond);
  else
    Reshape(vertex!.obj,VERTEX.rectangle);
  fi;
  return true;
end);

InstallOtherMethod( Reshape,
    "for a graphic graph, and a vertex",
    true,
    [ IsGraphicGraphRep, IsGGVertex ],
    0,

function( graph, vertex )
  local shape;
  
  shape := ChooseShape( graph, vertex!.data );
  Reshape(graph, vertex, shape);
  return true;
end);


#############################################################################
##
#M  Recolor(<graph>,<vertex>,<color>)  . . . . . . . . change color of vertex
#M  Recolor(<graph>,<vertex>)  . . . . . . . . . . . . change color of vertex
#M  Recolor(<poset>,<vertex1>,<vertex2>,<color>) . .  change color of an edge
#M  Recolor(<poset>,<vertex1>,<vertex2>) . . . . . .  change color of an edge
##
##  Changes the color of the vertex <vertex> or the edge between <vertex1>
##  and <vertex2>. <vertex> must be a vertex object in <graph>. For the
##  method where no color is specified the new color is chosen
##  functionally: `ChooseColor' is called for the corresponding
##  data. `Recolor' returns `fail' if an error occurs and `true'
##  otherwise. This operation already exists in {\XGAP} for graphic objects.
##
InstallOtherMethod( Recolor,
    "for a graphic graph, a vertex, and a color",
    true,
    [ IsGraphicGraphRep, IsGGVertex, IsColor ],
    0,

function( graph, vertex, color )
  Recolor(vertex!.obj,color);
  return true;
end);

InstallOtherMethod( Recolor,
    "for a graphic graph, and a vertex",
    true,
    [ IsGraphicGraphRep, IsGGVertex ],
    0,

function( graph, vertex )
  local color;
  
  color := ChooseColor( graph, vertex!.data );
  Recolor(graph, vertex, color);
  return true;
end);

InstallOtherMethod( Recolor,
    "for a graphic poset, two vertices, and a color",
    true,
    [ IsGraphicPosetRep, IsGPVertex, IsGPVertex, IsColor ],
    0,

function( poset, vertex1, vertex2, color )
  local   p;
  p := Position(vertex1!.maximals,vertex2);
  if p = fail then
    p := Position(vertex2!.maximals,vertex1);
    if p = fail then
      return fail;
    fi;
  fi;
  # we know now that there is a connection!
  p := Position(vertex1!.obj!.connections,vertex2!.obj);
  Recolor(vertex1!.obj!.connectingLines[p],color);
  return true;
end);

InstallOtherMethod( Recolor,
    "for a graphic poset, and two vertices",
    true,
    [ IsGraphicPosetRep, IsGPVertex, IsGPVertex ],
    0,

function( poset, vertex1, vertex2 )
  local   color;
  
  color := ChooseColor( poset, vertex1!.data, vertex2!.data );
  return Recolor(poset, vertex1, vertex2, color);
end);


#############################################################################
##
#M  SetWidth(<graph>,<vertex1>,<vertex2>,<width>) . change line width of edge
#M  SetWidth(<graph>,<vertex1>,<vertex2>) . . . . . change line width of edge
##
##  Changes the line width of an edge. <vertex1> and <vertex2> must be
##  vertices in the graph <graph>. For the method where no line width is
##  specified the width is chosen functionally: `ChooseWidth' is called for
##  the corresponding data pair. Returns `fail' if an error occurs and
##  `true' otherwise. This operation already exists in {\XGAP} for graphic
##  objects.
##
InstallOtherMethod( SetWidth,
    "for a graphic poset, two vertices, and an integer",
    true,
    [ IsGraphicPosetRep, IsGPVertex, IsGPVertex, IsInt ],
    0,

function( poset, vertex1, vertex2, width )
  local   p;
  p := Position(vertex1!.maximals,vertex2);
  if p = fail then
    p := Position(vertex2!.maximals,vertex1);
    if p = fail then
      return fail;
    fi;
  fi;
  # we know now that there is a connection!
  p := Position(vertex1!.obj!.connections,vertex2!.obj);
  SetWidth(vertex1!.obj!.connectingLines[p],width);
  return true;
end);

InstallOtherMethod( SetWidth,
    "for a graphic poset, and two vertices",
    true,
    [ IsGraphicPosetRep, IsGPVertex, IsGPVertex ],
    0,

function( poset, vertex1, vertex2 )
  local   width;
  
  width := ChooseWidth( poset, vertex1!.data, vertex2!.data );
  return SetWidth(poset, vertex1, vertex2, width);
end);


#############################################################################
##
#M  Highlight(<graph>,<vertex>)  . . . . . . . change highlightning of vertex
#M  Highlight(<graph>,<vertex>,<flag>) . . . . change highlightning of vertex
##
##  Changes the highlighting status of the vertex <vertex>. <vertex> must
##  be a vertex object in <graph>. For the method where no flag is
##  specified the new status is chosen functionally: `ChooseHighlight' is
##  called for the corresponding data. Returns `fail' if an error occurs
##  and `true' otherwise. This operation already exists in {\XGAP} for
##  graphic objects.
##
InstallOtherMethod( Highlight,
    "for a graphic graph, a vertex, and a flag",
    true,
    [ IsGraphicGraphRep, IsGGVertex, IsBool ],
    0,

function( graph, vertex, flag )
  Highlight(vertex!.obj,flag);
  return true;
end);

InstallOtherMethod( Highlight,
    "for a graphic graph, and a vertex",
    true,
    [ IsGraphicGraphRep, IsGGVertex ],
    0,

function( graph, vertex )
  local flag;
  
  flag := ChooseHighlight( graph, vertex!.data );
  Highlight(graph, vertex, flag);
  return true;
end);


#############################################################################
##
##  Set this variable temporarily to false if you change many selections!
##
GGSelectModifiesMenu := true;


#############################################################################
##
#M  Select(<graph>,<vertex>,<flag>) . . . . . . . . . . (de-)selects a vertex
#M  Select(<graph>,<vertex>)  . . . . . . . . . . . . . . .  selects a vertex
##
##  Changes the selection state of the vertex <vertex>. <vertex> must be a
##  vertex object in <graph>. The flag determines whether the vertex
##  should be selected or deselected. This operation already exists in
##  {\XGAP} for graphic objects.  The method without flags assumes `true'.
##
InstallOtherMethod( Select,
    "for a graphic graph, a vertex, and a flag",
    true,
    [ IsGraphicGraphRep, IsGGVertex, IsBool ],
    0,
      
function(graph,vertex,flag)
  local   p,  l;
  p := PositionSet(graph!.selectedvertices,vertex);
  if flag then
    if p <> fail then  
      return;
    fi;
    Highlight(graph,vertex,true);
    Recolor(graph,vertex,graph!.color.selected);
    AddSet(graph!.selectedvertices,vertex);
  else
    if p = fail then
      return;
    fi;
    Highlight(graph,vertex,false);
    Recolor(graph,vertex,graph!.color.unselected);
    RemoveSet(graph!.selectedvertices,vertex);
  fi;
  if GGSelectModifiesMenu then
    ModifyEnabled(graph,1,Length(graph!.menus));
  fi;
  return;
end);

InstallOtherMethod( Select,
    "for a graphic graph, and a vertex",
    true,
    [ IsGraphicGraphRep, IsGGVertex ],
    0,
      
function(graph,vertex)
  Select(graph,vertex,true);
end);  


#############################################################################
##
#M  DeselectAll(<graph>) . . . . . . . . . . . . . . . deselects all vertices
##
##  Deselects all vertices in graph.
##
InstallOtherMethod( DeselectAll,
    "for a graphic graph",
    true,
    [ IsGraphicGraphRep ],
    0,
      
function(graph)
  local   v;
  for v in graph!.selectedvertices do
    Highlight(graph,v,false);
    Recolor(graph,v,graph!.color.unselected);
  od;
  graph!.selectedvertices := [];
end);


#############################################################################
##
#M  Selected(<graph>) . . . . . . . . .  returns set of all selected vertices
##
##  Returns a (shallow-)copy of the set of all selected vertices.
##
InstallOtherMethod( Selected,
    "for a graphic graph",
    true,
    [ IsGraphicGraphRep ],
    0,
      
function(graph)
  return ShallowCopy(graph!.selectedvertices);
end);


#############################################################################
##
##  Methods for functional decisions:
##
#############################################################################


#############################################################################
##
#M  CompareLevels(<poset>,<levelp1>,<levelp2>) . . . compares two levelparams
##
##  Compare two levelparams. -1 means that levelp1 is "higher", 1 means
##  that levelp2 is "higher", 0 means that they are equal. fail means that
##  they are not comparable. This method is for the case if level
##  parameters are integers and lower values mean higher levels like in the
##  case of group lattices and subgroup indices.
##
InstallMethod( CompareLevels,
    "for a graphic poset, and two integers",
    true,
    [ IsGraphicPosetRep, IsInt, IsInt ],
    0,

function( poset, l1, l2 )
  if l1 < l2 then
    return -1;
  elif l1 > l2 then
    return 1;
  else
    return 0;
  fi;
end);


#############################################################################
##
#M  ChooseLabel(<graph>,<data>) . . . . . . . is called while vertex creation
#M  ChooseLabel(<graph>,<data>,<data>)  . . . . is called while edge creation
##
##  This operation is called while vertex or edge creation, if the caller 
##  didn't specify a label for the vertex or edge. It has to return a short 
##  string which will be attached to the vertex. If it returns fail the new 
##  vertex is not generated! This method just returns the empty string, so 
##  no label is generated.
##  This method is also called in the Relabel method without label parameter.
##
InstallMethod( ChooseLabel,
    "for a graphic graph, and an object",
    true,
    [ IsGraphicGraphRep, IsObject ],
    0,

function( graph, data )
  return "";
end);

InstallOtherMethod( ChooseLabel,
    "for a graphic graph, and two objects",
    true,
    [ IsGraphicGraphRep, IsObject, IsObject ],
    0,

function( poset, data1, data2 )
  return "";
end);


#############################################################################
##
#M  ChooseLevel(<poset>,<data>) . . . . . . . is called while vertex creation
##
##  This operation is called while vertex creation, if the caller didn't
##  specify a level where the vertex belongs to. It has to return a
##  levelparam which exists in the poset. If it returns fail the new vertex
##  is not generated!
##  This method just chooses the last, lowest level or fail, if there is no 
##  level in the poset.
##
InstallMethod( ChooseLevel,
    "for a graphic poset, and an object",
    true,
    [ IsGraphicPosetRep, IsObject ],
    0,

function( poset, data )
  local l;
  l := Length(poset!.levelparams);
  if l > 0 then
    return poset!.levelparams[Length(poset!.levelparams)];
  else
    return fail;
  fi;
end);


#############################################################################
##
#M  ChooseClass(<poset>,<data>,<levelp>) . .  is called while vertex creation
##
##  This operation is called while vertex creation, if the caller didn't
##  specify a class where the vertex belongs to. It has to return a
##  classparam which exists in the poset in levelp. If it returns fail the
##  new vertex is not generated!
##  This method just generates a new class in the level with classparam one 
##  bigger than the maximum of all (integer) classparams. It returns fail if
##  this maximum is no integer.
##
InstallMethod( ChooseClass,
    "for a graphic graph, and two objects",
    true,
    [ IsGraphicPosetRep, IsObject, IsObject ],
    0,

function( poset, data, levelparam )
  local l,m;
  
  l := Position(poset!.levelparams,levelparam);
  if l = fail then 
    return fail;
  fi;
  l := poset!.levels[l];
  
  if l!.classparams = [] then
    return CreateClass(poset,levelparam,1);
  fi;
    
  m := Maximum(l!.classparams);
  if not IsInt(m) then
    return fail;
  fi;
  
  return CreateClass(poset,levelparam,m+1);
end);
  

#############################################################################
##
#M  ChooseShape(<graph>,<data>) . . . . . . . is called while vertex creation
##
##  This operation is called while vertex creation.
##  It has to return a string out of the following list:
##  "circle", "diamond", "rectangle"
##  If it returns fail the new vertex is not generated!
##  This method just returns "circle".
##
InstallMethod( ChooseShape,
    "for a graphic graph, and an object",
    true,
    [ IsGraphicGraphRep, IsObject ],
    0,

function( graph, data )
  return "circle";
end);


#############################################################################
##
#M  ChooseWidth(<graph>,<data>) . . . . . . . is called while vertex creation
#M  ChooseWidth(<graph>,<data1>,<data2>)  . . . is called while edge creation
##
##  This operation is called while vertex or edge creation.
##  It has to return a line width.
##  If it returns fail the new vertex or edge is not generated!
##  This is also called by the SetWidth operation without width parameter.
##  This method just returns 1.
##
InstallOtherMethod( ChooseWidth,
    "for a graphic graph, and an object",
    true,
    [ IsGraphicGraphRep, IsObject ],
    0,

function( graph, data )
  return 1;
end);

InstallOtherMethod( ChooseWidth,
    "for a graphic graph, and two objects",
    true,
    [ IsGraphicGraphRep, IsObject, IsObject ],
    0,

function( graph, data1, data2 )
  return 1;
end);


#############################################################################
##
#M  ChooseColor(<graph>,<data>) . . . . . . . is called while vertex creation
#M  ChooseColor(<graph>,<data1>,<data2>). . . . is called while edge creation
##
##  This operation is called while vertex or edge creation. It has to return a
##  color. If it returns fail the new vertex is not generated!
##  It is also called in the Recolor method without color parameter.
##  This method just returns black.
##
InstallMethod( ChooseColor,
    "for a graphic graph, and an object",
    true,
    [ IsGraphicGraphRep, IsObject ],
    0,

function( graph, data )
  return COLORS.black;
end);

InstallOtherMethod( ChooseColor,
    "for a graphic graph, and two objects",
    true,
    [ IsGraphicGraphRep, IsObject, IsObject ],
    0,

function( graph, data1, data2 )
  return COLORS.black;
end);


#############################################################################
##
#M  ChooseHighlight(<graph>,<data>) . . . . . is called while vertex creation
##
##  This operation is called while vertex creation. It has to return a
##  flag which indicates, whether the vertex is highlighted or not. If it 
##  returns fail the new vertex is not generated!
##  It is also called in the Highlight method without flag parameter.
##
##  The following method just returns false.
InstallMethod( ChooseHighlight,
    "for a graphic graph, and an object",
    true,
    [ IsGraphicGraphRep, IsObject ],
    0,

function( graph, data )
  return false;
end);


#############################################################################
##
#M  ChoosePosition(<poset>,<data>,<level>,<class>)  . . . . . . . . . . . . . 
#M  ChoosePosition(<graph>,<data>)  . . . . . is called while vertex creation
##
##  This operation is called while vertex creation.  It has to return a
##  list with two integers: the coordinates. For posets those are relative
##  to the level the vertex resides in.  If it returns fail the new vertex
##  is not generated!  
##  This method positions a new vertex in a nonempty class next to the last
##  member in the class and a new vertex in a new class halfway to the
##  right end of the sheet from the rightmost vertex in the level or
##  halfway to the left end of the sheet from the leftmost vertex in the
##  class, depending where there is more space.
##
InstallMethod( ChoosePosition,
    "for a graphic poset, an object, a level object, a list, and a list",
    true,
    [ IsGraphicPosetRep, IsObject, IsGPLevel, IsList, IsList ],
    0,

function( poset, data, level, class, hints )
  local   position,  ranges,  cl,  gaps,  maxindex,  i;
  
  position := [];
  # not first in class:
  if class <> [] then
    # just near the others in the class:
    position[2] := class[Length(class)]!.y;
    position[1] := class[Length(class)]!.x + VERTEX.diameter + 2;
  else
    # collect all x ranges where classes reside:
    ranges := [[0,0]];
    for cl in level!.classes do
      if cl <> [] then
        Add(ranges,[cl[1]!.x-VERTEX.radius,cl[Length(cl)]!.x+VERTEX.radius]);
      fi;
    od;
    Add(ranges,[poset!.width,poset!.width]);
    ranges := Set(ranges);
    gaps := List([1..Length(ranges)-1],x->ranges[x+1][1]-ranges[x][2]);
    
    # search largest gap:
    maxindex := 1;
    for i in [2..Length(gaps)] do
      if gaps[i] > gaps[maxindex] then
        maxindex := i;
      fi;
    od;
    
    position[1] := QuoInt(ranges[maxindex][2]+ranges[maxindex+1][1],2);
    position[2] := QuoInt(level!.height,2);
  fi;
  return position;
end);



#############################################################################
##
##  Methods for getting information:
##
#############################################################################


#############################################################################
##
#M  WhichLevel(<poset>,<y>) . . . . . .  determine level in which position is
##
##  Determines level in which position is. Returns levelparam or fail.
##
InstallMethod( WhichLevel,
    "for a graphic poset, and an integer",
    true,
    [ IsGraphicPosetRep, IsInt ],
    0,

function( poset, y )
  local left, right, look;
  
  if poset!.levels = [] or y < 0 or y >= poset!.height then
    return fail;
  fi;
  
  # we do a binary search:
  left := 1;
  right := Length(poset!.levels);
  while left <= right do
    look := QuoInt(left+right,2);
    if y < poset!.levels[look]!.top then
      right := look-1;
    elif y >= poset!.levels[look]!.top + poset!.levels[look]!.height then
      left := look+1;
    else
      return poset!.levelparams[look];
    fi;
  od;
  
  return fail;
end);


#############################################################################
##
#M  WhichClass(<poset>,<x>,<y>) . . . .  determine class in which position is
##
##  Determines a class with a vertex which contains the position. The first
##  class found is taken.  Returns list with levelparam as first and
##  classparam as second element.  Returns fail if no such class is found.
##
InstallMethod( WhichClass,
    "for a graphic poset, and two integers",
    true,
    [ IsGraphicPosetRep, IsInt, IsInt ],
    0,
        
function(poset, x, y)
  local   lp,  l,  cl,  v;
  
  # first determine the level:
  lp := WhichLevel(poset,y);
  l := Position(poset!.levelparam,l);
  l := poset!.levels[l];
  
  # now search classes:
  for cl in [1..Length(l!.classes)] do
    for v in l!.classes[cl] do
      if [x,y] in v!.obj then
        return [lp,l!.classparams[cl]];
      fi;
    od;
  od;
  
  return fail;
end);


#############################################################################
##
#M  WhichVertex(<graph>,<x>,<y>) . . .  determine vertex in which position is
#M  WhichVertex(<graph>,<data>)  . . . . .  determine vertex with data <data>
#M  WhichVertex(<graph>,<data>,<func>)   . . .  determine vertex functionally
##
##  Determines a vertex which contains the position.  Returns vertex.
##  In the third form the function func must take two parameters "data" and
##  the data entry of a vertex in question. It must return true or false, 
##  according to the right vertex being found or not.
##  The function can for example consider just one record component of
##  data records.
##  Returns fail in case no vertex is found.
##
InstallOtherMethod( WhichVertex,
    "for a graphic poset, and two integers",
    true,
    [ IsGraphicPosetRep, IsInt, IsInt ],
    0,
        
function(poset, x, y)
  local   lp,  l,  cl,  v;
  
  # first determine the level:
  lp := WhichLevel(poset,y);
  l := Position(poset!.levelparams,lp);
  if l = fail then
    return fail;    # not even within a level
  fi;
  l := poset!.levels[l];
  
  # now search classes:
  for cl in [1..Length(l!.classes)] do
    for v in l!.classes[cl] do
      if [x,y] in v!.obj then
        return v;
      fi;
    od;
  od;
  
  return fail;
end);

##  Method for a data object with comparison function:
##
InstallOtherMethod( WhichVertex,
    "for a graphic poset, an object, and a function",
    true,
    [ IsGraphicPosetRep, IsObject, IsFunction ],
    0,
        
function(poset, data, func)
  
  local   lp,  l,  cl,  v;
  
  for lp in [1..Length(poset!.levels)] do
    l := poset!.levels[lp];
    for cl in [1..Length(l!.classes)] do
      for v in l!.classes[cl] do
        if func(data,v!.data) then
          return v;
        fi;
      od;
    od;
  od;
  
  return fail;
end);

##  Method for a data object:
##
InstallOtherMethod( WhichVertex,
    "for a graphic poset, and an object",
    true,
    [ IsGraphicPosetRep, IsObject ],
    0,
        
function(poset, data)
  
  local   lp,  l,  cl,  v;
  
  for lp in [1..Length(poset!.levels)] do
    l := poset!.levels[lp];
    for cl in [1..Length(l!.classes)] do
      for v in l!.classes[cl] do
        if v!.data = data then
          return v;
        fi;
      od;
    od;
  od;
  
  return fail;
end);


#############################################################################
##
#M  WhichVertices(<graph>,<x>,<y>) .  determine vertices in which position is
#M  WhichVertices(<graph>,<data>)  . . .  determine vertices with data <data>
#M  WhichVertices(<graph>,<data>,<func>) . .  determine vertices functionally
##
##  Determines the list of vertices which contain the position. Returns list.
##  In the third form the function func must take two parameters "data" and
##  the data entry of a vertex in question. It must return true or false, 
##  according to the vertex belonging into the result or not.
##  The function can for example consider just one record component of
##  data records.
##  Returns the empty list in case no vertex is found.
##
InstallMethod( WhichVertices,
    "for a graphic poset, and two integers",
    true,
    [ IsGraphicPosetRep, IsInt, IsInt ],
    0,
        
function(poset, x, y)
  local   lp,  l,  cl,  v, res;
  
  # first determine the level:
  lp := WhichLevel(poset,y);
  l := Position(poset!.levelparams,lp);
  if l = fail then
    return fail;    # not even within a level
  fi;
  l := poset!.levels[l];
  
  res := [];
  # now search classes:
  for cl in [1..Length(l!.classes)] do
    for v in l!.classes[cl] do
      if [x,y] in v!.obj then
        Add(res,v);
      fi;
    od;
  od;
  
  return res;
end);

##  Method for a data object with comparison function:
##
InstallOtherMethod( WhichVertices,
    "for a graphic poset, an object, and a function",
    true,
    [ IsGraphicPosetRep, IsObject, IsFunction ],
    0,
        
function(poset, data, func)
  
  local   lp,  l,  cl,  v,  res;
  
  res := [];
  for lp in [1..Length(poset!.levels)] do
    l := poset!.levels[lp];
    for cl in [1..Length(l!.classes)] do
      for v in l!.classes[cl] do
        if func(data, v!.data) then
          Add(res,v);
        fi;
      od;
    od;
  od;
  
  return res;
end);

##  Method for a data object:
##
InstallOtherMethod( WhichVertices,
    "for a graphic poset, and an object",
    true,
    [ IsGraphicPosetRep, IsObject ],
    0,
        
function(poset, data)
  
  local   lp,  l,  cl,  v,  res;
  
  res := [];
  for lp in [1..Length(poset!.levels)] do
    l := poset!.levels[lp];
    for cl in [1..Length(l!.classes)] do
      for v in l!.classes[cl] do
        if v!.data = data then
          Add(res,v);
        fi;
      od;
    od;
  od;
  
  return res;
end);


#############################################################################
##
#M  Levels(<poset>) . . . . . . . . . . . . . returns the list of levelparams
##
##  Returns the list of levelparams in descending order meaning highest to
##  lowest. 
##
InstallMethod( Levels,
    "for a graphic poset",
    true,
    [ IsGraphicPosetRep ],
    0,
        
function(poset)
  return poset!.levelparams;
end);


#############################################################################
##
#M  Classes(<poset>,<levelparam>) . . . . . . returns the list of classparams
##
##  Returns the list of classparams in level levelparam. Returns fail if no
##  level with parameter <levelparam> occurs.
##
InstallMethod( Classes,
    "for a graphic poset, and an object",
    true,
    [ IsGraphicPosetRep, IsObject ],
    0,
        
function(poset, levelparam)
  local l;
  
  l := Position(poset!.levelparams,levelparam);
  if l = fail then
    return fail;
  fi;
  l := poset!.levels[l];
  return l!.classparams;
end);


#############################################################################
##
#M  Vertices(<poset>,<levelparam>,<classparam>)  . . . . . . returns vertices
##
##  Returns the list of vertices in class classparams in level
##  levelparam. Returns fail no level with paramter <levelparam> or no
##  class with parameter <classparam> in the level.
##
InstallMethod( Vertices,
    "for a graphic poset, and two objects",
    true,
    [ IsGraphicPosetRep, IsObject, IsObject ],
    0,
        
function(poset, levelparam, classparam)
  local l, cl;
  
  l := Position(poset!.levelparams,levelparam);
  if l = fail then
    return fail;
  fi;
  l := poset!.levels[l];
  
  cl := Position(l!.classparams,classparam);
  if cl = fail then
    return fail;
  else
    return l!.classes[cl];
  fi;
end);


#############################################################################
##
#M  Maximals(<poset>,<vertex>) . . . . . . . . .  returns maximal subvertices
##
##  Returns the list of maximal subvertices in <vertex>. Returns fail if an
##  error occurs.
##
InstallMethod( Maximals,
    "for a graphic poset, and an object",
    true,
    [ IsGraphicPosetRep, IsObject ],
    0,
        
function(poset, vertex)
  return vertex!.maximals;
end);


#############################################################################
##
#M  MaximalIn(<poset>,<vertex>) . .  returns vertices, in which v. is maximal
##
##  Returns the list of vertices, in which <vertex> is maximal.  Returns
##  fail if an error occurs.
##
InstallMethod( MaximalIn,
    "for a graphic poset, and an object",
    true,
    [ IsGraphicPosetRep, IsObject ],
    0,
        
function(poset, vertex)
  return vertex!.maximalin;
end);


#############################################################################
##
#M  PositionLevel(<poset>,<levelparam>) . . . . . returns y position of level 
##
##  Returns the y position of the level relative to the graphic
##  sheet and the height. Returns fail if no level with parameter 
##  <levelparam> exists.
##
InstallMethod( PositionLevel,
    "for a graphic poset, and an object",
    true,
    [ IsGraphicPosetRep, IsObject ],
    0,
        
function(poset, levelparam)
  local l;
  
  l := Position(poset!.levelparams,levelparam);
  if l = fail then
    return fail;
  fi;
  return [poset!.levels[l]!.top,poset!.levels[l]!.height];
end);



#############################################################################
##
##  Methods for menus and mouseclicks:
##
#############################################################################


#############################################################################
##
#M  InstallPopup(<graph>,<func>) . install function for right click on vertex
##
##  Installs a function that is called if the user clicks with the right
##  button on a vertex. The function gets as parameters:
##   poset,vertex,x,y        (click position)
##
InstallMethod( InstallPopup,
    "for a graphic graph, and a function",
    true,
    [ IsGraphicGraphRep, IsFunction ],
    0,
        
function(graph, func)
  graph!.rightclickfunction := func;
end);


#############################################################################
##
#M  Menu(<graph>,<title>,<entrylist>,<typelist>,<functionslist>) . . new menu
##
##  This operation already exists in {\XGAP} for GraphicSheets.
##  Builts a new Menu but with information about the type of the menu entry.
##  This information describes the relation between the selection state of
##  the vertices and the parameters supplied to the functions. The following 
##  types are supported:
##    "forany"    : always enabled, generic routines don't change anything
##    "forone"    : enabled iff exactly one vertex is selected
##    "fortwo"    : enabled iff exactly two vertices are selected
##    "forthree"  : enabled iff exactly three vertices are selected
##    "forsubset" : enabled iff at least one vertex is selected
##    "foredge"   : enabled iff a connected pair of two vertices is selected
##    "formin2"   : enabled iff at least two vertices are selected
##    "formin3"   : enabled iff at least three vertices are selected
##  The IsMenu object is returned. It is also stored in the sheet.
InstallOtherMethod( Menu,
    "for a graphic graph, a string, a list of strings, a list of strings, and a list of functions",
    true,
    [ IsGraphicGraphRep, IsString, IsList, IsList, IsList ],
    0,
        
function(graph, title, entrylist, typelist, functionslist)
  local   l,  menu,  nr;
  
  l := Filtered([1..Length(entrylist)],
                x->IsBound(entrylist[x]) and (entrylist[x][1] <> '-'));
  menu := Menu(graph,title,entrylist,functionslist);
  Add(graph!.menutypes,typelist{l});
  Add(graph!.menuenabled,List(l,x->true));
  nr := Length(graph!.menuenabled);
  
  ModifyEnabled(graph,nr,nr);
  
  return graph!.menus[nr];
end);


#############################################################################
##
#M  ModifyEnabled(<graph>,<from>,<to>) , . .  modifies enablednes of entries
##
##  Modifies the "Enabledness" of menu entries according to their type and
##  number of selected vertices. <from> is the first menu to work on and
##  <to> the last one (indices). Only IsAlive menus are considered. Returns 
##  nothing.
##  There are two different methods for graphs and posets:  
##
InstallMethod( ModifyEnabled,
    "for a graph, and two integers",
    true,
    [ IsGraphicGraphRep, IsInt, IsInt ],
    0,
        
function(graph, from, to)
  local   len,  i,  j,  flag;
  
  len := Length(graph!.selectedvertices);
  for i in [from..to] do
    if IsAlive(graph!.menus[i]) then
      for j in [1..Length(graph!.menutypes[i])] do
        if graph!.menutypes[i][j] = "forone" then
          flag := len = 1;
        elif graph!.menutypes[i][j] = "fortwo" then
          flag := len = 2;
        elif graph!.menutypes[i][j] = "forthree" then
          flag := len = 3;
        elif graph!.menutypes[i][j] = "forsubset" then
          flag := len >= 1;
        elif graph!.menutypes[i][j] = "foredge" then
          flag := false;
          if len = 2 then
            if Position(graph!.edges,graph!.selectedvertices) <> fail or
               Position(graph!.edges,Reversed(graph!.selectedvertices))
               <> fail then
              flag := true;
            fi;
          fi;
        elif graph!.menutypes[i][j] = "formin2" then
          flag := len >= 2;
        elif graph!.menutypes[i][j] = "formin3" then
          flag := len >= 3;
        else
          flag := true;
        fi;
        if graph!.menuenabled[i][j] <> flag then
          graph!.menuenabled[i][j] := flag;
          Enable(graph!.menus[i]!.entries[j],flag);
        fi;
      od;
    fi;
  od;
end);
  
## Here follows nearly the same but: selected edges are different!
InstallMethod( ModifyEnabled,
    "for a poset, and two integers",
    true,
    [ IsGraphicPosetRep, IsInt, IsInt ],
    0,
        
function(poset, from, to)
  local   len,  i,  j,  flag;
  
  len := Length(poset!.selectedvertices);
  for i in [from..to] do
    if IsAlive(poset!.menus[i]) then
      for j in [1..Length(poset!.menutypes[i])] do
        if poset!.menutypes[i][j] = "forone" then
          flag := len = 1;
        elif poset!.menutypes[i][j] = "fortwo" then
          flag := len = 2;
        elif poset!.menutypes[i][j] = "forthree" then
          flag := len = 3;
        elif poset!.menutypes[i][j] = "forsubset" then
          flag := len >= 1;
        elif poset!.menutypes[i][j] = "foredge" then
          flag := false;
          if len = 2 then
            if Position(poset!.selectedvertices[1]!.maximals,
                        poset!.selectedvertices[2]) <> fail or
               Position(poset!.selectedvertices[2]!.maximals,
                        poset!.selectedvertices[1]) <> fail then
              flag := true;
            fi;
          fi;
        elif poset!.menutypes[i][j] = "formin2" then
          flag := len >= 2;
        elif poset!.menutypes[i][j] = "formin3" then
          flag := len >= 3;
        else   # "forany"
          flag := true;
        fi;
        if poset!.menuenabled[i][j] <> flag then
          poset!.menuenabled[i][j] := flag;
          Enable(poset!.menus[i],poset!.menus[i]!.entries[j],flag);
        fi;
      od;
    fi;
  od;
end);
  

#############################################################################
##
##  Methods for actual user interaction:
##
#############################################################################


#############################################################################
##
#M  PosetLeftClick(poset,x,y) . . . . method which is called after left click
##
##  This method is called when the user does a left click in a poset. It lets
##  the user move, select and deselect vertices or edges.
##  Edges are selected as pair of vertices.
##
InstallMethod(PosetLeftClick,
    "for a graph, and two integers",
    true,
    [ IsGraphicGraphRep, IsInt, IsInt ],
    0,

function(poset,x,y)
  
  local   v,  lp,  lev,  cp,  cl,  list,  minx,  maxx,  storex,  storey,  v2,  
          lno,  line,  limit,  box,  bx,  bw,  by,  bh;
  
  # is this a click on a vertex?
  v := WhichVertex(poset,x,y);
  if v <> fail then
    
    # yes! search for level:
    lp := v!.levelparam;
    lev := poset!.levels[Position(poset!.levelparams,lp)];
    
    # now we search for the class:
    cp := v!.classparam;
    cl := lev!.classes[Position(lev!.classparams,cp)];
    
    # we search for minimum and maximum x coordinates, rel. to mouse:
    list := List(cl,v->v!.x);
    minx := Minimum(list) - x;
    maxx := Maximum(list) - x;
    
    storex := v!.x;
    storey := v!.y;
    
    if Drag(poset,x,y,BUTTONS.left,
            function(x,y) 
              if x + minx < VERTEX.radius then 
                x := VERTEX.radius - minx; 
              elif x + maxx > poset!.width-VERTEX.radius then
                x := poset!.width-VERTEX.radius-maxx;
              fi;
              if y < lev!.top+VERTEX.radius then 
                y := lev!.top + VERTEX.radius; 
              elif y > lev!.top+lev!.height-VERTEX.radius then
                y := lev!.top+lev!.height-VERTEX.radius;
              fi;
	      Move(poset,v,x,y-lev!.top); 
            end) then
      for v2 in cl do
        if v <> v2 then
          Move(poset,v2,v2!.x + v!.x - storex,v2!.y + v!.y - storey);
        fi;
      od;
      # better we redraw:
      DoRedraw(poset);
    else
      DeselectAll(poset);
      Select(poset,v,true);
    fi;
  else  # no click on a vertex, so we drag a box:
    # if this is a poset then we check if somebody clicked on a level box:
    if IsGraphicPosetRep(poset) then
      if poset!.showlevels and x < 8 then
        lno := First([1..Length(poset!.levelboxes)],
                     i->([x,y] in poset!.levelboxes[i]));
        if lno <> fail then
          # user clicked on the levelbox no lno, he can now resize this level
          line := Line(poset,0,y,poset!.width,0);
          if COLORS.blue <> false then
            Recolor(line,COLORS.blue);
          fi;
          limit := poset!.levels[lno]!.top + VERTEX.diameter;
          if Drag(poset,x,y,BUTTONS.left,
                  function(x,y)
                    if y < limit then
                      y := limit;
                    fi;
                    Move(line,0,y);
                  end) then
            # the user moved the line! the new y coordinate is the new lower
            # limit of the level!      
            Delete(poset,line);
            ResizeLevel(poset,poset!.levelparams[lno],line!.y
                              - poset!.levels[lno]!.top);
          else
            Delete(poset,line);
          fi;
          return;
        fi;
      fi;
    fi;
    storex := x;
    storey := y;
    box := Rectangle(poset,x,y,0,0);
    if Drag(poset,x,y,BUTTONS.left,
            function(x,y)
              local bx,by,bw,bh;
              if x < storex then
                bx := x;
                bw := storex - x;
              else
                bx := storex;
                bw := x - storex;
              fi;
              if y < storey then
                by := y;
                bh := storey - y;
              else
                by := storey;
                bh := y - storey;
              fi;
              if bx <> box!.x or by <> box!.y then
                Move(box,bx,by);
              fi;
              if bw <> box!.w or bh <> box!.h then
                Reshape(box,bw,bh);
              fi;
            end) then
      # the box had at one time at least a certain size
      if box!.w > 0 and box!.h > 0 then
        DeselectAll(poset);
        GGSelectModifiesMenu := false;
        for lev in poset!.levels do
          if lev!.top < box!.y+box!.h and 
             lev!.top + lev!.height >= box!.y then
            for cl in lev!.classes do
              for v in cl do
                if [v!.x,v!.y+lev!.top] in box then
                  Select(poset,v,true);
                fi;
              od;
            od;
          fi;
        od;
        GGSelectModifiesMenu := true;
        ModifyEnabled(poset,1,Length(poset!.menus));
      fi;
      Delete(poset,box);
      # better we redraw:
      DoRedraw(poset);
    else  # no moving, so user wants to deselect all vertices
      DeselectAll(poset);
      ModifyEnabled(poset,1,Length(poset!.menus));
      Delete(poset,box);
    fi;   # Drag(...) --> true
  fi;
end);


#############################################################################
##
#M  PosetCtrlLeftClick(poset,x,y) . . method which is called after left click
##
##  This operation is called when the user does a left click in a poset while
##  holding down the control key. It lets the user move, select and deselect
##  vertices or edges. The difference to the operation without the control
##  key is, that while selecting the old vertices are NOT deselected.
##  Moving does not move the whole class but only one vertex. This allows
##  for permuting the vertices within a class.
##  Edges are selected as pair of vertices.
##
InstallMethod(PosetCtrlLeftClick,
    "for a graph, and two integers",
    true,
    [ IsGraphicGraphRep, IsInt, IsInt ],
    0,

function(poset,x,y)
  
  local   v,  lp,  lev,  cp,  cl,  storex,  storey,  lno,  box,  levellen,  
          pos,  bx,  bw,  by,  bh;
  
  # is this a click on a vertex?
  v := WhichVertex(poset,x,y);
  if v <> fail then
    
    # yes! search for level:
    lp := v!.levelparam;
    lev := poset!.levels[Position(poset!.levelparams,lp)];
    
    # now we search for the class:
    cp := v!.classparam;
    cl := lev!.classes[Position(lev!.classparams,cp)];
    
    storex := v!.x;
    storey := v!.y;
    
    if not Drag(poset,x,y,BUTTONS.left,
            function(x,y) 
              if x < VERTEX.radius then 
                x := VERTEX.radius;
              elif x > poset!.width-VERTEX.radius then
                x := poset!.width-VERTEX.radius;
              fi;
              if y < lev!.top+VERTEX.radius then 
                y := lev!.top + VERTEX.radius; 
              elif y > lev!.top+lev!.height-VERTEX.radius then
                y := lev!.top+lev!.height-VERTEX.radius;
              fi;
	      Move(poset,v,x,y-lev!.top); 
            end) then
      Select(poset,v,PositionSet(poset!.selectedvertices,v) = fail);
    else
      # better we redraw:
      DoRedraw(poset);
    fi;
  else  # no click on a vertex, so we drag a box:
    # if this is a poset then we check if somebody clicked on a level box:
    if IsGraphicPosetRep(poset) then
      if poset!.showlevels and x < 8 then
        lno := First([1..Length(poset!.levelboxes)],
                     i->([x,y] in poset!.levelboxes[i]));
        if lno <> fail then
          # user clicked on the levelbox no lno, he can now move this level
          box := Box(poset,4,y-8,8,8);
          if COLORS.red <> false then
            Recolor(box,COLORS.red);
          fi;
          levellen := Length(poset!.levels);
          if Drag(poset,x,y,BUTTONS.left,
                  function(x,y)
                    if y < 8 then
                      y := 8;
                    elif y > poset!.levels[levellen]!.top
                             + poset!.levels[levellen]!.height then
                      y := poset!.levels[levellen]!.top
                           + poset!.levels[levellen]!.height;
                    fi;
                    Move(box,4,y-8);
                  end) then
            # the user moved the box! we have to search in which level lies
            # the new y coordinate:
            pos := First([levellen,levellen-1..1],
                         i->box!.y >= poset!.levels[i]!.top);
            MoveLevel(poset,poset!.levelparams[lno],pos);
          fi;
          Delete(poset,box);
          return;
        fi;
      fi;
    fi;
    storex := x;
    storey := y;
    box := Rectangle(poset,x,y,0,0);
    if Drag(poset,x,y,BUTTONS.left,
            function(x,y)
              local bx,by,bw,bh;
              if x < storex then
                bx := x;
                bw := storex - x;
              else
                bx := storex;
                bw := x - storex;
              fi;
              if y < storey then
                by := y;
                bh := storey - y;
              else
                by := storey;
                bh := y - storey;
              fi;
              if bx <> box!.x or by <> box!.y then
                Move(box,bx,by);
              fi;
              if bw <> box!.w or bh <> box!.h then
                Reshape(box,bw,bh);
              fi;
            end) then
      # the box had at one time at least a certain size
      if box!.w > 0 and box!.h > 0 then
        GGSelectModifiesMenu := false;
        for lev in poset!.levels do
          if lev!.top < box!.y+box!.h and 
             lev!.top + lev!.height >= box!.y then
            for cl in lev!.classes do
              for v in cl do
                if [v!.x,v!.y+lev!.top] in box then
                  Select(poset,v,true);
                fi;
              od;
            od;
          fi;
        od;
        # better we redraw:
        Delete(poset,box);
        DoRedraw(poset);
        GGSelectModifiesMenu := true;
        ModifyEnabled(poset,1,Length(poset!.menus));
      else
        Delete(poset,box);
      fi;
      # Drag(...) --> true    
    else
      Delete(poset,box);
    fi;
  fi;
end);


#############################################################################
##
#M  PosetRightClick(graph,x,y) . . . method which is called after right click
##
##  This method is called when the user does a right click in a graph. 
##  This method just finds the vertex under the mouse pointer and calls the
##  rightclickfunction of the poset. Note that the rightclickfunction
##  can be called with `fail' if no vertex is hit.
##
InstallMethod(PosetRightClick,
    "for a graph, and two integers",
    true,
    [ IsGraphicGraphRep, IsInt, IsInt ],
    0,

function(graph,x,y)
  local   v;
  
  # is this a click on a vertex?
  v := WhichVertex(graph,x,y);
  if graph!.rightclickfunction <> false then
    graph!.rightclickfunction(graph,v,x,y);
  fi;
  return;
end);

  
#############################################################################
##
#M  UserDeleteVerticesOp . . . is called if the user wants to delete vertices
##
##  This operation is called when the user selects "Delete vertices". 
##  The generic method actually deletes the selected vertices including all
##  their edges.
##
InstallMethod( UserDeleteVerticesOp,
    "for a graphic poset, a menu, and a menu entry",
    true,
    [ IsGraphicGraphRep, IsMenu, IsString ],
    0,
        
function( graph, menu, entry )
  local   v;
  
  # it is guaranteed, that at least one vertex is selected!
  while graph!.selectedvertices <> [] do
    Delete(graph,graph!.selectedvertices[1]);
  od;
end);
    

#############################################################################
##
#M  UserDeleteEdgeOp  . . . . . is called if the user wants to delete an edge
##
##  This operation is called when the user selects "Delete edge". 
##  The generic method deletes the edge with no further warning!
##
InstallMethod( UserDeleteEdgeOp,
    "for a graphic graph, a menu, and a menu entry",
    true,
    [ IsGraphicGraphRep, IsMenu, IsString ],
    0,
        
function( graph, menu, entry )
  # it is guaranteed, that exactly two connected vertices are selected!
  Delete(graph,graph!.selectedvertices[1],graph!.selectedvertices[2]);
end);


#############################################################################
##
#M  UserMergeClassesOp (<sheet>, <menu>, <entry>) . . . . . . . . . . . . . .
##  . . . . . . . . . . . . . .  is called if the user wants to merge classes
##
##  This operation is called when the user selects `Merge Classes'.
##  The generic method walks through all levels and merges all classes that
##  contain a selected vertex. Afterwards `UserRearrangeClasses' is called.
##
InstallMethod( UserMergeClassesOp,
    "for a graphic poset, a menu, and a menu entry",
    true,
    [ IsGraphicGraphRep and IsGraphicPosetRep, IsMenu, IsString ],
    0,
        
function( poset, menu, entry )
  local   lps,  verts,  v,  pos,  i,  level,  cps,  cpos,  cls,  j;
  
  # it is guaranteed, that at least one vertex is selected!
  # we walk through the selected vertices and sort them according to their
  # level parameter:
  lps := [];
  verts := [];
  for v in Selected(poset) do
    pos := Position(lps,v!.levelparam);
    if pos = fail then
      Add(lps,v!.levelparam);
      Add(verts,[v]);
    else
      Add(verts[pos],v);
    fi;
  od;
  
  # All levels:
  for i in [1..Length(lps)] do
    # the current level:
    level := poset!.levels[Position(poset!.levelparams,lps[i])];
    
    # Now we collect all classes occuring:
    cps := [];
    cpos := [];
    cls := [];
    for v in verts[i] do
      pos := Position(cps,v!.classparam);
      if pos = fail then
        Add(cps,v!.classparam);
        pos := Position(level!.classparams,v!.classparam);
        Add(cpos,pos);
        Add(cls,level!.classes[pos]);
      fi;
    od;
    
    # now we have a list of classes that should be merged:
    # let's move all vertices into the first class:
    for j in [2..Length(cls)] do
      for v in cls[j] do
        v!.classparam := cps[1];
        Add(cls[1],v);
      od;
    od;
    
    # now we have to delete the other classes (but not their vertices!):
    cpos := cpos{[2..Length(cps)]};
    Sort(cpos);
    for j in [Length(cpos),Length(cpos)-1..1] do
      level!.classes[cpos[j]] := level!.classes[Length(level!.classes)];
      Unbind(level!.classes[Length(level!.classes)]);
      level!.classparams[cpos[j]] := 
        level!.classparams[Length(level!.classparams)];
      Unbind(level!.classparams[Length(level!.classparams)]);
    od;
  od;
  
  # At last we rearrange those classes:
  UserRearrangeClasses( poset, menu, "Rearrange Classes" );    
end);


#############################################################################
##
## This is used by the following three methods:
##
BindGlobal("PosetScaleLattice",function(poset,factorx,factory)
  local   l,  pos,  cl,  v,  newx,  newy,  diffx,  diffy;
  
  FastUpdate(poset,true);  
  Resize(poset, Int(poset!.width*factorx), Int(poset!.height*factory));
  for l in [1..Length(poset!.levelparams)] do
    pos := PositionLevel(poset,poset!.levelparams[l]);
    ResizeLevel(poset,poset!.levelparams[l],Int(pos[2]*factory));
    for cl in poset!.levels[l]!.classes do
      if cl <> [] then
        v := cl[1];
        newx := Int(v!.x*factorx);
        newy := Int(v!.y*factory);
        diffx := newx - v!.x;
        diffy := newy - v!.y;
        for v in cl do
          Move(poset,v,v!.x + diffx,v!.y + diffy);
        od;
      fi;
    od;
  od;
  FastUpdate(poset,false);
end);


#############################################################################
##
#M  UserMagnifyLattice . . . . . .  lets the user magnify the graphic lattice
##
##  This operation is called when the user selects "Magnify Lattice". 
##  The generic method scales everything by 144/100 including the sheet,
##  all heights of levels and positions of vertices.
##
InstallMethod( UserMagnifyLattice,
    "for a graphic poset, a menu, and a string",
    true,
    [ IsGraphicPosetRep, IsMenu, IsString ],
    0,
    
function(poset, menu, entry)
  local   l,  pos,  cl,  v;
  PosetScaleLattice(poset,144/100,144/100);
end);


#############################################################################
##
#M  UserShrinkLattice . . . . . . .  lets the user shrink the graphic lattice
##
##  This operation is called when the user selects "Shrink Lattice". 
##  The generic method scales everything by 100/144 including the sheet,
##  all heights of levels and positions of vertices.
##
InstallMethod( UserShrinkLattice,
    "for a graphic poset, a menu, and a string",
    true,
    [ IsGraphicPosetRep, IsMenu, IsString ],
    0,
    
function(poset, menu, entry)
  local   l,  pos,  cl,  v;
  PosetScaleLattice(poset,100/144,100/144);
end);

##
## Make a rational number from a string, accept fraction:
##
BindGlobal("PosetRatString",
  function( st )
    local n,d,p;
    p := Position( st, '/' );
    if p = fail then
      return Int(st);
    else
      n := Int(st{[1..p-1]});
      d := Int(st{[p+1..Length(st)]});
      if d <> 0 then
        return n/d;
      else
        return infinity;
      fi;
    fi;
  end);
  
##
## Extracts two factors out of a string:
##
BindGlobal("PosetFactorsString", 
  function( factor )
    local   p,  x,  y;

    # find ","
    p := Position( factor, ',' );
    if p = fail  then
        x := PosetRatString(factor);
        y := x;
    elif p = 1  then
        x := 1;
        y := PosetRatString(factor{[2..Length(factor)]});
    elif p = Length(factor)  then
        x := PosetRatString(factor{[1..p-1]});
        y := 1;
    else
        x := PosetRatString(factor{[1..p-1]});
        y := PosetRatString(factor{[p+1..Length(factor)]});
    fi;
    if x <= 0  then x := 1;  fi;
    if y <= 0  then y := 1;  fi;
    return [ x, y ];
  end);


#############################################################################
##
#M  UserResizeLattice . . . . . . .  lets the user resize the graphic lattice
##
##  This operation is called when the user selects "Resize Lattice". 
##  The generic method asks the user for a x and a y factor and scales
##  everything including the sheet, all heights of levels and positions of 
##  vertices.
##
InstallMethod( UserResizeLattice,
    "for a graphic poset, a menu, and a string",
    true,
    [ IsGraphicPosetRep, IsMenu, IsString ],
    0,
    
function(poset, menu, entry)
  local   res,  fac;
  
  res := Query( Dialog( "OKcancel", "X,Y factors" ) );
  if res = false or 0 = Length(res)  then
    return;
  fi;
  fac := PosetFactorsString(res);
  if fac[1] <> 1 or fac[2] <> 1 then
    PosetScaleLattice(poset,fac[1],fac[2]);
  fi;
end);


#############################################################################
##
#M  UserResizeSheet . . . . . . . . .  lets the user resize the graphic sheet
##
##  This operation is called when the user selects "Resize Sheet". 
##  The generic method asks the user for a x and a y pixel number and
##  changes the width and height of the sheet. No positions of levels and
##  vertices are changed. If the user asks for trouble he gets it!
##
InstallMethod( UserResizeSheet,
    "for a graphic graph, a menu, and a string",
    true,
    [ IsGraphicGraphRep, IsMenu, IsString ],
    0,
    
function(poset, menu, entry)
  local   res,  pix,  oldwidth,  t;
  res := Query( Dialog( "OKcancel", "New Width,Height" ) );
  if res = false or 0 = Length(res)  then
    return;
  fi;
  pix := PosetFactorsString(res);
  if pix[1] = 1 then
    pix[1] := poset!.width;
  fi;
  if pix[2] = 1 then
    pix[2] := poset!.height;
  fi;
  
  oldwidth := poset!.width;
  
  Resize(poset,pix[1],pix[2]);
  
  # we now have to move the texts of levelparameters if it is a poset:
  if IsGraphicPosetRep(poset) and poset!.showlevelparams then
    for t in [1..Length(poset!.levels)] do
      MoveDelta(poset!.lptexts[t],poset!.width-oldwidth,0);
    od;
  fi;
end);


#############################################################################
##
#M  UserMoveLattice . . . . . . . . . . . . . lets the user move all vertices
##
##  This operation is called when the user selects "Move Lattice". 
##  The generic method asks the user for a pixel number and
##  changes the position of all vertices horizontally. No positions of 
##  levels are changed. 
##
InstallMethod( UserMoveLattice,
    "for a graphic poset, a menu, and a string",
    true,
    [ IsGraphicGraphRep and IsGraphicPosetRep, IsMenu, IsString ],
    0,
    
function(poset, menu, entry)
  local   res,  pix,  l,  cl,  v;
  res := Query( Dialog( "OKcancel", "Move horizontally" ) );
  if res = false or 0 = Length(res)  then
    return;
  fi;
  pix := Int(res);
  if pix <> 0 then
    for l in poset!.levels do
      for cl in l!.classes do
        for v in cl do
          Move(poset,v,v!.x+pix,v!.y);
        od;
      od;
    od;
  fi;
end);


#############################################################################
##
#M  UserChangeLabels . . . . . . . .  lets the user change labels of vertices
##
##  This operation is called when the user selects "Change Labels". 
##  The user is prompted for every selected vertex, which label it should
##  have.
##
InstallMethod( UserChangeLabels,
    "for a graphic graph, a menu, and a string",
    true,
    [ IsGraphicGraphRep, IsMenu, IsString ],
    0,
        
function(graph, menu, entry)
  local   D,  sel,  v,  res;
  
  D := Dialog("OKcancel", "Label");
  sel := Selected(graph);
  for v in sel do
    res := Query(D,v!.label);
    if res = false then
      return;
    fi;
    if 0 < Length(res) then
      Relabel(graph,v,res);
    fi;
  od;
end);


#############################################################################
##
#M  UserAverageY . . . . . . . . .  average all y positions within all levels
##
##  This operation is called when the user selects ``Average Y Positions''.
##  In all level the average y coordinate is calculated and all vertices are
##  moved to this y position.
##
InstallMethod( UserAverageY,
    "for a graphic poset, a menu, and a string",
    true,
    [ IsGraphicSheet and IsGraphicGraphRep and IsGraphicPosetRep,
      IsMenu, IsString ],
    0,
function( poset, menu, string )
  local   lev,  av,  n,  cl,  v;
  for lev in poset!.levels do
    av := 0;
    n := 0;
    for cl in lev!.classes do
      for v in cl do
        av := av + v!.y;
        n := n + 1;
      od;
    od;
    if n > 0 then
      av := QuoInt(av,n);
      for cl in lev!.classes do
        for v in cl do
          Move(poset,v,v!.x,av);
        od;
      od;
    fi;
  od;
end);


#############################################################################
##
#M  UserAverageX . . . . . . . . . . average all x positions of sel. vertices
##
##  This operation is called when the user selects ``Average X Positions''.
##  The average of all x coordinates of the selected vertices is calculated.
##  Then all classes with a selected vertex are moved such that the first
##  selected vertex in this class has the calculated position as x position.
##
InstallMethod( UserAverageX,
    "for a graphic poset, a menu, and a string",
    true,
    [ IsGraphicSheet and IsGraphicGraphRep and IsGraphicPosetRep,
      IsMenu, IsString ],
    0,
function( poset, menu, string )
  local   sel,  av,  list,  v,  pair,  vertices,  diff;
  sel := Selected(poset);
  # we have at least one selected vertex!
  av := 0;
  list := [];   # we store all levelparam/classparam pairs
  for v in sel do
    av := av + v!.x;
    AddSet(list,[v!.levelparam,v!.classparam]);
  od;
  av := QuoInt(av,Length(sel));
  
  FastUpdate(poset,true);
  for pair in list do
    vertices := Vertices(poset,pair[1],pair[2]);
    if vertices <> fail then
      v := First(vertices,x->x in sel);
      if v <> fail then
        diff := av - v!.x;
        for v in vertices do
          Move(poset,v,v!.x + diff,v!.y);
        od;
      fi;
    fi;
  od;
  FastUpdate(poset,false);
end);

  
#############################################################################
##
#M  UserRearrangesClasses . . . . . . . . . . rearrange vertices within class
##
##  This operation is called when the user selects ``Rearrange Classes''.
##  All classes with a selected vertex are rearranged: The vertices are
##  lined up neatly one after the other, sorted according to their current
##  x position.
##
InstallMethod( UserRearrangeClasses,
    "for a graphic poset, a menu, and a string",
    true,
    [ IsGraphicSheet and IsGraphicGraphRep and IsGraphicPosetRep,
      IsMenu, IsString ],
    0,
function( poset, menu, string )
  local   sel,  av,  list,  v,  pair,  vlist,  xlist,  perm,  i;
  
  sel := Selected(poset);
  # we have at least one selected vertex!
  av := 0;
  list := [];   # we store all levelparam/classparam pairs
  for v in sel do
    AddSet(list,[v!.levelparam,v!.classparam]);
  od;
  
  FastUpdate(poset,true);
  for pair in list do
    # get the vertices in class:
    vlist := Vertices(poset,pair[1],pair[2]);
    if vlist <> fail then
      xlist := List(vlist,y->y!.x);
      perm := Sortex(xlist);
      vlist := Permuted(vlist,perm);
      for i in [2..Length(vlist)] do
        Move(poset,vlist[i],vlist[1]!.x + (i-1)*(VERTEX.diameter+2),
             vlist[1]!.y);
      od;
    fi;
  od;
  FastUpdate(poset,false);
end);


############################################################################
##
#M  UserUseBlackWhite . . . . . . . . . .  called if user selects bw in menu
##
##  This is called if the user selects ``Use Black and White'' in the menu.
##
InstallMethod( UserUseBlackWhite,
    "for a graphic graph, a menu, and a string",
    true,
    [ IsGraphicSheet and IsGraphicGraphRep, IsMenu, IsString ],
    0,
function( sheet, menu, entry )
  local   v;
  if sheet!.color.model = "monochrome" then
    sheet!.color.model := "color";
    Check(menu,entry,false);  
  else
    sheet!.color.model := "monochrome";
    Check(menu,entry,true);  
  fi;
  GPMakeColors(sheet);
  for v in Selected(sheet) do
    Recolor(sheet,v,sheet!.color.selected);
  od;
end);


#############################################################################
##
#M  PosetShowLevels  . . . . . . . . . . . . . . . . switch display of levels
##
##  This operation is called when the user selects "Show Levels" in the menu.
##  Switches the display of the little boxes for level handling on and off.
##
InstallMethod( PosetShowLevels,
    "for a graphic poset, a menu, and a menu entry",
    true,
    [ IsGraphicPosetRep, IsMenu, IsString ],
    0,
        
function( poset, menu, entry )
  local   b;
  poset!.showlevels := not(poset!.showlevels);
  if poset!.showlevels then
    for b in [1..Length(poset!.levelboxes)] do
      Revive(poset!.levelboxes[b]);
      Move(poset!.levelboxes[b],0,poset!.levels[b]!.top
                                  +poset!.levels[b]!.height-8);
    od;
  else
    for b in poset!.levelboxes do
      Destroy(b);
    od;
  fi;
  Check(menu,entry,poset!.showlevels);
end);


#############################################################################
##
#M  PosetShowLevelparams . . . . . . . . .  switch display of levelparameters
##
##  This operation is called when the user selects "Show Levelparameters" in 
##  the menu. Switches the display of the level parameters at the right of
##  the screen on and off.
##
InstallMethod( PosetShowLevelparams,
    "for a graphic poset, a menu, and a menu entry",
    true,
    [ IsGraphicPosetRep, IsMenu, IsString ],
    0,
        
function( poset, menu, entry )
  local   t;
  poset!.showlevelparams := not(poset!.showlevelparams);
  if poset!.showlevelparams then
    for t in [1..Length(poset!.lptexts)] do
      Revive(poset!.lptexts[t]);
      Move(poset!.lptexts[t],poset!.lptexts[t]!.x,poset!.levels[t]!.top
                                  +QuoInt(poset!.levels[t]!.height,2));
    od;
  else
    for t in poset!.lptexts do
      Destroy(t);
    od;
  fi;
  Check(menu,entry,poset!.showlevelparams);
end);


#############################################################################
##
#M  DoRedraw(<graph>). . . . . . . . . . redraws all vertices and connections
##
##  Redraws all vertices and connections.
##
InstallMethod( DoRedraw,
    "for a graphic poset",
    true,
    [ IsGraphicPosetRep ],
    0,

function(poset)
  local   lev,  cl,  v,  v2,  pos;
  
  for lev in poset!.levels do
    for cl in lev!.classes do
      for v in cl do
        Draw(v!.obj);
        for v2 in v!.maximals do
          pos := Position(v!.obj!.connections,v2!.obj);
          if pos <> fail then
            Draw(v!.obj!.connectingLines[pos]);
          fi;
        od;
      od;
    od;
  od;
end);


#############################################################################
##
##  Some things that don't fit in other sections:
##
#############################################################################

##
##  We want Position and PositionSorted for lists of vertices:
##
InstallMethod( EQ, "for two vertices", true, [IsGGVertex,IsGGVertex],0,
        IsIdenticalObj );
InstallMethod( \<, "for two vertices", true, [IsGGVertex,IsGGVertex],0,
        function(a,b) return (a!.serial < b!.serial); end);
InstallMethod( EQ, "for two levels", true, [IsGPLevel,IsGPLevel],0,
        IsIdenticalObj );
        
##
##  ViewObj methods:
##
InstallMethod( ViewObj,"for a graphic graph",true,
        [IsGraphicSheet and IsGraphicSheetRep and IsGraphicGraphRep],
        0,function( sheet ) 
  Print("<");
  if not IsAlive(sheet) then
    Print("dead ");
  fi;
  Print("graphic graph \"",sheet!.name,"\">");
end);
  
InstallMethod( ViewObj,"for a graphic poset",true,
        [IsGraphicSheet and IsGraphicSheetRep and IsGraphicGraphRep and 
         IsGraphicPosetRep],
        0,function( sheet ) 
  Print("<");
  if not IsAlive(sheet) then
    Print("dead ");
  fi;
  Print("graphic poset \"",sheet!.name,"\">");
end);
  
InstallMethod( ViewObj,"for a level",true,
        [IsGraphicObject and IsGPLevel],
        0,function( level ) 
  local   pos;
  pos := Position(level!.poset!.levels,level);
  Print("<level of graphic poset \"",level!.poset!.name,"\", Parameter: ",
        level!.poset!.levelparams[pos],">");
end);

InstallMethod( ViewObj,"for a vertex",true,
        [IsGraphicObject and IsGGVertex],
        0,function( vertex ) 
  Print("<vertex of graphic graph, label: \"",vertex!.label,"\", Serial:",
        vertex!.serial,">");
end);

## FIXME: ... TODO-List for graphs:
        
# comments for GraphicGraphRep
# generic Graph Menu with at least Redraw, probably Deletes also
#M  GraphicGraph( <name>, <width>, <height> ) . . . . . . a new graphic graph
#M  Vertex(<graph>,<data>[,<inf>]) . . . . . . . . . . . . creates new vertex
#M  Edge(<graph>,<vertex1>,<vertex2>) . . . . . . . . . . . . adds a new edge
#M  Edge(<graph>,<vertex1>,<vertex2>,<def>) . . . . . . . . . adds a new edge
#M  Delete(<graph>,<obj>) . . . . . . . . . . . . . remove something in graph
#M  Move(<graph>,<vertex>,<x>,<y>) . . . . . . . . . . . . . . .  move vertex
#M  Move(<graph>,<vertex>) . . . . . . . . . . . . . . . . . . .  move vertex
#M  SetWidth(<graph>,<vertex1>,<vertex2>,<width>) . change line width of edge
#M  SetWidth(<graph>,<vertex1>,<vertex2>) . . . . . change line width of edge
#M  ChooseLabel(<graph>,<data>,<data>)  . . . . is called while edge creation
#M  ChoosePosition(<graph>,<data>)  . . . . . is called while vertex creation
#M  WhichVertex(<graph>,<x>,<y>) . . .  determine vertex in which position is
#M  WhichVertex(<graph>,<data>)  . . . . .  determine vertex with data <data>


