1############################################################################# 2## 3#W brgrids.g GAP 4 package `browse' Thomas Breuer 4## 5#Y Copyright (C) 2007, Lehrstuhl D für Mathematik, RWTH Aachen, Germany 6## 7## This file contains some <C>SpecialGrid</C> functions. 8## 9 10#T TB, May 2007: 11#T extended version of `NCurses.Grid', 12#T supports ``indicating continuation'' and attributes for lines; 13#T the optional components `tend', `bend', `lend', `rend' are `true' 14#T if the shown grid is not continued at the top, bottom, left, and right, 15#T respectively (this is also the default), and `false' otherwise; 16#T the argument `attrs', if given, must be an integer 17#T describing the attributes for the lines. 18 19NCurses.GridExt:= function( win, args, attrs ) 20 local size, trow, brow, lcol, rcol, rowinds, colinds, 21 tend, bend, lend, rend, 22 tvis, bvis, ht, lvis, rvis, wdth, ld, lmr, i, j; 23 24 size := NCurses.getmaxyx(win); 25 if size = false then return false; fi; 26 27 trow:= args.trow; 28 brow:= args.brow; 29 lcol:= args.lcol; 30 rcol:= args.rcol; 31 rowinds:= args.rowinds; 32 colinds:= args.colinds; 33 tend:= not IsBound( args.tend ) or args.tend = true; 34 bend:= not IsBound( args.bend ) or args.bend = true; 35 lend:= not IsBound( args.lend ) or args.lend = true; 36 rend:= not IsBound( args.rend ) or args.rend = true; 37 38 if not ForAll([trow, brow, lcol, rcol], IsInt) then return false; fi; 39 if not ForAll(rowinds, IsInt) then return false; fi; 40 if not ForAll(colinds, IsInt) then return false; fi; 41 # find viewable rows and cols 42 rowinds := Filtered(rowinds, i-> i >= 0 and i >= trow and 43 i <= size[1]-1 and i <= brow); 44 colinds := Filtered(colinds, i-> i >= 0 and i >= lcol and 45 i <= size[2]-1 and i <= rcol); 46 if IsEmpty( rowinds ) and IsEmpty( colinds ) then 47 return false; 48 fi; 49 tvis := Maximum(trow, 0); 50 bvis := Minimum(brow, size[1]); 51 ht := bvis - tvis + 1; 52 lvis := Maximum(lcol, 0); 53 rvis := Minimum(rcol, size[2]); 54 wdth := rvis - lvis + 1; 55 # Set attributes for the lines. 56 NCurses.wattrset( win, attrs ); 57 # draw vlines 58 ld := NCurses.lineDraw; 59 for i in colinds do 60 NCurses.wmove(win, tvis, i); 61 NCurses.wvline(win, ld.VLINE, ht); 62 od; 63 # draw hlines and handle crossings 64 for i in rowinds do 65 NCurses.wmove(win, i, lvis); 66 NCurses.whline(win, ld.HLINE, wdth); 67 if i = trow and tend then 68 lmr := [ld.ULCORNER, ld.TTEE, ld.URCORNER]; 69 elif i = brow and bend then 70 lmr := [ld.LLCORNER, ld.BTEE, ld.LRCORNER]; 71 else 72 lmr := [ld.LTEE, ld.PLUS, ld.RTEE]; 73 fi; 74 for j in colinds do 75 NCurses.wmove(win, i, j); 76 if j = lcol and lend then 77 NCurses.waddch(win, lmr[1]); 78 elif j = rcol and rend then 79 NCurses.waddch(win, lmr[3]); 80 else 81 NCurses.waddch(win, lmr[2]); 82 fi; 83 od; 84 od; 85 # Reset the attributes. 86 NCurses.wattrset( win, NCurses.attrs.NORMAL ); 87 return true; 88end; 89 90 91############################################################################# 92## 93#F BrowseData.SpecialGridLineDraw( <t>, <data> ) 94## 95## When this special grid is used in a browse table, 96## nonempty row separators are overwritten with horizontal rows 97## that consist of the special character <C>NCurses.lineDraw.HLINE</C>, 98## non-blank characters in column separators are overwritten with vertical 99## rows that consist of the special character <C>NCurses.lineDraw.VLINE</C>, 100## and <Q>crossings</Q> of horizontal and vertical lines are handled as in 101## <Ref Func="NCurses.Grid"/>. 102## <P/> 103## In categorized browse tables, each set of table rows shown under 104## a category row gets a grid of its own, 105## and the separators below category rows are regarded as the top rows of 106## these grids. 107## So this special grid requires nonempty category separators if nonempty 108## row separators occur. 109## (Note that below a row separator, first the category rows and their 110## separators appear, and then the data rows and their separators. 111## If there is a nonempty row separator above the first data row 112## then it is overwritten by whitespace.) 113## 114BrowseData.SpecialGridLineDraw:= function( t, data ) 115 local win, entry, top, i; 116 117 win:= t.dynamic.window; 118 119 # Clear category separators, since they may exceed the last column. 120 for entry in data.catSeparators do 121 NCurses.wmove( win, entry[1], entry[2] ); 122 NCurses.waddstr( win, ListWithIdenticalEntries( entry[3], ' ' ) ); 123 od; 124 125 # Replace the row and column separators in all four tables by the lines. 126 for entry in data.gridsInfo do 127 NCurses.GridExt( win, entry, NCurses.attrs.NORMAL ); 128 od; 129 130 # Overwrite a row separator above the first data row 131 # if the table has a category for the first data row. 132 top:= t.dynamic.topleft[1]; 133 if IsOddInt( top ) 134 and t.dynamic.topleft[3] = 1 135 and BrowseData.LengthCell( t, top, "vert" ) <> 0 then 136 for i in [ top+1, top+3 .. Length( t.dynamic.indexRow ) - 1 ] do 137 if BrowseData.LengthCell( t, i, "vert" ) <> 0 then 138 if i in t.dynamic.categories[1] then 139 NCurses.wmove( win, data.topmargin + data.headerLength 140 + BrowseData.HeightLabelsColTable( t ), 141 data.leftmargin ); 142 NCurses.waddstr( win, ListWithIdenticalEntries( 143 NCurses.getmaxyx( win )[2] - data.leftmargin, ' ' ) ); 144 fi; 145 break; 146 fi; 147 od; 148 fi; 149 150 # Print those category separators that do not overwrite grid top lines. 151#T force printing of category separators involving attributes! 152 for entry in data.catSeparators do 153 if ForAll( data.gridsInfo, x -> entry[1] <> x.trow ) then 154 NCurses.wmove( win, entry[1], entry[2] ); 155 NCurses.whline( win, NCurses.lineDraw.HLINE, entry[3] ); 156 fi; 157 od; 158end; 159 160 161############################################################################# 162## 163#F BrowseData.SpecialGridLineDrawPlus( <t>, <data> ) 164## 165## This function is used for example in the `Browse' method for matrices. 166## 167## It draws the crossing of the two lines that separate the row and column 168## labels from the main table. 169## Note that the grids drawn by `BrowseData.SpecialGridLineDraw' belong to 170## one of the four subtables of a browse table, and it is not supported 171## that lines separate these tables. 172## 173BrowseData.SpecialGridLineDrawPlus:= function( t, data ) 174 local win; 175 176 BrowseData.SpecialGridLineDraw( t, data ); 177 win:= t.dynamic.window; 178 NCurses.wmove( win, data.gridsInfo[1].rowinds[1], 179 data.gridsInfo[1].colinds[1] ); 180 NCurses.waddch( win, NCurses.lineDraw.PLUS ); 181end; 182 183 184############################################################################# 185## 186#F BrowseData.SpecialGridTreeStyle( <t>, <data> ) 187## 188## *NOTE*: 189## This function does not yet support category separators 190## and cells of height larger than 1. 191## (And there is the general problem that when one expands all categories at 192## the end of the table, the programmatically hidden rows are taken into 193## account and then lead to unmotivated empty rows.) 194## (Scrolling by cells seems to be not appropriate for this application, 195## scrolling by characters would be better.) 196## 197BrowseData.SpecialGridTreeStyle:= function( t, data ) 198 local categories, catpos, win, i, fromrow, maxdepth, previousrow, scr, 199 ymin, ymax, y, prevdatarow, pos, entry, level, currlevel, pos2, 200 kmin, j, k, lastcategorypos, height, l; 201 202 categories:= t.dynamic.categories; 203 catpos:= categories[1]; 204 if IsEmpty( catpos ) or 2 < catpos[1] then 205 # `BrowseData.SpecialGridTreeStyle' expects category rows, 206 # starting in the first row. 207 return; 208 fi; 209 win:= t.dynamic.window; 210 i:= t.dynamic.topleft[1]; 211 fromrow:= t.dynamic.topleft[3]; 212 maxdepth:= Length( t.work.sepCol[1] ); 213 214 # The `i'-th entry in `previousrow' is the position of the previous 215 # level `i' category if there is one on the screen, and zero otherwise. 216 previousrow:= 0 * [ 0 .. maxdepth ]; 217 218 # Process the current line. 219 scr:= BrowseData.HeightWidthWindow( t ); 220 ymin:= data.topmargin + BrowseData.HeightLabelsColTable( t ) 221 + data.headerLength + 1; 222 ymax:= scr[1] - data.bottommargin - data.footerLength; 223 y:= ymin; 224 prevdatarow:= fail; 225 while y <= ymax+1 and IsBound( t.dynamic.indexRow[i] ) do 226 # We consider row `ymax+1' but we do not print there. 227 if i mod 2 = 0 then 228 229 # Deal with categories, omitting the first `fromrow - 1' rows. 230 pos:= PositionSorted( catpos, i ); 231#T Why is there no variant with a <from> argument? 232 while pos <= Length( catpos ) and catpos[ pos ] = i do 233 entry:= categories[2][ pos ]; 234 if entry.isUnderCollapsedCategory or entry.isRejectedCategory then 235 # This category and all of higher level are hidden. 236 break; 237 fi; 238 level:= entry.level; 239 240 # Deal with the category line. 241 if 1 < fromrow then 242 # The beginning of the cell is above the screen. 243 fromrow:= fromrow - 1; 244 else 245 # (We print no path to level 1 rows.) 246 if 1 < level then 247 if not IsBound( currlevel ) then 248 # The current level is that of the previous category row. 249 pos2:= pos; 250 while 1 < pos2 and catpos[ pos2 ] = i do 251 pos2:= pos2 - 1; 252 od; 253 currlevel:= categories[2][ pos2 ].level; 254 fi; 255 256 # Print the grid part for this level above the current line. 257 if currlevel < level then 258 # Fill the level `currlevel' column above the current line. 259 if ymin < y and previousrow[ currlevel ] + 1 < y then 260 NCurses.wmove( win, y-2, 0 ); 261 NCurses.waddstr( win, ListWithIdenticalEntries( 262 2*currlevel-2, ' ' ) ); 263 NCurses.wmove( win, y-2, 2*currlevel-2 ); 264 NCurses.waddch( win, NCurses.lineDraw.LTEE ); 265 fi; 266 elif currlevel = level then 267 # Fill the level `currlevel' column above the current line. 268 if previousrow[ currlevel ] = 0 then 269 kmin:= ymin; 270 NCurses.wmove( win, kmin-1, 0 ); 271 NCurses.waddstr( win, ListWithIdenticalEntries( 272 2*currlevel-4, ' ' ) ); 273 NCurses.wmove( win, kmin - 1, 2*currlevel-4 ); 274 NCurses.waddch( win, NCurses.lineDraw.VLINE ); 275 else 276 kmin:= previousrow[ currlevel ]; 277 NCurses.wmove( win, kmin-1, 0 ); 278 NCurses.waddstr( win, ListWithIdenticalEntries( 279 2*currlevel-4, ' ' ) ); 280 NCurses.wmove( win, kmin - 1, 2*currlevel-4 ); 281 NCurses.waddch( win, NCurses.lineDraw.LTEE ); 282 fi; 283 NCurses.wmove( win, kmin, 2 * ( currlevel-2 ) ); 284 NCurses.wvline( win, NCurses.lineDraw.VLINE, y - kmin - 1 ); 285 else 286 # Fill the column for level `level' above the current line. 287 if previousrow[ level ] = 0 then 288 kmin:= ymin; 289 else 290 kmin:= previousrow[ level ]; 291 fi; 292 NCurses.wmove( win, kmin-1, 0 ); 293 NCurses.waddstr( win, ListWithIdenticalEntries( 294 2*level-4, ' ' ) ); 295 NCurses.wmove( win, kmin - 1, 2*level-4 ); 296 if previousrow[ level ] = 0 then 297 NCurses.waddch( win, NCurses.lineDraw.VLINE ); 298 else 299 NCurses.waddch( win, NCurses.lineDraw.LTEE ); 300 fi; 301 NCurses.wmove( win, kmin, 2 * ( level-2 ) ); 302 NCurses.wvline( win, NCurses.lineDraw.VLINE, y - kmin - 1 ); 303 fi; 304 if y <= ymax then 305 NCurses.wmove( win, y-1, 0 ); 306 NCurses.waddstr( win, ListWithIdenticalEntries( 307 2*level-4, ' ' ) ); 308 NCurses.wmove( win, y-1, 2*level-4 ); 309 NCurses.waddch( win, NCurses.lineDraw.LLCORNER ); 310 fi; 311 fi; 312 previousrow[ level ]:= y; 313 currlevel:= level; 314 lastcategorypos:= pos; 315 316 y:= y + 1; 317 fi; 318 319 pos:= pos + 1; 320 od; 321 fi; 322 323 # Deal with the data rows. 324#T what about fromrow? 325 height:= BrowseData.HeightRow( t, i ); 326 if 0 < height then 327 if i mod 2 = 0 then 328 if not IsBound( currlevel ) then 329 # The first shown row is a data row. 330 # Determine the level of the category row above the first line. 331 pos:= PositionSorted( catpos, i ); 332 if IsBound( catpos[ pos ] ) and catpos[ pos ] = i then 333 if BrowseData.HeightCategories( t, i ) < fromrow then 334 # Take the last category row for `i'. 335 while IsBound( catpos[ pos ] ) and catpos[ pos ] = i do 336 pos:= pos + 1; 337 od; 338 else 339 # Take the last category row for `i' above the first line, 340 # together with its category separator below. 341 while IsBound( catpos[ pos ] ) 342 and BrowseData.HeightCategories( t, i, 343 categories[2][ pos ].level ) < fromrow do 344 pos:= pos + 1; 345 od; 346 fi; 347 fi; 348 if 1 < pos then 349 pos:= pos-1; 350 fi; 351 currlevel:= categories[2][ pos ].level; 352 lastcategorypos:= pos; 353 fi; 354 355 # Fill the column for level `currlevel' above the current line. 356 if prevdatarow = y-1 then 357 NCurses.wmove( win, y-2, 0 ); 358 NCurses.waddstr( win, ListWithIdenticalEntries( 359 2*currlevel-2, ' ' ) ); 360 NCurses.wmove( win, y-2, 2*currlevel-2 ); 361 NCurses.waddch( win, NCurses.lineDraw.LTEE ); 362 fi; 363 if y <= ymax then 364 NCurses.wmove( win, y-1, 0 ); 365 NCurses.waddstr( win, ListWithIdenticalEntries( 366 2*currlevel-2, ' ' ) ); 367 NCurses.wmove( win, y-1, 2*currlevel-2 ); 368 NCurses.waddch( win, NCurses.lineDraw.LLCORNER ); 369 fi; 370 prevdatarow:= y; 371#T fill with LTEE only in the first line of a cell, otherwise with VLINE 372 # Draw a horizontal line in row `y'. 373 if y <= ymax then 374 NCurses.wmove( win, y-1, 2 * currlevel - 1 ); 375 NCurses.whline( win, NCurses.lineDraw.HLINE, 376 2 * ( maxdepth - currlevel ) ); 377 fi; 378 fi; 379 y:= y + height - fromrow + 1; 380 fi; 381 i:= i + 1; 382 fromrow:= 1; 383 od; 384 385 # Draw the missing vertical lines from and to outside categories: 386 # If there is an unhidden category of level $k$ below and above 387 # the next category of level less than $k$ 388 # then either replace the `LLCORNER' in column $k-1$ by an `LTEE' and 389 # draw a `VLINE' below until the end of the window, 390 # or in the case that there is no `LLCORNER' in column $k-1$ 391 # draw a `VLINE' through the whole window. 392 for k in [ 2 .. Length( previousrow ) ] do 393 if previousrow[k] >= previousrow[ k-1 ] then 394 for pos2 in [ lastcategorypos + 1 .. Length( catpos ) ] do 395 if categories[2][ pos2 ].level = k 396 and not categories[2][ pos2 ].isRejectedCategory then 397 if previousrow[k] = 0 then 398 NCurses.wmove( win, ymin - 1, 2 * ( k-2 ) ); 399 NCurses.wvline( win, NCurses.lineDraw.VLINE, 400 ymax - ymin + 1 ); 401 elif previousrow[k] <= ymax then 402 NCurses.wmove( win, previousrow[k]-1, 2 * ( k-2 ) ); 403 NCurses.waddch( win, NCurses.lineDraw.LTEE ); 404 NCurses.wmove( win, previousrow[k], 2 * ( k-2 ) ); 405 NCurses.wvline( win, NCurses.lineDraw.VLINE, 406 ymax - previousrow[k] ); 407 fi; 408 break; 409 elif categories[2][ pos2 ].level < k 410 and not categories[2][ pos2 ].isRejectedCategory then 411 break; 412 fi; 413 od; 414 fi; 415 od; 416end; 417 418 419############################################################################# 420## 421#E 422 423