Goto Chapter: Top 1 2 3 4 5 6 A Bib Ind
 [Top of Book]  [Contents]   [Previous Chapter]   [Next Chapter] 

4 Browsing Tables in GAP using ncurses –The User Interface
 4.1 Features Supported by the Function NCurses.BrowseGeneric
 4.2 Data Structures used by NCurses.BrowseGeneric
 4.3 The Function NCurses.BrowseGeneric

4 Browsing Tables in GAP using ncurses –The User Interface

As stated in Section 1.1, one aim of the Browse package is to provide tools for the quite usual task to show a two-dimensional array or certain rows and columns of it on a character screen in a formatted way, to navigate in this array via key strokes (and mouse events), and to search for strings, to sort the array by row or column values etc.

The idea is that one starts with an array of data, the main table. Optionally, labels for each row of the main table are given, which are also arranged in an array (with perhaps several columns), the row labels table; analogously, a column labels table of labels for the columns of the main table may be given. The row labels are shown to the left of the main table, the column labels are shown above the main table. The space above the row labels and to the left of the column labels can be used for a fourth table, the corner table, with information about the labels or about the main table. Optionally, a header and a footer may be shown above and below these four tables, respectively. Header and footer are not separated into columns. So the shown window has the following structure.

header
footer
corner column labels
row labels main table

If not the whole tables fit into the window then only subranges of rows and columns of the main table are shown, together with the corresponding row and column labels. Also in this case, the row heights and column widths are computed w.r.t. the whole table not w.r.t. the shown rows and columns. This means that the shown row labels are unchanged if the range of shown columns is changed, the shown column labels are unchanged if the range of shown rows is changed, and the whole corner table is always shown.

The current chapter describes the user interface for standard applications of this kind, i. e., those applications for which one just has to collect the data to be shown in a record –which we call a browse table– without need for additional GAP programming.

Section 4.1 gives an overview of the features available in standard browse table applications, and Section 4.2 describes the data structures used in browse tables. Finally, Section 4.3 introduces the function NCurses.BrowseGeneric (4.3-1), which is the generic function for showing browse table in visual mode.

For technical details needed to extend these applications and to build other applications, see Chapter 5.

Examples of browse table applications are shown in Chapter 6.

4.1 Features Supported by the Function NCurses.BrowseGeneric

Standard applications of the function NCurses.BrowseGeneric (4.3-1) have the following functionality. Other applications may provide only a subset, or add further functionality, see Chapters 5 and 6.

Scrolling:

The subranges of shown rows and columns of the main table can be modified, such that the focus area is moved to the left, to the right, up, or down; depending on the context, the focus is moved by one character, by one table cell or a part of it, by the window height/width (minus one character or minus one table cell). If mouse events are enabled then cells can be selected also via mouse clicks.

Selecting:

A cell, row, or column of the main table can be selected; then it is shown highlighted on the screen (by default using the attribute NCurses.attrs.STANDOUT, see Section 2.1-7). The selection can be moved inside the main table to a neighboring cell, row, or column; this causes also scrolling of the main table when the window borders are reached.

Searching:

A search string is entered by the user, and the first matching cell becomes selected; one can search further for the next matching cell. Global search parameters define what matching means (case sensitive or not, search for substrings or complete words) and what the first and the next matching cells are (search in the whole table or just in the selected row or column, search for whole words or prefixes or suffixes, search forwards or backwards).

Sorting:

If a row or column is selected then the main table can be sorted w.r.t. the entries in this row or column. Global sort parameters describe for example whether one wants to sort ascending or descending, or case sensitive or not.

If a categorized table is sorted by a column then the category rows are removed and the current sorting and filtering by rows is reset before the table is sorted by the given column. If a table is sorted by a column/row that is already sorted by a column/row then this ordering is reset first.

Sorting can be undone globally, i. e., one can return to the unsorted table.

Sorting and Categorizing:

If a column is selected then the main table can be sorted w.r.t. the entries in this column, and additionally these entries are turned into category rows, i. e., additional rows are added to the main table, appearing immediately above the table rows with a fixed value in the selected column, and showing this column value. (There should be no danger to mix up this notion of categories with the one introduced in Reference: Categories.) The category rows can be collapsed (that is, the table rows that belong to this category row are not shown) or expanded (that is, the corresponding table rows are shown). Some of the global search parameters affect the category rows, for example, whether the category rows shall involve a counter showing the number of corresponding data rows, or whether a row of the browse table appears under different category rows.

Sorting and categorizing can be undone globally, i. e., one can return to the unsorted table without category rows.

Filtering:

The browse table can be restricted to those rows or columns in which a given search string occurs. (Also entries in collapsed rows can match; they remain collapsed then.) As a consequence, the category rows are restricted to those under which a matching row occurs. (It is irrelevant whether the search string occurs in category rows.)

If the search string does not occur at all then a message is printed, and the table remains as it was before. If a browse table is restricted then this fact is indicated by the message "restricted table" in the lower right corner of the window.

When a column or row is selected then the search is restricted to the entries in this column or row, respectively. Besides using a search, one can also explicitly hide the selected row or column. Filtering in an already restricted table restricts the shown rows or columns further.

Filtering can be undone globally, i. e., one can return to the unrestricted table.

Help:

Depending on the application and on the situation, different sets of user inputs may be available and different meanings of these inputs are possible. An overview of the currently available inputs and their meanings can be opened in each situation, by hitting the ? key.

Re-entering:

When one has called NCurses.BrowseGeneric (4.3-1) with a browse table, and returns from visual mode to the GAP prompt after some navigation steps, calling NCurses.BrowseGeneric again with this table will enter visual mode in the same situation where it was left. For example, the cell in the top-left position will be the same as before, and if a cell was selected before then this cell will be selected now. (One can avoid this behavior using the optional second argument of NCurses.BrowseGeneric.)

Logging:

The integers corresponding to the user inputs in visual mode are collected in a list that is stored in the component dynamic.log of the browse table. It can be used for repeating the inputs with the replay feature. (For browse table applications that give the user no access to the browse table itself, one can force the log to be assigned to the component log of the global variable BrowseData, see Section 5.4-1.)

Replay:

Instead of interactively hitting keys in visual mode, one can prescribe the user inputs to a browse table via a "replay record"; the inputs are then processed with given time intervals. The easiest way to create a meaningful replay record is via the function BrowseData.SetReplay (5.4-2), with argument the dynamic.log component of the browse table in question that was stored in an interactive session.

The following features are not available in standard applications. They require additional programming.

Clicking:

One possible action is to "click" a selected cell, row, or column, by hitting the Enter key. It depends on the application what the effect is. A typical situation is that a corresponding GAP object is added to the list of return values of NCurses.BrowseGeneric (4.3-1). Again it depends on the application what this GAP object is. In order to use this feature, one has to provide a record whose components are GAP functions, see Section 5.4-1 for details. If mouse events are enabled (see NCurses.UseMouse (2.2-10)) then also mouse clicks can be used as an alternative to hitting the Enter key.

Return Value:

The function NCurses.BrowseGeneric (4.3-1) may have an application dependent return value. A typical situation is that a list of objects corresponding to those cells is returned that were "clicked" in visual mode. In order to use this feature, one has to assign the desired value to the component dynamic.Return of the browse table.

4.2 Data Structures used by NCurses.BrowseGeneric

4.2-1 BrowseData.IsBrowseTableCellData
‣ BrowseData.IsBrowseTableCellData( obj )( function )

Returns: true if the argument is a list or a record in a supported format.

A table cell data object describes the input data for the contents of a cell in a browse table. It is represented by either an attribute line (see NCurses.IsAttributeLine (2.2-3)), for cells of height one, or a list of attribute lines or a record with the components rows, a list of attribute lines, and optionally align, a substring of "bclt", which describes the alignment of the attribute lines in the table cell -- bottom, horizontally centered, left, and top alignment; the default is right and vertically centered alignment. (Note that the height of a table row and the width of a table column can be larger than the height and width of an individual cell.)

gap> BrowseData.IsBrowseTableCellData( "abc" );
true
gap> BrowseData.IsBrowseTableCellData( [ "abc", "def" ] );
true
gap> BrowseData.IsBrowseTableCellData( rec( rows:= [ "ab", "cd" ],
>                                           align:= "tl" ) );
true
gap> BrowseData.IsBrowseTableCellData( "" );
true
gap> BrowseData.IsBrowseTableCellData( [] );
true

The empty string is a table cell data object of height one and width zero whereas the empty list (which is not in IsStringRep (Reference: IsStringRep)) is a table cell data object of height zero and width zero.

4.2-2 BrowseData.BlockEntry
‣ BrowseData.BlockEntry( tablecelldata, height, width )( function )

Returns: a list of attribute lines.

For a table cell data object tablecelldata (see BrowseData.IsBrowseTableCellData (4.2-1)) and two positive integers height and width, BrowseData.BlockEntry returns a list of height attribute lines of displayed length width each (see NCurses.WidthAttributeLine (2.2-7)), that represents the formatted version of tablecelldata.

If the rows of tablecelldata have different numbers of displayed characters then they are filled up to the desired numbers of rows and columns, according to the alignment prescribed by tablecelldata; the default alignment is right and vertically centered.

gap> BrowseData.BlockEntry( "abc", 3, 5 );
[ "     ", "  abc", "     " ]
gap> BrowseData.BlockEntry( rec( rows:= [ "ab", "cd" ],
>                                align:= "tl" ), 3, 5 );
[ "ab   ", "cd   ", "     " ]

4.2-3 BrowseData.IsBrowseTable
‣ BrowseData.IsBrowseTable( obj )( function )

Returns: true if the argument record has the required components and is consistent.

A browse table is a GAP record that can be used as the first argument of the function NCurses.BrowseGeneric (4.3-1).

The supported components of a browse table are work and dynamic, their values must be records: The components in work describe that part of the data that are not likely to depend on user interactions, such as the table entries and their heights and widths. The components in dynamic describe that part of the data that is intended to change with user interactions, such as the currently shown top-left entry of the table, or the current status w.r.t. sorting. For example, suppose you call NCurses.BrowseGeneric (4.3-1) twice with the same browse table; the second call enters the table in the same status where it was left after the first call if the component dynamic is kept, whereas one has to reset (which usually simply means to unbind) the component dynamic if one wants to start in the same status as before the first call.

The following components are the most important ones for standard browse applications. All these components belong to the work record. For other supported components (of work as well as of dynamic) and for the meaning of the term "mode", see Section 5.2.

main

is the list of lists of table cell data objects that form the matrix to be shown. There is no default for this component. (It is possible to compute the entries of the main table on demand, see the description of the component Main in Section 5.4-1. In this situation, the value of the component main can be an empty list.)

header

describes a header that shall be shown above the column labels. The value is either a list of attribute lines ("static header") or a function or a record whose component names are names of available modes of the browse table ("dynamic header"). In the function case, the function must take the browse table as its only argument, and return a list of attribute lines. In the record case, the values of the components must be such functions. It is assumed that the number of these lines depends at most on the mode. The default is an empty list, i. e., there is no header.

footer

describes a footer that shall be shown below the table. The value is analogous to that of footer. The default is an empty list, i. e., there is no footer.

labelsRow

is a list of row label rows, each being a list of table cell data objects. These rows are shown to the left of the main table. The default is an empty list, i. e., there are no row labels.

labelsCol

is a list of column information rows, each being a list of table cell data objects. These rows are shown between the header and the main table. The default is an empty list, i. e., there are no column labels.

corner

is a list of lists of table cell data objects that are printed in the upper left corner, i. e., to the left of the column label rows and above the row label columns. The default is an empty list.

sepRow

describes the separators above and below rows of the main table and of the row labels table. The value is either an attribute line or a (not necessarily dense) list of attribute lines. In the former case, repetitions of the attribute line are used as separators below each row and above the first row of the table; in the latter case, repetitions of the entry at the first position (if bound) are used above the first row, and repetitions of the last bound entry before the \((i+2)\)-th position (if there is such an entry at all) are used below the \(i\)-th table row. The default is an empty string, which means that there are no row separators.

sepCol

describes the separators in front of and behind columns of the main table and of the column labels table. The format of the value is analogous to that of the component sepRow; the default is the string " " (whitespace of width one).

sepLabelsCol

describes the separators above and below rows of the column labels table and of the corner table, analogously to sepRow. The default is an empty string, which means that there are no column label separators.

sepLabelsRow

describes the separators in front of and behind columns of the row labels table and of the corner table, analogously to sepCol. The default is an empty string.

We give a few examples of standard applications.

The first example defines a small browse table by prescribing only the component work.main, so the defaults for row and column labels (no such labels), and for separators are used. The table cells are given by plain strings, so they have height one. Usually this table will fit on the screen.

gap> m:= 10;;  n:= 5;;
gap> xpl1:= rec( work:= rec(
>      main:= List( [ 1 .. m ], i -> List( [ 1 .. n ],
>        j -> String( [ i, j ] ) ) ) ) );;
gap> BrowseData.IsBrowseTable( xpl1 );
true

In the second example, also row and column labels appear, and different separators are used. The table cells have height three. Also this table will usually fit on the screen.

gap> m:= 6;;  n:= 5;;
gap> xpl2:= rec( work:= rec(
>      main:= List( [ 1 .. m ], i -> List( [ 1 .. n ],
>        j -> rec( rows:= List( [ -i*j, i*j*1000+j, i-j ], String ),
>                  align:= "c" ) ) ),
>      labelsRow:= List( [ 1 .. m ], i -> [ String( i ) ] ),
>      labelsCol:= [ List( [ 1 .. n ], String ) ],
>      sepRow:= "-",
>      sepCol:= "|",
>  ) );;
gap> BrowseData.IsBrowseTable( xpl2 );
true

The third example additionally has a static header and a dynamic footer, and the table cells involve attributes. This table will usually not fit on the screen. Note that NCurses.attrs.ColorPairs is available only if the terminal supports colors, which can be checked using NCurses.attrs.has_colors (2.2-1).

gap> m:= 30;;  n:= 25;;
gap> xpl3:= rec( work:= rec(
>      header:= [ "                    Example 3" ],
>      labelsRow:= List( [ 1 .. 30 ], i -> [ String( i ) ] ),
>      sepLabelsRow:= " % ",
>      sepLabelsCol:= "=",
>      sepRow:= "*",
>      sepCol:= " |",
>      footer:= t -> [ Concatenation( "top-left entry is: ",
>                          String( t.dynamic.topleft{ [ 1, 2] } ) ) ],
>  ) );;
gap> if NCurses.attrs.has_colors then
>   xpl3.work.main:= List( [ 1 .. m ], i -> List( [ 1 .. n ],
>     j -> rec( rows:= [ String( -i*j ),
>                        [ NCurses.attrs.BOLD, true,
>                          NCurses.attrs.ColorPairs[56+1], true,
>                          String( i*j*1000+j ),
>                          NCurses.attrs.NORMAL, true ],
>                          String( i-j ) ],
>               align:= "c" ) ) );
>   xpl3.work.labelsCol:= [ List( [ 1 .. 30 ], i -> [
>     NCurses.attrs.ColorPairs[ 56+4 ], true,
>     String( i ),
>     NCurses.attrs.NORMAL, true ] ) ];
> else
>   xpl3.work.main:= List( [ 1 .. m ], i -> List( [ 1 .. n ],
>     j -> rec( rows:= [ String( -i*j ),
>                        [ NCurses.attrs.BOLD, true,
>                          String( i*j*1000+j ),
>                          NCurses.attrs.NORMAL, true ],
>                          String( i-j ) ],
>               align:= "c" ) ) );
>   xpl3.work.labelsCol:= [ List( [ 1 .. 30 ], i -> [
>     NCurses.attrs.BOLD, true,
>     String( i ),
>     NCurses.attrs.NORMAL, true ] ) ];
> fi;
gap> BrowseData.IsBrowseTable( xpl3 );
true

The fourth example illustrates that highlighting may not work properly for browse tables containing entries whose attributes are not set with explicit Boolean values, see NCurses.IsAttributeLine (2.2-3). Call NCurses.BrowseGeneric (4.3-1) with the browse table xpl4, and select an entry (or a column or a row): Only the middle row of each selected cell will be highlighted, because only in this row, the color attribute is switched on with an explicit true.

gap> xpl4:= rec(
>     defc:= NCurses.defaultColors,
>     wd:= Maximum( List( ~.defc, Length ) ),
>     ca:= NCurses.ColorAttr,
>     work:= rec(
>       header:= [ "Examples of NCurses.ColorAttr" ],
>       main:= List( ~.defc, i -> List( ~.defc,
>         j -> [ [ ~.ca( i, j ), String( i, ~.wd ) ],        # no true!
>                [ ~.ca( i, j ), true, String( "on", ~.wd ) ],
>                [ ~.ca( i, j ), String( j, ~.wd ) ] ] ) ),  # no true!
>       labelsRow:= List( ~.defc, i -> [ String( i ) ] ),
>       labelsCol:= [ List( ~.defc, String ) ],
>       sepRow:= "-",
>       sepCol:= [ " |", "|" ],
>  ) );;
gap> BrowseData.IsBrowseTable( xpl4 );
true

4.3 The Function NCurses.BrowseGeneric

4.3-1 NCurses.BrowseGeneric
‣ NCurses.BrowseGeneric( t[, arec] )( function )

Returns: an application dependent value, or nothing.

NCurses.BrowseGeneric is used to show the browse table t (see BrowseData.IsBrowseTable (4.2-3)) in a formatted way on a text screen, and allows the user to navigate in this table.

The optional argument arec, if given, must be a record whose components work and dynamic, if bound, are used to provide defaults for missing values in the corresponding components of t. The default for arec and for the components not provided in arec is BrowseData.defaults, see BrowseData (5.4-1), the function BrowseData.SetDefaults sets these default values.

At least the component work.main must be bound in t, with value a list of list of table cell data objects, see BrowseData.IsBrowseTableCellData (4.2-1).

When the window or the screen is too small for the browse table, according to its component work.minyx, the table will not be shown in visual mode, and fail is returned. (This holds also if there would be no return value of the call in a large enough screen.) Thus one should check for fail results of programmatic calls of NCurses.BrowseGeneric, and one should better not admit fail as a regular return value.

Most likely, NCurses.BrowseGeneric will not be called on the command line, but the browse table t will be composed by a suitable function which then calls NCurses.BrowseGeneric, see the examples in Chapter 6.

 [Top of Book]  [Contents]   [Previous Chapter]   [Next Chapter] 
Goto Chapter: Top 1 2 3 4 5 6 A Bib Ind

generated by GAPDoc2HTML