1#############################################################################
2##
3#W  poset.gi                  	XGAP library                  Max Neunhoeffer
4##
5##
6#Y  Copyright 1998,       Max Neunhoeffer,              Aachen,       Germany
7##
8##  This file contains the implementations for graphs and posets
9##
10
11
12#############################################################################
13##
14##  Declarations of representations:
15##
16#############################################################################
17
18
19#############################################################################
20##
21#R  IsGraphicGraphRep . . . . . . . . . . . . . . .  representation for graph
22##
23if not IsBound(IsGraphicGraphRep) then
24  DeclareRepresentation( "IsGraphicGraphRep",
25    IsComponentObjectRep and IsAttributeStoringRep and IsGraphicSheet and
26    IsGraphicSheetRep,
27# we inherit those components from the sheet:
28    [ "name", "width", "height", "gapMenu", "callbackName", "callbackFunc",
29      "menus", "objects", "free",
30# now our own components:
31      "vertices","edges","selectedvertices","menutypes",
32      "menuenabled","rightclickfunction","color"],
33    IsGraphicSheet );
34fi;
35
36
37
38#############################################################################
39##
40#R  IsGraphicPosetRep . . . . . . . . . . . . . . .  representation for poset
41##
42if not IsBound(IsGraphicPosetRep) then
43  DeclareRepresentation( "IsGraphicPosetRep",
44        IsComponentObjectRep and IsAttributeStoringRep and
45        IsGraphicSheet and IsGraphicSheetRep and IsGraphicGraphRep,
46# we inherit those components from the sheet:
47    [ "name", "width", "height", "gapMenu", "callbackName", "callbackFunc",
48      "menus", "objects", "free",
49# now our own components:
50      "levels",           # list of levels, stores current total ordering
51      "levelparams",      # list of level parameters
52      "selectedvertices", # list of selected vertices
53      "menutypes",        # one entry per menu which contains list of types
54      "menuenabled",      # one entry per menu which contains list of flags
55      "rightclickfunction",    # the current function which is called when
56                               # user clicks right button
57      "color",            # some color infos for the case of different models
58      "levelboxes",       # little graphic boxes for the user to handle levels
59      "showlevelparams",  # flag, if level parameters are shown
60      "showlevels"],      # flag, if levelboxes are shown
61    IsGraphicSheet );
62fi;
63
64
65#############################################################################
66##
67#R  IsGPLevel . . . . . . . . . . . . . . . . . . .  representation for level
68##
69if not IsBound(IsGPLevel) then
70  DeclareRepresentation( "IsGPLevel",
71        IsComponentObjectRep,
72        [ "top",          # y coordinate of top of level, relative to sheet
73          "height",       # height in pixels
74          "classes",      # list of classes, which are lists of vertices
75          "classparams",  # list of class parameters
76          "poset"         # poset to which level belongs
77        ],
78        IsGraphicObject );
79fi;
80
81#############################################################################
82##
83#R  IsGGVertex . . . . . . . . . . . . . . . . . .  representation for vertex
84##
85if not IsBound(IsGGVertex) then
86  DeclareRepresentation( "IsGGVertex",
87        IsComponentObjectRep and IsGraphicObject,
88        [ "data",         # the mathematical data
89          "obj",          # real graphic object
90          "x","y",        # coordinates of graphic object within sheet
91          "serial",       # a serial number for comparison
92          "label"         # the label of the vertex or false
93        ],
94        IsGraphicObject );
95fi;
96
97
98#############################################################################
99##
100#R  IsGPVertex . . . . . . . . . . . . . . . . . .  representation for vertex
101##
102if not IsBound(IsGPVertex) then
103  DeclareRepresentation( "IsGPVertex",
104        IsComponentObjectRep and IsGraphicObject,
105        [ "data",         # the mathematical data
106          "obj",          # real graphic object
107          "levelparam",   # level parameter
108          "classparam",   # class parameter
109          "maximals",     # list of vertices which are maximal subobjects
110          "maximalin",    # list of vertices where this one is maximal in
111          "x","y",        # coordinates of graphic object within level
112          "serial",       # a serial number for comparison
113          "label"         # the label of the vertex or false
114        ],
115        IsGraphicObject );
116fi;
117
118
119#############################################################################
120##
121##  Some global things we all need:
122##
123#############################################################################
124
125
126##  We count all vertices:
127PosetLastUsedSerialNumber := 0;
128
129
130##  The following function is installed as a LeftPBDown in every graph or
131##  poset. It calls the operation PosetLeftClick.
132
133PosetLeftClickCallback := function(poset,x,y)
134  PosetLeftClick(poset,x,y);
135end;
136
137##  The following function is installed as a RightPBDown in every graph or
138##  poset. It calls the operation PosetRightClick.
139
140PosetRightClickCallback := function(poset,x,y)
141  PosetRightClick(poset,x,y);
142end;
143
144##  The following function is installed as a CtrlLeftPBDown and
145##  ShiftLeftPBDown in every graph or poset. It calls the operation
146##  PosetCtrlLeftClick.
147
148PosetCtrlLeftClickCallback := function(poset,x,y)
149  PosetCtrlLeftClick(poset,x,y);
150end;
151
152
153##  The following is a for a menu entry and just calls another method:
154PosetDoRedraw := function(poset,menu,entry)
155  DoRedraw(poset);
156end;
157
158
159##  Our menu which goes in all poset sheets:
160PosetMenuEntries :=
161  ["Redraw","Show Levels","Show Level Parameters",,
162   "Delete Vertices","Delete Edge","Merge Classes",,
163   "Magnify Lattice", "Shrink Lattice", "Resize Lattice", "Resize Sheet",
164   "Move Lattice",,
165   "Change Labels","Average Y Positions","Average X Positions",
166   "Rearrange Classes"];
167PosetMenuTypes :=
168  ["forany","forany","forany",,
169   "forsubset","foredge","forsubset",,
170   "forany","forany","forany","forany","forany",,
171   "forsubset","forany","forsubset","forsubset"];
172PosetMenuFunctions :=
173  [ PosetDoRedraw,PosetShowLevels,PosetShowLevelparams,,
174    UserDeleteVerticesOp, UserDeleteEdgeOp, UserMergeClassesOp,,
175    UserMagnifyLattice,UserShrinkLattice,UserResizeLattice,UserResizeSheet,
176    UserMoveLattice,,
177    UserChangeLabels,UserAverageY,UserAverageX,UserRearrangeClasses];
178
179
180#############################################################################
181##
182##  Constructors:
183##
184#############################################################################
185
186
187## we need this to set up the colors in a sheet:
188
189BindGlobal( "GPMakeColors",
190        function( sheet )
191
192  # set up color information:
193  if sheet!.color.model = "color"  then
194    if COLORS.red <> false  then
195      sheet!.color.unselected := COLORS.black;
196      sheet!.color.selected   := COLORS.red;
197    else
198      sheet!.color.unselected := COLORS.dimGray;
199      sheet!.color.selected   := COLORS.black;
200    fi;
201    if COLORS.green <> false  then
202      sheet!.color.result := COLORS.green;
203    else
204      sheet!.color.result := COLORS.black; # COLORS.lightGray;
205    fi;
206  else
207    sheet!.color.selected   := COLORS.black;
208    sheet!.color.unselected := COLORS.black;
209    sheet!.color.result     := false;
210  fi;
211end);
212
213
214#############################################################################
215##
216#M  GraphicGraph( <name>, <width>, <height> ) . . . . . . a new graphic graph
217##
218##  creates a new graphic graph which is a graphic sheet representation
219##  with knowledge about vertices and edges and infrastructure for user
220##  interfaces.
221##
222InstallMethod( GraphicGraph,
223    "for a string, and two integers",
224    true,
225    [ IsString,
226      IsInt,
227      IsInt ],
228    0,
229
230function( name, width, height )
231  #local ...;
232
233end);
234
235
236#############################################################################
237##
238#M  GraphicPoset( <name>, <width>, <height> ) . . . . . . a new graphic poset
239##
240##  creates a new graphic poset which is a specialization of a graphic graph
241##  mainly because per definition a poset comes in "layers" or "levels". This
242##  leads to some algorithms that are more efficient than the general ones
243##  for graphs.
244##
245InstallMethod( GraphicPoset,
246    "for a string, and two integers",
247    true,
248    [ IsString,
249      IsInt,
250      IsInt ],
251    0,
252
253function( name, width, height )
254  local   poset,  tmpEntries,  tmpTypes,  tmpFuncs,  m;
255
256  poset := GraphicSheet(name,width,height);
257  SetFilterObj(poset,IsGraphicGraphRep);
258  SetFilterObj(poset,IsGraphicPosetRep);
259  poset!.levels := [];
260  poset!.levelparams := [];
261  poset!.selectedvertices := [];
262  # think of the GAP menu:
263  poset!.menutypes := [List(poset!.menus[1]!.entries,x->"forany")];
264  poset!.menuenabled := [List(poset!.menus[1]!.entries,x->true)];
265  poset!.rightclickfunction := Ignore;
266
267  # set up color information:
268  poset!.color := rec();
269  if COLORS.red <> false or COLORS.lightGray <> false  then
270    poset!.color.model := "color";
271    # note: if you rename this, think of the "use black&white" below!
272  else
273    poset!.color.model := "monochrome";
274  fi;
275  GPMakeColors(poset);
276
277  poset!.levelboxes := [];
278  poset!.showlevels := false;
279  poset!.lptexts := [];
280  poset!.showlevelparams := true;
281
282  InstallCallback(poset,"LeftPBDown",PosetLeftClickCallback);
283  InstallCallback(poset,"ShiftLeftPBDown",PosetCtrlLeftClickCallback);
284  InstallCallback(poset,"CtrlLeftPBDown",PosetCtrlLeftClickCallback);
285  InstallCallback(poset,"RightPBDown",PosetRightClickCallback);
286
287  tmpEntries := ShallowCopy(PosetMenuEntries);
288  tmpTypes := ShallowCopy(PosetMenuTypes);
289  tmpFuncs := ShallowCopy(PosetMenuFunctions);
290  if poset!.color.model = "color" then
291    Append(tmpEntries,["-","Use Black&White"]);
292    Append(tmpTypes,["-","forany"]);
293    Append(tmpFuncs,["-",UserUseBlackWhite]);
294  fi;
295  m := Menu(poset,"Poset",tmpEntries,tmpTypes,tmpFuncs);
296  Check(m,"Show Level Parameters",true);
297
298  return poset;
299end);
300
301
302#############################################################################
303##
304#M  CreateLevel(<poset>, <levelparam>) . . . . . . creates new level in poset
305#M  CreateLevel(<poset>, <levelparam>, <lptext>) . creates new level in poset
306##
307##  A level in a graphic poset can be thought of as a horizontal slice of
308##  the poset. It has a y coordinate of the top of the level relatively to
309##  the graphic sheet and a height. Every class of vertices in a graphic
310##  poset is in a level. The levels are totally ordered by their y
311##  coordinate. No two vertices which are included in each other are in the
312##  same level. A vertex containing another one is always "higher" on the
313##  screen, meaning in a "higher" level.  Every level has a unique
314##  levelparam, which can be any {\GAP} object. The user is responsible for
315##  all methods where a levelparam occurs as parameter and is not just an
316##  integer. There is NO {\GAP} object representing a level which is visible
317##  for the user of posets. All communication about levels goes via the
318##  levelparam.  Returns fail if there is already a level with a level
319##  parameter which is considered "equal" by CompareLevels or levelparam if
320##  everything went well.
321##  The second method allows to specify which text appears for the level at
322##  the right edge of the sheet.
323##
324InstallMethod( CreateLevel,
325    "for a graphic poset, a level parameter, and a string",
326    true,
327    [ IsGraphicPosetRep, IsObject, IsString ],
328    0,
329
330function( poset, levelparam, lpstr )
331  local   level,  box,  str,  strlen,  text,  l,  firstpos,  before,  look,
332          compare,  i,  cl,  v;
333
334  # does this level parameter exist already?
335  if Position(poset!.levelparams,levelparam) <> fail then
336    return fail;
337  fi;
338
339  # create a level object:
340  level := rec(classes := [],
341               classparams := [],
342               poset := poset);
343  Objectify(NewType(GraphicObjectFamily,IsGPLevel),level);
344
345  # is it the first level:
346  if poset!.levelparams = [] then
347    poset!.levelparams := [levelparam];
348    poset!.levels := [level];
349    level!.top := 0;
350    level!.height := 2 * VERTEX.diameter;
351
352    # make a level box:
353    box := Box(poset,0,level!.top+level!.height-8,8,8);
354    if COLORS.blue <> false then
355      Recolor(box,COLORS.blue);
356    fi;
357    if not poset!.showlevels then
358      Destroy(box);
359    fi;
360
361    poset!.levelboxes := [ box ];
362
363    # make a text for level parameter:
364    if lpstr <> "" then
365      str := lpstr;
366    else
367      str := String(levelparam);
368    fi;
369    strlen := Length(str);
370    text := Text(poset,FONTS.normal,
371                 poset!.width - 24 - strlen*FontInfo(FONTS.normal)[3],
372                 level!.top + QuoInt(level!.height,2),str);
373    if COLORS.blue <> false then
374      Recolor(text,COLORS.blue);
375    fi;
376    if not poset!.showlevelparams then
377      Destroy(text);
378    fi;
379
380    poset!.lptexts := [ text ];
381
382    return levelparam;
383  fi;
384
385  # now find the position, we choose the last position where the new level
386  # can be according to the partial order defined by CompareLevels we do a
387  # binary search, we insert not before "firstpos" and before "before".
388  # Attention: We cannot decide at a level which is not comparable to the
389  # new level, so we have to search linearly for a comparable level!
390  l := Length(poset!.levelparams);
391  firstpos := 1;
392  before := l + 1;
393  while firstpos < before do
394    look := QuoInt(firstpos + before,2);
395    repeat  # search first backward up to firstpos, then down
396      compare := CompareLevels(poset,levelparam,poset!.levelparams[look]);
397      if compare = 0 then
398        return fail;
399      elif compare = fail then   # not comparable
400        look := look-1;
401      fi;
402    until compare <> fail or look < firstpos;
403    if compare = fail then
404      # search now forward down to before
405      look := QuoInt(firstpos + before,2)+1;
406      if look = before then
407        firstpos := before;   # we insert right HERE!
408        compare := 0;
409      fi;
410      while compare = fail do
411        compare := CompareLevels(poset,levelparam,poset!.levelparams[look]);
412        if compare = 0 then
413          return fail;
414        elif compare = fail then     # not comparable
415          look := look+1;
416          if look = before then     # nothing comparable in between!
417            firstpos := before;     # we insert right HERE!
418            compare := 0;           # this does exactly that!
419          fi;
420        fi;
421      od;
422    fi;
423    if compare < 0 then
424      before := look;
425    elif compare > 0 then
426      firstpos := look+1;
427    fi;
428  od;
429
430  # we now insert at position firstpos = before:
431  poset!.levelparams{[firstpos+1..l+1]} := poset!.levelparams{[firstpos..l]};
432  poset!.levelparams[firstpos] := levelparam;
433  poset!.levels{[firstpos+1..l+1]} := poset!.levels{[firstpos..l]};
434  poset!.levels[firstpos] := level;
435  poset!.levelboxes{[firstpos+1..l+1]} := poset!.levelboxes{[firstpos..l]};
436  poset!.lptexts{[firstpos+1..l+1]} := poset!.lptexts{[firstpos..l]};
437
438  if firstpos = 1 then
439    level!.top := 0;
440  else
441    level!.top := poset!.levels[firstpos-1]!.top +
442                  poset!.levels[firstpos-1]!.height;
443  fi;
444  level!.height := 2 * VERTEX.diameter;
445
446  # move all lower levels down:
447  FastUpdate(poset,true);
448  for i in [firstpos+1..l+1] do
449    poset!.levels[i]!.top := poset!.levels[i]!.top + level!.height;
450    for cl in poset!.levels[i]!.classes do
451      for v in cl do
452        MoveDelta(v!.obj,0,level!.height);
453      od;
454    od;
455    if poset!.showlevels then
456      MoveDelta(poset!.levelboxes[i],0,level!.height);
457    fi;
458    if poset!.showlevelparams then
459      MoveDelta(poset!.lptexts[i],0,level!.height);
460    fi;
461  od;
462  FastUpdate(poset,false);
463
464  # has the graphic sheet become higher?
465  l := l + 1;    # this means:   l := Length(poset!.levels);
466  i := poset!.levels[l]!.top + poset!.levels[l]!.height;
467  if i > poset!.height then
468    Resize(poset,poset!.width,i);
469  fi;
470
471  # create a level box:
472  box := Box(poset,0,level!.top+level!.height-8,8,8);
473  if COLORS.blue <> false then
474    Recolor(box,COLORS.blue);
475  fi;
476  if not poset!.showlevels then
477    Destroy(box);
478  fi;
479  poset!.levelboxes[firstpos] := box;
480
481  # create a level parameter text:
482  if lpstr <> "" then
483    str := lpstr;
484  else
485    str := String(levelparam);
486  fi;
487  strlen := Length(str);
488  text := Text(poset,FONTS.normal,
489               poset!.width - 24 - strlen*FontInfo(FONTS.normal)[3],
490               level!.top + QuoInt(level!.height,2),str);
491  if COLORS.blue <> false then
492    Recolor(text,COLORS.blue);
493  fi;
494  if not poset!.showlevelparams then
495    Destroy(text);
496  fi;
497  poset!.lptexts[firstpos] := text;
498
499  return levelparam;
500end);
501
502
503InstallOtherMethod( CreateLevel,
504    "for a graphic poset, and a level parameter",
505    true,
506    [ IsGraphicPosetRep, IsObject ],
507    0,
508function( poset, levelparam )
509  return CreateLevel(poset,levelparam,"");
510end);
511
512
513#############################################################################
514##
515#M  CreateClass(<poset>,<levelparam>,<classparam>) . . . .  creates new class
516##
517##  A class in a graphic poset is a collection of vertices within a level
518##  which belong together in some sense.  Every vertex in a graphic poset
519##  is in a class, which in turn belongs to a level. Every class in a level
520##  has a unique classparam, which can be any {\GAP} object. The user is
521##  responsible for all methods where a classparam occurs as parameter and
522##  is not just an integer. There is NO {\GAP} object representing a class
523##  which is visible to the user of posets. All communication about classes
524##  goes via the classparam.  Returns fail if there is no level with
525##  parameter levelparam or there is already a class in this level with
526##  parameter classparam. Returns classparam otherwise.
527##
528InstallMethod( CreateClass,
529    "for a graphic poset, a level parameter, and a class parameter",
530    true,
531    [ IsGraphicPosetRep, IsObject, IsObject ],
532    0,
533
534function( poset, levelparam, classparam )
535  local nr, level;
536
537  nr := Position(poset!.levelparams,levelparam);
538  if nr = fail then
539    return fail;
540  fi;
541  level := poset!.levels[nr];
542
543  nr := Position(level!.classparams,classparam);
544  if nr <> fail then
545    return fail;
546  fi;
547
548  Add(level!.classparams,classparam);
549  Add(level!.classes,[]);
550
551  return classparam;
552end);
553
554
555#############################################################################
556##
557#M  Vertex(<graph>,<data>[,<inf>]) . . . . . . . . . . . . creates new vertex
558##
559##  Creates a new vertex. <inf> is a record in which additional info can be
560##  supplied for the new vertex. For general graphic graphs only the
561##  "label", "color", "shape", "x" and "y" components are applicable, they
562##  contain a short label which will be attached to the vertex, the color,
563##  the shape ("circle", "diamond", or "rectangle") and the coordinates
564##  relative to the graphic sheet respectively. For graphic posets also the
565##  components "levelparam" and "classparam" are evaluated. If the component
566##  "hints" is bound it must be a list of x coordinates which will be
567##  delivered to ChoosePosition to help placement. Those x coordinates will
568##  be the coordinates of other vertices related to the new one. All values of
569##  record components which are not specified will be determined by calling
570##  some methods for graphic graphs or posets. Those are:
571##    ChooseLabel for the label,
572##    ChooseColor for the color,
573##    ChooseShape for the shape,
574##    ChoosePosition for the position,
575##    ChooseLevel for the levelparam, and
576##    ChooseClass for the classparam.
577##    ChooseWidth for the line width of the vertex
578##  Returns fail no vertex was created. This happens only, if one of the
579##  choose functions return fail or no possible value, for example a
580##  non-existing level or class parameter.
581##  Returns vertex object if everything went well.
582##
583InstallOtherMethod( Vertex,
584    "for a graphic poset, an object, and a record",
585    true,
586    [ IsGraphicPosetRep, IsObject, IsRecord ],
587    0,
588
589function( poset, data, info )
590  local   lp,  lnr,  level,  cp,  cnr,  class,  vertex,  label,  shape,
591          color,  position, width;
592
593  # first determine levelparam:
594  if not IsBound(info.levelparam) then
595    lp := ChooseLevel(poset,data);
596  else
597    lp := info.levelparam;
598  fi;
599  if lp = fail then
600    return fail;
601  fi;
602
603  # we search for the level:
604  lnr := Position(poset!.levelparams,lp);
605  if lnr = fail then
606    return fail;
607  fi;
608  level := poset!.levels[lnr];
609
610  # now determine class:
611  if not IsBound(info.classparam) then
612    cp := ChooseClass(poset,data,lp);
613  else
614    cp := info.classparam;
615  fi;
616  if cp = fail then
617    return fail;
618  fi;
619
620  # we search for the class:
621  cnr := Position(level!.classparams,cp);
622  if cnr = fail then
623    return fail;
624  fi;
625  class := level!.classes[cnr];
626
627  # create a new vertex object:
628  PosetLastUsedSerialNumber := PosetLastUsedSerialNumber + 1;
629  vertex := rec(data := data,
630                levelparam := lp,
631                classparam := cp,
632                maximals := [],
633                maximalin := [],
634                serial := PosetLastUsedSerialNumber);
635  Objectify(NewType(GraphicObjectFamily,IsGPVertex),vertex);
636  SetFilterObj(vertex,IsGGVertex);
637  SetFilterObj(vertex,IsAlive);
638
639  # choose label, shape, color and position:
640  if not IsBound(info.label) then
641    label := ChooseLabel(poset,data);
642    if label = fail then
643      return fail;
644    fi;
645  else
646    label := info.label;
647  fi;
648  if not IsBound(info.shape) then
649    shape := ChooseShape(poset,data);
650    if shape = fail then
651      return fail;
652    fi;
653  else
654    shape := info.shape;
655  fi;
656  if not IsBound(info.color) then
657    color := ChooseColor(poset,data);
658    if color = fail then
659      return fail;
660    fi;
661  else
662    color := info.color;
663  fi;
664  if not (IsBound(info.x) and IsBound(info.y)) then
665    if IsBound(info.hints) then
666      position := ChoosePosition(poset,data,level,class,info.hints);
667    else
668      position := ChoosePosition(poset,data,level,class,[]);
669    fi;
670    if IsBound(info.x) then   # this takes precedence!
671      vertex!.x := info.x;
672    else
673      vertex!.x := position[1];
674    fi;
675    if IsBound(info.y) then   # this takes precedence!
676      vertex!.y := info.y;
677    else
678      vertex!.y := position[2];
679    fi;
680  else
681    vertex!.x := info.x;
682    vertex!.y := info.y;
683  fi;
684  if not IsBound(info.width) then
685    width := ChooseWidth(poset,data);
686    if width = fail then
687      return fail;
688    fi;
689  fi;
690
691  vertex!.label := label;
692
693  # create the graphic object:
694  vertex!.obj := Vertex(poset,vertex!.x,level!.top + vertex!.y,
695                        rec(label := label,color := color,width := width));
696  if shape = "diamond" then
697    Reshape(vertex!.obj,VERTEX.diamond);
698  elif shape = "rectangle" then
699    Reshape(vertex!.obj,VERTEX.rectangle);
700  fi;
701
702  # put it into the class:
703  Add(class,vertex);
704
705  return vertex;
706end);
707
708
709#############################################################################
710##
711##  The following function is only internal:
712##
713##  Use it on your own risk and only if you know what you are doing!
714##
715GPSearchWay := function(poset,v1,v2,l2)
716  local v, p;
717  for v in v1!.maximals do
718    if v = v2 then
719      return true;
720    fi;
721
722    if Position(poset!.levelparams,v!.levelparam) < l2 then
723      if GPSearchWay(poset,v,v2,l2) then
724        return true;
725      fi;
726    fi;
727  od;
728  return false;
729end;
730
731
732#############################################################################
733##
734#M  Edge(<poset>,<vertex1>,<vertex2>) . . . . . . . . . . . . adds a new edge
735#M  Edge(<poset>,<vertex1>,<vertex2>,<def>) . . . . . . . . . adds a new edge
736##
737##  Adds a new edge from <vertex1> to <vertex2>. For posets this puts one
738##  of the vertices into the other as a maximal subvertex. So either
739##  <vertex1> must lie in a "higher" level than <vertex2> or the other way
740##  round. There must be no vertex "between" <vertex1> and <vertex2>. If
741##  the two vertices are in the same level or one is already indirectly
742##  included in the other fail is returned, otherwise true. That means,
743##  that in the case where one of the two vertices is already a maximal
744##  subobject of the other, then the method does nothing and returns true.
745##  The variation with a defaults record just hands this over to the lower
746##  levels, meaning that the line width and color are modified.
747##
748InstallOtherMethod( Edge,
749    "for a graphic poset, two vertices, and a defaults record",
750    true,
751    [ IsGraphicPosetRep, IsGPVertex, IsGPVertex, IsRecord ],
752    0,
753
754function( poset, v1, v2, def )
755  local   l1,  l2,  dummy,  l,  p;
756
757  # we permute v1 and v2 such that v1 is in higher level:
758  if CompareLevels(poset,v1!.levelparam,v2!.levelparam) = 0 then
759    return fail;
760  fi;
761  l1 := Position(poset!.levelparams,v1!.levelparam);
762  l2 := Position(poset!.levelparams,v2!.levelparam);
763  if l1 > l2 then
764    dummy := v1;
765    v1 := v2;
766    v2 := dummy;
767    dummy := l1;
768    l1 := l2;
769    l2 := dummy;
770  fi;
771
772  # first we have to perform a few checks:
773  if Position(v1!.maximals,v2) <> fail then
774    return true;
775  fi;
776  if GPSearchWay(poset,v1,v2,l2) then
777    return fail;
778  fi;
779
780  # let's think about color, label and width:
781  if not IsBound(def.color) then
782    def.color := ChooseColor(poset,v1!.data,v2!.data);
783    if def.color = fail then
784      return fail;
785    fi;
786  fi;
787  if not IsBound(def.label) then
788    def.label := ChooseLabel(poset,v1!.data,v2!.data);
789    if def.label = fail then
790      return fail;
791    fi;
792  fi;
793  if not IsBound(def.width) then
794    def.width := ChooseWidth(poset,v1!.data,v2!.data);
795    if def.width = fail then
796      return fail;
797    fi;
798  fi;
799
800  # now we know that there is no direct or indirect inclusion of v2 in v1.
801  # we can savely put v2 "into" v1.
802  Add(v1!.maximals,v2);
803  Add(v2!.maximalin,v1);
804  Connection(v1!.obj,v2!.obj,def);
805
806  return true;
807
808end);
809
810InstallOtherMethod( Edge,
811    "for a graphic poset, and two vertices",
812    true,
813    [ IsGraphicPosetRep, IsGPVertex, IsGPVertex ],
814    0,
815
816function( poset, v1, v2 )
817  return Edge(poset,v1,v2,rec());
818end);
819
820
821#############################################################################
822##
823##  Destructors:
824##
825#############################################################################
826
827
828#############################################################################
829##
830##  Set this variable temporarily to false if you delete many things!
831##
832GGDeleteModifiesMenu := true;
833
834
835#############################################################################
836##
837#M  Delete(<graph>,<obj>) . . . . . . . . . . . . . remove something in graph
838##
839##  This operation already exists in {\XGAP} for the graphic objects!
840##  Applicable for edges, vertices, classes.
841##
842##  The following method applies to an edge, given by two vertices. It returns
843##  fail if not one of the vertices is maximal in the other and true
844##  otherwise.
845InstallOtherMethod( Delete,
846    "for a graphic poset, and two vertices",
847    true,
848    [ IsGraphicPosetRep, IsGPVertex, IsGPVertex ],
849    0,
850
851function( poset, v1, v2 )
852  local   p,  dummy,  l;
853
854  # determine which is the "bigger one":
855  p := Position(v2!.maximals,v1);
856  if p = fail then
857    p := Position(v1!.maximals,v2);
858    if p = fail then
859      return fail;
860    fi;
861    # swap the vertices:
862    dummy := v1;
863    v1 := v2;
864    v2 := dummy;
865  fi;
866  # v1 is now maximal in v2 at position p in v2!.maximals
867
868  Disconnect(v1!.obj,v2!.obj);
869  l := Length(v2!.maximals);
870  v2!.maximals[p] := v2!.maximals[l];
871  Unbind(v2!.maximals[l]);
872  p := Position(v1!.maximalin,v2);
873  # fail is not an option here! If that happens we bomb out!
874  l := Length(v1!.maximalin);
875  v1!.maximalin[p] := v1!.maximalin[l];
876  Unbind(v1!.maximalin[l]);
877
878  # think about the menus:
879  if GGDeleteModifiesMenu then
880    ModifyEnabled(poset,1,Length(poset!.menus));
881  fi;
882
883  return true;
884end);
885
886##  The following method applies to a vertex. It returns fail if the vertex
887##  is not in the poset. The vertex is deleted and all connections to other
888##  vertices are also deleted! Returns true if vertex is successfully deleted.
889InstallOtherMethod( Delete,
890    "for a graphic poset, and a vertex",
891    true,
892    [ IsGraphicPosetRep, IsGPVertex ],
893    0,
894
895function( poset, v )
896  local   lp,  l,  cp,  cl,  p,  savemaximals,  savemaximalin,  noerror,
897          v1,  v2,  store;
898
899  lp := Position(poset!.levelparams,v!.levelparam);
900  if lp = fail then
901    return fail;
902  fi;
903  l := poset!.levels[lp];
904
905  cp := Position(l!.classparams,v!.classparam);
906  if cp = fail then
907    return fail;
908  fi;
909  cl := l!.classes[cp];
910
911  p := Position(cl,v);
912  if p = fail then
913    return fail;
914  fi;
915
916  # Remember all connections:
917  savemaximals := ShallowCopy(v!.maximals);
918  savemaximalin := ShallowCopy(v!.maximalin);
919
920  # Delete all connections:
921  noerror := true;
922  store := GGDeleteModifiesMenu;
923  GGDeleteModifiesMenu := false;
924  while v!.maximals <> [] do
925    if Delete(poset,v,v!.maximals[1]) = fail then
926      noerror := fail;
927    fi;
928  od;
929  while v!.maximalin <> [] do
930    if Delete(poset,v,v!.maximalin[1]) = fail then
931      noerror := fail;
932    fi;
933  od;
934  GGDeleteModifiesMenu := store;
935
936  # was it selected?
937  RemoveSet(poset!.selectedvertices,v);
938
939  # now delete vertex:
940  Delete(v!.obj);
941  ResetFilterObj(v,IsAlive);
942
943  l := Length(cl);
944  cl[p] := cl[l];
945  Unbind(cl[l]);
946
947  # now we have to add new inclusions from the maximal subobjects to those
948  # where our vertex was maximal in. We should not do that however, if there is
949  # already a way. This ensures that the diagram will be again a Hasse diagram
950  # of the remaining vertices with the inclusions induced by the poset
951  # before deletion.
952  for v1 in savemaximals do
953    for v2 in savemaximalin do
954      if not GPSearchWay(poset,v2,v1,
955                         Position(poset!.levelparams,v1!.levelparam)) then
956        Edge(poset,v2,v1);
957      fi;
958    od;
959  od;
960
961  # think about the menus:
962  if GGDeleteModifiesMenu then
963    ModifyEnabled(poset,1,Length(poset!.menus));
964  fi;
965
966  return noerror;
967end);
968
969##  The following method applies to a class. It returns fail if the class
970##  is not in the poset. The class is deleted and all vertices including
971##  their connections to other vertices are also deleted! Returns true
972##  if class is successfully deleted.
973##  The two parameters are a level parameter and a class parameter.
974InstallOtherMethod( Delete,
975    "for a graphic poset, and two objects",
976    true,
977    [ IsGraphicPosetRep, IsObject, IsObject ],
978    0,
979
980function( poset, levelparam, classparam )
981  local   lp,  l,  cp,  noerror,  v,  store;
982
983  lp := Position(poset!.levelparams,levelparam);
984  if lp = fail then
985    return fail;
986  fi;
987  l := poset!.levels[lp];
988
989  cp := Position(l!.classparams,classparam);
990  if cp = fail then
991    return fail;
992  fi;
993
994  # delete all vertices:
995  noerror := true;
996  store := GGDeleteModifiesMenu;
997  GGDeleteModifiesMenu := false;
998  for v in l!.classes[cp] do
999    if Delete(poset,v) = fail then
1000      noerror := fail;
1001    fi;
1002  od;
1003  GGDeleteModifiesMenu := store;
1004
1005  lp := Length(l!.classes);
1006  l!.classes[cp] := l!.classes[lp];
1007  Unbind(l!.classes[lp]);
1008  l!.classparams[cp] := l!.classparams[lp];
1009  Unbind(l!.classparams[lp]);
1010
1011  # think about the menus:
1012  if GGDeleteModifiesMenu then
1013    ModifyEnabled(poset,1,Length(poset!.menus));
1014  fi;
1015
1016  return noerror;
1017end);
1018
1019
1020#############################################################################
1021##
1022#M  DeleteLevel(<poset>,<levelparam>) . . . . . . . . . remove level in poset
1023##
1024##  The following method applies to a level. It returns `fail' if no level
1025##  with level parameter <levelparam> is in the poset. Otherwise the level
1026##  is deleted and all classes within it are also deleted! `DeleteLevel'
1027##  returns `true' if the level is successfully deleted.
1028##
1029InstallOtherMethod( DeleteLevel,
1030    "for a graphic poset, and an object",
1031    true,
1032    [ IsGraphicPosetRep, IsObject ],
1033    0,
1034
1035function( poset, levelparam )
1036  local   lp,  noerror,  cl,  v,  l,  lev,  store;
1037
1038  lp := Position(poset!.levelparams,levelparam);
1039  if lp = fail then
1040    return fail;
1041  fi;
1042
1043  # delete all vertices:
1044  noerror := true;
1045  store := GGDeleteModifiesMenu;
1046  GGDeleteModifiesMenu := false;
1047  for cl in poset!.levels[lp]!.classes do
1048    while cl <> [] do
1049      if Delete(poset,cl[1]) = fail then
1050        noerror := fail;
1051      fi;
1052    od;
1053  od;
1054  GGDeleteModifiesMenu := store;
1055
1056  l := Length(poset!.levels);
1057  # now we have to move all lower levels up:
1058  FastUpdate(poset,true);
1059  for lev in [lp+1..l] do
1060    poset!.levels[lev]!.top := poset!.levels[lev]!.top
1061                               - poset!.levels[lp]!.height;
1062    for cl in poset!.levels[lev]!.classes do
1063      for v in cl do
1064        Move(poset,v,v!.x,v!.y);
1065      od;
1066    od;
1067    if IsAlive(poset!.levelboxes[lev]) then
1068      MoveDelta(poset!.levelboxes[lev],0,-poset!.levels[lp]!.height);
1069    fi;
1070    if IsAlive(poset!.lptexts[lev]) then
1071      MoveDelta(poset!.lptexts[lev],0,-poset!.levels[lp]!.height);
1072    fi;
1073  od;
1074  FastUpdate(poset,false);
1075  poset!.levels{[lp..l-1]} := poset!.levels{[lp+1..l]};
1076  Unbind(poset!.levels[l]);
1077  poset!.levelparams{[lp..l-1]} := poset!.levelparams{[lp+1..l]};
1078  Unbind(poset!.levelparams[l]);
1079  if IsAlive(poset!.levelboxes[lp]) then
1080    Delete(poset,poset!.levelboxes[lp]);
1081  fi;
1082  poset!.levelboxes{[lp..l-1]} := poset!.levelboxes{[lp+1..l]};
1083  Unbind(poset!.levelboxes[l]);
1084  if IsAlive(poset!.lptexts[lp]) then
1085    Delete(poset,poset!.lptexts[lp]);
1086  fi;
1087  poset!.lptexts{[lp..l-1]} := poset!.lptexts{[lp+1..l]};
1088  Unbind(poset!.lptexts[l]);
1089
1090  # think about the menus:
1091  if GGDeleteModifiesMenu then
1092    ModifyEnabled(poset,1,Length(poset!.menus));
1093  fi;
1094
1095  return noerror;
1096end);
1097
1098
1099#############################################################################
1100##
1101##  Modification methods:
1102##
1103#############################################################################
1104
1105
1106#############################################################################
1107##
1108#M  ResizeLevel(<poset>,<levelparam>,<height>)  . . .  change height of level
1109##
1110##  Changes the height of a level. The y coordinate can only be changed by
1111##  permuting levels, see below.
1112##  Attention: can increase the size of the sheet!
1113##  Returns fail if no level with parameter levelparam exists and true
1114##  otherwise.
1115##
1116InstallOtherMethod( ResizeLevel,
1117    "for a graphic poset, an object, and an integer",
1118    true,
1119    [ IsGraphicPosetRep, IsObject, IsInt ],
1120    0,
1121
1122function( poset, levelparam, height )
1123  local   lp,  l,  cl,  v,  dist,  len;
1124
1125  lp := Position(poset!.levelparams,levelparam);
1126  if lp = fail then
1127    return fail;
1128  fi;
1129  l := poset!.levels[lp];
1130
1131  if height < VERTEX.diameter then
1132    height := VERTEX.diameter;
1133  fi;
1134
1135  if height = l!.height then
1136    return true;
1137  elif height < l!.height then
1138    # move all vertices within level into the new range
1139    FastUpdate(poset,true);
1140    for cl in l!.classes do
1141      for v in cl do
1142        if v!.y > height-VERTEX.radius then
1143          v!.y := height-VERTEX.radius;
1144          Move(v!.obj,v!.x,v!.y + l!.top);
1145        fi;
1146      od;
1147    od;
1148
1149    # now move all lower levels up:
1150    dist := height - l!.height;
1151    l!.height := height;
1152
1153    # move level box and text:
1154    if poset!.showlevels then
1155      Move(poset!.levelboxes[lp],0,l!.top + l!.height - 8);
1156    fi;
1157    if poset!.showlevelparams then
1158      Move(poset!.lptexts[lp],poset!.lptexts[lp]!.x,
1159           l!.top + QuoInt(l!.height,2));
1160    fi;
1161    FastUpdate(poset,false);
1162
1163  else   # height > l!.height
1164    dist := height - l!.height;
1165    l!.height := height;
1166
1167    # do we have to increase height of sheet?
1168    len := Length(poset!.levels);
1169    if poset!.levels[len]!.top + poset!.levels[len]!.height + dist
1170       > poset!.height then
1171      Resize(poset,poset!.width,
1172             poset!.levels[len]!.top + poset!.levels[len]!.height + dist);
1173    fi;
1174
1175    if poset!.showlevels then
1176      Move(poset!.levelboxes[lp],0,l!.top + l!.height - 8);
1177    fi;
1178    if poset!.showlevelparams then
1179      Move(poset!.lptexts[lp],poset!.lptexts[lp]!.x,
1180           l!.top + QuoInt(l!.height,2));
1181    fi;
1182
1183    # next move down all the levels below the increased level:
1184  fi;
1185
1186  FastUpdate(poset,true);
1187  for l in [lp+1..Length(poset!.levels)] do
1188    poset!.levels[l]!.top := poset!.levels[l]!.top + dist;
1189    for cl in poset!.levels[l]!.classes do
1190      for v in cl do
1191        MoveDelta(v!.obj,0,dist);
1192      od;
1193    od;
1194    # move level box:
1195    if poset!.showlevels then
1196      MoveDelta(poset!.levelboxes[l],0,dist);
1197    fi;
1198    if poset!.showlevelparams then
1199      MoveDelta(poset!.lptexts[l],0,dist);
1200    fi;
1201  od;
1202  FastUpdate(poset,false);
1203end);
1204
1205
1206#############################################################################
1207##
1208#M  MoveLevel(<poset>,<levelparam>,<position>) move level to another position
1209##
1210##  Moves a level to another position. <position> is an absolute index in
1211##  the list of levels. The level with parameter <levelparam> will be at the
1212##  position <position> after the operation. This is only allowed if the
1213##  new ordering is compatible with the partial order given by CompareLevels
1214##  and if there is no connection of a vertex in the moving level with
1215##  another level with which it is interchanged.
1216##  So <levelparam> is compared with all levelparams between the old and
1217##  the new position. If there is a contradiction nothing happens and the
1218##  method returns fail. If everything works the operation returns true.
1219##  This operation already exists in {\XGAP} for graphic objects.
1220##
1221InstallOtherMethod( MoveLevel,
1222    "for a graphic poset, an object, and an integer",
1223    true,
1224    [ IsGraphicPosetRep, IsObject, IsInt ],
1225    0,
1226
1227function( poset, levelparam, position )
1228  local   lp,  i,  compare,  cl,  v,  v2,  p,  list;
1229  # nonsense position?
1230  if position < 1 or position > Length(poset!.levels) then
1231    return fail;
1232  fi;
1233
1234  # does level exist?
1235  lp := Position(poset!.levelparams,levelparam);
1236  if lp = fail then
1237    return fail;
1238  fi;
1239
1240  # nothing to do?
1241  if position = lp then
1242    return true;  # we are done
1243  fi;
1244
1245  if position < lp then   # move level UP
1246    # check with partial ordering:
1247    for i in [position..lp-1] do
1248      compare := CompareLevels(poset,poset!.levelparams[i],levelparam);
1249      if compare <> fail and compare < 0 then
1250        # that would contradict the partial order
1251        return fail;
1252      fi;
1253    od;
1254
1255    # now check vertices:
1256    for cl in poset!.levels[lp]!.classes do
1257      for v in cl do
1258        for v2 in v!.maximalin do
1259          p := Position(poset!.levelparams,v2!.levelparam);
1260          if p >= position then  # < lp is a MUST!
1261            return fail;
1262          fi;
1263        od;
1264      od;
1265    od;
1266
1267    # OK, we can do it:
1268    FastUpdate(poset,true);
1269    list := Concatenation([lp],[position..lp-1]);
1270    poset!.levels{[position..lp]} := poset!.levels{list};
1271    poset!.levelparams{[position..lp]} := poset!.levelparams{list};
1272    poset!.levelboxes{[position..lp]} := poset!.levelboxes{list};
1273    poset!.lptexts{[position..lp]} := poset!.lptexts{list};
1274    poset!.levels[position]!.top := poset!.levels[position+1]!.top;
1275    if poset!.showlevels then
1276      Move(poset!.levelboxes[position],0,poset!.levels[position]!.top
1277                                  + poset!.levels[position]!.height - 8);
1278    fi;
1279    if poset!.showlevelparams then
1280      Move(poset!.lptexts[position],poset!.lptexts[position]!.x,
1281           poset!.levels[position]!.top +
1282           QuoInt(poset!.levels[position]!.height,2));
1283    fi;
1284
1285    for cl in poset!.levels[position]!.classes do
1286      for v in cl do
1287        Move(poset,v,v!.x,v!.y);
1288      od;
1289    od;
1290    for i in [position+1..lp] do
1291      poset!.levels[i]!.top := poset!.levels[i]!.top
1292                               + poset!.levels[position]!.height;
1293
1294      if poset!.showlevels then
1295        Move(poset!.levelboxes[i],0,poset!.levels[i]!.top
1296                                    + poset!.levels[i]!.height - 8);
1297      fi;
1298      if poset!.showlevelparams then
1299        Move(poset!.lptexts[i],poset!.lptexts[i]!.x,
1300             poset!.levels[i]!.top + QuoInt(poset!.levels[i]!.height,2));
1301      fi;
1302      for cl in poset!.levels[i]!.classes do
1303        for v in cl do
1304          Move(poset,v,v!.x,v!.y);
1305        od;
1306      od;
1307    od;
1308    # in case another one has overwritten our box:
1309    if poset!.showlevels then
1310      Draw(poset!.levelboxes[position]);
1311    fi;
1312    if poset!.showlevelparams then
1313      Draw(poset!.lptexts[position]);
1314    fi;
1315    FastUpdate(poset,false);
1316
1317    # we did it.
1318  else   # position > lp, move level DOWN
1319    # check with partial ordering:
1320    for i in [lp+1..position] do
1321      compare := CompareLevels(poset,poset!.levelparams[i],levelparam);
1322      if compare <> fail and compare > 0 then
1323        # that would contradict the partial order
1324        return fail;
1325      fi;
1326    od;
1327
1328    # now check vertices:
1329    for cl in poset!.levels[lp]!.classes do
1330      for v in cl do
1331        for v2 in v!.maximals do
1332          p := Position(poset!.levelparams,v2!.levelparam);
1333          if p <= position then  # > lp is a MUST!
1334            return fail;
1335          fi;
1336        od;
1337      od;
1338    od;
1339
1340    # OK, we can do it:
1341    FastUpdate(poset,true);
1342    list := Concatenation([lp+1..position],[lp]);
1343    poset!.levels{[lp..position]} := poset!.levels{list};
1344    poset!.levelparams{[lp..position]} := poset!.levelparams{list};
1345    poset!.levelboxes{[lp..position]} := poset!.levelboxes{list};
1346    poset!.lptexts{[lp..position]} := poset!.lptexts{list};
1347    poset!.levels[position]!.top := poset!.levels[position-1]!.top
1348                                  - poset!.levels[position]!.height
1349                                    + poset!.levels[position-1]!.height;
1350    if poset!.showlevels then
1351      Move(poset!.levelboxes[position],0,poset!.levels[position]!.top
1352                                       + poset!.levels[position]!.height - 8);
1353    fi;
1354    if poset!.showlevelparams then
1355      Move(poset!.lptexts[position],poset!.lptexts[position]!.x,
1356           poset!.levels[position]!.top +
1357           QuoInt(poset!.levels[position]!.height,2));
1358    fi;
1359    for cl in poset!.levels[position]!.classes do
1360      for v in cl do
1361        Move(poset,v,v!.x,v!.y);
1362      od;
1363    od;
1364    for i in [lp..position-1] do
1365      poset!.levels[i]!.top := poset!.levels[i]!.top
1366                             - poset!.levels[position]!.height;
1367      if poset!.showlevels then
1368        Move(poset!.levelboxes[i],0,poset!.levels[i]!.top
1369                                           + poset!.levels[i]!.height - 8);
1370      fi;
1371      if poset!.showlevelparams then
1372        Move(poset!.lptexts[i],poset!.lptexts[i]!.x,
1373             poset!.levels[i]!.top + QuoInt(poset!.levels[i]!.height,2));
1374      fi;
1375      for cl in poset!.levels[i]!.classes do
1376        for v in cl do
1377          Move(poset,v,v!.x,v!.y);
1378        od;
1379      od;
1380    od;
1381    # in case another one has overwritten our box:
1382    if poset!.showlevels then
1383      Draw(poset!.levelboxes[position]);
1384    fi;
1385    if poset!.showlevelparams then
1386      Draw(poset!.lptexts[position]);
1387    fi;
1388    FastUpdate(poset,false);
1389
1390    # we did it.
1391  fi;
1392
1393  return true;
1394end);
1395
1396
1397#############################################################################
1398##
1399#M  Relabel(<graph>,<vertex>,<label>)  . . . . . . . . change label of vertex
1400#M  Relabel(<graph>,<vertex>)  . . . . . . . . . . . . change label of vertex
1401#M  Relabel(<poset>,<vertex1>,<vertex2>,<label>) . . . . change label of edge
1402#M  Relabel(<poset>,<vertex1>,<vertex2>) . . . . . . . . change label of edge
1403##
1404##  Changes the label of the vertex <vertex> or the edge between <vertex1>
1405##  and <vertex2>. This must be a short string. In the method where no
1406##  label is specified the new label is chosen functionally: the operation
1407##  `ChooseLabel' is called. `Relabel' returns `fail' if an error occurs
1408##  and `true' otherwise.  This operations already exists in {\XGAP} for
1409##  graphic objects.
1410##
1411InstallOtherMethod( Relabel,
1412    "for a graphic graph, a vertex, and a string",
1413    true,
1414    [ IsGraphicGraphRep, IsGGVertex, IsString ],
1415    0,
1416
1417function( graph, vertex, label )
1418  if label = "" then
1419    label := false;
1420  fi;
1421  # we just call the low level routines:
1422  vertex!.label := label;
1423  Relabel(vertex!.obj,label);
1424end);
1425
1426InstallOtherMethod( Relabel,
1427    "for a graphic graph, and a vertex",
1428    true,
1429    [ IsGraphicGraphRep, IsGGVertex ],
1430    0,
1431
1432function( graph, vertex)
1433  local label;
1434
1435  label := ChooseLabel( graph, vertex!.data );
1436  if label = "" then
1437    label := false;
1438  fi;
1439  # we just call the low level routines:
1440  vertex!.label := label;
1441  Relabel(vertex!.obj,label);
1442end);
1443
1444InstallOtherMethod( Relabel,
1445    "for a graphic poset, two vertices, and a string",
1446    true,
1447    [ IsGraphicPosetRep, IsGPVertex, IsGPVertex, IsString ],
1448    0,
1449
1450function( poset, v1, v2, label )
1451  local   p;
1452  p := Position(v1!.maximals,v2);
1453  if p = fail then
1454    p := Position(v2!.maximals,v1);
1455    if p = fail then
1456      return fail;
1457    fi;
1458  fi;
1459  # we know now that there is a connection!
1460  p := Position(v1!.obj!.connections,v2!.obj);
1461
1462  if label = "" then
1463    label := false;
1464  fi;
1465
1466  # now we just call the low level routines:
1467  Relabel(v1!.obj!.connectingLines[p],label);
1468end);
1469
1470InstallOtherMethod( Relabel,
1471    "for a graphic poset, and two vertices",
1472    true,
1473    [ IsGraphicPosetRep, IsGPVertex, IsGPVertex ],
1474    0,
1475
1476function( poset, v1, v2)
1477  local   label;
1478
1479  label := ChooseLabel( poset, v1!.data, v2!.data );
1480  if label = "" then
1481    label := false;
1482  fi;
1483  # we just call the low level routines:
1484  Relabel(poset,v1,v2,label);
1485end);
1486
1487
1488#############################################################################
1489##
1490#M  Move(<graph>,<vertex>,<x>,<y>) . . . . . . . . . . . . . . .  move vertex
1491#M  Move(<graph>,<vertex>) . . . . . . . . . . . . . . . . . . .  move vertex
1492##
1493##  Moves vertex <vertex>. For posets coordinates are relative to the level
1494##  of the vertex. <vertex> must be a vertex object in <graph>. If no
1495##  coordinates are specified the operation `ChoosePosition' is
1496##  called. Returns `fail' if an error occurs and `true' otherwise.  This
1497##  operations already exists in {\XGAP} for graphic objects.
1498##
1499InstallOtherMethod( Move,
1500    "for a graphic poset, a vertex, and two integers",
1501    true,
1502    [ IsGraphicPosetRep, IsGPVertex, IsInt, IsInt ],
1503    0,
1504
1505function( poset, vertex, x, y )
1506  local l;
1507
1508  if x < VERTEX.radius then
1509    x := VERTEX.radius;
1510  elif x > poset!.width-VERTEX.radius then
1511    x := poset!.width-VERTEX.radius;
1512  fi;
1513  l := Position(poset!.levelparams,vertex!.levelparam);
1514  l := poset!.levels[l];
1515  if y < VERTEX.radius then
1516    y := VERTEX.radius;
1517  elif y > l!.height-VERTEX.radius then
1518    y := l!.height-VERTEX.radius;
1519  fi;
1520
1521  vertex!.x := x;
1522  vertex!.y := y;
1523  Move(vertex!.obj,x,y+l!.top);
1524
1525  return true;
1526end);
1527
1528InstallOtherMethod( Move,
1529    "for a graphic poset, and a vertex",
1530    true,
1531    [ IsGraphicPosetRep, IsGPVertex ],
1532    0,
1533
1534function( poset, vertex )
1535  local position;
1536
1537  position := ChoosePosition(poset, vertex!.data, vertex!.levelparam,
1538                             vertex!.classparam);
1539  Move(poset,vertex,position[1],position[2]);
1540end);
1541
1542
1543#############################################################################
1544##
1545#M  Reshape(<graph>,<vertex>,<shape>)  . . . . . . . . change shape of vertex
1546#M  Reshape(<graph>,<vertex>)  . . . . . . . . . . . . change shape of vertex
1547##
1548##  Changes the shape of the vertex <vertex>. <vertex> must be a vertex
1549##  object in the graph or poset <graph>. For the method where no shape is
1550##  specified the new shape is chosen functionally: `ChooseShape` is called
1551##  for the corresponding data.  `Reshape' returns `fail' if an error
1552##  occurs and `true' otherwise.  This operations already exists in {\XGAP}
1553##  for graphic objects.
1554##
1555InstallOtherMethod( Reshape,
1556    "for a graphic graph, a vertex, and a string",
1557    true,
1558    [ IsGraphicGraphRep, IsGGVertex, IsString ],
1559    0,
1560
1561function( graph, vertex, shape )
1562  if shape = "circle" then
1563    Reshape(vertex!.obj,VERTEX.circle);
1564  elif shape = "diamond" then
1565    Reshape(vertex!.obj,VERTEX.diamond);
1566  else
1567    Reshape(vertex!.obj,VERTEX.rectangle);
1568  fi;
1569  return true;
1570end);
1571
1572InstallOtherMethod( Reshape,
1573    "for a graphic graph, and a vertex",
1574    true,
1575    [ IsGraphicGraphRep, IsGGVertex ],
1576    0,
1577
1578function( graph, vertex )
1579  local shape;
1580
1581  shape := ChooseShape( graph, vertex!.data );
1582  Reshape(graph, vertex, shape);
1583  return true;
1584end);
1585
1586
1587#############################################################################
1588##
1589#M  Recolor(<graph>,<vertex>,<color>)  . . . . . . . . change color of vertex
1590#M  Recolor(<graph>,<vertex>)  . . . . . . . . . . . . change color of vertex
1591#M  Recolor(<poset>,<vertex1>,<vertex2>,<color>) . .  change color of an edge
1592#M  Recolor(<poset>,<vertex1>,<vertex2>) . . . . . .  change color of an edge
1593##
1594##  Changes the color of the vertex <vertex> or the edge between <vertex1>
1595##  and <vertex2>. <vertex> must be a vertex object in <graph>. For the
1596##  method where no color is specified the new color is chosen
1597##  functionally: `ChooseColor' is called for the corresponding
1598##  data. `Recolor' returns `fail' if an error occurs and `true'
1599##  otherwise. This operation already exists in {\XGAP} for graphic objects.
1600##
1601InstallOtherMethod( Recolor,
1602    "for a graphic graph, a vertex, and a color",
1603    true,
1604    [ IsGraphicGraphRep, IsGGVertex, IsColor ],
1605    0,
1606
1607function( graph, vertex, color )
1608  Recolor(vertex!.obj,color);
1609  return true;
1610end);
1611
1612InstallOtherMethod( Recolor,
1613    "for a graphic graph, and a vertex",
1614    true,
1615    [ IsGraphicGraphRep, IsGGVertex ],
1616    0,
1617
1618function( graph, vertex )
1619  local color;
1620
1621  color := ChooseColor( graph, vertex!.data );
1622  Recolor(graph, vertex, color);
1623  return true;
1624end);
1625
1626InstallOtherMethod( Recolor,
1627    "for a graphic poset, two vertices, and a color",
1628    true,
1629    [ IsGraphicPosetRep, IsGPVertex, IsGPVertex, IsColor ],
1630    0,
1631
1632function( poset, vertex1, vertex2, color )
1633  local   p;
1634  p := Position(vertex1!.maximals,vertex2);
1635  if p = fail then
1636    p := Position(vertex2!.maximals,vertex1);
1637    if p = fail then
1638      return fail;
1639    fi;
1640  fi;
1641  # we know now that there is a connection!
1642  p := Position(vertex1!.obj!.connections,vertex2!.obj);
1643  Recolor(vertex1!.obj!.connectingLines[p],color);
1644  return true;
1645end);
1646
1647InstallOtherMethod( Recolor,
1648    "for a graphic poset, and two vertices",
1649    true,
1650    [ IsGraphicPosetRep, IsGPVertex, IsGPVertex ],
1651    0,
1652
1653function( poset, vertex1, vertex2 )
1654  local   color;
1655
1656  color := ChooseColor( poset, vertex1!.data, vertex2!.data );
1657  return Recolor(poset, vertex1, vertex2, color);
1658end);
1659
1660
1661#############################################################################
1662##
1663#M  SetWidth(<graph>,<vertex1>,<vertex2>,<width>) . change line width of edge
1664#M  SetWidth(<graph>,<vertex1>,<vertex2>) . . . . . change line width of edge
1665##
1666##  Changes the line width of an edge. <vertex1> and <vertex2> must be
1667##  vertices in the graph <graph>. For the method where no line width is
1668##  specified the width is chosen functionally: `ChooseWidth' is called for
1669##  the corresponding data pair. Returns `fail' if an error occurs and
1670##  `true' otherwise. This operation already exists in {\XGAP} for graphic
1671##  objects.
1672##
1673InstallOtherMethod( SetWidth,
1674    "for a graphic poset, two vertices, and an integer",
1675    true,
1676    [ IsGraphicPosetRep, IsGPVertex, IsGPVertex, IsInt ],
1677    0,
1678
1679function( poset, vertex1, vertex2, width )
1680  local   p;
1681  p := Position(vertex1!.maximals,vertex2);
1682  if p = fail then
1683    p := Position(vertex2!.maximals,vertex1);
1684    if p = fail then
1685      return fail;
1686    fi;
1687  fi;
1688  # we know now that there is a connection!
1689  p := Position(vertex1!.obj!.connections,vertex2!.obj);
1690  SetWidth(vertex1!.obj!.connectingLines[p],width);
1691  return true;
1692end);
1693
1694InstallOtherMethod( SetWidth,
1695    "for a graphic poset, and two vertices",
1696    true,
1697    [ IsGraphicPosetRep, IsGPVertex, IsGPVertex ],
1698    0,
1699
1700function( poset, vertex1, vertex2 )
1701  local   width;
1702
1703  width := ChooseWidth( poset, vertex1!.data, vertex2!.data );
1704  return SetWidth(poset, vertex1, vertex2, width);
1705end);
1706
1707
1708#############################################################################
1709##
1710#M  Highlight(<graph>,<vertex>)  . . . . . . . change highlightning of vertex
1711#M  Highlight(<graph>,<vertex>,<flag>) . . . . change highlightning of vertex
1712##
1713##  Changes the highlighting status of the vertex <vertex>. <vertex> must
1714##  be a vertex object in <graph>. For the method where no flag is
1715##  specified the new status is chosen functionally: `ChooseHighlight' is
1716##  called for the corresponding data. Returns `fail' if an error occurs
1717##  and `true' otherwise. This operation already exists in {\XGAP} for
1718##  graphic objects.
1719##
1720InstallOtherMethod( Highlight,
1721    "for a graphic graph, a vertex, and a flag",
1722    true,
1723    [ IsGraphicGraphRep, IsGGVertex, IsBool ],
1724    0,
1725
1726function( graph, vertex, flag )
1727  Highlight(vertex!.obj,flag);
1728  return true;
1729end);
1730
1731InstallOtherMethod( Highlight,
1732    "for a graphic graph, and a vertex",
1733    true,
1734    [ IsGraphicGraphRep, IsGGVertex ],
1735    0,
1736
1737function( graph, vertex )
1738  local flag;
1739
1740  flag := ChooseHighlight( graph, vertex!.data );
1741  Highlight(graph, vertex, flag);
1742  return true;
1743end);
1744
1745
1746#############################################################################
1747##
1748##  Set this variable temporarily to false if you change many selections!
1749##
1750GGSelectModifiesMenu := true;
1751
1752
1753#############################################################################
1754##
1755#M  Select(<graph>,<vertex>,<flag>) . . . . . . . . . . (de-)selects a vertex
1756#M  Select(<graph>,<vertex>)  . . . . . . . . . . . . . . .  selects a vertex
1757##
1758##  Changes the selection state of the vertex <vertex>. <vertex> must be a
1759##  vertex object in <graph>. The flag determines whether the vertex
1760##  should be selected or deselected. This operation already exists in
1761##  {\XGAP} for graphic objects.  The method without flags assumes `true'.
1762##
1763InstallOtherMethod( Select,
1764    "for a graphic graph, a vertex, and a flag",
1765    true,
1766    [ IsGraphicGraphRep, IsGGVertex, IsBool ],
1767    0,
1768
1769function(graph,vertex,flag)
1770  local   p,  l;
1771  p := PositionSet(graph!.selectedvertices,vertex);
1772  if flag then
1773    if p <> fail then
1774      return;
1775    fi;
1776    Highlight(graph,vertex,true);
1777    Recolor(graph,vertex,graph!.color.selected);
1778    AddSet(graph!.selectedvertices,vertex);
1779  else
1780    if p = fail then
1781      return;
1782    fi;
1783    Highlight(graph,vertex,false);
1784    Recolor(graph,vertex,graph!.color.unselected);
1785    RemoveSet(graph!.selectedvertices,vertex);
1786  fi;
1787  if GGSelectModifiesMenu then
1788    ModifyEnabled(graph,1,Length(graph!.menus));
1789  fi;
1790  return;
1791end);
1792
1793InstallOtherMethod( Select,
1794    "for a graphic graph, and a vertex",
1795    true,
1796    [ IsGraphicGraphRep, IsGGVertex ],
1797    0,
1798
1799function(graph,vertex)
1800  Select(graph,vertex,true);
1801end);
1802
1803
1804#############################################################################
1805##
1806#M  DeselectAll(<graph>) . . . . . . . . . . . . . . . deselects all vertices
1807##
1808##  Deselects all vertices in graph.
1809##
1810InstallOtherMethod( DeselectAll,
1811    "for a graphic graph",
1812    true,
1813    [ IsGraphicGraphRep ],
1814    0,
1815
1816function(graph)
1817  local   v;
1818  for v in graph!.selectedvertices do
1819    Highlight(graph,v,false);
1820    Recolor(graph,v,graph!.color.unselected);
1821  od;
1822  graph!.selectedvertices := [];
1823end);
1824
1825
1826#############################################################################
1827##
1828#M  Selected(<graph>) . . . . . . . . .  returns set of all selected vertices
1829##
1830##  Returns a (shallow-)copy of the set of all selected vertices.
1831##
1832InstallOtherMethod( Selected,
1833    "for a graphic graph",
1834    true,
1835    [ IsGraphicGraphRep ],
1836    0,
1837
1838function(graph)
1839  return ShallowCopy(graph!.selectedvertices);
1840end);
1841
1842
1843#############################################################################
1844##
1845##  Methods for functional decisions:
1846##
1847#############################################################################
1848
1849
1850#############################################################################
1851##
1852#M  CompareLevels(<poset>,<levelp1>,<levelp2>) . . . compares two levelparams
1853##
1854##  Compare two levelparams. -1 means that levelp1 is "higher", 1 means
1855##  that levelp2 is "higher", 0 means that they are equal. fail means that
1856##  they are not comparable. This method is for the case if level
1857##  parameters are integers and lower values mean higher levels like in the
1858##  case of group lattices and subgroup indices.
1859##
1860InstallMethod( CompareLevels,
1861    "for a graphic poset, and two integers",
1862    true,
1863    [ IsGraphicPosetRep, IsInt, IsInt ],
1864    0,
1865
1866function( poset, l1, l2 )
1867  if l1 < l2 then
1868    return -1;
1869  elif l1 > l2 then
1870    return 1;
1871  else
1872    return 0;
1873  fi;
1874end);
1875
1876
1877#############################################################################
1878##
1879#M  ChooseLabel(<graph>,<data>) . . . . . . . is called while vertex creation
1880#M  ChooseLabel(<graph>,<data>,<data>)  . . . . is called while edge creation
1881##
1882##  This operation is called while vertex or edge creation, if the caller
1883##  didn't specify a label for the vertex or edge. It has to return a short
1884##  string which will be attached to the vertex. If it returns fail the new
1885##  vertex is not generated! This method just returns the empty string, so
1886##  no label is generated.
1887##  This method is also called in the Relabel method without label parameter.
1888##
1889InstallMethod( ChooseLabel,
1890    "for a graphic graph, and an object",
1891    true,
1892    [ IsGraphicGraphRep, IsObject ],
1893    0,
1894
1895function( graph, data )
1896  return "";
1897end);
1898
1899InstallOtherMethod( ChooseLabel,
1900    "for a graphic graph, and two objects",
1901    true,
1902    [ IsGraphicGraphRep, IsObject, IsObject ],
1903    0,
1904
1905function( poset, data1, data2 )
1906  return "";
1907end);
1908
1909
1910#############################################################################
1911##
1912#M  ChooseLevel(<poset>,<data>) . . . . . . . is called while vertex creation
1913##
1914##  This operation is called while vertex creation, if the caller didn't
1915##  specify a level where the vertex belongs to. It has to return a
1916##  levelparam which exists in the poset. If it returns fail the new vertex
1917##  is not generated!
1918##  This method just chooses the last, lowest level or fail, if there is no
1919##  level in the poset.
1920##
1921InstallMethod( ChooseLevel,
1922    "for a graphic poset, and an object",
1923    true,
1924    [ IsGraphicPosetRep, IsObject ],
1925    0,
1926
1927function( poset, data )
1928  local l;
1929  l := Length(poset!.levelparams);
1930  if l > 0 then
1931    return poset!.levelparams[Length(poset!.levelparams)];
1932  else
1933    return fail;
1934  fi;
1935end);
1936
1937
1938#############################################################################
1939##
1940#M  ChooseClass(<poset>,<data>,<levelp>) . .  is called while vertex creation
1941##
1942##  This operation is called while vertex creation, if the caller didn't
1943##  specify a class where the vertex belongs to. It has to return a
1944##  classparam which exists in the poset in levelp. If it returns fail the
1945##  new vertex is not generated!
1946##  This method just generates a new class in the level with classparam one
1947##  bigger than the maximum of all (integer) classparams. It returns fail if
1948##  this maximum is no integer.
1949##
1950InstallMethod( ChooseClass,
1951    "for a graphic graph, and two objects",
1952    true,
1953    [ IsGraphicPosetRep, IsObject, IsObject ],
1954    0,
1955
1956function( poset, data, levelparam )
1957  local l,m;
1958
1959  l := Position(poset!.levelparams,levelparam);
1960  if l = fail then
1961    return fail;
1962  fi;
1963  l := poset!.levels[l];
1964
1965  if l!.classparams = [] then
1966    return CreateClass(poset,levelparam,1);
1967  fi;
1968
1969  m := Maximum(l!.classparams);
1970  if not IsInt(m) then
1971    return fail;
1972  fi;
1973
1974  return CreateClass(poset,levelparam,m+1);
1975end);
1976
1977
1978#############################################################################
1979##
1980#M  ChooseShape(<graph>,<data>) . . . . . . . is called while vertex creation
1981##
1982##  This operation is called while vertex creation.
1983##  It has to return a string out of the following list:
1984##  "circle", "diamond", "rectangle"
1985##  If it returns fail the new vertex is not generated!
1986##  This method just returns "circle".
1987##
1988InstallMethod( ChooseShape,
1989    "for a graphic graph, and an object",
1990    true,
1991    [ IsGraphicGraphRep, IsObject ],
1992    0,
1993
1994function( graph, data )
1995  return "circle";
1996end);
1997
1998
1999#############################################################################
2000##
2001#M  ChooseWidth(<graph>,<data>) . . . . . . . is called while vertex creation
2002#M  ChooseWidth(<graph>,<data1>,<data2>)  . . . is called while edge creation
2003##
2004##  This operation is called while vertex or edge creation.
2005##  It has to return a line width.
2006##  If it returns fail the new vertex or edge is not generated!
2007##  This is also called by the SetWidth operation without width parameter.
2008##  This method just returns 1.
2009##
2010InstallOtherMethod( ChooseWidth,
2011    "for a graphic graph, and an object",
2012    true,
2013    [ IsGraphicGraphRep, IsObject ],
2014    0,
2015
2016function( graph, data )
2017  return 1;
2018end);
2019
2020InstallOtherMethod( ChooseWidth,
2021    "for a graphic graph, and two objects",
2022    true,
2023    [ IsGraphicGraphRep, IsObject, IsObject ],
2024    0,
2025
2026function( graph, data1, data2 )
2027  return 1;
2028end);
2029
2030
2031#############################################################################
2032##
2033#M  ChooseColor(<graph>,<data>) . . . . . . . is called while vertex creation
2034#M  ChooseColor(<graph>,<data1>,<data2>). . . . is called while edge creation
2035##
2036##  This operation is called while vertex or edge creation. It has to return a
2037##  color. If it returns fail the new vertex is not generated!
2038##  It is also called in the Recolor method without color parameter.
2039##  This method just returns black.
2040##
2041InstallMethod( ChooseColor,
2042    "for a graphic graph, and an object",
2043    true,
2044    [ IsGraphicGraphRep, IsObject ],
2045    0,
2046
2047function( graph, data )
2048  return COLORS.black;
2049end);
2050
2051InstallOtherMethod( ChooseColor,
2052    "for a graphic graph, and two objects",
2053    true,
2054    [ IsGraphicGraphRep, IsObject, IsObject ],
2055    0,
2056
2057function( graph, data1, data2 )
2058  return COLORS.black;
2059end);
2060
2061
2062#############################################################################
2063##
2064#M  ChooseHighlight(<graph>,<data>) . . . . . is called while vertex creation
2065##
2066##  This operation is called while vertex creation. It has to return a
2067##  flag which indicates, whether the vertex is highlighted or not. If it
2068##  returns fail the new vertex is not generated!
2069##  It is also called in the Highlight method without flag parameter.
2070##
2071##  The following method just returns false.
2072InstallMethod( ChooseHighlight,
2073    "for a graphic graph, and an object",
2074    true,
2075    [ IsGraphicGraphRep, IsObject ],
2076    0,
2077
2078function( graph, data )
2079  return false;
2080end);
2081
2082
2083#############################################################################
2084##
2085#M  ChoosePosition(<poset>,<data>,<level>,<class>)  . . . . . . . . . . . . .
2086#M  ChoosePosition(<graph>,<data>)  . . . . . is called while vertex creation
2087##
2088##  This operation is called while vertex creation.  It has to return a
2089##  list with two integers: the coordinates. For posets those are relative
2090##  to the level the vertex resides in.  If it returns fail the new vertex
2091##  is not generated!
2092##  This method positions a new vertex in a nonempty class next to the last
2093##  member in the class and a new vertex in a new class halfway to the
2094##  right end of the sheet from the rightmost vertex in the level or
2095##  halfway to the left end of the sheet from the leftmost vertex in the
2096##  class, depending where there is more space.
2097##
2098InstallMethod( ChoosePosition,
2099    "for a graphic poset, an object, a level object, a list, and a list",
2100    true,
2101    [ IsGraphicPosetRep, IsObject, IsGPLevel, IsList, IsList ],
2102    0,
2103
2104function( poset, data, level, class, hints )
2105  local   position,  ranges,  cl,  gaps,  maxindex,  i;
2106
2107  position := [];
2108  # not first in class:
2109  if class <> [] then
2110    # just near the others in the class:
2111    position[2] := class[Length(class)]!.y;
2112    position[1] := class[Length(class)]!.x + VERTEX.diameter + 2;
2113  else
2114    # collect all x ranges where classes reside:
2115    ranges := [[0,0]];
2116    for cl in level!.classes do
2117      if cl <> [] then
2118        Add(ranges,[cl[1]!.x-VERTEX.radius,cl[Length(cl)]!.x+VERTEX.radius]);
2119      fi;
2120    od;
2121    Add(ranges,[poset!.width,poset!.width]);
2122    ranges := Set(ranges);
2123    gaps := List([1..Length(ranges)-1],x->ranges[x+1][1]-ranges[x][2]);
2124
2125    # search largest gap:
2126    maxindex := 1;
2127    for i in [2..Length(gaps)] do
2128      if gaps[i] > gaps[maxindex] then
2129        maxindex := i;
2130      fi;
2131    od;
2132
2133    position[1] := QuoInt(ranges[maxindex][2]+ranges[maxindex+1][1],2);
2134    position[2] := QuoInt(level!.height,2);
2135  fi;
2136  return position;
2137end);
2138
2139
2140
2141#############################################################################
2142##
2143##  Methods for getting information:
2144##
2145#############################################################################
2146
2147
2148#############################################################################
2149##
2150#M  WhichLevel(<poset>,<y>) . . . . . .  determine level in which position is
2151##
2152##  Determines level in which position is. Returns levelparam or fail.
2153##
2154InstallMethod( WhichLevel,
2155    "for a graphic poset, and an integer",
2156    true,
2157    [ IsGraphicPosetRep, IsInt ],
2158    0,
2159
2160function( poset, y )
2161  local left, right, look;
2162
2163  if poset!.levels = [] or y < 0 or y >= poset!.height then
2164    return fail;
2165  fi;
2166
2167  # we do a binary search:
2168  left := 1;
2169  right := Length(poset!.levels);
2170  while left <= right do
2171    look := QuoInt(left+right,2);
2172    if y < poset!.levels[look]!.top then
2173      right := look-1;
2174    elif y >= poset!.levels[look]!.top + poset!.levels[look]!.height then
2175      left := look+1;
2176    else
2177      return poset!.levelparams[look];
2178    fi;
2179  od;
2180
2181  return fail;
2182end);
2183
2184
2185#############################################################################
2186##
2187#M  WhichClass(<poset>,<x>,<y>) . . . .  determine class in which position is
2188##
2189##  Determines a class with a vertex which contains the position. The first
2190##  class found is taken.  Returns list with levelparam as first and
2191##  classparam as second element.  Returns fail if no such class is found.
2192##
2193InstallMethod( WhichClass,
2194    "for a graphic poset, and two integers",
2195    true,
2196    [ IsGraphicPosetRep, IsInt, IsInt ],
2197    0,
2198
2199function(poset, x, y)
2200  local   lp,  l,  cl,  v;
2201
2202  # first determine the level:
2203  lp := WhichLevel(poset,y);
2204  l := Position(poset!.levelparam,l);
2205  l := poset!.levels[l];
2206
2207  # now search classes:
2208  for cl in [1..Length(l!.classes)] do
2209    for v in l!.classes[cl] do
2210      if [x,y] in v!.obj then
2211        return [lp,l!.classparams[cl]];
2212      fi;
2213    od;
2214  od;
2215
2216  return fail;
2217end);
2218
2219
2220#############################################################################
2221##
2222#M  WhichVertex(<graph>,<x>,<y>) . . .  determine vertex in which position is
2223#M  WhichVertex(<graph>,<data>)  . . . . .  determine vertex with data <data>
2224#M  WhichVertex(<graph>,<data>,<func>)   . . .  determine vertex functionally
2225##
2226##  Determines a vertex which contains the position.  Returns vertex.
2227##  In the third form the function func must take two parameters "data" and
2228##  the data entry of a vertex in question. It must return true or false,
2229##  according to the right vertex being found or not.
2230##  The function can for example consider just one record component of
2231##  data records.
2232##  Returns fail in case no vertex is found.
2233##
2234InstallOtherMethod( WhichVertex,
2235    "for a graphic poset, and two integers",
2236    true,
2237    [ IsGraphicPosetRep, IsInt, IsInt ],
2238    0,
2239
2240function(poset, x, y)
2241  local   lp,  l,  cl,  v;
2242
2243  # first determine the level:
2244  lp := WhichLevel(poset,y);
2245  l := Position(poset!.levelparams,lp);
2246  if l = fail then
2247    return fail;    # not even within a level
2248  fi;
2249  l := poset!.levels[l];
2250
2251  # now search classes:
2252  for cl in [1..Length(l!.classes)] do
2253    for v in l!.classes[cl] do
2254      if [x,y] in v!.obj then
2255        return v;
2256      fi;
2257    od;
2258  od;
2259
2260  return fail;
2261end);
2262
2263##  Method for a data object with comparison function:
2264##
2265InstallOtherMethod( WhichVertex,
2266    "for a graphic poset, an object, and a function",
2267    true,
2268    [ IsGraphicPosetRep, IsObject, IsFunction ],
2269    0,
2270
2271function(poset, data, func)
2272
2273  local   lp,  l,  cl,  v;
2274
2275  for lp in [1..Length(poset!.levels)] do
2276    l := poset!.levels[lp];
2277    for cl in [1..Length(l!.classes)] do
2278      for v in l!.classes[cl] do
2279        if func(data,v!.data) then
2280          return v;
2281        fi;
2282      od;
2283    od;
2284  od;
2285
2286  return fail;
2287end);
2288
2289##  Method for a data object:
2290##
2291InstallOtherMethod( WhichVertex,
2292    "for a graphic poset, and an object",
2293    true,
2294    [ IsGraphicPosetRep, IsObject ],
2295    0,
2296
2297function(poset, data)
2298
2299  local   lp,  l,  cl,  v;
2300
2301  for lp in [1..Length(poset!.levels)] do
2302    l := poset!.levels[lp];
2303    for cl in [1..Length(l!.classes)] do
2304      for v in l!.classes[cl] do
2305        if v!.data = data then
2306          return v;
2307        fi;
2308      od;
2309    od;
2310  od;
2311
2312  return fail;
2313end);
2314
2315
2316#############################################################################
2317##
2318#M  WhichVertices(<graph>,<x>,<y>) .  determine vertices in which position is
2319#M  WhichVertices(<graph>,<data>)  . . .  determine vertices with data <data>
2320#M  WhichVertices(<graph>,<data>,<func>) . .  determine vertices functionally
2321##
2322##  Determines the list of vertices which contain the position. Returns list.
2323##  In the third form the function func must take two parameters "data" and
2324##  the data entry of a vertex in question. It must return true or false,
2325##  according to the vertex belonging into the result or not.
2326##  The function can for example consider just one record component of
2327##  data records.
2328##  Returns the empty list in case no vertex is found.
2329##
2330InstallMethod( WhichVertices,
2331    "for a graphic poset, and two integers",
2332    true,
2333    [ IsGraphicPosetRep, IsInt, IsInt ],
2334    0,
2335
2336function(poset, x, y)
2337  local   lp,  l,  cl,  v, res;
2338
2339  # first determine the level:
2340  lp := WhichLevel(poset,y);
2341  l := Position(poset!.levelparams,lp);
2342  if l = fail then
2343    return fail;    # not even within a level
2344  fi;
2345  l := poset!.levels[l];
2346
2347  res := [];
2348  # now search classes:
2349  for cl in [1..Length(l!.classes)] do
2350    for v in l!.classes[cl] do
2351      if [x,y] in v!.obj then
2352        Add(res,v);
2353      fi;
2354    od;
2355  od;
2356
2357  return res;
2358end);
2359
2360##  Method for a data object with comparison function:
2361##
2362InstallOtherMethod( WhichVertices,
2363    "for a graphic poset, an object, and a function",
2364    true,
2365    [ IsGraphicPosetRep, IsObject, IsFunction ],
2366    0,
2367
2368function(poset, data, func)
2369
2370  local   lp,  l,  cl,  v,  res;
2371
2372  res := [];
2373  for lp in [1..Length(poset!.levels)] do
2374    l := poset!.levels[lp];
2375    for cl in [1..Length(l!.classes)] do
2376      for v in l!.classes[cl] do
2377        if func(data, v!.data) then
2378          Add(res,v);
2379        fi;
2380      od;
2381    od;
2382  od;
2383
2384  return res;
2385end);
2386
2387##  Method for a data object:
2388##
2389InstallOtherMethod( WhichVertices,
2390    "for a graphic poset, and an object",
2391    true,
2392    [ IsGraphicPosetRep, IsObject ],
2393    0,
2394
2395function(poset, data)
2396
2397  local   lp,  l,  cl,  v,  res;
2398
2399  res := [];
2400  for lp in [1..Length(poset!.levels)] do
2401    l := poset!.levels[lp];
2402    for cl in [1..Length(l!.classes)] do
2403      for v in l!.classes[cl] do
2404        if v!.data = data then
2405          Add(res,v);
2406        fi;
2407      od;
2408    od;
2409  od;
2410
2411  return res;
2412end);
2413
2414
2415#############################################################################
2416##
2417#M  Levels(<poset>) . . . . . . . . . . . . . returns the list of levelparams
2418##
2419##  Returns the list of levelparams in descending order meaning highest to
2420##  lowest.
2421##
2422InstallMethod( Levels,
2423    "for a graphic poset",
2424    true,
2425    [ IsGraphicPosetRep ],
2426    0,
2427
2428function(poset)
2429  return poset!.levelparams;
2430end);
2431
2432
2433#############################################################################
2434##
2435#M  Classes(<poset>,<levelparam>) . . . . . . returns the list of classparams
2436##
2437##  Returns the list of classparams in level levelparam. Returns fail if no
2438##  level with parameter <levelparam> occurs.
2439##
2440InstallMethod( Classes,
2441    "for a graphic poset, and an object",
2442    true,
2443    [ IsGraphicPosetRep, IsObject ],
2444    0,
2445
2446function(poset, levelparam)
2447  local l;
2448
2449  l := Position(poset!.levelparams,levelparam);
2450  if l = fail then
2451    return fail;
2452  fi;
2453  l := poset!.levels[l];
2454  return l!.classparams;
2455end);
2456
2457
2458#############################################################################
2459##
2460#M  Vertices(<poset>,<levelparam>,<classparam>)  . . . . . . returns vertices
2461##
2462##  Returns the list of vertices in class classparams in level
2463##  levelparam. Returns fail no level with paramter <levelparam> or no
2464##  class with parameter <classparam> in the level.
2465##
2466InstallMethod( Vertices,
2467    "for a graphic poset, and two objects",
2468    true,
2469    [ IsGraphicPosetRep, IsObject, IsObject ],
2470    0,
2471
2472function(poset, levelparam, classparam)
2473  local l, cl;
2474
2475  l := Position(poset!.levelparams,levelparam);
2476  if l = fail then
2477    return fail;
2478  fi;
2479  l := poset!.levels[l];
2480
2481  cl := Position(l!.classparams,classparam);
2482  if cl = fail then
2483    return fail;
2484  else
2485    return l!.classes[cl];
2486  fi;
2487end);
2488
2489
2490#############################################################################
2491##
2492#M  Maximals(<poset>,<vertex>) . . . . . . . . .  returns maximal subvertices
2493##
2494##  Returns the list of maximal subvertices in <vertex>. Returns fail if an
2495##  error occurs.
2496##
2497InstallMethod( Maximals,
2498    "for a graphic poset, and an object",
2499    true,
2500    [ IsGraphicPosetRep, IsObject ],
2501    0,
2502
2503function(poset, vertex)
2504  return vertex!.maximals;
2505end);
2506
2507
2508#############################################################################
2509##
2510#M  MaximalIn(<poset>,<vertex>) . .  returns vertices, in which v. is maximal
2511##
2512##  Returns the list of vertices, in which <vertex> is maximal.  Returns
2513##  fail if an error occurs.
2514##
2515InstallMethod( MaximalIn,
2516    "for a graphic poset, and an object",
2517    true,
2518    [ IsGraphicPosetRep, IsObject ],
2519    0,
2520
2521function(poset, vertex)
2522  return vertex!.maximalin;
2523end);
2524
2525
2526#############################################################################
2527##
2528#M  PositionLevel(<poset>,<levelparam>) . . . . . returns y position of level
2529##
2530##  Returns the y position of the level relative to the graphic
2531##  sheet and the height. Returns fail if no level with parameter
2532##  <levelparam> exists.
2533##
2534InstallMethod( PositionLevel,
2535    "for a graphic poset, and an object",
2536    true,
2537    [ IsGraphicPosetRep, IsObject ],
2538    0,
2539
2540function(poset, levelparam)
2541  local l;
2542
2543  l := Position(poset!.levelparams,levelparam);
2544  if l = fail then
2545    return fail;
2546  fi;
2547  return [poset!.levels[l]!.top,poset!.levels[l]!.height];
2548end);
2549
2550
2551
2552#############################################################################
2553##
2554##  Methods for menus and mouseclicks:
2555##
2556#############################################################################
2557
2558
2559#############################################################################
2560##
2561#M  InstallPopup(<graph>,<func>) . install function for right click on vertex
2562##
2563##  Installs a function that is called if the user clicks with the right
2564##  button on a vertex. The function gets as parameters:
2565##   poset,vertex,x,y        (click position)
2566##
2567InstallMethod( InstallPopup,
2568    "for a graphic graph, and a function",
2569    true,
2570    [ IsGraphicGraphRep, IsFunction ],
2571    0,
2572
2573function(graph, func)
2574  graph!.rightclickfunction := func;
2575end);
2576
2577
2578#############################################################################
2579##
2580#M  Menu(<graph>,<title>,<entrylist>,<typelist>,<functionslist>) . . new menu
2581##
2582##  This operation already exists in {\XGAP} for GraphicSheets.
2583##  Builts a new Menu but with information about the type of the menu entry.
2584##  This information describes the relation between the selection state of
2585##  the vertices and the parameters supplied to the functions. The following
2586##  types are supported:
2587##    "forany"    : always enabled, generic routines don't change anything
2588##    "forone"    : enabled iff exactly one vertex is selected
2589##    "fortwo"    : enabled iff exactly two vertices are selected
2590##    "forthree"  : enabled iff exactly three vertices are selected
2591##    "forsubset" : enabled iff at least one vertex is selected
2592##    "foredge"   : enabled iff a connected pair of two vertices is selected
2593##    "formin2"   : enabled iff at least two vertices are selected
2594##    "formin3"   : enabled iff at least three vertices are selected
2595##  The IsMenu object is returned. It is also stored in the sheet.
2596InstallOtherMethod( Menu,
2597    "for a graphic graph, a string, a list of strings, a list of strings, and a list of functions",
2598    true,
2599    [ IsGraphicGraphRep, IsString, IsList, IsList, IsList ],
2600    0,
2601
2602function(graph, title, entrylist, typelist, functionslist)
2603  local   l,  menu,  nr;
2604
2605  l := Filtered([1..Length(entrylist)],
2606                x->IsBound(entrylist[x]) and (entrylist[x][1] <> '-'));
2607  menu := Menu(graph,title,entrylist,functionslist);
2608  Add(graph!.menutypes,typelist{l});
2609  Add(graph!.menuenabled,List(l,x->true));
2610  nr := Length(graph!.menuenabled);
2611
2612  ModifyEnabled(graph,nr,nr);
2613
2614  return graph!.menus[nr];
2615end);
2616
2617
2618#############################################################################
2619##
2620#M  ModifyEnabled(<graph>,<from>,<to>) , . .  modifies enablednes of entries
2621##
2622##  Modifies the "Enabledness" of menu entries according to their type and
2623##  number of selected vertices. <from> is the first menu to work on and
2624##  <to> the last one (indices). Only IsAlive menus are considered. Returns
2625##  nothing.
2626##  There are two different methods for graphs and posets:
2627##
2628InstallMethod( ModifyEnabled,
2629    "for a graph, and two integers",
2630    true,
2631    [ IsGraphicGraphRep, IsInt, IsInt ],
2632    0,
2633
2634function(graph, from, to)
2635  local   len,  i,  j,  flag;
2636
2637  len := Length(graph!.selectedvertices);
2638  for i in [from..to] do
2639    if IsAlive(graph!.menus[i]) then
2640      for j in [1..Length(graph!.menutypes[i])] do
2641        if graph!.menutypes[i][j] = "forone" then
2642          flag := len = 1;
2643        elif graph!.menutypes[i][j] = "fortwo" then
2644          flag := len = 2;
2645        elif graph!.menutypes[i][j] = "forthree" then
2646          flag := len = 3;
2647        elif graph!.menutypes[i][j] = "forsubset" then
2648          flag := len >= 1;
2649        elif graph!.menutypes[i][j] = "foredge" then
2650          flag := false;
2651          if len = 2 then
2652            if Position(graph!.edges,graph!.selectedvertices) <> fail or
2653               Position(graph!.edges,Reversed(graph!.selectedvertices))
2654               <> fail then
2655              flag := true;
2656            fi;
2657          fi;
2658        elif graph!.menutypes[i][j] = "formin2" then
2659          flag := len >= 2;
2660        elif graph!.menutypes[i][j] = "formin3" then
2661          flag := len >= 3;
2662        else
2663          flag := true;
2664        fi;
2665        if graph!.menuenabled[i][j] <> flag then
2666          graph!.menuenabled[i][j] := flag;
2667          Enable(graph!.menus[i]!.entries[j],flag);
2668        fi;
2669      od;
2670    fi;
2671  od;
2672end);
2673
2674## Here follows nearly the same but: selected edges are different!
2675InstallMethod( ModifyEnabled,
2676    "for a poset, and two integers",
2677    true,
2678    [ IsGraphicPosetRep, IsInt, IsInt ],
2679    0,
2680
2681function(poset, from, to)
2682  local   len,  i,  j,  flag;
2683
2684  len := Length(poset!.selectedvertices);
2685  for i in [from..to] do
2686    if IsAlive(poset!.menus[i]) then
2687      for j in [1..Length(poset!.menutypes[i])] do
2688        if poset!.menutypes[i][j] = "forone" then
2689          flag := len = 1;
2690        elif poset!.menutypes[i][j] = "fortwo" then
2691          flag := len = 2;
2692        elif poset!.menutypes[i][j] = "forthree" then
2693          flag := len = 3;
2694        elif poset!.menutypes[i][j] = "forsubset" then
2695          flag := len >= 1;
2696        elif poset!.menutypes[i][j] = "foredge" then
2697          flag := false;
2698          if len = 2 then
2699            if Position(poset!.selectedvertices[1]!.maximals,
2700                        poset!.selectedvertices[2]) <> fail or
2701               Position(poset!.selectedvertices[2]!.maximals,
2702                        poset!.selectedvertices[1]) <> fail then
2703              flag := true;
2704            fi;
2705          fi;
2706        elif poset!.menutypes[i][j] = "formin2" then
2707          flag := len >= 2;
2708        elif poset!.menutypes[i][j] = "formin3" then
2709          flag := len >= 3;
2710        else   # "forany"
2711          flag := true;
2712        fi;
2713        if poset!.menuenabled[i][j] <> flag then
2714          poset!.menuenabled[i][j] := flag;
2715          Enable(poset!.menus[i],poset!.menus[i]!.entries[j],flag);
2716        fi;
2717      od;
2718    fi;
2719  od;
2720end);
2721
2722
2723#############################################################################
2724##
2725##  Methods for actual user interaction:
2726##
2727#############################################################################
2728
2729
2730#############################################################################
2731##
2732#M  PosetLeftClick(poset,x,y) . . . . method which is called after left click
2733##
2734##  This method is called when the user does a left click in a poset. It lets
2735##  the user move, select and deselect vertices or edges.
2736##  Edges are selected as pair of vertices.
2737##
2738InstallMethod(PosetLeftClick,
2739    "for a graph, and two integers",
2740    true,
2741    [ IsGraphicGraphRep, IsInt, IsInt ],
2742    0,
2743
2744function(poset,x,y)
2745
2746  local   v,  lp,  lev,  cp,  cl,  list,  minx,  maxx,  storex,  storey,  v2,
2747          lno,  line,  limit,  box,  bx,  bw,  by,  bh;
2748
2749  # is this a click on a vertex?
2750  v := WhichVertex(poset,x,y);
2751  if v <> fail then
2752
2753    # yes! search for level:
2754    lp := v!.levelparam;
2755    lev := poset!.levels[Position(poset!.levelparams,lp)];
2756
2757    # now we search for the class:
2758    cp := v!.classparam;
2759    cl := lev!.classes[Position(lev!.classparams,cp)];
2760
2761    # we search for minimum and maximum x coordinates, rel. to mouse:
2762    list := List(cl,v->v!.x);
2763    minx := Minimum(list) - x;
2764    maxx := Maximum(list) - x;
2765
2766    storex := v!.x;
2767    storey := v!.y;
2768
2769    if Drag(poset,x,y,BUTTONS.left,
2770            function(x,y)
2771              if x + minx < VERTEX.radius then
2772                x := VERTEX.radius - minx;
2773              elif x + maxx > poset!.width-VERTEX.radius then
2774                x := poset!.width-VERTEX.radius-maxx;
2775              fi;
2776              if y < lev!.top+VERTEX.radius then
2777                y := lev!.top + VERTEX.radius;
2778              elif y > lev!.top+lev!.height-VERTEX.radius then
2779                y := lev!.top+lev!.height-VERTEX.radius;
2780              fi;
2781	      Move(poset,v,x,y-lev!.top);
2782            end) then
2783      for v2 in cl do
2784        if v <> v2 then
2785          Move(poset,v2,v2!.x + v!.x - storex,v2!.y + v!.y - storey);
2786        fi;
2787      od;
2788      # better we redraw:
2789      DoRedraw(poset);
2790    else
2791      DeselectAll(poset);
2792      Select(poset,v,true);
2793    fi;
2794  else  # no click on a vertex, so we drag a box:
2795    # if this is a poset then we check if somebody clicked on a level box:
2796    if IsGraphicPosetRep(poset) then
2797      if poset!.showlevels and x < 8 then
2798        lno := First([1..Length(poset!.levelboxes)],
2799                     i->([x,y] in poset!.levelboxes[i]));
2800        if lno <> fail then
2801          # user clicked on the levelbox no lno, he can now resize this level
2802          line := Line(poset,0,y,poset!.width,0);
2803          if COLORS.blue <> false then
2804            Recolor(line,COLORS.blue);
2805          fi;
2806          limit := poset!.levels[lno]!.top + VERTEX.diameter;
2807          if Drag(poset,x,y,BUTTONS.left,
2808                  function(x,y)
2809                    if y < limit then
2810                      y := limit;
2811                    fi;
2812                    Move(line,0,y);
2813                  end) then
2814            # the user moved the line! the new y coordinate is the new lower
2815            # limit of the level!
2816            Delete(poset,line);
2817            ResizeLevel(poset,poset!.levelparams[lno],line!.y
2818                              - poset!.levels[lno]!.top);
2819          else
2820            Delete(poset,line);
2821          fi;
2822          return;
2823        fi;
2824      fi;
2825    fi;
2826    storex := x;
2827    storey := y;
2828    box := Rectangle(poset,x,y,0,0);
2829    if Drag(poset,x,y,BUTTONS.left,
2830            function(x,y)
2831              local bx,by,bw,bh;
2832              if x < storex then
2833                bx := x;
2834                bw := storex - x;
2835              else
2836                bx := storex;
2837                bw := x - storex;
2838              fi;
2839              if y < storey then
2840                by := y;
2841                bh := storey - y;
2842              else
2843                by := storey;
2844                bh := y - storey;
2845              fi;
2846              if bx <> box!.x or by <> box!.y then
2847                Move(box,bx,by);
2848              fi;
2849              if bw <> box!.w or bh <> box!.h then
2850                Reshape(box,bw,bh);
2851              fi;
2852            end) then
2853      # the box had at one time at least a certain size
2854      if box!.w > 0 and box!.h > 0 then
2855        DeselectAll(poset);
2856        GGSelectModifiesMenu := false;
2857        for lev in poset!.levels do
2858          if lev!.top < box!.y+box!.h and
2859             lev!.top + lev!.height >= box!.y then
2860            for cl in lev!.classes do
2861              for v in cl do
2862                if [v!.x,v!.y+lev!.top] in box then
2863                  Select(poset,v,true);
2864                fi;
2865              od;
2866            od;
2867          fi;
2868        od;
2869        GGSelectModifiesMenu := true;
2870        ModifyEnabled(poset,1,Length(poset!.menus));
2871      fi;
2872      Delete(poset,box);
2873      # better we redraw:
2874      DoRedraw(poset);
2875    else  # no moving, so user wants to deselect all vertices
2876      DeselectAll(poset);
2877      ModifyEnabled(poset,1,Length(poset!.menus));
2878      Delete(poset,box);
2879    fi;   # Drag(...) --> true
2880  fi;
2881end);
2882
2883
2884#############################################################################
2885##
2886#M  PosetCtrlLeftClick(poset,x,y) . . method which is called after left click
2887##
2888##  This operation is called when the user does a left click in a poset while
2889##  holding down the control key. It lets the user move, select and deselect
2890##  vertices or edges. The difference to the operation without the control
2891##  key is, that while selecting the old vertices are NOT deselected.
2892##  Moving does not move the whole class but only one vertex. This allows
2893##  for permuting the vertices within a class.
2894##  Edges are selected as pair of vertices.
2895##
2896InstallMethod(PosetCtrlLeftClick,
2897    "for a graph, and two integers",
2898    true,
2899    [ IsGraphicGraphRep, IsInt, IsInt ],
2900    0,
2901
2902function(poset,x,y)
2903
2904  local   v,  lp,  lev,  cp,  cl,  storex,  storey,  lno,  box,  levellen,
2905          pos,  bx,  bw,  by,  bh;
2906
2907  # is this a click on a vertex?
2908  v := WhichVertex(poset,x,y);
2909  if v <> fail then
2910
2911    # yes! search for level:
2912    lp := v!.levelparam;
2913    lev := poset!.levels[Position(poset!.levelparams,lp)];
2914
2915    # now we search for the class:
2916    cp := v!.classparam;
2917    cl := lev!.classes[Position(lev!.classparams,cp)];
2918
2919    storex := v!.x;
2920    storey := v!.y;
2921
2922    if not Drag(poset,x,y,BUTTONS.left,
2923            function(x,y)
2924              if x < VERTEX.radius then
2925                x := VERTEX.radius;
2926              elif x > poset!.width-VERTEX.radius then
2927                x := poset!.width-VERTEX.radius;
2928              fi;
2929              if y < lev!.top+VERTEX.radius then
2930                y := lev!.top + VERTEX.radius;
2931              elif y > lev!.top+lev!.height-VERTEX.radius then
2932                y := lev!.top+lev!.height-VERTEX.radius;
2933              fi;
2934	      Move(poset,v,x,y-lev!.top);
2935            end) then
2936      Select(poset,v,PositionSet(poset!.selectedvertices,v) = fail);
2937    else
2938      # better we redraw:
2939      DoRedraw(poset);
2940    fi;
2941  else  # no click on a vertex, so we drag a box:
2942    # if this is a poset then we check if somebody clicked on a level box:
2943    if IsGraphicPosetRep(poset) then
2944      if poset!.showlevels and x < 8 then
2945        lno := First([1..Length(poset!.levelboxes)],
2946                     i->([x,y] in poset!.levelboxes[i]));
2947        if lno <> fail then
2948          # user clicked on the levelbox no lno, he can now move this level
2949          box := Box(poset,4,y-8,8,8);
2950          if COLORS.red <> false then
2951            Recolor(box,COLORS.red);
2952          fi;
2953          levellen := Length(poset!.levels);
2954          if Drag(poset,x,y,BUTTONS.left,
2955                  function(x,y)
2956                    if y < 8 then
2957                      y := 8;
2958                    elif y > poset!.levels[levellen]!.top
2959                             + poset!.levels[levellen]!.height then
2960                      y := poset!.levels[levellen]!.top
2961                           + poset!.levels[levellen]!.height;
2962                    fi;
2963                    Move(box,4,y-8);
2964                  end) then
2965            # the user moved the box! we have to search in which level lies
2966            # the new y coordinate:
2967            pos := First([levellen,levellen-1..1],
2968                         i->box!.y >= poset!.levels[i]!.top);
2969            MoveLevel(poset,poset!.levelparams[lno],pos);
2970          fi;
2971          Delete(poset,box);
2972          return;
2973        fi;
2974      fi;
2975    fi;
2976    storex := x;
2977    storey := y;
2978    box := Rectangle(poset,x,y,0,0);
2979    if Drag(poset,x,y,BUTTONS.left,
2980            function(x,y)
2981              local bx,by,bw,bh;
2982              if x < storex then
2983                bx := x;
2984                bw := storex - x;
2985              else
2986                bx := storex;
2987                bw := x - storex;
2988              fi;
2989              if y < storey then
2990                by := y;
2991                bh := storey - y;
2992              else
2993                by := storey;
2994                bh := y - storey;
2995              fi;
2996              if bx <> box!.x or by <> box!.y then
2997                Move(box,bx,by);
2998              fi;
2999              if bw <> box!.w or bh <> box!.h then
3000                Reshape(box,bw,bh);
3001              fi;
3002            end) then
3003      # the box had at one time at least a certain size
3004      if box!.w > 0 and box!.h > 0 then
3005        GGSelectModifiesMenu := false;
3006        for lev in poset!.levels do
3007          if lev!.top < box!.y+box!.h and
3008             lev!.top + lev!.height >= box!.y then
3009            for cl in lev!.classes do
3010              for v in cl do
3011                if [v!.x,v!.y+lev!.top] in box then
3012                  Select(poset,v,true);
3013                fi;
3014              od;
3015            od;
3016          fi;
3017        od;
3018        # better we redraw:
3019        Delete(poset,box);
3020        DoRedraw(poset);
3021        GGSelectModifiesMenu := true;
3022        ModifyEnabled(poset,1,Length(poset!.menus));
3023      else
3024        Delete(poset,box);
3025      fi;
3026      # Drag(...) --> true
3027    else
3028      Delete(poset,box);
3029    fi;
3030  fi;
3031end);
3032
3033
3034#############################################################################
3035##
3036#M  PosetRightClick(graph,x,y) . . . method which is called after right click
3037##
3038##  This method is called when the user does a right click in a graph.
3039##  This method just finds the vertex under the mouse pointer and calls the
3040##  rightclickfunction of the poset. Note that the rightclickfunction
3041##  can be called with `fail' if no vertex is hit.
3042##
3043InstallMethod(PosetRightClick,
3044    "for a graph, and two integers",
3045    true,
3046    [ IsGraphicGraphRep, IsInt, IsInt ],
3047    0,
3048
3049function(graph,x,y)
3050  local   v;
3051
3052  # is this a click on a vertex?
3053  v := WhichVertex(graph,x,y);
3054  if graph!.rightclickfunction <> false then
3055    graph!.rightclickfunction(graph,v,x,y);
3056  fi;
3057  return;
3058end);
3059
3060
3061#############################################################################
3062##
3063#M  UserDeleteVerticesOp . . . is called if the user wants to delete vertices
3064##
3065##  This operation is called when the user selects "Delete vertices".
3066##  The generic method actually deletes the selected vertices including all
3067##  their edges.
3068##
3069InstallMethod( UserDeleteVerticesOp,
3070    "for a graphic poset, a menu, and a menu entry",
3071    true,
3072    [ IsGraphicGraphRep, IsMenu, IsString ],
3073    0,
3074
3075function( graph, menu, entry )
3076  local   v;
3077
3078  # it is guaranteed, that at least one vertex is selected!
3079  while graph!.selectedvertices <> [] do
3080    Delete(graph,graph!.selectedvertices[1]);
3081  od;
3082end);
3083
3084
3085#############################################################################
3086##
3087#M  UserDeleteEdgeOp  . . . . . is called if the user wants to delete an edge
3088##
3089##  This operation is called when the user selects "Delete edge".
3090##  The generic method deletes the edge with no further warning!
3091##
3092InstallMethod( UserDeleteEdgeOp,
3093    "for a graphic graph, a menu, and a menu entry",
3094    true,
3095    [ IsGraphicGraphRep, IsMenu, IsString ],
3096    0,
3097
3098function( graph, menu, entry )
3099  # it is guaranteed, that exactly two connected vertices are selected!
3100  Delete(graph,graph!.selectedvertices[1],graph!.selectedvertices[2]);
3101end);
3102
3103
3104#############################################################################
3105##
3106#M  UserMergeClassesOp (<sheet>, <menu>, <entry>) . . . . . . . . . . . . . .
3107##  . . . . . . . . . . . . . .  is called if the user wants to merge classes
3108##
3109##  This operation is called when the user selects `Merge Classes'.
3110##  The generic method walks through all levels and merges all classes that
3111##  contain a selected vertex. Afterwards `UserRearrangeClasses' is called.
3112##
3113InstallMethod( UserMergeClassesOp,
3114    "for a graphic poset, a menu, and a menu entry",
3115    true,
3116    [ IsGraphicGraphRep and IsGraphicPosetRep, IsMenu, IsString ],
3117    0,
3118
3119function( poset, menu, entry )
3120  local   lps,  verts,  v,  pos,  i,  level,  cps,  cpos,  cls,  j;
3121
3122  # it is guaranteed, that at least one vertex is selected!
3123  # we walk through the selected vertices and sort them according to their
3124  # level parameter:
3125  lps := [];
3126  verts := [];
3127  for v in Selected(poset) do
3128    pos := Position(lps,v!.levelparam);
3129    if pos = fail then
3130      Add(lps,v!.levelparam);
3131      Add(verts,[v]);
3132    else
3133      Add(verts[pos],v);
3134    fi;
3135  od;
3136
3137  # All levels:
3138  for i in [1..Length(lps)] do
3139    # the current level:
3140    level := poset!.levels[Position(poset!.levelparams,lps[i])];
3141
3142    # Now we collect all classes occuring:
3143    cps := [];
3144    cpos := [];
3145    cls := [];
3146    for v in verts[i] do
3147      pos := Position(cps,v!.classparam);
3148      if pos = fail then
3149        Add(cps,v!.classparam);
3150        pos := Position(level!.classparams,v!.classparam);
3151        Add(cpos,pos);
3152        Add(cls,level!.classes[pos]);
3153      fi;
3154    od;
3155
3156    # now we have a list of classes that should be merged:
3157    # let's move all vertices into the first class:
3158    for j in [2..Length(cls)] do
3159      for v in cls[j] do
3160        v!.classparam := cps[1];
3161        Add(cls[1],v);
3162      od;
3163    od;
3164
3165    # now we have to delete the other classes (but not their vertices!):
3166    cpos := cpos{[2..Length(cps)]};
3167    Sort(cpos);
3168    for j in [Length(cpos),Length(cpos)-1..1] do
3169      level!.classes[cpos[j]] := level!.classes[Length(level!.classes)];
3170      Unbind(level!.classes[Length(level!.classes)]);
3171      level!.classparams[cpos[j]] :=
3172        level!.classparams[Length(level!.classparams)];
3173      Unbind(level!.classparams[Length(level!.classparams)]);
3174    od;
3175  od;
3176
3177  # At last we rearrange those classes:
3178  UserRearrangeClasses( poset, menu, "Rearrange Classes" );
3179end);
3180
3181
3182#############################################################################
3183##
3184## This is used by the following three methods:
3185##
3186BindGlobal("PosetScaleLattice",function(poset,factorx,factory)
3187  local   l,  pos,  cl,  v,  newx,  newy,  diffx,  diffy;
3188
3189  FastUpdate(poset,true);
3190  Resize(poset, Int(poset!.width*factorx), Int(poset!.height*factory));
3191  for l in [1..Length(poset!.levelparams)] do
3192    pos := PositionLevel(poset,poset!.levelparams[l]);
3193    ResizeLevel(poset,poset!.levelparams[l],Int(pos[2]*factory));
3194    for cl in poset!.levels[l]!.classes do
3195      if cl <> [] then
3196        v := cl[1];
3197        newx := Int(v!.x*factorx);
3198        newy := Int(v!.y*factory);
3199        diffx := newx - v!.x;
3200        diffy := newy - v!.y;
3201        for v in cl do
3202          Move(poset,v,v!.x + diffx,v!.y + diffy);
3203        od;
3204      fi;
3205    od;
3206  od;
3207  FastUpdate(poset,false);
3208end);
3209
3210
3211#############################################################################
3212##
3213#M  UserMagnifyLattice . . . . . .  lets the user magnify the graphic lattice
3214##
3215##  This operation is called when the user selects "Magnify Lattice".
3216##  The generic method scales everything by 144/100 including the sheet,
3217##  all heights of levels and positions of vertices.
3218##
3219InstallMethod( UserMagnifyLattice,
3220    "for a graphic poset, a menu, and a string",
3221    true,
3222    [ IsGraphicPosetRep, IsMenu, IsString ],
3223    0,
3224
3225function(poset, menu, entry)
3226  local   l,  pos,  cl,  v;
3227  PosetScaleLattice(poset,144/100,144/100);
3228end);
3229
3230
3231#############################################################################
3232##
3233#M  UserShrinkLattice . . . . . . .  lets the user shrink the graphic lattice
3234##
3235##  This operation is called when the user selects "Shrink Lattice".
3236##  The generic method scales everything by 100/144 including the sheet,
3237##  all heights of levels and positions of vertices.
3238##
3239InstallMethod( UserShrinkLattice,
3240    "for a graphic poset, a menu, and a string",
3241    true,
3242    [ IsGraphicPosetRep, IsMenu, IsString ],
3243    0,
3244
3245function(poset, menu, entry)
3246  local   l,  pos,  cl,  v;
3247  PosetScaleLattice(poset,100/144,100/144);
3248end);
3249
3250##
3251## Make a rational number from a string, accept fraction:
3252##
3253BindGlobal("PosetRatString",
3254  function( st )
3255    local n,d,p;
3256    p := Position( st, '/' );
3257    if p = fail then
3258      return Int(st);
3259    else
3260      n := Int(st{[1..p-1]});
3261      d := Int(st{[p+1..Length(st)]});
3262      if d <> 0 then
3263        return n/d;
3264      else
3265        return infinity;
3266      fi;
3267    fi;
3268  end);
3269
3270##
3271## Extracts two factors out of a string:
3272##
3273BindGlobal("PosetFactorsString",
3274  function( factor )
3275    local   p,  x,  y;
3276
3277    # find ","
3278    p := Position( factor, ',' );
3279    if p = fail  then
3280        x := PosetRatString(factor);
3281        y := x;
3282    elif p = 1  then
3283        x := 1;
3284        y := PosetRatString(factor{[2..Length(factor)]});
3285    elif p = Length(factor)  then
3286        x := PosetRatString(factor{[1..p-1]});
3287        y := 1;
3288    else
3289        x := PosetRatString(factor{[1..p-1]});
3290        y := PosetRatString(factor{[p+1..Length(factor)]});
3291    fi;
3292    if x <= 0  then x := 1;  fi;
3293    if y <= 0  then y := 1;  fi;
3294    return [ x, y ];
3295  end);
3296
3297
3298#############################################################################
3299##
3300#M  UserResizeLattice . . . . . . .  lets the user resize the graphic lattice
3301##
3302##  This operation is called when the user selects "Resize Lattice".
3303##  The generic method asks the user for a x and a y factor and scales
3304##  everything including the sheet, all heights of levels and positions of
3305##  vertices.
3306##
3307InstallMethod( UserResizeLattice,
3308    "for a graphic poset, a menu, and a string",
3309    true,
3310    [ IsGraphicPosetRep, IsMenu, IsString ],
3311    0,
3312
3313function(poset, menu, entry)
3314  local   res,  fac;
3315
3316  res := Query( Dialog( "OKcancel", "X,Y factors" ) );
3317  if res = false or 0 = Length(res)  then
3318    return;
3319  fi;
3320  fac := PosetFactorsString(res);
3321  if fac[1] <> 1 or fac[2] <> 1 then
3322    PosetScaleLattice(poset,fac[1],fac[2]);
3323  fi;
3324end);
3325
3326
3327#############################################################################
3328##
3329#M  UserResizeSheet . . . . . . . . .  lets the user resize the graphic sheet
3330##
3331##  This operation is called when the user selects "Resize Sheet".
3332##  The generic method asks the user for a x and a y pixel number and
3333##  changes the width and height of the sheet. No positions of levels and
3334##  vertices are changed. If the user asks for trouble he gets it!
3335##
3336InstallMethod( UserResizeSheet,
3337    "for a graphic graph, a menu, and a string",
3338    true,
3339    [ IsGraphicGraphRep, IsMenu, IsString ],
3340    0,
3341
3342function(poset, menu, entry)
3343  local   res,  pix,  oldwidth,  t;
3344  res := Query( Dialog( "OKcancel", "New Width,Height" ) );
3345  if res = false or 0 = Length(res)  then
3346    return;
3347  fi;
3348  pix := PosetFactorsString(res);
3349  if pix[1] = 1 then
3350    pix[1] := poset!.width;
3351  fi;
3352  if pix[2] = 1 then
3353    pix[2] := poset!.height;
3354  fi;
3355
3356  oldwidth := poset!.width;
3357
3358  Resize(poset,pix[1],pix[2]);
3359
3360  # we now have to move the texts of levelparameters if it is a poset:
3361  if IsGraphicPosetRep(poset) and poset!.showlevelparams then
3362    for t in [1..Length(poset!.levels)] do
3363      MoveDelta(poset!.lptexts[t],poset!.width-oldwidth,0);
3364    od;
3365  fi;
3366end);
3367
3368
3369#############################################################################
3370##
3371#M  UserMoveLattice . . . . . . . . . . . . . lets the user move all vertices
3372##
3373##  This operation is called when the user selects "Move Lattice".
3374##  The generic method asks the user for a pixel number and
3375##  changes the position of all vertices horizontally. No positions of
3376##  levels are changed.
3377##
3378InstallMethod( UserMoveLattice,
3379    "for a graphic poset, a menu, and a string",
3380    true,
3381    [ IsGraphicGraphRep and IsGraphicPosetRep, IsMenu, IsString ],
3382    0,
3383
3384function(poset, menu, entry)
3385  local   res,  pix,  l,  cl,  v;
3386  res := Query( Dialog( "OKcancel", "Move horizontally" ) );
3387  if res = false or 0 = Length(res)  then
3388    return;
3389  fi;
3390  pix := Int(res);
3391  if pix <> 0 then
3392    for l in poset!.levels do
3393      for cl in l!.classes do
3394        for v in cl do
3395          Move(poset,v,v!.x+pix,v!.y);
3396        od;
3397      od;
3398    od;
3399  fi;
3400end);
3401
3402
3403#############################################################################
3404##
3405#M  UserChangeLabels . . . . . . . .  lets the user change labels of vertices
3406##
3407##  This operation is called when the user selects "Change Labels".
3408##  The user is prompted for every selected vertex, which label it should
3409##  have.
3410##
3411InstallMethod( UserChangeLabels,
3412    "for a graphic graph, a menu, and a string",
3413    true,
3414    [ IsGraphicGraphRep, IsMenu, IsString ],
3415    0,
3416
3417function(graph, menu, entry)
3418  local   D,  sel,  v,  res;
3419
3420  D := Dialog("OKcancel", "Label");
3421  sel := Selected(graph);
3422  for v in sel do
3423    res := Query(D,v!.label);
3424    if res = false then
3425      return;
3426    fi;
3427    if 0 < Length(res) then
3428      Relabel(graph,v,res);
3429    fi;
3430  od;
3431end);
3432
3433
3434#############################################################################
3435##
3436#M  UserAverageY . . . . . . . . .  average all y positions within all levels
3437##
3438##  This operation is called when the user selects ``Average Y Positions''.
3439##  In all level the average y coordinate is calculated and all vertices are
3440##  moved to this y position.
3441##
3442InstallMethod( UserAverageY,
3443    "for a graphic poset, a menu, and a string",
3444    true,
3445    [ IsGraphicSheet and IsGraphicGraphRep and IsGraphicPosetRep,
3446      IsMenu, IsString ],
3447    0,
3448function( poset, menu, string )
3449  local   lev,  av,  n,  cl,  v;
3450  for lev in poset!.levels do
3451    av := 0;
3452    n := 0;
3453    for cl in lev!.classes do
3454      for v in cl do
3455        av := av + v!.y;
3456        n := n + 1;
3457      od;
3458    od;
3459    if n > 0 then
3460      av := QuoInt(av,n);
3461      for cl in lev!.classes do
3462        for v in cl do
3463          Move(poset,v,v!.x,av);
3464        od;
3465      od;
3466    fi;
3467  od;
3468end);
3469
3470
3471#############################################################################
3472##
3473#M  UserAverageX . . . . . . . . . . average all x positions of sel. vertices
3474##
3475##  This operation is called when the user selects ``Average X Positions''.
3476##  The average of all x coordinates of the selected vertices is calculated.
3477##  Then all classes with a selected vertex are moved such that the first
3478##  selected vertex in this class has the calculated position as x position.
3479##
3480InstallMethod( UserAverageX,
3481    "for a graphic poset, a menu, and a string",
3482    true,
3483    [ IsGraphicSheet and IsGraphicGraphRep and IsGraphicPosetRep,
3484      IsMenu, IsString ],
3485    0,
3486function( poset, menu, string )
3487  local   sel,  av,  list,  v,  pair,  vertices,  diff;
3488  sel := Selected(poset);
3489  # we have at least one selected vertex!
3490  av := 0;
3491  list := [];   # we store all levelparam/classparam pairs
3492  for v in sel do
3493    av := av + v!.x;
3494    AddSet(list,[v!.levelparam,v!.classparam]);
3495  od;
3496  av := QuoInt(av,Length(sel));
3497
3498  FastUpdate(poset,true);
3499  for pair in list do
3500    vertices := Vertices(poset,pair[1],pair[2]);
3501    if vertices <> fail then
3502      v := First(vertices,x->x in sel);
3503      if v <> fail then
3504        diff := av - v!.x;
3505        for v in vertices do
3506          Move(poset,v,v!.x + diff,v!.y);
3507        od;
3508      fi;
3509    fi;
3510  od;
3511  FastUpdate(poset,false);
3512end);
3513
3514
3515#############################################################################
3516##
3517#M  UserRearrangesClasses . . . . . . . . . . rearrange vertices within class
3518##
3519##  This operation is called when the user selects ``Rearrange Classes''.
3520##  All classes with a selected vertex are rearranged: The vertices are
3521##  lined up neatly one after the other, sorted according to their current
3522##  x position.
3523##
3524InstallMethod( UserRearrangeClasses,
3525    "for a graphic poset, a menu, and a string",
3526    true,
3527    [ IsGraphicSheet and IsGraphicGraphRep and IsGraphicPosetRep,
3528      IsMenu, IsString ],
3529    0,
3530function( poset, menu, string )
3531  local   sel,  av,  list,  v,  pair,  vlist,  xlist,  perm,  i;
3532
3533  sel := Selected(poset);
3534  # we have at least one selected vertex!
3535  av := 0;
3536  list := [];   # we store all levelparam/classparam pairs
3537  for v in sel do
3538    AddSet(list,[v!.levelparam,v!.classparam]);
3539  od;
3540
3541  FastUpdate(poset,true);
3542  for pair in list do
3543    # get the vertices in class:
3544    vlist := Vertices(poset,pair[1],pair[2]);
3545    if vlist <> fail then
3546      xlist := List(vlist,y->y!.x);
3547      perm := Sortex(xlist);
3548      vlist := Permuted(vlist,perm);
3549      for i in [2..Length(vlist)] do
3550        Move(poset,vlist[i],vlist[1]!.x + (i-1)*(VERTEX.diameter+2),
3551             vlist[1]!.y);
3552      od;
3553    fi;
3554  od;
3555  FastUpdate(poset,false);
3556end);
3557
3558
3559############################################################################
3560##
3561#M  UserUseBlackWhite . . . . . . . . . .  called if user selects bw in menu
3562##
3563##  This is called if the user selects ``Use Black and White'' in the menu.
3564##
3565InstallMethod( UserUseBlackWhite,
3566    "for a graphic graph, a menu, and a string",
3567    true,
3568    [ IsGraphicSheet and IsGraphicGraphRep, IsMenu, IsString ],
3569    0,
3570function( sheet, menu, entry )
3571  local   v;
3572  if sheet!.color.model = "monochrome" then
3573    sheet!.color.model := "color";
3574    Check(menu,entry,false);
3575  else
3576    sheet!.color.model := "monochrome";
3577    Check(menu,entry,true);
3578  fi;
3579  GPMakeColors(sheet);
3580  for v in Selected(sheet) do
3581    Recolor(sheet,v,sheet!.color.selected);
3582  od;
3583end);
3584
3585
3586#############################################################################
3587##
3588#M  PosetShowLevels  . . . . . . . . . . . . . . . . switch display of levels
3589##
3590##  This operation is called when the user selects "Show Levels" in the menu.
3591##  Switches the display of the little boxes for level handling on and off.
3592##
3593InstallMethod( PosetShowLevels,
3594    "for a graphic poset, a menu, and a menu entry",
3595    true,
3596    [ IsGraphicPosetRep, IsMenu, IsString ],
3597    0,
3598
3599function( poset, menu, entry )
3600  local   b;
3601  poset!.showlevels := not(poset!.showlevels);
3602  if poset!.showlevels then
3603    for b in [1..Length(poset!.levelboxes)] do
3604      Revive(poset!.levelboxes[b]);
3605      Move(poset!.levelboxes[b],0,poset!.levels[b]!.top
3606                                  +poset!.levels[b]!.height-8);
3607    od;
3608  else
3609    for b in poset!.levelboxes do
3610      Destroy(b);
3611    od;
3612  fi;
3613  Check(menu,entry,poset!.showlevels);
3614end);
3615
3616
3617#############################################################################
3618##
3619#M  PosetShowLevelparams . . . . . . . . .  switch display of levelparameters
3620##
3621##  This operation is called when the user selects "Show Levelparameters" in
3622##  the menu. Switches the display of the level parameters at the right of
3623##  the screen on and off.
3624##
3625InstallMethod( PosetShowLevelparams,
3626    "for a graphic poset, a menu, and a menu entry",
3627    true,
3628    [ IsGraphicPosetRep, IsMenu, IsString ],
3629    0,
3630
3631function( poset, menu, entry )
3632  local   t;
3633  poset!.showlevelparams := not(poset!.showlevelparams);
3634  if poset!.showlevelparams then
3635    for t in [1..Length(poset!.lptexts)] do
3636      Revive(poset!.lptexts[t]);
3637      Move(poset!.lptexts[t],poset!.lptexts[t]!.x,poset!.levels[t]!.top
3638                                  +QuoInt(poset!.levels[t]!.height,2));
3639    od;
3640  else
3641    for t in poset!.lptexts do
3642      Destroy(t);
3643    od;
3644  fi;
3645  Check(menu,entry,poset!.showlevelparams);
3646end);
3647
3648
3649#############################################################################
3650##
3651#M  DoRedraw(<graph>). . . . . . . . . . redraws all vertices and connections
3652##
3653##  Redraws all vertices and connections.
3654##
3655InstallMethod( DoRedraw,
3656    "for a graphic poset",
3657    true,
3658    [ IsGraphicPosetRep ],
3659    0,
3660
3661function(poset)
3662  local   lev,  cl,  v,  v2,  pos;
3663
3664  for lev in poset!.levels do
3665    for cl in lev!.classes do
3666      for v in cl do
3667        Draw(v!.obj);
3668        for v2 in v!.maximals do
3669          pos := Position(v!.obj!.connections,v2!.obj);
3670          if pos <> fail then
3671            Draw(v!.obj!.connectingLines[pos]);
3672          fi;
3673        od;
3674      od;
3675    od;
3676  od;
3677end);
3678
3679
3680#############################################################################
3681##
3682##  Some things that don't fit in other sections:
3683##
3684#############################################################################
3685
3686##
3687##  We want Position and PositionSorted for lists of vertices:
3688##
3689InstallMethod( EQ, "for two vertices", true, [IsGGVertex,IsGGVertex],0,
3690        IsIdenticalObj );
3691InstallMethod( \<, "for two vertices", true, [IsGGVertex,IsGGVertex],0,
3692        function(a,b) return (a!.serial < b!.serial); end);
3693InstallMethod( EQ, "for two levels", true, [IsGPLevel,IsGPLevel],0,
3694        IsIdenticalObj );
3695
3696##
3697##  ViewObj methods:
3698##
3699InstallMethod( ViewObj,"for a graphic graph",true,
3700        [IsGraphicSheet and IsGraphicSheetRep and IsGraphicGraphRep],
3701        0,function( sheet )
3702  Print("<");
3703  if not IsAlive(sheet) then
3704    Print("dead ");
3705  fi;
3706  Print("graphic graph \"",sheet!.name,"\">");
3707end);
3708
3709InstallMethod( ViewObj,"for a graphic poset",true,
3710        [IsGraphicSheet and IsGraphicSheetRep and IsGraphicGraphRep and
3711         IsGraphicPosetRep],
3712        0,function( sheet )
3713  Print("<");
3714  if not IsAlive(sheet) then
3715    Print("dead ");
3716  fi;
3717  Print("graphic poset \"",sheet!.name,"\">");
3718end);
3719
3720InstallMethod( ViewObj,"for a level",true,
3721        [IsGraphicObject and IsGPLevel],
3722        0,function( level )
3723  local   pos;
3724  pos := Position(level!.poset!.levels,level);
3725  Print("<level of graphic poset \"",level!.poset!.name,"\", Parameter: ",
3726        level!.poset!.levelparams[pos],">");
3727end);
3728
3729InstallMethod( ViewObj,"for a vertex",true,
3730        [IsGraphicObject and IsGGVertex],
3731        0,function( vertex )
3732  Print("<vertex of graphic graph, label: \"",vertex!.label,"\", Serial:",
3733        vertex!.serial,">");
3734end);
3735
3736## FIXME: ... TODO-List for graphs:
3737
3738# comments for GraphicGraphRep
3739# generic Graph Menu with at least Redraw, probably Deletes also
3740#M  GraphicGraph( <name>, <width>, <height> ) . . . . . . a new graphic graph
3741#M  Vertex(<graph>,<data>[,<inf>]) . . . . . . . . . . . . creates new vertex
3742#M  Edge(<graph>,<vertex1>,<vertex2>) . . . . . . . . . . . . adds a new edge
3743#M  Edge(<graph>,<vertex1>,<vertex2>,<def>) . . . . . . . . . adds a new edge
3744#M  Delete(<graph>,<obj>) . . . . . . . . . . . . . remove something in graph
3745#M  Move(<graph>,<vertex>,<x>,<y>) . . . . . . . . . . . . . . .  move vertex
3746#M  Move(<graph>,<vertex>) . . . . . . . . . . . . . . . . . . .  move vertex
3747#M  SetWidth(<graph>,<vertex1>,<vertex2>,<width>) . change line width of edge
3748#M  SetWidth(<graph>,<vertex1>,<vertex2>) . . . . . change line width of edge
3749#M  ChooseLabel(<graph>,<data>,<data>)  . . . . is called while edge creation
3750#M  ChoosePosition(<graph>,<data>)  . . . . . is called while vertex creation
3751#M  WhichVertex(<graph>,<x>,<y>) . . .  determine vertex in which position is
3752#M  WhichVertex(<graph>,<data>)  . . . . .  determine vertex with data <data>
3753
3754
3755