Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  poset.gi   Sprache: unbekannt

 
Spracherkennung für: .gi vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

#############################################################################
##
#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 safely 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 highlighting of vertex
#M  Highlight(<graph>,<vertex>,<flag>) . . . . change highlighting 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 parameter <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 occurring:
    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, t;
  
  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;

  # Keep level labels at the right side of the window
  for t in poset!.lptexts do
    if IsAlive(t) then
      Move(t, poset!.width - 24 - Length(t!.text)*FontInfo(FONTS.normal)[3], t!.y);
    else
      t!.x := poset!.width - 24 - Length(t!.text)*FontInfo(FONTS.normal)[3];
    fi;
  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>



[Dauer der Verarbeitung: 0.43 Sekunden, vorverarbeitet 2026-04-26]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge