GAP 4.8.9 installation with standard packages -- copy to your CoCalc project to get it
#############################################################################
##
#W brgrids.g GAP 4 package `browse' Thomas Breuer
##
#Y Copyright (C) 2007, Lehrstuhl D für Mathematik, RWTH Aachen, Germany
##
## This file contains some <C>SpecialGrid</C> functions.
##
#T TB, May 2007:
#T extended version of `NCurses.Grid',
#T supports ``indicating continuation'' and attributes for lines;
#T the optional components `tend', `bend', `lend', `rend' are `true'
#T if the shown grid is not continued at the top, bottom, left, and right,
#T respectively (this is also the default), and `false' otherwise;
#T the argument `attrs', if given, must be an integer
#T describing the attributes for the lines.
NCurses.GridExt:= function( win, args, attrs )
local size, trow, brow, lcol, rcol, rowinds, colinds,
tend, bend, lend, rend,
tvis, bvis, ht, lvis, rvis, wdth, ld, lmr, i, j;
size := NCurses.getmaxyx(win);
if size = false then return false; fi;
trow:= args.trow;
brow:= args.brow;
lcol:= args.lcol;
rcol:= args.rcol;
rowinds:= args.rowinds;
colinds:= args.colinds;
tend:= not IsBound( args.tend ) or args.tend = true;
bend:= not IsBound( args.bend ) or args.bend = true;
lend:= not IsBound( args.lend ) or args.lend = true;
rend:= not IsBound( args.rend ) or args.rend = true;
if not ForAll([trow, brow, lcol, rcol], IsInt) then return false; fi;
if not ForAll(rowinds, IsInt) then return false; fi;
if not ForAll(colinds, IsInt) then return false; fi;
# find viewable rows and cols
rowinds := Filtered(rowinds, i-> i >= 0 and i >= trow and
i <= size[1]-1 and i <= brow);
colinds := Filtered(colinds, i-> i >= 0 and i >= lcol and
i <= size[2]-1 and i <= rcol);
if IsEmpty( rowinds ) and IsEmpty( colinds ) then
return false;
fi;
tvis := Maximum(trow, 0);
bvis := Minimum(brow, size[1]);
ht := bvis - tvis + 1;
lvis := Maximum(lcol, 0);
rvis := Minimum(rcol, size[2]);
wdth := rvis - lvis + 1;
# Set attributes for the lines.
NCurses.wattrset( win, attrs );
# draw vlines
ld := NCurses.lineDraw;
for i in colinds do
NCurses.wmove(win, tvis, i);
NCurses.wvline(win, ld.VLINE, ht);
od;
# draw hlines and handle crossings
for i in rowinds do
NCurses.wmove(win, i, lvis);
NCurses.whline(win, ld.HLINE, wdth);
if i = trow and tend then
lmr := [ld.ULCORNER, ld.TTEE, ld.URCORNER];
elif i = brow and bend then
lmr := [ld.LLCORNER, ld.BTEE, ld.LRCORNER];
else
lmr := [ld.LTEE, ld.PLUS, ld.RTEE];
fi;
for j in colinds do
NCurses.wmove(win, i, j);
if j = lcol and lend then
NCurses.waddch(win, lmr[1]);
elif j = rcol and rend then
NCurses.waddch(win, lmr[3]);
else
NCurses.waddch(win, lmr[2]);
fi;
od;
od;
# Reset the attributes.
NCurses.wattrset( win, NCurses.attrs.NORMAL );
return true;
end;
#############################################################################
##
#F BrowseData.SpecialGridLineDraw( <t>, <data> )
##
## When this special grid is used in a browse table,
## nonempty row separators are overwritten with horizontal rows
## that consist of the special character <C>NCurses.lineDraw.HLINE</C>,
## non-blank characters in column separators are overwritten with vertical
## rows that consist of the special character <C>NCurses.lineDraw.VLINE</C>,
## and <Q>crossings</Q> of horizontal and vertical lines are handled as in
## <Ref Func="NCurses.Grid"/>.
## <P/>
## In categorized browse tables, each set of table rows shown under
## a category row gets a grid of its own,
## and the separators below category rows are regarded as the top rows of
## these grids.
## So this special grid requires nonempty category separators if nonempty
## row separators occur.
## (Note that below a row separator, first the category rows and their
## separators appear, and then the data rows and their separators.
## If there is a nonempty row separator above the first data row
## then it is overwritten by whitespace.)
##
BrowseData.SpecialGridLineDraw:= function( t, data )
local win, entry, top, i;
win:= t.dynamic.window;
# Clear category separators, since they may exceed the last column.
for entry in data.catSeparators do
NCurses.wmove( win, entry[1], entry[2] );
NCurses.waddstr( win, ListWithIdenticalEntries( entry[3], ' ' ) );
od;
# Replace the row and column separators in all four tables by the lines.
for entry in data.gridsInfo do
NCurses.GridExt( win, entry, NCurses.attrs.NORMAL );
od;
# Overwrite a row separator above the first data row
# if the table has a category for the first data row.
top:= t.dynamic.topleft[1];
if IsOddInt( top )
and t.dynamic.topleft[3] = 1
and BrowseData.LengthCell( t, top, "vert" ) <> 0 then
for i in [ top+1, top+3 .. Length( t.dynamic.indexRow ) - 1 ] do
if BrowseData.LengthCell( t, i, "vert" ) <> 0 then
if i in t.dynamic.categories[1] then
NCurses.wmove( win, data.topmargin + data.headerLength
+ BrowseData.HeightLabelsColTable( t ),
data.leftmargin );
NCurses.waddstr( win, ListWithIdenticalEntries(
NCurses.getmaxyx( win )[2] - data.leftmargin, ' ' ) );
fi;
break;
fi;
od;
fi;
# Print those category separators that do not overwrite grid top lines.
#T force printing of category separators involving attributes!
for entry in data.catSeparators do
if ForAll( data.gridsInfo, x -> entry[1] <> x.trow ) then
NCurses.wmove( win, entry[1], entry[2] );
NCurses.whline( win, NCurses.lineDraw.HLINE, entry[3] );
fi;
od;
end;
#############################################################################
##
#F BrowseData.SpecialGridLineDrawPlus( <t>, <data> )
##
## This function is used for example in the `Browse' method for matrices.
##
## It draws the crossing of the two lines that separate the row and column
## labels from the main table.
## Note that the grids drawn by `BrowseData.SpecialGridLineDraw' belong to
## one of the four subtables of a browse table, and it is not supported
## that lines separate these tables.
##
BrowseData.SpecialGridLineDrawPlus:= function( t, data )
local win;
BrowseData.SpecialGridLineDraw( t, data );
win:= t.dynamic.window;
NCurses.wmove( win, data.gridsInfo[1].rowinds[1],
data.gridsInfo[1].colinds[1] );
NCurses.waddch( win, NCurses.lineDraw.PLUS );
end;
#############################################################################
##
#F BrowseData.SpecialGridTreeStyle( <t>, <data> )
##
## *NOTE*:
## This function does not yet support category separators
## and cells of height larger than 1.
## (And there is the general problem that when one expands all categories at
## the end of the table, the programmatically hidden rows are taken into
## account and then lead to unmotivated empty rows.)
## (Scrolling by cells seems to be not appropriate for this application,
## scrolling by characters would be better.)
##
BrowseData.SpecialGridTreeStyle:= function( t, data )
local categories, catpos, win, i, fromrow, maxdepth, previousrow, scr,
ymin, ymax, y, prevdatarow, pos, entry, level, currlevel, pos2,
kmin, j, k, lastcategorypos, height, l;
categories:= t.dynamic.categories;
catpos:= categories[1];
if IsEmpty( catpos ) or 2 < catpos[1] then
# `BrowseData.SpecialGridTreeStyle' expects category rows,
# starting in the first row.
return;
fi;
win:= t.dynamic.window;
i:= t.dynamic.topleft[1];
fromrow:= t.dynamic.topleft[3];
maxdepth:= Length( t.work.sepCol[1] );
# The `i'-th entry in `previousrow' is the position of the previous
# level `i' category if there is one on the screen, and zero otherwise.
previousrow:= 0 * [ 0 .. maxdepth ];
# Process the current line.
scr:= BrowseData.HeightWidthWindow( t );
ymin:= data.topmargin + BrowseData.HeightLabelsColTable( t )
+ data.headerLength + 1;
ymax:= scr[1] - data.bottommargin - data.footerLength;
y:= ymin;
prevdatarow:= fail;
while y <= ymax+1 and IsBound( t.dynamic.indexRow[i] ) do
# We consider row `ymax+1' but we do not print there.
if i mod 2 = 0 then
# Deal with categories, omitting the first `fromrow - 1' rows.
pos:= PositionSorted( catpos, i );
#T Why is there no variant with a <from> argument?
while pos <= Length( catpos ) and catpos[ pos ] = i do
entry:= categories[2][ pos ];
if entry.isUnderCollapsedCategory or entry.isRejectedCategory then
# This category and all of higher level are hidden.
break;
fi;
level:= entry.level;
# Deal with the category line.
if 1 < fromrow then
# The beginning of the cell is above the screen.
fromrow:= fromrow - 1;
else
# (We print no path to level 1 rows.)
if 1 < level then
if not IsBound( currlevel ) then
# The current level is that of the previous category row.
pos2:= pos;
while 1 < pos2 and catpos[ pos2 ] = i do
pos2:= pos2 - 1;
od;
currlevel:= categories[2][ pos2 ].level;
fi;
# Print the grid part for this level above the current line.
if currlevel < level then
# Fill the level `currlevel' column above the current line.
if ymin < y and previousrow[ currlevel ] + 1 < y then
NCurses.wmove( win, y-2, 0 );
NCurses.waddstr( win, ListWithIdenticalEntries(
2*currlevel-2, ' ' ) );
NCurses.wmove( win, y-2, 2*currlevel-2 );
NCurses.waddch( win, NCurses.lineDraw.LTEE );
fi;
elif currlevel = level then
# Fill the level `currlevel' column above the current line.
if previousrow[ currlevel ] = 0 then
kmin:= ymin;
NCurses.wmove( win, kmin-1, 0 );
NCurses.waddstr( win, ListWithIdenticalEntries(
2*currlevel-4, ' ' ) );
NCurses.wmove( win, kmin - 1, 2*currlevel-4 );
NCurses.waddch( win, NCurses.lineDraw.VLINE );
else
kmin:= previousrow[ currlevel ];
NCurses.wmove( win, kmin-1, 0 );
NCurses.waddstr( win, ListWithIdenticalEntries(
2*currlevel-4, ' ' ) );
NCurses.wmove( win, kmin - 1, 2*currlevel-4 );
NCurses.waddch( win, NCurses.lineDraw.LTEE );
fi;
NCurses.wmove( win, kmin, 2 * ( currlevel-2 ) );
NCurses.wvline( win, NCurses.lineDraw.VLINE, y - kmin - 1 );
else
# Fill the column for level `level' above the current line.
if previousrow[ level ] = 0 then
kmin:= ymin;
else
kmin:= previousrow[ level ];
fi;
NCurses.wmove( win, kmin-1, 0 );
NCurses.waddstr( win, ListWithIdenticalEntries(
2*level-4, ' ' ) );
NCurses.wmove( win, kmin - 1, 2*level-4 );
if previousrow[ level ] = 0 then
NCurses.waddch( win, NCurses.lineDraw.VLINE );
else
NCurses.waddch( win, NCurses.lineDraw.LTEE );
fi;
NCurses.wmove( win, kmin, 2 * ( level-2 ) );
NCurses.wvline( win, NCurses.lineDraw.VLINE, y - kmin - 1 );
fi;
if y <= ymax then
NCurses.wmove( win, y-1, 0 );
NCurses.waddstr( win, ListWithIdenticalEntries(
2*level-4, ' ' ) );
NCurses.wmove( win, y-1, 2*level-4 );
NCurses.waddch( win, NCurses.lineDraw.LLCORNER );
fi;
fi;
previousrow[ level ]:= y;
currlevel:= level;
lastcategorypos:= pos;
y:= y + 1;
fi;
pos:= pos + 1;
od;
fi;
# Deal with the data rows.
#T what about fromrow?
height:= BrowseData.HeightRow( t, i );
if 0 < height then
if i mod 2 = 0 then
if not IsBound( currlevel ) then
# The first shown row is a data row.
# Determine the level of the category row above the first line.
pos:= PositionSorted( catpos, i );
if IsBound( catpos[ pos ] ) and catpos[ pos ] = i then
if BrowseData.HeightCategories( t, i ) < fromrow then
# Take the last category row for `i'.
while IsBound( catpos[ pos ] ) and catpos[ pos ] = i do
pos:= pos + 1;
od;
else
# Take the last category row for `i' above the first line,
# together with its category separator below.
while IsBound( catpos[ pos ] )
and BrowseData.HeightCategories( t, i,
categories[2][ pos ].level ) < fromrow do
pos:= pos + 1;
od;
fi;
fi;
if 1 < pos then
pos:= pos-1;
fi;
currlevel:= categories[2][ pos ].level;
lastcategorypos:= pos;
fi;
# Fill the column for level `currlevel' above the current line.
if prevdatarow = y-1 then
NCurses.wmove( win, y-2, 0 );
NCurses.waddstr( win, ListWithIdenticalEntries(
2*currlevel-2, ' ' ) );
NCurses.wmove( win, y-2, 2*currlevel-2 );
NCurses.waddch( win, NCurses.lineDraw.LTEE );
fi;
if y <= ymax then
NCurses.wmove( win, y-1, 0 );
NCurses.waddstr( win, ListWithIdenticalEntries(
2*currlevel-2, ' ' ) );
NCurses.wmove( win, y-1, 2*currlevel-2 );
NCurses.waddch( win, NCurses.lineDraw.LLCORNER );
fi;
prevdatarow:= y;
#T fill with LTEE only in the first line of a cell, otherwise with VLINE
# Draw a horizontal line in row `y'.
if y <= ymax then
NCurses.wmove( win, y-1, 2 * currlevel - 1 );
NCurses.whline( win, NCurses.lineDraw.HLINE,
2 * ( maxdepth - currlevel ) );
fi;
fi;
y:= y + height - fromrow + 1;
fi;
i:= i + 1;
fromrow:= 1;
od;
# Draw the missing vertical lines from and to outside categories:
# If there is an unhidden category of level $k$ below and above
# the next category of level less than $k$
# then either replace the `LLCORNER' in column $k-1$ by an `LTEE' and
# draw a `VLINE' below until the end of the window,
# or in the case that there is no `LLCORNER' in column $k-1$
# draw a `VLINE' through the whole window.
for k in [ 2 .. Length( previousrow ) ] do
if previousrow[k] >= previousrow[ k-1 ] then
for pos2 in [ lastcategorypos + 1 .. Length( catpos ) ] do
if categories[2][ pos2 ].level = k
and not categories[2][ pos2 ].isRejectedCategory then
if previousrow[k] = 0 then
NCurses.wmove( win, ymin - 1, 2 * ( k-2 ) );
NCurses.wvline( win, NCurses.lineDraw.VLINE,
ymax - ymin + 1 );
elif previousrow[k] <= ymax then
NCurses.wmove( win, previousrow[k]-1, 2 * ( k-2 ) );
NCurses.waddch( win, NCurses.lineDraw.LTEE );
NCurses.wmove( win, previousrow[k], 2 * ( k-2 ) );
NCurses.wvline( win, NCurses.lineDraw.VLINE,
ymax - previousrow[k] );
fi;
break;
elif categories[2][ pos2 ].level < k
and not categories[2][ pos2 ].isRejectedCategory then
break;
fi;
od;
fi;
od;
end;
#############################################################################
##
#E